Merge from Chromium at DEPS revision r220549

This commit was generated by merge_to_master.py.

Change-Id: I8fcb82db764ec1eb0294280936c177bd9ba8a9e9
diff --git a/.DEPS.git b/.DEPS.git
index cd8a199..b500f9a 100644
--- a/.DEPS.git
+++ b/.DEPS.git
@@ -11,7 +11,7 @@
     'git_url':
          'https://chromium.googlesource.com',
     'webkit_rev':
-         '@fd52c40dc3d9e0ec255caf735a72a6c4914f1b69'
+         '@db028b6375095edf57d70007874a8c0e2b7d0dc0'
 }
 
 deps = {
@@ -28,7 +28,7 @@
     'src/media/cdm/ppapi/api':
         Var('git_url') + '/chromium/cdm.git@c2b192a02546916d28233cfd8b7717ffcdcc8347',
     'src/native_client':
-        Var('git_url') + '/native_client/src/native_client.git@8bd7716486accbb3562003a8d9dfec9e369721d1',
+        Var('git_url') + '/native_client/src/native_client.git@740811ad6a5926cafc1dbc19ebc0c369323e7950',
     'src/sdch/open-vcdiff':
         Var('git_url') + '/external/open-vcdiff.git@438f2a5be6d809bc21611a94cd37bfc8c28ceb33',
     'src/testing/gmock':
@@ -40,7 +40,7 @@
     'src/third_party/accessibility-developer-tools':
         Var('git_url') + '/external/accessibility-developer-tools.git@2e3c9c8e49277a0ca1eeba510271eb03f9486d8f',
     'src/third_party/angle_dx11':
-        Var('git_url') + '/external/angle.git@827fb6ad615eca2755ae80789a02e1a5587832b6',
+        Var('git_url') + '/external/angle.git@41159326ab429bb88262f80118e79bb5a108f0db',
     'src/third_party/bidichecker':
         Var('git_url') + '/external/bidichecker/lib.git@97f2aa645b74c28c57eca56992235c79850fa9e0',
     'src/third_party/cacheinvalidation/src':
@@ -66,7 +66,7 @@
     'src/third_party/libexif/sources':
         Var('git_url') + '/chromium/deps/libexif/sources.git@d815c325bab0d1871d4c7e70600ecdfdab07db9e',
     'src/third_party/libjingle/source/talk':
-        Var('git_url') + '/external/webrtc/stable/talk.git@b57335ce77a25d53842d30fe148ccc4dac2e9c52',
+        Var('git_url') + '/external/webrtc/stable/talk.git@85698c96c51bae16ab87fef992e99b367c8cccae',
     'src/third_party/libjpeg_turbo':
         Var('git_url') + '/chromium/deps/libjpeg_turbo.git@82ce8a6d4ebe12a177c0c3597192f2b4f09e81c3',
     'src/third_party/libphonenumber/src/phonenumbers':
@@ -100,15 +100,15 @@
     'src/third_party/sfntly/cpp/src':
         Var('git_url') + '/external/sfntly/cpp/src.git@cfb2f1743f0169ad8d01035458617bce97107539',
     'src/third_party/skia/gyp':
-        Var('git_url') + '/external/skia/gyp.git@d47765b26e84582af96a763da51147211a0abd92',
+        Var('git_url') + '/external/skia/gyp.git@9d1d618692432166844d2485c53f84b5a3d8753d',
     'src/third_party/skia/include':
-        Var('git_url') + '/external/skia/include.git@0107089f772605188293bcec21abbc10a9b58d64',
+        Var('git_url') + '/external/skia/include.git@8a428faa7234318fe74571ddb417e1cc43f7d193',
     'src/third_party/skia/src':
-        Var('git_url') + '/external/skia/src.git@64f196fd22f821c9d99ddaf011473543c7b1e152',
+        Var('git_url') + '/external/skia/src.git@c5fdecc467cdf0a4c80fb28afaa24da9fcf6a133',
     'src/third_party/smhasher/src':
         Var('git_url') + '/external/smhasher.git@6f63a4882e6b2cf87e8eec1a3ef8644e0d963283',
     'src/third_party/snappy/src':
-        Var('git_url') + '/external/snappy.git@a18503d3a93fd2428f6a79fc829e9f732eb82360',
+        Var('git_url') + '/external/snappy.git@762bb32f0c9d2f31ba4958c7c0933d22e80c20bf',
     'src/third_party/speex':
         Var('git_url') + '/chromium/deps/speex.git@f448dfcceac99e0a771feaeedf447523b3fd26e1',
     'src/third_party/swig/Lib':
@@ -122,9 +122,9 @@
     'src/third_party/webgl_conformance':
         Var('git_url') + '/chromium/deps/webgl/sdk/tests.git@eddddb40ed480aa7aa25e4ff519fea122044ea8e',
     'src/third_party/webpagereplay':
-        Var('git_url') + '/external/web-page-replay.git@932244e6242b374f83b9b1d0be8913100af5b070',
+        Var('git_url') + '/external/web-page-replay.git@b6934747f4d589a4f24c42dd9d2a8540adaffd7d',
     'src/third_party/webrtc':
-        Var('git_url') + '/external/webrtc/stable/webrtc.git@728d09a99990016bc4cf5b826dec0c79e309a376',
+        Var('git_url') + '/external/webrtc/stable/webrtc.git@3e6006e94932eaf685f551fa8c36b1a08d4e0f7d',
     'src/third_party/yasm/source/patched-yasm':
         Var('git_url') + '/chromium/deps/yasm/patched-yasm.git@c960eb11ccda80b10ed50be39df4f0663b371d1d',
     'src/tools/deps2git':
@@ -132,13 +132,13 @@
     'src/tools/grit':
         Var('git_url') + '/external/grit-i18n.git@fbf8c54856bccf2d3e2431127ab334ab1dbd619e',
     'src/tools/gyp':
-        Var('git_url') + '/external/gyp.git@5d6c573d89bad8700442e12af9e6489d6bcafc4a',
+        Var('git_url') + '/external/gyp.git@3a479acaceb2157d3fe729db731a353608914731',
     'src/tools/page_cycler/acid3':
         Var('git_url') + '/chromium/deps/acid3.git@6be0a66a1ebd7ebc5abc1b2f405a945f6d871521',
     'src/tools/swarm_client':
         Var('git_url') + '/chromium/tools/swarm_client.git@51336a32732d4eea3f07e44e4a62c4d8103ef29f',
     'src/v8':
-        Var('git_url') + '/external/v8.git@3f7dfd6b89fc3921382fbdc1212fabab54a19757',
+        Var('git_url') + '/external/v8.git@1f410f9a9c4fbd4270749af64b477df87b753158',
 }
 
 deps_os = {
diff --git a/AUTHORS b/AUTHORS
index 794db30..da45b42 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -252,6 +252,7 @@
 Steven Pennington <spenn@engr.uvic.ca>
 Suchit Agrawal <a.suchit@samsung.com>
 Sudarsana Babu Nagineni <sudarsana.nagineni@intel.com>
+Sudarshan Parthasarathy <sudarshan.p@samsung.com>
 Sungguk Lim <limasdf@gmail.com>
 Sungmann Cho <sungmann.cho@gmail.com>
 Sylvain Zimmer <sylvinus@gmail.com>
@@ -288,6 +289,7 @@
 YoungKi Hong <simon.hong81@gmail.com>
 Yumikiyo Osanai <yumios.art@gmail.com>
 Yuri Gorobets <yuri.gorobets@gmail.com>
+Zeno Albisser <zeno.albisser@digia.com>
 Zheng Chuang <zhengchuangscu@gmail.com>
 方觉 (Fang Jue) <fangjue23303@gmail.com>
 
@@ -296,4 +298,5 @@
 NVIDIA Corporation <*@nvidia.com>
 Opera Software ASA <*@opera.com>
 The Chromium Authors <*@chromium.org>
+The MathWorks, Inc. <binod.pant@mathworks.com>
 Torchmobile Inc.
diff --git a/DEPS b/DEPS
index 0556178..f0c6244 100644
--- a/DEPS
+++ b/DEPS
@@ -8,12 +8,12 @@
   "sourceforge_url": "http://svn.code.sf.net/p/%(repo)s/code",
   "webkit_trunk": "http://src.chromium.org/blink/trunk",
   "nacl_trunk": "http://src.chromium.org/native_client/trunk",
-  "webkit_revision": "156598",
+  "webkit_revision": "156939",
   "chromium_git": "https://chromium.googlesource.com",
   "chromiumos_git": "https://chromium.googlesource.com/chromiumos",
   "skia_git": "https://skia.googlesource.com",
   "swig_revision": "69281",
-  "nacl_revision": "12040",
+  "nacl_revision": "12072",
   # After changing nacl_revision, run 'glient sync' and check native_client/DEPS
   # to update other nacl_*_revision's.
   "nacl_tools_revision": "11437",  # native_client/DEPS: tools_rev
@@ -29,16 +29,16 @@
   "ffmpeg_hash": "894e6f715645528e815aee2dad45b59704238dcd",
 
   "sfntly_revision": "134",
-  "skia_revision": "10884",
-  "skia_hash": "5ff3e8c7872eb5b840399b22fe4734b968f61a4b",
+  "skia_revision": "11007",
+  "skia_hash": "6ae54815276e1fd2f337798fd2badb45d5006308",
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and V8 without interference from each other.
-  "v8_revision": "16277",
+  "v8_revision": "16410",
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling WebRTC
   # and V8 without interference from each other.
-  "webrtc_revision": "4595",
+  "webrtc_revision": "4632",
   "jsoncpp_revision": "248",
   "nss_revision": "209026",
   # Three lines of non-changing comments so that
@@ -66,7 +66,7 @@
 
   "src/third_party/angle_dx11":
     Var("chromium_git") +
-    "/external/angle.git@827fb6ad615eca2755ae80789a02e1a5587832b6",
+    "/external/angle.git@41159326ab429bb88262f80118e79bb5a108f0db",
 
   "src/third_party/trace-viewer":
     (Var("googlecode_url") % "trace-viewer") + "/trunk@893",
@@ -97,13 +97,13 @@
     (Var("googlecode_url") % "leveldb") + "/trunk@76",
 
   "src/third_party/snappy/src":
-    (Var("googlecode_url") % "snappy") + "/trunk@74",
+    (Var("googlecode_url") % "snappy") + "/trunk@80",
 
   "src/tools/grit":
     (Var("googlecode_url") % "grit-i18n") + "/trunk@129",
 
   "src/tools/gyp":
-    (Var("googlecode_url") % "gyp") + "/trunk@1700",
+    (Var("googlecode_url") % "gyp") + "/trunk@1707",
 
   "src/tools/swarm_client":
     "/trunk/tools/swarm_client@" + Var("swarm_revision"),
@@ -233,7 +233,7 @@
     "/trunk/tools/deps2git@214390",
 
   "src/third_party/webpagereplay":
-    (Var("googlecode_url") % "web-page-replay") + "/trunk@519",
+    (Var("googlecode_url") % "web-page-replay") + "/trunk@520",
 
   "src/third_party/pywebsocket/src":
     (Var("googlecode_url") % "pywebsocket") + "/trunk/src@662",
diff --git a/GypAndroid.darwin-arm.mk b/GypAndroid.darwin-arm.mk
index d123e5d..9c73c2f 100644
--- a/GypAndroid.darwin-arm.mk
+++ b/GypAndroid.darwin-arm.mk
@@ -205,7 +205,6 @@
 include $(LOCAL_PATH)/ui/gl/gl.target.darwin-arm.mk
 include $(LOCAL_PATH)/ui/gl/gl_jni_headers.target.darwin-arm.mk
 include $(LOCAL_PATH)/ui/gl/surface_jni_headers.target.darwin-arm.mk
-include $(LOCAL_PATH)/ui/gl/surface_texture_jni_headers.target.darwin-arm.mk
 include $(LOCAL_PATH)/ui/native_theme/native_theme.target.darwin-arm.mk
 include $(LOCAL_PATH)/ui/shell_dialogs.target.darwin-arm.mk
 include $(LOCAL_PATH)/ui/snapshot/snapshot.target.darwin-arm.mk
diff --git a/GypAndroid.darwin-mips.mk b/GypAndroid.darwin-mips.mk
index 620cd4c..9d3381f 100644
--- a/GypAndroid.darwin-mips.mk
+++ b/GypAndroid.darwin-mips.mk
@@ -201,7 +201,6 @@
 include $(LOCAL_PATH)/ui/gl/gl.target.darwin-mips.mk
 include $(LOCAL_PATH)/ui/gl/gl_jni_headers.target.darwin-mips.mk
 include $(LOCAL_PATH)/ui/gl/surface_jni_headers.target.darwin-mips.mk
-include $(LOCAL_PATH)/ui/gl/surface_texture_jni_headers.target.darwin-mips.mk
 include $(LOCAL_PATH)/ui/native_theme/native_theme.target.darwin-mips.mk
 include $(LOCAL_PATH)/ui/shell_dialogs.target.darwin-mips.mk
 include $(LOCAL_PATH)/ui/snapshot/snapshot.target.darwin-mips.mk
diff --git a/GypAndroid.darwin-x86.mk b/GypAndroid.darwin-x86.mk
index 952d73c..4f42c66 100644
--- a/GypAndroid.darwin-x86.mk
+++ b/GypAndroid.darwin-x86.mk
@@ -219,7 +219,6 @@
 include $(LOCAL_PATH)/ui/gl/gl.target.darwin-x86.mk
 include $(LOCAL_PATH)/ui/gl/gl_jni_headers.target.darwin-x86.mk
 include $(LOCAL_PATH)/ui/gl/surface_jni_headers.target.darwin-x86.mk
-include $(LOCAL_PATH)/ui/gl/surface_texture_jni_headers.target.darwin-x86.mk
 include $(LOCAL_PATH)/ui/native_theme/native_theme.target.darwin-x86.mk
 include $(LOCAL_PATH)/ui/shell_dialogs.target.darwin-x86.mk
 include $(LOCAL_PATH)/ui/snapshot/snapshot.target.darwin-x86.mk
diff --git a/GypAndroid.linux-arm.mk b/GypAndroid.linux-arm.mk
index 4d94b7f..ca0bbae 100644
--- a/GypAndroid.linux-arm.mk
+++ b/GypAndroid.linux-arm.mk
@@ -205,7 +205,6 @@
 include $(LOCAL_PATH)/ui/gl/gl.target.linux-arm.mk
 include $(LOCAL_PATH)/ui/gl/gl_jni_headers.target.linux-arm.mk
 include $(LOCAL_PATH)/ui/gl/surface_jni_headers.target.linux-arm.mk
-include $(LOCAL_PATH)/ui/gl/surface_texture_jni_headers.target.linux-arm.mk
 include $(LOCAL_PATH)/ui/native_theme/native_theme.target.linux-arm.mk
 include $(LOCAL_PATH)/ui/shell_dialogs.target.linux-arm.mk
 include $(LOCAL_PATH)/ui/snapshot/snapshot.target.linux-arm.mk
diff --git a/GypAndroid.linux-mips.mk b/GypAndroid.linux-mips.mk
index 4e845b4..489971a 100644
--- a/GypAndroid.linux-mips.mk
+++ b/GypAndroid.linux-mips.mk
@@ -201,7 +201,6 @@
 include $(LOCAL_PATH)/ui/gl/gl.target.linux-mips.mk
 include $(LOCAL_PATH)/ui/gl/gl_jni_headers.target.linux-mips.mk
 include $(LOCAL_PATH)/ui/gl/surface_jni_headers.target.linux-mips.mk
-include $(LOCAL_PATH)/ui/gl/surface_texture_jni_headers.target.linux-mips.mk
 include $(LOCAL_PATH)/ui/native_theme/native_theme.target.linux-mips.mk
 include $(LOCAL_PATH)/ui/shell_dialogs.target.linux-mips.mk
 include $(LOCAL_PATH)/ui/snapshot/snapshot.target.linux-mips.mk
diff --git a/GypAndroid.linux-x86.mk b/GypAndroid.linux-x86.mk
index 0e437a7..bc08322 100644
--- a/GypAndroid.linux-x86.mk
+++ b/GypAndroid.linux-x86.mk
@@ -219,7 +219,6 @@
 include $(LOCAL_PATH)/ui/gl/gl.target.linux-x86.mk
 include $(LOCAL_PATH)/ui/gl/gl_jni_headers.target.linux-x86.mk
 include $(LOCAL_PATH)/ui/gl/surface_jni_headers.target.linux-x86.mk
-include $(LOCAL_PATH)/ui/gl/surface_texture_jni_headers.target.linux-x86.mk
 include $(LOCAL_PATH)/ui/native_theme/native_theme.target.linux-x86.mk
 include $(LOCAL_PATH)/ui/shell_dialogs.target.linux-x86.mk
 include $(LOCAL_PATH)/ui/snapshot/snapshot.target.linux-x86.mk
diff --git a/NOTICE b/NOTICE
index 58cba77..16d7baa 100644
--- a/NOTICE
+++ b/NOTICE
@@ -7912,6 +7912,209 @@
 
 */
 
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2011 Marco Braak
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
 Copyright 2007, Google Inc.
 All rights reserved.
 
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 7f4af43..cefb599 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -309,7 +309,7 @@
   problems = []
   for f in input_api.AffectedFiles():
     if (not f.LocalPath().endswith(('.cc', '.h')) or
-        f.LocalPath().endswith('test.cc')):
+        f.LocalPath().endswith(('test.cc', '_win.cc', '_win.h'))):
       continue
 
     allowWString = False
diff --git a/WATCHLISTS b/WATCHLISTS
index ff3e514..cd4ed25 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -470,6 +470,9 @@
                    '|third_party/hunspell_dictionaries/'\
                    '|webkit/tools/test_shell/mock_spellcheck'
     },
+    'streams': {
+      'filepath': 'content/browser/streams/',
+    },
     'sync': {
       'filepath': 'chrome/browser/sync/|sync/',
     },
@@ -604,7 +607,6 @@
                       'ctguil+watch@chromium.org',
                       'aboxhall+watch@chromium.org',
                       'davidbarr+watch@chromium.org',
-                      'zork+watch@chromium.org',
                       'hashimoto+watch@chromium.org',
                       'yoshiki+watch@chromium.org',
                       'yuzo+watch@chromium.org'],
@@ -628,8 +630,7 @@
                         'sievers+watch@chromium.org'],
     'autofill': ['isherman@chromium.org',
                  'dyu@chromium.org', 'abodenha@chromium.org',
-                 'estade+watch@chromium.org', 'ahutter@chromium.org',
-                 'dgwallinga@chromium.org', 'ramankk@chromium.org',
+                 'estade+watch@chromium.org', 'dgwallinga@chromium.org',
                  'benquan@chromium.org', 'rouslan+autofillwatch@chromium.org'],
     'automation': ['robertshield@chromium.org'],
     'base': ['erikwright+watch@chromium.org'],
@@ -692,7 +693,7 @@
     'geolocation': ['mvanouwerkerk@chromium.org'],
     'gfx_geometry': ['cc-bugs@chromium.org'],
     'gfx_image': ['rsesek+watch@chromium.org'],
-    'gpu': ['apatrick@chromium.org'],
+    'gpu': ['apatrick@chromium.org', 'piman+watch@chromium.org'],
     'history_ui': ['dubroy@chromium.org', 'pam+watch@chromium.org'],
     'i18n': ['jshin+watch@chromium.org'],
     'imageburner': ['tbarzic+watch@chromium.org'],
@@ -761,6 +762,7 @@
     'spellcheck': ['groby+spellwatch@chromium.org',
                    'rlp@chromium.org',
                    'rouslan+spellwatch@chromium.org'],
+    'streams': ['zork+watch@chromium.org'],
     'sync': ['haitaol+watch@chromium.org',
              'rsimha+watch@chromium.org',
              'tim+watch@chromium.org'],
diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc
index 51f1c17..9700645 100644
--- a/android_webview/browser/aw_browser_context.cc
+++ b/android_webview/browser/aw_browser_context.cc
@@ -9,6 +9,7 @@
 #include "android_webview/browser/aw_quota_manager_bridge.h"
 #include "android_webview/browser/jni_dependency_factory.h"
 #include "android_webview/browser/net/aw_url_request_context_getter.h"
+#include "android_webview/browser/net/init_native_callback.h"
 #include "base/prefs/pref_registry_simple.h"
 #include "base/prefs/pref_service.h"
 #include "base/prefs/pref_service_builder.h"
@@ -16,6 +17,7 @@
 #include "components/user_prefs/user_prefs.h"
 #include "components/visitedlink/browser/visitedlink_master.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/cookie_store_factory.h"
 #include "content/public/browser/resource_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
@@ -32,7 +34,9 @@
 class AwResourceContext : public content::ResourceContext {
  public:
   explicit AwResourceContext(net::URLRequestContextGetter* getter)
-      : getter_(getter) {}
+      : getter_(getter) {
+    DCHECK(getter_);
+  }
   virtual ~AwResourceContext() {}
 
   // content::ResourceContext implementation.
@@ -90,12 +94,18 @@
   return static_cast<AwBrowserContext*>(web_contents->GetBrowserContext());
 }
 
-void AwBrowserContext::InitializeBeforeThreadCreation() {
-  DCHECK(!url_request_context_getter_.get());
-  url_request_context_getter_ = new AwURLRequestContextGetter(this);
-}
-
 void AwBrowserContext::PreMainMessageLoopRun() {
+  cookie_store_ = content::CreatePersistentCookieStore(
+      GetPath().Append(FILE_PATH_LITERAL("Cookies")),
+      true,
+      NULL,
+      NULL);
+  cookie_store_->GetCookieMonster()->SetPersistSessionCookies(true);
+  url_request_context_getter_ =
+      new AwURLRequestContextGetter(GetPath(), cookie_store_.get());
+
+  DidCreateCookieMonster(cookie_store_->GetCookieMonster());
+
   visitedlink_master_.reset(
       new visitedlink::VisitedLinkMaster(this, this, false));
   visitedlink_master_->Init();
@@ -108,9 +118,14 @@
 
 net::URLRequestContextGetter* AwBrowserContext::CreateRequestContext(
     content::ProtocolHandlerMap* protocol_handlers) {
-  CHECK(url_request_context_getter_.get());
+  // This function cannot actually create the request context because
+  // there is a reentrant dependency on GetResourceContext() via
+  // content::StoragePartitionImplMap::Create(). This is not fixable
+  // until http://crbug.com/159193. Until then, assert that the context
+  // has already been allocated and just handle setting the protocol_handlers.
+  DCHECK(url_request_context_getter_);
   url_request_context_getter_->SetProtocolHandlers(protocol_handlers);
-  return url_request_context_getter_.get();
+  return url_request_context_getter_;
 }
 
 net::URLRequestContextGetter*
@@ -118,8 +133,8 @@
     const base::FilePath& partition_path,
     bool in_memory,
     content::ProtocolHandlerMap* protocol_handlers) {
-  CHECK(url_request_context_getter_.get());
-  return url_request_context_getter_.get();
+  NOTREACHED();
+  return NULL;
 }
 
 AwQuotaManagerBridge* AwBrowserContext::GetQuotaManagerBridge() {
@@ -205,12 +220,12 @@
 AwBrowserContext::GetMediaRequestContextForStoragePartition(
     const base::FilePath& partition_path,
     bool in_memory) {
-  return GetRequestContext();
+  NOTREACHED();
+  return NULL;
 }
 
 content::ResourceContext* AwBrowserContext::GetResourceContext() {
   if (!resource_context_) {
-    CHECK(url_request_context_getter_.get());
     resource_context_.reset(
         new AwResourceContext(url_request_context_getter_.get()));
   }
diff --git a/android_webview/browser/aw_browser_context.h b/android_webview/browser/aw_browser_context.h
index 8d1ff97..1654d9a 100644
--- a/android_webview/browser/aw_browser_context.h
+++ b/android_webview/browser/aw_browser_context.h
@@ -21,15 +21,19 @@
 
 class GURL;
 
-namespace visitedlink {
-class VisitedLinkMaster;
-}
-
 namespace content {
 class ResourceContext;
 class WebContents;
 }
 
+namespace net {
+class CookieStore;
+}
+
+namespace visitedlink {
+class VisitedLinkMaster;
+}
+
 namespace android_webview {
 
 class AwFormDatabaseService;
@@ -53,9 +57,6 @@
   static AwBrowserContext* FromWebContents(
       content::WebContents* web_contents);
 
-  // Called before BrowserThreads are created.
-  void InitializeBeforeThreadCreation();
-
   // Maps to BrowserMainParts::PreMainMessageLoopRun.
   void PreMainMessageLoopRun();
 
@@ -107,6 +108,7 @@
   base::FilePath context_storage_path_;
 
   JniDependencyFactory* native_factory_;
+  scoped_refptr<net::CookieStore> cookie_store_;
   scoped_refptr<AwURLRequestContextGetter> url_request_context_getter_;
   scoped_refptr<content::GeolocationPermissionContext>
       geolocation_permission_context_;
diff --git a/android_webview/browser/aw_browser_main_parts.cc b/android_webview/browser/aw_browser_main_parts.cc
index b54e65b..8cd57c3 100644
--- a/android_webview/browser/aw_browser_main_parts.cc
+++ b/android_webview/browser/aw_browser_main_parts.cc
@@ -44,8 +44,6 @@
 }
 
 int AwBrowserMainParts::PreCreateThreads() {
-  browser_context_->InitializeBeforeThreadCreation();
-
   ui::ResourceBundle::InitSharedInstanceLocaleOnly(
       l10n_util::GetDefaultLocale(), NULL);
 
diff --git a/android_webview/browser/aw_javascript_dialog_manager.cc b/android_webview/browser/aw_javascript_dialog_manager.cc
index 1690eac..d9ec1c1 100644
--- a/android_webview/browser/aw_javascript_dialog_manager.cc
+++ b/android_webview/browser/aw_javascript_dialog_manager.cc
@@ -21,6 +21,7 @@
     content::JavaScriptMessageType message_type,
     const string16& message_text,
     const string16& default_prompt_text,
+    bool user_gesture,
     const DialogClosedCallback& callback,
     bool* did_suppress_message) {
   AwContentsClientBridgeBase* bridge =
diff --git a/android_webview/browser/aw_javascript_dialog_manager.h b/android_webview/browser/aw_javascript_dialog_manager.h
index f8db746..0abb432 100644
--- a/android_webview/browser/aw_javascript_dialog_manager.h
+++ b/android_webview/browser/aw_javascript_dialog_manager.h
@@ -22,6 +22,7 @@
       content::JavaScriptMessageType message_type,
       const string16& message_text,
       const string16& default_prompt_text,
+      bool user_gesture,
       const DialogClosedCallback& callback,
       bool* did_suppress_message) OVERRIDE;
   virtual void RunBeforeUnloadDialog(
diff --git a/android_webview/browser/aw_pref_store.cc b/android_webview/browser/aw_pref_store.cc
index 613a847..0720ee9 100644
--- a/android_webview/browser/aw_pref_store.cc
+++ b/android_webview/browser/aw_pref_store.cc
@@ -29,8 +29,8 @@
   observers_.RemoveObserver(observer);
 }
 
-size_t AwPrefStore::NumberOfObservers() const {
-  return observers_.size();
+bool AwPrefStore::HasObservers() const {
+  return observers_.might_have_observers();
 }
 
 bool AwPrefStore::IsInitializationComplete() const {
diff --git a/android_webview/browser/aw_pref_store.h b/android_webview/browser/aw_pref_store.h
index e636e1b..8581a03 100644
--- a/android_webview/browser/aw_pref_store.h
+++ b/android_webview/browser/aw_pref_store.h
@@ -26,7 +26,7 @@
                         const base::Value** result) const OVERRIDE;
   virtual void AddObserver(PrefStore::Observer* observer) OVERRIDE;
   virtual void RemoveObserver(PrefStore::Observer* observer) OVERRIDE;
-  virtual size_t NumberOfObservers() const OVERRIDE;
+  virtual bool HasObservers() const OVERRIDE;
   virtual bool IsInitializationComplete() const OVERRIDE;
 
   // PersistentPrefStore overrides:
diff --git a/android_webview/browser/in_process_view_renderer.cc b/android_webview/browser/in_process_view_renderer.cc
index 3f935ff..49be2cc 100644
--- a/android_webview/browser/in_process_view_renderer.cc
+++ b/android_webview/browser/in_process_view_renderer.cc
@@ -23,8 +23,8 @@
 #include "content/public/browser/web_contents.h"
 #include "gpu/command_buffer/service/in_process_command_buffer.h"
 #include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
 #include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkDevice.h"
 #include "third_party/skia/include/core/SkGraphics.h"
 #include "third_party/skia/include/core/SkPicture.h"
 #include "ui/gfx/skia_util.h"
@@ -91,7 +91,7 @@
                      bitmap_info.stride);
     bitmap.setPixels(pixels);
 
-    SkDevice device(bitmap);
+    SkBitmapDevice device(bitmap);
     SkCanvas canvas(&device);
     canvas.translate(-scroll_x, -scroll_y);
     succeeded = renderer.Run(&canvas);
@@ -505,7 +505,7 @@
                    pixels->height,
                    pixels->row_bytes);
   bitmap.setPixels(pixels->pixels);
-  SkDevice device(bitmap);
+  SkBitmapDevice device(bitmap);
   SkCanvas canvas(&device);
   canvas.setMatrix(matrix);
 
@@ -802,7 +802,7 @@
   // means block_invalidates_ must still be true.
   DCHECK(block_invalidates_);
   if (compositor_needs_continuous_invalidate_ && compositor_) {
-    SkDevice device(SkBitmap::kARGB_8888_Config, 1, 1);
+    SkBitmapDevice device(SkBitmap::kARGB_8888_Config, 1, 1);
     SkCanvas canvas(&device);
     block_invalidates_ = true;
     CompositeSW(&canvas);
diff --git a/android_webview/browser/net/aw_url_request_context_getter.cc b/android_webview/browser/net/aw_url_request_context_getter.cc
index 4017c18..4a5ea41 100644
--- a/android_webview/browser/net/aw_url_request_context_getter.cc
+++ b/android_webview/browser/net/aw_url_request_context_getter.cc
@@ -6,7 +6,6 @@
 
 #include <vector>
 
-#include "android_webview/browser/aw_browser_context.h"
 #include "android_webview/browser/aw_content_browser_client.h"
 #include "android_webview/browser/aw_request_interceptor.h"
 #include "android_webview/browser/net/aw_network_delegate.h"
@@ -34,42 +33,110 @@
 
 namespace android_webview {
 
+
+namespace {
+
+void PopulateNetworkSessionParams(
+    net::URLRequestContext* context,
+    net::HttpNetworkSession::Params* params) {
+  params->host_resolver = context->host_resolver();
+  params->cert_verifier = context->cert_verifier();
+  params->server_bound_cert_service = context->server_bound_cert_service();
+  params->transport_security_state = context->transport_security_state();
+  params->proxy_service = context->proxy_service();
+  params->ssl_config_service = context->ssl_config_service();
+  params->http_auth_handler_factory = context->http_auth_handler_factory();
+  params->network_delegate = context->network_delegate();
+  params->http_server_properties = context->http_server_properties();
+  params->net_log = context->net_log();
+}
+
+scoped_ptr<net::URLRequestJobFactory> CreateJobFactory(
+    content::ProtocolHandlerMap* protocol_handlers) {
+  scoped_ptr<AwURLRequestJobFactory> aw_job_factory(new AwURLRequestJobFactory);
+  bool set_protocol = aw_job_factory->SetProtocolHandler(
+      chrome::kFileScheme,
+      new net::FileProtocolHandler(
+          content::BrowserThread::GetBlockingPool()->
+              GetTaskRunnerWithShutdownBehavior(
+                  base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)));
+  DCHECK(set_protocol);
+  set_protocol = aw_job_factory->SetProtocolHandler(
+      chrome::kDataScheme, new net::DataProtocolHandler());
+  DCHECK(set_protocol);
+  set_protocol = aw_job_factory->SetProtocolHandler(
+      chrome::kBlobScheme, (*protocol_handlers)[chrome::kBlobScheme].release());
+  DCHECK(set_protocol);
+  set_protocol = aw_job_factory->SetProtocolHandler(
+      chrome::kFileSystemScheme,
+      (*protocol_handlers)[chrome::kFileSystemScheme].release());
+  DCHECK(set_protocol);
+  set_protocol = aw_job_factory->SetProtocolHandler(
+      chrome::kChromeUIScheme,
+      (*protocol_handlers)[chrome::kChromeUIScheme].release());
+  DCHECK(set_protocol);
+  set_protocol = aw_job_factory->SetProtocolHandler(
+      chrome::kChromeDevToolsScheme,
+      (*protocol_handlers)[chrome::kChromeDevToolsScheme].release());
+  DCHECK(set_protocol);
+  protocol_handlers->clear();
+
+  // Create a chain of URLRequestJobFactories. The handlers will be invoked
+  // in the order in which they appear in the protocol_handlers vector.
+  typedef std::vector<net::URLRequestJobFactory::ProtocolHandler*>
+      ProtocolHandlerVector;
+  ProtocolHandlerVector protocol_interceptors;
+
+  // Note that even though the content:// scheme handler is created here,
+  // it cannot be used by child processes until access to it is granted via
+  // ChildProcessSecurityPolicy::GrantScheme(). This is done in
+  // AwContentBrowserClient.
+  protocol_interceptors.push_back(
+      CreateAndroidContentProtocolHandler().release());
+  protocol_interceptors.push_back(
+      CreateAndroidAssetFileProtocolHandler().release());
+  // The AwRequestInterceptor must come after the content and asset file job
+  // factories. This for WebViewClassic compatibility where it was not
+  // possible to intercept resource loads to resolvable content:// and
+  // file:// URIs.
+  // This logical dependency is also the reason why the Content
+  // ProtocolHandler has to be added as a ProtocolInterceptJobFactory rather
+  // than via SetProtocolHandler.
+  protocol_interceptors.push_back(new AwRequestInterceptor());
+
+  // The chain of responsibility will execute the handlers in reverse to the
+  // order in which the elements of the chain are created.
+  scoped_ptr<net::URLRequestJobFactory> job_factory(aw_job_factory.Pass());
+  for (ProtocolHandlerVector::reverse_iterator
+           i = protocol_interceptors.rbegin();
+       i != protocol_interceptors.rend();
+       ++i) {
+    job_factory.reset(new net::ProtocolInterceptJobFactory(
+        job_factory.Pass(), make_scoped_ptr(*i)));
+  }
+
+  return job_factory.Pass();
+}
+
+}  // namespace
+
 AwURLRequestContextGetter::AwURLRequestContextGetter(
-    AwBrowserContext* browser_context)
-    : browser_context_(browser_context),
+    const base::FilePath& partition_path, net::CookieStore* cookie_store)
+    : partition_path_(partition_path),
+      cookie_store_(cookie_store),
       proxy_config_service_(net::ProxyService::CreateSystemProxyConfigService(
           GetNetworkTaskRunner(),
           NULL /* Ignored on Android */)) {
   // CreateSystemProxyConfigService for Android must be called on main thread.
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  // All network stack initialization is done on the synchronous Init call when
-  // the IO thread is created.
-  BrowserThread::SetDelegate(BrowserThread::IO, this);
 }
 
 AwURLRequestContextGetter::~AwURLRequestContextGetter() {
-  BrowserThread::SetDelegate(BrowserThread::IO, NULL);
 }
 
-void AwURLRequestContextGetter::Init() {
+void AwURLRequestContextGetter::InitializeURLRequestContext() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-
-  cookie_store_ = content::CreatePersistentCookieStore(
-      browser_context_->GetPath().Append(FILE_PATH_LITERAL("Cookies")),
-      true,
-      NULL,
-      NULL);
-  cookie_store_->GetCookieMonster()->SetPersistSessionCookies(true);
-
-  // The CookieMonster must be passed here so it happens synchronously to
-  // the main thread initialization (to avoid race condition in another
-  // thread trying to access the CookieManager API).
-  DidCreateCookieMonster(cookie_store_->GetCookieMonster());
-}
-
-void AwURLRequestContextGetter::InitAsync() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK(!url_request_context_);
 
   net::URLRequestContextBuilder builder;
   builder.set_user_agent(content::GetUserAgent(GURL()));
@@ -90,102 +157,28 @@
         switches::kDisableSimpleCache)) {
     cache_type = net::CACHE_BACKEND_BLOCKFILE;
   }
-  PopulateNetworkSessionParams(&network_session_params);
+  PopulateNetworkSessionParams(url_request_context_.get(),
+                               &network_session_params);
   net::HttpCache* main_cache = new net::HttpCache(
       network_session_params,
       new net::HttpCache::DefaultBackend(
           net::DISK_CACHE,
           cache_type,
-          browser_context_->GetPath().Append(FILE_PATH_LITERAL("Cache")),
+          partition_path_.Append(FILE_PATH_LITERAL("Cache")),
           10 * 1024 * 1024,  // 10M
           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE)));
   main_http_factory_.reset(main_cache);
   url_request_context_->set_http_transaction_factory(main_cache);
-  url_request_context_->set_cookie_store(cookie_store_.get());
-}
+  url_request_context_->set_cookie_store(cookie_store_);
 
-void AwURLRequestContextGetter::PopulateNetworkSessionParams(
-    net::HttpNetworkSession::Params* params) {
-  net::URLRequestContext* context = url_request_context_.get();
-  params->host_resolver = context->host_resolver();
-  params->cert_verifier = context->cert_verifier();
-  params->server_bound_cert_service = context->server_bound_cert_service();
-  params->transport_security_state = context->transport_security_state();
-  params->proxy_service = context->proxy_service();
-  params->ssl_config_service = context->ssl_config_service();
-  params->http_auth_handler_factory = context->http_auth_handler_factory();
-  params->network_delegate = context->network_delegate();
-  params->http_server_properties = context->http_server_properties();
-  params->net_log = context->net_log();
+  job_factory_ = CreateJobFactory(&protocol_handlers_);
+  url_request_context_->set_job_factory(job_factory_.get());
 }
 
 net::URLRequestContext* AwURLRequestContextGetter::GetURLRequestContext() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  if (!job_factory_) {
-    scoped_ptr<AwURLRequestJobFactory> job_factory(new AwURLRequestJobFactory);
-    bool set_protocol = job_factory->SetProtocolHandler(
-        chrome::kFileScheme,
-        new net::FileProtocolHandler(
-            content::BrowserThread::GetBlockingPool()->
-                GetTaskRunnerWithShutdownBehavior(
-                    base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)));
-    DCHECK(set_protocol);
-    set_protocol = job_factory->SetProtocolHandler(
-        chrome::kDataScheme, new net::DataProtocolHandler());
-    DCHECK(set_protocol);
-    set_protocol = job_factory->SetProtocolHandler(
-        chrome::kBlobScheme, protocol_handlers_[chrome::kBlobScheme].release());
-    DCHECK(set_protocol);
-    set_protocol = job_factory->SetProtocolHandler(
-        chrome::kFileSystemScheme,
-        protocol_handlers_[chrome::kFileSystemScheme].release());
-    DCHECK(set_protocol);
-    set_protocol = job_factory->SetProtocolHandler(
-        chrome::kChromeUIScheme,
-        protocol_handlers_[chrome::kChromeUIScheme].release());
-    DCHECK(set_protocol);
-    set_protocol = job_factory->SetProtocolHandler(
-        chrome::kChromeDevToolsScheme,
-        protocol_handlers_[chrome::kChromeDevToolsScheme].release());
-    DCHECK(set_protocol);
-    protocol_handlers_.clear();
-
-    // Create a chain of URLRequestJobFactories. The handlers will be invoked
-    // in the order in which they appear in the protocol_handlers vector.
-    typedef std::vector<net::URLRequestJobFactory::ProtocolHandler*>
-        ProtocolHandlerVector;
-    ProtocolHandlerVector protocol_interceptors;
-
-    // Note that even though the content:// scheme handler is created here,
-    // it cannot be used by child processes until access to it is granted via
-    // ChildProcessSecurityPolicy::GrantScheme(). This is done in
-    // AwContentBrowserClient.
-    protocol_interceptors.push_back(
-        CreateAndroidContentProtocolHandler().release());
-    protocol_interceptors.push_back(
-        CreateAndroidAssetFileProtocolHandler().release());
-    // The AwRequestInterceptor must come after the content and asset file job
-    // factories. This for WebViewClassic compatibility where it was not
-    // possible to intercept resource loads to resolvable content:// and
-    // file:// URIs.
-    // This logical dependency is also the reason why the Content
-    // ProtocolHandler has to be added as a ProtocolInterceptJobFactory rather
-    // than via SetProtocolHandler.
-    protocol_interceptors.push_back(new AwRequestInterceptor());
-
-    // The chain of responsibility will execute the handlers in reverse to the
-    // order in which the elements of the chain are created.
-    job_factory_ = job_factory.PassAs<net::URLRequestJobFactory>();
-    for (ProtocolHandlerVector::reverse_iterator
-             i = protocol_interceptors.rbegin();
-         i != protocol_interceptors.rend();
-         ++i) {
-      job_factory_.reset(new net::ProtocolInterceptJobFactory(
-          job_factory_.Pass(), make_scoped_ptr(*i)));
-    }
-
-    url_request_context_->set_job_factory(job_factory_.get());
-  }
+  if (!url_request_context_)
+    InitializeURLRequestContext();
 
   return url_request_context_.get();
 }
diff --git a/android_webview/browser/net/aw_url_request_context_getter.h b/android_webview/browser/net/aw_url_request_context_getter.h
index 93d59aa..c16098e 100644
--- a/android_webview/browser/net/aw_url_request_context_getter.h
+++ b/android_webview/browser/net/aw_url_request_context_getter.h
@@ -7,14 +7,15 @@
 
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
+#include "base/files/file_path.h"
 #include "base/memory/scoped_ptr.h"
-#include "content/public/browser/browser_thread_delegate.h"
 #include "content/public/browser/content_browser_client.h"
 #include "net/http/http_network_session.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "net/url_request/url_request_job_factory.h"
 
 namespace net {
+class CookieStore;
 class HttpTransactionFactory;
 class ProxyConfigService;
 class URLRequestContext;
@@ -23,21 +24,15 @@
 
 namespace android_webview {
 
-class AwBrowserContext;
 class AwNetworkDelegate;
 
-class AwURLRequestContextGetter : public net::URLRequestContextGetter,
-                                  public content::BrowserThreadDelegate {
+class AwURLRequestContextGetter : public net::URLRequestContextGetter {
  public:
-  explicit AwURLRequestContextGetter(AwBrowserContext* browser_context);
+  AwURLRequestContextGetter(const base::FilePath& partition_path,
+                            net::CookieStore* cookie_store);
 
   void InitializeOnNetworkThread();
 
-  // content::BrowserThreadDelegate implementation.
-  virtual void Init() OVERRIDE;
-  virtual void InitAsync() OVERRIDE;
-  virtual void CleanUp() OVERRIDE {}
-
   // net::URLRequestContextGetter implementation.
   virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE;
   virtual scoped_refptr<base::SingleThreadTaskRunner>
@@ -45,7 +40,6 @@
 
  private:
   friend class AwBrowserContext;
-
   virtual ~AwURLRequestContextGetter();
 
   // Prior to GetURLRequestContext() being called, SetProtocolHandlers() is
@@ -56,9 +50,9 @@
   // on the UI thread while |job_factory_| must be created on the IO thread.
   void SetProtocolHandlers(content::ProtocolHandlerMap* protocol_handlers);
 
-  void PopulateNetworkSessionParams(net::HttpNetworkSession::Params* params);
+  void InitializeURLRequestContext();
 
-  AwBrowserContext* browser_context_;  // weak
+  const base::FilePath partition_path_;
   scoped_refptr<net::CookieStore> cookie_store_;
   scoped_ptr<net::URLRequestContext> url_request_context_;
   scoped_ptr<net::ProxyConfigService> proxy_config_service_;
diff --git a/android_webview/browser/renderer_host/aw_render_view_host_ext.cc b/android_webview/browser/renderer_host/aw_render_view_host_ext.cc
index 95f2358..78d6cf4 100644
--- a/android_webview/browser/renderer_host/aw_render_view_host_ext.cc
+++ b/android_webview/browser/renderer_host/aw_render_view_host_ext.cc
@@ -96,6 +96,10 @@
   }
 }
 
+void AwRenderViewHostExt::SetJsOnlineProperty(bool network_up) {
+  Send(new AwViewMsg_SetJsOnlineProperty(network_up));
+}
+
 void AwRenderViewHostExt::RenderViewCreated(
     content::RenderViewHost* render_view_host) {
   Send(new AwViewMsg_SetBackgroundColor(web_contents()->GetRoutingID(),
diff --git a/android_webview/browser/renderer_host/aw_render_view_host_ext.h b/android_webview/browser/renderer_host/aw_render_view_host_ext.h
index da9bcac..3007f15 100644
--- a/android_webview/browser/renderer_host/aw_render_view_host_ext.h
+++ b/android_webview/browser/renderer_host/aw_render_view_host_ext.h
@@ -72,6 +72,7 @@
   // the meta viewport tag.
   void SetInitialPageScale(double page_scale_factor);
   void SetBackgroundColor(SkColor c);
+  void SetJsOnlineProperty(bool network_up);
 
  private:
   // content::WebContentsObserver implementation.
diff --git a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
index 2e59cd5..1bab66b 100644
--- a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
+++ b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc
@@ -21,6 +21,7 @@
 #include "content/public/browser/resource_throttle.h"
 #include "content/public/common/url_constants.h"
 #include "net/base/load_flags.h"
+#include "net/http/http_response_headers.h"
 #include "net/url_request/url_request.h"
 
 using android_webview::AwContentsIoThreadClient;
@@ -93,6 +94,13 @@
 }
 
 void IoThreadClientThrottle::WillStartRequest(bool* defer) {
+  // TODO(sgurun): This block can be removed when crbug.com/277937 is fixed.
+  if (route_id_ < 1) {
+    // OPTIONS is used for preflighted requests which are generated internally.
+    DCHECK_EQ("OPTIONS", request_->method());
+    return;
+  }
+  DCHECK(child_id_);
   if (!MaybeDeferRequest(defer)) {
     MaybeBlockRequest();
   }
@@ -223,7 +231,7 @@
       (resource_type == ResourceType::MAIN_FRAME ||
        (resource_type == ResourceType::SUB_FRAME &&
         !request->url().SchemeIs(chrome::kHttpScheme) &&
-        !request->url().SchemeIs(chrome::kHttpsScheme)));
+        !request->url().SchemeIs(content::kHttpsScheme)));
   if (allow_intercepting) {
     throttles->push_back(InterceptNavigationDelegate::CreateThrottleFor(
         request));
@@ -245,10 +253,16 @@
   std::string mime_type;
   int64 content_length = request->GetExpectedContentSize();
 
-  request->GetResponseHeaderByName("content-disposition", &content_disposition);
   request->extra_request_headers().GetHeader(
       net::HttpRequestHeaders::kUserAgent, &user_agent);
-  request->GetMimeType(&mime_type);
+
+
+  net::HttpResponseHeaders* response_headers = request->response_headers();
+  if (response_headers) {
+    response_headers->GetNormalizedHeader("content-disposition",
+        &content_disposition);
+    response_headers->GetMimeType(&mime_type);
+  }
 
   request->Cancel();
 
diff --git a/android_webview/common/render_view_messages.h b/android_webview/common/render_view_messages.h
index 69f826c..96d1758 100644
--- a/android_webview/common/render_view_messages.h
+++ b/android_webview/common/render_view_messages.h
@@ -71,6 +71,9 @@
 IPC_MESSAGE_ROUTED1(AwViewMsg_SetBackgroundColor,
                     SkColor);
 
+IPC_MESSAGE_CONTROL1(AwViewMsg_SetJsOnlineProperty,
+                     bool /* network_up */)
+
 //-----------------------------------------------------------------------------
 // RenderView messages
 // These are messages sent from the renderer to the browser process.
@@ -87,4 +90,3 @@
 // Sent whenever the page scale factor (as seen by RenderView) is changed.
 IPC_MESSAGE_ROUTED1(AwViewHostMsg_PageScaleFactorChanged,
                     float /* page_scale_factor */)
-
diff --git a/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java b/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
index 7c3ab76..c0657a3 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
@@ -5,12 +5,11 @@
 package org.chromium.android_webview;
 
 import android.content.Context;
-import android.content.SharedPreferences;
 
 import org.chromium.base.PathUtils;
 import org.chromium.base.ThreadUtils;
 import org.chromium.content.app.LibraryLoader;
-import org.chromium.content.browser.AndroidBrowserProcess;
+import org.chromium.content.browser.BrowserStartupController;
 import org.chromium.content.common.ProcessInitException;
 
 /**
@@ -46,12 +45,9 @@
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                try {
-                    LibraryLoader.ensureInitialized();
-                    AndroidBrowserProcess.init(context,
-                            AndroidBrowserProcess.MAX_RENDERERS_SINGLE_PROCESS);
-                } catch (ProcessInitException e) {
-                    throw new RuntimeException("Cannot initialize WebView", e);
+                if( !BrowserStartupController.get(context).startBrowserProcessesSync(
+                            BrowserStartupController.MAX_RENDERERS_SINGLE_PROCESS)) {
+                    throw new RuntimeException("Cannot initialize WebView");
                 }
             }
         });
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 df560c4..18ba0cc 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -1639,6 +1639,11 @@
             mAwAutofillManagerDelegate.hideAutofillPopup();
     }
 
+    public void setNetworkAvailable(boolean networkUp) {
+        if (mNativeAwContents == 0) return;
+        nativeSetJsOnlineProperty(mNativeAwContents, networkUp);
+    }
+
     //--------------------------------------------------------------------------------------------
     //  Methods called from native via JNI
     //--------------------------------------------------------------------------------------------
@@ -1917,4 +1922,6 @@
 
     private native void nativeInvokeGeolocationCallback(
             int nativeAwContents, boolean value, String requestingFrame);
+
+    private native void nativeSetJsOnlineProperty(int nativeAwContents, boolean networkUp);
 }
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 260ea96..da09a1a 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
@@ -92,13 +92,6 @@
     }
 
     @Override
-    public boolean addNewContents(int nativeSourceWebContents, int nativeWebContents,
-            int disposition, Rect initialPosition, boolean userGesture) {
-        // This is overridden native side; see the other addNewContents overload.
-        throw new RuntimeException("Impossible");
-    }
-
-    @Override
     public void closeContents() {
         mContentsClient.onCloseWindow();
     }
diff --git a/android_webview/javatests/AndroidManifest.xml b/android_webview/javatests/AndroidManifest.xml
index aef384c..f236d4d 100644
--- a/android_webview/javatests/AndroidManifest.xml
+++ b/android_webview/javatests/AndroidManifest.xml
@@ -9,8 +9,6 @@
          needed when building test cases. -->
     <application android:hardwareAccelerated="false">
         <uses-library android:name="android.test.runner" />
-        <provider android:name="TestContentProvider"
-            android:authorities="org.chromium.android_webview.test.TestContentProvider" />
     </application>
     <!-- TODO(joth): Change minSdkVersion to 16 when crbug/161864 lands. -->
     <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17" />
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
index 640175c..8e4cef5 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
@@ -25,6 +25,7 @@
 import java.io.InputStream;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.List;
 import java.util.Random;
 
@@ -38,16 +39,23 @@
 
         public static class ShouldInterceptRequestHelper extends CallbackHelper {
             private List<String> mShouldInterceptRequestUrls = new ArrayList<String>();
+            private ConcurrentHashMap<String, InterceptedRequestData> mReturnValusByUrls
+                = new ConcurrentHashMap<String, InterceptedRequestData>();
             // This is read from the IO thread, so needs to be marked volatile.
             private volatile InterceptedRequestData mShouldInterceptRequestReturnValue = null;
             void setReturnValue(InterceptedRequestData value) {
                 mShouldInterceptRequestReturnValue = value;
             }
+            void setReturnValueForUrl(String url, InterceptedRequestData value) {
+                mReturnValusByUrls.put(url, value);
+            }
             public List<String> getUrls() {
                 assert getCallCount() > 0;
                 return mShouldInterceptRequestUrls;
             }
-            public InterceptedRequestData getReturnValue() {
+            public InterceptedRequestData getReturnValue(String url) {
+                InterceptedRequestData value = mReturnValusByUrls.get(url);
+                if (value != null) return value;
                 return mShouldInterceptRequestReturnValue;
             }
             public void notifyCalled(String url) {
@@ -72,7 +80,7 @@
 
         @Override
         public InterceptedRequestData shouldInterceptRequest(String url) {
-            InterceptedRequestData returnValue = mShouldInterceptRequestHelper.getReturnValue();
+            InterceptedRequestData returnValue = mShouldInterceptRequestHelper.getReturnValue(url);
             mShouldInterceptRequestHelper.notifyCalled(url);
             return returnValue;
         }
@@ -349,6 +357,33 @@
 
     @SmallTest
     @Feature({"AndroidWebView"})
+    public void testOnReceivedErrorCallback() throws Throwable {
+        mShouldInterceptRequestHelper.setReturnValue(new InterceptedRequestData(null, null, null));
+        OnReceivedErrorHelper onReceivedErrorHelper = mContentsClient.getOnReceivedErrorHelper();
+        int onReceivedErrorHelperCallCount = onReceivedErrorHelper.getCallCount();
+        loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), "foo://bar");
+        onReceivedErrorHelper.waitForCallback(onReceivedErrorHelperCallCount, 1);
+    }
+
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    public void testNoOnReceivedErrorCallback() throws Throwable {
+        final String imagePath = "/" + CommonResources.FAVICON_FILENAME;
+        final String imageUrl = mWebServer.setResponseBase64(imagePath,
+                CommonResources.FAVICON_DATA_BASE64, CommonResources.getImagePngHeaders(true));
+        final String pageWithImage =
+                addPageToTestServer(mWebServer, "/page_with_image.html",
+                        CommonResources.getOnImageLoadedHtml(CommonResources.FAVICON_FILENAME));
+        mShouldInterceptRequestHelper.setReturnValueForUrl(
+                imageUrl, new InterceptedRequestData(null, null, null));
+        OnReceivedErrorHelper onReceivedErrorHelper = mContentsClient.getOnReceivedErrorHelper();
+        int onReceivedErrorHelperCallCount = onReceivedErrorHelper.getCallCount();
+        loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), pageWithImage);
+        assertEquals(onReceivedErrorHelperCallCount, onReceivedErrorHelper.getCallCount());
+    }
+
+    @SmallTest
+    @Feature({"AndroidWebView"})
     public void testCalledForIframe() throws Throwable {
         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
         final String pageWithIframe = addPageToTestServer(mWebServer, "/page_with_iframe.html",
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
index 455d30b..bb9d68f 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
@@ -15,6 +15,7 @@
 import android.util.Pair;
 
 import org.chromium.android_webview.AwContents;
+import org.chromium.android_webview.AwSettings;
 import org.chromium.android_webview.test.util.CommonResources;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
@@ -57,7 +58,7 @@
             return mContentDisposition;
         }
 
-        public String getMimeType() {
+       public String getMimeType() {
             assert getCallCount() > 0;
             return mMimeType;
         }
@@ -392,4 +393,62 @@
             if (webServer != null) webServer.shutdown();
         }
     }
+
+    @Feature({"AndroidWebView", "setNetworkAvailable"})
+    @SmallTest
+    public void testSetNetworkAvailable() throws Throwable {
+        AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
+        AwContents awContents = testView.getAwContents();
+        String SCRIPT = "navigator.onLine";
+
+        enableJavaScriptOnUiThread(awContents);
+        loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), "about:blank");
+
+        // Default to "online".
+        assertEquals("true", executeJavaScriptAndWaitForResult(awContents, mContentsClient,
+              SCRIPT));
+
+        // Forcing "offline".
+        awContents.setNetworkAvailable(false);
+        assertEquals("false", executeJavaScriptAndWaitForResult(awContents, mContentsClient,
+              SCRIPT));
+
+        // Forcing "online".
+        awContents.setNetworkAvailable(true);
+        assertEquals("true", executeJavaScriptAndWaitForResult(awContents, mContentsClient,
+              SCRIPT));
+    }
+
+
+    static class JavaScriptObject {
+        private CallbackHelper mCallbackHelper;
+        public JavaScriptObject(CallbackHelper callbackHelper) {
+            mCallbackHelper = callbackHelper;
+        }
+
+        public void run() {
+            mCallbackHelper.notifyCalled();
+        }
+    };
+
+    @Feature({"AndroidWebView", "JavaBridge"})
+    @SmallTest
+    public void testJavaBridge() throws Throwable {
+        final AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
+        final CallbackHelper callback = new CallbackHelper();
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                AwContents awContents = testView.getAwContents();
+                AwSettings awSettings = awContents.getSettings();
+                awSettings.setJavaScriptEnabled(true);
+                awContents.addPossiblyUnsafeJavascriptInterface(
+                        new JavaScriptObject(callback), "bridge", null);
+                awContents.evaluateJavaScriptEvenIfNotYetNavigated(
+                        "javascript:window.bridge.run();");
+            }
+        });
+        callback.waitForCallback(0, 1, 20, TimeUnit.SECONDS);
+    }
 }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
index 3180969..d82af60 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java
@@ -39,6 +39,7 @@
 import org.chromium.net.test.util.TestWebServer;
 import org.chromium.ui.gfx.DeviceDisplayInfo;
 
+import java.io.File;
 import java.util.concurrent.Callable;
 import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
@@ -467,6 +468,7 @@
     }
 
     class AwSettingsDomStorageEnabledTestHelper extends AwSettingsTestHelper<Boolean> {
+        private static final String TEST_FILE = "webview/localStorage.html";
         private static final String NO_LOCAL_STORAGE = "No localStorage";
         private static final String HAS_LOCAL_STORAGE = "Has localStorage";
 
@@ -474,6 +476,7 @@
                 AwContents awContents,
                 TestAwContentsClient contentViewClient) throws Throwable {
             super(awContents, contentViewClient, true);
+            AwSettingsTest.assertFileIsReadable(UrlUtils.getTestFilePath(TEST_FILE));
         }
 
         @Override
@@ -500,7 +503,7 @@
         protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
             // It is not permitted to access localStorage from data URLs in WebKit,
             // that is why a standalone page must be used.
-            loadUrlSync(UrlUtils.getTestFileUrl("webview/localStorage.html"));
+            loadUrlSync(UrlUtils.getTestFileUrl(TEST_FILE));
             assertEquals(
                 value == ENABLED ? HAS_LOCAL_STORAGE : NO_LOCAL_STORAGE,
                 getTitleOnUiThread());
@@ -508,6 +511,7 @@
     }
 
     class AwSettingsDatabaseTestHelper extends AwSettingsTestHelper<Boolean> {
+        private static final String TEST_FILE = "webview/database_access.html";
         private static final String NO_DATABASE = "No database";
         private static final String HAS_DATABASE = "Has database";
 
@@ -515,6 +519,7 @@
                 AwContents awContents,
                 TestAwContentsClient contentViewClient) throws Throwable {
             super(awContents, contentViewClient, true);
+            AwSettingsTest.assertFileIsReadable(UrlUtils.getTestFilePath(TEST_FILE));
         }
 
         @Override
@@ -542,7 +547,7 @@
             // It seems accessing the database through a data scheme is not
             // supported, and fails with a DOM exception (likely a cross-domain
             // violation).
-            loadUrlSync(UrlUtils.getTestFileUrl("webview/database_access.html"));
+            loadUrlSync(UrlUtils.getTestFileUrl(TEST_FILE));
             assertEquals(
                 value == ENABLED ? HAS_DATABASE : NO_DATABASE,
                 getTitleOnUiThread());
@@ -550,14 +555,18 @@
     }
 
     class AwSettingsUniversalAccessFromFilesTestHelper extends AwSettingsTestHelper<Boolean> {
+        private static final String TEST_CONTAINER_FILE = "webview/iframe_access.html";
+        private static final String TEST_FILE = "webview/hello_world.html";
         private static final String ACCESS_DENIED_TITLE = "Exception";
 
         AwSettingsUniversalAccessFromFilesTestHelper(
                 AwContents awContents,
                 TestAwContentsClient contentViewClient) throws Throwable {
             super(awContents, contentViewClient, true);
-            mIframeContainerUrl = UrlUtils.getTestFileUrl("webview/iframe_access.html");
-            mIframeUrl = UrlUtils.getTestFileUrl("webview/hello_world.html");
+            AwSettingsTest.assertFileIsReadable(UrlUtils.getTestFilePath(TEST_CONTAINER_FILE));
+            AwSettingsTest.assertFileIsReadable(UrlUtils.getTestFilePath(TEST_FILE));
+            mIframeContainerUrl = UrlUtils.getTestFileUrl(TEST_CONTAINER_FILE);
+            mIframeUrl = UrlUtils.getTestFileUrl(TEST_FILE);
             // The value of the setting depends on the SDK version.
             mAwSettings.setAllowUniversalAccessFromFileURLs(false);
             // If universal access is true, the value of file access doesn't
@@ -599,14 +608,18 @@
     }
 
     class AwSettingsFileAccessFromFilesIframeTestHelper extends AwSettingsTestHelper<Boolean> {
+        private static final String TEST_CONTAINER_FILE = "webview/iframe_access.html";
+        private static final String TEST_FILE = "webview/hello_world.html";
         private static final String ACCESS_DENIED_TITLE = "Exception";
 
         AwSettingsFileAccessFromFilesIframeTestHelper(
                 AwContents awContents,
                 TestAwContentsClient contentViewClient) throws Throwable {
             super(awContents, contentViewClient, true);
-            mIframeContainerUrl = UrlUtils.getTestFileUrl("webview/iframe_access.html");
-            mIframeUrl = UrlUtils.getTestFileUrl("webview/hello_world.html");
+            AwSettingsTest.assertFileIsReadable(UrlUtils.getTestFilePath(TEST_CONTAINER_FILE));
+            AwSettingsTest.assertFileIsReadable(UrlUtils.getTestFilePath(TEST_FILE));
+            mIframeContainerUrl = UrlUtils.getTestFileUrl(TEST_CONTAINER_FILE);
+            mIframeUrl = UrlUtils.getTestFileUrl(TEST_FILE);
             mAwSettings.setAllowUniversalAccessFromFileURLs(false);
             // The value of the setting depends on the SDK version.
             mAwSettings.setAllowFileAccessFromFileURLs(false);
@@ -645,6 +658,7 @@
     }
 
     class AwSettingsFileAccessFromFilesXhrTestHelper extends AwSettingsTestHelper<Boolean> {
+        private static final String TEST_FILE = "webview/xhr_access.html";
         private static final String ACCESS_GRANTED_TITLE = "Hello, World!";
         private static final String ACCESS_DENIED_TITLE = "Exception";
 
@@ -652,7 +666,8 @@
                 AwContents awContents,
                 TestAwContentsClient contentViewClient) throws Throwable {
             super(awContents, contentViewClient, true);
-            mXhrContainerUrl = UrlUtils.getTestFileUrl("webview/xhr_access.html");
+            assertFileIsReadable(UrlUtils.getTestFilePath(TEST_FILE));
+            mXhrContainerUrl = UrlUtils.getTestFileUrl(TEST_FILE);
             mAwSettings.setAllowUniversalAccessFromFileURLs(false);
             // The value of the setting depends on the SDK version.
             mAwSettings.setAllowFileAccessFromFileURLs(false);
@@ -690,6 +705,7 @@
     }
 
     class AwSettingsFileUrlAccessTestHelper extends AwSettingsTestHelper<Boolean> {
+        private static final String TEST_FILE = "webview/hello_world.html";
         private static final String ACCESS_GRANTED_TITLE = "Hello, World!";
 
         AwSettingsFileUrlAccessTestHelper(
@@ -698,6 +714,7 @@
                 int startIndex) throws Throwable {
             super(awContents, contentViewClient, true);
             mIndex = startIndex;
+            AwSettingsTest.assertFileIsReadable(UrlUtils.getTestFilePath(TEST_FILE));
         }
 
         @Override
@@ -723,7 +740,7 @@
         @Override
         protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
             // Use query parameters to avoid hitting a cached page.
-            String fileUrl = UrlUtils.getTestFileUrl("webview/hello_world.html?id=" + mIndex);
+            String fileUrl = UrlUtils.getTestFileUrl(TEST_FILE + "?id=" + mIndex);
             mIndex += 2;
             if (value == ENABLED) {
                 loadUrlSync(fileUrl);
@@ -1617,12 +1634,8 @@
         helper.ensureSettingHasInitialValue();
     }
 
-    /**
-     * @SmallTest
-     * @Feature({"AndroidWebView", "Preferences"})
-     * crbug.com/277077
-     */
-    @DisabledTest
+    @SmallTest
+    @Feature({"AndroidWebView", "Preferences"})
     public void testUniversalAccessFromFilesWithTwoViews() throws Throwable {
         ViewPair views = createViews();
         runPerViewSettingsTest(
@@ -1634,14 +1647,12 @@
 
     // This test verifies that local image resources can be loaded from file:
     // URLs regardless of file access state.
-    /**
-     * @SmallTest
-     * @Feature({"AndroidWebView", "Preferences"})
-     * crbug.com/277077
-     */
-    @DisabledTest
+    @SmallTest
+    @Feature({"AndroidWebView", "Preferences"})
     public void testFileAccessFromFilesImage() throws Throwable {
-        final String imageContainerUrl = UrlUtils.getTestFileUrl("webview/image_access.html");
+        final String testFile = "webview/image_access.html";
+        assertFileIsReadable(UrlUtils.getTestFilePath(testFile));
+        final String imageContainerUrl = UrlUtils.getTestFileUrl(testFile);
         final String imageHeight = "16";
         final TestAwContentsClient contentClient = new TestAwContentsClient();
         final AwTestContainerView testContainerView =
@@ -1655,12 +1666,8 @@
         assertEquals(imageHeight, getTitleOnUiThread(awContents));
     }
 
-    /**
-     * @SmallTest
-     * @Feature({"AndroidWebView", "Preferences"})
-     * crbug.com/277077
-     */
-    @DisabledTest
+    @SmallTest
+    @Feature({"AndroidWebView", "Preferences"})
     public void testFileAccessFromFilesIframeWithTwoViews() throws Throwable {
         ViewPair views = createViews();
         runPerViewSettingsTest(
@@ -1670,12 +1677,8 @@
                 views.getContents1(), views.getClient1()));
     }
 
-    /**
-     * @SmallTest
-     * @Feature({"AndroidWebView", "Preferences"})
-     * crbug.com/277077
-     */
-    @DisabledTest
+    @SmallTest
+    @Feature({"AndroidWebView", "Preferences"})
     public void testFileAccessFromFilesXhrWithTwoViews() throws Throwable {
         ViewPair views = createViews();
         runPerViewSettingsTest(
@@ -2591,6 +2594,18 @@
             client1);
     }
 
+    static void assertFileIsReadable(String filePath) {
+        File file = new File(filePath);
+        try {
+            assertTrue("Test file \"" + filePath + "\" is not readable." +
+                    "Please make sure that files from android_webview/test/data/device_files/ " +
+                    "has been pushed to the device before testing",
+                    file.canRead());
+        } catch (SecurityException e) {
+            fail("Got a SecurityException for \"" + filePath + "\": " + e.toString());
+        }
+    }
+
     /**
      * Verifies the number of resource requests made to the content provider.
      * @param resource Resource name
diff --git a/android_webview/lib/main/aw_main_delegate.cc b/android_webview/lib/main/aw_main_delegate.cc
index 6843a74..05911db 100644
--- a/android_webview/lib/main/aw_main_delegate.cc
+++ b/android_webview/lib/main/aw_main_delegate.cc
@@ -52,8 +52,7 @@
 
   CommandLine* cl = CommandLine::ForCurrentProcess();
   cl->AppendSwitch(switches::kEnableBeginFrameScheduling);
-  if (!cl->HasSwitch("disable-map-image"))
-    cl->AppendSwitch(cc::switches::kUseMapImage);
+  cl->AppendSwitch(cc::switches::kEnableMapImage);
 
   // WebView uses the Android system's scrollbars and overscroll glow.
   cl->AppendSwitch(switches::kHideScrollbars);
diff --git a/android_webview/libwebviewchromium.target.darwin-arm.mk b/android_webview/libwebviewchromium.target.darwin-arm.mk
index b0ce2c2..614232d 100644
--- a/android_webview/libwebviewchromium.target.darwin-arm.mk
+++ b/android_webview/libwebviewchromium.target.darwin-arm.mk
@@ -30,57 +30,50 @@
 	$(call intermediates-dir-for,STATIC_LIBRARIES,content_content_common_gyp)/content_content_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,components_tracing_gyp)/components_tracing_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ipc_ipc_gyp)/ipc_ipc_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_gyp)/media_media_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,net_net_gyp)/net_net_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,base_base_i18n_gyp)/base_base_i18n_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_icu_icui18n_gyp)/icui18n.stamp \
+	$(call intermediates-dir-for,GYP,third_party_icu_system_icu_gyp)/system_icu.stamp \
+	$(call intermediates-dir-for,GYP,third_party_icu_icuuc_gyp)/icuuc.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,crypto_crypto_gyp)/crypto_crypto_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_openssl_openssl_gyp)/third_party_openssl_openssl_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,sdch_sdch_gyp)/sdch_sdch_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_zlib_zlib_gyp)/third_party_zlib_zlib_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,url_url_lib_gyp)/url_url_lib_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_icu_icudata_gyp)/icudata.stamp \
+	$(call intermediates-dir-for,GYP,net_net_resources_gyp)/net_resources.stamp \
+	$(call intermediates-dir-for,GYP,net_net_jni_headers_gyp)/net_jni_headers.stamp \
 	$(call intermediates-dir-for,GYP,skia_skia_gyp)/skia.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_library_gyp)/skia_skia_library_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_opts_gyp)/skia_skia_opts_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_opts_neon_gyp)/skia_skia_opts_neon_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_zlib_zlib_gyp)/third_party_zlib_zlib_gyp.a \
 	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_skia_config_gyp)/blink_skia_config.stamp \
 	$(call intermediates-dir-for,GYP,third_party_expat_expat_gyp)/expat.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_freetype_ft2_gyp)/third_party_freetype_ft2_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_chrome_gyp)/skia_skia_chrome_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_chrome_opts_gyp)/skia_skia_chrome_opts_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_opus_opus_gyp)/third_party_opus_opus_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,base_base_i18n_gyp)/base_base_i18n_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_icu_icui18n_gyp)/icui18n.stamp \
-	$(call intermediates-dir-for,GYP,third_party_icu_system_icu_gyp)/system_icu.stamp \
-	$(call intermediates-dir-for,GYP,third_party_icu_icuuc_gyp)/icuuc.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,net_net_gyp)/net_net_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,sdch_sdch_gyp)/sdch_sdch_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,url_url_lib_gyp)/url_url_lib_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_icu_icudata_gyp)/icudata.stamp \
-	$(call intermediates-dir-for,GYP,net_net_resources_gyp)/net_resources.stamp \
-	$(call intermediates-dir-for,GYP,net_net_jni_headers_gyp)/net_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_libpng_libpng_gyp)/third_party_libpng_libpng_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_shell_dialogs_gyp)/ui_shell_dialogs_gyp.a \
 	$(call intermediates-dir-for,GYP,ui_base_strings_ui_strings_gyp)/ui_strings.stamp \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_libpng_libpng_gyp)/third_party_libpng_libpng_gyp.a \
 	$(call intermediates-dir-for,GYP,ui_ui_resources_gyp)/ui_resources.stamp \
 	$(call intermediates-dir-for,GYP,third_party_libjpeg_libjpeg_gyp)/libjpeg.stamp \
 	$(call intermediates-dir-for,GYP,ui_ui_jni_headers_gyp)/ui_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_common_gyp)/gpu_command_buffer_common_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_gles2_utils_gyp)/gpu_command_buffer_gles2_utils_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_khronos_khronos_headers_gyp)/khronos_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_shared_memory_support_gyp)/media_shared_memory_support_gyp.a \
-	$(call intermediates-dir-for,GYP,media_media_android_jni_headers_gyp)/media_android_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_player_android_gyp)/media_player_android_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_mesa_mesa_headers_gyp)/mesa_headers.stamp \
-	$(call intermediates-dir-for,GYP,ui_gl_gl_jni_headers_gyp)/gl_jni_headers.stamp \
-	$(call intermediates-dir-for,GYP,ui_gl_surface_texture_jni_headers_gyp)/surface_texture_jni_headers.stamp \
-	$(call intermediates-dir-for,GYP,ui_gl_surface_jni_headers_gyp)/surface_jni_headers.stamp \
-	$(call intermediates-dir-for,GYP,media_video_capture_android_jni_headers_gyp)/video_capture_android_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_shell_dialogs_gyp)/ui_shell_dialogs_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,webkit_common_user_agent_user_agent_gyp)/webkit_common_user_agent_user_agent_gyp.a \
 	$(call intermediates-dir-for,GYP,webkit_common_user_agent_webkit_version_gyp)/webkit_version.stamp \
 	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,cc_cc_gyp)/cc_cc_gyp.a \
 	$(call intermediates-dir-for,GYP,gpu_gpu_gyp)/gpu.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_client_gyp)/gpu_command_buffer_client_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_common_gyp)/gpu_command_buffer_common_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_gles2_utils_gyp)/gpu_command_buffer_gles2_utils_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_khronos_khronos_headers_gyp)/khronos_headers.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_service_gyp)/gpu_command_buffer_service_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_disk_cache_proto_gyp)/gpu_disk_cache_proto_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_mesa_mesa_headers_gyp)/mesa_headers.stamp \
+	$(call intermediates-dir-for,GYP,ui_gl_gl_jni_headers_gyp)/gl_jni_headers.stamp \
+	$(call intermediates-dir-for,GYP,ui_gl_surface_jni_headers_gyp)/surface_jni_headers.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_angle_dx11_src_translator_glsl_gyp)/third_party_angle_dx11_src_translator_glsl_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_angle_dx11_src_translator_common_gyp)/third_party_angle_dx11_src_translator_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_angle_dx11_src_preprocessor_gyp)/third_party_angle_dx11_src_preprocessor_gyp.a \
@@ -89,6 +82,12 @@
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gles2_cmd_helper_gyp)/gpu_gles2_cmd_helper_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_config_gyp)/gpu_gpu_config_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_ipc_gyp)/gpu_gpu_ipc_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_gyp)/media_media_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_opus_opus_gyp)/third_party_opus_opus_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_shared_memory_support_gyp)/media_shared_memory_support_gyp.a \
+	$(call intermediates-dir-for,GYP,media_media_android_jni_headers_gyp)/media_android_jni_headers.stamp \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_player_android_gyp)/media_player_android_gyp.a \
+	$(call intermediates-dir-for,GYP,media_video_capture_android_jni_headers_gyp)/video_capture_android_jni_headers.stamp \
 	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_WebKit_Source_web_blink_common_gyp)/third_party_WebKit_Source_web_blink_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_WebKit_Source_wtf_wtf_gyp)/third_party_WebKit_Source_wtf_wtf_gyp.a \
@@ -302,9 +301,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -387,9 +386,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -470,34 +469,30 @@
 	content_content_common_gyp \
 	components_tracing_gyp \
 	ipc_ipc_gyp \
-	media_media_gyp \
+	net_net_gyp \
+	base_base_i18n_gyp \
 	crypto_crypto_gyp \
 	third_party_openssl_openssl_gyp \
+	sdch_sdch_gyp \
+	third_party_zlib_zlib_gyp \
+	url_url_lib_gyp \
 	skia_skia_library_gyp \
 	skia_skia_opts_gyp \
 	skia_skia_opts_neon_gyp \
-	third_party_zlib_zlib_gyp \
 	third_party_freetype_ft2_gyp \
 	skia_skia_chrome_gyp \
 	skia_skia_chrome_opts_gyp \
-	third_party_opus_opus_gyp \
-	ui_ui_gyp \
-	base_base_i18n_gyp \
-	net_net_gyp \
-	sdch_sdch_gyp \
-	url_url_lib_gyp \
-	third_party_libpng_libpng_gyp \
-	gpu_command_buffer_common_gyp \
-	gpu_command_buffer_gles2_utils_gyp \
-	media_shared_memory_support_gyp \
-	media_player_android_gyp \
-	ui_gl_gl_gyp \
 	ui_shell_dialogs_gyp \
+	ui_ui_gyp \
+	third_party_libpng_libpng_gyp \
 	webkit_common_user_agent_user_agent_gyp \
 	cc_cc_gyp \
 	gpu_command_buffer_client_gyp \
+	gpu_command_buffer_common_gyp \
+	gpu_command_buffer_gles2_utils_gyp \
 	gpu_command_buffer_service_gyp \
 	gpu_disk_cache_proto_gyp \
+	ui_gl_gl_gyp \
 	third_party_angle_dx11_src_translator_glsl_gyp \
 	third_party_angle_dx11_src_translator_common_gyp \
 	third_party_angle_dx11_src_preprocessor_gyp \
@@ -506,6 +501,10 @@
 	gpu_gles2_cmd_helper_gyp \
 	gpu_gpu_config_gyp \
 	gpu_gpu_ipc_gyp \
+	media_media_gyp \
+	third_party_opus_opus_gyp \
+	media_shared_memory_support_gyp \
+	media_player_android_gyp \
 	third_party_WebKit_Source_web_blink_common_gyp \
 	third_party_WebKit_Source_wtf_wtf_gyp \
 	v8_tools_gyp_v8_base_arm_gyp \
@@ -589,12 +588,12 @@
 	liblog \
 	libcutils \
 	libandroid \
-	libOpenSLES \
-	libexpat \
-	libjnigraphics \
 	libicui18n \
 	libicuuc \
+	libexpat \
+	libjnigraphics \
 	libjpeg \
+	libOpenSLES \
 	libstlport \
 	libdl
 
diff --git a/android_webview/libwebviewchromium.target.darwin-mips.mk b/android_webview/libwebviewchromium.target.darwin-mips.mk
index 1fa1577..379ad69 100644
--- a/android_webview/libwebviewchromium.target.darwin-mips.mk
+++ b/android_webview/libwebviewchromium.target.darwin-mips.mk
@@ -30,56 +30,49 @@
 	$(call intermediates-dir-for,STATIC_LIBRARIES,content_content_common_gyp)/content_content_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,components_tracing_gyp)/components_tracing_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ipc_ipc_gyp)/ipc_ipc_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_gyp)/media_media_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,net_net_gyp)/net_net_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,base_base_i18n_gyp)/base_base_i18n_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_icu_icui18n_gyp)/icui18n.stamp \
+	$(call intermediates-dir-for,GYP,third_party_icu_system_icu_gyp)/system_icu.stamp \
+	$(call intermediates-dir-for,GYP,third_party_icu_icuuc_gyp)/icuuc.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,crypto_crypto_gyp)/crypto_crypto_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_openssl_openssl_gyp)/third_party_openssl_openssl_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,sdch_sdch_gyp)/sdch_sdch_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_zlib_zlib_gyp)/third_party_zlib_zlib_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,url_url_lib_gyp)/url_url_lib_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_icu_icudata_gyp)/icudata.stamp \
+	$(call intermediates-dir-for,GYP,net_net_resources_gyp)/net_resources.stamp \
+	$(call intermediates-dir-for,GYP,net_net_jni_headers_gyp)/net_jni_headers.stamp \
 	$(call intermediates-dir-for,GYP,skia_skia_gyp)/skia.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_library_gyp)/skia_skia_library_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_opts_gyp)/skia_skia_opts_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_zlib_zlib_gyp)/third_party_zlib_zlib_gyp.a \
 	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_skia_config_gyp)/blink_skia_config.stamp \
 	$(call intermediates-dir-for,GYP,third_party_expat_expat_gyp)/expat.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_freetype_ft2_gyp)/third_party_freetype_ft2_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_chrome_gyp)/skia_skia_chrome_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_chrome_opts_gyp)/skia_skia_chrome_opts_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_opus_opus_gyp)/third_party_opus_opus_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,base_base_i18n_gyp)/base_base_i18n_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_icu_icui18n_gyp)/icui18n.stamp \
-	$(call intermediates-dir-for,GYP,third_party_icu_system_icu_gyp)/system_icu.stamp \
-	$(call intermediates-dir-for,GYP,third_party_icu_icuuc_gyp)/icuuc.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,net_net_gyp)/net_net_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,sdch_sdch_gyp)/sdch_sdch_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,url_url_lib_gyp)/url_url_lib_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_icu_icudata_gyp)/icudata.stamp \
-	$(call intermediates-dir-for,GYP,net_net_resources_gyp)/net_resources.stamp \
-	$(call intermediates-dir-for,GYP,net_net_jni_headers_gyp)/net_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_libpng_libpng_gyp)/third_party_libpng_libpng_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_shell_dialogs_gyp)/ui_shell_dialogs_gyp.a \
 	$(call intermediates-dir-for,GYP,ui_base_strings_ui_strings_gyp)/ui_strings.stamp \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_libpng_libpng_gyp)/third_party_libpng_libpng_gyp.a \
 	$(call intermediates-dir-for,GYP,ui_ui_resources_gyp)/ui_resources.stamp \
 	$(call intermediates-dir-for,GYP,third_party_libjpeg_libjpeg_gyp)/libjpeg.stamp \
 	$(call intermediates-dir-for,GYP,ui_ui_jni_headers_gyp)/ui_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_common_gyp)/gpu_command_buffer_common_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_gles2_utils_gyp)/gpu_command_buffer_gles2_utils_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_khronos_khronos_headers_gyp)/khronos_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_shared_memory_support_gyp)/media_shared_memory_support_gyp.a \
-	$(call intermediates-dir-for,GYP,media_media_android_jni_headers_gyp)/media_android_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_player_android_gyp)/media_player_android_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_mesa_mesa_headers_gyp)/mesa_headers.stamp \
-	$(call intermediates-dir-for,GYP,ui_gl_gl_jni_headers_gyp)/gl_jni_headers.stamp \
-	$(call intermediates-dir-for,GYP,ui_gl_surface_texture_jni_headers_gyp)/surface_texture_jni_headers.stamp \
-	$(call intermediates-dir-for,GYP,ui_gl_surface_jni_headers_gyp)/surface_jni_headers.stamp \
-	$(call intermediates-dir-for,GYP,media_video_capture_android_jni_headers_gyp)/video_capture_android_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_shell_dialogs_gyp)/ui_shell_dialogs_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,webkit_common_user_agent_user_agent_gyp)/webkit_common_user_agent_user_agent_gyp.a \
 	$(call intermediates-dir-for,GYP,webkit_common_user_agent_webkit_version_gyp)/webkit_version.stamp \
 	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,cc_cc_gyp)/cc_cc_gyp.a \
 	$(call intermediates-dir-for,GYP,gpu_gpu_gyp)/gpu.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_client_gyp)/gpu_command_buffer_client_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_common_gyp)/gpu_command_buffer_common_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_gles2_utils_gyp)/gpu_command_buffer_gles2_utils_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_khronos_khronos_headers_gyp)/khronos_headers.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_service_gyp)/gpu_command_buffer_service_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_disk_cache_proto_gyp)/gpu_disk_cache_proto_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_mesa_mesa_headers_gyp)/mesa_headers.stamp \
+	$(call intermediates-dir-for,GYP,ui_gl_gl_jni_headers_gyp)/gl_jni_headers.stamp \
+	$(call intermediates-dir-for,GYP,ui_gl_surface_jni_headers_gyp)/surface_jni_headers.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_angle_dx11_src_translator_glsl_gyp)/third_party_angle_dx11_src_translator_glsl_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_angle_dx11_src_translator_common_gyp)/third_party_angle_dx11_src_translator_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_angle_dx11_src_preprocessor_gyp)/third_party_angle_dx11_src_preprocessor_gyp.a \
@@ -88,6 +81,12 @@
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gles2_cmd_helper_gyp)/gpu_gles2_cmd_helper_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_config_gyp)/gpu_gpu_config_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_ipc_gyp)/gpu_gpu_ipc_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_gyp)/media_media_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_opus_opus_gyp)/third_party_opus_opus_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_shared_memory_support_gyp)/media_shared_memory_support_gyp.a \
+	$(call intermediates-dir-for,GYP,media_media_android_jni_headers_gyp)/media_android_jni_headers.stamp \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_player_android_gyp)/media_player_android_gyp.a \
+	$(call intermediates-dir-for,GYP,media_video_capture_android_jni_headers_gyp)/video_capture_android_jni_headers.stamp \
 	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_WebKit_Source_web_blink_common_gyp)/third_party_WebKit_Source_web_blink_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_WebKit_Source_wtf_wtf_gyp)/third_party_WebKit_Source_wtf_wtf_gyp.a \
@@ -297,9 +296,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -381,9 +380,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -460,33 +459,29 @@
 	content_content_common_gyp \
 	components_tracing_gyp \
 	ipc_ipc_gyp \
-	media_media_gyp \
+	net_net_gyp \
+	base_base_i18n_gyp \
 	crypto_crypto_gyp \
 	third_party_openssl_openssl_gyp \
+	sdch_sdch_gyp \
+	third_party_zlib_zlib_gyp \
+	url_url_lib_gyp \
 	skia_skia_library_gyp \
 	skia_skia_opts_gyp \
-	third_party_zlib_zlib_gyp \
 	third_party_freetype_ft2_gyp \
 	skia_skia_chrome_gyp \
 	skia_skia_chrome_opts_gyp \
-	third_party_opus_opus_gyp \
-	ui_ui_gyp \
-	base_base_i18n_gyp \
-	net_net_gyp \
-	sdch_sdch_gyp \
-	url_url_lib_gyp \
-	third_party_libpng_libpng_gyp \
-	gpu_command_buffer_common_gyp \
-	gpu_command_buffer_gles2_utils_gyp \
-	media_shared_memory_support_gyp \
-	media_player_android_gyp \
-	ui_gl_gl_gyp \
 	ui_shell_dialogs_gyp \
+	ui_ui_gyp \
+	third_party_libpng_libpng_gyp \
 	webkit_common_user_agent_user_agent_gyp \
 	cc_cc_gyp \
 	gpu_command_buffer_client_gyp \
+	gpu_command_buffer_common_gyp \
+	gpu_command_buffer_gles2_utils_gyp \
 	gpu_command_buffer_service_gyp \
 	gpu_disk_cache_proto_gyp \
+	ui_gl_gl_gyp \
 	third_party_angle_dx11_src_translator_glsl_gyp \
 	third_party_angle_dx11_src_translator_common_gyp \
 	third_party_angle_dx11_src_preprocessor_gyp \
@@ -495,6 +490,10 @@
 	gpu_gles2_cmd_helper_gyp \
 	gpu_gpu_config_gyp \
 	gpu_gpu_ipc_gyp \
+	media_media_gyp \
+	third_party_opus_opus_gyp \
+	media_shared_memory_support_gyp \
+	media_player_android_gyp \
 	third_party_WebKit_Source_web_blink_common_gyp \
 	third_party_WebKit_Source_wtf_wtf_gyp \
 	v8_tools_gyp_v8_base_mipsel_gyp \
@@ -575,12 +574,12 @@
 	liblog \
 	libcutils \
 	libandroid \
-	libOpenSLES \
-	libexpat \
-	libjnigraphics \
 	libicui18n \
 	libicuuc \
+	libexpat \
+	libjnigraphics \
 	libjpeg \
+	libOpenSLES \
 	libstlport \
 	libdl
 
diff --git a/android_webview/libwebviewchromium.target.darwin-x86.mk b/android_webview/libwebviewchromium.target.darwin-x86.mk
index c387e0b..f341e4b 100644
--- a/android_webview/libwebviewchromium.target.darwin-x86.mk
+++ b/android_webview/libwebviewchromium.target.darwin-x86.mk
@@ -30,62 +30,50 @@
 	$(call intermediates-dir-for,STATIC_LIBRARIES,content_content_common_gyp)/content_content_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,components_tracing_gyp)/components_tracing_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ipc_ipc_gyp)/ipc_ipc_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_gyp)/media_media_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,net_net_gyp)/net_net_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,base_base_i18n_gyp)/base_base_i18n_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_icu_icui18n_gyp)/icui18n.stamp \
+	$(call intermediates-dir-for,GYP,third_party_icu_system_icu_gyp)/system_icu.stamp \
+	$(call intermediates-dir-for,GYP,third_party_icu_icuuc_gyp)/icuuc.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,crypto_crypto_gyp)/crypto_crypto_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_openssl_openssl_gyp)/third_party_openssl_openssl_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,sdch_sdch_gyp)/sdch_sdch_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_zlib_zlib_gyp)/third_party_zlib_zlib_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,url_url_lib_gyp)/url_url_lib_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_icu_icudata_gyp)/icudata.stamp \
+	$(call intermediates-dir-for,GYP,net_net_resources_gyp)/net_resources.stamp \
+	$(call intermediates-dir-for,GYP,net_net_jni_headers_gyp)/net_jni_headers.stamp \
 	$(call intermediates-dir-for,GYP,skia_skia_gyp)/skia.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_library_gyp)/skia_skia_library_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_opts_gyp)/skia_skia_opts_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_opts_ssse3_gyp)/skia_skia_opts_ssse3_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_zlib_zlib_gyp)/third_party_zlib_zlib_gyp.a \
 	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_skia_config_gyp)/blink_skia_config.stamp \
 	$(call intermediates-dir-for,GYP,third_party_expat_expat_gyp)/expat.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_freetype_ft2_gyp)/third_party_freetype_ft2_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_chrome_gyp)/skia_skia_chrome_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_chrome_opts_gyp)/skia_skia_chrome_opts_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_opus_opus_gyp)/third_party_opus_opus_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,base_base_i18n_gyp)/base_base_i18n_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_icu_icui18n_gyp)/icui18n.stamp \
-	$(call intermediates-dir-for,GYP,third_party_icu_system_icu_gyp)/system_icu.stamp \
-	$(call intermediates-dir-for,GYP,third_party_icu_icuuc_gyp)/icuuc.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,net_net_gyp)/net_net_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,sdch_sdch_gyp)/sdch_sdch_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,url_url_lib_gyp)/url_url_lib_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_icu_icudata_gyp)/icudata.stamp \
-	$(call intermediates-dir-for,GYP,net_net_resources_gyp)/net_resources.stamp \
-	$(call intermediates-dir-for,GYP,net_net_jni_headers_gyp)/net_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_libpng_libpng_gyp)/third_party_libpng_libpng_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_shell_dialogs_gyp)/ui_shell_dialogs_gyp.a \
 	$(call intermediates-dir-for,GYP,ui_base_strings_ui_strings_gyp)/ui_strings.stamp \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_libpng_libpng_gyp)/third_party_libpng_libpng_gyp.a \
 	$(call intermediates-dir-for,GYP,ui_ui_resources_gyp)/ui_resources.stamp \
 	$(call intermediates-dir-for,GYP,third_party_libjpeg_libjpeg_gyp)/libjpeg.stamp \
 	$(call intermediates-dir-for,GYP,ui_ui_jni_headers_gyp)/ui_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_common_gyp)/gpu_command_buffer_common_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_gles2_utils_gyp)/gpu_command_buffer_gles2_utils_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_khronos_khronos_headers_gyp)/khronos_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_shared_memory_support_gyp)/media_shared_memory_support_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_shared_memory_support_sse_gyp)/media_shared_memory_support_sse_gyp.a \
-	$(call intermediates-dir-for,GYP,media_media_android_jni_headers_gyp)/media_android_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_player_android_gyp)/media_player_android_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_mesa_mesa_headers_gyp)/mesa_headers.stamp \
-	$(call intermediates-dir-for,GYP,ui_gl_gl_jni_headers_gyp)/gl_jni_headers.stamp \
-	$(call intermediates-dir-for,GYP,ui_gl_surface_texture_jni_headers_gyp)/surface_texture_jni_headers.stamp \
-	$(call intermediates-dir-for,GYP,ui_gl_surface_jni_headers_gyp)/surface_jni_headers.stamp \
-	$(call intermediates-dir-for,GYP,media_video_capture_android_jni_headers_gyp)/video_capture_android_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_asm_gyp)/media_media_asm_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_mmx_gyp)/media_media_mmx_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_sse_gyp)/media_media_sse_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_sse2_gyp)/media_media_sse2_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_shell_dialogs_gyp)/ui_shell_dialogs_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,webkit_common_user_agent_user_agent_gyp)/webkit_common_user_agent_user_agent_gyp.a \
 	$(call intermediates-dir-for,GYP,webkit_common_user_agent_webkit_version_gyp)/webkit_version.stamp \
 	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,cc_cc_gyp)/cc_cc_gyp.a \
 	$(call intermediates-dir-for,GYP,gpu_gpu_gyp)/gpu.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_client_gyp)/gpu_command_buffer_client_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_common_gyp)/gpu_command_buffer_common_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_gles2_utils_gyp)/gpu_command_buffer_gles2_utils_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_khronos_khronos_headers_gyp)/khronos_headers.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_service_gyp)/gpu_command_buffer_service_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_disk_cache_proto_gyp)/gpu_disk_cache_proto_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_mesa_mesa_headers_gyp)/mesa_headers.stamp \
+	$(call intermediates-dir-for,GYP,ui_gl_gl_jni_headers_gyp)/gl_jni_headers.stamp \
+	$(call intermediates-dir-for,GYP,ui_gl_surface_jni_headers_gyp)/surface_jni_headers.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_angle_dx11_src_translator_glsl_gyp)/third_party_angle_dx11_src_translator_glsl_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_angle_dx11_src_translator_common_gyp)/third_party_angle_dx11_src_translator_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_angle_dx11_src_preprocessor_gyp)/third_party_angle_dx11_src_preprocessor_gyp.a \
@@ -94,6 +82,17 @@
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gles2_cmd_helper_gyp)/gpu_gles2_cmd_helper_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_config_gyp)/gpu_gpu_config_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_ipc_gyp)/gpu_gpu_ipc_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_gyp)/media_media_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_opus_opus_gyp)/third_party_opus_opus_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_shared_memory_support_gyp)/media_shared_memory_support_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_shared_memory_support_sse_gyp)/media_shared_memory_support_sse_gyp.a \
+	$(call intermediates-dir-for,GYP,media_media_android_jni_headers_gyp)/media_android_jni_headers.stamp \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_player_android_gyp)/media_player_android_gyp.a \
+	$(call intermediates-dir-for,GYP,media_video_capture_android_jni_headers_gyp)/video_capture_android_jni_headers.stamp \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_asm_gyp)/media_media_asm_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_mmx_gyp)/media_media_mmx_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_sse_gyp)/media_media_sse_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_sse2_gyp)/media_media_sse2_gyp.a \
 	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_WebKit_Source_web_blink_common_gyp)/third_party_WebKit_Source_web_blink_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_WebKit_Source_wtf_wtf_gyp)/third_party_WebKit_Source_wtf_wtf_gyp.a \
@@ -308,9 +307,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -396,9 +395,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -474,39 +473,30 @@
 	content_content_common_gyp \
 	components_tracing_gyp \
 	ipc_ipc_gyp \
-	media_media_gyp \
+	net_net_gyp \
+	base_base_i18n_gyp \
 	crypto_crypto_gyp \
 	third_party_openssl_openssl_gyp \
+	sdch_sdch_gyp \
+	third_party_zlib_zlib_gyp \
+	url_url_lib_gyp \
 	skia_skia_library_gyp \
 	skia_skia_opts_gyp \
 	skia_skia_opts_ssse3_gyp \
-	third_party_zlib_zlib_gyp \
 	third_party_freetype_ft2_gyp \
 	skia_skia_chrome_gyp \
 	skia_skia_chrome_opts_gyp \
-	third_party_opus_opus_gyp \
-	ui_ui_gyp \
-	base_base_i18n_gyp \
-	net_net_gyp \
-	sdch_sdch_gyp \
-	url_url_lib_gyp \
-	third_party_libpng_libpng_gyp \
-	gpu_command_buffer_common_gyp \
-	gpu_command_buffer_gles2_utils_gyp \
-	media_shared_memory_support_gyp \
-	media_shared_memory_support_sse_gyp \
-	media_player_android_gyp \
-	ui_gl_gl_gyp \
-	media_media_asm_gyp \
-	media_media_mmx_gyp \
-	media_media_sse_gyp \
-	media_media_sse2_gyp \
 	ui_shell_dialogs_gyp \
+	ui_ui_gyp \
+	third_party_libpng_libpng_gyp \
 	webkit_common_user_agent_user_agent_gyp \
 	cc_cc_gyp \
 	gpu_command_buffer_client_gyp \
+	gpu_command_buffer_common_gyp \
+	gpu_command_buffer_gles2_utils_gyp \
 	gpu_command_buffer_service_gyp \
 	gpu_disk_cache_proto_gyp \
+	ui_gl_gl_gyp \
 	third_party_angle_dx11_src_translator_glsl_gyp \
 	third_party_angle_dx11_src_translator_common_gyp \
 	third_party_angle_dx11_src_preprocessor_gyp \
@@ -515,6 +505,15 @@
 	gpu_gles2_cmd_helper_gyp \
 	gpu_gpu_config_gyp \
 	gpu_gpu_ipc_gyp \
+	media_media_gyp \
+	third_party_opus_opus_gyp \
+	media_shared_memory_support_gyp \
+	media_shared_memory_support_sse_gyp \
+	media_player_android_gyp \
+	media_media_asm_gyp \
+	media_media_mmx_gyp \
+	media_media_sse_gyp \
+	media_media_sse2_gyp \
 	third_party_WebKit_Source_web_blink_common_gyp \
 	third_party_WebKit_Source_wtf_wtf_gyp \
 	v8_tools_gyp_v8_base_ia32_gyp \
@@ -596,12 +595,12 @@
 	liblog \
 	libcutils \
 	libandroid \
-	libOpenSLES \
-	libexpat \
-	libjnigraphics \
 	libicui18n \
 	libicuuc \
+	libexpat \
+	libjnigraphics \
 	libjpeg \
+	libOpenSLES \
 	libstlport \
 	libdl
 
diff --git a/android_webview/libwebviewchromium.target.linux-arm.mk b/android_webview/libwebviewchromium.target.linux-arm.mk
index b0ce2c2..614232d 100644
--- a/android_webview/libwebviewchromium.target.linux-arm.mk
+++ b/android_webview/libwebviewchromium.target.linux-arm.mk
@@ -30,57 +30,50 @@
 	$(call intermediates-dir-for,STATIC_LIBRARIES,content_content_common_gyp)/content_content_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,components_tracing_gyp)/components_tracing_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ipc_ipc_gyp)/ipc_ipc_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_gyp)/media_media_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,net_net_gyp)/net_net_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,base_base_i18n_gyp)/base_base_i18n_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_icu_icui18n_gyp)/icui18n.stamp \
+	$(call intermediates-dir-for,GYP,third_party_icu_system_icu_gyp)/system_icu.stamp \
+	$(call intermediates-dir-for,GYP,third_party_icu_icuuc_gyp)/icuuc.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,crypto_crypto_gyp)/crypto_crypto_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_openssl_openssl_gyp)/third_party_openssl_openssl_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,sdch_sdch_gyp)/sdch_sdch_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_zlib_zlib_gyp)/third_party_zlib_zlib_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,url_url_lib_gyp)/url_url_lib_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_icu_icudata_gyp)/icudata.stamp \
+	$(call intermediates-dir-for,GYP,net_net_resources_gyp)/net_resources.stamp \
+	$(call intermediates-dir-for,GYP,net_net_jni_headers_gyp)/net_jni_headers.stamp \
 	$(call intermediates-dir-for,GYP,skia_skia_gyp)/skia.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_library_gyp)/skia_skia_library_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_opts_gyp)/skia_skia_opts_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_opts_neon_gyp)/skia_skia_opts_neon_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_zlib_zlib_gyp)/third_party_zlib_zlib_gyp.a \
 	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_skia_config_gyp)/blink_skia_config.stamp \
 	$(call intermediates-dir-for,GYP,third_party_expat_expat_gyp)/expat.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_freetype_ft2_gyp)/third_party_freetype_ft2_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_chrome_gyp)/skia_skia_chrome_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_chrome_opts_gyp)/skia_skia_chrome_opts_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_opus_opus_gyp)/third_party_opus_opus_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,base_base_i18n_gyp)/base_base_i18n_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_icu_icui18n_gyp)/icui18n.stamp \
-	$(call intermediates-dir-for,GYP,third_party_icu_system_icu_gyp)/system_icu.stamp \
-	$(call intermediates-dir-for,GYP,third_party_icu_icuuc_gyp)/icuuc.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,net_net_gyp)/net_net_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,sdch_sdch_gyp)/sdch_sdch_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,url_url_lib_gyp)/url_url_lib_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_icu_icudata_gyp)/icudata.stamp \
-	$(call intermediates-dir-for,GYP,net_net_resources_gyp)/net_resources.stamp \
-	$(call intermediates-dir-for,GYP,net_net_jni_headers_gyp)/net_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_libpng_libpng_gyp)/third_party_libpng_libpng_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_shell_dialogs_gyp)/ui_shell_dialogs_gyp.a \
 	$(call intermediates-dir-for,GYP,ui_base_strings_ui_strings_gyp)/ui_strings.stamp \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_libpng_libpng_gyp)/third_party_libpng_libpng_gyp.a \
 	$(call intermediates-dir-for,GYP,ui_ui_resources_gyp)/ui_resources.stamp \
 	$(call intermediates-dir-for,GYP,third_party_libjpeg_libjpeg_gyp)/libjpeg.stamp \
 	$(call intermediates-dir-for,GYP,ui_ui_jni_headers_gyp)/ui_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_common_gyp)/gpu_command_buffer_common_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_gles2_utils_gyp)/gpu_command_buffer_gles2_utils_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_khronos_khronos_headers_gyp)/khronos_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_shared_memory_support_gyp)/media_shared_memory_support_gyp.a \
-	$(call intermediates-dir-for,GYP,media_media_android_jni_headers_gyp)/media_android_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_player_android_gyp)/media_player_android_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_mesa_mesa_headers_gyp)/mesa_headers.stamp \
-	$(call intermediates-dir-for,GYP,ui_gl_gl_jni_headers_gyp)/gl_jni_headers.stamp \
-	$(call intermediates-dir-for,GYP,ui_gl_surface_texture_jni_headers_gyp)/surface_texture_jni_headers.stamp \
-	$(call intermediates-dir-for,GYP,ui_gl_surface_jni_headers_gyp)/surface_jni_headers.stamp \
-	$(call intermediates-dir-for,GYP,media_video_capture_android_jni_headers_gyp)/video_capture_android_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_shell_dialogs_gyp)/ui_shell_dialogs_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,webkit_common_user_agent_user_agent_gyp)/webkit_common_user_agent_user_agent_gyp.a \
 	$(call intermediates-dir-for,GYP,webkit_common_user_agent_webkit_version_gyp)/webkit_version.stamp \
 	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,cc_cc_gyp)/cc_cc_gyp.a \
 	$(call intermediates-dir-for,GYP,gpu_gpu_gyp)/gpu.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_client_gyp)/gpu_command_buffer_client_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_common_gyp)/gpu_command_buffer_common_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_gles2_utils_gyp)/gpu_command_buffer_gles2_utils_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_khronos_khronos_headers_gyp)/khronos_headers.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_service_gyp)/gpu_command_buffer_service_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_disk_cache_proto_gyp)/gpu_disk_cache_proto_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_mesa_mesa_headers_gyp)/mesa_headers.stamp \
+	$(call intermediates-dir-for,GYP,ui_gl_gl_jni_headers_gyp)/gl_jni_headers.stamp \
+	$(call intermediates-dir-for,GYP,ui_gl_surface_jni_headers_gyp)/surface_jni_headers.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_angle_dx11_src_translator_glsl_gyp)/third_party_angle_dx11_src_translator_glsl_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_angle_dx11_src_translator_common_gyp)/third_party_angle_dx11_src_translator_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_angle_dx11_src_preprocessor_gyp)/third_party_angle_dx11_src_preprocessor_gyp.a \
@@ -89,6 +82,12 @@
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gles2_cmd_helper_gyp)/gpu_gles2_cmd_helper_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_config_gyp)/gpu_gpu_config_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_ipc_gyp)/gpu_gpu_ipc_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_gyp)/media_media_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_opus_opus_gyp)/third_party_opus_opus_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_shared_memory_support_gyp)/media_shared_memory_support_gyp.a \
+	$(call intermediates-dir-for,GYP,media_media_android_jni_headers_gyp)/media_android_jni_headers.stamp \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_player_android_gyp)/media_player_android_gyp.a \
+	$(call intermediates-dir-for,GYP,media_video_capture_android_jni_headers_gyp)/video_capture_android_jni_headers.stamp \
 	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_WebKit_Source_web_blink_common_gyp)/third_party_WebKit_Source_web_blink_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_WebKit_Source_wtf_wtf_gyp)/third_party_WebKit_Source_wtf_wtf_gyp.a \
@@ -302,9 +301,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -387,9 +386,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -470,34 +469,30 @@
 	content_content_common_gyp \
 	components_tracing_gyp \
 	ipc_ipc_gyp \
-	media_media_gyp \
+	net_net_gyp \
+	base_base_i18n_gyp \
 	crypto_crypto_gyp \
 	third_party_openssl_openssl_gyp \
+	sdch_sdch_gyp \
+	third_party_zlib_zlib_gyp \
+	url_url_lib_gyp \
 	skia_skia_library_gyp \
 	skia_skia_opts_gyp \
 	skia_skia_opts_neon_gyp \
-	third_party_zlib_zlib_gyp \
 	third_party_freetype_ft2_gyp \
 	skia_skia_chrome_gyp \
 	skia_skia_chrome_opts_gyp \
-	third_party_opus_opus_gyp \
-	ui_ui_gyp \
-	base_base_i18n_gyp \
-	net_net_gyp \
-	sdch_sdch_gyp \
-	url_url_lib_gyp \
-	third_party_libpng_libpng_gyp \
-	gpu_command_buffer_common_gyp \
-	gpu_command_buffer_gles2_utils_gyp \
-	media_shared_memory_support_gyp \
-	media_player_android_gyp \
-	ui_gl_gl_gyp \
 	ui_shell_dialogs_gyp \
+	ui_ui_gyp \
+	third_party_libpng_libpng_gyp \
 	webkit_common_user_agent_user_agent_gyp \
 	cc_cc_gyp \
 	gpu_command_buffer_client_gyp \
+	gpu_command_buffer_common_gyp \
+	gpu_command_buffer_gles2_utils_gyp \
 	gpu_command_buffer_service_gyp \
 	gpu_disk_cache_proto_gyp \
+	ui_gl_gl_gyp \
 	third_party_angle_dx11_src_translator_glsl_gyp \
 	third_party_angle_dx11_src_translator_common_gyp \
 	third_party_angle_dx11_src_preprocessor_gyp \
@@ -506,6 +501,10 @@
 	gpu_gles2_cmd_helper_gyp \
 	gpu_gpu_config_gyp \
 	gpu_gpu_ipc_gyp \
+	media_media_gyp \
+	third_party_opus_opus_gyp \
+	media_shared_memory_support_gyp \
+	media_player_android_gyp \
 	third_party_WebKit_Source_web_blink_common_gyp \
 	third_party_WebKit_Source_wtf_wtf_gyp \
 	v8_tools_gyp_v8_base_arm_gyp \
@@ -589,12 +588,12 @@
 	liblog \
 	libcutils \
 	libandroid \
-	libOpenSLES \
-	libexpat \
-	libjnigraphics \
 	libicui18n \
 	libicuuc \
+	libexpat \
+	libjnigraphics \
 	libjpeg \
+	libOpenSLES \
 	libstlport \
 	libdl
 
diff --git a/android_webview/libwebviewchromium.target.linux-mips.mk b/android_webview/libwebviewchromium.target.linux-mips.mk
index 1fa1577..379ad69 100644
--- a/android_webview/libwebviewchromium.target.linux-mips.mk
+++ b/android_webview/libwebviewchromium.target.linux-mips.mk
@@ -30,56 +30,49 @@
 	$(call intermediates-dir-for,STATIC_LIBRARIES,content_content_common_gyp)/content_content_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,components_tracing_gyp)/components_tracing_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ipc_ipc_gyp)/ipc_ipc_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_gyp)/media_media_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,net_net_gyp)/net_net_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,base_base_i18n_gyp)/base_base_i18n_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_icu_icui18n_gyp)/icui18n.stamp \
+	$(call intermediates-dir-for,GYP,third_party_icu_system_icu_gyp)/system_icu.stamp \
+	$(call intermediates-dir-for,GYP,third_party_icu_icuuc_gyp)/icuuc.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,crypto_crypto_gyp)/crypto_crypto_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_openssl_openssl_gyp)/third_party_openssl_openssl_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,sdch_sdch_gyp)/sdch_sdch_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_zlib_zlib_gyp)/third_party_zlib_zlib_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,url_url_lib_gyp)/url_url_lib_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_icu_icudata_gyp)/icudata.stamp \
+	$(call intermediates-dir-for,GYP,net_net_resources_gyp)/net_resources.stamp \
+	$(call intermediates-dir-for,GYP,net_net_jni_headers_gyp)/net_jni_headers.stamp \
 	$(call intermediates-dir-for,GYP,skia_skia_gyp)/skia.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_library_gyp)/skia_skia_library_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_opts_gyp)/skia_skia_opts_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_zlib_zlib_gyp)/third_party_zlib_zlib_gyp.a \
 	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_skia_config_gyp)/blink_skia_config.stamp \
 	$(call intermediates-dir-for,GYP,third_party_expat_expat_gyp)/expat.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_freetype_ft2_gyp)/third_party_freetype_ft2_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_chrome_gyp)/skia_skia_chrome_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_chrome_opts_gyp)/skia_skia_chrome_opts_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_opus_opus_gyp)/third_party_opus_opus_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,base_base_i18n_gyp)/base_base_i18n_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_icu_icui18n_gyp)/icui18n.stamp \
-	$(call intermediates-dir-for,GYP,third_party_icu_system_icu_gyp)/system_icu.stamp \
-	$(call intermediates-dir-for,GYP,third_party_icu_icuuc_gyp)/icuuc.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,net_net_gyp)/net_net_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,sdch_sdch_gyp)/sdch_sdch_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,url_url_lib_gyp)/url_url_lib_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_icu_icudata_gyp)/icudata.stamp \
-	$(call intermediates-dir-for,GYP,net_net_resources_gyp)/net_resources.stamp \
-	$(call intermediates-dir-for,GYP,net_net_jni_headers_gyp)/net_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_libpng_libpng_gyp)/third_party_libpng_libpng_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_shell_dialogs_gyp)/ui_shell_dialogs_gyp.a \
 	$(call intermediates-dir-for,GYP,ui_base_strings_ui_strings_gyp)/ui_strings.stamp \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_libpng_libpng_gyp)/third_party_libpng_libpng_gyp.a \
 	$(call intermediates-dir-for,GYP,ui_ui_resources_gyp)/ui_resources.stamp \
 	$(call intermediates-dir-for,GYP,third_party_libjpeg_libjpeg_gyp)/libjpeg.stamp \
 	$(call intermediates-dir-for,GYP,ui_ui_jni_headers_gyp)/ui_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_common_gyp)/gpu_command_buffer_common_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_gles2_utils_gyp)/gpu_command_buffer_gles2_utils_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_khronos_khronos_headers_gyp)/khronos_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_shared_memory_support_gyp)/media_shared_memory_support_gyp.a \
-	$(call intermediates-dir-for,GYP,media_media_android_jni_headers_gyp)/media_android_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_player_android_gyp)/media_player_android_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_mesa_mesa_headers_gyp)/mesa_headers.stamp \
-	$(call intermediates-dir-for,GYP,ui_gl_gl_jni_headers_gyp)/gl_jni_headers.stamp \
-	$(call intermediates-dir-for,GYP,ui_gl_surface_texture_jni_headers_gyp)/surface_texture_jni_headers.stamp \
-	$(call intermediates-dir-for,GYP,ui_gl_surface_jni_headers_gyp)/surface_jni_headers.stamp \
-	$(call intermediates-dir-for,GYP,media_video_capture_android_jni_headers_gyp)/video_capture_android_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_shell_dialogs_gyp)/ui_shell_dialogs_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,webkit_common_user_agent_user_agent_gyp)/webkit_common_user_agent_user_agent_gyp.a \
 	$(call intermediates-dir-for,GYP,webkit_common_user_agent_webkit_version_gyp)/webkit_version.stamp \
 	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,cc_cc_gyp)/cc_cc_gyp.a \
 	$(call intermediates-dir-for,GYP,gpu_gpu_gyp)/gpu.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_client_gyp)/gpu_command_buffer_client_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_common_gyp)/gpu_command_buffer_common_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_gles2_utils_gyp)/gpu_command_buffer_gles2_utils_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_khronos_khronos_headers_gyp)/khronos_headers.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_service_gyp)/gpu_command_buffer_service_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_disk_cache_proto_gyp)/gpu_disk_cache_proto_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_mesa_mesa_headers_gyp)/mesa_headers.stamp \
+	$(call intermediates-dir-for,GYP,ui_gl_gl_jni_headers_gyp)/gl_jni_headers.stamp \
+	$(call intermediates-dir-for,GYP,ui_gl_surface_jni_headers_gyp)/surface_jni_headers.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_angle_dx11_src_translator_glsl_gyp)/third_party_angle_dx11_src_translator_glsl_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_angle_dx11_src_translator_common_gyp)/third_party_angle_dx11_src_translator_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_angle_dx11_src_preprocessor_gyp)/third_party_angle_dx11_src_preprocessor_gyp.a \
@@ -88,6 +81,12 @@
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gles2_cmd_helper_gyp)/gpu_gles2_cmd_helper_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_config_gyp)/gpu_gpu_config_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_ipc_gyp)/gpu_gpu_ipc_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_gyp)/media_media_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_opus_opus_gyp)/third_party_opus_opus_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_shared_memory_support_gyp)/media_shared_memory_support_gyp.a \
+	$(call intermediates-dir-for,GYP,media_media_android_jni_headers_gyp)/media_android_jni_headers.stamp \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_player_android_gyp)/media_player_android_gyp.a \
+	$(call intermediates-dir-for,GYP,media_video_capture_android_jni_headers_gyp)/video_capture_android_jni_headers.stamp \
 	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_WebKit_Source_web_blink_common_gyp)/third_party_WebKit_Source_web_blink_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_WebKit_Source_wtf_wtf_gyp)/third_party_WebKit_Source_wtf_wtf_gyp.a \
@@ -297,9 +296,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -381,9 +380,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -460,33 +459,29 @@
 	content_content_common_gyp \
 	components_tracing_gyp \
 	ipc_ipc_gyp \
-	media_media_gyp \
+	net_net_gyp \
+	base_base_i18n_gyp \
 	crypto_crypto_gyp \
 	third_party_openssl_openssl_gyp \
+	sdch_sdch_gyp \
+	third_party_zlib_zlib_gyp \
+	url_url_lib_gyp \
 	skia_skia_library_gyp \
 	skia_skia_opts_gyp \
-	third_party_zlib_zlib_gyp \
 	third_party_freetype_ft2_gyp \
 	skia_skia_chrome_gyp \
 	skia_skia_chrome_opts_gyp \
-	third_party_opus_opus_gyp \
-	ui_ui_gyp \
-	base_base_i18n_gyp \
-	net_net_gyp \
-	sdch_sdch_gyp \
-	url_url_lib_gyp \
-	third_party_libpng_libpng_gyp \
-	gpu_command_buffer_common_gyp \
-	gpu_command_buffer_gles2_utils_gyp \
-	media_shared_memory_support_gyp \
-	media_player_android_gyp \
-	ui_gl_gl_gyp \
 	ui_shell_dialogs_gyp \
+	ui_ui_gyp \
+	third_party_libpng_libpng_gyp \
 	webkit_common_user_agent_user_agent_gyp \
 	cc_cc_gyp \
 	gpu_command_buffer_client_gyp \
+	gpu_command_buffer_common_gyp \
+	gpu_command_buffer_gles2_utils_gyp \
 	gpu_command_buffer_service_gyp \
 	gpu_disk_cache_proto_gyp \
+	ui_gl_gl_gyp \
 	third_party_angle_dx11_src_translator_glsl_gyp \
 	third_party_angle_dx11_src_translator_common_gyp \
 	third_party_angle_dx11_src_preprocessor_gyp \
@@ -495,6 +490,10 @@
 	gpu_gles2_cmd_helper_gyp \
 	gpu_gpu_config_gyp \
 	gpu_gpu_ipc_gyp \
+	media_media_gyp \
+	third_party_opus_opus_gyp \
+	media_shared_memory_support_gyp \
+	media_player_android_gyp \
 	third_party_WebKit_Source_web_blink_common_gyp \
 	third_party_WebKit_Source_wtf_wtf_gyp \
 	v8_tools_gyp_v8_base_mipsel_gyp \
@@ -575,12 +574,12 @@
 	liblog \
 	libcutils \
 	libandroid \
-	libOpenSLES \
-	libexpat \
-	libjnigraphics \
 	libicui18n \
 	libicuuc \
+	libexpat \
+	libjnigraphics \
 	libjpeg \
+	libOpenSLES \
 	libstlport \
 	libdl
 
diff --git a/android_webview/libwebviewchromium.target.linux-x86.mk b/android_webview/libwebviewchromium.target.linux-x86.mk
index c387e0b..f341e4b 100644
--- a/android_webview/libwebviewchromium.target.linux-x86.mk
+++ b/android_webview/libwebviewchromium.target.linux-x86.mk
@@ -30,62 +30,50 @@
 	$(call intermediates-dir-for,STATIC_LIBRARIES,content_content_common_gyp)/content_content_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,components_tracing_gyp)/components_tracing_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ipc_ipc_gyp)/ipc_ipc_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_gyp)/media_media_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,net_net_gyp)/net_net_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,base_base_i18n_gyp)/base_base_i18n_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_icu_icui18n_gyp)/icui18n.stamp \
+	$(call intermediates-dir-for,GYP,third_party_icu_system_icu_gyp)/system_icu.stamp \
+	$(call intermediates-dir-for,GYP,third_party_icu_icuuc_gyp)/icuuc.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,crypto_crypto_gyp)/crypto_crypto_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_openssl_openssl_gyp)/third_party_openssl_openssl_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,sdch_sdch_gyp)/sdch_sdch_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_zlib_zlib_gyp)/third_party_zlib_zlib_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,url_url_lib_gyp)/url_url_lib_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_icu_icudata_gyp)/icudata.stamp \
+	$(call intermediates-dir-for,GYP,net_net_resources_gyp)/net_resources.stamp \
+	$(call intermediates-dir-for,GYP,net_net_jni_headers_gyp)/net_jni_headers.stamp \
 	$(call intermediates-dir-for,GYP,skia_skia_gyp)/skia.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_library_gyp)/skia_skia_library_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_opts_gyp)/skia_skia_opts_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_opts_ssse3_gyp)/skia_skia_opts_ssse3_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_zlib_zlib_gyp)/third_party_zlib_zlib_gyp.a \
 	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_skia_config_gyp)/blink_skia_config.stamp \
 	$(call intermediates-dir-for,GYP,third_party_expat_expat_gyp)/expat.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_freetype_ft2_gyp)/third_party_freetype_ft2_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_chrome_gyp)/skia_skia_chrome_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_chrome_opts_gyp)/skia_skia_chrome_opts_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_opus_opus_gyp)/third_party_opus_opus_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,base_base_i18n_gyp)/base_base_i18n_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_icu_icui18n_gyp)/icui18n.stamp \
-	$(call intermediates-dir-for,GYP,third_party_icu_system_icu_gyp)/system_icu.stamp \
-	$(call intermediates-dir-for,GYP,third_party_icu_icuuc_gyp)/icuuc.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,net_net_gyp)/net_net_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,sdch_sdch_gyp)/sdch_sdch_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,url_url_lib_gyp)/url_url_lib_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_icu_icudata_gyp)/icudata.stamp \
-	$(call intermediates-dir-for,GYP,net_net_resources_gyp)/net_resources.stamp \
-	$(call intermediates-dir-for,GYP,net_net_jni_headers_gyp)/net_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_libpng_libpng_gyp)/third_party_libpng_libpng_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_shell_dialogs_gyp)/ui_shell_dialogs_gyp.a \
 	$(call intermediates-dir-for,GYP,ui_base_strings_ui_strings_gyp)/ui_strings.stamp \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_libpng_libpng_gyp)/third_party_libpng_libpng_gyp.a \
 	$(call intermediates-dir-for,GYP,ui_ui_resources_gyp)/ui_resources.stamp \
 	$(call intermediates-dir-for,GYP,third_party_libjpeg_libjpeg_gyp)/libjpeg.stamp \
 	$(call intermediates-dir-for,GYP,ui_ui_jni_headers_gyp)/ui_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_common_gyp)/gpu_command_buffer_common_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_gles2_utils_gyp)/gpu_command_buffer_gles2_utils_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_khronos_khronos_headers_gyp)/khronos_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_shared_memory_support_gyp)/media_shared_memory_support_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_shared_memory_support_sse_gyp)/media_shared_memory_support_sse_gyp.a \
-	$(call intermediates-dir-for,GYP,media_media_android_jni_headers_gyp)/media_android_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_player_android_gyp)/media_player_android_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_mesa_mesa_headers_gyp)/mesa_headers.stamp \
-	$(call intermediates-dir-for,GYP,ui_gl_gl_jni_headers_gyp)/gl_jni_headers.stamp \
-	$(call intermediates-dir-for,GYP,ui_gl_surface_texture_jni_headers_gyp)/surface_texture_jni_headers.stamp \
-	$(call intermediates-dir-for,GYP,ui_gl_surface_jni_headers_gyp)/surface_jni_headers.stamp \
-	$(call intermediates-dir-for,GYP,media_video_capture_android_jni_headers_gyp)/video_capture_android_jni_headers.stamp \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_asm_gyp)/media_media_asm_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_mmx_gyp)/media_media_mmx_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_sse_gyp)/media_media_sse_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_sse2_gyp)/media_media_sse2_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_shell_dialogs_gyp)/ui_shell_dialogs_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,webkit_common_user_agent_user_agent_gyp)/webkit_common_user_agent_user_agent_gyp.a \
 	$(call intermediates-dir-for,GYP,webkit_common_user_agent_webkit_version_gyp)/webkit_version.stamp \
 	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,cc_cc_gyp)/cc_cc_gyp.a \
 	$(call intermediates-dir-for,GYP,gpu_gpu_gyp)/gpu.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_client_gyp)/gpu_command_buffer_client_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_common_gyp)/gpu_command_buffer_common_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_gles2_utils_gyp)/gpu_command_buffer_gles2_utils_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_khronos_khronos_headers_gyp)/khronos_headers.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_command_buffer_service_gyp)/gpu_command_buffer_service_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_disk_cache_proto_gyp)/gpu_disk_cache_proto_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_mesa_mesa_headers_gyp)/mesa_headers.stamp \
+	$(call intermediates-dir-for,GYP,ui_gl_gl_jni_headers_gyp)/gl_jni_headers.stamp \
+	$(call intermediates-dir-for,GYP,ui_gl_surface_jni_headers_gyp)/surface_jni_headers.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_angle_dx11_src_translator_glsl_gyp)/third_party_angle_dx11_src_translator_glsl_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_angle_dx11_src_translator_common_gyp)/third_party_angle_dx11_src_translator_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_angle_dx11_src_preprocessor_gyp)/third_party_angle_dx11_src_preprocessor_gyp.a \
@@ -94,6 +82,17 @@
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gles2_cmd_helper_gyp)/gpu_gles2_cmd_helper_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_config_gyp)/gpu_gpu_config_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,gpu_gpu_ipc_gyp)/gpu_gpu_ipc_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_gyp)/media_media_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_opus_opus_gyp)/third_party_opus_opus_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_shared_memory_support_gyp)/media_shared_memory_support_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_shared_memory_support_sse_gyp)/media_shared_memory_support_sse_gyp.a \
+	$(call intermediates-dir-for,GYP,media_media_android_jni_headers_gyp)/media_android_jni_headers.stamp \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_player_android_gyp)/media_player_android_gyp.a \
+	$(call intermediates-dir-for,GYP,media_video_capture_android_jni_headers_gyp)/video_capture_android_jni_headers.stamp \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_asm_gyp)/media_media_asm_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_mmx_gyp)/media_media_mmx_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_sse_gyp)/media_media_sse_gyp.a \
+	$(call intermediates-dir-for,STATIC_LIBRARIES,media_media_sse2_gyp)/media_media_sse2_gyp.a \
 	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_WebKit_Source_web_blink_common_gyp)/third_party_WebKit_Source_web_blink_common_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,third_party_WebKit_Source_wtf_wtf_gyp)/third_party_WebKit_Source_wtf_wtf_gyp.a \
@@ -308,9 +307,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -396,9 +395,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -474,39 +473,30 @@
 	content_content_common_gyp \
 	components_tracing_gyp \
 	ipc_ipc_gyp \
-	media_media_gyp \
+	net_net_gyp \
+	base_base_i18n_gyp \
 	crypto_crypto_gyp \
 	third_party_openssl_openssl_gyp \
+	sdch_sdch_gyp \
+	third_party_zlib_zlib_gyp \
+	url_url_lib_gyp \
 	skia_skia_library_gyp \
 	skia_skia_opts_gyp \
 	skia_skia_opts_ssse3_gyp \
-	third_party_zlib_zlib_gyp \
 	third_party_freetype_ft2_gyp \
 	skia_skia_chrome_gyp \
 	skia_skia_chrome_opts_gyp \
-	third_party_opus_opus_gyp \
-	ui_ui_gyp \
-	base_base_i18n_gyp \
-	net_net_gyp \
-	sdch_sdch_gyp \
-	url_url_lib_gyp \
-	third_party_libpng_libpng_gyp \
-	gpu_command_buffer_common_gyp \
-	gpu_command_buffer_gles2_utils_gyp \
-	media_shared_memory_support_gyp \
-	media_shared_memory_support_sse_gyp \
-	media_player_android_gyp \
-	ui_gl_gl_gyp \
-	media_media_asm_gyp \
-	media_media_mmx_gyp \
-	media_media_sse_gyp \
-	media_media_sse2_gyp \
 	ui_shell_dialogs_gyp \
+	ui_ui_gyp \
+	third_party_libpng_libpng_gyp \
 	webkit_common_user_agent_user_agent_gyp \
 	cc_cc_gyp \
 	gpu_command_buffer_client_gyp \
+	gpu_command_buffer_common_gyp \
+	gpu_command_buffer_gles2_utils_gyp \
 	gpu_command_buffer_service_gyp \
 	gpu_disk_cache_proto_gyp \
+	ui_gl_gl_gyp \
 	third_party_angle_dx11_src_translator_glsl_gyp \
 	third_party_angle_dx11_src_translator_common_gyp \
 	third_party_angle_dx11_src_preprocessor_gyp \
@@ -515,6 +505,15 @@
 	gpu_gles2_cmd_helper_gyp \
 	gpu_gpu_config_gyp \
 	gpu_gpu_ipc_gyp \
+	media_media_gyp \
+	third_party_opus_opus_gyp \
+	media_shared_memory_support_gyp \
+	media_shared_memory_support_sse_gyp \
+	media_player_android_gyp \
+	media_media_asm_gyp \
+	media_media_mmx_gyp \
+	media_media_sse_gyp \
+	media_media_sse2_gyp \
 	third_party_WebKit_Source_web_blink_common_gyp \
 	third_party_WebKit_Source_wtf_wtf_gyp \
 	v8_tools_gyp_v8_base_ia32_gyp \
@@ -596,12 +595,12 @@
 	liblog \
 	libcutils \
 	libandroid \
-	libOpenSLES \
-	libexpat \
-	libjnigraphics \
 	libicui18n \
 	libicuuc \
+	libexpat \
+	libjnigraphics \
 	libjpeg \
+	libOpenSLES \
 	libstlport \
 	libdl
 
diff --git a/android_webview/native/aw_contents.cc b/android_webview/native/aw_contents.cc
index e6cf841..6b67525 100644
--- a/android_webview/native/aw_contents.cc
+++ b/android_webview/native/aw_contents.cc
@@ -43,6 +43,7 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/renderer_preferences.h"
 #include "content/public/common/ssl_status.h"
 #include "jni/AwContents_jni.h"
 #include "net/cert/x509_certificate.h"
@@ -145,6 +146,9 @@
       AwAutofillManagerDelegate::FromWebContents(web_contents_.get());
   if (autofill_manager_delegate)
     InitAutofillIfNecessary(autofill_manager_delegate->GetSaveFormData());
+
+  web_contents_->GetMutableRendererPrefs()->tap_multiple_targets_strategy =
+      content::TAP_MULTIPLE_TARGETS_STRATEGY_NONE;
 }
 
 void AwContents::SetJavaPeers(JNIEnv* env,
@@ -302,9 +306,10 @@
                                jstring jpath, jobject callback) {
   ScopedJavaGlobalRef<jobject>* j_callback = new ScopedJavaGlobalRef<jobject>();
   j_callback->Reset(env, callback);
+  base::FilePath target_path(ConvertJavaStringToUTF8(env, jpath));
   web_contents_->GenerateMHTML(
-      base::FilePath(ConvertJavaStringToUTF8(env, jpath)),
-      base::Bind(&GenerateMHTMLCallback, base::Owned(j_callback)));
+      target_path,
+      base::Bind(&GenerateMHTMLCallback, base::Owned(j_callback), target_path));
 }
 
 void AwContents::PerformLongClick() {
@@ -780,4 +785,10 @@
   browser_view_renderer_->EnableOnNewPicture(enabled);
 }
 
+void AwContents::SetJsOnlineProperty(JNIEnv* env,
+                                     jobject obj,
+                                     jboolean network_up) {
+  render_view_host_ext_->SetJsOnlineProperty(network_up);
+}
+
 }  // namespace android_webview
diff --git a/android_webview/native/aw_contents.h b/android_webview/native/aw_contents.h
index 24dff4f..272f0ae 100644
--- a/android_webview/native/aw_contents.h
+++ b/android_webview/native/aw_contents.h
@@ -173,6 +173,9 @@
 
   // Sets the java delegate
   void SetAwAutofillManagerDelegate(jobject delegate);
+
+  void SetJsOnlineProperty(JNIEnv* env, jobject obj, jboolean network_up);
+
  private:
   void InitAutofillIfNecessary(bool enabled);
 
diff --git a/android_webview/native/aw_contents_io_thread_client_impl.cc b/android_webview/native/aw_contents_io_thread_client_impl.cc
index 81ea0de..8002851 100644
--- a/android_webview/native/aw_contents_io_thread_client_impl.cc
+++ b/android_webview/native/aw_contents_io_thread_client_impl.cc
@@ -197,7 +197,8 @@
     return scoped_ptr<InterceptedRequestData>();
   const content::ResourceRequestInfo* info =
       content::ResourceRequestInfo::ForRequest(request);
-  bool is_main_frame = info && info->IsMainFrame();
+  bool is_main_frame = info &&
+      info->GetResourceType() == ResourceType::MAIN_FRAME;
 
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jstring> jstring_url =
diff --git a/android_webview/native/state_serializer.cc b/android_webview/native/state_serializer.cc
index 6aa3ae1..6791108 100644
--- a/android_webview/native/state_serializer.cc
+++ b/android_webview/native/state_serializer.cc
@@ -35,7 +35,7 @@
 // Sanity check value that we are restoring from a valid pickle.
 // This can potentially used as an actual serialization version number in the
 // future if we ever decide to support restoring from older versions.
-const uint32 AW_STATE_VERSION = 20121126;
+const uint32 AW_STATE_VERSION = 20130814;
 
 }  // namespace
 
@@ -186,6 +186,9 @@
   if (!pickle->WriteInt64(entry.GetTimestamp().ToInternalValue()))
     return false;
 
+  if (!pickle->WriteInt(entry.GetHttpStatusCode()))
+    return false;
+
   // Please update AW_STATE_VERSION if serialization format is changed.
 
   return true;
@@ -272,6 +275,13 @@
     entry->SetTimestamp(base::Time::FromInternalValue(timestamp));
   }
 
+  {
+    int http_status_code;
+    if (!iterator->ReadInt(&http_status_code))
+      return false;
+    entry->SetHttpStatusCode(http_status_code);
+  }
+
   return true;
 }
 
diff --git a/android_webview/native/state_serializer_unittests.cc b/android_webview/native/state_serializer_unittests.cc
index f592d29..408c888 100644
--- a/android_webview/native/state_serializer_unittests.cc
+++ b/android_webview/native/state_serializer_unittests.cc
@@ -54,6 +54,7 @@
   const GURL base_url_for_data_url("http://base_url");
   const bool is_overriding_user_agent = true;
   const base::Time timestamp = base::Time::FromInternalValue(12345);
+  const int http_status_code = 404;
 
   entry->SetURL(url);
   entry->SetVirtualURL(virtual_url);
@@ -65,6 +66,7 @@
   entry->SetBaseURLForDataURL(base_url_for_data_url);
   entry->SetIsOverridingUserAgent(is_overriding_user_agent);
   entry->SetTimestamp(timestamp);
+  entry->SetHttpStatusCode(http_status_code);
 
   Pickle pickle;
   bool result = internal::WriteNavigationEntryToPickle(*entry, &pickle);
@@ -86,6 +88,7 @@
   EXPECT_EQ(base_url_for_data_url, copy->GetBaseURLForDataURL());
   EXPECT_EQ(is_overriding_user_agent, copy->GetIsOverridingUserAgent());
   EXPECT_EQ(timestamp, copy->GetTimestamp());
+  EXPECT_EQ(http_status_code, copy->GetHttpStatusCode());
 }
 
 }  // namespace android_webview
diff --git a/android_webview/renderer/aw_render_process_observer.cc b/android_webview/renderer/aw_render_process_observer.cc
index 5e3ecca..561580c 100644
--- a/android_webview/renderer/aw_render_process_observer.cc
+++ b/android_webview/renderer/aw_render_process_observer.cc
@@ -7,6 +7,7 @@
 #include "android_webview/common/render_view_messages.h"
 #include "ipc/ipc_message_macros.h"
 #include "third_party/WebKit/public/web/WebCache.h"
+#include "third_party/WebKit/public/web/WebNetworkStateNotifier.h"
 
 namespace android_webview {
 
@@ -22,6 +23,7 @@
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(AwRenderProcessObserver, message)
     IPC_MESSAGE_HANDLER(AwViewMsg_ClearCache, OnClearCache)
+    IPC_MESSAGE_HANDLER(AwViewMsg_SetJsOnlineProperty, OnSetJsOnlineProperty)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
   return handled;
@@ -36,4 +38,9 @@
     WebKit::WebCache::clear();
 }
 
+void AwRenderProcessObserver::OnSetJsOnlineProperty(bool network_up) {
+  if (webkit_initialized_)
+    WebKit::WebNetworkStateNotifier::setOnLine(network_up);
+}
+
 }  // nanemspace android_webview
diff --git a/android_webview/renderer/aw_render_process_observer.h b/android_webview/renderer/aw_render_process_observer.h
index 5a5a8f9..2cb4d2f 100644
--- a/android_webview/renderer/aw_render_process_observer.h
+++ b/android_webview/renderer/aw_render_process_observer.h
@@ -24,6 +24,7 @@
 
  private:
   void OnClearCache();
+  void OnSetJsOnlineProperty(bool network_up);
 
   bool webkit_initialized_;
 };
diff --git a/android_webview/test/shell/AndroidManifest.xml b/android_webview/test/shell/AndroidManifest.xml
index 5e6c541..f656a55 100644
--- a/android_webview/test/shell/AndroidManifest.xml
+++ b/android_webview/test/shell/AndroidManifest.xml
@@ -25,6 +25,8 @@
         <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
       </intent-filter>
     </activity>
+    <provider android:name="org.chromium.android_webview.test.TestContentProvider"
+        android:authorities="org.chromium.android_webview.test.TestContentProvider" />
   </application>
 
   <!-- TODO(joth): Change minSdkVersion to 16 when crbug/161864 lands. -->
diff --git a/android_webview/tools/webview_licenses.py b/android_webview/tools/webview_licenses.py
index aa4f28f..d3233b3 100755
--- a/android_webview/tools/webview_licenses.py
+++ b/android_webview/tools/webview_licenses.py
@@ -109,9 +109,6 @@
   excluded_dirs_list.append('data/dom_perf')
   # Histogram tools, doesn't exist in the snapshot
   excluded_dirs_list.append('tools/histograms')
-  # swarm_client is a third_party and not used on Android.
-  excluded_dirs_list.append('tools/swarm_client')
-  excluded_dirs_list.append('tools/swarming_client')
   # Arm sysroot tools, doesn't exist in the snapshot
   excluded_dirs_list.append('arm-sysroot')
   # Data is not part of open source chromium, but are included on some bots.
@@ -194,6 +191,8 @@
     # third_party directories in this tree aren't actually third party, but
     # provide a way to shadow experimental buildfiles into those directories.
     os.path.join('tools', 'gn', 'secondary'),
+    # Not shipped, Chromium code
+    os.path.join('tools', 'swarm_client'),
   ]
   third_party_dirs = licenses.FindThirdPartyDirs(prune_paths, REPOSITORY_ROOT)
   return licenses.FilterDirsWithFiles(third_party_dirs, REPOSITORY_ROOT)
diff --git a/apps/app_restore_service_browsertest.cc b/apps/app_restore_service_browsertest.cc
index 88ceb65..88f7dce 100644
--- a/apps/app_restore_service_browsertest.cc
+++ b/apps/app_restore_service_browsertest.cc
@@ -93,7 +93,7 @@
 }
 
 // Flaky: crbug.com/269613
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_WIN)
 #define MAYBE_FileAccessIsRestored DISABLED_FileAccessIsRestored
 #else
 #define MAYBE_FileAccessIsRestored FileAccessIsRestored
@@ -137,7 +137,7 @@
   for (std::vector<SavedFileEntry>::const_iterator it = file_entries.begin();
        it != file_entries.end(); ++it) {
     saved_files_service->RegisterFileEntry(
-        extension->id(), it->id, it->path, it->writable);
+        extension->id(), it->id, it->path, it->is_directory);
   }
 
   apps::AppRestoreServiceFactory::GetForProfile(browser()->profile())->
diff --git a/apps/app_shim/app_shim_handler_mac.h b/apps/app_shim/app_shim_handler_mac.h
index aba7a1c..bb908f9 100644
--- a/apps/app_shim/app_shim_handler_mac.h
+++ b/apps/app_shim/app_shim_handler_mac.h
@@ -24,6 +24,8 @@
     virtual void OnAppLaunchComplete(AppShimLaunchResult result) = 0;
     // Invoked when the app is closed in the browser process.
     virtual void OnAppClosed() = 0;
+    // Invoked when the app is requesting user attention.
+    virtual void OnAppRequestUserAttention() = 0;
 
     // Allows the handler to determine which app this host corresponds to.
     virtual base::FilePath GetProfilePath() const = 0;
diff --git a/apps/app_shim/app_shim_host_mac.cc b/apps/app_shim/app_shim_host_mac.cc
index 72840c7..aba1a58 100644
--- a/apps/app_shim/app_shim_host_mac.cc
+++ b/apps/app_shim/app_shim_host_mac.cc
@@ -114,6 +114,10 @@
   Close();
 }
 
+void AppShimHost::OnAppRequestUserAttention() {
+  Send(new AppShimMsg_RequestUserAttention);
+}
+
 void AppShimHost::Close() {
   DCHECK(CalledOnValidThread());
   delete this;
diff --git a/apps/app_shim/app_shim_host_mac.h b/apps/app_shim/app_shim_host_mac.h
index 2bed7f1..9c3c948 100644
--- a/apps/app_shim/app_shim_host_mac.h
+++ b/apps/app_shim/app_shim_host_mac.h
@@ -65,6 +65,7 @@
   // apps::AppShimHandler::Host overrides:
   virtual void OnAppLaunchComplete(apps::AppShimLaunchResult result) OVERRIDE;
   virtual void OnAppClosed() OVERRIDE;
+  virtual void OnAppRequestUserAttention() OVERRIDE;
   virtual base::FilePath GetProfilePath() const OVERRIDE;
   virtual std::string GetAppId() const OVERRIDE;
 
diff --git a/apps/app_shim/app_shim_messages.h b/apps/app_shim/app_shim_messages.h
index 85a1df3..e0fd299 100644
--- a/apps/app_shim/app_shim_messages.h
+++ b/apps/app_shim/app_shim_messages.h
@@ -25,6 +25,9 @@
 IPC_MESSAGE_CONTROL1(AppShimMsg_LaunchApp_Done,
                      apps::AppShimLaunchResult /* launch result */)
 
+// Instructs the shim to request user attention.
+IPC_MESSAGE_CONTROL0(AppShimMsg_RequestUserAttention)
+
 // Signals to the main Chrome process that a shim has started indicating the
 // profile and app_id that the shim should be associated with and whether to
 // launch the app immediately.
diff --git a/apps/app_shim/app_shim_quit_interactive_uitest_mac.mm b/apps/app_shim/app_shim_quit_interactive_uitest_mac.mm
index 5b5dad6..4a74457 100644
--- a/apps/app_shim/app_shim_quit_interactive_uitest_mac.mm
+++ b/apps/app_shim/app_shim_quit_interactive_uitest_mac.mm
@@ -39,6 +39,7 @@
   virtual void OnAppClosed() OVERRIDE {
     handler_->OnShimClose(this);
   }
+  virtual void OnAppRequestUserAttention() OVERRIDE {}
   virtual base::FilePath GetProfilePath() const OVERRIDE {
     return profile_path_;
   }
diff --git a/apps/app_shim/chrome_main_app_mode_mac.mm b/apps/app_shim/chrome_main_app_mode_mac.mm
index ddad52a..3ca7c3f 100644
--- a/apps/app_shim/chrome_main_app_mode_mac.mm
+++ b/apps/app_shim/chrome_main_app_mode_mac.mm
@@ -21,6 +21,7 @@
 #include "base/mac/scoped_nsobject.h"
 #include "base/message_loop/message_loop.h"
 #include "base/path_service.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/threading/thread.h"
 #include "chrome/common/chrome_constants.h"
@@ -34,6 +35,16 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/l10n/l10n_util.h"
 
+// Replicate specific 10.7 SDK declarations for building with prior SDKs.
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+    MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+
+@interface NSApplication (LionSDKDeclarations)
+- (void)disableRelaunchOnLogin;
+@end
+
+#endif  // MAC_OS_X_VERSION_10_7
+
 namespace {
 
 const app_mode::ChromeAppModeInfo* g_info;
@@ -88,6 +99,9 @@
   // shim process should die.
   void OnLaunchAppDone(apps::AppShimLaunchResult result);
 
+  // Requests user attention.
+  void OnRequestUserAttention();
+
   // Terminates the app shim process.
   void Close();
 
@@ -106,6 +120,10 @@
 
   SetUpMenu();
 
+  // Chrome will relaunch shims when relaunching apps.
+  if (base::mac::IsOSLionOrLater())
+    [NSApp disableRelaunchOnLogin];
+
   // The user_data_dir for shims actually contains the app_data_path.
   // I.e. <user_data_dir>/<profile_dir>/Web Applications/_crx_extensionid/
   base::FilePath user_data_dir =
@@ -118,9 +136,12 @@
   channel_ = new IPC::ChannelProxy(handle, IPC::Channel::MODE_NAMED_CLIENT,
       this, g_io_thread->message_loop_proxy().get());
 
+  bool launched_by_chrome =
+      CommandLine::ForCurrentProcess()->HasSwitch(
+          app_mode::kLaunchedByChromeProcessId);
   channel_->Send(new AppShimHostMsg_LaunchApp(
       g_info->profile_dir, g_info->app_mode_id,
-      CommandLine::ForCurrentProcess()->HasSwitch(app_mode::kNoLaunchApp) ?
+      launched_by_chrome ?
           apps::APP_SHIM_LAUNCH_REGISTER_ONLY : apps::APP_SHIM_LAUNCH_NORMAL));
 
   nsapp_delegate_.reset([[AppShimDelegate alloc] initWithController:this]);
@@ -170,6 +191,7 @@
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(AppShimController, message)
     IPC_MESSAGE_HANDLER(AppShimMsg_LaunchApp_Done, OnLaunchAppDone)
+    IPC_MESSAGE_HANDLER(AppShimMsg_RequestUserAttention, OnRequestUserAttention)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
 
@@ -189,6 +211,10 @@
   launch_app_done_ = true;
 }
 
+void AppShimController::OnRequestUserAttention() {
+  [NSApp requestUserAttention:NSInformationalRequest];
+}
+
 void AppShimController::Close() {
   [nsapp_delegate_ terminateNow];
 }
@@ -432,15 +458,24 @@
   g_io_thread = io_thread;
 
   // Find already running instances of Chrome.
-  NSString* chrome_bundle_id = [base::mac::OuterBundle() bundleIdentifier];
-  NSArray* existing_chrome = [NSRunningApplication
-      runningApplicationsWithBundleIdentifier:chrome_bundle_id];
+  pid_t pid = -1;
+  std::string chrome_process_id = CommandLine::ForCurrentProcess()->
+      GetSwitchValueASCII(app_mode::kLaunchedByChromeProcessId);
+  if (!chrome_process_id.empty()) {
+    if (!base::StringToInt(chrome_process_id, &pid))
+      LOG(FATAL) << "Invalid PID: " << chrome_process_id;
+  } else {
+    NSString* chrome_bundle_id = [base::mac::OuterBundle() bundleIdentifier];
+    NSArray* existing_chrome = [NSRunningApplication
+        runningApplicationsWithBundleIdentifier:chrome_bundle_id];
+    if ([existing_chrome count] > 0)
+      pid = [[existing_chrome objectAtIndex:0] processIdentifier];
+  }
 
   // Launch Chrome if it isn't already running.
   ProcessSerialNumber psn;
-  if ([existing_chrome count] > 0) {
-    OSStatus status = GetProcessForPID(
-        [[existing_chrome objectAtIndex:0] processIdentifier], &psn);
+  if (pid > -1) {
+    OSStatus status = GetProcessForPID(pid, &psn);
     if (status)
       return 1;
 
diff --git a/apps/app_shim/extension_app_shim_handler_mac.cc b/apps/app_shim/extension_app_shim_handler_mac.cc
index 8fe8f6b..a45eccb 100644
--- a/apps/app_shim/extension_app_shim_handler_mac.cc
+++ b/apps/app_shim/extension_app_shim_handler_mac.cc
@@ -32,6 +32,8 @@
 
 namespace {
 
+typedef apps::ShellWindowRegistry::ShellWindowList ShellWindowList;
+
 void ProfileLoadedCallback(base::Callback<void(Profile*)> callback,
                            Profile* profile,
                            Profile::CreateStatus status) {
@@ -51,12 +53,22 @@
     chrome::AttemptExit();
 }
 
+void SetAppHidden(Profile* profile, const std::string& app_id, bool hidden) {
+  ShellWindowList windows =
+      apps::ShellWindowRegistry::Get(profile)->GetShellWindowsForApp(app_id);
+  for (ShellWindowList::const_reverse_iterator it = windows.rbegin();
+       it != windows.rend(); ++it) {
+    if (hidden)
+      (*it)->GetBaseWindow()->HideWithApp();
+    else
+      (*it)->GetBaseWindow()->ShowWithApp();
+  }
+}
+
 }  // namespace
 
 namespace apps {
 
-typedef ShellWindowRegistry::ShellWindowList ShellWindowList;
-
 bool ExtensionAppShimHandler::Delegate::ProfileExistsForPath(
     const base::FilePath& path) {
   ProfileManager* profile_manager = g_browser_process->profile_manager();
@@ -142,10 +154,6 @@
                  content::NotificationService::AllBrowserContextsAndSources());
   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
                  content::NotificationService::AllBrowserContextsAndSources());
-  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
-                 content::NotificationService::AllBrowserContextsAndSources());
-  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED,
-                 content::NotificationService::AllBrowserContextsAndSources());
   registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
                  content::NotificationService::AllBrowserContextsAndSources());
 }
@@ -175,6 +183,26 @@
   }
 }
 
+// static
+bool ExtensionAppShimHandler::RequestUserAttentionForWindow(
+    ShellWindow* shell_window) {
+  ExtensionAppShimHandler* handler =
+      g_browser_process->platform_part()->app_shim_host_manager()->
+          extension_app_shim_handler();
+  Profile* profile = shell_window->profile();
+  Host* host = handler->FindHost(profile, shell_window->extension_id());
+  if (host) {
+    // Bring the window to the front without showing it.
+    ShellWindowRegistry::Get(profile)->ShellWindowActivated(shell_window);
+    host->OnAppRequestUserAttention();
+    return true;
+  } else {
+    // Just show the app.
+    SetAppHidden(profile, shell_window->extension_id(), false);
+    return false;
+  }
+}
+
 void ExtensionAppShimHandler::OnShimLaunch(Host* host,
                                            AppShimLaunchType launch_type) {
   const std::string& app_id = host->GetAppId();
@@ -290,15 +318,7 @@
   DCHECK(delegate_->ProfileExistsForPath(host->GetProfilePath()));
   Profile* profile = delegate_->ProfileForPath(host->GetProfilePath());
 
-  const ShellWindowList windows =
-      delegate_->GetWindows(profile, host->GetAppId());
-  for (ShellWindowList::const_reverse_iterator it = windows.rbegin();
-       it != windows.rend(); ++it) {
-    if (hidden)
-      (*it)->GetBaseWindow()->Hide();
-    else
-      (*it)->GetBaseWindow()->ShowInactive();
-  }
+  SetAppHidden(profile, host->GetAppId(), hidden);
 }
 
 void ExtensionAppShimHandler::OnShimQuit(Host* host) {
@@ -312,12 +332,8 @@
        it != windows.end(); ++it) {
     (*it)->GetBaseWindow()->Close();
   }
-
-  DCHECK_NE(0u, hosts_.count(make_pair(profile, app_id)));
-  host->OnAppClosed();
-
-  if (!browser_opened_ever_ && hosts_.empty())
-    delegate_->MaybeTerminate();
+  // Once the last window closes, flow will end up in OnAppDeactivated via
+  // AppLifetimeMonitor.
 }
 
 void ExtensionAppShimHandler::set_delegate(Delegate* delegate) {
@@ -357,20 +373,6 @@
       }
       break;
     }
-    case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED:
-    case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
-      std::string app_id;
-      if (type == chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED) {
-        app_id = content::Details<extensions::ExtensionHost>(details).ptr()
-            ->extension_id();
-      } else {
-        app_id = content::Details<extensions::Extension>(details).ptr()->id();
-      }
-      Host* host = FindHost(profile, app_id);
-      if (host)
-        host->OnAppClosed();
-      break;
-    }
     default: {
       NOTREACHED();  // Unexpected notification.
       break;
@@ -399,7 +401,14 @@
 }
 
 void ExtensionAppShimHandler::OnAppDeactivated(Profile* profile,
-                                               const std::string& app_id) {}
+                                               const std::string& app_id) {
+  Host* host = FindHost(profile, app_id);
+  if (host)
+    host->OnAppClosed();
+
+  if (!browser_opened_ever_ && hosts_.empty())
+    delegate_->MaybeTerminate();
+}
 
 void ExtensionAppShimHandler::OnAppStop(Profile* profile,
                                         const std::string& app_id) {}
diff --git a/apps/app_shim/extension_app_shim_handler_mac.h b/apps/app_shim/extension_app_shim_handler_mac.h
index 8a9f64e..f707232 100644
--- a/apps/app_shim/extension_app_shim_handler_mac.h
+++ b/apps/app_shim/extension_app_shim_handler_mac.h
@@ -32,6 +32,8 @@
 
 namespace apps {
 
+class ShellWindow;
+
 // This app shim handler that handles events for app shims that correspond to an
 // extension.
 class ExtensionAppShimHandler : public AppShimHandler,
@@ -67,6 +69,10 @@
 
   static void QuitAppForWindow(ShellWindow* shell_window);
 
+  // Brings the window to the front without showing it and instructs the shim to
+  // request user attention. Returns false if there is no shim for this window.
+  static bool RequestUserAttentionForWindow(ShellWindow* shell_window);
+
   // AppShimHandler overrides:
   virtual void OnShimLaunch(Host* host, AppShimLaunchType launch_type) OVERRIDE;
   virtual void OnShimClose(Host* host) OVERRIDE;
diff --git a/apps/app_shim/extension_app_shim_handler_mac_unittest.cc b/apps/app_shim/extension_app_shim_handler_mac_unittest.cc
index 636ba1b..c51a017 100644
--- a/apps/app_shim/extension_app_shim_handler_mac_unittest.cc
+++ b/apps/app_shim/extension_app_shim_handler_mac_unittest.cc
@@ -65,7 +65,7 @@
   }
   virtual ~TestingExtensionAppShimHandler() {}
 
-  MOCK_METHOD2(OnShimFocus, void(Host* host, AppShimFocusType));
+  MOCK_METHOD2(OnShimFocus, void(Host*, AppShimFocusType));
 
   void RealOnShimFocus(Host* host, AppShimFocusType focus_type) {
     ExtensionAppShimHandler::OnShimFocus(host, focus_type);
@@ -102,6 +102,7 @@
     handler_->OnShimClose(this);
     ++close_count_;
   }
+  virtual void OnAppRequestUserAttention() OVERRIDE {}
   virtual base::FilePath GetProfilePath() const OVERRIDE {
     return profile_path_;
   }
@@ -252,10 +253,6 @@
   handler_->OnShimLaunch(&host_aa_, APP_SHIM_LAUNCH_REGISTER_ONLY);
   EXPECT_EQ(&host_aa_, handler_->FindHost(&profile_a_, kTestAppIdA));
 
-  // Closing all windows does not quit the shim.
-  handler_->OnAppDeactivated(&profile_a_, kTestAppIdA);
-  EXPECT_EQ(0, host_aa_.close_count());
-
   // Return no shell windows for OnShimFocus and OnShimQuit.
   ShellWindowList shell_window_list;
   EXPECT_CALL(*delegate_, GetWindows(&profile_a_, kTestAppIdA))
@@ -276,10 +273,16 @@
   EXPECT_CALL(*delegate_, LaunchApp(&profile_a_, extension_a_.get()));
   handler_->OnShimFocus(&host_aa_, APP_SHIM_FOCUS_REOPEN);
 
-  // Quit closes the shim.
+  // Quit just closes all the windows. This tests that it doesn't terminate,
+  // but we expect closing all windows triggers a OnAppDeactivated from
+  // AppLifetimeMonitor.
+  handler_->OnShimQuit(&host_aa_);
+
+  // Closing all windows closes the shim and checks if Chrome should be
+  // terminated.
   EXPECT_CALL(*delegate_, MaybeTerminate())
       .WillOnce(Return());
-  handler_->OnShimQuit(&host_aa_);
+  handler_->OnAppDeactivated(&profile_a_, kTestAppIdA);
   EXPECT_EQ(1, host_aa_.close_count());
 }
 
@@ -295,21 +298,32 @@
   handler_->OnShimLaunch(&host_ab_, register_only);
   EXPECT_EQ(&host_ab_, handler_->FindHost(&profile_a_, kTestAppIdB));
 
-  // The following quits should not terminate.
+  // Return empty window list.
   ShellWindowList shell_window_list;
   EXPECT_CALL(*delegate_, GetWindows(_, _))
       .WillRepeatedly(Return(shell_window_list));
+
+  // Quitting when there's another shim should not terminate.
   EXPECT_CALL(*delegate_, MaybeTerminate())
       .Times(0);
+  handler_->OnAppDeactivated(&profile_a_, kTestAppIdA);
 
-  // Quit when there are other shims.
-  handler_->OnShimQuit(&host_aa_);
+  // Quitting when it's the last shim should terminate.
+  EXPECT_CALL(*delegate_, MaybeTerminate());
+  handler_->OnAppDeactivated(&profile_a_, kTestAppIdB);
 
-  // Quit after a browser window has opened.
+  // Launch a shim again.
+  EXPECT_CALL(host_aa_, OnAppLaunchComplete(APP_SHIM_LAUNCH_SUCCESS));
+  handler_->OnShimLaunch(&host_aa_, register_only);
+  EXPECT_EQ(&host_aa_, handler_->FindHost(&profile_a_, kTestAppIdA));
+
+  // Quitting after a browser window has opened should not terminate.
   handler_->Observe(chrome::NOTIFICATION_BROWSER_OPENED,
                     content::NotificationService::AllSources(),
                     content::NotificationService::NoDetails());
-  handler_->OnShimQuit(&host_ab_);
+  EXPECT_CALL(*delegate_, MaybeTerminate())
+      .Times(0);
+  handler_->OnAppDeactivated(&profile_a_, kTestAppIdA);
 }
 
 TEST_F(ExtensionAppShimHandlerTest, RegisterOnly) {
diff --git a/apps/launcher.cc b/apps/launcher.cc
index e5990a5..6843790 100644
--- a/apps/launcher.cc
+++ b/apps/launcher.cc
@@ -46,11 +46,13 @@
 namespace app_runtime = extensions::api::app_runtime;
 
 using content::BrowserThread;
+using extensions::app_file_handler_util::CheckWritableFiles;
 using extensions::app_file_handler_util::FileHandlerForId;
 using extensions::app_file_handler_util::FileHandlerCanHandleFile;
 using extensions::app_file_handler_util::FirstFileHandlerForFile;
 using extensions::app_file_handler_util::CreateFileEntry;
 using extensions::app_file_handler_util::GrantedFileEntry;
+using extensions::app_file_handler_util::HasFileSystemWritePermission;
 using extensions::Extension;
 using extensions::ExtensionHost;
 using extensions::ExtensionSystem;
@@ -125,15 +127,19 @@
 
     DCHECK(file_path_.IsAbsolute());
 
-#if defined(OS_CHROMEOS)
-    if (drive::util::IsUnderDriveMountPoint(file_path_)) {
-      GetMimeTypeAndLaunchForDriveFile();
+    if (HasFileSystemWritePermission(extension_)) {
+      std::vector<base::FilePath> paths;
+      paths.push_back(file_path_);
+      CheckWritableFiles(
+          paths,
+          profile_,
+          false,
+          base::Bind(&PlatformAppPathLauncher::OnFileValid, this),
+          base::Bind(&PlatformAppPathLauncher::OnFileInvalid, this));
       return;
     }
-#endif
 
-    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(
-            &PlatformAppPathLauncher::GetMimeTypeAndLaunch, this));
+    OnFileValid();
   }
 
   void LaunchWithHandler(const std::string& handler_id) {
@@ -146,6 +152,24 @@
 
   virtual ~PlatformAppPathLauncher() {}
 
+  void OnFileValid() {
+#if defined(OS_CHROMEOS)
+    if (drive::util::IsUnderDriveMountPoint(file_path_)) {
+      PlatformAppPathLauncher::GetMimeTypeAndLaunchForDriveFile();
+      return;
+    }
+#endif
+
+    BrowserThread::PostTask(
+        BrowserThread::FILE,
+        FROM_HERE,
+        base::Bind(&PlatformAppPathLauncher::GetMimeTypeAndLaunch, this));
+  }
+
+  void OnFileInvalid(const base::FilePath& /* error_path */) {
+    LaunchWithNoLaunchData();
+  }
+
   void GetMimeTypeAndLaunch() {
     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
 
@@ -260,12 +284,12 @@
       return;
     }
 
-    GrantedFileEntry file_entry = CreateFileEntry(
-        profile_,
-        extension_->id(),
-        host->render_process_host()->GetID(),
-        file_path_,
-        false);
+    GrantedFileEntry file_entry =
+        CreateFileEntry(profile_,
+                        extension_,
+                        host->render_process_host()->GetID(),
+                        file_path_,
+                        false);
     extensions::AppEventRouter::DispatchOnLaunchedEventWithFileEntry(
         profile_, extension_, handler_id_, mime_type, file_entry);
   }
diff --git a/apps/native_app_window.h b/apps/native_app_window.h
index fcad3ce..9a1e05b 100644
--- a/apps/native_app_window.h
+++ b/apps/native_app_window.h
@@ -51,6 +51,12 @@
   // borders) and the content bounds, if any.
   virtual gfx::Insets GetFrameInsets() const = 0;
 
+  // Hide or show this window as part of hiding or showing the app.
+  // This may have different logic to Hide, Show, and ShowInactive as those are
+  // called via the AppWindow javascript API.
+  virtual void ShowWithApp() = 0;
+  virtual void HideWithApp() = 0;
+
   virtual ~NativeAppWindow() {}
 };
 
diff --git a/apps/pref_names.cc b/apps/pref_names.cc
index 02b652c..2ca3a4a 100644
--- a/apps/pref_names.cc
+++ b/apps/pref_names.cc
@@ -8,6 +8,10 @@
 
 namespace prefs {
 
+// A boolean that tracks whether apps are allowed to enter fullscreen mode.
+extern const char kAppFullscreenAllowed[] =
+    "apps.fullscreen.allowed";
+
 // A boolean that tracks whether the user has ever enabled the app launcher.
 const char kAppLauncherHasBeenEnabled[] =
     "apps.app_launcher.has_been_enabled";
diff --git a/apps/pref_names.h b/apps/pref_names.h
index 55af7d0..6a03b86 100644
--- a/apps/pref_names.h
+++ b/apps/pref_names.h
@@ -10,6 +10,7 @@
 
 // Alphabetical list of preference names specific to Apps component.
 // Keep alphabetized and document each one in the source file.
+extern const char kAppFullscreenAllowed[];
 extern const char kAppLauncherHasBeenEnabled[];
 extern const char kAppLauncherIsEnabled[];
 extern const char kAppLauncherShortcutVersion[];
diff --git a/apps/prefs.cc b/apps/prefs.cc
index 9347783..d53be17 100644
--- a/apps/prefs.cc
+++ b/apps/prefs.cc
@@ -38,6 +38,12 @@
 }
 
 void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
+#if !defined(OS_MACOSX)
+  registry->RegisterBooleanPref(
+      prefs::kAppFullscreenAllowed, true,
+      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+#endif
+
   // Indicates whether app shortcuts have been created.
   registry->RegisterBooleanPref(
       prefs::kShortcutsHaveBeenCreated, false,
diff --git a/apps/saved_files_service.cc b/apps/saved_files_service.cc
index fcf0248..f832ffe 100644
--- a/apps/saved_files_service.cc
+++ b/apps/saved_files_service.cc
@@ -36,8 +36,8 @@
 // The path to a file entry that the app had permission to access.
 const char kFileEntryPath[] = "path";
 
-// Whether or not the app had write access to a file entry.
-const char kFileEntryWritable[] = "writable";
+// Whether or not the the entry refers to a directory.
+const char kFileEntryIsDirectory[] = "is_directory";
 
 // The sequence number in the LRU of the file entry.
 const char kFileEntrySequenceNumber[] = "sequence_number";
@@ -62,7 +62,7 @@
 
   DictionaryValue* file_entry_dict = new DictionaryValue();
   file_entry_dict->Set(kFileEntryPath, CreateFilePathValue(file_entry.path));
-  file_entry_dict->SetBoolean(kFileEntryWritable, file_entry.writable);
+  file_entry_dict->SetBoolean(kFileEntryIsDirectory, file_entry.is_directory);
   file_entry_dict->SetInteger(kFileEntrySequenceNumber,
                               file_entry.sequence_number);
   file_entries->SetWithoutPathExpansion(file_entry.id, file_entry_dict);
@@ -122,31 +122,30 @@
     base::FilePath file_path;
     if (!GetValueAsFilePath(*path_value, &file_path))
       continue;
-    bool writable = false;
-    if (!file_entry->GetBoolean(kFileEntryWritable, &writable))
-      continue;
+    bool is_directory = false;
+    file_entry->GetBoolean(kFileEntryIsDirectory, &is_directory);
     int sequence_number = 0;
     if (!file_entry->GetInteger(kFileEntrySequenceNumber, &sequence_number))
       continue;
     if (!sequence_number)
       continue;
     result.push_back(
-        SavedFileEntry(it.key(), file_path, writable, sequence_number));
+        SavedFileEntry(it.key(), file_path, is_directory, sequence_number));
   }
   return result;
 }
 
 }  // namespace
 
-SavedFileEntry::SavedFileEntry() : writable(false), sequence_number(0) {}
+SavedFileEntry::SavedFileEntry() : is_directory(false), sequence_number(0) {}
 
 SavedFileEntry::SavedFileEntry(const std::string& id,
                                const base::FilePath& path,
-                               bool writable,
+                               bool is_directory,
                                int sequence_number)
     : id(id),
       path(path),
-      writable(writable),
+      is_directory(is_directory),
       sequence_number(sequence_number) {}
 
 class SavedFilesService::SavedFiles {
@@ -156,7 +155,7 @@
 
   void RegisterFileEntry(const std::string& id,
                          const base::FilePath& file_path,
-                         bool writable);
+                         bool is_directory);
   void EnqueueFileEntry(const std::string& id);
   bool IsRegistered(const std::string& id) const;
   const SavedFileEntry* GetFileEntry(const std::string& id) const;
@@ -231,8 +230,8 @@
 void SavedFilesService::RegisterFileEntry(const std::string& extension_id,
                                           const std::string& id,
                                           const base::FilePath& file_path,
-                                          bool writable) {
-  GetOrInsert(extension_id)->RegisterFileEntry(id, file_path, writable);
+                                          bool is_directory) {
+  GetOrInsert(extension_id)->RegisterFileEntry(id, file_path, is_directory);
 }
 
 void SavedFilesService::EnqueueFileEntry(const std::string& extension_id,
@@ -316,12 +315,12 @@
 void SavedFilesService::SavedFiles::RegisterFileEntry(
     const std::string& id,
     const base::FilePath& file_path,
-    bool writable) {
+    bool is_directory) {
   if (ContainsKey(registered_file_entries_, id))
     return;
 
   registered_file_entries_.insert(
-      std::make_pair(id, new SavedFileEntry(id, file_path, writable, 0)));
+      std::make_pair(id, new SavedFileEntry(id, file_path, is_directory, 0)));
 }
 
 void SavedFilesService::SavedFiles::EnqueueFileEntry(const std::string& id) {
diff --git a/apps/saved_files_service.h b/apps/saved_files_service.h
index 48a9a42..4c86a0c 100644
--- a/apps/saved_files_service.h
+++ b/apps/saved_files_service.h
@@ -37,7 +37,7 @@
 
   SavedFileEntry(const std::string& id,
                  const base::FilePath& path,
-                 bool writable,
+                 bool is_directory,
                  int sequence_number);
 
   // The opaque id of this file entry.
@@ -46,8 +46,8 @@
   // The path to a file entry that the app had permission to access.
   base::FilePath path;
 
-  // Whether or not the app had write access to a file entry.
-  bool writable;
+  // Whether or not the entry refers to a directory.
+  bool is_directory;
 
   // The sequence number in the LRU of the file entry. The value 0 indicates
   // that the entry is not in the LRU.
@@ -70,7 +70,7 @@
   void RegisterFileEntry(const std::string& extension_id,
                          const std::string& id,
                          const base::FilePath& file_path,
-                         bool writable);
+                         bool is_directory);
 
   // If the file with |id| is not in the queue of files to be retained
   // permanently, adds the file to the back of the queue, evicting the least
diff --git a/apps/saved_files_service_unittest.cc b/apps/saved_files_service_unittest.cc
index cc66c35..313c97c 100644
--- a/apps/saved_files_service_unittest.cc
+++ b/apps/saved_files_service_unittest.cc
@@ -71,7 +71,7 @@
     ASSERT_TRUE(entry);
     EXPECT_EQ(id_string, entry->id);
     EXPECT_EQ(path_, entry->path);
-    EXPECT_TRUE(entry->writable);
+    EXPECT_TRUE(entry->is_directory);
     EXPECT_EQ(sequence_number, entry->sequence_number);
   }
 
diff --git a/apps/shell_window.cc b/apps/shell_window.cc
index 425adf6..96c6abd 100644
--- a/apps/shell_window.cc
+++ b/apps/shell_window.cc
@@ -37,6 +37,11 @@
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/screen.h"
 
+#if !defined(OS_MACOSX)
+#include "apps/pref_names.h"
+#include "base/prefs/pref_service.h"
+#endif
+
 using content::ConsoleMessageLevel;
 using content::WebContents;
 using extensions::APIPermission;
@@ -494,6 +499,16 @@
 
 void ShellWindow::ToggleFullscreenModeForTab(content::WebContents* source,
                                              bool enter_fullscreen) {
+#if !defined(OS_MACOSX)
+  // Do not enter fullscreen mode if disallowed by pref.
+  // TODO(bartfab): Add a test once it becomes possible to simulate a user
+  // gesture. http://crbug.com/174178
+  if (enter_fullscreen &&
+      !profile()->GetPrefs()->GetBoolean(prefs::kAppFullscreenAllowed)) {
+    return;
+  }
+#endif
+
   if (!IsExtensionWithPermissionOrSuggestInConsole(
       APIPermission::kFullscreen,
       extension_,
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc
index d80cfea..de85b56 100644
--- a/ash/accelerators/accelerator_controller.cc
+++ b/ash/accelerators/accelerator_controller.cc
@@ -747,8 +747,10 @@
     case SHOW_SYSTEM_TRAY_BUBBLE: {
       internal::RootWindowController* controller =
           internal::RootWindowController::ForActiveRootWindow();
-      if (!controller->GetSystemTray()->HasSystemBubble())
+      if (!controller->GetSystemTray()->HasSystemBubble()) {
         controller->GetSystemTray()->ShowDefaultView(BUBBLE_CREATE_NEW);
+        return true;
+      }
       break;
     }
     case SHOW_MESSAGE_CENTER_BUBBLE: {
diff --git a/ash/accelerators/accelerator_table.cc b/ash/accelerators/accelerator_table.cc
index 982176a..e3902c7 100644
--- a/ash/accelerators/accelerator_table.cc
+++ b/ash/accelerators/accelerator_table.cc
@@ -270,6 +270,7 @@
   PRINT_VIEW_HIERARCHY,
   PRINT_WINDOW_HIERARCHY,
   ROTATE_WINDOW,
+  SHOW_SYSTEM_TRAY_BUBBLE,
   SWITCH_IME,  // Switch to another IME depending on the accelerator.
   TAKE_PARTIAL_SCREENSHOT,
   TAKE_SCREENSHOT,
diff --git a/ash/ash.gyp b/ash/ash.gyp
index a91b268..cc88ed2 100644
--- a/ash/ash.gyp
+++ b/ash/ash.gyp
@@ -134,10 +134,8 @@
         'keyboard_overlay/keyboard_overlay_delegate.h',
         'keyboard_overlay/keyboard_overlay_view.cc',
         'keyboard_overlay/keyboard_overlay_view.h',
-        'launcher/alternate_app_list_button.cc',
-        'launcher/alternate_app_list_button.h',
-        'launcher/app_list_button.cc',
-        'launcher/app_list_button.h',
+        'launcher/app_list_launcher_item_delegate.cc',
+        'launcher/app_list_launcher_item_delegate.h',
         'launcher/launcher.cc',
         'launcher/launcher.h',
         'launcher/launcher_alignment_menu.cc',
@@ -145,7 +143,10 @@
         'launcher/launcher_button.cc',
         'launcher/launcher_button.h',
         'launcher/launcher_delegate.h',
+        'launcher/launcher_item_delegate_manager.cc',
+        'launcher/launcher_item_delegate_manager.h',
         'launcher/launcher_icon_observer.h',
+        'launcher/launcher_item_delegate.h',
         'launcher/launcher_model.cc',
         'launcher/launcher_model.h',
         'launcher/launcher_model_observer.h',
@@ -159,13 +160,6 @@
         'launcher/launcher_util.h',
         'launcher/launcher_view.cc',
         'launcher/launcher_view.h',
-        'launcher/overflow_bubble.cc',
-        'launcher/overflow_bubble.h',
-        'launcher/overflow_button.cc',
-        'launcher/overflow_button.h',
-        'launcher/scoped_observer_with_duplicated_sources.h',
-        'launcher/tabbed_launcher_button.cc',
-        'launcher/tabbed_launcher_button.h',
         'magnifier/magnification_controller.cc',
         'magnifier/magnification_controller.h',
         'magnifier/magnifier_constants.h',
@@ -186,8 +180,17 @@
         'screenshot_delegate.h',
         'session_state_delegate.h',
         'session_state_observer.h',
+        'shelf/alternate_app_list_button.cc',
+        'shelf/alternate_app_list_button.h',
+        'shelf/app_list_button.cc',
+        'shelf/app_list_button.h',
         'shelf/background_animator.cc',
         'shelf/background_animator.h',
+        'shelf/overflow_bubble.cc',
+        'shelf/overflow_bubble.h',
+        'shelf/overflow_button.cc',
+        'shelf/overflow_button.h',
+        'shelf/scoped_observer_with_duplicated_sources.h',
         'shelf/shelf_bezel_event_filter.cc',
         'shelf/shelf_bezel_event_filter.h',
         'shelf/shelf_layout_manager.cc',
@@ -286,6 +289,8 @@
         'system/status_area_widget.h',
         'system/status_area_widget_delegate.cc',
         'system/status_area_widget_delegate.h',
+        'system/system_notifier.cc',
+        'system/system_notifier.h',
         'system/tray/actionable_view.cc',
         'system/tray/actionable_view.h',
         'system/tray/fixed_sized_image_view.cc',
@@ -426,6 +431,8 @@
         'wm/mru_window_tracker.h',
         'wm/overlay_event_filter.cc',
         'wm/overlay_event_filter.h',
+        'wm/overview/window_overview.cc',
+        'wm/overview/window_overview.h',
         'wm/overview/window_selector.cc',
         'wm/overview/window_selector.h',
         'wm/overview/window_selector_controller.cc',
@@ -698,13 +705,13 @@
         'launcher/launcher_tooltip_manager_unittest.cc',
         'launcher/launcher_unittest.cc',
         'launcher/launcher_view_unittest.cc',
-        'launcher/scoped_observer_with_duplicated_sources_unittest.cc',
         'magnifier/magnification_controller_unittest.cc',
         'root_window_controller_unittest.cc',
         'screen_ash_unittest.cc',
         'screensaver/screensaver_view_unittest.cc',
         'session_state_delegate_stub.cc',
         'session_state_delegate_stub.h',
+        'shelf/scoped_observer_with_duplicated_sources_unittest.cc',
         'shelf/shelf_layout_manager_unittest.cc',
         'shelf/shelf_widget_unittest.cc',
         'shell_unittest.cc',
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 62693dc..a0bafae 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -560,12 +560,18 @@
       <message name="IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_SUCCESS" desc="The title of the notification when a screenshot was taken.">
         Screenshot taken
       </message>
+      <message name="IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_DISABLED" desc="The title of the notification when taking a screenshot failed because screenshots are disabled.">
+        Screenshots disabled
+      </message>
       <message name="IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_FAIL" desc="The title of the notification when taking a screenshot failed.">
         An error occurred
       </message>
       <message name="IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_SUCCESS" desc="The text of the notification when a screenshot was taken.">
         Click to view
       </message>
+      <message name="IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_DISABLED" desc="The text of the notification when taking a screenshot failed because screenshots are disabled by administrator policy.">
+        The ability to take screenshots has been disabled by your administrator.
+      </message>
       <message name="IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_FAIL" desc="The text of the notification when taking a screenshot failed.">
         Failed to save screenshot
       </message>
diff --git a/ash/ash_switches.cc b/ash/ash_switches.cc
index 9fbc975..66f4927 100644
--- a/ash/ash_switches.cc
+++ b/ash/ash_switches.cc
@@ -90,6 +90,9 @@
 // Enable the dock area on a desktop.
 const char kAshEnableDockedWindows[] = "ash-enable-docked-windows";
 
+// Enable dragging items off the shelf to unpin them.
+const char kAshEnableDragOffShelf[] = "ash-enable-drag-off-shelf";
+
 // Enable immersive fullscreen mode, regardless of default setting.
 const char kAshEnableImmersiveFullscreen[] = "ash-enable-immersive-fullscreen";
 
@@ -181,6 +184,11 @@
       HasSwitch(ash::switches::kAshUseAlternateShelfLayout);
 }
 
+bool UseDragOffShelf() {
+  return CommandLine::ForCurrentProcess()->
+      HasSwitch(ash::switches::kAshEnableDragOffShelf);
+}
+
 bool ShowShelfAlignmentMenu() {
   return CommandLine::ForCurrentProcess()->
       HasSwitch(switches::kShowShelfAlignmentMenu);
diff --git a/ash/ash_switches.h b/ash/ash_switches.h
index ddafe6d..108592a 100644
--- a/ash/ash_switches.h
+++ b/ash/ash_switches.h
@@ -42,6 +42,7 @@
 ASH_EXPORT extern const char kAshEnableAdvancedGestures[];
 ASH_EXPORT extern const char kAshEnableBrightnessControl[];
 ASH_EXPORT extern const char kAshEnableDockedWindows[];
+ASH_EXPORT extern const char kAshEnableDragOffShelf[];
 #if defined(OS_LINUX)
 ASH_EXPORT extern const char kAshEnableMemoryMonitor[];
 #endif
@@ -70,6 +71,9 @@
 // Returns true if the alternate shelf layout should be used.
 ASH_EXPORT bool UseAlternateShelfLayout();
 
+// Returns true if items can be dragged off the shelf to unpin.
+ASH_EXPORT bool UseDragOffShelf();
+
 // Returns true if side shelf alignment is enabled.
 ASH_EXPORT bool ShowShelfAlignmentMenu();
 
diff --git a/ash/desktop_background/desktop_background_widget_controller.cc b/ash/desktop_background/desktop_background_widget_controller.cc
index cbd7727..575dc69 100644
--- a/ash/desktop_background/desktop_background_widget_controller.cc
+++ b/ash/desktop_background/desktop_background_widget_controller.cc
@@ -122,11 +122,14 @@
   if (widget_) {
     ui::ScopedLayerAnimationSettings settings(
         widget_->GetNativeView()->layer()->GetAnimator());
-    settings.SetPreemptionStrategy(ui::LayerAnimator::ENQUEUE_NEW_ANIMATION);
     settings.AddObserver(new ShowWallpaperAnimationObserver(
         root_window_controller, widget_,
         Shell::GetInstance()->user_wallpaper_delegate()->
             ShouldShowInitialAnimation()));
+    // When |widget_| shows, AnimateShowWindowCommon() is called to do the
+    // animation. Sets transition duration to 0 to avoid animating to the
+    // show animation's initial values.
+    settings.SetTransitionDuration(base::TimeDelta());
     widget_->Show();
     widget_->GetNativeView()->SetName("DesktopBackgroundView");
   } else if (layer_)
diff --git a/ash/display/display_error_observer_chromeos.cc b/ash/display/display_error_observer_chromeos.cc
index f12b61e..c01152c 100644
--- a/ash/display/display_error_observer_chromeos.cc
+++ b/ash/display/display_error_observer_chromeos.cc
@@ -4,6 +4,7 @@
 
 #include "ash/display/display_error_observer_chromeos.h"
 
+#include "ash/system/system_notifier.h"
 #include "grit/ash_resources.h"
 #include "grit/ash_strings.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -21,25 +22,6 @@
 
 const char kDisplayErrorNotificationId[] = "chrome://settings/display/error";
 
-class DisplayErrorNotificationDelegate
-    : public message_center::NotificationDelegate {
- public:
-  DisplayErrorNotificationDelegate() {}
-
-  // message_center::NotificationDelegate overrides:
-  virtual void Display() OVERRIDE {}
-  virtual void Error() OVERRIDE {}
-  virtual void Close(bool by_user) OVERRIDE {}
-  virtual bool HasClickedListener() OVERRIDE { return false; }
-  virtual void Click() OVERRIDE { }
-
- protected:
-  virtual ~DisplayErrorNotificationDelegate() {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DisplayErrorNotificationDelegate);
-};
-
 }  // namespace
 
 DisplayErrorObserver::DisplayErrorObserver() {
@@ -67,9 +49,9 @@
       base::string16(),  // message
       bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY),
       base::string16(),  // display_source
-      std::string(),  // extension_id
+      message_center::NotifierId(NOTIFIER_DISPLAY_ERROR),
       message_center::RichNotificationData(),
-      new DisplayErrorNotificationDelegate()));
+      NULL));
   message_center::MessageCenter::Get()->AddNotification(notification.Pass());
 }
 
diff --git a/ash/display/display_manager.h b/ash/display/display_manager.h
index b2ef8d1..4699eee 100644
--- a/ash/display/display_manager.h
+++ b/ash/display/display_manager.h
@@ -181,6 +181,8 @@
   // when displays are mirrored.
   size_t GetNumDisplays() const;
 
+  const std::vector<gfx::Display>& displays() const { return displays_; }
+
   // Returns the number of connected displays. This returns 2
   // when displays are mirrored.
   size_t num_connected_displays() const { return num_connected_displays_; }
diff --git a/ash/display/mirror_window_controller.cc b/ash/display/mirror_window_controller.cc
index 6ed6ff4..34cc57c 100644
--- a/ash/display/mirror_window_controller.cc
+++ b/ash/display/mirror_window_controller.cc
@@ -262,6 +262,7 @@
   current_cursor_rotation_ = display.rotation();
   int resource_id;
   bool success = ui::GetCursorDataFor(
+      ui::CURSOR_SET_NORMAL,  // Not support custom cursor set.
       current_cursor_type_,
       display.device_scale_factor(),
       &resource_id,
diff --git a/ash/display/resolution_notification_controller.cc b/ash/display/resolution_notification_controller.cc
index e2755a0..4085e0c 100644
--- a/ash/display/resolution_notification_controller.cc
+++ b/ash/display/resolution_notification_controller.cc
@@ -7,6 +7,7 @@
 #include "ash/display/display_controller.h"
 #include "ash/display/display_manager.h"
 #include "ash/shell.h"
+#include "ash/system/system_notifier.h"
 #include "base/strings/utf_string_conversions.h"
 #include "grit/ash_resources.h"
 #include "grit/ash_strings.h"
@@ -191,7 +192,8 @@
   return change_info_ && change_info_->timeout_count > 0;
 }
 
-void ResolutionNotificationController::CreateOrUpdateNotification() {
+void ResolutionNotificationController::CreateOrUpdateNotification(
+    bool enable_spoken_feedback) {
   message_center::MessageCenter* message_center =
       message_center::MessageCenter::Get();
   if (!change_info_) {
@@ -212,6 +214,8 @@
   data.buttons.push_back(message_center::ButtonInfo(
         l10n_util::GetStringUTF16(IDS_ASH_DISPLAY_RESOLUTION_CHANGE_REVERT)));
 
+  data.should_make_spoken_feedback_for_popup_updates = enable_spoken_feedback;
+
   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
   scoped_ptr<Notification> notification(new Notification(
       message_center::NOTIFICATION_TYPE_SIMPLE,
@@ -224,7 +228,7 @@
       timeout_message,
       bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY),
       base::string16() /* display_source */,
-      std::string() /* extension_id */,
+      message_center::NotifierId(NOTIFIER_DISPLAY_RESOLUTION_CHANGE),
       data,
       new ResolutionChangeNotificationDelegate(
           this, change_info_->timeout_count > 0)));
@@ -240,7 +244,7 @@
   if (change_info_->timeout_count == 0)
     RevertResolutionChange();
   else
-    CreateOrUpdateNotification();
+    CreateOrUpdateNotification(false);
 }
 
 void ResolutionNotificationController::AcceptResolutionChange(
@@ -282,7 +286,7 @@
   if (!change_info_)
     return;
 
-  CreateOrUpdateNotification();
+  CreateOrUpdateNotification(true);
   if (g_use_timer && change_info_->timeout_count > 0) {
     change_info_->timer.Start(FROM_HERE,
                               base::TimeDelta::FromSeconds(1),
diff --git a/ash/display/resolution_notification_controller.h b/ash/display/resolution_notification_controller.h
index 14e6b59..bacc9c8 100644
--- a/ash/display/resolution_notification_controller.h
+++ b/ash/display/resolution_notification_controller.h
@@ -70,7 +70,10 @@
   static const char kNotificationId[];
 
   // Create a new notification, or update its content if it already exists.
-  void CreateOrUpdateNotification();
+  // |enable_spoken_feedback| is set to false when the notification is updated
+  // during the countdown so the update isn't necessarily read by the spoken
+  // feedback.
+  void CreateOrUpdateNotification(bool enable_spoken_feedback);
 
   // Called every second for timeout.
   void OnTimerTick();
diff --git a/ash/drag_drop/drag_image_view.cc b/ash/drag_drop/drag_image_view.cc
index 4dd729b..5d0f2be 100644
--- a/ash/drag_drop/drag_image_view.cc
+++ b/ash/drag_drop/drag_image_view.cc
@@ -58,6 +58,10 @@
   widget_->SetBounds(gfx::Rect(position, widget_size_));
 }
 
+gfx::Rect DragImageView::GetBoundsInScreen() const {
+  return widget_->GetWindowBoundsInScreen();
+}
+
 void DragImageView::SetWidgetVisible(bool visible) {
   if (visible != widget_->IsVisible()) {
     if (visible)
@@ -67,6 +71,12 @@
   }
 }
 
+void DragImageView::SetOpacity(float visibility) {
+  DCHECK_GE(visibility, 0.0f);
+  DCHECK_LE(visibility, 1.0f);
+  widget_->SetOpacity(static_cast<int>(0xff * visibility));
+}
+
 void DragImageView::OnPaint(gfx::Canvas* canvas) {
   if (GetImage().isNull())
     return;
diff --git a/ash/drag_drop/drag_image_view.h b/ash/drag_drop/drag_image_view.h
index c2791c4..5480d00 100644
--- a/ash/drag_drop/drag_image_view.h
+++ b/ash/drag_drop/drag_image_view.h
@@ -14,6 +14,10 @@
 namespace ash {
 namespace internal {
 
+// This class allows to show a (native) view always on top of everything. It
+// does this by creating a widget and setting the content as the given view. The
+// caller can then use this object to freely move / drag it around on the
+// desktop in screen coordinates.
 class DragImageView : public views::ImageView {
  public:
   explicit DragImageView(gfx::NativeView context);
@@ -28,9 +32,15 @@
   // Sets the position of the native widget.
   void SetScreenPosition(const gfx::Point& position);
 
+  // Gets the image position in screen coordinates.
+  gfx::Rect GetBoundsInScreen() const;
+
   // Sets the visibility of the native widget.
   void SetWidgetVisible(bool visible);
 
+  // Sets the |opacity| of the image view between 0.0 and 1.0.
+  void SetOpacity(float opacity);
+
  private:
   // Overridden from views::ImageView.
   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
diff --git a/ash/launcher/OWNERS b/ash/launcher/OWNERS
index 3d25bb8..56a830a 100644
--- a/ash/launcher/OWNERS
+++ b/ash/launcher/OWNERS
@@ -1 +1,2 @@
 davemoore@chromium.org
+skuhne@chromium.org
diff --git a/ash/launcher/alternate_app_list_button.cc b/ash/launcher/alternate_app_list_button.cc
deleted file mode 100644
index 0466c71..0000000
--- a/ash/launcher/alternate_app_list_button.cc
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-#include "ash/launcher/alternate_app_list_button.h"
-
-#include "ash/ash_switches.h"
-#include "ash/launcher/launcher_button_host.h"
-#include "ash/launcher/launcher_types.h"
-#include "ash/shelf/shelf_layout_manager.h"
-#include "ash/shelf/shelf_widget.h"
-#include "ash/shell.h"
-#include "grit/ash_resources.h"
-#include "grit/ash_strings.h"
-#include "ui/base/accessibility/accessible_view_state.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/compositor/layer.h"
-#include "ui/compositor/layer_animation_element.h"
-#include "ui/compositor/layer_animation_sequence.h"
-#include "ui/compositor/scoped_layer_animation_settings.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/image/image_skia_operations.h"
-#include "ui/views/controls/button/image_button.h"
-
-namespace ash {
-namespace internal {
-namespace {
-const int kImagePaddingFromShelf = 5;
-}  // namespace
-
-// static
-const int AlternateAppListButton::kImageBoundsSize = 7;
-
-
-AlternateAppListButton::AlternateAppListButton(views::ButtonListener* listener,
-                                               LauncherButtonHost* host,
-                                               ShelfWidget* shelf_widget)
-    : views::ImageButton(listener),
-      host_(host),
-      shelf_widget_(shelf_widget) {
-  SetAccessibleName(l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_TITLE));
-  SetSize(gfx::Size(ShelfLayoutManager::kShelfSize,
-                    ShelfLayoutManager::kShelfSize));
-}
-
-AlternateAppListButton::~AlternateAppListButton() {
-}
-
-bool AlternateAppListButton::OnMousePressed(const ui::MouseEvent& event) {
-  ImageButton::OnMousePressed(event);
-  host_->PointerPressedOnButton(this, LauncherButtonHost::MOUSE, event);
-  return true;
-}
-
-void AlternateAppListButton::OnMouseReleased(const ui::MouseEvent& event) {
-  ImageButton::OnMouseReleased(event);
-  host_->PointerReleasedOnButton(this, LauncherButtonHost::MOUSE, false);
-}
-
-void AlternateAppListButton::OnMouseCaptureLost() {
-  host_->PointerReleasedOnButton(this, LauncherButtonHost::MOUSE, true);
-  ImageButton::OnMouseCaptureLost();
-}
-
-bool AlternateAppListButton::OnMouseDragged(const ui::MouseEvent& event) {
-  ImageButton::OnMouseDragged(event);
-  host_->PointerDraggedOnButton(this, LauncherButtonHost::MOUSE, event);
-  return true;
-}
-
-void AlternateAppListButton::OnMouseMoved(const ui::MouseEvent& event) {
-  ImageButton::OnMouseMoved(event);
-  host_->MouseMovedOverButton(this);
-}
-
-void AlternateAppListButton::OnMouseEntered(const ui::MouseEvent& event) {
-  ImageButton::OnMouseEntered(event);
-  host_->MouseEnteredButton(this);
-}
-
-void AlternateAppListButton::OnMouseExited(const ui::MouseEvent& event) {
-  ImageButton::OnMouseExited(event);
-  host_->MouseExitedButton(this);
-}
-
-void AlternateAppListButton::OnPaint(gfx::Canvas* canvas) {
-  // Call the base class first to paint any background/borders.
-  View::OnPaint(canvas);
-
-  int background_image_id = 0;
-  if (Shell::GetInstance()->GetAppListTargetVisibility()) {
-    background_image_id = IDR_AURA_NOTIFICATION_BACKGROUND_PRESSED;
-  } else {
-    if (shelf_widget_->GetDimsShelf())
-      background_image_id = IDR_AURA_NOTIFICATION_BACKGROUND_ON_BLACK;
-    else
-      background_image_id = IDR_AURA_NOTIFICATION_BACKGROUND_NORMAL;
-  }
-  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
-  const gfx::ImageSkia* background_image =
-      rb.GetImageNamed(background_image_id).ToImageSkia();
-  const gfx::ImageSkia* forground_image =
-      rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST_ALTERNATE).ToImageSkia();
-
-  gfx::Rect contents_bounds = GetContentsBounds();
-  gfx::Rect background_bounds, forground_bounds;
-
-  ShelfAlignment alignment = shelf_widget_->GetAlignment();
-  background_bounds.set_size(background_image->size());
-  if (alignment == SHELF_ALIGNMENT_LEFT) {
-    background_bounds.set_x(contents_bounds.width() -
-        kImagePaddingFromShelf - background_image->width());
-    background_bounds.set_y(contents_bounds.y() +
-        (contents_bounds.height() - background_image->height()) / 2);
-  } else if(alignment == SHELF_ALIGNMENT_RIGHT) {
-    background_bounds.set_x(kImagePaddingFromShelf);
-    background_bounds.set_y(contents_bounds.y() +
-        (contents_bounds.height() - background_image->height()) / 2);
-  } else {
-    background_bounds.set_y(kImagePaddingFromShelf);
-    background_bounds.set_x(contents_bounds.x() +
-        (contents_bounds.width() - background_image->width()) / 2);
-  }
-
-  forground_bounds.set_size(forground_image->size());
-  forground_bounds.set_x(background_bounds.x() +
-      std::max(0,
-          (background_bounds.width() - forground_bounds.width()) / 2));
-  forground_bounds.set_y(background_bounds.y() +
-      std::max(0,
-          (background_bounds.height() - forground_bounds.height()) / 2));
-
-  canvas->DrawImageInt(*background_image,
-                       background_bounds.x(),
-                       background_bounds.y());
-  canvas->DrawImageInt(*forground_image,
-                       forground_bounds.x(),
-                       forground_bounds.y());
-
-  OnPaintFocusBorder(canvas);
-}
-
-void AlternateAppListButton::GetAccessibleState(
-    ui::AccessibleViewState* state) {
-  state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
-  state->name = host_->GetAccessibleName(this);
-}
-
-}  // namespace internal
-}  // namespace ash
diff --git a/ash/launcher/alternate_app_list_button.h b/ash/launcher/alternate_app_list_button.h
deleted file mode 100644
index f68207c..0000000
--- a/ash/launcher/alternate_app_list_button.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_LAUNCHER_ALTERNATE_APP_LIST_BUTTON_H_
-#define ASH_LAUNCHER_ALTERNATE_APP_LIST_BUTTON_H_
-
-#include "ui/views/controls/button/image_button.h"
-
-namespace ash {
-
-class ShelfWidget;
-
-namespace internal {
-
-class LauncherButtonHost;
-
-
-
-// Button used for the AppList icon on the launcher.
-// This class is an alternate implementation to
-// ash::internal::AppListButton for the purposes of testing an
-// alternate shelf layout (see ash_switches: UseAlternateShelfLayout).
-class AlternateAppListButton : public views::ImageButton {
- public:
-  // Bounds size (inset) required for the app icon image (in pixels).
-  static const int kImageBoundsSize;
-
-  AlternateAppListButton(views::ButtonListener* listener,
-                         LauncherButtonHost* host,
-                         ShelfWidget* shelf_widget);
-  virtual ~AlternateAppListButton();
-
- protected:
-  // View overrides:
-  virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
-  virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE;
-  virtual void OnMouseCaptureLost() OVERRIDE;
-  virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE;
-  virtual void OnMouseMoved(const ui::MouseEvent& event) OVERRIDE;
-  virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE;
-  virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE;
-  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
-  virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
-
- private:
-  LauncherButtonHost* host_;
-  // Reference to the shelf widget containing this button, owned by the
-  // root window controller.
-  ShelfWidget* shelf_widget_;
-
-  DISALLOW_COPY_AND_ASSIGN(AlternateAppListButton);
-};
-
-}  // namespace internal
-}  // namespace ash
-
-#endif  // ASH_LAUNCHER_ALTERNATE_APP_LIST_BUTTON_H_
diff --git a/ash/launcher/app_list_button.cc b/ash/launcher/app_list_button.cc
deleted file mode 100644
index 8879a38..0000000
--- a/ash/launcher/app_list_button.cc
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/launcher/app_list_button.h"
-
-#include <vector>
-
-#include "ash/launcher/launcher_button_host.h"
-#include "ash/launcher/launcher_types.h"
-#include "grit/ash_resources.h"
-#include "grit/ash_strings.h"
-#include "ui/base/accessibility/accessible_view_state.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/compositor/layer.h"
-#include "ui/compositor/layer_animation_element.h"
-#include "ui/compositor/layer_animation_sequence.h"
-#include "ui/compositor/scoped_layer_animation_settings.h"
-
-namespace ash {
-namespace internal {
-
-namespace {
-
-const int kAnimationDurationInMs = 600;
-const float kAnimationOpacity[] = { 1.0f, 0.4f, 1.0f };
-const int kBorderSize = 9;
-}  // namespace
-
-AppListButton::AppListButton(views::ButtonListener* listener,
-                             LauncherButtonHost* host)
-    : views::ImageButton(listener),
-      host_(host) {
-  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
-  SetImage(
-      views::CustomButton::STATE_NORMAL,
-      rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST).ToImageSkia());
-  SetImage(
-      views::CustomButton::STATE_HOVERED,
-      rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST_HOT).
-          ToImageSkia());
-  SetImage(
-      views::CustomButton::STATE_PRESSED,
-      rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST_PUSHED).
-          ToImageSkia());
-  SetAccessibleName(l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_TITLE));
-  SetSize(gfx::Size(kLauncherPreferredSize, kLauncherPreferredSize));
-  SetImageAlignment(ImageButton::ALIGN_CENTER, ImageButton::ALIGN_TOP);
-}
-
-AppListButton::~AppListButton() {
-}
-
-void AppListButton::StartLoadingAnimation() {
-  layer()->GetAnimator()->StopAnimating();
-
-  scoped_ptr<ui::LayerAnimationSequence> opacity_sequence(
-      new ui::LayerAnimationSequence());
-
-  opacity_sequence->set_is_cyclic(true);
-
-  for (size_t i = 0; i < arraysize(kAnimationOpacity); ++i) {
-    opacity_sequence->AddElement(
-        ui::LayerAnimationElement::CreateOpacityElement(
-            kAnimationOpacity[i],
-            base::TimeDelta::FromMilliseconds(kAnimationDurationInMs)));
-  }
-
-  ui::LayerAnimationElement::AnimatableProperties opacity_properties;
-  opacity_properties.insert(ui::LayerAnimationElement::OPACITY);
-  opacity_sequence->AddElement(
-      ui::LayerAnimationElement::CreatePauseElement(
-          opacity_properties,
-          base::TimeDelta::FromMilliseconds(kAnimationDurationInMs)));
-
-  // LayerAnimator takes ownership of the sequences.
-  layer()->GetAnimator()->ScheduleAnimation(opacity_sequence.release());
-}
-
-void AppListButton::StopLoadingAnimation() {
-  layer()->GetAnimator()->StopAnimating();
-
-  ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator());
-  settings.SetTransitionDuration(
-      base::TimeDelta::FromMilliseconds(kAnimationDurationInMs));
-  layer()->SetOpacity(1.0f);
-  layer()->SetTransform(gfx::Transform());
-}
-
-bool AppListButton::OnMousePressed(const ui::MouseEvent& event) {
-  ImageButton::OnMousePressed(event);
-  host_->PointerPressedOnButton(this, LauncherButtonHost::MOUSE, event);
-  return true;
-}
-
-void AppListButton::OnMouseReleased(const ui::MouseEvent& event) {
-  ImageButton::OnMouseReleased(event);
-  host_->PointerReleasedOnButton(this, LauncherButtonHost::MOUSE, false);
-}
-
-void AppListButton::OnMouseCaptureLost() {
-  host_->PointerReleasedOnButton(this, LauncherButtonHost::MOUSE, true);
-  ImageButton::OnMouseCaptureLost();
-}
-
-bool AppListButton::OnMouseDragged(const ui::MouseEvent& event) {
-  ImageButton::OnMouseDragged(event);
-  host_->PointerDraggedOnButton(this, LauncherButtonHost::MOUSE, event);
-  return true;
-}
-
-void AppListButton::OnMouseMoved(const ui::MouseEvent& event) {
-  ImageButton::OnMouseMoved(event);
-  host_->MouseMovedOverButton(this);
-}
-
-void AppListButton::OnMouseEntered(const ui::MouseEvent& event) {
-  ImageButton::OnMouseEntered(event);
-  host_->MouseEnteredButton(this);
-}
-
-void AppListButton::OnMouseExited(const ui::MouseEvent& event) {
-  ImageButton::OnMouseExited(event);
-  host_->MouseExitedButton(this);
-}
-
-void AppListButton::GetAccessibleState(ui::AccessibleViewState* state) {
-  state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
-  state->name = host_->GetAccessibleName(this);
-}
-
-}  // namespace internal
-}  // namespace ash
diff --git a/ash/launcher/app_list_button.h b/ash/launcher/app_list_button.h
deleted file mode 100644
index 4d25254..0000000
--- a/ash/launcher/app_list_button.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_LAUNCHER_APP_LIST_BUTTON_H_
-#define ASH_LAUNCHER_APP_LIST_BUTTON_H_
-
-#include "ui/views/controls/button/image_button.h"
-
-namespace ash {
-namespace internal {
-
-class LauncherButtonHost;
-
-// Button used for the AppList icon on the launcher.
-class AppListButton : public views::ImageButton {
- public:
-  AppListButton(views::ButtonListener* listener,
-                LauncherButtonHost* host);
-  virtual ~AppListButton();
-
-  void StartLoadingAnimation();
-  void StopLoadingAnimation();
-
- protected:
-  // View overrides:
-  virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
-  virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE;
-  virtual void OnMouseCaptureLost() OVERRIDE;
-  virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE;
-  virtual void OnMouseMoved(const ui::MouseEvent& event) OVERRIDE;
-  virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE;
-  virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE;
-  virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
-
- private:
-  LauncherButtonHost* host_;
-
-  DISALLOW_COPY_AND_ASSIGN(AppListButton);
-};
-
-}  // namespace internal
-}  // namespace ash
-
-#endif  // ASH_LAUNCHER_APP_LIST_BUTTON_H_
diff --git a/ash/launcher/app_list_launcher_item_delegate.cc b/ash/launcher/app_list_launcher_item_delegate.cc
new file mode 100644
index 0000000..ffa6ba9
--- /dev/null
+++ b/ash/launcher/app_list_launcher_item_delegate.cc
@@ -0,0 +1,65 @@
+// 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 "ash/launcher/app_list_launcher_item_delegate.h"
+
+#include "ash/launcher/launcher_item_delegate_manager.h"
+#include "ash/launcher/launcher_model.h"
+#include "ash/shell.h"
+#include "ash/shell_delegate.h"
+#include "grit/ash_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace ash {
+namespace internal {
+
+AppListLauncherItemDelegate::AppListLauncherItemDelegate() {
+  // TODO(simon.hong81): This works for the moment, but the AppList LauncherItem
+  // creation should move here.
+  ash::Shell::GetInstance()->launcher_item_delegate_manager()->
+      RegisterLauncherItemDelegate(ash::TYPE_APP_LIST, this);
+}
+
+AppListLauncherItemDelegate::~AppListLauncherItemDelegate() {
+  // Don't unregister this from LauncherItemDelegateManager.
+  // LauncherItemDelegateManager is already destroyed.
+}
+
+void AppListLauncherItemDelegate::ItemSelected(const LauncherItem& item,
+                                               const ui::Event& event) {
+  // Pass NULL here to show the app list in the currently active RootWindow.
+  Shell::GetInstance()->ToggleAppList(NULL);
+}
+
+base::string16 AppListLauncherItemDelegate::GetTitle(const LauncherItem& item) {
+  LauncherModel* model = Shell::GetInstance()->launcher_model();
+  DCHECK(model);
+  return model->status() == LauncherModel::STATUS_LOADING ?
+      l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_SYNCING_TITLE) :
+      l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_TITLE);
+}
+
+ui::MenuModel* AppListLauncherItemDelegate::CreateContextMenu(
+    const LauncherItem& item,
+    aura::RootWindow* root_window) {
+  return NULL;
+}
+
+LauncherMenuModel* AppListLauncherItemDelegate::CreateApplicationMenu(
+    const LauncherItem& item,
+    int event_flags) {
+  // AppList does not show an application menu.
+  return NULL;
+}
+
+bool AppListLauncherItemDelegate::IsDraggable(const LauncherItem& item) {
+  return false;
+}
+
+bool AppListLauncherItemDelegate::ShouldShowTooltip(const LauncherItem& item) {
+  return true;
+}
+
+}  // namespace internal
+}  // namespace ash
diff --git a/ash/launcher/app_list_launcher_item_delegate.h b/ash/launcher/app_list_launcher_item_delegate.h
new file mode 100644
index 0000000..6452c6e
--- /dev/null
+++ b/ash/launcher/app_list_launcher_item_delegate.h
@@ -0,0 +1,42 @@
+// 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 ASH_LAUNCHER_APP_LIST_LAUNCHER_ITEM_DELEGATE_H_
+#define ASH_LAUNCHER_APP_LIST_LAUNCHER_ITEM_DELEGATE_H_
+
+#include "ash/launcher/launcher_item_delegate.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+
+namespace ash {
+namespace internal {
+
+// LauncherItemDelegate for TYPE_APP_LIST.
+class AppListLauncherItemDelegate : public LauncherItemDelegate {
+ public:
+  AppListLauncherItemDelegate();
+
+  virtual ~AppListLauncherItemDelegate();
+
+  // ash::LauncherItemDelegate overrides:
+  virtual void ItemSelected(const LauncherItem& item,
+                            const ui::Event& event) OVERRIDE;
+  virtual base::string16 GetTitle(const LauncherItem& item) OVERRIDE;
+  virtual ui::MenuModel* CreateContextMenu(
+      const LauncherItem& item,
+      aura::RootWindow* root_window) OVERRIDE;
+  virtual LauncherMenuModel* CreateApplicationMenu(
+      const LauncherItem& item,
+      int event_flags) OVERRIDE;
+  virtual bool IsDraggable(const LauncherItem& item) OVERRIDE;
+  virtual bool ShouldShowTooltip(const LauncherItem& item) OVERRIDE;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppListLauncherItemDelegate);
+};
+
+}  // namespace internal
+}  // namespace ash
+
+#endif  // ASH_LAUNCHER_APP_LIST_LAUNCHER_ITEM_DELEGATE_H_
diff --git a/ash/launcher/launcher.cc b/ash/launcher/launcher.cc
index 008646d..49ac6e7 100644
--- a/ash/launcher/launcher.cc
+++ b/ash/launcher/launcher.cc
@@ -9,6 +9,8 @@
 
 #include "ash/focus_cycler.h"
 #include "ash/launcher/launcher_delegate.h"
+#include "ash/launcher/launcher_item_delegate.h"
+#include "ash/launcher/launcher_item_delegate_manager.h"
 #include "ash/launcher/launcher_model.h"
 #include "ash/launcher/launcher_navigator.h"
 #include "ash/launcher/launcher_view.h"
@@ -106,9 +108,9 @@
                      ui::EF_NONE,
                      false);
 
-  const ash::LauncherItems& items =
-      launcher_view_->model()->items();
-  delegate_->ItemSelected(items[index], event);
+  const ash::LauncherItem& item = launcher_view_->model()->items()[index];
+  Shell::GetInstance()->launcher_item_delegate_manager()->
+      GetLauncherItemDelegate(item.type)->ItemSelected(item, event);
 }
 
 void Launcher::CycleWindowLinear(CycleDirection direction) {
diff --git a/ash/launcher/launcher_button.cc b/ash/launcher/launcher_button.cc
index 26677ae..2ce96eb 100644
--- a/ash/launcher/launcher_button.cc
+++ b/ash/launcher/launcher_button.cc
@@ -67,7 +67,7 @@
 
   void RemoveObserver(Observer* observer) {
     observers_.RemoveObserver(observer);
-    if (observers_.size() == 0)
+    if (!observers_.might_have_observers())
       animation_.Stop();
   }
 
@@ -293,6 +293,10 @@
       skia::ImageOperations::RESIZE_BEST, gfx::Size(width, height)));
 }
 
+const gfx::ImageSkia& LauncherButton::GetImage() const {
+  return icon_view_->GetImage();
+}
+
 void LauncherButton::AddState(State state) {
   if (!(state_ & state)) {
     if (!ash::switches::UseAlternateShelfLayout() &&
@@ -511,8 +515,23 @@
 }
 
 void LauncherButton::UpdateState() {
-  // Even if not shown, the activation state image has an influence on the
-  // layout. To avoid any odd movement we assign a bitmap here.
+  UpdateBar();
+
+  icon_view_->SetHorizontalAlignment(
+      shelf_layout_manager_->PrimaryAxisValue(views::ImageView::CENTER,
+                                              views::ImageView::LEADING));
+  icon_view_->SetVerticalAlignment(
+      shelf_layout_manager_->PrimaryAxisValue(views::ImageView::LEADING,
+                                              views::ImageView::CENTER));
+  SchedulePaint();
+}
+
+void LauncherButton::UpdateBar() {
+  if (state_ & STATE_HIDDEN) {
+    bar_->SetVisible(false);
+    return;
+  }
+
   int bar_id = 0;
   if (ash::switches::UseAlternateShelfLayout()) {
     if (state_ & STATE_ACTIVE)
@@ -557,14 +576,6 @@
   }
 
   bar_->SetVisible(bar_id != 0 && state_ != STATE_NORMAL);
-
-  icon_view_->SetHorizontalAlignment(
-      shelf_layout_manager_->PrimaryAxisValue(views::ImageView::CENTER,
-                                              views::ImageView::LEADING));
-  icon_view_->SetVerticalAlignment(
-      shelf_layout_manager_->PrimaryAxisValue(views::ImageView::LEADING,
-                                              views::ImageView::CENTER));
-  SchedulePaint();
 }
 
 }  // namespace internal
diff --git a/ash/launcher/launcher_button.h b/ash/launcher/launcher_button.h
index 707aac0..dec0edc 100644
--- a/ash/launcher/launcher_button.h
+++ b/ash/launcher/launcher_button.h
@@ -27,13 +27,14 @@
     // Button has mouse hovering on it.
     STATE_HOVERED   = 1 << 0,
     // Underlying LauncherItem has a running instance.
-    //   e.g. A TYPE_TABBED item that has a window.
     STATE_RUNNING   = 1 << 1,
     // Underlying LauncherItem is active (i.e. has focus).
     STATE_ACTIVE    = 1 << 2,
     // Underlying LauncherItem needs user's attention.
     STATE_ATTENTION = 1 << 3,
     STATE_FOCUSED   = 1 << 4,
+    // Hide the status (temporarily for some animations).
+    STATE_HIDDEN = 1 << 5,
   };
 
   virtual ~LauncherButton();
@@ -46,6 +47,9 @@
   // Sets the image to display for this entry.
   void SetImage(const gfx::ImageSkia& image);
 
+  // Retrieve the image to show proxy operations.
+  const gfx::ImageSkia& GetImage() const;
+
   // |state| is or'd into the current state.
   void AddState(State state);
   void ClearState(State state);
@@ -61,6 +65,9 @@
   virtual void ShowContextMenu(const gfx::Point& p,
                                ui::MenuSourceType source_type) OVERRIDE;
 
+  // View override - needed by unit test.
+  virtual void OnMouseCaptureLost() OVERRIDE;
+
  protected:
   LauncherButton(views::ButtonListener* listener,
                  LauncherButtonHost* host,
@@ -91,7 +98,6 @@
   // View overrides:
   virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
   virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE;
-  virtual void OnMouseCaptureLost() OVERRIDE;
   virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE;
   virtual void OnMouseMoved(const ui::MouseEvent& event) OVERRIDE;
   virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE;
@@ -125,6 +131,9 @@
   // alignment. This may add or remove views, layout and paint.
   void UpdateState();
 
+  // Updates the status bar (bitmap, orientation, visibility).
+  void UpdateBar();
+
   LauncherButtonHost* host_;
   IconView* icon_view_;
   // Draws a bar underneath the image to represent the state of the application.
diff --git a/ash/launcher/launcher_delegate.h b/ash/launcher/launcher_delegate.h
index 322df38..db69bab 100644
--- a/ash/launcher/launcher_delegate.h
+++ b/ash/launcher/launcher_delegate.h
@@ -7,83 +7,22 @@
 
 #include "ash/ash_export.h"
 #include "ash/launcher/launcher_types.h"
-#include "base/strings/string16.h"
-#include "ui/base/models/simple_menu_model.h"
-
-namespace aura {
-class RootWindow;
-}
-
-namespace ui {
-class Event;
-}
 
 namespace ash {
 class Launcher;
 
-// A special menu model which keeps track of an "active" menu item.
-class ASH_EXPORT LauncherMenuModel : public ui::SimpleMenuModel {
- public:
-  explicit LauncherMenuModel(ui::SimpleMenuModel::Delegate* delegate)
-      : ui::SimpleMenuModel(delegate) {}
-
-  // Returns |true| when the given |command_id| is active and needs to be drawn
-  // in a special state.
-  virtual bool IsCommandActive(int command_id) const = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(LauncherMenuModel);
-};
-
 // Delegate for the Launcher.
 class ASH_EXPORT LauncherDelegate {
  public:
   // Launcher owns the delegate.
   virtual ~LauncherDelegate() {}
 
-  // Invoked when the user clicks on a window entry in the launcher.
-  // |event| is the click event. The |event| is dispatched by a view
-  // and has an instance of |views::View| as the event target
-  // but not |aura::Window|. If the |event| is of type KeyEvent, it is assumed
-  // that this was triggered by keyboard action (Alt+<number>) and special
-  // handling might happen.
-  virtual void ItemSelected(const LauncherItem& item,
-                            const ui::Event& event) = 0;
-
-  // Returns the title to display for the specified launcher item.
-  virtual base::string16 GetTitle(const LauncherItem& item) = 0;
-
-  // Returns the context menumodel for the specified item on
-  // |root_window|.  Return NULL if there should be no context
-  // menu. The caller takes ownership of the returned model.
-  virtual ui::MenuModel* CreateContextMenu(const LauncherItem& item,
-                                           aura::RootWindow* root_window) = 0;
-
-  // Returns the application menu model for the specified item. There are three
-  // possible return values:
-  //  - A return of NULL indicates that no menu is wanted for this item.
-  //  - A return of a menu with one item means that only the name of the
-  //    application/item was added and there are no active applications.
-  //    Note: This is useful for hover menus which also show context help.
-  //  - A list containing the title and the active list of items.
-  // The caller takes ownership of the returned model.
-  // |event_flags| specifies the flags of the event which triggered this menu.
-  virtual LauncherMenuModel* CreateApplicationMenu(
-      const LauncherItem& item,
-      int event_flags) = 0;
-
   // Returns the id of the item associated with the specified window, or 0 if
   // there isn't one.
   // Note: Windows of tabbed browsers will return the |LauncherID| of the
   // currently active tab or selected tab.
   virtual LauncherID GetIDByWindow(aura::Window* window) = 0;
 
-  // Whether the given launcher item is draggable.
-  virtual bool IsDraggable(const LauncherItem& item) = 0;
-
-  // Returns true if a tooltip should be shown for the item.
-  virtual bool ShouldShowTooltip(const LauncherItem& item) = 0;
-
   // Callback used to allow delegate to perform initialization actions that
   // depend on the Launcher being in a known state.
   virtual void OnLauncherCreated(Launcher* launcher) = 0;
@@ -95,6 +34,9 @@
   // Get the launcher ID from an application ID.
   virtual LauncherID GetLauncherIDForAppID(const std::string& app_id) = 0;
 
+  // Get the application ID for a given launcher ID.
+  virtual const std::string& GetAppIDForLauncherID(LauncherID id) = 0;
+
   // Pins an app with |app_id| to launcher. A running instance will get pinned.
   // In case there is no running instance a new launcher item is created and
   // pinned.
@@ -103,9 +45,8 @@
   // Check if the app with |app_id_| is pinned to the launcher.
   virtual bool IsAppPinned(const std::string& app_id) = 0;
 
-  // Unpins any app item(s) whose id is |app_id|. The new launcher will collect
-  // all items under one item, the old launcher might have multiple items.
-  virtual void UnpinAppsWithID(const std::string& app_id) = 0;
+  // Unpins app item with |app_id|.
+  virtual void UnpinAppWithID(const std::string& app_id) = 0;
 };
 
 }  // namespace ash
diff --git a/ash/launcher/launcher_item_delegate.h b/ash/launcher/launcher_item_delegate.h
new file mode 100644
index 0000000..e1d940d
--- /dev/null
+++ b/ash/launcher/launcher_item_delegate.h
@@ -0,0 +1,83 @@
+// 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 ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_H_
+#define ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_H_
+
+#include "ash/ash_export.h"
+#include "ash/launcher/launcher_types.h"
+#include "base/strings/string16.h"
+#include "ui/base/models/simple_menu_model.h"
+
+namespace aura {
+class RootWindow;
+}
+
+namespace ui {
+class Event;
+}
+
+namespace ash {
+
+// A special menu model which keeps track of an "active" menu item.
+class ASH_EXPORT LauncherMenuModel : public ui::SimpleMenuModel {
+ public:
+  explicit LauncherMenuModel(ui::SimpleMenuModel::Delegate* delegate)
+      : ui::SimpleMenuModel(delegate) {}
+
+  // Returns |true| when the given |command_id| is active and needs to be drawn
+  // in a special state.
+  virtual bool IsCommandActive(int command_id) const = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LauncherMenuModel);
+};
+
+// Delegate for the LauncherItem.
+// TODO(simon.hong81): Remove LauncherItem from abstract function parameters.
+class ASH_EXPORT LauncherItemDelegate {
+ public:
+  virtual ~LauncherItemDelegate() {}
+
+  // Invoked when the user clicks on a window entry in the launcher.
+  // |event| is the click event. The |event| is dispatched by a view
+  // and has an instance of |views::View| as the event target
+  // but not |aura::Window|. If the |event| is of type KeyEvent, it is assumed
+  // that this was triggered by keyboard action (Alt+<number>) and special
+  // handling might happen.
+  virtual void ItemSelected(const LauncherItem& item,
+                            const ui::Event& event) = 0;
+
+  // Returns the title to display for the specified launcher item.
+  virtual base::string16 GetTitle(const LauncherItem& item) = 0;
+
+  // Returns the context menumodel for the specified item on
+  // |root_window|.  Return NULL if there should be no context
+  // menu. The caller takes ownership of the returned model.
+  virtual ui::MenuModel* CreateContextMenu(const LauncherItem& item,
+                                           aura::RootWindow* root_window) = 0;
+
+  // Returns the application menu model for the specified item. There are three
+  // possible return values:
+  //  - A return of NULL indicates that no menu is wanted for this item.
+  //  - A return of a menu with one item means that only the name of the
+  //    application/item was added and there are no active applications.
+  //    Note: This is useful for hover menus which also show context help.
+  //  - A list containing the title and the active list of items.
+  // The caller takes ownership of the returned model.
+  // |event_flags| specifies the flags of the event which triggered this menu.
+  virtual LauncherMenuModel* CreateApplicationMenu(
+      const LauncherItem& item,
+      int event_flags) = 0;
+
+  // Whether the given launcher item is draggable.
+  virtual bool IsDraggable(const LauncherItem& item) = 0;
+
+  // Returns true if a tooltip should be shown for the item.
+  virtual bool ShouldShowTooltip(const LauncherItem& item) = 0;
+};
+
+}  // namespace ash
+
+#endif  // ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_H_
diff --git a/ash/launcher/launcher_item_delegate_manager.cc b/ash/launcher/launcher_item_delegate_manager.cc
new file mode 100644
index 0000000..65fb0f0
--- /dev/null
+++ b/ash/launcher/launcher_item_delegate_manager.cc
@@ -0,0 +1,29 @@
+// 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 "ash/launcher/launcher_item_delegate_manager.h"
+
+namespace ash {
+
+LauncherItemDelegateManager::LauncherItemDelegateManager() {
+}
+
+LauncherItemDelegateManager::~LauncherItemDelegateManager() {
+}
+
+void LauncherItemDelegateManager::RegisterLauncherItemDelegate(
+    ash::LauncherItemType type, LauncherItemDelegate* item_delegate) {
+  // When a new |item_delegate| is registered with an exsiting |type|, it will
+  // get overwritten.
+  item_type_to_item_delegate_map_[type] = item_delegate;
+}
+
+LauncherItemDelegate* LauncherItemDelegateManager::GetLauncherItemDelegate(
+    ash::LauncherItemType item_type) {
+  DCHECK(item_type_to_item_delegate_map_.find(item_type) !=
+      item_type_to_item_delegate_map_.end());
+  return item_type_to_item_delegate_map_[item_type];
+}
+
+}  // namespace ash
diff --git a/ash/launcher/launcher_item_delegate_manager.h b/ash/launcher/launcher_item_delegate_manager.h
new file mode 100644
index 0000000..f7c52f5
--- /dev/null
+++ b/ash/launcher/launcher_item_delegate_manager.h
@@ -0,0 +1,50 @@
+// 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 ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_MANAGER_H_
+#define ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_MANAGER_H_
+
+#include <map>
+
+#include "ash/ash_export.h"
+#include "ash/launcher/launcher_types.h"
+#include "base/compiler_specific.h"
+
+namespace ash {
+class LauncherItemDelegate;
+
+// LauncherItemDelegateManager helps Launcher/LauncherView to choose right
+// LauncherItemDelegate based on LauncherItemType.
+// When new LauncherItemDelegate is created, it must be registered by
+// RegisterLauncherItemDelegate(). If not, Launcher/LauncherView can't get
+// LauncherItem's LauncherItemDelegate.
+// TODO(simon.hong81): This class should own all LauncherItemDelegate.
+class ASH_EXPORT LauncherItemDelegateManager {
+ public:
+  LauncherItemDelegateManager();
+  virtual ~LauncherItemDelegateManager();
+
+  // Returns LauncherItemDelegate for |item_type|.
+  // This class doesn't own each LauncherItemDelegate for now.
+  LauncherItemDelegate* GetLauncherItemDelegate(
+      ash::LauncherItemType item_type);
+
+  // Register |item_delegate| for |type|.
+  // For now, This class doesn't own |item_delegate|.
+  // TODO(simon.hong81): Register LauncherItemDelegate with LauncherID.
+  void RegisterLauncherItemDelegate(
+      ash::LauncherItemType type, LauncherItemDelegate* item_delegate);
+
+ private:
+  typedef std::map<ash::LauncherItemType, LauncherItemDelegate*>
+      LauncherItemTypeToItemDelegateMap;
+
+  LauncherItemTypeToItemDelegateMap item_type_to_item_delegate_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(LauncherItemDelegateManager);
+};
+
+}  // namespace ash
+
+#endif  // ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_MANAGER_H_
diff --git a/ash/launcher/launcher_model.cc b/ash/launcher/launcher_model.cc
index 9607de6..e82e8de 100644
--- a/ash/launcher/launcher_model.cc
+++ b/ash/launcher/launcher_model.cc
@@ -22,11 +22,13 @@
       case TYPE_APP_SHORTCUT:
       case TYPE_WINDOWED_APP:
         return 1;
-      case TYPE_TABBED:
       case TYPE_PLATFORM_APP:
         return 2;
       case TYPE_APP_PANEL:
         return 3;
+      case TYPE_UNDEFINED:
+        NOTREACHED() << "LauncherItemType must be set";
+        return -1;
     }
   } else {
     switch (type) {
@@ -34,13 +36,15 @@
       case TYPE_APP_SHORTCUT:
       case TYPE_WINDOWED_APP:
         return 0;
-      case TYPE_TABBED:
       case TYPE_PLATFORM_APP:
         return 1;
       case TYPE_APP_LIST:
         return 2;
       case TYPE_APP_PANEL:
         return 3;
+      case TYPE_UNDEFINED:
+        NOTREACHED() << "LauncherItemType must be set";
+        return -1;
     }
   }
 
@@ -57,7 +61,6 @@
 LauncherModel::LauncherModel() : next_id_(1), status_(STATUS_NORMAL) {
   LauncherItem app_list;
   app_list.type = TYPE_APP_LIST;
-  app_list.is_incognito = false;
   AddAt(0, app_list);
 }
 
diff --git a/ash/launcher/launcher_model_unittest.cc b/ash/launcher/launcher_model_unittest.cc
index c91a040..7011e27 100644
--- a/ash/launcher/launcher_model_unittest.cc
+++ b/ash/launcher/launcher_model_unittest.cc
@@ -83,6 +83,7 @@
   // Add an item.
   model.AddObserver(&observer);
   LauncherItem item;
+  item.type = TYPE_APP_SHORTCUT;
   int index = model.Add(item);
   EXPECT_EQ(2, model.item_count());
   EXPECT_EQ("added=1", observer.StateStringAndClear());
@@ -93,12 +94,13 @@
     ids.insert(model.items()[i].id);
   EXPECT_EQ(model.item_count(), static_cast<int>(ids.size()));
 
-  // Change a tabbed image.
+  // Change to a platform app item.
   LauncherID original_id = model.items()[index].id;
-  model.Set(index, LauncherItem());
+  item.type = TYPE_PLATFORM_APP;
+  model.Set(index, item);
   EXPECT_EQ(original_id, model.items()[index].id);
   EXPECT_EQ("changed=1", observer.StateStringAndClear());
-  EXPECT_EQ(TYPE_TABBED, model.items()[index].type);
+  EXPECT_EQ(TYPE_PLATFORM_APP, model.items()[index].type);
 
   // Remove the item.
   model.RemoveItemAt(index);
@@ -143,16 +145,17 @@
   int browser_shortcut_index = model.Add(browser_shortcut);
   EXPECT_EQ(0, browser_shortcut_index);
 
-  // Tabbed items should be after browser shortcut.
+  // platform app items should be after browser shortcut.
   LauncherItem item;
-  int tabbed_index1 = model.Add(item);
-  EXPECT_EQ(1, tabbed_index1);
+  item.type = TYPE_PLATFORM_APP;
+  int platform_app_index1 = model.Add(item);
+  EXPECT_EQ(1, platform_app_index1);
 
-  // Add another tabbed item, it should follow first.
-  int tabbed_index2 = model.Add(item);
-  EXPECT_EQ(2, tabbed_index2);
+  // Add another platform app item, it should follow first.
+  int platform_app_index2 = model.Add(item);
+  EXPECT_EQ(2, platform_app_index2);
 
-  // APP_SHORTCUT's priority is higher than TABBED but same as
+  // APP_SHORTCUT's priority is higher than PLATFORM_APP but same as
   // BROWSER_SHORTCUT. So APP_SHORTCUT is located after BROWSER_SHORCUT.
   item.type = TYPE_APP_SHORTCUT;
   int app_shortcut_index1 = model.Add(item);
@@ -181,26 +184,27 @@
   // Before there are any panels, no icons should be right aligned.
   EXPECT_EQ(model.item_count(), model.FirstPanelIndex());
 
-  // Check that AddAt() figures out the correct indexes for tabs and panels.
-  item.type = TYPE_TABBED;
-  int tabbed_index3 = model.AddAt(2, item);
-  EXPECT_EQ(6, tabbed_index3);
+  // Check that AddAt() figures out the correct indexes for platform apps and
+  // panels.
+  item.type = TYPE_PLATFORM_APP;
+  int platform_app_index3 = model.AddAt(2, item);
+  EXPECT_EQ(6, platform_app_index3);
 
   item.type = TYPE_APP_PANEL;
   int app_panel_index1 = model.AddAt(2, item);
   EXPECT_EQ(10, app_panel_index1);
 
-  item.type = TYPE_TABBED;
-  int tabbed_index4 = model.AddAt(11, item);
-  EXPECT_EQ(9, tabbed_index4);
+  item.type = TYPE_PLATFORM_APP;
+  int platform_app_index4 = model.AddAt(11, item);
+  EXPECT_EQ(9, platform_app_index4);
 
   item.type = TYPE_APP_PANEL;
   int app_panel_index2 = model.AddAt(12, item);
   EXPECT_EQ(12, app_panel_index2);
 
-  item.type = TYPE_TABBED;
-  int tabbed_index5 = model.AddAt(7, item);
-  EXPECT_EQ(7, tabbed_index5);
+  item.type = TYPE_PLATFORM_APP;
+  int platform_app_index5 = model.AddAt(7, item);
+  EXPECT_EQ(7, platform_app_index5);
 
   item.type = TYPE_APP_PANEL;
   int app_panel_index3 = model.AddAt(13, item);
@@ -236,7 +240,7 @@
 
   // Adding another item to the list should also produce a new ID.
   LauncherItem item;
-  item.type = TYPE_TABBED;
+  item.type = TYPE_PLATFORM_APP;
   model.Add(item);
   EXPECT_NE(model.next_id(), id2);
 }
diff --git a/ash/launcher/launcher_navigator_unittest.cc b/ash/launcher/launcher_navigator_unittest.cc
index 6013390..78f1261 100644
--- a/ash/launcher/launcher_navigator_unittest.cc
+++ b/ash/launcher/launcher_navigator_unittest.cc
@@ -40,14 +40,14 @@
       LauncherItem new_item;
       new_item.type = types[i];
       new_item.status =
-          (types[i] == TYPE_TABBED) ? STATUS_RUNNING : STATUS_CLOSED;
+          (types[i] == TYPE_PLATFORM_APP) ? STATUS_RUNNING : STATUS_CLOSED;
       model_->Add(new_item);
     }
 
     // Set the focused item.
     if (focused_index >= 0) {
       LauncherItem focused_item =model_->items()[focused_index];
-      if (focused_item.type == TYPE_TABBED) {
+      if (focused_item.type == TYPE_PLATFORM_APP) {
         focused_item.status = STATUS_ACTIVE;
         model_->Set(focused_index, focused_item);
       }
@@ -65,12 +65,12 @@
 } // namespace
 
 TEST_F(LauncherNavigatorTest, BasicCycle) {
-  // An app shortcut and three windows
+  // An app shortcut and three platform apps.
   LauncherItemType types[] = {
-    TYPE_APP_SHORTCUT, TYPE_TABBED, TYPE_TABBED, TYPE_TABBED,
+    TYPE_APP_SHORTCUT, TYPE_PLATFORM_APP, TYPE_PLATFORM_APP, TYPE_PLATFORM_APP,
   };
   // LauncherModel automatically adds BROWSER_SHORTCUT item at the
-  // beginning, so '2' refers the first TYPE_TABBED item.
+  // beginning, so '2' refers the first TYPE_PLATFORM_APP item.
   SetupMockLauncherModel(types, arraysize(types), 2);
 
   EXPECT_EQ(3, GetNextActivatedItemIndex(model(), CYCLE_FORWARD));
@@ -82,7 +82,7 @@
 
 TEST_F(LauncherNavigatorTest, WrapToBeginning) {
   LauncherItemType types[] = {
-    TYPE_APP_SHORTCUT, TYPE_TABBED, TYPE_TABBED, TYPE_TABBED,
+    TYPE_APP_SHORTCUT, TYPE_PLATFORM_APP, TYPE_PLATFORM_APP, TYPE_PLATFORM_APP,
   };
   SetupMockLauncherModel(types, arraysize(types), 4);
 
@@ -99,7 +99,7 @@
 }
 
 TEST_F(LauncherNavigatorTest, SingleEntry) {
-  LauncherItemType type = TYPE_TABBED;
+  LauncherItemType type = TYPE_PLATFORM_APP;
   SetupMockLauncherModel(&type, 1, 1);
 
   // If there's only one item there and it is already active, there's no item
@@ -110,7 +110,7 @@
 
 TEST_F(LauncherNavigatorTest, NoActive) {
   LauncherItemType types[] = {
-    TYPE_TABBED, TYPE_TABBED,
+    TYPE_PLATFORM_APP, TYPE_PLATFORM_APP,
   };
   // Special case: no items are 'STATUS_ACTIVE'.
   SetupMockLauncherModel(types, arraysize(types), -1);
diff --git a/ash/launcher/launcher_types.cc b/ash/launcher/launcher_types.cc
index a6080b0..46fc827 100644
--- a/ash/launcher/launcher_types.cc
+++ b/ash/launcher/launcher_types.cc
@@ -10,8 +10,7 @@
 const int kLauncherBackgroundAlpha = 204;
 
 LauncherItem::LauncherItem()
-    : type(TYPE_TABBED),
-      is_incognito(false),
+    : type(TYPE_UNDEFINED),
       id(0),
       status(STATUS_CLOSED) {
 }
diff --git a/ash/launcher/launcher_types.h b/ash/launcher/launcher_types.h
index f4eed67..c600629 100644
--- a/ash/launcher/launcher_types.h
+++ b/ash/launcher/launcher_types.h
@@ -23,9 +23,6 @@
 
 // Type the LauncherItem represents.
 enum LauncherItemType {
-  // Represents a tabbed browser.
-  TYPE_TABBED,
-
   // Represents a running app panel.
   TYPE_APP_PANEL,
 
@@ -43,6 +40,9 @@
 
   // Represents a windowed V1 browser app.
   TYPE_WINDOWED_APP,
+
+  // Default value.
+  TYPE_UNDEFINED,
 };
 
 // Represents the status of pinned or running app launcher items.
@@ -63,12 +63,7 @@
 
   LauncherItemType type;
 
-  // Whether it is drawn as an incognito icon or not. Only used if this is
-  // TYPE_TABBED. Note: This cannot be used for identifying incognito windows.
-  bool is_incognito;
-
-  // Image to display in the launcher. If this item is TYPE_TABBED the image is
-  // a favicon image.
+  // Image to display in the launcher.
   gfx::ImageSkia image;
 
   // Assigned by the model when the item is added.
diff --git a/ash/launcher/launcher_unittest.cc b/ash/launcher/launcher_unittest.cc
index b1798e3..688a29a 100644
--- a/ash/launcher/launcher_unittest.cc
+++ b/ash/launcher/launcher_unittest.cc
@@ -29,9 +29,8 @@
 
 namespace ash {
 
-// Confirm that launching a browser gets the appropriate state reflected in
-// its button.
-TEST_F(LauncherTest, OpenBrowser) {
+// Confirms that LauncherItem reflects the appropriated state.
+TEST_F(LauncherTest, StatusReflection) {
   Launcher* launcher = Launcher::ForPrimaryDisplay();
   ASSERT_TRUE(launcher);
   LauncherView* launcher_view = launcher->GetLauncherViewForTest();
@@ -41,9 +40,9 @@
   // Initially we have the app list and chrome icon.
   int button_count = test.GetButtonCount();
 
-  // Add running tab.
+  // Add running platform app.
   LauncherItem item;
-  item.type = TYPE_TABBED;
+  item.type = TYPE_PLATFORM_APP;
   item.status = STATUS_RUNNING;
   int index = model->Add(item);
   ASSERT_EQ(++button_count, test.GetButtonCount());
@@ -67,9 +66,9 @@
   // Initially we have the app list and chrome icon.
   int button_count = test.GetButtonCount();
 
-  // Add running tab.
+  // Add running platform app.
   LauncherItem item;
-  item.type = TYPE_TABBED;
+  item.type = TYPE_PLATFORM_APP;
   item.status = STATUS_RUNNING;
   int index = model->Add(item);
   ASSERT_EQ(++button_count, test.GetButtonCount());
@@ -92,11 +91,11 @@
   LauncherModel* model = launcher_view->model();
   LauncherID first_item_id = model->next_id();
 
-  // Add tabbed browser until overflow.
+  // Add platform app button until overflow.
   int items_added = 0;
   while (!test.IsOverflowButtonVisible()) {
     LauncherItem item;
-    item.type = TYPE_TABBED;
+    item.type = TYPE_PLATFORM_APP;
     item.status = STATUS_RUNNING;
     model->Add(item);
 
diff --git a/ash/launcher/launcher_view.cc b/ash/launcher/launcher_view.cc
index b6095a8..893edcc 100644
--- a/ash/launcher/launcher_view.cc
+++ b/ash/launcher/launcher_view.cc
@@ -9,18 +9,19 @@
 #include "ash/ash_constants.h"
 #include "ash/ash_switches.h"
 #include "ash/drag_drop/drag_image_view.h"
-#include "ash/launcher/alternate_app_list_button.h"
-#include "ash/launcher/app_list_button.h"
 #include "ash/launcher/launcher_button.h"
 #include "ash/launcher/launcher_delegate.h"
 #include "ash/launcher/launcher_icon_observer.h"
+#include "ash/launcher/launcher_item_delegate.h"
+#include "ash/launcher/launcher_item_delegate_manager.h"
 #include "ash/launcher/launcher_model.h"
 #include "ash/launcher/launcher_tooltip_manager.h"
-#include "ash/launcher/overflow_bubble.h"
-#include "ash/launcher/overflow_button.h"
-#include "ash/launcher/tabbed_launcher_button.h"
 #include "ash/root_window_controller.h"
 #include "ash/scoped_target_root_window.h"
+#include "ash/shelf/alternate_app_list_button.h"
+#include "ash/shelf/app_list_button.h"
+#include "ash/shelf/overflow_bubble.h"
+#include "ash/shelf/overflow_button.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell_delegate.h"
@@ -98,6 +99,15 @@
 // The maximum allowable length of a menu line of an application menu in pixels.
 const int kMaximumAppMenuItemLength = 350;
 
+// The distance of the cursor from the outer rim of the shelf before it
+// separates / re-inserts. Note that the rip off distance is bigger then the
+// re-insertion distance to avoid "flickering" between the two states.
+const int kRipOffDistance = 48;
+const int kReinsertDistance = 32;
+
+// The rip off drag and drop proxy image should get scaled by this factor.
+const float kDragAndDropProxyScale = 1.5f;
+
 namespace {
 
 // The MenuModelAdapter gets slightly changed to adapt the menu appearance to
@@ -401,7 +411,10 @@
       closing_event_time_(base::TimeDelta()),
       got_deleted_(NULL),
       drag_and_drop_item_pinned_(false),
-      drag_and_drop_launcher_id_(0) {
+      drag_and_drop_launcher_id_(0),
+      dragged_off_shelf_(false),
+      snap_back_from_rip_off_view_(NULL),
+      item_manager_(Shell::GetInstance()->launcher_item_delegate_manager()) {
   DCHECK(model_);
   bounds_animator_.reset(new views::BoundsAnimator(this));
   bounds_animator_->AddObserver(this);
@@ -670,7 +683,7 @@
 
   // Either destroy the temporarily created item - or - make the item visible.
   if (drag_and_drop_item_pinned_ && cancel)
-    delegate_->UnpinAppsWithID(drag_and_drop_app_id_);
+    delegate_->UnpinAppWithID(drag_and_drop_app_id_);
   else if (drag_and_drop_view)
     drag_and_drop_view->SetSize(pre_drag_and_drop_size_);
 
@@ -896,21 +909,6 @@
 views::View* LauncherView::CreateViewForItem(const LauncherItem& item) {
   views::View* view = NULL;
   switch (item.type) {
-    case TYPE_TABBED: {
-      TabbedLauncherButton* button =
-          TabbedLauncherButton::Create(
-              this,
-              this,
-              tooltip_->shelf_layout_manager(),
-              item.is_incognito ?
-                  TabbedLauncherButton::STATE_INCOGNITO :
-                  TabbedLauncherButton::STATE_NOT_INCOGNITO);
-      button->SetTabImage(item.image);
-      ReflectItemStatus(item, button);
-      view = button;
-      break;
-    }
-
     case TYPE_BROWSER_SHORTCUT:
     case TYPE_APP_SHORTCUT:
     case TYPE_WINDOWED_APP:
@@ -974,9 +972,15 @@
   drag_pointer_ = pointer;
   start_drag_index_ = view_model_->GetIndexOfView(drag_view_);
 
+  if (start_drag_index_== -1) {
+    CancelDrag(-1);
+    return;
+  }
+
   // If the item is no longer draggable, bail out.
-  if (start_drag_index_ == -1 ||
-      !delegate_->IsDraggable(model_->items()[start_drag_index_])) {
+  LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate(
+      model_->items()[start_drag_index_].type);
+  if (!item_delegate->IsDraggable(model_->items()[start_drag_index_])) {
     CancelDrag(-1);
     return;
   }
@@ -987,20 +991,31 @@
 }
 
 void LauncherView::ContinueDrag(const ui::LocatedEvent& event) {
-  ShelfLayoutManager* shelf = tooltip_->shelf_layout_manager();
+  // Due to a syncing operation the application might have been removed.
+  // Bail if it is gone.
+  int current_index = view_model_->GetIndexOfView(drag_view_);
+  DCHECK_NE(-1, current_index);
+
+  LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate(
+      model_->items()[current_index].type);
+  if (!item_delegate->IsDraggable(model_->items()[current_index])) {
+    CancelDrag(-1);
+    return;
+  }
+
+  // If this is not a drag and drop host operation and not the app list item,
+  // check if the item got ripped off the shelf - if it did we are done.
+  if (!drag_and_drop_launcher_id_ && ash::switches::UseDragOffShelf() &&
+      RemovableByRipOff(current_index) != NOT_REMOVABLE) {
+    if (HandleRipOffDrag(event))
+      return;
+    // The rip off handler could have changed the location of the item.
+    current_index = view_model_->GetIndexOfView(drag_view_);
+  }
 
   // TODO: I don't think this works correctly with RTL.
   gfx::Point drag_point(event.location());
   views::View::ConvertPointToTarget(drag_view_, this, &drag_point);
-  int current_index = view_model_->GetIndexOfView(drag_view_);
-  DCHECK_NE(-1, current_index);
-
-  // If the item is no longer draggable, bail out.
-  if (current_index == -1 ||
-      !delegate_->IsDraggable(model_->items()[current_index])) {
-    CancelDrag(-1);
-    return;
-  }
 
   // Constrain the location to the range of valid indices for the type.
   std::pair<int, int> indices(GetDragRange(current_index));
@@ -1012,6 +1027,7 @@
       last_drag_index > last_visible_index_)
     last_drag_index = last_visible_index_;
   int x = 0, y = 0;
+  ShelfLayoutManager* shelf = tooltip_->shelf_layout_manager();
   if (shelf->IsHorizontalAlignment()) {
     x = std::max(view_model_->ideal_bounds(indices.first).x(),
                      drag_point.x() - drag_offset_);
@@ -1050,19 +1066,138 @@
   bounds_animator_->StopAnimatingView(drag_view_);
 }
 
+bool LauncherView::HandleRipOffDrag(const ui::LocatedEvent& event) {
+  // Determine the distance to the shelf.
+  int delta = CalculateShelfDistance(event.root_location());
+  int current_index = view_model_->GetIndexOfView(drag_view_);
+  DCHECK_NE(-1, current_index);
+  // To avoid ugly forwards and backwards flipping we use different constants
+  // for ripping off / re-inserting the items.
+  if (dragged_off_shelf_) {
+    // If the re-insertion distance is undercut we insert the item back into
+    // the shelf. Note that the reinsertion value is slightly smaller then the
+    // rip off distance to avoid flickering.
+    if (delta < kReinsertDistance) {
+      // Destroy our proxy view item.
+      DestroyDragIconProxy();
+      // Re-insert the item and return simply false since the caller will handle
+      // the move as in any normal case.
+      dragged_off_shelf_ = false;
+      drag_view_->layer()->SetOpacity(1.0f);
+      return false;
+    }
+    // Move our proxy view item.
+    UpdateDragIconProxy(event.root_location());
+    return true;
+  }
+  // Check if we are too far away from the shelf to enter the ripped off state.
+  if (delta > kRipOffDistance) {
+    // Create a proxy view item which can be moved anywhere.
+    LauncherButton* button = static_cast<LauncherButton*>(drag_view_);
+    CreateDragIconProxy(event.root_location(),
+                        button->GetImage(),
+                        drag_view_,
+                        gfx::Vector2d(0, 0),
+                        kDragAndDropProxyScale);
+    drag_view_->layer()->SetOpacity(0.0f);
+    if (RemovableByRipOff(current_index) == REMOVABLE) {
+      // Move the item to the end of the launcher and hide it.
+      model_->Move(current_index, model_->item_count() - 1);
+      AnimateToIdealBounds();
+      // Make the item partially disappear to show that it will get removed if
+      // dropped.
+      drag_image_->SetOpacity(0.5f);
+    }
+    dragged_off_shelf_ = true;
+    return true;
+  }
+  return false;
+}
+
+void LauncherView::FinalizeRipOffDrag(bool cancel) {
+  if (!dragged_off_shelf_)
+    return;
+  // Make sure we do not come in here again.
+  dragged_off_shelf_ = false;
+
+  // Coming here we should always have a |drag_view_|.
+  DCHECK(drag_view_);
+  int current_index = view_model_->GetIndexOfView(drag_view_);
+  // If the view isn't part of the model anymore (|current_index| == -1), a sync
+  // operation must have removed it. In that case we shouldn't change the model
+  // and only delete the proxy image.
+  if (current_index == -1) {
+    DestroyDragIconProxy();
+    return;
+  }
+
+  // Set to true when the animation should snap back to where it was before.
+  bool snap_back = false;
+  // Items which cannot be dragged off will be handled as a cancel.
+  if (!cancel) {
+    // Make sure we do not try to remove un-removable items like items which
+    // were not pinned or have to be always there.
+    if (RemovableByRipOff(current_index) != REMOVABLE) {
+      cancel = true;
+      snap_back = true;
+    } else {
+      // Make sure the item stays invisible upon removal.
+      drag_view_->SetVisible(false);
+      std::string app_id =
+          delegate_->GetAppIDForLauncherID(model_->items()[current_index].id);
+      delegate_->UnpinAppWithID(app_id);
+    }
+  }
+  if (cancel || snap_back) {
+    if (!cancelling_drag_model_changed_) {
+      // Only do something if the change did not come through a model change.
+      gfx::Rect drag_bounds = drag_image_->GetBoundsInScreen();
+      gfx::Point relative_to = GetBoundsInScreen().origin();
+      gfx::Rect target(
+          gfx::PointAtOffsetFromOrigin(drag_bounds.origin()- relative_to),
+          drag_bounds.size());
+      drag_view_->SetBoundsRect(target);
+      // Hide the status from the active item since we snap it back now. Upon
+      // animation end the flag gets cleared if |snap_back_from_rip_off_view_|
+      // is set.
+      snap_back_from_rip_off_view_ = drag_view_;
+      LauncherButton* button = static_cast<LauncherButton*>(drag_view_);
+      button->AddState(LauncherButton::STATE_HIDDEN);
+      // When a canceling drag model is happening, the view model is diverged
+      // from the menu model and movements / animations should not be done.
+      model_->Move(current_index, start_drag_index_);
+      AnimateToIdealBounds();
+    }
+    drag_view_->layer()->SetOpacity(1.0f);
+  }
+  DestroyDragIconProxy();
+}
+
+LauncherView::RemovableState LauncherView::RemovableByRipOff(int index) {
+  LauncherItemType type = model_->items()[index].type;
+  if (type == TYPE_APP_LIST)
+    return NOT_REMOVABLE;
+  std::string app_id =
+      delegate_->GetAppIDForLauncherID(model_->items()[index].id);
+  // Note: Only pinned app shortcuts can be removed!
+  return (type == TYPE_APP_SHORTCUT && delegate_->IsAppPinned(app_id)) ?
+      REMOVABLE : DRAGGABLE;
+}
+
 bool LauncherView::SameDragType(LauncherItemType typea,
                                 LauncherItemType typeb) const {
   switch (typea) {
-    case TYPE_TABBED:
-    case TYPE_PLATFORM_APP:
-      return (typeb == TYPE_TABBED || typeb == TYPE_PLATFORM_APP);
     case TYPE_APP_SHORTCUT:
     case TYPE_BROWSER_SHORTCUT:
       return (typeb == TYPE_APP_SHORTCUT || typeb == TYPE_BROWSER_SHORTCUT);
+    case TYPE_PLATFORM_APP:
     case TYPE_WINDOWED_APP:
     case TYPE_APP_LIST:
     case TYPE_APP_PANEL:
       return typeb == typea;
+    case TYPE_UNDEFINED:
+      NOTREACHED() << "LauncherItemType must be set.";
+      return false;
   }
   NOTREACHED();
   return false;
@@ -1168,6 +1303,7 @@
 }
 
 int LauncherView::CancelDrag(int modified_index) {
+  FinalizeRipOffDrag(true);
   if (!drag_view_)
     return modified_index;
   bool was_dragging = dragging();
@@ -1332,17 +1468,6 @@
 
   views::View* view = view_model_->view_at(model_index);
   switch (item.type) {
-    case TYPE_TABBED: {
-      TabbedLauncherButton* button = static_cast<TabbedLauncherButton*>(view);
-      gfx::Size pref = button->GetPreferredSize();
-      button->SetTabImage(item.image);
-      if (pref != button->GetPreferredSize())
-        AnimateToIdealBounds();
-      else
-        button->SchedulePaint();
-      ReflectItemStatus(item, button);
-      break;
-    }
     case TYPE_BROWSER_SHORTCUT:
       // Fallthrough for the new Launcher since it needs to show the activation
       // change as well.
@@ -1394,9 +1519,13 @@
 
   tooltip_->Close();
   int index = view_model_->GetIndexOfView(view);
-  if (index == -1 ||
-      view_model_->view_size() <= 1 ||
-      !delegate_->IsDraggable(model_->items()[index]))
+  if (index == -1)
+    return;
+
+  LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate(
+      model_->items()[index].type);
+  if (view_model_->view_size() <= 1 ||
+      !item_delegate->IsDraggable(model_->items()[index]))
     return;  // View is being deleted or not draggable, ignore request.
 
   ShelfLayoutManager* shelf = tooltip_->shelf_layout_manager();
@@ -1425,6 +1554,7 @@
   if (canceled) {
     CancelDrag(-1);
   } else if (drag_pointer_ == pointer) {
+    FinalizeRipOffDrag(false);
     drag_pointer_ = NONE;
     AnimateToIdealBounds();
   }
@@ -1464,21 +1594,9 @@
   if (view_index == -1)
     return base::string16();
 
-  switch (model_->items()[view_index].type) {
-    case TYPE_TABBED:
-    case TYPE_APP_PANEL:
-    case TYPE_APP_SHORTCUT:
-    case TYPE_WINDOWED_APP:
-    case TYPE_PLATFORM_APP:
-    case TYPE_BROWSER_SHORTCUT:
-      return delegate_->GetTitle(model_->items()[view_index]);
-
-    case TYPE_APP_LIST:
-      return model_->status() == LauncherModel::STATUS_LOADING ?
-          l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_SYNCING_TITLE) :
-          l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_TITLE);
-  }
-  return base::string16();
+  LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate(
+      model_->items()[view_index].type);
+  return item_delegate->GetTitle(model_->items()[view_index]);
 }
 
 void LauncherView::ButtonPressed(views::Button* sender,
@@ -1522,23 +1640,27 @@
       case TYPE_BROWSER_SHORTCUT:
         Shell::GetInstance()->delegate()->RecordUserMetricsAction(
             UMA_LAUNCHER_CLICK_ON_APP);
-        // Fallthrough
-      case TYPE_TABBED:
-      case TYPE_APP_PANEL:
-        delegate_->ItemSelected(model_->items()[view_index], event);
-        // Don't show the menu when the user creates a new browser using ctrl
-        // click.
-        if (model_->items()[view_index].type != TYPE_BROWSER_SHORTCUT ||
-            !(event.flags() & ui::EF_CONTROL_DOWN))
-          ShowListMenuForView(model_->items()[view_index], sender, event);
         break;
 
       case TYPE_APP_LIST:
         Shell::GetInstance()->delegate()->RecordUserMetricsAction(
             UMA_LAUNCHER_CLICK_ON_APPLIST_BUTTON);
-        Shell::GetInstance()->ToggleAppList(GetWidget()->GetNativeView());
+        break;
+
+      case TYPE_APP_PANEL:
+        break;
+
+      case TYPE_UNDEFINED:
+        NOTREACHED() << "LauncherItemType must be set.";
         break;
     }
+
+    LauncherItemDelegate* item_delegate =
+        item_manager_->GetLauncherItemDelegate(
+            model_->items()[view_index].type);
+    item_delegate->ItemSelected(model_->items()[view_index], event);
+
+    ShowListMenuForView(model_->items()[view_index], sender, event);
   }
 }
 
@@ -1546,7 +1668,9 @@
                                        views::View* source,
                                        const ui::Event& event) {
   scoped_ptr<ash::LauncherMenuModel> menu_model;
-  menu_model.reset(delegate_->CreateApplicationMenu(item, event.flags()));
+  LauncherItemDelegate* item_delegate =
+      item_manager_->GetLauncherItemDelegate(item.type);
+  menu_model.reset(item_delegate->CreateApplicationMenu(item, event.flags()));
 
   // Make sure we have a menu and it has at least two items in addition to the
   // application title and the 3 spacing separators.
@@ -1566,6 +1690,8 @@
                                           const gfx::Point& point,
                                           ui:: MenuSourceType source_type) {
   int view_index = view_model_->GetIndexOfView(source);
+  // TODO(simon.hong81): Create LauncherContextMenu for applist in its
+  // LauncherItemDelegate.
   if (view_index != -1 &&
       model_->items()[view_index].type == TYPE_APP_LIST) {
     view_index = -1;
@@ -1577,11 +1703,15 @@
     Shell::GetInstance()->ShowContextMenu(point, source_type);
     return;
   }
-  scoped_ptr<ui::MenuModel> menu_model(delegate_->CreateContextMenu(
+  scoped_ptr<ui::MenuModel> menu_model;
+  LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate(
+      model_->items()[view_index].type);
+  menu_model.reset(item_delegate->CreateContextMenu(
       model_->items()[view_index],
       source->GetWidget()->GetNativeView()->GetRootWindow()));
   if (!menu_model)
     return;
+
   base::AutoReset<LauncherID> reseter(
       &context_menu_id_,
       view_index == -1 ? 0 : model_->items()[view_index].id);
@@ -1686,6 +1816,23 @@
 }
 
 void LauncherView::OnBoundsAnimatorDone(views::BoundsAnimator* animator) {
+  if (snap_back_from_rip_off_view_ && animator == bounds_animator_) {
+    if (!animator->IsAnimating(snap_back_from_rip_off_view_)) {
+      // Coming here the animation of the LauncherButton is finished and the
+      // previously hidden status can be shown again. Since the button itself
+      // might have gone away or changed locations we check that the button
+      // is still in the shelf and show its status again.
+      for (int index = 0; index < view_model_->view_size(); index++) {
+        views::View* view = view_model_->view_at(index);
+        if (view == snap_back_from_rip_off_view_) {
+          LauncherButton* button = static_cast<LauncherButton*>(view);
+          button->ClearState(LauncherButton::STATE_HIDDEN);
+          break;
+        }
+      }
+      snap_back_from_rip_off_view_ = NULL;
+    }
+  }
 }
 
 bool LauncherView::IsUsableEvent(const ui::Event& event) {
@@ -1713,7 +1860,34 @@
       Shell::GetInstance()->GetAppListWindow())
     return false;
   const LauncherItem* item = LauncherItemForView(view);
-  return (!item || delegate_->ShouldShowTooltip(*item));
+  if (!item)
+    return true;
+  LauncherItemDelegate* item_delegate =
+      item_manager_->GetLauncherItemDelegate(item->type);
+  return item_delegate->ShouldShowTooltip(*item);
+}
+
+int LauncherView::CalculateShelfDistance(const gfx::Point& coordinate) const {
+  ShelfWidget* shelf = RootWindowController::ForLauncher(
+      GetWidget()->GetNativeView())->shelf();
+  ash::ShelfAlignment align = shelf->GetAlignment();
+  const gfx::Rect bounds = GetBoundsInScreen();
+  int distance = 0;
+  switch (align) {
+    case ash::SHELF_ALIGNMENT_BOTTOM:
+      distance = bounds.y() - coordinate.y();
+      break;
+    case ash::SHELF_ALIGNMENT_LEFT:
+      distance = coordinate.x() - bounds.right();
+      break;
+    case ash::SHELF_ALIGNMENT_RIGHT:
+      distance = bounds.x() - coordinate.x();
+      break;
+    case ash::SHELF_ALIGNMENT_TOP:
+      distance = coordinate.y() - bounds.bottom();
+      break;
+  }
+  return distance > 0 ? distance : 0;
 }
 
 }  // namespace internal
diff --git a/ash/launcher/launcher_view.h b/ash/launcher/launcher_view.h
index 507c768..dcaf265 100644
--- a/ash/launcher/launcher_view.h
+++ b/ash/launcher/launcher_view.h
@@ -35,6 +35,7 @@
 class LauncherDelegate;
 struct LauncherItem;
 class LauncherIconObserver;
+class LauncherItemDelegateManager;
 class LauncherModel;
 
 namespace internal {
@@ -132,6 +133,12 @@
     gfx::Rect overflow_bounds;
   };
 
+  enum RemovableState {
+    REMOVABLE,     // Item can be removed when dragged away.
+    DRAGGABLE,     // Item can be removed, but will snap always back to origin.
+    NOT_REMOVABLE, // Item is fixed and can never be removed.
+  };
+
   bool is_overflow_mode() const {
     return first_visible_index_ > 0;
   }
@@ -171,6 +178,16 @@
   // Invoked when the mouse is dragged. Updates the models as appropriate.
   void ContinueDrag(const ui::LocatedEvent& event);
 
+  // Handles ripping off an item from the shelf. Returns true when the item got
+  // removed.
+  bool HandleRipOffDrag(const ui::LocatedEvent& event);
+
+  // Finalize the rip off dragging by either |cancel| the action or validating.
+  void FinalizeRipOffDrag(bool cancel);
+
+  // Check if an item can be ripped off or not.
+  RemovableState RemovableByRipOff(int index);
+
   // Returns true if |typea| and |typeb| should be in the same drag range.
   bool SameDragType(LauncherItemType typea, LauncherItemType typeb) const;
 
@@ -276,6 +293,10 @@
   // Returns true if a tooltip should be shown for |view|.
   bool ShouldShowTooltipForView(const views::View* view) const;
 
+  // Get the distance from the given |coordinate| to the closest point on this
+  // launcher/shelf.
+  int CalculateShelfDistance(const gfx::Point& coordinate) const;
+
   // The model; owned by Launcher.
   LauncherModel* model_;
 
@@ -372,6 +393,15 @@
   // The view which gets replaced by our drag icon proxy.
   views::View* drag_replaced_view_;
 
+  // True when the icon was dragged off the shelf.
+  bool dragged_off_shelf_;
+
+  // The rip off view when a snap back operation is underway.
+  views::View* snap_back_from_rip_off_view_;
+
+  // Holds LauncherItemDelegateManager.
+  LauncherItemDelegateManager* item_manager_;
+
   DISALLOW_COPY_AND_ASSIGN(LauncherView);
 };
 
diff --git a/ash/launcher/launcher_view_unittest.cc b/ash/launcher/launcher_view_unittest.cc
index 7731655..6c43404 100644
--- a/ash/launcher/launcher_view_unittest.cc
+++ b/ash/launcher/launcher_view_unittest.cc
@@ -224,7 +224,6 @@
   LauncherID AddBrowserShortcut() {
     LauncherItem browser_shortcut;
     browser_shortcut.type = TYPE_BROWSER_SHORTCUT;
-    browser_shortcut.is_incognito = false;
 
     LauncherID id = model_->next_id();
     model_->AddAt(0, browser_shortcut);
@@ -243,22 +242,6 @@
     return id;
   }
 
-  LauncherID AddTabbedBrowserNoWait() {
-    LauncherItem item;
-    item.type = TYPE_TABBED;
-    item.status = STATUS_RUNNING;
-
-    LauncherID id = model_->next_id();
-    model_->Add(item);
-    return id;
-  }
-
-  LauncherID AddTabbedBrowser() {
-    LauncherID id = AddTabbedBrowserNoWait();
-    test_api_->RunMessageLoopUntilAnimationsDone();
-    return id;
-  }
-
   LauncherID AddPanel() {
     LauncherID id = AddPanelNoWait();
     test_api_->RunMessageLoopUntilAnimationsDone();
@@ -439,7 +422,7 @@
 // Checks that the ideal item icon bounds match the view's bounds in the screen
 // in both LTR and RTL.
 TEST_P(LauncherViewTextDirectionTest, IdealBoundsOfItemIcon) {
-  LauncherID id = AddTabbedBrowser();
+  LauncherID id = AddPlatformApp();
   internal::LauncherButton* button = GetButtonByID(id);
   gfx::Rect item_bounds = button->GetBoundsInScreen();
   gfx::Point icon_offset = button->GetIconBounds().origin();
@@ -454,14 +437,6 @@
 
 // Checks that launcher view contents are considered in the correct drag group.
 TEST_F(LauncherViewTest, EnforceDragType) {
-  EXPECT_TRUE(test_api_->SameDragType(TYPE_TABBED, TYPE_TABBED));
-  EXPECT_TRUE(test_api_->SameDragType(TYPE_TABBED, TYPE_PLATFORM_APP));
-  EXPECT_FALSE(test_api_->SameDragType(TYPE_TABBED, TYPE_APP_SHORTCUT));
-  EXPECT_FALSE(test_api_->SameDragType(TYPE_TABBED, TYPE_BROWSER_SHORTCUT));
-  EXPECT_FALSE(test_api_->SameDragType(TYPE_TABBED, TYPE_WINDOWED_APP));
-  EXPECT_FALSE(test_api_->SameDragType(TYPE_TABBED, TYPE_APP_LIST));
-  EXPECT_FALSE(test_api_->SameDragType(TYPE_TABBED, TYPE_APP_PANEL));
-
   EXPECT_TRUE(test_api_->SameDragType(TYPE_PLATFORM_APP, TYPE_PLATFORM_APP));
   EXPECT_FALSE(test_api_->SameDragType(TYPE_PLATFORM_APP, TYPE_APP_SHORTCUT));
   EXPECT_FALSE(test_api_->SameDragType(TYPE_PLATFORM_APP,
@@ -495,21 +470,21 @@
   EXPECT_TRUE(test_api_->SameDragType(TYPE_APP_PANEL, TYPE_APP_PANEL));
 }
 
-// Adds browser button until overflow and verifies that the last added browser
-// button is hidden.
+// Adds platform app button until overflow and verifies that the last added
+// platform app button is hidden.
 TEST_F(LauncherViewTest, AddBrowserUntilOverflow) {
   // All buttons should be visible.
   ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
             test_api_->GetButtonCount());
 
-  // Add tabbed browser until overflow.
+  // Add platform app button until overflow.
   int items_added = 0;
-  LauncherID last_added = AddTabbedBrowser();
+  LauncherID last_added = AddPlatformApp();
   while (!test_api_->IsOverflowButtonVisible()) {
     // Added button is visible after animation while in this loop.
     EXPECT_TRUE(GetButtonByID(last_added)->visible());
 
-    last_added = AddTabbedBrowser();
+    last_added = AddPlatformApp();
     ++items_added;
     ASSERT_LT(items_added, 10000);
   }
@@ -518,15 +493,15 @@
   EXPECT_FALSE(GetButtonByID(last_added)->visible());
 }
 
-// Adds one browser button then adds app shortcut until overflow. Verifies that
-// the browser button gets hidden on overflow and last added app shortcut is
-// still visible.
+// Adds one platform app button then adds app shortcut until overflow. Verifies
+// that the browser button gets hidden on overflow and last added app shortcut
+// is still visible.
 TEST_F(LauncherViewTest, AddAppShortcutWithBrowserButtonUntilOverflow) {
   // All buttons should be visible.
   ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
             test_api_->GetButtonCount());
 
-  LauncherID browser_button_id = AddTabbedBrowser();
+  LauncherID browser_button_id = AddPlatformApp();
 
   // Add app shortcut until overflow.
   int items_added = 0;
@@ -542,21 +517,22 @@
 
   // The last added app short button should be visible.
   EXPECT_TRUE(GetButtonByID(last_added)->visible());
-  // And the browser button is invisible.
+  // And the platform app button is invisible.
   EXPECT_FALSE(GetButtonByID(browser_button_id)->visible());
 }
 
-TEST_F(LauncherViewTest, AddPanelHidesTabbedBrowser) {
+TEST_F(LauncherViewTest, AddPanelHidesPlatformAppButton) {
   ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
             test_api_->GetButtonCount());
 
-  // Add tabbed browser until overflow, remember last visible tabbed browser.
+  // Add platform app button until overflow, remember last visible platform app
+  // button.
   int items_added = 0;
-  LauncherID first_added = AddTabbedBrowser();
+  LauncherID first_added = AddPlatformApp();
   EXPECT_TRUE(GetButtonByID(first_added)->visible());
   LauncherID last_visible = first_added;
   while (true) {
-    LauncherID added = AddTabbedBrowser();
+    LauncherID added = AddPlatformApp();
     if (test_api_->IsOverflowButtonVisible()) {
       EXPECT_FALSE(GetButtonByID(added)->visible());
       break;
@@ -574,17 +550,17 @@
   EXPECT_TRUE(GetButtonByID(last_visible)->visible());
 }
 
-// When there are more panels then browsers we should hide panels rather
-// than browsers.
-TEST_F(LauncherViewTest, BrowserHidesExcessPanels) {
+// When there are more panels then platform app buttons we should hide panels
+// rather than platform apps.
+TEST_F(LauncherViewTest, PlatformAppHidesExcessPanels) {
   ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
             test_api_->GetButtonCount());
 
-  // Add tabbed browser.
-  LauncherID browser = AddTabbedBrowser();
+  // Add platform app button.
+  LauncherID platform_app = AddPlatformApp();
   LauncherID first_panel = AddPanel();
 
-  EXPECT_TRUE(GetButtonByID(browser)->visible());
+  EXPECT_TRUE(GetButtonByID(platform_app)->visible());
   EXPECT_TRUE(GetButtonByID(first_panel)->visible());
 
   // Add panels until there is an overflow.
@@ -596,22 +572,23 @@
     ASSERT_LT(items_added, 10000);
   }
 
-  // The first panel should now be hidden by the new browsers needing space.
+  // The first panel should now be hidden by the new platform apps needing
+  // space.
   EXPECT_FALSE(GetButtonByID(first_panel)->visible());
   EXPECT_TRUE(GetButtonByID(last_panel)->visible());
-  EXPECT_TRUE(GetButtonByID(browser)->visible());
+  EXPECT_TRUE(GetButtonByID(platform_app)->visible());
 
-  // Adding browsers should eventually begin to hide browsers. We will add
-  // browsers until either the last panel or browser is hidden.
+  // Adding platform apps should eventually begin to hide platform apps. We will
+  // add platform apps until either the last panel or platform app is hidden.
   items_added = 0;
-  while (GetButtonByID(browser)->visible() &&
+  while (GetButtonByID(platform_app)->visible() &&
          GetButtonByID(last_panel)->visible()) {
-    browser = AddTabbedBrowser();
+    platform_app = AddPlatformApp();
     ++items_added;
     ASSERT_LT(items_added, 10000);
   }
   EXPECT_TRUE(GetButtonByID(last_panel)->visible());
-  EXPECT_FALSE(GetButtonByID(browser)->visible());
+  EXPECT_FALSE(GetButtonByID(platform_app)->visible());
 }
 
 // Adds button until overflow then removes first added one. Verifies that
@@ -622,12 +599,12 @@
   ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
             test_api_->GetButtonCount());
 
-  // Add tabbed browser until overflow.
+  // Add platform app buttons until overflow.
   int items_added = 0;
-  LauncherID first_added = AddTabbedBrowser();
+  LauncherID first_added = AddPlatformApp();
   LauncherID last_added = first_added;
   while (!test_api_->IsOverflowButtonVisible()) {
-    last_added = AddTabbedBrowser();
+    last_added = AddPlatformApp();
     ++items_added;
     ASSERT_LT(items_added, 10000);
   }
@@ -652,11 +629,11 @@
   ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
             test_api_->GetButtonCount());
 
-  // Add tabbed browser until overflow.
+  // Add platform app button until overflow.
   int items_added = 0;
-  LauncherID last_added = AddTabbedBrowser();
+  LauncherID last_added = AddPlatformApp();
   while (!test_api_->IsOverflowButtonVisible()) {
-    last_added = AddTabbedBrowser();
+    last_added = AddPlatformApp();
     ++items_added;
     ASSERT_LT(items_added, 10000);
   }
@@ -665,17 +642,17 @@
   EXPECT_FALSE(test_api_->IsOverflowButtonVisible());
 }
 
-// Adds browser button without waiting for animation to finish and verifies
+// Adds platform app button without waiting for animation to finish and verifies
 // that all added buttons are visible.
 TEST_F(LauncherViewTest, AddButtonQuickly) {
   // All buttons should be visible.
   ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
             test_api_->GetButtonCount());
 
-  // Add a few tabbed browser quickly without wait for animation.
+  // Add a few platform buttons quickly without wait for animation.
   int added_count = 0;
   while (!test_api_->IsOverflowButtonVisible()) {
-    AddTabbedBrowserNoWait();
+    AddPlatformAppNoWait();
     ++added_count;
     ASSERT_LT(added_count, 10000);
   }
@@ -843,8 +820,8 @@
   ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
             test_api_->GetButtonCount());
 
-  // Add tabbed browser.
-  LauncherID last_added = AddTabbedBrowser();
+  // Add platform app button.
+  LauncherID last_added = AddPlatformApp();
   LauncherItem item = GetItemByID(last_added);
   int index = model_->ItemIndexByID(last_added);
   internal::LauncherButton* button = GetButtonByID(last_added);
@@ -862,7 +839,7 @@
             test_api_->GetButtonCount());
 
   // Add 2 items to the launcher.
-  LauncherID item1_id = AddTabbedBrowser();
+  LauncherID item1_id = AddPlatformApp();
   LauncherID item2_id = AddPlatformAppNoWait();
   internal::LauncherButton* item1_button = GetButtonByID(item1_id);
   internal::LauncherButton* item2_button = GetButtonByID(item2_id);
@@ -908,7 +885,7 @@
   ASSERT_EQ(test_api_->GetLastVisibleIndex() + 1,
             test_api_->GetButtonCount());
 
-  // Add tabbed browser.
+  // Add platform app button.
   LauncherID last_added = AddPlatformApp();
   LauncherItem item = GetItemByID(last_added);
   int index = model_->ItemIndexByID(last_added);
@@ -941,10 +918,10 @@
 
   // Prepare some items to the launcher.
   LauncherID app_button_id = AddAppShortcut();
-  LauncherID tab_button_id = AddTabbedBrowser();
+  LauncherID platform_button_id = AddPlatformApp();
 
   internal::LauncherButton* app_button = GetButtonByID(app_button_id);
-  internal::LauncherButton* tab_button = GetButtonByID(tab_button_id);
+  internal::LauncherButton* platform_button = GetButtonByID(platform_button_id);
 
   internal::LauncherButtonHost* button_host = launcher_view_;
   internal::LauncherTooltipManager* tooltip_manager =
@@ -966,24 +943,24 @@
 
   // When entered to another item, it switches to the new item.  There is no
   // delay for the visibility.
-  button_host->MouseEnteredButton(tab_button);
+  button_host->MouseEnteredButton(platform_button);
   EXPECT_TRUE(tooltip_manager->IsVisible());
-  EXPECT_EQ(tab_button, GetTooltipAnchorView());
+  EXPECT_EQ(platform_button, GetTooltipAnchorView());
 
-  button_host->MouseExitedButton(tab_button);
+  button_host->MouseExitedButton(platform_button);
   tooltip_manager->Close();
 
   // Next time: enter app_button -> move immediately to tab_button.
   button_host->MouseEnteredButton(app_button);
   button_host->MouseExitedButton(app_button);
-  button_host->MouseEnteredButton(tab_button);
+  button_host->MouseEnteredButton(platform_button);
   EXPECT_FALSE(tooltip_manager->IsVisible());
-  EXPECT_EQ(tab_button, GetTooltipAnchorView());
+  EXPECT_EQ(platform_button, GetTooltipAnchorView());
 }
 
 TEST_F(LauncherViewTest, ShouldHideTooltipTest) {
   LauncherID app_button_id = AddAppShortcut();
-  LauncherID tab_button_id = AddTabbedBrowser();
+  LauncherID platform_button_id = AddPlatformApp();
 
   // The tooltip shouldn't hide if the mouse is on normal buttons.
   for (int i = 0; i < test_api_->GetButtonCount(); i++) {
@@ -1003,10 +980,11 @@
 
   // The tooltip shouldn't hide if the mouse is in the gap between two buttons.
   gfx::Rect app_button_rect = GetButtonByID(app_button_id)->GetMirroredBounds();
-  gfx::Rect tab_button_rect = GetButtonByID(tab_button_id)->GetMirroredBounds();
-  ASSERT_FALSE(app_button_rect.Intersects(tab_button_rect));
+  gfx::Rect platform_button_rect =
+      GetButtonByID(platform_button_id)->GetMirroredBounds();
+  ASSERT_FALSE(app_button_rect.Intersects(platform_button_rect));
   EXPECT_FALSE(launcher_view_->ShouldHideTooltip(
-      gfx::UnionRects(app_button_rect, tab_button_rect).CenterPoint()));
+      gfx::UnionRects(app_button_rect, platform_button_rect).CenterPoint()));
 
   // The tooltip should hide if it's outside of all buttons.
   gfx::Rect all_area;
@@ -1104,10 +1082,10 @@
   // Add buttons until overflow. Let the non-overflow add animations finish but
   // leave the last running.
   int items_added = 0;
-  AddTabbedBrowserNoWait();
+  AddPlatformAppNoWait();
   while (!test_api_->IsOverflowButtonVisible()) {
     test_api_->RunMessageLoopUntilAnimationsDone();
-    AddTabbedBrowserNoWait();
+    AddPlatformAppNoWait();
     ++items_added;
     ASSERT_LT(items_added, 10000);
   }
diff --git a/ash/launcher/overflow_bubble.cc b/ash/launcher/overflow_bubble.cc
deleted file mode 100644
index 7d84d1e..0000000
--- a/ash/launcher/overflow_bubble.cc
+++ /dev/null
@@ -1,285 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/launcher/overflow_bubble.h"
-
-#include <algorithm>
-
-#include "ash/launcher/launcher_types.h"
-#include "ash/launcher/launcher_view.h"
-#include "ash/root_window_controller.h"
-#include "ash/shelf/shelf_layout_manager.h"
-#include "ash/shelf/shelf_widget.h"
-#include "ash/shell.h"
-#include "ash/system/tray/system_tray.h"
-#include "ui/aura/root_window.h"
-#include "ui/gfx/insets.h"
-#include "ui/gfx/screen.h"
-#include "ui/views/bubble/bubble_delegate.h"
-#include "ui/views/bubble/bubble_frame_view.h"
-#include "ui/views/widget/widget.h"
-
-namespace ash {
-namespace internal {
-
-namespace {
-
-// Max bubble size to screen size ratio.
-const float kMaxBubbleSizeToScreenRatio = 0.5f;
-
-// Inner padding in pixels for launcher view inside bubble.
-const int kPadding = 2;
-
-// Padding space in pixels between LauncherView's left/top edge to its contents.
-const int kLauncherViewLeadingInset = 8;
-
-////////////////////////////////////////////////////////////////////////////////
-// OverflowBubbleView
-// OverflowBubbleView hosts a LauncherView to display overflown items.
-
-class OverflowBubbleView : public views::BubbleDelegateView {
- public:
-  OverflowBubbleView();
-  virtual ~OverflowBubbleView();
-
-  void InitOverflowBubble(views::View* anchor, LauncherView* launcher_view);
-
- private:
-  bool IsHorizontalAlignment() const {
-    return GetShelfLayoutManagerForLauncher()->IsHorizontalAlignment();
-  }
-
-  const gfx::Size GetContentsSize() const {
-    return static_cast<views::View*>(launcher_view_)->GetPreferredSize();
-  }
-
-  // Gets arrow location based on shelf alignment.
-  views::BubbleBorder::Arrow GetBubbleArrow() const {
-    return GetShelfLayoutManagerForLauncher()->SelectValueForShelfAlignment(
-        views::BubbleBorder::BOTTOM_LEFT,
-        views::BubbleBorder::LEFT_TOP,
-        views::BubbleBorder::RIGHT_TOP,
-        views::BubbleBorder::TOP_LEFT);
-  }
-
-  void ScrollByXOffset(int x_offset);
-  void ScrollByYOffset(int y_offset);
-
-  // views::View overrides:
-  virtual gfx::Size GetPreferredSize() OVERRIDE;
-  virtual void Layout() OVERRIDE;
-  virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE;
-  virtual bool OnMouseWheel(const ui::MouseWheelEvent& event) OVERRIDE;
-
-  // ui::EventHandler overrides:
-  virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE;
-
-  // views::BubbleDelegate overrides:
-  virtual gfx::Rect GetBubbleBounds() OVERRIDE;
-
-  ShelfLayoutManager* GetShelfLayoutManagerForLauncher() const {
-    return ShelfLayoutManager::ForLauncher(
-        anchor_view()->GetWidget()->GetNativeView());
-  }
-
-  LauncherView* launcher_view_;  // Owned by views hierarchy.
-  gfx::Vector2d scroll_offset_;
-
-  DISALLOW_COPY_AND_ASSIGN(OverflowBubbleView);
-};
-
-OverflowBubbleView::OverflowBubbleView()
-    : launcher_view_(NULL) {
-}
-
-OverflowBubbleView::~OverflowBubbleView() {
-}
-
-void OverflowBubbleView::InitOverflowBubble(views::View* anchor,
-                                            LauncherView* launcher_view) {
-  // set_anchor_view needs to be called before GetShelfLayoutManagerForLauncher
-  // can be called.
-  set_anchor_view(anchor);
-  set_arrow(GetBubbleArrow());
-  set_background(NULL);
-  set_color(SkColorSetARGB(kLauncherBackgroundAlpha, 0, 0, 0));
-  set_margins(gfx::Insets(kPadding, kPadding, kPadding, kPadding));
-  set_move_with_anchor(true);
-
-  // Makes bubble view has a layer and clip its children layers.
-  SetPaintToLayer(true);
-  SetFillsBoundsOpaquely(false);
-  layer()->SetMasksToBounds(true);
-
-  launcher_view_ = launcher_view;
-  AddChildView(launcher_view_);
-
-  views::BubbleDelegateView::CreateBubble(this);
-}
-
-void OverflowBubbleView::ScrollByXOffset(int x_offset) {
-  const gfx::Rect visible_bounds(GetContentsBounds());
-  const gfx::Size contents_size(GetContentsSize());
-
-  int x = std::min(contents_size.width() - visible_bounds.width(),
-                   std::max(0, scroll_offset_.x() + x_offset));
-  scroll_offset_.set_x(x);
-}
-
-void OverflowBubbleView::ScrollByYOffset(int y_offset) {
-  const gfx::Rect visible_bounds(GetContentsBounds());
-  const gfx::Size contents_size(GetContentsSize());
-
-  int y = std::min(contents_size.height() - visible_bounds.height(),
-                   std::max(0, scroll_offset_.y() + y_offset));
-  scroll_offset_.set_y(y);
-}
-
-gfx::Size OverflowBubbleView::GetPreferredSize() {
-  gfx::Size preferred_size = GetContentsSize();
-
-  const gfx::Rect monitor_rect = Shell::GetScreen()->GetDisplayNearestPoint(
-      GetAnchorRect().CenterPoint()).work_area();
-  if (!monitor_rect.IsEmpty()) {
-    if (IsHorizontalAlignment()) {
-      preferred_size.set_width(std::min(
-          preferred_size.width(),
-          static_cast<int>(monitor_rect.width() *
-                           kMaxBubbleSizeToScreenRatio)));
-    } else {
-      preferred_size.set_height(std::min(
-          preferred_size.height(),
-          static_cast<int>(monitor_rect.height() *
-                           kMaxBubbleSizeToScreenRatio)));
-    }
-  }
-
-  return preferred_size;
-}
-
-void OverflowBubbleView::Layout() {
-  launcher_view_->SetBoundsRect(gfx::Rect(
-      gfx::PointAtOffsetFromOrigin(-scroll_offset_), GetContentsSize()));
-}
-
-void OverflowBubbleView::ChildPreferredSizeChanged(views::View* child) {
-  // Ensures |launch_view_| is still visible.
-  ScrollByXOffset(0);
-  ScrollByYOffset(0);
-  Layout();
-
-  SizeToContents();
-}
-
-bool OverflowBubbleView::OnMouseWheel(const ui::MouseWheelEvent& event) {
-  // The MouseWheelEvent was changed to support both X and Y offsets
-  // recently, but the behavior of this function was retained to continue
-  // using Y offsets only. Might be good to simply scroll in both
-  // directions as in OverflowBubbleView::OnScrollEvent.
-  if (IsHorizontalAlignment())
-    ScrollByXOffset(-event.y_offset());
-  else
-    ScrollByYOffset(-event.y_offset());
-  Layout();
-
-  return true;
-}
-
-void OverflowBubbleView::OnScrollEvent(ui::ScrollEvent* event) {
-  ScrollByXOffset(-event->x_offset());
-  ScrollByYOffset(-event->y_offset());
-  Layout();
-  event->SetHandled();
-}
-
-gfx::Rect OverflowBubbleView::GetBubbleBounds() {
-  views::BubbleBorder* border = GetBubbleFrameView()->bubble_border();
-  gfx::Insets bubble_insets = border->GetInsets();
-
-  const int border_size =
-      views::BubbleBorder::is_arrow_on_horizontal(arrow()) ?
-      bubble_insets.left() : bubble_insets.top();
-  const int arrow_offset = border_size + kPadding + kLauncherViewLeadingInset +
-      ShelfLayoutManager::GetPreferredShelfSize() / 2;
-
-  const gfx::Size content_size = GetPreferredSize();
-  border->set_arrow_offset(arrow_offset);
-
-  const gfx::Rect anchor_rect = GetAnchorRect();
-  gfx::Rect bubble_rect = GetBubbleFrameView()->GetUpdatedWindowBounds(
-      anchor_rect,
-      content_size,
-      false);
-
-  gfx::Rect monitor_rect = Shell::GetScreen()->GetDisplayNearestPoint(
-      anchor_rect.CenterPoint()).work_area();
-
-  int offset = 0;
-  if (views::BubbleBorder::is_arrow_on_horizontal(arrow())) {
-    if (bubble_rect.x() < monitor_rect.x())
-      offset = monitor_rect.x() - bubble_rect.x();
-    else if (bubble_rect.right() > monitor_rect.right())
-      offset = monitor_rect.right() - bubble_rect.right();
-
-    bubble_rect.Offset(offset, 0);
-    border->set_arrow_offset(anchor_rect.CenterPoint().x() - bubble_rect.x());
-  } else {
-    if (bubble_rect.y() < monitor_rect.y())
-      offset = monitor_rect.y() - bubble_rect.y();
-    else if (bubble_rect.bottom() > monitor_rect.bottom())
-      offset =  monitor_rect.bottom() - bubble_rect.bottom();
-
-    bubble_rect.Offset(0, offset);
-    border->set_arrow_offset(anchor_rect.CenterPoint().y() - bubble_rect.y());
-  }
-
-  GetBubbleFrameView()->SchedulePaint();
-  return bubble_rect;
-}
-
-}  // namespace
-
-OverflowBubble::OverflowBubble()
-    : bubble_(NULL),
-      launcher_view_(NULL) {
-}
-
-OverflowBubble::~OverflowBubble() {
-  Hide();
-}
-
-void OverflowBubble::Show(views::View* anchor, LauncherView* launcher_view) {
-  Hide();
-
-  OverflowBubbleView* bubble_view = new OverflowBubbleView();
-  bubble_view->InitOverflowBubble(anchor, launcher_view);
-  launcher_view_ = launcher_view;
-
-  bubble_ = bubble_view;
-  RootWindowController::ForWindow(anchor->GetWidget()->GetNativeView())->
-      GetSystemTray()->InitializeBubbleAnimations(bubble_->GetWidget());
-  bubble_->GetWidget()->AddObserver(this);
-  bubble_->GetWidget()->Show();
-}
-
-void OverflowBubble::Hide() {
-  if (!IsShowing())
-    return;
-
-  bubble_->GetWidget()->RemoveObserver(this);
-  bubble_->GetWidget()->Close();
-  bubble_ = NULL;
-  launcher_view_ = NULL;
-}
-
-void OverflowBubble::OnWidgetDestroying(views::Widget* widget) {
-  DCHECK(widget == bubble_->GetWidget());
-  bubble_ = NULL;
-  launcher_view_ = NULL;
-  ShelfLayoutManager::ForLauncher(
-      widget->GetNativeView())->shelf_widget()->launcher()->SchedulePaint();
-}
-
-}  // namespace internal
-}  // namespace ash
diff --git a/ash/launcher/overflow_bubble.h b/ash/launcher/overflow_bubble.h
deleted file mode 100644
index 84fbcc1..0000000
--- a/ash/launcher/overflow_bubble.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_LAUNCHER_OVERFLOW_BUBBLE_H_
-#define ASH_LAUNCHER_OVERFLOW_BUBBLE_H_
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "ui/views/widget/widget_observer.h"
-
-namespace views {
-class View;
-}
-
-namespace ash {
-
-class LauncherDelegate;
-class LauncherModel;
-
-namespace internal {
-
-class LauncherView;
-
-// OverflowBubble displays the overflown launcher items in a bubble.
-class OverflowBubble : public views::WidgetObserver {
- public:
-  OverflowBubble();
-  virtual ~OverflowBubble();
-
-  // Shows an bubble pointing to |anchor| with |launcher_view| as its content.
-  void Show(views::View* anchor, LauncherView* launcher_view);
-
-  void Hide();
-
-  bool IsShowing() const { return !!bubble_; }
-  LauncherView* launcher_view() { return launcher_view_; }
-
- private:
-  // Overridden from views::WidgetObserver:
-  virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE;
-
-  views::View* bubble_;  // Owned by views hierarchy.
-  LauncherView* launcher_view_;  // Owned by |bubble_|.
-
-  DISALLOW_COPY_AND_ASSIGN(OverflowBubble);
-};
-
-}  // namespace internal
-}  // namespace ash
-
-#endif  // ASH_LAUNCHER_OVERFLOW_BUBBLE_H_
diff --git a/ash/launcher/overflow_button.cc b/ash/launcher/overflow_button.cc
deleted file mode 100644
index c3d5a68..0000000
--- a/ash/launcher/overflow_button.cc
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/launcher/overflow_button.h"
-
-#include "ash/ash_switches.h"
-#include "ash/shelf/shelf_layout_manager.h"
-#include "ash/shelf/shelf_widget.h"
-#include "grit/ash_resources.h"
-#include "grit/ash_strings.h"
-#include "third_party/skia/include/core/SkPaint.h"
-#include "third_party/skia/include/core/SkPath.h"
-#include "ui/base/animation/throb_animation.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/image/image_skia_operations.h"
-#include "ui/gfx/skbitmap_operations.h"
-#include "ui/gfx/skia_util.h"
-#include "ui/gfx/transform.h"
-#include "ui/views/widget/widget.h"
-
-namespace ash {
-namespace internal {
-
-namespace {
-
-const int kButtonHoverAlpha = 150;
-
-const int kButtonCornerRadius = 2;
-
-const int kButtonHoverSize = 28;
-
-const int kBackgroundOffset = (48 - kButtonHoverSize) / 2;
-
-// Padding from the inner edge of the shelf (towards center of display) to
-// the edge of the background image of the overflow button.
-const int kImagePaddingFromShelf = 5;
-
-}  // namesapce
-
-OverflowButton::OverflowButton(views::ButtonListener* listener)
-    : CustomButton(listener),
-      bottom_image_(NULL) {
-  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
-  bottom_image_ = rb.GetImageNamed(IDR_AURA_LAUNCHER_OVERFLOW).ToImageSkia();
-
-
-  set_accessibility_focusable(true);
-  SetAccessibleName(l10n_util::GetStringUTF16(IDS_ASH_SHELF_OVERFLOW_NAME));
-}
-
-OverflowButton::~OverflowButton() {}
-
-void OverflowButton::OnShelfAlignmentChanged() {
-  SchedulePaint();
-}
-
-void OverflowButton::PaintBackground(gfx::Canvas* canvas, int alpha) {
-  gfx::Rect bounds(GetContentsBounds());
-  gfx::Rect rect(0, 0, kButtonHoverSize, kButtonHoverSize);
-  ShelfLayoutManager* shelf =
-      ShelfLayoutManager::ForLauncher(GetWidget()->GetNativeView());
-
-  // Nudge the background a little to line up right.
-  if (shelf->IsHorizontalAlignment()) {
-    rect.set_origin(gfx::Point(
-        bounds.x() + ((bounds.width() - kButtonHoverSize) / 2) - 1,
-        bounds.y() + kBackgroundOffset - 1));
-
-  } else {
-    rect.set_origin(gfx::Point(
-        bounds.x() + kBackgroundOffset - 1,
-        bounds.y() + ((bounds.height() - kButtonHoverSize) / 2) - 1));
-  }
-
-  SkPaint paint;
-  paint.setAntiAlias(true);
-  paint.setStyle(SkPaint::kFill_Style);
-  paint.setColor(SkColorSetARGB(
-      kButtonHoverAlpha * hover_animation_->GetCurrentValue(),
-      0, 0, 0));
-
-  const SkScalar radius = SkIntToScalar(kButtonCornerRadius);
-  SkPath path;
-  path.addRoundRect(gfx::RectToSkRect(rect), radius, radius);
-  canvas->DrawPath(path, paint);
-}
-
-void OverflowButton::OnPaint(gfx::Canvas* canvas) {
-  ShelfLayoutManager* layout_manager = ShelfLayoutManager::ForLauncher(
-      GetWidget()->GetNativeView());
-  ShelfAlignment alignment = layout_manager->GetAlignment();
-
-  gfx::Rect bounds(GetContentsBounds());
-  if (ash::switches::UseAlternateShelfLayout()) {
-    ResourceBundle& rb = ResourceBundle::GetSharedInstance();
-    int background_image_id = 0;
-    if (layout_manager->shelf_widget()->launcher()->IsShowingOverflowBubble())
-      background_image_id = IDR_AURA_NOTIFICATION_BACKGROUND_PRESSED;
-    else if(layout_manager->shelf_widget()->GetDimsShelf())
-      background_image_id = IDR_AURA_NOTIFICATION_BACKGROUND_ON_BLACK;
-    else
-      background_image_id = IDR_AURA_NOTIFICATION_BACKGROUND_NORMAL;
-
-    const gfx::ImageSkia* background =
-        rb.GetImageNamed(background_image_id).ToImageSkia();
-    if (alignment == SHELF_ALIGNMENT_LEFT) {
-      bounds = gfx::Rect(
-          bounds.right() - background->width() - kImagePaddingFromShelf,
-          bounds.y() + (bounds.height() - background->height()) / 2,
-          background->width(), background->height());
-    } else if (alignment == SHELF_ALIGNMENT_RIGHT) {
-      bounds = gfx::Rect(
-          bounds.x() + kImagePaddingFromShelf,
-          bounds.y() + (bounds.height() - background->height()) / 2,
-          background->width(), background->height());
-    } else {
-      bounds = gfx::Rect(
-          bounds.x() + (bounds.width() - background->width()) / 2,
-          bounds.y() + kImagePaddingFromShelf,
-          background->width(), background->height());
-    }
-    canvas->DrawImageInt(*background, bounds.x(), bounds.y());
-  } else {
-    if (alignment == SHELF_ALIGNMENT_BOTTOM) {
-      bounds = gfx::Rect(
-          bounds.x() + ((bounds.width() - kButtonHoverSize) / 2) - 1,
-          bounds.y() + kBackgroundOffset - 1,
-          kButtonHoverSize, kButtonHoverSize);
-    } else {
-      bounds = gfx::Rect(
-          bounds.x() + kBackgroundOffset -1,
-          bounds.y() + ((bounds.height() - kButtonHoverSize) / 2) -1,
-          kButtonHoverSize, kButtonHoverSize);
-    }
-    if (hover_animation_->is_animating()) {
-      PaintBackground(
-          canvas,
-          kButtonHoverAlpha * hover_animation_->GetCurrentValue());
-    } else if (state() == STATE_HOVERED || state() == STATE_PRESSED) {
-      PaintBackground(canvas, kButtonHoverAlpha);
-    }
-  }
-
-  if (height() < kButtonHoverSize)
-    return;
-
-  const gfx::ImageSkia* image = NULL;
-
-  switch(alignment) {
-    case SHELF_ALIGNMENT_LEFT:
-      if (left_image_.isNull()) {
-        left_image_ = gfx::ImageSkiaOperations::CreateRotatedImage(
-            *bottom_image_, SkBitmapOperations::ROTATION_90_CW);
-      }
-      image = &left_image_;
-      break;
-    case SHELF_ALIGNMENT_RIGHT:
-      if (right_image_.isNull()) {
-        right_image_ = gfx::ImageSkiaOperations::CreateRotatedImage(
-            *bottom_image_, SkBitmapOperations::ROTATION_270_CW);
-      }
-      image = &right_image_;
-      break;
-    default:
-      image = bottom_image_;
-      break;
-  }
-
-  canvas->DrawImageInt(*image,
-                       bounds.x() + ((bounds.width() - image->width()) / 2),
-                       bounds.y() + ((bounds.height() - image->height()) / 2));
-}
-
-}  // namespace internal
-}  // namespace ash
diff --git a/ash/launcher/overflow_button.h b/ash/launcher/overflow_button.h
deleted file mode 100644
index 0b225aa..0000000
--- a/ash/launcher/overflow_button.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_LAUNCHER_OVERFLOW_BUTTON_H_
-#define ASH_LAUNCHER_OVERFLOW_BUTTON_H_
-
-#include "ash/shelf/shelf_types.h"
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "ui/gfx/image/image_skia.h"
-#include "ui/views/controls/button/custom_button.h"
-
-namespace ash {
-namespace internal {
-
-// Launcher overflow chevron button.
-class OverflowButton : public views::CustomButton {
- public:
-  explicit OverflowButton(views::ButtonListener* listener);
-  virtual ~OverflowButton();
-
-  void OnShelfAlignmentChanged();
-
- private:
-  void PaintBackground(gfx::Canvas* canvas, int alpha);
-
-  // views::View overrides:
-  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
-
-  // Left and right images are rotations of bottom_image and are
-  // owned by the overflow button.
-  gfx::ImageSkia left_image_;
-  gfx::ImageSkia right_image_;
-  // Bottom image is owned by the resource bundle.
-  const gfx::ImageSkia* bottom_image_;
-
-  DISALLOW_COPY_AND_ASSIGN(OverflowButton);
-};
-
-}  // namespace internal
-}  // namespace ash
-
-#endif  // ASH_LAUNCHER_OVERFLOW_BUTTON_H_
diff --git a/ash/launcher/scoped_observer_with_duplicated_sources.h b/ash/launcher/scoped_observer_with_duplicated_sources.h
deleted file mode 100644
index 60b10f9..0000000
--- a/ash/launcher/scoped_observer_with_duplicated_sources.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_LAUNCHER_SCOPED_OBSERVER_WITH_DUPLICATED_SOURCES_H_
-#define ASH_LAUNCHER_SCOPED_OBSERVER_WITH_DUPLICATED_SOURCES_H_
-
-#include <map>
-
-#include "base/basictypes.h"
-#include "base/logging.h"
-
-// ScopedObserverWithDuplicatedSources is used to keep track of the set of
-// sources an object has attached itself to as an observer. When
-// ScopedObserverWithDuplicatedSources is destroyed it removes the object as an
-// observer from all sources it has been added to.
-// ScopedObserverWithDuplicatedSources adds |observer| once for a particular
-// source, no matter how many times Add() is invoked. Additionaly |observer| is
-// only removed once Remove() is invoked the same number of times as Add().
-
-template <class Source, class Observer>
-class ScopedObserverWithDuplicatedSources {
- public:
-  explicit ScopedObserverWithDuplicatedSources(Observer* observer)
-      : observer_(observer) {}
-
-  ~ScopedObserverWithDuplicatedSources() {
-    typename SourceToAddCountMap::const_iterator iter =
-        counted_sources_.begin();
-    for (; iter != counted_sources_.end(); ++iter)
-      iter->first->RemoveObserver(observer_);
-  }
-
-  // Adds the object passed to the constructor only once as an observer on
-  // |source|.
-  void Add(Source* source) {
-    if (counted_sources_.find(source) == counted_sources_.end())
-      source->AddObserver(observer_);
-    counted_sources_[source]++;
-  }
-
-  // Only remove the object passed to the constructor as an observer from
-  // |source| when Remove() is invoked the same number of times as Add().
-  void Remove(Source* source) {
-    typename SourceToAddCountMap::iterator iter =
-        counted_sources_.find(source);
-    DCHECK(iter != counted_sources_.end() && iter->second > 0);
-
-    if (--iter->second == 0) {
-      counted_sources_.erase(source);
-      source->RemoveObserver(observer_);
-    }
-  }
-
-  bool IsObserving(Source* source) const {
-    return counted_sources_.find(source) != counted_sources_.end();
-  }
-
- private:
-  typedef std::map<Source*, int> SourceToAddCountMap;
-
-  Observer* observer_;
-
-  // Map Source and its adding count.
-  std::map<Source*, int> counted_sources_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedObserverWithDuplicatedSources);
-};
-
-#endif  // ASH_LAUNCHER_SCOPED_OBSERVER_WITH_DUPLICATED_SOURCES_H_
diff --git a/ash/launcher/scoped_observer_with_duplicated_sources_unittest.cc b/ash/launcher/scoped_observer_with_duplicated_sources_unittest.cc
deleted file mode 100644
index d7d13f3..0000000
--- a/ash/launcher/scoped_observer_with_duplicated_sources_unittest.cc
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/launcher/scoped_observer_with_duplicated_sources.h"
-
-#include "base/memory/scoped_ptr.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-class TestObserver {
- public:
-  TestObserver() {}
-  ~TestObserver() {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TestObserver);
-};
-
-class TestSource {
- public:
-  TestSource() : observer_count_(0) {}
-  ~TestSource() {}
-
-  void AddObserver(TestObserver* observer) {
-    observer_count_++;
-  }
-  void RemoveObserver(TestObserver* observer) {
-    observer_count_--;
-  }
-
-  int GetObserverCount() {
-    return observer_count_;
-  }
-
- private:
-  int observer_count_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestSource);
-};
-
-TEST(ScopedObserverWithDuplicatedSourcesTest, DuplicatedSource) {
-  TestObserver observer;
-  TestSource source1;
-  TestSource source2;
-
-  ScopedObserverWithDuplicatedSources<TestSource, TestObserver>
-      observers(&observer);
-  EXPECT_EQ(0, source1.GetObserverCount());
-  EXPECT_FALSE(observers.IsObserving(&source1));
-  EXPECT_EQ(0, source2.GetObserverCount());
-  EXPECT_FALSE(observers.IsObserving(&source2));
-
-  // Add |source1|.
-  observers.Add(&source1);
-  EXPECT_EQ(1, source1.GetObserverCount());
-  EXPECT_TRUE(observers.IsObserving(&source1));
-  // AddObserver of TestSource is called only once.
-  observers.Add(&source1);
-  EXPECT_EQ(1, source1.GetObserverCount());
-  EXPECT_TRUE(observers.IsObserving(&source1));
-
-  // Add |source2|.
-  observers.Add(&source2);
-  EXPECT_EQ(1, source2.GetObserverCount());
-  EXPECT_TRUE(observers.IsObserving(&source2));
-
-  // Remove |source1|.
-  observers.Remove(&source1);
-  EXPECT_EQ(1, source1.GetObserverCount());
-  EXPECT_TRUE(observers.IsObserving(&source1));
-
-  // Remove |source2|.
-  observers.Remove(&source2);
-  EXPECT_EQ(0, source2.GetObserverCount());
-  EXPECT_FALSE(observers.IsObserving(&source2));
-
-  // Remove |source1| again.
-  observers.Remove(&source1);
-  // In this time, |observer| is removed from |source1|.
-  EXPECT_EQ(0, source1.GetObserverCount());
-  EXPECT_FALSE(observers.IsObserving(&source1));
-}
diff --git a/ash/launcher/tabbed_launcher_button.cc b/ash/launcher/tabbed_launcher_button.cc
deleted file mode 100644
index 517607c..0000000
--- a/ash/launcher/tabbed_launcher_button.cc
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/launcher/tabbed_launcher_button.h"
-
-#include <algorithm>
-
-#include "ash/launcher/launcher_button_host.h"
-#include "ash/launcher/launcher_types.h"
-#include "grit/ash_resources.h"
-#include "ui/base/accessibility/accessible_view_state.h"
-#include "ui/base/animation/multi_animation.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/image/image.h"
-#include "ui/gfx/insets.h"
-
-namespace {
-const int kIconOffsetY = 7;
-}
-
-namespace ash {
-namespace internal {
-
-TabbedLauncherButton::IconView::IconView(
-    TabbedLauncherButton* host)
-    : host_(host) {
-  if (!browser_image_) {
-    ResourceBundle& rb = ResourceBundle::GetSharedInstance();
-
-    browser_image_ = rb.GetImageNamed(IDR_AURA_LAUNCHER_BROWSER).ToImageSkia();
-    incognito_browser_image_ =
-        rb.GetImageNamed(IDR_AURA_LAUNCHER_INCOGNITO_BROWSER).ToImageSkia();
-    browser_panel_image_ =
-        rb.GetImageNamed(IDR_AURA_LAUNCHER_BROWSER_PANEL).ToImageSkia();
-    incognito_browser_panel_image_ =
-        rb.GetImageNamed(
-            IDR_AURA_LAUNCHER_INCOGNITO_BROWSER_PANEL).ToImageSkia();
-  }
-  set_icon_size(0);
-  if (host->is_incognito() == STATE_NOT_INCOGNITO)
-    LauncherButton::IconView::SetImage(*browser_image_);
-  else
-    LauncherButton::IconView::SetImage(*incognito_browser_image_);
-}
-
-TabbedLauncherButton::IconView::~IconView() {
-}
-
-void TabbedLauncherButton::IconView::AnimationEnded(
-    const ui::Animation* animation) {
-  AnimationProgressed(animation);
-  animating_image_ = gfx::ImageSkia();
-}
-
-void TabbedLauncherButton::IconView::AnimationProgressed(
-    const ui::Animation* animation) {
-  if (animation_->current_part_index() == 1)
-    SchedulePaint();
-}
-
-void TabbedLauncherButton::IconView::SetTabImage(const gfx::ImageSkia& image) {
-  if (image.isNull()) {
-    if (!image_.isNull()) {
-      // Pause for 500ms, then ease out for 200ms.
-      ui::MultiAnimation::Parts animation_parts;
-      animation_parts.push_back(ui::MultiAnimation::Part(500, ui::Tween::ZERO));
-      animation_parts.push_back(
-          ui::MultiAnimation::Part(200, ui::Tween::EASE_OUT));
-      animation_.reset(new ui::MultiAnimation(
-          animation_parts,
-          ui::MultiAnimation::GetDefaultTimerInterval()));
-      animation_->set_continuous(false);
-      animation_->set_delegate(this);
-      animation_->Start();
-      animating_image_ = image_;
-      image_ = image;
-    }
-  } else {
-    animation_.reset();
-    SchedulePaint();
-    image_ = image;
-  }
-}
-
-void TabbedLauncherButton::IconView::OnPaint(gfx::Canvas* canvas) {
-  LauncherButton::IconView::OnPaint(canvas);
-
-  // Only non incognito icons show the tab image.
-  if (host_->is_incognito() != STATE_NOT_INCOGNITO)
-    return;
-
-  if ((animation_.get() && animation_->is_animating() &&
-      animation_->current_part_index() == 1)) {
-    int x = (width() - animating_image_.width()) / 2;
-    canvas->SaveLayerAlpha(animation_->CurrentValueBetween(255, 0));
-    canvas->DrawImageInt(animating_image_, x, kIconOffsetY);
-    canvas->Restore();
-  } else {
-    int x = (width() - image_.width()) / 2;
-    canvas->DrawImageInt(image_, x, kIconOffsetY);
-  }
-}
-
-// static
-const gfx::ImageSkia* TabbedLauncherButton::IconView::browser_image_ = NULL;
-const gfx::ImageSkia* TabbedLauncherButton::IconView::incognito_browser_image_ =
-    NULL;
-const gfx::ImageSkia* TabbedLauncherButton::IconView::browser_panel_image_ =
-    NULL;
-const gfx::ImageSkia*
-    TabbedLauncherButton::IconView::incognito_browser_panel_image_ = NULL;
-
-TabbedLauncherButton* TabbedLauncherButton::Create(
-    views::ButtonListener* listener,
-    LauncherButtonHost* host,
-    ShelfLayoutManager* shelf_layout_manager,
-    IncognitoState is_incognito) {
-  TabbedLauncherButton* button = new TabbedLauncherButton(
-      listener, host, shelf_layout_manager, is_incognito);
-  button->Init();
-  return button;
-}
-
-TabbedLauncherButton::TabbedLauncherButton(
-    views::ButtonListener* listener,
-    LauncherButtonHost* host,
-    ShelfLayoutManager* shelf_layout_manager,
-    IncognitoState is_incognito)
-    : LauncherButton(listener, host, shelf_layout_manager),
-      is_incognito_(is_incognito) {
-  set_accessibility_focusable(true);
-}
-
-TabbedLauncherButton::~TabbedLauncherButton() {
-}
-
-void TabbedLauncherButton::SetTabImage(const gfx::ImageSkia& image) {
-  tabbed_icon_view()->SetTabImage(image);
-}
-
-void TabbedLauncherButton::GetAccessibleState(ui::AccessibleViewState* state) {
-  state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
-  state->name = host()->GetAccessibleName(this);
-}
-
-LauncherButton::IconView* TabbedLauncherButton::CreateIconView() {
-  return new IconView(this);
-}
-
-}  // namespace internal
-}  // namespace ash
diff --git a/ash/launcher/tabbed_launcher_button.h b/ash/launcher/tabbed_launcher_button.h
deleted file mode 100644
index 0ac2c3b..0000000
--- a/ash/launcher/tabbed_launcher_button.h
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_LAUNCHER_TABBED_LAUNCHER_BUTTON_H_
-#define ASH_LAUNCHER_TABBED_LAUNCHER_BUTTON_H_
-
-#include "ash/launcher/launcher_button.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/timer/timer.h"
-#include "ui/base/animation/animation_delegate.h"
-#include "ui/views/controls/button/image_button.h"
-#include "ui/views/controls/glow_hover_controller.h"
-
-namespace gfx {
-class ImageSkia;
-}
-
-namespace ui {
-class MultiAnimation;
-}
-
-namespace ash {
-
-struct LauncherItem;
-
-namespace internal {
-
-// Button used for items on the launcher corresponding to tabbed windows.
-class TabbedLauncherButton : public LauncherButton {
- public:
-  // Indicates if this button is incognito or not.
-  enum IncognitoState {
-    STATE_INCOGNITO,
-    STATE_NOT_INCOGNITO,
-  };
-
-  static TabbedLauncherButton* Create(views::ButtonListener* listener,
-                                      LauncherButtonHost* host,
-                                      ShelfLayoutManager* shelf_layout_manager,
-                                      IncognitoState is_incognito);
-  virtual ~TabbedLauncherButton();
-
-  // Sets the images to display for this entry.
-  void SetTabImage(const gfx::ImageSkia& image);
-
-  // This only defines how the icon is drawn. Do not use it for other purposes.
-  IncognitoState is_incognito() const { return is_incognito_; }
-
- protected:
-  TabbedLauncherButton(views::ButtonListener* listener,
-                       LauncherButtonHost* host,
-                       ShelfLayoutManager* shelf_layout_manager,
-                       IncognitoState is_incognito);
-  // View override.
-  virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
-
-  // LauncherButton override.
-  virtual IconView* CreateIconView() OVERRIDE;
-
- private:
-  // Used as the delegate for |animation_|.
-  class IconView : public LauncherButton::IconView,
-                   public ui::AnimationDelegate {
-   public:
-    explicit IconView(TabbedLauncherButton* host);
-    virtual ~IconView();
-
-    // ui::AnimationDelegateImpl overrides:
-    virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE;
-    virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE;
-
-    // Sets the image to display for this entry.
-    void SetTabImage(const gfx::ImageSkia& image);
-
-   protected:
-    // View override.
-    virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
-
-   private:
-    TabbedLauncherButton* host_;
-    gfx::ImageSkia image_;
-    gfx::ImageSkia animating_image_;
-
-    // Used to animate image.
-    scoped_ptr<ui::MultiAnimation> animation_;
-
-    // Background images. Which one is chosen depends on the type of the window.
-    static const gfx::ImageSkia* browser_image_;
-    static const gfx::ImageSkia* incognito_browser_image_;
-    // TODO[dave] implement panel specific image.
-    static const gfx::ImageSkia* browser_panel_image_;
-    static const gfx::ImageSkia* incognito_browser_panel_image_;
-
-    DISALLOW_COPY_AND_ASSIGN(IconView);
-  };
-
-  IconView* tabbed_icon_view() {
-    return static_cast<IconView*>(icon_view());
-  }
-
-  // Indicates how the icon is drawn. If true an Incognito symbol will be
-  // drawn. It does not necessarily indicate if the window is 'incognito'.
-  const IncognitoState is_incognito_;
-
-  DISALLOW_COPY_AND_ASSIGN(TabbedLauncherButton);
-};
-
-}  // namespace internal
-}  // namespace ash
-
-#endif  // ASH_LAUNCHER_TABBED_LAUNCHER_BUTTON_H_
diff --git a/ash/resources/ash_resources.grd b/ash/resources/ash_resources.grd
index 991c895..fd4ca14 100644
--- a/ash/resources/ash_resources.grd
+++ b/ash/resources/ash_resources.grd
@@ -20,16 +20,11 @@
            BECAUSE YOUR RESOURCES ARE FUNCTIONALLY RELATED OR FALL UNDER THE
            SAME CONDITIONALS. -->
       <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_BACKGROUND" file="common/launcher/launcher_background.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_BROWSER" file="common/launcher/launcher_browser.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_BROWSER_PANEL" file="common/launcher/launcher_browser_panel.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_BROWSER_SHORTCUT" file="common/chromium-32.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_DIMMING" file="common/launcher/launcher_dimming.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_ICON_APPLIST" file="common/launcher/launcher_appmenu.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_ICON_APPLIST_ALTERNATE" file="common/alt_launcher/status_app_menu_icon.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_ICON_APPLIST_HOT" file="common/launcher/launcher_appmenu_hover.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_ICON_APPLIST_PUSHED" file="common/launcher/launcher_appmenu_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_INCOGNITO_BROWSER" file="common/launcher/launcher_incognito_browser.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_INCOGNITO_BROWSER_PANEL" file="common/launcher/launcher_incognito_browser_panel.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_LIST_BROWSER" file="common/launcher/window_switcher_icon_normal.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_LIST_INCOGNITO_BROWSER" file="common/launcher/window_switcher_icon_incognito.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_OVERFLOW" file="common/launcher/launcher_overflow.png" />
@@ -222,25 +217,15 @@
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_FULLSCREEN_SHADOW_RTL" file="common/window_fullscreen_shadow_rtl.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_HEADER_BASE_ACTIVE" file="common/window_header_base_active.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_HEADER_BASE_INACTIVE" file="common/window_header_base_inactive.png" />
-        <!-- TODO(jamescook): We need updated incognito art for Aura. -->
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_ACTIVE" file="common/window_header_base_incognito_active.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_INACTIVE" file="common/window_header_base_incognito_inactive.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_HEADER_BASE_MINIMAL" file="common/window_header_base_minimal.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_HEADER_SHADE_LEFT" file="common/window_header_shade_left.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_HEADER_SHADE_MIDDLE" file="common/window_header_shade_middle.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_HEADER_SHADE_RIGHT" file="common/window_header_shade_right.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_HEADER_SHADE_TOP" file="common/window_header_shade_top.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_HEADER_SHADE_TOP_LEFT" file="common/window_header_shade_top_left.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_HEADER_SHADE_TOP_RIGHT" file="common/window_header_shade_top_right.png" />
 
-      <!-- Art for experimental "immersive mode" button at top of screen. crbug.com/157612 -->
-      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_IMMERSIVE_ENTER" file="common/window_immersive_enter_normal.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_IMMERSIVE_ENTER_H" file="common/window_immersive_enter_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_IMMERSIVE_ENTER_P" file="common/window_immersive_enter_pressed.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_IMMERSIVE_EXIT" file="common/window_immersive_exit_normal.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_IMMERSIVE_EXIT_H" file="common/window_immersive_exit_hover.png" />
-      <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_IMMERSIVE_EXIT_P" file="common/window_immersive_exit_pressed.png" />
-
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CLOSE" file="common/window_close_tall_normal.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CLOSE_H" file="common/window_close_tall_hover.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_WINDOW_CLOSE_P" file="common/window_close_tall_pressed.png" />
diff --git a/ash/resources/default_100_percent/common/chromium-32.png b/ash/resources/default_100_percent/common/chromium-32.png
deleted file mode 100644
index e708447..0000000
--- a/ash/resources/default_100_percent/common/chromium-32.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_100_percent/common/launcher/launcher_browser.png b/ash/resources/default_100_percent/common/launcher/launcher_browser.png
deleted file mode 100644
index 669a3a9..0000000
--- a/ash/resources/default_100_percent/common/launcher/launcher_browser.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_100_percent/common/launcher/launcher_browser_panel.png b/ash/resources/default_100_percent/common/launcher/launcher_browser_panel.png
deleted file mode 100644
index b974cb3..0000000
--- a/ash/resources/default_100_percent/common/launcher/launcher_browser_panel.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_100_percent/common/launcher/launcher_incognito_browser.png b/ash/resources/default_100_percent/common/launcher/launcher_incognito_browser.png
deleted file mode 100644
index f9c4918..0000000
--- a/ash/resources/default_100_percent/common/launcher/launcher_incognito_browser.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_100_percent/common/launcher/launcher_incognito_browser_panel.png b/ash/resources/default_100_percent/common/launcher/launcher_incognito_browser_panel.png
deleted file mode 100644
index 3bfdcc3..0000000
--- a/ash/resources/default_100_percent/common/launcher/launcher_incognito_browser_panel.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_header_shade_middle.png b/ash/resources/default_100_percent/common/window_header_shade_middle.png
deleted file mode 100644
index 7c1448a..0000000
--- a/ash/resources/default_100_percent/common/window_header_shade_middle.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_immersive_enter_hover.png b/ash/resources/default_100_percent/common/window_immersive_enter_hover.png
deleted file mode 100644
index e9c8aef..0000000
--- a/ash/resources/default_100_percent/common/window_immersive_enter_hover.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_immersive_enter_normal.png b/ash/resources/default_100_percent/common/window_immersive_enter_normal.png
deleted file mode 100644
index bdb7c2a..0000000
--- a/ash/resources/default_100_percent/common/window_immersive_enter_normal.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_immersive_enter_pressed.png b/ash/resources/default_100_percent/common/window_immersive_enter_pressed.png
deleted file mode 100644
index 095a17f..0000000
--- a/ash/resources/default_100_percent/common/window_immersive_enter_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_immersive_exit_hover.png b/ash/resources/default_100_percent/common/window_immersive_exit_hover.png
deleted file mode 100644
index a0d6e04..0000000
--- a/ash/resources/default_100_percent/common/window_immersive_exit_hover.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_immersive_exit_normal.png b/ash/resources/default_100_percent/common/window_immersive_exit_normal.png
deleted file mode 100644
index 4b6fd37..0000000
--- a/ash/resources/default_100_percent/common/window_immersive_exit_normal.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_immersive_exit_pressed.png b/ash/resources/default_100_percent/common/window_immersive_exit_pressed.png
deleted file mode 100644
index cc903e9..0000000
--- a/ash/resources/default_100_percent/common/window_immersive_exit_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_browser.png b/ash/resources/default_200_percent/common/launcher/launcher_browser.png
deleted file mode 100644
index 0f02221..0000000
--- a/ash/resources/default_200_percent/common/launcher/launcher_browser.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_browser_panel.png b/ash/resources/default_200_percent/common/launcher/launcher_browser_panel.png
deleted file mode 100644
index 457026e..0000000
--- a/ash/resources/default_200_percent/common/launcher/launcher_browser_panel.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_incognito_browser.png b/ash/resources/default_200_percent/common/launcher/launcher_incognito_browser.png
deleted file mode 100644
index f6d4821..0000000
--- a/ash/resources/default_200_percent/common/launcher/launcher_incognito_browser.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_incognito_browser_panel.png b/ash/resources/default_200_percent/common/launcher/launcher_incognito_browser_panel.png
deleted file mode 100644
index 4ba2a86..0000000
--- a/ash/resources/default_200_percent/common/launcher/launcher_incognito_browser_panel.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_header_shade_middle.png b/ash/resources/default_200_percent/common/window_header_shade_middle.png
deleted file mode 100644
index 8634bef..0000000
--- a/ash/resources/default_200_percent/common/window_header_shade_middle.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_immersive_enter_hover.png b/ash/resources/default_200_percent/common/window_immersive_enter_hover.png
deleted file mode 100644
index fb796a0..0000000
--- a/ash/resources/default_200_percent/common/window_immersive_enter_hover.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_immersive_enter_normal.png b/ash/resources/default_200_percent/common/window_immersive_enter_normal.png
deleted file mode 100644
index e2e0ee2..0000000
--- a/ash/resources/default_200_percent/common/window_immersive_enter_normal.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_immersive_enter_pressed.png b/ash/resources/default_200_percent/common/window_immersive_enter_pressed.png
deleted file mode 100644
index 0421788..0000000
--- a/ash/resources/default_200_percent/common/window_immersive_enter_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_immersive_exit_hover.png b/ash/resources/default_200_percent/common/window_immersive_exit_hover.png
deleted file mode 100644
index d267969..0000000
--- a/ash/resources/default_200_percent/common/window_immersive_exit_hover.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_immersive_exit_normal.png b/ash/resources/default_200_percent/common/window_immersive_exit_normal.png
deleted file mode 100644
index bcf3050..0000000
--- a/ash/resources/default_200_percent/common/window_immersive_exit_normal.png
+++ /dev/null
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_immersive_exit_pressed.png b/ash/resources/default_200_percent/common/window_immersive_exit_pressed.png
deleted file mode 100644
index 724f7f2..0000000
--- a/ash/resources/default_200_percent/common/window_immersive_exit_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ash/screen_ash.cc b/ash/screen_ash.cc
index f2217d7..31c5bcc 100644
--- a/ash/screen_ash.cc
+++ b/ash/screen_ash.cc
@@ -118,15 +118,22 @@
   return aura::Env::GetInstance()->last_mouse_location();
 }
 
-gfx::NativeWindow ScreenAsh::GetWindowAtCursorScreenPoint() {
-  const gfx::Point point = Shell::GetScreen()->GetCursorScreenPoint();
+gfx::NativeWindow ScreenAsh::GetWindowUnderCursor() {
+  return GetWindowAtScreenPoint(Shell::GetScreen()->GetCursorScreenPoint());
+}
+
+gfx::NativeWindow ScreenAsh::GetWindowAtScreenPoint(const gfx::Point& point) {
   return wm::GetRootWindowAt(point)->GetTopWindowContainingPoint(point);
 }
 
-int ScreenAsh::GetNumDisplays() {
+int ScreenAsh::GetNumDisplays() const {
   return DisplayController::GetNumDisplays();
 }
 
+std::vector<gfx::Display> ScreenAsh::GetAllDisplays() const {
+  return GetDisplayManager()->displays();
+}
+
 gfx::Display ScreenAsh::GetDisplayNearestWindow(gfx::NativeView window) const {
   return GetDisplayController()->GetDisplayNearestWindow(window);
 }
diff --git a/ash/screen_ash.h b/ash/screen_ash.h
index 649c3d9..6c07607 100644
--- a/ash/screen_ash.h
+++ b/ash/screen_ash.h
@@ -65,8 +65,11 @@
   // gfx::Screen overrides:
   virtual bool IsDIPEnabled() OVERRIDE;
   virtual gfx::Point GetCursorScreenPoint() OVERRIDE;
-  virtual gfx::NativeWindow GetWindowAtCursorScreenPoint() OVERRIDE;
-  virtual int GetNumDisplays() OVERRIDE;
+  virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE;
+  virtual gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point)
+      OVERRIDE;
+  virtual int GetNumDisplays() const OVERRIDE;
+  virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE;
   virtual gfx::Display GetDisplayNearestWindow(
       gfx::NativeView view) const OVERRIDE;
   virtual gfx::Display GetDisplayNearestPoint(
diff --git a/ash/shelf/alternate_app_list_button.cc b/ash/shelf/alternate_app_list_button.cc
new file mode 100644
index 0000000..7bcb84b
--- /dev/null
+++ b/ash/shelf/alternate_app_list_button.cc
@@ -0,0 +1,151 @@
+// 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 "ash/shelf/alternate_app_list_button.h"
+
+#include "ash/ash_switches.h"
+#include "ash/launcher/launcher_button_host.h"
+#include "ash/launcher/launcher_types.h"
+#include "ash/shelf/shelf_layout_manager.h"
+#include "ash/shelf/shelf_widget.h"
+#include "ash/shell.h"
+#include "grit/ash_resources.h"
+#include "grit/ash_strings.h"
+#include "ui/base/accessibility/accessible_view_state.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_animation_element.h"
+#include "ui/compositor/layer_animation_sequence.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/image_skia_operations.h"
+#include "ui/views/controls/button/image_button.h"
+
+namespace ash {
+namespace internal {
+namespace {
+const int kImagePaddingFromShelf = 5;
+}  // namespace
+
+// static
+const int AlternateAppListButton::kImageBoundsSize = 7;
+
+
+AlternateAppListButton::AlternateAppListButton(views::ButtonListener* listener,
+                                               LauncherButtonHost* host,
+                                               ShelfWidget* shelf_widget)
+    : views::ImageButton(listener),
+      host_(host),
+      shelf_widget_(shelf_widget) {
+  SetAccessibleName(l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_TITLE));
+  SetSize(gfx::Size(ShelfLayoutManager::kShelfSize,
+                    ShelfLayoutManager::kShelfSize));
+}
+
+AlternateAppListButton::~AlternateAppListButton() {
+}
+
+bool AlternateAppListButton::OnMousePressed(const ui::MouseEvent& event) {
+  ImageButton::OnMousePressed(event);
+  host_->PointerPressedOnButton(this, LauncherButtonHost::MOUSE, event);
+  return true;
+}
+
+void AlternateAppListButton::OnMouseReleased(const ui::MouseEvent& event) {
+  ImageButton::OnMouseReleased(event);
+  host_->PointerReleasedOnButton(this, LauncherButtonHost::MOUSE, false);
+}
+
+void AlternateAppListButton::OnMouseCaptureLost() {
+  host_->PointerReleasedOnButton(this, LauncherButtonHost::MOUSE, true);
+  ImageButton::OnMouseCaptureLost();
+}
+
+bool AlternateAppListButton::OnMouseDragged(const ui::MouseEvent& event) {
+  ImageButton::OnMouseDragged(event);
+  host_->PointerDraggedOnButton(this, LauncherButtonHost::MOUSE, event);
+  return true;
+}
+
+void AlternateAppListButton::OnMouseMoved(const ui::MouseEvent& event) {
+  ImageButton::OnMouseMoved(event);
+  host_->MouseMovedOverButton(this);
+}
+
+void AlternateAppListButton::OnMouseEntered(const ui::MouseEvent& event) {
+  ImageButton::OnMouseEntered(event);
+  host_->MouseEnteredButton(this);
+}
+
+void AlternateAppListButton::OnMouseExited(const ui::MouseEvent& event) {
+  ImageButton::OnMouseExited(event);
+  host_->MouseExitedButton(this);
+}
+
+void AlternateAppListButton::OnPaint(gfx::Canvas* canvas) {
+  // Call the base class first to paint any background/borders.
+  View::OnPaint(canvas);
+
+  int background_image_id = 0;
+  if (Shell::GetInstance()->GetAppListTargetVisibility()) {
+    background_image_id = IDR_AURA_NOTIFICATION_BACKGROUND_PRESSED;
+  } else {
+    if (shelf_widget_->GetDimsShelf())
+      background_image_id = IDR_AURA_NOTIFICATION_BACKGROUND_ON_BLACK;
+    else
+      background_image_id = IDR_AURA_NOTIFICATION_BACKGROUND_NORMAL;
+  }
+  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+  const gfx::ImageSkia* background_image =
+      rb.GetImageNamed(background_image_id).ToImageSkia();
+  const gfx::ImageSkia* forground_image =
+      rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST_ALTERNATE).ToImageSkia();
+
+  gfx::Rect contents_bounds = GetContentsBounds();
+  gfx::Rect background_bounds, forground_bounds;
+
+  ShelfAlignment alignment = shelf_widget_->GetAlignment();
+  background_bounds.set_size(background_image->size());
+  if (alignment == SHELF_ALIGNMENT_LEFT) {
+    background_bounds.set_x(contents_bounds.width() -
+        kImagePaddingFromShelf - background_image->width());
+    background_bounds.set_y(contents_bounds.y() +
+        (contents_bounds.height() - background_image->height()) / 2);
+  } else if(alignment == SHELF_ALIGNMENT_RIGHT) {
+    background_bounds.set_x(kImagePaddingFromShelf);
+    background_bounds.set_y(contents_bounds.y() +
+        (contents_bounds.height() - background_image->height()) / 2);
+  } else {
+    background_bounds.set_y(kImagePaddingFromShelf);
+    background_bounds.set_x(contents_bounds.x() +
+        (contents_bounds.width() - background_image->width()) / 2);
+  }
+
+  forground_bounds.set_size(forground_image->size());
+  forground_bounds.set_x(background_bounds.x() +
+      std::max(0,
+          (background_bounds.width() - forground_bounds.width()) / 2));
+  forground_bounds.set_y(background_bounds.y() +
+      std::max(0,
+          (background_bounds.height() - forground_bounds.height()) / 2));
+
+  canvas->DrawImageInt(*background_image,
+                       background_bounds.x(),
+                       background_bounds.y());
+  canvas->DrawImageInt(*forground_image,
+                       forground_bounds.x(),
+                       forground_bounds.y());
+
+  OnPaintFocusBorder(canvas);
+}
+
+void AlternateAppListButton::GetAccessibleState(
+    ui::AccessibleViewState* state) {
+  state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
+  state->name = host_->GetAccessibleName(this);
+}
+
+}  // namespace internal
+}  // namespace ash
diff --git a/ash/shelf/alternate_app_list_button.h b/ash/shelf/alternate_app_list_button.h
new file mode 100644
index 0000000..f38f91b
--- /dev/null
+++ b/ash/shelf/alternate_app_list_button.h
@@ -0,0 +1,56 @@
+// 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 ASH_SHELF_ALTERNATE_APP_LIST_BUTTON_H_
+#define ASH_SHELF_ALTERNATE_APP_LIST_BUTTON_H_
+
+#include "ui/views/controls/button/image_button.h"
+
+namespace ash {
+
+class ShelfWidget;
+
+namespace internal {
+
+class LauncherButtonHost;
+
+// Button used for the AppList icon on the launcher.
+// This class is an alternate implementation to
+// ash::internal::AppListButton for the purposes of testing an
+// alternate shelf layout (see ash_switches: UseAlternateShelfLayout).
+class AlternateAppListButton : public views::ImageButton {
+ public:
+  // Bounds size (inset) required for the app icon image (in pixels).
+  static const int kImageBoundsSize;
+
+  AlternateAppListButton(views::ButtonListener* listener,
+                         LauncherButtonHost* host,
+                         ShelfWidget* shelf_widget);
+  virtual ~AlternateAppListButton();
+
+ protected:
+  // views::ImageButton:
+  virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnMouseCaptureLost() OVERRIDE;
+  virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnMouseMoved(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+  virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
+
+ private:
+  LauncherButtonHost* host_;
+  // Reference to the shelf widget containing this button, owned by the
+  // root window controller.
+  ShelfWidget* shelf_widget_;
+
+  DISALLOW_COPY_AND_ASSIGN(AlternateAppListButton);
+};
+
+}  // namespace internal
+}  // namespace ash
+
+#endif  // ASH_SHELF_ALTERNATE_APP_LIST_BUTTON_H_
diff --git a/ash/shelf/app_list_button.cc b/ash/shelf/app_list_button.cc
new file mode 100644
index 0000000..ca47d06
--- /dev/null
+++ b/ash/shelf/app_list_button.cc
@@ -0,0 +1,134 @@
+// 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 "ash/shelf/app_list_button.h"
+
+#include <vector>
+
+#include "ash/launcher/launcher_button_host.h"
+#include "ash/launcher/launcher_types.h"
+#include "grit/ash_resources.h"
+#include "grit/ash_strings.h"
+#include "ui/base/accessibility/accessible_view_state.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_animation_element.h"
+#include "ui/compositor/layer_animation_sequence.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+
+namespace ash {
+namespace internal {
+
+namespace {
+
+const int kAnimationDurationInMs = 600;
+const float kAnimationOpacity[] = { 1.0f, 0.4f, 1.0f };
+const int kBorderSize = 9;
+}  // namespace
+
+AppListButton::AppListButton(views::ButtonListener* listener,
+                             LauncherButtonHost* host)
+    : views::ImageButton(listener),
+      host_(host) {
+  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+  SetImage(
+      views::CustomButton::STATE_NORMAL,
+      rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST).ToImageSkia());
+  SetImage(
+      views::CustomButton::STATE_HOVERED,
+      rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST_HOT).
+          ToImageSkia());
+  SetImage(
+      views::CustomButton::STATE_PRESSED,
+      rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST_PUSHED).
+          ToImageSkia());
+  SetAccessibleName(l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_TITLE));
+  SetSize(gfx::Size(kLauncherPreferredSize, kLauncherPreferredSize));
+  SetImageAlignment(ImageButton::ALIGN_CENTER, ImageButton::ALIGN_TOP);
+}
+
+AppListButton::~AppListButton() {
+}
+
+void AppListButton::StartLoadingAnimation() {
+  layer()->GetAnimator()->StopAnimating();
+
+  scoped_ptr<ui::LayerAnimationSequence> opacity_sequence(
+      new ui::LayerAnimationSequence());
+
+  opacity_sequence->set_is_cyclic(true);
+
+  for (size_t i = 0; i < arraysize(kAnimationOpacity); ++i) {
+    opacity_sequence->AddElement(
+        ui::LayerAnimationElement::CreateOpacityElement(
+            kAnimationOpacity[i],
+            base::TimeDelta::FromMilliseconds(kAnimationDurationInMs)));
+  }
+
+  ui::LayerAnimationElement::AnimatableProperties opacity_properties;
+  opacity_properties.insert(ui::LayerAnimationElement::OPACITY);
+  opacity_sequence->AddElement(
+      ui::LayerAnimationElement::CreatePauseElement(
+          opacity_properties,
+          base::TimeDelta::FromMilliseconds(kAnimationDurationInMs)));
+
+  // LayerAnimator takes ownership of the sequences.
+  layer()->GetAnimator()->ScheduleAnimation(opacity_sequence.release());
+}
+
+void AppListButton::StopLoadingAnimation() {
+  layer()->GetAnimator()->StopAnimating();
+
+  ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator());
+  settings.SetTransitionDuration(
+      base::TimeDelta::FromMilliseconds(kAnimationDurationInMs));
+  layer()->SetOpacity(1.0f);
+  layer()->SetTransform(gfx::Transform());
+}
+
+bool AppListButton::OnMousePressed(const ui::MouseEvent& event) {
+  ImageButton::OnMousePressed(event);
+  host_->PointerPressedOnButton(this, LauncherButtonHost::MOUSE, event);
+  return true;
+}
+
+void AppListButton::OnMouseReleased(const ui::MouseEvent& event) {
+  ImageButton::OnMouseReleased(event);
+  host_->PointerReleasedOnButton(this, LauncherButtonHost::MOUSE, false);
+}
+
+void AppListButton::OnMouseCaptureLost() {
+  host_->PointerReleasedOnButton(this, LauncherButtonHost::MOUSE, true);
+  ImageButton::OnMouseCaptureLost();
+}
+
+bool AppListButton::OnMouseDragged(const ui::MouseEvent& event) {
+  ImageButton::OnMouseDragged(event);
+  host_->PointerDraggedOnButton(this, LauncherButtonHost::MOUSE, event);
+  return true;
+}
+
+void AppListButton::OnMouseMoved(const ui::MouseEvent& event) {
+  ImageButton::OnMouseMoved(event);
+  host_->MouseMovedOverButton(this);
+}
+
+void AppListButton::OnMouseEntered(const ui::MouseEvent& event) {
+  ImageButton::OnMouseEntered(event);
+  host_->MouseEnteredButton(this);
+}
+
+void AppListButton::OnMouseExited(const ui::MouseEvent& event) {
+  ImageButton::OnMouseExited(event);
+  host_->MouseExitedButton(this);
+}
+
+void AppListButton::GetAccessibleState(ui::AccessibleViewState* state) {
+  state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
+  state->name = host_->GetAccessibleName(this);
+}
+
+}  // namespace internal
+}  // namespace ash
diff --git a/ash/shelf/app_list_button.h b/ash/shelf/app_list_button.h
new file mode 100644
index 0000000..a8c8e70
--- /dev/null
+++ b/ash/shelf/app_list_button.h
@@ -0,0 +1,45 @@
+// 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 ASH_SHELF_APP_LIST_BUTTON_H_
+#define ASH_SHELF_APP_LIST_BUTTON_H_
+
+#include "ui/views/controls/button/image_button.h"
+
+namespace ash {
+namespace internal {
+
+class LauncherButtonHost;
+
+// Button used for the AppList icon on the launcher.
+class AppListButton : public views::ImageButton {
+ public:
+  AppListButton(views::ButtonListener* listener,
+                LauncherButtonHost* host);
+  virtual ~AppListButton();
+
+  void StartLoadingAnimation();
+  void StopLoadingAnimation();
+
+ protected:
+  // views::ImageButton:
+  virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnMouseCaptureLost() OVERRIDE;
+  virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnMouseMoved(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE;
+  virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
+
+ private:
+  LauncherButtonHost* host_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppListButton);
+};
+
+}  // namespace internal
+}  // namespace ash
+
+#endif  // ASH_SHELF_APP_LIST_BUTTON_H_
diff --git a/ash/shelf/overflow_bubble.cc b/ash/shelf/overflow_bubble.cc
new file mode 100644
index 0000000..b2049dc
--- /dev/null
+++ b/ash/shelf/overflow_bubble.cc
@@ -0,0 +1,285 @@
+// 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 "ash/shelf/overflow_bubble.h"
+
+#include <algorithm>
+
+#include "ash/launcher/launcher_types.h"
+#include "ash/launcher/launcher_view.h"
+#include "ash/root_window_controller.h"
+#include "ash/shelf/shelf_layout_manager.h"
+#include "ash/shelf/shelf_widget.h"
+#include "ash/shell.h"
+#include "ash/system/tray/system_tray.h"
+#include "ui/aura/root_window.h"
+#include "ui/gfx/insets.h"
+#include "ui/gfx/screen.h"
+#include "ui/views/bubble/bubble_delegate.h"
+#include "ui/views/bubble/bubble_frame_view.h"
+#include "ui/views/widget/widget.h"
+
+namespace ash {
+namespace internal {
+
+namespace {
+
+// Max bubble size to screen size ratio.
+const float kMaxBubbleSizeToScreenRatio = 0.5f;
+
+// Inner padding in pixels for launcher view inside bubble.
+const int kPadding = 2;
+
+// Padding space in pixels between LauncherView's left/top edge to its contents.
+const int kLauncherViewLeadingInset = 8;
+
+////////////////////////////////////////////////////////////////////////////////
+// OverflowBubbleView
+// OverflowBubbleView hosts a LauncherView to display overflown items.
+
+class OverflowBubbleView : public views::BubbleDelegateView {
+ public:
+  OverflowBubbleView();
+  virtual ~OverflowBubbleView();
+
+  void InitOverflowBubble(views::View* anchor, LauncherView* launcher_view);
+
+ private:
+  bool IsHorizontalAlignment() const {
+    return GetShelfLayoutManagerForLauncher()->IsHorizontalAlignment();
+  }
+
+  const gfx::Size GetContentsSize() const {
+    return static_cast<views::View*>(launcher_view_)->GetPreferredSize();
+  }
+
+  // Gets arrow location based on shelf alignment.
+  views::BubbleBorder::Arrow GetBubbleArrow() const {
+    return GetShelfLayoutManagerForLauncher()->SelectValueForShelfAlignment(
+        views::BubbleBorder::BOTTOM_LEFT,
+        views::BubbleBorder::LEFT_TOP,
+        views::BubbleBorder::RIGHT_TOP,
+        views::BubbleBorder::TOP_LEFT);
+  }
+
+  void ScrollByXOffset(int x_offset);
+  void ScrollByYOffset(int y_offset);
+
+  // views::View overrides:
+  virtual gfx::Size GetPreferredSize() OVERRIDE;
+  virtual void Layout() OVERRIDE;
+  virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE;
+  virtual bool OnMouseWheel(const ui::MouseWheelEvent& event) OVERRIDE;
+
+  // ui::EventHandler overrides:
+  virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE;
+
+  // views::BubbleDelegate overrides:
+  virtual gfx::Rect GetBubbleBounds() OVERRIDE;
+
+  ShelfLayoutManager* GetShelfLayoutManagerForLauncher() const {
+    return ShelfLayoutManager::ForLauncher(
+        anchor_view()->GetWidget()->GetNativeView());
+  }
+
+  LauncherView* launcher_view_;  // Owned by views hierarchy.
+  gfx::Vector2d scroll_offset_;
+
+  DISALLOW_COPY_AND_ASSIGN(OverflowBubbleView);
+};
+
+OverflowBubbleView::OverflowBubbleView()
+    : launcher_view_(NULL) {
+}
+
+OverflowBubbleView::~OverflowBubbleView() {
+}
+
+void OverflowBubbleView::InitOverflowBubble(views::View* anchor,
+                                            LauncherView* launcher_view) {
+  // set_anchor_view needs to be called before GetShelfLayoutManagerForLauncher
+  // can be called.
+  set_anchor_view(anchor);
+  set_arrow(GetBubbleArrow());
+  set_background(NULL);
+  set_color(SkColorSetARGB(kLauncherBackgroundAlpha, 0, 0, 0));
+  set_margins(gfx::Insets(kPadding, kPadding, kPadding, kPadding));
+  set_move_with_anchor(true);
+
+  // Makes bubble view has a layer and clip its children layers.
+  SetPaintToLayer(true);
+  SetFillsBoundsOpaquely(false);
+  layer()->SetMasksToBounds(true);
+
+  launcher_view_ = launcher_view;
+  AddChildView(launcher_view_);
+
+  views::BubbleDelegateView::CreateBubble(this);
+}
+
+void OverflowBubbleView::ScrollByXOffset(int x_offset) {
+  const gfx::Rect visible_bounds(GetContentsBounds());
+  const gfx::Size contents_size(GetContentsSize());
+
+  int x = std::min(contents_size.width() - visible_bounds.width(),
+                   std::max(0, scroll_offset_.x() + x_offset));
+  scroll_offset_.set_x(x);
+}
+
+void OverflowBubbleView::ScrollByYOffset(int y_offset) {
+  const gfx::Rect visible_bounds(GetContentsBounds());
+  const gfx::Size contents_size(GetContentsSize());
+
+  int y = std::min(contents_size.height() - visible_bounds.height(),
+                   std::max(0, scroll_offset_.y() + y_offset));
+  scroll_offset_.set_y(y);
+}
+
+gfx::Size OverflowBubbleView::GetPreferredSize() {
+  gfx::Size preferred_size = GetContentsSize();
+
+  const gfx::Rect monitor_rect = Shell::GetScreen()->GetDisplayNearestPoint(
+      GetAnchorRect().CenterPoint()).work_area();
+  if (!monitor_rect.IsEmpty()) {
+    if (IsHorizontalAlignment()) {
+      preferred_size.set_width(std::min(
+          preferred_size.width(),
+          static_cast<int>(monitor_rect.width() *
+                           kMaxBubbleSizeToScreenRatio)));
+    } else {
+      preferred_size.set_height(std::min(
+          preferred_size.height(),
+          static_cast<int>(monitor_rect.height() *
+                           kMaxBubbleSizeToScreenRatio)));
+    }
+  }
+
+  return preferred_size;
+}
+
+void OverflowBubbleView::Layout() {
+  launcher_view_->SetBoundsRect(gfx::Rect(
+      gfx::PointAtOffsetFromOrigin(-scroll_offset_), GetContentsSize()));
+}
+
+void OverflowBubbleView::ChildPreferredSizeChanged(views::View* child) {
+  // Ensures |launch_view_| is still visible.
+  ScrollByXOffset(0);
+  ScrollByYOffset(0);
+  Layout();
+
+  SizeToContents();
+}
+
+bool OverflowBubbleView::OnMouseWheel(const ui::MouseWheelEvent& event) {
+  // The MouseWheelEvent was changed to support both X and Y offsets
+  // recently, but the behavior of this function was retained to continue
+  // using Y offsets only. Might be good to simply scroll in both
+  // directions as in OverflowBubbleView::OnScrollEvent.
+  if (IsHorizontalAlignment())
+    ScrollByXOffset(-event.y_offset());
+  else
+    ScrollByYOffset(-event.y_offset());
+  Layout();
+
+  return true;
+}
+
+void OverflowBubbleView::OnScrollEvent(ui::ScrollEvent* event) {
+  ScrollByXOffset(-event->x_offset());
+  ScrollByYOffset(-event->y_offset());
+  Layout();
+  event->SetHandled();
+}
+
+gfx::Rect OverflowBubbleView::GetBubbleBounds() {
+  views::BubbleBorder* border = GetBubbleFrameView()->bubble_border();
+  gfx::Insets bubble_insets = border->GetInsets();
+
+  const int border_size =
+      views::BubbleBorder::is_arrow_on_horizontal(arrow()) ?
+      bubble_insets.left() : bubble_insets.top();
+  const int arrow_offset = border_size + kPadding + kLauncherViewLeadingInset +
+      ShelfLayoutManager::GetPreferredShelfSize() / 2;
+
+  const gfx::Size content_size = GetPreferredSize();
+  border->set_arrow_offset(arrow_offset);
+
+  const gfx::Rect anchor_rect = GetAnchorRect();
+  gfx::Rect bubble_rect = GetBubbleFrameView()->GetUpdatedWindowBounds(
+      anchor_rect,
+      content_size,
+      false);
+
+  gfx::Rect monitor_rect = Shell::GetScreen()->GetDisplayNearestPoint(
+      anchor_rect.CenterPoint()).work_area();
+
+  int offset = 0;
+  if (views::BubbleBorder::is_arrow_on_horizontal(arrow())) {
+    if (bubble_rect.x() < monitor_rect.x())
+      offset = monitor_rect.x() - bubble_rect.x();
+    else if (bubble_rect.right() > monitor_rect.right())
+      offset = monitor_rect.right() - bubble_rect.right();
+
+    bubble_rect.Offset(offset, 0);
+    border->set_arrow_offset(anchor_rect.CenterPoint().x() - bubble_rect.x());
+  } else {
+    if (bubble_rect.y() < monitor_rect.y())
+      offset = monitor_rect.y() - bubble_rect.y();
+    else if (bubble_rect.bottom() > monitor_rect.bottom())
+      offset =  monitor_rect.bottom() - bubble_rect.bottom();
+
+    bubble_rect.Offset(0, offset);
+    border->set_arrow_offset(anchor_rect.CenterPoint().y() - bubble_rect.y());
+  }
+
+  GetBubbleFrameView()->SchedulePaint();
+  return bubble_rect;
+}
+
+}  // namespace
+
+OverflowBubble::OverflowBubble()
+    : bubble_(NULL),
+      launcher_view_(NULL) {
+}
+
+OverflowBubble::~OverflowBubble() {
+  Hide();
+}
+
+void OverflowBubble::Show(views::View* anchor, LauncherView* launcher_view) {
+  Hide();
+
+  OverflowBubbleView* bubble_view = new OverflowBubbleView();
+  bubble_view->InitOverflowBubble(anchor, launcher_view);
+  launcher_view_ = launcher_view;
+
+  bubble_ = bubble_view;
+  RootWindowController::ForWindow(anchor->GetWidget()->GetNativeView())->
+      GetSystemTray()->InitializeBubbleAnimations(bubble_->GetWidget());
+  bubble_->GetWidget()->AddObserver(this);
+  bubble_->GetWidget()->Show();
+}
+
+void OverflowBubble::Hide() {
+  if (!IsShowing())
+    return;
+
+  bubble_->GetWidget()->RemoveObserver(this);
+  bubble_->GetWidget()->Close();
+  bubble_ = NULL;
+  launcher_view_ = NULL;
+}
+
+void OverflowBubble::OnWidgetDestroying(views::Widget* widget) {
+  DCHECK(widget == bubble_->GetWidget());
+  bubble_ = NULL;
+  launcher_view_ = NULL;
+  ShelfLayoutManager::ForLauncher(
+      widget->GetNativeView())->shelf_widget()->launcher()->SchedulePaint();
+}
+
+}  // namespace internal
+}  // namespace ash
diff --git a/ash/shelf/overflow_bubble.h b/ash/shelf/overflow_bubble.h
new file mode 100644
index 0000000..cb0d5ba
--- /dev/null
+++ b/ash/shelf/overflow_bubble.h
@@ -0,0 +1,52 @@
+// 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 ASH_SHELF_OVERFLOW_BUBBLE_H_
+#define ASH_SHELF_OVERFLOW_BUBBLE_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "ui/views/widget/widget_observer.h"
+
+namespace views {
+class View;
+}
+
+namespace ash {
+
+class LauncherDelegate;
+class LauncherModel;
+
+namespace internal {
+
+class LauncherView;
+
+// OverflowBubble displays the overflown launcher items in a bubble.
+class OverflowBubble : public views::WidgetObserver {
+ public:
+  OverflowBubble();
+  virtual ~OverflowBubble();
+
+  // Shows an bubble pointing to |anchor| with |launcher_view| as its content.
+  void Show(views::View* anchor, LauncherView* launcher_view);
+
+  void Hide();
+
+  bool IsShowing() const { return !!bubble_; }
+  LauncherView* launcher_view() { return launcher_view_; }
+
+ private:
+  // Overridden from views::WidgetObserver:
+  virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE;
+
+  views::View* bubble_;  // Owned by views hierarchy.
+  LauncherView* launcher_view_;  // Owned by |bubble_|.
+
+  DISALLOW_COPY_AND_ASSIGN(OverflowBubble);
+};
+
+}  // namespace internal
+}  // namespace ash
+
+#endif  // ASH_SHELF_OVERFLOW_BUBBLE_H_
diff --git a/ash/shelf/overflow_button.cc b/ash/shelf/overflow_button.cc
new file mode 100644
index 0000000..586b088
--- /dev/null
+++ b/ash/shelf/overflow_button.cc
@@ -0,0 +1,178 @@
+// 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 "ash/shelf/overflow_button.h"
+
+#include "ash/ash_switches.h"
+#include "ash/shelf/shelf_layout_manager.h"
+#include "ash/shelf/shelf_widget.h"
+#include "grit/ash_resources.h"
+#include "grit/ash_strings.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/core/SkPath.h"
+#include "ui/base/animation/throb_animation.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/image_skia_operations.h"
+#include "ui/gfx/skbitmap_operations.h"
+#include "ui/gfx/skia_util.h"
+#include "ui/gfx/transform.h"
+#include "ui/views/widget/widget.h"
+
+namespace ash {
+namespace internal {
+
+namespace {
+
+const int kButtonHoverAlpha = 150;
+
+const int kButtonCornerRadius = 2;
+
+const int kButtonHoverSize = 28;
+
+const int kBackgroundOffset = (48 - kButtonHoverSize) / 2;
+
+// Padding from the inner edge of the shelf (towards center of display) to
+// the edge of the background image of the overflow button.
+const int kImagePaddingFromShelf = 5;
+
+}  // namesapce
+
+OverflowButton::OverflowButton(views::ButtonListener* listener)
+    : CustomButton(listener),
+      bottom_image_(NULL) {
+  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+  bottom_image_ = rb.GetImageNamed(IDR_AURA_LAUNCHER_OVERFLOW).ToImageSkia();
+
+
+  set_accessibility_focusable(true);
+  SetAccessibleName(l10n_util::GetStringUTF16(IDS_ASH_SHELF_OVERFLOW_NAME));
+}
+
+OverflowButton::~OverflowButton() {}
+
+void OverflowButton::OnShelfAlignmentChanged() {
+  SchedulePaint();
+}
+
+void OverflowButton::PaintBackground(gfx::Canvas* canvas, int alpha) {
+  gfx::Rect bounds(GetContentsBounds());
+  gfx::Rect rect(0, 0, kButtonHoverSize, kButtonHoverSize);
+  ShelfLayoutManager* shelf =
+      ShelfLayoutManager::ForLauncher(GetWidget()->GetNativeView());
+
+  // Nudge the background a little to line up right.
+  if (shelf->IsHorizontalAlignment()) {
+    rect.set_origin(gfx::Point(
+        bounds.x() + ((bounds.width() - kButtonHoverSize) / 2) - 1,
+        bounds.y() + kBackgroundOffset - 1));
+
+  } else {
+    rect.set_origin(gfx::Point(
+        bounds.x() + kBackgroundOffset - 1,
+        bounds.y() + ((bounds.height() - kButtonHoverSize) / 2) - 1));
+  }
+
+  SkPaint paint;
+  paint.setAntiAlias(true);
+  paint.setStyle(SkPaint::kFill_Style);
+  paint.setColor(SkColorSetARGB(
+      kButtonHoverAlpha * hover_animation_->GetCurrentValue(),
+      0, 0, 0));
+
+  const SkScalar radius = SkIntToScalar(kButtonCornerRadius);
+  SkPath path;
+  path.addRoundRect(gfx::RectToSkRect(rect), radius, radius);
+  canvas->DrawPath(path, paint);
+}
+
+void OverflowButton::OnPaint(gfx::Canvas* canvas) {
+  ShelfLayoutManager* layout_manager = ShelfLayoutManager::ForLauncher(
+      GetWidget()->GetNativeView());
+  ShelfAlignment alignment = layout_manager->GetAlignment();
+
+  gfx::Rect bounds(GetContentsBounds());
+  if (ash::switches::UseAlternateShelfLayout()) {
+    ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+    int background_image_id = 0;
+    if (layout_manager->shelf_widget()->launcher()->IsShowingOverflowBubble())
+      background_image_id = IDR_AURA_NOTIFICATION_BACKGROUND_PRESSED;
+    else if(layout_manager->shelf_widget()->GetDimsShelf())
+      background_image_id = IDR_AURA_NOTIFICATION_BACKGROUND_ON_BLACK;
+    else
+      background_image_id = IDR_AURA_NOTIFICATION_BACKGROUND_NORMAL;
+
+    const gfx::ImageSkia* background =
+        rb.GetImageNamed(background_image_id).ToImageSkia();
+    if (alignment == SHELF_ALIGNMENT_LEFT) {
+      bounds = gfx::Rect(
+          bounds.right() - background->width() - kImagePaddingFromShelf,
+          bounds.y() + (bounds.height() - background->height()) / 2,
+          background->width(), background->height());
+    } else if (alignment == SHELF_ALIGNMENT_RIGHT) {
+      bounds = gfx::Rect(
+          bounds.x() + kImagePaddingFromShelf,
+          bounds.y() + (bounds.height() - background->height()) / 2,
+          background->width(), background->height());
+    } else {
+      bounds = gfx::Rect(
+          bounds.x() + (bounds.width() - background->width()) / 2,
+          bounds.y() + kImagePaddingFromShelf,
+          background->width(), background->height());
+    }
+    canvas->DrawImageInt(*background, bounds.x(), bounds.y());
+  } else {
+    if (alignment == SHELF_ALIGNMENT_BOTTOM) {
+      bounds = gfx::Rect(
+          bounds.x() + ((bounds.width() - kButtonHoverSize) / 2) - 1,
+          bounds.y() + kBackgroundOffset - 1,
+          kButtonHoverSize, kButtonHoverSize);
+    } else {
+      bounds = gfx::Rect(
+          bounds.x() + kBackgroundOffset -1,
+          bounds.y() + ((bounds.height() - kButtonHoverSize) / 2) -1,
+          kButtonHoverSize, kButtonHoverSize);
+    }
+    if (hover_animation_->is_animating()) {
+      PaintBackground(
+          canvas,
+          kButtonHoverAlpha * hover_animation_->GetCurrentValue());
+    } else if (state() == STATE_HOVERED || state() == STATE_PRESSED) {
+      PaintBackground(canvas, kButtonHoverAlpha);
+    }
+  }
+
+  if (height() < kButtonHoverSize)
+    return;
+
+  const gfx::ImageSkia* image = NULL;
+
+  switch(alignment) {
+    case SHELF_ALIGNMENT_LEFT:
+      if (left_image_.isNull()) {
+        left_image_ = gfx::ImageSkiaOperations::CreateRotatedImage(
+            *bottom_image_, SkBitmapOperations::ROTATION_90_CW);
+      }
+      image = &left_image_;
+      break;
+    case SHELF_ALIGNMENT_RIGHT:
+      if (right_image_.isNull()) {
+        right_image_ = gfx::ImageSkiaOperations::CreateRotatedImage(
+            *bottom_image_, SkBitmapOperations::ROTATION_270_CW);
+      }
+      image = &right_image_;
+      break;
+    default:
+      image = bottom_image_;
+      break;
+  }
+
+  canvas->DrawImageInt(*image,
+                       bounds.x() + ((bounds.width() - image->width()) / 2),
+                       bounds.y() + ((bounds.height() - image->height()) / 2));
+}
+
+}  // namespace internal
+}  // namespace ash
diff --git a/ash/shelf/overflow_button.h b/ash/shelf/overflow_button.h
new file mode 100644
index 0000000..7b6e400
--- /dev/null
+++ b/ash/shelf/overflow_button.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 ASH_SHELF_OVERFLOW_BUTTON_H_
+#define ASH_SHELF_OVERFLOW_BUTTON_H_
+
+#include "ash/shelf/shelf_types.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/views/controls/button/custom_button.h"
+
+namespace ash {
+namespace internal {
+
+// Launcher overflow chevron button.
+class OverflowButton : public views::CustomButton {
+ public:
+  explicit OverflowButton(views::ButtonListener* listener);
+  virtual ~OverflowButton();
+
+  void OnShelfAlignmentChanged();
+
+ private:
+  void PaintBackground(gfx::Canvas* canvas, int alpha);
+
+  // views::View overrides:
+  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+
+  // Left and right images are rotations of bottom_image and are
+  // owned by the overflow button.
+  gfx::ImageSkia left_image_;
+  gfx::ImageSkia right_image_;
+  // Bottom image is owned by the resource bundle.
+  const gfx::ImageSkia* bottom_image_;
+
+  DISALLOW_COPY_AND_ASSIGN(OverflowButton);
+};
+
+}  // namespace internal
+}  // namespace ash
+
+#endif  // ASH_SHELF_OVERFLOW_BUTTON_H_
diff --git a/ash/shelf/scoped_observer_with_duplicated_sources.h b/ash/shelf/scoped_observer_with_duplicated_sources.h
new file mode 100644
index 0000000..c326712
--- /dev/null
+++ b/ash/shelf/scoped_observer_with_duplicated_sources.h
@@ -0,0 +1,70 @@
+// 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 ASH_SHELF_SCOPED_OBSERVER_WITH_DUPLICATED_SOURCES_H_
+#define ASH_SHELF_SCOPED_OBSERVER_WITH_DUPLICATED_SOURCES_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+// ScopedObserverWithDuplicatedSources is used to keep track of the set of
+// sources an object has attached itself to as an observer. When
+// ScopedObserverWithDuplicatedSources is destroyed it removes the object as an
+// observer from all sources it has been added to.
+// ScopedObserverWithDuplicatedSources adds |observer| once for a particular
+// source, no matter how many times Add() is invoked. Additionaly |observer| is
+// only removed once Remove() is invoked the same number of times as Add().
+
+template <class Source, class Observer>
+class ScopedObserverWithDuplicatedSources {
+ public:
+  explicit ScopedObserverWithDuplicatedSources(Observer* observer)
+      : observer_(observer) {}
+
+  ~ScopedObserverWithDuplicatedSources() {
+    typename SourceToAddCountMap::const_iterator iter =
+        counted_sources_.begin();
+    for (; iter != counted_sources_.end(); ++iter)
+      iter->first->RemoveObserver(observer_);
+  }
+
+  // Adds the object passed to the constructor only once as an observer on
+  // |source|.
+  void Add(Source* source) {
+    if (counted_sources_.find(source) == counted_sources_.end())
+      source->AddObserver(observer_);
+    counted_sources_[source]++;
+  }
+
+  // Only remove the object passed to the constructor as an observer from
+  // |source| when Remove() is invoked the same number of times as Add().
+  void Remove(Source* source) {
+    typename SourceToAddCountMap::iterator iter =
+        counted_sources_.find(source);
+    DCHECK(iter != counted_sources_.end() && iter->second > 0);
+
+    if (--iter->second == 0) {
+      counted_sources_.erase(source);
+      source->RemoveObserver(observer_);
+    }
+  }
+
+  bool IsObserving(Source* source) const {
+    return counted_sources_.find(source) != counted_sources_.end();
+  }
+
+ private:
+  typedef std::map<Source*, int> SourceToAddCountMap;
+
+  Observer* observer_;
+
+  // Map Source and its adding count.
+  std::map<Source*, int> counted_sources_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedObserverWithDuplicatedSources);
+};
+
+#endif  // ASH_SHELF_SCOPED_OBSERVER_WITH_DUPLICATED_SOURCES_H_
diff --git a/ash/shelf/scoped_observer_with_duplicated_sources_unittest.cc b/ash/shelf/scoped_observer_with_duplicated_sources_unittest.cc
new file mode 100644
index 0000000..82d2b83
--- /dev/null
+++ b/ash/shelf/scoped_observer_with_duplicated_sources_unittest.cc
@@ -0,0 +1,82 @@
+// 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 "ash/shelf/scoped_observer_with_duplicated_sources.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class TestObserver {
+ public:
+  TestObserver() {}
+  ~TestObserver() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestObserver);
+};
+
+class TestSource {
+ public:
+  TestSource() : observer_count_(0) {}
+  ~TestSource() {}
+
+  void AddObserver(TestObserver* observer) {
+    observer_count_++;
+  }
+  void RemoveObserver(TestObserver* observer) {
+    observer_count_--;
+  }
+
+  int GetObserverCount() {
+    return observer_count_;
+  }
+
+ private:
+  int observer_count_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestSource);
+};
+
+TEST(ScopedObserverWithDuplicatedSourcesTest, DuplicatedSource) {
+  TestObserver observer;
+  TestSource source1;
+  TestSource source2;
+
+  ScopedObserverWithDuplicatedSources<TestSource, TestObserver>
+      observers(&observer);
+  EXPECT_EQ(0, source1.GetObserverCount());
+  EXPECT_FALSE(observers.IsObserving(&source1));
+  EXPECT_EQ(0, source2.GetObserverCount());
+  EXPECT_FALSE(observers.IsObserving(&source2));
+
+  // Add |source1|.
+  observers.Add(&source1);
+  EXPECT_EQ(1, source1.GetObserverCount());
+  EXPECT_TRUE(observers.IsObserving(&source1));
+  // AddObserver of TestSource is called only once.
+  observers.Add(&source1);
+  EXPECT_EQ(1, source1.GetObserverCount());
+  EXPECT_TRUE(observers.IsObserving(&source1));
+
+  // Add |source2|.
+  observers.Add(&source2);
+  EXPECT_EQ(1, source2.GetObserverCount());
+  EXPECT_TRUE(observers.IsObserving(&source2));
+
+  // Remove |source1|.
+  observers.Remove(&source1);
+  EXPECT_EQ(1, source1.GetObserverCount());
+  EXPECT_TRUE(observers.IsObserving(&source1));
+
+  // Remove |source2|.
+  observers.Remove(&source2);
+  EXPECT_EQ(0, source2.GetObserverCount());
+  EXPECT_FALSE(observers.IsObserving(&source2));
+
+  // Remove |source1| again.
+  observers.Remove(&source1);
+  // In this time, |observer| is removed from |source1|.
+  EXPECT_EQ(0, source1.GetObserverCount());
+  EXPECT_FALSE(observers.IsObserving(&source1));
+}
diff --git a/ash/shell.cc b/ash/shell.cc
index c32371f..96a8921 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -23,7 +23,10 @@
 #include "ash/focus_cycler.h"
 #include "ash/high_contrast/high_contrast_controller.h"
 #include "ash/host/root_window_host_factory.h"
+#include "ash/launcher/app_list_launcher_item_delegate.h"
 #include "ash/launcher/launcher_delegate.h"
+#include "ash/launcher/launcher_item_delegate.h"
+#include "ash/launcher/launcher_item_delegate_manager.h"
 #include "ash/launcher/launcher_model.h"
 #include "ash/magnifier/magnification_controller.h"
 #include "ash/magnifier/partial_magnification_controller.h"
@@ -865,9 +868,13 @@
 
 LauncherDelegate* Shell::GetLauncherDelegate() {
   if (!launcher_delegate_) {
+    // Creates LauncherItemDelegateManager before LauncherDelegate.
+    launcher_item_delegate_manager_.reset(new LauncherItemDelegateManager);
     launcher_model_.reset(new LauncherModel);
     launcher_delegate_.reset(
         delegate_->CreateLauncherDelegate(launcher_model_.get()));
+    app_list_launcher_item_delegate_.reset(
+        new internal::AppListLauncherItemDelegate);
   }
   return launcher_delegate_.get();
 }
diff --git a/ash/shell.h b/ash/shell.h
index 5068815..718d1f8 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -75,6 +75,7 @@
 class HighContrastController;
 class Launcher;
 class LauncherDelegate;
+class LauncherItemDelegateManager;
 class LauncherModel;
 class MagnificationController;
 class MruWindowTracker;
@@ -101,6 +102,7 @@
 class AcceleratorFilter;
 class ActivationController;
 class AppListController;
+class AppListLauncherItemDelegate;
 class CaptureController;
 class DisplayChangeObserver;
 class DisplayErrorObserver;
@@ -366,6 +368,10 @@
     return activation_client_;
   }
 
+  LauncherItemDelegateManager* launcher_item_delegate_manager() {
+    return launcher_item_delegate_manager_.get();
+  }
+
   ScreenAsh* screen() { return screen_; }
 
   // Force the shelf to query for it's current visibility state.
@@ -541,6 +547,9 @@
   scoped_ptr<CapsLockDelegate> caps_lock_delegate_;
   scoped_ptr<SessionStateDelegate> session_state_delegate_;
   scoped_ptr<LauncherDelegate> launcher_delegate_;
+  scoped_ptr<LauncherItemDelegateManager> launcher_item_delegate_manager_;
+  scoped_ptr<internal::AppListLauncherItemDelegate>
+      app_list_launcher_item_delegate_;
 
   scoped_ptr<LauncherModel> launcher_model_;
 
diff --git a/ash/shell/launcher_delegate_impl.cc b/ash/shell/launcher_delegate_impl.cc
index ac0aeee..1e14db8 100644
--- a/ash/shell/launcher_delegate_impl.cc
+++ b/ash/shell/launcher_delegate_impl.cc
@@ -4,10 +4,13 @@
 
 #include "ash/shell/launcher_delegate_impl.h"
 
+#include "ash/launcher/launcher_item_delegate_manager.h"
 #include "ash/launcher/launcher_util.h"
+#include "ash/shell.h"
 #include "ash/shell/toplevel_window.h"
 #include "ash/shell/window_watcher.h"
 #include "ash/wm/window_util.h"
+#include "base/strings/string_util.h"
 #include "grit/ash_resources.h"
 #include "ui/aura/window.h"
 
@@ -16,6 +19,10 @@
 
 LauncherDelegateImpl::LauncherDelegateImpl(WindowWatcher* watcher)
     : watcher_(watcher) {
+  ash::LauncherItemDelegateManager* manager =
+      ash::Shell::GetInstance()->launcher_item_delegate_manager();
+  manager->RegisterLauncherItemDelegate(ash::TYPE_APP_PANEL, this);
+  manager->RegisterLauncherItemDelegate(ash::TYPE_PLATFORM_APP, this);
 }
 
 LauncherDelegateImpl::~LauncherDelegateImpl() {
@@ -69,6 +76,10 @@
   return 0;
 }
 
+const std::string& LauncherDelegateImpl::GetAppIDForLauncherID(LauncherID id) {
+  return EmptyString();
+}
+
 void LauncherDelegateImpl::PinAppWithID(const std::string& app_id) {
 }
 
@@ -76,7 +87,7 @@
   return false;
 }
 
-void LauncherDelegateImpl::UnpinAppsWithID(const std::string& app_id) {
+void LauncherDelegateImpl::UnpinAppWithID(const std::string& app_id) {
 }
 
 }  // namespace shell
diff --git a/ash/shell/launcher_delegate_impl.h b/ash/shell/launcher_delegate_impl.h
index 86a3579..d6945d7 100644
--- a/ash/shell/launcher_delegate_impl.h
+++ b/ash/shell/launcher_delegate_impl.h
@@ -6,6 +6,7 @@
 #define ASH_SHELL_LAUNCHER_DELEGATE_IMPL_H_
 
 #include "ash/launcher/launcher_delegate.h"
+#include "ash/launcher/launcher_item_delegate.h"
 #include "base/compiler_specific.h"
 
 namespace aura {
@@ -17,7 +18,8 @@
 
 class WindowWatcher;
 
-class LauncherDelegateImpl : public ash::LauncherDelegate {
+class LauncherDelegateImpl : public ash::LauncherDelegate,
+                             public ash::LauncherItemDelegate {
  public:
   explicit LauncherDelegateImpl(WindowWatcher* watcher);
   virtual ~LauncherDelegateImpl();
@@ -25,6 +27,16 @@
   void set_watcher(WindowWatcher* watcher) { watcher_ = watcher; }
 
   // LauncherDelegate overrides:
+  virtual ash::LauncherID GetIDByWindow(aura::Window* window) OVERRIDE;
+  virtual void OnLauncherCreated(Launcher* launcher) OVERRIDE;
+  virtual void OnLauncherDestroyed(Launcher* launcher) OVERRIDE;
+  virtual LauncherID GetLauncherIDForAppID(const std::string& app_id) OVERRIDE;
+  virtual const std::string& GetAppIDForLauncherID(LauncherID id) OVERRIDE;
+  virtual void PinAppWithID(const std::string& app_id) OVERRIDE;
+  virtual bool IsAppPinned(const std::string& app_id) OVERRIDE;
+  virtual void UnpinAppWithID(const std::string& app_id) OVERRIDE;
+
+  // LauncherItemDelegate overrides:
   virtual void ItemSelected(const ash::LauncherItem& item,
                            const ui::Event& event) OVERRIDE;
   virtual base::string16 GetTitle(const ash::LauncherItem& item) OVERRIDE;
@@ -34,15 +46,8 @@
   virtual ash::LauncherMenuModel* CreateApplicationMenu(
       const ash::LauncherItem&,
       int event_flags) OVERRIDE;
-  virtual ash::LauncherID GetIDByWindow(aura::Window* window) OVERRIDE;
   virtual bool IsDraggable(const ash::LauncherItem& item) OVERRIDE;
   virtual bool ShouldShowTooltip(const LauncherItem& item) OVERRIDE;
-  virtual void OnLauncherCreated(Launcher* launcher) OVERRIDE;
-  virtual void OnLauncherDestroyed(Launcher* launcher) OVERRIDE;
-  virtual LauncherID GetLauncherIDForAppID(const std::string& app_id) OVERRIDE;
-  virtual void PinAppWithID(const std::string& app_id) OVERRIDE;
-  virtual bool IsAppPinned(const std::string& app_id) OVERRIDE;
-  virtual void UnpinAppsWithID(const std::string& app_id) OVERRIDE;
 
  private:
   // Used to update Launcher. Owned by main.
diff --git a/ash/shell/window_type_launcher.cc b/ash/shell/window_type_launcher.cc
index 799c34c..949b3ae 100644
--- a/ash/shell/window_type_launcher.cc
+++ b/ash/shell/window_type_launcher.cc
@@ -345,7 +345,7 @@
         ASCIIToUTF16("Notification message body."),
         gfx::Image(),
         ASCIIToUTF16("www.testshell.org"),
-        "" /* extension id */,
+        message_center::NotifierId(),
         message_center::RichNotificationData(),
         NULL /* delegate */));
 
diff --git a/ash/shell/window_watcher.cc b/ash/shell/window_watcher.cc
index 0963eb7..4dfcea4 100644
--- a/ash/shell/window_watcher.cc
+++ b/ash/shell/window_watcher.cc
@@ -107,7 +107,7 @@
   ash::LauncherModel* model = Shell::GetInstance()->launcher_model();
   ash::LauncherItem item;
   item.type = new_window->type() == aura::client::WINDOW_TYPE_PANEL ?
-                                    ash::TYPE_APP_PANEL : ash::TYPE_TABBED;
+      ash::TYPE_APP_PANEL : ash::TYPE_PLATFORM_APP;
   id_to_window_[model->next_id()] = new_window;
 
   SkBitmap icon_bitmap;
diff --git a/ash/system/chromeos/managed/tray_locally_managed_user.cc b/ash/system/chromeos/managed/tray_locally_managed_user.cc
index e42ea6f..4038b32 100644
--- a/ash/system/chromeos/managed/tray_locally_managed_user.cc
+++ b/ash/system/chromeos/managed/tray_locally_managed_user.cc
@@ -5,6 +5,7 @@
 #include "ash/system/chromeos/managed/tray_locally_managed_user.h"
 
 #include "ash/system/chromeos/label_tray_view.h"
+#include "ash/system/system_notifier.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/tray/tray_notification_view.h"
 #include "ash/system/user/login_status.h"
@@ -82,7 +83,7 @@
       base::string16() /* body is empty */,
       bundle.GetImageNamed(IDR_AURA_UBER_TRAY_MANAGED_USER),
       base::string16() /* display_source */,
-      std::string() /* extension_id */,
+      message_center::NotifierId(NOTIFIER_LOCALLY_MANAGED_USER),
       message_center::RichNotificationData(),
       NULL /* no delegate */));
   notification->SetSystemPriority();
diff --git a/ash/system/chromeos/network/network_connect.cc b/ash/system/chromeos/network/network_connect.cc
index 534b706..0e53569 100644
--- a/ash/system/chromeos/network/network_connect.cc
+++ b/ash/system/chromeos/network/network_connect.cc
@@ -6,6 +6,7 @@
 
 #include "ash/shell.h"
 #include "ash/system/chromeos/network/network_state_notifier.h"
+#include "ash/system/system_notifier.h"
 #include "ash/system/tray/system_tray_delegate.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "base/bind.h"
@@ -14,6 +15,7 @@
 #include "base/values.h"
 #include "chromeos/login/login_state.h"
 #include "chromeos/network/device_state.h"
+#include "chromeos/network/network_activation_handler.h"
 #include "chromeos/network/network_configuration_handler.h"
 #include "chromeos/network/network_connection_handler.h"
 #include "chromeos/network/network_event_log.h"
@@ -357,7 +359,7 @@
     return;
   }
 
-  NetworkHandler::Get()->network_connection_handler()->ActivateNetwork(
+  NetworkHandler::Get()->network_activation_handler()->Activate(
       service_path,
       "",  // carrier
       base::Bind(&OnActivateSucceeded, service_path),
@@ -382,6 +384,7 @@
                                        UTF8ToUTF16(cellular->name())),
             ui::ResourceBundle::GetSharedInstance().GetImageNamed(
                 IDR_AURA_UBER_TRAY_CELLULAR_NETWORK_FAILED),
+            ash::NOTIFIER_NETWORK,
             base::Bind(&ash::network_connect::ShowNetworkSettings,
                        service_path)));
     return;
diff --git a/ash/system/chromeos/network/network_icon_animation.cc b/ash/system/chromeos/network/network_icon_animation.cc
index 1169c67..2a6428c 100644
--- a/ash/system/chromeos/network/network_icon_animation.cc
+++ b/ash/system/chromeos/network/network_icon_animation.cc
@@ -45,7 +45,7 @@
 
 void NetworkIconAnimation::RemoveObserver(AnimationObserver* observer) {
   observers_.RemoveObserver(observer);
-  if (observers_.size() == 0)
+  if (!observers_.might_have_observers())
     animation_.Reset();  // Stops the animation and resets the current value.
 }
 
diff --git a/ash/system/chromeos/network/network_state_notifier.cc b/ash/system/chromeos/network/network_state_notifier.cc
index dbd8702..1baa151 100644
--- a/ash/system/chromeos/network/network_state_notifier.cc
+++ b/ash/system/chromeos/network/network_state_notifier.cc
@@ -6,6 +6,7 @@
 
 #include "ash/shell.h"
 #include "ash/system/chromeos/network/network_connect.h"
+#include "ash/system/system_notifier.h"
 #include "ash/system/tray/system_tray_delegate.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_util.h"
@@ -60,7 +61,12 @@
       ui::ResourceBundle::GetSharedInstance().GetImageNamed(icon_id);
   message_center::MessageCenter::Get()->AddNotification(
       message_center::Notification::CreateSystemNotification(
-          notification_id, title, message, icon, callback));
+          notification_id,
+          title,
+          message,
+          icon,
+          ash::NOTIFIER_NETWORK_ERROR,
+          callback));
 }
 
 void ConfigureNetwork(const std::string& service_path) {
@@ -182,6 +188,7 @@
           l10n_util::GetStringFUTF16(IDS_NETWORK_CELLULAR_ACTIVATED,
                                      UTF8ToUTF16((cellular->name()))),
           icon,
+          NOTIFIER_NETWORK,
           base::Bind(&ash::network_connect::ShowNetworkSettings,
                      cellular->path())));
 }
diff --git a/ash/system/chromeos/network/tray_vpn.cc b/ash/system/chromeos/network/tray_vpn.cc
index 31c6d6a..992029c 100644
--- a/ash/system/chromeos/network/tray_vpn.cc
+++ b/ash/system/chromeos/network/tray_vpn.cc
@@ -9,7 +9,6 @@
 #include "ash/system/chromeos/network/network_state_list_detailed_view.h"
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_delegate.h"
-#include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_item_more.h"
 #include "ash/system/tray/tray_popup_label_button.h"
@@ -138,10 +137,6 @@
   return detailed_;
 }
 
-views::View* TrayVPN::CreateNotificationView(user::LoginStatus status) {
-  return NULL;
-}
-
 void TrayVPN::DestroyTrayView() {
 }
 
@@ -153,9 +148,6 @@
   detailed_ = NULL;
 }
 
-void TrayVPN::DestroyNotificationView() {
-}
-
 void TrayVPN::UpdateAfterLoginStatusChange(user::LoginStatus status) {
 }
 
diff --git a/ash/system/chromeos/network/tray_vpn.h b/ash/system/chromeos/network/tray_vpn.h
index ece975b..1d16e54 100644
--- a/ash/system/chromeos/network/tray_vpn.h
+++ b/ash/system/chromeos/network/tray_vpn.h
@@ -30,12 +30,9 @@
   virtual views::View* CreateTrayView(user::LoginStatus status) OVERRIDE;
   virtual views::View* CreateDefaultView(user::LoginStatus status) OVERRIDE;
   virtual views::View* CreateDetailedView(user::LoginStatus status) OVERRIDE;
-  virtual views::View* CreateNotificationView(
-      user::LoginStatus status) OVERRIDE;
   virtual void DestroyTrayView() OVERRIDE;
   virtual void DestroyDefaultView() OVERRIDE;
   virtual void DestroyDetailedView() OVERRIDE;
-  virtual void DestroyNotificationView() OVERRIDE;
   virtual void UpdateAfterLoginStatusChange(user::LoginStatus status) OVERRIDE;
   virtual void UpdateAfterShelfAlignmentChange(
       ShelfAlignment alignment) OVERRIDE;
diff --git a/ash/system/chromeos/power/tray_power.cc b/ash/system/chromeos/power/tray_power.cc
index cdf5300..0125d1e 100644
--- a/ash/system/chromeos/power/tray_power.cc
+++ b/ash/system/chromeos/power/tray_power.cc
@@ -7,6 +7,7 @@
 #include "ash/ash_switches.h"
 #include "ash/system/chromeos/power/power_status_view.h"
 #include "ash/system/date/date_view.h"
+#include "ash/system/system_notifier.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_notification_view.h"
 #include "ash/system/tray/tray_utils.h"
@@ -207,7 +208,7 @@
         rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_MESSAGE),
         rb.GetImageNamed(IDR_AURA_NOTIFICATION_LOW_POWER_CHARGER),
         base::string16(),
-        std::string(),
+        message_center::NotifierId(NOTIFIER_POWER),
         message_center::RichNotificationData(),
         NULL));
     message_center_->AddNotification(notification.Pass());
diff --git a/ash/system/chromeos/screen_security/screen_capture_tray_item.cc b/ash/system/chromeos/screen_security/screen_capture_tray_item.cc
index 6563e4b..6d67f5f 100644
--- a/ash/system/chromeos/screen_security/screen_capture_tray_item.cc
+++ b/ash/system/chromeos/screen_security/screen_capture_tray_item.cc
@@ -5,6 +5,7 @@
 #include "ash/system/chromeos/screen_security/screen_capture_tray_item.h"
 
 #include "ash/shell.h"
+#include "ash/system/system_notifier.h"
 #include "grit/ash_resources.h"
 #include "grit/ash_strings.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -62,7 +63,7 @@
       base::string16() /* body is blank */,
       resource_bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY),
       base::string16() /* display_source */,
-      std::string() /* extension_id */,
+      message_center::NotifierId(NOTIFIER_SCREEN_CAPTURE),
       data,
       new tray::ScreenNotificationDelegate(this)));
   notification->SetSystemPriority();
diff --git a/ash/system/chromeos/screen_security/screen_share_tray_item.cc b/ash/system/chromeos/screen_security/screen_share_tray_item.cc
index 18dd317..78d99f1 100644
--- a/ash/system/chromeos/screen_security/screen_share_tray_item.cc
+++ b/ash/system/chromeos/screen_security/screen_share_tray_item.cc
@@ -5,6 +5,7 @@
 #include "ash/system/chromeos/screen_security/screen_share_tray_item.h"
 
 #include "ash/shell.h"
+#include "ash/system/system_notifier.h"
 #include "grit/ash_resources.h"
 #include "grit/ash_strings.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -72,7 +73,7 @@
       base::string16() /* body is blank */,
       resource_bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY),
       base::string16() /* display_source */,
-      std::string() /* extension_id */,
+      message_center::NotifierId(NOTIFIER_SCREEN_SHARE),
       data,
       new tray::ScreenNotificationDelegate(this)));
   notification->SetSystemPriority();
diff --git a/ash/system/chromeos/tray_display.cc b/ash/system/chromeos/tray_display.cc
index 0231c5d..5a23927 100644
--- a/ash/system/chromeos/tray_display.cc
+++ b/ash/system/chromeos/tray_display.cc
@@ -7,6 +7,7 @@
 #include "ash/display/display_controller.h"
 #include "ash/display/display_manager.h"
 #include "ash/shell.h"
+#include "ash/system/system_notifier.h"
 #include "ash/system/tray/actionable_view.h"
 #include "ash/system/tray/fixed_sized_image_view.h"
 #include "ash/system/tray/system_tray.h"
@@ -174,7 +175,9 @@
   return base::string16();
 }
 
-void OpenSettings(user::LoginStatus login_status) {
+void OpenSettings() {
+  user::LoginStatus login_status =
+      Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus();
   if (login_status == ash::user::LOGGED_IN_USER ||
       login_status == ash::user::LOGGED_IN_OWNER ||
       login_status == ash::user::LOGGED_IN_GUEST) {
@@ -188,8 +191,7 @@
 
 class DisplayView : public ash::internal::ActionableView {
  public:
-  explicit DisplayView(user::LoginStatus login_status)
-      : login_status_(login_status) {
+  explicit DisplayView() {
     SetLayoutManager(new views::BoxLayout(
         views::BoxLayout::kHorizontal,
         ash::kTrayPopupPaddingHorizontal, 0,
@@ -245,7 +247,7 @@
 
   // Overridden from ActionableView.
   virtual bool PerformAction(const ui::Event& event) OVERRIDE {
-    OpenSettings(login_status_);
+    OpenSettings();
     return true;
   }
 
@@ -256,49 +258,12 @@
     PreferredSizeChanged();
   }
 
-  user::LoginStatus login_status_;
   views::ImageView* image_;
   views::Label* label_;
 
   DISALLOW_COPY_AND_ASSIGN(DisplayView);
 };
 
-class DisplayNotificationView : public TrayNotificationView {
- public:
-  DisplayNotificationView(user::LoginStatus login_status,
-                          TrayDisplay* tray_item,
-                          const base::string16& message)
-      : TrayNotificationView(tray_item, IDR_AURA_UBER_TRAY_DISPLAY),
-        login_status_(login_status) {
-    StartAutoCloseTimer(kTrayPopupAutoCloseDelayForTextInSeconds);
-    Update(message);
-  }
-
-  virtual ~DisplayNotificationView() {}
-
-  void Update(const base::string16& message) {
-    if (message.empty()) {
-      owner()->HideNotificationView();
-    } else {
-      views::Label* label = new views::Label(message);
-      label->SetMultiLine(true);
-      label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-      UpdateView(label);
-      RestartAutoCloseTimer();
-    }
-  }
-
-  // Overridden from TrayNotificationView:
-  virtual void OnClickAction() OVERRIDE {
-    OpenSettings(login_status_);
-  }
-
- private:
-  user::LoginStatus login_status_;
-
-  DISALLOW_COPY_AND_ASSIGN(DisplayNotificationView);
-};
-
 TrayDisplay::TrayDisplay(SystemTray* system_tray)
     : SystemTrayItem(system_tray),
       default_(NULL) {
@@ -398,18 +363,16 @@
       additional_message,
       bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY),
       base::string16(),  // display_source
-      "",  // extension_id
+      message_center::NotifierId(NOTIFIER_DISPLAY),
       message_center::RichNotificationData(),
       new message_center::HandleNotificationClickedDelegate(
-          base::Bind(&OpenSettings,
-                     Shell::GetInstance()->system_tray_delegate()->
-                     GetUserLoginStatus()))));
+          base::Bind(&OpenSettings))));
   message_center::MessageCenter::Get()->AddNotification(notification.Pass());
 }
 
 views::View* TrayDisplay::CreateDefaultView(user::LoginStatus status) {
   DCHECK(default_ == NULL);
-  default_ = new DisplayView(status);
+  default_ = new DisplayView();
   return default_;
 }
 
diff --git a/ash/system/chromeos/tray_display.h b/ash/system/chromeos/tray_display.h
index 2923f5d..3ea73da 100644
--- a/ash/system/chromeos/tray_display.h
+++ b/ash/system/chromeos/tray_display.h
@@ -21,8 +21,6 @@
 
 namespace internal {
 
-class DisplayNotificationView;
-
 class ASH_EXPORT TrayDisplay : public SystemTrayItem,
                                public DisplayController::Observer {
  public:
diff --git a/ash/system/ime/tray_ime.cc b/ash/system/ime/tray_ime.cc
index e8c9eda..7fd3b3d 100644
--- a/ash/system/ime/tray_ime.cc
+++ b/ash/system/ime/tray_ime.cc
@@ -9,6 +9,7 @@
 #include "ash/root_window_controller.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
+#include "ash/system/system_notifier.h"
 #include "ash/system/tray/hover_highlight_view.h"
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_delegate.h"
@@ -230,7 +231,7 @@
       base::string16(),  // message
       bundle.GetImageNamed(IDR_AURA_UBER_TRAY_IME),
       base::string16(),  // display_source
-      "",  // extension_id
+      message_center::NotifierId(NOTIFIER_INPUT_METHOD),
       message_center::RichNotificationData(),
       new message_center::HandleNotificationClickedDelegate(
           base::Bind(&TrayIME::PopupDetailedView,
diff --git a/ash/system/locale/locale_notification_controller.cc b/ash/system/locale/locale_notification_controller.cc
index 9f368b0..b23bf6d 100644
--- a/ash/system/locale/locale_notification_controller.cc
+++ b/ash/system/locale/locale_notification_controller.cc
@@ -5,6 +5,7 @@
 #include "ash/system/locale/locale_notification_controller.h"
 
 #include "ash/shell.h"
+#include "ash/system/system_notifier.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "base/strings/string16.h"
 #include "grit/ash_resources.h"
@@ -116,7 +117,7 @@
       base::string16()  /* message */,
       bundle.GetImageNamed(IDR_AURA_UBER_TRAY_LOCALE),
       base::string16()  /* display_source */,
-      std::string()  /* extension_id */,
+      message_center::NotifierId(NOTIFIER_LOCALE),
       optional,
       new LocaleNotificationDelegate(delegate)));
   message_center::MessageCenter::Get()->AddNotification(notification.Pass());
diff --git a/ash/system/session_length_limit/tray_session_length_limit.cc b/ash/system/session_length_limit/tray_session_length_limit.cc
index c807446..af32635 100644
--- a/ash/system/session_length_limit/tray_session_length_limit.cc
+++ b/ash/system/session_length_limit/tray_session_length_limit.cc
@@ -8,11 +8,11 @@
 
 #include "ash/shelf/shelf_types.h"
 #include "ash/shell.h"
+#include "ash/system/system_notifier.h"
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_delegate.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/tray/tray_constants.h"
-#include "ash/system/tray/tray_notification_view.h"
 #include "ash/system/tray/tray_utils.h"
 #include "base/location.h"
 #include "base/logging.h"
@@ -24,14 +24,18 @@
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/time_format.h"
+#include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/font.h"
-#include "ui/gfx/text_constants.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/notification.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/grid_layout.h"
 #include "ui/views/view.h"
 
+using message_center::Notification;
+
 namespace ash {
 namespace internal {
 
@@ -46,6 +50,8 @@
 // Color in which the remaining session time is shown when it is expiring soon.
 const SkColor kRemainingTimeExpiringSoonColor = SK_ColorRED;
 
+const char kSessionLengthTimeoutNotificationId[] = "chrome://session/timeout";
+
 views::Label* CreateAndSetupLabel() {
   views::Label* label = new views::Label;
   label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
@@ -70,26 +76,29 @@
       ui::TimeFormat::TimeDurationLong(remaining_session_time));
 }
 
+void CreateOrUpdateNotification(const base::TimeDelta& remaining_time,
+                                bool enable_spoken_feedback) {
+  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+  message_center::RichNotificationData data;
+  data.should_make_spoken_feedback_for_popup_updates = enable_spoken_feedback;
+  scoped_ptr<Notification> notification(new Notification(
+      message_center::NOTIFICATION_TYPE_SIMPLE,
+      kSessionLengthTimeoutNotificationId,
+      FormatRemainingSessionTimeNotification(remaining_time),
+      base::string16() /* message */,
+      bundle.GetImageNamed(IDR_AURA_UBER_TRAY_SESSION_LENGTH_LIMIT_TIMER),
+      base::string16() /* display_source */,
+      message_center::NotifierId(NOTIFIER_SESSION_LENGTH_TIMEOUT),
+      data,
+      NULL /* delegate */));
+  notification->SetSystemPriority();
+  message_center::MessageCenter::Get()->AddNotification(notification.Pass());
+}
+
 }  // namespace
 
 namespace tray {
 
-class RemainingSessionTimeNotificationView : public TrayNotificationView {
- public:
-  explicit RemainingSessionTimeNotificationView(TraySessionLengthLimit* owner);
-  virtual ~RemainingSessionTimeNotificationView();
-
-  // TrayNotificationView:
-  virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE;
-
-  void Update();
-
- private:
-  views::Label* label_;
-
-  DISALLOW_COPY_AND_ASSIGN(RemainingSessionTimeNotificationView);
-};
-
 class RemainingSessionTimeTrayView : public views::View {
  public:
   RemainingSessionTimeTrayView(const TraySessionLengthLimit* owner,
@@ -115,31 +124,6 @@
   DISALLOW_COPY_AND_ASSIGN(RemainingSessionTimeTrayView);
 };
 
-RemainingSessionTimeNotificationView::RemainingSessionTimeNotificationView(
-    TraySessionLengthLimit* owner)
-    : TrayNotificationView(owner,
-                           IDR_AURA_UBER_TRAY_SESSION_LENGTH_LIMIT_TIMER) {
-  label_ = new views::Label;
-  label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  label_->SetMultiLine(true);
-  Update();
-  InitView(label_);
-}
-
-RemainingSessionTimeNotificationView::~RemainingSessionTimeNotificationView() {
-}
-
-void RemainingSessionTimeNotificationView::ChildPreferredSizeChanged(
-    views::View* child) {
-  PreferredSizeChanged();
-}
-
-void RemainingSessionTimeNotificationView::Update() {
-  label_->SetText(FormatRemainingSessionTimeNotification(
-      reinterpret_cast<TraySessionLengthLimit*>(owner())->
-          GetRemainingSessionTime()));
-}
-
 RemainingSessionTimeTrayView::RemainingSessionTimeTrayView(
     const TraySessionLengthLimit* owner,
     ShelfAlignment shelf_alignment)
@@ -281,7 +265,6 @@
 TraySessionLengthLimit::TraySessionLengthLimit(SystemTray* system_tray)
     : SystemTrayItem(system_tray),
       tray_view_(NULL),
-      notification_view_(NULL),
       limit_state_(LIMIT_NONE) {
   Shell::GetInstance()->system_tray_notifier()->
       AddSessionLengthLimitObserver(this);
@@ -300,21 +283,10 @@
   return tray_view_;
 }
 
-views::View* TraySessionLengthLimit::CreateNotificationView(
-    user::LoginStatus status) {
-  CHECK(notification_view_ == NULL);
-  notification_view_ = new tray::RemainingSessionTimeNotificationView(this);
-  return notification_view_;
-}
-
 void TraySessionLengthLimit::DestroyTrayView() {
   tray_view_ = NULL;
 }
 
-void TraySessionLengthLimit::DestroyNotificationView() {
-  notification_view_ = NULL;
-}
-
 void TraySessionLengthLimit::UpdateAfterShelfAlignmentChange(
     ShelfAlignment alignment) {
   if (tray_view_)
@@ -338,12 +310,6 @@
   return remaining_session_time_;
 }
 
-void TraySessionLengthLimit::ShowAndSpeakNotification() {
-  ShowNotificationView();
-  Shell::GetInstance()->system_tray_delegate()->MaybeSpeak(UTF16ToUTF8(
-      FormatRemainingSessionTimeNotification(remaining_session_time_)));
-}
-
 void TraySessionLengthLimit::Update() {
   SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
   const LimitState previous_limit_state = limit_state_;
@@ -369,22 +335,21 @@
     }
   }
 
-  if (notification_view_)
-    notification_view_->Update();
-
   switch (limit_state_) {
     case LIMIT_NONE:
-      HideNotificationView();
+      message_center::MessageCenter::Get()->RemoveNotification(
+          kSessionLengthTimeoutNotificationId, false /* by_user */);
       break;
     case LIMIT_SET:
-      if (previous_limit_state == LIMIT_NONE)
-        ShowAndSpeakNotification();
+      CreateOrUpdateNotification(
+          remaining_session_time_,
+          previous_limit_state == LIMIT_NONE);
       break;
     case LIMIT_EXPIRING_SOON:
-      if (previous_limit_state == LIMIT_NONE ||
-          previous_limit_state == LIMIT_SET) {
-        ShowAndSpeakNotification();
-      }
+      CreateOrUpdateNotification(
+          remaining_session_time_,
+          previous_limit_state == LIMIT_NONE ||
+          previous_limit_state == LIMIT_SET);
       break;
   }
 
diff --git a/ash/system/session_length_limit/tray_session_length_limit.h b/ash/system/session_length_limit/tray_session_length_limit.h
index 1df4bb0..94ce137 100644
--- a/ash/system/session_length_limit/tray_session_length_limit.h
+++ b/ash/system/session_length_limit/tray_session_length_limit.h
@@ -17,7 +17,6 @@
 namespace internal {
 
 namespace tray {
-class RemainingSessionTimeNotificationView;
 class RemainingSessionTimeTrayView;
 }
 
@@ -36,10 +35,7 @@
 
   // SystemTrayItem:
   virtual views::View* CreateTrayView(user::LoginStatus status) OVERRIDE;
-  virtual views::View* CreateNotificationView(
-      user::LoginStatus status) OVERRIDE;
   virtual void DestroyTrayView() OVERRIDE;
-  virtual void DestroyNotificationView() OVERRIDE;
   virtual void UpdateAfterShelfAlignmentChange(
       ShelfAlignment alignment) OVERRIDE;
 
@@ -51,11 +47,9 @@
   base::TimeDelta GetRemainingSessionTime() const;
 
  private:
-  void ShowAndSpeakNotification();
   void Update();
 
   tray::RemainingSessionTimeTrayView* tray_view_;
-  tray::RemainingSessionTimeNotificationView* notification_view_;
 
   LimitState limit_state_;
   base::TimeTicks session_start_time_;
diff --git a/ash/system/system_notifier.cc b/ash/system/system_notifier.cc
new file mode 100644
index 0000000..a765ee1
--- /dev/null
+++ b/ash/system/system_notifier.cc
@@ -0,0 +1,20 @@
+// 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 "ash/system/system_notifier.h"
+
+#include "base/logging.h"
+
+namespace ash {
+
+std::string SystemComponentTypeToString(AshSystemComponentNotifierType type) {
+  if (type == NOTIFIER_SCREENSHOT)
+    return "screenshot";
+
+  // TODO(mukai): fill the names of other components.
+  NOTIMPLEMENTED();
+  return std::string();
+}
+
+}  // namespace
diff --git a/ash/system/system_notifier.h b/ash/system/system_notifier.h
new file mode 100644
index 0000000..1680a1b
--- /dev/null
+++ b/ash/system/system_notifier.h
@@ -0,0 +1,38 @@
+// 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 ASH_SYSTEM_SYSTEM_NOTIFIER_H_
+#define ASH_SYSTEM_SYSTEM_NOTIFIER_H_
+
+#include <string>
+
+#include "ash/ash_export.h"
+
+namespace ash {
+
+enum AshSystemComponentNotifierType {
+  NOTIFIER_NO_SYSTEM_COMPONENT = -1,
+
+  // Alphabetical order.
+  NOTIFIER_DISPLAY,
+  NOTIFIER_DISPLAY_RESOLUTION_CHANGE,
+  NOTIFIER_DISPLAY_ERROR,
+  NOTIFIER_INPUT_METHOD,
+  NOTIFIER_LOCALE,
+  NOTIFIER_LOCALLY_MANAGED_USER,
+  NOTIFIER_NETWORK,
+  NOTIFIER_NETWORK_ERROR,
+  NOTIFIER_SCREENSHOT,
+  NOTIFIER_SCREEN_CAPTURE,
+  NOTIFIER_SCREEN_SHARE,
+  NOTIFIER_SESSION_LENGTH_TIMEOUT,
+  NOTIFIER_POWER,
+};
+
+ASH_EXPORT std::string SystemComponentTypeToString(
+    AshSystemComponentNotifierType type);
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_SYSTEM_NOTIFIER_H_
diff --git a/ash/system/web_notification/web_notification_tray.cc b/ash/system/web_notification/web_notification_tray.cc
index 08e1904..5527133 100644
--- a/ash/system/web_notification/web_notification_tray.cc
+++ b/ash/system/web_notification/web_notification_tray.cc
@@ -55,6 +55,15 @@
 #endif  // defined(OS_CHROMEOS)
 
 namespace ash {
+namespace {
+
+// Menu commands
+const int kToggleQuietMode = 0;
+const int kEnableQuietModeHour = 1;
+const int kEnableQuietModeDay = 2;
+
+}
+
 namespace internal {
 namespace {
 
@@ -420,37 +429,6 @@
         status_area_widget()->system_tray()->HasNotificationBubble());
 }
 
-void WebNotificationTray::ShowQuietModeMenu(const ui::Event& event) {
-  base::AutoReset<bool> reset(&should_block_shelf_auto_hide_, true);
-  scoped_ptr<ui::MenuModel> menu_model(
-      message_center_tray_->CreateQuietModeMenu());
-  quiet_mode_menu_runner_.reset(new views::MenuRunner(menu_model.get()));
-  gfx::Point point;
-  views::View::ConvertPointToScreen(this, &point);
-  if (quiet_mode_menu_runner_->RunMenuAt(
-      GetWidget(),
-      NULL,
-      gfx::Rect(point, bounds().size()),
-      views::MenuItemView::BUBBLE_ABOVE,
-      ui::GetMenuSourceTypeForEvent(event),
-      views::MenuRunner::HAS_MNEMONICS) == views::MenuRunner::MENU_DELETED)
-    return;
-
-  quiet_mode_menu_runner_.reset();
-  GetShelfLayoutManager()->UpdateAutoHideState();
-}
-
-bool WebNotificationTray::ShouldShowQuietModeMenu(const ui::Event& event) {
-  // TODO(mukai): Add keyboard event handler.
-  if (!event.IsMouseEvent())
-    return false;
-
-  const ui::MouseEvent* mouse_event =
-      static_cast<const ui::MouseEvent*>(&event);
-
-  return mouse_event->IsRightMouseButton();
-}
-
 void WebNotificationTray::UpdateAfterLoginStatusChange(
     user::LoginStatus login_status) {
   if (login_status == user::LOGGED_IN_LOCKED) {
@@ -518,11 +496,6 @@
 }
 
 bool WebNotificationTray::PerformAction(const ui::Event& event) {
-  if (ShouldShowQuietModeMenu(event)) {
-    ShowQuietModeMenu(event);
-    return true;
-  }
-
   if (message_center_bubble())
     message_center_tray_->HideMessageCenterBubble();
   else
@@ -567,6 +540,34 @@
   return message_center_tray_.get();
 }
 
+bool WebNotificationTray::IsCommandIdChecked(int command_id) const {
+  if (command_id != kToggleQuietMode)
+    return false;
+  return message_center()->IsQuietMode();
+}
+
+bool WebNotificationTray::IsCommandIdEnabled(int command_id) const {
+  return true;
+}
+
+bool WebNotificationTray::GetAcceleratorForCommandId(
+    int command_id,
+    ui::Accelerator* accelerator) {
+  return false;
+}
+
+void WebNotificationTray::ExecuteCommand(int command_id, int event_flags) {
+  if (command_id == kToggleQuietMode) {
+    bool in_quiet_mode = message_center()->IsQuietMode();
+    message_center()->SetQuietMode(!in_quiet_mode);
+    return;
+  }
+  base::TimeDelta expires_in = command_id == kEnableQuietModeDay ?
+      base::TimeDelta::FromDays(1):
+      base::TimeDelta::FromHours(1);
+  message_center()->EnterQuietModeWithExpire(expires_in);
+}
+
 bool WebNotificationTray::IsPressed() {
   return IsMessageCenterBubbleVisible();
 }
@@ -615,7 +616,7 @@
   return true;
 }
 
-message_center::MessageCenter* WebNotificationTray::message_center() {
+message_center::MessageCenter* WebNotificationTray::message_center() const {
   return message_center_tray_->message_center();
 }
 
diff --git a/ash/system/web_notification/web_notification_tray.h b/ash/system/web_notification/web_notification_tray.h
index 1fb9c4d..18a85ee 100644
--- a/ash/system/web_notification/web_notification_tray.h
+++ b/ash/system/web_notification/web_notification_tray.h
@@ -11,6 +11,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "ui/base/models/simple_menu_model.h"
 #include "ui/message_center/message_center_tray.h"
 #include "ui/message_center/message_center_tray_delegate.h"
 #include "ui/views/bubble/tray_bubble_view.h"
@@ -49,7 +50,8 @@
       public views::TrayBubbleView::Delegate,
       public message_center::MessageCenterTrayDelegate,
       public views::ButtonListener,
-      public base::SupportsWeakPtr<WebNotificationTray> {
+      public base::SupportsWeakPtr<WebNotificationTray>,
+      public ui::SimpleMenuModel::Delegate {
  public:
   explicit WebNotificationTray(
       internal::StatusAreaWidget* status_area_widget);
@@ -109,10 +111,18 @@
   virtual bool ShowNotifierSettings() OVERRIDE;
   virtual message_center::MessageCenterTray* GetMessageCenterTray() OVERRIDE;
 
+  // Overridden from SimpleMenuModel::Delegate.
+  virtual bool IsCommandIdChecked(int command_id) const OVERRIDE;
+  virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE;
+  virtual bool GetAcceleratorForCommandId(
+      int command_id,
+      ui::Accelerator* accelerator) OVERRIDE;
+  virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE;
+
   // Overridden from TrayBackgroundView.
   virtual bool IsPressed() OVERRIDE;
 
-  message_center::MessageCenter* message_center();
+  message_center::MessageCenter* message_center() const;
 
  private:
   friend class WebNotificationTrayTest;
@@ -143,6 +153,9 @@
   // Shows the quiet mode menu.
   void ShowQuietModeMenu(const ui::Event& event);
 
+  // Creates the menu model for quiet mode and returns it.
+  ui::MenuModel* CreateQuietModeMenu();
+
   internal::WebNotificationBubbleWrapper* message_center_bubble() const {
     return message_center_bubble_.get();
   }
@@ -154,7 +167,6 @@
   scoped_ptr<message_center::MessageCenterTray> message_center_tray_;
   scoped_ptr<internal::WebNotificationBubbleWrapper> message_center_bubble_;
   scoped_ptr<message_center::MessagePopupCollection> popup_collection_;
-  scoped_ptr<views::MenuRunner> quiet_mode_menu_runner_;
   internal::WebNotificationButton* button_;
 
   bool show_message_center_on_unlock_;
diff --git a/ash/system/web_notification/web_notification_tray_unittest.cc b/ash/system/web_notification/web_notification_tray_unittest.cc
index 5b67b7d..ff1728e 100644
--- a/ash/system/web_notification/web_notification_tray_unittest.cc
+++ b/ash/system/web_notification/web_notification_tray_unittest.cc
@@ -112,7 +112,7 @@
         ASCIIToUTF16("Notification message body."),
         gfx::Image(),
         ASCIIToUTF16("www.test.org"),
-        "" /* extension id */,
+        message_center::NotifierId(),
         message_center::RichNotificationData(),
         NULL /* delegate */));
     GetMessageCenter()->AddNotification(notification.Pass());
@@ -128,7 +128,7 @@
         ASCIIToUTF16("Updated message body."),
         gfx::Image(),
         ASCIIToUTF16("www.test.org"),
-        "" /* extension id */,
+        message_center::NotifierId(),
         message_center::RichNotificationData(),
         NULL /* delegate */));
     GetMessageCenter()->UpdateNotification(old_id, notification.Pass());
diff --git a/ash/test/cursor_manager_test_api.cc b/ash/test/cursor_manager_test_api.cc
index 2fc569c..e93b59e 100644
--- a/ash/test/cursor_manager_test_api.cc
+++ b/ash/test/cursor_manager_test_api.cc
@@ -27,6 +27,11 @@
       cursor_manager_)->GetCurrentScale();
 }
 
+ui::CursorSetType CursorManagerTestApi::GetCurrentCursorSet() const {
+  return static_cast<views::corewm::NativeCursorManagerDelegate*>(
+      cursor_manager_)->GetCurrentCursorSet();
+}
+
 gfx::NativeCursor CursorManagerTestApi::GetCurrentCursor() const {
   return static_cast<views::corewm::NativeCursorManagerDelegate*>(
       cursor_manager_)->GetCurrentCursor();
diff --git a/ash/test/cursor_manager_test_api.h b/ash/test/cursor_manager_test_api.h
index 48ef6dc..9cffac5 100644
--- a/ash/test/cursor_manager_test_api.h
+++ b/ash/test/cursor_manager_test_api.h
@@ -6,6 +6,7 @@
 #define ASH_TEST_CURSOR_MANAGER_TEST_API_H_
 
 #include "base/basictypes.h"
+#include "ui/base/cursor/cursor.h"
 #include "ui/gfx/native_widget_types.h"
 
 namespace gfx {
@@ -28,6 +29,7 @@
   ~CursorManagerTestApi();
 
   float GetCurrentScale() const;
+  ui::CursorSetType GetCurrentCursorSet() const;
   gfx::NativeCursor GetCurrentCursor() const;
   gfx::Display GetDisplay() const;
 
diff --git a/ash/test/launcher_view_test_api.cc b/ash/test/launcher_view_test_api.cc
index e357920..fa1be56 100644
--- a/ash/test/launcher_view_test_api.cc
+++ b/ash/test/launcher_view_test_api.cc
@@ -7,7 +7,7 @@
 #include "ash/launcher/launcher_button.h"
 #include "ash/launcher/launcher_model.h"
 #include "ash/launcher/launcher_view.h"
-#include "ash/launcher/overflow_button.h"
+#include "ash/shelf/overflow_button.h"
 #include "base/message_loop/message_loop.h"
 #include "ui/views/animation/bounds_animator.h"
 #include "ui/views/view_model.h"
diff --git a/ash/test/test_launcher_delegate.cc b/ash/test/test_launcher_delegate.cc
index ed3e517..b0025b5 100644
--- a/ash/test/test_launcher_delegate.cc
+++ b/ash/test/test_launcher_delegate.cc
@@ -4,9 +4,12 @@
 
 #include "ash/test/test_launcher_delegate.h"
 
+#include "ash/launcher/launcher_item_delegate_manager.h"
 #include "ash/launcher/launcher_model.h"
 #include "ash/launcher/launcher_util.h"
+#include "ash/shell.h"
 #include "ash/wm/window_util.h"
+#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "grit/ash_resources.h"
 #include "ui/aura/window.h"
@@ -20,6 +23,14 @@
     : model_(model) {
   CHECK(!instance_);
   instance_ = this;
+
+  ash::LauncherItemDelegateManager* manager =
+      ash::Shell::GetInstance()->launcher_item_delegate_manager();
+  manager->RegisterLauncherItemDelegate(ash::TYPE_APP_PANEL, this);
+  manager->RegisterLauncherItemDelegate(ash::TYPE_APP_SHORTCUT, this);
+  manager->RegisterLauncherItemDelegate(ash::TYPE_BROWSER_SHORTCUT, this);
+  manager->RegisterLauncherItemDelegate(ash::TYPE_PLATFORM_APP, this);
+  manager->RegisterLauncherItemDelegate(ash::TYPE_WINDOWED_APP, this);
 }
 
 TestLauncherDelegate::~TestLauncherDelegate() {
@@ -37,7 +48,7 @@
   if (window->type() == aura::client::WINDOW_TYPE_PANEL)
     item.type = ash::TYPE_APP_PANEL;
   else
-    item.type = ash::TYPE_TABBED;
+    item.type = ash::TYPE_PLATFORM_APP;
   DCHECK(window_to_id_.find(window) == window_to_id_.end());
   window_to_id_[window] = model_->next_id();
   item.status = status;
@@ -131,6 +142,10 @@
   return 0;
 }
 
+const std::string& TestLauncherDelegate::GetAppIDForLauncherID(LauncherID id) {
+  return EmptyString();
+}
+
 void TestLauncherDelegate::PinAppWithID(const std::string& app_id) {
 }
 
@@ -138,7 +153,7 @@
   return false;
 }
 
-void TestLauncherDelegate::UnpinAppsWithID(const std::string& app_id) {
+void TestLauncherDelegate::UnpinAppWithID(const std::string& app_id) {
 }
 
 }  // namespace test
diff --git a/ash/test/test_launcher_delegate.h b/ash/test/test_launcher_delegate.h
index 06d0cde..e084a1a 100644
--- a/ash/test/test_launcher_delegate.h
+++ b/ash/test/test_launcher_delegate.h
@@ -9,6 +9,7 @@
 #include <set>
 
 #include "ash/launcher/launcher_delegate.h"
+#include "ash/launcher/launcher_item_delegate.h"
 #include "base/compiler_specific.h"
 #include "ui/aura/window_observer.h"
 
@@ -21,6 +22,7 @@
 // Test implementation of LauncherDelegate.
 // Tests may create icons for windows by calling AddLauncherItem
 class TestLauncherDelegate : public LauncherDelegate,
+                             public LauncherItemDelegate,
                              public aura::WindowObserver {
  public:
   explicit TestLauncherDelegate(LauncherModel* model);
@@ -38,6 +40,16 @@
       const HierarchyChangeParams& params) OVERRIDE;
 
   // LauncherDelegate implementation.
+  virtual ash::LauncherID GetIDByWindow(aura::Window* window) OVERRIDE;
+  virtual void OnLauncherCreated(Launcher* launcher) OVERRIDE;
+  virtual void OnLauncherDestroyed(Launcher* launcher) OVERRIDE;
+  virtual LauncherID GetLauncherIDForAppID(const std::string& app_id) OVERRIDE;
+  virtual const std::string& GetAppIDForLauncherID(LauncherID id) OVERRIDE;
+  virtual void PinAppWithID(const std::string& app_id) OVERRIDE;
+  virtual bool IsAppPinned(const std::string& app_id) OVERRIDE;
+  virtual void UnpinAppWithID(const std::string& app_id) OVERRIDE;
+
+  // LauncherItemDelegate implementation.
   virtual void ItemSelected(const LauncherItem& item,
                            const ui::Event& event) OVERRIDE;
   virtual base::string16 GetTitle(const LauncherItem& item) OVERRIDE;
@@ -46,15 +58,8 @@
   virtual ash::LauncherMenuModel* CreateApplicationMenu(
       const LauncherItem& item,
       int event_flags) OVERRIDE;
-  virtual ash::LauncherID GetIDByWindow(aura::Window* window) OVERRIDE;
   virtual bool IsDraggable(const ash::LauncherItem& item) OVERRIDE;
   virtual bool ShouldShowTooltip(const LauncherItem& item) OVERRIDE;
-  virtual void OnLauncherCreated(Launcher* launcher) OVERRIDE;
-  virtual void OnLauncherDestroyed(Launcher* launcher) OVERRIDE;
-  virtual LauncherID GetLauncherIDForAppID(const std::string& app_id) OVERRIDE;
-  virtual void PinAppWithID(const std::string& app_id) OVERRIDE;
-  virtual bool IsAppPinned(const std::string& app_id) OVERRIDE;
-  virtual void UnpinAppsWithID(const std::string& app_id) OVERRIDE;
 
  private:
   typedef std::map<aura::Window*, ash::LauncherID> WindowToID;
diff --git a/ash/wm/ash_native_cursor_manager.cc b/ash/wm/ash_native_cursor_manager.cc
index 28e0785..6b88809 100644
--- a/ash/wm/ash_native_cursor_manager.cc
+++ b/ash/wm/ash_native_cursor_manager.cc
@@ -79,13 +79,24 @@
     SetCursorOnAllRootWindows(new_cursor);
 }
 
+void AshNativeCursorManager::SetCursorSet(
+    ui::CursorSetType cursor_set,
+    views::corewm::NativeCursorManagerDelegate* delegate) {
+  image_cursors_->SetCursorSet(cursor_set);
+  delegate->CommitCursorSet(cursor_set);
+
+  // Sets the cursor to reflect the scale change immediately.
+  if (delegate->GetCurrentVisibility())
+    SetCursor(delegate->GetCurrentCursor(), delegate);
+}
+
 void AshNativeCursorManager::SetScale(
     float scale,
     views::corewm::NativeCursorManagerDelegate* delegate) {
   image_cursors_->SetScale(scale);
   delegate->CommitScale(scale);
 
-  // Sets the cursor to refrect the scale change imidiately.
+  // Sets the cursor to reflect the scale change immediately.
   SetCursor(delegate->GetCurrentCursor(), delegate);
 }
 
diff --git a/ash/wm/ash_native_cursor_manager.h b/ash/wm/ash_native_cursor_manager.h
index 5ea9ec9..f307b16 100644
--- a/ash/wm/ash_native_cursor_manager.h
+++ b/ash/wm/ash_native_cursor_manager.h
@@ -49,6 +49,9 @@
   virtual void SetScale(
       float scale,
       views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE;
+  virtual void SetCursorSet(
+      ui::CursorSetType cursor_set,
+      views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE;
   virtual void SetMouseEventsEnabled(
       bool enabled,
       views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE;
diff --git a/ash/wm/ash_native_cursor_manager_unittest.cc b/ash/wm/ash_native_cursor_manager_unittest.cc
index 8e1de2e..113e624 100644
--- a/ash/wm/ash_native_cursor_manager_unittest.cc
+++ b/ash/wm/ash_native_cursor_manager_unittest.cc
@@ -68,12 +68,22 @@
   cursor_manager->SetDisplay(display);
   EXPECT_EQ(2.5f, test_api.GetCurrentScale());
   EXPECT_EQ(2.0f, test_api.GetDisplay().device_scale_factor());
+  EXPECT_EQ(ui::CURSOR_SET_NORMAL, test_api.GetCurrentCursorSet());
   EXPECT_EQ(gfx::Display::ROTATE_90, test_api.GetDisplay().rotation());
   EXPECT_TRUE(test_api.GetCurrentCursor().platform());
 
   cursor_manager->LockCursor();
   EXPECT_TRUE(cursor_manager->IsCursorLocked());
 
+  // Cursor type does not change while cursor is locked.
+  EXPECT_EQ(ui::CURSOR_SET_NORMAL, test_api.GetCurrentCursorSet());
+  cursor_manager->SetCursorSet(ui::CURSOR_SET_NORMAL);
+  EXPECT_EQ(ui::CURSOR_SET_NORMAL, test_api.GetCurrentCursorSet());
+  cursor_manager->SetCursorSet(ui::CURSOR_SET_LARGE);
+  EXPECT_EQ(ui::CURSOR_SET_LARGE, test_api.GetCurrentCursorSet());
+  cursor_manager->SetCursorSet(ui::CURSOR_SET_NORMAL);
+  EXPECT_EQ(ui::CURSOR_SET_NORMAL, test_api.GetCurrentCursorSet());
+
   // Cusror scale does change even while cursor is locked.
   EXPECT_EQ(2.5f, test_api.GetCurrentScale());
   cursor_manager->SetScale(1.f);
@@ -116,6 +126,22 @@
   EXPECT_TRUE(test_api.GetCurrentCursor().platform());
 }
 
+TEST_F(AshNativeCursorManagerTest, SetCursorSet) {
+  CursorManager* cursor_manager = Shell::GetInstance()->cursor_manager();
+  CursorManagerTestApi test_api(cursor_manager);
+
+  EXPECT_EQ(ui::CURSOR_SET_NORMAL, test_api.GetCurrentCursorSet());
+
+  cursor_manager->SetCursorSet(ui::CURSOR_SET_NORMAL);
+  EXPECT_EQ(ui::CURSOR_SET_NORMAL, test_api.GetCurrentCursorSet());
+
+  cursor_manager->SetCursorSet(ui::CURSOR_SET_LARGE);
+  EXPECT_EQ(ui::CURSOR_SET_LARGE, test_api.GetCurrentCursorSet());
+
+  cursor_manager->SetCursorSet(ui::CURSOR_SET_NORMAL);
+  EXPECT_EQ(ui::CURSOR_SET_NORMAL, test_api.GetCurrentCursorSet());
+}
+
 TEST_F(AshNativeCursorManagerTest, SetScale) {
   CursorManager* cursor_manager = Shell::GetInstance()->cursor_manager();
   CursorManagerTestApi test_api(cursor_manager);
diff --git a/ash/wm/custom_frame_view_ash.cc b/ash/wm/custom_frame_view_ash.cc
index f7090a4..7824464 100644
--- a/ash/wm/custom_frame_view_ash.cc
+++ b/ash/wm/custom_frame_view_ash.cc
@@ -4,21 +4,15 @@
 
 #include "ash/wm/custom_frame_view_ash.h"
 
-#include "ash/shell_delegate.h"
 #include "ash/wm/frame_painter.h"
-#include "ash/wm/workspace/frame_maximize_button.h"
+#include "ash/wm/workspace/frame_caption_button_container_view.h"
 #include "grit/ash_resources.h"
-#include "grit/ui_strings.h"  // Accessibility names
-#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
-#include "ui/compositor/layer_animator.h"
-#include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/rect.h"
 #include "ui/gfx/size.h"
-#include "ui/views/controls/button/image_button.h"
 #include "ui/views/widget/native_widget_aura.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
@@ -43,8 +37,7 @@
 // CustomFrameViewAsh, public:
 CustomFrameViewAsh::CustomFrameViewAsh()
     : frame_(NULL),
-      maximize_button_(NULL),
-      close_button_(NULL),
+      caption_button_container_(NULL),
       frame_painter_(new ash::FramePainter) {
 }
 
@@ -54,19 +47,17 @@
 void CustomFrameViewAsh::Init(views::Widget* frame) {
   frame_ = frame;
 
-  maximize_button_ = new FrameMaximizeButton(this, this);
-  maximize_button_->SetAccessibleName(
-      l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE));
-  AddChildView(maximize_button_);
-  close_button_ = new views::ImageButton(this);
-  close_button_->SetAccessibleName(
-      l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
-  AddChildView(close_button_);
+  // Unfortunately, there is no views::WidgetDelegate::CanMinimize(). Assume
+  // that the window frame can be minimized if it can be maximized.
+  FrameCaptionButtonContainerView::MinimizeAllowed minimize_allowed =
+      frame_->widget_delegate()->CanMaximize() ?
+          FrameCaptionButtonContainerView::MINIMIZE_ALLOWED :
+          FrameCaptionButtonContainerView::MINIMIZE_DISALLOWED;
+  caption_button_container_ = new FrameCaptionButtonContainerView(this, frame,
+      minimize_allowed);
+  AddChildView(caption_button_container_);
 
-  maximize_button_->SetVisible(frame_->widget_delegate()->CanMaximize());
-
-  frame_painter_->Init(frame_, NULL, maximize_button_, close_button_,
-                       FramePainter::SIZE_BUTTON_MAXIMIZES);
+  frame_painter_->Init(frame_, NULL, caption_button_container_);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -94,7 +85,7 @@
 }
 
 void CustomFrameViewAsh::ResetWindowControls() {
-  maximize_button_->SetState(views::CustomButton::STATE_NORMAL);
+  caption_button_container_->ResetWindowControls();
 }
 
 void CustomFrameViewAsh::UpdateWindowIcon() {
@@ -161,39 +152,6 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// views::ButtonListener overrides:
-void CustomFrameViewAsh::ButtonPressed(views::Button* sender,
-                                       const ui::Event& event) {
-  scoped_ptr<ui::ScopedAnimationDurationScaleMode> slow_duration_mode;
-  if (event.IsShiftDown()) {
-    slow_duration_mode.reset(new ui::ScopedAnimationDurationScaleMode(
-        ui::ScopedAnimationDurationScaleMode::SLOW_DURATION));
-  }
-
-  ash::UserMetricsAction action =
-      ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE;
-
-  if (sender == maximize_button_) {
-    // The maximize button may move out from under the cursor.
-    ResetWindowControls();
-    if (frame_->IsMaximized()) {
-      action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE;
-      frame_->Restore();
-    } else {
-      frame_->Maximize();
-    }
-    // |this| may be deleted - some windows delete their frames on maximize.
-  } else if (sender == close_button_) {
-    action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK;
-    frame_->Close();
-  } else {
-    return;
-  }
-
-  ash::Shell::GetInstance()->delegate()->RecordUserMetricsAction(action);
-}
-
-////////////////////////////////////////////////////////////////////////////////
 // CustomFrameViewAsh, private:
 
 int CustomFrameViewAsh::NonClientTopBorderHeight() const {
@@ -202,7 +160,7 @@
 
   // Reserve enough space to see the buttons, including any offset from top and
   // reserving space for the separator line.
-  return close_button_->bounds().bottom() +
+  return caption_button_container_->bounds().bottom() +
       frame_painter_->HeaderContentSeparatorSize();
 }
 
diff --git a/ash/wm/custom_frame_view_ash.h b/ash/wm/custom_frame_view_ash.h
index 80525c3..57526b4 100644
--- a/ash/wm/custom_frame_view_ash.h
+++ b/ash/wm/custom_frame_view_ash.h
@@ -12,7 +12,6 @@
 
 namespace ash {
 class FramePainter;
-class FrameMaximizeButton;
 }
 namespace gfx {
 class Font;
@@ -24,10 +23,15 @@
 
 namespace ash {
 
+class FrameCaptionButtonContainerView;
+
+namespace test {
+class CustomFrameViewAshTest;
+}
+
 // A NonClientFrameView used for dialogs and other non-browser windows.
 // See also views::CustomFrameView and BrowserNonClientFrameViewAsh.
-class ASH_EXPORT CustomFrameViewAsh : public views::NonClientFrameView,
-                                      public views::ButtonListener {
+class ASH_EXPORT CustomFrameViewAsh : public views::NonClientFrameView {
  public:
   // Internal class name.
   static const char kViewClassName[];
@@ -35,25 +39,8 @@
   CustomFrameViewAsh();
   virtual ~CustomFrameViewAsh();
 
-  // For testing.
-  class TestApi {
-    public:
-     explicit TestApi(CustomFrameViewAsh* frame) : frame_(frame) {
-     }
-
-     ash::FrameMaximizeButton* maximize_button() const {
-       return frame_->maximize_button_;
-     }
-
-    private:
-     TestApi();
-     CustomFrameViewAsh* frame_;
-  };
-
   void Init(views::Widget* frame);
 
-  views::ImageButton* close_button() { return close_button_; }
-
   // views::NonClientFrameView overrides:
   virtual gfx::Rect GetBoundsForClientView() const OVERRIDE;
   virtual gfx::Rect GetWindowBoundsForClientBounds(
@@ -73,19 +60,17 @@
   virtual gfx::Size GetMinimumSize() OVERRIDE;
   virtual gfx::Size GetMaximumSize() OVERRIDE;
 
-  // views::ButtonListener overrides:
-  virtual void ButtonPressed(views::Button* sender,
-                             const ui::Event& event) OVERRIDE;
-
  private:
+  friend class test::CustomFrameViewAshTest;
+
   // Height from top of window to top of client area.
   int NonClientTopBorderHeight() const;
 
   // Not owned.
   views::Widget* frame_;
 
-  ash::FrameMaximizeButton* maximize_button_;
-  views::ImageButton* close_button_;
+  // View which contains the window controls.
+  FrameCaptionButtonContainerView* caption_button_container_;
 
   scoped_ptr<FramePainter> frame_painter_;
 
diff --git a/ash/wm/custom_frame_view_ash_unittest.cc b/ash/wm/custom_frame_view_ash_unittest.cc
index c087046..8036cc6 100644
--- a/ash/wm/custom_frame_view_ash_unittest.cc
+++ b/ash/wm/custom_frame_view_ash_unittest.cc
@@ -9,6 +9,7 @@
 #include "ash/wm/maximize_bubble_controller.h"
 #include "ash/wm/property_util.h"
 #include "ash/wm/window_util.h"
+#include "ash/wm/workspace/frame_caption_button_container_view.h"
 #include "ash/wm/workspace/frame_maximize_button.h"
 #include "ash/wm/workspace/snap_sizer.h"
 #include "base/command_line.h"
@@ -20,16 +21,14 @@
 #include "ui/base/events/event_utils.h"
 #include "ui/base/gestures/gesture_configuration.h"
 #include "ui/views/controls/button/image_button.h"
-#include "ui/views/test/test_views_delegate.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 
 namespace ash {
-namespace internal {
+namespace test {
 
 namespace {
 
-
 class CancelCallbackHandler {
  public:
   CancelCallbackHandler(int update_events_before_cancel,
@@ -60,28 +59,6 @@
   DISALLOW_COPY_AND_ASSIGN(CancelCallbackHandler);
 };
 
-class ShellViewsDelegate : public views::TestViewsDelegate {
- public:
-  ShellViewsDelegate() {}
-  virtual ~ShellViewsDelegate() {}
-
-  // Overridden from views::TestViewsDelegate:
-  virtual views::NonClientFrameView* CreateDefaultNonClientFrameView(
-      views::Widget* widget) OVERRIDE {
-    // Always test CustomFrameViewAsh, which may not be the ash::Shell default.
-    CustomFrameViewAsh* frame_view = new CustomFrameViewAsh;
-    frame_view->Init(widget);
-    return frame_view;
-  }
-  virtual bool UseTransparentWindows() const OVERRIDE {
-    // Ash uses transparent window frames.
-    return true;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ShellViewsDelegate);
-};
-
 class TestWidgetDelegate : public views::WidgetDelegateView {
  public:
   TestWidgetDelegate() {}
@@ -97,6 +74,13 @@
   virtual bool CanMaximize() const OVERRIDE {
     return true;
   }
+  virtual views::NonClientFrameView* CreateNonClientFrameView(
+      views::Widget* widget) OVERRIDE {
+    // Always test CustomFrameViewAsh, which may not be the ash::Shell default.
+    CustomFrameViewAsh* frame_view = new CustomFrameViewAsh;
+    frame_view->Init(widget);
+    return frame_view;
+  }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate);
@@ -114,34 +98,43 @@
     views::Widget* widget = new views::Widget;
     params.context = CurrentContext();
     params.delegate = new TestWidgetDelegate;
+    params.bounds = gfx::Rect(10, 10, 100, 100);
+    params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
     widget->Init(params);
     widget->Show();
     return widget;
   }
 
-  CustomFrameViewAsh* GetCustomFrameViewAsh(views::Widget* widget) const {
-    return static_cast<CustomFrameViewAsh*>(widget->non_client_view()->
-        frame_view());
+  void CloseWidget() {
+    if (widget_)
+      widget_->CloseNow();
+    widget_ = NULL;
   }
 
   virtual void SetUp() OVERRIDE {
     AshTestBase::SetUp();
-    if (!views::ViewsDelegate::views_delegate) {
-      views_delegate_.reset(new ShellViewsDelegate);
-      views::ViewsDelegate::views_delegate = views_delegate_.get();
-    }
+
+    widget_ = CreateWidget();
+    CustomFrameViewAsh* frame = static_cast<CustomFrameViewAsh*>(
+        widget()->non_client_view()->frame_view());
+    FrameCaptionButtonContainerView::TestApi test(
+        frame->caption_button_container_);
+    maximize_button_ = static_cast<FrameMaximizeButton*>(
+        test.size_button());
   }
 
   virtual void TearDown() OVERRIDE {
-    if (views_delegate_) {
-      views::ViewsDelegate::views_delegate = NULL;
-      views_delegate_.reset();
-    }
+    CloseWidget();
     AshTestBase::TearDown();
   }
 
+  views::Widget* widget() { return widget_; }
+
+  FrameMaximizeButton* maximize_button() { return maximize_button_; }
+
  private:
-  scoped_ptr<views::ViewsDelegate> views_delegate_;
+  views::Widget* widget_;
+  FrameMaximizeButton* maximize_button_;
 
   DISALLOW_COPY_AND_ASSIGN(CustomFrameViewAshTest);
 };
@@ -149,11 +142,8 @@
 // Tests that clicking on the resize-button toggles between maximize and normal
 // state.
 TEST_F(CustomFrameViewAshTest, ResizeButtonToggleMaximize) {
-  views::Widget* widget = CreateWidget();
-  aura::Window* window = widget->GetNativeWindow();
-  CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget);
-  CustomFrameViewAsh::TestApi test(frame);
-  views::View* view = test.maximize_button();
+  aura::Window* window = widget()->GetNativeWindow();
+  views::View* view = maximize_button();
   gfx::Point center = view->GetBoundsInScreen().CenterPoint();
 
   aura::test::EventGenerator generator(window->GetRootWindow(), center);
@@ -181,8 +171,6 @@
 
   generator.GestureTapDownAndUp(view->GetBoundsInScreen().CenterPoint());
   EXPECT_FALSE(ash::wm::IsWindowMaximized(window));
-
-  widget->Close();
 }
 
 #if defined(OS_WIN)
@@ -194,11 +182,8 @@
 
 // Tests that click+dragging on the resize-button tiles or minimizes the window.
 TEST_F(CustomFrameViewAshTest, MAYBE_ResizeButtonDrag) {
-  views::Widget* widget = CreateWidget();
-  aura::Window* window = widget->GetNativeWindow();
-  CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget);
-  CustomFrameViewAsh::TestApi test(frame);
-  views::View* view = test.maximize_button();
+  aura::Window* window = widget()->GetNativeWindow();
+  views::View* view = maximize_button();
   gfx::Point center = view->GetBoundsInScreen().CenterPoint();
 
   aura::test::EventGenerator generator(window->GetRootWindow(), center);
@@ -302,8 +287,6 @@
   }
 
   // Test with gesture events.
-
-  widget->Close();
 }
 
 #if defined(OS_WIN)
@@ -319,16 +302,13 @@
 // trigger dependent on the available drag distance.
 TEST_F(CustomFrameViewAshTest,
        MAYBE_TouchDragResizeCloseToCornerDiffersFromMouse) {
-  views::Widget* widget = CreateWidget();
-  aura::Window* window = widget->GetNativeWindow();
-  CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget);
-  CustomFrameViewAsh::TestApi test(frame);
-  views::View* view = test.maximize_button();
+  aura::Window* window = widget()->GetNativeWindow();
+  views::View* view = maximize_button();
 
-  gfx::Rect work_area = widget->GetWorkAreaBoundsInScreen();
+  gfx::Rect work_area = widget()->GetWorkAreaBoundsInScreen();
   gfx::Rect bounds = window->bounds();
   bounds.set_x(work_area.width() - bounds.width());
-  widget->SetBounds(bounds);
+  widget()->SetBounds(bounds);
 
   gfx::Point start_point = view->GetBoundsInScreen().CenterPoint();
   // We want to move all the way to the right (the few pixels we have).
@@ -351,7 +331,7 @@
   EXPECT_NE(bounds.ToString(), touch_result.ToString());
 
   // Set the position back to where it was before and re-try with a mouse.
-  widget->SetBounds(bounds);
+  widget()->SetBounds(bounds);
 
   generator.MoveMouseTo(start_point);
   generator.PressLeftButton();
@@ -370,16 +350,13 @@
   EXPECT_GT(mouse_result.width(), touch_result.width());
 }
 
-
 // Test that closing the (browser) window with an opened balloon does not
 // crash the system. In other words: Make sure that shutting down the frame
 // destroys the opened balloon in an orderly fashion.
 TEST_F(CustomFrameViewAshTest, MaximizeButtonExternalShutDown) {
-  views::Widget* widget = CreateWidget();
-  aura::Window* window = widget->GetNativeWindow();
-  CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget);
-  CustomFrameViewAsh::TestApi test(frame);
-  ash::FrameMaximizeButton* maximize_button = test.maximize_button();
+  aura::Window* window = widget()->GetNativeWindow();
+  ash::FrameMaximizeButton* maximize_button =
+      CustomFrameViewAshTest::maximize_button();
   maximize_button->set_bubble_appearance_delay_ms(0);
   gfx::Point button_pos = maximize_button->GetBoundsInScreen().CenterPoint();
   gfx::Point off_pos(button_pos.x() + 100, button_pos.y() + 100);
@@ -394,17 +371,15 @@
 
   // Even though the widget is closing the bubble menu should not crash upon
   // its delayed destruction.
-  widget->CloseNow();
+  CloseWidget();
 }
 
 // Test that maximizing the browser after hovering in does not crash the system
 // when the observer gets removed in the bubble destruction process.
 TEST_F(CustomFrameViewAshTest, MaximizeOnHoverThenClick) {
-  views::Widget* widget = CreateWidget();
-  aura::Window* window = widget->GetNativeWindow();
-  CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget);
-  CustomFrameViewAsh::TestApi test(frame);
-  ash::FrameMaximizeButton* maximize_button = test.maximize_button();
+  aura::Window* window = widget()->GetNativeWindow();
+  ash::FrameMaximizeButton* maximize_button =
+      CustomFrameViewAshTest::maximize_button();
   maximize_button->set_bubble_appearance_delay_ms(0);
   gfx::Point button_pos = maximize_button->GetBoundsInScreen().CenterPoint();
   gfx::Point off_pos(button_pos.x() + 100, button_pos.y() + 100);
@@ -425,11 +400,9 @@
 // pressing and dragging the button itself off the button will also release the
 // phantom window.
 TEST_F(CustomFrameViewAshTest, MaximizeLeftButtonDragOut) {
-  views::Widget* widget = CreateWidget();
-  aura::Window* window = widget->GetNativeWindow();
-  CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget);
-  CustomFrameViewAsh::TestApi test(frame);
-  ash::FrameMaximizeButton* maximize_button = test.maximize_button();
+  aura::Window* window = widget()->GetNativeWindow();
+  ash::FrameMaximizeButton* maximize_button =
+      CustomFrameViewAshTest::maximize_button();
   maximize_button->set_bubble_appearance_delay_ms(0);
   gfx::Point button_pos = maximize_button->GetBoundsInScreen().CenterPoint();
   gfx::Point off_pos(button_pos.x() + 100, button_pos.y() + 100);
@@ -472,11 +445,9 @@
 // Test that clicking a button in the maximizer bubble (in this case the
 // maximize left button) will do the requested action.
 TEST_F(CustomFrameViewAshTest, MaximizeLeftByButton) {
-  views::Widget* widget = CreateWidget();
-  aura::Window* window = widget->GetNativeWindow();
-  CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget);
-  CustomFrameViewAsh::TestApi test(frame);
-  ash::FrameMaximizeButton* maximize_button = test.maximize_button();
+  aura::Window* window = widget()->GetNativeWindow();
+  ash::FrameMaximizeButton* maximize_button =
+      CustomFrameViewAshTest::maximize_button();
   maximize_button->set_bubble_appearance_delay_ms(0);
   gfx::Point button_pos = maximize_button->GetBoundsInScreen().CenterPoint();
   gfx::Point off_pos(button_pos.x() + 100, button_pos.y() + 100);
@@ -511,11 +482,9 @@
 
 // Test that the activation focus does not change when the bubble gets shown.
 TEST_F(CustomFrameViewAshTest, MaximizeKeepFocus) {
-  views::Widget* widget = CreateWidget();
-  aura::Window* window = widget->GetNativeWindow();
-  CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget);
-  CustomFrameViewAsh::TestApi test(frame);
-  ash::FrameMaximizeButton* maximize_button = test.maximize_button();
+  aura::Window* window = widget()->GetNativeWindow();
+  ash::FrameMaximizeButton* maximize_button =
+      CustomFrameViewAshTest::maximize_button();
   maximize_button->set_bubble_appearance_delay_ms(0);
   gfx::Point button_pos = maximize_button->GetBoundsInScreen().CenterPoint();
   gfx::Point off_pos(button_pos.x() + 100, button_pos.y() + 100);
@@ -536,12 +505,10 @@
 }
 
 TEST_F(CustomFrameViewAshTest, MaximizeTap) {
-  views::Widget* widget = CreateWidget();
-  aura::Window* window = widget->GetNativeWindow();
+  aura::Window* window = widget()->GetNativeWindow();
   aura::RootWindow* root_window = window->GetRootWindow();
-  CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget);
-  CustomFrameViewAsh::TestApi test(frame);
-  ash::FrameMaximizeButton* maximize_button = test.maximize_button();
+  ash::FrameMaximizeButton* maximize_button =
+      CustomFrameViewAshTest::maximize_button();
   gfx::Point button_pos = maximize_button->GetBoundsInScreen().CenterPoint();
 
   const int touch_default_radius =
@@ -568,11 +535,9 @@
 
 // Test that only the left button will activate the maximize button.
 TEST_F(CustomFrameViewAshTest, OnlyLeftButtonMaximizes) {
-  views::Widget* widget = CreateWidget();
-  aura::Window* window = widget->GetNativeWindow();
-  CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget);
-  CustomFrameViewAsh::TestApi test(frame);
-  ash::FrameMaximizeButton* maximize_button = test.maximize_button();
+  aura::Window* window = widget()->GetNativeWindow();
+  ash::FrameMaximizeButton* maximize_button =
+      CustomFrameViewAshTest::maximize_button();
   maximize_button->set_bubble_appearance_delay_ms(0);
   gfx::Point button_pos = maximize_button->GetBoundsInScreen().CenterPoint();
   gfx::Point off_pos(button_pos.x() + 100, button_pos.y() + 100);
@@ -658,27 +623,24 @@
 
 // Test that the restore from left/right maximize is properly done.
 TEST_F(CustomFrameViewAshTest, MaximizeLeftRestore) {
-  views::Widget* widget = CreateWidget();
-  aura::Window* window = widget->GetNativeWindow();
-  widget->SetBounds(gfx::Rect(10, 10, 100, 100));
-  gfx::Rect initial_bounds = widget->GetWindowBoundsInScreen();
-  CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget);
-  CustomFrameViewAsh::TestApi test(frame);
-  ash::FrameMaximizeButton* maximize_button = test.maximize_button();
+  aura::Window* window = widget()->GetNativeWindow();
+  gfx::Rect initial_bounds = widget()->GetWindowBoundsInScreen();
+  ash::FrameMaximizeButton* maximize_button =
+      CustomFrameViewAshTest::maximize_button();
   maximize_button->set_bubble_appearance_delay_ms(0);
 
   ClickMaxButton(maximize_button, window, SNAP_LEFT);
   // The window should not be maximized.
   EXPECT_FALSE(ash::wm::IsWindowMaximized(window));
   // But the bounds should be different.
-  gfx::Rect new_bounds = widget->GetWindowBoundsInScreen();
+  gfx::Rect new_bounds = widget()->GetWindowBoundsInScreen();
   EXPECT_EQ(0, new_bounds.x());
   EXPECT_EQ(0, new_bounds.y());
 
   // Now click the same button again to see that it restores.
   ClickMaxButton(maximize_button, window, SNAP_LEFT);
   // But the bounds should be restored.
-  new_bounds = widget->GetWindowBoundsInScreen();
+  new_bounds = widget()->GetWindowBoundsInScreen();
   EXPECT_EQ(new_bounds.x(), initial_bounds.x());
   EXPECT_EQ(new_bounds.y(), initial_bounds.x());
   EXPECT_EQ(new_bounds.width(), initial_bounds.width());
@@ -689,13 +651,10 @@
 
 // Maximize, left/right maximize and then restore should works.
 TEST_F(CustomFrameViewAshTest, MaximizeMaximizeLeftRestore) {
-  views::Widget* widget = CreateWidget();
-  aura::Window* window = widget->GetNativeWindow();
-  widget->SetBounds(gfx::Rect(10, 10, 100, 100));
-  gfx::Rect initial_bounds = widget->GetWindowBoundsInScreen();
-  CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget);
-  CustomFrameViewAsh::TestApi test(frame);
-  ash::FrameMaximizeButton* maximize_button = test.maximize_button();
+  aura::Window* window = widget()->GetNativeWindow();
+  gfx::Rect initial_bounds = widget()->GetWindowBoundsInScreen();
+  ash::FrameMaximizeButton* maximize_button =
+      CustomFrameViewAshTest::maximize_button();
   maximize_button->set_bubble_appearance_delay_ms(0);
 
   ClickMaxButton(maximize_button, window, SNAP_NONE);
@@ -703,7 +662,7 @@
 
   ClickMaxButton(maximize_button, window, SNAP_LEFT);
   EXPECT_FALSE(ash::wm::IsWindowMaximized(window));
-  gfx::Rect new_bounds = widget->GetWindowBoundsInScreen();
+  gfx::Rect new_bounds = widget()->GetWindowBoundsInScreen();
   EXPECT_EQ(0, new_bounds.x());
   EXPECT_EQ(0, new_bounds.y());
 
@@ -711,7 +670,7 @@
   ClickMaxButton(maximize_button, window, SNAP_LEFT);
   RunAllPendingInMessageLoop();
   // But the bounds should be restored.
-  new_bounds = widget->GetWindowBoundsInScreen();
+  new_bounds = widget()->GetWindowBoundsInScreen();
   EXPECT_EQ(new_bounds.x(), initial_bounds.x());
   EXPECT_EQ(new_bounds.y(), initial_bounds.x());
   EXPECT_EQ(new_bounds.width(), initial_bounds.width());
@@ -722,13 +681,10 @@
 
 // Left/right maximize, maximize and then restore should work.
 TEST_F(CustomFrameViewAshTest, MaximizeLeftMaximizeRestore) {
-  views::Widget* widget = CreateWidget();
-  aura::Window* window = widget->GetNativeWindow();
-  widget->SetBounds(gfx::Rect(10, 10, 100, 100));
-  gfx::Rect initial_bounds = widget->GetWindowBoundsInScreen();
-  CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget);
-  CustomFrameViewAsh::TestApi test(frame);
-  ash::FrameMaximizeButton* maximize_button = test.maximize_button();
+  aura::Window* window = widget()->GetNativeWindow();
+  gfx::Rect initial_bounds = widget()->GetWindowBoundsInScreen();
+  ash::FrameMaximizeButton* maximize_button =
+      CustomFrameViewAshTest::maximize_button();
   maximize_button->set_bubble_appearance_delay_ms(0);
 
   ClickMaxButton(maximize_button, window, SNAP_LEFT);
@@ -739,7 +695,7 @@
 
   ClickMaxButton(maximize_button, window, SNAP_NONE);
   EXPECT_FALSE(ash::wm::IsWindowMaximized(window));
-  gfx::Rect new_bounds = widget->GetWindowBoundsInScreen();
+  gfx::Rect new_bounds = widget()->GetWindowBoundsInScreen();
   EXPECT_EQ(new_bounds.x(), initial_bounds.x());
   EXPECT_EQ(new_bounds.y(), initial_bounds.x());
   EXPECT_EQ(new_bounds.width(), initial_bounds.width());
@@ -751,13 +707,10 @@
 // Starting with a window which has no restore bounds, maximize then left/right
 // maximize should not be centered but left/right maximized.
 TEST_F(CustomFrameViewAshTest, MaximizeThenLeftMaximize) {
-  views::Widget* widget = CreateWidget();
-  aura::Window* window = widget->GetNativeWindow();
-  widget->SetBounds(gfx::Rect(10, 10, 100, 100));
-  gfx::Rect initial_bounds = widget->GetWindowBoundsInScreen();
-  CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget);
-  CustomFrameViewAsh::TestApi test(frame);
-  ash::FrameMaximizeButton* maximize_button = test.maximize_button();
+  aura::Window* window = widget()->GetNativeWindow();
+  gfx::Rect initial_bounds = widget()->GetWindowBoundsInScreen();
+  ash::FrameMaximizeButton* maximize_button =
+      CustomFrameViewAshTest::maximize_button();
   maximize_button->set_bubble_appearance_delay_ms(0);
   // Make sure that there is no restore rectangle.
   EXPECT_EQ(NULL, GetRestoreBoundsInScreen(window));
@@ -768,7 +721,7 @@
   ClickMaxButton(maximize_button, window, SNAP_LEFT);
   EXPECT_FALSE(ash::wm::IsWindowMaximized(window));
 
-  gfx::Rect new_bounds = widget->GetWindowBoundsInScreen();
+  gfx::Rect new_bounds = widget()->GetWindowBoundsInScreen();
   EXPECT_EQ(new_bounds.x(), 0);
   EXPECT_EQ(new_bounds.y(), 0);
   // Make sure that the restore rectangle is the original rectangle.
@@ -778,12 +731,9 @@
 
 // Test that minimizing the window per keyboard closes the maximize bubble.
 TEST_F(CustomFrameViewAshTest, MinimizePerKeyClosesBubble) {
-  views::Widget* widget = CreateWidget();
-  aura::Window* window = widget->GetNativeWindow();
-  widget->SetBounds(gfx::Rect(10, 10, 100, 100));
-  CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget);
-  CustomFrameViewAsh::TestApi test(frame);
-  ash::FrameMaximizeButton* maximize_button = test.maximize_button();
+  aura::Window* window = widget()->GetNativeWindow();
+  ash::FrameMaximizeButton* maximize_button =
+      CustomFrameViewAshTest::maximize_button();
 
   gfx::Point button_pos = maximize_button->GetBoundsInScreen().CenterPoint();
   gfx::Point off_pos(button_pos.x() + 100, button_pos.y() + 100);
@@ -805,12 +755,9 @@
 
 // Tests that dragging down on the maximize button minimizes the window.
 TEST_F(CustomFrameViewAshTest, MaximizeButtonDragDownMinimizes) {
-  views::Widget* widget = CreateWidget();
-  aura::Window* window = widget->GetNativeWindow();
-  widget->SetBounds(gfx::Rect(10, 10, 100, 100));
-  CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget);
-  CustomFrameViewAsh::TestApi test(frame);
-  FrameMaximizeButton* maximize_button = test.maximize_button();
+  aura::Window* window = widget()->GetNativeWindow();
+  ash::FrameMaximizeButton* maximize_button =
+      CustomFrameViewAshTest::maximize_button();
 
   // Drag down on a maximized window.
   wm::MaximizeWindow(window);
@@ -838,13 +785,10 @@
 
 // Tests that dragging Left and pressing ESC does properly abort.
 TEST_F(CustomFrameViewAshTest, MaximizeButtonDragLeftEscapeExits) {
-  views::Widget* widget = CreateWidget();
-  aura::Window* window = widget->GetNativeWindow();
-  gfx::Rect window_position = gfx::Rect(10, 10, 100, 100);
-  widget->SetBounds(window_position);
-  CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget);
-  CustomFrameViewAsh::TestApi test(frame);
-  FrameMaximizeButton* maximize_button = test.maximize_button();
+  aura::Window* window = widget()->GetNativeWindow();
+  gfx::Rect initial_bounds = widget()->GetWindowBoundsInScreen();
+  ash::FrameMaximizeButton* maximize_button =
+      CustomFrameViewAshTest::maximize_button();
 
   gfx::Point button_pos = maximize_button->GetBoundsInScreen().CenterPoint();
   gfx::Point off_pos(button_pos.x() - button_pos.x() / 2, button_pos.y());
@@ -861,11 +805,11 @@
                  base::Unretained(&cancel_handler)));
 
   // Check that there was no size change.
-  EXPECT_EQ(widget->GetWindowBoundsInScreen().size().ToString(),
-            window_position.size().ToString());
+  EXPECT_EQ(widget()->GetWindowBoundsInScreen().size().ToString(),
+            initial_bounds.size().ToString());
   // Check that there is no phantom window left open.
   EXPECT_FALSE(maximize_button->phantom_window_open());
 }
 
-}  // namespace internal
+}  // namespace test
 }  // namespace ash
diff --git a/ash/wm/frame_painter.cc b/ash/wm/frame_painter.cc
index 5b7782a..31e8232 100644
--- a/ash/wm/frame_painter.cc
+++ b/ash/wm/frame_painter.cc
@@ -13,6 +13,7 @@
 #include "ash/wm/property_util.h"
 #include "ash/wm/window_properties.h"
 #include "ash/wm/window_util.h"
+#include "ash/wm/workspace/frame_caption_button_container_view.h"
 #include "base/logging.h"  // DCHECK
 #include "grit/ash_resources.h"
 #include "third_party/skia/include/core/SkCanvas.h"
@@ -33,7 +34,6 @@
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/screen.h"
 #include "ui/gfx/skia_util.h"
-#include "ui/views/controls/button/image_button.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 
@@ -63,13 +63,6 @@
 const int kHeaderContentSeparatorSize = 1;
 // Color of header bottom edge line.
 const SkColor kHeaderContentSeparatorColor = SkColorSetRGB(128, 128, 128);
-// Space between close button and right edge of window.
-const int kCloseButtonOffsetX = 0;
-// Space between close button and top edge of window.
-const int kCloseButtonOffsetY = 0;
-// The size and close buttons are designed to slightly overlap in order
-// to do fancy hover highlighting.
-const int kSizeButtonOffsetX = -1;
 // In the pre-Ash era the web content area had a frame along the left edge, so
 // user-generated theme images for the new tab page assume they are shifted
 // right relative to the header.  Now that we have removed the left edge frame
@@ -215,10 +208,8 @@
 FramePainter::FramePainter()
     : frame_(NULL),
       window_icon_(NULL),
-      size_button_(NULL),
-      close_button_(NULL),
+      caption_button_container_(NULL),
       window_(NULL),
-      button_separator_(NULL),
       top_left_corner_(NULL),
       top_edge_(NULL),
       top_right_corner_(NULL),
@@ -229,8 +220,7 @@
       previous_opacity_(0),
       crossfade_theme_frame_id_(0),
       crossfade_theme_frame_overlay_id_(0),
-      crossfade_opacity_(0),
-      size_button_behavior_(SIZE_BUTTON_MAXIMIZES) {}
+      crossfade_opacity_(0) {}
 
 FramePainter::~FramePainter() {
   // Sometimes we are destroyed before the window closes, so ensure we clean up.
@@ -239,25 +229,19 @@
   }
 }
 
-void FramePainter::Init(views::Widget* frame,
-                        views::View* window_icon,
-                        views::ImageButton* size_button,
-                        views::ImageButton* close_button,
-                        SizeButtonBehavior behavior) {
+void FramePainter::Init(
+    views::Widget* frame,
+    views::View* window_icon,
+    FrameCaptionButtonContainerView* caption_button_container) {
   DCHECK(frame);
   // window_icon may be NULL.
-  DCHECK(size_button);
-  DCHECK(close_button);
+  DCHECK(caption_button_container);
   frame_ = frame;
   window_icon_ = window_icon;
-  size_button_ = size_button;
-  close_button_ = close_button;
-  size_button_behavior_ = behavior;
+  caption_button_container_ = caption_button_container;
 
   // Window frame image parts.
   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  button_separator_ =
-      rb.GetImageNamed(IDR_AURA_WINDOW_BUTTON_SEPARATOR).ToImageSkia();
   top_left_corner_ =
       rb.GetImageNamed(IDR_AURA_WINDOW_HEADER_SHADE_TOP_LEFT).ToImageSkia();
   top_edge_ =
@@ -353,13 +337,15 @@
   if (client_component != HTNOWHERE)
     return client_component;
 
-  // Then see if the point is within any of the window controls.
-  if (close_button_->visible() &&
-      close_button_->GetMirroredBounds().Contains(point))
-    return HTCLOSE;
-  if (size_button_->visible() &&
-      size_button_->GetMirroredBounds().Contains(point))
-    return HTMAXBUTTON;
+  if (caption_button_container_->visible()) {
+    gfx::Point point_in_caption_button_container(point);
+    views::View::ConvertPointToTarget(view, caption_button_container_,
+        &point_in_caption_button_container);
+    client_component = caption_button_container_->NonClientHitTest(
+        point_in_caption_button_container);
+    if (client_component != HTNOWHERE)
+      return client_component;
+  }
 
   // Caption is a safe default.
   return HTCAPTION;
@@ -373,8 +359,7 @@
   // Ensure we have enough space for the window icon and buttons.  We allow
   // the title string to collapse to zero width.
   int title_width = GetTitleOffsetX() +
-      size_button_->width() + kSizeButtonOffsetX +
-      close_button_->width() + kCloseButtonOffsetX;
+      caption_button_container_->GetMinimumSize().width();
   if (title_width > min_size.width())
     min_size.set_width(title_width);
   return min_size;
@@ -385,11 +370,7 @@
 }
 
 int FramePainter::GetRightInset() const {
-  gfx::Size close_size = close_button_->GetPreferredSize();
-  gfx::Size size_button_size = size_button_->GetPreferredSize();
-  int inset = close_size.width() + kCloseButtonOffsetX +
-      size_button_size.width() + kSizeButtonOffsetX;
-  return inset;
+  return caption_button_container_->GetPreferredSize().width();
 }
 
 int FramePainter::GetThemeBackgroundXInset() const {
@@ -504,14 +485,6 @@
   previous_theme_frame_overlay_id_ = theme_frame_overlay_id;
   previous_opacity_ = opacity;
 
-  // Separator between the maximize and close buttons.  It overlaps the left
-  // edge of the close button.
-  gfx::Rect divider(close_button_->x(), close_button_->y(),
-                    button_separator_->width(), close_button_->height());
-  canvas->DrawImageInt(*button_separator_,
-                       view->GetMirroredXForRect(divider),
-                       close_button_->y());
-
   // We don't need the extra lightness in the edges when we're at the top edge
   // of the screen or when the header's corners are not rounded.
   //
@@ -595,72 +568,24 @@
 
 void FramePainter::LayoutHeader(views::NonClientFrameView* view,
                                 bool shorter_layout) {
-  // The new assets only make sense if the window is actually maximized or
-  // fullscreen.
-  if (shorter_layout &&
-      (frame_->IsMaximized() || frame_->IsFullscreen()) &&
-      GetTrackedByWorkspace(frame_->GetNativeWindow())) {
-    SetButtonImages(close_button_,
-                    IDR_AURA_WINDOW_MAXIMIZED_CLOSE2,
-                    IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H,
-                    IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P);
-    // The chat window cannot be restored but only minimized.
-    if (size_button_behavior_ == SIZE_BUTTON_MINIMIZES) {
-      SetButtonImages(size_button_,
-                      IDR_AURA_WINDOW_MINIMIZE_SHORT,
-                      IDR_AURA_WINDOW_MINIMIZE_SHORT_H,
-                      IDR_AURA_WINDOW_MINIMIZE_SHORT_P);
-    } else {
-      SetButtonImages(size_button_,
-                      IDR_AURA_WINDOW_MAXIMIZED_RESTORE2,
-                      IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H,
-                      IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P);
-    }
-  } else if (shorter_layout) {
-    SetButtonImages(close_button_,
-                    IDR_AURA_WINDOW_MAXIMIZED_CLOSE,
-                    IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H,
-                    IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P);
-    // The chat window cannot be restored but only minimized.
-    if (size_button_behavior_ == SIZE_BUTTON_MINIMIZES) {
-      SetButtonImages(size_button_,
-                      IDR_AURA_WINDOW_MINIMIZE_SHORT,
-                      IDR_AURA_WINDOW_MINIMIZE_SHORT_H,
-                      IDR_AURA_WINDOW_MINIMIZE_SHORT_P);
-    } else {
-      SetButtonImages(size_button_,
-                      IDR_AURA_WINDOW_MAXIMIZED_RESTORE,
-                      IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H,
-                      IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P);
-    }
-  } else {
-    SetButtonImages(close_button_,
-                    IDR_AURA_WINDOW_CLOSE,
-                    IDR_AURA_WINDOW_CLOSE_H,
-                    IDR_AURA_WINDOW_CLOSE_P);
-    SetButtonImages(size_button_,
-                    IDR_AURA_WINDOW_MAXIMIZE,
-                    IDR_AURA_WINDOW_MAXIMIZE_H,
-                    IDR_AURA_WINDOW_MAXIMIZE_P);
-  }
+  caption_button_container_->set_header_style(shorter_layout ?
+      FrameCaptionButtonContainerView::HEADER_STYLE_SHORT :
+      FrameCaptionButtonContainerView::HEADER_STYLE_TALL);
+  caption_button_container_->Layout();
 
-  gfx::Size close_size = close_button_->GetPreferredSize();
-  close_button_->SetBounds(
-      view->width() - close_size.width() - kCloseButtonOffsetX,
-      kCloseButtonOffsetY,
-      close_size.width(),
-      close_size.height());
-
-  gfx::Size size_button_size = size_button_->GetPreferredSize();
-  size_button_->SetBounds(
-      close_button_->x() - size_button_size.width() - kSizeButtonOffsetX,
-      close_button_->y(),
-      size_button_size.width(),
-      size_button_size.height());
+  gfx::Size caption_button_container_size =
+      caption_button_container_->GetPreferredSize();
+  caption_button_container_->SetBounds(
+      view->width() - caption_button_container_size.width(),
+      0,
+      caption_button_container_size.width(),
+      caption_button_container_size.height());
 
   if (window_icon_) {
-    // Vertically center the window icon with respect to the close button.
-    int icon_offset_y = GetCloseButtonCenterY() - window_icon_->height() / 2;
+    // Vertically center the window icon with respect to the caption button
+    // container.
+    int icon_offset_y =
+        GetCaptionButtonContainerCenterY() - window_icon_->height() / 2;
     window_icon_->SetBounds(kIconOffsetX, icon_offset_y, kIconSize, kIconSize);
   }
 }
@@ -774,27 +699,15 @@
 ///////////////////////////////////////////////////////////////////////////////
 // FramePainter, private:
 
-void FramePainter::SetButtonImages(views::ImageButton* button,
-                                   int normal_image_id,
-                                   int hot_image_id,
-                                   int pushed_image_id) {
-  ui::ThemeProvider* theme_provider = frame_->GetThemeProvider();
-  button->SetImage(views::CustomButton::STATE_NORMAL,
-                   theme_provider->GetImageSkiaNamed(normal_image_id));
-  button->SetImage(views::CustomButton::STATE_HOVERED,
-                   theme_provider->GetImageSkiaNamed(hot_image_id));
-  button->SetImage(views::CustomButton::STATE_PRESSED,
-                   theme_provider->GetImageSkiaNamed(pushed_image_id));
-}
-
 int FramePainter::GetTitleOffsetX() const {
   return window_icon_ ?
       window_icon_->bounds().right() + kTitleIconOffsetX :
       kTitleNoIconOffsetX;
 }
 
-int FramePainter::GetCloseButtonCenterY() const {
-  return close_button_->y() + close_button_->height() / 2;
+int FramePainter::GetCaptionButtonContainerCenterY() const {
+  return caption_button_container_->y() +
+      caption_button_container_->height() / 2;
 }
 
 int FramePainter::GetHeaderCornerRadius() const {
@@ -908,14 +821,14 @@
 
 gfx::Rect FramePainter::GetTitleBounds(const gfx::Font& title_font) {
   int title_x = GetTitleOffsetX();
-  // Center the text with respect to the close button. This way it adapts to
-  // the caption height and aligns exactly with the window icon. Don't use
-  // |window_icon_| for this computation as it may be NULL.
-  int title_y = GetCloseButtonCenterY() - title_font.GetHeight() / 2;
+  // Center the text with respect to the caption button container. This way it
+  // adapts to the caption button height and aligns exactly with the window
+  // icon. Don't use |window_icon_| for this computation as it may be NULL.
+  int title_y = GetCaptionButtonContainerCenterY() - title_font.GetHeight() / 2;
   return gfx::Rect(
       title_x,
       std::max(0, title_y),
-      std::max(0, size_button_->x() - kTitleLogoSpacing - title_x),
+      std::max(0, caption_button_container_->x() - kTitleLogoSpacing - title_x),
       title_font.GetHeight());
 }
 
diff --git a/ash/wm/frame_painter.h b/ash/wm/frame_painter.h
index b164edf..1f41c8c 100644
--- a/ash/wm/frame_painter.h
+++ b/ash/wm/frame_painter.h
@@ -29,13 +29,13 @@
 class SlideAnimation;
 }
 namespace views {
-class ImageButton;
 class NonClientFrameView;
 class View;
 class Widget;
 }
 
 namespace ash {
+class FrameCaptionButtonContainerView;
 
 // Helper class for painting window frames.  Exists to share code between
 // various implementations of views::NonClientFrameView.  Canonical source of
@@ -70,9 +70,7 @@
   // |frame| and buttons are used for layout and are not owned.
   void Init(views::Widget* frame,
             views::View* window_icon,
-            views::ImageButton* size_button,
-            views::ImageButton* close_button,
-            SizeButtonBehavior behavior);
+            FrameCaptionButtonContainerView* caption_button_container);
 
   // Updates the solo-window transparent header appearance for all windows
   // using frame painters in |root_window|.
@@ -162,17 +160,12 @@
   FRIEND_TEST_ALL_PREFIXES(FramePainterTest,
                            NoCrashShutdownWithAlwaysOnTopWindow);
 
-  // Sets the images for a button based on IDs from the |frame_| theme provider.
-  void SetButtonImages(views::ImageButton* button,
-                       int normal_image_id,
-                       int hot_image_id,
-                       int pushed_image_id);
-
   // Returns the offset between window left edge and title string.
   int GetTitleOffsetX() const;
 
-  // Returns the vertical center of the close button in window coordinates.
-  int GetCloseButtonCenterY() const;
+  // Returns the vertical center of the caption button container in window
+  // coordinates.
+  int GetCaptionButtonContainerCenterY() const;
 
   // Returns the opacity value used to paint the header.
   // |theme_frame_overlay_id| is 0 if no overlay image is used.
@@ -215,12 +208,10 @@
   // Not owned
   views::Widget* frame_;
   views::View* window_icon_;  // May be NULL.
-  views::ImageButton* size_button_;
-  views::ImageButton* close_button_;
+  FrameCaptionButtonContainerView* caption_button_container_;
   aura::Window* window_;
 
   // Window frame header/caption parts.
-  const gfx::ImageSkia* button_separator_;
   const gfx::ImageSkia* top_left_corner_;
   const gfx::ImageSkia* top_edge_;
   const gfx::ImageSkia* top_right_corner_;
@@ -240,8 +231,6 @@
   gfx::Rect header_frame_bounds_;
   scoped_ptr<ui::SlideAnimation> crossfade_animation_;
 
-  SizeButtonBehavior size_button_behavior_;
-
   DISALLOW_COPY_AND_ASSIGN(FramePainter);
 };
 
diff --git a/ash/wm/frame_painter_unittest.cc b/ash/wm/frame_painter_unittest.cc
index 5826be6..a669f8a 100644
--- a/ash/wm/frame_painter_unittest.cc
+++ b/ash/wm/frame_painter_unittest.cc
@@ -11,6 +11,7 @@
 #include "ash/wm/property_util.h"
 #include "ash/wm/window_properties.h"
 #include "ash/wm/window_util.h"
+#include "ash/wm/workspace/frame_caption_button_container_view.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_loop.h"
 #include "grit/ash_resources.h"
@@ -19,39 +20,19 @@
 #include "ui/aura/root_window.h"
 #include "ui/aura/window_observer.h"
 #include "ui/base/hit_test.h"
-#include "ui/base/theme_provider.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/screen.h"
-#include "ui/views/controls/button/button.h"
-#include "ui/views/controls/button/image_button.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 #include "ui/views/widget/widget_observer.h"
 #include "ui/views/window/non_client_view.h"
 
 using ash::FramePainter;
-using ui::ThemeProvider;
-using views::Button;
-using views::ImageButton;
 using views::NonClientFrameView;
-using views::ToggleImageButton;
 using views::Widget;
 
 namespace {
 
-bool ImagesMatch(ImageButton* button,
-                 int normal_image_id,
-                 int hovered_image_id,
-                 int pressed_image_id) {
-  ThemeProvider* theme = button->GetWidget()->GetThemeProvider();
-  gfx::ImageSkia* normal = theme->GetImageSkiaNamed(normal_image_id);
-  gfx::ImageSkia* hovered = theme->GetImageSkiaNamed(hovered_image_id);
-  gfx::ImageSkia* pressed = theme->GetImageSkiaNamed(pressed_image_id);
-  return button->GetImage(Button::STATE_NORMAL).BackedBySameObjectAs(*normal) &&
-      button->GetImage(Button::STATE_HOVERED).BackedBySameObjectAs(*hovered) &&
-      button->GetImage(Button::STATE_PRESSED).BackedBySameObjectAs(*pressed);
-}
-
 class ResizableWidgetDelegate : public views::WidgetDelegate {
  public:
   ResizableWidgetDelegate(views::Widget* widget) {
@@ -135,18 +116,16 @@
 // Creates a new FramePainter with empty buttons. Caller owns the memory.
 FramePainter* CreateTestPainter(Widget* widget) {
   FramePainter* painter = new FramePainter();
-  ImageButton* size_button = new ImageButton(NULL);
-  ImageButton* close_button = new ImageButton(NULL);
-  // Add the buttons to the widget's non-client frame view so they will be
-  // deleted when the widget is destroyed.
   NonClientFrameView* frame_view = widget->non_client_view()->frame_view();
-  frame_view->AddChildView(size_button);
-  frame_view->AddChildView(close_button);
-  painter->Init(widget,
-                NULL,
-                size_button,
-                close_button,
-                FramePainter::SIZE_BUTTON_MAXIMIZES);
+  ash::FrameCaptionButtonContainerView* container =
+      new ash::FrameCaptionButtonContainerView(
+          frame_view,
+          widget,
+          ash::FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
+  // Add the container to the widget's non-client frame view so that it will be
+  // deleted when the widget is destroyed.
+  frame_view->AddChildView(container);
+  painter->Init(widget, NULL, container);
   return painter;
 }
 
@@ -251,69 +230,6 @@
   EXPECT_TRUE(root->GetProperty(internal::kSoloWindowHeaderKey));
 }
 
-TEST_F(FramePainterTest, LayoutHeader) {
-  scoped_ptr<Widget> widget(CreateTestWidget());
-  ImageButton size_button(NULL);
-  ImageButton close_button(NULL);
-  NonClientFrameView* frame_view = widget->non_client_view()->frame_view();
-  frame_view->AddChildView(&size_button);
-  frame_view->AddChildView(&close_button);
-  scoped_ptr<FramePainter> painter(new FramePainter);
-  painter->Init(widget.get(),
-                NULL,
-                &size_button,
-                &close_button,
-                FramePainter::SIZE_BUTTON_MAXIMIZES);
-  widget->Show();
-
-  // Basic layout.
-  painter->LayoutHeader(frame_view, false);
-  EXPECT_TRUE(ImagesMatch(&close_button,
-                          IDR_AURA_WINDOW_CLOSE,
-                          IDR_AURA_WINDOW_CLOSE_H,
-                          IDR_AURA_WINDOW_CLOSE_P));
-  EXPECT_TRUE(ImagesMatch(&size_button,
-                          IDR_AURA_WINDOW_MAXIMIZE,
-                          IDR_AURA_WINDOW_MAXIMIZE_H,
-                          IDR_AURA_WINDOW_MAXIMIZE_P));
-
-  // Shorter layout.
-  painter->LayoutHeader(frame_view, true);
-  EXPECT_TRUE(ImagesMatch(&close_button,
-                          IDR_AURA_WINDOW_MAXIMIZED_CLOSE,
-                          IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H,
-                          IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P));
-  EXPECT_TRUE(ImagesMatch(&size_button,
-                          IDR_AURA_WINDOW_MAXIMIZED_RESTORE,
-                          IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H,
-                          IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P));
-
-  // Maximized shorter layout.
-  widget->Maximize();
-  painter->LayoutHeader(frame_view, true);
-  EXPECT_TRUE(ImagesMatch(&close_button,
-                          IDR_AURA_WINDOW_MAXIMIZED_CLOSE2,
-                          IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H,
-                          IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P));
-  EXPECT_TRUE(ImagesMatch(&size_button,
-                          IDR_AURA_WINDOW_MAXIMIZED_RESTORE2,
-                          IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H,
-                          IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P));
-
-  // Fullscreen can show the buttons during an immersive reveal, so it should
-  // use the same images as maximized.
-  widget->SetFullscreen(true);
-  painter->LayoutHeader(frame_view, true);
-  EXPECT_TRUE(ImagesMatch(&close_button,
-                          IDR_AURA_WINDOW_MAXIMIZED_CLOSE2,
-                          IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H,
-                          IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P));
-  EXPECT_TRUE(ImagesMatch(&size_button,
-                          IDR_AURA_WINDOW_MAXIMIZED_RESTORE2,
-                          IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H,
-                          IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P));
-}
-
 TEST_F(FramePainterTest, UseSoloWindowHeader) {
   // Create a widget and a painter for it.
   scoped_ptr<Widget> w1(CreateTestWidget());
@@ -660,15 +576,11 @@
 TEST_F(FramePainterTest, TitleIconAlignment) {
   scoped_ptr<Widget> w(CreateTestWidget());
   FramePainter p;
-  ImageButton size(NULL);
-  ImageButton close(NULL);
+  ash::FrameCaptionButtonContainerView container(NULL, w.get(),
+      ash::FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
   views::View window_icon;
   window_icon.SetBounds(0, 0, 16, 16);
-  p.Init(w.get(),
-         &window_icon,
-         &size,
-         &close,
-         FramePainter::SIZE_BUTTON_MAXIMIZES);
+  p.Init(w.get(), &window_icon, &container);
   w->SetBounds(gfx::Rect(0, 0, 500, 500));
   w->Show();
 
diff --git a/ash/wm/image_cursors.cc b/ash/wm/image_cursors.cc
index 33b3582..e63bece 100644
--- a/ash/wm/image_cursors.cc
+++ b/ash/wm/image_cursors.cc
@@ -56,7 +56,7 @@
   ui::kCursorProgress
 };
 
-ImageCursors::ImageCursors() : scale_(1.f) {
+ImageCursors::ImageCursors() : scale_(1.f), cursor_set_(ui::CURSOR_SET_NORMAL) {
 }
 
 ImageCursors::~ImageCursors() {
@@ -97,7 +97,8 @@
   for (size_t i = 0; i < arraysize(kImageCursorIds); ++i) {
     int resource_id = -1;
     gfx::Point hot_point;
-    bool success = ui::GetCursorDataFor(kImageCursorIds[i],
+    bool success = ui::GetCursorDataFor(cursor_set_,
+                                        kImageCursorIds[i],
                                         device_scale_factor,
                                         &resource_id,
                                         &hot_point);
@@ -107,7 +108,8 @@
   for (size_t i = 0; i < arraysize(kAnimatedCursorIds); ++i) {
     int resource_id = -1;
     gfx::Point hot_point;
-    bool success = ui::GetAnimatedCursorDataFor(kAnimatedCursorIds[i],
+    bool success = ui::GetAnimatedCursorDataFor(cursor_set_,
+                                                kAnimatedCursorIds[i],
                                                 device_scale_factor,
                                                 &resource_id,
                                                 &hot_point);
@@ -133,6 +135,16 @@
   }
 }
 
+void ImageCursors::SetCursorSet(ui::CursorSetType cursor_set) {
+  if (cursor_set_ == cursor_set)
+    return;
+
+  cursor_set_ = cursor_set;
+
+  if (cursor_loader_.get())
+    ReloadCursors();
+}
+
 void ImageCursors::SetPlatformCursor(gfx::NativeCursor* cursor) {
   cursor_loader_->SetPlatformCursor(cursor);
 }
diff --git a/ash/wm/image_cursors.h b/ash/wm/image_cursors.h
index 72c004f..fafc10a 100644
--- a/ash/wm/image_cursors.h
+++ b/ash/wm/image_cursors.h
@@ -40,6 +40,9 @@
   // Sets the scale of the mouse cursor icon.
   void SetScale(float scale);
 
+  // Sets the type of the mouse cursor icon.
+  void SetCursorSet(ui::CursorSetType cursor_set);
+
   // Sets the platform cursor based on the native type of |cursor|.
   void SetPlatformCursor(gfx::NativeCursor* cursor);
 
@@ -49,6 +52,7 @@
 
   scoped_ptr<ui::CursorLoader> cursor_loader_;
   float scale_;
+  ui::CursorSetType cursor_set_;
 
   DISALLOW_COPY_AND_ASSIGN(ImageCursors);
 };
diff --git a/ash/wm/overview/window_overview.cc b/ash/wm/overview/window_overview.cc
new file mode 100644
index 0000000..ea139a2
--- /dev/null
+++ b/ash/wm/overview/window_overview.cc
@@ -0,0 +1,245 @@
+// 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 "ash/wm/overview/window_overview.h"
+
+#include <algorithm>
+
+#include "ash/screen_ash.h"
+#include "ash/shell.h"
+#include "ash/shell_window_ids.h"
+#include "ash/wm/overview/window_selector.h"
+#include "ash/wm/overview/window_selector_window.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/window.h"
+#include "ui/base/events/event.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/views/widget/widget.h"
+
+namespace ash {
+
+namespace {
+
+const float kCardAspectRatio = 4.0f / 3.0f;
+const int kWindowMargin = 30;
+const int kMinCardsMajor = 3;
+const int kOverviewSelectorTransitionMilliseconds = 100;
+const SkColor kWindowOverviewSelectionColor = SK_ColorBLACK;
+const float kWindowOverviewSelectionOpacity = 0.5f;
+const int kWindowOverviewSelectionPadding = 15;
+
+// A comparator for locating a given target window.
+struct WindowSelectorWindowComparator
+    : public std::unary_function<WindowSelectorWindow*, bool> {
+  explicit WindowSelectorWindowComparator(const aura::Window* target_window)
+      : target(target_window) {
+  }
+
+  bool operator()(const WindowSelectorWindow* window) const {
+    return target == window->window();
+  }
+
+  const aura::Window* target;
+};
+
+}  // namespace
+
+WindowOverview::WindowOverview(WindowSelector* window_selector,
+                               WindowSelectorWindowList* windows,
+                               aura::RootWindow* single_root_window)
+    : window_selector_(window_selector),
+      windows_(windows),
+      single_root_window_(single_root_window) {
+  PositionWindows();
+  ash::Shell::GetInstance()->AddPreTargetHandler(this);
+}
+
+WindowOverview::~WindowOverview() {
+  ash::Shell::GetInstance()->RemovePreTargetHandler(this);
+}
+
+void WindowOverview::SetSelection(size_t index) {
+  DCHECK_LT(index, windows_->size());
+  gfx::Rect target_bounds = (*windows_)[index]->bounds();
+  target_bounds.Inset(-kWindowOverviewSelectionPadding,
+                      -kWindowOverviewSelectionPadding);
+
+  if (selection_widget_) {
+    // If the selection widget is already active, animate to the new bounds.
+    ui::ScopedLayerAnimationSettings animation_settings(
+        selection_widget_->GetNativeWindow()->layer()->GetAnimator());
+    animation_settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
+        kOverviewSelectorTransitionMilliseconds));
+    animation_settings.SetPreemptionStrategy(
+        ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+    selection_widget_->SetBounds(target_bounds);
+  } else {
+    InitializeSelectionWidget();
+    selection_widget_->SetBounds(target_bounds);
+  }
+}
+
+void WindowOverview::OnWindowsChanged() {
+  PositionWindows();
+}
+
+void WindowOverview::OnEvent(ui::Event* event) {
+  // If the event is targetted at any of the windows in the overview, then
+  // prevent it from propagating.
+  aura::Window* target = static_cast<aura::Window*>(event->target());
+  for (WindowSelectorWindowList::iterator iter = windows_->begin();
+       iter != windows_->end(); ++iter) {
+    if ((*iter)->Contains(target)) {
+      // TODO(flackr): StopPropogation prevents generation of gesture events.
+      // We should find a better way to prevent events from being delivered to
+      // the window, perhaps a transparent window in front of the target window
+      // or using EventClientImpl::CanProcessEventsWithinSubtree.
+      event->StopPropagation();
+      break;
+    }
+  }
+
+  // This object may not be valid after this call as a selection event can
+  // trigger deletion of the window selector.
+  ui::EventHandler::OnEvent(event);
+}
+
+void WindowOverview::OnKeyEvent(ui::KeyEvent* event) {
+  if (event->type() != ui::ET_KEY_PRESSED)
+    return;
+  if (event->key_code() == ui::VKEY_ESCAPE)
+    window_selector_->CancelSelection();
+}
+
+void WindowOverview::OnMouseEvent(ui::MouseEvent* event) {
+  if (event->type() != ui::ET_MOUSE_RELEASED)
+    return;
+  WindowSelectorWindow* target = GetEventTarget(event);
+  if (!target)
+    return;
+
+  window_selector_->SelectWindow(target->window());
+}
+
+void WindowOverview::OnTouchEvent(ui::TouchEvent* event) {
+  if (event->type() != ui::ET_TOUCH_PRESSED)
+    return;
+  WindowSelectorWindow* target = GetEventTarget(event);
+  if (!target)
+    return;
+
+  window_selector_->SelectWindow(target->window());
+}
+
+WindowSelectorWindow* WindowOverview::GetEventTarget(ui::LocatedEvent* event) {
+  aura::Window* target = static_cast<aura::Window*>(event->target());
+  // If the target window doesn't actually contain the event location (i.e.
+  // mouse down over the window and mouse up elsewhere) then do not select the
+  // window.
+  if (!target->HitTest(event->location()))
+    return NULL;
+
+  for (WindowSelectorWindowList::iterator iter = windows_->begin();
+       iter != windows_->end(); ++iter) {
+    if ((*iter)->Contains(target))
+      return *iter;
+  }
+  return NULL;
+}
+
+void WindowOverview::PositionWindows() {
+  if (single_root_window_) {
+    std::vector<WindowSelectorWindow*> windows;
+    for (WindowSelectorWindowList::iterator iter = windows_->begin();
+         iter != windows_->end(); ++iter) {
+      windows.push_back(*iter);
+    }
+    PositionWindowsOnRoot(single_root_window_, windows);
+  } else {
+    Shell::RootWindowList root_window_list = Shell::GetAllRootWindows();
+    for (size_t i = 0; i < root_window_list.size(); ++i)
+      PositionWindowsFromRoot(root_window_list[i]);
+  }
+}
+
+void WindowOverview::PositionWindowsFromRoot(aura::RootWindow* root_window) {
+  std::vector<WindowSelectorWindow*> windows;
+  for (WindowSelectorWindowList::iterator iter = windows_->begin();
+       iter != windows_->end(); ++iter) {
+    if ((*iter)->window()->GetRootWindow() == root_window)
+      windows.push_back(*iter);
+  }
+  PositionWindowsOnRoot(root_window, windows);
+}
+
+void WindowOverview::PositionWindowsOnRoot(
+    aura::RootWindow* root_window,
+    const std::vector<WindowSelectorWindow*>& windows) {
+  if (windows.empty())
+    return;
+
+  gfx::Size window_size;
+  gfx::Rect total_bounds = ScreenAsh::ConvertRectToScreen(root_window,
+      ScreenAsh::GetDisplayWorkAreaBoundsInParent(
+      Shell::GetContainer(root_window,
+                          internal::kShellWindowId_DefaultContainer)));
+
+  // Find the minimum number of windows per row that will fit all of the
+  // windows on screen.
+  size_t columns = std::max(
+      total_bounds.width() > total_bounds.height() ? kMinCardsMajor : 1,
+      static_cast<int>(ceil(sqrt(total_bounds.width() * windows.size() /
+                                 (kCardAspectRatio * total_bounds.height())))));
+  size_t rows = ((windows.size() + columns - 1) / columns);
+  window_size.set_width(std::min(
+      static_cast<int>(total_bounds.width() / columns),
+      static_cast<int>(total_bounds.height() * kCardAspectRatio / rows)));
+  window_size.set_height(window_size.width() / kCardAspectRatio);
+
+  // Calculate the X and Y offsets necessary to center the grid.
+  int x_offset = total_bounds.x() + ((windows.size() >= columns ? 0 :
+      (columns - windows.size()) * window_size.width()) +
+      (total_bounds.width() - columns * window_size.width())) / 2;
+  int y_offset = total_bounds.y() + (total_bounds.height() -
+      rows * window_size.height()) / 2;
+  for (size_t i = 0; i < windows.size(); ++i) {
+    gfx::Transform transform;
+    int column = i % columns;
+    int row = i / columns;
+    gfx::Rect target_bounds(window_size.width() * column + x_offset,
+                            window_size.height() * row + y_offset,
+                            window_size.width(),
+                            window_size.height());
+    target_bounds.Inset(kWindowMargin, kWindowMargin);
+    windows[i]->TransformToFitBounds(root_window, target_bounds);
+  }
+}
+
+void WindowOverview::InitializeSelectionWidget() {
+  selection_widget_.reset(new views::Widget);
+  views::Widget::InitParams params;
+  params.type = views::Widget::InitParams::TYPE_POPUP;
+  params.can_activate = false;
+  params.keep_on_top = false;
+  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+  params.opacity = views::Widget::InitParams::OPAQUE_WINDOW;
+  params.parent = Shell::GetContainer(
+      single_root_window_,
+      internal::kShellWindowId_DefaultContainer);
+  params.accept_events = false;
+  selection_widget_->set_focus_on_creation(false);
+  selection_widget_->Init(params);
+  views::View* content_view = new views::View;
+  content_view->set_background(
+      views::Background::CreateSolidBackground(kWindowOverviewSelectionColor));
+  selection_widget_->SetContentsView(content_view);
+  selection_widget_->GetNativeWindow()->parent()->StackChildAtBottom(
+      selection_widget_->GetNativeWindow());
+  selection_widget_->Show();
+  selection_widget_->GetNativeWindow()->layer()->SetOpacity(
+      kWindowOverviewSelectionOpacity);
+}
+
+}  // namespace ash
diff --git a/ash/wm/overview/window_overview.h b/ash/wm/overview/window_overview.h
new file mode 100644
index 0000000..640de03
--- /dev/null
+++ b/ash/wm/overview/window_overview.h
@@ -0,0 +1,98 @@
+// 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 ASH_WM_OVERVIEW_WINDOW_OVERVIEW_H_
+#define ASH_WM_OVERVIEW_WINDOW_OVERVIEW_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "ui/base/events/event_handler.h"
+
+namespace aura {
+class RootWindow;
+}
+
+namespace ui {
+class LocatedEvent;
+}
+
+namespace views {
+class Widget;
+}
+
+namespace ash {
+
+class WindowSelector;
+class WindowSelectorWindow;
+
+// The WindowOverview shows a grid of all of your windows and allows selecting
+// a window by clicking or tapping on it. It also displays a selection widget
+// used to indicate the current selection when alt-tabbing between windows.
+class WindowOverview : public ui::EventHandler {
+ public:
+  typedef ScopedVector<WindowSelectorWindow> WindowSelectorWindowList;
+
+  // Enters an overview mode displaying |windows| and dispatches methods
+  // on |window_selector| when a window is selected or selection is canceled.
+  // If |single_root_window| is not NULL, all windows will be positioned on the
+  // given root window.
+  WindowOverview(WindowSelector* window_selector,
+                 WindowSelectorWindowList* windows,
+                 aura::RootWindow* single_root_window);
+  virtual ~WindowOverview();
+
+  // Sets the selected window to be the window in position |index|.
+  void SetSelection(size_t index);
+
+  // Dispatched when the list of windows has changed.
+  void OnWindowsChanged();
+
+  // ui::EventHandler:
+  virtual void OnEvent(ui::Event* event) OVERRIDE;
+  virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
+  virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
+  virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE;
+
+ private:
+  // Returns the target of |event| or NULL if the event is not targeted at
+  // any of the windows in the selector.
+  WindowSelectorWindow* GetEventTarget(ui::LocatedEvent* event);
+
+  // Position all of the windows based on the current selection mode.
+  void PositionWindows();
+  // Position all of the windows from |root_window| on |root_window|.
+  void PositionWindowsFromRoot(aura::RootWindow* root_window);
+  // Position all of the |windows| to fit on the |root_window|.
+  void PositionWindowsOnRoot(aura::RootWindow* root_window,
+                             const std::vector<WindowSelectorWindow*>& windows);
+
+  void InitializeSelectionWidget();
+
+  // Updates the selection widget's location to the currently selected window.
+  // If |animate| the transition to the new location is animated.
+  void UpdateSelectionLocation(bool animate);
+
+  // Weak pointer to the window selector which owns this class.
+  WindowSelector* window_selector_;
+
+  // A weak pointer to the collection of windows in the overview wrapped by a
+  // helper class which restores their state and helps transform them to other
+  // root windows.
+  WindowSelectorWindowList* windows_;
+
+  // Widget indicating which window is currently selected.
+  scoped_ptr<views::Widget> selection_widget_;
+
+  // If NULL, each root window displays an overview of the windows in that
+  // display. Otherwise, all windows are in a single overview on
+  // |single_root_window_|.
+  aura::RootWindow* single_root_window_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowOverview);
+};
+
+}  // namespace ash
+
+#endif  // ASH_WM_OVERVIEW_WINDOW_OVERVIEW_H_
diff --git a/ash/wm/overview/window_selector.cc b/ash/wm/overview/window_selector.cc
index 91aca6d..2124172 100644
--- a/ash/wm/overview/window_selector.cc
+++ b/ash/wm/overview/window_selector.cc
@@ -6,31 +6,18 @@
 
 #include <algorithm>
 
-#include "ash/screen_ash.h"
 #include "ash/shell.h"
-#include "ash/shell_window_ids.h"
+#include "ash/wm/overview/window_overview.h"
 #include "ash/wm/overview/window_selector_delegate.h"
 #include "ash/wm/overview/window_selector_window.h"
-#include "ash/wm/window_util.h"
-#include "base/memory/scoped_ptr.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "ui/aura/root_window.h"
+#include "base/timer/timer.h"
 #include "ui/aura/window.h"
-#include "ui/base/events/event.h"
-#include "ui/compositor/scoped_layer_animation_settings.h"
-#include "ui/views/widget/widget.h"
 
 namespace ash {
 
 namespace {
 
-const float kCardAspectRatio = 4.0f / 3.0f;
-const int kWindowMargin = 30;
-const int kMinCardsMajor = 3;
-const int kOverviewSelectorTransitionMilliseconds = 100;
-const SkColor kWindowSelectorSelectionColor = SK_ColorBLACK;
-const float kWindowSelectorSelectionOpacity = 0.5f;
-const int kWindowSelectorSelectionPadding = 15;
+const int kOverviewDelayOnCycleMilliseconds = 300;
 
 // A comparator for locating a given target window.
 struct WindowSelectorWindowComparator
@@ -52,78 +39,61 @@
                                WindowSelector::Mode mode,
                                WindowSelectorDelegate* delegate)
     : mode_(mode),
+      start_overview_timer_(FROM_HERE,
+          base::TimeDelta::FromMilliseconds(kOverviewDelayOnCycleMilliseconds),
+          this, &WindowSelector::StartOverview),
       delegate_(delegate),
-      selected_window_(0),
-      selection_root_(NULL) {
+      selected_window_(0) {
   DCHECK(delegate_);
   for (size_t i = 0; i < windows.size(); ++i) {
     windows[i]->AddObserver(this);
     windows_.push_back(new WindowSelectorWindow(windows[i]));
   }
   if (mode == WindowSelector::CYCLE)
-    selection_root_ = ash::Shell::GetActiveRootWindow();
-  PositionWindows();
-  ash::Shell::GetInstance()->AddPreTargetHandler(this);
+    start_overview_timer_.Reset();
+  else
+    StartOverview();
 }
 
 WindowSelector::~WindowSelector() {
   for (size_t i = 0; i < windows_.size(); i++) {
     windows_[i]->window()->RemoveObserver(this);
   }
-  ash::Shell::GetInstance()->RemovePreTargetHandler(this);
 }
 
 void WindowSelector::Step(WindowSelector::Direction direction) {
-  DCHECK(windows_.size() > 0);
-  if (!selection_widget_)
-    InitializeSelectionWidget();
+  DCHECK_EQ(CYCLE, mode_);
+  DCHECK(!windows_.empty());
   selected_window_ = (selected_window_ + windows_.size() +
       (direction == WindowSelector::FORWARD ? 1 : -1)) % windows_.size();
-  UpdateSelectionLocation(true);
+  if (window_overview_) {
+    window_overview_->SetSelection(selected_window_);
+  } else {
+    aura::Window* current_window = windows_[selected_window_]->window();
+    current_window->Show();
+    current_window->SetTransform(gfx::Transform());
+    current_window->parent()->StackChildAtTop(current_window);
+    start_overview_timer_.Reset();
+  }
 }
 
 void WindowSelector::SelectWindow() {
-  delegate_->OnWindowSelected(windows_[selected_window_]->window());
+  SelectWindow(windows_[selected_window_]->window());
 }
 
-void WindowSelector::OnEvent(ui::Event* event) {
-  // If the event is targetted at any of the windows in the overview, then
-  // prevent it from propagating.
-  aura::Window* target = static_cast<aura::Window*>(event->target());
-  for (size_t i = 0; i < windows_.size(); ++i) {
-    if (windows_[i]->Contains(target)) {
-      // TODO(flackr): StopPropogation prevents generation of gesture events.
-      // We should find a better way to prevent events from being delivered to
-      // the window, perhaps a transparent window in front of the target window
-      // or using EventClientImpl::CanProcessEventsWithinSubtree.
-      event->StopPropagation();
-      break;
-    }
-  }
-
-  // This object may not be valid after this call as a selection event can
-  // trigger deletion of the window selector.
-  ui::EventHandler::OnEvent(event);
+void WindowSelector::SelectWindow(aura::Window* window) {
+  ScopedVector<WindowSelectorWindow>::iterator iter =
+      std::find_if(windows_.begin(), windows_.end(),
+                   WindowSelectorWindowComparator(window));
+  DCHECK(iter != windows_.end());
+  // The selected window should not be minimized when window selection is
+  // ended.
+  (*iter)->RestoreWindowOnExit();
+  delegate_->OnWindowSelected(window);
 }
 
-void WindowSelector::OnMouseEvent(ui::MouseEvent* event) {
-  if (event->type() != ui::ET_MOUSE_RELEASED)
-    return;
-  WindowSelectorWindow* target = GetEventTarget(event);
-  if (!target)
-    return;
-
-  HandleSelectionEvent(target);
-}
-
-void WindowSelector::OnTouchEvent(ui::TouchEvent* event) {
-  if (event->type() != ui::ET_TOUCH_PRESSED)
-    return;
-  WindowSelectorWindow* target = GetEventTarget(event);
-  if (!target)
-    return;
-
-  HandleSelectionEvent(target);
+void WindowSelector::CancelSelection() {
+  delegate_->OnSelectionCanceled();
 }
 
 void WindowSelector::OnWindowDestroyed(aura::Window* window) {
@@ -135,151 +105,25 @@
   (*iter)->OnWindowDestroyed();
   windows_.erase(iter);
   if (windows_.empty()) {
-    delegate_->OnSelectionCanceled();
+    CancelSelection();
     return;
   }
-  if (selected_window_ >= deleted_index) {
+  window_overview_->OnWindowsChanged();
+  if (mode_ == CYCLE && selected_window_ >= deleted_index) {
     if (selected_window_ > deleted_index)
       selected_window_--;
     selected_window_ = selected_window_ % windows_.size();
-    UpdateSelectionLocation(true);
-  }
-
-  PositionWindows();
-}
-
-WindowSelectorWindow* WindowSelector::GetEventTarget(ui::LocatedEvent* event) {
-  aura::Window* target = static_cast<aura::Window*>(event->target());
-  // If the target window doesn't actually contain the event location (i.e.
-  // mouse down over the window and mouse up elsewhere) then do not select the
-  // window.
-  if (!target->HitTest(event->location()))
-    return NULL;
-
-  for (size_t i = 0; i < windows_.size(); i++) {
-    if (windows_[i]->Contains(target))
-      return windows_[i];
-  }
-  return NULL;
-}
-
-void WindowSelector::HandleSelectionEvent(WindowSelectorWindow* target) {
-  // The selected window should not be minimized when window selection is
-  // ended.
-  target->RestoreWindowOnExit();
-  delegate_->OnWindowSelected(target->window());
-}
-
-void WindowSelector::PositionWindows() {
-  if (selection_root_) {
-    DCHECK_EQ(mode_, CYCLE);
-    std::vector<WindowSelectorWindow*> windows;
-    for (size_t i = 0; i < windows_.size(); ++i)
-      windows.push_back(windows_[i]);
-    PositionWindowsOnRoot(selection_root_, windows);
-  } else {
-    DCHECK_EQ(mode_, OVERVIEW);
-    Shell::RootWindowList root_window_list = Shell::GetAllRootWindows();
-    for (size_t i = 0; i < root_window_list.size(); ++i)
-      PositionWindowsFromRoot(root_window_list[i]);
+    if (window_overview_)
+      window_overview_->SetSelection(selected_window_);
   }
 }
 
-void WindowSelector::PositionWindowsFromRoot(aura::RootWindow* root_window) {
-  std::vector<WindowSelectorWindow*> windows;
-  for (size_t i = 0; i < windows_.size(); ++i) {
-    if (windows_[i]->window()->GetRootWindow() == root_window)
-      windows.push_back(windows_[i]);
-  }
-  PositionWindowsOnRoot(root_window, windows);
-}
-
-void WindowSelector::PositionWindowsOnRoot(
-    aura::RootWindow* root_window,
-    const std::vector<WindowSelectorWindow*>& windows) {
-  if (windows.empty())
-    return;
-
-  gfx::Size window_size;
-  gfx::Rect total_bounds = ScreenAsh::ConvertRectToScreen(root_window,
-      ScreenAsh::GetDisplayWorkAreaBoundsInParent(
-      Shell::GetContainer(root_window,
-                          internal::kShellWindowId_DefaultContainer)));
-
-  // Find the minimum number of windows per row that will fit all of the
-  // windows on screen.
-  size_t columns = std::max(
-      total_bounds.width() > total_bounds.height() ? kMinCardsMajor : 1,
-      static_cast<int>(ceil(sqrt(total_bounds.width() * windows.size() /
-                                 (kCardAspectRatio * total_bounds.height())))));
-  size_t rows = ((windows.size() + columns - 1) / columns);
-  window_size.set_width(std::min(
-      static_cast<int>(total_bounds.width() / columns),
-      static_cast<int>(total_bounds.height() * kCardAspectRatio / rows)));
-  window_size.set_height(window_size.width() / kCardAspectRatio);
-
-  // Calculate the X and Y offsets necessary to center the grid.
-  int x_offset = total_bounds.x() + ((windows.size() >= columns ? 0 :
-      (columns - windows.size()) * window_size.width()) +
-      (total_bounds.width() - columns * window_size.width())) / 2;
-  int y_offset = total_bounds.y() + (total_bounds.height() -
-      rows * window_size.height()) / 2;
-  for (size_t i = 0; i < windows.size(); ++i) {
-    gfx::Transform transform;
-    int column = i % columns;
-    int row = i / columns;
-    gfx::Rect target_bounds(window_size.width() * column + x_offset,
-                            window_size.height() * row + y_offset,
-                            window_size.width(),
-                            window_size.height());
-    target_bounds.Inset(kWindowMargin, kWindowMargin);
-    windows[i]->TransformToFitBounds(root_window, target_bounds);
-  }
-}
-
-void WindowSelector::InitializeSelectionWidget() {
-  selection_widget_.reset(new views::Widget);
-  views::Widget::InitParams params;
-  params.type = views::Widget::InitParams::TYPE_POPUP;
-  params.can_activate = false;
-  params.keep_on_top = false;
-  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
-  params.opacity = views::Widget::InitParams::OPAQUE_WINDOW;
-  params.parent = Shell::GetContainer(
-      selection_root_,
-      internal::kShellWindowId_DefaultContainer);
-  params.accept_events = false;
-  selection_widget_->set_focus_on_creation(false);
-  selection_widget_->Init(params);
-  views::View* content_view = new views::View;
-  content_view->set_background(
-      views::Background::CreateSolidBackground(kWindowSelectorSelectionColor));
-  selection_widget_->SetContentsView(content_view);
-  UpdateSelectionLocation(false);
-  selection_widget_->GetNativeWindow()->parent()->StackChildAtBottom(
-      selection_widget_->GetNativeWindow());
-  selection_widget_->Show();
-  selection_widget_->GetNativeWindow()->layer()->SetOpacity(
-      kWindowSelectorSelectionOpacity);
-}
-
-void WindowSelector::UpdateSelectionLocation(bool animate) {
-  if (!selection_widget_)
-    return;
-  gfx::Rect target_bounds = windows_[selected_window_]->bounds();
-  target_bounds.Inset(-kWindowSelectorSelectionPadding,
-                      -kWindowSelectorSelectionPadding);
-  if (animate) {
-    ui::ScopedLayerAnimationSettings animation_settings(
-        selection_widget_->GetNativeWindow()->layer()->GetAnimator());
-    animation_settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
-        kOverviewSelectorTransitionMilliseconds));
-    animation_settings.SetPreemptionStrategy(
-        ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
-    selection_widget_->SetBounds(target_bounds);
-  } else {
-    selection_widget_->SetBounds(target_bounds);
-  }
+void WindowSelector::StartOverview() {
+  DCHECK(!window_overview_);
+  window_overview_.reset(new WindowOverview(this, &windows_,
+      mode_ == CYCLE ? Shell::GetActiveRootWindow() : NULL));
+  if (mode_ == CYCLE)
+    window_overview_->SetSelection(selected_window_);
 }
 
 }  // namespace ash
diff --git a/ash/wm/overview/window_selector.h b/ash/wm/overview/window_selector.h
index 79de36f..8e6f01c 100644
--- a/ash/wm/overview/window_selector.h
+++ b/ash/wm/overview/window_selector.h
@@ -7,12 +7,12 @@
 
 #include <vector>
 
+#include "ash/ash_export.h"
 #include "base/compiler_specific.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/scoped_vector.h"
+#include "base/timer/timer.h"
 #include "ui/aura/window_observer.h"
-#include "ui/base/events/event_handler.h"
-#include "ui/gfx/transform.h"
 
 namespace aura {
 class RootWindow;
@@ -22,20 +22,20 @@
 class LocatedEvent;
 }
 
-namespace views {
-class Widget;
-}
-
 namespace ash {
 
+namespace internal {
+class WindowSelectorTest;
+}
+
+class WindowOverview;
 class WindowSelectorDelegate;
 class WindowSelectorWindow;
 
-// The WindowSelector shows a grid of all of your windows and allows selecting
-// a window by clicking or tapping on it (OVERVIEW mode) or by alt-tabbing to
-// it (CYCLE mode).
-class WindowSelector : public ui::EventHandler,
-                       public aura::WindowObserver {
+// The WindowSelector allows selecting a window by alt-tabbing (CYCLE mode) or
+// by clicking or tapping on it (OVERVIEW mode). A WindowOverview will be shown
+// in OVERVIEW mode or if the user lingers on a window while alt tabbing.
+class ASH_EXPORT WindowSelector : public aura::WindowObserver {
  public:
   enum Direction {
     FORWARD,
@@ -56,40 +56,25 @@
   // Step to the next window in |direction|.
   void Step(Direction direction);
 
-  // Select the current window.
+  // Choose the currently selected window.
   void SelectWindow();
 
-  Mode mode() { return mode_; }
+  // Choose |window| from the available windows to select.
+  void SelectWindow(aura::Window* window);
 
-  // ui::EventHandler:
-  virtual void OnEvent(ui::Event* event) OVERRIDE;
-  virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
-  virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE;
+  // Cancels window selection.
+  void CancelSelection();
+
+  Mode mode() { return mode_; }
 
   // aura::WindowObserver:
   virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
 
  private:
-  // Returns the target of |event| or NULL if the event is not targeted at
-  // any of the windows in the selector.
-  WindowSelectorWindow* GetEventTarget(ui::LocatedEvent* event);
+  friend class internal::WindowSelectorTest;
 
-  // Handles a selection event for |target|.
-  void HandleSelectionEvent(WindowSelectorWindow* target);
-
-  // Position all of the windows based on the current selection mode.
-  void PositionWindows();
-  // Position all of the windows from |root_window| on |root_window|.
-  void PositionWindowsFromRoot(aura::RootWindow* root_window);
-  // Position all of the |windows| to fit on the |root_window|.
-  void PositionWindowsOnRoot(aura::RootWindow* root_window,
-                             const std::vector<WindowSelectorWindow*>& windows);
-
-  void InitializeSelectionWidget();
-
-  // Updates the selection widget's location to the currently selected window.
-  // If |animate| the transition to the new location is animated.
-  void UpdateSelectionLocation(bool animate);
+  // Begins positioning windows such that all windows are visible on the screen.
+  void StartOverview();
 
   // The collection of windows in the overview wrapped by a helper class which
   // restores their state and helps transform them to other root windows.
@@ -98,6 +83,9 @@
   // The window selection mode.
   Mode mode_;
 
+  base::DelayTimer<WindowSelector> start_overview_timer_;
+  scoped_ptr<WindowOverview> window_overview_;
+
   // Weak pointer to the selector delegate which will be called when a
   // selection is made.
   WindowSelectorDelegate* delegate_;
@@ -105,13 +93,6 @@
   // Index of the currently selected window if the mode is CYCLE.
   size_t selected_window_;
 
-  // Widget indicating which window is currently selected.
-  scoped_ptr<views::Widget> selection_widget_;
-
-  // In CYCLE mode, the root window in which selection is taking place.
-  // NULL otherwise.
-  aura::RootWindow* selection_root_;
-
   DISALLOW_COPY_AND_ASSIGN(WindowSelector);
 };
 
diff --git a/ash/wm/overview/window_selector_controller.cc b/ash/wm/overview/window_selector_controller.cc
index a04669f..9531373 100644
--- a/ash/wm/overview/window_selector_controller.cc
+++ b/ash/wm/overview/window_selector_controller.cc
@@ -9,6 +9,8 @@
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/window_selector.h"
 #include "ash/wm/window_util.h"
+#include "ui/aura/client/focus_client.h"
+#include "ui/aura/root_window.h"
 #include "ui/base/events/event.h"
 #include "ui/base/events/event_handler.h"
 
@@ -50,7 +52,8 @@
 
 }  // namespace
 
-WindowSelectorController::WindowSelectorController() {
+WindowSelectorController::WindowSelectorController()
+    : restore_focus_window_(NULL) {
 }
 
 WindowSelectorController::~WindowSelectorController() {
@@ -65,8 +68,8 @@
 }
 
 void WindowSelectorController::ToggleOverview() {
-  if (window_selector_.get()) {
-    window_selector_.reset();
+  if (IsSelecting()) {
+    OnSelectionCanceled();
   } else {
     std::vector<aura::Window*> windows = ash::Shell::GetInstance()->
         mru_window_tracker()->BuildMruWindowList();
@@ -74,11 +77,8 @@
     if (windows.empty())
       return;
 
-    // Deactivating the window will hide popup windows like the omnibar or
-    // open menus.
-    aura::Window* active_window = wm::GetActiveWindow();
-    if (active_window)
-      wm::DeactivateWindow(active_window);
+    // Removing focus will hide popup windows like the omnibar or open menus.
+    RemoveFocusAndSetRestoreWindow();
     window_selector_.reset(
         new WindowSelector(windows, WindowSelector::OVERVIEW, this));
   }
@@ -93,6 +93,8 @@
     event_handler_.reset(new WindowSelectorEventFilter());
     std::vector<aura::Window*> windows = ash::Shell::GetInstance()->
         mru_window_tracker()->BuildMruWindowList();
+    // Removing focus will hide popup windows like the omnibar or open menus.
+    RemoveFocusAndSetRestoreWindow();
     window_selector_.reset(
         new WindowSelector(windows, WindowSelector::CYCLE, this));
     window_selector_->Step(direction);
@@ -112,11 +114,39 @@
 
 void WindowSelectorController::OnWindowSelected(aura::Window* window) {
   window_selector_.reset();
+  ResetFocusRestoreWindow(false);
   wm::ActivateWindow(window);
 }
 
 void WindowSelectorController::OnSelectionCanceled() {
   window_selector_.reset();
+  ResetFocusRestoreWindow(true);
+}
+
+void WindowSelectorController::OnWindowDestroyed(aura::Window* window) {
+  DCHECK_EQ(window, restore_focus_window_);
+  restore_focus_window_->RemoveObserver(this);
+  restore_focus_window_ = NULL;
+}
+
+void WindowSelectorController::RemoveFocusAndSetRestoreWindow() {
+  aura::client::FocusClient* focus_client = aura::client::GetFocusClient(
+      Shell::GetActiveRootWindow());
+  DCHECK(!restore_focus_window_);
+  restore_focus_window_ = focus_client->GetFocusedWindow();
+  if (restore_focus_window_) {
+    focus_client->FocusWindow(NULL);
+    restore_focus_window_->AddObserver(this);
+  }
+}
+
+void WindowSelectorController::ResetFocusRestoreWindow(bool focus) {
+  if (!restore_focus_window_)
+    return;
+  if (focus)
+    restore_focus_window_->Focus();
+  restore_focus_window_->RemoveObserver(this);
+  restore_focus_window_ = NULL;
 }
 
 }  // namespace ash
diff --git a/ash/wm/overview/window_selector_controller.h b/ash/wm/overview/window_selector_controller.h
index 725e902..7997171 100644
--- a/ash/wm/overview/window_selector_controller.h
+++ b/ash/wm/overview/window_selector_controller.h
@@ -25,12 +25,17 @@
 
 namespace ash {
 
+namespace internal {
+class WindowSelectorTest;
+}
+
 class WindowSelector;
 
 // Manages a window selector which displays an overview of all windows and
 // allows selecting a window to activate it.
 class ASH_EXPORT WindowSelectorController
-    : public WindowSelectorDelegate {
+    : public WindowSelectorDelegate,
+      public aura::WindowObserver {
  public:
   WindowSelectorController();
   virtual ~WindowSelectorController();
@@ -60,10 +65,26 @@
   virtual void OnWindowSelected(aura::Window* window) OVERRIDE;
   virtual void OnSelectionCanceled() OVERRIDE;
 
+  // aura::WindowObserver:
+  virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
+
  private:
+  friend class internal::WindowSelectorTest;
+
+  // Stores the currently focused window and removes focus from it.
+  void RemoveFocusAndSetRestoreWindow();
+
+  // If |focus|, restores focus to the stored window from
+  // RemoveFocusAndSetRestoreWindow. Resets the stored window to NULL.
+  void ResetFocusRestoreWindow(bool focus);
+
   scoped_ptr<WindowSelector> window_selector_;
   scoped_ptr<ui::EventHandler> event_handler_;
 
+  // A weak pointer to the window which was focused on entering overview mode.
+  // If overview mode is canceled the focus should be restored to this window.
+  aura::Window* restore_focus_window_;
+
   DISALLOW_COPY_AND_ASSIGN(WindowSelectorController);
 };
 
diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc
index c36332b..e0baf81 100644
--- a/ash/wm/overview/window_selector_unittest.cc
+++ b/ash/wm/overview/window_selector_unittest.cc
@@ -8,6 +8,7 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/test/shell_test_api.h"
 #include "ash/wm/mru_window_tracker.h"
+#include "ash/wm/overview/window_selector.h"
 #include "ash/wm/overview/window_selector_controller.h"
 #include "ash/wm/window_util.h"
 #include "base/basictypes.h"
@@ -15,6 +16,7 @@
 #include "base/memory/scoped_vector.h"
 #include "base/run_loop.h"
 #include "ui/aura/client/aura_constants.h"
+#include "ui/aura/client/focus_client.h"
 #include "ui/aura/root_window.h"
 #include "ui/aura/test/event_generator.h"
 #include "ui/aura/test/test_window_delegate.h"
@@ -55,6 +57,15 @@
     ash::Shell::GetInstance()->window_selector_controller()->AltKeyReleased();
   }
 
+  void FireOverviewStartTimer() {
+    // Calls the method to start overview mode which is normally called by the
+    // timer. The timer will still fire and call this method triggering the
+    // DCHECK that overview mode was not already started, except that we call
+    // StopCycling before the timer has a chance to fire.
+    ash::Shell::GetInstance()->window_selector_controller()->window_selector_->
+        StartOverview();
+  }
+
   gfx::RectF GetTransformedBounds(aura::Window* window) {
     gfx::RectF bounds(window->layer()->bounds());
     window->layer()->transform().TransformRect(&bounds);
@@ -78,6 +89,11 @@
         IsSelecting();
   }
 
+  aura::Window* GetFocusedWindow() {
+    return aura::client::GetFocusClient(
+        Shell::GetActiveRootWindow())->GetFocusedWindow();
+  }
+
  private:
   aura::test::TestWindowDelegate wd;
 
@@ -93,15 +109,19 @@
   wm::ActivateWindow(window2.get());
   EXPECT_FALSE(wm::IsActiveWindow(window1.get()));
   EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
+  EXPECT_EQ(window2.get(), GetFocusedWindow());
 
-  // In overview mode the windows should no longer overlap.
+  // In overview mode the windows should no longer overlap and focus should
+  // be removed from the window.
   ToggleOverview();
+  EXPECT_EQ(NULL, GetFocusedWindow());
   EXPECT_FALSE(WindowsOverlapping(window1.get(), window2.get()));
 
   // Clicking window 1 should activate it.
   ClickWindow(window1.get());
   EXPECT_TRUE(wm::IsActiveWindow(window1.get()));
   EXPECT_FALSE(wm::IsActiveWindow(window2.get()));
+  EXPECT_EQ(window1.get(), GetFocusedWindow());
 }
 
 // Tests entering overview mode with three windows and cycling through them.
@@ -127,6 +147,41 @@
   EXPECT_TRUE(wm::IsActiveWindow(window3.get()));
 }
 
+// Verifies that overview mode only begins after a delay when cycling.
+TEST_F(WindowSelectorTest, CycleOverviewDelay) {
+  gfx::Rect bounds(0, 0, 400, 400);
+  scoped_ptr<aura::Window> window1(CreateWindow(bounds));
+  scoped_ptr<aura::Window> window2(CreateWindow(bounds));
+  EXPECT_TRUE(WindowsOverlapping(window1.get(), window2.get()));
+
+  // When cycling first starts, the windows will still be overlapping.
+  Cycle(WindowSelector::FORWARD);
+  EXPECT_TRUE(IsSelecting());
+  EXPECT_TRUE(WindowsOverlapping(window1.get(), window2.get()));
+
+  // Once the overview timer fires, the windows should no longer overlap.
+  FireOverviewStartTimer();
+  EXPECT_FALSE(WindowsOverlapping(window1.get(), window2.get()));
+  StopCycling();
+}
+
+// Tests that exiting overview mode without selecting a window restores focus
+// to the previously focused window.
+TEST_F(WindowSelectorTest, CancelRestoresFocus) {
+  gfx::Rect bounds(0, 0, 400, 400);
+  scoped_ptr<aura::Window> window(CreateWindow(bounds));
+  wm::ActivateWindow(window.get());
+  EXPECT_EQ(window.get(), GetFocusedWindow());
+
+  // In overview mode, focus should be removed.
+  ToggleOverview();
+  EXPECT_EQ(NULL, GetFocusedWindow());
+
+  // If canceling overview mode, focus should be restored.
+  ToggleOverview();
+  EXPECT_EQ(window.get(), GetFocusedWindow());
+}
+
 // Tests that overview mode is exited if the last remaining window is destroyed.
 TEST_F(WindowSelectorTest, LastWindowDestroyed) {
   gfx::Rect bounds(0, 0, 400, 400);
diff --git a/ash/wm/overview/window_selector_window.cc b/ash/wm/overview/window_selector_window.cc
index 47c1dda..226c782 100644
--- a/ash/wm/overview/window_selector_window.cc
+++ b/ash/wm/overview/window_selector_window.cc
@@ -153,8 +153,6 @@
       minimized_(window->GetProperty(aura::client::kShowStateKey) ==
                  ui::SHOW_STATE_MINIMIZED),
       original_transform_(window->layer()->GetTargetTransform()) {
-  if (minimized_)
-    window_->Show();
 }
 
 WindowSelectorWindow::~WindowSelectorWindow() {
@@ -162,7 +160,8 @@
     WindowSelectorAnimationSettings animation_settings(window_);
     gfx::Transform transform;
     window_->SetTransform(original_transform_);
-    if (minimized_) {
+    if (minimized_ && window_->GetProperty(aura::client::kShowStateKey) !=
+        ui::SHOW_STATE_MINIMIZED) {
       // Setting opacity 0 and visible false ensures that the property change
       // to SHOW_STATE_MINIMIZED will not animate the window from its original
       // bounds to the minimized position.
@@ -210,6 +209,10 @@
 void WindowSelectorWindow::TransformToFitBounds(
     aura::RootWindow* root_window,
     const gfx::Rect& target_bounds) {
+  if (minimized_ && window_->GetProperty(aura::client::kShowStateKey) ==
+      ui::SHOW_STATE_MINIMIZED) {
+    window_->Show();
+  }
   fit_bounds_ = target_bounds;
   const gfx::Rect bounds = window_->GetBoundsInScreen();
   float scale = std::min(1.0f,
diff --git a/ash/wm/panels/panel_frame_view.cc b/ash/wm/panels/panel_frame_view.cc
index f394dc0..db137fa 100644
--- a/ash/wm/panels/panel_frame_view.cc
+++ b/ash/wm/panels/panel_frame_view.cc
@@ -5,8 +5,8 @@
 #include "ash/wm/panels/panel_frame_view.h"
 
 #include "ash/wm/frame_painter.h"
+#include "ash/wm/workspace/frame_caption_button_container_view.h"
 #include "grit/ash_resources.h"
-#include "grit/ui_strings.h"  // Accessibility names
 #include "third_party/skia/include/core/SkPaint.h"
 #include "ui/base/animation/throb_animation.h"
 #include "ui/base/cursor/cursor.h"
@@ -16,7 +16,6 @@
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/image/image.h"
-#include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/widget/native_widget_aura.h"
 #include "ui/views/widget/widget.h"
@@ -25,12 +24,11 @@
 namespace ash {
 
 // static
-const char PanelFrameView::kViewClassName[] = "ash/wm/panels/PanelFrameView";
+const char PanelFrameView::kViewClassName[] = "PanelFrameView";
 
 PanelFrameView::PanelFrameView(views::Widget* frame, FrameType frame_type)
     : frame_(frame),
-      close_button_(NULL),
-      minimize_button_(NULL),
+      caption_button_container_(NULL),
       window_icon_(NULL),
       title_font_(gfx::Font(views::NativeWidgetAura::GetWindowTitleFont())) {
   if (frame_type != FRAME_NONE)
@@ -47,23 +45,16 @@
 void PanelFrameView::InitFramePainter() {
   frame_painter_.reset(new FramePainter);
 
-  close_button_ = new views::ImageButton(this);
-  close_button_->SetAccessibleName(
-      l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
-  AddChildView(close_button_);
-
-  minimize_button_ = new views::ImageButton(this);
-  minimize_button_->SetAccessibleName(
-      l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE));
-  AddChildView(minimize_button_);
+  caption_button_container_ = new FrameCaptionButtonContainerView(this, frame_,
+      FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
+  AddChildView(caption_button_container_);
 
   if (frame_->widget_delegate()->ShouldShowWindowIcon()) {
     window_icon_ = new views::ImageView();
     AddChildView(window_icon_);
   }
 
-  frame_painter_->Init(frame_, window_icon_, minimize_button_, close_button_,
-                       FramePainter::SIZE_BUTTON_MINIMIZES);
+  frame_painter_->Init(frame_, window_icon_, caption_button_container_);
 }
 
 gfx::Size PanelFrameView::GetMinimumSize() {
@@ -133,7 +124,7 @@
   if (!frame_painter_)
     return bounds();
   return frame_painter_->GetBoundsForClientView(
-      close_button_->bounds().bottom(),
+      caption_button_container_->bounds().bottom(),
       bounds());
 }
 
@@ -142,15 +133,7 @@
   if (!frame_painter_)
     return client_bounds;
   return frame_painter_->GetWindowBoundsForClientBounds(
-      close_button_->bounds().bottom(), client_bounds);
-}
-
-void PanelFrameView::ButtonPressed(views::Button* sender,
-                                   const ui::Event& event) {
-  if (sender == close_button_)
-    GetWidget()->Close();
-  if (sender == minimize_button_)
-    GetWidget()->Minimize();
+      caption_button_container_->bounds().bottom(), client_bounds);
 }
 
 }  // namespace ash
diff --git a/ash/wm/panels/panel_frame_view.h b/ash/wm/panels/panel_frame_view.h
index 4a0a6f2..cccfd18 100644
--- a/ash/wm/panels/panel_frame_view.h
+++ b/ash/wm/panels/panel_frame_view.h
@@ -19,10 +19,10 @@
 
 namespace ash {
 
+class FrameCaptionButtonContainerView;
 class FramePainter;
 
-class ASH_EXPORT PanelFrameView : public views::NonClientFrameView,
-                                  public views::ButtonListener {
+class ASH_EXPORT PanelFrameView : public views::NonClientFrameView {
  public:
   // Internal class name.
   static const char kViewClassName[];
@@ -57,16 +57,11 @@
   virtual void Layout() OVERRIDE;
   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
 
-  // Overridden from views::ButtonListener:
-  virtual void ButtonPressed(views::Button* sender,
-                             const ui::Event& event) OVERRIDE;
-
   // Child View class describing the panel's title bar behavior
   // and buttons, owned by the view hierarchy
   scoped_ptr<FramePainter> frame_painter_;
   views::Widget* frame_;
-  views::ImageButton* close_button_;
-  views::ImageButton* minimize_button_;
+  FrameCaptionButtonContainerView* caption_button_container_;
   views::ImageView* window_icon_;
   gfx::Rect client_view_bounds_;
   const gfx::Font title_font_;
diff --git a/ash/wm/property_util.h b/ash/wm/property_util.h
index 32ae278..41a9d93 100644
--- a/ash/wm/property_util.h
+++ b/ash/wm/property_util.h
@@ -63,10 +63,6 @@
 ASH_EXPORT void SetTrackedByWorkspace(aura::Window* window, bool value);
 ASH_EXPORT bool GetTrackedByWorkspace(const aura::Window* window);
 
-// Sets the default value for whether windows persist across all workspaces.
-// The default is false.
-ASH_EXPORT void SetDefaultPersistsAcrossAllWorkspaces(bool value);
-
 // Sets/Gets the RootWindowController for |root_window|.
 ASH_EXPORT void SetRootWindowController(
     aura::RootWindow* root_window,
diff --git a/ash/wm/window_animations.cc b/ash/wm/window_animations.cc
index a3c0ca8..73deafd 100644
--- a/ash/wm/window_animations.cc
+++ b/ash/wm/window_animations.cc
@@ -295,6 +295,9 @@
     // Triggers OnImplicitAnimationsCompleted() to be called and deletes us.
     layer_->GetAnimator()->StopAnimating();
   }
+  virtual void OnWindowRemovingFromRootWindow(aura::Window* window) OVERRIDE {
+    layer_->GetAnimator()->StopAnimating();
+  }
 
   // ui::ImplicitAnimationObserver overrides:
   virtual void OnImplicitAnimationsCompleted() OVERRIDE {
diff --git a/ash/wm/window_properties.cc b/ash/wm/window_properties.cc
index 1db17a8..90157af 100644
--- a/ash/wm/window_properties.cc
+++ b/ash/wm/window_properties.cc
@@ -14,6 +14,7 @@
 
 namespace ash {
 namespace internal {
+DEFINE_WINDOW_PROPERTY_KEY(bool, kAnimateToFullscreenKey, true);
 DEFINE_WINDOW_PROPERTY_KEY(bool, kContinueDragAfterReparent, false);
 DEFINE_WINDOW_PROPERTY_KEY(bool, kFullscreenUsesMinimalChromeKey, false);
 DEFINE_WINDOW_PROPERTY_KEY(bool, kIgnoreSoloWindowFramePainterPolicy, false);
diff --git a/ash/wm/window_properties.h b/ash/wm/window_properties.h
index d32e368..86ce39c 100644
--- a/ash/wm/window_properties.h
+++ b/ash/wm/window_properties.h
@@ -23,6 +23,10 @@
 
 // Alphabetical sort.
 
+//  A property key to suppress the cross-fade animation for the transition to
+// the fullscreen state.
+extern const aura::WindowProperty<bool>* const kAnimateToFullscreenKey;
+
 // A property key to indicate that an in progress drag should be continued
 // after the window is reparented to another container.
 extern const aura::WindowProperty<bool>* const kContinueDragAfterReparent;
diff --git a/ash/wm/window_util.cc b/ash/wm/window_util.cc
index 7b8e616..c3101c7 100644
--- a/ash/wm/window_util.cc
+++ b/ash/wm/window_util.cc
@@ -141,6 +141,10 @@
   window->SetProperty(ash::internal::kWindowPositionManagedKey, managed);
 }
 
+void SetAnimateToFullscreen(aura::Window* window, bool animate) {
+  window->SetProperty(ash::internal::kAnimateToFullscreenKey, animate);
+}
+
 bool HasUserChangedWindowPositionOrSize(const aura::Window* window) {
   return window->GetProperty(
       ash::internal::kUserChangedWindowPositionOrSizeKey);
diff --git a/ash/wm/window_util.h b/ash/wm/window_util.h
index 659d3e1..6822a77 100644
--- a/ash/wm/window_util.h
+++ b/ash/wm/window_util.h
@@ -91,6 +91,9 @@
 // Change the |window|'s position manageability to |managed|.
 ASH_EXPORT void SetWindowPositionManaged(aura::Window* window, bool managed);
 
+// Change the availability of animation to the fullscreen of the |window|.
+ASH_EXPORT void SetAnimateToFullscreen(aura::Window* window, bool animate);
+
 // Returns true if the user has changed the |window|'s position or size.
 ASH_EXPORT bool HasUserChangedWindowPositionOrSize(const aura::Window* window);
 
diff --git a/ash/wm/workspace/auto_window_management.cc b/ash/wm/workspace/auto_window_management.cc
index 07073ff..ddcd19c 100644
--- a/ash/wm/workspace/auto_window_management.cc
+++ b/ash/wm/workspace/auto_window_management.cc
@@ -6,6 +6,7 @@
 
 #include "ash/ash_switches.h"
 #include "ash/shell.h"
+#include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/property_util.h"
 #include "ash/wm/window_animations.h"
 #include "ash/wm/window_util.h"
@@ -41,31 +42,6 @@
           !wm::HasUserChangedWindowPositionOrSize(window));
 }
 
-// Given a |window|, return the only |other_window| which has an impact on
-// the automated windows location management. If there is more then one window,
-// false is returned, but the |other_window| will be set to the first one
-// found.
-// If the return value is true a single window was found.
-bool GetOtherVisibleAndManageableWindow(const aura::Window* window,
-                                        aura::Window** other_window) {
-  *other_window = NULL;
-  const aura::Window::Windows& windows = window->parent()->children();
-  // Find a single open managed window.
-  for (size_t i = 0; i < windows.size(); i++) {
-    aura::Window* iterated_window = windows[i];
-    if (window != iterated_window &&
-        iterated_window->type() == aura::client::WINDOW_TYPE_NORMAL &&
-        iterated_window->TargetVisibility() &&
-        wm::IsWindowPositionManaged(iterated_window)) {
-      // Bail if we find a second usable window.
-      if (*other_window)
-        return false;
-      *other_window = iterated_window;
-    }
-  }
-  return *other_window != NULL;
-}
-
 // Get the work area for a given |window|.
 gfx::Rect GetWorkAreaForWindow(aura::Window* window) {
 #if defined(OS_WIN)
@@ -137,15 +113,68 @@
     window->SetBounds(bounds);
 }
 
+// Get the first open (non minimized) window which is on the screen defined.
+aura::Window* GetReferenceWindow(const aura::RootWindow* root_window,
+                                 const aura::Window* exclude,
+                                 bool *single_window) {
+  if (single_window)
+    *single_window = true;
+  // Get the active window.
+  aura::Window* active = ash::wm::GetActiveWindow();
+  if (active && active->GetRootWindow() != root_window)
+    active = NULL;
+
+  // Get a list of all windows.
+  const std::vector<aura::Window*> windows =
+      ash::MruWindowTracker::BuildWindowList(false);
+
+  if (windows.empty())
+    return NULL;
+
+  aura::Window::Windows::const_iterator iter = windows.begin();
+  // Find the index of the current active window.
+  if (active)
+    iter = std::find(windows.begin(), windows.end(), active);
+
+  int index = (iter == windows.end()) ? 0 : (iter - windows.begin());
+
+  // Scan the cycle list backwards to see which is the second topmost window
+  // (and so on). Note that we might cycle a few indices twice if there is no
+  // suitable window. However - since the list is fairly small this should be
+  // very fast anyways.
+  aura::Window* found = NULL;
+  for (int i = index + windows.size(); i >= 0; i--) {
+    aura::Window* window = windows[i % windows.size()];
+    if (window != exclude &&
+        window->type() == aura::client::WINDOW_TYPE_NORMAL &&
+        window->GetRootWindow() == root_window &&
+        window->TargetVisibility() &&
+        wm::IsWindowPositionManaged(window)) {
+      if (found && found != window) {
+        // no need to check !signle_window because the function must have
+        // been already returned in the "if (!single_window)" below.
+        *single_window = false;
+        return found;
+      }
+      found = window;
+      // If there is no need to check single window, return now.
+      if (!single_window)
+        return found;
+    }
+  }
+  return found;
+}
+
 } // namespace
 
 void RearrangeVisibleWindowOnHideOrRemove(const aura::Window* removed_window) {
   if (!UseAutoWindowManager(removed_window))
     return;
   // Find a single open browser window.
-  aura::Window* other_shown_window = NULL;
-  if (!GetOtherVisibleAndManageableWindow(removed_window,
-                                          &other_shown_window) ||
+  bool single_window;
+  aura::Window* other_shown_window = GetReferenceWindow(
+      removed_window->GetRootWindow(), removed_window, &single_window);
+  if (!other_shown_window || !single_window ||
       !WindowPositionCanBeManaged(other_shown_window))
     return;
   AutoPlaceSingleWindow(other_shown_window, true);
@@ -157,9 +186,11 @@
       !added_window->TargetVisibility())
     return;
   // Find a single open managed window.
-  aura::Window* other_shown_window = NULL;
-  if (!GetOtherVisibleAndManageableWindow(added_window,
-                                          &other_shown_window)) {
+  bool single_window;
+  aura::Window* other_shown_window = GetReferenceWindow(
+      added_window->GetRootWindow(), added_window, &single_window);
+
+  if (!other_shown_window) {
     // It could be that this window is the first window joining the workspace.
     if (!WindowPositionCanBeManaged(added_window) || other_shown_window)
       return;
@@ -169,31 +200,44 @@
     return;
   }
 
-  // When going from one to two windows both windows loose their "positioned
-  // by user" flags.
-  ash::wm::SetUserHasChangedWindowPositionOrSize(added_window, false);
-  ash::wm::SetUserHasChangedWindowPositionOrSize(other_shown_window, false);
+  gfx::Rect other_bounds = other_shown_window->bounds();
+  gfx::Rect work_area = GetWorkAreaForWindow(added_window);
+  bool move_other_right =
+      other_bounds.CenterPoint().x() > work_area.width() / 2;
 
-  if (WindowPositionCanBeManaged(other_shown_window)) {
-    gfx::Rect work_area = GetWorkAreaForWindow(added_window);
+  // Push the other window to the size only if there are two windows left.
+  if (single_window) {
+    // When going from one to two windows both windows loose their "positioned
+    // by user" flags.
+    ash::wm::SetUserHasChangedWindowPositionOrSize(added_window, false);
+    ash::wm::SetUserHasChangedWindowPositionOrSize(other_shown_window, false);
 
-    // Push away the other window after remembering its current position.
-    gfx::Rect other_bounds = other_shown_window->bounds();
-    ash::wm::SetPreAutoManageWindowBounds(other_shown_window, other_bounds);
+    if (WindowPositionCanBeManaged(other_shown_window)) {
+      // Don't override pre auto managed bounds as the current bounds
+      // may not be original.
+      if (!ash::wm::GetPreAutoManageWindowBounds(other_shown_window))
+        ash::wm::SetPreAutoManageWindowBounds(other_shown_window, other_bounds);
 
-    bool move_right = other_bounds.CenterPoint().x() > work_area.width() / 2;
-    if (MoveRectToOneSide(work_area.width(), move_right, &other_bounds))
-      SetBoundsAnimated(other_shown_window, other_bounds);
-
-    // Remember the current location of the new window and push it also to the
-    // opposite location (if needed).
-    // Since it is just coming into view, we do not need to animate it.
-    gfx::Rect added_bounds = added_window->bounds();
-    ash::wm::SetPreAutoManageWindowBounds(added_window, added_bounds);
-    if (MoveRectToOneSide(work_area.width(), !move_right, &added_bounds))
-      added_window->SetBounds(added_bounds);
+      // Push away the other window after remembering its current position.
+      if (MoveRectToOneSide(work_area.width(), move_other_right, &other_bounds))
+        SetBoundsAnimated(other_shown_window, other_bounds);
+    }
   }
+
+  // Remember the current location of the window if it's new and push
+  // it also to the opposite location if needed.  Since it is just
+  // being shown, we do not need to animate it.
+  gfx::Rect added_bounds = added_window->bounds();
+  if (!ash::wm::GetPreAutoManageWindowBounds(added_window))
+    ash::wm::SetPreAutoManageWindowBounds(added_window, added_bounds);
+  if (MoveRectToOneSide(work_area.width(), !move_other_right, &added_bounds))
+    added_window->SetBounds(added_bounds);
 }
 
 } // namespace internal
+
+aura::Window* GetTopWindowForNewWindow(const aura::RootWindow* root_window) {
+  return internal::GetReferenceWindow(root_window, NULL, NULL);
+}
+
 } // namespace ash
diff --git a/ash/wm/workspace/auto_window_management.h b/ash/wm/workspace/auto_window_management.h
index 138cb54..9469acc 100644
--- a/ash/wm/workspace/auto_window_management.h
+++ b/ash/wm/workspace/auto_window_management.h
@@ -2,7 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ui/aura/window.h"
+#include "ash/ash_export.h"
+
+namespace gfx {
+class Rect;
+}
+namespace aura {
+class RootWindow;
+class Window;
+}
 
 namespace ash {
 namespace internal {
@@ -16,5 +24,12 @@
 void RearrangeVisibleWindowOnShow(aura::Window* added_window);
 
 } // namespace internal
-} // namespace ash
 
+// Returns the top window used for automated windows location
+// management.
+// TODO(oshima): This is temporarily made public and exported until we
+// move the initial bounds logic in window_sizer_ash.cc into ash.
+ASH_EXPORT aura::Window* GetTopWindowForNewWindow(
+    const aura::RootWindow* root_window);
+
+} // namespace ash
diff --git a/ash/wm/workspace/frame_caption_button_container_view.cc b/ash/wm/workspace/frame_caption_button_container_view.cc
index 7f1d020..962f847 100644
--- a/ash/wm/workspace/frame_caption_button_container_view.cc
+++ b/ash/wm/workspace/frame_caption_button_container_view.cc
@@ -6,14 +6,18 @@
 
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
+#include "ash/wm/property_util.h"
 #include "ash/wm/workspace/frame_maximize_button.h"
 #include "grit/ash_resources.h"
 #include "grit/ui_strings.h"  // Accessibility names
+#include "ui/base/hit_test.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/gfx/canvas.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
 #include "ui/views/window/non_client_view.h"
 
 namespace ash {
@@ -31,29 +35,61 @@
 
 FrameCaptionButtonContainerView::FrameCaptionButtonContainerView(
     views::NonClientFrameView* frame_view,
-    views::Widget* frame)
+    views::Widget* frame,
+    MinimizeAllowed minimize_allowed)
     : frame_(frame),
+      header_style_(HEADER_STYLE_SHORT),
+      minimize_button_(new views::ImageButton(this)),
       size_button_(new FrameMaximizeButton(this, frame_view)),
       close_button_(new views::ImageButton(this)) {
   // Insert the buttons left to right.
+  minimize_button_->SetAccessibleName(
+      l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE));
+  // Hide |minimize_button_| when |size_button_| is visible because
+  // |size_button_| is capable of minimizing.
+  // TODO(pkotwicz): We should probably show the minimize button when in
+  // "always maximized" mode.
+  minimize_button_->SetVisible(minimize_allowed == MINIMIZE_ALLOWED &&
+                               !frame_->widget_delegate()->CanMaximize());
+  AddChildView(minimize_button_);
+
   size_button_->SetAccessibleName(
       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE));
-  if (ash::Shell::IsForcedMaximizeMode())
-    size_button_->SetVisible(false);
+  size_button_->SetVisible(frame_->widget_delegate()->CanMaximize() &&
+                           !ash::Shell::IsForcedMaximizeMode());
   AddChildView(size_button_);
 
   close_button_->SetAccessibleName(
       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
   AddChildView(close_button_);
+
+  button_separator_ = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+      IDR_AURA_WINDOW_BUTTON_SEPARATOR).AsImageSkia();
 }
 
 FrameCaptionButtonContainerView::~FrameCaptionButtonContainerView() {
 }
 
 void FrameCaptionButtonContainerView::ResetWindowControls() {
+  minimize_button_->SetState(views::CustomButton::STATE_NORMAL);
   size_button_->SetState(views::CustomButton::STATE_NORMAL);
 }
 
+int FrameCaptionButtonContainerView::NonClientHitTest(
+    const gfx::Point& point) const {
+  if (close_button_->visible() &&
+      close_button_->GetMirroredBounds().Contains(point)) {
+    return HTCLOSE;
+  } else if (size_button_->visible() &&
+             size_button_->GetMirroredBounds().Contains(point)) {
+    return HTMAXBUTTON;
+  } else if (minimize_button_->visible() &&
+             minimize_button_->GetMirroredBounds().Contains(point)) {
+    return HTMINBUTTON;
+  }
+  return HTNOWHERE;
+}
+
 gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() {
   int width = 0;
   bool first_visible = true;
@@ -74,14 +110,53 @@
 }
 
 void FrameCaptionButtonContainerView::Layout() {
-  SetButtonImages(size_button_,
-                  IDR_AURA_WINDOW_FULLSCREEN_RESTORE,
-                  IDR_AURA_WINDOW_FULLSCREEN_RESTORE_H,
-                  IDR_AURA_WINDOW_FULLSCREEN_RESTORE_P);
-  SetButtonImages(close_button_,
-                  IDR_AURA_WINDOW_FULLSCREEN_CLOSE,
-                  IDR_AURA_WINDOW_FULLSCREEN_CLOSE_H,
-                  IDR_AURA_WINDOW_FULLSCREEN_CLOSE_P);
+  SetButtonImages(minimize_button_,
+                  IDR_AURA_WINDOW_MINIMIZE_SHORT,
+                  IDR_AURA_WINDOW_MINIMIZE_SHORT_H,
+                  IDR_AURA_WINDOW_MINIMIZE_SHORT_P);
+
+  if (header_style_ == HEADER_STYLE_MAXIMIZED_HOSTED_APP) {
+    SetButtonImages(size_button_,
+                    IDR_AURA_WINDOW_FULLSCREEN_RESTORE,
+                    IDR_AURA_WINDOW_FULLSCREEN_RESTORE_H,
+                    IDR_AURA_WINDOW_FULLSCREEN_RESTORE_P);
+    SetButtonImages(close_button_,
+                    IDR_AURA_WINDOW_FULLSCREEN_CLOSE,
+                    IDR_AURA_WINDOW_FULLSCREEN_CLOSE_H,
+                    IDR_AURA_WINDOW_FULLSCREEN_CLOSE_P);
+  } else if (header_style_ == HEADER_STYLE_SHORT) {
+    // The new assets only make sense if the window is maximized or fullscreen
+    // because we usually use a black header in this case.
+    if ((frame_->IsMaximized() || frame_->IsFullscreen()) &&
+        GetTrackedByWorkspace(frame_->GetNativeWindow())) {
+      SetButtonImages(size_button_,
+                      IDR_AURA_WINDOW_MAXIMIZED_RESTORE2,
+                      IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H,
+                      IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P);
+      SetButtonImages(close_button_,
+                      IDR_AURA_WINDOW_MAXIMIZED_CLOSE2,
+                      IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H,
+                      IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P);
+    } else {
+      SetButtonImages(size_button_,
+                      IDR_AURA_WINDOW_MAXIMIZED_RESTORE,
+                      IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H,
+                      IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P);
+      SetButtonImages(close_button_,
+                      IDR_AURA_WINDOW_MAXIMIZED_CLOSE,
+                      IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H,
+                      IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P);
+    }
+  } else {
+    SetButtonImages(size_button_,
+                    IDR_AURA_WINDOW_MAXIMIZE,
+                    IDR_AURA_WINDOW_MAXIMIZE_H,
+                    IDR_AURA_WINDOW_MAXIMIZE_P);
+    SetButtonImages(close_button_,
+                    IDR_AURA_WINDOW_CLOSE,
+                    IDR_AURA_WINDOW_CLOSE_H,
+                    IDR_AURA_WINDOW_CLOSE_P);
+  }
 
   gfx::Insets insets(GetInsets());
   int x = insets.left();
@@ -101,6 +176,22 @@
   return kViewClassName;
 }
 
+void FrameCaptionButtonContainerView::OnPaint(gfx::Canvas* canvas) {
+  views::View::OnPaint(canvas);
+
+  // AppNonClientFrameViewAsh does not paint the button separator.
+  if (header_style_ != HEADER_STYLE_MAXIMIZED_HOSTED_APP) {
+    // We should have at most two visible buttons. The button separator is
+    // always painted underneath the close button regardless of whether a
+    // button other than the close button is visible.
+    gfx::Rect divider(close_button_->bounds().origin(),
+                      button_separator_.size());
+    canvas->DrawImageInt(button_separator_,
+                         GetMirroredXForRect(divider),
+                         divider.y());
+  }
+}
+
 void FrameCaptionButtonContainerView::SetButtonImages(
     views::ImageButton* button,
     int normal_image_id,
@@ -117,15 +208,37 @@
 
 void FrameCaptionButtonContainerView::ButtonPressed(views::Button* sender,
                                                     const ui::Event& event) {
+  // When shift-clicking, slow down animations for visual debugging.
+  // We used to do this via an event filter that looked for the shift key being
+  // pressed but this interfered with several normal keyboard shortcuts.
+  scoped_ptr<ui::ScopedAnimationDurationScaleMode> slow_duration_mode;
+  if (event.IsShiftDown()) {
+    slow_duration_mode.reset(new ui::ScopedAnimationDurationScaleMode(
+        ui::ScopedAnimationDurationScaleMode::SLOW_DURATION));
+  }
+
   ash::UserMetricsAction action =
-      ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE;
-  if (sender == size_button_) {
-    // The maximize button may move out from under the cursor.
+      ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE;
+  if (sender == minimize_button_) {
+    // The minimize button may move out from under the cursor.
     ResetWindowControls();
-    frame_->Restore();
-  } else if (sender == close_button_) {
-    action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK;
+    frame_->Minimize();
+  } else if (sender == size_button_) {
+    // The size button may move out from under the cursor.
+    ResetWindowControls();
+    if (frame_->IsFullscreen()) { // Can be clicked in immersive fullscreen.
+      frame_->SetFullscreen(false);
+      action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN;
+    } else if (frame_->IsMaximized()) {
+      frame_->Restore();
+      action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE;
+    } else {
+      frame_->Maximize();
+      action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE;
+    }
+  } else if(sender == close_button_) {
     frame_->Close();
+    action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK;
   } else {
     return;
   }
diff --git a/ash/wm/workspace/frame_caption_button_container_view.h b/ash/wm/workspace/frame_caption_button_container_view.h
index 0fe256b..b1bb377 100644
--- a/ash/wm/workspace/frame_caption_button_container_view.h
+++ b/ash/wm/workspace/frame_caption_button_container_view.h
@@ -6,6 +6,7 @@
 #define ASH_WM_WORKSPACE_FRAME_CAPTION_BUTTON_CONTAINER_VIEW_H_
 
 #include "ash/ash_export.h"
+#include "ui/gfx/image/image_skia.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/view.h"
 
@@ -25,12 +26,34 @@
  public:
   static const char kViewClassName[];
 
+  // Whether the frame can be minimized (either via the maximize/restore button
+  // or via a dedicated button).
+  enum MinimizeAllowed {
+    MINIMIZE_ALLOWED,
+    MINIMIZE_DISALLOWED
+  };
+  enum HeaderStyle {
+    // Dialogs, panels, packaged apps, tabbed maximized/fullscreen browser
+    // windows.
+    HEADER_STYLE_SHORT,
+
+    // Restored tabbed browser windows, popups for browser windows, restored
+    // hosted app windows, popups for hosted app windows.
+    HEADER_STYLE_TALL,
+
+    // AppNonClientFrameViewAsh.
+    HEADER_STYLE_MAXIMIZED_HOSTED_APP
+  };
+
   // |frame_view| and |frame| are the NonClientFrameView and the views::Widget
   // that the caption buttons act on.
+  // |minimize_allowed| indicates whether the frame can be minimized (either via
+  // the maximize/restore button or via a dedicated button).
   // TODO(pkotwicz): Remove the |frame_view| parameter once FrameMaximizeButton
   // is refactored to take in a views::Widget instead.
   FrameCaptionButtonContainerView(views::NonClientFrameView* frame_view,
-                                  views::Widget* frame);
+                                  views::Widget* frame,
+                                  MinimizeAllowed minimize_allowed);
   virtual ~FrameCaptionButtonContainerView();
 
   // For testing.
@@ -40,6 +63,10 @@
         : container_view_(container_view) {
     }
 
+    views::ImageButton* minimize_button() const {
+      return container_view_->minimize_button_;
+    }
+
     views::ImageButton* size_button() const {
       return container_view_->size_button_;
     }
@@ -57,10 +84,21 @@
   // Tell the window controls to reset themselves to the normal state.
   void ResetWindowControls();
 
+  // Determines the window HT* code for the caption button at |point|. Returns
+  // HTNOWHERE if |point| is not over any of the caption buttons. |point| must
+  // be in the coordinates of the FrameCaptionButtonContainerView.
+  int NonClientHitTest(const gfx::Point& point) const;
+
+  // Sets the header style.
+  void set_header_style(HeaderStyle header_style) {
+    header_style_ = header_style;
+  }
+
   // views::View overrides:
   virtual gfx::Size GetPreferredSize() OVERRIDE;
   virtual void Layout() OVERRIDE;
   virtual const char* GetClassName() const OVERRIDE;
+  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
 
  private:
   // Sets the images for a button based on the given ids.
@@ -76,7 +114,14 @@
   // The widget that the buttons act on.
   views::Widget* frame_;
 
-  // The buttons.
+  // The close button separator.
+  gfx::ImageSkia button_separator_;
+
+  HeaderStyle header_style_;
+
+  // The buttons. At most one of |minimize_button_| and |size_button_| is
+  // visible.
+  views::ImageButton* minimize_button_;
   views::ImageButton* size_button_;
   views::ImageButton* close_button_;
 
diff --git a/ash/wm/workspace/frame_caption_button_container_view_unittest.cc b/ash/wm/workspace/frame_caption_button_container_view_unittest.cc
index 69bb97b..2f520cf 100644
--- a/ash/wm/workspace/frame_caption_button_container_view_unittest.cc
+++ b/ash/wm/workspace/frame_caption_button_container_view_unittest.cc
@@ -5,70 +5,255 @@
 #include "ash/wm/workspace/frame_caption_button_container_view.h"
 
 #include "ash/ash_switches.h"
+#include "ash/test/ash_test_base.h"
 #include "base/command_line.h"
-#include "testing/gtest/include/gtest/gtest.h"
+#include "grit/ash_resources.h"
+#include "ui/aura/root_window.h"
+#include "ui/base/resource/resource_bundle.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/button/image_button.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
 
 namespace ash {
 
-typedef testing::Test FrameCaptionButtonContainerViewTest;
+namespace {
 
-TEST_F(FrameCaptionButtonContainerViewTest, Sanity) {
-  // 1) Test the layout and the caption button visibility in the default case.
-  // Both the size button and the close button should be visible.
-  FrameCaptionButtonContainerView c1(NULL, NULL);
-  c1.Layout();
-  FrameCaptionButtonContainerView::TestApi t1(&c1);
-  views::ImageButton* size_button = t1.size_button();
-  views::ImageButton* close_button = t1.close_button();
-  EXPECT_TRUE(size_button->visible());
-  EXPECT_TRUE(close_button->visible());
+// Returns true if the images for |button|'s states match the passed in ids.
+bool ImagesMatch(views::ImageButton* button,
+                 int normal_image_id,
+                 int hovered_image_id,
+                 int pressed_image_id) {
+  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+  gfx::ImageSkia* normal = rb.GetImageSkiaNamed(normal_image_id);
+  gfx::ImageSkia* hovered = rb.GetImageSkiaNamed(hovered_image_id);
+  gfx::ImageSkia* pressed = rb.GetImageSkiaNamed(pressed_image_id);
+  using views::Button;
+  return button->GetImage(Button::STATE_NORMAL).BackedBySameObjectAs(*normal) &&
+      button->GetImage(Button::STATE_HOVERED).BackedBySameObjectAs(*hovered) &&
+      button->GetImage(Button::STATE_PRESSED).BackedBySameObjectAs(*pressed);
+}
 
-  // The size button should be left of the close button. (in non-RTL)
-  EXPECT_LT(size_button->x(), close_button->x());
+// Returns true if |leftmost| and |rightmost| are flush with |container|'s
+// edges.
+bool CheckButtonsAtEdges(FrameCaptionButtonContainerView* container,
+                         const views::ImageButton& leftmost,
+                         const views::ImageButton& rightmost) {
+  gfx::Rect container_size(container->GetPreferredSize());
+  if (leftmost.y() == rightmost.y() &&
+      leftmost.height() == rightmost.height() &&
+      leftmost.x() == 0 &&
+      leftmost.y() == 0 &&
+      leftmost.height() == container_size.height() &&
+      rightmost.bounds().right() == container_size.width()) {
+    return true;
+  }
 
-  // The container's bounds should be flush with the caption buttons.
-  EXPECT_EQ(0, size_button->x());
-  EXPECT_EQ(c1.GetPreferredSize().width(), close_button->bounds().right());
-  EXPECT_EQ(c1.GetPreferredSize().height(), close_button->bounds().height());
+  LOG(ERROR) << "Buttons " << leftmost.bounds().ToString() << " "
+             << rightmost.bounds().ToString() << " not at edges of "
+             << gfx::Rect(container_size).ToString();
+  return false;
+}
 
-  // 2) Test the layout and the caption button visibility when the
-  // "force-maximize-mode" experiment is turned on. Only the close button
-  // should be visible.
+class TestWidgetDelegate : public views::WidgetDelegateView {
+ public:
+  TestWidgetDelegate(bool can_maximize) : can_maximize_(can_maximize) {
+  }
+  virtual ~TestWidgetDelegate() {
+  }
+
+  virtual bool CanMaximize() const OVERRIDE {
+    return can_maximize_;
+  }
+
+ private:
+  bool can_maximize_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate);
+};
+
+}  // namespace
+
+class FrameCaptionButtonContainerViewTest : public ash::test::AshTestBase {
+ public:
+  enum MaximizeAllowed {
+    MAXIMIZE_ALLOWED,
+    MAXIMIZE_DISALLOWED
+  };
+
+  FrameCaptionButtonContainerViewTest() {
+  }
+
+  virtual ~FrameCaptionButtonContainerViewTest() {
+  }
+
+  // Creates a widget which allows maximizing based on |maximize_allowed|.
+  // The caller takes ownership of the returned widget.
+  views::Widget* CreateTestWidget(
+      MaximizeAllowed maximize_allowed) WARN_UNUSED_RESULT {
+    views::Widget* widget = new views::Widget;
+    views::Widget::InitParams params;
+    params.delegate = new TestWidgetDelegate(
+        maximize_allowed == MAXIMIZE_ALLOWED);
+    params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+    params.context = CurrentContext();
+    widget->Init(params);
+    return widget;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FrameCaptionButtonContainerViewTest);
+};
+
+// Test how the allowed actions affect which caption buttons are visible.
+TEST_F(FrameCaptionButtonContainerViewTest, ButtonVisibility) {
+  // The minimize button should be hidden when both minimizing and maximizing
+  // are allowed because the size button can do both.
+  scoped_ptr<views::Widget> widget_can_maximize(
+      CreateTestWidget(MAXIMIZE_ALLOWED));
+  FrameCaptionButtonContainerView container1(NULL, widget_can_maximize.get(),
+      FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
+  container1.Layout();
+  FrameCaptionButtonContainerView::TestApi t1(&container1);
+  EXPECT_FALSE(t1.minimize_button()->visible());
+  EXPECT_TRUE(t1.size_button()->visible());
+  EXPECT_TRUE(t1.close_button()->visible());
+  EXPECT_TRUE(CheckButtonsAtEdges(
+      &container1, *t1.size_button(), *t1.close_button()));
+
+  // The minimize button should be visible when minimizing is allowed but
+  // maximizing is disallowed.
+  scoped_ptr<views::Widget> widget_cannot_maximize(
+      CreateTestWidget(MAXIMIZE_DISALLOWED));
+  FrameCaptionButtonContainerView container2(NULL, widget_cannot_maximize.get(),
+      FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
+  container2.Layout();
+  FrameCaptionButtonContainerView::TestApi t2(&container2);
+  EXPECT_TRUE(t2.minimize_button()->visible());
+  EXPECT_FALSE(t2.size_button()->visible());
+  EXPECT_TRUE(t2.close_button()->visible());
+  EXPECT_TRUE(CheckButtonsAtEdges(
+      &container2, *t2.minimize_button(), *t2.close_button()));
+
+  // Neither the minimize button nor the size button should be visible when
+  // neither minimizing nor maximizing are allowed.
+  FrameCaptionButtonContainerView container3(NULL, widget_cannot_maximize.get(),
+      FrameCaptionButtonContainerView::MINIMIZE_DISALLOWED);
+  container3.Layout();
+  FrameCaptionButtonContainerView::TestApi t3(&container3);
+  EXPECT_FALSE(t3.minimize_button()->visible());
+  EXPECT_FALSE(t3.size_button()->visible());
+  EXPECT_TRUE(t3.close_button()->visible());
+  EXPECT_TRUE(CheckButtonsAtEdges(
+      &container3, *t3.close_button(), *t3.close_button()));
+
+  // Neither the minimize button nor the size button should be visible when the
+  // "force-maximize-mode" experiment is turned on.
   CommandLine::ForCurrentProcess()->AppendSwitch(switches::kForcedMaximizeMode);
-
-  FrameCaptionButtonContainerView c2(NULL, NULL);
-  c2.Layout();
-  FrameCaptionButtonContainerView::TestApi t2(&c2);
-  size_button = t2.size_button();
-  close_button = t2.close_button();
-
-  EXPECT_FALSE(size_button->visible());
-  EXPECT_TRUE(close_button->visible());
-  EXPECT_EQ(c2.GetPreferredSize().width(), close_button->width());
-  EXPECT_EQ(c2.GetPreferredSize().height(), close_button->height());
+  FrameCaptionButtonContainerView container4(NULL, widget_can_maximize.get(),
+      FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
+  container4.Layout();
+  FrameCaptionButtonContainerView::TestApi t4(&container4);
+  EXPECT_FALSE(t4.minimize_button()->visible());
+  EXPECT_FALSE(t4.size_button()->visible());
+  EXPECT_TRUE(t4.close_button()->visible());
+  EXPECT_TRUE(CheckButtonsAtEdges(
+      &container4, *t4.close_button(), *t4.close_button()));
 }
 
 // Test the layout when a border is set on the container.
-TEST_F(FrameCaptionButtonContainerViewTest, Border) {
+TEST_F(FrameCaptionButtonContainerViewTest, LayoutBorder) {
   const int kTopInset = 1;
   const int kLeftInset = 2;
   const int kBottomInset = 3;
   const int kRightInset = 4;
 
-  FrameCaptionButtonContainerView c(NULL, NULL);
-  c.set_border(views::Border::CreateEmptyBorder(
+  scoped_ptr<views::Widget> widget(CreateTestWidget(MAXIMIZE_ALLOWED));
+  FrameCaptionButtonContainerView container(NULL, widget.get(),
+      FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
+  container.set_border(views::Border::CreateEmptyBorder(
       kTopInset, kLeftInset, kBottomInset, kRightInset));
-  c.Layout();
-  FrameCaptionButtonContainerView::TestApi t(&c);
+  container.Layout();
+  FrameCaptionButtonContainerView::TestApi t(&container);
 
   EXPECT_EQ(kLeftInset, t.size_button()->x());
   EXPECT_EQ(kTopInset, t.close_button()->y());
-  EXPECT_EQ(c.GetPreferredSize().height(),
+  EXPECT_EQ(container.GetPreferredSize().height(),
             t.close_button()->bounds().bottom() + kBottomInset);
-  EXPECT_EQ(c.GetPreferredSize().width(),
+  EXPECT_EQ(container.GetPreferredSize().width(),
             t.close_button()->bounds().right() + kRightInset);
 }
 
+// Test how the header style affects which images are used for the buttons.
+TEST_F(FrameCaptionButtonContainerViewTest, HeaderStyle) {
+  scoped_ptr<views::Widget> widget(CreateTestWidget(MAXIMIZE_ALLOWED));
+  FrameCaptionButtonContainerView container(NULL, widget.get(),
+      FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
+  FrameCaptionButtonContainerView::TestApi t(&container);
+
+  // Tall header style.
+  container.set_header_style(
+      FrameCaptionButtonContainerView::HEADER_STYLE_TALL);
+  container.Layout();
+  EXPECT_TRUE(ImagesMatch(t.size_button(),
+                          IDR_AURA_WINDOW_MAXIMIZE,
+                          IDR_AURA_WINDOW_MAXIMIZE_H,
+                          IDR_AURA_WINDOW_MAXIMIZE_P));
+  EXPECT_TRUE(ImagesMatch(t.close_button(),
+                          IDR_AURA_WINDOW_CLOSE,
+                          IDR_AURA_WINDOW_CLOSE_H,
+                          IDR_AURA_WINDOW_CLOSE_P));
+
+  // Short header style.
+  container.set_header_style(
+      FrameCaptionButtonContainerView::HEADER_STYLE_SHORT);
+  container.Layout();
+  EXPECT_TRUE(ImagesMatch(t.size_button(),
+                          IDR_AURA_WINDOW_MAXIMIZED_RESTORE,
+                          IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H,
+                          IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P));
+  EXPECT_TRUE(ImagesMatch(t.close_button(),
+                          IDR_AURA_WINDOW_MAXIMIZED_CLOSE,
+                          IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H,
+                          IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P));
+
+  // Maximized short header style.
+  widget->Maximize();
+  container.Layout();
+  EXPECT_TRUE(ImagesMatch(t.size_button(),
+                          IDR_AURA_WINDOW_MAXIMIZED_RESTORE2,
+                          IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H,
+                          IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P));
+  EXPECT_TRUE(ImagesMatch(t.close_button(),
+                          IDR_AURA_WINDOW_MAXIMIZED_CLOSE2,
+                          IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H,
+                          IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P));
+
+  // The buttons are visible during a reveal of the top-of-window views in
+  // immersive fullscreen. They should use the same images as maximized.
+  widget->SetFullscreen(true);
+  container.Layout();
+  EXPECT_TRUE(ImagesMatch(t.size_button(),
+                          IDR_AURA_WINDOW_MAXIMIZED_RESTORE2,
+                          IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H,
+                          IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P));
+  EXPECT_TRUE(ImagesMatch(t.close_button(),
+                          IDR_AURA_WINDOW_MAXIMIZED_CLOSE2,
+                          IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H,
+                          IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P));
+
+  // AppNonClientFrameViewAsh has a dedicated set of images.
+  container.set_header_style(
+      FrameCaptionButtonContainerView::HEADER_STYLE_MAXIMIZED_HOSTED_APP);
+  container.Layout();
+  EXPECT_TRUE(ImagesMatch(t.size_button(),
+                          IDR_AURA_WINDOW_FULLSCREEN_RESTORE,
+                          IDR_AURA_WINDOW_FULLSCREEN_RESTORE_H,
+                          IDR_AURA_WINDOW_FULLSCREEN_RESTORE_P));
+  EXPECT_TRUE(ImagesMatch(t.close_button(),
+                          IDR_AURA_WINDOW_FULLSCREEN_CLOSE,
+                          IDR_AURA_WINDOW_FULLSCREEN_CLOSE_H,
+                          IDR_AURA_WINDOW_FULLSCREEN_CLOSE_P));
+}
+
 }  // namespace ash
diff --git a/ash/wm/workspace/workspace_layout_manager.cc b/ash/wm/workspace/workspace_layout_manager.cc
index 0dd9dbf..44e7bfa 100644
--- a/ash/wm/workspace/workspace_layout_manager.cc
+++ b/ash/wm/workspace/workspace_layout_manager.cc
@@ -213,16 +213,13 @@
   if (!GetTrackedByWorkspace(window))
     return;
 
-  // Use cross fade transition for the maximized window if the adjustment
-  // happens due to the shelf's visibility change. Otherwise the background
-  // can be seen slightly between the bottom edge of resized-window and
-  // the animating shelf.
-  // TODO(mukai): this cause slight blur at the window frame because of the
-  // cross fade. I think this is better, but should reconsider if someone
-  // raises voice for this.
+  // Do not cross fade here: the window's layer hierarchy may be messed up for
+  // the transition between mirroring and extended. See also: crbug.com/267698
+  // TODO(oshima): Differentiate display change and shelf visibility change, and
+  // bring back CrossFade animation.
   if (wm::IsWindowMaximized(window) &&
       reason == ADJUST_WINDOW_WORK_AREA_INSETS_CHANGED) {
-    CrossFadeToBounds(window, ScreenAsh::GetMaximizedWindowBoundsInParent(
+    SetChildBoundsDirect(window, ScreenAsh::GetMaximizedWindowBoundsInParent(
         window->parent()->parent()));
     return;
   }
@@ -325,11 +322,18 @@
       CrossFadeToBounds(window, ScreenAsh::GetMaximizedWindowBoundsInParent(
           window->parent()->parent()));
       break;
-    case ui::SHOW_STATE_FULLSCREEN:
+
+    case ui::SHOW_STATE_FULLSCREEN: {
       MoveToDisplayForRestore(window);
-      SetChildBoundsDirect(window, ScreenAsh::GetDisplayBoundsInParent(
-          window->parent()->parent()));
+      gfx::Rect new_bounds = ScreenAsh::GetDisplayBoundsInParent(
+          window->parent()->parent());
+      if (window->GetProperty(kAnimateToFullscreenKey))
+        CrossFadeToBounds(window, new_bounds);
+      else
+        SetChildBoundsDirect(window, new_bounds);
       break;
+    }
+
     default:
       break;
   }
diff --git a/ash/wm/workspace_controller_unittest.cc b/ash/wm/workspace_controller_unittest.cc
index 7dca75a..0410bf0 100644
--- a/ash/wm/workspace_controller_unittest.cc
+++ b/ash/wm/workspace_controller_unittest.cc
@@ -101,15 +101,18 @@
     return window;
   }
 
-  aura::Window* CreateAppTestWindow(aura::Window* parent) {
-    aura::Window* window = new aura::Window(NULL);
-    window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
-    window->SetType(aura::client::WINDOW_TYPE_POPUP);
-    window->Init(ui::LAYER_TEXTURED);
-    if (!parent)
-      SetDefaultParentByPrimaryRootWindow(window);
-    else
-      parent->AddChild(window);
+  aura::Window* CreateBrowserLikeWindow(const gfx::Rect& bounds) {
+    aura::Window* window = CreateTestWindow();
+    window->SetBounds(bounds);
+    SetTrackedByWorkspace(window, true);
+    wm::SetWindowPositionManaged(window, true);
+    window->Show();
+    return window;
+  }
+
+  aura::Window* CreatePopupLikeWindow(const gfx::Rect& bounds) {
+    aura::Window* window = CreateTestWindowInShellWithBounds(bounds);
+    window->Show();
     return window;
   }
 
@@ -718,9 +721,64 @@
   EXPECT_EQ(w1->parent(), w2->parent());
 }
 
+// Test the placement of newly created windows.
+TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnCreate) {
+  if (!SupportsHostWindowResize())
+    return;
+  UpdateDisplay("1600x1200");
+  // Creating a popup handler here to make sure it does not interfere with the
+  // existing windows.
+  gfx::Rect source_browser_bounds(16, 32, 640, 320);
+  scoped_ptr<aura::Window> browser_window(CreateBrowserLikeWindow(
+      source_browser_bounds));
+
+  // Creating a popup to make sure it does not interfere with the positioning.
+  scoped_ptr<aura::Window> browser_popup(CreatePopupLikeWindow(
+      gfx::Rect(16, 32, 128, 256)));
+
+  browser_window->Show();
+  browser_popup->Show();
+
+  { // With a shown window it's size should get returned.
+    scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
+        source_browser_bounds));
+    // The position should be right flush.
+    EXPECT_EQ("960,32 640x320", new_browser_window->bounds().ToString());
+  }
+
+  { // With the window shown - but more on the right side then on the left
+    // side (and partially out of the screen), it should default to the other
+    // side and inside the screen.
+    gfx::Rect source_browser_bounds(gfx::Rect(1000, 600, 640, 320));
+    browser_window->SetBounds(source_browser_bounds);
+
+    scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
+        source_browser_bounds));
+    // The position should be left & bottom flush.
+    EXPECT_EQ("0,600 640x320", new_browser_window->bounds().ToString());
+
+    // If the other window was already beyond the point to get right flush
+    // it will remain where it is.
+    EXPECT_EQ("1000,600 640x320", browser_window->bounds().ToString());
+  }
+
+  { // Make sure that popups do not get changed.
+    scoped_ptr<aura::Window> new_popup_window(CreatePopupLikeWindow(
+        gfx::Rect(50, 100, 300, 150)));
+    EXPECT_EQ("50,100 300x150", new_popup_window->bounds().ToString());
+  }
+
+  browser_window->Hide();
+  { // If a window is there but not shown the default should be centered.
+    scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
+        gfx::Rect(50, 100, 300, 150)));
+    EXPECT_EQ("650,100 300x150", new_browser_window->bounds().ToString());
+  }
+}
+
 // Test the basic auto placement of one and or two windows in a "simulated
 // session" of sequential window operations.
-TEST_F(WorkspaceControllerTest, BasicAutoPlacing) {
+TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnShowHide) {
   // Test 1: In case there is no manageable window, no window should shift.
 
   scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
@@ -821,7 +879,7 @@
   // positionable again (user movement cleared).
   window2->Show();
 
-  // |window1| should be flush left and |window3| flush right.
+  // |window1| should be flush left and |window2| flush right.
   EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
   EXPECT_EQ(
       base::IntToString(desktop_area.width() - window2->bounds().width()) +
@@ -837,6 +895,54 @@
   EXPECT_TRUE(ash::wm::HasUserChangedWindowPositionOrSize(window1.get()));
 }
 
+// Test if the single window will be restored at original position.
+TEST_F(WorkspaceControllerTest, TestSingleWindowsRestoredBounds) {
+  scoped_ptr<aura::Window> window1(
+      CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
+  scoped_ptr<aura::Window> window2(
+      CreateTestWindowInShellWithBounds(gfx::Rect(110, 110, 100, 100)));
+  scoped_ptr<aura::Window> window3(
+      CreateTestWindowInShellWithBounds(gfx::Rect(120, 120, 100, 100)));
+  window1->Hide();
+  window2->Hide();
+  window3->Hide();
+  ash::wm::SetWindowPositionManaged(window1.get(), true);
+  ash::wm::SetWindowPositionManaged(window2.get(), true);
+  ash::wm::SetWindowPositionManaged(window3.get(), true);
+
+  window1->Show();
+  wm::ActivateWindow(window1.get());
+  window2->Show();
+  wm::ActivateWindow(window2.get());
+  window3->Show();
+  wm::ActivateWindow(window3.get());
+  EXPECT_EQ(0, window1->bounds().x());
+  EXPECT_EQ(window2->GetRootWindow()->bounds().right(),
+            window2->bounds().right());
+  EXPECT_EQ(0, window3->bounds().x());
+
+  window1->Hide();
+  EXPECT_EQ(window2->GetRootWindow()->bounds().right(),
+            window2->bounds().right());
+  EXPECT_EQ(0, window3->bounds().x());
+
+  // Being a single window will retore the original location.
+  window3->Hide();
+  wm::ActivateWindow(window2.get());
+  EXPECT_EQ("110,110 100x100", window2->bounds().ToString());
+
+  // Showing the 3rd will push the 2nd window left.
+  window3->Show();
+  wm::ActivateWindow(window3.get());
+  EXPECT_EQ(0, window2->bounds().x());
+  EXPECT_EQ(window3->GetRootWindow()->bounds().right(),
+            window3->bounds().right());
+
+  // Being a single window will retore the original location.
+  window2->Hide();
+  EXPECT_EQ("120,120 100x100", window3->bounds().ToString());
+}
+
 // Test that user placed windows go back to their user placement after the user
 // closes all other windows.
 TEST_F(WorkspaceControllerTest, TestUserHandledWindowRestore) {
diff --git a/base/OWNERS b/base/OWNERS
index 7e0e67f..f85ea53 100644
--- a/base/OWNERS
+++ b/base/OWNERS
@@ -3,6 +3,8 @@
 brettw@chromium.org
 willchan@chromium.org
 jar@chromium.org
+ajwong@chromium.org
+thakis@chromium.org
 
 per-file *.isolate=csharp@chromium.org
 per-file *.isolate=maruel@chromium.org
diff --git a/base/async_socket_io_handler.h b/base/async_socket_io_handler.h
index 562ae25..2f4b13d 100644
--- a/base/async_socket_io_handler.h
+++ b/base/async_socket_io_handler.h
@@ -9,14 +9,7 @@
 #include "base/sync_socket.h"
 #include "base/threading/non_thread_safe.h"
 
-namespace media {
-
-// The message loop callback interface is different based on platforms.
-#if defined(OS_WIN)
-typedef base::MessageLoopForIO::IOHandler MessageLoopIOHandler;
-#elif defined(OS_POSIX)
-typedef base::MessageLoopForIO::Watcher MessageLoopIOHandler;
-#endif
+namespace base {
 
 // Extends the CancelableSyncSocket class to allow reading from a socket
 // asynchronously on a TYPE_IO message loop thread.  This makes it easy to share
@@ -46,14 +39,19 @@
 //     }
 //   }
 //
-//   media::AsyncSocketIoHandler io_handler;
+//   base::AsyncSocketIoHandler io_handler;
 //   base::CancelableSyncSocket* socket_;
 //   char buffer_[kBufferSize];
 // };
 //
 class BASE_EXPORT AsyncSocketIoHandler
     : public NON_EXPORTED_BASE(base::NonThreadSafe),
-      public NON_EXPORTED_BASE(MessageLoopIOHandler) {
+// The message loop callback interface is different based on platforms.
+#if defined(OS_WIN)
+      public NON_EXPORTED_BASE(base::MessageLoopForIO::IOHandler) {
+#else
+      public NON_EXPORTED_BASE(base::MessageLoopForIO::Watcher) {
+#endif
  public:
   AsyncSocketIoHandler();
   virtual ~AsyncSocketIoHandler();
@@ -107,6 +105,6 @@
   DISALLOW_COPY_AND_ASSIGN(AsyncSocketIoHandler);
 };
 
-}  // namespace media.
+}  // namespace base.
 
 #endif  // BASE_ASYNC_SOCKET_IO_HANDLER_H_
diff --git a/base/async_socket_io_handler_posix.cc b/base/async_socket_io_handler_posix.cc
index 34c1c53..2fffb84 100644
--- a/base/async_socket_io_handler_posix.cc
+++ b/base/async_socket_io_handler_posix.cc
@@ -8,7 +8,7 @@
 
 #include "base/posix/eintr_wrapper.h"
 
-namespace media {
+namespace base {
 
 AsyncSocketIoHandler::AsyncSocketIoHandler()
     : socket_(base::SyncSocket::kInvalidHandle),
@@ -95,4 +95,4 @@
   }
 }
 
-}  // namespace media.
+}  // namespace base.
diff --git a/base/async_socket_io_handler_unittest.cc b/base/async_socket_io_handler_unittest.cc
index e8bdacd..2b0e3c2 100644
--- a/base/async_socket_io_handler_unittest.cc
+++ b/base/async_socket_io_handler_unittest.cc
@@ -56,7 +56,7 @@
     }
   }
 
-  media::AsyncSocketIoHandler io_handler;
+  base::AsyncSocketIoHandler io_handler;
   base::CancelableSyncSocket* socket_;  // Ownership lies outside the class.
   char buffer_[kAsyncSocketIoTestStringLength];
   int number_of_reads_before_quit_;
diff --git a/base/async_socket_io_handler_win.cc b/base/async_socket_io_handler_win.cc
index f974d6e..e1d215c 100644
--- a/base/async_socket_io_handler_win.cc
+++ b/base/async_socket_io_handler_win.cc
@@ -4,7 +4,7 @@
 
 #include "base/async_socket_io_handler.h"
 
-namespace media {
+namespace base {
 
 AsyncSocketIoHandler::AsyncSocketIoHandler()
     : socket_(base::SyncSocket::kInvalidHandle),
@@ -74,4 +74,4 @@
   return true;
 }
 
-}  // namespace media.
+}  // namespace base.
diff --git a/base/barrier_closure.cc b/base/barrier_closure.cc
new file mode 100644
index 0000000..65e9c08
--- /dev/null
+++ b/base/barrier_closure.cc
@@ -0,0 +1,52 @@
+// 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 "base/barrier_closure.h"
+
+#include "base/atomic_ref_count.h"
+#include "base/bind.h"
+
+namespace {
+
+// Maintains state for a BarrierClosure.
+class BarrierInfo {
+ public:
+  BarrierInfo(int num_callbacks_left, const base::Closure& done_closure);
+  void Run();
+
+ private:
+  base::AtomicRefCount num_callbacks_left_;
+  base::Closure done_closure_;
+};
+
+BarrierInfo::BarrierInfo(int num_callbacks, const base::Closure& done_closure)
+    : num_callbacks_left_(num_callbacks),
+      done_closure_(done_closure) {
+}
+
+void BarrierInfo::Run() {
+  DCHECK(!base::AtomicRefCountIsZero(&num_callbacks_left_));
+  if (!base::AtomicRefCountDec(&num_callbacks_left_)) {
+    done_closure_.Run();
+    done_closure_.Reset();
+  }
+}
+
+}  // namespace
+
+namespace base {
+
+base::Closure BarrierClosure(int num_callbacks_left,
+                             const base::Closure& done_closure) {
+  DCHECK(num_callbacks_left >= 0);
+
+  if (num_callbacks_left == 0)
+    done_closure.Run();
+
+  return base::Bind(&BarrierInfo::Run,
+                    base::Owned(
+                        new BarrierInfo(num_callbacks_left, done_closure)));
+}
+
+}  // namespace base
\ No newline at end of file
diff --git a/base/barrier_closure.h b/base/barrier_closure.h
new file mode 100644
index 0000000..2fb870f
--- /dev/null
+++ b/base/barrier_closure.h
@@ -0,0 +1,30 @@
+// 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 BASE_BARRIER_CLOSURE_H_
+#define BASE_BARRIER_CLOSURE_H_
+
+#include "base/base_export.h"
+#include "base/callback_forward.h"
+
+namespace base {
+
+// BarrierClosure executes |done_closure| after it has been invoked
+// |num_closures| times.
+//
+// If |num_closures| is 0, |done_closure| is executed immediately.
+//
+// BarrierClosure is thread-safe - the count of remaining closures is
+// maintained as a base::AtomicRefCount. |done_closure| will be run on
+// the thread that calls the final Run() on the returned closures.
+//
+// |done_closure| is also Reset() on the final calling thread but due to the
+// refcounted nature of callbacks, it is hard to know what thread resources
+// will be released on.
+BASE_EXPORT base::Closure BarrierClosure(int num_closures,
+                                         const base::Closure& done_closure);
+
+}  // namespace base
+
+#endif  // BASE_BARRIER_CLOSURE_H_
\ No newline at end of file
diff --git a/base/barrier_closure_unittest.cc b/base/barrier_closure_unittest.cc
new file mode 100644
index 0000000..ab05cb8
--- /dev/null
+++ b/base/barrier_closure_unittest.cc
@@ -0,0 +1,36 @@
+// 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 "base/barrier_closure.h"
+
+#include "base/bind.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void Increment(int* count) { (*count)++; }
+
+TEST(BarrierClosureTest, RunImmediatelyForZeroClosures) {
+  int count = 0;
+  base::Closure doneClosure(base::Bind(&Increment, base::Unretained(&count)));
+
+  base::Closure barrierClosure = base::BarrierClosure(0, doneClosure);
+  EXPECT_EQ(1, count);
+}
+
+TEST(BarrierClosureTest, RunAfterNumClosures) {
+  int count = 0;
+  base::Closure doneClosure(base::Bind(&Increment, base::Unretained(&count)));
+
+  base::Closure barrierClosure = base::BarrierClosure(2, doneClosure);
+  EXPECT_EQ(0, count);
+
+  barrierClosure.Run();
+  EXPECT_EQ(0, count);
+
+  barrierClosure.Run();
+  EXPECT_EQ(1, count);
+}
+
+}  // namespace
diff --git a/base/base.gyp b/base/base.gyp
index 2600754..0cdcaa4 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -453,12 +453,13 @@
         'async_socket_io_handler_unittest.cc',
         'at_exit_unittest.cc',
         'atomicops_unittest.cc',
+        'barrier_closure_unittest.cc',
         'base64_unittest.cc',
-        'bind_helpers_unittest.cc',
         'bind_unittest.cc',
         'bind_unittest.nc',
         'bits_unittest.cc',
         'build_time_unittest.cc',
+        'callback_helpers_unittest.cc',
         'callback_unittest.cc',
         'callback_unittest.nc',
         'cancelable_callback_unittest.cc',
@@ -864,7 +865,6 @@
         }],
       ],
       'sources': [
-        'perftimer.cc',
         'test/expectations/expectation.cc',
         'test/expectations/expectation.h',
         'test/expectations/parser.cc',
@@ -884,6 +884,7 @@
         'test/null_task_runner.h',
         'test/perf_test_suite.cc',
         'test/perf_test_suite.h',
+        'test/perftimer.cc',
         'test/power_monitor_test_base.cc',
         'test/power_monitor_test_base.h',
         'test/scoped_locale.cc',
@@ -934,9 +935,9 @@
         'test/thread_test_helper.h',
         'test/trace_event_analyzer.cc',
         'test/trace_event_analyzer.h',
-        'test/unit_test_launcher_ios.cc',
         'test/unit_test_launcher.cc',
         'test/unit_test_launcher.h',
+        'test/unit_test_launcher_ios.cc',
         'test/values_test_util.cc',
         'test/values_test_util.h',
       ],
@@ -962,7 +963,7 @@
         '../testing/gtest.gyp:gtest',
       ],
       'sources': [
-        'perftimer.cc',
+        'test/perftimer.cc',
         'test/run_all_perftests.cc',
       ],
       'direct_dependent_settings': {
diff --git a/base/base.gypi b/base/base.gypi
index e22aa2e..6a90a0f 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -78,6 +78,8 @@
           'atomicops_internals_x86_gcc.cc',
           'atomicops_internals_x86_gcc.h',
           'atomicops_internals_x86_msvc.h',
+          'barrier_closure.cc',
+          'barrier_closure.h',
           'base_export.h',
           'base_paths.cc',
           'base_paths.h',
@@ -102,6 +104,7 @@
           'build_time.cc',
           'build_time.h',
           'callback.h',
+          'callback_helpers.cc',
           'callback_helpers.h',
           'callback_internal.cc',
           'callback_internal.h',
@@ -114,6 +117,7 @@
           'containers/hash_tables.h',
           'containers/linked_list.h',
           'containers/mru_cache.h',
+          'containers/scoped_ptr_hash_map.h',
           'containers/small_map.h',
           'containers/stack_container.h',
           'cpu.cc',
@@ -758,6 +762,13 @@
               ['include', '^threading/platform_thread_linux\\.cc$'],
             ],
           }],
+          ['OS == "android" and <(android_webview_build)==1', {
+            'defines': [
+               # WebView builds as part of the system which already has sincos;
+               # avoid defining it again as it causes a linker warning.
+               'ANDROID_SINCOS_PROVIDED',
+            ],
+          }],
           ['OS == "ios" and _toolset != "host"', {
             'sources/': [
               # Pull in specific Mac files for iOS (which have been filtered out
diff --git a/base/base.target.darwin-arm.mk b/base/base.target.darwin-arm.mk
index 3ecb816..c01af86 100644
--- a/base/base.target.darwin-arm.mk
+++ b/base/base.target.darwin-arm.mk
@@ -59,11 +59,13 @@
 	base/android/path_utils.cc \
 	base/android/sys_utils.cc \
 	base/at_exit.cc \
+	base/barrier_closure.cc \
 	base/base_paths.cc \
 	base/base_paths_android.cc \
 	base/base64.cc \
 	base/bind_helpers.cc \
 	base/build_time.cc \
+	base/callback_helpers.cc \
 	base/callback_internal.cc \
 	base/command_line.cc \
 	base/cpu.cc \
@@ -282,6 +284,7 @@
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
 	'-DBASE_IMPLEMENTATION' \
+	'-DANDROID_SINCOS_PROVIDED' \
 	'-DANDROID' \
 	'-D__GNU_SOURCE=1' \
 	'-DUSE_STLPORT=1' \
@@ -364,6 +367,7 @@
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
 	'-DBASE_IMPLEMENTATION' \
+	'-DANDROID_SINCOS_PROVIDED' \
 	'-DANDROID' \
 	'-D__GNU_SOURCE=1' \
 	'-DUSE_STLPORT=1' \
diff --git a/base/base.target.darwin-mips.mk b/base/base.target.darwin-mips.mk
index bfa2447..c95318b 100644
--- a/base/base.target.darwin-mips.mk
+++ b/base/base.target.darwin-mips.mk
@@ -59,11 +59,13 @@
 	base/android/path_utils.cc \
 	base/android/sys_utils.cc \
 	base/at_exit.cc \
+	base/barrier_closure.cc \
 	base/base_paths.cc \
 	base/base_paths_android.cc \
 	base/base64.cc \
 	base/bind_helpers.cc \
 	base/build_time.cc \
+	base/callback_helpers.cc \
 	base/callback_internal.cc \
 	base/command_line.cc \
 	base/cpu.cc \
@@ -281,6 +283,7 @@
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
 	'-DBASE_IMPLEMENTATION' \
+	'-DANDROID_SINCOS_PROVIDED' \
 	'-DANDROID' \
 	'-D__GNU_SOURCE=1' \
 	'-DUSE_STLPORT=1' \
@@ -362,6 +365,7 @@
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
 	'-DBASE_IMPLEMENTATION' \
+	'-DANDROID_SINCOS_PROVIDED' \
 	'-DANDROID' \
 	'-D__GNU_SOURCE=1' \
 	'-DUSE_STLPORT=1' \
diff --git a/base/base.target.darwin-x86.mk b/base/base.target.darwin-x86.mk
index 3d8c404..6969ebe 100644
--- a/base/base.target.darwin-x86.mk
+++ b/base/base.target.darwin-x86.mk
@@ -60,11 +60,13 @@
 	base/android/sys_utils.cc \
 	base/at_exit.cc \
 	base/atomicops_internals_x86_gcc.cc \
+	base/barrier_closure.cc \
 	base/base_paths.cc \
 	base/base_paths_android.cc \
 	base/base64.cc \
 	base/bind_helpers.cc \
 	base/build_time.cc \
+	base/callback_helpers.cc \
 	base/callback_internal.cc \
 	base/command_line.cc \
 	base/cpu.cc \
@@ -285,6 +287,7 @@
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
 	'-DBASE_IMPLEMENTATION' \
+	'-DANDROID_SINCOS_PROVIDED' \
 	'-DANDROID' \
 	'-D__GNU_SOURCE=1' \
 	'-DUSE_STLPORT=1' \
@@ -370,6 +373,7 @@
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
 	'-DBASE_IMPLEMENTATION' \
+	'-DANDROID_SINCOS_PROVIDED' \
 	'-DANDROID' \
 	'-D__GNU_SOURCE=1' \
 	'-DUSE_STLPORT=1' \
diff --git a/base/base.target.linux-arm.mk b/base/base.target.linux-arm.mk
index 3ecb816..c01af86 100644
--- a/base/base.target.linux-arm.mk
+++ b/base/base.target.linux-arm.mk
@@ -59,11 +59,13 @@
 	base/android/path_utils.cc \
 	base/android/sys_utils.cc \
 	base/at_exit.cc \
+	base/barrier_closure.cc \
 	base/base_paths.cc \
 	base/base_paths_android.cc \
 	base/base64.cc \
 	base/bind_helpers.cc \
 	base/build_time.cc \
+	base/callback_helpers.cc \
 	base/callback_internal.cc \
 	base/command_line.cc \
 	base/cpu.cc \
@@ -282,6 +284,7 @@
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
 	'-DBASE_IMPLEMENTATION' \
+	'-DANDROID_SINCOS_PROVIDED' \
 	'-DANDROID' \
 	'-D__GNU_SOURCE=1' \
 	'-DUSE_STLPORT=1' \
@@ -364,6 +367,7 @@
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
 	'-DBASE_IMPLEMENTATION' \
+	'-DANDROID_SINCOS_PROVIDED' \
 	'-DANDROID' \
 	'-D__GNU_SOURCE=1' \
 	'-DUSE_STLPORT=1' \
diff --git a/base/base.target.linux-mips.mk b/base/base.target.linux-mips.mk
index bfa2447..c95318b 100644
--- a/base/base.target.linux-mips.mk
+++ b/base/base.target.linux-mips.mk
@@ -59,11 +59,13 @@
 	base/android/path_utils.cc \
 	base/android/sys_utils.cc \
 	base/at_exit.cc \
+	base/barrier_closure.cc \
 	base/base_paths.cc \
 	base/base_paths_android.cc \
 	base/base64.cc \
 	base/bind_helpers.cc \
 	base/build_time.cc \
+	base/callback_helpers.cc \
 	base/callback_internal.cc \
 	base/command_line.cc \
 	base/cpu.cc \
@@ -281,6 +283,7 @@
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
 	'-DBASE_IMPLEMENTATION' \
+	'-DANDROID_SINCOS_PROVIDED' \
 	'-DANDROID' \
 	'-D__GNU_SOURCE=1' \
 	'-DUSE_STLPORT=1' \
@@ -362,6 +365,7 @@
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
 	'-DBASE_IMPLEMENTATION' \
+	'-DANDROID_SINCOS_PROVIDED' \
 	'-DANDROID' \
 	'-D__GNU_SOURCE=1' \
 	'-DUSE_STLPORT=1' \
diff --git a/base/base.target.linux-x86.mk b/base/base.target.linux-x86.mk
index 3d8c404..6969ebe 100644
--- a/base/base.target.linux-x86.mk
+++ b/base/base.target.linux-x86.mk
@@ -60,11 +60,13 @@
 	base/android/sys_utils.cc \
 	base/at_exit.cc \
 	base/atomicops_internals_x86_gcc.cc \
+	base/barrier_closure.cc \
 	base/base_paths.cc \
 	base/base_paths_android.cc \
 	base/base64.cc \
 	base/bind_helpers.cc \
 	base/build_time.cc \
+	base/callback_helpers.cc \
 	base/callback_internal.cc \
 	base/command_line.cc \
 	base/cpu.cc \
@@ -285,6 +287,7 @@
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
 	'-DBASE_IMPLEMENTATION' \
+	'-DANDROID_SINCOS_PROVIDED' \
 	'-DANDROID' \
 	'-D__GNU_SOURCE=1' \
 	'-DUSE_STLPORT=1' \
@@ -370,6 +373,7 @@
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
 	'-DBASE_IMPLEMENTATION' \
+	'-DANDROID_SINCOS_PROVIDED' \
 	'-DANDROID' \
 	'-D__GNU_SOURCE=1' \
 	'-DUSE_STLPORT=1' \
diff --git a/base/bind_helpers.cc b/base/bind_helpers.cc
index f2fc3bb..f1fe46d 100644
--- a/base/bind_helpers.cc
+++ b/base/bind_helpers.cc
@@ -11,19 +11,4 @@
 void DoNothing() {
 }
 
-ScopedClosureRunner::ScopedClosureRunner(const Closure& closure)
-    : closure_(closure) {
-}
-
-ScopedClosureRunner::~ScopedClosureRunner() {
-  if (!closure_.is_null())
-    closure_.Run();
-}
-
-Closure ScopedClosureRunner::Release() {
-  Closure result = closure_;
-  closure_.Reset();
-  return result;
-}
-
 }  // namespace base
diff --git a/base/bind_helpers.h b/base/bind_helpers.h
index 0cfaab7..d717892 100644
--- a/base/bind_helpers.h
+++ b/base/bind_helpers.h
@@ -139,10 +139,6 @@
 //                        pointer when invoked. Only use this when necessary.
 //                        In most cases MessageLoop::DeleteSoon() is a better
 //                        fit.
-//   ScopedClosureRunner - Scoper object that runs the wrapped closure when it
-//                         goes out of scope. It's conceptually similar to
-//                         scoped_ptr<> but calls Run() instead of deleting
-//                         the pointer.
 
 #ifndef BASE_BIND_HELPERS_H_
 #define BASE_BIND_HELPERS_H_
@@ -543,21 +539,6 @@
   delete obj;
 }
 
-// ScopedClosureRunner is akin to scoped_ptr for Closures. It ensures that the
-// Closure is executed and deleted no matter how the current scope exits.
-class BASE_EXPORT ScopedClosureRunner {
- public:
-  explicit ScopedClosureRunner(const Closure& closure);
-  ~ScopedClosureRunner();
-
-  Closure Release();
-
- private:
-  Closure closure_;
-
-  DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedClosureRunner);
-};
-
 }  // namespace base
 
 #endif  // BASE_BIND_HELPERS_H_
diff --git a/base/bind_helpers_unittest.cc b/base/bind_helpers_unittest.cc
deleted file mode 100644
index 3ef2d75..0000000
--- a/base/bind_helpers_unittest.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/bind_helpers.h"
-
-#include "base/callback.h"
-#include "base/bind.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-void Increment(int* value) {
-  (*value)++;
-}
-
-TEST(BindHelpersTest, TestScopedClosureRunnerExitScope) {
-  int run_count = 0;
-  {
-    base::ScopedClosureRunner runner(base::Bind(&Increment, &run_count));
-    EXPECT_EQ(0, run_count);
-  }
-  EXPECT_EQ(1, run_count);
-}
-
-TEST(BindHelpersTest, TestScopedClosureRunnerRelease) {
-  int run_count = 0;
-  base::Closure c;
-  {
-    base::ScopedClosureRunner runner(base::Bind(&Increment, &run_count));
-    c = runner.Release();
-    EXPECT_EQ(0, run_count);
-  }
-  EXPECT_EQ(0, run_count);
-  c.Run();
-  EXPECT_EQ(1, run_count);
-}
-
-}  // namespace
diff --git a/base/callback_helpers.cc b/base/callback_helpers.cc
new file mode 100644
index 0000000..ef02b2b
--- /dev/null
+++ b/base/callback_helpers.cc
@@ -0,0 +1,42 @@
+// 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 "base/callback_helpers.h"
+
+#include "base/callback.h"
+
+namespace base {
+
+ScopedClosureRunner::ScopedClosureRunner() {
+}
+
+ScopedClosureRunner::ScopedClosureRunner(const Closure& closure)
+    : closure_(closure) {
+}
+
+ScopedClosureRunner::~ScopedClosureRunner() {
+  if (!closure_.is_null())
+    closure_.Run();
+}
+
+void ScopedClosureRunner::Reset() {
+  Closure old_closure = Release();
+  if (!old_closure.is_null())
+    old_closure.Run();
+}
+
+void ScopedClosureRunner::Reset(const Closure& closure) {
+  Closure old_closure = Release();
+  closure_ = closure;
+  if (!old_closure.is_null())
+    old_closure.Run();
+}
+
+Closure ScopedClosureRunner::Release() {
+  Closure result = closure_;
+  closure_.Reset();
+  return result;
+}
+
+}  // namespace base
diff --git a/base/callback_helpers.h b/base/callback_helpers.h
index 52cb71b..8481e3e 100644
--- a/base/callback_helpers.h
+++ b/base/callback_helpers.h
@@ -14,7 +14,9 @@
 #ifndef BASE_CALLBACK_HELPERS_H_
 #define BASE_CALLBACK_HELPERS_H_
 
+#include "base/basictypes.h"
 #include "base/callback.h"
+#include "base/compiler_specific.h"
 
 namespace base {
 
@@ -25,6 +27,24 @@
   return ret;
 }
 
+// ScopedClosureRunner is akin to scoped_ptr for Closures. It ensures that the
+// Closure is executed and deleted no matter how the current scope exits.
+class BASE_EXPORT ScopedClosureRunner {
+ public:
+  ScopedClosureRunner();
+  explicit ScopedClosureRunner(const Closure& closure);
+  ~ScopedClosureRunner();
+
+  void Reset();
+  void Reset(const Closure& closure);
+  Closure Release() WARN_UNUSED_RESULT;
+
+ private:
+  Closure closure_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedClosureRunner);
+};
+
 }  // namespace base
 
 #endif  // BASE_CALLBACK_HELPERS_H_
diff --git a/base/callback_helpers_unittest.cc b/base/callback_helpers_unittest.cc
new file mode 100644
index 0000000..3b17a6b
--- /dev/null
+++ b/base/callback_helpers_unittest.cc
@@ -0,0 +1,61 @@
+// 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 "base/callback_helpers.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void Increment(int* value) {
+  (*value)++;
+}
+
+TEST(BindHelpersTest, TestScopedClosureRunnerExitScope) {
+  int run_count = 0;
+  {
+    base::ScopedClosureRunner runner(base::Bind(&Increment, &run_count));
+    EXPECT_EQ(0, run_count);
+  }
+  EXPECT_EQ(1, run_count);
+}
+
+TEST(BindHelpersTest, TestScopedClosureRunnerRelease) {
+  int run_count = 0;
+  base::Closure c;
+  {
+    base::ScopedClosureRunner runner(base::Bind(&Increment, &run_count));
+    c = runner.Release();
+    EXPECT_EQ(0, run_count);
+  }
+  EXPECT_EQ(0, run_count);
+  c.Run();
+  EXPECT_EQ(1, run_count);
+}
+
+TEST(BindHelpersTest, TestScopedClosureRunnerReset) {
+  int run_count_1 = 0;
+  int run_count_2 = 0;
+  {
+    base::ScopedClosureRunner runner;
+    runner.Reset(base::Bind(&Increment, &run_count_1));
+    runner.Reset(base::Bind(&Increment, &run_count_2));
+    EXPECT_EQ(1, run_count_1);
+    EXPECT_EQ(0, run_count_2);
+  }
+  EXPECT_EQ(1, run_count_2);
+
+  int run_count_3 = 0;
+  {
+    base::ScopedClosureRunner runner(base::Bind(&Increment, &run_count_3));
+    EXPECT_EQ(0, run_count_3);
+    runner.Reset();
+    EXPECT_EQ(1, run_count_3);
+  }
+  EXPECT_EQ(1, run_count_3);
+}
+
+}  // namespace
diff --git a/base/containers/scoped_ptr_hash_map.h b/base/containers/scoped_ptr_hash_map.h
new file mode 100644
index 0000000..8d92b39
--- /dev/null
+++ b/base/containers/scoped_ptr_hash_map.h
@@ -0,0 +1,157 @@
+// 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 BASE_CONTAINERS_SCOPED_PTR_HASH_MAP_H_
+#define BASE_CONTAINERS_SCOPED_PTR_HASH_MAP_H_
+
+#include <algorithm>
+#include <utility>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+
+namespace base {
+
+// This type acts like a hash_map<K, scoped_ptr<V> >, based on top of
+// base::hash_map. The ScopedPtrHashMap has ownership of all values in the data
+// structure.
+template <typename Key, typename Value>
+class ScopedPtrHashMap {
+  typedef base::hash_map<Key, Value*> Container;
+
+ public:
+  typedef typename Container::iterator iterator;
+  typedef typename Container::const_iterator const_iterator;
+
+  ScopedPtrHashMap() {}
+
+  ~ScopedPtrHashMap() { clear(); }
+
+  void swap(ScopedPtrHashMap<Key, Value>& other) {
+    data_.swap(other.data_);
+  }
+
+  std::pair<iterator, bool> insert(
+      std::pair<Key, const scoped_ptr<Value> > pair) {
+    return data_.insert(
+        std::pair<Key, Value*>(pair.first, pair.second.release()));
+  }
+
+  // Replaces value but not key if key is already present.
+  std::pair<iterator, bool> set(Key key, scoped_ptr<Value> data) {
+    iterator it = find(key);
+    if (it != end())
+      erase(it);
+    Value* raw_ptr = data.release();
+    return data_.insert(std::pair<Key, Value*>(key, raw_ptr));
+  }
+
+  // Does nothing if key is already present
+  std::pair<iterator, bool> add(Key key, scoped_ptr<Value> data) {
+    Value* raw_ptr = data.release();
+    return data_.insert(std::pair<Key, Value*>(key, raw_ptr));
+  }
+
+  void erase(iterator it) {
+    if (it->second)
+      delete it->second;
+    data_.erase(it);
+  }
+
+  size_t erase(const Key& k) {
+    iterator it = data_.find(k);
+    if (it == data_.end())
+      return 0;
+    erase(it);
+    return 1;
+  }
+
+  scoped_ptr<Value> take(iterator it) {
+    DCHECK(it != data_.end());
+    if (it == data_.end())
+      return scoped_ptr<Value>();
+
+    Key key = it->first;
+    scoped_ptr<Value> ret(it->second);
+    data_.erase(it);
+    data_.insert(std::pair<Key, Value*>(key, static_cast<Value*>(NULL)));
+    return ret.Pass();
+  }
+
+  scoped_ptr<Value> take(const Key& k) {
+    iterator it = find(k);
+    if (it == data_.end())
+      return scoped_ptr<Value>();
+
+    return take(it);
+  }
+
+  scoped_ptr<Value> take_and_erase(iterator it) {
+    DCHECK(it != data_.end());
+    if (it == data_.end())
+      return scoped_ptr<Value>();
+
+    scoped_ptr<Value> ret(it->second);
+    data_.erase(it);
+    return ret.Pass();
+  }
+
+  scoped_ptr<Value> take_and_erase(const Key& k) {
+    iterator it = find(k);
+    if (it == data_.end())
+      return scoped_ptr<Value>();
+
+    return take_and_erase(it);
+  }
+
+  // Returns the first element in the hash_map that matches the given key.
+  // If no such element exists it returns NULL.
+  Value* get(const Key& k) const {
+    const_iterator it = find(k);
+    if (it == end())
+      return 0;
+    return it->second;
+  }
+
+  inline bool contains(const Key& k) const { return data_.count(k) > 0; }
+
+  inline void clear() { STLDeleteValues(&data_); }
+
+  inline const_iterator find(const Key& k) const { return data_.find(k); }
+  inline iterator find(const Key& k) { return data_.find(k); }
+
+  inline size_t count(const Key& k) const { return data_.count(k); }
+  inline std::pair<const_iterator, const_iterator> equal_range(
+      const Key& k) const {
+    return data_.equal_range(k);
+  }
+  inline std::pair<iterator, iterator> equal_range(const Key& k) {
+    return data_.equal_range(k);
+  }
+
+  inline size_t size() const { return data_.size(); }
+  inline size_t max_size() const { return data_.max_size(); }
+
+  inline bool empty() const { return data_.empty(); }
+
+  inline size_t bucket_count() const { return data_.bucket_count(); }
+  inline void resize(size_t size) const { return data_.resize(size); }
+
+  inline iterator begin() { return data_.begin(); }
+  inline const_iterator begin() const { return data_.begin(); }
+  inline iterator end() { return data_.end(); }
+  inline const_iterator end() const { return data_.end(); }
+
+ private:
+  Container data_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedPtrHashMap);
+};
+
+}  // namespace base
+
+#endif  // BASE_CONTAINERS_SCOPED_PTR_HASH_MAP_H_
diff --git a/base/debug/leak_tracker.h b/base/debug/leak_tracker.h
index 93cea39..e5f0cb1 100644
--- a/base/debug/leak_tracker.h
+++ b/base/debug/leak_tracker.h
@@ -103,7 +103,7 @@
     // doesn't optimize it out, and it will appear in mini-dumps).
     if (count == 0x1234) {
       for (size_t i = 0; i < kMaxStackTracesToCopyOntoStack; ++i)
-        stacktraces[i].PrintBacktrace();
+        stacktraces[i].Print();
     }
   }
 
diff --git a/base/debug/stack_trace.h b/base/debug/stack_trace.h
index 53bed3c..b0883c1 100644
--- a/base/debug/stack_trace.h
+++ b/base/debug/stack_trace.h
@@ -55,8 +55,8 @@
   // number of elements in the returned array.
   const void* const* Addresses(size_t* count) const;
 
-  // Prints a backtrace to stderr
-  void PrintBacktrace() const;
+  // Prints the stack trace to stderr.
+  void Print() const;
 
   // Resolves backtrace to symbols and write to stream.
   void OutputToStream(std::ostream* os) const;
diff --git a/base/debug/stack_trace_android.cc b/base/debug/stack_trace_android.cc
index 4c53d4a..ec0508a 100644
--- a/base/debug/stack_trace_android.cc
+++ b/base/debug/stack_trace_android.cc
@@ -75,7 +75,7 @@
   count_ = state.frame_count;
 }
 
-void StackTrace::PrintBacktrace() const {
+void StackTrace::Print() const {
   std::string backtrace = ToString();
   __android_log_write(ANDROID_LOG_ERROR, "chromium", backtrace.c_str());
 }
diff --git a/base/debug/stack_trace_posix.cc b/base/debug/stack_trace_posix.cc
index 8a8ae47..eb5ee08 100644
--- a/base/debug/stack_trace_posix.cc
+++ b/base/debug/stack_trace_posix.cc
@@ -253,7 +253,7 @@
   }
   PrintToStderr("\n");
 
-  debug::StackTrace().PrintBacktrace();
+  debug::StackTrace().Print();
 
 #if defined(OS_LINUX)
 #if ARCH_CPU_X86_FAMILY
@@ -474,7 +474,7 @@
   count_ = std::max(backtrace(trace_, arraysize(trace_)), 0);
 }
 
-void StackTrace::PrintBacktrace() const {
+void StackTrace::Print() const {
   // NOTE: This code MUST be async-signal safe (it's used by in-process
   // stack dumping signal handler). NO malloc or stdio is allowed here.
 
diff --git a/base/debug/stack_trace_unittest.cc b/base/debug/stack_trace_unittest.cc
index b676327..701ebc4 100644
--- a/base/debug/stack_trace_unittest.cc
+++ b/base/debug/stack_trace_unittest.cc
@@ -128,7 +128,7 @@
 
 // The test is used for manual testing, e.g., to see the raw output.
 TEST_F(StackTraceTest, DebugPrintBacktrace) {
-  StackTrace().PrintBacktrace();
+  StackTrace().Print();
 }
 
 #if defined(OS_POSIX) && !defined(OS_ANDROID)
diff --git a/base/debug/stack_trace_win.cc b/base/debug/stack_trace_win.cc
index 986241b..eb35b6a 100644
--- a/base/debug/stack_trace_win.cc
+++ b/base/debug/stack_trace_win.cc
@@ -30,7 +30,7 @@
 // Prints the exception call stack.
 // This is the unit tests exception filter.
 long WINAPI StackDumpExceptionFilter(EXCEPTION_POINTERS* info) {
-  debug::StackTrace(info).PrintBacktrace();
+  debug::StackTrace(info).Print();
   if (g_previous_filter)
     return g_previous_filter(info);
   return EXCEPTION_CONTINUE_SEARCH;
@@ -248,7 +248,7 @@
     trace_[i] = NULL;
 }
 
-void StackTrace::PrintBacktrace() const {
+void StackTrace::Print() const {
   OutputToStream(&std::cerr);
 }
 
diff --git a/base/debug/trace_event.h b/base/debug/trace_event.h
index 6805513..43eb09c 100644
--- a/base/debug/trace_event.h
+++ b/base/debug/trace_event.h
@@ -233,6 +233,14 @@
   INTERNAL_TRACE_EVENT_ADD_SCOPED( \
       category_group, name, arg1_name, arg1_val, arg2_name, arg2_val)
 
+// Records events like TRACE_EVENT2 but uses |memory_tag| for memory tracing.
+// Use this where |name| is too generic to accurately aggregate allocations.
+#define TRACE_EVENT_WITH_MEMORY_TAG2( \
+    category, name, memory_tag, arg1_name, arg1_val, arg2_name, arg2_val) \
+  INTERNAL_TRACE_MEMORY(category, memory_tag) \
+  INTERNAL_TRACE_EVENT_ADD_SCOPED( \
+      category, name, arg1_name, arg1_val, arg2_name, arg2_val)
+
 // UNSHIPPED_TRACE_EVENT* are like TRACE_EVENT* except that they are not
 // included in official builds.
 
diff --git a/base/debug/trace_event_impl.cc b/base/debug/trace_event_impl.cc
index 2f63f91..f221005 100644
--- a/base/debug/trace_event_impl.cc
+++ b/base/debug/trace_event_impl.cc
@@ -1697,6 +1697,7 @@
         TRACE_EVENT_FLAG_NONE);     // flags
   } else {
     category_group_enabled_ = NULL;
+    name_ = NULL;
   }
 }
 
diff --git a/base/debug/trace_event_memory.cc b/base/debug/trace_event_memory.cc
index fb5f65b..42655b3 100644
--- a/base/debug/trace_event_memory.cc
+++ b/base/debug/trace_event_memory.cc
@@ -19,9 +19,10 @@
 
 namespace {
 
-// Maximum number of nested TRACE_MEMORY scopes to record. Must be greater than
-// or equal to HeapProfileTable::kMaxStackDepth.
-const size_t kMaxStackSize = 32;
+// Maximum number of nested TRACE_EVENT scopes to record. Must be less than
+// or equal to HeapProfileTable::kMaxStackDepth / 2 because we record two
+// entries on the pseudo-stack per scope.
+const size_t kMaxScopeDepth = 16;
 
 /////////////////////////////////////////////////////////////////////////////
 // Holds a memory dump until the tracing system needs to serialize it.
@@ -46,13 +47,17 @@
 /////////////////////////////////////////////////////////////////////////////
 // Records a stack of TRACE_MEMORY events. One per thread is required.
 struct TraceMemoryStack {
-  TraceMemoryStack() : index_(0) {
-    memset(category_stack_, 0, kMaxStackSize * sizeof(category_stack_[0]));
+  TraceMemoryStack() : scope_depth(0) {
+    memset(scope_data, 0, kMaxScopeDepth * sizeof(scope_data[0]));
   }
 
-  // Points to the next free entry.
-  size_t index_;
-  const char* category_stack_[kMaxStackSize];
+  // Depth of the currently nested TRACE_EVENT scopes. Allowed to be greater
+  // than kMaxScopeDepth so we can match scope pushes and pops even if we don't
+  // have enough space to store the EventData.
+  size_t scope_depth;
+
+  // Stack of categories and names.
+  ScopedTraceMemory::ScopeData scope_data[kMaxScopeDepth];
 };
 
 // Pointer to a TraceMemoryStack per thread.
@@ -103,8 +108,17 @@
   return stack;
 }
 
-// Returns a "pseudo-stack" of pointers to trace events.
-// TODO(jamescook): Record both category and name, perhaps in a pair for speed.
+// Returns a "pseudo-stack" of pointers to trace event categories and names.
+// Because tcmalloc stores one pointer per stack frame this converts N nested
+// trace events into N * 2 pseudo-stack entries. Thus this macro invocation:
+//    TRACE_EVENT0("category1", "name1");
+//    TRACE_EVENT0("category2", "name2");
+// becomes this pseudo-stack:
+//    stack_out[0] = "category1"
+//    stack_out[1] = "name1"
+//    stack_out[2] = "category2"
+//    stack_out[3] = "name2"
+// Returns int instead of size_t to match the signature required by tcmalloc.
 int GetPseudoStack(int skip_count_ignored, void** stack_out) {
   // If the tracing system isn't fully initialized, just skip this allocation.
   // Attempting to initialize will allocate memory, causing this function to
@@ -113,14 +127,15 @@
     return 0;
   TraceMemoryStack* stack =
       static_cast<TraceMemoryStack*>(tls_trace_memory_stack.Get());
-  // Copy at most kMaxStackSize stack entries.
-  const size_t count = std::min(stack->index_, kMaxStackSize);
+  // Copy at most kMaxScopeDepth scope entries.
+  const size_t count = std::min(stack->scope_depth, kMaxScopeDepth);
   // Notes that memcpy() works for zero bytes.
   memcpy(stack_out,
-         stack->category_stack_,
-         count * sizeof(stack->category_stack_[0]));
-  // Function must return an int to match the signature required by tcmalloc.
-  return static_cast<int>(count);
+         stack->scope_data,
+         count * sizeof(stack->scope_data[0]));
+  // Each item in the trace event stack contains both name and category so tell
+  // tcmalloc that we have returned |count| * 2 stack frames.
+  return static_cast<int>(count * 2);
 }
 
 }  // namespace
@@ -231,19 +246,22 @@
 // static
 bool ScopedTraceMemory::enabled_ = false;
 
-ScopedTraceMemory::ScopedTraceMemory(const char* category) {
+ScopedTraceMemory::ScopedTraceMemory(const char* category, const char* name) {
   // Not enabled indicates that the trace system isn't running, so don't
   // record anything.
   if (!enabled_)
     return;
   // Get our thread's copy of the stack.
   TraceMemoryStack* trace_memory_stack = GetTraceMemoryStack();
-  const size_t index = trace_memory_stack->index_;
-  // Allow deep nesting of stacks (needed for tests), but only record
-  // |kMaxStackSize| entries.
-  if (index < kMaxStackSize)
-    trace_memory_stack->category_stack_[index] = category;
-  trace_memory_stack->index_++;
+  const size_t index = trace_memory_stack->scope_depth;
+  // Don't record data for deeply nested scopes, but continue to increment
+  // |stack_depth| so we can match pushes and pops.
+  if (index < kMaxScopeDepth) {
+    ScopeData& event = trace_memory_stack->scope_data[index];
+    event.category = category;
+    event.name = name;
+  }
+  trace_memory_stack->scope_depth++;
 }
 
 ScopedTraceMemory::~ScopedTraceMemory() {
@@ -255,8 +273,8 @@
   TraceMemoryStack* trace_memory_stack = GetTraceMemoryStack();
   // The tracing system can be turned on with ScopedTraceMemory objects
   // allocated on the stack, so avoid potential underflow as they are destroyed.
-  if (trace_memory_stack->index_ > 0)
-    trace_memory_stack->index_--;
+  if (trace_memory_stack->scope_depth > 0)
+    trace_memory_stack->scope_depth--;
 }
 
 // static
@@ -272,15 +290,16 @@
 }
 
 // static
-int ScopedTraceMemory::GetStackIndexForTest() {
+int ScopedTraceMemory::GetStackDepthForTest() {
   TraceMemoryStack* stack = GetTraceMemoryStack();
-  return static_cast<int>(stack->index_);
+  return static_cast<int>(stack->scope_depth);
 }
 
 // static
-const char* ScopedTraceMemory::GetItemForTest(int index) {
+ScopedTraceMemory::ScopeData ScopedTraceMemory::GetScopeDataForTest(
+    int stack_index) {
   TraceMemoryStack* stack = GetTraceMemoryStack();
-  return stack->category_stack_[index];
+  return stack->scope_data[stack_index];
 }
 
 /////////////////////////////////////////////////////////////////////////////
@@ -366,7 +385,8 @@
   // 98009 = Total bytes (malloc bytes)
   //
   // 0x7fa7fa9b9ba0 0x7fa7f4b3be13 = Stack trace represented as pointers to
-  //                                 static strings from trace event names.
+  //                                 static strings from trace event categories
+  //                                 and names.
   std::vector<std::string> tokens;
   Tokenize(line, " :[]@", &tokens);
   // It's valid to have no stack addresses, so only require 4 tokens.
@@ -384,24 +404,26 @@
   output->append(tokens[1]);
   output->append(", \"trace\": \"");
 
-  // Convert the "stack addresses" into strings.
+  // Convert pairs of "stack addresses" into category and name strings.
   const std::string kSingleQuote = "'";
-  for (size_t t = 4; t < tokens.size(); ++t) {
-    // Each stack address is a pointer to a constant trace name string.
-    uint64 address = 0;
-    if (!base::HexStringToUInt64(tokens[t], &address))
-      break;
-    // This is ugly but otherwise tcmalloc would need to gain a special output
-    // serializer for pseudo-stacks. Note that this cast also handles 64-bit to
-    // 32-bit conversion if necessary. Tests use a null address.
-    const char* trace_name =
-        address ? reinterpret_cast<const char*>(address) : "null";
+  for (size_t t = 4; t < tokens.size(); t += 2) {
+    // Casting strings into pointers is ugly but otherwise tcmalloc would need
+    // to gain a special output serializer just for pseudo-stacks.
+    const char* trace_category = StringFromHexAddress(tokens[t]);
+    DCHECK_LT(t + 1, tokens.size());
+    const char* trace_name = StringFromHexAddress(tokens[t + 1]);
+
+    // TODO(jamescook): Report the trace category and name separately to the
+    // trace viewer and allow it to decide what decorations to apply. For now
+    // just hard-code a decoration for posted tasks.
+    std::string trace_string(trace_name);
+    if (!strcmp(trace_category, "task"))
+      trace_string.append("->PostTask");
 
     // Some trace name strings have double quotes, convert them to single.
-    std::string trace_name_string(trace_name);
-    ReplaceChars(trace_name_string, "\"", kSingleQuote, &trace_name_string);
+    ReplaceChars(trace_string, "\"", kSingleQuote, &trace_string);
 
-    output->append(trace_name_string);
+    output->append(trace_string);
 
     // Trace viewer expects a trailing space.
     output->append(" ");
@@ -410,5 +432,15 @@
   return true;
 }
 
+const char* StringFromHexAddress(const std::string& hex_address) {
+  uint64 address = 0;
+  if (!base::HexStringToUInt64(hex_address, &address))
+    return "error";
+  if (!address)
+    return "null";
+  // Note that this cast handles 64-bit to 32-bit conversion if necessary.
+  return reinterpret_cast<const char*>(address);
+}
+
 }  // namespace debug
 }  // namespace base
diff --git a/base/debug/trace_event_memory.h b/base/debug/trace_event_memory.h
index 0d82198..5427154 100644
--- a/base/debug/trace_event_memory.h
+++ b/base/debug/trace_event_memory.h
@@ -87,9 +87,14 @@
 // recording by tcmalloc heap profiling.
 class BASE_EXPORT ScopedTraceMemory {
  public:
-  // Memory for |name| must be static, for example, a literal string in
-  // a TRACE_EVENT macro.
-  explicit ScopedTraceMemory(const char* name);
+  struct ScopeData {
+    const char* category;
+    const char* name;
+  };
+
+  // Memory for |category| and |name| must be static, for example, literal
+  // strings in a TRACE_EVENT macro.
+  ScopedTraceMemory(const char* category, const char* name);
   ~ScopedTraceMemory();
 
   // Enables the storing of trace names on a per-thread stack.
@@ -98,8 +103,8 @@
   // Testing interface:
   static void InitForTest();
   static void CleanupForTest();
-  static int GetStackIndexForTest();
-  static const char* GetItemForTest(int index);
+  static int GetStackDepthForTest();
+  static ScopeData GetScopeDataForTest(int stack_index);
 
  private:
   static bool enabled_;
@@ -125,6 +130,11 @@
 BASE_EXPORT bool AppendHeapProfileLineAsTraceFormat(const std::string& line,
                                                     std::string* output);
 
+// Returns a pointer to a string given its hexadecimal address in |hex_address|.
+// Handles both 32-bit and 64-bit addresses. Returns "null" for null pointers
+// and "error" if |address| could not be parsed. Visible for testing.
+BASE_EXPORT const char* StringFromHexAddress(const std::string& hex_address);
+
 }  // namespace debug
 }  // namespace base
 
@@ -136,10 +146,9 @@
 
 // This is the core macro that adds a scope to each TRACE_EVENT location.
 // It generates a unique local variable name using the macros above.
-// TODO(jamescook): Make it record both category and name.
 #if defined(TCMALLOC_TRACE_MEMORY_SUPPORTED)
 #define INTERNAL_TRACE_MEMORY(category, name) \
-  base::debug::ScopedTraceMemory INTERNAL_TRACE_MEMORY_ID(name);
+  base::debug::ScopedTraceMemory INTERNAL_TRACE_MEMORY_ID(category, name);
 #else
 #define INTERNAL_TRACE_MEMORY(category, name)
 #endif  // defined(TRACE_MEMORY_SUPPORTED)
diff --git a/base/debug/trace_event_memory_unittest.cc b/base/debug/trace_event_memory_unittest.cc
index 7c4eae6..c0a1568 100644
--- a/base/debug/trace_event_memory_unittest.cc
+++ b/base/debug/trace_event_memory_unittest.cc
@@ -72,40 +72,39 @@
   ScopedTraceMemory::InitForTest();
 
   // Start with an empty stack.
-  EXPECT_EQ(0, ScopedTraceMemory::GetStackIndexForTest());
+  EXPECT_EQ(0, ScopedTraceMemory::GetStackDepthForTest());
 
   {
     // Push an item.
-    const char kScope1[] = "scope1";
-    ScopedTraceMemory scope1(kScope1);
-    EXPECT_EQ(1, ScopedTraceMemory::GetStackIndexForTest());
-    EXPECT_EQ(kScope1, ScopedTraceMemory::GetItemForTest(0));
+    ScopedTraceMemory scope1("cat1", "name1");
+    EXPECT_EQ(1, ScopedTraceMemory::GetStackDepthForTest());
+    EXPECT_EQ("cat1", ScopedTraceMemory::GetScopeDataForTest(0).category);
+    EXPECT_EQ("name1", ScopedTraceMemory::GetScopeDataForTest(0).name);
 
     {
       // One more item.
-      const char kScope2[] = "scope2";
-      ScopedTraceMemory scope2(kScope2);
-      EXPECT_EQ(2, ScopedTraceMemory::GetStackIndexForTest());
-      EXPECT_EQ(kScope2, ScopedTraceMemory::GetItemForTest(1));
+      ScopedTraceMemory scope2("cat2", "name2");
+      EXPECT_EQ(2, ScopedTraceMemory::GetStackDepthForTest());
+      EXPECT_EQ("cat2", ScopedTraceMemory::GetScopeDataForTest(1).category);
+      EXPECT_EQ("name2", ScopedTraceMemory::GetScopeDataForTest(1).name);
     }
 
     // Ended scope 2.
-    EXPECT_EQ(1, ScopedTraceMemory::GetStackIndexForTest());
+    EXPECT_EQ(1, ScopedTraceMemory::GetStackDepthForTest());
   }
 
   // Ended scope 1.
-  EXPECT_EQ(0, ScopedTraceMemory::GetStackIndexForTest());
+  EXPECT_EQ(0, ScopedTraceMemory::GetStackDepthForTest());
 
   ScopedTraceMemory::CleanupForTest();
 }
 
 void TestDeepScopeNesting(int current, int depth) {
-  EXPECT_EQ(current, ScopedTraceMemory::GetStackIndexForTest());
-  const char kCategory[] = "foo";
-  ScopedTraceMemory scope(kCategory);
+  EXPECT_EQ(current, ScopedTraceMemory::GetStackDepthForTest());
+  ScopedTraceMemory scope("category", "name");
   if (current < depth)
     TestDeepScopeNesting(current + 1, depth);
-  EXPECT_EQ(current + 1, ScopedTraceMemory::GetStackIndexForTest());
+  EXPECT_EQ(current + 1, ScopedTraceMemory::GetStackDepthForTest());
 }
 
 TEST_F(TraceMemoryTest, DeepScopeNesting) {
@@ -147,27 +146,48 @@
   std::string junk_output;
   EXPECT_FALSE(AppendHeapProfileLineAsTraceFormat("junk", &junk_output));
 
-  // Input with the addresses of name1 and name2.
-  const char kName1[] = "name1";
-  const char kName2[] = "name2";
+  // Input with normal category and name entries.
+  const char kCategory[] = "category";
+  const char kName[] = "name";
   std::ostringstream input;
-  input << "   68:     4195 [  1087:    98009] @ " << &kName1 << " " << &kName2;
+  input << "   68:     4195 [  1087:    98009] @ " << &kCategory << " "
+        << &kName;
   const std::string kExpectedOutput =
       ",\n"
       "{"
       "\"current_allocs\": 68, "
       "\"current_bytes\": 4195, "
-      "\"trace\": \"name1 name2 \""
+      "\"trace\": \"name \""
       "}";
   std::string output;
   EXPECT_TRUE(
       AppendHeapProfileLineAsTraceFormat(input.str().c_str(), &output));
   EXPECT_EQ(kExpectedOutput, output);
 
+  // Input with with the category "task".
+  // TODO(jamescook): Eliminate this special case and move the logic to the
+  // trace viewer code.
+  const char kTaskCategory[] = "task";
+  const char kTaskName[] = "TaskName";
+  std::ostringstream input2;
+  input2 << "   68:     4195 [  1087:    98009] @ " << &kTaskCategory << " "
+        << &kTaskName;
+  const std::string kExpectedOutput2 =
+      ",\n"
+      "{"
+      "\"current_allocs\": 68, "
+      "\"current_bytes\": 4195, "
+      "\"trace\": \"TaskName->PostTask \""
+      "}";
+  std::string output2;
+  EXPECT_TRUE(
+      AppendHeapProfileLineAsTraceFormat(input2.str().c_str(), &output2));
+  EXPECT_EQ(kExpectedOutput2, output2);
+
   // Zero current allocations is skipped.
   std::ostringstream zero_input;
-  zero_input << "   0:     0 [  1087:    98009] @ " << &kName1 << " "
-             << &kName2;
+  zero_input << "   0:     0 [  1087:    98009] @ " << &kCategory << " "
+             << &kName;
   std::string zero_output;
   EXPECT_FALSE(AppendHeapProfileLineAsTraceFormat(zero_input.str().c_str(),
                                                   &zero_output));
@@ -200,12 +220,21 @@
       "\"trace\": \"\"},\n"
       "{\"current_allocs\": 77, "
       "\"current_bytes\": 32546, "
-      "\"trace\": \"null null \""
+      "\"trace\": \"null \""
       "}]\n";
   std::string output;
   AppendHeapProfileAsTraceFormat(input, &output);
   EXPECT_EQ(kExpectedOutput, output);
 }
 
+TEST_F(TraceMemoryTest, StringFromHexAddress) {
+  EXPECT_STREQ("null", StringFromHexAddress("0x0"));
+  EXPECT_STREQ("error", StringFromHexAddress("not an address"));
+  const char kHello[] = "hello";
+  std::ostringstream hex_address;
+  hex_address << &kHello;
+  EXPECT_STREQ(kHello, StringFromHexAddress(hex_address.str()));
+}
+
 }  // namespace debug
 }  // namespace base
diff --git a/base/mac/bind_objc_block_unittest.mm b/base/mac/bind_objc_block_unittest.mm
index 888b3dc..a4bcd76 100644
--- a/base/mac/bind_objc_block_unittest.mm
+++ b/base/mac/bind_objc_block_unittest.mm
@@ -6,7 +6,7 @@
 
 #include "base/callback.h"
 #include "base/bind.h"
-#include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc
index 92039e2..701594a 100644
--- a/base/message_loop/message_loop.cc
+++ b/base/message_loop/message_loop.cc
@@ -44,7 +44,7 @@
 
 // A lazily created thread local storage for quick access to a thread's message
 // loop, if one exists.  This should be safe and free of static constructors.
-LazyInstance<base::ThreadLocalPointer<MessageLoop> > lazy_tls_ptr =
+LazyInstance<base::ThreadLocalPointer<MessageLoop> >::Leaky lazy_tls_ptr =
     LAZY_INSTANCE_INITIALIZER;
 
 // Logical events for Histogram profiling. Run with -message-loop-histogrammer
@@ -458,9 +458,13 @@
       TRACE_ID_MANGLE(GetTaskTraceID(pending_task)),
       "queue_duration",
       (start_time - pending_task.EffectiveTimePosted()).InMilliseconds());
-  TRACE_EVENT2("task", "MessageLoop::RunTask",
-               "src_file", pending_task.posted_from.file_name(),
-               "src_func", pending_task.posted_from.function_name());
+  // When tracing memory for posted tasks it's more valuable to attribute the
+  // memory allocations to the source function than generically to "RunTask".
+  TRACE_EVENT_WITH_MEMORY_TAG2(
+      "task", "MessageLoop::RunTask",
+      pending_task.posted_from.function_name(),  // Name for memory tracking.
+      "src_file", pending_task.posted_from.file_name(),
+      "src_func", pending_task.posted_from.function_name());
 
   DCHECK(nestable_tasks_allowed_);
   // Execute the task and assume the worst: It is probably not reentrant.
diff --git a/base/metrics/stats_table.cc b/base/metrics/stats_table.cc
index 4a3d939..403e28c 100644
--- a/base/metrics/stats_table.cc
+++ b/base/metrics/stats_table.cc
@@ -397,9 +397,9 @@
 
   int rv = 0;
   int* row = impl_->row(index);
-  for (int slot_id = 0; slot_id < impl_->max_threads(); slot_id++) {
+  for (int slot_id = 1; slot_id <= impl_->max_threads(); slot_id++) {
     if (pid == 0 || *impl_->thread_pid(slot_id) == pid)
-      rv += row[slot_id];
+      rv += row[slot_id-1];
   }
   return rv;
 }
diff --git a/base/observer_list.h b/base/observer_list.h
index 20f183d..b44e33f 100644
--- a/base/observer_list.h
+++ b/base/observer_list.h
@@ -157,9 +157,9 @@
     }
   }
 
+ protected:
   size_t size() const { return observers_.size(); }
 
- protected:
   void Compact() {
     observers_.erase(
         std::remove(observers_.begin(), observers_.end(),
diff --git a/base/os_compat_android.cc b/base/os_compat_android.cc
index 2643dc3..ec221e4 100644
--- a/base/os_compat_android.cc
+++ b/base/os_compat_android.cc
@@ -75,7 +75,8 @@
 // for each function would simply end up calling itself, resulting in a
 // runtime crash due to stack overflow.
 //
-#if defined(__GNUC__) && !defined(__clang__)
+#if defined(__GNUC__) && !defined(__clang__) && \
+    !defined(ANDROID_SINCOS_PROVIDED)
 
 // For the record, Clang does not support the 'optimize' attribute.
 // In the unlikely event that it begins performing this optimization too,
diff --git a/base/perftimer.cc b/base/perftimer.cc
deleted file mode 100644
index 9ab7c6b..0000000
--- a/base/perftimer.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2006-2008 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/perftimer.h"
-
-#include <stdio.h>
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/file_util.h"
-#include "base/files/file_path.h"
-#include "base/logging.h"
-
-static FILE* perf_log_file = NULL;
-
-bool InitPerfLog(const base::FilePath& log_file) {
-  if (perf_log_file) {
-    // trying to initialize twice
-    NOTREACHED();
-    return false;
-  }
-
-  perf_log_file = file_util::OpenFile(log_file, "w");
-  return perf_log_file != NULL;
-}
-
-void FinalizePerfLog() {
-  if (!perf_log_file) {
-    // trying to cleanup without initializing
-    NOTREACHED();
-    return;
-  }
-  file_util::CloseFile(perf_log_file);
-}
-
-void LogPerfResult(const char* test_name, double value, const char* units) {
-  if (!perf_log_file) {
-    NOTREACHED();
-    return;
-  }
-
-  fprintf(perf_log_file, "%s\t%g\t%s\n", test_name, value, units);
-  printf("%s\t%g\t%s\n", test_name, value, units);
-}
diff --git a/base/perftimer.h b/base/perftimer.h
deleted file mode 100644
index 466f81d..0000000
--- a/base/perftimer.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (c) 2006-2008 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_PERFTIMER_H_
-#define BASE_PERFTIMER_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/time/time.h"
-
-namespace base {
-class FilePath;
-}
-
-// ----------------------------------------------------------------------
-// Initializes and finalizes the perf log. These functions should be
-// called at the beginning and end (respectively) of running all the
-// performance tests. The init function returns true on success.
-// ----------------------------------------------------------------------
-bool InitPerfLog(const base::FilePath& log_path);
-void FinalizePerfLog();
-
-// ----------------------------------------------------------------------
-// LogPerfResult
-//   Writes to the perf result log the given 'value' resulting from the
-//   named 'test'. The units are to aid in reading the log by people.
-// ----------------------------------------------------------------------
-void LogPerfResult(const char* test_name, double value, const char* units);
-
-// ----------------------------------------------------------------------
-// PerfTimer
-//   A simple wrapper around Now()
-// ----------------------------------------------------------------------
-class PerfTimer {
- public:
-  PerfTimer() {
-    begin_ = base::TimeTicks::Now();
-  }
-
-  // Returns the time elapsed since object construction
-  base::TimeDelta Elapsed() const {
-    return base::TimeTicks::Now() - begin_;
-  }
-
- private:
-  base::TimeTicks begin_;
-};
-
-// ----------------------------------------------------------------------
-// PerfTimeLogger
-//   Automates calling LogPerfResult for the common case where you want
-//   to measure the time that something took. Call Done() when the test
-//   is complete if you do extra work after the test or there are stack
-//   objects with potentially expensive constructors. Otherwise, this
-//   class with automatically log on destruction.
-// ----------------------------------------------------------------------
-class PerfTimeLogger {
- public:
-  explicit PerfTimeLogger(const char* test_name)
-      : logged_(false),
-        test_name_(test_name) {
-  }
-
-  ~PerfTimeLogger() {
-    if (!logged_)
-      Done();
-  }
-
-  void Done() {
-    // we use a floating-point millisecond value because it is more
-    // intuitive than microseconds and we want more precision than
-    // integer milliseconds
-    LogPerfResult(test_name_.c_str(), timer_.Elapsed().InMillisecondsF(), "ms");
-    logged_ = true;
-  }
-
- private:
-  bool logged_;
-  std::string test_name_;
-  PerfTimer timer_;
-};
-
-#endif  // BASE_PERFTIMER_H_
diff --git a/base/prefs/default_pref_store.cc b/base/prefs/default_pref_store.cc
index babb4d5..92abba1 100644
--- a/base/prefs/default_pref_store.cc
+++ b/base/prefs/default_pref_store.cc
@@ -22,8 +22,8 @@
   observers_.RemoveObserver(observer);
 }
 
-size_t DefaultPrefStore::NumberOfObservers() const {
-  return observers_.size();
+bool DefaultPrefStore::HasObservers() const {
+  return observers_.might_have_observers();
 }
 
 void DefaultPrefStore::SetDefaultValue(const std::string& key,
diff --git a/base/prefs/default_pref_store.h b/base/prefs/default_pref_store.h
index 97b3960..23b9096 100644
--- a/base/prefs/default_pref_store.h
+++ b/base/prefs/default_pref_store.h
@@ -25,7 +25,7 @@
                         const base::Value** result) const OVERRIDE;
   virtual void AddObserver(PrefStore::Observer* observer) OVERRIDE;
   virtual void RemoveObserver(PrefStore::Observer* observer) OVERRIDE;
-  virtual size_t NumberOfObservers() const OVERRIDE;
+  virtual bool HasObservers() const OVERRIDE;
 
   // Sets a |value| for |key|. Should only be called if a value has not been
   // set yet; otherwise call ReplaceDefaultValue().
diff --git a/base/prefs/json_pref_store.cc b/base/prefs/json_pref_store.cc
index 0b93f74..e230407 100644
--- a/base/prefs/json_pref_store.cc
+++ b/base/prefs/json_pref_store.cc
@@ -179,8 +179,8 @@
   observers_.RemoveObserver(observer);
 }
 
-size_t JsonPrefStore::NumberOfObservers() const {
-  return observers_.size();
+bool JsonPrefStore::HasObservers() const {
+  return observers_.might_have_observers();
 }
 
 bool JsonPrefStore::IsInitializationComplete() const {
diff --git a/base/prefs/json_pref_store.h b/base/prefs/json_pref_store.h
index 738b917..9e6c182 100644
--- a/base/prefs/json_pref_store.h
+++ b/base/prefs/json_pref_store.h
@@ -48,7 +48,7 @@
                         const base::Value** result) const OVERRIDE;
   virtual void AddObserver(PrefStore::Observer* observer) OVERRIDE;
   virtual void RemoveObserver(PrefStore::Observer* observer) OVERRIDE;
-  virtual size_t NumberOfObservers() const OVERRIDE;
+  virtual bool HasObservers() const OVERRIDE;
   virtual bool IsInitializationComplete() const OVERRIDE;
 
   // PersistentPrefStore overrides:
diff --git a/base/prefs/overlay_user_pref_store.cc b/base/prefs/overlay_user_pref_store.cc
index 628d3b4..47668cc 100644
--- a/base/prefs/overlay_user_pref_store.cc
+++ b/base/prefs/overlay_user_pref_store.cc
@@ -25,8 +25,8 @@
   observers_.RemoveObserver(observer);
 }
 
-size_t OverlayUserPrefStore::NumberOfObservers() const {
-  return observers_.size();
+bool OverlayUserPrefStore::HasObservers() const {
+  return observers_.might_have_observers();
 }
 
 bool OverlayUserPrefStore::IsInitializationComplete() const {
diff --git a/base/prefs/overlay_user_pref_store.h b/base/prefs/overlay_user_pref_store.h
index 120d405..1895ac0 100644
--- a/base/prefs/overlay_user_pref_store.h
+++ b/base/prefs/overlay_user_pref_store.h
@@ -32,7 +32,7 @@
   // Methods of PrefStore.
   virtual void AddObserver(PrefStore::Observer* observer) OVERRIDE;
   virtual void RemoveObserver(PrefStore::Observer* observer) OVERRIDE;
-  virtual size_t NumberOfObservers() const OVERRIDE;
+  virtual bool HasObservers() const OVERRIDE;
   virtual bool IsInitializationComplete() const OVERRIDE;
   virtual bool GetValue(const std::string& key,
                         const base::Value** result) const OVERRIDE;
diff --git a/base/prefs/pref_member.cc b/base/prefs/pref_member.cc
index ca6e3be..eb70839 100644
--- a/base/prefs/pref_member.cc
+++ b/base/prefs/pref_member.cc
@@ -4,8 +4,8 @@
 
 #include "base/prefs/pref_member.h"
 
-#include "base/bind_helpers.h"
 #include "base/callback.h"
+#include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/prefs/pref_service.h"
 #include "base/value_conversions.h"
@@ -94,7 +94,8 @@
 
 PrefMemberBase::Internal::Internal()
     : thread_loop_(MessageLoopProxy::current()),
-      is_managed_(false) {
+      is_managed_(false),
+      is_user_modifiable_(false) {
 }
 PrefMemberBase::Internal::~Internal() { }
 
diff --git a/base/prefs/pref_service_builder.cc b/base/prefs/pref_service_builder.cc
index b3d3533..16b4565 100644
--- a/base/prefs/pref_service_builder.cc
+++ b/base/prefs/pref_service_builder.cc
@@ -32,6 +32,12 @@
   return *this;
 }
 
+PrefServiceBuilder& PrefServiceBuilder::WithSupervisedUserPrefs(
+    PrefStore* store) {
+  supervised_user_prefs_ = store;
+  return *this;
+}
+
 PrefServiceBuilder& PrefServiceBuilder::WithExtensionPrefs(PrefStore* store) {
   extension_prefs_ = store;
   return *this;
@@ -77,6 +83,7 @@
   PrefService* pref_service =
       new PrefService(pref_notifier,
                       new PrefValueStore(managed_prefs_.get(),
+                                         supervised_user_prefs_.get(),
                                          extension_prefs_.get(),
                                          command_line_prefs_.get(),
                                          user_prefs_.get(),
@@ -93,6 +100,7 @@
 
 void PrefServiceBuilder::ResetDefaultState() {
   managed_prefs_ = NULL;
+  supervised_user_prefs_ = NULL;
   extension_prefs_ = NULL;
   command_line_prefs_ = NULL;
   user_prefs_ = NULL;
diff --git a/base/prefs/pref_service_builder.h b/base/prefs/pref_service_builder.h
index 4bd4fde..7af4392 100644
--- a/base/prefs/pref_service_builder.h
+++ b/base/prefs/pref_service_builder.h
@@ -29,6 +29,7 @@
   // Functions for setting the various parameters of the PrefService to build.
   // These take ownership of the |store| parameter.
   PrefServiceBuilder& WithManagedPrefs(PrefStore* store);
+  PrefServiceBuilder& WithSupervisedUserPrefs(PrefStore* store);
   PrefServiceBuilder& WithExtensionPrefs(PrefStore* store);
   PrefServiceBuilder& WithCommandLinePrefs(PrefStore* store);
   PrefServiceBuilder& WithUserPrefs(PersistentPrefStore* store);
@@ -55,6 +56,7 @@
   virtual void ResetDefaultState();
 
   scoped_refptr<PrefStore> managed_prefs_;
+  scoped_refptr<PrefStore> supervised_user_prefs_;
   scoped_refptr<PrefStore> extension_prefs_;
   scoped_refptr<PrefStore> command_line_prefs_;
   scoped_refptr<PersistentPrefStore> user_prefs_;
diff --git a/base/prefs/pref_store.cc b/base/prefs/pref_store.cc
index 0521654..f286a33 100644
--- a/base/prefs/pref_store.cc
+++ b/base/prefs/pref_store.cc
@@ -4,8 +4,8 @@
 
 #include "base/prefs/pref_store.h"
 
-size_t PrefStore::NumberOfObservers() const {
-  return 0;
+bool PrefStore::HasObservers() const {
+  return false;
 }
 
 bool PrefStore::IsInitializationComplete() const {
diff --git a/base/prefs/pref_store.h b/base/prefs/pref_store.h
index 2239528..b736ac3 100644
--- a/base/prefs/pref_store.h
+++ b/base/prefs/pref_store.h
@@ -41,7 +41,7 @@
   // Add and remove observers.
   virtual void AddObserver(Observer* observer) {}
   virtual void RemoveObserver(Observer* observer) {}
-  virtual size_t NumberOfObservers() const;
+  virtual bool HasObservers() const;
 
   // Whether the store has completed all asynchronous initialization.
   virtual bool IsInitializationComplete() const;
diff --git a/base/prefs/pref_value_store.cc b/base/prefs/pref_value_store.cc
index 7d54f09..2c22f17 100644
--- a/base/prefs/pref_value_store.cc
+++ b/base/prefs/pref_value_store.cc
@@ -27,7 +27,7 @@
     PrefValueStore::PrefStoreType type) {
   if (pref_store_.get()) {
     pref_store_->RemoveObserver(this);
-    DCHECK_EQ(0U, pref_store_->NumberOfObservers());
+    DCHECK(!pref_store_->HasObservers());
   }
   type_ = type;
   pref_value_store_ = store;
@@ -47,6 +47,7 @@
 }
 
 PrefValueStore::PrefValueStore(PrefStore* managed_prefs,
+                               PrefStore* supervised_user_prefs,
                                PrefStore* extension_prefs,
                                PrefStore* command_line_prefs,
                                PrefStore* user_prefs,
@@ -56,6 +57,7 @@
     : pref_notifier_(pref_notifier),
       initialization_failed_(false) {
   InitPrefStore(MANAGED_STORE, managed_prefs);
+  InitPrefStore(SUPERVISED_USER_STORE, supervised_user_prefs);
   InitPrefStore(EXTENSION_STORE, extension_prefs);
   InitPrefStore(COMMAND_LINE_STORE, command_line_prefs);
   InitPrefStore(USER_STORE, user_prefs);
@@ -69,6 +71,7 @@
 
 PrefValueStore* PrefValueStore::CloneAndSpecialize(
     PrefStore* managed_prefs,
+    PrefStore* supervised_user_prefs,
     PrefStore* extension_prefs,
     PrefStore* command_line_prefs,
     PrefStore* user_prefs,
@@ -78,6 +81,8 @@
   DCHECK(pref_notifier);
   if (!managed_prefs)
     managed_prefs = GetPrefStore(MANAGED_STORE);
+  if (!supervised_user_prefs)
+    supervised_user_prefs = GetPrefStore(SUPERVISED_USER_STORE);
   if (!extension_prefs)
     extension_prefs = GetPrefStore(EXTENSION_STORE);
   if (!command_line_prefs)
@@ -90,8 +95,8 @@
     default_prefs = GetPrefStore(DEFAULT_STORE);
 
   return new PrefValueStore(
-      managed_prefs, extension_prefs, command_line_prefs, user_prefs,
-      recommended_prefs, default_prefs, pref_notifier);
+      managed_prefs, supervised_user_prefs, extension_prefs, command_line_prefs,
+      user_prefs, recommended_prefs, default_prefs, pref_notifier);
 }
 
 void PrefValueStore::set_callback(const PrefChangedCallback& callback) {
diff --git a/base/prefs/pref_value_store.h b/base/prefs/pref_value_store.h
index 4036e40..1c85ca7 100644
--- a/base/prefs/pref_value_store.h
+++ b/base/prefs/pref_value_store.h
@@ -33,6 +33,9 @@
 
   // In decreasing order of precedence:
   //   |managed_prefs| contains all preferences from mandatory policies.
+  //   |supervised_user_prefs| contains all preferences from supervised user
+  //        settings, i.e. settings configured for a supervised user by their
+  //        custodian.
   //   |extension_prefs| contains preference values set by extensions.
   //   |command_line_prefs| contains preference values set by command-line
   //        switches.
@@ -44,6 +47,7 @@
   // |pref_notifier| facilitates broadcasting preference change notifications
   // to the world.
   PrefValueStore(PrefStore* managed_prefs,
+                 PrefStore* supervised_user_prefs,
                  PrefStore* extension_prefs,
                  PrefStore* command_line_prefs,
                  PrefStore* user_prefs,
@@ -55,6 +59,7 @@
   // Creates a clone of this PrefValueStore with PrefStores overwritten
   // by the parameters passed, if unequal NULL.
   PrefValueStore* CloneAndSpecialize(PrefStore* managed_prefs,
+                                     PrefStore* supervised_user_prefs,
                                      PrefStore* extension_prefs,
                                      PrefStore* command_line_prefs,
                                      PrefStore* user_prefs,
@@ -116,6 +121,7 @@
   // PrefStores must be listed here in order from highest to lowest priority.
   //   MANAGED contains all managed preference values that are provided by
   //      mandatory policies (e.g. Windows Group Policy or cloud policy).
+  //   SUPERVISED_USER contains preferences that are valid for supervised users.
   //   EXTENSION contains preference values set by extensions.
   //   COMMAND_LINE contains preference values set by command-line switches.
   //   USER contains all user-set preference values.
@@ -127,6 +133,7 @@
     // an invalid marker, e.g. as a return value.
     INVALID_STORE = -1,
     MANAGED_STORE = 0,
+    SUPERVISED_USER_STORE,
     EXTENSION_STORE,
     COMMAND_LINE_STORE,
     USER_STORE,
diff --git a/base/prefs/pref_value_store_unittest.cc b/base/prefs/pref_value_store_unittest.cc
index b38c4ac..3afe9dc 100644
--- a/base/prefs/pref_value_store_unittest.cc
+++ b/base/prefs/pref_value_store_unittest.cc
@@ -37,6 +37,7 @@
 // Names of the preferences used in this test.
 namespace prefs {
 const char kManagedPref[] = "this.pref.managed";
+const char kSupervisedUserPref[] = "this.pref.supervised_user";
 const char kCommandLinePref[] = "this.pref.command_line";
 const char kExtensionPref[] = "this.pref.extension";
 const char kUserPref[] = "this.pref.user";
@@ -50,19 +51,27 @@
 const char kManagedValue[] = "managed:managed";
 }
 
+namespace supervised_user_pref {
+const char kManagedValue[] = "supervised_user:managed";
+const char kSupervisedUserValue[] = "supervised_user:supervised_user";
+}
+
 namespace extension_pref {
 const char kManagedValue[] = "extension:managed";
+const char kSupervisedUserValue[] = "extension:supervised_user";
 const char kExtensionValue[] = "extension:extension";
 }
 
 namespace command_line_pref {
 const char kManagedValue[] = "command_line:managed";
+const char kSupervisedUserValue[] = "command_line:supervised_user";
 const char kExtensionValue[] = "command_line:extension";
 const char kCommandLineValue[] = "command_line:command_line";
 }
 
 namespace user_pref {
 const char kManagedValue[] = "user:managed";
+const char kSupervisedUserValue[] = "supervised_user:supervised_user";
 const char kExtensionValue[] = "user:extension";
 const char kCommandLineValue[] = "user:command_line";
 const char kUserValue[] = "user:user";
@@ -70,6 +79,7 @@
 
 namespace recommended_pref {
 const char kManagedValue[] = "recommended:managed";
+const char kSupervisedUserValue[] = "recommended:supervised_user";
 const char kExtensionValue[] = "recommended:extension";
 const char kCommandLineValue[] = "recommended:command_line";
 const char kUserValue[] = "recommended:user";
@@ -78,6 +88,7 @@
 
 namespace default_pref {
 const char kManagedValue[] = "default:managed";
+const char kSupervisedUserValue[] = "default:supervised_user";
 const char kExtensionValue[] = "default:extension";
 const char kCommandLineValue[] = "default:command_line";
 const char kUserValue[] = "default:user";
@@ -90,6 +101,7 @@
   virtual void SetUp() {
     // Create TestingPrefStores.
     CreateManagedPrefs();
+    CreateSupervisedUserPrefs();
     CreateExtensionPrefs();
     CreateCommandLinePrefs();
     CreateUserPrefs();
@@ -98,13 +110,15 @@
     sync_associator_.reset(new MockPrefModelAssociator());
 
     // Create a fresh PrefValueStore.
-    pref_value_store_.reset(new PrefValueStore(managed_pref_store_.get(),
-                                               extension_pref_store_.get(),
-                                               command_line_pref_store_.get(),
-                                               user_pref_store_.get(),
-                                               recommended_pref_store_.get(),
-                                               default_pref_store_.get(),
-                                               &pref_notifier_));
+    pref_value_store_.reset(
+        new PrefValueStore(managed_pref_store_.get(),
+                           supervised_user_pref_store_.get(),
+                           extension_pref_store_.get(),
+                           command_line_pref_store_.get(),
+                           user_pref_store_.get(),
+                           recommended_pref_store_.get(),
+                           default_pref_store_.get(),
+                           &pref_notifier_));
 
     pref_value_store_->set_callback(
         base::Bind(&MockPrefModelAssociator::ProcessPrefChange,
@@ -118,12 +132,25 @@
         managed_pref::kManagedValue);
   }
 
+  void CreateSupervisedUserPrefs() {
+    supervised_user_pref_store_ = new TestingPrefStore;
+    supervised_user_pref_store_->SetString(
+        prefs::kManagedPref,
+        supervised_user_pref::kManagedValue);
+    supervised_user_pref_store_->SetString(
+        prefs::kSupervisedUserPref,
+        supervised_user_pref::kSupervisedUserValue);
+  }
+
   void CreateExtensionPrefs() {
     extension_pref_store_ = new TestingPrefStore;
     extension_pref_store_->SetString(
         prefs::kManagedPref,
         extension_pref::kManagedValue);
     extension_pref_store_->SetString(
+        prefs::kSupervisedUserPref,
+        extension_pref::kSupervisedUserValue);
+    extension_pref_store_->SetString(
         prefs::kExtensionPref,
         extension_pref::kExtensionValue);
   }
@@ -134,6 +161,9 @@
         prefs::kManagedPref,
         command_line_pref::kManagedValue);
     command_line_pref_store_->SetString(
+        prefs::kSupervisedUserPref,
+        command_line_pref::kSupervisedUserValue);
+    command_line_pref_store_->SetString(
         prefs::kExtensionPref,
         command_line_pref::kExtensionValue);
     command_line_pref_store_->SetString(
@@ -147,6 +177,9 @@
         prefs::kManagedPref,
         user_pref::kManagedValue);
     user_pref_store_->SetString(
+        prefs::kSupervisedUserPref,
+        user_pref::kSupervisedUserValue);
+    user_pref_store_->SetString(
         prefs::kCommandLinePref,
         user_pref::kCommandLineValue);
     user_pref_store_->SetString(
@@ -163,6 +196,9 @@
         prefs::kManagedPref,
         recommended_pref::kManagedValue);
     recommended_pref_store_->SetString(
+        prefs::kSupervisedUserPref,
+        recommended_pref::kSupervisedUserValue);
+    recommended_pref_store_->SetString(
         prefs::kCommandLinePref,
         recommended_pref::kCommandLineValue);
     recommended_pref_store_->SetString(
@@ -179,6 +215,9 @@
   void CreateDefaultPrefs() {
     default_pref_store_ = new TestingPrefStore;
     default_pref_store_->SetString(
+        prefs::kSupervisedUserPref,
+        default_pref::kSupervisedUserValue);
+    default_pref_store_->SetString(
         prefs::kManagedPref,
         default_pref::kManagedValue);
     default_pref_store_->SetString(
@@ -213,6 +252,7 @@
   scoped_ptr<PrefValueStore> pref_value_store_;
 
   scoped_refptr<TestingPrefStore> managed_pref_store_;
+  scoped_refptr<TestingPrefStore> supervised_user_pref_store_;
   scoped_refptr<TestingPrefStore> extension_pref_store_;
   scoped_refptr<TestingPrefStore> command_line_pref_store_;
   scoped_refptr<TestingPrefStore> user_pref_store_;
@@ -235,6 +275,13 @@
   EXPECT_TRUE(value->GetAsString(&actual_str_value));
   EXPECT_EQ(managed_pref::kManagedValue, actual_str_value);
 
+  // Test getting a supervised user value.
+  value = NULL;
+  ASSERT_TRUE(pref_value_store_->GetValue(prefs::kSupervisedUserPref,
+                                          base::Value::TYPE_STRING, &value));
+  EXPECT_TRUE(value->GetAsString(&actual_str_value));
+  EXPECT_EQ(supervised_user_pref::kSupervisedUserValue, actual_str_value);
+
   // Test getting an extension value.
   value = NULL;
   ASSERT_TRUE(pref_value_store_->GetValue(prefs::kExtensionPref,
@@ -296,6 +343,14 @@
   EXPECT_TRUE(value->GetAsString(&actual_str_value));
   EXPECT_EQ(recommended_pref::kManagedValue, actual_str_value);
 
+  // Test getting recommended value when a supervised user value is present.
+  value = NULL;
+  ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
+      prefs::kSupervisedUserPref,
+      base::Value::TYPE_STRING, &value));
+  EXPECT_TRUE(value->GetAsString(&actual_str_value));
+  EXPECT_EQ(recommended_pref::kSupervisedUserValue, actual_str_value);
+
   // Test getting recommended value when an extension value is present.
   value = NULL;
   ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
@@ -353,6 +408,10 @@
   CheckAndClearValueChangeNotifications();
 
   ExpectValueChangeNotifications(prefs::kManagedPref);
+  supervised_user_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref);
+  CheckAndClearValueChangeNotifications();
+
+  ExpectValueChangeNotifications(prefs::kManagedPref);
   extension_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref);
   CheckAndClearValueChangeNotifications();
 
@@ -426,6 +485,7 @@
 TEST_F(PrefValueStoreTest, OnInitializationCompleted) {
   EXPECT_CALL(pref_notifier_, OnInitializationCompleted(true)).Times(0);
   managed_pref_store_->SetInitializationCompleted();
+  supervised_user_pref_store_->SetInitializationCompleted();
   extension_pref_store_->SetInitializationCompleted();
   command_line_pref_store_->SetInitializationCompleted();
   recommended_pref_store_->SetInitializationCompleted();
@@ -442,6 +502,8 @@
   EXPECT_TRUE(pref_value_store_->PrefValueInManagedStore(
       prefs::kManagedPref));
   EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
+      prefs::kSupervisedUserPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
       prefs::kExtensionPref));
   EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
       prefs::kCommandLinePref));
@@ -459,6 +521,8 @@
   EXPECT_TRUE(pref_value_store_->PrefValueInExtensionStore(
       prefs::kManagedPref));
   EXPECT_TRUE(pref_value_store_->PrefValueInExtensionStore(
+      prefs::kSupervisedUserPref));
+  EXPECT_TRUE(pref_value_store_->PrefValueInExtensionStore(
       prefs::kExtensionPref));
   EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore(
       prefs::kCommandLinePref));
@@ -476,6 +540,8 @@
   EXPECT_TRUE(pref_value_store_->PrefValueInUserStore(
       prefs::kManagedPref));
   EXPECT_TRUE(pref_value_store_->PrefValueInUserStore(
+      prefs::kSupervisedUserPref));
+  EXPECT_TRUE(pref_value_store_->PrefValueInUserStore(
       prefs::kExtensionPref));
   EXPECT_TRUE(pref_value_store_->PrefValueInUserStore(
       prefs::kCommandLinePref));
@@ -492,6 +558,8 @@
 TEST_F(PrefValueStoreTest, PrefValueFromExtensionStore) {
   EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
       prefs::kManagedPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
+      prefs::kSupervisedUserPref));
   EXPECT_TRUE(pref_value_store_->PrefValueFromExtensionStore(
       prefs::kExtensionPref));
   EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
@@ -510,6 +578,8 @@
   EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
       prefs::kManagedPref));
   EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
+      prefs::kSupervisedUserPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
       prefs::kExtensionPref));
   EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
       prefs::kCommandLinePref));
@@ -527,6 +597,8 @@
   EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore(
       prefs::kManagedPref));
   EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore(
+      prefs::kSupervisedUserPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore(
       prefs::kExtensionPref));
   EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore(
       prefs::kCommandLinePref));
@@ -544,6 +616,8 @@
   EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore(
       prefs::kManagedPref));
   EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore(
+      prefs::kSupervisedUserPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore(
       prefs::kExtensionPref));
   EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore(
       prefs::kCommandLinePref));
@@ -561,6 +635,8 @@
   EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable(
       prefs::kManagedPref));
   EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable(
+      prefs::kSupervisedUserPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable(
       prefs::kExtensionPref));
   EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable(
       prefs::kCommandLinePref));
@@ -577,6 +653,8 @@
 TEST_F(PrefValueStoreTest, PrefValueExtensionModifiable) {
   EXPECT_FALSE(pref_value_store_->PrefValueExtensionModifiable(
       prefs::kManagedPref));
+  EXPECT_FALSE(pref_value_store_->PrefValueExtensionModifiable(
+      prefs::kSupervisedUserPref));
   EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable(
       prefs::kExtensionPref));
   EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable(
diff --git a/base/prefs/testing_pref_service.cc b/base/prefs/testing_pref_service.cc
index b96268a..5899376 100644
--- a/base/prefs/testing_pref_service.cc
+++ b/base/prefs/testing_pref_service.cc
@@ -24,6 +24,7 @@
           new PrefValueStore(managed_prefs,
                              NULL,
                              NULL,
+                             NULL,
                              user_prefs,
                              recommended_prefs,
                              pref_registry->defaults().get(),
@@ -39,12 +40,11 @@
 
 TestingPrefServiceSimple::TestingPrefServiceSimple()
     : TestingPrefServiceBase<PrefService, PrefRegistry>(
-        new TestingPrefStore(),
-        new TestingPrefStore(),
-        new TestingPrefStore(),
-        new PrefRegistrySimple(),
-        new PrefNotifierImpl()) {
-}
+          new TestingPrefStore(),
+          new TestingPrefStore(),
+          new TestingPrefStore(),
+          new PrefRegistrySimple(),
+          new PrefNotifierImpl()) {}
 
 TestingPrefServiceSimple::~TestingPrefServiceSimple() {
 }
diff --git a/base/prefs/testing_pref_store.cc b/base/prefs/testing_pref_store.cc
index fcb48ce..b420969 100644
--- a/base/prefs/testing_pref_store.cc
+++ b/base/prefs/testing_pref_store.cc
@@ -30,8 +30,8 @@
   observers_.RemoveObserver(observer);
 }
 
-size_t TestingPrefStore::NumberOfObservers() const {
-  return observers_.size();
+bool TestingPrefStore::HasObservers() const {
+  return observers_.might_have_observers();
 }
 
 bool TestingPrefStore::IsInitializationComplete() const {
diff --git a/base/prefs/testing_pref_store.h b/base/prefs/testing_pref_store.h
index a9f1e92..08d7125 100644
--- a/base/prefs/testing_pref_store.h
+++ b/base/prefs/testing_pref_store.h
@@ -25,7 +25,7 @@
                         const base::Value** result) const OVERRIDE;
   virtual void AddObserver(PrefStore::Observer* observer) OVERRIDE;
   virtual void RemoveObserver(PrefStore::Observer* observer) OVERRIDE;
-  virtual size_t NumberOfObservers() const OVERRIDE;
+  virtual bool HasObservers() const OVERRIDE;
   virtual bool IsInitializationComplete() const OVERRIDE;
 
   // PersistentPrefStore overrides:
diff --git a/base/prefs/value_map_pref_store.cc b/base/prefs/value_map_pref_store.cc
index b4b5751..750688c 100644
--- a/base/prefs/value_map_pref_store.cc
+++ b/base/prefs/value_map_pref_store.cc
@@ -24,8 +24,8 @@
   observers_.RemoveObserver(observer);
 }
 
-size_t ValueMapPrefStore::NumberOfObservers() const {
-  return observers_.size();
+bool ValueMapPrefStore::HasObservers() const {
+  return observers_.might_have_observers();
 }
 
 ValueMapPrefStore::~ValueMapPrefStore() {}
diff --git a/base/prefs/value_map_pref_store.h b/base/prefs/value_map_pref_store.h
index c9c9b1c..21e4b88 100644
--- a/base/prefs/value_map_pref_store.h
+++ b/base/prefs/value_map_pref_store.h
@@ -25,7 +25,7 @@
                         const base::Value** value) const OVERRIDE;
   virtual void AddObserver(PrefStore::Observer* observer) OVERRIDE;
   virtual void RemoveObserver(PrefStore::Observer* observer) OVERRIDE;
-  virtual size_t NumberOfObservers() const OVERRIDE;
+  virtual bool HasObservers() const OVERRIDE;
 
  protected:
   virtual ~ValueMapPrefStore();
diff --git a/base/process/process_metrics.h b/base/process/process_metrics.h
index 8a7fb13..273c8e0 100644
--- a/base/process/process_metrics.h
+++ b/base/process/process_metrics.h
@@ -263,6 +263,29 @@
 BASE_EXPORT bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo);
 #endif  // defined(OS_LINUX) || defined(OS_ANDROID)
 
+#if defined(OS_CHROMEOS)
+// Data from files in directory /sys/block/zram0 about ZRAM usage.
+struct BASE_EXPORT SwapInfo {
+  SwapInfo()
+      : num_reads(0),
+        num_writes(0),
+        compr_data_size(0),
+        orig_data_size(0),
+        mem_used_total(0) {
+  }
+
+  uint64 num_reads;
+  uint64 num_writes;
+  uint64 compr_data_size;
+  uint64 orig_data_size;
+  uint64 mem_used_total;
+};
+
+// In ChromeOS, reads files from /sys/block/zram0 that contain ZRAM usage data.
+// Fills in the provided |swap_data| structure.
+BASE_EXPORT void GetSwapInfo(SwapInfo* swap_info);
+#endif  // defined(OS_CHROMEOS)
+
 // Collects and holds performance metrics for system memory and disk.
 // Provides functionality to retrieve the data on various platforms and
 // to serialize the stored data.
diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc
index 8d1a57c..c500459 100644
--- a/base/process/process_metrics_linux.cc
+++ b/base/process/process_metrics_linux.cc
@@ -30,6 +30,20 @@
   KEY_VALUE
 };
 
+#ifdef OS_CHROMEOS
+// Read a file with a single number string and return the number as a uint64.
+static uint64 ReadFileToUint64(const base::FilePath file) {
+  std::string file_as_string;
+  if (!file_util::ReadFileToString(file, &file_as_string))
+    return 0;
+  TrimWhitespaceASCII(file_as_string, TRIM_ALL, &file_as_string);
+  uint64 file_as_uint64 = 0;
+  if (!base::StringToUint64(file_as_string, &file_as_uint64))
+    return 0;
+  return file_as_uint64;
+}
+#endif
+
 // Read /proc/<pid>/status and returns the value for |field|, or 0 on failure.
 // Only works for fields in the form of "Field: value kB".
 size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) {
@@ -513,4 +527,31 @@
   return true;
 }
 
+#if defined(OS_CHROMEOS)
+void GetSwapInfo(SwapInfo* swap_info) {
+  // Synchronously reading files in /sys/block/zram0 is safe.
+  ThreadRestrictions::ScopedAllowIO allow_io;
+
+  base::FilePath zram_path("/sys/block/zram0");
+  uint64 orig_data_size = ReadFileToUint64(zram_path.Append("orig_data_size"));
+  if (orig_data_size <= 4096) {
+    // A single page is compressed at startup, and has a high compression
+    // ratio. We ignore this as it doesn't indicate any real swapping.
+    swap_info->orig_data_size = 0;
+    swap_info->num_reads = 0;
+    swap_info->num_writes = 0;
+    swap_info->compr_data_size = 0;
+    swap_info->mem_used_total = 0;
+    return;
+  }
+  swap_info->orig_data_size = orig_data_size;
+  swap_info->num_reads = ReadFileToUint64(zram_path.Append("num_reads"));
+  swap_info->num_writes = ReadFileToUint64(zram_path.Append("num_writes"));
+  swap_info->compr_data_size =
+      ReadFileToUint64(zram_path.Append("compr_data_size"));
+  swap_info->mem_used_total =
+      ReadFileToUint64(zram_path.Append("mem_used_total"));
+}
+#endif  // defined(OS_CHROMEOS)
+
 }  // namespace base
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/UrlUtils.java b/base/test/android/javatests/src/org/chromium/base/test/util/UrlUtils.java
index 767c872..8ef0ac5 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/util/UrlUtils.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/UrlUtils.java
@@ -15,11 +15,19 @@
     private final static String DATA_DIR = "/chrome/test/data/";
 
     /**
+     * Construct the full path of a test data file.
+     * @param path Pathname relative to external/chrome/testing/data
+     */
+    public static String getTestFilePath(String path) {
+        return PathUtils.getExternalStorageDirectory() + DATA_DIR + path;
+    }
+
+    /**
      * Construct a suitable URL for loading a test data file.
      * @param path Pathname relative to external/chrome/testing/data
      */
     public static String getTestFileUrl(String path) {
-        return "file://" + PathUtils.getExternalStorageDirectory() + DATA_DIR + path;
+        return "file://" + getTestFilePath(path);
     }
 
     /**
diff --git a/base/test/gtest_xml_util.cc b/base/test/gtest_xml_util.cc
index 6187d9f..524369a 100644
--- a/base/test/gtest_xml_util.cc
+++ b/base/test/gtest_xml_util.cc
@@ -6,19 +6,100 @@
 
 #include "base/file_util.h"
 #include "base/logging.h"
+#include "base/strings/stringprintf.h"
 #include "base/test/test_launcher.h"
 #include "third_party/libxml/chromium/libxml_utils.h"
 
 namespace base {
 
+namespace {
+
+// This is used for the xml parser to report errors. This assumes the context
+// is a pointer to a std::string where the error message should be appended.
+static void XmlErrorFunc(void *context, const char *message, ...) {
+  va_list args;
+  va_start(args, message);
+  std::string* error = static_cast<std::string*>(context);
+  base::StringAppendV(error, message, args);
+  va_end(args);
+}
+
+}  // namespace
+
+XmlUnitTestResultPrinter::XmlUnitTestResultPrinter() : output_file_(NULL) {
+}
+
+XmlUnitTestResultPrinter::~XmlUnitTestResultPrinter() {
+  if (output_file_) {
+    fprintf(output_file_, "</testsuites>\n");
+    fflush(output_file_);
+    file_util::CloseFile(output_file_);
+  }
+}
+
+bool XmlUnitTestResultPrinter::Initialize(const FilePath& output_file_path) {
+  DCHECK(!output_file_);
+  output_file_ = file_util::OpenFile(output_file_path, "w");
+  if (!output_file_)
+    return false;
+
+  fprintf(output_file_,
+          "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<testsuites>\n");
+  fflush(output_file_);
+
+  return true;
+}
+
+void XmlUnitTestResultPrinter::OnTestCaseStart(
+    const testing::TestCase& test_case) {
+  fprintf(output_file_, "  <testsuite>\n");
+  fflush(output_file_);
+}
+
+void XmlUnitTestResultPrinter::OnTestStart(const testing::TestInfo& test_info) {
+  // This is our custom extension - it helps to recognize which test was running
+  // when the test binary crashed. Note that we cannot even open the <testcase>
+  // tag here - it requires e.g. run time of the test to be known.
+  fprintf(output_file_,
+          "    <x-teststart name=\"%s\" classname=\"%s\" />\n",
+          test_info.name(),
+          test_info.test_case_name());
+  fflush(output_file_);
+}
+
+void XmlUnitTestResultPrinter::OnTestEnd(const testing::TestInfo& test_info) {
+  fprintf(output_file_,
+          "    <testcase name=\"%s\" status=\"run\" time=\"%.3f\""
+          " classname=\"%s\">\n",
+          test_info.name(),
+          static_cast<double>(test_info.result()->elapsed_time()) /
+              Time::kMillisecondsPerSecond,
+          test_info.test_case_name());
+  if (test_info.result()->Failed())
+    fprintf(output_file_, "      <failure message=\"\" type=\"\"></failure>\n");
+  fprintf(output_file_, "    </testcase>\n");
+  fflush(output_file_);
+}
+
+void XmlUnitTestResultPrinter::OnTestCaseEnd(
+    const testing::TestCase& test_case) {
+  fprintf(output_file_, "  </testsuite>\n");
+  fflush(output_file_);
+}
+
 bool ProcessGTestOutput(const base::FilePath& output_file,
-                        std::vector<TestResult>* results) {
+                        std::vector<TestResult>* results,
+                        bool* crashed) {
   DCHECK(results);
 
   std::string xml_contents;
   if (!file_util::ReadFileToString(output_file, &xml_contents))
     return false;
 
+  // Silence XML errors - otherwise they go to stderr.
+  std::string xml_errors;
+  ScopedXmlErrorFunc error_func(&xml_errors, &XmlErrorFunc);
+
   XmlReader xml_reader;
   if (!xml_reader.Load(xml_contents))
     return false;
@@ -53,6 +134,23 @@
       case STATE_TESTCASE:
         if (node_name == "testsuite" && xml_reader.IsClosingElement()) {
           state = STATE_TESTSUITE;
+        } else if (node_name == "x-teststart" &&
+                   !xml_reader.IsClosingElement()) {
+          // This is our custom extension that helps recognize which test was
+          // running when the test binary crashed.
+          TestResult result;
+          if (!xml_reader.NodeAttribute("classname", &result.test_case_name))
+            return false;
+          if (!xml_reader.NodeAttribute("name", &result.test_name))
+            return false;
+
+          result.elapsed_time = TimeDelta();
+
+          // Assume the test crashed - we can correct that later.
+          result.success = false;
+          result.crashed = true;
+
+          results->push_back(result);
         } else if (node_name == "testcase" && !xml_reader.IsClosingElement()) {
           std::string test_status;
           if (!xml_reader.NodeAttribute("status", &test_status))
@@ -77,6 +175,16 @@
                                           Time::kMicrosecondsPerSecond);
 
           result.success = true;
+          result.crashed = false;
+
+          if (!results->empty() &&
+              results->at(results->size() - 1).GetFullName() ==
+                  result.GetFullName() &&
+              results->at(results->size() - 1).crashed) {
+            // Erase the fail-safe "crashed" result - now we know the test did
+            // not crash.
+            results->pop_back();
+          }
 
           results->push_back(result);
         } else if (node_name == "failure" && !xml_reader.IsClosingElement()) {
@@ -107,7 +215,8 @@
     }
   }
 
+  *crashed = (state != STATE_END);
   return true;
 }
 
-}  // namespace base
\ No newline at end of file
+}  // namespace base
diff --git a/base/test/gtest_xml_util.h b/base/test/gtest_xml_util.h
index 0aecfb1..79527e5 100644
--- a/base/test/gtest_xml_util.h
+++ b/base/test/gtest_xml_util.h
@@ -7,18 +7,45 @@
 
 #include <vector>
 
+#include "base/basictypes.h"
 #include "base/compiler_specific.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
 
 class FilePath;
 struct TestResult;
 
+// Generates an XML output file. Format is very close to GTest, but has
+// extensions needed by the test launcher.
+class XmlUnitTestResultPrinter : public testing::EmptyTestEventListener {
+ public:
+  XmlUnitTestResultPrinter();
+  virtual ~XmlUnitTestResultPrinter();
+
+  // Must be called before adding as a listener. Returns true on success.
+  bool Initialize(const FilePath& output_file_path) WARN_UNUSED_RESULT;
+
+ private:
+  // testing::EmptyTestEventListener:
+  virtual void OnTestCaseStart(const testing::TestCase& test_case) OVERRIDE;
+  virtual void OnTestStart(const testing::TestInfo& test_info) OVERRIDE;
+  virtual void OnTestEnd(const testing::TestInfo& test_info) OVERRIDE;
+  virtual void OnTestCaseEnd(const testing::TestCase& test_case) OVERRIDE;
+
+  FILE* output_file_;
+
+  DISALLOW_COPY_AND_ASSIGN(XmlUnitTestResultPrinter);
+};
+
 // Produces a vector of test results based on GTest output file.
 // Returns true iff the output file exists and has been successfully parsed.
+// On successful return |crashed| is set to true if the test results
+// are valid but incomplete.
 bool ProcessGTestOutput(const base::FilePath& output_file,
-                        std::vector<TestResult>* results) WARN_UNUSED_RESULT;
+                        std::vector<TestResult>* results,
+                        bool* crashed) WARN_UNUSED_RESULT;
 
 }  // namespace base
 
-#endif  // BASE_TEST_GTEST_XML_UTIL_H_
\ No newline at end of file
+#endif  // BASE_TEST_GTEST_XML_UTIL_H_
diff --git a/base/test/perf_test_suite.cc b/base/test/perf_test_suite.cc
index 8cfbb73..86be119 100644
--- a/base/test/perf_test_suite.cc
+++ b/base/test/perf_test_suite.cc
@@ -8,9 +8,9 @@
 #include "base/debug/debugger.h"
 #include "base/files/file_path.h"
 #include "base/path_service.h"
-#include "base/perftimer.h"
 #include "base/process/launch.h"
 #include "base/strings/string_util.h"
+#include "base/test/perftimer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
diff --git a/base/test/perftimer.cc b/base/test/perftimer.cc
new file mode 100644
index 0000000..6815f0b
--- /dev/null
+++ b/base/test/perftimer.cc
@@ -0,0 +1,45 @@
+// 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 "base/test/perftimer.h"
+
+#include <stdio.h>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+
+static FILE* perf_log_file = NULL;
+
+bool InitPerfLog(const base::FilePath& log_file) {
+  if (perf_log_file) {
+    // trying to initialize twice
+    NOTREACHED();
+    return false;
+  }
+
+  perf_log_file = file_util::OpenFile(log_file, "w");
+  return perf_log_file != NULL;
+}
+
+void FinalizePerfLog() {
+  if (!perf_log_file) {
+    // trying to cleanup without initializing
+    NOTREACHED();
+    return;
+  }
+  file_util::CloseFile(perf_log_file);
+}
+
+void LogPerfResult(const char* test_name, double value, const char* units) {
+  if (!perf_log_file) {
+    NOTREACHED();
+    return;
+  }
+
+  fprintf(perf_log_file, "%s\t%g\t%s\n", test_name, value, units);
+  printf("%s\t%g\t%s\n", test_name, value, units);
+}
diff --git a/base/test/perftimer.h b/base/test/perftimer.h
new file mode 100644
index 0000000..1a26cf5
--- /dev/null
+++ b/base/test/perftimer.h
@@ -0,0 +1,85 @@
+// 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 BASE_TEST_PERFTIMER_H_
+#define BASE_TEST_PERFTIMER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/time/time.h"
+
+namespace base {
+class FilePath;
+}
+
+// ----------------------------------------------------------------------
+// Initializes and finalizes the perf log. These functions should be
+// called at the beginning and end (respectively) of running all the
+// performance tests. The init function returns true on success.
+// ----------------------------------------------------------------------
+bool InitPerfLog(const base::FilePath& log_path);
+void FinalizePerfLog();
+
+// ----------------------------------------------------------------------
+// LogPerfResult
+//   Writes to the perf result log the given 'value' resulting from the
+//   named 'test'. The units are to aid in reading the log by people.
+// ----------------------------------------------------------------------
+void LogPerfResult(const char* test_name, double value, const char* units);
+
+// ----------------------------------------------------------------------
+// PerfTimer
+//   A simple wrapper around Now()
+// ----------------------------------------------------------------------
+class PerfTimer {
+ public:
+  PerfTimer() {
+    begin_ = base::TimeTicks::Now();
+  }
+
+  // Returns the time elapsed since object construction
+  base::TimeDelta Elapsed() const {
+    return base::TimeTicks::Now() - begin_;
+  }
+
+ private:
+  base::TimeTicks begin_;
+};
+
+// ----------------------------------------------------------------------
+// PerfTimeLogger
+//   Automates calling LogPerfResult for the common case where you want
+//   to measure the time that something took. Call Done() when the test
+//   is complete if you do extra work after the test or there are stack
+//   objects with potentially expensive constructors. Otherwise, this
+//   class with automatically log on destruction.
+// ----------------------------------------------------------------------
+class PerfTimeLogger {
+ public:
+  explicit PerfTimeLogger(const char* test_name)
+      : logged_(false),
+        test_name_(test_name) {
+  }
+
+  ~PerfTimeLogger() {
+    if (!logged_)
+      Done();
+  }
+
+  void Done() {
+    // we use a floating-point millisecond value because it is more
+    // intuitive than microseconds and we want more precision than
+    // integer milliseconds
+    LogPerfResult(test_name_.c_str(), timer_.Elapsed().InMillisecondsF(), "ms");
+    logged_ = true;
+  }
+
+ private:
+  bool logged_;
+  std::string test_name_;
+  PerfTimer timer_;
+};
+
+#endif  // BASE_TEST_PERFTIMER_H_
diff --git a/base/test/test_launcher.cc b/base/test/test_launcher.cc
index a70570c..84b906f 100644
--- a/base/test/test_launcher.cc
+++ b/base/test/test_launcher.cc
@@ -13,6 +13,7 @@
 #include "base/format_macros.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
 #include "base/process/kill.h"
 #include "base/process/launch.h"
 #include "base/strings/string_number_conversions.h"
@@ -255,9 +256,44 @@
   }
 }
 
-bool RunTests(TestLauncherDelegate* launcher_delegate,
+typedef Callback<void(bool)> RunTestsCallback;
+
+void RunRemainingTestsDone(scoped_ptr<ResultsPrinter> printer,
+                           size_t num_started_tests,
+                           const RunTestsCallback& callback) {
+  bool result = true;
+
+  printf("%" PRIuS " test%s run\n",
+         printer->test_run_count(),
+         printer->test_run_count() > 1 ? "s" : "");
+  printf("%" PRIuS " test%s failed\n",
+         printer->failed_tests().size(),
+         printer->failed_tests().size() != 1 ? "s" : "");
+  if (num_started_tests != printer->test_run_count()) {
+    result = false;
+    // TODO(phajdan.jr): Print more detailed info which test results
+    // are missing or superfluous.
+    printf("BUG: %" PRIuS " tests started but only got results for %" PRIuS
+           " tests back.\n", num_started_tests, printer->test_run_count());
+  }
+
+  if (!printer->failed_tests().empty()) {
+    result = false;
+
+    printf("Failing tests:\n");
+    for (size_t i = 0; i < printer->failed_tests().size(); ++i)
+      printf("%s\n", printer->failed_tests()[i].c_str());
+  }
+
+  fflush(stdout);
+
+  callback.Run(result);
+}
+
+void RunTests(TestLauncherDelegate* launcher_delegate,
               int total_shards,
-              int shard_index) {
+              int shard_index,
+              const RunTestsCallback& callback) {
   const CommandLine* command_line = CommandLine::ForCurrentProcess();
 
   DCHECK(!command_line->HasSwitch(kGTestListTestsFlag));
@@ -277,8 +313,9 @@
   }
 
   int num_runnable_tests = 0;
+  size_t num_started_tests = 0;
 
-  ResultsPrinter printer(*command_line);
+  scoped_ptr<ResultsPrinter> printer(new ResultsPrinter(*command_line));
   for (int i = 0; i < unit_test->total_test_case_count(); ++i) {
     const testing::TestCase* test_case = unit_test->GetTestCase(i);
     for (int j = 0; j < test_case->total_test_count(); ++j) {
@@ -305,36 +342,56 @@
 
       bool should_run = ShouldRunTestOnShard(total_shards, shard_index,
                                              num_runnable_tests);
-      num_runnable_tests += 1;
+      num_runnable_tests++;
       if (!should_run)
         continue;
 
+      num_started_tests++;
       launcher_delegate->RunTest(test_case,
                                  test_info,
                                  base::Bind(
                                      &ResultsPrinter::AddTestResult,
-                                     base::Unretained(&printer)));
+                                     base::Unretained(printer.get())));
     }
   }
 
-  launcher_delegate->RunRemainingTests();
-
-  printf("%" PRIuS " test%s run\n",
-         printer.test_run_count(),
-         printer.test_run_count() > 1 ? "s" : "");
-  printf("%" PRIuS " test%s failed\n",
-         printer.failed_tests().size(),
-         printer.failed_tests().size() != 1 ? "s" : "");
-  if (printer.failed_tests().empty())
-    return true;
-
-  printf("Failing tests:\n");
-  for (size_t i = 0; i < printer.failed_tests().size(); ++i)
-    printf("%s\n", printer.failed_tests()[i].c_str());
-
-  return false;
+  launcher_delegate->RunRemainingTests(
+      Bind(&RunRemainingTestsDone,
+           Passed(&printer), num_started_tests, callback));
 }
 
+void RunTestIteration(TestLauncherDelegate* launcher_delegate,
+                      int32 total_shards,
+                      int32 shard_index,
+                      int cycles,
+                      int* exit_code,
+                      bool run_tests_success) {
+  if (!run_tests_success) {
+    *exit_code = 1;
+    MessageLoop::current()->Quit();
+    return;
+  }
+
+  if (cycles == 0) {
+    MessageLoop::current()->Quit();
+    return;
+  }
+
+  // Special value "-1" means "repeat indefinitely".
+  int new_cycles = (cycles == -1) ? cycles : cycles - 1;
+
+  MessageLoop::current()->PostTask(
+      FROM_HERE,
+      Bind(&RunTests, launcher_delegate, total_shards, shard_index,
+           Bind(&RunTestIteration,
+                launcher_delegate,
+                total_shards,
+                shard_index,
+                new_cycles,
+                exit_code)));
+}
+
+
 }  // namespace
 
 const char kGTestFilterFlag[] = "gtest_filter";
@@ -345,7 +402,7 @@
 
 const char kHelpFlag[]   = "help";
 
-TestResult::TestResult() {
+TestResult::TestResult() : success(false), crashed(false) {
 }
 
 TestLauncherDelegate::~TestLauncherDelegate() {
@@ -421,6 +478,7 @@
                 char** argv) {
   const CommandLine* command_line = CommandLine::ForCurrentProcess();
 
+
   int32 total_shards;
   int32 shard_index;
   InitSharding(&total_shards, &shard_index);
@@ -430,16 +488,18 @@
     StringToInt(command_line->GetSwitchValueASCII(kGTestRepeatFlag), &cycles);
 
   int exit_code = 0;
-  while (cycles != 0) {
-    if (!RunTests(launcher_delegate, total_shards, shard_index)) {
-      exit_code = 1;
-      break;
-    }
+  MessageLoop message_loop;
+  message_loop.PostTask(
+      FROM_HERE,
+      Bind(&RunTestIteration,
+           launcher_delegate,
+           total_shards,
+           shard_index,
+           cycles,
+           &exit_code,
+           true));
 
-    // Special value "-1" means "repeat indefinitely".
-    if (cycles != -1)
-      cycles--;
-  }
+  message_loop.Run();
 
   return exit_code;
 }
diff --git a/base/test/test_launcher.h b/base/test/test_launcher.h
index 264454f..7829137 100644
--- a/base/test/test_launcher.h
+++ b/base/test/test_launcher.h
@@ -43,6 +43,10 @@
   // True if the test passed.
   bool success;
 
+  // True if the test binary crashed while executing this test (i.e. the test
+  // didn't actually finish).
+  bool crashed;
+
   // Time it took to run the test.
   base::TimeDelta elapsed_time;
 };
@@ -68,7 +72,8 @@
   // If the delegate is running tests asynchronously, it must finish
   // running all pending tests and call their callbacks before returning
   // from this method.
-  virtual void RunRemainingTests() = 0;
+  typedef base::Closure RunRemainingTestsCallback;
+  virtual void RunRemainingTests(const RunRemainingTestsCallback& callback) = 0;
 
  protected:
   virtual ~TestLauncherDelegate();
diff --git a/base/test/test_suite.cc b/base/test/test_suite.cc
index e394381..ef3ee99 100644
--- a/base/test/test_suite.cc
+++ b/base/test/test_suite.cc
@@ -11,13 +11,16 @@
 #include "base/debug/debug_on_start_win.h"
 #include "base/debug/debugger.h"
 #include "base/debug/stack_trace.h"
+#include "base/file_util.h"
 #include "base/files/file_path.h"
 #include "base/i18n/icu_util.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/path_service.h"
 #include "base/process/memory.h"
+#include "base/test/gtest_xml_util.h"
 #include "base/test/multiprocess_test.h"
+#include "base/test/test_switches.h"
 #include "base/test/test_timeouts.h"
 #include "base/time/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -142,6 +145,34 @@
   listeners.Append(new TestClientInitializer);
 }
 
+#if !defined(OS_IOS)
+void TestSuite::AddTestLauncherResultPrinter() {
+  // Only add the custom printer if requested.
+  if (!CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kTestLauncherOutput)) {
+    return;
+  }
+
+  FilePath output_path(CommandLine::ForCurrentProcess()->GetSwitchValuePath(
+                           switches::kTestLauncherOutput));
+
+  // Do not add the result printer if output path already exists. It's an
+  // indicator there is a process printing to that file, and we're likely
+  // its child. Do not clobber the results in that case.
+  if (PathExists(output_path)) {
+    LOG(WARNING) << "Test launcher output path " << output_path.AsUTF8Unsafe()
+                 << " exists. Not adding test launcher result printer.";
+    return;
+  }
+
+  XmlUnitTestResultPrinter* printer = new XmlUnitTestResultPrinter;
+  CHECK(printer->Initialize(output_path));
+  testing::TestEventListeners& listeners =
+      testing::UnitTest::GetInstance()->listeners();
+  listeners.Append(printer);
+}
+#endif  // !defined(OS_IOS)
+
 // Don't add additional code to this method.  Instead add it to
 // Initialize().  See bug 6436.
 int TestSuite::Run() {
@@ -250,6 +281,9 @@
 
   CatchMaybeTests();
   ResetCommandLine();
+#if !defined(OS_IOS)
+  AddTestLauncherResultPrinter();
+#endif  // !defined(OS_IOS)
 
   TestTimeouts::Initialize();
 }
diff --git a/base/test/test_suite.h b/base/test/test_suite.h
index 9f139ed..50cde84 100644
--- a/base/test/test_suite.h
+++ b/base/test/test_suite.h
@@ -37,6 +37,8 @@
 
   void ResetCommandLine();
 
+  void AddTestLauncherResultPrinter();
+
   int Run();
 
  protected:
diff --git a/base/test/test_switches.cc b/base/test/test_switches.cc
index 79fbad1..bf7a76a 100644
--- a/base/test/test_switches.cc
+++ b/base/test/test_switches.cc
@@ -7,6 +7,12 @@
 // Time (in milliseconds) that the tests should wait before timing out.
 // TODO(phajdan.jr): Clean up the switch names.
 const char switches::kTestLargeTimeout[] = "test-large-timeout";
+
+// Path to test results file in our custom test launcher format.
+const char switches::kTestLauncherOutput[] = "test-launcher-output";
+
+// Time (in milliseconds) that the tests should wait before timing out.
+// TODO(phajdan.jr): Clean up the switch names.
 const char switches::kTestTinyTimeout[] = "test-tiny-timeout";
 const char switches::kUiTestActionTimeout[] = "ui-test-action-timeout";
 const char switches::kUiTestActionMaxTimeout[] = "ui-test-action-max-timeout";
diff --git a/base/test/test_switches.h b/base/test/test_switches.h
index a5665d4..123d480 100644
--- a/base/test/test_switches.h
+++ b/base/test/test_switches.h
@@ -10,6 +10,7 @@
 // All switches in alphabetical order. The switches should be documented
 // alongside the definition of their values in the .cc file.
 extern const char kTestLargeTimeout[];
+extern const char kTestLauncherOutput[];
 extern const char kTestTinyTimeout[];
 extern const char kUiTestActionTimeout[];
 extern const char kUiTestActionMaxTimeout[];
diff --git a/base/test/unit_test_launcher.cc b/base/test/unit_test_launcher.cc
index ebf3d40..0116bba 100644
--- a/base/test/unit_test_launcher.cc
+++ b/base/test/unit_test_launcher.cc
@@ -4,6 +4,8 @@
 
 #include "base/test/unit_test_launcher.h"
 
+#include "base/bind.h"
+#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/files/scoped_temp_dir.h"
@@ -13,6 +15,7 @@
 #include "base/strings/string_util.h"
 #include "base/test/gtest_xml_util.h"
 #include "base/test/test_launcher.h"
+#include "base/test/test_switches.h"
 #include "base/test/test_timeouts.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -36,22 +39,10 @@
 CommandLine GetCommandLineForChildGTestProcess(
     const std::vector<std::string>& test_names,
     const base::FilePath& output_file) {
-  CommandLine new_cmd_line(CommandLine::ForCurrentProcess()->GetProgram());
-  CommandLine::SwitchMap switches =
-      CommandLine::ForCurrentProcess()->GetSwitches();
+  CommandLine new_cmd_line(*CommandLine::ForCurrentProcess());
 
-  switches.erase(kGTestOutputFlag);
-  new_cmd_line.AppendSwitchPath(
-      kGTestOutputFlag,
-      base::FilePath(FILE_PATH_LITERAL("xml:") + output_file.value()));
-
-  for (CommandLine::SwitchMap::const_iterator iter = switches.begin();
-        iter != switches.end(); ++iter) {
-    new_cmd_line.AppendSwitchNative(iter->first, iter->second);
-  }
-
-  new_cmd_line.AppendSwitchASCII(kGTestFilterFlag,
-                                  JoinString(test_names, ":"));
+  new_cmd_line.AppendSwitchPath(switches::kTestLauncherOutput, output_file);
+  new_cmd_line.AppendSwitchASCII(kGTestFilterFlag, JoinString(test_names, ":"));
   new_cmd_line.AppendSwitch(kSingleProcessTestsFlag);
   new_cmd_line.AppendSwitch(kBraveNewTestLauncherFlag);
 
@@ -87,10 +78,13 @@
 
     // Run tests in batches no larger than the limit.
     if (tests_.size() >= kTestBatchLimit)
-      RunRemainingTests();
+      RunRemainingTests(Bind(&DoNothing));
   }
 
-  virtual void RunRemainingTests() OVERRIDE {
+  virtual void RunRemainingTests(
+      const RunRemainingTestsCallback& callback) OVERRIDE {
+    ScopedClosureRunner scoped_runner(callback);
+
     if (tests_.empty())
       return;
 
@@ -125,30 +119,54 @@
                                             timeout,
                                             &was_timeout);
 
-    ProcessTestResults(output_file, exit_code);
+    std::vector<TestLaunchInfo> tests_to_relaunch_after_crash;
+    ProcessTestResults(output_file, exit_code, &tests_to_relaunch_after_crash);
 
-    tests_.clear();
+    tests_ = tests_to_relaunch_after_crash;
   }
 
-  void ProcessTestResults(const base::FilePath& output_file, int exit_code) {
+  void ProcessTestResults(
+      const base::FilePath& output_file,
+      int exit_code,
+      std::vector<TestLaunchInfo>* tests_to_relaunch_after_crash) {
     std::vector<TestResult> test_results;
-    bool have_test_results = ProcessGTestOutput(output_file, &test_results);
+    bool crashed = false;
+    bool have_test_results =
+        ProcessGTestOutput(output_file, &test_results, &crashed);
 
     if (have_test_results) {
       // TODO(phajdan.jr): Check for duplicates and mismatches between
       // the results we got from XML file and tests we intended to run.
-      std::map<std::string, bool> results_map;
+      std::map<std::string, TestResult> results_map;
       for (size_t i = 0; i < test_results.size(); i++)
-        results_map[test_results[i].GetFullName()] = test_results[i].success;
+        results_map[test_results[i].GetFullName()] = test_results[i];
+
+      bool had_crashed_test = false;
 
       for (size_t i = 0; i < tests_.size(); i++) {
-        TestResult test_result;
-        test_result.test_case_name = tests_[i].test_case_name;
-        test_result.test_name = tests_[i].test_name;
-        test_result.success = results_map[tests_[i].GetFullName()];
-        tests_[i].callback.Run(test_result);
+        if (ContainsKey(results_map, tests_[i].GetFullName())) {
+          TestResult result = results_map[tests_[i].GetFullName()];
+          if (result.crashed)
+            had_crashed_test = true;
+          tests_[i].callback.Run(results_map[tests_[i].GetFullName()]);
+        } else if (had_crashed_test) {
+          tests_to_relaunch_after_crash->push_back(tests_[i]);
+        } else {
+          // TODO(phajdan.jr): Explicitly pass the info that the test didn't
+          // run for a mysterious reason.
+          LOG(ERROR) << "no test result for " << tests_[i].GetFullName();
+          TestResult test_result;
+          test_result.test_case_name = tests_[i].test_case_name;
+          test_result.test_name = tests_[i].test_name;
+          test_result.success = false;
+          test_result.crashed = false;
+          tests_[i].callback.Run(test_result);
+        }
       }
 
+      // TODO(phajdan.jr): Handle the case where processing XML output
+      // indicates a crash but none of the test results is marked as crashing.
+
       // TODO(phajdan.jr): Handle the case where the exit code is non-zero
       // but results file indicates that all tests passed (e.g. crash during
       // shutdown).
@@ -163,6 +181,7 @@
         test_result.test_case_name = tests_[i].test_case_name;
         test_result.test_name = tests_[i].test_name;
         test_result.success = (exit_code == 0);
+        test_result.crashed = false;
         tests_[i].callback.Run(test_result);
       }
     }
@@ -206,4 +225,4 @@
   return exit_code;
 }
 
-}  // namespace base
\ No newline at end of file
+}  // namespace base
diff --git a/base/threading/platform_thread_linux.cc b/base/threading/platform_thread_linux.cc
index 80227c3..6d46de4 100644
--- a/base/threading/platform_thread_linux.cc
+++ b/base/threading/platform_thread_linux.cc
@@ -27,19 +27,15 @@
 
 namespace {
 int ThreadNiceValue(ThreadPriority priority) {
-  static const int threadPriorityAudio = -10;
-  static const int threadPriorityBackground = 10;
-  static const int threadPriorityDefault = 0;
-  static const int threadPriorityDisplay = -6;
   switch (priority) {
     case kThreadPriority_RealtimeAudio:
-      return threadPriorityAudio;
+      return -10;
     case kThreadPriority_Background:
-      return threadPriorityBackground;
+      return 10;
     case kThreadPriority_Normal:
-      return threadPriorityDefault;
+      return 0;
     case kThreadPriority_Display:
-      return threadPriorityDisplay;
+      return -6;
     default:
       NOTREACHED() << "Unknown priority.";
       return 0;
@@ -52,7 +48,7 @@
   ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name);
   tracked_objects::ThreadData::InitializeThreadContext(name);
 
-#ifndef OS_NACL
+#if !defined(OS_NACL)
   // On linux we can get the thread names to show up in the debugger by setting
   // the process name for the LWP.  We don't want to do this for the main
   // thread because that would rename the process, causing tools like killall
@@ -69,7 +65,7 @@
   // We expect EPERM failures in sandboxed processes, just ignore those.
   if (err < 0 && errno != EPERM)
     DPLOG(ERROR) << "prctl(PR_SET_NAME)";
-#endif
+#endif  //  !defined(OS_NACL)
 }
 
 // static
@@ -77,13 +73,8 @@
                                        ThreadPriority priority) {
 #if !defined(OS_NACL)
   if (priority == kThreadPriority_RealtimeAudio) {
-    const int kRealTimePrio = 8;
-
-    struct sched_param sched_param;
-    memset(&sched_param, 0, sizeof(sched_param));
-    sched_param.sched_priority = kRealTimePrio;
-
-    if (pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param) == 0) {
+    const struct sched_param kRealTimePrio = { 8 };
+    if (pthread_setschedparam(pthread_self(), SCHED_RR, &kRealTimePrio) == 0) {
       // Got real time priority, no need to set nice level.
       return;
     }
@@ -94,20 +85,19 @@
   // process. Setting this priority will only succeed if the user has been
   // granted permission to adjust nice values on the system.
   DCHECK_NE(handle.id_, kInvalidThreadId);
-  int kNiceSetting = ThreadNiceValue(priority);
-  if (setpriority(PRIO_PROCESS, handle.id_, kNiceSetting))
-    LOG(ERROR) << "Failed to set nice value of thread to " << kNiceSetting;
-#endif  // !OS_NACL
+  const int kNiceSetting = ThreadNiceValue(priority);
+  if (setpriority(PRIO_PROCESS, handle.id_, kNiceSetting)) {
+    DVPLOG(1) << "Failed to set nice value of thread ("
+              << handle.id_ << ") to " << kNiceSetting;
+  }
+#endif  //  !defined(OS_NACL)
 }
 
-void InitThreading() {
-}
+void InitThreading() {}
 
-void InitOnThread() {
-}
+void InitOnThread() {}
 
-void TerminateOnThread() {
-}
+void TerminateOnThread() {}
 
 size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) {
   return 0;
diff --git a/base/threading/post_task_and_reply_impl.cc b/base/threading/post_task_and_reply_impl.cc
index f464c6a..a82a4fd 100644
--- a/base/threading/post_task_and_reply_impl.cc
+++ b/base/threading/post_task_and_reply_impl.cc
@@ -6,7 +6,8 @@
 
 #include "base/bind.h"
 #include "base/location.h"
-#include "base/message_loop/message_loop_proxy.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
 
 namespace base {
 
@@ -26,7 +27,7 @@
   PostTaskAndReplyRelay(const tracked_objects::Location& from_here,
                         const Closure& task, const Closure& reply)
       : from_here_(from_here),
-        origin_loop_(MessageLoopProxy::current()) {
+        origin_loop_(ThreadTaskRunnerHandle::Get()) {
     task_ = task;
     reply_ = reply;
   }
@@ -61,7 +62,7 @@
   }
 
   tracked_objects::Location from_here_;
-  scoped_refptr<MessageLoopProxy> origin_loop_;
+  scoped_refptr<SingleThreadTaskRunner> origin_loop_;
   Closure reply_;
   Closure task_;
 };
diff --git a/base/threading/thread_id_name_manager.cc b/base/threading/thread_id_name_manager.cc
index ef08548..7c85c1b 100644
--- a/base/threading/thread_id_name_manager.cc
+++ b/base/threading/thread_id_name_manager.cc
@@ -20,7 +20,8 @@
 }
 
 ThreadIdNameManager::ThreadIdNameManager()
-    : main_process_id_(kInvalidThreadId) {
+    : main_process_name_(NULL),
+      main_process_id_(kInvalidThreadId) {
   g_default_name = new std::string(kDefaultName);
 
   AutoLock locked(lock_);
diff --git a/base/win/win_util.cc b/base/win/win_util.cc
index 11398bf..813af31 100644
--- a/base/win/win_util.cc
+++ b/base/win/win_util.cc
@@ -115,6 +115,11 @@
   return (::GetKeyState(VK_MENU) & 0x8000) == 0x8000;
 }
 
+bool IsAltGrPressed() {
+  return (::GetKeyState(VK_MENU) & 0x8000) == 0x8000 &&
+      (::GetKeyState(VK_CONTROL) & 0x8000) == 0x8000;
+}
+
 bool UserAccountControlIsEnabled() {
   // This can be slow if Windows ends up going to disk.  Should watch this key
   // for changes and only read it once, preferably on the file thread.
diff --git a/base/win/win_util.h b/base/win/win_util.h
index a91183a..903fcf9 100644
--- a/base/win/win_util.h
+++ b/base/win/win_util.h
@@ -50,6 +50,11 @@
 // Returns true if the alt key is currently pressed.
 BASE_EXPORT bool IsAltPressed();
 
+// Returns true if the altgr key is currently pressed.
+// Windows does not have specific key code and modifier bit and Alt+Ctrl key is
+// used as AltGr key in Windows.
+BASE_EXPORT bool IsAltGrPressed();
+
 // Returns false if user account control (UAC) has been disabled with the
 // EnableLUA registry flag. Returns true if user account control is enabled.
 // NOTE: The EnableLUA registry flag, which is ignored on Windows XP
diff --git a/build/all.gyp b/build/all.gyp
index e81c9c8..32ad72a 100644
--- a/build/all.gyp
+++ b/build/all.gyp
@@ -14,7 +14,6 @@
         '../chrome/chrome.gyp:*',
         '../content/content.gyp:*',
         '../crypto/crypto.gyp:*',
-        '../media/media.gyp:*',
         '../net/net.gyp:*',
         '../sdch/sdch.gyp:*',
         '../sql/sql.gyp:*',
@@ -41,6 +40,7 @@
             '../gpu/tools/tools.gyp:*',
             '../ipc/ipc.gyp:*',
             '../jingle/jingle.gyp:*',
+            '../media/media.gyp:*',
             '../ppapi/ppapi.gyp:*',
             '../ppapi/ppapi_internal.gyp:*',
             '../printing/printing.gyp:*',
@@ -207,7 +207,6 @@
         '../base/base.gyp:base_unittests',
         '../chrome/chrome.gyp:unit_tests',
         '../crypto/crypto.gyp:crypto_unittests',
-        '../media/media.gyp:media_unittests',
         '../net/net.gyp:net_unittests',
         '../sql/sql.gyp:sql_unittests',
         '../ui/ui.gyp:ui_unittests',
@@ -232,6 +231,7 @@
             '../gpu/gles2_conform_support/gles2_conform_support.gyp:gles2_conform_support',
             '../ipc/ipc.gyp:ipc_tests',
             '../jingle/jingle.gyp:jingle_unittests',
+            '../media/media.gyp:media_unittests',
             '../ppapi/ppapi_internal.gyp:ppapi_unittests',
             '../printing/printing.gyp:printing_unittests',
             '../remoting/remoting.gyp:remoting_unittests',
@@ -350,6 +350,11 @@
                 '../breakpad/breakpad.gyp:minidump_stackwalk',
               ],
             }],
+            ['OS=="linux"', {
+              'dependencies': [
+                '../chrome/chrome.gyp:linux_symbols'
+              ],
+            }],
           ],
         }, # target_name: chromium_builder_perf
         {
diff --git a/build/android/adb_profile_chrome b/build/android/adb_profile_chrome
index 79a3d5d..0a7199f 100755
--- a/build/android/adb_profile_chrome
+++ b/build/android/adb_profile_chrome
@@ -43,14 +43,45 @@
 }
 
 download_latest_trace() {
-  TRACE_FILE=$(adb logcat -d | \
-      grep "Logging performance trace to file: " | \
-      tail -1 | \
-      perl -pi -e "s/.*\/storage\/emulated\/.+\/([^\r]+).*/\/sdcard\/Download\/\\1/g")
-  if [ -z "$TRACE_FILE" ]; then
-    echo "Unable to determine trace file name"
-    exit 1
-  fi
+  (adb logcat -d | grep -q "Logging performance trace to file") || {
+    echo "WARNING: Trace start marker not found. Is the correct version of Chrome running?"
+  }
+
+  local ITERATION=0
+  while true; do
+    # Chrome logs two different messages related to tracing:
+    #
+    # 1. "Logging performance trace to file [...]"
+    # 2. "Profiler finished. Results are in [...]"
+    #
+    # The first one is printed when tracing starts and the second one indicates
+    # that the trace file is ready to be downloaded.
+    #
+    # We have to look for both of these messages to make sure we get the results
+    # from the latest trace and that the trace file is complete. This is done by
+    # first looking for the last instance of the first message and then checking
+    # for the second message in the remaining part of the log.
+    TRACE_FILE=$(adb logcat -d | \
+        tac | \
+        grep --max-count=1 --before-context=100000 "Logging performance trace to file" | \
+        tac | \
+        grep "Profiler finished[.] Results are in " | \
+        perl -pi -e "s{.*/storage/emulated/.+/([^\r]+)[.].*}{/sdcard/Download/\\1}g")
+    if [ -n "$TRACE_FILE" ]; then
+      break
+    fi
+    if [ $ITERATION -eq 0 ]; then
+      echo -n "Waiting for Chrome to finish tracing..."
+    else
+      echo -n "."
+    fi
+    let ITERATION=ITERATION+1
+    if [ $ITERATION -eq 60 ]; then
+      echo "Timed out"
+      exit 1
+    fi
+    sleep 1
+  done
 
   adb pull $TRACE_FILE 2> /dev/null
   LOCAL_TRACE_FILE=$(basename $TRACE_FILE)
@@ -72,7 +103,6 @@
   echo "done"
 
   echo -n "Downloading trace..."
-  sleep $[${INTERVAL} / 4 + 1]
   download_latest_trace
   echo "done"
 
diff --git a/build/android/buildbot/OWNERS b/build/android/buildbot/OWNERS
index e358955..b0bb076 100644
--- a/build/android/buildbot/OWNERS
+++ b/build/android/buildbot/OWNERS
@@ -2,5 +2,7 @@
 
 bulach@chromium.org
 cmp@chromium.org
+craigdh@chromium.org
+frankf@chromium.org
 ilevy@chromium.org
 yfriedman@chromium.org
diff --git a/build/android/buildbot/bb_device_steps.py b/build/android/buildbot/bb_device_steps.py
index 2cb431e..b8a1b84 100755
--- a/build/android/buildbot/bb_device_steps.py
+++ b/build/android/buildbot/bb_device_steps.py
@@ -141,6 +141,14 @@
   """
   if print_step:
     bb_annotations.PrintNamedStep('install_%s' % test.name.lower())
+  # TODO(gkanwar): Quick hack to make sure AndroidWebViewTest.apk is replaced
+  # before AndroidWebView.apk is. This can be removed once the bots cycle.
+  args = ['--apk', '%s.apk' % test.test_apk]
+  if options.target == 'Release':
+    args.append('--release')
+
+  RunCmd(['build/android/adb_install_apk.py'] + args, halt_on_failure=True)
+
   args = ['--apk', test.apk, '--apk_package', test.apk_package]
   if options.target == 'Release':
     args.append('--release')
@@ -149,7 +157,7 @@
 
 
 def RunInstrumentationSuite(options, test, flunk_on_failure=True,
-                            python_only=False):
+                            python_only=False, official_build=False):
   """Manages an invocation of test_runner.py for instrumentation tests.
 
   Args:
@@ -157,6 +165,7 @@
     test: An I_TEST namedtuple
     flunk_on_failure: Flunk the step if tests fail.
     Python: Run only host driven Python tests.
+    official_build: Run official-build tests.
   """
   bb_annotations.PrintNamedStep('%s_instrumentation_tests' % test.name.lower())
 
@@ -182,6 +191,10 @@
     args.extend(test.extra_flags)
   if python_only:
     args.append('-p')
+  if official_build:
+    # The option needs to be assigned 'True' as it does not have an action
+    # associated with it.
+    args.append('--official-build')
 
   RunCmd(['build/android/test_runner.py', 'instrumentation'] + args,
          flunk_on_failure=flunk_on_failure)
@@ -268,7 +281,7 @@
 def GetDeviceSetupStepCmds():
   return [
     ('provision_devices', ProvisionDevices),
-    ('device_status_check', DeviceStatusCheck)
+    ('device_status_check', DeviceStatusCheck),
   ]
 
 
@@ -336,6 +349,7 @@
   RunCmd(['build/android/generate_emma_html.py',
           '--coverage-dir', options.coverage_dir,
           '--metadata-dir', os.path.join(CHROME_SRC, 'out', options.target),
+          '--cleanup',
           '--output', os.path.join(coverage_html, 'index.html')])
   UploadCoverageData(options, coverage_html, 'java')
 
diff --git a/build/android/buildbot/bb_host_steps.py b/build/android/buildbot/bb_host_steps.py
index adb9ca0..e27ba08 100755
--- a/build/android/buildbot/bb_host_steps.py
+++ b/build/android/buildbot/bb_host_steps.py
@@ -102,12 +102,19 @@
           '-w', os.path.join(constants.DIR_SOURCE_ROOT, os.pardir)])
 
 
+def DownloadWebRTCResources(_):
+  bb_annotations.PrintNamedStep('download_resources')
+  RunCmd([SrcPath('third_party', 'webrtc', 'tools', 'update_resources.py'),
+          '-p', '../../../'], halt_on_failure=True)
+
+
 def GetHostStepCmds():
   return [
       ('compile', Compile),
       ('extract_build', ExtractBuild),
       ('check_webview_licenses', CheckWebViewLicenses),
       ('bisect_perf_regression', BisectPerfRegression),
+      ('download_webrtc_resources', DownloadWebRTCResources),
       ('findbugs', FindBugs),
       ('zip_build', ZipBuild)
   ]
diff --git a/build/android/buildbot/bb_run_bot.py b/build/android/buildbot/bb_run_bot.py
index 23db7ec..11a917f 100755
--- a/build/android/buildbot/bb_run_bot.py
+++ b/build/android/buildbot/bb_run_bot.py
@@ -45,10 +45,12 @@
   return '\n'.join(diff)
 
 
-def GetEnvironment(host_obj, testing):
+def GetEnvironment(host_obj, testing, extra_env_vars=None):
   init_env = dict(os.environ)
   init_env['GYP_GENERATORS'] = 'ninja'
   init_env['GOMA_DIR'] = bb_utils.GOMA_DIR
+  if extra_env_vars:
+    init_env.update(extra_env_vars)
   envsetup_cmd = '. build/android/envsetup.sh'
   if host_obj.target_arch:
     envsetup_cmd += ' --target-arch=%s' % host_obj.target_arch
@@ -169,7 +171,8 @@
         H(std_build_steps,
           extra_args=['--build-targets=android_builder_webrtc'],
           extra_gyp='include_tests=1 enable_tracing=1')),
-      B('webrtc-tests', H(std_test_steps), T(['webrtc'], [flakiness_server])),
+      B('webrtc-tests', H(['download_webrtc_resources'] + std_test_steps),
+        T(['webrtc'], [flakiness_server])),
 
       # Generic builder config (for substring match).
       B('builder', H(std_build_steps)),
diff --git a/build/android/envsetup_functions.sh b/build/android/envsetup_functions.sh
index fef07ef..2777d2d 100755
--- a/build/android/envsetup_functions.sh
+++ b/build/android/envsetup_functions.sh
@@ -223,16 +223,21 @@
   # Allow the caller to override a few environment variables. If any of them is
   # unset, we default to a sane value that's known to work. This allows for
   # experimentation with a custom SDK.
+  local sdk_defines=""
   if [[ -z "${ANDROID_NDK_ROOT}" || ! -d "${ANDROID_NDK_ROOT}" ]]; then
     export ANDROID_NDK_ROOT="${CHROME_SRC}/third_party/android_tools/ndk/"
   fi
   if [[ -z "${ANDROID_SDK_VERSION}" ]]; then
     export ANDROID_SDK_VERSION=18
+  else
+    sdk_defines+=" android_sdk_version=${ANDROID_SDK_VERSION}"
   fi
   local sdk_suffix=platforms/android-${ANDROID_SDK_VERSION}
   if [[ -z "${ANDROID_SDK_ROOT}" || \
        ! -d "${ANDROID_SDK_ROOT}/${sdk_suffix}" ]]; then
     export ANDROID_SDK_ROOT="${CHROME_SRC}/third_party/android_tools/sdk/"
+  else
+    sdk_defines+=" android_sdk_root=${ANDROID_SDK_ROOT}"
   fi
   if [[ -z "${ANDROID_SDK_BUILD_TOOLS_VERSION}" ]]; then
     export ANDROID_SDK_BUILD_TOOLS_VERSION=18.0.1
@@ -248,6 +253,9 @@
   unset ANDROID_TOOLCHAIN
 
   common_vars_defines
+
+  DEFINES+="${sdk_defines}"
+
   common_gyp_vars
 
   if [[ -n "$CHROME_ANDROID_BUILD_WEBVIEW" ]]; then
diff --git a/build/android/generate_emma_html.py b/build/android/generate_emma_html.py
index a226343..1b00a32 100755
--- a/build/android/generate_emma_html.py
+++ b/build/android/generate_emma_html.py
@@ -38,17 +38,19 @@
 
 def main(argv):
   option_parser = optparse.OptionParser()
-  option_parser.add_option('-o', '--output', help='HTML output filename.')
-  option_parser.add_option('-c', '--coverage-dir', default=None,
+  option_parser.add_option('--output', help='HTML output filename.')
+  option_parser.add_option('--coverage-dir', default=None,
                            help=('Root of the directory in which to search for '
                                  'coverage data (.ec) files.'))
-  option_parser.add_option('-m', '--metadata-dir', default=None,
+  option_parser.add_option('--metadata-dir', default=None,
                            help=('Root of the directory in which to search for '
                                  'coverage metadata (.em) files.'))
+  option_parser.add_option('--cleanup', action='store_true',
+                           help='If set, removes coverage/metadata files.')
   options, args = option_parser.parse_args()
 
   if not (options.coverage_dir and options.metadata_dir and options.output):
-    option_parser.error('All arguments are required.')
+    option_parser.error('One or more mandatory options are missing.')
 
   coverage_files = _GetFilesWithExt(options.coverage_dir, 'ec')
   metadata_files = _GetFilesWithExt(options.metadata_dir, 'em')
@@ -88,6 +90,10 @@
        'emma', 'report', '-r', 'html']
       + input_args + output_args + source_args)
 
+  if options.cleanup:
+    for f in coverage_files + metadata_files:
+      os.remove(f)
+
   if exit_code > 0:
     return exit_code
   elif err:
diff --git a/build/android/pylib/android_commands.py b/build/android/pylib/android_commands.py
index c407ffd..5a031be 100644
--- a/build/android/pylib/android_commands.py
+++ b/build/android/pylib/android_commands.py
@@ -835,8 +835,8 @@
     # only a single file is given as the base name given in device_path may
     # differ from that in host_path.
     def HostToDevicePath(host_file_path):
-      return os.path.join(os.path.dirname(device_path), os.path.relpath(
-          host_file_path, os.path.dirname(os.path.normpath(host_path))))
+      return os.path.join(device_path, os.path.relpath(
+          host_file_path, os.path.normpath(host_path)))
 
     device_hashes = [h.hash for h in device_hash_tuples]
     return [(t.path, HostToDevicePath(t.path) if os.path.isdir(host_path) else
diff --git a/build/android/pylib/base/base_test_runner.py b/build/android/pylib/base/base_test_runner.py
index bd8fefe..154ea66 100644
--- a/build/android/pylib/base/base_test_runner.py
+++ b/build/android/pylib/base/base_test_runner.py
@@ -15,7 +15,6 @@
 from pylib import constants
 from pylib import ports
 from pylib.chrome_test_server_spawner import SpawningServer
-from pylib.flag_changer import FlagChanger
 from pylib.forwarder import Forwarder
 from pylib.valgrind_tools import CreateTool
 # TODO(frankf): Move this to pylib/utils
@@ -45,8 +44,6 @@
     self._forwarder_device_port = 8000
     self.forwarder_base_url = ('http://localhost:%d' %
         self._forwarder_device_port)
-    self.flags = FlagChanger(self.adb)
-    self.flags.AddFlags(['--disable-fre'])
     self._spawning_server = None
     # We will allocate port for test server spawner when calling method
     # LaunchChromeTestServerSpawner and allocate port for test server when
@@ -166,7 +163,6 @@
       self._http_server.ShutdownHttpServer()
     if self._spawning_server:
       self._spawning_server.Stop()
-    self.flags.Restore()
 
   def CleanupSpawningServerState(self):
     """Tells the spawning server to clean up any state.
diff --git a/build/android/pylib/base/test_dispatcher.py b/build/android/pylib/base/test_dispatcher.py
index 31fa078..11374bb 100644
--- a/build/android/pylib/base/test_dispatcher.py
+++ b/build/android/pylib/base/test_dispatcher.py
@@ -126,6 +126,10 @@
         break
       yield r
 
+  def __len__(self):
+    """Return the number of tests currently in the collection."""
+    return len(self._tests)
+
 
 def _RunTestsFromQueue(runner, test_collection, out_results, watcher,
                        num_retries, tag_results_with_device=False):
@@ -235,16 +239,18 @@
   logging.warning('Running tests with %s test runners.' % (len(runners)))
   results = []
   exit_code = 0
-  watcher = watchdog_timer.WatchdogTimer(timeout)
-
-  workers = reraiser_thread.ReraiserThreadGroup(
-      [reraiser_thread.ReraiserThread(
-          _RunTestsFromQueue,
-          [r, test_collection_factory(), results, watcher, num_retries,
-           tag_results_with_device],
-          name=r.device[-4:])
-       for r in runners])
   run_results = base_test_result.TestRunResults()
+  watcher = watchdog_timer.WatchdogTimer(timeout)
+  test_collections = [test_collection_factory() for _ in runners]
+
+  threads = [
+      reraiser_thread.ReraiserThread(
+          _RunTestsFromQueue,
+          [r, tc, results, watcher, num_retries, tag_results_with_device],
+          name=r.device[-4:])
+      for r, tc in zip(runners, test_collections)]
+
+  workers = reraiser_thread.ReraiserThreadGroup(threads)
   workers.StartAll()
 
   # Catch DeviceUnresponsiveErrors and set a warning exit code
@@ -254,6 +260,10 @@
     logging.error(e)
     exit_code = constants.WARNING_EXIT_CODE
 
+  assert all([len(tc) == 0 for tc in test_collections]), (
+      'Some tests were not run, all devices are likely offline (ran %d tests)' %
+      len(run_results.GetAll()))
+
   for r in results:
     run_results.AddTestRunResults(r)
   if not run_results.DidRunPass():
diff --git a/build/android/pylib/base/test_dispatcher_unittest.py b/build/android/pylib/base/test_dispatcher_unittest.py
index 88dfda6..c6078db 100644
--- a/build/android/pylib/base/test_dispatcher_unittest.py
+++ b/build/android/pylib/base/test_dispatcher_unittest.py
@@ -186,6 +186,15 @@
     self.assertEqual(len(results.GetAll()), 0)
     self.assertEqual(exit_code, constants.ERROR_EXIT_CODE)
 
+  def testTestsRemainWithAllDevicesOffline(self):
+    attached_devices = android_commands.GetAttachedDevices
+    android_commands.GetAttachedDevices = lambda: []
+    try:
+      with self.assertRaises(AssertionError):
+        results, exit_code = TestShard._RunShard(MockRunner)
+    finally:
+      android_commands.GetAttachedDevices = attached_devices
+
 
 class TestReplicate(unittest.TestCase):
   """Tests test_dispatcher.RunTests with replication."""
diff --git a/build/android/pylib/cmd_helper.py b/build/android/pylib/cmd_helper.py
index 1237912..96a30bb 100644
--- a/build/android/pylib/cmd_helper.py
+++ b/build/android/pylib/cmd_helper.py
@@ -14,10 +14,10 @@
 import constants
 
 
-def _Call(args, stdout=None, stderr=None, shell=None, cwd=None):
+def Call(args, stdout=None, stderr=None, shell=None, cwd=None, env=None):
   return subprocess.call(
       args=args, cwd=cwd, stdout=stdout, stderr=stderr,
-      shell=shell, close_fds=True,
+      shell=shell, close_fds=True, env=env,
       preexec_fn=lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL))
 
 
@@ -34,7 +34,7 @@
     Return code from the command execution.
   """
   logging.info(str(args) + ' ' + (cwd or ''))
-  return _Call(args, cwd=cwd)
+  return Call(args, cwd=cwd)
 
 
 def GetCmdOutput(args, cwd=None, shell=False):
@@ -84,7 +84,7 @@
   logging.info(s)
   tmpout = tempfile.TemporaryFile(bufsize=0)
   tmperr = tempfile.TemporaryFile(bufsize=0)
-  exit_code = _Call(args, cwd=cwd, stdout=tmpout, stderr=tmperr, shell=shell)
+  exit_code = Call(args, cwd=cwd, stdout=tmpout, stderr=tmperr, shell=shell)
   tmperr.seek(0)
   stderr = tmperr.read()
   tmperr.close()
diff --git a/build/android/pylib/constants.py b/build/android/pylib/constants.py
index 59ffcd4..ea148e7 100644
--- a/build/android/pylib/constants.py
+++ b/build/android/pylib/constants.py
@@ -4,6 +4,7 @@
 
 """Defines a set of constants shared by test runners and other scripts."""
 
+import collections
 import os
 import subprocess
 import sys
@@ -14,23 +15,20 @@
 ISOLATE_DEPS_DIR = os.path.join(DIR_SOURCE_ROOT, 'isolate_deps_dir')
 EMULATOR_SDK_ROOT = os.path.abspath(os.path.join(DIR_SOURCE_ROOT, os.pardir,
                                                  os.pardir))
+
+# TODO(craigdh): Remove these once references have been removed downstream.
 CHROME_PACKAGE = 'com.google.android.apps.chrome'
 CHROME_ACTIVITY = 'com.google.android.apps.chrome.Main'
-CHROME_DEVTOOLS_SOCKET = 'chrome_devtools_remote'
 
 CHROME_STABLE_PACKAGE = 'com.android.chrome'
 CHROME_BETA_PACKAGE = 'com.chrome.beta'
 
-CHROME_TESTS_PACKAGE = 'com.google.android.apps.chrome.tests'
-
 LEGACY_BROWSER_PACKAGE = 'com.google.android.browser'
 LEGACY_BROWSER_ACTIVITY = 'com.android.browser.BrowserActivity'
 
 CONTENT_SHELL_PACKAGE = 'org.chromium.content_shell_apk'
 CONTENT_SHELL_ACTIVITY = 'org.chromium.content_shell_apk.ContentShellActivity'
 
-CHROME_SHELL_PACKAGE = 'org.chromium.chrome.browser.test'
-
 CHROMIUM_TEST_SHELL_PACKAGE = 'org.chromium.chrome.testshell'
 CHROMIUM_TEST_SHELL_ACTIVITY = (
     'org.chromium.chrome.testshell.ChromiumTestShellActivity')
@@ -38,14 +36,50 @@
 CHROMIUM_TEST_SHELL_HOST_DRIVEN_DIR = os.path.join(
     DIR_SOURCE_ROOT, 'chrome', 'android')
 
-GTEST_TEST_PACKAGE_NAME = 'org.chromium.native_test'
-GTEST_TEST_ACTIVITY_NAME = 'org.chromium.native_test.ChromeNativeTestActivity'
-GTEST_COMMAND_LINE_FILE = 'chrome-native-tests-command-line'
 
-BROWSERTEST_TEST_PACKAGE_NAME = 'org.chromium.content_browsertests_apk'
-BROWSERTEST_TEST_ACTIVITY_NAME = (
-    'org.chromium.content_browsertests_apk.ContentBrowserTestsActivity')
-BROWSERTEST_COMMAND_LINE_FILE = 'content-browser-tests-command-line'
+PackageInfo = collections.namedtuple('PackageInfo',
+    ['package', 'activity', 'cmdline_file', 'devtools_socket',
+     'test_package'])
+
+PACKAGE_INFO = {
+    'chrome': PackageInfo(
+        CHROME_PACKAGE,
+        CHROME_ACTIVITY,
+        '/data/local/chrome-command-line',
+        'chrome_devtools_remote',
+        'com.google.android.apps.chrome.tests'),
+    'legacy_browser': PackageInfo(
+        LEGACY_BROWSER_PACKAGE,
+        LEGACY_BROWSER_ACTIVITY,
+        None,
+        None,
+        None),
+    'content_shell': PackageInfo(
+        CONTENT_SHELL_PACKAGE,
+        CONTENT_SHELL_ACTIVITY,
+        '/data/local/tmp/content-shell-command-line',
+        None,
+        None),
+    'chromium_test_shell': PackageInfo(
+        CHROMIUM_TEST_SHELL_PACKAGE,
+        CHROMIUM_TEST_SHELL_ACTIVITY,
+        '/data/local/tmp/chromium-testshell-command-line',
+        CHROMIUM_TEST_SHELL_DEVTOOLS_SOCKET,
+        'org.chromium.chrome.testshell.tests'),
+    'gtest': PackageInfo(
+        'org.chromium.native_test',
+        'org.chromium.native_test.ChromeNativeTestActivity',
+        '/data/local/tmp/chrome-native-tests-command-line',
+        None,
+        None),
+    'content_browsertests': PackageInfo(
+        'org.chromium.content_browsertests_apk',
+        'org.chromium.content_browsertests_apk.ContentBrowserTestsActivity',
+        '/data/local/tmp/content-browser-tests-command-line',
+        None,
+        None),
+}
+
 
 # Ports arrangement for various test servers used in Chrome for Android.
 # Lighttpd server will attempt to use 9000 as default port, if unavailable it
diff --git a/build/android/pylib/flag_changer.py b/build/android/pylib/flag_changer.py
index 6eb9f0b..6c4883a 100644
--- a/build/android/pylib/flag_changer.py
+++ b/build/android/pylib/flag_changer.py
@@ -5,12 +5,8 @@
 import constants
 import logging
 import traceback
-import warnings
 
 
-# Location where chrome reads command line flags from
-CHROME_COMMAND_FILE = '/data/local/chrome-command-line'
-
 class FlagChanger(object):
   """Changes the flags Chrome runs with.
 
@@ -21,11 +17,19 @@
     once the tests have completed.
   """
 
-  def __init__(self, android_cmd):
-    self._android_cmd = android_cmd
+  def __init__(self, adb,
+               cmdline_file=constants.PACKAGE_INFO['chrome'].cmdline_file):
+    """Initializes the FlagChanger and records the original arguments.
+
+    Args:
+      adb: An instance of AndroidCommands.
+      cmdline_file: Path to the command line file on the device.
+    """
+    self._adb = adb
+    self._cmdline_file = cmdline_file
 
     # Save the original flags.
-    self._orig_line = self._android_cmd.GetFileContents(CHROME_COMMAND_FILE)
+    self._orig_line = self._adb.GetFileContents(self._cmdline_file)
     if self._orig_line:
       self._orig_line = self._orig_line[0].strip()
 
@@ -90,11 +94,12 @@
     logging.info('Current flags: %s', self._current_flags)
 
     if self._current_flags:
-      self._android_cmd.SetProtectedFileContents(CHROME_COMMAND_FILE,
-                                        'chrome ' +
-                                        ' '.join(self._current_flags))
+      # The first command line argument doesn't matter as we are not actually
+      # launching the chrome executable using this command line.
+      self._adb.SetProtectedFileContents(self._cmdline_file,
+                                         ' '.join(['_'] + self._current_flags))
     else:
-      self._android_cmd.RunShellCommand('su -c rm ' + CHROME_COMMAND_FILE)
+      self._adb.RunShellCommand('su -c rm ' + self._cmdline_file)
 
   def _TokenizeFlags(self, line):
     """Changes the string containing the command line into a list of flags.
@@ -137,7 +142,7 @@
     # Tack on the last flag.
     if not current_flag:
       if within_quotations:
-        warnings.warn("Unterminated quoted string: " + current_flag)
+        logging.warn('Unterminated quoted argument: ' + line)
     else:
       tokenized_flags.append(current_flag)
 
diff --git a/build/android/pylib/gtest/gtest_config.py b/build/android/pylib/gtest/gtest_config.py
index 64cdbaf..936d645 100644
--- a/build/android/pylib/gtest/gtest_config.py
+++ b/build/android/pylib/gtest/gtest_config.py
@@ -6,6 +6,7 @@
 
 # Add new suites here before upgrading them to the stable list below.
 EXPERIMENTAL_TEST_SUITES = [
+    'gl_tests',
 ]
 
 # Do not modify this list without approval of an android owner.
diff --git a/build/android/pylib/gtest/setup.py b/build/android/pylib/gtest/setup.py
index f139c97..3be6b81 100644
--- a/build/android/pylib/gtest/setup.py
+++ b/build/android/pylib/gtest/setup.py
@@ -21,8 +21,9 @@
 import test_runner
 
 sys.path.insert(0,
-                os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'util', 'lib'))
-from common import unittest_util
+                os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'util', 'lib',
+                             'common'))
+import unittest_util
 
 
 _ISOLATE_FILE_PATHS = {
@@ -33,7 +34,6 @@
     'content_browsertests': 'content/content_browsertests.isolate',
     'content_unittests': 'content/content_unittests.isolate',
     'media_unittests': 'media/media_unittests.isolate',
-    'modules_unittests': 'third_party/webrtc/modules/modules_unittests.isolate',
     'net_unittests': 'net/net_unittests.isolate',
     'ui_unittests': 'ui/ui_unittests.isolate',
     'unit_tests': 'chrome/unit_tests.isolate',
@@ -41,6 +41,29 @@
       'third_party/WebKit/Source/web/WebKitUnitTests.isolate',
 }
 
+# Paths relative to third_party/webrtc/ (kept separate for readability).
+_WEBRTC_ISOLATE_FILE_PATHS = {
+    'audio_decoder_unittests':
+      'modules/audio_coding/neteq4/audio_decoder_unittests.isolate',
+    'common_audio_unittests': 'common_audio/common_audio_unittests.isolate',
+    'common_video_unittests': 'common_video/common_video_unittests.isolate',
+    'metrics_unittests': 'test/metrics_unittests.isolate',
+    'modules_tests': 'modules/modules_tests.isolate',
+    'modules_unittests': 'modules/modules_unittests.isolate',
+    'neteq_unittests': 'modules/audio_coding/neteq/neteq_unittests.isolate',
+    'system_wrappers_unittests':
+      'system_wrappers/source/system_wrappers_unittests.isolate',
+    'test_support_unittests': 'test/test_support_unittests.isolate',
+    'tools_unittests': 'tools/tools_unittests.isolate',
+    'video_engine_core_unittests':
+      'video_engine/video_engine_core_unittests.isolate',
+    'voice_engine_unittests': 'voice_engine/voice_engine_unittests.isolate',
+}
+
+# Append the WebRTC tests with the full path from Chromium's src/ root.
+for test,isolate_path in _WEBRTC_ISOLATE_FILE_PATHS.items():
+  _ISOLATE_FILE_PATHS[test] = 'third_party/webrtc/%s' % isolate_path
+
 # Used for filtering large data deps at a finer grain than what's allowed in
 # isolate files since pushing deps to devices is expensive.
 # Wildcards are allowed.
diff --git a/build/android/pylib/gtest/test_package_apk.py b/build/android/pylib/gtest/test_package_apk.py
index 5944147..81c4cfe 100644
--- a/build/android/pylib/gtest/test_package_apk.py
+++ b/build/android/pylib/gtest/test_package_apk.py
@@ -34,15 +34,11 @@
     if suite_name == 'content_browsertests':
       self.suite_path = os.path.join(
           product_dir, 'apks', '%s.apk' % suite_name)
-      self._test_apk_package_name = constants.BROWSERTEST_TEST_PACKAGE_NAME
-      self._test_activity_name = constants.BROWSERTEST_TEST_ACTIVITY_NAME
-      self._command_line_file = constants.BROWSERTEST_COMMAND_LINE_FILE
+      self._package_info = constants.PACKAGE_INFO['content_browsertests']
     else:
       self.suite_path = os.path.join(
           product_dir, '%s_apk' % suite_name, '%s-debug.apk' % suite_name)
-      self._test_apk_package_name = constants.GTEST_TEST_PACKAGE_NAME
-      self._test_activity_name = constants.GTEST_TEST_ACTIVITY_NAME
-      self._command_line_file = constants.GTEST_COMMAND_LINE_FILE
+      self._package_info = constants.PACKAGE_INFO['gtest']
 
   def _CreateCommandLineFileOnDevice(self, adb, options):
     command_line_file = tempfile.NamedTemporaryFile()
@@ -50,15 +46,14 @@
     command_line_file.write(self.suite_name + ' ' + options)
     command_line_file.flush()
     adb.PushIfNeeded(command_line_file.name,
-                          constants.TEST_EXECUTABLE_DIR + '/' +
-                          self._command_line_file)
+                     self._package_info.cmdline_file)
 
   def _GetFifo(self):
     # The test.fifo path is determined by:
     # testing/android/java/src/org/chromium/native_test/
     #     ChromeNativeTestActivity.java and
     # testing/android/native_test_launcher.cc
-    return '/data/data/' + self._test_apk_package_name + '/files/test.fifo'
+    return '/data/data/' + self._package_info.package + '/files/test.fifo'
 
   def _ClearFifo(self, adb):
     adb.RunShellCommand('rm -f ' + self._GetFifo())
@@ -78,15 +73,15 @@
 
   def _StartActivity(self, adb):
     adb.StartActivity(
-        self._test_apk_package_name,
-        self._test_activity_name,
+        self._package_info.package,
+        self._package_info.activity,
         wait_for_completion=True,
         action='android.intent.action.MAIN',
         force_stop=True)
 
   #override
   def ClearApplicationState(self, adb):
-    adb.ClearApplicationState(self._test_apk_package_name)
+    adb.ClearApplicationState(self._package_info.package)
     # Content shell creates a profile on the sdscard which accumulates cache
     # files over time.
     if self.suite_name == 'content_browsertests':
@@ -132,4 +127,4 @@
   def Install(self, adb):
     self.tool.CopyFiles()
     adb.ManagedInstall(self.suite_path, False,
-                            package_name=self._test_apk_package_name)
+                            package_name=self._package_info.package)
diff --git a/build/android/pylib/instrumentation/setup.py b/build/android/pylib/instrumentation/setup.py
index 3b24188..22c2344 100644
--- a/build/android/pylib/instrumentation/setup.py
+++ b/build/android/pylib/instrumentation/setup.py
@@ -5,6 +5,7 @@
 """Generates test runner factory and tests for instrumentation tests."""
 
 import logging
+import os
 
 import test_package
 import test_runner
@@ -19,6 +20,10 @@
   Returns:
     A tuple of (TestRunnerFactory, tests).
   """
+  if (test_options.coverage_dir and not
+      os.path.exists(test_options.coverage_dir)):
+    os.makedirs(test_options.coverage_dir)
+
   test_pkg = test_package.TestPackage(test_options.test_apk_path,
                                       test_options.test_apk_jar_path)
   tests = test_pkg._GetAllMatchingTests(
diff --git a/build/android/pylib/instrumentation/test_runner.py b/build/android/pylib/instrumentation/test_runner.py
index 73440f5..9599b25 100644
--- a/build/android/pylib/instrumentation/test_runner.py
+++ b/build/android/pylib/instrumentation/test_runner.py
@@ -11,6 +11,7 @@
 
 from pylib import android_commands
 from pylib import constants
+from pylib import flag_changer
 from pylib import json_perf_parser
 from pylib import perf_tests_helper
 from pylib import valgrind_tools
@@ -71,6 +72,14 @@
     self.test_pkg = test_pkg
     self.ports_to_forward = ports_to_forward
     self.coverage_dir = test_options.coverage_dir
+    # Use the correct command line file for the package under test.
+    cmdline_file = [a.cmdline_file for a in constants.PACKAGE_INFO.itervalues()
+                    if a.test_package == self.test_pkg.GetPackageName()]
+    assert len(cmdline_file) < 2, 'Multiple packages have the same test package'
+    if len(cmdline_file) and cmdline_file[0]:
+      self.flags = flag_changer.FlagChanger(self.adb, cmdline_file[0])
+    else:
+      self.flags = flag_changer.FlagChanger(self.adb)
 
   #override
   def InstallTestPackage(self):
@@ -141,10 +150,11 @@
         os.path.join(constants.DIR_SOURCE_ROOT), self._lighttp_port)
     if self.ports_to_forward:
       self._ForwardPorts([(port, port) for port in self.ports_to_forward])
-    self.flags.AddFlags(['--enable-test-intents'])
+    self.flags.AddFlags(['--disable-fre', '--enable-test-intents'])
 
   def TearDown(self):
     """Cleans up the test harness and saves outstanding data from test run."""
+    self.flags.Restore()
     if self.ports_to_forward:
       self._UnmapPorts([(port, port) for port in self.ports_to_forward])
     super(TestRunner, self).TearDown()
diff --git a/build/android/pylib/perf/test_runner.py b/build/android/pylib/perf/test_runner.py
index bf09dfe..f125a23 100644
--- a/build/android/pylib/perf/test_runner.py
+++ b/build/android/pylib/perf/test_runner.py
@@ -72,6 +72,10 @@
 
   with file(file_name, 'r') as f:
     persisted_result = pickle.loads(f.read())
+  logging.info('*' * 80)
+  logging.info('Output from:')
+  logging.info(persisted_result['cmd'])
+  logging.info('*' * 80)
   print persisted_result['output']
 
   return persisted_result['exit_code']
@@ -115,6 +119,8 @@
         withexitstatus=True, logfile=sys.stdout, timeout=1800,
         env=os.environ)
     end_time = datetime.datetime.now()
+    if exit_code is None:
+      exit_code = -1
     logging.info('%s : exit_code=%d in %d secs at %s',
                  test_name, exit_code, (end_time - start_time).seconds,
                  self.device)
@@ -132,6 +138,7 @@
         'result_type': result_type,
         'total_time': (end_time - start_time).seconds,
         'device': self.device,
+        'cmd': cmd,
     }
     self._SaveResult(persisted_result)
 
diff --git a/build/common.gypi b/build/common.gypi
index e70eaac..a13bc6f 100644
--- a/build/common.gypi
+++ b/build/common.gypi
@@ -165,14 +165,6 @@
           }, {
             'use_default_render_theme%': 0,
           }],
-
-          # TODO(thestig) Remove the linux_lsb_release check after all the
-          # official Ubuntu Lucid builder are gone.
-          ['OS=="linux" and branding=="Chrome" and buildtype=="Official" and chromeos==0', {
-            'linux_lsb_release%': '<!(lsb_release -r -s)',
-          }, {
-            'linux_lsb_release%': '',
-          }], # OS=="linux" and branding=="Chrome" and buildtype=="Official" and chromeos==0
         ],
       },
 
@@ -197,7 +189,6 @@
       'buildtype%': '<(buildtype)',
       'branding%': '<(branding)',
       'arm_version%': '<(arm_version)',
-      'linux_lsb_release%': '<(linux_lsb_release)',
 
       # Set to 1 to enable fast builds. Set to 2 for even faster builds
       # (it disables debug info for fastest compilation - only for use
@@ -546,6 +537,8 @@
         # proprietary codecs.
         ['OS=="android" or branding=="Chrome"', {
           'proprietary_codecs%': 1,
+        }, {
+          'proprietary_codecs%': 0,
         }],
 
         # Enable autofill dialog for Android, Mac and Views-enabled platforms.
@@ -672,7 +665,8 @@
           'sysroot%': '<!(cd <(DEPTH) && pwd -P)/arm-sysroot',
         }], # OS=="linux" and target_arch=="arm" and chromeos==0
 
-        ['linux_lsb_release=="12.04"', {
+
+        ['OS=="linux" and branding=="Chrome" and buildtype=="Official" and chromeos==0', {
           'conditions': [
             ['target_arch=="x64"', {
               'sysroot%': '<!(cd <(DEPTH) && pwd -P)/chrome/installer/linux/debian_wheezy_amd64-sysroot',
@@ -681,7 +675,7 @@
               'sysroot%': '<!(cd <(DEPTH) && pwd -P)/chrome/installer/linux/debian_wheezy_i386-sysroot',
             }],
         ],
-        }], # linux_lsb_release=="12.04"
+        }], # OS=="linux" and branding=="Chrome" and buildtype=="Official" and chromeos==0
 
         ['OS=="linux" and target_arch=="mipsel"', {
           'sysroot%': '<!(cd <(DEPTH) && pwd -P)/mipsel-sysroot/sysroot',
@@ -882,6 +876,7 @@
     'spdy_proxy_auth_value%': '<(spdy_proxy_auth_value)',
     'enable_mdns%' : '<(enable_mdns)',
     'v8_optimized_debug': '<(v8_optimized_debug)',
+    'proprietary_codecs%': '<(proprietary_codecs)',
 
     # Use system nspr instead of the bundled one.
     'use_system_nspr%': 0,
@@ -941,10 +936,6 @@
     # to ~/.gyp/include.gypi, gclient runhooks --force, and do a release build.
     'win_use_allocator_shim%': 1, # 1 = shim allocator via libcmt; 0 = msvcrt
 
-    # Whether proprietary audio/video codecs are assumed to be included with
-    # this build (only meaningful if branding!=Chrome).
-    'proprietary_codecs%': 0,
-
     # TODO(bradnelson): eliminate this when possible.
     # To allow local gyp files to prevent release.vsprops from being included.
     # Yes(1) means include release.vsprops.
@@ -1626,6 +1617,9 @@
       ['enable_webrtc==1', {
         'grit_defines': ['-D', 'enable_webrtc'],
       }],
+      ['enable_mdns==1', {
+        'grit_defines': ['-D', 'enable_mdns'],
+      }],
       ['clang_use_chrome_plugins==1 and OS!="win"', {
         'clang_chrome_plugins_flags': [
           '<!@(<(DEPTH)/tools/clang/scripts/plugin_flags.sh)'
diff --git a/build/install-build-deps-android.sh b/build/install-build-deps-android.sh
index f6b445b..9417b1a 100755
--- a/build/install-build-deps-android.sh
+++ b/build/install-build-deps-android.sh
@@ -49,8 +49,8 @@
 # Few binaries in the Android SDK require 32-bit libraries on the host.
 sudo apt-get -y install lib32z1 g++-multilib
 
-if /usr/bin/lsb_release -r -s | grep -q "12."; then
-  # Ubuntu 12.x
+if [ $(/usr/bin/lsb_release -r -s | cut -d"." -f1) -ge 12 ]; then
+  # Ubuntu >= 12.x
   sudo apt-get -y install ant
 
   # Java can not be installed via ppa on Ubuntu 12.04+ so we'll
diff --git a/build/ios/grit_whitelist.txt b/build/ios/grit_whitelist.txt
index ed15d46..2abb5b3 100644
--- a/build/ios/grit_whitelist.txt
+++ b/build/ios/grit_whitelist.txt
@@ -484,6 +484,8 @@
 IDS_FLAGS_DISABLE_NATIVE_AUTOFILL_UI_NAME
 IDS_FLAGS_DISABLE_OVERSCROLL_HISTORY_NAVIGATION_DESCRIPTION
 IDS_FLAGS_DISABLE_OVERSCROLL_HISTORY_NAVIGATION_NAME
+IDS_FLAGS_DISABLE_PNACL_DESCRIPTION
+IDS_FLAGS_DISABLE_PNACL_NAME
 IDS_FLAGS_DISABLE_RESTORE_SESSION_STATE_DESCRIPTION
 IDS_FLAGS_DISABLE_RESTORE_SESSION_STATE_NAME
 IDS_FLAGS_DISABLE_SOFTWARE_RASTERIZER_DESCRIPTION
@@ -670,8 +672,6 @@
 IDS_FLAGS_PERFORMANCE_MONITOR_GATHERING_NAME
 IDS_FLAGS_PER_TILE_PAINTING_DESCRIPTION
 IDS_FLAGS_PER_TILE_PAINTING_NAME
-IDS_FLAGS_PNACL_DESCRIPTION
-IDS_FLAGS_PNACL_NAME
 IDS_FLAGS_PRESENT_WITH_GDI_ALL_SHOW
 IDS_FLAGS_PRESENT_WITH_GDI_DESCRIPTION
 IDS_FLAGS_PRESENT_WITH_GDI_FIRST_SHOW
diff --git a/build/isolate.gypi b/build/isolate.gypi
index 15fcf4c..76882c7 100644
--- a/build/isolate.gypi
+++ b/build/isolate.gypi
@@ -42,11 +42,8 @@
       'inputs': [
         # Files that are known to be involved in this step.
         '<(DEPTH)/tools/swarm_client/isolate.py',
-        '<(DEPTH)/tools/swarm_client/isolateserver_archive.py',
         '<(DEPTH)/tools/swarm_client/run_isolated.py',
         '<(DEPTH)/tools/swarm_client/googletest/run_test_cases.py',
-        '<(DEPTH)/tools/swarm_client/short_expression_finder.py',
-        '<(DEPTH)/tools/swarm_client/trace_inputs.py',
 
         # Disable file tracking by the build driver for now. This means the
         # project must have the proper build-time dependency for their runtime
diff --git a/build/java.gypi b/build/java.gypi
index 95858ee..2e03bab 100644
--- a/build/java.gypi
+++ b/build/java.gypi
@@ -78,6 +78,7 @@
     'variables': {
       'variables': {
         'proguard_preprocess%': 0,
+        'emma_never_instrument%': 0,
       },
       'conditions': [
         ['proguard_preprocess == 1', {
@@ -85,16 +86,15 @@
         }, {
           'javac_jar_path': '<(jar_path)'
         }],
+        ['chromium_code != 0 and emma_coverage != 0 and emma_never_instrument == 0', {
+          'emma_instrument': 1,
+        }, {
+          'emma_instrument': 0,
+        }],
       ],
     },
+    'emma_instrument': '<(emma_instrument)',
     'javac_jar_path': '<(javac_jar_path)',
-    'conditions': [
-      ['chromium_code != 0 and emma_coverage != 0', {
-        'emma_instrument': 1,
-      }, {
-        'emma_instrument': 0,
-      }],
-    ],
   },
   # This all_dependent_settings is used for java targets only. This will add the
   # jar path to the classpath of dependent java targets.
diff --git a/build/util/LASTCHANGE b/build/util/LASTCHANGE
index 9422be6..98744d2 100644
--- a/build/util/LASTCHANGE
+++ b/build/util/LASTCHANGE
@@ -1 +1 @@
-LASTCHANGE=219274
+LASTCHANGE=220549
diff --git a/build/util/LASTCHANGE.blink b/build/util/LASTCHANGE.blink
index d36e8c4..6fc2f63 100644
--- a/build/util/LASTCHANGE.blink
+++ b/build/util/LASTCHANGE.blink
@@ -1 +1 @@
-LASTCHANGE=156598
+LASTCHANGE=156939
diff --git a/cc/animation/transform_operation.cc b/cc/animation/transform_operation.cc
index b7bb063..ea3b0b2 100644
--- a/cc/animation/transform_operation.cc
+++ b/cc/animation/transform_operation.cc
@@ -6,6 +6,7 @@
 #include <cmath>
 #include <limits>
 
+#include "base/logging.h"
 #include "cc/animation/transform_operation.h"
 #include "ui/gfx/box_f.h"
 #include "ui/gfx/vector3d_f.h"
@@ -293,9 +294,14 @@
     case TransformOperation::TransformOperationIdentity:
       *bounds = box;
       return true;
-    default:
+    case TransformOperation::TransformOperationRotate:
+    case TransformOperation::TransformOperationSkew:
+    case TransformOperation::TransformOperationPerspective:
+    case TransformOperation::TransformOperationMatrix:
       return false;
   }
+  NOTREACHED();
+  return false;
 }
 
 }  // namespace cc
diff --git a/cc/base/math_util.cc b/cc/base/math_util.cc
index fa7b21a..919a172 100644
--- a/cc/base/math_util.cc
+++ b/cc/base/math_util.cc
@@ -446,6 +446,26 @@
   return h.CartesianPoint2d();
 }
 
+gfx::RectF MathUtil::ScaleRectProportional(const gfx::RectF& input_outer_rect,
+                                           const gfx::RectF& scale_outer_rect,
+                                           const gfx::RectF& scale_inner_rect) {
+  gfx::RectF output_inner_rect = input_outer_rect;
+  float scale_rect_to_input_scale_x =
+      scale_outer_rect.width() / input_outer_rect.width();
+  float scale_rect_to_input_scale_y =
+      scale_outer_rect.height() / input_outer_rect.height();
+
+  gfx::Vector2dF top_left_diff =
+      scale_inner_rect.origin() - scale_outer_rect.origin();
+  gfx::Vector2dF bottom_right_diff =
+      scale_inner_rect.bottom_right() - scale_outer_rect.bottom_right();
+  output_inner_rect.Inset(top_left_diff.x() / scale_rect_to_input_scale_x,
+                          top_left_diff.y() / scale_rect_to_input_scale_y,
+                          -bottom_right_diff.x() / scale_rect_to_input_scale_x,
+                          -bottom_right_diff.y() / scale_rect_to_input_scale_y);
+  return output_inner_rect;
+}
+
 static inline float ScaleOnAxis(double a, double b, double c) {
   return std::sqrt(a * a + b * b + c * c);
 }
diff --git a/cc/base/math_util.h b/cc/base/math_util.h
index 37f0c07..6cf929c 100644
--- a/cc/base/math_util.h
+++ b/cc/base/math_util.h
@@ -144,6 +144,15 @@
   static gfx::Vector2dF ComputeTransform2dScaleComponents(const gfx::Transform&,
                                                           float fallbackValue);
 
+  // Makes a rect that has the same relationship to input_outer_rect as
+  // scale_inner_rect has to scale_outer_rect. scale_inner_rect should be
+  // contained within scale_outer_rect, and likewise the rectangle that is
+  // returned will be within input_outer_rect at a similar relative, scaled
+  // position.
+  static gfx::RectF ScaleRectProportional(const gfx::RectF& input_outer_rect,
+                                          const gfx::RectF& scale_outer_rect,
+                                          const gfx::RectF& scale_inner_rect);
+
   // Returns the smallest angle between the given two vectors in degrees.
   // Neither vector is assumed to be normalized.
   static float SmallestAngleBetweenVectors(gfx::Vector2dF v1,
diff --git a/cc/base/scoped_ptr_hash_map.h b/cc/base/scoped_ptr_hash_map.h
deleted file mode 100644
index 4713582..0000000
--- a/cc/base/scoped_ptr_hash_map.h
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CC_BASE_SCOPED_PTR_HASH_MAP_H_
-#define CC_BASE_SCOPED_PTR_HASH_MAP_H_
-
-#include <algorithm>
-#include <utility>
-
-#include "base/basictypes.h"
-#include "base/containers/hash_tables.h"
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/stl_util.h"
-
-namespace cc {
-
-// This type acts like a hash_map<K, scoped_ptr<V> >, based on top of
-// base::hash_map. The ScopedPtrHashMap has ownership of all values in the data
-// structure.
-template <typename Key, typename Value>
-class ScopedPtrHashMap {
-  typedef base::hash_map<Key, Value*> Container;
-
- public:
-  typedef typename Container::iterator iterator;
-  typedef typename Container::const_iterator const_iterator;
-
-  ScopedPtrHashMap() {}
-
-  ~ScopedPtrHashMap() { clear(); }
-
-  void swap(ScopedPtrHashMap<Key, Value>& other) {
-    data_.swap(other.data_);
-  }
-
-  std::pair<iterator, bool> insert(
-      std::pair<Key, const scoped_ptr<Value> > pair) {
-    return data_.insert(
-        std::pair<Key, Value*>(pair.first, pair.second.release()));
-  }
-
-  // Replaces value but not key if key is already present.
-  std::pair<iterator, bool> set(Key key, scoped_ptr<Value> data) {
-    iterator it = find(key);
-    if (it != end())
-      erase(it);
-    Value* raw_ptr = data.release();
-    return data_.insert(std::pair<Key, Value*>(key, raw_ptr));
-  }
-
-  // Does nothing if key is already present
-  std::pair<iterator, bool> add(Key key, scoped_ptr<Value> data) {
-    Value* raw_ptr = data.release();
-    return data_.insert(std::pair<Key, Value*>(key, raw_ptr));
-  }
-
-  void erase(iterator it) {
-    if (it->second)
-      delete it->second;
-    data_.erase(it);
-  }
-
-  size_t erase(const Key& k) {
-    iterator it = data_.find(k);
-    if (it == data_.end())
-      return 0;
-    erase(it);
-    return 1;
-  }
-
-  scoped_ptr<Value> take(iterator it) {
-    DCHECK(it != data_.end());
-    if (it == data_.end())
-      return scoped_ptr<Value>();
-
-    Key key = it->first;
-    scoped_ptr<Value> ret(it->second);
-    data_.erase(it);
-    data_.insert(std::pair<Key, Value*>(key, static_cast<Value*>(NULL)));
-    return ret.Pass();
-  }
-
-  scoped_ptr<Value> take(const Key& k) {
-    iterator it = find(k);
-    if (it == data_.end())
-      return scoped_ptr<Value>();
-
-    return take(it);
-  }
-
-  scoped_ptr<Value> take_and_erase(iterator it) {
-    DCHECK(it != data_.end());
-    if (it == data_.end())
-      return scoped_ptr<Value>();
-
-    scoped_ptr<Value> ret(it->second);
-    data_.erase(it);
-    return ret.Pass();
-  }
-
-  scoped_ptr<Value> take_and_erase(const Key& k) {
-    iterator it = find(k);
-    if (it == data_.end())
-      return scoped_ptr<Value>();
-
-    return take_and_erase(it);
-  }
-
-  // Returns the first element in the hash_map that matches the given key.
-  // If no such element exists it returns NULL.
-  Value* get(const Key& k) const {
-    const_iterator it = find(k);
-    if (it == end())
-      return 0;
-    return it->second;
-  }
-
-  inline bool contains(const Key& k) const { return data_.count(k) > 0; }
-
-  inline void clear() { STLDeleteValues(&data_); }
-
-  inline const_iterator find(const Key& k) const { return data_.find(k); }
-  inline iterator find(const Key& k) { return data_.find(k); }
-
-  inline size_t count(const Key& k) const { return data_.count(k); }
-  inline std::pair<const_iterator, const_iterator> equal_range(
-      const Key& k) const {
-    return data_.equal_range(k);
-  }
-  inline std::pair<iterator, iterator> equal_range(const Key& k) {
-    return data_.equal_range(k);
-  }
-
-  inline size_t size() const { return data_.size(); }
-  inline size_t max_size() const { return data_.max_size(); }
-
-  inline bool empty() const { return data_.empty(); }
-
-  inline size_t bucket_count() const { return data_.bucket_count(); }
-  inline void resize(size_t size) const { return data_.resize(size); }
-
-  inline iterator begin() { return data_.begin(); }
-  inline const_iterator begin() const { return data_.begin(); }
-  inline iterator end() { return data_.end(); }
-  inline const_iterator end() const { return data_.end(); }
-
- private:
-  Container data_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedPtrHashMap);
-};
-
-}  // namespace cc
-
-#endif  // CC_BASE_SCOPED_PTR_HASH_MAP_H_
diff --git a/cc/base/switches.cc b/cc/base/switches.cc
index 2dac58b..3fc4302 100644
--- a/cc/base/switches.cc
+++ b/cc/base/switches.cc
@@ -125,8 +125,12 @@
 const char kShowNonOccludingRects[] = "show-nonoccluding-rects";
 const char kUIShowNonOccludingRects[] = "ui-show-nonoccluding-rects";
 
-// Enable the codepath that uses images within TileManager.
-const char kUseMapImage[] = "use-map-image";
+// Enable rasterizer that writes directly to GPU memory.
+const char kEnableMapImage[] = "enable-map-image";
+
+// Disable rasterizer that writes directly to GPU memory.
+// Overrides the kEnableMapImage flag.
+const char kDisableMapImage[] = "disable-map-image";
 
 // Prevents the layer tree unit tests from timing out.
 const char kCCLayerTreeTestNoTimeout[] = "cc-layer-tree-test-no-timeout";
@@ -146,5 +150,16 @@
 #endif
 }
 
+bool IsMapImageEnabled() {
+  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+
+  if (command_line.HasSwitch(cc::switches::kDisableMapImage))
+    return false;
+  else if (command_line.HasSwitch(cc::switches::kEnableMapImage))
+    return true;
+
+  return false;
+}
+
 }  // namespace switches
 }  // namespace cc
diff --git a/cc/base/switches.h b/cc/base/switches.h
index f87eb53..5a84e15 100644
--- a/cc/base/switches.h
+++ b/cc/base/switches.h
@@ -37,7 +37,8 @@
 CC_EXPORT extern const char kEnablePinchVirtualViewport[];
 CC_EXPORT extern const char kEnablePartialSwap[];
 CC_EXPORT extern const char kStrictLayerPropertyChangeChecking[];
-CC_EXPORT extern const char kUseMapImage[];
+CC_EXPORT extern const char kEnableMapImage[];
+CC_EXPORT extern const char kDisableMapImage[];
 
 // Switches for both the renderer and ui compositors.
 CC_EXPORT extern const char kUIDisablePartialSwap[];
@@ -66,6 +67,7 @@
 CC_EXPORT extern const char kCCLayerTreeTestNoTimeout[];
 
 CC_EXPORT bool IsImplSidePaintingEnabled();
+CC_EXPORT bool IsMapImageEnabled();
 
 }  // namespace switches
 }  // namespace cc
diff --git a/cc/cc.gyp b/cc/cc.gyp
index c6f3355..b6a01cc 100644
--- a/cc/cc.gyp
+++ b/cc/cc.gyp
@@ -14,11 +14,11 @@
         '<(DEPTH)/base/base.gyp:base',
         '<(DEPTH)/base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
         '<(DEPTH)/gpu/gpu.gyp:gpu',
-        '<(DEPTH)/skia/skia.gyp:skia',
         '<(DEPTH)/media/media.gyp:media',
+        '<(DEPTH)/skia/skia.gyp:skia',
+        '<(DEPTH)/third_party/WebKit/public/blink.gyp:blink_minimal',
         '<(DEPTH)/ui/gl/gl.gyp:gl',
         '<(DEPTH)/ui/ui.gyp:ui',
-        '<(DEPTH)/third_party/WebKit/public/blink.gyp:blink_minimal',
       ],
       'defines': [
         'CC_IMPLEMENTATION=1',
@@ -59,7 +59,6 @@
         'base/region.h',
         'base/scoped_ptr_algorithm.h',
         'base/scoped_ptr_deque.h',
-        'base/scoped_ptr_hash_map.h',
         'base/scoped_ptr_vector.h',
         'base/switches.cc',
         'base/switches.h',
@@ -185,6 +184,7 @@
         'output/compositor_frame_ack.h',
         'output/compositor_frame_metadata.cc',
         'output/compositor_frame_metadata.h',
+        'output/context_provider.cc',
         'output/context_provider.h',
         'output/copy_output_request.cc',
         'output/copy_output_request.h',
@@ -360,6 +360,8 @@
         'scheduler/texture_uploader.cc',
         'scheduler/texture_uploader.h',
         'scheduler/time_source.h',
+        'trees/blocking_task_runner.cc',
+        'trees/blocking_task_runner.h',
         'trees/damage_tracker.cc',
         'trees/damage_tracker.h',
         'trees/layer_sorter.cc',
diff --git a/cc/cc.target.darwin-arm.mk b/cc/cc.target.darwin-arm.mk
index 8ef4d17..aef85e5 100644
--- a/cc/cc.target.darwin-arm.mk
+++ b/cc/cc.target.darwin-arm.mk
@@ -14,9 +14,9 @@
 	$(call intermediates-dir-for,GYP,gpu_gpu_gyp)/gpu.stamp \
 	$(call intermediates-dir-for,GYP,skia_skia_gyp)/skia.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_library_gyp)/skia_skia_library_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a
 
 GYP_GENERATED_OUTPUTS :=
 
@@ -97,6 +97,7 @@
 	cc/output/compositor_frame.cc \
 	cc/output/compositor_frame_ack.cc \
 	cc/output/compositor_frame_metadata.cc \
+	cc/output/context_provider.cc \
 	cc/output/copy_output_request.cc \
 	cc/output/copy_output_result.cc \
 	cc/output/delegated_frame_data.cc \
@@ -181,6 +182,7 @@
 	cc/scheduler/scheduler_settings.cc \
 	cc/scheduler/scheduler_state_machine.cc \
 	cc/scheduler/texture_uploader.cc \
+	cc/trees/blocking_task_runner.cc \
 	cc/trees/damage_tracker.cc \
 	cc/trees/layer_sorter.cc \
 	cc/trees/layer_tree_host.cc \
@@ -245,6 +247,7 @@
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
 	'-DCC_IMPLEMENTATION=1' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
 	'-DGR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"' \
@@ -252,7 +255,6 @@
 	'-DUSE_CHROMIUM_SKIA' \
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-DPOSIX_AVOID_MMAP' \
 	'-DU_USING_ICU_NAMESPACE=0' \
@@ -289,11 +291,11 @@
 	$(LOCAL_PATH)/third_party/skia/include/ports \
 	$(LOCAL_PATH)/third_party/skia/include/utils \
 	$(LOCAL_PATH)/skia/ext \
+	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(LOCAL_PATH)/v8/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -360,6 +362,7 @@
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
 	'-DCC_IMPLEMENTATION=1' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
 	'-DGR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"' \
@@ -367,7 +370,6 @@
 	'-DUSE_CHROMIUM_SKIA' \
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-DPOSIX_AVOID_MMAP' \
 	'-DU_USING_ICU_NAMESPACE=0' \
@@ -405,11 +407,11 @@
 	$(LOCAL_PATH)/third_party/skia/include/ports \
 	$(LOCAL_PATH)/third_party/skia/include/utils \
 	$(LOCAL_PATH)/skia/ext \
+	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(LOCAL_PATH)/v8/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/cc/cc.target.darwin-mips.mk b/cc/cc.target.darwin-mips.mk
index 6880650..ff5f0d5 100644
--- a/cc/cc.target.darwin-mips.mk
+++ b/cc/cc.target.darwin-mips.mk
@@ -14,9 +14,9 @@
 	$(call intermediates-dir-for,GYP,gpu_gpu_gyp)/gpu.stamp \
 	$(call intermediates-dir-for,GYP,skia_skia_gyp)/skia.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_library_gyp)/skia_skia_library_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a
 
 GYP_GENERATED_OUTPUTS :=
 
@@ -97,6 +97,7 @@
 	cc/output/compositor_frame.cc \
 	cc/output/compositor_frame_ack.cc \
 	cc/output/compositor_frame_metadata.cc \
+	cc/output/context_provider.cc \
 	cc/output/copy_output_request.cc \
 	cc/output/copy_output_result.cc \
 	cc/output/delegated_frame_data.cc \
@@ -181,6 +182,7 @@
 	cc/scheduler/scheduler_settings.cc \
 	cc/scheduler/scheduler_state_machine.cc \
 	cc/scheduler/texture_uploader.cc \
+	cc/trees/blocking_task_runner.cc \
 	cc/trees/damage_tracker.cc \
 	cc/trees/layer_sorter.cc \
 	cc/trees/layer_tree_host.cc \
@@ -244,6 +246,7 @@
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
 	'-DCC_IMPLEMENTATION=1' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
 	'-DGR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"' \
@@ -251,7 +254,6 @@
 	'-DUSE_CHROMIUM_SKIA' \
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-DPOSIX_AVOID_MMAP' \
 	'-DU_USING_ICU_NAMESPACE=0' \
@@ -288,11 +290,11 @@
 	$(LOCAL_PATH)/third_party/skia/include/ports \
 	$(LOCAL_PATH)/third_party/skia/include/utils \
 	$(LOCAL_PATH)/skia/ext \
+	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(LOCAL_PATH)/v8/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -358,6 +360,7 @@
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
 	'-DCC_IMPLEMENTATION=1' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
 	'-DGR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"' \
@@ -365,7 +368,6 @@
 	'-DUSE_CHROMIUM_SKIA' \
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-DPOSIX_AVOID_MMAP' \
 	'-DU_USING_ICU_NAMESPACE=0' \
@@ -403,11 +405,11 @@
 	$(LOCAL_PATH)/third_party/skia/include/ports \
 	$(LOCAL_PATH)/third_party/skia/include/utils \
 	$(LOCAL_PATH)/skia/ext \
+	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(LOCAL_PATH)/v8/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/cc/cc.target.darwin-x86.mk b/cc/cc.target.darwin-x86.mk
index 3e7af3c..7daa837 100644
--- a/cc/cc.target.darwin-x86.mk
+++ b/cc/cc.target.darwin-x86.mk
@@ -14,9 +14,9 @@
 	$(call intermediates-dir-for,GYP,gpu_gpu_gyp)/gpu.stamp \
 	$(call intermediates-dir-for,GYP,skia_skia_gyp)/skia.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_library_gyp)/skia_skia_library_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a
 
 GYP_GENERATED_OUTPUTS :=
 
@@ -97,6 +97,7 @@
 	cc/output/compositor_frame.cc \
 	cc/output/compositor_frame_ack.cc \
 	cc/output/compositor_frame_metadata.cc \
+	cc/output/context_provider.cc \
 	cc/output/copy_output_request.cc \
 	cc/output/copy_output_result.cc \
 	cc/output/delegated_frame_data.cc \
@@ -181,6 +182,7 @@
 	cc/scheduler/scheduler_settings.cc \
 	cc/scheduler/scheduler_state_machine.cc \
 	cc/scheduler/texture_uploader.cc \
+	cc/trees/blocking_task_runner.cc \
 	cc/trees/damage_tracker.cc \
 	cc/trees/layer_sorter.cc \
 	cc/trees/layer_tree_host.cc \
@@ -247,6 +249,7 @@
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
 	'-DCC_IMPLEMENTATION=1' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
 	'-DGR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"' \
@@ -254,7 +257,6 @@
 	'-DUSE_CHROMIUM_SKIA' \
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-DU_USING_ICU_NAMESPACE=0' \
 	'-D__STDC_CONSTANT_MACROS' \
@@ -290,11 +292,11 @@
 	$(LOCAL_PATH)/third_party/skia/include/ports \
 	$(LOCAL_PATH)/third_party/skia/include/utils \
 	$(LOCAL_PATH)/skia/ext \
+	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(LOCAL_PATH)/v8/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -364,6 +366,7 @@
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
 	'-DCC_IMPLEMENTATION=1' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
 	'-DGR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"' \
@@ -371,7 +374,6 @@
 	'-DUSE_CHROMIUM_SKIA' \
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-DU_USING_ICU_NAMESPACE=0' \
 	'-D__STDC_CONSTANT_MACROS' \
@@ -408,11 +410,11 @@
 	$(LOCAL_PATH)/third_party/skia/include/ports \
 	$(LOCAL_PATH)/third_party/skia/include/utils \
 	$(LOCAL_PATH)/skia/ext \
+	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(LOCAL_PATH)/v8/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/cc/cc.target.linux-arm.mk b/cc/cc.target.linux-arm.mk
index 8ef4d17..aef85e5 100644
--- a/cc/cc.target.linux-arm.mk
+++ b/cc/cc.target.linux-arm.mk
@@ -14,9 +14,9 @@
 	$(call intermediates-dir-for,GYP,gpu_gpu_gyp)/gpu.stamp \
 	$(call intermediates-dir-for,GYP,skia_skia_gyp)/skia.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_library_gyp)/skia_skia_library_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a
 
 GYP_GENERATED_OUTPUTS :=
 
@@ -97,6 +97,7 @@
 	cc/output/compositor_frame.cc \
 	cc/output/compositor_frame_ack.cc \
 	cc/output/compositor_frame_metadata.cc \
+	cc/output/context_provider.cc \
 	cc/output/copy_output_request.cc \
 	cc/output/copy_output_result.cc \
 	cc/output/delegated_frame_data.cc \
@@ -181,6 +182,7 @@
 	cc/scheduler/scheduler_settings.cc \
 	cc/scheduler/scheduler_state_machine.cc \
 	cc/scheduler/texture_uploader.cc \
+	cc/trees/blocking_task_runner.cc \
 	cc/trees/damage_tracker.cc \
 	cc/trees/layer_sorter.cc \
 	cc/trees/layer_tree_host.cc \
@@ -245,6 +247,7 @@
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
 	'-DCC_IMPLEMENTATION=1' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
 	'-DGR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"' \
@@ -252,7 +255,6 @@
 	'-DUSE_CHROMIUM_SKIA' \
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-DPOSIX_AVOID_MMAP' \
 	'-DU_USING_ICU_NAMESPACE=0' \
@@ -289,11 +291,11 @@
 	$(LOCAL_PATH)/third_party/skia/include/ports \
 	$(LOCAL_PATH)/third_party/skia/include/utils \
 	$(LOCAL_PATH)/skia/ext \
+	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(LOCAL_PATH)/v8/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -360,6 +362,7 @@
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
 	'-DCC_IMPLEMENTATION=1' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
 	'-DGR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"' \
@@ -367,7 +370,6 @@
 	'-DUSE_CHROMIUM_SKIA' \
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-DPOSIX_AVOID_MMAP' \
 	'-DU_USING_ICU_NAMESPACE=0' \
@@ -405,11 +407,11 @@
 	$(LOCAL_PATH)/third_party/skia/include/ports \
 	$(LOCAL_PATH)/third_party/skia/include/utils \
 	$(LOCAL_PATH)/skia/ext \
+	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(LOCAL_PATH)/v8/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/cc/cc.target.linux-mips.mk b/cc/cc.target.linux-mips.mk
index 6880650..ff5f0d5 100644
--- a/cc/cc.target.linux-mips.mk
+++ b/cc/cc.target.linux-mips.mk
@@ -14,9 +14,9 @@
 	$(call intermediates-dir-for,GYP,gpu_gpu_gyp)/gpu.stamp \
 	$(call intermediates-dir-for,GYP,skia_skia_gyp)/skia.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_library_gyp)/skia_skia_library_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a
 
 GYP_GENERATED_OUTPUTS :=
 
@@ -97,6 +97,7 @@
 	cc/output/compositor_frame.cc \
 	cc/output/compositor_frame_ack.cc \
 	cc/output/compositor_frame_metadata.cc \
+	cc/output/context_provider.cc \
 	cc/output/copy_output_request.cc \
 	cc/output/copy_output_result.cc \
 	cc/output/delegated_frame_data.cc \
@@ -181,6 +182,7 @@
 	cc/scheduler/scheduler_settings.cc \
 	cc/scheduler/scheduler_state_machine.cc \
 	cc/scheduler/texture_uploader.cc \
+	cc/trees/blocking_task_runner.cc \
 	cc/trees/damage_tracker.cc \
 	cc/trees/layer_sorter.cc \
 	cc/trees/layer_tree_host.cc \
@@ -244,6 +246,7 @@
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
 	'-DCC_IMPLEMENTATION=1' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
 	'-DGR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"' \
@@ -251,7 +254,6 @@
 	'-DUSE_CHROMIUM_SKIA' \
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-DPOSIX_AVOID_MMAP' \
 	'-DU_USING_ICU_NAMESPACE=0' \
@@ -288,11 +290,11 @@
 	$(LOCAL_PATH)/third_party/skia/include/ports \
 	$(LOCAL_PATH)/third_party/skia/include/utils \
 	$(LOCAL_PATH)/skia/ext \
+	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(LOCAL_PATH)/v8/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -358,6 +360,7 @@
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
 	'-DCC_IMPLEMENTATION=1' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
 	'-DGR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"' \
@@ -365,7 +368,6 @@
 	'-DUSE_CHROMIUM_SKIA' \
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-DPOSIX_AVOID_MMAP' \
 	'-DU_USING_ICU_NAMESPACE=0' \
@@ -403,11 +405,11 @@
 	$(LOCAL_PATH)/third_party/skia/include/ports \
 	$(LOCAL_PATH)/third_party/skia/include/utils \
 	$(LOCAL_PATH)/skia/ext \
+	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(LOCAL_PATH)/v8/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/cc/cc.target.linux-x86.mk b/cc/cc.target.linux-x86.mk
index 3e7af3c..7daa837 100644
--- a/cc/cc.target.linux-x86.mk
+++ b/cc/cc.target.linux-x86.mk
@@ -14,9 +14,9 @@
 	$(call intermediates-dir-for,GYP,gpu_gpu_gyp)/gpu.stamp \
 	$(call intermediates-dir-for,GYP,skia_skia_gyp)/skia.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_library_gyp)/skia_skia_library_gyp.a \
+	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
-	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
-	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp
+	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a
 
 GYP_GENERATED_OUTPUTS :=
 
@@ -97,6 +97,7 @@
 	cc/output/compositor_frame.cc \
 	cc/output/compositor_frame_ack.cc \
 	cc/output/compositor_frame_metadata.cc \
+	cc/output/context_provider.cc \
 	cc/output/copy_output_request.cc \
 	cc/output/copy_output_result.cc \
 	cc/output/delegated_frame_data.cc \
@@ -181,6 +182,7 @@
 	cc/scheduler/scheduler_settings.cc \
 	cc/scheduler/scheduler_state_machine.cc \
 	cc/scheduler/texture_uploader.cc \
+	cc/trees/blocking_task_runner.cc \
 	cc/trees/damage_tracker.cc \
 	cc/trees/layer_sorter.cc \
 	cc/trees/layer_tree_host.cc \
@@ -247,6 +249,7 @@
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
 	'-DCC_IMPLEMENTATION=1' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
 	'-DGR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"' \
@@ -254,7 +257,6 @@
 	'-DUSE_CHROMIUM_SKIA' \
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-DU_USING_ICU_NAMESPACE=0' \
 	'-D__STDC_CONSTANT_MACROS' \
@@ -290,11 +292,11 @@
 	$(LOCAL_PATH)/third_party/skia/include/ports \
 	$(LOCAL_PATH)/third_party/skia/include/utils \
 	$(LOCAL_PATH)/skia/ext \
+	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(LOCAL_PATH)/v8/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -364,6 +366,7 @@
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
 	'-DCC_IMPLEMENTATION=1' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
 	'-DGR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"' \
@@ -371,7 +374,6 @@
 	'-DUSE_CHROMIUM_SKIA' \
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-DU_USING_ICU_NAMESPACE=0' \
 	'-D__STDC_CONSTANT_MACROS' \
@@ -408,11 +410,11 @@
 	$(LOCAL_PATH)/third_party/skia/include/ports \
 	$(LOCAL_PATH)/third_party/skia/include/utils \
 	$(LOCAL_PATH)/skia/ext \
+	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(LOCAL_PATH)/v8/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp
index c84b11b..114b4a9 100644
--- a/cc/cc_tests.gyp
+++ b/cc/cc_tests.gyp
@@ -241,10 +241,12 @@
         'cc_test_support',
       ],
       'sources': [
+        'resources/picture_layer_tiling_perftest.cc',
         'resources/raster_worker_pool_perftest.cc',
         'resources/tile_manager_perftest.cc',
         'resources/worker_pool_perftest.cc',
         'test/cc_test_suite.cc',
+        'test/lap_timer.cc',
         'test/run_all_unittests.cc',
         'trees/layer_tree_host_perftest.cc',
       ],
diff --git a/cc/debug/frame_rate_counter.cc b/cc/debug/frame_rate_counter.cc
index 8a829ff..11632b1 100644
--- a/cc/debug/frame_rate_counter.cc
+++ b/cc/debug/frame_rate_counter.cc
@@ -44,7 +44,7 @@
 FrameRateCounter::FrameRateCounter(bool has_impl_thread)
     : has_impl_thread_(has_impl_thread), dropped_frame_count_(0) {}
 
-void FrameRateCounter::SaveTimeStamp(base::TimeTicks timestamp) {
+void FrameRateCounter::SaveTimeStamp(base::TimeTicks timestamp, bool software) {
   ring_buffer_.SaveToBuffer(timestamp);
 
   // Check if frame interval can be computed.
@@ -55,11 +55,20 @@
       RecentFrameInterval(ring_buffer_.BufferSize() - 1);
 
   if (has_impl_thread_ && ring_buffer_.CurrentIndex() > 0) {
-    UMA_HISTOGRAM_CUSTOM_COUNTS("Renderer4.CompositorThreadImplDrawDelay",
-                                frame_interval_seconds.InMilliseconds(),
-                                1,
-                                120,
-                                60);
+    if (software) {
+      UMA_HISTOGRAM_CUSTOM_COUNTS(
+          "Renderer4.SoftwareCompositorThreadImplDrawDelay",
+          frame_interval_seconds.InMilliseconds(),
+          1,
+          120,
+          60);
+    } else {
+      UMA_HISTOGRAM_CUSTOM_COUNTS("Renderer4.CompositorThreadImplDrawDelay",
+                                  frame_interval_seconds.InMilliseconds(),
+                                  1,
+                                  120,
+                                  60);
+    }
   }
 
   if (!IsBadFrameInterval(frame_interval_seconds) &&
diff --git a/cc/debug/frame_rate_counter.h b/cc/debug/frame_rate_counter.h
index 32b4c2c..0b69b29 100644
--- a/cc/debug/frame_rate_counter.h
+++ b/cc/debug/frame_rate_counter.h
@@ -22,7 +22,7 @@
   int dropped_frame_count() const { return dropped_frame_count_; }
   size_t time_stamp_history_size() const { return ring_buffer_.BufferSize(); }
 
-  void SaveTimeStamp(base::TimeTicks timestamp);
+  void SaveTimeStamp(base::TimeTicks timestamp, bool software);
 
   // n = 0 returns the oldest frame interval retained in the history, while n =
   // time_stamp_history_size() - 1 returns the most recent frame interval.
diff --git a/cc/debug/rendering_stats.cc b/cc/debug/rendering_stats.cc
index e6875ce..64cf69e 100644
--- a/cc/debug/rendering_stats.cc
+++ b/cc/debug/rendering_stats.cc
@@ -75,6 +75,13 @@
                         impl_stats.tile_analysis_time.InSecondsF());
 }
 
+void MainThreadRenderingStats::IssueTraceEvent() const {
+  TRACE_EVENT_INSTANT1("benchmark",
+                       "MainThreadRenderingStats::IssueTraceEvent",
+                       TRACE_EVENT_SCOPE_THREAD,
+                       "data", AsTraceableData());
+}
+
 scoped_ptr<base::debug::ConvertableToTraceFormat>
 MainThreadRenderingStats::AsTraceableData() const {
   scoped_ptr<base::DictionaryValue> record_data(new base::DictionaryValue());
@@ -96,9 +103,18 @@
                           recorded_pixel_count);
   record_data->SetInteger("image_gathering_count",
                           image_gathering_count);
+  record_data->SetDouble("image_gathering_time",
+                         image_gathering_time.InSecondsF());
   return TracedValue::FromValue(record_data.release());
 }
 
+void ImplThreadRenderingStats::IssueTraceEvent() const {
+  TRACE_EVENT_INSTANT1("benchmark",
+                       "ImplThreadRenderingStats::IssueTraceEvent",
+                       TRACE_EVENT_SCOPE_THREAD,
+                       "data", AsTraceableData());
+}
+
 scoped_ptr<base::debug::ConvertableToTraceFormat>
 ImplThreadRenderingStats::AsTraceableData() const {
   scoped_ptr<base::DictionaryValue> record_data(new base::DictionaryValue());
diff --git a/cc/debug/rendering_stats.h b/cc/debug/rendering_stats.h
index c3647d5..ee13706 100644
--- a/cc/debug/rendering_stats.h
+++ b/cc/debug/rendering_stats.h
@@ -44,6 +44,7 @@
   base::TimeDelta image_gathering_time;
 
   MainThreadRenderingStats();
+  void IssueTraceEvent() const;
   scoped_ptr<base::debug::ConvertableToTraceFormat> AsTraceableData() const;
   void Add(const MainThreadRenderingStats& other);
 };
@@ -70,6 +71,7 @@
   base::TimeDelta tile_analysis_time;
 
   ImplThreadRenderingStats();
+  void IssueTraceEvent() const;
   scoped_ptr<base::debug::ConvertableToTraceFormat> AsTraceableData() const;
   void Add(const ImplThreadRenderingStats& other);
 };
diff --git a/cc/debug/rendering_stats_instrumentation.cc b/cc/debug/rendering_stats_instrumentation.cc
index 3c164f9..2c5b252 100644
--- a/cc/debug/rendering_stats_instrumentation.cc
+++ b/cc/debug/rendering_stats_instrumentation.cc
@@ -59,12 +59,16 @@
   main_stats_.animation_frame_count++;
 }
 
-void RenderingStatsInstrumentation::IncrementScreenFrameCount(int64 count) {
+void RenderingStatsInstrumentation::IncrementScreenFrameCount(
+    int64 count, bool main_thread) {
   if (!record_rendering_stats_)
     return;
 
   base::AutoLock scoped_lock(lock_);
-  impl_stats_.screen_frame_count += count;
+  if (main_thread)
+    main_stats_.screen_frame_count += count;
+  else
+    impl_stats_.screen_frame_count += count;
 }
 
 void RenderingStatsInstrumentation::IncrementDroppedFrameCount(int64 count) {
diff --git a/cc/debug/rendering_stats_instrumentation.h b/cc/debug/rendering_stats_instrumentation.h
index a7f9b30..5f74f9e 100644
--- a/cc/debug/rendering_stats_instrumentation.h
+++ b/cc/debug/rendering_stats_instrumentation.h
@@ -30,12 +30,21 @@
   RenderingStats GetRenderingStats();
 
   // Add current main thread rendering stats to accumulator and
-  // clear current stats
+  // clear current stats.
   void AccumulateAndClearMainThreadStats();
   // Add current impl thread rendering stats to accumulator and
-  // clear current stats
+  // clear current stats.
   void AccumulateAndClearImplThreadStats();
 
+  // Issue trace event for current main thread rendering stats.
+  void IssueTraceEventForMainThreadStats() {
+    main_stats_.IssueTraceEvent();
+  }
+  // Issue trace event for current impl thread rendering stats.
+  void IssueTraceEventForImplThreadStats() {
+    impl_stats_.IssueTraceEvent();
+  }
+
   // Read and write access to the record_rendering_stats_ flag is not locked to
   // improve performance. The flag is commonly turned off and hardly changes
   // it's value during runtime.
@@ -49,7 +58,7 @@
   base::TimeDelta EndRecording(base::TimeTicks start_time) const;
 
   void IncrementAnimationFrameCount();
-  void IncrementScreenFrameCount(int64 count);
+  void IncrementScreenFrameCount(int64 count, bool main_thread);
   void IncrementDroppedFrameCount(int64 count);
 
   void AddCommit(base::TimeDelta duration);
diff --git a/cc/debug/test_context_provider.cc b/cc/debug/test_context_provider.cc
index 613ef53..0d6fc01 100644
--- a/cc/debug/test_context_provider.cc
+++ b/cc/debug/test_context_provider.cc
@@ -4,9 +4,13 @@
 
 #include "cc/debug/test_context_provider.h"
 
+#include <set>
+#include <vector>
+
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/logging.h"
+#include "base/strings/string_split.h"
 #include "cc/debug/test_web_graphics_context_3d.h"
 
 namespace cc {
@@ -122,6 +126,14 @@
   return true;
 }
 
+ContextProvider::Capabilities TestContextProvider::ContextCapabilities() {
+  DCHECK(context3d_);
+  DCHECK(bound_);
+  DCHECK(context_thread_checker_.CalledOnValidThread());
+
+  return context3d_->test_capabilities();
+}
+
 WebKit::WebGraphicsContext3D* TestContextProvider::Context3d() {
   DCHECK(context3d_);
   DCHECK(bound_);
diff --git a/cc/debug/test_context_provider.h b/cc/debug/test_context_provider.h
index af3e599..0be90af 100644
--- a/cc/debug/test_context_provider.h
+++ b/cc/debug/test_context_provider.h
@@ -30,6 +30,7 @@
       scoped_ptr<TestWebGraphicsContext3D> context);
 
   virtual bool BindToCurrentThread() OVERRIDE;
+  virtual Capabilities ContextCapabilities() OVERRIDE;
   virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE;
   virtual class GrContext* GrContext() OVERRIDE;
   virtual void VerifyContexts() OVERRIDE;
diff --git a/cc/debug/test_web_graphics_context_3d.cc b/cc/debug/test_web_graphics_context_3d.cc
index 11d1ed8..04027ae 100644
--- a/cc/debug/test_web_graphics_context_3d.cc
+++ b/cc/debug/test_web_graphics_context_3d.cc
@@ -61,9 +61,6 @@
 TestWebGraphicsContext3D::TestWebGraphicsContext3D()
     : FakeWebGraphicsContext3D(),
       context_id_(s_context_id++),
-      support_swapbuffers_complete_callback_(true),
-      have_extension_io_surface_(false),
-      have_extension_egl_image_(false),
       times_make_current_succeeds_(-1),
       times_bind_texture_succeeds_(-1),
       times_end_query_succeeds_(-1),
@@ -79,6 +76,7 @@
       bound_buffer_(0),
       weak_ptr_factory_(this) {
   CreateNamespace();
+  test_capabilities_.swapbuffers_complete_callback = true;
 }
 
 TestWebGraphicsContext3D::TestWebGraphicsContext3D(
@@ -86,9 +84,6 @@
     : FakeWebGraphicsContext3D(),
       context_id_(s_context_id++),
       attributes_(attributes),
-      support_swapbuffers_complete_callback_(true),
-      have_extension_io_surface_(false),
-      have_extension_egl_image_(false),
       times_make_current_succeeds_(-1),
       times_bind_texture_succeeds_(-1),
       times_end_query_succeeds_(-1),
@@ -104,6 +99,7 @@
       bound_buffer_(0),
       weak_ptr_factory_(this) {
   CreateNamespace();
+  test_capabilities_.swapbuffers_complete_callback = true;
 }
 
 void TestWebGraphicsContext3D::CreateNamespace() {
@@ -175,19 +171,7 @@
 }
 
 WebKit::WebString TestWebGraphicsContext3D::getString(WGC3Denum name) {
-  std::string string;
-
-  if (support_swapbuffers_complete_callback_)
-    string += "GL_CHROMIUM_swapbuffers_complete_callback";
-
-  if (name == GL_EXTENSIONS) {
-    if (have_extension_io_surface_)
-      string += " GL_CHROMIUM_iosurface GL_ARB_texture_rectangle";
-    if (have_extension_egl_image_)
-      string += " GL_OES_EGL_image_external";
-  }
-
-  return WebKit::WebString::fromUTF8(string.c_str());
+  return WebKit::WebString();
 }
 
 WGC3Dint TestWebGraphicsContext3D::getUniformLocation(
@@ -427,7 +411,7 @@
 
 void TestWebGraphicsContext3D::setSwapBuffersCompleteCallbackCHROMIUM(
     WebGraphicsSwapBuffersCompleteCallbackCHROMIUM* callback) {
-  if (support_swapbuffers_complete_callback_)
+  if (test_capabilities_.swapbuffers_complete_callback)
     swap_buffers_callback_ = callback;
 }
 
@@ -482,7 +466,7 @@
   DCHECK(buffer_id && buffer_id < namespace_->next_buffer_id);
   DCHECK_EQ(context_id, context_id_);
 
-  ScopedPtrHashMap<unsigned, Buffer>& buffers = namespace_->buffers;
+  base::ScopedPtrHashMap<unsigned, Buffer>& buffers = namespace_->buffers;
   if (buffers.count(bound_buffer_) == 0)
     buffers.set(bound_buffer_, make_scoped_ptr(new Buffer).Pass());
 
@@ -494,7 +478,7 @@
                                           const void* data,
                                           WebKit::WGC3Denum usage) {
   base::AutoLock lock(namespace_->lock);
-  ScopedPtrHashMap<unsigned, Buffer>& buffers = namespace_->buffers;
+  base::ScopedPtrHashMap<unsigned, Buffer>& buffers = namespace_->buffers;
   DCHECK_GT(buffers.count(bound_buffer_), 0u);
   DCHECK_EQ(target, buffers.get(bound_buffer_)->target);
   if (context_lost_) {
@@ -509,7 +493,7 @@
 void* TestWebGraphicsContext3D::mapBufferCHROMIUM(WebKit::WGC3Denum target,
                                                   WebKit::WGC3Denum access) {
   base::AutoLock lock(namespace_->lock);
-  ScopedPtrHashMap<unsigned, Buffer>& buffers = namespace_->buffers;
+  base::ScopedPtrHashMap<unsigned, Buffer>& buffers = namespace_->buffers;
   DCHECK_GT(buffers.count(bound_buffer_), 0u);
   DCHECK_EQ(target, buffers.get(bound_buffer_)->target);
   if (times_map_buffer_chromium_succeeds_ >= 0) {
@@ -524,7 +508,7 @@
 WebKit::WGC3Dboolean TestWebGraphicsContext3D::unmapBufferCHROMIUM(
     WebKit::WGC3Denum target) {
   base::AutoLock lock(namespace_->lock);
-  ScopedPtrHashMap<unsigned, Buffer>& buffers = namespace_->buffers;
+  base::ScopedPtrHashMap<unsigned, Buffer>& buffers = namespace_->buffers;
   DCHECK_GT(buffers.count(bound_buffer_), 0u);
   DCHECK_EQ(target, buffers.get(bound_buffer_)->target);
   buffers.get(bound_buffer_)->pixels.reset();
@@ -537,7 +521,7 @@
   DCHECK_EQ(GL_RGBA8_OES, static_cast<int>(internalformat));
   WebKit::WGC3Duint image_id = NextImageId();
   base::AutoLock lock(namespace_->lock);
-  ScopedPtrHashMap<unsigned, Image>& images = namespace_->images;
+  base::ScopedPtrHashMap<unsigned, Image>& images = namespace_->images;
   images.set(image_id, make_scoped_ptr(new Image).Pass());
   images.get(image_id)->pixels.reset(new uint8[width * height * 4]);
   return image_id;
@@ -565,7 +549,7 @@
 void* TestWebGraphicsContext3D::mapImageCHROMIUM(WebKit::WGC3Duint image_id,
                                                  WebKit::WGC3Denum access) {
   base::AutoLock lock(namespace_->lock);
-  ScopedPtrHashMap<unsigned, Image>& images = namespace_->images;
+  base::ScopedPtrHashMap<unsigned, Image>& images = namespace_->images;
   DCHECK_GT(images.count(image_id), 0u);
   if (times_map_image_chromium_succeeds_ >= 0) {
     if (!times_map_image_chromium_succeeds_) {
diff --git a/cc/debug/test_web_graphics_context_3d.h b/cc/debug/test_web_graphics_context_3d.h
index 5910d03..3dfc7ed 100644
--- a/cc/debug/test_web_graphics_context_3d.h
+++ b/cc/debug/test_web_graphics_context_3d.h
@@ -9,14 +9,15 @@
 
 #include "base/compiler_specific.h"
 #include "base/containers/hash_tables.h"
+#include "base/containers/scoped_ptr_hash_map.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/stl_util.h"
 #include "base/synchronization/lock.h"
 #include "cc/base/cc_export.h"
-#include "cc/base/scoped_ptr_hash_map.h"
 #include "cc/debug/fake_web_graphics_context_3d.h"
+#include "cc/output/context_provider.h"
 #include "third_party/khronos/GLES2/gl2.h"
 
 namespace cc {
@@ -141,6 +142,10 @@
       WebKit::WGC3Denum access);
   virtual void unmapImageCHROMIUM(WebKit::WGC3Duint image_id);
 
+  const ContextProvider::Capabilities& test_capabilities() const {
+    return test_capabilities_;
+  }
+
   // When set, MakeCurrent() will fail after this many times.
   void set_times_make_current_succeeds(int times) {
     times_make_current_succeeds_ = times;
@@ -174,13 +179,16 @@
   void ResetUsedTextures() { used_textures_.clear(); }
 
   void set_support_swapbuffers_complete_callback(bool support) {
-    support_swapbuffers_complete_callback_ = support;
+    test_capabilities_.swapbuffers_complete_callback = support;
   }
   void set_have_extension_io_surface(bool have) {
-    have_extension_io_surface_ = have;
+    test_capabilities_.iosurface = have;
   }
   void set_have_extension_egl_image(bool have) {
-    have_extension_egl_image_ = have;
+    test_capabilities_.egl_image_external = have;
+  }
+  void set_have_post_sub_buffer(bool have) {
+    test_capabilities_.post_sub_buffer = have;
   }
 
   // When this context is lost, all contexts in its share group are also lost.
@@ -228,8 +236,8 @@
     unsigned next_image_id;
     unsigned next_texture_id;
     std::vector<WebKit::WebGLId> textures;
-    ScopedPtrHashMap<unsigned, Buffer> buffers;
-    ScopedPtrHashMap<unsigned, Image> images;
+    base::ScopedPtrHashMap<unsigned, Buffer> buffers;
+    base::ScopedPtrHashMap<unsigned, Image> images;
 
    private:
     friend class base::RefCountedThreadSafe<Namespace>;
@@ -247,9 +255,7 @@
 
   unsigned context_id_;
   Attributes attributes_;
-  bool support_swapbuffers_complete_callback_;
-  bool have_extension_io_surface_;
-  bool have_extension_egl_image_;
+  ContextProvider::Capabilities test_capabilities_;
   int times_make_current_succeeds_;
   int times_bind_texture_succeeds_;
   int times_end_query_succeeds_;
diff --git a/cc/layers/delegated_renderer_layer.cc b/cc/layers/delegated_renderer_layer.cc
index 0ffa54a..1e1a8a0 100644
--- a/cc/layers/delegated_renderer_layer.cc
+++ b/cc/layers/delegated_renderer_layer.cc
@@ -29,6 +29,23 @@
       tree_impl, layer_id_).PassAs<LayerImpl>();
 }
 
+void DelegatedRendererLayer::SetLayerTreeHost(LayerTreeHost* host) {
+  if (layer_tree_host() == host) {
+    Layer::SetLayerTreeHost(host);
+    return;
+  }
+
+  if (!host) {
+    // The active frame needs to be removed from the active tree and resources
+    // returned before the commit is called complete.
+    // TODO(danakj): Don't need to do this if the last frame commited was empty
+    // or we never commited a frame with resources.
+    SetNextCommitWaitsForActivation();
+  }
+
+  Layer::SetLayerTreeHost(host);
+}
+
 bool DelegatedRendererLayer::DrawsContent() const {
   return Layer::DrawsContent() && !frame_size_.IsEmpty();
 }
@@ -83,6 +100,9 @@
     frame_size_ = gfx::Size();
   }
   SetNeedsCommit();
+  // The active frame needs to be replaced and resources returned before the
+  // commit is called complete.
+  SetNextCommitWaitsForActivation();
 }
 
 void DelegatedRendererLayer::TakeUnusedResourcesForChildCompositor(
@@ -93,12 +113,4 @@
   array->swap(unused_resources_for_child_compositor_);
 }
 
-bool DelegatedRendererLayer::BlocksPendingCommit() const {
-  // The active frame needs to be replaced and resources returned before the
-  // commit is called complete. This is true even whenever there may be
-  // resources to return, regardless of if the layer will draw in its new
-  // state.
-  return true;
-}
-
 }  // namespace cc
diff --git a/cc/layers/delegated_renderer_layer.h b/cc/layers/delegated_renderer_layer.h
index f16217b..afee475 100644
--- a/cc/layers/delegated_renderer_layer.h
+++ b/cc/layers/delegated_renderer_layer.h
@@ -21,6 +21,7 @@
 
   virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
       OVERRIDE;
+  virtual void SetLayerTreeHost(LayerTreeHost* host) OVERRIDE;
   virtual void PushPropertiesTo(LayerImpl* impl) OVERRIDE;
   virtual bool DrawsContent() const OVERRIDE;
 
@@ -36,8 +37,6 @@
   // compositor to the given array, so they can be given back to the child.
   void TakeUnusedResourcesForChildCompositor(ReturnedResourceArray* array);
 
-  virtual bool BlocksPendingCommit() const OVERRIDE;
-
  protected:
   explicit DelegatedRendererLayer(DelegatedRendererLayerClient* client);
   virtual ~DelegatedRendererLayer();
diff --git a/cc/layers/delegated_renderer_layer_impl.cc b/cc/layers/delegated_renderer_layer_impl.cc
index 426dcbb..17a0c1c 100644
--- a/cc/layers/delegated_renderer_layer_impl.cc
+++ b/cc/layers/delegated_renderer_layer_impl.cc
@@ -421,6 +421,7 @@
         if (render_target() == this) {
           DCHECK(!is_clipped());
           DCHECK(render_surface());
+          DCHECK_EQ(0, num_unclipped_descendants());
           output_shared_quad_state->clip_rect = MathUtil::MapClippedRect(
               delegated_frame_to_target_transform,
               output_shared_quad_state->clip_rect);
diff --git a/cc/layers/draw_properties.h b/cc/layers/draw_properties.h
index bb80dce..181d900 100644
--- a/cc/layers/draw_properties.h
+++ b/cc/layers/draw_properties.h
@@ -27,6 +27,7 @@
         contents_scale_x(1.f),
         contents_scale_y(1.f),
         num_descendants_that_draw_content(0),
+        num_unclipped_descendants(0),
         descendants_can_clip_selves(false),
         can_draw_directly_to_backbuffer(false),
         layer_or_descendant_has_copy_request(false) {}
@@ -88,6 +89,10 @@
   // Does not include this layer itself, only its children and descendants.
   int num_descendants_that_draw_content;
 
+  // Number of descendants with a clip parent that is our ancestor. NB - this
+  // does not include our clip children because they are clipped by us.
+  int num_unclipped_descendants;
+
   // If true, every descendant in the sub-tree can clip itself without the
   // need to use hardware sissoring or a new render target.
   bool descendants_can_clip_selves;
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 6b71982..e7cf755 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -152,14 +152,11 @@
   layer_tree_host_->SetNeedsFullTreeSync();
 }
 
-bool Layer::IsPropertyChangeAllowed() const {
+void Layer::SetNextCommitWaitsForActivation() {
   if (!layer_tree_host_)
-    return true;
+    return;
 
-  if (!layer_tree_host_->settings().strict_layer_property_change_checking)
-    return true;
-
-  return !layer_tree_host_->in_paint_layer_contents();
+  layer_tree_host_->SetNextCommitWaitsForActivation();
 }
 
 void Layer::SetNeedsPushProperties() {
@@ -187,6 +184,16 @@
       parent_->RemoveDependentNeedsPushProperties();
 }
 
+bool Layer::IsPropertyChangeAllowed() const {
+  if (!layer_tree_host_)
+    return true;
+
+  if (!layer_tree_host_->settings().strict_layer_property_change_checking)
+    return true;
+
+  return !layer_tree_host_->in_paint_layer_contents();
+}
+
 gfx::Rect Layer::LayerRectToContentRect(const gfx::RectF& layer_rect) const {
   gfx::RectF content_rect =
       gfx::ScaleRect(layer_rect, contents_scale_x(), contents_scale_y());
@@ -196,10 +203,6 @@
   return gfx::ToEnclosingRect(content_rect);
 }
 
-bool Layer::BlocksPendingCommit() const {
-  return false;
-}
-
 skia::RefPtr<SkPicture> Layer::GetPicture() const {
   return skia::RefPtr<SkPicture>();
 }
@@ -208,20 +211,6 @@
   return false;
 }
 
-bool Layer::BlocksPendingCommitRecursive() const {
-  if (BlocksPendingCommit())
-    return true;
-  if (mask_layer() && mask_layer()->BlocksPendingCommitRecursive())
-    return true;
-  if (replica_layer() && replica_layer()->BlocksPendingCommitRecursive())
-    return true;
-  for (size_t i = 0; i < children_.size(); ++i) {
-    if (children_[i]->BlocksPendingCommitRecursive())
-      return true;
-  }
-  return false;
-}
-
 void Layer::SetParent(Layer* layer) {
   DCHECK(!layer || !layer->HasAncestor(this));
 
@@ -856,7 +845,6 @@
   DCHECK(!(TransformIsAnimating() && layer->TransformIsAnimatingOnImplOnly()));
 
   layer->SetScrollable(scrollable_);
-  layer->SetScrollOffset(scroll_offset_);
   layer->SetMaxScrollOffset(max_scroll_offset_);
 
   LayerImpl* scroll_parent = NULL;
@@ -890,6 +878,17 @@
     layer->SetClipChildren(clip_children);
   }
 
+  // Adjust the scroll delta to be just the scrolls that have happened since
+  // the begin frame was sent.  This happens for impl-side painting
+  // in LayerImpl::ApplyScrollDeltasSinceBeginFrame in a separate tree walk.
+  if (layer->layer_tree_impl()->settings().impl_side_painting) {
+    layer->SetScrollOffset(scroll_offset_);
+  } else {
+    layer->SetScrollOffsetAndDelta(
+        scroll_offset_, layer->ScrollDelta() - layer->sent_scroll_delta());
+    layer->SetSentScrollDelta(gfx::Vector2d());
+  }
+
   // Wrap the copy_requests_ in a PostTask to the main thread.
   ScopedPtrVector<CopyOutputRequest> main_thread_copy_requests;
   for (ScopedPtrVector<CopyOutputRequest>::iterator it = copy_requests_.begin();
@@ -917,14 +916,6 @@
   update_rect_.Union(layer->update_rect());
   layer->set_update_rect(update_rect_);
 
-  // Adjust the scroll delta to be just the scrolls that have happened since
-  // the begin frame was sent.  This happens for impl-side painting
-  // in LayerImpl::ApplyScrollDeltasSinceBeginFrame in a separate tree walk.
-  if (!layer->layer_tree_impl()->settings().impl_side_painting) {
-    layer->SetScrollDelta(layer->ScrollDelta() - layer->sent_scroll_delta());
-    layer->SetSentScrollDelta(gfx::Vector2d());
-  }
-
   layer->SetStackingOrderChanged(stacking_order_changed_);
 
   layer_animation_controller_->PushAnimationUpdatesTo(
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 5519651..7a8f9d1 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -236,6 +236,9 @@
   RenderSurface* render_surface() const {
     return draw_properties_.render_surface.get();
   }
+  int num_unclipped_descendants() const {
+    return draw_properties_.num_unclipped_descendants;
+  }
 
   void SetScrollOffset(gfx::Vector2d scroll_offset);
   gfx::Vector2d scroll_offset() const { return scroll_offset_; }
@@ -354,7 +357,8 @@
                                       float* contents_scale_y,
                                       gfx::Size* content_bounds);
 
-  LayerTreeHost* layer_tree_host() const { return layer_tree_host_; }
+  LayerTreeHost* layer_tree_host() { return layer_tree_host_; }
+  const LayerTreeHost* layer_tree_host() const { return layer_tree_host_; }
 
   // Set the priority of all desired textures in this layer.
   virtual void SetTexturePriorities(const PriorityCalculator& priority_calc) {}
@@ -393,13 +397,6 @@
 
   gfx::Rect LayerRectToContentRect(const gfx::RectF& layer_rect) const;
 
-  // In impl-side painting, this returns true if this layer type is not
-  // compatible with the main thread running freely, such as a double-buffered
-  // canvas that doesn't want to be triple-buffered across all three trees.
-  virtual bool BlocksPendingCommit() const;
-  // Returns true if anything in this tree blocksPendingCommit.
-  bool BlocksPendingCommitRecursive() const;
-
   virtual skia::RefPtr<SkPicture> GetPicture() const;
 
   virtual bool CanClipSelf() const;
@@ -451,7 +448,11 @@
   // Called when there's been a change in layer structure.  Implies both
   // SetNeedsUpdate and SetNeedsCommit, but not SetNeedsPushProperties.
   void SetNeedsFullTreeSync();
-  bool IsPropertyChangeAllowed() const;
+
+  // Called when the next commit should wait until the pending tree is activated
+  // before finishing the commit and unblocking the main thread. Used to ensure
+  // unused resources on the impl thread are returned before commit completes.
+  void SetNextCommitWaitsForActivation();
 
   void SetNeedsPushProperties();
   void AddDependentNeedsPushProperties();
@@ -460,6 +461,8 @@
     return needs_push_properties() || descendant_needs_push_properties();
   }
 
+  bool IsPropertyChangeAllowed() const;
+
   // If this layer has a scroll parent, it removes |this| from its list of
   // scroll children.
   void RemoveFromScrollTree();
@@ -538,7 +541,6 @@
   gfx::PointF position_;
   gfx::PointF anchor_point_;
   SkColor background_color_;
-  std::string debug_name_;
   CompositingReasons compositing_reasons_;
   float opacity_;
   skia::RefPtr<SkImageFilter> filter_;
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index d77f8f7..dc2bbca 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -140,6 +140,9 @@
   if (scroll_parent_ == parent)
     return;
 
+  // Having both a scroll parent and a scroll offset delegate is unsupported.
+  DCHECK(!scroll_offset_delegate_);
+
   if (scroll_parent_)
     scroll_parent_->RemoveScrollChild(this);
 
@@ -516,7 +519,10 @@
   layer->SetTransform(transform_);
 
   layer->SetScrollable(scrollable_);
-  layer->SetScrollOffset(scroll_offset_);
+  layer->SetScrollOffsetAndDelta(
+      scroll_offset_, layer->ScrollDelta() - layer->sent_scroll_delta());
+  layer->SetSentScrollDelta(gfx::Vector2d());
+
   layer->SetMaxScrollOffset(max_scroll_offset_);
 
   LayerImpl* scroll_parent = NULL;
@@ -556,9 +562,6 @@
   update_rect_.Union(layer->update_rect());
   layer->set_update_rect(update_rect_);
 
-  layer->SetScrollDelta(layer->ScrollDelta() - layer->sent_scroll_delta());
-  layer->SetSentScrollDelta(gfx::Vector2d());
-
   layer->SetStackingOrderChanged(stacking_order_changed_);
 
   // Reset any state that should be cleared for the next update.
@@ -988,6 +991,8 @@
 
 void LayerImpl::SetScrollOffsetDelegate(
     LayerScrollOffsetDelegate* scroll_offset_delegate) {
+  // Having both a scroll parent and a scroll offset delegate is unsupported.
+  DCHECK(!scroll_parent_);
   if (!scroll_offset_delegate && scroll_offset_delegate_) {
     scroll_delta_ =
         scroll_offset_delegate_->GetTotalScrollOffset() - scroll_offset_;
@@ -999,16 +1004,49 @@
 }
 
 void LayerImpl::SetScrollOffset(gfx::Vector2d scroll_offset) {
-  if (scroll_offset_ == scroll_offset)
-    return;
+  SetScrollOffsetAndDelta(scroll_offset, ScrollDelta());
+}
 
-  scroll_offset_ = scroll_offset;
+void LayerImpl::SetScrollOffsetAndDelta(gfx::Vector2d scroll_offset,
+                                        gfx::Vector2dF scroll_delta) {
+  bool changed = false;
 
-  if (scroll_offset_delegate_)
-    scroll_offset_delegate_->SetTotalScrollOffset(TotalScrollOffset());
+  if (scroll_offset_ != scroll_offset) {
+    changed = true;
+    scroll_offset_ = scroll_offset;
 
-  NoteLayerPropertyChangedForSubtree();
-  UpdateScrollbarPositions();
+    if (scroll_offset_delegate_)
+      scroll_offset_delegate_->SetTotalScrollOffset(TotalScrollOffset());
+  }
+
+  if (ScrollDelta() != scroll_delta) {
+    changed = true;
+    if (layer_tree_impl()->IsActiveTree()) {
+      LayerImpl* pending_twin =
+          layer_tree_impl()->FindPendingTreeLayerById(id());
+      if (pending_twin) {
+        // The pending twin can't mirror the scroll delta of the active
+        // layer.  Although the delta - sent scroll delta difference is
+        // identical for both twins, the sent scroll delta for the pending
+        // layer is zero, as anything that has been sent has been baked
+        // into the layer's position/scroll offset as a part of commit.
+        DCHECK(pending_twin->sent_scroll_delta().IsZero());
+        pending_twin->SetScrollDelta(scroll_delta - sent_scroll_delta());
+      }
+    }
+
+    if (scroll_offset_delegate_) {
+      scroll_offset_delegate_->SetTotalScrollOffset(scroll_offset_ +
+                                                    scroll_delta);
+    } else {
+      scroll_delta_ = scroll_delta;
+    }
+  }
+
+  if (changed) {
+    NoteLayerPropertyChangedForSubtree();
+    UpdateScrollbarPositions();
+  }
 }
 
 gfx::Vector2dF LayerImpl::ScrollDelta() const {
@@ -1018,31 +1056,7 @@
 }
 
 void LayerImpl::SetScrollDelta(gfx::Vector2dF scroll_delta) {
-  if (ScrollDelta() == scroll_delta)
-    return;
-
-  if (layer_tree_impl()->IsActiveTree()) {
-    LayerImpl* pending_twin = layer_tree_impl()->FindPendingTreeLayerById(id());
-    if (pending_twin) {
-      // The pending twin can't mirror the scroll delta of the active
-      // layer.  Although the delta - sent scroll delta difference is
-      // identical for both twins, the sent scroll delta for the pending
-      // layer is zero, as anything that has been sent has been baked
-      // into the layer's position/scroll offset as a part of commit.
-      DCHECK(pending_twin->sent_scroll_delta().IsZero());
-      pending_twin->SetScrollDelta(scroll_delta - sent_scroll_delta());
-    }
-  }
-
-  if (scroll_offset_delegate_) {
-    scroll_offset_delegate_->SetTotalScrollOffset(
-        scroll_offset_ + scroll_delta);
-  } else {
-    scroll_delta_ = scroll_delta;
-  }
-
-  NoteLayerPropertyChangedForSubtree();
-  UpdateScrollbarPositions();
+  SetScrollOffsetAndDelta(scroll_offset_, scroll_delta);
 }
 
 gfx::Vector2dF LayerImpl::TotalScrollOffset() const {
@@ -1084,24 +1098,37 @@
 }
 
 void LayerImpl::DidBecomeActive() {
-  if (!layer_tree_impl_->settings().use_linear_fade_scrollbar_animator)
+  if (layer_tree_impl_->settings().scrollbar_animator ==
+      LayerTreeSettings::NoAnimator) {
     return;
+  }
 
   bool need_scrollbar_animation_controller = horizontal_scrollbar_layer_ ||
                                              vertical_scrollbar_layer_;
-  if (need_scrollbar_animation_controller) {
-    if (!scrollbar_animation_controller_) {
-      base::TimeDelta fadeout_delay = base::TimeDelta::FromMilliseconds(
-          layer_tree_impl_->settings().scrollbar_linear_fade_delay_ms);
-      base::TimeDelta fadeout_length = base::TimeDelta::FromMilliseconds(
-          layer_tree_impl_->settings().scrollbar_linear_fade_length_ms);
-      scrollbar_animation_controller_ =
-          ScrollbarAnimationControllerLinearFade::Create(
-              this, fadeout_delay, fadeout_length)
-              .PassAs<ScrollbarAnimationController>();
-    }
-  } else {
+  if (!need_scrollbar_animation_controller) {
     scrollbar_animation_controller_.reset();
+    return;
+  }
+
+  if (scrollbar_animation_controller_)
+    return;
+
+  switch (layer_tree_impl_->settings().scrollbar_animator) {
+  case LayerTreeSettings::LinearFade: {
+    base::TimeDelta fadeout_delay = base::TimeDelta::FromMilliseconds(
+        layer_tree_impl_->settings().scrollbar_linear_fade_delay_ms);
+    base::TimeDelta fadeout_length = base::TimeDelta::FromMilliseconds(
+        layer_tree_impl_->settings().scrollbar_linear_fade_length_ms);
+
+    scrollbar_animation_controller_ =
+        ScrollbarAnimationControllerLinearFade::Create(
+            this, fadeout_delay, fadeout_length)
+            .PassAs<ScrollbarAnimationController>();
+    break;
+  }
+  case LayerTreeSettings::NoAnimator:
+    NOTREACHED();
+    break;
   }
 }
 void LayerImpl::SetHorizontalScrollbarLayer(
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index cdb6526..dc7508d 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -317,6 +317,9 @@
   RenderSurfaceImpl* render_surface() const {
     return draw_properties_.render_surface.get();
   }
+  int num_unclipped_descendants() const {
+    return draw_properties_.num_unclipped_descendants;
+  }
 
   // The client should be responsible for setting bounds, content bounds and
   // contents scale to appropriate values. LayerImpl doesn't calculate any of
@@ -343,6 +346,8 @@
   void SetScrollOffsetDelegate(
       LayerScrollOffsetDelegate* scroll_offset_delegate);
   void SetScrollOffset(gfx::Vector2d scroll_offset);
+  void SetScrollOffsetAndDelta(gfx::Vector2d scroll_offset,
+                               gfx::Vector2dF scroll_delta);
   gfx::Vector2d scroll_offset() const { return scroll_offset_; }
 
   void SetMaxScrollOffset(gfx::Vector2d max_scroll_offset);
diff --git a/cc/layers/painted_scrollbar_layer.cc b/cc/layers/painted_scrollbar_layer.cc
index 8346d04..8b90250 100644
--- a/cc/layers/painted_scrollbar_layer.cc
+++ b/cc/layers/painted_scrollbar_layer.cc
@@ -125,11 +125,11 @@
   scrollbar_layer->SetThumbLength(thumb_length_);
   if (Orientation() == HORIZONTAL) {
     scrollbar_layer->SetTrackStart(
-        track_rect_.x() - scrollbar_->Location().x());
+        track_rect_.x() - location_.x());
     scrollbar_layer->SetTrackLength(track_rect_.width());
   } else {
     scrollbar_layer->SetTrackStart(
-        track_rect_.y() - scrollbar_->Location().y());
+        track_rect_.y() - location_.y());
     scrollbar_layer->SetTrackLength(track_rect_.height());
   }
 
@@ -186,6 +186,7 @@
 
 void PaintedScrollbarLayer::UpdateThumbAndTrackGeometry() {
   track_rect_ = scrollbar_->TrackRect();
+  location_ = scrollbar_->Location();
   if (scrollbar_->HasThumb()) {
     thumb_thickness_ = scrollbar_->ThumbThickness();
     thumb_length_ = scrollbar_->ThumbLength();
@@ -197,7 +198,7 @@
   UpdateThumbAndTrackGeometry();
 
   gfx::Rect scaled_track_rect = ScrollbarLayerRectToContentRect(
-      gfx::Rect(scrollbar_->Location(), bounds()));
+      gfx::Rect(location_, bounds()));
 
   if (layer_tree_host()->settings().solid_color_scrollbars ||
       track_rect_.IsEmpty() || scaled_track_rect.IsEmpty())
diff --git a/cc/layers/painted_scrollbar_layer.h b/cc/layers/painted_scrollbar_layer.h
index 48fb376..1e103d7 100644
--- a/cc/layers/painted_scrollbar_layer.h
+++ b/cc/layers/painted_scrollbar_layer.h
@@ -71,9 +71,13 @@
 
   scoped_ptr<Scrollbar> scrollbar_;
 
+  // Snapshot of properties taken in UpdateThumbAndTrackGeometry and used in
+  // PushPropertiesTo.
   int thumb_thickness_;
   int thumb_length_;
+  gfx::Point location_;
   gfx::Rect track_rect_;
+
   int scroll_layer_id_;
 
   scoped_ptr<ScopedUIResource> track_resource_;
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index 8b7ed9c..82eef41 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -148,11 +148,6 @@
 
   AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data);
 
-  bool clipped = false;
-  gfx::QuadF target_quad = MathUtil::MapQuad(
-      draw_transform(),
-      gfx::QuadF(rect),
-      &clipped);
   if (ShowDebugBorders()) {
     for (PictureLayerTilingSet::CoverageIterator iter(
         tilings_.get(), contents_scale_x(), rect, ideal_contents_scale_);
@@ -227,6 +222,7 @@
 
     const ManagedTileState::TileVersion& tile_version =
         iter->GetTileVersionForDrawing();
+    scoped_ptr<DrawQuad> draw_quad;
     switch (tile_version.mode()) {
       case ManagedTileState::TileVersion::RESOURCE_MODE: {
         gfx::RectF texture_rect = iter.texture_rect();
@@ -244,7 +240,7 @@
                      texture_rect,
                      iter.texture_size(),
                      tile_version.contents_swizzled());
-        quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+        draw_quad = quad.PassAs<DrawQuad>();
         break;
       }
       case ManagedTileState::TileVersion::PICTURE_PILE_MODE: {
@@ -267,7 +263,7 @@
                      iter->contents_scale(),
                      draw_direct_to_backbuffer,
                      pile_);
-        quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+        draw_quad = quad.PassAs<DrawQuad>();
         break;
       }
       case ManagedTileState::TileVersion::SOLID_COLOR_MODE: {
@@ -276,14 +272,15 @@
                      geometry_rect,
                      tile_version.get_solid_color(),
                      false);
-        quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+        draw_quad = quad.PassAs<DrawQuad>();
         break;
       }
-      default:
-        NOTREACHED();
     }
 
-    if (!seen_tilings.size() || seen_tilings.back() != iter.CurrentTiling())
+    DCHECK(draw_quad);
+    quad_sink->Append(draw_quad.Pass(), append_quads_data);
+
+    if (seen_tilings.empty() || seen_tilings.back() != iter.CurrentTiling())
       seen_tilings.push_back(iter.CurrentTiling());
   }
 
@@ -578,6 +575,8 @@
   } else {
     tilings_->RemoveAllTilings();
   }
+
+  SanityCheckTilingState();
 }
 
 void PictureLayerImpl::SyncTiling(
@@ -754,6 +753,7 @@
       break;
     }
   }
+  SanityCheckTilingState();
 }
 
 namespace {
@@ -837,12 +837,13 @@
       low_res != high_res)
     low_res = AddTiling(low_res_raster_contents_scale_);
 
-  if (high_res)
-    high_res->set_resolution(HIGH_RESOLUTION);
+  high_res->set_resolution(HIGH_RESOLUTION);
   if (low_res && low_res != high_res)
     low_res->set_resolution(LOW_RESOLUTION);
   else if (!low_res && previous_low_res)
     previous_low_res->set_resolution(LOW_RESOLUTION);
+
+  SanityCheckTilingState();
 }
 
 bool PictureLayerImpl::ShouldAdjustRasterScale(
@@ -955,6 +956,8 @@
       twin->RemoveTiling(to_remove[i]->contents_scale());
     tilings_->Remove(to_remove[i]);
   }
+
+  SanityCheckTilingState();
 }
 
 float PictureLayerImpl::MinimumContentsScale() const {
@@ -1010,6 +1013,22 @@
   return true;
 }
 
+void PictureLayerImpl::SanityCheckTilingState() const {
+  if (!DCHECK_IS_ON())
+    return;
+
+  if (!CanHaveTilings()) {
+    DCHECK_EQ(0u, tilings_->num_tilings());
+    return;
+  }
+  if (tilings_->num_tilings() == 0)
+    return;
+
+  // MarkVisibleResourcesAsRequired depends on having exactly 1 high res
+  // tiling to mark its tiles as being required for activation.
+  DCHECK_EQ(1, tilings_->NumHighResTilings());
+}
+
 void PictureLayerImpl::GetDebugBorderProperties(
     SkColor* color,
     float* width) const {
diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h
index 42efa72..26239ed 100644
--- a/cc/layers/picture_layer_impl.h
+++ b/cc/layers/picture_layer_impl.h
@@ -96,6 +96,7 @@
 
   bool CanHaveTilings() const;
   bool CanHaveTilingWithScale(float contents_scale) const;
+  void SanityCheckTilingState() const;
 
   virtual void GetDebugBorderProperties(
       SkColor* color, float* width) const OVERRIDE;
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc
index 56f6d50..09c438d 100644
--- a/cc/layers/picture_layer_impl_unittest.cc
+++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -20,7 +20,7 @@
 #include "cc/test/mock_quad_culler.h"
 #include "cc/trees/layer_tree_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
 #include "ui/gfx/rect_conversions.h"
 
 namespace cc {
@@ -28,7 +28,7 @@
 
 class MockCanvas : public SkCanvas {
  public:
-  explicit MockCanvas(SkDevice* device) : SkCanvas(device) {}
+  explicit MockCanvas(SkBaseDevice* device) : SkCanvas(device) {}
 
   virtual void drawRect(const SkRect& rect, const SkPaint& paint) OVERRIDE {
     // Capture calls before SkCanvas quickReject() kicks in.
@@ -180,7 +180,7 @@
 
     SkBitmap store;
     store.setConfig(SkBitmap::kNo_Config, 1000, 1000);
-    SkDevice device(store);
+    SkBitmapDevice device(store);
 
     std::vector<SkRect>::const_iterator rect_iter = rects.begin();
     for (tile_iter = tiles.begin(); tile_iter < tiles.end(); tile_iter++) {
diff --git a/cc/layers/texture_layer.cc b/cc/layers/texture_layer.cc
index 2759473..d0ab7d9 100644
--- a/cc/layers/texture_layer.cc
+++ b/cc/layers/texture_layer.cc
@@ -6,9 +6,10 @@
 
 #include "base/bind.h"
 #include "base/location.h"
-#include "base/message_loop/message_loop_proxy.h"
+#include "base/synchronization/lock.h"
 #include "cc/layers/texture_layer_client.h"
 #include "cc/layers/texture_layer_impl.h"
+#include "cc/trees/blocking_task_runner.h"
 #include "cc/trees/layer_tree_host.h"
 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
 
@@ -124,6 +125,9 @@
     layer_tree_host()->AcquireLayerTextures();
   texture_id_ = id;
   SetNeedsCommit();
+  // The texture id needs to be removed from the active tree before the
+  // commit is called complete.
+  SetNextCommitWaitsForActivation();
 }
 
 void TextureLayer::SetTextureMailbox(const TextureMailbox& mailbox) {
@@ -137,6 +141,9 @@
     holder_ref_.reset();
   needs_set_mailbox_ = true;
   SetNeedsCommit();
+  // The active frame needs to be replaced and the mailbox returned before the
+  // commit is called complete.
+  SetNextCommitWaitsForActivation();
 }
 
 void TextureLayer::WillModifyTexture() {
@@ -160,16 +167,24 @@
   }
 
   if (layer_tree_host()) {
-    if (texture_id_)
+    if (texture_id_) {
       layer_tree_host()->AcquireLayerTextures();
+      // The texture id needs to be removed from the active tree before the
+      // commit is called complete.
+      SetNextCommitWaitsForActivation();
+    }
     if (rate_limit_context_ && client_)
       layer_tree_host()->StopRateLimiter(client_->Context3d());
   }
   // If we're removed from the tree, the TextureLayerImpl will be destroyed, and
   // we will need to set the mailbox again on a new TextureLayerImpl the next
   // time we push.
-  if (!host && uses_mailbox_ && holder_ref_)
+  if (!host && uses_mailbox_ && holder_ref_) {
     needs_set_mailbox_ = true;
+    // The active frame needs to be replaced and the mailbox returned before the
+    // commit is called complete.
+    SetNextCommitWaitsForActivation();
+  }
   Layer::SetLayerTreeHost(host);
 }
 
@@ -195,6 +210,9 @@
           client_->Context3d()->getGraphicsResetStatusARB() != GL_NO_ERROR)
         texture_id_ = 0;
       updated = true;
+      // The texture id needs to be removed from the active tree before the
+      // commit is called complete.
+      SetNextCommitWaitsForActivation();
     }
   }
 
@@ -240,13 +258,6 @@
   return Region();
 }
 
-bool TextureLayer::BlocksPendingCommit() const {
-  // Double-buffered texture layers need to be blocked until they can be made
-  // triple-buffered.  Single-buffered layers already prevent draws, so
-  // can block too for simplicity.
-  return DrawsContent();
-}
-
 bool TextureLayer::CanClipSelf() const {
   return true;
 }
@@ -262,7 +273,7 @@
 }
 
 TextureLayer::MailboxHolder::MailboxHolder(const TextureMailbox& mailbox)
-    : message_loop_(base::MessageLoopProxy::current()),
+    : message_loop_(BlockingTaskRunner::current()),
       internal_references_(0),
       mailbox_(mailbox),
       sync_point_(mailbox.sync_point()),
@@ -280,6 +291,7 @@
 }
 
 void TextureLayer::MailboxHolder::Return(unsigned sync_point, bool is_lost) {
+  base::AutoLock lock(arguments_lock_);
   sync_point_ = sync_point;
   is_lost_ = is_lost;
 }
@@ -301,22 +313,15 @@
   DCHECK(message_loop_->BelongsToCurrentThread());
   if (!--internal_references_) {
     mailbox_.RunReleaseCallback(sync_point_, is_lost_);
-    mailbox_ = TextureMailbox();
+    DCHECK(mailbox_.callback().is_null());
   }
 }
 
-void TextureLayer::MailboxHolder::ReturnAndReleaseOnMainThread(
-    unsigned sync_point, bool is_lost) {
-  DCHECK(message_loop_->BelongsToCurrentThread());
-  Return(sync_point, is_lost);
-  InternalRelease();
-}
-
 void TextureLayer::MailboxHolder::ReturnAndReleaseOnImplThread(
     unsigned sync_point, bool is_lost) {
-  message_loop_->PostTask(FROM_HERE, base::Bind(
-      &MailboxHolder::ReturnAndReleaseOnMainThread,
-      this, sync_point, is_lost));
+  Return(sync_point, is_lost);
+  message_loop_->PostTask(FROM_HERE,
+                          base::Bind(&MailboxHolder::InternalRelease, this));
 }
 
 }  // namespace cc
diff --git a/cc/layers/texture_layer.h b/cc/layers/texture_layer.h
index 9e63b68..cb34cf5 100644
--- a/cc/layers/texture_layer.h
+++ b/cc/layers/texture_layer.h
@@ -8,23 +8,76 @@
 #include <string>
 
 #include "base/callback.h"
+#include "base/synchronization/lock.h"
 #include "cc/base/cc_export.h"
 #include "cc/layers/layer.h"
 #include "cc/resources/texture_mailbox.h"
 
 namespace WebKit { class WebGraphicsContext3D; }
 
-namespace base {
-class MessageLoopProxy;
-}
-
 namespace cc {
-
+class BlockingTaskRunner;
 class TextureLayerClient;
 
 // A Layer containing a the rendered output of a plugin instance.
 class CC_EXPORT TextureLayer : public Layer {
  public:
+  class CC_EXPORT MailboxHolder
+      : public base::RefCountedThreadSafe<MailboxHolder> {
+   public:
+    class CC_EXPORT MainThreadReference {
+     public:
+      explicit MainThreadReference(MailboxHolder* holder);
+      ~MainThreadReference();
+      MailboxHolder* holder() { return holder_.get(); }
+
+     private:
+      scoped_refptr<MailboxHolder> holder_;
+      DISALLOW_COPY_AND_ASSIGN(MainThreadReference);
+    };
+
+    const TextureMailbox& mailbox() const { return mailbox_; }
+    void Return(unsigned sync_point, bool is_lost);
+
+    // Gets a ReleaseCallback that can be called from another thread. Note: the
+    // caller must ensure the callback is called.
+    TextureMailbox::ReleaseCallback GetCallbackForImplThread();
+
+   protected:
+    friend class TextureLayer;
+
+    // Protected visiblity so only TextureLayer and unit tests can create these.
+    static scoped_ptr<MainThreadReference> Create(
+        const TextureMailbox& mailbox);
+    virtual ~MailboxHolder();
+
+   private:
+    friend class base::RefCountedThreadSafe<MailboxHolder>;
+    friend class MainThreadReference;
+    explicit MailboxHolder(const TextureMailbox& mailbox);
+
+    void InternalAddRef();
+    void InternalRelease();
+    void ReturnAndReleaseOnImplThread(unsigned sync_point, bool is_lost);
+
+    // This member is thread safe, and is accessed on main and impl threads.
+    const scoped_refptr<BlockingTaskRunner> message_loop_;
+
+    // These members are only accessed on the main thread, or on the impl thread
+    // during commit where the main thread is blocked.
+    unsigned internal_references_;
+    TextureMailbox mailbox_;
+
+    // This lock guards the sync_point_ and is_lost_ fields because they can be
+    // accessed on both the impl and main thread. We do this to ensure that the
+    // values of these fields are well-ordered such that the last call to
+    // ReturnAndReleaseOnImplThread() defines their values.
+    base::Lock arguments_lock_;
+    unsigned sync_point_;
+    bool is_lost_;
+    DISALLOW_COPY_AND_ASSIGN(MailboxHolder);
+  };
+
   // If this texture layer requires special preparation logic for each frame
   // driven by the compositor, pass in a non-nil client. Pass in a nil client
   // pointer if texture updates are driven by an external process.
@@ -67,6 +120,7 @@
   void SetRateLimitContext(bool rate_limit);
 
   // Code path for plugins which supply their own texture ID.
+  // DEPRECATED. DO NOT USE.
   void SetTextureId(unsigned texture_id);
 
   // Code path for plugins which supply their own mailbox.
@@ -83,7 +137,6 @@
                       const OcclusionTracker* occlusion) OVERRIDE;
   virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
   virtual Region VisibleContentOpaqueRegion() const OVERRIDE;
-  virtual bool BlocksPendingCommit() const OVERRIDE;
 
   virtual bool CanClipSelf() const OVERRIDE;
 
@@ -92,50 +145,6 @@
   virtual ~TextureLayer();
 
  private:
-  class MailboxHolder : public base::RefCountedThreadSafe<MailboxHolder> {
-   public:
-    class MainThreadReference {
-     public:
-      explicit MainThreadReference(MailboxHolder* holder);
-      ~MainThreadReference();
-      MailboxHolder* holder() { return holder_.get(); }
-
-     private:
-      scoped_refptr<MailboxHolder> holder_;
-      DISALLOW_COPY_AND_ASSIGN(MainThreadReference);
-    };
-
-    static scoped_ptr<MainThreadReference> Create(
-        const TextureMailbox& mailbox);
-
-    const TextureMailbox& mailbox() const { return mailbox_; }
-    void Return(unsigned sync_point, bool is_lost);
-
-    // Gets a ReleaseCallback that can be called from another thread. Note: the
-    // caller must ensure the callback is called.
-    TextureMailbox::ReleaseCallback GetCallbackForImplThread();
-
-   private:
-    friend class base::RefCountedThreadSafe<MailboxHolder>;
-    friend class MainThreadReference;
-    explicit MailboxHolder(const TextureMailbox& mailbox);
-    ~MailboxHolder();
-    void InternalAddRef();
-    void InternalRelease();
-    void ReturnAndReleaseOnMainThread(unsigned sync_point, bool is_lost);
-    void ReturnAndReleaseOnImplThread(unsigned sync_point, bool is_lost);
-
-    // Thread safety notes: except for the thread-safe message_loop_, all fields
-    // are only used on the main thread, or on the impl thread during commit
-    // where the main thread is blocked.
-    const scoped_refptr<base::MessageLoopProxy> message_loop_;
-    unsigned internal_references_;
-    TextureMailbox mailbox_;
-    unsigned sync_point_;
-    bool is_lost_;
-    DISALLOW_COPY_AND_ASSIGN(MailboxHolder);
-  };
-
   TextureLayerClient* client_;
   bool uses_mailbox_;
 
diff --git a/cc/layers/texture_layer_unittest.cc b/cc/layers/texture_layer_unittest.cc
index 8fb2a15..a3c568f 100644
--- a/cc/layers/texture_layer_unittest.cc
+++ b/cc/layers/texture_layer_unittest.cc
@@ -7,6 +7,9 @@
 #include <string>
 
 #include "base/callback.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
 #include "cc/debug/test_web_graphics_context_3d.h"
 #include "cc/layers/texture_layer_client.h"
 #include "cc/layers/texture_layer_impl.h"
@@ -17,6 +20,7 @@
 #include "cc/test/fake_output_surface.h"
 #include "cc/test/layer_test_common.h"
 #include "cc/test/layer_tree_test.h"
+#include "cc/trees/blocking_task_runner.h"
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/layer_tree_impl.h"
 #include "cc/trees/single_thread_proxy.h"
@@ -358,6 +362,14 @@
   scoped_ptr<base::SharedMemory> shared_memory_;
 };
 
+class TestMailboxHolder : public TextureLayer::MailboxHolder {
+ public:
+  using TextureLayer::MailboxHolder::Create;
+
+ protected:
+  virtual ~TestMailboxHolder() {}
+};
+
 class TextureLayerWithMailboxTest : public TextureLayerTest {
  protected:
   virtual void TearDown() {
@@ -427,6 +439,298 @@
   test_layer->SetTextureMailbox(test_data_.mailbox1_);
 }
 
+class TextureLayerMailboxHolderTest : public TextureLayerTest {
+ public:
+  TextureLayerMailboxHolderTest()
+      : main_thread_("MAIN") {
+    main_thread_.Start();
+  }
+
+  void Wait(const base::Thread& thread) {
+    bool manual_reset = false;
+    bool initially_signaled = false;
+    base::WaitableEvent event(manual_reset, initially_signaled);
+    thread.message_loop()->PostTask(
+        FROM_HERE,
+        base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event)));
+    event.Wait();
+  }
+
+  void CreateMainRef() {
+    main_ref_ = TestMailboxHolder::Create(
+        test_data_.mailbox1_).Pass();
+  }
+
+  void ReleaseMainRef() {
+    main_ref_.reset();
+  }
+
+  void CreateImplRef(TextureMailbox::ReleaseCallback* impl_ref) {
+    *impl_ref = main_ref_->holder()->GetCallbackForImplThread();
+  }
+
+  void CapturePostTasksAndWait(base::WaitableEvent* begin_capture,
+                               base::WaitableEvent* wait_for_capture,
+                               base::WaitableEvent* stop_capture) {
+    begin_capture->Wait();
+    BlockingTaskRunner::CapturePostTasks capture;
+    wait_for_capture->Signal();
+    stop_capture->Wait();
+  }
+
+ protected:
+  scoped_ptr<TestMailboxHolder::MainThreadReference>
+      main_ref_;
+  base::Thread main_thread_;
+  CommonMailboxObjects test_data_;
+};
+
+TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_BothReleaseThenMain) {
+  scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL);
+  ASSERT_TRUE(test_layer.get());
+
+  main_thread_.message_loop()->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.
+  TextureMailbox::ReleaseCallback compositor1;
+  main_thread_.message_loop()->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.
+  TextureMailbox::ReleaseCallback compositor2;
+  main_thread_.message_loop()->PostTask(
+      FROM_HERE,
+      base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
+                 base::Unretained(this),
+                 &compositor2));
+
+  Wait(main_thread_);
+  Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+
+  // The compositors both destroy their impl trees before the main thread layer
+  // is destroyed.
+  compositor1.Run(100, false);
+  compositor2.Run(200, false);
+
+  Wait(main_thread_);
+
+  EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0);
+  Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+
+  // The main thread ref is the last one, so the mailbox is released back to the
+  // embedder, with the last sync point provided by the impl trees.
+  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)));
+  Wait(main_thread_);
+  Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+}
+
+TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_MainReleaseBetween) {
+  scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL);
+  ASSERT_TRUE(test_layer.get());
+
+  main_thread_.message_loop()->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.
+  TextureMailbox::ReleaseCallback compositor1;
+  main_thread_.message_loop()->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.
+  TextureMailbox::ReleaseCallback compositor2;
+  main_thread_.message_loop()->PostTask(
+      FROM_HERE,
+      base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
+                 base::Unretained(this),
+                 &compositor2));
+
+  Wait(main_thread_);
+  Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+
+  // One compositor destroys their impl tree.
+  compositor1.Run(100, false);
+
+  // Then the main thread reference is destroyed.
+  main_thread_.message_loop()->PostTask(
+      FROM_HERE,
+      base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef,
+                 base::Unretained(this)));
+
+  Wait(main_thread_);
+
+  EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0);
+  Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+
+  // The second impl reference is destroyed last, causing the mailbox to be
+  // released back to the embedder with the last sync point from the impl tree.
+  EXPECT_CALL(test_data_.mock_callback_,
+              Release(test_data_.mailbox_name1_, 200, true)).Times(1);
+
+  compositor2.Run(200, true);
+  Wait(main_thread_);
+  Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+}
+
+TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_MainReleasedFirst) {
+  scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL);
+  ASSERT_TRUE(test_layer.get());
+
+  main_thread_.message_loop()->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.
+  TextureMailbox::ReleaseCallback compositor1;
+  main_thread_.message_loop()->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.
+  TextureMailbox::ReleaseCallback compositor2;
+  main_thread_.message_loop()->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)));
+
+  // One compositor destroys their impl tree.
+  compositor2.Run(200, false);
+
+  Wait(main_thread_);
+
+  EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0);
+  Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+
+  // The second impl reference is destroyed last, causing the mailbox to be
+  // released back to the embedder with the last sync point from the impl tree.
+  EXPECT_CALL(test_data_.mock_callback_,
+              Release(test_data_.mailbox_name1_, 100, true)).Times(1);
+
+  compositor1.Run(100, true);
+  Wait(main_thread_);
+  Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+}
+
+TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_SecondImplRefShortcut) {
+  scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL);
+  ASSERT_TRUE(test_layer.get());
+
+  main_thread_.message_loop()->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.
+  TextureMailbox::ReleaseCallback compositor1;
+  main_thread_.message_loop()->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.
+  TextureMailbox::ReleaseCallback compositor2;
+  main_thread_.message_loop()->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)));
+
+  EXPECT_CALL(test_data_.mock_callback_,
+              Release(test_data_.mailbox_name1_, 200, true)).Times(1);
+
+  bool manual_reset = false;
+  bool initially_signaled = false;
+  base::WaitableEvent begin_capture(manual_reset, initially_signaled);
+  base::WaitableEvent wait_for_capture(manual_reset, initially_signaled);
+  base::WaitableEvent stop_capture(manual_reset, initially_signaled);
+
+  // 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(
+      FROM_HERE,
+      base::Bind(&TextureLayerMailboxHolderTest::CapturePostTasksAndWait,
+                 base::Unretained(this),
+                 &begin_capture,
+                 &wait_for_capture,
+                 &stop_capture));
+
+  // Before the main thread capturing starts, one compositor destroys their
+  // impl reference. Since capturing did not start, this gets post-tasked to
+  // the main thread.
+  compositor1.Run(100, false);
+
+  // Start capturing on the main thread.
+  begin_capture.Signal();
+  wait_for_capture.Wait();
+
+  // Meanwhile, the second compositor released its impl reference, but this task
+  // gets shortcutted directly to the main thread. This means the reference is
+  // released before compositor1, whose reference will be released later when
+  // the post-task is serviced. But since it was destroyed _on the impl thread_
+  // last, its sync point values should be used.
+  compositor2.Run(200, true);
+
+  stop_capture.Signal();
+  Wait(main_thread_);
+
+  Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+}
+
 class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest {
  public:
   TextureLayerImplWithMailboxThreadedCallback()
@@ -435,12 +739,13 @@
 
   // Make sure callback is received on main and doesn't block the impl thread.
   void ReleaseCallback(unsigned sync_point, bool lost_resource) {
-    EXPECT_EQ(true, proxy()->IsMainThread());
+    EXPECT_EQ(true, main_thread_.CalledOnValidThread());
     EXPECT_FALSE(lost_resource);
     ++callback_count_;
   }
 
   void SetMailbox(char mailbox_char) {
+    EXPECT_EQ(true, main_thread_.CalledOnValidThread());
     TextureMailbox mailbox(
         std::string(64, mailbox_char),
         base::Bind(
@@ -450,6 +755,8 @@
   }
 
   virtual void BeginTest() OVERRIDE {
+    EXPECT_EQ(true, main_thread_.CalledOnValidThread());
+
     gfx::Size bounds(100, 100);
     root_ = Layer::Create();
     root_->SetAnchorPoint(gfx::PointF());
@@ -484,58 +791,53 @@
         EXPECT_EQ(1, callback_count_);
         break;
       case 2:
-        // Old mailbox was released, task was posted, but won't execute
-        // until this DidCommit returns.
-        // TODO(piman): fix this.
-        EXPECT_EQ(1, callback_count_);
-        layer_tree_host()->SetNeedsCommit();
-        break;
-      case 3:
         EXPECT_EQ(2, callback_count_);
         // Case #3: change mailbox when the layer doesn't draw. The old
         // mailbox should be released during the next commit.
         layer_->SetBounds(gfx::Size());
         SetMailbox('4');
         break;
-      case 4:
-        // Old mailbox was released, task was posted, but won't execute
-        // until this DidCommit returns.
-        // TODO(piman): fix this.
-        EXPECT_EQ(2, callback_count_);
-        layer_tree_host()->SetNeedsCommit();
-        break;
-      case 5:
+      case 3:
         EXPECT_EQ(3, callback_count_);
         // Case #4: release mailbox that was committed but never drawn. The
         // old mailbox should be released during the next commit.
         layer_->SetTextureMailbox(TextureMailbox());
         break;
-      case 6:
-        // Old mailbox was released, task was posted, but won't execute
-        // until this DidCommit returns.
-        // TODO(piman): fix this.
-        EXPECT_EQ(3, callback_count_);
-        layer_tree_host()->SetNeedsCommit();
-        break;
-      case 7:
+      case 4:
+        if (layer_tree_host()->settings().impl_side_painting) {
+          // With impl painting, the texture mailbox will still be on the impl
+          // thread when the commit finishes, because the layer is not drawble
+          // when it has no texture mailbox, and thus does not block the commit
+          // on activation. So, we wait for activation.
+          // TODO(danakj): fix this. crbug.com/277953
+          layer_tree_host()->SetNeedsCommit();
+          break;
+        } else {
+          ++commit_count_;
+        }
+      case 5:
         EXPECT_EQ(4, callback_count_);
         // Restore a mailbox for the next step.
         SetMailbox('5');
         break;
-      case 8:
+      case 6:
         // Case #5: remove layer from tree. Callback should *not* be called, the
         // mailbox is returned to the main thread.
         EXPECT_EQ(4, callback_count_);
         layer_->RemoveFromParent();
         break;
-      case 9:
-        // Mailbox was released to the main thread, task was posted, but won't
-        // execute until this DidCommit returns.
-        // TODO(piman): fix this.
-        EXPECT_EQ(4, callback_count_);
-        layer_tree_host()->SetNeedsCommit();
-        break;
-      case 10:
+      case 7:
+        if (layer_tree_host()->settings().impl_side_painting) {
+          // With impl painting, the texture mailbox will still be on the impl
+          // thread when the commit finishes, because the layer is not around to
+          // block the commit on activation anymore. So, we wait for activation.
+          // TODO(danakj): fix this. crbug.com/277953
+          layer_tree_host()->SetNeedsCommit();
+          break;
+        } else {
+          ++commit_count_;
+        }
+      case 8:
         EXPECT_EQ(4, callback_count_);
         // Resetting the mailbox will call the callback now.
         layer_->SetTextureMailbox(TextureMailbox());
@@ -551,6 +853,7 @@
   virtual void AfterTest() OVERRIDE {}
 
  private:
+  base::ThreadChecker main_thread_;
   int callback_count_;
   int commit_count_;
   scoped_refptr<Layer> root_;
@@ -560,6 +863,245 @@
 SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(
     TextureLayerImplWithMailboxThreadedCallback);
 
+
+class TextureLayerNoMailboxIsActivatedDuringCommit : public LayerTreeTest,
+                                                     public TextureLayerClient {
+ protected:
+  TextureLayerNoMailboxIsActivatedDuringCommit()
+      : wait_thread_("WAIT"),
+        wait_event_(false, false) {
+    wait_thread_.Start();
+  }
+
+  virtual void BeginTest() OVERRIDE {
+    activate_count_ = 0;
+
+    gfx::Size bounds(100, 100);
+    root_ = Layer::Create();
+    root_->SetAnchorPoint(gfx::PointF());
+    root_->SetBounds(bounds);
+
+    layer_ = TextureLayer::Create(this);
+    layer_->SetIsDrawable(true);
+    layer_->SetAnchorPoint(gfx::PointF());
+    layer_->SetBounds(bounds);
+
+    root_->AddChild(layer_);
+    layer_tree_host()->SetRootLayer(root_);
+    layer_tree_host()->SetViewportSize(bounds);
+
+    PostSetNeedsCommitToMainThread();
+  }
+
+  // TextureLayerClient implementation.
+  virtual unsigned PrepareTexture() OVERRIDE {
+    return OffscreenContextProviderForMainThread()
+        ->Context3d()->createTexture();
+  }
+  virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE {
+    return OffscreenContextProviderForMainThread()->Context3d();
+  }
+  virtual bool PrepareTextureMailbox(TextureMailbox* mailbox,
+                                     bool use_shared_memory) OVERRIDE {
+    return false;
+  }
+
+  virtual void WillActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+    // Slow down activation so the main thread DidCommit() will run if
+    // not blocked.
+    wait_thread_.message_loop()->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&base::WaitableEvent::Signal,
+                   base::Unretained(&wait_event_)),
+        base::TimeDelta::FromMilliseconds(10));
+    wait_event_.Wait();
+
+    base::AutoLock lock(activate_lock_);
+    ++activate_count_;
+  }
+
+  virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+    // The main thread is awake now, and will run DidCommit() immediately.
+    // Run DidActivate() afterwards by posting it now.
+    proxy()->MainThreadTaskRunner()->PostTask(
+        FROM_HERE,
+        base::Bind(&TextureLayerNoMailboxIsActivatedDuringCommit::DidActivate,
+                   base::Unretained(this)));
+  }
+
+  void DidActivate() {
+    base::AutoLock lock(activate_lock_);
+    switch (activate_count_) {
+      case 1:
+        // The first texture has been activated. Invalidate the layer so it
+        // grabs a new texture id from the client.
+        layer_->SetNeedsDisplay();
+        // So this commit number should complete after the second activate.
+        EXPECT_EQ(1, layer_tree_host()->source_frame_number());
+        break;
+      case 2:
+        // The second mailbox has been activated. Remove the layer from
+        // the tree to cause another commit/activation. The commit should
+        // finish *after* the layer is removed from the active tree.
+        layer_->RemoveFromParent();
+        // So this commit number should complete after the third activate.
+        EXPECT_EQ(2, layer_tree_host()->source_frame_number());
+        break;
+      case 3:
+        EndTest();
+        break;
+    }
+  }
+
+  virtual void DidCommit() OVERRIDE {
+    switch (layer_tree_host()->source_frame_number()) {
+      case 2: {
+        // The activate for the 2nd texture should have happened before now.
+        base::AutoLock lock(activate_lock_);
+        EXPECT_EQ(2, activate_count_);
+        break;
+      }
+      case 3: {
+        // The activate to remove the layer should have happened before now.
+        base::AutoLock lock(activate_lock_);
+        EXPECT_EQ(3, activate_count_);
+        break;
+      }
+    }
+  }
+
+
+  virtual void AfterTest() OVERRIDE {}
+
+  base::Thread wait_thread_;
+  base::WaitableEvent wait_event_;
+  base::Lock activate_lock_;
+  int activate_count_;
+  int activate_commit_;
+  scoped_refptr<Layer> root_;
+  scoped_refptr<TextureLayer> layer_;
+};
+
+SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(
+    TextureLayerNoMailboxIsActivatedDuringCommit);
+
+class TextureLayerMailboxIsActivatedDuringCommit : public LayerTreeTest {
+ protected:
+  TextureLayerMailboxIsActivatedDuringCommit()
+      : wait_thread_("WAIT"),
+        wait_event_(false, false) {
+    wait_thread_.Start();
+  }
+
+  static void ReleaseCallback(unsigned sync_point, bool lost_resource) {}
+
+  void SetMailbox(char mailbox_char) {
+    TextureMailbox mailbox(
+        std::string(64, mailbox_char),
+        base::Bind(
+            &TextureLayerMailboxIsActivatedDuringCommit::ReleaseCallback));
+    layer_->SetTextureMailbox(mailbox);
+  }
+
+  virtual void BeginTest() OVERRIDE {
+    activate_count_ = 0;
+
+    gfx::Size bounds(100, 100);
+    root_ = Layer::Create();
+    root_->SetAnchorPoint(gfx::PointF());
+    root_->SetBounds(bounds);
+
+    layer_ = TextureLayer::CreateForMailbox(NULL);
+    layer_->SetIsDrawable(true);
+    layer_->SetAnchorPoint(gfx::PointF());
+    layer_->SetBounds(bounds);
+
+    root_->AddChild(layer_);
+    layer_tree_host()->SetRootLayer(root_);
+    layer_tree_host()->SetViewportSize(bounds);
+    SetMailbox('1');
+
+    PostSetNeedsCommitToMainThread();
+  }
+
+  virtual void WillActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+    // Slow down activation so the main thread DidCommit() will run if
+    // not blocked.
+    wait_thread_.message_loop()->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&base::WaitableEvent::Signal,
+                   base::Unretained(&wait_event_)),
+        base::TimeDelta::FromMilliseconds(10));
+    wait_event_.Wait();
+
+    base::AutoLock lock(activate_lock_);
+    ++activate_count_;
+  }
+
+  virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+    // The main thread is awake now, and will run DidCommit() immediately.
+    // Run DidActivate() afterwards by posting it now.
+    proxy()->MainThreadTaskRunner()->PostTask(
+        FROM_HERE,
+        base::Bind(&TextureLayerMailboxIsActivatedDuringCommit::DidActivate,
+                   base::Unretained(this)));
+  }
+
+  void DidActivate() {
+    base::AutoLock lock(activate_lock_);
+    switch (activate_count_) {
+      case 1:
+        // The first mailbox has been activated. Set a new mailbox, and
+        // expect the next commit to finish *after* it is activated.
+        SetMailbox('2');
+        // So this commit number should complete after the second activate.
+        EXPECT_EQ(1, layer_tree_host()->source_frame_number());
+        break;
+      case 2:
+        // The second mailbox has been activated. Remove the layer from
+        // the tree to cause another commit/activation. The commit should
+        // finish *after* the layer is removed from the active tree.
+        layer_->RemoveFromParent();
+        // So this commit number should complete after the third activate.
+        EXPECT_EQ(2, layer_tree_host()->source_frame_number());
+        break;
+      case 3:
+        EndTest();
+        break;
+    }
+  }
+
+  virtual void DidCommit() OVERRIDE {
+    switch (layer_tree_host()->source_frame_number()) {
+      case 2: {
+        // The activate for the 2nd mailbox should have happened before now.
+        base::AutoLock lock(activate_lock_);
+        EXPECT_EQ(2, activate_count_);
+        break;
+      }
+      case 3: {
+        // The activate to remove the layer should have happened before now.
+        base::AutoLock lock(activate_lock_);
+        EXPECT_EQ(3, activate_count_);
+        break;
+      }
+    }
+  }
+
+
+  virtual void AfterTest() OVERRIDE {}
+
+  base::Thread wait_thread_;
+  base::WaitableEvent wait_event_;
+  base::Lock activate_lock_;
+  int activate_count_;
+  scoped_refptr<Layer> root_;
+  scoped_refptr<TextureLayer> layer_;
+};
+
+SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(
+    TextureLayerMailboxIsActivatedDuringCommit);
+
 class TextureLayerImplWithMailboxTest : public TextureLayerTest {
  protected:
   TextureLayerImplWithMailboxTest()
@@ -991,13 +1533,14 @@
 class TextureLayerWithMailboxMainThreadDeleted : public LayerTreeTest {
  public:
   void ReleaseCallback(unsigned sync_point, bool lost_resource) {
-    EXPECT_EQ(true, proxy()->IsMainThread());
+    EXPECT_EQ(true, main_thread_.CalledOnValidThread());
     EXPECT_FALSE(lost_resource);
     ++callback_count_;
     EndTest();
   }
 
   void SetMailbox(char mailbox_char) {
+    EXPECT_EQ(true, main_thread_.CalledOnValidThread());
     TextureMailbox mailbox(
         std::string(64, mailbox_char),
         base::Bind(
@@ -1023,6 +1566,8 @@
   }
 
   virtual void BeginTest() OVERRIDE {
+    EXPECT_EQ(true, main_thread_.CalledOnValidThread());
+
     callback_count_ = 0;
 
     // Set the mailbox on the main thread.
@@ -1048,6 +1593,7 @@
   }
 
  private:
+  base::ThreadChecker main_thread_;
   int callback_count_;
   scoped_refptr<Layer> root_;
   scoped_refptr<TextureLayer> layer_;
@@ -1059,13 +1605,14 @@
 class TextureLayerWithMailboxImplThreadDeleted : public LayerTreeTest {
  public:
   void ReleaseCallback(unsigned sync_point, bool lost_resource) {
-    EXPECT_EQ(true, proxy()->IsMainThread());
+    EXPECT_EQ(true, main_thread_.CalledOnValidThread());
     EXPECT_FALSE(lost_resource);
     ++callback_count_;
     EndTest();
   }
 
   void SetMailbox(char mailbox_char) {
+    EXPECT_EQ(true, main_thread_.CalledOnValidThread());
     TextureMailbox mailbox(
         std::string(64, mailbox_char),
         base::Bind(
@@ -1091,6 +1638,8 @@
   }
 
   virtual void BeginTest() OVERRIDE {
+    EXPECT_EQ(true, main_thread_.CalledOnValidThread());
+
     callback_count_ = 0;
 
     // Set the mailbox on the main thread.
@@ -1119,6 +1668,7 @@
   }
 
  private:
+  base::ThreadChecker main_thread_;
   int callback_count_;
   scoped_refptr<Layer> root_;
   scoped_refptr<TextureLayer> layer_;
diff --git a/cc/layers/tiled_layer.cc b/cc/layers/tiled_layer.cc
index bac12f0..2d812e8 100644
--- a/cc/layers/tiled_layer.cc
+++ b/cc/layers/tiled_layer.cc
@@ -235,9 +235,7 @@
   needs_push_properties_ = true;
 }
 
-bool TiledLayer::BlocksPendingCommit() const { return true; }
-
-PrioritizedResourceManager* TiledLayer::ResourceManager() const {
+PrioritizedResourceManager* TiledLayer::ResourceManager() {
   if (!layer_tree_host())
     return NULL;
   return layer_tree_host()->contents_texture_manager();
@@ -731,6 +729,10 @@
                         const OcclusionTracker* occlusion) {
   DCHECK(!skips_draw_ && !failed_update_);  // Did ResetUpdateState get skipped?
 
+  // Tiled layer always causes commits to wait for activation, as it does
+  // not support pending trees.
+  SetNextCommitWaitsForActivation();
+
   bool updated = false;
 
   {
diff --git a/cc/layers/tiled_layer.h b/cc/layers/tiled_layer.h
index 8ad7df9..51afed2 100644
--- a/cc/layers/tiled_layer.h
+++ b/cc/layers/tiled_layer.h
@@ -26,7 +26,6 @@
   // Layer implementation.
   virtual void SetIsMask(bool is_mask) OVERRIDE;
   virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
-  virtual bool BlocksPendingCommit() const OVERRIDE;
   virtual bool DrawsContent() const OVERRIDE;
   virtual void ReduceMemoryUsage() OVERRIDE;
   virtual void SetNeedsDisplayRect(const gfx::RectF& dirty_rect) OVERRIDE;
@@ -68,7 +67,7 @@
   bool SkipsDraw() const { return skips_draw_; }
 
   // Virtual for testing
-  virtual PrioritizedResourceManager* ResourceManager() const;
+  virtual PrioritizedResourceManager* ResourceManager();
   const LayerTilingData* TilerForTesting() const { return tiler_.get(); }
   const PrioritizedResource* ResourceAtForTesting(int i, int j) const;
 
diff --git a/cc/output/context_provider.cc b/cc/output/context_provider.cc
new file mode 100644
index 0000000..fb6d8e6
--- /dev/null
+++ b/cc/output/context_provider.cc
@@ -0,0 +1,26 @@
+// 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 "cc/output/context_provider.h"
+
+namespace cc {
+
+ContextProvider::Capabilities::Capabilities()
+    : bind_uniform_location(false),
+      discard_backbuffer(false),
+      egl_image_external(false),
+      fast_npot_mo8_textures(false),
+      iosurface(false),
+      map_image(false),
+      map_sub(false),
+      post_sub_buffer(false),
+      set_visibility(false),
+      shallow_flush(false),
+      swapbuffers_complete_callback(false),
+      texture_format_bgra8888(false),
+      texture_rectangle(false),
+      texture_storage(false),
+      texture_usage(false) {}
+
+}  // namespace cc
diff --git a/cc/output/context_provider.h b/cc/output/context_provider.h
index 7d61763..3aae7eb 100644
--- a/cc/output/context_provider.h
+++ b/cc/output/context_provider.h
@@ -7,6 +7,7 @@
 
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
+#include "cc/base/cc_export.h"
 
 class GrContext;
 namespace WebKit { class WebGraphicsContext3D; }
@@ -25,6 +26,28 @@
   virtual WebKit::WebGraphicsContext3D* Context3d() = 0;
   virtual class GrContext* GrContext() = 0;
 
+  struct Capabilities {
+    bool bind_uniform_location;
+    bool discard_backbuffer;
+    bool egl_image_external;
+    bool fast_npot_mo8_textures;
+    bool iosurface;
+    bool map_image;
+    bool map_sub;
+    bool post_sub_buffer;
+    bool set_visibility;
+    bool shallow_flush;
+    bool swapbuffers_complete_callback;
+    bool texture_format_bgra8888;
+    bool texture_rectangle;
+    bool texture_storage;
+    bool texture_usage;
+
+    CC_EXPORT Capabilities();
+  };
+  // Returns the capabilities of the currently bound 3d context.
+  virtual Capabilities ContextCapabilities() = 0;
+
   // Ask the provider to check if the contexts are valid or lost. If they are,
   // this should invalidate the provider so that it can be replaced with a new
   // one.
diff --git a/cc/output/delegating_renderer.cc b/cc/output/delegating_renderer.cc
index 226fddb..799ca83 100644
--- a/cc/output/delegating_renderer.cc
+++ b/cc/output/delegating_renderer.cc
@@ -75,41 +75,14 @@
       context3d);
   context3d->pushGroupMarkerEXT(unique_context_name.c_str());
 
-  std::string extensions_string =
-      UTF16ToASCII(context3d->getString(GL_EXTENSIONS));
+  const ContextProvider::Capabilities& caps =
+      output_surface_->context_provider()->ContextCapabilities();
 
-  std::vector<std::string> extensions;
-  base::SplitString(extensions_string, ' ', &extensions);
+  DCHECK(!caps.iosurface || caps.texture_rectangle);
 
-  // TODO(danakj): We need non-GPU-specific paths for these things. This
-  // renderer shouldn't need to use context3d extensions directly.
-  bool has_set_visibility = false;
-  bool has_io_surface = false;
-  bool has_arb_texture_rect = false;
-  bool has_egl_image = false;
-  bool has_map_image = false;
-  for (size_t i = 0; i < extensions.size(); ++i) {
-    if (extensions[i] == "GL_CHROMIUM_set_visibility") {
-      has_set_visibility = true;
-    } else if (extensions[i] == "GL_CHROMIUM_iosurface") {
-      has_io_surface = true;
-    } else if (extensions[i] == "GL_ARB_texture_rectangle") {
-        has_arb_texture_rect = true;
-    } else if (extensions[i] == "GL_OES_EGL_image_external") {
-        has_egl_image = true;
-    } else if (extensions[i] == "GL_CHROMIUM_map_image") {
-      has_map_image = true;
-    }
-  }
-
-  if (has_io_surface)
-    DCHECK(has_arb_texture_rect);
-
-  capabilities_.using_set_visibility = has_set_visibility;
-
-  capabilities_.using_egl_image = has_egl_image;
-
-  capabilities_.using_map_image = has_map_image;
+  capabilities_.using_set_visibility = caps.set_visibility;
+  capabilities_.using_egl_image = caps.egl_image_external;
+  capabilities_.using_map_image = caps.map_image;
 
   return true;
 }
@@ -130,7 +103,8 @@
 }
 
 void DelegatingRenderer::DrawFrame(
-    RenderPassList* render_passes_in_draw_order) {
+    RenderPassList* render_passes_in_draw_order,
+    ContextProvider* offscreen_context_provider) {
   TRACE_EVENT0("cc", "DelegatingRenderer::DrawFrame");
 
   DCHECK(!frame_for_swap_buffers_.delegated_frame_data);
diff --git a/cc/output/delegating_renderer.h b/cc/output/delegating_renderer.h
index 11c8c6b..3e900ac 100644
--- a/cc/output/delegating_renderer.h
+++ b/cc/output/delegating_renderer.h
@@ -27,7 +27,8 @@
 
   virtual bool CanReadPixels() const OVERRIDE;
 
-  virtual void DrawFrame(RenderPassList* render_passes_in_draw_order) OVERRIDE;
+  virtual void DrawFrame(RenderPassList* render_passes_in_draw_order,
+                         ContextProvider* offscreen_context_provider) OVERRIDE;
 
   virtual void Finish() OVERRIDE {}
 
diff --git a/cc/output/direct_renderer.cc b/cc/output/direct_renderer.cc
index a664d7d..87eefba 100644
--- a/cc/output/direct_renderer.cc
+++ b/cc/output/direct_renderer.cc
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "base/containers/hash_tables.h"
+#include "base/containers/scoped_ptr_hash_map.h"
 #include "base/debug/trace_event.h"
 #include "base/metrics/histogram.h"
 #include "cc/base/math_util.h"
@@ -58,7 +59,8 @@
 DirectRenderer::DrawingFrame::DrawingFrame()
     : root_render_pass(NULL),
       current_render_pass(NULL),
-      current_texture(NULL) {}
+      current_texture(NULL),
+      offscreen_context_provider(NULL) {}
 
 DirectRenderer::DrawingFrame::~DrawingFrame() {}
 
@@ -151,7 +153,8 @@
         render_passes_in_draw_order[i]->id, render_passes_in_draw_order[i]));
 
   std::vector<RenderPass::Id> passes_to_delete;
-  ScopedPtrHashMap<RenderPass::Id, CachedResource>::const_iterator pass_iter;
+  base::ScopedPtrHashMap<RenderPass::Id, CachedResource>::const_iterator
+      pass_iter;
   for (pass_iter = render_pass_textures_.begin();
        pass_iter != render_pass_textures_.end();
        ++pass_iter) {
@@ -169,7 +172,7 @@
     DCHECK(texture);
 
     bool size_appropriate = texture->size().width() >= required_size.width() &&
-                           texture->size().height() >= required_size.height();
+                            texture->size().height() >= required_size.height();
     if (texture->id() &&
         (!size_appropriate || texture->format() != required_format))
       texture->Free();
@@ -190,7 +193,8 @@
   }
 }
 
-void DirectRenderer::DrawFrame(RenderPassList* render_passes_in_draw_order) {
+void DirectRenderer::DrawFrame(RenderPassList* render_passes_in_draw_order,
+                               ContextProvider* offscreen_context_provider) {
   TRACE_EVENT0("cc", "DirectRenderer::DrawFrame");
   UMA_HISTOGRAM_COUNTS("Renderer4.renderPassCount",
                        render_passes_in_draw_order->size());
@@ -204,6 +208,7 @@
       Capabilities().using_partial_swap && client_->AllowPartialSwap() ?
       root_render_pass->damage_rect : root_render_pass->output_rect;
   frame.root_damage_rect.Intersect(gfx::Rect(client_->DeviceViewport().size()));
+  frame.offscreen_context_provider = offscreen_context_provider;
 
   EnsureBackbuffer();
 
diff --git a/cc/output/direct_renderer.h b/cc/output/direct_renderer.h
index ffc2b67..504eaef 100644
--- a/cc/output/direct_renderer.h
+++ b/cc/output/direct_renderer.h
@@ -7,6 +7,7 @@
 
 #include "base/basictypes.h"
 #include "base/callback.h"
+#include "base/containers/scoped_ptr_hash_map.h"
 #include "cc/base/cc_export.h"
 #include "cc/output/renderer.h"
 #include "cc/resources/resource_provider.h"
@@ -30,7 +31,8 @@
       const RenderPassList& render_passes_in_draw_order) OVERRIDE;
   virtual bool HaveCachedResourcesForRenderPassId(RenderPass::Id id) const
       OVERRIDE;
-  virtual void DrawFrame(RenderPassList* render_passes_in_draw_order) OVERRIDE;
+  virtual void DrawFrame(RenderPassList* render_passes_in_draw_order,
+                         ContextProvider* offscreen_context_provider) OVERRIDE;
 
   struct CC_EXPORT DrawingFrame {
     DrawingFrame();
@@ -44,6 +46,8 @@
 
     gfx::Transform projection_matrix;
     gfx::Transform window_matrix;
+
+    ContextProvider* offscreen_context_provider;
   };
 
   void SetEnlargePassTextureAmountForTesting(gfx::Vector2d amount);
@@ -120,7 +124,7 @@
       DrawingFrame* frame,
       scoped_ptr<CopyOutputRequest> request) = 0;
 
-  ScopedPtrHashMap<RenderPass::Id, CachedResource> render_pass_textures_;
+  base::ScopedPtrHashMap<RenderPass::Id, CachedResource> render_pass_textures_;
   OutputSurface* output_surface_;
   ResourceProvider* resource_provider_;
 
diff --git a/cc/output/filter_operation.cc b/cc/output/filter_operation.cc
index 6a77838..e8a0880 100644
--- a/cc/output/filter_operation.cc
+++ b/cc/output/filter_operation.cc
@@ -147,10 +147,9 @@
       return FilterOperation::CreateZoomFilter(1.f, 0);
     case FilterOperation::SATURATING_BRIGHTNESS:
       return FilterOperation::CreateSaturatingBrightnessFilter(0.f);
-    default:
-      NOTREACHED();
-      return FilterOperation::CreateEmptyFilter();
   }
+  NOTREACHED();
+  return FilterOperation::CreateEmptyFilter();
 }
 
 static float ClampAmountForFilterType(float amount,
@@ -173,10 +172,11 @@
     case FilterOperation::SATURATING_BRIGHTNESS:
       return amount;
     case FilterOperation::COLOR_MATRIX:
-    default:
       NOTREACHED();
       return amount;
   }
+  NOTREACHED();
+  return amount;
 }
 
 // static
diff --git a/cc/output/filter_operations.cc b/cc/output/filter_operations.cc
index 2420841..e526f5c 100644
--- a/cc/output/filter_operations.cc
+++ b/cc/output/filter_operations.cc
@@ -86,7 +86,16 @@
       case FilterOperation::DROP_SHADOW:
       case FilterOperation::ZOOM:
         return true;
-      default:
+      case FilterOperation::OPACITY:
+      case FilterOperation::COLOR_MATRIX:
+      case FilterOperation::GRAYSCALE:
+      case FilterOperation::SEPIA:
+      case FilterOperation::SATURATE:
+      case FilterOperation::HUE_ROTATE:
+      case FilterOperation::INVERT:
+      case FilterOperation::BRIGHTNESS:
+      case FilterOperation::CONTRAST:
+      case FilterOperation::SATURATING_BRIGHTNESS:
         break;
     }
   }
@@ -104,10 +113,22 @@
         return true;
       case FilterOperation::COLOR_MATRIX: {
         const SkScalar* matrix = op.matrix();
-        return matrix[15] || matrix[16] || matrix[17] || matrix[18] != 1 ||
-               matrix[19];
+        if (matrix[15] ||
+            matrix[16] ||
+            matrix[17] ||
+            matrix[18] != 1 ||
+            matrix[19])
+          return true;
+        break;
       }
-      default:
+      case FilterOperation::GRAYSCALE:
+      case FilterOperation::SEPIA:
+      case FilterOperation::SATURATE:
+      case FilterOperation::HUE_ROTATE:
+      case FilterOperation::INVERT:
+      case FilterOperation::BRIGHTNESS:
+      case FilterOperation::CONTRAST:
+      case FilterOperation::SATURATING_BRIGHTNESS:
         break;
     }
   }
diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc
index c9b6c58..a80feac 100644
--- a/cc/output/gl_renderer.cc
+++ b/cc/output/gl_renderer.cc
@@ -176,25 +176,18 @@
       context_);
   context_->pushGroupMarkerEXT(unique_context_name.c_str());
 
-  std::string extensions_string =
-      UTF16ToASCII(context_->getString(GL_EXTENSIONS));
-  std::vector<std::string> extensions_list;
-  base::SplitString(extensions_string, ' ', &extensions_list);
-  std::set<std::string> extensions(extensions_list.begin(),
-                                   extensions_list.end());
+  ContextProvider::Capabilities context_caps =
+    output_surface_->context_provider()->ContextCapabilities();
 
   capabilities_.using_partial_swap =
       Settings().partial_swap_enabled &&
-      extensions.count("GL_CHROMIUM_post_sub_buffer");
+      context_caps.post_sub_buffer;
 
-  capabilities_.using_set_visibility =
-      extensions.count("GL_CHROMIUM_set_visibility") > 0;
+  capabilities_.using_set_visibility = context_caps.set_visibility;
 
-  if (extensions.count("GL_CHROMIUM_iosurface") > 0)
-    DCHECK_GT(extensions.count("GL_ARB_texture_rectangle"), 0u);
+  DCHECK(!context_caps.iosurface || context_caps.texture_rectangle);
 
-  capabilities_.using_egl_image =
-      extensions.count("GL_OES_EGL_image_external") > 0;
+  capabilities_.using_egl_image = context_caps.egl_image_external;
 
   capabilities_.max_texture_size = resource_provider_->max_texture_size();
   capabilities_.best_texture_format = resource_provider_->best_texture_format();
@@ -204,17 +197,14 @@
 
   // Check for texture fast paths. Currently we always use MO8 textures,
   // so we only need to avoid POT textures if we have an NPOT fast-path.
-  capabilities_.avoid_pow2_textures =
-      extensions.count("GL_CHROMIUM_fast_NPOT_MO8_textures") > 0;
+  capabilities_.avoid_pow2_textures = context_caps.fast_npot_mo8_textures;
 
   capabilities_.using_offscreen_context3d = true;
 
   capabilities_.using_map_image =
-      extensions.count("GL_CHROMIUM_map_image") > 0 &&
-      Settings().use_map_image;
+      Settings().use_map_image && context_caps.map_image;
 
-  is_using_bind_uniform_ =
-      extensions.count("GL_CHROMIUM_bind_uniform_location") > 0;
+  is_using_bind_uniform_ = context_caps.bind_uniform_location;
 
   if (!InitializeSharedObjects())
     return false;
@@ -243,7 +233,6 @@
     pending_async_read_pixels_.pop_back();
   }
 
-  context_->setMemoryAllocationChangedCallbackCHROMIUM(NULL);
   CleanupSharedObjects();
 }
 
@@ -470,13 +459,12 @@
 }
 
 static inline SkBitmap ApplyFilters(GLRenderer* renderer,
+                                    ContextProvider* offscreen_contexts,
                                     const FilterOperations& filters,
                                     ScopedResource* source_texture_resource) {
   if (filters.IsEmpty())
     return SkBitmap();
 
-  ContextProvider* offscreen_contexts =
-      renderer->resource_provider()->offscreen_context_provider();
   if (!offscreen_contexts || !offscreen_contexts->GrContext())
     return SkBitmap();
 
@@ -492,7 +480,7 @@
   offscreen_contexts->Context3d()->makeContextCurrent();
 
   // Lazily label this context.
-  renderer->LazyLabelOffscreenContext();
+  renderer->LazyLabelOffscreenContext(offscreen_contexts);
 
   SkBitmap source =
       RenderSurfaceFilters::Apply(filters,
@@ -514,13 +502,13 @@
 }
 
 static SkBitmap ApplyImageFilter(GLRenderer* renderer,
+                                 ContextProvider* offscreen_contexts,
+                                 gfx::Point origin,
                                  SkImageFilter* filter,
                                  ScopedResource* source_texture_resource) {
   if (!filter)
     return SkBitmap();
 
-  ContextProvider* offscreen_contexts =
-      renderer->resource_provider()->offscreen_context_provider();
   if (!offscreen_contexts || !offscreen_contexts->GrContext())
     return SkBitmap();
 
@@ -536,7 +524,7 @@
   offscreen_contexts->Context3d()->makeContextCurrent();
 
   // Lazily label this context.
-  renderer->LazyLabelOffscreenContext();
+  renderer->LazyLabelOffscreenContext(offscreen_contexts);
 
   // Wrap the source texture in a Ganesh platform texture.
   GrBackendTextureDesc backend_texture_description;
@@ -580,6 +568,11 @@
   SkPaint paint;
   paint.setImageFilter(filter);
   canvas.clear(SK_ColorTRANSPARENT);
+
+  // TODO(senorblanco): in addition to the origin translation here, the canvas
+  // should also be scaled to accomodate device pixel ratio and pinch zoom. See
+  // crbug.com/281516 and crbug.com/281518.
+  canvas.translate(SkIntToScalar(-origin.x()), SkIntToScalar(-origin.y()));
   canvas.drawSprite(source, 0, 0, &paint);
 
   // Flush skia context so that all the rendered stuff appears on the
@@ -662,7 +655,10 @@
   }
 
   SkBitmap filtered_device_background =
-      ApplyFilters(this, filters, device_background_texture.get());
+      ApplyFilters(this,
+                   frame->offscreen_context_provider,
+                   filters,
+                   device_background_texture.get());
   if (!filtered_device_background.getTexture())
     return scoped_ptr<ScopedResource>();
 
@@ -776,8 +772,11 @@
       // in the compositor.
       use_color_matrix = true;
     } else {
-      filter_bitmap =
-          ApplyImageFilter(this, quad->filter.get(), contents_texture);
+      filter_bitmap = ApplyImageFilter(this,
+                                       frame->offscreen_context_provider,
+                                       quad->rect.origin(),
+                                       quad->filter.get(),
+                                       contents_texture);
     }
   } else if (!quad->filters.IsEmpty()) {
     FilterOperations optimized_filters =
@@ -789,7 +788,10 @@
           color_matrix, optimized_filters.at(0).matrix(), sizeof(color_matrix));
       use_color_matrix = true;
     } else {
-      filter_bitmap = ApplyFilters(this, optimized_filters, contents_texture);
+      filter_bitmap = ApplyFilters(this,
+                                   frame->offscreen_context_provider,
+                                   optimized_filters,
+                                   contents_texture);
     }
   }
 
@@ -1323,20 +1325,11 @@
                                  ResourceProvider::ResourceId resource_id) {
   gfx::Rect tile_rect = quad->visible_rect;
 
-  gfx::RectF tex_coord_rect = quad->tex_coord_rect;
-  float tex_to_geom_scale_x = quad->rect.width() / tex_coord_rect.width();
-  float tex_to_geom_scale_y = quad->rect.height() / tex_coord_rect.height();
-
-  // tex_coord_rect corresponds to quad_rect, but quad_visible_rect may be
-  // smaller than quad_rect due to occlusion or clipping. Adjust
-  // tex_coord_rect to match.
-  gfx::Vector2d top_left_diff = tile_rect.origin() - quad->rect.origin();
-  gfx::Vector2d bottom_right_diff =
-      tile_rect.bottom_right() - quad->rect.bottom_right();
-  tex_coord_rect.Inset(top_left_diff.x() / tex_to_geom_scale_x,
-                       top_left_diff.y() / tex_to_geom_scale_y,
-                       -bottom_right_diff.x() / tex_to_geom_scale_x,
-                       -bottom_right_diff.y() / tex_to_geom_scale_y);
+  gfx::RectF tex_coord_rect = MathUtil::ScaleRectProportional(
+      quad->tex_coord_rect, quad->rect, tile_rect);
+  float tex_to_geom_scale_x = quad->rect.width() / quad->tex_coord_rect.width();
+  float tex_to_geom_scale_y =
+      quad->rect.height() / quad->tex_coord_rect.height();
 
   gfx::RectF clamp_geom_rect(tile_rect);
   gfx::RectF clamp_tex_rect(tex_coord_rect);
@@ -1705,7 +1698,7 @@
         ResourceProvider::TextureUsageAny);
   }
 
-  SkDevice device(on_demand_tile_raster_bitmap_);
+  SkBitmapDevice device(on_demand_tile_raster_bitmap_);
   SkCanvas canvas(&device);
 
   quad->picture_pile->RasterToBitmap(&canvas, quad->content_rect,
@@ -2348,7 +2341,7 @@
   if (is_async) {
     query = context_->createQueryEXT();
     GLC(context_, context_->beginQueryEXT(
-        GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM,
+        GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM,
         query));
   }
 
@@ -2389,7 +2382,7 @@
 
   if (is_async) {
     GLC(context_, context_->endQueryEXT(
-        GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM));
+        GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM));
     SyncPointHelper::SignalQuery(
         context_,
         query,
@@ -3114,7 +3107,7 @@
 
   skia::RefPtr<GrSurface> surface(
       skia::AdoptRef(gr_context_->wrapBackendRenderTarget(desc)));
-  skia::RefPtr<SkDevice> device(
+  skia::RefPtr<SkBaseDevice> device(
       skia::AdoptRef(SkGpuDevice::Create(surface.get())));
   sk_canvas_ = skia::AdoptRef(new SkCanvas(device.get()));
 }
@@ -3149,7 +3142,8 @@
   return (context_->getGraphicsResetStatusARB() != GL_NO_ERROR);
 }
 
-void GLRenderer::LazyLabelOffscreenContext() {
+void GLRenderer::LazyLabelOffscreenContext(
+    ContextProvider* offscreen_context_provider) {
   if (offscreen_context_labelled_)
     return;
   offscreen_context_labelled_ = true;
@@ -3157,8 +3151,8 @@
       "%s-Offscreen-%p",
       Settings().compositor_name.c_str(),
       context_);
-  resource_provider()->offscreen_context_provider()->Context3d()->
-    pushGroupMarkerEXT(unique_context_name.c_str());
+  offscreen_context_provider->Context3d()->pushGroupMarkerEXT(
+      unique_context_name.c_str());
 }
 
 
diff --git a/cc/output/gl_renderer.h b/cc/output/gl_renderer.h
index 3969423..6908e1b 100644
--- a/cc/output/gl_renderer.h
+++ b/cc/output/gl_renderer.h
@@ -77,7 +77,7 @@
                           int line);
 
   bool CanUseSkiaGPUBackend() const;
-  void LazyLabelOffscreenContext();
+  void LazyLabelOffscreenContext(ContextProvider* offscreen_context_provider);
 
  protected:
   GLRenderer(RendererClient* client,
diff --git a/cc/output/gl_renderer_unittest.cc b/cc/output/gl_renderer_unittest.cc
index 0c90b64..c12bbfe 100644
--- a/cc/output/gl_renderer_unittest.cc
+++ b/cc/output/gl_renderer_unittest.cc
@@ -117,19 +117,16 @@
 
 class FrameCountingContext : public TestWebGraphicsContext3D {
  public:
-  FrameCountingContext() : frame_(0) {}
+  FrameCountingContext()
+      : frame_(0) {
+    test_capabilities_.set_visibility = true;
+    test_capabilities_.discard_backbuffer = true;
+  }
 
   // WebGraphicsContext3D methods.
 
   // This method would normally do a glSwapBuffers under the hood.
   virtual void prepareTexture() { frame_++; }
-  virtual WebString getString(WebKit::WGC3Denum name) {
-    if (name == GL_EXTENSIONS)
-      return WebString(
-          "GL_CHROMIUM_set_visibility GL_CHROMIUM_gpu_memory_manager "
-          "GL_CHROMIUM_discard_backbuffer");
-    return WebString();
-  }
 
   // Methods added for test.
   int frame_count() { return frame_; }
@@ -462,7 +459,7 @@
   EXPECT_EQ(1, renderer_client_.set_full_root_layer_damage_count());
 
   renderer_->SetVisible(true);
-  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order());
+  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order(), NULL);
   EXPECT_FALSE(renderer_->IsBackbufferDiscarded());
 
   SwapBuffers();
@@ -476,7 +473,7 @@
   EXPECT_EQ(1, renderer_client_.set_full_root_layer_damage_count());
 
   char pixels[4];
-  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order());
+  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order(), NULL);
   EXPECT_FALSE(renderer_->IsBackbufferDiscarded());
 
   renderer_->GetFramebufferPixels(pixels, gfx::Rect(0, 0, 1, 1));
@@ -490,7 +487,7 @@
   renderer_client_.EnableExternalStencilTest();
   renderer_client_.root_render_pass()->has_transparent_background = false;
 
-  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order());
+  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order(), NULL);
   EXPECT_TRUE(renderer_->stencil_enabled());
 }
 
@@ -571,11 +568,7 @@
   }
 
   virtual WebString getString(WGC3Denum name) {
-    // We allow querying the extension string.
-    // TODO(enne): It'd be better to check that we only do this before starting
-    // any other expensive work (like starting a compilation)
-    if (name != GL_EXTENSIONS)
-      ADD_FAILURE();
+    ADD_FAILURE() << name;
     return WebString();
   }
 
@@ -736,7 +729,7 @@
 
   EXPECT_TRUE(renderer.Initialize());
 
-  renderer.DrawFrame(renderer_client.render_passes_in_draw_order());
+  renderer.DrawFrame(renderer_client.render_passes_in_draw_order(), NULL);
 
 // On DEBUG builds, render passes with opaque background clear to blue to
 // easily see regions that were not drawn on the screen.
@@ -767,7 +760,7 @@
 
   EXPECT_TRUE(renderer.Initialize());
 
-  renderer.DrawFrame(renderer_client.render_passes_in_draw_order());
+  renderer.DrawFrame(renderer_client.render_passes_in_draw_order(), NULL);
 
   EXPECT_EQ(1, context->clear_count());
 }
@@ -776,7 +769,10 @@
     : public TestWebGraphicsContext3D {
  public:
   VisibilityChangeIsLastCallTrackingContext()
-      : last_call_was_set_visibility_(false) {}
+      : last_call_was_set_visibility_(false) {
+    test_capabilities_.set_visibility = true;
+    test_capabilities_.discard_backbuffer = true;
+  }
 
   // WebGraphicsContext3D methods.
   virtual void setVisibilityCHROMIUM(bool visible) {
@@ -805,15 +801,6 @@
     last_call_was_set_visibility_ = false;
   }
 
-  // This method would normally do a glSwapBuffers under the hood.
-  virtual WebString getString(WebKit::WGC3Denum name) {
-    if (name == GL_EXTENSIONS)
-      return WebString(
-          "GL_CHROMIUM_set_visibility GL_CHROMIUM_gpu_memory_manager "
-          "GL_CHROMIUM_discard_backbuffer");
-    return WebString();
-  }
-
   // Methods added for test.
   bool last_call_was_set_visibility() const {
     return last_call_was_set_visibility_;
@@ -848,19 +835,16 @@
   // RenderClient and the Context by giving them both a pointer to a variable on
   // the stack.
   renderer.SetVisible(true);
-  renderer.DrawFrame(renderer_client.render_passes_in_draw_order());
+  renderer.DrawFrame(renderer_client.render_passes_in_draw_order(), NULL);
   renderer.SetVisible(false);
   EXPECT_TRUE(context->last_call_was_set_visibility());
 }
 
 class TextureStateTrackingContext : public TestWebGraphicsContext3D {
  public:
-  TextureStateTrackingContext() : active_texture_(GL_INVALID_ENUM) {}
-
-  virtual WebString getString(WGC3Denum name) {
-    if (name == GL_EXTENSIONS)
-      return WebString("GL_OES_EGL_image_external");
-    return WebString();
+  TextureStateTrackingContext()
+      : active_texture_(GL_INVALID_ENUM) {
+    test_capabilities_.egl_image_external = true;
   }
 
   MOCK_METHOD3(texParameteri,
@@ -1027,7 +1011,7 @@
 
   renderer.DecideRenderPassAllocationsForFrame(
       *renderer_client.render_passes_in_draw_order());
-  renderer.DrawFrame(renderer_client.render_passes_in_draw_order());
+  renderer.DrawFrame(renderer_client.render_passes_in_draw_order(), NULL);
 
   // In multiple render passes all but the root pass should clear the
   // framebuffer.
@@ -1099,7 +1083,7 @@
 
   renderer.DecideRenderPassAllocationsForFrame(
       *renderer_client.render_passes_in_draw_order());
-  renderer.DrawFrame(renderer_client.render_passes_in_draw_order());
+  renderer.DrawFrame(renderer_client.render_passes_in_draw_order(), NULL);
 }
 
 class NonReshapableOutputSurface : public FakeOutputSurface {
@@ -1184,7 +1168,7 @@
 
   renderer.DecideRenderPassAllocationsForFrame(
       *renderer_client.render_passes_in_draw_order());
-  renderer.DrawFrame(renderer_client.render_passes_in_draw_order());
+  renderer.DrawFrame(renderer_client.render_passes_in_draw_order(), NULL);
 }
 
 TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) {
@@ -1246,7 +1230,7 @@
 
   renderer_->DecideRenderPassAllocationsForFrame(
       *renderer_client_.render_passes_in_draw_order());
-  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order());
+  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order(), NULL);
   TestRenderPassProgram();
 
   // RenderPassColorMatrixProgram
@@ -1262,7 +1246,7 @@
 
   renderer_->DecideRenderPassAllocationsForFrame(
       *renderer_client_.render_passes_in_draw_order());
-  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order());
+  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order(), NULL);
   TestRenderPassColorMatrixProgram();
 
   // RenderPassMaskProgram
@@ -1282,7 +1266,7 @@
 
   renderer_->DecideRenderPassAllocationsForFrame(
       *renderer_client_.render_passes_in_draw_order());
-  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order());
+  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order(), NULL);
   TestRenderPassMaskProgram();
 
   // RenderPassMaskColorMatrixProgram
@@ -1298,7 +1282,7 @@
 
   renderer_->DecideRenderPassAllocationsForFrame(
       *renderer_client_.render_passes_in_draw_order());
-  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order());
+  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order(), NULL);
   TestRenderPassMaskColorMatrixProgram();
 
   // RenderPassProgramAA
@@ -1318,7 +1302,7 @@
 
   renderer_->DecideRenderPassAllocationsForFrame(
       *renderer_client_.render_passes_in_draw_order());
-  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order());
+  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order(), NULL);
   TestRenderPassProgramAA();
 
   // RenderPassColorMatrixProgramAA
@@ -1334,7 +1318,7 @@
 
   renderer_->DecideRenderPassAllocationsForFrame(
       *renderer_client_.render_passes_in_draw_order());
-  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order());
+  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order(), NULL);
   TestRenderPassColorMatrixProgramAA();
 
   // RenderPassMaskProgramAA
@@ -1351,7 +1335,7 @@
 
   renderer_->DecideRenderPassAllocationsForFrame(
       *renderer_client_.render_passes_in_draw_order());
-  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order());
+  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order(), NULL);
   TestRenderPassMaskProgramAA();
 
   // RenderPassMaskColorMatrixProgramAA
@@ -1367,7 +1351,7 @@
 
   renderer_->DecideRenderPassAllocationsForFrame(
       *renderer_client_.render_passes_in_draw_order());
-  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order());
+  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order(), NULL);
   TestRenderPassMaskColorMatrixProgramAA();
 }
 
@@ -1415,7 +1399,7 @@
 
   renderer_->DecideRenderPassAllocationsForFrame(
       *renderer_client_.render_passes_in_draw_order());
-  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order());
+  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order(), NULL);
 
   // If use_aa incorrectly ignores clipping, it will use the
   // RenderPassProgramAA shader instead of the RenderPassProgram.
@@ -1445,13 +1429,18 @@
 
   renderer_->DecideRenderPassAllocationsForFrame(
       *renderer_client_.render_passes_in_draw_order());
-  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order());
+  renderer_->DrawFrame(renderer_client_.render_passes_in_draw_order(), NULL);
 
   TestSolidColorProgramAA();
 }
 
 class OutputSurfaceMockContext : public TestWebGraphicsContext3D {
  public:
+  OutputSurfaceMockContext() {
+    test_capabilities_.discard_backbuffer = true;
+    test_capabilities_.post_sub_buffer = true;
+  }
+
   // Specifically override methods even if they are unused (used in conjunction
   // with StrictMock). We need to make sure that GLRenderer does not issue
   // framebuffer-related GL calls directly. Instead these are supposed to go
@@ -1467,13 +1456,6 @@
                     WGC3Dsizei count,
                     WGC3Denum type,
                     WGC3Dintptr offset));
-
-  virtual WebString getString(WebKit::WGC3Denum name) {
-    if (name == GL_EXTENSIONS)
-      return WebString(
-          "GL_CHROMIUM_post_sub_buffer GL_CHROMIUM_discard_backbuffer");
-    return WebString();
-  }
 };
 
 class MockOutputSurface : public OutputSurface {
@@ -1530,7 +1512,7 @@
 
     renderer_->DecideRenderPassAllocationsForFrame(
         *render_passes_in_draw_order());
-    renderer_->DrawFrame(render_passes_in_draw_order());
+    renderer_->DrawFrame(render_passes_in_draw_order(), NULL);
   }
 
   OutputSurfaceMockContext* Context() {
diff --git a/cc/output/output_surface.cc b/cc/output/output_surface.cc
index f085d49..19683b2 100644
--- a/cc/output/output_surface.cc
+++ b/cc/output/output_surface.cc
@@ -287,17 +287,11 @@
   DCHECK(context_provider_);
   DCHECK(client_);
 
-  WebKit::WebGraphicsContext3D* context3d = context_provider_->Context3d();
+  const ContextProvider::Capabilities& caps =
+      context_provider_->ContextCapabilities();
 
-  string extensions_string =
-      UTF16ToASCII(context3d->getString(GL_EXTENSIONS));
-  vector<string> extensions_list;
-  base::SplitString(extensions_string, ' ', &extensions_list);
-  set<string> extensions(extensions_list.begin(), extensions_list.end());
-  has_gl_discard_backbuffer_ =
-      extensions.count("GL_CHROMIUM_discard_backbuffer") > 0;
-  has_swap_buffers_complete_callback_ =
-       extensions.count("GL_CHROMIUM_swapbuffers_complete_callback") > 0;
+  has_gl_discard_backbuffer_ = caps.discard_backbuffer;
+  has_swap_buffers_complete_callback_ = caps.swapbuffers_complete_callback;
 
   context_provider_->SetLostContextCallback(
       base::Bind(&OutputSurface::DidLoseOutputSurface,
diff --git a/cc/output/render_surface_filters.cc b/cc/output/render_surface_filters.cc
index fa84e67..04d8c5a 100644
--- a/cc/output/render_surface_filters.cc
+++ b/cc/output/render_surface_filters.cc
@@ -226,9 +226,13 @@
       memcpy(matrix, op.matrix(), sizeof(SkScalar[20]));
       return true;
     }
-    default:
+    case FilterOperation::BLUR:
+    case FilterOperation::DROP_SHADOW:
+    case FilterOperation::ZOOM:
       return false;
   }
+  NOTREACHED();
+  return false;
 }
 
 class FilterBufferState {
diff --git a/cc/output/renderer.h b/cc/output/renderer.h
index a9a800b..9619852 100644
--- a/cc/output/renderer.h
+++ b/cc/output/renderer.h
@@ -54,7 +54,10 @@
 
   // This passes ownership of the render passes to the renderer. It should
   // consume them, and empty the list.
-  virtual void DrawFrame(RenderPassList* render_passes_in_draw_order) = 0;
+  // The |offscreen_context_provider| may change from frame to frame and should
+  // not be cached.
+  virtual void DrawFrame(RenderPassList* render_passes_in_draw_order,
+                         ContextProvider* offscreen_context_provider) = 0;
 
   // Waits for rendering to finish.
   virtual void Finish() = 0;
diff --git a/cc/output/renderer_pixeltest.cc b/cc/output/renderer_pixeltest.cc
index ba76f16..e8d1323 100644
--- a/cc/output/renderer_pixeltest.cc
+++ b/cc/output/renderer_pixeltest.cc
@@ -215,6 +215,7 @@
 
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("green.png")),
       ExactPixelComparator(true)));
 }
@@ -256,6 +257,7 @@
   EXPECT_TRUE(this->RunPixelTestWithReadbackTarget(
       &pass_list,
       child_pass_ptr,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("green_small.png")),
       ExactPixelComparator(true)));
 }
@@ -287,6 +289,7 @@
 
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("green_alpha.png")),
       FuzzyPixelOffByOneComparator(true)));
 }
@@ -321,6 +324,7 @@
 
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("green_alpha.png")),
       FuzzyPixelOffByOneComparator(true)));
 }
@@ -353,6 +357,7 @@
 
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("green_alpha.png")),
       FuzzyPixelOffByOneComparator(true)));
 }
@@ -388,6 +393,7 @@
 
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("green_alpha.png")),
       FuzzyPixelOffByOneComparator(true)));
 }
@@ -477,6 +483,7 @@
 
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("green.png")),
       ExactPixelComparator(true)));
 }
@@ -505,6 +512,7 @@
 
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("green_alpha.png")),
       ExactPixelComparator(true)));
 }
@@ -533,6 +541,7 @@
 
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("black.png")),
       ExactPixelComparator(true)));
 }
@@ -632,6 +641,7 @@
   // renderer so use a fuzzy comparator.
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("blue_yellow_alpha.png")),
       FuzzyForSoftwareOnlyPixelComparator<TypeParam>(false)));
 }
@@ -734,6 +744,7 @@
   // renderer so use a fuzzy comparator.
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("blue_yellow_alpha_translate.png")),
       FuzzyForSoftwareOnlyPixelComparator<TypeParam>(false)));
 }
@@ -790,6 +801,7 @@
 
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("blue_yellow.png")),
       ExactPixelComparator(true)));
 }
@@ -858,6 +870,7 @@
 
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("blue_yellow_anti_aliasing.png")),
       FuzzyPixelOffByOneComparator(true)));
 }
@@ -997,6 +1010,7 @@
   this->SetUpRenderPassList();
   EXPECT_TRUE(this->RunPixelTest(
       &this->pass_list_,
+      PixelTest::WithOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("background_filter.png")),
       ExactPixelComparator(true)));
 }
@@ -1057,6 +1071,7 @@
 
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("four_blue_green_checkers.png")),
       ExactPixelComparator(true)));
 }
@@ -1079,6 +1094,7 @@
 
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("green.png")),
       ExactPixelComparator(true)));
 }
@@ -1128,6 +1144,7 @@
 
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("four_blue_green_checkers.png")),
       ExactPixelComparator(true)));
 }
@@ -1173,6 +1190,7 @@
 
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("anti_aliasing.png")),
       FuzzyPixelOffByOneComparator(true)));
 }
@@ -1225,6 +1243,7 @@
 
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("axis_aligned.png")),
       ExactPixelComparator(true)));
 }
@@ -1266,6 +1285,7 @@
 
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("force_anti_aliasing_off.png")),
       ExactPixelComparator(false)));
 }
@@ -1306,6 +1326,7 @@
 
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("anti_aliasing_perspective.png")),
       FuzzyPixelOffByOneComparator(true)));
 }
@@ -1390,10 +1411,84 @@
 
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("green_with_blue_corner.png")),
       ExactPixelComparator(true)));
 }
 
+// Not WithSkiaGPUBackend since that path currently requires tiles for opacity.
+TYPED_TEST(RendererPixelTest, PictureDrawQuadOpacity) {
+  gfx::Size pile_tile_size(1000, 1000);
+  gfx::Rect viewport(this->device_viewport_size_);
+  bool use_skia_gpu_backend = this->UseSkiaGPUBackend();
+  bool contents_swizzled = !PlatformColor::SameComponentOrder(GL_RGBA);
+
+  RenderPass::Id id(1, 1);
+  gfx::Transform transform_to_root;
+  scoped_ptr<RenderPass> pass =
+      CreateTestRenderPass(id, viewport, transform_to_root);
+
+  // One viewport-filling 0.5-opacity green quad.
+  scoped_refptr<FakePicturePileImpl> green_pile =
+      FakePicturePileImpl::CreateFilledPile(pile_tile_size, viewport.size());
+  SkPaint green_paint;
+  green_paint.setColor(SK_ColorGREEN);
+  green_pile->add_draw_rect_with_paint(viewport, green_paint);
+  green_pile->RerecordPile();
+
+  gfx::Transform green_content_to_target_transform;
+  scoped_ptr<SharedQuadState> green_shared_state =
+      CreateTestSharedQuadState(green_content_to_target_transform, viewport);
+  green_shared_state->opacity = 0.5f;
+
+  scoped_ptr<PictureDrawQuad> green_quad = PictureDrawQuad::Create();
+  green_quad->SetNew(green_shared_state.get(),
+                     viewport,
+                     gfx::Rect(),
+                     gfx::RectF(0, 0, 1, 1),
+                     viewport.size(),
+                     contents_swizzled,
+                     viewport,
+                     1.f,
+                     use_skia_gpu_backend,
+                     green_pile);
+  pass->quad_list.push_back(green_quad.PassAs<DrawQuad>());
+
+  // One viewport-filling white quad.
+  scoped_refptr<FakePicturePileImpl> white_pile =
+      FakePicturePileImpl::CreateFilledPile(pile_tile_size, viewport.size());
+  SkPaint white_paint;
+  white_paint.setColor(SK_ColorWHITE);
+  white_pile->add_draw_rect_with_paint(viewport, white_paint);
+  white_pile->RerecordPile();
+
+  gfx::Transform white_content_to_target_transform;
+  scoped_ptr<SharedQuadState> white_shared_state =
+      CreateTestSharedQuadState(white_content_to_target_transform, viewport);
+
+  scoped_ptr<PictureDrawQuad> white_quad = PictureDrawQuad::Create();
+  white_quad->SetNew(white_shared_state.get(),
+                     viewport,
+                     gfx::Rect(),
+                     gfx::RectF(0, 0, 1, 1),
+                     viewport.size(),
+                     contents_swizzled,
+                     viewport,
+                     1.f,
+                     use_skia_gpu_backend,
+                     white_pile);
+  pass->quad_list.push_back(white_quad.PassAs<DrawQuad>());
+
+  RenderPassList pass_list;
+  pass_list.push_back(pass.Pass());
+
+  EXPECT_TRUE(this->RunPixelTest(
+      &pass_list,
+      PixelTest::NoOffscreenContext,
+      base::FilePath(FILE_PATH_LITERAL("green_alpha.png")),
+      FuzzyPixelOffByOneComparator(true)));
+}
+
 TYPED_TEST(RendererPixelTestWithSkiaGPUBackend,
            PictureDrawQuadNonIdentityScale) {
   gfx::Size pile_tile_size(1000, 1000);
@@ -1542,6 +1637,7 @@
 
   EXPECT_TRUE(this->RunPixelTest(
       &pass_list,
+      PixelTest::NoOffscreenContext,
       base::FilePath(FILE_PATH_LITERAL("four_blue_green_checkers.png")),
       ExactPixelComparator(true)));
 }
diff --git a/cc/output/software_output_device.cc b/cc/output/software_output_device.cc
index fc77eb4..03f977e 100644
--- a/cc/output/software_output_device.cc
+++ b/cc/output/software_output_device.cc
@@ -6,8 +6,8 @@
 
 #include "base/logging.h"
 #include "cc/output/software_frame_data.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
 #include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkDevice.h"
 #include "ui/gfx/skia_util.h"
 
 namespace cc {
@@ -21,7 +21,7 @@
     return;
 
   viewport_size_ = viewport_size;
-  device_ = skia::AdoptRef(new SkDevice(SkBitmap::kARGB_8888_Config,
+  device_ = skia::AdoptRef(new SkBitmapDevice(SkBitmap::kARGB_8888_Config,
       viewport_size.width(), viewport_size.height(), true));
   canvas_ = skia::AdoptRef(new SkCanvas(device_.get()));
 }
diff --git a/cc/output/software_output_device.h b/cc/output/software_output_device.h
index c1a97dc..788673e 100644
--- a/cc/output/software_output_device.h
+++ b/cc/output/software_output_device.h
@@ -8,12 +8,13 @@
 #include "base/basictypes.h"
 #include "cc/base/cc_export.h"
 #include "skia/ext/refptr.h"
+// TODO(robertphillips): change this to "class SkBaseDevice;"
+#include "third_party/skia/include/core/SkDevice.h"
 #include "ui/gfx/rect.h"
 #include "ui/gfx/size.h"
 #include "ui/gfx/vector2d.h"
 
 class SkBitmap;
-class SkDevice;
 class SkCanvas;
 
 namespace cc {
@@ -46,7 +47,7 @@
  protected:
   gfx::Size viewport_size_;
   gfx::Rect damage_rect_;
-  skia::RefPtr<SkDevice> device_;
+  skia::RefPtr<SkBaseDevice> device_;
   skia::RefPtr<SkCanvas> canvas_;
 
  private:
diff --git a/cc/output/software_renderer.cc b/cc/output/software_renderer.cc
index b62f5a0..77c44ea 100644
--- a/cc/output/software_renderer.cc
+++ b/cc/output/software_renderer.cc
@@ -19,9 +19,9 @@
 #include "cc/quads/solid_color_draw_quad.h"
 #include "cc/quads/texture_draw_quad.h"
 #include "cc/quads/tile_draw_quad.h"
+#include "skia/ext/opacity_draw_filter.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkColor.h"
-#include "third_party/skia/include/core/SkDevice.h"
 #include "third_party/skia/include/core/SkMatrix.h"
 #include "third_party/skia/include/core/SkShader.h"
 #include "third_party/skia/include/effects/SkLayerRasterizer.h"
@@ -128,7 +128,7 @@
   // clipRect on the current SkCanvas. This is done by setting clipRect to
   // the viewport's dimensions.
   is_scissor_enabled_ = false;
-  SkDevice* device = current_canvas_->getDevice();
+  SkBaseDevice* device = current_canvas_->getDevice();
   SetClipRect(gfx::Rect(device->width(), device->height()));
 }
 
@@ -277,9 +277,11 @@
 
 void SoftwareRenderer::DrawCheckerboardQuad(const DrawingFrame* frame,
                                             const CheckerboardDrawQuad* quad) {
+  gfx::RectF visible_quad_vertex_rect = MathUtil::ScaleRectProportional(
+      QuadVertexRect(), quad->rect, quad->visible_rect);
   current_paint_.setColor(quad->color);
   current_paint_.setAlpha(quad->opacity() * SkColorGetA(quad->color));
-  current_canvas_->drawRect(gfx::RectFToSkRect(QuadVertexRect()),
+  current_canvas_->drawRect(gfx::RectFToSkRect(visible_quad_vertex_rect),
                             current_paint_);
 }
 
@@ -311,34 +313,28 @@
       SkMatrix::kFill_ScaleToFit);
   current_canvas_->concat(content_matrix);
 
-  if (quad->ShouldDrawWithBlending()) {
-    TRACE_EVENT0("cc", "SoftwareRenderer::DrawPictureQuad with blending");
-    SkBitmap temp_bitmap;
-    temp_bitmap.setConfig(SkBitmap::kARGB_8888_Config,
-                          quad->texture_size.width(),
-                          quad->texture_size.height());
-    temp_bitmap.allocPixels();
-    SkDevice temp_device(temp_bitmap);
-    SkCanvas temp_canvas(&temp_device);
+  // TODO(aelias): This isn't correct in all cases. We should detect these
+  // cases and fall back to a persistent bitmap backing
+  // (http://crbug.com/280374).
+  DCHECK(!current_canvas_->getDrawFilter());
+  current_canvas_->setDrawFilter(new skia::OpacityDrawFilter(quad->opacity(),
+                                                             true));
 
-    quad->picture_pile->RasterToBitmap(
-        &temp_canvas, quad->content_rect, quad->contents_scale, NULL);
+  TRACE_EVENT0("cc",
+               "SoftwareRenderer::DrawPictureQuad");
+  quad->picture_pile->RasterDirect(
+      current_canvas_, quad->content_rect, quad->contents_scale, NULL);
 
-    current_paint_.setFilterBitmap(true);
-    current_canvas_->drawBitmap(temp_bitmap, 0, 0, &current_paint_);
-  } else {
-    TRACE_EVENT0("cc",
-                 "SoftwareRenderer::DrawPictureQuad direct from PicturePile");
-    quad->picture_pile->RasterDirect(
-        current_canvas_, quad->content_rect, quad->contents_scale, NULL);
-  }
+  current_canvas_->setDrawFilter(NULL);
 }
 
 void SoftwareRenderer::DrawSolidColorQuad(const DrawingFrame* frame,
                                           const SolidColorDrawQuad* quad) {
+  gfx::RectF visible_quad_vertex_rect = MathUtil::ScaleRectProportional(
+      QuadVertexRect(), quad->rect, quad->visible_rect);
   current_paint_.setColor(quad->color);
   current_paint_.setAlpha(quad->opacity() * SkColorGetA(quad->color));
-  current_canvas_->drawRect(gfx::RectFToSkRect(QuadVertexRect()),
+  current_canvas_->drawRect(gfx::RectFToSkRect(visible_quad_vertex_rect),
                             current_paint_);
 }
 
@@ -357,8 +353,12 @@
                                                         quad->uv_bottom_right),
                                       bitmap->width(),
                                       bitmap->height());
-  SkRect sk_uv_rect = gfx::RectFToSkRect(uv_rect);
-  SkRect quad_rect = gfx::RectFToSkRect(QuadVertexRect());
+  gfx::RectF visible_uv_rect =
+      MathUtil::ScaleRectProportional(uv_rect, quad->rect, quad->visible_rect);
+  SkRect sk_uv_rect = gfx::RectFToSkRect(visible_uv_rect);
+  gfx::RectF visible_quad_vertex_rect = MathUtil::ScaleRectProportional(
+      QuadVertexRect(), quad->rect, quad->visible_rect);
+  SkRect quad_rect = gfx::RectFToSkRect(visible_quad_vertex_rect);
 
   if (quad->flipped)
     current_canvas_->scale(1, -1);
@@ -391,12 +391,18 @@
   DCHECK(IsSoftwareResource(quad->resource_id));
   ResourceProvider::ScopedReadLockSoftware lock(resource_provider_,
                                                 quad->resource_id);
+  gfx::RectF visible_tex_coord_rect = MathUtil::ScaleRectProportional(
+      quad->tex_coord_rect, quad->rect, quad->visible_rect);
+  gfx::RectF visible_quad_vertex_rect = MathUtil::ScaleRectProportional(
+      QuadVertexRect(), quad->rect, quad->visible_rect);
 
-  SkRect uv_rect = gfx::RectFToSkRect(quad->tex_coord_rect);
+  SkRect uv_rect = gfx::RectFToSkRect(visible_tex_coord_rect);
   current_paint_.setFilterBitmap(true);
-  current_canvas_->drawBitmapRectToRect(*lock.sk_bitmap(), &uv_rect,
-                                        gfx::RectFToSkRect(QuadVertexRect()),
-                                        &current_paint_);
+  current_canvas_->drawBitmapRectToRect(
+      *lock.sk_bitmap(),
+      &uv_rect,
+      gfx::RectFToSkRect(visible_quad_vertex_rect),
+      &current_paint_);
 }
 
 void SoftwareRenderer::DrawRenderPassQuad(const DrawingFrame* frame,
@@ -411,6 +417,8 @@
                                                 content_texture->id());
 
   SkRect dest_rect = gfx::RectFToSkRect(QuadVertexRect());
+  SkRect dest_visible_rect = gfx::RectFToSkRect(MathUtil::ScaleRectProportional(
+      QuadVertexRect(), quad->rect, quad->visible_rect));
   SkRect content_rect = SkRect::MakeWH(quad->rect.width(), quad->rect.height());
 
   SkMatrix content_mat;
@@ -458,10 +466,10 @@
     mask_rasterizer->addLayer(mask_paint);
 
     current_paint_.setRasterizer(mask_rasterizer.get());
-    current_canvas_->drawRect(dest_rect, current_paint_);
+    current_canvas_->drawRect(dest_visible_rect, current_paint_);
   } else {
     // TODO(skaslev): Apply background filters and blend with content
-    current_canvas_->drawRect(dest_rect, current_paint_);
+    current_canvas_->drawRect(dest_visible_rect, current_paint_);
   }
 }
 
diff --git a/cc/output/software_renderer_unittest.cc b/cc/output/software_renderer_unittest.cc
index a3361a9..ba1a0da 100644
--- a/cc/output/software_renderer_unittest.cc
+++ b/cc/output/software_renderer_unittest.cc
@@ -92,6 +92,7 @@
   gfx::Size inner_size(98, 98);
   gfx::Rect outer_rect(outer_size);
   gfx::Rect inner_rect(gfx::Point(1, 1), inner_size);
+  gfx::Rect visible_rect(gfx::Point(1, 2), gfx::Size(98, 97));
   set_viewport(gfx::Rect(outer_size));
 
   InitializeRenderer(make_scoped_ptr(new SoftwareOutputDevice));
@@ -108,12 +109,13 @@
       shared_quad_state.get(), outer_rect, SK_ColorYELLOW, false);
   scoped_ptr<SolidColorDrawQuad> inner_quad = SolidColorDrawQuad::Create();
   inner_quad->SetNew(shared_quad_state.get(), inner_rect, SK_ColorCYAN, false);
+  inner_quad->visible_rect = visible_rect;
   root_render_pass->AppendQuad(inner_quad.PassAs<DrawQuad>());
   root_render_pass->AppendQuad(outer_quad.PassAs<DrawQuad>());
 
   RenderPassList list;
   list.push_back(root_render_pass.PassAs<RenderPass>());
-  renderer()->DrawFrame(&list);
+  renderer()->DrawFrame(&list, NULL);
 
   SkBitmap output;
   output.setConfig(SkBitmap::kARGB_8888_Config,
@@ -125,7 +127,8 @@
   EXPECT_EQ(SK_ColorYELLOW, output.getColor(0, 0));
   EXPECT_EQ(SK_ColorYELLOW,
             output.getColor(outer_size.width() - 1, outer_size.height() - 1));
-  EXPECT_EQ(SK_ColorCYAN, output.getColor(1, 1));
+  EXPECT_EQ(SK_ColorYELLOW, output.getColor(1, 1));
+  EXPECT_EQ(SK_ColorCYAN, output.getColor(1, 2));
   EXPECT_EQ(SK_ColorCYAN,
             output.getColor(inner_size.width() - 1, inner_size.height() - 1));
 }
@@ -199,7 +202,7 @@
 
   RenderPassList list;
   list.push_back(root_render_pass.PassAs<RenderPass>());
-  renderer()->DrawFrame(&list);
+  renderer()->DrawFrame(&list, NULL);
 
   SkBitmap output;
   output.setConfig(SkBitmap::kARGB_8888_Config,
@@ -216,6 +219,85 @@
             output.getColor(inner_size.width() - 1, inner_size.height() - 1));
 }
 
+TEST_F(SoftwareRendererTest, TileQuadVisibleRect) {
+  gfx::Size tile_size(100, 100);
+  gfx::Rect tile_rect(tile_size);
+  gfx::Rect visible_rect = tile_rect;
+  visible_rect.Inset(1, 2, 3, 4);
+  set_viewport(gfx::Rect(tile_size));
+  InitializeRenderer(make_scoped_ptr(new SoftwareOutputDevice));
+
+  ResourceProvider::ResourceId resource_cyan =
+      resource_provider()->CreateResource(
+          tile_size, GL_RGBA, ResourceProvider::TextureUsageAny);
+
+  SkBitmap cyan_tile;  // The lowest five rows are yellow.
+  cyan_tile.setConfig(
+      SkBitmap::kARGB_8888_Config, tile_size.width(), tile_size.height());
+  cyan_tile.allocPixels();
+  cyan_tile.eraseColor(SK_ColorCYAN);
+  cyan_tile.eraseArea(
+      SkIRect::MakeLTRB(
+          0, visible_rect.bottom() - 1, tile_rect.width(), tile_rect.bottom()),
+      SK_ColorYELLOW);
+
+  resource_provider()->SetPixels(resource_cyan,
+                                 static_cast<uint8_t*>(cyan_tile.getPixels()),
+                                 gfx::Rect(tile_size),
+                                 gfx::Rect(tile_size),
+                                 gfx::Vector2d());
+
+  gfx::Rect root_rect = DeviceViewport();
+
+  scoped_ptr<SharedQuadState> shared_quad_state = SharedQuadState::Create();
+  shared_quad_state->SetAll(
+      gfx::Transform(), tile_size, tile_rect, tile_rect, false, 1.0);
+  RenderPass::Id root_render_pass_id = RenderPass::Id(1, 1);
+  scoped_ptr<TestRenderPass> root_render_pass = TestRenderPass::Create();
+  root_render_pass->SetNew(
+      root_render_pass_id, root_rect, root_rect, gfx::Transform());
+  scoped_ptr<TileDrawQuad> quad = TileDrawQuad::Create();
+  quad->SetNew(shared_quad_state.get(),
+               tile_rect,
+               tile_rect,
+               resource_cyan,
+               gfx::RectF(tile_size),
+               tile_size,
+               false);
+  quad->visible_rect = visible_rect;
+  root_render_pass->AppendQuad(quad.PassAs<DrawQuad>());
+
+  RenderPassList list;
+  list.push_back(root_render_pass.PassAs<RenderPass>());
+  renderer()->DrawFrame(&list, NULL);
+
+  SkBitmap output;
+  output.setConfig(SkBitmap::kARGB_8888_Config,
+                   DeviceViewport().width(),
+                   DeviceViewport().height());
+  output.allocPixels();
+  renderer()->GetFramebufferPixels(output.getPixels(), tile_rect);
+
+  // Check portion of tile not in visible rect isn't drawn.
+  const unsigned int kTransparent = SK_ColorTRANSPARENT;
+  EXPECT_EQ(kTransparent, output.getColor(0, 0));
+  EXPECT_EQ(kTransparent,
+            output.getColor(tile_rect.width() - 1, tile_rect.height() - 1));
+  EXPECT_EQ(kTransparent,
+            output.getColor(visible_rect.x() - 1, visible_rect.y() - 1));
+  EXPECT_EQ(kTransparent,
+            output.getColor(visible_rect.right(), visible_rect.bottom()));
+  // Ensure visible part is drawn correctly.
+  EXPECT_EQ(SK_ColorCYAN, output.getColor(visible_rect.x(), visible_rect.y()));
+  EXPECT_EQ(
+      SK_ColorCYAN,
+      output.getColor(visible_rect.right() - 2, visible_rect.bottom() - 2));
+  // Ensure last visible line is correct.
+  EXPECT_EQ(
+      SK_ColorYELLOW,
+      output.getColor(visible_rect.right() - 1, visible_rect.bottom() - 1));
+}
+
 TEST_F(SoftwareRendererTest, ShouldClearRootRenderPass) {
   gfx::Rect viewport_rect(0, 0, 100, 100);
   set_viewport(viewport_rect);
@@ -237,7 +319,7 @@
   AddQuad(root_clear_pass, viewport_rect, SK_ColorGREEN);
 
   renderer()->DecideRenderPassAllocationsForFrame(list);
-  renderer()->DrawFrame(&list);
+  renderer()->DrawFrame(&list, NULL);
   renderer()->GetFramebufferPixels(output.getPixels(), viewport_rect);
 
   EXPECT_EQ(SK_ColorGREEN, output.getColor(0, 0));
@@ -256,7 +338,7 @@
   AddQuad(root_smaller_pass, smaller_rect, SK_ColorMAGENTA);
 
   renderer()->DecideRenderPassAllocationsForFrame(list);
-  renderer()->DrawFrame(&list);
+  renderer()->DrawFrame(&list, NULL);
   renderer()->GetFramebufferPixels(output.getPixels(), viewport_rect);
 
   // If we didn't clear, the borders should still be green.
@@ -270,5 +352,59 @@
       output.getColor(smaller_rect.right() - 1, smaller_rect.bottom() - 1));
 }
 
+TEST_F(SoftwareRendererTest, RenderPassVisibleRect) {
+  gfx::Rect viewport_rect(0, 0, 100, 100);
+  set_viewport(viewport_rect);
+  InitializeRenderer(make_scoped_ptr(new SoftwareOutputDevice));
+
+  RenderPassList list;
+
+  SkBitmap output;
+  output.setConfig(SkBitmap::kARGB_8888_Config,
+                   viewport_rect.width(),
+                   viewport_rect.height());
+  output.allocPixels();
+
+  // Pass drawn as inner quad is magenta.
+  gfx::Rect smaller_rect(20, 20, 60, 60);
+  RenderPass::Id smaller_pass_id(2, 1);
+  TestRenderPass* smaller_pass =
+      AddRenderPass(&list, smaller_pass_id, smaller_rect, gfx::Transform());
+  AddQuad(smaller_pass, smaller_rect, SK_ColorMAGENTA);
+
+  // Root pass is green.
+  RenderPass::Id root_clear_pass_id(1, 0);
+  TestRenderPass* root_clear_pass =
+      AddRenderPass(&list, root_clear_pass_id, viewport_rect, gfx::Transform());
+  AddRenderPassQuad(root_clear_pass, smaller_pass);
+  AddQuad(root_clear_pass, viewport_rect, SK_ColorGREEN);
+
+  // Interior pass quad has smaller visible rect.
+  gfx::Rect interior_visible_rect(30, 30, 40, 40);
+  root_clear_pass->quad_list[0]->visible_rect = interior_visible_rect;
+
+  renderer()->DecideRenderPassAllocationsForFrame(list);
+  renderer()->DrawFrame(&list, NULL);
+  renderer()->GetFramebufferPixels(output.getPixels(), viewport_rect);
+
+  EXPECT_EQ(SK_ColorGREEN, output.getColor(0, 0));
+  EXPECT_EQ(
+      SK_ColorGREEN,
+      output.getColor(viewport_rect.width() - 1, viewport_rect.height() - 1));
+
+  // Part outside visible rect should remain green.
+  EXPECT_EQ(SK_ColorGREEN, output.getColor(smaller_rect.x(), smaller_rect.y()));
+  EXPECT_EQ(
+      SK_ColorGREEN,
+      output.getColor(smaller_rect.right() - 1, smaller_rect.bottom() - 1));
+
+  EXPECT_EQ(
+      SK_ColorMAGENTA,
+      output.getColor(interior_visible_rect.x(), interior_visible_rect.y()));
+  EXPECT_EQ(SK_ColorMAGENTA,
+            output.getColor(interior_visible_rect.right() - 1,
+                            interior_visible_rect.bottom() - 1));
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/quads/render_pass.h b/cc/quads/render_pass.h
index 224a050..7889829 100644
--- a/cc/quads/render_pass.h
+++ b/cc/quads/render_pass.h
@@ -11,7 +11,6 @@
 #include "base/callback.h"
 #include "base/containers/hash_tables.h"
 #include "cc/base/cc_export.h"
-#include "cc/base/scoped_ptr_hash_map.h"
 #include "cc/base/scoped_ptr_vector.h"
 #include "skia/ext/refptr.h"
 #include "third_party/skia/include/core/SkColor.h"
diff --git a/cc/resources/bitmap_content_layer_updater.cc b/cc/resources/bitmap_content_layer_updater.cc
index 3d90eb0..75b99c5 100644
--- a/cc/resources/bitmap_content_layer_updater.cc
+++ b/cc/resources/bitmap_content_layer_updater.cc
@@ -43,8 +43,7 @@
     scoped_ptr<LayerPainter> painter,
     RenderingStatsInstrumentation* stats_instrumentation,
     int layer_id)
-    : ContentLayerUpdater(painter.Pass(), stats_instrumentation, layer_id),
-      opaque_(false) {}
+    : ContentLayerUpdater(painter.Pass(), stats_instrumentation, layer_id) {}
 
 BitmapContentLayerUpdater::~BitmapContentLayerUpdater() {}
 
@@ -67,13 +66,13 @@
         devtools_instrumentation::kPaintSetup, layer_id_);
     canvas_size_ = content_rect.size();
     canvas_ = skia::AdoptRef(skia::CreateBitmapCanvas(
-        canvas_size_.width(), canvas_size_.height(), opaque_));
+        canvas_size_.width(), canvas_size_.height(), layer_is_opaque_));
   }
 
   base::TimeTicks start_time =
       rendering_stats_instrumentation_->StartRecording();
   PaintContents(canvas_.get(),
-                content_rect,
+                content_rect.origin(),
                 contents_width_scale,
                 contents_height_scale,
                 resulting_opaque_rect);
@@ -108,11 +107,12 @@
 }
 
 void BitmapContentLayerUpdater::SetOpaque(bool opaque) {
-  if (opaque != opaque_) {
+  if (opaque != layer_is_opaque_) {
     canvas_.clear();
     canvas_size_ = gfx::Size();
   }
-  opaque_ = opaque;
+
+  ContentLayerUpdater::SetOpaque(opaque);
 }
 
 }  // namespace cc
diff --git a/cc/resources/bitmap_skpicture_content_layer_updater.cc b/cc/resources/bitmap_skpicture_content_layer_updater.cc
index 2c00099..2db3f41 100644
--- a/cc/resources/bitmap_skpicture_content_layer_updater.cc
+++ b/cc/resources/bitmap_skpicture_content_layer_updater.cc
@@ -9,8 +9,8 @@
 #include "cc/resources/layer_painter.h"
 #include "cc/resources/prioritized_resource.h"
 #include "cc/resources/resource_update_queue.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
 #include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkDevice.h"
 
 namespace cc {
 
@@ -28,7 +28,7 @@
       SkBitmap::kARGB_8888_Config, source_rect.width(), source_rect.height());
   bitmap_.allocPixels();
   bitmap_.setIsOpaque(updater_->layer_is_opaque());
-  SkDevice device(bitmap_);
+  SkBitmapDevice device(bitmap_);
   SkCanvas canvas(&device);
   updater_->PaintContentsRect(&canvas, source_rect);
 
diff --git a/cc/resources/content_layer_updater.cc b/cc/resources/content_layer_updater.cc
index c9c2ecc..2e73c4b 100644
--- a/cc/resources/content_layer_updater.cc
+++ b/cc/resources/content_layer_updater.cc
@@ -9,6 +9,7 @@
 #include "cc/debug/rendering_stats_instrumentation.h"
 #include "cc/resources/layer_painter.h"
 #include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkDevice.h"
 #include "third_party/skia/include/core/SkPaint.h"
 #include "third_party/skia/include/core/SkRect.h"
 #include "third_party/skia/include/core/SkScalar.h"
@@ -23,7 +24,8 @@
     int layer_id)
     : rendering_stats_instrumentation_(stats_instrumentation),
       layer_id_(layer_id),
-      painter_(painter.Pass()) {}
+      painter_(painter.Pass()),
+      layer_is_opaque_(false) {}
 
 ContentLayerUpdater::~ContentLayerUpdater() {}
 
@@ -33,14 +35,17 @@
 }
 
 void ContentLayerUpdater::PaintContents(SkCanvas* canvas,
-                                        gfx::Rect content_rect,
+                                        gfx::Point origin,
                                         float contents_width_scale,
                                         float contents_height_scale,
                                         gfx::Rect* resulting_opaque_rect) {
   TRACE_EVENT0("cc", "ContentLayerUpdater::PaintContents");
   canvas->save();
-  canvas->translate(SkFloatToScalar(-content_rect.x()),
-                    SkFloatToScalar(-content_rect.y()));
+  canvas->translate(SkFloatToScalar(-origin.x()),
+                    SkFloatToScalar(-origin.y()));
+
+  SkBaseDevice* device = canvas->getDevice();
+  gfx::Rect content_rect(origin, gfx::Size(device->width(), device->height()));
 
   gfx::Rect layer_rect = content_rect;
 
@@ -52,12 +57,14 @@
         content_rect, 1.f / contents_width_scale, 1.f / contents_height_scale);
   }
 
-  SkPaint paint;
-  paint.setAntiAlias(false);
-  paint.setXfermodeMode(SkXfermode::kClear_Mode);
   SkRect layer_sk_rect = SkRect::MakeXYWH(
       layer_rect.x(), layer_rect.y(), layer_rect.width(), layer_rect.height());
-  canvas->drawRect(layer_sk_rect, paint);
+
+  // If the layer has opaque contents then there is no need to
+  // clear the canvas before painting.
+  if (!layer_is_opaque_)
+    canvas->clear(SK_ColorTRANSPARENT);
+
   canvas->clipRect(layer_sk_rect);
 
   gfx::RectF opaque_layer_rect;
@@ -71,4 +78,8 @@
   content_rect_ = content_rect;
 }
 
+void ContentLayerUpdater::SetOpaque(bool opaque) {
+  layer_is_opaque_ = opaque;
+}
+
 }  // namespace cc
diff --git a/cc/resources/content_layer_updater.h b/cc/resources/content_layer_updater.h
index 6c8dee3..6959526 100644
--- a/cc/resources/content_layer_updater.h
+++ b/cc/resources/content_layer_updater.h
@@ -22,6 +22,7 @@
 class CC_EXPORT ContentLayerUpdater : public LayerUpdater {
  public:
   void set_rendering_stats_instrumentation(RenderingStatsInstrumentation* rsi);
+  virtual void SetOpaque(bool) OVERRIDE;
 
  protected:
   ContentLayerUpdater(scoped_ptr<LayerPainter> painter,
@@ -30,12 +31,14 @@
   virtual ~ContentLayerUpdater();
 
   void PaintContents(SkCanvas* canvas,
-                     gfx::Rect content_rect,
+                     gfx::Point origin,
                      float contents_width_scale,
                      float contents_height_scale,
                      gfx::Rect* resulting_opaque_rect);
   gfx::Rect content_rect() const { return content_rect_; }
 
+  bool layer_is_opaque() const { return layer_is_opaque_; }
+
   RenderingStatsInstrumentation* rendering_stats_instrumentation_;
   int layer_id_;
 
@@ -43,6 +46,10 @@
   gfx::Rect content_rect_;
   scoped_ptr<LayerPainter> painter_;
 
+ protected:
+  // True when it is known that all output pixels will be opaque.
+  bool layer_is_opaque_;
+
   DISALLOW_COPY_AND_ASSIGN(ContentLayerUpdater);
 };
 
diff --git a/cc/resources/image_raster_worker_pool.cc b/cc/resources/image_raster_worker_pool.cc
index 7e524f6..9862484 100644
--- a/cc/resources/image_raster_worker_pool.cc
+++ b/cc/resources/image_raster_worker_pool.cc
@@ -8,7 +8,7 @@
 #include "base/values.h"
 #include "cc/debug/traced_value.h"
 #include "cc/resources/resource.h"
-#include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
 
 namespace cc {
 
@@ -40,7 +40,7 @@
                      task_->resource()->size().height(),
                      stride_);
     bitmap.setPixels(buffer_);
-    SkDevice device(bitmap);
+    SkBitmapDevice device(bitmap);
     task_->RunOnWorkerThread(&device, thread_index);
   }
   virtual void CompleteOnOriginThread() OVERRIDE {
diff --git a/cc/resources/layer_tiling_data.h b/cc/resources/layer_tiling_data.h
index 51565eb..c5c9a74 100644
--- a/cc/resources/layer_tiling_data.h
+++ b/cc/resources/layer_tiling_data.h
@@ -9,10 +9,10 @@
 
 #include "base/basictypes.h"
 #include "base/containers/hash_tables.h"
+#include "base/containers/scoped_ptr_hash_map.h"
 #include "base/memory/scoped_ptr.h"
 #include "cc/base/cc_export.h"
 #include "cc/base/region.h"
-#include "cc/base/scoped_ptr_hash_map.h"
 #include "cc/base/tiling_data.h"
 #include "ui/gfx/rect.h"
 
@@ -72,7 +72,7 @@
     DISALLOW_COPY_AND_ASSIGN(Tile);
   };
   typedef std::pair<int, int> TileMapKey;
-  typedef ScopedPtrHashMap<TileMapKey, Tile> TileMap;
+  typedef base::ScopedPtrHashMap<TileMapKey, Tile> TileMap;
 
   void AddTile(scoped_ptr<Tile> tile, int i, int j);
   scoped_ptr<Tile> TakeTile(int i, int j);
diff --git a/cc/resources/managed_tile_state.cc b/cc/resources/managed_tile_state.cc
index 6f85331..4583e53 100644
--- a/cc/resources/managed_tile_state.cc
+++ b/cc/resources/managed_tile_state.cc
@@ -12,52 +12,42 @@
 
 scoped_ptr<base::Value> ManagedTileBinAsValue(ManagedTileBin bin) {
   switch (bin) {
-  case NOW_AND_READY_TO_DRAW_BIN:
-      return scoped_ptr<base::Value>(base::Value::CreateStringValue(
-          "NOW_AND_READY_TO_DRAW_BIN"));
-  case NOW_BIN:
-      return scoped_ptr<base::Value>(base::Value::CreateStringValue(
-          "NOW_BIN"));
-  case SOON_BIN:
-      return scoped_ptr<base::Value>(base::Value::CreateStringValue(
-          "SOON_BIN"));
-  case EVENTUALLY_AND_ACTIVE_BIN:
-      return scoped_ptr<base::Value>(base::Value::CreateStringValue(
-          "EVENTUALLY_AND_ACTIVE_BIN"));
-  case EVENTUALLY_BIN:
-      return scoped_ptr<base::Value>(base::Value::CreateStringValue(
-          "EVENTUALLY_BIN"));
-  case NEVER_AND_ACTIVE_BIN:
-      return scoped_ptr<base::Value>(base::Value::CreateStringValue(
-          "NEVER_AND_ACTIVE_BIN"));
-  case NEVER_BIN:
-      return scoped_ptr<base::Value>(base::Value::CreateStringValue(
-          "NEVER_BIN"));
-  default:
-      DCHECK(false) << "Unrecognized ManagedTileBin value " << bin;
-      return scoped_ptr<base::Value>(base::Value::CreateStringValue(
-          "<unknown ManagedTileBin value>"));
+    case NOW_AND_READY_TO_DRAW_BIN:
+      return scoped_ptr<base::Value>(
+          base::Value::CreateStringValue("NOW_AND_READY_TO_DRAW_BIN"));
+    case NOW_BIN:
+      return scoped_ptr<base::Value>(
+          base::Value::CreateStringValue("NOW_BIN"));
+    case SOON_BIN:
+      return scoped_ptr<base::Value>(
+          base::Value::CreateStringValue("SOON_BIN"));
+    case EVENTUALLY_AND_ACTIVE_BIN:
+      return scoped_ptr<base::Value>(
+          base::Value::CreateStringValue("EVENTUALLY_AND_ACTIVE_BIN"));
+    case EVENTUALLY_BIN:
+      return scoped_ptr<base::Value>(
+          base::Value::CreateStringValue("EVENTUALLY_BIN"));
+    case AT_LAST_AND_ACTIVE_BIN:
+      return scoped_ptr<base::Value>(
+          base::Value::CreateStringValue("AT_LAST_AND_ACTIVE_BIN"));
+    case AT_LAST_BIN:
+      return scoped_ptr<base::Value>(
+          base::Value::CreateStringValue("AT_LAST_BIN"));
+    case NEVER_BIN:
+      return scoped_ptr<base::Value>(
+          base::Value::CreateStringValue("NEVER_BIN"));
+    case NUM_BINS:
+      NOTREACHED();
+      return scoped_ptr<base::Value>(
+          base::Value::CreateStringValue("Invalid Bin (NUM_BINS)"));
   }
-}
-
-scoped_ptr<base::Value> ManagedTileBinPriorityAsValue(
-    ManagedTileBinPriority bin_priority) {
-  switch (bin_priority) {
-  case HIGH_PRIORITY_BIN:
-      return scoped_ptr<base::Value>(base::Value::CreateStringValue(
-          "HIGH_PRIORITY_BIN"));
-  case LOW_PRIORITY_BIN:
-      return scoped_ptr<base::Value>(base::Value::CreateStringValue(
-          "LOW_PRIORITY_BIN"));
-  default:
-      DCHECK(false) << "Unrecognized ManagedTileBinPriority value";
-      return scoped_ptr<base::Value>(base::Value::CreateStringValue(
-          "<unknown ManagedTileBinPriority value>"));
-  }
+  return scoped_ptr<base::Value>(
+      base::Value::CreateStringValue("Invalid Bin (UNKNOWN)"));
 }
 
 ManagedTileState::ManagedTileState()
     : raster_mode(LOW_QUALITY_RASTER_MODE),
+      bin(NEVER_BIN),
       gpu_memmgr_stats_bin(NEVER_BIN),
       resolution(NON_IDEAL_RESOLUTION),
       required_for_activation(false),
@@ -65,10 +55,8 @@
       distance_to_visible_in_pixels(std::numeric_limits<float>::infinity()),
       visible_and_ready_to_draw(false),
       scheduled_priority(0) {
-  for (int i = 0; i < NUM_TREES; ++i) {
+  for (int i = 0; i < NUM_TREES; ++i)
     tree_bin[i] = NEVER_BIN;
-    bin[i] = NEVER_BIN;
-  }
 }
 
 ManagedTileState::TileVersion::TileVersion()
@@ -87,10 +75,9 @@
     case SOLID_COLOR_MODE:
     case PICTURE_PILE_MODE:
       return true;
-    default:
-      NOTREACHED();
-      return false;
   }
+  NOTREACHED();
+  return false;
 }
 
 size_t ManagedTileState::TileVersion::GPUMemoryUsageInBytes() const {
@@ -106,10 +93,12 @@
   scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue());
   state->SetBoolean("has_resource",
                     tile_versions[raster_mode].resource_.get() != 0);
-  state->Set("bin.0", ManagedTileBinAsValue(bin[ACTIVE_TREE]).release());
-  state->Set("bin.1", ManagedTileBinAsValue(bin[PENDING_TREE]).release());
+  state->Set("tree_bin.0",
+             ManagedTileBinAsValue(tree_bin[ACTIVE_TREE]).release());
+  state->Set("tree_bin.1",
+             ManagedTileBinAsValue(tree_bin[PENDING_TREE]).release());
   state->Set("gpu_memmgr_stats_bin",
-      ManagedTileBinAsValue(bin[ACTIVE_TREE]).release());
+      ManagedTileBinAsValue(gpu_memmgr_stats_bin).release());
   state->Set("resolution", TileResolutionAsValue(resolution).release());
   state->Set("time_to_needed_in_seconds",
       MathUtil::AsValueSafely(time_to_needed_in_seconds).release());
diff --git a/cc/resources/managed_tile_state.h b/cc/resources/managed_tile_state.h
index 113c532..8b72917 100644
--- a/cc/resources/managed_tile_state.h
+++ b/cc/resources/managed_tile_state.h
@@ -22,9 +22,10 @@
   SOON_BIN = 2,                   // Impl-side version of prepainting.
   EVENTUALLY_AND_ACTIVE_BIN = 3,  // Nice to have, and has a task or resource.
   EVENTUALLY_BIN = 4,             // Nice to have, if we've got memory and time.
-  NEVER_AND_ACTIVE_BIN = 5,       // Dont bother, but has a task or resource.
-  NEVER_BIN = 6,                  // Dont bother.
-  NUM_BINS = 7
+  AT_LAST_AND_ACTIVE_BIN = 5,     // Only do this after all other bins.
+  AT_LAST_BIN = 6,                // Only do this after all other bins.
+  NEVER_BIN = 7,                  // Dont bother.
+  NUM_BINS = 8
   // NOTE: Be sure to update ManagedTileBinAsValue and kBinPolicyMap when adding
   // or reordering fields.
 };
@@ -48,8 +49,7 @@
       enum Mode {
         RESOURCE_MODE,
         SOLID_COLOR_MODE,
-        PICTURE_PILE_MODE,
-        NUM_MODES
+        PICTURE_PILE_MODE
       };
 
       TileVersion();
@@ -132,15 +132,7 @@
   TileVersion tile_versions[NUM_RASTER_MODES];
   RasterMode raster_mode;
 
-  // Ephemeral state, valid only during TileManager::ManageTiles.
-  bool is_in_never_bin_on_both_trees() const {
-    return (bin[HIGH_PRIORITY_BIN] == NEVER_BIN ||
-            bin[HIGH_PRIORITY_BIN] == NEVER_AND_ACTIVE_BIN) &&
-           (bin[LOW_PRIORITY_BIN] == NEVER_BIN ||
-            bin[LOW_PRIORITY_BIN] == NEVER_AND_ACTIVE_BIN);
-  }
-
-  ManagedTileBin bin[NUM_BIN_PRIORITIES];
+  ManagedTileBin bin;
   ManagedTileBin tree_bin[NUM_TREES];
 
   // The bin that the tile would have if the GPU memory manager had
diff --git a/cc/resources/picture_layer_tiling_perftest.cc b/cc/resources/picture_layer_tiling_perftest.cc
new file mode 100644
index 0000000..22dab13
--- /dev/null
+++ b/cc/resources/picture_layer_tiling_perftest.cc
@@ -0,0 +1,186 @@
+// 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 "cc/resources/picture_layer_tiling.h"
+#include "cc/test/fake_picture_layer_tiling_client.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+
+namespace {
+
+static const int kTimeLimitMillis = 2000;
+static const int kWarmupRuns = 5;
+static const int kTimeCheckInterval = 10;
+
+class PictureLayerTilingPerfTest : public testing::Test {
+ public:
+  PictureLayerTilingPerfTest() : num_runs_(0) {}
+
+  virtual void SetUp() OVERRIDE {
+    picture_layer_tiling_client_.SetTileSize(gfx::Size(256, 256));
+    picture_layer_tiling_ = PictureLayerTiling::Create(
+        1, gfx::Size(256 * 50, 256 * 50), &picture_layer_tiling_client_);
+    picture_layer_tiling_->CreateAllTilesForTesting();
+  }
+
+  virtual void TearDown() OVERRIDE {
+    picture_layer_tiling_.reset(NULL);
+  }
+
+  void EndTest() {
+    elapsed_ = base::TimeTicks::HighResNow() - start_time_;
+  }
+
+  void AfterTest(const std::string& test_name) {
+    printf("*RESULT %s: %.2f runs/s\n",
+           test_name.c_str(),
+           num_runs_ / elapsed_.InSecondsF());
+  }
+
+  bool DidRun() {
+    ++num_runs_;
+    if (num_runs_ == kWarmupRuns)
+      start_time_ = base::TimeTicks::HighResNow();
+
+    if (!start_time_.is_null() && (num_runs_ % kTimeCheckInterval) == 0) {
+      base::TimeDelta elapsed = base::TimeTicks::HighResNow() - start_time_;
+      if (elapsed >= base::TimeDelta::FromMilliseconds(kTimeLimitMillis)) {
+        elapsed_ = elapsed;
+        return false;
+      }
+    }
+    return true;
+  }
+
+  void RunInvalidateTest(const std::string& test_name, const Region& region) {
+    start_time_ = base::TimeTicks();
+    num_runs_ = 0;
+    do {
+      picture_layer_tiling_->Invalidate(region);
+    } while (DidRun());
+
+    AfterTest(test_name);
+  }
+
+  void RunUpdateTilePrioritiesStationaryTest(
+      const std::string& test_name,
+      const gfx::Transform& transform) {
+    start_time_ = base::TimeTicks();
+    num_runs_ = 0;
+
+    gfx::Size layer_bounds(50 * 256, 50 * 256);
+    do {
+      picture_layer_tiling_->UpdateTilePriorities(
+        ACTIVE_TREE,
+        layer_bounds,
+        gfx::Rect(layer_bounds),
+        gfx::Rect(layer_bounds),
+        layer_bounds,
+        layer_bounds,
+        1.f,
+        1.f,
+        transform,
+        transform,
+        num_runs_ + 1,
+        250);
+    } while (DidRun());
+
+    AfterTest(test_name);
+  }
+
+  void RunUpdateTilePrioritiesScrollingTest(
+      const std::string& test_name,
+      const gfx::Transform& transform) {
+    start_time_ = base::TimeTicks();
+    num_runs_ = 0;
+
+    gfx::Size layer_bounds(50 * 256, 50 * 256);
+    gfx::Size viewport_size(1024, 768);
+    gfx::Rect viewport_rect(viewport_size);
+    int xoffsets[] = {10, 0, -10, 0};
+    int yoffsets[] = {0, 10, 0, -10};
+    int offsetIndex = 0;
+    int offsetCount = 0;
+    const int maxOffsetCount = 1000;
+    do {
+      picture_layer_tiling_->UpdateTilePriorities(
+        ACTIVE_TREE,
+        viewport_size,
+        viewport_rect,
+        gfx::Rect(layer_bounds),
+        layer_bounds,
+        layer_bounds,
+        1.f,
+        1.f,
+        transform,
+        transform,
+        num_runs_ + 1,
+        250);
+
+      viewport_rect = gfx::Rect(
+        viewport_rect.x() + xoffsets[offsetIndex],
+        viewport_rect.y() + yoffsets[offsetIndex],
+        viewport_rect.width(),
+        viewport_rect.height());
+
+      if (++offsetCount > maxOffsetCount) {
+        offsetCount = 0;
+        offsetIndex = (offsetIndex + 1) % 4;
+      }
+    } while (DidRun());
+
+    AfterTest(test_name);
+  }
+
+ private:
+  FakePictureLayerTilingClient picture_layer_tiling_client_;
+  scoped_ptr<PictureLayerTiling> picture_layer_tiling_;
+
+  base::TimeTicks start_time_;
+  base::TimeDelta elapsed_;
+  int num_runs_;
+};
+
+TEST_F(PictureLayerTilingPerfTest, Invalidate) {
+  Region one_tile(gfx::Rect(256, 256));
+  RunInvalidateTest("invalidation_1x1", one_tile);
+
+  Region half_region(gfx::Rect(25 * 256, 50 * 256));
+  RunInvalidateTest("invalidation_25x50", half_region);
+
+  Region full_region(gfx::Rect(50 * 256, 50 * 256));
+  RunInvalidateTest("invalidation_50x50", full_region);
+}
+
+TEST_F(PictureLayerTilingPerfTest, UpdateTilePriorities) {
+  gfx::Transform transform;
+  RunUpdateTilePrioritiesStationaryTest(
+      "update_tile_priorities_stationary_no_transform",
+      transform);
+  RunUpdateTilePrioritiesScrollingTest(
+      "update_tile_priorities_scrolling_no_transform",
+      transform);
+
+  transform.Rotate(10);
+  RunUpdateTilePrioritiesStationaryTest(
+      "update_tile_priorities_stationary_rotation",
+      transform);
+  RunUpdateTilePrioritiesScrollingTest(
+      "update_tile_priorities_scrolling_rotation",
+      transform);
+
+  transform.ApplyPerspectiveDepth(10);
+  RunUpdateTilePrioritiesStationaryTest(
+      "update_tile_priorities_stationary_perspective",
+      transform);
+  RunUpdateTilePrioritiesScrollingTest(
+      "update_tile_priorities_scrolling_perspective",
+      transform);
+}
+
+}  // namespace
+
+}  // namespace cc
diff --git a/cc/resources/picture_layer_tiling_set.cc b/cc/resources/picture_layer_tiling_set.cc
index 1b0cb5f..14c9cc8 100644
--- a/cc/resources/picture_layer_tiling_set.cc
+++ b/cc/resources/picture_layer_tiling_set.cc
@@ -110,6 +110,15 @@
   return appended;
 }
 
+int PictureLayerTilingSet::NumHighResTilings() const {
+  int num_high_res = 0;
+  for (size_t i = 0; i < tilings_.size(); ++i) {
+    if (tilings_[i]->resolution() == HIGH_RESOLUTION)
+      num_high_res++;
+  }
+  return num_high_res;
+}
+
 PictureLayerTiling* PictureLayerTilingSet::TilingAtScale(float scale) const {
   for (size_t i = 0; i < tilings_.size(); ++i) {
     if (tilings_[i]->contents_scale() == scale)
diff --git a/cc/resources/picture_layer_tiling_set.h b/cc/resources/picture_layer_tiling_set.h
index e498cb9..9a3f00d 100644
--- a/cc/resources/picture_layer_tiling_set.h
+++ b/cc/resources/picture_layer_tiling_set.h
@@ -37,6 +37,7 @@
 
   PictureLayerTiling* AddTiling(float contents_scale);
   size_t num_tilings() const { return tilings_.size(); }
+  int NumHighResTilings() const;
   PictureLayerTiling* tiling_at(size_t idx) { return tilings_[idx]; }
   const PictureLayerTiling* tiling_at(size_t idx) const {
     return tilings_[idx];
diff --git a/cc/resources/picture_pile_base.cc b/cc/resources/picture_pile_base.cc
index d982f9f..0352d30 100644
--- a/cc/resources/picture_pile_base.cc
+++ b/cc/resources/picture_pile_base.cc
@@ -152,12 +152,10 @@
 
 void PicturePileBase::UpdateRecordedRegion() {
   recorded_region_.Clear();
-  for (int x = 0; x < num_tiles_x(); ++x) {
-    for (int y = 0; y < num_tiles_y(); ++y) {
-      if (!HasRecordingAt(x, y))
-        continue;
-      recorded_region_.Union(tile_bounds(x, y));
-    }
+  for (PictureListMap::iterator it = picture_list_map_.begin();
+       it != picture_list_map_.end(); ++it) {
+    const PictureListMapKey& key = it->first;
+    recorded_region_.Union(tile_bounds(key.first, key.second));
   }
 }
 
diff --git a/cc/resources/pixel_buffer_raster_worker_pool.cc b/cc/resources/pixel_buffer_raster_worker_pool.cc
index b0e74c1..5685b60 100644
--- a/cc/resources/pixel_buffer_raster_worker_pool.cc
+++ b/cc/resources/pixel_buffer_raster_worker_pool.cc
@@ -9,7 +9,7 @@
 #include "base/values.h"
 #include "cc/debug/traced_value.h"
 #include "cc/resources/resource.h"
-#include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
 
 #if defined(OS_ANDROID)
 #include "base/android/sys_utils.h"
@@ -46,7 +46,7 @@
                      task_->resource()->size().width(),
                      task_->resource()->size().height());
     bitmap.setPixels(buffer_);
-    SkDevice device(bitmap);
+    SkBitmapDevice device(bitmap);
     needs_upload_ = task_->RunOnWorkerThread(&device, thread_index);
   }
   virtual void CompleteOnOriginThread() OVERRIDE {
diff --git a/cc/resources/platform_color.h b/cc/resources/platform_color.h
index 09a606a..bd27d82 100644
--- a/cc/resources/platform_color.h
+++ b/cc/resources/platform_color.h
@@ -26,19 +26,16 @@
 
   // Returns the most efficient texture format for this platform.
   static GLenum BestTextureFormat(bool supports_bgra8888) {
-    GLenum texture_format = GL_RGBA;
     switch (Format()) {
-      case SOURCE_FORMAT_RGBA8:
-        break;
       case SOURCE_FORMAT_BGRA8:
         if (supports_bgra8888)
-          texture_format = GL_BGRA_EXT;
-        break;
-      default:
-        NOTREACHED();
-        break;
+          return GL_BGRA_EXT;
+        return GL_RGBA;
+      case SOURCE_FORMAT_RGBA8:
+        return GL_RGBA;
     }
-    return texture_format;
+    NOTREACHED();
+    return GL_RGBA;
   }
 
   // Return true if the given texture format has the same component order
@@ -49,10 +46,9 @@
         return texture_format == GL_RGBA;
       case SOURCE_FORMAT_BGRA8:
         return texture_format == GL_BGRA_EXT;
-      default:
-        NOTREACHED();
-        return false;
     }
+    NOTREACHED();
+    return false;
   }
 
  private:
diff --git a/cc/resources/prioritized_tile_set.cc b/cc/resources/prioritized_tile_set.cc
index d1cc5b7..6c5c472 100644
--- a/cc/resources/prioritized_tile_set.cc
+++ b/cc/resources/prioritized_tile_set.cc
@@ -18,9 +18,6 @@
     const ManagedTileState& ams = a->managed_state();
     const ManagedTileState& bms = b->managed_state();
 
-    if (ams.bin[LOW_PRIORITY_BIN] != bms.bin[LOW_PRIORITY_BIN])
-      return ams.bin[LOW_PRIORITY_BIN] < bms.bin[LOW_PRIORITY_BIN];
-
     if (ams.required_for_activation != bms.required_for_activation)
       return ams.required_for_activation;
 
@@ -51,13 +48,14 @@
 void SortBinTiles(ManagedTileBin bin, TileVector* tiles) {
   switch (bin) {
     case NOW_AND_READY_TO_DRAW_BIN:
+    case NEVER_BIN:
       break;
     case NOW_BIN:
     case SOON_BIN:
     case EVENTUALLY_AND_ACTIVE_BIN:
     case EVENTUALLY_BIN:
-    case NEVER_AND_ACTIVE_BIN:
-    case NEVER_BIN:
+    case AT_LAST_AND_ACTIVE_BIN:
+    case AT_LAST_BIN:
       std::sort(tiles->begin(), tiles->end(), BinComparator());
       break;
     default:
diff --git a/cc/resources/prioritized_tile_set_unittest.cc b/cc/resources/prioritized_tile_set_unittest.cc
index 9325691..45c714f 100644
--- a/cc/resources/prioritized_tile_set_unittest.cc
+++ b/cc/resources/prioritized_tile_set_unittest.cc
@@ -25,9 +25,6 @@
     const ManagedTileState& ams = a->managed_state();
     const ManagedTileState& bms = b->managed_state();
 
-    if (ams.bin[LOW_PRIORITY_BIN] != bms.bin[LOW_PRIORITY_BIN])
-      return ams.bin[LOW_PRIORITY_BIN] < bms.bin[LOW_PRIORITY_BIN];
-
     if (ams.required_for_activation != bms.required_for_activation)
       return ams.required_for_activation;
 
@@ -310,8 +307,8 @@
   EXPECT_EQ(20, i);
 }
 
-TEST_F(PrioritizedTileSetTest, NeverAndActiveBin) {
-  // Ensure that NEVER_AND_ACTIVE_BIN tiles are sorted.
+TEST_F(PrioritizedTileSetTest, AtLastAndActiveBin) {
+  // Ensure that AT_LAST_AND_ACTIVE_BIN tiles are sorted.
 
   PrioritizedTileSet set;
   TilePriority priorities[4] = {
@@ -327,7 +324,7 @@
       tile->SetPriority(ACTIVE_TREE, priorities[priority]);
       tile->SetPriority(PENDING_TREE, priorities[priority]);
       tiles.push_back(tile);
-      set.InsertTile(tile, NEVER_AND_ACTIVE_BIN);
+      set.InsertTile(tile, AT_LAST_AND_ACTIVE_BIN);
     }
   }
 
@@ -344,9 +341,8 @@
   EXPECT_EQ(20, i);
 }
 
-TEST_F(PrioritizedTileSetTest, NeverBin) {
-  // Ensure that NEVER_BIN tiles are sorted, since they might not
-  // be NEVER_BIN on a LOW_PRIORITY tree.
+TEST_F(PrioritizedTileSetTest, AtLastBin) {
+  // Ensure that AT_LAST_BIN tiles are sorted.
 
   PrioritizedTileSet set;
   TilePriority priorities[4] = {
@@ -362,7 +358,7 @@
       tile->SetPriority(ACTIVE_TREE, priorities[priority]);
       tile->SetPriority(PENDING_TREE, priorities[priority]);
       tiles.push_back(tile);
-      set.InsertTile(tile, NEVER_BIN);
+      set.InsertTile(tile, AT_LAST_BIN);
     }
   }
 
@@ -388,16 +384,16 @@
   scoped_refptr<Tile> soon_bin = CreateTile();
   scoped_refptr<Tile> eventually_and_active_bin = CreateTile();
   scoped_refptr<Tile> eventually_bin = CreateTile();
-  scoped_refptr<Tile> never_bin = CreateTile();
-  scoped_refptr<Tile> never_and_active_bin = CreateTile();
+  scoped_refptr<Tile> at_last_bin = CreateTile();
+  scoped_refptr<Tile> at_last_and_active_bin = CreateTile();
 
   PrioritizedTileSet set;
   set.InsertTile(soon_bin, SOON_BIN);
-  set.InsertTile(never_and_active_bin, NEVER_AND_ACTIVE_BIN);
+  set.InsertTile(at_last_and_active_bin, AT_LAST_AND_ACTIVE_BIN);
   set.InsertTile(eventually_bin, EVENTUALLY_BIN);
   set.InsertTile(now_bin, NOW_BIN);
   set.InsertTile(eventually_and_active_bin, EVENTUALLY_AND_ACTIVE_BIN);
-  set.InsertTile(never_bin, NEVER_BIN);
+  set.InsertTile(at_last_bin, AT_LAST_BIN);
   set.InsertTile(now_and_ready_to_draw_bin, NOW_AND_READY_TO_DRAW_BIN);
 
   // Tiles should appear in order.
@@ -412,9 +408,9 @@
   ++it;
   EXPECT_TRUE(*it == eventually_bin.get());
   ++it;
-  EXPECT_TRUE(*it == never_and_active_bin.get());
+  EXPECT_TRUE(*it == at_last_and_active_bin.get());
   ++it;
-  EXPECT_TRUE(*it == never_bin.get());
+  EXPECT_TRUE(*it == at_last_bin.get());
   ++it;
   EXPECT_FALSE(it);
 }
@@ -428,8 +424,8 @@
   std::vector<scoped_refptr<Tile> > soon_bins;
   std::vector<scoped_refptr<Tile> > eventually_and_active_bins;
   std::vector<scoped_refptr<Tile> > eventually_bins;
-  std::vector<scoped_refptr<Tile> > never_bins;
-  std::vector<scoped_refptr<Tile> > never_and_active_bins;
+  std::vector<scoped_refptr<Tile> > at_last_bins;
+  std::vector<scoped_refptr<Tile> > at_last_and_active_bins;
 
   TilePriority priorities[4] = {
       TilePriorityForEventualBin(),
@@ -449,16 +445,16 @@
       soon_bins.push_back(tile);
       eventually_and_active_bins.push_back(tile);
       eventually_bins.push_back(tile);
-      never_bins.push_back(tile);
-      never_and_active_bins.push_back(tile);
+      at_last_bins.push_back(tile);
+      at_last_and_active_bins.push_back(tile);
 
       set.InsertTile(tile, NOW_AND_READY_TO_DRAW_BIN);
       set.InsertTile(tile, NOW_BIN);
       set.InsertTile(tile, SOON_BIN);
       set.InsertTile(tile, EVENTUALLY_AND_ACTIVE_BIN);
       set.InsertTile(tile, EVENTUALLY_BIN);
-      set.InsertTile(tile, NEVER_BIN);
-      set.InsertTile(tile, NEVER_AND_ACTIVE_BIN);
+      set.InsertTile(tile, AT_LAST_BIN);
+      set.InsertTile(tile, AT_LAST_AND_ACTIVE_BIN);
     }
   }
 
@@ -507,20 +503,20 @@
     ++it;
   }
 
-  // Never and active bins are sorted.
-  std::sort(never_and_active_bins.begin(),
-            never_and_active_bins.end(),
+  // At last and active bins are sorted.
+  std::sort(at_last_and_active_bins.begin(),
+            at_last_and_active_bins.end(),
             BinComparator());
-  for (vector_it = never_and_active_bins.begin();
-       vector_it != never_and_active_bins.end();
+  for (vector_it = at_last_and_active_bins.begin();
+       vector_it != at_last_and_active_bins.end();
        ++vector_it) {
     EXPECT_TRUE(*vector_it == *it);
     ++it;
   }
 
-  // Never bins are sorted.
-  std::sort(never_bins.begin(), never_bins.end(), BinComparator());
-  for (vector_it = never_bins.begin(); vector_it != never_bins.end();
+  // At last bins are sorted.
+  std::sort(at_last_bins.begin(), at_last_bins.end(), BinComparator());
+  for (vector_it = at_last_bins.begin(); vector_it != at_last_bins.end();
        ++vector_it) {
     EXPECT_TRUE(*vector_it == *it);
     ++it;
@@ -539,8 +535,8 @@
   std::vector<scoped_refptr<Tile> > soon_bins;
   std::vector<scoped_refptr<Tile> > eventually_and_active_bins;
   std::vector<scoped_refptr<Tile> > eventually_bins;
-  std::vector<scoped_refptr<Tile> > never_bins;
-  std::vector<scoped_refptr<Tile> > never_and_active_bins;
+  std::vector<scoped_refptr<Tile> > at_last_bins;
+  std::vector<scoped_refptr<Tile> > at_last_and_active_bins;
 
   TilePriority priorities[4] = {
       TilePriorityForEventualBin(),
@@ -560,16 +556,16 @@
       soon_bins.push_back(tile);
       eventually_and_active_bins.push_back(tile);
       eventually_bins.push_back(tile);
-      never_bins.push_back(tile);
-      never_and_active_bins.push_back(tile);
+      at_last_bins.push_back(tile);
+      at_last_and_active_bins.push_back(tile);
 
       set.InsertTile(tile, NOW_AND_READY_TO_DRAW_BIN);
       set.InsertTile(tile, NOW_BIN);
       set.InsertTile(tile, SOON_BIN);
       set.InsertTile(tile, EVENTUALLY_AND_ACTIVE_BIN);
       set.InsertTile(tile, EVENTUALLY_BIN);
-      set.InsertTile(tile, NEVER_BIN);
-      set.InsertTile(tile, NEVER_AND_ACTIVE_BIN);
+      set.InsertTile(tile, AT_LAST_BIN);
+      set.InsertTile(tile, AT_LAST_AND_ACTIVE_BIN);
     }
   }
 
@@ -620,16 +616,16 @@
     ++it;
   }
 
-  // Never and active bins are not sorted.
-  for (vector_it = never_and_active_bins.begin();
-       vector_it != never_and_active_bins.end();
+  // At last and active bins are not sorted.
+  for (vector_it = at_last_and_active_bins.begin();
+       vector_it != at_last_and_active_bins.end();
        ++vector_it) {
     EXPECT_TRUE(*vector_it == *it);
     ++it;
   }
 
-  // Never bins are not sorted.
-  for (vector_it = never_bins.begin(); vector_it != never_bins.end();
+  // At last bins are not sorted.
+  for (vector_it = at_last_bins.begin(); vector_it != at_last_bins.end();
        ++vector_it) {
     EXPECT_TRUE(*vector_it == *it);
     ++it;
@@ -643,17 +639,17 @@
   // we just get two tiles from the iterator.
 
   scoped_refptr<Tile> now_and_ready_to_draw_bin = CreateTile();
-  scoped_refptr<Tile> never_bin = CreateTile();
+  scoped_refptr<Tile> at_last_bin = CreateTile();
 
   PrioritizedTileSet set;
-  set.InsertTile(never_bin, NEVER_BIN);
+  set.InsertTile(at_last_bin, AT_LAST_BIN);
   set.InsertTile(now_and_ready_to_draw_bin, NOW_AND_READY_TO_DRAW_BIN);
 
   // Only two tiles should appear and they should appear in order.
   PrioritizedTileSet::Iterator it(&set, true);
   EXPECT_TRUE(*it == now_and_ready_to_draw_bin.get());
   ++it;
-  EXPECT_TRUE(*it == never_bin.get());
+  EXPECT_TRUE(*it == at_last_bin.get());
   ++it;
   EXPECT_FALSE(it);
 }
@@ -665,13 +661,13 @@
   scoped_refptr<Tile> now_bin = CreateTile();
   scoped_refptr<Tile> soon_bin = CreateTile();
   scoped_refptr<Tile> eventually_bin = CreateTile();
-  scoped_refptr<Tile> never_bin = CreateTile();
+  scoped_refptr<Tile> at_last_bin = CreateTile();
 
   PrioritizedTileSet set;
   set.InsertTile(soon_bin, SOON_BIN);
   set.InsertTile(eventually_bin, EVENTUALLY_BIN);
   set.InsertTile(now_bin, NOW_BIN);
-  set.InsertTile(never_bin, NEVER_BIN);
+  set.InsertTile(at_last_bin, AT_LAST_BIN);
   set.InsertTile(now_and_ready_to_draw_bin, NOW_AND_READY_TO_DRAW_BIN);
 
   // Tiles should appear in order.
@@ -684,7 +680,7 @@
   ++it;
   EXPECT_TRUE(*it == eventually_bin.get());
   ++it;
-  EXPECT_TRUE(*it == never_bin.get());
+  EXPECT_TRUE(*it == at_last_bin.get());
   ++it;
   EXPECT_FALSE(it);
 
@@ -712,7 +708,7 @@
   EXPECT_TRUE(third_it);
   EXPECT_TRUE(*third_it == soon_bin.get());
   EXPECT_TRUE(second_it);
-  EXPECT_TRUE(*second_it == never_bin.get());
+  EXPECT_TRUE(*second_it == at_last_bin.get());
   EXPECT_FALSE(it);
 
   ++second_it;
diff --git a/cc/resources/raster_mode.cc b/cc/resources/raster_mode.cc
index b35bc0b..47d6344 100644
--- a/cc/resources/raster_mode.cc
+++ b/cc/resources/raster_mode.cc
@@ -21,7 +21,6 @@
     case LOW_QUALITY_RASTER_MODE:
       return scoped_ptr<base::Value>(
           base::Value::CreateStringValue("LOW_QUALITY_RASTER_MODE"));
-    case NUM_RASTER_MODES:
     default:
       NOTREACHED() << "Unrecognized RasterMode value " << raster_mode;
       return scoped_ptr<base::Value>(
diff --git a/cc/resources/raster_worker_pool.cc b/cc/resources/raster_worker_pool.cc
index aced03f..15b8aa4 100644
--- a/cc/resources/raster_worker_pool.cc
+++ b/cc/resources/raster_worker_pool.cc
@@ -89,7 +89,7 @@
     analysis_.is_solid_color &= kUseColorEstimator;
   }
 
-  bool RunRasterOnThread(SkDevice* device, unsigned thread_index) {
+  bool RunRasterOnThread(SkBaseDevice* device, unsigned thread_index) {
     TRACE_EVENT2(
         benchmark_instrumentation::kCategory,
         benchmark_instrumentation::kRunRasterOnThread,
@@ -153,7 +153,7 @@
   }
 
   // Overridden from internal::RasterWorkerPoolTask:
-  virtual bool RunOnWorkerThread(SkDevice* device, unsigned thread_index)
+  virtual bool RunOnWorkerThread(SkBaseDevice* device, unsigned thread_index)
       OVERRIDE {
     RunAnalysisOnThread(thread_index);
     return RunRasterOnThread(device, thread_index);
diff --git a/cc/resources/raster_worker_pool.h b/cc/resources/raster_worker_pool.h
index 8c4b493..a53e52f 100644
--- a/cc/resources/raster_worker_pool.h
+++ b/cc/resources/raster_worker_pool.h
@@ -14,8 +14,8 @@
 #include "cc/resources/tile_priority.h"
 #include "cc/resources/worker_pool.h"
 #include "third_party/khronos/GLES2/gl2.h"
-
-class SkDevice;
+// TODO(robertphillips): change this to "class SkBaseDevice;"
+#include "third_party/skia/include/core/SkDevice.h"
 
 namespace skia {
 class LazyPixelRef;
@@ -37,7 +37,8 @@
   // Returns true if |device| was written to. False indicate that
   // the content of |device| is undefined and the resource doesn't
   // need to be initialized.
-  virtual bool RunOnWorkerThread(SkDevice* device, unsigned thread_index) = 0;
+  virtual bool RunOnWorkerThread(SkBaseDevice* device,
+                                 unsigned thread_index) = 0;
   virtual void CompleteOnOriginThread() = 0;
 
   void DidRun(bool was_canceled);
diff --git a/cc/resources/raster_worker_pool_perftest.cc b/cc/resources/raster_worker_pool_perftest.cc
index 9215950..7cc5ee9 100644
--- a/cc/resources/raster_worker_pool_perftest.cc
+++ b/cc/resources/raster_worker_pool_perftest.cc
@@ -5,6 +5,7 @@
 #include "cc/resources/raster_worker_pool.h"
 
 #include "base/time/time.h"
+#include "cc/test/lap_timer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cc {
@@ -120,7 +121,10 @@
 
 class RasterWorkerPoolPerfTest : public testing::Test {
  public:
-  RasterWorkerPoolPerfTest() : num_runs_(0) {}
+  RasterWorkerPoolPerfTest()
+      : timer_(kWarmupRuns,
+               base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
+               kTimeCheckInterval) {}
 
   // Overridden from testing::Test:
   virtual void SetUp() OVERRIDE {
@@ -130,31 +134,10 @@
     raster_worker_pool_->Shutdown();
   }
 
-  void EndTest() {
-    elapsed_ = base::TimeTicks::HighResNow() - start_time_;
-  }
-
   void AfterTest(const std::string test_name) {
     // Format matches chrome/test/perf/perf_test.h:PrintResult
-    printf("*RESULT %s: %.2f runs/s\n",
-           test_name.c_str(),
-           num_runs_ / elapsed_.InSecondsF());
-  }
-
-  bool DidRun() {
-    ++num_runs_;
-    if (num_runs_ == kWarmupRuns)
-      start_time_ = base::TimeTicks::HighResNow();
-
-    if (!start_time_.is_null() && (num_runs_ % kTimeCheckInterval) == 0) {
-      base::TimeDelta elapsed = base::TimeTicks::HighResNow() - start_time_;
-      if (elapsed >= base::TimeDelta::FromMilliseconds(kTimeLimitMillis)) {
-        elapsed_ = elapsed;
-        return false;
-      }
-    }
-
-    return true;
+    printf(
+        "*RESULT %s: %.2f runs/s\n", test_name.c_str(), timer_.LapsPerSecond());
   }
 
   void CreateTasks(RasterWorkerPool::RasterTask::Queue* tasks,
@@ -201,14 +184,14 @@
   void RunBuildTaskGraphTest(const std::string test_name,
                              unsigned num_raster_tasks,
                              unsigned num_image_decode_tasks) {
-    start_time_ = base::TimeTicks();
-    num_runs_ = 0;
+    timer_.Reset();
     RasterWorkerPool::RasterTask::Queue tasks;
     CreateTasks(&tasks, num_raster_tasks, num_image_decode_tasks);
     raster_worker_pool_->SetRasterTasks(&tasks);
     do {
       raster_worker_pool_->BuildTaskGraph();
-    } while (DidRun());
+      timer_.NextLap();
+    } while (!timer_.HasTimeLimitExpired());
 
     AfterTest(test_name);
   }
@@ -219,9 +202,7 @@
   static void OnImageDecodeTaskCompleted(bool was_canceled) {}
 
   scoped_ptr<PerfRasterWorkerPool> raster_worker_pool_;
-  base::TimeTicks start_time_;
-  base::TimeDelta elapsed_;
-  int num_runs_;
+  LapTimer timer_;
 };
 
 TEST_F(RasterWorkerPoolPerfTest, BuildTaskGraph) {
diff --git a/cc/resources/raster_worker_pool_unittest.cc b/cc/resources/raster_worker_pool_unittest.cc
index 39a8a48..ac148bd 100644
--- a/cc/resources/raster_worker_pool_unittest.cc
+++ b/cc/resources/raster_worker_pool_unittest.cc
@@ -34,7 +34,7 @@
         did_raster_(false) {}
 
   // Overridden from internal::WorkerPoolTask:
-  virtual bool RunOnWorkerThread(SkDevice* device, unsigned thread_index)
+  virtual bool RunOnWorkerThread(SkBaseDevice* device, unsigned thread_index)
       OVERRIDE {
     did_raster_ = true;
     return true;
diff --git a/cc/resources/resource_pool.cc b/cc/resources/resource_pool.cc
index 2d455a0..afcc114 100644
--- a/cc/resources/resource_pool.cc
+++ b/cc/resources/resource_pool.cc
@@ -47,12 +47,8 @@
        it != unused_resources_.end(); ++it) {
     Resource* resource = *it;
 
-    // TODO(epenner): It would be nice to DCHECK that this
-    // doesn't happen two frames in a row for any resource
-    // in this pool.
     if (!resource_provider_->CanLockForWrite(resource->id()))
       continue;
-
     if (resource->size() != size)
       continue;
     if (resource->format() != format)
diff --git a/cc/resources/resource_provider.cc b/cc/resources/resource_provider.cc
index 1911d86..9779a54 100644
--- a/cc/resources/resource_provider.cc
+++ b/cc/resources/resource_provider.cc
@@ -709,24 +709,14 @@
 
   default_resource_type_ = GLTexture;
 
-  std::string extensions_string =
-      UTF16ToASCII(context3d->getString(GL_EXTENSIONS));
-  std::vector<std::string> extensions;
-  base::SplitString(extensions_string, ' ', &extensions);
-  bool use_map_sub = false;
-  bool use_bgra = false;
-  for (size_t i = 0; i < extensions.size(); ++i) {
-    if (extensions[i] == "GL_EXT_texture_storage")
-      use_texture_storage_ext_ = true;
-    else if (extensions[i] == "GL_ANGLE_texture_usage")
-      use_texture_usage_hint_ = true;
-    else if (extensions[i] == "GL_CHROMIUM_map_sub")
-      use_map_sub = true;
-    else if (extensions[i] == "GL_CHROMIUM_shallow_flush")
-      use_shallow_flush_ = true;
-    else if (extensions[i] == "GL_EXT_texture_format_BGRA8888")
-      use_bgra = true;
-  }
+  const ContextProvider::Capabilities& caps =
+      output_surface_->context_provider()->ContextCapabilities();
+
+  bool use_map_sub = caps.map_sub;
+  bool use_bgra = caps.texture_format_bgra8888;
+  use_texture_storage_ext_ = caps.texture_storage;
+  use_shallow_flush_ = caps.shallow_flush;
+  use_texture_usage_hint_ = caps.texture_usage;
 
   texture_uploader_ =
       TextureUploader::Create(context3d, use_map_sub, use_shallow_flush_);
@@ -1167,7 +1157,7 @@
     if (!resource->gl_upload_query_id)
       resource->gl_upload_query_id = context3d->createQueryEXT();
     context3d->beginQueryEXT(
-        GL_ASYNC_PIXEL_TRANSFERS_COMPLETED_CHROMIUM,
+        GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM,
         resource->gl_upload_query_id);
     if (allocate) {
       context3d->asyncTexImage2DCHROMIUM(GL_TEXTURE_2D,
@@ -1190,7 +1180,7 @@
                                             GL_UNSIGNED_BYTE,
                                             NULL);
     }
-    context3d->endQueryEXT(GL_ASYNC_PIXEL_TRANSFERS_COMPLETED_CHROMIUM);
+    context3d->endQueryEXT(GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM);
     context3d->bindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, 0);
   }
 
diff --git a/cc/resources/resource_provider.h b/cc/resources/resource_provider.h
index 146ceb1..412c92b 100644
--- a/cc/resources/resource_provider.h
+++ b/cc/resources/resource_provider.h
@@ -324,13 +324,6 @@
   // Indicates if we can currently lock this resource for write.
   bool CanLockForWrite(ResourceId id);
 
-  ContextProvider* offscreen_context_provider() {
-    return offscreen_context_provider_.get();
-  }
-  void set_offscreen_context_provider(
-      scoped_refptr<ContextProvider> offscreen_context_provider) {
-    offscreen_context_provider_ = offscreen_context_provider;
-  }
   static GLint GetActiveTextureUnit(WebKit::WebGraphicsContext3D* context);
 
  private:
@@ -442,8 +435,6 @@
   int max_texture_size_;
   GLenum best_texture_format_;
 
-  scoped_refptr<cc::ContextProvider> offscreen_context_provider_;
-
   base::ThreadChecker thread_checker_;
 
   scoped_refptr<Fence> current_read_lock_fence_;
diff --git a/cc/resources/resource_update_controller_unittest.cc b/cc/resources/resource_update_controller_unittest.cc
index e7c59bf..ccbfadd 100644
--- a/cc/resources/resource_update_controller_unittest.cc
+++ b/cc/resources/resource_update_controller_unittest.cc
@@ -35,8 +35,9 @@
 class WebGraphicsContext3DForUploadTest : public TestWebGraphicsContext3D {
  public:
   explicit WebGraphicsContext3DForUploadTest(ResourceUpdateControllerTest* test)
-      : test_(test),
-        support_shallow_flush_(true) {}
+      : test_(test) {
+    test_capabilities_.shallow_flush = true;
+  }
 
   virtual void flush(void) OVERRIDE;
   virtual void shallowFlushCHROMIUM(void) OVERRIDE;
@@ -52,12 +53,6 @@
       const void* pixels) OVERRIDE;
   virtual GrGLInterface* onCreateGrGLInterface() OVERRIDE { return NULL; }
 
-  virtual WebString getString(WGC3Denum name) OVERRIDE {
-    if (support_shallow_flush_)
-      return WebString("GL_CHROMIUM_shallow_flush");
-    return WebString("");
-  }
-
   virtual void getQueryObjectuivEXT(
       WebGLId id,
       WGC3Denum pname,
@@ -65,7 +60,6 @@
 
  private:
   ResourceUpdateControllerTest* test_;
-  bool support_shallow_flush_;
 };
 
 class ResourceUpdateControllerTest : public Test {
diff --git a/cc/resources/skpicture_content_layer_updater.cc b/cc/resources/skpicture_content_layer_updater.cc
index e44dbc2..79769bc 100644
--- a/cc/resources/skpicture_content_layer_updater.cc
+++ b/cc/resources/skpicture_content_layer_updater.cc
@@ -17,8 +17,7 @@
     scoped_ptr<LayerPainter> painter,
     RenderingStatsInstrumentation* stats_instrumentation,
     int layer_id)
-    : ContentLayerUpdater(painter.Pass(), stats_instrumentation, layer_id),
-      layer_is_opaque_(false) {}
+    : ContentLayerUpdater(painter.Pass(), stats_instrumentation, layer_id) {}
 
 SkPictureContentLayerUpdater::~SkPictureContentLayerUpdater() {}
 
@@ -33,7 +32,7 @@
   base::TimeTicks start_time =
       rendering_stats_instrumentation_->StartRecording();
   PaintContents(canvas,
-                content_rect,
+                content_rect.origin(),
                 contents_width_scale,
                 contents_height_scale,
                 resulting_opaque_rect);
@@ -49,8 +48,4 @@
   canvas->drawPicture(picture_);
 }
 
-void SkPictureContentLayerUpdater::SetOpaque(bool opaque) {
-  layer_is_opaque_ = opaque;
-}
-
 }  // namespace cc
diff --git a/cc/resources/skpicture_content_layer_updater.h b/cc/resources/skpicture_content_layer_updater.h
index 41a9f01..511d8ee 100644
--- a/cc/resources/skpicture_content_layer_updater.h
+++ b/cc/resources/skpicture_content_layer_updater.h
@@ -20,9 +20,6 @@
 // FrameBufferSkPictureContentLayerUpdater are two examples of such
 // implementations.
 class SkPictureContentLayerUpdater : public ContentLayerUpdater {
- public:
-  virtual void SetOpaque(bool opaque) OVERRIDE;
-
  protected:
   SkPictureContentLayerUpdater(
       scoped_ptr<LayerPainter> painter,
@@ -37,13 +34,9 @@
                                gfx::Rect* resulting_opaque_rect) OVERRIDE;
   void DrawPicture(SkCanvas* canvas);
 
-  bool layer_is_opaque() const { return layer_is_opaque_; }
-
  private:
   // Recording canvas.
   SkPicture picture_;
-  // True when it is known that all output pixels will be opaque.
-  bool layer_is_opaque_;
 
   DISALLOW_COPY_AND_ASSIGN(SkPictureContentLayerUpdater);
 };
diff --git a/cc/resources/texture_mailbox.cc b/cc/resources/texture_mailbox.cc
index 0d47874..2a8f570 100644
--- a/cc/resources/texture_mailbox.cc
+++ b/cc/resources/texture_mailbox.cc
@@ -4,6 +4,7 @@
 
 #include "cc/resources/texture_mailbox.h"
 
+#include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "third_party/khronos/GLES2/gl2.h"
 
@@ -97,15 +98,15 @@
   return shared_memory_ && shared_memory_->handle() == handle;
 }
 
-void TextureMailbox::RunReleaseCallback(unsigned sync_point,
-                                        bool lost_resource) const {
-  if (!callback_.is_null())
-    callback_.Run(sync_point, lost_resource);
+void TextureMailbox::SetName(const gpu::Mailbox& name) {
+  DCHECK(shared_memory_ == NULL);
+  name_ = name;
 }
 
-void TextureMailbox::SetName(const gpu::Mailbox& other) {
-  DCHECK(shared_memory_ == NULL);
-  name_.SetName(other.name);
+void TextureMailbox::RunReleaseCallback(unsigned sync_point,
+                                        bool lost_resource) {
+  if (!callback_.is_null())
+    base::ResetAndReturn(&callback_).Run(sync_point, lost_resource);
 }
 
 TextureMailbox TextureMailbox::CopyWithNewCallback(
diff --git a/cc/resources/texture_mailbox.h b/cc/resources/texture_mailbox.h
index 855287d..9490939 100644
--- a/cc/resources/texture_mailbox.h
+++ b/cc/resources/texture_mailbox.h
@@ -47,12 +47,9 @@
   bool ContainsMailbox(const gpu::Mailbox&) const;
   bool ContainsHandle(base::SharedMemoryHandle handle) const;
 
-  const ReleaseCallback& callback() const { return callback_; }
   const int8* data() const { return name_.name; }
   const gpu::Mailbox& name() const { return name_; }
   void ResetSyncPoint() { sync_point_ = 0; }
-  void RunReleaseCallback(unsigned sync_point, bool lost_resource) const;
-  void SetName(const gpu::Mailbox&);
   unsigned target() const { return target_; }
   unsigned sync_point() const { return sync_point_; }
 
@@ -60,7 +57,16 @@
   gfx::Size shared_memory_size() const { return shared_memory_size_; }
   size_t shared_memory_size_in_bytes() const;
 
+  // TODO(danakj): ReleaseCallback should be separate from this class, and stop
+  // storing a TextureMailbox in ResourceProvider. Then we can remove this.
+  void SetName(const gpu::Mailbox& name);
+
+  // TODO(danakj): ReleaseCallback should be a separate scoped_ptr outside this
+  // class to avoid silently adding references to the callback's internals.
+  void RunReleaseCallback(unsigned sync_point, bool lost_resource);
+
   TextureMailbox CopyWithNewCallback(const ReleaseCallback& callback) const;
+  const ReleaseCallback& callback() const { return callback_; }
 
  private:
   gpu::Mailbox name_;
diff --git a/cc/resources/tile.cc b/cc/resources/tile.cc
index 26e7841..7142038 100644
--- a/cc/resources/tile.cc
+++ b/cc/resources/tile.cc
@@ -42,11 +42,17 @@
 }
 
 void Tile::SetPriority(WhichTree tree, const TilePriority& priority) {
+  if (priority == priority_[tree])
+    return;
+
   priority_[tree] = priority;
   tile_manager_->DidChangeTilePriority(this);
 }
 
 void Tile::MarkRequiredForActivation() {
+  if (priority_[PENDING_TREE].required_for_activation)
+    return;
+
   priority_[PENDING_TREE].required_for_activation = true;
   tile_manager_->DidChangeTilePriority(this);
 }
diff --git a/cc/resources/tile_manager.cc b/cc/resources/tile_manager.cc
index 6b108d8..3e91167 100644
--- a/cc/resources/tile_manager.cc
+++ b/cc/resources/tile_manager.cc
@@ -32,7 +32,8 @@
     NEVER_BIN,                  // [SOON_BIN]
     NEVER_BIN,                  // [EVENTUALLY_AND_ACTIVE_BIN]
     NEVER_BIN,                  // [EVENTUALLY_BIN]
-    NEVER_BIN,                  // [NEVER_AND_ACTIVE_BIN]
+    NEVER_BIN,                  // [AT_LAST_AND_ACTIVE_BIN]
+    NEVER_BIN,                  // [AT_LAST_BIN]
     NEVER_BIN                   // [NEVER_BIN]
   }, {  // [ALLOW_ABSOLUTE_MINIMUM]
     NOW_AND_READY_TO_DRAW_BIN,  // [NOW_AND_READY_TO_DRAW_BIN]
@@ -40,7 +41,8 @@
     NEVER_BIN,                  // [SOON_BIN]
     NEVER_BIN,                  // [EVENTUALLY_AND_ACTIVE_BIN]
     NEVER_BIN,                  // [EVENTUALLY_BIN]
-    NEVER_BIN,                  // [NEVER_AND_ACTIVE_BIN]
+    NEVER_BIN,                  // [AT_LAST_AND_ACTIVE_BIN]
+    NEVER_BIN,                  // [AT_LAST_BIN]
     NEVER_BIN                   // [NEVER_BIN]
   }, {  // [ALLOW_PREPAINT_ONLY]
     NOW_AND_READY_TO_DRAW_BIN,  // [NOW_AND_READY_TO_DRAW_BIN]
@@ -48,7 +50,8 @@
     SOON_BIN,                   // [SOON_BIN]
     NEVER_BIN,                  // [EVENTUALLY_AND_ACTIVE_BIN]
     NEVER_BIN,                  // [EVENTUALLY_BIN]
-    NEVER_BIN,                  // [NEVER_AND_ACTIVE_BIN]
+    NEVER_BIN,                  // [AT_LAST_AND_ACTIVE_BIN]
+    NEVER_BIN,                  // [AT_LAST_BIN]
     NEVER_BIN                   // [NEVER_BIN]
   }, {  // [ALLOW_ANYTHING]
     NOW_AND_READY_TO_DRAW_BIN,  // [NOW_AND_READY_TO_DRAW_BIN]
@@ -56,7 +59,8 @@
     SOON_BIN,                   // [SOON_BIN]
     EVENTUALLY_AND_ACTIVE_BIN,  // [EVENTUALLY_AND_ACTIVE_BIN]
     EVENTUALLY_BIN,             // [EVENTUALLY_BIN]
-    NEVER_AND_ACTIVE_BIN,       // [NEVER_AND_ACTIVE_BIN]
+    AT_LAST_AND_ACTIVE_BIN,     // [AT_LAST_AND_ACTIVE_BIN]
+    AT_LAST_BIN,                // [AT_LAST_BIN]
     NEVER_BIN                   // [NEVER_BIN]
   }
 };
@@ -78,7 +82,7 @@
 
   if (prio.distance_to_visible_in_pixels ==
       std::numeric_limits<float>::infinity())
-    return is_active ? NEVER_AND_ACTIVE_BIN : NEVER_BIN;
+    return NEVER_BIN;
 
   if (can_be_in_now_bin && prio.time_to_visible_in_seconds == 0)
     return is_ready_to_draw ? NOW_AND_READY_TO_DRAW_BIN : NOW_BIN;
@@ -308,21 +312,28 @@
     TilePriority* high_priority = NULL;
     switch (tree_priority) {
       case SAME_PRIORITY_FOR_BOTH_TREES:
-        mts.bin[HIGH_PRIORITY_BIN] = mts.bin[LOW_PRIORITY_BIN] = combined_bin;
+        mts.bin = combined_bin;
         high_priority = &combined_priority;
         break;
       case SMOOTHNESS_TAKES_PRIORITY:
-        mts.bin[HIGH_PRIORITY_BIN] = mts.tree_bin[ACTIVE_TREE];
-        mts.bin[LOW_PRIORITY_BIN] = mts.tree_bin[PENDING_TREE];
+        mts.bin = mts.tree_bin[ACTIVE_TREE];
         high_priority = &active_priority;
         break;
       case NEW_CONTENT_TAKES_PRIORITY:
-        mts.bin[HIGH_PRIORITY_BIN] = mts.tree_bin[PENDING_TREE];
-        mts.bin[LOW_PRIORITY_BIN] = mts.tree_bin[ACTIVE_TREE];
+        mts.bin = mts.tree_bin[PENDING_TREE];
         high_priority = &pending_priority;
         break;
     }
 
+    // Bump up the priority if we determined it's NEVER_BIN on one tree,
+    // but is still required on the other tree.
+    bool is_in_never_bin_on_both_trees =
+        mts.tree_bin[ACTIVE_TREE] == NEVER_BIN &&
+        mts.tree_bin[PENDING_TREE] == NEVER_BIN;
+
+    if (mts.bin == NEVER_BIN && !is_in_never_bin_on_both_trees)
+      mts.bin = tile_is_active ? AT_LAST_AND_ACTIVE_BIN : AT_LAST_BIN;
+
     DCHECK(high_priority != NULL);
 
     mts.resolution = high_priority->resolution;
@@ -334,7 +345,7 @@
     mts.visible_and_ready_to_draw =
         mts.tree_bin[ACTIVE_TREE] == NOW_AND_READY_TO_DRAW_BIN;
 
-    if (mts.is_in_never_bin_on_both_trees()) {
+    if (mts.bin == NEVER_BIN) {
       FreeResourcesForTile(tile);
       continue;
     }
@@ -346,7 +357,7 @@
     // priority.
     ManagedTileBin priority_bin = mts.visible_and_ready_to_draw
                                   ? NOW_AND_READY_TO_DRAW_BIN
-                                  : mts.bin[HIGH_PRIORITY_BIN];
+                                  : mts.bin;
 
     // Insert the tile into a priority set.
     tiles->InsertTile(tile, priority_bin);
@@ -526,7 +537,7 @@
       continue;
 
     // If the tile is not needed, free it up.
-    if (mts.is_in_never_bin_on_both_trees()) {
+    if (mts.bin == NEVER_BIN) {
       FreeResourcesForTile(tile);
       continue;
     }
diff --git a/cc/resources/tile_manager_perftest.cc b/cc/resources/tile_manager_perftest.cc
index 89a3092..28f3914 100644
--- a/cc/resources/tile_manager_perftest.cc
+++ b/cc/resources/tile_manager_perftest.cc
@@ -10,6 +10,7 @@
 #include "cc/test/fake_picture_pile_impl.h"
 #include "cc/test/fake_tile_manager.h"
 #include "cc/test/fake_tile_manager_client.h"
+#include "cc/test/lap_timer.h"
 #include "cc/test/test_tile_priorities.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
@@ -24,9 +25,13 @@
 
 class TileManagerPerfTest : public testing::Test {
  public:
-  typedef std::vector<scoped_refptr<Tile> > TileVector;
+  typedef std::vector<std::pair<scoped_refptr<Tile>, ManagedTileBin> >
+      TileBinVector;
 
-  TileManagerPerfTest() : num_runs_(0) {}
+  TileManagerPerfTest()
+      : timer_(kWarmupRuns,
+               base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
+               kTimeCheckInterval) {}
 
   // Overridden from testing::Test:
   virtual void SetUp() OVERRIDE {
@@ -53,34 +58,53 @@
     picture_pile_ = NULL;
   }
 
-  void EndTest() {
-    elapsed_ = base::TimeTicks::HighResNow() - start_time_;
-  }
-
   void AfterTest(const std::string test_name) {
     // Format matches chrome/test/perf/perf_test.h:PrintResult
-    printf("*RESULT %s: %.2f runs/s\n",
-           test_name.c_str(),
-           num_runs_ / elapsed_.InSecondsF());
+    printf(
+        "*RESULT %s: %.2f runs/s\n", test_name.c_str(), timer_.LapsPerSecond());
   }
 
-  bool DidRun() {
-    ++num_runs_;
-    if (num_runs_ == kWarmupRuns)
-      start_time_ = base::TimeTicks::HighResNow();
-
-    if (!start_time_.is_null() && (num_runs_ % kTimeCheckInterval) == 0) {
-      base::TimeDelta elapsed = base::TimeTicks::HighResNow() - start_time_;
-      if (elapsed >= base::TimeDelta::FromMilliseconds(kTimeLimitMillis)) {
-        elapsed_ = elapsed;
-        return false;
-      }
+  TilePriority GetTilePriorityFromBin(ManagedTileBin bin) {
+    switch (bin) {
+      case NOW_AND_READY_TO_DRAW_BIN:
+      case NOW_BIN:
+        return TilePriorityForNowBin();
+      case SOON_BIN:
+        return TilePriorityForSoonBin();
+      case EVENTUALLY_AND_ACTIVE_BIN:
+      case EVENTUALLY_BIN:
+        return TilePriorityForEventualBin();
+      case AT_LAST_BIN:
+      case AT_LAST_AND_ACTIVE_BIN:
+      case NEVER_BIN:
+        return TilePriority();
+      default:
+        NOTREACHED();
+        return TilePriority();
     }
-
-    return true;
   }
 
-  void CreateBinTiles(int count, TilePriority priority, TileVector* tiles) {
+  ManagedTileBin GetNextBin(ManagedTileBin bin) {
+    switch (bin) {
+      case NOW_AND_READY_TO_DRAW_BIN:
+      case NOW_BIN:
+        return SOON_BIN;
+      case SOON_BIN:
+        return EVENTUALLY_BIN;
+      case EVENTUALLY_AND_ACTIVE_BIN:
+      case EVENTUALLY_BIN:
+        return NEVER_BIN;
+      case AT_LAST_BIN:
+      case AT_LAST_AND_ACTIVE_BIN:
+      case NEVER_BIN:
+        return NOW_BIN;
+      default:
+        NOTREACHED();
+        return NEVER_BIN;
+    }
+  }
+
+  void CreateBinTiles(int count, ManagedTileBin bin, TileBinVector* tiles) {
     for (int i = 0; i < count; ++i) {
       scoped_refptr<Tile> tile =
           make_scoped_refptr(new Tile(tile_manager_.get(),
@@ -92,30 +116,45 @@
                                       0,
                                       0,
                                       true));
-      tile->SetPriority(ACTIVE_TREE, priority);
-      tile->SetPriority(PENDING_TREE, priority);
-      tiles->push_back(tile);
+      tile->SetPriority(ACTIVE_TREE, GetTilePriorityFromBin(bin));
+      tile->SetPriority(PENDING_TREE, GetTilePriorityFromBin(bin));
+      tiles->push_back(std::make_pair(tile, bin));
     }
   }
 
-  void CreateTiles(int count, TileVector* tiles) {
+  void CreateTiles(int count, TileBinVector* tiles) {
     // Roughly an equal amount of all bins.
     int count_per_bin = count / NUM_BINS;
-    CreateBinTiles(count_per_bin, TilePriorityForNowBin(), tiles);
-    CreateBinTiles(count_per_bin, TilePriorityForSoonBin(), tiles);
-    CreateBinTiles(count_per_bin, TilePriorityForEventualBin(), tiles);
-    CreateBinTiles(count - 3 * count_per_bin, TilePriority(), tiles);
+    CreateBinTiles(count_per_bin, NOW_BIN, tiles);
+    CreateBinTiles(count_per_bin, SOON_BIN, tiles);
+    CreateBinTiles(count_per_bin, EVENTUALLY_BIN, tiles);
+    CreateBinTiles(count - 3 * count_per_bin, NEVER_BIN, tiles);
   }
 
   void RunManageTilesTest(const std::string test_name,
-                          unsigned tile_count) {
-    start_time_ = base::TimeTicks();
-    num_runs_ = 0;
-    TileVector tiles;
+                          unsigned tile_count,
+                          unsigned priority_change_percent) {
+    DCHECK_GE(tile_count, 100u);
+    DCHECK_LE(priority_change_percent, 100u);
+    TileBinVector tiles;
     CreateTiles(tile_count, &tiles);
+    timer_.Reset();
     do {
+      if (priority_change_percent) {
+        for (unsigned i = 0;
+             i < tile_count;
+             i += 100 / priority_change_percent) {
+          Tile* tile = tiles[i].first;
+          ManagedTileBin bin = GetNextBin(tiles[i].second);
+          tile->SetPriority(ACTIVE_TREE, GetTilePriorityFromBin(bin));
+          tile->SetPriority(PENDING_TREE, GetTilePriorityFromBin(bin));
+          tiles[i].second = bin;
+        }
+      }
+
       tile_manager_->ManageTiles();
-    } while (DidRun());
+      timer_.NextLap();
+    } while (!timer_.HasTimeLimitExpired());
 
     AfterTest(test_name);
   }
@@ -128,16 +167,19 @@
   FakeOutputSurfaceClient output_surface_client_;
   scoped_ptr<FakeOutputSurface> output_surface_;
   scoped_ptr<ResourceProvider> resource_provider_;
-
-  base::TimeTicks start_time_;
-  base::TimeDelta elapsed_;
-  int num_runs_;
+  LapTimer timer_;
 };
 
 TEST_F(TileManagerPerfTest, ManageTiles) {
-  RunManageTilesTest("manage_tiles_100", 100);
-  RunManageTilesTest("manage_tiles_1000", 1000);
-  RunManageTilesTest("manage_tiles_10000", 10000);
+  RunManageTilesTest("manage_tiles_100_0", 100, 0);
+  RunManageTilesTest("manage_tiles_1000_0", 1000, 0);
+  RunManageTilesTest("manage_tiles_10000_0", 10000, 0);
+  RunManageTilesTest("manage_tiles_100_10", 100, 10);
+  RunManageTilesTest("manage_tiles_1000_10", 1000, 10);
+  RunManageTilesTest("manage_tiles_10000_10", 10000, 10);
+  RunManageTilesTest("manage_tiles_100_100", 100, 100);
+  RunManageTilesTest("manage_tiles_1000_100", 1000, 100);
+  RunManageTilesTest("manage_tiles_10000_100", 10000, 100);
 }
 
 }  // namespace
diff --git a/cc/resources/tile_priority.cc b/cc/resources/tile_priority.cc
index 970ea1d..eeb87cd 100644
--- a/cc/resources/tile_priority.cc
+++ b/cc/resources/tile_priority.cc
@@ -87,11 +87,10 @@
   case NON_IDEAL_RESOLUTION:
       return scoped_ptr<base::Value>(base::Value::CreateStringValue(
         "NON_IDEAL_RESOLUTION"));
-  default:
-    DCHECK(false) << "Unrecognized TileResolution value " << resolution;
-    return scoped_ptr<base::Value>(base::Value::CreateStringValue(
-        "<unknown TileResolution value>"));
   }
+  DCHECK(false) << "Unrecognized TileResolution value " << resolution;
+  return scoped_ptr<base::Value>(base::Value::CreateStringValue(
+      "<unknown TileResolution value>"));
 }
 
 scoped_ptr<base::Value> TilePriority::AsValue() const {
@@ -176,11 +175,10 @@
   case NEW_CONTENT_TAKES_PRIORITY:
       return scoped_ptr<base::Value>(base::Value::CreateStringValue(
           "NEW_CONTENT_TAKES_PRIORITY"));
-  default:
-      DCHECK(false) << "Unrecognized priority value " << prio;
-      return scoped_ptr<base::Value>(base::Value::CreateStringValue(
-          "<unknown>"));
   }
+  DCHECK(false) << "Unrecognized priority value " << prio;
+  return scoped_ptr<base::Value>(base::Value::CreateStringValue(
+      "<unknown>"));
 }
 
 scoped_ptr<base::Value> GlobalStateThatImpactsTilePriority::AsValue() const {
diff --git a/cc/resources/tile_priority.h b/cc/resources/tile_priority.h
index 5c92d2f..e613dd7 100644
--- a/cc/resources/tile_priority.h
+++ b/cc/resources/tile_priority.h
@@ -106,7 +106,8 @@
   bool operator ==(const TilePriority& other) const {
     return resolution == other.resolution &&
         time_to_visible_in_seconds == other.time_to_visible_in_seconds &&
-        distance_to_visible_in_pixels == other.distance_to_visible_in_pixels;
+        distance_to_visible_in_pixels == other.distance_to_visible_in_pixels &&
+        required_for_activation == other.required_for_activation;
     // No need to compare current_screen_quad which is for debug only and
     // never changes by itself.
   }
diff --git a/cc/resources/worker_pool.h b/cc/resources/worker_pool.h
index c26ed07..cc8c39f 100644
--- a/cc/resources/worker_pool.h
+++ b/cc/resources/worker_pool.h
@@ -10,12 +10,12 @@
 #include <vector>
 
 #include "base/cancelable_callback.h"
+#include "base/containers/scoped_ptr_hash_map.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/message_loop/message_loop.h"
 #include "cc/base/cc_export.h"
-#include "cc/base/scoped_ptr_hash_map.h"
 
 namespace cc {
 namespace internal {
@@ -113,7 +113,7 @@
   // dependencies pointing in the direction of the dependents. Each task
   // need to be assigned a unique priority and a run count that matches
   // the number of dependencies.
-  typedef ScopedPtrHashMap<internal::WorkerPoolTask*, internal::GraphNode>
+  typedef base::ScopedPtrHashMap<internal::WorkerPoolTask*, internal::GraphNode>
       GraphNodeMap;
   typedef GraphNodeMap TaskGraph;
 
diff --git a/cc/resources/worker_pool_perftest.cc b/cc/resources/worker_pool_perftest.cc
index aee1f24..0618650 100644
--- a/cc/resources/worker_pool_perftest.cc
+++ b/cc/resources/worker_pool_perftest.cc
@@ -6,6 +6,7 @@
 
 #include "base/time/time.h"
 #include "cc/base/completion_event.h"
+#include "cc/test/lap_timer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cc {
@@ -144,7 +145,10 @@
 
 class WorkerPoolPerfTest : public testing::Test {
  public:
-  WorkerPoolPerfTest() : num_runs_(0) {}
+  WorkerPoolPerfTest()
+      : timer_(kWarmupRuns,
+               base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
+               kTimeCheckInterval) {}
 
   // Overridden from testing::Test:
   virtual void SetUp() OVERRIDE {
@@ -155,38 +159,16 @@
     worker_pool_->CheckForCompletedTasks();
   }
 
-  void EndTest() {
-    elapsed_ = base::TimeTicks::HighResNow() - start_time_;
-  }
-
   void AfterTest(const std::string test_name) {
     // Format matches chrome/test/perf/perf_test.h:PrintResult
-    printf("*RESULT %s: %.2f runs/s\n",
-           test_name.c_str(),
-           num_runs_ / elapsed_.InSecondsF());
-  }
-
-  bool DidRun() {
-    ++num_runs_;
-    if (num_runs_ == kWarmupRuns)
-      start_time_ = base::TimeTicks::HighResNow();
-
-    if (!start_time_.is_null() && (num_runs_ % kTimeCheckInterval) == 0) {
-      base::TimeDelta elapsed = base::TimeTicks::HighResNow() - start_time_;
-      if (elapsed >= base::TimeDelta::FromMilliseconds(kTimeLimitMillis)) {
-        elapsed_ = elapsed;
-        return false;
-      }
-    }
-
-    return true;
+    printf(
+        "*RESULT %s: %.2f runs/s\n", test_name.c_str(), timer_.LapsPerSecond());
   }
 
   void RunScheduleTasksTest(const std::string test_name,
                             unsigned max_depth,
                             unsigned num_children_per_node) {
-    start_time_ = base::TimeTicks();
-    num_runs_ = 0;
+    timer_.Reset();
     do {
       scoped_refptr<PerfControlWorkerPoolTaskImpl> leaf_task(
           new PerfControlWorkerPoolTaskImpl);
@@ -196,7 +178,8 @@
       worker_pool_->ScheduleTasks(NULL, NULL, 0, 0);
       worker_pool_->CheckForCompletedTasks();
       leaf_task->AllowTaskToFinish();
-    } while (DidRun());
+      timer_.NextLap();
+    } while (!timer_.HasTimeLimitExpired());
 
     AfterTest(test_name);
   }
@@ -204,8 +187,7 @@
   void RunExecuteTasksTest(const std::string test_name,
                            unsigned max_depth,
                            unsigned num_children_per_node) {
-    start_time_ = base::TimeTicks();
-    num_runs_ = 0;
+    timer_.Reset();
     do {
       scoped_refptr<PerfControlWorkerPoolTaskImpl> root_task(
           new PerfControlWorkerPoolTaskImpl);
@@ -214,16 +196,15 @@
       root_task->WaitForTaskToStartRunning();
       root_task->AllowTaskToFinish();
       worker_pool_->CheckForCompletedTasks();
-    } while (DidRun());
+      timer_.NextLap();
+    } while (!timer_.HasTimeLimitExpired());
 
     AfterTest(test_name);
   }
 
  protected:
   scoped_ptr<PerfWorkerPool> worker_pool_;
-  base::TimeTicks start_time_;
-  base::TimeDelta elapsed_;
-  int num_runs_;
+  LapTimer timer_;
 };
 
 TEST_F(WorkerPoolPerfTest, ScheduleTasks) {
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
index 3e8833f..e383894 100644
--- a/cc/scheduler/scheduler.cc
+++ b/cc/scheduler/scheduler.cc
@@ -44,8 +44,8 @@
   ProcessScheduledActions();
 }
 
-void Scheduler::SetHasPendingTree(bool has_pending_tree) {
-  state_machine_.SetHasPendingTree(has_pending_tree);
+void Scheduler::NotifyReadyToActivate() {
+  state_machine_.NotifyReadyToActivate();
   ProcessScheduledActions();
 }
 
@@ -220,8 +220,8 @@
       case SchedulerStateMachine::ACTION_UPDATE_VISIBLE_TILES:
         client_->ScheduledActionUpdateVisibleTiles();
         break;
-      case SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED:
-        client_->ScheduledActionActivatePendingTreeIfNeeded();
+      case SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE:
+        client_->ScheduledActionActivatePendingTree();
         break;
       case SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE:
         DrawAndSwapIfPossible();
diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h
index c64dad0..9932079 100644
--- a/cc/scheduler/scheduler.h
+++ b/cc/scheduler/scheduler.h
@@ -41,7 +41,7 @@
   ScheduledActionDrawAndSwapForced() = 0;
   virtual void ScheduledActionCommit() = 0;
   virtual void ScheduledActionUpdateVisibleTiles() = 0;
-  virtual void ScheduledActionActivatePendingTreeIfNeeded() = 0;
+  virtual void ScheduledActionActivatePendingTree() = 0;
   virtual void ScheduledActionBeginOutputSurfaceCreation() = 0;
   virtual void ScheduledActionAcquireLayerTexturesForMainThread() = 0;
   virtual void DidAnticipatedDrawTimeChange(base::TimeTicks time) = 0;
@@ -67,7 +67,7 @@
 
   void SetVisible(bool visible);
   void SetCanDraw(bool can_draw);
-  void SetHasPendingTree(bool has_pending_tree);
+  void NotifyReadyToActivate();
 
   void SetNeedsCommit();
 
@@ -105,7 +105,7 @@
 
   void BeginFrame(const BeginFrameArgs& args);
 
-  scoped_ptr<base::Value> StateAsValueForTesting() {
+  scoped_ptr<base::Value> StateAsValue() {
     return state_machine_.AsValue().Pass();
   }
 
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc
index 5108a11..704259b 100644
--- a/cc/scheduler/scheduler_state_machine.cc
+++ b/cc/scheduler/scheduler_state_machine.cc
@@ -13,12 +13,12 @@
 
 SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings)
     : settings_(settings),
+      output_surface_state_(OUTPUT_SURFACE_LOST),
       commit_state_(COMMIT_STATE_IDLE),
       commit_count_(0),
       current_frame_number_(0),
       last_frame_number_where_begin_frame_sent_to_main_thread_(-1),
       last_frame_number_where_draw_was_called_(-1),
-      last_frame_number_where_tree_activation_attempted_(-1),
       last_frame_number_where_update_visible_tiles_was_called_(-1),
       consecutive_failed_draws_(0),
       maximum_number_of_failed_draws_before_draw_is_forced_(3),
@@ -26,7 +26,6 @@
       swap_used_incomplete_tile_(false),
       needs_forced_redraw_(false),
       needs_forced_redraw_after_next_commit_(false),
-      needs_redraw_after_next_commit_(false),
       needs_commit_(false),
       needs_forced_commit_(false),
       expect_immediate_begin_frame_for_main_thread_(false),
@@ -36,11 +35,30 @@
       can_start_(false),
       can_draw_(false),
       has_pending_tree_(false),
+      pending_tree_is_ready_for_activation_(false),
+      active_tree_has_been_drawn_(false),
       draw_if_possible_failed_(false),
       texture_state_(LAYER_TEXTURE_STATE_UNLOCKED),
-      output_surface_state_(OUTPUT_SURFACE_LOST),
       did_create_and_initialize_first_output_surface_(false) {}
 
+const char* SchedulerStateMachine::OutputSurfaceStateToString(
+    OutputSurfaceState state) {
+  switch (state) {
+    case OUTPUT_SURFACE_ACTIVE:
+      return "OUTPUT_SURFACE_ACTIVE";
+    case OUTPUT_SURFACE_LOST:
+      return "OUTPUT_SURFACE_LOST";
+    case OUTPUT_SURFACE_CREATING:
+      return "OUTPUT_SURFACE_CREATING";
+    case OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT:
+      return "OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT";
+    case OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION:
+      return "OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION";
+  }
+  NOTREACHED();
+  return "???";
+}
+
 const char* SchedulerStateMachine::CommitStateToString(CommitState state) {
   switch (state) {
     case COMMIT_STATE_IDLE:
@@ -71,20 +89,6 @@
   return "???";
 }
 
-const char* SchedulerStateMachine::OutputSurfaceStateToString(
-    OutputSurfaceState state) {
-  switch (state) {
-    case OUTPUT_SURFACE_ACTIVE:
-      return "OUTPUT_SURFACE_ACTIVE";
-    case OUTPUT_SURFACE_LOST:
-      return "OUTPUT_SURFACE_LOST";
-    case OUTPUT_SURFACE_CREATING:
-      return "OUTPUT_SURFACE_CREATING";
-  }
-  NOTREACHED();
-  return "???";
-}
-
 const char* SchedulerStateMachine::ActionToString(Action action) {
   switch (action) {
     case ACTION_NONE:
@@ -95,8 +99,8 @@
       return "ACTION_COMMIT";
     case ACTION_UPDATE_VISIBLE_TILES:
       return "ACTION_UPDATE_VISIBLE_TILES";
-    case ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED:
-      return "ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED";
+    case ACTION_ACTIVATE_PENDING_TREE:
+      return "ACTION_ACTIVATE_PENDING_TREE";
     case ACTION_DRAW_IF_POSSIBLE:
       return "ACTION_DRAW_IF_POSSIBLE";
     case ACTION_DRAW_FORCED:
@@ -159,8 +163,6 @@
       last_frame_number_where_begin_frame_sent_to_main_thread_);
   minor_state->SetInteger("last_frame_number_where_draw_was_called",
                           last_frame_number_where_draw_was_called_);
-  minor_state->SetInteger("last_frame_number_where_tree_activation_attempted",
-                          last_frame_number_where_tree_activation_attempted_);
   minor_state->SetInteger(
       "last_frame_number_where_update_visible_tiles_was_called",
       last_frame_number_where_update_visible_tiles_was_called_);
@@ -175,8 +177,6 @@
   minor_state->SetBoolean("needs_forced_redraw", needs_forced_redraw_);
   minor_state->SetBoolean("needs_forced_redraw_after_next_commit",
                           needs_forced_redraw_after_next_commit_);
-  minor_state->SetBoolean("needs_redraw_after_next_commit",
-                          needs_redraw_after_next_commit_);
   minor_state->SetBoolean("needs_commit", needs_commit_);
   minor_state->SetBoolean("needs_forced_commit", needs_forced_commit_);
   minor_state->SetBoolean("expect_immediate_begin_frame_for_main_thread",
@@ -188,6 +188,10 @@
   minor_state->SetBoolean("can_start", can_start_);
   minor_state->SetBoolean("can_draw", can_draw_);
   minor_state->SetBoolean("has_pending_tree", has_pending_tree_);
+  minor_state->SetBoolean("pending_tree_is_ready_for_activation_",
+                          pending_tree_is_ready_for_activation_);
+  minor_state->SetBoolean("active_tree_has_been_drawn_",
+                          active_tree_has_been_drawn_);
   minor_state->SetBoolean("draw_if_possible_failed", draw_if_possible_failed_);
   minor_state->SetBoolean("did_create_and_initialize_first_output_surface",
                           did_create_and_initialize_first_output_surface_);
@@ -200,78 +204,90 @@
   return current_frame_number_ == last_frame_number_where_draw_was_called_;
 }
 
-bool SchedulerStateMachine::HasAttemptedTreeActivationThisFrame() const {
-  return current_frame_number_ ==
-         last_frame_number_where_tree_activation_attempted_;
-}
-
 bool SchedulerStateMachine::HasUpdatedVisibleTilesThisFrame() const {
   return current_frame_number_ ==
          last_frame_number_where_update_visible_tiles_was_called_;
 }
 
-void SchedulerStateMachine::HandleCommitInternal(bool commit_was_aborted) {
-  commit_count_++;
-
-  if (commit_was_aborted) {
-    commit_state_ = COMMIT_STATE_IDLE;
-  } else if (expect_immediate_begin_frame_for_main_thread_) {
-    commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW;
-  } else {
-    commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW;
-  }
-
-  if (!settings_.impl_side_painting && !commit_was_aborted)
-    needs_redraw_ = true;
-
-  // This post-commit work is common to both completed and aborted commits.
-  if (draw_if_possible_failed_)
-    last_frame_number_where_draw_was_called_ = -1;
-
-  if (needs_forced_redraw_after_next_commit_) {
-    needs_forced_redraw_after_next_commit_ = false;
-    needs_forced_redraw_ = true;
-  }
-  if (needs_redraw_after_next_commit_) {
-    needs_redraw_after_next_commit_ = false;
-    needs_redraw_ = true;
-  }
-
-  // If we are planing to draw with the new commit, lock the layer textures for
-  // use on the impl thread. Otherwise, leave them unlocked.
-  if (!commit_was_aborted || needs_redraw_ || needs_forced_redraw_)
-    texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD;
-  else
-    texture_state_ = LAYER_TEXTURE_STATE_UNLOCKED;
+bool SchedulerStateMachine::HasSentBeginFrameToMainThreadThisFrame() const {
+  return current_frame_number_ ==
+         last_frame_number_where_begin_frame_sent_to_main_thread_;
 }
 
 bool SchedulerStateMachine::PendingDrawsShouldBeAborted() const {
-  // These are all the cases where, if we do not abort draws to make
-  // forward progress, we might deadlock with the main thread.
+  // These are all the cases where we normally cannot or do not want to draw
+  // but, if needs_redraw_ is true and we do not draw to make forward progress,
+  // we might deadlock with the main thread.
+  // This should be a superset of PendingActivationsShouldBeForced() since
+  // activation of the pending tree is blocked by drawing of the active tree and
+  // the main thread might be blocked on activation of the most recent commit.
+  if (PendingActivationsShouldBeForced())
+    return true;
+
+  // Additional states where we should abort draws.
+  // Note: We don't force activation in these cases because doing so would
+  // result in checkerboarding on resize, becoming visible, etc.
   if (!can_draw_)
     return true;
   if (!visible_)
     return true;
-  if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE)
-    return true;
-  if (texture_state_ == LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD)
+  if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION)
     return true;
   return false;
 }
 
+bool SchedulerStateMachine::PendingActivationsShouldBeForced() const {
+  // These are all the cases where, if we do not force activations to make
+  // forward progress, we might deadlock with the main thread.
+  if (texture_state_ == LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD)
+    return true;
+  if (output_surface_state_ == OUTPUT_SURFACE_LOST ||
+      output_surface_state_ == OUTPUT_SURFACE_CREATING ||
+      output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT)
+    return true;
+  return false;
+}
+
+bool SchedulerStateMachine::ShouldBeginOutputSurfaceCreation() const {
+  // Don't try to initialize too early.
+  if (!can_start_)
+    return false;
+
+  // We only want to start output surface initialization after the
+  // previous commit is complete.
+  if (commit_state_ != COMMIT_STATE_IDLE)
+    return false;
+
+  // We need to create the output surface if we don't have one and we haven't
+  // started creating one yet.
+  return output_surface_state_ == OUTPUT_SURFACE_LOST;
+}
+
 bool SchedulerStateMachine::ShouldDraw() const {
+  // We should not draw or abort draws while we are waiting for the
+  // first activation. Doing so will cause us to transition to
+  // COMMIT_STATE_IDLE and start the 2nd commit before the 1st commit
+  // has been drawn. It will also cause us to fail readbacks when we could
+  // have succeeded by waiting a little longer.
+  if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION)
+    return false;
+
+  // When we are waiting for a forced draw, only force draw once
+  // needs_forced_redraw_ is true.
+  if (commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW)
+    return needs_forced_redraw_;
+
   // Always handle forced draws ASAP.
   if (needs_forced_redraw_)
     return true;
 
-  // If we are going to abort draws, we should do so ASAP.
-  if (PendingDrawsShouldBeAborted()) {
-    // TODO(brianderson): Remove the !has_pending_tree_ condition once
-    // the Scheduler controls activation. It's dangerous for us to rely on
-    // an eventual activation if we've lost the output surface.
-    return commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_DRAW &&
-           !has_pending_tree_;
-  }
+  // If we need to abort draws, we should do so ASAP since the draw could
+  // be blocking other important actions (like output surface initialization),
+  // from occuring. If we are waiting for the first draw, then perfom the
+  // aborted draw to keep things moving. If we are not waiting for the first
+  // draw however, we don't want to abort for no reason.
+  if (PendingDrawsShouldBeAborted())
+    return commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_DRAW;
 
   // After this line, we only want to draw once per frame.
   if (HasDrawnThisFrame())
@@ -284,21 +300,6 @@
   return needs_redraw_;
 }
 
-bool SchedulerStateMachine::ShouldAttemptTreeActivation() const {
-  return has_pending_tree_ && inside_begin_frame_ &&
-         !HasAttemptedTreeActivationThisFrame();
-}
-
-bool SchedulerStateMachine::ShouldUpdateVisibleTiles() const {
-  if (!settings_.impl_side_painting)
-    return false;
-  if (HasUpdatedVisibleTilesThisFrame())
-    return false;
-
-  return ShouldAttemptTreeActivation() || ShouldDraw() ||
-         swap_used_incomplete_tile_;
-}
-
 bool SchedulerStateMachine::ShouldAcquireLayerTexturesForMainThread() const {
   if (!main_thread_needs_layer_textures_)
     return false;
@@ -308,84 +309,112 @@
   return false;
 }
 
+bool SchedulerStateMachine::ShouldActivatePendingTree() const {
+  // There is nothing to activate.
+  if (!has_pending_tree_)
+    return false;
+
+  // We don't want to activate a second tree before drawing the first one.
+  // Note: It is possible that there is no active tree to draw when
+  // output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION,
+  // so we don't block activation on draw in that case.
+  if (!active_tree_has_been_drawn_ &&
+      output_surface_state_ != OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION)
+    return false;
+
+  // If we want to force activation, do so ASAP.
+  if (PendingActivationsShouldBeForced())
+    return true;
+
+  // At this point, only activate if we are ready to activate.
+  return pending_tree_is_ready_for_activation_;
+}
+
+bool SchedulerStateMachine::ShouldUpdateVisibleTiles() const {
+  if (!settings_.impl_side_painting)
+    return false;
+  if (HasUpdatedVisibleTilesThisFrame())
+    return false;
+
+  // There's no reason to check for tiles if we don't have an output surface.
+  if (!HasInitializedOutputSurface())
+    return false;
+
+  // We always want to update the most recent visible tiles before drawing
+  // so we draw with fewer missing tiles.
+  if (ShouldDraw())
+    return true;
+
+  // If the last swap drew with checkerboard or missing tiles, we should
+  // poll for any new visible tiles so we can be notified to draw again
+  // when there are.
+  if (swap_used_incomplete_tile_)
+    return true;
+
+  return false;
+}
+
+bool SchedulerStateMachine::ShouldSendBeginFrameToMainThread() const {
+  if (!needs_commit_)
+    return false;
+
+  // Only send BeginFrame to the main thread when idle.
+  if (commit_state_ != COMMIT_STATE_IDLE)
+    return false;
+
+  // We can't accept a commit if we have a pending tree.
+  if (has_pending_tree_)
+    return false;
+
+  // We want to start forced commits ASAP.
+  if (needs_forced_commit_)
+    return true;
+
+  // We do not need commits if we are not visible, unless there's a
+  // request for a forced commit.
+  if (!visible_)
+    return false;
+
+  // We want to start the first commit after we get a new output surface ASAP.
+  if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT)
+    return true;
+
+  // After this point, we only start a commit once per frame.
+  if (HasSentBeginFrameToMainThreadThisFrame())
+    return false;
+
+  // We shouldn't normally accept commits if there isn't an OutputSurface.
+  if (!HasInitializedOutputSurface())
+    return false;
+
+  return true;
+}
+
+bool SchedulerStateMachine::ShouldCommit() const {
+  return commit_state_ == COMMIT_STATE_READY_TO_COMMIT;
+}
+
 SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const {
   if (ShouldAcquireLayerTexturesForMainThread())
     return ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD;
-
-  switch (commit_state_) {
-    case COMMIT_STATE_IDLE: {
-      if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE &&
-          needs_forced_redraw_)
-        return ACTION_DRAW_FORCED;
-      if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE &&
-          needs_forced_commit_)
-        // TODO(enne): Should probably drop the active tree on force commit.
-        return has_pending_tree_ ? ACTION_NONE
-                                 : ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD;
-      if (output_surface_state_ == OUTPUT_SURFACE_LOST && can_start_)
-        return ACTION_BEGIN_OUTPUT_SURFACE_CREATION;
-      if (output_surface_state_ == OUTPUT_SURFACE_CREATING)
-        return ACTION_NONE;
-      if (ShouldUpdateVisibleTiles())
-        return ACTION_UPDATE_VISIBLE_TILES;
-      if (ShouldAttemptTreeActivation())
-        return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED;
-      if (ShouldDraw()) {
-        return needs_forced_redraw_ ? ACTION_DRAW_FORCED
-                                    : ACTION_DRAW_IF_POSSIBLE;
-      }
-      bool can_commit_this_frame =
-          visible_ &&
-          current_frame_number_ >
-              last_frame_number_where_begin_frame_sent_to_main_thread_;
-      if (needs_commit_ && ((can_commit_this_frame &&
-                             output_surface_state_ == OUTPUT_SURFACE_ACTIVE) ||
-                            needs_forced_commit_))
-        // TODO(enne): Should probably drop the active tree on force commit.
-        return has_pending_tree_ ? ACTION_NONE
-                                 : ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD;
-      return ACTION_NONE;
-    }
-    case COMMIT_STATE_FRAME_IN_PROGRESS:
-      if (ShouldUpdateVisibleTiles())
-        return ACTION_UPDATE_VISIBLE_TILES;
-      if (ShouldAttemptTreeActivation())
-        return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED;
-      if (ShouldDraw()) {
-        return needs_forced_redraw_ ? ACTION_DRAW_FORCED
-                                    : ACTION_DRAW_IF_POSSIBLE;
-      }
-      return ACTION_NONE;
-
-    case COMMIT_STATE_READY_TO_COMMIT:
-      return ACTION_COMMIT;
-
-    case COMMIT_STATE_WAITING_FOR_FIRST_DRAW: {
-      if (ShouldUpdateVisibleTiles())
-        return ACTION_UPDATE_VISIBLE_TILES;
-      if (ShouldAttemptTreeActivation())
-        return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED;
-      if (ShouldDraw()) {
-        if (needs_forced_redraw_)
-          return ACTION_DRAW_FORCED;
-        else if (PendingDrawsShouldBeAborted())
-          return ACTION_DRAW_AND_SWAP_ABORT;
-        else
-          return ACTION_DRAW_IF_POSSIBLE;
-      }
-      return ACTION_NONE;
-    }
-
-    case COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW:
-      if (ShouldUpdateVisibleTiles())
-        return ACTION_UPDATE_VISIBLE_TILES;
-      if (ShouldAttemptTreeActivation())
-        return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED;
-      if (needs_forced_redraw_)
-        return ACTION_DRAW_FORCED;
-      return ACTION_NONE;
+  if (ShouldUpdateVisibleTiles())
+    return ACTION_UPDATE_VISIBLE_TILES;
+  if (ShouldActivatePendingTree())
+    return ACTION_ACTIVATE_PENDING_TREE;
+  if (ShouldCommit())
+    return ACTION_COMMIT;
+  if (ShouldDraw()) {
+    if (needs_forced_redraw_)
+      return ACTION_DRAW_FORCED;
+    else if (PendingDrawsShouldBeAborted())
+      return ACTION_DRAW_AND_SWAP_ABORT;
+    else
+      return ACTION_DRAW_IF_POSSIBLE;
   }
-  NOTREACHED();
+  if (ShouldSendBeginFrameToMainThread())
+    return ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD;
+  if (ShouldBeginOutputSurfaceCreation())
+    return ACTION_BEGIN_OUTPUT_SURFACE_CREATION;
   return ACTION_NONE;
 }
 
@@ -399,14 +428,14 @@
           current_frame_number_;
       return;
 
-    case ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED:
-      last_frame_number_where_tree_activation_attempted_ =
-          current_frame_number_;
+    case ACTION_ACTIVATE_PENDING_TREE:
+      UpdateStateOnActivation();
       return;
 
     case ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD:
       DCHECK(!has_pending_tree_);
-      if (!needs_forced_commit_) {
+      if (!needs_forced_commit_ &&
+          output_surface_state_ != OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT) {
         DCHECK(visible_);
         DCHECK_GT(current_frame_number_,
                   last_frame_number_where_begin_frame_sent_to_main_thread_);
@@ -420,7 +449,7 @@
 
     case ACTION_COMMIT: {
       bool commit_was_aborted = false;
-      HandleCommitInternal(commit_was_aborted);
+      UpdateStateOnCommit(commit_was_aborted);
       return;
     }
 
@@ -450,6 +479,69 @@
   }
 }
 
+void SchedulerStateMachine::UpdateStateOnCommit(bool commit_was_aborted) {
+  commit_count_++;
+
+  // If we are impl-side-painting but the commit was aborted, then we behave
+  // mostly as if we are not impl-side-painting since there is no pending tree.
+  has_pending_tree_ = settings_.impl_side_painting && !commit_was_aborted;
+
+  // Update the commit state.
+  if (expect_immediate_begin_frame_for_main_thread_)
+    commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW;
+  else if (!commit_was_aborted)
+    commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW;
+  else
+    commit_state_ = COMMIT_STATE_IDLE;
+
+  // Update the output surface state.
+  DCHECK_NE(output_surface_state_, OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION);
+  if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT) {
+    if (has_pending_tree_) {
+      output_surface_state_ = OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION;
+    } else {
+      output_surface_state_ = OUTPUT_SURFACE_ACTIVE;
+      needs_redraw_ = true;
+    }
+  }
+
+  // Update state if we have a new active tree to draw, or if the active tree
+  // was unchanged but we need to do a readback.
+  if (!has_pending_tree_ &&
+      (!commit_was_aborted || expect_immediate_begin_frame_for_main_thread_)) {
+    needs_redraw_ = true;
+    active_tree_has_been_drawn_ = false;
+  }
+
+  // This post-commit work is common to both completed and aborted commits.
+  pending_tree_is_ready_for_activation_ = false;
+
+  if (draw_if_possible_failed_)
+    last_frame_number_where_draw_was_called_ = -1;
+
+  if (needs_forced_redraw_after_next_commit_) {
+    needs_forced_redraw_after_next_commit_ = false;
+    needs_forced_redraw_ = true;
+  }
+
+  // If we are planing to draw with the new commit, lock the layer textures for
+  // use on the impl thread. Otherwise, leave them unlocked.
+  if (has_pending_tree_ || needs_redraw_ || needs_forced_redraw_)
+    texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD;
+  else
+    texture_state_ = LAYER_TEXTURE_STATE_UNLOCKED;
+}
+
+void SchedulerStateMachine::UpdateStateOnActivation() {
+  if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION)
+    output_surface_state_ = OUTPUT_SURFACE_ACTIVE;
+
+  has_pending_tree_ = false;
+  pending_tree_is_ready_for_activation_ = false;
+  active_tree_has_been_drawn_ = false;
+  needs_redraw_ = true;
+}
+
 void SchedulerStateMachine::UpdateStateOnDraw(bool did_swap) {
   if (inside_begin_frame_)
     last_frame_number_where_draw_was_called_ = current_frame_number_;
@@ -466,6 +558,7 @@
   needs_redraw_ = false;
   needs_forced_redraw_ = false;
   draw_if_possible_failed_ = false;
+  active_tree_has_been_drawn_ = true;
 
   if (did_swap)
     swap_used_incomplete_tile_ = false;
@@ -488,13 +581,17 @@
   if (visible_ && swap_used_incomplete_tile_)
     return true;
 
-  return needs_redraw_ && visible_ &&
-         output_surface_state_ == OUTPUT_SURFACE_ACTIVE;
+  return needs_redraw_ && visible_ && HasInitializedOutputSurface();
 }
 
 bool SchedulerStateMachine::ProactiveBeginFrameWantedByImplThread() const {
+  // The output surface is the provider of BeginFrames for the impl thread,
+  // so we are not going to get them even if we ask for them.
+  if (!HasInitializedOutputSurface())
+    return false;
+
   // Do not be proactive when invisible.
-  if (!visible_ || output_surface_state_ != OUTPUT_SURFACE_ACTIVE)
+  if (!visible_)
     return false;
 
   // We should proactively request a BeginFrame if a commit or a tree activation
@@ -563,7 +660,7 @@
   DCHECK_EQ(commit_state_, COMMIT_STATE_FRAME_IN_PROGRESS);
   if (did_handle) {
     bool commit_was_aborted = true;
-    HandleCommitInternal(commit_was_aborted);
+    UpdateStateOnCommit(commit_was_aborted);
   } else if (expect_immediate_begin_frame_for_main_thread_) {
     expect_immediate_begin_frame_for_main_thread_ = false;
   } else {
@@ -577,33 +674,41 @@
       output_surface_state_ == OUTPUT_SURFACE_CREATING)
     return;
   output_surface_state_ = OUTPUT_SURFACE_LOST;
+  needs_redraw_ = false;
 }
 
-void SchedulerStateMachine::SetHasPendingTree(bool has_pending_tree) {
-  has_pending_tree_ = has_pending_tree;
+void SchedulerStateMachine::NotifyReadyToActivate() {
+  if (has_pending_tree_)
+    pending_tree_is_ready_for_activation_ = true;
 }
 
 void SchedulerStateMachine::SetCanDraw(bool can) { can_draw_ = can; }
 
 void SchedulerStateMachine::DidCreateAndInitializeOutputSurface() {
   DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_CREATING);
-  output_surface_state_ = OUTPUT_SURFACE_ACTIVE;
+  output_surface_state_ = OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT;
 
   if (did_create_and_initialize_first_output_surface_) {
     // TODO(boliu): See if we can remove this when impl-side painting is always
     // on. Does anything on the main thread need to update after recreate?
     needs_commit_ = true;
-    // If anything has requested a redraw, we don't want to actually draw
-    // when the output surface is restored until things have a chance to
-    // sort themselves out with a commit.
-    needs_redraw_ = false;
   }
-  needs_redraw_after_next_commit_ = true;
   did_create_and_initialize_first_output_surface_ = true;
 }
 
 bool SchedulerStateMachine::HasInitializedOutputSurface() const {
-  return output_surface_state_ == OUTPUT_SURFACE_ACTIVE;
+  switch (output_surface_state_) {
+    case OUTPUT_SURFACE_LOST:
+    case OUTPUT_SURFACE_CREATING:
+      return false;
+
+    case OUTPUT_SURFACE_ACTIVE:
+    case OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT:
+    case OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION:
+      return true;
+  }
+  NOTREACHED();
+  return false;
 }
 
 void SchedulerStateMachine::SetMaximumNumberOfFailedDrawsBeforeDrawIsForced(
diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h
index eeae995..43bf2be 100644
--- a/cc/scheduler/scheduler_state_machine.h
+++ b/cc/scheduler/scheduler_state_machine.h
@@ -36,6 +36,15 @@
   // settings must be valid for the lifetime of this class.
   explicit SchedulerStateMachine(const SchedulerSettings& settings);
 
+  enum OutputSurfaceState {
+    OUTPUT_SURFACE_ACTIVE,
+    OUTPUT_SURFACE_LOST,
+    OUTPUT_SURFACE_CREATING,
+    OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT,
+    OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION,
+  };
+  static const char* OutputSurfaceStateToString(OutputSurfaceState state);
+
   enum CommitState {
     COMMIT_STATE_IDLE,
     COMMIT_STATE_FRAME_IN_PROGRESS,
@@ -52,13 +61,6 @@
   };
   static const char* TextureStateToString(TextureState state);
 
-  enum OutputSurfaceState {
-    OUTPUT_SURFACE_ACTIVE,
-    OUTPUT_SURFACE_LOST,
-    OUTPUT_SURFACE_CREATING,
-  };
-  static const char* OutputSurfaceStateToString(OutputSurfaceState state);
-
   bool CommitPending() const {
     return commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS ||
            commit_state_ == COMMIT_STATE_READY_TO_COMMIT;
@@ -71,7 +73,7 @@
     ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
     ACTION_COMMIT,
     ACTION_UPDATE_VISIBLE_TILES,
-    ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED,
+    ACTION_ACTIVATE_PENDING_TREE,
     ACTION_DRAW_IF_POSSIBLE,
     ACTION_DRAW_FORCED,
     ACTION_DRAW_AND_SWAP_ABORT,
@@ -151,12 +153,9 @@
   // when such behavior would be undesirable.
   void SetCanDraw(bool can);
 
-  // Indicates whether or not there is a pending tree.  This influences
-  // whether or not we can succesfully commit at this time.  If the
-  // last commit is still being processed (but not blocking), it may not
-  // be possible to take another commit yet.  This overrides force commit,
-  // as a commit is already still in flight.
-  void SetHasPendingTree(bool has_pending_tree);
+  // Indicates that the pending tree is ready for activation.
+  void NotifyReadyToActivate();
+
   bool has_pending_tree() const { return has_pending_tree_; }
 
   void DidLoseOutputSurface();
@@ -170,28 +169,36 @@
   bool PendingDrawsShouldBeAborted() const;
 
  protected:
+  // True if we need to force activations to make forward progress.
+  bool PendingActivationsShouldBeForced() const;
+
+  bool ShouldBeginOutputSurfaceCreation() const;
   bool ShouldDrawForced() const;
   bool ShouldDraw() const;
-  bool ShouldAttemptTreeActivation() const;
+  bool ShouldActivatePendingTree() const;
   bool ShouldAcquireLayerTexturesForMainThread() const;
   bool ShouldUpdateVisibleTiles() const;
+  bool ShouldSendBeginFrameToMainThread() const;
+  bool ShouldCommit() const;
 
   bool HasDrawnThisFrame() const;
-  bool HasAttemptedTreeActivationThisFrame() const;
+  bool HasActivatedPendingTreeThisFrame() const;
   bool HasUpdatedVisibleTilesThisFrame() const;
+  bool HasSentBeginFrameToMainThreadThisFrame() const;
 
-  void HandleCommitInternal(bool commit_was_aborted);
+  void UpdateStateOnCommit(bool commit_was_aborted);
+  void UpdateStateOnActivation();
   void UpdateStateOnDraw(bool did_swap);
 
   const SchedulerSettings settings_;
 
+  OutputSurfaceState output_surface_state_;
   CommitState commit_state_;
-  int commit_count_;
 
+  int commit_count_;
   int current_frame_number_;
   int last_frame_number_where_begin_frame_sent_to_main_thread_;
   int last_frame_number_where_draw_was_called_;
-  int last_frame_number_where_tree_activation_attempted_;
   int last_frame_number_where_update_visible_tiles_was_called_;
   int consecutive_failed_draws_;
   int maximum_number_of_failed_draws_before_draw_is_forced_;
@@ -199,7 +206,6 @@
   bool swap_used_incomplete_tile_;
   bool needs_forced_redraw_;
   bool needs_forced_redraw_after_next_commit_;
-  bool needs_redraw_after_next_commit_;
   bool needs_commit_;
   bool needs_forced_commit_;
   bool expect_immediate_begin_frame_for_main_thread_;
@@ -210,9 +216,10 @@
   bool can_start_;
   bool can_draw_;
   bool has_pending_tree_;
+  bool pending_tree_is_ready_for_activation_;
+  bool active_tree_has_been_drawn_;
   bool draw_if_possible_failed_;
   TextureState texture_state_;
-  OutputSurfaceState output_surface_state_;
   bool did_create_and_initialize_first_output_surface_;
 
  private:
diff --git a/cc/scheduler/scheduler_state_machine_unittest.cc b/cc/scheduler/scheduler_state_machine_unittest.cc
index c9454ce..53a2e09 100644
--- a/cc/scheduler/scheduler_state_machine_unittest.cc
+++ b/cc/scheduler/scheduler_state_machine_unittest.cc
@@ -23,9 +23,19 @@
  public:
   explicit StateMachine(const SchedulerSettings& scheduler_settings)
       : SchedulerStateMachine(scheduler_settings) {}
+
+  void CreateAndInitializeOutputSurfaceWithActivatedCommit() {
+    DidCreateAndInitializeOutputSurface();
+    output_surface_state_ = OUTPUT_SURFACE_ACTIVE;
+  }
+
   void SetCommitState(CommitState cs) { commit_state_ = cs; }
   CommitState CommitState() const { return commit_state_; }
 
+  OutputSurfaceState output_surface_state() const {
+    return output_surface_state_;
+  }
+
   bool NeedsCommit() const { return needs_commit_; }
 
   void SetNeedsRedraw(bool b) { needs_redraw_ = b; }
@@ -46,7 +56,7 @@
     StateMachine state(default_scheduler_settings);
     state.SetCanStart();
     state.UpdateState(state.NextAction());
-    state.DidCreateAndInitializeOutputSurface();
+    state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
     state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_IDLE);
     state.SetNeedsRedraw(false);
     state.SetVisible(true);
@@ -91,7 +101,7 @@
     StateMachine state(default_scheduler_settings);
     state.SetCanStart();
     state.UpdateState(state.NextAction());
-    state.DidCreateAndInitializeOutputSurface();
+    state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
     state.SetVisible(true);
     state.UpdateState(
         SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
@@ -114,13 +124,13 @@
 TEST(SchedulerStateMachineTest,
      TestFailedDrawSetsNeedsCommitAndDoesNotDrawAgain) {
   SchedulerSettings default_scheduler_settings;
-  SchedulerStateMachine state(default_scheduler_settings);
+  StateMachine state(default_scheduler_settings);
   state.SetCanStart();
   state.UpdateState(state.NextAction());
-  state.DidCreateAndInitializeOutputSurface();
+  state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
   state.SetVisible(true);
   state.SetCanDraw(true);
-  state.SetNeedsRedraw();
+  state.SetNeedsRedraw(true);
   EXPECT_TRUE(state.RedrawPending());
   EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
   state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
@@ -145,14 +155,14 @@
 TEST(SchedulerStateMachineTest,
      TestsetNeedsRedrawDuringFailedDrawDoesNotRemoveNeedsRedraw) {
   SchedulerSettings default_scheduler_settings;
-  SchedulerStateMachine state(default_scheduler_settings);
+  StateMachine state(default_scheduler_settings);
   state.SetCanStart();
   state.UpdateState(state.NextAction());
-  state.DidCreateAndInitializeOutputSurface();
+  state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
 
   state.SetVisible(true);
   state.SetCanDraw(true);
-  state.SetNeedsRedraw();
+  state.SetNeedsRedraw(true);
   EXPECT_TRUE(state.RedrawPending());
   EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
   state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
@@ -166,7 +176,7 @@
 
   // While still in the same begin frame callback on the main thread,
   // set needs redraw again. This should not redraw.
-  state.SetNeedsRedraw();
+  state.SetNeedsRedraw(true);
   EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
 
   // Failing the draw makes us require a commit.
@@ -179,10 +189,10 @@
 TEST(SchedulerStateMachineTest,
      TestCommitAfterFailedDrawAllowsDrawInSameFrame) {
   SchedulerSettings default_scheduler_settings;
-  SchedulerStateMachine state(default_scheduler_settings);
+  StateMachine state(default_scheduler_settings);
   state.SetCanStart();
   state.UpdateState(state.NextAction());
-  state.DidCreateAndInitializeOutputSurface();
+  state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
   state.SetVisible(true);
   state.SetCanDraw(true);
 
@@ -195,7 +205,7 @@
   EXPECT_TRUE(state.CommitPending());
 
   // Then initiate a draw.
-  state.SetNeedsRedraw();
+  state.SetNeedsRedraw(true);
   EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
   state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
   EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
@@ -222,10 +232,10 @@
 TEST(SchedulerStateMachineTest,
      TestCommitAfterFailedAndSuccessfulDrawDoesNotAllowDrawInSameFrame) {
   SchedulerSettings default_scheduler_settings;
-  SchedulerStateMachine state(default_scheduler_settings);
+  StateMachine state(default_scheduler_settings);
   state.SetCanStart();
   state.UpdateState(state.NextAction());
-  state.DidCreateAndInitializeOutputSurface();
+  state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
   state.SetVisible(true);
   state.SetCanDraw(true);
 
@@ -238,7 +248,7 @@
   EXPECT_TRUE(state.CommitPending());
 
   // Then initiate a draw.
-  state.SetNeedsRedraw();
+  state.SetNeedsRedraw(true);
   EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
   state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
   EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
@@ -253,7 +263,7 @@
   EXPECT_TRUE(state.CommitPending());
 
   // Force a draw.
-  state.SetNeedsForcedRedraw();
+  state.SetNeedsForcedRedraw(true);
   EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction());
 
   // Do the forced draw.
@@ -276,10 +286,10 @@
 TEST(SchedulerStateMachineTest,
      TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit) {
   SchedulerSettings default_scheduler_settings;
-  SchedulerStateMachine state(default_scheduler_settings);
+  StateMachine state(default_scheduler_settings);
   state.SetCanStart();
   state.UpdateState(state.NextAction());
-  state.DidCreateAndInitializeOutputSurface();
+  state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
   state.SetVisible(true);
   state.SetCanDraw(true);
   state.SetMaximumNumberOfFailedDrawsBeforeDrawIsForced(1);
@@ -293,7 +303,7 @@
   EXPECT_TRUE(state.CommitPending());
 
   // Then initiate a draw.
-  state.SetNeedsRedraw();
+  state.SetNeedsRedraw(true);
   EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
   state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
   EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
@@ -321,15 +331,15 @@
 TEST(SchedulerStateMachineTest,
     TestFailedDrawIsRetriedInNextBeginFrameForImplThread) {
   SchedulerSettings default_scheduler_settings;
-  SchedulerStateMachine state(default_scheduler_settings);
+  StateMachine state(default_scheduler_settings);
   state.SetCanStart();
   state.UpdateState(state.NextAction());
-  state.DidCreateAndInitializeOutputSurface();
+  state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
   state.SetVisible(true);
   state.SetCanDraw(true);
 
   // Start a draw.
-  state.SetNeedsRedraw();
+  state.SetNeedsRedraw(true);
   EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
   state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
   EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
@@ -355,13 +365,13 @@
 
 TEST(SchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame) {
   SchedulerSettings default_scheduler_settings;
-  SchedulerStateMachine state(default_scheduler_settings);
+  StateMachine state(default_scheduler_settings);
   state.SetCanStart();
   state.UpdateState(state.NextAction());
-  state.DidCreateAndInitializeOutputSurface();
+  state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
   state.SetVisible(true);
   state.SetCanDraw(true);
-  state.SetNeedsRedraw();
+  state.SetNeedsRedraw(true);
   EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
   state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
   EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
@@ -369,7 +379,7 @@
 
   // While still in the same begin frame for the impl thread, set needs redraw
   // again. This should not redraw.
-  state.SetNeedsRedraw();
+  state.SetNeedsRedraw(true);
   EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
 
   // Move to another frame. This should now draw.
@@ -396,7 +406,7 @@
       StateMachine state(default_scheduler_settings);
       state.SetCanStart();
       state.UpdateState(state.NextAction());
-      state.DidCreateAndInitializeOutputSurface();
+      state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
       state.SetCommitState(all_commit_states[i]);
       bool visible = j;
       if (!visible) {
@@ -425,7 +435,7 @@
       StateMachine state(default_scheduler_settings);
       state.SetCanStart();
       state.UpdateState(state.NextAction());
-      state.DidCreateAndInitializeOutputSurface();
+      state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
       state.SetCanDraw(true);
       state.SetCommitState(all_commit_states[i]);
       bool forced_draw = j;
@@ -470,7 +480,7 @@
       StateMachine state(default_scheduler_settings);
       state.SetCanStart();
       state.UpdateState(state.NextAction());
-      state.DidCreateAndInitializeOutputSurface();
+      state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
       state.SetCommitState(all_commit_states[i]);
       state.SetVisible(false);
       state.SetNeedsRedraw(true);
@@ -501,7 +511,7 @@
       StateMachine state(default_scheduler_settings);
       state.SetCanStart();
       state.UpdateState(state.NextAction());
-      state.DidCreateAndInitializeOutputSurface();
+      state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
       state.SetCommitState(all_commit_states[i]);
       state.SetVisible(false);
       state.SetNeedsRedraw(true);
@@ -522,7 +532,7 @@
   StateMachine state(default_scheduler_settings);
   state.SetCanStart();
   state.UpdateState(state.NextAction());
-  state.DidCreateAndInitializeOutputSurface();
+  state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
   state.SetCommitState(
       SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
   state.SetNeedsCommit();
@@ -541,7 +551,7 @@
   StateMachine state(default_scheduler_settings);
   state.SetCanStart();
   state.UpdateState(state.NextAction());
-  state.DidCreateAndInitializeOutputSurface();
+  state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
   state.SetNeedsCommit();
   state.SetVisible(true);
   state.SetCanDraw(true);
@@ -587,7 +597,7 @@
   StateMachine state(default_scheduler_settings);
   state.SetCanStart();
   state.UpdateState(state.NextAction());
-  state.DidCreateAndInitializeOutputSurface();
+  state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
   state.SetVisible(true);
   state.SetCanDraw(true);
 
@@ -637,7 +647,7 @@
   StateMachine state(default_scheduler_settings);
   state.SetCanStart();
   state.UpdateState(state.NextAction());
-  state.DidCreateAndInitializeOutputSurface();
+  state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
   state.SetVisible(true);
   state.SetCanDraw(true);
 
@@ -692,7 +702,7 @@
   StateMachine state(default_scheduler_settings);
   state.SetCanStart();
   state.UpdateState(state.NextAction());
-  state.DidCreateAndInitializeOutputSurface();
+  state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
   state.SetNeedsCommit();
   EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
 }
@@ -702,7 +712,7 @@
   StateMachine state(default_scheduler_settings);
   state.SetCanStart();
   state.UpdateState(state.NextAction());
-  state.DidCreateAndInitializeOutputSurface();
+  state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
   state.SetVisible(true);
   state.SetCanDraw(true);
 
@@ -781,7 +791,8 @@
   // Start a new frame; draw because this is the first frame since output
   // surface init'd.
   state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
-  EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
+  EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction())
+      << *state.AsValue();
   state.DidLeaveBeginFrame();
 
   // Verify another commit doesn't start on another frame either.
@@ -902,7 +913,7 @@
   StateMachine state(default_scheduler_settings);
   state.SetCanStart();
   state.UpdateState(state.NextAction());
-  state.DidCreateAndInitializeOutputSurface();
+  state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
   state.SetVisible(true);
   state.SetCanDraw(true);
 
@@ -955,7 +966,7 @@
   StateMachine state(default_scheduler_settings);
   state.SetCanStart();
   state.UpdateState(state.NextAction());
-  state.DidCreateAndInitializeOutputSurface();
+  state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
   state.SetVisible(true);
   state.SetCanDraw(true);
 
@@ -1132,7 +1143,7 @@
   EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
 }
 
-TEST(SchedulerStateMachineTest, TestSendBeginFrameToMainThreadWhenContextLost) {
+TEST(SchedulerStateMachineTest, TestInitialActionsWhenContextLost) {
   SchedulerSettings default_scheduler_settings;
   StateMachine state(default_scheduler_settings);
   state.SetCanStart();
@@ -1141,10 +1152,31 @@
   state.SetVisible(true);
   state.SetCanDraw(true);
   state.SetNeedsCommit();
-  state.SetNeedsForcedCommit();
   state.DidLoseOutputSurface();
+
+  // When we are visible, we normally want to begin output surface creation
+  // as soon as possible.
+  EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
+            state.NextAction()) << *state.AsValue();
+  state.UpdateState(state.NextAction());
+
+  state.DidCreateAndInitializeOutputSurface();
+  EXPECT_EQ(state.output_surface_state(),
+            SchedulerStateMachine::OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT);
+
+  // We should not send a BeginFrame to them main thread when we are invisible,
+  // even if we've lost the output surface and are trying to get the first
+  // commit, since the main thread will just abort anyway.
+  state.SetVisible(false);
+  EXPECT_EQ(SchedulerStateMachine::ACTION_NONE,
+            state.NextAction()) << *state.AsValue();
+
+  // If there is a forced commit, however, we could be blocking a readback
+  // on the main thread, so we need to unblock it before we can get our
+  // output surface, even if we are not visible.
+  state.SetNeedsForcedCommit();
   EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
-            state.NextAction());
+            state.NextAction()) << *state.AsValue();
 }
 
 TEST(SchedulerStateMachineTest, TestImmediateFinishCommit) {
@@ -1306,10 +1338,10 @@
 
 TEST(SchedulerStateMachineTest, ReportIfNotDrawing) {
   SchedulerSettings default_scheduler_settings;
-  SchedulerStateMachine state(default_scheduler_settings);
+  StateMachine state(default_scheduler_settings);
   state.SetCanStart();
   state.UpdateState(state.NextAction());
-  state.DidCreateAndInitializeOutputSurface();
+  state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
 
   state.SetCanDraw(true);
   state.SetVisible(true);
@@ -1334,10 +1366,10 @@
 
 TEST(SchedulerStateMachineTest, ReportIfNotDrawingFromAcquiredTextures) {
   SchedulerSettings default_scheduler_settings;
-  SchedulerStateMachine state(default_scheduler_settings);
+  StateMachine state(default_scheduler_settings);
   state.SetCanStart();
   state.UpdateState(state.NextAction());
-  state.DidCreateAndInitializeOutputSurface();
+  state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
   state.SetCanDraw(true);
   state.SetVisible(true);
   EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc
index 0c60131..9936234 100644
--- a/cc/scheduler/scheduler_unittest.cc
+++ b/cc/scheduler/scheduler_unittest.cc
@@ -29,6 +29,17 @@
 namespace cc {
 namespace {
 
+void InitializeOutputSurfaceAndFirstCommit(Scheduler* scheduler) {
+  scheduler->DidCreateAndInitializeOutputSurface();
+  scheduler->SetNeedsCommit();
+  scheduler->FinishCommit();
+  // Go through the motions to draw the commit.
+  scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+  // We need another BeginFrame so scheduler calls SetNeedsBeginFrame(false).
+  scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+  scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+}
+
 class FakeSchedulerClient : public SchedulerClient {
  public:
   FakeSchedulerClient()
@@ -72,17 +83,17 @@
   // Scheduler Implementation.
   virtual void SetNeedsBeginFrameOnImplThread(bool enable) OVERRIDE {
     actions_.push_back("SetNeedsBeginFrameOnImplThread");
-    states_.push_back(scheduler_->StateAsValueForTesting().release());
+    states_.push_back(scheduler_->StateAsValue().release());
     needs_begin_frame_ = enable;
   }
   virtual void ScheduledActionSendBeginFrameToMainThread() OVERRIDE {
     actions_.push_back("ScheduledActionSendBeginFrameToMainThread");
-    states_.push_back(scheduler_->StateAsValueForTesting().release());
+    states_.push_back(scheduler_->StateAsValue().release());
   }
   virtual ScheduledActionDrawAndSwapResult
   ScheduledActionDrawAndSwapIfPossible() OVERRIDE {
     actions_.push_back("ScheduledActionDrawAndSwapIfPossible");
-    states_.push_back(scheduler_->StateAsValueForTesting().release());
+    states_.push_back(scheduler_->StateAsValue().release());
     num_draws_++;
     return ScheduledActionDrawAndSwapResult(draw_will_happen_,
                                             draw_will_happen_ &&
@@ -91,29 +102,29 @@
   virtual ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapForced()
       OVERRIDE {
     actions_.push_back("ScheduledActionDrawAndSwapForced");
-    states_.push_back(scheduler_->StateAsValueForTesting().release());
+    states_.push_back(scheduler_->StateAsValue().release());
     return ScheduledActionDrawAndSwapResult(true,
                                             swap_will_happen_if_draw_happens_);
   }
   virtual void ScheduledActionCommit() OVERRIDE {
     actions_.push_back("ScheduledActionCommit");
-    states_.push_back(scheduler_->StateAsValueForTesting().release());
+    states_.push_back(scheduler_->StateAsValue().release());
   }
   virtual void ScheduledActionUpdateVisibleTiles() OVERRIDE {
     actions_.push_back("ScheduledActionUpdateVisibleTiles");
-    states_.push_back(scheduler_->StateAsValueForTesting().release());
+    states_.push_back(scheduler_->StateAsValue().release());
   }
-  virtual void ScheduledActionActivatePendingTreeIfNeeded() OVERRIDE {
-    actions_.push_back("ScheduledActionActivatePendingTreeIfNeeded");
-    states_.push_back(scheduler_->StateAsValueForTesting().release());
+  virtual void ScheduledActionActivatePendingTree() OVERRIDE {
+    actions_.push_back("ScheduledActionActivatePendingTree");
+    states_.push_back(scheduler_->StateAsValue().release());
   }
   virtual void ScheduledActionBeginOutputSurfaceCreation() OVERRIDE {
     actions_.push_back("ScheduledActionBeginOutputSurfaceCreation");
-    states_.push_back(scheduler_->StateAsValueForTesting().release());
+    states_.push_back(scheduler_->StateAsValue().release());
   }
   virtual void ScheduledActionAcquireLayerTexturesForMainThread() OVERRIDE {
     actions_.push_back("ScheduledActionAcquireLayerTexturesForMainThread");
-    states_.push_back(scheduler_->StateAsValueForTesting().release());
+    states_.push_back(scheduler_->StateAsValue().release());
   }
   virtual void DidAnticipatedDrawTimeChange(base::TimeTicks) OVERRIDE {}
   virtual base::TimeDelta DrawDurationEstimate() OVERRIDE {
@@ -159,10 +170,10 @@
   scheduler->SetCanDraw(true);
 
   EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
-  client.Reset();
-  scheduler->DidCreateAndInitializeOutputSurface();
+  InitializeOutputSurfaceAndFirstCommit(scheduler);
 
   // SetNeedsCommit should begin the frame.
+  client.Reset();
   scheduler->SetNeedsCommit();
   EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
   EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
@@ -171,8 +182,7 @@
 
   // FinishCommit should commit
   scheduler->FinishCommit();
-  EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
-  EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+  EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
   EXPECT_TRUE(client.needs_begin_frame());
   client.Reset();
 
@@ -193,8 +203,8 @@
   scheduler->SetCanDraw(true);
 
   EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
+  InitializeOutputSurfaceAndFirstCommit(scheduler);
   client.Reset();
-  scheduler->DidCreateAndInitializeOutputSurface();
 
   // SetNedsCommit should begin the frame.
   scheduler->SetNeedsCommit();
@@ -204,24 +214,21 @@
 
   // Now SetNeedsCommit again. Calling here means we need a second frame.
   scheduler->SetNeedsCommit();
-  EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 0, 1);
-  client.Reset();
 
-  // Since another commit is needed, FinishCommit should commit,
-  // then begin another frame.
+  // Finish the commit for the first frame.
   scheduler->FinishCommit();
-  EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
-  EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+  EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
   client.Reset();
 
-  // Tick should draw but then begin another frame.
+  // Tick should draw but then begin another frame for the second commit.
   scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
   EXPECT_TRUE(client.needs_begin_frame());
   EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
   EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 2);
   client.Reset();
 
-  // Go back to quiescent state and verify we no longer request BeginFrames.
+  // Finish the second commit to go back to quiescent state and verify we no
+  // longer request BeginFrames.
   scheduler->FinishCommit();
   scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
   EXPECT_FALSE(client.needs_begin_frame());
@@ -236,8 +243,8 @@
   scheduler->SetCanDraw(true);
   EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
 
+  InitializeOutputSurfaceAndFirstCommit(scheduler);
   client.Reset();
-  scheduler->DidCreateAndInitializeOutputSurface();
   scheduler->SetNeedsRedraw();
   EXPECT_TRUE(scheduler->RedrawPending());
   EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
@@ -300,18 +307,15 @@
   scheduler->SetCanDraw(true);
 
   EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
-  client.Reset();
-  scheduler->DidCreateAndInitializeOutputSurface();
+  InitializeOutputSurfaceAndFirstCommit(scheduler);
 
+  client.Reset();
   scheduler->SetNeedsCommit();
   scheduler->SetMainThreadNeedsLayerTextures();
-  EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 4);
-  EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 4);
-  EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread",
-                client,
-                2,
-                4);
-  EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 3, 4);
+  EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 3);
+  EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 3);
+  EXPECT_ACTION(
+      "ScheduledActionAcquireLayerTexturesForMainThread", client, 2, 3);
   client.Reset();
 
   // Although the compositor cannot draw because textures are locked by main
@@ -425,7 +429,8 @@
   scheduler->SetCanStart();
   scheduler->SetVisible(true);
   scheduler->SetCanDraw(true);
-  scheduler->DidCreateAndInitializeOutputSurface();
+  InitializeOutputSurfaceAndFirstCommit(scheduler);
+  client.Reset();
 
   scheduler->SetNeedsRedraw();
   EXPECT_TRUE(scheduler->RedrawPending());
@@ -451,7 +456,8 @@
   scheduler->SetCanStart();
   scheduler->SetVisible(true);
   scheduler->SetCanDraw(true);
-  scheduler->DidCreateAndInitializeOutputSurface();
+  InitializeOutputSurfaceAndFirstCommit(scheduler);
+  client.Reset();
 
   client.SetDrawWillHappen(false);
 
@@ -486,14 +492,19 @@
   EXPECT_TRUE(client.needs_begin_frame());
 }
 
-class SchedulerClientThatsetNeedsCommitInsideDraw : public FakeSchedulerClient {
+class SchedulerClientThatSetNeedsCommitInsideDraw : public FakeSchedulerClient {
  public:
+  SchedulerClientThatSetNeedsCommitInsideDraw()
+      : set_needs_commit_on_next_draw_(false) {}
+
   virtual void ScheduledActionSendBeginFrameToMainThread() OVERRIDE {}
   virtual ScheduledActionDrawAndSwapResult
   ScheduledActionDrawAndSwapIfPossible() OVERRIDE {
     // Only SetNeedsCommit the first time this is called
-    if (!num_draws_)
+    if (set_needs_commit_on_next_draw_) {
       scheduler_->SetNeedsCommit();
+      set_needs_commit_on_next_draw_ = false;
+    }
     return FakeSchedulerClient::ScheduledActionDrawAndSwapIfPossible();
   }
 
@@ -506,24 +517,31 @@
   virtual void ScheduledActionCommit() OVERRIDE {}
   virtual void ScheduledActionBeginOutputSurfaceCreation() OVERRIDE {}
   virtual void DidAnticipatedDrawTimeChange(base::TimeTicks) OVERRIDE {}
+
+  void SetNeedsCommitOnNextDraw() { set_needs_commit_on_next_draw_ = true; }
+
+ private:
+  bool set_needs_commit_on_next_draw_;
 };
 
 // Tests for the scheduler infinite-looping on SetNeedsCommit requests that
 // happen inside a ScheduledActionDrawAndSwap
 TEST(SchedulerTest, RequestCommitInsideDraw) {
-  SchedulerClientThatsetNeedsCommitInsideDraw client;
+  SchedulerClientThatSetNeedsCommitInsideDraw client;
   SchedulerSettings default_scheduler_settings;
   Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
   scheduler->SetCanStart();
   scheduler->SetVisible(true);
   scheduler->SetCanDraw(true);
-  scheduler->DidCreateAndInitializeOutputSurface();
+  InitializeOutputSurfaceAndFirstCommit(scheduler);
+  client.Reset();
 
   scheduler->SetNeedsRedraw();
   EXPECT_TRUE(scheduler->RedrawPending());
   EXPECT_EQ(0, client.num_draws());
   EXPECT_TRUE(client.needs_begin_frame());
 
+  client.SetNeedsCommitOnNextDraw();
   scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
   EXPECT_EQ(1, client.num_draws());
   EXPECT_TRUE(scheduler->CommitPending());
@@ -545,7 +563,8 @@
   scheduler->SetCanStart();
   scheduler->SetVisible(true);
   scheduler->SetCanDraw(true);
-  scheduler->DidCreateAndInitializeOutputSurface();
+  InitializeOutputSurfaceAndFirstCommit(scheduler);
+  client.Reset();
 
   client.SetDrawWillHappen(false);
 
@@ -581,13 +600,14 @@
 }
 
 TEST(SchedulerTest, NoSwapWhenDrawFails) {
-  SchedulerClientThatsetNeedsCommitInsideDraw client;
+  SchedulerClientThatSetNeedsCommitInsideDraw client;
   SchedulerSettings default_scheduler_settings;
   Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
   scheduler->SetCanStart();
   scheduler->SetVisible(true);
   scheduler->SetCanDraw(true);
-  scheduler->DidCreateAndInitializeOutputSurface();
+  InitializeOutputSurfaceAndFirstCommit(scheduler);
+  client.Reset();
 
   scheduler->SetNeedsRedraw();
   EXPECT_TRUE(scheduler->RedrawPending());
@@ -595,6 +615,7 @@
   EXPECT_EQ(0, client.num_draws());
 
   // Draw successfully, this starts a new frame.
+  client.SetNeedsCommitOnNextDraw();
   scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
   EXPECT_EQ(1, client.num_draws());
 
@@ -604,6 +625,7 @@
 
   // Fail to draw, this should not start a frame.
   client.SetDrawWillHappen(false);
+  client.SetNeedsCommitOnNextDraw();
   scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
   EXPECT_EQ(2, client.num_draws());
 }
diff --git a/cc/test/fake_layer_tree_host_impl_client.h b/cc/test/fake_layer_tree_host_impl_client.h
index efc40f2..1783f83 100644
--- a/cc/test/fake_layer_tree_host_impl_client.h
+++ b/cc/test/fake_layer_tree_host_impl_client.h
@@ -13,15 +13,12 @@
 class FakeLayerTreeHostImplClient : public LayerTreeHostImplClient {
  public:
   // LayerTreeHostImplClient implementation.
-  virtual void DidTryInitializeRendererOnImplThread(
-      bool success,
-      scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE {}
   virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE {}
   virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE {}
   virtual void BeginFrameOnImplThread(const BeginFrameArgs& args)
       OVERRIDE {}
   virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE {}
-  virtual void OnHasPendingTreeStateChanged(bool has_pending_tree) OVERRIDE {}
+  virtual void NotifyReadyToActivate() OVERRIDE {}
   virtual void SetNeedsRedrawOnImplThread() OVERRIDE {}
   virtual void SetNeedsRedrawRectOnImplThread(gfx::Rect damage_rect) OVERRIDE {}
   virtual void DidInitializeVisibleTileOnImplThread() OVERRIDE {}
diff --git a/cc/test/fake_proxy.h b/cc/test/fake_proxy.h
index b3bca69..3127cb4 100644
--- a/cc/test/fake_proxy.h
+++ b/cc/test/fake_proxy.h
@@ -32,6 +32,7 @@
   virtual void SetNeedsUpdateLayers() OVERRIDE {}
   virtual void SetNeedsCommit() OVERRIDE {}
   virtual void SetNeedsRedraw(gfx::Rect damage_rect) OVERRIDE {}
+  virtual void SetNextCommitWaitsForActivation() OVERRIDE {}
   virtual void NotifyInputThrottledUntilCommit() OVERRIDE {}
   virtual void SetDeferCommits(bool defer_commits) OVERRIDE {}
   virtual void MainThreadHasStoppedFlinging() OVERRIDE {}
diff --git a/cc/test/lap_timer.cc b/cc/test/lap_timer.cc
new file mode 100644
index 0000000..6d16ba7
--- /dev/null
+++ b/cc/test/lap_timer.cc
@@ -0,0 +1,64 @@
+// 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 "cc/test/lap_timer.h"
+
+#include "base/logging.h"
+
+namespace cc {
+
+LapTimer::LapTimer(int warmup_laps,
+                   base::TimeDelta time_limit,
+                   int check_interval)
+    : warmup_laps_(warmup_laps),
+      time_limit_(time_limit),
+      check_interval_(check_interval) {
+  DCHECK_GT(check_interval, 0);
+  Reset();
+}
+
+void LapTimer::Reset() {
+  accumulator_ = base::TimeDelta();
+  num_laps_ = 0;
+  accumulated_ = true;
+  remaining_warmups_ = warmup_laps_;
+  Start();
+}
+
+void LapTimer::Start() { start_time_ = base::TimeTicks::HighResNow(); }
+
+bool LapTimer::IsWarmedUp() { return remaining_warmups_ <= 0; }
+
+void LapTimer::NextLap() {
+  if (!IsWarmedUp()) {
+    --remaining_warmups_;
+    if (IsWarmedUp()) {
+      Start();
+    }
+    return;
+  }
+  ++num_laps_;
+  accumulated_ = (num_laps_ % check_interval_) == 0;
+  if (accumulated_) {
+    base::TimeTicks now = base::TimeTicks::HighResNow();
+    accumulator_ += now - start_time_;
+    start_time_ = now;
+  }
+}
+
+bool LapTimer::HasTimeLimitExpired() { return accumulator_ >= time_limit_; }
+
+float LapTimer::MsPerLap() {
+  DCHECK(accumulated_);
+  return accumulator_.InMillisecondsF() / num_laps_;
+}
+
+float LapTimer::LapsPerSecond() {
+  DCHECK(accumulated_);
+  return num_laps_ / accumulator_.InSecondsF();
+}
+
+int LapTimer::NumLaps() { return num_laps_; }
+
+}  // namespace cc
diff --git a/cc/test/lap_timer.h b/cc/test/lap_timer.h
new file mode 100644
index 0000000..1ab05f5
--- /dev/null
+++ b/cc/test/lap_timer.h
@@ -0,0 +1,60 @@
+// 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 CC_TEST_LAP_TIMER_H_
+#define CC_TEST_LAP_TIMER_H_
+
+#include "base/time/time.h"
+
+namespace cc {
+
+// LapTimer is used to calculate average times per "Lap" in perf tests.
+// Current() reports the time since the last call to Start().
+// Store() adds the time since the last call to Start() to the accumulator, and
+// resets the start time to now. Stored() returns the accumulated time.
+// NextLap increments the lap counter, used in counting the per lap averages.
+// If you initialize the LapTimer with a non zero warmup_laps, it will ignore
+// the times for that many laps at the start.
+// If you set the time_limit then you can use HasTimeLimitExpired() to see if
+// the current accumulated time has crossed that threshold, with an optimization
+// that it only tests this every check_interval laps.
+class LapTimer {
+ public:
+  LapTimer(int warmup_laps, base::TimeDelta time_limit, int check_interval);
+  // Resets the timer back to it's starting state.
+  void Reset();
+  // Sets the start point to now.
+  void Start();
+  // Returns true if there are no more warmup laps to do.
+  bool IsWarmedUp();
+  // Advance the lap counter and update the accumulated time.
+  // The accumulated time is only updated every check_interval laps.
+  // If accumulating then the start point will also be updated.
+  void NextLap();
+  // Returns true if the stored time has exceeded the time limit specified.
+  // May cause a call to Store().
+  bool HasTimeLimitExpired();
+  // The average milliseconds per lap.
+  float MsPerLap();
+  // The number of laps per second.
+  float LapsPerSecond();
+  // The number of laps recorded.
+  int NumLaps();
+
+ private:
+  base::TimeTicks start_time_;
+  base::TimeDelta accumulator_;
+  int num_laps_;
+  int warmup_laps_;
+  int remaining_warmups_;
+  base::TimeDelta time_limit_;
+  int check_interval_;
+  bool accumulated_;
+
+  DISALLOW_COPY_AND_ASSIGN(LapTimer);
+};
+
+}  // namespace cc
+
+#endif  // CC_TEST_LAP_TIMER_H_
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index 6795f90..9afe3c4 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -37,12 +37,8 @@
   return true;
 }
 
-bool TestHooks::CanActivatePendingTree(LayerTreeHostImpl* host_impl) {
-  return true;
-}
-
-bool TestHooks::CanActivatePendingTreeIfNeeded(LayerTreeHostImpl* host_impl) {
-  return true;
+base::TimeDelta TestHooks::LowFrequencyAnimationInterval() const {
+  return base::TimeDelta::FromMilliseconds(16);
 }
 
 // Adapts LayerTreeHostImpl for test. Runs real code, then invokes test hooks.
@@ -73,7 +69,14 @@
                           host_impl_client,
                           proxy,
                           stats_instrumentation),
-        test_hooks_(test_hooks) {}
+        test_hooks_(test_hooks),
+        block_notify_ready_to_activate_for_testing_(false),
+        notify_ready_to_activate_was_blocked_(false) {}
+
+  virtual void BeginFrame(const BeginFrameArgs& args) OVERRIDE {
+    test_hooks_->WillBeginImplFrameOnThread(this, args);
+    LayerTreeHostImpl::BeginFrame(args);
+  }
 
   virtual void BeginCommit() OVERRIDE {
     LayerTreeHostImpl::BeginCommit();
@@ -114,20 +117,22 @@
     test_hooks_->SwapBuffersCompleteOnThread(this);
   }
 
-  virtual void ActivatePendingTreeIfNeeded() OVERRIDE {
-    if (!pending_tree())
-      return;
+  virtual void NotifyReadyToActivate() OVERRIDE {
+    if (block_notify_ready_to_activate_for_testing_)
+      notify_ready_to_activate_was_blocked_ = true;
+    else
+      client_->NotifyReadyToActivate();
+  }
 
-    if (!test_hooks_->CanActivatePendingTreeIfNeeded(this))
-      return;
-
-    LayerTreeHostImpl::ActivatePendingTreeIfNeeded();
+  virtual void BlockNotifyReadyToActivateForTesting(bool block) OVERRIDE {
+    block_notify_ready_to_activate_for_testing_ = block;
+    if (!block && notify_ready_to_activate_was_blocked_) {
+      NotifyReadyToActivate();
+      notify_ready_to_activate_was_blocked_ = false;
+    }
   }
 
   virtual void ActivatePendingTree() OVERRIDE {
-    if (!test_hooks_->CanActivatePendingTree(this))
-      return;
-
     test_hooks_->WillActivateTreeOnThread(this);
     LayerTreeHostImpl::ActivatePendingTree();
     DCHECK(!pending_tree());
@@ -168,11 +173,13 @@
   }
 
   virtual base::TimeDelta LowFrequencyAnimationInterval() const OVERRIDE {
-    return base::TimeDelta::FromMilliseconds(16);
+    return test_hooks_->LowFrequencyAnimationInterval();
   }
 
  private:
   TestHooks* test_hooks_;
+  bool block_notify_ready_to_activate_for_testing_;
+  bool notify_ready_to_activate_was_blocked_;
 };
 
 // Adapts LayerTreeHost for test. Injects LayerTreeHostImplForTesting.
diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h
index 3191e5b..c6a3f4d 100644
--- a/cc/test/layer_tree_test.h
+++ b/cc/test/layer_tree_test.h
@@ -31,6 +31,8 @@
 
   void ReadSettings(const LayerTreeSettings& settings);
 
+  virtual void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl,
+                                          const BeginFrameArgs& args) {}
   virtual void BeginCommitOnThread(LayerTreeHostImpl* host_impl) {}
   virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) {}
   virtual void WillActivateTreeOnThread(LayerTreeHostImpl* host_impl) {}
@@ -64,10 +66,9 @@
   virtual void DidCompleteSwapBuffers() {}
   virtual void ScheduleComposite() {}
   virtual void DidDeferCommit() {}
-  virtual bool CanActivatePendingTree(LayerTreeHostImpl* host_impl);
-  virtual bool CanActivatePendingTreeIfNeeded(LayerTreeHostImpl* host_impl);
   virtual void DidSetVisibleOnImplTree(LayerTreeHostImpl* host_impl,
                                        bool visible) {}
+  virtual base::TimeDelta LowFrequencyAnimationInterval() const;
 
   // Implementation of AnimationDelegate:
   virtual void NotifyAnimationStarted(double time) OVERRIDE {}
diff --git a/cc/test/pixel_test.cc b/cc/test/pixel_test.cc
index dffe378..73c4437 100644
--- a/cc/test/pixel_test.cc
+++ b/cc/test/pixel_test.cc
@@ -89,10 +89,12 @@
 PixelTest::~PixelTest() {}
 
 bool PixelTest::RunPixelTest(RenderPassList* pass_list,
+                             OffscreenContextOption provide_offscreen_context,
                              const base::FilePath& ref_file,
                              const PixelComparator& comparator) {
   return RunPixelTestWithReadbackTarget(pass_list,
                                         pass_list->back(),
+                                        provide_offscreen_context,
                                         ref_file,
                                         comparator);
 }
@@ -100,6 +102,7 @@
 bool PixelTest::RunPixelTestWithReadbackTarget(
     RenderPassList* pass_list,
     RenderPass* target,
+    OffscreenContextOption provide_offscreen_context,
     const base::FilePath& ref_file,
     const PixelComparator& comparator) {
   base::RunLoop run_loop;
@@ -109,8 +112,19 @@
                  base::Unretained(this),
                  run_loop.QuitClosure())));
 
+  scoped_refptr<webkit::gpu::ContextProviderInProcess> offscreen_contexts;
+  switch (provide_offscreen_context) {
+    case NoOffscreenContext:
+      break;
+    case WithOffscreenContext:
+      offscreen_contexts =
+          webkit::gpu::ContextProviderInProcess::CreateOffscreen();
+      CHECK(offscreen_contexts->BindToCurrentThread());
+      break;
+  }
+
   renderer_->DecideRenderPassAllocationsForFrame(*pass_list);
-  renderer_->DrawFrame(pass_list);
+  renderer_->DrawFrame(pass_list, offscreen_contexts.get());
 
   // Wait for the readback to complete.
   resource_provider_->Finish();
@@ -159,11 +173,6 @@
                                  resource_provider_.get(),
                                  0,
                                  use_skia_gpu_backend).PassAs<DirectRenderer>();
-
-  scoped_refptr<webkit::gpu::ContextProviderInProcess> offscreen_contexts =
-      webkit::gpu::ContextProviderInProcess::CreateOffscreen();
-  ASSERT_TRUE(offscreen_contexts->BindToCurrentThread());
-  resource_provider_->set_offscreen_context_provider(offscreen_contexts);
 }
 
 void PixelTest::ForceExpandedViewport(gfx::Size surface_expansion,
diff --git a/cc/test/pixel_test.h b/cc/test/pixel_test.h
index 1dd3e1a..e3476bf 100644
--- a/cc/test/pixel_test.h
+++ b/cc/test/pixel_test.h
@@ -25,14 +25,22 @@
   PixelTest();
   virtual ~PixelTest();
 
+  enum OffscreenContextOption {
+    NoOffscreenContext,
+    WithOffscreenContext
+  };
+
   bool RunPixelTest(RenderPassList* pass_list,
+                    OffscreenContextOption provide_offscreen_context,
                     const base::FilePath& ref_file,
                     const PixelComparator& comparator);
 
-  bool RunPixelTestWithReadbackTarget(RenderPassList* pass_list,
-                                      RenderPass* target,
-                                      const base::FilePath& ref_file,
-                                      const PixelComparator& comparator);
+  bool RunPixelTestWithReadbackTarget(
+      RenderPassList* pass_list,
+      RenderPass* target,
+      OffscreenContextOption provide_offscreen_context,
+      const base::FilePath& ref_file,
+      const PixelComparator& comparator);
 
   gfx::Size device_viewport_size_;
   class PixelTestRendererClient;
diff --git a/cc/test/skia_common.cc b/cc/test/skia_common.cc
index 5c83f13..67c72f4 100644
--- a/cc/test/skia_common.cc
+++ b/cc/test/skia_common.cc
@@ -6,7 +6,7 @@
 
 #include "cc/resources/picture.h"
 #include "skia/ext/refptr.h"
-#include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
 #include "ui/gfx/rect.h"
 #include "ui/gfx/skia_util.h"
 
@@ -65,7 +65,7 @@
                    layer_rect.width(),
                    layer_rect.height());
   bitmap.setPixels(buffer);
-  SkDevice device(bitmap);
+  SkBitmapDevice device(bitmap);
   SkCanvas canvas(&device);
   canvas.clipRect(gfx::RectToSkRect(layer_rect));
   picture->Raster(&canvas, NULL, layer_rect, 1.0f);
diff --git a/cc/test/tiled_layer_test_common.cc b/cc/test/tiled_layer_test_common.cc
index 92fc9b9..79d658d 100644
--- a/cc/test/tiled_layer_test_common.cc
+++ b/cc/test/tiled_layer_test_common.cc
@@ -109,7 +109,7 @@
   }
 }
 
-PrioritizedResourceManager* FakeTiledLayer::ResourceManager() const {
+PrioritizedResourceManager* FakeTiledLayer::ResourceManager() {
   return resource_manager_;
 }
 
diff --git a/cc/test/tiled_layer_test_common.h b/cc/test/tiled_layer_test_common.h
index c793c00..ea1eb12 100644
--- a/cc/test/tiled_layer_test_common.h
+++ b/cc/test/tiled_layer_test_common.h
@@ -112,7 +112,7 @@
   virtual void SetTexturePriorities(
       const PriorityCalculator& priority_calculator) OVERRIDE;
 
-  virtual PrioritizedResourceManager* ResourceManager() const OVERRIDE;
+  virtual PrioritizedResourceManager* ResourceManager() OVERRIDE;
   FakeLayerUpdater* fake_layer_updater() { return fake_updater_.get(); }
   gfx::RectF update_rect() { return update_rect_; }
 
diff --git a/cc/trees/blocking_task_runner.cc b/cc/trees/blocking_task_runner.cc
new file mode 100644
index 0000000..576aa9b
--- /dev/null
+++ b/cc/trees/blocking_task_runner.cc
@@ -0,0 +1,105 @@
+// 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 "cc/trees/blocking_task_runner.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/message_loop/message_loop_proxy.h"
+
+namespace cc {
+
+typedef std::pair<base::SingleThreadTaskRunner*,
+                  scoped_refptr<BlockingTaskRunner> > TaskRunnerPair;
+
+struct TaskRunnerPairs {
+  static TaskRunnerPairs* GetInstance() {
+    return Singleton<TaskRunnerPairs>::get();
+  }
+
+  base::Lock lock;
+  std::vector<TaskRunnerPair> pairs;
+
+ private:
+  friend struct DefaultSingletonTraits<TaskRunnerPairs>;
+};
+
+// static
+scoped_refptr<BlockingTaskRunner> BlockingTaskRunner::current() {
+  TaskRunnerPairs* task_runners = TaskRunnerPairs::GetInstance();
+
+  base::AutoLock lock(task_runners->lock);
+
+  for (size_t i = 0; i < task_runners->pairs.size(); ++i) {
+    if (task_runners->pairs[i].first->HasOneRef()) {
+      // The SingleThreadTaskRunner is kept alive by its MessageLoop, and we
+      // hold a second reference in the TaskRunnerPairs array. If the
+      // SingleThreadTaskRunner has one ref, then it is being held alive only
+      // by the BlockingTaskRunner and the MessageLoop is gone, so drop the
+      // BlockingTaskRunner from the TaskRunnerPairs array along with the
+      // SingleThreadTaskRunner.
+      task_runners->pairs.erase(task_runners->pairs.begin() + i);
+      --i;
+    }
+  }
+
+  scoped_refptr<base::SingleThreadTaskRunner> current =
+      base::MessageLoopProxy::current();
+  for (size_t i = 0; i < task_runners->pairs.size(); ++i) {
+    if (task_runners->pairs[i].first == current.get())
+      return task_runners->pairs[i].second.get();
+  }
+
+  scoped_refptr<BlockingTaskRunner> runner = new BlockingTaskRunner(current);
+  task_runners->pairs.push_back(TaskRunnerPair(current, runner));
+  return runner;
+}
+
+BlockingTaskRunner::BlockingTaskRunner(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : task_runner_(task_runner), capture_(0) {}
+
+BlockingTaskRunner::~BlockingTaskRunner() {}
+
+bool BlockingTaskRunner::PostTask(const tracked_objects::Location& from_here,
+                                  const base::Closure& task) {
+  base::AutoLock lock(lock_);
+  if (!capture_)
+    return task_runner_->PostTask(from_here, task);
+  captured_tasks_.push_back(task);
+  return true;
+}
+
+void BlockingTaskRunner::SetCapture(bool capture) {
+  DCHECK(BelongsToCurrentThread());
+
+  std::vector<base::Closure> tasks;
+
+  {
+    base::AutoLock lock(lock_);
+    capture_ += capture ? 1 : -1;
+    DCHECK_GE(capture_, 0);
+
+    if (capture_)
+      return;
+
+    // We're done capturing, so grab all the captured tasks and run them.
+    tasks.swap(captured_tasks_);
+  }
+  for (size_t i = 0; i < tasks.size(); ++i)
+    tasks[i].Run();
+}
+
+BlockingTaskRunner::CapturePostTasks::CapturePostTasks()
+    : blocking_runner_(BlockingTaskRunner::current()) {
+  blocking_runner_->SetCapture(true);
+}
+
+BlockingTaskRunner::CapturePostTasks::~CapturePostTasks() {
+  blocking_runner_->SetCapture(false);
+}
+
+}  // namespace cc
diff --git a/cc/trees/blocking_task_runner.h b/cc/trees/blocking_task_runner.h
new file mode 100644
index 0000000..2863317
--- /dev/null
+++ b/cc/trees/blocking_task_runner.h
@@ -0,0 +1,94 @@
+// 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 CC_TREES_BLOCKING_TASK_RUNNER_H_
+#define CC_TREES_BLOCKING_TASK_RUNNER_H_
+
+#include <vector>
+
+#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "cc/base/cc_export.h"
+
+namespace cc {
+
+// This class wraps a SingleThreadTaskRunner but allows posted tasks to be
+// run without a round trip through the message loop. This shortcutting
+// removes guarantees about ordering. Tasks posted while the
+// BlockingTaskRunner is in a capturing state will run in order, and tasks
+// posted while the BlockingTaskRunner is /not/ in a capturing state will
+// run in order, but the two sets of tasks will *not* run in order relative
+// to when they were posted.
+//
+// To use this class, post tasks to the task runner returned by
+// BlockingTaskRunner::current() on the thread you want the tasks to run.
+// Hold a reference to the BlockingTaskRunner as long as you intend to
+// post tasks to it.
+//
+// Then, on the thread from which the BlockingTaskRunner was created, you
+// may instantiate a BlockingTaskRunner::CapturePostTasks. While this object
+// exists, the task runner will collect any PostTasks called on it, posting
+// tasks to that thread from anywhere. This CapturePostTasks object provides
+// a window in time where tasks can shortcut past the MessageLoop. As soon
+// as the CapturePostTasks object is destroyed (goes out of scope), all
+// tasks that had been posted to the thread during the window will be exectuted
+// immediately.
+//
+// Beware of re-entrancy, make sure the CapturePostTasks object is destroyed at
+// a time when it makes sense for the embedder to call arbitrary things.
+class CC_EXPORT BlockingTaskRunner
+    : public base::RefCountedThreadSafe<BlockingTaskRunner> {
+ public:
+  // Returns the BlockingTaskRunner for the current thread, creating one if
+  // necessary.
+  static scoped_refptr<BlockingTaskRunner> current();
+
+  // While an object of this type is held alive on a thread, any tasks
+  // posted to the thread will be captured and run as soon as the object
+  // is destroyed, shortcutting past the MessageLoop.
+  class CC_EXPORT CapturePostTasks {
+   public:
+    CapturePostTasks();
+    ~CapturePostTasks();
+
+   private:
+    scoped_refptr<BlockingTaskRunner> blocking_runner_;
+
+    DISALLOW_COPY_AND_ASSIGN(CapturePostTasks);
+  };
+
+  // True if tasks posted to the BlockingTaskRunner will run on the current
+  // thread.
+  bool BelongsToCurrentThread() {
+    return task_runner_->BelongsToCurrentThread();
+  }
+
+  // Posts a task using the contained SingleThreadTaskRunner unless |capture_|
+  // is true. When |capture_| is true, tasks posted will be caught and stored
+  // until the capturing stops. At that time the tasks will be run directly
+  // instead of being posted to the SingleThreadTaskRunner.
+  bool PostTask(const tracked_objects::Location& from_here,
+                const base::Closure& task);
+
+ private:
+  friend class base::RefCountedThreadSafe<BlockingTaskRunner>;
+
+  explicit BlockingTaskRunner(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+  virtual ~BlockingTaskRunner();
+
+  void SetCapture(bool capture);
+
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  base::Lock lock_;
+  int capture_;
+  std::vector<base::Closure> captured_tasks_;
+};
+
+}  // namespace cc
+
+#endif  // CC_TREES_BLOCKING_TASK_RUNNER_H_
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index cb76b46..27659e0 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -518,6 +518,10 @@
   return proxy_->CommitRequested();
 }
 
+void LayerTreeHost::SetNextCommitWaitsForActivation() {
+  proxy_->SetNextCommitWaitsForActivation();
+}
+
 void LayerTreeHost::SetAnimationEvents(scoped_ptr<AnimationEventsVector> events,
                                        base::Time wall_clock_time) {
   DCHECK(proxy_->IsMainThread());
@@ -545,9 +549,6 @@
         case AnimationEvent::PropertyUpdate:
           (*iter).second->NotifyAnimationPropertyUpdate((*events)[event_index]);
           break;
-
-        default:
-          NOTREACHED();
       }
     }
   }
@@ -1092,12 +1093,6 @@
                  animate));
 }
 
-bool LayerTreeHost::BlocksPendingCommit() const {
-  if (!root_layer_.get())
-    return false;
-  return root_layer_->BlocksPendingCommitRecursive();
-}
-
 scoped_ptr<base::Value> LayerTreeHost::AsValue() const {
   scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue());
   state->Set("proxy", proxy_->AsValue().release());
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index 619c18e..be0ab06 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -191,6 +191,8 @@
   void SetNeedsRedrawRect(gfx::Rect damage_rect);
   bool CommitRequested() const;
 
+  void SetNextCommitWaitsForActivation();
+
   void SetAnimationEvents(scoped_ptr<AnimationEventsVector> events,
                           base::Time wall_clock_time);
 
@@ -266,8 +268,6 @@
     return animation_registrar_.get();
   }
 
-  bool BlocksPendingCommit() const;
-
   // Obtains a thorough dump of the LayerTreeHost as a value.
   scoped_ptr<base::Value> AsValue() const;
 
diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc
index dba7b50..7855421 100644
--- a/cc/trees/layer_tree_host_common.cc
+++ b/cc/trees/layer_tree_host_common.cc
@@ -40,6 +40,29 @@
   layer_sorter->Sort(first, end);
 }
 
+template <typename LayerType>
+static gfx::Vector2dF GetEffectiveScrollDelta(LayerType* layer) {
+  gfx::Vector2dF scroll_delta = layer->ScrollDelta();
+  // The scroll parent's scroll delta is the amount we've scrolled on the
+  // compositor thread since the commit for this layer tree's source frame.
+  // we last reported to the main thread. I.e., it's the discrepancy between
+  // a scroll parent's scroll delta and offset, so we must add it here.
+  if (layer->scroll_parent())
+    scroll_delta += layer->scroll_parent()->ScrollDelta();
+  return scroll_delta;
+}
+
+template <typename LayerType>
+static gfx::Vector2dF GetEffectiveTotalScrollOffset(LayerType* layer) {
+  gfx::Vector2dF offset = layer->TotalScrollOffset();
+  // The scroll parent's total scroll offset (scroll offset + scroll delta)
+  // can't be used because its scroll offset has already been applied to the
+  // scroll children's positions by the main thread layer positioning code.
+  if (layer->scroll_parent())
+    offset += layer->scroll_parent()->ScrollDelta();
+  return offset;
+}
+
 inline gfx::Rect CalculateVisibleRectWithCachedLayerRect(
     gfx::Rect target_surface_rect,
     gfx::Rect layer_bound_rect,
@@ -90,6 +113,196 @@
       target_surface_rect, layer_bound_rect, layer_in_surface_space, transform);
 }
 
+template <typename LayerType>
+static LayerType* NextTargetSurface(LayerType* layer) {
+  return layer->parent() ? layer->parent()->render_target() : 0;
+}
+
+// Given two layers, this function finds their respective render targets and,
+// computes a change of basis translation. It does this by accumulating the
+// translation components of the draw transforms of each target between the
+// ancestor and descendant. These transforms must be 2D translations, and this
+// requirement is enforced at every step.
+template <typename LayerType, typename RenderSurfaceType>
+static gfx::Vector2dF ComputeChangeOfBasisTranslation(
+    const LayerType& ancestor_layer,
+    const LayerType& descendant_layer) {
+  DCHECK(descendant_layer.HasAncestor(&ancestor_layer));
+  const LayerType* descendant_target = descendant_layer.render_target();
+  DCHECK(descendant_target);
+  const LayerType* ancestor_target = ancestor_layer.render_target();
+  DCHECK(ancestor_target);
+
+  gfx::Vector2dF translation;
+  for (const LayerType* target = descendant_target; target != ancestor_target;
+       target = NextTargetSurface(target))
+    translation += target->render_surface()->draw_transform().To2dTranslation();
+
+  return translation;
+}
+
+enum TranslateRectDirection {
+  TranslateRectDirectionToAncestor,
+  TranslateRectDirectionToDescendant
+};
+
+template <typename LayerType, typename RenderSurfaceType>
+static gfx::Rect TranslateRectToTargetSpace(const LayerType& ancestor_layer,
+                                            const LayerType& descendant_layer,
+                                            gfx::Rect rect,
+                                            TranslateRectDirection direction) {
+  gfx::Vector2dF translation =
+      ComputeChangeOfBasisTranslation<LayerType, RenderSurfaceType>(
+          ancestor_layer, descendant_layer);
+  if (direction == TranslateRectDirectionToDescendant)
+    translation.Scale(-1.f);
+  return gfx::ToEnclosingRect(
+      gfx::RectF(rect.origin() + translation, rect.size()));
+}
+
+// Attempts to update the clip rects for the given layer. If the layer has a
+// clip_parent, it may not inherit its immediate ancestor's clip.
+template <typename LayerType, typename RenderSurfaceType>
+static void UpdateClipRectsForClipChild(
+    const LayerType* layer,
+    gfx::Rect* clip_rect_in_parent_target_space,
+    bool* subtree_should_be_clipped) {
+  // If the layer has no clip_parent, or the ancestor is the same as its actual
+  // parent, then we don't need special clip rects. Bail now and leave the out
+  // parameters untouched.
+  const LayerType* clip_parent = layer->clip_parent();
+  if (!clip_parent || clip_parent == layer->parent())
+    return;
+
+  // The root layer is never a clip child.
+  DCHECK(layer->parent());
+
+  // Grab the cached values.
+  *clip_rect_in_parent_target_space = clip_parent->clip_rect();
+  *subtree_should_be_clipped = clip_parent->is_clipped();
+
+  // We may have to project the clip rect into our parent's target space. Note,
+  // it must be our parent's target space, not ours. For one, we haven't
+  // computed our transforms, so we couldn't put it in our space yet even if we
+  // wanted to. But more importantly, this matches the expectations of
+  // CalculateDrawPropertiesInternal. If we, say, create a render surface, these
+  // clip rects will want to be in its target space, not ours.
+  *clip_rect_in_parent_target_space =
+      TranslateRectToTargetSpace<LayerType, RenderSurfaceType>(
+          *clip_parent,
+          *layer->parent(),
+          *clip_rect_in_parent_target_space,
+          TranslateRectDirectionToDescendant);
+}
+
+// We collect an accumulated drawable content rect per render surface.
+// Typically, a layer will contribute to only one surface, the surface
+// associated with its render target. Clip children, however, may affect
+// several surfaces since there may be several surfaces between the clip child
+// and its parent.
+//
+// NB: we accumulate the layer's *clipped* drawable content rect.
+template <typename LayerType>
+struct AccumulatedSurfaceState {
+  explicit AccumulatedSurfaceState(LayerType* render_target)
+      : render_target(render_target) {}
+
+  // The accumulated drawable content rect for the surface associated with the
+  // given |render_target|.
+  gfx::Rect drawable_content_rect;
+
+  // The target owning the surface. (We hang onto the target rather than the
+  // surface so that we can DCHECK that the surface's draw transform is simply
+  // a translation when |render_target| reports that it has no unclipped
+  // descendants).
+  LayerType* render_target;
+};
+
+template <typename LayerType, typename RenderSurfaceType>
+void UpdateAccumulatedSurfaceState(
+    LayerType* layer,
+    gfx::Rect drawable_content_rect,
+    std::vector<AccumulatedSurfaceState<LayerType> >*
+        accumulated_surface_state) {
+  if (IsRootLayer(layer))
+    return;
+
+  // We will apply our drawable content rect to the accumulated rects for all
+  // surfaces between us and |render_target| (inclusive). This is either our
+  // clip parent's target if we are a clip child, or else simply our parent's
+  // target. We use our parent's target because we're either the owner of a
+  // render surface and we'll want to add our rect to our *surface's* target, or
+  // we're not and our target is the same as our parent's. In both cases, the
+  // parent's target gives us what we want.
+  LayerType* render_target = layer->clip_parent()
+                                 ? layer->clip_parent()->render_target()
+                                 : layer->parent()->render_target();
+
+  // If the layer owns a surface, then the content rect is in the wrong space.
+  // Instead, we will use the surface's DrawableContentRect which is in target
+  // space as required.
+  gfx::Rect target_rect = drawable_content_rect;
+  if (layer->render_surface()) {
+    target_rect =
+        gfx::ToEnclosedRect(layer->render_surface()->DrawableContentRect());
+  }
+
+  if (render_target->is_clipped()) {
+    gfx::Rect clip_rect = render_target->clip_rect();
+    // If the layer has a clip parent, the clip rect may be in the wrong space,
+    // so we'll need to transform it before it is applied.
+    if (layer->clip_parent()) {
+      clip_rect = TranslateRectToTargetSpace<LayerType, RenderSurfaceType>(
+          *layer->clip_parent(),
+          *layer,
+          clip_rect,
+          TranslateRectDirectionToDescendant);
+    }
+    target_rect.Intersect(clip_rect);
+  }
+
+  // We must have at least one entry in the vector for the root.
+  DCHECK_LT(0ul, accumulated_surface_state->size());
+
+  typedef typename std::vector<AccumulatedSurfaceState<LayerType> >
+      AccumulatedSurfaceStateVector;
+  typedef typename AccumulatedSurfaceStateVector::reverse_iterator
+      AccumulatedSurfaceStateIterator;
+  AccumulatedSurfaceStateIterator current_state =
+      accumulated_surface_state->rbegin();
+
+  // Add this rect to the accumulated content rect for all surfaces until we
+  // reach the target surface.
+  bool found_render_target = false;
+  for (; current_state != accumulated_surface_state->rend(); ++current_state) {
+    current_state->drawable_content_rect.Union(target_rect);
+
+    // If we've reached |render_target| our work is done and we can bail.
+    if (current_state->render_target == render_target) {
+      found_render_target = true;
+      break;
+    }
+
+    // Transform rect from the current target's space to the next.
+    LayerType* current_target = current_state->render_target;
+    DCHECK(current_target->render_surface());
+    const gfx::Transform& current_draw_transform =
+         current_target->render_surface()->draw_transform();
+
+    // If we have unclipped descendants, the draw transform is a translation.
+    DCHECK(current_target->num_unclipped_descendants() == 0 ||
+           current_draw_transform.IsIdentityOrTranslation());
+
+    target_rect = gfx::ToEnclosingRect(
+        MathUtil::MapClippedRect(current_draw_transform, target_rect));
+  }
+
+  // It is an error to not reach |render_target|. If this happens, it means that
+  // either the clip parent is not an ancestor of the clip child or the surface
+  // state vector is empty, both of which should be impossible.
+  DCHECK(found_render_target);
+}
+
 template <typename LayerType> static inline bool IsRootLayer(LayerType* layer) {
   return !layer->parent();
 }
@@ -416,10 +629,6 @@
   return false;
 }
 
-static LayerImpl* NextTargetSurface(LayerImpl* layer) {
-  return layer->parent() ? layer->parent()->render_target() : 0;
-}
-
 // This function returns a translation matrix that can be applied on a vector
 // that's in the layer's target surface coordinate, while the position offset is
 // specified in some ancestor layer's coordinate.
@@ -550,9 +759,10 @@
   //
 
   gfx::Transform scroll_compensation_for_this_layer = parent_matrix;  // Step 3
+  gfx::Vector2dF scroll_delta = GetEffectiveScrollDelta(scrolling_layer);
   scroll_compensation_for_this_layer.Translate(
-      scrolling_layer->ScrollDelta().x(),
-      scrolling_layer->ScrollDelta().y());  // Step 2
+      scroll_delta.x(),
+      scroll_delta.y());  // Step 2
 
   gfx::Transform inverse_parent_matrix(gfx::Transform::kSkipInitialization);
   if (!parent_matrix.GetInverse(&inverse_parent_matrix)) {
@@ -601,8 +811,9 @@
   // Avoid the overheads (including stack allocation and matrix
   // initialization/copy) if we know that the scroll compensation doesn't need
   // to be reset or adjusted.
+  gfx::Vector2dF scroll_delta = GetEffectiveScrollDelta(layer);
   if (!layer->IsContainerForFixedPositionLayers() &&
-      layer->ScrollDelta().IsZero() && !layer->render_surface())
+      scroll_delta.IsZero() && !layer->render_surface())
     return current_scroll_compensation_matrix;
 
   // Start as identity matrix.
@@ -616,7 +827,7 @@
   // If the current layer has a non-zero scroll_delta, then we should compute
   // its local scroll compensation and accumulate it to the
   // next_scroll_compensation_matrix.
-  if (!layer->ScrollDelta().IsZero()) {
+  if (!scroll_delta.IsZero()) {
     gfx::Transform scroll_compensation_for_this_layer =
         ComputeScrollCompensationForThisLayer(
             layer, parent_matrix);
@@ -785,13 +996,17 @@
 
 struct PreCalculateMetaInformationRecursiveData {
   bool layer_or_descendant_has_copy_request;
+  int num_unclipped_descendants;
 
   PreCalculateMetaInformationRecursiveData()
-      : layer_or_descendant_has_copy_request(false) {}
+      : layer_or_descendant_has_copy_request(false),
+        num_unclipped_descendants(0) {}
 
   void Merge(const PreCalculateMetaInformationRecursiveData& data) {
     layer_or_descendant_has_copy_request |=
         data.layer_or_descendant_has_copy_request;
+    num_unclipped_descendants +=
+        data.num_unclipped_descendants;
   }
 };
 
@@ -814,6 +1029,9 @@
     descendants_can_clip_selves = false;
   }
 
+  if (layer->clip_parent())
+    recursive_data->num_unclipped_descendants++;
+
   for (size_t i = 0; i < layer->children().size(); ++i) {
     LayerType* child_layer =
         LayerTreeHostCommon::get_child_as_raw_ptr(layer->children(), i);
@@ -839,11 +1057,19 @@
     recursive_data->Merge(data_for_child);
   }
 
+  if (layer->clip_children()) {
+    int num_clip_children = layer->clip_children()->size();
+    DCHECK_GE(recursive_data->num_unclipped_descendants, num_clip_children);
+    recursive_data->num_unclipped_descendants -= num_clip_children;
+  }
+
   if (layer->HasCopyRequest())
     recursive_data->layer_or_descendant_has_copy_request = true;
 
   layer->draw_properties().num_descendants_that_draw_content =
       num_descendants_that_draw_content;
+  layer->draw_properties().num_unclipped_descendants =
+      recursive_data->num_unclipped_descendants;
   layer->draw_properties().descendants_can_clip_selves =
       descendants_can_clip_selves;
   layer->draw_properties().layer_or_descendant_has_copy_request =
@@ -917,7 +1143,8 @@
     const DataForRecursion<LayerType, RenderSurfaceType>& data_from_ancestor,
     LayerListType* render_surface_layer_list,
     LayerListType* layer_list,
-    gfx::Rect* drawable_content_rect_of_subtree) {
+    std::vector<AccumulatedSurfaceState<LayerType> >*
+        accumulated_surface_state) {
   // This function computes the new matrix transformations recursively for this
   // layer and all its descendants. It also computes the appropriate render
   // surfaces.
@@ -1045,10 +1272,6 @@
   DCHECK(globals.page_scale_application_layer ||
          (globals.page_scale_factor == 1.f));
 
-  // If we early-exit anywhere in this function, the drawable_content_rect of
-  // this subtree should be considered empty.
-  *drawable_content_rect_of_subtree = gfx::Rect();
-
   DataForRecursion<LayerType, RenderSurfaceType> data_for_children;
   RenderSurfaceType* nearest_ancestor_surface_that_moves_pixels =
       data_from_ancestor.nearest_ancestor_surface_that_moves_pixels;
@@ -1067,8 +1290,25 @@
     layer_is_visible = true;
 
   // The root layer cannot skip CalcDrawProperties.
-  if (!IsRootLayer(layer) && SubtreeShouldBeSkipped(layer, layer_is_visible))
+  if (!IsRootLayer(layer) && SubtreeShouldBeSkipped(layer, layer_is_visible)) {
+    if (layer->render_surface())
+      layer->ClearRenderSurface();
     return;
+  }
+
+  // We need to circumvent the normal recursive flow of information for clip
+  // children (they don't inherit their direct ancestor's clip information).
+  // This is unfortunate, and would be unnecessary if we were to formally
+  // separate the clipping hierarchy from the layer hierarchy.
+  bool ancestor_clips_subtree = data_from_ancestor.ancestor_clips_subtree;
+  gfx::Rect ancestor_clip_rect_in_target_space =
+      data_from_ancestor.clip_rect_in_target_space;
+
+  // Update our clipping state. If we have a clip parent we will need to pull
+  // from the clip state cache rather than using the clip state passed from our
+  // immediate ancestor.
+  UpdateClipRectsForClipChild<LayerType, RenderSurfaceType>(
+      layer, &ancestor_clip_rect_in_target_space, &ancestor_clips_subtree);
 
   // As this function proceeds, these are the properties for the current
   // layer that actually get computed. To avoid unnecessary copies
@@ -1108,7 +1348,8 @@
 
   gfx::Size bounds = layer->bounds();
   gfx::PointF anchor_point = layer->anchor_point();
-  gfx::PointF position = layer->position() - layer->TotalScrollOffset();
+  gfx::Vector2dF scroll_offset = GetEffectiveTotalScrollOffset(layer);
+  gfx::PointF position = layer->position() - scroll_offset;
 
   gfx::Transform combined_transform = data_from_ancestor.parent_matrix;
   if (!layer->transform().IsIdentity()) {
@@ -1214,8 +1455,10 @@
     // Check back-face visibility before continuing with this surface and its
     // subtree
     if (!layer->double_sided() && TransformToParentIsKnown(layer) &&
-        IsSurfaceBackFaceVisible(layer, combined_transform))
+        IsSurfaceBackFaceVisible(layer, combined_transform)) {
+      layer->ClearRenderSurface();
       return;
+    }
 
     RenderSurfaceType* render_surface = CreateOrReuseRenderSurface(layer);
 
@@ -1285,13 +1528,6 @@
     data_for_children.full_hierarchy_matrix.PreconcatTransform(
         render_surface->draw_transform());
 
-    // The new render_surface here will correctly clip the entire subtree. So,
-    // we do not need to continue propagating the clipping state further down
-    // the tree. This way, we can avoid transforming clip rects from ancestor
-    // target surface space to current target surface space that could cause
-    // more w < 0 headaches.
-    layer_or_ancestor_clips_descendants = false;
-
     if (layer->mask_layer()) {
       DrawProperties<LayerType, RenderSurfaceType>& mask_layer_draw_properties =
           layer->mask_layer()->draw_properties();
@@ -1314,14 +1550,15 @@
     if (layer->filters().HasFilterThatMovesPixels() || layer->filter())
       nearest_ancestor_surface_that_moves_pixels = render_surface;
 
-    // The render surface clip rect is expressed in the space where this surface
-    // draws, i.e. the same space as
-    // data_from_ancestor.clip_rect_in_target_space.
-    render_surface->SetIsClipped(data_from_ancestor.ancestor_clips_subtree);
-    if (data_from_ancestor.ancestor_clips_subtree) {
-      render_surface->SetClipRect(
-          data_from_ancestor.clip_rect_in_target_space);
+    render_surface->SetNearestAncestorThatMovesPixels(
+        nearest_ancestor_surface_that_moves_pixels);
 
+    layer_or_ancestor_clips_descendants = false;
+    bool subtree_is_clipped_by_surface_bounds = false;
+    if (ancestor_clips_subtree) {
+      // It may be the layer or the surface doing the clipping of the subtree,
+      // but in either case, we'll be clipping to the projected clip rect of our
+      // ancestor.
       gfx::Transform inverse_surface_draw_transform(
           gfx::Transform::kSkipInitialization);
       if (!render_surface->draw_transform().GetInverse(
@@ -1329,18 +1566,48 @@
         // TODO(shawnsingh): Either we need to handle uninvertible transforms
         // here, or DCHECK that the transform is invertible.
       }
-      clip_rect_of_target_surface_in_target_space =
-          gfx::ToEnclosingRect(MathUtil::ProjectClippedRect(
-              inverse_surface_draw_transform, render_surface->clip_rect()));
-    } else {
+
+      gfx::Rect projected_surface_rect = gfx::ToEnclosingRect(
+          MathUtil::ProjectClippedRect(inverse_surface_draw_transform,
+                                       ancestor_clip_rect_in_target_space));
+
+      if (layer_draw_properties.num_unclipped_descendants > 0) {
+        // If we have unclipped descendants, we cannot count on the render
+        // surface's bounds clipping our subtree: the unclipped descendants
+        // could cause us to expand our bounds. In this case, we must rely on
+        // layer clipping for correctess. NB: since we can only encounter
+        // translations between a clip child and its clip parent, clipping is
+        // guaranteed to be exact in this case.
+        layer_or_ancestor_clips_descendants = true;
+        clip_rect_in_target_space = projected_surface_rect;
+      } else {
+        // The new render_surface here will correctly clip the entire subtree.
+        // So, we do not need to continue propagating the clipping state further
+        // down the tree. This way, we can avoid transforming clip rects from
+        // ancestor target surface space to current target surface space that
+        // could cause more w < 0 headaches. The render surface clip rect is
+        // expressed in the space where this surface draws, i.e. the same space
+        // as clip_rect_from_ancestor_in_ancestor_target_space.
+        render_surface->SetClipRect(ancestor_clip_rect_in_target_space);
+        clip_rect_of_target_surface_in_target_space = projected_surface_rect;
+        subtree_is_clipped_by_surface_bounds = true;
+      }
+    }
+
+    DCHECK(layer->render_surface());
+    DCHECK(!layer->parent() || layer->parent()->render_target() ==
+           accumulated_surface_state->back().render_target);
+
+    accumulated_surface_state->push_back(
+        AccumulatedSurfaceState<LayerType>(layer));
+
+    render_surface->SetIsClipped(subtree_is_clipped_by_surface_bounds);
+    if (!subtree_is_clipped_by_surface_bounds) {
       render_surface->SetClipRect(gfx::Rect());
       clip_rect_of_target_surface_in_target_space =
           data_from_ancestor.clip_rect_of_target_surface_in_target_space;
     }
 
-    render_surface->SetNearestAncestorThatMovesPixels(
-        nearest_ancestor_surface_that_moves_pixels);
-
     // If the new render surface is drawn translucent or with a non-integral
     // translation then the subtree that gets drawn on this render surface
     // cannot use LCD text.
@@ -1366,11 +1633,10 @@
 
     // Layers without render_surfaces directly inherit the ancestor's clip
     // status.
-    layer_or_ancestor_clips_descendants =
-        data_from_ancestor.ancestor_clips_subtree;
-    if (data_from_ancestor.ancestor_clips_subtree) {
+    layer_or_ancestor_clips_descendants = ancestor_clips_subtree;
+    if (ancestor_clips_subtree) {
       clip_rect_in_target_space =
-          data_from_ancestor.clip_rect_in_target_space;
+          ancestor_clip_rect_in_target_space;
     }
 
     // The surface's cached clip rect value propagates regardless of what
@@ -1406,16 +1672,30 @@
 
   if (LayerClipsSubtree(layer)) {
     layer_or_ancestor_clips_descendants = true;
-    if (data_from_ancestor.ancestor_clips_subtree && !layer->render_surface()) {
+    if (ancestor_clips_subtree && !layer->render_surface()) {
       // A layer without render surface shares the same target as its ancestor.
       clip_rect_in_target_space =
-          data_from_ancestor.clip_rect_in_target_space;
+          ancestor_clip_rect_in_target_space;
       clip_rect_in_target_space.Intersect(rect_in_target_space);
     } else {
       clip_rect_in_target_space = rect_in_target_space;
     }
   }
 
+  // Tell the layer the rect that it's clipped by. In theory we could use a
+  // tighter clip rect here (drawable_content_rect), but that actually does not
+  // reduce how much would be drawn, and instead it would create unnecessary
+  // changes to scissor state affecting GPU performance. Our clip information
+  // is used in the recursion below, so we must set it beforehand.
+  layer_draw_properties.is_clipped = layer_or_ancestor_clips_descendants;
+  if (layer_or_ancestor_clips_descendants) {
+    layer_draw_properties.clip_rect = clip_rect_in_target_space;
+  } else {
+    // Initialize the clip rect to a safe value that will not clip the
+    // layer, just in case clipping is still accidentally used.
+    layer_draw_properties.clip_rect = rect_in_target_space;
+  }
+
   LayerListType& descendants =
       (layer->render_surface() ? layer->render_surface()->layer_list()
                                : *layer_list);
@@ -1470,7 +1750,6 @@
     data_for_children.subtree_is_visible_from_ancestor = layer_is_visible;
   }
 
-  gfx::Rect accumulated_drawable_content_rect_of_children;
   for (size_t i = 0; i < layer->children().size(); ++i) {
     LayerType* child =
         LayerTreeHostCommon::get_child_as_raw_ptr(layer->children(), i);
@@ -1484,29 +1763,34 @@
         data_for_children,
         render_surface_layer_list,
         &descendants,
-        &drawable_content_rect_of_child_subtree);
-    if (!drawable_content_rect_of_child_subtree.IsEmpty()) {
-      accumulated_drawable_content_rect_of_children.Union(
-          drawable_content_rect_of_child_subtree);
-      if (child->render_surface())
-        descendants.push_back(child);
+        accumulated_surface_state);
+    if (child->render_surface() &&
+        !child->render_surface()->content_rect().IsEmpty()) {
+      descendants.push_back(child);
     }
   }
 
+  // Compute the total drawable_content_rect for this subtree (the rect is in
+  // target surface space).
+  gfx::Rect local_drawable_content_rect_of_subtree =
+      accumulated_surface_state->back().drawable_content_rect;
+  if (layer->render_surface()) {
+    DCHECK(accumulated_surface_state->back().render_target == layer);
+    accumulated_surface_state->pop_back();
+  }
+
   if (layer->render_surface() && !IsRootLayer(layer) &&
       layer->render_surface()->layer_list().empty()) {
     RemoveSurfaceForEarlyExit(layer, render_surface_layer_list);
     return;
   }
 
-  // Compute the total drawable_content_rect for this subtree (the rect is in
-  // target surface space).
-  gfx::Rect local_drawable_content_rect_of_subtree =
-      accumulated_drawable_content_rect_of_children;
-  if (layer->DrawsContent())
-    local_drawable_content_rect_of_subtree.Union(rect_in_target_space);
-  if (layer_or_ancestor_clips_descendants)
-    local_drawable_content_rect_of_subtree.Intersect(clip_rect_in_target_space);
+  if (layer->DrawsContent()) {
+    gfx::Rect local_drawable_content_rect = rect_in_target_space;
+    if (layer_or_ancestor_clips_descendants)
+      local_drawable_content_rect.Intersect(clip_rect_in_target_space);
+    local_drawable_content_rect_of_subtree.Union(local_drawable_content_rect);
+  }
 
   // Compute the layer's drawable content rect (the rect is in target surface
   // space).
@@ -1516,19 +1800,6 @@
         Intersect(clip_rect_in_target_space);
   }
 
-  // Tell the layer the rect that is clipped by. In theory we could use a
-  // tighter clip rect here (drawable_content_rect), but that actually does not
-  // reduce how much would be drawn, and instead it would create unnecessary
-  // changes to scissor state affecting GPU performance.
-  layer_draw_properties.is_clipped = layer_or_ancestor_clips_descendants;
-  if (layer_or_ancestor_clips_descendants) {
-    layer_draw_properties.clip_rect = clip_rect_in_target_space;
-  } else {
-    // Initialize the clip rect to a safe value that will not clip the
-    // layer, just in case clipping is still accidentally used.
-    layer_draw_properties.clip_rect = rect_in_target_space;
-  }
-
   // Compute the layer's visible content rect (the rect is in content space).
   layer_draw_properties.visible_content_rect = CalculateVisibleContentRect(
       layer, clip_rect_of_target_surface_in_target_space, rect_in_target_space);
@@ -1539,7 +1810,7 @@
     // The root layer's surface's content_rect is always the entire viewport.
     DCHECK(layer->render_surface());
     layer->render_surface()->SetContentRect(
-        data_from_ancestor.clip_rect_in_target_space);
+        ancestor_clip_rect_in_target_space);
   } else if (layer->render_surface() && !IsRootLayer(layer)) {
     RenderSurfaceType* render_surface = layer->render_surface();
     gfx::Rect clipped_content_rect = local_drawable_content_rect_of_subtree;
@@ -1552,8 +1823,7 @@
       // Note, it is correct to use data_from_ancestor.ancestor_clips_subtree
       // here, because we are looking at this layer's render_surface, not the
       // layer itself.
-      if (data_from_ancestor.ancestor_clips_subtree &&
-          !clipped_content_rect.IsEmpty()) {
+      if (render_surface->is_clipped() && !clipped_content_rect.IsEmpty()) {
         gfx::Rect surface_clip_rect = LayerTreeHostCommon::CalculateVisibleRect(
             render_surface->clip_rect(),
             clipped_content_rect,
@@ -1638,12 +1908,8 @@
                globals.layer_sorter);
   }
 
-  if (layer->render_surface()) {
-    *drawable_content_rect_of_subtree =
-        gfx::ToEnclosingRect(layer->render_surface()->DrawableContentRect());
-  } else {
-    *drawable_content_rect_of_subtree = local_drawable_content_rect_of_subtree;
-  }
+  UpdateAccumulatedSurfaceState<LayerType, RenderSurfaceType>(
+      layer, local_drawable_content_rect_of_subtree, accumulated_surface_state);
 
   if (layer->HasContributingDelegatedRenderPasses()) {
     layer->render_target()->render_surface()->
@@ -1656,7 +1922,6 @@
   DCHECK(inputs->root_layer);
   DCHECK(IsRootLayer(inputs->root_layer));
   DCHECK(inputs->render_surface_layer_list);
-  gfx::Rect total_drawable_content_rect;
   gfx::Transform identity_matrix;
   gfx::Transform scaled_device_transform = inputs->device_transform;
   scaled_device_transform.Scale(inputs->device_scale_factor,
@@ -1691,14 +1956,14 @@
 
   PreCalculateMetaInformationRecursiveData recursive_data;
   PreCalculateMetaInformation(inputs->root_layer, &recursive_data);
-
+  std::vector<AccumulatedSurfaceState<Layer> > accumulated_surface_state;
   CalculateDrawPropertiesInternal<Layer, RenderSurfaceLayerList, RenderSurface>(
       inputs->root_layer,
       globals,
       data_for_recursion,
       inputs->render_surface_layer_list,
       &dummy_layer_list,
-      &total_drawable_content_rect);
+      &accumulated_surface_state);
 
   // The dummy layer list should not have been used.
   DCHECK_EQ(0u, dummy_layer_list.size());
@@ -1713,7 +1978,6 @@
   DCHECK(IsRootLayer(inputs->root_layer));
   DCHECK(inputs->render_surface_layer_list);
 
-  gfx::Rect total_drawable_content_rect;
   gfx::Transform identity_matrix;
   gfx::Transform scaled_device_transform = inputs->device_transform;
   scaled_device_transform.Scale(inputs->device_scale_factor,
@@ -1749,14 +2013,15 @@
 
   PreCalculateMetaInformationRecursiveData recursive_data;
   PreCalculateMetaInformation(inputs->root_layer, &recursive_data);
-
+  std::vector<AccumulatedSurfaceState<LayerImpl> >
+      accumulated_surface_state;
   CalculateDrawPropertiesInternal<LayerImpl, LayerImplList, RenderSurfaceImpl>(
       inputs->root_layer,
       globals,
       data_for_recursion,
       inputs->render_surface_layer_list,
       &dummy_layer_list,
-      &total_drawable_content_rect);
+      &accumulated_surface_state);
 
   // The dummy layer list should not have been used.
   DCHECK_EQ(0u, dummy_layer_list.size());
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc
index 49b80e8..cd71c88 100644
--- a/cc/trees/layer_tree_host_common_unittest.cc
+++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -164,6 +164,10 @@
                                    false);
   }
 
+  RenderSurfaceLayerList* render_surface_layer_list() const {
+    return render_surface_layer_list_.get();
+  }
+
  private:
   scoped_ptr<RenderSurfaceLayerList> render_surface_layer_list_;
 };
@@ -8350,5 +8354,623 @@
             surface_child->visible_content_rect().ToString());
 }
 
+TEST_F(LayerTreeHostCommonTest, TransformedClipParent) {
+  // Ensure that a transform between the layer and its render surface is not a
+  // problem. Constructs the following layer tree.
+  //
+  //   root (a render surface)
+  //     + render_surface
+  //       + clip_parent (scaled)
+  //         + intervening_clipping_layer
+  //           + clip_child
+  //
+  // The render surface should be resized correctly and the clip child should
+  // inherit the right clip rect.
+  scoped_refptr<Layer> root = Layer::Create();
+  scoped_refptr<Layer> render_surface = Layer::Create();
+  scoped_refptr<Layer> clip_parent = Layer::Create();
+  scoped_refptr<Layer> intervening = Layer::Create();
+  scoped_refptr<LayerWithForcedDrawsContent> clip_child =
+      make_scoped_refptr(new LayerWithForcedDrawsContent);
+
+  root->AddChild(render_surface);
+  render_surface->AddChild(clip_parent);
+  clip_parent->AddChild(intervening);
+  intervening->AddChild(clip_child);
+
+  clip_child->SetClipParent(clip_parent.get());
+
+  intervening->SetMasksToBounds(true);
+  clip_parent->SetMasksToBounds(true);
+
+  render_surface->SetForceRenderSurface(true);
+
+  gfx::Transform scale_transform;
+  scale_transform.Scale(2, 2);
+
+  gfx::Transform identity_transform;
+
+  SetLayerPropertiesForTesting(root.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(50, 50),
+                               false);
+  SetLayerPropertiesForTesting(render_surface.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(10, 10),
+                               false);
+  SetLayerPropertiesForTesting(clip_parent.get(),
+                               scale_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(1.f, 1.f),
+                               gfx::Size(10, 10),
+                               false);
+  SetLayerPropertiesForTesting(intervening.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(1.f, 1.f),
+                               gfx::Size(5, 5),
+                               false);
+  SetLayerPropertiesForTesting(clip_child.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(1.f, 1.f),
+                               gfx::Size(10, 10),
+                               false);
+
+  scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
+  host->SetRootLayer(root);
+
+  ExecuteCalculateDrawProperties(root.get());
+
+  ASSERT_TRUE(root->render_surface());
+  ASSERT_TRUE(render_surface->render_surface());
+
+  // Ensure that we've inherited our clip parent's clip and weren't affected
+  // by the intervening clip layer.
+  ASSERT_EQ(gfx::Rect(1, 1, 20, 20).ToString(),
+            clip_parent->clip_rect().ToString());
+  ASSERT_EQ(clip_parent->clip_rect().ToString(),
+            clip_child->clip_rect().ToString());
+  ASSERT_EQ(gfx::Rect(3, 3, 10, 10).ToString(),
+            intervening->clip_rect().ToString());
+
+  // Ensure that the render surface reports a content rect that has been grown
+  // to accomodate for the clip child.
+  ASSERT_EQ(gfx::Rect(5, 5, 16, 16).ToString(),
+            render_surface->render_surface()->content_rect().ToString());
+
+  // The above check implies the two below, but they nicely demonstrate that
+  // we've grown, despite the intervening layer's clip.
+  ASSERT_TRUE(clip_parent->clip_rect().Contains(
+      render_surface->render_surface()->content_rect()));
+  ASSERT_FALSE(intervening->clip_rect().Contains(
+      render_surface->render_surface()->content_rect()));
+}
+
+TEST_F(LayerTreeHostCommonTest, ClipParentWithInterveningRenderSurface) {
+  // Ensure that intervening render surfaces are not a problem in the basic
+  // case. In the following tree, both render surfaces should be resized to
+  // accomodate for the clip child, despite an intervening clip.
+  //
+  //   root (a render surface)
+  //    + clip_parent (masks to bounds)
+  //      + render_surface1 (sets opacity)
+  //        + intervening (masks to bounds)
+  //          + render_surface2 (also sets opacity)
+  //            + clip_child
+  //
+  scoped_refptr<Layer> root = Layer::Create();
+  scoped_refptr<Layer> clip_parent = Layer::Create();
+  scoped_refptr<Layer> render_surface1 = Layer::Create();
+  scoped_refptr<Layer> intervening = Layer::Create();
+  scoped_refptr<Layer> render_surface2 = Layer::Create();
+  scoped_refptr<LayerWithForcedDrawsContent> clip_child =
+      make_scoped_refptr(new LayerWithForcedDrawsContent);
+
+  root->AddChild(clip_parent);
+  clip_parent->AddChild(render_surface1);
+  render_surface1->AddChild(intervening);
+  intervening->AddChild(render_surface2);
+  render_surface2->AddChild(clip_child);
+
+  clip_child->SetClipParent(clip_parent.get());
+
+  intervening->SetMasksToBounds(true);
+  clip_parent->SetMasksToBounds(true);
+
+  render_surface1->SetForceRenderSurface(true);
+  render_surface2->SetForceRenderSurface(true);
+
+  gfx::Transform translation_transform;
+  translation_transform.Translate(2, 2);
+
+  gfx::Transform identity_transform;
+  SetLayerPropertiesForTesting(root.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(50, 50),
+                               false);
+  SetLayerPropertiesForTesting(clip_parent.get(),
+                               translation_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(1.f, 1.f),
+                               gfx::Size(40, 40),
+                               false);
+  SetLayerPropertiesForTesting(render_surface1.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(10, 10),
+                               false);
+  SetLayerPropertiesForTesting(intervening.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(1.f, 1.f),
+                               gfx::Size(5, 5),
+                               false);
+  SetLayerPropertiesForTesting(render_surface2.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(10, 10),
+                               false);
+  SetLayerPropertiesForTesting(clip_child.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(-10.f, -10.f),
+                               gfx::Size(60, 60),
+                               false);
+
+  scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
+  host->SetRootLayer(root);
+
+  ExecuteCalculateDrawProperties(root.get());
+
+  EXPECT_TRUE(root->render_surface());
+  EXPECT_TRUE(render_surface1->render_surface());
+  EXPECT_TRUE(render_surface2->render_surface());
+
+  // Since the render surfaces could have expanded, they should not clip (their
+  // bounds would no longer be reliable). We should resort to layer clipping
+  // in this case.
+  EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(),
+            render_surface1->render_surface()->clip_rect().ToString());
+  EXPECT_FALSE(render_surface1->render_surface()->is_clipped());
+  EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(),
+            render_surface2->render_surface()->clip_rect().ToString());
+  EXPECT_FALSE(render_surface2->render_surface()->is_clipped());
+
+  // NB: clip rects are in target space.
+  EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(),
+            render_surface1->clip_rect().ToString());
+  EXPECT_TRUE(render_surface1->is_clipped());
+
+  // This value is inherited from the clipping ancestor layer, 'intervening'.
+  EXPECT_EQ(gfx::Rect(0, 0, 5, 5).ToString(),
+            render_surface2->clip_rect().ToString());
+  EXPECT_TRUE(render_surface2->is_clipped());
+
+  // The content rects of both render surfaces should both have expanded to
+  // contain the clip child.
+  EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(),
+            render_surface1->render_surface()->content_rect().ToString());
+  EXPECT_EQ(gfx::Rect(-1, -1, 40, 40).ToString(),
+            render_surface2->render_surface()->content_rect().ToString());
+
+  // The clip child should have inherited the clip parent's clip (projected to
+  // the right space, of course), and should have the correctly sized visible
+  // content rect.
+  EXPECT_EQ(gfx::Rect(-1, -1, 40, 40).ToString(),
+            clip_child->clip_rect().ToString());
+  EXPECT_EQ(gfx::Rect(9, 9, 40, 40).ToString(),
+            clip_child->visible_content_rect().ToString());
+  EXPECT_TRUE(clip_child->is_clipped());
+}
+
+TEST_F(LayerTreeHostCommonTest, ClipParentScrolledInterveningLayer) {
+  // Ensure that intervening render surfaces are not a problem, even if there
+  // is a scroll involved. Note, we do _not_ have to consider any other sort
+  // of transform.
+  //
+  //   root (a render surface)
+  //    + clip_parent (masks to bounds)
+  //      + render_surface1 (sets opacity)
+  //        + intervening (masks to bounds AND scrolls)
+  //          + render_surface2 (also sets opacity)
+  //            + clip_child
+  //
+  scoped_refptr<Layer> root = Layer::Create();
+  scoped_refptr<Layer> clip_parent = Layer::Create();
+  scoped_refptr<Layer> render_surface1 = Layer::Create();
+  scoped_refptr<Layer> intervening = Layer::Create();
+  scoped_refptr<Layer> render_surface2 = Layer::Create();
+  scoped_refptr<LayerWithForcedDrawsContent> clip_child =
+      make_scoped_refptr(new LayerWithForcedDrawsContent);
+
+  root->AddChild(clip_parent);
+  clip_parent->AddChild(render_surface1);
+  render_surface1->AddChild(intervening);
+  intervening->AddChild(render_surface2);
+  render_surface2->AddChild(clip_child);
+
+  clip_child->SetClipParent(clip_parent.get());
+
+  intervening->SetMasksToBounds(true);
+  clip_parent->SetMasksToBounds(true);
+  intervening->SetScrollable(true);
+  intervening->SetMaxScrollOffset(gfx::Vector2d(50, 50));
+  intervening->SetScrollOffset(gfx::Vector2d(3, 3));
+
+  render_surface1->SetForceRenderSurface(true);
+  render_surface2->SetForceRenderSurface(true);
+
+  gfx::Transform translation_transform;
+  translation_transform.Translate(2, 2);
+
+  gfx::Transform identity_transform;
+  SetLayerPropertiesForTesting(root.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(50, 50),
+                               false);
+  SetLayerPropertiesForTesting(clip_parent.get(),
+                               translation_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(1.f, 1.f),
+                               gfx::Size(40, 40),
+                               false);
+  SetLayerPropertiesForTesting(render_surface1.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(10, 10),
+                               false);
+  SetLayerPropertiesForTesting(intervening.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(1.f, 1.f),
+                               gfx::Size(5, 5),
+                               false);
+  SetLayerPropertiesForTesting(render_surface2.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(10, 10),
+                               false);
+  SetLayerPropertiesForTesting(clip_child.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(-10.f, -10.f),
+                               gfx::Size(60, 60),
+                               false);
+
+  scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
+  host->SetRootLayer(root);
+
+  ExecuteCalculateDrawProperties(root.get());
+
+  EXPECT_TRUE(root->render_surface());
+  EXPECT_TRUE(render_surface1->render_surface());
+  EXPECT_TRUE(render_surface2->render_surface());
+
+  // Since the render surfaces could have expanded, they should not clip (their
+  // bounds would no longer be reliable). We should resort to layer clipping
+  // in this case.
+  EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(),
+            render_surface1->render_surface()->clip_rect().ToString());
+  EXPECT_FALSE(render_surface1->render_surface()->is_clipped());
+  EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(),
+            render_surface2->render_surface()->clip_rect().ToString());
+  EXPECT_FALSE(render_surface2->render_surface()->is_clipped());
+
+  // NB: clip rects are in target space.
+  EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(),
+            render_surface1->clip_rect().ToString());
+  EXPECT_TRUE(render_surface1->is_clipped());
+
+  // This value is inherited from the clipping ancestor layer, 'intervening'.
+  EXPECT_EQ(gfx::Rect(2, 2, 3, 3).ToString(),
+            render_surface2->clip_rect().ToString());
+  EXPECT_TRUE(render_surface2->is_clipped());
+
+  // The content rects of both render surfaces should both have expanded to
+  // contain the clip child.
+  EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(),
+            render_surface1->render_surface()->content_rect().ToString());
+  EXPECT_EQ(gfx::Rect(2, 2, 40, 40).ToString(),
+            render_surface2->render_surface()->content_rect().ToString());
+
+  // The clip child should have inherited the clip parent's clip (projected to
+  // the right space, of course), and should have the correctly sized visible
+  // content rect.
+  EXPECT_EQ(gfx::Rect(2, 2, 40, 40).ToString(),
+            clip_child->clip_rect().ToString());
+  EXPECT_EQ(gfx::Rect(12, 12, 40, 40).ToString(),
+            clip_child->visible_content_rect().ToString());
+  EXPECT_TRUE(clip_child->is_clipped());
+}
+
+TEST_F(LayerTreeHostCommonTest, DescendantsOfClipChildren) {
+  // Ensures that descendants of the clip child inherit the correct clip.
+  //
+  //   root (a render surface)
+  //    + clip_parent (masks to bounds)
+  //      + intervening (masks to bounds)
+  //        + clip_child
+  //          + child
+  //
+  scoped_refptr<Layer> root = Layer::Create();
+  scoped_refptr<Layer> clip_parent = Layer::Create();
+  scoped_refptr<Layer> intervening = Layer::Create();
+  scoped_refptr<Layer> clip_child = Layer::Create();
+  scoped_refptr<LayerWithForcedDrawsContent> child =
+      make_scoped_refptr(new LayerWithForcedDrawsContent);
+
+  root->AddChild(clip_parent);
+  clip_parent->AddChild(intervening);
+  intervening->AddChild(clip_child);
+  clip_child->AddChild(child);
+
+  clip_child->SetClipParent(clip_parent.get());
+
+  intervening->SetMasksToBounds(true);
+  clip_parent->SetMasksToBounds(true);
+
+  gfx::Transform identity_transform;
+  SetLayerPropertiesForTesting(root.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(50, 50),
+                               false);
+  SetLayerPropertiesForTesting(clip_parent.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(40, 40),
+                               false);
+  SetLayerPropertiesForTesting(intervening.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(5, 5),
+                               false);
+  SetLayerPropertiesForTesting(clip_child.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(60, 60),
+                               false);
+  SetLayerPropertiesForTesting(child.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(60, 60),
+                               false);
+
+  scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
+  host->SetRootLayer(root);
+
+  ExecuteCalculateDrawProperties(root.get());
+
+  EXPECT_TRUE(root->render_surface());
+
+  // Neither the clip child nor its descendant should have inherited the clip
+  // from |intervening|.
+  EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(),
+            clip_child->clip_rect().ToString());
+  EXPECT_TRUE(clip_child->is_clipped());
+  EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(),
+            child->visible_content_rect().ToString());
+  EXPECT_TRUE(child->is_clipped());
+}
+
+TEST_F(LayerTreeHostCommonTest,
+       SurfacesShouldBeUnaffectedByNonDescendantClipChildren) {
+  // Ensures that non-descendant clip children in the tree do not affect
+  // render surfaces.
+  //
+  //   root (a render surface)
+  //    + clip_parent (masks to bounds)
+  //      + render_surface1
+  //        + clip_child
+  //      + render_surface2
+  //        + non_clip_child
+  //
+  // In this example render_surface2 should be unaffected by clip_child.
+  scoped_refptr<Layer> root = Layer::Create();
+  scoped_refptr<Layer> clip_parent = Layer::Create();
+  scoped_refptr<Layer> render_surface1 = Layer::Create();
+  scoped_refptr<LayerWithForcedDrawsContent> clip_child =
+      make_scoped_refptr(new LayerWithForcedDrawsContent);
+  scoped_refptr<Layer> render_surface2 = Layer::Create();
+  scoped_refptr<LayerWithForcedDrawsContent> non_clip_child =
+      make_scoped_refptr(new LayerWithForcedDrawsContent);
+
+  root->AddChild(clip_parent);
+  clip_parent->AddChild(render_surface1);
+  render_surface1->AddChild(clip_child);
+  clip_parent->AddChild(render_surface2);
+  render_surface2->AddChild(non_clip_child);
+
+  clip_child->SetClipParent(clip_parent.get());
+
+  clip_parent->SetMasksToBounds(true);
+  render_surface1->SetMasksToBounds(true);
+
+  gfx::Transform identity_transform;
+  SetLayerPropertiesForTesting(root.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(15, 15),
+                               false);
+  SetLayerPropertiesForTesting(clip_parent.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(10, 10),
+                               false);
+  SetLayerPropertiesForTesting(render_surface1.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(5, 5),
+                               gfx::Size(5, 5),
+                               false);
+  SetLayerPropertiesForTesting(render_surface2.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(5, 5),
+                               false);
+  SetLayerPropertiesForTesting(clip_child.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(-1, 1),
+                               gfx::Size(10, 10),
+                               false);
+  SetLayerPropertiesForTesting(non_clip_child.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(5, 5),
+                               false);
+
+  render_surface1->SetForceRenderSurface(true);
+  render_surface2->SetForceRenderSurface(true);
+
+  scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
+  host->SetRootLayer(root);
+
+  ExecuteCalculateDrawProperties(root.get());
+
+  EXPECT_TRUE(root->render_surface());
+  EXPECT_TRUE(render_surface1->render_surface());
+  EXPECT_TRUE(render_surface2->render_surface());
+
+  EXPECT_EQ(gfx::Rect(0, 0, 5, 5).ToString(),
+            render_surface1->clip_rect().ToString());
+  EXPECT_TRUE(render_surface1->is_clipped());
+
+  // The render surface should not clip (it has unclipped descendants), instead
+  // it should rely on layer clipping.
+  EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(),
+            render_surface1->render_surface()->clip_rect().ToString());
+  EXPECT_FALSE(render_surface1->render_surface()->is_clipped());
+
+  // That said, it should have grown to accomodate the unclipped descendant.
+  EXPECT_EQ(gfx::Rect(-1, 1, 6, 4).ToString(),
+            render_surface1->render_surface()->content_rect().ToString());
+
+  // This render surface should clip. It has no unclipped descendants.
+  EXPECT_EQ(gfx::Rect(0, 0, 5, 5).ToString(),
+            render_surface2->clip_rect().ToString());
+  EXPECT_TRUE(render_surface2->render_surface()->is_clipped());
+
+  // It also shouldn't have grown to accomodate the clip child.
+  EXPECT_EQ(gfx::Rect(0, 0, 5, 5).ToString(),
+            render_surface2->render_surface()->content_rect().ToString());
+
+  // Sanity check our num_unclipped_descendants values.
+  EXPECT_EQ(1, render_surface1->num_unclipped_descendants());
+  EXPECT_EQ(0, render_surface2->num_unclipped_descendants());
+}
+
+TEST_F(LayerTreeHostCommonTest, DoNotIncludeBackfaceInvisibleSurfaces) {
+  scoped_refptr<Layer> root = Layer::Create();
+  scoped_refptr<Layer> render_surface = Layer::Create();
+  scoped_refptr<LayerWithForcedDrawsContent> child =
+      make_scoped_refptr(new LayerWithForcedDrawsContent);
+
+  root->AddChild(render_surface);
+  render_surface->AddChild(child);
+
+  gfx::Transform identity_transform;
+  SetLayerPropertiesForTesting(root.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(50, 50),
+                               false);
+  SetLayerPropertiesForTesting(render_surface.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(30, 30),
+                               false);
+  SetLayerPropertiesForTesting(child.get(),
+                               identity_transform,
+                               identity_transform,
+                               gfx::PointF(),
+                               gfx::PointF(),
+                               gfx::Size(20, 20),
+                               false);
+
+  root->SetPreserves3d(true);
+  render_surface->SetDoubleSided(false);
+  render_surface->SetForceRenderSurface(true);
+
+  scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
+  host->SetRootLayer(root);
+
+  ExecuteCalculateDrawProperties(root.get());
+
+  EXPECT_EQ(2u, render_surface_layer_list()->size());
+  EXPECT_EQ(1u,
+            render_surface_layer_list()->at(0)
+                ->render_surface()->layer_list().size());
+  EXPECT_EQ(1u,
+            render_surface_layer_list()->at(1)
+                ->render_surface()->layer_list().size());
+
+  gfx::Transform rotation_transform = identity_transform;
+  rotation_transform.RotateAboutXAxis(180.0);
+
+  render_surface->SetTransform(rotation_transform);
+
+  ExecuteCalculateDrawProperties(root.get());
+
+  EXPECT_EQ(1u, render_surface_layer_list()->size());
+  EXPECT_EQ(0u,
+            render_surface_layer_list()->at(0)
+                ->render_surface()->layer_list().size());
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 9f664fc..870b65d 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -110,12 +110,8 @@
 
     // TODO(enne): This should probably happen post-animate.
     if (layer_tree_host_impl_->pending_tree()) {
-      layer_tree_host_impl_->ActivatePendingTreeIfNeeded();
-
-      if (layer_tree_host_impl_->pending_tree()) {
-        layer_tree_host_impl_->pending_tree()->UpdateDrawProperties();
-        layer_tree_host_impl_->ManageTiles();
-      }
+      layer_tree_host_impl_->pending_tree()->UpdateDrawProperties();
+      layer_tree_host_impl_->ManageTiles();
     }
 
     layer_tree_host_impl_->Animate(
@@ -132,6 +128,8 @@
       time_source_->SetActive(active);
   }
 
+  bool Active() const { return time_source_->Active(); }
+
  private:
   LayerTreeHostImplTimeSourceAdapter(
       LayerTreeHostImpl* layer_tree_host_impl,
@@ -254,9 +252,14 @@
     pending_tree_->set_needs_update_draw_properties();
     pending_tree_->UpdateDrawProperties();
     // Start working on newly created tiles immediately if needed.
-    ManageTiles();
+    if (!tile_manager_ || !manage_tiles_needed_)
+      NotifyReadyToActivate();
+    else
+      ManageTiles();
   } else {
     active_tree_->set_needs_update_draw_properties();
+    if (time_source_client_adapter_ && time_source_client_adapter_->Active())
+      DCHECK(active_tree_->root_layer());
   }
 
   client_->SendManagedMemoryStats();
@@ -826,6 +829,8 @@
 void LayerTreeHostImpl::UpdateBackgroundAnimateTicking(
     bool should_background_tick) {
   DCHECK(proxy_->IsImplThread());
+  if (should_background_tick)
+    DCHECK(active_tree_->root_layer());
 
   bool enabled = should_background_tick &&
                  !animation_registrar_->active_animation_controllers().empty();
@@ -1026,6 +1031,10 @@
   EnforceManagedMemoryPolicy(ManagedMemoryPolicy(0));
 }
 
+void LayerTreeHostImpl::BlockNotifyReadyToActivateForTesting(bool block) {
+  NOTREACHED();
+}
+
 void LayerTreeHostImpl::EnforceManagedMemoryPolicy(
     const ManagedMemoryPolicy& policy) {
 
@@ -1085,10 +1094,7 @@
 }
 
 void LayerTreeHostImpl::NotifyReadyToActivate() {
-  if (pending_tree_) {
-    need_to_update_visible_tiles_before_draw_ = true;
-    ActivatePendingTree();
-  }
+  client_->NotifyReadyToActivate();
 }
 
 bool LayerTreeHostImpl::ShouldClearRootRenderPass() const {
@@ -1227,23 +1233,25 @@
 
 void LayerTreeHostImpl::DrawLayers(FrameData* frame,
                                    base::TimeTicks frame_begin_time) {
-  TRACE_EVENT_BEGIN0("cc", "LayerTreeHostImpl::DrawLayers");
+  TRACE_EVENT0("cc", "LayerTreeHostImpl::DrawLayers");
   DCHECK(CanDraw());
 
   if (frame->has_no_damage) {
     TRACE_EVENT0("cc", "EarlyOut_NoDamage");
     DCHECK(!output_surface_->capabilities()
                .draw_and_swap_full_viewport_every_frame);
-    TRACE_EVENT_END0("cc", "LayerTreeHostImpl::DrawLayers");
     return;
   }
 
   DCHECK(!frame->render_passes.empty());
 
   int old_dropped_frame_count = fps_counter_->dropped_frame_count();
-  fps_counter_->SaveTimeStamp(frame_begin_time);
+  fps_counter_->SaveTimeStamp(frame_begin_time,
+                              !output_surface_->context_provider());
 
-  rendering_stats_instrumentation_->IncrementScreenFrameCount(1);
+  bool on_main_thread = false;
+  rendering_stats_instrumentation_->IncrementScreenFrameCount(
+      1, on_main_thread);
   rendering_stats_instrumentation_->IncrementDroppedFrameCount(
       fps_counter_->dropped_frame_count() - old_dropped_frame_count);
 
@@ -1294,9 +1302,10 @@
   if (output_surface_->ForcedDrawToSoftwareDevice()) {
     scoped_ptr<SoftwareRenderer> temp_software_renderer =
         SoftwareRenderer::Create(this, output_surface_.get(), NULL);
-    temp_software_renderer->DrawFrame(&frame->render_passes);
+    temp_software_renderer->DrawFrame(&frame->render_passes, NULL);
   } else {
-    renderer_->DrawFrame(&frame->render_passes);
+    renderer_->DrawFrame(&frame->render_passes,
+                         offscreen_context_provider_.get());
   }
   // The render passes should be consumed by the renderer.
   DCHECK(frame->render_passes.empty());
@@ -1309,9 +1318,8 @@
         DidDrawDamagedArea();
   }
   active_tree_->root_layer()->ResetAllChangeTrackingForSubtree();
-  TRACE_EVENT_END1("cc", "LayerTreeHostImpl::DrawLayers",
-                   "data", rendering_stats_instrumentation_->
-                   GetImplThreadRenderingStats().AsTraceableData());
+
+  rendering_stats_instrumentation_->IssueTraceEventForImplThreadStats();
   rendering_stats_instrumentation_->AccumulateAndClearImplThreadStats();
 }
 
@@ -1433,7 +1441,6 @@
   else
     pending_tree_ = LayerTreeImpl::create(this);
   client_->OnCanDrawStateChanged(CanDraw());
-  client_->OnHasPendingTreeStateChanged(pending_tree_);
   TRACE_EVENT_ASYNC_BEGIN0("cc", "PendingTree", pending_tree_.get());
   TRACE_EVENT_ASYNC_STEP0("cc",
                           "PendingTree", pending_tree_.get(), "waiting");
@@ -1449,33 +1456,12 @@
   need_to_update_visible_tiles_before_draw_ = false;
 }
 
-void LayerTreeHostImpl::ActivatePendingTreeIfNeeded() {
-  DCHECK(pending_tree_);
-  CHECK(settings_.impl_side_painting);
-
-  if (!pending_tree_)
-    return;
-
-  // The tile manager is usually responsible for notifying activation.
-  // If there is no tile manager, then we need to manually activate.
-  if (!tile_manager_ || tile_manager_->AreTilesRequiredForActivationReady()) {
-    ActivatePendingTree();
-    return;
-  }
-
-  // Manage tiles in case state affecting tile priority has changed.
-  ManageTiles();
-
-  TRACE_EVENT_ASYNC_STEP1(
-    "cc",
-    "PendingTree", pending_tree_.get(), "activate",
-    "state", TracedValue::FromValue(ActivationStateAsValue().release()));
-}
-
 void LayerTreeHostImpl::ActivatePendingTree() {
   CHECK(pending_tree_);
   TRACE_EVENT_ASYNC_END0("cc", "PendingTree", pending_tree_.get());
 
+  need_to_update_visible_tiles_before_draw_ = true;
+
   active_tree_->SetRootLayerScrollOffsetDelegate(NULL);
   active_tree_->PushPersistedState(pending_tree_.get());
   if (pending_tree_->needs_full_tree_sync()) {
@@ -1509,7 +1495,6 @@
   client_->ReduceWastedContentsTextureMemoryOnImplThread();
 
   client_->OnCanDrawStateChanged(CanDraw());
-  client_->OnHasPendingTreeStateChanged(pending_tree_);
   client_->SetNeedsRedrawOnImplThread();
   client_->RenewTreePriority();
 
@@ -1524,6 +1509,9 @@
   client_->DidActivatePendingTree();
   if (!tree_activation_callback_.is_null())
     tree_activation_callback_.Run();
+
+  if (time_source_client_adapter_ && time_source_client_adapter_->Active())
+    DCHECK(active_tree_->root_layer());
 }
 
 void LayerTreeHostImpl::SetVisible(bool visible) {
@@ -1702,18 +1690,52 @@
 
   ReleaseTreeResources();
   renderer_.reset();
-  resource_provider_->InitializeGL();
-  bool skip_gl_renderer = false;
-  CreateAndSetRenderer(
-      output_surface_.get(), resource_provider_.get(), skip_gl_renderer);
 
-  bool success = !!renderer_.get();
-  client_->DidTryInitializeRendererOnImplThread(success,
-                                                offscreen_context_provider);
+  bool resource_provider_success = resource_provider_->InitializeGL();
+
+  bool success = resource_provider_success;
+  if (success) {
+    bool skip_gl_renderer = false;
+    CreateAndSetRenderer(
+        output_surface_.get(), resource_provider_.get(), skip_gl_renderer);
+    if (!renderer_)
+      success = false;
+  }
+
+  if (success) {
+    if (offscreen_context_provider.get() &&
+        !offscreen_context_provider->BindToCurrentThread())
+      success = false;
+  }
+
   if (success) {
     EnforceZeroBudget(false);
     client_->SetNeedsCommitOnImplThread();
+  } else {
+    if (offscreen_context_provider.get()) {
+      if (offscreen_context_provider->BindToCurrentThread())
+        offscreen_context_provider->VerifyContexts();
+      offscreen_context_provider = NULL;
+    }
+
+    client_->DidLoseOutputSurfaceOnImplThread();
+
+    if (resource_provider_success) {
+      // If this fails the context provider will be dropped from the output
+      // surface and destroyed. But the GLRenderer expects the output surface
+      // to stick around - and hold onto the context3d - as long as it is alive.
+      // TODO(danakj): Remove the need for this code path: crbug.com/276411
+      renderer_.reset();
+
+      // The resource provider can't stay in GL mode or it tries to clean up GL
+      // stuff, but the context provider is going away on the output surface
+      // which contradicts being in GL mode.
+      // TODO(danakj): Remove the need for this code path: crbug.com/276411
+      resource_provider_->InitializeSoftware();
+    }
   }
+
+  SetOffscreenContextProvider(offscreen_context_provider);
   return success;
 }
 
@@ -1738,9 +1760,8 @@
                           GetRendererCapabilities().using_map_image);
   DCHECK(tile_manager_);
 
-  bool success = true;
-  client_->DidTryInitializeRendererOnImplThread(
-      success, scoped_refptr<ContextProvider>());
+  SetOffscreenContextProvider(NULL);
+
   client_->SetNeedsCommitOnImplThread();
 }
 
@@ -1814,6 +1835,12 @@
   input_handler_client_ = client;
 }
 
+static LayerImpl* NextScrollLayer(LayerImpl* layer) {
+  if (LayerImpl* scroll_parent = layer->scroll_parent())
+    return scroll_parent;
+  return layer->parent();
+}
+
 InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin(
     gfx::Point viewport_point, InputHandler::ScrollInputType type) {
   TRACE_EVENT0("cc", "LayerTreeHostImpl::ScrollBegin");
@@ -1837,7 +1864,7 @@
 
   // Walk up the hierarchy and look for a scrollable layer.
   LayerImpl* potentially_scrolling_layer_impl = 0;
-  for (; layer_impl; layer_impl = layer_impl->parent()) {
+  for (; layer_impl; layer_impl = NextScrollLayer(layer_impl)) {
     // The content layer can also block attempts to scroll outside the main
     // thread.
     ScrollStatus status = layer_impl->TryScroll(device_viewport_point, type);
@@ -2335,6 +2362,21 @@
     SendReleaseResourcesRecursive(current->children()[i]);
 }
 
+void LayerTreeHostImpl::SetOffscreenContextProvider(
+    const scoped_refptr<ContextProvider>& offscreen_context_provider) {
+  if (!offscreen_context_provider.get()) {
+    offscreen_context_provider_ = NULL;
+    return;
+  }
+
+  if (!offscreen_context_provider->BindToCurrentThread()) {
+    offscreen_context_provider_ = NULL;
+    return;
+  }
+
+  offscreen_context_provider_ = offscreen_context_provider;
+}
+
 std::string LayerTreeHostImpl::LayerTreeAsJson() const {
   std::string str;
   if (active_tree_->root_layer()) {
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 31870ac..f5d7384 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -54,14 +54,11 @@
 // LayerTreeHost->Proxy callback interface.
 class LayerTreeHostImplClient {
  public:
-  virtual void DidTryInitializeRendererOnImplThread(
-      bool success,
-      scoped_refptr<ContextProvider> offscreen_context_provider) = 0;
   virtual void DidLoseOutputSurfaceOnImplThread() = 0;
   virtual void OnSwapBuffersCompleteOnImplThread() = 0;
   virtual void BeginFrameOnImplThread(const BeginFrameArgs& args) = 0;
   virtual void OnCanDrawStateChanged(bool can_draw) = 0;
-  virtual void OnHasPendingTreeStateChanged(bool has_pending_tree) = 0;
+  virtual void NotifyReadyToActivate() = 0;
   virtual void SetNeedsRedrawOnImplThread() = 0;
   virtual void SetNeedsRedrawRectOnImplThread(gfx::Rect damage_rect) = 0;
   virtual void DidInitializeVisibleTileOnImplThread() = 0;
@@ -184,6 +181,11 @@
   // Evict all textures by enforcing a memory policy with an allocation of 0.
   void EvictTexturesForTesting();
 
+  // When blocking, this prevents client_->NotifyReadyToActivate() from being
+  // called. When disabled, it calls client_->NotifyReadyToActivate()
+  // immediately if any notifications had been blocked while blocking.
+  virtual void BlockNotifyReadyToActivateForTesting(bool block);
+
   // RendererClient implementation
   virtual gfx::Rect DeviceViewport() const OVERRIDE;
  private:
@@ -219,10 +221,16 @@
   // Called from LayerTreeImpl.
   void OnCanDrawStateChangedForTree();
 
-  // Implementation
+  // Implementation.
   bool CanDraw() const;
   OutputSurface* output_surface() const { return output_surface_.get(); }
 
+  void SetOffscreenContextProvider(
+      const scoped_refptr<ContextProvider>& offscreen_context_provider);
+  ContextProvider* offscreen_context_provider() const {
+    return offscreen_context_provider_.get();
+  }
+
   std::string LayerTreeAsJson() const;
 
   void FinishAllRendering();
@@ -247,7 +255,7 @@
   const LayerTreeImpl* recycle_tree() const { return recycle_tree_.get(); }
   virtual void CreatePendingTree();
   void UpdateVisibleTiles();
-  virtual void ActivatePendingTreeIfNeeded();
+  virtual void ActivatePendingTree();
 
   // Shortcuts to layers on the active tree.
   LayerImpl* RootLayer() const;
@@ -391,7 +399,6 @@
       LayerTreeHostImplClient* client,
       Proxy* proxy,
       RenderingStatsInstrumentation* rendering_stats_instrumentation);
-  virtual void ActivatePendingTree();
 
   // Virtual for testing.
   virtual void AnimateLayers(base::TimeTicks monotonic_time,
@@ -409,9 +416,10 @@
   Proxy* proxy_;
 
  private:
-  void CreateAndSetRenderer(OutputSurface* output_surface,
-                            ResourceProvider* resource_provider,
-                            bool skip_gl_renderer);
+  void CreateAndSetRenderer(
+      OutputSurface* output_surface,
+      ResourceProvider* resource_provider,
+      bool skip_gl_renderer);
   void CreateAndSetTileManager(ResourceProvider* resource_provider,
                                bool using_map_image);
   void ReleaseTreeResources();
@@ -460,6 +468,7 @@
   UIResourceMap ui_resource_map_;
 
   scoped_ptr<OutputSurface> output_surface_;
+  scoped_refptr<ContextProvider> offscreen_context_provider_;
 
   // |resource_provider_| and |tile_manager_| can be NULL, e.g. when using tile-
   // free rendering - see OutputSurface::ForcedDrawToSoftwareDevice().
@@ -471,7 +480,7 @@
   scoped_ptr<LayerTreeImpl> active_tree_;
 
   // In impl-side painting mode, tree with possibly incomplete rasterized
-  // content. May be promoted to active by ActivatePendingTreeIfNeeded().
+  // content. May be promoted to active by ActivatePendingTree().
   scoped_ptr<LayerTreeImpl> pending_tree_;
 
   // In impl-side painting mode, inert tree with layers that can be recycled
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 98f4866..686efc3 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/containers/hash_tables.h"
+#include "base/containers/scoped_ptr_hash_map.h"
 #include "cc/base/math_util.h"
 #include "cc/debug/test_web_graphics_context_3d.h"
 #include "cc/input/top_controls_manager.h"
@@ -66,12 +67,12 @@
       : proxy_(),
         always_impl_thread_(&proxy_),
         always_main_thread_blocked_(&proxy_),
-        did_try_initialize_renderer_(false),
         on_can_draw_state_changed_called_(false),
-        has_pending_tree_(false),
+        did_notify_ready_to_activate_(false),
         did_request_commit_(false),
         did_request_redraw_(false),
         did_upload_visible_tile_(false),
+        did_lose_output_surface_(false),
         reduce_memory_result_(true),
         current_limit_bytes_(0),
         current_priority_cutoff_value_(0) {
@@ -94,20 +95,18 @@
 
   virtual void TearDown() OVERRIDE {}
 
-  virtual void DidTryInitializeRendererOnImplThread(
-      bool success,
-      scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE {
-    did_try_initialize_renderer_ = true;
+  virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE {
+    did_lose_output_surface_ = true;
   }
-  virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE {}
   virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE {}
   virtual void BeginFrameOnImplThread(const BeginFrameArgs& args)
       OVERRIDE {}
   virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE {
     on_can_draw_state_changed_called_ = true;
   }
-  virtual void OnHasPendingTreeStateChanged(bool has_pending_tree) OVERRIDE {
-    has_pending_tree_ = has_pending_tree;
+  virtual void NotifyReadyToActivate() OVERRIDE {
+    did_notify_ready_to_activate_ = true;
+    host_impl_->ActivatePendingTree();
   }
   virtual void SetNeedsRedrawOnImplThread() OVERRIDE {
     did_request_redraw_ = true;
@@ -351,12 +350,12 @@
 
   scoped_ptr<LayerTreeHostImpl> host_impl_;
   FakeRenderingStatsInstrumentation stats_instrumentation_;
-  bool did_try_initialize_renderer_;
   bool on_can_draw_state_changed_called_;
-  bool has_pending_tree_;
+  bool did_notify_ready_to_activate_;
   bool did_request_commit_;
   bool did_request_redraw_;
   bool did_upload_visible_tile_;
+  bool did_lose_output_surface_;
   bool reduce_memory_result_;
   base::TimeDelta requested_scrollbar_animation_delay_;
   size_t current_limit_bytes_;
@@ -1090,7 +1089,7 @@
 
 TEST_F(LayerTreeHostImplTest, ScrollbarLinearFadeScheduling) {
   LayerTreeSettings settings;
-  settings.use_linear_fade_scrollbar_animator = true;
+  settings.scrollbar_animator = LayerTreeSettings::LinearFade;
   settings.scrollbar_linear_fade_delay_ms = 20;
   settings.scrollbar_linear_fade_length_ms = 20;
 
@@ -3022,7 +3021,7 @@
   host_impl_->SetViewportSize(larger_viewport);
   EXPECT_TRUE(host_impl_->active_tree()->ViewportSizeInvalid());
   did_activate_pending_tree_ = false;
-  host_impl_->ActivatePendingTreeIfNeeded();
+  host_impl_->ActivatePendingTree();
   EXPECT_TRUE(did_activate_pending_tree_);
   EXPECT_FALSE(host_impl_->active_tree()->ViewportSizeInvalid());
 
@@ -3133,7 +3132,11 @@
 
 class SwapTrackerContext : public TestWebGraphicsContext3D {
  public:
-  SwapTrackerContext() : last_update_type_(NoUpdate) {}
+  SwapTrackerContext()
+      : last_update_type_(NoUpdate) {
+    test_capabilities_.post_sub_buffer = true;
+    test_capabilities_.set_visibility = true;
+  }
 
   virtual void prepareTexture() OVERRIDE {
     update_rect_ = gfx::Rect(width_, height_);
@@ -3146,15 +3149,6 @@
     last_update_type_ = PostSubBuffer;
   }
 
-  virtual WebKit::WebString getString(WebKit::WGC3Denum name) OVERRIDE {
-    if (name == GL_EXTENSIONS) {
-      return WebKit::WebString(
-          "GL_CHROMIUM_post_sub_buffer GL_CHROMIUM_set_visibility");
-    }
-
-    return WebKit::WebString();
-  }
-
   gfx::Rect update_rect() const { return update_rect_; }
 
   enum UpdateType {
@@ -3329,7 +3323,6 @@
                                   WebKit::WGC3Dsizei count,
                                   WebKit::WGC3Denum type,
                                   WebKit::WGC3Dintptr offset));
-  MOCK_METHOD1(getString, WebKit::WebString(WebKit::WGC3Denum name));
   MOCK_METHOD0(getRequestableExtensionsCHROMIUM, WebKit::WebString());
   MOCK_METHOD1(enable, void(WebKit::WGC3Denum cap));
   MOCK_METHOD1(disable, void(WebKit::WGC3Denum cap));
@@ -3346,6 +3339,8 @@
  public:
   explicit MockContextHarness(MockContext* context)
       : context_(context) {
+    context_->set_have_post_sub_buffer(true);
+
     // Catch "uninteresting" calls
     EXPECT_CALL(*context_, useProgram(_))
         .Times(0);
@@ -3360,19 +3355,6 @@
     EXPECT_CALL(*context_, uniform4f(_, _, _, _, _))
         .WillRepeatedly(Return());
 
-    // Any other strings are empty
-    EXPECT_CALL(*context_, getString(_))
-        .WillRepeatedly(Return(WebKit::WebString()));
-
-    // Support for partial swap, if needed
-    EXPECT_CALL(*context_, getString(GL_EXTENSIONS))
-        .WillRepeatedly(Return(
-            WebKit::WebString("GL_CHROMIUM_post_sub_buffer")));
-
-    EXPECT_CALL(*context_, getRequestableExtensionsCHROMIUM())
-        .WillRepeatedly(Return(
-            WebKit::WebString("GL_CHROMIUM_post_sub_buffer")));
-
     // Any un-sanctioned calls to enable() are OK
     EXPECT_CALL(*context_, enable(_))
         .WillRepeatedly(Return());
@@ -3489,14 +3471,8 @@
 
 class PartialSwapContext : public TestWebGraphicsContext3D {
  public:
-  virtual WebKit::WebString getString(WebKit::WGC3Denum name) OVERRIDE {
-    if (name == GL_EXTENSIONS)
-      return WebKit::WebString("GL_CHROMIUM_post_sub_buffer");
-    return WebKit::WebString();
-  }
-
-  virtual WebKit::WebString getRequestableExtensionsCHROMIUM() OVERRIDE {
-    return WebKit::WebString("GL_CHROMIUM_post_sub_buffer");
+  PartialSwapContext() {
+    test_capabilities_.post_sub_buffer = true;
   }
 
   // Unlimited texture size.
@@ -3632,7 +3608,10 @@
  public:
   TrackingWebGraphicsContext3D()
       : TestWebGraphicsContext3D(),
-        num_textures_(0) {}
+        num_textures_(0) {
+    test_capabilities_.iosurface = true;
+    test_capabilities_.texture_rectangle = true;
+  }
 
   virtual WebKit::WebGLId createTexture() OVERRIDE {
     WebKit::WebGLId id = TestWebGraphicsContext3D::createTexture();
@@ -3650,15 +3629,6 @@
     --num_textures_;
   }
 
-  virtual WebKit::WebString getString(WebKit::WGC3Denum name) OVERRIDE {
-    if (name == GL_EXTENSIONS) {
-      return WebKit::WebString(
-          "GL_CHROMIUM_iosurface GL_ARB_texture_rectangle");
-    }
-
-    return WebKit::WebString();
-  }
-
   unsigned num_textures() const { return num_textures_; }
 
  private:
@@ -4915,7 +4885,7 @@
 }
 
 struct RenderPassRemovalTestData : public LayerTreeHostImpl::FrameData {
-  ScopedPtrHashMap<RenderPass::Id, TestRenderPass> render_pass_cache;
+  base::ScopedPtrHashMap<RenderPass::Id, TestRenderPass> render_pass_cache;
   scoped_ptr<SharedQuadState> shared_quad_state;
 };
 
@@ -6181,54 +6151,152 @@
   EXPECT_EQ(host_impl_->active_tree()->root_layer(), frame.will_draw_layers[0]);
 }
 
-TEST_F(LayerTreeHostImplTest, DeferredInitializeSmoke) {
-  set_reduce_memory_result(false);
-  scoped_ptr<FakeOutputSurface> output_surface(
-      FakeOutputSurface::CreateDeferredGL(
-          scoped_ptr<SoftwareOutputDevice>(new CountingSoftwareDevice())));
-  FakeOutputSurface* output_surface_ptr = output_surface.get();
-  EXPECT_TRUE(
-      host_impl_->InitializeRenderer(output_surface.PassAs<OutputSurface>()));
+class LayerTreeHostImplTestDeferredInitialize : public LayerTreeHostImplTest {
+ protected:
+  virtual void SetUp() OVERRIDE {
+    LayerTreeHostImplTest::SetUp();
 
-  // Add two layers.
-  scoped_ptr<SolidColorLayerImpl> root_layer =
-      SolidColorLayerImpl::Create(host_impl_->active_tree(), 1);
-  FakeVideoFrameProvider provider;
-  scoped_ptr<VideoLayerImpl> video_layer =
-      VideoLayerImpl::Create(host_impl_->active_tree(), 2, &provider);
-  video_layer->SetBounds(gfx::Size(10, 10));
-  video_layer->SetContentBounds(gfx::Size(10, 10));
-  video_layer->SetDrawsContent(true);
-  root_layer->AddChild(video_layer.PassAs<LayerImpl>());
-  SetupRootLayerImpl(root_layer.PassAs<LayerImpl>());
+    set_reduce_memory_result(false);
 
+    scoped_ptr<FakeOutputSurface> output_surface(
+        FakeOutputSurface::CreateDeferredGL(
+            scoped_ptr<SoftwareOutputDevice>(new CountingSoftwareDevice())));
+    output_surface_ = output_surface.get();
+
+    EXPECT_TRUE(host_impl_->InitializeRenderer(
+        output_surface.PassAs<OutputSurface>()));
+
+    scoped_ptr<SolidColorLayerImpl> root_layer =
+        SolidColorLayerImpl::Create(host_impl_->active_tree(), 1);
+    SetupRootLayerImpl(root_layer.PassAs<LayerImpl>());
+
+    onscreen_context_provider_ = TestContextProvider::Create();
+    offscreen_context_provider_ = TestContextProvider::Create();
+  }
+
+  FakeOutputSurface* output_surface_;
+  scoped_refptr<TestContextProvider> onscreen_context_provider_;
+  scoped_refptr<TestContextProvider> offscreen_context_provider_;
+};
+
+
+TEST_F(LayerTreeHostImplTestDeferredInitialize, Success) {
   // Software draw.
   DrawFrame();
 
+  EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+  EXPECT_FALSE(host_impl_->offscreen_context_provider());
+
   // DeferredInitialize and hardware draw.
-  EXPECT_FALSE(did_try_initialize_renderer_);
-  EXPECT_TRUE(output_surface_ptr->InitializeAndSetContext3d(
-      TestContextProvider::Create(), NULL));
-  EXPECT_TRUE(did_try_initialize_renderer_);
+  EXPECT_TRUE(output_surface_->InitializeAndSetContext3d(
+      onscreen_context_provider_, offscreen_context_provider_));
+  EXPECT_EQ(onscreen_context_provider_,
+            host_impl_->output_surface()->context_provider());
+  EXPECT_EQ(offscreen_context_provider_,
+            host_impl_->offscreen_context_provider());
 
   // Defer intialized GL draw.
   DrawFrame();
 
   // Revert back to software.
-  did_try_initialize_renderer_ = false;
-  output_surface_ptr->ReleaseGL();
-  EXPECT_TRUE(did_try_initialize_renderer_);
+  output_surface_->ReleaseGL();
+  EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+  EXPECT_FALSE(host_impl_->offscreen_context_provider());
+
+  // Software draw again.
   DrawFrame();
 }
 
-class ContextThatDoesNotSupportMemoryManagmentExtensions
-    : public TestWebGraphicsContext3D {
- public:
-  // WebGraphicsContext3D methods.
-  virtual WebKit::WebString getString(WebKit::WGC3Denum name) {
-    return WebKit::WebString();
-  }
-};
+TEST_F(LayerTreeHostImplTestDeferredInitialize, Fails_OnscreenContext_0) {
+  // Software draw.
+  DrawFrame();
+
+  // Fail initialization of the onscreen context before the OutputSurface binds
+  // it to the thread.
+  onscreen_context_provider_->UnboundTestContext3d()
+      ->set_times_make_current_succeeds(0);
+
+  EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+  EXPECT_FALSE(host_impl_->offscreen_context_provider());
+  EXPECT_FALSE(did_lose_output_surface_);
+
+  // DeferredInitialize fails.
+  EXPECT_FALSE(output_surface_->InitializeAndSetContext3d(
+      onscreen_context_provider_, offscreen_context_provider_));
+  EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+  EXPECT_FALSE(host_impl_->offscreen_context_provider());
+
+  // Software draw again.
+  DrawFrame();
+}
+
+TEST_F(LayerTreeHostImplTestDeferredInitialize, Fails_OnscreenContext_1) {
+  // Software draw.
+  DrawFrame();
+
+  // Fail initialization of the onscreen context after the OutputSurface binds
+  // it to the thread.
+  onscreen_context_provider_->UnboundTestContext3d()
+      ->set_times_make_current_succeeds(2);
+
+  EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+  EXPECT_FALSE(host_impl_->offscreen_context_provider());
+  EXPECT_FALSE(did_lose_output_surface_);
+
+  // DeferredInitialize fails.
+  EXPECT_FALSE(output_surface_->InitializeAndSetContext3d(
+      onscreen_context_provider_, offscreen_context_provider_));
+  EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+  EXPECT_FALSE(host_impl_->offscreen_context_provider());
+
+  // We lose the output surface.
+  EXPECT_TRUE(did_lose_output_surface_);
+}
+
+TEST_F(LayerTreeHostImplTestDeferredInitialize, Fails_OnscreenContext_2) {
+  // Software draw.
+  DrawFrame();
+
+  // Fail initialization of the onscreen context after the OutputSurface binds
+  // it to the thread and during renderer initialization.
+  onscreen_context_provider_->UnboundTestContext3d()
+      ->set_times_make_current_succeeds(1);
+
+  EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+  EXPECT_FALSE(host_impl_->offscreen_context_provider());
+  EXPECT_FALSE(did_lose_output_surface_);
+
+  // DeferredInitialize fails.
+  EXPECT_FALSE(output_surface_->InitializeAndSetContext3d(
+      onscreen_context_provider_, offscreen_context_provider_));
+  EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+  EXPECT_FALSE(host_impl_->offscreen_context_provider());
+
+  // We lose the output surface.
+  EXPECT_TRUE(did_lose_output_surface_);
+}
+
+TEST_F(LayerTreeHostImplTestDeferredInitialize, Fails_OffscreenContext) {
+  // Software draw.
+  DrawFrame();
+
+  // Fail initialization of the offscreen context.
+  offscreen_context_provider_->UnboundTestContext3d()
+      ->set_times_make_current_succeeds(0);
+
+  EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+  EXPECT_FALSE(host_impl_->offscreen_context_provider());
+  EXPECT_FALSE(did_lose_output_surface_);
+
+  // DeferredInitialize fails.
+  EXPECT_FALSE(output_surface_->InitializeAndSetContext3d(
+      onscreen_context_provider_, offscreen_context_provider_));
+  EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+  EXPECT_FALSE(host_impl_->offscreen_context_provider());
+
+  // We lose the output surface.
+  EXPECT_TRUE(did_lose_output_surface_);
+}
 
 // Checks that we have a non-0 default allocation if we pass a context that
 // doesn't support memory management extensions.
@@ -6240,8 +6308,7 @@
                                          &stats_instrumentation_);
 
   scoped_ptr<OutputSurface> output_surface(
-      FakeOutputSurface::Create3d(scoped_ptr<TestWebGraphicsContext3D>(
-          new ContextThatDoesNotSupportMemoryManagmentExtensions)));
+      FakeOutputSurface::Create3d(TestWebGraphicsContext3D::Create()));
   host_impl_->InitializeRenderer(output_surface.Pass());
   EXPECT_LT(0ul, host_impl_->memory_allocation_limit_bytes());
 }
diff --git a/cc/trees/layer_tree_host_perftest.cc b/cc/trees/layer_tree_host_perftest.cc
index fc7673c..2d934e8 100644
--- a/cc/trees/layer_tree_host_perftest.cc
+++ b/cc/trees/layer_tree_host_perftest.cc
@@ -8,10 +8,12 @@
 #include "base/files/file_path.h"
 #include "base/path_service.h"
 #include "base/strings/string_piece.h"
+#include "base/time/time.h"
 #include "cc/layers/content_layer.h"
 #include "cc/layers/nine_patch_layer.h"
 #include "cc/layers/solid_color_layer.h"
 #include "cc/test/fake_content_layer_client.h"
+#include "cc/test/lap_timer.h"
 #include "cc/test/layer_tree_json_parser.h"
 #include "cc/test/layer_tree_test.h"
 #include "cc/test/paths.h"
@@ -27,8 +29,10 @@
 class LayerTreeHostPerfTest : public LayerTreeTest {
  public:
   LayerTreeHostPerfTest()
-      : num_draws_(0),
-        num_commits_(0),
+      : draw_timer_(kWarmupRuns,
+                    base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
+                    kTimeCheckInterval),
+        commit_timer_(0, base::TimeDelta(), 1),
         full_damage_each_frame_(false),
         animation_driven_drawing_(false),
         measure_commit_cost_(false) {
@@ -47,28 +51,23 @@
 
   virtual void BeginCommitOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
     if (measure_commit_cost_)
-      commit_start_time_ = base::TimeTicks::HighResNow();
+      commit_timer_.Start();
   }
 
   virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
-    if (measure_commit_cost_ && num_draws_ >= kWarmupRuns) {
-      total_commit_time_ += base::TimeTicks::HighResNow() - commit_start_time_;
-      ++num_commits_;
+    if (measure_commit_cost_ && draw_timer_.IsWarmedUp()) {
+      commit_timer_.NextLap();
     }
   }
 
   virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE {
-    ++num_draws_;
-    if (num_draws_ == kWarmupRuns)
-      start_time_ = base::TimeTicks::HighResNow();
-
-    if (!start_time_.is_null() && (num_draws_ % kTimeCheckInterval) == 0) {
-      base::TimeDelta elapsed = base::TimeTicks::HighResNow() - start_time_;
-      if (elapsed >= base::TimeDelta::FromMilliseconds(kTimeLimitMillis)) {
-        elapsed_ = elapsed;
-        EndTest();
-        return;
-      }
+    if (TestEnded()) {
+      return;
+    }
+    draw_timer_.NextLap();
+    if (draw_timer_.HasTimeLimitExpired()) {
+      EndTest();
+      return;
     }
     if (!animation_driven_drawing_)
       impl->SetNeedsRedraw();
@@ -79,34 +78,29 @@
   virtual void BuildTree() {}
 
   virtual void AfterTest() OVERRIDE {
-    num_draws_ -= kWarmupRuns;
-
     // Format matches chrome/test/perf/perf_test.h:PrintResult
     printf("*RESULT %s: frames: %d, %.2f ms/frame\n",
            test_name_.c_str(),
-           num_draws_,
-           elapsed_.InMillisecondsF() / num_draws_);
+           draw_timer_.NumLaps(),
+           draw_timer_.MsPerLap());
     if (measure_commit_cost_) {
       printf("*RESULT %s: commits: %d, %.2f ms/commit\n",
              test_name_.c_str(),
-             num_commits_,
-             total_commit_time_.InMillisecondsF() / num_commits_);
+             commit_timer_.NumLaps(),
+             commit_timer_.MsPerLap());
     }
   }
 
  protected:
-  base::TimeTicks start_time_;
-  int num_draws_;
-  int num_commits_;
+  LapTimer draw_timer_;
+  LapTimer commit_timer_;
+
   std::string test_name_;
-  base::TimeDelta elapsed_;
   FakeContentLayerClient fake_content_layer_client_;
   bool full_damage_each_frame_;
   bool animation_driven_drawing_;
 
   bool measure_commit_cost_;
-  base::TimeTicks commit_start_time_;
-  base::TimeDelta total_commit_time_;
 };
 
 
diff --git a/cc/trees/layer_tree_host_pixeltest_filters.cc b/cc/trees/layer_tree_host_pixeltest_filters.cc
index 553e66e..7068b4b 100644
--- a/cc/trees/layer_tree_host_pixeltest_filters.cc
+++ b/cc/trees/layer_tree_host_pixeltest_filters.cc
@@ -6,6 +6,8 @@
 #include "cc/layers/solid_color_layer.h"
 #include "cc/test/layer_tree_pixel_test.h"
 #include "cc/test/pixel_comparator.h"
+#include "third_party/skia/include/effects/SkColorFilterImageFilter.h"
+#include "third_party/skia/include/effects/SkColorMatrixFilter.h"
 
 #if !defined(OS_ANDROID)
 
@@ -52,7 +54,7 @@
                base::FilePath(FILE_PATH_LITERAL("background_filter_blur.png")));
 }
 
-TEST_F(LayerTreeHostFiltersPixelTest, DISABLED_BackgroundFilterBlurOutsets) {
+TEST_F(LayerTreeHostFiltersPixelTest, BackgroundFilterBlurOutsets) {
   scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
       gfx::Rect(200, 200), SK_ColorWHITE);
 
@@ -73,8 +75,8 @@
   blur->SetBackgroundFilters(filters);
 
 #if defined(OS_WIN)
-  // Windows has 2250 pixels off by at most 2: crbug.com/259922
-  float percentage_pixels_large_error = 5.625f;  // 2250px / (200*200)
+  // Windows has 2596 pixels off by at most 2: crbug.com/259922
+  float percentage_pixels_large_error = 6.5f;  // 2596px / (200*200), rounded up
   float percentage_pixels_small_error = 0.0f;
   float average_error_allowed_in_bad_pixels = 1.f;
   int large_error_allowed = 2;
@@ -148,6 +150,51 @@
                    "background_filter_blur_off_axis.png")));
 }
 
+TEST_F(LayerTreeHostFiltersPixelTest, ImageFilterClipped) {
+  scoped_refptr<SolidColorLayer> root = CreateSolidColorLayer(
+      gfx::Rect(200, 200), SK_ColorBLACK);
+
+  scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
+      gfx::Rect(200, 200), SK_ColorYELLOW);
+  root->AddChild(background);
+
+  scoped_refptr<SolidColorLayer> foreground = CreateSolidColorLayer(
+      gfx::Rect(200, 200), SK_ColorRED);
+  background->AddChild(foreground);
+
+  SkScalar matrix[20];
+  memset(matrix, 0, 20 * sizeof(matrix[0]));
+  // This filter does a red-blue swap, so the foreground becomes blue.
+  matrix[2] = matrix[6] = matrix[10] = matrix[18] = SK_Scalar1;
+  skia::RefPtr<SkColorFilter> colorFilter(skia::AdoptRef(
+      new SkColorMatrixFilter(matrix)));
+  // We filter only the bottom 200x100 pixels of the foreground.
+  SkIRect cropRect = SkIRect::MakeXYWH(0, 100, 200, 100);
+  skia::RefPtr<SkImageFilter> filter =
+      skia::AdoptRef(SkColorFilterImageFilter::Create(
+          colorFilter.get(),
+          NULL,
+          &cropRect));
+
+  // Make the foreground layer's render surface be clipped by the background
+  // layer.
+  background->SetMasksToBounds(true);
+  foreground->SetFilter(filter);
+
+  // Then we translate the foreground up by 100 pixels in Y, so the cropped
+  // region is moved to to the top. This ensures that the crop rect is being
+  // correctly transformed in skia by the amount of clipping that the
+  // compositor performs.
+  gfx::Transform transform;
+  transform.Translate(0.0, -100.0);
+  foreground->SetTransform(transform);
+
+  RunPixelTest(GL_WITH_BITMAP,
+               background,
+               base::FilePath(FILE_PATH_LITERAL(
+                   "blue_yellow.png")));
+}
+
 }  // namespace
 }  // namespace cc
 
diff --git a/cc/trees/layer_tree_host_pixeltest_readback.cc b/cc/trees/layer_tree_host_pixeltest_readback.cc
index a9bc6ce..1c8380b 100644
--- a/cc/trees/layer_tree_host_pixeltest_readback.cc
+++ b/cc/trees/layer_tree_host_pixeltest_readback.cc
@@ -852,7 +852,7 @@
   bitmap.allocPixels();
   bitmap.eraseColor(SK_ColorGREEN);
   {
-    SkDevice device(bitmap);
+    SkBitmapDevice device(bitmap);
     skia::RefPtr<SkCanvas> canvas = skia::AdoptRef(new SkCanvas(&device));
     SkPaint paint;
     paint.setStyle(SkPaint::kFill_Style);
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 73033a1..0102a10 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -701,7 +701,8 @@
 class LayerTreeHostTestFrameTimeUpdatesAfterActivationFails
     : public LayerTreeHostTest {
  public:
-  LayerTreeHostTestFrameTimeUpdatesAfterActivationFails() : frame_(0) {}
+  LayerTreeHostTestFrameTimeUpdatesAfterActivationFails()
+      : frame_count_with_pending_tree_(0) {}
 
   virtual void BeginTest() OVERRIDE {
     layer_tree_host()->SetViewportSize(gfx::Size(20, 20));
@@ -710,9 +711,18 @@
     PostSetNeedsCommitToMainThread();
   }
 
+  virtual void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl,
+                                          const BeginFrameArgs& args) OVERRIDE {
+    if (host_impl->pending_tree())
+      frame_count_with_pending_tree_++;
+    host_impl->BlockNotifyReadyToActivateForTesting(
+        frame_count_with_pending_tree_ <= 1);
+  }
+
   virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE {
-    if (frame_ >= 1) {
-      EXPECT_NE(first_frame_time_, impl->CurrentFrameTimeTicks());
+    if (frame_count_with_pending_tree_ > 1) {
+      EXPECT_NE(first_frame_time_.ToInternalValue(),
+                impl->CurrentFrameTimeTicks().ToInternalValue());
       EndTest();
       return;
     }
@@ -720,35 +730,15 @@
     EXPECT_FALSE(impl->settings().impl_side_painting);
     EndTest();
   }
-
-  virtual bool CanActivatePendingTree(LayerTreeHostImpl* impl) OVERRIDE {
-    if (frame_ >= 1)
-      return true;
-
-    return false;
-  }
-
-  virtual bool CanActivatePendingTreeIfNeeded(LayerTreeHostImpl* impl)
-      OVERRIDE {
-    frame_++;
-    if (frame_ == 1) {
-      first_frame_time_ = impl->CurrentFrameTimeTicks();
-
-      // Since base::TimeTicks::Now() uses a low-resolution clock on
-      // Windows, we need to make sure that the clock has incremented past
-      // first_frame_time_.
-      while (first_frame_time_ == base::TimeTicks::Now()) {}
-
-      return false;
-    }
-
-    return true;
+  virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+    if (impl->settings().impl_side_painting)
+      EXPECT_NE(frame_count_with_pending_tree_, 1);
   }
 
   virtual void AfterTest() OVERRIDE {}
 
  private:
-  int frame_;
+  int frame_count_with_pending_tree_;
   base::TimeTicks first_frame_time_;
 };
 
@@ -806,9 +796,10 @@
 
 // Verifies that StartPageScaleAnimation events propagate correctly
 // from LayerTreeHost to LayerTreeHostImpl in the MT compositor.
-class LayerTreeHostTestStartPageScaleAnimation : public LayerTreeHostTest {
+class DISABLED_LayerTreeHostTestStartPageScaleAnimation
+    : public LayerTreeHostTest {
  public:
-  LayerTreeHostTestStartPageScaleAnimation() {}
+  DISABLED_LayerTreeHostTestStartPageScaleAnimation() {}
 
   virtual void SetupTree() OVERRIDE {
     LayerTreeHostTest::SetupTree();
@@ -868,7 +859,8 @@
   scoped_refptr<FakeContentLayer> scroll_layer_;
 };
 
-MULTI_THREAD_TEST_F(LayerTreeHostTestStartPageScaleAnimation);
+// Disabled. See: crbug.com/280508
+MULTI_THREAD_TEST_F(DISABLED_LayerTreeHostTestStartPageScaleAnimation);
 
 class LayerTreeHostTestSetVisible : public LayerTreeHostTest {
  public:
@@ -1117,7 +1109,7 @@
     // Make sure partial texture updates are turned off.
     settings->max_partial_texture_updates = 0;
     // Linear fade animator prevents scrollbars from drawing immediately.
-    settings->use_linear_fade_scrollbar_animator = false;
+    settings->scrollbar_animator = LayerTreeSettings::NoAnimator;
   }
 
   virtual void SetupTree() OVERRIDE {
@@ -2372,6 +2364,11 @@
 
 class MockIOSurfaceWebGraphicsContext3D : public TestWebGraphicsContext3D {
  public:
+  MockIOSurfaceWebGraphicsContext3D() {
+    test_capabilities_.iosurface = true;
+    test_capabilities_.texture_rectangle = true;
+  }
+
   virtual WebKit::WebGLId createTexture() OVERRIDE {
     return 1;
   }
@@ -2403,8 +2400,6 @@
         new MockIOSurfaceWebGraphicsContext3D);
     mock_context_ = mock_context_owned.get();
 
-    mock_context_->set_have_extension_io_surface(true);
-
     scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
         mock_context_owned.PassAs<TestWebGraphicsContext3D>()));
     return output_surface.Pass();
diff --git a/cc/trees/layer_tree_host_unittest_animation.cc b/cc/trees/layer_tree_host_unittest_animation.cc
index dbc4275..70e2a9e 100644
--- a/cc/trees/layer_tree_host_unittest_animation.cc
+++ b/cc/trees/layer_tree_host_unittest_animation.cc
@@ -293,6 +293,136 @@
 SINGLE_AND_MULTI_THREAD_TEST_F(
     LayerTreeHostAnimationTestTickAnimationWhileBackgrounded);
 
+// Ensures that animations do not tick when we are backgrounded and
+// and we have an empty active tree.
+class LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree
+    : public LayerTreeHostAnimationTest {
+ protected:
+  LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree()
+      : active_tree_was_animated_(false) {}
+
+  virtual base::TimeDelta LowFrequencyAnimationInterval() const OVERRIDE {
+    return base::TimeDelta::FromMilliseconds(4);
+  }
+
+  virtual void BeginTest() OVERRIDE {
+    PostAddAnimationToMainThread(layer_tree_host()->root_layer());
+  }
+
+  virtual void NotifyAnimationFinished(double time) OVERRIDE {
+    // Replace animated commits with an empty tree.
+    layer_tree_host()->SetRootLayer(make_scoped_refptr<Layer>(NULL));
+  }
+
+  virtual void DidCommit() OVERRIDE {
+    // This alternates setting an empty tree and a non-empty tree with an
+    // animation.
+    switch (layer_tree_host()->source_frame_number()) {
+      case 1:
+        // Wait for NotifyAnimationFinished to commit an empty tree.
+        break;
+      case 2:
+        SetupTree();
+        AddOpacityTransitionToLayer(
+            layer_tree_host()->root_layer(), 0.000001, 0, 0.5, true);
+        break;
+      case 3:
+        // Wait for NotifyAnimationFinished to commit an empty tree.
+        break;
+      case 4:
+        EndTest();
+        break;
+    }
+  }
+
+  virtual void BeginCommitOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+    // At the start of every commit, block activations and make sure
+    // we are backgrounded.
+    host_impl->BlockNotifyReadyToActivateForTesting(true);
+    PostSetVisibleToMainThread(false);
+  }
+
+  virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+    if (!host_impl->settings().impl_side_painting) {
+      // There are no activations to block if we're not impl-side-painting,
+      // so just advance the test immediately.
+      if (host_impl->active_tree()->source_frame_number() < 3)
+        UnblockActivations(host_impl);
+      return;
+    }
+
+    // We block activation for several ticks to make sure that, even though
+    // there is a pending tree with animations, we still do not background
+    // tick if the active tree is empty.
+    if (host_impl->pending_tree()->source_frame_number() < 3) {
+      base::MessageLoopProxy::current()->PostDelayedTask(
+          FROM_HERE,
+          base::Bind(
+              &LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree::
+                   UnblockActivations,
+              base::Unretained(this),
+              host_impl),
+          4 * LowFrequencyAnimationInterval());
+    }
+  }
+
+  virtual void UnblockActivations(LayerTreeHostImpl* host_impl) {
+    host_impl->BlockNotifyReadyToActivateForTesting(false);
+  }
+
+  virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+    active_tree_was_animated_ = false;
+
+    // Verify that commits are actually alternating with empty / non-empty
+    // trees.
+    switch (host_impl->active_tree()->source_frame_number()) {
+      case 0:
+      case 2:
+        EXPECT_TRUE(host_impl->active_tree()->root_layer());
+        break;
+      case 1:
+      case 3:
+        EXPECT_FALSE(host_impl->active_tree()->root_layer());
+        break;
+    }
+
+    if (host_impl->active_tree()->source_frame_number() < 3) {
+      // Initiate the next commit after a delay to give us a chance to
+      // background tick if the active tree isn't empty.
+      base::MessageLoopProxy::current()->PostDelayedTask(
+          FROM_HERE,
+          base::Bind(
+              &LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree::
+                   InitiateNextCommit,
+              base::Unretained(this),
+              host_impl),
+          4 * LowFrequencyAnimationInterval());
+    }
+  }
+
+  virtual void WillAnimateLayers(LayerTreeHostImpl* host_impl,
+                                 base::TimeTicks monotonic_time) OVERRIDE {
+    EXPECT_TRUE(host_impl->active_tree()->root_layer());
+    active_tree_was_animated_ = true;
+  }
+
+  void InitiateNextCommit(LayerTreeHostImpl* host_impl) {
+    // Verify that we actually animated when we should have.
+    bool has_active_tree = host_impl->active_tree()->root_layer();
+    EXPECT_EQ(has_active_tree, active_tree_was_animated_);
+
+    // The next commit is blocked until we become visible again.
+    PostSetVisibleToMainThread(true);
+  }
+
+  virtual void AfterTest() OVERRIDE {}
+
+  bool active_tree_was_animated_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+    LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree);
+
 // Ensure that an animation's timing function is respected.
 class LayerTreeHostAnimationTestAddAnimationWithTimingFunction
     : public LayerTreeHostAnimationTest {
diff --git a/cc/trees/layer_tree_host_unittest_context.cc b/cc/trees/layer_tree_host_unittest_context.cc
index d9ae64c..16196ee 100644
--- a/cc/trees/layer_tree_host_unittest_context.cc
+++ b/cc/trees/layer_tree_host_unittest_context.cc
@@ -476,9 +476,9 @@
     // the active context.
     EXPECT_TRUE(content_impl->HaveResourceForTileAt(0, 0));
 
-    cc::ContextProvider* contexts =
-        host_impl->resource_provider()->offscreen_context_provider();
+    cc::ContextProvider* contexts = host_impl->offscreen_context_provider();
     if (use_surface_) {
+      ASSERT_TRUE(contexts);
       EXPECT_TRUE(contexts->Context3d());
       // TODO(danakj): Make a fake GrContext.
       // EXPECT_TRUE(contexts->GrContext());
@@ -597,8 +597,7 @@
   }
 
   virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
-    cc::ContextProvider* contexts =
-        host_impl->resource_provider()->offscreen_context_provider();
+    cc::ContextProvider* contexts = host_impl->offscreen_context_provider();
     EXPECT_FALSE(contexts);
 
     // This did not lead to create failure.
diff --git a/cc/trees/layer_tree_host_unittest_damage.cc b/cc/trees/layer_tree_host_unittest_damage.cc
index a3a8af3..e53dc2e 100644
--- a/cc/trees/layer_tree_host_unittest_damage.cc
+++ b/cc/trees/layer_tree_host_unittest_damage.cc
@@ -286,13 +286,7 @@
 
 SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDamageTestForcedFullDamage);
 
-class LayerTreeHostDamageTestScrollbarDoesDamage
-    : public LayerTreeHostDamageTest {
-  virtual void BeginTest() OVERRIDE {
-    did_swaps_ = 0;
-    PostSetNeedsCommitToMainThread();
-  }
-
+class LayerTreeHostScrollbarDamageTest : public LayerTreeHostDamageTest {
   virtual void SetupTree() OVERRIDE {
     scoped_refptr<Layer> root_layer = Layer::Create();
     root_layer->SetBounds(gfx::Size(400, 400));
@@ -320,6 +314,17 @@
     LayerTreeHostDamageTest::SetupTree();
   }
 
+ private:
+  FakeContentLayerClient client_;
+};
+
+class LayerTreeHostDamageTestScrollbarDoesDamage
+    : public LayerTreeHostScrollbarDamageTest {
+  virtual void BeginTest() OVERRIDE {
+    did_swaps_ = 0;
+    PostSetNeedsCommitToMainThread();
+  }
+
   virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
                                      LayerTreeHostImpl::FrameData* frame_data,
                                      bool result) OVERRIDE {
@@ -359,17 +364,14 @@
     switch (did_swaps_) {
       case 1:
         // Test that modifying the position of the content layer (not
-        // scrolling) won't damage the scrollbar. Do this before the other
-        // tests, as they can cause commits that will later damage the
-        // scrollbar. http://crbug.com/276657
+        // scrolling) won't damage the scrollbar.
         scroll_layer->SetPosition(gfx::Point(1, 1));
         scroll_layer->SetScrollOffset(scroll_layer->scroll_offset());
         host_impl->SetNeedsRedraw();
         break;
       case 2:
-        host_impl->ScrollBegin(gfx::Point(1, 1), InputHandler::Wheel);
-        EXPECT_TRUE(host_impl->ScrollBy(gfx::Point(),
-                                        gfx::Vector2dF(10.0, 10.0)));
+        scroll_layer->ScrollBy(gfx::Vector2dF(10.f, 10.f));
+        host_impl->SetNeedsRedraw();
         break;
       case 3:
         scroll_layer->SetMaxScrollOffset(gfx::Vector2d(60, 100));
@@ -379,15 +381,88 @@
   }
 
   virtual void AfterTest() OVERRIDE {
-    // Extra commits may happen and cause swaps: http://crbug.com/276657
-    EXPECT_LE(4, did_swaps_);
+    EXPECT_EQ(4, did_swaps_);
   }
 
-  FakeContentLayerClient client_;
   int did_swaps_;
 };
 
 MULTI_THREAD_TEST_F(LayerTreeHostDamageTestScrollbarDoesDamage);
 
+class LayerTreeHostDamageTestScrollbarCommitDoesNoDamage
+    : public LayerTreeHostScrollbarDamageTest {
+  virtual void BeginTest() OVERRIDE {
+    did_swaps_ = 0;
+    PostSetNeedsCommitToMainThread();
+  }
+
+  virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
+                                     LayerTreeHostImpl::FrameData* frame_data,
+                                     bool result) OVERRIDE {
+    EXPECT_TRUE(result);
+    RenderSurfaceImpl* root_surface =
+        host_impl->active_tree()->root_layer()->render_surface();
+    gfx::RectF root_damage =
+        root_surface->damage_tracker()->current_damage_rect();
+    root_damage.Intersect(root_surface->content_rect());
+    int frame = host_impl->active_tree()->source_frame_number();
+    switch (did_swaps_) {
+      case 0:
+        // The first frame has damage, so we should draw and swap.
+        EXPECT_EQ(0, frame);
+        break;
+      case 1:
+        // The second frame has scrolled, so the scrollbar should be damaged.
+        EXPECT_EQ(0, frame);
+        EXPECT_TRUE(root_damage.Contains(gfx::Rect(300, 300, 10, 100)));
+        break;
+      case 2:
+        // The third frame (after the commit) has no changes, so it shouldn't.
+        EXPECT_EQ(1, frame);
+        EXPECT_FALSE(root_damage.Intersects(gfx::Rect(300, 300, 10, 100)));
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+    return result;
+  }
+
+  virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl,
+                                   bool result) OVERRIDE {
+    ++did_swaps_;
+    EXPECT_TRUE(result);
+    LayerImpl* root = host_impl->active_tree()->root_layer();
+    LayerImpl* scroll_layer = root->children()[0];
+    switch (did_swaps_) {
+      case 1:
+        // Scroll on the thread.  This should damage the scrollbar for the
+        // next draw on the thread.
+        scroll_layer->ScrollBy(gfx::Vector2dF(10.f, 10.f));
+        host_impl->SetNeedsRedraw();
+        break;
+      case 2:
+        // Forcibly send the scroll to the main thread.
+        PostSetNeedsCommitToMainThread();
+        break;
+      case 3:
+        // First swap after second commit.
+        EndTest();
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+  }
+
+  virtual void AfterTest() OVERRIDE {
+    EXPECT_EQ(3, did_swaps_);
+  }
+
+  int did_swaps_;
+};
+
+MULTI_THREAD_TEST_F(LayerTreeHostDamageTestScrollbarCommitDoesNoDamage);
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host_unittest_delegated.cc b/cc/trees/layer_tree_host_unittest_delegated.cc
index 721613a..3fd040bc 100644
--- a/cc/trees/layer_tree_host_unittest_delegated.cc
+++ b/cc/trees/layer_tree_host_unittest_delegated.cc
@@ -7,6 +7,9 @@
 #include <algorithm>
 
 #include "base/bind.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
 #include "cc/layers/delegated_renderer_layer.h"
 #include "cc/layers/delegated_renderer_layer_client.h"
 #include "cc/layers/delegated_renderer_layer_impl.h"
@@ -1582,5 +1585,107 @@
 
 SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDelegatedTestCommitWithoutTake);
 
+class DelegatedFrameIsActivatedDuringCommit
+    : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer {
+ protected:
+  DelegatedFrameIsActivatedDuringCommit()
+      : wait_thread_("WAIT"),
+        wait_event_(false, false) {
+    wait_thread_.Start();
+  }
+
+  virtual void BeginTest() OVERRIDE {
+    activate_count_ = 0;
+
+    scoped_ptr<DelegatedFrameData> frame =
+        CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
+    AddTextureQuad(frame.get(), 999);
+    AddTransferableResource(frame.get(), 999);
+    delegated_->SetFrameData(frame.Pass());
+
+    PostSetNeedsCommitToMainThread();
+  }
+
+  virtual void WillActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+    // Slow down activation so the main thread DidCommit() will run if
+    // not blocked.
+    wait_thread_.message_loop()->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&base::WaitableEvent::Signal,
+                   base::Unretained(&wait_event_)),
+        base::TimeDelta::FromMilliseconds(10));
+    wait_event_.Wait();
+
+    base::AutoLock lock(activate_lock_);
+    ++activate_count_;
+  }
+
+  virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+    // The main thread is awake now, and will run DidCommit() immediately.
+    // Run DidActivate() afterwards by posting it now.
+    proxy()->MainThreadTaskRunner()->PostTask(
+        FROM_HERE,
+        base::Bind(&DelegatedFrameIsActivatedDuringCommit::DidActivate,
+                   base::Unretained(this)));
+  }
+
+  void DidActivate() {
+    base::AutoLock lock(activate_lock_);
+    switch (activate_count_) {
+      case 1: {
+        // The first frame has been activated. Set a new frame, and
+        // expect the next commit to finish *after* it is activated.
+        scoped_ptr<DelegatedFrameData> frame =
+            CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
+        AddTextureQuad(frame.get(), 555);
+        AddTransferableResource(frame.get(), 555);
+        delegated_->SetFrameData(frame.Pass());
+        // So this commit number should complete after the second activate.
+        EXPECT_EQ(1, layer_tree_host()->source_frame_number());
+        break;
+      }
+      case 2:
+        // The second frame has been activated. Remove the layer from
+        // the tree to cause another commit/activation. The commit should
+        // finish *after* the layer is removed from the active tree.
+        delegated_->RemoveFromParent();
+        // So this commit number should complete after the third activate.
+        EXPECT_EQ(2, layer_tree_host()->source_frame_number());
+        break;
+      case 3:
+        EndTest();
+        break;
+    }
+  }
+
+  virtual void DidCommit() OVERRIDE {
+    switch (layer_tree_host()->source_frame_number()) {
+      case 2: {
+        // The activate for the 2nd frame should have happened before now.
+        base::AutoLock lock(activate_lock_);
+        EXPECT_EQ(2, activate_count_);
+        break;
+      }
+      case 3: {
+        // The activate to remove the layer should have happened before now.
+        base::AutoLock lock(activate_lock_);
+        EXPECT_EQ(3, activate_count_);
+        break;
+      }
+    }
+  }
+
+
+  virtual void AfterTest() OVERRIDE {}
+
+  base::Thread wait_thread_;
+  base::WaitableEvent wait_event_;
+  base::Lock activate_lock_;
+  int activate_count_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+    DelegatedFrameIsActivatedDuringCommit);
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc
index 585d5bf..ab87be1 100644
--- a/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -722,8 +722,7 @@
         main_thread_scroll_(40, 5),
         impl_thread_scroll1_(2, -1),
         impl_thread_scroll2_(-3, 10),
-        num_scrolls_(0),
-        can_activate_(true) {}
+        num_scrolls_(0) {}
 
   virtual void BeginTest() OVERRIDE {
     layer_tree_host()->root_layer()->SetScrollable(true);
@@ -748,15 +747,6 @@
     }
   }
 
-  virtual bool CanActivatePendingTree(LayerTreeHostImpl* impl) OVERRIDE {
-    return can_activate_;
-  }
-
-  virtual bool CanActivatePendingTreeIfNeeded(LayerTreeHostImpl* impl)
-      OVERRIDE {
-    return can_activate_;
-  }
-
   virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE {
     // We force a second draw here of the first commit before activating
     // the second commit.
@@ -774,7 +764,7 @@
     switch (impl->active_tree()->source_frame_number()) {
       case 0:
         if (!impl->pending_tree()) {
-          can_activate_ = false;
+          impl->BlockNotifyReadyToActivateForTesting(true);
           EXPECT_VECTOR_EQ(root->ScrollDelta(), gfx::Vector2d());
           root->ScrollBy(impl_thread_scroll1_);
 
@@ -786,7 +776,7 @@
           // CommitCompleteOnThread will trigger this function again
           // and cause us to take the else clause.
         } else {
-          can_activate_ = true;
+          impl->BlockNotifyReadyToActivateForTesting(false);
           ASSERT_TRUE(pending_root);
           EXPECT_EQ(impl->pending_tree()->source_frame_number(), 1);
 
@@ -828,7 +818,6 @@
   gfx::Vector2d impl_thread_scroll1_;
   gfx::Vector2d impl_thread_scroll2_;
   int num_scrolls_;
-  bool can_activate_;
 };
 
 MULTI_THREAD_TEST_F(ImplSidePaintingScrollTestSimple);
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index f9b294b..853a113 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -616,7 +616,7 @@
       case UIResourceRequest::UIResourceDelete:
         layer_tree_host_impl_->DeleteUIResource(req.id);
         break;
-      default:
+      case UIResourceRequest::UIResourceInvalidRequest:
         NOTREACHED();
         break;
     }
diff --git a/cc/trees/layer_tree_settings.cc b/cc/trees/layer_tree_settings.cc
index 62fd261..729b4c7 100644
--- a/cc/trees/layer_tree_settings.cc
+++ b/cc/trees/layer_tree_settings.cc
@@ -26,7 +26,7 @@
       show_overdraw_in_tracing(false),
       can_use_lcd_text(true),
       should_clear_root_render_pass(true),
-      use_linear_fade_scrollbar_animator(false),
+      scrollbar_animator(NoAnimator),
       scrollbar_linear_fade_delay_ms(300),
       scrollbar_linear_fade_length_ms(300),
       solid_color_scrollbars(false),
diff --git a/cc/trees/layer_tree_settings.h b/cc/trees/layer_tree_settings.h
index d3a71cf..86a657e 100644
--- a/cc/trees/layer_tree_settings.h
+++ b/cc/trees/layer_tree_settings.h
@@ -33,7 +33,12 @@
   bool show_overdraw_in_tracing;
   bool can_use_lcd_text;
   bool should_clear_root_render_pass;
-  bool use_linear_fade_scrollbar_animator;
+
+  enum ScrollbarAnimator {
+    NoAnimator,
+    LinearFade,
+  };
+  ScrollbarAnimator scrollbar_animator;
   int scrollbar_linear_fade_delay_ms;
   int scrollbar_linear_fade_length_ms;
   bool solid_color_scrollbars;
diff --git a/cc/trees/proxy.h b/cc/trees/proxy.h
index 9845b0f..84844c8 100644
--- a/cc/trees/proxy.h
+++ b/cc/trees/proxy.h
@@ -69,6 +69,7 @@
   virtual void SetNeedsUpdateLayers() = 0;
   virtual void SetNeedsCommit() = 0;
   virtual void SetNeedsRedraw(gfx::Rect damage_rect) = 0;
+  virtual void SetNextCommitWaitsForActivation() = 0;
 
   virtual void NotifyInputThrottledUntilCommit() = 0;
 
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index 65d7980..7390a32 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -11,6 +11,7 @@
 #include "cc/quads/draw_quad.h"
 #include "cc/resources/prioritized_resource_manager.h"
 #include "cc/resources/resource_update_controller.h"
+#include "cc/trees/blocking_task_runner.h"
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/layer_tree_impl.h"
 
@@ -100,7 +101,7 @@
   layer_tree_host_impl_->SetVisible(visible);
 
   // Changing visibility could change ShouldComposite().
-  layer_tree_host_impl_->UpdateBackgroundAnimateTicking(!ShouldComposite());
+  UpdateBackgroundAnimateTicking();
 }
 
 void SingleThreadProxy::CreateAndInitializeOutputSurface() {
@@ -143,12 +144,13 @@
     if (initialized) {
       renderer_capabilities_for_main_thread_ =
           layer_tree_host_impl_->GetRendererCapabilities();
-
-      layer_tree_host_impl_->resource_provider()->
-          set_offscreen_context_provider(offscreen_context_provider);
     } else if (offscreen_context_provider.get()) {
       offscreen_context_provider->VerifyContexts();
+      offscreen_context_provider = NULL;
     }
+
+    layer_tree_host_impl_->SetOffscreenContextProvider(
+        offscreen_context_provider);
   }
 
   OnOutputSurfaceInitializeAttempted(initialized);
@@ -186,6 +188,11 @@
     DebugScopedSetMainThreadBlocked mainThreadBlocked(this);
     DebugScopedSetImplThread impl(this);
 
+    // This CapturePostTasks should be destroyed before CommitComplete() is
+    // called since that goes out to the embedder, and we want the embedder
+    // to receive its callbacks before that.
+    BlockingTaskRunner::CapturePostTasks blocked;
+
     RenderingStatsInstrumentation* stats_instrumentation =
         layer_tree_host_->rendering_stats_instrumentation();
     base::TimeTicks start_time = stats_instrumentation->StartRecording();
@@ -221,6 +228,7 @@
 
     base::TimeDelta duration = stats_instrumentation->EndRecording(start_time);
     stats_instrumentation->AddCommit(duration);
+    stats_instrumentation->IssueTraceEventForMainThreadStats();
     stats_instrumentation->AccumulateAndClearMainThreadStats();
   }
   layer_tree_host_->CommitComplete();
@@ -236,9 +244,8 @@
   SetNeedsRedrawRectOnImplThread(damage_rect);
 }
 
-void SingleThreadProxy::OnHasPendingTreeStateChanged(bool have_pending_tree) {
-  // Thread-only feature.
-  NOTREACHED();
+void SingleThreadProxy::SetNextCommitWaitsForActivation() {
+  // There is no activation here other than commit. So do nothing.
 }
 
 void SingleThreadProxy::SetDeferCommits(bool defer_commits) {
@@ -268,7 +275,12 @@
 
 void SingleThreadProxy::OnCanDrawStateChanged(bool can_draw) {
   DCHECK(Proxy::IsImplThread());
-  layer_tree_host_impl_->UpdateBackgroundAnimateTicking(!ShouldComposite());
+  UpdateBackgroundAnimateTicking();
+}
+
+void SingleThreadProxy::NotifyReadyToActivate() {
+  // Thread-only feature.
+  NOTREACHED();
 }
 
 void SingleThreadProxy::SetNeedsRedrawOnImplThread() {
@@ -333,13 +345,6 @@
 
 bool SingleThreadProxy::IsInsideDraw() { return inside_draw_; }
 
-void SingleThreadProxy::DidTryInitializeRendererOnImplThread(
-    bool success,
-    scoped_refptr<ContextProvider> offscreen_context_provider) {
-  NOTREACHED()
-      << "This is only used on threaded compositing with impl-side painting";
-}
-
 void SingleThreadProxy::DidLoseOutputSurfaceOnImplThread() {
   // Cause a commit so we can notice the lost context.
   SetNeedsCommitOnImplThread();
@@ -432,6 +437,12 @@
          layer_tree_host_impl_->CanDraw();
 }
 
+void SingleThreadProxy::UpdateBackgroundAnimateTicking() {
+  DCHECK(Proxy::IsImplThread());
+  layer_tree_host_impl_->UpdateBackgroundAnimateTicking(
+      !ShouldComposite() && layer_tree_host_impl_->active_tree()->root_layer());
+}
+
 bool SingleThreadProxy::DoComposite(
     scoped_refptr<cc::ContextProvider> offscreen_context_provider,
     base::TimeTicks frame_begin_time,
@@ -445,8 +456,8 @@
     DebugScopedSetImplThread impl(this);
     base::AutoReset<bool> mark_inside(&inside_draw_, true);
 
-    layer_tree_host_impl_->resource_provider()->
-        set_offscreen_context_provider(offscreen_context_provider);
+    layer_tree_host_impl_->SetOffscreenContextProvider(
+        offscreen_context_provider);
 
     bool can_do_readback = layer_tree_host_impl_->renderer()->CanReadPixels();
 
@@ -455,14 +466,14 @@
     // DrawLayers() depends on the result of PrepareToDraw(), it is guarded on
     // CanDraw() as well.
     if (!ShouldComposite() || (for_readback && !can_do_readback)) {
-      layer_tree_host_impl_->UpdateBackgroundAnimateTicking(true);
+      UpdateBackgroundAnimateTicking();
       return false;
     }
 
     layer_tree_host_impl_->Animate(
         layer_tree_host_impl_->CurrentFrameTimeTicks(),
         layer_tree_host_impl_->CurrentFrameTime());
-    layer_tree_host_impl_->UpdateBackgroundAnimateTicking(false);
+    UpdateBackgroundAnimateTicking();
 
     layer_tree_host_impl_->PrepareToDraw(frame, device_viewport_damage_rect);
     layer_tree_host_impl_->DrawLayers(frame, frame_begin_time);
@@ -476,8 +487,8 @@
   }
 
   if (lost_output_surface) {
-    cc::ContextProvider* offscreen_contexts = layer_tree_host_impl_->
-        resource_provider()->offscreen_context_provider();
+    cc::ContextProvider* offscreen_contexts =
+        layer_tree_host_impl_->offscreen_context_provider();
     if (offscreen_contexts)
       offscreen_contexts->VerifyContexts();
     layer_tree_host_->DidLoseOutputSurface();
diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h
index 8e9438c..ee92173 100644
--- a/cc/trees/single_thread_proxy.h
+++ b/cc/trees/single_thread_proxy.h
@@ -35,6 +35,7 @@
   virtual void SetNeedsUpdateLayers() OVERRIDE;
   virtual void SetNeedsCommit() OVERRIDE;
   virtual void SetNeedsRedraw(gfx::Rect damage_rect) OVERRIDE;
+  virtual void SetNextCommitWaitsForActivation() OVERRIDE;
   virtual void NotifyInputThrottledUntilCommit() OVERRIDE {}
   virtual void SetDeferCommits(bool defer_commits) OVERRIDE;
   virtual bool CommitRequested() const OVERRIDE;
@@ -48,15 +49,12 @@
   virtual bool CommitPendingForTesting() OVERRIDE;
 
   // LayerTreeHostImplClient implementation
-  virtual void DidTryInitializeRendererOnImplThread(
-      bool success,
-      scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE;
   virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE;
   virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE {}
   virtual void BeginFrameOnImplThread(const BeginFrameArgs& args)
       OVERRIDE {}
   virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE;
-  virtual void OnHasPendingTreeStateChanged(bool have_pending_tree) OVERRIDE;
+  virtual void NotifyReadyToActivate() OVERRIDE;
   virtual void SetNeedsRedrawOnImplThread() OVERRIDE;
   virtual void SetNeedsRedrawRectOnImplThread(gfx::Rect dirty_rect) OVERRIDE;
   virtual void DidInitializeVisibleTileOnImplThread() OVERRIDE;
@@ -96,6 +94,7 @@
   void DidSwapFrame();
 
   bool ShouldComposite() const;
+  void UpdateBackgroundAnimateTicking();
 
   // Accessed on main thread only.
   LayerTreeHost* layer_tree_host_;
diff --git a/cc/trees/thread_proxy.cc b/cc/trees/thread_proxy.cc
index 955ce25..dda25ec 100644
--- a/cc/trees/thread_proxy.cc
+++ b/cc/trees/thread_proxy.cc
@@ -18,6 +18,7 @@
 #include "cc/scheduler/delay_based_time_source.h"
 #include "cc/scheduler/frame_rate_controller.h"
 #include "cc/scheduler/scheduler.h"
+#include "cc/trees/blocking_task_runner.h"
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/layer_tree_impl.h"
 
@@ -75,6 +76,8 @@
       textures_acquired_(true),
       in_composite_and_readback_(false),
       manage_tiles_pending_(false),
+      commit_waits_for_activation_(false),
+      inside_commit_(false),
       weak_factory_on_impl_thread_(this),
       weak_factory_(this),
       begin_frame_sent_to_main_thread_completion_event_on_impl_thread_(NULL),
@@ -223,6 +226,7 @@
 void ThreadProxy::SetVisible(bool visible) {
   TRACE_EVENT0("cc", "ThreadProxy::SetVisible");
   DebugScopedSetMainThreadBlocked main_thread_blocked(this);
+
   CompletionEvent completion;
   Proxy::ImplThreadTaskRunner()->PostTask(
       FROM_HERE,
@@ -238,11 +242,16 @@
   TRACE_EVENT0("cc", "ThreadProxy::SetVisibleOnImplThread");
   layer_tree_host_impl_->SetVisible(visible);
   scheduler_on_impl_thread_->SetVisible(visible);
-  layer_tree_host_impl_->UpdateBackgroundAnimateTicking(
-      !scheduler_on_impl_thread_->WillDrawIfNeeded());
+  UpdateBackgroundAnimateTicking();
   completion->Signal();
 }
 
+void ThreadProxy::UpdateBackgroundAnimateTicking() {
+  layer_tree_host_impl_->UpdateBackgroundAnimateTicking(
+      !scheduler_on_impl_thread_->WillDrawIfNeeded() &&
+      layer_tree_host_impl_->active_tree()->root_layer());
+}
+
 void ThreadProxy::DoCreateAndInitializeOutputSurface() {
   TRACE_EVENT0("cc", "ThreadProxy::DoCreateAndInitializeOutputSurface");
   DCHECK(IsMainThread());
@@ -374,8 +383,8 @@
   TRACE_EVENT0("cc", "ThreadProxy::CheckOutputSurfaceStatusOnImplThread");
   if (!layer_tree_host_impl_->IsContextLost())
     return;
-  if (cc::ContextProvider* offscreen_contexts = layer_tree_host_impl_
-          ->resource_provider()->offscreen_context_provider())
+  if (cc::ContextProvider* offscreen_contexts =
+          layer_tree_host_impl_->offscreen_context_provider())
     offscreen_contexts->VerifyContexts();
   scheduler_on_impl_thread_->DidLoseOutputSurface();
 }
@@ -406,15 +415,12 @@
   TRACE_EVENT1(
       "cc", "ThreadProxy::OnCanDrawStateChanged", "can_draw", can_draw);
   scheduler_on_impl_thread_->SetCanDraw(can_draw);
-  layer_tree_host_impl_->UpdateBackgroundAnimateTicking(
-      !scheduler_on_impl_thread_->WillDrawIfNeeded());
+  UpdateBackgroundAnimateTicking();
 }
 
-void ThreadProxy::OnHasPendingTreeStateChanged(bool has_pending_tree) {
-  DCHECK(IsImplThread());
-  TRACE_EVENT1("cc", "ThreadProxy::OnHasPendingTreeStateChanged",
-               "has_pending_tree", has_pending_tree);
-  scheduler_on_impl_thread_->SetHasPendingTree(has_pending_tree);
+void ThreadProxy::NotifyReadyToActivate() {
+  TRACE_EVENT0("cc", "ThreadProxy::NotifyReadyToActivate");
+  scheduler_on_impl_thread_->NotifyReadyToActivate();
 }
 
 void ThreadProxy::SetNeedsCommitOnImplThread() {
@@ -502,6 +508,12 @@
                  damage_rect));
 }
 
+void ThreadProxy::SetNextCommitWaitsForActivation() {
+  DCHECK(IsMainThread());
+  DCHECK(!inside_commit_);
+  commit_waits_for_activation_ = true;
+}
+
 void ThreadProxy::SetDeferCommits(bool defer_commits) {
   DCHECK(IsMainThread());
   DCHECK_NE(defer_commits_, defer_commits);
@@ -584,6 +596,7 @@
   DCHECK(IsMainThread());
   DCHECK(Proxy::HasImplThread());
   DCHECK(first_output_surface);
+
   // Create LayerTreeHostImpl.
   DebugScopedSetMainThreadBlocked main_thread_blocked(this);
   CompletionEvent completion;
@@ -817,10 +830,15 @@
   // point of view, but asynchronously performed on the impl thread,
   // coordinated by the Scheduler.
   {
-    TRACE_EVENT_BEGIN0("cc", "ThreadProxy::BeginFrameOnMainThread::commit");
+    TRACE_EVENT0("cc", "ThreadProxy::BeginFrameOnMainThread::commit");
 
     DebugScopedSetMainThreadBlocked main_thread_blocked(this);
 
+    // This CapturePostTasks should be destroyed before CommitComplete() is
+    // called since that goes out to the embedder, and we want the embedder
+    // to receive its callbacks before that.
+    BlockingTaskRunner::CapturePostTasks blocked;
+
     RenderingStatsInstrumentation* stats_instrumentation =
         layer_tree_host_->rendering_stats_instrumentation();
     base::TimeTicks start_time = stats_instrumentation->StartRecording();
@@ -837,9 +855,7 @@
 
     base::TimeDelta duration = stats_instrumentation->EndRecording(start_time);
     stats_instrumentation->AddCommit(duration);
-    TRACE_EVENT_END1("cc", "ThreadProxy::BeginFrameOnMainThread::commit",
-                     "data", stats_instrumentation->
-                     GetMainThreadRenderingStats().AsTraceableData());
+    stats_instrumentation->IssueTraceEventForMainThreadStats();
     stats_instrumentation->AccumulateAndClearMainThreadStats();
   }
 
@@ -867,8 +883,8 @@
 
   if (offscreen_context_provider.get())
     offscreen_context_provider->BindToCurrentThread();
-  layer_tree_host_impl_->resource_provider()->
-      set_offscreen_context_provider(offscreen_context_provider);
+  layer_tree_host_impl_->SetOffscreenContextProvider(
+      offscreen_context_provider);
 
   if (layer_tree_host_->contents_texture_manager()) {
     if (layer_tree_host_->contents_texture_manager()->
@@ -925,21 +941,21 @@
   current_resource_update_controller_on_impl_thread_->Finalize();
   current_resource_update_controller_on_impl_thread_.reset();
 
+  inside_commit_ = true;
   layer_tree_host_impl_->BeginCommit();
   layer_tree_host_->BeginCommitOnImplThread(layer_tree_host_impl_.get());
   layer_tree_host_->FinishCommitOnImplThread(layer_tree_host_impl_.get());
   layer_tree_host_impl_->CommitComplete();
+  inside_commit_ = false;
 
   SetInputThrottledUntilCommitOnImplThread(false);
 
-  layer_tree_host_impl_->UpdateBackgroundAnimateTicking(
-      !scheduler_on_impl_thread_->WillDrawIfNeeded());
+  UpdateBackgroundAnimateTicking();
 
   next_frame_is_newly_committed_frame_on_impl_thread_ = true;
 
   if (layer_tree_host_->settings().impl_side_painting &&
-      layer_tree_host_->BlocksPendingCommit() &&
-      layer_tree_host_impl_->pending_tree()) {
+      commit_waits_for_activation_) {
     // For some layer types in impl-side painting, the commit is held until
     // the pending tree is activated.  It's also possible that the
     // pending tree has already activated if there was no work to be done.
@@ -952,6 +968,8 @@
     commit_completion_event_on_impl_thread_ = NULL;
   }
 
+  commit_waits_for_activation_ = false;
+
   commit_complete_time_ = base::TimeTicks::HighResNow();
   begin_frame_to_commit_duration_history_.InsertSample(
       commit_complete_time_ - begin_frame_sent_to_main_thread_time_);
@@ -966,10 +984,10 @@
   layer_tree_host_impl_->UpdateVisibleTiles();
 }
 
-void ThreadProxy::ScheduledActionActivatePendingTreeIfNeeded() {
+void ThreadProxy::ScheduledActionActivatePendingTree() {
   DCHECK(IsImplThread());
-  TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionActivatePendingTreeIfNeeded");
-  layer_tree_host_impl_->ActivatePendingTreeIfNeeded();
+  TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionActivatePendingTree");
+  layer_tree_host_impl_->ActivatePendingTree();
 }
 
 void ThreadProxy::ScheduledActionBeginOutputSurfaceCreation() {
@@ -1003,13 +1021,10 @@
   base::Time wall_clock_time = layer_tree_host_impl_->CurrentFrameTime();
 
   // TODO(enne): This should probably happen post-animate.
-  if (layer_tree_host_impl_->pending_tree()) {
-    layer_tree_host_impl_->ActivatePendingTreeIfNeeded();
-    if (layer_tree_host_impl_->pending_tree())
-      layer_tree_host_impl_->pending_tree()->UpdateDrawProperties();
-  }
+  if (layer_tree_host_impl_->pending_tree())
+    layer_tree_host_impl_->pending_tree()->UpdateDrawProperties();
   layer_tree_host_impl_->Animate(monotonic_time, wall_clock_time);
-  layer_tree_host_impl_->UpdateBackgroundAnimateTicking(false);
+  UpdateBackgroundAnimateTicking();
 
   base::TimeTicks start_time = base::TimeTicks::HighResNow();
   base::TimeDelta draw_duration_estimate = DrawDurationEstimate();
@@ -1056,7 +1071,6 @@
     result.did_draw = true;
   }
   layer_tree_host_impl_->DidDrawAllLayers(frame);
-
   layer_tree_host_impl_->UpdateAnimationState(start_ready_animations);
 
   // Check for a pending CompositeAndReadback.
@@ -1302,30 +1316,18 @@
   if (*success) {
     *capabilities = layer_tree_host_impl_->GetRendererCapabilities();
     scheduler_on_impl_thread_->DidCreateAndInitializeOutputSurface();
+  } else if (offscreen_context_provider.get()) {
+    if (offscreen_context_provider->BindToCurrentThread())
+      offscreen_context_provider->VerifyContexts();
+    offscreen_context_provider = NULL;
   }
 
-  DidTryInitializeRendererOnImplThread(*success, offscreen_context_provider);
+  layer_tree_host_impl_->SetOffscreenContextProvider(
+      offscreen_context_provider);
 
   completion->Signal();
 }
 
-void ThreadProxy::DidTryInitializeRendererOnImplThread(
-    bool success,
-    scoped_refptr<ContextProvider> offscreen_context_provider) {
-  DCHECK(IsImplThread());
-  DCHECK(!inside_draw_);
-
-  if (offscreen_context_provider.get())
-    offscreen_context_provider->BindToCurrentThread();
-
-  if (success) {
-    layer_tree_host_impl_->resource_provider()->
-        set_offscreen_context_provider(offscreen_context_provider);
-  } else if (offscreen_context_provider.get()) {
-    offscreen_context_provider->VerifyContexts();
-  }
-}
-
 void ThreadProxy::FinishGLOnImplThread(CompletionEvent* completion) {
   TRACE_EVENT0("cc", "ThreadProxy::FinishGLOnImplThread");
   DCHECK(IsImplThread());
@@ -1407,7 +1409,7 @@
 
 scoped_ptr<base::Value> ThreadProxy::SchedulerStateAsValueForTesting() {
   if (IsImplThread())
-    return scheduler_on_impl_thread_->StateAsValueForTesting().Pass();
+    return scheduler_on_impl_thread_->StateAsValue().Pass();
 
   SchedulerStateRequest scheduler_state_request;
   {
@@ -1425,7 +1427,7 @@
 void ThreadProxy::SchedulerStateAsValueOnImplThreadForTesting(
     SchedulerStateRequest* request) {
   DCHECK(IsImplThread());
-  request->state = scheduler_on_impl_thread_->StateAsValueForTesting();
+  request->state = scheduler_on_impl_thread_->StateAsValue();
   request->completion.Signal();
 }
 
@@ -1519,6 +1521,8 @@
     completion_event_for_commit_held_on_tree_activation_ = NULL;
   }
 
+  UpdateBackgroundAnimateTicking();
+
   commit_to_activate_duration_history_.InsertSample(
       base::TimeTicks::HighResNow() - commit_complete_time_);
 }
diff --git a/cc/trees/thread_proxy.h b/cc/trees/thread_proxy.h
index 7a8a146..8122bf4 100644
--- a/cc/trees/thread_proxy.h
+++ b/cc/trees/thread_proxy.h
@@ -52,6 +52,7 @@
   virtual void SetNeedsUpdateLayers() OVERRIDE;
   virtual void SetNeedsCommit() OVERRIDE;
   virtual void SetNeedsRedraw(gfx::Rect damage_rect) OVERRIDE;
+  virtual void SetNextCommitWaitsForActivation() OVERRIDE;
   virtual void NotifyInputThrottledUntilCommit() OVERRIDE;
   virtual void SetDeferCommits(bool defer_commits) OVERRIDE;
   virtual bool CommitRequested() const OVERRIDE;
@@ -66,14 +67,11 @@
   virtual scoped_ptr<base::Value> SchedulerStateAsValueForTesting() OVERRIDE;
 
   // LayerTreeHostImplClient implementation
-  virtual void DidTryInitializeRendererOnImplThread(
-      bool success,
-      scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE;
   virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE;
   virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE;
   virtual void BeginFrameOnImplThread(const BeginFrameArgs& args) OVERRIDE;
   virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE;
-  virtual void OnHasPendingTreeStateChanged(bool has_pending_tree) OVERRIDE;
+  virtual void NotifyReadyToActivate() OVERRIDE;
   virtual void SetNeedsRedrawOnImplThread() OVERRIDE;
   virtual void SetNeedsRedrawRectOnImplThread(gfx::Rect dirty_rect) OVERRIDE;
   virtual void DidInitializeVisibleTileOnImplThread() OVERRIDE;
@@ -101,7 +99,7 @@
       OVERRIDE;
   virtual void ScheduledActionCommit() OVERRIDE;
   virtual void ScheduledActionUpdateVisibleTiles() OVERRIDE;
-  virtual void ScheduledActionActivatePendingTreeIfNeeded() OVERRIDE;
+  virtual void ScheduledActionActivatePendingTree() OVERRIDE;
   virtual void ScheduledActionBeginOutputSurfaceCreation() OVERRIDE;
   virtual void ScheduledActionAcquireLayerTexturesForMainThread() OVERRIDE;
   virtual void DidAnticipatedDrawTimeChange(base::TimeTicks time) OVERRIDE;
@@ -155,6 +153,7 @@
   void InitializeImplOnImplThread(CompletionEvent* completion);
   void SetLayerTreeHostClientReadyOnImplThread();
   void SetVisibleOnImplThread(CompletionEvent* completion, bool visible);
+  void UpdateBackgroundAnimateTicking();
   void HasInitializedOutputSurfaceOnImplThread(
       CompletionEvent* completion,
       bool* has_initialized_output_surface);
@@ -206,6 +205,10 @@
   // anything else.
   scoped_ptr<OutputSurface> first_output_surface_;
 
+  // Accessed on the main thread, or when main thread is blocked.
+  bool commit_waits_for_activation_;
+  bool inside_commit_;
+
   base::WeakPtrFactory<ThreadProxy> weak_factory_on_impl_thread_;
 
   base::WeakPtr<ThreadProxy> main_thread_weak_ptr_;
diff --git a/cc/trees/tree_synchronizer.cc b/cc/trees/tree_synchronizer.cc
index aed4de0..cf1b382 100644
--- a/cc/trees/tree_synchronizer.cc
+++ b/cc/trees/tree_synchronizer.cc
@@ -5,6 +5,7 @@
 #include "cc/trees/tree_synchronizer.h"
 
 #include "base/containers/hash_tables.h"
+#include "base/containers/scoped_ptr_hash_map.h"
 #include "base/debug/trace_event.h"
 #include "base/logging.h"
 #include "cc/animation/scrollbar_animation_controller.h"
@@ -16,7 +17,7 @@
 
 namespace cc {
 
-typedef ScopedPtrHashMap<int, LayerImpl> ScopedPtrLayerImplMap;
+typedef base::ScopedPtrHashMap<int, LayerImpl> ScopedPtrLayerImplMap;
 typedef base::hash_map<int, LayerImpl*> RawPtrLayerImplMap;
 
 void CollectExistingLayerImplRecursive(ScopedPtrLayerImplMap* old_layers,
diff --git a/cc/trees/tree_synchronizer.h b/cc/trees/tree_synchronizer.h
index 40fa69d..de48cee 100644
--- a/cc/trees/tree_synchronizer.h
+++ b/cc/trees/tree_synchronizer.h
@@ -8,7 +8,6 @@
 #include "base/basictypes.h"
 #include "base/memory/scoped_ptr.h"
 #include "cc/base/cc_export.h"
-#include "cc/base/scoped_ptr_hash_map.h"
 
 namespace cc {
 
diff --git a/chrome/DEPS b/chrome/DEPS
index 07ac7d3..252d584 100644
--- a/chrome/DEPS
+++ b/chrome/DEPS
@@ -30,6 +30,7 @@
   "+chrome/test",
   "+components/json_schema",
   "+components/sessions",
+  "+components/variations",
   "+components/visitedlink/common",
   "+content/public/common",
   "+content/public/test",
diff --git a/chrome/VERSION b/chrome/VERSION
index e1956ba..0fb1bd9 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=31
 MINOR=0
-BUILD=1610
+BUILD=1617
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/BookmarksBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/BookmarksBridge.java
index db0c485..ec65f74 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/BookmarksBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/BookmarksBridge.java
@@ -11,7 +11,8 @@
 import java.util.List;
 
 /**
- * Handler to fetch the bookmarks, titles, urls and folder hierarchy.
+ * Provides the communication channel for Android to fetch and manipulate the
+ * bookmark model stored in native.
  */
 public class BookmarksBridge {
 
@@ -99,8 +100,16 @@
         }
     }
 
+    /**
+     * Deletes a specified bookmark node.
+     * @param bookmarkId The ID of the bookmark to be deleted.
+     */
+    public void deleteBookmark(long bookmarkId) {
+        nativeDeleteBookmark(mNativeBookmarksBridge, bookmarkId);
+    }
+
     @CalledByNative
-    public void bookmarkModelLoaded() {
+    private void bookmarkModelLoaded() {
         mIsNativeBookmarkModelLoaded = true;
         if (!mDelayedBookmarkCallbacks.isEmpty()) {
             for (int i = 0; i < mDelayedBookmarkCallbacks.size(); i++) {
@@ -117,8 +126,8 @@
 
     @CalledByNative
     private static void create(List<BookmarkItem> bookmarksList, long id, String title, String url,
-            boolean isFolder, long parentId) {
-        bookmarksList.add(new BookmarkItem(id, title, url, isFolder, parentId));
+            boolean isFolder, long parentId, boolean isEditable) {
+        bookmarksList.add(new BookmarkItem(id, title, url, isFolder, parentId, isEditable));
     }
 
     private native void nativeGetBookmarksForFolder(int nativeBookmarksBridge,
@@ -127,6 +136,7 @@
     private native void nativeGetCurrentFolderHierarchy(int nativeBookmarksBridge,
             long folderId, BookmarksCallback callback,
             List<BookmarkItem> bookmarksList);
+    private native void nativeDeleteBookmark(int nativeBookmarksBridge, long bookmarkId);
     private native int nativeInit(Profile profile);
     private native void nativeDestroy(int nativeBookmarksBridge);
 
@@ -140,49 +150,47 @@
         private final long mId;
         private final boolean mIsFolder;
         private final long mParentId;
+        private final boolean mIsEditable;
 
-        private BookmarkItem(long id, String title, String url, boolean isFolder, long parentId) {
+        private BookmarkItem(long id, String title, String url, boolean isFolder, long parentId,
+                boolean isEditable) {
             mId = id;
             mTitle = title;
             mUrl = url;
             mIsFolder = isFolder;
             mParentId = parentId;
+            mIsEditable = isEditable;
         }
 
-        /**
-         * @return Title of the bookmark item.
-         */
+        /** @return Title of the bookmark item. */
         public String getTitle() {
             return mTitle;
         }
 
-        /**
-         * @return Url of the bookmark item.
-         */
+        /** @return Url of the bookmark item. */
         public String getUrl() {
             return mUrl;
         }
 
-        /**
-         * @return Id of the bookmark item.
-         */
+        /** @return Id of the bookmark item. */
         public long getId() {
             return mId;
         }
 
-        /**
-         * @return Whether item is a folder or a bookmark.
-         */
+        /** @return Whether item is a folder or a bookmark. */
         public boolean isFolder() {
             return mIsFolder;
         }
 
-        /**
-         * @return Parent id of the bookmark item.
-         */
+        /** @return Parent id of the bookmark item. */
         public long getParentId() {
             return mParentId;
         }
+
+        /** @return Whether this bookmark can be edited. */
+        public boolean isEditable() {
+            return mIsEditable;
+        }
     }
 
     /**
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 61bd49b..43f3987 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBrowserProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBrowserProvider.java
@@ -1280,9 +1280,9 @@
      * Call to get the intent to create a bookmark shortcut on homescreen.
      */
     public static Intent getShortcutToBookmark(String url, String title, Bitmap favicon, int rValue,
-            int gValue, int bValue, Activity activity) {
-        return BookmarkUtils.createAddToHomeIntent(activity, url, title, favicon, rValue, gValue,
-                bValue);
+            int gValue, int bValue, Context context) {
+        return BookmarkUtils.createAddToHomeIntent(
+                context, url, title, favicon, rValue, gValue, bValue);
     }
 
     private native int nativeInit();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeWebContentsDelegateAndroid.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeWebContentsDelegateAndroid.java
index 546f302..f1c0355 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeWebContentsDelegateAndroid.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeWebContentsDelegateAndroid.java
@@ -26,6 +26,12 @@
     public void onFindMatchRectsAvailable(FindMatchRectsDetails result) {
     }
 
+    @CalledByNative
+    public boolean addNewContents(int nativeSourceWebContents, int nativeWebContents,
+            int disposition, Rect initialPosition, boolean userGesture) {
+        return false;
+    }
+
     // Helper functions used to create types that are part of the public interface
     @CalledByNative
     private static Rect createRect(int x, int y, int right, int bottom) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/EmptyTabObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/EmptyTabObserver.java
new file mode 100644
index 0000000..de2149d
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/EmptyTabObserver.java
@@ -0,0 +1,23 @@
+// 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.
+
+package org.chromium.chrome.browser;
+
+/**
+ * An implementation of the {@link TabObserver} which has empty implementations of all methods.
+ */
+public class EmptyTabObserver implements TabObserver {
+
+    @Override
+    public void onLoadProgressChanged(TabBase tab, int progress) { }
+
+    @Override
+    public void onUpdateUrl(TabBase tab, String url) { }
+
+    @Override
+    public void onDestroyed(TabBase tab) { }
+
+    @Override
+    public void onContentChanged(TabBase tab) { }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/NativePage.java b/chrome/android/java/src/org/chromium/chrome/browser/NativePage.java
new file mode 100644
index 0000000..d4d4790
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/NativePage.java
@@ -0,0 +1,28 @@
+// 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.
+
+package org.chromium.chrome.browser;
+
+import org.chromium.content.browser.PageInfo;
+
+/**
+ * An interface for pages that will be shown in a tab using Android views instead of html.
+ */
+public interface NativePage extends PageInfo {
+
+    /**
+     * @return The hostname for this page, e.g. "newtab" or "bookmarks".
+     */
+    public String getHost();
+
+    /**
+     * Called after a page has been removed from the view hierarchy and will no longer be used.
+     */
+    public void destroy();
+
+    /**
+     * Updates the native page based on the given url.
+     */
+    public void updateForUrl(String url);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/TabBase.java b/chrome/android/java/src/org/chromium/chrome/browser/TabBase.java
index 7f73cac..6dd7b17 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/TabBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/TabBase.java
@@ -4,10 +4,25 @@
 
 package org.chromium.chrome.browser;
 
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Color;
+import android.view.View;
+
 import org.chromium.base.CalledByNative;
+import org.chromium.base.ObserverList;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.content.browser.ContentView;
+import org.chromium.content.browser.ContentViewClient;
+import org.chromium.content.browser.ContentViewCore;
+import org.chromium.content.browser.NavigationClient;
+import org.chromium.content.browser.NavigationHistory;
+import org.chromium.content.browser.PageInfo;
+import org.chromium.content.browser.WebContentsObserverAndroid;
 import org.chromium.ui.WindowAndroid;
 
+import java.util.concurrent.atomic.AtomicInteger;
+
 /**
  * The basic Java representation of a tab.  Contains and manages a {@link ContentView}.
  *
@@ -19,18 +34,488 @@
  * destroyed which will call into the native subclass and finally lead to the destruction of the
  * parent classes.
  */
-public abstract class TabBase {
+public abstract class TabBase implements NavigationClient {
     public static final int INVALID_TAB_ID = -1;
 
-    private final WindowAndroid mWindowAndroid;
+    /** Used for automatically generating tab ids. */
+    private static final AtomicInteger sIdCounter = new AtomicInteger();
+
     private int mNativeTabAndroid;
 
-    protected TabBase(WindowAndroid window) {
+    /** Unique id of this tab (within its container). */
+    private final int mId;
+
+    /** Whether or not this tab is an incognito tab. */
+    private final boolean mIncognito;
+
+    /** An Application {@link Context}.  Unlike {@link #mContext}, this is the only one that is
+     * publicly exposed to help prevent leaking the {@link Activity}. */
+    private final Context mApplicationContext;
+
+    /** The {@link Context} used to create {@link View}s and other Android components.  Unlike
+     * {@link #mApplicationContext}, this is not publicly exposed to help prevent leaking the
+     * {@link Activity}. */
+    private final Context mContext;
+
+    /** Gives {@link TabBase} a way to interact with the Android window. */
+    private final WindowAndroid mWindowAndroid;
+
+    /** The current native page (e.g. chrome-native://newtab), or {@code null} if there is none. */
+    private NativePage mNativePage;
+
+    /** The {@link ContentView} showing the current page or {@code null} if the tab is frozen. */
+    private ContentView mContentView;
+
+    /**
+     * The {@link ContentViewCore} for the current page, provided for convenience. This always
+     * equals {@link ContentView#getContentViewCore()}, or {@code null} if mContentView is
+     * {@code null}.
+     */
+    private ContentViewCore mContentViewCore;
+
+    // Observers and Delegates.
+    private ContentViewClient mContentViewClient;
+    private WebContentsObserverAndroid mWebContentsObserver;
+    private TabBaseChromeWebContentsDelegateAndroid mWebContentsDelegate;
+    private ObserverList<TabObserver> mObservers = new ObserverList<TabObserver>();
+
+    /**
+     * A basic {@link ChromeWebContentsDelegateAndroid} that forwards some calls to the registered
+     * {@link TabObserver}s.  Meant to be overridden by subclasses.
+     */
+    public class TabBaseChromeWebContentsDelegateAndroid
+            extends ChromeWebContentsDelegateAndroid {
+        @Override
+        public void onLoadProgressChanged(int progress) {
+            for (TabObserver observer : mObservers) {
+                observer.onLoadProgressChanged(TabBase.this, progress);
+            }
+        }
+
+        @Override
+        public void onUpdateUrl(String url) {
+            for (TabObserver observer : mObservers) observer.onUpdateUrl(TabBase.this, url);
+        }
+    }
+
+    /**
+     * Creates an instance of a {@link TabBase} with no id.
+     * @param incognito Whether or not this tab is incognito.
+     * @param context   An instance of a {@link Context}.
+     * @param window    An instance of a {@link WindowAndroid}.
+     */
+    public TabBase(boolean incognito, Context context, WindowAndroid window) {
+        this(INVALID_TAB_ID, incognito, context, window);
+    }
+
+    /**
+     * Creates an instance of a {@link TabBase}.
+     * @param id        The id this tab should be identified with.
+     * @param incognito Whether or not this tab is incognito.
+     * @param context   An instance of a {@link Context}.
+     * @param window    An instance of a {@link WindowAndroid}.
+     */
+    public TabBase(int id, boolean incognito, Context context, WindowAndroid window) {
+        // We need a valid Activity Context to build the ContentView with.
+        assert context == null || context instanceof Activity;
+
+        mId = generateValidId(id);
+        mIncognito = incognito;
+        // TODO(dtrainor): Only store application context here.
+        mContext = context;
+        mApplicationContext = context != null ? context.getApplicationContext() : null;
         mWindowAndroid = window;
     }
 
+    /**
+     * Adds a {@link TabObserver} to be notified on {@link TabBase} changes.
+     * @param observer The {@link TabObserver} to add.
+     */
+    public final void addObserver(TabObserver observer) {
+        mObservers.addObserver(observer);
+    }
+
+    /**
+     * Removes a {@link TabObserver}.
+     * @param observer The {@link TabObserver} to remove.
+     */
+    public final void removeObserver(TabObserver observer) {
+        mObservers.removeObserver(observer);
+    }
+
+    /**
+     * @return Whether or not this tab has a previous navigation entry.
+     */
+    public boolean canGoBack() {
+        return mContentViewCore != null && mContentViewCore.canGoBack();
+    }
+
+    /**
+     * @return Whether or not this tab has a navigation entry after the current one.
+     */
+    public boolean canGoForward() {
+        return mContentViewCore != null && mContentViewCore.canGoForward();
+    }
+
+    /**
+     * Goes to the navigation entry before the current one.
+     */
+    public void goBack() {
+        if (mContentViewCore != null) mContentViewCore.goBack();
+    }
+
+    /**
+     * Goes to the navigation entry after the current one.
+     */
+    public void goForward() {
+        if (mContentViewCore != null) mContentViewCore.goForward();
+    }
+
+    @Override
+    public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit) {
+        if (mContentViewCore != null) {
+            return mContentViewCore.getDirectedNavigationHistory(isForward, itemLimit);
+        } else {
+            return new NavigationHistory();
+        }
+    }
+
+    @Override
+    public void goToNavigationIndex(int index) {
+        if (mContentViewCore != null) mContentViewCore.goToNavigationIndex(index);
+    }
+
+    /**
+     * @return Whether or not the {@link TabBase} is currently showing an interstitial page, such as
+     *         a bad HTTPS page.
+     */
+    public boolean isShowingInterstitialPage() {
+        ContentViewCore contentViewCore = getContentViewCore();
+        return contentViewCore != null && contentViewCore.isShowingInterstitialPage();
+    }
+
+    /**
+     * @return Whether or not the tab has something valid to render.
+     */
+    public boolean isReady() {
+        return mNativePage != null || (mContentViewCore != null && mContentViewCore.isReady());
+    }
+
+    /**
+     * @return The {@link View} displaying the current page in the tab. This might be a
+     *         {@link ContentView} but could potentially be any instance of {@link View}. This can
+     *         be {@code null}, if the tab is frozen or being initialized or destroyed.
+     */
+    public View getView() {
+        PageInfo pageInfo = getPageInfo();
+        return pageInfo != null ? pageInfo.getView() : null;
+    }
+
+    /**
+     * @return The width of the content of this tab.  Can be 0 if there is no content.
+     */
+    public int getWidth() {
+        View view = getView();
+        return view != null ? view.getWidth() : 0;
+    }
+
+    /**
+     * @return The height of the content of this tab.  Can be 0 if there is no content.
+     */
+    public int getHeight() {
+        View view = getView();
+        return view != null ? view.getHeight() : 0;
+    }
+
+    /**
+     * @return The application {@link Context} associated with this tab.
+     */
+    protected Context getApplicationContext() {
+        return mApplicationContext;
+    }
+
+    /**
+     * Reloads the current page content if it is a {@link ContentView}.
+     */
+    public void reload() {
+        // TODO(dtrainor): Should we try to rebuild the ContentView if it's frozen?
+        ContentViewCore contentViewCore = getContentViewCore();
+        if (contentViewCore != null) contentViewCore.reload();
+    }
+
+    /**
+     * @return The background color of the tab.
+     */
+    public int getBackgroundColor() {
+        return getPageInfo() != null ? getPageInfo().getBackgroundColor() : Color.WHITE;
+    }
+
+    /**
+     * @return The profile associated with this tab.
+     */
+    public Profile getProfile() {
+        if (mNativeTabAndroid == 0) return null;
+        return nativeGetProfileAndroid(mNativeTabAndroid);
+    }
+
+    /**
+     * @return The id representing this tab.
+     */
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * @return Whether or not this tab is incognito.
+     */
+    public boolean isIncognito() {
+        return mIncognito;
+    }
+
+    /**
+     * @return The {@link ContentView} associated with the current page, or {@code null} if
+     *         there is no current page or the current page is displayed using something besides a
+     *         {@link ContentView}.
+     */
+    public ContentView getContentView() {
+        return mNativePage == null ? mContentView : null;
+    }
+
+    /**
+     * @return The {@link ContentViewCore} associated with the current page, or {@code null} if
+     *         there is no current page or the current page is displayed using something besides a
+     *         {@link ContentView}.
+     */
+    public ContentViewCore getContentViewCore() {
+        return mNativePage == null ? mContentViewCore : null;
+    }
+
+    /**
+     * @return A {@link PageInfo} describing the current page.  This is always not {@code null}
+     *         except during initialization, destruction, and when the tab is frozen.
+     */
+    public PageInfo getPageInfo() {
+        return mNativePage != null ? mNativePage : mContentView;
+    }
+
+    /**
+     * @return The {@link NativePage} associated with the current page, or {@code null} if there is
+     *         no current page or the current page is displayed using something besides
+     *         {@link NativePage}.
+     */
+    public NativePage getNativePage() {
+        return mNativePage;
+    }
+
+    /**
+     * @return Whether or not the {@link TabBase} represents a {@link NativePage}.
+     */
+    public boolean isNativePage() {
+        return mNativePage != null;
+    }
+
+    /**
+     * Set whether or not the {@link ContentViewCore} should be using a desktop user agent for the
+     * currently loaded page.
+     * @param useDesktop     If {@code true}, use a desktop user agent.  Otherwise use a mobile one.
+     * @param reloadOnChange Reload the page if the user agent has changed.
+     */
+    public void setUseDesktopUserAgent(boolean useDesktop, boolean reloadOnChange) {
+        if (mContentViewCore != null) {
+            mContentViewCore.setUseDesktopUserAgent(useDesktop, reloadOnChange);
+        }
+    }
+
+    /**
+     * @return Whether or not the {@link ContentViewCore} is using a desktop user agent.
+     */
+    public boolean getUseDesktopUserAgent() {
+        return mContentViewCore != null && mContentViewCore.getUseDesktopUserAgent();
+    }
+
+    /**
+     * @return The {@link ContentViewClient} currently bound to any {@link ContentViewCore}
+     *         associated with the current page.  There can still be a {@link ContentViewClient}
+     *         even when there is no {@link ContentViewCore}.
+     */
+    protected ContentViewClient getContentViewClient() {
+        return mContentViewClient;
+    }
+
+    /**
+     * @param client The {@link ContentViewClient} to be bound to any current or new
+     *               {@link ContentViewCore}s associated with this {@link TabBase}.
+     */
+    protected void setContentViewClient(ContentViewClient client) {
+        if (mContentViewClient == client) return;
+
+        ContentViewClient oldClient = mContentViewClient;
+        mContentViewClient = client;
+
+        if (mContentViewCore == null) return;
+
+        if (mContentViewClient != null) {
+            mContentViewCore.setContentViewClient(mContentViewClient);
+        } else if (oldClient != null) {
+            // We can't set a null client, but we should clear references to the last one.
+            mContentViewCore.setContentViewClient(new ContentViewClient());
+        }
+    }
+
+    /**
+     * Shows the given {@code nativePage} if it's not already showing.
+     * @param nativePage The {@link NativePage} to show.
+     */
+    protected void showNativePage(NativePage nativePage) {
+        if (mNativePage == nativePage) return;
+        destroyNativePageInternal();
+        mNativePage = nativePage;
+        for (TabObserver observer : mObservers) observer.onContentChanged(this);
+    }
+
+    /**
+     * Hides the current {@link NativePage}, if any, and shows the {@link ContentView}.
+     */
+    protected void showRenderedPage() {
+        if (mNativePage == null) return;
+        destroyNativePageInternal();
+        for (TabObserver observer : mObservers) observer.onContentChanged(this);
+    }
+
+    /**
+     * Initializes this {@link TabBase}.
+     */
+    public void initialize() { }
+
+    /**
+     * A helper method to initialize a {@link ContentView} without any native WebContents pointer.
+     */
+    protected final void initContentView() {
+        initContentView(ContentViewUtil.createNativeWebContents(mIncognito));
+    }
+
+    /**
+     * Completes the {@link ContentView} specific initialization around a native WebContents
+     * pointer.  {@link #getPageInfo()} will still return the {@link NativePage} if there is one.
+     * All initialization that needs to reoccur after a web contents swap should be added here.
+     * <p />
+     * NOTE: If you attempt to pass a native WebContents that does not have the same incognito
+     * state as this tab this call will fail.
+     *
+     * @param nativeWebContents The native web contents pointer.
+     */
+    protected void initContentView(int nativeWebContents) {
+        destroyNativePageInternal();
+
+        mContentView = ContentView.newInstance(mContext, nativeWebContents, getWindowAndroid());
+
+        mContentViewCore = mContentView.getContentViewCore();
+        mWebContentsDelegate = createWebContentsDelegate();
+        mWebContentsObserver = createWebContentsObserverAndroid(mContentViewCore);
+
+        if (mContentViewClient != null) mContentViewCore.setContentViewClient(mContentViewClient);
+        nativeInitWebContents(
+                mNativeTabAndroid, mId, mIncognito, mContentViewCore, mWebContentsDelegate);
+    }
+
+    /**
+     * Cleans up all internal state, destroying any {@link NativePage} or {@link ContentView}
+     * currently associated with this {@link TabBase}.  Typically, pnce this call is made this
+     * {@link TabBase} should no longer be used as subclasses usually destroy the native component.
+     */
+    public void destroy() {
+        for (TabObserver observer : mObservers) observer.onDestroyed(this);
+
+        destroyNativePageInternal();
+        destroyContentView(true);
+    }
+
+    private void destroyNativePageInternal() {
+        if (mNativePage == null) return;
+
+        mNativePage.destroy();
+        mNativePage = null;
+    }
+
+    /**
+     * Destroys the current {@link ContentView}.
+     * @param deleteNativeWebContents Whether or not to delete the native WebContents pointer.
+     */
+    protected final void destroyContentView(boolean deleteNativeWebContents) {
+        if (mContentView == null) return;
+
+        destroyContentViewInternal(mContentView);
+
+        if (mContentViewCore != null) mContentViewCore.destroy();
+
+        mContentView = null;
+        mContentViewCore = null;
+        mWebContentsDelegate = null;
+        mWebContentsObserver = null;
+        nativeDestroyWebContents(mNativeTabAndroid, deleteNativeWebContents);
+    }
+
+    /**
+     * Gives subclasses the chance to clean up some state associated with this {@link ContentView}.
+     * This is because {@link #getContentView()} can return {@code null} if a {@link NativePage}
+     * is showing.
+     * @param contentView The {@link ContentView} that should have associated state cleaned up.
+     */
+    protected void destroyContentViewInternal(ContentView contentView) {
+    }
+
+    /**
+     * A helper method to allow subclasses to build their own delegate.
+     * @return An instance of a {@link TabBaseChromeWebContentsDelegateAndroid}.
+     */
+    protected TabBaseChromeWebContentsDelegateAndroid createWebContentsDelegate() {
+        return new TabBaseChromeWebContentsDelegateAndroid();
+    }
+
+    /**
+     * A helper method to allow subclasses to build their own observer.
+     * @param contentViewCore The {@link ContentViewCore} this observer should be built for.
+     * @return An instance of a {@link WebContentsObserverAndroid}.
+     */
+    protected WebContentsObserverAndroid createWebContentsObserverAndroid(
+            ContentViewCore contentViewCore) {
+        return null;
+    }
+
+    /**
+     * @return The {@link WindowAndroid} associated with this {@link TabBase}.
+     */
+    protected WindowAndroid getWindowAndroid() {
+        return mWindowAndroid;
+    }
+
+    /**
+     * @return The current {@link TabBaseChromeWebContentsDelegateAndroid} instance.
+     */
+    protected TabBaseChromeWebContentsDelegateAndroid getChromeWebContentsDelegateAndroid() {
+        return mWebContentsDelegate;
+    }
+
+    /**
+     * @return The native pointer representing the native side of this {@link TabBase} object.
+     */
     @CalledByNative
-    private void destroyBase() {
+    protected int getNativePtr() {
+        return mNativeTabAndroid;
+    }
+
+    /** This is currently called when committing a pre-rendered page. */
+    @CalledByNative
+    private void swapWebContents(final int newWebContents) {
+        destroyContentView(false);
+        initContentView(newWebContents);
+
+        mContentViewCore.onShow();
+        for (TabObserver observer : mObservers) observer.onContentChanged(this);
+    }
+
+    @CalledByNative
+    private void clearNativePtr() {
         assert mNativeTabAndroid != 0;
         mNativeTabAndroid = 0;
     }
@@ -41,11 +526,44 @@
         mNativeTabAndroid = nativePtr;
     }
 
-    int getNativePtr() {
-        return mNativeTabAndroid;
+    /**
+     * Validates {@code id} and increments the internal counter to make sure future ids don't
+     * collide.
+     * @param id The current id.  Maybe {@link #INVALID_TAB_ID}.
+     * @return   A new id if {@code id} was {@link #INVALID_TAB_ID}, or {@code id}.
+     */
+    private static int generateValidId(int id) {
+        if (id == INVALID_TAB_ID) id = generateNextId();
+        incrementIdCounterTo(id + 1);
+
+        return id;
     }
 
-    protected WindowAndroid getWindowAndroid() {
-        return mWindowAndroid;
+    /**
+     * @return An unused id.
+     */
+    private static int generateNextId() {
+        return sIdCounter.getAndIncrement();
     }
+
+    /**
+     * Ensures the counter is at least as high as the specified value.  The counter should always
+     * point to an unused ID (which will be handed out next time a request comes in).  Exposed so
+     * that anything externally loading tabs and ids can set enforce new tabs start at the correct
+     * id.
+     * TODO(aurimas): Investigate reducing the visiblity of this method.
+     * @param id The minimum id we should hand out to the next new tab.
+     */
+    public static void incrementIdCounterTo(int id) {
+        int diff = id - sIdCounter.get();
+        if (diff <= 0) return;
+        // It's possible idCounter has been incremented between the get above and the add below
+        // but that's OK, because in the worst case we'll overly increment idCounter.
+        sIdCounter.addAndGet(diff);
+    }
+
+    private native void nativeInitWebContents(int nativeTabAndroid, int id, boolean incognito,
+            ContentViewCore contentViewCore, ChromeWebContentsDelegateAndroid delegate);
+    private native void nativeDestroyWebContents(int nativeTabAndroid, boolean deleteNative);
+    private native Profile nativeGetProfileAndroid(int nativeTabAndroid);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/TabObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/TabObserver.java
new file mode 100644
index 0000000..57b3bc65
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/TabObserver.java
@@ -0,0 +1,37 @@
+// 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.
+
+package org.chromium.chrome.browser;
+
+/**
+ * An observer that is notified of changes to a {@link TabBase} object.
+ */
+public interface TabObserver {
+
+    /**
+     * Called when the load progress of a {@link TabBase} changes.
+     * @param tab      The notifying {@link TabBase}.
+     * @param progress The new progress from [0,100].
+     */
+    public void onLoadProgressChanged(TabBase tab, int progress);
+
+    /**
+     * Called when the URL of a {@link TabBase} changes.
+     * @param tab The notifying {@link TabBase}.
+     * @param url The new URL.
+     */
+    public void onUpdateUrl(TabBase tab, String url);
+
+    /**
+     * Called when a {@link TabBase} is being destroyed.
+     * @param tab The notifying {@link TabBase}.
+     */
+    public void onDestroyed(TabBase tab);
+
+    /**
+     * Called when the tab content changes (to/from native pages or swapping native WebContents).
+     * @param tab The notifying {@link TabBase}.
+     */
+    public void onContentChanged(TabBase tab);
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java b/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java
index ee26e4c..4ea0c30 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java
@@ -127,8 +127,13 @@
      * @return {@link TemplateUrlService.TemplateUrl} for the default search engine.
      */
     public TemplateUrl getDefaultSearchEngineTemplateUrl() {
+        if (!isLoaded()) return null;
+
+        int defaultSearchEngineIndex = getDefaultSearchEngineIndex();
+        assert defaultSearchEngineIndex >= 0;
+
         return nativeGetPrepopulatedTemplateUrlAt(
-                mNativeTemplateUrlServiceAndroid, getDefaultSearchEngineIndex());
+                mNativeTemplateUrlServiceAndroid, defaultSearchEngineIndex);
     }
 
     public void setSearchEngine(int selectedIndex) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/StreamUtil.java b/chrome/android/java/src/org/chromium/chrome/browser/util/StreamUtil.java
new file mode 100644
index 0000000..5b30e2c
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/util/StreamUtil.java
@@ -0,0 +1,28 @@
+// 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.
+
+package org.chromium.chrome.browser.util;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * Helper methods to deal with stream related tasks.
+ */
+public class StreamUtil {
+    /**
+     * Handle closing a {@link java.io.Closeable} via {@link java.io.Closeable#close()} and catch
+     * the potentially thrown {@link java.io.IOException}.
+     * @param closeable The Closeable to be closed.
+     */
+    public static void closeQuietly(Closeable closeable) {
+        if (closeable == null) return;
+
+        try {
+            closeable.close();
+        } catch (IOException ex) {
+            // Ignore the exception on close.
+        }
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ShortcutHelperTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ShortcutHelperTest.java
index cf0afd5..43e0112 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ShortcutHelperTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ShortcutHelperTest.java
@@ -29,6 +29,13 @@
             + "<title>" + WEBAPP_TITLE + "</title>"
             + "</head><body>Webapp capable</body></html>");
 
+    private static final String SECOND_WEBAPP_TITLE = "Webapp shortcut #2";
+    private static final String SECOND_WEBAPP_HTML = UrlUtils.encodeHtmlDataUri(
+            "<html><head>"
+            + "<meta name=\"mobile-web-app-capable\" content=\"yes\" />"
+            + "<title>" + SECOND_WEBAPP_TITLE + "</title>"
+            + "</head><body>Webapp capable again</body></html>");
+
     private static final String NORMAL_TITLE = "Plain shortcut";
     private static final String NORMAL_HTML = UrlUtils.encodeHtmlDataUri(
             "<html>"
@@ -48,6 +55,10 @@
 
             return true;
         }
+
+        public void reset() {
+            firedIntent = null;
+        }
     }
 
     private ChromiumTestShellActivity mActivity;
@@ -69,10 +80,9 @@
 
     @MediumTest
     @Feature("{Webapp}")
-    public void testAddWebappShortcut() throws InterruptedException {
+    public void testAddWebappShortcuts() throws InterruptedException {
+        // Add a webapp shortcut and make sure the intent's parameters make sense.
         addShortcutToURL(WEBAPP_HTML);
-
-        // Make sure the intent's parameters make sense.
         Intent firedIntent = mTestObserver.firedIntent;
         assertEquals(WEBAPP_TITLE, firedIntent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME));
 
@@ -80,6 +90,18 @@
         assertEquals(WEBAPP_HTML, launchIntent.getStringExtra(ShortcutHelper.EXTRA_URL));
         assertEquals(WEBAPP_PACKAGE_NAME, launchIntent.getComponent().getPackageName());
         assertEquals(WEBAPP_CLASS_NAME, launchIntent.getComponent().getClassName());
+
+        // Add a second shortcut and make sure it matches the second webapp's parameters.
+        mTestObserver.reset();
+        addShortcutToURL(SECOND_WEBAPP_HTML);
+        Intent newFiredIntent = mTestObserver.firedIntent;
+        assertEquals(SECOND_WEBAPP_TITLE,
+                newFiredIntent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME));
+
+        Intent newLaunchIntent = newFiredIntent.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
+        assertEquals(SECOND_WEBAPP_HTML, newLaunchIntent.getStringExtra(ShortcutHelper.EXTRA_URL));
+        assertEquals(WEBAPP_PACKAGE_NAME, newLaunchIntent.getComponent().getPackageName());
+        assertEquals(WEBAPP_CLASS_NAME, newLaunchIntent.getComponent().getClassName());
     }
 
     @MediumTest
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/test/ModalDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/test/ModalDialogTest.java
index beb56fd..61165dc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/test/ModalDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/test/ModalDialogTest.java
@@ -229,8 +229,9 @@
      * Verifies that repeated dialogs give the option to disable dialogs
      * altogether and then that disabling them works.
      */
-    @MediumTest
-    @Feature({"Browser", "Main"})
+    //@MediumTest
+    //@Feature({"Browser", "Main"})
+    @DisabledTest //crbug/280693
     public void testDisableRepeatedDialogs()
             throws InterruptedException, TimeoutException, ExecutionException {
         OnEvaluateJavaScriptResultHelper scriptEvent =
diff --git a/chrome/android/testshell/java/src/org/chromium/chrome/testshell/TestShellTab.java b/chrome/android/testshell/java/src/org/chromium/chrome/testshell/TestShellTab.java
index 66b1995..0c4e2a3 100644
--- a/chrome/android/testshell/java/src/org/chromium/chrome/testshell/TestShellTab.java
+++ b/chrome/android/testshell/java/src/org/chromium/chrome/testshell/TestShellTab.java
@@ -7,9 +7,6 @@
 import android.content.Context;
 import android.text.TextUtils;
 
-import org.chromium.base.ObserverList;
-import org.chromium.chrome.browser.ChromeWebContentsDelegateAndroid;
-import org.chromium.chrome.browser.ContentViewUtil;
 import org.chromium.chrome.browser.TabBase;
 import org.chromium.content.browser.ContentView;
 import org.chromium.content.browser.LoadUrlParams;
@@ -21,11 +18,7 @@
  * and extends {@link TabBase}.
  */
 public class TestShellTab extends TabBase {
-    private ChromeWebContentsDelegateAndroid mWebContentsDelegate;
-    private ContentView mContentView;
     private int mNativeTestShellTab;
-    private final ObserverList<TestShellTabObserver> mObservers =
-        new ObserverList<TestShellTabObserver>();
 
     private CleanupReference mCleanupReference;
 
@@ -38,39 +31,24 @@
      * @param window   The WindowAndroid should represent this tab.
      */
     public TestShellTab(Context context, String url, WindowAndroid window) {
-        super(window);
-        init(context);
+        super(false, context, window);
+        initialize();
+        initContentView();
         loadUrlWithSanitization(url);
     }
 
-    /**
-     * @param context              The Context the view is running in.
-     */
-    private void init(Context context) {
-        // Build the WebContents and the ContentView/ContentViewCore
-        int nativeWebContentsPtr = ContentViewUtil.createNativeWebContents(false);
-        mContentView = ContentView.newInstance(context, nativeWebContentsPtr, getWindowAndroid());
-        mNativeTestShellTab = nativeInit(nativeWebContentsPtr,
-                getWindowAndroid().getNativePointer());
+    @Override
+    public void initialize() {
+        super.initialize();
 
-        // Build the WebContentsDelegate
-        mWebContentsDelegate = new TabBaseChromeWebContentsDelegateAndroid();
-        nativeInitWebContentsDelegate(mNativeTestShellTab, mWebContentsDelegate);
-
-        // To be called after everything is initialized.
-        mCleanupReference = new CleanupReference(this,
-                new DestroyRunnable(mNativeTestShellTab));
+        mNativeTestShellTab = nativeInit();
+        mCleanupReference = new CleanupReference(this, new DestroyRunnable(mNativeTestShellTab));
     }
 
-    /**
-     * Should be called when the tab is no longer needed.  Once this is called this tab should not
-     * be used.
-     */
+    @Override
     public void destroy() {
-        for (TestShellTabObserver observer : mObservers) {
-            observer.onCloseTab(TestShellTab.this);
-        }
-        destroyContentView();
+        super.destroy();
+
         if (mNativeTestShellTab != 0) {
             mCleanupReference.cleanupNow();
             mNativeTestShellTab = 0;
@@ -78,21 +56,6 @@
     }
 
     /**
-     * @param observer The {@link TestShellTabObserver} that should be notified of changes.
-     */
-    public void addObserver(TestShellTabObserver observer) {
-        mObservers.addObserver(observer);
-    }
-
-    /**
-     * @param observer The {@link TestShellTabObserver} that should no longer be notified of
-     * changes.
-     */
-    public void removeObserver(TestShellTabObserver observer) {
-        mObservers.removeObserver(observer);
-    }
-
-    /**
      * @return Whether or not the tab is currently loading.
      */
     public boolean isLoading() {
@@ -100,21 +63,6 @@
     }
 
     /**
-     * @return The {@link ContentView} represented by this tab.
-     */
-    public ContentView getContentView() {
-        return mContentView;
-    }
-
-
-    private void destroyContentView() {
-        if (mContentView == null) return;
-
-        mContentView.destroy();
-        mContentView = null;
-    }
-
-    /**
      * Navigates this Tab's {@link ContentView} to a sanitized version of {@code url}.
      * @param url The potentially unsanitized URL to navigate to.
      */
@@ -127,13 +75,19 @@
         // Invalid URLs will just return empty.
         if (TextUtils.isEmpty(url)) return;
 
-        if (TextUtils.equals(url, mContentView.getUrl())) {
-            mContentView.reload();
+        ContentView contentView = getContentView();
+        if (TextUtils.equals(url, contentView.getUrl())) {
+            contentView.reload();
         } else {
-            mContentView.loadUrl(new LoadUrlParams(url));
+            contentView.loadUrl(new LoadUrlParams(url));
         }
     }
 
+    @Override
+    protected TabBaseChromeWebContentsDelegateAndroid createWebContentsDelegate() {
+        return new TestShellTabBaseChromeWebContentsDelegateAndroid();
+    }
+
     private static final class DestroyRunnable implements Runnable {
         private final int mNativeTestShellTab;
         private DestroyRunnable(int nativeTestShellTab) {
@@ -145,22 +99,8 @@
         }
     }
 
-    private class TabBaseChromeWebContentsDelegateAndroid
-            extends ChromeWebContentsDelegateAndroid {
-        @Override
-        public void onLoadProgressChanged(int progress) {
-            for (TestShellTabObserver observer : mObservers) {
-                observer.onLoadProgressChanged(TestShellTab.this, progress);
-            }
-        }
-
-        @Override
-        public void onUpdateUrl(String url) {
-            for (TestShellTabObserver observer : mObservers) {
-                observer.onUpdateUrl(TestShellTab.this, url);
-            }
-        }
-
+    private class TestShellTabBaseChromeWebContentsDelegateAndroid
+            extends TabBaseChromeWebContentsDelegateAndroid {
         @Override
         public void onLoadStarted() {
             mIsLoading = true;
@@ -172,9 +112,7 @@
         }
     }
 
-    private native int nativeInit(int webContentsPtr, int windowAndroidPtr);
+    private native int nativeInit();
     private static native void nativeDestroy(int nativeTestShellTab);
-    private native void nativeInitWebContentsDelegate(int nativeTestShellTab,
-            ChromeWebContentsDelegateAndroid chromeWebContentsDelegateAndroid);
     private native String nativeFixupUrl(int nativeTestShellTab, String url);
 }
diff --git a/chrome/android/testshell/java/src/org/chromium/chrome/testshell/TestShellTabObserver.java b/chrome/android/testshell/java/src/org/chromium/chrome/testshell/TestShellTabObserver.java
deleted file mode 100644
index 4330557..0000000
--- a/chrome/android/testshell/java/src/org/chromium/chrome/testshell/TestShellTabObserver.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.testshell;
-
-/**
- * Observes changes to the {@link TestShellTab} class.  A {@link TestShellTabObserver} can observe
- * multiple {@link TestShellTab} objects, as the first parameter for each method is the specific
- * {@link TestShellTab} that is affected.  Additionally, a {@link TestShellTab} can have multiple
- * {@link TestShellTabObserver}s.
- *
- * Note that this interface does not expose control methods, only observation methods.
- */
-public interface TestShellTabObserver {
-    /**
-     * Called when the load progress of a {@link TestShellTab} has changed.
-     * @param tab      The notifying {@link TestShellTab}.
-     * @param progress The new progress amount (from 0 to 100).
-     */
-    public void onLoadProgressChanged(TestShellTab tab, int progress);
-
-    /**
-     * Called when the URL of a {@link TestShellTab} has changed.
-     * @param tab The notifying {@link TestShellTab}.
-     * @param url The new URL.
-     */
-    public void onUpdateUrl(TestShellTab tab, String url);
-
-    /**
-     * Called when the tab is about to close.
-     * @param tab The closing {@link TestShellTab}.
-     */
-    public void onCloseTab(TestShellTab tab);
-}
\ No newline at end of file
diff --git a/chrome/android/testshell/java/src/org/chromium/chrome/testshell/TestShellToolbar.java b/chrome/android/testshell/java/src/org/chromium/chrome/testshell/TestShellToolbar.java
index 3a3ad0c..168939b 100644
--- a/chrome/android/testshell/java/src/org/chromium/chrome/testshell/TestShellToolbar.java
+++ b/chrome/android/testshell/java/src/org/chromium/chrome/testshell/TestShellToolbar.java
@@ -19,6 +19,9 @@
 import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
 
+import org.chromium.chrome.browser.TabBase;
+import org.chromium.chrome.browser.TabObserver;
+import org.chromium.chrome.browser.EmptyTabObserver;
 import org.chromium.content.browser.LoadUrlParams;
 
 /**
@@ -41,7 +44,7 @@
     private ClipDrawable mProgressDrawable;
 
     private TestShellTab mTab;
-    private TestShellTabObserver mTabObserver = new TestShellTabObserverImpl();
+    private TabObserver mTabObserver = new TabObserverImpl();
     private MenuHandler mMenuHandler;
 
     /**
@@ -123,7 +126,7 @@
         mPrevButton.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View arg0) {
-                if (mTab.getContentView().canGoBack()) mTab.getContentView().goBack();
+                if (mTab.canGoBack()) mTab.goBack();
             }
         });
 
@@ -131,7 +134,7 @@
         mNextButton.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
-                if (mTab.getContentView().canGoForward()) mTab.getContentView().goForward();
+                if (mTab.canGoForward()) mTab.goForward();
             }
         });
     }
@@ -156,19 +159,15 @@
         }
     }
 
-    private class TestShellTabObserverImpl implements TestShellTabObserver {
+    private class TabObserverImpl extends EmptyTabObserver {
         @Override
-        public void onLoadProgressChanged(TestShellTab tab, int progress) {
+        public void onLoadProgressChanged(TabBase tab, int progress) {
             if (tab == mTab) TestShellToolbar.this.onLoadProgressChanged(progress);
         }
 
         @Override
-        public void onUpdateUrl(TestShellTab tab, String url) {
+        public void onUpdateUrl(TabBase tab, String url) {
             if (tab == mTab) TestShellToolbar.this.onUpdateUrl(url);
         }
-
-        @Override
-        public void onCloseTab(TestShellTab tab) {
-        }
     }
 }
diff --git a/chrome/android/testshell/javatests/src/org/chromium/chrome/testshell/ChromiumTestShellTestBase.java b/chrome/android/testshell/javatests/src/org/chromium/chrome/testshell/ChromiumTestShellTestBase.java
index fe5a941..c982a72 100644
--- a/chrome/android/testshell/javatests/src/org/chromium/chrome/testshell/ChromiumTestShellTestBase.java
+++ b/chrome/android/testshell/javatests/src/org/chromium/chrome/testshell/ChromiumTestShellTestBase.java
@@ -5,16 +5,15 @@
 package org.chromium.chrome.testshell;
 
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 import android.test.ActivityInstrumentationTestCase2;
 import android.text.TextUtils;
 
+import org.chromium.chrome.test.util.ApplicationData;
 import org.chromium.content.browser.test.util.Criteria;
 import org.chromium.content.browser.test.util.CriteriaHelper;
 
-import java.io.File;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -56,7 +55,7 @@
      * loading pages. Instead it should be used more for test initialization. The proper way
      * to wait is to use a TestCallbackHelperContainer after the initial load is completed.
      * @return Whether or not the Shell was actually finished loading.
-     * @throws Exception
+     * @throws InterruptedException
      */
     protected boolean waitForActiveShellToBeDoneLoading() throws InterruptedException {
         final ChromiumTestShellActivity activity = getActivity();
@@ -89,84 +88,17 @@
     }
 
     /**
-     * Clear all files in the testshell's directory except 'lib'.
+     * Clear all files and folders in the Chromium testshell's application directory except 'lib'.
+     *
+     * The 'cache' directory is recreated as an empty directory.
      *
      * @return Whether clearing the application data was successful.
      */
-    protected boolean clearAppData() throws Exception {
-        final int MAX_TIMEOUT_MS = 3000;
-        final String appDir = getAppDir();
-        return CriteriaHelper.pollForCriteria(
-                new Criteria() {
-                    private boolean mDataRemoved = false;
-                    @Override
-                    public boolean isSatisfied() {
-                        if (!mDataRemoved && !removeAppData(appDir)) {
-                            return false;
-                        }
-                        mDataRemoved = true;
-                        // We have to make sure the cache directory still exists, as the framework
-                        // will try to create it otherwise and will fail for sandbox processes with
-                        // an NPE.
-                        File cacheDir = new File(appDir, "cache");
-                        if (cacheDir.exists()) {
-                            return true;
-                        }
-                        return cacheDir.mkdir();
-                    }
-                },
-                MAX_TIMEOUT_MS, MAX_TIMEOUT_MS / 10);
+    protected boolean clearAppData() throws InterruptedException {
+        return ApplicationData.clearAppData(getInstrumentation().getTargetContext());
     }
 
     /**
-     * Remove all files and directories under the given appDir except 'lib'
-     *
-     * @param appDir the app directory to remove.
-     *
-     * @return true if succeeded.
-     */
-    private boolean removeAppData(String appDir) {
-        File[] files = new File(appDir).listFiles();
-        if (files == null) {
-            return true;
-        }
-        for (File file : files) {
-            if (!file.getAbsolutePath().endsWith("/lib") && !removeFile(file)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private String getAppDir() {
-        Context target_ctx = getInstrumentation().getTargetContext();
-        String cacheDir = target_ctx.getCacheDir().getAbsolutePath();
-        return cacheDir.substring(0, cacheDir.lastIndexOf('/'));
-    }
-
-    /**
-     * Remove the given file or directory.
-     *
-     * @param file the file or directory to remove.
-     *
-     * @return true if succeeded.
-     */
-    private static boolean removeFile(File file) {
-        if (file.isDirectory()) {
-            File[] files = file.listFiles();
-            if (files == null) {
-                return true;
-            }
-            for (File sub_file : files) {
-                if (!removeFile(sub_file))
-                    return false;
-            }
-        }
-        return file.delete();
-    }
-
-
-    /**
      * Navigates the currently active tab to a sanitized version of {@code url}.
      * @param url The potentially unsanitized URL to navigate to.
      */
diff --git a/chrome/android/testshell/javatests/src/org/chromium/chrome/testshell/TabShellTabUtils.java b/chrome/android/testshell/javatests/src/org/chromium/chrome/testshell/TabShellTabUtils.java
index c9bc381..26d2fe3 100644
--- a/chrome/android/testshell/javatests/src/org/chromium/chrome/testshell/TabShellTabUtils.java
+++ b/chrome/android/testshell/javatests/src/org/chromium/chrome/testshell/TabShellTabUtils.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.testshell;
 
+import org.chromium.chrome.browser.TabBase;
+import org.chromium.chrome.browser.TabObserver;
 import org.chromium.content.browser.ContentViewClient;
 import org.chromium.content.browser.test.util.CallbackHelper;
 import org.chromium.content.browser.test.util.TestCallbackHelperContainer;
@@ -25,7 +27,7 @@
     }
 
     public static class TestCallbackHelperContainerForTab
-            extends TestCallbackHelperContainer implements TestShellTabObserver {
+            extends TestCallbackHelperContainer implements TabObserver {
         private final OnCloseTabHelper mOnCloseTabHelper;
         public TestCallbackHelperContainerForTab(TestShellTab tab) {
             super(createTestContentViewClientForTab(tab),
@@ -42,17 +44,21 @@
         }
 
         @Override
-        public void onLoadProgressChanged(TestShellTab tab, int progress) {
+        public void onLoadProgressChanged(TabBase tab, int progress) {
         }
 
         @Override
-        public void onUpdateUrl(TestShellTab tab, String url) {
+        public void onUpdateUrl(TabBase tab, String url) {
         }
 
         @Override
-        public void onCloseTab(TestShellTab tab) {
+        public void onDestroyed(TabBase tab) {
             mOnCloseTabHelper.notifyCalled();
         }
+
+        @Override
+        public void onContentChanged(TabBase tab) {
+        }
     }
 
     /**
diff --git a/chrome/android/testshell/testshell_stubs.cc b/chrome/android/testshell/testshell_stubs.cc
index eeb71d7..aec1d7b 100644
--- a/chrome/android/testshell/testshell_stubs.cc
+++ b/chrome/android/testshell/testshell_stubs.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "base/strings/string16.h"
-#include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/infobars/confirm_infobar_delegate.h"
 #include "chrome/browser/translate/translate_infobar_delegate.h"
 #include "chrome/browser/ui/auto_login_infobar_delegate.h"
@@ -18,11 +17,6 @@
 
 class InfoBarService;
 
-// static
-TabAndroid* TabAndroid::FromWebContents(content::WebContents* web_contents) {
-  return NULL;
-}
-
 // AutoLoginInfoBarDelegatAndroid empty implementation for test_shell
 // TODO(miguelg) remove once the AutoLoginInfoBar is upstreamed.
 AutoLoginInfoBarDelegateAndroid::AutoLoginInfoBarDelegateAndroid(
diff --git a/chrome/android/testshell/testshell_tab.cc b/chrome/android/testshell/testshell_tab.cc
index 656bc31..d9101c8 100644
--- a/chrome/android/testshell/testshell_tab.cc
+++ b/chrome/android/testshell/testshell_tab.cc
@@ -8,6 +8,7 @@
 #include "base/logging.h"
 #include "chrome/browser/android/chrome_web_contents_delegate_android.h"
 #include "chrome/browser/ui/android/window_android_helper.h"
+#include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/common/net/url_fixer_upper.h"
 #include "content/public/browser/android/content_view_core.h"
 #include "content/public/browser/web_contents.h"
@@ -23,14 +24,8 @@
 using ui::WindowAndroid;
 
 TestShellTab::TestShellTab(JNIEnv* env,
-                           jobject obj,
-                           WebContents* web_contents,
-                           WindowAndroid* window_android)
-    : TabAndroid(env, obj),
-      web_contents_(web_contents) {
-  InitTabHelpers(web_contents);
-  WindowAndroidHelper::FromWebContents(web_contents)->
-      SetWindowAndroid(window_android);
+                           jobject obj)
+    : TabAndroid(env, obj) {
 }
 
 TestShellTab::~TestShellTab() {
@@ -40,10 +35,6 @@
   delete this;
 }
 
-WebContents* TestShellTab::GetWebContents() {
-  return web_contents_.get();
-}
-
 browser_sync::SyncedTabDelegate* TestShellTab::GetSyncedTabDelegate() {
   NOTIMPLEMENTED();
   return NULL;
@@ -97,17 +88,12 @@
   NOTIMPLEMENTED();
 }
 
-bool TestShellTab::RegisterTestShellTab(JNIEnv* env) {
-  return RegisterNativesImpl(env);
+void TestShellTab::HandlePopupNavigation(chrome::NavigateParams* params) {
+  NOTIMPLEMENTED();
 }
 
-void TestShellTab::InitWebContentsDelegate(
-    JNIEnv* env,
-    jobject obj,
-    jobject web_contents_delegate) {
-  web_contents_delegate_.reset(
-      new ChromeWebContentsDelegateAndroid(env, web_contents_delegate));
-  web_contents_->SetDelegate(web_contents_delegate_.get());
+bool TestShellTab::RegisterTestShellTab(JNIEnv* env) {
+  return RegisterNativesImpl(env);
 }
 
 ScopedJavaLocalRef<jstring> TestShellTab::FixupUrl(JNIEnv* env,
@@ -123,16 +109,8 @@
   return ConvertUTF8ToJavaString(env, fixed_spec);
 }
 
-static jint Init(JNIEnv* env,
-                 jobject obj,
-                 jint web_contents_ptr,
-                 jint window_android_ptr) {
-  TestShellTab* tab = new TestShellTab(
-      env,
-      obj,
-      reinterpret_cast<WebContents*>(web_contents_ptr),
-      reinterpret_cast<WindowAndroid*>(window_android_ptr));
-  return reinterpret_cast<jint>(tab);
+static jint Init(JNIEnv* env, jobject obj) {
+  return reinterpret_cast<jint>(new TestShellTab(env, obj));
 }
 
 int TestShellTab::GetSyncId() const {
diff --git a/chrome/android/testshell/testshell_tab.h b/chrome/android/testshell/testshell_tab.h
index bb06ad9..7bfde34 100644
--- a/chrome/android/testshell/testshell_tab.h
+++ b/chrome/android/testshell/testshell_tab.h
@@ -16,6 +16,10 @@
 }
 
 namespace chrome {
+struct NavigateParams;
+}
+
+namespace chrome {
 namespace android {
 class ChromeWebContentsDelegateAndroid;
 }
@@ -31,17 +35,12 @@
 
 class TestShellTab : public TabAndroid {
  public:
-  TestShellTab(JNIEnv* env,
-               jobject obj,
-               content::WebContents* web_contents,
-               ui::WindowAndroid* window_android);
+  TestShellTab(JNIEnv* env, jobject obj);
   void Destroy(JNIEnv* env, jobject obj);
 
   // --------------------------------------------------------------------------
   // TabAndroid Methods
   // --------------------------------------------------------------------------
-  virtual content::WebContents* GetWebContents() OVERRIDE;
-
   virtual browser_sync::SyncedTabDelegate* GetSyncedTabDelegate() OVERRIDE;
 
   virtual void OnReceivedHttpAuthRequest(jobject auth_handler,
@@ -68,7 +67,7 @@
   virtual void OnNewTabPageReady() OVERRIDE;
 
   virtual void RunExternalProtocolDialog(const GURL& url) OVERRIDE;
-
+  virtual void HandlePopupNavigation(chrome::NavigateParams* params) OVERRIDE;
   virtual int GetSyncId() const OVERRIDE;
   virtual void SetSyncId(int sync_id) OVERRIDE;
 
@@ -78,10 +77,6 @@
   // --------------------------------------------------------------------------
   // Methods called from Java via JNI
   // --------------------------------------------------------------------------
-  void InitWebContentsDelegate(JNIEnv* env,
-                               jobject obj,
-                               jobject web_contents_delegate);
-
   base::android::ScopedJavaLocalRef<jstring> FixupUrl(JNIEnv* env,
                                                       jobject obj,
                                                       jstring url);
@@ -90,10 +85,6 @@
   virtual ~TestShellTab();
 
  private:
-  scoped_ptr<content::WebContents> web_contents_;
-  scoped_ptr<chrome::android::ChromeWebContentsDelegateAndroid>
-          web_contents_delegate_;
-
   DISALLOW_COPY_AND_ASSIGN(TestShellTab);
 };
 
diff --git a/chrome/app/android/chrome_main_delegate_android.cc b/chrome/app/android/chrome_main_delegate_android.cc
index 81f4029..6b726c6 100644
--- a/chrome/app/android/chrome_main_delegate_android.cc
+++ b/chrome/app/android/chrome_main_delegate_android.cc
@@ -32,7 +32,13 @@
     JNIEnv* env = base::android::AttachCurrentThread();
     RegisterApplicationNativeMethods(env);
 
-    browser_runner_.reset(content::BrowserMainRunner::Create());
+    // Because the browser process can be started asynchronously as a series of
+    // of UI thread tasks a second request to start it can come in while the
+    // first request is still being processed. We must keep the same
+    // browser runner for the second request.
+    if (!browser_runner_.get()) {
+      browser_runner_.reset(content::BrowserMainRunner::Create());
+    }
     return browser_runner_->Initialize(main_function_params);
   }
 
diff --git a/chrome/app/breakpad_linux.cc b/chrome/app/breakpad_linux.cc
index 60e4fda..f2c127d 100644
--- a/chrome/app/breakpad_linux.cc
+++ b/chrome/app/breakpad_linux.cc
@@ -1152,11 +1152,6 @@
   //   abcdef \r\n
   //   BOUNDARY \r\n
   //
-  //   zero or more gpu entries:
-  //   Content-Disposition: form-data; name="gpu-xxxxx" \r\n \r\n
-  //   <gpu-xxxxx> \r\n
-  //   BOUNDARY \r\n
-  //
   //   zero or one:
   //   Content-Disposition: form-data; name="lsb-release" \r\n \r\n
   //   abcdef \r\n
@@ -1297,38 +1292,6 @@
     writer.Flush();
   }
 
-  // If GPU info is known, send it.
-  if (*child_process_logging::g_gpu_vendor_id) {
-#if !defined(OS_ANDROID)
-    static const char vendor_msg[] = "gpu-venid";
-    static const char device_msg[] = "gpu-devid";
-#endif
-    static const char gl_vendor_msg[] = "gpu-gl-vendor";
-    static const char gl_renderer_msg[] = "gpu-gl-renderer";
-    static const char driver_msg[] = "gpu-driver";
-    static const char psver_msg[] = "gpu-psver";
-    static const char vsver_msg[] = "gpu-vsver";
-
-#if !defined(OS_ANDROID)
-    writer.AddPairString(vendor_msg, child_process_logging::g_gpu_vendor_id);
-    writer.AddBoundary();
-    writer.AddPairString(device_msg, child_process_logging::g_gpu_device_id);
-    writer.AddBoundary();
-#endif
-    writer.AddPairString(gl_vendor_msg, child_process_logging::g_gpu_gl_vendor);
-    writer.AddBoundary();
-    writer.AddPairString(gl_renderer_msg,
-                         child_process_logging::g_gpu_gl_renderer);
-    writer.AddBoundary();
-    writer.AddPairString(driver_msg, child_process_logging::g_gpu_driver_ver);
-    writer.AddBoundary();
-    writer.AddPairString(psver_msg, child_process_logging::g_gpu_ps_ver);
-    writer.AddBoundary();
-    writer.AddPairString(vsver_msg, child_process_logging::g_gpu_vs_ver);
-    writer.AddBoundary();
-    writer.Flush();
-  }
-
   if (info.distro_length) {
     static const char distro_msg[] = "lsb-release";
     writer.AddPairString(distro_msg, info.distro);
diff --git a/chrome/app/breakpad_win.cc b/chrome/app/breakpad_win.cc
index 11c634b..9fc4531 100644
--- a/chrome/app/breakpad_win.cc
+++ b/chrome/app/breakpad_win.cc
@@ -13,6 +13,7 @@
 #include <algorithm>
 #include <vector>
 
+#include "base/basictypes.h"
 #include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/debug/crash_logging.h"
@@ -104,7 +105,6 @@
 static size_t g_num_of_extensions_offset = 0;
 static size_t g_extension_ids_offset = 0;
 static size_t g_client_id_offset = 0;
-static size_t g_gpu_info_offset = 0;
 static size_t g_printer_info_offset = 0;
 static size_t g_num_of_views_offset = 0;
 static size_t g_num_switches_offset = 0;
@@ -151,7 +151,7 @@
   // before consuming the signal (overwriting the signal with an identical one).
   // For now, we're willing to live with that risk.
   int length = swprintf(g_browser_crash_dump_value,
-                        sizeof(g_browser_crash_dump_value),
+                        arraysize(g_browser_crash_dump_value),
                         kBrowserCrashDumpValueFormatStr,
                         ::GetCurrentProcessId(),
                         ::GetTickCount());
@@ -450,21 +450,6 @@
         base::StringPrintf(L"extension-%i", i).c_str(), L""));
   }
 
-  // Add empty values for the gpu_info. We'll put the actual values when we
-  // collect them at this location.
-  g_gpu_info_offset = g_custom_entries->size();
-  static const wchar_t* const kGpuEntries[] = {
-    L"gpu-venid",
-    L"gpu-devid",
-    L"gpu-driver",
-    L"gpu-psver",
-    L"gpu-vsver",
-  };
-  for (size_t i = 0; i < arraysize(kGpuEntries); ++i) {
-    g_custom_entries->push_back(
-        google_breakpad::CustomInfoEntry(kGpuEntries[i], L""));
-  }
-
   // Add empty values for the prn_info-*. We'll put the actual values when we
   // collect them at this location.
   g_printer_info_offset = g_custom_entries->size();
@@ -686,28 +671,6 @@
                 google_breakpad::CustomInfoEntry::kValueMaxLength);
 }
 
-extern "C" void __declspec(dllexport) __cdecl SetGpuInfo(
-    const wchar_t* vendor_id, const wchar_t* device_id,
-    const wchar_t* driver_version, const wchar_t* pixel_shader_version,
-    const wchar_t* vertex_shader_version) {
-  if (!g_custom_entries)
-    return;
-
-  const wchar_t* info[] = {
-    vendor_id,
-    device_id,
-    driver_version,
-    pixel_shader_version,
-    vertex_shader_version
-  };
-
-  for (size_t i = 0; i < arraysize(info); ++i) {
-    base::wcslcpy((*g_custom_entries)[g_gpu_info_offset + i].value,
-                  info[i],
-                  google_breakpad::CustomInfoEntry::kValueMaxLength);
-  }
-}
-
 extern "C" void __declspec(dllexport) __cdecl SetPrinterInfo(
     const wchar_t* printer_info) {
   if (!g_custom_entries)
diff --git a/chrome/app/chrome_breakpad_client.cc b/chrome/app/chrome_breakpad_client.cc
index 2c0936e..9ac5e74 100644
--- a/chrome/app/chrome_breakpad_client.cc
+++ b/chrome/app/chrome_breakpad_client.cc
@@ -119,7 +119,8 @@
                                                    bool* is_rtl_locale) {
   scoped_ptr<base::Environment> env(base::Environment::Create());
   if (!env->HasVar(env_vars::kShowRestart) ||
-      !env->HasVar(env_vars::kRestartInfo)) {
+      !env->HasVar(env_vars::kRestartInfo) ||
+      env->HasVar(env_vars::kMetroConnected)) {
     return false;
   }
 
diff --git a/chrome/app/chrome_main.cc b/chrome/app/chrome_main.cc
index 425932e..e464788 100644
--- a/chrome/app/chrome_main.cc
+++ b/chrome/app/chrome_main.cc
@@ -7,6 +7,8 @@
 #include "content/public/app/content_main.h"
 
 #if defined(OS_WIN)
+#include "base/win/win_util.h"
+
 #define DLLEXPORT __declspec(dllexport)
 
 // We use extern C for the prototype DLLEXPORT to avoid C++ name mangling.
@@ -24,8 +26,13 @@
 #if defined(OS_WIN)
 DLLEXPORT int __cdecl ChromeMain(HINSTANCE instance,
                                  sandbox::SandboxInterfaceInfo* sandbox_info) {
+  // The process should crash when going through abnormal termination.
+  base::win::SetShouldCrashOnProcessDetach(true);
+  base::win::SetAbortBehaviorForCrashReporting();
   ChromeMainDelegate chrome_main_delegate;
-  return content::ContentMain(instance, sandbox_info, &chrome_main_delegate);
+  int rv = content::ContentMain(instance, sandbox_info, &chrome_main_delegate);
+  base::win::SetShouldCrashOnProcessDetach(false);
+  return rv;
 #elif defined(OS_POSIX)
 int ChromeMain(int argc, const char** argv) {
   ChromeMainDelegate chrome_main_delegate;
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index b041077..bdab589 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -8,6 +8,7 @@
 #include "base/files/file_path.h"
 #include "base/lazy_instance.h"
 #include "base/message_loop/message_loop.h"
+#include "base/metrics/statistics_recorder.h"
 #include "base/metrics/stats_counters.h"
 #include "base/path_service.h"
 #include "base/process/memory.h"
@@ -394,9 +395,8 @@
 // No support for ANDROID yet as DiagnosticsController needs wchar support.
 // TODO(gspencer): That's not true anymore, or at least there are no w-string
 // references anymore. Not sure if that means this can be enabled on Android or
-// not though: it still uses string16. As there is no easily accessible command
-// line on Android, I'm not sure this is a big deal, at least for purposes of
-// troubleshooting with a customer.
+// not though.  As there is no easily accessible command line on Android, I'm
+// not sure this is a big deal.
 #if !defined(OS_ANDROID) && !defined(CHROME_MULTIPLE_DLL_CHILD)
   // If we are in diagnostics mode this is the end of the line: after the
   // diagnostics are run the process will invariably exit.
@@ -430,12 +430,15 @@
   // original command line.
   if (command_line.HasSwitch(chromeos::switches::kLoginUser) ||
       command_line.HasSwitch(switches::kDiagnosticsRecovery)) {
+
+    // The statistics subsystem needs get initialized soon enough for the
+    // statistics to be collected.  It's safe to call this more than once.
+    base::StatisticsRecorder::Initialize();
+
     CommandLine interim_command_line(command_line.GetProgram());
-    if (command_line.HasSwitch(switches::kUserDataDir)) {
-      interim_command_line.AppendSwitchPath(
-          switches::kUserDataDir,
-          command_line.GetSwitchValuePath(switches::kUserDataDir));
-    }
+    const char* kSwitchNames[] = {switches::kUserDataDir, };
+    interim_command_line.CopySwitchesFrom(
+        command_line, kSwitchNames, arraysize(kSwitchNames));
     interim_command_line.AppendSwitch(switches::kDiagnostics);
     interim_command_line.AppendSwitch(switches::kDiagnosticsRecovery);
 
@@ -473,6 +476,8 @@
       *exit_code = recovery_exit_code;
       return true;
     }
+  } else {  // Not running diagnostics or recovery.
+    diagnostics::DiagnosticsController::GetInstance()->RecordRegularStartup();
   }
 #endif
 
@@ -756,6 +761,9 @@
 
 #if !defined(DISABLE_NACL) && !defined(CHROME_MULTIPLE_DLL_BROWSER)
     { switches::kNaClLoaderProcess,  NaClMain },
+#else
+    { "<invalid>", NULL },  // To avoid constant array of size 0
+                            // when DISABLE_NACL and CHROME_MULTIPLE_DLL_CHILD
 #endif  // DISABLE_NACL
   };
 
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index ca2fef6..121606f 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -855,11 +855,11 @@
       <!-- One click sign in infobar -->
       <if expr="not pp_ifdef('chromeos')">
         <!-- New one-click signin dialog contents for SAML support -->
-        <message name="IDS_ONE_CLICK_SIGNIN_DIALOG_TITLE_NEW" desc="The title of the modal dialog window that opens when the user chooses to use one click sign in.">
+        <message name="IDS_ONE_CLICK_SIGNIN_DIALOG_TITLE" desc="The title of the modal dialog window that opens when the user chooses to use one click sign in.">
           You're signed in to Chromium!
         </message>
         <message name="IDS_ONE_CLICK_SIGNIN_DIALOG_MESSAGE_NEW" desc="The message of the one click sign in dialog.">
-          You're signed in as <ph name="USER_EMAIL_ADDRESS">$1<ex>foo@gmail.com</ex></ph>. Now you can access your bookmarks, history, and other settings on all your signed in devices.
+          You're signing in to Chromium using your <ph name="USER_EMAIL_ADDRESS">$1<ex>foo@gmail.com</ex></ph> account. You will be able to access your bookmarks, history, and other settings on all of your signed in devices.
         </message>
         <message name="IDS_ONE_CLICK_SIGNIN_BUBBLE_MESSAGE" desc="The body of the sync promo NTP bubble.">
           You're now signed in to Chromium! Your bookmarks, history, and other settings will be synced to your Google Account.
diff --git a/chrome/app/client_util.cc b/chrome/app/client_util.cc
index 0d1c52c..4aa2336 100644
--- a/chrome/app/client_util.cc
+++ b/chrome/app/client_util.cc
@@ -58,7 +58,12 @@
   // performance loss.
   if (!cmd_line.HasSwitch(switches::kProcessType)) {
     const size_t kStepSize = 1024 * 1024;
-    uint8 percent = base::win::GetVersion() > base::win::VERSION_XP ? 25 : 0;
+    // Pre-read 100% of the binary. This was the fallback behaviour for an
+    // expired pre-read experiment as well as the standard behaviour before
+    // the pre-read experiment. Performance regressed when the experiment
+    // (and fallback to 100%) was removed. See http://crbug/245435
+    // TODO(rogerm): Revive the pre-read experiment under finch.
+    uint8 percent = 100;
     ImagePreReader::PartialPreReadImage(dir->c_str(), percent, kStepSize);
   }
 #endif
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 43b728b..fb9233a 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -3533,6 +3533,9 @@
       <message name="IDS_TASK_MANAGER_TAB_PREFIX" desc="The prefix for a Task Manager Tab row">
         Tab: <ph name="TAB_NAME">$1<ex>Google</ex></ph>
       </message>
+      <message name="IDS_TASK_MANAGER_TAB_INCOGNITO_PREFIX" desc="The prefix for a Task Manager incognito Tab row (may not be visible if incognito is not open)">
+        Incognito Tab: <ph name="TAB_NAME">$1<ex>Google</ex></ph>
+      </message>
       <message name="IDS_TASK_MANAGER_BACKGROUND_PREFIX" desc="The prefix for a Task Manager background page row">
         Background Page: <ph name="BACKGROUND_PAGE_URL">$1<ex>http://www.google.com</ex></ph>
       </message>
@@ -4147,6 +4150,9 @@
       <message name="IDS_EXTENSION_PROMPT_WARNING_DOWNLOADS_OPEN" desc="Permission string for access to downloads.">
         Open downloaded files
       </message>
+      <message name="IDS_EXTENSION_PROMPT_WARNING_FILE_SYSTEM_DIRECTORY" desc="Permission string for access to directories and their contents.">
+        Access folders that you open, and all files and folders those folders contain.
+      </message>
       <message name="IDS_EXTENSION_PROMPT_WARNING_FILE_SYSTEM_WRITE" desc="Permission string for write access to the file system.">
         Write to files that you have opened in the application
       </message>
@@ -4524,6 +4530,15 @@
       <message name="IDS_APPS_DEVTOOL_DELETE_CONFIRMATION_MESSAGE_EXTENSION" desc="Text of the message of the apps/extension delete confirmation dialog in case the user is deleting an extension.">
         Do you really want to delete this extension?
       </message>
+      <message name="IDS_APPS_DEVTOOL_MANAGED_PROFILE_DIALOG_CLOSE_BUTTON" desc="Title of the button to close the dialog for managed profile.">
+        Close
+      </message>
+      <message name="IDS_APPS_DEVTOOL_MANAGED_PROFILE_DIALOG_TITLE" desc="Title of the dialog for managed profile.">
+        This user is supervised.
+      </message>
+      <message name="IDS_APPS_DEVTOOL_MANAGED_PROFILE_DIALOG_DESCRIPTION" desc="Content of the dialog for managed profile. It's informing a supervised user that extensions cannot be changed.">
+        Applications and extensions cannot be modified by supervised users. Apps Developer Tools will be closed.
+      </message>
       <message name="IDS_EXTENSIONS_NONE_INSTALLED_SUGGEST_GALLERY" desc="Text on next line after IDS_EXTENSIONS_NONE_INSTALLED that suggests the user look in the gallery for extensions to install.">
         Want to <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>browse the gallery<ph name="END_LINK">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph> instead?
       </message>
@@ -4584,9 +4599,6 @@
       <message name="IDS_EXTENSIONS_LAUNCH" desc="The link for launching apps.">
         Launch
       </message>
-      <message name="IDS_EXTENSIONS_RESTART" desc="The link for restarting apps.">
-        Restart
-      </message>
       <if expr="is_macosx">
         <message name="IDS_EXTENSIONS_RELOAD_UNPACKED" desc="The link for reloading unpacked extensions.">
           Reload (⌘R)
@@ -4656,7 +4668,7 @@
         (This extension is managed and cannot be removed or disabled.)
       </message>
       <message name="IDS_EXTENSIONS_LOCKED_MANAGED_USER" desc="The error message (either shown in the extensions UI or logged) informing a supervised user that extensions cannot be changed.">
-        Extensions cannot be modified by supervised users.
+        Applications and extensions cannot be modified by supervised users.
       </message>
       <message name="IDS_EXTENSIONS_USE_APPS_DEV_TOOLS" desc="The message in the banner to invite people to use Apps Developer Tools.">
         The new Apps Developer Tools makes it easier than ever to manage and debug your apps and extensions. It's now available in the Chrome App Launcher.
@@ -4870,6 +4882,9 @@
       <message name="IDS_EXTENSIONS_DELETE" desc="Text for the link to delete the extension / app.">
         Delete
       </message>
+      <message name="IDS_EXTENSIONS_ADT_DELETE" desc="Text for the link to uninstall the extension / app.">
+        Uninstall
+      </message>
       <message name="IDS_EXTENSIONS_PACK" desc="Text for the link to pack the extension / app.">
         Pack
       </message>
@@ -5312,6 +5327,12 @@
         <message name="IDS_FLAGS_ALTERNATE_SHELF_LAYOUT_DESCRIPTION">
           Use the alternative shelf layout.
         </message>
+        <message name="IDS_FLAGS_DRAG_OFF_SHELF_NAME">
+          Dragging off items from shelf to unpin
+        </message>
+        <message name="IDS_FLAGS_DRAG_OFF_SHELF_DESCRIPTION">
+          Enable to allow items to be dragged off the shelf to unpin them.
+        </message>
         <message name="IDS_FLAGS_RELAUNCH_BUTTON" desc="Text on a button that restarts Chrome OS when clicked. ">
           Restart Now
         </message>
@@ -5371,11 +5392,11 @@
       <message name="IDS_NACL_DEBUG_MASK_CHOICE_EXCLUDE_UTILS">
         Debug everything except secure shell
       </message>
-      <message name="IDS_FLAGS_PNACL_NAME" desc="Name of the 'Portable Native Client' lab.">
-        Portable Native Client.
+      <message name="IDS_FLAGS_DISABLE_PNACL_NAME" desc="Name of the 'Disable Portable Native Client' lab.">
+        Disable Portable Native Client.
       </message>
-      <message name="IDS_FLAGS_PNACL_DESCRIPTION" desc="Description of the 'Portable Native Client' lab.">
-        Enable support for Portable Native Client.
+      <message name="IDS_FLAGS_DISABLE_PNACL_DESCRIPTION" desc="Description of the 'Disable Portable Native Client' lab.">
+        Disable support for Portable Native Client (PNaCl).
       </message>
       <message name="IDS_FLAGS_SAVE_PAGE_AS_MHTML_NAME" desc="Name of the 'Save Page as MHTML' lab.">
         Save Page as MHTML
@@ -6365,6 +6386,12 @@
       <message name="IDS_FLAGS_DELEGATED_RENDERER_DESCRIPTION" desc="Description of about:flags option for delegated renderer.">
         If enabled, the renderer delegates compositing to the browser, merging both compositing passes.
       </message>
+      <message name="IDS_FLAGS_MAP_IMAGE_NAME" desc="Name of about:flags option for map-image rasterizer.">
+        Map-image rasterizer (AKA Zero-copy)
+      </message>
+      <message name="IDS_FLAGS_MAP_IMAGE_DESCRIPTION" desc="Description of about:flags option for map-image rasterizer.">
+        If enabled, raster threads write directly to GPU memory.
+      </message>
       <message name="IDS_FLAGS_TRACK_ACTIVE_VISIT_TIME_NAME" desc="Name of about:flags option for tracking active time on a page.">
         Track time spent on each each page
       </message>
@@ -9257,8 +9284,8 @@
       <message name="IDS_OPTIONS_LANGUAGES_ADD_SELECT_LABEL" desc="The label for the add language select control.">
         Language:
       </message>
-      <message name="IDS_OPTIONS_LANGUAGES_DONT_TRANSLATE_IN_THIS_LANGUAGE" desc="The label for a checkbox which indicates whether or not the language should be translated.">
-        Don't translate pages in this language
+      <message name="IDS_OPTIONS_LANGUAGES_OFFER_TO_TRANSLATE_IN_THIS_LANGUAGE" desc="The label for a checkbox which indicates whether or not the language should be translated.">
+        Offer to translate pages in this language
       </message>
       <message name="IDS_OPTIONS_LANGUAGES_CANNOT_TRANSLATE_IN_THIS_LANGUAGE" desc="The string displayed to user when the language cannot be translated.">
         This language cannot be translated
@@ -10116,7 +10143,7 @@
         Google Wallet is protecting your card
       </message>
       <message name="IDS_AUTOFILL_GENERATED_CREDIT_CARD_BUBBLE_CONTENTS" desc="Text in the bubble explaining that a temporary card has been generated for the user's security by Google Wallet.">
-        Don't worry, this purchase will still be charged to your |$1|. Your card has been protected with the Google Wallet Virtual Online Card (|$2|), this number will appear on your receipt.
+        Your card has been protected with the Google Wallet Virtual Online Card (<ph name="FRONTING_CREDIT_CARD">|$1|<ex>Visa - 1234</ex></ph>) and this number will appear on your receipt. This purchase will still be charged to your <ph name="BACKING_CREDIT_CARD">|$2|<ex>Visa - 5678</ex></ph>.
       </message>
       <message name="IDS_AUTOFILL_NEW_CREDIT_CARD_BUBBLE_LINK" desc="The text of a link at the bottom of the autofill dialog bubble that sends users to a Google Wallet billing/card management page.">
         Manage Saved Cards
@@ -10889,13 +10916,13 @@
         <message name="IDS_NEW_TAB_CONTEXT_MENU_REMOVE_ALL" desc="Context menu description for removing all items from a list">
           Remove all
         </message>
-        <message name="IDS_NEW_TAB_CONTEXT_MENU_EDIT_BOOKMARK" desc="Context menu description for editing the selected bookmark">
+        <message name="IDS_NEW_TAB_CONTEXT_MENU_EDIT_BOOKMARK" desc="Context menu description for editing the selected bookmark" formatter_data="android_java">
           Edit bookmark
         </message>
-        <message name="IDS_NEW_TAB_CONTEXT_MENU_DELETE_BOOKMARK" desc="Context menu description for deleting the selected bookmark">
+        <message name="IDS_NEW_TAB_CONTEXT_MENU_DELETE_BOOKMARK" desc="Context menu description for deleting the selected bookmark" formatter_data="android_java">
           Delete bookmark
         </message>
-        <message name="IDS_NEW_TAB_CONTEXT_MENU_BOOKMARK_SHORTCUT" desc="Context menu description for creating a shortcut to bookmark on home screen">
+        <message name="IDS_NEW_TAB_CONTEXT_MENU_BOOKMARK_SHORTCUT" desc="Context menu description for creating a shortcut to bookmark on home screen" formatter_data="android_java">
           Add to home screen
         </message>
         <message name="IDS_SYNC_PROMO_DESKTOP_INSTRUCTIONS" desc="Information about sync displayed on the NTP when the user has signed in on mobile but not on desktop">
@@ -12202,6 +12229,16 @@
         Formatting the removable media is going to erase all data. Do you wish to continue?
       </message>
 
+      <message name="IDS_FILE_BROWSER_SUGGEST_DIALOG_TITLE" desc="Title of the suggest app dialog, which shows the list of the apps which supports the selected file.">
+        Select an app to open this file.
+      </message>
+      <message name="IDS_FILE_BROWSER_SUGGEST_DIALOG_LINK_TO_WEBSTORE" desc="Text of the link to the app list on Chrome Webstore, which shows the more apps than in the suggest app dialog.">
+        See more...
+      </message>
+      <message name="IDS_FILE_BROWSER_SUGGEST_DIALOG_INSTALLATION_FAILED" desc="Error message when the installation of the app from WebStore is failed.">
+        Installation failed.
+      </message>
+
       <message name="IDS_FILE_BROWSER_SELECT_FOLDER_TITLE" desc="Select folder title.">
         Select a folder to open
       </message>
@@ -14922,53 +14959,21 @@
       <message name="IDS_NETWORK_LOG_LEVEL_DEBUG" desc="Debug logging level checkbox">
         Debug
       </message>
+      <message name="IDS_NETWORK_LOG_LEVEL_FILEINFO" desc="Debug logging level checkbox">
+        File Info
+      </message>
       <message name="IDS_NETWORK_LOG_ENTRY" desc="The log entry displayed in network event log table.">
         [<ph name="TIMESTAMP">$1<ex>Timestamp</ex></ph>]
-        <ph name="EVENT_NAME">$2<ex>Event Name</ex></ph>
-        <ph name="DESCRIPTION">$3<ex>Description</ex></ph>
+        <ph name="FILE_INFO">$2<ex>file:123</ex></ph>
+        <ph name="EVENT_NAME">$3<ex>Event Name</ex></ph>
+        <ph name="DESCRIPTION">$4<ex>Description</ex></ph>
       </message>
     </if>
 
     <!-- Local Device Discovery display strings -->
-    <message name="IDS_LOCAL_DISCOVERY_SERVICE_NAME" desc="Table header for service name">
-      Name
-    </message>
-    <message name="IDS_LOCAL_DISCOVERY_SERVICE_DOMAIN" desc="Table header for service domain">
-      Domain
-    </message>
-    <message name="IDS_LOCAL_DISCOVERY_SERVICE_PORT" desc="Table header for service port">
-      Port
-    </message>
-    <message name="IDS_LOCAL_DISCOVERY_SERVICE_IP" desc="Table header for IP">
-      IP
-    </message>
-    <message name="IDS_LOCAL_DISCOVERY_SERVICE_LASTSEEN" desc="Table header for when a service was last seen">
-      Last seen
-    </message>
     <message name="IDS_LOCAL_DISCOVERY_SERVICE_REGISTER" desc="Table header for registering a service">
       Register
     </message>
-    <message name="IDS_LOCAL_DISCOVERY_REGISTERING_SERVICE" desc="Message to indicate a service is being registered">
-      Registering <ph name="DEVICE_NAME">$1</ph>...
-    </message>
-    <message name="IDS_LOCAL_DISCOVERY_REGISTRATION_FAILED" desc="Message to indicate service registration has failed">
-      Registration failed: <ph name="ERROR_STRING">$1</ph>.
-    </message>
-    <message name="IDS_LOCAL_DISCOVERY_REGISTRATION_SUCCEEDED" desc="Message to indicate service registration has succeeded">
-      Successfully registered device with ID <ph name="DEVICE_ID">$1</ph>.
-    </message>
-    <message name="IDS_LOCAL_DISCOVERY_REGISTERED" desc="Message to show that a listed service has already been registered">
-      Registered
-    </message>
-    <message name="IDS_LOCAL_DISCOVERY_INFO_STARTED" desc="Message to show that a listed service is being asked for info">
-      Info request started for <ph name="DEVICE_NAME">$1</ph>...
-    </message>
-    <message name="IDS_LOCAL_DISCOVERY_INFO_FAILED" desc="Message to show that an info request has failed">
-      Info request failed: <ph name="ERROR_STRING">$1</ph>...
-    </message>
-    <message name="IDS_LOCAL_DISCOVERY_SERVICE_INFO" desc="Table header and button for info requests">
-      Info
-    </message>
     <message name="IDS_LOCAL_DISOCVERY_NOTIFICATION_TITLE_PRINTER" desc="Title of notification for a new printer showing up on your network">
       New printer on your network
     </message>
@@ -14978,6 +14983,42 @@
     <message name="IDS_LOCAL_DISOCVERY_NOTIFICATION_DISPLAY_SOURCE_PRINTER" desc="Display name for notification for a new printer showing up on your network">
       Google Cloud Print
     </message>
+    <message name="IDS_LOCAL_DISOCVERY_NOTIFICATION_BUTTON_PRINTER" desc="Message on registration button for printer">
+      Add to Cloud Print
+    </message>
+    <message name="IDS_LOCAL_DISCOVERY_DEVICES_PAGE_TITLE" desc="Title for devices page">
+      Devices
+    </message>
+    <message name="IDS_LOCAL_DISCOVERY_REGISTER_CONFIRMATION" desc="Confirmation for registering a printer to Google Cloud Print">
+      Do you want to add <ph name="PRINTER_NAME">$1</ph> to Google Cloud Print?
+    </message>
+    <message name="IDS_LOCAL_DISCOVERY_REGISTER_USER" desc="Label for user picker in registration dialog">
+      User:
+    </message>
+    <message name="IDS_LOCAL_DISCOVERY_CONFIRM_REGISTRATION" desc="Title for 'confirm registration' page">
+      Confirm registration
+    </message>
+    <message name="IDS_LOCAL_DISCOVERY_ADDING_PRINTER" desc="Title for 'adding printer' page">
+      Adding printer...
+    </message>
+    <message name="IDS_LOCAL_DISCOVERY_ERROR_OCURRED" desc="Title for error page">
+      An error has occured
+    </message>
+    <message name="IDS_LOCAL_DISCOVERY_ADDING_PRINTER_MESSAGE1" desc="Message for adding printer number 1">
+      You must confirm registration on your printer to finish this process - check it now.
+    </message>
+     <message name="IDS_LOCAL_DISCOVERY_ADDING_PRINTER_MESSAGE2" desc="Message for adding printer number 2">
+      Adding the printer to your account - this may take a moment...
+    </message>
+    <message name="IDS_LOCAL_DISCOVERY_ERROR_OCURRED_MESSAGE" desc="Message for error page">
+      An error has occured. Please check your printer and try again.
+    </message>
+    <message name="IDS_LOCAL_DISCOVERY_REGISTERED_DEVICES_TITLE" desc="Title for registered devices">
+      Registered devices
+    </message>
+    <message name="IDS_LOCAL_DISCOVERY_UNREGISTERED_DEVICES_TITLE" desc="Title for unregistered devices">
+      Unregistered devices
+    </message>
   </messages>
 
   <structures fallback_to_english="true">
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 114568e..ec0a145 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -779,11 +779,11 @@
       <!-- One click sign in infobar -->
       <if expr="not pp_ifdef('chromeos')">
         <!-- New one-click signin dialog contents for SAML support -->
-        <message name="IDS_ONE_CLICK_SIGNIN_DIALOG_TITLE_NEW" desc="The title of the modal dialog window that opens when the user chooses to use one click sign in.">
+        <message name="IDS_ONE_CLICK_SIGNIN_DIALOG_TITLE" desc="The title of the modal dialog window that opens when the user chooses to use one click sign in.">
           You're signed in to Chrome!
         </message>
         <message name="IDS_ONE_CLICK_SIGNIN_DIALOG_MESSAGE_NEW" desc="The message of the one click sign in dialog.">
-          You're signed in as <ph name="USER_EMAIL_ADDRESS">$1<ex>foo@gmail.com</ex></ph>. Now you can access your bookmarks, history, and other settings on all your signed in devices.
+          You're signing in to Chrome using your <ph name="USER_EMAIL_ADDRESS">$1<ex>foo@gmail.com</ex></ph> account. You will be able to access your bookmarks, history, and other settings on all of your signed in devices.
         </message>
         <message name="IDS_ONE_CLICK_SIGNIN_BUBBLE_MESSAGE" desc="The body of the sync promo NTP bubble.">
           You're now signed in to Chrome! Your bookmarks, history, and other settings will be synced to your Google Account.
diff --git a/chrome/app/nibs/InfoBarContainer.xib b/chrome/app/nibs/InfoBarContainer.xib
deleted file mode 100644
index cdd5bbc..0000000
--- a/chrome/app/nibs/InfoBarContainer.xib
+++ /dev/null
@@ -1,169 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
-	<data>
-		<int key="IBDocument.SystemTarget">1050</int>
-		<string key="IBDocument.SystemVersion">12B19</string>
-		<string key="IBDocument.InterfaceBuilderVersion">2549</string>
-		<string key="IBDocument.AppKitVersion">1187</string>
-		<string key="IBDocument.HIToolboxVersion">624.00</string>
-		<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
-			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
-			<string key="NS.object.0">2549</string>
-		</object>
-		<object class="NSArray" key="IBDocument.IntegratedClassDependencies">
-			<bool key="EncodedWithXMLCoder">YES</bool>
-			<string>NSCustomObject</string>
-			<string>NSCustomView</string>
-		</object>
-		<object class="NSArray" key="IBDocument.PluginDependencies">
-			<bool key="EncodedWithXMLCoder">YES</bool>
-			<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
-		</object>
-		<object class="NSMutableDictionary" key="IBDocument.Metadata">
-			<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
-			<integer value="1" key="NS.object.0"/>
-		</object>
-		<object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
-			<bool key="EncodedWithXMLCoder">YES</bool>
-			<object class="NSCustomObject" id="1001">
-				<string key="NSClassName">InfoBarContainerController</string>
-			</object>
-			<object class="NSCustomObject" id="1003">
-				<string key="NSClassName">FirstResponder</string>
-			</object>
-			<object class="NSCustomObject" id="1004">
-				<string key="NSClassName">NSApplication</string>
-			</object>
-			<object class="NSCustomView" id="1005">
-				<reference key="NSNextResponder"/>
-				<int key="NSvFlags">266</int>
-				<string key="NSFrameSize">{480, 0}</string>
-				<reference key="NSSuperview"/>
-				<reference key="NSWindow"/>
-				<string key="NSClassName">NSView</string>
-			</object>
-		</object>
-		<object class="IBObjectContainer" key="IBDocument.Objects">
-			<object class="NSMutableArray" key="connectionRecords">
-				<bool key="EncodedWithXMLCoder">YES</bool>
-				<object class="IBConnectionRecord">
-					<object class="IBOutletConnection" key="connection">
-						<string key="label">view</string>
-						<reference key="source" ref="1001"/>
-						<reference key="destination" ref="1005"/>
-					</object>
-					<int key="connectionID">12</int>
-				</object>
-			</object>
-			<object class="IBMutableOrderedSet" key="objectRecords">
-				<object class="NSArray" key="orderedObjects">
-					<bool key="EncodedWithXMLCoder">YES</bool>
-					<object class="IBObjectRecord">
-						<int key="objectID">0</int>
-						<object class="NSArray" key="object" id="0">
-							<bool key="EncodedWithXMLCoder">YES</bool>
-						</object>
-						<reference key="children" ref="1000"/>
-						<nil key="parent"/>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">-2</int>
-						<reference key="object" ref="1001"/>
-						<reference key="parent" ref="0"/>
-						<string key="objectName">File's Owner</string>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">-1</int>
-						<reference key="object" ref="1003"/>
-						<reference key="parent" ref="0"/>
-						<string key="objectName">First Responder</string>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">-3</int>
-						<reference key="object" ref="1004"/>
-						<reference key="parent" ref="0"/>
-						<string key="objectName">Application</string>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">1</int>
-						<reference key="object" ref="1005"/>
-						<object class="NSMutableArray" key="children">
-							<bool key="EncodedWithXMLCoder">YES</bool>
-						</object>
-						<reference key="parent" ref="0"/>
-					</object>
-				</object>
-			</object>
-			<object class="NSMutableDictionary" key="flattenedProperties">
-				<bool key="EncodedWithXMLCoder">YES</bool>
-				<object class="NSArray" key="dict.sortedKeys">
-					<bool key="EncodedWithXMLCoder">YES</bool>
-					<string>-1.IBPluginDependency</string>
-					<string>-2.IBPluginDependency</string>
-					<string>-3.IBPluginDependency</string>
-					<string>1.IBPluginDependency</string>
-				</object>
-				<object class="NSArray" key="dict.values">
-					<bool key="EncodedWithXMLCoder">YES</bool>
-					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
-					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
-					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
-					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
-				</object>
-			</object>
-			<object class="NSMutableDictionary" key="unlocalizedProperties">
-				<bool key="EncodedWithXMLCoder">YES</bool>
-				<reference key="dict.sortedKeys" ref="0"/>
-				<reference key="dict.values" ref="0"/>
-			</object>
-			<nil key="activeLocalization"/>
-			<object class="NSMutableDictionary" key="localizations">
-				<bool key="EncodedWithXMLCoder">YES</bool>
-				<reference key="dict.sortedKeys" ref="0"/>
-				<reference key="dict.values" ref="0"/>
-			</object>
-			<nil key="sourceID"/>
-			<int key="maxID">19</int>
-		</object>
-		<object class="IBClassDescriber" key="IBDocument.Classes">
-			<object class="NSMutableArray" key="referencedPartialClassDescriptions">
-				<bool key="EncodedWithXMLCoder">YES</bool>
-				<object class="IBPartialClassDescription">
-					<string key="className">InfoBarContainerController</string>
-					<string key="superclassName">NSViewController</string>
-					<object class="NSMutableDictionary" key="outlets">
-						<string key="NS.key.0">resizeDelegate_</string>
-						<string key="NS.object.0">id</string>
-					</object>
-					<object class="NSMutableDictionary" key="toOneOutletInfosByName">
-						<string key="NS.key.0">resizeDelegate_</string>
-						<object class="IBToOneOutletInfo" key="NS.object.0">
-							<string key="name">resizeDelegate_</string>
-							<string key="candidateClassName">id</string>
-						</object>
-					</object>
-					<object class="IBClassDescriptionSource" key="sourceIdentifier">
-						<string key="majorKey">IBProjectSource</string>
-						<string key="minorKey">./Classes/InfoBarContainerController.h</string>
-					</object>
-				</object>
-			</object>
-		</object>
-		<int key="IBDocument.localizationMode">0</int>
-		<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
-		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
-			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
-			<integer value="1050" key="NS.object.0"/>
-		</object>
-		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
-			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
-			<real value="1070" key="NS.object.0"/>
-		</object>
-		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
-			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
-			<integer value="3000" key="NS.object.0"/>
-		</object>
-		<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
-		<int key="IBDocument.defaultPropertyAccessControl">3</int>
-	</data>
-</archive>
diff --git a/chrome/app/policy/policy_templates.json b/chrome/app/policy/policy_templates.json
index c4444fb..226f6f4 100644
--- a/chrome/app/policy/policy_templates.json
+++ b/chrome/app/policy/policy_templates.json
@@ -117,7 +117,7 @@
 #   persistent IDs for all fields (but not for groups!) are needed. These are
 #   specified by the 'id' keys of each policy. NEVER CHANGE EXISTING IDs,
 #   because doing so would break the deployed wire format!
-#   For your editing convenience: highest ID currently used: 239
+#   For your editing convenience: highest ID currently used: 240
 #
 # Placeholders:
 #   The following placeholder strings are automatically substituted:
@@ -4243,6 +4243,28 @@
       The policy value should be specified in milliseconds. Values are clamped to a range of 30 seconds to 24 hours.''',
     },
     {
+      'name': 'FullscreenAllowed',
+      'type': 'main',
+      'schema': { 'type': 'boolean' },
+      'supported_on': ['chrome.win:31-', 'chrome.linux:31-', 'chrome_os:31-'],
+      'features': {
+        'dynamic_refresh': True,
+        'per_profile': True,
+      },
+      'example_value': True,
+      'id': 240,
+      'caption': '''Allow fullscreen mode''',
+      'desc': '''Allow fullscreen mode.
+
+      This policy controls the availability of fullscreen mode in which all <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> UI is hidden and only web content is visible.
+
+      If this policy is set to true or not not configured, the user, apps and extensions with appropriate permissions can enter fullscreen mode.
+
+      If this policy is set to false, neither the user nor any apps or extensions can enter fullscreen mode.
+
+      On all platforms except <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph>, kiosk mode is unvailable when fullscreen mode is disabled.''',
+    },
+    {
       'name': 'PowerManagement',
       'type': 'group',
       'caption': '''Power mangement''',
diff --git a/chrome/app/theme/default_100_percent/common/cloudprint.png b/chrome/app/theme/default_100_percent/common/cloudprint.png
new file mode 100644
index 0000000..98c0de0
--- /dev/null
+++ b/chrome/app/theme/default_100_percent/common/cloudprint.png
Binary files differ
diff --git a/chrome/app/theme/default_100_percent/common/web_intent_progress_background.png b/chrome/app/theme/default_100_percent/common/web_intent_progress_background.png
deleted file mode 100644
index d92c9d2..0000000
--- a/chrome/app/theme/default_100_percent/common/web_intent_progress_background.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_100_percent/common/web_intent_progress_foreground.png b/chrome/app/theme/default_100_percent/common/web_intent_progress_foreground.png
deleted file mode 100644
index 8790326..0000000
--- a/chrome/app/theme/default_100_percent/common/web_intent_progress_foreground.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/cloudprint.png b/chrome/app/theme/default_200_percent/common/cloudprint.png
new file mode 100644
index 0000000..01c8e88
--- /dev/null
+++ b/chrome/app/theme/default_200_percent/common/cloudprint.png
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/web_intent_progress_background.png b/chrome/app/theme/default_200_percent/common/web_intent_progress_background.png
deleted file mode 100644
index fe01894..0000000
--- a/chrome/app/theme/default_200_percent/common/web_intent_progress_background.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/web_intent_progress_foreground.png b/chrome/app/theme/default_200_percent/common/web_intent_progress_foreground.png
deleted file mode 100644
index fd7a7c0..0000000
--- a/chrome/app/theme/default_200_percent/common/web_intent_progress_foreground.png
+++ /dev/null
Binary files differ
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index ed45386..eae1f83 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -100,7 +100,7 @@
       <structure type="chrome_scaled_image" name="IDR_BLOCKED_JAVASCRIPT" file="common/blocked_script.png" />
       <structure type="chrome_scaled_image" name="IDR_BLOCKED_LOCATION" file="common/blocked_location.png" />
       <structure type="chrome_scaled_image" name="IDR_BLOCKED_MEDIA" file="common/blocked_media.png" />
-      <structure type="chrome_scaled_image" name="IDR_SAVE_PASSWORD" file="common/infobar_savepassword.png" />
+      <structure type="chrome_scaled_image" name="IDR_SAVE_PASSWORD" file="common/save_password.png" />
       <structure type="chrome_scaled_image" name="IDR_BLOCKED_MICROPHONE" file="common/blocked_mic_only.png" />
       <structure type="chrome_scaled_image" name="IDR_BLOCKED_MIDI_SYSEX" file="common/blocked_midi.png" />
       <structure type="chrome_scaled_image" name="IDR_BLOCKED_MOUSE_CURSOR" file="common/blocked_mouse_cursor.png" />
@@ -423,6 +423,9 @@
       <structure type="chrome_scaled_image" name="IDR_KEYWORD_SEARCH_MAGNIFIER" file="keyword_search_magnifier.png" />
       <structure type="chrome_scaled_image" name="IDR_KILLED_TAB" file="killtab.png" />
       <structure type="chrome_scaled_image" name="IDR_LAPTOP_FAVICON" file="common/favicon_laptop.png" />
+      <if expr="pp_ifdef('enable_mdns')">
+        <structure type="chrome_scaled_image" name="IDR_LOCAL_DISCOVERY_CLOUDPRINT_ICON" file="common/cloudprint.png" />
+      </if>
       <structure type="chrome_scaled_image" name="IDR_LOCATION_BAR_HTTP" file="common/location_bar_http.png" />
       <if expr="pp_ifdef('chromeos')">
         <!-- TODO(nkostylev): This resource needs to be removed when cros login code is moved to ash. -->
@@ -970,8 +973,6 @@
       <structure type="chrome_scaled_image" name="IDR_WALLET_LOGO" file="common/wallet_logo.png" />
       <structure type="chrome_scaled_image" name="IDR_WALLET_STEP_CHECK" file="common/payment_checkmark.png" />
       <structure type="chrome_scaled_image" name="IDR_WARNING" file="alert_small.png" />
-      <structure type="chrome_scaled_image" name="IDR_WEB_INTENT_PROGRESS_BACKGROUND" file="common/web_intent_progress_background.png" />
-      <structure type="chrome_scaled_image" name="IDR_WEB_INTENT_PROGRESS_FOREGROUND" file="common/web_intent_progress_foreground.png" />
       <structure type="chrome_scaled_image" name="IDR_WEBSITE_SETTINGS_TABSTRIP_CENTER" file="website_settings_tabstrip_center.png" />
       <structure type="chrome_scaled_image" name="IDR_WEBSITE_SETTINGS_TABSTRIP_LEFT" file="website_settings_tabstrip_left.png" />
       <structure type="chrome_scaled_image" name="IDR_WEBSITE_SETTINGS_TABSTRIP_RIGHT" file="website_settings_tabstrip_right.png" />
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 59ecd61..83fd128 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -277,6 +277,14 @@
     switches::kTabCaptureDownscaleQuality, "best" },
 };
 
+const Experiment::Choice kMapImageChoices[] = {
+  { IDS_GENERIC_EXPERIMENT_CHOICE_DEFAULT, "", "" },
+  { IDS_GENERIC_EXPERIMENT_CHOICE_ENABLED,
+    cc::switches::kEnableMapImage, ""},
+  { IDS_GENERIC_EXPERIMENT_CHOICE_DISABLED,
+    cc::switches::kDisableMapImage, ""}
+};
+
 // RECORDING USER METRICS FOR FLAGS:
 // -----------------------------------------------------------------------------
 // The first line of the experiment is the internal name. If you'd like to
@@ -507,6 +515,13 @@
     SINGLE_VALUE_TYPE(switches::kEnableNaClDebug)
   },
   {
+    "disable-pnacl",  // FLAGS:RECORD_UMA
+    IDS_FLAGS_DISABLE_PNACL_NAME,
+    IDS_FLAGS_DISABLE_PNACL_DESCRIPTION,
+    kOsDesktop,
+    SINGLE_VALUE_TYPE(switches::kDisablePnacl)
+  },
+  {
     "nacl-debug-mask",  // FLAGS:RECORD_UMA
     IDS_FLAGS_NACL_DEBUG_MASK_NAME,
     IDS_FLAGS_NACL_DEBUG_MASK_DESCRIPTION,
@@ -514,13 +529,6 @@
     MULTI_VALUE_TYPE(kNaClDebugMaskChoices)
   },
   {
-    "disable-pnacl",  // FLAGS:RECORD_UMA
-    IDS_FLAGS_PNACL_NAME,
-    IDS_FLAGS_PNACL_DESCRIPTION,
-    kOsDesktop,
-    ENABLE_DISABLE_VALUE_TYPE("", switches::kDisablePnacl)
-  },
-  {
     "extension-apis",  // FLAGS:RECORD_UMA
     IDS_FLAGS_EXPERIMENTAL_EXTENSION_APIS_NAME,
     IDS_FLAGS_EXPERIMENTAL_EXTENSION_APIS_DESCRIPTION,
@@ -918,6 +926,13 @@
       SINGLE_VALUE_TYPE(ash::switches::kAshUseAlternateShelfLayout)
   },
   {
+      "ash-enable-drag-off-shelf",
+      IDS_FLAGS_DRAG_OFF_SHELF_NAME,
+      IDS_FLAGS_DRAG_OFF_SHELF_DESCRIPTION,
+      kOsCrOS,
+      SINGLE_VALUE_TYPE(ash::switches::kAshEnableDragOffShelf)
+  },
+  {
     "enable-background-loader",
     IDS_ENABLE_BACKLOADER_NAME,
     IDS_ENABLE_BACKLOADER_DESCRIPTION,
@@ -1562,13 +1577,6 @@
   },
 #endif
   {
-    "enable-translate-settings",
-    IDS_FLAGS_ENABLE_TRANSLATE_SETTINGS_NAME,
-    IDS_FLAGS_ENABLE_TRANSLATE_SETTINGS_DESCRIPTION,
-    kOsAll,
-    SINGLE_VALUE_TYPE(switches::kEnableTranslateSettings)
-  },
-  {
     "enable-web-midi",
     IDS_FLAGS_ENABLE_WEB_MIDI_NAME,
     IDS_FLAGS_ENABLE_WEB_MIDI_DESCRIPTION,
@@ -1621,6 +1629,20 @@
     kOsDesktop,
     SINGLE_VALUE_TYPE(switches::kEnableBatchedShutdown)
   },
+  {
+    "map-image",
+    IDS_FLAGS_MAP_IMAGE_NAME,
+    IDS_FLAGS_MAP_IMAGE_DESCRIPTION,
+    kOsAll,
+    MULTI_VALUE_TYPE(kMapImageChoices)
+  },
+  {
+    "enable-device-motion",
+    IDS_FLAGS_ENABLE_DEVICE_MOTION_NAME,
+    IDS_FLAGS_ENABLE_DEVICE_MOTION_DESCRIPTION,
+    kOsDesktop,
+    SINGLE_VALUE_TYPE(switches::kEnableDeviceMotion)
+  },
 };
 
 const Experiment* experiments = kExperiments;
@@ -1987,6 +2009,7 @@
     const std::pair<std::string, std::string>&
         switch_and_value_pair = name_to_switch_it->second;
 
+    CHECK(!switch_and_value_pair.first.empty());
     command_line->AppendSwitchASCII(switch_and_value_pair.first,
                                     switch_and_value_pair.second);
     flags_switches_[switch_and_value_pair.first] = switch_and_value_pair.second;
diff --git a/chrome/browser/android/bookmarks_bridge.cc b/chrome/browser/android/bookmarks_bridge.cc
index c957358..de47425 100644
--- a/chrome/browser/android/bookmarks_bridge.cc
+++ b/chrome/browser/android/bookmarks_bridge.cc
@@ -5,10 +5,12 @@
 #include "chrome/browser/android/bookmarks_bridge.h"
 
 #include "base/android/jni_string.h"
+#include "base/prefs/pref_service.h"
 #include "chrome/browser/bookmarks/bookmark_model.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
+#include "chrome/common/pref_names.h"
 #include "content/public/browser/browser_thread.h"
 #include "jni/BookmarksBridge_jni.h"
 
@@ -25,11 +27,12 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile);
   bookmark_model_ = BookmarkModelFactory::GetForProfile(profile);
+  pref_service_ = profile->GetPrefs();
+
   // Registers the notifications we are interested.
   bookmark_model_->AddObserver(this);
-  if (bookmark_model_->loaded()) {
+  if (bookmark_model_->loaded())
     Java_BookmarksBridge_bookmarkModelLoaded(env, obj);
-  }
 }
 
 BookmarksBridge::~BookmarksBridge() {
@@ -85,6 +88,22 @@
       env, j_callback_obj, folder->id(), j_result_obj);
 }
 
+void BookmarksBridge::DeleteBookmark(JNIEnv* env,
+                                     jobject obj,
+                                     jlong bookmark_id) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(bookmark_model_->loaded());
+
+  const BookmarkNode* node = bookmark_model_->GetNodeByID(bookmark_id);
+  if (!IsEditable(node)) {
+    NOTREACHED();
+    return;
+  }
+
+  const BookmarkNode* parent_node = node->parent();
+  bookmark_model_->Remove(parent_node, parent_node->GetIndexOf(node));
+}
+
 void BookmarksBridge::ExtractBookmarkNodeInformation(
     const BookmarkNode* node,
     jobject j_result_obj) {
@@ -100,7 +119,7 @@
       env, j_result_obj, node->id(),
       ConvertUTF16ToJavaString(env, node->GetTitle()).obj(),
       ConvertUTF8ToJavaString(env, url).obj(),
-      node->is_folder(), parent_id);
+      node->is_folder(), parent_id, IsEditable(node));
 }
 
 const BookmarkNode* BookmarksBridge::GetFolderNodeFromId(jlong folder_id) {
@@ -116,6 +135,13 @@
   return folder;
 }
 
+bool BookmarksBridge::IsEditable(const BookmarkNode* node) const {
+  return node &&
+         (node->type() == BookmarkNode::FOLDER ||
+          node->type() == BookmarkNode::URL) &&
+         pref_service_->GetBoolean(prefs::kEditBookmarksEnabled);
+}
+
 // ------------- Observer-related methods ------------- //
 
 void BookmarksBridge::BookmarkModelChanged() {
diff --git a/chrome/browser/android/bookmarks_bridge.h b/chrome/browser/android/bookmarks_bridge.h
index ad8d8f6..4c9c6ed 100644
--- a/chrome/browser/android/bookmarks_bridge.h
+++ b/chrome/browser/android/bookmarks_bridge.h
@@ -13,6 +13,7 @@
 #include "base/compiler_specific.h"
 #include "chrome/browser/bookmarks/base_bookmark_model_observer.h"
 
+class PrefService;
 class Profile;
 
 // The delegate to fetch bookmarks information for the Android native
@@ -37,6 +38,10 @@
                                  jobject j_callback_obj,
                                  jobject j_result_obj);
 
+  void DeleteBookmark(JNIEnv* env,
+                      jobject obj,
+                      jlong bookmark_id);
+
  private:
   virtual ~BookmarksBridge();
 
@@ -44,6 +49,8 @@
       const BookmarkNode* node,
       jobject j_result_obj);
   const BookmarkNode* GetFolderNodeFromId(jlong folder_id);
+  // Returns true if |node| can be modified by the user.
+  bool IsEditable(const BookmarkNode* node) const;
 
   // Override BaseBookmarkModelObserver.
   virtual void BookmarkModelChanged() OVERRIDE;
@@ -51,7 +58,8 @@
   virtual void BookmarkModelBeingDeleted(BookmarkModel* model) OVERRIDE;
 
   JavaObjectWeakGlobalRef weak_java_ref_;
-  BookmarkModel* bookmark_model_;
+  BookmarkModel* bookmark_model_;  // weak
+  PrefService* pref_service_;  // weak
 
   DISALLOW_COPY_AND_ASSIGN(BookmarksBridge);
 };
diff --git a/chrome/browser/android/chrome_web_contents_delegate_android.cc b/chrome/browser/android/chrome_web_contents_delegate_android.cc
index 0eb1157..b2df0ec 100644
--- a/chrome/browser/android/chrome_web_contents_delegate_android.cc
+++ b/chrome/browser/android/chrome_web_contents_delegate_android.cc
@@ -5,18 +5,27 @@
 #include "chrome/browser/android/chrome_web_contents_delegate_android.h"
 
 #include "base/android/jni_android.h"
+#include "base/command_line.h"
+#include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/file_select_helper.h"
 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_modal_dialogs/javascript_dialog_manager.h"
+#include "chrome/browser/ui/blocked_content/blocked_content_tab_helper.h"
+#include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
+#include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/find_bar/find_notification_details.h"
 #include "chrome/browser/ui/find_bar/find_tab_helper.h"
+#include "chrome/common/chrome_switches.h"
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
+#include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/file_chooser_params.h"
 #include "jni/ChromeWebContentsDelegateAndroid_jni.h"
+#include "third_party/WebKit/public/web/WebWindowFeatures.h"
 #include "ui/gfx/rect.h"
 #include "ui/gfx/rect_f.h"
 
@@ -24,6 +33,7 @@
 #include "chrome/browser/pepper_broker_infobar_delegate.h"
 #endif
 
+using base::android::AttachCurrentThread;
 using base::android::ScopedJavaLocalRef;
 using content::FileChooserParams;
 using content::WebContents;
@@ -217,6 +227,96 @@
 #endif
 }
 
+WebContents* ChromeWebContentsDelegateAndroid::OpenURLFromTab(
+    WebContents* source,
+    const content::OpenURLParams& params) {
+  WindowOpenDisposition disposition = params.disposition;
+  if (!source || (disposition != CURRENT_TAB &&
+                  disposition != NEW_FOREGROUND_TAB &&
+                  disposition != NEW_BACKGROUND_TAB &&
+                  disposition != OFF_THE_RECORD &&
+                  disposition != NEW_POPUP &&
+                  disposition != NEW_WINDOW)) {
+    // We can't handle this here.  Give the parent a chance.
+    return WebContentsDelegateAndroid::OpenURLFromTab(source, params);
+  }
+
+  Profile* profile = Profile::FromBrowserContext(source->GetBrowserContext());
+  chrome::NavigateParams nav_params(profile,
+                                    params.url,
+                                    params.transition);
+  FillNavigateParamsFromOpenURLParams(&nav_params, params);
+  nav_params.source_contents = source;
+  nav_params.window_action = chrome::NavigateParams::SHOW_WINDOW;
+  nav_params.user_gesture = params.user_gesture;
+
+  PopupBlockerTabHelper* popup_blocker_helper =
+      PopupBlockerTabHelper::FromWebContents(source);
+  DCHECK(popup_blocker_helper);
+
+  if ((params.disposition == NEW_POPUP ||
+       params.disposition == NEW_FOREGROUND_TAB ||
+       params.disposition == NEW_BACKGROUND_TAB ||
+       params.disposition == NEW_WINDOW) &&
+      !params.user_gesture &&
+      !CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisablePopupBlocking)) {
+    if (popup_blocker_helper->MaybeBlockPopup(nav_params,
+                                              WebKit::WebWindowFeatures())) {
+      return NULL;
+    }
+  }
+
+  return WebContentsDelegateAndroid::OpenURLFromTab(source, params);
+}
+
+void ChromeWebContentsDelegateAndroid::AddNewContents(
+    WebContents* source,
+    WebContents* new_contents,
+    WindowOpenDisposition disposition,
+    const gfx::Rect& initial_pos,
+    bool user_gesture,
+    bool* was_blocked) {
+  // No code for this yet.
+  DCHECK_NE(disposition, SAVE_TO_DISK);
+  // Can't create a new contents for the current tab - invalid case.
+  DCHECK_NE(disposition, CURRENT_TAB);
+
+  BlockedContentTabHelper* source_blocked_content = NULL;
+  if (source)
+    source_blocked_content = BlockedContentTabHelper::FromWebContents(source);
+
+  TabAndroid::InitTabHelpers(new_contents);
+
+  if (source_blocked_content) {
+    if (source_blocked_content->all_contents_blocked()) {
+      source_blocked_content->AddWebContents(
+          new_contents, disposition, initial_pos, user_gesture);
+      if (was_blocked)
+        *was_blocked = true;
+      return;
+    }
+
+    new_contents->GetRenderViewHost()->DisassociateFromPopupCount();
+  }
+
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
+  bool handled = false;
+  if (!obj.is_null()) {
+    handled = Java_ChromeWebContentsDelegateAndroid_addNewContents(
+        env,
+        obj.obj(),
+        reinterpret_cast<jint>(source),
+        reinterpret_cast<jint>(new_contents),
+        static_cast<jint>(disposition),
+        NULL,
+        user_gesture);
+  }
+
+  if (!handled)
+    delete new_contents;
+}
 
 }  // namespace android
 }  // namespace chrome
diff --git a/chrome/browser/android/chrome_web_contents_delegate_android.h b/chrome/browser/android/chrome_web_contents_delegate_android.h
index 321b8b7..9c22997 100644
--- a/chrome/browser/android/chrome_web_contents_delegate_android.h
+++ b/chrome/browser/android/chrome_web_contents_delegate_android.h
@@ -62,6 +62,15 @@
       const GURL& url,
       const base::FilePath& plugin_path,
       const base::Callback<void(bool)>& callback) OVERRIDE;
+  virtual content::WebContents* OpenURLFromTab(
+      content::WebContents* source,
+      const content::OpenURLParams& params) OVERRIDE;
+  virtual void AddNewContents(content::WebContents* source,
+                              content::WebContents* new_contents,
+                              WindowOpenDisposition disposition,
+                              const gfx::Rect& initial_pos,
+                              bool user_gesture,
+                              bool* was_blocked) OVERRIDE;
 
  private:
   // NotificationObserver implementation.
diff --git a/chrome/browser/android/shortcut_helper.cc b/chrome/browser/android/shortcut_helper.cc
index 58206be..5322e84 100644
--- a/chrome/browser/android/shortcut_helper.cc
+++ b/chrome/browser/android/shortcut_helper.cc
@@ -26,40 +26,9 @@
 #include "ui/gfx/color_analysis.h"
 #include "url/gurl.h"
 
-namespace {
-
-// Adds a shortcut to the current URL to the Android home screen.
-// This proceeds over three phases:
-// 1) The renderer is asked to parse out webapp related meta tags with an async
-//    IPC message.
-// 2) The highest-resolution favicon available is retrieved for use as the
-//    icon on the home screen.
-// 3) A JNI call is made to fire an Intent at the Android launcher, which adds
-//    adds the shortcut.
-class ShortcutBuilder : public content::WebContentsObserver {
- public:
-  explicit ShortcutBuilder(content::WebContents* web_contents);
-  virtual ~ShortcutBuilder();
-
-  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
-  virtual void WebContentsDestroyed(content::WebContents* contents) OVERRIDE;
-
- private:
-  void OnDidRetrieveWebappInformation(bool success, bool is_webapp_capable);
-
-  Profile* profile_;
-  GURL url_;
-  string16 title_;
-  bool is_webapp_capable_;
-  CancelableTaskTracker cancelable_task_tracker_;
-
-  DISALLOW_COPY_AND_ASSIGN(ShortcutBuilder);
-};
-
 ShortcutBuilder::ShortcutBuilder(content::WebContents* web_contents)
-    : content::WebContentsObserver(web_contents) {
-  profile_ =
-      Profile::FromBrowserContext(web_contents->GetBrowserContext());
+    : is_webapp_capable_(false) {
+  Observe(web_contents);
   url_ = web_contents->GetURL();
   title_ = web_contents->GetTitle();
 
@@ -67,15 +36,20 @@
   Send(new ChromeViewMsg_RetrieveWebappInformation(routing_id(), url_));
 }
 
-ShortcutBuilder::~ShortcutBuilder() {
-}
-
 void ShortcutBuilder::OnDidRetrieveWebappInformation(bool success,
-                                                     bool is_webapp_capable) {
-  // Abort adding the shortcut if the renderer failed to process the page or
-  // if a navigation was instantiated before the process finished.
+                                                     bool is_webapp_capable,
+                                                     const GURL& expected_url) {
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
+  Observe(NULL);
+
   if (!success) {
     LOG(ERROR) << "Failed to parse webpage.";
+    Destroy();
+    return;
+  } else if (expected_url != url_) {
+    LOG(ERROR) << "Unexpected URL returned.";
+    Destroy();
     return;
   }
   is_webapp_capable_ = is_webapp_capable;
@@ -84,28 +58,33 @@
   // TODO(dfalcantara): Try combining with the new BookmarksHandler once its
   //                    rewrite is further along.
   FaviconService::FaviconForURLParams favicon_params(
-      profile_,
+      profile,
       url_,
       chrome::TOUCH_PRECOMPOSED_ICON | chrome::TOUCH_ICON | chrome::FAVICON,
       0);
 
   FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
-      profile_, Profile::EXPLICIT_ACCESS);
+      profile, Profile::EXPLICIT_ACCESS);
 
   favicon_service->GetRawFaviconForURL(
       favicon_params,
       ui::SCALE_FACTOR_100P,
-      base::Bind(&ShortcutHelper::FinishAddingShortcut,
-                 url_,
-                 title_,
-                 is_webapp_capable_),
+      base::Bind(&ShortcutBuilder::FinishAddingShortcut,
+                 base::Unretained(this)),
       &cancelable_task_tracker_);
 }
 
-void ShortcutBuilder::WebContentsDestroyed(content::WebContents* web_contents) {
-  if (cancelable_task_tracker_.HasTrackedTasks())
-    cancelable_task_tracker_.TryCancelAll();
-  delete this;
+void ShortcutBuilder::FinishAddingShortcut(
+    const chrome::FaviconBitmapResult& bitmap_result) {
+  base::WorkerPool::PostTask(
+      FROM_HERE,
+      base::Bind(&ShortcutHelper::AddShortcutInBackground,
+                 url_,
+                 title_,
+                 is_webapp_capable_,
+                 bitmap_result),
+      true);
+  Destroy();
 }
 
 bool ShortcutBuilder::OnMessageReceived(const IPC::Message& message) {
@@ -118,7 +97,16 @@
   return handled;
 }
 
-}  // namespace
+void ShortcutBuilder::WebContentsDestroyed(content::WebContents* web_contents) {
+  Destroy();
+}
+
+void ShortcutBuilder::Destroy() {
+  if (cancelable_task_tracker_.HasTrackedTasks()) {
+    cancelable_task_tracker_.TryCancelAll();
+  }
+  delete this;
+}
 
 void ShortcutHelper::AddShortcut(content::WebContents* web_contents) {
   // The ShortcutBuilder deletes itself when it's done.
@@ -129,22 +117,7 @@
   return RegisterNativesImpl(env);
 }
 
-void ShortcutHelper::FinishAddingShortcut(
-    const GURL& url,
-    const string16& title,
-    bool is_webapp_capable,
-    const chrome::FaviconBitmapResult& bitmap_result) {
-  base::WorkerPool::PostTask(
-      FROM_HERE,
-      base::Bind(&ShortcutHelper::FinishAddingShortcutInBackground,
-                 url,
-                 title,
-                 is_webapp_capable,
-                 bitmap_result),
-      true);
-}
-
-void ShortcutHelper::FinishAddingShortcutInBackground(
+void ShortcutHelper::AddShortcutInBackground(
     const GURL& url,
     const string16& title,
     bool is_webapp_capable,
@@ -196,5 +169,5 @@
 // its otherwise inaccessible WebContents.
 static void AddShortcut(JNIEnv* env, jclass clazz, jint tab_android_ptr) {
   TabAndroid* tab = reinterpret_cast<TabAndroid*>(tab_android_ptr);
-  ShortcutHelper::AddShortcut(tab->GetWebContents());
+  ShortcutHelper::AddShortcut(tab->web_contents());
 }
diff --git a/chrome/browser/android/shortcut_helper.h b/chrome/browser/android/shortcut_helper.h
index 90543da..2e66a9c 100644
--- a/chrome/browser/android/shortcut_helper.h
+++ b/chrome/browser/android/shortcut_helper.h
@@ -9,6 +9,8 @@
 #include "base/basictypes.h"
 #include "base/strings/string16.h"
 #include "chrome/browser/android/tab_android.h"
+#include "chrome/common/cancelable_task_tracker.h"
+#include "content/public/browser/web_contents_observer.h"
 
 namespace chrome {
 struct FaviconBitmapResult;
@@ -18,33 +20,64 @@
 class WebContents;
 }  // namespace content
 
+namespace IPC {
+class Message;
+}
+
 class GURL;
 
 // Adds a shortcut to the current URL to the Android home screen.
+// This proceeds over three phases:
+// 1) The renderer is asked to parse out webapp related meta tags with an async
+//    IPC message.
+// 2) The highest-resolution favicon available is retrieved for use as the
+//    icon on the home screen.
+// 3) A JNI call is made to fire an Intent at the Android launcher, which adds
+//    the shortcut.
+class ShortcutBuilder : public content::WebContentsObserver {
+ public:
+  explicit ShortcutBuilder(content::WebContents* web_contents);
+  virtual ~ShortcutBuilder() {}
+
+  void OnDidRetrieveWebappInformation(bool success,
+                                      bool is_webapp_capable,
+                                      const GURL& expected_url);
+
+  void FinishAddingShortcut(const chrome::FaviconBitmapResult& bitmap_result);
+
+  // WebContentsObserver
+  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+  virtual void WebContentsDestroyed(content::WebContents* web_contents)
+      OVERRIDE;
+
+ private:
+  void Destroy();
+
+  GURL url_;
+  string16 title_;
+  bool is_webapp_capable_;
+  CancelableTaskTracker cancelable_task_tracker_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShortcutBuilder);
+};
+
 class ShortcutHelper {
  public:
   // Adds a shortcut to the current URL to the Android home screen, firing
   // background tasks to pull all the data required.
   static void AddShortcut(content::WebContents* web_contents);
 
+  // Adds a shortcut to the launcher.  Must be called from a WorkerPool task.
+  static void AddShortcutInBackground(
+      const GURL& url,
+      const base::string16& title,
+      bool is_webapp_capable,
+      const chrome::FaviconBitmapResult& bitmap_result);
+
   // Registers JNI hooks.
   static bool RegisterShortcutHelper(JNIEnv* env);
 
-  // Adds a shortcut to the launcher.
-  static void FinishAddingShortcut(
-      const GURL& url,
-      const base::string16& title,
-      bool is_webapp_capable,
-      const chrome::FaviconBitmapResult& bitmap_result);
-
  private:
-  // Adds a shortcut to the launcher in the background.
-  static void FinishAddingShortcutInBackground(
-      const GURL& url,
-      const base::string16& title,
-      bool is_webapp_capable,
-      const chrome::FaviconBitmapResult& bitmap_result);
-
   DISALLOW_COPY_AND_ASSIGN(ShortcutHelper);
 };
 
diff --git a/chrome/browser/android/tab_android.cc b/chrome/browser/android/tab_android.cc
index d4da9b7..0f60a6f 100644
--- a/chrome/browser/android/tab_android.cc
+++ b/chrome/browser/android/tab_android.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/android/tab_android.h"
 
+#include "base/android/jni_android.h"
+#include "chrome/browser/android/chrome_web_contents_delegate_android.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
 #include "chrome/browser/extensions/tab_helper.h"
@@ -13,6 +15,8 @@
 #include "chrome/browser/password_manager/password_manager.h"
 #include "chrome/browser/password_manager/password_manager_delegate_impl.h"
 #include "chrome/browser/prerender/prerender_tab_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_android.h"
 #include "chrome/browser/sessions/session_tab_helper.h"
 #include "chrome/browser/ssl/ssl_tab_helper.h"
 #include "chrome/browser/translate/translate_tab_helper.h"
@@ -20,6 +24,7 @@
 #include "chrome/browser/ui/android/window_android_helper.h"
 #include "chrome/browser/ui/autofill/tab_autofill_manager_delegate.h"
 #include "chrome/browser/ui/blocked_content/blocked_content_tab_helper.h"
+#include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
 #include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h"
 #include "chrome/browser/ui/browser_tab_contents.h"
 #include "chrome/browser/ui/find_bar/find_tab_helper.h"
@@ -32,8 +37,6 @@
 #include "extensions/browser/view_type_utils.h"
 #include "jni/TabBase_jni.h"
 
-using content::WebContents;
-
 namespace {
 
 const char kTabHelpersInitializedUserDataKey[] =
@@ -41,7 +44,7 @@
 
 }  // namespace
 
-void BrowserTabContents::AttachTabHelpers(WebContents* contents) {
+void BrowserTabContents::AttachTabHelpers(content::WebContents* contents) {
   // If already initialized, nothing to be done.
   base::SupportsUserData::Data* initialization_tag =
       contents->GetUserData(&kTabHelpersInitializedUserDataKey);
@@ -77,6 +80,7 @@
   PasswordManagerDelegateImpl::CreateForWebContents(contents);
   PasswordManager::CreateForWebContentsAndDelegate(
       contents, PasswordManagerDelegateImpl::FromWebContents(contents));
+  PopupBlockerTabHelper::CreateForWebContents(contents);
   PrefsTabHelper::CreateForWebContents(contents);
   prerender::PrerenderTabHelper::CreateForWebContentsWithPasswordManager(
       contents, PasswordManager::FromWebContents(contents));
@@ -86,51 +90,132 @@
   WindowAndroidHelper::CreateForWebContents(contents);
 }
 
-void TabAndroid::InitTabHelpers(WebContents* contents) {
+// TODO(dtrainor): Refactor so we do not need this method.
+void TabAndroid::InitTabHelpers(content::WebContents* contents) {
   BrowserTabContents::AttachTabHelpers(contents);
 }
 
-WebContents* TabAndroid::InitWebContentsFromView(JNIEnv* env,
-                                                 jobject content_view) {
-  content::ContentViewCore* content_view_core =
-      content::ContentViewCore::GetNativeContentViewCore(env, content_view);
-  DCHECK(content_view_core);
-  WebContents* web_contents = content_view_core->GetWebContents();
-  DCHECK(web_contents);
-  InitTabHelpers(web_contents);
-  // Make sure tab id is same as web contents id. This means tab id can change
-  // based on when web_contents are attached to tab.
-  // TODO(shashishekhar): Add a new notification for this, so any
-  // observers can make appropriate state changes.
-  tab_id_.set_id(SessionTabHelper::FromWebContents(web_contents)
-                     ->session_id().id());
-  return web_contents;
+TabAndroid* TabAndroid::FromWebContents(content::WebContents* web_contents) {
+  CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(web_contents);
+  if (!core_tab_helper)
+    return NULL;
+
+  CoreTabHelperDelegate* core_delegate = core_tab_helper->delegate();
+  if (!core_delegate)
+    return NULL;
+
+  return static_cast<TabAndroid*>(core_delegate);
 }
 
-TabAndroid::TabAndroid(JNIEnv* env, jobject obj) : weak_java_tab_(env, obj) {
+TabAndroid* TabAndroid::GetNativeTab(JNIEnv* env, jobject obj) {
+  return reinterpret_cast<TabAndroid*>(Java_TabBase_getNativePtr(env, obj));
+}
+
+TabAndroid::TabAndroid(JNIEnv* env, jobject obj)
+    : weak_java_tab_(env, obj),
+      session_tab_id_(),
+      android_tab_id_(-1) {
   Java_TabBase_setNativePtr(env, obj, reinterpret_cast<jint>(this));
 }
 
+content::ContentViewCore* TabAndroid::GetContentViewCore() const {
+  if (!web_contents())
+    return NULL;
+
+  return content::ContentViewCore::FromWebContents(web_contents());
+}
+
+Profile* TabAndroid::GetProfile() const {
+  if (!web_contents())
+    return NULL;
+
+  return Profile::FromBrowserContext(web_contents()->GetBrowserContext());
+}
+
 TabAndroid::~TabAndroid() {
   JNIEnv* env = base::android::AttachCurrentThread();
   ScopedJavaLocalRef<jobject> obj = weak_java_tab_.get(env);
   if (obj.is_null())
     return;
 
-  Java_TabBase_destroyBase(env, obj.obj());
-}
-
-content::WebContents* TabAndroid::GetWebContents() {
-  return NULL;
+  Java_TabBase_clearNativePtr(env, obj.obj());
 }
 
 ToolbarModel::SecurityLevel TabAndroid::GetSecurityLevel() {
-  return ToolbarModelImpl::GetSecurityLevelForWebContents(GetWebContents());
+  return ToolbarModelImpl::GetSecurityLevelForWebContents(web_contents());
 }
 
 void TabAndroid::RunExternalProtocolDialog(const GURL& url) {
 }
 
+void TabAndroid::SwapTabContents(content::WebContents* old_contents,
+                                 content::WebContents* new_contents) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_TabBase_swapWebContents(
+      env,
+      weak_java_tab_.get(env).obj(),
+      reinterpret_cast<jint>(new_contents));
+}
+
+void TabAndroid::InitWebContents(JNIEnv* env,
+                                 jobject obj,
+                                 jint tab_id,
+                                 jboolean incognito,
+                                 jobject jcontent_view_core,
+                                 jobject jweb_contents_delegate) {
+  android_tab_id_ = tab_id;
+
+  content::ContentViewCore* content_view_core =
+      content::ContentViewCore::GetNativeContentViewCore(env,
+                                                         jcontent_view_core);
+  DCHECK(content_view_core);
+  DCHECK(content_view_core->GetWebContents());
+
+  web_contents_.reset(content_view_core->GetWebContents());
+  InitTabHelpers(web_contents_.get());
+
+  session_tab_id_.set_id(
+      SessionTabHelper::FromWebContents(web_contents())->session_id().id());
+  WindowAndroidHelper::FromWebContents(web_contents())->
+      SetWindowAndroid(content_view_core->GetWindowAndroid());
+  CoreTabHelper::FromWebContents(web_contents())->set_delegate(this);
+  web_contents_delegate_.reset(
+      new chrome::android::ChromeWebContentsDelegateAndroid(
+          env, jweb_contents_delegate));
+  web_contents_delegate_->LoadProgressChanged(web_contents(), 0);
+  web_contents()->SetDelegate(web_contents_delegate_.get());
+
+  // Verify that the WebContents this tab represents matches the expected
+  // off the record state.
+  CHECK_EQ(GetProfile()->IsOffTheRecord(), incognito);
+}
+
+void TabAndroid::DestroyWebContents(JNIEnv* env,
+                                    jobject obj,
+                                    jboolean delete_native) {
+  web_contents()->SetDelegate(NULL);
+
+  if (delete_native) {
+    web_contents_.reset();
+  } else {
+    // Release the WebContents so it does not get deleted by the scoped_ptr.
+    ignore_result(web_contents_.release());
+  }
+}
+
+base::android::ScopedJavaLocalRef<jobject> TabAndroid::GetProfileAndroid(
+    JNIEnv* env,
+    jobject obj) {
+  Profile* profile = GetProfile();
+  if (!profile)
+    return base::android::ScopedJavaLocalRef<jobject>();
+  ProfileAndroid* profile_android = ProfileAndroid::FromProfile(profile);
+  if (!profile_android)
+    return base::android::ScopedJavaLocalRef<jobject>();
+
+  return profile_android->GetJavaObject();
+}
+
 bool TabAndroid::RegisterTabAndroid(JNIEnv* env) {
   return RegisterNativesImpl(env);
 }
diff --git a/chrome/browser/android/tab_android.h b/chrome/browser/android/tab_android.h
index 19d8f25..81ef955 100644
--- a/chrome/browser/android/tab_android.h
+++ b/chrome/browser/android/tab_android.h
@@ -9,40 +9,65 @@
 
 #include "base/android/jni_helper.h"
 #include "base/callback_forward.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/strings/string16.h"
 #include "chrome/browser/sessions/session_id.h"
+#include "chrome/browser/ui/tab_contents/core_tab_helper_delegate.h"
 #include "chrome/browser/ui/toolbar/toolbar_model.h"
 
 class GURL;
+class Profile;
 class SkBitmap;
 
 namespace browser_sync {
 class SyncedTabDelegate;
 }
 
+namespace chrome {
+struct NavigateParams;
+}
+
+namespace chrome {
+namespace android {
+class ChromeWebContentsDelegateAndroid;
+}
+}
+
 namespace content {
+class ContentViewCore;
 struct ContextMenuParams;
 class WebContents;
 }
 
-class TabAndroid {
+class TabAndroid : public CoreTabHelperDelegate {
  public:
-  TabAndroid(JNIEnv* env, jobject obj);
-
   // Convenience method to retrieve the Tab associated with the passed
   // WebContents.  Can return NULL.
   static TabAndroid* FromWebContents(content::WebContents* web_contents);
 
+  // Returns the native TabAndroid stored in the Java TabBase represented by
+  // |obj|.
   static TabAndroid* GetNativeTab(JNIEnv* env, jobject obj);
 
-  // TODO(tedchoc): Make pure virtual once all derived classes can be updated.
-  virtual content::WebContents* GetWebContents();
+  TabAndroid(JNIEnv* env, jobject obj);
 
-  virtual browser_sync::SyncedTabDelegate* GetSyncedTabDelegate() = 0;
+  // Return the WebContents, if any, currently owned by this TabAndroid.
+  content::WebContents* web_contents() const { return web_contents_.get(); }
+
+  // Return specific id information regarding this TabAndroid.
+  const SessionID& session_id() const { return session_tab_id_; }
+  int android_id() const { return android_tab_id_; }
+
+  // Helper methods to make it easier to access objects from the associated
+  // WebContents.  Can return NULL.
+  content::ContentViewCore* GetContentViewCore() const;
+  Profile* GetProfile() const;
 
   virtual ToolbarModel::SecurityLevel GetSecurityLevel();
 
-  const SessionID& id() const { return tab_id_; }
+  virtual browser_sync::SyncedTabDelegate* GetSyncedTabDelegate() = 0;
+
+  virtual void HandlePopupNavigation(chrome::NavigateParams* params) = 0;
 
   virtual void OnReceivedHttpAuthRequest(jobject auth_handler,
                                          const string16& host,
@@ -88,20 +113,44 @@
   virtual int GetSyncId() const = 0;
   virtual void SetSyncId(int sync_id) = 0;
 
+  static void InitTabHelpers(content::WebContents* web_contents);
+
+  // Register the Tab's native methods through JNI.
   static bool RegisterTabAndroid(JNIEnv* env);
 
+  // CoreTabHelperDelegate ----------------------------------------------------
+
+  virtual void SwapTabContents(content::WebContents* old_contents,
+                               content::WebContents* new_contents) OVERRIDE;
+
+  // Methods called from Java via JNI -----------------------------------------
+
+  virtual void InitWebContents(JNIEnv* env,
+                               jobject obj,
+                               jint tab_id,
+                               jboolean incognito,
+                               jobject jcontent_view_core,
+                               jobject jweb_contents_delegate);
+  virtual void DestroyWebContents(JNIEnv* env,
+                                  jobject obj,
+                                  jboolean delete_native);
+  base::android::ScopedJavaLocalRef<jobject> GetProfileAndroid(JNIEnv* env,
+                                                               jobject obj);
+
  protected:
   virtual ~TabAndroid();
 
-  static void InitTabHelpers(content::WebContents* web_contents);
-
-  content::WebContents* InitWebContentsFromView(JNIEnv* env,
-                                                jobject content_view);
-
-  SessionID tab_id_;
-
  private:
   JavaObjectWeakGlobalRef weak_java_tab_;
+
+  SessionID session_tab_id_;
+  int android_tab_id_;
+
+  scoped_ptr<content::WebContents> web_contents_;
+  scoped_ptr<chrome::android::ChromeWebContentsDelegateAndroid>
+      web_contents_delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(TabAndroid);
 };
 
 #endif  // CHROME_BROWSER_ANDROID_TAB_ANDROID_H_
diff --git a/chrome/browser/android/tab_android_test_stubs.cc b/chrome/browser/android/tab_android_test_stubs.cc
index 823e59e..0e3dc37 100644
--- a/chrome/browser/android/tab_android_test_stubs.cc
+++ b/chrome/browser/android/tab_android_test_stubs.cc
@@ -13,16 +13,6 @@
 #include "printing/printing_context.h"
 #include "printing/printing_context_android.h"
 
-// static
-TabAndroid* TabAndroid::FromWebContents(content::WebContents* web_contents) {
-  return NULL;
-}
-
-// static
-TabAndroid* TabAndroid::GetNativeTab(JNIEnv* env, jobject obj) {
-  return NULL;
-}
-
 // AutoLoginInfoBarDelegatAndroid empty implementation for test_shell.
 // TODO(miguelg) remove once the AutoLoginInfoBar is upstreamed.
 AutoLoginInfoBarDelegateAndroid::AutoLoginInfoBarDelegateAndroid(
diff --git a/chrome/browser/app_controller_mac.h b/chrome/browser/app_controller_mac.h
index 46d8a05..bc10cd2 100644
--- a/chrome/browser/app_controller_mac.h
+++ b/chrome/browser/app_controller_mac.h
@@ -17,6 +17,7 @@
 #include "ui/base/work_area_watcher_observer.h"
 
 class AppControllerProfileObserver;
+@class AppShimMenuController;
 class BookmarkMenuBridge;
 class CommandUpdater;
 class GURL;
@@ -49,6 +50,9 @@
   scoped_ptr<BookmarkMenuBridge> bookmarkMenuBridge_;
   scoped_ptr<HistoryMenuBridge> historyMenuBridge_;
 
+  // Controller that manages main menu items for packaged apps.
+  base::scoped_nsobject<AppShimMenuController> appShimMenuController_;
+
   // The profile menu, which appears right before the Help menu. It is only
   // available when multiple profiles is enabled.
   base::scoped_nsobject<ProfileMenuController> profileMenuController_;
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index b2d30cd..34147c6 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -53,6 +53,7 @@
 #include "chrome/browser/ui/browser_mac.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/chrome_pages.h"
+#import "chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac.h"
 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h"
 #import "chrome/browser/ui/cocoa/browser_window_cocoa.h"
 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
@@ -165,6 +166,8 @@
   // real, user-visible app bundle directory. (The alternatives give either the
   // framework's path or the initial app's path, which may be an app mode shim
   // or a unit test.)
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
   base::FilePath appBundlePath =
       chrome::GetVersionedDirectory().DirName().DirName().DirName();
   CFPreferencesSetAppValue(
@@ -433,6 +436,8 @@
   localPrefRegistrar_.RemoveAll();
 
   [self unregisterEventHandlers];
+
+  appShimMenuController_.reset();
 }
 
 - (void)didEndMainMessageLoop {
@@ -651,6 +656,11 @@
 
   [self setUpdateCheckInterval];
 
+  // Start managing the menu for app windows. This needs to be done here because
+  // main menu item titles are not yet initialized in awakeFromNib.
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableAppShims))
+    appShimMenuController_.reset([[AppShimMenuController alloc] init]);
+
   // Build up the encoding menu, the order of the items differs based on the
   // current locale (see http://crbug.com/7647 for details).
   // We need a valid g_browser_process to get the profile which is why we can't
@@ -671,8 +681,11 @@
   [NSApp setHelpMenu:helpMenu_];
 
   // Record the path to the (browser) app bundle; this is used by the app mode
-  // shim.
-  RecordLastRunAppBundlePath();
+  // shim.  It has to be done in FILE thread because getting the path requires
+  // I/O.
+  BrowserThread::PostTask(
+      BrowserThread::FILE, FROM_HERE,
+      base::Bind(&RecordLastRunAppBundlePath));
 
   // Makes "Services" menu items available.
   [self registerServicesMenuTypesTo:[notify object]];
diff --git a/chrome/browser/apps/app_browsertest.cc b/chrome/browser/apps/app_browsertest.cc
index d21586b..951fd09 100644
--- a/chrome/browser/apps/app_browsertest.cc
+++ b/chrome/browser/apps/app_browsertest.cc
@@ -31,6 +31,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/api/app_runtime.h"
 #include "chrome/common/pref_names.h"
@@ -611,7 +612,18 @@
       << message_;
 }
 
-#endif  // defined(OS_CHROMEOS)
+// Tests that the file is created if the file does not exist and the app has the
+// fileSystem.write permission.
+IN_PROC_BROWSER_TEST_F(PlatformAppBrowserTest, LaunchNewFile) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  ClearCommandLineArgs();
+  CommandLine* command_line = CommandLine::ForCurrentProcess();
+  command_line->AppendArgPath(temp_dir.path().AppendASCII("new_file.txt"));
+  ASSERT_TRUE(RunPlatformAppTest("platform_apps/launch_new_file")) << message_;
+}
+
+#endif  // !defined(OS_CHROMEOS)
 
 IN_PROC_BROWSER_TEST_F(PlatformAppBrowserTest, OpenLink) {
   ASSERT_TRUE(StartEmbeddedTestServer());
@@ -1025,6 +1037,24 @@
       GetRenderWidgetHostView()->HasFocus());
 }
 
+// Currently this test only works if the PDF preview plug-in is available. This
+// will only happen in Chrome release builds or if the plug-in has been manually
+// copied from a Chrome release build. In the former case, this test will run
+// automatically. In the later case, it can be run manually by commenting out
+// the next three lines and the corresponding #endif and then running
+// browser_tests with a --enable-print-preview flag.
+#if !defined(GOOGLE_CHROME_BUILD)
+#define MAYBE_WindowDotPrintWorks DISABLED_WindowDotPrintWorks
+#else
+#define MAYBE_WindowDotPrintWorks WindowDotPrintWorks
+#endif
+
+IN_PROC_BROWSER_TEST_F(PlatformAppBrowserTest, MAYBE_WindowDotPrintWorks) {
+  PrintPreviewUI::SetAutoCancelForTesting(true);
+  ASSERT_TRUE(RunPlatformAppTest("platform_apps/print_api")) << message_;
+  PrintPreviewUI::SetAutoCancelForTesting(false);
+}
+
 
 #if defined(OS_CHROMEOS)
 
diff --git a/chrome/browser/apps/web_view_browsertest.cc b/chrome/browser/apps/web_view_browsertest.cc
index a75b301..0166c5b 100644
--- a/chrome/browser/apps/web_view_browsertest.cc
+++ b/chrome/browser/apps/web_view_browsertest.cc
@@ -20,6 +20,7 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents_delegate.h"
+#include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/fake_speech_recognition_manager.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -255,6 +256,17 @@
     }
   }
 
+  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+    const testing::TestInfo* const test_info =
+        testing::UnitTest::GetInstance()->current_test_info();
+
+    // Force SW rendering to check autosize bug.
+    if (!strncmp(test_info->name(), "AutoSizeSW", strlen("AutosizeSW")))
+      command_line->AppendSwitch(switches::kDisableForceCompositingMode);
+
+    extensions::PlatformAppBrowserTest::SetUpCommandLine(command_line);
+  }
+
   // This method is responsible for initializing a packaged app, which contains
   // multiple webview tags. The tags have different partition identifiers and
   // their WebContent objects are returned as output. The method also verifies
@@ -555,6 +567,29 @@
       fake_speech_recognition_manager_;
 };
 
+// This test ensures JavaScript errors ("Cannot redefine property") do not
+// happen when a <webview> is removed from DOM and added back.
+IN_PROC_BROWSER_TEST_F(WebViewTest,
+                       AddRemoveWebView_AddRemoveWebView) {
+  ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
+  ASSERT_TRUE(RunPlatformAppTest("platform_apps/web_view/addremove"))
+      << message_;
+}
+
+IN_PROC_BROWSER_TEST_F(WebViewTest, AutoSize) {
+  ASSERT_TRUE(RunPlatformAppTest("platform_apps/web_view/autosize"))
+      << message_;
+}
+
+#if !defined(OS_CHROMEOS)
+// This test ensures <webview> doesn't crash in SW rendering when autosize is
+// turned on.
+IN_PROC_BROWSER_TEST_F(WebViewTest, AutoSizeSW) {
+  ASSERT_TRUE(RunPlatformAppTest("platform_apps/web_view/autosize"))
+      << message_;
+}
+#endif
+
 IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAutosizeAfterNavigation) {
   TestHelper("testAutosizeAfterNavigation",
              "DoneShimTest.PASSED",
@@ -575,7 +610,9 @@
              "web_view/shim");
 }
 
-IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestAutosizeWithPartialAttributes) {
+// This test is flaky. crbug.com/282116
+IN_PROC_BROWSER_TEST_F(WebViewTest,
+                       DISABLED_Shim_TestAutosizeWithPartialAttributes) {
   TestHelper("testAutosizeWithPartialAttributes",
              "DoneShimTest.PASSED",
              "DoneShimTest.FAILED",
@@ -668,6 +705,21 @@
              "web_view/shim");
 }
 
+IN_PROC_BROWSER_TEST_F(WebViewTest,
+                       Shim_TestNavOnConsecutiveSrcAttributeChanges) {
+  TestHelper("testNavOnConsecutiveSrcAttributeChanges",
+             "DoneShimTest.PASSED",
+             "DoneShimTest.FAILED",
+             "web_view/shim");
+}
+
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestNavOnSrcAttributeChange) {
+  TestHelper("testNavOnSrcAttributeChange",
+             "DoneShimTest.PASSED",
+             "DoneShimTest.FAILED",
+             "web_view/shim");
+}
+
 IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestRemoveSrcAttribute) {
   TestHelper("testRemoveSrcAttribute",
              "DoneShimTest.PASSED",
@@ -675,6 +727,13 @@
              "web_view/shim");
 }
 
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestReassignSrcAttribute) {
+  TestHelper("testReassignSrcAttribute",
+             "DoneShimTest.PASSED",
+             "DoneShimTest.FAILED",
+             "web_view/shim");
+}
+
 IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestBrowserPluginNotAllowed) {
 #if defined(OS_WIN)
   // Flaky on XP bots. http://crbug.com/267300
@@ -839,6 +898,13 @@
              "web_view/shim");
 }
 
+IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestNavigationToExternalProtocol) {
+  TestHelper("testNavigationToExternalProtocol",
+             "DoneShimTest.PASSED",
+             "DoneShimTest.FAILED",
+             "web_view/shim");
+}
+
 IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestResizeWebviewResizesContent) {
   TestHelper("testResizeWebviewResizesContent",
              "DoneShimTest.PASSED",
diff --git a/chrome/browser/apps/web_view_interactive_browsertest.cc b/chrome/browser/apps/web_view_interactive_browsertest.cc
index 4e0607d..bf1e4ec 100644
--- a/chrome/browser/apps/web_view_interactive_browsertest.cc
+++ b/chrome/browser/apps/web_view_interactive_browsertest.cc
@@ -467,8 +467,15 @@
 
 #endif  // (defined(OS_WIN) || defined(OS_LINUX))
 
+// Fails on Windows. crbug.com/236040
+// Also flaky on ChromiumOS. crbug.com/281815
+#if defined(OS_WIN) || defined(OS_CHROMEOS)
+#define MAYBE_Focus DISABLED_Focus
+#else
+#define MAYBE_Focus Focus
+#endif
 // Tests that setting focus on the <webview> sets focus on the guest.
-IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, DISABLED_Focus) {
+IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, MAYBE_Focus) {
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
   ASSERT_TRUE(RunPlatformAppTest("platform_apps/web_view/focus"))
       << message_;
@@ -561,6 +568,14 @@
              "web_view/newwindow");
 }
 
+IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest,
+                       NewWindow_WebRequestRemoveElement) {
+  TestHelper("testNewWindowWebRequestRemoveElement",
+             "DoneNewWindowTest.PASSED",
+             "DoneNewWindowTest.FAILED",
+             "web_view/newwindow");
+}
+
 IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest, ExecuteCode) {
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
   ASSERT_TRUE(RunPlatformAppTestWithArg(
@@ -668,11 +683,3 @@
              "DonePointerLockTest.FAILED",
              "web_view/pointerlock");
 }
-
-// Currently re-adding a webview doesn't work. See http://crbug.com/260622
-IN_PROC_BROWSER_TEST_F(WebViewInteractiveTest,
-                       AddRemoveWebView_AddRemoveWebView) {
-  ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
-  ASSERT_TRUE(RunPlatformAppTest("platform_apps/web_view/addremove"))
-      << message_;
-}
diff --git a/chrome/browser/autocomplete/autocomplete_input.cc b/chrome/browser/autocomplete/autocomplete_input.cc
index e192a89..c1fdf54 100644
--- a/chrome/browser/autocomplete/autocomplete_input.cc
+++ b/chrome/browser/autocomplete/autocomplete_input.cc
@@ -173,7 +173,7 @@
   // until I run into some cases that really need it.
   if (parts->scheme.is_nonempty() &&
       !LowerCaseEqualsASCII(parsed_scheme, chrome::kHttpScheme) &&
-      !LowerCaseEqualsASCII(parsed_scheme, chrome::kHttpsScheme)) {
+      !LowerCaseEqualsASCII(parsed_scheme, content::kHttpsScheme)) {
     // See if we know how to handle the URL internally.
     if (ProfileIOData::IsHandledProtocol(UTF16ToASCII(parsed_scheme)))
       return URL;
diff --git a/chrome/browser/autocomplete/autocomplete_input.h b/chrome/browser/autocomplete/autocomplete_input.h
index 3aba2d1..6c6b2e8 100644
--- a/chrome/browser/autocomplete/autocomplete_input.h
+++ b/chrome/browser/autocomplete/autocomplete_input.h
@@ -50,25 +50,43 @@
   // and update omnibox_event.proto::PageClassification and
   // omnibox_edit_model.cc::ClassifyPage() too.
   enum PageClassification {
-    INVALID_SPEC = 0,   // invalid URI; shouldn't happen
-    NEW_TAB_PAGE = 1,   // chrome://newtab/
-    // Note that chrome://newtab/ doesn't have to be the built-in
-    // version; it could be replaced by an extension.
-    BLANK = 2,          // about:blank
-    HOMEPAGE = 3,       // user switched settings to "open this page" mode.
-    // Note that if the homepage is set to the new tab page or about blank,
-    // then we'll classify the web page into those categories, not HOMEPAGE.
-    OTHER = 4,          // everything not included somewhere else on this list
+    // An invalid URL; shouldn't happen.
+    INVALID_SPEC = 0,
+
+    // chrome://newtab/.  This can be either the built-in version or a
+    // replacement new tab page from an extension.  Note that when Instant
+    // Extended is enabled, the new tab page will be reported as either
+    // INSTANT_NEW_TAB_PAGE_WITH_OMNIBOX_AS_STARTING_FOCUS or
+    // INSTANT_NEW_TAB_PAGE_WITH_FAKEBOX_AS_STARTING_FOCUS below,
+    // unless an extension is replacing the new tab page, in which case
+    // it will still be reported as NEW_TAB_PAGE.
+    NEW_TAB_PAGE = 1,
+
+    // about:blank.
+    BLANK = 2,
+
+    // The user's home page.  Note that if the home page is set to any
+    // of the new tab page versions or to about:blank, then we'll
+    // classify the page into those categories, not HOME_PAGE.
+    HOME_PAGE = 3,
+
+    // The catch-all entry of everything not included somewhere else
+    // on this list.
+    OTHER = 4,
+
     // The user is on a search result page that's doing search term
     // replacement, meaning the search terms should've appeared in the omnibox
     // before the user started editing it, not the URL of the page.
     SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT = 6,
+
     // The new tab page in which this omnibox interaction first started
     // with the user having focus in the omnibox.
     INSTANT_NEW_TAB_PAGE_WITH_OMNIBOX_AS_STARTING_FOCUS = 7,
+
     // The new tab page in which this omnibox interaction first started
     // with the user having focus in the fakebox.
     INSTANT_NEW_TAB_PAGE_WITH_FAKEBOX_AS_STARTING_FOCUS = 8,
+
     // The user is on a search result page that's not doing search term
     // replacement, meaning the URL of the page should've appeared in the
     // omnibox before the user started editing it, not the search terms.
diff --git a/chrome/browser/autocomplete/autocomplete_match.cc b/chrome/browser/autocomplete/autocomplete_match.cc
index ca6cb7d..ae89aa0 100644
--- a/chrome/browser/autocomplete/autocomplete_match.cc
+++ b/chrome/browser/autocomplete/autocomplete_match.cc
@@ -376,7 +376,7 @@
   }
 
   // Replace https protocol with http protocol.
-  if (stripped_destination_url.SchemeIs(chrome::kHttpsScheme)) {
+  if (stripped_destination_url.SchemeIs(content::kHttpsScheme)) {
     replacements.SetScheme(
         chrome::kHttpScheme,
         url_parse::Component(0, strlen(chrome::kHttpScheme)));
diff --git a/chrome/browser/autocomplete/autocomplete_result_unittest.cc b/chrome/browser/autocomplete/autocomplete_result_unittest.cc
index dc8ed62..babf1ae 100644
--- a/chrome/browser/autocomplete/autocomplete_result_unittest.cc
+++ b/chrome/browser/autocomplete/autocomplete_result_unittest.cc
@@ -17,9 +17,9 @@
 #include "chrome/browser/search_engines/template_url_service.h"
 #include "chrome/browser/search_engines/template_url_service_test_util.h"
 #include "chrome/common/autocomplete_match_type.h"
-#include "chrome/common/metrics/entropy_provider.h"
 #include "chrome/common/metrics/variations/variations_util.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/variations/entropy_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 class AutocompleteResultTest : public testing::Test  {
@@ -351,7 +351,7 @@
   {
     std::map<std::string, std::string> params;
     params[std::string(OmniboxFieldTrial::kDemoteByTypeRule) + ":3:*"] =
-        "1:50,7:100,2:0";  // 3 == HOMEPAGE
+        "1:50,7:100,2:0";  // 3 == HOME_PAGE
     ASSERT_TRUE(chrome_variations::AssociateVariationParams(
         OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
   }
@@ -361,7 +361,7 @@
   AutocompleteResult result;
   result.AppendMatches(matches);
   AutocompleteInput input(string16(), string16::npos, string16(), GURL(),
-                          AutocompleteInput::HOMEPAGE, false, false, false,
+                          AutocompleteInput::HOME_PAGE, false, false, false,
                           AutocompleteInput::ALL_MATCHES);
   result.SortAndCull(input, test_util_.profile());
 
@@ -386,7 +386,7 @@
   };
 
   std::map<std::string, std::string> params;
-  // Enable reorder for omnibox inputs on the user's homepage.
+  // Enable reorder for omnibox inputs on the user's home page.
   params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) +
          ":3:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleEnabled;
   ASSERT_TRUE(chrome_variations::AssociateVariationParams(
@@ -403,7 +403,7 @@
     AutocompleteResult result;
     result.AppendMatches(matches);
     AutocompleteInput input(string16(), string16::npos, string16(), GURL(),
-                            AutocompleteInput::HOMEPAGE, false, false, false,
+                            AutocompleteInput::HOME_PAGE, false, false, false,
                             AutocompleteInput::ALL_MATCHES);
     result.SortAndCull(input, test_util_.profile());
     AssertResultMatches(result, data, 4);
@@ -418,7 +418,7 @@
     AutocompleteResult result;
     result.AppendMatches(matches);
     AutocompleteInput input(string16(), string16::npos, string16(), GURL(),
-                            AutocompleteInput::HOMEPAGE, false, false, false,
+                            AutocompleteInput::HOME_PAGE, false, false, false,
                             AutocompleteInput::ALL_MATCHES);
     result.SortAndCull(input, test_util_.profile());
     ASSERT_EQ(4U, result.size());
diff --git a/chrome/browser/autocomplete/search_provider.cc b/chrome/browser/autocomplete/search_provider.cc
index 05b49e6..5b366b6 100644
--- a/chrome/browser/autocomplete/search_provider.cc
+++ b/chrome/browser/autocomplete/search_provider.cc
@@ -136,9 +136,11 @@
 SearchProvider::SuggestResult::SuggestResult(const string16& suggestion,
                                              bool from_keyword_provider,
                                              int relevance,
-                                             bool relevance_from_server)
+                                             bool relevance_from_server,
+                                             bool should_prefetch)
     : Result(from_keyword_provider, relevance, relevance_from_server),
-      suggestion_(suggestion) {
+      suggestion_(suggestion),
+      should_prefetch_(should_prefetch) {
 }
 
 SearchProvider::SuggestResult::~SuggestResult() {
@@ -212,6 +214,7 @@
   suggest_results.clear();
   navigation_results.clear();
   verbatim_relevance = -1;
+  metadata.clear();
 }
 
 bool SearchProvider::Results::HasServerProvidedScores() const {
@@ -243,6 +246,8 @@
 const int SearchProvider::kKeywordProviderURLFetcherID = 2;
 int SearchProvider::kMinimumTimeBetweenSuggestQueriesMs = 100;
 const char SearchProvider::kRelevanceFromServerKey[] = "relevance_from_server";
+const char SearchProvider::kShouldPrefetchKey[] = "should_prefetch";
+const char SearchProvider::kSuggestMetadataKey[] = "suggest_metadata";
 const char SearchProvider::kTrue[] = "true";
 const char SearchProvider::kFalse[] = "false";
 
@@ -352,6 +357,16 @@
   return match;
 }
 
+// static
+bool SearchProvider::ShouldPrefetch(const AutocompleteMatch& match) {
+  return match.GetAdditionalInfo(kShouldPrefetchKey) == kTrue;
+}
+
+// static
+std::string SearchProvider::GetSuggestMetadata(const AutocompleteMatch& match) {
+  return match.GetAdditionalInfo(kSuggestMetadataKey);
+}
+
 void SearchProvider::AddProviderInfo(ProvidersInfo* provider_info) const {
   provider_info->push_back(metrics::OmniboxEventProto_ProviderInfo());
   metrics::OmniboxEventProto_ProviderInfo& new_entry = provider_info->back();
@@ -731,7 +746,7 @@
   // our checks below.  Other QUERY cases are less likely to be URLs and thus we
   // assume we're OK.
   if (!LowerCaseEqualsASCII(input_.scheme(), chrome::kHttpScheme) &&
-      !LowerCaseEqualsASCII(input_.scheme(), chrome::kHttpsScheme) &&
+      !LowerCaseEqualsASCII(input_.scheme(), content::kHttpsScheme) &&
       !LowerCaseEqualsASCII(input_.scheme(), chrome::kFtpScheme))
     return (input_.type() == AutocompleteInput::QUERY);
 
@@ -749,7 +764,7 @@
   // Don't send anything for https except the hostname.  Hostnames are OK
   // because they are visible when the TCP connection is established, but the
   // specific path may reveal private information.
-  if (LowerCaseEqualsASCII(input_.scheme(), chrome::kHttpsScheme) &&
+  if (LowerCaseEqualsASCII(input_.scheme(), content::kHttpsScheme) &&
       parts.path.is_nonempty())
     return false;
 
@@ -881,6 +896,7 @@
   ListValue* types = NULL;
   ListValue* relevances = NULL;
   DictionaryValue* extras = NULL;
+  int prefetch_index = -1;
   if (root_list->GetDictionary(4, &extras)) {
     extras->GetList("google:suggesttype", &types);
 
@@ -897,6 +913,16 @@
     extras->GetBoolean("google:fieldtrialtriggered", &triggered);
     field_trial_triggered_ |= triggered;
     field_trial_triggered_in_session_ |= triggered;
+
+    // Extract the prefetch hint.
+    DictionaryValue* client_data = NULL;
+    if (extras->GetDictionary("google:clientdata", &client_data) && client_data)
+      client_data->GetInteger("phi", &prefetch_index);
+
+    // Store the metadata that came with the response in case we need to pass it
+    // along with the prefetch query to Instant.
+    JSONStringValueSerializer json_serializer(&results->metadata);
+    json_serializer.Serialize(*extras);
   }
 
   // Clear the previous results now that new results are available.
@@ -925,9 +951,10 @@
             *this, url, title, is_keyword, relevance, true));
       }
     } else {
+      bool should_prefetch = static_cast<int>(index) == prefetch_index;
       // TODO(kochi): Improve calculator result presentation.
       results->suggest_results.push_back(
-          SuggestResult(result, is_keyword, relevance, true));
+          SuggestResult(result, is_keyword, relevance, true, should_prefetch));
     }
   }
 
@@ -965,7 +992,7 @@
       TemplateURLRef::NO_SUGGESTION_CHOSEN;
   if (verbatim_relevance > 0) {
     AddMatchToMap(input_.text(), input_.text(), verbatim_relevance,
-                  relevance_from_server,
+                  relevance_from_server, false, std::string(),
                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
                   did_not_accept_default_suggestion, false, &map);
   }
@@ -984,6 +1011,7 @@
       if (keyword_verbatim_relevance > 0) {
         AddMatchToMap(keyword_input_.text(), keyword_input_.text(),
                       keyword_verbatim_relevance, keyword_relevance_from_server,
+                      false, std::string(),
                       AutocompleteMatchType::SEARCH_OTHER_ENGINE,
                       did_not_accept_keyword_suggestion, true, &map);
       }
@@ -994,8 +1022,9 @@
   AddHistoryResultsToMap(default_history_results_, false,
                          did_not_accept_default_suggestion, &map);
 
-  AddSuggestResultsToMap(keyword_results_.suggest_results, &map);
-  AddSuggestResultsToMap(default_results_.suggest_results, &map);
+  AddSuggestResultsToMap(keyword_results_.suggest_results, std::string(), &map);
+  AddSuggestResultsToMap(default_results_.suggest_results,
+                         default_results_.metadata, &map);
 
   ACMatches matches;
   for (MatchMap::const_iterator i(map.begin()); i != map.end(); ++i)
@@ -1209,9 +1238,8 @@
   for (SuggestResults::const_iterator i(scored_results.begin());
        i != scored_results.end(); ++i) {
     AddMatchToMap(i->suggestion(), input_text, i->relevance(), false,
-                  AutocompleteMatchType::SEARCH_HISTORY,
-                  did_not_accept_suggestion,
-                  is_keyword, map);
+                  false, std::string(), AutocompleteMatchType::SEARCH_HISTORY,
+                  did_not_accept_suggestion, is_keyword, map);
   }
 }
 
@@ -1258,7 +1286,7 @@
         i->time, is_keyword, !prevent_inline_autocomplete,
         prevent_search_history_inlining);
     scored_results.push_back(
-        SuggestResult(i->term, is_keyword, relevance, false));
+        SuggestResult(i->term, is_keyword, relevance, false, false));
   }
 
   // History returns results sorted for us.  However, we may have docked some
@@ -1280,12 +1308,14 @@
 }
 
 void SearchProvider::AddSuggestResultsToMap(const SuggestResults& results,
+                                            const std::string& metadata,
                                             MatchMap* map) {
   for (size_t i = 0; i < results.size(); ++i) {
     const bool is_keyword = results[i].from_keyword_provider();
     const string16& input = is_keyword ? keyword_input_.text() : input_.text();
     AddMatchToMap(results[i].suggestion(), input, results[i].relevance(),
                   results[i].relevance_from_server(),
+                  results[i].should_prefetch(), metadata,
                   AutocompleteMatchType::SEARCH_SUGGEST, i, is_keyword, map);
   }
 }
@@ -1402,6 +1432,8 @@
                                    const string16& input_text,
                                    int relevance,
                                    bool relevance_from_server,
+                                   bool should_prefetch,
+                                   const std::string& metadata,
                                    AutocompleteMatch::Type type,
                                    int accepted_suggestion,
                                    bool is_keyword,
@@ -1431,22 +1463,50 @@
     return;
   match.RecordAdditionalInfo(kRelevanceFromServerKey,
                              relevance_from_server ? kTrue : kFalse);
+  match.RecordAdditionalInfo(kShouldPrefetchKey,
+                             should_prefetch ? kTrue : kFalse);
+
+  // Metadata is needed only for prefetching queries.
+  if (should_prefetch)
+    match.RecordAdditionalInfo(kSuggestMetadataKey, metadata);
 
   // Try to add |match| to |map|.  If a match for |query_string| is already in
   // |map|, replace it if |match| is more relevant.
   // NOTE: Keep this ToLower() call in sync with url_database.cc.
   const std::pair<MatchMap::iterator, bool> i(
       map->insert(std::make_pair(base::i18n::ToLower(query_string), match)));
-  // NOTE: We purposefully do a direct relevance comparison here instead of
-  // using AutocompleteMatch::MoreRelevant(), so that we'll prefer "items added
-  // first" rather than "items alphabetically first" when the scores are equal.
-  // The only case this matters is when a user has results with the same score
-  // that differ only by capitalization; because the history system returns
-  // results sorted by recency, this means we'll pick the most recent such
-  // result even if the precision of our relevance score is too low to
-  // distinguish the two.
-  if (!i.second && (match.relevance > i.first->second.relevance))
-    i.first->second = match;
+
+  if (!i.second) {
+    // NOTE: We purposefully do a direct relevance comparison here instead of
+    // using AutocompleteMatch::MoreRelevant(), so that we'll prefer "items
+    // added first" rather than "items alphabetically first" when the scores are
+    // equal. The only case this matters is when a user has results with the
+    // same score that differ only by capitalization; because the history system
+    // returns results sorted by recency, this means we'll pick the most
+    // recent such result even if the precision of our relevance score is too
+    // low to distinguish the two.
+    if (match.relevance > i.first->second.relevance) {
+      i.first->second = match;
+    } else if (match.keyword == i.first->second.keyword) {
+      // Old and new matches are from the same search provider. It is okay to
+      // record one match's prefetch data onto a different match (for the same
+      // query string) for the following reasons:
+      // 1. Because the suggest server only sends down a query string from which
+      // we construct a URL, rather than sending a full URL, and because we
+      // construct URLs from query strings in the same way every time, the URLs
+      // for the two matches will be the same. Therefore, we won't end up
+      // prefetching something the server didn't intend.
+      // 2. Presumably the server sets the prefetch bit on a match it things is
+      // sufficiently relevant that the user is likely to choose it. Surely
+      // setting the prefetch bit on a match of even higher relevance won't
+      // violate this assumption.
+      should_prefetch |= ShouldPrefetch(i.first->second);
+      i.first->second.RecordAdditionalInfo(kShouldPrefetchKey,
+                                           should_prefetch ? kTrue : kFalse);
+      if (should_prefetch)
+        i.first->second.RecordAdditionalInfo(kSuggestMetadataKey, metadata);
+    }
+  }
 }
 
 AutocompleteMatch SearchProvider::NavigationToMatch(
@@ -1513,6 +1573,7 @@
   match.RecordAdditionalInfo(
       kRelevanceFromServerKey,
       navigation.relevance_from_server() ? kTrue : kFalse);
+  match.RecordAdditionalInfo(kShouldPrefetchKey, kFalse);
 
   return match;
 }
diff --git a/chrome/browser/autocomplete/search_provider.h b/chrome/browser/autocomplete/search_provider.h
index 281684d..5993a46 100644
--- a/chrome/browser/autocomplete/search_provider.h
+++ b/chrome/browser/autocomplete/search_provider.h
@@ -90,6 +90,14 @@
       int omnibox_start_margin,
       bool append_extra_query_params);
 
+  // Returns whether the SearchProvider previously flagged |match| as a query
+  // that should be prefetched.
+  static bool ShouldPrefetch(const AutocompleteMatch& match);
+
+  // Extracts the suggest response metadata which SearchProvider previously
+  // stored for |match|.
+  static std::string GetSuggestMetadata(const AutocompleteMatch& match);
+
   // AutocompleteProvider:
   virtual void AddProviderInfo(ProvidersInfo* provider_info) const OVERRIDE;
   virtual void ResetSession() OVERRIDE;
@@ -109,6 +117,8 @@
   FRIEND_TEST_ALL_PREFIXES(SearchProviderTest, RemoveStaleResultsTest);
   FRIEND_TEST_ALL_PREFIXES(SearchProviderTest, SuggestRelevanceExperiment);
   FRIEND_TEST_ALL_PREFIXES(AutocompleteProviderTest, GetDestinationURL);
+  FRIEND_TEST_ALL_PREFIXES(InstantExtendedPrefetchTest, ClearPrefetchedResults);
+  FRIEND_TEST_ALL_PREFIXES(InstantExtendedPrefetchTest, SetPrefetchQuery);
 
   // Manages the providers (TemplateURLs) used by SearchProvider. Two providers
   // may be used:
@@ -213,10 +223,12 @@
     SuggestResult(const string16& suggestion,
                   bool from_keyword_provider,
                   int relevance,
-                  bool relevance_from_server);
+                  bool relevance_from_server,
+                  bool should_prefetch);
     virtual ~SuggestResult();
 
     const string16& suggestion() const { return suggestion_; }
+    bool should_prefetch() const { return should_prefetch_; }
 
     // Result:
     virtual bool IsInlineable(const string16& input) const OVERRIDE;
@@ -227,6 +239,9 @@
    private:
     // The search suggestion string.
     string16 suggestion_;
+
+    // Should this result be prefetched?
+    bool should_prefetch_;
   };
 
   class NavigationResult : public Result {
@@ -298,6 +313,9 @@
     // suppresses the verbatim result.
     int verbatim_relevance;
 
+    // The JSON metadata associated with this server response.
+    std::string metadata;
+
    private:
     DISALLOW_COPY_AND_ASSIGN(Results);
   };
@@ -404,7 +422,9 @@
                                      bool is_keyword);
 
   // Adds matches for |results| to |map|.
-  void AddSuggestResultsToMap(const SuggestResults& results, MatchMap* map);
+  void AddSuggestResultsToMap(const SuggestResults& results,
+                              const std::string& metadata,
+                              MatchMap* map);
 
   // Gets the relevance score for the verbatim result.  This value may be
   // provided by the suggest server or calculated locally; if
@@ -449,6 +469,8 @@
                      const string16& input_text,
                      int relevance,
                      bool relevance_from_server,
+                     bool should_prefetch,
+                     const std::string& metadata,
                      AutocompleteMatch::Type type,
                      int accepted_suggestion,
                      bool is_keyword,
@@ -474,10 +496,20 @@
   // previous one.  Non-const because some unittests modify this value.
   static int kMinimumTimeBetweenSuggestQueriesMs;
 
+  // The following keys are used to record additional information on matches.
+
   // We annotate our AutocompleteMatches with whether their relevance scores
   // were server-provided using this key in the |additional_info| field.
   static const char kRelevanceFromServerKey[];
-  // These are the values we record with the above key.
+
+  // Indicates whether the server said a match should be prefetched.
+  static const char kShouldPrefetchKey[];
+
+  // Used to store metadata from the server response, which is needed for
+  // prefetching.
+  static const char kSuggestMetadataKey[];
+
+  // These are the values for the above keys.
   static const char kTrue[];
   static const char kFalse[];
 
diff --git a/chrome/browser/autocomplete/search_provider_unittest.cc b/chrome/browser/autocomplete/search_provider_unittest.cc
index 5aa485f..b575f5c 100644
--- a/chrome/browser/autocomplete/search_provider_unittest.cc
+++ b/chrome/browser/autocomplete/search_provider_unittest.cc
@@ -27,17 +27,16 @@
 #include "chrome/browser/search_engines/template_url_service.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/metrics/entropy_provider.h"
 #include "chrome/common/metrics/variations/variations_util.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/variations/entropy_provider.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "net/url_request/test_url_fetcher_factory.h"
 #include "net/url_request/url_request_status.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-
 // SearchProviderTest ---------------------------------------------------------
 
 // The following environment is configured for these tests:
@@ -2429,7 +2428,7 @@
         provider_->default_results_.suggest_results.push_back(
             SearchProvider::SuggestResult(ASCIIToUTF16(suggestion), false,
                                           cases[i].results[j].relevance,
-                                          false));
+                                          false, false));
       }
     }
 
@@ -2468,3 +2467,137 @@
     EXPECT_EQ(nav_end, nav_it);
   }
 }
+
+// A basic test that verifies the prefetch metadata parsing logic.
+TEST_F(SearchProviderTest, PrefetchMetadataParsing) {
+  struct Match {
+    std::string contents;
+    bool allowed_to_be_prefetched;
+    AutocompleteMatchType::Type type;
+    bool from_keyword;
+  };
+  const Match kEmptyMatch = { kNotApplicable,
+                              false,
+                              AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
+                              false };
+
+  struct {
+    const std::string input_text;
+    bool prefer_keyword_provider_results;
+    const std::string default_provider_response_json;
+    const std::string keyword_provider_response_json;
+    const Match matches[5];
+  } cases[] = {
+    // Default provider response does not have prefetch details. Ensure that the
+    // suggestions are not marked as prefetch query.
+    { "a",
+      false,
+      "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
+      std::string(),
+      { { "a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
+        { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
+        { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
+        kEmptyMatch,
+        kEmptyMatch
+      },
+    },
+    // Ensure that default provider suggest response prefetch details are
+    // parsed and recorded in AutocompleteMatch.
+    { "ab",
+      false,
+      "[\"ab\",[\"abc\", \"http://b.com\", \"http://c.com\"],[],[],"
+          "{\"google:clientdata\":{\"phi\": 0},"
+          "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"NAVIGATION\"],"
+          "\"google:suggestrelevance\":[999, 12, 1]}]",
+      std::string(),
+      { { "ab",    false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
+        { "abc",   true,  AutocompleteMatchType::SEARCH_SUGGEST, false },
+        { "b.com", false, AutocompleteMatchType::NAVSUGGEST, false },
+        { "c.com", false, AutocompleteMatchType::NAVSUGGEST, false },
+        kEmptyMatch
+      },
+    },
+    // Default provider suggest response has prefetch details.
+    // SEARCH_WHAT_YOU_TYPE suggestion outranks SEARCH_SUGGEST suggestion for
+    // the same query string. Ensure that the prefetch details from
+    // SEARCH_SUGGEST match are set onto SEARCH_WHAT_YOU_TYPE match.
+    { "ab",
+      false,
+      "[\"ab\",[\"ab\", \"http://ab.com\"],[],[],"
+          "{\"google:clientdata\":{\"phi\": 0},"
+          "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
+          "\"google:suggestrelevance\":[99, 98]}]",
+      std::string(),
+      { {"ab", true, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
+        {"ab.com", false, AutocompleteMatchType::NAVSUGGEST, false },
+        kEmptyMatch,
+        kEmptyMatch,
+        kEmptyMatch
+      },
+    },
+    // Default provider response has prefetch details. We prefer keyword
+    // provider results. Ensure that prefetch bit for a suggestion from the
+    // default search provider does not get copied onto a higher-scoring match
+    // for the same query string from the keyword provider.
+    { "k a",
+      true,
+      "[\"k a\",[\"a\", \"ab\"],[],[], {\"google:clientdata\":{\"phi\": 0},"
+          "\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
+          "\"google:suggestrelevance\":[9, 12]}]",
+      "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
+      { { "a", false, AutocompleteMatchType::SEARCH_OTHER_ENGINE, true},
+        { "k a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
+        { "ab", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
+        { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, true },
+        { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, true }
+      },
+    }
+  };
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
+    QueryForInput(ASCIIToUTF16(cases[i].input_text), false,
+                  cases[i].prefer_keyword_provider_results);
+
+    // Set up a default fetcher with provided results.
+    net::TestURLFetcher* fetcher =
+        test_factory_.GetFetcherByID(
+            SearchProvider::kDefaultProviderURLFetcherID);
+    ASSERT_TRUE(fetcher);
+    fetcher->set_response_code(200);
+    fetcher->SetResponseString(cases[i].default_provider_response_json);
+    fetcher->delegate()->OnURLFetchComplete(fetcher);
+
+    if (cases[i].prefer_keyword_provider_results) {
+      // Set up a keyword fetcher with provided results.
+      net::TestURLFetcher* keyword_fetcher =
+          test_factory_.GetFetcherByID(
+              SearchProvider::kKeywordProviderURLFetcherID);
+      ASSERT_TRUE(keyword_fetcher);
+      keyword_fetcher->set_response_code(200);
+      keyword_fetcher->SetResponseString(
+          cases[i].keyword_provider_response_json);
+      keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
+      keyword_fetcher = NULL;
+    }
+
+    RunTillProviderDone();
+
+    const std::string description =
+        "for input with json =" + cases[i].default_provider_response_json;
+    const ACMatches& matches = provider_->matches();
+    // The top match must inline and score as highly as calculated verbatim.
+    ASSERT_FALSE(matches.empty());
+    EXPECT_GE(matches[0].relevance, 1300);
+
+    // Ensure that the returned matches equal the expectations.
+    for (size_t j = 0; j < matches.size(); ++j) {
+      SCOPED_TRACE(description);
+      EXPECT_EQ(cases[i].matches[j].contents, UTF16ToUTF8(matches[j].contents));
+      EXPECT_EQ(cases[i].matches[j].allowed_to_be_prefetched,
+                SearchProvider::ShouldPrefetch(matches[j]));
+      EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
+      EXPECT_EQ(cases[i].matches[j].from_keyword,
+                matches[j].keyword == ASCIIToUTF16("k"));
+    }
+  }
+}
diff --git a/chrome/browser/autocomplete/zero_suggest_provider.cc b/chrome/browser/autocomplete/zero_suggest_provider.cc
index f19b287..6680bb9 100644
--- a/chrome/browser/autocomplete/zero_suggest_provider.cc
+++ b/chrome/browser/autocomplete/zero_suggest_provider.cc
@@ -13,6 +13,8 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
+#include "chrome/browser/autocomplete/autocomplete_classifier.h"
+#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
 #include "chrome/browser/autocomplete/autocomplete_input.h"
 #include "chrome/browser/autocomplete/autocomplete_match.h"
 #include "chrome/browser/autocomplete/autocomplete_provider_listener.h"
@@ -296,7 +298,7 @@
       }
     } else {
       suggest_results->push_back(SearchProvider::SuggestResult(
-          result, false, relevance, relevances != NULL));
+          result, false, relevance, relevances != NULL, false));
     }
   }
 }
@@ -390,7 +392,7 @@
       ReplaceSearchTerms(search_term_args);
   GURL suggest_url(req_url);
   // Make sure we are sending the suggest request through HTTPS.
-  if (!suggest_url.SchemeIs(chrome::kHttpsScheme)) {
+  if (!suggest_url.SchemeIs(content::kHttpsScheme)) {
     Stop(true);
     return;
   }
@@ -460,9 +462,9 @@
                           GURL(current_query_), current_page_classification_,
                           false, false, true, AutocompleteInput::ALL_MATCHES);
 
-  AutocompleteMatch match(
-      HistoryURLProvider::SuggestExactInput(this, input,
-                                            !HasHTTPScheme(input.text())));
+  AutocompleteMatch match;
+  AutocompleteClassifierFactory::GetForProfile(profile_)->Classify(
+      permanent_text_, false, true, &match, NULL);
   match.is_history_what_you_typed_match = false;
   match.allowed_to_be_default_match = true;
 
diff --git a/chrome/browser/autofill/autofill_browsertest.cc b/chrome/browser/autofill/autofill_browsertest.cc
index 4498d2d..96042e4 100644
--- a/chrome/browser/autofill/autofill_browsertest.cc
+++ b/chrome/browser/autofill/autofill_browsertest.cc
@@ -202,13 +202,17 @@
     CHECK(file_util::ReadFileToString(data_file, &data));
     std::vector<std::string> lines;
     base::SplitString(data, '\n', &lines);
+    int parsed_profiles = 0;
     for (size_t i = 0; i < lines.size(); ++i) {
       if (StartsWithASCII(lines[i], "#", false))
         continue;
+
       std::vector<std::string> fields;
       base::SplitString(lines[i], '|', &fields);
       if (fields.empty())
         continue;  // Blank line.
+
+      ++parsed_profiles;
       CHECK_EQ(12u, fields.size());
 
       FormMap data;
@@ -227,7 +231,7 @@
 
       FillFormAndSubmit("duplicate_profiles_test.html", data);
     }
-    return lines.size();
+    return parsed_profiles;
   }
 
   void ExpectFieldValue(const std::string& field_name,
@@ -558,7 +562,7 @@
 // Test profile is saved if phone number is valid in selected country.
 // The data file contains two profiles with valid phone numbers and two
 // profiles with invalid phone numbers from their respective country.
-// DISABLED: http://crbug.com/150084
+// DISABLED: http://crbug.com/281582
 IN_PROC_BROWSER_TEST_F(AutofillTest,
                        DISABLED_ProfileSavedWithValidCountryPhone) {
   ASSERT_TRUE(test_server()->Start());
@@ -691,10 +695,10 @@
 // 'Address Line 1' and 'City' data match. When two profiles are merged, any
 // remaining address fields are expected to be overwritten. Any non-address
 // fields should accumulate multi-valued data.
-// DISABLED: http://crbug.com/150084
+// DISABLED: http://crbug.com/281541
 IN_PROC_BROWSER_TEST_F(AutofillTest,
                        DISABLED_MergeAggregatedProfilesWithSameAddress) {
-  AggregateProfilesIntoAutofillPrefs("dataset_2.txt");
+  AggregateProfilesIntoAutofillPrefs("dataset_same_address.txt");
 
   ASSERT_EQ(3u, personal_data_manager()->GetProfiles().size());
 }
@@ -703,7 +707,7 @@
 // Mininum address values needed during aggregation are: address line 1, city,
 // state, and zip code.
 // Profiles are merged when data for address line 1 and city match.
-// DISABLED: http://crbug.com/150084
+// DISABLED: http://crbug.com/281541
 IN_PROC_BROWSER_TEST_F(AutofillTest,
                        DISABLED_ProfilesNotMergedWhenNoMinAddressData) {
   AggregateProfilesIntoAutofillPrefs("dataset_no_address.txt");
@@ -713,11 +717,11 @@
 
 // Test Autofill ability to merge duplicate profiles and throw away junk.
 // TODO(isherman): this looks redundant, consider removing.
-// DISABLED: http://crbug.com/150084
+// DISABLED: http://crbug.com/281541
 IN_PROC_BROWSER_TEST_F(AutofillTest,
                        DISABLED_MergeAggregatedDuplicatedProfiles) {
   int num_of_profiles =
-      AggregateProfilesIntoAutofillPrefs("dataset_no_address.txt");
+      AggregateProfilesIntoAutofillPrefs("dataset_duplicated_profiles.txt");
 
   ASSERT_GT(num_of_profiles,
             static_cast<int>(personal_data_manager()->GetProfiles().size()));
diff --git a/chrome/browser/autofill/autofill_interactive_uitest.cc b/chrome/browser/autofill/autofill_interactive_uitest.cc
index 798202e..2758ecd 100644
--- a/chrome/browser/autofill/autofill_interactive_uitest.cc
+++ b/chrome/browser/autofill/autofill_interactive_uitest.cc
@@ -200,30 +200,32 @@
                                AutofillDriver* autofill_driver)
       : AutofillExternalDelegate(web_contents, autofill_manager,
                                  autofill_driver),
-        keyboard_listener_(NULL) {
+        key_press_event_callback_(NULL) {
   }
 
   virtual ~TestAutofillExternalDelegate() {}
 
   // AutofillExternalDelegate:
-  virtual void OnPopupShown(content::KeyboardListener* listener) OVERRIDE {
-    AutofillExternalDelegate::OnPopupShown(listener);
-    keyboard_listener_ = listener;
+  virtual void OnPopupShown(
+      content::RenderWidgetHost::KeyPressEventCallback* callback) OVERRIDE {
+    AutofillExternalDelegate::OnPopupShown(callback);
+    key_press_event_callback_ = callback;
   }
 
-  virtual void OnPopupHidden(content::KeyboardListener* listener) OVERRIDE {
-    keyboard_listener_ = NULL;
-    AutofillExternalDelegate::OnPopupHidden(listener);
+  virtual void OnPopupHidden(
+      content::RenderWidgetHost::KeyPressEventCallback* callback) OVERRIDE {
+    key_press_event_callback_ = NULL;
+    AutofillExternalDelegate::OnPopupHidden(callback);
   }
 
-  content::KeyboardListener* keyboard_listener() {
-    return keyboard_listener_;
+  content::RenderWidgetHost::KeyPressEventCallback* keyboard_listener() {
+    return key_press_event_callback_;
   }
 
  private:
-  // The popup that is currently registered as a keyboard listener, or NULL if
-  // there is none.
-  content::KeyboardListener* keyboard_listener_;
+  // The popup that is currently registered as a key press event callback, or
+  // NULL if there is none.
+  content::RenderWidgetHost::KeyPressEventCallback* key_press_event_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(TestAutofillExternalDelegate);
 };
@@ -411,7 +413,7 @@
     content::NativeWebKeyboardEvent event;
     event.windowsKeyCode = key;
     test_delegate_.Reset();
-    GetExternalDelegate()->keyboard_listener()->HandleKeyPressEvent(event);
+    GetExternalDelegate()->keyboard_listener()->Run(event);
     test_delegate_.Wait();
   }
 
@@ -858,14 +860,14 @@
                "我々は重要な、興味深いものになるが、時折状況が発生するため苦労や痛みは"
                "彼にいくつかの素晴らしいを調達することができます。それから、いくつかの利");
 
-  content::WindowedNotificationObserver infobar(
+  content::WindowedNotificationObserver infobar_observer(
       chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED,
       content::NotificationService::AllSources());
   ASSERT_NO_FATAL_FAILURE(
       ui_test_utils::NavigateToURL(browser(), url));
 
   // Wait for the translation bar to appear and get it.
-  infobar.Wait();
+  infobar_observer.Wait();
   TranslateInfoBarDelegate* delegate = InfoBarService::FromWebContents(
       browser()->tab_strip_model()->GetActiveWebContents())->infobar_at(0)->
           AsTranslateInfoBarDelegate();
@@ -876,14 +878,14 @@
   // Simulate translation button press.
   delegate->Translate();
 
-  // Simulate the translate script being retrieved.
-  // Pass fake google.translate lib as the translate script.
-  SimulateURLFetch(true);
-
   content::WindowedNotificationObserver translation_observer(
       chrome::NOTIFICATION_PAGE_TRANSLATED,
       content::NotificationService::AllSources());
 
+  // Simulate the translate script being retrieved.
+  // Pass fake google.translate lib as the translate script.
+  SimulateURLFetch(true);
+
   // Simulate the render notifying the translation has been done.
   translation_observer.Wait();
 
@@ -1029,7 +1031,7 @@
   // TODO(isherman): verify entire form.
 }
 
-// http://crbug.com/150084
+// http://crbug.com/281527
 #if defined(OS_MACOSX)
 #define MAYBE_FormFillLatencyAfterSubmit FormFillLatencyAfterSubmit
 #else
@@ -1126,7 +1128,7 @@
   // autofilled form.
   content::NativeWebKeyboardEvent event;
   event.windowsKeyCode = ui::VKEY_DOWN;
-  GetExternalDelegate()->keyboard_listener()->HandleKeyPressEvent(event);
+  GetExternalDelegate()->keyboard_listener()->Run(event);
 
   // Wait for any IPCs to complete by performing an action that generates an
   // IPC that's easy to wait for.  Chrome shouldn't crash.
diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc
index 064df01..7f988a8 100644
--- a/chrome/browser/automation/testing_automation_provider.cc
+++ b/chrome/browser/automation/testing_automation_provider.cc
@@ -1762,28 +1762,11 @@
   handler_map_["NetworkScan"] = &TestingAutomationProvider::NetworkScan;
   handler_map_["ToggleNetworkDevice"] =
       &TestingAutomationProvider::ToggleNetworkDevice;
-  handler_map_["ConnectToCellularNetwork"] =
-      &TestingAutomationProvider::ConnectToCellularNetwork;
-  handler_map_["DisconnectFromCellularNetwork"] =
-      &TestingAutomationProvider::DisconnectFromCellularNetwork;
-  handler_map_["ConnectToWifiNetwork"] =
-      &TestingAutomationProvider::ConnectToWifiNetwork;
   handler_map_["ConnectToHiddenWifiNetwork"] =
       &TestingAutomationProvider::ConnectToHiddenWifiNetwork;
-  handler_map_["DisconnectFromWifiNetwork"] =
-      &TestingAutomationProvider::DisconnectFromWifiNetwork;
   handler_map_["ForgetWifiNetwork"] =
       &TestingAutomationProvider::ForgetWifiNetwork;
 
-  handler_map_["AddPrivateNetwork"] =
-      &TestingAutomationProvider::AddPrivateNetwork;
-  handler_map_["GetPrivateNetworkInfo"] =
-      &TestingAutomationProvider::GetPrivateNetworkInfo;
-  handler_map_["ConnectToPrivateNetwork"] =
-      &TestingAutomationProvider::ConnectToPrivateNetwork;
-  handler_map_["DisconnectFromPrivateNetwork"] =
-      &TestingAutomationProvider::DisconnectFromPrivateNetwork;
-
   handler_map_["EnableSpokenFeedback"] =
       &TestingAutomationProvider::EnableSpokenFeedback;
   handler_map_["IsSpokenFeedbackEnabled"] =
@@ -1799,10 +1782,6 @@
   handler_map_["SetMute"] = &TestingAutomationProvider::SetMute;
 
   handler_map_["OpenCrosh"] = &TestingAutomationProvider::OpenCrosh;
-  handler_map_["SetProxySettings"] =
-      &TestingAutomationProvider::SetProxySettings;
-  handler_map_["SetSharedProxies"] =
-      &TestingAutomationProvider::SetSharedProxies;
 
   browser_handler_map_["GetTimeInfo"] =
       &TestingAutomationProvider::GetTimeInfo;
diff --git a/chrome/browser/automation/testing_automation_provider.h b/chrome/browser/automation/testing_automation_provider.h
index 4e35f60..f9721f1 100644
--- a/chrome/browser/automation/testing_automation_provider.h
+++ b/chrome/browser/automation/testing_automation_provider.h
@@ -1319,41 +1319,11 @@
   void ToggleNetworkDevice(base::DictionaryValue* args,
                            IPC::Message* reply_message);
 
-  void SetProxySettings(base::DictionaryValue* args,
-                        IPC::Message* reply_message);
-
-  void SetSharedProxies(base::DictionaryValue* args,
-                        IPC::Message* reply_message);
-
-  void ConnectToCellularNetwork(base::DictionaryValue* args,
-                            IPC::Message* reply_message);
-
-  void DisconnectFromCellularNetwork(base::DictionaryValue* args,
-                                 IPC::Message* reply_message);
-
-  void ConnectToWifiNetwork(base::DictionaryValue* args,
-                            IPC::Message* reply_message);
-
   void ConnectToHiddenWifiNetwork(base::DictionaryValue* args,
                                   IPC::Message* reply_message);
 
-  void DisconnectFromWifiNetwork(base::DictionaryValue* args,
-                                 IPC::Message* reply_message);
-
   void ForgetWifiNetwork(DictionaryValue* args, IPC::Message* reply_message);
 
-  // VPN.
-  void AddPrivateNetwork(DictionaryValue* args, IPC::Message* reply_message);
-
-  void GetPrivateNetworkInfo(base::DictionaryValue* args,
-                             IPC::Message* reply_message);
-
-  void ConnectToPrivateNetwork(base::DictionaryValue* args,
-                               IPC::Message* reply_message);
-
-  void DisconnectFromPrivateNetwork(base::DictionaryValue* args,
-                                    IPC::Message* reply_message);
-
   // Accessibility.
   void EnableSpokenFeedback(DictionaryValue* args, IPC::Message* reply_message);
 
diff --git a/chrome/browser/automation/testing_automation_provider_chromeos.cc b/chrome/browser/automation/testing_automation_provider_chromeos.cc
index f1b91d5..38f2e42 100644
--- a/chrome/browser/automation/testing_automation_provider_chromeos.cc
+++ b/chrome/browser/automation/testing_automation_provider_chromeos.cc
@@ -632,128 +632,6 @@
   }
 }
 
-void TestingAutomationProvider::SetSharedProxies(
-    DictionaryValue* args,
-    IPC::Message* reply_message) {
-
-  AutomationJSONReply reply(this, reply_message);
-  base::Value* value;
-  if (!args->Get("value", &value)) {
-    reply.SendError("Invalid or missing value argument.");
-    return;
-  }
-  std::string error_message;
-  Profile* profile =
-      automation_util::GetCurrentProfileOnChromeOS(&error_message);
-  if (!profile) {
-    reply.SendError(error_message);
-    return;
-  }
-  PrefService* pref_service = profile->GetPrefs();
-  pref_service->Set(prefs::kUseSharedProxies, *value);
-  reply.SendSuccess(NULL);
-}
-
-void TestingAutomationProvider::SetProxySettings(DictionaryValue* args,
-                                                 IPC::Message* reply_message) {
-  AutomationJSONReply reply(this, reply_message);
-  std::string proxy_config_str;
-  if (!args->GetString("proxy_config", &proxy_config_str)) {
-    reply.SendError("Invalid or missing args.");
-    return;
-  }
-
-  const chromeos::NetworkState* network = chromeos::NetworkHandler::Get()->
-      network_state_handler()->DefaultNetwork();
-  if (!network) {
-    reply.SendError("No network connected.");
-    return;
-  }
-
-  scoped_ptr<base::DictionaryValue> proxy_config_dict(
-      chromeos::onc::ReadDictionaryFromJson(proxy_config_str));
-  ProxyConfigDictionary proxy_config(proxy_config_dict.get());
-  chromeos::proxy_config::SetProxyConfigForNetwork(proxy_config, *network);
-
-  reply.SendSuccess(NULL);
-}
-
-void TestingAutomationProvider::ConnectToCellularNetwork(
-    DictionaryValue* args, IPC::Message* reply_message) {
-  std::string service_path;
-  if (!args->GetString("service_path", &service_path)) {
-    AutomationJSONReply(this, reply_message).SendError(
-        "Invalid or missing args.");
-    return;
-  }
-
-  NetworkLibrary* network_library = NetworkLibrary::Get();
-  chromeos::CellularNetwork* cellular =
-      network_library->FindCellularNetworkByPath(service_path);
-  if (!cellular) {
-    AutomationJSONReply(this, reply_message).SendError(
-        "No network found with specified service path.");
-    return;
-  }
-
-  // Set up an observer (it will delete itself).
-  new ServicePathConnectObserver(this, reply_message, service_path);
-
-  network_library->ConnectToCellularNetwork(cellular);
-  network_library->RequestNetworkScan();
-}
-
-void TestingAutomationProvider::DisconnectFromCellularNetwork(
-    DictionaryValue* args, IPC::Message* reply_message) {
-  NetworkLibrary* network_library = NetworkLibrary::Get();
-  const chromeos::CellularNetwork* cellular =
-        network_library->cellular_network();
-  if (!cellular) {
-    AutomationJSONReply(this, reply_message).SendError(
-        "Not connected to any cellular network.");
-    return;
-  }
-
-  // Set up an observer (it will delete itself).
-  new NetworkDisconnectObserver(this, reply_message, cellular->service_path());
-
-  network_library->DisconnectFromNetwork(cellular);
-}
-
-void TestingAutomationProvider::ConnectToWifiNetwork(
-    DictionaryValue* args, IPC::Message* reply_message) {
-  AutomationJSONReply reply(this, reply_message);
-  std::string service_path, password;
-  bool shared;
-  if (!args->GetString("service_path", &service_path) ||
-      !args->GetString("password", &password) ||
-      !args->GetBoolean("shared", &shared)) {
-    reply.SendError("Invalid or missing args.");
-    return;
-  }
-
-  NetworkLibrary* network_library = NetworkLibrary::Get();
-  chromeos::WifiNetwork* wifi =
-      network_library->FindWifiNetworkByPath(service_path);
-  if (!wifi) {
-    reply.SendError("No network found with specified service path.");
-    return;
-  }
-  if (!password.empty())
-    wifi->SetPassphrase(password);
-
-  // Regardless of what was passed, if the network is open and visible,
-  // the network must be shared because of a UI restriction.
-  if (wifi->encryption() == chromeos::SECURITY_NONE)
-    shared = true;
-
-  // Set up an observer (it will delete itself).
-  new ServicePathConnectObserver(this, reply_message, service_path);
-
-  network_library->ConnectToWifiNetwork(wifi, shared);
-  network_library->RequestNetworkScan();
-}
-
 void TestingAutomationProvider::ForgetWifiNetwork(
     DictionaryValue* args, IPC::Message* reply_message) {
   std::string service_path;
@@ -859,168 +737,6 @@
   }
 }
 
-void TestingAutomationProvider::DisconnectFromWifiNetwork(
-    DictionaryValue* args, IPC::Message* reply_message) {
-  AutomationJSONReply reply(this, reply_message);
-  NetworkLibrary* network_library = NetworkLibrary::Get();
-  const chromeos::WifiNetwork* wifi = network_library->wifi_network();
-  if (!wifi) {
-    reply.SendError("Not connected to any wifi network.");
-    return;
-  }
-
-  network_library->DisconnectFromNetwork(wifi);
-  reply.SendSuccess(NULL);
-}
-
-void TestingAutomationProvider::AddPrivateNetwork(
-    DictionaryValue* args, IPC::Message* reply_message) {
-  std::string hostname, service_name, provider_type, key, cert_id, username,
-      password;
-  if (!args->GetString("hostname", &hostname) ||
-      !args->GetString("service_name", &service_name) ||
-      !args->GetString("provider_type", &provider_type) ||
-      !args->GetString("username", &username) ||
-      !args->GetString("password", &password)) {
-    AutomationJSONReply(this, reply_message)
-        .SendError("Invalid or missing args.");
-    return;
-  }
-
-  NetworkLibrary* network_library = NetworkLibrary::Get();
-
-  // Attempt to connect to the VPN based on the provider type.
-  if (provider_type == VPNProviderTypeToString(
-      chromeos::PROVIDER_TYPE_L2TP_IPSEC_PSK)) {
-    if (!args->GetString("key", &key)) {
-      AutomationJSONReply(this, reply_message)
-          .SendError("Missing key arg.");
-      return;
-    }
-    new VirtualConnectObserver(this, reply_message, service_name);
-    // Connect using a pre-shared key.
-    chromeos::NetworkLibrary::VPNConfigData config_data;
-    config_data.psk = key;
-    config_data.username = username;
-    config_data.user_passphrase = password;
-    network_library->ConnectToUnconfiguredVirtualNetwork(
-        service_name,
-        hostname,
-        chromeos::PROVIDER_TYPE_L2TP_IPSEC_PSK,
-        config_data);
-  } else if (provider_type == VPNProviderTypeToString(
-      chromeos::PROVIDER_TYPE_L2TP_IPSEC_USER_CERT)) {
-    if (!args->GetString("cert_id", &cert_id)) {
-      AutomationJSONReply(this, reply_message)
-          .SendError("Missing a certificate arg.");
-      return;
-    }
-    new VirtualConnectObserver(this, reply_message, service_name);
-    // Connect using a user certificate.
-    chromeos::NetworkLibrary::VPNConfigData config_data;
-    config_data.client_cert_pkcs11_id = cert_id;
-    config_data.username = username;
-    config_data.user_passphrase = password;
-    network_library->ConnectToUnconfiguredVirtualNetwork(
-        service_name,
-        hostname,
-        chromeos::PROVIDER_TYPE_L2TP_IPSEC_USER_CERT,
-        config_data);
-  } else if (provider_type == VPNProviderTypeToString(
-      chromeos::PROVIDER_TYPE_OPEN_VPN)) {
-    std::string otp;
-    args->GetString("otp", &otp);
-    // Connect using OPEN_VPN.
-    chromeos::NetworkLibrary::VPNConfigData config_data;
-    config_data.client_cert_pkcs11_id = cert_id;
-    config_data.username = username;
-    config_data.user_passphrase = password;
-    config_data.otp = otp;
-    network_library->ConnectToUnconfiguredVirtualNetwork(
-        service_name,
-        hostname,
-        chromeos::PROVIDER_TYPE_OPEN_VPN,
-        config_data);
-  } else {
-    AutomationJSONReply(this, reply_message)
-        .SendError("Unsupported provider type.");
-    return;
-  }
-}
-
-void TestingAutomationProvider::ConnectToPrivateNetwork(
-    DictionaryValue* args, IPC::Message* reply_message) {
-  AutomationJSONReply reply(this, reply_message);
-  std::string service_path;
-  if (!args->GetString("service_path", &service_path)) {
-    reply.SendError("Invalid or missing args.");
-    return;
-  }
-
-  // Connect to a remembered VPN by its service_path. Valid service_paths
-  // can be found in the dictionary returned by GetPrivateNetworkInfo.
-  NetworkLibrary* network_library = NetworkLibrary::Get();
-  chromeos::VirtualNetwork* network =
-      network_library->FindVirtualNetworkByPath(service_path);
-  if (!network) {
-    reply.SendError(base::StringPrintf("No virtual network found: %s",
-                                       service_path.c_str()));
-    return;
-  }
-  if (network->NeedMoreInfoToConnect()) {
-    reply.SendError("Virtual network is missing info required to connect.");
-    return;
-  };
-
-  // Set up an observer (it will delete itself).
-  new VirtualConnectObserver(this, reply_message, network->name());
-  network_library->ConnectToVirtualNetwork(network);
-}
-
-void TestingAutomationProvider::GetPrivateNetworkInfo(
-    DictionaryValue* args, IPC::Message* reply_message) {
-  scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
-  NetworkLibrary* network_library = NetworkLibrary::Get();
-  const chromeos::VirtualNetworkVector& virtual_networks =
-      network_library->virtual_networks();
-
-  // Construct a dictionary of fields describing remembered VPNs. Also list
-  // the currently active VPN, if any.
-  if (network_library->virtual_network())
-    return_value->SetString("connected",
-                            network_library->virtual_network()->service_path());
-  for (chromeos::VirtualNetworkVector::const_iterator iter =
-       virtual_networks.begin(); iter != virtual_networks.end(); ++iter) {
-    const chromeos::VirtualNetwork* virt = *iter;
-    DictionaryValue* item = new DictionaryValue;
-    item->SetString("name", virt->name());
-    item->SetString("provider_type",
-                    VPNProviderTypeToString(virt->provider_type()));
-    item->SetString("hostname", virt->server_hostname());
-    item->SetString("key", virt->psk_passphrase());
-    item->SetString("cert_id", virt->client_cert_id());
-    item->SetString("username", virt->username());
-    item->SetString("password", virt->user_passphrase());
-    return_value->Set(virt->service_path(), item);
-  }
-
-  AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
-}
-
-void TestingAutomationProvider::DisconnectFromPrivateNetwork(
-    DictionaryValue* args, IPC::Message* reply_message) {
-  AutomationJSONReply reply(this, reply_message);
-  NetworkLibrary* network_library = NetworkLibrary::Get();
-  const chromeos::VirtualNetwork* virt = network_library->virtual_network();
-  if (!virt) {
-    reply.SendError("Not connected to any virtual network.");
-    return;
-  }
-
-  network_library->DisconnectFromNetwork(virt);
-  reply.SendSuccess(NULL);
-}
-
 void TestingAutomationProvider::ExecuteJavascriptInOOBEWebUI(
     DictionaryValue* args, IPC::Message* reply_message) {
   std::string javascript, frame_xpath;
diff --git a/chrome/browser/background/background_mode_manager.cc b/chrome/browser/background/background_mode_manager.cc
index 579f031..7dc5c5e 100644
--- a/chrome/browser/background/background_mode_manager.cc
+++ b/chrome/browser/background/background_mode_manager.cc
@@ -63,24 +63,7 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-//  BackgroundModeManager::BackgroundModeData, ui::SimpleMenuModel overrides
-bool BackgroundModeManager::BackgroundModeData::IsCommandIdChecked(
-    int command_id) const {
-  NOTREACHED() << "There are no checked items in the profile submenu.";
-  return false;
-}
-
-bool BackgroundModeManager::BackgroundModeData::IsCommandIdEnabled(
-    int command_id) const {
-  return command_id != IDC_MinimumLabelValue;
-}
-
-bool BackgroundModeManager::BackgroundModeData::GetAcceleratorForCommandId(
-    int command_id, ui::Accelerator* accelerator) {
-  // No accelerators for status icon context menus.
-  return false;
-}
-
+//  BackgroundModeManager::BackgroundModeData, StatusIconMenuModel overrides
 void BackgroundModeManager::BackgroundModeData::ExecuteCommand(
     int item,
     int event_flags) {
@@ -110,14 +93,15 @@
 }
 
 void BackgroundModeManager::BackgroundModeData::BuildProfileMenu(
-    ui::SimpleMenuModel* menu,
-    ui::SimpleMenuModel* containing_menu) {
+    StatusIconMenuModel* menu,
+    StatusIconMenuModel* containing_menu) {
   int position = 0;
   // When there are no background applications, we want to display
   // just a label stating that none are running.
   if (applications_->size() < 1) {
     menu->AddItemWithStringId(IDC_MinimumLabelValue,
                               IDS_BACKGROUND_APP_NOT_INSTALLED);
+    menu->SetCommandIdEnabled(IDC_MinimumLabelValue, false);
   } else {
     for (extensions::ExtensionList::const_iterator cursor =
              applications_->begin();
@@ -459,29 +443,7 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-//  BackgroundModeManager::BackgroundModeData, ui::SimpleMenuModel overrides
-bool BackgroundModeManager::IsCommandIdChecked(
-    int command_id) const {
-  DCHECK(command_id == IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND);
-  return true;
-}
-
-bool BackgroundModeManager::IsCommandIdEnabled(
-    int command_id) const {
-  if (command_id == IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND) {
-    PrefService* service = g_browser_process->local_state();
-    DCHECK(service);
-    return service->IsUserModifiablePreference(prefs::kBackgroundModeEnabled);
-  }
-  return command_id != IDC_MinimumLabelValue;
-}
-
-bool BackgroundModeManager::GetAcceleratorForCommandId(
-    int command_id, ui::Accelerator* accelerator) {
-  // No accelerators for status icon context menus.
-  return false;
-}
-
+//  BackgroundModeManager::BackgroundModeData, StatusIconMenuModel overrides
 void BackgroundModeManager::ExecuteCommand(int command_id, int event_flags) {
   // When a browser window is necessary, we use the first profile. The windows
   // opened for these commands are not profile-specific, so any profile would
@@ -699,7 +661,7 @@
 
   // TODO(rlp): Add current profile color or indicator.
   // Create a context menu item for Chrome.
-  ui::SimpleMenuModel* menu = new ui::SimpleMenuModel(this);
+  scoped_ptr<StatusIconMenuModel> menu(new StatusIconMenuModel(this));
   // Add About item
   menu->AddItem(IDC_ABOUT, l10n_util::GetStringUTF16(IDS_ABOUT));
   menu->AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER);
@@ -724,8 +686,8 @@
       // We should only display the profile in the status icon if it has at
       // least one background app.
       if (bmd->GetBackgroundAppCount() > 0) {
-        ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(bmd);
-        bmd->BuildProfileMenu(submenu, menu);
+        StatusIconMenuModel* submenu = new StatusIconMenuModel(bmd);
+        bmd->BuildProfileMenu(submenu, menu.get());
         profiles_with_apps++;
       }
     }
@@ -738,17 +700,27 @@
     // have any profiles in the cache.
     DCHECK(profile_cache_->GetNumberOfProfiles() == size_t(1) ||
            keep_alive_for_test_);
-    background_mode_data_.begin()->second->BuildProfileMenu(menu, NULL);
+    background_mode_data_.begin()->second->BuildProfileMenu(menu.get(), NULL);
   }
 
   menu->AddSeparator(ui::NORMAL_SEPARATOR);
   menu->AddCheckItemWithStringId(
       IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND,
       IDS_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND);
+  menu->SetCommandIdChecked(IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND,
+                            true);
+
+  PrefService* service = g_browser_process->local_state();
+  DCHECK(service);
+  bool enabled =
+      service->IsUserModifiablePreference(prefs::kBackgroundModeEnabled);
+  menu->SetCommandIdEnabled(IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND,
+                            enabled);
+
   menu->AddItemWithStringId(IDC_EXIT, IDS_EXIT);
 
-  context_menu_ = menu;
-  status_icon_->SetContextMenu(menu);
+  context_menu_ = menu.get();
+  status_icon_->SetContextMenu(menu.Pass());
 }
 
 void BackgroundModeManager::RemoveStatusTrayIcon() {
diff --git a/chrome/browser/background/background_mode_manager.h b/chrome/browser/background/background_mode_manager.h
index e1c8051..118d386 100644
--- a/chrome/browser/background/background_mode_manager.h
+++ b/chrome/browser/background/background_mode_manager.h
@@ -12,10 +12,10 @@
 #include "chrome/browser/background/background_application_list_model.h"
 #include "chrome/browser/profiles/profile_info_cache_observer.h"
 #include "chrome/browser/status_icons/status_icon.h"
+#include "chrome/browser/status_icons/status_icon_menu_model.h"
 #include "components/browser_context_keyed_service/browser_context_keyed_service.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
-#include "ui/base/models/simple_menu_model.h"
 
 class Browser;
 class CommandLine;
@@ -47,7 +47,7 @@
     : public content::NotificationObserver,
       public BackgroundApplicationListModel::Observer,
       public ProfileInfoCacheObserver,
-      public ui::SimpleMenuModel::Delegate {
+      public StatusIconMenuModel::Delegate {
  public:
   BackgroundModeManager(CommandLine* command_line,
                         ProfileInfoCache* profile_cache);
@@ -88,7 +88,7 @@
   FRIEND_TEST_ALL_PREFIXES(BackgroundAppBrowserTest,
                            ReloadBackgroundApp);
 
-  class BackgroundModeData : public ui::SimpleMenuModel::Delegate {
+  class BackgroundModeData : public StatusIconMenuModel::Delegate {
    public:
     explicit BackgroundModeData(
         int command_id,
@@ -98,12 +98,7 @@
     // The cached list of BackgroundApplications.
     scoped_ptr<BackgroundApplicationListModel> applications_;
 
-    // Overrides from SimpleMenuModel::Delegate implementation.
-    virtual bool IsCommandIdChecked(int command_id) const OVERRIDE;
-    virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE;
-    virtual bool GetAcceleratorForCommandId(int command_id,
-                                            ui::Accelerator* accelerator)
-                                            OVERRIDE;
+    // Overrides from StatusIconMenuModel::Delegate implementation.
     virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE;
 
     // Returns a browser window, or creates one if none are open. Used by
@@ -118,8 +113,8 @@
     // be a submenu in the case of multi-profiles or the main menu in the case
     // of the single profile case. If containing_menu is valid, we will add
     // menu as a submenu to it.
-    void BuildProfileMenu(ui::SimpleMenuModel* menu,
-                          ui::SimpleMenuModel* containing_menu);
+    void BuildProfileMenu(StatusIconMenuModel* menu,
+                          StatusIconMenuModel* containing_menu);
 
     // Set the name associated with this background mode data for displaying in
     // the status tray.
@@ -175,12 +170,7 @@
   virtual void OnProfileNameChanged(const base::FilePath& profile_path,
                                     const string16& old_profile_name) OVERRIDE;
 
-  // Overrides from SimpleMenuModel::Delegate implementation.
-  virtual bool IsCommandIdChecked(int command_id) const OVERRIDE;
-  virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE;
-  virtual bool GetAcceleratorForCommandId(int command_id,
-                                          ui::Accelerator* accelerator)
-                                          OVERRIDE;
+  // Overrides from StatusIconMenuModel::Delegate implementation.
   virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE;
 
   // Invoked when an extension is installed so we can ensure that
@@ -295,7 +285,7 @@
 
   // Reference to our status icon's context menu (if any) - owned by the
   // status_icon_.
-  ui::SimpleMenuModel* context_menu_;
+  StatusIconMenuModel* context_menu_;
 
   // Set to true when we are running in background mode. Allows us to track our
   // current background state so we can take the appropriate action when the
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 49ba340..21e11cd 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -154,7 +154,7 @@
 
       <include name="IDR_LOCAL_DISCOVERY_HTML" file="resources\local_discovery\local_discovery.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
       <include name="IDR_LOCAL_DISCOVERY_CSS" file="resources\local_discovery\local_discovery.css" type="BINDATA" />
-      <include name="IDR_LOCAL_DISCOVERY_JS" file="resources\local_discovery\local_discovery.js" type="BINDATA" />
+      <include name="IDR_LOCAL_DISCOVERY_JS" file="resources\local_discovery\local_discovery.js" flattenhtml="true" type="BINDATA" />
 
       <include name="IDR_INSTANT_IFRAME_VALIDATION_JS" file="resources\local_ntp\instant_iframe_validation.js" type="BINDATA" />
       <include name="IDR_LOCAL_NTP_HTML" file="resources\local_ntp\local_ntp.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
diff --git a/chrome/browser/browsing_data/browsing_data_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_helper_unittest.cc
index d0fbd04..26f04ae 100644
--- a/chrome/browser/browsing_data/browsing_data_helper_unittest.cc
+++ b/chrome/browser/browsing_data/browsing_data_helper_unittest.cc
@@ -59,7 +59,7 @@
 
 TEST_F(BrowsingDataHelperTest, WebSafeSchemesAreWebSafe) {
   EXPECT_TRUE(IsWebScheme(chrome::kHttpScheme));
-  EXPECT_TRUE(IsWebScheme(chrome::kHttpsScheme));
+  EXPECT_TRUE(IsWebScheme(content::kHttpsScheme));
   EXPECT_TRUE(IsWebScheme(chrome::kFtpScheme));
   EXPECT_TRUE(IsWebScheme(chrome::kDataScheme));
   EXPECT_TRUE(IsWebScheme("feed"));
@@ -83,7 +83,7 @@
 
 TEST_F(BrowsingDataHelperTest, WebSafeSchemesAreNotExtensions) {
   EXPECT_FALSE(IsExtensionScheme(chrome::kHttpScheme));
-  EXPECT_FALSE(IsExtensionScheme(chrome::kHttpsScheme));
+  EXPECT_FALSE(IsExtensionScheme(content::kHttpsScheme));
   EXPECT_FALSE(IsExtensionScheme(chrome::kFtpScheme));
   EXPECT_FALSE(IsExtensionScheme(chrome::kDataScheme));
   EXPECT_FALSE(IsExtensionScheme("feed"));
diff --git a/chrome/browser/browsing_data/cookies_tree_model.cc b/chrome/browser/browsing_data/cookies_tree_model.cc
index 58abc27..f894e75 100644
--- a/chrome/browser/browsing_data/cookies_tree_model.cc
+++ b/chrome/browser/browsing_data/cookies_tree_model.cc
@@ -1229,7 +1229,7 @@
     if (!origin.is_valid()) {
       // Domain Bound Cert.  Make a valid URL to satisfy the
       // CookieTreeRootNode::GetOrCreateHostNode interface.
-      origin = GURL(std::string(chrome::kHttpsScheme) +
+      origin = GURL(std::string(content::kHttpsScheme) +
           content::kStandardSchemeSeparator +
           cert_info->server_identifier() + "/");
     }
diff --git a/chrome/browser/chrome_browser_field_trials_desktop.cc b/chrome/browser/chrome_browser_field_trials_desktop.cc
index 92b6645..7096ba8 100644
--- a/chrome/browser/chrome_browser_field_trials_desktop.cc
+++ b/chrome/browser/chrome_browser_field_trials_desktop.cc
@@ -27,10 +27,6 @@
 #include "net/spdy/spdy_session.h"
 #include "ui/base/layout.h"
 
-#if defined(OS_WIN)
-#include "net/socket/tcp_client_socket_win.h"
-#endif  // defined(OS_WIN)
-
 namespace chrome {
 
 namespace {
@@ -118,31 +114,6 @@
   // TODO(gavinp,rvargas): Re-add 400 group to field trial if results justify.
 }
 
-void WindowsOverlappedTCPReadsFieldTrial(
-    const CommandLine& parsed_command_line) {
-#if defined(OS_WIN)
-  if (parsed_command_line.HasSwitch(switches::kOverlappedRead)) {
-    std::string option =
-        parsed_command_line.GetSwitchValueASCII(switches::kOverlappedRead);
-    if (LowerCaseEqualsASCII(option, "off"))
-      net::TCPClientSocketWin::DisableOverlappedReads();
-  } else {
-    const base::FieldTrial::Probability kDivisor = 2;  // 1 in 2 chance
-    const base::FieldTrial::Probability kOverlappedReadProbability = 1;
-    scoped_refptr<base::FieldTrial> overlapped_reads_trial(
-        base::FieldTrialList::FactoryGetFieldTrial(
-            "OverlappedReadImpact", kDivisor, "OverlappedReadEnabled",
-            2013, 6, 1, base::FieldTrial::SESSION_RANDOMIZED, NULL));
-    int overlapped_reads_disabled_group =
-        overlapped_reads_trial->AppendGroup("OverlappedReadDisabled",
-                                            kOverlappedReadProbability);
-    int assigned_group = overlapped_reads_trial->group();
-    if (assigned_group == overlapped_reads_disabled_group)
-      net::TCPClientSocketWin::DisableOverlappedReads();
-  }
-#endif
-}
-
 void SetupLowLatencyFlashAudioFieldTrial() {
   scoped_refptr<base::FieldTrial> trial(
       base::FieldTrialList::FactoryGetFieldTrial(
@@ -166,7 +137,6 @@
   SetupInfiniteCacheFieldTrial();
   SetupCacheSensitivityAnalysisFieldTrial();
   DisableShowProfileSwitcherTrialIfNecessary();
-  WindowsOverlappedTCPReadsFieldTrial(parsed_command_line);
   SetupAppLauncherFieldTrial(local_state);
   SetupLowLatencyFlashAudioFieldTrial();
 }
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 380aa2a..2d60399 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -81,6 +81,7 @@
 #include "chrome/browser/ssl/ssl_tab_helper.h"
 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
 #include "chrome/browser/tab_contents/tab_util.h"
+#include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
 #include "chrome/browser/ui/chrome_select_file_policy.h"
 #include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/browser/ui/tab_contents/chrome_web_contents_view_delegate.h"
@@ -169,10 +170,6 @@
 #include "chrome/browser/crash_handler_host_linux.h"
 #endif
 
-#if !defined(OS_ANDROID)
-#include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
-#endif
-
 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
 #include "chrome/browser/captive_portal/captive_portal_tab_helper.h"
 #endif
@@ -525,7 +522,6 @@
   g_io_thread_application_locale.Get() = locale;
 }
 
-#if !defined(OS_ANDROID)
 struct BlockedPopupParams {
   BlockedPopupParams(const GURL& target_url,
                      const content::Referrer& referrer,
@@ -572,7 +568,6 @@
                                 params.user_gesture,
                                 params.opener_suppressed);
 }
-#endif
 
 }  // namespace
 
@@ -1996,7 +1991,6 @@
     return false;
   }
 
-#if !defined(OS_ANDROID)
   if (is_guest)
     return true;
 
@@ -2026,7 +2020,6 @@
                                                           opener_id)));
     return false;
   }
-#endif
 
   return true;
 }
diff --git a/chrome/browser/chromeos/OWNERS b/chrome/browser/chromeos/OWNERS
index e435523..b6d4773 100644
--- a/chrome/browser/chromeos/OWNERS
+++ b/chrome/browser/chromeos/OWNERS
@@ -9,4 +9,4 @@
 xiyuan@chromium.org
 zelidrag@chromium.org
 
-per-file proxy*=pneubeck@chromium.org
+per-file *proxy*=pneubeck@chromium.org
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_launch_error.cc b/chrome/browser/chromeos/app_mode/kiosk_app_launch_error.cc
index 3461d9f..4e0c076 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_launch_error.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_launch_error.cc
@@ -28,6 +28,7 @@
     case HAS_PENDING_LAUNCH:
     case NOT_KIOSK_ENABLED:
     case UNABLE_TO_RETRIEVE_HASH:
+    case POLICY_LOAD_FAILED:
       return l10n_util::GetStringUTF8(IDS_KIOSK_APP_FAILED_TO_LAUNCH);
 
     case CRYPTOHOMED_NOT_RUNNING:
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h b/chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h
index 6131665..2ee9809 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h
@@ -24,6 +24,7 @@
     USER_CANCEL,              // Canceled by user.
     NOT_KIOSK_ENABLED,        // Not a kiosk enabled app.
     UNABLE_TO_RETRIEVE_HASH,  // Unable to retrieve username hash.
+    POLICY_LOAD_FAILED,       // Failed to load policy for kiosk account.
   };
 
   // Returns a message for given |error|.
diff --git a/chrome/browser/chromeos/app_mode/kiosk_profile_loader.cc b/chrome/browser/chromeos/app_mode/kiosk_profile_loader.cc
index 530cc4d..97c002d 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_profile_loader.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_profile_loader.cc
@@ -8,8 +8,10 @@
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
 #include "base/message_loop/message_loop.h"
+#include "base/strings/string_util.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
+#include "chrome/browser/chromeos/login/login_status_consumer.h"
 #include "chrome/browser/chromeos/login/login_utils.h"
 #include "chrome/browser/chromeos/login/user_manager.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
@@ -19,6 +21,7 @@
 #include "chromeos/dbus/cryptohome_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "content/public/browser/browser_thread.h"
+#include "google_apis/gaia/gaia_auth_util.h"
 
 using content::BrowserThread;
 
@@ -28,6 +31,40 @@
 
 void IgnoreResult(bool mount_success, cryptohome::MountError mount_error) {}
 
+// Converts a user id to the old non-canonicalized format. We need this
+// old user id to cleanup crypthomes from older verisons.
+// TODO(tengs): Remove this after all clients migrated to new home.
+std::string GetOldUserId(const std::string& user_id) {
+  size_t separator_pos = user_id.find('@');
+  if (separator_pos != user_id.npos && separator_pos < user_id.length() - 1) {
+    std::string username = user_id.substr(0, separator_pos);
+    std::string domain = user_id.substr(separator_pos + 1);
+    StringToUpperASCII(&username);
+    return username + "@" + domain;
+  } else {
+    LOG(ERROR) << "User id "
+        << user_id << " does not seem to be a valid format";
+    NOTREACHED();
+    return user_id;
+  }
+}
+
+KioskAppLaunchError::Error LoginFailureToKioskAppLaunchError(
+    const LoginFailure& error) {
+  switch (error.reason()) {
+    case LoginFailure::COULD_NOT_MOUNT_TMPFS:
+    case LoginFailure::COULD_NOT_MOUNT_CRYPTOHOME:
+      return KioskAppLaunchError::UNABLE_TO_MOUNT;
+    case LoginFailure::DATA_REMOVAL_FAILED:
+      return KioskAppLaunchError::UNABLE_TO_REMOVE;
+    case LoginFailure::USERNAME_HASH_FAILED:
+      return KioskAppLaunchError::UNABLE_TO_RETRIEVE_HASH;
+    default:
+      NOTREACHED();
+      return KioskAppLaunchError::UNABLE_TO_MOUNT;
+  }
+}
+
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -35,7 +72,7 @@
 // and running by issuing an IsMounted call. If the call does not go through
 // and chromeos::DBUS_METHOD_CALL_SUCCESS is not returned, it will retry after
 // some time out and at the maximum five times before it gives up. Upon
-// success, it resumes the launch by calling KioskProfileLoader::StartMount.
+// success, it resumes the launch by logging in as a kiosk mode account.
 
 class KioskProfileLoader::CryptohomedChecker
     : public base::SupportsWeakPtr<CryptohomedChecker> {
@@ -84,7 +121,7 @@
 
   void ReportCheckResult(KioskAppLaunchError::Error error) {
     if (error == KioskAppLaunchError::NONE)
-      loader_->StartMount();
+      loader_->LoginAsKioskAccount();
     else
       loader_->ReportLaunchResult(error);
   }
@@ -95,61 +132,6 @@
   DISALLOW_COPY_AND_ASSIGN(CryptohomedChecker);
 };
 
-////////////////////////////////////////////////////////////////////////////////
-// KioskProfileLoader::ProfileLoader actually creates or loads the app profile.
-
-class KioskProfileLoader::ProfileLoader : public LoginUtils::Delegate {
- public:
-  ProfileLoader(KioskAppManager* kiosk_app_manager,
-                KioskProfileLoader* kiosk_profile_loader)
-      : kiosk_profile_loader_(kiosk_profile_loader),
-        user_id_(kiosk_profile_loader->user_id_) {
-    CHECK(!user_id_.empty());
-  }
-
-  virtual ~ProfileLoader() {
-    LoginUtils::Get()->DelegateDeleted(this);
-  }
-
-  void Start() {
-    cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername(
-        user_id_,
-        base::Bind(&ProfileLoader::OnUsernameHashRetrieved,
-                   base::Unretained(this)));
-  }
-
- private:
-  void OnUsernameHashRetrieved(bool success,
-                               const std::string& username_hash) {
-    if (!success) {
-      LOG(ERROR) << "Unable to retrieve username hash for user '" << user_id_
-                 << "'.";
-      kiosk_profile_loader_->ReportLaunchResult(
-          KioskAppLaunchError::UNABLE_TO_RETRIEVE_HASH);
-      return;
-    }
-    LoginUtils::Get()->PrepareProfile(
-        UserContext(user_id_,
-                    std::string(),   // password
-                    std::string(),   // auth_code
-                    username_hash),
-        std::string(),  // display email
-        false,  // using_oauth
-        false,  // has_cookies
-        false,  // has_active_session
-        this);
-  }
-
-  // LoginUtils::Delegate overrides:
-  virtual void OnProfilePrepared(Profile* profile) OVERRIDE {
-    kiosk_profile_loader_->OnProfilePrepared(profile);
-  }
-
-  KioskProfileLoader* kiosk_profile_loader_;
-  std::string user_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(ProfileLoader);
-};
 
 ////////////////////////////////////////////////////////////////////////////////
 // KioskProfileLoader
@@ -159,8 +141,7 @@
                                        Delegate* delegate)
     : kiosk_app_manager_(kiosk_app_manager),
       app_id_(app_id),
-      delegate_(delegate),
-      remove_attempted_(false) {
+      delegate_(delegate) {
   KioskAppManager::App app;
   CHECK(kiosk_app_manager_->GetApp(app_id_, &app));
   user_id_ = app.user_id;
@@ -170,11 +151,26 @@
 
 void KioskProfileLoader::Start() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  login_performer_.reset();
+  cryptohomed_checker_.reset(new CryptohomedChecker(this));
+  cryptohomed_checker_->StartCheck();
+}
 
-  // Check cryptohomed. If all goes good, flow goes to StartMount. Otherwise
-  // it goes to ReportLaunchResult with failure.
-  crytohomed_checker.reset(new CryptohomedChecker(this));
-  crytohomed_checker->StartCheck();
+void KioskProfileLoader::LoginAsKioskAccount() {
+  // Nuke old home that uses |app_id_| as cryptohome user id.
+  // TODO(xiyuan): Remove this after all clients migrated to new home.
+  cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove(
+      app_id_,
+      base::Bind(&IgnoreResult));
+
+  // Nuke old home that uses non-canonicalized user id.
+  // TODO(tengs): Remove this after all clients migrated to new home.
+  cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove(
+      GetOldUserId(user_id_),
+      base::Bind(&IgnoreResult));
+
+  login_performer_.reset(new LoginPerformer(this));
+  login_performer_->LoginAsKioskAccount(user_id_);
 }
 
 void KioskProfileLoader::ReportLaunchResult(KioskAppLaunchError::Error error) {
@@ -185,60 +181,44 @@
   }
 }
 
-void KioskProfileLoader::StartMount() {
-  // Nuke old home that uses |app_id_| as cryptohome user id.
-  // TODO(xiyuan): Remove this after all clients migrated to new home.
-  cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove(
-      app_id_,
-      base::Bind(&IgnoreResult));
+void KioskProfileLoader::OnLoginSuccess(
+    const UserContext& user_context,
+    bool pending_requests,
+    bool using_oauth)  {
+  // LoginPerformer will delete itself.
+  login_performer_->set_delegate(NULL);
+  ignore_result(login_performer_.release());
 
-  cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountPublic(
-      user_id_,
-      cryptohome::CREATE_IF_MISSING,
-      base::Bind(&KioskProfileLoader::MountCallback,
-                 base::Unretained(this)));
+  LoginUtils::Get()->PrepareProfile(user_context,
+                                    std::string(),  // display email
+                                    false,  // using_oauth
+                                    false,  // has_cookies
+                                    false,  // has_active_session
+                                    this);
 }
 
-void KioskProfileLoader::MountCallback(bool mount_success,
-                                       cryptohome::MountError mount_error) {
-  if (mount_success) {
-    profile_loader_.reset(new ProfileLoader(kiosk_app_manager_, this));
-    profile_loader_->Start();
-    return;
-  }
-
-  if (!remove_attempted_) {
-    LOG(ERROR) << "Attempt to remove app cryptohome because of mount failure"
-               << ", mount error=" << mount_error;
-
-    remove_attempted_ = true;
-    AttemptRemove();
-    return;
-  }
-
-  LOG(ERROR) << "Failed to mount app cryptohome, error=" << mount_error;
-  ReportLaunchResult(KioskAppLaunchError::UNABLE_TO_MOUNT);
+void KioskProfileLoader::OnLoginFailure(const LoginFailure& error) {
+  ReportLaunchResult(LoginFailureToKioskAppLaunchError(error));
 }
 
-void KioskProfileLoader::AttemptRemove() {
-  cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove(
-      user_id_,
-      base::Bind(&KioskProfileLoader::RemoveCallback,
-                 base::Unretained(this)));
+void KioskProfileLoader::WhiteListCheckFailed(const std::string& email) {
+  NOTREACHED();
 }
 
-void KioskProfileLoader::RemoveCallback(bool success,
-                                      cryptohome::MountError return_code) {
-  if (success) {
-    StartMount();
-    return;
-  }
+void KioskProfileLoader::PolicyLoadFailed() {
+  ReportLaunchResult(KioskAppLaunchError::POLICY_LOAD_FAILED);
+}
 
-  LOG(ERROR) << "Failed to remove app cryptohome, error=" << return_code;
-  ReportLaunchResult(KioskAppLaunchError::UNABLE_TO_REMOVE);
+void KioskProfileLoader::OnOnlineChecked(
+    const std::string& email, bool success) {
+  NOTREACHED();
 }
 
 void KioskProfileLoader::OnProfilePrepared(Profile* profile) {
+  // This object could be deleted any time after successfully reporting
+  // a profile load, so invalidate the LoginUtils delegate now.
+  LoginUtils::Get()->DelegateDeleted(this);
+
   UserManager::Get()->SessionStarted();
   delegate_->OnProfileLoaded(profile);
   ReportLaunchResult(KioskAppLaunchError::NONE);
diff --git a/chrome/browser/chromeos/app_mode/kiosk_profile_loader.h b/chrome/browser/chromeos/app_mode/kiosk_profile_loader.h
index 7c54304..46d47eb 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_profile_loader.h
+++ b/chrome/browser/chromeos/app_mode/kiosk_profile_loader.h
@@ -11,7 +11,8 @@
 #include "base/callback.h"
 #include "base/memory/scoped_ptr.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h"
-#include "third_party/cros_system_api/dbus/service_constants.h"
+#include "chrome/browser/chromeos/login/login_performer.h"
+#include "chrome/browser/chromeos/login/login_utils.h"
 
 class Profile;
 
@@ -19,10 +20,11 @@
 
 class KioskAppManager;
 
-// KioskProfileLoader loads a special profile for a given app. It first attempts
-// to mount a cryptohome for the app. If the mount is successful, it prepares
-// app profile then calls the delegate.
-class KioskProfileLoader {
+// KioskProfileLoader loads a special profile for a given app. It first
+// attempts to login for the app's generated user id. If the login is
+// successful, it prepares app profile then calls the delegate.
+class KioskProfileLoader : public LoginPerformer::Delegate,
+                           public LoginUtils::Delegate {
  public:
   class Delegate {
    public:
@@ -37,36 +39,37 @@
                      const std::string& app_id,
                      Delegate* delegate);
 
-  ~KioskProfileLoader();
+  virtual ~KioskProfileLoader();
 
   // Starts profile load. Calls delegate on success or failure.
   void Start();
 
  private:
   class CryptohomedChecker;
-  class ProfileLoader;
 
+  void LoginAsKioskAccount();
   void ReportLaunchResult(KioskAppLaunchError::Error error);
 
-  void StartMount();
-  void MountCallback(bool mount_success, cryptohome::MountError mount_error);
+  // LoginPerformer::Delegate overrides
+  virtual void OnLoginSuccess(
+      const UserContext& user_context,
+      bool pending_requests,
+      bool using_oauth) OVERRIDE;
+  virtual void OnLoginFailure(const LoginFailure& error) OVERRIDE;
+  virtual void WhiteListCheckFailed(const std::string& email) OVERRIDE;
+  virtual void PolicyLoadFailed() OVERRIDE;
+  virtual void OnOnlineChecked(
+      const std::string& email, bool success) OVERRIDE;
 
-  void AttemptRemove();
-  void RemoveCallback(bool success,
-                      cryptohome::MountError return_code);
-
-  void OnProfilePrepared(Profile* profile);
+  // LoginUtils::Delegate implementation:
+  virtual void OnProfilePrepared(Profile* profile) OVERRIDE;
 
   KioskAppManager* kiosk_app_manager_;
   const std::string app_id_;
   std::string user_id_;
   Delegate* delegate_;
-
-  scoped_ptr<CryptohomedChecker> crytohomed_checker;
-  scoped_ptr<ProfileLoader> profile_loader_;
-
-  // Whether remove existing cryptohome has attempted.
-  bool remove_attempted_;
+  scoped_ptr<CryptohomedChecker> cryptohomed_checker_;
+  scoped_ptr<LoginPerformer> login_performer_;
 
   DISALLOW_COPY_AND_ASSIGN(KioskProfileLoader);
 };
diff --git a/chrome/browser/chromeos/app_mode/startup_app_launcher.cc b/chrome/browser/chromeos/app_mode/startup_app_launcher.cc
index 9f6bc11..d7d26a1 100644
--- a/chrome/browser/chromeos/app_mode/startup_app_launcher.cc
+++ b/chrome/browser/chromeos/app_mode/startup_app_launcher.cc
@@ -203,7 +203,7 @@
 }
 
 void StartupAppLauncher::OnLaunchFailure(KioskAppLaunchError::Error error) {
-  LOG(ERROR) << "App launch failed";
+  LOG(ERROR) << "App launch failed, error: " << error;
   DCHECK_NE(KioskAppLaunchError::NONE, error);
 
   FOR_EACH_OBSERVER(Observer, observer_list_, OnLaunchFailed(error));
@@ -266,9 +266,7 @@
     return;
   }
 
-  // TODO: revert this.
-  LOG(ERROR) << "Failed to install app with error: " << error << ".";
-  LOG(ERROR) << "            " << app_id_;
+  LOG(ERROR) << "App install failed: " << error;
   OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
 }
 
diff --git a/chrome/browser/chromeos/app_mode/startup_app_launcher.h b/chrome/browser/chromeos/app_mode/startup_app_launcher.h
index 5ed968e..6f24442 100644
--- a/chrome/browser/chromeos/app_mode/startup_app_launcher.h
+++ b/chrome/browser/chromeos/app_mode/startup_app_launcher.h
@@ -13,7 +13,7 @@
 #include "base/observer_list.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 #include "net/base/network_change_notifier.h"
 
 class Profile;
diff --git a/chrome/browser/chromeos/attestation/platform_verification_flow.cc b/chrome/browser/chromeos/attestation/platform_verification_flow.cc
index 023d667..3dc6997 100644
--- a/chrome/browser/chromeos/attestation/platform_verification_flow.cc
+++ b/chrome/browser/chromeos/attestation/platform_verification_flow.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/chromeos/attestation/attestation_ca_client.h"
 #include "chrome/browser/chromeos/login/user_manager.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
+#include "chrome/browser/chromeos/system/statistics_provider.h"
 #include "chromeos/attestation/attestation_flow.h"
 #include "chromeos/cryptohome/async_method_caller.h"
 #include "chromeos/dbus/cryptohome_client.h"
@@ -38,11 +39,13 @@
     cryptohome::AsyncMethodCaller* async_caller,
     CryptohomeClient* cryptohome_client,
     UserManager* user_manager,
+    system::StatisticsProvider* statistics_provider,
     Delegate* delegate)
     : attestation_flow_(attestation_flow),
       async_caller_(async_caller),
       cryptohome_client_(cryptohome_client),
       user_manager_(user_manager),
+      statistics_provider_(statistics_provider),
       delegate_(delegate),
       weak_factory_(this) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
@@ -74,6 +77,28 @@
   cryptohome_client_->TpmAttestationIsEnrolled(dbus_callback);
 }
 
+void PlatformVerificationFlow::CheckPlatformState(
+    const base::Callback<void(bool result)>& callback) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  std::string stat_value;
+  if (!statistics_provider_->GetMachineStatistic(system::kDevSwitchBootMode,
+                                                 &stat_value)) {
+    LOG(ERROR) << __func__ << ": Failed to get boot mode statistic.";
+    callback.Run(false);
+    return;
+  }
+  if (stat_value != "0") {
+    LOG(INFO) << __func__ << ": Statistic indicates developer mode.";
+    callback.Run(false);
+    return;
+  }
+  BoolDBusMethodCallback dbus_callback = base::Bind(
+      &DBusCallback,
+      callback,
+      base::Bind(callback, false));
+  cryptohome_client_->TpmAttestationIsPrepared(dbus_callback);
+}
+
 void PlatformVerificationFlow::CheckConsent(content::WebContents* web_contents,
                                             const std::string& service_id,
                                             const std::string& challenge,
diff --git a/chrome/browser/chromeos/attestation/platform_verification_flow.h b/chrome/browser/chromeos/attestation/platform_verification_flow.h
index c5ffeab..973aa3c 100644
--- a/chrome/browser/chromeos/attestation/platform_verification_flow.h
+++ b/chrome/browser/chromeos/attestation/platform_verification_flow.h
@@ -25,6 +25,10 @@
 class CryptohomeClient;
 class UserManager;
 
+namespace system {
+class StatisticsProvider;
+}
+
 namespace attestation {
 
 class AttestationFlow;
@@ -119,6 +123,7 @@
                            cryptohome::AsyncMethodCaller* async_caller,
                            CryptohomeClient* cryptohome_client,
                            UserManager* user_manager,
+                           system::StatisticsProvider* statistics_provider,
                            Delegate* delegate);
 
   virtual ~PlatformVerificationFlow();
@@ -138,6 +143,13 @@
                             const std::string& challenge,
                             const ChallengeCallback& callback);
 
+  // Performs a quick check to see if platform verification is reasonably
+  // expected to succeed.  The result of the check will be sent to the given
+  // |callback|.  If the |result| is true, then platform verification is
+  // expected to succeed.  However, this result is not authoritative either true
+  // or false.  If an error occurs, |result| will be false.
+  void CheckPlatformState(const base::Callback<void(bool result)>& callback);
+
  private:
   // Checks whether we need to prompt the user for consent before proceeding and
   // invokes the consent UI if so.  All parameters are the same as in
@@ -189,6 +201,7 @@
   cryptohome::AsyncMethodCaller* async_caller_;
   CryptohomeClient* cryptohome_client_;
   UserManager* user_manager_;
+  system::StatisticsProvider* statistics_provider_;
   Delegate* delegate_;
   scoped_ptr<Delegate> default_delegate_;
 
diff --git a/chrome/browser/chromeos/attestation/platform_verification_flow_unittest.cc b/chrome/browser/chromeos/attestation/platform_verification_flow_unittest.cc
index 45b71ab..153548a 100644
--- a/chrome/browser/chromeos/attestation/platform_verification_flow_unittest.cc
+++ b/chrome/browser/chromeos/attestation/platform_verification_flow_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/run_loop.h"
 #include "chrome/browser/chromeos/attestation/platform_verification_flow.h"
 #include "chrome/browser/chromeos/login/mock_user_manager.h"
+#include "chrome/browser/chromeos/system/mock_statistics_provider.h"
 #include "chromeos/attestation/mock_attestation_flow.h"
 #include "chromeos/cryptohome/mock_async_method_caller.h"
 #include "chromeos/dbus/fake_cryptohome_client.h"
@@ -16,7 +17,10 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using testing::_;
+using testing::DoAll;
 using testing::Invoke;
+using testing::Return;
+using testing::SetArgumentPointee;
 using testing::StrictMock;
 using testing::WithArgs;
 
@@ -103,14 +107,14 @@
   bool always_ask_required_;
   bool update_settings_result_;
 
-
   DISALLOW_COPY_AND_ASSIGN(FakeDelegate);
 };
 
 class CustomFakeCryptohomeClient : public FakeCryptohomeClient {
  public:
   CustomFakeCryptohomeClient() : call_status_(DBUS_METHOD_CALL_SUCCESS),
-                                 attestation_enrolled_(true) {}
+                                 attestation_enrolled_(true),
+                                 attestation_prepared_(true) {}
   virtual void TpmAttestationIsEnrolled(
       const BoolDBusMethodCallback& callback) OVERRIDE {
     base::MessageLoop::current()->PostTask(FROM_HERE,
@@ -119,6 +123,14 @@
                                                       attestation_enrolled_));
   }
 
+  virtual void TpmAttestationIsPrepared(
+      const BoolDBusMethodCallback& callback) OVERRIDE {
+    base::MessageLoop::current()->PostTask(FROM_HERE,
+                                           base::Bind(callback,
+                                                      call_status_,
+                                                      attestation_prepared_));
+  }
+
   void set_call_status(DBusMethodCallStatus call_status) {
     call_status_ = call_status;
   }
@@ -127,9 +139,14 @@
     attestation_enrolled_ = attestation_enrolled;
   }
 
+  void set_attestation_prepared(bool attestation_prepared) {
+    attestation_prepared_ = attestation_prepared;
+  }
+
  private:
   DBusMethodCallStatus call_status_;
   bool attestation_enrolled_;
+  bool attestation_prepared_;
 };
 
 }  // namespace
@@ -141,22 +158,33 @@
         ui_thread_(content::BrowserThread::UI, &message_loop_),
         certificate_success_(true),
         sign_challenge_success_(true),
-        result_(PlatformVerificationFlow::INTERNAL_ERROR) {}
+        result_(PlatformVerificationFlow::INTERNAL_ERROR),
+        check_state_result_(false) {}
 
   void SetUp() {
     // Configure a user for the mock user manager.
     mock_user_manager_.SetActiveUser(kTestEmail);
 
+    // Configure the statistics provider to report verified mode.
+    EXPECT_CALL(mock_statistics_provider_,
+                GetMachineStatistic(system::kDevSwitchBootMode, _))
+        .WillRepeatedly(DoAll(SetArgumentPointee<1>(std::string("0")),
+                              Return(true)));
+
     // Create a verifier for tests to call.
     verifier_.reset(new PlatformVerificationFlow(&mock_attestation_flow_,
                                                  &mock_async_caller_,
                                                  &fake_cryptohome_client_,
                                                  &mock_user_manager_,
+                                                 &mock_statistics_provider_,
                                                  &fake_delegate_));
 
-    // Create a callback for tests to use with verifier_.
+    // Create callbacks for tests to use with verifier_.
     callback_ = base::Bind(&PlatformVerificationFlowTest::FakeChallengeCallback,
                            base::Unretained(this));
+    check_state_callback_ = base::Bind(
+        &PlatformVerificationFlowTest::FakeCheckStateCallback,
+        base::Unretained(this));
   }
 
   void TearDown() {
@@ -209,6 +237,10 @@
     certificate_ = certificate;
   }
 
+  void FakeCheckStateCallback(bool result) {
+    check_state_result_ = result;
+  }
+
  protected:
   base::MessageLoop message_loop_;
   content::TestBrowserThread ui_thread_;
@@ -216,6 +248,7 @@
   cryptohome::MockAsyncMethodCaller mock_async_caller_;
   CustomFakeCryptohomeClient fake_cryptohome_client_;
   MockUserManager mock_user_manager_;
+  system::MockStatisticsProvider mock_statistics_provider_;
   FakeDelegate fake_delegate_;
   scoped_ptr<PlatformVerificationFlow> verifier_;
 
@@ -225,11 +258,13 @@
   // Controls result of FakeSignChallenge.
   bool sign_challenge_success_;
 
-  // Callback function and data.
+  // Callback functions and data.
   PlatformVerificationFlow::ChallengeCallback callback_;
   PlatformVerificationFlow::Result result_;
   std::string challenge_response_;
   std::string certificate_;
+  base::Callback<void(bool result)> check_state_callback_;
+  bool check_state_result_;
 };
 
 TEST_F(PlatformVerificationFlowTest, SuccessNoConsent) {
@@ -335,5 +370,60 @@
   EXPECT_EQ(PlatformVerificationFlow::USER_REJECTED, result_);
 }
 
+TEST_F(PlatformVerificationFlowTest, FastCheck) {
+  verifier_->CheckPlatformState(check_state_callback_);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(check_state_result_);
+}
+
+TEST_F(PlatformVerificationFlowTest, FastCheckNoStat) {
+  // Configure the stats provider to fail.
+  EXPECT_CALL(mock_statistics_provider_,
+              GetMachineStatistic(system::kDevSwitchBootMode, _))
+      .WillRepeatedly(Return(false));
+
+  verifier_->CheckPlatformState(check_state_callback_);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(check_state_result_);
+}
+
+TEST_F(PlatformVerificationFlowTest, FastCheckStatDevMode) {
+  // Configure the stats provider to fail.
+  EXPECT_CALL(mock_statistics_provider_,
+              GetMachineStatistic(system::kDevSwitchBootMode, _))
+      .WillRepeatedly(DoAll(SetArgumentPointee<1>(std::string("1")),
+                            Return(true)));
+
+  verifier_->CheckPlatformState(check_state_callback_);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(check_state_result_);
+}
+
+TEST_F(PlatformVerificationFlowTest, FastCheckStatInvalidMode) {
+  // Configure the stats provider to fail.
+  EXPECT_CALL(mock_statistics_provider_,
+              GetMachineStatistic(system::kDevSwitchBootMode, _))
+      .WillRepeatedly(DoAll(SetArgumentPointee<1>(std::string("INVALID")),
+                            Return(true)));
+
+  verifier_->CheckPlatformState(check_state_callback_);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(check_state_result_);
+}
+
+TEST_F(PlatformVerificationFlowTest, FastCheckNoAttestation) {
+  fake_cryptohome_client_.set_attestation_prepared(false);
+  verifier_->CheckPlatformState(check_state_callback_);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(check_state_result_);
+}
+
+TEST_F(PlatformVerificationFlowTest, FastCheckDBusFailure) {
+  fake_cryptohome_client_.set_call_status(DBUS_METHOD_CALL_FAILURE);
+  verifier_->CheckPlatformState(check_state_callback_);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(check_state_result_);
+}
+
 }  // namespace attestation
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/cros/network_library_impl_base.cc b/chrome/browser/chromeos/cros/network_library_impl_base.cc
index 6d38e02..9b169a5 100644
--- a/chrome/browser/chromeos/cros/network_library_impl_base.cc
+++ b/chrome/browser/chromeos/cros/network_library_impl_base.cc
@@ -127,7 +127,7 @@
       network_observers_.find(service_path);
   if (map_iter != network_observers_.end()) {
     map_iter->second->RemoveObserver(observer);
-    if (!map_iter->second->size()) {
+    if (!map_iter->second->might_have_observers()) {
       MonitorNetworkStop(service_path);
       delete map_iter->second;
       network_observers_.erase(map_iter);
@@ -141,7 +141,7 @@
   NetworkObserverMap::iterator map_iter = network_observers_.begin();
   while (map_iter != network_observers_.end()) {
     map_iter->second->RemoveObserver(observer);
-    if (!map_iter->second->size()) {
+    if (!map_iter->second->might_have_observers()) {
       MonitorNetworkStop(map_iter->first);
       delete map_iter->second;
       network_observers_.erase(map_iter++);
diff --git a/chrome/browser/chromeos/drive/change_list_loader.cc b/chrome/browser/chromeos/drive/change_list_loader.cc
index 3e33a6a..8fc97cb 100644
--- a/chrome/browser/chromeos/drive/change_list_loader.cc
+++ b/chrome/browser/chromeos/drive/change_list_loader.cc
@@ -97,6 +97,19 @@
   Load(directory_fetch_info, callback);
 }
 
+void ChangeListLoader::LoadDirectoryFromServer(
+    const std::string& directory_resource_id,
+    const FileOperationCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!callback.is_null());
+
+  scheduler_->GetAboutResource(
+      base::Bind(&ChangeListLoader::LoadDirectoryFromServerAfterGetAbout,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 directory_resource_id,
+                 callback));
+}
+
 void ChangeListLoader::Load(const DirectoryFetchInfo& directory_fetch_info,
                             const FileOperationCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -375,12 +388,13 @@
   DCHECK(resource_list);
   change_lists.push_back(new ChangeList(*resource_list));
 
+  // TODO(hidehiko): Use page token instead of next url.
   GURL next_url;
   if (resource_list->GetNextFeedURL(&next_url) &&
       !next_url.is_empty()) {
     // There is the remaining result so fetch it.
-    scheduler_->ContinueGetResourceList(
-        next_url,
+    scheduler_->GetRemainingChangeList(
+        next_url.spec(),
         base::Bind(&ChangeListLoader::OnGetChangeList,
                    weak_ptr_factory_.GetWeakPtr(),
                    base::Passed(&change_lists),
@@ -427,6 +441,22 @@
                     OnLoadFromServerComplete());
 }
 
+void ChangeListLoader::LoadDirectoryFromServerAfterGetAbout(
+    const std::string& directory_resource_id,
+    const FileOperationCallback& callback,
+    google_apis::GDataErrorCode status,
+    scoped_ptr<google_apis::AboutResource> about_resource) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!callback.is_null());
+
+  if (GDataToFileError(status) == FILE_ERROR_OK)
+    last_known_remote_changestamp_ = about_resource->largest_change_id();
+
+  DoLoadDirectoryFromServer(
+      DirectoryFetchInfo(directory_resource_id, last_known_remote_changestamp_),
+      callback);
+}
+
 void ChangeListLoader::CheckChangestampAndLoadDirectoryIfNeeded(
     const DirectoryFetchInfo& directory_fetch_info,
     int64 local_changestamp,
@@ -503,14 +533,48 @@
                  weak_ptr_factory_.GetWeakPtr(),
                  directory_fetch_info,
                  callback);
-  base::TimeTicks start_time = base::TimeTicks::Now();
   scheduler_->GetResourceListInDirectory(
       directory_fetch_info.resource_id(),
-      base::Bind(&ChangeListLoader::OnGetChangeList,
+      base::Bind(&ChangeListLoader::OnGetFileList,
                  weak_ptr_factory_.GetWeakPtr(),
                  base::Passed(ScopedVector<ChangeList>()),
-                 completion_callback,
-                 start_time));
+                 completion_callback));
+}
+
+void ChangeListLoader::OnGetFileList(
+    ScopedVector<ChangeList> change_lists,
+    const LoadChangeListCallback& callback,
+    google_apis::GDataErrorCode status,
+    scoped_ptr<google_apis::ResourceList> resource_list) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!callback.is_null());
+
+  FileError error = GDataToFileError(status);
+  if (error != FILE_ERROR_OK) {
+    callback.Run(ScopedVector<ChangeList>(), error);
+    return;
+  }
+
+  // Add the current change list to the list of collected lists.
+  DCHECK(resource_list);
+  change_lists.push_back(new ChangeList(*resource_list));
+
+  // TODO(hidehiko): Use page token instead of next url.
+  GURL next_url;
+  if (resource_list->GetNextFeedURL(&next_url) &&
+      !next_url.is_empty()) {
+    // There is the remaining result so fetch it.
+    scheduler_->GetRemainingFileList(
+        next_url.spec(),
+        base::Bind(&ChangeListLoader::OnGetFileList,
+                   weak_ptr_factory_.GetWeakPtr(),
+                   base::Passed(&change_lists),
+                   callback));
+    return;
+  }
+
+  // Run the callback so the client can process the retrieved change lists.
+  callback.Run(change_lists.Pass(), FILE_ERROR_OK);
 }
 
 void
@@ -556,22 +620,38 @@
     return;
   }
 
-  // Grand root will be changed.
-  base::FilePath* changed_directory_path =
-      new base::FilePath(util::GetDriveGrandRootPath());
   // Add "My Drive".
   const std::string& root_resource_id = about_resource->root_folder_id();
+  std::string* local_id = new std::string;
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_,
       FROM_HERE,
       base::Bind(&ResourceMetadata::AddEntry,
                  base::Unretained(resource_metadata_),
-                 util::CreateMyDriveRootEntry(root_resource_id)),
-      base::Bind(&ChangeListLoader::DoLoadDirectoryFromServerAfterRefresh,
+                 util::CreateMyDriveRootEntry(root_resource_id),
+                 local_id),
+      base::Bind(&ChangeListLoader::DoLoadDirectoryFromServerAfterAddMyDrive,
                  weak_ptr_factory_.GetWeakPtr(),
                  directory_fetch_info,
                  callback,
-                 base::Owned(changed_directory_path)));
+                 base::Owned(local_id)));
+}
+
+void ChangeListLoader::DoLoadDirectoryFromServerAfterAddMyDrive(
+    const DirectoryFetchInfo& directory_fetch_info,
+    const FileOperationCallback& callback,
+    std::string* local_id,
+    FileError error) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!callback.is_null());
+  DCHECK_EQ(directory_fetch_info.resource_id(),
+            util::kDriveGrandRootSpecialResourceId);
+
+  const base::FilePath changed_directory_path(util::GetDriveGrandRootPath());
+  DoLoadDirectoryFromServerAfterRefresh(directory_fetch_info,
+                                        callback,
+                                        &changed_directory_path,
+                                        error);
 }
 
 void ChangeListLoader::DoLoadDirectoryFromServerAfterLoad(
diff --git a/chrome/browser/chromeos/drive/change_list_loader.h b/chrome/browser/chromeos/drive/change_list_loader.h
index 307fde3..19a10e8 100644
--- a/chrome/browser/chromeos/drive/change_list_loader.h
+++ b/chrome/browser/chromeos/drive/change_list_loader.h
@@ -92,6 +92,12 @@
   void LoadIfNeeded(const DirectoryFetchInfo& directory_fetch_info,
                     const FileOperationCallback& callback);
 
+  // Loads the directory content from the server, without comparing the
+  // changestamps. The purpose of this function is to update thumbnail URLs
+  // in the directory which can stale over time.
+  void LoadDirectoryFromServer(const std::string& directory_resource_id,
+                               const FileOperationCallback& callback);
+
  private:
   // Starts the resource metadata loading and calls |callback| when it's
   // done. |directory_fetch_info| is used for fast fetch. If there is already
@@ -172,6 +178,14 @@
 
   // ================= Implementation for directory loading =================
 
+  // Part of LoadDirectoryFromServer(), called after the current remote
+  // changestamp is obtained as |about_resource|.
+  void LoadDirectoryFromServerAfterGetAbout(
+      const std::string& directory_resource_id,
+      const FileOperationCallback& callback,
+      google_apis::GDataErrorCode status,
+      scoped_ptr<google_apis::AboutResource> about_resource);
+
   // Compares the directory's changestamp and |last_known_remote_changestamp_|.
   // Starts DoLoadDirectoryFromServer() if the local data is old and runs
   // |callback| when finished. If it is up to date, calls back immediately.
@@ -199,6 +213,13 @@
       google_apis::GDataErrorCode status,
       scoped_ptr<google_apis::AboutResource> about_resource);
 
+  // Part of DoLoadDirectoryFromServer() for the grand root ("/drive").
+  void DoLoadDirectoryFromServerAfterAddMyDrive(
+      const DirectoryFetchInfo& directory_fetch_info,
+      const FileOperationCallback& callback,
+      std::string* local_id,
+      FileError error);
+
   // Part of DoLoadDirectoryFromServer() for a normal directory.
   void DoLoadDirectoryFromServerAfterLoad(
       const DirectoryFetchInfo& directory_fetch_info,
@@ -216,8 +237,7 @@
   // ================= Implementation for other stuff =================
 
   // This function is used to handle pagenation for the result from
-  // JobScheduler::GetChangeList/GetAllResourceList/ContinueGetResourceList/
-  // GetResourceListInDirectory().
+  // JobScheduler::GetChangeList()/GetAllResourceList().
   //
   // After all the change lists are fetched, |callback| will be invoked with
   // the collected change lists.
@@ -227,6 +247,16 @@
                        google_apis::GDataErrorCode status,
                        scoped_ptr<google_apis::ResourceList> resource_list);
 
+  // This function is used to handle pagenation for the result from
+  // JobScheduler::GetResourceListInDirectory().
+  //
+  // After all the file lists are fetched, |callback| will be invoked with
+  // the collected file lists.
+  void OnGetFileList(ScopedVector<ChangeList> change_lists,
+                     const LoadChangeListCallback& callback,
+                     google_apis::GDataErrorCode status,
+                     scoped_ptr<google_apis::ResourceList> resource_list);
+
   // Updates from the whole change list collected in |change_lists|.
   // Record file statistics as UMA histograms.
   //
diff --git a/chrome/browser/chromeos/drive/change_list_processor.cc b/chrome/browser/chromeos/drive/change_list_processor.cc
index 181dc9d..ecdbb48 100644
--- a/chrome/browser/chromeos/drive/change_list_processor.cc
+++ b/chrome/browser/chromeos/drive/change_list_processor.cc
@@ -27,10 +27,15 @@
 
   entries_.resize(resource_list.entries().size());
   size_t entries_index = 0;
+  std::string parent_resource_id;
   for (size_t i = 0; i < resource_list.entries().size(); ++i) {
     if (ConvertToResourceEntry(*resource_list.entries()[i],
-                               &entries_[entries_index]))
+                               &entries_[entries_index],
+                               &parent_resource_id)) {
+      // TODO(hashimoto): Resolve local ID before use. crbug.com/260514
+      entries_[entries_index].set_parent_local_id(parent_resource_id);
       ++entries_index;
+    }
   }
   entries_.resize(entries_index);
 }
@@ -242,7 +247,8 @@
 }
 
 void ChangeListProcessor::AddEntry(const ResourceEntry& entry) {
-  FileError error = resource_metadata_->AddEntry(entry);
+  std::string local_id;
+  FileError error = resource_metadata_->AddEntry(entry, &local_id);
 
   if (error == FILE_ERROR_OK)
     UpdateChangedDirs(entry);
@@ -323,8 +329,10 @@
     }
 
     error = resource_metadata->RefreshEntry(it->first, entry);
-    if (error == FILE_ERROR_NOT_FOUND)  // If refreshing fails, try adding.
-      error = resource_metadata->AddEntry(entry);
+    if (error == FILE_ERROR_NOT_FOUND) {  // If refreshing fails, try adding.
+      std::string local_id;
+      error = resource_metadata->AddEntry(entry, &local_id);
+    }
 
     if (error != FILE_ERROR_OK)
       return error;
diff --git a/chrome/browser/chromeos/drive/drive_app_registry.cc b/chrome/browser/chromeos/drive/drive_app_registry.cc
index 9e4bae6..8b0af43 100644
--- a/chrome/browser/chromeos/drive/drive_app_registry.cc
+++ b/chrome/browser/chromeos/drive/drive_app_registry.cc
@@ -11,7 +11,6 @@
 
 #include "base/files/file_path.h"
 #include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/chromeos/drive/job_scheduler.h"
 #include "chrome/browser/google_apis/drive_api_parser.h"
@@ -56,8 +55,8 @@
     const google_apis::InstalledApp::IconList& app_icons,
     const google_apis::InstalledApp::IconList& document_icons,
     const std::string& web_store_id,
-    const string16& app_name,
-    const string16& object_type,
+    const std::string& app_name,
+    const std::string& object_type,
     bool is_primary_selector)
     : app_id(app_id),
       app_icons(app_icons),
@@ -77,7 +76,7 @@
     const GURL& product_link,
     const google_apis::InstalledApp::IconList& app_icons,
     const google_apis::InstalledApp::IconList& document_icons,
-    const string16& object_type,
+    const std::string& object_type,
     const std::string& app_id,
     bool is_primary_selector)
     : product_link(product_link),
@@ -107,7 +106,7 @@
 void DriveAppRegistry::GetAppsForFile(
     const base::FilePath& file_path,
     const std::string& mime_type,
-    ScopedVector<DriveAppInfo>* apps) {
+    ScopedVector<DriveAppInfo>* apps) const {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   SelectorAppList result_map;
@@ -163,12 +162,16 @@
   }
 
   DCHECK(app_list);
+  UpdateFromAppList(*app_list);
+}
 
+void DriveAppRegistry::UpdateFromAppList(
+    const google_apis::AppList& app_list) {
   url_to_name_map_.clear();
   STLDeleteValues(&app_extension_map_);
   STLDeleteValues(&app_mimetypes_map_);
-  for (size_t i = 0; i < app_list->items().size(); ++i) {
-    const google_apis::AppResource& app = *app_list->items()[i];
+  for (size_t i = 0; i < app_list.items().size(); ++i) {
+    const google_apis::AppResource& app = *app_list.items()[i];
     if (app.product_url().is_empty())
       continue;
 
@@ -242,7 +245,7 @@
         *value, new DriveAppFileSelector(product_link,
                                          app_icons,
                                          document_icons,
-                                         UTF8ToUTF16(object_type),
+                                         object_type,
                                          app_id,
                                          is_primary_selector)));
   }
@@ -251,7 +254,7 @@
 void DriveAppRegistry::FindAppsForSelector(
     const std::string& file_selector,
     const DriveAppFileSelectorMap& map,
-    SelectorAppList* apps) {
+    SelectorAppList* apps) const {
   for (DriveAppFileSelectorMap::const_iterator it = map.find(file_selector);
        it != map.end() && it->first == file_selector; ++it) {
     const DriveAppFileSelector* web_app = it->second;
@@ -275,10 +278,30 @@
                          web_app->app_icons,
                          web_app->document_icons,
                          web_store_id,
-                         UTF8ToUTF16(product_iter->second),   // app name.
+                         product_iter->second,  // app name
                          web_app->object_type,
                          web_app->is_primary_selector)));
   }
 }
 
+namespace util {
+
+GURL FindPreferredIcon(
+    const google_apis::InstalledApp::IconList& icons,
+    int preferred_size) {
+  if (icons.empty())
+    return GURL();
+
+  google_apis::InstalledApp::IconList sorted_icons = icons;
+  std::sort(sorted_icons.begin(), sorted_icons.end());
+  GURL result = sorted_icons.rbegin()->second;
+  for (google_apis::InstalledApp::IconList::const_reverse_iterator
+           iter = sorted_icons.rbegin();
+       iter != sorted_icons.rend() && iter->first >= preferred_size; ++iter) {
+    result = iter->second;
+  }
+  return result;
+}
+
+}  // namespace util
 }  // namespace drive
diff --git a/chrome/browser/chromeos/drive/drive_app_registry.h b/chrome/browser/chromeos/drive/drive_app_registry.h
index 2b89a41..a3e86e5 100644
--- a/chrome/browser/chromeos/drive/drive_app_registry.h
+++ b/chrome/browser/chromeos/drive/drive_app_registry.h
@@ -36,8 +36,8 @@
                const google_apis::InstalledApp::IconList& app_icons,
                const google_apis::InstalledApp::IconList& document_icons,
                const std::string& web_store_id,
-               const string16& app_name,
-               const string16& object_type,
+               const std::string& app_name,
+               const std::string& object_type,
                bool is_primary_selector);
   ~DriveAppInfo();
 
@@ -52,9 +52,9 @@
   // Web store id/extension id;
   std::string web_store_id;
   // App name.
-  string16 app_name;
+  std::string app_name;
   // Object (file) type description handled by this app.
-  string16 object_type;
+  std::string object_type;
   // Is app the primary selector for file (default open action).
   bool is_primary_selector;
 };
@@ -68,11 +68,14 @@
   // Returns a list of web app information for the |file| with |mime_type|.
   void GetAppsForFile(const base::FilePath& file_path,
                       const std::string& mime_type,
-                      ScopedVector<DriveAppInfo>* apps);
+                      ScopedVector<DriveAppInfo>* apps) const;
 
   // Updates this registry by fetching the data from the server.
   void Update();
 
+  // Updates this registry from the |app_list|.
+  void UpdateFromAppList(const google_apis::AppList& app_list);
+
  private:
   // Defines application details that are associated with a given
   // file extension or content mimetype.
@@ -81,7 +84,7 @@
         const GURL& product_link,
         const google_apis::InstalledApp::IconList& app_icons,
         const google_apis::InstalledApp::IconList& document_icons,
-        const string16& object_type,
+        const std::string& object_type,
         const std::string& app_id,
         bool is_primary_selector);
     ~DriveAppFileSelector();
@@ -94,7 +97,7 @@
     // a side in pixels).
     google_apis::InstalledApp::IconList document_icons;
     // Object (file) type description.
-    string16 object_type;
+    std::string object_type;
     // Drive app id
     std::string app_id;
     // True if the selector is the default one. The default selector should
@@ -132,7 +135,7 @@
   // Finds matching |apps| from |map| based on provided file |selector|.
   void FindAppsForSelector(const std::string& selector,
                            const DriveAppFileSelectorMap& map,
-                           SelectorAppList* apps);
+                           SelectorAppList* apps) const;
 
   JobScheduler* scheduler_;
 
@@ -153,6 +156,21 @@
   DISALLOW_COPY_AND_ASSIGN(DriveAppRegistry);
 };
 
+namespace util {
+
+// The preferred icon size, which should usually be used for FindPreferredIcon;
+const int kPreferredIconSize = 16;
+
+// Finds an icon in the list of icons. If unable to find an icon of the exact
+// size requested, returns one with the next larger size. If all icons are
+// smaller than the preferred size, we'll return the largest one available.
+// Icons do not have to be sorted by the icon size. If there are no icons in
+// the list, returns an empty URL.
+GURL FindPreferredIcon(const google_apis::InstalledApp::IconList& icons,
+                       int preferred_size);
+
+}  // namespace util
+
 }  // namespace drive
 
 #endif  // CHROME_BROWSER_CHROMEOS_DRIVE_DRIVE_APP_REGISTRY_H_
diff --git a/chrome/browser/chromeos/drive/drive_app_registry_unittest.cc b/chrome/browser/chromeos/drive/drive_app_registry_unittest.cc
index eacfae6..64dc25d 100644
--- a/chrome/browser/chromeos/drive/drive_app_registry_unittest.cc
+++ b/chrome/browser/chromeos/drive/drive_app_registry_unittest.cc
@@ -7,10 +7,12 @@
 #include "base/files/file_path.h"
 #include "base/prefs/testing_pref_service.h"
 #include "base/run_loop.h"
-#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/chromeos/drive/job_scheduler.h"
 #include "chrome/browser/chromeos/drive/test_util.h"
 #include "chrome/browser/drive/fake_drive_service.h"
+#include "chrome/browser/google_apis/drive_api_parser.h"
+#include "chrome/browser/google_apis/gdata_wapi_parser.h"
+#include "chrome/browser/google_apis/test_util.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -44,8 +46,8 @@
       const DriveAppInfo* app = *it;
       if (web_store_id == app->web_store_id) {
         EXPECT_EQ(app_id, app->app_id);
-        EXPECT_EQ(app_name, UTF16ToUTF8(app->app_name));
-        EXPECT_EQ(object_type, UTF16ToUTF8(app->object_type));
+        EXPECT_EQ(app_name, app->app_name);
+        EXPECT_EQ(object_type, app->object_type);
         EXPECT_EQ(is_primary, app->is_primary_selector);
         found = true;
         break;
@@ -92,6 +94,21 @@
             "Drive app 1", "", false);
 }
 
+TEST_F(DriveAppRegistryTest, UpdateFromAppList) {
+  scoped_ptr<base::Value> app_info_value =
+      google_apis::test_util::LoadJSONFile("drive/applist.json");
+  scoped_ptr<google_apis::AppList> app_list(
+      google_apis::AppList::CreateFrom(*app_info_value));
+
+  web_apps_registry_->UpdateFromAppList(*app_list);
+
+  // Confirm that something was loaded from applist.json.
+  ScopedVector<DriveAppInfo> ext_results;
+  base::FilePath ext_file(FILE_PATH_LITERAL("drive/file.exe"));
+  web_apps_registry_->GetAppsForFile(ext_file, std::string(), &ext_results);
+  ASSERT_EQ(1U, ext_results.size());
+}
+
 TEST_F(DriveAppRegistryTest, MultipleUpdate) {
   // Call Update().
   web_apps_registry_->Update();
@@ -105,4 +122,42 @@
   EXPECT_EQ(1, fake_drive_service_->app_list_load_count());
 }
 
+TEST(DriveAppRegistryUtilTest, FindPreferredIcon_Empty) {
+  google_apis::InstalledApp::IconList icons;
+  EXPECT_EQ("",
+            util::FindPreferredIcon(icons, util::kPreferredIconSize).spec());
+}
+
+TEST(DriveAppRegistryUtilTest, FindPreferredIcon_) {
+  const char kSmallerIconUrl[] = "http://example.com/smaller.png";
+  const char kMediumIconUrl[] = "http://example.com/medium.png";
+  const char kBiggerIconUrl[] = "http://example.com/bigger.png";
+  const int kMediumSize = 16;
+
+  google_apis::InstalledApp::IconList icons;
+  // The icons are not sorted by the size.
+  icons.push_back(std::make_pair(kMediumSize,
+                                 GURL(kMediumIconUrl)));
+  icons.push_back(std::make_pair(kMediumSize + 2,
+                                 GURL(kBiggerIconUrl)));
+  icons.push_back(std::make_pair(kMediumSize - 2,
+                                 GURL(kSmallerIconUrl)));
+
+  // Exact match.
+  EXPECT_EQ(kMediumIconUrl,
+            util::FindPreferredIcon(icons, kMediumSize).spec());
+  // The requested size is in-between of smaller.png and
+  // medium.png. medium.png should be returned.
+  EXPECT_EQ(kMediumIconUrl,
+            util::FindPreferredIcon(icons, kMediumSize - 1).spec());
+  // The requested size is smaller than the smallest icon. The smallest icon
+  // should be returned.
+  EXPECT_EQ(kSmallerIconUrl,
+            util::FindPreferredIcon(icons, kMediumSize - 3).spec());
+  // The requested size is larger than the largest icon. The largest icon
+  // should be returned.
+  EXPECT_EQ(kBiggerIconUrl,
+            util::FindPreferredIcon(icons, kMediumSize + 3).spec());
+}
+
 }  // namespace drive
diff --git a/chrome/browser/chromeos/drive/drive_file_stream_reader_unittest.cc b/chrome/browser/chromeos/drive/drive_file_stream_reader_unittest.cc
index 2f4b1fb..775dad5 100644
--- a/chrome/browser/chromeos/drive/drive_file_stream_reader_unittest.cc
+++ b/chrome/browser/chromeos/drive/drive_file_stream_reader_unittest.cc
@@ -300,13 +300,6 @@
     fake_file_system_->Initialize();
   }
 
-  virtual void TearDown() OVERRIDE {
-    fake_file_system_.reset();
-    fake_drive_service_.reset();
-
-    worker_thread_.reset();
-  }
-
   FileSystemInterface* GetFileSystem() {
     return fake_file_system_.get();
   }
diff --git a/chrome/browser/chromeos/drive/dummy_file_system.h b/chrome/browser/chromeos/drive/dummy_file_system.h
index 6a10c27..3fcefe5 100644
--- a/chrome/browser/chromeos/drive/dummy_file_system.h
+++ b/chrome/browser/chromeos/drive/dummy_file_system.h
@@ -71,7 +71,7 @@
       const base::FilePath& file_path,
       const ReadDirectoryCallback& callback) OVERRIDE {}
   virtual void Search(const std::string& search_query,
-                      const GURL& next_url,
+                      const std::string& page_token,
                       const SearchCallback& callback) OVERRIDE {}
   virtual void SearchMetadata(
       const std::string& query,
@@ -92,8 +92,8 @@
   virtual void MarkCacheFileAsUnmounted(
       const base::FilePath& cache_file_path,
       const FileOperationCallback& callback) OVERRIDE {}
-  virtual void GetCacheEntryByResourceId(
-      const std::string& resource_id,
+  virtual void GetCacheEntryByPath(
+      const base::FilePath& drive_file_path,
       const GetCacheEntryCallback& callback) OVERRIDE {}
   virtual void Reload() OVERRIDE {}
 };
diff --git a/chrome/browser/chromeos/drive/fake_file_system.cc b/chrome/browser/chromeos/drive/fake_file_system.cc
index b34b24b..3ea014f 100644
--- a/chrome/browser/chromeos/drive/fake_file_system.cc
+++ b/chrome/browser/chromeos/drive/fake_file_system.cc
@@ -180,7 +180,7 @@
 }
 
 void FakeFileSystem::Search(const std::string& search_query,
-                            const GURL& next_url,
+                            const std::string& page_token,
                             const SearchCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 }
@@ -222,8 +222,8 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 }
 
-void FakeFileSystem::GetCacheEntryByResourceId(
-    const std::string& resource_id,
+void FakeFileSystem::GetCacheEntryByPath(
+    const base::FilePath& drive_file_path,
     const GetCacheEntryCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 }
@@ -279,8 +279,11 @@
   DCHECK(gdata_entry);
 
   scoped_ptr<ResourceEntry> entry(new ResourceEntry);
-  bool converted = ConvertToResourceEntry(*gdata_entry, entry.get());
+  std::string parent_resource_id;
+  bool converted =
+      ConvertToResourceEntry(*gdata_entry, entry.get(), &parent_resource_id);
   DCHECK(converted);
+  entry->set_parent_local_id(parent_resource_id);
 
   base::FilePath cache_path =
       cache_dir_.path().AppendASCII(entry->resource_id());
@@ -371,8 +374,11 @@
       resource_list->entries();
   for (size_t i = 0; i < entries.size(); ++i) {
     scoped_ptr<ResourceEntry> entry(new ResourceEntry);
-    bool converted = ConvertToResourceEntry(*entries[i], entry.get());
+    std::string parent_resource_id;
+    bool converted =
+        ConvertToResourceEntry(*entries[i], entry.get(), &parent_resource_id);
     DCHECK(converted);
+    entry->set_parent_local_id(parent_resource_id);
 
     if (entry->base_name() == base_name.AsUTF8Unsafe()) {
       // Found the target entry.
diff --git a/chrome/browser/chromeos/drive/fake_file_system.h b/chrome/browser/chromeos/drive/fake_file_system.h
index 052e672..4f56547 100644
--- a/chrome/browser/chromeos/drive/fake_file_system.h
+++ b/chrome/browser/chromeos/drive/fake_file_system.h
@@ -103,7 +103,7 @@
       const base::FilePath& file_path,
       const ReadDirectoryCallback& callback) OVERRIDE;
   virtual void Search(const std::string& search_query,
-                      const GURL& next_url,
+                      const std::string& page_token,
                       const SearchCallback& callback) OVERRIDE;
   virtual void SearchMetadata(const std::string& query,
                               int options,
@@ -123,8 +123,8 @@
   virtual void MarkCacheFileAsUnmounted(
       const base::FilePath& cache_file_path,
       const FileOperationCallback& callback) OVERRIDE;
-  virtual void GetCacheEntryByResourceId(
-      const std::string& resource_id,
+  virtual void GetCacheEntryByPath(
+      const base::FilePath& drive_file_path,
       const GetCacheEntryCallback& callback) OVERRIDE;
   virtual void Reload() OVERRIDE;
 
diff --git a/chrome/browser/chromeos/drive/file_cache.cc b/chrome/browser/chromeos/drive/file_cache.cc
index b5a562f..49c9a83 100644
--- a/chrome/browser/chromeos/drive/file_cache.cc
+++ b/chrome/browser/chromeos/drive/file_cache.cc
@@ -28,8 +28,8 @@
 
 typedef std::map<std::string, FileCacheEntry> CacheMap;
 
-// Returns resource ID extracted from the path.
-std::string GetResourceIdFromPath(const base::FilePath& path) {
+// Returns ID extracted from the path.
+std::string GetIdFromPath(const base::FilePath& path) {
   return util::UnescapeCacheFileName(path.BaseName().AsUTF8Unsafe());
 }
 
@@ -41,7 +41,7 @@
                                   base::FileEnumerator::FILES);
   for (base::FilePath current = enumerator.Next(); !current.empty();
        current = enumerator.Next()) {
-    std::string resource_id = GetResourceIdFromPath(current);
+    std::string id = GetIdFromPath(current);
 
     // Calculate MD5.
     std::string md5 = util::GetMd5Digest(current);
@@ -54,7 +54,7 @@
     cache_entry.set_is_present(true);
 
     // Create and insert new entry into cache map.
-    cache_map->insert(std::make_pair(resource_id, cache_entry));
+    cache_map->insert(std::make_pair(id, cache_entry));
   }
 }
 
@@ -110,10 +110,9 @@
   AssertOnSequencedWorkerPool();
 }
 
-base::FilePath FileCache::GetCacheFilePath(
-    const std::string& resource_id) const {
+base::FilePath FileCache::GetCacheFilePath(const std::string& id) const {
   return cache_file_directory_.Append(
-      base::FilePath::FromUTF8Unsafe(util::EscapeCacheFileName(resource_id)));
+      base::FilePath::FromUTF8Unsafe(util::EscapeCacheFileName(id)));
 }
 
 void FileCache::AssertOnSequencedWorkerPool() {
@@ -125,7 +124,7 @@
   return cache_file_directory_.IsParent(path);
 }
 
-void FileCache::GetCacheEntryOnUIThread(const std::string& resource_id,
+void FileCache::GetCacheEntryOnUIThread(const std::string& id,
                                         const GetCacheEntryCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
@@ -136,17 +135,16 @@
       FROM_HERE,
       base::Bind(&FileCache::GetCacheEntry,
                  base::Unretained(this),
-                 resource_id,
+                 id,
                  cache_entry),
       base::Bind(
           &RunGetCacheEntryCallback, callback, base::Owned(cache_entry)));
 }
 
-bool FileCache::GetCacheEntry(const std::string& resource_id,
-                              FileCacheEntry* entry) {
+bool FileCache::GetCacheEntry(const std::string& id, FileCacheEntry* entry) {
   DCHECK(entry);
   AssertOnSequencedWorkerPool();
-  return storage_->GetCacheEntry(resource_id, entry);
+  return storage_->GetCacheEntry(id, entry);
 }
 
 void FileCache::IterateOnUIThread(
@@ -198,8 +196,8 @@
   FileCacheEntry entry;
   for (base::FilePath current = enumerator.Next(); !current.empty();
        current = enumerator.Next()) {
-    std::string resource_id = GetResourceIdFromPath(current);
-    if (!storage_->GetCacheEntry(resource_id, &entry))
+    std::string id = GetIdFromPath(current);
+    if (!storage_->GetCacheEntry(id, &entry))
       base::DeleteFile(current, false /* recursive */);
   }
 
@@ -207,7 +205,7 @@
   return HasEnoughSpaceFor(num_bytes, cache_file_directory_);
 }
 
-void FileCache::GetFileOnUIThread(const std::string& resource_id,
+void FileCache::GetFileOnUIThread(const std::string& id,
                                   const GetFileFromCacheCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
@@ -217,28 +215,28 @@
                                    FROM_HERE,
                                    base::Bind(&FileCache::GetFile,
                                               base::Unretained(this),
-                                              resource_id,
+                                              id,
                                               cache_file_path),
                                    base::Bind(&RunGetFileFromCacheCallback,
                                               callback,
                                               base::Owned(cache_file_path)));
 }
 
-FileError FileCache::GetFile(const std::string& resource_id,
+FileError FileCache::GetFile(const std::string& id,
                              base::FilePath* cache_file_path) {
   AssertOnSequencedWorkerPool();
   DCHECK(cache_file_path);
 
   FileCacheEntry cache_entry;
-  if (!storage_->GetCacheEntry(resource_id, &cache_entry) ||
+  if (!storage_->GetCacheEntry(id, &cache_entry) ||
       !cache_entry.is_present())
     return FILE_ERROR_NOT_FOUND;
 
-  *cache_file_path = GetCacheFilePath(resource_id);
+  *cache_file_path = GetCacheFilePath(id);
   return FILE_ERROR_OK;
 }
 
-void FileCache::StoreOnUIThread(const std::string& resource_id,
+void FileCache::StoreOnUIThread(const std::string& id,
                                 const std::string& md5,
                                 const base::FilePath& source_path,
                                 FileOperationType file_operation_type,
@@ -250,22 +248,22 @@
                                    FROM_HERE,
                                    base::Bind(&FileCache::Store,
                                               base::Unretained(this),
-                                              resource_id,
+                                              id,
                                               md5,
                                               source_path,
                                               file_operation_type),
                                    callback);
 }
 
-FileError FileCache::Store(const std::string& resource_id,
+FileError FileCache::Store(const std::string& id,
                            const std::string& md5,
                            const base::FilePath& source_path,
                            FileOperationType file_operation_type) {
   AssertOnSequencedWorkerPool();
-  return StoreInternal(resource_id, md5, source_path, file_operation_type);
+  return StoreInternal(id, md5, source_path, file_operation_type);
 }
 
-void FileCache::PinOnUIThread(const std::string& resource_id,
+void FileCache::PinOnUIThread(const std::string& id,
                               const FileOperationCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
@@ -273,21 +271,21 @@
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(),
       FROM_HERE,
-      base::Bind(&FileCache::Pin, base::Unretained(this), resource_id),
+      base::Bind(&FileCache::Pin, base::Unretained(this), id),
       callback);
 }
 
-FileError FileCache::Pin(const std::string& resource_id) {
+FileError FileCache::Pin(const std::string& id) {
   AssertOnSequencedWorkerPool();
 
   FileCacheEntry cache_entry;
-  storage_->GetCacheEntry(resource_id, &cache_entry);
+  storage_->GetCacheEntry(id, &cache_entry);
   cache_entry.set_is_pinned(true);
-  return storage_->PutCacheEntry(resource_id, cache_entry) ?
+  return storage_->PutCacheEntry(id, cache_entry) ?
       FILE_ERROR_OK : FILE_ERROR_FAILED;
 }
 
-void FileCache::UnpinOnUIThread(const std::string& resource_id,
+void FileCache::UnpinOnUIThread(const std::string& id,
                                 const FileOperationCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
@@ -295,26 +293,26 @@
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(),
       FROM_HERE,
-      base::Bind(&FileCache::Unpin, base::Unretained(this), resource_id),
+      base::Bind(&FileCache::Unpin, base::Unretained(this), id),
       callback);
 }
 
-FileError FileCache::Unpin(const std::string& resource_id) {
+FileError FileCache::Unpin(const std::string& id) {
   AssertOnSequencedWorkerPool();
 
   // Unpinning a file means its entry must exist in cache.
   FileCacheEntry cache_entry;
-  if (!storage_->GetCacheEntry(resource_id, &cache_entry))
+  if (!storage_->GetCacheEntry(id, &cache_entry))
     return FILE_ERROR_NOT_FOUND;
 
   // Now that file operations have completed, update metadata.
   if (cache_entry.is_present()) {
     cache_entry.set_is_pinned(false);
-    if (!storage_->PutCacheEntry(resource_id, cache_entry))
+    if (!storage_->PutCacheEntry(id, cache_entry))
       return FILE_ERROR_FAILED;
   } else {
     // Remove the existing entry if we are unpinning a non-present file.
-    if  (!storage_->RemoveCacheEntry(resource_id))
+    if  (!storage_->RemoveCacheEntry(id))
       return FILE_ERROR_FAILED;
   }
 
@@ -325,7 +323,7 @@
 }
 
 void FileCache::MarkAsMountedOnUIThread(
-    const std::string& resource_id,
+    const std::string& id,
     const GetFileFromCacheCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
@@ -336,12 +334,41 @@
       FROM_HERE,
       base::Bind(&FileCache::MarkAsMounted,
                  base::Unretained(this),
-                 resource_id,
+                 id,
                  cache_file_path),
       base::Bind(
           RunGetFileFromCacheCallback, callback, base::Owned(cache_file_path)));
 }
 
+FileError FileCache::MarkAsMounted(const std::string& id,
+                                   base::FilePath* cache_file_path) {
+  AssertOnSequencedWorkerPool();
+  DCHECK(cache_file_path);
+
+  // Get cache entry associated with the id and md5
+  FileCacheEntry cache_entry;
+  if (!storage_->GetCacheEntry(id, &cache_entry))
+    return FILE_ERROR_NOT_FOUND;
+
+  if (mounted_files_.count(id))
+    return FILE_ERROR_INVALID_OPERATION;
+
+  // Ensure the file is readable to cros_disks. See crbug.com/236994.
+  base::FilePath path = GetCacheFilePath(id);
+  if (!file_util::SetPosixFilePermissions(
+          path,
+          file_util::FILE_PERMISSION_READ_BY_USER |
+          file_util::FILE_PERMISSION_WRITE_BY_USER |
+          file_util::FILE_PERMISSION_READ_BY_GROUP |
+          file_util::FILE_PERMISSION_READ_BY_OTHERS))
+    return FILE_ERROR_FAILED;
+
+  mounted_files_.insert(id);
+
+  *cache_file_path = path;
+  return FILE_ERROR_OK;
+}
+
 void FileCache::MarkAsUnmountedOnUIThread(
     const base::FilePath& file_path,
     const FileOperationCallback& callback) {
@@ -356,7 +383,7 @@
       callback);
 }
 
-void FileCache::MarkDirtyOnUIThread(const std::string& resource_id,
+void FileCache::MarkDirtyOnUIThread(const std::string& id,
                                     const FileOperationCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
@@ -364,20 +391,19 @@
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(),
       FROM_HERE,
-      base::Bind(&FileCache::MarkDirty, base::Unretained(this), resource_id),
+      base::Bind(&FileCache::MarkDirty, base::Unretained(this), id),
       callback);
 }
 
-FileError FileCache::MarkDirty(const std::string& resource_id) {
+FileError FileCache::MarkDirty(const std::string& id) {
   AssertOnSequencedWorkerPool();
 
   // Marking a file dirty means its entry and actual file blob must exist in
   // cache.
   FileCacheEntry cache_entry;
-  if (!storage_->GetCacheEntry(resource_id, &cache_entry) ||
+  if (!storage_->GetCacheEntry(id, &cache_entry) ||
       !cache_entry.is_present()) {
-    LOG(WARNING) << "Can't mark dirty a file that wasn't cached: "
-                 << resource_id;
+    LOG(WARNING) << "Can't mark dirty a file that wasn't cached: " << id;
     return FILE_ERROR_NOT_FOUND;
   }
 
@@ -385,39 +411,37 @@
     return FILE_ERROR_OK;
 
   cache_entry.set_is_dirty(true);
-  return storage_->PutCacheEntry(resource_id, cache_entry) ?
+  return storage_->PutCacheEntry(id, cache_entry) ?
       FILE_ERROR_OK : FILE_ERROR_FAILED;
 }
 
-FileError FileCache::ClearDirty(const std::string& resource_id,
-                                const std::string& md5) {
+FileError FileCache::ClearDirty(const std::string& id, const std::string& md5) {
   AssertOnSequencedWorkerPool();
 
   // Clearing a dirty file means its entry and actual file blob must exist in
   // cache.
   FileCacheEntry cache_entry;
-  if (!storage_->GetCacheEntry(resource_id, &cache_entry) ||
+  if (!storage_->GetCacheEntry(id, &cache_entry) ||
       !cache_entry.is_present()) {
     LOG(WARNING) << "Can't clear dirty state of a file that wasn't cached: "
-                 << resource_id;
+                 << id;
     return FILE_ERROR_NOT_FOUND;
   }
 
   // If a file is not dirty (it should have been marked dirty via
   // MarkDirtyInCache), clearing its dirty state is an invalid operation.
   if (!cache_entry.is_dirty()) {
-    LOG(WARNING) << "Can't clear dirty state of a non-dirty file: "
-                 << resource_id;
+    LOG(WARNING) << "Can't clear dirty state of a non-dirty file: " << id;
     return FILE_ERROR_INVALID_OPERATION;
   }
 
   cache_entry.set_md5(md5);
   cache_entry.set_is_dirty(false);
-  return storage_->PutCacheEntry(resource_id, cache_entry) ?
+  return storage_->PutCacheEntry(id, cache_entry) ?
       FILE_ERROR_OK : FILE_ERROR_FAILED;
 }
 
-void FileCache::RemoveOnUIThread(const std::string& resource_id,
+void FileCache::RemoveOnUIThread(const std::string& id,
                                  const FileOperationCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
@@ -425,31 +449,30 @@
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(),
       FROM_HERE,
-      base::Bind(&FileCache::Remove, base::Unretained(this), resource_id),
+      base::Bind(&FileCache::Remove, base::Unretained(this), id),
       callback);
 }
 
-FileError FileCache::Remove(const std::string& resource_id) {
+FileError FileCache::Remove(const std::string& id) {
   AssertOnSequencedWorkerPool();
 
   FileCacheEntry cache_entry;
 
   // If entry doesn't exist, nothing to do.
-  if (!storage_->GetCacheEntry(resource_id, &cache_entry))
+  if (!storage_->GetCacheEntry(id, &cache_entry))
     return FILE_ERROR_OK;
 
   // Cannot delete a mounted file.
-  if (mounted_files_.count(resource_id))
+  if (mounted_files_.count(id))
     return FILE_ERROR_IN_USE;
 
   // Delete the file.
-  base::FilePath path = GetCacheFilePath(resource_id);
+  base::FilePath path = GetCacheFilePath(id);
   if (!base::DeleteFile(path, false /* recursive */))
     return FILE_ERROR_FAILED;
 
   // Now that all file operations have completed, remove from metadata.
-  return storage_->RemoveCacheEntry(resource_id) ?
-      FILE_ERROR_OK : FILE_ERROR_FAILED;
+  return storage_->RemoveCacheEntry(id) ? FILE_ERROR_OK : FILE_ERROR_FAILED;
 }
 
 void FileCache::ClearAllOnUIThread(const ClearAllCallback& callback) {
@@ -498,7 +521,7 @@
   delete this;
 }
 
-FileError FileCache::StoreInternal(const std::string& resource_id,
+FileError FileCache::StoreInternal(const std::string& id,
                                    const std::string& md5,
                                    const base::FilePath& source_path,
                                    FileOperationType file_operation_type) {
@@ -515,13 +538,13 @@
     return FILE_ERROR_NO_LOCAL_SPACE;
 
   FileCacheEntry cache_entry;
-  storage_->GetCacheEntry(resource_id, &cache_entry);
+  storage_->GetCacheEntry(id, &cache_entry);
 
   // If file is dirty or mounted, return error.
-  if (cache_entry.is_dirty() || mounted_files_.count(resource_id))
+  if (cache_entry.is_dirty() || mounted_files_.count(id))
     return FILE_ERROR_IN_USE;
 
-  base::FilePath dest_path = GetCacheFilePath(resource_id);
+  base::FilePath dest_path = GetCacheFilePath(id);
   bool success = false;
   switch (file_operation_type) {
     case FILE_OPERATION_MOVE:
@@ -546,51 +569,22 @@
   cache_entry.set_md5(md5);
   cache_entry.set_is_present(true);
   cache_entry.set_is_dirty(false);
-  return storage_->PutCacheEntry(resource_id, cache_entry) ?
+  return storage_->PutCacheEntry(id, cache_entry) ?
       FILE_ERROR_OK : FILE_ERROR_FAILED;
 }
 
-FileError FileCache::MarkAsMounted(const std::string& resource_id,
-                                   base::FilePath* cache_file_path) {
-  AssertOnSequencedWorkerPool();
-  DCHECK(cache_file_path);
-
-  // Get cache entry associated with the resource_id and md5
-  FileCacheEntry cache_entry;
-  if (!storage_->GetCacheEntry(resource_id, &cache_entry))
-    return FILE_ERROR_NOT_FOUND;
-
-  if (mounted_files_.count(resource_id))
-    return FILE_ERROR_INVALID_OPERATION;
-
-  // Ensure the file is readable to cros_disks. See crbug.com/236994.
-  base::FilePath path = GetCacheFilePath(resource_id);
-  if (!file_util::SetPosixFilePermissions(
-          path,
-          file_util::FILE_PERMISSION_READ_BY_USER |
-          file_util::FILE_PERMISSION_WRITE_BY_USER |
-          file_util::FILE_PERMISSION_READ_BY_GROUP |
-          file_util::FILE_PERMISSION_READ_BY_OTHERS))
-    return FILE_ERROR_FAILED;
-
-  mounted_files_.insert(resource_id);
-
-  *cache_file_path = path;
-  return FILE_ERROR_OK;
-}
-
 FileError FileCache::MarkAsUnmounted(const base::FilePath& file_path) {
   AssertOnSequencedWorkerPool();
   DCHECK(IsUnderFileCacheDirectory(file_path));
 
-  std::string resource_id = GetResourceIdFromPath(file_path);
+  std::string id = GetIdFromPath(file_path);
 
-  // Get cache entry associated with the resource_id and md5
+  // Get cache entry associated with the id and md5
   FileCacheEntry cache_entry;
-  if (!storage_->GetCacheEntry(resource_id, &cache_entry))
+  if (!storage_->GetCacheEntry(id, &cache_entry))
     return FILE_ERROR_NOT_FOUND;
 
-  std::set<std::string>::iterator it = mounted_files_.find(resource_id);
+  std::set<std::string>::iterator it = mounted_files_.find(id);
   if (it == mounted_files_.end())
     return FILE_ERROR_INVALID_OPERATION;
 
diff --git a/chrome/browser/chromeos/drive/file_cache.h b/chrome/browser/chromeos/drive/file_cache.h
index 4adc25e..a7df210 100644
--- a/chrome/browser/chromeos/drive/file_cache.h
+++ b/chrome/browser/chromeos/drive/file_cache.h
@@ -32,7 +32,7 @@
     GetCacheEntryCallback;
 
 // Callback for Iterate().
-typedef base::Callback<void(const std::string& resource_id,
+typedef base::Callback<void(const std::string& id,
                             const FileCacheEntry& cache_entry)>
     CacheIterateCallback;
 
@@ -90,18 +90,17 @@
   // Can be called on any thread.
   bool IsUnderFileCacheDirectory(const base::FilePath& path) const;
 
-  // Gets the cache entry for file corresponding to |resource_id| and runs
+  // Gets the cache entry for file corresponding to |id| and runs
   // |callback| with true and the entry found if entry exists in cache map.
   // Otherwise, runs |callback| with false.
   // |callback| must not be null.
   // Must be called on the UI thread.
-  void GetCacheEntryOnUIThread(const std::string& resource_id,
+  void GetCacheEntryOnUIThread(const std::string& id,
                                const GetCacheEntryCallback& callback);
 
-  // Gets the cache entry by the given resource ID.
+  // Gets the cache entry by the given ID.
   // See also GetCacheEntryOnUIThread().
-  bool GetCacheEntry(const std::string& resource_id,
-                     FileCacheEntry* entry);
+  bool GetCacheEntry(const std::string& id, FileCacheEntry* entry);
 
   // Runs Iterate() with |iteration_callback| on |blocking_task_runner_| and
   // runs |completion_callback| upon completion.
@@ -122,28 +121,27 @@
   // the result asynchronously.
   // |callback| must not be null.
   // Must be called on the UI thread.
-  void GetFileOnUIThread(const std::string& resource_id,
+  void GetFileOnUIThread(const std::string& id,
                          const GetFileFromCacheCallback& callback);
 
-  // Checks if file corresponding to |resource_id| exists in cache, and returns
+  // Checks if file corresponding to |id| exists in cache, and returns
   // FILE_ERROR_OK with |cache_file_path| storing the path to the file.
   // |cache_file_path| must not be null.
-  FileError GetFile(const std::string& resource_id,
-                    base::FilePath* cache_file_path);
+  FileError GetFile(const std::string& id, base::FilePath* cache_file_path);
 
   // Runs Store() on |blocking_task_runner_|, and calls |callback| with
   // the result asynchronously.
   // |callback| must not be null.
   // Must be called on the UI thread.
-  void StoreOnUIThread(const std::string& resource_id,
+  void StoreOnUIThread(const std::string& id,
                        const std::string& md5,
                        const base::FilePath& source_path,
                        FileOperationType file_operation_type,
                        const FileOperationCallback& callback);
 
   // Stores |source_path| as a cache of the remote content of the file
-  // with |resource_id| and |md5|.
-  FileError Store(const std::string& resource_Id,
+  // with |id| and |md5|.
+  FileError Store(const std::string& id,
                   const std::string& md5,
                   const base::FilePath& source_path,
                   FileOperationType file_operation_type);
@@ -152,29 +150,33 @@
   // asynchronously.
   // |callback| must not be null.
   // Must be called on the UI thread.
-  void PinOnUIThread(const std::string& resource_id,
+  void PinOnUIThread(const std::string& id,
                      const FileOperationCallback& callback);
 
   // Pins the specified entry.
-  FileError Pin(const std::string& resource_id);
+  FileError Pin(const std::string& id);
 
   // Runs Unpin() on |blocking_task_runner_|, and calls |callback| with the
   // result asynchronously.
   // |callback| must not be null.
   // Must be called on the UI thread.
-  void UnpinOnUIThread(const std::string& resource_id,
+  void UnpinOnUIThread(const std::string& id,
                        const FileOperationCallback& callback);
 
   // Unpins the specified entry.
-  FileError Unpin(const std::string& resource_id);
+  FileError Unpin(const std::string& id);
 
-  // Sets the state of the cache entry corresponding to |resource_id| as
-  // mounted.
+  // Runs MarkAsMounted() on |blocking_task_runner_|, and calls |callback| with
+  // the result asynchronously.
   // |callback| must not be null.
   // Must be called on the UI thread.
-  void MarkAsMountedOnUIThread(const std::string& resource_id,
+  void MarkAsMountedOnUIThread(const std::string& id,
                                const GetFileFromCacheCallback& callback);
 
+  // Sets the state of the cache entry corresponding to |id| as mounted.
+  FileError MarkAsMounted(const std::string& id,
+                          base::FilePath* cache_file_path);
+
   // Set the state of the cache entry corresponding to file_path as unmounted.
   // |callback| must not be null.
   // Must be called on the UI thread.
@@ -185,25 +187,24 @@
   // result asynchronously.
   // |callback| must not be null.
   // Must be called on the UI thread.
-  void MarkDirtyOnUIThread(const std::string& resource_id,
+  void MarkDirtyOnUIThread(const std::string& id,
                            const FileOperationCallback& callback);
 
   // Marks the specified entry dirty.
-  FileError MarkDirty(const std::string& resource_id);
+  FileError MarkDirty(const std::string& id);
 
   // Clears dirty state of the specified entry and updates its MD5.
-  FileError ClearDirty(const std::string& resource_id,
-                       const std::string& md5);
+  FileError ClearDirty(const std::string& id, const std::string& md5);
 
   // Runs Remove() on |blocking_task_runner_| and runs |callback| with the
   // result.
   // Must be called on the UI thread.
-  void RemoveOnUIThread(const std::string& resource_id,
+  void RemoveOnUIThread(const std::string& id,
                         const FileOperationCallback& callback);
 
   // Removes the specified cache entry and delete cache files if available.
   // Synchronous version of RemoveOnUIThread().
-  FileError Remove(const std::string& resource_id);
+  FileError Remove(const std::string& id);
 
   // Does the following:
   // - remove all the files in the cache directory.
@@ -229,7 +230,7 @@
   // Returns absolute path of the file if it were cached or to be cached.
   //
   // Can be called on any thread.
-  base::FilePath GetCacheFilePath(const std::string& resource_id) const;
+  base::FilePath GetCacheFilePath(const std::string& id) const;
 
   // Checks whether the current thread is on the right sequenced worker pool
   // with the right sequence ID. If not, DCHECK will fail.
@@ -241,15 +242,11 @@
   // Used to implement Store and StoreLocallyModifiedOnUIThread.
   // TODO(hidehiko): Merge this method with Store(), after
   // StoreLocallyModifiedOnUIThread is removed.
-  FileError StoreInternal(const std::string& resource_id,
+  FileError StoreInternal(const std::string& id,
                           const std::string& md5,
                           const base::FilePath& source_path,
                           FileOperationType file_operation_type);
 
-  // Used to implement MarkAsMountedOnUIThread.
-  FileError MarkAsMounted(const std::string& resource_id,
-                          base::FilePath* cache_file_path);
-
   // Used to implement MarkAsUnmountedOnUIThread.
   FileError MarkAsUnmounted(const base::FilePath& file_path);
 
@@ -260,7 +257,7 @@
   // bytes, while keeping kMinFreeSpace bytes on the disk.
   bool HasEnoughSpaceFor(int64 num_bytes, const base::FilePath& path);
 
-  // Renames cache files from old "resource_id.md5" format to the new format.
+  // Renames cache files from old "id.md5" format to the new format.
   // TODO(hashimoto): Remove this method at some point.
   void RenameCacheFilesToNewFormat();
 
@@ -272,7 +269,7 @@
 
   FreeDiskSpaceGetterInterface* free_disk_space_getter_;  // Not owned.
 
-  // Resource IDs of files marked mounted.
+  // IDs of files marked mounted.
   std::set<std::string> mounted_files_;
 
   // Note: This should remain the last member so it'll be destroyed and
diff --git a/chrome/browser/chromeos/drive/file_cache_unittest.cc b/chrome/browser/chromeos/drive/file_cache_unittest.cc
index ecd79a1..7c7b947 100644
--- a/chrome/browser/chromeos/drive/file_cache_unittest.cc
+++ b/chrome/browser/chromeos/drive/file_cache_unittest.cc
@@ -36,11 +36,11 @@
 };
 
 // Copies results from Iterate().
-void OnIterate(std::vector<std::string>* out_resource_ids,
+void OnIterate(std::vector<std::string>* out_ids,
                std::vector<FileCacheEntry>* out_cache_entries,
-               const std::string& resource_id,
+               const std::string& id,
                const FileCacheEntry& cache_entry) {
-  out_resource_ids->push_back(resource_id);
+  out_ids->push_back(id);
   out_cache_entries->push_back(cache_entry);
 }
 
@@ -105,11 +105,10 @@
     ASSERT_TRUE(success);
   }
 
-  void TestGetFile(const std::string& resource_id,
-                   FileError expected_error) {
+  void TestGetFile(const std::string& id, FileError expected_error) {
     FileError error = FILE_ERROR_OK;
     base::FilePath cache_file_path;
-    cache_->GetFileOnUIThread(resource_id,
+    cache_->GetFileOnUIThread(id,
                               google_apis::test_util::CreateCopyResultCallback(
                                   &error, &cache_file_path));
     test_util::RunBlockingPoolTask();
@@ -117,14 +116,14 @@
     EXPECT_EQ(expected_error, error);
     if (error == FILE_ERROR_OK) {
       // Verify filename of |cache_file_path|.
-      EXPECT_EQ(util::EscapeCacheFileName(resource_id),
+      EXPECT_EQ(util::EscapeCacheFileName(id),
                 cache_file_path.BaseName().AsUTF8Unsafe());
     } else {
       EXPECT_TRUE(cache_file_path.empty());
     }
   }
 
-  void TestStoreToCache(const std::string& resource_id,
+  void TestStoreToCache(const std::string& id,
                         const std::string& md5,
                         const base::FilePath& source_path,
                         FileError expected_error,
@@ -134,45 +133,44 @@
 
     FileError error = FILE_ERROR_OK;
     cache_->StoreOnUIThread(
-        resource_id, md5, source_path,
+        id, md5, source_path,
         FileCache::FILE_OPERATION_COPY,
         google_apis::test_util::CreateCopyResultCallback(&error));
     test_util::RunBlockingPoolTask();
 
     if (error == FILE_ERROR_OK) {
       FileCacheEntry cache_entry;
-      EXPECT_TRUE(GetCacheEntryFromOriginThread(resource_id, &cache_entry));
+      EXPECT_TRUE(GetCacheEntryFromOriginThread(id, &cache_entry));
       EXPECT_EQ(md5, cache_entry.md5());
     }
 
-    VerifyCacheFileState(error, resource_id);
+    VerifyCacheFileState(error, id);
   }
 
-  void TestRemoveFromCache(const std::string& resource_id,
-                           FileError expected_error) {
+  void TestRemoveFromCache(const std::string& id, FileError expected_error) {
     expected_error_ = expected_error;
 
     FileError error = FILE_ERROR_OK;
     cache_->RemoveOnUIThread(
-        resource_id,
+        id,
         google_apis::test_util::CreateCopyResultCallback(&error));
     test_util::RunBlockingPoolTask();
-    VerifyRemoveFromCache(error, resource_id);
+    VerifyRemoveFromCache(error, id);
   }
 
-  void VerifyRemoveFromCache(FileError error, const std::string& resource_id) {
+  void VerifyRemoveFromCache(FileError error, const std::string& id) {
     EXPECT_EQ(expected_error_, error);
 
     FileCacheEntry cache_entry;
-    if (!GetCacheEntryFromOriginThread(resource_id, &cache_entry)) {
+    if (!GetCacheEntryFromOriginThread(id, &cache_entry)) {
       EXPECT_EQ(FILE_ERROR_OK, error);
 
-      const base::FilePath path = cache_->GetCacheFilePath(resource_id);
+      const base::FilePath path = cache_->GetCacheFilePath(id);
       EXPECT_FALSE(base::PathExists(path));
     }
   }
 
-  void TestPin(const std::string& resource_id,
+  void TestPin(const std::string& id,
                FileError expected_error,
                int expected_cache_state) {
     expected_error_ = expected_error;
@@ -180,13 +178,13 @@
 
     FileError error = FILE_ERROR_OK;
     cache_->PinOnUIThread(
-        resource_id,
+        id,
         google_apis::test_util::CreateCopyResultCallback(&error));
     test_util::RunBlockingPoolTask();
-    VerifyCacheFileState(error, resource_id);
+    VerifyCacheFileState(error, id);
   }
 
-  void TestUnpin(const std::string& resource_id,
+  void TestUnpin(const std::string& id,
                  FileError expected_error,
                  int expected_cache_state) {
     expected_error_ = expected_error;
@@ -194,13 +192,13 @@
 
     FileError error = FILE_ERROR_OK;
     cache_->UnpinOnUIThread(
-        resource_id,
+        id,
         google_apis::test_util::CreateCopyResultCallback(&error));
     test_util::RunBlockingPoolTask();
-    VerifyCacheFileState(error, resource_id);
+    VerifyCacheFileState(error, id);
   }
 
-  void TestMarkDirty(const std::string& resource_id,
+  void TestMarkDirty(const std::string& id,
                      FileError expected_error,
                      int expected_cache_state) {
     expected_error_ = expected_error;
@@ -208,28 +206,28 @@
 
     FileError error = FILE_ERROR_OK;
     cache_->MarkDirtyOnUIThread(
-        resource_id,
+        id,
         google_apis::test_util::CreateCopyResultCallback(&error));
     test_util::RunBlockingPoolTask();
 
-    VerifyCacheFileState(error, resource_id);
+    VerifyCacheFileState(error, id);
 
     // Verify filename.
     if (error == FILE_ERROR_OK) {
       base::FilePath cache_file_path;
       cache_->GetFileOnUIThread(
-          resource_id,
+          id,
           google_apis::test_util::CreateCopyResultCallback(
               &error, &cache_file_path));
       test_util::RunBlockingPoolTask();
 
       EXPECT_EQ(FILE_ERROR_OK, error);
-      EXPECT_EQ(util::EscapeCacheFileName(resource_id),
+      EXPECT_EQ(util::EscapeCacheFileName(id),
                 cache_file_path.BaseName().AsUTF8Unsafe());
     }
   }
 
-  void TestClearDirty(const std::string& resource_id,
+  void TestClearDirty(const std::string& id,
                       const std::string& md5,
                       FileError expected_error,
                       int expected_cache_state) {
@@ -242,42 +240,42 @@
         FROM_HERE,
         base::Bind(&FileCache::ClearDirty,
                    base::Unretained(cache_.get()),
-                   resource_id,
+                   id,
                    md5),
         google_apis::test_util::CreateCopyResultCallback(&error));
     test_util::RunBlockingPoolTask();
 
     if (error == FILE_ERROR_OK) {
       FileCacheEntry cache_entry;
-      EXPECT_TRUE(GetCacheEntryFromOriginThread(resource_id, &cache_entry));
+      EXPECT_TRUE(GetCacheEntryFromOriginThread(id, &cache_entry));
       EXPECT_EQ(md5, cache_entry.md5());
     }
 
-    VerifyCacheFileState(error, resource_id);
+    VerifyCacheFileState(error, id);
   }
 
-  void TestMarkAsMounted(const std::string& resource_id,
+  void TestMarkAsMounted(const std::string& id,
                          FileError expected_error,
                          int expected_cache_state) {
     expected_error_ = expected_error;
     expected_cache_state_ = expected_cache_state;
 
     FileCacheEntry entry;
-    EXPECT_TRUE(GetCacheEntryFromOriginThread(resource_id, &entry));
+    EXPECT_TRUE(GetCacheEntryFromOriginThread(id, &entry));
 
     FileError error = FILE_ERROR_OK;
     base::FilePath cache_file_path;
     cache_->MarkAsMountedOnUIThread(
-        resource_id,
+        id,
         google_apis::test_util::CreateCopyResultCallback(
             &error, &cache_file_path));
     test_util::RunBlockingPoolTask();
 
     EXPECT_TRUE(base::PathExists(cache_file_path));
-    EXPECT_EQ(cache_file_path, cache_->GetCacheFilePath(resource_id));
+    EXPECT_EQ(cache_file_path, cache_->GetCacheFilePath(id));
   }
 
-  void TestMarkAsUnmounted(const std::string& resource_id,
+  void TestMarkAsUnmounted(const std::string& id,
                            const base::FilePath& file_path,
                            FileError expected_error,
                            int expected_cache_state) {
@@ -292,23 +290,23 @@
 
     base::FilePath cache_file_path;
     cache_->GetFileOnUIThread(
-        resource_id,
+        id,
         google_apis::test_util::CreateCopyResultCallback(
             &error, &cache_file_path));
     test_util::RunBlockingPoolTask();
     EXPECT_EQ(FILE_ERROR_OK, error);
 
     EXPECT_TRUE(base::PathExists(cache_file_path));
-    EXPECT_EQ(cache_file_path, cache_->GetCacheFilePath(resource_id));
+    EXPECT_EQ(cache_file_path, cache_->GetCacheFilePath(id));
   }
 
-  void VerifyCacheFileState(FileError error, const std::string& resource_id) {
+  void VerifyCacheFileState(FileError error, const std::string& id) {
     EXPECT_EQ(expected_error_, error);
 
     // Verify cache map.
     FileCacheEntry cache_entry;
     const bool cache_entry_found =
-        GetCacheEntryFromOriginThread(resource_id, &cache_entry);
+        GetCacheEntryFromOriginThread(id, &cache_entry);
     if ((expected_cache_state_ & TEST_CACHE_STATE_PRESENT) ||
         (expected_cache_state_ & TEST_CACHE_STATE_PINNED)) {
       ASSERT_TRUE(cache_entry_found);
@@ -323,33 +321,32 @@
     }
 
     // Verify actual cache file.
-    base::FilePath dest_path = cache_->GetCacheFilePath(resource_id);
+    base::FilePath dest_path = cache_->GetCacheFilePath(id);
     EXPECT_EQ((expected_cache_state_ & TEST_CACHE_STATE_PRESENT) != 0,
               base::PathExists(dest_path));
   }
 
   // Helper function to call GetCacheEntry from origin thread.
-  bool GetCacheEntryFromOriginThread(const std::string& resource_id,
+  bool GetCacheEntryFromOriginThread(const std::string& id,
                                      FileCacheEntry* cache_entry) {
     bool result = false;
     cache_->GetCacheEntryOnUIThread(
-        resource_id,
+        id,
         google_apis::test_util::CreateCopyResultCallback(&result, cache_entry));
     test_util::RunBlockingPoolTask();
     return result;
   }
 
-  // Returns true if the cache entry exists for the given resource ID.
-  bool CacheEntryExists(const std::string& resource_id) {
+  // Returns true if the cache entry exists for the given ID.
+  bool CacheEntryExists(const std::string& id) {
     FileCacheEntry cache_entry;
-    return GetCacheEntryFromOriginThread(resource_id, &cache_entry);
+    return GetCacheEntryFromOriginThread(id, &cache_entry);
   }
 
-  // Returns the number of the cache files with name <resource_id>, and Confirm
+  // Returns the number of the cache files with name <id>, and Confirm
   // that they have the <md5>. This should return 1 or 0.
-  size_t CountCacheFiles(const std::string& resource_id,
-                         const std::string& md5) {
-    base::FilePath path = cache_->GetCacheFilePath(resource_id);
+  size_t CountCacheFiles(const std::string& id, const std::string& md5) {
+    base::FilePath path = cache_->GetCacheFilePath(id);
     base::FileEnumerator enumerator(path.DirName(),
                                     false,  // recursive
                                     base::FileEnumerator::FILES,
@@ -358,7 +355,7 @@
     for (base::FilePath current = enumerator.Next(); !current.empty();
          current = enumerator.Next()) {
       ++num_files_found;
-      EXPECT_EQ(util::EscapeCacheFileName(resource_id),
+      EXPECT_EQ(util::EscapeCacheFileName(id),
                 current.BaseName().AsUTF8Unsafe());
     }
     return num_files_found;
@@ -380,221 +377,219 @@
 };
 
 TEST_F(FileCacheTestOnUIThread, StoreToCacheSimple) {
-  std::string resource_id("pdf:1a2b");
+  std::string id("pdf:1a2b");
   std::string md5("abcdef0123456789");
 
   // Store an existing file.
-  TestStoreToCache(resource_id, md5, dummy_file_path_,
+  TestStoreToCache(id, md5, dummy_file_path_,
                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
 
-  // Store a non-existent file to the same |resource_id| and |md5|.
-  TestStoreToCache(resource_id, md5,
+  // Store a non-existent file to the same |id| and |md5|.
+  TestStoreToCache(id, md5,
                    base::FilePath::FromUTF8Unsafe("non_existent_file"),
                    FILE_ERROR_FAILED,
                    TEST_CACHE_STATE_PRESENT);
 
-  // Store a different existing file to the same |resource_id| but different
+  // Store a different existing file to the same |id| but different
   // |md5|.
   md5 = "new_md5";
-  TestStoreToCache(resource_id, md5, dummy_file_path_,
+  TestStoreToCache(id, md5, dummy_file_path_,
                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
 
-  // Verify that there's only one file with name <resource_id>, i.e. previously
+  // Verify that there's only one file with name <id>, i.e. previously
   // cached file with the different md5 should be deleted.
-  EXPECT_EQ(1U, CountCacheFiles(resource_id, md5));
+  EXPECT_EQ(1U, CountCacheFiles(id, md5));
 }
 
 
 TEST_F(FileCacheTestOnUIThread, GetFromCacheSimple) {
-  std::string resource_id("pdf:1a2b");
+  std::string id("pdf:1a2b");
   std::string md5("abcdef0123456789");
   // First store a file to cache.
-  TestStoreToCache(resource_id, md5, dummy_file_path_,
+  TestStoreToCache(id, md5, dummy_file_path_,
                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
 
   // Then try to get the existing file from cache.
-  TestGetFile(resource_id, FILE_ERROR_OK);
+  TestGetFile(id, FILE_ERROR_OK);
 
-  // Get file from cache with different resource id.
-  resource_id = "document:1a2b";
-  TestGetFile(resource_id, FILE_ERROR_NOT_FOUND);
+  // Get file from cache with different ID.
+  id = "document:1a2b";
+  TestGetFile(id, FILE_ERROR_NOT_FOUND);
 }
 
 TEST_F(FileCacheTestOnUIThread, RemoveFromCacheSimple) {
-  // Use alphanumeric characters for resource id.
-  std::string resource_id("pdf:1a2b");
+  std::string id("pdf:1a2b");
   std::string md5("abcdef0123456789");
   // First store a file to cache.
-  TestStoreToCache(resource_id, md5, dummy_file_path_,
+  TestStoreToCache(id, md5, dummy_file_path_,
                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
 
   // Then try to remove existing file from cache.
-  TestRemoveFromCache(resource_id, FILE_ERROR_OK);
+  TestRemoveFromCache(id, FILE_ERROR_OK);
 
-  // Repeat using non-alphanumeric characters for resource id, including '.'
+  // Repeat using non-alphanumeric characters for ID, including '.'
   // which is an extension separator.
-  resource_id = "pdf:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?";
-  TestStoreToCache(resource_id, md5, dummy_file_path_,
+  id = "pdf:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?";
+  TestStoreToCache(id, md5, dummy_file_path_,
                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
 
-  TestRemoveFromCache(resource_id, FILE_ERROR_OK);
+  TestRemoveFromCache(id, FILE_ERROR_OK);
 }
 
 TEST_F(FileCacheTestOnUIThread, PinAndUnpin) {
-  std::string resource_id("pdf:1a2b");
+  std::string id("pdf:1a2b");
   std::string md5("abcdef0123456789");
 
   // First store a file to cache.
-  TestStoreToCache(resource_id, md5, dummy_file_path_,
+  TestStoreToCache(id, md5, dummy_file_path_,
                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
 
   // Pin the existing file in cache.
-  TestPin(resource_id, FILE_ERROR_OK,
+  TestPin(id, FILE_ERROR_OK,
           TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
 
   // Unpin the existing file in cache.
-  TestUnpin(resource_id, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
+  TestUnpin(id, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
 
   // Pin back the same existing file in cache.
-  TestPin(resource_id, FILE_ERROR_OK,
+  TestPin(id, FILE_ERROR_OK,
           TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
 
   // Pin a non-existent file in cache.
-  resource_id = "document:1a2b";
+  id = "document:1a2b";
 
-  TestPin(resource_id, FILE_ERROR_OK, TEST_CACHE_STATE_PINNED);
+  TestPin(id, FILE_ERROR_OK, TEST_CACHE_STATE_PINNED);
 
   // Unpin the previously pinned non-existent file in cache.
-  TestUnpin(resource_id, FILE_ERROR_OK, TEST_CACHE_STATE_NONE);
+  TestUnpin(id, FILE_ERROR_OK, TEST_CACHE_STATE_NONE);
 
   // Unpin a file that doesn't exist in cache and is not pinned, i.e. cache
   // has zero knowledge of the file.
-  resource_id = "not-in-cache:1a2b";
+  id = "not-in-cache:1a2b";
 
-  TestUnpin(resource_id, FILE_ERROR_NOT_FOUND, TEST_CACHE_STATE_NONE);
+  TestUnpin(id, FILE_ERROR_NOT_FOUND, TEST_CACHE_STATE_NONE);
 }
 
 TEST_F(FileCacheTestOnUIThread, StoreToCachePinned) {
-  std::string resource_id("pdf:1a2b");
+  std::string id("pdf:1a2b");
   std::string md5("abcdef0123456789");
 
   // Pin a non-existent file.
-  TestPin(resource_id, FILE_ERROR_OK, TEST_CACHE_STATE_PINNED);
+  TestPin(id, FILE_ERROR_OK, TEST_CACHE_STATE_PINNED);
 
   // Store an existing file to a previously pinned file.
-  TestStoreToCache(resource_id, md5, dummy_file_path_, FILE_ERROR_OK,
+  TestStoreToCache(id, md5, dummy_file_path_, FILE_ERROR_OK,
                    TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
 
   // Store a non-existent file to a previously pinned and stored file.
-  TestStoreToCache(resource_id, md5,
+  TestStoreToCache(id, md5,
                    base::FilePath::FromUTF8Unsafe("non_existent_file"),
                    FILE_ERROR_FAILED,
                    TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
 }
 
 TEST_F(FileCacheTestOnUIThread, GetFromCachePinned) {
-  std::string resource_id("pdf:1a2b");
+  std::string id("pdf:1a2b");
   std::string md5("abcdef0123456789");
 
   // Pin a non-existent file.
-  TestPin(resource_id, FILE_ERROR_OK, TEST_CACHE_STATE_PINNED);
+  TestPin(id, FILE_ERROR_OK, TEST_CACHE_STATE_PINNED);
 
   // Get the non-existent pinned file from cache.
-  TestGetFile(resource_id, FILE_ERROR_NOT_FOUND);
+  TestGetFile(id, FILE_ERROR_NOT_FOUND);
 
   // Store an existing file to the previously pinned non-existent file.
-  TestStoreToCache(resource_id, md5, dummy_file_path_, FILE_ERROR_OK,
+  TestStoreToCache(id, md5, dummy_file_path_, FILE_ERROR_OK,
                    TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
 
   // Get the previously pinned and stored file from cache.
-  TestGetFile(resource_id, FILE_ERROR_OK);
+  TestGetFile(id, FILE_ERROR_OK);
 }
 
 TEST_F(FileCacheTestOnUIThread, RemoveFromCachePinned) {
-  // Use alphanumeric characters for resource_id.
-  std::string resource_id("pdf:1a2b");
+  std::string id("pdf:1a2b");
   std::string md5("abcdef0123456789");
 
   // Store a file to cache, and pin it.
-  TestStoreToCache(resource_id, md5, dummy_file_path_,
+  TestStoreToCache(id, md5, dummy_file_path_,
                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
-  TestPin(resource_id, FILE_ERROR_OK,
+  TestPin(id, FILE_ERROR_OK,
           TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
 
-  // Remove |resource_id| from cache.
-  TestRemoveFromCache(resource_id, FILE_ERROR_OK);
+  // Remove |id| from cache.
+  TestRemoveFromCache(id, FILE_ERROR_OK);
 
-  // Repeat using non-alphanumeric characters for resource id, including '.'
+  // Use non-alphanumeric characters for ID, including '.'
   // which is an extension separator.
-  resource_id = "pdf:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?";
+  id = "pdf:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?";
 
-  TestStoreToCache(resource_id, md5, dummy_file_path_,
+  TestStoreToCache(id, md5, dummy_file_path_,
                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
-  TestPin(resource_id, FILE_ERROR_OK,
+  TestPin(id, FILE_ERROR_OK,
           TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
 
-  TestRemoveFromCache(resource_id, FILE_ERROR_OK);
+  TestRemoveFromCache(id, FILE_ERROR_OK);
 }
 
 TEST_F(FileCacheTestOnUIThread, DirtyCacheSimple) {
-  std::string resource_id("pdf:1a2b");
+  std::string id("pdf:1a2b");
   std::string md5("abcdef0123456789");
 
   // First store a file to cache.
-  TestStoreToCache(resource_id, md5, dummy_file_path_,
+  TestStoreToCache(id, md5, dummy_file_path_,
                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
 
   // Mark the file dirty.
-  TestMarkDirty(resource_id, FILE_ERROR_OK,
+  TestMarkDirty(id, FILE_ERROR_OK,
                 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
 
   // Clear dirty state of the file.
-  TestClearDirty(resource_id, md5, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
+  TestClearDirty(id, md5, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
 }
 
 TEST_F(FileCacheTestOnUIThread, DirtyCachePinned) {
-  std::string resource_id("pdf:1a2b");
+  std::string id("pdf:1a2b");
   std::string md5("abcdef0123456789");
 
   // First store a file to cache and pin it.
-  TestStoreToCache(resource_id, md5, dummy_file_path_,
+  TestStoreToCache(id, md5, dummy_file_path_,
                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
-  TestPin(resource_id, FILE_ERROR_OK,
+  TestPin(id, FILE_ERROR_OK,
           TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
 
   // Mark the file dirty.
-  TestMarkDirty(resource_id, FILE_ERROR_OK,
+  TestMarkDirty(id, FILE_ERROR_OK,
                 TEST_CACHE_STATE_PRESENT |
                 TEST_CACHE_STATE_DIRTY |
                 TEST_CACHE_STATE_PINNED);
 
   // Clear dirty state of the file.
-  TestClearDirty(resource_id, md5, FILE_ERROR_OK,
+  TestClearDirty(id, md5, FILE_ERROR_OK,
                  TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
 }
 
 TEST_F(FileCacheTestOnUIThread, PinAndUnpinDirtyCache) {
-  std::string resource_id("pdf:1a2b");
+  std::string id("pdf:1a2b");
   std::string md5("abcdef0123456789");
 
   // First store a file to cache and mark it as dirty.
-  TestStoreToCache(resource_id, md5, dummy_file_path_,
+  TestStoreToCache(id, md5, dummy_file_path_,
                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
-  TestMarkDirty(resource_id, FILE_ERROR_OK,
+  TestMarkDirty(id, FILE_ERROR_OK,
                 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
 
   // Verifies dirty file exists.
   base::FilePath dirty_path;
   FileError error = FILE_ERROR_FAILED;
   cache_->GetFileOnUIThread(
-      resource_id,
+      id,
       google_apis::test_util::CreateCopyResultCallback(&error, &dirty_path));
   test_util::RunBlockingPoolTask();
   EXPECT_EQ(FILE_ERROR_OK, error);
   EXPECT_TRUE(base::PathExists(dirty_path));
 
   // Pin the dirty file.
-  TestPin(resource_id, FILE_ERROR_OK,
+  TestPin(id, FILE_ERROR_OK,
           TEST_CACHE_STATE_PRESENT |
           TEST_CACHE_STATE_DIRTY |
           TEST_CACHE_STATE_PINNED);
@@ -603,7 +598,7 @@
   EXPECT_TRUE(base::PathExists(dirty_path));
 
   // Unpin the dirty file.
-  TestUnpin(resource_id, FILE_ERROR_OK,
+  TestUnpin(id, FILE_ERROR_OK,
             TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
 
   // Verify dirty file still exist at the same pathname.
@@ -611,67 +606,66 @@
 }
 
 TEST_F(FileCacheTestOnUIThread, DirtyCacheRepetitive) {
-  std::string resource_id("pdf:1a2b");
+  std::string id("pdf:1a2b");
   std::string md5("abcdef0123456789");
 
   // First store a file to cache.
-  TestStoreToCache(resource_id, md5, dummy_file_path_,
+  TestStoreToCache(id, md5, dummy_file_path_,
                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
 
   // Mark the file dirty.
-  TestMarkDirty(resource_id, FILE_ERROR_OK,
+  TestMarkDirty(id, FILE_ERROR_OK,
                 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
 
   // Again, mark the file dirty.  Nothing should change.
-  TestMarkDirty(resource_id, FILE_ERROR_OK,
+  TestMarkDirty(id, FILE_ERROR_OK,
                 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
 
   // Clear dirty state of the file.
-  TestClearDirty(resource_id, md5, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
+  TestClearDirty(id, md5, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
 
   // Again, clear dirty state of the file, which is no longer dirty.
-  TestClearDirty(resource_id, md5, FILE_ERROR_INVALID_OPERATION,
+  TestClearDirty(id, md5, FILE_ERROR_INVALID_OPERATION,
                  TEST_CACHE_STATE_PRESENT);
 }
 
 TEST_F(FileCacheTestOnUIThread, DirtyCacheInvalid) {
-  std::string resource_id("pdf:1a2b");
+  std::string id("pdf:1a2b");
   std::string md5("abcdef0123456789");
 
   // Mark a non-existent file dirty.
-  TestMarkDirty(resource_id, FILE_ERROR_NOT_FOUND, TEST_CACHE_STATE_NONE);
+  TestMarkDirty(id, FILE_ERROR_NOT_FOUND, TEST_CACHE_STATE_NONE);
 
   // Clear dirty state of a non-existent file.
-  TestClearDirty(resource_id, md5, FILE_ERROR_NOT_FOUND, TEST_CACHE_STATE_NONE);
+  TestClearDirty(id, md5, FILE_ERROR_NOT_FOUND, TEST_CACHE_STATE_NONE);
 
   // Store a file to cache.
-  TestStoreToCache(resource_id, md5, dummy_file_path_,
+  TestStoreToCache(id, md5, dummy_file_path_,
                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
 
   // Clear dirty state of a non-dirty existing file.
-  TestClearDirty(resource_id, md5, FILE_ERROR_INVALID_OPERATION,
+  TestClearDirty(id, md5, FILE_ERROR_INVALID_OPERATION,
                  TEST_CACHE_STATE_PRESENT);
 
-  // Mark an existing file dirty, then store a new file to the same resource id
+  // Mark an existing file dirty, then store a new file to the same ID
   // but different md5, which should fail.
-  TestMarkDirty(resource_id, FILE_ERROR_OK,
+  TestMarkDirty(id, FILE_ERROR_OK,
                 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
   md5 = "new_md5";
-  TestStoreToCache(resource_id, md5, dummy_file_path_,
-                   FILE_ERROR_IN_USE,
+  TestStoreToCache(id, md5, dummy_file_path_, FILE_ERROR_IN_USE,
                    TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
 }
 
 TEST_F(FileCacheTestOnUIThread, RemoveFromDirtyCache) {
-  std::string resource_id("pdf:1a2b");
+  std::string id("pdf:1a2b");
   std::string md5("abcdef0123456789");
 
   // Store a file to cache, pin it, mark it dirty and commit it.
-  TestStoreToCache(resource_id, md5, dummy_file_path_,
+  TestStoreToCache(id, md5, dummy_file_path_,
                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
-  TestPin(resource_id, FILE_ERROR_OK,
+  TestPin(id, FILE_ERROR_OK,
           TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
-  TestMarkDirty(resource_id, FILE_ERROR_OK,
+  TestMarkDirty(id, FILE_ERROR_OK,
                 TEST_CACHE_STATE_PRESENT |
                 TEST_CACHE_STATE_PINNED |
                 TEST_CACHE_STATE_DIRTY);
@@ -680,39 +674,38 @@
   // FileCache::Remove. Upper layer cache clearance functions like
   // FreeDiskSpaceIfNeededFor() and RemoveStaleCacheFiles() takes care of
   // securing dirty files.
-  TestRemoveFromCache(resource_id, FILE_ERROR_OK);
+  TestRemoveFromCache(id, FILE_ERROR_OK);
 }
 
 TEST_F(FileCacheTestOnUIThread, MountUnmount) {
-  std::string resource_id("pdf:1a2b");
+  std::string id("pdf:1a2b");
   std::string md5("abcdef0123456789");
 
   // First store a file to cache.
-  TestStoreToCache(resource_id, md5, dummy_file_path_,
+  TestStoreToCache(id, md5, dummy_file_path_,
                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
 
   // Mark the file mounted.
-  TestMarkAsMounted(resource_id, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
-  EXPECT_TRUE(CacheEntryExists(resource_id));
+  TestMarkAsMounted(id, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
+  EXPECT_TRUE(CacheEntryExists(id));
 
   // Try to remove the file.
-  TestRemoveFromCache(resource_id, FILE_ERROR_IN_USE);
+  TestRemoveFromCache(id, FILE_ERROR_IN_USE);
 
   // Clear mounted state of the file.
   base::FilePath file_path;
   FileError error = FILE_ERROR_FAILED;
   cache_->GetFileOnUIThread(
-      resource_id,
+      id,
       google_apis::test_util::CreateCopyResultCallback(&error, &file_path));
   test_util::RunBlockingPoolTask();
   EXPECT_EQ(FILE_ERROR_OK, error);
 
-  TestMarkAsUnmounted(resource_id, file_path, FILE_ERROR_OK,
-                      TEST_CACHE_STATE_PRESENT);
-  EXPECT_TRUE(CacheEntryExists(resource_id));
+  TestMarkAsUnmounted(id, file_path, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
+  EXPECT_TRUE(CacheEntryExists(id));
 
   // Try to remove the file.
-  TestRemoveFromCache(resource_id, FILE_ERROR_OK);
+  TestRemoveFromCache(id, FILE_ERROR_OK);
 }
 
 TEST_F(FileCacheTestOnUIThread, Iterate) {
@@ -721,38 +714,38 @@
   ASSERT_TRUE(test_util::PrepareTestCacheResources(cache_.get(),
                                                    cache_resources));
 
-  std::vector<std::string> resource_ids;
+  std::vector<std::string> ids;
   std::vector<FileCacheEntry> cache_entries;
   bool completed = false;
   cache_->IterateOnUIThread(
-      base::Bind(&OnIterate, &resource_ids, &cache_entries),
+      base::Bind(&OnIterate, &ids, &cache_entries),
       base::Bind(&OnIterateCompleted, &completed));
   test_util::RunBlockingPoolTask();
 
   ASSERT_TRUE(completed);
 
-  sort(resource_ids.begin(), resource_ids.end());
-  ASSERT_EQ(6U, resource_ids.size());
-  EXPECT_EQ("dirty:existing", resource_ids[0]);
-  EXPECT_EQ("dirty_and_pinned:existing", resource_ids[1]);
-  EXPECT_EQ("pinned:existing", resource_ids[2]);
-  EXPECT_EQ("pinned:non-existent", resource_ids[3]);
-  EXPECT_EQ("tmp:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?", resource_ids[4]);
-  EXPECT_EQ("tmp:resource_id", resource_ids[5]);
+  sort(ids.begin(), ids.end());
+  ASSERT_EQ(6U, ids.size());
+  EXPECT_EQ("dirty:existing", ids[0]);
+  EXPECT_EQ("dirty_and_pinned:existing", ids[1]);
+  EXPECT_EQ("pinned:existing", ids[2]);
+  EXPECT_EQ("pinned:non-existent", ids[3]);
+  EXPECT_EQ("tmp:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?", ids[4]);
+  EXPECT_EQ("tmp:resource_id", ids[5]);
 
   ASSERT_EQ(6U, cache_entries.size());
 }
 
 TEST_F(FileCacheTestOnUIThread, ClearAll) {
-  std::string resource_id("pdf:1a2b");
+  std::string id("pdf:1a2b");
   std::string md5("abcdef0123456789");
 
   // Store an existing file.
-  TestStoreToCache(resource_id, md5, dummy_file_path_,
+  TestStoreToCache(id, md5, dummy_file_path_,
                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
 
   // Verify that there's only one cached file.
-  EXPECT_EQ(1U, CountCacheFiles(resource_id, md5));
+  EXPECT_EQ(1U, CountCacheFiles(id, md5));
 
   // Clear cache.
   bool success = false;
@@ -763,40 +756,40 @@
 
   // Verify that all the cache is removed.
   expected_error_ = FILE_ERROR_OK;
-  VerifyRemoveFromCache(FILE_ERROR_OK, resource_id);
-  EXPECT_EQ(0U, CountCacheFiles(resource_id, md5));
+  VerifyRemoveFromCache(FILE_ERROR_OK, id);
+  EXPECT_EQ(0U, CountCacheFiles(id, md5));
 }
 
 TEST_F(FileCacheTestOnUIThread, StoreToCacheNoSpace) {
   fake_free_disk_space_getter_->set_default_value(0);
 
-  std::string resource_id("pdf:1a2b");
+  std::string id("pdf:1a2b");
   std::string md5("abcdef0123456789");
 
   // Try to store an existing file.
-  TestStoreToCache(resource_id, md5, dummy_file_path_,
+  TestStoreToCache(id, md5, dummy_file_path_,
                    FILE_ERROR_NO_LOCAL_SPACE,
                    TEST_CACHE_STATE_NONE);
 
   // Verify that there's no files added.
-  EXPECT_EQ(0U, CountCacheFiles(resource_id, md5));
+  EXPECT_EQ(0U, CountCacheFiles(id, md5));
 }
 
 TEST_F(FileCacheTestOnUIThread, UpdatePinnedCache) {
-  std::string resource_id("pdf:1a2b");
+  std::string id("pdf:1a2b");
   std::string md5("abcdef0123456789");
   std::string md5_modified("aaaaaa0000000000");
 
   // Store an existing file.
-  TestStoreToCache(resource_id, md5, dummy_file_path_, FILE_ERROR_OK,
+  TestStoreToCache(id, md5, dummy_file_path_, FILE_ERROR_OK,
                    TEST_CACHE_STATE_PRESENT);
 
   // Pin the file.
-  TestPin(resource_id, FILE_ERROR_OK,
+  TestPin(id, FILE_ERROR_OK,
           TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
 
   // Store the file with a modified content and md5. It should stay pinned.
-  TestStoreToCache(resource_id, md5_modified, dummy_file_path_, FILE_ERROR_OK,
+  TestStoreToCache(id, md5_modified, dummy_file_path_, FILE_ERROR_OK,
                    TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
 }
 
@@ -873,21 +866,21 @@
   ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
 
   // Store a file as a 'temporary' file and remember the path.
-  const std::string resource_id_tmp = "id_tmp", md5_tmp = "md5_tmp";
+  const std::string id_tmp = "id_tmp", md5_tmp = "md5_tmp";
   ASSERT_EQ(FILE_ERROR_OK,
-            cache_->Store(resource_id_tmp, md5_tmp, src_file,
+            cache_->Store(id_tmp, md5_tmp, src_file,
                           FileCache::FILE_OPERATION_COPY));
   base::FilePath tmp_path;
-  ASSERT_EQ(FILE_ERROR_OK, cache_->GetFile(resource_id_tmp, &tmp_path));
+  ASSERT_EQ(FILE_ERROR_OK, cache_->GetFile(id_tmp, &tmp_path));
 
   // Store a file as a pinned file and remember the path.
-  const std::string resource_id_pinned = "id_pinned", md5_pinned = "md5_pinned";
+  const std::string id_pinned = "id_pinned", md5_pinned = "md5_pinned";
   ASSERT_EQ(FILE_ERROR_OK,
-            cache_->Store(resource_id_pinned, md5_pinned, src_file,
+            cache_->Store(id_pinned, md5_pinned, src_file,
                           FileCache::FILE_OPERATION_COPY));
-  ASSERT_EQ(FILE_ERROR_OK, cache_->Pin(resource_id_pinned));
+  ASSERT_EQ(FILE_ERROR_OK, cache_->Pin(id_pinned));
   base::FilePath pinned_path;
-  ASSERT_EQ(FILE_ERROR_OK, cache_->GetFile(resource_id_pinned, &pinned_path));
+  ASSERT_EQ(FILE_ERROR_OK, cache_->GetFile(id_pinned, &pinned_path));
 
   // Call FreeDiskSpaceIfNeededFor().
   fake_free_disk_space_getter_->set_default_value(test_util::kLotsOfSpace);
@@ -897,10 +890,10 @@
 
   // Only 'temporary' file gets removed.
   FileCacheEntry entry;
-  EXPECT_FALSE(cache_->GetCacheEntry(resource_id_tmp, &entry));
+  EXPECT_FALSE(cache_->GetCacheEntry(id_tmp, &entry));
   EXPECT_FALSE(base::PathExists(tmp_path));
 
-  EXPECT_TRUE(cache_->GetCacheEntry(resource_id_pinned, &entry));
+  EXPECT_TRUE(cache_->GetCacheEntry(id_pinned, &entry));
   EXPECT_TRUE(base::PathExists(pinned_path));
 
   // Returns false when disk space cannot be freed.
@@ -912,7 +905,7 @@
   const base::FilePath file_directory =
       temp_dir_.path().Append(util::kCacheFileDirectory);
 
-  // File with an old style "<resource ID>.<MD5>" name.
+  // File with an old style "<ID>.<MD5>" name.
   ASSERT_TRUE(google_apis::test_util::WriteStringToFile(
       file_directory.AppendASCII("id_koo.md5"), "koo"));
 
diff --git a/chrome/browser/chromeos/drive/file_system.cc b/chrome/browser/chromeos/drive/file_system.cc
index 2d527f9..960d209 100644
--- a/chrome/browser/chromeos/drive/file_system.cc
+++ b/chrome/browser/chromeos/drive/file_system.cc
@@ -12,6 +12,7 @@
 #include "base/prefs/pref_service.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/sequenced_worker_pool.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/drive/change_list_loader.h"
 #include "chrome/browser/chromeos/drive/change_list_processor.h"
 #include "chrome/browser/chromeos/drive/drive.pb.h"
@@ -31,6 +32,7 @@
 #include "chrome/browser/chromeos/drive/file_system_observer.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/chromeos/drive/job_scheduler.h"
+#include "chrome/browser/chromeos/drive/logging.h"
 #include "chrome/browser/chromeos/drive/remove_stale_cache_files.h"
 #include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
 #include "chrome/browser/chromeos/drive/search_metadata.h"
@@ -40,6 +42,7 @@
 #include "chrome/browser/google_apis/drive_api_parser.h"
 #include "chrome/common/pref_names.h"
 #include "content/public/browser/browser_thread.h"
+#include "net/http/http_status_code.h"
 
 using content::BrowserThread;
 
@@ -53,8 +56,13 @@
     internal::FileCache* cache,
     const base::FilePath& file_path,
     ResourceEntry* entry) {
+  std::string local_id;
   FileError error =
-      resource_metadata->GetResourceEntryByPath(file_path, entry);
+      resource_metadata->GetIdByPath(file_path, &local_id);
+  if (error != FILE_ERROR_OK)
+    return error;
+
+  error = resource_metadata->GetResourceEntryById(local_id, entry);
   if (error != FILE_ERROR_OK)
     return error;
 
@@ -66,13 +74,12 @@
 
   // When no dirty cache is found, use the original resource entry as is.
   FileCacheEntry cache_entry;
-  if (!cache->GetCacheEntry(entry->resource_id(), &cache_entry) ||
-      !cache_entry.is_dirty())
+  if (!cache->GetCacheEntry(local_id, &cache_entry) || !cache_entry.is_dirty())
     return FILE_ERROR_OK;
 
   // If the cache is dirty, obtain the file info from the cache file itself.
   base::FilePath local_cache_path;
-  error = cache->GetFile(entry->resource_id(), &local_cache_path);
+  error = cache->GetFile(local_id, &local_cache_path);
   if (error != FILE_ERROR_OK)
     return error;
 
@@ -96,6 +103,81 @@
   callback.Run(error, entry.Pass());
 }
 
+// Used to implement Pin().
+FileError PinInternal(internal::ResourceMetadata* resource_metadata,
+                      internal::FileCache* cache,
+                      const base::FilePath& file_path,
+                      std::string* local_id) {
+  FileError error = resource_metadata->GetIdByPath(file_path, local_id);
+  if (error != FILE_ERROR_OK)
+    return error;
+
+  ResourceEntry entry;
+  error = resource_metadata->GetResourceEntryById(*local_id, &entry);
+  if (error != FILE_ERROR_OK)
+    return error;
+
+  // TODO(hashimoto): Support pinning directories. crbug.com/127831
+  if (entry.file_info().is_directory())
+    return FILE_ERROR_NOT_A_FILE;
+
+  return cache->Pin(*local_id);
+}
+
+// Used to implement Unpin().
+FileError UnpinInternal(internal::ResourceMetadata* resource_metadata,
+                        internal::FileCache* cache,
+                        const base::FilePath& file_path,
+                        std::string* local_id) {
+  FileError error = resource_metadata->GetIdByPath(file_path, local_id);
+  if (error != FILE_ERROR_OK)
+    return error;
+
+  return cache->Unpin(*local_id);
+}
+
+// Used to implement MarkCacheFileAsMounted().
+FileError MarkCacheFileAsMountedInternal(
+    internal::ResourceMetadata* resource_metadata,
+    internal::FileCache* cache,
+    const base::FilePath& drive_file_path,
+    base::FilePath* cache_file_path) {
+  std::string local_id;
+  FileError error = resource_metadata->GetIdByPath(drive_file_path, &local_id);
+  if (error != FILE_ERROR_OK)
+    return error;
+
+  return cache->MarkAsMounted(local_id, cache_file_path);
+}
+
+// Runs the callback with arguments.
+void RunMarkMountedCallback(const MarkMountedCallback& callback,
+                            base::FilePath* cache_file_path,
+                            FileError error) {
+  DCHECK(!callback.is_null());
+  callback.Run(error, *cache_file_path);
+}
+
+// Used to implement GetCacheEntryByPath.
+bool GetCacheEntryByPathInternal(internal::ResourceMetadata* resource_metadata,
+                                 internal::FileCache* cache,
+                                 const base::FilePath& drive_file_path,
+                                 FileCacheEntry* cache_entry) {
+  std::string id;
+  if (resource_metadata->GetIdByPath(drive_file_path, &id) != FILE_ERROR_OK)
+    return false;
+
+  return cache->GetCacheEntry(id, cache_entry);
+}
+
+// Runs the callback with arguments.
+void RunGetCacheEntryCallback(const GetCacheEntryCallback& callback,
+                              const FileCacheEntry* cache_entry,
+                              bool success) {
+  DCHECK(!callback.is_null());
+  callback.Run(success, *cache_entry);
+}
+
 // Callback for ResourceMetadata::GetLargestChangestamp.
 // |callback| must not be null.
 void OnGetLargestChangestamp(
@@ -118,6 +200,45 @@
   callback.Run(error);
 }
 
+// Checks whether the |url| passed to the constructor is accessible. If it is
+// not, invokes |on_stale_closure|.
+class StaleURLChecker : public net::URLFetcherDelegate {
+ public:
+  StaleURLChecker(const GURL& url, const base::Closure& on_stale_closure)
+      : on_stale_closure_(on_stale_closure) {
+    fetcher_.reset(net::URLFetcher::Create(url, net::URLFetcher::HEAD, this));
+    fetcher_->SetRequestContext(g_browser_process->system_request_context());
+    fetcher_->Start();
+  }
+
+  virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
+    int code = source->GetResponseCode();
+    if (code == net::HTTP_FORBIDDEN)
+      on_stale_closure_.Run();
+    delete this;
+  }
+
+ private:
+  scoped_ptr<net::URLFetcher> fetcher_;
+  const base::Closure on_stale_closure_;
+};
+
+// Checks the first thumbnail URL in |entries| whether it is still available
+// by sending a HEAD request. If it's stale, invokes |on_stale_closure|.
+void CheckStaleThumbnailURL(ResourceEntryVector* entries,
+                            const base::Closure& on_stale_closure) {
+  const char kImageThumbnailDomain[] = "googleusercontent.com";
+  for (size_t i = 0; i < entries->size(); ++i) {
+    const std::string& url =
+        entries->at(i).file_specific_info().thumbnail_url();
+    if (url.find(kImageThumbnailDomain) != std::string::npos) {
+      // The stale URL checker deletes itself.
+      new StaleURLChecker(GURL(url), on_stale_closure);
+      break;
+    }
+  }
+}
+
 }  // namespace
 
 FileSystem::FileSystem(
@@ -143,6 +264,10 @@
 }
 
 void FileSystem::Reload() {
+  // Discard the current loader and renew. This is to avoid that change lists
+  // requested before the metadata reset is applied after the reset.
+  SetupChangeListLoader();
+
   resource_metadata_->ResetOnUIThread(base::Bind(
       &FileSystem::ReloadAfterReset,
       weak_ptr_factory_.GetWeakPtr()));
@@ -239,8 +364,6 @@
     return;
   }
 
-  SetupChangeListLoader();
-
   change_list_loader_->LoadIfNeeded(
       internal::DirectoryFetchInfo(),
       base::Bind(&FileSystem::OnUpdateChecked,
@@ -392,44 +515,29 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  GetResourceEntryByPath(file_path,
-                         base::Bind(&FileSystem::PinAfterGetResourceEntryByPath,
-                                    weak_ptr_factory_.GetWeakPtr(),
-                                    callback));
-}
-
-void FileSystem::PinAfterGetResourceEntryByPath(
-    const FileOperationCallback& callback,
-    FileError error,
-    scoped_ptr<ResourceEntry> entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(!callback.is_null());
-
-  // TODO(hashimoto): Support pinning directories. crbug.com/127831
-  if (entry && entry->file_info().is_directory())
-    error = FILE_ERROR_NOT_A_FILE;
-
-  if (error != FILE_ERROR_OK) {
-    callback.Run(error);
-    return;
-  }
-  DCHECK(entry);
-
-  cache_->PinOnUIThread(entry->resource_id(),
-                        base::Bind(&FileSystem::FinishPin,
-                                   weak_ptr_factory_.GetWeakPtr(),
-                                   callback,
-                                   entry->resource_id()));
+  std::string* local_id = new std::string;
+  base::PostTaskAndReplyWithResult(
+      blocking_task_runner_,
+      FROM_HERE,
+      base::Bind(&PinInternal,
+                 resource_metadata_,
+                 cache_,
+                 file_path,
+                 local_id),
+      base::Bind(&FileSystem::FinishPin,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 callback,
+                 base::Owned(local_id)));
 }
 
 void FileSystem::FinishPin(const FileOperationCallback& callback,
-                           const std::string& resource_id,
+                           const std::string* local_id,
                            FileError error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
   if (error == FILE_ERROR_OK)
-    sync_client_->AddFetchTask(resource_id);
+    sync_client_->AddFetchTask(*local_id);
   callback.Run(error);
 }
 
@@ -438,45 +546,29 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  GetResourceEntryByPath(
-      file_path,
-      base::Bind(&FileSystem::UnpinAfterGetResourceEntryByPath,
+  std::string* local_id = new std::string;
+  base::PostTaskAndReplyWithResult(
+      blocking_task_runner_,
+      FROM_HERE,
+      base::Bind(&UnpinInternal,
+                 resource_metadata_,
+                 cache_,
+                 file_path,
+                 local_id),
+      base::Bind(&FileSystem::FinishUnpin,
                  weak_ptr_factory_.GetWeakPtr(),
-                 callback));
-}
-
-void FileSystem::UnpinAfterGetResourceEntryByPath(
-    const FileOperationCallback& callback,
-    FileError error,
-    scoped_ptr<ResourceEntry> entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(!callback.is_null());
-
-  // TODO(hashimoto): Support pinning directories. crbug.com/127831
-  if (entry && entry->file_info().is_directory())
-    error = FILE_ERROR_NOT_A_FILE;
-
-  if (error != FILE_ERROR_OK) {
-    callback.Run(error);
-    return;
-  }
-  DCHECK(entry);
-
-  cache_->UnpinOnUIThread(entry->resource_id(),
-                          base::Bind(&FileSystem::FinishUnpin,
-                                     weak_ptr_factory_.GetWeakPtr(),
-                                     callback,
-                                     entry->resource_id()));
+                 callback,
+                 base::Owned(local_id)));
 }
 
 void FileSystem::FinishUnpin(const FileOperationCallback& callback,
-                             const std::string& resource_id,
+                             const std::string* local_id,
                              FileError error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
   if (error == FILE_ERROR_OK)
-    sync_client_->RemoveFetchTask(resource_id);
+    sync_client_->RemoveFetchTask(*local_id);
   callback.Run(error);
 }
 
@@ -666,10 +758,12 @@
       directory_path,
       base::Bind(&FileSystem::ReadDirectoryByPathAfterRead,
                  weak_ptr_factory_.GetWeakPtr(),
+                 directory_path,
                  callback));
 }
 
 void FileSystem::ReadDirectoryByPathAfterRead(
+    const base::FilePath& directory_path,
     const ReadDirectoryCallback& callback,
     FileError error,
     scoped_ptr<ResourceEntryVector> entries) {
@@ -692,6 +786,15 @@
     }
     filtered->push_back(entries->at(i));
   }
+
+  // Thumbnail URLs are short-lived. We check the validness of the URL in
+  // background, and refresh the metadata for the directory if necessary.
+  // TODO(kinaba): Remove this hack by using persistent URLs crbug.com/254025.
+  CheckStaleThumbnailURL(filtered.get(),
+                         base::Bind(&FileSystem::RefreshDirectory,
+                                    weak_ptr_factory_.GetWeakPtr(),
+                                    directory_path));
+
   callback.Run(FILE_ERROR_OK, filtered.Pass());
 }
 
@@ -792,11 +895,11 @@
 }
 
 void FileSystem::Search(const std::string& search_query,
-                        const GURL& next_url,
+                        const std::string& page_token,
                         const SearchCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
-  search_operation_->Search(search_query, next_url, callback);
+  search_operation_->Search(search_query, page_token, callback);
 }
 
 void FileSystem::SearchMetadata(const std::string& query,
@@ -822,8 +925,8 @@
 }
 
 void FileSystem::OnCacheFileUploadNeededByOperation(
-    const std::string& resource_id) {
-  sync_client_->AddUploadTask(resource_id);
+    const std::string& local_id) {
+  sync_client_->AddUploadTask(local_id);
 }
 
 void FileSystem::OnDirectoryChanged(const base::FilePath& directory_path) {
@@ -871,26 +974,18 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  GetResourceEntryByPath(
-      drive_file_path,
-      base::Bind(&FileSystem::MarkCacheFileAsMountedAfterGetResourceEntry,
-                 weak_ptr_factory_.GetWeakPtr(), callback));
-}
-
-void FileSystem::MarkCacheFileAsMountedAfterGetResourceEntry(
-    const MarkMountedCallback& callback,
-    FileError error,
-    scoped_ptr<ResourceEntry> entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(!callback.is_null());
-
-  if (error != FILE_ERROR_OK) {
-    callback.Run(error, base::FilePath());
-    return;
-  }
-
-  DCHECK(entry);
-  cache_->MarkAsMountedOnUIThread(entry->resource_id(), callback);
+  base::FilePath* cache_file_path = new base::FilePath;
+  base::PostTaskAndReplyWithResult(
+      blocking_task_runner_,
+      FROM_HERE,
+      base::Bind(&MarkCacheFileAsMountedInternal,
+                 resource_metadata_,
+                 cache_,
+                 drive_file_path,
+                 cache_file_path),
+      base::Bind(&RunMarkMountedCallback,
+                 callback,
+                 base::Owned(cache_file_path)));
 }
 
 void FileSystem::MarkCacheFileAsUnmounted(
@@ -906,14 +1001,24 @@
   cache_->MarkAsUnmountedOnUIThread(cache_file_path, callback);
 }
 
-void FileSystem::GetCacheEntryByResourceId(
-    const std::string& resource_id,
+void FileSystem::GetCacheEntryByPath(
+    const base::FilePath& drive_file_path,
     const GetCacheEntryCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(!resource_id.empty());
   DCHECK(!callback.is_null());
 
-  cache_->GetCacheEntryOnUIThread(resource_id, callback);
+  FileCacheEntry* cache_entry = new FileCacheEntry;
+  base::PostTaskAndReplyWithResult(
+      blocking_task_runner_,
+      FROM_HERE,
+      base::Bind(&GetCacheEntryByPathInternal,
+                 resource_metadata_,
+                 cache_,
+                 drive_file_path,
+                 cache_entry),
+      base::Bind(&RunGetCacheEntryCallback,
+                 callback,
+                 base::Owned(cache_entry)));
 }
 
 void FileSystem::OnDisableDriveHostedFilesChanged() {
@@ -956,4 +1061,34 @@
   open_file_operation_->OpenFile(file_path, open_mode, mime_type, callback);
 }
 
+void FileSystem::RefreshDirectory(const base::FilePath& directory_path) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  resource_metadata_->GetResourceEntryByPathOnUIThread(
+      directory_path,
+      base::Bind(&FileSystem::RefreshDirectoryAfterGetResourceEntry,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 directory_path));
+}
+
+void FileSystem::RefreshDirectoryAfterGetResourceEntry(
+    const base::FilePath& directory_path,
+    FileError error,
+    scoped_ptr<ResourceEntry> entry) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  if (error != FILE_ERROR_OK || !entry->file_info().is_directory())
+    return;
+
+  // Do not load special directories. Just return.
+  const std::string& id = entry->resource_id();
+  if (util::IsSpecialResourceId(id))
+    return;
+
+  util::Log(logging::LOG_INFO,
+            "Thumbnail refresh for %s", directory_path.AsUTF8Unsafe().c_str());
+  change_list_loader_->LoadDirectoryFromServer(
+      id, base::Bind(&util::EmptyFileOperationCallback));
+}
+
 }  // namespace drive
diff --git a/chrome/browser/chromeos/drive/file_system.h b/chrome/browser/chromeos/drive/file_system.h
index bc20e69..755247c 100644
--- a/chrome/browser/chromeos/drive/file_system.h
+++ b/chrome/browser/chromeos/drive/file_system.h
@@ -77,7 +77,7 @@
   virtual void RemoveObserver(FileSystemObserver* observer) OVERRIDE;
   virtual void CheckForUpdates() OVERRIDE;
   virtual void Search(const std::string& search_query,
-                      const GURL& next_url,
+                      const std::string& page_token,
                       const SearchCallback& callback) OVERRIDE;
   virtual void SearchMetadata(const std::string& query,
                               int options,
@@ -148,8 +148,8 @@
   virtual void MarkCacheFileAsUnmounted(
       const base::FilePath& cache_file_path,
       const FileOperationCallback& callback) OVERRIDE;
-  virtual void GetCacheEntryByResourceId(
-      const std::string& resource_id,
+  virtual void GetCacheEntryByPath(
+      const base::FilePath& drive_file_path,
       const GetCacheEntryCallback& callback) OVERRIDE;
   virtual void Reload() OVERRIDE;
 
@@ -157,7 +157,7 @@
   virtual void OnDirectoryChangedByOperation(
       const base::FilePath& directory_path) OVERRIDE;
   virtual void OnCacheFileUploadNeededByOperation(
-      const std::string& resource_id) OVERRIDE;
+      const std::string& local_id) OVERRIDE;
 
   // ChangeListLoader::Observer overrides.
   // Used to propagate events from ChangeListLoader.
@@ -190,20 +190,12 @@
                                 const FileOperationCallback& callback,
                                 FileError load_error);
 
-  // Used to implement Pin().
-  void PinAfterGetResourceEntryByPath(const FileOperationCallback& callback,
-                                      FileError error,
-                                      scoped_ptr<ResourceEntry> entry);
   void FinishPin(const FileOperationCallback& callback,
-                 const std::string& resource_id,
+                 const std::string* local_id,
                  FileError error);
 
-  // Used to implement Unpin().
-  void UnpinAfterGetResourceEntryByPath(const FileOperationCallback& callback,
-                                        FileError error,
-                                        scoped_ptr<ResourceEntry> entry);
   void FinishUnpin(const FileOperationCallback& callback,
-                   const std::string& resource_id,
+                   const std::string* local_id,
                    FileError error);
 
   // Callback for handling about resource fetch.
@@ -253,17 +245,11 @@
       const ReadDirectoryCallback& callback,
       FileError error);
   void ReadDirectoryByPathAfterRead(
+      const base::FilePath& directory_path,
       const ReadDirectoryCallback& callback,
       FileError error,
       scoped_ptr<ResourceEntryVector> entries);
 
-  // Part of MarkCacheFileAsMounted. Called after GetResourceEntryByPath is
-  // completed. |callback| must not be null.
-  void MarkCacheFileAsMountedAfterGetResourceEntry(
-      const MarkMountedCallback& callback,
-      FileError error,
-      scoped_ptr<ResourceEntry> entry);
-
   // Part of GetShareUrl. Resolves the resource entry to get the resource it,
   // and then uses it to ask for the share url. |callback| must not be null.
   void GetShareUrlAfterGetResourceEntry(
@@ -277,6 +263,13 @@
       google_apis::GDataErrorCode status,
       const GURL& share_url);
 
+  // Reloads the metadata for the directory to refresh stale thumbnail URLs.
+  void RefreshDirectory(const base::FilePath& directory_path);
+  void RefreshDirectoryAfterGetResourceEntry(
+      const base::FilePath& directory_path,
+      FileError error,
+      scoped_ptr<ResourceEntry> entry);
+
   // Used to get Drive related preferences.
   PrefService* pref_service_;
 
diff --git a/chrome/browser/chromeos/drive/file_system/copy_operation.cc b/chrome/browser/chromeos/drive/file_system/copy_operation.cc
index 62e8380..552bf78 100644
--- a/chrome/browser/chromeos/drive/file_system/copy_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/copy_operation.cc
@@ -27,6 +27,31 @@
 
 namespace {
 
+FileError PrepareCopy(internal::ResourceMetadata* metadata,
+                      const base::FilePath& src_path,
+                      const base::FilePath& dest_path,
+                      ResourceEntry* src_entry,
+                      std::string* parent_resource_id) {
+  FileError error = metadata->GetResourceEntryByPath(src_path, src_entry);
+  if (error != FILE_ERROR_OK)
+    return error;
+
+  ResourceEntry parent_entry;
+  error = metadata->GetResourceEntryByPath(dest_path.DirName(), &parent_entry);
+  if (error != FILE_ERROR_OK)
+    return error;
+
+  if (!parent_entry.file_info().is_directory())
+    return FILE_ERROR_NOT_A_DIRECTORY;
+
+  // Drive File System doesn't support recursive copy.
+  if (src_entry->file_info().is_directory())
+    return FILE_ERROR_INVALID_OPERATION;
+
+  *parent_resource_id = parent_entry.resource_id();
+  return FILE_ERROR_OK;
+}
+
 int64 GetFileSize(const base::FilePath& file_path) {
   int64 file_size;
   if (!file_util::GetFileSize(file_path, &file_size))
@@ -34,6 +59,24 @@
   return file_size;
 }
 
+// Stores the copied entry and returns its path.
+FileError UpdateLocalStateForServerSideCopy(
+    internal::ResourceMetadata* metadata,
+    const ResourceEntry& entry,
+    base::FilePath* file_path) {
+  std::string local_id;
+  FileError error = metadata->AddEntry(entry, &local_id);
+  // Depending on timing, the metadata may have inserted via change list
+  // already. So, FILE_ERROR_EXISTS is not an error.
+  if (error == FILE_ERROR_EXISTS)
+    error = metadata->GetIdByResourceId(entry.resource_id(), &local_id);
+
+  if (error == FILE_ERROR_OK)
+    *file_path = metadata->GetFilePath(local_id);
+
+  return error;
+}
+
 // Stores the file at |local_file_path| to the cache as a content of entry at
 // |remote_dest_path|, and marks it dirty.
 FileError UpdateLocalStateForScheduleTransfer(
@@ -65,23 +108,25 @@
 // of |remote_path| to prepare the necessary information for transfer.
 FileError PrepareTransferFileFromLocalToRemote(
     internal::ResourceMetadata* metadata,
-    const base::FilePath& local_path,
-    const base::FilePath& remote_path,
-    ResourceEntry* parent_entry) {
-  DCHECK(metadata);
-  DCHECK(parent_entry);
+    const base::FilePath& local_src_path,
+    const base::FilePath& remote_dest_path,
+    std::string* resource_id) {
+  ResourceEntry parent_entry;
+  FileError error = metadata->GetResourceEntryByPath(
+      remote_dest_path.DirName(), &parent_entry);
+  if (error != FILE_ERROR_OK)
+    return error;
 
-  return metadata->GetResourceEntryByPath(remote_path.DirName(), parent_entry);
-}
+  // The destination's parent must be a directory.
+  if (!parent_entry.file_info().is_directory())
+    return FILE_ERROR_NOT_A_DIRECTORY;
 
-FileError AddEntryToLocalMetadata(internal::ResourceMetadata* metadata,
-                                  const ResourceEntry& entry) {
-  FileError error = metadata->AddEntry(entry);
-  // Depending on timing, the metadata may have inserted via change list
-  // already. So, FILE_ERROR_EXISTS is not an error.
-  if (error == FILE_ERROR_EXISTS)
-    error = FILE_ERROR_OK;
-  return error;
+  // Try to parse GDoc File and extract the resource id, if necessary.
+  // Failing isn't problem. It'd be handled as a regular file, then.
+  if (util::HasGDocFileExtension(local_src_path))
+    *resource_id = util::ReadResourceIdFromGDocFile(local_src_path);
+
+  return FILE_ERROR_OK;
 }
 
 }  // namespace
@@ -125,41 +170,216 @@
 void CopyOperation::Copy(const base::FilePath& src_file_path,
                          const base::FilePath& dest_file_path,
                          const FileOperationCallback& callback) {
-  BrowserThread::CurrentlyOn(BrowserThread::UI);
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  metadata_->GetResourceEntryPairByPathsOnUIThread(
-      src_file_path,
-      dest_file_path.DirName(),
-      base::Bind(&CopyOperation::CopyAfterGetResourceEntryPair,
+  ResourceEntry* src_entry = new ResourceEntry;
+  std::string* parent_resource_id = new std::string;
+  base::PostTaskAndReplyWithResult(
+      blocking_task_runner_.get(),
+      FROM_HERE,
+      base::Bind(&PrepareCopy,
+                 metadata_, src_file_path, dest_file_path,
+                 src_entry, parent_resource_id),
+      base::Bind(&CopyOperation::CopyAfterPrepare,
                  weak_ptr_factory_.GetWeakPtr(),
-                 dest_file_path,
-                 callback));
+                 src_file_path, dest_file_path, callback,
+                 base::Owned(src_entry), base::Owned(parent_resource_id)));
+}
+
+void CopyOperation::CopyAfterPrepare(
+    const base::FilePath& src_file_path,
+    const base::FilePath& dest_file_path,
+    const FileOperationCallback& callback,
+    ResourceEntry* src_entry,
+    std::string* parent_resource_id,
+    FileError error) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!callback.is_null());
+
+  if (error != FILE_ERROR_OK) {
+    callback.Run(error);
+    return;
+  }
+
+  // If Drive API v2 is enabled, we can copy resources on server side.
+  if (util::IsDriveV2ApiEnabled()) {
+    base::FilePath new_title = dest_file_path.BaseName();
+    if (src_entry->file_specific_info().is_hosted_document()) {
+      // Drop the document extension, which should not be in the title.
+      // TODO(yoshiki): Remove this code with crbug.com/223304.
+      new_title = new_title.RemoveExtension();
+    }
+
+    CopyResourceOnServer(
+        src_entry->resource_id(), *parent_resource_id,
+        new_title.AsUTF8Unsafe(), callback);
+    return;
+  }
+
+  // Here after GData WAPI's code.
+  if (src_entry->file_specific_info().is_hosted_document()) {
+    // For hosted documents, GData WAPI copies it on server.
+    CopyHostedDocument(
+        src_entry->resource_id(),
+        // Drop the document extension, which should not be in the title.
+        // TODO(yoshiki): Remove this code with crbug.com/223304.
+        dest_file_path.RemoveExtension(),
+        callback);
+    return;
+  }
+
+  // For regular files, download the content, and then re-upload.
+  // Note that upload is done later by SyncClient.
+  download_operation_->EnsureFileDownloadedByPath(
+      src_file_path,
+      ClientContext(USER_INITIATED),
+      GetFileContentInitializedCallback(),
+      google_apis::GetContentCallback(),
+      base::Bind(&CopyOperation::CopyAfterDownload,
+                 weak_ptr_factory_.GetWeakPtr(), dest_file_path, callback));
+}
+
+void CopyOperation::CopyAfterDownload(
+    const base::FilePath& dest_file_path,
+    const FileOperationCallback& callback,
+    FileError error,
+    const base::FilePath& local_file_path,
+    scoped_ptr<ResourceEntry> entry) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!callback.is_null());
+
+  if (error != FILE_ERROR_OK) {
+    callback.Run(error);
+    return;
+  }
+
+  // This callback is only triggered for a regular file via Copy().
+  DCHECK(entry && !entry->file_specific_info().is_hosted_document());
+  ScheduleTransferRegularFile(local_file_path, dest_file_path, callback);
 }
 
 void CopyOperation::TransferFileFromLocalToRemote(
-    const base::FilePath& local_src_file_path,
-    const base::FilePath& remote_dest_file_path,
+    const base::FilePath& local_src_path,
+    const base::FilePath& remote_dest_path,
     const FileOperationCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  ResourceEntry* parent_entry = new ResourceEntry;
+  std::string* resource_id = new std::string;
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(),
       FROM_HERE,
-      base::Bind(&PrepareTransferFileFromLocalToRemote,
-                 metadata_,
-                 local_src_file_path,
-                 remote_dest_file_path,
-                 parent_entry),
+      base::Bind(
+          &PrepareTransferFileFromLocalToRemote,
+          metadata_, local_src_path, remote_dest_path, resource_id),
       base::Bind(
           &CopyOperation::TransferFileFromLocalToRemoteAfterPrepare,
           weak_ptr_factory_.GetWeakPtr(),
-          local_src_file_path,
-          remote_dest_file_path,
-          callback,
-          base::Owned(parent_entry)));
+          local_src_path, remote_dest_path, callback,
+          base::Owned(resource_id)));
+}
+
+void CopyOperation::TransferFileFromLocalToRemoteAfterPrepare(
+    const base::FilePath& local_src_path,
+    const base::FilePath& remote_dest_path,
+    const FileOperationCallback& callback,
+    std::string* resource_id,
+    FileError error) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!callback.is_null());
+
+  if (error != FILE_ERROR_OK) {
+    callback.Run(error);
+    return;
+  }
+
+  // For regular files, schedule the transfer.
+  if (resource_id->empty()) {
+    ScheduleTransferRegularFile(local_src_path, remote_dest_path, callback);
+    return;
+  }
+
+  // This is uploading a JSON file representing a hosted document.
+  // Copy the document on the Drive server.
+
+  // GDoc file may contain a resource ID in the old format.
+  const std::string canonicalized_resource_id =
+      drive_service_->CanonicalizeResourceId(*resource_id);
+
+  // TODO(hidehiko): Use CopyResource for Drive API v2.
+
+  CopyHostedDocument(
+      canonicalized_resource_id,
+      // Drop the document extension, which should not be
+      // in the document title.
+      // TODO(yoshiki): Remove this code with crbug.com/223304.
+      remote_dest_path.RemoveExtension(),
+      callback);
+}
+
+void CopyOperation::CopyResourceOnServer(
+    const std::string& resource_id,
+    const std::string& parent_resource_id,
+    const std::string& new_title,
+    const FileOperationCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!callback.is_null());
+
+  scheduler_->CopyResource(
+      resource_id, parent_resource_id, new_title,
+      base::Bind(&CopyOperation::CopyResourceOnServerAfterServerSideCopy,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 callback));
+}
+
+void CopyOperation::CopyResourceOnServerAfterServerSideCopy(
+    const FileOperationCallback& callback,
+    google_apis::GDataErrorCode status,
+    scoped_ptr<google_apis::ResourceEntry> resource_entry) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!callback.is_null());
+
+  FileError error = GDataToFileError(status);
+  if (error != FILE_ERROR_OK) {
+    callback.Run(error);
+    return;
+  }
+
+  DCHECK(resource_entry);
+  ResourceEntry entry;
+  std::string parent_resource_id;
+  if (!ConvertToResourceEntry(*resource_entry, &entry, &parent_resource_id)) {
+    callback.Run(FILE_ERROR_NOT_A_FILE);
+    return;
+  }
+
+  // TODO(hashimoto): Resolve local ID before use. crbug.com/260514
+  entry.set_parent_local_id(parent_resource_id);
+
+  // The copy on the server side is completed successfully. Update the local
+  // metadata.
+  base::FilePath* file_path = new base::FilePath;
+  base::PostTaskAndReplyWithResult(
+      blocking_task_runner_.get(),
+      FROM_HERE,
+      base::Bind(&UpdateLocalStateForServerSideCopy,
+                 metadata_, entry, file_path),
+      base::Bind(&CopyOperation::CopyResourceOnServerAfterUpdateLocalState,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 callback, base::Owned(file_path)));
+}
+
+void CopyOperation::CopyResourceOnServerAfterUpdateLocalState(
+    const FileOperationCallback& callback,
+    base::FilePath* file_path,
+    FileError error) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!callback.is_null());
+
+  if (error == FILE_ERROR_OK)
+    observer_->OnDirectoryChangedByOperation(file_path->DirName());
+  callback.Run(error);
 }
 
 void CopyOperation::ScheduleTransferRegularFile(
@@ -270,9 +490,9 @@
   callback.Run(error);
 }
 
-void CopyOperation::CopyHostedDocumentToDirectory(
-    const base::FilePath& dest_path,
+void CopyOperation::CopyHostedDocument(
     const std::string& resource_id,
+    const base::FilePath& dest_path,
     const FileOperationCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
@@ -280,11 +500,11 @@
   scheduler_->CopyHostedDocument(
       resource_id,
       dest_path.BaseName().AsUTF8Unsafe(),
-      base::Bind(&CopyOperation::OnCopyHostedDocumentCompleted,
+      base::Bind(&CopyOperation::CopyHostedDocumentAfterServerSideCopy,
                  weak_ptr_factory_.GetWeakPtr(), dest_path, callback));
 }
 
-void CopyOperation::OnCopyHostedDocumentCompleted(
+void CopyOperation::CopyHostedDocumentAfterServerSideCopy(
     const base::FilePath& dest_path,
     const FileOperationCallback& callback,
     google_apis::GDataErrorCode status,
@@ -297,37 +517,42 @@
     callback.Run(error);
     return;
   }
-  DCHECK(resource_entry);
 
+  DCHECK(resource_entry);
   ResourceEntry entry;
-  if (!ConvertToResourceEntry(*resource_entry, &entry)) {
+  std::string parent_resource_id;
+  if (!ConvertToResourceEntry(*resource_entry, &entry, &parent_resource_id)) {
     callback.Run(FILE_ERROR_NOT_A_FILE);
     return;
   }
 
-  // The entry was added in the root directory on the server, so we should
-  // first add it to the root to mirror the state and then move it to the
-  // destination directory by MoveEntryFromRootDirectory().
-  metadata_->AddEntryOnUIThread(
-      entry,
-      base::Bind(&CopyOperation::MoveEntryFromRootDirectory,
-                 weak_ptr_factory_.GetWeakPtr(), dest_path, callback));
+  // TODO(hashimoto): Resolve local ID before use. crbug.com/260514
+  entry.set_parent_local_id(parent_resource_id);
+
+  // The document is copied into the root directory (temporarily) on the server,
+  // so we should first update the local metadata, then move it to the
+  // destination directory.
+  base::FilePath* file_path = new base::FilePath;
+  base::PostTaskAndReplyWithResult(
+      blocking_task_runner_.get(),
+      FROM_HERE,
+      base::Bind(&UpdateLocalStateForServerSideCopy,
+                 metadata_, entry, file_path),
+      base::Bind(&CopyOperation::CopyHostedDocumentAfterUpdateLocalState,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 dest_path, callback, base::Owned(file_path)));
 }
 
-void CopyOperation::MoveEntryFromRootDirectory(
+void CopyOperation::CopyHostedDocumentAfterUpdateLocalState(
     const base::FilePath& dest_path,
     const FileOperationCallback& callback,
-    FileError error,
-    const base::FilePath& file_path) {
+    base::FilePath* file_path,
+    FileError error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  // Depending on timing, the metadata may have inserted via change list
-  // already. So, FILE_ERROR_EXISTS is not an error.
-  if (error == FILE_ERROR_EXISTS)
-    error = FILE_ERROR_OK;
-
-  // Return if there is an error or |dir_path| is the root directory.
+  // If an error is found, or the destination directory is mydrive root
+  // (i.e. it is unnecessary to move the copied document), return the status.
   if (error != FILE_ERROR_OK ||
       dest_path.DirName() == util::GetDriveMyDriveRootPath()) {
     callback.Run(error);
@@ -335,199 +560,9 @@
   }
 
   DCHECK_EQ(util::GetDriveMyDriveRootPath().value(),
-            file_path.DirName().value()) << file_path.value();
+            file_path->DirName().value()) << file_path->value();
 
-  move_operation_->Move(file_path, dest_path, callback);
-}
-
-void CopyOperation::CopyAfterGetResourceEntryPair(
-    const base::FilePath& dest_file_path,
-    const FileOperationCallback& callback,
-    scoped_ptr<EntryInfoPairResult> result) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(!callback.is_null());
-  DCHECK(result.get());
-
-  if (result->first.error != FILE_ERROR_OK) {
-    callback.Run(result->first.error);
-    return;
-  } else if (result->second.error != FILE_ERROR_OK) {
-    callback.Run(result->second.error);
-    return;
-  }
-
-  scoped_ptr<ResourceEntry> src_file_proto = result->first.entry.Pass();
-  scoped_ptr<ResourceEntry> dest_parent_proto = result->second.entry.Pass();
-
-  if (!dest_parent_proto->file_info().is_directory()) {
-    callback.Run(FILE_ERROR_NOT_A_DIRECTORY);
-    return;
-  } else if (src_file_proto->file_info().is_directory()) {
-    // TODO(kochi): Implement copy for directories. In the interim,
-    // we handle recursive directory copy in the file manager.
-    // crbug.com/141596
-    callback.Run(FILE_ERROR_INVALID_OPERATION);
-    return;
-  }
-
-  // If Drive API v2 is enabled, we can copy resources on server side.
-  if (util::IsDriveV2ApiEnabled()) {
-    base::FilePath new_title = dest_file_path.BaseName();
-    if (src_file_proto->file_specific_info().is_hosted_document()) {
-      // Drop the document extension, which should not be in the title.
-      // TODO(yoshiki): Remove this code with crbug.com/223304.
-      new_title = new_title.RemoveExtension();
-    }
-
-    scheduler_->CopyResource(
-        src_file_proto->resource_id(),
-        dest_parent_proto->resource_id(),
-        new_title.value(),
-        base::Bind(&CopyOperation::OnCopyResourceCompleted,
-                   weak_ptr_factory_.GetWeakPtr(), callback));
-    return;
-  }
-
-
-  if (src_file_proto->file_specific_info().is_hosted_document()) {
-    CopyHostedDocumentToDirectory(
-        // Drop the document extension, which should not be in the title.
-        // TODO(yoshiki): Remove this code with crbug.com/223304.
-        dest_file_path.RemoveExtension(),
-        src_file_proto->resource_id(),
-        callback);
-    return;
-  }
-
-  const base::FilePath& src_file_path = result->first.path;
-  download_operation_->EnsureFileDownloadedByPath(
-      src_file_path,
-      ClientContext(USER_INITIATED),
-      GetFileContentInitializedCallback(),
-      google_apis::GetContentCallback(),
-      base::Bind(&CopyOperation::OnGetFileCompleteForCopy,
-                 weak_ptr_factory_.GetWeakPtr(),
-                 dest_file_path,
-                 callback));
-}
-
-void CopyOperation::OnCopyResourceCompleted(
-    const FileOperationCallback& callback,
-    google_apis::GDataErrorCode status,
-    scoped_ptr<google_apis::ResourceEntry> resource_entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(!callback.is_null());
-
-  FileError error = GDataToFileError(status);
-  if (error != FILE_ERROR_OK) {
-    callback.Run(error);
-    return;
-  }
-
-  DCHECK(resource_entry);
-  ResourceEntry entry;
-  if (!ConvertToResourceEntry(*resource_entry, &entry)) {
-    callback.Run(FILE_ERROR_NOT_A_FILE);
-    return;
-  }
-
-  // The copy on the server side is completed successfully. Update the local
-  // metadata.
-  base::PostTaskAndReplyWithResult(
-      blocking_task_runner_.get(),
-      FROM_HERE,
-      base::Bind(&AddEntryToLocalMetadata, metadata_, entry),
-      callback);
-}
-
-void CopyOperation::OnGetFileCompleteForCopy(
-    const base::FilePath& remote_dest_path,
-    const FileOperationCallback& callback,
-    FileError error,
-    const base::FilePath& local_file_path,
-    scoped_ptr<ResourceEntry> entry) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(!callback.is_null());
-
-  if (error != FILE_ERROR_OK) {
-    callback.Run(error);
-    return;
-  }
-
-  // This callback is only triggered for a regular file via Copy().
-  DCHECK(entry && !entry->file_specific_info().is_hosted_document());
-  ScheduleTransferRegularFile(local_file_path, remote_dest_path, callback);
-}
-
-void CopyOperation::TransferFileFromLocalToRemoteAfterPrepare(
-    const base::FilePath& local_src_file_path,
-    const base::FilePath& remote_dest_file_path,
-    const FileOperationCallback& callback,
-    ResourceEntry* parent_entry,
-    FileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(!callback.is_null());
-  DCHECK(parent_entry);
-
-  if (error != FILE_ERROR_OK) {
-    callback.Run(error);
-    return;
-  }
-
-  if (!parent_entry->file_info().is_directory()) {
-    // The parent of |remote_dest_file_path| is not a directory.
-    callback.Run(FILE_ERROR_NOT_A_DIRECTORY);
-    return;
-  }
-
-  if (util::HasGDocFileExtension(local_src_file_path)) {
-    // TODO(hidehiko): This should be a part of
-    // PrepareTransferFileFromLocalToRemote.
-    base::PostTaskAndReplyWithResult(
-        blocking_task_runner_.get(),
-        FROM_HERE,
-        base::Bind(&util::ReadResourceIdFromGDocFile, local_src_file_path),
-        base::Bind(&CopyOperation::TransferFileForResourceId,
-                   weak_ptr_factory_.GetWeakPtr(),
-                   local_src_file_path,
-                   remote_dest_file_path,
-                   callback));
-  } else {
-    ScheduleTransferRegularFile(
-        local_src_file_path, remote_dest_file_path, callback);
-  }
-}
-
-void CopyOperation::TransferFileForResourceId(
-    const base::FilePath& local_file_path,
-    const base::FilePath& remote_dest_file_path,
-    const FileOperationCallback& callback,
-    const std::string& resource_id) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(!callback.is_null());
-
-  if (resource_id.empty()) {
-    // If |resource_id| is empty, upload the local file as a regular file.
-    ScheduleTransferRegularFile(
-        local_file_path, remote_dest_file_path, callback);
-    return;
-  }
-
-  // GDoc file may contain a resource ID in the old format.
-  const std::string canonicalized_resource_id =
-      drive_service_->CanonicalizeResourceId(resource_id);
-
-  // TODO(hidehiko): Use CopyResource for Drive API v2.
-
-  // Otherwise, copy the document on the server side and add the new copy
-  // to the destination directory (collection).
-  CopyHostedDocumentToDirectory(
-      // Drop the document extension, which should not be
-      // in the document title.
-      // TODO(yoshiki): Remove this code with crbug.com/223304.
-      remote_dest_file_path.RemoveExtension(),
-      canonicalized_resource_id,
-      callback);
+  move_operation_->Move(*file_path, dest_path, callback);
 }
 
 }  // namespace file_system
diff --git a/chrome/browser/chromeos/drive/file_system/copy_operation.h b/chrome/browser/chromeos/drive/file_system/copy_operation.h
index 43513c4..467d146 100644
--- a/chrome/browser/chromeos/drive/file_system/copy_operation.h
+++ b/chrome/browser/chromeos/drive/file_system/copy_operation.h
@@ -72,6 +72,49 @@
       const FileOperationCallback& callback);
 
  private:
+  // Part of Copy(). Called after prepartion is done.
+  void CopyAfterPrepare(const base::FilePath& src_file_path,
+                        const base::FilePath& dest_file_path,
+                        const FileOperationCallback& callback,
+                        ResourceEntry* src_entry,
+                        std::string* parent_resource_id,
+                        FileError error);
+
+  // Part of Copy(). Called after the file content is downloaded.
+  void CopyAfterDownload(const base::FilePath& dest_file_path,
+                         const FileOperationCallback& callback,
+                         FileError error,
+                         const base::FilePath& local_file_path,
+                         scoped_ptr<ResourceEntry> entry);
+
+  // Part of TransferFileFromLocalToRemote(). Called after preparation is done.
+  // |resource_id| is available only if the file is JSON GDoc file.
+  void TransferFileFromLocalToRemoteAfterPrepare(
+      const base::FilePath& local_src_path,
+      const base::FilePath& remote_dest_path,
+      const FileOperationCallback& callback,
+      std::string* resource_id,
+      FileError error);
+
+  // Copies resource with |resource_id| into the directory |parent_resource_id|
+  // with renaming it to |new_title|.
+  void CopyResourceOnServer(const std::string& resource_id,
+                            const std::string& parent_resource_id,
+                            const std::string& new_title,
+                            const FileOperationCallback& callback);
+
+  // Part of CopyResourceOnServer. Called after server side copy is done.
+  void CopyResourceOnServerAfterServerSideCopy(
+      const FileOperationCallback& callback,
+      google_apis::GDataErrorCode status,
+      scoped_ptr<google_apis::ResourceEntry> resource_entry);
+
+  // Part of CopyResourceOnServer. Called after local state update is done.
+  void CopyResourceOnServerAfterUpdateLocalState(
+      const FileOperationCallback& callback,
+      base::FilePath* file_path,
+      FileError error);
+
   // Creates an empty file on the server at |remote_dest_path| to ensure
   // the location, stores a file at |local_file_path| in cache and marks it
   // dirty, so that SyncClient will upload the data later.
@@ -111,76 +154,30 @@
       std::string* resource_id,
       FileError error);
 
-  // Copies a hosted document with |resource_id| to |dest_path|
-  void CopyHostedDocumentToDirectory(const base::FilePath& dest_path,
-                                     const std::string& resource_id,
-                                     const FileOperationCallback& callback);
+  // Copies the hosted document with |resource_id| to the |dest_path|.
+  // This method is designed based on GData WAPI. It cannot create a copy in
+  // any directory but my drive root. So, this method creates the copy at
+  // my drive root first, then move it into the destination directory.
+  // For Drive API v2, this should work, but CopyResourceOnServer should work
+  // more efficiently.
+  void CopyHostedDocument(const std::string& resource_id,
+                          const base::FilePath& dest_path,
+                          const FileOperationCallback& callback);
 
-  // Callback for handling document copy attempt.
-  void OnCopyHostedDocumentCompleted(
+  // Part of CopyHostedDocument(). Called after server side copy is done.
+  void CopyHostedDocumentAfterServerSideCopy(
       const base::FilePath& dest_path,
       const FileOperationCallback& callback,
       google_apis::GDataErrorCode status,
       scoped_ptr<google_apis::ResourceEntry> resource_entry);
 
-  // Moves a file or directory at |file_path| in the root directory to
-  // |dest_path|. This function does nothing if |dest_path| points to a
-  // resource directly under the root directory.
-  void MoveEntryFromRootDirectory(const base::FilePath& dest_path,
-                                  const FileOperationCallback& callback,
-                                  FileError error,
-                                  const base::FilePath& file_path);
-
-  // Part of Copy(). Called after GetResourceEntryPairByPaths() is complete.
-  void CopyAfterGetResourceEntryPair(const base::FilePath& dest_file_path,
-                                     const FileOperationCallback& callback,
-                                     scoped_ptr<EntryInfoPairResult> result);
-
-  // Callback for handling resource copy on the server.
-  // |callback| must not be null.
-  void OnCopyResourceCompleted(
+  // Part of CopyHostedDocument(). Called after the local metadata is updated.
+  void CopyHostedDocumentAfterUpdateLocalState(
+      const base::FilePath& dest_path,
       const FileOperationCallback& callback,
-      google_apis::GDataErrorCode status,
-      scoped_ptr<google_apis::ResourceEntry> resource_entry);
-
-  // Invoked upon completion of GetFileByPath initiated by Copy. If
-  // GetFileByPath reports no error, calls TransferRegularFile to transfer
-  // |local_file_path| to |remote_dest_file_path|.
-  void OnGetFileCompleteForCopy(const base::FilePath& remote_dest_file_path,
-                                const FileOperationCallback& callback,
-                                FileError error,
-                                const base::FilePath& local_file_path,
-                                scoped_ptr<ResourceEntry> entry);
-
-  // Part of TransferFileFromLocalToRemote(). Called after |parent_entry| is
-  // retrieved in the blocking pool.
-  void TransferFileFromLocalToRemoteAfterPrepare(
-      const base::FilePath& local_src_file_path,
-      const base::FilePath& remote_dest_file_path,
-      const FileOperationCallback& callback,
-      ResourceEntry* parent_entry,
+      base::FilePath* file_path,
       FileError error);
 
-  // Part of TransferFileFromLocalToRemote(). Called after the result of
-  // GetAboutResource() is avaiable.
-  void TransferFileFromLocalToRemoteAfterGetQuota(
-      const base::FilePath& local_src_file_path,
-      const base::FilePath& remote_dest_file_path,
-      const FileOperationCallback& callback,
-      google_apis::GDataErrorCode status,
-      scoped_ptr<google_apis::AboutResource> about_resource);
-
-  // Initiates transfer of |local_file_path| with |resource_id| to
-  // |remote_dest_file_path|. |local_file_path| must be a file from the local
-  // file system, |remote_dest_file_path| is the virtual destination path within
-  // Drive file system. If |resource_id| is a non-empty string, the transfer is
-  // handled by CopyDocumentToDirectory. Otherwise, the transfer is handled by
-  // TransferRegularFile.
-  void TransferFileForResourceId(const base::FilePath& local_file_path,
-                                 const base::FilePath& remote_dest_file_path,
-                                 const FileOperationCallback& callback,
-                                 const std::string& resource_id);
-
   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
   OperationObserver* observer_;
   JobScheduler* scheduler_;
diff --git a/chrome/browser/chromeos/drive/file_system/copy_operation_unittest.cc b/chrome/browser/chromeos/drive/file_system/copy_operation_unittest.cc
index cefdeba..d597dae 100644
--- a/chrome/browser/chromeos/drive/file_system/copy_operation_unittest.cc
+++ b/chrome/browser/chromeos/drive/file_system/copy_operation_unittest.cc
@@ -55,7 +55,7 @@
   // TransferFileFromLocalToRemote stores a copy of the local file in the cache,
   // marks it dirty and requests the observer to upload the file.
   EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(remote_dest_path, &entry));
-  EXPECT_EQ(1U, observer()->upload_needed_resource_ids().count(
+  EXPECT_EQ(1U, observer()->upload_needed_local_ids().count(
       entry.resource_id()));
   FileCacheEntry cache_entry;
   bool found = false;
diff --git a/chrome/browser/chromeos/drive/file_system/create_directory_operation.cc b/chrome/browser/chromeos/drive/file_system/create_directory_operation.cc
index b9ced16..af7805e 100644
--- a/chrome/browser/chromeos/drive/file_system/create_directory_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/create_directory_operation.cc
@@ -25,19 +25,32 @@
 // the path to the result file.
 FileError UpdateLocalStateForCreateDirectoryRecursively(
     internal::ResourceMetadata* metadata,
-    const ResourceEntry& entry,
+    scoped_ptr<google_apis::ResourceEntry> resource_entry,
     base::FilePath* file_path) {
   DCHECK(metadata);
   DCHECK(file_path);
 
-  FileError result = metadata->AddEntry(entry);
+  ResourceEntry entry;
+  std::string parent_resource_id;
+  if (!ConvertToResourceEntry(*resource_entry, &entry, &parent_resource_id))
+    return FILE_ERROR_NOT_A_FILE;
+
+  std::string parent_local_id;
+  FileError result = metadata->GetIdByResourceId(parent_resource_id,
+                                                 &parent_local_id);
+  if (result != FILE_ERROR_OK)
+    return result;
+  entry.set_parent_local_id(parent_local_id);
+
+  std::string local_id;
+  result = metadata->AddEntry(entry, &local_id);
   // Depending on timing, a metadata may be updated by change list already.
   // So, FILE_ERROR_EXISTS is not an error.
   if (result == FILE_ERROR_EXISTS)
-    result = FILE_ERROR_OK;
+    result = metadata->GetIdByResourceId(entry.resource_id(), &local_id);
 
   if (result == FILE_ERROR_OK)
-    *file_path = metadata->GetFilePath(entry.resource_id());
+    *file_path = metadata->GetFilePath(local_id);
 
   return result;
 }
@@ -101,22 +114,21 @@
   if (components.empty() || components[0] != util::kDriveGrandRootDirName)
     return base::FilePath();
 
-  std::string resource_id = util::kDriveGrandRootSpecialResourceId;
+  std::string local_id = util::kDriveGrandRootSpecialResourceId;
   for (size_t i = 1; i < components.size(); ++i) {
-    std::string child_resource_id =
-        metadata->GetChildResourceId(resource_id, components[i]);
-    if (child_resource_id.empty())
+    std::string child_local_id = metadata->GetChildId(local_id, components[i]);
+    if (child_local_id.empty())
       break;
-    resource_id = child_resource_id;
+    local_id = child_local_id;
   }
 
-  FileError error = metadata->GetResourceEntryById(resource_id, entry);
+  FileError error = metadata->GetResourceEntryById(local_id, entry);
   DCHECK_EQ(FILE_ERROR_OK, error);
 
   if (!entry->file_info().is_directory())
     return base::FilePath();
 
-  return metadata->GetFilePath(resource_id);
+  return metadata->GetFilePath(local_id);
 }
 
 void CreateDirectoryOperation::CreateDirectoryAfterGetExistingDeepestDirectory(
@@ -193,12 +205,7 @@
     return;
   }
   DCHECK(resource_entry);
-
-  ResourceEntry entry;
-  if (!ConvertToResourceEntry(*resource_entry, &entry)) {
-    callback.Run(FILE_ERROR_NOT_A_FILE);
-    return;
-  }
+  const std::string& resource_id = resource_entry->resource_id();
 
   // Note that the created directory may be renamed inside
   // ResourceMetadata::AddEntry due to name confliction.
@@ -210,12 +217,12 @@
       FROM_HERE,
       base::Bind(&UpdateLocalStateForCreateDirectoryRecursively,
                  metadata_,
-                 entry,
+                 base::Passed(&resource_entry),
                  file_path),
       base::Bind(&CreateDirectoryOperation::
                      CreateDirectoryRecursivelyAfterUpdateLocalState,
                  weak_ptr_factory_.GetWeakPtr(),
-                 resource_entry->resource_id(),
+                 resource_id,
                  remaining_path,
                  callback,
                  base::Owned(file_path)));
diff --git a/chrome/browser/chromeos/drive/file_system/create_file_operation.cc b/chrome/browser/chromeos/drive/file_system/create_file_operation.cc
index 2188e34..67cc830 100644
--- a/chrome/browser/chromeos/drive/file_system/create_file_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/create_file_operation.cc
@@ -85,20 +85,30 @@
   DCHECK(file_path);
 
   // Add the entry to the local resource metadata.
-  FileError error = FILE_ERROR_NOT_A_FILE;
   ResourceEntry entry;
-  if (ConvertToResourceEntry(*resource_entry, &entry))
-    error = metadata->AddEntry(entry);
+  std::string parent_resource_id;
+  if (!ConvertToResourceEntry(*resource_entry, &entry, &parent_resource_id))
+    return FILE_ERROR_NOT_A_FILE;
+
+  std::string parent_local_id;
+  FileError error = metadata->GetIdByResourceId(parent_resource_id,
+                                                &parent_local_id);
+  if (error != FILE_ERROR_OK)
+    return error;
+  entry.set_parent_local_id(parent_local_id);
+
+  std::string local_id;
+  error = metadata->AddEntry(entry, &local_id);
 
   // Depending on timing, the metadata may have inserted via change list
   // already. So, FILE_ERROR_EXISTS is not an error.
   if (error == FILE_ERROR_EXISTS)
-    error = FILE_ERROR_OK;
+    error = metadata->GetIdByResourceId(entry.resource_id(), &local_id);
 
   if (error == FILE_ERROR_OK) {
     // At this point, upload to the server is fully succeeded.
     // Populate the |file_path| which will be used to notify the observer.
-    *file_path = metadata->GetFilePath(entry.resource_id());
+    *file_path = metadata->GetFilePath(local_id);
 
     // Also store an empty file to the cache.
     // Here, failure is not a fatal error, so ignore the returned code.
@@ -106,7 +116,7 @@
     base::FilePath empty_file;
     if (file_util::CreateTemporaryFile(&empty_file)) {
       cache_store_error =  cache->Store(
-          entry.resource_id(),
+          local_id,
           entry.file_specific_info().md5(),
           empty_file,
           internal::FileCache::FILE_OPERATION_MOVE);
@@ -114,7 +124,7 @@
     DLOG_IF(WARNING, cache_store_error != FILE_ERROR_OK)
         << "Failed to store a cache file: "
         << FileErrorToString(cache_store_error)
-        << ", resource_id: " << entry.resource_id();
+        << ", local_id: " << local_id;
   }
 
   return error;
diff --git a/chrome/browser/chromeos/drive/file_system/download_operation.cc b/chrome/browser/chromeos/drive/file_system/download_operation.cc
index eaa2cb5..cdc79dc 100644
--- a/chrome/browser/chromeos/drive/file_system/download_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/download_operation.cc
@@ -38,12 +38,17 @@
     internal::ResourceMetadata* metadata,
     internal::FileCache* cache,
     const base::FilePath& temporary_file_directory,
+    const std::string& local_id,
     ResourceEntry* entry,
     base::FilePath* cache_file_path) {
   DCHECK(metadata);
   DCHECK(cache);
   DCHECK(cache_file_path);
 
+  FileError error = metadata->GetResourceEntryById(local_id, entry);
+  if (error != FILE_ERROR_OK)
+    return error;
+
   if (entry->file_info().is_directory())
     return FILE_ERROR_NOT_A_FILE;
 
@@ -72,7 +77,7 @@
 
   // Leave |cache_file_path| empty when no cache entry is found.
   FileCacheEntry cache_entry;
-  if (!cache->GetCacheEntry(entry->resource_id(), &cache_entry))
+  if (!cache->GetCacheEntry(local_id, &cache_entry))
     return FILE_ERROR_OK;
 
   // Leave |cache_file_path| empty when the stored file is obsolete and has no
@@ -82,7 +87,7 @@
     return FILE_ERROR_OK;
 
   // Fill |cache_file_path| with the path to the cached file.
-  FileError error = cache->GetFile(entry->resource_id(), cache_file_path);
+  error = cache->GetFile(local_id, cache_file_path);
   if (error != FILE_ERROR_OK)
     return error;
 
@@ -102,20 +107,18 @@
 
 // Calls CheckPreConditionForEnsureFileDownloaded() with the entry specified by
 // the given ID. Also fills |drive_file_path| with the path of the entry.
-FileError CheckPreConditionForEnsureFileDownloadedByResourceId(
+FileError CheckPreConditionForEnsureFileDownloadedByLocalId(
     internal::ResourceMetadata* metadata,
     internal::FileCache* cache,
-    const std::string& resource_id,
+    const std::string& local_id,
     const base::FilePath& temporary_file_directory,
     base::FilePath* drive_file_path,
     base::FilePath* cache_file_path,
     ResourceEntry* entry) {
-  FileError error = metadata->GetResourceEntryById(resource_id, entry);
-  *drive_file_path = metadata->GetFilePath(resource_id);
-  if (error != FILE_ERROR_OK)
-    return error;
+  *drive_file_path = metadata->GetFilePath(local_id);
   return CheckPreConditionForEnsureFileDownloaded(
-      metadata, cache, temporary_file_directory, entry, cache_file_path);
+      metadata, cache, temporary_file_directory, local_id, entry,
+      cache_file_path);
 }
 
 // Calls CheckPreConditionForEnsureFileDownloaded() with the entry specified by
@@ -125,13 +128,15 @@
     internal::FileCache* cache,
     const base::FilePath& file_path,
     const base::FilePath& temporary_file_directory,
+    std::string* local_id,
     base::FilePath* cache_file_path,
     ResourceEntry* entry) {
-  FileError error = metadata->GetResourceEntryByPath(file_path, entry);
+  FileError error = metadata->GetIdByPath(file_path, local_id);
   if (error != FILE_ERROR_OK)
     return error;
   return CheckPreConditionForEnsureFileDownloaded(
-      metadata, cache, temporary_file_directory, entry, cache_file_path);
+      metadata, cache, temporary_file_directory, *local_id, entry,
+      cache_file_path);
 }
 
 // Creates a file with unique name in |dir| and stores the path to |temp_file|.
@@ -151,8 +156,8 @@
       file_util::FILE_PERMISSION_READ_BY_OTHERS);
 }
 
-// Prepares for downloading the file. Given the |resource_id|, allocates the
-// enough space for the file in the cache.
+// Prepares for downloading the file. Allocates the enough space for the file
+// in the cache.
 // If succeeded, returns FILE_ERROR_OK with |temp_download_file| storing the
 // path to the file in the cache.
 FileError PrepareForDownloadFile(internal::FileCache* cache,
@@ -178,7 +183,7 @@
 // If failed, returns an error code with deleting |downloaded_file_path|.
 FileError UpdateLocalStateForDownloadFile(
     internal::FileCache* cache,
-    const std::string& resource_id,
+    const std::string& local_id,
     const std::string& md5,
     google_apis::GDataErrorCode gdata_error,
     const base::FilePath& downloaded_file_path,
@@ -192,48 +197,51 @@
   }
 
   // Here the download is completed successfully, so store it into the cache.
-  error = cache->Store(resource_id, md5, downloaded_file_path,
+  error = cache->Store(local_id, md5, downloaded_file_path,
                        internal::FileCache::FILE_OPERATION_MOVE);
   if (error != FILE_ERROR_OK) {
     base::DeleteFile(downloaded_file_path, false /* recursive */);
     return error;
   }
 
-  return cache->GetFile(resource_id, cache_file_path);
+  return cache->GetFile(local_id, cache_file_path);
 }
 
 }  // namespace
 
-class DownloadOperation::DownloadCallback {
+class DownloadOperation::DownloadParams {
  public:
-  DownloadCallback(
+  DownloadParams(
       const GetFileContentInitializedCallback initialized_callback,
       const google_apis::GetContentCallback get_content_callback,
-      const GetFileCallback completion_callback)
+      const GetFileCallback completion_callback,
+      scoped_ptr<ResourceEntry> entry)
       : initialized_callback_(initialized_callback),
         get_content_callback_(get_content_callback),
-        completion_callback_(completion_callback) {
+        completion_callback_(completion_callback),
+        entry_(entry.Pass()) {
     DCHECK(!completion_callback_.is_null());
+    DCHECK(entry_);
   }
 
-  void OnCacheFileFound(const ResourceEntry& entry,
-                        const base::FilePath& cache_file_path) const {
+  void OnCacheFileFound(const base::FilePath& cache_file_path) const {
     if (initialized_callback_.is_null())
       return;
 
+    DCHECK(entry_);
     initialized_callback_.Run(
-        FILE_ERROR_OK, make_scoped_ptr(new ResourceEntry(entry)),
+        FILE_ERROR_OK, make_scoped_ptr(new ResourceEntry(*entry_)),
         cache_file_path, base::Closure());
   }
 
-  void OnStartDownloading(const ResourceEntry& entry,
-                          const base::Closure& cancel_download_closure) const {
+  void OnStartDownloading(const base::Closure& cancel_download_closure) const {
     if (initialized_callback_.is_null()) {
       return;
     }
 
+    DCHECK(entry_);
     initialized_callback_.Run(
-        FILE_ERROR_OK, make_scoped_ptr(new ResourceEntry(entry)),
+        FILE_ERROR_OK, make_scoped_ptr(new ResourceEntry(*entry_)),
         base::FilePath(), cancel_download_closure);
   }
 
@@ -242,21 +250,24 @@
         error, base::FilePath(), scoped_ptr<ResourceEntry>());
   }
 
-  void OnComplete(const base::FilePath& cache_file_path,
-                  scoped_ptr<ResourceEntry> entry) const {
-    completion_callback_.Run(FILE_ERROR_OK, cache_file_path, entry.Pass());
+  void OnComplete(const base::FilePath& cache_file_path) {
+    completion_callback_.Run(FILE_ERROR_OK, cache_file_path, entry_.Pass());
   }
 
   const google_apis::GetContentCallback& get_content_callback() const {
     return get_content_callback_;
   }
 
+  const ResourceEntry& entry() const { return *entry_; }
+
  private:
   const GetFileContentInitializedCallback initialized_callback_;
   const google_apis::GetContentCallback get_content_callback_;
   const GetFileCallback completion_callback_;
 
-  // This class is copiable.
+  scoped_ptr<ResourceEntry> entry_;
+
+  DISALLOW_COPY_AND_ASSIGN(DownloadParams);
 };
 
 DownloadOperation::DownloadOperation(
@@ -278,8 +289,8 @@
 DownloadOperation::~DownloadOperation() {
 }
 
-void DownloadOperation::EnsureFileDownloadedByResourceId(
-    const std::string& resource_id,
+void DownloadOperation::EnsureFileDownloadedByLocalId(
+    const std::string& local_id,
     const ClientContext& context,
     const GetFileContentInitializedCallback& initialized_callback,
     const google_apis::GetContentCallback& get_content_callback,
@@ -287,28 +298,28 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!completion_callback.is_null());
 
-  DownloadCallback callback(
-      initialized_callback, get_content_callback, completion_callback);
-
   base::FilePath* drive_file_path = new base::FilePath;
   base::FilePath* cache_file_path = new base::FilePath;
   ResourceEntry* entry = new ResourceEntry;
+  scoped_ptr<DownloadParams> params(new DownloadParams(
+      initialized_callback, get_content_callback, completion_callback,
+      make_scoped_ptr(entry)));
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(),
       FROM_HERE,
-      base::Bind(&CheckPreConditionForEnsureFileDownloadedByResourceId,
+      base::Bind(&CheckPreConditionForEnsureFileDownloadedByLocalId,
                  base::Unretained(metadata_),
                  base::Unretained(cache_),
-                 resource_id,
+                 local_id,
                  temporary_file_directory_,
                  drive_file_path,
                  cache_file_path,
                  entry),
       base::Bind(&DownloadOperation::EnsureFileDownloadedAfterCheckPreCondition,
                  weak_ptr_factory_.GetWeakPtr(),
-                 callback,
+                 base::Passed(&params),
                  context,
-                 base::Passed(make_scoped_ptr(entry)),
+                 base::Owned(new std::string(local_id)),
                  base::Owned(drive_file_path),
                  base::Owned(cache_file_path)));
 }
@@ -322,12 +333,13 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!completion_callback.is_null());
 
-  DownloadCallback callback(
-      initialized_callback, get_content_callback, completion_callback);
-
+  std::string* local_id = new std::string;
   base::FilePath* drive_file_path = new base::FilePath(file_path);
   base::FilePath* cache_file_path = new base::FilePath;
   ResourceEntry* entry = new ResourceEntry;
+  scoped_ptr<DownloadParams> params(new DownloadParams(
+      initialized_callback, get_content_callback, completion_callback,
+      make_scoped_ptr(entry)));
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(),
       FROM_HERE,
@@ -336,39 +348,41 @@
                  base::Unretained(cache_),
                  file_path,
                  temporary_file_directory_,
+                 local_id,
                  cache_file_path,
                  entry),
       base::Bind(&DownloadOperation::EnsureFileDownloadedAfterCheckPreCondition,
                  weak_ptr_factory_.GetWeakPtr(),
-                 callback,
+                 base::Passed(&params),
                  context,
-                 base::Passed(make_scoped_ptr(entry)),
+                 base::Owned(local_id),
                  base::Owned(drive_file_path),
                  base::Owned(cache_file_path)));
 }
 
 void DownloadOperation::EnsureFileDownloadedAfterCheckPreCondition(
-    const DownloadCallback& callback,
+    scoped_ptr<DownloadParams> params,
     const ClientContext& context,
-    scoped_ptr<ResourceEntry> entry,
+    std::string* local_id,
     base::FilePath* drive_file_path,
     base::FilePath* cache_file_path,
     FileError error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(entry);
+  DCHECK(params);
+  DCHECK(local_id);
   DCHECK(drive_file_path);
   DCHECK(cache_file_path);
 
   if (error != FILE_ERROR_OK) {
     // During precondition check, an error is found.
-    callback.OnError(error);
+    params->OnError(error);
     return;
   }
 
   if (!cache_file_path->empty()) {
     // The cache file is found.
-    callback.OnCacheFileFound(*entry, *cache_file_path);
-    callback.OnComplete(*cache_file_path, entry.Pass());
+    params->OnCacheFileFound(*cache_file_path);
+    params->OnComplete(*cache_file_path);
     return;
   }
 
@@ -377,7 +391,7 @@
   // - if we don't have enough space, try to free up the disk space
   // - if we still don't have enough space, return "no space" error
   // - if we have enough space, start downloading the file from the server
-  int64 size = entry->file_info().size();
+  int64 size = params->entry().file_info().size();
   base::FilePath* temp_download_file_path = new base::FilePath;
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(),
@@ -390,93 +404,91 @@
       base::Bind(
           &DownloadOperation::EnsureFileDownloadedAfterPrepareForDownloadFile,
           weak_ptr_factory_.GetWeakPtr(),
-          callback,
+          base::Passed(&params),
           context,
-          base::Passed(&entry),
+          *local_id,
           *drive_file_path,
           base::Owned(temp_download_file_path)));
 }
 
 void DownloadOperation::EnsureFileDownloadedAfterPrepareForDownloadFile(
-    const DownloadCallback& callback,
+    scoped_ptr<DownloadParams> params,
     const ClientContext& context,
-    scoped_ptr<ResourceEntry> entry,
+    const std::string& local_id,
     const base::FilePath& drive_file_path,
     base::FilePath* temp_download_file_path,
     FileError error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(entry);
+  DCHECK(params);
   DCHECK(temp_download_file_path);
 
   if (error != FILE_ERROR_OK) {
-    callback.OnError(error);
+    params->OnError(error);
     return;
   }
 
-  ResourceEntry* entry_ptr = entry.get();
+  DownloadParams* params_ptr = params.get();
   JobID id = scheduler_->DownloadFile(
       drive_file_path,
+      params_ptr->entry().file_info().size(),
       *temp_download_file_path,
-      entry_ptr->resource_id(),
+      params_ptr->entry().resource_id(),
       context,
       base::Bind(&DownloadOperation::EnsureFileDownloadedAfterDownloadFile,
                  weak_ptr_factory_.GetWeakPtr(),
                  drive_file_path,
-                 base::Passed(&entry),
-                 callback),
-      callback.get_content_callback());
+                 local_id,
+                 base::Passed(&params)),
+      params_ptr->get_content_callback());
 
   // Notify via |initialized_callback| if necessary.
-  callback.OnStartDownloading(
-      *entry_ptr,
+  params_ptr->OnStartDownloading(
       base::Bind(&DownloadOperation::CancelJob,
                  weak_ptr_factory_.GetWeakPtr(), id));
 }
 
 void DownloadOperation::EnsureFileDownloadedAfterDownloadFile(
     const base::FilePath& drive_file_path,
-    scoped_ptr<ResourceEntry> entry,
-    const DownloadCallback& callback,
+    const std::string& local_id,
+    scoped_ptr<DownloadParams> params,
     google_apis::GDataErrorCode gdata_error,
     const base::FilePath& downloaded_file_path) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-  ResourceEntry* entry_ptr = entry.get();
+  const std::string& md5 = params->entry().file_specific_info().md5();
   base::FilePath* cache_file_path = new base::FilePath;
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(),
       FROM_HERE,
       base::Bind(&UpdateLocalStateForDownloadFile,
                  base::Unretained(cache_),
-                 entry_ptr->resource_id(),
-                 entry_ptr->file_specific_info().md5(),
+                 local_id,
+                 md5,
                  gdata_error,
                  downloaded_file_path,
                  cache_file_path),
       base::Bind(&DownloadOperation::EnsureFileDownloadedAfterUpdateLocalState,
                  weak_ptr_factory_.GetWeakPtr(),
                  drive_file_path,
-                 callback,
-                 base::Passed(&entry),
+                 base::Passed(&params),
                  base::Owned(cache_file_path)));
 }
 
 void DownloadOperation::EnsureFileDownloadedAfterUpdateLocalState(
     const base::FilePath& file_path,
-    const DownloadCallback& callback,
-    scoped_ptr<ResourceEntry> entry,
+    scoped_ptr<DownloadParams> params,
     base::FilePath* cache_file_path,
     FileError error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   if (error != FILE_ERROR_OK) {
-    callback.OnError(error);
+    params->OnError(error);
     return;
   }
 
   // Storing to cache changes the "offline available" status, hence notify.
   observer_->OnDirectoryChangedByOperation(file_path.DirName());
-  callback.OnComplete(*cache_file_path, entry.Pass());
+  params->OnComplete(*cache_file_path);
 }
 
 void DownloadOperation::CancelJob(JobID job_id) {
diff --git a/chrome/browser/chromeos/drive/file_system/download_operation.h b/chrome/browser/chromeos/drive/file_system/download_operation.h
index c95b809..ac1e13e 100644
--- a/chrome/browser/chromeos/drive/file_system/download_operation.h
+++ b/chrome/browser/chromeos/drive/file_system/download_operation.h
@@ -45,7 +45,7 @@
                     const base::FilePath& temporary_file_directory);
   ~DownloadOperation();
 
-  // Ensures that the file content specified by |resource_id| is locally
+  // Ensures that the file content specified by |local_id| is locally
   // downloaded.
   // For hosted documents, this method may create a JSON file representing the
   // file.
@@ -61,14 +61,14 @@
   // |initialized_callback| and |get_content_callback| can be null if not
   // needed.
   // |completion_callback| must not be null.
-  void EnsureFileDownloadedByResourceId(
-      const std::string& resource_id,
+  void EnsureFileDownloadedByLocalId(
+      const std::string& local_id,
       const ClientContext& context,
       const GetFileContentInitializedCallback& initialized_callback,
       const google_apis::GetContentCallback& get_content_callback,
       const GetFileCallback& completion_callback);
 
-  // Does the same thing as EnsureFileDownloadedByResourceId for the file
+  // Does the same thing as EnsureFileDownloadedByLocalId for the file
   // specified by |file_path|.
   void EnsureFileDownloadedByPath(
       const base::FilePath& file_path,
@@ -78,15 +78,15 @@
       const GetFileCallback& completion_callback);
 
  private:
-  // Thin wrapper of Callbacks for EnsureFileDownloaded.
-  class DownloadCallback;
+  // Parameters for EnsureFileDownloaded.
+  class DownloadParams;
 
   // Part of EnsureFileDownloaded(). Called upon the completion of precondition
   // check.
   void EnsureFileDownloadedAfterCheckPreCondition(
-      const DownloadCallback& callback,
+      scoped_ptr<DownloadParams> params,
       const ClientContext& context,
-      scoped_ptr<ResourceEntry> entry,
+      std::string* local_id,
       base::FilePath* drive_file_path,
       base::FilePath* cache_file_path,
       FileError error);
@@ -94,9 +94,9 @@
   // Part of EnsureFileDownloaded(). Called when it is ready to start
   // downloading the file.
   void EnsureFileDownloadedAfterPrepareForDownloadFile(
-      const DownloadCallback& callback,
+      scoped_ptr<DownloadParams> params,
       const ClientContext& context,
-      scoped_ptr<ResourceEntry> entry,
+      const std::string& local_id,
       const base::FilePath& drive_file_path,
       base::FilePath* temp_download_file_path,
       FileError error);
@@ -104,8 +104,8 @@
   // Part of EnsureFileDownloaded(). Called after the actual downloading.
   void EnsureFileDownloadedAfterDownloadFile(
       const base::FilePath& drive_file_path,
-      scoped_ptr<ResourceEntry> entry,
-      const DownloadCallback& callback,
+      const std::string& local_id,
+      scoped_ptr<DownloadParams> params,
       google_apis::GDataErrorCode gdata_error,
       const base::FilePath& downloaded_file_path);
 
@@ -113,8 +113,7 @@
   // completed.
   void EnsureFileDownloadedAfterUpdateLocalState(
       const base::FilePath& file_path,
-      const DownloadCallback& callback,
-      scoped_ptr<ResourceEntry> entry,
+      scoped_ptr<DownloadParams> params,
       base::FilePath* cache_file_path,
       FileError error);
 
diff --git a/chrome/browser/chromeos/drive/file_system/download_operation_unittest.cc b/chrome/browser/chromeos/drive/file_system/download_operation_unittest.cc
index 2b55291..282336a 100644
--- a/chrome/browser/chromeos/drive/file_system/download_operation_unittest.cc
+++ b/chrome/browser/chromeos/drive/file_system/download_operation_unittest.cc
@@ -18,7 +18,7 @@
 
 class DownloadOperationTest : public OperationTestBase {
  protected:
-  virtual void SetUp() {
+  virtual void SetUp() OVERRIDE {
     OperationTestBase::SetUp();
 
     operation_.reset(new DownloadOperation(
@@ -118,7 +118,7 @@
 
   FileError error = FILE_ERROR_FAILED;
   cache()->StoreOnUIThread(
-      "<resource_id>", "<md5>", tmp_file,
+      "<id>", "<md5>", tmp_file,
       internal::FileCache::FILE_OPERATION_COPY,
       google_apis::test_util::CreateCopyResultCallback(&error));
   test_util::RunBlockingPoolTask();
@@ -148,7 +148,7 @@
   FileCacheEntry cache_entry;
   bool result = true;
   cache()->GetCacheEntryOnUIThread(
-      "<resource_id>",
+      "<id>",
       google_apis::test_util::CreateCopyResultCallback(&result,
                                                        &cache_entry));
   test_util::RunBlockingPoolTask();
@@ -197,7 +197,7 @@
   // Store something as cached version of this file.
   FileError error = FILE_ERROR_OK;
   cache()->StoreOnUIThread(
-      src_entry.resource_id(),
+      GetLocalId(file_in_root),
       src_entry.file_specific_info().md5(),
       temp_file,
       internal::FileCache::FILE_OPERATION_COPY,
@@ -247,17 +247,16 @@
   EXPECT_EQ(entry->resource_id(), util::ReadResourceIdFromGDocFile(file_path));
 }
 
-TEST_F(DownloadOperationTest, EnsureFileDownloadedByResourceId) {
+TEST_F(DownloadOperationTest, EnsureFileDownloadedByLocalId) {
   base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
   ResourceEntry src_entry;
   ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry));
-  std::string resource_id = src_entry.resource_id();
 
   FileError error = FILE_ERROR_OK;
   base::FilePath file_path;
   scoped_ptr<ResourceEntry> entry;
-  operation_->EnsureFileDownloadedByResourceId(
-      resource_id,
+  operation_->EnsureFileDownloadedByLocalId(
+      GetLocalId(file_in_root),
       ClientContext(USER_INITIATED),
       GetFileContentInitializedCallback(),
       google_apis::GetContentCallback(),
@@ -350,7 +349,7 @@
   }
 }
 
-TEST_F(DownloadOperationTest, EnsureFileDownloadedByResourceId_FromCache) {
+TEST_F(DownloadOperationTest, EnsureFileDownloadedByLocalId_FromCache) {
   base::FilePath temp_file;
   ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir(), &temp_file));
 
@@ -361,7 +360,7 @@
   // Store something as cached version of this file.
   FileError error = FILE_ERROR_FAILED;
   cache()->StoreOnUIThread(
-      src_entry.resource_id(),
+      GetLocalId(file_in_root),
       src_entry.file_specific_info().md5(),
       temp_file,
       internal::FileCache::FILE_OPERATION_COPY,
@@ -373,11 +372,10 @@
   // Hence the downloading should work even if the drive service is offline.
   fake_service()->set_offline(true);
 
-  std::string resource_id = src_entry.resource_id();
   base::FilePath file_path;
   scoped_ptr<ResourceEntry> entry;
-  operation_->EnsureFileDownloadedByResourceId(
-      resource_id,
+  operation_->EnsureFileDownloadedByLocalId(
+      GetLocalId(file_in_root),
       ClientContext(USER_INITIATED),
       GetFileContentInitializedCallback(),
       google_apis::GetContentCallback(),
@@ -405,7 +403,7 @@
   // Store the file as a cache, marking it to be dirty.
   FileError error = FILE_ERROR_FAILED;
   cache()->StoreOnUIThread(
-      src_entry.resource_id(),
+      GetLocalId(file_in_root),
       src_entry.file_specific_info().md5(),
       dirty_file,
       internal::FileCache::FILE_OPERATION_COPY,
@@ -413,7 +411,7 @@
   test_util::RunBlockingPoolTask();
   EXPECT_EQ(FILE_ERROR_OK, error);
   cache()->MarkDirtyOnUIThread(
-      src_entry.resource_id(),
+      GetLocalId(file_in_root),
       google_apis::test_util::CreateCopyResultCallback(&error));
   test_util::RunBlockingPoolTask();
   EXPECT_EQ(FILE_ERROR_OK, error);
diff --git a/chrome/browser/chromeos/drive/file_system/move_operation.cc b/chrome/browser/chromeos/drive/file_system/move_operation.cc
index 0f9a2d9..a74418e 100644
--- a/chrome/browser/chromeos/drive/file_system/move_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/move_operation.cc
@@ -166,11 +166,15 @@
   }
 
   ResourceEntry entry;
-  if (!ConvertToResourceEntry(*resource_entry, &entry)) {
+  std::string parent_resource_id;
+  if (!ConvertToResourceEntry(*resource_entry, &entry, &parent_resource_id)) {
     callback.Run(FILE_ERROR_FAILED);
     return;
   }
 
+  // TODO(hashimoto): Resolve local ID before use. crbug.com/260514
+  entry.set_parent_local_id(parent_resource_id);
+
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(),
       FROM_HERE,
diff --git a/chrome/browser/chromeos/drive/file_system/open_file_operation_unittest.cc b/chrome/browser/chromeos/drive/file_system/open_file_operation_unittest.cc
index fceaf38..8b8ef04 100644
--- a/chrome/browser/chromeos/drive/file_system/open_file_operation_unittest.cc
+++ b/chrome/browser/chromeos/drive/file_system/open_file_operation_unittest.cc
@@ -20,7 +20,7 @@
 
 class OpenFileOperationTest : public OperationTestBase {
  protected:
-  virtual void SetUp() {
+  virtual void SetUp() OVERRIDE {
     OperationTestBase::SetUp();
 
     operation_.reset(new OpenFileOperation(
@@ -59,7 +59,7 @@
   close_callback.Run();
   EXPECT_EQ(
       1U,
-      observer()->upload_needed_resource_ids().count(src_entry.resource_id()));
+      observer()->upload_needed_local_ids().count(src_entry.resource_id()));
 }
 
 TEST_F(OpenFileOperationTest, OpenNonExistingFile) {
@@ -126,7 +126,7 @@
   close_callback.Run();
   // Here we don't know about the resource id, so just make sure
   // OnCacheFileUploadNeededByOperation is called actually.
-  EXPECT_EQ(1U, observer()->upload_needed_resource_ids().size());
+  EXPECT_EQ(1U, observer()->upload_needed_local_ids().size());
 }
 
 TEST_F(OpenFileOperationTest, OpenOrCreateExistingFile) {
@@ -157,7 +157,7 @@
   close_callback.Run();
   EXPECT_EQ(
       1U,
-      observer()->upload_needed_resource_ids().count(src_entry.resource_id()));
+      observer()->upload_needed_local_ids().count(src_entry.resource_id()));
 }
 
 TEST_F(OpenFileOperationTest, OpenOrCreateNonExistingFile) {
@@ -185,7 +185,7 @@
   close_callback.Run();
   // Here we don't know about the resource id, so just make sure
   // OnCacheFileUploadNeededByOperation is called actually.
-  EXPECT_EQ(1U, observer()->upload_needed_resource_ids().size());
+  EXPECT_EQ(1U, observer()->upload_needed_local_ids().size());
 }
 
 TEST_F(OpenFileOperationTest, OpenFileTwice) {
@@ -235,14 +235,14 @@
 
   // There still remains a client opening the file, so it shouldn't be
   // uploaded yet.
-  EXPECT_TRUE(observer()->upload_needed_resource_ids().empty());
+  EXPECT_TRUE(observer()->upload_needed_local_ids().empty());
 
   close_callback2.Run();
 
   // Here, all the clients close the file, so it should be uploaded then.
   EXPECT_EQ(
       1U,
-      observer()->upload_needed_resource_ids().count(src_entry.resource_id()));
+      observer()->upload_needed_local_ids().count(src_entry.resource_id()));
 }
 
 }  // namespace file_system
diff --git a/chrome/browser/chromeos/drive/file_system/operation_observer.h b/chrome/browser/chromeos/drive/file_system/operation_observer.h
index c7f1754..8561406 100644
--- a/chrome/browser/chromeos/drive/file_system/operation_observer.h
+++ b/chrome/browser/chromeos/drive/file_system/operation_observer.h
@@ -23,7 +23,7 @@
 
   // Sent when a cache file is modified and upload is needed.
   virtual void OnCacheFileUploadNeededByOperation(
-      const std::string& resource_id) = 0;
+      const std::string& local_id) = 0;
 };
 
 }  // namespace file_system
diff --git a/chrome/browser/chromeos/drive/file_system/operation_test_base.cc b/chrome/browser/chromeos/drive/file_system/operation_test_base.cc
index ccacced..84f362b 100644
--- a/chrome/browser/chromeos/drive/file_system/operation_test_base.cc
+++ b/chrome/browser/chromeos/drive/file_system/operation_test_base.cc
@@ -6,7 +6,6 @@
 
 #include "base/prefs/testing_pref_service.h"
 #include "base/threading/sequenced_worker_pool.h"
-#include "chrome/browser/chromeos/drive/change_list_loader.h"
 #include "chrome/browser/chromeos/drive/change_list_processor.h"
 #include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h"
 #include "chrome/browser/chromeos/drive/file_cache.h"
@@ -33,8 +32,8 @@
 }
 
 void OperationTestBase::LoggingObserver::OnCacheFileUploadNeededByOperation(
-    const std::string& resource_id) {
-  upload_needed_resource_ids_.insert(resource_id);
+    const std::string& local_id) {
+  upload_needed_local_ids_.insert(local_id);
 }
 
 OperationTestBase::OperationTestBase() {
@@ -110,10 +109,9 @@
   ASSERT_TRUE(success);
 
   // Makes sure the FakeDriveService's content is loaded to the metadata_.
-  internal::ChangeListLoader change_list_loader(
-      blocking_task_runner_.get(), metadata_.get(), scheduler_.get());
-
-  change_list_loader.LoadIfNeeded(
+  change_list_loader_.reset(new internal::ChangeListLoader(
+      blocking_task_runner_.get(), metadata_.get(), scheduler_.get()));
+  change_list_loader_->LoadIfNeeded(
       internal::DirectoryFetchInfo(),
       google_apis::test_util::CreateCopyResultCallback(&error));
   test_util::RunBlockingPoolTask();
@@ -133,5 +131,27 @@
   return error;
 }
 
+std::string OperationTestBase::GetLocalId(const base::FilePath& path) {
+  std::string local_id;
+  FileError error = FILE_ERROR_FAILED;
+  base::PostTaskAndReplyWithResult(
+      blocking_task_runner(),
+      FROM_HERE,
+      base::Bind(&internal::ResourceMetadata::GetIdByPath,
+                 base::Unretained(metadata()), path, &local_id),
+      base::Bind(google_apis::test_util::CreateCopyResultCallback(&error)));
+  test_util::RunBlockingPoolTask();
+  EXPECT_EQ(FILE_ERROR_OK, error) << path.value();
+  return local_id;
+}
+
+FileError OperationTestBase::CheckForUpdates() {
+  FileError error = FILE_ERROR_FAILED;
+  change_list_loader_->CheckForUpdates(
+      google_apis::test_util::CreateCopyResultCallback(&error));
+  test_util::RunBlockingPoolTask();
+  return error;
+}
+
 }  // namespace file_system
 }  // namespace drive
diff --git a/chrome/browser/chromeos/drive/file_system/operation_test_base.h b/chrome/browser/chromeos/drive/file_system/operation_test_base.h
index 4056455..d4afea4 100644
--- a/chrome/browser/chromeos/drive/file_system/operation_test_base.h
+++ b/chrome/browser/chromeos/drive/file_system/operation_test_base.h
@@ -8,6 +8,7 @@
 #include <set>
 
 #include "base/files/scoped_temp_dir.h"
+#include "chrome/browser/chromeos/drive/change_list_loader.h"
 #include "chrome/browser/chromeos/drive/drive.pb.h"
 #include "chrome/browser/chromeos/drive/file_system/operation_observer.h"
 #include "chrome/browser/chromeos/drive/test_util.h"
@@ -49,21 +50,21 @@
     virtual void OnDirectoryChangedByOperation(
         const base::FilePath& path) OVERRIDE;
     virtual void OnCacheFileUploadNeededByOperation(
-        const std::string& resource_id) OVERRIDE;
+        const std::string& local_id) OVERRIDE;
 
     // Gets the set of changed paths.
     const std::set<base::FilePath>& get_changed_paths() {
       return changed_paths_;
     }
 
-    // Gets the set of upload needed resource IDs.
-    const std::set<std::string>& upload_needed_resource_ids() const {
-      return upload_needed_resource_ids_;
+    // Gets the set of upload needed local IDs.
+    const std::set<std::string>& upload_needed_local_ids() const {
+      return upload_needed_local_ids_;
     }
 
    private:
     std::set<base::FilePath> changed_paths_;
-    std::set<std::string> upload_needed_resource_ids_;
+    std::set<std::string> upload_needed_local_ids_;
   };
 
   OperationTestBase();
@@ -81,6 +82,13 @@
   FileError GetLocalResourceEntry(const base::FilePath& path,
                                   ResourceEntry* entry);
 
+  // Gets the local ID of the entry specified by the path.
+  std::string GetLocalId(const base::FilePath& path);
+
+  // Synchronously updates |metadata_| by fetching the change feed from the
+  // |fake_service_|.
+  FileError CheckForUpdates();
+
   // Accessors for the components.
   FakeDriveService* fake_service() {
     return fake_drive_service_.get();
@@ -111,6 +119,7 @@
       metadata_;
   scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_;
   scoped_ptr<internal::FileCache, test_util::DestroyHelperForTests> cache_;
+  scoped_ptr<internal::ChangeListLoader> change_list_loader_;
 };
 
 }  // namespace file_system
diff --git a/chrome/browser/chromeos/drive/file_system/remove_operation.cc b/chrome/browser/chromeos/drive/file_system/remove_operation.cc
index 5c07ccb..c1a0c58 100644
--- a/chrome/browser/chromeos/drive/file_system/remove_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/remove_operation.cc
@@ -23,8 +23,13 @@
 FileError CheckLocalState(internal::ResourceMetadata* metadata,
                           const base::FilePath& path,
                           bool is_recursive,
+                          std::string* local_id,
                           ResourceEntry* entry) {
-  FileError error = metadata->GetResourceEntryByPath(path, entry);
+  FileError error = metadata->GetIdByPath(path, local_id);
+  if (error != FILE_ERROR_OK)
+    return error;
+
+  error = metadata->GetResourceEntryById(*local_id, entry);
   if (error != FILE_ERROR_OK)
     return error;
 
@@ -44,15 +49,15 @@
 // Updates local metadata and cache state after remote delete.
 FileError UpdateLocalState(internal::ResourceMetadata* metadata,
                            internal::FileCache* cache,
-                           const std::string& resource_id,
+                           const std::string& local_id,
                            base::FilePath* changed_directory_path) {
-  *changed_directory_path = metadata->GetFilePath(resource_id).DirName();
-  FileError error = metadata->RemoveEntry(resource_id);
+  *changed_directory_path = metadata->GetFilePath(local_id).DirName();
+  FileError error = metadata->RemoveEntry(local_id);
   if (error != FILE_ERROR_OK)
     return error;
 
-  error = cache->Remove(resource_id);
-  DLOG_IF(ERROR, error != FILE_ERROR_OK) << "Failed to remove: " << resource_id;
+  error = cache->Remove(local_id);
+  DLOG_IF(ERROR, error != FILE_ERROR_OK) << "Failed to remove: " << local_id;
 
   return FILE_ERROR_OK;
 }
@@ -84,19 +89,27 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
+  std::string* local_id = new std::string;
   ResourceEntry* entry = new ResourceEntry;
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(),
       FROM_HERE,
-      base::Bind(&CheckLocalState, metadata_, path, is_recursive, entry),
+      base::Bind(&CheckLocalState,
+                 metadata_,
+                 path,
+                 is_recursive,
+                 local_id,
+                 entry),
       base::Bind(&RemoveOperation::RemoveAfterCheckLocalState,
                  weak_ptr_factory_.GetWeakPtr(),
                  callback,
+                 base::Owned(local_id),
                  base::Owned(entry)));
 }
 
 void RemoveOperation::RemoveAfterCheckLocalState(
     const FileOperationCallback& callback,
+    const std::string* local_id,
     const ResourceEntry* entry,
     FileError error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -112,12 +125,12 @@
       base::Bind(&RemoveOperation::RemoveAfterDeleteResource,
                  weak_ptr_factory_.GetWeakPtr(),
                  callback,
-                 entry->resource_id()));
+                 *local_id));
 }
 
 void RemoveOperation::RemoveAfterDeleteResource(
     const FileOperationCallback& callback,
-    const std::string& resource_id,
+    const std::string& local_id,
     google_apis::GDataErrorCode status) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
@@ -135,7 +148,7 @@
       base::Bind(&UpdateLocalState,
                  metadata_,
                  cache_,
-                 resource_id,
+                 local_id,
                  changed_directory_path),
       base::Bind(&RemoveOperation::RemoveAfterUpdateLocalState,
                  weak_ptr_factory_.GetWeakPtr(),
diff --git a/chrome/browser/chromeos/drive/file_system/remove_operation.h b/chrome/browser/chromeos/drive/file_system/remove_operation.h
index c5b694a..5c606c2 100644
--- a/chrome/browser/chromeos/drive/file_system/remove_operation.h
+++ b/chrome/browser/chromeos/drive/file_system/remove_operation.h
@@ -55,12 +55,13 @@
  private:
   // Part of Remove(). Called after CheckLocalState() completion.
   void RemoveAfterCheckLocalState(const FileOperationCallback& callback,
+                                  const std::string* local_id,
                                   const ResourceEntry* entry,
                                   FileError error);
 
   // Part of Remove(). Called after server-side removal is done.
   void RemoveAfterDeleteResource(const FileOperationCallback& callback,
-                                 const std::string& resource_id,
+                                 const std::string& local_id,
                                  google_apis::GDataErrorCode status);
 
   // Part of Remove(). Called after UpdateLocalState() completion.
diff --git a/chrome/browser/chromeos/drive/file_system/search_operation.cc b/chrome/browser/chromeos/drive/file_system/search_operation.cc
index d1972d2..276d204 100644
--- a/chrome/browser/chromeos/drive/file_system/search_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/search_operation.cc
@@ -25,9 +25,11 @@
 namespace file_system {
 namespace {
 
-// Refreshes entries of |resource_metadata| based on |resource_list|, and
-// returns the result. Refreshed entries will be stored into |result|.
-FileError RefreshEntriesOnBlockingPool(
+// Computes the path of each item in |resource_list| returned from the server
+// and stores to |result|, by using |resource_metadata|. If the metadata is not
+// up-to-date and did not contain an item, adds the item to "drive/other" for
+// temporally assigning a path.
+FileError ResolveSearchResultOnBlockingPool(
     internal::ResourceMetadata* resource_metadata,
     scoped_ptr<google_apis::ResourceList> resource_list,
     std::vector<SearchResultInfo>* result) {
@@ -38,13 +40,19 @@
       resource_list->entries();
   result->reserve(entries.size());
   for (size_t i = 0; i < entries.size(); ++i) {
-    ResourceEntry entry;
-    if (!ConvertToResourceEntry(*entries[i], &entry))
-      continue;  // Skip non-file entries.
+    std::string local_id;
+    FileError error = resource_metadata->GetIdByResourceId(
+        entries[i]->resource_id(), &local_id);
 
-    const std::string id = entry.resource_id();
-    FileError error = resource_metadata->RefreshEntry(id, entry);
+    ResourceEntry entry;
+    if (error == FILE_ERROR_OK)
+      error = resource_metadata->GetResourceEntryById(local_id, &entry);
+
     if (error == FILE_ERROR_NOT_FOUND) {
+      std::string original_parent_id;
+      if (!ConvertToResourceEntry(*entries[i], &entry, &original_parent_id))
+        continue;  // Skip non-file entries.
+
       // The result is absent in local resource metadata. This can happen if
       // the metadata is not synced to the latest server state yet. In that
       // case, we temporarily add the file to the special "drive/other"
@@ -54,19 +62,12 @@
       // It will be moved to the right place when the metadata gets synced
       // in normal loading process in ChangeListProcessor.
       entry.set_parent_local_id(util::kDriveOtherDirSpecialResourceId);
-      error = resource_metadata->AddEntry(entry);
-
-      // FILE_ERROR_EXISTS may happen if we have already added the entry to
-      // "drive/other" once before. That's not an error.
-      if (error == FILE_ERROR_EXISTS)
-        error = FILE_ERROR_OK;
+      error = resource_metadata->AddEntry(entry, &local_id);
     }
-    if (error == FILE_ERROR_OK)
-      error = resource_metadata->GetResourceEntryById(id, &entry);
     if (error != FILE_ERROR_OK)
       return error;
-    result->push_back(SearchResultInfo(resource_metadata->GetFilePath(id),
-                                       entry));
+    result->push_back(
+        SearchResultInfo(resource_metadata->GetFilePath(local_id), entry));
   }
 
   return FILE_ERROR_OK;
@@ -88,12 +89,12 @@
 }
 
 void SearchOperation::Search(const std::string& search_query,
-                             const GURL& next_url,
+                             const std::string& page_token,
                              const SearchCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  if (next_url.is_empty()) {
+  if (page_token.empty()) {
     // This is first request for the |search_query|.
     scheduler_->Search(
         search_query,
@@ -101,8 +102,8 @@
                    weak_ptr_factory_.GetWeakPtr(), callback));
   } else {
     // There is the remaining result so fetch it.
-    scheduler_->ContinueGetResourceList(
-        next_url,
+    scheduler_->GetRemainingFileList(
+        page_token,
         base::Bind(&SearchOperation::SearchAfterGetResourceList,
                    weak_ptr_factory_.GetWeakPtr(), callback));
   }
@@ -117,24 +118,23 @@
 
   FileError error = GDataToFileError(gdata_error);
   if (error != FILE_ERROR_OK) {
-    callback.Run(error, GURL(), scoped_ptr<std::vector<SearchResultInfo> >());
+    callback.Run(
+        error, std::string(), scoped_ptr<std::vector<SearchResultInfo> >());
     return;
   }
 
   DCHECK(resource_list);
 
+  // TODO(hidehiko): Replace this to page token.
   GURL next_url;
   resource_list->GetNextFeedURL(&next_url);
 
-  // The search results will be returned using virtual directory.
-  // The directory is not really part of the file system, so it has no parent or
-  // root.
   scoped_ptr<std::vector<SearchResultInfo> > result(
       new std::vector<SearchResultInfo>);
   if (resource_list->entries().empty()) {
     // Short cut. If the resource entry is empty, we don't need to refresh
     // the resource metadata.
-    callback.Run(FILE_ERROR_OK, next_url, result.Pass());
+    callback.Run(FILE_ERROR_OK, next_url.spec(), result.Pass());
     return;
   }
 
@@ -142,20 +142,20 @@
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(),
       FROM_HERE,
-      base::Bind(&RefreshEntriesOnBlockingPool,
+      base::Bind(&ResolveSearchResultOnBlockingPool,
                  metadata_,
                  base::Passed(&resource_list),
                  result_ptr),
-      base::Bind(&SearchOperation::SearchAfterRefreshEntry,
+      base::Bind(&SearchOperation::SearchAfterResolveSearchResult,
                  weak_ptr_factory_.GetWeakPtr(),
                  callback,
-                 next_url,
+                 next_url.spec(),
                  base::Passed(&result)));
 }
 
-void SearchOperation::SearchAfterRefreshEntry(
+void SearchOperation::SearchAfterResolveSearchResult(
     const SearchCallback& callback,
-    const GURL& next_url,
+    const std::string& page_token,
     scoped_ptr<std::vector<SearchResultInfo> > result,
     FileError error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -163,11 +163,12 @@
   DCHECK(result);
 
   if (error != FILE_ERROR_OK) {
-    callback.Run(error, GURL(), scoped_ptr<std::vector<SearchResultInfo> >());
+    callback.Run(
+        error, std::string(), scoped_ptr<std::vector<SearchResultInfo> >());
     return;
   }
 
-  callback.Run(error, next_url, result.Pass());
+  callback.Run(error, page_token, result.Pass());
 }
 
 }  // namespace file_system
diff --git a/chrome/browser/chromeos/drive/file_system/search_operation.h b/chrome/browser/chromeos/drive/file_system/search_operation.h
index 34c2025..737171c 100644
--- a/chrome/browser/chromeos/drive/file_system/search_operation.h
+++ b/chrome/browser/chromeos/drive/file_system/search_operation.h
@@ -13,8 +13,6 @@
 #include "chrome/browser/chromeos/drive/file_system_interface.h"
 #include "chrome/browser/google_apis/gdata_errorcode.h"
 
-class GURL;
-
 namespace base {
 class SequencedTaskRunner;
 }  // namespace base
@@ -50,22 +48,20 @@
   //
   // |callback| must not be null.
   void Search(const std::string& search_query,
-              const GURL& next_url,
+              const std::string& page_token,
               const SearchCallback& callback);
 
  private:
-  // Part of Search(). This is called after the ResourceList is fetched from
-  // the server.
+  // Part of Search(), called after the ResourceList is fetched from the server.
   void SearchAfterGetResourceList(
       const SearchCallback& callback,
       google_apis::GDataErrorCode gdata_error,
       scoped_ptr<google_apis::ResourceList> resource_list);
 
-  // Part of Search(). This is called after the RefreshEntryOnBlockingPool
-  // is completed.
-  void SearchAfterRefreshEntry(
+  // Part of Search(), called after |result| is filled on the blocking pool.
+  void SearchAfterResolveSearchResult(
       const SearchCallback& callback,
-      const GURL& next_url,
+      const std::string& page_token,
       scoped_ptr<std::vector<SearchResultInfo> > result,
       FileError error);
 
diff --git a/chrome/browser/chromeos/drive/file_system/search_operation_unittest.cc b/chrome/browser/chromeos/drive/file_system/search_operation_unittest.cc
index e8426d0..c4fb21d 100644
--- a/chrome/browser/chromeos/drive/file_system/search_operation_unittest.cc
+++ b/chrome/browser/chromeos/drive/file_system/search_operation_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/chromeos/drive/file_system/search_operation.h"
 
-#include "chrome/browser/chromeos/drive/change_list_loader.h"
 #include "chrome/browser/chromeos/drive/file_system/operation_test_base.h"
 #include "chrome/browser/drive/fake_drive_service.h"
 #include "chrome/browser/google_apis/gdata_wapi_parser.h"
@@ -38,16 +37,16 @@
   };
 
   FileError error = FILE_ERROR_FAILED;
-  GURL next_url;
+  std::string page_token;
   scoped_ptr<std::vector<SearchResultInfo> > results;
 
-  operation.Search("Directory", GURL(),
+  operation.Search("Directory", std::string(),
                    google_apis::test_util::CreateCopyResultCallback(
-                       &error, &next_url, &results));
+                       &error, &page_token, &results));
   test_util::RunBlockingPoolTask();
 
   EXPECT_EQ(FILE_ERROR_OK, error);
-  EXPECT_EQ(GURL(), next_url);
+  EXPECT_EQ(std::string(), page_token);
   EXPECT_EQ(ARRAYSIZE_UNSAFE(kExpectedResults), results->size());
   for (size_t i = 0; i < results->size(); i++) {
     EXPECT_EQ(kExpectedResults[i].path, results->at(i).path.AsUTF8Unsafe());
@@ -79,16 +78,16 @@
   };
 
   FileError error = FILE_ERROR_FAILED;
-  GURL next_url;
+  std::string page_token;
   scoped_ptr<std::vector<SearchResultInfo> > results;
 
-  operation.Search("\"Directory 1\"", GURL(),
+  operation.Search("\"Directory 1\"", std::string(),
                    google_apis::test_util::CreateCopyResultCallback(
-                       &error, &next_url, &results));
+                       &error, &page_token, &results));
   test_util::RunBlockingPoolTask();
 
   EXPECT_EQ(FILE_ERROR_OK, error);
-  EXPECT_EQ(GURL(), next_url);
+  EXPECT_EQ(std::string(), page_token);
   ASSERT_EQ(ARRAYSIZE_UNSAFE(kExpectedResultsBeforeLoad), results->size());
   for (size_t i = 0; i < results->size(); i++) {
     EXPECT_EQ(kExpectedResultsBeforeLoad[i].path,
@@ -98,11 +97,7 @@
   }
 
   // Load the change from FakeDriveService.
-  internal::ChangeListLoader change_list_loader(
-      blocking_task_runner(), metadata(), scheduler());
-  change_list_loader.CheckForUpdates(
-      google_apis::test_util::CreateCopyResultCallback(&error));
-  test_util::RunBlockingPoolTask();
+  ASSERT_EQ(FILE_ERROR_OK, CheckForUpdates());
 
   // Now the new entry must be reported to be in the right directory.
   const SearchResultPair kExpectedResultsAfterLoad[] = {
@@ -110,13 +105,13 @@
       { "drive/root/New Directory 1!", true },
   };
   error = FILE_ERROR_FAILED;
-  operation.Search("\"Directory 1\"", GURL(),
+  operation.Search("\"Directory 1\"", std::string(),
                    google_apis::test_util::CreateCopyResultCallback(
-                       &error, &next_url, &results));
+                       &error, &page_token, &results));
   test_util::RunBlockingPoolTask();
 
   EXPECT_EQ(FILE_ERROR_OK, error);
-  EXPECT_EQ(GURL(), next_url);
+  EXPECT_EQ(std::string(), page_token);
   ASSERT_EQ(ARRAYSIZE_UNSAFE(kExpectedResultsAfterLoad), results->size());
   for (size_t i = 0; i < results->size(); i++) {
     EXPECT_EQ(kExpectedResultsAfterLoad[i].path,
@@ -130,16 +125,16 @@
   SearchOperation operation(blocking_task_runner(), scheduler(), metadata());
 
   FileError error = FILE_ERROR_FAILED;
-  GURL next_url;
+  std::string page_token;
   scoped_ptr<std::vector<SearchResultInfo> > results;
 
-  operation.Search("\"no-match query\"", GURL(),
+  operation.Search("\"no-match query\"", std::string(),
                    google_apis::test_util::CreateCopyResultCallback(
-                       &error, &next_url, &results));
+                       &error, &page_token, &results));
   test_util::RunBlockingPoolTask();
 
   EXPECT_EQ(FILE_ERROR_OK, error);
-  EXPECT_EQ(GURL(), next_url);
+  EXPECT_EQ(std::string(), page_token);
   EXPECT_EQ(0U, results->size());
 }
 
diff --git a/chrome/browser/chromeos/drive/file_system/touch_operation.cc b/chrome/browser/chromeos/drive/file_system/touch_operation.cc
index 1c7d7de..1ead717 100644
--- a/chrome/browser/chromeos/drive/file_system/touch_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/touch_operation.cc
@@ -21,6 +21,50 @@
 namespace drive {
 namespace file_system {
 
+namespace {
+
+// Returns ResourceEntry and the local ID of the entry at the given path.
+FileError GetResourceEntryAndIdByPath(internal::ResourceMetadata* metadata,
+                                      const base::FilePath& file_path,
+                                      std::string* local_id,
+                                      ResourceEntry* entry) {
+  FileError error = metadata->GetIdByPath(file_path, local_id);
+  if (error != FILE_ERROR_OK)
+    return error;
+  return metadata->GetResourceEntryById(*local_id, entry);
+}
+
+// Refreshes the entry specified by |local_id| with the contents of
+// |resource_entry|.
+FileError RefreshEntry(internal::ResourceMetadata* metadata,
+                       const std::string& local_id,
+                       scoped_ptr<google_apis::ResourceEntry> resource_entry,
+                       base::FilePath* file_path) {
+  DCHECK(resource_entry);
+
+  ResourceEntry entry;
+  std::string parent_resource_id;
+  if (!ConvertToResourceEntry(*resource_entry, &entry, &parent_resource_id))
+    return FILE_ERROR_NOT_A_FILE;
+
+  std::string parent_local_id;
+  FileError error = metadata->GetIdByResourceId(parent_resource_id,
+                                                &parent_local_id);
+  if (error != FILE_ERROR_OK)
+    return error;
+
+  entry.set_parent_local_id(parent_local_id);
+
+  error = metadata->RefreshEntry(local_id, entry);
+  if (error != FILE_ERROR_OK)
+    return error;
+
+  *file_path = metadata->GetFilePath(local_id);
+  return file_path->empty() ? FILE_ERROR_FAILED : FILE_ERROR_OK;
+}
+
+}  // namespace
+
 TouchOperation::TouchOperation(base::SequencedTaskRunner* blocking_task_runner,
                                OperationObserver* observer,
                                JobScheduler* scheduler,
@@ -43,27 +87,29 @@
   DCHECK(!callback.is_null());
 
   ResourceEntry* entry = new ResourceEntry;
+  std::string* local_id = new std::string;
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(),
       FROM_HERE,
-      base::Bind(&internal::ResourceMetadata::GetResourceEntryByPath,
+      base::Bind(&GetResourceEntryAndIdByPath,
                  base::Unretained(metadata_),
                  file_path,
+                 local_id,
                  entry),
       base::Bind(&TouchOperation::TouchFileAfterGetResourceEntry,
                  weak_ptr_factory_.GetWeakPtr(),
-                 file_path,
                  last_access_time,
                  last_modified_time,
                  callback,
+                 base::Owned(local_id),
                  base::Owned(entry)));
 }
 
 void TouchOperation::TouchFileAfterGetResourceEntry(
-    const base::FilePath& file_path,
     const base::Time& last_access_time,
     const base::Time& last_modified_time,
     const FileOperationCallback& callback,
+    std::string* local_id,
     ResourceEntry* entry,
     FileError error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -81,11 +127,11 @@
       entry->resource_id(), last_modified_time, last_access_time,
       base::Bind(&TouchOperation::TouchFileAfterServerTimeStampUpdated,
                  weak_ptr_factory_.GetWeakPtr(),
-                 file_path, callback));
+                 *local_id, callback));
 }
 
 void TouchOperation::TouchFileAfterServerTimeStampUpdated(
-    const base::FilePath& file_path,
+    const std::string& local_id,
     const FileOperationCallback& callback,
     google_apis::GDataErrorCode gdata_error,
     scoped_ptr<google_apis::ResourceEntry> resource_entry) {
@@ -98,35 +144,30 @@
     return;
   }
 
-  DCHECK(resource_entry);
-  ResourceEntry entry;
-  if (!ConvertToResourceEntry(*resource_entry, &entry)) {
-    callback.Run(FILE_ERROR_NOT_A_FILE);
-    return;
-  }
-
+  base::FilePath* file_path = new base::FilePath;
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(),
       FROM_HERE,
-      base::Bind(&internal::ResourceMetadata::RefreshEntry,
+      base::Bind(&RefreshEntry,
                  base::Unretained(metadata_),
-                 entry.resource_id(),
-                 entry),
+                 local_id,
+                 base::Passed(&resource_entry),
+                 file_path),
       base::Bind(&TouchOperation::TouchFileAfterRefreshMetadata,
                  weak_ptr_factory_.GetWeakPtr(),
-                 file_path,
+                 base::Owned(file_path),
                  callback));
 }
 
 void TouchOperation::TouchFileAfterRefreshMetadata(
-    const base::FilePath& file_path,
+    const base::FilePath* file_path,
     const FileOperationCallback& callback,
     FileError error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
   if (error == FILE_ERROR_OK)
-    observer_->OnDirectoryChangedByOperation(file_path.DirName());
+    observer_->OnDirectoryChangedByOperation(file_path->DirName());
 
   callback.Run(error);
 }
diff --git a/chrome/browser/chromeos/drive/file_system/touch_operation.h b/chrome/browser/chromeos/drive/file_system/touch_operation.h
index 48a3590..be00163 100644
--- a/chrome/browser/chromeos/drive/file_system/touch_operation.h
+++ b/chrome/browser/chromeos/drive/file_system/touch_operation.h
@@ -52,23 +52,23 @@
 
  private:
   // Part of TouchFile(). Runs after GetResourceEntry is completed.
-  void TouchFileAfterGetResourceEntry(const base::FilePath& file_path,
-                                      const base::Time& last_access_time,
+  void TouchFileAfterGetResourceEntry(const base::Time& last_access_time,
                                       const base::Time& last_modified_time,
                                       const FileOperationCallback& callback,
+                                      std::string* local_id,
                                       ResourceEntry* entry,
                                       FileError error);
 
   // Part of TouchFile(). Runs after the server side update for last access time
   // and last modified time is completed.
   void TouchFileAfterServerTimeStampUpdated(
-      const base::FilePath& file_path,
+      const std::string& local_id,
       const FileOperationCallback& callback,
       google_apis::GDataErrorCode gdata_error,
       scoped_ptr<google_apis::ResourceEntry> resource_entry);
 
   // Part of TouchFile(). Runs after refreshing the local metadata is completed.
-  void TouchFileAfterRefreshMetadata(const base::FilePath& file_path,
+  void TouchFileAfterRefreshMetadata(const base::FilePath* file_path,
                                      const FileOperationCallback& callback,
                                      FileError error);
 
diff --git a/chrome/browser/chromeos/drive/file_system/truncate_operation.cc b/chrome/browser/chromeos/drive/file_system/truncate_operation.cc
index e1c0bc7..7b81350 100644
--- a/chrome/browser/chromeos/drive/file_system/truncate_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/truncate_operation.cc
@@ -27,13 +27,21 @@
 
 // Truncates the local file at |local_cache_path| to the |length| bytes,
 // then marks the resource is dirty on |cache|.
-FileError TruncateOnBlockingPool(internal::FileCache* cache,
+FileError TruncateOnBlockingPool(internal::ResourceMetadata* metadata,
+                                 internal::FileCache* cache,
                                  const std::string& resource_id,
                                  const base::FilePath& local_cache_path,
-                                 int64 length) {
+                                 int64 length,
+                                 std::string* local_id) {
+  DCHECK(metadata);
   DCHECK(cache);
+  DCHECK(local_id);
 
-  FileError error = cache->MarkDirty(resource_id);
+  FileError error = metadata->GetIdByResourceId(resource_id, local_id);
+  if (error != FILE_ERROR_OK)
+    return error;
+
+  error = cache->MarkDirty(*local_id);
   if (error != FILE_ERROR_OK)
     return error;
 
@@ -66,6 +74,7 @@
     const base::FilePath& temporary_file_directory)
     : blocking_task_runner_(blocking_task_runner),
       observer_(observer),
+      metadata_(metadata),
       cache_(cache),
       download_operation_(new DownloadOperation(blocking_task_runner,
                                                 observer,
@@ -83,6 +92,7 @@
                                  int64 length,
                                  const FileOperationCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!callback.is_null());
 
   if (length < 0) {
     base::MessageLoopProxy::current()->PostTask(
@@ -99,11 +109,10 @@
       GetFileContentInitializedCallback(),
       google_apis::GetContentCallback(),
       base::Bind(&TruncateOperation::TruncateAfterEnsureFileDownloadedByPath,
-                 weak_ptr_factory_.GetWeakPtr(), file_path, length, callback));
+                 weak_ptr_factory_.GetWeakPtr(), length, callback));
 }
 
 void TruncateOperation::TruncateAfterEnsureFileDownloadedByPath(
-    const base::FilePath& file_path,
     int64 length,
     const FileOperationCallback& callback,
     FileError error,
@@ -124,26 +133,26 @@
     return;
   }
 
+  std::string* local_id = new std::string;
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(),
       FROM_HERE,
       base::Bind(&TruncateOnBlockingPool,
-                 base::Unretained(cache_),
-                 entry->resource_id(),
-                 local_file_path, length),
+                 metadata_, cache_, entry->resource_id(), local_file_path,
+                 length, local_id),
       base::Bind(
           &TruncateOperation::TruncateAfterTruncateOnBlockingPool,
-          weak_ptr_factory_.GetWeakPtr(), entry->resource_id(), callback));
+          weak_ptr_factory_.GetWeakPtr(), base::Owned(local_id), callback));
 }
 
 void TruncateOperation::TruncateAfterTruncateOnBlockingPool(
-    const std::string& resource_id,
+    const std::string* local_id,
     const FileOperationCallback& callback,
     FileError error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  observer_->OnCacheFileUploadNeededByOperation(resource_id);
+  observer_->OnCacheFileUploadNeededByOperation(*local_id);
 
   callback.Run(error);
 }
diff --git a/chrome/browser/chromeos/drive/file_system/truncate_operation.h b/chrome/browser/chromeos/drive/file_system/truncate_operation.h
index 39eba6b..eca409d 100644
--- a/chrome/browser/chromeos/drive/file_system/truncate_operation.h
+++ b/chrome/browser/chromeos/drive/file_system/truncate_operation.h
@@ -54,7 +54,6 @@
  private:
   // Part of Truncate(). Called after EnsureFileDownloadedByPath() is complete.
   void TruncateAfterEnsureFileDownloadedByPath(
-      const base::FilePath& file_path,
       int64 length,
       const FileOperationCallback& callback,
       FileError error,
@@ -63,12 +62,13 @@
 
   // Part of Truncate(). Called after TruncateOnBlockingPool() is complete.
   void TruncateAfterTruncateOnBlockingPool(
-      const std::string& resource_id,
+      const std::string* local_id,
       const FileOperationCallback& callback,
       FileError error);
 
   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
   OperationObserver* observer_;
+  internal::ResourceMetadata* metadata_;
   internal::FileCache* cache_;
 
   scoped_ptr<DownloadOperation> download_operation_;
diff --git a/chrome/browser/chromeos/drive/file_system/truncate_operation_unittest.cc b/chrome/browser/chromeos/drive/file_system/truncate_operation_unittest.cc
index e2c8575..1897809 100644
--- a/chrome/browser/chromeos/drive/file_system/truncate_operation_unittest.cc
+++ b/chrome/browser/chromeos/drive/file_system/truncate_operation_unittest.cc
@@ -17,7 +17,7 @@
 
 class TruncateOperationTest : public OperationTestBase {
  protected:
-  virtual void SetUp() {
+  virtual void SetUp() OVERRIDE {
     OperationTestBase::SetUp();
 
     operation_.reset(new TruncateOperation(
@@ -48,7 +48,7 @@
   base::FilePath local_path;
   error = FILE_ERROR_FAILED;
   cache()->GetFileOnUIThread(
-      src_entry.resource_id(),
+      GetLocalId(file_in_root),
       google_apis::test_util::CreateCopyResultCallback(&error, &local_path));
   test_util::RunBlockingPoolTask();
   ASSERT_EQ(FILE_ERROR_OK, error);
@@ -107,7 +107,7 @@
   base::FilePath local_path;
   error = FILE_ERROR_FAILED;
   cache()->GetFileOnUIThread(
-      src_entry.resource_id(),
+      GetLocalId(file_in_root),
       google_apis::test_util::CreateCopyResultCallback(&error, &local_path));
   test_util::RunBlockingPoolTask();
   ASSERT_EQ(FILE_ERROR_OK, error);
diff --git a/chrome/browser/chromeos/drive/file_system/update_operation.cc b/chrome/browser/chromeos/drive/file_system/update_operation.cc
index f787e17..212ff97 100644
--- a/chrome/browser/chromeos/drive/file_system/update_operation.cc
+++ b/chrome/browser/chromeos/drive/file_system/update_operation.cc
@@ -23,6 +23,7 @@
   LocalState() : content_is_same(false) {
   }
 
+  std::string local_id;
   ResourceEntry entry;
   base::FilePath drive_file_path;
   base::FilePath cache_file_path;
@@ -34,10 +35,9 @@
 // Gets locally stored information about the specified file.
 FileError GetFileLocalState(internal::ResourceMetadata* metadata,
                             internal::FileCache* cache,
-                            const std::string& resource_id,
                             UpdateOperation::ContentCheckMode check,
                             UpdateOperation::LocalState* local_state) {
-  FileError error = metadata->GetResourceEntryById(resource_id,
+  FileError error = metadata->GetResourceEntryById(local_state->local_id,
                                                    &local_state->entry);
   if (error != FILE_ERROR_OK)
     return error;
@@ -45,11 +45,11 @@
   if (local_state->entry.file_info().is_directory())
     return FILE_ERROR_NOT_A_FILE;
 
-  local_state->drive_file_path = metadata->GetFilePath(resource_id);
+  local_state->drive_file_path = metadata->GetFilePath(local_state->local_id);
   if (local_state->drive_file_path.empty())
     return FILE_ERROR_NOT_FOUND;
 
-  error = cache->GetFile(resource_id, &local_state->cache_file_path);
+  error = cache->GetFile(local_state->local_id, &local_state->cache_file_path);
   if (error != FILE_ERROR_OK)
     return error;
 
@@ -58,7 +58,7 @@
     local_state->content_is_same =
         (md5 == local_state->entry.file_specific_info().md5());
     if (local_state->content_is_same)
-      cache->ClearDirty(resource_id, md5);
+      cache->ClearDirty(local_state->local_id, md5);
   } else {
     local_state->content_is_same = false;
   }
@@ -70,23 +70,31 @@
 FileError UpdateFileLocalState(
     internal::ResourceMetadata* metadata,
     internal::FileCache* cache,
+    const std::string& local_id,
     scoped_ptr<google_apis::ResourceEntry> resource_entry,
     base::FilePath* drive_file_path) {
   ResourceEntry entry;
-  if (!ConvertToResourceEntry(*resource_entry, &entry))
+  std::string parent_resource_id;
+  if (!ConvertToResourceEntry(*resource_entry, &entry, &parent_resource_id))
     return FILE_ERROR_NOT_A_FILE;
 
-  FileError error = metadata->RefreshEntry(entry.resource_id(), entry);
+  std::string parent_local_id;
+  FileError error = metadata->GetIdByResourceId(parent_resource_id,
+                                                &parent_local_id);
+  if (error != FILE_ERROR_OK)
+    return error;
+  entry.set_parent_local_id(parent_local_id);
+
+  error = metadata->RefreshEntry(local_id, entry);
   if (error != FILE_ERROR_OK)
     return error;
 
-  *drive_file_path = metadata->GetFilePath(entry.resource_id());
+  *drive_file_path = metadata->GetFilePath(local_id);
   if (drive_file_path->empty())
     return FILE_ERROR_NOT_FOUND;
 
   // Clear the dirty bit if we have updated an existing file.
-  return cache->ClearDirty(entry.resource_id(),
-                           entry.file_specific_info().md5());
+  return cache->ClearDirty(local_id, entry.file_specific_info().md5());
 }
 
 }  // namespace
@@ -110,8 +118,8 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 }
 
-void UpdateOperation::UpdateFileByResourceId(
-    const std::string& resource_id,
+void UpdateOperation::UpdateFileByLocalId(
+    const std::string& local_id,
     const ClientContext& context,
     ContentCheckMode check,
     const FileOperationCallback& callback) {
@@ -119,13 +127,13 @@
   DCHECK(!callback.is_null());
 
   LocalState* local_state = new LocalState;
+  local_state->local_id = local_id;
   base::PostTaskAndReplyWithResult(
       blocking_task_runner_.get(),
       FROM_HERE,
       base::Bind(&GetFileLocalState,
                  metadata_,
                  cache_,
-                 resource_id,
                  check,
                  local_state),
       base::Bind(&UpdateOperation::UpdateFileAfterGetLocalState,
@@ -157,11 +165,13 @@
       context,
       base::Bind(&UpdateOperation::UpdateFileAfterUpload,
                  weak_ptr_factory_.GetWeakPtr(),
-                 callback));
+                 callback,
+                 local_state->local_id));
 }
 
 void UpdateOperation::UpdateFileAfterUpload(
     const FileOperationCallback& callback,
+    const std::string& local_id,
     google_apis::GDataErrorCode error,
     scoped_ptr<google_apis::ResourceEntry> resource_entry) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -180,6 +190,7 @@
       base::Bind(&UpdateFileLocalState,
                  metadata_,
                  cache_,
+                 local_id,
                  base::Passed(&resource_entry),
                  drive_file_path),
       base::Bind(&UpdateOperation::UpdateFileAfterUpdateLocalState,
diff --git a/chrome/browser/chromeos/drive/file_system/update_operation.h b/chrome/browser/chromeos/drive/file_system/update_operation.h
index 3a26ab4..869e526 100644
--- a/chrome/browser/chromeos/drive/file_system/update_operation.h
+++ b/chrome/browser/chromeos/drive/file_system/update_operation.h
@@ -56,15 +56,15 @@
     RUN_CONTENT_CHECK,
   };
 
-  // Updates a file by the given |resource_id| on the Drive server by
+  // Updates a file by the given |local_id| on the Drive server by
   // uploading an updated version. Used for uploading dirty files. The file
   // should already be present in the cache.
   //
   // |callback| must not be null.
-  void UpdateFileByResourceId(const std::string& resource_id,
-                              const ClientContext& context,
-                              ContentCheckMode check,
-                              const FileOperationCallback& callback);
+  void UpdateFileByLocalId(const std::string& local_id,
+                           const ClientContext& context,
+                           ContentCheckMode check,
+                           const FileOperationCallback& callback);
 
   struct LocalState;
 
@@ -76,6 +76,7 @@
 
   void UpdateFileAfterUpload(
       const FileOperationCallback& callback,
+      const std::string& local_id,
       google_apis::GDataErrorCode error,
       scoped_ptr<google_apis::ResourceEntry> resource_entry);
 
diff --git a/chrome/browser/chromeos/drive/file_system/update_operation_unittest.cc b/chrome/browser/chromeos/drive/file_system/update_operation_unittest.cc
index ebb2d4d..2975c5e 100644
--- a/chrome/browser/chromeos/drive/file_system/update_operation_unittest.cc
+++ b/chrome/browser/chromeos/drive/file_system/update_operation_unittest.cc
@@ -28,7 +28,7 @@
  scoped_ptr<UpdateOperation> operation_;
 };
 
-TEST_F(UpdateOperationTest, UpdateFileByResourceId_PersistentFile) {
+TEST_F(UpdateOperationTest, UpdateFileByLocalId_PersistentFile) {
   const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt"));
   const std::string kResourceId("file:2_file_resource_id");
   const std::string kMd5("3b4382ebefec6e743578c76bbd0575ce");
@@ -64,10 +64,9 @@
 
   int64 original_changestamp = fake_service()->largest_changestamp();
 
-  // The callback will be called upon completion of
-  // UpdateFileByResourceId().
+  // The callback will be called upon completion of UpdateFileByLocalId().
   error = FILE_ERROR_FAILED;
-  operation_->UpdateFileByResourceId(
+  operation_->UpdateFileByLocalId(
       kResourceId,
       ClientContext(USER_INITIATED),
       UpdateOperation::RUN_CONTENT_CHECK,
@@ -101,9 +100,9 @@
   EXPECT_FALSE(cache_entry.is_dirty());
 }
 
-TEST_F(UpdateOperationTest, UpdateFileByResourceId_NonexistentFile) {
+TEST_F(UpdateOperationTest, UpdateFileByLocalId_NonexistentFile) {
   FileError error = FILE_ERROR_OK;
-  operation_->UpdateFileByResourceId(
+  operation_->UpdateFileByLocalId(
       "file:nonexistent_resource_id",
       ClientContext(USER_INITIATED),
       UpdateOperation::RUN_CONTENT_CHECK,
@@ -112,7 +111,7 @@
   EXPECT_EQ(FILE_ERROR_NOT_FOUND, error);
 }
 
-TEST_F(UpdateOperationTest, UpdateFileByResourceId_Md5) {
+TEST_F(UpdateOperationTest, UpdateFileByLocalId_Md5) {
   const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt"));
   const std::string kResourceId("file:2_file_resource_id");
   const std::string kMd5("3b4382ebefec6e743578c76bbd0575ce");
@@ -140,10 +139,9 @@
 
   int64 original_changestamp = fake_service()->largest_changestamp();
 
-  // The callback will be called upon completion of
-  // UpdateFileByResourceId().
+  // The callback will be called upon completion of UpdateFileByLocalId().
   error = FILE_ERROR_FAILED;
-  operation_->UpdateFileByResourceId(
+  operation_->UpdateFileByLocalId(
       kResourceId,
       ClientContext(USER_INITIATED),
       UpdateOperation::RUN_CONTENT_CHECK,
@@ -184,12 +182,12 @@
   test_util::RunBlockingPoolTask();
   EXPECT_EQ(FILE_ERROR_OK, error);
 
-  // And call UpdateFileByResourceId again.
+  // And call UpdateFileByLocalId again.
   // In this case, although the file is marked as dirty, but the content
   // hasn't been changed. Thus, the actual uploading should be skipped.
   original_changestamp = fake_service()->largest_changestamp();
   error = FILE_ERROR_FAILED;
-  operation_->UpdateFileByResourceId(
+  operation_->UpdateFileByLocalId(
       kResourceId,
       ClientContext(USER_INITIATED),
       UpdateOperation::RUN_CONTENT_CHECK,
@@ -216,12 +214,12 @@
   test_util::RunBlockingPoolTask();
   EXPECT_EQ(FILE_ERROR_OK, error);
 
-  // And call UpdateFileByResourceId again.
+  // And call UpdateFileByLocalId again.
   // In this case, NO_CONTENT_CHECK is set, so the actual uploading should run
   // no matter the content is changed or not.
   original_changestamp = fake_service()->largest_changestamp();
   error = FILE_ERROR_FAILED;
-  operation_->UpdateFileByResourceId(
+  operation_->UpdateFileByLocalId(
       kResourceId,
       ClientContext(USER_INITIATED),
       UpdateOperation::NO_CONTENT_CHECK,
diff --git a/chrome/browser/chromeos/drive/file_system_interface.h b/chrome/browser/chromeos/drive/file_system_interface.h
index a038060..e2d91a7 100644
--- a/chrome/browser/chromeos/drive/file_system_interface.h
+++ b/chrome/browser/chromeos/drive/file_system_interface.h
@@ -37,14 +37,12 @@
 
 // Struct to represent a search result for SearchMetadata().
 struct MetadataSearchResult {
-  MetadataSearchResult(const ResourceEntry& in_entry,
+  MetadataSearchResult(const base::FilePath& in_path,
+                       const ResourceEntry& in_entry,
                        const std::string& in_highlighted_base_name)
-      : entry(in_entry),
+      : path(in_path),
+        entry(in_entry),
         highlighted_base_name(in_highlighted_base_name) {
-    // Note: |path| is set separately from |entry| or other fields, because
-    // getting path typically takes longer time hence we want to fill it only
-    // when it is necessary. (I.e., not for temporary candidates, just for
-    // final user visible results.)
   }
 
   // The two members are used to create FileEntry object.
@@ -87,7 +85,7 @@
 // If |error| is not FILE_ERROR_OK, |result_paths| is empty.
 typedef base::Callback<void(
     FileError error,
-    const GURL& next_url,
+    const std::string& page_token,
     scoped_ptr<std::vector<SearchResultInfo> > result_paths)> SearchCallback;
 
 // Callback for SearchMetadata(). On success, |error| is FILE_ERROR_OK, and
@@ -367,13 +365,14 @@
       const ReadDirectoryCallback& callback) = 0;
 
   // Does server side content search for |search_query|.
-  // If |next_url| is set, this is the search result url that will be fetched.
-  // Search results will be returned as a list of results' |SearchResultInfo|
-  // structs, which contains file's path and is_directory flag.
+  // If |page_token| is set, this is the search result url that will be
+  // fetched. Search results will be returned as a list of results'
+  // |SearchResultInfo| structs, which contains file's path and is_directory
+  // flag.
   //
   // |callback| must not be null.
   virtual void Search(const std::string& search_query,
-                      const GURL& next_url,
+                      const std::string& page_token,
                       const SearchCallback& callback) = 0;
 
   // Searches the local resource metadata, and returns the entries
@@ -418,13 +417,12 @@
       const base::FilePath& cache_file_path,
       const FileOperationCallback& callback) = 0;
 
-  // Gets the cache entry for file corresponding to |resource_id| and runs
+  // Gets the cache entry for file corresponding to |drive_file_path| and runs
   // |callback| with true and the found entry if the entry exists in the cache
   // map. Otherwise, runs |callback| with false.
   // |callback| must not be null.
-  virtual void GetCacheEntryByResourceId(
-      const std::string& resource_id,
-      const GetCacheEntryCallback& callback) = 0;
+  virtual void GetCacheEntryByPath(const base::FilePath& drive_file_path,
+                                   const GetCacheEntryCallback& callback) = 0;
 
   // Reloads the resource metadata from the server.
   virtual void Reload() = 0;
diff --git a/chrome/browser/chromeos/drive/file_system_unittest.cc b/chrome/browser/chromeos/drive/file_system_unittest.cc
index 769200d..8a7ec26 100644
--- a/chrome/browser/chromeos/drive/file_system_unittest.cc
+++ b/chrome/browser/chromeos/drive/file_system_unittest.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/drive/fake_drive_service.h"
 #include "chrome/browser/google_apis/drive_api_parser.h"
 #include "chrome/browser/google_apis/test_util.h"
-#include "chrome/test/base/testing_profile.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -80,7 +79,7 @@
 class FileSystemTest : public testing::Test {
  protected:
   virtual void SetUp() OVERRIDE {
-    profile_.reset(new TestingProfile);
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     pref_service_.reset(new TestingPrefServiceSimple);
     test_util::RegisterDrivePrefs(pref_service_->registry());
 
@@ -99,28 +98,23 @@
                                       fake_drive_service_.get(),
                                       base::MessageLoopProxy::current().get()));
 
-    ASSERT_TRUE(file_util::CreateDirectory(util::GetCacheRootPath(
-        profile_.get()).Append(util::kMetadataDirectory)));
-    ASSERT_TRUE(file_util::CreateDirectory(util::GetCacheRootPath(
-        profile_.get()).Append(util::kCacheFileDirectory)));
-    ASSERT_TRUE(file_util::CreateDirectory(util::GetCacheRootPath(
-        profile_.get()).Append(util::kTemporaryFileDirectory)));
-
     mock_directory_observer_.reset(new MockDirectoryChangeObserver);
 
     SetUpResourceMetadataAndFileSystem();
   }
 
   void SetUpResourceMetadataAndFileSystem() {
+    const base::FilePath metadata_dir = temp_dir_.path().AppendASCII("meta");
+    ASSERT_TRUE(file_util::CreateDirectory(metadata_dir));
     metadata_storage_.reset(new internal::ResourceMetadataStorage(
-        util::GetCacheRootPath(profile_.get()).Append(util::kMetadataDirectory),
-        base::MessageLoopProxy::current().get()));
+        metadata_dir, base::MessageLoopProxy::current().get()));
     ASSERT_TRUE(metadata_storage_->Initialize());
 
+    const base::FilePath cache_dir = temp_dir_.path().AppendASCII("files");
+    ASSERT_TRUE(file_util::CreateDirectory(cache_dir));
     cache_.reset(new internal::FileCache(
         metadata_storage_.get(),
-        util::GetCacheRootPath(profile_.get()).Append(
-            util::kCacheFileDirectory),
+        cache_dir,
         base::MessageLoopProxy::current().get(),
         fake_free_disk_space_getter_.get()));
     ASSERT_TRUE(cache_->Initialize());
@@ -128,6 +122,8 @@
     resource_metadata_.reset(new internal::ResourceMetadata(
         metadata_storage_.get(), base::MessageLoopProxy::current()));
 
+    const base::FilePath temp_file_dir = temp_dir_.path().AppendASCII("tmp");
+    ASSERT_TRUE(file_util::CreateDirectory(temp_file_dir));
     file_system_.reset(new FileSystem(
         pref_service_.get(),
         cache_.get(),
@@ -135,8 +131,7 @@
         scheduler_.get(),
         resource_metadata_.get(),
         base::MessageLoopProxy::current().get(),
-        util::GetCacheRootPath(profile_.get()).Append(
-            util::kTemporaryFileDirectory)));
+        temp_file_dir));
     file_system_->AddObserver(mock_directory_observer_.get());
     file_system_->Initialize();
 
@@ -210,34 +205,34 @@
   // drive/root/Dir1/SubDir2/File3. If |use_up_to_date_timestamp| is true, sets
   // the changestamp to 654321, equal to that of "account_metadata.json" test
   // data, indicating the cache is holding the latest file system info.
-  bool SetUpTestFileSystem(SetUpTestFileSystemParam param) {
+  void SetUpTestFileSystem(SetUpTestFileSystemParam param) {
     // Destroy the existing resource metadata to close DB.
     resource_metadata_.reset();
 
-    base::FilePath metadata_directory =
-        util::GetCacheRootPath(profile_.get()).Append(util::kMetadataDirectory);
+    const base::FilePath metadata_dir = temp_dir_.path().AppendASCII("meta");
+    ASSERT_TRUE(file_util::CreateDirectory(metadata_dir));
     scoped_ptr<internal::ResourceMetadataStorage,
                test_util::DestroyHelperForTests> metadata_storage(
         new internal::ResourceMetadataStorage(
-            metadata_directory, base::MessageLoopProxy::current().get()));
+            metadata_dir, base::MessageLoopProxy::current().get()));
 
     scoped_ptr<internal::ResourceMetadata, test_util::DestroyHelperForTests>
         resource_metadata(new internal::ResourceMetadata(
             metadata_storage_.get(), base::MessageLoopProxy::current()));
 
-    if (resource_metadata->Initialize() != FILE_ERROR_OK)
-      return false;
+    ASSERT_EQ(FILE_ERROR_OK, resource_metadata->Initialize());
 
     const int64 changestamp = param == USE_SERVER_TIMESTAMP ? 654321 : 1;
-    if (resource_metadata->SetLargestChangestamp(changestamp) != FILE_ERROR_OK)
-      return false;
+    ASSERT_EQ(FILE_ERROR_OK,
+              resource_metadata->SetLargestChangestamp(changestamp));
 
     // drive/root
     const std::string root_resource_id =
         fake_drive_service_->GetRootResourceId();
-    if (resource_metadata->AddEntry(util::CreateMyDriveRootEntry(
-            root_resource_id)) != FILE_ERROR_OK)
-      return false;
+    std::string local_id;
+    ASSERT_EQ(FILE_ERROR_OK,
+              resource_metadata->AddEntry(util::CreateMyDriveRootEntry(
+                  root_resource_id), &local_id));
 
     // drive/root/File1
     ResourceEntry file1;
@@ -247,8 +242,7 @@
     file1.mutable_file_specific_info()->set_md5("md5");
     file1.mutable_file_info()->set_is_directory(false);
     file1.mutable_file_info()->set_size(1048576);
-    if (resource_metadata->AddEntry(file1) != FILE_ERROR_OK)
-      return false;
+    ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(file1, &local_id));
 
     // drive/root/Dir1
     ResourceEntry dir1;
@@ -256,8 +250,7 @@
     dir1.set_resource_id("resource_id:Dir1");
     dir1.set_parent_local_id(root_resource_id);
     dir1.mutable_file_info()->set_is_directory(true);
-    if (resource_metadata->AddEntry(dir1) != FILE_ERROR_OK)
-      return false;
+    ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(dir1, &local_id));
 
     // drive/root/Dir1/File2
     ResourceEntry file2;
@@ -267,8 +260,7 @@
     file2.mutable_file_specific_info()->set_md5("md5");
     file2.mutable_file_info()->set_is_directory(false);
     file2.mutable_file_info()->set_size(555);
-    if (resource_metadata->AddEntry(file2) != FILE_ERROR_OK)
-      return false;
+    ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(file2, &local_id));
 
     // drive/root/Dir1/SubDir2
     ResourceEntry dir2;
@@ -276,8 +268,7 @@
     dir2.set_resource_id("resource_id:SubDir2");
     dir2.set_parent_local_id(dir1.resource_id());
     dir2.mutable_file_info()->set_is_directory(true);
-    if (resource_metadata->AddEntry(dir2) != FILE_ERROR_OK)
-      return false;
+    ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(dir2, &local_id));
 
     // drive/root/Dir1/SubDir2/File3
     ResourceEntry file3;
@@ -287,17 +278,14 @@
     file3.mutable_file_specific_info()->set_md5("md5");
     file3.mutable_file_info()->set_is_directory(false);
     file3.mutable_file_info()->set_size(12345);
-    if (resource_metadata->AddEntry(file3) != FILE_ERROR_OK)
-      return false;
+    ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(file3, &local_id));
 
     // Recreate resource metadata.
     SetUpResourceMetadataAndFileSystem();
-
-    return true;
   }
 
   content::TestBrowserThreadBundle thread_bundle_;
-  scoped_ptr<TestingProfile> profile_;
+  base::ScopedTempDir temp_dir_;
   // We don't use TestingProfile::GetPrefs() in favor of having less
   // dependencies to Profile in general.
   scoped_ptr<TestingPrefServiceSimple> pref_service_;
@@ -528,7 +516,7 @@
 }
 
 TEST_F(FileSystemTest, LoadFileSystemFromUpToDateCache) {
-  ASSERT_TRUE(SetUpTestFileSystem(USE_SERVER_TIMESTAMP));
+  ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_SERVER_TIMESTAMP));
 
   // Kicks loading of cached file system and query for server update.
   EXPECT_TRUE(ReadDirectoryByPathSync(util::GetDriveMyDriveRootPath()));
@@ -548,7 +536,7 @@
 }
 
 TEST_F(FileSystemTest, LoadFileSystemFromCacheWhileOffline) {
-  ASSERT_TRUE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
+  ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
 
   // Make GetResourceList fail for simulating offline situation. This will
   // leave the file system "loaded from cache, but not synced with server"
@@ -597,7 +585,7 @@
 
 TEST_F(FileSystemTest, ReadDirectoryWhileRefreshing) {
   // Enter the "refreshing" state so the fast fetch will be performed.
-  ASSERT_TRUE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
+  ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
   file_system_->CheckForUpdates();
 
   // The list of resources in "drive/root/Dir1" should be fetched.
@@ -610,7 +598,7 @@
 
 TEST_F(FileSystemTest, GetResourceEntryExistingWhileRefreshing) {
   // Enter the "refreshing" state.
-  ASSERT_TRUE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
+  ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
   file_system_->CheckForUpdates();
 
   // If an entry is already found in local metadata, no directory fetch happens.
@@ -621,7 +609,7 @@
 
 TEST_F(FileSystemTest, GetResourceEntryNonExistentWhileRefreshing) {
   // Enter the "refreshing" state so the fast fetch will be performed.
-  ASSERT_TRUE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
+  ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
   file_system_->CheckForUpdates();
 
   // If an entry is not found, parent directory's resource list is fetched.
diff --git a/chrome/browser/chromeos/drive/job_list.cc b/chrome/browser/chromeos/drive/job_list.cc
index 0ae803c..04edb3b 100644
--- a/chrome/browser/chromeos/drive/job_list.cc
+++ b/chrome/browser/chromeos/drive/job_list.cc
@@ -26,6 +26,10 @@
       return "TYPE_GET_CHANGE_LIST";
     case TYPE_CONTINUE_GET_RESOURCE_LIST:
       return "TYPE_CONTINUE_GET_RESOURCE_LIST";
+    case TYPE_GET_REMAINING_CHANGE_LIST:
+      return "TYPE_GET_REMAINING_CHANGE_LIST";
+    case TYPE_GET_REMAINING_FILE_LIST:
+      return "TYPE_GET_REMAINING_FILE_LIST";
     case TYPE_GET_RESOURCE_ENTRY:
       return "TYPE_GET_RESOURCE_ENTRY";
     case TYPE_GET_SHARE_URL:
@@ -118,6 +122,8 @@
     case TYPE_SEARCH:
     case TYPE_GET_CHANGE_LIST:
     case TYPE_CONTINUE_GET_RESOURCE_LIST:
+    case TYPE_GET_REMAINING_CHANGE_LIST:
+    case TYPE_GET_REMAINING_FILE_LIST:
     case TYPE_GET_RESOURCE_ENTRY:
     case TYPE_GET_SHARE_URL:
     case TYPE_DELETE_RESOURCE:
diff --git a/chrome/browser/chromeos/drive/job_list.h b/chrome/browser/chromeos/drive/job_list.h
index 8318ec4..3ad4203 100644
--- a/chrome/browser/chromeos/drive/job_list.h
+++ b/chrome/browser/chromeos/drive/job_list.h
@@ -22,6 +22,8 @@
   TYPE_SEARCH,
   TYPE_GET_CHANGE_LIST,
   TYPE_CONTINUE_GET_RESOURCE_LIST,
+  TYPE_GET_REMAINING_CHANGE_LIST,
+  TYPE_GET_REMAINING_FILE_LIST,
   TYPE_GET_RESOURCE_ENTRY,
   TYPE_GET_SHARE_URL,
   TYPE_DELETE_RESOURCE,
diff --git a/chrome/browser/chromeos/drive/job_scheduler.cc b/chrome/browser/chromeos/drive/job_scheduler.cc
index 079139d..b856d38 100644
--- a/chrome/browser/chromeos/drive/job_scheduler.cc
+++ b/chrome/browser/chromeos/drive/job_scheduler.cc
@@ -192,7 +192,7 @@
 }
 
 void JobScheduler::GetAboutResource(
-    const google_apis::GetAboutResourceCallback& callback) {
+    const google_apis::AboutResourceCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
@@ -208,8 +208,7 @@
   StartJob(new_job);
 }
 
-void JobScheduler::GetAppList(
-    const google_apis::GetAppListCallback& callback) {
+void JobScheduler::GetAppList(const google_apis::AppListCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
@@ -319,6 +318,44 @@
   StartJob(new_job);
 }
 
+void JobScheduler::GetRemainingChangeList(
+    const std::string& page_token,
+    const google_apis::GetResourceListCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!callback.is_null());
+
+  JobEntry* new_job = CreateNewJob(TYPE_GET_REMAINING_CHANGE_LIST);
+  new_job->task = base::Bind(
+      &DriveServiceInterface::GetRemainingChangeList,
+      base::Unretained(drive_service_),
+      page_token,
+      base::Bind(&JobScheduler::OnGetResourceListJobDone,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 new_job->job_info.job_id,
+                 callback));
+  new_job->abort_callback = google_apis::CreateErrorRunCallback(callback);
+  StartJob(new_job);
+}
+
+void JobScheduler::GetRemainingFileList(
+    const std::string& page_token,
+    const google_apis::GetResourceListCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!callback.is_null());
+
+  JobEntry* new_job = CreateNewJob(TYPE_GET_REMAINING_FILE_LIST);
+  new_job->task = base::Bind(
+      &DriveServiceInterface::GetRemainingFileList,
+      base::Unretained(drive_service_),
+      page_token,
+      base::Bind(&JobScheduler::OnGetResourceListJobDone,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 new_job->job_info.job_id,
+                 callback));
+  new_job->abort_callback = google_apis::CreateErrorRunCallback(callback);
+  StartJob(new_job);
+}
+
 void JobScheduler::GetResourceEntry(
     const std::string& resource_id,
     const ClientContext& context,
@@ -557,6 +594,7 @@
 
 JobID JobScheduler::DownloadFile(
     const base::FilePath& virtual_path,
+    int64 expected_file_size,
     const base::FilePath& local_cache_path,
     const std::string& resource_id,
     const ClientContext& context,
@@ -566,6 +604,7 @@
 
   JobEntry* new_job = CreateNewJob(TYPE_DOWNLOAD_FILE);
   new_job->job_info.file_path = virtual_path;
+  new_job->job_info.num_total_bytes = expected_file_size;
   new_job->context = context;
   new_job->task = base::Bind(
       &DriveServiceInterface::DownloadFile,
@@ -904,7 +943,7 @@
 
 void JobScheduler::OnGetAboutResourceJobDone(
     JobID job_id,
-    const google_apis::GetAboutResourceCallback& callback,
+    const google_apis::AboutResourceCallback& callback,
     google_apis::GDataErrorCode error,
     scoped_ptr<google_apis::AboutResource> about_resource) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -928,7 +967,7 @@
 
 void JobScheduler::OnGetAppListJobDone(
     JobID job_id,
-    const google_apis::GetAppListCallback& callback,
+    const google_apis::AppListCallback& callback,
     google_apis::GDataErrorCode error,
     scoped_ptr<google_apis::AppList> app_list) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -1026,7 +1065,8 @@
   DCHECK(job_entry);
 
   job_entry->job_info.num_completed_bytes = progress;
-  job_entry->job_info.num_total_bytes = total;
+  if (total != -1)
+    job_entry->job_info.num_total_bytes = total;
   NotifyJobUpdated(job_entry->job_info);
 }
 
@@ -1050,6 +1090,8 @@
     case TYPE_SEARCH:
     case TYPE_GET_CHANGE_LIST:
     case TYPE_CONTINUE_GET_RESOURCE_LIST:
+    case TYPE_GET_REMAINING_CHANGE_LIST:
+    case TYPE_GET_REMAINING_FILE_LIST:
     case TYPE_GET_RESOURCE_ENTRY:
     case TYPE_GET_SHARE_URL:
     case TYPE_DELETE_RESOURCE:
diff --git a/chrome/browser/chromeos/drive/job_scheduler.h b/chrome/browser/chromeos/drive/job_scheduler.h
index 9d25786..a93735e 100644
--- a/chrome/browser/chromeos/drive/job_scheduler.h
+++ b/chrome/browser/chromeos/drive/job_scheduler.h
@@ -65,11 +65,11 @@
 
   // Adds a GetAppList operation to the queue.
   // |callback| must not be null.
-  void GetAppList(const google_apis::GetAppListCallback& callback);
+  void GetAppList(const google_apis::AppListCallback& callback);
 
   // Adds a GetAboutResource operation to the queue.
   // |callback| must not be null.
-  void GetAboutResource(const google_apis::GetAboutResourceCallback& callback);
+  void GetAboutResource(const google_apis::AboutResourceCallback& callback);
 
   // Adds a GetAllResourceList operation to the queue.
   // |callback| must not be null.
@@ -97,6 +97,18 @@
       const GURL& next_url,
       const google_apis::GetResourceListCallback& callback);
 
+  // Adds GetRemainingChangeList operation to the queue.
+  // |callback| must not be null.
+  void GetRemainingChangeList(
+      const std::string& page_token,
+      const google_apis::GetResourceListCallback& callback);
+
+  // Adds GetRemainingFileList operation to the queue.
+  // |callback| must not be null.
+  void GetRemainingFileList(
+      const std::string& page_token,
+      const google_apis::GetResourceListCallback& callback);
+
   // Adds a GetResourceEntry operation to the queue.
   void GetResourceEntry(const std::string& resource_id,
                         const ClientContext& context,
@@ -160,8 +172,12 @@
                        const google_apis::GetResourceEntryCallback& callback);
 
   // Adds a DownloadFile operation to the queue.
+  // The first two arguments |virtual_path| and |expected_file_size| are used
+  // only for filling JobInfo for the operation so that observers can get the
+  // detail. The actual operation never refers these values.
   JobID DownloadFile(
       const base::FilePath& virtual_path,
+      int64 expected_file_size,
       const base::FilePath& local_cache_path,
       const std::string& resource_id,
       const ClientContext& context,
@@ -271,10 +287,10 @@
       google_apis::GDataErrorCode error,
       scoped_ptr<google_apis::ResourceEntry> entry);
 
-  // Callback for job finishing with a GetAboutResourceCallback.
+  // Callback for job finishing with a AboutResourceCallback.
   void OnGetAboutResourceJobDone(
       JobID job_id,
-      const google_apis::GetAboutResourceCallback& callback,
+      const google_apis::AboutResourceCallback& callback,
       google_apis::GDataErrorCode error,
       scoped_ptr<google_apis::AboutResource> about_resource);
 
@@ -285,10 +301,10 @@
       google_apis::GDataErrorCode error,
       const GURL& share_url);
 
-  // Callback for job finishing with a GetAppListCallback.
+  // Callback for job finishing with a AppListCallback.
   void OnGetAppListJobDone(
       JobID job_id,
-      const google_apis::GetAppListCallback& callback,
+      const google_apis::AppListCallback& callback,
       google_apis::GDataErrorCode error,
       scoped_ptr<google_apis::AppList> app_list);
 
diff --git a/chrome/browser/chromeos/drive/job_scheduler_unittest.cc b/chrome/browser/chromeos/drive/job_scheduler_unittest.cc
index 920918a..2b06f85 100644
--- a/chrome/browser/chromeos/drive/job_scheduler_unittest.cc
+++ b/chrome/browser/chromeos/drive/job_scheduler_unittest.cc
@@ -25,6 +25,9 @@
 
 namespace {
 
+// Dummy value passed for the |expected_file_size| parameter of DownloadFile().
+const int64 kDummyDownloadFileSize = 0;
+
 void CopyResourceIdFromGetResourceEntryCallback(
     std::vector<std::string>* id_list_out,
     const std::string& requested_id,
@@ -288,6 +291,75 @@
   ASSERT_TRUE(resource_list);
 }
 
+TEST_F(JobSchedulerTest, GetRemainingChangeList) {
+  ConnectToWifi();
+  fake_drive_service_->set_default_max_results(2);
+
+  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
+  scoped_ptr<google_apis::ResourceList> resource_list;
+
+  scheduler_->GetAllResourceList(
+      google_apis::test_util::CreateCopyResultCallback(
+          &error, &resource_list));
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
+  ASSERT_TRUE(resource_list);
+
+  const google_apis::Link* next_link =
+      resource_list->GetLinkByType(google_apis::Link::LINK_NEXT);
+  ASSERT_TRUE(next_link);
+  // Keep the next url before releasing the |resource_list|.
+  GURL next_url(next_link->href());
+
+  error = google_apis::GDATA_OTHER_ERROR;
+  resource_list.reset();
+
+  scheduler_->GetRemainingChangeList(
+      next_url.spec(),
+      google_apis::test_util::CreateCopyResultCallback(
+          &error, &resource_list));
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
+  ASSERT_TRUE(resource_list);
+}
+
+TEST_F(JobSchedulerTest, GetRemainingFileList) {
+  ConnectToWifi();
+  fake_drive_service_->set_default_max_results(2);
+
+  google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
+  scoped_ptr<google_apis::ResourceList> resource_list;
+
+  scheduler_->GetResourceListInDirectory(
+      fake_drive_service_->GetRootResourceId(),
+      google_apis::test_util::CreateCopyResultCallback(
+          &error, &resource_list));
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
+  ASSERT_TRUE(resource_list);
+
+  const google_apis::Link* next_link =
+      resource_list->GetLinkByType(google_apis::Link::LINK_NEXT);
+  ASSERT_TRUE(next_link);
+  // Keep the next url before releasing the |resource_list|.
+  GURL next_url(next_link->href());
+
+  error = google_apis::GDATA_OTHER_ERROR;
+  resource_list.reset();
+
+  scheduler_->GetRemainingFileList(
+      next_url.spec(),
+      google_apis::test_util::CreateCopyResultCallback(
+          &error, &resource_list));
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_EQ(google_apis::HTTP_SUCCESS, error);
+  ASSERT_TRUE(resource_list);
+}
+
 TEST_F(JobSchedulerTest, GetResourceEntry) {
   ConnectToWifi();
 
@@ -553,6 +625,7 @@
   base::FilePath output_file_path;
   scheduler_->DownloadFile(
       base::FilePath::FromUTF8Unsafe("drive/whatever.txt"),  // virtual path
+      kDummyDownloadFileSize,
       kOutputFilePath,
       "file:2_file_resource_id",
       ClientContext(BACKGROUND),
@@ -605,6 +678,7 @@
   base::FilePath output_file_path;
   scheduler_->DownloadFile(
       base::FilePath::FromUTF8Unsafe("drive/whatever.txt"),  // virtual path
+      kDummyDownloadFileSize,
       kOutputFilePath,
       "file:2_file_resource_id",
       ClientContext(BACKGROUND),
@@ -657,6 +731,7 @@
   base::FilePath output_file_path;
   scheduler_->DownloadFile(
       base::FilePath::FromUTF8Unsafe("drive/whatever.txt"),  // virtual path
+      kDummyDownloadFileSize,
       kOutputFilePath,
       "file:2_file_resource_id",
       ClientContext(BACKGROUND),
@@ -701,6 +776,7 @@
   base::FilePath output_file_path;
   scheduler_->DownloadFile(
       base::FilePath::FromUTF8Unsafe("drive/whatever.txt"),  // virtual path
+      kDummyDownloadFileSize,
       kOutputFilePath,
       "file:2_file_resource_id",
       ClientContext(BACKGROUND),
@@ -765,6 +841,7 @@
   expected_types.insert(TYPE_DOWNLOAD_FILE);
   scheduler_->DownloadFile(
       base::FilePath::FromUTF8Unsafe("drive/whatever.txt"),  // virtual path
+      kDummyDownloadFileSize,
       temp_dir.path().AppendASCII("whatever.txt"),
       "file:2_file_resource_id",
       ClientContext(BACKGROUND),
@@ -860,6 +937,7 @@
   // Download job.
   scheduler_->DownloadFile(
       base::FilePath::FromUTF8Unsafe("drive/whatever.txt"),  // virtual path
+      kDummyDownloadFileSize,
       temp_dir.path().AppendASCII("whatever.txt"),
       "file:2_file_resource_id",
       ClientContext(BACKGROUND),
diff --git a/chrome/browser/chromeos/drive/remove_stale_cache_files_unittest.cc b/chrome/browser/chromeos/drive/remove_stale_cache_files_unittest.cc
index bca7672..77d823e 100644
--- a/chrome/browser/chromeos/drive/remove_stale_cache_files_unittest.cc
+++ b/chrome/browser/chromeos/drive/remove_stale_cache_files_unittest.cc
@@ -106,10 +106,11 @@
   EXPECT_EQ(FILE_ERROR_OK, cache_->MarkDirty(resource_id_2));
 
   ResourceEntry entry;
+  std::string local_id;
   entry.set_resource_id(resource_id_2);
   entry.mutable_file_specific_info()->set_md5(md5_2_metadata);
   entry.set_parent_local_id(util::kDriveGrandRootSpecialResourceId);
-  resource_metadata_->AddEntry(entry);
+  resource_metadata_->AddEntry(entry, &local_id);
 
   // Remove stale cache files.
   RemoveStaleCacheFiles(cache_.get(), resource_metadata_.get());
diff --git a/chrome/browser/chromeos/drive/resource_entry_conversion.cc b/chrome/browser/chromeos/drive/resource_entry_conversion.cc
index 1b7b3c4..36d0ad0 100644
--- a/chrome/browser/chromeos/drive/resource_entry_conversion.cc
+++ b/chrome/browser/chromeos/drive/resource_entry_conversion.cc
@@ -33,8 +33,10 @@
 }  // namespace
 
 bool ConvertToResourceEntry(const google_apis::ResourceEntry& input,
-                            ResourceEntry* output) {
-  DCHECK(output);
+                            ResourceEntry* out_entry,
+                            std::string* out_parent_resource_id) {
+  DCHECK(out_entry);
+  DCHECK(out_parent_resource_id);
 
   ResourceEntry converted;
 
@@ -46,20 +48,20 @@
   converted.set_base_name(util::NormalizeFileName(converted.title()));
   converted.set_resource_id(input.resource_id());
 
-  // Sets parent Resource ID. On drive.google.com, a file can have multiple
+  // Gets parent Resource ID. On drive.google.com, a file can have multiple
   // parents or no parent, but we are forcing a tree-shaped structure (i.e. no
   // multi-parent or zero-parent entries). Therefore the first found "parent" is
   // used for the entry and if the entry has no parent, we assign a special ID
   // which represents no-parent entries. Tracked in http://crbug.com/158904.
+  std::string parent_resource_id;
   const google_apis::Link* parent_link =
       input.GetLinkByType(google_apis::Link::LINK_PARENT);
-  if (parent_link) {
-    converted.set_parent_local_id(util::ExtractResourceIdFromUrl(
-        parent_link->href()));
-  }
+  if (parent_link)
+    parent_resource_id = util::ExtractResourceIdFromUrl(parent_link->href());
+
   // Apply mapping from an empty parent to the special dummy directory.
-  if (converted.parent_local_id().empty())
-    converted.set_parent_local_id(util::kDriveOtherDirSpecialResourceId);
+  if (parent_resource_id.empty())
+    parent_resource_id = util::kDriveOtherDirSpecialResourceId;
 
   converted.set_deleted(input.deleted());
   converted.set_shared_with_me(HasSharedWithMeLabel(input));
@@ -122,7 +124,8 @@
       return false;
   }
 
-  output->Swap(&converted);
+  out_entry->Swap(&converted);
+  swap(*out_parent_resource_id, parent_resource_id);
   return true;
 }
 
diff --git a/chrome/browser/chromeos/drive/resource_entry_conversion.h b/chrome/browser/chromeos/drive/resource_entry_conversion.h
index 7de8ef7..56607b9 100644
--- a/chrome/browser/chromeos/drive/resource_entry_conversion.h
+++ b/chrome/browser/chromeos/drive/resource_entry_conversion.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_CHROMEOS_DRIVE_RESOURCE_ENTRY_CONVERSION_H_
 #define CHROME_BROWSER_CHROMEOS_DRIVE_RESOURCE_ENTRY_CONVERSION_H_
 
+#include <string>
+
 namespace base {
 struct PlatformFileInfo;
 }
@@ -18,8 +20,9 @@
 class ResourceEntry;
 
 // Converts a google_apis::ResourceEntry into a drive::ResourceEntry.
-// If the conversion succeeded, return true and sets the result to |output|.
-// If failed, it returns false and keeps |*output| untouched.
+// If the conversion succeeded, return true and sets the result to |out_entry|.
+// |out_parent_resource_id| will be set to the resource ID of the parent entry.
+// If failed, it returns false and keeps output arguments untouched.
 //
 // Every entry is guaranteed to have one parent resource ID in ResourceMetadata.
 // This requirement is needed to represent contents in Drive as a file system
@@ -31,7 +34,8 @@
 // 2) Entries with multiple parents are allowed on drive.google.com. For these
 // entries, the first parent is chosen.
 bool ConvertToResourceEntry(const google_apis::ResourceEntry& input,
-                            ResourceEntry* output);
+                            ResourceEntry* out_entry,
+                            std::string* out_parent_resource_id);
 
 // Converts the resource entry to the platform file info.
 void ConvertResourceEntryToPlatformFileInfo(const ResourceEntry& entry,
diff --git a/chrome/browser/chromeos/drive/resource_entry_conversion_unittest.cc b/chrome/browser/chromeos/drive/resource_entry_conversion_unittest.cc
index ff65313..b944e48 100644
--- a/chrome/browser/chromeos/drive/resource_entry_conversion_unittest.cc
+++ b/chrome/browser/chromeos/drive/resource_entry_conversion_unittest.cc
@@ -25,12 +25,14 @@
   ASSERT_TRUE(gdata_resource_entry.get());
 
   ResourceEntry entry;
-  EXPECT_TRUE(ConvertToResourceEntry(*gdata_resource_entry, &entry));
+  std::string parent_resource_id;
+  EXPECT_TRUE(ConvertToResourceEntry(*gdata_resource_entry, &entry,
+                                     &parent_resource_id));
 
   EXPECT_EQ("File 1.mp3", entry.title());
   EXPECT_EQ("File 1.mp3", entry.base_name());
   EXPECT_EQ("file:2_file_resource_id", entry.resource_id());
-  EXPECT_EQ(util::kDriveOtherDirSpecialResourceId, entry.parent_local_id());
+  EXPECT_EQ(util::kDriveOtherDirSpecialResourceId, parent_resource_id);
 
   EXPECT_FALSE(entry.deleted());
   EXPECT_FALSE(entry.shared_with_me());
@@ -100,13 +102,15 @@
   ASSERT_TRUE(gdata_resource_entry.get());
 
   ResourceEntry entry;
-  EXPECT_TRUE(ConvertToResourceEntry(*gdata_resource_entry, &entry));
+  std::string parent_resource_id;
+  EXPECT_TRUE(ConvertToResourceEntry(*gdata_resource_entry, &entry,
+                                     &parent_resource_id));
 
   EXPECT_EQ("Document 1", entry.title());
   EXPECT_EQ("Document 1.gdoc", entry.base_name());  // The suffix added.
   EXPECT_EQ(".gdoc", entry.file_specific_info().document_extension());
   EXPECT_EQ("document:5_document_resource_id", entry.resource_id());
-  EXPECT_EQ(util::kDriveOtherDirSpecialResourceId, entry.parent_local_id());
+  EXPECT_EQ(util::kDriveOtherDirSpecialResourceId, parent_resource_id);
 
   EXPECT_FALSE(entry.deleted());
   EXPECT_FALSE(entry.shared_with_me());
@@ -180,14 +184,16 @@
   ASSERT_TRUE(gdata_resource_entry.get());
 
   ResourceEntry entry;
-  EXPECT_TRUE(ConvertToResourceEntry(*gdata_resource_entry, &entry));
+  std::string parent_resource_id;
+  EXPECT_TRUE(ConvertToResourceEntry(*gdata_resource_entry, &entry,
+                                     &parent_resource_id));
 
   EXPECT_EQ("Sub Directory Folder", entry.title());
   EXPECT_EQ("Sub Directory Folder", entry.base_name());
   EXPECT_EQ("folder:sub_dir_folder_resource_id", entry.resource_id());
   // The parent resource ID should be obtained as this is a sub directory
   // under a non-root directory.
-  EXPECT_EQ("folder:1_folder_resource_id", entry.parent_local_id());
+  EXPECT_EQ("folder:1_folder_resource_id", parent_resource_id);
 
   EXPECT_FALSE(entry.deleted());
   EXPECT_FALSE(entry.shared_with_me());
@@ -251,12 +257,14 @@
   ASSERT_TRUE(gdata_resource_entry.get());
 
   ResourceEntry entry;
-  EXPECT_TRUE(ConvertToResourceEntry(*gdata_resource_entry, &entry));
+  std::string parent_resource_id;
+  EXPECT_TRUE(ConvertToResourceEntry(*gdata_resource_entry, &entry,
+                                     &parent_resource_id));
 
   EXPECT_EQ("Deleted document", entry.title());
   EXPECT_EQ("Deleted document.gdoc", entry.base_name());
   EXPECT_EQ("document:deleted_in_root_id", entry.resource_id());
-  EXPECT_EQ(util::kDriveOtherDirSpecialResourceId, entry.parent_local_id());
+  EXPECT_EQ(util::kDriveOtherDirSpecialResourceId, parent_resource_id);
 
   EXPECT_TRUE(entry.deleted());  // The document was deleted.
   EXPECT_FALSE(entry.shared_with_me());
@@ -328,8 +336,9 @@
   ASSERT_TRUE(gdata_resource_entry.get());
 
   ResourceEntry entry;
-  EXPECT_TRUE(ConvertToResourceEntry(*gdata_resource_entry, &entry));
-
+  std::string parent_resource_id;
+  EXPECT_TRUE(ConvertToResourceEntry(*gdata_resource_entry, &entry,
+                                     &parent_resource_id));
   EXPECT_TRUE(entry.shared_with_me());
 }
 
diff --git a/chrome/browser/chromeos/drive/resource_metadata.cc b/chrome/browser/chromeos/drive/resource_metadata.cc
index b942b0e..2b9162b 100644
--- a/chrome/browser/chromeos/drive/resource_metadata.cc
+++ b/chrome/browser/chromeos/drive/resource_metadata.cc
@@ -85,17 +85,6 @@
       base::Bind(&RunFileMoveCallback, callback, base::Owned(file_path)));
 }
 
-FileError AddEntryWithFilePath(internal::ResourceMetadata* metadata,
-                               const ResourceEntry& entry,
-                               base::FilePath* out_file_path) {
-  DCHECK(metadata);
-  DCHECK(out_file_path);
-  FileError error = metadata->AddEntry(entry);
-  if (error == FILE_ERROR_OK)
-    *out_file_path = metadata->GetFilePath(entry.resource_id());
-  return error;
-}
-
 }  // namespace
 
 EntryInfoResult::EntryInfoResult() : error(FILE_ERROR_FAILED) {
@@ -245,25 +234,18 @@
       FILE_ERROR_OK : FILE_ERROR_FAILED;
 }
 
-void ResourceMetadata::AddEntryOnUIThread(const ResourceEntry& entry,
-                                          const FileMoveCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(!callback.is_null());
-
-  PostFileMoveTask(
-      blocking_task_runner_.get(),
-      base::Bind(&AddEntryWithFilePath, base::Unretained(this), entry),
-      callback);
-}
-
-FileError ResourceMetadata::AddEntry(const ResourceEntry& entry) {
+FileError ResourceMetadata::AddEntry(const ResourceEntry& entry,
+                                     std::string* out_id) {
   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
 
   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
     return FILE_ERROR_NO_LOCAL_SPACE;
 
-  ResourceEntry existing_entry;
-  if (storage_->GetEntry(entry.resource_id(), &existing_entry))
+  // Multiple entries with the same resource ID should not be present.
+  std::string existing_entry_id;
+  if (!entry.resource_id().empty() &&
+      GetIdByResourceId(entry.resource_id(),
+                        &existing_entry_id) == FILE_ERROR_OK)
     return FILE_ERROR_EXISTS;
 
   ResourceEntry parent;
@@ -271,13 +253,13 @@
       !parent.file_info().is_directory())
     return FILE_ERROR_NOT_FOUND;
 
-  // TODO(hashimoto): Generate local ID here and pass it to the caller.
-  // crbug.com/26051
-  const std::string& id = entry.resource_id();
+  // TODO(hashimoto): Generate local ID here. crbug.com/26051
+  const std::string local_id = entry.resource_id();
 
-  if (!PutEntryUnderDirectory(id, entry))
+  if (!PutEntryUnderDirectory(local_id, entry))
     return FILE_ERROR_FAILED;
 
+  *out_id = local_id;
   return FILE_ERROR_OK;
 }
 
@@ -411,6 +393,8 @@
 FileError ResourceMetadata::RefreshEntry(const std::string& id,
                                          const ResourceEntry& entry) {
   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
+  // TODO(hashimoto): Return an error if the operation will result in having
+  // multiple entries with the same resource ID.
 
   if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path()))
     return FILE_ERROR_NO_LOCAL_SPACE;
@@ -455,9 +439,8 @@
   }
 }
 
-std::string ResourceMetadata::GetChildResourceId(
-    const std::string& parent_local_id,
-    const std::string& base_name) {
+std::string ResourceMetadata::GetChildId(const std::string& parent_local_id,
+                                         const std::string& base_name) {
   DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
   return storage_->GetChild(parent_local_id, base_name);
 }
@@ -503,6 +486,19 @@
   return FILE_ERROR_OK;
 }
 
+FileError ResourceMetadata::GetIdByResourceId(const std::string& resource_id,
+                                              std::string* out_local_id) {
+  DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
+
+  // TODO(hashimoto): Implement the real resource ID to local ID look up.
+  // crbug.com/260514
+  ResourceEntry entry;
+  FileError error = GetResourceEntryById(resource_id, &entry);
+  if (error == FILE_ERROR_OK)
+    *out_local_id = resource_id;
+  return error;
+}
+
 void ResourceMetadata::GetResourceEntryPairByPathsOnUIThread(
     const base::FilePath& first_path,
     const base::FilePath& second_path,
diff --git a/chrome/browser/chromeos/drive/resource_metadata.h b/chrome/browser/chromeos/drive/resource_metadata.h
index 06c9bea..06b5040 100644
--- a/chrome/browser/chromeos/drive/resource_metadata.h
+++ b/chrome/browser/chromeos/drive/resource_metadata.h
@@ -110,16 +110,8 @@
   // Synchronous version of SetLargestChangestampOnUIThread.
   FileError SetLargestChangestamp(int64 value);
 
-  // Runs AddEntry() on blocking pool. Upon completion, the |callback| will be
-  // called with the new file path.
-  // |callback| must not be null.
-  // Must be called on the UI thread.
-  void AddEntryOnUIThread(const ResourceEntry& entry,
-                          const FileMoveCallback& callback);
-
-  // Adds |entry| to the metadata tree based on its parent_local_id
-  // synchronously.
-  FileError AddEntry(const ResourceEntry& entry);
+  // Adds |entry| to the metadata tree based on its parent_local_id.
+  FileError AddEntry(const ResourceEntry& entry, std::string* out_id);
 
   // Removes entry with |id| from its parent.
   FileError RemoveEntry(const std::string& id);
@@ -172,11 +164,11 @@
   void GetSubDirectoriesRecursively(const std::string& id,
                                     std::set<base::FilePath>* sub_directories);
 
-  // Returns the resource id of the resource named |base_name| directly under
+  // Returns the id of the resource named |base_name| directly under
   // the directory with |parent_local_id|.
   // If not found, empty string will be returned.
-  std::string GetChildResourceId(const std::string& parent_local_id,
-                                 const std::string& base_name);
+  std::string GetChildId(const std::string& parent_local_id,
+                         const std::string& base_name);
 
   // Returns an object to iterate over entries.
   scoped_ptr<Iterator> GetIterator();
@@ -187,6 +179,10 @@
   // Returns ID of the entry at the given path.
   FileError GetIdByPath(const base::FilePath& file_path, std::string* out_id);
 
+  // Returns the local ID associated with the given resource ID.
+  FileError GetIdByResourceId(const std::string& resource_id,
+                              std::string* out_local_id);
+
  private:
   // Note: Use Destroy() to delete this object.
   ~ResourceMetadata();
diff --git a/chrome/browser/chromeos/drive/resource_metadata_unittest.cc b/chrome/browser/chromeos/drive/resource_metadata_unittest.cc
index 851c75b..03d73b7 100644
--- a/chrome/browser/chromeos/drive/resource_metadata_unittest.cc
+++ b/chrome/browser/chromeos/drive/resource_metadata_unittest.cc
@@ -46,7 +46,7 @@
                                    const std::string& parent_local_id) {
   ResourceEntry entry;
   entry.set_title(title);
-  entry.set_resource_id("resource_id:" + title);
+  entry.set_resource_id("id:" + title);
   entry.set_parent_local_id(parent_local_id);
   entry.mutable_file_info()->set_is_directory(true);
   entry.mutable_directory_specific_info()->set_changestamp(kTestChangestamp);
@@ -58,7 +58,7 @@
                               const std::string& parent_local_id) {
   ResourceEntry entry;
   entry.set_title(title);
-  entry.set_resource_id("resource_id:" + title);
+  entry.set_resource_id("id:" + title);
   entry.set_parent_local_id(parent_local_id);
   entry.mutable_file_info()->set_is_directory(false);
   entry.mutable_file_info()->set_size(1024);
@@ -79,32 +79,33 @@
 // drive/root/dir1/dir3/file10
 void SetUpEntries(ResourceMetadata* resource_metadata) {
   // Create mydrive root directory.
+  std::string local_id;
   ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
-      util::CreateMyDriveRootEntry(kTestRootResourceId)));
+      util::CreateMyDriveRootEntry(kTestRootResourceId), &local_id));
 
   ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
-      CreateDirectoryEntry("dir1", kTestRootResourceId)));
+      CreateDirectoryEntry("dir1", kTestRootResourceId), &local_id));
   ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
-      CreateDirectoryEntry("dir2", kTestRootResourceId)));
+      CreateDirectoryEntry("dir2", kTestRootResourceId), &local_id));
 
   ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
-      CreateDirectoryEntry("dir3", "resource_id:dir1")));
+      CreateDirectoryEntry("dir3", "id:dir1"), &local_id));
   ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
-      CreateFileEntry("file4", "resource_id:dir1")));
+      CreateFileEntry("file4", "id:dir1"), &local_id));
   ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
-      CreateFileEntry("file5", "resource_id:dir1")));
+      CreateFileEntry("file5", "id:dir1"), &local_id));
 
   ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
-      CreateFileEntry("file6", "resource_id:dir2")));
+      CreateFileEntry("file6", "id:dir2"), &local_id));
   ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
-      CreateFileEntry("file7", "resource_id:dir2")));
+      CreateFileEntry("file7", "id:dir2"), &local_id));
   ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
-      CreateFileEntry("file8", "resource_id:dir2")));
+      CreateFileEntry("file8", "id:dir2"), &local_id));
 
   ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
-      CreateFileEntry("file9", "resource_id:dir3")));
+      CreateFileEntry("file9", "id:dir3"), &local_id));
   ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(
-      CreateFileEntry("file10", "resource_id:dir3")));
+      CreateFileEntry("file10", "id:dir3"), &local_id));
 
   ASSERT_EQ(FILE_ERROR_OK,
             resource_metadata->SetLargestChangestamp(kTestChangestamp));
@@ -232,7 +233,7 @@
   FileError error = FILE_ERROR_FAILED;
   scoped_ptr<ResourceEntry> entry;
   resource_metadata_->GetResourceEntryByIdOnUIThread(
-      "resource_id:file4",
+      "id:file4",
       google_apis::test_util::CreateCopyResultCallback(&error, &entry));
   test_util::RunBlockingPoolTask();
   EXPECT_EQ(FILE_ERROR_OK, error);
@@ -399,50 +400,6 @@
   ASSERT_FALSE(pair_result->second.entry.get());
 }
 
-TEST_F(ResourceMetadataTestOnUIThread, AddEntry) {
-  FileError error = FILE_ERROR_FAILED;
-  base::FilePath drive_file_path;
-
-  // Add a file to dir3.
-  ResourceEntry file_entry = CreateFileEntry("file100", "resource_id:dir3");
-  resource_metadata_->AddEntryOnUIThread(
-      file_entry,
-      google_apis::test_util::CreateCopyResultCallback(
-          &error, &drive_file_path));
-  test_util::RunBlockingPoolTask();
-  EXPECT_EQ(FILE_ERROR_OK, error);
-  EXPECT_EQ(base::FilePath::FromUTF8Unsafe("drive/root/dir1/dir3/file100"),
-            drive_file_path);
-
-  // Add a directory.
-  ResourceEntry dir_entry = CreateDirectoryEntry("dir101", "resource_id:dir1");
-  resource_metadata_->AddEntryOnUIThread(
-      dir_entry,
-      google_apis::test_util::CreateCopyResultCallback(
-          &error, &drive_file_path));
-  test_util::RunBlockingPoolTask();
-  EXPECT_EQ(FILE_ERROR_OK, error);
-  EXPECT_EQ(base::FilePath::FromUTF8Unsafe("drive/root/dir1/dir101"),
-            drive_file_path);
-
-  // Add to an invalid parent.
-  ResourceEntry file_entry3 = CreateFileEntry("file103", "resource_id:invalid");
-  resource_metadata_->AddEntryOnUIThread(
-      file_entry3,
-      google_apis::test_util::CreateCopyResultCallback(
-          &error, &drive_file_path));
-  test_util::RunBlockingPoolTask();
-  EXPECT_EQ(FILE_ERROR_NOT_FOUND, error);
-
-  // Add an existing file.
-  resource_metadata_->AddEntryOnUIThread(
-      file_entry,
-      google_apis::test_util::CreateCopyResultCallback(
-          &error, &drive_file_path));
-  test_util::RunBlockingPoolTask();
-  EXPECT_EQ(FILE_ERROR_EXISTS, error);
-}
-
 TEST_F(ResourceMetadataTestOnUIThread, Reset) {
   // The grand root has "root" which is not empty.
   scoped_ptr<ResourceEntryVector> entries;
@@ -578,7 +535,7 @@
   // Change the name to dir100 and change the parent to drive/dir1/dir3.
   ResourceEntry dir_entry(entry);
   dir_entry.set_title("dir100");
-  dir_entry.set_parent_local_id("resource_id:dir3");
+  dir_entry.set_parent_local_id("id:dir3");
   EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->RefreshEntry(dir_id, dir_entry));
 
   EXPECT_EQ("drive/root/dir1/dir3/dir100",
@@ -588,7 +545,7 @@
             resource_metadata_->GetResourceEntryById(dir_id, &entry));
   EXPECT_EQ("dir100", entry.base_name());
   EXPECT_TRUE(entry.file_info().is_directory());
-  EXPECT_EQ("resource_id:dir2", entry.resource_id());
+  EXPECT_EQ("id:dir2", entry.resource_id());
 
   // Make sure the children have moved over. Test file6.
   entry.Clear();
@@ -610,7 +567,7 @@
   dir_entry.Clear();
   dir_entry.set_resource_id(util::kDriveGrandRootSpecialResourceId);
   dir_entry.set_title("new-root-name");
-  dir_entry.set_parent_local_id("resource_id:dir1");
+  dir_entry.set_parent_local_id("id:dir1");
   EXPECT_EQ(FILE_ERROR_INVALID_OPERATION, resource_metadata_->RefreshEntry(
       util::kDriveGrandRootSpecialResourceId, dir_entry));
 }
@@ -619,17 +576,17 @@
   std::set<base::FilePath> sub_directories;
 
   // file9: not a directory, so no children.
-  resource_metadata_->GetSubDirectoriesRecursively("resource_id:file9",
+  resource_metadata_->GetSubDirectoriesRecursively("id:file9",
                                                    &sub_directories);
   EXPECT_TRUE(sub_directories.empty());
 
   // dir2: no child directories.
-  resource_metadata_->GetSubDirectoriesRecursively("resource_id:dir2",
+  resource_metadata_->GetSubDirectoriesRecursively("id:dir2",
                                                    &sub_directories);
   EXPECT_TRUE(sub_directories.empty());
 
   // dir1: dir3 is the only child
-  resource_metadata_->GetSubDirectoriesRecursively("resource_id:dir1",
+  resource_metadata_->GetSubDirectoriesRecursively("id:dir1",
                                                    &sub_directories);
   EXPECT_EQ(1u, sub_directories.size());
   EXPECT_EQ(1u, sub_directories.count(
@@ -645,24 +602,25 @@
   // dir2/dir101/dir102/dir105
   // dir2/dir101/dir102/dir105/dir106
   // dir2/dir101/dir102/dir105/dir106/dir107
+  std::string local_id;
   EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
-      CreateDirectoryEntry("dir100", "resource_id:dir2")));
+      CreateDirectoryEntry("dir100", "id:dir2"), &local_id));
   EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
-      CreateDirectoryEntry("dir101", "resource_id:dir2")));
+      CreateDirectoryEntry("dir101", "id:dir2"), &local_id));
   EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
-      CreateDirectoryEntry("dir102", "resource_id:dir101")));
+      CreateDirectoryEntry("dir102", "id:dir101"), &local_id));
   EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
-      CreateDirectoryEntry("dir103", "resource_id:dir101")));
+      CreateDirectoryEntry("dir103", "id:dir101"), &local_id));
   EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
-      CreateDirectoryEntry("dir104", "resource_id:dir101")));
+      CreateDirectoryEntry("dir104", "id:dir101"), &local_id));
   EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
-      CreateDirectoryEntry("dir105", "resource_id:dir102")));
+      CreateDirectoryEntry("dir105", "id:dir102"), &local_id));
   EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
-      CreateDirectoryEntry("dir106", "resource_id:dir105")));
+      CreateDirectoryEntry("dir106", "id:dir105"), &local_id));
   EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
-      CreateDirectoryEntry("dir107", "resource_id:dir106")));
+      CreateDirectoryEntry("dir107", "id:dir106"), &local_id));
 
-  resource_metadata_->GetSubDirectoriesRecursively("resource_id:dir2",
+  resource_metadata_->GetSubDirectoriesRecursively("id:dir2",
                                                    &sub_directories);
   EXPECT_EQ(8u, sub_directories.size());
   EXPECT_EQ(1u, sub_directories.count(base::FilePath::FromUTF8Unsafe(
@@ -673,9 +631,35 @@
       "drive/root/dir2/dir101/dir102/dir105/dir106/dir107")));
 }
 
+TEST_F(ResourceMetadataTest, AddEntry) {
+  base::FilePath drive_file_path;
+
+  // Add a file to dir3.
+  std::string local_id;
+  ResourceEntry file_entry = CreateFileEntry("file100", "id:dir3");
+  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(file_entry, &local_id));
+  EXPECT_EQ("drive/root/dir1/dir3/file100",
+            resource_metadata_->GetFilePath(local_id).AsUTF8Unsafe());
+
+  // Add a directory.
+  ResourceEntry dir_entry = CreateDirectoryEntry("dir101", "id:dir1");
+  EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(dir_entry, &local_id));
+  EXPECT_EQ("drive/root/dir1/dir101",
+            resource_metadata_->GetFilePath(local_id).AsUTF8Unsafe());
+
+  // Add to an invalid parent.
+  ResourceEntry file_entry3 = CreateFileEntry("file103", "id:invalid");
+  EXPECT_EQ(FILE_ERROR_NOT_FOUND,
+            resource_metadata_->AddEntry(file_entry3, &local_id));
+
+  // Add an existing file.
+  EXPECT_EQ(FILE_ERROR_EXISTS,
+            resource_metadata_->AddEntry(file_entry, &local_id));
+}
+
 TEST_F(ResourceMetadataTest, RemoveEntry) {
   // Make sure file9 is found.
-  const std::string file9_resource_id = "resource_id:file9";
+  const std::string file9_resource_id = "id:file9";
   ResourceEntry entry;
   EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryById(
       file9_resource_id, &entry));
@@ -689,7 +673,7 @@
       file9_resource_id, &entry));
 
   // Look for dir3.
-  const std::string dir3_resource_id = "resource_id:dir3";
+  const std::string dir3_resource_id = "id:dir3";
   EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->GetResourceEntryById(
       dir3_resource_id, &entry));
   EXPECT_EQ("dir3", entry.base_name());
diff --git a/chrome/browser/chromeos/drive/search_metadata.cc b/chrome/browser/chromeos/drive/search_metadata.cc
index dc38f04..06843f7 100644
--- a/chrome/browser/chromeos/drive/search_metadata.cc
+++ b/chrome/browser/chromeos/drive/search_metadata.cc
@@ -21,6 +21,20 @@
 
 namespace {
 
+struct ResultCandidate {
+  ResultCandidate(const std::string& local_id,
+                  const ResourceEntry& entry,
+                  const std::string& highlighted_base_name)
+      : local_id(local_id),
+        entry(entry),
+        highlighted_base_name(highlighted_base_name) {
+  }
+
+  std::string local_id;
+  ResourceEntry entry;
+  std::string highlighted_base_name;
+};
+
 // Used to sort the result candidates per the last accessed/modified time. The
 // recently accessed/modified files come first.
 bool CompareByTimestamp(const ResourceEntry& a, const ResourceEntry& b) {
@@ -36,9 +50,8 @@
   return a_file_info.last_modified() > b_file_info.last_modified();
 }
 
-struct MetadataSearchResultComparator {
-  bool operator()(const MetadataSearchResult* a,
-                  const MetadataSearchResult* b) const {
+struct ResultCandidateComparator {
+  bool operator()(const ResultCandidate* a, const ResultCandidate* b) const {
     return CompareByTimestamp(a->entry, b->entry);
   }
 };
@@ -104,7 +117,7 @@
   }
 
   // Exclude "drive", "drive/root", and "drive/other".
-  if (entry.resource_id() == util::kDriveGrandRootSpecialResourceId ||
+  if (it->GetID() == util::kDriveGrandRootSpecialResourceId ||
       entry.parent_local_id() == util::kDriveGrandRootSpecialResourceId) {
     return false;
   }
@@ -122,8 +135,8 @@
     base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents* query,
     int options,
     size_t at_most_num_matches,
-    ScopedPriorityQueue<MetadataSearchResult,
-                        MetadataSearchResultComparator>* result_candidates) {
+    ScopedPriorityQueue<ResultCandidate,
+                        ResultCandidateComparator>* result_candidates) {
   DCHECK_GE(at_most_num_matches, result_candidates->size());
 
   const ResourceEntry& entry = it->GetValue();
@@ -146,7 +159,7 @@
   // Make space for |entry| when appropriate.
   if (result_candidates->size() == at_most_num_matches)
     result_candidates->pop();
-  result_candidates->push(new MetadataSearchResult(entry, highlighted));
+  result_candidates->push(new ResultCandidate(it->GetID(), entry, highlighted));
 }
 
 // Implements SearchMetadata().
@@ -155,8 +168,8 @@
                                        int options,
                                        int at_most_num_matches,
                                        MetadataSearchResultVector* results) {
-  ScopedPriorityQueue<MetadataSearchResult,
-                      MetadataSearchResultComparator> result_candidates;
+  ScopedPriorityQueue<ResultCandidate,
+                      ResultCandidateComparator> result_candidates;
 
   // Prepare data structure for searching.
   base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents query(
@@ -173,15 +186,15 @@
 
   // Prepare the result.
   for (; !result_candidates.empty(); result_candidates.pop()) {
+    const ResultCandidate& candidate = *result_candidates.top();
     // The path field of entries in result_candidates are empty at this point,
     // because we don't want to run the expensive metadata DB look up except for
     // the final results. Hence, here we fill the part.
-    base::FilePath path = resource_metadata->GetFilePath(
-        result_candidates.top()->entry.resource_id());
+    base::FilePath path = resource_metadata->GetFilePath(candidate.local_id);
     if (path.empty())
       return FILE_ERROR_FAILED;
-    results->push_back(*result_candidates.top());
-    results->back().path = path;
+    results->push_back(MetadataSearchResult(
+        path, candidate.entry, candidate.highlighted_base_name));
   }
 
   // Reverse the order here because |result_candidates| puts the most
diff --git a/chrome/browser/chromeos/drive/search_metadata_unittest.cc b/chrome/browser/chromeos/drive/search_metadata_unittest.cc
index 87a9e6b..5b3d3f9 100644
--- a/chrome/browser/chromeos/drive/search_metadata_unittest.cc
+++ b/chrome/browser/chromeos/drive/search_metadata_unittest.cc
@@ -100,34 +100,35 @@
 
   void AddEntriesToMetadata() {
     ResourceEntry entry;
+    std::string local_id;
 
     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetDirectoryEntry(
         util::kDriveMyDriveRootDirName, "root", 100,
-        util::kDriveGrandRootSpecialResourceId)));
+        util::kDriveGrandRootSpecialResourceId), &local_id));
 
     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetDirectoryEntry(
-        "Directory 1", "dir1", 1, "root")));
+        "Directory 1", "dir1", 1, "root"), &local_id));
     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetFileEntry(
-        "SubDirectory File 1.txt", "file1a", 2, "dir1")));
+        "SubDirectory File 1.txt", "file1a", 2, "dir1"), &local_id));
 
     entry = GetFileEntry(
         "Shared To The Account Owner.txt", "file1b", 3, "dir1");
     entry.set_shared_with_me(true);
-    EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(entry));
+    EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(entry, &local_id));
 
     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetDirectoryEntry(
-        "Directory 2 excludeDir-test", "dir2", 4, "root")));
+        "Directory 2 excludeDir-test", "dir2", 4, "root"), &local_id));
 
     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetDirectoryEntry(
-        "Slash \xE2\x88\x95 in directory", "dir3", 5, "root")));
+        "Slash \xE2\x88\x95 in directory", "dir3", 5, "root"), &local_id));
     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(GetFileEntry(
-        "Slash SubDir File.txt", "file3a", 6, "dir3")));
+        "Slash SubDir File.txt", "file3a", 6, "dir3"), &local_id));
 
     entry = GetFileEntry(
         "Document 1 excludeDir-test", "doc1", 7, "root");
     entry.mutable_file_specific_info()->set_is_hosted_document(true);
     entry.mutable_file_specific_info()->set_document_extension(".gdoc");
-    EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(entry));
+    EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(entry, &local_id));
   }
 
   // Adds a directory at |path|. Parent directories are added if needed just
@@ -149,13 +150,14 @@
     const std::string parent_id =
         AddDirectoryToMetadataWithParents(path.DirName(), generator);
     const std::string id = generator->GetId();
+    std::string local_id;
     EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
         GetDirectoryEntry(path.BaseName().AsUTF8Unsafe(),
                           id,
                           generator->GetLastAccessed(),
-                          parent_id)));
+                          parent_id), &local_id));
     generator->Advance();
-    return id;
+    return local_id;
   }
 
   // Adds entries for |cache_resources| to |resource_metadata_|.  The parent
@@ -168,11 +170,12 @@
       const base::FilePath path(resource.source_file);
       const std::string parent_id =
           AddDirectoryToMetadataWithParents(path.DirName(), generator);
+      std::string local_id;
       EXPECT_EQ(FILE_ERROR_OK, resource_metadata_->AddEntry(
           GetFileEntry(path.BaseName().AsUTF8Unsafe(),
                        resource.resource_id,
                        generator->GetLastAccessed(),
-                       parent_id)));
+                       parent_id), &local_id));
       generator->Advance();
     }
   }
diff --git a/chrome/browser/chromeos/drive/sync_client.cc b/chrome/browser/chromeos/drive/sync_client.cc
index 31771d6..ce2419a 100644
--- a/chrome/browser/chromeos/drive/sync_client.cc
+++ b/chrome/browser/chromeos/drive/sync_client.cc
@@ -41,21 +41,21 @@
 // The delay constant is used to delay retrying a sync task on server errors.
 const int kLongDelaySeconds = 600;
 
-// Appends |resource_id| to |to_fetch| if the file is pinned but not fetched
+// Appends |local_id| to |to_fetch| if the file is pinned but not fetched
 // (not present locally), or to |to_upload| if the file is dirty but not
 // uploaded.
 void CollectBacklog(std::vector<std::string>* to_fetch,
                     std::vector<std::string>* to_upload,
-                    const std::string& resource_id,
+                    const std::string& local_id,
                     const FileCacheEntry& cache_entry) {
   DCHECK(to_fetch);
   DCHECK(to_upload);
 
   if (cache_entry.is_pinned() && !cache_entry.is_present())
-    to_fetch->push_back(resource_id);
+    to_fetch->push_back(local_id);
 
   if (cache_entry.is_dirty())
-    to_upload->push_back(resource_id);
+    to_upload->push_back(local_id);
 }
 
 }  // namespace
@@ -96,7 +96,7 @@
   std::vector<std::string>* to_fetch = new std::vector<std::string>;
   std::vector<std::string>* to_upload = new std::vector<std::string>;
   cache_->IterateOnUIThread(base::Bind(&CollectBacklog, to_fetch, to_upload),
-                            base::Bind(&SyncClient::OnGetResourceIdsOfBacklog,
+                            base::Bind(&SyncClient::OnGetLocalIdsOfBacklog,
                                        weak_ptr_factory_.GetWeakPtr(),
                                        base::Owned(to_fetch),
                                        base::Owned(to_upload)));
@@ -106,49 +106,49 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   cache_->IterateOnUIThread(
-      base::Bind(&SyncClient::OnGetResourceIdOfExistingPinnedFile,
+      base::Bind(&SyncClient::OnGetLocalIdOfExistingPinnedFile,
                  weak_ptr_factory_.GetWeakPtr()),
       base::Bind(&base::DoNothing));
 }
 
-void SyncClient::AddFetchTask(const std::string& resource_id) {
+void SyncClient::AddFetchTask(const std::string& local_id) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-  AddTaskToQueue(FETCH, resource_id, delay_);
+  AddTaskToQueue(FETCH, local_id, delay_);
 }
 
-void SyncClient::RemoveFetchTask(const std::string& resource_id) {
+void SyncClient::RemoveFetchTask(const std::string& local_id) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   // TODO(kinaba): Cancel tasks in JobScheduler as well. crbug.com/248856
-  pending_fetch_list_.erase(resource_id);
+  pending_fetch_list_.erase(local_id);
 }
 
-void SyncClient::AddUploadTask(const std::string& resource_id) {
+void SyncClient::AddUploadTask(const std::string& local_id) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-  AddTaskToQueue(UPLOAD, resource_id, delay_);
+  AddTaskToQueue(UPLOAD, local_id, delay_);
 }
 
 void SyncClient::AddTaskToQueue(SyncType type,
-                                const std::string& resource_id,
+                                const std::string& local_id,
                                 const base::TimeDelta& delay) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   // If the same task is already queued, ignore this task.
   switch (type) {
     case FETCH:
-      if (fetch_list_.find(resource_id) == fetch_list_.end()) {
-        fetch_list_.insert(resource_id);
-        pending_fetch_list_.insert(resource_id);
+      if (fetch_list_.find(local_id) == fetch_list_.end()) {
+        fetch_list_.insert(local_id);
+        pending_fetch_list_.insert(local_id);
       } else {
         return;
       }
       break;
     case UPLOAD:
     case UPLOAD_NO_CONTENT_CHECK:
-      if (upload_list_.find(resource_id) == upload_list_.end()) {
-        upload_list_.insert(resource_id);
+      if (upload_list_.find(local_id) == upload_list_.end()) {
+        upload_list_.insert(local_id);
       } else {
         return;
       }
@@ -160,47 +160,47 @@
       base::Bind(&SyncClient::StartTask,
                  weak_ptr_factory_.GetWeakPtr(),
                  type,
-                 resource_id),
+                 local_id),
       delay);
 }
 
-void SyncClient::StartTask(SyncType type, const std::string& resource_id) {
+void SyncClient::StartTask(SyncType type, const std::string& local_id) {
   switch (type) {
     case FETCH:
       // Check if the resource has been removed from the start list.
-      if (pending_fetch_list_.find(resource_id) != pending_fetch_list_.end()) {
-        DVLOG(1) << "Fetching " << resource_id;
-        pending_fetch_list_.erase(resource_id);
+      if (pending_fetch_list_.find(local_id) != pending_fetch_list_.end()) {
+        DVLOG(1) << "Fetching " << local_id;
+        pending_fetch_list_.erase(local_id);
 
-        download_operation_->EnsureFileDownloadedByResourceId(
-            resource_id,
+        download_operation_->EnsureFileDownloadedByLocalId(
+            local_id,
             ClientContext(BACKGROUND),
             GetFileContentInitializedCallback(),
             google_apis::GetContentCallback(),
             base::Bind(&SyncClient::OnFetchFileComplete,
                        weak_ptr_factory_.GetWeakPtr(),
-                       resource_id));
+                       local_id));
       } else {
         // Cancel the task.
-        fetch_list_.erase(resource_id);
+        fetch_list_.erase(local_id);
       }
       break;
     case UPLOAD:
     case UPLOAD_NO_CONTENT_CHECK:
-      DVLOG(1) << "Uploading " << resource_id;
-      update_operation_->UpdateFileByResourceId(
-          resource_id,
+      DVLOG(1) << "Uploading " << local_id;
+      update_operation_->UpdateFileByLocalId(
+          local_id,
           ClientContext(BACKGROUND),
           type == UPLOAD ? file_system::UpdateOperation::RUN_CONTENT_CHECK
                          : file_system::UpdateOperation::NO_CONTENT_CHECK,
           base::Bind(&SyncClient::OnUploadFileComplete,
                      weak_ptr_factory_.GetWeakPtr(),
-                     resource_id));
+                     local_id));
       break;
   }
 }
 
-void SyncClient::OnGetResourceIdsOfBacklog(
+void SyncClient::OnGetLocalIdsOfBacklog(
     const std::vector<std::string>* to_fetch,
     const std::vector<std::string>* to_upload) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -208,45 +208,44 @@
   // Give priority to upload tasks over fetch tasks, so that dirty files are
   // uploaded as soon as possible.
   for (size_t i = 0; i < to_upload->size(); ++i) {
-    const std::string& resource_id = (*to_upload)[i];
-    DVLOG(1) << "Queuing to upload: " << resource_id;
-    AddTaskToQueue(UPLOAD_NO_CONTENT_CHECK, resource_id, delay_);
+    const std::string& local_id = (*to_upload)[i];
+    DVLOG(1) << "Queuing to upload: " << local_id;
+    AddTaskToQueue(UPLOAD_NO_CONTENT_CHECK, local_id, delay_);
   }
 
   for (size_t i = 0; i < to_fetch->size(); ++i) {
-    const std::string& resource_id = (*to_fetch)[i];
-    DVLOG(1) << "Queuing to fetch: " << resource_id;
-    AddTaskToQueue(FETCH, resource_id, delay_);
+    const std::string& local_id = (*to_fetch)[i];
+    DVLOG(1) << "Queuing to fetch: " << local_id;
+    AddTaskToQueue(FETCH, local_id, delay_);
   }
 }
 
-void SyncClient::OnGetResourceIdOfExistingPinnedFile(
-    const std::string& resource_id,
+void SyncClient::OnGetLocalIdOfExistingPinnedFile(
+    const std::string& local_id,
     const FileCacheEntry& cache_entry) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   if (cache_entry.is_pinned() && cache_entry.is_present()) {
     metadata_->GetResourceEntryByIdOnUIThread(
-        resource_id,
+        local_id,
         base::Bind(&SyncClient::OnGetResourceEntryById,
                    weak_ptr_factory_.GetWeakPtr(),
-                   resource_id,
+                   local_id,
                    cache_entry));
   }
 }
 
-void SyncClient::OnGetResourceEntryById(
-    const std::string& resource_id,
-    const FileCacheEntry& cache_entry,
-    FileError error,
-    scoped_ptr<ResourceEntry> entry) {
+void SyncClient::OnGetResourceEntryById(const std::string& local_id,
+                                        const FileCacheEntry& cache_entry,
+                                        FileError error,
+                                        scoped_ptr<ResourceEntry> entry) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   if (entry.get() && !entry->has_file_specific_info())
     error = FILE_ERROR_NOT_FOUND;
 
   if (error != FILE_ERROR_OK) {
-    LOG(WARNING) << "Entry not found: " << resource_id;
+    LOG(WARNING) << "Entry not found: " << local_id;
     return;
   }
 
@@ -255,97 +254,94 @@
   // file when we have a locally modified version.
   if (entry->file_specific_info().md5() != cache_entry.md5() &&
       !cache_entry.is_dirty()) {
-    cache_->RemoveOnUIThread(resource_id,
+    cache_->RemoveOnUIThread(local_id,
                              base::Bind(&SyncClient::OnRemove,
                                         weak_ptr_factory_.GetWeakPtr(),
-                                        resource_id));
+                                        local_id));
   }
 }
 
-void SyncClient::OnRemove(const std::string& resource_id,
-                          FileError error) {
+void SyncClient::OnRemove(const std::string& local_id, FileError error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   if (error != FILE_ERROR_OK) {
-    LOG(WARNING) << "Failed to remove cache entry: " << resource_id;
+    LOG(WARNING) << "Failed to remove cache entry: " << local_id;
     return;
   }
 
   // Before fetching, we should pin this file again, so that the fetched file
   // is downloaded properly to the persistent directory and marked pinned.
-  cache_->PinOnUIThread(resource_id,
+  cache_->PinOnUIThread(local_id,
                         base::Bind(&SyncClient::OnPinned,
                                    weak_ptr_factory_.GetWeakPtr(),
-                                   resource_id));
+                                   local_id));
 }
 
-void SyncClient::OnPinned(const std::string& resource_id,
-                          FileError error) {
+void SyncClient::OnPinned(const std::string& local_id, FileError error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   if (error != FILE_ERROR_OK) {
-    LOG(WARNING) << "Failed to pin cache entry: " << resource_id;
+    LOG(WARNING) << "Failed to pin cache entry: " << local_id;
     return;
   }
 
   // Finally, adding to the queue.
-  AddTaskToQueue(FETCH, resource_id, delay_);
+  AddTaskToQueue(FETCH, local_id, delay_);
 }
 
-void SyncClient::OnFetchFileComplete(const std::string& resource_id,
+void SyncClient::OnFetchFileComplete(const std::string& local_id,
                                      FileError error,
                                      const base::FilePath& local_path,
                                      scoped_ptr<ResourceEntry> entry) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-  fetch_list_.erase(resource_id);
+  fetch_list_.erase(local_id);
 
   if (error == FILE_ERROR_OK) {
-    DVLOG(1) << "Fetched " << resource_id << ": "
-             << local_path.value();
+    DVLOG(1) << "Fetched " << local_id << ": " << local_path.value();
   } else {
     switch (error) {
       case FILE_ERROR_ABORT:
         // If user cancels download, unpin the file so that we do not sync the
         // file again.
-        cache_->UnpinOnUIThread(resource_id,
+        cache_->UnpinOnUIThread(local_id,
                                 base::Bind(&util::EmptyFileOperationCallback));
         break;
       case FILE_ERROR_NO_CONNECTION:
         // Re-queue the task so that we'll retry once the connection is back.
-        AddTaskToQueue(FETCH, resource_id, delay_);
+        AddTaskToQueue(FETCH, local_id, delay_);
         break;
       case FILE_ERROR_SERVICE_UNAVAILABLE:
         // Re-queue the task so that we'll retry once the service is back.
-        AddTaskToQueue(FETCH, resource_id, long_delay_);
+        AddTaskToQueue(FETCH, local_id, long_delay_);
         break;
       default:
-        LOG(WARNING) << "Failed to fetch " << resource_id
+        LOG(WARNING) << "Failed to fetch " << local_id
                      << ": " << FileErrorToString(error);
     }
   }
 }
 
-void SyncClient::OnUploadFileComplete(const std::string& resource_id,
+void SyncClient::OnUploadFileComplete(const std::string& local_id,
                                       FileError error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-  upload_list_.erase(resource_id);
+  upload_list_.erase(local_id);
 
   if (error == FILE_ERROR_OK) {
-    DVLOG(1) << "Uploaded " << resource_id;
+    DVLOG(1) << "Uploaded " << local_id;
   } else {
     switch (error) {
       case FILE_ERROR_NO_CONNECTION:
         // Re-queue the task so that we'll retry once the connection is back.
-        AddTaskToQueue(UPLOAD_NO_CONTENT_CHECK, resource_id, delay_);
+        AddTaskToQueue(UPLOAD_NO_CONTENT_CHECK, local_id, delay_);
         break;
       case FILE_ERROR_SERVICE_UNAVAILABLE:
         // Re-queue the task so that we'll retry once the service is back.
-        AddTaskToQueue(UPLOAD_NO_CONTENT_CHECK, resource_id, long_delay_);
+        AddTaskToQueue(UPLOAD_NO_CONTENT_CHECK, local_id, long_delay_);
         break;
       default:
-        LOG(WARNING) << "Failed to upload " << resource_id << ": "
+        LOG(WARNING) << "Failed to upload " << local_id << ": "
                      << FileErrorToString(error);
     }
   }
diff --git a/chrome/browser/chromeos/drive/sync_client.h b/chrome/browser/chromeos/drive/sync_client.h
index 3178f5b..fd55fde 100644
--- a/chrome/browser/chromeos/drive/sync_client.h
+++ b/chrome/browser/chromeos/drive/sync_client.h
@@ -62,21 +62,21 @@
   virtual ~SyncClient();
 
   // Adds a fetch task to the queue.
-  void AddFetchTask(const std::string& resource_id);
+  void AddFetchTask(const std::string& local_id);
 
   // Removes a fetch task from the queue.
-  void RemoveFetchTask(const std::string& resource_id);
+  void RemoveFetchTask(const std::string& local_id);
 
   // Adds an upload task to the queue.
-  void AddUploadTask(const std::string& resource_id);
+  void AddUploadTask(const std::string& local_id);
 
   // Starts processing the backlog (i.e. pinned-but-not-filed files and
-  // dirty-but-not-uploaded files). Kicks off retrieval of the resource
+  // dirty-but-not-uploaded files). Kicks off retrieval of the local
   // IDs of these files, and then starts the sync loop.
   void StartProcessingBacklog();
 
   // Starts checking the existing pinned files to see if these are
-  // up-to-date. If stale files are detected, the resource IDs of these files
+  // up-to-date. If stale files are detected, the local IDs of these files
   // are added to the queue and the sync loop is started.
   void StartCheckingExistingPinnedFiles();
 
@@ -92,49 +92,48 @@
   // Adds the given task to the queue. If the same task is queued, remove the
   // existing one, and adds a new one to the end of the queue.
   void AddTaskToQueue(SyncType type,
-                      const std::string& resource_id,
+                      const std::string& local_id,
                       const base::TimeDelta& delay);
 
   // Called when a task is ready to be added to the queue.
-  void StartTask(SyncType type, const std::string& resource_id);
+  void StartTask(SyncType type, const std::string& local_id);
 
-  // Called when the resource IDs of files in the backlog are obtained.
-  void OnGetResourceIdsOfBacklog(const std::vector<std::string>* to_fetch,
+  // Called when the local IDs of files in the backlog are obtained.
+  void OnGetLocalIdsOfBacklog(const std::vector<std::string>* to_fetch,
                                  const std::vector<std::string>* to_upload);
 
-  // Called when the resource ID of a pinned file is obtained.
-  void OnGetResourceIdOfExistingPinnedFile(const std::string& resource_id,
-                                           const FileCacheEntry& cache_entry);
+  // Called when the local ID of a pinned file is obtained.
+  void OnGetLocalIdOfExistingPinnedFile(const std::string& local_id,
+                                        const FileCacheEntry& cache_entry);
 
   // Called when a file entry is obtained.
-  void OnGetResourceEntryById(const std::string& resource_id,
+  void OnGetResourceEntryById(const std::string& local_id,
                               const FileCacheEntry& cache_entry,
                               FileError error,
                               scoped_ptr<ResourceEntry> entry);
 
   // Called when a cache entry is obtained.
-  void OnGetCacheEntry(const std::string& resource_id,
+  void OnGetCacheEntry(const std::string& local_id,
                        const std::string& latest_md5,
                        bool success,
                        const FileCacheEntry& cache_entry);
 
   // Called when an existing cache entry and the local files are removed.
-  void OnRemove(const std::string& resource_id, FileError error);
+  void OnRemove(const std::string& local_id, FileError error);
 
   // Called when a file is pinned.
-  void OnPinned(const std::string& resource_id, FileError error);
+  void OnPinned(const std::string& local_id, FileError error);
 
-  // Called when the file for |resource_id| is fetched.
+  // Called when the file for |local_id| is fetched.
   // Calls DoSyncLoop() to go back to the sync loop.
-  void OnFetchFileComplete(const std::string& resource_id,
+  void OnFetchFileComplete(const std::string& local_id,
                            FileError error,
                            const base::FilePath& local_path,
                            scoped_ptr<ResourceEntry> entry);
 
-  // Called when the file for |resource_id| is uploaded.
+  // Called when the file for |local_id| is uploaded.
   // Calls DoSyncLoop() to go back to the sync loop.
-  void OnUploadFileComplete(const std::string& resource_id,
-                            FileError error);
+  void OnUploadFileComplete(const std::string& local_id, FileError error);
 
   ResourceMetadata* metadata_;
   FileCache* cache_;
@@ -145,10 +144,10 @@
   // Used to upload committed files.
   scoped_ptr<file_system::UpdateOperation> update_operation_;
 
-  // List of the resource ids of resources which have a fetch task created.
+  // List of the local ids of resources which have a fetch task created.
   std::set<std::string> fetch_list_;
 
-  // List of the resource ids of resources which have a upload task created.
+  // List of the local ids of resources which have a upload task created.
   std::set<std::string> upload_list_;
 
   // Fetch tasks which have been created, but not started yet.  If they are
diff --git a/chrome/browser/chromeos/drive/sync_client_unittest.cc b/chrome/browser/chromeos/drive/sync_client_unittest.cc
index e4d48ec..d67be23 100644
--- a/chrome/browser/chromeos/drive/sync_client_unittest.cc
+++ b/chrome/browser/chromeos/drive/sync_client_unittest.cc
@@ -76,7 +76,7 @@
   virtual void OnDirectoryChangedByOperation(
       const base::FilePath& path) OVERRIDE {}
   virtual void OnCacheFileUploadNeededByOperation(
-      const std::string& resource_id) OVERRIDE {}
+      const std::string& local_id) OVERRIDE {}
 };
 
 }  // namespace
diff --git a/chrome/browser/chromeos/extensions/echo_private_api.cc b/chrome/browser/chromeos/extensions/echo_private_api.cc
index edf3f6f..efa557d 100644
--- a/chrome/browser/chromeos/extensions/echo_private_api.cc
+++ b/chrome/browser/chromeos/extensions/echo_private_api.cc
@@ -9,17 +9,23 @@
 #include "base/bind.h"
 #include "base/file_util.h"
 #include "base/location.h"
+#include "base/prefs/pref_registry_simple.h"
+#include "base/prefs/pref_service.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/kiosk_mode/kiosk_mode_settings.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/system/statistics_provider.h"
 #include "chrome/browser/chromeos/ui/echo_dialog_view.h"
+#include "chrome/browser/prefs/scoped_user_pref_update.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/common/extensions/api/echo_private.h"
 #include "chrome/common/extensions/extension.h"
+#include "chrome/common/pref_names.h"
 #include "content/public/browser/browser_thread.h"
 
 namespace echo_api = extensions::api::echo_private;
@@ -35,6 +41,18 @@
 
 }  // namespace
 
+namespace chromeos {
+
+namespace echo_offer {
+
+void RegisterPrefs(PrefRegistrySimple* registry) {
+  registry->RegisterDictionaryPref(prefs::kEchoCheckedOffers);
+}
+
+} // namespace echo_offer
+
+} // namespace chromeos
+
 EchoPrivateGetRegistrationCodeFunction::
     EchoPrivateGetRegistrationCodeFunction() {}
 
@@ -80,6 +98,52 @@
   return true;
 }
 
+EchoPrivateSetOfferInfoFunction::EchoPrivateSetOfferInfoFunction() {}
+
+EchoPrivateSetOfferInfoFunction::~EchoPrivateSetOfferInfoFunction() {}
+
+bool EchoPrivateSetOfferInfoFunction::RunImpl() {
+  scoped_ptr<echo_api::SetOfferInfo::Params> params =
+      echo_api::SetOfferInfo::Params::Create(*args_);
+  EXTENSION_FUNCTION_VALIDATE(params);
+
+  const std::string& service_id = params->id;
+  base::DictionaryValue* dict = params->offer_info.
+      additional_properties.DeepCopyWithoutEmptyChildren();
+
+  PrefService* local_state = g_browser_process->local_state();
+  DictionaryPrefUpdate offer_update(local_state, prefs::kEchoCheckedOffers);
+  offer_update->SetWithoutPathExpansion("echo." + service_id, dict);
+  return true;
+}
+
+EchoPrivateGetOfferInfoFunction::EchoPrivateGetOfferInfoFunction() {}
+
+EchoPrivateGetOfferInfoFunction::~EchoPrivateGetOfferInfoFunction() {}
+
+bool EchoPrivateGetOfferInfoFunction::RunImpl() {
+  scoped_ptr<echo_api::GetOfferInfo::Params> params =
+      echo_api::GetOfferInfo::Params::Create(*args_);
+  EXTENSION_FUNCTION_VALIDATE(params);
+
+  const std::string& service_id = params->id;
+  PrefService* local_state = g_browser_process->local_state();
+  const base::DictionaryValue* offer_infos = local_state->
+      GetDictionary(prefs::kEchoCheckedOffers);
+
+  const base::DictionaryValue* offer_info = NULL;
+  if (!offer_infos->GetDictionaryWithoutPathExpansion(
+         "echo." + service_id, &offer_info)) {
+    error_ = "Not found";
+    return false;
+  }
+
+  echo_api::GetOfferInfo::Results::Result result;
+  result.additional_properties.MergeDictionary(offer_info);
+  results_ = echo_api::GetOfferInfo::Results::Create(result);
+  return true;
+}
+
 EchoPrivateGetOobeTimestampFunction::EchoPrivateGetOobeTimestampFunction() {
 }
 
diff --git a/chrome/browser/chromeos/extensions/echo_private_api.h b/chrome/browser/chromeos/extensions/echo_private_api.h
index 1d44362..f559079 100644
--- a/chrome/browser/chromeos/extensions/echo_private_api.h
+++ b/chrome/browser/chromeos/extensions/echo_private_api.h
@@ -9,9 +9,20 @@
 #include "chrome/browser/chromeos/ui/echo_dialog_listener.h"
 #include "chrome/browser/extensions/extension_function.h"
 
+class PrefRegistrySimple;
+
 namespace chromeos {
+
 class EchoDialogView;
-}
+
+// Namespace to register the EchoCheckedOffers field in Local State.
+namespace echo_offer {
+
+void RegisterPrefs(PrefRegistrySimple* registry);
+
+}  // namespace echo_offer
+
+}  // namespace chromeos
 
 class EchoPrivateGetRegistrationCodeFunction : public SyncExtensionFunction {
  public:
@@ -41,6 +52,32 @@
                              ECHOPRIVATE_GETOOBETIMESTAMP)
 };
 
+class EchoPrivateSetOfferInfoFunction : public SyncExtensionFunction {
+ public:
+  EchoPrivateSetOfferInfoFunction();
+
+ protected:
+  virtual ~EchoPrivateSetOfferInfoFunction();
+  virtual bool RunImpl() OVERRIDE;
+
+ private:
+  DECLARE_EXTENSION_FUNCTION("echoPrivate.setOfferInfo",
+                             ECHOPRIVATE_SETOFFERINFO)
+};
+
+class EchoPrivateGetOfferInfoFunction : public SyncExtensionFunction {
+ public:
+  EchoPrivateGetOfferInfoFunction();
+
+ protected:
+  virtual ~EchoPrivateGetOfferInfoFunction();
+  virtual bool RunImpl() OVERRIDE;
+
+ private:
+  DECLARE_EXTENSION_FUNCTION("echoPrivate.getOfferInfo",
+                             ECHOPRIVATE_GETOFFERINFO)
+};
+
 // TODO(tbarzic): Remove this once echo.getUserConsent function is up and
 // running.
 class EchoPrivateCheckAllowRedeemOffersFunction
diff --git a/chrome/browser/chromeos/extensions/file_manager/event_router.cc b/chrome/browser/chromeos/extensions/file_manager/event_router.cc
index ab460cf..2f7499c 100644
--- a/chrome/browser/chromeos/extensions/file_manager/event_router.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/event_router.cc
@@ -18,9 +18,9 @@
 #include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/chromeos/extensions/file_manager/app_id.h"
 #include "chrome/browser/chromeos/extensions/file_manager/desktop_notifications.h"
-#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
 #include "chrome/browser/chromeos/extensions/file_manager/fileapi_util.h"
 #include "chrome/browser/chromeos/extensions/file_manager/mounted_disk_monitor.h"
+#include "chrome/browser/chromeos/extensions/file_manager/open_util.h"
 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
 #include "chrome/browser/chromeos/login/screen_locker.h"
 #include "chrome/browser/drive/drive_service_interface.h"
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.cc b/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.cc
index e2b428f..ca1a900 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.cc
@@ -35,7 +35,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_loop_proxy.h"
 #include "base/platform_file.h"
-#include "chrome/browser/chromeos/extensions/file_manager/file_tasks.h"
+#include "chrome/browser/chromeos/extensions/file_manager/fileapi_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
@@ -333,9 +333,10 @@
 
   // We have to open file system in order to create a FileEntry object for the
   // selected file path.
-  content::SiteInstance* site_instance = render_view_host()->GetSiteInstance();
-  BrowserContext::GetStoragePartition(profile_, site_instance)->
-      GetFileSystemContext()->OpenFileSystem(
+  scoped_refptr<fileapi::FileSystemContext> file_system_context =
+      file_manager::util::GetFileSystemContextForRenderViewHost(
+          profile_, render_view_host());
+  file_system_context->OpenFileSystem(
           source_url_.GetOrigin(), fileapi::kFileSystemTypeExternal,
           fileapi::OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
           base::Bind(
@@ -366,10 +367,9 @@
 }
 
 void FileBrowserHandlerInternalSelectFileFunction::GrantPermissions() {
-  content::SiteInstance* site_instance = render_view_host()->GetSiteInstance();
   fileapi::ExternalFileSystemBackend* external_backend =
-      BrowserContext::GetStoragePartition(profile_, site_instance)->
-      GetFileSystemContext()->external_backend();
+      file_manager::util::GetFileSystemContextForRenderViewHost(
+          profile_, render_view_host())->external_backend();
   DCHECK(external_backend);
 
   external_backend->GetVirtualPath(full_path_, &virtual_path_);
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_browser_handlers.cc b/chrome/browser/chromeos/extensions/file_manager/file_browser_handlers.cc
index 889d933..789536d 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_browser_handlers.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_browser_handlers.cc
@@ -10,8 +10,8 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/chromeos/extensions/file_manager/app_id.h"
-#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
 #include "chrome/browser/chromeos/extensions/file_manager/fileapi_util.h"
+#include "chrome/browser/chromeos/extensions/file_manager/open_with_browser.h"
 #include "chrome/browser/chromeos/fileapi/file_system_backend.h"
 #include "chrome/browser/extensions/event_router.h"
 #include "chrome/browser/extensions/extension_host.h"
@@ -479,23 +479,16 @@
            action_id == "open-hosted-gslides"));
 }
 
-// Opens the files specified by |file_urls| with the browser. |profile| is
-// used for finding an active browser. Returns true on success. It's a
-// failure if no files are opened.
+// Opens the files specified by |file_urls| with the browser for |profile|.
+// Returns true on success. It's a failure if no files are opened.
 bool OpenFilesWithBrowser(Profile* profile,
                           const std::vector<FileSystemURL>& file_urls) {
-  Browser* browser = chrome::FindLastActiveWithProfile(
-      profile,
-      chrome::HOST_DESKTOP_TYPE_ASH);
-  if (!browser)
-    return false;
-
   int num_opened = 0;
   for (size_t i = 0; i < file_urls.size(); ++i) {
     const FileSystemURL& file_url = file_urls[i];
     if (chromeos::FileSystemBackend::CanHandleURL(file_url)) {
       const base::FilePath& file_path = file_url.path();
-      num_opened += util::OpenFileWithBrowser(browser, file_path);
+      num_opened += util::OpenFileWithBrowser(profile, file_path);
     }
   }
   return num_opened > 0;
@@ -526,16 +519,16 @@
   return true;
 }
 
-bool IsFallbackFileBrowserHandler(const FileBrowserHandler* handler) {
-  const std::string& extension_id = handler->extension_id();
-  return (extension_id == kFileManagerAppId ||
-          extension_id == extension_misc::kQuickOfficeComponentExtensionId ||
-          extension_id == extension_misc::kQuickOfficeDevExtensionId ||
-          extension_id == extension_misc::kQuickOfficeExtensionId);
+bool IsFallbackFileBrowserHandler(const file_tasks::TaskDescriptor& task) {
+  return (task.task_type == file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER &&
+          (task.app_id == kFileManagerAppId ||
+           task.app_id == extension_misc::kQuickOfficeComponentExtensionId ||
+           task.app_id == extension_misc::kQuickOfficeDevExtensionId ||
+           task.app_id == extension_misc::kQuickOfficeExtensionId));
 }
 
 FileBrowserHandlerList FindDefaultFileBrowserHandlers(
-    Profile* profile,
+    const PrefService& pref_service,
     const std::vector<base::FilePath>& file_list,
     const FileBrowserHandlerList& common_handlers) {
   FileBrowserHandlerList default_handlers;
@@ -544,7 +537,7 @@
   for (std::vector<base::FilePath>::const_iterator it = file_list.begin();
        it != file_list.end(); ++it) {
     std::string task_id = file_tasks::GetDefaultTaskIdFromPrefs(
-        profile, "", it->Extension());
+        pref_service, "", it->Extension());
     if (!task_id.empty())
       default_ids.insert(task_id);
   }
@@ -554,10 +547,12 @@
   // from common_handlers.
   for (size_t i = 0; i < common_handlers.size(); ++i) {
     const FileBrowserHandler* handler = common_handlers[i];
-    std::string task_id = file_tasks::MakeTaskID(
+    const file_tasks::TaskDescriptor task_descriptor(
         handler->extension_id(),
         file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER,
         handler->id());
+    const std::string task_id =
+        file_tasks::TaskDescriptorToId(task_descriptor);
     std::set<std::string>::iterator default_iter = default_ids.find(task_id);
     if (default_iter != default_ids.end()) {
       default_handlers.push_back(handler);
@@ -565,7 +560,7 @@
     }
 
     // Remember the first fallback handler.
-    if (!fallback_handler && IsFallbackFileBrowserHandler(handler))
+    if (!fallback_handler && IsFallbackFileBrowserHandler(task_descriptor))
       fallback_handler = handler;
   }
 
@@ -643,7 +638,9 @@
   file_paths.push_back(file_path);
 
   FileBrowserHandlerList default_handlers =
-      FindDefaultFileBrowserHandlers(profile, file_paths, common_handlers);
+      FindDefaultFileBrowserHandlers(*profile->GetPrefs(),
+                                     file_paths,
+                                     common_handlers);
 
   // If there's none, or more than one, then we don't have a canonical default.
   if (!default_handlers.empty()) {
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_browser_handlers.h b/chrome/browser/chromeos/extensions/file_manager/file_browser_handlers.h
index 87b5c92..5c446ee 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_browser_handlers.h
+++ b/chrome/browser/chromeos/extensions/file_manager/file_browser_handlers.h
@@ -17,6 +17,7 @@
 
 class FileBrowserHandler;
 class GURL;
+class PrefService;
 class Profile;
 
 namespace base {
@@ -37,16 +38,16 @@
 // Tasks are stored as a vector in order of priorities.
 typedef std::vector<const FileBrowserHandler*> FileBrowserHandlerList;
 
-// Returns true if the given file browser handler should be used as a
-// fallback. Such handlers are Files.app's internal handlers as well as quick
-// office extensions.
-bool IsFallbackFileBrowserHandler(const FileBrowserHandler* handler);
+// Returns true if the given task is a fallback file browser handler. Such
+// handlers are Files.app's internal handlers as well as quick office
+// extensions.
+bool IsFallbackFileBrowserHandler(const file_tasks::TaskDescriptor& task);
 
 // Finds file browser handlers set as default from |common_tasks| for
 // |file_list|. If no handlers are set as default, choose the the firstly
 // found fallback handler as default.
 FileBrowserHandlerList FindDefaultFileBrowserHandlers(
-    Profile* profile,
+    const PrefService& pref_service,
     const std::vector<base::FilePath>& file_list,
     const FileBrowserHandlerList& common_tasks);
 
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.cc b/chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.cc
index 6a39f9e..91757e7 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.cc
@@ -23,52 +23,82 @@
   ExtensionFunctionRegistry* registry =
       ExtensionFunctionRegistry::GetInstance();
   // Tasks related functions.
-  registry->RegisterFunction<ExecuteTaskFunction>();
-  registry->RegisterFunction<GetFileTasksFunction>();
-  registry->RegisterFunction<SetDefaultTaskFunction>();
-  registry->RegisterFunction<ViewFilesFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateExecuteTaskFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateGetFileTasksFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateSetDefaultTaskFunction>();
 
   // Drive related functions.
-  registry->RegisterFunction<GetDriveEntryPropertiesFunction>();
-  registry->RegisterFunction<PinDriveFileFunction>();
-  registry->RegisterFunction<GetDriveFilesFunction>();
-  registry->RegisterFunction<CancelFileTransfersFunction>();
-  registry->RegisterFunction<SearchDriveFunction>();
-  registry->RegisterFunction<SearchDriveMetadataFunction>();
-  registry->RegisterFunction<ClearDriveCacheFunction>();
-  registry->RegisterFunction<GetDriveConnectionStateFunction>();
-  registry->RegisterFunction<RequestAccessTokenFunction>();
-  registry->RegisterFunction<GetShareUrlFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateGetDriveEntryPropertiesFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivatePinDriveFileFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateGetDriveFilesFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateCancelFileTransfersFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateSearchDriveFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateSearchDriveMetadataFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateClearDriveCacheFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateGetDriveConnectionStateFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateRequestAccessTokenFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateGetShareUrlFunction>();
 
   // Select file dialog related functions.
-  registry->RegisterFunction<CancelFileDialogFunction>();
-  registry->RegisterFunction<SelectFileFunction>();
-  registry->RegisterFunction<SelectFilesFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateCancelDialogFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateSelectFileFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateSelectFilesFunction>();
 
   // Mount points related functions.
-  registry->RegisterFunction<AddMountFunction>();
-  registry->RegisterFunction<RemoveMountFunction>();
-  registry->RegisterFunction<GetMountPointsFunction>();
+  registry->RegisterFunction<extensions::FileBrowserPrivateAddMountFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateRemoveMountFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateGetMountPointsFunction>();
 
   // Hundreds of strings for the file manager.
-  registry->RegisterFunction<GetStringsFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateGetStringsFunction>();
 
   // File system related functions.
-  registry->RegisterFunction<RequestFileSystemFunction>();
-  registry->RegisterFunction<AddFileWatchFunction>();
-  registry->RegisterFunction<RemoveFileWatchFunction>();
-  registry->RegisterFunction<SetLastModifiedFunction>();
-  registry->RegisterFunction<GetSizeStatsFunction>();
-  registry->RegisterFunction<GetVolumeMetadataFunction>();
-  registry->RegisterFunction<ValidatePathNameLengthFunction>();
-  registry->RegisterFunction<FormatDeviceFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateRequestFileSystemFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateAddFileWatchFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateRemoveFileWatchFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateSetLastModifiedFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateGetSizeStatsFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateGetVolumeMetadataFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateValidatePathNameLengthFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateFormatDeviceFunction>();
 
   // Miscellaneous functions.
-  registry->RegisterFunction<LogoutUserFunction>();
-  registry->RegisterFunction<GetPreferencesFunction>();
-  registry->RegisterFunction<SetPreferencesFunction>();
-  registry->RegisterFunction<ZipSelectionFunction>();
-  registry->RegisterFunction<ZoomFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateLogoutUserFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateGetPreferencesFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateSetPreferencesFunction>();
+  registry->RegisterFunction<
+    extensions::FileBrowserPrivateZipSelectionFunction>();
+  registry->RegisterFunction<extensions::FileBrowserPrivateZoomFunction>();
   event_router_->ObserveFileSystemEvents();
 }
 
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/extensions/file_manager/file_manager_browsertest.cc
index 9e370d6..618f1a1 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_browsertest.cc
@@ -62,32 +62,68 @@
 }
 
 struct TestEntryInfo {
+  TestEntryInfo(EntryType type,
+                const std::string& source_file_name,
+                const std::string& target_name,
+                const std::string& mime_type,
+                SharedOption shared_option,
+                const base::Time& last_modified_time) :
+      type(type),
+      source_file_name(source_file_name),
+      target_name(target_name),
+      mime_type(mime_type),
+      shared_option(shared_option),
+      last_modified_time(last_modified_time) {
+  }
+
   EntryType type;
-  const char* source_file_name;  // Source file name to be used as a prototype.
-  const char* target_name;  // Target file or directory name.
-  const char* mime_type;
+  std::string source_file_name;  // Source file name to be used as a prototype.
+  std::string target_name;  // Target file or directory name.
+  std::string mime_type;
   SharedOption shared_option;
-  const char* last_modified_time_as_string;
+  base::Time last_modified_time;
 };
 
-TestEntryInfo kTestEntrySetCommon[] = {
-  { FILE, "text.txt", "hello.txt", "text/plain", NONE, "4 Sep 1998 12:34:56" },
-  { FILE, "image.png", "My Desktop Background.png", "text/plain", NONE,
-    "18 Jan 2038 01:02:03" },
-  { FILE, "music.ogg", "Beautiful Song.ogg", "text/plain", NONE,
-    "12 Nov 2086 12:00:00" },
-  { FILE, "video.ogv", "world.ogv", "text/plain", NONE,
-    "4 July 2012 10:35:00" },
-  { DIRECTORY, "", "photos", NULL, NONE, "1 Jan 1980 23:59:59" },
-  { DIRECTORY, "", ".warez", NULL, NONE, "26 Oct 1985 13:39" }
-};
+// Create the test entry data for common use.
+std::vector<TestEntryInfo> createTestEntrySetCommon() {
+  std::vector<TestEntryInfo> entryInfoSet;
+  base::Time time;
+  base::Time::FromString("4 Sep 1998 12:34:56", &time);
+  entryInfoSet.push_back(TestEntryInfo(
+      FILE, "text.txt", "hello.txt", "text/plain", NONE, time));
+  base::Time::FromString("18 Jan 2038 01:02:03", &time);
+  entryInfoSet.push_back(TestEntryInfo(
+      FILE, "image.png", "My Desktop Background.png", "text/plain", NONE,
+      time));
+  base::Time::FromString("12 Nov 2086 12:00:00", &time);
+  entryInfoSet.push_back(TestEntryInfo(
+      FILE, "music.ogg", "Beautiful Song.ogg", "text/plain", NONE, time));
+  base::Time::FromString("4 July 2012 10:35:00", &time);
+  entryInfoSet.push_back(TestEntryInfo(
+      FILE, "video.ogv", "world.ogv", "text/plain", NONE, time));
+  base::Time::FromString("1 Jan 1980 23:59:59", &time);
+  entryInfoSet.push_back(TestEntryInfo(
+      DIRECTORY, "", "photos", "", NONE, time));
+  base::Time::FromString("26 Oct 1985 13:39", &time);
+  entryInfoSet.push_back(TestEntryInfo(
+      DIRECTORY, "", ".warez", "", NONE, time));
+  return entryInfoSet;
+}
 
-TestEntryInfo kTestEntrySetDriveOnly[] = {
-  { FILE, "", "Test Document", "application/vnd.google-apps.document", NONE,
-    "10 Apr 2013 16:20:00" },
-  { FILE, "", "Test Shared Document", "application/vnd.google-apps.document",
-    SHARED, "20 Mar 2013 22:40:00" }
-};
+// Create the test entry data for the drive volume.
+std::vector<TestEntryInfo> createTestEntrySetDriveOnly() {
+  std::vector<TestEntryInfo> entryInfoSet;
+  base::Time time;
+  base::Time::FromString("10 Apr 2013 16:20:00", &time);
+  entryInfoSet.push_back(TestEntryInfo(
+      FILE, "", "Test Document", "application/vnd.google-apps.document", NONE,
+      time));
+  base::Time::FromString("20 Mar 2013 22:40:00", &time);
+  entryInfoSet.push_back(TestEntryInfo(
+      FILE, "", "Test Shared Document", "application/vnd.google-apps.document",
+      SHARED, time));
+  return entryInfoSet;
+}
 
 // The local volume class for test.
 // This class provides the operations for a test volume that simulates local
@@ -130,10 +166,8 @@
             "Failed to create a directory: " << target_path.value();
         break;
     }
-    base::Time time;
-    ASSERT_TRUE(base::Time::FromString(entry.last_modified_time_as_string,
-                                       &time));
-    ASSERT_TRUE(file_util::SetLastModifiedTime(target_path, time));
+    ASSERT_TRUE(
+        file_util::SetLastModifiedTime(target_path, entry.last_modified_time));
   }
 
  private:
@@ -169,17 +203,17 @@
                    entry.target_name,
                    entry.mime_type,
                    entry.shared_option == SHARED,
-                   entry.last_modified_time_as_string);
+                   entry.last_modified_time);
         break;
       case DIRECTORY:
-        CreateDirectory(entry.target_name, entry.last_modified_time_as_string);
+        CreateDirectory(entry.target_name, entry.last_modified_time);
         break;
     }
   }
 
   // Creates an empty directory with the given |name| and |modification_time|.
   void CreateDirectory(const std::string& name,
-                       const std::string& modification_time) {
+                       const base::Time& modification_time) {
     google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
     scoped_ptr<google_apis::ResourceEntry> resource_entry;
     fake_drive_service_->AddNewDirectory(
@@ -191,11 +225,9 @@
     ASSERT_TRUE(error == google_apis::HTTP_CREATED);
     ASSERT_TRUE(resource_entry);
 
-    base::Time time;
-    ASSERT_TRUE(base::Time::FromString(modification_time.c_str(), &time));
     fake_drive_service_->SetLastModifiedTime(
         resource_entry->resource_id(),
-        time,
+        modification_time,
         google_apis::test_util::CreateCopyResultCallback(&error,
                                                          &resource_entry));
     base::MessageLoop::current()->RunUntilIdle();
@@ -210,7 +242,7 @@
                   const std::string& target_file_name,
                   const std::string& mime_type,
                   bool shared_with_me,
-                  const std::string& modification_time) {
+                  const base::Time& modification_time) {
     google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
 
     std::string content_data;
@@ -234,11 +266,9 @@
     ASSERT_EQ(google_apis::HTTP_CREATED, error);
     ASSERT_TRUE(resource_entry);
 
-    base::Time time;
-    ASSERT_TRUE(base::Time::FromString(modification_time.c_str(), &time));
     fake_drive_service_->SetLastModifiedTime(
         resource_entry->resource_id(),
-        time,
+        modification_time,
         google_apis::test_util::CreateCopyResultCallback(&error,
                                                          &resource_entry));
     base::MessageLoop::current()->RunUntilIdle();
@@ -381,8 +411,13 @@
   ExtensionApiTest::SetUpOnMainThread();
   ASSERT_TRUE(local_volume_->Mount(browser()->profile()));
 
-  for (size_t i = 0; i < arraysize(kTestEntrySetCommon); ++i)
-    local_volume_->CreateEntry(kTestEntrySetCommon[i]);
+  const std::vector<TestEntryInfo> testEntrySetCommon(
+      createTestEntrySetCommon());
+  const std::vector<TestEntryInfo> testEntrySetDriveOnly(
+      createTestEntrySetDriveOnly());
+
+  for (size_t i = 0; i < testEntrySetCommon.size(); ++i)
+    local_volume_->CreateEntry(testEntrySetCommon[i]);
 
   if (drive_volume_) {
     // Install the web server to serve the mocked share dialog.
@@ -391,13 +426,13 @@
         "/chromeos/file_manager/share_dialog_mock/index.html"));
     drive_volume_->ConfigureShareUrlBase(share_url_base);
 
-    for (size_t i = 0; i < arraysize(kTestEntrySetCommon); ++i)
-      drive_volume_->CreateEntry(kTestEntrySetCommon[i]);
+    for (size_t i = 0; i < testEntrySetCommon.size(); ++i)
+      drive_volume_->CreateEntry(testEntrySetCommon[i]);
 
     // For testing Drive, create more entries with Drive specific attributes.
     // TODO(haruki): Add a case for an entry cached by DriveCache.
-    for (size_t i = 0; i < arraysize(kTestEntrySetDriveOnly); ++i)
-      drive_volume_->CreateEntry(kTestEntrySetDriveOnly[i]);
+    for (size_t i = 0; i < testEntrySetDriveOnly.size(); ++i)
+      drive_volume_->CreateEntry(testEntrySetDriveOnly[i]);
 
     test_util::WaitUntilDriveMountPointIsAdded(browser()->profile());
   }
@@ -438,14 +473,15 @@
       entry.function->Reply(std::tr1::get<0>(GetParam()) ? "true" : "false");
     } else if (entry.message == "addEntry") {
       // Add the extra entry.
-      const TestEntryInfo file = {
-        FILE,
-        "music.ogg",  // Prototype file name.
-        "newly added file.ogg",  // Target file name.
-        "audio/ogg",
-        NONE,
-        "4 Sep 1998 00:00:00"
-      };
+      base::Time time;
+      ASSERT_TRUE(base::Time::FromString("4 Sep 1998 00:00:00", &time));
+      const TestEntryInfo file(
+          FILE,
+          "music.ogg",  // Prototype file name.
+          "newly added file.ogg",  // Target file name.
+          "audio/ogg",
+          NONE,
+          time);
       if (drive_volume_)
         drive_volume_->CreateEntry(file);
       local_volume_->CreateEntry(file);
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_util.cc b/chrome/browser/chromeos/extensions/file_manager/file_manager_util.cc
deleted file mode 100644
index 48702c4..0000000
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_util.cc
+++ /dev/null
@@ -1,595 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/logging.h"
-#include "base/metrics/histogram.h"
-#include "base/path_service.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/threading/sequenced_worker_pool.h"
-#include "chrome/browser/chromeos/drive/drive.pb.h"
-#include "chrome/browser/chromeos/drive/drive_integration_service.h"
-#include "chrome/browser/chromeos/drive/file_system.h"
-#include "chrome/browser/chromeos/drive/file_system_util.h"
-#include "chrome/browser/chromeos/extensions/file_manager/app_id.h"
-#include "chrome/browser/chromeos/extensions/file_manager/file_browser_handlers.h"
-#include "chrome/browser/chromeos/extensions/file_manager/file_tasks.h"
-#include "chrome/browser/chromeos/extensions/file_manager/fileapi_util.h"
-#include "chrome/browser/chromeos/extensions/file_manager/url_util.h"
-#include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
-#include "chrome/browser/extensions/crx_installer.h"
-#include "chrome/browser/extensions/extension_install_prompt.h"
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_system.h"
-#include "chrome/browser/google_apis/task_util.h"
-#include "chrome/browser/plugins/plugin_prefs.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/browser_tabstrip.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/extensions/application_launch.h"
-#include "chrome/browser/ui/simple_message_box.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/common/extensions/api/file_browser_handlers/file_browser_handler.h"
-#include "chrome/common/url_constants.h"
-#include "chromeos/chromeos_switches.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/plugin_service.h"
-#include "content/public/browser/storage_partition.h"
-#include "content/public/browser/user_metrics.h"
-#include "content/public/common/pepper_plugin_info.h"
-#include "content/public/common/webplugininfo.h"
-#include "grit/generated_resources.h"
-#include "net/base/escape.h"
-#include "net/base/mime_util.h"
-#include "net/base/net_util.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "webkit/browser/fileapi/file_system_backend.h"
-#include "webkit/browser/fileapi/file_system_context.h"
-#include "webkit/browser/fileapi/file_system_operation_runner.h"
-#include "webkit/browser/fileapi/file_system_url.h"
-
-using content::BrowserContext;
-using content::BrowserThread;
-using content::PluginService;
-using content::UserMetricsAction;
-using extensions::Extension;
-using extensions::app_file_handler_util::FindFileHandlersForFiles;
-using extensions::app_file_handler_util::PathAndMimeTypeSet;
-using fileapi::FileSystemURL;
-
-namespace file_manager {
-namespace util {
-namespace {
-
-const base::FilePath::CharType kCRXExtension[] = FILE_PATH_LITERAL(".crx");
-const base::FilePath::CharType kPdfExtension[] = FILE_PATH_LITERAL(".pdf");
-const base::FilePath::CharType kSwfExtension[] = FILE_PATH_LITERAL(".swf");
-
-// List of file extensions viewable in the browser.
-const base::FilePath::CharType* kFileExtensionsViewableInBrowser[] = {
-#if defined(GOOGLE_CHROME_BUILD)
-  FILE_PATH_LITERAL(".pdf"),
-  FILE_PATH_LITERAL(".swf"),
-#endif
-  FILE_PATH_LITERAL(".bmp"),
-  FILE_PATH_LITERAL(".jpg"),
-  FILE_PATH_LITERAL(".jpeg"),
-  FILE_PATH_LITERAL(".png"),
-  FILE_PATH_LITERAL(".webp"),
-  FILE_PATH_LITERAL(".gif"),
-  FILE_PATH_LITERAL(".txt"),
-  FILE_PATH_LITERAL(".html"),
-  FILE_PATH_LITERAL(".htm"),
-  FILE_PATH_LITERAL(".mhtml"),
-  FILE_PATH_LITERAL(".mht"),
-  FILE_PATH_LITERAL(".svg"),
-};
-
-// Returns true if |file_path| is viewable in the browser (ex. HTML file).
-bool IsViewableInBrowser(const base::FilePath& file_path) {
-  for (size_t i = 0; i < arraysize(kFileExtensionsViewableInBrowser); i++) {
-    if (file_path.MatchesExtension(kFileExtensionsViewableInBrowser[i]))
-      return true;
-  }
-  return false;
-}
-
-bool IsPepperPluginEnabled(Profile* profile,
-                           const base::FilePath& plugin_path) {
-  content::PepperPluginInfo* pepper_info =
-      PluginService::GetInstance()->GetRegisteredPpapiPluginInfo(plugin_path);
-  if (!pepper_info)
-    return false;
-
-  scoped_refptr<PluginPrefs> plugin_prefs = PluginPrefs::GetForProfile(profile);
-  if (!plugin_prefs.get())
-    return false;
-
-  return plugin_prefs->IsPluginEnabled(pepper_info->ToWebPluginInfo());
-}
-
-bool IsPdfPluginEnabled(Profile* profile) {
-  base::FilePath plugin_path;
-  PathService::Get(chrome::FILE_PDF_PLUGIN, &plugin_path);
-  return IsPepperPluginEnabled(profile, plugin_path);
-}
-
-bool IsFlashPluginEnabled(Profile* profile) {
-  base::FilePath plugin_path(
-      CommandLine::ForCurrentProcess()->GetSwitchValueNative(
-          switches::kPpapiFlashPath));
-  if (plugin_path.empty())
-    PathService::Get(chrome::FILE_PEPPER_FLASH_PLUGIN, &plugin_path);
-  return IsPepperPluginEnabled(profile, plugin_path);
-}
-
-void OpenNewTab(Profile* profile, const GURL& url) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  Browser* browser = chrome::FindOrCreateTabbedBrowser(
-      profile ? profile : ProfileManager::GetDefaultProfileOrOffTheRecord(),
-      chrome::HOST_DESKTOP_TYPE_ASH);
-  chrome::AddSelectedTabWithURL(browser, url, content::PAGE_TRANSITION_LINK);
-  // If the current browser is not tabbed then the new tab will be created
-  // in a different browser. Make sure it is visible.
-  browser->window()->Show();
-}
-
-// Shows a warning message box saying that the file could not be opened.
-void ShowWarningMessageBox(Profile* profile, const base::FilePath& file_path) {
-  // TODO: if FindOrCreateTabbedBrowser creates a new browser the returned
-  // browser is leaked.
-  Browser* browser =
-      chrome::FindOrCreateTabbedBrowser(profile,
-                                        chrome::HOST_DESKTOP_TYPE_ASH);
-  chrome::ShowMessageBox(
-      browser->window()->GetNativeWindow(),
-      l10n_util::GetStringFUTF16(
-          IDS_FILE_BROWSER_ERROR_VIEWING_FILE_TITLE,
-          UTF8ToUTF16(file_path.BaseName().value())),
-      l10n_util::GetStringUTF16(IDS_FILE_BROWSER_ERROR_VIEWING_FILE),
-      chrome::MESSAGE_BOX_TYPE_WARNING);
-}
-
-void InstallCRX(Browser* browser, const base::FilePath& file_path) {
-  ExtensionService* service =
-      extensions::ExtensionSystem::Get(browser->profile())->extension_service();
-  CHECK(service);
-
-  scoped_refptr<extensions::CrxInstaller> installer(
-      extensions::CrxInstaller::Create(
-          service,
-          scoped_ptr<ExtensionInstallPrompt>(new ExtensionInstallPrompt(
-              browser->profile(), NULL, NULL))));
-  installer->set_error_on_unsupported_requirements(true);
-  installer->set_is_gallery_install(false);
-  installer->set_allow_silent_install(false);
-  installer->InstallCrx(file_path);
-}
-
-// Called when a crx file on Drive was downloaded.
-void OnCRXDownloadCallback(Browser* browser,
-                           drive::FileError error,
-                           const base::FilePath& file,
-                           scoped_ptr<drive::ResourceEntry> entry) {
-  if (error != drive::FILE_ERROR_OK)
-    return;
-  InstallCRX(browser, file);
-}
-
-// Grants file system access to the file manager.
-bool GrantFileSystemAccessToFileBrowser(Profile* profile) {
-  // The file manager always runs in the site for its extension id, so that
-  // is the site for which file access permissions should be granted.
-  GURL site = extensions::ExtensionSystem::Get(profile)->extension_service()->
-      GetSiteForExtensionId(kFileManagerAppId);
-  fileapi::ExternalFileSystemBackend* backend =
-      BrowserContext::GetStoragePartitionForSite(profile, site)->
-          GetFileSystemContext()->external_backend();
-  if (!backend)
-    return false;
-  backend->GrantFullAccessToExtension(kFileManagerAppId);
-  return true;
-}
-
-// Executes the |task| for the file specified by |url|.
-void ExecuteFileTaskForUrl(Profile* profile,
-                           const file_tasks::TaskDescriptor& task,
-                           const GURL& url) {
-  // If the file manager has not been open yet then it did not request access
-  // to the file system. Do it now.
-  if (!GrantFileSystemAccessToFileBrowser(profile))
-    return;
-
-  fileapi::FileSystemContext* file_system_context =
-      GetFileSystemContextForExtensionId(
-          profile, kFileManagerAppId);
-
-  // We are executing the task on behalf of the file manager.
-  const GURL source_url = GetFileManagerMainPageUrl();
-  std::vector<FileSystemURL> urls;
-  urls.push_back(file_system_context->CrackURL(url));
-
-  file_tasks::ExecuteFileTask(
-      profile,
-      source_url,
-      kFileManagerAppId,
-      0, // no tab id
-      task,
-      urls,
-      file_tasks::FileTaskFinishedCallback());
-}
-
-// Opens the file manager for the specified |file_path|. Used to implement
-// internal handlers of special action IDs:
-//
-// "open" - Open the file manager for the given folder.
-// "auto-open" - Open the file manager for the given removal drive and close
-//               the file manager when the removal drive is unmounted.
-// "select" - Open the file manager for the given file. The folder containing
-//            the file will be opened with the file selected.
-void OpenFileManagerWithInternalActionId(const base::FilePath& file_path,
-                                         const std::string& action_id) {
-  DCHECK(action_id == "auto-open" ||
-         action_id == "open" ||
-         action_id == "select");
-
-  content::RecordAction(UserMetricsAction("ShowFileBrowserFullTab"));
-  Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord();
-
-  GURL url;
-  if (!ConvertAbsoluteFilePathToFileSystemUrl(
-          profile, file_path, kFileManagerAppId, &url))
-    return;
-
-  file_tasks::TaskDescriptor task(kFileManagerAppId,
-                                  file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER,
-                                  action_id);
-  ExecuteFileTaskForUrl(profile, task, url);
-}
-
-// Opens the file specified by |file_path| and |url| with a file handler,
-// preferably the default handler for the type of the file.  Returns false if
-// no file handler is found.
-bool OpenFileWithFileHandler(Profile* profile,
-                              const base::FilePath& file_path,
-                              const GURL& url,
-                              const std::string& mime_type,
-                              const std::string& default_task_id) {
-  ExtensionService* service = profile->GetExtensionService();
-  if (!service)
-    return false;
-
-  PathAndMimeTypeSet files;
-  files.insert(std::make_pair(file_path, mime_type));
-  const extensions::FileHandlerInfo* first_handler = NULL;
-  const extensions::Extension* extension_for_first_handler = NULL;
-
-  // If we find the default handler, we execute it immediately, but otherwise,
-  // we remember the first handler, and if there was no default handler, simply
-  // execute the first one.
-  for (ExtensionSet::const_iterator iter = service->extensions()->begin();
-       iter != service->extensions()->end();
-       ++iter) {
-    const Extension* extension = iter->get();
-
-    // We don't support using hosted apps to open files.
-    if (!extension->is_platform_app())
-      continue;
-
-    // We only support apps that specify "incognito: split" if in incognito
-    // mode.
-    if (profile->IsOffTheRecord() &&
-        !service->IsIncognitoEnabled(extension->id()))
-      continue;
-
-    typedef std::vector<const extensions::FileHandlerInfo*> FileHandlerList;
-    FileHandlerList file_handlers = FindFileHandlersForFiles(*extension, files);
-    for (FileHandlerList::iterator i = file_handlers.begin();
-         i != file_handlers.end(); ++i) {
-      const extensions::FileHandlerInfo* handler = *i;
-      std::string task_id = file_tasks::MakeTaskID(
-          extension->id(),
-          file_tasks::TASK_TYPE_FILE_HANDLER,
-          handler->id);
-      if (task_id == default_task_id) {
-        file_tasks::TaskDescriptor task(extension->id(),
-                                        file_tasks::TASK_TYPE_FILE_HANDLER,
-                                        handler->id);
-        ExecuteFileTaskForUrl(profile, task, url);
-        return true;
-
-      } else if (!first_handler) {
-        first_handler = handler;
-        extension_for_first_handler = extension;
-      }
-    }
-  }
-  if (first_handler) {
-    file_tasks::TaskDescriptor task(extension_for_first_handler->id(),
-                                    file_tasks::TASK_TYPE_FILE_HANDLER,
-                                    first_handler->id);
-    ExecuteFileTaskForUrl(profile, task, url);
-    return true;
-  }
-  return false;
-}
-
-// Opens the file specified by |file_path| and |url| with the file browser
-// handler specified by |handler|. Returns false if failed to open the file.
-bool OpenFileWithFileBrowserHandler(Profile* profile,
-                                    const base::FilePath& file_path,
-                                    const FileBrowserHandler& handler,
-                                    const GURL& url) {
-  file_tasks::TaskDescriptor task(handler.extension_id(),
-                                  file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER,
-                                  handler.id());
-  ExecuteFileTaskForUrl(profile, task, url);
-  return true;
-}
-
-// Opens the file specified by |file_path| with a handler (either of file
-// browser handler or file handler, preferably the default handler for the
-// type of the file), or opens the file with the browser. Returns false if
-// failed to open the file.
-bool OpenFileWithHandler(Profile* profile, const base::FilePath& file_path) {
-  GURL url;
-  if (!ConvertAbsoluteFilePathToFileSystemUrl(
-          profile, file_path, kFileManagerAppId, &url))
-    return false;
-
-  std::string mime_type = GetMimeTypeForPath(file_path);
-  std::string default_task_id = file_tasks::GetDefaultTaskIdFromPrefs(
-      profile, mime_type, file_path.Extension());
-
-  // We choose the file handler from the following in decreasing priority or
-  // fail if none support the file type:
-  // 1. default file browser handler
-  // 2. default file handler
-  // 3. a fallback handler (e.g. opening in the browser)
-  // 4. non-default file handler
-  // 5. non-default file browser handler
-  // Note that there can be at most one of default extension and default app.
-  const FileBrowserHandler* handler =
-      file_browser_handlers::FindFileBrowserHandlerForURLAndPath(
-          profile, url, file_path);
-  if (!handler) {
-    return OpenFileWithFileHandler(
-        profile, file_path, url, mime_type, default_task_id);
-  }
-
-  std::string handler_task_id = file_tasks::MakeTaskID(
-        handler->extension_id(),
-        file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER,
-        handler->id());
-  if (handler_task_id != default_task_id &&
-      !file_browser_handlers::IsFallbackFileBrowserHandler(handler) &&
-      OpenFileWithFileHandler(
-          profile, file_path, url, mime_type, default_task_id)) {
-    return true;
-  }
-  return OpenFileWithFileBrowserHandler(profile, file_path, *handler, url);
-}
-
-// Reads the alternate URL from a GDoc file. When it fails, returns a file URL
-// for |file_path| as fallback.
-// Note that an alternate url is a URL to open a hosted document.
-GURL ReadUrlFromGDocOnBlockingPool(const base::FilePath& file_path) {
-  GURL url = drive::util::ReadUrlFromGDocFile(file_path);
-  if (url.is_empty())
-    url = net::FilePathToFileURL(file_path);
-  return url;
-}
-
-// Used to implement OpenItem().
-void ContinueOpenItem(Profile* profile,
-                      const base::FilePath& file_path,
-                      base::PlatformFileError error) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  if (error == base::PLATFORM_FILE_OK) {
-    // A directory exists at |file_path|. Open it with the file manager.
-    OpenFileManagerWithInternalActionId(file_path, "open");
-  } else {
-    // |file_path| should be a file. Open it with a handler.
-    if (!OpenFileWithHandler(profile, file_path))
-      ShowWarningMessageBox(profile, file_path);
-  }
-}
-
-// Used to implement CheckIfDirectoryExists().
-void CheckIfDirectoryExistsOnIOThread(
-    scoped_refptr<fileapi::FileSystemContext> file_system_context,
-    const GURL& url,
-    const fileapi::FileSystemOperationRunner::StatusCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-
-  fileapi::FileSystemURL file_system_url = file_system_context->CrackURL(url);
-  file_system_context->operation_runner()->DirectoryExists(
-      file_system_url, callback);
-}
-
-// Checks if a directory exists at |url|.
-void CheckIfDirectoryExists(
-    scoped_refptr<fileapi::FileSystemContext> file_system_context,
-    const GURL& url,
-    const fileapi::FileSystemOperationRunner::StatusCallback& callback) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  BrowserThread::PostTask(
-      BrowserThread::IO, FROM_HERE,
-      base::Bind(&CheckIfDirectoryExistsOnIOThread,
-                 file_system_context,
-                 url,
-                 google_apis::CreateRelayCallback(callback)));
-}
-
-}  // namespace
-
-string16 GetTitleFromType(ui::SelectFileDialog::Type dialog_type) {
-  string16 title;
-  switch (dialog_type) {
-    case ui::SelectFileDialog::SELECT_NONE:
-      // Full page file manager doesn't need a title.
-      break;
-
-    case ui::SelectFileDialog::SELECT_FOLDER:
-      title = l10n_util::GetStringUTF16(
-          IDS_FILE_BROWSER_SELECT_FOLDER_TITLE);
-      break;
-
-    case ui::SelectFileDialog::SELECT_UPLOAD_FOLDER:
-      title = l10n_util::GetStringUTF16(
-          IDS_FILE_BROWSER_SELECT_UPLOAD_FOLDER_TITLE);
-      break;
-
-    case ui::SelectFileDialog::SELECT_SAVEAS_FILE:
-      title = l10n_util::GetStringUTF16(
-          IDS_FILE_BROWSER_SELECT_SAVEAS_FILE_TITLE);
-      break;
-
-    case ui::SelectFileDialog::SELECT_OPEN_FILE:
-      title = l10n_util::GetStringUTF16(
-          IDS_FILE_BROWSER_SELECT_OPEN_FILE_TITLE);
-      break;
-
-    case ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE:
-      title = l10n_util::GetStringUTF16(
-          IDS_FILE_BROWSER_SELECT_OPEN_MULTI_FILE_TITLE);
-      break;
-
-    default:
-      NOTREACHED();
-  }
-
-  return title;
-}
-
-void OpenRemovableDrive(const base::FilePath& file_path) {
-  OpenFileManagerWithInternalActionId(file_path, "auto-open");
-}
-
-void OpenItem(const base::FilePath& file_path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord();
-  GURL url;
-  if (!ConvertAbsoluteFilePathToFileSystemUrl(
-          profile, file_path, kFileManagerAppId, &url) ||
-      !GrantFileSystemAccessToFileBrowser(profile)) {
-    ShowWarningMessageBox(profile, file_path);
-    return;
-  }
-
-  scoped_refptr<fileapi::FileSystemContext> file_system_context =
-      GetFileSystemContextForExtensionId(
-          profile, kFileManagerAppId);
-
-  CheckIfDirectoryExists(file_system_context, url,
-                         base::Bind(&ContinueOpenItem, profile, file_path));
-}
-
-void ShowItemInFolder(const base::FilePath& file_path) {
-  // This action changes the selection so we do not reuse existing tabs.
-  OpenFileManagerWithInternalActionId(file_path, "select");
-}
-
-bool OpenFileWithBrowser(Browser* browser, const base::FilePath& file_path) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  Profile* profile = browser->profile();
-  // For things supported natively by the browser, we should open it
-  // in a tab.
-  if (IsViewableInBrowser(file_path) ||
-      ShouldBeOpenedWithPlugin(profile, file_path.Extension())) {
-    GURL page_url = net::FilePathToFileURL(file_path);
-    // Override drive resource to point to internal handler instead of file URL.
-    if (drive::util::IsUnderDriveMountPoint(file_path)) {
-      page_url = drive::util::FilePathToDriveURL(
-          drive::util::ExtractDrivePath(file_path));
-    }
-    OpenNewTab(profile, page_url);
-    return true;
-  }
-
-  if (drive::util::HasGDocFileExtension(file_path)) {
-    if (drive::util::IsUnderDriveMountPoint(file_path)) {
-      // The file is on Google Docs. Open with drive URL.
-      GURL url = drive::util::FilePathToDriveURL(
-          drive::util::ExtractDrivePath(file_path));
-      OpenNewTab(profile, url);
-    } else {
-      // The file is local (downloaded from an attachment or otherwise copied).
-      // Parse the file to extract the Docs url and open this url.
-      base::PostTaskAndReplyWithResult(
-          BrowserThread::GetBlockingPool(),
-          FROM_HERE,
-          base::Bind(&ReadUrlFromGDocOnBlockingPool, file_path),
-          base::Bind(&OpenNewTab, static_cast<Profile*>(NULL)));
-    }
-    return true;
-  }
-
-  if (file_path.MatchesExtension(kCRXExtension)) {
-    if (drive::util::IsUnderDriveMountPoint(file_path)) {
-      drive::DriveIntegrationService* integration_service =
-          drive::DriveIntegrationServiceFactory::GetForProfile(profile);
-      if (!integration_service)
-        return false;
-      integration_service->file_system()->GetFileByPath(
-          drive::util::ExtractDrivePath(file_path),
-          base::Bind(&OnCRXDownloadCallback, browser));
-    } else {
-      InstallCRX(browser, file_path);
-    }
-    return true;
-  }
-
-  // Failed to open the file of unknown type.
-  LOG(WARNING) << "Unknown file type: " << file_path.value();
-  return false;
-}
-
-// If a bundled plugin is enabled, we should open pdf/swf files in a tab.
-bool ShouldBeOpenedWithPlugin(
-    Profile* profile,
-    const base::FilePath::StringType& file_extension) {
-  const base::FilePath file_path =
-      base::FilePath::FromUTF8Unsafe("dummy").AddExtension(file_extension);
-  if (file_path.MatchesExtension(kPdfExtension))
-    return IsPdfPluginEnabled(profile);
-  if (file_path.MatchesExtension(kSwfExtension))
-    return IsFlashPluginEnabled(profile);
-  return false;
-}
-
-std::string GetMimeTypeForPath(const base::FilePath& file_path) {
-  const base::FilePath::StringType file_extension =
-      StringToLowerASCII(file_path.Extension());
-
-  // TODO(thorogood): Rearchitect this call so it can run on the File thread;
-  // GetMimeTypeFromFile requires this on Linux. Right now, we use
-  // Chrome-level knowledge only.
-  std::string mime_type;
-  if (file_extension.empty() ||
-      !net::GetWellKnownMimeTypeFromExtension(file_extension.substr(1),
-                                              &mime_type)) {
-    // If the file doesn't have an extension or its mime-type cannot be
-    // determined, then indicate that it has the empty mime-type. This will
-    // only be matched if the Web Intents accepts "*" or "*/*".
-    return "";
-  } else {
-    return mime_type;
-  }
-}
-
-}  // namespace util
-}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_util.h b/chrome/browser/chromeos/extensions/file_manager/file_manager_util.h
deleted file mode 100644
index 681ef4c..0000000
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_util.h
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_FILE_MANAGER_UTIL_H_
-#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_FILE_MANAGER_UTIL_H_
-
-#include <string>
-
-#include "base/files/file_path.h"
-#include "ui/shell_dialogs/select_file_dialog.h"
-
-class Browser;
-class GURL;
-class Profile;
-
-// File manager helper methods.
-namespace file_manager {
-namespace util {
-
-// Get file dialog title string from its type.
-string16 GetTitleFromType(ui::SelectFileDialog::Type type);
-
-// Opens the file manager for the freshly mounted removable drive specified
-// by |file_path|.
-// If there is another file manager instance open, this call does nothing.
-// The mount event will cause the file manager to show the new drive in
-// the left panel.
-// If there is no file manager open, this call opens a new one pointing to
-// |file_path|. In this case the tab will automatically close on |file_path|
-// unmount.
-void OpenRemovableDrive(const base::FilePath& file_path);
-
-// Opens an item (file or directory). If the target is a directory, the
-// directory will be opened in the file manager. If the target is a file, the
-// file will be opened using a file handler, a file browser handler, or the
-// browser (open in a tab). The default handler has precedence over other
-// handlers, if defined for the type of the target file.
-void OpenItem(const base::FilePath& file_path);
-
-// Opens the file manager for the folder containing the item specified by
-// |file_path|, with the item selected.
-void ShowItemInFolder(const base::FilePath& file_path);
-
-// Opens the file specified by |file_path| with the browser. This function takes
-// care of the following intricacies:
-//
-// - If the file is a Drive hosted document, the hosted document will be
-//   opened in the browser by extracting the right URL for the file.
-// - If the file is a CRX file, the CRX file will be installed.
-// - If the file is on Drive, the file will be downloaded from Drive as
-//   needed.
-//
-// Returns false if failed to open. This happens if the file type is unknown.
-bool OpenFileWithBrowser(Browser* browser, const base::FilePath& file_path);
-
-// Checks whether a pepper plugin for |file_extension| is enabled.
-bool ShouldBeOpenedWithPlugin(
-    Profile* profile,
-    const base::FilePath::StringType& file_extension);
-
-// Returns the MIME type of |file_path|. Returns "" if the MIME type is
-// unknown.
-std::string GetMimeTypeForPath(const base::FilePath& file_path);
-
-}  // namespace util
-}  // namespace file_manager
-
-#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_FILE_MANAGER_UTIL_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_util_unittest.cc b/chrome/browser/chromeos/extensions/file_manager/file_manager_util_unittest.cc
deleted file mode 100644
index 4ecba07..0000000
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_util_unittest.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace file_manager {
-namespace util {
-
-TEST(FileManagerUtilTest, GetMimeTypeForPath) {
-  EXPECT_EQ("image/jpeg",
-            GetMimeTypeForPath(base::FilePath::FromUTF8Unsafe("foo.jpg")));
-  EXPECT_EQ("image/jpeg",
-            GetMimeTypeForPath(base::FilePath::FromUTF8Unsafe("FOO.JPG")));
-  EXPECT_EQ("",
-            GetMimeTypeForPath(base::FilePath::FromUTF8Unsafe("foo.zzz")));
-}
-
-}  // namespace util
-}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_tasks.cc b/chrome/browser/chromeos/extensions/file_manager/file_tasks.cc
index cc899fa..1cb331d 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_tasks.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_tasks.cc
@@ -6,22 +6,30 @@
 
 #include "apps/launcher.h"
 #include "base/bind.h"
+#include "base/prefs/pref_service.h"
 #include "base/strings/stringprintf.h"
+#include "chrome/browser/chromeos/drive/drive_app_registry.h"
+#include "chrome/browser/chromeos/drive/drive_integration_service.h"
 #include "chrome/browser/chromeos/drive/file_task_executor.h"
 #include "chrome/browser/chromeos/extensions/file_manager/file_browser_handlers.h"
-#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
 #include "chrome/browser/chromeos/extensions/file_manager/fileapi_util.h"
+#include "chrome/browser/chromeos/extensions/file_manager/open_util.h"
 #include "chrome/browser/chromeos/fileapi/file_system_backend.h"
 #include "chrome/browser/extensions/extension_host.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
 #include "chrome/browser/extensions/extension_system.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
+#include "chrome/common/extensions/api/file_browser_handlers/file_browser_handler.h"
 #include "chrome/common/pref_names.h"
 #include "webkit/browser/fileapi/file_system_context.h"
 #include "webkit/browser/fileapi/file_system_url.h"
 
 using extensions::Extension;
+using extensions::app_file_handler_util::FindFileHandlersForFiles;
 using fileapi::FileSystemURL;
 
 namespace file_manager {
@@ -39,6 +47,9 @@
 const char kFileHandlerTaskType[] = "app";
 const char kDriveAppTaskType[] = "drive";
 
+// Default icon path for drive docs.
+const char kDefaultIcon[] = "images/filetype_generic.png";
+
 // Converts a TaskType to a string.
 std::string TaskTypeToString(TaskType task_type) {
   switch (task_type) {
@@ -100,15 +111,39 @@
 
 }  // namespace
 
-void UpdateDefaultTask(Profile* profile,
+FullTaskDescriptor::FullTaskDescriptor(
+    const TaskDescriptor& task_descriptor,
+    const std::string& task_title,
+    const GURL& icon_url,
+    bool is_default)
+    : task_descriptor_(task_descriptor),
+      task_title_(task_title),
+      icon_url_(icon_url),
+      is_default_(is_default){
+}
+
+scoped_ptr<base::DictionaryValue>
+FullTaskDescriptor::AsDictionaryValue() const {
+  scoped_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue);
+  dictionary->SetString("taskId", TaskDescriptorToId(task_descriptor_));
+  if (!icon_url_.is_empty())
+    dictionary->SetString("iconUrl", icon_url_.spec());
+  dictionary->SetBoolean("driveApp",
+                         task_descriptor_.task_type == TASK_TYPE_DRIVE_APP);
+  dictionary->SetString("title", task_title_);
+  dictionary->SetBoolean("isDefault", is_default_);
+  return dictionary.Pass();
+}
+
+void UpdateDefaultTask(PrefService* pref_service,
                        const std::string& task_id,
                        const std::set<std::string>& suffixes,
                        const std::set<std::string>& mime_types) {
-  if (!profile || !profile->GetPrefs())
+  if (!pref_service)
     return;
 
   if (!mime_types.empty()) {
-    DictionaryPrefUpdate mime_type_pref(profile->GetPrefs(),
+    DictionaryPrefUpdate mime_type_pref(pref_service,
                                         prefs::kDefaultTasksByMimeType);
     for (std::set<std::string>::const_iterator iter = mime_types.begin();
         iter != mime_types.end(); ++iter) {
@@ -118,7 +153,7 @@
   }
 
   if (!suffixes.empty()) {
-    DictionaryPrefUpdate mime_type_pref(profile->GetPrefs(),
+    DictionaryPrefUpdate mime_type_pref(pref_service,
                                         prefs::kDefaultTasksBySuffix);
     for (std::set<std::string>::const_iterator iter = suffixes.begin();
         iter != suffixes.end(); ++iter) {
@@ -130,7 +165,7 @@
   }
 }
 
-std::string GetDefaultTaskIdFromPrefs(Profile* profile,
+std::string GetDefaultTaskIdFromPrefs(const PrefService& pref_service,
                                       const std::string& mime_type,
                                       const std::string& suffix) {
   VLOG(1) << "Looking for default for MIME type: " << mime_type
@@ -138,7 +173,7 @@
   std::string task_id;
   if (!mime_type.empty()) {
     const DictionaryValue* mime_task_prefs =
-        profile->GetPrefs()->GetDictionary(prefs::kDefaultTasksByMimeType);
+        pref_service.GetDictionary(prefs::kDefaultTasksByMimeType);
     DCHECK(mime_task_prefs);
     LOG_IF(ERROR, !mime_task_prefs) << "Unable to open MIME type prefs";
     if (mime_task_prefs &&
@@ -149,7 +184,7 @@
   }
 
   const DictionaryValue* suffix_task_prefs =
-      profile->GetPrefs()->GetDictionary(prefs::kDefaultTasksBySuffix);
+      pref_service.GetDictionary(prefs::kDefaultTasksBySuffix);
   DCHECK(suffix_task_prefs);
   LOG_IF(ERROR, !suffix_task_prefs) << "Unable to open suffix prefs";
   std::string lower_suffix = StringToLowerASCII(suffix);
@@ -159,11 +194,11 @@
   return task_id;
 }
 
-std::string MakeTaskID(const std::string& extension_id,
+std::string MakeTaskID(const std::string& app_id,
                        TaskType task_type,
                        const std::string& action_id) {
   return base::StringPrintf("%s|%s|%s",
-                            extension_id.c_str(),
+                            app_id.c_str(),
                             TaskTypeToString(task_type).c_str(),
                             action_id.c_str());
 }
@@ -172,6 +207,12 @@
   return MakeTaskID(app_id, TASK_TYPE_DRIVE_APP, "open-with");
 }
 
+std::string TaskDescriptorToId(const TaskDescriptor& task_descriptor) {
+  return MakeTaskID(task_descriptor.app_id,
+                    task_descriptor.task_type,
+                    task_descriptor.action_id);
+}
+
 bool ParseTaskID(const std::string& task_id, TaskDescriptor* task) {
   DCHECK(task);
 
@@ -261,5 +302,316 @@
   return false;
 }
 
+struct TaskInfo {
+  TaskInfo(const std::string& app_name, const GURL& icon_url)
+      : app_name(app_name), icon_url(icon_url) {
+  }
+
+  std::string app_name;
+  GURL icon_url;
+};
+
+void GetAvailableDriveTasks(
+    const drive::DriveAppRegistry& drive_app_registry,
+    const PathAndMimeTypeSet& path_mime_set,
+    TaskInfoMap* task_info_map) {
+  DCHECK(task_info_map);
+  DCHECK(task_info_map->empty());
+
+  bool is_first = true;
+  for (PathAndMimeTypeSet::const_iterator it = path_mime_set.begin();
+       it != path_mime_set.end(); ++it) {
+    const base::FilePath& file_path = it->first;
+    const std::string& mime_type = it->second;
+    if (file_path.empty())
+      continue;
+
+    ScopedVector<drive::DriveAppInfo> app_info_list;
+    drive_app_registry.GetAppsForFile(file_path, mime_type, &app_info_list);
+
+    if (is_first) {
+      // For the first file, we store all the info.
+      for (size_t j = 0; j < app_info_list.size(); ++j) {
+        const drive::DriveAppInfo& app_info = *app_info_list[j];
+        GURL icon_url = drive::util::FindPreferredIcon(
+            app_info.app_icons,
+            drive::util::kPreferredIconSize);
+        task_info_map->insert(std::pair<std::string, TaskInfo>(
+            file_tasks::MakeDriveAppTaskId(app_info.app_id),
+            TaskInfo(app_info.app_name, icon_url)));
+      }
+    } else {
+      // For remaining files, take the intersection with the current result,
+      // based on the task id.
+      std::set<std::string> task_id_set;
+      for (size_t j = 0; j < app_info_list.size(); ++j) {
+        task_id_set.insert(
+            file_tasks::MakeDriveAppTaskId(app_info_list[j]->app_id));
+      }
+      for (TaskInfoMap::iterator iter = task_info_map->begin();
+           iter != task_info_map->end(); ) {
+        if (task_id_set.find(iter->first) == task_id_set.end()) {
+          task_info_map->erase(iter++);
+        } else {
+          ++iter;
+        }
+      }
+    }
+
+    is_first = false;
+  }
+}
+
+void FindDefaultDriveTasks(
+    const PrefService& pref_service,
+    const PathAndMimeTypeSet& path_mime_set,
+    const TaskInfoMap& task_info_map,
+    std::set<std::string>* default_tasks) {
+  DCHECK(default_tasks);
+
+  for (PathAndMimeTypeSet::const_iterator it = path_mime_set.begin();
+       it != path_mime_set.end(); ++it) {
+    const base::FilePath& file_path = it->first;
+    const std::string& mime_type = it->second;
+    std::string task_id = file_tasks::GetDefaultTaskIdFromPrefs(
+        pref_service, mime_type, file_path.Extension());
+    if (task_info_map.find(task_id) != task_info_map.end())
+      default_tasks->insert(task_id);
+  }
+}
+
+void CreateDriveTasks(
+    const TaskInfoMap& task_info_map,
+    const std::set<std::string>& default_tasks,
+    std::vector<FullTaskDescriptor>* result_list,
+    bool* default_already_set) {
+  DCHECK(result_list);
+  DCHECK(default_already_set);
+
+  for (TaskInfoMap::const_iterator iter = task_info_map.begin();
+       iter != task_info_map.end(); ++iter) {
+    bool is_default = false;
+    // Once we set a default app, we don't want to set any more.
+    if (!(*default_already_set) &&
+        default_tasks.find(iter->first) != default_tasks.end()) {
+      is_default = true;
+      *default_already_set = true;
+    }
+
+    TaskDescriptor descriptor;
+    DCHECK(ParseTaskID(iter->first, &descriptor));
+    result_list->push_back(
+        FullTaskDescriptor(descriptor,
+                           iter->second.app_name,
+                           iter->second.icon_url,
+                           is_default));
+  }
+}
+
+void FindDriveAppTasks(
+    Profile* profile,
+    const PathAndMimeTypeSet& path_mime_set,
+    std::vector<FullTaskDescriptor>* result_list,
+    bool* default_already_set) {
+  DCHECK(!path_mime_set.empty());
+  DCHECK(result_list);
+  DCHECK(default_already_set);
+
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::GetForProfile(profile);
+  // |integration_service| is NULL if Drive is disabled.
+  if (!integration_service || !integration_service->drive_app_registry())
+    return;
+
+  // Map of task_id to TaskInfo of available tasks.
+  TaskInfoMap task_info_map;
+  GetAvailableDriveTasks(*integration_service->drive_app_registry(),
+                         path_mime_set,
+                         &task_info_map);
+
+  std::set<std::string> default_tasks;
+  FindDefaultDriveTasks(*profile->GetPrefs(),
+                        path_mime_set,
+                        task_info_map,
+                        &default_tasks);
+  CreateDriveTasks(
+      task_info_map, default_tasks, result_list, default_already_set);
+}
+
+void FindFileHandlerTasks(
+    Profile* profile,
+    const PathAndMimeTypeSet& path_mime_set,
+    std::vector<FullTaskDescriptor>* result_list,
+    bool* default_already_set) {
+  DCHECK(!path_mime_set.empty());
+  DCHECK(result_list);
+  DCHECK(default_already_set);
+
+  ExtensionService* service = profile->GetExtensionService();
+  if (!service)
+    return;
+
+  std::set<std::string> default_tasks;
+  for (PathAndMimeTypeSet::iterator it = path_mime_set.begin();
+       it != path_mime_set.end(); ++it) {
+    default_tasks.insert(file_tasks::GetDefaultTaskIdFromPrefs(
+        *profile->GetPrefs(), it->second, it->first.Extension()));
+  }
+
+  for (ExtensionSet::const_iterator iter = service->extensions()->begin();
+       iter != service->extensions()->end();
+       ++iter) {
+    const Extension* extension = iter->get();
+
+    // We don't support using hosted apps to open files.
+    if (!extension->is_platform_app())
+      continue;
+
+    if (profile->IsOffTheRecord() &&
+        !service->IsIncognitoEnabled(extension->id()))
+      continue;
+
+    typedef std::vector<const extensions::FileHandlerInfo*> FileHandlerList;
+    FileHandlerList file_handlers =
+        FindFileHandlersForFiles(*extension, path_mime_set);
+    if (file_handlers.empty())
+      continue;
+
+    for (FileHandlerList::iterator i = file_handlers.begin();
+         i != file_handlers.end(); ++i) {
+      std::string task_id = file_tasks::MakeTaskID(
+          extension->id(), file_tasks::TASK_TYPE_FILE_HANDLER, (*i)->id);
+
+      bool is_default = false;
+      if (!(*default_already_set) && ContainsKey(default_tasks, task_id)) {
+        is_default = true;
+        *default_already_set = true;
+      }
+
+      GURL best_icon = extensions::ExtensionIconSource::GetIconURL(
+          extension,
+          drive::util::kPreferredIconSize,
+          ExtensionIconSet::MATCH_BIGGER,
+          false,  // grayscale
+          NULL);  // exists
+
+      result_list->push_back(FullTaskDescriptor(
+          TaskDescriptor(extension->id(),
+                         file_tasks::TASK_TYPE_FILE_HANDLER,
+                         (*i)->id),
+          (*i)->title,
+          best_icon,
+          is_default));
+    }
+  }
+}
+
+void FindFileBrowserHandlerTasks(
+    Profile* profile,
+    const std::vector<GURL>& file_urls,
+    const std::vector<base::FilePath>& file_paths,
+    std::vector<FullTaskDescriptor>* result_list,
+    bool* default_already_set) {
+  DCHECK(!file_paths.empty());
+  DCHECK(!file_urls.empty());
+  DCHECK(result_list);
+  DCHECK(default_already_set);
+
+  file_browser_handlers::FileBrowserHandlerList common_tasks =
+      file_browser_handlers::FindCommonFileBrowserHandlers(profile, file_urls);
+  if (common_tasks.empty())
+    return;
+  file_browser_handlers::FileBrowserHandlerList default_tasks =
+      file_browser_handlers::FindDefaultFileBrowserHandlers(
+          *profile->GetPrefs(), file_paths, common_tasks);
+
+  ExtensionService* service =
+      extensions::ExtensionSystem::Get(profile)->extension_service();
+  for (file_browser_handlers::FileBrowserHandlerList::const_iterator iter =
+           common_tasks.begin();
+       iter != common_tasks.end();
+       ++iter) {
+    const FileBrowserHandler* handler = *iter;
+    const std::string extension_id = handler->extension_id();
+    const Extension* extension = service->GetExtensionById(extension_id, false);
+    DCHECK(extension);
+
+    // TODO(zelidrag): Figure out how to expose icon URL that task defined in
+    // manifest instead of the default extension icon.
+    const GURL icon_url = extensions::ExtensionIconSource::GetIconURL(
+        extension,
+        extension_misc::EXTENSION_ICON_BITTY,
+        ExtensionIconSet::MATCH_BIGGER,
+        false,  // grayscale
+        NULL);  // exists
+
+    // Only set the default if there isn't already a default set.
+    bool is_default = false;
+    if (!*default_already_set &&
+        std::find(default_tasks.begin(), default_tasks.end(), *iter) !=
+        default_tasks.end()) {
+      is_default = true;
+      *default_already_set = true;
+    }
+
+    result_list->push_back(FullTaskDescriptor(
+        TaskDescriptor(extension_id,
+                       file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER,
+                       handler->id()),
+        handler->title(),
+        icon_url,
+        is_default));
+  }
+}
+
+void FindAllTypesOfTasks(
+    Profile* profile,
+    const PathAndMimeTypeSet& path_mime_set,
+    const std::vector<GURL>& file_urls,
+    const std::vector<base::FilePath>& file_paths,
+    std::vector<FullTaskDescriptor>* result_list) {
+  DCHECK(profile);
+  DCHECK(result_list);
+
+  // Check if file_paths contain a google document.
+  bool has_google_document = false;
+  for (size_t i = 0; i < file_paths.size(); ++i) {
+    if (google_apis::ResourceEntry::ClassifyEntryKindByFileExtension(
+            file_paths[i]) &
+        google_apis::ResourceEntry::KIND_OF_GOOGLE_DOCUMENT) {
+      has_google_document = true;
+      break;
+    }
+  }
+
+  // Find the Drive app tasks first, because we want them to take precedence
+  // when setting the default app.
+  bool default_already_set = false;
+  // Google document are not opened by drive apps but file manager.
+  if (!has_google_document) {
+    FindDriveAppTasks(profile,
+                      path_mime_set,
+                      result_list,
+                      &default_already_set);
+  }
+
+  // Find and append file handler tasks. We know there aren't duplicates
+  // because Drive apps and platform apps are entirely different kinds of
+  // tasks.
+  FindFileHandlerTasks(profile,
+                       path_mime_set,
+                       result_list,
+                       &default_already_set);
+
+  // Find and append file browser handler tasks. We know there aren't
+  // duplicates because "file_browser_handlers" and "file_handlers" shouldn't
+  // be used in the same manifest.json.
+  FindFileBrowserHandlerTasks(profile,
+                              file_urls,
+                              file_paths,
+                              result_list,
+                              &default_already_set);
+}
+
 }  // namespace file_tasks
 }  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_tasks.h b/chrome/browser/chromeos/extensions/file_manager/file_tasks.h
index 8d37fcb..e914ad4 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_tasks.h
+++ b/chrome/browser/chromeos/extensions/file_manager/file_tasks.h
@@ -116,10 +116,16 @@
 
 #include "base/basictypes.h"
 #include "base/callback_forward.h"
+#include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
+#include "url/gurl.h"
 
-class GURL;
+class PrefService;
 class Profile;
 
+namespace drive {
+class DriveAppRegistry;
+}
+
 namespace fileapi {
 class FileSystemURL;
 }
@@ -153,9 +159,47 @@
   std::string action_id;
 };
 
+// Describes a task with extra information such as icon URL.
+class FullTaskDescriptor {
+ public:
+  FullTaskDescriptor(const TaskDescriptor& task_descriptor,
+                     const std::string& task_title,
+                     const GURL& icon_url,
+                     bool is_default);
+  // The icon URL for the task (ex. app icon)
+  const GURL& icon_url() const { return icon_url_; }
+
+  // True if this task is set as default.
+  bool is_default() const { return is_default_; }
+
+  // The title of the task.
+  const std::string& task_title() { return task_title_; }
+
+  // Returns a DictionaryValue representation, which looks like:
+  //
+  // {
+  //   "driveApp": true,
+  //   "iconUrl": "<app_icon_url>",
+  //   "isDefault": false,
+  //   "taskId": "<drive_app_id>|drive|open-with",
+  //   "title": "Drive App Name (ex. Pixlr Editor)"
+  // },
+  //
+  // "iconUrl" is omitted if icon_url_ is empty.
+  //
+  // This representation will be used to send task info to the JavaScript.
+  scoped_ptr<base::DictionaryValue> AsDictionaryValue() const;
+
+ private:
+  TaskDescriptor task_descriptor_;
+  std::string task_title_;
+  GURL icon_url_;
+  bool is_default_;
+};
+
 // Update the default file handler for the given sets of suffixes and MIME
 // types.
-void UpdateDefaultTask(Profile* profile,
+void UpdateDefaultTask(PrefService* pref_service,
                        const std::string& task_id,
                        const std::set<std::string>& suffixes,
                        const std::set<std::string>& mime_types);
@@ -164,7 +208,7 @@
 // combination. If it finds a MIME type match, then it prefers that over a
 // suffix match. If it a default can't be found, then it returns the empty
 // string.
-std::string GetDefaultTaskIdFromPrefs(Profile* profile,
+std::string GetDefaultTaskIdFromPrefs(const PrefService& pref_service,
                                       const std::string& mime_type,
                                       const std::string& suffix);
 
@@ -173,7 +217,7 @@
 //
 // |app_id| is either of Chrome Extension/App ID or Drive App ID.
 // |action_id| is a free-form string ID for the action.
-std::string MakeTaskID(const std::string& extension_id,
+std::string MakeTaskID(const std::string& app_id,
                        TaskType task_type,
                        const std::string& action_id);
 
@@ -182,6 +226,9 @@
 // could add any actions that the drive app supports.
 std::string MakeDriveAppTaskId(const std::string& app_id);
 
+// Converts |task_descriptor| to a task ID.
+std::string TaskDescriptorToId(const TaskDescriptor& task_descriptor);
+
 // Parses the task ID and extracts app ID, task type, and action ID into
 // |task|. On failure, returns false, and the contents of |task| are
 // undefined.
@@ -218,6 +265,80 @@
                      const std::vector<fileapi::FileSystemURL>& file_urls,
                      const FileTaskFinishedCallback& done);
 
+typedef extensions::app_file_handler_util::PathAndMimeTypeSet
+    PathAndMimeTypeSet;
+
+// Holds fields to build a task result.
+struct TaskInfo;
+
+// Map from a task id to TaskInfo.
+typedef std::map<std::string, TaskInfo> TaskInfoMap;
+
+// Looks up available apps for each file in |path_mime_set| in the
+// |registry|, and returns the intersection of all available apps as a
+// map from task id to TaskInfo.
+void GetAvailableDriveTasks(const drive::DriveAppRegistry& registry,
+                            const PathAndMimeTypeSet& path_mime_set,
+                            TaskInfoMap* task_info_map);
+
+// Looks in the preferences and finds any of the available apps that are
+// also listed as default apps for any of the files in the info list.
+void FindDefaultDriveTasks(const PrefService& pref_service,
+                           const PathAndMimeTypeSet& path_mime_set,
+                           const TaskInfoMap& task_info_map,
+                           std::set<std::string>* default_tasks);
+
+// Creates a list of each task in |task_info_map| and stores the result into
+// |result_list|. If a default task is set in the result list,
+// |default_already_set| is set to true.
+void CreateDriveTasks(const TaskInfoMap& task_info_map,
+                      const std::set<std::string>& default_tasks,
+                      std::vector<FullTaskDescriptor>* result_list,
+                      bool* default_already_set);
+
+// Finds the drive app tasks that can be used with the given files, and
+// append them to the |result_list|. |*default_already_set| indicates if
+// the |result_list| already contains the default task. If the value is
+// false, the function will find the default task and set the value to true
+// if found.
+//
+// "taskId" field in |result_list| will look like
+// "<drive-app-id>|drive|open-with" (See also file_tasks.h).
+// "driveApp" field in |result_list| will be set to "true".
+void FindDriveAppTasks(Profile* profile,
+                       const PathAndMimeTypeSet& path_mime_set,
+                       std::vector<FullTaskDescriptor>* result_list,
+                       bool* default_already_set);
+
+// Finds the file handler tasks (apps declaring "file_handlers" in
+// manifest.json) that can be used with the given files, appending them to
+// the |result_list|. See the comment at FindDriveAppTasks() about
+// |default_already_set|
+void FindFileHandlerTasks(Profile* profile,
+                          const PathAndMimeTypeSet& path_mime_set,
+                          std::vector<FullTaskDescriptor>* result_list,
+                          bool* default_already_set);
+
+// Finds the file browser handler tasks (app/extensions declaring
+// "file_browser_handlers" in manifest.json) that can be used with the
+// given files, appending them to the |result_list|. See the comment at
+// FindDriveAppTasks() about |default_already_set|
+void FindFileBrowserHandlerTasks(
+    Profile* profile,
+    const std::vector<GURL>& file_urls,
+    const std::vector<base::FilePath>& file_paths,
+    std::vector<FullTaskDescriptor>* result_list,
+    bool* default_already_set);
+
+// Finds all types (drive, file handlers, file browser handlers) of
+// tasks. See the comment at FindDriveAppTasks() about |result_list|.
+void FindAllTypesOfTasks(
+    Profile* profile,
+    const PathAndMimeTypeSet& path_mime_set,
+    const std::vector<GURL>& file_urls,
+    const std::vector<base::FilePath>& file_paths,
+    std::vector<FullTaskDescriptor>* result_list);
+
 }  // namespace file_tasks
 }  // namespace file_manager
 
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_tasks_unittest.cc b/chrome/browser/chromeos/extensions/file_manager/file_tasks_unittest.cc
index ebd249f..45a9061 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_tasks_unittest.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_tasks_unittest.cc
@@ -4,11 +4,77 @@
 
 #include "chrome/browser/chromeos/extensions/file_manager/file_tasks.h"
 
+#include "base/values.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace file_manager {
 namespace file_tasks {
 
+TEST(FileManagerFileTasksTest,
+     FullTaskDescriptor_NonDriveAppWithIconAndDefault) {
+  FullTaskDescriptor full_descriptor(
+      TaskDescriptor("app-id",
+                     TASK_TYPE_FILE_BROWSER_HANDLER,
+                     "action-id"),
+      "task title",
+      GURL("http://example.com/icon.png"),
+      true /* is_default */);
+
+  scoped_ptr<base::DictionaryValue> dictionary(
+      full_descriptor.AsDictionaryValue());
+  std::string task_id;
+  EXPECT_TRUE(dictionary->GetString("taskId", &task_id));
+  EXPECT_EQ("app-id|file|action-id", task_id);
+
+  std::string icon_url;
+  EXPECT_TRUE(dictionary->GetString("iconUrl", &icon_url));
+  EXPECT_EQ("http://example.com/icon.png", icon_url);
+
+  std::string title;
+  EXPECT_TRUE(dictionary->GetString("title", &title));
+  EXPECT_EQ("task title", title);
+
+  bool is_drive_app = false;
+  EXPECT_TRUE(dictionary->GetBoolean("driveApp", &is_drive_app));
+  EXPECT_FALSE(is_drive_app);
+
+  bool is_default = false;
+  EXPECT_TRUE(dictionary->GetBoolean("isDefault", &is_default));
+  EXPECT_TRUE(is_default);
+}
+
+TEST(FileManagerFileTasksTest,
+     FullTaskDescriptor_DriveAppWithoutIconAndNotDefault) {
+  FullTaskDescriptor full_descriptor(
+      TaskDescriptor("app-id",
+                     TASK_TYPE_DRIVE_APP,
+                     "action-id"),
+      "task title",
+      GURL(),  // No icon URL.
+      false /* is_default */);
+
+  scoped_ptr<base::DictionaryValue> dictionary(
+      full_descriptor.AsDictionaryValue());
+  std::string task_id;
+  EXPECT_TRUE(dictionary->GetString("taskId", &task_id));
+  EXPECT_EQ("app-id|drive|action-id", task_id);
+
+  std::string icon_url;
+  EXPECT_FALSE(dictionary->GetString("iconUrl", &icon_url));
+
+  std::string title;
+  EXPECT_TRUE(dictionary->GetString("title", &title));
+  EXPECT_EQ("task title", title);
+
+  bool is_drive_app = false;
+  EXPECT_TRUE(dictionary->GetBoolean("driveApp", &is_drive_app));
+  EXPECT_TRUE(is_drive_app);
+
+  bool is_default = false;
+  EXPECT_TRUE(dictionary->GetBoolean("isDefault", &is_default));
+  EXPECT_FALSE(is_default);
+}
+
 TEST(FileManagerFileTasksTest, MakeTaskID) {
   EXPECT_EQ("app-id|file|action-id",
             MakeTaskID("app-id", TASK_TYPE_FILE_BROWSER_HANDLER, "action-id"));
@@ -22,6 +88,13 @@
   EXPECT_EQ("app-id|drive|open-with", MakeDriveAppTaskId("app-id"));
 }
 
+TEST(FileManagerFileTasksTest, TaskDescriptorToId) {
+  EXPECT_EQ("app-id|file|action-id",
+            TaskDescriptorToId(TaskDescriptor("app-id",
+                                              TASK_TYPE_FILE_BROWSER_HANDLER,
+                                              "action-id")));
+}
+
 TEST(FileManagerFileTasksTest, ParseTaskID_FileBrowserHandler) {
   TaskDescriptor task;
   EXPECT_TRUE(ParseTaskID("app-id|file|action-id", &task));
diff --git a/chrome/browser/chromeos/extensions/file_manager/mime_util.cc b/chrome/browser/chromeos/extensions/file_manager/mime_util.cc
new file mode 100644
index 0000000..e048c07
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/mime_util.cc
@@ -0,0 +1,36 @@
+// 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 "chrome/browser/chromeos/extensions/file_manager/mime_util.h"
+
+#include <utility>
+#include "base/files/file_path.h"
+#include "base/strings/string_util.h"
+#include "net/base/mime_util.h"
+
+namespace file_manager {
+namespace util {
+
+std::string GetMimeTypeForPath(const base::FilePath& file_path) {
+  const base::FilePath::StringType file_extension =
+      StringToLowerASCII(file_path.Extension());
+
+  // TODO(thorogood): Rearchitect this call so it can run on the File thread;
+  // GetMimeTypeFromFile requires this on Linux. Right now, we use
+  // Chrome-level knowledge only.
+  std::string mime_type;
+  if (file_extension.empty() ||
+      !net::GetWellKnownMimeTypeFromExtension(file_extension.substr(1),
+                                              &mime_type)) {
+    // If the file doesn't have an extension or its mime-type cannot be
+    // determined, then indicate that it has the empty mime-type. This will
+    // only be matched if the Web Intents accepts "*" or "*/*".
+    return "";
+  } else {
+    return mime_type;
+  }
+}
+
+}  // namespace util
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/mime_util.h b/chrome/browser/chromeos/extensions/file_manager/mime_util.h
new file mode 100644
index 0000000..28f713d
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/mime_util.h
@@ -0,0 +1,29 @@
+// 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.
+//
+// This file provides MIME related utilities.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_MIME_UTIL_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_MIME_UTIL_H_
+
+#include <string>
+
+#include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
+#include "url/gurl.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace file_manager {
+namespace util {
+
+// Returns the MIME type of |file_path|. Returns "" if the MIME type is
+// unknown.
+std::string GetMimeTypeForPath(const base::FilePath& file_path);
+
+}  // namespace util
+}  // namespace file_manager
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_MIME_UTIL_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/mime_util_unittest.cc b/chrome/browser/chromeos/extensions/file_manager/mime_util_unittest.cc
new file mode 100644
index 0000000..cfaf4c8
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/mime_util_unittest.cc
@@ -0,0 +1,23 @@
+// 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 "chrome/browser/chromeos/extensions/file_manager/mime_util.h"
+
+#include "base/files/file_path.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace file_manager {
+namespace util {
+
+TEST(FileManagerMimeUtilTest, GetMimeTypeForPath) {
+  EXPECT_EQ("image/jpeg",
+            GetMimeTypeForPath(base::FilePath::FromUTF8Unsafe("foo.jpg")));
+  EXPECT_EQ("image/jpeg",
+            GetMimeTypeForPath(base::FilePath::FromUTF8Unsafe("FOO.JPG")));
+  EXPECT_EQ("",
+            GetMimeTypeForPath(base::FilePath::FromUTF8Unsafe("foo.zzz")));
+}
+
+}  // namespace util
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/open_util.cc b/chrome/browser/chromeos/extensions/file_manager/open_util.cc
new file mode 100644
index 0000000..d3f9bba
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/open_util.cc
@@ -0,0 +1,337 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/extensions/file_manager/open_util.h"
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/chromeos/extensions/file_manager/app_id.h"
+#include "chrome/browser/chromeos/extensions/file_manager/file_browser_handlers.h"
+#include "chrome/browser/chromeos/extensions/file_manager/file_tasks.h"
+#include "chrome/browser/chromeos/extensions/file_manager/fileapi_util.h"
+#include "chrome/browser/chromeos/extensions/file_manager/mime_util.h"
+#include "chrome/browser/chromeos/extensions/file_manager/url_util.h"
+#include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/google_apis/task_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/extensions/application_launch.h"
+#include "chrome/browser/ui/simple_message_box.h"
+#include "chrome/common/extensions/api/file_browser_handlers/file_browser_handler.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/user_metrics.h"
+#include "grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "webkit/browser/fileapi/file_system_backend.h"
+#include "webkit/browser/fileapi/file_system_context.h"
+#include "webkit/browser/fileapi/file_system_operation_runner.h"
+#include "webkit/browser/fileapi/file_system_url.h"
+
+using content::BrowserContext;
+using content::BrowserThread;
+using content::UserMetricsAction;
+using extensions::Extension;
+using extensions::app_file_handler_util::FindFileHandlersForFiles;
+using extensions::app_file_handler_util::PathAndMimeTypeSet;
+using fileapi::FileSystemURL;
+
+namespace file_manager {
+namespace util {
+namespace {
+
+// Shows a warning message box saying that the file could not be opened.
+void ShowWarningMessageBox(Profile* profile, const base::FilePath& file_path) {
+  // TODO: if FindOrCreateTabbedBrowser creates a new browser the returned
+  // browser is leaked.
+  Browser* browser =
+      chrome::FindOrCreateTabbedBrowser(profile,
+                                        chrome::HOST_DESKTOP_TYPE_ASH);
+  chrome::ShowMessageBox(
+      browser->window()->GetNativeWindow(),
+      l10n_util::GetStringFUTF16(
+          IDS_FILE_BROWSER_ERROR_VIEWING_FILE_TITLE,
+          UTF8ToUTF16(file_path.BaseName().value())),
+      l10n_util::GetStringUTF16(IDS_FILE_BROWSER_ERROR_VIEWING_FILE),
+      chrome::MESSAGE_BOX_TYPE_WARNING);
+}
+
+// Grants file system access to the file manager.
+bool GrantFileSystemAccessToFileBrowser(Profile* profile) {
+  // The file manager always runs in the site for its extension id, so that
+  // is the site for which file access permissions should be granted.
+  fileapi::ExternalFileSystemBackend* backend =
+      GetFileSystemContextForExtensionId(
+          profile, kFileManagerAppId)->external_backend();
+  if (!backend)
+    return false;
+  backend->GrantFullAccessToExtension(kFileManagerAppId);
+  return true;
+}
+
+// Executes the |task| for the file specified by |url|.
+void ExecuteFileTaskForUrl(Profile* profile,
+                           const file_tasks::TaskDescriptor& task,
+                           const GURL& url) {
+  // If the file manager has not been open yet then it did not request access
+  // to the file system. Do it now.
+  if (!GrantFileSystemAccessToFileBrowser(profile))
+    return;
+
+  fileapi::FileSystemContext* file_system_context =
+      GetFileSystemContextForExtensionId(
+          profile, kFileManagerAppId);
+
+  // We are executing the task on behalf of the file manager.
+  const GURL source_url = GetFileManagerMainPageUrl();
+  std::vector<FileSystemURL> urls;
+  urls.push_back(file_system_context->CrackURL(url));
+
+  file_tasks::ExecuteFileTask(
+      profile,
+      source_url,
+      kFileManagerAppId,
+      0, // no tab id
+      task,
+      urls,
+      file_tasks::FileTaskFinishedCallback());
+}
+
+// Opens the file manager for the specified |file_path|. Used to implement
+// internal handlers of special action IDs:
+//
+// "open" - Open the file manager for the given folder.
+// "auto-open" - Open the file manager for the given removal drive and close
+//               the file manager when the removal drive is unmounted.
+// "select" - Open the file manager for the given file. The folder containing
+//            the file will be opened with the file selected.
+void OpenFileManagerWithInternalActionId(const base::FilePath& file_path,
+                                         const std::string& action_id) {
+  DCHECK(action_id == "auto-open" ||
+         action_id == "open" ||
+         action_id == "select");
+
+  content::RecordAction(UserMetricsAction("ShowFileBrowserFullTab"));
+  Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord();
+
+  GURL url;
+  if (!ConvertAbsoluteFilePathToFileSystemUrl(
+          profile, file_path, kFileManagerAppId, &url))
+    return;
+
+  file_tasks::TaskDescriptor task(kFileManagerAppId,
+                                  file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER,
+                                  action_id);
+  ExecuteFileTaskForUrl(profile, task, url);
+}
+
+// Opens the file specified by |file_path| and |url| with a file handler,
+// preferably the default handler for the type of the file.  Returns false if
+// no file handler is found.
+bool OpenFileWithFileHandler(Profile* profile,
+                              const base::FilePath& file_path,
+                              const GURL& url,
+                              const std::string& mime_type,
+                              const std::string& default_task_id) {
+  ExtensionService* service = profile->GetExtensionService();
+  if (!service)
+    return false;
+
+  PathAndMimeTypeSet files;
+  files.insert(std::make_pair(file_path, mime_type));
+  const extensions::FileHandlerInfo* first_handler = NULL;
+  const extensions::Extension* extension_for_first_handler = NULL;
+
+  // If we find the default handler, we execute it immediately, but otherwise,
+  // we remember the first handler, and if there was no default handler, simply
+  // execute the first one.
+  for (ExtensionSet::const_iterator iter = service->extensions()->begin();
+       iter != service->extensions()->end();
+       ++iter) {
+    const Extension* extension = iter->get();
+
+    // We don't support using hosted apps to open files.
+    if (!extension->is_platform_app())
+      continue;
+
+    // We only support apps that specify "incognito: split" if in incognito
+    // mode.
+    if (profile->IsOffTheRecord() &&
+        !service->IsIncognitoEnabled(extension->id()))
+      continue;
+
+    typedef std::vector<const extensions::FileHandlerInfo*> FileHandlerList;
+    FileHandlerList file_handlers = FindFileHandlersForFiles(*extension, files);
+    for (FileHandlerList::iterator i = file_handlers.begin();
+         i != file_handlers.end(); ++i) {
+      const extensions::FileHandlerInfo* handler = *i;
+      std::string task_id = file_tasks::MakeTaskID(
+          extension->id(),
+          file_tasks::TASK_TYPE_FILE_HANDLER,
+          handler->id);
+      if (task_id == default_task_id) {
+        file_tasks::TaskDescriptor task(extension->id(),
+                                        file_tasks::TASK_TYPE_FILE_HANDLER,
+                                        handler->id);
+        ExecuteFileTaskForUrl(profile, task, url);
+        return true;
+
+      } else if (!first_handler) {
+        first_handler = handler;
+        extension_for_first_handler = extension;
+      }
+    }
+  }
+  if (first_handler) {
+    file_tasks::TaskDescriptor task(extension_for_first_handler->id(),
+                                    file_tasks::TASK_TYPE_FILE_HANDLER,
+                                    first_handler->id);
+    ExecuteFileTaskForUrl(profile, task, url);
+    return true;
+  }
+  return false;
+}
+
+// Opens the file specified by |file_path| and |url| with the file browser
+// handler specified by |handler|. Returns false if failed to open the file.
+bool OpenFileWithFileBrowserHandler(Profile* profile,
+                                    const base::FilePath& file_path,
+                                    const FileBrowserHandler& handler,
+                                    const GURL& url) {
+  file_tasks::TaskDescriptor task(handler.extension_id(),
+                                  file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER,
+                                  handler.id());
+  ExecuteFileTaskForUrl(profile, task, url);
+  return true;
+}
+
+// Opens the file specified by |file_path| with a handler (either of file
+// browser handler or file handler, preferably the default handler for the
+// type of the file), or opens the file with the browser. Returns false if
+// failed to open the file.
+bool OpenFileWithHandler(Profile* profile, const base::FilePath& file_path) {
+  GURL url;
+  if (!ConvertAbsoluteFilePathToFileSystemUrl(
+          profile, file_path, kFileManagerAppId, &url))
+    return false;
+
+  std::string mime_type = GetMimeTypeForPath(file_path);
+  std::string default_task_id = file_tasks::GetDefaultTaskIdFromPrefs(
+      *profile->GetPrefs(), mime_type, file_path.Extension());
+
+  // We choose the file handler from the following in decreasing priority or
+  // fail if none support the file type:
+  // 1. default file browser handler
+  // 2. default file handler
+  // 3. a fallback handler (e.g. opening in the browser)
+  // 4. non-default file handler
+  // 5. non-default file browser handler
+  // Note that there can be at most one of default extension and default app.
+  const FileBrowserHandler* handler =
+      file_browser_handlers::FindFileBrowserHandlerForURLAndPath(
+          profile, url, file_path);
+  if (!handler) {
+    return OpenFileWithFileHandler(
+        profile, file_path, url, mime_type, default_task_id);
+  }
+
+  const file_tasks::TaskDescriptor task_descriptor(
+      handler->extension_id(),
+      file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER,
+      handler->id());
+  const std::string handler_task_id =
+      file_tasks::TaskDescriptorToId(task_descriptor);
+  if (handler_task_id != default_task_id &&
+      !file_browser_handlers::IsFallbackFileBrowserHandler(task_descriptor) &&
+      OpenFileWithFileHandler(
+          profile, file_path, url, mime_type, default_task_id)) {
+    return true;
+  }
+  return OpenFileWithFileBrowserHandler(profile, file_path, *handler, url);
+}
+
+// Used to implement OpenItem().
+void ContinueOpenItem(Profile* profile,
+                      const base::FilePath& file_path,
+                      base::PlatformFileError error) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  if (error == base::PLATFORM_FILE_OK) {
+    // A directory exists at |file_path|. Open it with the file manager.
+    OpenFileManagerWithInternalActionId(file_path, "open");
+  } else {
+    // |file_path| should be a file. Open it with a handler.
+    if (!OpenFileWithHandler(profile, file_path))
+      ShowWarningMessageBox(profile, file_path);
+  }
+}
+
+// Used to implement CheckIfDirectoryExists().
+void CheckIfDirectoryExistsOnIOThread(
+    scoped_refptr<fileapi::FileSystemContext> file_system_context,
+    const GURL& url,
+    const fileapi::FileSystemOperationRunner::StatusCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  fileapi::FileSystemURL file_system_url = file_system_context->CrackURL(url);
+  file_system_context->operation_runner()->DirectoryExists(
+      file_system_url, callback);
+}
+
+// Checks if a directory exists at |url|.
+void CheckIfDirectoryExists(
+    scoped_refptr<fileapi::FileSystemContext> file_system_context,
+    const GURL& url,
+    const fileapi::FileSystemOperationRunner::StatusCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(&CheckIfDirectoryExistsOnIOThread,
+                 file_system_context,
+                 url,
+                 google_apis::CreateRelayCallback(callback)));
+}
+
+}  // namespace
+
+void OpenRemovableDrive(const base::FilePath& file_path) {
+  OpenFileManagerWithInternalActionId(file_path, "auto-open");
+}
+
+void OpenItem(const base::FilePath& file_path) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord();
+  GURL url;
+  if (!ConvertAbsoluteFilePathToFileSystemUrl(
+          profile, file_path, kFileManagerAppId, &url) ||
+      !GrantFileSystemAccessToFileBrowser(profile)) {
+    ShowWarningMessageBox(profile, file_path);
+    return;
+  }
+
+  scoped_refptr<fileapi::FileSystemContext> file_system_context =
+      GetFileSystemContextForExtensionId(
+          profile, kFileManagerAppId);
+
+  CheckIfDirectoryExists(file_system_context, url,
+                         base::Bind(&ContinueOpenItem, profile, file_path));
+}
+
+void ShowItemInFolder(const base::FilePath& file_path) {
+  // This action changes the selection so we do not reuse existing tabs.
+  OpenFileManagerWithInternalActionId(file_path, "select");
+}
+
+}  // namespace util
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/open_util.h b/chrome/browser/chromeos/extensions/file_manager/open_util.h
new file mode 100644
index 0000000..e910f2d
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/open_util.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file provides functions for opening an item (file or directory) using
+// the file manager.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_OPEN_UTIL_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_OPEN_UTIL_H_
+
+namespace base {
+class FilePath;
+}
+
+namespace file_manager {
+namespace util {
+
+// Opens the file manager for the freshly mounted removable drive specified
+// by |file_path|.
+// If there is another file manager instance open, this call does nothing.
+// The mount event will cause the file manager to show the new drive in
+// the left panel.
+// If there is no file manager open, this call opens a new one pointing to
+// |file_path|. In this case the tab will automatically close on |file_path|
+// unmount.
+void OpenRemovableDrive(const base::FilePath& file_path);
+
+// Opens an item (file or directory). If the target is a directory, the
+// directory will be opened in the file manager. If the target is a file, the
+// file will be opened using a file handler, a file browser handler, or the
+// browser (open in a tab). The default handler has precedence over other
+// handlers, if defined for the type of the target file.
+void OpenItem(const base::FilePath& file_path);
+
+// Opens the file manager for the folder containing the item specified by
+// |file_path|, with the item selected.
+void ShowItemInFolder(const base::FilePath& file_path);
+
+}  // namespace util
+}  // namespace file_manager
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_OPEN_UTIL_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/open_with_browser.cc b/chrome/browser/chromeos/extensions/file_manager/open_with_browser.cc
new file mode 100644
index 0000000..66ab07f
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/open_with_browser.cc
@@ -0,0 +1,239 @@
+// 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 "chrome/browser/chromeos/extensions/file_manager/open_with_browser.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "chrome/browser/chromeos/drive/drive.pb.h"
+#include "chrome/browser/chromeos/drive/drive_integration_service.h"
+#include "chrome/browser/chromeos/drive/file_system.h"
+#include "chrome/browser/chromeos/drive/file_system_util.h"
+#include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
+#include "chrome/browser/extensions/crx_installer.h"
+#include "chrome/browser/extensions/extension_install_prompt.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/plugins/plugin_prefs.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/simple_message_box.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chromeos/chromeos_switches.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/plugin_service.h"
+#include "content/public/common/pepper_plugin_info.h"
+#include "content/public/common/webplugininfo.h"
+#include "net/base/net_util.h"
+
+using content::BrowserThread;
+using content::PluginService;
+
+namespace file_manager {
+namespace util {
+namespace {
+
+const base::FilePath::CharType kCRXExtension[] = FILE_PATH_LITERAL(".crx");
+const base::FilePath::CharType kPdfExtension[] = FILE_PATH_LITERAL(".pdf");
+const base::FilePath::CharType kSwfExtension[] = FILE_PATH_LITERAL(".swf");
+
+// List of file extensions viewable in the browser.
+const base::FilePath::CharType* kFileExtensionsViewableInBrowser[] = {
+#if defined(GOOGLE_CHROME_BUILD)
+  FILE_PATH_LITERAL(".pdf"),
+  FILE_PATH_LITERAL(".swf"),
+#endif
+  FILE_PATH_LITERAL(".bmp"),
+  FILE_PATH_LITERAL(".jpg"),
+  FILE_PATH_LITERAL(".jpeg"),
+  FILE_PATH_LITERAL(".png"),
+  FILE_PATH_LITERAL(".webp"),
+  FILE_PATH_LITERAL(".gif"),
+  FILE_PATH_LITERAL(".txt"),
+  FILE_PATH_LITERAL(".html"),
+  FILE_PATH_LITERAL(".htm"),
+  FILE_PATH_LITERAL(".mhtml"),
+  FILE_PATH_LITERAL(".mht"),
+  FILE_PATH_LITERAL(".svg"),
+};
+
+// Returns true if |file_path| is viewable in the browser (ex. HTML file).
+bool IsViewableInBrowser(const base::FilePath& file_path) {
+  for (size_t i = 0; i < arraysize(kFileExtensionsViewableInBrowser); i++) {
+    if (file_path.MatchesExtension(kFileExtensionsViewableInBrowser[i]))
+      return true;
+  }
+  return false;
+}
+
+bool IsPepperPluginEnabled(Profile* profile,
+                           const base::FilePath& plugin_path) {
+  DCHECK(profile);
+
+  content::PepperPluginInfo* pepper_info =
+      PluginService::GetInstance()->GetRegisteredPpapiPluginInfo(plugin_path);
+  if (!pepper_info)
+    return false;
+
+  scoped_refptr<PluginPrefs> plugin_prefs = PluginPrefs::GetForProfile(profile);
+  if (!plugin_prefs.get())
+    return false;
+
+  return plugin_prefs->IsPluginEnabled(pepper_info->ToWebPluginInfo());
+}
+
+bool IsPdfPluginEnabled(Profile* profile) {
+  DCHECK(profile);
+
+  base::FilePath plugin_path;
+  PathService::Get(chrome::FILE_PDF_PLUGIN, &plugin_path);
+  return IsPepperPluginEnabled(profile, plugin_path);
+}
+
+bool IsFlashPluginEnabled(Profile* profile) {
+  DCHECK(profile);
+
+  base::FilePath plugin_path(
+      CommandLine::ForCurrentProcess()->GetSwitchValueNative(
+          switches::kPpapiFlashPath));
+  if (plugin_path.empty())
+    PathService::Get(chrome::FILE_PEPPER_FLASH_PLUGIN, &plugin_path);
+  return IsPepperPluginEnabled(profile, plugin_path);
+}
+
+void OpenNewTab(Profile* profile, const GURL& url) {
+  DCHECK(profile);
+
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  Browser* browser = chrome::FindOrCreateTabbedBrowser(
+      profile, chrome::HOST_DESKTOP_TYPE_ASH);
+  chrome::AddSelectedTabWithURL(browser, url, content::PAGE_TRANSITION_LINK);
+  // If the current browser is not tabbed then the new tab will be created
+  // in a different browser. Make sure it is visible.
+  browser->window()->Show();
+}
+
+void InstallCRX(Profile* profile, const base::FilePath& file_path) {
+  DCHECK(profile);
+
+  ExtensionService* service =
+      extensions::ExtensionSystem::Get(profile)->extension_service();
+  CHECK(service);
+
+  scoped_refptr<extensions::CrxInstaller> installer(
+      extensions::CrxInstaller::Create(
+          service,
+          scoped_ptr<ExtensionInstallPrompt>(new ExtensionInstallPrompt(
+              profile, NULL, NULL))));
+  installer->set_error_on_unsupported_requirements(true);
+  installer->set_is_gallery_install(false);
+  installer->set_allow_silent_install(false);
+  installer->InstallCrx(file_path);
+}
+
+// Called when a crx file on Drive was downloaded.
+void OnCRXDownloadCallback(Profile* profile,
+                           drive::FileError error,
+                           const base::FilePath& file,
+                           scoped_ptr<drive::ResourceEntry> entry) {
+  DCHECK(profile);
+
+  if (error != drive::FILE_ERROR_OK)
+    return;
+  InstallCRX(profile, file);
+}
+
+// Reads the alternate URL from a GDoc file. When it fails, returns a file URL
+// for |file_path| as fallback.
+// Note that an alternate url is a URL to open a hosted document.
+GURL ReadUrlFromGDocOnBlockingPool(const base::FilePath& file_path) {
+  GURL url = drive::util::ReadUrlFromGDocFile(file_path);
+  if (url.is_empty())
+    url = net::FilePathToFileURL(file_path);
+  return url;
+}
+
+}  // namespace
+
+bool OpenFileWithBrowser(Profile* profile, const base::FilePath& file_path) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(profile);
+
+  // For things supported natively by the browser, we should open it
+  // in a tab.
+  if (IsViewableInBrowser(file_path) ||
+      ShouldBeOpenedWithPlugin(profile, file_path.Extension())) {
+    GURL page_url = net::FilePathToFileURL(file_path);
+    // Override drive resource to point to internal handler instead of file URL.
+    if (drive::util::IsUnderDriveMountPoint(file_path)) {
+      page_url = drive::util::FilePathToDriveURL(
+          drive::util::ExtractDrivePath(file_path));
+    }
+    OpenNewTab(profile, page_url);
+    return true;
+  }
+
+  if (drive::util::HasGDocFileExtension(file_path)) {
+    if (drive::util::IsUnderDriveMountPoint(file_path)) {
+      // The file is on Google Docs. Open with drive URL.
+      GURL url = drive::util::FilePathToDriveURL(
+          drive::util::ExtractDrivePath(file_path));
+      OpenNewTab(profile, url);
+    } else {
+      // The file is local (downloaded from an attachment or otherwise copied).
+      // Parse the file to extract the Docs url and open this url.
+      base::PostTaskAndReplyWithResult(
+          BrowserThread::GetBlockingPool(),
+          FROM_HERE,
+          base::Bind(&ReadUrlFromGDocOnBlockingPool, file_path),
+          base::Bind(&OpenNewTab, static_cast<Profile*>(NULL)));
+    }
+    return true;
+  }
+
+  if (file_path.MatchesExtension(kCRXExtension)) {
+    if (drive::util::IsUnderDriveMountPoint(file_path)) {
+      drive::DriveIntegrationService* integration_service =
+          drive::DriveIntegrationServiceFactory::GetForProfile(profile);
+      if (!integration_service)
+        return false;
+      integration_service->file_system()->GetFileByPath(
+          drive::util::ExtractDrivePath(file_path),
+          base::Bind(&OnCRXDownloadCallback, profile));
+    } else {
+      InstallCRX(profile, file_path);
+    }
+    return true;
+  }
+
+  // Failed to open the file of unknown type.
+  LOG(WARNING) << "Unknown file type: " << file_path.value();
+  return false;
+}
+
+// If a bundled plugin is enabled, we should open pdf/swf files in a tab.
+bool ShouldBeOpenedWithPlugin(
+    Profile* profile,
+    const base::FilePath::StringType& file_extension) {
+  DCHECK(profile);
+
+  const base::FilePath file_path =
+      base::FilePath::FromUTF8Unsafe("dummy").AddExtension(file_extension);
+  if (file_path.MatchesExtension(kPdfExtension))
+    return IsPdfPluginEnabled(profile);
+  if (file_path.MatchesExtension(kSwfExtension))
+    return IsFlashPluginEnabled(profile);
+  return false;
+}
+
+}  // namespace util
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/open_with_browser.h b/chrome/browser/chromeos/extensions/file_manager/open_with_browser.h
new file mode 100644
index 0000000..66e09a9
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/open_with_browser.h
@@ -0,0 +1,39 @@
+// 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.
+//
+// This file provides utilities for opening files with the browser.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_OPEN_WITH_BROWSER_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_OPEN_WITH_BROWSER_H_
+
+#include "base/files/file_path.h"
+
+class Browser;
+class Profile;
+
+namespace file_manager {
+namespace util {
+
+// Opens the file specified by |file_path| with the browser for
+// |profile|. This function takes care of the following intricacies:
+//
+// - If there is no active browser window, open it.
+// - If the file is a Drive hosted document, the hosted document will be
+//   opened in the browser by extracting the right URL for the file.
+// - If the file is a CRX file, the CRX file will be installed.
+// - If the file is on Drive, the file will be downloaded from Drive as
+//   needed.
+//
+// Returns false if failed to open. This happens if the file type is unknown.
+bool OpenFileWithBrowser(Profile* profile, const base::FilePath& file_path);
+
+// Checks whether a pepper plugin for |file_extension| is enabled.
+bool ShouldBeOpenedWithPlugin(
+    Profile* profile,
+    const base::FilePath::StringType& file_extension);
+
+}  // namespace util
+}  // namespace file_manager
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_OPEN_WITH_BROWSER_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_base.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_base.cc
index d0779f9..1ed47c8 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_base.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_base.cc
@@ -7,7 +7,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/chromeos/drive/logging.h"
 
-namespace file_manager {
+namespace extensions {
 namespace {
 
 const int kSlowOperationThresholdMs = 500;  // In ms.
@@ -43,4 +43,4 @@
   AsyncExtensionFunction::SendResponse(success);
 }
 
-}  // namespace file_manager
+}  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_base.h b/chrome/browser/chromeos/extensions/file_manager/private_api_base.h
index d9b5ef4..308713b 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_base.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_base.h
@@ -10,7 +10,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/extensions/extension_function.h"
 
-namespace file_manager {
+namespace extensions {
 
 // This class adds a logging feature to AsyncExtensionFunction. Logging is
 // done when sending the response to JavaScript, using drive::util::Log().
@@ -40,6 +40,6 @@
   bool log_on_completion_;
 };
 
-}  // namespace file_manager
+}  // namespace extensions
 
 #endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_BASE_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.cc
index 9ea1bf2..cc2c15e 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.cc
@@ -11,28 +11,30 @@
 
 using content::BrowserThread;
 
-namespace file_manager {
+namespace extensions {
 
-CancelFileDialogFunction::CancelFileDialogFunction() {
+FileBrowserPrivateCancelDialogFunction::
+    FileBrowserPrivateCancelDialogFunction() {
 }
 
-CancelFileDialogFunction::~CancelFileDialogFunction() {
+FileBrowserPrivateCancelDialogFunction::
+    ~FileBrowserPrivateCancelDialogFunction() {
 }
 
-bool CancelFileDialogFunction::RunImpl() {
-  int32 tab_id = util::GetTabId(dispatcher());
+bool FileBrowserPrivateCancelDialogFunction::RunImpl() {
+  int32 tab_id = file_manager::util::GetTabId(dispatcher());
   SelectFileDialogExtension::OnFileSelectionCanceled(tab_id);
   SendResponse(true);
   return true;
 }
 
-SelectFileFunction::SelectFileFunction() {
+FileBrowserPrivateSelectFileFunction::FileBrowserPrivateSelectFileFunction() {
 }
 
-SelectFileFunction::~SelectFileFunction() {
+FileBrowserPrivateSelectFileFunction::~FileBrowserPrivateSelectFileFunction() {
 }
 
-bool SelectFileFunction::RunImpl() {
+bool FileBrowserPrivateSelectFileFunction::RunImpl() {
   if (args_->GetSize() != 4) {
     return false;
   }
@@ -45,23 +47,25 @@
   bool need_local_path = false;
   args_->GetBoolean(3, &need_local_path);
 
-  util::GetSelectedFileInfoLocalPathOption option =
-      util::NO_LOCAL_PATH_RESOLUTION;
+  file_manager::util::GetSelectedFileInfoLocalPathOption option =
+      file_manager::util::NO_LOCAL_PATH_RESOLUTION;
   if (need_local_path) {
     option = for_opening ?
-        util::NEED_LOCAL_PATH_FOR_OPENING : util::NEED_LOCAL_PATH_FOR_SAVING;
+        file_manager::util::NEED_LOCAL_PATH_FOR_OPENING :
+        file_manager::util::NEED_LOCAL_PATH_FOR_SAVING;
   }
 
-  util::GetSelectedFileInfo(
+  file_manager::util::GetSelectedFileInfo(
       render_view_host(),
       profile(),
       file_paths,
       option,
-      base::Bind(&SelectFileFunction::GetSelectedFileInfoResponse, this));
+      base::Bind(&FileBrowserPrivateSelectFileFunction::
+                     GetSelectedFileInfoResponse, this));
   return true;
 }
 
-void SelectFileFunction::GetSelectedFileInfoResponse(
+void FileBrowserPrivateSelectFileFunction::GetSelectedFileInfoResponse(
     const std::vector<ui::SelectedFileInfo>& files) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   if (files.size() != 1) {
@@ -70,18 +74,19 @@
   }
   int index;
   args_->GetInteger(1, &index);
-  int32 tab_id = util::GetTabId(dispatcher());
+  int32 tab_id = file_manager::util::GetTabId(dispatcher());
   SelectFileDialogExtension::OnFileSelected(tab_id, files[0], index);
   SendResponse(true);
 }
 
-SelectFilesFunction::SelectFilesFunction() {
+FileBrowserPrivateSelectFilesFunction::FileBrowserPrivateSelectFilesFunction() {
 }
 
-SelectFilesFunction::~SelectFilesFunction() {
+FileBrowserPrivateSelectFilesFunction::
+    ~FileBrowserPrivateSelectFilesFunction() {
 }
 
-bool SelectFilesFunction::RunImpl() {
+bool FileBrowserPrivateSelectFilesFunction::RunImpl() {
   if (args_->GetSize() != 2) {
     return false;
   }
@@ -102,22 +107,24 @@
   bool need_local_path = false;
   args_->GetBoolean(1, &need_local_path);
 
-  util::GetSelectedFileInfo(
+  file_manager::util::GetSelectedFileInfo(
       render_view_host(),
       profile(),
       file_urls,
       need_local_path ?
-          util::NEED_LOCAL_PATH_FOR_OPENING : util::NO_LOCAL_PATH_RESOLUTION,
-      base::Bind(&SelectFilesFunction::GetSelectedFileInfoResponse, this));
+          file_manager::util::NEED_LOCAL_PATH_FOR_OPENING :
+          file_manager::util::NO_LOCAL_PATH_RESOLUTION,
+      base::Bind(&FileBrowserPrivateSelectFilesFunction::
+                     GetSelectedFileInfoResponse, this));
   return true;
 }
 
-void SelectFilesFunction::GetSelectedFileInfoResponse(
+void FileBrowserPrivateSelectFilesFunction::GetSelectedFileInfoResponse(
     const std::vector<ui::SelectedFileInfo>& files) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  int32 tab_id = util::GetTabId(dispatcher());
+  int32 tab_id = file_manager::util::GetTabId(dispatcher());
   SelectFileDialogExtension::OnMultiFilesSelected(tab_id, files);
   SendResponse(true);
 }
 
-}  // namespace file_manager
+}  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.h b/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.h
index ba8cdf1..5d23666 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_dialog.h
@@ -15,32 +15,34 @@
 struct SelectedFileInfo;
 }
 
-namespace file_manager {
+namespace extensions {
 
 // Cancel file selection Dialog.  Closes the dialog window.
-class CancelFileDialogFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateCancelDialogFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.cancelDialog",
                              FILEBROWSERPRIVATE_CANCELDIALOG)
 
-  CancelFileDialogFunction();
+  FileBrowserPrivateCancelDialogFunction();
 
  protected:
-  virtual ~CancelFileDialogFunction();
+  virtual ~FileBrowserPrivateCancelDialogFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
 };
 
-class SelectFileFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateSelectFileFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.selectFile",
                              FILEBROWSERPRIVATE_SELECTFILE)
 
-  SelectFileFunction();
+  FileBrowserPrivateSelectFileFunction();
 
  protected:
-  virtual ~SelectFileFunction();
+  virtual ~FileBrowserPrivateSelectFileFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
@@ -52,15 +54,16 @@
 };
 
 // Select multiple files.  Closes the dialog window.
-class SelectFilesFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateSelectFilesFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.selectFiles",
                              FILEBROWSERPRIVATE_SELECTFILES)
 
-  SelectFilesFunction();
+  FileBrowserPrivateSelectFilesFunction();
 
  protected:
-  virtual ~SelectFilesFunction();
+  virtual ~FileBrowserPrivateSelectFilesFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
@@ -71,6 +74,6 @@
       const std::vector<ui::SelectedFileInfo>& files);
 };
 
-}  // namespace file_manager
+}  // namespace extensions
 
 #endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_DIALOG_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
index 0032cdb..257f8ae 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
@@ -22,7 +22,7 @@
 
 using content::BrowserThread;
 
-namespace file_manager {
+namespace extensions {
 namespace {
 
 // List of connection types of drive.
@@ -65,13 +65,15 @@
 
 }  // namespace
 
-GetDriveEntryPropertiesFunction::GetDriveEntryPropertiesFunction() {
+FileBrowserPrivateGetDriveEntryPropertiesFunction::
+    FileBrowserPrivateGetDriveEntryPropertiesFunction() {
 }
 
-GetDriveEntryPropertiesFunction::~GetDriveEntryPropertiesFunction() {
+FileBrowserPrivateGetDriveEntryPropertiesFunction::
+    ~FileBrowserPrivateGetDriveEntryPropertiesFunction() {
 }
 
-bool GetDriveEntryPropertiesFunction::RunImpl() {
+bool FileBrowserPrivateGetDriveEntryPropertiesFunction::RunImpl() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   std::string file_url_str;
@@ -80,7 +82,8 @@
 
   GURL file_url = GURL(file_url_str);
   file_path_ = drive::util::ExtractDrivePath(
-      util::GetLocalPathFromURL(render_view_host(), profile(), file_url));
+      file_manager::util::GetLocalPathFromURL(
+          render_view_host(), profile(), file_url));
 
   properties_.reset(new base::DictionaryValue);
   properties_->SetString("fileUrl", file_url.spec());
@@ -96,11 +99,12 @@
 
   integration_service->file_system()->GetResourceEntryByPath(
       file_path_,
-      base::Bind(&GetDriveEntryPropertiesFunction::OnGetFileInfo, this));
+      base::Bind(&FileBrowserPrivateGetDriveEntryPropertiesFunction::
+                     OnGetFileInfo, this));
   return true;
 }
 
-void GetDriveEntryPropertiesFunction::OnGetFileInfo(
+void FileBrowserPrivateGetDriveEntryPropertiesFunction::OnGetFileInfo(
     drive::FileError error,
     scoped_ptr<drive::ResourceEntry> entry) {
   DCHECK(properties_);
@@ -131,49 +135,39 @@
   const drive::FileSpecificInfo& file_specific_info =
       entry->file_specific_info();
 
-  // Get drive WebApps that can accept this file.
+  // Get drive WebApps that can accept this file. We just need to extract the
+  // doc icon for the drive app, which is set as default.
   ScopedVector<drive::DriveAppInfo> drive_apps;
   integration_service->drive_app_registry()->GetAppsForFile(
       file_path_, file_specific_info.content_mime_type(), &drive_apps);
   if (!drive_apps.empty()) {
-    std::string default_task_id = file_tasks::GetDefaultTaskIdFromPrefs(
-        profile_,
-        file_specific_info.content_mime_type(),
-        file_path_.Extension());
-    file_tasks::TaskDescriptor default_task;
-    file_tasks::ParseTaskID(default_task_id, &default_task);
+    std::string default_task_id =
+        file_manager::file_tasks::GetDefaultTaskIdFromPrefs(
+            *profile_->GetPrefs(),
+            file_specific_info.content_mime_type(),
+            file_path_.Extension());
+    file_manager::file_tasks::TaskDescriptor default_task;
+    file_manager::file_tasks::ParseTaskID(default_task_id, &default_task);
     DCHECK(default_task_id.empty() || !default_task.app_id.empty());
-
-    ListValue* apps = new ListValue();
-    properties_->Set("driveApps", apps);
-    for (ScopedVector<drive::DriveAppInfo>::const_iterator it =
-             drive_apps.begin();
-         it != drive_apps.end(); ++it) {
-      const drive::DriveAppInfo* app_info = *it;
-      DictionaryValue* app = new DictionaryValue();
-      app->SetString("appId", app_info->app_id);
-      app->SetString("appName", app_info->app_name);
-      GURL app_icon = util::FindPreferredIcon(app_info->app_icons,
-                                              util::kPreferredIconSize);
-      if (!app_icon.is_empty())
-        app->SetString("appIcon", app_icon.spec());
-      GURL doc_icon = util::FindPreferredIcon(app_info->document_icons,
-                                              util::kPreferredIconSize);
-      if (!doc_icon.is_empty())
-        app->SetString("docIcon", doc_icon.spec());
-      app->SetString("objectType", app_info->object_type);
-      app->SetBoolean("isPrimary",
-                      default_task.app_id == app_info->app_id);
-      apps->Append(app);
+    for (size_t i = 0; i < drive_apps.size(); ++i) {
+      const drive::DriveAppInfo* app_info = drive_apps[i];
+      if (default_task.app_id == app_info->app_id) {
+        // The drive app is set as default. Files.app should use the doc icon.
+        const GURL doc_icon =
+            drive::util::FindPreferredIcon(app_info->document_icons,
+                                           drive::util::kPreferredIconSize);
+        properties_->SetString("customIconUrl", doc_icon.spec());
+      }
     }
   }
 
-  integration_service->file_system()->GetCacheEntryByResourceId(
-      entry->resource_id(),
-      base::Bind(&GetDriveEntryPropertiesFunction::CacheStateReceived, this));
+  integration_service->file_system()->GetCacheEntryByPath(
+      file_path_,
+      base::Bind(&FileBrowserPrivateGetDriveEntryPropertiesFunction::
+                     CacheStateReceived, this));
 }
 
-void GetDriveEntryPropertiesFunction::CacheStateReceived(
+void FileBrowserPrivateGetDriveEntryPropertiesFunction::CacheStateReceived(
     bool /* success */,
     const drive::FileCacheEntry& cache_entry) {
   // In case of an error (i.e. success is false), cache_entry.is_*() all
@@ -185,21 +179,23 @@
   CompleteGetFileProperties(drive::FILE_ERROR_OK);
 }
 
-void GetDriveEntryPropertiesFunction::CompleteGetFileProperties(
-    drive::FileError error) {
+void FileBrowserPrivateGetDriveEntryPropertiesFunction::
+    CompleteGetFileProperties(drive::FileError error) {
   if (error != drive::FILE_ERROR_OK)
     properties_->SetInteger("errorCode", error);
   SetResult(properties_.release());
   SendResponse(true);
 }
 
-PinDriveFileFunction::PinDriveFileFunction() {
+FileBrowserPrivatePinDriveFileFunction::
+    FileBrowserPrivatePinDriveFileFunction() {
 }
 
-PinDriveFileFunction::~PinDriveFileFunction() {
+FileBrowserPrivatePinDriveFileFunction::
+    ~FileBrowserPrivatePinDriveFileFunction() {
 }
 
-bool PinDriveFileFunction::RunImpl() {
+bool FileBrowserPrivatePinDriveFileFunction::RunImpl() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   std::string url;
   bool set_pin = false;
@@ -216,19 +212,22 @@
     return false;
 
   base::FilePath drive_path =
-      drive::util::ExtractDrivePath(
-          util::GetLocalPathFromURL(render_view_host(), profile(), GURL(url)));
+      drive::util::ExtractDrivePath(file_manager::util::GetLocalPathFromURL(
+          render_view_host(), profile(), GURL(url)));
   if (set_pin) {
     file_system->Pin(drive_path,
-                     base::Bind(&PinDriveFileFunction::OnPinStateSet, this));
+                     base::Bind(&FileBrowserPrivatePinDriveFileFunction::
+                                    OnPinStateSet, this));
   } else {
     file_system->Unpin(drive_path,
-                       base::Bind(&PinDriveFileFunction::OnPinStateSet, this));
+                       base::Bind(&FileBrowserPrivatePinDriveFileFunction::
+                                      OnPinStateSet, this));
   }
   return true;
 }
 
-void PinDriveFileFunction::OnPinStateSet(drive::FileError error) {
+void FileBrowserPrivatePinDriveFileFunction::
+    OnPinStateSet(drive::FileError error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   if (error == drive::FILE_ERROR_OK) {
@@ -239,14 +238,15 @@
   }
 }
 
-GetDriveFilesFunction::GetDriveFilesFunction()
-    : local_paths_(NULL) {
+FileBrowserPrivateGetDriveFilesFunction::
+    FileBrowserPrivateGetDriveFilesFunction() : local_paths_(NULL) {
 }
 
-GetDriveFilesFunction::~GetDriveFilesFunction() {
+FileBrowserPrivateGetDriveFilesFunction::
+    ~FileBrowserPrivateGetDriveFilesFunction() {
 }
 
-bool GetDriveFilesFunction::RunImpl() {
+bool FileBrowserPrivateGetDriveFilesFunction::RunImpl() {
   ListValue* file_urls_as_strings = NULL;
   if (!args_->GetList(0, &file_urls_as_strings))
     return false;
@@ -256,7 +256,7 @@
     std::string file_url_as_string;
     if (!file_urls_as_strings->GetString(i, &file_url_as_string))
       return false;
-    const base::FilePath path = util::GetLocalPathFromURL(
+    const base::FilePath path = file_manager::util::GetLocalPathFromURL(
         render_view_host(), profile(), GURL(file_url_as_string));
     DCHECK(drive::util::IsUnderDriveMountPoint(path));
     base::FilePath drive_path = drive::util::ExtractDrivePath(path);
@@ -268,7 +268,7 @@
   return true;
 }
 
-void GetDriveFilesFunction::GetFileOrSendResponse() {
+void FileBrowserPrivateGetDriveFilesFunction::GetFileOrSendResponse() {
   // Send the response if all files are obtained.
   if (remaining_drive_paths_.empty()) {
     SetResult(local_paths_);
@@ -290,11 +290,11 @@
 
   integration_service->file_system()->GetFileByPath(
       drive_path,
-      base::Bind(&GetDriveFilesFunction::OnFileReady, this));
+      base::Bind(&FileBrowserPrivateGetDriveFilesFunction::OnFileReady, this));
 }
 
 
-void GetDriveFilesFunction::OnFileReady(
+void FileBrowserPrivateGetDriveFilesFunction::OnFileReady(
     drive::FileError error,
     const base::FilePath& local_path,
     scoped_ptr<drive::ResourceEntry> entry) {
@@ -321,13 +321,15 @@
   GetFileOrSendResponse();
 }
 
-CancelFileTransfersFunction::CancelFileTransfersFunction() {
+FileBrowserPrivateCancelFileTransfersFunction::
+    FileBrowserPrivateCancelFileTransfersFunction() {
 }
 
-CancelFileTransfersFunction::~CancelFileTransfersFunction() {
+FileBrowserPrivateCancelFileTransfersFunction::
+    ~FileBrowserPrivateCancelFileTransfersFunction() {
 }
 
-bool CancelFileTransfersFunction::RunImpl() {
+bool FileBrowserPrivateCancelFileTransfersFunction::RunImpl() {
   ListValue* url_list = NULL;
   if (!args_->GetList(0, &url_list))
     return false;
@@ -356,7 +358,7 @@
     std::string url_as_string;
     url_list->GetString(i, &url_as_string);
 
-    base::FilePath file_path = util::GetLocalPathFromURL(
+    base::FilePath file_path = file_manager::util::GetLocalPathFromURL(
         render_view_host(), profile(), GURL(url_as_string));
     if (file_path.empty())
       continue;
@@ -382,13 +384,15 @@
   return true;
 }
 
-SearchDriveFunction::SearchDriveFunction() {
+FileBrowserPrivateSearchDriveFunction::
+    FileBrowserPrivateSearchDriveFunction() {
 }
 
-SearchDriveFunction::~SearchDriveFunction() {
+FileBrowserPrivateSearchDriveFunction::
+    ~FileBrowserPrivateSearchDriveFunction() {
 }
 
-bool SearchDriveFunction::RunImpl() {
+bool FileBrowserPrivateSearchDriveFunction::RunImpl() {
   DictionaryValue* search_params;
   if (!args_->GetDictionary(0, &search_params))
     return false;
@@ -408,14 +412,14 @@
     return false;
 
   integration_service->file_system()->Search(
-      query, GURL(next_feed),
-      base::Bind(&SearchDriveFunction::OnSearch, this));
+      query, next_feed,
+      base::Bind(&FileBrowserPrivateSearchDriveFunction::OnSearch, this));
   return true;
 }
 
-void SearchDriveFunction::OnSearch(
+void FileBrowserPrivateSearchDriveFunction::OnSearch(
     drive::FileError error,
-    const GURL& next_feed,
+    const std::string& next_feed,
     scoped_ptr<std::vector<drive::SearchResultInfo> > results) {
   if (error != drive::FILE_ERROR_OK) {
     SendResponse(false);
@@ -445,19 +449,21 @@
 
   base::DictionaryValue* result = new DictionaryValue();
   result->Set("entries", entries);
-  result->SetString("nextFeed", next_feed.spec());
+  result->SetString("nextFeed", next_feed);
 
   SetResult(result);
   SendResponse(true);
 }
 
-SearchDriveMetadataFunction::SearchDriveMetadataFunction() {
+FileBrowserPrivateSearchDriveMetadataFunction::
+    FileBrowserPrivateSearchDriveMetadataFunction() {
 }
 
-SearchDriveMetadataFunction::~SearchDriveMetadataFunction() {
+FileBrowserPrivateSearchDriveMetadataFunction::
+    ~FileBrowserPrivateSearchDriveMetadataFunction() {
 }
 
-bool SearchDriveMetadataFunction::RunImpl() {
+bool FileBrowserPrivateSearchDriveMetadataFunction::RunImpl() {
   DictionaryValue* search_params;
   if (!args_->GetDictionary(0, &search_params))
     return false;
@@ -503,11 +509,12 @@
       query,
       options,
       max_results,
-      base::Bind(&SearchDriveMetadataFunction::OnSearchMetadata, this));
+      base::Bind(&FileBrowserPrivateSearchDriveMetadataFunction::
+                     OnSearchMetadata, this));
   return true;
 }
 
-void SearchDriveMetadataFunction::OnSearchMetadata(
+void FileBrowserPrivateSearchDriveMetadataFunction::OnSearchMetadata(
     drive::FileError error,
     scoped_ptr<drive::MetadataSearchResultVector> results) {
   if (error != drive::FILE_ERROR_OK) {
@@ -550,13 +557,15 @@
   SendResponse(true);
 }
 
-ClearDriveCacheFunction::ClearDriveCacheFunction() {
+FileBrowserPrivateClearDriveCacheFunction::
+    FileBrowserPrivateClearDriveCacheFunction() {
 }
 
-ClearDriveCacheFunction::~ClearDriveCacheFunction() {
+FileBrowserPrivateClearDriveCacheFunction::
+    ~FileBrowserPrivateClearDriveCacheFunction() {
 }
 
-bool ClearDriveCacheFunction::RunImpl() {
+bool FileBrowserPrivateClearDriveCacheFunction::RunImpl() {
   drive::DriveIntegrationService* integration_service =
       drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
   // |integration_service| is NULL if Drive is disabled.
@@ -572,13 +581,15 @@
   return true;
 }
 
-GetDriveConnectionStateFunction::GetDriveConnectionStateFunction() {
+FileBrowserPrivateGetDriveConnectionStateFunction::
+    FileBrowserPrivateGetDriveConnectionStateFunction() {
 }
 
-GetDriveConnectionStateFunction::~GetDriveConnectionStateFunction() {
+FileBrowserPrivateGetDriveConnectionStateFunction::
+    ~FileBrowserPrivateGetDriveConnectionStateFunction() {
 }
 
-bool GetDriveConnectionStateFunction::RunImpl() {
+bool FileBrowserPrivateGetDriveConnectionStateFunction::RunImpl() {
   scoped_ptr<DictionaryValue> value(new DictionaryValue());
   scoped_ptr<ListValue> reasons(new ListValue());
 
@@ -616,13 +627,15 @@
   return true;
 }
 
-RequestAccessTokenFunction::RequestAccessTokenFunction() {
+FileBrowserPrivateRequestAccessTokenFunction::
+    FileBrowserPrivateRequestAccessTokenFunction() {
 }
 
-RequestAccessTokenFunction::~RequestAccessTokenFunction() {
+FileBrowserPrivateRequestAccessTokenFunction::
+    ~FileBrowserPrivateRequestAccessTokenFunction() {
 }
 
-bool RequestAccessTokenFunction::RunImpl() {
+bool FileBrowserPrivateRequestAccessTokenFunction::RunImpl() {
   drive::DriveIntegrationService* integration_service =
       drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
   bool refresh;
@@ -641,28 +654,31 @@
   // Retrieve the cached auth token (if available), otherwise the AuthService
   // instance will try to refetch it.
   integration_service->drive_service()->RequestAccessToken(
-      base::Bind(&RequestAccessTokenFunction::OnAccessTokenFetched, this));
+      base::Bind(&FileBrowserPrivateRequestAccessTokenFunction::
+                      OnAccessTokenFetched, this));
   return true;
 }
 
-void RequestAccessTokenFunction::OnAccessTokenFetched(
-    google_apis::GDataErrorCode code, const std::string& access_token) {
+void FileBrowserPrivateRequestAccessTokenFunction::OnAccessTokenFetched(
+    google_apis::GDataErrorCode code,
+    const std::string& access_token) {
   SetResult(new base::StringValue(access_token));
   SendResponse(true);
 }
 
-GetShareUrlFunction::GetShareUrlFunction() {
+FileBrowserPrivateGetShareUrlFunction::FileBrowserPrivateGetShareUrlFunction() {
 }
 
-GetShareUrlFunction::~GetShareUrlFunction() {
+FileBrowserPrivateGetShareUrlFunction::
+    ~FileBrowserPrivateGetShareUrlFunction() {
 }
 
-bool GetShareUrlFunction::RunImpl() {
+bool FileBrowserPrivateGetShareUrlFunction::RunImpl() {
   std::string file_url;
   if (!args_->GetString(0, &file_url))
     return false;
 
-  const base::FilePath path = util::GetLocalPathFromURL(
+  const base::FilePath path = file_manager::util::GetLocalPathFromURL(
       render_view_host(), profile(), GURL(file_url));
   DCHECK(drive::util::IsUnderDriveMountPoint(path));
 
@@ -676,14 +692,15 @@
 
   integration_service->file_system()->GetShareUrl(
       drive_path,
-      util::GetFileManagerBaseUrl(),  // embed origin
-      base::Bind(&GetShareUrlFunction::OnGetShareUrl, this));
+      file_manager::util::GetFileManagerBaseUrl(),  // embed origin
+      base::Bind(&FileBrowserPrivateGetShareUrlFunction::OnGetShareUrl, this));
   return true;
 }
 
 
-void GetShareUrlFunction::OnGetShareUrl(drive::FileError error,
-                                        const GURL& share_url) {
+void FileBrowserPrivateGetShareUrlFunction::OnGetShareUrl(
+    drive::FileError error,
+    const GURL& share_url) {
   if (error != drive::FILE_ERROR_OK) {
     error_ = "Share Url for this item is not available.";
     SendResponse(false);
@@ -694,4 +711,4 @@
   SendResponse(true);
 }
 
-}  // namespace file_manager
+}  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.h b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.h
index 33eade3..e1b2995 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.h
@@ -18,20 +18,21 @@
 struct SearchResultInfo;
 }
 
-namespace file_manager {
+namespace extensions {
 
 // Retrieves property information for an entry and returns it as a dictionary.
 // On error, returns a dictionary with the key "error" set to the error number
 // (drive::FileError).
-class GetDriveEntryPropertiesFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateGetDriveEntryPropertiesFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getDriveEntryProperties",
                              FILEBROWSERPRIVATE_GETDRIVEFILEPROPERTIES)
 
-  GetDriveEntryPropertiesFunction();
+  FileBrowserPrivateGetDriveEntryPropertiesFunction();
 
  protected:
-  virtual ~GetDriveEntryPropertiesFunction();
+  virtual ~FileBrowserPrivateGetDriveEntryPropertiesFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
@@ -50,15 +51,16 @@
 };
 
 // Implements the chrome.fileBrowserPrivate.pinDriveFile method.
-class PinDriveFileFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivatePinDriveFileFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.pinDriveFile",
                              FILEBROWSERPRIVATE_PINDRIVEFILE)
 
-  PinDriveFileFunction();
+  FileBrowserPrivatePinDriveFileFunction();
 
  protected:
-  virtual ~PinDriveFileFunction();
+  virtual ~FileBrowserPrivatePinDriveFileFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
@@ -76,15 +78,16 @@
 // file manager should check if the local paths returned from getDriveFiles()
 // contain empty paths.
 // TODO(satorux): Should we propagate error types to the JavaScript layer?
-class GetDriveFilesFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateGetDriveFilesFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getDriveFiles",
                              FILEBROWSERPRIVATE_GETDRIVEFILES)
 
-  GetDriveFilesFunction();
+  FileBrowserPrivateGetDriveFilesFunction();
 
  protected:
-  virtual ~GetDriveFilesFunction();
+  virtual ~FileBrowserPrivateGetDriveFilesFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
@@ -105,50 +108,53 @@
 };
 
 // Implements the chrome.fileBrowserPrivate.cancelFileTransfers method.
-class CancelFileTransfersFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateCancelFileTransfersFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.cancelFileTransfers",
                              FILEBROWSERPRIVATE_CANCELFILETRANSFERS)
 
-  CancelFileTransfersFunction();
+  FileBrowserPrivateCancelFileTransfersFunction();
 
  protected:
-  virtual ~CancelFileTransfersFunction();
+  virtual ~FileBrowserPrivateCancelFileTransfersFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
 };
 
-class SearchDriveFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateSearchDriveFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.searchDrive",
                              FILEBROWSERPRIVATE_SEARCHDRIVE)
 
-  SearchDriveFunction();
+  FileBrowserPrivateSearchDriveFunction();
 
  protected:
-  virtual ~SearchDriveFunction();
+  virtual ~FileBrowserPrivateSearchDriveFunction();
 
   virtual bool RunImpl() OVERRIDE;
 
  private:
   // Callback for Search().
   void OnSearch(drive::FileError error,
-                const GURL& next_feed,
+                const std::string& next_feed,
                 scoped_ptr<std::vector<drive::SearchResultInfo> > result_paths);
 };
 
-// Similar to SearchDriveFunction but this one is used for searching drive
-// metadata which is stored locally.
-class SearchDriveMetadataFunction : public LoggedAsyncExtensionFunction {
+// Similar to FileBrowserPrivateSearchDriveFunction but this one is used for
+// searching drive metadata which is stored locally.
+class FileBrowserPrivateSearchDriveMetadataFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.searchDriveMetadata",
                              FILEBROWSERPRIVATE_SEARCHDRIVEMETADATA)
 
-  SearchDriveMetadataFunction();
+  FileBrowserPrivateSearchDriveMetadataFunction();
 
  protected:
-  virtual ~SearchDriveMetadataFunction();
+  virtual ~FileBrowserPrivateSearchDriveMetadataFunction();
 
   virtual bool RunImpl() OVERRIDE;
 
@@ -158,44 +164,47 @@
                         scoped_ptr<drive::MetadataSearchResultVector> results);
 };
 
-class ClearDriveCacheFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateClearDriveCacheFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.clearDriveCache",
                              FILEBROWSERPRIVATE_CLEARDRIVECACHE)
 
-  ClearDriveCacheFunction();
+  FileBrowserPrivateClearDriveCacheFunction();
 
  protected:
-  virtual ~ClearDriveCacheFunction();
+  virtual ~FileBrowserPrivateClearDriveCacheFunction();
 
   virtual bool RunImpl() OVERRIDE;
 };
 
 // Implements the chrome.fileBrowserPrivate.getDriveConnectionState method.
-class GetDriveConnectionStateFunction : public SyncExtensionFunction {
+class FileBrowserPrivateGetDriveConnectionStateFunction
+    : public SyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION(
       "fileBrowserPrivate.getDriveConnectionState",
       FILEBROWSERPRIVATE_GETDRIVECONNECTIONSTATE);
 
-  GetDriveConnectionStateFunction();
+  FileBrowserPrivateGetDriveConnectionStateFunction();
 
  protected:
-  virtual ~GetDriveConnectionStateFunction();
+  virtual ~FileBrowserPrivateGetDriveConnectionStateFunction();
 
   virtual bool RunImpl() OVERRIDE;
 };
 
 // Implements the chrome.fileBrowserPrivate.requestAccessToken method.
-class RequestAccessTokenFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateRequestAccessTokenFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.requestAccessToken",
                              FILEBROWSERPRIVATE_REQUESTACCESSTOKEN)
 
-  RequestAccessTokenFunction();
+  FileBrowserPrivateRequestAccessTokenFunction();
 
  protected:
-  virtual ~RequestAccessTokenFunction();
+  virtual ~FileBrowserPrivateRequestAccessTokenFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
@@ -206,15 +215,16 @@
 };
 
 // Implements the chrome.fileBrowserPrivate.getShareUrl method.
-class GetShareUrlFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateGetShareUrlFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getShareUrl",
                              FILEBROWSERPRIVATE_GETSHAREURL)
 
-  GetShareUrlFunction();
+  FileBrowserPrivateGetShareUrlFunction();
 
  protected:
-  virtual ~GetShareUrlFunction();
+  virtual ~FileBrowserPrivateGetShareUrlFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
@@ -224,6 +234,6 @@
   void OnGetShareUrl(drive::FileError error, const GURL& share_url);
 };
 
-}  // namespace file_manager
+}  // namespace extensions
 
 #endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_DRIVE_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
index f709142..7e94990 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.cc
@@ -9,6 +9,7 @@
 #include <sys/types.h>
 #include <utime.h>
 
+#include "base/path_service.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/strings/stringprintf.h"
 #include "base/task_runner_util.h"
@@ -19,7 +20,6 @@
 #include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/chromeos/extensions/file_manager/event_router.h"
 #include "chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.h"
-#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
 #include "chrome/browser/chromeos/extensions/file_manager/fileapi_util.h"
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
 #include "chrome/browser/chromeos/fileapi/file_system_backend.h"
@@ -44,7 +44,7 @@
 using content::WebContents;
 using fileapi::FileSystemURL;
 
-namespace file_manager {
+namespace extensions {
 namespace {
 
 // Error messages.
@@ -73,7 +73,7 @@
   std::string mount_path;
   if (!volume->mount_path().empty()) {
     base::FilePath relative_mount_path;
-    util::ConvertAbsoluteFilePathToRelativeFileSystemPath(
+    file_manager::util::ConvertAbsoluteFilePathToRelativeFileSystemPath(
         profile, extension_id, base::FilePath(volume->mount_path()),
         &relative_mount_path);
     mount_path = relative_mount_path.value();
@@ -98,6 +98,13 @@
   return volume_info;
 }
 
+base::DictionaryValue* CreateDownloadsVolumeMetadata() {
+  base::DictionaryValue* volume_info = new base::DictionaryValue;
+  volume_info->SetString("mountPath", "Downloads");
+  volume_info->SetBoolean("isReadOnly", false);
+  return volume_info;
+}
+
 // Sets permissions for the Drive mount point so Files.app can access files
 // in the mount point directory. It's safe to call this function even if
 // Drive is disabled by the setting (i.e. prefs::kDisableDrive is true).
@@ -110,10 +117,9 @@
     return;
   }
 
-  content::SiteInstance* site_instance = render_view_host->GetSiteInstance();
   fileapi::ExternalFileSystemBackend* backend =
-      BrowserContext::GetStoragePartition(profile, site_instance)->
-      GetFileSystemContext()->external_backend();
+      file_manager::util::GetFileSystemContextForRenderViewHost(
+          profile, render_view_host)->external_backend();
   if (!backend)
     return;
 
@@ -172,13 +178,15 @@
 
 }  // namespace
 
-RequestFileSystemFunction::RequestFileSystemFunction() {
+FileBrowserPrivateRequestFileSystemFunction::
+    FileBrowserPrivateRequestFileSystemFunction() {
 }
 
-RequestFileSystemFunction::~RequestFileSystemFunction() {
+FileBrowserPrivateRequestFileSystemFunction::
+    ~FileBrowserPrivateRequestFileSystemFunction() {
 }
 
-void RequestFileSystemFunction::DidOpenFileSystem(
+void FileBrowserPrivateRequestFileSystemFunction::DidOpenFileSystem(
     scoped_refptr<fileapi::FileSystemContext> file_system_context,
     base::PlatformFileError result,
     const std::string& name,
@@ -222,7 +230,7 @@
   SendResponse(true);
 }
 
-void RequestFileSystemFunction::DidFail(
+void FileBrowserPrivateRequestFileSystemFunction::DidFail(
     base::PlatformFileError error_code) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
@@ -230,10 +238,11 @@
   SendResponse(false);
 }
 
-bool RequestFileSystemFunction::SetupFileSystemAccessPermissions(
-    scoped_refptr<fileapi::FileSystemContext> file_system_context,
-    int child_id,
-    scoped_refptr<const extensions::Extension> extension) {
+bool FileBrowserPrivateRequestFileSystemFunction::
+    SetupFileSystemAccessPermissions(
+        scoped_refptr<fileapi::FileSystemContext> file_system_context,
+        int child_id,
+        scoped_refptr<const extensions::Extension> extension) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   if (!extension.get())
@@ -265,7 +274,7 @@
   return true;
 }
 
-bool RequestFileSystemFunction::RunImpl() {
+bool FileBrowserPrivateRequestFileSystemFunction::RunImpl() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   if (!dispatcher() || !render_view_host() || !render_view_host()->GetProcess())
@@ -274,7 +283,7 @@
   set_log_on_completion(true);
 
   scoped_refptr<fileapi::FileSystemContext> file_system_context =
-      util::GetFileSystemContextForRenderViewHost(
+      file_manager::util::GetFileSystemContextForRenderViewHost(
           profile(), render_view_host());
 
   const GURL origin_url = source_url_.GetOrigin();
@@ -282,7 +291,8 @@
       origin_url,
       fileapi::kFileSystemTypeExternal,
       fileapi::OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
-      base::Bind(&RequestFileSystemFunction::DidOpenFileSystem,
+      base::Bind(&FileBrowserPrivateRequestFileSystemFunction::
+                     DidOpenFileSystem,
                  this,
                  file_system_context));
   return true;
@@ -313,7 +323,7 @@
     return false;
 
   scoped_refptr<fileapi::FileSystemContext> file_system_context =
-      util::GetFileSystemContextForRenderViewHost(
+      file_manager::util::GetFileSystemContextForRenderViewHost(
           profile(), render_view_host());
 
   FileSystemURL file_watch_url = file_system_context->CrackURL(GURL(url));
@@ -328,52 +338,58 @@
   return true;
 }
 
-AddFileWatchFunction::AddFileWatchFunction() {
+FileBrowserPrivateAddFileWatchFunction::
+    FileBrowserPrivateAddFileWatchFunction() {
 }
 
-AddFileWatchFunction::~AddFileWatchFunction() {
+FileBrowserPrivateAddFileWatchFunction::
+    ~FileBrowserPrivateAddFileWatchFunction() {
 }
 
-void AddFileWatchFunction::PerformFileWatchOperation(
+void FileBrowserPrivateAddFileWatchFunction::PerformFileWatchOperation(
     const base::FilePath& local_path,
     const base::FilePath& virtual_path,
     const std::string& extension_id) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-  EventRouter* event_router =
-      FileBrowserPrivateAPI::Get(profile_)->event_router();
+  file_manager::EventRouter* event_router =
+      file_manager::FileBrowserPrivateAPI::Get(profile_)->event_router();
   event_router->AddFileWatch(
       local_path,
       virtual_path,
       extension_id,
-      base::Bind(&AddFileWatchFunction::Respond, this));
+      base::Bind(&FileBrowserPrivateAddFileWatchFunction::Respond, this));
 }
 
-RemoveFileWatchFunction::RemoveFileWatchFunction() {
+FileBrowserPrivateRemoveFileWatchFunction::
+    FileBrowserPrivateRemoveFileWatchFunction() {
 }
 
-RemoveFileWatchFunction::~RemoveFileWatchFunction() {
+FileBrowserPrivateRemoveFileWatchFunction::
+    ~FileBrowserPrivateRemoveFileWatchFunction() {
 }
 
-void RemoveFileWatchFunction::PerformFileWatchOperation(
+void FileBrowserPrivateRemoveFileWatchFunction::PerformFileWatchOperation(
     const base::FilePath& local_path,
     const base::FilePath& unused,
     const std::string& extension_id) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-  EventRouter* event_router =
-      FileBrowserPrivateAPI::Get(profile_)->event_router();
+  file_manager::EventRouter* event_router =
+      file_manager::FileBrowserPrivateAPI::Get(profile_)->event_router();
   event_router->RemoveFileWatch(local_path, extension_id);
   Respond(true);
 }
 
-SetLastModifiedFunction::SetLastModifiedFunction() {
+FileBrowserPrivateSetLastModifiedFunction::
+    FileBrowserPrivateSetLastModifiedFunction() {
 }
 
-SetLastModifiedFunction::~SetLastModifiedFunction() {
+FileBrowserPrivateSetLastModifiedFunction::
+    ~FileBrowserPrivateSetLastModifiedFunction() {
 }
 
-bool SetLastModifiedFunction::RunImpl() {
+bool FileBrowserPrivateSetLastModifiedFunction::RunImpl() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   if (args_->GetSize() != 2) {
@@ -388,7 +404,7 @@
   if (!args_->GetString(1, &timestamp))
     return false;
 
-  base::FilePath local_path = util::GetLocalPathFromURL(
+  base::FilePath local_path = file_manager::util::GetLocalPathFromURL(
       render_view_host(), profile(), GURL(file_url));
 
   base::PostTaskAndReplyWithResult(
@@ -397,18 +413,20 @@
       base::Bind(&SetLastModifiedOnBlockingPool,
                  local_path,
                  strtoul(timestamp.c_str(), NULL, 0)),
-      base::Bind(&SetLastModifiedFunction::SendResponse,
+      base::Bind(&FileBrowserPrivateSetLastModifiedFunction::SendResponse,
                  this));
   return true;
 }
 
-GetSizeStatsFunction::GetSizeStatsFunction() {
+FileBrowserPrivateGetSizeStatsFunction::
+    FileBrowserPrivateGetSizeStatsFunction() {
 }
 
-GetSizeStatsFunction::~GetSizeStatsFunction() {
+FileBrowserPrivateGetSizeStatsFunction::
+    ~FileBrowserPrivateGetSizeStatsFunction() {
 }
 
-bool GetSizeStatsFunction::RunImpl() {
+bool FileBrowserPrivateGetSizeStatsFunction::RunImpl() {
   if (args_->GetSize() != 1) {
     return false;
   }
@@ -417,7 +435,7 @@
   if (!args_->GetString(0, &mount_url))
     return false;
 
-  base::FilePath file_path = util::GetLocalPathFromURL(
+  base::FilePath file_path = file_manager::util::GetLocalPathFromURL(
       render_view_host(), profile(), GURL(mount_url));
   if (file_path.empty())
     return false;
@@ -437,7 +455,8 @@
         integration_service->file_system();
 
     file_system->GetAvailableSpace(
-        base::Bind(&GetSizeStatsFunction::GetDriveAvailableSpaceCallback,
+        base::Bind(&FileBrowserPrivateGetSizeStatsFunction::
+                       GetDriveAvailableSpaceCallback,
                    this));
 
   } else {
@@ -449,7 +468,8 @@
                    file_path.value(),
                    total_size,
                    remaining_size),
-        base::Bind(&GetSizeStatsFunction::GetSizeStatsCallback,
+        base::Bind(&FileBrowserPrivateGetSizeStatsFunction::
+                       GetSizeStatsCallback,
                    this,
                    base::Owned(total_size),
                    base::Owned(remaining_size)));
@@ -457,7 +477,7 @@
   return true;
 }
 
-void GetSizeStatsFunction::GetDriveAvailableSpaceCallback(
+void FileBrowserPrivateGetSizeStatsFunction::GetDriveAvailableSpaceCallback(
     drive::FileError error,
     int64 bytes_total,
     int64 bytes_used) {
@@ -472,7 +492,7 @@
   }
 }
 
-void GetSizeStatsFunction::GetSizeStatsCallback(
+void FileBrowserPrivateGetSizeStatsFunction::GetSizeStatsCallback(
     const uint64* total_size,
     const uint64* remaining_size) {
   base::DictionaryValue* sizes = new base::DictionaryValue();
@@ -484,13 +504,15 @@
   SendResponse(true);
 }
 
-GetVolumeMetadataFunction::GetVolumeMetadataFunction() {
+FileBrowserPrivateGetVolumeMetadataFunction::
+    FileBrowserPrivateGetVolumeMetadataFunction() {
 }
 
-GetVolumeMetadataFunction::~GetVolumeMetadataFunction() {
+FileBrowserPrivateGetVolumeMetadataFunction::
+    ~FileBrowserPrivateGetVolumeMetadataFunction() {
 }
 
-bool GetVolumeMetadataFunction::RunImpl() {
+bool FileBrowserPrivateGetVolumeMetadataFunction::RunImpl() {
   if (args_->GetSize() != 1) {
     error_ = "Invalid argument count";
     return false;
@@ -502,7 +524,7 @@
     return false;
   }
 
-  base::FilePath file_path = util::GetLocalPathFromURL(
+  base::FilePath file_path = file_manager::util::GetLocalPathFromURL(
       render_view_host(), profile(), GURL(volume_mount_url));
   if (file_path.empty()) {
     error_ = "Invalid mount path.";
@@ -511,24 +533,31 @@
 
   results_.reset();
 
-  const DiskMountManager::Disk* volume = GetVolumeAsDisk(file_path.value());
-  if (volume) {
-    DictionaryValue* volume_info =
-        CreateValueFromDisk(profile_, extension_->id(), volume);
-    SetResult(volume_info);
+  base::FilePath home_path;
+  // TODO(hidehiko): Return the volume info for Drive File System.
+  if (PathService::Get(base::DIR_HOME, &home_path) &&
+      file_path == home_path.AppendASCII("Downloads")) {
+    // Return simple (fake) volume metadata for Downloads volume.
+    SetResult(CreateDownloadsVolumeMetadata());
+  } else {
+    const DiskMountManager::Disk* volume = GetVolumeAsDisk(file_path.value());
+    if (volume)
+      SetResult(CreateValueFromDisk(profile_, extension_->id(), volume));
   }
 
   SendResponse(true);
   return true;
 }
 
-ValidatePathNameLengthFunction::ValidatePathNameLengthFunction() {
+FileBrowserPrivateValidatePathNameLengthFunction::
+    FileBrowserPrivateValidatePathNameLengthFunction() {
 }
 
-ValidatePathNameLengthFunction::~ValidatePathNameLengthFunction() {
+FileBrowserPrivateValidatePathNameLengthFunction::
+    ~FileBrowserPrivateValidatePathNameLengthFunction() {
 }
 
-bool ValidatePathNameLengthFunction::RunImpl() {
+bool FileBrowserPrivateValidatePathNameLengthFunction::RunImpl() {
   std::string parent_url;
   if (!args_->GetString(0, &parent_url))
     return false;
@@ -538,7 +567,7 @@
     return false;
 
   scoped_refptr<fileapi::FileSystemContext> file_system_context =
-      util::GetFileSystemContextForRenderViewHost(
+      file_manager::util::GetFileSystemContextForRenderViewHost(
           profile(), render_view_host());
 
   fileapi::FileSystemURL filesystem_url(
@@ -558,25 +587,28 @@
       FROM_HERE,
       base::Bind(&GetFileNameMaxLengthOnBlockingPool,
                  filesystem_url.path().AsUTF8Unsafe()),
-      base::Bind(&ValidatePathNameLengthFunction::OnFilePathLimitRetrieved,
+      base::Bind(&FileBrowserPrivateValidatePathNameLengthFunction::
+                     OnFilePathLimitRetrieved,
                  this, name.size()));
   return true;
 }
 
-void ValidatePathNameLengthFunction::OnFilePathLimitRetrieved(
+void FileBrowserPrivateValidatePathNameLengthFunction::OnFilePathLimitRetrieved(
     size_t current_length,
     size_t max_length) {
   SetResult(new base::FundamentalValue(current_length <= max_length));
   SendResponse(true);
 }
 
-FormatDeviceFunction::FormatDeviceFunction() {
+FileBrowserPrivateFormatDeviceFunction::
+    FileBrowserPrivateFormatDeviceFunction() {
 }
 
-FormatDeviceFunction::~FormatDeviceFunction() {
+FileBrowserPrivateFormatDeviceFunction::
+    ~FileBrowserPrivateFormatDeviceFunction() {
 }
 
-bool FormatDeviceFunction::RunImpl() {
+bool FileBrowserPrivateFormatDeviceFunction::RunImpl() {
   if (args_->GetSize() != 1) {
     return false;
   }
@@ -587,7 +619,7 @@
     return false;
   }
 
-  base::FilePath file_path = util::GetLocalPathFromURL(
+  base::FilePath file_path = file_manager::util::GetLocalPathFromURL(
       render_view_host(), profile(), GURL(volume_file_url));
   if (file_path.empty())
     return false;
@@ -597,4 +629,4 @@
   return true;
 }
 
-}  // namespace file_manager
+}  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h
index d4e4f13..9b76aec 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h
@@ -23,18 +23,19 @@
 class FileSystemContext;
 }
 
-namespace file_manager {
+namespace extensions {
 
 // Implements the chrome.fileBrowserPrivate.requestFileSystem method.
-class RequestFileSystemFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateRequestFileSystemFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.requestFileSystem",
                              FILEBROWSERPRIVATE_REQUESTFILESYSTEM)
 
-  RequestFileSystemFunction();
+  FileBrowserPrivateRequestFileSystemFunction();
 
  protected:
-  virtual ~RequestFileSystemFunction();
+  virtual ~FileBrowserPrivateRequestFileSystemFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
@@ -63,9 +64,10 @@
       scoped_refptr<const extensions::Extension> extension);
 };
 
-// Base class for AddFileWatchFunction and RemoveFileWatchFunction. Although
-// it's called "FileWatch", the class and its sub classes are used only for
-// watching changes in directories.
+// Base class for FileBrowserPrivateAddFileWatchFunction and
+// FileBrowserPrivateRemoveFileWatchFunction. Although it's called "FileWatch",
+// the class and its sub classes are used only for watching changes in
+// directories.
 class FileWatchFunctionBase : public LoggedAsyncExtensionFunction {
  public:
   FileWatchFunctionBase();
@@ -88,15 +90,15 @@
 
 // Implements the chrome.fileBrowserPrivate.addFileWatch method.
 // Starts watching changes in directories.
-class AddFileWatchFunction : public FileWatchFunctionBase {
+class FileBrowserPrivateAddFileWatchFunction : public FileWatchFunctionBase {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.addFileWatch",
                              FILEBROWSERPRIVATE_ADDFILEWATCH)
 
-  AddFileWatchFunction();
+  FileBrowserPrivateAddFileWatchFunction();
 
  protected:
-  virtual ~AddFileWatchFunction();
+  virtual ~FileBrowserPrivateAddFileWatchFunction();
 
   // FileWatchFunctionBase override.
   virtual void PerformFileWatchOperation(
@@ -108,15 +110,15 @@
 
 // Implements the chrome.fileBrowserPrivate.removeFileWatch method.
 // Stops watching changes in directories.
-class RemoveFileWatchFunction : public FileWatchFunctionBase {
+class FileBrowserPrivateRemoveFileWatchFunction : public FileWatchFunctionBase {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.removeFileWatch",
                              FILEBROWSERPRIVATE_REMOVEFILEWATCH)
 
-  RemoveFileWatchFunction();
+  FileBrowserPrivateRemoveFileWatchFunction();
 
  protected:
-  virtual ~RemoveFileWatchFunction();
+  virtual ~FileBrowserPrivateRemoveFileWatchFunction();
 
   // FileWatchFunctionBase override.
   virtual void PerformFileWatchOperation(
@@ -127,30 +129,32 @@
 
 // Implements the chrome.fileBrowserPrivate.setLastModified method.
 // Sets last modified date in seconds of local file
-class SetLastModifiedFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateSetLastModifiedFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.setLastModified",
                              FILEBROWSERPRIVATE_SETLASTMODIFIED)
 
-  SetLastModifiedFunction();
+  FileBrowserPrivateSetLastModifiedFunction();
 
  protected:
-  virtual ~SetLastModifiedFunction();
+  virtual ~FileBrowserPrivateSetLastModifiedFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
 };
 
 // Implements the chrome.fileBrowserPrivate.getSizeStats method.
-class GetSizeStatsFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateGetSizeStatsFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getSizeStats",
                              FILEBROWSERPRIVATE_GETSIZESTATS)
 
-  GetSizeStatsFunction();
+  FileBrowserPrivateGetSizeStatsFunction();
 
  protected:
-  virtual ~GetSizeStatsFunction();
+  virtual ~FileBrowserPrivateGetSizeStatsFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
@@ -166,30 +170,32 @@
 
 // Implements the chrome.fileBrowserPrivate.getVolumeMetadata method.
 // Retrieves devices meta-data. Expects volume's device path as an argument.
-class GetVolumeMetadataFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateGetVolumeMetadataFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getVolumeMetadata",
                              FILEBROWSERPRIVATE_GETVOLUMEMETADATA)
 
-  GetVolumeMetadataFunction();
+  FileBrowserPrivateGetVolumeMetadataFunction();
 
  protected:
-  virtual ~GetVolumeMetadataFunction();
+  virtual ~FileBrowserPrivateGetVolumeMetadataFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
 };
 
 // Implements the chrome.fileBrowserPrivate.validatePathNameLength method.
-class ValidatePathNameLengthFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateValidatePathNameLengthFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.validatePathNameLength",
                              FILEBROWSERPRIVATE_VALIDATEPATHNAMELENGTH)
 
-  ValidatePathNameLengthFunction();
+  FileBrowserPrivateValidatePathNameLengthFunction();
 
  protected:
-  virtual ~ValidatePathNameLengthFunction();
+  virtual ~FileBrowserPrivateValidatePathNameLengthFunction();
 
   void OnFilePathLimitRetrieved(size_t current_length, size_t max_length);
 
@@ -199,20 +205,21 @@
 
 // Implements the chrome.fileBrowserPrivate.formatDevice method.
 // Formats Device given its mount path.
-class FormatDeviceFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateFormatDeviceFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.formatDevice",
                              FILEBROWSERPRIVATE_FORMATDEVICE)
 
-  FormatDeviceFunction();
+  FileBrowserPrivateFormatDeviceFunction();
 
  protected:
-  virtual ~FormatDeviceFunction();
+  virtual ~FileBrowserPrivateFormatDeviceFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
 };
 
-}  // namespace file_manager
+}  // namespace extensions
 
 #endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_FILE_SYSTEM_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
index e102016..41ef131 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
@@ -18,26 +18,28 @@
 #include "content/public/common/page_zoom.h"
 #include "url/gurl.h"
 
-namespace file_manager {
+namespace extensions {
 
-LogoutUserFunction::LogoutUserFunction() {
+FileBrowserPrivateLogoutUserFunction::FileBrowserPrivateLogoutUserFunction() {
 }
 
-LogoutUserFunction::~LogoutUserFunction() {
+FileBrowserPrivateLogoutUserFunction::~FileBrowserPrivateLogoutUserFunction() {
 }
 
-bool LogoutUserFunction::RunImpl() {
+bool FileBrowserPrivateLogoutUserFunction::RunImpl() {
   chrome::AttemptUserExit();
   return true;
 }
 
-GetPreferencesFunction::GetPreferencesFunction() {
+FileBrowserPrivateGetPreferencesFunction::
+    FileBrowserPrivateGetPreferencesFunction() {
 }
 
-GetPreferencesFunction::~GetPreferencesFunction() {
+FileBrowserPrivateGetPreferencesFunction::
+    ~FileBrowserPrivateGetPreferencesFunction() {
 }
 
-bool GetPreferencesFunction::RunImpl() {
+bool FileBrowserPrivateGetPreferencesFunction::RunImpl() {
   scoped_ptr<DictionaryValue> value(new DictionaryValue());
 
   const PrefService* service = profile_->GetPrefs();
@@ -69,13 +71,15 @@
   return true;
 }
 
-SetPreferencesFunction::SetPreferencesFunction() {
+FileBrowserPrivateSetPreferencesFunction::
+    FileBrowserPrivateSetPreferencesFunction() {
 }
 
-SetPreferencesFunction::~SetPreferencesFunction() {
+FileBrowserPrivateSetPreferencesFunction::
+    ~FileBrowserPrivateSetPreferencesFunction() {
 }
 
-bool SetPreferencesFunction::RunImpl() {
+bool FileBrowserPrivateSetPreferencesFunction::RunImpl() {
   base::DictionaryValue* value = NULL;
 
   if (!args_->GetDictionary(0, &value) || !value)
@@ -95,13 +99,15 @@
   return true;
 }
 
-ZipSelectionFunction::ZipSelectionFunction() {
+FileBrowserPrivateZipSelectionFunction::
+    FileBrowserPrivateZipSelectionFunction() {
 }
 
-ZipSelectionFunction::~ZipSelectionFunction() {
+FileBrowserPrivateZipSelectionFunction::
+    ~FileBrowserPrivateZipSelectionFunction() {
 }
 
-bool ZipSelectionFunction::RunImpl() {
+bool FileBrowserPrivateZipSelectionFunction::RunImpl() {
   if (args_->GetSize() < 3) {
     return false;
   }
@@ -111,7 +117,7 @@
   if (!args_->GetString(0, &dir_url) || dir_url.empty())
     return false;
 
-  base::FilePath src_dir = util::GetLocalPathFromURL(
+  base::FilePath src_dir = file_manager::util::GetLocalPathFromURL(
       render_view_host(), profile(), GURL(dir_url));
   if (src_dir.empty())
     return false;
@@ -126,7 +132,7 @@
   for (size_t i = 0; i < selection_urls->GetSize(); ++i) {
     std::string file_url;
     selection_urls->GetString(i, &file_url);
-    base::FilePath path = util::GetLocalPathFromURL(
+    base::FilePath path = file_manager::util::GetLocalPathFromURL(
         render_view_host(), profile(), GURL(file_url));
     if (path.empty())
       return false;
@@ -155,10 +161,10 @@
     src_relative_paths.push_back(relative_path);
   }
 
-  zip_file_creator_ = new ZipFileCreator(this,
-                                         src_dir,
-                                         src_relative_paths,
-                                         dest_file);
+  zip_file_creator_ = new file_manager::ZipFileCreator(this,
+                                                       src_dir,
+                                                       src_relative_paths,
+                                                       dest_file);
 
   // Keep the refcount until the zipping is complete on utility process.
   AddRef();
@@ -167,19 +173,19 @@
   return true;
 }
 
-void ZipSelectionFunction::OnZipDone(bool success) {
+void FileBrowserPrivateZipSelectionFunction::OnZipDone(bool success) {
   SetResult(new base::FundamentalValue(success));
   SendResponse(true);
   Release();
 }
 
-ZoomFunction::ZoomFunction() {
+FileBrowserPrivateZoomFunction::FileBrowserPrivateZoomFunction() {
 }
 
-ZoomFunction::~ZoomFunction() {
+FileBrowserPrivateZoomFunction::~FileBrowserPrivateZoomFunction() {
 }
 
-bool ZoomFunction::RunImpl() {
+bool FileBrowserPrivateZoomFunction::RunImpl() {
   content::RenderViewHost* const view_host = render_view_host();
   std::string operation;
   args_->GetString(0, &operation);
@@ -198,4 +204,4 @@
   return true;
 }
 
-}  // namespace file_manager
+}  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h
index 922864d..942fa34 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h
@@ -11,18 +11,18 @@
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_base.h"
 #include "chrome/browser/chromeos/extensions/file_manager/zip_file_creator.h"
 
-namespace file_manager {
+namespace extensions {
 
 // Implements the chrome.fileBrowserPrivate.logoutUser method.
-class LogoutUserFunction : public SyncExtensionFunction {
+class FileBrowserPrivateLogoutUserFunction : public SyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.logoutUser",
                              FILEBROWSERPRIVATE_LOGOUTUSER)
 
-  LogoutUserFunction();
+  FileBrowserPrivateLogoutUserFunction();
 
  protected:
-  virtual ~LogoutUserFunction();
+  virtual ~FileBrowserPrivateLogoutUserFunction();
 
   // SyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
@@ -30,46 +30,47 @@
 
 // Implements the chrome.fileBrowserPrivate.getPreferences method.
 // Gets settings for Files.app.
-class GetPreferencesFunction : public SyncExtensionFunction {
+class FileBrowserPrivateGetPreferencesFunction : public SyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getPreferences",
                              FILEBROWSERPRIVATE_GETPREFERENCES)
 
-  GetPreferencesFunction();
+  FileBrowserPrivateGetPreferencesFunction();
 
  protected:
-  virtual ~GetPreferencesFunction();
+  virtual ~FileBrowserPrivateGetPreferencesFunction();
 
   virtual bool RunImpl() OVERRIDE;
 };
 
 // Implements the chrome.fileBrowserPrivate.setPreferences method.
 // Sets settings for Files.app.
-class SetPreferencesFunction : public SyncExtensionFunction {
+class FileBrowserPrivateSetPreferencesFunction : public SyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.setPreferences",
                              FILEBROWSERPRIVATE_SETPREFERENCES)
 
-  SetPreferencesFunction();
+  FileBrowserPrivateSetPreferencesFunction();
 
  protected:
-  virtual ~SetPreferencesFunction();
+  virtual ~FileBrowserPrivateSetPreferencesFunction();
 
   virtual bool RunImpl() OVERRIDE;
 };
 
 // Implements the chrome.fileBrowserPrivate.zipSelection method.
 // Creates a zip file for the selected files.
-class ZipSelectionFunction : public LoggedAsyncExtensionFunction,
-                             public ZipFileCreator::Observer {
+class FileBrowserPrivateZipSelectionFunction
+    : public LoggedAsyncExtensionFunction,
+      public file_manager::ZipFileCreator::Observer {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.zipSelection",
                              FILEBROWSERPRIVATE_ZIPSELECTION)
 
-  ZipSelectionFunction();
+  FileBrowserPrivateZipSelectionFunction();
 
  protected:
-  virtual ~ZipSelectionFunction();
+  virtual ~FileBrowserPrivateZipSelectionFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
@@ -78,25 +79,25 @@
   virtual void OnZipDone(bool success) OVERRIDE;
 
  private:
-  scoped_refptr<ZipFileCreator> zip_file_creator_;
+  scoped_refptr<file_manager::ZipFileCreator> zip_file_creator_;
 };
 
 // Implements the chrome.fileBrowserPrivate.zoom method.
 // Changes the zoom level of the file manager by internally calling
 // RenderViewHost::Zoom(). TODO(hirono): Remove this function once the zoom
 // level change is supported for all apps. crbug.com/227175.
-class ZoomFunction : public SyncExtensionFunction {
+class FileBrowserPrivateZoomFunction : public SyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.zoom",
                              FILEBROWSERPRIVATE_ZOOM);
 
-  ZoomFunction();
+  FileBrowserPrivateZoomFunction();
 
  protected:
-  virtual ~ZoomFunction();
+  virtual ~FileBrowserPrivateZoomFunction();
   virtual bool RunImpl() OVERRIDE;
 };
 
-}  // namespace file_manager
+}  // namespace extensions
 
 #endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_MISC_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_mount.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_mount.cc
index 52c47fe..5a715b2 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_mount.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_mount.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/chromeos/drive/logging.h"
 #include "chrome/browser/chromeos/extensions/file_manager/event_router.h"
 #include "chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.h"
-#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
 #include "chrome/browser/chromeos/extensions/file_manager/fileapi_util.h"
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
 #include "chrome/browser/profiles/profile.h"
@@ -23,7 +22,7 @@
 using chromeos::disks::DiskMountManager;
 using content::BrowserThread;
 
-namespace file_manager {
+namespace extensions {
 namespace {
 
 // Creates a dictionary describing the mount point of |mount_point_info|.
@@ -42,7 +41,7 @@
   base::FilePath relative_mount_path;
   // Convert mount point path to relative path with the external file system
   // exposed within File API.
-  if (util::ConvertAbsoluteFilePathToRelativeFileSystemPath(
+  if (file_manager::util::ConvertAbsoluteFilePathToRelativeFileSystemPath(
           profile,
           extension_id,
           base::FilePath(mount_point_info.mount_path),
@@ -57,15 +56,25 @@
   return mount_info;
 }
 
+base::DictionaryValue* CreateDownloadsMountPointInfo() {
+  base::DictionaryValue* result = new base::DictionaryValue;
+  result->SetString("mountPath", "Downloads");
+  result->SetString(
+      "mountCondition",
+      DiskMountManager::MountConditionToString(
+          chromeos::disks::MOUNT_CONDITION_NONE));
+  return result;
+}
+
 }  // namespace
 
-AddMountFunction::AddMountFunction() {
+FileBrowserPrivateAddMountFunction::FileBrowserPrivateAddMountFunction() {
 }
 
-AddMountFunction::~AddMountFunction() {
+FileBrowserPrivateAddMountFunction::~FileBrowserPrivateAddMountFunction() {
 }
 
-bool AddMountFunction::RunImpl() {
+bool FileBrowserPrivateAddMountFunction::RunImpl() {
   // The third argument is simply ignored.
   if (args_->GetSize() != 2 && args_->GetSize() != 3) {
     error_ = "Invalid argument count";
@@ -104,7 +113,7 @@
     case chromeos::MOUNT_TYPE_GOOGLE_DRIVE: {
       // Dispatch fake 'mounted' event because JS code depends on it.
       // TODO(hashimoto): Remove this redanduncy.
-      FileBrowserPrivateAPI::Get(profile_)->event_router()->
+      file_manager::FileBrowserPrivateAPI::Get(profile_)->event_router()->
           OnFileSystemMounted();
 
       // Pass back the drive mount point path as source path.
@@ -115,7 +124,7 @@
       break;
     }
     default: {
-      const base::FilePath path = util::GetLocalPathFromURL(
+      const base::FilePath path = file_manager::util::GetLocalPathFromURL(
           render_view_host(), profile(), GURL(file_url));
 
       if (path.empty()) {
@@ -137,7 +146,7 @@
         }
         file_system->MarkCacheFileAsMounted(
             drive::util::ExtractDrivePath(path),
-            base::Bind(&AddMountFunction::OnMountedStateSet,
+            base::Bind(&FileBrowserPrivateAddMountFunction::OnMountedStateSet,
                        this, mount_type_str, display_name));
       } else {
         OnMountedStateSet(mount_type_str, display_name,
@@ -150,7 +159,7 @@
   return true;
 }
 
-void AddMountFunction::OnMountedStateSet(
+void FileBrowserPrivateAddMountFunction::OnMountedStateSet(
     const std::string& mount_type,
     const base::FilePath::StringType& file_name,
     drive::FileError error,
@@ -172,13 +181,14 @@
       file_name, DiskMountManager::MountTypeFromString(mount_type));
 }
 
-RemoveMountFunction::RemoveMountFunction() {
+FileBrowserPrivateRemoveMountFunction::FileBrowserPrivateRemoveMountFunction() {
 }
 
-RemoveMountFunction::~RemoveMountFunction() {
+FileBrowserPrivateRemoveMountFunction::
+    ~FileBrowserPrivateRemoveMountFunction() {
 }
 
-bool RemoveMountFunction::RunImpl() {
+bool FileBrowserPrivateRemoveMountFunction::RunImpl() {
   if (args_->GetSize() != 1) {
     return false;
   }
@@ -197,16 +207,17 @@
 
   std::vector<GURL> file_paths;
   file_paths.push_back(GURL(mount_path));
-  util::GetSelectedFileInfo(
+  file_manager::util::GetSelectedFileInfo(
       render_view_host(),
       profile(),
       file_paths,
-      util::NEED_LOCAL_PATH_FOR_OPENING,
-      base::Bind(&RemoveMountFunction::GetSelectedFileInfoResponse, this));
+      file_manager::util::NEED_LOCAL_PATH_FOR_OPENING,
+      base::Bind(&FileBrowserPrivateRemoveMountFunction::
+                     GetSelectedFileInfoResponse, this));
   return true;
 }
 
-void RemoveMountFunction::GetSelectedFileInfoResponse(
+void FileBrowserPrivateRemoveMountFunction::GetSelectedFileInfoResponse(
     const std::vector<ui::SelectedFileInfo>& files) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
@@ -225,13 +236,15 @@
   SendResponse(true);
 }
 
-GetMountPointsFunction::GetMountPointsFunction() {
+FileBrowserPrivateGetMountPointsFunction::
+    FileBrowserPrivateGetMountPointsFunction() {
 }
 
-GetMountPointsFunction::~GetMountPointsFunction() {
+FileBrowserPrivateGetMountPointsFunction::
+    ~FileBrowserPrivateGetMountPointsFunction() {
 }
 
-bool GetMountPointsFunction::RunImpl() {
+bool FileBrowserPrivateGetMountPointsFunction::RunImpl() {
   if (args_->GetSize())
     return false;
 
@@ -243,7 +256,14 @@
       disk_mount_manager->mount_points();
 
   std::string log_string = "[";
-  const char *separator = "";
+
+  // TODO(hidehiko): Returns Drive if available.
+
+  // Always return "Downloads".
+  log_string += "Downloads";
+  mounts->Append(CreateDownloadsMountPointInfo());
+
+  const char *kSeparator = ", ";
   for (DiskMountManager::MountPointMap::const_iterator it =
            mount_points.begin();
        it != mount_points.end();
@@ -251,8 +271,7 @@
     mounts->Append(CreateValueFromMountPoint(profile_,
                                              it->second,
                                              extension_->id()));
-    log_string += separator + it->first;
-    separator = ", ";
+    log_string += kSeparator + it->first;
   }
 
   log_string += "]";
@@ -268,4 +287,4 @@
   return true;
 }
 
-}  // namespace file_manager
+}  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_mount.h b/chrome/browser/chromeos/extensions/file_manager/private_api_mount.h
index a3913f7..f12f68d 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_mount.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_mount.h
@@ -18,19 +18,19 @@
 struct SelectedFileInfo;
 }
 
-namespace file_manager {
+namespace extensions {
 
 // Implements chrome.fileBrowserPrivate.addMount method.
 // Mounts a device or a file.
-class AddMountFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateAddMountFunction : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.addMount",
                              FILEBROWSERPRIVATE_ADDMOUNT)
 
-  AddMountFunction();
+  FileBrowserPrivateAddMountFunction();
 
  protected:
-  virtual ~AddMountFunction();
+  virtual ~FileBrowserPrivateAddMountFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
@@ -45,15 +45,16 @@
 
 // Implements chrome.fileBrowserPrivate.removeMount method.
 // Unmounts selected device. Expects mount point path as an argument.
-class RemoveMountFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateRemoveMountFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.removeMount",
                              FILEBROWSERPRIVATE_REMOVEMOUNT)
 
-  RemoveMountFunction();
+  FileBrowserPrivateRemoveMountFunction();
 
  protected:
-  virtual ~RemoveMountFunction();
+  virtual ~FileBrowserPrivateRemoveMountFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
@@ -65,20 +66,21 @@
 };
 
 // Implements chrome.fileBrowserPrivate.getMountPoints method.
-class GetMountPointsFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateGetMountPointsFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getMountPoints",
                              FILEBROWSERPRIVATE_GETMOUNTPOINTS)
 
-  GetMountPointsFunction();
+  FileBrowserPrivateGetMountPointsFunction();
 
  protected:
-  virtual ~GetMountPointsFunction();
+  virtual ~FileBrowserPrivateGetMountPointsFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
 };
 
-}  // namespace file_manager
+}  // namespace extensions
 
 #endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_MOUNT_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
index fe21f77..7fbc6a4 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
@@ -4,22 +4,22 @@
 
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_strings.h"
 
-#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
+#include "chrome/browser/chromeos/extensions/file_manager/open_with_browser.h"
 #include "chrome/browser/chromeos/system/statistics_provider.h"
 #include "grit/app_locale_settings.h"
 #include "grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/webui/web_ui_util.h"
 
-namespace file_manager {
+namespace extensions {
 
-GetStringsFunction::GetStringsFunction() {
+FileBrowserPrivateGetStringsFunction::FileBrowserPrivateGetStringsFunction() {
 }
 
-GetStringsFunction::~GetStringsFunction() {
+FileBrowserPrivateGetStringsFunction::~FileBrowserPrivateGetStringsFunction() {
 }
 
-bool GetStringsFunction::RunImpl() {
+bool FileBrowserPrivateGetStringsFunction::RunImpl() {
   DictionaryValue* dict = new DictionaryValue();
   SetResult(dict);
 
@@ -193,6 +193,12 @@
   SET_STRING("ACTION_CHOICE_LOADING_SD",
              IDS_FILE_BROWSER_ACTION_CHOICE_LOADING_SD);
 
+  SET_STRING("SUGGEST_DIALOG_TITLE", IDS_FILE_BROWSER_SUGGEST_DIALOG_TITLE);
+  SET_STRING("SUGGEST_DIALOG_LINK_TO_WEBSTORE",
+             IDS_FILE_BROWSER_SUGGEST_DIALOG_LINK_TO_WEBSTORE);
+  SET_STRING("SUGGEST_DIALOG_INSTALLATION_FAILED",
+             IDS_FILE_BROWSER_SUGGEST_DIALOG_INSTALLATION_FAILED);
+
   SET_STRING("PHOTO_IMPORT_TITLE", IDS_FILE_BROWSER_PHOTO_IMPORT_TITLE);
   SET_STRING("PHOTO_IMPORT_IMPORT_BUTTON",
              IDS_FILE_BROWSER_PHOTO_IMPORT_IMPORT_BUTTON);
@@ -503,10 +509,10 @@
 #undef SET_STRING
 
   dict->SetBoolean("PDF_VIEW_ENABLED",
-                   util::ShouldBeOpenedWithPlugin(profile(),
+                   file_manager::util::ShouldBeOpenedWithPlugin(profile(),
                                                   FILE_PATH_LITERAL(".pdf")));
   dict->SetBoolean("SWF_VIEW_ENABLED",
-                   util::ShouldBeOpenedWithPlugin(profile(),
+                   file_manager::util::ShouldBeOpenedWithPlugin(profile(),
                                                   FILE_PATH_LITERAL(".swf")));
 
   webui::SetFontAndTextDirection(dict);
@@ -522,4 +528,4 @@
   return true;
 }
 
-}  // namespace file_manager
+}  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.h b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.h
index abdb172..579632f 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.h
@@ -9,24 +9,24 @@
 
 #include "chrome/browser/extensions/extension_function.h"
 
-namespace file_manager {
+namespace extensions {
 
 // Implements the chrome.fileBrowserPrivate.getStrings method.
 // Used to get strings for the file manager from JavaScript.
-class GetStringsFunction : public SyncExtensionFunction {
+class FileBrowserPrivateGetStringsFunction : public SyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getStrings",
                              FILEBROWSERPRIVATE_GETSTRINGS)
 
-  GetStringsFunction();
+  FileBrowserPrivateGetStringsFunction();
 
  protected:
-  virtual ~GetStringsFunction();
+  virtual ~FileBrowserPrivateGetStringsFunction();
 
   // SyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
 };
 
-}  // namespace file_manager
+}  // namespace extensions
 
 #endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_STRINGS_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.cc
index 2456b40..17b4ca8 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.cc
@@ -4,86 +4,26 @@
 
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_tasks.h"
 
-#include "chrome/browser/chromeos/drive/drive_app_registry.h"
-#include "chrome/browser/chromeos/drive/drive_integration_service.h"
-#include "chrome/browser/chromeos/extensions/file_manager/file_browser_handlers.h"
-#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
 #include "chrome/browser/chromeos/extensions/file_manager/file_tasks.h"
+#include "chrome/browser/chromeos/extensions/file_manager/fileapi_util.h"
+#include "chrome/browser/chromeos/extensions/file_manager/mime_util.h"
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
 #include "chrome/browser/chromeos/fileapi/file_system_backend.h"
-#include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_system.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
-#include "chrome/common/extensions/api/file_browser_handlers/file_browser_handler.h"
-#include "content/public/browser/browser_context.h"
 #include "content/public/browser/render_view_host.h"
-#include "content/public/browser/storage_partition.h"
 #include "webkit/browser/fileapi/file_system_context.h"
 #include "webkit/browser/fileapi/file_system_url.h"
 
-using content::BrowserContext;
-using extensions::app_file_handler_util::FindFileHandlersForFiles;
 using extensions::app_file_handler_util::PathAndMimeTypeSet;
 using extensions::Extension;
 using fileapi::FileSystemURL;
 
-namespace file_manager {
+namespace extensions {
 namespace {
 
 // Error messages.
 const char kInvalidFileUrl[] = "Invalid file URL";
 
-// Default icon path for drive docs.
-const char kDefaultIcon[] = "images/filetype_generic.png";
-
-// Logs the default task for debugging.
-void LogDefaultTask(const std::set<std::string>& mime_types,
-                    const std::set<std::string>& suffixes,
-                    const std::string& task_id) {
-  if (!mime_types.empty()) {
-    std::string mime_types_str;
-    for (std::set<std::string>::const_iterator iter = mime_types.begin();
-         iter != mime_types.end(); ++iter) {
-      if (iter == mime_types.begin()) {
-        mime_types_str = *iter;
-      } else {
-        mime_types_str += ", " + *iter;
-      }
-    }
-    VLOG(1) << "Associating task " << task_id
-            << " with the following MIME types: ";
-    VLOG(1) << "  " << mime_types_str;
-  }
-
-  if (!suffixes.empty()) {
-    std::string suffixes_str;
-    for (std::set<std::string>::const_iterator iter = suffixes.begin();
-         iter != suffixes.end(); ++iter) {
-      if (iter == suffixes.begin()) {
-        suffixes_str = *iter;
-      } else {
-        suffixes_str += ", " + *iter;
-      }
-    }
-    VLOG(1) << "Associating task " << task_id
-            << " with the following suffixes: ";
-    VLOG(1) << "  " << suffixes_str;
-  }
-}
-
-// Gets the mime types for the given file paths.
-void GetMimeTypesForFileURLs(const std::vector<base::FilePath>& file_paths,
-                             PathAndMimeTypeSet* files) {
-  for (std::vector<base::FilePath>::const_iterator iter = file_paths.begin();
-       iter != file_paths.end(); ++iter) {
-    files->insert(
-        std::make_pair(*iter, util::GetMimeTypeForPath(*iter)));
-  }
-}
-
 // Make a set of unique filename suffixes out of the list of file URLs.
 std::set<std::string> GetUniqueSuffixes(base::ListValue* file_url_list,
                                         fileapi::FileSystemContext* context) {
@@ -118,13 +58,14 @@
 
 }  // namespace
 
-ExecuteTaskFunction::ExecuteTaskFunction() {
+FileBrowserPrivateExecuteTaskFunction::FileBrowserPrivateExecuteTaskFunction() {
 }
 
-ExecuteTaskFunction::~ExecuteTaskFunction() {
+FileBrowserPrivateExecuteTaskFunction::
+    ~FileBrowserPrivateExecuteTaskFunction() {
 }
 
-bool ExecuteTaskFunction::RunImpl() {
+bool FileBrowserPrivateExecuteTaskFunction::RunImpl() {
   // First param is task id that was to the extension with getFileTasks call.
   std::string task_id;
   if (!args_->GetString(0, &task_id) || !task_id.size())
@@ -139,8 +80,8 @@
   if (!args_->GetList(1, &files_list))
     return false;
 
-  file_tasks::TaskDescriptor task;
-  if (!file_tasks::ParseTaskID(task_id, &task)) {
+  file_manager::file_tasks::TaskDescriptor task;
+  if (!file_manager::file_tasks::ParseTaskID(task_id, &task)) {
     LOG(WARNING) << "Invalid task " << task_id;
     return false;
   }
@@ -148,10 +89,9 @@
   if (!files_list->GetSize())
     return true;
 
-  content::SiteInstance* site_instance = render_view_host()->GetSiteInstance();
   scoped_refptr<fileapi::FileSystemContext> file_system_context =
-      BrowserContext::GetStoragePartition(profile(), site_instance)->
-      GetFileSystemContext();
+      file_manager::util::GetFileSystemContextForRenderViewHost(
+          profile(), render_view_host());
 
   std::vector<FileSystemURL> file_urls;
   for (size_t i = 0; i < files_list->GetSize(); i++) {
@@ -168,303 +108,31 @@
     file_urls.push_back(url);
   }
 
-  int32 tab_id = util::GetTabId(dispatcher());
-  return file_tasks::ExecuteFileTask(
+  int32 tab_id = file_manager::util::GetTabId(dispatcher());
+  return file_manager::file_tasks::ExecuteFileTask(
       profile(),
       source_url(),
       extension_->id(),
       tab_id,
       task,
       file_urls,
-      base::Bind(&ExecuteTaskFunction::OnTaskExecuted, this));
+      base::Bind(&FileBrowserPrivateExecuteTaskFunction::OnTaskExecuted, this));
 }
 
-void ExecuteTaskFunction::OnTaskExecuted(bool success) {
+void FileBrowserPrivateExecuteTaskFunction::OnTaskExecuted(bool success) {
   SetResult(new base::FundamentalValue(success));
   SendResponse(true);
 }
 
-struct GetFileTasksFunction::FileInfo {
-  GURL file_url;
-  base::FilePath file_path;
-  std::string mime_type;
-};
-
-struct GetFileTasksFunction::TaskInfo {
-  TaskInfo(const string16& app_name, const GURL& icon_url)
-      : app_name(app_name), icon_url(icon_url) {
-  }
-
-  string16 app_name;
-  GURL icon_url;
-};
-
-GetFileTasksFunction::GetFileTasksFunction() {
+FileBrowserPrivateGetFileTasksFunction::
+    FileBrowserPrivateGetFileTasksFunction() {
 }
 
-GetFileTasksFunction::~GetFileTasksFunction() {
+FileBrowserPrivateGetFileTasksFunction::
+    ~FileBrowserPrivateGetFileTasksFunction() {
 }
 
-// static
-void GetFileTasksFunction::GetAvailableDriveTasks(
-    drive::DriveAppRegistry* registry,
-    const FileInfoList& file_info_list,
-    TaskInfoMap* task_info_map) {
-  DCHECK(registry);
-  DCHECK(task_info_map);
-  DCHECK(task_info_map->empty());
-
-  bool is_first = true;
-  for (size_t i = 0; i < file_info_list.size(); ++i) {
-    const FileInfo& file_info = file_info_list[i];
-    if (file_info.file_path.empty())
-      continue;
-
-    ScopedVector<drive::DriveAppInfo> app_info_list;
-    registry->GetAppsForFile(
-        file_info.file_path, file_info.mime_type, &app_info_list);
-
-    if (is_first) {
-      // For the first file, we store all the info.
-      for (size_t j = 0; j < app_info_list.size(); ++j) {
-        const drive::DriveAppInfo& app_info = *app_info_list[j];
-        GURL icon_url = util::FindPreferredIcon(app_info.app_icons,
-                                                util::kPreferredIconSize);
-        task_info_map->insert(std::pair<std::string, TaskInfo>(
-            file_tasks::MakeDriveAppTaskId(app_info.app_id),
-            TaskInfo(app_info.app_name, icon_url)));
-      }
-    } else {
-      // For remaining files, take the intersection with the current result,
-      // based on the task id.
-      std::set<std::string> task_id_set;
-      for (size_t j = 0; j < app_info_list.size(); ++j) {
-        task_id_set.insert(
-            file_tasks::MakeDriveAppTaskId(app_info_list[j]->app_id));
-      }
-      for (TaskInfoMap::iterator iter = task_info_map->begin();
-           iter != task_info_map->end(); ) {
-        if (task_id_set.find(iter->first) == task_id_set.end()) {
-          task_info_map->erase(iter++);
-        } else {
-          ++iter;
-        }
-      }
-    }
-
-    is_first = false;
-  }
-}
-
-void GetFileTasksFunction::FindDefaultDriveTasks(
-    const FileInfoList& file_info_list,
-    const TaskInfoMap& task_info_map,
-    std::set<std::string>* default_tasks) {
-  DCHECK(default_tasks);
-
-  for (size_t i = 0; i < file_info_list.size(); ++i) {
-    const FileInfo& file_info = file_info_list[i];
-    std::string task_id = file_tasks::GetDefaultTaskIdFromPrefs(
-        profile_, file_info.mime_type, file_info.file_path.Extension());
-    if (task_info_map.find(task_id) != task_info_map.end())
-      default_tasks->insert(task_id);
-  }
-}
-
-// static
-void GetFileTasksFunction::CreateDriveTasks(
-    const TaskInfoMap& task_info_map,
-    const std::set<std::string>& default_tasks,
-    ListValue* result_list,
-    bool* default_already_set) {
-  DCHECK(result_list);
-  DCHECK(default_already_set);
-
-  for (TaskInfoMap::const_iterator iter = task_info_map.begin();
-       iter != task_info_map.end(); ++iter) {
-    DictionaryValue* task = new DictionaryValue;
-    task->SetString("taskId", iter->first);
-    task->SetString("title", iter->second.app_name);
-
-    const GURL& icon_url = iter->second.icon_url;
-    if (!icon_url.is_empty())
-      task->SetString("iconUrl", icon_url.spec());
-
-    task->SetBoolean("driveApp", true);
-
-    // Once we set a default app, we don't want to set any more.
-    if (!(*default_already_set) &&
-        default_tasks.find(iter->first) != default_tasks.end()) {
-      task->SetBoolean("isDefault", true);
-      *default_already_set = true;
-    } else {
-      task->SetBoolean("isDefault", false);
-    }
-    result_list->Append(task);
-  }
-}
-
-void GetFileTasksFunction::FindDriveAppTasks(
-    const FileInfoList& file_info_list,
-    ListValue* result_list,
-    bool* default_already_set) {
-  DCHECK(result_list);
-  DCHECK(default_already_set);
-
-  if (file_info_list.empty())
-    return;
-
-  drive::DriveIntegrationService* integration_service =
-      drive::DriveIntegrationServiceFactory::GetForProfile(profile_);
-  // |integration_service| is NULL if Drive is disabled. We return true in this
-  // case because there might be other extension tasks, even if we don't have
-  // any to add.
-  if (!integration_service || !integration_service->drive_app_registry())
-    return;
-
-  drive::DriveAppRegistry* registry =
-      integration_service->drive_app_registry();
-  DCHECK(registry);
-
-  // Map of task_id to TaskInfo of available tasks.
-  TaskInfoMap task_info_map;
-  GetAvailableDriveTasks(registry, file_info_list, &task_info_map);
-  std::set<std::string> default_tasks;
-  FindDefaultDriveTasks(file_info_list, task_info_map, &default_tasks);
-  CreateDriveTasks(
-      task_info_map, default_tasks, result_list, default_already_set);
-}
-
-void GetFileTasksFunction::FindFileHandlerTasks(
-    const std::vector<base::FilePath>& file_paths,
-    ListValue* result_list,
-    bool* default_already_set) {
-  DCHECK(!file_paths.empty());
-  DCHECK(result_list);
-  DCHECK(default_already_set);
-
-  ExtensionService* service = profile_->GetExtensionService();
-  if (!service)
-    return;
-
-  PathAndMimeTypeSet files;
-  GetMimeTypesForFileURLs(file_paths, &files);
-  std::set<std::string> default_tasks;
-  for (PathAndMimeTypeSet::iterator it = files.begin(); it != files.end();
-       ++it) {
-    default_tasks.insert(file_tasks::GetDefaultTaskIdFromPrefs(
-        profile_, it->second, it->first.Extension()));
-  }
-
-  for (ExtensionSet::const_iterator iter = service->extensions()->begin();
-       iter != service->extensions()->end();
-       ++iter) {
-    const Extension* extension = iter->get();
-
-    // We don't support using hosted apps to open files.
-    if (!extension->is_platform_app())
-      continue;
-
-    if (profile_->IsOffTheRecord() &&
-        !service->IsIncognitoEnabled(extension->id()))
-      continue;
-
-    typedef std::vector<const extensions::FileHandlerInfo*> FileHandlerList;
-    FileHandlerList file_handlers = FindFileHandlersForFiles(*extension, files);
-    if (file_handlers.empty())
-      continue;
-
-    for (FileHandlerList::iterator i = file_handlers.begin();
-         i != file_handlers.end(); ++i) {
-      DictionaryValue* task = new DictionaryValue;
-      std::string task_id = file_tasks::MakeTaskID(
-          extension->id(), file_tasks::TASK_TYPE_FILE_HANDLER, (*i)->id);
-      task->SetString("taskId", task_id);
-      task->SetString("title", (*i)->title);
-      if (!(*default_already_set) && ContainsKey(default_tasks, task_id)) {
-        task->SetBoolean("isDefault", true);
-        *default_already_set = true;
-      } else {
-        task->SetBoolean("isDefault", false);
-      }
-
-      GURL best_icon = extensions::ExtensionIconSource::GetIconURL(
-          extension,
-          util::kPreferredIconSize,
-          ExtensionIconSet::MATCH_BIGGER,
-          false,  // grayscale
-          NULL);  // exists
-      if (!best_icon.is_empty())
-        task->SetString("iconUrl", best_icon.spec());
-      else
-        task->SetString("iconUrl", kDefaultIcon);
-
-      task->SetBoolean("driveApp", false);
-      result_list->Append(task);
-    }
-  }
-}
-
-void GetFileTasksFunction::FindFileBrowserHandlerTasks(
-    const std::vector<GURL>& file_urls,
-    const std::vector<base::FilePath>& file_paths,
-    ListValue* result_list,
-    bool* default_already_set) {
-  DCHECK(!file_paths.empty());
-  DCHECK(!file_urls.empty());
-  DCHECK(result_list);
-  DCHECK(default_already_set);
-
-  file_browser_handlers::FileBrowserHandlerList common_tasks =
-      file_browser_handlers::FindCommonFileBrowserHandlers(profile_, file_urls);
-  if (common_tasks.empty())
-    return;
-  file_browser_handlers::FileBrowserHandlerList default_tasks =
-      file_browser_handlers::FindDefaultFileBrowserHandlers(
-          profile_, file_paths, common_tasks);
-
-  ExtensionService* service =
-      extensions::ExtensionSystem::Get(profile_)->extension_service();
-  for (file_browser_handlers::FileBrowserHandlerList::const_iterator iter =
-           common_tasks.begin();
-       iter != common_tasks.end();
-       ++iter) {
-    const FileBrowserHandler* handler = *iter;
-    const std::string extension_id = handler->extension_id();
-    const Extension* extension = service->GetExtensionById(extension_id, false);
-    CHECK(extension);
-    DictionaryValue* task = new DictionaryValue;
-    task->SetString("taskId", file_tasks::MakeTaskID(
-        extension_id,
-        file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER,
-        handler->id()));
-    task->SetString("title", handler->title());
-    // TODO(zelidrag): Figure out how to expose icon URL that task defined in
-    // manifest instead of the default extension icon.
-    GURL icon = extensions::ExtensionIconSource::GetIconURL(
-        extension,
-        extension_misc::EXTENSION_ICON_BITTY,
-        ExtensionIconSet::MATCH_BIGGER,
-        false,  // grayscale
-        NULL);  // exists
-    task->SetString("iconUrl", icon.spec());
-    task->SetBoolean("driveApp", false);
-
-    // Only set the default if there isn't already a default set.
-    if (!*default_already_set &&
-        std::find(default_tasks.begin(), default_tasks.end(), *iter) !=
-        default_tasks.end()) {
-      task->SetBoolean("isDefault", true);
-      *default_already_set = true;
-    } else {
-      task->SetBoolean("isDefault", false);
-    }
-
-    result_list->Append(task);
-  }
-}
-
-bool GetFileTasksFunction::RunImpl() {
+bool FileBrowserPrivateGetFileTasksFunction::RunImpl() {
   // First argument is the list of files to get tasks for.
   ListValue* files_list = NULL;
   if (!args_->GetList(0, &files_list))
@@ -483,25 +151,23 @@
       mime_types_list->GetSize() != 0)
     return false;
 
-  content::SiteInstance* site_instance = render_view_host()->GetSiteInstance();
   scoped_refptr<fileapi::FileSystemContext> file_system_context =
-      BrowserContext::GetStoragePartition(profile(), site_instance)->
-      GetFileSystemContext();
+      file_manager::util::GetFileSystemContextForRenderViewHost(
+          profile(), render_view_host());
 
   // Collect all the URLs, convert them to GURLs, and crack all the urls into
   // file paths.
-  FileInfoList info_list;
+  PathAndMimeTypeSet path_mime_set;
   std::vector<GURL> file_urls;
   std::vector<base::FilePath> file_paths;
-  bool has_google_document = false;
   for (size_t i = 0; i < files_list->GetSize(); ++i) {
-    FileInfo info;
     std::string file_url_str;
     if (!files_list->GetString(i, &file_url_str))
       return false;
 
+    std::string mime_type;
     if (mime_types_list->GetSize() != 0 &&
-        !mime_types_list->GetString(i, &info.mime_type))
+        !mime_types_list->GetString(i, &mime_type))
       return false;
 
     GURL file_url(file_url_str);
@@ -509,55 +175,43 @@
         file_system_context->CrackURL(file_url));
     if (!chromeos::FileSystemBackend::CanHandleURL(file_system_url))
       continue;
+    const base::FilePath file_path = file_system_url.path();
 
     file_urls.push_back(file_url);
-    file_paths.push_back(file_system_url.path());
+    file_paths.push_back(file_path);
 
-    info.file_url = file_url;
-    info.file_path = file_system_url.path();
-    info_list.push_back(info);
+    // If MIME type is not provided, guess it from the file path.
+    if (mime_type.empty())
+      mime_type = file_manager::util::GetMimeTypeForPath(file_path);
 
-    if (google_apis::ResourceEntry::ClassifyEntryKindByFileExtension(
-            info.file_path) &
-        google_apis::ResourceEntry::KIND_OF_GOOGLE_DOCUMENT) {
-      has_google_document = true;
-    }
+    path_mime_set.insert(std::make_pair(file_path, mime_type));
   }
 
+  std::vector<file_manager::file_tasks::FullTaskDescriptor> tasks;
+  file_manager::file_tasks::FindAllTypesOfTasks(profile_,
+                                                path_mime_set,
+                                                file_urls,
+                                                file_paths,
+                                                &tasks);
+  // Convert the tasks into JSON format.
   ListValue* result_list = new ListValue();
+  for (size_t i = 0; i < tasks.size(); ++i)
+    result_list->Append(tasks[i].AsDictionaryValue().release());
+
   SetResult(result_list);
-
-  // Find the Drive app tasks first, because we want them to take precedence
-  // when setting the default app.
-  bool default_already_set = false;
-  // Google document are not opened by drive apps but file manager.
-  if (!has_google_document)
-    FindDriveAppTasks(info_list, result_list, &default_already_set);
-
-  // Find and append file handler tasks. We know there aren't duplicates
-  // because Drive apps and platform apps are entirely different kinds of
-  // tasks.
-  FindFileHandlerTasks(file_paths, result_list, &default_already_set);
-
-  // Find and append file browser handler tasks. We know there aren't
-  // duplicates because "file_browser_handlers" and "file_handlers" shouldn't
-  // be used in the same manifest.json.
-  FindFileBrowserHandlerTasks(file_urls,
-                              file_paths,
-                              result_list,
-                              &default_already_set);
-
   SendResponse(true);
   return true;
 }
 
-SetDefaultTaskFunction::SetDefaultTaskFunction() {
+FileBrowserPrivateSetDefaultTaskFunction::
+    FileBrowserPrivateSetDefaultTaskFunction() {
 }
 
-SetDefaultTaskFunction::~SetDefaultTaskFunction() {
+FileBrowserPrivateSetDefaultTaskFunction::
+    ~FileBrowserPrivateSetDefaultTaskFunction() {
 }
 
-bool SetDefaultTaskFunction::RunImpl() {
+bool FileBrowserPrivateSetDefaultTaskFunction::RunImpl() {
   // First param is task id that was to the extension with setDefaultTask call.
   std::string task_id;
   if (!args_->GetString(0, &task_id) || !task_id.size())
@@ -567,13 +221,12 @@
   if (!args_->GetList(1, &file_url_list))
     return false;
 
-  content::SiteInstance* site_instance = render_view_host()->GetSiteInstance();
-  scoped_refptr<fileapi::FileSystemContext> context =
-      BrowserContext::GetStoragePartition(profile(), site_instance)->
-      GetFileSystemContext();
+  scoped_refptr<fileapi::FileSystemContext> file_system_context =
+      file_manager::util::GetFileSystemContextForRenderViewHost(
+          profile(), render_view_host());
 
   std::set<std::string> suffixes =
-      GetUniqueSuffixes(file_url_list, context.get());
+      GetUniqueSuffixes(file_url_list, file_system_context.get());
 
   // MIME types are an optional parameter.
   base::ListValue* mime_type_list;
@@ -584,9 +237,6 @@
     mime_types = GetUniqueMimeTypes(mime_type_list);
   }
 
-  if (VLOG_IS_ON(1))
-    LogDefaultTask(mime_types, suffixes, task_id);
-
   // If there weren't any mime_types, and all the suffixes were blank,
   // then we "succeed", but don't actually associate with anything.
   // Otherwise, any time we set the default on a file with no extension
@@ -598,52 +248,11 @@
     return true;
   }
 
-  file_tasks::UpdateDefaultTask(profile_, task_id, suffixes, mime_types);
-
+  file_manager::file_tasks::UpdateDefaultTask(profile_->GetPrefs(),
+                                              task_id,
+                                              suffixes,
+                                              mime_types);
   return true;
 }
 
-ViewFilesFunction::ViewFilesFunction() {
-}
-
-ViewFilesFunction::~ViewFilesFunction() {
-}
-
-bool ViewFilesFunction::RunImpl() {
-  if (args_->GetSize() < 1) {
-    return false;
-  }
-
-  ListValue* path_list = NULL;
-  args_->GetList(0, &path_list);
-  DCHECK(path_list);
-
-  std::vector<base::FilePath> files;
-  for (size_t i = 0; i < path_list->GetSize(); ++i) {
-    std::string url_as_string;
-    path_list->GetString(i, &url_as_string);
-    base::FilePath path = util::GetLocalPathFromURL(
-        render_view_host(), profile(), GURL(url_as_string));
-    if (path.empty())
-      return false;
-    files.push_back(path);
-  }
-
-  Browser* browser = chrome::FindOrCreateTabbedBrowser(
-      profile_, chrome::HOST_DESKTOP_TYPE_ASH);
-  bool success = browser;
-
-  if (browser) {
-    for (size_t i = 0; i < files.size(); ++i) {
-      bool handled = util::OpenFileWithBrowser(browser, files[i]);
-      if (!handled && files.size() == 1)
-        success = false;
-    }
-  }
-
-  SetResult(Value::CreateBooleanValue(success));
-  SendResponse(true);
-  return true;
-}
-
-}  // namespace file_manager
+}  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.h b/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.h
index 4c12832..ad376d7 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_tasks.h
@@ -14,22 +14,25 @@
 
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_base.h"
 
+class PrefService;
+
 namespace drive {
 class DriveAppRegistry;
 }
 
-namespace file_manager {
+namespace extensions {
 
 // Implements the chrome.fileBrowserPrivate.executeTask method.
-class ExecuteTaskFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateExecuteTaskFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.executeTask",
                              FILEBROWSERPRIVATE_EXECUTETASK)
 
-  ExecuteTaskFunction();
+  FileBrowserPrivateExecuteTaskFunction();
 
  protected:
-  virtual ~ExecuteTaskFunction();
+  virtual ~FileBrowserPrivateExecuteTaskFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
@@ -38,113 +41,36 @@
 };
 
 // Implements the chrome.fileBrowserPrivate.getFileTasks method.
-class GetFileTasksFunction : public LoggedAsyncExtensionFunction {
+class FileBrowserPrivateGetFileTasksFunction
+    : public LoggedAsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.getFileTasks",
                              FILEBROWSERPRIVATE_GETFILETASKS)
 
-  GetFileTasksFunction();
+  FileBrowserPrivateGetFileTasksFunction();
 
  protected:
-  virtual ~GetFileTasksFunction();
+  virtual ~FileBrowserPrivateGetFileTasksFunction();
 
   // AsyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
-
- private:
-  struct FileInfo;
-  typedef std::vector<FileInfo> FileInfoList;
-
-  // Holds fields to build a task result.
-  struct TaskInfo;
-
-  // Map from a task id to TaskInfo.
-  typedef std::map<std::string, TaskInfo> TaskInfoMap;
-
-  // Looks up available apps for each file in |file_info_list| in the
-  // |registry|, and returns the intersection of all available apps as a
-  // map from task id to TaskInfo.
-  static void GetAvailableDriveTasks(drive::DriveAppRegistry* registry,
-                                     const FileInfoList& file_info_list,
-                                     TaskInfoMap* task_info_map);
-
-  // Looks in the preferences and finds any of the available apps that are
-  // also listed as default apps for any of the files in the info list.
-  void FindDefaultDriveTasks(const FileInfoList& file_info_list,
-                             const TaskInfoMap& task_info_map,
-                             std::set<std::string>* default_tasks);
-
-  // Creates a list of each task in |task_info_map| and stores the result into
-  // |result_list|. If a default task is set in the result list,
-  // |default_already_set| is set to true.
-  static void CreateDriveTasks(const TaskInfoMap& task_info_map,
-                               const std::set<std::string>& default_tasks,
-                               ListValue* result_list,
-                               bool* default_already_set);
-
-  // Finds the drive app tasks that can be used with the given files, and
-  // append them to the |result_list|. |*default_already_set| indicates if
-  // the |result_list| already contains the default task. If the value is
-  // false, the function will find the default task and set the value to true
-  // if found.
-  //
-  // "taskId" field in |result_list| will look like
-  // "<drive-app-id>|drive|open-with" (See also file_tasks.h).
-  // "driveApp" field in |result_list| will be set to "true".
-  void FindDriveAppTasks(const FileInfoList& file_info_list,
-                         ListValue* result_list,
-                         bool* default_already_set);
-
-  // Find the file handler tasks (apps declaring "file_handlers" in
-  // manifest.json) that can be used with the given files, appending them to
-  // the |result_list|. See the comment at FindDriveAppTasks() about
-  // |default_already_set|
-  void FindFileHandlerTasks(const std::vector<base::FilePath>& file_paths,
-                            ListValue* result_list,
-                            bool* default_already_set);
-
-  // Find the file browser handler tasks (app/extensions declaring
-  // "file_browser_handlers" in manifest.json) that can be used with the
-  // given files, appending them to the |result_list|. See the comment at
-  // FindDriveAppTasks() about |default_already_set|
-  void FindFileBrowserHandlerTasks(
-      const std::vector<GURL>& file_urls,
-      const std::vector<base::FilePath>& file_paths,
-      ListValue* result_list,
-      bool* default_already_set);
 };
 
 // Implements the chrome.fileBrowserPrivate.setDefaultTask method.
-class SetDefaultTaskFunction : public SyncExtensionFunction {
+class FileBrowserPrivateSetDefaultTaskFunction : public SyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.setDefaultTask",
                              FILEBROWSERPRIVATE_SETDEFAULTTASK)
 
-  SetDefaultTaskFunction();
+  FileBrowserPrivateSetDefaultTaskFunction();
 
  protected:
-  virtual ~SetDefaultTaskFunction();
+  virtual ~FileBrowserPrivateSetDefaultTaskFunction();
 
   // SyncExtensionFunction overrides.
   virtual bool RunImpl() OVERRIDE;
 };
 
-// Implements the chrome.fileBrowserPrivate.viewFiles method.
-// Views multiple selected files.  Window stays open.
-class ViewFilesFunction : public LoggedAsyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.viewFiles",
-                             FILEBROWSERPRIVATE_VIEWFILES)
-
-  ViewFilesFunction();
-
- protected:
-  virtual ~ViewFilesFunction();
-
-  // AsyncExtensionFunction overrides.
-  virtual bool RunImpl() OVERRIDE;
-};
-
-}  // namespace file_manager
+}  // namespace extensions
 
 #endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_TASKS_H_
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
index e076861..720e32a 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
@@ -24,7 +24,6 @@
 #include "webkit/browser/fileapi/file_system_url.h"
 
 using content::BrowserThread;
-using google_apis::InstalledApp;
 
 namespace file_manager {
 namespace util {
@@ -137,19 +136,6 @@
   return ExtensionTabUtil::GetTabId(web_contents);
 }
 
-GURL FindPreferredIcon(const InstalledApp::IconList& icons,
-                       int preferred_size) {
-  GURL result;
-  if (icons.empty())
-    return result;
-  result = icons.rbegin()->second;
-  for (InstalledApp::IconList::const_reverse_iterator iter = icons.rbegin();
-       iter != icons.rend() && iter->first >= preferred_size; ++iter) {
-    result = iter->second;
-  }
-  return result;
-}
-
 base::FilePath GetLocalPathFromURL(
     content::RenderViewHost* render_view_host,
     Profile* profile,
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_util.h b/chrome/browser/chromeos/extensions/file_manager/private_api_util.h
index cbf4186..be2e15e 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_util.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_util.h
@@ -29,17 +29,6 @@
 // error.
 int32 GetTabId(ExtensionFunctionDispatcher* dispatcher);
 
-// Finds an icon in the list of icons. If unable to find an icon of the exact
-// size requested, returns one with the next larger size. If all icons are
-// smaller than the preferred size, we'll return the largest one available.
-// Icons must be sorted by the icon size, smallest to largest. If there are no
-// icons in the list, returns an empty URL.
-GURL FindPreferredIcon(const google_apis::InstalledApp::IconList& icons,
-                       int preferred_size);
-
-// The preferred icon size, which should usually be used for FindPreferredIcon;
-const int kPreferredIconSize = 16;
-
 // Returns the local FilePath associated with |url|. If the file isn't of the
 // type FileSystemBackend handles, returns an empty
 // FilePath. |render_view_host| and |profile| are needed to obtain the
diff --git a/chrome/browser/chromeos/extensions/file_manager/select_file_dialog_util.cc b/chrome/browser/chromeos/extensions/file_manager/select_file_dialog_util.cc
new file mode 100644
index 0000000..fa5c530
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/select_file_dialog_util.cc
@@ -0,0 +1,53 @@
+// 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 "chrome/browser/chromeos/extensions/file_manager/select_file_dialog_util.h"
+
+#include "grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace file_manager {
+namespace util {
+
+string16 GetSelectFileDialogTitle(ui::SelectFileDialog::Type dialog_type) {
+  string16 title;
+  switch (dialog_type) {
+    case ui::SelectFileDialog::SELECT_NONE:
+      // Full page file manager doesn't need a title.
+      break;
+
+    case ui::SelectFileDialog::SELECT_FOLDER:
+      title = l10n_util::GetStringUTF16(
+          IDS_FILE_BROWSER_SELECT_FOLDER_TITLE);
+      break;
+
+    case ui::SelectFileDialog::SELECT_UPLOAD_FOLDER:
+      title = l10n_util::GetStringUTF16(
+          IDS_FILE_BROWSER_SELECT_UPLOAD_FOLDER_TITLE);
+      break;
+
+    case ui::SelectFileDialog::SELECT_SAVEAS_FILE:
+      title = l10n_util::GetStringUTF16(
+          IDS_FILE_BROWSER_SELECT_SAVEAS_FILE_TITLE);
+      break;
+
+    case ui::SelectFileDialog::SELECT_OPEN_FILE:
+      title = l10n_util::GetStringUTF16(
+          IDS_FILE_BROWSER_SELECT_OPEN_FILE_TITLE);
+      break;
+
+    case ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE:
+      title = l10n_util::GetStringUTF16(
+          IDS_FILE_BROWSER_SELECT_OPEN_MULTI_FILE_TITLE);
+      break;
+
+    default:
+      NOTREACHED();
+  }
+
+  return title;
+}
+
+}  // namespace util
+}  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/select_file_dialog_util.h b/chrome/browser/chromeos/extensions/file_manager/select_file_dialog_util.h
new file mode 100644
index 0000000..09e9505
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/file_manager/select_file_dialog_util.h
@@ -0,0 +1,21 @@
+// 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.
+//
+// This file provides utilities related to the select file dialog.
+
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_SELECT_FILE_DIALOG_UTIL_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_SELECT_FILE_DIALOG_UTIL_H_
+
+#include "ui/shell_dialogs/select_file_dialog.h"
+
+namespace file_manager {
+namespace util {
+
+// Get file dialog title string from its type.
+string16 GetSelectFileDialogTitle(ui::SelectFileDialog::Type type);
+
+}  // namespace util
+}  // namespace file_manager
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_SELECT_FILE_DIALOG_UTIL_H_
diff --git a/chrome/browser/chromeos/extensions/input_method_apitest_chromeos.cc b/chrome/browser/chromeos/extensions/input_method_apitest_chromeos.cc
index 128cd7f..3d46ad0 100644
--- a/chrome/browser/chromeos/extensions/input_method_apitest_chromeos.cc
+++ b/chrome/browser/chromeos/extensions/input_method_apitest_chromeos.cc
@@ -18,9 +18,9 @@
 
 namespace {
 
-const char kLoginScreenUILanguage[] = "ru";
+const char kLoginScreenUILanguage[] = "fr";
 const char kInitialInputMethodOnLoginScreen[] = "xkb:us::eng";
-const char kNewInputMethod[] = "ru::rus";
+const char kNewInputMethod[] = "fr::fra";
 const char kSetInputMethodMessage[] = "setInputMethod";
 const char kSetInputMethodDone[] = "done";
 
diff --git a/chrome/browser/chromeos/external_metrics.cc b/chrome/browser/chromeos/external_metrics.cc
index 37e6703..4e7dd11 100644
--- a/chrome/browser/chromeos/external_metrics.cc
+++ b/chrome/browser/chromeos/external_metrics.cc
@@ -23,9 +23,9 @@
 #include "base/metrics/histogram.h"
 #include "base/metrics/sparse_histogram.h"
 #include "base/metrics/statistics_recorder.h"
-#include "base/perftimer.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/sys_info.h"
+#include "base/test/perftimer.h"
 #include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/metrics/metrics_service.h"
diff --git a/chrome/browser/chromeos/fileapi/file_system_backend.cc b/chrome/browser/chromeos/fileapi/file_system_backend.cc
index 43a62c1..1ffc31d 100644
--- a/chrome/browser/chromeos/fileapi/file_system_backend.cc
+++ b/chrome/browser/chromeos/fileapi/file_system_backend.cc
@@ -144,7 +144,7 @@
   std::string extension_id = origin_url.host();
   // TODO(mtomasz): Temporarily whitelist TimeScapes. Remove this in M-31.
   // See: crbug.com/271946
-  if (extension_id == "mppoamgbcpnkpacolchbacppkflagjbp" &&
+  if (extension_id == "mlbmkoenclnokonejhlfakkeabdlmpek" &&
       url.type() == fileapi::kFileSystemTypeRestrictedNativeLocal) {
     return true;
   }
@@ -214,13 +214,6 @@
   return root_dirs;
 }
 
-fileapi::FileSystemFileUtil* FileSystemBackend::GetFileUtil(
-    fileapi::FileSystemType type) {
-  DCHECK(type == fileapi::kFileSystemTypeNativeLocal ||
-         type == fileapi::kFileSystemTypeRestrictedNativeLocal);
-  return local_file_util_->sync_file_util();
-}
-
 fileapi::AsyncFileUtil* FileSystemBackend::GetAsyncFileUtil(
     fileapi::FileSystemType type) {
   if (type == fileapi::kFileSystemTypeDrive)
diff --git a/chrome/browser/chromeos/fileapi/file_system_backend.h b/chrome/browser/chromeos/fileapi/file_system_backend.h
index 1ca1050..342814d 100644
--- a/chrome/browser/chromeos/fileapi/file_system_backend.h
+++ b/chrome/browser/chromeos/fileapi/file_system_backend.h
@@ -96,8 +96,6 @@
       fileapi::FileSystemType type,
       fileapi::OpenFileSystemMode mode,
       const OpenFileSystemCallback& callback) OVERRIDE;
-  virtual fileapi::FileSystemFileUtil* GetFileUtil(
-      fileapi::FileSystemType type) OVERRIDE;
   virtual fileapi::AsyncFileUtil* GetAsyncFileUtil(
       fileapi::FileSystemType type) OVERRIDE;
   virtual fileapi::CopyOrMoveFileValidatorFactory*
diff --git a/chrome/browser/chromeos/input_method/candidate_window_controller_impl.cc b/chrome/browser/chromeos/input_method/candidate_window_controller_impl.cc
index e49f670..54a6410 100644
--- a/chrome/browser/chromeos/input_method/candidate_window_controller_impl.cc
+++ b/chrome/browser/chromeos/input_method/candidate_window_controller_impl.cc
@@ -7,20 +7,17 @@
 #include <string>
 #include <vector>
 
+#include "ash/shell.h"
+#include "ash/shell_window_ids.h"
+#include "ash/wm/window_animations.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/observer_list.h"
 #include "chrome/browser/chromeos/input_method/candidate_window_view.h"
 #include "chrome/browser/chromeos/input_method/delayable_widget.h"
 #include "chrome/browser/chromeos/input_method/infolist_window_view.h"
-#include "chromeos/dbus/dbus_thread_manager.h"
 #include "ui/views/widget/widget.h"
 
-#if defined(USE_ASH)
-#include "ash/shell.h"
-#include "ash/shell_window_ids.h"
-#include "ash/wm/window_animations.h"
-#endif  // USE_ASH
 
 namespace chromeos {
 namespace input_method {
@@ -35,27 +32,15 @@
 gfx::Rect IBusRectToGfxRect(const ibus::Rect& rect) {
   return gfx::Rect(rect.x, rect.y, rect.width, rect.height);
 }
-
-// Returns pointer of IBusPanelService. This function returns NULL if it is not
-// ready.
-IBusPanelService* GetIBusPanelService() {
-  return DBusThreadManager::Get()->GetIBusPanelService();
-}
 }  // namespace
 
 bool CandidateWindowControllerImpl::Init() {
-  if (DBusThreadManager::Get()->GetIBusPanelService()) {
-    DBusThreadManager::Get()->GetIBusPanelService()->
-        SetUpCandidateWindowHandler(this);
-  }
-  IBusDaemonController::GetInstance()->AddObserver(this);
   // Create the candidate window view.
   CreateView();
   return true;
 }
 
 void CandidateWindowControllerImpl::Shutdown() {
-  IBusDaemonController::GetInstance()->RemoveObserver(this);
 }
 
 void CandidateWindowControllerImpl::CreateView() {
@@ -67,19 +52,14 @@
   // they should use WIDGET_OWNS_NATIVE_WIDGET ownership.
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   // Show the candidate window always on top
-#if defined(USE_ASH)
   params.parent = ash::Shell::GetContainer(
       ash::Shell::GetActiveRootWindow(),
       ash::internal::kShellWindowId_InputMethodContainer);
-#else
-  params.keep_on_top = true;
-#endif
   frame_->Init(params);
-#if defined(USE_ASH)
+
   views::corewm::SetWindowVisibilityAnimationType(
       frame_->GetNativeView(),
       views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
-#endif  // USE_ASH
 
   // Create the candidate window.
   candidate_window_ = new CandidateWindowView(frame_.get());
@@ -92,11 +72,10 @@
   // Create the infolist window.
   infolist_window_.reset(new DelayableWidget);
   infolist_window_->Init(params);
-#if defined(USE_ASH)
+
   views::corewm::SetWindowVisibilityAnimationType(
       infolist_window_->GetNativeView(),
       views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
-#endif  // USE_ASH
 
   InfolistWindowView* infolist_view = new InfolistWindowView;
   infolist_view->Init();
@@ -105,12 +84,12 @@
 
 CandidateWindowControllerImpl::CandidateWindowControllerImpl()
     : candidate_window_(NULL),
-      latest_infolist_focused_index_(InfolistWindowView::InvalidFocusIndex()) {}
+      latest_infolist_focused_index_(InfolistWindowView::InvalidFocusIndex()) {
+  IBusBridge::Get()->SetCandidateWindowHandler(this);
+}
 
 CandidateWindowControllerImpl::~CandidateWindowControllerImpl() {
-  if (DBusThreadManager::Get()->GetIBusPanelService())
-    DBusThreadManager::Get()->GetIBusPanelService()->
-        SetUpCandidateWindowHandler(NULL);
+  IBusBridge::Get()->SetCandidateWindowHandler(NULL);
   candidate_window_->RemoveObserver(this);
 }
 
@@ -300,10 +279,11 @@
 void CandidateWindowControllerImpl::OnCandidateCommitted(int index,
                                                          int button,
                                                          int flags) {
-  GetIBusPanelService()->CandidateClicked(
-      index,
-      static_cast<ibus::IBusMouseButton>(button),
-      flags);
+  IBusEngineHandlerInterface* engine = IBusBridge::Get()->GetEngineHandler();
+  if (engine)
+    engine->CandidateClicked(index,
+                             static_cast<ibus::IBusMouseButton>(button),
+                             flags);
 }
 
 void CandidateWindowControllerImpl::OnCandidateWindowOpened() {
@@ -326,18 +306,6 @@
   observers_.RemoveObserver(observer);
 }
 
-void CandidateWindowControllerImpl::OnConnected() {
-  DBusThreadManager::Get()->GetIBusPanelService()->SetUpCandidateWindowHandler(
-      this);
-}
-
-void CandidateWindowControllerImpl::OnDisconnected() {
-  candidate_window_->HideAll();
-  infolist_window_->Hide();
-  DBusThreadManager::Get()->GetIBusPanelService()->SetUpCandidateWindowHandler(
-      NULL);
-}
-
 // static
 gfx::Point CandidateWindowControllerImpl::GetInfolistWindowPosition(
     const gfx::Rect& candidate_window_rect,
diff --git a/chrome/browser/chromeos/input_method/candidate_window_controller_impl.h b/chrome/browser/chromeos/input_method/candidate_window_controller_impl.h
index caa4d45..3b6c4eb 100644
--- a/chrome/browser/chromeos/input_method/candidate_window_controller_impl.h
+++ b/chrome/browser/chromeos/input_method/candidate_window_controller_impl.h
@@ -11,7 +11,6 @@
 #include "base/observer_list.h"
 #include "chrome/browser/chromeos/input_method/candidate_window_view.h"
 #include "chrome/browser/chromeos/input_method/infolist_window_view.h"
-#include "chromeos/dbus/ibus/ibus_panel_service.h"
 #include "chromeos/ime/ibus_bridge.h"
 #include "chromeos/ime/ibus_daemon_controller.h"
 
@@ -31,8 +30,7 @@
 class CandidateWindowControllerImpl
     : public CandidateWindowController,
       public CandidateWindowView::Observer,
-      public IBusPanelCandidateWindowHandlerInterface,
-      public IBusDaemonController::Observer {
+      public IBusPanelCandidateWindowHandlerInterface {
  public:
   CandidateWindowControllerImpl();
   virtual ~CandidateWindowControllerImpl();
@@ -95,10 +93,6 @@
   virtual void UpdatePreeditText(const std::string& utf8_text,
                                  unsigned int cursor, bool visible) OVERRIDE;
 
-  // IBusDaemonController::Observer override
-  virtual void OnConnected() OVERRIDE;
-  virtual void OnDisconnected() OVERRIDE;
-
   // Updates infolist bounds, if current bounds is up-to-date, this function
   // does nothing.
   void UpdateInfolistBounds();
diff --git a/chrome/browser/chromeos/input_method/ibus_controller_base.cc b/chrome/browser/chromeos/input_method/ibus_controller_base.cc
deleted file mode 100644
index 0f16084..0000000
--- a/chrome/browser/chromeos/input_method/ibus_controller_base.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/input_method/ibus_controller_base.h"
-
-namespace chromeos {
-namespace input_method {
-
-IBusControllerBase::IBusControllerBase() {
-}
-
-IBusControllerBase::~IBusControllerBase() {
-}
-
-void IBusControllerBase::AddObserver(Observer* observer) {
-  observers_.AddObserver(observer);
-}
-
-void IBusControllerBase::RemoveObserver(Observer* observer) {
-  observers_.RemoveObserver(observer);
-}
-
-const InputMethodPropertyList&
-IBusControllerBase::GetCurrentProperties() const {
-  return current_property_list_;
-}
-
-void IBusControllerBase::ClearProperties() {
-  current_property_list_.clear();
-}
-
-void IBusControllerBase::NotifyPropertyChangedForTesting() {
-  FOR_EACH_OBSERVER(Observer, observers_, PropertyChanged());
-}
-
-void IBusControllerBase::SetCurrentPropertiesForTesting(
-    const InputMethodPropertyList& current_property_list) {
-  current_property_list_ = current_property_list;
-}
-
-}  // namespace input_method
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/input_method/ibus_controller_base.h b/chrome/browser/chromeos/input_method/ibus_controller_base.h
deleted file mode 100644
index 83b0f93..0000000
--- a/chrome/browser/chromeos/input_method/ibus_controller_base.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_INPUT_METHOD_IBUS_CONTROLLER_BASE_H_
-#define CHROME_BROWSER_CHROMEOS_INPUT_METHOD_IBUS_CONTROLLER_BASE_H_
-
-#include <map>
-#include <utility>
-
-#include "base/observer_list.h"
-#include "chrome/browser/chromeos/input_method/ibus_controller.h"
-#include "chromeos/ime/input_method_property.h"
-
-namespace chromeos {
-namespace input_method {
-
-// The common implementation of the IBusController. This file does not depend on
-// libibus, hence is unit-testable.
-class IBusControllerBase : public IBusController {
- public:
-  IBusControllerBase();
-  virtual ~IBusControllerBase();
-
-  // IBusController overrides. Derived classes should not override these 4
-  // functions.
-  virtual void AddObserver(Observer* observer) OVERRIDE;
-  virtual void RemoveObserver(Observer* observer) OVERRIDE;
-  virtual const InputMethodPropertyList& GetCurrentProperties() const OVERRIDE;
-  virtual void ClearProperties() OVERRIDE;
-
-  // Notifies all |observers_|.
-  void NotifyPropertyChangedForTesting();
-
-  // Updates |current_property_list_|.
-  void SetCurrentPropertiesForTesting(
-      const InputMethodPropertyList& current_property_list);
-
- protected:
-  ObserverList<Observer> observers_;
-
-  // The value which will be returned by GetCurrentProperties(). Derived classes
-  // should update this variable when needed.
-  InputMethodPropertyList current_property_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(IBusControllerBase);
-};
-
-}  // namespace input_method
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_INPUT_METHOD_IBUS_CONTROLLER_BASE_H_
diff --git a/chrome/browser/chromeos/input_method/ibus_controller_base_unittest.cc b/chrome/browser/chromeos/input_method/ibus_controller_base_unittest.cc
deleted file mode 100644
index 353b109..0000000
--- a/chrome/browser/chromeos/input_method/ibus_controller_base_unittest.cc
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "chrome/browser/chromeos/input_method/ibus_controller_base.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace chromeos {
-namespace input_method {
-
-namespace {
-
-// A mock class for testing SetInputMethodConfig(), AddObserver(), and
-// RemoveObserver() methods in IBusControllerBase.
-class TestIBusController : public IBusControllerBase {
- public:
-  TestIBusController() {
-  }
-  virtual ~TestIBusController() {
-  }
-
-  // IBusController overrides:
-  virtual void ClearProperties() OVERRIDE {}
-  virtual bool ActivateInputMethodProperty(const std::string& key) OVERRIDE {
-    return true;
-  }
-
-  size_t GetObserverCount() const {
-    return observers_.size();
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TestIBusController);
-};
-
-class TestObserver : public IBusController::Observer {
- public:
-  // IBusController::Observer overrides:
-  virtual void PropertyChanged() OVERRIDE {}
-};
-
-class IBusControllerBaseTest : public testing::Test {
- public:
-  virtual void SetUp() {
-    controller_.reset(new TestIBusController);
-  }
-  virtual void TearDown() {
-    controller_.reset();
-  }
-
- protected:
-  scoped_ptr<TestIBusController> controller_;
-};
-
-}  // namespace
-
-TEST_F(IBusControllerBaseTest, TestAddRemoveObserver) {
-  TestObserver observer1;
-  TestObserver observer2;
-  TestObserver observer3;
-  EXPECT_EQ(0U, controller_->GetObserverCount());
-  controller_->AddObserver(&observer1);
-  EXPECT_EQ(1U, controller_->GetObserverCount());
-  controller_->AddObserver(&observer2);
-  EXPECT_EQ(2U, controller_->GetObserverCount());
-  controller_->RemoveObserver(&observer3);  // nop
-  EXPECT_EQ(2U, controller_->GetObserverCount());
-  controller_->RemoveObserver(&observer1);
-  EXPECT_EQ(1U, controller_->GetObserverCount());
-  controller_->RemoveObserver(&observer1);  // nop
-  EXPECT_EQ(1U, controller_->GetObserverCount());
-  controller_->RemoveObserver(&observer2);
-  EXPECT_EQ(0U, controller_->GetObserverCount());
-}
-
-TEST_F(IBusControllerBaseTest, TestGetCurrentProperties) {
-  EXPECT_EQ(0U, controller_->GetCurrentProperties().size());
-}
-
-}  // namespace input_method
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/input_method/ibus_controller_impl.cc b/chrome/browser/chromeos/input_method/ibus_controller_impl.cc
index 1bd85f9..256937b 100644
--- a/chrome/browser/chromeos/input_method/ibus_controller_impl.cc
+++ b/chrome/browser/chromeos/input_method/ibus_controller_impl.cc
@@ -212,6 +212,23 @@
   return true;
 }
 
+void IBusControllerImpl::AddObserver(Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void IBusControllerImpl::RemoveObserver(Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+const InputMethodPropertyList&
+IBusControllerImpl::GetCurrentProperties() const {
+  return current_property_list_;
+}
+
+void IBusControllerImpl::ClearProperties() {
+  current_property_list_.clear();
+}
+
 void IBusControllerImpl::RegisterProperties(
     const IBusPropertyList& ibus_prop_list) {
   current_property_list_.clear();
diff --git a/chrome/browser/chromeos/input_method/ibus_controller_impl.h b/chrome/browser/chromeos/input_method/ibus_controller_impl.h
index e262e8d..45de917 100644
--- a/chrome/browser/chromeos/input_method/ibus_controller_impl.h
+++ b/chrome/browser/chromeos/input_method/ibus_controller_impl.h
@@ -8,8 +8,10 @@
 #include <string>
 #include <vector>
 
-#include "chrome/browser/chromeos/input_method/ibus_controller_base.h"
+#include "base/observer_list.h"
+#include "chrome/browser/chromeos/input_method/ibus_controller.h"
 #include "chromeos/ime/ibus_bridge.h"
+#include "chromeos/ime/input_method_property.h"
 
 namespace chromeos {
 namespace input_method {
@@ -18,9 +20,7 @@
 typedef std::vector<InputMethodProperty> InputMethodPropertyList;
 
 // The IBusController implementation.
-// TODO(nona): Merge to IBusControllerBase, there is no longer reason to split
-//             this class into Impl and Base.
-class IBusControllerImpl : public IBusControllerBase,
+class IBusControllerImpl : public IBusController,
                            public IBusPanelPropertyHandlerInterface {
  public:
   IBusControllerImpl();
@@ -29,12 +29,26 @@
   // IBusController overrides:
   virtual bool ActivateInputMethodProperty(const std::string& key) OVERRIDE;
 
+  // IBusController overrides. Derived classes should not override these 4
+  // functions.
+  virtual void AddObserver(Observer* observer) OVERRIDE;
+  virtual void RemoveObserver(Observer* observer) OVERRIDE;
+  virtual const InputMethodPropertyList& GetCurrentProperties() const OVERRIDE;
+  virtual void ClearProperties() OVERRIDE;
+
   // Calls <anonymous_namespace>::FindAndUpdateProperty. This method is just for
   // unit testing.
   static bool FindAndUpdatePropertyForTesting(
       const InputMethodProperty& new_prop,
       InputMethodPropertyList* prop_list);
 
+ protected:
+  ObserverList<Observer> observers_;
+
+  // The value which will be returned by GetCurrentProperties(). Derived classes
+  // should update this variable when needed.
+  InputMethodPropertyList current_property_list_;
+
  private:
   // IBusPanelPropertyHandlerInterface overrides:
   virtual void RegisterProperties(
diff --git a/chrome/browser/chromeos/input_method/ibus_controller_impl_unittest.cc b/chrome/browser/chromeos/input_method/ibus_controller_impl_unittest.cc
index 2a251c8..da9afcf 100644
--- a/chrome/browser/chromeos/input_method/ibus_controller_impl_unittest.cc
+++ b/chrome/browser/chromeos/input_method/ibus_controller_impl_unittest.cc
@@ -18,6 +18,29 @@
                                                              prop_list);
 }
 
+// A mock class for testing AddObserver() and RemoveObserver() methods
+// in IBusControllerImpl.
+class TestIBusController : public IBusControllerImpl {
+ public:
+  TestIBusController() {
+  }
+
+  virtual ~TestIBusController() {
+  }
+
+  bool HasObservers() const {
+    return observers_.might_have_observers();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestIBusController);
+};
+
+class TestObserver : public IBusController::Observer {
+ public:
+  // IBusController::Observer overrides:
+  virtual void PropertyChanged() OVERRIDE {}
+};
 }  // namespace
 
 TEST(IBusControllerImplTest, TestFindAndUpdateProperty) {
@@ -49,5 +72,38 @@
             properties[1]);
 }
 
+TEST(IBusControllerImplTest, TestAddRemoveObserver) {
+  IBusBridge::Initialize();
+  {
+    TestIBusController controller;
+    TestObserver observer1;
+    TestObserver observer2;
+    TestObserver observer3;
+    EXPECT_FALSE(controller.HasObservers());
+    controller.AddObserver(&observer1);
+    EXPECT_TRUE(controller.HasObservers());
+    controller.AddObserver(&observer2);
+    EXPECT_TRUE(controller.HasObservers());
+    controller.RemoveObserver(&observer3);  // nop
+    EXPECT_TRUE(controller.HasObservers());
+    controller.RemoveObserver(&observer1);
+    EXPECT_TRUE(controller.HasObservers());
+    controller.RemoveObserver(&observer1);  // nop
+    EXPECT_TRUE(controller.HasObservers());
+    controller.RemoveObserver(&observer2);
+    EXPECT_FALSE(controller.HasObservers());
+  }
+  IBusBridge::Shutdown();
+}
+
+TEST(IBusControllerImplTest, TestGetCurrentProperties) {
+  IBusBridge::Initialize();
+  {
+    IBusControllerImpl controller;
+    EXPECT_EQ(0U, controller.GetCurrentProperties().size());
+  }
+  IBusBridge::Shutdown();
+}
+
 }  // namespace input_method
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
index 5a41b15..ab90ac6 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
@@ -10,8 +10,10 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/prefs/pref_service.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/input_method/candidate_window_controller.h"
 #include "chrome/browser/chromeos/input_method/component_extension_ime_manager_impl.h"
 #include "chrome/browser/chromeos/input_method/input_method_engine_ibus.h"
@@ -115,10 +117,11 @@
 
 }  // namespace
 
-bool InputMethodManagerImpl::IsFullLatinKeyboard(
+bool InputMethodManagerImpl::IsLoginKeyboard(
     const std::string& layout) const {
-  const std::string& lang = util_.GetLanguageCodeFromInputMethodId(layout);
-  return full_latin_keyboard_checker.IsFullLatinKeyboard(layout, lang);
+  const InputMethodDescriptor* ime =
+      util_.GetInputMethodDescriptorFromId(layout);
+  return ime ? ime->is_login_keyboard() : false;
 }
 
 InputMethodManagerImpl::InputMethodManagerImpl(
@@ -246,7 +249,7 @@
   // layouts, so it appears first on the list of active input
   // methods at the input language status menu.
   if (util_.IsValidInputMethodId(initial_layout) &&
-      InputMethodUtil::IsKeyboardLayout(initial_layout)) {
+      IsLoginKeyboard(initial_layout)) {
     layouts.push_back(initial_layout);
   } else if (!initial_layout.empty()) {
     DVLOG(1) << "EnableLayouts: ignoring non-keyboard or invalid ID: "
@@ -258,7 +261,7 @@
     const std::string& candidate = candidates[i];
     // Not efficient, but should be fine, as the two vectors are very
     // short (2-5 items).
-    if (!Contains(layouts, candidate))
+    if (!Contains(layouts, candidate) && IsLoginKeyboard(candidate))
       layouts.push_back(candidate);
   }
 
@@ -562,7 +565,7 @@
   }
 
   extra_input_methods_[id] =
-      InputMethodDescriptor(id, name, layouts, languages, options_url);
+      InputMethodDescriptor(id, name, layouts, languages, false, options_url);
   if (Contains(enabled_extension_imes_, id) &&
       !ComponentExtensionIMEManager::IsComponentExtensionIMEId(id)) {
     if (!Contains(active_input_method_ids_, id)) {
@@ -668,6 +671,24 @@
   }
 }
 
+void InputMethodManagerImpl::SetInputMethodDefault() {
+  // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty
+  // and US dvorak keyboard layouts.
+  if (g_browser_process && g_browser_process->local_state()) {
+    const std::string locale = g_browser_process->GetApplicationLocale();
+    // If the preferred keyboard for the login screen has been saved, use it.
+    PrefService* prefs = g_browser_process->local_state();
+    std::string initial_input_method_id =
+        prefs->GetString(chromeos::language_prefs::kPreferredKeyboardLayout);
+    if (initial_input_method_id.empty()) {
+      // If kPreferredKeyboardLayout is not specified, use the hardware layout.
+      initial_input_method_id =
+          GetInputMethodUtil()->GetHardwareInputMethodId();
+    }
+    EnableLayouts(locale, initial_input_method_id);
+  }
+}
+
 bool InputMethodManagerImpl::SwitchToNextInputMethod() {
   // Sanity checks.
   if (active_input_method_ids_.empty()) {
@@ -925,7 +946,7 @@
     const std::string& input_method_id = saved_active_input_method_ids_[i];
     // Skip if it's not a keyboard layout. Drop input methods including
     // extension ones.
-    if (!InputMethodUtil::IsKeyboardLayout(input_method_id))
+    if (!IsLoginKeyboard(input_method_id))
       continue;
     active_input_method_ids_.push_back(input_method_id);
     if (input_method_id == hardware_keyboard_id)
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.h b/chrome/browser/chromeos/input_method/input_method_manager_impl.h
index 88f1875..fdb24cc 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl.h
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.h
@@ -14,7 +14,6 @@
 #include "base/threading/thread_checker.h"
 #include "chrome/browser/chromeos/input_method/candidate_window_controller.h"
 #include "chrome/browser/chromeos/input_method/ibus_controller.h"
-#include "chrome/browser/chromeos/input_method/input_method_manager_impl_ll.h"
 #include "chrome/browser/chromeos/input_method/input_method_util.h"
 #include "chromeos/ime/ibus_daemon_controller.h"
 #include "chromeos/ime/input_method_manager.h"
@@ -86,6 +85,7 @@
   virtual void GetInputMethodExtensions(
       InputMethodDescriptors* result) OVERRIDE;
   virtual void SetEnabledExtensionImes(std::vector<std::string>* ids) OVERRIDE;
+  virtual void SetInputMethodDefault() OVERRIDE;
   virtual bool SwitchToNextInputMethod() OVERRIDE;
   virtual bool SwitchToPreviousInputMethod(
       const ui::Accelerator& accelerator) OVERRIDE;
@@ -97,7 +97,7 @@
   virtual InputMethodUtil* GetInputMethodUtil() OVERRIDE;
   virtual ComponentExtensionIMEManager*
       GetComponentExtensionIMEManager() OVERRIDE;
-  virtual bool IsFullLatinKeyboard(const std::string& layout) const OVERRIDE;
+  virtual bool IsLoginKeyboard(const std::string& layout) const OVERRIDE;
 
   // Sets |ibus_controller_|.
   void SetIBusControllerForTesting(IBusController* ibus_controller);
@@ -235,10 +235,6 @@
 
   base::WeakPtrFactory<InputMethodManagerImpl> weak_ptr_factory_;
 
-  // Check if input method id allows full latin input (for entering passwords on
-  // login screen)
-  FullLatinKeyboardLayoutChecker full_latin_keyboard_checker;
-
   DISALLOW_COPY_AND_ASSIGN(InputMethodManagerImpl);
 };
 
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl_ll.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl_ll.cc
deleted file mode 100644
index 7bf850c..0000000
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl_ll.cc
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/input_method/input_method_manager_impl_ll.h"
-
-#include <string.h>
-
-#include <limits>
-
-namespace chromeos {
-namespace input_method {
-
-struct KBDList {
-  const char* const* layouts;
-  size_t size;
-};
-
-namespace {
-
-// A language may have some special layout that allows full latin input.
-static const char* const kJPFullLatinKeyboardLayouts[] = {
-  "xkb:jp::jpn"
-};
-
-static const KBDList kJPFullLatinKeyboards = {
-  kJPFullLatinKeyboardLayouts, arraysize(kJPFullLatinKeyboardLayouts)
-};
-
-// A list of languages and their layouts having full 26 latin letter set on
-// keyboard.
-
-// If permitted_layouts is NULL, then all keyboard layouts for the
-// language are "Full Latin Input" and can be used to input passwords on
-// login screen.
-
-// If permitted_layouts is not NULL, it must contain all layouts for the
-// language, that can be used at login screen.
-//
-static const struct SomeLatinKeyboardLanguageList {
-  const char* lang;
-  const KBDList* permitted_layouts;
-} kHasLatinKeyboardLanguageList[] = {
-  {"ca" /* Catalan            */, NULL},
-  {"cs" /* Czech              */, NULL},
-  {"da" /* Danish             */, NULL},
-  {"de" /* German             */, NULL},
-  {"en" /* English            */, NULL},
-  {"es" /* Spanish            */, NULL},
-  {"et" /* Estonian           */, NULL},
-  {"fi" /* Finnish            */, NULL},
-  {"fr" /* French             */, NULL},
-  {"ja" /* Japanese           */, &kJPFullLatinKeyboards},
-  {"hr" /* Croatian           */, NULL},
-  {"hu" /* Hungarian          */, NULL},
-  {"is" /* Icelandic          */, NULL},
-  {"it" /* Italian            */, NULL},
-  {"lt" /* Lithuanian         */, NULL},
-  {"lv" /* Latvian            */, NULL},
-  {"nb" /* Norwegian (Bokmal) */, NULL},
-  {"nl" /* Dutch              */, NULL},
-  {"pl" /* Polish             */, NULL},
-  {"pt" /* Portuguese         */, NULL},
-  {"ro" /* Romanian           */, NULL},
-  {"sk" /* Slovak             */, NULL},
-  {"sl" /* Slovenian          */, NULL},
-  {"sv" /* Swedish            */, NULL},
-  {"tr" /* Turkish            */, NULL},
-};
-
-}  // namespace
-
-bool FullLatinKeyboardLayoutChecker::IsFullLatinKeyboard(
-    const std::string& layout,
-    const std::string& lang) const {
-  if (lang.size() < 2) {
-    return false;
-  }
-
-  const TwoLetterLanguageCode ll(lang.c_str());
-  const std::vector<TwoLetterLanguageCode2KBDList>::const_iterator pos =
-      std::lower_bound(full_latin_keyboard_languages_.begin(),
-                       full_latin_keyboard_languages_.end(),
-                       ll);
-
-  if (pos == full_latin_keyboard_languages_.end())
-    return false;
-
-  if (pos->lang != ll)
-    return false;
-
-  const KBDList* kbdlist =
-      kHasLatinKeyboardLanguageList[pos->index].permitted_layouts;
-
-  if (kbdlist == NULL)
-    return true;
-
-  for (size_t i = 0; i < kbdlist->size; ++i)
-    if (strcmp(layout.c_str(), kbdlist->layouts[i]) == 0)
-      return true;
-
-  return false;
-}
-
-FullLatinKeyboardLayoutChecker::FullLatinKeyboardLayoutChecker() {
-  DCHECK(arraysize(kHasLatinKeyboardLanguageList) <
-         std::numeric_limits<uint16_t>::max());
-
-  full_latin_keyboard_languages_.reserve(
-      arraysize(kHasLatinKeyboardLanguageList));
-
-  for (size_t i = 0; i < arraysize(kHasLatinKeyboardLanguageList); ++i)
-    full_latin_keyboard_languages_.push_back(TwoLetterLanguageCode2KBDList(
-        kHasLatinKeyboardLanguageList[i].lang, i));
-
-  std::sort(full_latin_keyboard_languages_.begin(),
-            full_latin_keyboard_languages_.end());
-}
-
-FullLatinKeyboardLayoutChecker::~FullLatinKeyboardLayoutChecker() {
-}
-
-}  // namespace input_method
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl_ll.h b/chrome/browser/chromeos/input_method/input_method_manager_impl_ll.h
deleted file mode 100644
index f670abc..0000000
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl_ll.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_INPUT_METHOD_INPUT_METHOD_MANAGER_IMPL_LL_H_
-#define CHROME_BROWSER_CHROMEOS_INPUT_METHOD_INPUT_METHOD_MANAGER_IMPL_LL_H_
-
-// "Latin Layout" checker: checks if given keyboard layout is "Full Latin Input"
-
-#include <string>
-#include <vector>
-
-#include "base/strings/string_util.h"
-
-namespace chromeos {
-namespace input_method {
-
-class TwoLetterLanguageCode {
- public:
-  TwoLetterLanguageCode() : val(0) {}
-  explicit TwoLetterLanguageCode(const char* lang)
-      : val(base::ToLowerASCII(lang[0]) * 256 + base::ToLowerASCII(lang[1])) {}
-
-  bool operator<(const TwoLetterLanguageCode& r) const { return val < r.val; }
-  bool operator!=(const TwoLetterLanguageCode& r) const { return val != r.val; }
-
- private:
-  uint16_t val;
-};
-
-// To keep index small, sizeof(TwoLetterLanguageCode2KBDList) = 4.
-class TwoLetterLanguageCode2KBDList {
- public:
-  TwoLetterLanguageCode2KBDList() : index(0) {}
-  TwoLetterLanguageCode2KBDList(const char* l, const uint16_t i)
-      : lang(l), index(i) {}
-
-  bool operator<(const TwoLetterLanguageCode2KBDList& r) const {
-    return lang < r.lang;
-  }
-  bool operator<(const TwoLetterLanguageCode& r) const { return lang < r; }
-
-  TwoLetterLanguageCode lang;
-
-  // index in kHasLatinKeyboardLanguageList[]
-  uint16_t index;
-};
-
-// For fast lookup "whether this language and layout are among listed in
-// kHasLatinKeyboardLanguageList[] or not".
-class FullLatinKeyboardLayoutChecker {
- public:
-  FullLatinKeyboardLayoutChecker();
-  ~FullLatinKeyboardLayoutChecker();
-
-  bool IsFullLatinKeyboard(const std::string& layout,
-                           const std::string& lang) const;
-
- private:
-  // Sorted vector for fast lookup.
-  std::vector<TwoLetterLanguageCode2KBDList> full_latin_keyboard_languages_;
-};
-
-}  // namespace input_method
-}  // namespace chromeos
-
-#endif  // CHROME_BROWSER_CHROMEOS_INPUT_METHOD_INPUT_METHOD_MANAGER_IMPL_LL_H_
diff --git a/chrome/browser/chromeos/input_method/input_method_persistence.cc b/chrome/browser/chromeos/input_method/input_method_persistence.cc
index 49809fe..da37994 100644
--- a/chrome/browser/chromeos/input_method/input_method_persistence.cc
+++ b/chrome/browser/chromeos/input_method/input_method_persistence.cc
@@ -33,7 +33,7 @@
     const chromeos::input_method::InputMethodManager* const manager) {
   // Skip if it's not a keyboard layout. Drop input methods including
   // extension ones.
-  if (!InputMethodUtil::IsKeyboardLayout(input_method))
+  if (!manager->IsLoginKeyboard(input_method))
     return;
 
   PrefService* const local_state = g_browser_process->local_state();
@@ -43,7 +43,7 @@
   if (profile == NULL)
     return;
 
-  if (!manager->IsFullLatinKeyboard(input_method))
+  if (!manager->IsLoginKeyboard(input_method))
     return;
 
   const std::string username = profile->GetProfileName();
@@ -124,7 +124,7 @@
   // Save the new input method id depending on the current browser state.
   switch (state_) {
     case InputMethodManager::STATE_LOGIN_SCREEN:
-      if (!InputMethodUtil::IsKeyboardLayout(current_input_method)) {
+      if (!manager->IsLoginKeyboard(current_input_method)) {
         DVLOG(1) << "Only keyboard layouts are supported: "
                  << current_input_method;
         return;
diff --git a/chrome/browser/chromeos/input_method/input_method_util.cc b/chrome/browser/chromeos/input_method/input_method_util.cc
index e8ed45b..609dfb7 100644
--- a/chrome/browser/chromeos/input_method/input_method_util.cc
+++ b/chrome/browser/chromeos/input_method/input_method_util.cc
@@ -637,6 +637,7 @@
                                "",
                                layouts,
                                languages,
+                               true,  // login keyboard.
                                GURL());  // options page, not available.
 }
 
diff --git a/chrome/browser/chromeos/input_method/input_method_util_unittest.cc b/chrome/browser/chromeos/input_method/input_method_util_unittest.cc
index 74a16b9..692a38f 100644
--- a/chrome/browser/chromeos/input_method/input_method_util_unittest.cc
+++ b/chrome/browser/chromeos/input_method/input_method_util_unittest.cc
@@ -66,6 +66,7 @@
                                      "Pinyin input for testing",
                                      layouts,
                                      languages,
+                                     false,
                                      GURL(""));
     input_methods.push_back(pinyin_ime);
 
@@ -75,6 +76,7 @@
                                      "Zhuyin input for testing",
                                      layouts,
                                      languages,
+                                     false,
                                      GURL(""));
     input_methods.push_back(zhuyin_ime);
 
@@ -92,6 +94,7 @@
                                  "",
                                  layouts,
                                  languages,
+                                 true,
                                  GURL());  // options page url
   }
 
diff --git a/chrome/browser/chromeos/input_method/mock_ibus_controller.cc b/chrome/browser/chromeos/input_method/mock_ibus_controller.cc
index babc5d9..dbced56 100644
--- a/chrome/browser/chromeos/input_method/mock_ibus_controller.cc
+++ b/chrome/browser/chromeos/input_method/mock_ibus_controller.cc
@@ -23,5 +23,31 @@
   return activate_input_method_property_return_;
 }
 
+void MockIBusController::AddObserver(Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void MockIBusController::RemoveObserver(Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+const InputMethodPropertyList&
+MockIBusController::GetCurrentProperties() const {
+  return current_property_list_;
+}
+
+void MockIBusController::ClearProperties() {
+  current_property_list_.clear();
+}
+
+void MockIBusController::NotifyPropertyChangedForTesting() {
+  FOR_EACH_OBSERVER(Observer, observers_, PropertyChanged());
+}
+
+void MockIBusController::SetCurrentPropertiesForTesting(
+    const InputMethodPropertyList& current_property_list) {
+  current_property_list_ = current_property_list;
+}
+
 }  // namespace input_method
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/input_method/mock_ibus_controller.h b/chrome/browser/chromeos/input_method/mock_ibus_controller.h
index e27c336..180152a 100644
--- a/chrome/browser/chromeos/input_method/mock_ibus_controller.h
+++ b/chrome/browser/chromeos/input_method/mock_ibus_controller.h
@@ -5,27 +5,47 @@
 #ifndef CHROME_BROWSER_CHROMEOS_INPUT_METHOD_MOCK_IBUS_CONTROLLER_H_
 #define CHROME_BROWSER_CHROMEOS_INPUT_METHOD_MOCK_IBUS_CONTROLLER_H_
 
-#include "chrome/browser/chromeos/input_method/ibus_controller_base.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/observer_list.h"
+#include "chrome/browser/chromeos/input_method/ibus_controller.h"
+#include "chromeos/ime/input_method_config.h"
 
 namespace chromeos {
 namespace input_method {
 
-struct InputMethodProperty;
-
 // Mock IBusController implementation.
 // TODO(nona): Remove this class and use MockIBus stuff instead.
-class MockIBusController : public IBusControllerBase {
+class MockIBusController : public IBusController {
  public:
   MockIBusController();
   virtual ~MockIBusController();
 
   // IBusController overrides:
   virtual bool ActivateInputMethodProperty(const std::string& key) OVERRIDE;
+  virtual void AddObserver(Observer* observer) OVERRIDE;
+  virtual void RemoveObserver(Observer* observer) OVERRIDE;
+  virtual const InputMethodPropertyList& GetCurrentProperties() const OVERRIDE;
+  virtual void ClearProperties() OVERRIDE;
+
+  // Notifies all |observers_|.
+  void NotifyPropertyChangedForTesting();
+
+  // Updates |current_property_list_|.
+  void SetCurrentPropertiesForTesting(
+    const InputMethodPropertyList& current_property_list);
 
   int activate_input_method_property_count_;
   std::string activate_input_method_property_key_;
   bool activate_input_method_property_return_;
 
+ protected:
+  ObserverList<Observer> observers_;
+
+  // The value which will be returned by GetCurrentProperties(). Derived classes
+  // should update this variable when needed.
+  InputMethodPropertyList current_property_list_;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(MockIBusController);
 };
diff --git a/chrome/browser/chromeos/input_method/mock_input_method_manager.cc b/chrome/browser/chromeos/input_method/mock_input_method_manager.cc
index da33694..906097c 100644
--- a/chrome/browser/chromeos/input_method/mock_input_method_manager.cc
+++ b/chrome/browser/chromeos/input_method/mock_input_method_manager.cc
@@ -113,6 +113,9 @@
     std::vector<std::string>* ids) {
 }
 
+void MockInputMethodManager::SetInputMethodDefault() {
+}
+
 bool MockInputMethodManager::SwitchToNextInputMethod() {
   return true;
 }
@@ -135,6 +138,7 @@
                                  descriptor.name(),
                                  descriptor.keyboard_layouts(),
                                  descriptor.language_codes(),
+                                 true,
                                  GURL());  // options page url.
   }
   return descriptor;
@@ -167,7 +171,7 @@
   delegate_.set_hardware_keyboard_layout(value);
 }
 
-bool MockInputMethodManager::IsFullLatinKeyboard(
+bool MockInputMethodManager::IsLoginKeyboard(
     const std::string& layout) const {
   return true;
 }
diff --git a/chrome/browser/chromeos/input_method/mock_input_method_manager.h b/chrome/browser/chromeos/input_method/mock_input_method_manager.h
index 0534b8a..e532791 100644
--- a/chrome/browser/chromeos/input_method/mock_input_method_manager.h
+++ b/chrome/browser/chromeos/input_method/mock_input_method_manager.h
@@ -58,6 +58,7 @@
   virtual void GetInputMethodExtensions(
       InputMethodDescriptors* result) OVERRIDE;
   virtual void SetEnabledExtensionImes(std::vector<std::string>* ids) OVERRIDE;
+  virtual void SetInputMethodDefault() OVERRIDE;
   virtual bool SwitchToNextInputMethod() OVERRIDE;
   virtual bool SwitchToPreviousInputMethod(
       const ui::Accelerator& accelerator) OVERRIDE;
@@ -69,7 +70,7 @@
   virtual InputMethodUtil* GetInputMethodUtil() OVERRIDE;
   virtual ComponentExtensionIMEManager*
       GetComponentExtensionIMEManager() OVERRIDE;
-  virtual bool IsFullLatinKeyboard(const std::string& layout) const OVERRIDE;
+  virtual bool IsLoginKeyboard(const std::string& layout) const OVERRIDE;
 
   // Sets an input method ID which will be returned by GetCurrentInputMethod().
   void SetCurrentInputMethodId(const std::string& input_method_id) {
diff --git a/chrome/browser/chromeos/login/app_launch_controller.cc b/chrome/browser/chromeos/login/app_launch_controller.cc
index 65819ec..15db70d 100644
--- a/chrome/browser/chromeos/login/app_launch_controller.cc
+++ b/chrome/browser/chromeos/login/app_launch_controller.cc
@@ -80,7 +80,7 @@
   DVLOG(1) << "Profile loaded... Starting app launch.";
   profile_ = profile;
 
-  // StartupAppLauncher manages its own lifetime.
+  kiosk_profile_loader_.reset();
   startup_app_launcher_.reset(new StartupAppLauncher(profile_, app_id_));
   startup_app_launcher_->AddObserver(this);
   startup_app_launcher_->Start();
@@ -153,5 +153,4 @@
   Cleanup();
 }
 
-
 }   // namespace chromeos
diff --git a/chrome/browser/chromeos/login/auth_attempt_state.cc b/chrome/browser/chromeos/login/auth_attempt_state.cc
index 9ab86bd..dacf5de 100644
--- a/chrome/browser/chromeos/login/auth_attempt_state.cc
+++ b/chrome/browser/chromeos/login/auth_attempt_state.cc
@@ -33,7 +33,8 @@
       cryptohome_complete_(false),
       cryptohome_outcome_(false),
       cryptohome_code_(cryptohome::MOUNT_ERROR_NONE),
-      username_hash_obtained_(true) {
+      username_hash_obtained_(true),
+      username_hash_valid_(true) {
 }
 
 AuthAttemptState::AuthAttemptState(const std::string& username,
@@ -99,6 +100,13 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   user_context.username_hash = username_hash;
   username_hash_obtained_ = true;
+  username_hash_valid_ = true;
+}
+
+void AuthAttemptState::RecordUsernameHashFailed() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  username_hash_obtained_ = true;
+  username_hash_valid_ = false;
 }
 
 void AuthAttemptState::UsernameHashRequested() {
@@ -153,4 +161,9 @@
   return username_hash_obtained_;
 }
 
+bool AuthAttemptState::username_hash_valid() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  return username_hash_obtained_;
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/auth_attempt_state.h b/chrome/browser/chromeos/login/auth_attempt_state.h
index 54ffc31..489fbdc 100644
--- a/chrome/browser/chromeos/login/auth_attempt_state.h
+++ b/chrome/browser/chromeos/login/auth_attempt_state.h
@@ -16,7 +16,7 @@
 namespace chromeos {
 
 // Tracks the state associated with a single attempt to log in to chromium os.
-// Enforces that methods are only called on the IO thread.
+// Enforces that methods are only called on the UI thread.
 
 class AuthAttemptState {
  public:
@@ -39,16 +39,19 @@
   virtual ~AuthAttemptState();
 
   // Copy |user_context| and copy |outcome| into this object, so we can have
-  // a copy we're sure to own, and can make available on the IO thread.
-  // Must be called from the IO thread.
+  // a copy we're sure to own, and can make available on the UI thread.
+  // Must be called from the UI thread.
   void RecordOnlineLoginStatus(
       const LoginFailure& outcome);
 
   // Copy |username_hash| into this object, so we can have
-  // a copy we're sure to own, and can make available on the IO thread.
-  // Must be called from the IO thread.
+  // a copy we're sure to own, and can make available on the UI thread.
+  // Must be called from the UI thread.
   void RecordUsernameHash(const std::string& username_hash);
 
+  // Marks that the username hash request attempt has failed.
+  void RecordUsernameHashFailed();
+
   // Marks username hash as being requested so that flow will block till both
   // requests (Mount/GetUsernameHash) are completed.
   void UsernameHashRequested();
@@ -58,12 +61,12 @@
 
   // Copy |cryptohome_code| and |cryptohome_outcome| into this object,
   // so we can have a copy we're sure to own, and can make available
-  // on the IO thread.  Must be called from the IO thread.
+  // on the UI thread.  Must be called from the UI thread.
   void RecordCryptohomeStatus(bool cryptohome_outcome,
                               cryptohome::MountError cryptohome_code);
 
   // Blow away locally stored cryptohome login status.
-  // Must be called from the IO thread.
+  // Must be called from the UI thread.
   void ResetCryptohomeStatus();
 
   virtual bool online_complete();
@@ -76,6 +79,7 @@
   virtual cryptohome::MountError cryptohome_code();
 
   virtual bool username_hash_obtained();
+  virtual bool username_hash_valid();
 
   // Saved so we can retry client login, and also so we know for whom login
   // has succeeded, in the event of successful completion.
@@ -112,6 +116,10 @@
   // that would explicitly request username hash would have to reset this.
   bool username_hash_obtained_;
 
+  // After the username hash request is completed, this marks whether
+  // the request was successful.
+  bool username_hash_valid_;
+
   DISALLOW_COPY_AND_ASSIGN(AuthAttemptState);
 };
 
diff --git a/chrome/browser/chromeos/login/authenticator.h b/chrome/browser/chromeos/login/authenticator.h
index 7fce86e..b20a35b 100644
--- a/chrome/browser/chromeos/login/authenticator.h
+++ b/chrome/browser/chromeos/login/authenticator.h
@@ -58,6 +58,10 @@
   // Initiates login into the public account identified by |username|.
   virtual void LoginAsPublicAccount(const std::string& username) = 0;
 
+  // Initiates login into kiosk mode account identified by |app_user_id|.
+  // The |app_user_id| is a generated username for the account.
+  virtual void LoginAsKioskAccount(const std::string& app_user_id) = 0;
+
   // Completes retail mode login.
   virtual void OnRetailModeLoginSuccess() = 0;
 
diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc
index 3de6a5a..9d6b395 100644
--- a/chrome/browser/chromeos/login/chrome_restart_request.cc
+++ b/chrome/browser/chromeos/login/chrome_restart_request.cc
@@ -157,8 +157,10 @@
       cc::switches::kCompositeToMailbox,
       cc::switches::kDisableCompositedAntialiasing,
       cc::switches::kDisableImplSidePainting,
+      cc::switches::kDisableMapImage,
       cc::switches::kDisableThreadedAnimation,
       cc::switches::kEnableImplSidePainting,
+      cc::switches::kEnableMapImage,
       cc::switches::kEnablePartialSwap,
       cc::switches::kEnablePerTilePainting,
       cc::switches::kEnablePinchVirtualViewport,
@@ -180,7 +182,6 @@
       cc::switches::kTraceOverdraw,
       cc::switches::kUIDisablePartialSwap,
       cc::switches::kUIEnablePerTilePainting,
-      cc::switches::kUseMapImage,
       chromeos::switches::kDbusStub,
       chromeos::switches::kDisableLoginAnimations,
       chromeos::switches::kDisableOobeAnimation,
diff --git a/chrome/browser/chromeos/login/default_pinned_apps_field_trial_unittest.cc b/chrome/browser/chromeos/login/default_pinned_apps_field_trial_unittest.cc
index 1406ca7..0fc5566 100644
--- a/chrome/browser/chromeos/login/default_pinned_apps_field_trial_unittest.cc
+++ b/chrome/browser/chromeos/login/default_pinned_apps_field_trial_unittest.cc
@@ -7,8 +7,8 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/metrics/field_trial.h"
 #include "base/prefs/testing_pref_service.h"
-#include "chrome/common/metrics/entropy_provider.h"
 #include "chrome/test/base/testing_browser_process.h"
+#include "components/variations/entropy_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
diff --git a/chrome/browser/chromeos/login/default_user_images.cc b/chrome/browser/chromeos/login/default_user_images.cc
index 6d325e7..d99429a 100644
--- a/chrome/browser/chromeos/login/default_user_images.cc
+++ b/chrome/browser/chromeos/login/default_user_images.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/chromeos/login/default_user_images.h"
 
 #include "base/basictypes.h"
+#include "base/chromeos/chromeos_version.h"
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
@@ -73,7 +74,7 @@
 // image if its valid.
 std::string GetDefaultImageString(int index, const std::string& prefix) {
   if (index < 0 || index >= kDefaultImagesCount) {
-    NOTREACHED();
+    DCHECK(!base::chromeos::IsRunningOnChromeOS());
     return std::string();
   }
   return base::StringPrintf("%s%d", prefix.c_str(), index);
diff --git a/chrome/browser/chromeos/login/fake_login_utils.cc b/chrome/browser/chromeos/login/fake_login_utils.cc
index f0e68c3..bee416c 100644
--- a/chrome/browser/chromeos/login/fake_login_utils.cc
+++ b/chrome/browser/chromeos/login/fake_login_utils.cc
@@ -7,6 +7,7 @@
 #include "base/command_line.h"
 #include "base/path_service.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/login/login_display_host.h"
 #include "chrome/browser/chromeos/login/mock_authenticator.h"
 #include "chrome/browser/first_run/first_run.h"
@@ -15,6 +16,7 @@
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/notification_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
@@ -25,6 +27,11 @@
 
 void FakeLoginUtils::DoBrowserLaunch(Profile* profile,
                                      LoginDisplayHost* login_host) {
+
+  if (!UserManager::Get()->GetCurrentUserFlow()->ShouldLaunchBrowser()) {
+      UserManager::Get()->GetCurrentUserFlow()->LaunchExtraSteps(profile);
+      return;
+  }
   login_host->BeforeSessionStart();
   if (should_launch_browser_) {
     StartupBrowserCreator browser_creator;
@@ -60,6 +67,10 @@
     g_browser_process->profile_manager()->
         RegisterTestingProfile(profile, false, false);
   }
+  content::NotificationService::current()->Notify(
+      chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
+      content::NotificationService::AllSources(),
+      content::Details<Profile>(profile));
   if (delegate)
     delegate->OnProfilePrepared(profile);
 }
diff --git a/chrome/browser/chromeos/login/login_display_host_impl.cc b/chrome/browser/chromeos/login/login_display_host_impl.cc
index 8da65b4..8001931 100644
--- a/chrome/browser/chromeos/login/login_display_host_impl.cc
+++ b/chrome/browser/chromeos/login/login_display_host_impl.cc
@@ -307,14 +307,18 @@
 
 void LoginDisplayHostImpl::Finalize() {
   DVLOG(1) << "Session starting";
-  ash::Shell::GetInstance()->
-      desktop_background_controller()->MoveDesktopToUnlockedContainer();
+  if (ash::Shell::HasInstance()) {
+    ash::Shell::GetInstance()->
+        desktop_background_controller()->MoveDesktopToUnlockedContainer();
+  }
   if (wizard_controller_.get())
     wizard_controller_->OnSessionStart();
   if (!IsRunningUserAdding()) {
     // Display host is deleted once animation is completed
     // since sign in screen widget has to stay alive.
-    StartAnimation();
+    if (ash::Shell::HasInstance()) {
+      StartAnimation();
+    }
   }
   ShutdownDisplayHost(false);
 }
@@ -857,18 +861,9 @@
   // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty
   // and US dvorak keyboard layouts.
   if (g_browser_process && g_browser_process->local_state()) {
-    const std::string locale = g_browser_process->GetApplicationLocale();
-    // If the preferred keyboard for the login screen has been saved, use it.
-    PrefService* prefs = g_browser_process->local_state();
-    std::string initial_input_method_id =
-        prefs->GetString(chromeos::language_prefs::kPreferredKeyboardLayout);
-    if (initial_input_method_id.empty()) {
-      // If kPreferredKeyboardLayout is not specified, use the hardware layout.
-      initial_input_method_id =
-          manager->GetInputMethodUtil()->GetHardwareInputMethodId();
-    }
-    manager->EnableLayouts(locale, initial_input_method_id);
+    manager->SetInputMethodDefault();
 
+    PrefService* prefs = g_browser_process->local_state();
     // Apply owner preferences for tap-to-click and mouse buttons swap for
     // login screen.
     system::mouse_settings::SetPrimaryButtonRight(
diff --git a/chrome/browser/chromeos/login/login_manager_test.cc b/chrome/browser/chromeos/login/login_manager_test.cc
index 6196c83..4f1a149 100644
--- a/chrome/browser/chromeos/login/login_manager_test.cc
+++ b/chrome/browser/chromeos/login/login_manager_test.cc
@@ -7,25 +7,37 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/login/existing_user_controller.h"
+#include "chrome/browser/chromeos/login/login_display_host_impl.h"
 #include "chrome/browser/chromeos/login/user_manager.h"
+#include "chrome/browser/chromeos/login/webui_login_view.h"
 #include "chrome/browser/prefs/scoped_user_pref_update.h"
 #include "chrome/common/chrome_switches.h"
 #include "chromeos/chromeos_switches.h"
 #include "content/public/browser/notification_service.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
 
 LoginManagerTest::LoginManagerTest(bool should_launch_browser)
-    : should_launch_browser_(should_launch_browser) {
+    : should_launch_browser_(should_launch_browser),
+      web_contents_(NULL) {
   set_exit_when_last_browser_closes(false);
 }
 
+void LoginManagerTest::CleanUpOnMainThread() {
+  if (LoginDisplayHostImpl::default_host())
+    LoginDisplayHostImpl::default_host()->Finalize();
+  base::MessageLoop::current()->RunUntilIdle();
+}
+
 void LoginManagerTest::SetUpCommandLine(CommandLine* command_line) {
   command_line->AppendSwitch(chromeos::switches::kLoginManager);
   command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
   command_line->AppendSwitch(::switches::kMultiProfiles);
+  InProcessBrowserTest::SetUpCommandLine(command_line);
 }
 
 void LoginManagerTest::SetUpInProcessBrowserTestFixture() {
@@ -36,6 +48,13 @@
   LoginUtils::Set(mock_login_utils_);
 }
 
+void LoginManagerTest::SetUpOnMainThread() {
+  content::WindowedNotificationObserver(
+      chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
+      content::NotificationService::AllSources()).Wait();
+  InitializeWebContents();
+}
+
 void LoginManagerTest::RegisterUser(const std::string& username) {
   ListPrefUpdate users_pref(g_browser_process->local_state(), "LoggedInUsers");
   users_pref->AppendIfNotPresent(new base::StringValue(username));
@@ -65,4 +84,24 @@
   EXPECT_TRUE(TryToLogin(username, "password"));
 }
 
+void LoginManagerTest::JSExpect(const std::string& expression) {
+  bool result;
+  EXPECT_TRUE(web_contents_ != NULL);
+  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+      web_contents(),
+      "window.domAutomationController.send(!!(" + expression + "));",
+      &result));
+  ASSERT_TRUE(result) << expression;
+}
+
+void LoginManagerTest::InitializeWebContents() {
+    LoginDisplayHost* host = LoginDisplayHostImpl::default_host();
+    EXPECT_TRUE(host != NULL);
+
+    content::WebContents* web_contents =
+        host->GetWebUILoginView()->GetWebContents();
+    EXPECT_TRUE(web_contents != NULL);
+    set_web_contents(web_contents);
+  }
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/login_manager_test.h b/chrome/browser/chromeos/login/login_manager_test.h
index e88634f..c393e53 100644
--- a/chrome/browser/chromeos/login/login_manager_test.h
+++ b/chrome/browser/chromeos/login/login_manager_test.h
@@ -8,15 +8,27 @@
 #include "chrome/browser/chromeos/login/mock_login_utils.h"
 #include "chrome/test/base/in_process_browser_test.h"
 
+namespace content {
+class WebContents;
+}  // namespace content
+
 namespace chromeos {
 
+// Base class for Chrome OS out-of-box/login WebUI tests.
+// If no special configuration is done launches out-of-box WebUI.
+// To launch login UI use PRE_* test that will register user(s) and mark
+// out-of-box as completed.
+// Guarantees that WebUI has been initialized by waiting for
+// NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE notification.
 class LoginManagerTest : public InProcessBrowserTest {
  public:
   explicit LoginManagerTest(bool should_launch_browser);
 
   // Overriden from InProcessBrowserTest.
+  virtual void CleanUpOnMainThread() OVERRIDE;
   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE;
   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE;
+  virtual void SetUpOnMainThread() OVERRIDE;
 
   // Registers user with given |username| on device.
   // Should be called in PRE_* test.
@@ -34,11 +46,24 @@
   // Login user with |username|. User should be registered using RegisterUser().
   void LoginUser(const std::string& username);
 
+  // Executes given JS |expression| in |web_contents_| and checks
+  // that it is true.
+  void JSExpect(const std::string& expression);
+
   MockLoginUtils& login_utils() { return *mock_login_utils_; }
 
+  content::WebContents* web_contents() { return web_contents_; }
+
  private:
+  void InitializeWebContents();
+
+  void set_web_contents(content::WebContents* web_contents) {
+    web_contents_ = web_contents;
+  }
+
   MockLoginUtils* mock_login_utils_;
   bool should_launch_browser_;
+  content::WebContents* web_contents_;
 
   DISALLOW_COPY_AND_ASSIGN(LoginManagerTest);
 };
diff --git a/chrome/browser/chromeos/login/login_performer.cc b/chrome/browser/chromeos/login/login_performer.cc
index 2819ee6..c50b962 100644
--- a/chrome/browser/chromeos/login/login_performer.cc
+++ b/chrome/browser/chromeos/login/login_performer.cc
@@ -273,6 +273,14 @@
                  username));
 }
 
+void LoginPerformer::LoginAsKioskAccount(const std::string& app_user_id) {
+  authenticator_ = LoginUtils::Get()->CreateAuthenticator(this);
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(&Authenticator::LoginAsKioskAccount, authenticator_.get(),
+                 app_user_id));
+}
+
 void LoginPerformer::RecoverEncryptedData(const std::string& old_password) {
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
diff --git a/chrome/browser/chromeos/login/login_performer.h b/chrome/browser/chromeos/login/login_performer.h
index 72411ed..d96ebcf 100644
--- a/chrome/browser/chromeos/login/login_performer.h
+++ b/chrome/browser/chromeos/login/login_performer.h
@@ -79,6 +79,9 @@
   // Performs a login into the public account identified by |username|.
   void LoginAsPublicAccount(const std::string& username);
 
+  // Performs a login into the kiosk mode account with |app_user_id|.
+  void LoginAsKioskAccount(const std::string& app_user_id);
+
   // Migrates cryptohome using |old_password| specified.
   void RecoverEncryptedData(const std::string& old_password);
 
diff --git a/chrome/browser/chromeos/login/login_status_consumer.h b/chrome/browser/chromeos/login/login_status_consumer.h
index bdf32cc..9f1b14d 100644
--- a/chrome/browser/chromeos/login/login_status_consumer.h
+++ b/chrome/browser/chromeos/login/login_status_consumer.h
@@ -34,6 +34,7 @@
                             // only. It is never generated or seen by any of the
                             // other authenticator classes.
     TPM_ERROR,              // Critical TPM error encountered.
+    USERNAME_HASH_FAILED,   // Could not get username hash.
     NUM_FAILURE_REASONS,    // This has to be the last item.
   };
 
diff --git a/chrome/browser/chromeos/login/login_ui_browsertest.cc b/chrome/browser/chromeos/login/login_ui_browsertest.cc
new file mode 100644
index 0000000..9b3cabe
--- /dev/null
+++ b/chrome/browser/chromeos/login/login_ui_browsertest.cc
@@ -0,0 +1,48 @@
+// 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 "chrome/browser/chromeos/login/login_manager_test.h"
+#include "chrome/browser/chromeos/login/startup_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::Return;
+
+namespace chromeos {
+
+namespace {
+
+const char kTestUser1[] = "test-user1@gmail.com";
+const char kTestUser2[] = "test-user2@gmail.com";
+
+}  // anonymous namespace
+
+class LoginUITest : public chromeos::LoginManagerTest {
+ public:
+  LoginUITest() : LoginManagerTest(false) {}
+  virtual ~LoginUITest() {}
+};
+
+IN_PROC_BROWSER_TEST_F(LoginUITest, PRE_LoginUIVisible) {
+  RegisterUser(kTestUser1);
+  RegisterUser(kTestUser2);
+  StartupUtils::MarkOobeCompleted();
+}
+
+// Verifies basic login UI properties.
+IN_PROC_BROWSER_TEST_F(LoginUITest, LoginUIVisible) {
+  JSExpect("!!document.querySelector('#account-picker')");
+  JSExpect("!!document.querySelector('#pod-row')");
+  JSExpect(
+      "document.querySelectorAll('.pod:not(#user-pod-template)').length == 2");
+
+  JSExpect("document.querySelectorAll('.pod:not(#user-pod-template)')[0]"
+           ".user.emailAddress == '" + std::string(kTestUser1) + "'");
+  JSExpect("document.querySelectorAll('.pod:not(#user-pod-template)')[1]"
+           ".user.emailAddress == '" + std::string(kTestUser2) + "'");
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.cc b/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.cc
index 92a0c31..6d1f404 100644
--- a/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.cc
+++ b/chrome/browser/chromeos/login/managed/locally_managed_user_creation_controller.cc
@@ -21,6 +21,7 @@
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/session_manager_client.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/user_metrics.h"
 #include "crypto/random.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 
@@ -220,6 +221,10 @@
 void LocallyManagedUserCreationController::OnManagedUserFilesStored(
     bool success) {
   timeout_timer_.Stop();
+
+  content::RecordAction(
+      content::UserMetricsAction("ManagedMode_LocallyManagedUserCreated"));
+
   if (!success) {
     if (consumer_)
       consumer_->OnCreationError(TOKEN_WRITE_FAILED);
diff --git a/chrome/browser/chromeos/login/managed/supervised_user_creation_browsertest.cc b/chrome/browser/chromeos/login/managed/supervised_user_creation_browsertest.cc
new file mode 100644
index 0000000..3409d5f
--- /dev/null
+++ b/chrome/browser/chromeos/login/managed/supervised_user_creation_browsertest.cc
@@ -0,0 +1,246 @@
+// 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 <string>
+
+
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/chromeos/login/login_display_host_impl.h"
+#include "chrome/browser/chromeos/login/login_manager_test.h"
+#include "chrome/browser/chromeos/login/startup_utils.h"
+#include "chrome/browser/chromeos/login/webui_login_view.h"
+#include "chrome/browser/chromeos/net/network_portal_detector_stub.h"
+#include "chrome/browser/managed_mode/managed_user_registration_utility.h"
+#include "chrome/browser/managed_mode/managed_user_registration_utility_stub.h"
+#include "chrome/common/chrome_switches.h"
+#include "chromeos/cryptohome/mock_async_method_caller.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+
+using testing::_;
+
+namespace chromeos {
+
+namespace {
+
+const char kStubEthernetServicePath[] = "eth0";
+
+const char kTestManager[] = "test-manager@gmail.com";
+const char kTestOtherUser[] = "test-user@gmail.com";
+
+const char kTestManagerPassword[] = "password";
+const char kSupervisedUserDisplayName[] = "John Doe";
+const char kSupervisedUserPassword[] = "simplepassword";
+
+}  // namespace
+
+class SupervisedUserCreationTest : public chromeos::LoginManagerTest {
+ protected:
+  SupervisedUserCreationTest() : LoginManagerTest(true),
+                                 mock_async_method_caller_(NULL),
+                                 network_portal_detector_stub_(NULL),
+                                 registration_utility_stub_(NULL) {
+  }
+
+  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+    LoginManagerTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitch(::switches::kEnableManagedUsers);
+  }
+
+  virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
+    LoginManagerTest::SetUpInProcessBrowserTestFixture();
+    mock_async_method_caller_ = new cryptohome::MockAsyncMethodCaller;
+    mock_async_method_caller_->SetUp(true, cryptohome::MOUNT_ERROR_NONE);
+    cryptohome::AsyncMethodCaller::InitializeForTesting(
+        mock_async_method_caller_);
+
+    registration_utility_stub_ = new ManagedUserRegistrationUtilityStub();
+    scoped_utility_.reset(
+        new ScopedTestingManagedUserRegistrationUtility(
+            registration_utility_stub_));
+
+    // Setup network portal detector to return online state for both
+    // ethernet and wifi networks. Ethernet is an active network by
+    // default.
+    network_portal_detector_stub_ =
+        static_cast<NetworkPortalDetectorStub*>(
+            NetworkPortalDetector::GetInstance());
+    NetworkPortalDetector::CaptivePortalState online_state;
+    online_state.status = NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE;
+    online_state.response_code = 204;
+    network_portal_detector_stub_->SetDefaultNetworkPathForTesting(
+        kStubEthernetServicePath);
+    network_portal_detector_stub_->SetDetectionResultsForTesting(
+        kStubEthernetServicePath, online_state);
+  }
+
+  virtual void CleanUpOnMainThread() OVERRIDE {
+    LoginManagerTest::CleanUpOnMainThread();
+    scoped_utility_.reset(NULL);
+  }
+
+  virtual void TearDown() OVERRIDE {
+    cryptohome::AsyncMethodCaller::Shutdown();
+    mock_async_method_caller_ = NULL;
+    LoginManagerTest::TearDown();
+  }
+
+  void JSEval(const std::string& script) {
+    EXPECT_TRUE(content::ExecuteScript(web_contents(), script));
+  }
+
+  void JSExpectAsync(const std::string& function) {
+    bool result;
+    EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
+        web_contents(),
+        "(" + function + ")(function() {"
+        "  window.domAutomationController.send(true);"
+        "});",
+        &result));
+    EXPECT_TRUE(result);
+  }
+
+  void JSSetTextField(const std::string& element_selector,
+                      const std::string& value) {
+    std::string function;
+    function.append("document.querySelector('");
+    function.append(element_selector);
+    function.append("').value = '");
+    function.append(value);
+    function.append("'");
+    JSEval(function);
+  }
+
+ protected:
+   cryptohome::MockAsyncMethodCaller* mock_async_method_caller_;
+   NetworkPortalDetectorStub* network_portal_detector_stub_;
+   ManagedUserRegistrationUtilityStub* registration_utility_stub_;
+   scoped_ptr<ScopedTestingManagedUserRegistrationUtility> scoped_utility_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SupervisedUserCreationTest);
+};
+
+IN_PROC_BROWSER_TEST_F(SupervisedUserCreationTest,
+    PRE_PRE_CreateSupervisedUser) {
+  RegisterUser(kTestManager);
+  RegisterUser(kTestOtherUser);
+  chromeos::StartupUtils::MarkOobeCompleted();
+}
+
+IN_PROC_BROWSER_TEST_F(SupervisedUserCreationTest, PRE_CreateSupervisedUser) {
+  // Create supervised user.
+
+  // Navigate to supervised user creation screen.
+  JSEval("chrome.send('showLocallyManagedUserCreationScreen')");
+
+  // Read intro and proceed.
+  JSExpect("$('managed-user-creation').currentPage_ == 'intro'");
+
+  JSEval("$('managed-user-creation-start-button').click()");
+
+  // Check that both users appear as managers, and test-manager@gmail.com is
+  // the first one. As no manager is selected, 'next' button is disabled
+  JSExpect("$('managed-user-creation').currentPage_ == 'manager'");
+
+  JSExpect(std::string("document.querySelectorAll(")
+      .append("'#managed-user-creation-managers-pane .manager-pod.focused')")
+      .append(".length == 0"));
+  JSExpect("$('managed-user-creation-next-button').disabled");
+
+  JSExpect("$('managed-user-creation').managerList_.pods.length == 2");
+  JSExpect(std::string("document.querySelectorAll(")
+      .append("'#managed-user-creation-managers-pane .manager-pod')")
+      .append(".length == 2"));
+  JSExpect(std::string("document.querySelectorAll(")
+      .append("'#managed-user-creation-managers-pane .manager-pod')")
+      .append("[0].user.emailAddress == '").append(kTestManager).append("'"));
+  // Select the first user as manager, and enter password.
+  JSEval(std::string("supervisedUserManagerPod = ")
+      .append("$('managed-user-creation').managerList_.pods[0]"));
+  JSEval(std::string("$('managed-user-creation').managerList_")
+      .append(".selectPod(supervisedUserManagerPod)"));
+  JSExpect("$('managed-user-creation-next-button').disabled");
+  JSSetTextField(
+      "#managed-user-creation .manager-pod.focused input",
+      kTestManagerPassword);
+
+  JSEval("$('managed-user-creation').updateNextButtonForManager_()");
+
+  // Next button is now enabled.
+  JSExpect("!$('managed-user-creation-next-button').disabled");
+  SetExpectedCredentials(kTestManager, kTestManagerPassword);
+  content::WindowedNotificationObserver login_observer(
+      chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
+      content::NotificationService::AllSources());
+  // Log in as manager.
+  JSEval("$('managed-user-creation-next-button').click()");
+  login_observer.Wait();
+  // OAuth token is valid.
+  UserManager::Get()->
+      SaveUserOAuthStatus(kTestManager, User::OAUTH2_TOKEN_STATUS_VALID);
+  base::RunLoop().RunUntilIdle();
+  // Enter managed user details.
+  JSExpect("$('managed-user-creation').currentPage_ == 'username'");
+
+  JSExpect("$('managed-user-creation-next-button').disabled");
+  JSSetTextField(
+      "#managed-user-creation-name",
+      kSupervisedUserDisplayName);
+  JSEval("$('managed-user-creation').checkUserName_()");
+
+  base::RunLoop().RunUntilIdle();
+
+  JSSetTextField(
+      "#managed-user-creation-password",
+      kSupervisedUserPassword);
+  JSSetTextField(
+      "#managed-user-creation-password-confirm",
+      kSupervisedUserPassword);
+
+  JSEval("$('managed-user-creation').updateNextButtonForUser_()");
+  JSExpect("!$('managed-user-creation-next-button').disabled");
+
+  EXPECT_CALL(*mock_async_method_caller_, AsyncMount(_, _, _, _))
+      .Times(1);
+  EXPECT_CALL(*mock_async_method_caller_, AsyncGetSanitizedUsername(_, _))
+      .Times(1);
+  EXPECT_CALL(*mock_async_method_caller_, AsyncAddKey(_, _, _, _))
+      .Times(1);
+
+  JSEval("$('managed-user-creation-next-button').click()");
+
+  testing::Mock::VerifyAndClearExpectations(mock_async_method_caller_);
+
+  EXPECT_TRUE(registration_utility_stub_->register_was_called());
+  EXPECT_EQ(registration_utility_stub_->display_name(),
+            UTF8ToUTF16(kSupervisedUserDisplayName));
+
+  registration_utility_stub_->RunSuccessCallback("token");
+
+  // Token writing moves control to BlockingPool and back.
+  base::RunLoop().RunUntilIdle();
+  content::BrowserThread::GetBlockingPool()->FlushForTesting();
+  base::RunLoop().RunUntilIdle();
+
+  JSExpect("$('managed-user-creation').currentPage_ == 'created-1'");
+  JSEval("$('managed-user-creation-gotit-button').click()");
+}
+
+IN_PROC_BROWSER_TEST_F(SupervisedUserCreationTest, CreateSupervisedUser) {
+  // Log in as supervised user, make sure that everything works.
+  ASSERT_EQ(3UL, UserManager::Get()->GetUsers().size());
+  // Created supervised user have to be first in a list.
+  const User* user = UserManager::Get()->GetUsers().at(0);
+  ASSERT_EQ(UTF8ToUTF16(kSupervisedUserDisplayName), user->display_name());
+  LoginUser(user->email());
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/mock_authenticator.cc b/chrome/browser/chromeos/login/mock_authenticator.cc
index 47ffe15..da3b141 100644
--- a/chrome/browser/chromeos/login/mock_authenticator.cc
+++ b/chrome/browser/chromeos/login/mock_authenticator.cc
@@ -65,6 +65,16 @@
                             false);
 }
 
+void MockAuthenticator::LoginAsKioskAccount(
+    const std::string& app_user_id) {
+  consumer_->OnLoginSuccess(UserContext(expected_username_,
+                                        std::string(),
+                                        std::string(),
+                                        expected_username_),
+                            false,
+                            false);
+}
+
 void MockAuthenticator::LoginOffTheRecord() {
   consumer_->OnOffTheRecordLoginSuccess();
 }
diff --git a/chrome/browser/chromeos/login/mock_authenticator.h b/chrome/browser/chromeos/login/mock_authenticator.h
index aefe07c..e810bcf 100644
--- a/chrome/browser/chromeos/login/mock_authenticator.h
+++ b/chrome/browser/chromeos/login/mock_authenticator.h
@@ -38,6 +38,7 @@
       const UserContext& user_context) OVERRIDE;
   virtual void LoginRetailMode() OVERRIDE;
   virtual void LoginAsPublicAccount(const std::string& username) OVERRIDE;
+  virtual void LoginAsKioskAccount(const std::string& app_user_id) OVERRIDE;
   virtual void LoginOffTheRecord() OVERRIDE;
 
   virtual void OnRetailModeLoginSuccess() OVERRIDE;
diff --git a/chrome/browser/chromeos/login/oauth2_login_manager.h b/chrome/browser/chromeos/login/oauth2_login_manager.h
index 2d1397f..ea7e2c7 100644
--- a/chrome/browser/chromeos/login/oauth2_login_manager.h
+++ b/chrome/browser/chromeos/login/oauth2_login_manager.h
@@ -11,7 +11,7 @@
 #include "chrome/browser/chromeos/login/oauth2_login_verifier.h"
 #include "chrome/browser/chromeos/login/oauth2_token_fetcher.h"
 #include "chrome/browser/chromeos/login/oauth_login_manager.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 #include "net/url_request/url_request_context_getter.h"
 
 class GoogleServiceAuthError;
diff --git a/chrome/browser/chromeos/login/oauth2_login_verifier.h b/chrome/browser/chromeos/login/oauth2_login_verifier.h
index 917662b..cbfb85b 100644
--- a/chrome/browser/chromeos/login/oauth2_login_verifier.h
+++ b/chrome/browser/chromeos/login/oauth2_login_verifier.h
@@ -14,10 +14,10 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "google_apis/gaia/gaia_auth_consumer.h"
 #include "google_apis/gaia/gaia_auth_fetcher.h"
 #include "google_apis/gaia/oauth2_access_token_fetcher.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 #include "net/url_request/url_request_context_getter.h"
 
 namespace chromeos {
diff --git a/chrome/browser/chromeos/login/parallel_authenticator.cc b/chrome/browser/chromeos/login/parallel_authenticator.cc
index 44aef96..435049b 100644
--- a/chrome/browser/chromeos/login/parallel_authenticator.cc
+++ b/chrome/browser/chromeos/login/parallel_authenticator.cc
@@ -54,7 +54,10 @@
                         bool success,
                         const std::string& username_hash) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  attempt->RecordUsernameHash(username_hash);
+  if (success)
+    attempt->RecordUsernameHash(username_hash);
+  else
+    attempt->RecordUsernameHashFailed();
   resolver->Resolve();
 }
 
@@ -105,6 +108,25 @@
                  resolver));
 }
 
+// Calls cryptohome's MountPublic method
+void MountPublic(AuthAttemptState* attempt,
+                 scoped_refptr<ParallelAuthenticator> resolver,
+                 int flags) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountPublic(
+      attempt->user_context.username,
+      flags,
+      base::Bind(&TriggerResolveWithLoginTimeMarker,
+                 "CryptohomeMountPublic-End",
+                 attempt,
+                 resolver));
+  cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername(
+      attempt->user_context.username,
+      base::Bind(&TriggerResolveHash,
+                 attempt,
+                 resolver));
+}
+
 // Calls cryptohome's key migration method.
 void Migrate(AuthAttemptState* attempt,
              scoped_refptr<ParallelAuthenticator> resolver,
@@ -185,12 +207,15 @@
     : Authenticator(consumer),
       migrate_attempted_(false),
       remove_attempted_(false),
+      resync_attempted_(false),
       ephemeral_mount_attempted_(false),
       check_key_attempted_(false),
       already_reported_success_(false),
       owner_is_verified_(false),
       user_can_login_(false),
-      using_oauth_(true) {
+      using_oauth_(true),
+      remove_user_data_on_failure_(false),
+      delayed_login_failure_(NULL) {
 }
 
 void ParallelAuthenticator::AuthenticateToLogin(
@@ -349,6 +374,24 @@
         cryptohome::CREATE_IF_MISSING | cryptohome::ENSURE_EPHEMERAL);
 }
 
+void ParallelAuthenticator::LoginAsKioskAccount(
+    const std::string& app_user_id) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  current_state_.reset(new AuthAttemptState(
+      UserContext(app_user_id,
+                  std::string(),  // password
+                  std::string()),  // auth_code
+      std::string(),  // ascii_hash
+      std::string(),  // login_token
+      std::string(),  // login_captcha
+      User::USER_TYPE_KIOSK_APP,
+      false));
+  remove_user_data_on_failure_ = true;
+  MountPublic(current_state_.get(),
+        scoped_refptr<ParallelAuthenticator>(this),
+        cryptohome::CREATE_IF_MISSING);
+}
+
 void ParallelAuthenticator::OnRetailModeLoginSuccess() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   VLOG(1) << "Retail mode login success";
@@ -401,6 +444,15 @@
 
 void ParallelAuthenticator::OnLoginFailure(const LoginFailure& error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  // OnLoginFailure will be called again with the same |error|
+  // after the cryptohome has been removed.
+  if (remove_user_data_on_failure_) {
+    delayed_login_failure_ = &error;
+    RemoveEncryptedData();
+    return;
+  }
+
   // Send notification of failure
   AuthenticationNotificationDetails details(false);
   content::NotificationService::current()->Notify(
@@ -426,7 +478,7 @@
                  old_hash));
 }
 
-void ParallelAuthenticator::ResyncEncryptedData() {
+void ParallelAuthenticator::RemoveEncryptedData() {
   remove_attempted_ = true;
   current_state_->ResetCryptohomeStatus();
   BrowserThread::PostTask(
@@ -436,6 +488,16 @@
                  scoped_refptr<ParallelAuthenticator>(this)));
 }
 
+void ParallelAuthenticator::ResyncEncryptedData() {
+  resync_attempted_ = true;
+  current_state_->ResetCryptohomeStatus();
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(&Remove,
+                 current_state_.get(),
+                 scoped_refptr<ParallelAuthenticator>(this)));
+}
+
 bool ParallelAuthenticator::VerifyOwner() {
   if (owner_is_verified_)
     return true;
@@ -490,6 +552,7 @@
     case FAILED_REMOVE:
       // In this case, we tried to remove the user's old cryptohome at her
       // request, and the remove failed.
+      remove_user_data_on_failure_ = false;
       BrowserThread::PostTask(
           BrowserThread::UI, FROM_HERE,
           base::Bind(&ParallelAuthenticator::OnLoginFailure, this,
@@ -511,6 +574,21 @@
           base::Bind(&ParallelAuthenticator::OnLoginFailure, this,
                      LoginFailure(LoginFailure::TPM_ERROR)));
       break;
+    case FAILED_USERNAME_HASH:
+      // In this case, we failed the GetSanitizedUsername request to
+      // cryptohomed. This can happen for any login attempt.
+      BrowserThread::PostTask(
+          BrowserThread::UI, FROM_HERE,
+          base::Bind(&ParallelAuthenticator::OnLoginFailure, this,
+                     LoginFailure(LoginFailure::USERNAME_HASH_FAILED)));
+      break;
+    case REMOVED_DATA_AFTER_FAILURE:
+      remove_user_data_on_failure_ = false;
+      BrowserThread::PostTask(
+          BrowserThread::UI, FROM_HERE,
+          base::Bind(&ParallelAuthenticator::OnLoginFailure, this,
+                     *delayed_login_failure_));
+      break;
     case CREATE_NEW:
       mount_flags |= cryptohome::CREATE_IF_MISSING;
     case RECOVER_MOUNT:
@@ -565,6 +643,7 @@
           BrowserThread::UI, FROM_HERE,
           base::Bind(&ParallelAuthenticator::OnOffTheRecordLoginSuccess, this));
       break;
+    case KIOSK_ACCOUNT_LOGIN:
     case PUBLIC_ACCOUNT_LOGIN:
       using_oauth_ = false;
       BrowserThread::PostTask(
@@ -625,14 +704,17 @@
 
   AuthState state = CONTINUE;
 
-  if (current_state_->cryptohome_outcome())
+  if (current_state_->cryptohome_outcome() &&
+      current_state_->username_hash_valid()) {
     state = ResolveCryptohomeSuccessState();
-  else
+  } else {
     state = ResolveCryptohomeFailureState();
+  }
 
   DCHECK(current_state_->cryptohome_complete());  // Ensure invariant holds.
   migrate_attempted_ = false;
   remove_attempted_ = false;
+  resync_attempted_ = false;
   ephemeral_mount_attempted_ = false;
   check_key_attempted_ = false;
 
@@ -655,7 +737,7 @@
 ParallelAuthenticator::AuthState
 ParallelAuthenticator::ResolveCryptohomeFailureState() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  if (remove_attempted_)
+  if (remove_attempted_ || resync_attempted_)
     return FAILED_REMOVE;
   if (ephemeral_mount_attempted_)
     return FAILED_TMPFS;
@@ -691,14 +773,19 @@
     }
   }
 
+  if (!current_state_->username_hash_valid())
+    return FAILED_USERNAME_HASH;
+
   return FAILED_MOUNT;
 }
 
 ParallelAuthenticator::AuthState
 ParallelAuthenticator::ResolveCryptohomeSuccessState() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  if (remove_attempted_)
+  if (resync_attempted_)
     return CREATE_NEW;
+  if (remove_attempted_)
+    return REMOVED_DATA_AFTER_FAILURE;
   if (migrate_attempted_)
     return RECOVER_MOUNT;
   if (check_key_attempted_)
@@ -710,6 +797,8 @@
     return DEMO_LOGIN;
   if (current_state_->user_type == User::USER_TYPE_PUBLIC_ACCOUNT)
     return PUBLIC_ACCOUNT_LOGIN;
+  if (current_state_->user_type == User::USER_TYPE_KIOSK_APP)
+    return KIOSK_ACCOUNT_LOGIN;
   if (current_state_->user_type == User::USER_TYPE_LOCALLY_MANAGED)
     return LOCALLY_MANAGED_USER_LOGIN;
 
diff --git a/chrome/browser/chromeos/login/parallel_authenticator.h b/chrome/browser/chromeos/login/parallel_authenticator.h
index ff4064a..972c8f9 100644
--- a/chrome/browser/chromeos/login/parallel_authenticator.h
+++ b/chrome/browser/chromeos/login/parallel_authenticator.h
@@ -83,7 +83,11 @@
     PUBLIC_ACCOUNT_LOGIN = 18,        // Logged into a public account.
     LOCALLY_MANAGED_USER_LOGIN = 19,  // Logged in as a locally managed user.
     LOGIN_FAILED = 20,       // Login denied.
-    OWNER_REQUIRED = 21      // Login is restricted to the owner only.
+    OWNER_REQUIRED = 21,     // Login is restricted to the owner only.
+    FAILED_USERNAME_HASH = 22,        // Failed GetSanitizedUsername request.
+    KIOSK_ACCOUNT_LOGIN = 23,         // Logged into a kiosk account.
+    REMOVED_DATA_AFTER_FAILURE = 24,  // Successfully removed the user's
+                                      // cryptohome after a login failure.
   };
 
   explicit ParallelAuthenticator(LoginStatusConsumer* consumer);
@@ -129,6 +133,11 @@
   // success/failure.
   virtual void LoginAsPublicAccount(const std::string& username) OVERRIDE;
 
+  // Initiates login into the kiosk mode account identified by |app_user_id|.
+  // Mounts an public but non-ephemeral cryptohome and notifies consumer on the
+  // success/failure.
+  virtual void LoginAsKioskAccount(const std::string& app_user_id) OVERRIDE;
+
   // These methods must be called on the UI thread, as they make DBus calls
   // and also call back to the login UI.
   virtual void OnRetailModeLoginSuccess() OVERRIDE;
@@ -161,6 +170,9 @@
   FRIEND_TEST_ALL_PREFIXES(ParallelAuthenticatorTest,
                            ResolveOwnerNeededFailedMount);
 
+  // Removes the cryptohome of the user.
+  void RemoveEncryptedData();
+
   // Returns the AuthState we're in, given the status info we have at
   // the time of call.
   // Must be called on the IO thread.
@@ -219,6 +231,7 @@
   scoped_ptr<OnlineAttempt> current_online_;
   bool migrate_attempted_;
   bool remove_attempted_;
+  bool resync_attempted_;
   bool ephemeral_mount_attempted_;
   bool check_key_attempted_;
 
@@ -237,6 +250,13 @@
   // True if we use OAuth-based authentication flow.
   bool using_oauth_;
 
+  // Flag indicating to delete the user's cryptohome the login fails.
+  bool remove_user_data_on_failure_;
+
+  // When |remove_user_data_on_failure_| is set, we delay calling
+  // consumer_->OnLoginFailure() until we removed the user cryptohome.
+  const LoginFailure* delayed_login_failure_;
+
   DISALLOW_COPY_AND_ASSIGN(ParallelAuthenticator);
 };
 
diff --git a/chrome/browser/chromeos/login/user_manager_impl.cc b/chrome/browser/chromeos/login/user_manager_impl.cc
index 66a448f..a9db655 100644
--- a/chrome/browser/chromeos/login/user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/user_manager_impl.cc
@@ -46,7 +46,6 @@
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/cryptohome/async_method_caller.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/ime/input_method_manager.h"
 #include "chromeos/login/login_state.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
@@ -790,7 +789,7 @@
 }
 
 void UserManagerImpl::OnStateChanged() {
-  DCHECK(IsLoggedInAsRegularUser());
+  DCHECK(IsLoggedInAsRegularUser() || IsLoggedInAsLocallyManagedUser());
   GoogleServiceAuthError::State state =
       observed_sync_service_->GetAuthError().state();
   if (state != GoogleServiceAuthError::NONE &&
diff --git a/chrome/browser/chromeos/login/wallpaper_manager.cc b/chrome/browser/chromeos/login/wallpaper_manager.cc
index 09ebcc8..e34ebdc 100644
--- a/chrome/browser/chromeos/login/wallpaper_manager.cc
+++ b/chrome/browser/chromeos/login/wallpaper_manager.cc
@@ -1082,7 +1082,6 @@
   base::FilePath large_wallpaper_path =
       GetCustomWallpaperPath(kLargeWallpaperSubDir, email, file_name);
 
-  std::vector<unsigned char> image_data = wallpaper.raw_image();
   // Re-encode orginal file to jpeg format and saves the result in case that
   // resized wallpaper is not generated (i.e. chrome shutdown before resized
   // wallpaper is saved).
diff --git a/chrome/browser/chromeos/mobile/mobile_activator.cc b/chrome/browser/chromeos/mobile/mobile_activator.cc
index 3e1a82e..d0d3802 100644
--- a/chrome/browser/chromeos/mobile/mobile_activator.cc
+++ b/chrome/browser/chromeos/mobile/mobile_activator.cc
@@ -13,6 +13,7 @@
 #include "base/bind_helpers.h"
 #include "base/file_util.h"
 #include "base/json/json_reader.h"
+#include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/message_loop/message_loop.h"
@@ -25,9 +26,17 @@
 #include "base/timer/timer.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chromeos/cros/network_library.h"
 #include "chrome/common/pref_names.h"
+#include "chromeos/network/device_state.h"
+#include "chromeos/network/network_activation_handler.h"
+#include "chromeos/network/network_configuration_handler.h"
+#include "chromeos/network/network_connection_handler.h"
+#include "chromeos/network/network_event_log.h"
+#include "chromeos/network/network_handler_callbacks.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
 #include "content/public/browser/browser_thread.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
 
 using content::BrowserThread;
 
@@ -151,7 +160,11 @@
       state_(PLAN_ACTIVATION_PAGE_LOADING),
       reenable_cert_check_(false),
       terminated_(true),
+      pending_activation_request_(false),
       connection_retry_count_(0),
+      initial_OTASP_attempts_(0),
+      trying_OTASP_attempts_(0),
+      final_OTASP_attempts_(0),
       payment_reconnect_count_(0) {
 }
 
@@ -164,21 +177,18 @@
 }
 
 void MobileActivator::TerminateActivation() {
-  // We're exiting; don't continue with termination.
-  if (!NetworkLibrary::Get())
-    return;
-
   state_duration_timer_.Stop();
   continue_reconnect_timer_.Stop();
   reconnect_timeout_timer_.Stop();
 
-  NetworkLibrary* lib = GetNetworkLibrary();
-  lib->RemoveNetworkManagerObserver(this);
-  lib->RemoveObserverForAllNetworks(this);
+  if (NetworkHandler::IsInitialized())
+    NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
+                                                                   FROM_HERE);
   ReEnableCertRevocationChecking();
   meid_.clear();
   iccid_.clear();
   service_path_.clear();
+  device_path_.clear();
   state_ = PLAN_ACTIVATION_PAGE_LOADING;
   reenable_cert_check_ = false;
   terminated_ = true;
@@ -186,40 +196,36 @@
   cellular_config_ = new CellularConfigDocument();
 }
 
-void MobileActivator::OnNetworkManagerChanged(NetworkLibrary* cros) {
-  if (state_ == PLAN_ACTIVATION_PAGE_LOADING ||
-      state_ == PLAN_ACTIVATION_DONE ||
-      state_ == PLAN_ACTIVATION_ERROR) {
-    return;
-  }
-
-  CellularNetwork* network = FindMatchingCellularNetwork(true);
-  if (network && network->activate_over_non_cellular_network()) {
-    bool waiting = (state_ == PLAN_ACTIVATION_WAITING_FOR_CONNECTION);
-    bool is_online =
-        cros->connected_network() && cros->connected_network()->online();
-    if (waiting && is_online) {
-      ChangeState(network, post_reconnect_state_, "");
-    } else if (!waiting && !is_online) {
-      ChangeState(network, PLAN_ACTIVATION_WAITING_FOR_CONNECTION, "");
-    }
-  }
-
-  EvaluateCellularNetwork(network);
+void MobileActivator::DefaultNetworkChanged(const NetworkState* network) {
+  RefreshCellularNetworks();
 }
 
-void MobileActivator::OnNetworkChanged(NetworkLibrary* cros,
-                                       const Network* network) {
+void MobileActivator::NetworkPropertiesUpdated(const NetworkState* network) {
   if (state_ == PLAN_ACTIVATION_PAGE_LOADING)
     return;
 
-  if (!network || network->type() != TYPE_CELLULAR) {
-    NOTREACHED();
+  if (!network || network->type() != flimflam::kTypeCellular)
+    return;
+
+  const DeviceState* device = NetworkHandler::Get()->network_state_handler()->
+      GetDeviceState(network->device_path());
+  if (!device) {
+    LOG(ERROR) << "Cellular device can't be found: " << network->device_path();
+    return;
+  }
+  if (network->device_path() != device_path_) {
+    LOG(WARNING) << "Ignoring property update for cellular service "
+                 << network->path()
+                 << " on unknown device " << network->device_path()
+                 << " (Stored device path = " << device_path_ << ")";
     return;
   }
 
-  EvaluateCellularNetwork(
-      static_cast<CellularNetwork*>(const_cast<Network*>(network)));
+  // A modem reset leads to a new service path. Since we have verified that we
+  // are a cellular service on a still valid stored device path, update it.
+  service_path_ = network->path();
+
+  EvaluateCellularNetwork(network);
 }
 
 void MobileActivator::AddObserver(MobileActivator::Observer* observer) {
@@ -234,15 +240,14 @@
 
 void MobileActivator::InitiateActivation(const std::string& service_path) {
   DCHECK(content::BrowserThread::CurrentlyOn(BrowserThread::UI));
-  NetworkLibrary* lib = GetNetworkLibrary();
-  CellularNetwork* network = lib->FindCellularNetworkByPath(service_path);
+  const NetworkState* network =  GetNetworkState(service_path);
   if (!network) {
     LOG(ERROR) << "Cellular service can't be found: " << service_path;
     return;
   }
 
-  const chromeos::NetworkDevice* device =
-      lib->FindNetworkDeviceByPath(network->device_path());
+  const DeviceState* device = NetworkHandler::Get()->network_state_handler()->
+      GetDeviceState(network->device_path());
   if (!device) {
     LOG(ERROR) << "Cellular device can't be found: " << network->device_path();
     return;
@@ -252,6 +257,7 @@
   meid_ = device->meid();
   iccid_ = device->iccid();
   service_path_ = service_path;
+  device_path_ = network->device_path();
 
   ChangeState(network, PLAN_ACTIVATION_PAGE_LOADING, "");
 
@@ -262,14 +268,21 @@
 }
 
 void MobileActivator::ContinueActivation() {
-  CellularNetwork* network = FindMatchingCellularNetwork(false);
-  if (!network || !network->SupportsActivation())
+  const NetworkState* network = GetNetworkState(service_path_);
+  if (!network ||
+      (network->payment_url().empty() && network->usage_url().empty()))
     return;
 
   DisableCertRevocationChecking();
-  // We want shill to connect us after activations.
-  network->SetAutoConnect(true);
 
+  // We want shill to connect us after activations, so enable autoconnect.
+  DictionaryValue auto_connect_property;
+  auto_connect_property.SetBoolean(flimflam::kAutoConnectProperty, true);
+  NetworkHandler::Get()->network_configuration_handler()->SetProperties(
+      network->path(),
+      auto_connect_property,
+      base::Bind(&base::DoNothing),
+      network_handler::ErrorCallback());
   StartActivation();
 }
 
@@ -283,13 +296,15 @@
   // The payment is received, try to reconnect and check the status all over
   // again.
   if (success && state_ == PLAN_ACTIVATION_SHOWING_PAYMENT) {
-    NetworkLibrary* lib = GetNetworkLibrary();
-    lib->SignalCellularPlanPayment();
+    SignalCellularPlanPayment();
     UMA_HISTOGRAM_COUNTS("Cellular.PaymentReceived", 1);
-    CellularNetwork* network = FindMatchingCellularNetwork(true);
-    if (network && network->activate_over_non_cellular_network()) {
+    const NetworkState* network = GetNetworkState(service_path_);
+    if (network && network->activate_over_non_cellular_networks()) {
       state_ = PLAN_ACTIVATION_DONE;
-      network->CompleteActivation();
+      NetworkHandler::Get()->network_activation_handler()->
+          CompleteActivation(network->path(),
+                             base::Bind(&base::DoNothing),
+                             network_handler::ErrorCallback());
     } else {
       StartOTASP();
     }
@@ -305,7 +320,7 @@
 }
 
 void MobileActivator::HandlePortalLoaded(bool success) {
-  CellularNetwork* network = FindMatchingCellularNetwork(true);
+  const NetworkState* network = GetNetworkState(service_path_);
   if (!network) {
     ChangeState(NULL, PLAN_ACTIVATION_ERROR,
                 GetErrorMessage(kErrorNoService));
@@ -319,7 +334,7 @@
     } else {
       // There is no point in forcing reconnecting the cellular network if the
       // activation should not be done over it.
-      if (network->activate_over_non_cellular_network())
+      if (network->activate_over_non_cellular_networks())
         return;
 
       payment_reconnect_count_++;
@@ -340,31 +355,8 @@
   }
 }
 
-CellularNetwork* MobileActivator::FindMatchingCellularNetwork(
-    bool reattach_observer) {
-  NetworkLibrary* lib = GetNetworkLibrary();
-  for (CellularNetworkVector::const_iterator it =
-           lib->cellular_networks().begin();
-       it != lib->cellular_networks().end(); ++it) {
-    const chromeos::NetworkDevice* device =
-        lib->FindNetworkDeviceByPath((*it)->device_path());
-    if (device && ((!meid_.empty() && meid_ == device->meid()) ||
-                   (!iccid_.empty() && iccid_ == device->iccid()))) {
-      CellularNetwork* network = *it;
-      // If service path has changed, reattach the event observer for this
-      // network service.
-      if (reattach_observer && service_path_ != network->service_path()) {
-        lib->RemoveObserverForAllNetworks(this);
-        lib->AddNetworkObserver(network->service_path(), this);
-        service_path_ = network->service_path();
-      }
-      return network;
-    }
-  }
-  return NULL;
-}
-
 void MobileActivator::StartOTASPTimer() {
+  pending_activation_request_ = false;
   state_duration_timer_.Start(
       FROM_HERE,
       base::TimeDelta::FromMilliseconds(kOTASPRetryDelay),
@@ -373,39 +365,44 @@
 
 void MobileActivator::StartActivation() {
   UMA_HISTOGRAM_COUNTS("Cellular.MobileSetupStart", 1);
-  NetworkLibrary* lib = GetNetworkLibrary();
-  CellularNetwork* network = FindMatchingCellularNetwork(true);
+  const NetworkState* network = GetNetworkState(service_path_);
   // Check if we can start activation process.
   if (!network) {
+    NetworkStateHandler::TechnologyState technology_state =
+        NetworkHandler::Get()->network_state_handler()->GetTechnologyState(
+            flimflam::kTypeCellular);
     std::string error;
-    if (!lib->cellular_available())
+    if (technology_state == NetworkStateHandler::TECHNOLOGY_UNAVAILABLE) {
       error = kErrorNoDevice;
-    else if (!lib->cellular_enabled())
+    } else if (technology_state != NetworkStateHandler::TECHNOLOGY_ENABLED) {
       error = kErrorDisabled;
-    else
+    } else {
       error = kErrorNoService;
+    }
     ChangeState(NULL, PLAN_ACTIVATION_ERROR, GetErrorMessage(error));
     return;
   }
 
   // Start monitoring network property changes.
-  lib->AddNetworkManagerObserver(this);
-  if (network->activate_over_non_cellular_network()) {
+  NetworkHandler::Get()->network_state_handler()->AddObserver(this, FROM_HERE);
+  if (network->activate_over_non_cellular_networks()) {
     // Fast forward to payment portal loading if the activation is performed
     // over a non-cellular network.
-    ChangeState(network,
-                (network->activation_state() == ACTIVATION_STATE_ACTIVATED) ?
-                PLAN_ACTIVATION_DONE :
-                PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING,
-                "");
+    ChangeState(
+        network,
+        (network->activation_state() == flimflam::kActivationStateActivated) ?
+        PLAN_ACTIVATION_DONE :
+        PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING,
+        "");
     // Verify that there is no need to wait for the connection. This will also
     // evaluate the network.
-    OnNetworkManagerChanged(lib);
+    RefreshCellularNetworks();
     return;
   }
 
-  if (lib->HasRecentCellularPlanPayment() &&
-      network->activation_state() == ACTIVATION_STATE_PARTIALLY_ACTIVATED) {
+  if (HasRecentCellularPlanPayment() &&
+      (network->activation_state() ==
+       flimflam::kActivationStatePartiallyActivated)) {
     // Try to start with OTASP immediately if we have received payment recently.
     state_ = PLAN_ACTIVATION_START_OTASP;
   } else {
@@ -421,14 +418,14 @@
 }
 
 void MobileActivator::StartOTASP() {
-  CellularNetwork* network = FindMatchingCellularNetwork(true);
+  const NetworkState* network = GetNetworkState(service_path_);
   ChangeState(network, PLAN_ACTIVATION_START_OTASP, std::string());
   EvaluateCellularNetwork(network);
 }
 
 void MobileActivator::HandleOTASPTimeout() {
   LOG(WARNING) << "OTASP seems to be taking too long.";
-  CellularNetwork* network = FindMatchingCellularNetwork(true);
+  const NetworkState* network = GetNetworkState(service_path_);
   // We're here because one of OTASP steps is taking too long to complete.
   // Usually, this means something bad has happened below us.
   if (state_ == PLAN_ACTIVATION_INITIATING_ACTIVATION) {
@@ -465,18 +462,21 @@
               GetErrorMessage(kErrorDefault));
 }
 
-void MobileActivator::ForceReconnect(CellularNetwork* network,
+void MobileActivator::ForceReconnect(const NetworkState* network,
                                      PlanActivationState next_state) {
   DCHECK(network);
   // Store away our next destination for when we complete.
   post_reconnect_state_ = next_state;
   UMA_HISTOGRAM_COUNTS("Cellular.ActivationRetry", 1);
   // First, disconnect...
-  LOG(INFO) << "Disconnecting from " << network->service_path();
+  LOG(INFO) << "Disconnecting from " << network->path();
   // Explicit service Disconnect()s disable autoconnect on the service until
   // Connect() is called on the service again.  Hence this dance to explicitly
   // call Connect().
-  GetNetworkLibrary()->DisconnectFromNetwork(network);
+  NetworkHandler::Get()->network_connection_handler()->DisconnectNetwork(
+      network->path(),
+      base::Bind(&base::DoNothing),
+      network_handler::ErrorCallback());
   // Keep trying to connect until told otherwise.
   continue_reconnect_timer_.Stop();
   continue_reconnect_timer_.Start(
@@ -493,20 +493,24 @@
 
 void MobileActivator::ReconnectTimedOut() {
   LOG(ERROR) << "Ending activation attempt after failing to reconnect.";
-  ChangeState(FindMatchingCellularNetwork(true),
+  const NetworkState* network = GetNetworkState(service_path_);
+  ChangeState(network,
               PLAN_ACTIVATION_ERROR,
               GetErrorMessage(kFailedConnectivity));
 }
 
 void MobileActivator::ContinueConnecting() {
-  CellularNetwork* network = FindMatchingCellularNetwork(true);
-  if (network && network->connected()) {
-    if (network->restricted_pool() &&
-        network->error() == ERROR_DNS_LOOKUP_FAILED) {
+  const NetworkState* network = GetNetworkState(service_path_);
+  if (network && network->IsConnectedState()) {
+    if (network->connection_state() == flimflam::kStatePortal &&
+        network->error() == flimflam::kErrorDNSLookupFailed) {
       // It isn't an error to be in a restricted pool, but if DNS doesn't work,
       // then we're not getting traffic through at all.  Just disconnect and
       // try again.
-      GetNetworkLibrary()->DisconnectFromNetwork(network);
+      NetworkHandler::Get()->network_connection_handler()->DisconnectNetwork(
+          network->path(),
+          base::Bind(&base::DoNothing),
+          network_handler::ErrorCallback());
       return;
     }
     // Stop this callback
@@ -515,14 +519,43 @@
   } else {
     LOG(WARNING) << "Connect failed, will try again in a little bit.";
     if (network) {
-      LOG(INFO) << "Connecting to: " << network->service_path();
+      LOG(INFO) << "Connecting to: " << network->path();
       ash::network_connect::ConnectToNetwork(
-          network->service_path(), NULL /* no parent window */);
+          network->path(), NULL /* no parent window */);
     }
   }
 }
 
-void MobileActivator::EvaluateCellularNetwork(CellularNetwork* network) {
+void MobileActivator::RefreshCellularNetworks() {
+  if (state_ == PLAN_ACTIVATION_PAGE_LOADING ||
+      state_ == PLAN_ACTIVATION_DONE ||
+      state_ == PLAN_ACTIVATION_ERROR) {
+    return;
+  }
+
+  NetworkStateHandler* nsh = NetworkHandler::Get()->network_state_handler();
+  const NetworkState* network = GetNetworkState(service_path_);
+  if (network && network->activate_over_non_cellular_networks()) {
+    bool waiting = (state_ == PLAN_ACTIVATION_WAITING_FOR_CONNECTION);
+    bool is_online = nsh->DefaultNetwork() &&
+        nsh->DefaultNetwork()->connection_state() == flimflam::kStateOnline;
+    if (waiting && is_online) {
+      ChangeState(network, post_reconnect_state_, "");
+    } else if (!waiting && !is_online) {
+      ChangeState(network, PLAN_ACTIVATION_WAITING_FOR_CONNECTION, "");
+    }
+  }
+
+  EvaluateCellularNetwork(network);
+}
+
+const NetworkState* MobileActivator::GetNetworkState(
+    const std::string& service_path) {
+  return NetworkHandler::Get()->network_state_handler()->GetNetworkState(
+      service_path);
+}
+
+void MobileActivator::EvaluateCellularNetwork(const NetworkState* network) {
   if (terminated_) {
     LOG(ERROR) << "Tried to run MobileActivator state machine while "
                << "terminated.";
@@ -534,16 +567,16 @@
     return;
   }
 
-  LOG(WARNING) << "Cellular:\n  service=" << network->GetStateString()
-      << "\n  ui=" << GetStateDescription(state_)
-      << "\n  activation=" << network->GetActivationStateString()
-      << "\n  error=" << network->GetErrorString()
-      << "\n  setvice_path=" << network->service_path()
-      << "\n  connected=" << network->connected();
+  LOG(WARNING) << "Cellular:\n  service state=" << network->connection_state()
+               << "\n  ui=" << GetStateDescription(state_)
+               << "\n  activation=" << network->activation_state()
+               << "\n  error=" << network->error()
+               << "\n  setvice_path=" << network->path()
+               << "\n  connected=" << network->IsConnectedState();
 
   // If the network is activated over non cellular network, the activator state
   // does not depend on the network's own state.
-  if (network->activate_over_non_cellular_network())
+  if (network->activate_over_non_cellular_networks())
     return;
 
   std::string error_description;
@@ -553,9 +586,9 @@
 }
 
 MobileActivator::PlanActivationState MobileActivator::PickNextState(
-    CellularNetwork* network, std::string* error_description) const {
+    const NetworkState* network, std::string* error_description) const {
   PlanActivationState new_state = state_;
-  if (!network->connected())
+  if (!network->IsConnectedState())
     new_state = PickNextOfflineState(network);
   else
     new_state = PickNextOnlineState(network);
@@ -564,13 +597,13 @@
     // Check for this special case when we try to do activate partially
     // activated device. If that attempt failed, try to disconnect to clear the
     // state and reconnect again.
-    if ((network->activation_state() == ACTIVATION_STATE_PARTIALLY_ACTIVATED ||
-         network->activation_state() == ACTIVATION_STATE_ACTIVATING) &&
-        (network->error() == ERROR_NO_ERROR ||
-         network->error() == ERROR_OTASP_FAILED) &&
-        network->state() == STATE_ACTIVATION_FAILURE) {
-      LOG(WARNING) << "Activation failure detected "
-                   << network->service_path();
+    const std::string& activation = network->activation_state();
+    if ((activation == flimflam::kActivationStatePartiallyActivated ||
+         activation == flimflam::kActivationStateActivating) &&
+        (network->error().empty() ||
+         network->error() == flimflam::kErrorOtaspFailed) &&
+        network->connection_state() == flimflam::kStateActivationFailure) {
+      NET_LOG_EVENT("Activation failure detected ", network->path());
       switch (state_) {
         case PLAN_ACTIVATION_OTASP:
           new_state = PLAN_ACTIVATION_DELAY_OTASP;
@@ -592,8 +625,7 @@
           break;
       }
     } else {
-      LOG(WARNING) << "Unexpected activation failure for "
-                   << network->service_path();
+      LOG(WARNING) << "Unexpected activation failure for " << network->path();
       new_state = PLAN_ACTIVATION_ERROR;
     }
   }
@@ -604,70 +636,59 @@
 }
 
 MobileActivator::PlanActivationState MobileActivator::PickNextOfflineState(
-    CellularNetwork* network) const {
+    const NetworkState* network) const {
   PlanActivationState new_state = state_;
+  const std::string& activation = network->activation_state();
   switch (state_) {
     case PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING:
     case PLAN_ACTIVATION_SHOWING_PAYMENT:
-      if (!network->activate_over_non_cellular_network())
+      if (!network->activate_over_non_cellular_networks())
         new_state = PLAN_ACTIVATION_RECONNECTING;
       break;
     case PLAN_ACTIVATION_START:
-      switch (network->activation_state()) {
-        case ACTIVATION_STATE_ACTIVATED:
-          if (network->restricted_pool())
-            new_state = PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING;
-          else
-            new_state = PLAN_ACTIVATION_DONE;
-          break;
-        case ACTIVATION_STATE_PARTIALLY_ACTIVATED:
-          new_state = PLAN_ACTIVATION_TRYING_OTASP;
-          break;
-        default:
-          new_state = PLAN_ACTIVATION_INITIATING_ACTIVATION;
-          break;
+      if (activation == flimflam::kActivationStateActivated) {
+        if (network->connection_state() == flimflam::kStatePortal)
+          new_state = PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING;
+        else
+          new_state = PLAN_ACTIVATION_DONE;
+      } else if (activation == flimflam::kActivationStatePartiallyActivated) {
+        new_state = PLAN_ACTIVATION_TRYING_OTASP;
+      } else {
+        new_state = PLAN_ACTIVATION_INITIATING_ACTIVATION;
       }
       break;
-    default: {
+    default:
       LOG(INFO) << "Waiting for cellular service to connect.";
-      } break;
+      break;
   }
   return new_state;
 }
 
 MobileActivator::PlanActivationState MobileActivator::PickNextOnlineState(
-    CellularNetwork* network) const {
+    const NetworkState* network) const {
   PlanActivationState new_state = state_;
+  const std::string& activation = network->activation_state();
   switch (state_) {
     case PLAN_ACTIVATION_START:
-      switch (network->activation_state()) {
-        case ACTIVATION_STATE_ACTIVATED:
-          if (network->restricted_pool())
-            new_state = PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING;
-          else
-            new_state = PLAN_ACTIVATION_DONE;
-          break;
-        case ACTIVATION_STATE_PARTIALLY_ACTIVATED:
-          new_state = PLAN_ACTIVATION_TRYING_OTASP;
-          break;
-        default:
-          new_state = PLAN_ACTIVATION_INITIATING_ACTIVATION;
-          break;
+      if (activation == flimflam::kActivationStateActivated) {
+        if (network->connection_state() == flimflam::kStatePortal)
+          new_state = PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING;
+        else
+          new_state = PLAN_ACTIVATION_DONE;
+      } else if (activation == flimflam::kActivationStatePartiallyActivated) {
+        new_state = PLAN_ACTIVATION_TRYING_OTASP;
+      } else {
+        new_state = PLAN_ACTIVATION_INITIATING_ACTIVATION;
       }
       break;
     case PLAN_ACTIVATION_START_OTASP: {
-      switch (network->activation_state()) {
-        case ACTIVATION_STATE_PARTIALLY_ACTIVATED:
+      if (activation == flimflam::kActivationStatePartiallyActivated) {
           new_state = PLAN_ACTIVATION_OTASP;
-          break;
-        case ACTIVATION_STATE_ACTIVATED:
-          new_state = PLAN_ACTIVATION_RECONNECTING;
-          break;
-        default: {
-          LOG(WARNING) << "Unexpected activation state for device "
-                       << network->service_path();
-          break;
-        }
+      } else if (activation == flimflam::kActivationStateActivated) {
+        new_state = PLAN_ACTIVATION_RECONNECTING;
+      } else {
+        LOG(WARNING) << "Unexpected activation state for device "
+                     << network->path();
       }
       break;
     }
@@ -675,36 +696,36 @@
       // Just ignore any changes until the OTASP retry timer kicks in.
       break;
     case PLAN_ACTIVATION_INITIATING_ACTIVATION: {
-      switch (network->activation_state()) {
-        case ACTIVATION_STATE_ACTIVATED:
-        case ACTIVATION_STATE_PARTIALLY_ACTIVATED:
-          new_state = PLAN_ACTIVATION_START;
-          break;
-        case ACTIVATION_STATE_NOT_ACTIVATED:
-        case ACTIVATION_STATE_ACTIVATING:
-          // Wait in this state until activation state changes.
-          break;
-        default:
-          LOG(WARNING) << "Unknown transition";
-          break;
+      if (pending_activation_request_) {
+        LOG(INFO) << "Waiting for pending activation attempt to finish";
+      } else if (activation == flimflam::kActivationStateActivated ||
+                  activation == flimflam::kActivationStatePartiallyActivated) {
+        new_state = PLAN_ACTIVATION_START;
+      } else if (activation == flimflam::kActivationStateNotActivated ||
+                 activation == flimflam::kActivationStateActivating) {
+        // Wait in this state until activation state changes.
+      } else {
+        LOG(WARNING) << "Unknown transition";
       }
       break;
     }
     case PLAN_ACTIVATION_OTASP:
     case PLAN_ACTIVATION_TRYING_OTASP:
-      if (network->activation_state() == ACTIVATION_STATE_NOT_ACTIVATED ||
-          network->activation_state() == ACTIVATION_STATE_ACTIVATING) {
+      if (pending_activation_request_) {
+        LOG(INFO) << "Waiting for pending activation attempt to finish";
+      } else if (activation == flimflam::kActivationStateNotActivated ||
+                 activation == flimflam::kActivationStateActivating) {
         LOG(INFO) << "Waiting for the OTASP to finish and the service to "
                   << "come back online";
-      } else if (network->activation_state() == ACTIVATION_STATE_ACTIVATED) {
+      } else if (activation == flimflam::kActivationStateActivated) {
         new_state = PLAN_ACTIVATION_DONE;
       } else {
         new_state = PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING;
       }
       break;
     case PLAN_ACTIVATION_RECONNECTING_PAYMENT:
-      if (!network->restricted_pool() &&
-          network->activation_state() == ACTIVATION_STATE_ACTIVATED)
+      if (network->connection_state() != flimflam::kStatePortal &&
+          activation == flimflam::kActivationStateActivated)
         // We're not portalled, and we're already activated, so we're online!
         new_state = PLAN_ACTIVATION_DONE;
       else
@@ -767,12 +788,11 @@
 }
 
 
-void MobileActivator::CompleteActivation(
-    CellularNetwork* network) {
+void MobileActivator::CompleteActivation() {
   // Remove observers, we are done with this page.
-  NetworkLibrary* lib = GetNetworkLibrary();
-  lib->RemoveNetworkManagerObserver(this);
-  lib->RemoveObserverForAllNetworks(this);
+  NetworkHandler::Get()->network_state_handler()->RemoveObserver(
+      this, FROM_HERE);
+
   // Reactivate other types of connections if we have
   // shut them down previously.
   ReEnableCertRevocationChecking();
@@ -784,7 +804,44 @@
            state_ == PLAN_ACTIVATION_PAGE_LOADING);
 }
 
-void MobileActivator::ChangeState(CellularNetwork* network,
+void MobileActivator::HandleActivationFailure(
+    const std::string& service_path,
+    PlanActivationState new_state,
+    const std::string& error_name,
+    scoped_ptr<base::DictionaryValue> error_data) {
+  pending_activation_request_ = false;
+  const NetworkState* network = GetNetworkState(service_path);
+  if (!network) {
+    NET_LOG_ERROR("Cellular service no longer exists", service_path);
+    return;
+  }
+  UMA_HISTOGRAM_COUNTS("Cellular.ActivationFailure", 1);
+  NET_LOG_ERROR("Failed to call Activate() on service", service_path);
+  if (new_state == PLAN_ACTIVATION_OTASP) {
+    ChangeState(network, PLAN_ACTIVATION_DELAY_OTASP, std::string());
+  } else {
+    ChangeState(network,
+                PLAN_ACTIVATION_ERROR,
+                GetErrorMessage(kFailedConnectivity));
+  }
+}
+
+void MobileActivator::RequestCellularActivation(
+    const NetworkState* network,
+    const base::Closure& success_callback,
+    const network_handler::ErrorCallback& error_callback) {
+  DCHECK(network);
+  NET_LOG_EVENT("Activating cellular service", network->path());
+  UMA_HISTOGRAM_COUNTS("Cellular.ActivationTry", 1);
+  pending_activation_request_ = true;
+  NetworkHandler::Get()->network_activation_handler()->
+      Activate(network->path(),
+               "",  // carrier
+               success_callback,
+               error_callback);
+}
+
+void MobileActivator::ChangeState(const NetworkState* network,
                                   PlanActivationState new_state,
                                   const std::string& error_description) {
   static bool first_time = true;
@@ -822,22 +879,16 @@
       break;
     case PLAN_ACTIVATION_INITIATING_ACTIVATION:
     case PLAN_ACTIVATION_TRYING_OTASP:
-    case PLAN_ACTIVATION_OTASP:
+    case PLAN_ACTIVATION_OTASP: {
       DCHECK(network);
-      LOG(WARNING) << "Activating service " << network->service_path();
-      UMA_HISTOGRAM_COUNTS("Cellular.ActivationTry", 1);
-      if (!network->StartActivation()) {
-        UMA_HISTOGRAM_COUNTS("Cellular.ActivationFailure", 1);
-        LOG(ERROR) << "Failed to call Activate() on service in shill.";
-        if (new_state == PLAN_ACTIVATION_OTASP) {
-          ChangeState(network, PLAN_ACTIVATION_DELAY_OTASP, std::string());
-        } else {
-          ChangeState(network,
-                      PLAN_ACTIVATION_ERROR,
-                      GetErrorMessage(kFailedConnectivity));
-        }
-      } else {
-        StartOTASPTimer();
+      network_handler::ErrorCallback on_activation_error =
+          base::Bind(&MobileActivator::HandleActivationFailure, AsWeakPtr(),
+                     network->path(),
+                     new_state);
+      RequestCellularActivation(
+          network,
+          base::Bind(&MobileActivator::StartOTASPTimer, AsWeakPtr()),
+          on_activation_error);
       }
       break;
     case PLAN_ACTIVATION_PAGE_LOADING:
@@ -866,7 +917,7 @@
           break;
         case PLAN_ACTIVATION_START_OTASP:
         case PLAN_ACTIVATION_OTASP:
-          if (!network || !network->connected()) {
+          if (!network || !network->IsConnectedState()) {
             next_state = PLAN_ACTIVATION_START_OTASP;
           } else {
             // We're online, which means we've conspired with
@@ -886,11 +937,11 @@
     }
     case PLAN_ACTIVATION_DONE:
       DCHECK(network);
-      CompleteActivation(network);
+      CompleteActivation();
       UMA_HISTOGRAM_COUNTS("Cellular.MobileSetupSucceeded", 1);
       break;
     case PLAN_ACTIVATION_ERROR:
-      CompleteActivation(NULL);
+      CompleteActivation();
       UMA_HISTOGRAM_COUNTS("Cellular.PlanFailed", 1);
       break;
     default:
@@ -921,33 +972,32 @@
 }
 
 bool MobileActivator::GotActivationError(
-    CellularNetwork* network, std::string* error) const {
+    const NetworkState* network, std::string* error) const {
   DCHECK(network);
   bool got_error = false;
   const char* error_code = kErrorDefault;
+  const std::string& activation = network->activation_state();
 
   // This is the magic for detection of errors in during activation process.
-  if (network->state() == STATE_FAILURE &&
-      network->error() == ERROR_AAA_FAILED) {
-    if (network->activation_state() ==
-            ACTIVATION_STATE_PARTIALLY_ACTIVATED) {
+  if (network->connection_state() == flimflam::kStateFailure &&
+      network->error() == flimflam::kErrorAaaFailed) {
+    if (activation == flimflam::kActivationStatePartiallyActivated) {
       error_code = kErrorBadConnectionPartial;
-    } else if (network->activation_state() == ACTIVATION_STATE_ACTIVATED) {
-      if (network->roaming_state() == ROAMING_STATE_HOME)
+    } else if (activation == flimflam::kActivationStateActivated) {
+      if (network->roaming() == flimflam::kRoamingStateHome)
         error_code = kErrorBadConnectionActivated;
-      else if (network->roaming_state() == ROAMING_STATE_ROAMING)
+      else if (network->roaming() == flimflam::kRoamingStateRoaming)
         error_code = kErrorRoamingOnConnection;
     }
     got_error = true;
-  } else if (network->state() == STATE_ACTIVATION_FAILURE) {
-    if (network->error() == ERROR_NEED_EVDO) {
-      if (network->activation_state() == ACTIVATION_STATE_PARTIALLY_ACTIVATED)
+  } else if (network->connection_state() == flimflam::kStateActivationFailure) {
+    if (network->error() == flimflam::kErrorNeedEvdo) {
+      if (activation == flimflam::kActivationStatePartiallyActivated)
         error_code = kErrorNoEVDO;
-    } else if (network->error() == ERROR_NEED_HOME_NETWORK) {
-      if (network->activation_state() == ACTIVATION_STATE_NOT_ACTIVATED) {
+    } else if (network->error() == flimflam::kErrorNeedHomeNetwork) {
+      if (activation == flimflam::kActivationStateNotActivated) {
         error_code = kErrorRoamingActivation;
-      } else if (network->activation_state() ==
-                 ACTIVATION_STATE_PARTIALLY_ACTIVATED) {
+      } else if (activation == flimflam::kActivationStatePartiallyActivated) {
         error_code = kErrorRoamingPartiallyActivated;
       }
     }
@@ -960,32 +1010,19 @@
   return got_error;
 }
 
-void MobileActivator::GetDeviceInfo(CellularNetwork* network,
-                                    DictionaryValue* value) {
-  DCHECK(network);
-  NetworkLibrary* cros = NetworkLibrary::Get();
-  if (!cros)
-    return;
-  value->SetString("carrier", network->name());
-  value->SetString("payment_url", network->payment_url());
-  if (network->using_post() && network->post_data().length())
-    value->SetString("post_data", network->post_data());
-
-  const NetworkDevice* device =
-      cros->FindNetworkDeviceByPath(network->device_path());
-  if (device) {
-    value->SetString("MEID", device->meid());
-    value->SetString("IMEI", device->imei());
-    value->SetString("MDN", device->mdn());
-  }
-}
-
 std::string MobileActivator::GetErrorMessage(const std::string& code) const {
   return cellular_config_->GetErrorMessage(code);
 }
 
-NetworkLibrary* MobileActivator::GetNetworkLibrary() const {
-  return NetworkLibrary::Get();
+void MobileActivator::SignalCellularPlanPayment() {
+  DCHECK(!HasRecentCellularPlanPayment());
+  cellular_plan_payment_time_ = base::Time::Now();
+}
+
+bool MobileActivator::HasRecentCellularPlanPayment() const {
+  const int kRecentPlanPaymentHours = 6;
+  return (base::Time::Now() -
+          cellular_plan_payment_time_).InHours() < kRecentPlanPaymentHours;
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/mobile/mobile_activator.h b/chrome/browser/chromeos/mobile/mobile_activator.h
index 3ba0e86..19bf158 100644
--- a/chrome/browser/chromeos/mobile/mobile_activator.h
+++ b/chrome/browser/chromeos/mobile/mobile_activator.h
@@ -9,14 +9,22 @@
 #include <string>
 
 #include "base/files/file_path.h"
+#include "base/gtest_prod_util.h"
 #include "base/memory/singleton.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
-#include "chrome/browser/chromeos/cros/network_library.h"
+#include "base/timer/timer.h"
+#include "chromeos/network/network_handler_callbacks.h"
+#include "chromeos/network/network_state_handler_observer.h"
 #include "content/public/browser/browser_thread.h"
 
+namespace base {
+class DictionaryValue;
+}
+
 namespace chromeos {
 
+class NetworkState;
 class TestMobileActivator;
 
 // Cellular plan config document.
@@ -48,9 +56,8 @@
 
 // This class performs mobile plan activation process.
 class MobileActivator
-  : public NetworkLibrary::NetworkManagerObserver,
-    public NetworkLibrary::NetworkObserver,
-    public base::SupportsWeakPtr<MobileActivator> {
+    : public base::SupportsWeakPtr<MobileActivator>,
+      public NetworkStateHandlerObserver {
  public:
   // Activation state.
   enum PlanActivationState {
@@ -91,7 +98,7 @@
    public:
     // Signals activation |state| change for given |network|.
     virtual void OnActivationStateChanged(
-        CellularNetwork* network,
+        const NetworkState* network,
         PlanActivationState state,
         const std::string& error_description) = 0;
 
@@ -133,11 +140,9 @@
   MobileActivator();
   virtual ~MobileActivator();
 
-  // NetworkLibrary::NetworkManagerObserver overrides.
-  virtual void OnNetworkManagerChanged(NetworkLibrary* obj) OVERRIDE;
-  // NetworkLibrary::NetworkObserver overrides.
-  virtual void OnNetworkChanged(NetworkLibrary* obj,
-                                const Network* network) OVERRIDE;
+  // NetworkStateHandlerObserver overrides.
+  virtual void DefaultNetworkChanged(const NetworkState* network) OVERRIDE;
+  virtual void NetworkPropertiesUpdated(const NetworkState* network) OVERRIDE;
 
   // Continue activation after inital setup (config load).
   void ContinueActivation();
@@ -161,32 +166,57 @@
   // Called when an OTASP attempt times out.
   void HandleOTASPTimeout();
   // Forces disconnect / reconnect when we detect portal connectivity issues.
-  void ForceReconnect(CellularNetwork* network, PlanActivationState next_state);
+  void ForceReconnect(const NetworkState* network,
+                      PlanActivationState next_state);
   // Called when ForceReconnect takes too long to reconnect.
   void ReconnectTimedOut();
+
+  // Called on default network changes to update cellular network activation
+  // state.
+  void RefreshCellularNetworks();
+
+  // Helper to get network state corresponding to service path. Provided
+  // for easy mocking in unit tests.
+  virtual const NetworkState* GetNetworkState(const std::string& service_path);
+
   // Verify the state of cellular network and modify internal state.
-  virtual void EvaluateCellularNetwork(CellularNetwork* network);
+  virtual void EvaluateCellularNetwork(const NetworkState* network);
   // PickNextState selects the desired state based on the current state of the
   // modem and the activator.  It does not transition to this state however.
-  PlanActivationState PickNextState(CellularNetwork* network,
+  PlanActivationState PickNextState(const NetworkState* network,
                                     std::string* error_description) const;
   // One of PickNext*State are called in PickNextState based on whether the
   // modem is online or not.
-  PlanActivationState PickNextOnlineState(CellularNetwork* network) const;
-  PlanActivationState PickNextOfflineState(CellularNetwork* network) const;
+  PlanActivationState PickNextOnlineState(const NetworkState* network) const;
+  PlanActivationState PickNextOfflineState(const NetworkState* network) const;
   // Check the current cellular network for error conditions.
-  bool GotActivationError(CellularNetwork* network,
+  bool GotActivationError(const NetworkState* network,
                           std::string* error) const;
   // Sends status updates to WebUI page.
-  void UpdatePage(CellularNetwork* network,
+  void UpdatePage(const NetworkState* network,
                   const std::string& error_description);
+
+  // Callback used to handle an activation error.
+  void HandleActivationFailure(
+      const std::string& service_path,
+      PlanActivationState new_state,
+      const std::string& error_name,
+      scoped_ptr<base::DictionaryValue> error_data);
+
+  // Request cellular activation for |network|.
+  // On success, |success_callback| will be called.
+  // On failure, |error_callback| will be called.
+  virtual void RequestCellularActivation(
+      const NetworkState* network,
+      const base::Closure& success_callback,
+      const network_handler::ErrorCallback& error_callback);
+
   // Changes internal state.
-  virtual void ChangeState(CellularNetwork* network,
+  virtual void ChangeState(const NetworkState* network,
                            PlanActivationState new_state,
                            const std::string& error_description);
   // Resets network devices after cellular activation process.
-  // |network| should be NULL if the activation process failed.
-  void CompleteActivation(CellularNetwork* network);
+  void CompleteActivation();
   // Disables SSL certificate revocation checking mechanism. In the case
   // where captive portal connection is the only one present, such revocation
   // checks could prevent payment portal page from loading.
@@ -196,9 +226,6 @@
   // Return error message for a given code.
   std::string GetErrorMessage(const std::string& code) const;
 
-  // Converts the currently active CellularNetwork device into a JS object.
-  static void GetDeviceInfo(CellularNetwork* network,
-                            DictionaryValue* value);
   static bool ShouldReportDeviceState(std::string* state, std::string* error);
 
   // Performs activation state cellular device evaluation.
@@ -207,16 +234,17 @@
   static bool EvaluateCellularDeviceState(bool* report_status,
                                           std::string* state,
                                           std::string* error);
-  // Finds cellular network that matches |meid_| or |iccid_|, reattach network
-  // change observer if |reattach_observer| flag is set.
-  virtual CellularNetwork* FindMatchingCellularNetwork(bool reattach_observer);
   // Starts the OTASP timeout timer.  If the timer fires, we'll force a
   // disconnect/reconnect cycle on this network.
   virtual void StartOTASPTimer();
 
-  static const char* GetStateDescription(PlanActivationState state);
+  // Records information that cellular plan payment has happened.
+  virtual void SignalCellularPlanPayment();
 
-  virtual NetworkLibrary* GetNetworkLibrary() const;
+  // Returns true if cellular plan payment has been recorded recently.
+  virtual bool HasRecentCellularPlanPayment() const;
+
+  static const char* GetStateDescription(PlanActivationState state);
 
   scoped_refptr<CellularConfigDocument> cellular_config_;
   // Internal handler state.
@@ -228,11 +256,18 @@
   // Service path of network being activated. Note that the path can change
   // during the activation process while still representing the same service.
   std::string service_path_;
+  // Device on which the network service is activated. While the service path
+  // can change during activation due to modem resets, the device path stays
+  // the same.
+  std::string device_path_;
   // Flags that controls if cert_checks needs to be restored
   // after the activation of cellular network.
   bool reenable_cert_check_;
   // True if activation process has been terminated.
   bool terminated_;
+  // True if an asynchronous activation request was dispatched to Shill
+  // but the succcess or failure of the request is yet unknown.
+  bool pending_activation_request_;
   // Connection retry counter.
   int connection_retry_count_;
   // Counters for how many times we've tried each OTASP step.
@@ -250,7 +285,8 @@
   base::RepeatingTimer<MobileActivator> continue_reconnect_timer_;
   // Called when the reconnect attempt times out.
   base::OneShotTimer<MobileActivator> reconnect_timeout_timer_;
-
+  // Cellular plan payment time.
+  base::Time cellular_plan_payment_time_;
 
   ObserverList<Observer> observers_;
 
diff --git a/chrome/browser/chromeos/mobile/mobile_activator_unittest.cc b/chrome/browser/chromeos/mobile/mobile_activator_unittest.cc
index 563a06a..764dfd6 100644
--- a/chrome/browser/chromeos/mobile/mobile_activator_unittest.cc
+++ b/chrome/browser/chromeos/mobile/mobile_activator_unittest.cc
@@ -4,17 +4,21 @@
 
 #include "chrome/browser/chromeos/mobile/mobile_activator.h"
 
-#include "chrome/browser/chromeos/cros/mock_network_library.h"
-#include "chrome/browser/chromeos/cros/network_library.h"
+#include "base/message_loop/message_loop.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/network/network_state.h"
 #include "content/public/browser/browser_thread.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
 
 using std::string;
 
 using content::BrowserThread;
 using testing::_;
 using testing::Eq;
+using testing::Invoke;
 using testing::Return;
 
 namespace {
@@ -34,39 +38,39 @@
 
 class TestMobileActivator : public MobileActivator {
  public:
-  TestMobileActivator(MockNetworkLibrary* mock_network_library,
-                      CellularNetwork* cellular_network)
-      : mock_network_library_(mock_network_library),
+  explicit TestMobileActivator(NetworkState* cellular_network) :
         cellular_network_(cellular_network) {
     // Provide reasonable defaults for basic things we're usually not testing.
     ON_CALL(*this, DCheckOnThread(_))
         .WillByDefault(Return());
-    ON_CALL(*this, FindMatchingCellularNetwork(_))
-        .WillByDefault(Return(cellular_network));
-    ON_CALL(*this, GetNetworkLibrary())
-        .WillByDefault(Return(mock_network_library_));
+    ON_CALL(*this, GetNetworkState(_))
+        .WillByDefault(Return(cellular_network_));
   }
   virtual ~TestMobileActivator() {}
 
-  MOCK_METHOD3(ChangeState, void(CellularNetwork*,
+  MOCK_METHOD3(RequestCellularActivation,
+               void(const NetworkState*,
+                    const base::Closure&,
+                    const network_handler::ErrorCallback&));
+  MOCK_METHOD3(ChangeState, void(const NetworkState*,
                                  MobileActivator::PlanActivationState,
                                  const std::string&));
-  MOCK_METHOD1(EvaluateCellularNetwork, void(CellularNetwork*));
-  MOCK_METHOD1(FindMatchingCellularNetwork, CellularNetwork*(bool));
+  MOCK_METHOD1(GetNetworkState, const NetworkState*(const std::string&));
+  MOCK_METHOD1(EvaluateCellularNetwork, void(const NetworkState*));
+  MOCK_METHOD0(SignalCellularPlanPayment, void(void));
   MOCK_METHOD0(StartOTASPTimer, void(void));
+  MOCK_CONST_METHOD0(HasRecentCellularPlanPayment, bool(void));
 
-  void InvokeChangeState(CellularNetwork* network,
+  void InvokeChangeState(const NetworkState* network,
                          MobileActivator::PlanActivationState new_state,
                          const std::string& error_description) {
     MobileActivator::ChangeState(network, new_state, error_description);
   }
 
  private:
-  MOCK_CONST_METHOD0(GetNetworkLibrary, NetworkLibrary*(void));
   MOCK_CONST_METHOD1(DCheckOnThread, void(const BrowserThread::ID id));
 
-  MockNetworkLibrary* mock_network_library_;
-  CellularNetwork* cellular_network_;
+  NetworkState* cellular_network_;
 
   DISALLOW_COPY_AND_ASSIGN(TestMobileActivator);
 };
@@ -74,27 +78,33 @@
 class MobileActivatorTest : public testing::Test {
  public:
   MobileActivatorTest()
-      : network_library_(),
-        cellular_network_(string(kTestServicePath)),
-        mobile_activator_(&network_library_, &cellular_network_) {}
+      : cellular_network_(string(kTestServicePath)),
+        mobile_activator_(&cellular_network_) {
+  }
   virtual ~MobileActivatorTest() {}
 
  protected:
-  virtual void SetUp() {}
-  virtual void TearDown() {}
+  virtual void SetUp() {
+    DBusThreadManager::InitializeWithStub();
+    NetworkHandler::Initialize();
+  }
+  virtual void TearDown() {
+    NetworkHandler::Shutdown();
+    DBusThreadManager::Shutdown();
+  }
 
   void set_activator_state(const MobileActivator::PlanActivationState state) {
     mobile_activator_.state_ = state;
   }
-  void set_network_activation_state(ActivationState state) {
-    cellular_network_.activation_state_ = state;
+  void set_network_activation_state(const std::string& activation_state) {
+    cellular_network_.activation_state_ = activation_state;
   }
-  void set_connection_state(ConnectionState state) {
-    cellular_network_.state_ = state;
+  void set_connection_state(const std::string& state) {
+    cellular_network_.connection_state_ = state;
   }
 
-  MockNetworkLibrary network_library_;
-  MockCellularNetwork cellular_network_;
+  base::MessageLoop message_loop_;
+  NetworkState cellular_network_;
   TestMobileActivator mobile_activator_;
  private:
   DISALLOW_COPY_AND_ASSIGN(MobileActivatorTest);
@@ -106,24 +116,24 @@
   // activated.
   std::string error_description;
   set_activator_state(MobileActivator::PLAN_ACTIVATION_START);
-  set_connection_state(STATE_IDLE);
-  set_network_activation_state(ACTIVATION_STATE_NOT_ACTIVATED);
+  set_connection_state(flimflam::kStateIdle);
+  set_network_activation_state(flimflam::kActivationStateNotActivated);
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_INITIATING_ACTIVATION,
             mobile_activator_.PickNextState(&cellular_network_,
                                             &error_description));
   // Now behave as if ChangeState() has initiated an activation.
   set_activator_state(MobileActivator::PLAN_ACTIVATION_INITIATING_ACTIVATION);
-  set_network_activation_state(ACTIVATION_STATE_ACTIVATING);
+  set_network_activation_state(flimflam::kActivationStateActivating);
   // We'll sit in this state while we wait for the OTASP to finish.
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_INITIATING_ACTIVATION,
             mobile_activator_.PickNextState(&cellular_network_,
                                             &error_description));
-  set_network_activation_state(ACTIVATION_STATE_PARTIALLY_ACTIVATED);
+  set_network_activation_state(flimflam::kActivationStatePartiallyActivated);
   // We'll sit in this state until we go online as well.
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_INITIATING_ACTIVATION,
             mobile_activator_.PickNextState(&cellular_network_,
                                             &error_description));
-  set_connection_state(STATE_PORTAL);
+  set_connection_state(flimflam::kStatePortal);
   // After we go online, we go back to START, which acts as a jumping off
   // point for the two types of initial OTASP.
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_START,
@@ -135,12 +145,12 @@
                                             &error_description));
   // Very similar things happen while we're trying OTASP.
   set_activator_state(MobileActivator::PLAN_ACTIVATION_TRYING_OTASP);
-  set_network_activation_state(ACTIVATION_STATE_ACTIVATING);
+  set_network_activation_state(flimflam::kActivationStateActivating);
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_TRYING_OTASP,
             mobile_activator_.PickNextState(&cellular_network_,
                                             &error_description));
-  set_network_activation_state(ACTIVATION_STATE_PARTIALLY_ACTIVATED);
-  set_connection_state(STATE_PORTAL);
+  set_network_activation_state(flimflam::kActivationStatePartiallyActivated);
+  set_connection_state(flimflam::kStatePortal);
   // And when we come back online again and aren't activating, load the portal.
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING,
             mobile_activator_.PickNextState(&cellular_network_,
@@ -149,7 +159,7 @@
   set_activator_state(MobileActivator::PLAN_ACTIVATION_SHOWING_PAYMENT);
   // The JS also calls us to signal that the portal is done.  This triggers us
   // to start our final OTASP via the aptly named StartOTASP().
-  EXPECT_CALL(network_library_, SignalCellularPlanPayment());
+  EXPECT_CALL(mobile_activator_, SignalCellularPlanPayment());
   EXPECT_CALL(mobile_activator_,
               ChangeState(Eq(&cellular_network_),
                           Eq(MobileActivator::PLAN_ACTIVATION_START_OTASP),
@@ -166,34 +176,52 @@
                                             &error_description));
   // Similarly to TRYING_OTASP and INITIATING_OTASP above...
   set_activator_state(MobileActivator::PLAN_ACTIVATION_OTASP);
-  set_network_activation_state(ACTIVATION_STATE_ACTIVATING);
+  set_network_activation_state(flimflam::kActivationStateActivating);
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_OTASP,
             mobile_activator_.PickNextState(&cellular_network_,
                                             &error_description));
-  set_network_activation_state(ACTIVATION_STATE_ACTIVATED);
+  set_network_activation_state(flimflam::kActivationStateActivated);
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_DONE,
             mobile_activator_.PickNextState(&cellular_network_,
                                             &error_description));
 }
 
+// A fake for MobileActivator::RequestCellularActivation that always succeeds.
+void FakeRequestCellularActivationSuccess(
+    const NetworkState* network,
+    const base::Closure& success_callback,
+    const network_handler::ErrorCallback& error_callback) {
+  success_callback.Run();
+}
+
+// A fake for MobileActivator::RequestCellularActivation that always fails.
+void FakeRequestCellularActivationFailure(
+    const NetworkState* network,
+    const base::Closure& success_callback,
+    const network_handler::ErrorCallback& error_callback) {
+  scoped_ptr<base::DictionaryValue> value;
+  error_callback.Run("", value.Pass());
+}
+
 TEST_F(MobileActivatorTest, OTASPScheduling) {
   const std::string error;
   for (size_t i = 0; i < kNumOTASPStates; ++i) {
     // When activation works, we start a timer to watch for success.
-    EXPECT_CALL(cellular_network_, StartActivation())
+    EXPECT_CALL(mobile_activator_, RequestCellularActivation(_, _, _))
         .Times(1)
-        .WillOnce(Return(true));
+        .WillOnce(Invoke(FakeRequestCellularActivationSuccess));
     EXPECT_CALL(mobile_activator_, StartOTASPTimer())
-        .Times(1);
+         .Times(1);
     set_activator_state(MobileActivator::PLAN_ACTIVATION_START);
     mobile_activator_.InvokeChangeState(&cellular_network_,
                                         kOTASPStates[i],
                                         error);
+
     // When activation fails, it's an error, unless we're trying for the final
     // OTASP, in which case we try again via DELAY_OTASP.
-    EXPECT_CALL(cellular_network_, StartActivation())
+    EXPECT_CALL(mobile_activator_, RequestCellularActivation(_, _, _))
         .Times(1)
-        .WillOnce(Return(false));
+        .WillOnce(Invoke(FakeRequestCellularActivationFailure));
     if (kOTASPStates[i] == MobileActivator::PLAN_ACTIVATION_OTASP) {
       EXPECT_CALL(mobile_activator_, ChangeState(
           Eq(&cellular_network_),
@@ -221,8 +249,8 @@
   // like when we're displaying the portal care quite a bit about going
   // offline.  Lets test for those cases.
   std::string error_description;
-  set_connection_state(STATE_FAILURE);
-  set_network_activation_state(ACTIVATION_STATE_PARTIALLY_ACTIVATED);
+  set_connection_state(flimflam::kStateFailure);
+  set_network_activation_state(flimflam::kActivationStatePartiallyActivated);
   set_activator_state(MobileActivator::PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING);
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_RECONNECTING,
             mobile_activator_.PickNextState(&cellular_network_,
@@ -234,10 +262,8 @@
 }
 
 TEST_F(MobileActivatorTest, StartAtStart) {
-  EXPECT_CALL(network_library_,
-              AddNetworkManagerObserver(Eq(&mobile_activator_)));
-  EXPECT_CALL(network_library_, HasRecentCellularPlanPayment()).
-      WillOnce(Return(false));
+  EXPECT_CALL(mobile_activator_, HasRecentCellularPlanPayment())
+      .WillOnce(Return(false));
   EXPECT_CALL(mobile_activator_,
               EvaluateCellularNetwork(Eq(&cellular_network_)));
   mobile_activator_.StartActivation();
diff --git a/chrome/browser/chromeos/net/OWNERS b/chrome/browser/chromeos/net/OWNERS
index 62b7b0d..7a8eb65 100644
--- a/chrome/browser/chromeos/net/OWNERS
+++ b/chrome/browser/chromeos/net/OWNERS
@@ -1,5 +1,3 @@
-per-file managed_network_configuration_handler.*=gspencer@chromium.org
-per-file managed_network_configuration_handler.*=pneubeck@chromium.org
-
-per-file onc_utils.*=gspencer@chromium.org
-per-file onc_utils.*=pneubeck@chromium.org
+per-file onc_utils*=gspencer@chromium.org
+per-file onc_utils*=pneubeck@chromium.org
+per-file proxy_config_handler*=pneubeck@chromium.org
diff --git a/chrome/browser/chromeos/net/network_portal_detector_stub.cc b/chrome/browser/chromeos/net/network_portal_detector_stub.cc
index 30d7053..2fd08aa 100644
--- a/chrome/browser/chromeos/net/network_portal_detector_stub.cc
+++ b/chrome/browser/chromeos/net/network_portal_detector_stub.cc
@@ -13,6 +13,31 @@
 NetworkPortalDetectorStub::~NetworkPortalDetectorStub() {
 }
 
+void NetworkPortalDetectorStub::SetDefaultNetworkPathForTesting(
+    const std::string& service_path) {
+  if (service_path.empty())
+    default_network_.reset();
+  else
+    default_network_.reset(new NetworkState(service_path));
+}
+
+void NetworkPortalDetectorStub::SetDetectionResultsForTesting(
+    const std::string& service_path,
+    const CaptivePortalState& state) {
+  if (!service_path.empty())
+    portal_state_map_[service_path] = state;
+}
+
+void NetworkPortalDetectorStub::NotifyObserversForTesting() {
+  CaptivePortalState state;
+  if (default_network_ &&
+      portal_state_map_.count(default_network_->path())) {
+    state = portal_state_map_[default_network_->path()];
+  }
+  FOR_EACH_OBSERVER(Observer, observers_,
+                    OnPortalDetectionCompleted(default_network_.get(), state));
+}
+
 void NetworkPortalDetectorStub::Init() {
 }
 
@@ -69,30 +94,4 @@
 void NetworkPortalDetectorStub::DisableLazyDetection() {
 }
 
-void NetworkPortalDetectorStub::SetDefaultNetworkPathForTesting(
-    const std::string& service_path) {
-  if (service_path.empty())
-    default_network_.reset();
-  else
-    default_network_.reset(new NetworkState(service_path));
-}
-
-void NetworkPortalDetectorStub::SetDetectionResultsForTesting(
-    const std::string& service_path,
-    const CaptivePortalState& state) {
-  if (service_path.empty())
-    return;
-  portal_state_map_[service_path] = state;
-}
-
-void NetworkPortalDetectorStub::NotifyObserversForTesting() {
-  CaptivePortalState state;
-  if (default_network_ &&
-      portal_state_map_.count(default_network_->path())) {
-    state = portal_state_map_[default_network_->path()];
-  }
-  FOR_EACH_OBSERVER(Observer, observers_,
-                    OnPortalDetectionCompleted(default_network_.get(), state));
-}
-
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/net/network_portal_detector_stub.h b/chrome/browser/chromeos/net/network_portal_detector_stub.h
index 8623503..9494109 100644
--- a/chrome/browser/chromeos/net/network_portal_detector_stub.h
+++ b/chrome/browser/chromeos/net/network_portal_detector_stub.h
@@ -21,6 +21,11 @@
   NetworkPortalDetectorStub();
   virtual ~NetworkPortalDetectorStub();
 
+  void SetDefaultNetworkPathForTesting(const std::string& service_path);
+  void SetDetectionResultsForTesting(const std::string& service_path,
+                                     const CaptivePortalState& state);
+  void NotifyObserversForTesting();
+
   // NetworkPortalDetector implementation:
   virtual void Init() OVERRIDE;
   virtual void Shutdown() OVERRIDE;
@@ -36,15 +41,9 @@
   virtual void DisableLazyDetection() OVERRIDE;
 
  private:
-  friend class UpdateScreenTest;
-
   typedef std::string NetworkId;
   typedef base::hash_map<NetworkId, CaptivePortalState> CaptivePortalStateMap;
 
-  void SetDefaultNetworkPathForTesting(const std::string& service_path);
-  void SetDetectionResultsForTesting(const std::string& service_path,
-                                     const CaptivePortalState& state);
-  void NotifyObserversForTesting();
 
   ObserverList<Observer> observers_;
   scoped_ptr<NetworkState> default_network_;
diff --git a/chrome/browser/chromeos/options/wifi_config_view.cc b/chrome/browser/chromeos/options/wifi_config_view.cc
index ae52f47..1f57adc 100644
--- a/chrome/browser/chromeos/options/wifi_config_view.cc
+++ b/chrome/browser/chromeos/options/wifi_config_view.cc
@@ -153,7 +153,7 @@
 
 class UserCertComboboxModel : public ui::ComboboxModel {
  public:
-  UserCertComboboxModel();
+  explicit UserCertComboboxModel(WifiConfigView* owner);
   virtual ~UserCertComboboxModel();
 
   // Overridden from ui::ComboboxModel:
@@ -161,6 +161,8 @@
   virtual string16 GetItemAt(int index) OVERRIDE;
 
  private:
+  WifiConfigView* owner_;
+
   DISALLOW_COPY_AND_ASSIGN(UserCertComboboxModel);
 };
 
@@ -301,13 +303,16 @@
 
 // UserCertComboboxModel -------------------------------------------------------
 
-UserCertComboboxModel::UserCertComboboxModel() {
+UserCertComboboxModel::UserCertComboboxModel(WifiConfigView* owner)
+    : owner_(owner) {
 }
 
 UserCertComboboxModel::~UserCertComboboxModel() {
 }
 
 int UserCertComboboxModel::GetItemCount() const {
+  if (!owner_->UserCertActive())
+    return 0;
   if (CertLibrary::Get()->CertificatesLoading())
     return 1;  // "Loading"
   int num_certs =
@@ -318,6 +323,8 @@
 }
 
 string16 UserCertComboboxModel::GetItemAt(int index) {
+  if (!owner_->UserCertActive())
+    return string16();
   if (CertLibrary::Get()->CertificatesLoading())
     return l10n_util::GetStringUTF16(
         IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT_LOADING);
@@ -803,8 +810,8 @@
 
 std::string WifiConfigView::GetEapClientCertPkcs11Id() const {
   DCHECK(user_cert_combobox_);
-  if (!HaveUserCerts()) {
-    return std::string();  // "None installed"
+  if (!HaveUserCerts() || !UserCertActive()) {
+    return std::string();  // No certificate selected or not required.
   } else {
     // Certificates are listed in the order they appear in the model.
     int index = user_cert_combobox_->selected_index();
@@ -1016,7 +1023,7 @@
         IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT);
     user_cert_label_ = new views::Label(user_cert_label_text);
     layout->AddView(user_cert_label_);
-    user_cert_combobox_model_.reset(new internal::UserCertComboboxModel());
+    user_cert_combobox_model_.reset(new internal::UserCertComboboxModel(this));
     user_cert_combobox_ = new views::Combobox(user_cert_combobox_model_.get());
     user_cert_combobox_->SetAccessibleName(user_cert_label_text);
     user_cert_label_->SetEnabled(false);
diff --git a/chrome/browser/chromeos/options/wifi_config_view.h b/chrome/browser/chromeos/options/wifi_config_view.h
index 497671e..983e359 100644
--- a/chrome/browser/chromeos/options/wifi_config_view.h
+++ b/chrome/browser/chromeos/options/wifi_config_view.h
@@ -94,6 +94,8 @@
                                      const std::string& key);
 
  private:
+  friend class internal::UserCertComboboxModel;
+
   // Initializes UI.  If |show_8021x| includes 802.1x config options.
   void Init(bool show_8021x);
 
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc
index 3b4a12b..109f3a3 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc
@@ -27,7 +27,7 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/cryptohome/cryptohome_library.h"
 #include "chromeos/cryptohome/mock_cryptohome_library.h"
-#include "chromeos/dbus/cryptohome_client.h"
+#include "chromeos/dbus/cryptohome_client_stub.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 #include "google_apis/gaia/gaia_oauth_client.h"
 #include "net/url_request/test_url_fetcher_factory.h"
@@ -60,13 +60,14 @@
  protected:
   DeviceCloudPolicyManagerChromeOSTest()
       : cryptohome_library_(chromeos::CryptohomeLibrary::GetTestImpl()),
-        stub_cryptohome_client_(chromeos::CryptohomeClient::Create(
-            chromeos::STUB_DBUS_CLIENT_IMPLEMENTATION, NULL)),
+        stub_cryptohome_client_(new chromeos::CryptohomeClientStubImpl()),
         install_attributes_(cryptohome_library_.get(),
                             stub_cryptohome_client_.get()),
         store_(new DeviceCloudPolicyStoreChromeOS(&device_settings_service_,
                                                   &install_attributes_)),
-        manager_(make_scoped_ptr(store_), &install_attributes_) {}
+        manager_(make_scoped_ptr(store_), &install_attributes_) {
+    stub_cryptohome_client_->Init(NULL /* no dbus::Bus */);
+  }
 
   virtual void SetUp() OVERRIDE {
     DeviceSettingsTestBase::SetUp();
@@ -99,7 +100,7 @@
   }
 
   scoped_ptr<chromeos::CryptohomeLibrary> cryptohome_library_;
-  scoped_ptr<chromeos::CryptohomeClient> stub_cryptohome_client_;
+  scoped_ptr<chromeos::CryptohomeClientStubImpl> stub_cryptohome_client_;
   EnterpriseInstallAttributes install_attributes_;
 
   scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos_unittest.cc b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos_unittest.cc
index d423a74..c87c340 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos_unittest.cc
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos_unittest.cc
@@ -14,7 +14,7 @@
 #include "chrome/browser/chromeos/settings/device_settings_test_helper.h"
 #include "chrome/browser/policy/proto/chromeos/chrome_device_policy.pb.h"
 #include "chromeos/cryptohome/cryptohome_library.h"
-#include "chromeos/dbus/cryptohome_client.h"
+#include "chromeos/dbus/cryptohome_client_stub.h"
 #include "policy/policy_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -36,12 +36,13 @@
  protected:
   DeviceCloudPolicyStoreChromeOSTest()
       : cryptohome_library_(chromeos::CryptohomeLibrary::GetTestImpl()),
-        stub_cryptohome_client_(chromeos::CryptohomeClient::Create(
-            chromeos::STUB_DBUS_CLIENT_IMPLEMENTATION, NULL)),
+        stub_cryptohome_client_(new chromeos::CryptohomeClientStubImpl()),
         install_attributes_(new EnterpriseInstallAttributes(
             cryptohome_library_.get(), stub_cryptohome_client_.get())),
         store_(new DeviceCloudPolicyStoreChromeOS(&device_settings_service_,
-                                                  install_attributes_.get())) {}
+                                                  install_attributes_.get())) {
+    stub_cryptohome_client_->Init(NULL /* no dbus::Bus */);
+  }
 
   virtual void SetUp() OVERRIDE {
     DeviceSettingsTestBase::SetUp();
@@ -104,7 +105,7 @@
   }
 
   scoped_ptr<chromeos::CryptohomeLibrary> cryptohome_library_;
-  scoped_ptr<chromeos::CryptohomeClient> stub_cryptohome_client_;
+  scoped_ptr<chromeos::CryptohomeClientStubImpl> stub_cryptohome_client_;
   scoped_ptr<EnterpriseInstallAttributes> install_attributes_;
 
   scoped_ptr<DeviceCloudPolicyStoreChromeOS> store_;
diff --git a/chrome/browser/chromeos/policy/device_local_account.cc b/chrome/browser/chromeos/policy/device_local_account.cc
index 79fdf3b..18ac3b4 100644
--- a/chrome/browser/chromeos/policy/device_local_account.cc
+++ b/chrome/browser/chromeos/policy/device_local_account.cc
@@ -53,8 +53,9 @@
       NOTREACHED();
       break;
   }
-  return base::HexEncode(account_id.c_str(), account_id.size()) + "@" +
-      domain_prefix + kDeviceLocalAccountDomainSuffix;
+  return gaia::CanonicalizeEmail(
+      base::HexEncode(account_id.c_str(), account_id.size()) + "@" +
+      domain_prefix + kDeviceLocalAccountDomainSuffix);
 }
 
 bool IsDeviceLocalAccountUser(const std::string& user_id) {
diff --git a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
index b3d0c74..450f192 100644
--- a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
@@ -40,9 +40,13 @@
 #include "chrome/browser/policy/proto/chromeos/chrome_device_policy.pb.h"
 #include "chrome/browser/policy/test/local_policy_test_server.h"
 #include "chrome/browser/prefs/session_startup_pref.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/host_desktop.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
@@ -361,6 +365,62 @@
   }
 }
 
+IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, FullscreenDisallowed) {
+  UploadAndInstallDeviceLocalAccountPolicy();
+  AddPublicSessionToDevicePolicy(kAccountId1);
+
+  // This observes the display name becoming available as this indicates
+  // device-local account policy is fully loaded, which is a prerequisite for
+  // successful login.
+  content::WindowedNotificationObserver(
+      chrome::NOTIFICATION_USER_LIST_CHANGED,
+      base::Bind(&DisplayNameMatches, user_id_1_, kDisplayName)).Wait();
+
+  // Wait for the login UI to be ready.
+  chromeos::LoginDisplayHostImpl* host =
+      reinterpret_cast<chromeos::LoginDisplayHostImpl*>(
+          chromeos::LoginDisplayHostImpl::default_host());
+  ASSERT_TRUE(host);
+  chromeos::OobeUI* oobe_ui = host->GetOobeUI();
+  ASSERT_TRUE(oobe_ui);
+  base::RunLoop run_loop;
+  const bool oobe_ui_ready = oobe_ui->IsJSReady(run_loop.QuitClosure());
+  if (!oobe_ui_ready)
+    run_loop.Run();
+
+  // Ensure that the browser stays alive, even though no windows are opened
+  // during session start.
+  chrome::StartKeepAlive();
+
+  // Start login into the device-local account.
+  host->StartSignInScreen();
+  chromeos::ExistingUserController* controller =
+      chromeos::ExistingUserController::current_controller();
+  ASSERT_TRUE(controller);
+  controller->LoginAsPublicAccount(user_id_1_);
+
+  // Wait for the session to start.
+  content::WindowedNotificationObserver(chrome::NOTIFICATION_SESSION_STARTED,
+                                        base::Bind(IsSessionStarted)).Wait();
+
+  // Open a browser window.
+  chrome::NewEmptyWindow(ProfileManager::GetDefaultProfile(),
+                         chrome::HOST_DESKTOP_TYPE_ASH);
+  BrowserList* browser_list =
+    BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
+  EXPECT_EQ(1U, browser_list->size());
+  Browser* browser = browser_list->get(0);
+  ASSERT_TRUE(browser);
+  BrowserWindow* browser_window = browser->window();
+  ASSERT_TRUE(browser_window);
+  chrome::EndKeepAlive();
+
+  // Verify that an attempt to enter fullscreen mode is denied.
+  EXPECT_FALSE(browser_window->IsFullscreen());
+  chrome::ToggleFullscreenMode(browser);
+  EXPECT_FALSE(browser_window->IsFullscreen());
+}
+
 class TermsOfServiceTest : public DeviceLocalAccountTest,
                            public testing::WithParamInterface<bool> {
 };
diff --git a/chrome/browser/chromeos/policy/device_local_account_policy_service_unittest.cc b/chrome/browser/chromeos/policy/device_local_account_policy_service_unittest.cc
index baae74b..e8eadce 100644
--- a/chrome/browser/chromeos/policy/device_local_account_policy_service_unittest.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_policy_service_unittest.cc
@@ -75,6 +75,11 @@
                              POLICY_SCOPE_USER,
                              Value::CreateBooleanValue(true),
                              NULL);
+    expected_policy_map_.Set(key::kFullscreenAllowed,
+                             POLICY_LEVEL_MANDATORY,
+                             POLICY_SCOPE_USER,
+                             Value::CreateBooleanValue(false),
+                             NULL);
     scoped_ptr<base::ListValue> allowed_extension_types(new base::ListValue());
     allowed_extension_types->AppendString("hosted_app");
     expected_policy_map_.Set(key::kExtensionAllowedTypes,
diff --git a/chrome/browser/chromeos/policy/device_local_account_policy_store.cc b/chrome/browser/chromeos/policy/device_local_account_policy_store.cc
index 786a2c9..392cdf8 100644
--- a/chrome/browser/chromeos/policy/device_local_account_policy_store.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_policy_store.cc
@@ -101,6 +101,13 @@
                   POLICY_SCOPE_USER,
                   Value::CreateBooleanValue(true),
                   NULL);
+  // Force the |FullscreenAllowed| policy to |false|, ensuring that the ash
+  // shelf cannot be hidden by entering fullscreen mode.
+  policy_map_.Set(key::kFullscreenAllowed,
+                  POLICY_LEVEL_MANDATORY,
+                  POLICY_SCOPE_USER,
+                  Value::CreateBooleanValue(false),
+                  NULL);
   // Restrict device-local accounts to hosted apps for now (i.e. no extensions,
   // packaged apps etc.) for security/privacy reasons (i.e. we'd like to
   // prevent the admin from stealing private information from random people).
diff --git a/chrome/browser/chromeos/policy/enterprise_install_attributes_unittest.cc b/chrome/browser/chromeos/policy/enterprise_install_attributes_unittest.cc
index 5014997..32f2f08 100644
--- a/chrome/browser/chromeos/policy/enterprise_install_attributes_unittest.cc
+++ b/chrome/browser/chromeos/policy/enterprise_install_attributes_unittest.cc
@@ -11,7 +11,7 @@
 #include "base/run_loop.h"
 #include "chrome/browser/policy/proto/chromeos/install_attributes.pb.h"
 #include "chromeos/cryptohome/cryptohome_library.h"
-#include "chromeos/dbus/cryptohome_client.h"
+#include "chromeos/dbus/cryptohome_client_stub.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -37,9 +37,10 @@
  protected:
   EnterpriseInstallAttributesTest()
       : cryptohome_(chromeos::CryptohomeLibrary::GetTestImpl()),
-        stub_cryptohome_client_(chromeos::CryptohomeClient::Create(
-            chromeos::STUB_DBUS_CLIENT_IMPLEMENTATION, NULL)),
-        install_attributes_(cryptohome_.get(), stub_cryptohome_client_.get()) {}
+        stub_cryptohome_client_(new chromeos::CryptohomeClientStubImpl()),
+        install_attributes_(cryptohome_.get(), stub_cryptohome_client_.get()) {
+    stub_cryptohome_client_->Init(NULL /* no dbus::Bus */);
+  }
 
   virtual void SetUp() OVERRIDE {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
@@ -62,7 +63,7 @@
   base::MessageLoopForUI message_loop_;
   base::ScopedTempDir temp_dir_;
   scoped_ptr<chromeos::CryptohomeLibrary> cryptohome_;
-  scoped_ptr<chromeos::CryptohomeClient> stub_cryptohome_client_;
+  scoped_ptr<chromeos::CryptohomeClientStubImpl> stub_cryptohome_client_;
   EnterpriseInstallAttributes install_attributes_;
 
   EnterpriseInstallAttributes::LockResult LockDeviceAndWaitForResult(
diff --git a/chrome/browser/chromeos/policy/network_configuration_updater.cc b/chrome/browser/chromeos/policy/network_configuration_updater.cc
index db2dd93..d9c2d14 100644
--- a/chrome/browser/chromeos/policy/network_configuration_updater.cc
+++ b/chrome/browser/chromeos/policy/network_configuration_updater.cc
@@ -3,78 +3,131 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/chromeos/policy/network_configuration_updater.h"
-#include "chromeos/network/onc/onc_constants.h"
-#include "content/public/browser/browser_thread.h"
-#include "net/cert/cert_trust_anchor_provider.h"
 
-using content::BrowserThread;
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/values.h"
+#include "chrome/browser/policy/policy_map.h"
+#include "chromeos/network/managed_network_configuration_handler.h"
+#include "chromeos/network/onc/onc_certificate_importer.h"
+#include "chromeos/network/onc/onc_utils.h"
+#include "policy/policy_constants.h"
 
 namespace policy {
 
-namespace {
-
-// A simple implementation of net::CertTrustAnchorProvider that returns a list
-// of certificates that can be set by the owner of this object.
-class CrosTrustAnchorProvider : public net::CertTrustAnchorProvider {
- public:
-  CrosTrustAnchorProvider()
-      : trust_anchors_(new net::CertificateList) {
-  }
-
-  virtual ~CrosTrustAnchorProvider() {
-  }
-
-  // CertTrustAnchorProvider overrides.
-  virtual const net::CertificateList& GetAdditionalTrustAnchors() OVERRIDE {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-    return *trust_anchors_;
-  }
-
-  void SetTrustAnchors(scoped_ptr<net::CertificateList> trust_anchors) {
-    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-    trust_anchors_ = trust_anchors.Pass();
-  }
-
- private:
-  scoped_ptr<net::CertificateList> trust_anchors_;
-
-  DISALLOW_COPY_AND_ASSIGN(CrosTrustAnchorProvider);
-};
-
-}  // namespace
-
-NetworkConfigurationUpdater::NetworkConfigurationUpdater()
-    : allow_trusted_certificates_from_policy_(false),
-      cert_trust_provider_(new CrosTrustAnchorProvider()) {
-}
-
 NetworkConfigurationUpdater::~NetworkConfigurationUpdater() {
-  bool posted = BrowserThread::DeleteSoon(
-      BrowserThread::IO, FROM_HERE, cert_trust_provider_);
-  if (!posted)
-    delete cert_trust_provider_;
+  policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, this);
 }
 
-net::CertTrustAnchorProvider*
-NetworkConfigurationUpdater::GetCertTrustAnchorProvider() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  return cert_trust_provider_;
+// static
+scoped_ptr<NetworkConfigurationUpdater>
+NetworkConfigurationUpdater::CreateForDevicePolicy(
+    scoped_ptr<chromeos::onc::CertificateImporter> certificate_importer,
+    PolicyService* policy_service,
+    chromeos::ManagedNetworkConfigurationHandler* network_config_handler) {
+  scoped_ptr<NetworkConfigurationUpdater> updater(
+      new NetworkConfigurationUpdater(chromeos::onc::ONC_SOURCE_DEVICE_POLICY,
+                                      key::kDeviceOpenNetworkConfiguration,
+                                      certificate_importer.Pass(),
+                                      policy_service,
+                                      network_config_handler));
+  updater->Init();
+  return updater.Pass();
 }
 
-void NetworkConfigurationUpdater::SetAllowTrustedCertsFromPolicy() {
-  allow_trusted_certificates_from_policy_ = true;
+void NetworkConfigurationUpdater::OnPolicyUpdated(const PolicyNamespace& ns,
+                                                  const PolicyMap& previous,
+                                                  const PolicyMap& current) {
+  // Ignore this call. Policy changes are already observed by the registrar.
 }
 
-void NetworkConfigurationUpdater::SetTrustAnchors(
-    scoped_ptr<net::CertificateList> web_trust_certs) {
-  if (allow_trusted_certificates_from_policy_) {
-    BrowserThread::PostTask(
-        BrowserThread::IO, FROM_HERE,
-        base::Bind(&CrosTrustAnchorProvider::SetTrustAnchors,
-                   base::Unretained(static_cast<CrosTrustAnchorProvider*>(
-                       cert_trust_provider_)),
-                   base::Passed(&web_trust_certs)));
+void NetworkConfigurationUpdater::OnPolicyServiceInitialized(
+    PolicyDomain domain) {
+  if (domain != POLICY_DOMAIN_CHROME)
+    return;
+
+  if (policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME)) {
+    VLOG(1) << LogHeader() << " initialized.";
+    policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, this);
+    ApplyPolicy();
   }
 }
 
+NetworkConfigurationUpdater::NetworkConfigurationUpdater(
+    chromeos::onc::ONCSource onc_source,
+    std::string policy_key,
+    scoped_ptr<chromeos::onc::CertificateImporter> certificate_importer,
+    PolicyService* policy_service,
+    chromeos::ManagedNetworkConfigurationHandler* network_config_handler)
+    : onc_source_(onc_source),
+      network_config_handler_(network_config_handler),
+      certificate_importer_(certificate_importer.Pass()),
+      policy_key_(policy_key),
+      policy_change_registrar_(policy_service,
+                               PolicyNamespace(POLICY_DOMAIN_CHROME,
+                                               std::string())),
+      policy_service_(policy_service) {
+}
+
+void NetworkConfigurationUpdater::Init() {
+  policy_change_registrar_.Observe(
+      policy_key_,
+      base::Bind(&NetworkConfigurationUpdater::OnPolicyChanged,
+                 base::Unretained(this)));
+
+  if (policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME)) {
+    VLOG(1) << LogHeader() << " is already initialized.";
+    ApplyPolicy();
+  } else {
+    policy_service_->AddObserver(POLICY_DOMAIN_CHROME, this);
+  }
+}
+
+void NetworkConfigurationUpdater::ImportCertificates(
+    const base::ListValue& certificates_onc) {
+  certificate_importer_->ImportCertificates(
+      certificates_onc, onc_source_, NULL);
+}
+
+void NetworkConfigurationUpdater::ApplyNetworkPolicy(
+    base::ListValue* network_configs_onc) {
+  network_config_handler_->SetPolicy(
+      onc_source_, std::string() /* no username hash */, *network_configs_onc);
+}
+
+void NetworkConfigurationUpdater::OnPolicyChanged(
+    const base::Value* previous,
+    const base::Value* current) {
+  VLOG(1) << LogHeader() << " changed.";
+  ApplyPolicy();
+}
+
+void NetworkConfigurationUpdater::ApplyPolicy() {
+  const PolicyMap& policies = policy_service_->GetPolicies(
+      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
+  const base::Value* policy_value = policies.GetValue(policy_key_);
+
+  std::string onc_blob;
+  if (!policy_value)
+    VLOG(2) << LogHeader() << " is not set.";
+  else if (!policy_value->GetAsString(&onc_blob))
+    LOG(ERROR) << LogHeader() << " is not a string value.";
+
+  base::ListValue network_configs;
+  base::ListValue certificates;
+  chromeos::onc::ParseAndValidateOncForImport(onc_blob,
+                                              onc_source_,
+                                              "" /* no passphrase */,
+                                              &network_configs,
+                                              &certificates);
+
+  ImportCertificates(certificates);
+  ApplyNetworkPolicy(&network_configs);
+}
+
+std::string NetworkConfigurationUpdater::LogHeader() const {
+  return chromeos::onc::GetSourceAsString(onc_source_);
+}
+
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/network_configuration_updater.h b/chrome/browser/chromeos/policy/network_configuration_updater.h
index 4916107..c10a224 100644
--- a/chrome/browser/chromeos/policy/network_configuration_updater.h
+++ b/chrome/browser/chromeos/policy/network_configuration_updater.h
@@ -5,66 +5,95 @@
 #ifndef CHROME_BROWSER_CHROMEOS_POLICY_NETWORK_CONFIGURATION_UPDATER_H_
 #define CHROME_BROWSER_CHROMEOS_POLICY_NETWORK_CONFIGURATION_UPDATER_H_
 
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/cert/x509_certificate.h"
+#include <string>
 
-namespace chromeos {
-class User;
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/policy/policy_service.h"
+#include "chromeos/network/onc/onc_constants.h"
+
+namespace base {
+class Value;
 }
 
-namespace net {
-class CertTrustAnchorProvider;
+namespace chromeos {
+class ManagedNetworkConfigurationHandler;
+
+namespace onc {
+class CertificateImporter;
+}
 }
 
 namespace policy {
 
-class PolicyService;
+class PolicyMap;
 
-// Keeps track of the network configuration policy settings and pushes changes
-// to the respective configuration backend, which in turn writes configurations
-// to Shill.
-class NetworkConfigurationUpdater {
+// Implements the common part of tracking a OpenNetworkConfiguration device or
+// user policy. Pushes the network configs to the
+// ManagedNetworkConfigurationHandler, which in turn writes configurations to
+// Shill. Certificates are imported with the chromeos::onc::CertificateImporter.
+// For user policies the subclass UserNetworkConfigurationUpdater must be used.
+// Does not handle proxy settings.
+class NetworkConfigurationUpdater : public PolicyService::Observer {
  public:
-  NetworkConfigurationUpdater();
   virtual ~NetworkConfigurationUpdater();
 
-  // Provides the user policy service to the updater. Before this function is
-  // called and the policy service is completely initialized, the user policy is
-  // not applied. This function may trigger immediate policy applications.  Web
-  // trust isn't given to certificates imported from ONC by default. Setting
-  // |allow_trust_certs_from_policy| to true allows giving Web trust to the
-  // certificates that request it. References to |user_policy_service| and
-  // |user| are stored until UnsetUserPolicyService() is called.
-  virtual void SetUserPolicyService(bool allow_trusted_certs_from_policy,
-                                    const chromeos::User* user,
-                                    PolicyService* user_policy_service) = 0;
+  // Creates an updater that applies the ONC device policy from |policy_service|
+  // once the policy service is completely initialized and on each policy
+  // change.
+  static scoped_ptr<NetworkConfigurationUpdater> CreateForDevicePolicy(
+      scoped_ptr<chromeos::onc::CertificateImporter> certificate_importer,
+      PolicyService* policy_service,
+      chromeos::ManagedNetworkConfigurationHandler* network_config_handler);
 
-  // Unregisters from the PolicyService previously provided by
-  // SetUserPolicyService and unsets the stored pointer.
-  virtual void UnsetUserPolicyService() = 0;
-
-  // Returns a CertTrustAnchorProvider that provides the list of server and
-  // CA certificates with the Web trust flag set that were retrieved from the
-  // last user ONC policy update.
-  // This getter must be used on the UI thread, and the provider must be used
-  // on the IO thread. It is only valid as long as the
-  // NetworkConfigurationUpdater is valid; the NetworkConfigurationUpdater
-  // outlives all the profiles, and deletes the provider on the IO thread.
-  net::CertTrustAnchorProvider* GetCertTrustAnchorProvider();
+  // PolicyService::Observer overrides
+  virtual void OnPolicyUpdated(const PolicyNamespace& ns,
+                               const PolicyMap& previous,
+                               const PolicyMap& current) OVERRIDE;
+  virtual void OnPolicyServiceInitialized(PolicyDomain domain) OVERRIDE;
 
  protected:
-  void SetAllowTrustedCertsFromPolicy();
+  NetworkConfigurationUpdater(
+      chromeos::onc::ONCSource onc_source,
+      std::string policy_key,
+      scoped_ptr<chromeos::onc::CertificateImporter> certificate_importer,
+      PolicyService* policy_service,
+      chromeos::ManagedNetworkConfigurationHandler* network_config_handler);
 
-  void SetTrustAnchors(scoped_ptr<net::CertificateList> web_trust_certs);
+  void Init();
+
+  // Imports the certificates part of the policy.
+  virtual void ImportCertificates(const base::ListValue& certificates_onc);
+
+  // Pushes the network part of the policy to the
+  // ManagedNetworkConfigurationHandler. This can be overridden by subclasses to
+  // modify |network_configs_onc| before the actual application.
+  virtual void ApplyNetworkPolicy(base::ListValue* network_configs_onc);
+
+  chromeos::onc::ONCSource onc_source_;
+
+  // Pointer to the global singleton or a test instance.
+  chromeos::ManagedNetworkConfigurationHandler* network_config_handler_;
+
+  scoped_ptr<chromeos::onc::CertificateImporter> certificate_importer_;
 
  private:
-  // Whether Web trust is allowed or not.
-  bool allow_trusted_certificates_from_policy_;
+  // Called if the ONC policy changed.
+  void OnPolicyChanged(const base::Value* previous, const base::Value* current);
 
-  // An implementation of CertTrustAnchorProvider. Owned by this class, but
-  // runs and is deleted on the IO thread.
-  net::CertTrustAnchorProvider* cert_trust_provider_;
+  // Apply the observed policy, i.e. both networks and certificates.
+  void ApplyPolicy();
+
+  std::string LogHeader() const;
+
+  std::string policy_key_;
+
+  // Used to register for notifications from the |policy_service_|.
+  PolicyChangeRegistrar policy_change_registrar_;
+
+  // Used to retrieve the policies.
+  PolicyService* policy_service_;
 
   DISALLOW_COPY_AND_ASSIGN(NetworkConfigurationUpdater);
 };
diff --git a/chrome/browser/chromeos/policy/network_configuration_updater_impl.cc b/chrome/browser/chromeos/policy/network_configuration_updater_impl.cc
deleted file mode 100644
index c14a358..0000000
--- a/chrome/browser/chromeos/policy/network_configuration_updater_impl.cc
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright (c) 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 "chrome/browser/chromeos/policy/network_configuration_updater_impl.h"
-
-#include <string>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/logging.h"
-#include "base/values.h"
-#include "chrome/browser/chromeos/login/user.h"
-#include "chrome/browser/chromeos/net/onc_utils.h"
-#include "chrome/browser/policy/policy_map.h"
-#include "chromeos/network/managed_network_configuration_handler.h"
-#include "chromeos/network/onc/onc_certificate_importer.h"
-#include "chromeos/network/onc/onc_constants.h"
-#include "chromeos/network/onc/onc_utils.h"
-#include "policy/policy_constants.h"
-
-namespace policy {
-
-NetworkConfigurationUpdaterImpl::NetworkConfigurationUpdaterImpl(
-    PolicyService* device_policy_service,
-    chromeos::ManagedNetworkConfigurationHandler* network_config_handler,
-    scoped_ptr<chromeos::onc::CertificateImporter> certificate_importer)
-    : device_policy_change_registrar_(device_policy_service,
-                                      PolicyNamespace(POLICY_DOMAIN_CHROME,
-                                                      std::string())),
-      user_policy_service_(NULL),
-      device_policy_service_(device_policy_service),
-      user_(NULL),
-      network_config_handler_(network_config_handler),
-      certificate_importer_(certificate_importer.Pass()) {
-  device_policy_change_registrar_.Observe(
-      key::kDeviceOpenNetworkConfiguration,
-      base::Bind(&NetworkConfigurationUpdaterImpl::OnPolicyChanged,
-                 base::Unretained(this),
-                 chromeos::onc::ONC_SOURCE_DEVICE_POLICY));
-
-  if (device_policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME)) {
-    // Apply the current device policies immediately.
-    VLOG(1) << "Device policy service is already initialized.";
-    ApplyNetworkConfiguration(chromeos::onc::ONC_SOURCE_DEVICE_POLICY);
-  } else {
-    device_policy_service_->AddObserver(POLICY_DOMAIN_CHROME, this);
-  }
-}
-
-NetworkConfigurationUpdaterImpl::~NetworkConfigurationUpdaterImpl() {
-  DCHECK(!user_policy_service_);
-  DCHECK(!user_policy_change_registrar_);
-  device_policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, this);
-}
-
-void NetworkConfigurationUpdaterImpl::SetUserPolicyService(
-    bool allow_trusted_certs_from_policy,
-    const chromeos::User* user,
-    PolicyService* user_policy_service) {
-  VLOG(1) << "Got user policy service.";
-  user_policy_service_ = user_policy_service;
-  user_ = user;
-  if (allow_trusted_certs_from_policy)
-    SetAllowTrustedCertsFromPolicy();
-
-  user_policy_change_registrar_.reset(new PolicyChangeRegistrar(
-      user_policy_service_,
-      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string())));
-  user_policy_change_registrar_->Observe(
-      key::kOpenNetworkConfiguration,
-      base::Bind(&NetworkConfigurationUpdaterImpl::OnPolicyChanged,
-                 base::Unretained(this),
-                 chromeos::onc::ONC_SOURCE_USER_POLICY));
-
-  if (user_policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME)) {
-    VLOG(1) << "User policy service is already initialized.";
-    ApplyNetworkConfiguration(chromeos::onc::ONC_SOURCE_USER_POLICY);
-  } else {
-    user_policy_service_->AddObserver(POLICY_DOMAIN_CHROME, this);
-  }
-}
-
-void NetworkConfigurationUpdaterImpl::UnsetUserPolicyService() {
-  if (!user_policy_service_)
-    return;
-
-  user_policy_change_registrar_.reset();
-  user_policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, this);
-  user_policy_service_ = NULL;
-}
-
-void NetworkConfigurationUpdaterImpl::OnPolicyUpdated(
-    const PolicyNamespace& ns,
-    const PolicyMap& previous,
-    const PolicyMap& current) {
-  // Ignore this call. Policy changes are already observed by the registrar.
-}
-
-void NetworkConfigurationUpdaterImpl::OnPolicyServiceInitialized(
-    PolicyDomain domain) {
-  if (domain != POLICY_DOMAIN_CHROME)
-    return;
-
-  // We don't know which policy service called this function, thus check
-  // both. Multiple removes are handled gracefully.
-  if (device_policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME)) {
-    VLOG(1) << "Device policy service initialized.";
-    device_policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, this);
-    ApplyNetworkConfiguration(chromeos::onc::ONC_SOURCE_DEVICE_POLICY);
-  }
-  if (user_policy_service_ &&
-      user_policy_service_->IsInitializationComplete(POLICY_DOMAIN_CHROME)) {
-    VLOG(1) << "User policy service initialized.";
-    user_policy_service_->RemoveObserver(POLICY_DOMAIN_CHROME, this);
-    ApplyNetworkConfiguration(chromeos::onc::ONC_SOURCE_USER_POLICY);
-  }
-}
-
-void NetworkConfigurationUpdaterImpl::OnPolicyChanged(
-    chromeos::onc::ONCSource onc_source,
-    const base::Value* previous,
-    const base::Value* current) {
-  VLOG(1) << "Policy for ONC source "
-          << chromeos::onc::GetSourceAsString(onc_source) << " changed.";
-  ApplyNetworkConfiguration(onc_source);
-}
-
-void NetworkConfigurationUpdaterImpl::ApplyNetworkConfiguration(
-    chromeos::onc::ONCSource onc_source) {
-  VLOG(1) << "Apply policy for ONC source "
-          << chromeos::onc::GetSourceAsString(onc_source);
-
-  std::string policy_key;
-  PolicyService* policy_service;
-  if (onc_source == chromeos::onc::ONC_SOURCE_USER_POLICY) {
-    policy_key = key::kOpenNetworkConfiguration;
-    policy_service = user_policy_service_;
-  } else {
-    policy_key = key::kDeviceOpenNetworkConfiguration;
-    policy_service = device_policy_service_;
-  }
-
-  const PolicyMap& policies = policy_service->GetPolicies(
-      PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
-  const base::Value* policy_value = policies.GetValue(policy_key);
-
-  std::string onc_blob;
-  if (policy_value) {
-    if (!policy_value->GetAsString(&onc_blob))
-      LOG(ERROR) << "ONC policy " << policy_key << " is not a string value.";
-  } else {
-    VLOG(2) << "The policy is not set.";
-  }
-  VLOG(2) << "The policy contains this ONC: " << onc_blob;
-
-  base::ListValue network_configs;
-  base::ListValue certificates;
-  chromeos::onc::ParseAndValidateOncForImport(
-      onc_blob, onc_source, "", &network_configs, &certificates);
-
-  scoped_ptr<net::CertificateList> web_trust_certs(new net::CertificateList);
-  certificate_importer_->ImportCertificates(
-      certificates, onc_source, web_trust_certs.get());
-
-  std::string userhash;
-  if (onc_source == chromeos::onc::ONC_SOURCE_USER_POLICY && user_) {
-    userhash = user_->username_hash();
-    chromeos::onc::ExpandStringPlaceholdersInNetworksForUser(user_,
-                                                             &network_configs);
-  }
-
-  network_config_handler_->SetPolicy(onc_source, userhash, network_configs);
-
-  if (onc_source == chromeos::onc::ONC_SOURCE_USER_POLICY)
-    SetTrustAnchors(web_trust_certs.Pass());
-}
-
-}  // namespace policy
diff --git a/chrome/browser/chromeos/policy/network_configuration_updater_impl.h b/chrome/browser/chromeos/policy/network_configuration_updater_impl.h
deleted file mode 100644
index 3cbc216..0000000
--- a/chrome/browser/chromeos/policy/network_configuration_updater_impl.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright (c) 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 CHROME_BROWSER_CHROMEOS_POLICY_NETWORK_CONFIGURATION_UPDATER_IMPL_H_
-#define CHROME_BROWSER_CHROMEOS_POLICY_NETWORK_CONFIGURATION_UPDATER_IMPL_H_
-
-#include "chrome/browser/chromeos/policy/network_configuration_updater.h"
-#include "chrome/browser/policy/policy_service.h"
-#include "chromeos/network/onc/onc_constants.h"
-
-namespace base {
-class Value;
-}
-
-namespace chromeos {
-class ManagedNetworkConfigurationHandler;
-
-namespace onc {
-class CertificateImporter;
-}
-}
-
-namespace policy {
-
-class PolicyMap;
-
-// This implementation pushes policies to the
-// ManagedNetworkConfigurationHandler. User policies are only pushed after
-// OnUserPolicyInitialized() was called.
-class NetworkConfigurationUpdaterImpl : public NetworkConfigurationUpdater,
-                                        public PolicyService::Observer {
- public:
-  NetworkConfigurationUpdaterImpl(
-      PolicyService* device_policy_service,
-      chromeos::ManagedNetworkConfigurationHandler* network_config_handler,
-      scoped_ptr<chromeos::onc::CertificateImporter> certificate_importer);
-  virtual ~NetworkConfigurationUpdaterImpl();
-
-  // NetworkConfigurationUpdater overrides.
-  virtual void SetUserPolicyService(
-      bool allow_trusted_certs_from_policy,
-      const chromeos::User* user,
-      PolicyService* user_policy_service) OVERRIDE;
-
-  virtual void UnsetUserPolicyService() OVERRIDE;
-
-  // PolicyService::Observer overrides for both device and user policies.
-  virtual void OnPolicyUpdated(const PolicyNamespace& ns,
-                               const PolicyMap& previous,
-                               const PolicyMap& current) OVERRIDE;
-  virtual void OnPolicyServiceInitialized(PolicyDomain domain) OVERRIDE;
-
-  private:
-   // Called if the ONC policy from |onc_source| changed.
-   void OnPolicyChanged(chromeos::onc::ONCSource onc_source,
-                        const base::Value* previous,
-                        const base::Value* current);
-
-   void ApplyNetworkConfiguration(chromeos::onc::ONCSource onc_source);
-
-   // Used to register for notifications from the |user_policy_service_|.
-   scoped_ptr<PolicyChangeRegistrar> user_policy_change_registrar_;
-
-   // Used to register for notifications from the |device_policy_service_|.
-   PolicyChangeRegistrar device_policy_change_registrar_;
-
-   // Used to retrieve user policies.
-   PolicyService* user_policy_service_;
-
-   // Used to retrieve device policies.
-   PolicyService* device_policy_service_;
-
-   // The user for whom the user policy will be applied. The User object must be
-   // valid until UnsetUserPolicyService() is called.
-   const chromeos::User* user_;
-
-   // Pointer to the global singleton or a test instance.
-   chromeos::ManagedNetworkConfigurationHandler* network_config_handler_;
-
-   scoped_ptr<chromeos::onc::CertificateImporter> certificate_importer_;
-
-   DISALLOW_COPY_AND_ASSIGN(NetworkConfigurationUpdaterImpl);
-};
-
-}  // namespace policy
-
-#endif  // CHROME_BROWSER_CHROMEOS_POLICY_NETWORK_CONFIGURATION_UPDATER_IMPL_H_
diff --git a/chrome/browser/chromeos/policy/network_configuration_updater_impl_unittest.cc b/chrome/browser/chromeos/policy/network_configuration_updater_impl_unittest.cc
deleted file mode 100644
index 3d60a72..0000000
--- a/chrome/browser/chromeos/policy/network_configuration_updater_impl_unittest.cc
+++ /dev/null
@@ -1,395 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/policy/network_configuration_updater_impl.h"
-
-#include "base/callback.h"
-#include "base/files/file_path.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "base/values.h"
-#include "chrome/browser/chromeos/login/user.h"
-#include "chrome/browser/policy/external_data_fetcher.h"
-#include "chrome/browser/policy/mock_configuration_policy_provider.h"
-#include "chrome/browser/policy/policy_map.h"
-#include "chrome/browser/policy/policy_service_impl.h"
-#include "chromeos/network/mock_managed_network_configuration_handler.h"
-#include "chromeos/network/onc/mock_certificate_importer.h"
-#include "chromeos/network/onc/onc_constants.h"
-#include "chromeos/network/onc/onc_test_utils.h"
-#include "chromeos/network/onc/onc_utils.h"
-#include "content/public/test/test_browser_thread.h"
-#include "content/public/test/test_utils.h"
-#include "net/base/test_data_directory.h"
-#include "net/cert/cert_trust_anchor_provider.h"
-#include "net/cert/x509_certificate.h"
-#include "net/test/cert_test_util.h"
-#include "policy/policy_constants.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::AnyNumber;
-using testing::Mock;
-using testing::Ne;
-using testing::Return;
-using testing::StrictMock;
-using testing::_;
-
-namespace onc = ::chromeos::onc;
-
-namespace policy {
-
-namespace {
-const char kFakeUserEmail[] = "fake email";
-const char kFakeUsernameHash[] = "fake hash";
-
-class FakeUser : public chromeos::User {
- public:
-  FakeUser() : User(kFakeUserEmail) {
-    set_display_email(kFakeUserEmail);
-    set_username_hash(kFakeUsernameHash);
-  }
-  virtual ~FakeUser() {}
-
-  // User overrides
-  virtual UserType GetType() const OVERRIDE {
-    return USER_TYPE_REGULAR;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(FakeUser);
-};
-
-const char kFakeONC[] =
-    "{ \"NetworkConfigurations\": ["
-    "    { \"GUID\": \"{485d6076-dd44-6b6d-69787465725f5040}\","
-    "      \"Type\": \"WiFi\","
-    "      \"Name\": \"My WiFi Network\","
-    "      \"WiFi\": {"
-    "        \"SSID\": \"ssid-none\","
-    "        \"Security\": \"None\" }"
-    "    }"
-    "  ],"
-    "  \"Certificates\": ["
-    "    { \"GUID\": \"{f998f760-272b-6939-4c2beffe428697ac}\","
-    "      \"PKCS12\": \"abc\","
-    "       \"Type\": \"Client\" }"
-    "  ],"
-    "  \"Type\": \"UnencryptedConfiguration\""
-    "}";
-
-std::string ValueToString(const base::Value* value) {
-    std::stringstream str;
-    str << *value;
-    return str.str();
-}
-
-// Matcher to match base::Value.
-MATCHER_P(IsEqualTo,
-          value,
-          std::string(negation ? "isn't" : "is") + " equal to " +
-          ValueToString(value)) {
-  return value->Equals(&arg);
-}
-
-ACTION_P(SetCertificateList, list) {
-  *arg2 = list;
-  return true;
-}
-
-ACTION_P(SetImportedCerts, map) {
-  *arg3 = map;
-  return true;
-}
-
-}  // namespace
-
-class NetworkConfigurationUpdaterTest : public testing::Test {
- protected:
-  NetworkConfigurationUpdaterTest()
-      : ui_thread_(content::BrowserThread::UI, &loop_),
-        io_thread_(content::BrowserThread::IO, &loop_) {}
-
-  virtual void SetUp() OVERRIDE {
-    EXPECT_CALL(provider_, IsInitializationComplete(_))
-        .WillRepeatedly(Return(true));
-    provider_.Init();
-    PolicyServiceImpl::Providers providers;
-    providers.push_back(&provider_);
-    policy_service_.reset(new PolicyServiceImpl(providers));
-
-    empty_network_configs_.reset(new base::ListValue);
-    empty_certificates_.reset(new base::ListValue);
-
-    scoped_ptr<base::DictionaryValue> fake_toplevel_onc =
-        onc::ReadDictionaryFromJson(kFakeONC);
-
-    scoped_ptr<base::Value> network_configs_value;
-    base::ListValue* network_configs = NULL;
-    fake_toplevel_onc->RemoveWithoutPathExpansion(
-        onc::toplevel_config::kNetworkConfigurations, &network_configs_value);
-    network_configs_value.release()->GetAsList(&network_configs);
-    fake_network_configs_.reset(network_configs);
-
-    scoped_ptr<base::Value> certs_value;
-    base::ListValue* certs = NULL;
-    fake_toplevel_onc->RemoveWithoutPathExpansion(
-        onc::toplevel_config::kCertificates, &certs_value);
-    certs_value.release()->GetAsList(&certs);
-    fake_certificates_.reset(certs);
-  }
-
-  virtual void TearDown() OVERRIDE {
-    provider_.Shutdown();
-    content::RunAllPendingInMessageLoop(content::BrowserThread::IO);
-  }
-
-  void UpdateProviderPolicy(const PolicyMap& policy) {
-    provider_.UpdateChromePolicy(policy);
-    base::RunLoop loop;
-    loop.RunUntilIdle();
-  }
-
-  scoped_ptr<base::ListValue> empty_network_configs_;
-  scoped_ptr<base::ListValue> empty_certificates_;
-  scoped_ptr<base::ListValue> fake_network_configs_;
-  scoped_ptr<base::ListValue> fake_certificates_;
-  StrictMock<chromeos::MockManagedNetworkConfigurationHandler>
-      network_config_handler_;
-  StrictMock<MockConfigurationPolicyProvider> provider_;
-  scoped_ptr<PolicyServiceImpl> policy_service_;
-  FakeUser fake_user_;
-  base::MessageLoop loop_;
-  content::TestBrowserThread ui_thread_;
-  content::TestBrowserThread io_thread_;
-};
-
-TEST_F(NetworkConfigurationUpdaterTest, PolicyIsValidatedAndRepaired) {
-  std::string onc_policy =
-      onc::test_utils::ReadTestData("toplevel_partially_invalid.onc");
-
-  scoped_ptr<base::DictionaryValue> onc_repaired =
-      onc::test_utils::ReadTestDictionary(
-          "repaired_toplevel_partially_invalid.onc");
-
-  base::ListValue* network_configs_repaired = NULL;
-  onc_repaired->GetListWithoutPathExpansion(
-      onc::toplevel_config::kNetworkConfigurations, &network_configs_repaired);
-  ASSERT_TRUE(network_configs_repaired);
-
-  PolicyMap policy;
-  policy.Set(key::kOpenNetworkConfiguration, POLICY_LEVEL_MANDATORY,
-             POLICY_SCOPE_USER, Value::CreateStringValue(onc_policy), NULL);
-  UpdateProviderPolicy(policy);
-
-  // Ignore the device policy update.
-  EXPECT_CALL(network_config_handler_, SetPolicy(_, _, _));
-  StrictMock<chromeos::onc::MockCertificateImporter>* certificate_importer =
-      new StrictMock<chromeos::onc::MockCertificateImporter>();
-  EXPECT_CALL(*certificate_importer, ImportCertificates(_, _, _));
-
-  NetworkConfigurationUpdaterImpl updater(
-      policy_service_.get(),
-      &network_config_handler_,
-      make_scoped_ptr<chromeos::onc::CertificateImporter>(
-          certificate_importer));
-  Mock::VerifyAndClearExpectations(&network_config_handler_);
-  Mock::VerifyAndClearExpectations(&certificate_importer);
-
-  // After the user policy is initialized.
-  EXPECT_CALL(
-      network_config_handler_,
-      SetPolicy(
-          onc::ONC_SOURCE_USER_POLICY, _, IsEqualTo(network_configs_repaired)));
-  EXPECT_CALL(*certificate_importer,
-              ImportCertificates(_, chromeos::onc::ONC_SOURCE_USER_POLICY, _));
-
-  updater.SetUserPolicyService(false, &fake_user_, policy_service_.get());
-  updater.UnsetUserPolicyService();
-}
-
-class NetworkConfigurationUpdaterTestWithParam
-    : public NetworkConfigurationUpdaterTest,
-      public testing::WithParamInterface<const char*> {
- public:
-  // Returns the currently tested ONC source.
-  onc::ONCSource CurrentONCSource() {
-    if (GetParam() == key::kDeviceOpenNetworkConfiguration)
-      return onc::ONC_SOURCE_DEVICE_POLICY;
-    if (GetParam() == key::kOpenNetworkConfiguration)
-      return onc::ONC_SOURCE_USER_POLICY;
-    return onc::ONC_SOURCE_NONE;
-  }
-};
-
-TEST_P(NetworkConfigurationUpdaterTestWithParam, InitialUpdates) {
-  PolicyMap policy;
-  policy.Set(GetParam(), POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-             Value::CreateStringValue(kFakeONC), NULL);
-  UpdateProviderPolicy(policy);
-
-  // Initially, only the device policy is applied. The user policy is only
-  // applied after the user profile was initialized.
-  base::ListValue* device_networks;
-  base::ListValue* device_certs;
-  base::ListValue* user_networks;
-  base::ListValue* user_certs;
-  if (GetParam() == key::kDeviceOpenNetworkConfiguration) {
-    device_networks = fake_network_configs_.get();
-    device_certs = fake_certificates_.get();
-    user_networks = empty_network_configs_.get();
-    user_certs = empty_certificates_.get();
-  } else {
-    device_networks = empty_network_configs_.get();
-    device_certs = empty_certificates_.get();
-    user_networks = fake_network_configs_.get();
-    user_certs = fake_certificates_.get();
-  }
-
-  EXPECT_CALL(
-      network_config_handler_,
-      SetPolicy(onc::ONC_SOURCE_DEVICE_POLICY, _, IsEqualTo(device_networks)));
-  StrictMock<chromeos::onc::MockCertificateImporter>* certificate_importer =
-      new StrictMock<chromeos::onc::MockCertificateImporter>();
-  EXPECT_CALL(*certificate_importer, ImportCertificates(
-      IsEqualTo(device_certs), onc::ONC_SOURCE_DEVICE_POLICY, _));
-
-  NetworkConfigurationUpdaterImpl updater(
-      policy_service_.get(),
-      &network_config_handler_,
-      make_scoped_ptr<chromeos::onc::CertificateImporter>(
-          certificate_importer));
-  Mock::VerifyAndClearExpectations(&network_config_handler_);
-  Mock::VerifyAndClearExpectations(&certificate_importer);
-
-  EXPECT_CALL(
-      network_config_handler_,
-      SetPolicy(onc::ONC_SOURCE_USER_POLICY, _, IsEqualTo(user_networks)));
-  EXPECT_CALL(*certificate_importer, ImportCertificates(
-      IsEqualTo(user_certs), onc::ONC_SOURCE_USER_POLICY, _));
-
-  // We just need an initialized PolicyService, so we can reuse
-  // |policy_service_|.
-  updater.SetUserPolicyService(false, &fake_user_, policy_service_.get());
-  updater.UnsetUserPolicyService();
-}
-
-TEST_P(NetworkConfigurationUpdaterTestWithParam,
-       AllowTrustedCertificatesFromPolicy) {
-  const net::CertificateList empty_cert_list;
-
-  EXPECT_CALL(network_config_handler_, SetPolicy(_, _, _)).Times(AnyNumber());
-  StrictMock<chromeos::onc::MockCertificateImporter>* certificate_importer =
-      new StrictMock<chromeos::onc::MockCertificateImporter>();
-  EXPECT_CALL(*certificate_importer, ImportCertificates(_, _, _))
-      .WillRepeatedly(SetCertificateList(empty_cert_list));
-  NetworkConfigurationUpdaterImpl updater(
-      policy_service_.get(),
-      &network_config_handler_,
-      make_scoped_ptr<chromeos::onc::CertificateImporter>(
-          certificate_importer));
-  net::CertTrustAnchorProvider* trust_provider =
-      updater.GetCertTrustAnchorProvider();
-  ASSERT_TRUE(trust_provider);
-  // The initial list of trust anchors is empty.
-  content::RunAllPendingInMessageLoop(content::BrowserThread::IO);
-  EXPECT_TRUE(trust_provider->GetAdditionalTrustAnchors().empty());
-
-  // Initially, certificates imported from policy don't have trust flags.
-  updater.SetUserPolicyService(false, &fake_user_, policy_service_.get());
-  content::RunAllPendingInMessageLoop(content::BrowserThread::IO);
-  Mock::VerifyAndClearExpectations(&network_config_handler_);
-  Mock::VerifyAndClearExpectations(&certificate_importer);
-  EXPECT_TRUE(trust_provider->GetAdditionalTrustAnchors().empty());
-
-  // Certificates with the "Web" trust flag set should be forwarded to the
-  // trust provider.
-  EXPECT_CALL(network_config_handler_,
-              SetPolicy(chromeos::onc::ONC_SOURCE_USER_POLICY, _, _));
-
-  net::CertificateList cert_list;
-  if (GetParam() == key::kOpenNetworkConfiguration) {
-    cert_list =
-        net::CreateCertificateListFromFile(net::GetTestCertsDirectory(),
-                                           "ok_cert.pem",
-                                           net::X509Certificate::FORMAT_AUTO);
-    ASSERT_EQ(1u, cert_list.size());
-  }
-  EXPECT_CALL(*certificate_importer,
-              ImportCertificates(_, chromeos::onc::ONC_SOURCE_USER_POLICY, _))
-      .WillRepeatedly(SetCertificateList(cert_list));
-
-  // Trigger a new policy load, and spin the IO message loop to pass the
-  // certificates to the |trust_provider| on the IO thread.
-  updater.SetUserPolicyService(true, &fake_user_, policy_service_.get());
-  base::RunLoop loop;
-  loop.RunUntilIdle();
-  Mock::VerifyAndClearExpectations(&network_config_handler_);
-  Mock::VerifyAndClearExpectations(&certificate_importer);
-
-  // Certificates are only provided as trust anchors if they come from user
-  // policy.
-  size_t expected_certs = 0u;
-  if (GetParam() == key::kOpenNetworkConfiguration)
-    expected_certs = 1u;
-  EXPECT_EQ(expected_certs,
-            trust_provider->GetAdditionalTrustAnchors().size());
-
-  updater.UnsetUserPolicyService();
-}
-
-TEST_P(NetworkConfigurationUpdaterTestWithParam, PolicyChange) {
-  // Ignore the initial updates.
-  EXPECT_CALL(network_config_handler_, SetPolicy(_, _, _)).Times(AnyNumber());
-  StrictMock<chromeos::onc::MockCertificateImporter>* certificate_importer =
-      new StrictMock<chromeos::onc::MockCertificateImporter>();
-  EXPECT_CALL(*certificate_importer, ImportCertificates(_, _, _))
-      .Times(AnyNumber());
-  NetworkConfigurationUpdaterImpl updater(
-      policy_service_.get(),
-      &network_config_handler_,
-      make_scoped_ptr<chromeos::onc::CertificateImporter>(
-          certificate_importer));
-  updater.SetUserPolicyService(false, &fake_user_, policy_service_.get());
-  Mock::VerifyAndClearExpectations(&network_config_handler_);
-  Mock::VerifyAndClearExpectations(&certificate_importer);
-
-  // We should update if policy changes.
-  EXPECT_CALL(network_config_handler_,
-              SetPolicy(CurrentONCSource(),
-                        _,
-                        IsEqualTo(fake_network_configs_.get())));
-  EXPECT_CALL(*certificate_importer, ImportCertificates(
-      IsEqualTo(fake_certificates_.get()), CurrentONCSource(), _));
-
-  PolicyMap policy;
-  policy.Set(GetParam(), POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-             Value::CreateStringValue(kFakeONC), NULL);
-  UpdateProviderPolicy(policy);
-  Mock::VerifyAndClearExpectations(&network_config_handler_);
-  Mock::VerifyAndClearExpectations(&certificate_importer);
-
-  // Another update is expected if the policy goes away.
-  EXPECT_CALL(network_config_handler_,
-              SetPolicy(CurrentONCSource(),
-                        _,
-                        IsEqualTo(empty_network_configs_.get())));
-  EXPECT_CALL(*certificate_importer, ImportCertificates(
-      IsEqualTo(empty_certificates_.get()),
-      CurrentONCSource(),
-      _));
-
-  policy.Erase(GetParam());
-  UpdateProviderPolicy(policy);
-  updater.UnsetUserPolicyService();
-}
-
-INSTANTIATE_TEST_CASE_P(
-    NetworkConfigurationUpdaterTestWithParamInstance,
-    NetworkConfigurationUpdaterTestWithParam,
-    testing::Values(key::kDeviceOpenNetworkConfiguration,
-                    key::kOpenNetworkConfiguration));
-
-}  // namespace policy
diff --git a/chrome/browser/chromeos/policy/network_configuration_updater_unittest.cc b/chrome/browser/chromeos/policy/network_configuration_updater_unittest.cc
new file mode 100644
index 0000000..53cc0b5
--- /dev/null
+++ b/chrome/browser/chromeos/policy/network_configuration_updater_unittest.cc
@@ -0,0 +1,380 @@
+// 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 "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/run_loop.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/login/user.h"
+#include "chrome/browser/chromeos/policy/policy_cert_verifier.h"
+#include "chrome/browser/chromeos/policy/user_network_configuration_updater.h"
+#include "chrome/browser/policy/external_data_fetcher.h"
+#include "chrome/browser/policy/mock_configuration_policy_provider.h"
+#include "chrome/browser/policy/policy_map.h"
+#include "chrome/browser/policy/policy_service_impl.h"
+#include "chromeos/network/mock_managed_network_configuration_handler.h"
+#include "chromeos/network/onc/mock_certificate_importer.h"
+#include "chromeos/network/onc/onc_constants.h"
+#include "chromeos/network/onc/onc_test_utils.h"
+#include "chromeos/network/onc/onc_utils.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_utils.h"
+#include "net/base/test_data_directory.h"
+#include "net/cert/x509_certificate.h"
+#include "net/test/cert_test_util.h"
+#include "policy/policy_constants.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::AnyNumber;
+using testing::AtLeast;
+using testing::Mock;
+using testing::Ne;
+using testing::Return;
+using testing::StrictMock;
+using testing::_;
+
+namespace onc = ::chromeos::onc;
+
+namespace policy {
+
+namespace {
+
+const char kFakeUserEmail[] = "fake email";
+const char kFakeUsernameHash[] = "fake hash";
+
+class FakeUser : public chromeos::User {
+ public:
+  FakeUser() : User(kFakeUserEmail) {
+    set_display_email(kFakeUserEmail);
+    set_username_hash(kFakeUsernameHash);
+  }
+  virtual ~FakeUser() {}
+
+  // User overrides
+  virtual UserType GetType() const OVERRIDE {
+    return USER_TYPE_REGULAR;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FakeUser);
+};
+
+const char kFakeONC[] =
+    "{ \"NetworkConfigurations\": ["
+    "    { \"GUID\": \"{485d6076-dd44-6b6d-69787465725f5040}\","
+    "      \"Type\": \"WiFi\","
+    "      \"Name\": \"My WiFi Network\","
+    "      \"WiFi\": {"
+    "        \"SSID\": \"ssid-none\","
+    "        \"Security\": \"None\" }"
+    "    }"
+    "  ],"
+    "  \"Certificates\": ["
+    "    { \"GUID\": \"{f998f760-272b-6939-4c2beffe428697ac}\","
+    "      \"PKCS12\": \"abc\","
+    "       \"Type\": \"Client\" }"
+    "  ],"
+    "  \"Type\": \"UnencryptedConfiguration\""
+    "}";
+
+std::string ValueToString(const base::Value* value) {
+  std::stringstream str;
+  str << *value;
+  return str.str();
+}
+
+// Matcher to match base::Value.
+MATCHER_P(IsEqualTo,
+          value,
+          std::string(negation ? "isn't" : "is") + " equal to " +
+              ValueToString(value)) {
+  return value->Equals(&arg);
+}
+
+ACTION_P(SetCertificateList, list) {
+  if (arg2)
+    *arg2 = list;
+  return true;
+}
+
+}  // namespace
+
+class NetworkConfigurationUpdaterTest : public testing::Test {
+ protected:
+  NetworkConfigurationUpdaterTest() {
+  }
+
+  virtual void SetUp() OVERRIDE {
+    EXPECT_CALL(provider_, IsInitializationComplete(_))
+        .WillRepeatedly(Return(true));
+    provider_.Init();
+    PolicyServiceImpl::Providers providers;
+    providers.push_back(&provider_);
+    policy_service_.reset(new PolicyServiceImpl(providers));
+
+    empty_network_configs_.reset(new base::ListValue);
+    empty_certificates_.reset(new base::ListValue);
+
+    scoped_ptr<base::DictionaryValue> fake_toplevel_onc =
+        onc::ReadDictionaryFromJson(kFakeONC);
+
+    scoped_ptr<base::Value> network_configs_value;
+    base::ListValue* network_configs = NULL;
+    fake_toplevel_onc->RemoveWithoutPathExpansion(
+        onc::toplevel_config::kNetworkConfigurations, &network_configs_value);
+    network_configs_value.release()->GetAsList(&network_configs);
+    fake_network_configs_.reset(network_configs);
+
+    scoped_ptr<base::Value> certs_value;
+    base::ListValue* certs = NULL;
+    fake_toplevel_onc->RemoveWithoutPathExpansion(
+        onc::toplevel_config::kCertificates, &certs_value);
+    certs_value.release()->GetAsList(&certs);
+    fake_certificates_.reset(certs);
+
+    certificate_importer_ = new StrictMock<onc::MockCertificateImporter>();
+    certificate_importer_owned_.reset(certificate_importer_);
+  }
+
+  virtual void TearDown() OVERRIDE {
+    network_configuration_updater_.reset();
+    provider_.Shutdown();
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void UpdateProviderPolicy(const PolicyMap& policy) {
+    provider_.UpdateChromePolicy(policy);
+    base::RunLoop().RunUntilIdle();
+  }
+
+  UserNetworkConfigurationUpdater*
+  CreateNetworkConfigurationUpdaterForUserPolicy(
+      bool allow_trusted_certs_from_policy) {
+    UserNetworkConfigurationUpdater* updater =
+        UserNetworkConfigurationUpdater::CreateForUserPolicy(
+            allow_trusted_certs_from_policy,
+            fake_user_,
+            certificate_importer_owned_.Pass(),
+            policy_service_.get(),
+            &network_config_handler_).release();
+    network_configuration_updater_.reset(updater);
+    return updater;
+  }
+
+  void CreateNetworkConfigurationUpdaterForDevicePolicy() {
+    network_configuration_updater_ =
+        NetworkConfigurationUpdater::CreateForDevicePolicy(
+            certificate_importer_owned_.Pass(),
+            policy_service_.get(),
+            &network_config_handler_);
+  }
+
+  scoped_ptr<base::ListValue> empty_network_configs_;
+  scoped_ptr<base::ListValue> empty_certificates_;
+  scoped_ptr<base::ListValue> fake_network_configs_;
+  scoped_ptr<base::ListValue> fake_certificates_;
+  StrictMock<chromeos::MockManagedNetworkConfigurationHandler>
+      network_config_handler_;
+
+  // Ownership of certificate_importer_owned_ is passed to the
+  // NetworkConfigurationUpdater. When that happens, |certificate_importer_|
+  // continues to point to that instance but |certificate_importer_owned_| is
+  // released.
+  StrictMock<onc::MockCertificateImporter>* certificate_importer_;
+  scoped_ptr<onc::CertificateImporter> certificate_importer_owned_;
+
+  StrictMock<MockConfigurationPolicyProvider> provider_;
+  scoped_ptr<PolicyServiceImpl> policy_service_;
+  FakeUser fake_user_;
+
+  scoped_ptr<NetworkConfigurationUpdater> network_configuration_updater_;
+  content::TestBrowserThreadBundle thread_bundle_;
+};
+
+TEST_F(NetworkConfigurationUpdaterTest, PolicyIsValidatedAndRepaired) {
+  std::string onc_policy =
+      onc::test_utils::ReadTestData("toplevel_partially_invalid.onc");
+
+  scoped_ptr<base::DictionaryValue> onc_repaired =
+      onc::test_utils::ReadTestDictionary(
+          "repaired_toplevel_partially_invalid.onc");
+
+  base::ListValue* network_configs_repaired = NULL;
+  onc_repaired->GetListWithoutPathExpansion(
+      onc::toplevel_config::kNetworkConfigurations, &network_configs_repaired);
+  ASSERT_TRUE(network_configs_repaired);
+
+  PolicyMap policy;
+  policy.Set(key::kOpenNetworkConfiguration,
+             POLICY_LEVEL_MANDATORY,
+             POLICY_SCOPE_USER,
+             new base::StringValue(onc_policy),
+             NULL);
+  UpdateProviderPolicy(policy);
+
+  EXPECT_CALL(
+      network_config_handler_,
+      SetPolicy(
+          onc::ONC_SOURCE_USER_POLICY, _, IsEqualTo(network_configs_repaired)));
+  EXPECT_CALL(*certificate_importer_,
+              ImportCertificates(_, onc::ONC_SOURCE_USER_POLICY, _));
+
+  CreateNetworkConfigurationUpdaterForUserPolicy(
+      false /* do not allow trusted certs from policy */ );
+}
+
+TEST_F(NetworkConfigurationUpdaterTest,
+       DoNotAllowTrustedCertificatesFromPolicy) {
+  net::CertificateList cert_list;
+  cert_list =
+      net::CreateCertificateListFromFile(net::GetTestCertsDirectory(),
+                                         "ok_cert.pem",
+                                         net::X509Certificate::FORMAT_AUTO);
+  ASSERT_EQ(1u, cert_list.size());
+
+  EXPECT_CALL(network_config_handler_,
+              SetPolicy(onc::ONC_SOURCE_USER_POLICY, _, _));
+  EXPECT_CALL(*certificate_importer_, ImportCertificates(_, _, _))
+      .WillRepeatedly(SetCertificateList(cert_list));
+
+  UserNetworkConfigurationUpdater* updater =
+      CreateNetworkConfigurationUpdaterForUserPolicy(
+          false /* do not allow trusted certs from policy */);
+
+  // Certificates with the "Web" trust flag set should not be forwarded to the
+  // trust provider.
+  policy::PolicyCertVerifier cert_verifier((
+      base::Closure() /* no policy cert trusted callback */));
+  updater->SetPolicyCertVerifier(&cert_verifier);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(cert_verifier.GetAdditionalTrustAnchors().empty());
+
+  // |cert_verifier| must outlive the updater.
+  network_configuration_updater_.reset();
+}
+
+TEST_F(NetworkConfigurationUpdaterTest, AllowTrustedCertificatesFromPolicy) {
+  net::CertificateList cert_list;
+  cert_list =
+      net::CreateCertificateListFromFile(net::GetTestCertsDirectory(),
+                                         "ok_cert.pem",
+                                         net::X509Certificate::FORMAT_AUTO);
+  ASSERT_EQ(1u, cert_list.size());
+
+  EXPECT_CALL(network_config_handler_,
+              SetPolicy(onc::ONC_SOURCE_USER_POLICY, _, _));
+  EXPECT_CALL(*certificate_importer_,
+              ImportCertificates(_, onc::ONC_SOURCE_USER_POLICY, _))
+      .WillRepeatedly(SetCertificateList(cert_list));
+
+  UserNetworkConfigurationUpdater* updater =
+      CreateNetworkConfigurationUpdaterForUserPolicy(
+          true /* allow trusted certs from policy */);
+
+  // Certificates with the "Web" trust flag set should be forwarded to the
+  // trust provider.
+  policy::PolicyCertVerifier cert_verifier((
+      base::Closure() /* no policy cert trusted callback */));
+  updater->SetPolicyCertVerifier(&cert_verifier);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1u, cert_verifier.GetAdditionalTrustAnchors().size());
+
+  // |cert_verifier| must outlive the updater.
+  network_configuration_updater_.reset();
+}
+
+class NetworkConfigurationUpdaterTestWithParam
+    : public NetworkConfigurationUpdaterTest,
+      public testing::WithParamInterface<const char*> {
+ protected:
+  // Returns the currently tested ONC source.
+  onc::ONCSource CurrentONCSource() {
+    if (GetParam() == key::kOpenNetworkConfiguration)
+      return onc::ONC_SOURCE_USER_POLICY;
+    DCHECK(GetParam() == key::kDeviceOpenNetworkConfiguration);
+    return onc::ONC_SOURCE_DEVICE_POLICY;
+  }
+
+  // Returns the expected username hash to push policies to
+  // ManagedNetworkConfigurationHandler.
+  std::string ExpectedUsernameHash() {
+    if (GetParam() == key::kOpenNetworkConfiguration)
+      return kFakeUsernameHash;
+    return std::string();
+  }
+
+  void CreateNetworkConfigurationUpdater() {
+    if (GetParam() == key::kOpenNetworkConfiguration) {
+      CreateNetworkConfigurationUpdaterForUserPolicy(
+          false /* do not allow trusted certs from policy */);
+    } else {
+      CreateNetworkConfigurationUpdaterForDevicePolicy();
+    }
+  }
+};
+
+TEST_P(NetworkConfigurationUpdaterTestWithParam, InitialUpdates) {
+  PolicyMap policy;
+  policy.Set(GetParam(), POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+             new base::StringValue(kFakeONC), NULL);
+  UpdateProviderPolicy(policy);
+
+  EXPECT_CALL(network_config_handler_,
+              SetPolicy(CurrentONCSource(),
+                        ExpectedUsernameHash(),
+                        IsEqualTo(fake_network_configs_.get())));
+  EXPECT_CALL(*certificate_importer_,
+              ImportCertificates(
+                  IsEqualTo(fake_certificates_.get()), CurrentONCSource(), _));
+
+  CreateNetworkConfigurationUpdater();
+}
+
+
+TEST_P(NetworkConfigurationUpdaterTestWithParam, PolicyChange) {
+  // Ignore the initial updates.
+  EXPECT_CALL(network_config_handler_, SetPolicy(_, _, _)).Times(AtLeast(1));
+  EXPECT_CALL(*certificate_importer_, ImportCertificates(_, _, _))
+      .Times(AtLeast(1));
+  CreateNetworkConfigurationUpdater();
+  Mock::VerifyAndClearExpectations(&network_config_handler_);
+  Mock::VerifyAndClearExpectations(certificate_importer_);
+
+  // The Updater should update if policy changes.
+  EXPECT_CALL(
+      network_config_handler_,
+      SetPolicy(CurrentONCSource(), _, IsEqualTo(fake_network_configs_.get())));
+  EXPECT_CALL(*certificate_importer_,
+              ImportCertificates(
+                  IsEqualTo(fake_certificates_.get()), CurrentONCSource(), _));
+
+  PolicyMap policy;
+  policy.Set(GetParam(), POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+             new base::StringValue(kFakeONC), NULL);
+  UpdateProviderPolicy(policy);
+  Mock::VerifyAndClearExpectations(&network_config_handler_);
+  Mock::VerifyAndClearExpectations(certificate_importer_);
+
+  // Another update is expected if the policy goes away.
+  EXPECT_CALL(
+      network_config_handler_,
+      SetPolicy(
+          CurrentONCSource(), _, IsEqualTo(empty_network_configs_.get())));
+  EXPECT_CALL(*certificate_importer_,
+              ImportCertificates(
+                  IsEqualTo(empty_certificates_.get()), CurrentONCSource(), _));
+
+  policy.Erase(GetParam());
+  UpdateProviderPolicy(policy);
+}
+
+INSTANTIATE_TEST_CASE_P(NetworkConfigurationUpdaterTestWithParamInstance,
+                        NetworkConfigurationUpdaterTestWithParam,
+                        testing::Values(key::kDeviceOpenNetworkConfiguration,
+                                        key::kOpenNetworkConfiguration));
+
+}  // namespace policy
diff --git a/chrome/browser/chromeos/policy/policy_cert_verifier.cc b/chrome/browser/chromeos/policy/policy_cert_verifier.cc
index 648c138..307cdd0 100644
--- a/chrome/browser/chromeos/policy/policy_cert_verifier.cc
+++ b/chrome/browser/chromeos/policy/policy_cert_verifier.cc
@@ -6,11 +6,7 @@
 
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
-#include "base/prefs/pref_service.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/common/pref_names.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/base/net_errors.h"
 #include "net/cert/cert_verify_proc.h"
@@ -20,40 +16,41 @@
 
 namespace {
 
-void TaintProfile(void* profile_ptr) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  Profile* profile = reinterpret_cast<Profile*>(profile_ptr);
-  if (!g_browser_process->profile_manager()->IsValidProfile(profile))
+void MaybeSignalAnchorUse(int error,
+                          const base::Closure& anchor_used_callback,
+                          const net::CertVerifyResult& verify_result) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  if (error != net::OK || !verify_result.is_issued_by_additional_trust_anchor ||
+      anchor_used_callback.is_null()) {
     return;
-  profile->GetPrefs()->SetBoolean(prefs::kUsedPolicyCertificatesOnce, true);
-}
-
-void MaybeTaintProfile(const net::CertVerifyResult& verify_result,
-                       void* profile) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-  if (verify_result.is_issued_by_additional_trust_anchor) {
-    content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
-                                     base::Bind(&TaintProfile, profile));
   }
+  anchor_used_callback.Run();
 }
 
-void CallbackWrapper(void* profile,
-                     const net::CertVerifyResult* verify_result,
-                     const net::CompletionCallback& original_callback,
-                     int error) {
+void CompleteAndSignalAnchorUse(
+    const base::Closure& anchor_used_callback,
+    const net::CompletionCallback& completion_callback,
+    const net::CertVerifyResult* verify_result,
+    int error) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-  if (error == net::OK)
-    MaybeTaintProfile(*verify_result, profile);
-  if (!original_callback.is_null())
-    original_callback.Run(error);
+  MaybeSignalAnchorUse(error, anchor_used_callback, *verify_result);
+  if (!completion_callback.is_null())
+    completion_callback.Run(error);
 }
 
 }  // namespace
 
 PolicyCertVerifier::PolicyCertVerifier(
-    void* profile,
-    net::CertTrustAnchorProvider* trust_anchor_provider)
-    : profile_(profile) {
+    const base::Closure& anchor_used_callback)
+    : anchor_used_callback_(anchor_used_callback) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+}
+
+PolicyCertVerifier::~PolicyCertVerifier() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+}
+
+void PolicyCertVerifier::InitializeOnIOThread() {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
   scoped_refptr<net::CertVerifyProc> verify_proc =
       net::CertVerifyProc::CreateDefault();
@@ -63,29 +60,35 @@
   }
   net::MultiThreadedCertVerifier* verifier =
       new net::MultiThreadedCertVerifier(verify_proc.get());
-  verifier->SetCertTrustAnchorProvider(trust_anchor_provider);
+  verifier->SetCertTrustAnchorProvider(this);
   delegate_.reset(verifier);
 }
 
-PolicyCertVerifier::~PolicyCertVerifier() {
+void PolicyCertVerifier::SetTrustAnchors(
+    const net::CertificateList& trust_anchors) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  trust_anchors_ = trust_anchors;
 }
 
-int PolicyCertVerifier::Verify(net::X509Certificate* cert,
-                               const std::string& hostname,
-                               int flags,
-                               net::CRLSet* crl_set,
-                               net::CertVerifyResult* verify_result,
-                               const net::CompletionCallback& callback,
-                               RequestHandle* out_req,
-                               const net::BoundNetLog& net_log) {
+int PolicyCertVerifier::Verify(
+    net::X509Certificate* cert,
+    const std::string& hostname,
+    int flags,
+    net::CRLSet* crl_set,
+    net::CertVerifyResult* verify_result,
+    const net::CompletionCallback& completion_callback,
+    RequestHandle* out_req,
+    const net::BoundNetLog& net_log) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  DCHECK(delegate_);
   net::CompletionCallback wrapped_callback =
-      base::Bind(&CallbackWrapper, profile_, verify_result, callback);
+      base::Bind(&CompleteAndSignalAnchorUse,
+                 anchor_used_callback_,
+                 completion_callback,
+                 verify_result);
   int error = delegate_->Verify(cert, hostname, flags, crl_set, verify_result,
                                 wrapped_callback, out_req, net_log);
-  if (error == net::OK)
-    MaybeTaintProfile(*verify_result, profile_);
+  MaybeSignalAnchorUse(error, anchor_used_callback_, *verify_result);
   return error;
 }
 
@@ -94,4 +97,9 @@
   delegate_->CancelRequest(req);
 }
 
+const net::CertificateList& PolicyCertVerifier::GetAdditionalTrustAnchors() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+  return trust_anchors_;
+}
+
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/policy_cert_verifier.h b/chrome/browser/chromeos/policy/policy_cert_verifier.h
index 813981d..c671161 100644
--- a/chrome/browser/chromeos/policy/policy_cert_verifier.h
+++ b/chrome/browser/chromeos/policy/policy_cert_verifier.h
@@ -5,29 +5,40 @@
 #ifndef CHROME_BROWSER_CHROMEOS_POLICY_POLICY_CERT_VERIFIER_H_
 #define CHROME_BROWSER_CHROMEOS_POLICY_POLICY_CERT_VERIFIER_H_
 
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
+#include "net/cert/cert_trust_anchor_provider.h"
 #include "net/cert/cert_verifier.h"
 
 namespace net {
-class CertTrustAnchorProvider;
+class X509Certificate;
+typedef std::vector<scoped_refptr<X509Certificate> > CertificateList;
 }
 
 namespace policy {
 
 // Wraps a MultiThreadedCertVerifier to make it use the additional trust anchors
 // configured by the ONC user policy.
-class PolicyCertVerifier : public net::CertVerifier {
+class PolicyCertVerifier : public net::CertVerifier,
+                           public net::CertTrustAnchorProvider {
  public:
-  // |profile| is a handle to the Profile whose request context makes use of
-  // this verified. This object can be created on the IO thread; the handle is
-  // only used on the UI thread, if it's still valid.
-  // |trust_anchor_provider| is used to retrieve the current list of trust
-  // anchors.
-  PolicyCertVerifier(void* profile,
-                     net::CertTrustAnchorProvider* trust_anchor_provider);
+  // This object must be created on the UI thread. It's member functions and
+  // destructor must be called on the IO thread. |anchor_used_callback| is
+  // called on the IO thread everytime a certificate from the additional trust
+  // anchors (set with SetTrustAnchors) is used.
+  explicit PolicyCertVerifier(const base::Closure& anchor_used_callback);
   virtual ~PolicyCertVerifier();
 
-  // CertVerifier implementation:
+  void InitializeOnIOThread();
+
+  void SetTrustAnchors(const net::CertificateList& trust_anchors);
+
+  // CertVerifier:
   // Note: |callback| can be null.
   virtual int Verify(net::X509Certificate* cert,
                      const std::string& hostname,
@@ -40,9 +51,15 @@
 
   virtual void CancelRequest(RequestHandle req) OVERRIDE;
 
+  // CertTrustAnchorProvider:
+  virtual const net::CertificateList& GetAdditionalTrustAnchors() OVERRIDE;
+
  private:
-  void* profile_;
+  net::CertificateList trust_anchors_;
+  base::Closure anchor_used_callback_;
   scoped_ptr<CertVerifier> delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(PolicyCertVerifier);
 };
 
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/policy_cert_verifier_browsertest.cc b/chrome/browser/chromeos/policy/policy_cert_verifier_browsertest.cc
index 9203fd8..002cb87 100644
--- a/chrome/browser/chromeos/policy/policy_cert_verifier_browsertest.cc
+++ b/chrome/browser/chromeos/policy/policy_cert_verifier_browsertest.cc
@@ -4,16 +4,14 @@
 
 #include "chrome/browser/chromeos/policy/policy_cert_verifier.h"
 
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/test/base/testing_browser_process.h"
-#include "chrome/test/base/testing_profile.h"
-#include "chrome/test/base/testing_profile_manager.h"
 #include "content/public/browser/browser_thread.h"
-#include "content/public/test/test_browser_thread.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "crypto/nss_util.h"
 #include "net/base/net_log.h"
 #include "net/base/test_completion_callback.h"
@@ -24,26 +22,10 @@
 #include "net/cert/nss_cert_database.h"
 #include "net/cert/x509_certificate.h"
 #include "net/test/cert_test_util.h"
-#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using testing::Mock;
-using testing::ReturnRef;
-
 namespace policy {
 
-namespace {
-
-class MockCertTrustAnchorProvider : public net::CertTrustAnchorProvider {
- public:
-  MockCertTrustAnchorProvider() {}
-  virtual ~MockCertTrustAnchorProvider() {}
-
-  MOCK_METHOD0(GetAdditionalTrustAnchors, const net::CertificateList&());
-};
-
-}  // namespace
-
 // This is actually a unit test, but is linked with browser_tests because
 // importing a certificate into the NSS test database persists for the duration
 // of a process; since each browser_test runs in a separate process then this
@@ -52,12 +34,7 @@
 // is fixed.
 class PolicyCertVerifierTest : public testing::Test {
  public:
-  PolicyCertVerifierTest()
-      : cert_db_(NULL),
-        ui_thread_(content::BrowserThread::UI, &loop_),
-        io_thread_(content::BrowserThread::IO, &loop_),
-        profile_manager_(TestingBrowserProcess::GetGlobal()),
-        profile_(NULL) {}
+  PolicyCertVerifierTest() : cert_db_(NULL), trust_anchor_used_(false) {}
 
   virtual ~PolicyCertVerifierTest() {}
 
@@ -65,10 +42,35 @@
     ASSERT_TRUE(test_nssdb_.is_open());
     cert_db_ = net::NSSCertDatabase::GetInstance();
 
-    ASSERT_TRUE(profile_manager_.SetUp());
-    profile_ = profile_manager_.CreateTestingProfile("profile");
+    cert_verifier_.reset(new PolicyCertVerifier(base::Bind(
+        &PolicyCertVerifierTest::OnTrustAnchorUsed, base::Unretained(this))));
+    cert_verifier_->InitializeOnIOThread();
 
-    cert_verifier_.reset(new PolicyCertVerifier(profile_, &trust_provider_));
+    test_ca_cert_ = LoadCertificate("root_ca_cert.pem", net::CA_CERT);
+    ASSERT_TRUE(test_ca_cert_);
+    test_server_cert_ = LoadCertificate("ok_cert.pem", net::SERVER_CERT);
+    ASSERT_TRUE(test_server_cert_);
+    test_ca_cert_list_.push_back(test_ca_cert_);
+  }
+
+  virtual void TearDown() OVERRIDE {
+    // Destroy |cert_verifier_| before destroying the ThreadBundle, otherwise
+    // BrowserThread::CurrentlyOn checks fail.
+    cert_verifier_.reset();
+  }
+
+ protected:
+  int VerifyTestServerCert(const net::TestCompletionCallback& test_callback,
+                           net::CertVerifyResult* verify_result,
+                           net::CertVerifier::RequestHandle* request_handle) {
+    return cert_verifier_->Verify(test_server_cert_.get(),
+                                  "127.0.0.1",
+                                  0,
+                                  NULL,
+                                  verify_result,
+                                  test_callback.callback(),
+                                  request_handle,
+                                  net::BoundNetLog());
   }
 
   bool SupportsAdditionalTrustAnchors() {
@@ -77,6 +79,28 @@
     return proc->SupportsAdditionalTrustAnchors();
   }
 
+  // Returns whether |cert_verifier| signalled usage of one of the additional
+  // trust anchors (i.e. of |test_ca_cert_|) for the first time or since the
+  // last call of this function.
+  bool WasTrustAnchorUsedAndReset() {
+    base::RunLoop().RunUntilIdle();
+    bool result = trust_anchor_used_;
+    trust_anchor_used_ = false;
+    return result;
+  }
+
+  // |test_ca_cert_| is the issuer of |test_server_cert_|.
+  scoped_refptr<net::X509Certificate> test_ca_cert_;
+  scoped_refptr<net::X509Certificate> test_server_cert_;
+  net::CertificateList test_ca_cert_list_;
+  net::NSSCertDatabase* cert_db_;
+  scoped_ptr<PolicyCertVerifier> cert_verifier_;
+
+ private:
+  void OnTrustAnchorUsed() {
+    trust_anchor_used_ = true;
+  }
+
   scoped_refptr<net::X509Certificate> LoadCertificate(const std::string& name,
                                                       net::CertType type) {
     scoped_refptr<net::X509Certificate> cert =
@@ -90,112 +114,62 @@
     return cert;
   }
 
- protected:
+  bool trust_anchor_used_;
   crypto::ScopedTestNSSDB test_nssdb_;
-  net::NSSCertDatabase* cert_db_;
-  base::MessageLoop loop_;
-  content::TestBrowserThread ui_thread_;
-  content::TestBrowserThread io_thread_;
-  TestingProfileManager profile_manager_;
-  TestingProfile* profile_;
-  MockCertTrustAnchorProvider trust_provider_;
-  scoped_ptr<PolicyCertVerifier> cert_verifier_;
-  const net::CertificateList empty_cert_list_;
+  content::TestBrowserThreadBundle thread_bundle_;
 };
 
 TEST_F(PolicyCertVerifierTest, VerifyUntrustedCert) {
-  scoped_refptr<net::X509Certificate> cert =
-      LoadCertificate("ok_cert.pem", net::SERVER_CERT);
-  ASSERT_TRUE(cert.get());
-
-  // |cert| is untrusted, so Verify() fails.
-  net::CertVerifyResult verify_result;
-  net::TestCompletionCallback callback;
-  net::CertVerifier::RequestHandle request_handle;
-  EXPECT_CALL(trust_provider_, GetAdditionalTrustAnchors())
-      .WillOnce(ReturnRef(empty_cert_list_));
-  int error = cert_verifier_->Verify(cert.get(),
-                                     "127.0.0.1",
-                                     0,
-                                     NULL,
-                                     &verify_result,
-                                     callback.callback(),
-                                     &request_handle,
-                                     net::BoundNetLog());
-  Mock::VerifyAndClearExpectations(&trust_provider_);
-  ASSERT_EQ(net::ERR_IO_PENDING, error);
-  ASSERT_TRUE(request_handle);
-  error = callback.WaitForResult();
-  EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error);
+  // |test_server_cert_| is untrusted, so Verify() fails.
+  {
+    net::CertVerifyResult verify_result;
+    net::TestCompletionCallback callback;
+    net::CertVerifier::RequestHandle request_handle = NULL;
+    int error = VerifyTestServerCert(callback, &verify_result, &request_handle);
+    ASSERT_EQ(net::ERR_IO_PENDING, error);
+    EXPECT_TRUE(request_handle);
+    error = callback.WaitForResult();
+    EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error);
+  }
 
   // Issuing the same request again hits the cache. This tests the synchronous
   // path.
-  EXPECT_CALL(trust_provider_, GetAdditionalTrustAnchors())
-      .WillOnce(ReturnRef(empty_cert_list_));
-  error = cert_verifier_->Verify(cert.get(),
-                                 "127.0.0.1",
-                                 0,
-                                 NULL,
-                                 &verify_result,
-                                 callback.callback(),
-                                 &request_handle,
-                                 net::BoundNetLog());
-  Mock::VerifyAndClearExpectations(&trust_provider_);
-  EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error);
+  {
+    net::CertVerifyResult verify_result;
+    net::TestCompletionCallback callback;
+    net::CertVerifier::RequestHandle request_handle = NULL;
+    int error = VerifyTestServerCert(callback, &verify_result, &request_handle);
+    EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error);
+  }
 
-  // The profile is not tainted.
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(
-      profile_->GetPrefs()->GetBoolean(prefs::kUsedPolicyCertificatesOnce));
+  EXPECT_FALSE(WasTrustAnchorUsedAndReset());
 }
 
 TEST_F(PolicyCertVerifierTest, VerifyTrustedCert) {
-  // |ca_cert| is the issuer of |cert|.
-  scoped_refptr<net::X509Certificate> ca_cert =
-      LoadCertificate("root_ca_cert.pem", net::CA_CERT);
-  ASSERT_TRUE(ca_cert.get());
-  scoped_refptr<net::X509Certificate> cert =
-      LoadCertificate("ok_cert.pem", net::SERVER_CERT);
-  ASSERT_TRUE(cert.get());
-
-  // Make the database trust |ca_cert|.
-  net::CertificateList import_list;
-  import_list.push_back(ca_cert);
+  // Make the database trust |test_ca_cert_|.
   net::NSSCertDatabase::ImportCertFailureList failure_list;
   ASSERT_TRUE(cert_db_->ImportCACerts(
-      import_list, net::NSSCertDatabase::TRUSTED_SSL, &failure_list));
+      test_ca_cert_list_, net::NSSCertDatabase::TRUSTED_SSL, &failure_list));
   ASSERT_TRUE(failure_list.empty());
 
   // Verify that it is now trusted.
   net::NSSCertDatabase::TrustBits trust =
-      cert_db_->GetCertTrust(ca_cert.get(), net::CA_CERT);
+      cert_db_->GetCertTrust(test_ca_cert_.get(), net::CA_CERT);
   EXPECT_EQ(net::NSSCertDatabase::TRUSTED_SSL, trust);
 
-  // Verify() successfully verifies |cert| after it was imported.
+  // Verify() successfully verifies |test_server_cert_| after it was imported.
   net::CertVerifyResult verify_result;
   net::TestCompletionCallback callback;
-  net::CertVerifier::RequestHandle request_handle;
-  EXPECT_CALL(trust_provider_, GetAdditionalTrustAnchors())
-      .WillOnce(ReturnRef(empty_cert_list_));
-  int error = cert_verifier_->Verify(cert.get(),
-                                     "127.0.0.1",
-                                     0,
-                                     NULL,
-                                     &verify_result,
-                                     callback.callback(),
-                                     &request_handle,
-                                     net::BoundNetLog());
-  Mock::VerifyAndClearExpectations(&trust_provider_);
+  net::CertVerifier::RequestHandle request_handle = NULL;
+  int error = VerifyTestServerCert(callback, &verify_result, &request_handle);
   ASSERT_EQ(net::ERR_IO_PENDING, error);
-  ASSERT_TRUE(request_handle);
+  EXPECT_TRUE(request_handle);
   error = callback.WaitForResult();
   EXPECT_EQ(net::OK, error);
 
-  // The profile is not tainted, since the certificate is trusted from the
-  // database.
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(
-      profile_->GetPrefs()->GetBoolean(prefs::kUsedPolicyCertificatesOnce));
+  // The additional trust anchors were not used, since the certificate is
+  // trusted from the database.
+  EXPECT_FALSE(WasTrustAnchorUsedAndReset());
 }
 
 TEST_F(PolicyCertVerifierTest, VerifyUsingAdditionalTrustAnchor) {
@@ -204,129 +178,57 @@
     return;
   }
 
-  // |ca_cert| is the issuer of |cert|.
-  scoped_refptr<net::X509Certificate> ca_cert =
-      LoadCertificate("root_ca_cert.pem", net::CA_CERT);
-  ASSERT_TRUE(ca_cert.get());
-  scoped_refptr<net::X509Certificate> cert =
-      LoadCertificate("ok_cert.pem", net::SERVER_CERT);
-  ASSERT_TRUE(cert.get());
-
-  net::CertificateList additional_trust_anchors;
-  additional_trust_anchors.push_back(ca_cert);
-
-  // Verify() successfully verifies |cert|, using |ca_cert| from the list of
-  // |additional_trust_anchors|.
-  net::CertVerifyResult verify_result;
-  net::TestCompletionCallback callback;
-  net::CertVerifier::RequestHandle request_handle;
-  EXPECT_CALL(trust_provider_, GetAdditionalTrustAnchors())
-      .WillOnce(ReturnRef(additional_trust_anchors));
-  int error = cert_verifier_->Verify(cert.get(),
-                                     "127.0.0.1",
-                                     0,
-                                     NULL,
-                                     &verify_result,
-                                     callback.callback(),
-                                     &request_handle,
-                                     net::BoundNetLog());
-  Mock::VerifyAndClearExpectations(&trust_provider_);
-  ASSERT_EQ(net::ERR_IO_PENDING, error);
-  ASSERT_TRUE(request_handle);
-  error = callback.WaitForResult();
-  EXPECT_EQ(net::OK, error);
-
-  // The profile becomes tainted after using the trust anchors that came from
-  // the policy configuration.
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(
-      profile_->GetPrefs()->GetBoolean(prefs::kUsedPolicyCertificatesOnce));
-}
-
-TEST_F(PolicyCertVerifierTest, ProfileRemainsTainted) {
-  if (!SupportsAdditionalTrustAnchors()) {
-    LOG(INFO) << "Test skipped on this platform. NSS >= 3.14.2 required.";
-    return;
+  // |test_server_cert_| is untrusted, so Verify() fails.
+  {
+    net::CertVerifyResult verify_result;
+    net::TestCompletionCallback callback;
+    net::CertVerifier::RequestHandle request_handle = NULL;
+    int error = VerifyTestServerCert(callback, &verify_result, &request_handle);
+    ASSERT_EQ(net::ERR_IO_PENDING, error);
+    EXPECT_TRUE(request_handle);
+    error = callback.WaitForResult();
+    EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error);
   }
-
-  // |ca_cert| is the issuer of |cert|.
-  scoped_refptr<net::X509Certificate> ca_cert =
-      LoadCertificate("root_ca_cert.pem", net::CA_CERT);
-  ASSERT_TRUE(ca_cert.get());
-  scoped_refptr<net::X509Certificate> cert =
-      LoadCertificate("ok_cert.pem", net::SERVER_CERT);
-  ASSERT_TRUE(cert.get());
-
-  net::CertificateList additional_trust_anchors;
-  additional_trust_anchors.push_back(ca_cert);
-
-  // |cert| is untrusted, so Verify() fails.
-  net::CertVerifyResult verify_result;
-  net::TestCompletionCallback callback;
-  net::CertVerifier::RequestHandle request_handle;
-  EXPECT_CALL(trust_provider_, GetAdditionalTrustAnchors())
-      .WillOnce(ReturnRef(empty_cert_list_));
-  int error = cert_verifier_->Verify(cert.get(),
-                                     "127.0.0.1",
-                                     0,
-                                     NULL,
-                                     &verify_result,
-                                     callback.callback(),
-                                     &request_handle,
-                                     net::BoundNetLog());
-  Mock::VerifyAndClearExpectations(&trust_provider_);
-  ASSERT_EQ(net::ERR_IO_PENDING, error);
-  ASSERT_TRUE(request_handle);
-  error = callback.WaitForResult();
-  EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error);
-
-  // The profile is not tainted.
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(
-      profile_->GetPrefs()->GetBoolean(prefs::kUsedPolicyCertificatesOnce));
+  EXPECT_FALSE(WasTrustAnchorUsedAndReset());
 
   // Verify() again with the additional trust anchors.
-  EXPECT_CALL(trust_provider_, GetAdditionalTrustAnchors())
-      .WillOnce(ReturnRef(additional_trust_anchors));
-  error = cert_verifier_->Verify(cert.get(),
-                                 "127.0.0.1",
-                                 0,
-                                 NULL,
-                                 &verify_result,
-                                 callback.callback(),
-                                 &request_handle,
-                                 net::BoundNetLog());
-  Mock::VerifyAndClearExpectations(&trust_provider_);
-  ASSERT_EQ(net::ERR_IO_PENDING, error);
-  ASSERT_TRUE(request_handle);
-  error = callback.WaitForResult();
-  EXPECT_EQ(net::OK, error);
+  cert_verifier_->SetTrustAnchors(test_ca_cert_list_);
+  {
+    net::CertVerifyResult verify_result;
+    net::TestCompletionCallback callback;
+    net::CertVerifier::RequestHandle request_handle = NULL;
+    int error = VerifyTestServerCert(callback, &verify_result, &request_handle);
+    ASSERT_EQ(net::ERR_IO_PENDING, error);
+    EXPECT_TRUE(request_handle);
+    error = callback.WaitForResult();
+    EXPECT_EQ(net::OK, error);
+  }
+  EXPECT_TRUE(WasTrustAnchorUsedAndReset());
 
-  // The profile becomes tainted after using the trust anchors that came from
-  // the policy configuration.
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(
-      profile_->GetPrefs()->GetBoolean(prefs::kUsedPolicyCertificatesOnce));
+  // Verify() again with the additional trust anchors will hit the cache.
+  cert_verifier_->SetTrustAnchors(test_ca_cert_list_);
+  {
+    net::CertVerifyResult verify_result;
+    net::TestCompletionCallback callback;
+    net::CertVerifier::RequestHandle request_handle = NULL;
+    int error = VerifyTestServerCert(callback, &verify_result, &request_handle);
+    EXPECT_EQ(net::OK, error);
+  }
+  EXPECT_TRUE(WasTrustAnchorUsedAndReset());
 
   // Verifying after removing the trust anchors should now fail.
-  EXPECT_CALL(trust_provider_, GetAdditionalTrustAnchors())
-      .WillOnce(ReturnRef(empty_cert_list_));
-  error = cert_verifier_->Verify(cert.get(),
-                                 "127.0.0.1",
-                                 0,
-                                 NULL,
-                                 &verify_result,
-                                 callback.callback(),
-                                 &request_handle,
-                                 net::BoundNetLog());
-  Mock::VerifyAndClearExpectations(&trust_provider_);
-  // Note: this hits the cached result from the first Verify() in this test.
-  EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error);
-
-  // The profile is still tainted.
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(
-      profile_->GetPrefs()->GetBoolean(prefs::kUsedPolicyCertificatesOnce));
+  cert_verifier_->SetTrustAnchors(net::CertificateList());
+  {
+    net::CertVerifyResult verify_result;
+    net::TestCompletionCallback callback;
+    net::CertVerifier::RequestHandle request_handle = NULL;
+    int error = VerifyTestServerCert(callback, &verify_result, &request_handle);
+    // Note: this hits the cached result from the first Verify() in this test.
+    EXPECT_EQ(net::ERR_CERT_AUTHORITY_INVALID, error);
+  }
+  // The additional trust anchors were reset, thus |cert_verifier_| should not
+  // signal it's usage anymore.
+  EXPECT_FALSE(WasTrustAnchorUsedAndReset());
 }
 
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.h b/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.h
index dbd93a4..8c57185 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.h
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_token_forwarder.h
@@ -7,8 +7,8 @@
 
 #include "base/basictypes.h"
 #include "chrome/browser/policy/cloud/cloud_policy_service.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "components/browser_context_keyed_service/browser_context_keyed_service.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 
 class ProfileOAuth2TokenService;
 
diff --git a/chrome/browser/chromeos/policy/user_network_configuration_updater.cc b/chrome/browser/chromeos/policy/user_network_configuration_updater.cc
new file mode 100644
index 0000000..1d7859b
--- /dev/null
+++ b/chrome/browser/chromeos/policy/user_network_configuration_updater.cc
@@ -0,0 +1,98 @@
+// 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 "chrome/browser/chromeos/policy/user_network_configuration_updater.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "chrome/browser/chromeos/login/user.h"
+#include "chrome/browser/chromeos/net/onc_utils.h"
+#include "chrome/browser/chromeos/policy/policy_cert_verifier.h"
+#include "chromeos/network/managed_network_configuration_handler.h"
+#include "chromeos/network/onc/onc_certificate_importer.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/cert/x509_certificate.h"
+#include "policy/policy_constants.h"
+
+namespace policy {
+
+UserNetworkConfigurationUpdater::~UserNetworkConfigurationUpdater() {}
+
+// static
+scoped_ptr<UserNetworkConfigurationUpdater>
+UserNetworkConfigurationUpdater::CreateForUserPolicy(
+    bool allow_trusted_certs_from_policy,
+    const chromeos::User& user,
+    scoped_ptr<chromeos::onc::CertificateImporter> certificate_importer,
+    PolicyService* policy_service,
+    chromeos::ManagedNetworkConfigurationHandler* network_config_handler) {
+  scoped_ptr<UserNetworkConfigurationUpdater> updater(
+      new UserNetworkConfigurationUpdater(allow_trusted_certs_from_policy,
+                                          user,
+                                          certificate_importer.Pass(),
+                                          policy_service,
+                                          network_config_handler));
+  updater->Init();
+  return updater.Pass();
+}
+
+UserNetworkConfigurationUpdater::UserNetworkConfigurationUpdater(
+    bool allow_trusted_certs_from_policy,
+    const chromeos::User& user,
+    scoped_ptr<chromeos::onc::CertificateImporter> certificate_importer,
+    PolicyService* policy_service,
+    chromeos::ManagedNetworkConfigurationHandler* network_config_handler)
+    : NetworkConfigurationUpdater(chromeos::onc::ONC_SOURCE_USER_POLICY,
+                                  key::kOpenNetworkConfiguration,
+                                  certificate_importer.Pass(),
+                                  policy_service,
+                                  network_config_handler),
+      allow_trusted_certificates_from_policy_(allow_trusted_certs_from_policy),
+      user_(&user),
+      cert_verifier_(NULL) {}
+
+void UserNetworkConfigurationUpdater::SetPolicyCertVerifier(
+    PolicyCertVerifier* cert_verifier) {
+  cert_verifier_ = cert_verifier;
+  SetTrustAnchors();
+}
+
+void UserNetworkConfigurationUpdater::GetWebTrustedCertificates(
+    net::CertificateList* certs) const {
+  *certs = web_trust_certs_;
+}
+
+void UserNetworkConfigurationUpdater::ImportCertificates(
+    const base::ListValue& certificates_onc) {
+  web_trust_certs_.clear();
+  certificate_importer_->ImportCertificates(
+      certificates_onc,
+      onc_source_,
+      allow_trusted_certificates_from_policy_ ? &web_trust_certs_ : NULL);
+
+  SetTrustAnchors();
+}
+
+void UserNetworkConfigurationUpdater::ApplyNetworkPolicy(
+    base::ListValue* network_configs_onc) {
+  DCHECK(user_);
+  chromeos::onc::ExpandStringPlaceholdersInNetworksForUser(user_,
+                                                           network_configs_onc);
+  network_config_handler_->SetPolicy(
+      onc_source_, user_->username_hash(), *network_configs_onc);
+}
+
+void UserNetworkConfigurationUpdater::SetTrustAnchors() {
+  if (!cert_verifier_)
+    return;
+  content::BrowserThread::PostTask(
+      content::BrowserThread::IO,
+      FROM_HERE,
+      base::Bind(&PolicyCertVerifier::SetTrustAnchors,
+                 base::Unretained(cert_verifier_),
+                 web_trust_certs_));
+}
+
+}  // namespace policy
diff --git a/chrome/browser/chromeos/policy/user_network_configuration_updater.h b/chrome/browser/chromeos/policy/user_network_configuration_updater.h
new file mode 100644
index 0000000..78d9ef1
--- /dev/null
+++ b/chrome/browser/chromeos/policy/user_network_configuration_updater.h
@@ -0,0 +1,100 @@
+// 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 CHROME_BROWSER_CHROMEOS_POLICY_USER_NETWORK_CONFIGURATION_UPDATER_H_
+#define CHROME_BROWSER_CHROMEOS_POLICY_USER_NETWORK_CONFIGURATION_UPDATER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/chromeos/policy/network_configuration_updater.h"
+
+namespace chromeos {
+class User;
+}
+
+namespace net {
+class X509Certificate;
+typedef std::vector<scoped_refptr<X509Certificate> > CertificateList;
+}
+
+namespace policy {
+
+class PolicyCertVerifier;
+class PolicyService;
+
+// Implements additional special handling of ONC user policies. Namely string
+// expansion with the user's name (or email address, etc.) and handling of "Web"
+// trust of certificates. Web trusted certificates are pushed to the
+// PolicyCertVerifier if set.
+class UserNetworkConfigurationUpdater : public NetworkConfigurationUpdater {
+ public:
+  virtual ~UserNetworkConfigurationUpdater();
+
+  // Creates an updater that applies the ONC user policy from |policy_service|
+  // for user |user| once the policy service is completely initialized and on
+  // each policy change. Imported certificates, that request it, are only
+  // granted Web trust if |allow_trusted_certs_from_policy| is true. A reference
+  // to |user| is stored. It must outlive the returned updater.
+  static scoped_ptr<UserNetworkConfigurationUpdater> CreateForUserPolicy(
+      bool allow_trusted_certs_from_policy,
+      const chromeos::User& user,
+      scoped_ptr<chromeos::onc::CertificateImporter> certificate_importer,
+      PolicyService* policy_service,
+      chromeos::ManagedNetworkConfigurationHandler* network_config_handler);
+
+  // Sets the CertVerifier on which the current list of Web trusted server and
+  // CA certificates will be set. Policy updates will trigger further calls to
+  // |cert_verifier| later. |cert_verifier| must be valid until
+  // SetPolicyCertVerifier is called again (with another CertVerifier or NULL)
+  // or until this Updater is destructed. |cert_verifier|'s methods are only
+  // called on the IO thread. This function must be called on the UI thread.
+  void SetPolicyCertVerifier(PolicyCertVerifier* cert_verifier);
+
+  // Sets |certs| to the list of Web trusted server and CA certificates from the
+  // last received policy.
+  void GetWebTrustedCertificates(net::CertificateList* certs) const;
+
+ private:
+  class CrosTrustAnchorProvider;
+
+  UserNetworkConfigurationUpdater(
+      bool allow_trusted_certs_from_policy,
+      const chromeos::User& user,
+      scoped_ptr<chromeos::onc::CertificateImporter> certificate_importer,
+      PolicyService* policy_service,
+      chromeos::ManagedNetworkConfigurationHandler* network_config_handler);
+
+  virtual void ImportCertificates(
+      const base::ListValue& certificates_onc) OVERRIDE;
+
+  virtual void ApplyNetworkPolicy(
+      base::ListValue* network_configs_onc) OVERRIDE;
+
+  // Push |web_trust_certs_| to |cert_verifier_| if necessary.
+  void SetTrustAnchors();
+
+  // Whether Web trust is allowed or not. Only relevant for user policies.
+  bool allow_trusted_certificates_from_policy_;
+
+  // The user for whom the user policy will be applied. Is NULL if this Updater
+  // is used for device policy.
+  const chromeos::User* user_;
+
+  // Calls to this object are only allowed on the IO Thread.
+  PolicyCertVerifier* cert_verifier_;
+
+  // Contains the certificates of the last import that requested web trust. Must
+  // be empty if Web trust from policy is not allowed.
+  net::CertificateList web_trust_certs_;
+
+  DISALLOW_COPY_AND_ASSIGN(UserNetworkConfigurationUpdater);
+};
+
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_CHROMEOS_POLICY_USER_NETWORK_CONFIGURATION_UPDATER_H_
diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service.h b/chrome/browser/chromeos/settings/device_oauth2_token_service.h
index 24f8529..1ce6196 100644
--- a/chrome/browser/chromeos/settings/device_oauth2_token_service.h
+++ b/chrome/browser/chromeos/settings/device_oauth2_token_service.h
@@ -13,8 +13,8 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/stl_util.h"
 #include "base/time/time.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "google_apis/gaia/gaia_oauth_client.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 #include "net/url_request/url_request_context_getter.h"
 
 namespace net {
diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc b/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc
index d03655c..98fed4e 100644
--- a/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc
+++ b/chrome/browser/chromeos/settings/device_oauth2_token_service_unittest.cc
@@ -7,7 +7,6 @@
 #include "base/message_loop/message_loop.h"
 #include "base/prefs/testing_pref_service.h"
 #include "base/run_loop.h"
-#include "chrome/browser/signin/oauth2_token_service_test_util.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/scoped_testing_local_state.h"
 #include "chrome/test/base/testing_browser_process.h"
@@ -15,6 +14,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_browser_thread.h"
 #include "google_apis/gaia/gaia_oauth_client.h"
+#include "google_apis/gaia/oauth2_token_service_test_util.h"
 #include "net/http/http_status_code.h"
 #include "net/url_request/test_url_fetcher_factory.h"
 #include "net/url_request/url_fetcher_delegate.h"
diff --git a/chrome/browser/chromeos/settings/device_settings_test_helper.cc b/chrome/browser/chromeos/settings/device_settings_test_helper.cc
index efe0a7c..ed39d10 100644
--- a/chrome/browser/chromeos/settings/device_settings_test_helper.cc
+++ b/chrome/browser/chromeos/settings/device_settings_test_helper.cc
@@ -96,6 +96,8 @@
   return false;
 }
 
+void DeviceSettingsTestHelper::Init(dbus::Bus* bus) {}
+
 void DeviceSettingsTestHelper::AddObserver(Observer* observer) {}
 
 void DeviceSettingsTestHelper::RemoveObserver(Observer* observer) {}
diff --git a/chrome/browser/chromeos/settings/device_settings_test_helper.h b/chrome/browser/chromeos/settings/device_settings_test_helper.h
index 8e38806..9f1b601 100644
--- a/chrome/browser/chromeos/settings/device_settings_test_helper.h
+++ b/chrome/browser/chromeos/settings/device_settings_test_helper.h
@@ -78,6 +78,7 @@
   }
 
   // SessionManagerClient:
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void AddObserver(Observer* observer) OVERRIDE;
   virtual void RemoveObserver(Observer* observer) OVERRIDE;
   virtual bool HasObserver(Observer* observer) OVERRIDE;
diff --git a/chrome/browser/chromeos/status/data_promo_notification.cc b/chrome/browser/chromeos/status/data_promo_notification.cc
index 3ea4dce..d9b1da2 100644
--- a/chrome/browser/chromeos/status/data_promo_notification.cc
+++ b/chrome/browser/chromeos/status/data_promo_notification.cc
@@ -7,6 +7,7 @@
 #include "ash/shell.h"
 #include "ash/shell_window_ids.h"
 #include "ash/system/chromeos/network/network_connect.h"
+#include "ash/system/system_notifier.h"
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "base/prefs/pref_registry_simple.h"
@@ -226,6 +227,7 @@
           base::string16() /* title */,
           message,
           icon,
+          ash::NOTIFIER_NETWORK,
           base::Bind(&NotificationClicked,
                      default_network->path(), info_url)));
 
diff --git a/chrome/browser/chromeos/system/ash_system_tray_delegate.cc b/chrome/browser/chromeos/system/ash_system_tray_delegate.cc
index 1669cd4..5d05eef 100644
--- a/chrome/browser/chromeos/system/ash_system_tray_delegate.cc
+++ b/chrome/browser/chromeos/system/ash_system_tray_delegate.cc
@@ -275,7 +275,8 @@
       return;
     }
     // No special configure or setup for |network|, show the settings UI.
-    ShowNetworkSettingsPage(service_path);
+    if (LoginState::Get()->IsUserLoggedIn())
+      ShowNetworkSettingsPage(service_path);
     return;
   }
   NOTREACHED();
diff --git a/chrome/browser/chromeos/system/input_device_settings.cc b/chrome/browser/chromeos/system/input_device_settings.cc
index dd576fb..e6eed85 100644
--- a/chrome/browser/chromeos/system/input_device_settings.cc
+++ b/chrome/browser/chromeos/system/input_device_settings.cc
@@ -155,7 +155,6 @@
 void SetThreeFingerClick(bool enabled) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
 
-  SetTPControl("three_finger_click", enabled);
   // For Alex/ZGB.
   SetTPControl("t5r2_three_finger_click", enabled);
 }
diff --git a/chrome/browser/component_updater/component_updater_service.h b/chrome/browser/component_updater/component_updater_service.h
index 782f250..027d30f 100644
--- a/chrome/browser/component_updater/component_updater_service.h
+++ b/chrome/browser/component_updater/component_updater_service.h
@@ -46,7 +46,6 @@
   virtual bool GetInstalledFile(const std::string& file,
                                 base::FilePath* installed_file) = 0;
 
- protected:
   virtual ~ComponentInstaller() {}
 };
 
diff --git a/chrome/browser/component_updater/default_component_installer.cc b/chrome/browser/component_updater/default_component_installer.cc
new file mode 100644
index 0000000..da8dac9
--- /dev/null
+++ b/chrome/browser/component_updater/default_component_installer.cc
@@ -0,0 +1,173 @@
+// 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 "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/values.h"
+#include "base/version.h"
+#include "chrome/browser/component_updater/default_component_installer.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace {
+// Version "0" corresponds to no installed version. By the server's conventions,
+// we represent it as a dotted quad.
+const char kNullVersion[] = "0.0.0.0";
+}  // namespace
+
+DefaultComponentInstaller::DefaultComponentInstaller(
+    scoped_ptr<ComponentInstallerTraits> installer_traits)
+    : current_version_(kNullVersion) {
+  installer_traits_ = installer_traits.Pass();
+}
+
+DefaultComponentInstaller::~DefaultComponentInstaller() {
+}
+
+void DefaultComponentInstaller::Register(ComponentUpdateService* cus) {
+  if (!installer_traits_) {
+    NOTREACHED() << "A DefaultComponentInstaller has been created but "
+                 << "has no installer traits.";
+    return;
+  }
+  content::BrowserThread::PostTask(
+      content::BrowserThread::FILE, FROM_HERE,
+      base::Bind(&DefaultComponentInstaller::StartRegistration,
+                 base::Unretained(this),
+                 cus));
+}
+
+void DefaultComponentInstaller::OnUpdateError(int error) {
+  NOTREACHED() << "Component update error: " << error;
+}
+
+bool DefaultComponentInstaller::Install(const base::DictionaryValue& manifest,
+                                        const base::FilePath& unpack_path) {
+  std::string manifest_version;
+  manifest.GetStringASCII("version", &manifest_version);
+  base::Version version(manifest_version.c_str());
+  if (!version.IsValid())
+    return false;
+  if (current_version_.CompareTo(version) > 0)
+    return false;
+  base::FilePath install_path =
+      installer_traits_->GetBaseDirectory().AppendASCII(version.GetString());
+  if (base::PathExists(install_path))
+    return false;
+  if (!base::Move(unpack_path, install_path))
+    return false;
+  if (!installer_traits_->OnCustomInstall(manifest, install_path))
+    return false;
+  if (!installer_traits_->VerifyInstallation(install_path)) {
+    return false;
+  }
+  current_version_ = version;
+  installer_traits_->ComponentReady(current_version_, GetInstallDirectory());
+  return true;
+}
+
+bool DefaultComponentInstaller::GetInstalledFile(
+    const std::string& file,
+    base::FilePath* installed_file) {
+  if (current_version_.Equals(base::Version(kNullVersion)))
+    return false;  // No component has been installed yet.
+
+  *installed_file =
+      installer_traits_->GetBaseDirectory()
+          .AppendASCII(current_version_.GetString()).AppendASCII(file);
+  return true;
+}
+
+void DefaultComponentInstaller::StartRegistration(
+    ComponentUpdateService* cus) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
+  base::FilePath base_dir = installer_traits_->GetBaseDirectory();
+  if (!base::PathExists(base_dir) &&
+      !file_util::CreateDirectory(base_dir)) {
+    NOTREACHED() << "Could not create the base directory for "
+                 << installer_traits_->GetName() << " ("
+                 << base_dir.MaybeAsASCII() << ").";
+    return;
+  }
+
+  base::FilePath latest_dir;
+  base::Version latest_version(kNullVersion);
+  std::vector<base::FilePath> older_dirs;
+  bool found = false;
+  base::FileEnumerator file_enumerator(
+      base_dir, false, base::FileEnumerator::DIRECTORIES);
+  for (base::FilePath path = file_enumerator.Next();
+       !path.value().empty();
+       path = file_enumerator.Next()) {
+    base::Version version(path.BaseName().MaybeAsASCII());
+    if (!version.IsValid())
+      continue;
+    if (found) {
+      if (version.CompareTo(latest_version) > 0) {
+        older_dirs.push_back(latest_dir);
+        latest_dir = path;
+        latest_version = version;
+      } else {
+        older_dirs.push_back(path);
+      }
+    } else {
+      latest_dir = path;
+      latest_version = version;
+      found = true;
+    }
+  }
+
+  if (found) {
+    current_version_ = latest_version;
+    file_util::ReadFileToString(latest_dir.AppendASCII("manifest.fingerprint"),
+                                &current_fingerprint_);
+  }
+
+  // Remove older versions of the component. None should be in use during
+  // browser startup.
+  for (std::vector<base::FilePath>::iterator iter = older_dirs.begin();
+       iter != older_dirs.end(); ++iter) {
+   base::DeleteFile(*iter, true);
+  }
+
+  content::BrowserThread::PostTask(
+      content::BrowserThread::UI, FROM_HERE,
+      base::Bind(&DefaultComponentInstaller::FinishRegistration,
+                 base::Unretained(this),
+                 cus));
+}
+
+base::FilePath DefaultComponentInstaller::GetInstallDirectory() {
+  return installer_traits_->GetBaseDirectory()
+      .AppendASCII(current_version_.GetString());
+}
+
+void DefaultComponentInstaller::FinishRegistration(
+    ComponentUpdateService* cus) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  CrxComponent crx;
+  crx.name = installer_traits_->GetName();
+  crx.installer = this;
+  crx.version = current_version_;
+  crx.fingerprint = current_fingerprint_;
+  installer_traits_->GetHash(&crx.pk_hash);
+  ComponentUpdateService::Status status = cus->RegisterComponent(crx);
+  if (status != ComponentUpdateService::kOk &&
+      status != ComponentUpdateService::kReplaced) {
+    NOTREACHED() << "Component registration failed for "
+                 << installer_traits_->GetName();
+    return;
+  }
+
+  base::Version installed_version;
+  if (current_version_.CompareTo(base::Version(kNullVersion)) <= 0) {
+    content::BrowserThread::PostTask(
+        content::BrowserThread::FILE, FROM_HERE,
+        base::Bind(&ComponentInstallerTraits::ComponentReady,
+                   base::Unretained(installer_traits_.get()),
+                   installed_version,
+                   GetInstallDirectory()));
+  }
+}
diff --git a/chrome/browser/component_updater/default_component_installer.h b/chrome/browser/component_updater/default_component_installer.h
new file mode 100644
index 0000000..037438e
--- /dev/null
+++ b/chrome/browser/component_updater/default_component_installer.h
@@ -0,0 +1,100 @@
+// 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 CHROME_BROWSER_COMPONENT_UPDATER_DEFAULT_COMPONENT_INSTALLER_H_
+#define CHROME_BROWSER_COMPONENT_UPDATER_DEFAULT_COMPONENT_INSTALLER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/version.h"
+#include "chrome/browser/component_updater/component_updater_service.h"
+
+namespace base {
+class DictionaryValue;
+class FilePath;
+}  // namespace base
+
+// Components should use a DefaultComponentInstaller by defining a class that
+// implements the members of ComponentInstallerTraits, and then registering a
+// DefaultComponentInstaller that has been constructed with an instance of that
+// class.
+class ComponentInstallerTraits {
+ public:
+  virtual ~ComponentInstallerTraits() {}
+
+  // Verifies that a working installation resides within the directory specified
+  // by |dir|. |dir| is of the form <base directory>/<version>.
+  virtual bool VerifyInstallation(const base::FilePath& dir) const = 0;
+
+  // Returns true if the component can be automatically updated. Called once
+  // during component registration from the UI thread.
+  virtual bool CanAutoUpdate() const = 0;
+
+  // OnCustomInstall is called during the installation process. Components that
+  // require custom installation operations should implement them here.
+  // Returns false if a custom operation failed, and true otherwise.
+  // Called only from the FILE thread.
+  virtual bool OnCustomInstall(const base::DictionaryValue& manifest,
+                               const base::FilePath& install_dir) = 0;
+
+  // ComponentReady is called in two cases:
+  //   1) After an installation is successfully completed.
+  //   2) During component registration if the component is already installed.
+  // In both cases the install is verified before this is called. This method
+  // is guaranteed to be called before any observers of the component are
+  // notified of a successful install, and is meant to support follow-on work
+  // such as updating paths elsewhere in Chrome. Called only from the FILE
+  // thread.
+  // |version| is the version of the component, while |path| is the path to the
+  // install directory.
+  virtual void ComponentReady(const base::Version& version,
+                              const base::FilePath& install_dir) = 0;
+
+  // Returns the directory that the installer will place versioned installs of
+  // the component into.
+  virtual base::FilePath GetBaseDirectory() const = 0;
+
+  // Returns the component's SHA2 hash as raw bytes.
+  virtual void GetHash(std::vector<uint8>* hash) const = 0;
+
+  // Returns the human-readable name of the component.
+  virtual std::string GetName() const = 0;
+};
+
+// A DefaultComponentInstaller is intended to be final, and not derived from.
+// Customization must be provided by passing a ComponentInstallerTraits object
+// to the constructor.
+class DefaultComponentInstaller : public ComponentInstaller {
+ public:
+  explicit DefaultComponentInstaller(
+      scoped_ptr<ComponentInstallerTraits> installer_traits);
+
+  // Registers the component for update checks and installs.
+  void Register(ComponentUpdateService* cus);
+
+  // Overridden from ComponentInstaller:
+  virtual void OnUpdateError(int error) OVERRIDE;
+  virtual bool Install(const base::DictionaryValue& manifest,
+                       const base::FilePath& unpack_path) OVERRIDE;
+  virtual bool GetInstalledFile(const std::string& file,
+                                base::FilePath* installed_file) OVERRIDE;
+
+  virtual ~DefaultComponentInstaller();
+
+ private:
+  base::FilePath GetInstallDirectory();
+  void StartRegistration(ComponentUpdateService* cus);
+  void FinishRegistration(ComponentUpdateService* cus);
+
+  base::Version current_version_;
+  std::string current_fingerprint_;
+  scoped_ptr<ComponentInstallerTraits> installer_traits_;
+
+  DISALLOW_COPY_AND_ASSIGN(DefaultComponentInstaller);
+};
+
+#endif  // CHROME_BROWSER_COMPONENT_UPDATER_DEFAULT_COMPONENT_INSTALLER_H_
diff --git a/chrome/browser/component_updater/widevine_cdm_component_installer.cc b/chrome/browser/component_updater/widevine_cdm_component_installer.cc
index 769a38b..60382d1 100644
--- a/chrome/browser/component_updater/widevine_cdm_component_installer.cc
+++ b/chrome/browser/component_updater/widevine_cdm_component_installer.cc
@@ -10,18 +10,14 @@
 
 #include "base/base_paths.h"
 #include "base/bind.h"
-#include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/file_util.h"
-#include "base/files/file_enumerator.h"
 #include "base/files/file_path.h"
-#include "base/logging.h"
 #include "base/path_service.h"
-#include "base/strings/string_util.h"
 #include "base/values.h"
-#include "base/version.h"
 #include "build/build_config.h"
 #include "chrome/browser/component_updater/component_updater_service.h"
+#include "chrome/browser/component_updater/default_component_installer.h"
 #include "chrome/browser/plugins/plugin_prefs.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_paths.h"
@@ -36,13 +32,10 @@
 using content::BrowserThread;
 using content::PluginService;
 
-namespace {
-
-// TODO(xhwang): Move duplicate code among all component installer
-// implementations to some common place.
-
 #if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
 
+namespace {
+
 // CRX hash. The extension id is: oimompecagnajdejgnnjijobebaeigek.
 const uint8 kSha2Hash[] = { 0xe8, 0xce, 0xcf, 0x42, 0x06, 0xd0, 0x93, 0x49,
                             0x6d, 0xd9, 0x89, 0xe1, 0x41, 0x04, 0x86, 0x4a,
@@ -72,17 +65,6 @@
     "???";
 #endif
 
-// If we don't have a Widevine CDM component, this is the version we claim.
-const char kNullVersion[] = "0.0.0.0";
-
-// The base directory on Windows looks like:
-// <profile>\AppData\Local\Google\Chrome\User Data\WidevineCdm\.
-base::FilePath GetWidevineCdmBaseDirectory() {
-  base::FilePath result;
-  PathService::Get(chrome::DIR_COMPONENT_WIDEVINE_CDM, &result);
-  return result;
-}
-
 // Widevine CDM is packaged as a multi-CRX. Widevine CDM binaries are located in
 // _platform_specific/<platform_arch> folder in the package. This function
 // returns the platform-specific subdirectory that is part of that multi-CRX.
@@ -93,41 +75,6 @@
   return base_path.AppendASCII("_platform_specific").AppendASCII(platform_arch);
 }
 
-// Widevine CDM has the version encoded in the path so we need to enumerate the
-// directories to find the full path.
-// On success, |latest_dir| returns something like:
-// <profile>\AppData\Local\Google\Chrome\User Data\WidevineCdm\10.3.44.555\.
-// |latest_version| returns the corresponding version number. |older_dirs|
-// returns directories of all older versions.
-bool GetWidevineCdmDirectory(base::FilePath* latest_dir,
-                             base::Version* latest_version,
-                             std::vector<base::FilePath>* older_dirs) {
-  base::FilePath base_dir = GetWidevineCdmBaseDirectory();
-  bool found = false;
-  base::FileEnumerator file_enumerator(
-      base_dir, false, base::FileEnumerator::DIRECTORIES);
-  for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
-       path = file_enumerator.Next()) {
-    base::Version version(path.BaseName().MaybeAsASCII());
-    if (!version.IsValid())
-      continue;
-    if (found) {
-      if (version.CompareTo(*latest_version) > 0) {
-        older_dirs->push_back(*latest_dir);
-        *latest_dir = path;
-        *latest_version = version;
-      } else {
-        older_dirs->push_back(path);
-      }
-    } else {
-      *latest_dir = path;
-      *latest_version = version;
-      found = true;
-    }
-  }
-  return found;
-}
-
 bool MakeWidevineCdmPluginInfo(const base::FilePath& path,
                                const base::Version& version,
                                content::PepperPluginInfo* plugin_info) {
@@ -166,168 +113,96 @@
   PluginService::GetInstance()->RefreshPlugins();
 }
 
-// Returns true if this browser is compatible with the given Widevine CDM
-// manifest, with the version specified in the manifest in |version_out|.
-bool CheckWidevineCdmManifest(const base::DictionaryValue& manifest,
-                              base::Version* version_out) {
-  std::string name;
-  manifest.GetStringASCII("name", &name);
+}  // namespace
 
-  if (name != kWidevineCdmManifestName)
-    return false;
-
-  std::string proposed_version;
-  manifest.GetStringASCII("version", &proposed_version);
-  base::Version version(proposed_version.c_str());
-  if (!version.IsValid())
-    return false;
-
-  *version_out = version;
-  return true;
-}
-
-class WidevineCdmComponentInstaller : public ComponentInstaller {
+class WidevineCdmComponentInstallerTraits : public ComponentInstallerTraits {
  public:
-  explicit WidevineCdmComponentInstaller(const base::Version& version);
-  virtual ~WidevineCdmComponentInstaller() {}
-
-  virtual void OnUpdateError(int error) OVERRIDE;
-  virtual bool Install(const base::DictionaryValue& manifest,
-                       const base::FilePath& unpack_path) OVERRIDE;
-
-  virtual bool GetInstalledFile(const std::string& file,
-                                base::FilePath* installed_file) OVERRIDE;
+  WidevineCdmComponentInstallerTraits();
+  virtual ~WidevineCdmComponentInstallerTraits() {}
 
  private:
-  base::Version current_version_;
+  // The following methods override ComponentInstallerTraits.
+  virtual bool CanAutoUpdate() const OVERRIDE;
+  virtual bool OnCustomInstall(const base::DictionaryValue& manifest,
+                               const base::FilePath& install_dir) OVERRIDE;
+  virtual bool VerifyInstallation(
+      const base::FilePath& install_dir) const OVERRIDE;
+  virtual void ComponentReady(const base::Version& version,
+                              const base::FilePath& path) OVERRIDE;
+  virtual base::FilePath GetBaseDirectory() const OVERRIDE;
+  virtual void GetHash(std::vector<uint8>* hash) const OVERRIDE;
+  virtual std::string GetName() const OVERRIDE;
+  DISALLOW_COPY_AND_ASSIGN(WidevineCdmComponentInstallerTraits);
 };
 
-WidevineCdmComponentInstaller::WidevineCdmComponentInstaller(
-    const base::Version& version)
-    : current_version_(version) {
-  DCHECK(version.IsValid());
+WidevineCdmComponentInstallerTraits::WidevineCdmComponentInstallerTraits() {
 }
 
-void WidevineCdmComponentInstaller::OnUpdateError(int error) {
-  NOTREACHED() << "Widevine CDM update error: " << error;
+bool WidevineCdmComponentInstallerTraits::CanAutoUpdate() const {
+  return true;
 }
 
-bool WidevineCdmComponentInstaller::Install(
+// The adapter is copied into the install directory as part of the installation.
+bool WidevineCdmComponentInstallerTraits::OnCustomInstall(
     const base::DictionaryValue& manifest,
-    const base::FilePath& unpack_path) {
-  base::Version version;
-  if (!CheckWidevineCdmManifest(manifest, &version))
-    return false;
-  if (current_version_.CompareTo(version) > 0)
-    return false;
-
-  if (!base::PathExists(
-      GetPlatformDirectory(unpack_path).AppendASCII(kWidevineCdmFileName))) {
-    return false;
-  }
-
-  base::FilePath adapter_source_path;
-  PathService::Get(chrome::FILE_WIDEVINE_CDM_ADAPTER, &adapter_source_path);
-  if (!base::PathExists(adapter_source_path))
-    return false;
-
-  // Passed the basic tests. Time to install it.
-  base::FilePath install_path =
-      GetWidevineCdmBaseDirectory().AppendASCII(version.GetString());
-  if (base::PathExists(install_path))
-    return false;
-  if (!base::Move(unpack_path, install_path))
-    return false;
-
+    const base::FilePath& install_path) {
   base::FilePath adapter_install_path = GetPlatformDirectory(install_path)
       .AppendASCII(kWidevineCdmAdapterFileName);
-  if (!base::CopyFile(adapter_source_path, adapter_install_path))
-    return false;
+  base::FilePath adapter_source_path;
+  PathService::Get(chrome::FILE_WIDEVINE_CDM_ADAPTER, &adapter_source_path);
+  return base::CopyFile(adapter_source_path, adapter_install_path);
+}
 
-  // Installation is done. Now register the Widevine CDM with chrome.
-  current_version_ = version;
+// Once the component is installed, register the new version with Chrome.
+void WidevineCdmComponentInstallerTraits::ComponentReady(
+    const base::Version& version,
+    const base::FilePath& path) {
+  base::FilePath adapter_install_path = GetPlatformDirectory(path)
+      .AppendASCII(kWidevineCdmAdapterFileName);
   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
       &RegisterWidevineCdmWithChrome, adapter_install_path, version));
-  return true;
 }
 
-// Given |file|, a path like "_platform_specific/win_x86/widevinecdm.dll",
-// returns the assumed install path. The path separator in |file| is '/'
-// for all platforms. Caller is responsible for checking that the
-// |installed_file| actually exists.
-bool WidevineCdmComponentInstaller::GetInstalledFile(
-    const std::string& file, base::FilePath* installed_file) {
-  if (current_version_.Equals(base::Version(kNullVersion)))
-    return false;  // No CDM has been installed yet.
-
-  *installed_file = GetWidevineCdmBaseDirectory().AppendASCII(
-      current_version_.GetString()).AppendASCII(file);
-  return true;
+bool WidevineCdmComponentInstallerTraits::VerifyInstallation(
+    const base::FilePath& install_dir) const {
+  return base::PathExists(GetPlatformDirectory(install_dir)
+          .AppendASCII(kWidevineCdmFileName))
+      && base::PathExists(GetPlatformDirectory(install_dir)
+          .AppendASCII(kWidevineCdmAdapterFileName));
 }
 
-void FinishWidevineCdmUpdateRegistration(ComponentUpdateService* cus,
-                                         const base::Version& version) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  CrxComponent widevine_cdm;
-  widevine_cdm.name = "WidevineCdm";
-  widevine_cdm.installer = new WidevineCdmComponentInstaller(version);
-  widevine_cdm.version = version;
-  widevine_cdm.pk_hash.assign(kSha2Hash, &kSha2Hash[sizeof(kSha2Hash)]);
-  if (cus->RegisterComponent(widevine_cdm) != ComponentUpdateService::kOk) {
-    NOTREACHED() << "Widevine CDM component registration failed.";
-    return;
-  }
+// The base directory on Windows looks like:
+// <profile>\AppData\Local\Google\Chrome\User Data\WidevineCdm\.
+base::FilePath WidevineCdmComponentInstallerTraits::GetBaseDirectory() const {
+  base::FilePath result;
+  PathService::Get(chrome::DIR_COMPONENT_WIDEVINE_CDM, &result);
+  return result;
 }
 
-void StartWidevineCdmUpdateRegistration(ComponentUpdateService* cus) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
-  base::FilePath base_dir = GetWidevineCdmBaseDirectory();
-  if (!base::PathExists(base_dir) &&
-      !file_util::CreateDirectory(base_dir)) {
-    NOTREACHED() << "Could not create Widevine CDM directory.";
-    return;
-  }
+void WidevineCdmComponentInstallerTraits::GetHash(
+    std::vector<uint8>* hash) const {
+  hash->assign(kSha2Hash, kSha2Hash + arraysize(kSha2Hash));
+}
 
-  base::FilePath latest_dir;
-  base::Version version(kNullVersion);
-  std::vector<base::FilePath> older_dirs;
-
-  if (GetWidevineCdmDirectory(&latest_dir, &version, &older_dirs)) {
-    base::FilePath latest_platform_dir = GetPlatformDirectory(latest_dir);
-    base::FilePath adapter_path =
-        latest_platform_dir.AppendASCII(kWidevineCdmAdapterFileName);
-    base::FilePath cdm_path =
-        latest_platform_dir.AppendASCII(kWidevineCdmFileName);
-
-    if (base::PathExists(adapter_path) &&
-        base::PathExists(cdm_path)) {
-      BrowserThread::PostTask(
-          BrowserThread::UI, FROM_HERE,
-          base::Bind(&RegisterWidevineCdmWithChrome, adapter_path, version));
-    } else {
-      base::DeleteFile(latest_dir, true);
-      version = base::Version(kNullVersion);
-    }
-  }
-
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::Bind(&FinishWidevineCdmUpdateRegistration, cus, version));
-
-  // Remove older versions of Widevine CDM.
-  for (std::vector<base::FilePath>::iterator iter = older_dirs.begin();
-       iter != older_dirs.end(); ++iter) {
-    base::DeleteFile(*iter, true);
-  }
+std::string WidevineCdmComponentInstallerTraits::GetName() const {
+  return kWidevineCdmManifestName;
 }
 
 #endif  // defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
 
-}  // namespace
-
 void RegisterWidevineCdmComponent(ComponentUpdateService* cus) {
 #if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
-  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
-                          base::Bind(&StartWidevineCdmUpdateRegistration, cus));
+  base::FilePath adapter_source_path;
+  PathService::Get(chrome::FILE_WIDEVINE_CDM_ADAPTER, &adapter_source_path);
+  if (!base::PathExists(adapter_source_path))
+    return;
+  scoped_ptr<ComponentInstallerTraits> traits(
+      new WidevineCdmComponentInstallerTraits);
+  // |cus| will take ownership of |installer| during installer->Register(cus).
+  DefaultComponentInstaller* installer
+      = new DefaultComponentInstaller(traits.Pass());
+  installer->Register(cus);
+#else
+  return;
 #endif  // defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
 }
diff --git a/chrome/browser/content_settings/host_content_settings_map.cc b/chrome/browser/content_settings/host_content_settings_map.cc
index 20237d3..9367ba5 100644
--- a/chrome/browser/content_settings/host_content_settings_map.cc
+++ b/chrome/browser/content_settings/host_content_settings_map.cc
@@ -541,9 +541,17 @@
     return true;
   }
   if (primary_url.SchemeIs(extensions::kExtensionScheme)) {
-    return content_type != CONTENT_SETTINGS_TYPE_PLUGINS &&
-        (content_type != CONTENT_SETTINGS_TYPE_COOKIES ||
-            secondary_url.SchemeIs(extensions::kExtensionScheme));
+    switch (content_type) {
+      case CONTENT_SETTINGS_TYPE_PLUGINS:
+      case CONTENT_SETTINGS_TYPE_MEDIASTREAM:
+      case CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC:
+      case CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA:
+        return false;
+      case CONTENT_SETTINGS_TYPE_COOKIES:
+        return secondary_url.SchemeIs(extensions::kExtensionScheme);
+      default:
+        return true;
+    }
   }
   return primary_url.SchemeIs(chrome::kChromeDevToolsScheme) ||
          primary_url.SchemeIs(chrome::kChromeInternalScheme) ||
diff --git a/chrome/browser/devtools/adb/android_usb_device.cc b/chrome/browser/devtools/adb/android_usb_device.cc
index f32b665..335d27b 100644
--- a/chrome/browser/devtools/adb/android_usb_device.cc
+++ b/chrome/browser/devtools/adb/android_usb_device.cc
@@ -48,12 +48,13 @@
 scoped_refptr<AndroidUsbDevice> ClaimInterface(
     crypto::RSAPrivateKey* rsa_key,
     scoped_refptr<UsbDeviceHandle> usb_device,
-    const UsbInterface* interface) {
+    scoped_refptr<const UsbInterfaceDescriptor> interface,
+    int interface_id) {
   if (interface->GetNumAltSettings() == 0)
     return NULL;
 
-  scoped_refptr<const UsbInterfaceDescriptor> idesc =
-      interface->GetAltSetting(0).get();
+  scoped_refptr<const UsbInterfaceAltSettingDescriptor> idesc =
+      interface->GetAltSetting(0);
 
   if (idesc->GetInterfaceClass() != kAdbClass ||
       idesc->GetInterfaceSubclass() != kAdbSubclass ||
@@ -68,7 +69,7 @@
 
   for (size_t i = 0; i < idesc->GetNumEndpoints(); ++i) {
     scoped_refptr<const UsbEndpointDescriptor> edesc =
-        idesc->GetEndpoint(i).get();
+        idesc->GetEndpoint(i);
     if (edesc->GetTransferType() != USB_TRANSFER_BULK)
       continue;
     if (edesc->GetDirection() == USB_DIRECTION_INBOUND)
@@ -81,7 +82,7 @@
   if (inbound_address == 0 || outbound_address == 0)
     return NULL;
 
-  if (!usb_device->ClaimInterface(1))
+  if (!usb_device->ClaimInterface(interface_id))
     return NULL;
 
   base::string16 serial;
@@ -187,9 +188,8 @@
     if (ContainsKey(claimed_devices, it->get()))
       continue;
 
-    scoped_refptr<UsbConfigDescriptor> config = new UsbConfigDescriptor();
-    bool success = (*it)->ListInterfaces(config.get());
-    if (!success)
+    scoped_refptr<UsbConfigDescriptor> config = (*it)->ListInterfaces();
+    if (!config)
       continue;
 
     scoped_refptr<UsbDeviceHandle> usb_device = (*it)->Open();
@@ -198,7 +198,7 @@
 
     for (size_t j = 0; j < config->GetNumInterfaces(); ++j) {
       scoped_refptr<AndroidUsbDevice> device =
-          ClaimInterface(rsa_key, usb_device, config->GetInterface(j));
+          ClaimInterface(rsa_key, usb_device, config->GetInterface(j), j);
       if (device.get())
         devices.push_back(device);
     }
diff --git a/chrome/browser/devtools/devtools_adb_bridge.cc b/chrome/browser/devtools/devtools_adb_bridge.cc
index e19910a..9df51ae 100644
--- a/chrome/browser/devtools/devtools_adb_bridge.cc
+++ b/chrome/browser/devtools/devtools_adb_bridge.cc
@@ -56,6 +56,8 @@
 static const char kVersionRequest[] = "GET /json/version HTTP/1.1\r\n\r\n";
 static const char kClosePageRequest[] = "GET /json/close/%s HTTP/1.1\r\n\r\n";
 static const char kNewPageRequest[] = "GET /json/new HTTP/1.1\r\n\r\n";
+static const char kActivatePageRequest[] =
+    "GET /json/activate/%s HTTP/1.1\r\n\r\n";
 const int kAdbPort = 5037;
 const int kBufferSize = 16 * 1024;
 const int kAdbPollingIntervalMs = 1000;
@@ -672,6 +674,14 @@
   value.GetString("webSocketDebuggerUrl", &debug_url_);
   value.GetString("devtoolsFrontendUrl", &frontend_url_);
 
+  if (id_.empty() && !debug_url_.empty())  {
+    // Target id is not available until Chrome 26. Use page id at the end of
+    // debug_url_ instead. For attached targets the id will remain empty.
+    std::vector<std::string> parts;
+    Tokenize(debug_url_, "/", &parts);
+    id_ = parts[parts.size()-1];
+  }
+
   if (debug_url_.find("ws://") == 0)
     debug_url_ = debug_url_.substr(5);
   else
@@ -686,20 +696,17 @@
 
 void DevToolsAdbBridge::RemotePage::Inspect(Profile* profile) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  std::string agent_id = base::StringPrintf("%s:%s:%s",
-    device_->serial().c_str(), socket_.c_str(), id_.c_str());
-  AgentHostDelegates::iterator it =
-      g_host_delegates.Get().find(agent_id);
-  if (it != g_host_delegates.Get().end())
-    it->second->OpenFrontend();
-  else if (!debug_url_.empty())
-    new AgentHostDelegate(
-        agent_id, device_, socket_, debug_url_,
-        frontend_url_, bridge_->GetAdbMessageLoop(), profile);
+  RequestActivate(
+      base::Bind(&RemotePage::InspectOnHandlerThread, this, profile));
 }
 
 static void Noop(int, const std::string&) {}
 
+void DevToolsAdbBridge::RemotePage::Activate() {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  RequestActivate(base::Bind(&Noop));
+}
+
 void DevToolsAdbBridge::RemotePage::Close() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   if (attached())
@@ -727,6 +734,36 @@
 DevToolsAdbBridge::RemotePage::~RemotePage() {
 }
 
+void DevToolsAdbBridge::RemotePage::RequestActivate(
+    const CommandCallback& callback) {
+  std::string request = base::StringPrintf(kActivatePageRequest, id_.c_str());
+  bridge_->GetAdbMessageLoop()->PostTask(FROM_HERE,
+      base::Bind(&AndroidDevice::HttpQuery,
+          device_, socket_, request, callback));
+}
+
+void DevToolsAdbBridge::RemotePage::InspectOnHandlerThread(
+    Profile* profile, int result, const std::string& response) {
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(&RemotePage::InspectOnUIThread, this, profile));
+}
+
+void DevToolsAdbBridge::RemotePage::InspectOnUIThread(Profile* profile) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  std::string agent_id = base::StringPrintf("%s:%s:%s",
+    device_->serial().c_str(), socket_.c_str(), id_.c_str());
+  AgentHostDelegates::iterator it =
+      g_host_delegates.Get().find(agent_id);
+  if (it != g_host_delegates.Get().end()) {
+    it->second->OpenFrontend();
+  } else if (!attached()) {
+    new AgentHostDelegate(
+        agent_id, device_, socket_, debug_url_,
+        frontend_url_, bridge_->GetAdbMessageLoop(), profile);
+  }
+}
+
 DevToolsAdbBridge::RemoteBrowser::RemoteBrowser(
     scoped_refptr<DevToolsAdbBridge> bridge,
     scoped_refptr<AndroidDevice> device,
diff --git a/chrome/browser/devtools/devtools_adb_bridge.h b/chrome/browser/devtools/devtools_adb_bridge.h
index b51b3bd..0dd05c9 100644
--- a/chrome/browser/devtools/devtools_adb_bridge.h
+++ b/chrome/browser/devtools/devtools_adb_bridge.h
@@ -98,6 +98,7 @@
     bool attached() { return debug_url_.empty(); }
 
     void Inspect(Profile* profile);
+    void Activate();
     void Close();
     void Reload();
 
@@ -108,6 +109,13 @@
     friend class base::RefCounted<RemotePage>;
     virtual ~RemotePage();
 
+    void RequestActivate(const CommandCallback& callback);
+
+    void InspectOnHandlerThread(
+        Profile* profile, int result, const std::string& response);
+
+    void InspectOnUIThread(Profile* profile);
+
     scoped_refptr<DevToolsAdbBridge> bridge_;
     scoped_refptr<AndroidDevice> device_;
     std::string socket_;
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc
index e5f185e..d4af7a2 100644
--- a/chrome/browser/devtools/devtools_window.cc
+++ b/chrome/browser/devtools/devtools_window.cc
@@ -328,7 +328,8 @@
 // static
 DevToolsWindow* DevToolsWindow::CreateDevToolsWindowForWorker(
     Profile* profile) {
-  return Create(profile, GURL(), NULL, DEVTOOLS_DOCK_SIDE_UNDOCKED, true);
+  return Create(profile, GURL(), NULL, DEVTOOLS_DOCK_SIDE_UNDOCKED, true,
+                false);
 }
 
 // static
@@ -360,7 +361,7 @@
   DevToolsWindow* window = FindDevToolsWindow(agent_host);
   if (!window) {
     window = Create(profile, DevToolsUI::GetProxyURL(frontend_url), NULL,
-                    DEVTOOLS_DOCK_SIDE_UNDOCKED, false);
+                    DEVTOOLS_DOCK_SIDE_UNDOCKED, false, true);
     content::DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor(
         agent_host, window->frontend_host_.get());
   }
@@ -381,7 +382,7 @@
     Profile* profile = Profile::FromBrowserContext(
         inspected_rvh->GetProcess()->GetBrowserContext());
     DevToolsDockSide dock_side = GetDockSideFromPrefs(profile);
-    window = Create(profile, GURL(), inspected_rvh, dock_side, false);
+    window = Create(profile, GURL(), inspected_rvh, dock_side, false, false);
     manager->RegisterDevToolsClientHostFor(agent.get(),
                                            window->frontend_host_.get());
     do_open = true;
@@ -590,10 +591,12 @@
     const GURL& frontend_url,
     content::RenderViewHost* inspected_rvh,
     DevToolsDockSide dock_side,
-    bool shared_worker_frontend) {
+    bool shared_worker_frontend,
+    bool external_frontend) {
   // Create WebContents with devtools.
   GURL url(GetDevToolsURL(profile, frontend_url, dock_side,
-                          shared_worker_frontend));
+                          shared_worker_frontend,
+                          external_frontend));
   return new DevToolsWindow(profile, url, inspected_rvh, dock_side);
 }
 
@@ -601,7 +604,8 @@
 GURL DevToolsWindow::GetDevToolsURL(Profile* profile,
                                     const GURL& base_url,
                                     DevToolsDockSide dock_side,
-                                    bool shared_worker_frontend) {
+                                    bool shared_worker_frontend,
+                                    bool external_frontend) {
   std::string frontend_url(
       base_url.is_empty() ? chrome::kChromeUIDevToolsURL : base_url.spec());
   ThemeService* tp = ThemeServiceFactory::GetForProfile(profile);
@@ -616,6 +620,8 @@
       SkColorToRGBAString(tp->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT)));
   if (shared_worker_frontend)
     url_string += "&isSharedWorker=true";
+  if (external_frontend)
+    url_string += "&remoteFrontend=true";
   if (CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kEnableDevToolsExperiments))
     url_string += "&experiments=true";
diff --git a/chrome/browser/devtools/devtools_window.h b/chrome/browser/devtools/devtools_window.h
index fc2faba..cd556be 100644
--- a/chrome/browser/devtools/devtools_window.h
+++ b/chrome/browser/devtools/devtools_window.h
@@ -135,11 +135,13 @@
                                 const GURL& frontend_url,
                                 content::RenderViewHost* inspected_rvh,
                                 DevToolsDockSide dock_side,
-                                bool shared_worker_frontend);
+                                bool shared_worker_frontend,
+                                bool external_frontend);
   static GURL GetDevToolsURL(Profile* profile,
                              const GURL& base_url,
                              DevToolsDockSide dock_side,
-                             bool shared_worker_frontend);
+                             bool shared_worker_frontend,
+                             bool external_frontend);
   static DevToolsWindow* FindDevToolsWindow(content::DevToolsAgentHost*);
   static DevToolsWindow* AsDevToolsWindow(content::RenderViewHost*);
   static DevToolsDockSide GetDockSideFromPrefs(Profile* profile);
diff --git a/chrome/browser/devtools/tethering_adb_filter.cc b/chrome/browser/devtools/tethering_adb_filter.cc
index 0bb5ad3..9a06bfe 100644
--- a/chrome/browser/devtools/tethering_adb_filter.cc
+++ b/chrome/browser/devtools/tethering_adb_filter.cc
@@ -47,6 +47,7 @@
 
 static const char kChromeProductName[] = "Chrome";
 static const char kDevToolsRemoteBrowserTarget[] = "/devtools/browser";
+const int kMinVersionPortForwarding = 28;
 
 class SocketTunnel {
  public:
@@ -236,6 +237,10 @@
     left.begin(), left.end(), right.begin(), right.end());
 }
 
+static bool IsPortForwardingSupported(const ParsedVersion& version) {
+  return !version.empty() && version[0] >= kMinVersionPortForwarding;
+}
+
 static std::string FindBestSocketForTethering(
     const DevToolsAdbBridge::RemoteBrowsers browsers) {
   std::string socket;
@@ -245,6 +250,7 @@
     scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser = *it;
     ParsedVersion current_version = ParseVersion(browser->version());
     if (browser->product() == kChromeProductName &&
+        IsPortForwardingSupported(current_version) &&
         IsVersionLower(newest_version, current_version)) {
       socket = browser->socket();
       newest_version = current_version;
diff --git a/chrome/browser/diagnostics/diagnostics_controller.cc b/chrome/browser/diagnostics/diagnostics_controller.cc
index 76bf7c9..4095535 100644
--- a/chrome/browser/diagnostics/diagnostics_controller.cc
+++ b/chrome/browser/diagnostics/diagnostics_controller.cc
@@ -9,11 +9,17 @@
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram.h"
 #include "base/time/time.h"
 #include "chrome/browser/diagnostics/diagnostics_model.h"
+#include "chrome/browser/diagnostics/diagnostics_test.h"
 #include "chrome/browser/diagnostics/diagnostics_writer.h"
 #include "chrome/common/chrome_switches.h"
 
+#if defined(OS_CHROMEOS)
+#include "chromeos/chromeos_switches.h"
+#endif
+
 namespace diagnostics {
 
 DiagnosticsController* DiagnosticsController::GetInstance() {
@@ -34,6 +40,22 @@
 
 void DiagnosticsController::ClearResults() { model_.reset(); }
 
+void DiagnosticsController::RecordRegularStartup() {
+#if defined(OS_CHROMEOS)  // Only collecting UMA stats on ChromeOS
+  // Count the number of normal starts, so we can compare that with the number
+  // of recovery runs to get a percentage.
+  UMA_HISTOGRAM_ENUMERATION(
+      "Diagnostics.RecoveryRun", RECOVERY_NOT_RUN, RECOVERY_RUN_METRICS_COUNT);
+
+  // For each of the test types, record a normal start (no diagnostics run), so
+  // we have a common denominator.
+  for (int i = 0; i < DIAGNOSTICS_TEST_ID_COUNT; ++i) {
+    RecordUMARecoveryResult(static_cast<DiagnosticsTestId>(i), RESULT_NOT_RUN);
+    RecordUMATestResult(static_cast<DiagnosticsTestId>(i), RESULT_NOT_RUN);
+  }
+#endif
+}
+
 // This entry point is called from early in startup when very few things have
 // been initialized, so be careful what you use.
 int DiagnosticsController::Run(const CommandLine& command_line,
@@ -50,6 +72,20 @@
 // been initialized, so be careful what you use.
 int DiagnosticsController::RunRecovery(const CommandLine& command_line,
                                        DiagnosticsWriter* writer) {
+// Separate out recoveries that we execute automatically as a result of a
+// crash from user-run recoveries.
+#if defined(OS_CHROMEOS)  // Only collecting UMA stats on ChromeOS
+  if (command_line.HasSwitch(chromeos::switches::kLoginUser)) {
+    UMA_HISTOGRAM_ENUMERATION("Diagnostics.RecoveryRun",
+                              diagnostics::RECOVERY_CRASH_RUN,
+                              diagnostics::RECOVERY_RUN_METRICS_COUNT);
+  } else {
+    UMA_HISTOGRAM_ENUMERATION("Diagnostics.RecoveryRun",
+                              diagnostics::RECOVERY_USER_RUN,
+                              diagnostics::RECOVERY_RUN_METRICS_COUNT);
+  }
+#endif
+
   if (!HasResults()) {
     if (writer) {
       writer->WriteInfoLine("No diagnostics have been run.");
diff --git a/chrome/browser/diagnostics/diagnostics_controller.h b/chrome/browser/diagnostics/diagnostics_controller.h
index ff25fe1..f5d3d22 100644
--- a/chrome/browser/diagnostics/diagnostics_controller.h
+++ b/chrome/browser/diagnostics/diagnostics_controller.h
@@ -38,6 +38,11 @@
   // GetResults until after Run is called again.
   void ClearResults();
 
+  // Records UMA statistics indicating that a regular Chrome startup happened,
+  // with no diagnostics or recovery being run.  This is necessary to provide a
+  // denominator for the diagnostics metrics.
+  void RecordRegularStartup();
+
  private:
   friend struct DefaultSingletonTraits<DiagnosticsController>;
 
diff --git a/chrome/browser/diagnostics/diagnostics_controller_unittest.cc b/chrome/browser/diagnostics/diagnostics_controller_unittest.cc
index f4505fd..0a408bb 100644
--- a/chrome/browser/diagnostics/diagnostics_controller_unittest.cc
+++ b/chrome/browser/diagnostics/diagnostics_controller_unittest.cc
@@ -101,7 +101,7 @@
   for (int i = 0; i < results.GetTestRunCount(); ++i) {
     const DiagnosticsModel::TestInfo& info(results.GetTest(i));
     EXPECT_EQ(DiagnosticsModel::TEST_OK, info.GetResult()) << "Test: "
-                                                           << info.GetId();
+                                                           << info.GetName();
   }
 }
 
@@ -115,8 +115,8 @@
   EXPECT_EQ(DiagnosticsModel::kDiagnosticsTestCount, results.GetTestRunCount());
   for (int i = 0; i < results.GetTestRunCount(); ++i) {
     const DiagnosticsModel::TestInfo& info(results.GetTest(i));
-    EXPECT_EQ(DiagnosticsModel::RECOVERY_OK, info.GetResult()) << "Test: "
-                                                               << info.GetId();
+    EXPECT_EQ(DiagnosticsModel::RECOVERY_OK, info.GetResult())
+        << "Test: " << info.GetName();
   }
 }
 
@@ -133,7 +133,8 @@
   EXPECT_EQ(DiagnosticsModel::kDiagnosticsTestCount, results.GetTestRunCount());
 
   const DiagnosticsModel::TestInfo* info = NULL;
-  EXPECT_TRUE(results.GetTestInfo(kSQLiteIntegrityNSSCertTest, &info));
+  EXPECT_TRUE(
+      results.GetTestInfo(DIAGNOSTICS_SQLITE_INTEGRITY_NSS_CERT_TEST, &info));
   EXPECT_EQ(DiagnosticsModel::TEST_FAIL_CONTINUE, info->GetResult());
   EXPECT_EQ(DIAG_SQLITE_ERROR_HANDLER_CALLED, info->GetOutcomeCode());
 
@@ -154,7 +155,8 @@
   EXPECT_EQ(DiagnosticsModel::kDiagnosticsTestCount, results.GetTestRunCount());
 
   const DiagnosticsModel::TestInfo* info = NULL;
-  EXPECT_TRUE(results.GetTestInfo(kSQLiteIntegrityNSSKeyTest, &info));
+  EXPECT_TRUE(
+      results.GetTestInfo(DIAGNOSTICS_SQLITE_INTEGRITY_NSS_KEY_TEST, &info));
   EXPECT_EQ(DiagnosticsModel::TEST_FAIL_CONTINUE, info->GetResult());
   EXPECT_EQ(DIAG_SQLITE_ERROR_HANDLER_CALLED, info->GetOutcomeCode());
 
diff --git a/chrome/browser/diagnostics/diagnostics_metrics.cc b/chrome/browser/diagnostics/diagnostics_metrics.cc
new file mode 100644
index 0000000..7dd6ec7
--- /dev/null
+++ b/chrome/browser/diagnostics/diagnostics_metrics.cc
@@ -0,0 +1,128 @@
+// 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 "chrome/browser/diagnostics/diagnostics_metrics.h"
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/metrics/histogram.h"
+#include "chrome/browser/diagnostics/diagnostics_test.h"
+
+namespace diagnostics {
+
+namespace {
+
+// A struct to hold information about the tests.
+struct TestNameInfo {
+  // Should only contain characters [A-Za-z0-9] (no spaces).
+  const char* name;
+
+  // A non-localized description only meant for developer consumption.
+  const char* description;
+};
+
+// This structure MUST have DIAGNOSTICS_TEST_COUNT entries in it: one for each
+// value of DiagnosticsTestId.  Note that the values in the "name" fields are
+// used for UMA metrics names (with "Diagnostics.Test." or
+// "Diagnostics.Recovery." prepended), so do not change them without
+// understanding the consequences.
+const TestNameInfo kTestNameInfo[] = {
+    {"ConflictingDlls", "Conflicting modules"},
+    {"DiskSpace", "Available disk space"},
+    {"InstallType", "Install type"},
+    {"JSONBookmarks", "Bookmark file"},
+    {"JSONLocalState", "Local state integrity"},
+    {"JSONPreferences", "User preferences integrity"},
+    {"OperatingSystem", "Operating system supported version"},
+    {"PathDictionaries", "App dictionaries directory path"},
+    {"PathLocalState", "Local state path"},
+    {"PathResources", "Resources path"},
+    {"PathUserData", "User data path"},
+    {"Version", "Chrome version test"},
+    {"SQLiteIntegrityAppCache", "Application cache database"},
+    {"SQLiteIntegrityArchivedHistory", "Archived history database"},
+    {"SQLiteIntegrityCookie", "Cookie database"},
+    {"SQLiteIntegrityDatabaseTracker", "Database tracker database"},
+    {"SQLiteIntegrityHistory", "History database"},
+    {"SQLiteIntegrityNSSCert", "NSS certificate database"},
+    {"SQLiteIntegrityNSSKey", "NSS Key database"},
+    {"SQLiteIntegrityThumbnails", "Thumbnails database"},
+    {"SQLiteIntegrityWebData", "Web Data database"},
+    // Add new entries in the same order as DiagnosticsTestId.
+};
+
+COMPILE_ASSERT(arraysize(kTestNameInfo) == DIAGNOSTICS_TEST_ID_COUNT,
+               diagnostics_test_info_mismatch);
+
+const TestNameInfo* FindTestInfo(DiagnosticsTestId id) {
+  DCHECK(id < DIAGNOSTICS_TEST_ID_COUNT);
+  return &kTestNameInfo[id];
+}
+
+}  // namespace
+
+std::string GetTestName(DiagnosticsTestId id) {
+  return std::string(FindTestInfo(id)->name);
+}
+
+std::string GetTestDescription(DiagnosticsTestId id) {
+  return std::string(FindTestInfo(id)->description);
+}
+
+#define TEST_CASE(name, id)                                \
+  case id:                                                 \
+    UMA_HISTOGRAM_ENUMERATION(name, result, RESULT_COUNT); \
+    break
+
+// These must each have their own complete case so that the UMA macros create
+// a unique static pointer block for each individual metric.  This is done as a
+// macro to prevent errors where the ID is added to one function below, but not
+// the other, because they must match.
+#define TEST_CASES(name)                                               \
+  TEST_CASE(name, DIAGNOSTICS_CONFLICTING_DLLS_TEST);                  \
+  TEST_CASE(name, DIAGNOSTICS_DISK_SPACE_TEST);                        \
+  TEST_CASE(name, DIAGNOSTICS_INSTALL_TYPE_TEST);                      \
+  TEST_CASE(name, DIAGNOSTICS_JSON_BOOKMARKS_TEST);                    \
+  TEST_CASE(name, DIAGNOSTICS_JSON_LOCAL_STATE_TEST);                  \
+  TEST_CASE(name, DIAGNOSTICS_JSON_PREFERENCES_TEST);                  \
+  TEST_CASE(name, DIAGNOSTICS_OPERATING_SYSTEM_TEST);                  \
+  TEST_CASE(name, DIAGNOSTICS_PATH_DICTIONARIES_TEST);                 \
+  TEST_CASE(name, DIAGNOSTICS_PATH_LOCAL_STATE_TEST);                  \
+  TEST_CASE(name, DIAGNOSTICS_PATH_RESOURCES_TEST);                    \
+  TEST_CASE(name, DIAGNOSTICS_PATH_USER_DATA_TEST);                    \
+  TEST_CASE(name, DIAGNOSTICS_VERSION_TEST);                           \
+  TEST_CASE(name, DIAGNOSTICS_SQLITE_INTEGRITY_APP_CACHE_TEST);        \
+  TEST_CASE(name, DIAGNOSTICS_SQLITE_INTEGRITY_ARCHIVED_HISTORY_TEST); \
+  TEST_CASE(name, DIAGNOSTICS_SQLITE_INTEGRITY_COOKIE_TEST);           \
+  TEST_CASE(name, DIAGNOSTICS_SQLITE_INTEGRITY_DATABASE_TRACKER_TEST); \
+  TEST_CASE(name, DIAGNOSTICS_SQLITE_INTEGRITY_HISTORY_TEST);          \
+  TEST_CASE(name, DIAGNOSTICS_SQLITE_INTEGRITY_NSS_CERT_TEST);         \
+  TEST_CASE(name, DIAGNOSTICS_SQLITE_INTEGRITY_NSS_KEY_TEST);          \
+  TEST_CASE(name, DIAGNOSTICS_SQLITE_INTEGRITY_THUMBNAILS_TEST);       \
+  TEST_CASE(name, DIAGNOSTICS_SQLITE_INTEGRITY_WEB_DATA_TEST)
+
+void RecordUMARecoveryResult(DiagnosticsTestId id, RunResultMetrics result) {
+  const std::string name("Diagnostics.Recovery." +
+                         GetTestName(static_cast<DiagnosticsTestId>(id)));
+  switch (id) {
+    TEST_CASES(name);  // See above
+    default:
+      NOTREACHED() << "Unhandled UMA Metric type" << id;
+  }
+}
+
+void RecordUMATestResult(DiagnosticsTestId id, RunResultMetrics result) {
+  const std::string name("Diagnostics.Test." +
+                         GetTestName(static_cast<DiagnosticsTestId>(id)));
+  switch (id) {
+    TEST_CASES(name);  // See above
+    default:
+      NOTREACHED() << "Unhandled UMA Metric type" << id;
+  }
+}
+#undef TEST_CASE
+#undef TEST_CASES
+
+}  // namespace diagnostics
diff --git a/chrome/browser/diagnostics/diagnostics_metrics.h b/chrome/browser/diagnostics/diagnostics_metrics.h
new file mode 100644
index 0000000..2d84bd0
--- /dev/null
+++ b/chrome/browser/diagnostics/diagnostics_metrics.h
@@ -0,0 +1,80 @@
+// 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 CHROME_BROWSER_DIAGNOSTICS_DIAGNOSTICS_METRICS_H_
+#define CHROME_BROWSER_DIAGNOSTICS_DIAGNOSTICS_METRICS_H_
+
+#include <string>
+
+namespace diagnostics {
+
+// Test IDs used to indicate in UMA stats which diagnostics fail, and also to
+// look up string identifiers for tests. If you add an ID here, you will also
+// need to add corresponding strings to several things in the .cc file.
+enum DiagnosticsTestId {
+  DIAGNOSTICS_CONFLICTING_DLLS_TEST,
+  DIAGNOSTICS_DISK_SPACE_TEST,
+  DIAGNOSTICS_INSTALL_TYPE_TEST,
+  DIAGNOSTICS_JSON_BOOKMARKS_TEST,
+  DIAGNOSTICS_JSON_LOCAL_STATE_TEST,
+  DIAGNOSTICS_JSON_PREFERENCES_TEST,
+  DIAGNOSTICS_OPERATING_SYSTEM_TEST,
+  DIAGNOSTICS_PATH_DICTIONARIES_TEST,
+  DIAGNOSTICS_PATH_LOCAL_STATE_TEST,
+  DIAGNOSTICS_PATH_RESOURCES_TEST,
+  DIAGNOSTICS_PATH_USER_DATA_TEST,
+  DIAGNOSTICS_VERSION_TEST,
+  DIAGNOSTICS_SQLITE_INTEGRITY_APP_CACHE_TEST,
+  DIAGNOSTICS_SQLITE_INTEGRITY_ARCHIVED_HISTORY_TEST,
+  DIAGNOSTICS_SQLITE_INTEGRITY_COOKIE_TEST,
+  DIAGNOSTICS_SQLITE_INTEGRITY_DATABASE_TRACKER_TEST,
+  DIAGNOSTICS_SQLITE_INTEGRITY_HISTORY_TEST,
+  DIAGNOSTICS_SQLITE_INTEGRITY_NSS_CERT_TEST,
+  DIAGNOSTICS_SQLITE_INTEGRITY_NSS_KEY_TEST,
+  DIAGNOSTICS_SQLITE_INTEGRITY_THUMBNAILS_TEST,
+  DIAGNOSTICS_SQLITE_INTEGRITY_WEB_DATA_TEST,
+  // Add new entries immediately above this comment. Do not reorder or renumber
+  // the entries, as they are tied to historical enum values in the UMA stats.
+  // If you add an entry, you will need to also add an entry to kTestNameInfo,
+  // and to the TEST_CASES macro in the .cc.
+
+  // This must always be last in the list.
+  DIAGNOSTICS_TEST_ID_COUNT
+};
+
+// Enumeration of metrics for UMA recording of recovery runs.
+enum RecoveryRunMetrics {
+  RECOVERY_NOT_RUN,
+  RECOVERY_CRASH_RUN,
+  RECOVERY_USER_RUN,
+  // Add new items above this line.
+  RECOVERY_RUN_METRICS_COUNT
+};
+
+// Possible enum values for individual test metrics.
+enum RunResultMetrics {
+  RESULT_NOT_RUN,
+  RESULT_SUCCESS,
+  RESULT_FAILURE,
+  RESULT_SKIPPED,
+  // Add new items above this line.
+  RESULT_COUNT
+};
+
+// Returns the string identifier of a test |id|. It will only contain
+// characters [A-Za-z0-9] with no spaces.
+std::string GetTestName(DiagnosticsTestId id);
+
+// Returns the string description of a test |id|. This is not a localized
+// string. It is only meant for developer consumption, because this function
+// will be called before the localization services are initialized.
+std::string GetTestDescription(DiagnosticsTestId id);
+
+// These record an UMA metric for the given test or recovery operation.
+void RecordUMARecoveryResult(DiagnosticsTestId id, RunResultMetrics result);
+void RecordUMATestResult(DiagnosticsTestId id, RunResultMetrics result);
+
+}  // namespace diagnostics
+
+#endif  // CHROME_BROWSER_DIAGNOSTICS_DIAGNOSTICS_METRICS_H_
diff --git a/chrome/browser/diagnostics/diagnostics_model.cc b/chrome/browser/diagnostics/diagnostics_model.cc
index 91e3305..1216bf0 100644
--- a/chrome/browser/diagnostics/diagnostics_model.cc
+++ b/chrome/browser/diagnostics/diagnostics_model.cc
@@ -49,25 +49,31 @@
  public:
   DiagnosticsModelImpl() : tests_run_(0) {}
 
-  virtual ~DiagnosticsModelImpl() {
-    STLDeleteElements(&tests_);
-  }
+  virtual ~DiagnosticsModelImpl() { STLDeleteElements(&tests_); }
 
-  virtual int GetTestRunCount() const OVERRIDE {
-    return tests_run_;
-  }
+  virtual int GetTestRunCount() const OVERRIDE { return tests_run_; }
 
-  virtual int GetTestAvailableCount() const OVERRIDE {
-    return tests_.size();
-  }
+  virtual int GetTestAvailableCount() const OVERRIDE { return tests_.size(); }
 
   virtual void RunAll(DiagnosticsModel::Observer* observer) OVERRIDE {
     size_t test_count = tests_.size();
-    for (size_t ix = 0; ix != test_count; ++ix) {
-      bool do_next = RunTest(tests_[ix], observer, ix);
-      ++tests_run_;
-      if (!do_next)
+    bool continue_running = true;
+    for (size_t i = 0; i != test_count; ++i) {
+      // If one of the diagnostic steps returns false, we want to
+      // mark the rest of them as "skipped" in the UMA stats.
+      if (continue_running) {
+        continue_running = RunTest(tests_[i], observer, i);
+        ++tests_run_;
+      } else {
+#if defined(OS_CHROMEOS)  // Only collecting UMA stats on ChromeOS
+        RecordUMATestResult(static_cast<DiagnosticsTestId>(tests_[i]->GetId()),
+                            RESULT_SKIPPED);
+#else
+        // On other platforms, we can just bail out if a diagnostic step returns
+        // false.
         break;
+#endif
+      }
     }
     if (observer)
       observer->OnAllTestsDone(this);
@@ -75,10 +81,22 @@
 
   virtual void RecoverAll(DiagnosticsModel::Observer* observer) OVERRIDE {
     size_t test_count = tests_.size();
+    bool continue_running = true;
     for (size_t i = 0; i != test_count; ++i) {
-      bool do_next = RunRecovery(tests_[i], observer, i);
-      if (!do_next)
+      // If one of the recovery steps returns false, we want to
+      // mark the rest of them as "skipped" in the UMA stats.
+      if (continue_running) {
+        continue_running = RunRecovery(tests_[i], observer, i);
+      } else {
+#if defined(OS_CHROMEOS)  // Only collecting UMA stats on ChromeOS
+        RecordUMARecoveryResult(
+            static_cast<DiagnosticsTestId>(tests_[i]->GetId()), RESULT_SKIPPED);
+#else
+        // On other platforms, we can just bail out if a recovery step returns
+        // false.
         break;
+#endif
+      }
     }
     if (observer)
       observer->OnAllRecoveryDone(this);
@@ -88,8 +106,9 @@
     return *tests_[index];
   }
 
-  virtual bool GetTestInfo(const std::string& id,
-                           const TestInfo** result) const OVERRIDE {
+  virtual bool GetTestInfo(int id, const TestInfo** result) const OVERRIDE {
+    DCHECK(id < DIAGNOSTICS_TEST_ID_COUNT);
+    DCHECK(id >= 0);
     for (size_t i = 0; i < tests_.size(); i++) {
       if (tests_[i]->GetId() == id) {
         *result = tests_[i];
@@ -142,7 +161,7 @@
     tests_.push_back(MakePreferencesTest());
     tests_.push_back(MakeLocalStateTest());
     tests_.push_back(MakeBookMarksTest());
-    tests_.push_back(MakeSqliteWebDbTest());
+    tests_.push_back(MakeSqliteWebDataDbTest());
     tests_.push_back(MakeSqliteCookiesDbTest());
     tests_.push_back(MakeSqliteHistoryDbTest());
     tests_.push_back(MakeSqliteArchivedHistoryDbTest());
@@ -167,7 +186,7 @@
     tests_.push_back(MakePreferencesTest());
     tests_.push_back(MakeLocalStateTest());
     tests_.push_back(MakeBookMarksTest());
-    tests_.push_back(MakeSqliteWebDbTest());
+    tests_.push_back(MakeSqliteWebDataDbTest());
     tests_.push_back(MakeSqliteCookiesDbTest());
     tests_.push_back(MakeSqliteHistoryDbTest());
     tests_.push_back(MakeSqliteArchivedHistoryDbTest());
@@ -194,7 +213,7 @@
     tests_.push_back(MakePreferencesTest());
     tests_.push_back(MakeLocalStateTest());
     tests_.push_back(MakeBookMarksTest());
-    tests_.push_back(MakeSqliteWebDbTest());
+    tests_.push_back(MakeSqliteWebDataDbTest());
     tests_.push_back(MakeSqliteCookiesDbTest());
     tests_.push_back(MakeSqliteHistoryDbTest());
     tests_.push_back(MakeSqliteArchivedHistoryDbTest());
diff --git a/chrome/browser/diagnostics/diagnostics_model.h b/chrome/browser/diagnostics/diagnostics_model.h
index 0d9ad73..1727aaa 100644
--- a/chrome/browser/diagnostics/diagnostics_model.h
+++ b/chrome/browser/diagnostics/diagnostics_model.h
@@ -54,8 +54,11 @@
   class TestInfo {
    public:
     virtual ~TestInfo() {}
+    // A numerical id for this test. Must be a unique number among all the
+    // tests.
+    virtual int GetId() const = 0;
     // A parse-able ASCII string that indicates what is being tested.
-    virtual std::string GetId() const = 0;
+    virtual std::string GetName() const = 0;
     // A human readable string that tells you what is being tested.
     // This is not localized: it is only meant for developer consumption.
     virtual std::string GetTitle() const = 0;
@@ -89,11 +92,10 @@
   // Get the information for a particular test. Lifetime of returned object is
   // limited to the lifetime of this model.
   virtual const TestInfo& GetTest(size_t index) const = 0;
-  // Get the information for a test with given |id|. Lifetime of returned object
-  // is limited to the lifetime of this model. Returns false if there is no such
-  // id. |result| may not be NULL.
-  virtual bool GetTestInfo(const std::string& id,
-                           const TestInfo** result) const = 0;
+  // Get the information for a test with given numerical |id|. Lifetime of
+  // returned object is limited to the lifetime of this model. Returns false if
+  // there is no such id. |result| may not be NULL.
+  virtual bool GetTestInfo(int id, const TestInfo** result) const = 0;
 };
 
 // The factory for the model. The main purpose is to hide the creation of
diff --git a/chrome/browser/diagnostics/diagnostics_test.cc b/chrome/browser/diagnostics/diagnostics_test.cc
index 4e6e020..65482da 100644
--- a/chrome/browser/diagnostics/diagnostics_test.cc
+++ b/chrome/browser/diagnostics/diagnostics_test.cc
@@ -5,18 +5,16 @@
 #include "chrome/browser/diagnostics/diagnostics_test.h"
 
 #include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
 #include "base/path_service.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_paths.h"
 
 namespace diagnostics {
 
-DiagnosticsTest::DiagnosticsTest(const std::string& id,
-                                 const std::string& title)
-    : id_(id),
-      title_(title),
-      outcome_code_(-1),
-      result_(DiagnosticsModel::TEST_NOT_RUN) {}
+DiagnosticsTest::DiagnosticsTest(DiagnosticsTestId id)
+    : id_(id), outcome_code_(-1), result_(DiagnosticsModel::TEST_NOT_RUN) {}
 
 DiagnosticsTest::~DiagnosticsTest() {}
 
@@ -38,6 +36,15 @@
   bool keep_going = RecoveryImpl(observer);
   result_ = keep_going ? DiagnosticsModel::RECOVERY_OK
                        : DiagnosticsModel::RECOVERY_FAIL_STOP;
+#if defined(OS_CHROMEOS)  // Only collecting UMA stats on ChromeOS
+  if (result_ == DiagnosticsModel::RECOVERY_OK) {
+    RecordUMARecoveryResult(static_cast<DiagnosticsTestId>(GetId()),
+                            RESULT_SUCCESS);
+  } else {
+    RecordUMARecoveryResult(static_cast<DiagnosticsTestId>(GetId()),
+                            RESULT_FAILURE);
+  }
+#endif
   if (observer)
     observer->OnRecoveryFinished(index, model);
   return keep_going;
@@ -50,6 +57,21 @@
   outcome_code_ = outcome_code;
   additional_info_ = additional_info;
   result_ = result;
+#if defined(OS_CHROMEOS)  // Only collecting UMA stats on ChromeOS
+  if (result_ == DiagnosticsModel::TEST_OK) {
+    // Record individual test success.
+    RecordUMATestResult(static_cast<DiagnosticsTestId>(GetId()),
+                        RESULT_SUCCESS);
+  } else if (result_ == DiagnosticsModel::TEST_FAIL_CONTINUE ||
+             result_ == DiagnosticsModel::TEST_FAIL_STOP) {
+    // Record test failure in summary histogram.
+    UMA_HISTOGRAM_ENUMERATION(
+        "Diagnostics.TestFailures", GetId(), DIAGNOSTICS_TEST_ID_COUNT);
+    // Record individual test failure.
+    RecordUMATestResult(static_cast<DiagnosticsTestId>(GetId()),
+                        RESULT_FAILURE);
+  }
+#endif
 }
 
 // static
@@ -60,9 +82,13 @@
   return path.AppendASCII(chrome::kInitialProfile);
 }
 
-std::string DiagnosticsTest::GetId() const { return id_; }
+int DiagnosticsTest::GetId() const { return id_; }
 
-std::string DiagnosticsTest::GetTitle() const { return title_; }
+std::string DiagnosticsTest::GetName() const { return GetTestName(id_); }
+
+std::string DiagnosticsTest::GetTitle() const {
+  return GetTestDescription(id_);
+}
 
 DiagnosticsModel::TestResult DiagnosticsTest::GetResult() const {
   return result_;
@@ -80,7 +106,6 @@
 
 bool DiagnosticsTest::RecoveryImpl(DiagnosticsModel::Observer* observer) {
   return true;
-};
-
+}
 
 }  // namespace diagnostics
diff --git a/chrome/browser/diagnostics/diagnostics_test.h b/chrome/browser/diagnostics/diagnostics_test.h
index 8d45a10..938ccda 100644
--- a/chrome/browser/diagnostics/diagnostics_test.h
+++ b/chrome/browser/diagnostics/diagnostics_test.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_DIAGNOSTICS_DIAGNOSTICS_TEST_H_
 
 #include "base/compiler_specific.h"
+#include "chrome/browser/diagnostics/diagnostics_metrics.h"
 #include "chrome/browser/diagnostics/diagnostics_model.h"
 
 namespace base {
@@ -26,11 +27,7 @@
 // 4- Optionally call observer->OnSkipped() if the test cannot be run.
 class DiagnosticsTest : public DiagnosticsModel::TestInfo {
  public:
-  // |id| is a parse-able ASCII ID string that uniquely identifies the test. It
-  // should only have letters, numbers and underscores in it (and no spaces).
-  // |title| is the human readable string that says what the objective of the
-  // test is.
-  DiagnosticsTest(const std::string& id, const std::string& title);
+  explicit DiagnosticsTest(DiagnosticsTestId id);
 
   virtual ~DiagnosticsTest();
 
@@ -65,7 +62,8 @@
   static base::FilePath GetUserDefaultProfileDir();
 
   // DiagnosticsModel::TestInfo overrides
-  virtual std::string GetId() const OVERRIDE;
+  virtual int GetId() const OVERRIDE;
+  virtual std::string GetName() const OVERRIDE;
   virtual std::string GetTitle() const OVERRIDE;
   virtual DiagnosticsModel::TestResult GetResult() const OVERRIDE;
   virtual std::string GetAdditionalInfo() const OVERRIDE;
@@ -80,8 +78,7 @@
   // makes sense for the diagnostics test.
   virtual bool RecoveryImpl(DiagnosticsModel::Observer* observer);
 
-  const std::string id_;
-  const std::string title_;
+  const DiagnosticsTestId id_;
   std::string additional_info_;
   int outcome_code_;
   DiagnosticsModel::TestResult result_;
diff --git a/chrome/browser/diagnostics/diagnostics_writer.cc b/chrome/browser/diagnostics/diagnostics_writer.cc
index 0de54a6..4b95ef6 100644
--- a/chrome/browser/diagnostics/diagnostics_writer.cc
+++ b/chrome/browser/diagnostics/diagnostics_writer.cc
@@ -226,7 +226,7 @@
   const DiagnosticsModel::TestInfo& test_info = model->GetTest(index);
   bool success = (DiagnosticsModel::TEST_OK == test_info.GetResult());
   WriteResult(success,
-              test_info.GetId(),
+              test_info.GetName(),
               test_info.GetTitle(),
               test_info.GetOutcomeCode(),
               test_info.GetAdditionalInfo());
diff --git a/chrome/browser/diagnostics/recon_diagnostics.cc b/chrome/browser/diagnostics/recon_diagnostics.cc
index 2761f7c..c59c35c 100644
--- a/chrome/browser/diagnostics/recon_diagnostics.cc
+++ b/chrome/browser/diagnostics/recon_diagnostics.cc
@@ -36,59 +36,19 @@
 
 namespace diagnostics {
 
-const char kConflictingDllsTest[] = "ConflictingDlls";
-const char kDiskSpaceTest[] = "DiskSpace";
-const char kInstallTypeTest[] = "InstallType";
-const char kJSONBookmarksTest[] = "JSONBookmarks";
-const char kJSONLocalStateTest[] = "JSONLocalState";
-const char kJSONProfileTest[] = "JSONProfile";
-const char kOperatingSystemTest[] = "OperatingSystem";
-const char kPathDictionariesTest[] = "PathDictionaries";
-const char kPathLocalStateTest[] = "PathLocalState";
-const char kPathResourcesTest[] = "PathResources";
-const char kPathUserDataTest[] = "PathUserData";
-const char kVersionTest[] = "Version";
-
 namespace {
 
+const int64 kOneKilobyte = 1024;
+const int64 kOneMegabyte = 1024 * kOneKilobyte;
+
 class InstallTypeTest;
 InstallTypeTest* g_install_type = 0;
 
-// Check that the flavor of the operating system is supported.
-class OperatingSystemTest : public DiagnosticsTest {
- public:
-  OperatingSystemTest()
-      : DiagnosticsTest(kOperatingSystemTest, "Operating System") {}
-
-  virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
-#if defined(OS_WIN)
-    base::win::Version version = base::win::GetVersion();
-    if ((version < base::win::VERSION_XP) ||
-        ((version == base::win::VERSION_XP) &&
-         (base::win::OSInfo::GetInstance()->service_pack().major < 2))) {
-      RecordFailure(DIAG_RECON_PRE_WINDOW_XP_SP2,
-                    "Must have Windows XP SP2 or later");
-      return false;
-    }
-#else
-// TODO(port): define the OS criteria for Linux and Mac.
-#endif  // defined(OS_WIN)
-    RecordSuccess(
-        base::StringPrintf("%s %s",
-                           base::SysInfo::OperatingSystemName().c_str(),
-                           base::SysInfo::OperatingSystemVersion().c_str()));
-    return true;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(OperatingSystemTest);
-};
-
 // Check if any conflicting DLLs are loaded.
 class ConflictingDllsTest : public DiagnosticsTest {
  public:
   ConflictingDllsTest()
-      : DiagnosticsTest(kConflictingDllsTest, "Conflicting modules") {}
+      : DiagnosticsTest(DIAGNOSTICS_CONFLICTING_DLLS_TEST) {}
 
   virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
 #if defined(OS_WIN)
@@ -140,11 +100,40 @@
   DISALLOW_COPY_AND_ASSIGN(ConflictingDllsTest);
 };
 
+// Check that the disk space in the volume where the user data directory
+// normally lives is not dangerously low.
+class DiskSpaceTest : public DiagnosticsTest {
+ public:
+  DiskSpaceTest() : DiagnosticsTest(DIAGNOSTICS_DISK_SPACE_TEST) {}
+
+  virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
+    base::FilePath data_dir;
+    if (!PathService::Get(chrome::DIR_USER_DATA, &data_dir))
+      return false;
+    int64 disk_space = base::SysInfo::AmountOfFreeDiskSpace(data_dir);
+    if (disk_space < 0) {
+      RecordFailure(DIAG_RECON_UNABLE_TO_QUERY, "Unable to query free space");
+      return true;
+    }
+    std::string printable_size = base::Int64ToString(disk_space);
+    if (disk_space < 80 * kOneMegabyte) {
+      RecordFailure(DIAG_RECON_LOW_DISK_SPACE,
+                    "Low disk space: " + printable_size);
+      return true;
+    }
+    RecordSuccess("Free space: " + printable_size);
+    return true;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DiskSpaceTest);
+};
+
 // Check if it is system install or per-user install.
 class InstallTypeTest : public DiagnosticsTest {
  public:
   InstallTypeTest()
-      : DiagnosticsTest(kInstallTypeTest, "Install Type"), user_level_(false) {}
+      : DiagnosticsTest(DIAGNOSTICS_INSTALL_TYPE_TEST), user_level_(false) {}
 
   virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
 #if defined(OS_WIN)
@@ -171,40 +160,110 @@
   DISALLOW_COPY_AND_ASSIGN(InstallTypeTest);
 };
 
-// Check the version of Chrome.
-class VersionTest : public DiagnosticsTest {
+// Checks that a given JSON file can be correctly parsed.
+class JSONTest : public DiagnosticsTest {
  public:
-  VersionTest() : DiagnosticsTest(kVersionTest, "Browser Version") {}
+  enum FileImportance {
+    NON_CRITICAL,
+    CRITICAL
+  };
+
+  JSONTest(const base::FilePath& path,
+           DiagnosticsTestId id,
+           int64 max_file_size,
+           FileImportance importance)
+      : DiagnosticsTest(id),
+        path_(path),
+        max_file_size_(max_file_size),
+        importance_(importance) {}
 
   virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
-    chrome::VersionInfo version_info;
-    if (!version_info.is_valid()) {
-      RecordFailure(DIAG_RECON_NO_VERSION, "No Version");
+    if (!base::PathExists(path_)) {
+      if (importance_ == CRITICAL) {
+        RecordOutcome(DIAG_RECON_FILE_NOT_FOUND,
+                      "File not found",
+                      DiagnosticsModel::TEST_FAIL_CONTINUE);
+      } else {
+        RecordOutcome(DIAG_RECON_FILE_NOT_FOUND_OK,
+                      "File not found (but that is OK)",
+                      DiagnosticsModel::TEST_OK);
+      }
       return true;
     }
-    std::string current_version = version_info.Version();
-    if (current_version.empty()) {
-      RecordFailure(DIAG_RECON_EMPTY_VERSION, "Empty Version");
+    int64 file_size;
+    if (!file_util::GetFileSize(path_, &file_size)) {
+      RecordFailure(DIAG_RECON_CANNOT_OBTAIN_FILE_SIZE,
+                    "Cannot obtain file size");
       return true;
     }
-    std::string version_modifier =
-        chrome::VersionInfo::GetVersionStringModifier();
-    if (!version_modifier.empty())
-      current_version += " " + version_modifier;
-#if defined(GOOGLE_CHROME_BUILD)
-    current_version += " GCB";
-#endif  // defined(GOOGLE_CHROME_BUILD)
-    RecordSuccess(current_version);
+
+    if (file_size > max_file_size_) {
+      RecordFailure(DIAG_RECON_FILE_TOO_BIG, "File too big");
+      return true;
+    }
+    // Being small enough, we can process it in-memory.
+    std::string json_data;
+    if (!file_util::ReadFileToString(path_, &json_data)) {
+      RecordFailure(DIAG_RECON_UNABLE_TO_OPEN_FILE,
+                    "Could not open file. Possibly locked by another process");
+      return true;
+    }
+
+    JSONStringValueSerializer json(json_data);
+    int error_code = base::JSONReader::JSON_NO_ERROR;
+    std::string error_message;
+    scoped_ptr<Value> json_root(json.Deserialize(&error_code, &error_message));
+    if (base::JSONReader::JSON_NO_ERROR != error_code) {
+      if (error_message.empty()) {
+        error_message = "Parse error " + base::IntToString(error_code);
+      }
+      RecordFailure(DIAG_RECON_PARSE_ERROR, error_message);
+      return true;
+    }
+
+    RecordSuccess("File parsed OK");
     return true;
   }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(VersionTest);
+  base::FilePath path_;
+  int64 max_file_size_;
+  FileImportance importance_;
+  DISALLOW_COPY_AND_ASSIGN(JSONTest);
+};
+
+// Check that the flavor of the operating system is supported.
+class OperatingSystemTest : public DiagnosticsTest {
+ public:
+  OperatingSystemTest()
+      : DiagnosticsTest(DIAGNOSTICS_OPERATING_SYSTEM_TEST) {}
+
+  virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
+#if defined(OS_WIN)
+    base::win::Version version = base::win::GetVersion();
+    if ((version < base::win::VERSION_XP) ||
+        ((version == base::win::VERSION_XP) &&
+         (base::win::OSInfo::GetInstance()->service_pack().major < 2))) {
+      RecordFailure(DIAG_RECON_PRE_WINDOW_XP_SP2,
+                    "Must have Windows XP SP2 or later");
+      return false;
+    }
+#else
+// TODO(port): define the OS criteria for Linux and Mac.
+#endif  // defined(OS_WIN)
+    RecordSuccess(
+        base::StringPrintf("%s %s",
+                           base::SysInfo::OperatingSystemName().c_str(),
+                           base::SysInfo::OperatingSystemVersion().c_str()));
+    return true;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(OperatingSystemTest);
 };
 
 struct TestPathInfo {
-  const char* test_name;
-  const char* test_id;
+  DiagnosticsTestId test_id;
   int path_id;
   bool is_directory;
   bool is_optional;
@@ -212,18 +271,15 @@
   int64 max_size;
 };
 
-const int64 kOneKilobyte = 1024;
-const int64 kOneMegabyte = 1024 * kOneKilobyte;
-
 const TestPathInfo kPathsToTest[] = {
-  {"User data Directory", kPathUserDataTest, chrome::DIR_USER_DATA, true, false,
-   true, 850 * kOneMegabyte},
-  {"Local state file", kPathLocalStateTest, chrome::FILE_LOCAL_STATE, false,
-   false, true, 500 * kOneKilobyte},
-  {"Dictionaries Directory", kPathDictionariesTest,
-   chrome::DIR_APP_DICTIONARIES, true, true, false, 0},
-  {"Resources file", kPathResourcesTest, chrome::FILE_RESOURCES_PACK, false,
-   false, false, 0}
+    {DIAGNOSTICS_PATH_DICTIONARIES_TEST, chrome::DIR_APP_DICTIONARIES, true,
+     true, false, 0},
+    {DIAGNOSTICS_PATH_LOCAL_STATE_TEST, chrome::FILE_LOCAL_STATE, false, false,
+     true, 500 * kOneKilobyte},
+    {DIAGNOSTICS_PATH_RESOURCES_TEST, chrome::FILE_RESOURCES_PACK, false, false,
+     false, 0},
+    {DIAGNOSTICS_PATH_USER_DATA_TEST, chrome::DIR_USER_DATA, true, false, true,
+     850 * kOneMegabyte},
 };
 
 // Check that the user's data directory exists and the paths are writable.
@@ -232,7 +288,7 @@
 class PathTest : public DiagnosticsTest {
  public:
   explicit PathTest(const TestPathInfo& path_info)
-      : DiagnosticsTest(path_info.test_id, path_info.test_name),
+      : DiagnosticsTest(path_info.test_id),
         path_info_(path_info) {}
 
   virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
@@ -293,150 +349,50 @@
   DISALLOW_COPY_AND_ASSIGN(PathTest);
 };
 
-// Check that the disk space in the volume where the user data directory
-// normally lives is not dangerously low.
-class DiskSpaceTest : public DiagnosticsTest {
+// Check the version of Chrome.
+class VersionTest : public DiagnosticsTest {
  public:
-  DiskSpaceTest() : DiagnosticsTest(kDiskSpaceTest, "Disk Space") {}
+  VersionTest() : DiagnosticsTest(DIAGNOSTICS_VERSION_TEST) {}
 
   virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
-    base::FilePath data_dir;
-    if (!PathService::Get(chrome::DIR_USER_DATA, &data_dir))
-      return false;
-    int64 disk_space = base::SysInfo::AmountOfFreeDiskSpace(data_dir);
-    if (disk_space < 0) {
-      RecordFailure(DIAG_RECON_UNABLE_TO_QUERY, "Unable to query free space");
+    chrome::VersionInfo version_info;
+    if (!version_info.is_valid()) {
+      RecordFailure(DIAG_RECON_NO_VERSION, "No Version");
       return true;
     }
-    std::string printable_size = base::Int64ToString(disk_space);
-    if (disk_space < 80 * kOneMegabyte) {
-      RecordFailure(DIAG_RECON_LOW_DISK_SPACE,
-                    "Low disk space: " + printable_size);
+    std::string current_version = version_info.Version();
+    if (current_version.empty()) {
+      RecordFailure(DIAG_RECON_EMPTY_VERSION, "Empty Version");
       return true;
     }
-    RecordSuccess("Free space: " + printable_size);
+    std::string version_modifier =
+        chrome::VersionInfo::GetVersionStringModifier();
+    if (!version_modifier.empty())
+      current_version += " " + version_modifier;
+#if defined(GOOGLE_CHROME_BUILD)
+    current_version += " GCB";
+#endif  // defined(GOOGLE_CHROME_BUILD)
+    RecordSuccess(current_version);
     return true;
   }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(DiskSpaceTest);
-};
-
-// Checks that a given json file can be correctly parsed.
-class JSONTest : public DiagnosticsTest {
- public:
-  enum FileImportance {
-    NON_CRITICAL,
-    CRITICAL
-  };
-
-  JSONTest(const base::FilePath& path,
-           const std::string& id,
-           const std::string& name,
-           int64 max_file_size,
-           FileImportance importance)
-      : DiagnosticsTest(id, name),
-        path_(path),
-        max_file_size_(max_file_size),
-        importance_(importance) {}
-
-  virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
-    if (!base::PathExists(path_)) {
-      if (importance_ == CRITICAL) {
-        RecordOutcome(DIAG_RECON_FILE_NOT_FOUND,
-                      "File not found",
-                      DiagnosticsModel::TEST_FAIL_CONTINUE);
-      } else {
-        RecordOutcome(DIAG_RECON_FILE_NOT_FOUND_OK,
-                      "File not found (but that is OK)",
-                      DiagnosticsModel::TEST_OK);
-      }
-      return true;
-    }
-    int64 file_size;
-    if (!file_util::GetFileSize(path_, &file_size)) {
-      RecordFailure(DIAG_RECON_CANNOT_OBTAIN_FILE_SIZE,
-                    "Cannot obtain file size");
-      return true;
-    }
-
-    if (file_size > max_file_size_) {
-      RecordFailure(DIAG_RECON_FILE_TOO_BIG, "File too big");
-      return true;
-    }
-    // Being small enough, we can process it in-memory.
-    std::string json_data;
-    if (!file_util::ReadFileToString(path_, &json_data)) {
-      RecordFailure(DIAG_RECON_UNABLE_TO_OPEN_FILE,
-                    "Could not open file. Possibly locked by another process");
-      return true;
-    }
-
-    JSONStringValueSerializer json(json_data);
-    int error_code = base::JSONReader::JSON_NO_ERROR;
-    std::string error_message;
-    scoped_ptr<Value> json_root(json.Deserialize(&error_code, &error_message));
-    if (base::JSONReader::JSON_NO_ERROR != error_code) {
-      if (error_message.empty()) {
-        error_message = "Parse error " + base::IntToString(error_code);
-      }
-      RecordFailure(DIAG_RECON_PARSE_ERROR, error_message);
-      return true;
-    }
-
-    RecordSuccess("File parsed OK");
-    return true;
-  }
-
- private:
-  base::FilePath path_;
-  int64 max_file_size_;
-  FileImportance importance_;
-  DISALLOW_COPY_AND_ASSIGN(JSONTest);
+  DISALLOW_COPY_AND_ASSIGN(VersionTest);
 };
 
 }  // namespace
 
-DiagnosticsTest* MakeUserDirTest() { return new PathTest(kPathsToTest[0]); }
-
-DiagnosticsTest* MakeLocalStateFileTest() {
-  return new PathTest(kPathsToTest[1]);
-}
-
-DiagnosticsTest* MakeDictonaryDirTest() {
-  return new PathTest(kPathsToTest[2]);
-}
-
-DiagnosticsTest* MakeResourcesFileTest() {
-  return new PathTest(kPathsToTest[3]);
-}
-
-DiagnosticsTest* MakeVersionTest() { return new VersionTest(); }
+DiagnosticsTest* MakeConflictingDllsTest() { return new ConflictingDllsTest(); }
 
 DiagnosticsTest* MakeDiskSpaceTest() { return new DiskSpaceTest(); }
 
-DiagnosticsTest* MakeOperatingSystemTest() { return new OperatingSystemTest(); }
-
-DiagnosticsTest* MakeConflictingDllsTest() { return new ConflictingDllsTest(); }
-
 DiagnosticsTest* MakeInstallTypeTest() { return new InstallTypeTest(); }
 
-DiagnosticsTest* MakePreferencesTest() {
-  base::FilePath path = DiagnosticsTest::GetUserDefaultProfileDir();
-  path = path.Append(chrome::kPreferencesFilename);
-  return new JSONTest(path,
-                      kJSONProfileTest,
-                      "Profile JSON",
-                      100 * kOneKilobyte,
-                      JSONTest::CRITICAL);
-}
-
 DiagnosticsTest* MakeBookMarksTest() {
   base::FilePath path = DiagnosticsTest::GetUserDefaultProfileDir();
   path = path.Append(chrome::kBookmarksFileName);
   return new JSONTest(path,
-                      kJSONBookmarksTest,
-                      "Bookmarks JSON",
+                      DIAGNOSTICS_JSON_BOOKMARKS_TEST,
                       2 * kOneMegabyte,
                       JSONTest::NON_CRITICAL);
 }
@@ -446,10 +402,37 @@
   PathService::Get(chrome::DIR_USER_DATA, &path);
   path = path.Append(chrome::kLocalStateFilename);
   return new JSONTest(path,
-                      kJSONLocalStateTest,
-                      "Local State JSON",
+                      DIAGNOSTICS_JSON_LOCAL_STATE_TEST,
                       50 * kOneKilobyte,
                       JSONTest::CRITICAL);
 }
 
+DiagnosticsTest* MakePreferencesTest() {
+  base::FilePath path = DiagnosticsTest::GetUserDefaultProfileDir();
+  path = path.Append(chrome::kPreferencesFilename);
+  return new JSONTest(path,
+                      DIAGNOSTICS_JSON_PREFERENCES_TEST,
+                      100 * kOneKilobyte,
+                      JSONTest::CRITICAL);
+}
+
+
+DiagnosticsTest* MakeOperatingSystemTest() { return new OperatingSystemTest(); }
+
+DiagnosticsTest* MakeDictonaryDirTest() {
+  return new PathTest(kPathsToTest[0]);
+}
+
+DiagnosticsTest* MakeLocalStateFileTest() {
+  return new PathTest(kPathsToTest[1]);
+}
+
+DiagnosticsTest* MakeResourcesFileTest() {
+  return new PathTest(kPathsToTest[2]);
+}
+
+DiagnosticsTest* MakeUserDirTest() { return new PathTest(kPathsToTest[3]); }
+
+DiagnosticsTest* MakeVersionTest() { return new VersionTest(); }
+
 }  // namespace diagnostics
diff --git a/chrome/browser/diagnostics/recon_diagnostics.h b/chrome/browser/diagnostics/recon_diagnostics.h
index 3ecfe95..114abd9 100644
--- a/chrome/browser/diagnostics/recon_diagnostics.h
+++ b/chrome/browser/diagnostics/recon_diagnostics.h
@@ -51,32 +51,18 @@
   DIAG_RECON_PARSE_ERROR,
 };
 
-// Identifiers for the tests.
-extern const char kConflictingDllsTest[];
-extern const char kDiskSpaceTest[];
-extern const char kInstallTypeTest[];
-extern const char kJSONBookmarksTest[];
-extern const char kJSONLocalStateTest[];
-extern const char kJSONProfileTest[];
-extern const char kOperatingSystemTest[];
-extern const char kPathDictionariesTest[];
-extern const char kPathLocalStateTest[];
-extern const char kPathResourcesTest[];
-extern const char kPathUserDataTest[];
-extern const char kVersionTest[];
-
-DiagnosticsTest* MakeOperatingSystemTest();
-DiagnosticsTest* MakeConflictingDllsTest();
-DiagnosticsTest* MakeInstallTypeTest();
-DiagnosticsTest* MakeVersionTest();
-DiagnosticsTest* MakeUserDirTest();
-DiagnosticsTest* MakeLocalStateFileTest();
-DiagnosticsTest* MakeDictonaryDirTest();
-DiagnosticsTest* MakeResourcesFileTest();
-DiagnosticsTest* MakeDiskSpaceTest();
-DiagnosticsTest* MakePreferencesTest();
 DiagnosticsTest* MakeBookMarksTest();
+DiagnosticsTest* MakeConflictingDllsTest();
+DiagnosticsTest* MakeDictonaryDirTest();
+DiagnosticsTest* MakeDiskSpaceTest();
+DiagnosticsTest* MakeInstallTypeTest();
+DiagnosticsTest* MakeLocalStateFileTest();
 DiagnosticsTest* MakeLocalStateTest();
+DiagnosticsTest* MakeOperatingSystemTest();
+DiagnosticsTest* MakePreferencesTest();
+DiagnosticsTest* MakeResourcesFileTest();
+DiagnosticsTest* MakeUserDirTest();
+DiagnosticsTest* MakeVersionTest();
 
 }  // namespace diagnostics
 
diff --git a/chrome/browser/diagnostics/sqlite_diagnostics.cc b/chrome/browser/diagnostics/sqlite_diagnostics.cc
index 2cd1ca1..dcb3491 100644
--- a/chrome/browser/diagnostics/sqlite_diagnostics.cc
+++ b/chrome/browser/diagnostics/sqlite_diagnostics.cc
@@ -27,21 +27,6 @@
 
 namespace diagnostics {
 
-const char kSQLiteIntegrityAppCacheTest[] = "SQLiteIntegrityAppCache";
-const char kSQLiteIntegrityArchivedHistoryTest[] =
-    "SQLiteIntegrityArchivedHistory";
-const char kSQLiteIntegrityCookieTest[] = "SQLiteIntegrityCookie";
-const char kSQLiteIntegrityDatabaseTrackerTest[] =
-    "SQLiteIntegrityDatabaseTracker";
-const char kSQLiteIntegrityHistoryTest[] = "SQLiteIntegrityHistory";
-const char kSQLiteIntegrityThumbnailsTest[] = "SQLiteIntegrityThumbnails";
-const char kSQLiteIntegrityWebTest[] = "SQLiteIntegrityWeb";
-
-#if defined(OS_CHROMEOS)
-const char kSQLiteIntegrityNSSCertTest[] = "SQLiteIntegrityNSSCert";
-const char kSQLiteIntegrityNSSKeyTest[] = "SQLiteIntegrityNSSKey";
-#endif
-
 namespace {
 
 // Generic diagnostic test class for checking SQLite database integrity.
@@ -55,12 +40,9 @@
   };
 
   SqliteIntegrityTest(uint32 flags,
-                      const std::string& id,
-                      const std::string& title,
+                      DiagnosticsTestId id,
                       const base::FilePath& db_path)
-      : DiagnosticsTest(id, title),
-        flags_(flags),
-        db_path_(db_path) {}
+      : DiagnosticsTest(id), flags_(flags), db_path_(db_path) {}
 
   virtual bool RecoveryImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
     int outcome_code = GetOutcomeCode();
@@ -218,77 +200,70 @@
 
 }  // namespace
 
-DiagnosticsTest* MakeSqliteWebDbTest() {
-  return new SqliteIntegrityTest(SqliteIntegrityTest::CRITICAL,
-                                 kSQLiteIntegrityWebTest,
-                                 "Web Database",
-                                 base::FilePath(kWebDataFilename));
-}
-
-DiagnosticsTest* MakeSqliteCookiesDbTest() {
-  return new SqliteIntegrityTest(SqliteIntegrityTest::CRITICAL,
-                                 kSQLiteIntegrityCookieTest,
-                                 "Cookies Database",
-                                 base::FilePath(chrome::kCookieFilename));
-}
-
-DiagnosticsTest* MakeSqliteHistoryDbTest() {
-  return new SqliteIntegrityTest(SqliteIntegrityTest::CRITICAL,
-                                 kSQLiteIntegrityHistoryTest,
-                                 "History Database",
-                                 base::FilePath(chrome::kHistoryFilename));
-}
-
-DiagnosticsTest* MakeSqliteArchivedHistoryDbTest() {
-  return new SqliteIntegrityTest(
-      SqliteIntegrityTest::NO_FLAGS_SET,
-      kSQLiteIntegrityArchivedHistoryTest,
-      "Archived History Database",
-      base::FilePath(chrome::kArchivedHistoryFilename));
-}
-
-DiagnosticsTest* MakeSqliteThumbnailsDbTest() {
-  return new SqliteIntegrityTest(SqliteIntegrityTest::NO_FLAGS_SET,
-                                 kSQLiteIntegrityThumbnailsTest,
-                                 "Thumbnails Database",
-                                 base::FilePath(chrome::kThumbnailsFilename));
-}
-
 DiagnosticsTest* MakeSqliteAppCacheDbTest() {
   base::FilePath appcache_dir(content::kAppCacheDirname);
   base::FilePath appcache_db =
       appcache_dir.Append(appcache::kAppCacheDatabaseName);
   return new SqliteIntegrityTest(SqliteIntegrityTest::NO_FLAGS_SET,
-                                 kSQLiteIntegrityAppCacheTest,
-                                 "Application Cache Database",
+                                 DIAGNOSTICS_SQLITE_INTEGRITY_APP_CACHE_TEST,
                                  appcache_db);
 }
 
+DiagnosticsTest* MakeSqliteArchivedHistoryDbTest() {
+  return new SqliteIntegrityTest(
+      SqliteIntegrityTest::NO_FLAGS_SET,
+      DIAGNOSTICS_SQLITE_INTEGRITY_ARCHIVED_HISTORY_TEST,
+      base::FilePath(chrome::kArchivedHistoryFilename));
+}
+
+DiagnosticsTest* MakeSqliteCookiesDbTest() {
+  return new SqliteIntegrityTest(SqliteIntegrityTest::CRITICAL,
+                                 DIAGNOSTICS_SQLITE_INTEGRITY_COOKIE_TEST,
+                                 base::FilePath(chrome::kCookieFilename));
+}
+
 DiagnosticsTest* MakeSqliteWebDatabaseTrackerDbTest() {
   base::FilePath databases_dir(webkit_database::kDatabaseDirectoryName);
   base::FilePath tracker_db =
       databases_dir.Append(webkit_database::kTrackerDatabaseFileName);
-  return new SqliteIntegrityTest(SqliteIntegrityTest::NO_FLAGS_SET,
-                                 kSQLiteIntegrityDatabaseTrackerTest,
-                                 "Database Tracker Database",
-                                 tracker_db);
+  return new SqliteIntegrityTest(
+      SqliteIntegrityTest::NO_FLAGS_SET,
+      DIAGNOSTICS_SQLITE_INTEGRITY_DATABASE_TRACKER_TEST,
+      tracker_db);
+}
+
+DiagnosticsTest* MakeSqliteHistoryDbTest() {
+  return new SqliteIntegrityTest(SqliteIntegrityTest::CRITICAL,
+                                 DIAGNOSTICS_SQLITE_INTEGRITY_HISTORY_TEST,
+                                 base::FilePath(chrome::kHistoryFilename));
 }
 
 #if defined(OS_CHROMEOS)
 DiagnosticsTest* MakeSqliteNssCertDbTest() {
   base::FilePath home_dir = file_util::GetHomeDir();
   return new SqliteIntegrityTest(SqliteIntegrityTest::REMOVE_IF_CORRUPT,
-                                 kSQLiteIntegrityNSSCertTest,
-                                 "NSS Certificate Database",
+                                 DIAGNOSTICS_SQLITE_INTEGRITY_NSS_CERT_TEST,
                                  home_dir.Append(chromeos::kNssCertDbPath));
 }
 
 DiagnosticsTest* MakeSqliteNssKeyDbTest() {
   base::FilePath home_dir = file_util::GetHomeDir();
   return new SqliteIntegrityTest(SqliteIntegrityTest::REMOVE_IF_CORRUPT,
-                                 kSQLiteIntegrityNSSKeyTest,
-                                 "NSS Key Database",
+                                 DIAGNOSTICS_SQLITE_INTEGRITY_NSS_KEY_TEST,
                                  home_dir.Append(chromeos::kNssKeyDbPath));
 }
 #endif  // defined(OS_CHROMEOS)
-}       // namespace diagnostics
+
+DiagnosticsTest* MakeSqliteThumbnailsDbTest() {
+  return new SqliteIntegrityTest(SqliteIntegrityTest::NO_FLAGS_SET,
+                                 DIAGNOSTICS_SQLITE_INTEGRITY_THUMBNAILS_TEST,
+                                 base::FilePath(chrome::kThumbnailsFilename));
+}
+
+DiagnosticsTest* MakeSqliteWebDataDbTest() {
+  return new SqliteIntegrityTest(SqliteIntegrityTest::CRITICAL,
+                                 DIAGNOSTICS_SQLITE_INTEGRITY_WEB_DATA_TEST,
+                                 base::FilePath(kWebDataFilename));
+}
+
+}  // namespace diagnostics
diff --git a/chrome/browser/diagnostics/sqlite_diagnostics.h b/chrome/browser/diagnostics/sqlite_diagnostics.h
index 279dd5b..5058ef0 100644
--- a/chrome/browser/diagnostics/sqlite_diagnostics.h
+++ b/chrome/browser/diagnostics/sqlite_diagnostics.h
@@ -20,33 +20,21 @@
   DIAG_SQLITE_DB_CORRUPTED
 };
 
-extern const char kSQLiteIntegrityAppCacheTest[];
-extern const char kSQLiteIntegrityArchivedHistoryTest[];
-extern const char kSQLiteIntegrityCookieTest[];
-extern const char kSQLiteIntegrityDatabaseTrackerTest[];
-extern const char kSQLiteIntegrityHistoryTest[];
-extern const char kSQLiteIntegrityThumbnailsTest[];
-extern const char kSQLiteIntegrityWebTest[];
-
-#if defined(OS_CHROMEOS)
-extern const char kSQLiteIntegrityNSSCertTest[];
-extern const char kSQLiteIntegrityNSSKeyTest[];
-#endif
-
 // Factories for the database integrity tests we run in diagnostic mode.
-DiagnosticsTest* MakeSqliteWebDbTest();
+DiagnosticsTest* MakeSqliteAppCacheDbTest();
+DiagnosticsTest* MakeSqliteArchivedHistoryDbTest();
 DiagnosticsTest* MakeSqliteCookiesDbTest();
 DiagnosticsTest* MakeSqliteHistoryDbTest();
-DiagnosticsTest* MakeSqliteArchivedHistoryDbTest();
 DiagnosticsTest* MakeSqliteThumbnailsDbTest();
-DiagnosticsTest* MakeSqliteAppCacheDbTest();
-DiagnosticsTest* MakeSqliteWebDatabaseTrackerDbTest();
 
 #if defined(OS_CHROMEOS)
 DiagnosticsTest* MakeSqliteNssCertDbTest();
 DiagnosticsTest* MakeSqliteNssKeyDbTest();
 #endif  // defined(OS_CHROMEOS)
 
+DiagnosticsTest* MakeSqliteWebDatabaseTrackerDbTest();
+DiagnosticsTest* MakeSqliteWebDataDbTest();
+
 }  // namespace diagnostics
 
 #endif  // CHROME_BROWSER_DIAGNOSTICS_SQLITE_DIAGNOSTICS_H_
diff --git a/chrome/browser/download/chrome_download_manager_delegate.cc b/chrome/browser/download/chrome_download_manager_delegate.cc
index aec10bb..8e2d138 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/download/download_service.h"
 #include "chrome/browser/download/download_service_factory.h"
 #include "chrome/browser/download/download_target_determiner.h"
-#include "chrome/browser/download/download_util.h"
 #include "chrome/browser/download/save_package_file_picker.h"
 #include "chrome/browser/extensions/api/downloads/downloads_api.h"
 #include "chrome/browser/extensions/crx_installer.h"
diff --git a/chrome/browser/download/download_danger_prompt.h b/chrome/browser/download/download_danger_prompt.h
index 8c2407f..e1b5c61 100644
--- a/chrome/browser/download/download_danger_prompt.h
+++ b/chrome/browser/download/download_danger_prompt.h
@@ -43,9 +43,6 @@
       bool show_context,
       const OnDone& done);
 
- protected:
-  friend class DownloadDangerPromptTest;
-
   // Only to be used by tests. Subclasses must override to manually call the
   // respective button click handler.
   virtual void InvokeActionForTesting(Action action) = 0;
diff --git a/chrome/browser/download/download_path_reservation_tracker.cc b/chrome/browser/download/download_path_reservation_tracker.cc
index 37954a3..28c3990 100644
--- a/chrome/browser/download/download_path_reservation_tracker.cc
+++ b/chrome/browser/download/download_path_reservation_tracker.cc
@@ -15,7 +15,6 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/third_party/icu/icu_utf.h"
-#include "chrome/browser/download/download_util.h"
 #include "chrome/common/chrome_paths.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_item.h"
diff --git a/chrome/browser/download/download_path_reservation_tracker_unittest.cc b/chrome/browser/download/download_path_reservation_tracker_unittest.cc
index 06183a5..5703da3 100644
--- a/chrome/browser/download/download_path_reservation_tracker_unittest.cc
+++ b/chrome/browser/download/download_path_reservation_tracker_unittest.cc
@@ -35,7 +35,7 @@
   }
   virtual ~FakeDownloadItem() {
     FOR_EACH_OBSERVER(Observer, observers_, OnDownloadDestroyed(this));
-    EXPECT_EQ(0u, observers_.size());
+    EXPECT_FALSE(observers_.might_have_observers());
   }
   virtual void AddObserver(Observer* observer) OVERRIDE {
     observers_.AddObserver(observer);
diff --git a/chrome/browser/download/download_prefs.cc b/chrome/browser/download/download_prefs.cc
index 4b7eef2..c1ac67c 100644
--- a/chrome/browser/download/download_prefs.cc
+++ b/chrome/browser/download/download_prefs.cc
@@ -22,7 +22,6 @@
 #include "chrome/browser/download/download_extensions.h"
 #include "chrome/browser/download/download_service.h"
 #include "chrome/browser/download/download_service_factory.h"
-#include "chrome/browser/download/download_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/chrome_paths.h"
diff --git a/chrome/browser/download/download_status_updater.cc b/chrome/browser/download/download_status_updater.cc
index b084378..5df688b 100644
--- a/chrome/browser/download/download_status_updater.cc
+++ b/chrome/browser/download/download_status_updater.cc
@@ -8,7 +8,6 @@
 
 #include "base/logging.h"
 #include "base/stl_util.h"
-#include "chrome/browser/download/download_util.h"
 
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
 #include "ui/linux_ui/linux_ui.h"
diff --git a/chrome/browser/download/download_target_determiner.cc b/chrome/browser/download/download_target_determiner.cc
index 02429b4..7291336 100644
--- a/chrome/browser/download/download_target_determiner.cc
+++ b/chrome/browser/download/download_target_determiner.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/download/download_crx_util.h"
 #include "chrome/browser/download/download_extensions.h"
 #include "chrome/browser/download/download_prefs.h"
-#include "chrome/browser/download/download_util.h"
 #include "chrome/browser/extensions/webstore_installer.h"
 #include "chrome/browser/history/history_service.h"
 #include "chrome/browser/history/history_service_factory.h"
diff --git a/chrome/browser/download/download_target_determiner_unittest.cc b/chrome/browser/download/download_target_determiner_unittest.cc
index 46a1803..aca0720 100644
--- a/chrome/browser/download/download_target_determiner_unittest.cc
+++ b/chrome/browser/download/download_target_determiner_unittest.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/download/download_extensions.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/download/download_target_determiner.h"
-#include "chrome/browser/download/download_util.h"
 #include "chrome/browser/history/history_service.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history/history_types.h"
diff --git a/chrome/browser/download/download_util.cc b/chrome/browser/download/download_util.cc
deleted file mode 100644
index dd4cf1f..0000000
--- a/chrome/browser/download/download_util.cc
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/download/download_util.h"
-
-#include <string>
-
-#include "content/public/browser/download_item.h"
-#include "net/base/mime_util.h"
-#include "net/base/net_util.h"
-#include "ui/gfx/image/image.h"
-#include "ui/gfx/point.h"
-#include "url/gurl.h"
-
-#if defined(TOOLKIT_VIEWS)
-#include "ui/base/dragdrop/drag_drop_types.h"
-#include "ui/base/dragdrop/drag_utils.h"
-#include "ui/base/dragdrop/os_exchange_data.h"
-#include "ui/gfx/screen.h"
-#include "ui/views/widget/widget.h"
-#endif
-
-#if defined(TOOLKIT_GTK)
-#include "chrome/browser/ui/gtk/custom_drag.h"
-#endif  // defined(TOOLKIT_GTK)
-
-#if defined(OS_WIN) && !defined(USE_AURA)
-#include "ui/base/dragdrop/drag_source_win.h"
-#include "ui/base/dragdrop/os_exchange_data_provider_win.h"
-#endif
-
-#if defined(USE_AURA)
-#include "ui/aura/client/drag_drop_client.h"
-#include "ui/aura/root_window.h"
-#include "ui/aura/window.h"
-#endif
-
-namespace download_util {
-
-using content::DownloadItem;
-
-#if defined(TOOLKIT_VIEWS)
-// Download dragging
-void DragDownload(const DownloadItem* download,
-                  gfx::Image* icon,
-                  gfx::NativeView view) {
-  DCHECK(download);
-  DCHECK_EQ(DownloadItem::COMPLETE, download->GetState());
-
-  // Set up our OLE machinery
-  ui::OSExchangeData data;
-
-  if (icon) {
-    drag_utils::CreateDragImageForFile(
-        download->GetFileNameToReportUser(), icon->ToImageSkia(), &data);
-  }
-
-  const base::FilePath full_path = download->GetTargetFilePath();
-  data.SetFilename(full_path);
-
-  std::string mime_type = download->GetMimeType();
-  if (mime_type.empty())
-    net::GetMimeTypeFromFile(full_path, &mime_type);
-
-  // Add URL so that we can load supported files when dragged to WebContents.
-  if (net::IsSupportedMimeType(mime_type)) {
-    data.SetURL(net::FilePathToFileURL(full_path),
-                download->GetFileNameToReportUser().LossyDisplayName());
-  }
-
-#if !defined(TOOLKIT_GTK)
-#if defined(USE_AURA)
-  aura::RootWindow* root_window = view->GetRootWindow();
-  if (!root_window || !aura::client::GetDragDropClient(root_window))
-    return;
-
-  gfx::Point location = gfx::Screen::GetScreenFor(view)->GetCursorScreenPoint();
-  // TODO(varunjain): Properly determine and send DRAG_EVENT_SOURCE below.
-  aura::client::GetDragDropClient(root_window)->StartDragAndDrop(
-      data,
-      root_window,
-      view,
-      location,
-      ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK,
-      ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
-#else  // We are on WIN without AURA
-  // We cannot use Widget::RunShellDrag on WIN since the |view| is backed by a
-  // WebContentsViewWin, not a NativeWidgetWin.
-  scoped_refptr<ui::DragSourceWin> drag_source(new ui::DragSourceWin);
-  // Run the drag and drop loop
-  DWORD effects;
-  DoDragDrop(ui::OSExchangeDataProviderWin::GetIDataObject(data),
-             drag_source.get(), DROPEFFECT_COPY | DROPEFFECT_LINK, &effects);
-#endif
-
-#else
-  GtkWidget* root = gtk_widget_get_toplevel(view);
-  if (!root)
-    return;
-
-  views::NativeWidgetGtk* widget = static_cast<views::NativeWidgetGtk*>(
-      views::Widget::GetWidgetForNativeView(root)->native_widget());
-  if (!widget)
-    return;
-
-  widget->DoDrag(data,
-                 ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK);
-#endif  // TOOLKIT_GTK
-}
-#elif defined(USE_X11)
-void DragDownload(const DownloadItem* download,
-                  gfx::Image* icon,
-                  gfx::NativeView view) {
-  DownloadItemDrag::BeginDrag(download, icon);
-}
-#endif  // USE_X11
-
-}  // namespace download_util
diff --git a/chrome/browser/download/download_util.h b/chrome/browser/download/download_util.h
deleted file mode 100644
index 900e960..0000000
--- a/chrome/browser/download/download_util.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_DOWNLOAD_DOWNLOAD_UTIL_H_
-#define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_UTIL_H_
-
-#include "ui/gfx/native_widget_types.h"
-
-namespace content {
-class DownloadItem;
-}
-
-namespace gfx {
-class Image;
-}
-
-namespace download_util {
-
-// Helper function for download views to use when acting as a drag source for a
-// DownloadItem. If |icon| is NULL, no image will be accompany the drag. |view|
-// is only required for Mac OS X, elsewhere it can be NULL.
-void DragDownload(const content::DownloadItem* download,
-                  gfx::Image* icon,
-                  gfx::NativeView view);
-
-}  // namespace download_util
-
-#endif  // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_UTIL_H_
diff --git a/chrome/browser/download/drag_download_item.h b/chrome/browser/download/drag_download_item.h
new file mode 100644
index 0000000..34f0285
--- /dev/null
+++ b/chrome/browser/download/drag_download_item.h
@@ -0,0 +1,25 @@
+// 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 CHROME_BROWSER_DOWNLOAD_DRAG_DOWNLOAD_ITEM_H_
+#define CHROME_BROWSER_DOWNLOAD_DRAG_DOWNLOAD_ITEM_H_
+
+#include "ui/gfx/native_widget_types.h"
+
+namespace content {
+class DownloadItem;
+}
+
+namespace gfx {
+class Image;
+}
+
+// Helper function for download views to use when acting as a drag source for a
+// DownloadItem. If |icon| is NULL, no image will be accompany the drag. |view|
+// is only required for Mac OS X, elsewhere it can be NULL.
+void DragDownloadItem(const content::DownloadItem* download,
+                      gfx::Image* icon,
+                      gfx::NativeView view);
+
+#endif  // CHROME_BROWSER_DOWNLOAD_DRAG_DOWNLOAD_ITEM_H_
diff --git a/chrome/browser/download/drag_download_item_gtk.cc b/chrome/browser/download/drag_download_item_gtk.cc
new file mode 100644
index 0000000..10bb338
--- /dev/null
+++ b/chrome/browser/download/drag_download_item_gtk.cc
@@ -0,0 +1,13 @@
+// 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 "chrome/browser/download/drag_download_item.h"
+
+#include "chrome/browser/ui/gtk/custom_drag.h"
+
+void DragDownloadItem(const content::DownloadItem* download,
+                      gfx::Image* icon,
+                      gfx::NativeView view) {
+  DownloadItemDrag::BeginDrag(download, icon);
+}
diff --git a/chrome/browser/download/drag_download_item_views.cc b/chrome/browser/download/drag_download_item_views.cc
new file mode 100644
index 0000000..872e1d5
--- /dev/null
+++ b/chrome/browser/download/drag_download_item_views.cc
@@ -0,0 +1,99 @@
+// 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 "chrome/browser/download/drag_download_item.h"
+
+#include <string>
+
+#include "content/public/browser/download_item.h"
+#include "net/base/mime_util.h"
+#include "net/base/net_util.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/dragdrop/drag_utils.h"
+#include "ui/base/dragdrop/os_exchange_data.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/point.h"
+#include "ui/gfx/screen.h"
+#include "ui/views/widget/widget.h"
+#include "url/gurl.h"
+
+#if defined(OS_WIN) && !defined(USE_AURA)
+#include "ui/base/dragdrop/drag_source_win.h"
+#include "ui/base/dragdrop/os_exchange_data_provider_win.h"
+#endif
+
+#if defined(USE_AURA)
+#include "ui/aura/client/drag_drop_client.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/window.h"
+#endif
+
+void DragDownloadItem(const content::DownloadItem* download,
+                      gfx::Image* icon,
+                      gfx::NativeView view) {
+  DCHECK(download);
+  DCHECK_EQ(content::DownloadItem::COMPLETE, download->GetState());
+
+  // Set up our OLE machinery
+  ui::OSExchangeData data;
+
+  if (icon) {
+    drag_utils::CreateDragImageForFile(
+        download->GetFileNameToReportUser(), icon->ToImageSkia(), &data);
+  }
+
+  const base::FilePath full_path = download->GetTargetFilePath();
+  data.SetFilename(full_path);
+
+  std::string mime_type = download->GetMimeType();
+  if (mime_type.empty())
+    net::GetMimeTypeFromFile(full_path, &mime_type);
+
+  // Add URL so that we can load supported files when dragged to WebContents.
+  if (net::IsSupportedMimeType(mime_type)) {
+    data.SetURL(net::FilePathToFileURL(full_path),
+                download->GetFileNameToReportUser().LossyDisplayName());
+  }
+
+#if !defined(TOOLKIT_GTK)
+#if defined(USE_AURA)
+  aura::RootWindow* root_window = view->GetRootWindow();
+  if (!root_window || !aura::client::GetDragDropClient(root_window))
+    return;
+
+  gfx::Point location = gfx::Screen::GetScreenFor(view)->GetCursorScreenPoint();
+  // TODO(varunjain): Properly determine and send DRAG_EVENT_SOURCE below.
+  aura::client::GetDragDropClient(root_window)->StartDragAndDrop(
+      data,
+      root_window,
+      view,
+      location,
+      ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK,
+      ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
+#else  // We are on WIN without AURA
+  // We cannot use Widget::RunShellDrag on WIN since the |view| is backed by a
+  // WebContentsViewWin, not a NativeWidgetWin.
+  scoped_refptr<ui::DragSourceWin> drag_source(new ui::DragSourceWin);
+  // Run the drag and drop loop
+  DWORD effects;
+  DoDragDrop(ui::OSExchangeDataProviderWin::GetIDataObject(data),
+             drag_source.get(),
+             DROPEFFECT_COPY | DROPEFFECT_LINK,
+             &effects);
+#endif
+
+#else
+  GtkWidget* root = gtk_widget_get_toplevel(view);
+  if (!root)
+    return;
+
+  views::NativeWidgetGtk* widget = static_cast<views::NativeWidgetGtk*>(
+      views::Widget::GetWidgetForNativeView(root)->native_widget());
+  if (!widget)
+    return;
+
+  widget->DoDrag(data,
+                 ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK);
+#endif  // TOOLKIT_GTK
+}
diff --git a/chrome/browser/drive/drive_api_service.cc b/chrome/browser/drive/drive_api_service.cc
index 48ee6b5..9cb3d73 100644
--- a/chrome/browser/drive/drive_api_service.cc
+++ b/chrome/browser/drive/drive_api_service.cc
@@ -23,6 +23,7 @@
 
 using content::BrowserThread;
 using google_apis::AppList;
+using google_apis::AppListCallback;
 using google_apis::AuthStatusCallback;
 using google_apis::AuthorizeAppCallback;
 using google_apis::CancelCallback;
@@ -34,14 +35,8 @@
 using google_apis::GDATA_OTHER_ERROR;
 using google_apis::GDATA_PARSE_ERROR;
 using google_apis::GDataErrorCode;
-using google_apis::GetAboutRequest;
-using google_apis::GetAboutResourceCallback;
-using google_apis::GetAppListCallback;
-using google_apis::GetApplistRequest;
-using google_apis::GetChangelistRequest;
+using google_apis::AboutResourceCallback;
 using google_apis::GetContentCallback;
-using google_apis::GetFileRequest;
-using google_apis::GetFilelistRequest;
 using google_apis::GetResourceEntryCallback;
 using google_apis::GetResourceEntryRequest;
 using google_apis::GetResourceListCallback;
@@ -56,19 +51,23 @@
 using google_apis::ResourceList;
 using google_apis::UploadRangeCallback;
 using google_apis::UploadRangeResponse;
+using google_apis::drive::AboutGetRequest;
+using google_apis::drive::AppsListRequest;
+using google_apis::drive::ChangesListRequest;
 using google_apis::drive::ContinueGetFileListRequest;
-using google_apis::drive::CopyResourceRequest;
-using google_apis::drive::CreateDirectoryRequest;
 using google_apis::drive::DeleteResourceRequest;
 using google_apis::drive::DownloadFileRequest;
+using google_apis::drive::FilesCopyRequest;
+using google_apis::drive::FilesGetRequest;
+using google_apis::drive::FilesInsertRequest;
+using google_apis::drive::FilesPatchRequest;
+using google_apis::drive::FilesListRequest;
+using google_apis::drive::FilesTrashRequest;
 using google_apis::drive::GetUploadStatusRequest;
 using google_apis::drive::InitiateUploadExistingFileRequest;
 using google_apis::drive::InitiateUploadNewFileRequest;
 using google_apis::drive::InsertResourceRequest;
-using google_apis::drive::MoveResourceRequest;
 using google_apis::drive::ResumeUploadRequest;
-using google_apis::drive::TouchResourceRequest;
-using google_apis::drive::TrashResourceRequest;
 
 namespace drive {
 
@@ -79,6 +78,9 @@
 const char kDriveAppsReadonlyScope[] =
     "https://www.googleapis.com/auth/drive.apps.readonly";
 
+// Mime type to create a directory.
+const char kFolderMimeType[] = "application/vnd.google-apps.folder";
+
 // Expected max number of files resources in a http request.
 // Be careful not to use something too small because it might overload the
 // server. Be careful not to use something too large because it takes longer
@@ -149,16 +151,16 @@
     return;
   }
 
-  PostTaskAndReplyWithResult(
+  base::PostTaskAndReplyWithResult(
       blocking_task_runner.get(),
       FROM_HERE,
       base::Bind(&ParseResourceListOnBlockingPool, base::Passed(&value)),
       base::Bind(&DidParseResourceListOnBlockingPool, callback));
 }
 
-// Parses the FileResource value to ResourceEntry and runs |callback| on the
+// Converts the FileResource value to ResourceEntry and runs |callback| on the
 // UI thread.
-void ParseResourceEntryAndRun(
+void ConvertFileEntryToResourceEntryAndRun(
     const GetResourceEntryCallback& callback,
     GDataErrorCode error,
     scoped_ptr<FileResource> value) {
@@ -181,29 +183,62 @@
   callback.Run(error, entry.Pass());
 }
 
-// Parses the JSON value to AppList runs |callback| on the UI thread
-// once parsing is done.
-void ParseAppListAndRun(const google_apis::GetAppListCallback& callback,
-                        google_apis::GDataErrorCode error,
-                        scoped_ptr<base::Value> value) {
+// Thin adapter of CreateFromFileList.
+scoped_ptr<ResourceList> ConvertFileListToResourceList(
+    scoped_ptr<FileList> file_list) {
+  return ResourceList::CreateFromFileList(*file_list);
+}
+
+// Converts the FileList value to ResourceList on blocking pool and runs
+// |callback| on the UI thread.
+void ConvertFileListToResourceListOnBlockingPoolAndRun(
+    scoped_refptr<base::TaskRunner> blocking_task_runner,
+    const GetResourceListCallback& callback,
+    GDataErrorCode error,
+    scoped_ptr<FileList> value) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
   if (!value) {
-    callback.Run(error, scoped_ptr<google_apis::AppList>());
+    callback.Run(error, scoped_ptr<ResourceList>());
     return;
   }
 
-  // Parsing AppList is cheap enough to do on UI thread.
-  scoped_ptr<google_apis::AppList> app_list =
-      google_apis::AppList::CreateFrom(*value);
-  if (!app_list) {
-    callback.Run(google_apis::GDATA_PARSE_ERROR,
-                 scoped_ptr<google_apis::AppList>());
+  // Convert the value on blocking pool.
+  base::PostTaskAndReplyWithResult(
+      blocking_task_runner.get(),
+      FROM_HERE,
+      base::Bind(&ConvertFileListToResourceList, base::Passed(&value)),
+      base::Bind(&DidParseResourceListOnBlockingPool, callback));
+}
+
+// Thin adapter of CreateFromChangeList.
+scoped_ptr<ResourceList> ConvertChangeListToResourceList(
+    scoped_ptr<ChangeList> change_list) {
+  return ResourceList::CreateFromChangeList(*change_list);
+}
+
+// Converts the FileList value to ResourceList on blocking pool and runs
+// |callback| on the UI thread.
+void ConvertChangeListToResourceListOnBlockingPoolAndRun(
+    scoped_refptr<base::TaskRunner> blocking_task_runner,
+    const GetResourceListCallback& callback,
+    GDataErrorCode error,
+    scoped_ptr<ChangeList> value) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!callback.is_null());
+
+  if (!value) {
+    callback.Run(error, scoped_ptr<ResourceList>());
     return;
   }
 
-  callback.Run(error, app_list.Pass());
+  // Convert the value on blocking pool.
+  base::PostTaskAndReplyWithResult(
+      blocking_task_runner.get(),
+      FROM_HERE,
+      base::Bind(&ConvertChangeListToResourceList, base::Passed(&value)),
+      base::Bind(&DidParseResourceListOnBlockingPool, callback));
 }
 
 // Parses the FileResource value to ResourceEntry for upload range request,
@@ -347,16 +382,12 @@
   // but it seems impossible to know the returned list's changestamp.
   // Thus, instead, we use changes.list method with includeDeleted=false here.
   // The returned list should contain only resources currently existing.
-  return sender_->StartRequestWithRetry(
-      new GetChangelistRequest(
-          sender_.get(),
-          url_generator_,
-          false,  // include deleted
-          0,
-          kMaxNumFilesResourcePerRequest,
-          base::Bind(&ParseResourceListOnBlockingPoolAndRun,
-                     blocking_task_runner_,
-                     callback)));
+  ChangesListRequest* request = new ChangesListRequest(
+      sender_.get(), url_generator_,
+      base::Bind(&ConvertChangeListToResourceListOnBlockingPoolAndRun,
+                 blocking_task_runner_, callback));
+  request->set_include_deleted(false);
+  return sender_->StartRequestWithRetry(request);
 }
 
 CancelCallback DriveAPIService::GetResourceListInDirectory(
@@ -373,18 +404,16 @@
   // code up by moving the responsibility to include "parents" in the query
   // to client side.
   // We aren't interested in files in trash in this context, neither.
-  return sender_->StartRequestWithRetry(
-      new GetFilelistRequest(
-          sender_.get(),
-          url_generator_,
-          base::StringPrintf(
-              "'%s' in parents and trashed = false",
-              drive::util::EscapeQueryStringValue(
-                  directory_resource_id).c_str()),
-          kMaxNumFilesResourcePerRequest,
-          base::Bind(&ParseResourceListOnBlockingPoolAndRun,
-                     blocking_task_runner_,
-                     callback)));
+  FilesListRequest* request = new FilesListRequest(
+      sender_.get(), url_generator_,
+      base::Bind(&ConvertFileListToResourceListOnBlockingPoolAndRun,
+                 blocking_task_runner_, callback));
+  request->set_max_results(kMaxNumFilesResourcePerRequest);
+  request->set_q(base::StringPrintf(
+      "'%s' in parents and trashed = false",
+      drive::util::EscapeQueryStringValue(directory_resource_id).c_str()));
+
+  return sender_->StartRequestWithRetry(request);
 }
 
 CancelCallback DriveAPIService::Search(
@@ -394,15 +423,14 @@
   DCHECK(!search_query.empty());
   DCHECK(!callback.is_null());
 
-  return sender_->StartRequestWithRetry(
-      new GetFilelistRequest(
-          sender_.get(),
-          url_generator_,
-          drive::util::TranslateQuery(search_query),
-          kMaxNumFilesResourcePerRequestForSearch,
-          base::Bind(&ParseResourceListOnBlockingPoolAndRun,
-                     blocking_task_runner_,
-                     callback)));
+  FilesListRequest* request = new FilesListRequest(
+      sender_.get(), url_generator_,
+      base::Bind(&ConvertFileListToResourceListOnBlockingPoolAndRun,
+                 blocking_task_runner_, callback));
+  request->set_max_results(kMaxNumFilesResourcePerRequestForSearch);
+  request->set_q(drive::util::TranslateQuery(search_query));
+
+  return sender_->StartRequestWithRetry(request);
 }
 
 CancelCallback DriveAPIService::SearchByTitle(
@@ -423,15 +451,14 @@
   }
   query += " and trashed = false";
 
-  return sender_->StartRequestWithRetry(
-      new GetFilelistRequest(
-          sender_.get(),
-          url_generator_,
-          query,
-          kMaxNumFilesResourcePerRequest,
-          base::Bind(&ParseResourceListOnBlockingPoolAndRun,
-                     blocking_task_runner_,
-                     callback)));
+  FilesListRequest* request = new FilesListRequest(
+      sender_.get(), url_generator_,
+      base::Bind(&ConvertFileListToResourceListOnBlockingPoolAndRun,
+                 blocking_task_runner_, callback));
+  request->set_max_results(kMaxNumFilesResourcePerRequest);
+  request->set_q(query);
+
+  return sender_->StartRequestWithRetry(request);
 }
 
 CancelCallback DriveAPIService::GetChangeList(
@@ -440,16 +467,13 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  return sender_->StartRequestWithRetry(
-      new GetChangelistRequest(
-          sender_.get(),
-          url_generator_,
-          true,  // include deleted
-          start_changestamp,
-          kMaxNumFilesResourcePerRequest,
-          base::Bind(&ParseResourceListOnBlockingPoolAndRun,
-                     blocking_task_runner_,
-                     callback)));
+  ChangesListRequest* request = new ChangesListRequest(
+      sender_.get(), url_generator_,
+      base::Bind(&ConvertChangeListToResourceListOnBlockingPoolAndRun,
+                 blocking_task_runner_, callback));
+  request->set_max_results(kMaxNumFilesResourcePerRequest);
+  request->set_start_change_id(start_changestamp);
+  return sender_->StartRequestWithRetry(request);
 }
 
 CancelCallback DriveAPIService::ContinueGetResourceList(
@@ -467,17 +491,41 @@
                      callback)));
 }
 
+CancelCallback DriveAPIService::GetRemainingChangeList(
+    const std::string& page_token,
+    const GetResourceListCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!page_token.empty());
+  DCHECK(!callback.is_null());
+
+  // Currently page_token is a URL.
+  // TODO(hidehiko): Use actual page token.
+  return ContinueGetResourceList(GURL(page_token), callback);
+}
+
+CancelCallback DriveAPIService::GetRemainingFileList(
+    const std::string& page_token,
+    const GetResourceListCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!page_token.empty());
+  DCHECK(!callback.is_null());
+
+  // Currently page_token is a URL.
+  // TODO(hidehiko): Use actual page token.
+  return ContinueGetResourceList(GURL(page_token), callback);
+}
+
 CancelCallback DriveAPIService::GetResourceEntry(
     const std::string& resource_id,
     const GetResourceEntryCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  return sender_->StartRequestWithRetry(new GetFileRequest(
-      sender_.get(),
-      url_generator_,
-      resource_id,
-      base::Bind(&ParseResourceEntryAndRun, callback)));
+  FilesGetRequest* request = new FilesGetRequest(
+      sender_.get(), url_generator_,
+      base::Bind(&ConvertFileEntryToResourceEntryAndRun, callback));
+  request->set_file_id(resource_id);
+  return sender_->StartRequestWithRetry(request);
 }
 
 CancelCallback DriveAPIService::GetShareUrl(
@@ -501,25 +549,20 @@
 }
 
 CancelCallback DriveAPIService::GetAboutResource(
-    const GetAboutResourceCallback& callback) {
+    const AboutResourceCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
   return sender_->StartRequestWithRetry(
-      new GetAboutRequest(
-          sender_.get(),
-          url_generator_,
-          callback));
+      new AboutGetRequest(sender_.get(), url_generator_, callback));
 }
 
-CancelCallback DriveAPIService::GetAppList(const GetAppListCallback& callback) {
+CancelCallback DriveAPIService::GetAppList(const AppListCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  return sender_->StartRequestWithRetry(new GetApplistRequest(
-      sender_.get(),
-      url_generator_,
-      base::Bind(&ParseAppListAndRun, callback)));
+  return sender_->StartRequestWithRetry(
+      new AppsListRequest(sender_.get(), url_generator_, callback));
 }
 
 CancelCallback DriveAPIService::DownloadFile(
@@ -549,11 +592,11 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  return sender_->StartRequestWithRetry(new TrashResourceRequest(
-      sender_.get(),
-      url_generator_,
-      resource_id,
-      callback));
+  FilesTrashRequest* request = new FilesTrashRequest(
+      sender_.get(), url_generator_,
+      base::Bind(&EntryActionCallbackAdapter, callback));
+  request->set_file_id(resource_id);
+  return sender_->StartRequestWithRetry(request);
 }
 
 CancelCallback DriveAPIService::AddNewDirectory(
@@ -563,13 +606,13 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  return sender_->StartRequestWithRetry(
-      new CreateDirectoryRequest(
-          sender_.get(),
-          url_generator_,
-          parent_resource_id,
-          directory_title,
-          base::Bind(&ParseResourceEntryAndRun, callback)));
+  FilesInsertRequest* request = new FilesInsertRequest(
+      sender_.get(), url_generator_,
+      base::Bind(&ConvertFileEntryToResourceEntryAndRun, callback));
+  request->set_mime_type(kFolderMimeType);
+  request->add_parent(parent_resource_id);
+  request->set_title(directory_title);
+  return sender_->StartRequestWithRetry(request);
 }
 
 CancelCallback DriveAPIService::CopyResource(
@@ -580,14 +623,13 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  return sender_->StartRequestWithRetry(
-      new CopyResourceRequest(
-          sender_.get(),
-          url_generator_,
-          resource_id,
-          parent_resource_id,
-          new_title,
-          base::Bind(&ParseResourceEntryAndRun, callback)));
+  FilesCopyRequest* request = new FilesCopyRequest(
+      sender_.get(), url_generator_,
+      base::Bind(&ConvertFileEntryToResourceEntryAndRun, callback));
+  request->set_file_id(resource_id);
+  request->add_parent(parent_resource_id);
+  request->set_title(new_title);
+  return sender_->StartRequestWithRetry(request);
 }
 
 CancelCallback DriveAPIService::CopyHostedDocument(
@@ -597,14 +639,12 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  return sender_->StartRequestWithRetry(
-      new CopyResourceRequest(
-          sender_.get(),
-          url_generator_,
-          resource_id,
-          std::string(),  // parent_resource_id.
-          new_title,
-          base::Bind(&ParseResourceEntryAndRun, callback)));
+  FilesCopyRequest* request = new FilesCopyRequest(
+      sender_.get(), url_generator_,
+      base::Bind(&ConvertFileEntryToResourceEntryAndRun, callback));
+  request->set_file_id(resource_id);
+  request->set_title(new_title);
+  return sender_->StartRequestWithRetry(request);
 }
 
 CancelCallback DriveAPIService::MoveResource(
@@ -615,14 +655,14 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  return sender_->StartRequestWithRetry(
-      new MoveResourceRequest(
-          sender_.get(),
-          url_generator_,
-          resource_id,
-          parent_resource_id,
-          new_title,
-          base::Bind(&ParseResourceEntryAndRun, callback)));
+  FilesPatchRequest* request = new FilesPatchRequest(
+      sender_.get(), url_generator_,
+      base::Bind(&ConvertFileEntryToResourceEntryAndRun, callback));
+  request->set_file_id(resource_id);
+  request->set_title(new_title);
+  if (!parent_resource_id.empty())
+    request->add_parent(parent_resource_id);
+  return sender_->StartRequestWithRetry(request);
 }
 
 CancelCallback DriveAPIService::RenameResource(
@@ -632,14 +672,12 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  return sender_->StartRequestWithRetry(
-      new MoveResourceRequest(
-          sender_.get(),
-          url_generator_,
-          resource_id,
-          std::string(),
-          new_title,
-          base::Bind(&EntryActionCallbackAdapter, callback)));
+  FilesPatchRequest* request = new FilesPatchRequest(
+      sender_.get(), url_generator_,
+      base::Bind(&EntryActionCallbackAdapter, callback));
+  request->set_file_id(resource_id);
+  request->set_title(new_title);
+  return sender_->StartRequestWithRetry(request);
 }
 
 CancelCallback DriveAPIService::TouchResource(
@@ -652,14 +690,19 @@
   DCHECK(!last_viewed_by_me_date.is_null());
   DCHECK(!callback.is_null());
 
-  return sender_->StartRequestWithRetry(
-      new TouchResourceRequest(
-          sender_.get(),
-          url_generator_,
-          resource_id,
-          modified_date,
-          last_viewed_by_me_date,
-          base::Bind(&ParseResourceEntryAndRun, callback)));
+  FilesPatchRequest* request = new FilesPatchRequest(
+      sender_.get(), url_generator_,
+      base::Bind(&ConvertFileEntryToResourceEntryAndRun, callback));
+  // Need to set setModifiedDate to true to overwrite modifiedDate.
+  request->set_set_modified_date(true);
+
+  // Need to set updateViewedDate to false, otherwise the lastViewedByMeDate
+  // will be set to the request time (not the specified time via request).
+  request->set_update_viewed_date(false);
+
+  request->set_modified_date(modified_date);
+  request->set_last_viewed_by_me_date(last_viewed_by_me_date);
+  return sender_->StartRequestWithRetry(request);
 }
 
 CancelCallback DriveAPIService::AddResourceToDirectory(
@@ -780,11 +823,11 @@
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
-  return sender_->StartRequestWithRetry(new GetFileRequest(
-      sender_.get(),
-      url_generator_,
-      resource_id,
-      base::Bind(&ExtractOpenUrlAndRun, app_id, callback)));
+  FilesGetRequest* request = new FilesGetRequest(
+      sender_.get(), url_generator_,
+      base::Bind(&ExtractOpenUrlAndRun, app_id, callback));
+  request->set_file_id(resource_id);
+  return sender_->StartRequestWithRetry(request);
 }
 
 bool DriveAPIService::HasAccessToken() const {
diff --git a/chrome/browser/drive/drive_api_service.h b/chrome/browser/drive/drive_api_service.h
index b896112..299aed4 100644
--- a/chrome/browser/drive/drive_api_service.h
+++ b/chrome/browser/drive/drive_api_service.h
@@ -93,6 +93,12 @@
   virtual google_apis::CancelCallback ContinueGetResourceList(
       const GURL& override_url,
       const google_apis::GetResourceListCallback& callback) OVERRIDE;
+  virtual google_apis::CancelCallback GetRemainingChangeList(
+      const std::string& page_token,
+      const google_apis::GetResourceListCallback& callback) OVERRIDE;
+  virtual google_apis::CancelCallback GetRemainingFileList(
+      const std::string& page_token,
+      const google_apis::GetResourceListCallback& callback) OVERRIDE;
   virtual google_apis::CancelCallback GetResourceEntry(
       const std::string& resource_id,
       const google_apis::GetResourceEntryCallback& callback) OVERRIDE;
@@ -101,9 +107,9 @@
       const GURL& embed_origin,
       const google_apis::GetShareUrlCallback& callback) OVERRIDE;
   virtual google_apis::CancelCallback GetAboutResource(
-      const google_apis::GetAboutResourceCallback& callback) OVERRIDE;
+      const google_apis::AboutResourceCallback& callback) OVERRIDE;
   virtual google_apis::CancelCallback GetAppList(
-      const google_apis::GetAppListCallback& callback) OVERRIDE;
+      const google_apis::AppListCallback& callback) OVERRIDE;
   virtual google_apis::CancelCallback DeleteResource(
       const std::string& resource_id,
       const std::string& etag,
diff --git a/chrome/browser/drive/drive_service_interface.h b/chrome/browser/drive/drive_service_interface.h
index 72ee3a8..d9fa83e 100644
--- a/chrome/browser/drive/drive_service_interface.h
+++ b/chrome/browser/drive/drive_service_interface.h
@@ -135,6 +135,9 @@
       int64 start_changestamp,
       const google_apis::GetResourceListCallback& callback) = 0;
 
+  // THIS METHOD IS DEPRECATED. NEW CLIENTS SHOULD USE GetRemainingChangeList()
+  // or GetRemainingFileList().
+  //
   // Requests returning GetResourceList may be paged. In such a case,
   // a URL to fetch remaining result is returned. The URL can be used for this
   // method. |callback| will be called upon completion.
@@ -145,6 +148,26 @@
       const GURL& override_url,
       const google_apis::GetResourceListCallback& callback) = 0;
 
+  // The result of GetChangeList() and GetAllResourceList() may be paged.
+  // In such a case, a page token to fetch remaining result is returned.
+  // The page token can be used for this method. |callback| will be called upon
+  // completion.
+  //
+  // |page_token| must not be empty. |callback| must not be null.
+  virtual google_apis::CancelCallback GetRemainingChangeList(
+      const std::string& page_token,
+      const google_apis::GetResourceListCallback& callback) = 0;
+
+  // The result of GetResourceListInDirectory(), Search() and SearchByTitle()
+  // may be paged. In such a case, a page token to fetch remaining result is
+  // returned. The page token can be used for this method. |callback| will be
+  // called upon completion.
+  //
+  // |page_token| must not be empty. |callback| must not be null.
+  virtual google_apis::CancelCallback GetRemainingFileList(
+      const std::string& page_token,
+      const google_apis::GetResourceListCallback& callback) = 0;
+
   // Fetches single entry metadata from server. The entry's resource id equals
   // |resource_id|.
   // Upon completion, invokes |callback| with results on the calling thread.
@@ -166,13 +189,13 @@
   // Upon completion, invokes |callback| with results on the calling thread.
   // |callback| must not be null.
   virtual google_apis::CancelCallback GetAboutResource(
-      const google_apis::GetAboutResourceCallback& callback) = 0;
+      const google_apis::AboutResourceCallback& callback) = 0;
 
   // Gets the application information from the server.
   // Upon completion, invokes |callback| with results on the calling thread.
   // |callback| must not be null.
   virtual google_apis::CancelCallback GetAppList(
-      const google_apis::GetAppListCallback& callback) = 0;
+      const google_apis::AppListCallback& callback) = 0;
 
   // Deletes a resource identified by its |resource_id|.
   // If |etag| is not empty and did not match, the deletion fails with
diff --git a/chrome/browser/drive/dummy_drive_service.cc b/chrome/browser/drive/dummy_drive_service.cc
index 99b7314..9db0daa 100644
--- a/chrome/browser/drive/dummy_drive_service.cc
+++ b/chrome/browser/drive/dummy_drive_service.cc
@@ -4,13 +4,13 @@
 
 #include "chrome/browser/drive/dummy_drive_service.h"
 
+using google_apis::AboutResourceCallback;
+using google_apis::AppListCallback;
 using google_apis::AuthStatusCallback;
 using google_apis::AuthorizeAppCallback;
 using google_apis::CancelCallback;
 using google_apis::DownloadActionCallback;
 using google_apis::EntryActionCallback;
-using google_apis::GetAboutResourceCallback;
-using google_apis::GetAppListCallback;
 using google_apis::GetContentCallback;
 using google_apis::GetResourceEntryCallback;
 using google_apis::GetResourceListCallback;
@@ -78,6 +78,14 @@
     const GURL& override_url,
     const GetResourceListCallback& callback) { return CancelCallback(); }
 
+CancelCallback DummyDriveService::GetRemainingChangeList(
+    const std::string& page_token,
+    const GetResourceListCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::GetRemainingFileList(
+    const std::string& page_token,
+    const GetResourceListCallback& callback) { return CancelCallback(); }
+
 CancelCallback DummyDriveService::GetResourceEntry(
     const std::string& resource_id,
     const GetResourceEntryCallback& callback) { return CancelCallback(); }
@@ -88,10 +96,10 @@
     const GetShareUrlCallback& callback) { return CancelCallback(); }
 
 CancelCallback DummyDriveService::GetAboutResource(
-    const GetAboutResourceCallback& callback) { return CancelCallback(); }
+    const AboutResourceCallback& callback) { return CancelCallback(); }
 
 CancelCallback DummyDriveService::GetAppList(
-    const GetAppListCallback& callback) { return CancelCallback(); }
+    const AppListCallback& callback) { return CancelCallback(); }
 
 CancelCallback DummyDriveService::DeleteResource(
     const std::string& resource_id,
diff --git a/chrome/browser/drive/dummy_drive_service.h b/chrome/browser/drive/dummy_drive_service.h
index c63becd..24daeed 100644
--- a/chrome/browser/drive/dummy_drive_service.h
+++ b/chrome/browser/drive/dummy_drive_service.h
@@ -49,6 +49,12 @@
   virtual google_apis::CancelCallback ContinueGetResourceList(
       const GURL& override_url,
       const google_apis::GetResourceListCallback& callback) OVERRIDE;
+  virtual google_apis::CancelCallback GetRemainingChangeList(
+      const std::string& page_token,
+      const google_apis::GetResourceListCallback& callback) OVERRIDE;
+  virtual google_apis::CancelCallback GetRemainingFileList(
+      const std::string& page_token,
+      const google_apis::GetResourceListCallback& callback) OVERRIDE;
   virtual google_apis::CancelCallback GetResourceEntry(
       const std::string& resource_id,
       const google_apis::GetResourceEntryCallback& callback) OVERRIDE;
@@ -57,9 +63,9 @@
       const GURL& embed_origin,
       const google_apis::GetShareUrlCallback& callback) OVERRIDE;
   virtual google_apis::CancelCallback GetAboutResource(
-      const google_apis::GetAboutResourceCallback& callback) OVERRIDE;
+      const google_apis::AboutResourceCallback& callback) OVERRIDE;
   virtual google_apis::CancelCallback GetAppList(
-      const google_apis::GetAppListCallback& callback) OVERRIDE;
+      const google_apis::AppListCallback& callback) OVERRIDE;
   virtual google_apis::CancelCallback DeleteResource(
       const std::string& resource_id,
       const std::string& etag,
diff --git a/chrome/browser/drive/fake_drive_service.cc b/chrome/browser/drive/fake_drive_service.cc
index 7075338..26cc58c 100644
--- a/chrome/browser/drive/fake_drive_service.cc
+++ b/chrome/browser/drive/fake_drive_service.cc
@@ -26,19 +26,19 @@
 
 using content::BrowserThread;
 using google_apis::AboutResource;
+using google_apis::AboutResourceCallback;
 using google_apis::AccountMetadata;
 using google_apis::AppList;
+using google_apis::AppListCallback;
 using google_apis::AuthStatusCallback;
 using google_apis::AuthorizeAppCallback;
 using google_apis::CancelCallback;
 using google_apis::DownloadActionCallback;
 using google_apis::EntryActionCallback;
-using google_apis::GDataErrorCode;
 using google_apis::GDATA_FILE_ERROR;
 using google_apis::GDATA_NO_CONNECTION;
 using google_apis::GDATA_OTHER_ERROR;
-using google_apis::GetAboutResourceCallback;
-using google_apis::GetAppListCallback;
+using google_apis::GDataErrorCode;
 using google_apis::GetContentCallback;
 using google_apis::GetResourceEntryCallback;
 using google_apis::GetResourceListCallback;
@@ -456,6 +456,26 @@
   return CancelCallback();
 }
 
+CancelCallback FakeDriveService::GetRemainingChangeList(
+    const std::string& page_token,
+    const GetResourceListCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!page_token.empty());
+  DCHECK(!callback.is_null());
+
+  return ContinueGetResourceList(GURL(page_token), callback);
+}
+
+CancelCallback FakeDriveService::GetRemainingFileList(
+    const std::string& page_token,
+    const GetResourceListCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!page_token.empty());
+  DCHECK(!callback.is_null());
+
+  return ContinueGetResourceList(GURL(page_token), callback);
+}
+
 CancelCallback FakeDriveService::GetResourceEntry(
     const std::string& resource_id,
     const GetResourceEntryCallback& callback) {
@@ -533,7 +553,7 @@
 }
 
 CancelCallback FakeDriveService::GetAboutResource(
-    const GetAboutResourceCallback& callback) {
+    const AboutResourceCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
@@ -563,8 +583,7 @@
   return CancelCallback();
 }
 
-CancelCallback FakeDriveService::GetAppList(
-    const GetAppListCallback& callback) {
+CancelCallback FakeDriveService::GetAppList(const AppListCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
   DCHECK(app_info_value_);
diff --git a/chrome/browser/drive/fake_drive_service.h b/chrome/browser/drive/fake_drive_service.h
index ad7fdc3..12a7448 100644
--- a/chrome/browser/drive/fake_drive_service.h
+++ b/chrome/browser/drive/fake_drive_service.h
@@ -124,6 +124,12 @@
   virtual google_apis::CancelCallback ContinueGetResourceList(
       const GURL& override_url,
       const google_apis::GetResourceListCallback& callback) OVERRIDE;
+  virtual google_apis::CancelCallback GetRemainingChangeList(
+      const std::string& page_token,
+      const google_apis::GetResourceListCallback& callback) OVERRIDE;
+  virtual google_apis::CancelCallback GetRemainingFileList(
+      const std::string& page_token,
+      const google_apis::GetResourceListCallback& callback) OVERRIDE;
   virtual google_apis::CancelCallback GetResourceEntry(
       const std::string& resource_id,
       const google_apis::GetResourceEntryCallback& callback) OVERRIDE;
@@ -132,9 +138,9 @@
       const GURL& embed_origin,
       const google_apis::GetShareUrlCallback& callback) OVERRIDE;
   virtual google_apis::CancelCallback GetAboutResource(
-      const google_apis::GetAboutResourceCallback& callback) OVERRIDE;
+      const google_apis::AboutResourceCallback& callback) OVERRIDE;
   virtual google_apis::CancelCallback GetAppList(
-      const google_apis::GetAppListCallback& callback) OVERRIDE;
+      const google_apis::AppListCallback& callback) OVERRIDE;
   virtual google_apis::CancelCallback DeleteResource(
       const std::string& resource_id,
       const std::string& etag,
diff --git a/chrome/browser/drive/fake_drive_service_unittest.cc b/chrome/browser/drive/fake_drive_service_unittest.cc
index efbf716..3c69648 100644
--- a/chrome/browser/drive/fake_drive_service_unittest.cc
+++ b/chrome/browser/drive/fake_drive_service_unittest.cc
@@ -463,7 +463,7 @@
   EXPECT_EQ(1, fake_service_.change_list_load_count());
 }
 
-TEST_F(FakeDriveServiceTest, ContinueGetResourceList_GetAllResourceList) {
+TEST_F(FakeDriveServiceTest, GetRemainingChangeList_GetAllResourceList) {
   ASSERT_TRUE(fake_service_.LoadResourceListForWapi(
       "gdata/root_feed.json"));
   fake_service_.set_default_max_results(6);
@@ -491,8 +491,8 @@
 
   error = GDATA_OTHER_ERROR;
   resource_list.reset();
-  fake_service_.ContinueGetResourceList(
-      next_url,
+  fake_service_.GetRemainingChangeList(
+      next_url.spec(),
       test_util::CreateCopyResultCallback(&error, &resource_list));
   base::RunLoop().RunUntilIdle();
 
@@ -509,8 +509,8 @@
 
   error = GDATA_OTHER_ERROR;
   resource_list.reset();
-  fake_service_.ContinueGetResourceList(
-      next_url,
+  fake_service_.GetRemainingChangeList(
+      next_url.spec(),
       test_util::CreateCopyResultCallback(&error, &resource_list));
   base::RunLoop().RunUntilIdle();
 
@@ -522,7 +522,7 @@
 }
 
 TEST_F(FakeDriveServiceTest,
-       ContinueGetResourceList_GetResourceListInDirectory) {
+       GetRemainingFileList_GetResourceListInDirectory) {
   ASSERT_TRUE(fake_service_.LoadResourceListForWapi(
       "gdata/root_feed.json"));
   fake_service_.set_default_max_results(3);
@@ -551,8 +551,8 @@
 
   error = GDATA_OTHER_ERROR;
   resource_list.reset();
-  fake_service_.ContinueGetResourceList(
-      next_url,
+  fake_service_.GetRemainingFileList(
+      next_url.spec(),
       test_util::CreateCopyResultCallback(&error, &resource_list));
   base::RunLoop().RunUntilIdle();
 
@@ -569,8 +569,8 @@
 
   error = GDATA_OTHER_ERROR;
   resource_list.reset();
-  fake_service_.ContinueGetResourceList(
-      next_url,
+  fake_service_.GetRemainingFileList(
+      next_url.spec(),
       test_util::CreateCopyResultCallback(&error, &resource_list));
   base::RunLoop().RunUntilIdle();
 
@@ -581,7 +581,7 @@
   EXPECT_EQ(1, fake_service_.directory_load_count());
 }
 
-TEST_F(FakeDriveServiceTest, ContinueGetResourceList_Search) {
+TEST_F(FakeDriveServiceTest, GetRemainingFileList_Search) {
   ASSERT_TRUE(fake_service_.LoadResourceListForWapi(
       "gdata/root_feed.json"));
   fake_service_.set_default_max_results(2);
@@ -609,8 +609,8 @@
 
   error = GDATA_OTHER_ERROR;
   resource_list.reset();
-  fake_service_.ContinueGetResourceList(
-      next_url,
+  fake_service_.GetRemainingFileList(
+      next_url.spec(),
       test_util::CreateCopyResultCallback(&error, &resource_list));
   base::RunLoop().RunUntilIdle();
 
@@ -620,7 +620,7 @@
   EXPECT_EQ(2U, resource_list->entries().size());
 }
 
-TEST_F(FakeDriveServiceTest, ContinueGetResourceList_GetChangeList) {
+TEST_F(FakeDriveServiceTest, GetRemainingChangeList_GetChangeList) {
   ASSERT_TRUE(fake_service_.LoadResourceListForWapi(
       "gdata/root_feed.json"));
   fake_service_.set_default_max_results(2);
@@ -661,8 +661,8 @@
 
   error = GDATA_OTHER_ERROR;
   resource_list.reset();
-  fake_service_.ContinueGetResourceList(
-      next_url,
+  fake_service_.GetRemainingChangeList(
+      next_url.spec(),
       test_util::CreateCopyResultCallback(&error, &resource_list));
   base::RunLoop().RunUntilIdle();
 
@@ -679,8 +679,8 @@
 
   error = GDATA_OTHER_ERROR;
   resource_list.reset();
-  fake_service_.ContinueGetResourceList(
-      next_url,
+  fake_service_.GetRemainingChangeList(
+      next_url.spec(),
       test_util::CreateCopyResultCallback(&error, &resource_list));
   base::RunLoop().RunUntilIdle();
 
diff --git a/chrome/browser/drive/gdata_wapi_service.cc b/chrome/browser/drive/gdata_wapi_service.cc
index 8a262a0..a6b375a 100644
--- a/chrome/browser/drive/gdata_wapi_service.cc
+++ b/chrome/browser/drive/gdata_wapi_service.cc
@@ -22,13 +22,15 @@
 
 using content::BrowserThread;
 using google_apis::AboutResource;
+using google_apis::AboutResourceCallback;
 using google_apis::AccountMetadata;
 using google_apis::AddResourceToDirectoryRequest;
 using google_apis::AppList;
+using google_apis::AppListCallback;
+using google_apis::AuthService;
 using google_apis::AuthStatusCallback;
 using google_apis::AuthorizeAppCallback;
 using google_apis::AuthorizeAppRequest;
-using google_apis::AuthService;
 using google_apis::CancelCallback;
 using google_apis::CopyHostedDocumentRequest;
 using google_apis::CreateDirectoryRequest;
@@ -36,11 +38,9 @@
 using google_apis::DownloadActionCallback;
 using google_apis::DownloadFileRequest;
 using google_apis::EntryActionCallback;
-using google_apis::GDataErrorCode;
 using google_apis::GDATA_PARSE_ERROR;
-using google_apis::GetAboutResourceCallback;
+using google_apis::GDataErrorCode;
 using google_apis::GetAccountMetadataRequest;
-using google_apis::GetAppListCallback;
 using google_apis::GetContentCallback;
 using google_apis::GetResourceEntryCallback;
 using google_apis::GetResourceEntryRequest;
@@ -97,7 +97,7 @@
 }
 
 void ParseAboutResourceAndRun(
-    const GetAboutResourceCallback& callback,
+    const AboutResourceCallback& callback,
     GDataErrorCode error,
     scoped_ptr<AccountMetadata> account_metadata) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -113,7 +113,7 @@
 }
 
 void ParseAppListAndRun(
-    const GetAppListCallback& callback,
+    const AppListCallback& callback,
     GDataErrorCode error,
     scoped_ptr<AccountMetadata> account_metadata) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -294,6 +294,28 @@
                                  callback));
 }
 
+CancelCallback GDataWapiService::GetRemainingChangeList(
+    const std::string& page_token,
+    const GetResourceListCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!page_token.empty());
+  DCHECK(!callback.is_null());
+
+  // The page token for the GData WAPI is an URL.
+  return ContinueGetResourceList(GURL(page_token), callback);
+}
+
+CancelCallback GDataWapiService::GetRemainingFileList(
+    const std::string& page_token,
+    const GetResourceListCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!page_token.empty());
+  DCHECK(!callback.is_null());
+
+  // The page token for the GData WAPI is an URL.
+  return ContinueGetResourceList(GURL(page_token), callback);
+}
+
 CancelCallback GDataWapiService::GetResourceEntry(
     const std::string& resource_id,
     const GetResourceEntryCallback& callback) {
@@ -326,7 +348,7 @@
 }
 
 CancelCallback GDataWapiService::GetAboutResource(
-    const GetAboutResourceCallback& callback) {
+    const AboutResourceCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
@@ -338,8 +360,7 @@
           false));  // Exclude installed apps.
 }
 
-CancelCallback GDataWapiService::GetAppList(
-    const GetAppListCallback& callback) {
+CancelCallback GDataWapiService::GetAppList(const AppListCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(!callback.is_null());
 
diff --git a/chrome/browser/drive/gdata_wapi_service.h b/chrome/browser/drive/gdata_wapi_service.h
index a7139b8..b605b5d 100644
--- a/chrome/browser/drive/gdata_wapi_service.h
+++ b/chrome/browser/drive/gdata_wapi_service.h
@@ -89,6 +89,12 @@
   virtual google_apis::CancelCallback ContinueGetResourceList(
       const GURL& override_url,
       const google_apis::GetResourceListCallback& callback) OVERRIDE;
+  virtual google_apis::CancelCallback GetRemainingChangeList(
+      const std::string& page_token,
+      const google_apis::GetResourceListCallback& callback) OVERRIDE;
+  virtual google_apis::CancelCallback GetRemainingFileList(
+      const std::string& page_token,
+      const google_apis::GetResourceListCallback& callback) OVERRIDE;
   virtual google_apis::CancelCallback GetResourceEntry(
       const std::string& resource_id,
       const google_apis::GetResourceEntryCallback& callback) OVERRIDE;
@@ -97,9 +103,9 @@
       const GURL& embed_origin,
       const google_apis::GetShareUrlCallback& callback) OVERRIDE;
   virtual google_apis::CancelCallback GetAboutResource(
-      const google_apis::GetAboutResourceCallback& callback) OVERRIDE;
+      const google_apis::AboutResourceCallback& callback) OVERRIDE;
   virtual google_apis::CancelCallback GetAppList(
-      const google_apis::GetAppListCallback& callback) OVERRIDE;
+      const google_apis::AppListCallback& callback) OVERRIDE;
   virtual google_apis::CancelCallback DeleteResource(
       const std::string& resource_id,
       const std::string& etag,
diff --git a/chrome/browser/extensions/activity_log/activity_actions.cc b/chrome/browser/extensions/activity_log/activity_actions.cc
index a871fc9..67dcfca 100644
--- a/chrome/browser/extensions/activity_log/activity_actions.cc
+++ b/chrome/browser/extensions/activity_log/activity_actions.cc
@@ -153,8 +153,10 @@
       result->activity_type = ExtensionActivity::ACTIVITY_TYPE_WEB_REQUEST;
       break;
     case UNUSED_ACTION_API_BLOCKED:
-      // Here for legacy reasons. It shouldn't be reached, but some people
-      // might have old db entries. Treat it like an API call if that happens.
+    case ACTION_ANY:
+    default:
+      // This shouldn't be reached, but some people might have old or otherwise
+      // weird db entries. Treat it like an API call if that happens.
       result->activity_type = ExtensionActivity::ACTIVITY_TYPE_API_CALL;
       break;
   }
diff --git a/chrome/browser/extensions/activity_log/activity_actions.h b/chrome/browser/extensions/activity_log/activity_actions.h
index d9e59a8..c5cd7ab 100644
--- a/chrome/browser/extensions/activity_log/activity_actions.h
+++ b/chrome/browser/extensions/activity_log/activity_actions.h
@@ -34,6 +34,7 @@
     ACTION_DOM_ACCESS = 4,
     ACTION_DOM_EVENT = 5,
     ACTION_WEB_REQUEST = 6,
+    ACTION_ANY = 1001,              // Used for lookups of unspecified type.
   };
 
   // A useful shorthand for methods that take or return collections of Action
diff --git a/chrome/browser/extensions/activity_log/activity_database.cc b/chrome/browser/extensions/activity_log/activity_database.cc
index 7679d15..66844af 100644
--- a/chrome/browser/extensions/activity_log/activity_database.cc
+++ b/chrome/browser/extensions/activity_log/activity_database.cc
@@ -28,14 +28,27 @@
 
 namespace extensions {
 
+// A size threshold at which data should be flushed to the database.  The
+// ActivityDatabase will signal the Delegate to write out data based on a
+// periodic timer, but will also initiate a flush if AdviseFlush indicates that
+// more than kSizeThresholdForFlush action records are queued in memory.  This
+// should be set large enough that write costs can be amortized across many
+// records, but not so large that too much space can be tied up holding records
+// in memory.
+static const int kSizeThresholdForFlush = 200;
+
 ActivityDatabase::ActivityDatabase(ActivityDatabase::Delegate* delegate)
     : delegate_(delegate),
       valid_db_(false),
+      batch_mode_(true),
       already_closed_(false),
       did_init_(false) {
-  // We don't batch commits when in testing mode.
-  batch_mode_ = !(CommandLine::ForCurrentProcess()->
-      HasSwitch(switches::kEnableExtensionActivityLogTesting));
+  if (CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableExtensionActivityLogTesting)) {
+    batching_period_ = base::TimeDelta::FromSeconds(10);
+  } else {
+    batching_period_ = base::TimeDelta::FromMinutes(2);
+  }
 }
 
 ActivityDatabase::~ActivityDatabase() {}
@@ -81,7 +94,7 @@
 
   valid_db_ = true;
   timer_.Start(FROM_HERE,
-               base::TimeDelta::FromMinutes(2),
+               batching_period_,
                this,
                &ActivityDatabase::RecordBatchedActions);
 }
@@ -94,7 +107,8 @@
 void ActivityDatabase::AdviseFlush(int size) {
   if (!valid_db_)
     return;
-  if (!batch_mode_ || size == kFlushImmediately) {
+  if (!batch_mode_ || size == kFlushImmediately ||
+      size >= kSizeThresholdForFlush) {
     if (!delegate_->FlushDatabase(&db_))
       SoftFailureClose();
   }
@@ -110,7 +124,7 @@
 void ActivityDatabase::SetBatchModeForTesting(bool batch_mode) {
   if (batch_mode && !batch_mode_) {
     timer_.Start(FROM_HERE,
-                 base::TimeDelta::FromMinutes(2),
+                 batching_period_,
                  this,
                  &ActivityDatabase::RecordBatchedActions);
   } else if (!batch_mode && batch_mode_) {
diff --git a/chrome/browser/extensions/activity_log/activity_database.h b/chrome/browser/extensions/activity_log/activity_database.h
index 30b32f7..f7216ee 100644
--- a/chrome/browser/extensions/activity_log/activity_database.h
+++ b/chrome/browser/extensions/activity_log/activity_database.h
@@ -189,6 +189,7 @@
   sql::Connection db_;
   bool valid_db_;
   bool batch_mode_;
+  base::TimeDelta batching_period_;
   base::RepeatingTimer<ActivityDatabase> timer_;
   bool already_closed_;
   bool did_init_;
diff --git a/chrome/browser/extensions/activity_log/activity_log.cc b/chrome/browser/extensions/activity_log/activity_log.cc
index d80eedc..4e3350f 100644
--- a/chrome/browser/extensions/activity_log/activity_log.cc
+++ b/chrome/browser/extensions/activity_log/activity_log.cc
@@ -176,33 +176,7 @@
 
 // ActivityLog
 
-void ActivityLog::SetDefaultPolicy(ActivityLogPolicy::PolicyType policy_type) {
-  // Can't use IsLogEnabled() here because this is called from inside Init.
-  if (policy_type != policy_type_ && enabled_) {
-    // Deleting the old policy takes place asynchronously, on the database
-    // thread.  Initializing a new policy below similarly happens
-    // asynchronously.  Since the two operations are both queued for the
-    // database, the queue ordering should ensure that the deletion completes
-    // before database initialization occurs.
-    //
-    // However, changing policies at runtime is still not recommended, and
-    // likely only should be done for unit tests.
-    if (policy_)
-      policy_->Close();
-
-    switch (policy_type) {
-      case ActivityLogPolicy::POLICY_FULLSTREAM:
-        policy_ = new FullStreamUIPolicy(profile_);
-        break;
-      case ActivityLogPolicy::POLICY_COUNTS:
-        policy_ = new CountingPolicy(profile_);
-        break;
-      default:
-        NOTREACHED();
-    }
-    policy_type_ = policy_type;
-  }
-}
+// SET THINGS UP. --------------------------------------------------------------
 
 // Use GetInstance instead of directly creating an ActivityLog.
 ActivityLog::ActivityLog(Profile* profile)
@@ -248,6 +222,47 @@
   ChooseDefaultPolicy();
 }
 
+void ActivityLog::SetDefaultPolicy(ActivityLogPolicy::PolicyType policy_type) {
+  // Can't use IsLogEnabled() here because this is called from inside Init.
+  if (policy_type != policy_type_ && enabled_) {
+    // Deleting the old policy takes place asynchronously, on the database
+    // thread.  Initializing a new policy below similarly happens
+    // asynchronously.  Since the two operations are both queued for the
+    // database, the queue ordering should ensure that the deletion completes
+    // before database initialization occurs.
+    //
+    // However, changing policies at runtime is still not recommended, and
+    // likely only should be done for unit tests.
+    if (policy_)
+      policy_->Close();
+
+    switch (policy_type) {
+      case ActivityLogPolicy::POLICY_FULLSTREAM:
+        policy_ = new FullStreamUIPolicy(profile_);
+        break;
+      case ActivityLogPolicy::POLICY_COUNTS:
+        policy_ = new CountingPolicy(profile_);
+        break;
+      default:
+        NOTREACHED();
+    }
+    policy_type_ = policy_type;
+  }
+}
+
+// SHUT DOWN. ------------------------------------------------------------------
+
+void ActivityLog::Shutdown() {
+  if (tracker_) tracker_->RemoveObserver(this);
+}
+
+ActivityLog::~ActivityLog() {
+  if (policy_)
+    policy_->Close();
+}
+
+// MAINTAIN STATUS. ------------------------------------------------------------
+
 void ActivityLog::InitInstallTracker() {
   tracker_ = InstallTrackerFactory::GetForProfile(profile_);
   tracker_->AddObserver(this);
@@ -261,15 +276,6 @@
     SetDefaultPolicy(ActivityLogPolicy::POLICY_COUNTS);
 }
 
-void ActivityLog::Shutdown() {
-  if (tracker_) tracker_->RemoveObserver(this);
-}
-
-ActivityLog::~ActivityLog() {
-  if (policy_)
-    policy_->Close();
-}
-
 // static
 bool ActivityLog::enabled_on_any_profile_ = false;
 
@@ -324,6 +330,17 @@
   observers_->RemoveObserver(observer);
 }
 
+// static
+void ActivityLog::RegisterProfilePrefs(
+    user_prefs::PrefRegistrySyncable* registry) {
+  registry->RegisterBooleanPref(
+      prefs::kWatchdogExtensionActive,
+      false,
+      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+}
+
+// LOG ACTIONS. ----------------------------------------------------------------
+
 void ActivityLog::LogAction(scoped_refptr<Action> action) {
   if (!IsLogEnabled() ||
       ActivityLogAPI::IsExtensionWhitelisted(action->extension_id()))
@@ -347,16 +364,6 @@
     LOG(INFO) << action->PrintForDebug();
 }
 
-void ActivityLog::GetActions(
-    const std::string& extension_id,
-    const int day,
-    const base::Callback
-        <void(scoped_ptr<std::vector<scoped_refptr<Action> > >)>& callback) {
-  if (policy_) {
-    policy_->ReadData(extension_id, day, callback);
-  }
-}
-
 void ActivityLog::OnScriptsExecuted(
     const content::WebContents* web_contents,
     const ExecutingScriptsMap& extension_ids,
@@ -404,13 +411,60 @@
   }
 }
 
-// static
-void ActivityLog::RegisterProfilePrefs(
-    user_prefs::PrefRegistrySyncable* registry) {
-  registry->RegisterBooleanPref(
-      prefs::kWatchdogExtensionActive,
-      false,
-      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+// LOOKUP ACTIONS. -------------------------------------------------------------
+
+void ActivityLog::GetActions(
+    const std::string& extension_id,
+    const int day,
+    const base::Callback
+        <void(scoped_ptr<std::vector<scoped_refptr<Action> > >)>& callback) {
+  if (policy_) {
+    policy_->ReadData(extension_id, day, callback);
+  }
+}
+
+void ActivityLog::GetFilteredActions(
+    const std::string& extension_id,
+    const Action::ActionType type,
+    const std::string& api_name,
+    const std::string& page_url,
+    const std::string& arg_url,
+    const base::Callback
+        <void(scoped_ptr<std::vector<scoped_refptr<Action> > >)>& callback) {
+  if (policy_) {
+    policy_->ReadFilteredData(
+        extension_id, type, api_name, page_url, arg_url, callback);
+  }
+}
+
+// DELETE ACTIONS. -------------------------------------------------------------
+
+void ActivityLog::RemoveURLs(const std::vector<GURL>& restrict_urls) {
+  if (!policy_ || !IsLogEnabled()) {
+    return;
+  }
+  policy_->RemoveURLs(restrict_urls);
+}
+
+void ActivityLog::RemoveURLs(const std::set<GURL>& restrict_urls) {
+  if (!policy_ || !IsLogEnabled()) {
+    return;
+  }
+
+  std::vector<GURL> urls;
+  for (std::set<GURL>::const_iterator it = restrict_urls.begin();
+       it != restrict_urls.end(); ++it) {
+    urls.push_back(*it);
+  }
+  policy_->RemoveURLs(urls);
+}
+
+void ActivityLog::RemoveURL(const GURL& url) {
+  if (url.is_empty())
+    return;
+  std::vector<GURL> urls;
+  urls.push_back(url);
+  RemoveURLs(urls);
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/activity_log/activity_log.h b/chrome/browser/extensions/activity_log/activity_log.h
index 93f61e3..84b6305 100644
--- a/chrome/browser/extensions/activity_log/activity_log.h
+++ b/chrome/browser/extensions/activity_log/activity_log.h
@@ -84,6 +84,18 @@
                       <void(scoped_ptr<std::vector<scoped_refptr<Action> > >)>&
                       callback);
 
+  // Gets all actions that match the specified fields. URLs are treated like
+  // prefixes; other fields are exact matches. Empty strings are not matched to
+  // anything.
+  void GetFilteredActions(
+      const std::string& extension_id,
+      const Action::ActionType type,
+      const std::string& api_name,
+      const std::string& page_url,
+      const std::string& arg_url,
+      const base::Callback
+          <void(scoped_ptr<std::vector<scoped_refptr<Action> > >)>& callback);
+
   // Extension::InstallObserver
   // We keep track of whether the whitelisted extension is installed; if it is,
   // we want to recompute whether to have logging enabled.
@@ -110,6 +122,13 @@
 
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
+  // Clean up URLs from the activity log database.
+  // If restrict_urls is empty then all URLs in the activity log database are
+  // removed, otherwise only those in restrict_urls are removed.
+  virtual void RemoveURLs(const std::vector<GURL>& restrict_urls);
+  virtual void RemoveURLs(const std::set<GURL>& restrict_urls);
+  virtual void RemoveURL(const GURL& url);
+
  private:
   friend class ActivityLogFactory;
   friend class ActivityLogTest;
diff --git a/chrome/browser/extensions/activity_log/activity_log_policy.h b/chrome/browser/extensions/activity_log/activity_log_policy.h
index a0d3472..c2dad07 100644
--- a/chrome/browser/extensions/activity_log/activity_log_policy.h
+++ b/chrome/browser/extensions/activity_log/activity_log_policy.h
@@ -15,6 +15,7 @@
 #include "base/values.h"
 #include "chrome/browser/extensions/activity_log/activity_actions.h"
 #include "chrome/browser/extensions/activity_log/activity_database.h"
+#include "chrome/common/extensions/api/activity_log_private.h"
 #include "content/public/browser/browser_thread.h"
 #include "url/gurl.h"
 
@@ -38,6 +39,7 @@
 // (1) Receiving Actions to process, and summarizing, compression, and storing
 //     these as appropriate.
 // (2) Reading Actions back from storage.
+// (3) Cleaning of URLs
 //
 // Implementations based on a database should likely implement
 // ActivityDatabase::Delegate, which provides hooks on database events and
@@ -87,6 +89,22 @@
       const base::Callback
           <void(scoped_ptr<Action::ActionVector>)>& callback) = 0;
 
+  // Gets all actions that match the specified fields. URLs are treated like
+  // prefixes; other fields are exact matches. Empty strings are not matched to
+  // anything.
+  virtual void ReadFilteredData(
+      const std::string& extension_id,
+      const Action::ActionType type,
+      const std::string& api_name,
+      const std::string& page_url,
+      const std::string& arg_url,
+      const base::Callback
+         <void(scoped_ptr<Action::ActionVector>)>& callback) = 0;
+
+  // Clean the relevant URL data. The cleaning may need to be different for
+  // different policies. If restrict_urls is empty then all URLs are removed.
+  virtual void RemoveURLs(const std::vector<GURL>& restrict_urls) = 0;
+
   // For unit testing only.
   void SetClockForTesting(scoped_ptr<base::Clock> clock);
 
diff --git a/chrome/browser/extensions/activity_log/activity_log_unittest.cc b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
index 30ade1f..367d0df 100644
--- a/chrome/browser/extensions/activity_log/activity_log_unittest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
@@ -99,12 +99,6 @@
 TEST_F(ActivityLogTest, Construct) {
   ActivityLog* activity_log = ActivityLog::GetInstance(profile());
   ASSERT_TRUE(activity_log->IsLogEnabled());
-
-  scoped_refptr<Action> action = new Action(kExtensionId,
-                                            base::Time::Now(),
-                                            Action::ACTION_API_CALL,
-                                            "tabs.testMethod");
-  activity_log->LogAction(action);
 }
 
 TEST_F(ActivityLogTest, LogAndFetchActions) {
diff --git a/chrome/browser/extensions/activity_log/counting_policy.cc b/chrome/browser/extensions/activity_log/counting_policy.cc
index 8f9b1a1..9188b48 100644
--- a/chrome/browser/extensions/activity_log/counting_policy.cc
+++ b/chrome/browser/extensions/activity_log/counting_policy.cc
@@ -404,6 +404,96 @@
   return true;
 }
 
+scoped_ptr<Action::ActionVector> CountingPolicy::DoReadFilteredData(
+    const std::string& extension_id,
+    const Action::ActionType type,
+    const std::string& api_name,
+    const std::string& page_url,
+    const std::string& arg_url) {
+  // Ensure data is flushed to the database first so that we query over all
+  // data.
+  activity_database()->AdviseFlush(ActivityDatabase::kFlushImmediately);
+  scoped_ptr<Action::ActionVector> actions(new Action::ActionVector());
+
+  sql::Connection* db = GetDatabaseConnection();
+  if (!db)
+    return actions.Pass();
+
+  // Build up the query based on which parameters were specified.
+  std::string where_str = "";
+  std::string where_next = "";
+  if (!extension_id.empty()) {
+    where_str += "extension_id=?";
+    where_next = " AND ";
+  }
+  if (!api_name.empty()) {
+    where_str += where_next + "api_name=?";
+    where_next = " AND ";
+  }
+  if (type != Action::ACTION_ANY) {
+    where_str += where_next + "action_type=?";
+    where_next = " AND ";
+  }
+  if (!page_url.empty()) {
+    where_str += where_next + "page_url LIKE ?";
+    where_next = " AND ";
+  }
+  if (!arg_url.empty())
+    where_str += where_next + "arg_url LIKE ?";
+  std::string query_str = base::StringPrintf(
+      "SELECT extension_id,time, action_type, api_name, args, page_url,"
+      "page_title, arg_url, other, count FROM %s WHERE %s ORDER BY time DESC",
+      kReadViewName,
+      where_str.c_str());
+  sql::Statement query(db->GetUniqueStatement(query_str.c_str()));
+  int i = -1;
+  if (!extension_id.empty())
+    query.BindString(++i, extension_id);
+  if (!api_name.empty())
+    query.BindString(++i, api_name);
+  if (type != Action::ACTION_ANY)
+    query.BindInt(++i, static_cast<int>(type));
+  if (!page_url.empty())
+    query.BindString(++i, page_url + "%");
+  if (!arg_url.empty())
+    query.BindString(++i, arg_url + "%");
+
+  // Execute the query and get results.
+  while (query.is_valid() && query.Step()) {
+    scoped_refptr<CountedAction> action =
+        new CountedAction(query.ColumnString(0),
+                   base::Time::FromInternalValue(query.ColumnInt64(1)),
+                   static_cast<Action::ActionType>(query.ColumnInt(2)),
+                   query.ColumnString(3));
+
+    if (query.ColumnType(4) != sql::COLUMN_TYPE_NULL) {
+      scoped_ptr<Value> parsed_value(
+          base::JSONReader::Read(query.ColumnString(4)));
+      if (parsed_value && parsed_value->IsType(Value::TYPE_LIST)) {
+        action->set_args(
+            make_scoped_ptr(static_cast<ListValue*>(parsed_value.release())));
+      }
+    }
+
+    action->ParsePageUrl(query.ColumnString(5));
+    action->set_page_title(query.ColumnString(6));
+    action->ParseArgUrl(query.ColumnString(7));
+
+    if (query.ColumnType(8) != sql::COLUMN_TYPE_NULL) {
+      scoped_ptr<Value> parsed_value(
+          base::JSONReader::Read(query.ColumnString(8)));
+      if (parsed_value && parsed_value->IsType(Value::TYPE_DICTIONARY)) {
+        action->set_other(make_scoped_ptr(
+            static_cast<DictionaryValue*>(parsed_value.release())));
+      }
+    }
+    action->set_count(query.ColumnInt(9));
+    actions->push_back(action);
+  }
+
+  return actions.Pass();
+}
+
 scoped_ptr<Action::ActionVector> CountingPolicy::DoReadData(
     const std::string& extension_id,
     const int days_ago) {
@@ -477,6 +567,78 @@
   return actions.Pass();
 }
 
+void CountingPolicy::DoRemoveURLs(const std::vector<GURL>& restrict_urls) {
+  sql::Connection* db = GetDatabaseConnection();
+  if (!db) {
+    LOG(ERROR) << "Unable to connect to database";
+    return;
+  }
+
+  // Flush data first so the URL clearing affects queued-up data as well.
+  activity_database()->AdviseFlush(ActivityDatabase::kFlushImmediately);
+
+  // If no restrictions then then all URLs need to be removed.
+  if (restrict_urls.empty()) {
+    std::string sql_str = base::StringPrintf(
+      "UPDATE %s SET page_url_x=NULL,page_title_x=NULL,arg_url_x=NULL",
+      kTableName);
+
+    sql::Statement statement;
+    statement.Assign(db->GetCachedStatement(
+        sql::StatementID(SQL_FROM_HERE), sql_str.c_str()));
+
+    if (!statement.Run()) {
+      LOG(ERROR) << "Removing all URLs from database failed: "
+                 << statement.GetSQLStatement();
+      return;
+    }
+  }
+
+  // If URLs are specified then restrict to only those URLs.
+  for (size_t i = 0; i < restrict_urls.size(); ++i) {
+    int64 url_id;
+    if (!restrict_urls[i].is_valid() ||
+        !url_table_.StringToInt(db, restrict_urls[i].spec(), &url_id)) {
+      continue;
+    }
+
+    // Remove any that match the page_url.
+    std::string sql_str = base::StringPrintf(
+      "UPDATE %s SET page_url_x=NULL,page_title_x=NULL WHERE page_url_x IS ?",
+      kTableName);
+
+    sql::Statement statement;
+    statement.Assign(db->GetCachedStatement(
+        sql::StatementID(SQL_FROM_HERE), sql_str.c_str()));
+    statement.BindInt64(0, url_id);
+
+    if (!statement.Run()) {
+      LOG(ERROR) << "Removing page URL from database failed: "
+                 << statement.GetSQLStatement();
+      return;
+    }
+
+    // Remove any that match the arg_url.
+    sql_str = base::StringPrintf(
+      "UPDATE %s SET arg_url_x=NULL WHERE arg_url_x IS ?", kTableName);
+
+    statement.Assign(db->GetCachedStatement(
+        sql::StatementID(SQL_FROM_HERE), sql_str.c_str()));
+    statement.BindInt64(0, url_id);
+
+    if (!statement.Run()) {
+      LOG(ERROR) << "Removing arg URL from database failed: "
+                 << statement.GetSQLStatement();
+      return;
+    }
+  }
+
+  // Clean up unused strings from the strings and urls table to really delete
+  // the urls and page titles. Should be called even if an error occured when
+  // removing a URL as there may some things to clean up.
+  CleanStringTables(db);
+}
+
 void CountingPolicy::ReadData(
     const std::string& extension_id,
     const int day,
@@ -491,6 +653,31 @@
       callback);
 }
 
+void CountingPolicy::ReadFilteredData(
+    const std::string& extension_id,
+    const Action::ActionType type,
+    const std::string& api_name,
+    const std::string& page_url,
+    const std::string& arg_url,
+    const base::Callback
+        <void(scoped_ptr<Action::ActionVector>)>& callback) {
+  BrowserThread::PostTaskAndReplyWithResult(
+      BrowserThread::DB,
+      FROM_HERE,
+      base::Bind(&CountingPolicy::DoReadFilteredData,
+                 base::Unretained(this),
+                 extension_id,
+                 type,
+                 api_name,
+                 page_url,
+                 arg_url),
+      callback);
+}
+
+void CountingPolicy::RemoveURLs(const std::vector<GURL>& restrict_urls) {
+  ScheduleAndForget(this, &CountingPolicy::DoRemoveURLs, restrict_urls);
+}
+
 void CountingPolicy::OnDatabaseFailure() {
   queued_actions_.clear();
 }
diff --git a/chrome/browser/extensions/activity_log/counting_policy.h b/chrome/browser/extensions/activity_log/counting_policy.h
index 3911bca..b48dd62 100644
--- a/chrome/browser/extensions/activity_log/counting_policy.h
+++ b/chrome/browser/extensions/activity_log/counting_policy.h
@@ -28,6 +28,15 @@
       const base::Callback
           <void(scoped_ptr<Action::ActionVector>)>& callback) OVERRIDE;
 
+  virtual void ReadFilteredData(
+      const std::string& extension_id,
+      const Action::ActionType type,
+      const std::string& api_name,
+      const std::string& page_url,
+      const std::string& arg_url,
+      const base::Callback
+          <void(scoped_ptr<Action::ActionVector>)>& callback) OVERRIDE;
+
   virtual void Close() OVERRIDE;
 
   // Gets or sets the amount of time that old records are kept in the database.
@@ -36,6 +45,9 @@
     retention_time_ = delta;
   }
 
+  // Clean the URL data stored for this policy.
+  virtual void RemoveURLs(const std::vector<GURL>&) OVERRIDE;
+
   // The main database table, and the name for a read-only view that
   // decompresses string values for easier parsing.
   static const char* kTableName;
@@ -66,6 +78,19 @@
       const std::string& extension_id,
       const int days_ago);
 
+  // Internal method to read data from the database; called on the database
+  // thread.
+  scoped_ptr<Action::ActionVector> DoReadFilteredData(
+      const std::string& extension_id,
+      const Action::ActionType type,
+      const std::string& api_name,
+      const std::string& page_url,
+      const std::string& arg_url);
+
+  // The implementation of RemoveURLs; this must only run on the database
+  // thread.
+  void DoRemoveURLs(const std::vector<GURL>& restrict_urls);
+
   // Cleans old records from the activity log database.
   bool CleanOlderThan(sql::Connection* db, const base::Time& cutoff);
 
@@ -104,6 +129,7 @@
   base::Time last_database_cleaning_time_;
 
   friend class CountingPolicyTest;
+  FRIEND_TEST_ALL_PREFIXES(CountingPolicyTest, EarlyFlush);
   FRIEND_TEST_ALL_PREFIXES(CountingPolicyTest, MergingAndExpiring);
   FRIEND_TEST_ALL_PREFIXES(CountingPolicyTest, StringTableCleaning);
 };
diff --git a/chrome/browser/extensions/activity_log/counting_policy_unittest.cc b/chrome/browser/extensions/activity_log/counting_policy_unittest.cc
index c7c364e..dbd55a4 100644
--- a/chrome/browser/extensions/activity_log/counting_policy_unittest.cc
+++ b/chrome/browser/extensions/activity_log/counting_policy_unittest.cc
@@ -6,6 +6,7 @@
 #include "base/command_line.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/run_loop.h"
+#include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/test/simple_test_clock.h"
@@ -100,6 +101,43 @@
     timeout.Cancel();
   }
 
+  // A helper function to call ReadFilteredData on a policy object and wait for
+  // the results to be processed.
+  void CheckReadFilteredData(
+      ActivityLogPolicy* policy,
+      const std::string& extension_id,
+      const Action::ActionType type,
+      const std::string& api_name,
+      const std::string& page_url,
+      const std::string& arg_url,
+      const base::Callback<void(scoped_ptr<Action::ActionVector>)>& checker) {
+    // Submit a request to the policy to read back some data, and call the
+    // checker function when results are available.  This will happen on the
+    // database thread.
+    policy->ReadFilteredData(
+        extension_id,
+        type,
+        api_name,
+        page_url,
+        arg_url,
+        base::Bind(&CountingPolicyTest::CheckWrapper,
+                   checker,
+                   base::MessageLoop::current()->QuitClosure()));
+
+    // Set up a timeout that will trigger after 5 seconds; if we haven't
+    // received any results by then assume that the test is broken.
+    base::CancelableClosure timeout(
+        base::Bind(&CountingPolicyTest::TimeoutCallback));
+    base::MessageLoop::current()->PostDelayedTask(
+        FROM_HERE, timeout.callback(), base::TimeDelta::FromSeconds(5));
+
+    // Wait for results; either the checker or the timeout callbacks should
+    // cause the main loop to exit.
+    base::MessageLoop::current()->Run();
+
+    timeout.Cancel();
+  }
+
   // A helper function which verifies that the string_ids and url_ids tables in
   // the database have the specified sizes.
   static void CheckStringTableSizes(CountingPolicy* policy,
@@ -117,6 +155,14 @@
     ASSERT_EQ(url_size, statement2.ColumnInt(0));
   }
 
+  // Checks that the number of queued actions to be written out does not exceed
+  // kSizeThresholdForFlush.  Runs on the database thread.
+  static void CheckQueueSize(CountingPolicy* policy) {
+    // This should be updated if kSizeThresholdForFlush in activity_database.cc
+    // changes.
+    ASSERT_LE(policy->queued_actions_.size(), 200U);
+  }
+
   static void CheckWrapper(
       const base::Callback<void(scoped_ptr<Action::ActionVector>)>& checker,
       const base::Closure& done,
@@ -130,6 +176,16 @@
     FAIL() << "Policy test timed out waiting for results";
   }
 
+  static void RetrieveActions_FetchFilteredActions1(
+      scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
+    ASSERT_EQ(1, static_cast<int>(i->size()));
+  }
+
+  static void RetrieveActions_FetchFilteredActions2(
+      scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
+    ASSERT_EQ(2, static_cast<int>(i->size()));
+  }
+
   static void Arguments_Stripped(scoped_ptr<Action::ActionVector> i) {
     scoped_refptr<Action> last = i->front();
     std::string args =
@@ -194,6 +250,49 @@
     }
   }
 
+  static void AllURLsRemoved(scoped_ptr<Action::ActionVector> actions) {
+    ASSERT_EQ(2, static_cast<int>(actions->size()));
+    CheckAction(*actions->at(0), "punky", Action::ACTION_DOM_ACCESS, "lets",
+                "[\"vamoose\"]", "", "", "");
+    CheckAction(*actions->at(1), "punky", Action::ACTION_DOM_ACCESS, "lets",
+                "[\"vamoose\"]", "", "", "");
+  }
+
+  static void SomeURLsRemoved(scoped_ptr<Action::ActionVector> actions) {
+    // These will be in the vector in reverse time order.
+    ASSERT_EQ(5, static_cast<int>(actions->size()));
+    CheckAction(*actions->at(0), "punky", Action::ACTION_DOM_ACCESS, "lets",
+                "[\"vamoose\"]", "http://www.google.com/", "Google",
+                "http://www.args-url.com/");
+    CheckAction(*actions->at(1), "punky", Action::ACTION_DOM_ACCESS, "lets",
+                "[\"vamoose\"]", "http://www.google.com/", "Google", "");
+    CheckAction(*actions->at(2), "punky", Action::ACTION_DOM_ACCESS, "lets",
+                "[\"vamoose\"]", "", "", "");
+    CheckAction(*actions->at(3), "punky", Action::ACTION_DOM_ACCESS, "lets",
+                "[\"vamoose\"]", "", "", "http://www.google.com/");
+    CheckAction(*actions->at(4), "punky", Action::ACTION_DOM_ACCESS, "lets",
+                "[\"vamoose\"]", "", "", "");
+  }
+
+  // TODO(karenlees): add checking for the count value.
+  static void CheckAction(const Action& action,
+                          const std::string& expected_id,
+                          const Action::ActionType& expected_type,
+                          const std::string& expected_api_name,
+                          const std::string& expected_args_str,
+                          const std::string& expected_page_url,
+                          const std::string& expected_page_title,
+                          const std::string& expected_arg_url) {
+    ASSERT_EQ(expected_id, action.extension_id());
+    ASSERT_EQ(expected_type, action.action_type());
+    ASSERT_EQ(expected_api_name, action.api_name());
+    ASSERT_EQ(expected_args_str,
+              ActivityLogPolicy::Util::Serialize(action.args()));
+    ASSERT_EQ(expected_page_url, action.SerializePageUrl());
+    ASSERT_EQ(expected_page_title, action.page_title());
+    ASSERT_EQ(expected_arg_url, action.SerializeArgUrl());
+  }
+
  protected:
   ExtensionService* extension_service_;
   scoped_ptr<TestingProfile> profile_;
@@ -374,6 +473,97 @@
   policy->Close();
 }
 
+TEST_F(CountingPolicyTest, LogAndFetchFilteredActions) {
+  ActivityLogPolicy* policy = new CountingPolicy(profile_.get());
+  scoped_refptr<const Extension> extension =
+      ExtensionBuilder()
+          .SetManifest(DictionaryBuilder()
+                       .Set("name", "Test extension")
+                       .Set("version", "1.0.0")
+                       .Set("manifest_version", 2))
+          .Build();
+  extension_service_->AddExtension(extension.get());
+  GURL gurl("http://www.google.com");
+
+  // Write some API calls
+  scoped_refptr<Action> action_api = new Action(extension->id(),
+                                                base::Time::Now(),
+                                                Action::ACTION_API_CALL,
+                                                "tabs.testMethod");
+  action_api->set_args(make_scoped_ptr(new base::ListValue()));
+  policy->ProcessAction(action_api);
+
+  scoped_refptr<Action> action_dom = new Action(extension->id(),
+                                                base::Time::Now(),
+                                                Action::ACTION_DOM_ACCESS,
+                                                "document.write");
+  action_dom->set_args(make_scoped_ptr(new base::ListValue()));
+  action_dom->set_page_url(gurl);
+  policy->ProcessAction(action_dom);
+
+  CheckReadFilteredData(
+      policy,
+      extension->id(),
+      Action::ACTION_API_CALL,
+      "tabs.testMethod",
+      "",
+      "",
+      base::Bind(
+          &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
+
+  CheckReadFilteredData(
+      policy,
+      "",
+      Action::ACTION_DOM_ACCESS,
+      "",
+      "",
+      "",
+      base::Bind(
+          &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
+
+  CheckReadFilteredData(
+      policy,
+      "",
+      Action::ACTION_DOM_ACCESS,
+      "",
+      "http://www.google.com/",
+      "",
+      base::Bind(
+          &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
+
+  CheckReadFilteredData(
+      policy,
+      "",
+      Action::ACTION_DOM_ACCESS,
+      "",
+      "http://www.google.com",
+      "",
+      base::Bind(
+          &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
+
+  CheckReadFilteredData(
+      policy,
+      "",
+      Action::ACTION_DOM_ACCESS,
+      "",
+      "http://www.goo",
+      "",
+      base::Bind(
+          &CountingPolicyTest::RetrieveActions_FetchFilteredActions1));
+
+  CheckReadFilteredData(
+      policy,
+      extension->id(),
+      Action::ACTION_ANY,
+      "",
+      "",
+      "",
+      base::Bind(
+          &CountingPolicyTest::RetrieveActions_FetchFilteredActions2));
+
+  policy->Close();
+}
+
 // Check that merging of actions only occurs within the same day, not across
 // days, and that old data can be expired from the database.
 TEST_F(CountingPolicyTest, MergingAndExpiring) {
@@ -572,4 +762,141 @@
   policy->Close();
 }
 
+// Check that actions are flushed to disk before letting too many accumulate in
+// memory.
+TEST_F(CountingPolicyTest, EarlyFlush) {
+  CountingPolicy* policy = new CountingPolicy(profile_.get());
+
+  for (int i = 0; i < 500; i++) {
+    scoped_refptr<Action> action =
+        new Action("punky",
+                   base::Time::Now(),
+                   Action::ACTION_API_CALL,
+                   base::StringPrintf("apicall_%d", i));
+    policy->ProcessAction(action);
+  }
+
+  policy->ScheduleAndForget(policy, &CountingPolicyTest::CheckQueueSize);
+  WaitOnThread(BrowserThread::DB);
+
+  policy->Close();
+}
+
+TEST_F(CountingPolicyTest, RemoveAllURLs) {
+  ActivityLogPolicy* policy = new CountingPolicy(profile_.get());
+
+  // Use a mock clock to ensure that events are not recorded on the wrong day
+  // when the test is run close to local midnight.
+  base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
+  mock_clock->SetNow(base::Time::Now().LocalMidnight() +
+                     base::TimeDelta::FromHours(12));
+  policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
+
+  // Record some actions
+  scoped_refptr<Action> action =
+      new Action("punky", mock_clock->Now(),
+                 Action::ACTION_DOM_ACCESS, "lets");
+  action->mutable_args()->AppendString("vamoose");
+  action->set_page_url(GURL("http://www.google.com"));
+  action->set_page_title("Google");
+  action->set_arg_url(GURL("http://www.args-url.com"));
+  policy->ProcessAction(action);
+
+  mock_clock->Advance(base::TimeDelta::FromSeconds(1));
+  action = new Action(
+      "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
+  action->mutable_args()->AppendString("vamoose");
+  action->set_page_url(GURL("http://www.google2.com"));
+  action->set_page_title("Google");
+  // Deliberately no arg url set to make sure it stills works if there is no arg
+  // url.
+  policy->ProcessAction(action);
+
+  // Clean all the URLs.
+  std::vector<GURL> no_url_restrictions;
+  policy->RemoveURLs(no_url_restrictions);
+
+  CheckReadData(
+      policy,
+      "punky",
+      0,
+      base::Bind(&CountingPolicyTest::AllURLsRemoved));
+  policy->Close();
+}
+
+TEST_F(CountingPolicyTest, RemoveSpecificURLs) {
+  ActivityLogPolicy* policy = new CountingPolicy(profile_.get());
+
+  // Use a mock clock to ensure that events are not recorded on the wrong day
+  // when the test is run close to local midnight.
+  base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
+  mock_clock->SetNow(base::Time::Now().LocalMidnight() +
+                     base::TimeDelta::FromHours(12));
+  policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
+
+  // Record some actions
+  // This should have the page url and args url cleared.
+  scoped_refptr<Action> action = new Action("punky", mock_clock->Now(),
+                                            Action::ACTION_DOM_ACCESS, "lets");
+  action->mutable_args()->AppendString("vamoose");
+  action->set_page_url(GURL("http://www.google1.com"));
+  action->set_page_title("Google");
+  action->set_arg_url(GURL("http://www.google1.com"));
+  policy->ProcessAction(action);
+
+  // This should have the page url cleared but not args url.
+  mock_clock->Advance(base::TimeDelta::FromSeconds(1));
+  action = new Action(
+      "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
+  action->mutable_args()->AppendString("vamoose");
+  action->set_page_url(GURL("http://www.google1.com"));
+  action->set_page_title("Google");
+  action->set_arg_url(GURL("http://www.google.com"));
+  policy->ProcessAction(action);
+
+  // This should have the page url cleared. The args url is deliberately not
+  // set to make sure this doesn't cause any issues.
+  mock_clock->Advance(base::TimeDelta::FromSeconds(1));
+  action = new Action(
+      "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
+  action->mutable_args()->AppendString("vamoose");
+  action->set_page_url(GURL("http://www.google2.com"));
+  action->set_page_title("Google");
+  policy->ProcessAction(action);
+
+  // This should have the args url cleared but not the page url or page title.
+  mock_clock->Advance(base::TimeDelta::FromSeconds(1));
+  action = new Action(
+      "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
+  action->mutable_args()->AppendString("vamoose");
+  action->set_page_url(GURL("http://www.google.com"));
+  action->set_page_title("Google");
+  action->set_arg_url(GURL("http://www.google1.com"));
+  policy->ProcessAction(action);
+
+  // This should have neither cleared.
+  mock_clock->Advance(base::TimeDelta::FromSeconds(1));
+  action = new Action(
+      "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
+  action->mutable_args()->AppendString("vamoose");
+  action->set_page_url(GURL("http://www.google.com"));
+  action->set_page_title("Google");
+  action->set_arg_url(GURL("http://www.args-url.com"));
+  policy->ProcessAction(action);
+
+    // Clean some URLs.
+  std::vector<GURL> urls;
+  urls.push_back(GURL("http://www.google1.com"));
+  urls.push_back(GURL("http://www.google2.com"));
+  urls.push_back(GURL("http://www.url_not_in_db.com"));
+  policy->RemoveURLs(urls);
+
+  CheckReadData(
+      policy,
+      "punky",
+      0,
+      base::Bind(&CountingPolicyTest::SomeURLsRemoved));
+  policy->Close();
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/activity_log/database_string_table.cc b/chrome/browser/extensions/activity_log/database_string_table.cc
index c9e832e..01c52c9 100644
--- a/chrome/browser/extensions/activity_log/database_string_table.cc
+++ b/chrome/browser/extensions/activity_log/database_string_table.cc
@@ -12,6 +12,10 @@
 
 namespace extensions {
 
+// A target maximum size (in number of entries) for the mapping tables.  If the
+// cache would grow larger than this, the size should be reduced.
+static const size_t kMaximumCacheSize = 1000;
+
 DatabaseStringTable::DatabaseStringTable(const std::string& table)
     : table_(table) {}
 
@@ -40,6 +44,10 @@
     return true;
   }
 
+  // We will be adding data to the cache below--check the cache size now and
+  // reduce it if needed.
+  PruneCache();
+
   // Operate on the assumption that the cache does a good job on
   // frequently-used strings--if there is a cache miss, first act on the
   // assumption that the string is not in the database either.
@@ -82,6 +90,10 @@
     return true;
   }
 
+  // We will be adding data to the cache below--check the cache size now and
+  // reduce it if needed.
+  PruneCache();
+
   sql::Statement query(connection->GetUniqueStatement(
       StringPrintf("SELECT value FROM %s WHERE id = ?", table_.c_str())
           .c_str()));
@@ -100,4 +112,16 @@
   value_to_id_.clear();
 }
 
+void DatabaseStringTable::PruneCache() {
+  if (id_to_value_.size() <= kMaximumCacheSize &&
+      value_to_id_.size() <= kMaximumCacheSize)
+    return;
+
+  // TODO(mvrable): Perhaps implement a more intelligent caching policy.  For
+  // now, to limit memory usage we simply clear the entire cache when it would
+  // become too large.  Data will be brought back in from the database as
+  // needed.
+  ClearCache();
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/activity_log/database_string_table.h b/chrome/browser/extensions/activity_log/database_string_table.h
index 9677fae..6c23040 100644
--- a/chrome/browser/extensions/activity_log/database_string_table.h
+++ b/chrome/browser/extensions/activity_log/database_string_table.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
 
 namespace sql {
 class Connection;
@@ -51,11 +52,14 @@
   // found.
   bool IntToString(sql::Connection* connection, int64 id, std::string* value);
 
-  // Clear the in-memory cache; this should be called if the underlying
+  // Clears the in-memory cache; this should be called if the underlying
   // database table has been manipulated and the cache may be stale.
   void ClearCache();
 
  private:
+  // Reduces the size of the cache if too many entries are held in it.
+  void PruneCache();
+
   // In-memory caches of recently accessed values.
   std::map<int64, std::string> id_to_value_;
   std::map<std::string, int64> value_to_id_;
@@ -63,6 +67,8 @@
   // The name of the database table where the mapping is stored.
   std::string table_;
 
+  FRIEND_TEST_ALL_PREFIXES(DatabaseStringTableTest, Prune);
+
   DISALLOW_COPY_AND_ASSIGN(DatabaseStringTable);
 };
 
diff --git a/chrome/browser/extensions/activity_log/database_string_table_unittest.cc b/chrome/browser/extensions/activity_log/database_string_table_unittest.cc
index 2024827..19908ca 100644
--- a/chrome/browser/extensions/activity_log/database_string_table_unittest.cc
+++ b/chrome/browser/extensions/activity_log/database_string_table_unittest.cc
@@ -3,9 +3,11 @@
 // found in the LICENSE file.
 
 #include "base/files/scoped_temp_dir.h"
+#include "base/strings/stringprintf.h"
 #include "chrome/browser/extensions/activity_log/database_string_table.h"
 #include "sql/connection.h"
 #include "sql/statement.h"
+#include "sql/transaction.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace extensions {
@@ -130,4 +132,28 @@
   ASSERT_EQ("abc", value);
 }
 
+// Check that the in-memory cache for the string table does not become too
+// large, even if many items are inserted.
+TEST_F(DatabaseStringTableTest, Prune) {
+  DatabaseStringTable table("size_test");
+  table.Initialize(&db_);
+
+  // Wrap the lookups in a transaction to improve performance.
+  sql::Transaction transaction(&db_);
+
+  transaction.Begin();
+  for (int i = 0; i < 2000; i++) {
+    int64 id;
+    ASSERT_TRUE(table.StringToInt(&db_, base::StringPrintf("value-%d", i),
+                                  &id));
+  }
+  transaction.Commit();
+
+  // The maximum size below should correspond to kMaximumCacheSize in
+  // database_string_table.cc, with a small amount of additional slop (an entry
+  // might be inserted after doing the pruning).
+  ASSERT_LE(table.id_to_value_.size(), 1005U);
+  ASSERT_LE(table.value_to_id_.size(), 1005U);
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/activity_log/fullstream_ui_policy.cc b/chrome/browser/extensions/activity_log/fullstream_ui_policy.cc
index 94f6371..9847a83 100644
--- a/chrome/browser/extensions/activity_log/fullstream_ui_policy.cc
+++ b/chrome/browser/extensions/activity_log/fullstream_ui_policy.cc
@@ -97,9 +97,9 @@
     if (!action.page_title().empty()) {
       statement.BindString(6, action.page_title());
     }
-    std::string arg_url_string = action.SerializePageUrl();
+    std::string arg_url_string = action.SerializeArgUrl();
     if (!arg_url_string.empty()) {
-      statement.BindString(5, arg_url_string);
+      statement.BindString(7, arg_url_string);
     }
     if (action.other()) {
       statement.BindString(8, Util::Serialize(action.other()));
@@ -118,6 +118,97 @@
   return true;
 }
 
+scoped_ptr<Action::ActionVector> FullStreamUIPolicy::DoReadFilteredData(
+    const std::string& extension_id,
+    const Action::ActionType type,
+    const std::string& api_name,
+    const std::string& page_url,
+    const std::string& arg_url) {
+  // Ensure data is flushed to the database first so that we query over all
+  // data.
+  activity_database()->AdviseFlush(ActivityDatabase::kFlushImmediately);
+  scoped_ptr<Action::ActionVector> actions(new Action::ActionVector());
+
+  sql::Connection* db = GetDatabaseConnection();
+  if (!db) {
+    return actions.Pass();
+  }
+
+  // Build up the query based on which parameters were specified.
+  std::string where_str = "";
+  std::string where_next = "";
+  if (!extension_id.empty()) {
+    where_str += "extension_id=?";
+    where_next = " AND ";
+  }
+  if (!api_name.empty()) {
+    where_str += where_next + "api_name=?";
+    where_next = " AND ";
+  }
+  if (type != Action::ACTION_ANY) {
+    where_str += where_next + "action_type=?";
+    where_next = " AND ";
+  }
+  if (!page_url.empty()) {
+    where_str += where_next + "page_url LIKE ?";
+    where_next = " AND ";
+  }
+  if (!arg_url.empty()) {
+    where_str += where_next + "arg_url LIKE ?";
+  }
+  std::string query_str = base::StringPrintf(
+      "SELECT extension_id,time,action_type,api_name,args,page_url,page_title,"
+      "arg_url,other FROM %s WHERE %s ORDER BY time DESC",
+      kTableName,
+      where_str.c_str());
+  sql::Statement query(db->GetUniqueStatement(query_str.c_str()));
+  int i = -1;
+  if (!extension_id.empty())
+    query.BindString(++i, extension_id);
+  if (!api_name.empty())
+    query.BindString(++i, api_name);
+  if (type != Action::ACTION_ANY)
+    query.BindInt(++i, static_cast<int>(type));
+  if (!page_url.empty())
+    query.BindString(++i, page_url + "%");
+  if (!arg_url.empty())
+    query.BindString(++i, arg_url + "%");
+
+  // Execute the query and get results.
+  while (query.is_valid() && query.Step()) {
+    scoped_refptr<Action> action =
+        new Action(query.ColumnString(0),
+                   base::Time::FromInternalValue(query.ColumnInt64(1)),
+                   static_cast<Action::ActionType>(query.ColumnInt(2)),
+                   query.ColumnString(3));
+
+    if (query.ColumnType(4) != sql::COLUMN_TYPE_NULL) {
+      scoped_ptr<Value> parsed_value(
+          base::JSONReader::Read(query.ColumnString(4)));
+      if (parsed_value && parsed_value->IsType(Value::TYPE_LIST)) {
+        action->set_args(
+            make_scoped_ptr(static_cast<ListValue*>(parsed_value.release())));
+      }
+    }
+
+    action->ParsePageUrl(query.ColumnString(5));
+    action->set_page_title(query.ColumnString(6));
+    action->ParseArgUrl(query.ColumnString(7));
+
+    if (query.ColumnType(8) != sql::COLUMN_TYPE_NULL) {
+      scoped_ptr<Value> parsed_value(
+          base::JSONReader::Read(query.ColumnString(8)));
+      if (parsed_value && parsed_value->IsType(Value::TYPE_DICTIONARY)) {
+        action->set_other(make_scoped_ptr(
+            static_cast<DictionaryValue*>(parsed_value.release())));
+      }
+    }
+    actions->push_back(action);
+  }
+
+  return actions.Pass();
+}
+
 scoped_ptr<Action::ActionVector> FullStreamUIPolicy::DoReadData(
     const std::string& extension_id,
     const int days_ago) {
@@ -187,6 +278,68 @@
   return actions.Pass();
 }
 
+void FullStreamUIPolicy::DoRemoveURLs(const std::vector<GURL>& restrict_urls) {
+  sql::Connection* db = GetDatabaseConnection();
+  if (!db) {
+    LOG(ERROR) << "Unable to connect to database";
+    return;
+  }
+
+  // Make sure any queued in memory are sent to the database before cleaning.
+  activity_database()->AdviseFlush(ActivityDatabase::kFlushImmediately);
+
+  // If no restrictions then then all URLs need to be removed.
+  if (restrict_urls.empty()) {
+    sql::Statement statement;
+    std::string sql_str = base::StringPrintf(
+        "UPDATE %s SET page_url=NULL,page_title=NULL,arg_url=NULL",
+        kTableName);
+    statement.Assign(db->GetCachedStatement(
+        sql::StatementID(SQL_FROM_HERE), sql_str.c_str()));
+
+    if (!statement.Run()) {
+      LOG(ERROR) << "Removing URLs from database failed: "
+                 << statement.GetSQLStatement();
+    }
+    return;
+  }
+
+  // If URLs are specified then restrict to only those URLs.
+  for (size_t i = 0; i < restrict_urls.size(); ++i) {
+    if (!restrict_urls[i].is_valid()) {
+      continue;
+    }
+
+    // Remove any matching page url info.
+    sql::Statement statement;
+    std::string sql_str = base::StringPrintf(
+      "UPDATE %s SET page_url=NULL,page_title=NULL WHERE page_url=?",
+      kTableName);
+    statement.Assign(db->GetCachedStatement(
+        sql::StatementID(SQL_FROM_HERE), sql_str.c_str()));
+    statement.BindString(0, restrict_urls[i].spec());
+
+    if (!statement.Run()) {
+      LOG(ERROR) << "Removing page URL from database failed: "
+                 << statement.GetSQLStatement();
+      return;
+    }
+
+    // Remove any matching arg urls.
+    sql_str = base::StringPrintf("UPDATE %s SET arg_url=NULL WHERE arg_url=?",
+                                 kTableName);
+    statement.Assign(db->GetCachedStatement(
+        sql::StatementID(SQL_FROM_HERE), sql_str.c_str()));
+    statement.BindString(0, restrict_urls[i].spec());
+
+    if (!statement.Run()) {
+      LOG(ERROR) << "Removing arg URL from database failed: "
+                 << statement.GetSQLStatement();
+      return;
+    }
+  }
+}
+
 void FullStreamUIPolicy::OnDatabaseFailure() {
   queued_actions_.clear();
 }
@@ -216,6 +369,31 @@
       callback);
 }
 
+void FullStreamUIPolicy::ReadFilteredData(
+    const std::string& extension_id,
+    const Action::ActionType type,
+    const std::string& api_name,
+    const std::string& page_url,
+    const std::string& arg_url,
+    const base::Callback
+        <void(scoped_ptr<Action::ActionVector>)>& callback) {
+  BrowserThread::PostTaskAndReplyWithResult(
+      BrowserThread::DB,
+      FROM_HERE,
+      base::Bind(&FullStreamUIPolicy::DoReadFilteredData,
+                 base::Unretained(this),
+                 extension_id,
+                 type,
+                 api_name,
+                 page_url,
+                 arg_url),
+      callback);
+}
+
+void FullStreamUIPolicy::RemoveURLs(const std::vector<GURL>& restrict_urls) {
+  ScheduleAndForget(this, &FullStreamUIPolicy::DoRemoveURLs, restrict_urls);
+}
+
 scoped_refptr<Action> FullStreamUIPolicy::ProcessArguments(
     scoped_refptr<Action> action) const {
   return action;
diff --git a/chrome/browser/extensions/activity_log/fullstream_ui_policy.h b/chrome/browser/extensions/activity_log/fullstream_ui_policy.h
index 4f50d54..e4a2f64 100644
--- a/chrome/browser/extensions/activity_log/fullstream_ui_policy.h
+++ b/chrome/browser/extensions/activity_log/fullstream_ui_policy.h
@@ -39,8 +39,20 @@
       const base::Callback
           <void(scoped_ptr<Action::ActionVector>)>& callback) OVERRIDE;
 
+  virtual void ReadFilteredData(
+      const std::string& extension_id,
+      const Action::ActionType type,
+      const std::string& api_name,
+      const std::string& page_url,
+      const std::string& arg_url,
+      const base::Callback
+          <void(scoped_ptr<Action::ActionVector>)>& callback) OVERRIDE;
+
   virtual void Close() OVERRIDE;
 
+  // Clean the URL data stored for this policy.
+  virtual void RemoveURLs(const std::vector<GURL>& restrict_urls) OVERRIDE;
+
   // Database table schema.
   static const char* kTableName;
   static const char* kTableContentFields[];
@@ -65,6 +77,10 @@
   virtual scoped_refptr<Action> ProcessArguments(
       scoped_refptr<Action> action) const;
 
+  // The implementation of RemoveURLs; this must only run on the database
+  // thread.
+  virtual void DoRemoveURLs(const std::vector<GURL>& restrict_urls);
+
   // Tracks any pending updates to be written to the database, if write
   // batching is turned on.  Should only be accessed from the database thread.
   Action::ActionVector queued_actions_;
@@ -77,6 +93,15 @@
   // The implementation of ReadData; this must only run on the database thread.
   scoped_ptr<Action::ActionVector> DoReadData(const std::string& extension_id,
                                               const int days_ago);
+
+  // Internal method to read data from the database; called on the database
+  // thread.
+  scoped_ptr<Action::ActionVector> DoReadFilteredData(
+      const std::string& extension_id,
+      const Action::ActionType type,
+      const std::string& api_name,
+      const std::string& page_url,
+      const std::string& arg_url);
 };
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/activity_log/fullstream_ui_policy_unittest.cc b/chrome/browser/extensions/activity_log/fullstream_ui_policy_unittest.cc
index 2c453a8..5dc6445 100644
--- a/chrome/browser/extensions/activity_log/fullstream_ui_policy_unittest.cc
+++ b/chrome/browser/extensions/activity_log/fullstream_ui_policy_unittest.cc
@@ -91,6 +91,43 @@
     timeout.Cancel();
   }
 
+  // A helper function to call ReadFilteredData on a policy object and wait for
+  // the results to be processed.
+  void CheckReadFilteredData(
+      ActivityLogPolicy* policy,
+      const std::string& extension_id,
+      const Action::ActionType type,
+      const std::string& api_name,
+      const std::string& page_url,
+      const std::string& arg_url,
+      const base::Callback<void(scoped_ptr<Action::ActionVector>)>& checker) {
+    // Submit a request to the policy to read back some data, and call the
+    // checker function when results are available.  This will happen on the
+    // database thread.
+    policy->ReadFilteredData(
+        extension_id,
+        type,
+        api_name,
+        page_url,
+        arg_url,
+        base::Bind(&FullStreamUIPolicyTest::CheckWrapper,
+                   checker,
+                   base::MessageLoop::current()->QuitClosure()));
+
+    // Set up a timeout that will trigger after 5 seconds; if we haven't
+    // received any results by then assume that the test is broken.
+    base::CancelableClosure timeout(
+        base::Bind(&FullStreamUIPolicyTest::TimeoutCallback));
+    base::MessageLoop::current()->PostDelayedTask(
+        FROM_HERE, timeout.callback(), base::TimeDelta::FromSeconds(5));
+
+    // Wait for results; either the checker or the timeout callbacks should
+    // cause the main loop to exit.
+    base::MessageLoop::current()->Run();
+
+    timeout.Cancel();
+  }
+
   static void CheckWrapper(
       const base::Callback<void(scoped_ptr<Action::ActionVector>)>& checker,
       const base::Closure& done,
@@ -109,6 +146,16 @@
     ASSERT_EQ(2, static_cast<int>(i->size()));
   }
 
+  static void RetrieveActions_FetchFilteredActions1(
+      scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
+    ASSERT_EQ(1, static_cast<int>(i->size()));
+  }
+
+  static void RetrieveActions_FetchFilteredActions2(
+      scoped_ptr<std::vector<scoped_refptr<Action> > > i) {
+    ASSERT_EQ(2, static_cast<int>(i->size()));
+  }
+
   static void Arguments_Present(scoped_ptr<Action::ActionVector> i) {
     scoped_refptr<Action> last = i->front();
     std::string args =
@@ -120,10 +167,12 @@
   static void Arguments_GetTodaysActions(
       scoped_ptr<Action::ActionVector> actions) {
     std::string api_print =
-        "ID=punky CATEGORY=api_call API=brewster ARGS=[\"woof\"]";
+        "ID=punky CATEGORY=api_call API=brewster ARGS=[\"woof\"] "
+        "PAGE_TITLE=\"Page Title\" ARG_URL=http://www.arg-url.com/";
     std::string dom_print =
         "ID=punky CATEGORY=dom_access API=lets ARGS=[\"vamoose\"] "
-        "PAGE_URL=http://www.google.com/";
+        "PAGE_URL=http://www.google.com/ PAGE_TITLE=\"Page Title\" "
+        "ARG_URL=http://www.arg-url.com/";
     ASSERT_EQ(2, static_cast<int>(actions->size()));
     ASSERT_EQ(dom_print, actions->at(0)->PrintForDebug());
     ASSERT_EQ(api_print, actions->at(1)->PrintForDebug());
@@ -141,6 +190,48 @@
     ASSERT_EQ(api_print, actions->at(1)->PrintForDebug());
   }
 
+  static void AllURLsRemoved(scoped_ptr<Action::ActionVector> actions) {
+    ASSERT_EQ(2, static_cast<int>(actions->size()));
+    CheckAction(*actions->at(0), "punky", Action::ACTION_API_CALL, "lets",
+                "[\"vamoose\"]", "", "", "");
+    CheckAction(*actions->at(1), "punky", Action::ACTION_DOM_ACCESS, "lets",
+                "[\"vamoose\"]", "", "", "");
+  }
+
+  static void SomeURLsRemoved(scoped_ptr<Action::ActionVector> actions) {
+    // These will be in the vector in reverse time order.
+    ASSERT_EQ(5, static_cast<int>(actions->size()));
+    CheckAction(*actions->at(0), "punky", Action::ACTION_DOM_ACCESS, "lets",
+                "[\"vamoose\"]", "http://www.google.com/", "Google",
+                "http://www.args-url.com/");
+    CheckAction(*actions->at(1), "punky", Action::ACTION_DOM_ACCESS, "lets",
+                "[\"vamoose\"]", "http://www.google.com/", "Google", "");
+    CheckAction(*actions->at(2), "punky", Action::ACTION_DOM_ACCESS, "lets",
+                "[\"vamoose\"]", "", "", "");
+    CheckAction(*actions->at(3), "punky", Action::ACTION_DOM_ACCESS, "lets",
+                "[\"vamoose\"]", "", "", "http://www.google.com/");
+    CheckAction(*actions->at(4), "punky", Action::ACTION_DOM_ACCESS, "lets",
+                "[\"vamoose\"]", "", "", "");
+  }
+
+  static void CheckAction(const Action& action,
+                          const std::string& expected_id,
+                          const Action::ActionType& expected_type,
+                          const std::string& expected_api_name,
+                          const std::string& expected_args_str,
+                          const std::string& expected_page_url,
+                          const std::string& expected_page_title,
+                          const std::string& expected_arg_url) {
+    ASSERT_EQ(expected_id, action.extension_id());
+    ASSERT_EQ(expected_type, action.action_type());
+    ASSERT_EQ(expected_api_name, action.api_name());
+    ASSERT_EQ(expected_args_str,
+              ActivityLogPolicy::Util::Serialize(action.args()));
+    ASSERT_EQ(expected_page_url, action.SerializePageUrl());
+    ASSERT_EQ(expected_page_title, action.page_title());
+    ASSERT_EQ(expected_arg_url, action.SerializeArgUrl());
+  }
+
  protected:
   ExtensionService* extension_service_;
   scoped_ptr<TestingProfile> profile_;
@@ -215,6 +306,97 @@
   policy->Close();
 }
 
+TEST_F(FullStreamUIPolicyTest, LogAndFetchFilteredActions) {
+  ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
+  scoped_refptr<const Extension> extension =
+      ExtensionBuilder()
+          .SetManifest(DictionaryBuilder()
+                       .Set("name", "Test extension")
+                       .Set("version", "1.0.0")
+                       .Set("manifest_version", 2))
+          .Build();
+  extension_service_->AddExtension(extension.get());
+  GURL gurl("http://www.google.com");
+
+  // Write some API calls
+  scoped_refptr<Action> action_api = new Action(extension->id(),
+                                                base::Time::Now(),
+                                                Action::ACTION_API_CALL,
+                                                "tabs.testMethod");
+  action_api->set_args(make_scoped_ptr(new base::ListValue()));
+  policy->ProcessAction(action_api);
+
+  scoped_refptr<Action> action_dom = new Action(extension->id(),
+                                                base::Time::Now(),
+                                                Action::ACTION_DOM_ACCESS,
+                                                "document.write");
+  action_dom->set_args(make_scoped_ptr(new base::ListValue()));
+  action_dom->set_page_url(gurl);
+  policy->ProcessAction(action_dom);
+
+  CheckReadFilteredData(
+      policy,
+      extension->id(),
+      Action::ACTION_API_CALL,
+      "tabs.testMethod",
+      "",
+      "",
+      base::Bind(
+          &FullStreamUIPolicyTest::RetrieveActions_FetchFilteredActions1));
+
+  CheckReadFilteredData(
+      policy,
+      "",
+      Action::ACTION_DOM_ACCESS,
+      "",
+      "",
+      "",
+      base::Bind(
+          &FullStreamUIPolicyTest::RetrieveActions_FetchFilteredActions1));
+
+  CheckReadFilteredData(
+      policy,
+      "",
+      Action::ACTION_DOM_ACCESS,
+      "",
+      "http://www.google.com/",
+      "",
+      base::Bind(
+          &FullStreamUIPolicyTest::RetrieveActions_FetchFilteredActions1));
+
+  CheckReadFilteredData(
+      policy,
+      "",
+      Action::ACTION_DOM_ACCESS,
+      "",
+      "http://www.google.com",
+      "",
+      base::Bind(
+          &FullStreamUIPolicyTest::RetrieveActions_FetchFilteredActions1));
+
+  CheckReadFilteredData(
+      policy,
+      "",
+      Action::ACTION_DOM_ACCESS,
+      "",
+      "http://www.goo",
+      "",
+      base::Bind(
+          &FullStreamUIPolicyTest::RetrieveActions_FetchFilteredActions1));
+
+  CheckReadFilteredData(
+      policy,
+      extension->id(),
+      Action::ACTION_ANY,
+      "",
+      "",
+      "",
+      base::Bind(
+          &FullStreamUIPolicyTest::RetrieveActions_FetchFilteredActions2));
+
+  policy->Close();
+}
+
 TEST_F(FullStreamUIPolicyTest, LogWithArguments) {
   ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
   scoped_refptr<const Extension> extension =
@@ -263,18 +445,23 @@
                  Action::ACTION_API_CALL,
                  "brewster");
   action->mutable_args()->AppendString("woof");
+  action->set_arg_url(GURL("http://www.arg-url.com"));
+  action->set_page_title("Page Title");
   policy->ProcessAction(action);
 
   action =
       new Action("punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
   action->mutable_args()->AppendString("vamoose");
   action->set_page_url(GURL("http://www.google.com"));
+  action->set_arg_url(GURL("http://www.arg-url.com"));
+  action->set_page_title("Page Title");
   policy->ProcessAction(action);
 
   action = new Action(
       "scoobydoo", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
   action->mutable_args()->AppendString("vamoose");
   action->set_page_url(GURL("http://www.google.com"));
+  action->set_arg_url(GURL("http://www.arg-url.com"));
   policy->ProcessAction(action);
 
   CheckReadData(
@@ -338,4 +525,121 @@
   policy->Close();
 }
 
+TEST_F(FullStreamUIPolicyTest, RemoveAllURLs) {
+  ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
+
+  // Use a mock clock to ensure that events are not recorded on the wrong day
+  // when the test is run close to local midnight.
+  base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
+  mock_clock->SetNow(base::Time::Now().LocalMidnight() +
+                     base::TimeDelta::FromHours(12));
+  policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
+
+  // Record some actions
+  scoped_refptr<Action> action =
+      new Action("punky", mock_clock->Now(),
+                 Action::ACTION_DOM_ACCESS, "lets");
+  action->mutable_args()->AppendString("vamoose");
+  action->set_page_url(GURL("http://www.google.com"));
+  action->set_page_title("Google");
+  action->set_arg_url(GURL("http://www.google.com"));
+  policy->ProcessAction(action);
+
+  mock_clock->Advance(base::TimeDelta::FromSeconds(1));
+  action = new Action(
+      "punky", mock_clock->Now(), Action::ACTION_API_CALL, "lets");
+  action->mutable_args()->AppendString("vamoose");
+  action->set_page_url(GURL("http://www.google2.com"));
+  action->set_page_title("Google");
+  // Deliberately no arg url set to make sure it still works when there is no
+  // arg url.
+  policy->ProcessAction(action);
+
+  // Clean all the URLs.
+  std::vector<GURL> no_url_restrictions;
+  policy->RemoveURLs(no_url_restrictions);
+
+  CheckReadData(
+      policy,
+      "punky",
+      0,
+      base::Bind(&FullStreamUIPolicyTest::AllURLsRemoved));
+  policy->Close();
+}
+
+TEST_F(FullStreamUIPolicyTest, RemoveSpecificURLs) {
+  ActivityLogPolicy* policy = new FullStreamUIPolicy(profile_.get());
+
+  // Use a mock clock to ensure that events are not recorded on the wrong day
+  // when the test is run close to local midnight.
+  base::SimpleTestClock* mock_clock = new base::SimpleTestClock();
+  mock_clock->SetNow(base::Time::Now().LocalMidnight() +
+                     base::TimeDelta::FromHours(12));
+  policy->SetClockForTesting(scoped_ptr<base::Clock>(mock_clock));
+
+  // Record some actions
+  // This should have the page url and args url cleared.
+  scoped_refptr<Action> action = new Action("punky", mock_clock->Now(),
+                                            Action::ACTION_DOM_ACCESS, "lets");
+  action->mutable_args()->AppendString("vamoose");
+  action->set_page_url(GURL("http://www.google1.com"));
+  action->set_page_title("Google");
+  action->set_arg_url(GURL("http://www.google1.com"));
+  policy->ProcessAction(action);
+
+  // This should have the page url cleared but not args url.
+  mock_clock->Advance(base::TimeDelta::FromSeconds(1));
+  action = new Action(
+      "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
+  action->mutable_args()->AppendString("vamoose");
+  action->set_page_url(GURL("http://www.google1.com"));
+  action->set_page_title("Google");
+  action->set_arg_url(GURL("http://www.google.com"));
+  policy->ProcessAction(action);
+
+  // This should have the page url cleared. The args url is deliberately not set
+  // to make sure this doesn't cause any issues.
+  mock_clock->Advance(base::TimeDelta::FromSeconds(1));
+  action = new Action(
+      "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
+  action->mutable_args()->AppendString("vamoose");
+  action->set_page_url(GURL("http://www.google2.com"));
+  action->set_page_title("Google");
+  policy->ProcessAction(action);
+
+  // This should have the args url cleared but not the page url or page title.
+  mock_clock->Advance(base::TimeDelta::FromSeconds(1));
+  action = new Action(
+      "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
+  action->mutable_args()->AppendString("vamoose");
+  action->set_page_url(GURL("http://www.google.com"));
+  action->set_page_title("Google");
+  action->set_arg_url(GURL("http://www.google1.com"));
+  policy->ProcessAction(action);
+
+  // This should have neither cleared.
+  mock_clock->Advance(base::TimeDelta::FromSeconds(1));
+  action = new Action(
+      "punky", mock_clock->Now(), Action::ACTION_DOM_ACCESS, "lets");
+  action->mutable_args()->AppendString("vamoose");
+  action->set_page_url(GURL("http://www.google.com"));
+  action->set_page_title("Google");
+  action->set_arg_url(GURL("http://www.args-url.com"));
+  policy->ProcessAction(action);
+
+  // Clean some URLs.
+  std::vector<GURL> urls;
+  urls.push_back(GURL("http://www.google1.com"));
+  urls.push_back(GURL("http://www.google2.com"));
+  urls.push_back(GURL("http://www.url_not_in_db.com"));
+  policy->RemoveURLs(urls);
+
+  CheckReadData(
+      policy,
+      "punky",
+      0,
+      base::Bind(&FullStreamUIPolicyTest::SomeURLsRemoved));
+  policy->Close();
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.cc b/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.cc
index f2e18eb..dd8ba68 100644
--- a/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.cc
+++ b/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.cc
@@ -20,7 +20,9 @@
 
 namespace activity_log_private = api::activity_log_private;
 
+using api::activity_log_private::ActivityResultSet;
 using api::activity_log_private::ExtensionActivity;
+using api::activity_log_private::Filter;
 
 const char kActivityLogExtensionId[] = "acldcpdepobcjbdanifkmfndkjoilgba";
 const char kActivityLogTestExtensionId[] = "ajabfgledjhbabeoojlabelaifmakodf";
@@ -93,7 +95,84 @@
 }
 
 bool ActivityLogPrivateGetExtensionActivitiesFunction::RunImpl() {
+  scoped_ptr<activity_log_private::GetExtensionActivities::Params> params(
+      activity_log_private::GetExtensionActivities::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params.get());
+
+  // Get the arguments in the right format.
+  scoped_ptr<Filter> filter;
+  filter.reset(&params.release()->filter);
+  Action::ActionType action_type = Action::ACTION_API_CALL;
+  switch (filter->activity_type) {
+    case Filter::ACTIVITY_TYPE_API_CALL:
+      action_type = Action::ACTION_API_CALL;
+      break;
+    case Filter::ACTIVITY_TYPE_API_EVENT:
+      action_type = Action::ACTION_API_EVENT;
+      break;
+    case Filter::ACTIVITY_TYPE_CONTENT_SCRIPT:
+      action_type = Action::ACTION_CONTENT_SCRIPT;
+      break;
+    case Filter::ACTIVITY_TYPE_DOM_ACCESS:
+      action_type = Action::ACTION_DOM_ACCESS;
+      break;
+    case Filter::ACTIVITY_TYPE_DOM_EVENT:
+      action_type = Action::ACTION_DOM_EVENT;
+      break;
+    case  Filter::ACTIVITY_TYPE_WEB_REQUEST:
+      action_type = Action::ACTION_WEB_REQUEST;
+      break;
+    case Filter::ACTIVITY_TYPE_ANY:
+    default:
+      action_type = Action::ACTION_ANY;
+  }
+  std::string extension_id =
+      filter->extension_id.get() ? *filter->extension_id : std::string();
+  std::string api_call =
+      filter->api_call.get() ? *filter->api_call : std::string();
+  std::string page_url =
+      filter->page_url.get() ? *filter->page_url : std::string();
+  std::string arg_url =
+      filter->arg_url.get() ? *filter->arg_url : std::string();
+
+  // Call the ActivityLog.
+  ActivityLog* activity_log = ActivityLog::GetInstance(profile_);
+  DCHECK(activity_log);
+  activity_log->GetFilteredActions(
+      extension_id,
+      action_type,
+      api_call,
+      page_url,
+      arg_url,
+      base::Bind(
+          &ActivityLogPrivateGetExtensionActivitiesFunction::OnLookupCompleted,
+          this));
+
   return true;
 }
 
+void ActivityLogPrivateGetExtensionActivitiesFunction::OnLookupCompleted(
+    scoped_ptr<std::vector<scoped_refptr<Action> > > activities) {
+  // Convert Actions to ExtensionActivities.
+  std::vector<linked_ptr<ExtensionActivity> > result_arr;
+  for (std::vector<scoped_refptr<Action> >::iterator it = activities->begin();
+       it != activities->end();
+       ++it) {
+    result_arr.push_back(
+        make_linked_ptr(it->get()->ConvertToExtensionActivity().release()));
+  }
+
+  // Populate the return object.
+  // TODO(felt): Implement paging. Right now max_time and more_results are
+  // placeholder values.
+  scoped_ptr<ActivityResultSet> result_set(new ActivityResultSet);
+  result_set->activities = result_arr;
+  result_set->max_time = scoped_ptr<int>(new int(0));
+  result_set->more_results = false;
+  results_ = activity_log_private::GetExtensionActivities::Results::Create(
+      *result_set);
+
+  SendResponse(true);
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h b/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h
index cab0ccf..a24e262 100644
--- a/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h
+++ b/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h
@@ -12,7 +12,6 @@
 #include "base/synchronization/lock.h"
 #include "chrome/browser/extensions/activity_log/activity_actions.h"
 #include "chrome/browser/extensions/activity_log/activity_log.h"
-#include "chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h"
 #include "chrome/browser/extensions/api/profile_keyed_api_factory.h"
 #include "chrome/browser/extensions/event_router.h"
 #include "chrome/browser/extensions/extension_function.h"
@@ -76,6 +75,10 @@
 
   // ExtensionFunction:
   virtual bool RunImpl() OVERRIDE;
+
+ private:
+  void OnLookupCompleted(
+      scoped_ptr<std::vector<scoped_refptr<Action> > > activities);
 };
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/content_settings/content_settings_helpers.cc b/chrome/browser/extensions/api/content_settings/content_settings_helpers.cc
index 537d08f..133e330 100644
--- a/chrome/browser/extensions/api/content_settings/content_settings_helpers.cc
+++ b/chrome/browser/extensions/api/content_settings/content_settings_helpers.cc
@@ -45,7 +45,7 @@
 std::string GetDefaultPort(const std::string& scheme) {
   if (scheme == chrome::kHttpScheme)
     return "80";
-  if (scheme == chrome::kHttpsScheme)
+  if (scheme == content::kHttpsScheme)
     return "443";
   NOTREACHED();
   return std::string();
diff --git a/chrome/browser/extensions/api/cookies/cookies_helpers.cc b/chrome/browser/extensions/api/cookies/cookies_helpers.cc
index b68146b..2d3e67e 100644
--- a/chrome/browser/extensions/api/cookies/cookies_helpers.cc
+++ b/chrome/browser/extensions/api/cookies/cookies_helpers.cc
@@ -119,7 +119,7 @@
 GURL GetURLFromCanonicalCookie(const net::CanonicalCookie& cookie) {
   const std::string& domain_key = cookie.Domain();
   const std::string scheme =
-      cookie.IsSecure() ? chrome::kHttpsScheme : chrome::kHttpScheme;
+      cookie.IsSecure() ? content::kHttpsScheme : chrome::kHttpScheme;
   const std::string host =
       domain_key.find('.') != 0 ? domain_key : domain_key.substr(1);
   return GURL(scheme + content::kStandardSchemeSeparator + host + "/");
diff --git a/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc b/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc
index 363d987..55e443a 100644
--- a/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc
+++ b/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc
@@ -7,6 +7,7 @@
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/extensions/extension_test_message_listener.h"
+#include "chrome/browser/extensions/test_extension_dir.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/extensions/features/feature_channel.h"
 #include "content/public/test/browser_test_utils.h"
@@ -14,6 +15,22 @@
 namespace extensions {
 namespace {
 
+const char kDeclarativeContentManifest[] =
+    "{\n"
+    "  \"name\": \"Declarative Content apitest\",\n"
+    "  \"version\": \"0.1\",\n"
+    "  \"manifest_version\": 2,\n"
+    "  \"description\": \n"
+    "      \"end-to-end browser test for the declarative Content API\",\n"
+    "  \"background\": {\n"
+    "    \"scripts\": [\"background.js\"]\n"
+    "  },\n"
+    "  \"permissions\": [\n"
+    "    \"declarativeContent\"\n"
+    "  ],\n"
+    "  \"page_action\": {}\n"
+    "}\n";
+
 class DeclarativeContentApiTest : public ExtensionApiTest {
  public:
   DeclarativeContentApiTest()
@@ -24,12 +41,36 @@
   virtual ~DeclarativeContentApiTest() {}
 
   extensions::ScopedCurrentChannel current_channel_;
+  TestExtensionDir ext_dir_;
 };
 
 IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, Overview) {
+  ext_dir_.WriteManifest(kDeclarativeContentManifest);
+  ext_dir_.WriteFile(
+      FILE_PATH_LITERAL("background.js"),
+      "var declarative = chrome.declarative;\n"
+      "\n"
+      "var PageStateMatcher = chrome.declarativeContent.PageStateMatcher;\n"
+      "var ShowPageAction = chrome.declarativeContent.ShowPageAction;\n"
+      "\n"
+      "var rule0 = {\n"
+      "  conditions: [new PageStateMatcher({pageUrl: {hostPrefix: "
+      "\"test1\"}}),\n"
+      "               new PageStateMatcher({css: "
+      "[\"input[type='password']\"]})],\n"
+      "  actions: [new ShowPageAction()]\n"
+      "}\n"
+      "\n"
+      "var testEvent = chrome.declarativeContent.onPageChanged;\n"
+      "\n"
+      "testEvent.removeRules(undefined, function() {\n"
+      "  testEvent.addRules([rule0], function() {\n"
+      "    chrome.test.sendMessage(\"ready\", function(reply) {\n"
+      "    })\n"
+      "  });\n"
+      "});\n");
   ExtensionTestMessageListener ready("ready", true);
-  const Extension* extension =
-      LoadExtension(test_data_dir_.AppendASCII("declarative_content/overview"));
+  const Extension* extension = LoadExtension(ext_dir_.unpacked_path());
   ASSERT_TRUE(extension);
   const ExtensionAction* page_action =
       ExtensionActionManager::Get(browser()->profile())->
@@ -68,5 +109,63 @@
   EXPECT_FALSE(page_action->GetIsVisible(tab_id));
 }
 
+IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest,
+                       CanonicalizesPageStateMatcherCss) {
+  ext_dir_.WriteManifest(kDeclarativeContentManifest);
+  ext_dir_.WriteFile(
+      FILE_PATH_LITERAL("background.js"),
+      "var declarative = chrome.declarative;\n"
+      "\n"
+      "var PageStateMatcher = chrome.declarativeContent.PageStateMatcher;\n"
+      "function NewPageStateMatcher(obj) {\n"
+      "  return new PageStateMatcher(obj);\n"
+      "}\n"
+      "\n"
+      "chrome.test.runTests([\n"
+      "  function canonicalizesCss() {\n"
+      "    var psm = new PageStateMatcher(\n"
+      "        {css: [\"input[type='password']\"]});\n"
+      "    chrome.test.assertEq(['input[type=\"password\"]'], psm.css);\n"
+      "    chrome.test.succeed();\n"
+      "  },\n"
+      "\n"
+      "  function throwsOnNonArrayCss() {\n"
+      "    chrome.test.assertThrows(NewPageStateMatcher,\n"
+      "                             undefined,\n"
+      "                             [{css: 'Not-an-array'}],\n"
+      "                             /css.*Expected 'array'/);\n"
+      "    chrome.test.succeed();\n"
+      "  },\n"
+      "\n"
+      "  function throwsOnNonStringCss() {\n"
+      "    chrome.test.assertThrows(NewPageStateMatcher,\n"
+      "                             undefined,\n"
+      "                             [{css: [null]}],\n"
+      "                             /css\\.0.*Expected 'string'/);\n"
+      "    chrome.test.succeed();\n"
+      "  },\n"
+      "\n"
+      "  function throwsOnBadCss() {\n"
+      "    chrome.test.assertThrows(NewPageStateMatcher,\n"
+      "                             undefined,\n"
+      "                             [{css: [\"input''\"]}],\n"
+      "                             /valid.*: input''$/);\n"
+      "    chrome.test.succeed();\n"
+      "  },\n"
+      "\n"
+      "  function throwsOnComplexSelector() {\n"
+      "    chrome.test.assertThrows(NewPageStateMatcher,\n"
+      "                             undefined,\n"
+      "                             [{css: [\"div input\"]}],\n"
+      "                             /compound selector.*: div input$/);\n"
+      "    chrome.test.succeed();\n"
+      "  },\n"
+      "]);\n"
+      "\n");
+  ResultCatcher catcher;
+  ASSERT_TRUE(LoadExtension(ext_dir_.unpacked_path()));
+  ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
+}
+
 }  // namespace
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.cc b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
index e1fd58b..c0c4e5b 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
@@ -128,7 +128,6 @@
 namespace Inspect = api::developer_private::Inspect;
 namespace PackDirectory = api::developer_private::PackDirectory;
 namespace Reload = api::developer_private::Reload;
-namespace Restart = api::developer_private::Restart;
 
 DeveloperPrivateAPI* DeveloperPrivateAPI::Get(Profile* profile) {
   return DeveloperPrivateAPIFactory::GetForProfile(profile);
@@ -343,8 +342,11 @@
 
 void DeveloperPrivateGetItemsInfoFunction::
     GetInspectablePagesForExtensionProcess(
+        const Extension* extension,
         const std::set<content::RenderViewHost*>& views,
         ItemInspectViewList* result) {
+  bool has_generated_background_page =
+      BackgroundInfo::HasGeneratedBackgroundPage(extension);
   for (std::set<content::RenderViewHost*>::const_iterator iter = views.begin();
        iter != views.end(); ++iter) {
     content::RenderViewHost* host = *iter;
@@ -356,12 +358,14 @@
       continue;
 
     content::RenderProcessHost* process = host->GetProcess();
-    result->push_back(
-        constructInspectView(web_contents->GetURL(),
-                             process->GetID(),
-                             host->GetRoutingID(),
-                             process->GetBrowserContext()->IsOffTheRecord(),
-                             false));
+    bool is_background_page =
+        (web_contents->GetURL() == BackgroundInfo::GetBackgroundURL(extension));
+    result->push_back(constructInspectView(
+        web_contents->GetURL(),
+        process->GetID(),
+        host->GetRoutingID(),
+        process->GetBrowserContext()->IsOffTheRecord(),
+        is_background_page && has_generated_background_page));
   }
 }
 
@@ -375,17 +379,21 @@
   const ShellWindowRegistry::ShellWindowList windows =
       registry->GetShellWindowsForApp(extension->id());
 
+  bool has_generated_background_page =
+      BackgroundInfo::HasGeneratedBackgroundPage(extension);
   for (ShellWindowRegistry::const_iterator it = windows.begin();
        it != windows.end(); ++it) {
     content::WebContents* web_contents = (*it)->web_contents();
     RenderViewHost* host = web_contents->GetRenderViewHost();
     content::RenderProcessHost* process = host->GetProcess();
-    result->push_back(
-        constructInspectView(web_contents->GetURL(),
-                             process->GetID(),
-                             host->GetRoutingID(),
-                             process->GetBrowserContext()->IsOffTheRecord(),
-                             false));
+    bool is_background_page =
+        (web_contents->GetURL() == BackgroundInfo::GetBackgroundURL(extension));
+    result->push_back(constructInspectView(
+        web_contents->GetURL(),
+        process->GetID(),
+        host->GetRoutingID(),
+        process->GetBrowserContext()->IsOffTheRecord(),
+        is_background_page && has_generated_background_page));
   }
 }
 
@@ -423,6 +431,7 @@
   ExtensionProcessManager* process_manager =
       ExtensionSystem::Get(profile())->process_manager();
   GetInspectablePagesForExtensionProcess(
+      extension,
       process_manager->GetRenderViewHostsForExtension(extension->id()),
       &result);
 
@@ -449,6 +458,7 @@
     process_manager = ExtensionSystem::Get(
         service->profile()->GetOffTheRecordProfile())->process_manager();
     GetInspectablePagesForExtensionProcess(
+        extension,
         process_manager->GetRenderViewHostsForExtension(extension->id()),
         &result);
 
@@ -627,23 +637,6 @@
 DeveloperPrivateShowPermissionsDialogFunction::
     ~DeveloperPrivateShowPermissionsDialogFunction() {}
 
-bool DeveloperPrivateRestartFunction::RunImpl() {
-  scoped_ptr<Restart::Params> params(Restart::Params::Create(*args_));
-  EXTENSION_FUNCTION_VALIDATE(params.get());
-
-  apps::AppLoadService* service = apps::AppLoadService::Get(profile());
-  EXTENSION_FUNCTION_VALIDATE(!params->item_id.empty());
-  ExtensionService* extension_service = profile()->GetExtensionService();
-  // Don't restart disabled applications.
-  if (!extension_service->IsExtensionEnabled(params->item_id))
-    return false;
-
-  service->RestartApplication(params->item_id);
-  return true;
-}
-
-DeveloperPrivateRestartFunction::~DeveloperPrivateRestartFunction() {}
-
 DeveloperPrivateEnableFunction::DeveloperPrivateEnableFunction() {}
 
 bool DeveloperPrivateEnableFunction::RunImpl() {
@@ -1210,7 +1203,6 @@
   SET_STRING("extensionSettingsReloadUnpacked",
              IDS_APPS_DEV_TOOLS_RELOAD_UNPACKED);
   SET_STRING("extensionSettingsLaunch", IDS_EXTENSIONS_LAUNCH);
-  SET_STRING("extensionSettingsRestart", IDS_EXTENSIONS_RESTART);
   SET_STRING("extensionSettingsOptions", IDS_EXTENSIONS_OPTIONS_LINK);
   SET_STRING("extensionSettingsPermissions", IDS_EXTENSIONS_PERMISSIONS_LINK);
   SET_STRING("extensionSettingsVisitWebsite", IDS_EXTENSIONS_VISIT_WEBSITE);
@@ -1237,7 +1229,7 @@
   SET_STRING("extensionSettingsShowLogsButton", IDS_EXTENSIONS_SHOW_LOGS);
   SET_STRING("extensionSettingsMoreDetailsButton", IDS_EXTENSIONS_MORE_DETAILS);
   SET_STRING("extensionSettingsVersion", IDS_EXTENSIONS_VERSION);
-  SET_STRING("extensionSettingsDelete", IDS_EXTENSIONS_DELETE);
+  SET_STRING("extensionSettingsDelete", IDS_EXTENSIONS_ADT_DELETE);
   SET_STRING("extensionSettingsPack", IDS_EXTENSIONS_PACK);
 
 // Pack Extension strings
@@ -1267,12 +1259,29 @@
   SET_STRING("deleteConfirmationMessageExtension",
       IDS_APPS_DEVTOOL_DELETE_CONFIRMATION_MESSAGE_EXTENSION);
 
+// Dialog when profile is managed.
+  SET_STRING("managedProfileDialogCloseButton",
+      IDS_APPS_DEVTOOL_MANAGED_PROFILE_DIALOG_CLOSE_BUTTON);
+  SET_STRING("managedProfileDialogTitle",
+      IDS_APPS_DEVTOOL_MANAGED_PROFILE_DIALOG_TITLE);
+  SET_STRING("managedProfileDialogDescription",
+      IDS_APPS_DEVTOOL_MANAGED_PROFILE_DIALOG_DESCRIPTION);
+
   #undef   SET_STRING
   return true;
 }
 
 DeveloperPrivateGetStringsFunction::~DeveloperPrivateGetStringsFunction() {}
 
+bool DeveloperPrivateIsProfileManagedFunction::RunImpl() {
+  SetResult(new base::FundamentalValue(profile_->IsManaged()));
+  return true;
+}
+
+DeveloperPrivateIsProfileManagedFunction::
+    ~DeveloperPrivateIsProfileManagedFunction() {
+}
+
 } // namespace api
 
 } // namespace extensions
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.h b/chrome/browser/extensions/api/developer_private/developer_private_api.h
index ce86299..ac5dc5c 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api.h
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api.h
@@ -151,6 +151,7 @@
 
   // Helper that lists the current inspectable html pages for the extension.
   void GetInspectablePagesForExtensionProcess(
+      const Extension* extension,
       const std::set<content::RenderViewHost*>& views,
       ItemInspectViewList* result);
 
@@ -241,18 +242,6 @@
 
 };
 
-class DeveloperPrivateRestartFunction : public SyncExtensionFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION("developerPrivate.restart",
-                             DEVELOPERPRIVATE_RESTART);
-
- protected:
-  virtual ~DeveloperPrivateRestartFunction();
-
-  // ExtensionFunction:
-  virtual bool RunImpl() OVERRIDE;
-};
-
 class DeveloperPrivateEnableFunction
     : public SyncExtensionFunction,
       public base::SupportsWeakPtr<DeveloperPrivateEnableFunction> {
@@ -361,6 +350,18 @@
    virtual bool RunImpl() OVERRIDE;
 };
 
+class DeveloperPrivateIsProfileManagedFunction : public SyncExtensionFunction {
+  public:
+   DECLARE_EXTENSION_FUNCTION("developerPrivate.isProfileManaged",
+                              DEVELOPERPRIVATE_ISPROFILEMANAGED);
+
+  protected:
+   virtual ~DeveloperPrivateIsProfileManagedFunction();
+
+   // ExtensionFunction
+   virtual bool RunImpl() OVERRIDE;
+};
+
 class DeveloperPrivateExportSyncfsFolderToLocalfsFunction
     : public AsyncExtensionFunction {
   public:
diff --git a/chrome/browser/extensions/api/dial/dial_api.cc b/chrome/browser/extensions/api/dial/dial_api.cc
index 062fc6f..c018f31 100644
--- a/chrome/browser/extensions/api/dial/dial_api.cc
+++ b/chrome/browser/extensions/api/dial/dial_api.cc
@@ -154,7 +154,7 @@
 bool DialDiscoverNowFunction::Prepare() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(profile());
-  dial_ = DialAPIFactory::GetInstance()->GetForProfile(profile()).get();
+  dial_ = DialAPIFactory::GetForProfile(profile()).get();
   return true;
 }
 
diff --git a/chrome/browser/extensions/api/dial/dial_service.cc b/chrome/browser/extensions/api/dial/dial_service.cc
index 2a2b8c3..605dfdb 100644
--- a/chrome/browser/extensions/api/dial/dial_service.cc
+++ b/chrome/browser/extensions/api/dial/dial_service.cc
@@ -126,8 +126,7 @@
     finish_delay_(TimeDelta::FromMilliseconds((kDialMaxRequests - 1) *
                                               kDialRequestIntervalMillis) +
                   TimeDelta::FromSeconds(kDialResponseTimeoutSecs)),
-    request_interval_(TimeDelta::FromMilliseconds(kDialRequestIntervalMillis))
- {
+    request_interval_(TimeDelta::FromMilliseconds(kDialRequestIntervalMillis)) {
   IPAddressNumber address;
   bool result = net::ParseIPLiteralToNumber(kDialRequestAddress, &address);
   DCHECK(result);
diff --git a/chrome/browser/extensions/api/downloads/downloads_api.cc b/chrome/browser/extensions/api/downloads/downloads_api.cc
index aeeac02..8a0e161 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api.cc
@@ -37,7 +37,7 @@
 #include "chrome/browser/download/download_service_factory.h"
 #include "chrome/browser/download/download_shelf.h"
 #include "chrome/browser/download/download_stats.h"
-#include "chrome/browser/download/download_util.h"
+#include "chrome/browser/download/drag_download_item.h"
 #include "chrome/browser/extensions/event_router.h"
 #include "chrome/browser/extensions/extension_function_dispatcher.h"
 #include "chrome/browser/extensions/extension_info_map.h"
@@ -1245,6 +1245,9 @@
 
 DownloadsAcceptDangerFunction::~DownloadsAcceptDangerFunction() {}
 
+DownloadsAcceptDangerFunction::OnPromptCreatedCallback*
+    DownloadsAcceptDangerFunction::on_prompt_created_ = NULL;
+
 bool DownloadsAcceptDangerFunction::RunImpl() {
   scoped_ptr<downloads::AcceptDanger::Params> params(
       downloads::AcceptDanger::Params::Create(*args_));
@@ -1262,13 +1265,15 @@
   RecordApiFunctions(DOWNLOADS_FUNCTION_ACCEPT_DANGER);
   // DownloadDangerPrompt displays a modal dialog using native widgets that the
   // user must either accept or cancel. It cannot be scripted.
-  DownloadDangerPrompt::Create(
+  DownloadDangerPrompt* prompt = DownloadDangerPrompt::Create(
       download_item,
       web_contents,
       true,
       base::Bind(&DownloadsAcceptDangerFunction::DangerPromptCallback,
                  this, params->download_id));
   // DownloadDangerPrompt deletes itself
+  if (on_prompt_created_ && !on_prompt_created_->is_null())
+    on_prompt_created_->Run(prompt);
   return true;
 }
 
@@ -1370,7 +1375,7 @@
     // Enable nested tasks during DnD, while |DragDownload()| blocks.
     base::MessageLoop::ScopedNestableTaskAllower allow(
         base::MessageLoop::current());
-    download_util::DragDownload(download_item, icon, view);
+    DragDownloadItem(download_item, icon, view);
   }
   return true;
 }
diff --git a/chrome/browser/extensions/api/downloads/downloads_api.h b/chrome/browser/extensions/api/downloads/downloads_api.h
index e7db6d0..fed2a8b 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api.h
+++ b/chrome/browser/extensions/api/downloads/downloads_api.h
@@ -191,6 +191,12 @@
 
 class DownloadsAcceptDangerFunction : public AsyncExtensionFunction {
  public:
+  typedef base::Callback<void(DownloadDangerPrompt*)> OnPromptCreatedCallback;
+  static void OnPromptCreatedForTesting(
+      OnPromptCreatedCallback* callback) {
+    on_prompt_created_ = callback;
+  }
+
   DECLARE_EXTENSION_FUNCTION("downloads.acceptDanger", DOWNLOADS_ACCEPTDANGER)
   DownloadsAcceptDangerFunction();
   virtual bool RunImpl() OVERRIDE;
@@ -201,6 +207,7 @@
                             DownloadDangerPrompt::Action action);
 
  private:
+  static OnPromptCreatedCallback* on_prompt_created_;
   DISALLOW_COPY_AND_ASSIGN(DownloadsAcceptDangerFunction);
 };
 
diff --git a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
index 98c5f48..2c81f39 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/download/download_service_factory.h"
 #include "chrome/browser/download/download_test_file_activity_observer.h"
 #include "chrome/browser/extensions/api/downloads/downloads_api.h"
+#include "chrome/browser/extensions/browser_action_test_util.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_function_test_utils.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -3469,6 +3470,45 @@
 // TODO(benjhayden) Test that the shelf is shown for download() both with and
 // without a WebContents.
 
+void OnDangerPromptCreated(DownloadDangerPrompt* prompt) {
+  prompt->InvokeActionForTesting(DownloadDangerPrompt::ACCEPT);
+}
+
+IN_PROC_BROWSER_TEST_F(DownloadExtensionTest,
+                       DownloadExtensionTest_AcceptDanger) {
+  // Download a file that will be marked dangerous; click the browser action
+  // button; the browser action poup will call acceptDanger(); when the
+  // DownloadDangerPrompt is created, pretend that the user clicks the Accept
+  // button; wait until the download completes.
+  LoadExtension("downloads_split");
+  scoped_ptr<base::Value> result(RunFunctionAndReturnResult(
+      new DownloadsDownloadFunction(),
+      "[{\"url\": \"data:,\", \"filename\": \"dangerous.swf\"}]"));
+  ASSERT_TRUE(result.get());
+  int result_id = -1;
+  ASSERT_TRUE(result->GetAsInteger(&result_id));
+  DownloadItem* item = GetCurrentManager()->GetDownload(result_id);
+  ASSERT_TRUE(item);
+  ASSERT_TRUE(WaitFor(api::OnChanged::kEventName, base::StringPrintf(
+      "[{\"id\": %d, "
+      "  \"danger\": {"
+      "    \"previous\": \"safe\","
+      "    \"current\": \"file\"}}]",
+      result_id)));
+  ASSERT_TRUE(item->IsDangerous());
+  ScopedCancellingItem canceller(item);
+  scoped_ptr<content::DownloadTestObserver> observer(
+      new content::DownloadTestObserverTerminal(
+          GetCurrentManager(), 1,
+          content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_IGNORE));
+  DownloadsAcceptDangerFunction::OnPromptCreatedCallback callback =
+      base::Bind(&OnDangerPromptCreated);
+  DownloadsAcceptDangerFunction::OnPromptCreatedForTesting(
+      &callback);
+  BrowserActionTestUtil(browser()).Press(0);
+  observer->WaitForFinished();
+}
+
 class DownloadsApiTest : public ExtensionApiTest {
  public:
   DownloadsApiTest() {}
diff --git a/chrome/browser/extensions/api/execute_code_function.cc b/chrome/browser/extensions/api/execute_code_function.cc
index 52c70e6..96bfb9d 100644
--- a/chrome/browser/extensions/api/execute_code_function.cc
+++ b/chrome/browser/extensions/api/execute_code_function.cc
@@ -127,7 +127,10 @@
       frame_scope,
       run_at,
       ScriptExecutor::ISOLATED_WORLD,
-      IsWebView(),
+      IsWebView() ? ScriptExecutor::WEB_VIEW_PROCESS
+                  : ScriptExecutor::DEFAULT_PROCESS,
+      has_callback() ? ScriptExecutor::JSON_SERIALIZED_RESULT
+                     : ScriptExecutor::NO_RESULT,
       base::Bind(&ExecuteCodeFunction::OnExecuteCodeFinished, this));
   return true;
 }
diff --git a/chrome/browser/extensions/api/file_handlers/app_file_handler_util.cc b/chrome/browser/extensions/api/file_handlers/app_file_handler_util.cc
index 17888ef..81648f0 100644
--- a/chrome/browser/extensions/api/file_handlers/app_file_handler_util.cc
+++ b/chrome/browser/extensions/api/file_handlers/app_file_handler_util.cc
@@ -4,12 +4,19 @@
 
 #include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
 
+#include "base/file_util.h"
+#include "base/files/file_path.h"
 #include "chrome/browser/extensions/extension_prefs.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/child_process_security_policy.h"
 #include "net/base/mime_util.h"
 #include "webkit/browser/fileapi/isolated_context.h"
 #include "webkit/common/fileapi/file_system_types.h"
 
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/drive/file_system_util.h"
+#endif
+
 namespace extensions {
 
 namespace app_file_handler_util {
@@ -52,6 +59,171 @@
   return false;
 }
 
+bool DoCheckWritableFile(const base::FilePath& path, bool is_directory) {
+  // Don't allow links.
+  if (base::PathExists(path) && file_util::IsLink(path))
+    return false;
+
+  if (is_directory)
+    return base::DirectoryExists(path);
+
+  // Create the file if it doesn't already exist.
+  base::PlatformFileError error = base::PLATFORM_FILE_OK;
+  int creation_flags = base::PLATFORM_FILE_CREATE |
+                       base::PLATFORM_FILE_READ |
+                       base::PLATFORM_FILE_WRITE;
+  base::PlatformFile file = base::CreatePlatformFile(path, creation_flags,
+                                                     NULL, &error);
+  // Close the file so we don't keep a lock open.
+  if (file != base::kInvalidPlatformFileValue)
+    base::ClosePlatformFile(file);
+  if (error != base::PLATFORM_FILE_OK &&
+      error != base::PLATFORM_FILE_ERROR_EXISTS) {
+    return false;
+  }
+
+  return true;
+}
+
+// Checks whether a list of paths are all OK for writing and calls a provided
+// on_success or on_failure callback when done. A file is OK for writing if it
+// is not a symlink, is not in a blacklisted path and can be opened for writing;
+// files are created if they do not exist.
+class WritableFileChecker
+    : public base::RefCountedThreadSafe<WritableFileChecker> {
+ public:
+  WritableFileChecker(
+      const std::vector<base::FilePath>& paths,
+      Profile* profile,
+      bool is_directory,
+      const base::Closure& on_success,
+      const base::Callback<void(const base::FilePath&)>& on_failure);
+
+  void Check();
+
+ private:
+  friend class base::RefCountedThreadSafe<WritableFileChecker>;
+  virtual ~WritableFileChecker();
+
+  // Called when a work item is completed. If all work items are done, this
+  // calls the success or failure callback.
+  void TaskDone();
+
+  // Reports an error in completing a work item. This may be called more than
+  // once, but only the last message will be retained.
+  void Error(const base::FilePath& error_path);
+
+  void CheckLocalWritableFiles();
+
+#if defined(OS_CHROMEOS)
+  void CheckRemoteWritableFile(const base::FilePath& remote_path,
+                               drive::FileError error,
+                               const base::FilePath& local_path);
+#endif
+
+  const std::vector<base::FilePath> paths_;
+  Profile* profile_;
+  const bool is_directory_;
+  int outstanding_tasks_;
+  base::FilePath error_path_;
+  base::Closure on_success_;
+  base::Callback<void(const base::FilePath&)> on_failure_;
+};
+
+WritableFileChecker::WritableFileChecker(
+    const std::vector<base::FilePath>& paths,
+    Profile* profile,
+    bool is_directory,
+    const base::Closure& on_success,
+    const base::Callback<void(const base::FilePath&)>& on_failure)
+    : paths_(paths),
+      profile_(profile),
+      is_directory_(is_directory),
+      outstanding_tasks_(1),
+      on_success_(on_success),
+      on_failure_(on_failure) {}
+
+void WritableFileChecker::Check() {
+#if defined(OS_CHROMEOS)
+  if (drive::util::IsUnderDriveMountPoint(paths_[0])) {
+    outstanding_tasks_ = paths_.size();
+    for (std::vector<base::FilePath>::const_iterator it = paths_.begin();
+         it != paths_.end();
+         ++it) {
+      DCHECK(drive::util::IsUnderDriveMountPoint(*it));
+      drive::util::PrepareWritableFileAndRun(
+          profile_,
+          *it,
+          base::Bind(&WritableFileChecker::CheckRemoteWritableFile, this, *it));
+    }
+    return;
+  }
+#endif
+  content::BrowserThread::PostTask(
+      content::BrowserThread::FILE,
+      FROM_HERE,
+      base::Bind(&WritableFileChecker::CheckLocalWritableFiles, this));
+}
+
+WritableFileChecker::~WritableFileChecker() {}
+
+void WritableFileChecker::TaskDone() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  if (--outstanding_tasks_ == 0) {
+    if (error_path_.empty())
+      on_success_.Run();
+    else
+      on_failure_.Run(error_path_);
+  }
+}
+
+// Reports an error in completing a work item. This may be called more than
+// once, but only the last message will be retained.
+void WritableFileChecker::Error(const base::FilePath& error_path) {
+  DCHECK(!error_path.empty());
+  error_path_ = error_path;
+  TaskDone();
+}
+
+void WritableFileChecker::CheckLocalWritableFiles() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
+  std::string error;
+  for (std::vector<base::FilePath>::const_iterator it = paths_.begin();
+       it != paths_.end();
+       ++it) {
+    if (!DoCheckWritableFile(*it, is_directory_)) {
+      content::BrowserThread::PostTask(
+          content::BrowserThread::UI,
+          FROM_HERE,
+          base::Bind(&WritableFileChecker::Error, this, *it));
+      return;
+    }
+  }
+  content::BrowserThread::PostTask(
+      content::BrowserThread::UI,
+      FROM_HERE,
+      base::Bind(&WritableFileChecker::TaskDone, this));
+}
+
+#if defined(OS_CHROMEOS)
+void WritableFileChecker::CheckRemoteWritableFile(
+    const base::FilePath& remote_path,
+    drive::FileError error,
+    const base::FilePath& /* local_path */) {
+  if (error == drive::FILE_ERROR_OK) {
+    content::BrowserThread::PostTask(
+        content::BrowserThread::UI,
+        FROM_HERE,
+        base::Bind(&WritableFileChecker::TaskDone, this));
+  } else {
+    content::BrowserThread::PostTask(
+        content::BrowserThread::UI,
+        FROM_HERE,
+        base::Bind(&WritableFileChecker::Error, this, remote_path));
+  }
+}
+#endif
+
 }  // namespace
 
 typedef std::vector<FileHandlerInfo> FileHandlerList;
@@ -123,10 +295,10 @@
 
 GrantedFileEntry CreateFileEntry(
     Profile* profile,
-    const std::string& extension_id,
+    const Extension* extension,
     int renderer_id,
     const base::FilePath& path,
-    bool writable) {
+    bool is_directory) {
   GrantedFileEntry result;
   fileapi::IsolatedContext* isolated_context =
       fileapi::IsolatedContext::GetInstance();
@@ -139,13 +311,33 @@
   content::ChildProcessSecurityPolicy* policy =
       content::ChildProcessSecurityPolicy::GetInstance();
   policy->GrantReadFileSystem(renderer_id, result.filesystem_id);
-  if (writable)
+  if (HasFileSystemWritePermission(extension)) {
     policy->GrantWriteFileSystem(renderer_id, result.filesystem_id);
+    if (is_directory)
+      policy->GrantCreateFileForFileSystem(renderer_id, result.filesystem_id);
+  }
 
   result.id = result.filesystem_id + ":" + result.registered_name;
   return result;
 }
 
+void CheckWritableFiles(
+    const std::vector<base::FilePath>& paths,
+    Profile* profile,
+    bool is_directory,
+    const base::Closure& on_success,
+    const base::Callback<void(const base::FilePath&)>& on_failure) {
+  scoped_refptr<WritableFileChecker> checker(new WritableFileChecker(
+      paths, profile, is_directory, on_success, on_failure));
+  checker->Check();
+}
+
+GrantedFileEntry::GrantedFileEntry() {}
+
+bool HasFileSystemWritePermission(const Extension* extension) {
+  return extension->HasAPIPermission(APIPermission::kFileSystemWrite);
+}
+
 }  // namespace app_file_handler_util
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/file_handlers/app_file_handler_util.h b/chrome/browser/extensions/api/file_handlers/app_file_handler_util.h
index 6d9681f..a2ab82b 100644
--- a/chrome/browser/extensions/api/file_handlers/app_file_handler_util.h
+++ b/chrome/browser/extensions/api/file_handlers/app_file_handler_util.h
@@ -49,6 +49,8 @@
 
 // Refers to a file entry that a renderer has been given access to.
 struct GrantedFileEntry {
+  GrantedFileEntry();
+
   std::string id;
   std::string filesystem_id;
   std::string registered_name;
@@ -58,10 +60,20 @@
 // registers a new file system for |path|.
 GrantedFileEntry CreateFileEntry(
     Profile* profile,
-    const std::string& extension_id,
+    const Extension* extension,
     int renderer_id,
     const base::FilePath& path,
-    bool writable);
+    bool is_directory);
+
+void CheckWritableFiles(
+    const std::vector<base::FilePath>& paths,
+    Profile* profile,
+    bool is_directory,
+    const base::Closure& on_success,
+    const base::Callback<void(const base::FilePath&)>& on_failure);
+
+// Returns whether |extension| has the fileSystem.write permission.
+bool HasFileSystemWritePermission(const Extension* extension);
 
 }  // namespace app_file_handler_util
 
diff --git a/chrome/browser/extensions/api/file_system/file_system_api.cc b/chrome/browser/extensions/api/file_system/file_system_api.cc
index 99e761d..4e3de96 100644
--- a/chrome/browser/extensions/api/file_system/file_system_api.cc
+++ b/chrome/browser/extensions/api/file_system/file_system_api.cc
@@ -47,10 +47,6 @@
 #include "base/mac/foundation_util.h"
 #endif
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/drive/file_system_util.h"
-#endif
-
 using apps::SavedFileEntry;
 using apps::SavedFilesService;
 using apps::ShellWindow;
@@ -61,11 +57,11 @@
 const char kInvalidCallingPage[] = "Invalid calling page. This function can't "
     "be called from a background page.";
 const char kUserCancelled[] = "User cancelled";
-const char kWritableFileRestrictedLocationError[] =
-    "Cannot write to file in a restricted location";
 const char kWritableFileErrorFormat[] = "Error opening %s";
 const char kRequiresFileSystemWriteError[] =
     "Operation requires fileSystem.write permission";
+const char kRequiresFileSystemDirectoryError[] =
+    "Operation requires fileSystem.directory permission";
 const char kMultipleUnsupportedError[] =
     "acceptsMultiple: true is not supported for 'saveFile'";
 const char kUnknownIdError[] = "Unknown id";
@@ -75,19 +71,6 @@
 
 namespace {
 
-const int kBlacklistedPaths[] = {
-  chrome::DIR_APP,
-  chrome::DIR_USER_DATA,
-};
-
-#if defined(OS_CHROMEOS)
-// On Chrome OS, the default downloads directory is a subdirectory of user data
-// directory, and should be whitelisted.
-const int kWhitelistedPaths[] = {
-  chrome::DIR_DEFAULT_DOWNLOADS_SAFE,
-};
-#endif
-
 #if defined(OS_MACOSX)
 // Retrieves the localized display name for the base name of the given path.
 // If the path is not localized, this will just return the base name.
@@ -162,14 +145,14 @@
 base::FilePath* g_path_to_be_picked_for_test;
 std::vector<base::FilePath>* g_paths_to_be_picked_for_test;
 
-bool GetFileSystemAndPathOfFileEntry(
+bool ValidateFileEntryAndGetPath(
     const std::string& filesystem_name,
     const std::string& filesystem_path,
     const content::RenderViewHost* render_view_host,
-    std::string* filesystem_id,
     base::FilePath* file_path,
     std::string* error) {
-  if (!fileapi::CrackIsolatedFileSystemName(filesystem_name, filesystem_id)) {
+  std::string filesystem_id;
+  if (!fileapi::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id)) {
     *error = kInvalidParameters;
     return false;
   }
@@ -179,7 +162,7 @@
   content::ChildProcessSecurityPolicy* policy =
       content::ChildProcessSecurityPolicy::GetInstance();
   if (!policy->CanReadFileSystem(render_view_host->GetProcess()->GetID(),
-                                 *filesystem_id)) {
+                                 filesystem_id)) {
     *error = kSecurityError;
     return false;
   }
@@ -187,12 +170,20 @@
   IsolatedContext* context = IsolatedContext::GetInstance();
   base::FilePath relative_path =
       base::FilePath::FromUTF8Unsafe(filesystem_path);
-  base::FilePath virtual_path = context->CreateVirtualRootPath(*filesystem_id)
+  base::FilePath virtual_path = context->CreateVirtualRootPath(filesystem_id)
       .Append(relative_path);
-  if (!context->CrackVirtualPath(virtual_path,
-                                 filesystem_id,
-                                 NULL,
-                                 file_path)) {
+  fileapi::FileSystemType type;
+  if (!context->CrackVirtualPath(
+          virtual_path, &filesystem_id, &type, file_path)) {
+    *error = kInvalidParameters;
+    return false;
+  }
+
+  // The file system API is only intended to operate on file entries that
+  // correspond to a native file, selected by the user so only allow file
+  // systems returned by the file system API or from a drag and drop operation.
+  if (type != fileapi::kFileSystemTypeNativeForPlatformApp &&
+      type != fileapi::kFileSystemTypeDragged) {
     *error = kInvalidParameters;
     return false;
   }
@@ -200,187 +191,6 @@
   return true;
 }
 
-bool GetFilePathOfFileEntry(const std::string& filesystem_name,
-                            const std::string& filesystem_path,
-                            const content::RenderViewHost* render_view_host,
-                            base::FilePath* file_path,
-                            std::string* error) {
-  std::string filesystem_id;
-  return GetFileSystemAndPathOfFileEntry(filesystem_name,
-                                         filesystem_path,
-                                         render_view_host,
-                                         &filesystem_id,
-                                         file_path,
-                                         error);
-}
-
-bool DoCheckWritableFile(const base::FilePath& path,
-                         const base::FilePath& extension_directory,
-                         std::string* error_message) {
-  // Don't allow links.
-  if (base::PathExists(path) && file_util::IsLink(path)) {
-    *error_message = base::StringPrintf(kWritableFileErrorFormat,
-                                        path.BaseName().AsUTF8Unsafe().c_str());
-    return false;
-  }
-
-  if (extension_directory == path || extension_directory.IsParent(path)) {
-    *error_message = kWritableFileRestrictedLocationError;
-    return false;
-  }
-
-  bool is_whitelisted_path = false;
-
-#if defined(OS_CHROMEOS)
-  for (size_t i = 0; i < arraysize(kWhitelistedPaths); i++) {
-    base::FilePath whitelisted_path;
-    if (PathService::Get(kWhitelistedPaths[i], &whitelisted_path) &&
-        (whitelisted_path == path || whitelisted_path.IsParent(path))) {
-      is_whitelisted_path = true;
-      break;
-    }
-  }
-#endif
-
-  if (!is_whitelisted_path) {
-    for (size_t i = 0; i < arraysize(kBlacklistedPaths); i++) {
-      base::FilePath blacklisted_path;
-      if (PathService::Get(kBlacklistedPaths[i], &blacklisted_path) &&
-          (blacklisted_path == path || blacklisted_path.IsParent(path))) {
-        *error_message = kWritableFileRestrictedLocationError;
-        return false;
-      }
-    }
-  }
-
-  // Create the file if it doesn't already exist.
-  base::PlatformFileError error = base::PLATFORM_FILE_OK;
-  int creation_flags = base::PLATFORM_FILE_CREATE |
-                       base::PLATFORM_FILE_READ |
-                       base::PLATFORM_FILE_WRITE;
-  base::PlatformFile file = base::CreatePlatformFile(path, creation_flags,
-                                                     NULL, &error);
-  // Close the file so we don't keep a lock open.
-  if (file != base::kInvalidPlatformFileValue)
-    base::ClosePlatformFile(file);
-  if (error != base::PLATFORM_FILE_OK &&
-      error != base::PLATFORM_FILE_ERROR_EXISTS) {
-    *error_message = base::StringPrintf(kWritableFileErrorFormat,
-                                        path.BaseName().AsUTF8Unsafe().c_str());
-    return false;
-  }
-
-  return true;
-}
-
-// Checks whether a list of paths are all OK for writing and calls a provided
-// on_success or on_failure callback when done. A file is OK for writing if it
-// is not a symlink, is not in a blacklisted path and can be opened for writing;
-// files are created if they do not exist.
-class WritableFileChecker
-    : public base::RefCountedThreadSafe<WritableFileChecker> {
- public:
-  WritableFileChecker(
-      const std::vector<base::FilePath>& paths,
-      Profile* profile,
-      const base::FilePath& extension_path,
-      const base::Closure& on_success,
-      const base::Callback<void(const std::string&)>& on_failure)
-      : outstanding_tasks_(1),
-        extension_path_(extension_path),
-        on_success_(on_success),
-        on_failure_(on_failure) {
-#if defined(OS_CHROMEOS)
-    if (drive::util::IsUnderDriveMountPoint(paths[0])) {
-      outstanding_tasks_ = paths.size();
-      for (std::vector<base::FilePath>::const_iterator it = paths.begin();
-           it != paths.end(); ++it) {
-        DCHECK(drive::util::IsUnderDriveMountPoint(*it));
-        drive::util::PrepareWritableFileAndRun(
-            profile,
-            *it,
-            base::Bind(&WritableFileChecker::CheckRemoteWritableFile, this));
-      }
-      return;
-    }
-#endif
-    content::BrowserThread::PostTask(
-        content::BrowserThread::FILE,
-        FROM_HERE,
-        base::Bind(&WritableFileChecker::CheckLocalWritableFiles, this, paths));
-  }
-
- private:
-  friend class base::RefCountedThreadSafe<WritableFileChecker>;
-  virtual ~WritableFileChecker() {}
-
-  // Called when a work item is completed. If all work items are done, this
-  // calls the success or failure callback.
-  void TaskDone() {
-    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-    if (--outstanding_tasks_ == 0) {
-      if (error_.empty())
-        on_success_.Run();
-      else
-        on_failure_.Run(error_);
-    }
-  }
-
-  // Reports an error in completing a work item. This may be called more than
-  // once, but only the last message will be retained.
-  void Error(const std::string& message) {
-    DCHECK(!message.empty());
-    error_ = message;
-    TaskDone();
-  }
-
-  void CheckLocalWritableFiles(const std::vector<base::FilePath>& paths) {
-    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
-    std::string error;
-    for (std::vector<base::FilePath>::const_iterator it = paths.begin();
-         it != paths.end(); ++it) {
-      if (!DoCheckWritableFile(*it, extension_path_, &error)) {
-        content::BrowserThread::PostTask(
-            content::BrowserThread::UI,
-            FROM_HERE,
-            base::Bind(&WritableFileChecker::Error, this, error));
-        return;
-      }
-    }
-    content::BrowserThread::PostTask(
-        content::BrowserThread::UI,
-        FROM_HERE,
-        base::Bind(&WritableFileChecker::TaskDone, this));
-  }
-
-#if defined(OS_CHROMEOS)
-  void CheckRemoteWritableFile(drive::FileError error,
-                               const base::FilePath& path) {
-    if (error == drive::FILE_ERROR_OK) {
-      content::BrowserThread::PostTask(
-          content::BrowserThread::UI,
-          FROM_HERE,
-          base::Bind(&WritableFileChecker::TaskDone, this));
-    } else {
-      content::BrowserThread::PostTask(
-          content::BrowserThread::UI,
-          FROM_HERE,
-          base::Bind(
-              &WritableFileChecker::Error,
-              this,
-              base::StringPrintf(kWritableFileErrorFormat,
-                                 path.BaseName().AsUTF8Unsafe().c_str())));
-    }
-  }
-#endif
-
-  int outstanding_tasks_;
-  const base::FilePath extension_path_;
-  std::string error_;
-  base::Closure on_success_;
-  base::Callback<void(const std::string&)> on_failure_;
-};
-
 // Expand the mime-types and extensions provided in an AcceptOption, returning
 // them within the passed extension vector. Returns false if no valid types
 // were found.
@@ -485,8 +295,8 @@
   EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
 
   base::FilePath file_path;
-  if (!GetFilePathOfFileEntry(filesystem_name, filesystem_path,
-                              render_view_host_, &file_path, &error_))
+  if (!ValidateFileEntryAndGetPath(filesystem_name, filesystem_path,
+                                   render_view_host_, &file_path, &error_))
     return false;
 
   file_path = PrettifyPath(file_path);
@@ -496,25 +306,19 @@
 
 FileSystemEntryFunction::FileSystemEntryFunction()
     : multiple_(false),
-      entry_type_(READ_ONLY),
+      is_directory_(false),
       response_(NULL) {}
 
-bool FileSystemEntryFunction::HasFileSystemWritePermission() {
-  const extensions::Extension* extension = GetExtension();
-  if (!extension)
-    return false;
-
-  return extension->HasAPIPermission(APIPermission::kFileSystemWrite);
-}
-
 void FileSystemEntryFunction::CheckWritableFiles(
     const std::vector<base::FilePath>& paths) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  scoped_refptr<WritableFileChecker> helper = new WritableFileChecker(
-      paths, profile_, extension_->path(),
-      base::Bind(
-          &FileSystemEntryFunction::RegisterFileSystemsAndSendResponse,
-          this, paths),
+  app_file_handler_util::CheckWritableFiles(
+      paths,
+      profile_,
+      is_directory_,
+      base::Bind(&FileSystemEntryFunction::RegisterFileSystemsAndSendResponse,
+                 this,
+                 paths),
       base::Bind(&FileSystemEntryFunction::HandleWritableFileError, this));
 }
 
@@ -543,14 +347,13 @@
     const base::FilePath& path,
     const std::string& id_override) {
   DCHECK(response_);
-  bool writable = entry_type_ == WRITABLE;
   extensions::app_file_handler_util::GrantedFileEntry file_entry =
       extensions::app_file_handler_util::CreateFileEntry(
           profile(),
-          GetExtension()->id(),
+          GetExtension(),
           render_view_host_->GetProcess()->GetID(),
           path,
-          writable);
+          is_directory_);
   base::ListValue* entries;
   bool success = response_->GetList("entries", &entries);
   DCHECK(success);
@@ -562,13 +365,15 @@
     entry->SetString("id", file_entry.id);
   else
     entry->SetString("id", id_override);
+  entry->SetBoolean("isDirectory", is_directory_);
   entries->Append(entry);
 }
 
 void FileSystemEntryFunction::HandleWritableFileError(
-    const std::string& error) {
+    const base::FilePath& error_path) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  error_ = error;
+  error_ = base::StringPrintf(kWritableFileErrorFormat,
+                              error_path.BaseName().AsUTF8Unsafe().c_str());
   SendResponse(false);
 }
 
@@ -578,23 +383,46 @@
   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
   EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
 
-  if (!HasFileSystemWritePermission()) {
+  if (!app_file_handler_util::HasFileSystemWritePermission(extension_)) {
     error_ = kRequiresFileSystemWriteError;
     return false;
   }
-  entry_type_ = WRITABLE;
 
-  base::FilePath path;
-  if (!GetFilePathOfFileEntry(filesystem_name, filesystem_path,
-                              render_view_host_, &path, &error_))
+  if (!ValidateFileEntryAndGetPath(filesystem_name, filesystem_path,
+                                   render_view_host_, &path_, &error_))
     return false;
 
-  std::vector<base::FilePath> paths;
-  paths.push_back(path);
-  CheckWritableFiles(paths);
+  content::BrowserThread::PostTaskAndReply(
+      content::BrowserThread::FILE,
+      FROM_HERE,
+      base::Bind(
+          &FileSystemGetWritableEntryFunction::SetIsDirectoryOnFileThread,
+          this),
+      base::Bind(
+          &FileSystemGetWritableEntryFunction::CheckPermissionAndSendResponse,
+          this));
   return true;
 }
 
+void FileSystemGetWritableEntryFunction::CheckPermissionAndSendResponse() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  if (is_directory_ &&
+      !extension_->HasAPIPermission(APIPermission::kFileSystemDirectory)) {
+    error_ = kRequiresFileSystemDirectoryError;
+    SendResponse(false);
+  }
+  std::vector<base::FilePath> paths;
+  paths.push_back(path_);
+  CheckWritableFiles(paths);
+}
+
+void FileSystemGetWritableEntryFunction::SetIsDirectoryOnFileThread() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
+  if (base::DirectoryExists(path_)) {
+    is_directory_ = true;
+  }
+}
+
 bool FileSystemIsWritableEntryFunction::RunImpl() {
   std::string filesystem_name;
   std::string filesystem_path;
@@ -829,9 +657,16 @@
 void FileSystemChooseEntryFunction::FilesSelected(
     const std::vector<base::FilePath>& paths) {
   DCHECK(!paths.empty());
-  file_system_api::SetLastChooseEntryDirectory(
-      ExtensionPrefs::Get(profile()), GetExtension()->id(), paths[0].DirName());
-  if (entry_type_ == WRITABLE) {
+  base::FilePath last_choose_directory;
+  if (is_directory_) {
+    last_choose_directory = paths[0];
+  } else {
+    last_choose_directory = paths[0].DirName();
+  }
+  file_system_api::SetLastChooseEntryDirectory(ExtensionPrefs::Get(profile()),
+                                               GetExtension()->id(),
+                                               last_choose_directory);
+  if (app_file_handler_util::HasFileSystemWritePermission(extension_)) {
     CheckWritableFiles(paths);
     return;
   }
@@ -919,15 +754,32 @@
     multiple_ = options->accepts_multiple;
     if (multiple_)
       picker_type = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
-    if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENWRITABLEFILE) {
-      entry_type_ = WRITABLE;
+
+    if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENWRITABLEFILE &&
+        !app_file_handler_util::HasFileSystemWritePermission(extension_)) {
+      error_ = kRequiresFileSystemWriteError;
+      return false;
     } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_SAVEFILE) {
+      if (!app_file_handler_util::HasFileSystemWritePermission(extension_)) {
+        error_ = kRequiresFileSystemWriteError;
+        return false;
+      }
       if (multiple_) {
         error_ = kMultipleUnsupportedError;
         return false;
       }
-      entry_type_ = WRITABLE;
       picker_type = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
+    } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENDIRECTORY) {
+      is_directory_ = true;
+      if (!extension_->HasAPIPermission(APIPermission::kFileSystemDirectory)) {
+        error_ = kRequiresFileSystemDirectoryError;
+        return false;
+      }
+      if (multiple_) {
+        error_ = kMultipleUnsupportedError;
+        return false;
+      }
+      picker_type = ui::SelectFileDialog::SELECT_FOLDER;
     }
 
     base::FilePath::StringType suggested_extension;
@@ -938,11 +790,6 @@
         options->accepts.get(), options->accepts_all_types.get());
   }
 
-  if (entry_type_ == WRITABLE && !HasFileSystemWritePermission()) {
-    error_ = kRequiresFileSystemWriteError;
-    return false;
-  }
-
   file_type_info.support_drive = true;
 
   base::FilePath previous_path;
@@ -968,38 +815,47 @@
   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id));
   SavedFilesService* saved_files_service = SavedFilesService::Get(profile());
   // Add the file to the retain list if it is not already on there.
-  if (!saved_files_service->IsRegistered(extension_->id(), entry_id) &&
-      !RetainFileEntry(entry_id)) {
-    return false;
+  if (!saved_files_service->IsRegistered(extension_->id(), entry_id)) {
+    std::string filesystem_name;
+    std::string filesystem_path;
+    EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_name));
+    EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &filesystem_path));
+    if (!ValidateFileEntryAndGetPath(filesystem_name,
+                                     filesystem_path,
+                                     render_view_host_,
+                                     &path_,
+                                     &error_)) {
+      return false;
+    }
+
+    content::BrowserThread::PostTaskAndReply(
+        content::BrowserThread::FILE,
+        FROM_HERE,
+        base::Bind(&FileSystemRetainEntryFunction::SetIsDirectoryOnFileThread,
+                   this),
+        base::Bind(
+            &FileSystemRetainEntryFunction::RetainFileEntry, this, entry_id));
+    return true;
   }
+
   saved_files_service->EnqueueFileEntry(extension_->id(), entry_id);
+  SendResponse(true);
   return true;
 }
 
-bool FileSystemRetainEntryFunction::RetainFileEntry(
+void FileSystemRetainEntryFunction::RetainFileEntry(
     const std::string& entry_id) {
-  std::string filesystem_name;
-  std::string filesystem_path;
-  EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_name));
-  EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &filesystem_path));
-  std::string filesystem_id;
-  base::FilePath path;
-  if (!GetFileSystemAndPathOfFileEntry(filesystem_name,
-                                       filesystem_path,
-                                       render_view_host_,
-                                       &filesystem_id,
-                                       &path,
-                                       &error_)) {
-    return false;
-  }
+  SavedFilesService* saved_files_service = SavedFilesService::Get(profile());
+  saved_files_service->RegisterFileEntry(
+      extension_->id(), entry_id, path_, is_directory_);
+  saved_files_service->EnqueueFileEntry(extension_->id(), entry_id);
+  SendResponse(true);
+}
 
-  content::ChildProcessSecurityPolicy* policy =
-      content::ChildProcessSecurityPolicy::GetInstance();
-  bool is_writable = policy->CanReadWriteFileSystem(
-      render_view_host_->GetProcess()->GetID(), filesystem_id);
-  SavedFilesService::Get(profile())->RegisterFileEntry(
-      extension_->id(), entry_id, path, is_writable);
-  return true;
+void FileSystemRetainEntryFunction::SetIsDirectoryOnFileThread() {
+  if (base::DirectoryExists(path_)) {
+    is_directory_ = true;
+  }
 }
 
 bool FileSystemIsRestorableFunction::RunImpl() {
@@ -1029,7 +885,7 @@
   // |needs_new_entry| will be false if the renderer already has an Entry for
   // |entry_id|.
   if (needs_new_entry) {
-    entry_type_ = file_entry->writable ? WRITABLE : READ_ONLY;
+    is_directory_ = file_entry->is_directory;
     CreateResponse();
     AddEntryToResponse(file_entry->path, file_entry->id);
   }
diff --git a/chrome/browser/extensions/api/file_system/file_system_api.h b/chrome/browser/extensions/api/file_system/file_system_api.h
index cf46329..2a65fa5 100644
--- a/chrome/browser/extensions/api/file_system/file_system_api.h
+++ b/chrome/browser/extensions/api/file_system/file_system_api.h
@@ -45,17 +45,10 @@
 
 class FileSystemEntryFunction : public AsyncExtensionFunction {
  protected:
-  enum EntryType {
-    READ_ONLY,
-    WRITABLE
-  };
-
   FileSystemEntryFunction();
 
   virtual ~FileSystemEntryFunction() {}
 
-  bool HasFileSystemWritePermission();
-
   // This is called when writable file entries are being returned. The function
   // will ensure the files exist, creating them if necessary, and also check
   // that none of the files are links. If it succeeds it proceeds to
@@ -76,13 +69,13 @@
                           const std::string& id_override);
 
   // called on the UI thread if there is a problem checking a writable file.
-  void HandleWritableFileError(const std::string& error);
+  void HandleWritableFileError(const base::FilePath& error_path);
 
   // Whether multiple entries have been requested.
   bool multiple_;
 
-  // The type of the entry or entries to return.
-  EntryType entry_type_;
+  // Whether a directory has been requested.
+  bool is_directory_;
 
   // The dictionary to send as the response.
   base::DictionaryValue* response_;
@@ -96,6 +89,13 @@
  protected:
   virtual ~FileSystemGetWritableEntryFunction() {}
   virtual bool RunImpl() OVERRIDE;
+
+ private:
+  void CheckPermissionAndSendResponse();
+  void SetIsDirectoryOnFileThread();
+
+  // The path to the file for which a writable entry has been requested.
+  base::FilePath path_;
 };
 
 class FileSystemIsWritableEntryFunction : public SyncExtensionFunction {
@@ -156,7 +156,7 @@
   base::FilePath initial_path_;
 };
 
-class FileSystemRetainEntryFunction : public SyncExtensionFunction {
+class FileSystemRetainEntryFunction : public AsyncExtensionFunction {
  public:
   DECLARE_EXTENSION_FUNCTION("fileSystem.retainEntry", FILESYSTEM_RETAINENTRY)
 
@@ -167,7 +167,15 @@
  private:
   // Retains the file entry referenced by |entry_id| in apps::SavedFilesService.
   // |entry_id| must refer to an entry in an isolated file system.
-  bool RetainFileEntry(const std::string& entry_id);
+  void RetainFileEntry(const std::string& entry_id);
+
+  void SetIsDirectoryOnFileThread();
+
+  // Whether the file being retained is a directory.
+  bool is_directory_;
+
+  // The path to the file to retain.
+  base::FilePath path_;
 };
 
 class FileSystemIsRestorableFunction : public SyncExtensionFunction {
diff --git a/chrome/browser/extensions/api/file_system/file_system_apitest.cc b/chrome/browser/extensions/api/file_system/file_system_apitest.cc
index 90c90bf..b26f530 100644
--- a/chrome/browser/extensions/api/file_system/file_system_apitest.cc
+++ b/chrome/browser/extensions/api/file_system/file_system_apitest.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/extensions/api/file_system/file_system_api.h"
 #include "chrome/browser/extensions/extension_prefs.h"
 #include "chrome/common/chrome_paths.h"
+#include "chrome/common/extensions/features/feature_channel.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_service.h"
 
@@ -56,10 +57,11 @@
 }
 
 void AddSavedEntry(const base::FilePath& path_to_save,
+                   bool is_directory,
                    apps::SavedFilesService* service,
                    const Extension* extension) {
   service->RegisterFileEntry(
-      extension->id(), "magic id", path_to_save, /* writable */ true);
+      extension->id(), "magic id", path_to_save, is_directory);
 }
 
 }  // namespace
@@ -74,6 +76,10 @@
         "test_root", test_root_folder_);
   }
 
+  virtual void SetUpOnMainThread() OVERRIDE {
+    ClearCommandLineArgs();
+  }
+
   virtual void TearDown() OVERRIDE {
     FileSystemChooseEntryFunction::StopSkippingPickerForTest();
     PlatformAppBrowserTest::TearDown();
@@ -271,6 +277,58 @@
       << message_;
 }
 
+IN_PROC_BROWSER_TEST_F(FileSystemApiTest, FileSystemApiOpenDirectoryTest) {
+  ScopedCurrentChannel channel(chrome::VersionInfo::CHANNEL_UNKNOWN);
+  base::FilePath test_file = TempFilePath("open_existing.txt", true);
+  ASSERT_FALSE(test_file.empty());
+  base::FilePath test_directory = test_file.DirName();
+  FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
+      &test_directory);
+  ASSERT_TRUE(RunPlatformAppTest("api_test/file_system/open_directory"))
+      << message_;
+  CheckStoredDirectoryMatches(test_file);
+}
+
+IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
+                       FileSystemApiOpenDirectoryWithWriteTest) {
+  ScopedCurrentChannel channel(chrome::VersionInfo::CHANNEL_UNKNOWN);
+  base::FilePath test_file = TempFilePath("open_existing.txt", true);
+  ASSERT_FALSE(test_file.empty());
+  base::FilePath test_directory = test_file.DirName();
+  FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
+      &test_directory);
+  ASSERT_TRUE(
+      RunPlatformAppTest("api_test/file_system/open_directory_with_write"))
+      << message_;
+  CheckStoredDirectoryMatches(test_file);
+}
+
+IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
+                       FileSystemApiOpenDirectoryWithoutPermissionTest) {
+  base::FilePath test_file = TempFilePath("open_existing.txt", true);
+  ASSERT_FALSE(test_file.empty());
+  base::FilePath test_directory = test_file.DirName();
+  FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
+      &test_directory);
+  ASSERT_TRUE(RunPlatformAppTest(
+      "api_test/file_system/open_directory_without_permission"))
+      << message_;
+  CheckStoredDirectoryMatches(base::FilePath());
+}
+
+IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
+                       FileSystemApiOpenDirectoryWithOnlyWritePermissionTest) {
+  base::FilePath test_file = TempFilePath("open_existing.txt", true);
+  ASSERT_FALSE(test_file.empty());
+  base::FilePath test_directory = test_file.DirName();
+  FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
+      &test_directory);
+  ASSERT_TRUE(RunPlatformAppTest(
+      "api_test/file_system/open_directory_with_only_write"))
+      << message_;
+  CheckStoredDirectoryMatches(base::FilePath());
+}
+
 IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
     FileSystemApiInvalidChooseEntryTypeTest) {
   base::FilePath test_file = TempFilePath("open_existing.txt", true);
@@ -431,54 +489,24 @@
       "api_test/file_system/get_writable_file_entry_with_write")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
-                       FileSystemApiGetWritableInUserDataDirTest) {
-  base::FilePath test_file =
-      base::MakeAbsoluteFilePath(TempFilePath("test.js", true));
-  ASSERT_FALSE(test_file.empty());
-  FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
-      &test_file);
-  ASSERT_TRUE(PathService::OverrideAndCreateIfNeeded(
-      chrome::DIR_USER_DATA, test_file.DirName(), false));
-  ASSERT_TRUE(RunPlatformAppTest(
-      "api_test/file_system/get_writable_file_entry_non_writable_file"))
-      << message_;
-}
-
-IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
-                       FileSystemApiGetWritableInChromeDirTest) {
-  base::FilePath test_file =
-      base::MakeAbsoluteFilePath(TempFilePath("test.js", true));
-  ASSERT_FALSE(test_file.empty());
-  FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
-      &test_file);
-  ASSERT_TRUE(PathService::OverrideAndCreateIfNeeded(
-      chrome::DIR_APP, test_file.DirName(), false));
-  ASSERT_TRUE(RunPlatformAppTest(
-      "api_test/file_system/get_writable_file_entry_non_writable_file"))
-      << message_;
-}
-
-IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
-                       FileSystemApiGetWritableInAppDirectory) {
-  FileSystemChooseEntryFunction::SkipPickerAndSelectSuggestedPathForTest();
-  {
-    AppInstallObserver observer(
-        base::Bind(SetLastChooseEntryDirectoryToAppDirectory,
-                   ExtensionPrefs::Get(profile())));
-    ASSERT_TRUE(RunPlatformAppTest(
-        "api_test/file_system/get_writable_file_entry_non_writable_file"))
-        << message_;
-  }
-}
-
 IN_PROC_BROWSER_TEST_F(FileSystemApiTest, FileSystemApiIsWritableTest) {
   base::FilePath test_file = TempFilePath("writable.txt", true);
   ASSERT_FALSE(test_file.empty());
   FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
       &test_file);
+  ASSERT_TRUE(RunPlatformAppTest("api_test/file_system/is_writable_file_entry"))
+      << message_;
+}
+
+IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
+                       FileSystemApiIsWritableWithWritePermissionTest) {
+  base::FilePath test_file = TempFilePath("writable.txt", true);
+  ASSERT_FALSE(test_file.empty());
+  FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
+      &test_file);
   ASSERT_TRUE(RunPlatformAppTest(
-      "api_test/file_system/is_writable_file_entry")) << message_;
+      "api_test/file_system/is_writable_file_entry_with_write"))
+      << message_;
 }
 
 IN_PROC_BROWSER_TEST_F(FileSystemApiTest, FileSystemApiRetainEntry) {
@@ -493,7 +521,23 @@
   ASSERT_EQ(1u, file_entries.size());
   EXPECT_EQ(test_file, file_entries[0].path);
   EXPECT_EQ(1, file_entries[0].sequence_number);
-  EXPECT_FALSE(file_entries[0].writable);
+}
+
+IN_PROC_BROWSER_TEST_F(FileSystemApiTest, FileSystemApiRetainDirectoryEntry) {
+  ScopedCurrentChannel channel(chrome::VersionInfo::CHANNEL_UNKNOWN);
+  base::FilePath test_file = TempFilePath("open_existing.txt", true);
+  ASSERT_FALSE(test_file.empty());
+  base::FilePath test_directory = test_file.DirName();
+  FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
+      &test_directory);
+  ASSERT_TRUE(RunPlatformAppTest("api_test/file_system/retain_directory"))
+      << message_;
+  std::vector<apps::SavedFileEntry> file_entries = apps::SavedFilesService::Get(
+      profile())->GetAllFileEntries(GetSingleLoadedExtension()->id());
+  ASSERT_EQ(1u, file_entries.size());
+  EXPECT_EQ(test_directory, file_entries[0].path);
+  EXPECT_EQ(1, file_entries[0].sequence_number);
+  EXPECT_TRUE(file_entries[0].is_directory);
 }
 
 IN_PROC_BROWSER_TEST_F(FileSystemApiTest, FileSystemApiRestoreEntry) {
@@ -501,81 +545,29 @@
   ASSERT_FALSE(test_file.empty());
   FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
       &test_file);
-  {
-    AppInstallObserver observer(base::Bind(
-        AddSavedEntry, test_file, apps::SavedFilesService::Get(profile())));
-    ASSERT_TRUE(RunPlatformAppTest(
-        "api_test/file_system/restore_entry")) << message_;
-  }
-}
-
-IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
-                       FileSystemApiOpenNonWritableFileForRead) {
-  base::FilePath test_file = TempFilePath("open_existing.txt", true);
-  ASSERT_FALSE(test_file.empty());
-  ASSERT_TRUE(PathService::OverrideAndCreateIfNeeded(
-      chrome::DIR_USER_DATA, test_file.DirName(), false));
-  FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
-      &test_file);
-  ASSERT_TRUE(RunPlatformAppTest("api_test/file_system/open_existing"))
+  AppInstallObserver observer(
+      base::Bind(AddSavedEntry,
+                  test_file,
+                  false,
+                  apps::SavedFilesService::Get(profile())));
+  ASSERT_TRUE(RunPlatformAppTest("api_test/file_system/restore_entry"))
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
-                       FileSystemApiOpenInUserDataDirForWrite) {
-  base::FilePath test_file =
-      base::MakeAbsoluteFilePath(TempFilePath("open_existing.txt", true));
+IN_PROC_BROWSER_TEST_F(FileSystemApiTest, FileSystemApiRestoreDirectoryEntry) {
+  ScopedCurrentChannel channel(chrome::VersionInfo::CHANNEL_UNKNOWN);
+  base::FilePath test_file = TempFilePath("writable.txt", true);
   ASSERT_FALSE(test_file.empty());
-  ASSERT_TRUE(PathService::OverrideAndCreateIfNeeded(
-      chrome::DIR_USER_DATA, test_file.DirName(), false));
+  base::FilePath test_directory = test_file.DirName();
   FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
       &test_file);
-  ASSERT_TRUE(RunPlatformAppTest(
-      "api_test/file_system/open_writable_existing_non_writable")) << message_;
-}
-
-#if defined(OS_CHROMEOS)
-// In Chrome OS the download directory is whitelisted for write.
-IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
-                       FileSystemApiOpenInDownloadDirForWrite) {
-  base::FilePath test_file =
-      base::MakeAbsoluteFilePath(TempFilePath("writable.txt", true));
-  ASSERT_FALSE(test_file.empty());
-  ASSERT_TRUE(PathService::OverrideAndCreateIfNeeded(
-      chrome::DIR_USER_DATA, test_file.DirName(), false));
-  ASSERT_TRUE(PathService::OverrideAndCreateIfNeeded(
-      chrome::DIR_DEFAULT_DOWNLOADS_SAFE, test_file.DirName(), false));
-  FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
-      &test_file);
-  ASSERT_TRUE(RunPlatformAppTest(
-      "api_test/file_system/is_writable_file_entry")) << message_;
-}
-#endif
-
-IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
-                       FileSystemApiOpenInChromeDirForWrite) {
-  base::FilePath test_file =
-      base::MakeAbsoluteFilePath(TempFilePath("open_existing.txt", true));
-  ASSERT_FALSE(test_file.empty());
-  ASSERT_TRUE(PathService::OverrideAndCreateIfNeeded(
-      chrome::DIR_APP, test_file.DirName(), false));
-  FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
-      &test_file);
-  ASSERT_TRUE(RunPlatformAppTest(
-      "api_test/file_system/open_writable_existing_non_writable")) << message_;
-}
-
-IN_PROC_BROWSER_TEST_F(FileSystemApiTest,
-                       FileSystemApiOpenInAppDirectoryForWrite) {
-  FileSystemChooseEntryFunction::SkipPickerAndSelectSuggestedPathForTest();
-  {
-    AppInstallObserver observer(
-        base::Bind(SetLastChooseEntryDirectoryToAppDirectory,
-                   ExtensionPrefs::Get(profile())));
-    ASSERT_TRUE(RunPlatformAppTest(
-        "api_test/file_system/open_writable_existing_non_writable"))
-        << message_;
-  }
+  AppInstallObserver observer(
+      base::Bind(AddSavedEntry,
+                 test_directory,
+                 true,
+                 apps::SavedFilesService::Get(profile())));
+  ASSERT_TRUE(RunPlatformAppTest("api_test/file_system/restore_directory"))
+      << message_;
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/identity/experimental_identity_api.h b/chrome/browser/extensions/api/identity/experimental_identity_api.h
index 3dec6a7..af57d18 100644
--- a/chrome/browser/extensions/api/identity/experimental_identity_api.h
+++ b/chrome/browser/extensions/api/identity/experimental_identity_api.h
@@ -15,8 +15,8 @@
 #include "chrome/browser/extensions/api/identity/identity_signin_flow.h"
 #include "chrome/browser/extensions/extension_function.h"
 #include "chrome/browser/extensions/extension_install_prompt.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "google_apis/gaia/oauth2_mint_token_flow.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 
 namespace extensions {
 
diff --git a/chrome/browser/extensions/api/identity/identity_api.h b/chrome/browser/extensions/api/identity/identity_api.h
index b46668d..96bbc19 100644
--- a/chrome/browser/extensions/api/identity/identity_api.h
+++ b/chrome/browser/extensions/api/identity/identity_api.h
@@ -19,9 +19,9 @@
 #include "chrome/browser/extensions/api/identity/web_auth_flow.h"
 #include "chrome/browser/extensions/api/profile_keyed_api_factory.h"
 #include "chrome/browser/extensions/extension_function.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "chrome/browser/signin/signin_global_error.h"
 #include "google_apis/gaia/oauth2_mint_token_flow.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 
 class GoogleServiceAuthError;
 class MockGetAuthTokenFunction;
diff --git a/chrome/browser/extensions/api/identity/identity_signin_flow.h b/chrome/browser/extensions/api/identity/identity_signin_flow.h
index cb1eb74..207e6b5 100644
--- a/chrome/browser/extensions/api/identity/identity_signin_flow.h
+++ b/chrome/browser/extensions/api/identity/identity_signin_flow.h
@@ -8,7 +8,7 @@
 #include <string>
 
 #include "base/memory/scoped_ptr.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 
 class Profile;
 
diff --git a/chrome/browser/extensions/api/input_ime/input_ime_api.cc b/chrome/browser/extensions/api/input_ime/input_ime_api.cc
index 15baf4b..53b8ec2 100644
--- a/chrome/browser/extensions/api/input_ime/input_ime_api.cc
+++ b/chrome/browser/extensions/api/input_ime/input_ime_api.cc
@@ -95,8 +95,7 @@
     if (profile_ == NULL || extension_id_.empty())
       return;
 
-    scoped_ptr<base::ListValue> args(new base::ListValue());
-    args->Append(new base::StringValue(engine_id));
+    scoped_ptr<base::ListValue> args(input_ime::OnActivate::Create(engine_id));
 
     DispatchEventToExtension(profile_, extension_id_,
                              input_ime::OnActivate::kEventName, args.Pass());
@@ -106,8 +105,8 @@
     if (profile_ == NULL || extension_id_.empty())
       return;
 
-    scoped_ptr<base::ListValue> args(new base::ListValue());
-    args->Append(new base::StringValue(engine_id));
+    scoped_ptr<base::ListValue> args(
+        input_ime::OnDeactivated::Create(engine_id));
 
     DispatchEventToExtension(profile_, extension_id_,
                              input_ime::OnDeactivated::kEventName, args.Pass());
@@ -118,12 +117,11 @@
     if (profile_ == NULL || extension_id_.empty())
       return;
 
-    base::DictionaryValue* dict = new base::DictionaryValue();
-    dict->SetInteger("contextID", context.id);
-    dict->SetString("type", context.type);
+    input_ime::InputContext context_value;
+    context_value.context_id = context.id;
+    context_value.type = input_ime::InputContext::ParseType(context.type);
 
-    scoped_ptr<base::ListValue> args(new base::ListValue());
-    args->Append(dict);
+    scoped_ptr<base::ListValue> args(input_ime::OnFocus::Create(context_value));
 
     DispatchEventToExtension(profile_, extension_id_,
                              input_ime::OnFocus::kEventName, args.Pass());
@@ -133,8 +131,7 @@
     if (profile_ == NULL || extension_id_.empty())
       return;
 
-    scoped_ptr<base::ListValue> args(new base::ListValue());
-    args->Append(new base::FundamentalValue(context_id));
+    scoped_ptr<base::ListValue> args(input_ime::OnBlur::Create(context_id));
 
     DispatchEventToExtension(profile_, extension_id_,
                              input_ime::OnBlur::kEventName, args.Pass());
@@ -145,12 +142,12 @@
     if (profile_ == NULL || extension_id_.empty())
       return;
 
-    base::DictionaryValue* dict = new base::DictionaryValue();
-    dict->SetInteger("contextID", context.id);
-    dict->SetString("type", context.type);
+    input_ime::InputContext context_value;
+    context_value.context_id = context.id;
+    context_value.type = input_ime::InputContext::ParseType(context.type);
 
-    scoped_ptr<base::ListValue> args(new base::ListValue());
-    args->Append(dict);
+    scoped_ptr<base::ListValue> args(
+        input_ime::OnInputContextUpdate::Create(context_value));
 
     DispatchEventToExtension(profile_,
                              extension_id_,
@@ -169,19 +166,18 @@
         extensions::InputImeEventRouter::GetInstance()->AddRequest(engine_id,
                                                                    key_data);
 
-    base::DictionaryValue* dict = new base::DictionaryValue();
-    dict->SetString("type", event.type);
-    dict->SetString("requestId", request_id);
-    dict->SetString("key", event.key);
-    dict->SetString("code", event.code);
-    dict->SetBoolean("altKey", event.alt_key);
-    dict->SetBoolean("ctrlKey", event.ctrl_key);
-    dict->SetBoolean("shiftKey", event.shift_key);
-    dict->SetBoolean("capsLock", event.caps_lock);
+    input_ime::KeyboardEvent key_data_value;
+    key_data_value.type = input_ime::KeyboardEvent::ParseType(event.type);
+    key_data_value.request_id = request_id;
+    key_data_value.key = event.key;
+    key_data_value.code = event.code;
+    key_data_value.alt_key.reset(new bool(event.alt_key));
+    key_data_value.ctrl_key.reset(new bool(event.ctrl_key));
+    key_data_value.shift_key.reset(new bool(event.shift_key));
+    key_data_value.caps_lock.reset(new bool(event.caps_lock));
 
-    scoped_ptr<base::ListValue> args(new base::ListValue());
-    args->Append(new base::StringValue(engine_id));
-    args->Append(dict);
+    scoped_ptr<base::ListValue> args(
+        input_ime::OnKeyEvent::Create(engine_id, key_data_value));
 
     DispatchEventToExtension(profile_, extension_id_,
                              input_ime::OnKeyEvent::kEventName, args.Pass());
@@ -194,25 +190,29 @@
     if (profile_ == NULL || extension_id_.empty())
       return;
 
-    scoped_ptr<base::ListValue> args(new base::ListValue());
-    args->Append(new base::StringValue(engine_id));
-    args->Append(new base::FundamentalValue(candidate_id));
+    input_ime::OnCandidateClicked::Button button_enum =
+        input_ime::OnCandidateClicked::BUTTON_NONE;
     switch (button) {
       case chromeos::InputMethodEngine::MOUSE_BUTTON_MIDDLE:
-        args->Append(new base::StringValue("middle"));
+        button_enum = input_ime::OnCandidateClicked::BUTTON_MIDDLE;
         break;
 
       case chromeos::InputMethodEngine::MOUSE_BUTTON_RIGHT:
-        args->Append(new base::StringValue("right"));
+        button_enum = input_ime::OnCandidateClicked::BUTTON_RIGHT;
         break;
 
       case chromeos::InputMethodEngine::MOUSE_BUTTON_LEFT:
       // Default to left.
       default:
-        args->Append(new base::StringValue("left"));
+        button_enum = input_ime::OnCandidateClicked::BUTTON_LEFT;
         break;
     }
 
+    scoped_ptr<base::ListValue> args(
+        input_ime::OnCandidateClicked::Create(engine_id,
+                                              candidate_id,
+                                              button_enum));
+
     DispatchEventToExtension(profile_,
                              extension_id_,
                              input_ime::OnCandidateClicked::kEventName,
@@ -224,9 +224,8 @@
     if (profile_ == NULL || extension_id_.empty())
       return;
 
-    scoped_ptr<base::ListValue> args(new base::ListValue());
-    args->Append(new base::StringValue(engine_id));
-    args->Append(new base::StringValue(menu_id));
+    scoped_ptr<base::ListValue> args(
+        input_ime::OnMenuItemActivated::Create(engine_id, menu_id));
 
     DispatchEventToExtension(profile_,
                              extension_id_,
@@ -240,14 +239,13 @@
                                         int anchor_pos) OVERRIDE {
     if (profile_ == NULL || extension_id_.empty())
       return;
-    base::DictionaryValue* dict = new base::DictionaryValue();
-    dict->SetString("text", text);
-    dict->SetInteger("focus", cursor_pos);
-    dict->SetInteger("anchor", anchor_pos);
 
-    scoped_ptr<ListValue> args(new base::ListValue);
-    args->Append(new base::StringValue(engine_id));
-    args->Append(dict);
+    input_ime::OnSurroundingTextChanged::SurroundingInfo info;
+    info.text = text;
+    info.focus = cursor_pos;
+    info.anchor = anchor_pos;
+    scoped_ptr<base::ListValue> args(
+        input_ime::OnSurroundingTextChanged::Create(engine_id, info));
 
     DispatchEventToExtension(profile_,
                              extension_id_,
@@ -258,8 +256,8 @@
   virtual void OnReset(const std::string& engine_id) OVERRIDE {
     if (profile_ == NULL || extension_id_.empty())
       return;
-    scoped_ptr<base::ListValue> args(new base::ListValue());
-    args->Append(new base::StringValue(engine_id));
+
+    scoped_ptr<base::ListValue> args(input_ime::OnReset::Create(engine_id));
 
     DispatchEventToExtension(profile_,
                              extension_id_,
diff --git a/chrome/browser/extensions/api/metrics_private/metrics_apitest.cc b/chrome/browser/extensions/api/metrics_private/metrics_apitest.cc
index d31559a..f87f7dd 100644
--- a/chrome/browser/extensions/api/metrics_private/metrics_apitest.cc
+++ b/chrome/browser/extensions/api/metrics_private/metrics_apitest.cc
@@ -8,7 +8,7 @@
 #include "base/metrics/histogram.h"
 #include "base/metrics/statistics_recorder.h"
 #include "chrome/browser/extensions/extension_apitest.h"
-#include "chrome/common/metrics/variations/variations_associated_data.h"
+#include "components/variations/variations_associated_data.h"
 #include "content/public/browser/user_metrics.h"
 
 namespace {
diff --git a/chrome/browser/extensions/api/metrics_private/metrics_private_api.cc b/chrome/browser/extensions/api/metrics_private/metrics_private_api.cc
index 8aa2b1b..fe4a350 100644
--- a/chrome/browser/extensions/api/metrics_private/metrics_private_api.cc
+++ b/chrome/browser/extensions/api/metrics_private/metrics_private_api.cc
@@ -12,8 +12,8 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/common/extensions/api/metrics_private.h"
 #include "chrome/common/extensions/extension.h"
-#include "chrome/common/metrics/variations/variations_associated_data.h"
 #include "chrome/common/pref_names.h"
+#include "components/variations/variations_associated_data.h"
 #include "content/public/browser/user_metrics.h"
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/extensions/api/notifications/notifications_api.cc b/chrome/browser/extensions/api/notifications/notifications_api.cc
index 9c330aa..51089fa 100644
--- a/chrome/browser/extensions/api/notifications/notifications_api.cc
+++ b/chrome/browser/extensions/api/notifications/notifications_api.cc
@@ -331,6 +331,9 @@
                             message,
                             icon,
                             WebKit::WebTextDirectionDefault,
+                            message_center::NotifierId(
+                                message_center::NotifierId::APPLICATION,
+                                extension_->id()),
                             UTF8ToUTF16(extension_->name()),
                             UTF8ToUTF16(api_delegate->id()),
                             optional_fields,
diff --git a/chrome/browser/extensions/api/page_capture/page_capture_api.cc b/chrome/browser/extensions/api/page_capture/page_capture_api.cc
index 1fe87c7..c40a19e 100644
--- a/chrome/browser/extensions/api/page_capture/page_capture_api.cc
+++ b/chrome/browser/extensions/api/page_capture/page_capture_api.cc
@@ -136,9 +136,7 @@
 }
 
 void PageCaptureSaveAsMHTMLFunction::MHTMLGenerated(
-    const base::FilePath& file_path,
     int64 mhtml_file_size) {
-  DCHECK(mhtml_path_ == file_path);
   if (mhtml_file_size <= 0) {
     ReturnFailure(kMHTMLGenerationFailedError);
     return;
diff --git a/chrome/browser/extensions/api/page_capture/page_capture_api.h b/chrome/browser/extensions/api/page_capture/page_capture_api.h
index a0f18b2..aa76864 100644
--- a/chrome/browser/extensions/api/page_capture/page_capture_api.h
+++ b/chrome/browser/extensions/api/page_capture/page_capture_api.h
@@ -50,7 +50,7 @@
   void ReturnSuccess(int64 file_size);
 
   // Callback called once the MHTML generation is done.
-  void MHTMLGenerated(const base::FilePath& file_path, int64 mhtml_file_size);
+  void MHTMLGenerated(int64 mhtml_file_size);
 
   // Returns the WebContents we are associated with, NULL if it's been closed.
   content::WebContents* GetWebContents();
diff --git a/chrome/browser/extensions/api/push_messaging/push_messaging_api.h b/chrome/browser/extensions/api/push_messaging/push_messaging_api.h
index 70e37a9..d1f0a0c 100644
--- a/chrome/browser/extensions/api/push_messaging/push_messaging_api.h
+++ b/chrome/browser/extensions/api/push_messaging/push_messaging_api.h
@@ -15,11 +15,11 @@
 #include "chrome/browser/extensions/api/push_messaging/obfuscated_gaia_id_fetcher.h"
 #include "chrome/browser/extensions/api/push_messaging/push_messaging_invalidation_handler_delegate.h"
 #include "chrome/browser/extensions/extension_function.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 
 class Profile;
 
diff --git a/chrome/browser/extensions/api/runtime/runtime_apitest.cc b/chrome/browser/extensions/api/runtime/runtime_apitest.cc
index c2c17be..500dcdc 100644
--- a/chrome/browser/extensions/api/runtime/runtime_apitest.cc
+++ b/chrome/browser/extensions/api/runtime/runtime_apitest.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 "chrome/browser/apps/app_browsertest_util.h"
 #include "chrome/browser/extensions/api/runtime/runtime_api.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_function_test_utils.h"
@@ -51,9 +52,10 @@
 }
 
 // Tests chrome.runtime.getPackageDirectory with an app.
-IN_PROC_BROWSER_TEST_F(ExtensionApiTest,
+IN_PROC_BROWSER_TEST_F(PlatformAppBrowserTest,
                        ChromeRuntimeGetPackageDirectoryEntryApp) {
-  ASSERT_TRUE(RunPlatformAppTest("runtime/get_package_directory/app"))
+  ClearCommandLineArgs();
+  ASSERT_TRUE(RunPlatformAppTest("api_test/runtime/get_package_directory/app"))
       << message_;
 }
 
diff --git a/chrome/browser/extensions/api/serial/serial_api.cc b/chrome/browser/extensions/api/serial/serial_api.cc
index b958944..3f33da4 100644
--- a/chrome/browser/extensions/api/serial/serial_api.cc
+++ b/chrome/browser/extensions/api/serial/serial_api.cc
@@ -12,6 +12,8 @@
 
 using content::BrowserThread;
 
+namespace serial = extensions::api::serial;
+
 namespace extensions {
 
 const char kConnectionIdKey[] = "connectionId";
@@ -19,6 +21,9 @@
 const char kBytesReadKey[] = "bytesRead";
 const char kBytesWrittenKey[] = "bytesWritten";
 const char kBitrateKey[] = "bitrate";
+const char kDataBitKey[] = "dataBit";
+const char kParityKey[] = "parityBit";
+const char kStopBitKey[] = "stopBit";
 const char kSuccessKey[] = "success";
 const char kDcdKey[] = "dcd";
 const char kCtsKey[] = "cts";
@@ -83,7 +88,9 @@
 // But we'd like to pick something that has a chance of working, and 9600 is a
 // good balance between popularity and speed. So 9600 it is.
 SerialOpenFunction::SerialOpenFunction()
-    : bitrate_(9600) {
+    : bitrate_(9600), databit_(serial::DATA_BIT_EIGHTBIT),
+      parity_(serial::PARITY_BIT_NOPARITY),
+      stopbit_(serial::STOP_BIT_ONESTOPBIT) {
 }
 
 SerialOpenFunction::~SerialOpenFunction() {
@@ -99,6 +106,24 @@
     scoped_ptr<base::DictionaryValue> options = params_->options->ToValue();
     if (options->HasKey(kBitrateKey))
       EXTENSION_FUNCTION_VALIDATE(options->GetInteger(kBitrateKey, &bitrate_));
+    if (options->HasKey(kDataBitKey)) {
+      std::string data;
+      options->GetString(kDataBitKey, &data);
+      if (!data.empty())
+        databit_ = serial::ParseDataBit(data);
+    }
+    if (options->HasKey(kParityKey)) {
+      std::string parity;
+      options->GetString(kParityKey, &parity);
+      if (!parity.empty())
+        parity_ = serial::ParseParityBit(parity);
+    }
+    if (options->HasKey(kStopBitKey)) {
+      std::string stopbit;
+      options->GetString(kStopBitKey, &stopbit);
+      if (!stopbit.empty())
+        stopbit_ = serial::ParseStopBit(stopbit);
+    }
   }
 
   return true;
@@ -116,6 +141,9 @@
     SerialConnection* serial_connection = CreateSerialConnection(
       params_->port,
       bitrate_,
+      databit_,
+      parity_,
+      stopbit_,
       extension_->id());
     CHECK(serial_connection);
     int id = manager_->Add(serial_connection);
@@ -143,8 +171,12 @@
 SerialConnection* SerialOpenFunction::CreateSerialConnection(
     const std::string& port,
     int bitrate,
+    serial::DataBit databit,
+    serial::ParityBit parity,
+    serial::StopBit stopbit,
     const std::string& owner_extension_id) {
-  return new SerialConnection(port, bitrate, owner_extension_id);
+  return new SerialConnection(port, bitrate, databit, parity, stopbit,
+      owner_extension_id);
 }
 
 bool SerialOpenFunction::DoesPortExist(const std::string& port) {
diff --git a/chrome/browser/extensions/api/serial/serial_api.h b/chrome/browser/extensions/api/serial/serial_api.h
index cd3a8bb..6798ab5 100644
--- a/chrome/browser/extensions/api/serial/serial_api.h
+++ b/chrome/browser/extensions/api/serial/serial_api.h
@@ -13,6 +13,8 @@
 #include "chrome/common/extensions/api/serial.h"
 #include "net/base/io_buffer.h"
 
+namespace serial = extensions::api::serial;
+
 namespace extensions {
 
 class SerialConnection;
@@ -69,12 +71,19 @@
   virtual SerialConnection* CreateSerialConnection(
       const std::string& port,
       int bitrate,
+      serial::DataBit databit,
+      serial::ParityBit parity,
+      serial::StopBit stopbit,
       const std::string& owner_extension_id);
+
   virtual bool DoesPortExist(const std::string& port);
 
  private:
   scoped_ptr<api::serial::Open::Params> params_;
   int bitrate_;
+  api::serial::DataBit databit_;
+  api::serial::ParityBit parity_;
+  api::serial::StopBit stopbit_;
 };
 
 class SerialCloseFunction : public SerialAsyncApiFunction {
diff --git a/chrome/browser/extensions/api/serial/serial_apitest.cc b/chrome/browser/extensions/api/serial/serial_apitest.cc
index 646dfd1..a61fb3c 100644
--- a/chrome/browser/extensions/api/serial/serial_apitest.cc
+++ b/chrome/browser/extensions/api/serial/serial_apitest.cc
@@ -22,6 +22,8 @@
 
 using content::BrowserThread;
 
+namespace serial = extensions::api::serial;
+
 namespace {
 
 class SerialApiTest : public ExtensionApiTest {
@@ -52,8 +54,12 @@
   explicit FakeEchoSerialConnection(
       const std::string& port,
       int bitrate,
+      serial::DataBit databit,
+      serial::ParityBit parity,
+      serial::StopBit stopbit,
       const std::string& owner_extension_id)
-      : SerialConnection(port, bitrate, owner_extension_id),
+      : SerialConnection(port, bitrate, databit, parity, stopbit,
+                        owner_extension_id),
         opened_(true) {
     Flush();
     opened_ = false;
@@ -118,9 +124,13 @@
   virtual SerialConnection* CreateSerialConnection(
       const std::string& port,
       int bitrate,
+      serial::DataBit databit,
+      serial::ParityBit parity,
+      serial::StopBit stopbit,
       const std::string& owner_extension_id) OVERRIDE {
     FakeEchoSerialConnection* serial_connection =
-        new FakeEchoSerialConnection(port, bitrate, owner_extension_id);
+        new FakeEchoSerialConnection(port, bitrate, databit, parity, stopbit,
+                                     owner_extension_id);
     EXPECT_CALL(*serial_connection, GetControlSignals(_)).
         Times(1).WillOnce(Return(true));
     EXPECT_CALL(*serial_connection, SetControlSignals(_)).
diff --git a/chrome/browser/extensions/api/serial/serial_connection.cc b/chrome/browser/extensions/api/serial/serial_connection.cc
index ffa49b8..aed373c 100644
--- a/chrome/browser/extensions/api/serial/serial_connection.cc
+++ b/chrome/browser/extensions/api/serial/serial_connection.cc
@@ -10,6 +10,9 @@
 #include "base/lazy_instance.h"
 #include "base/strings/string_util.h"
 #include "chrome/browser/extensions/api/api_resource_manager.h"
+#include "chrome/common/extensions/api/serial.h"
+
+namespace serial = extensions::api::serial;
 
 namespace extensions {
 
@@ -27,8 +30,12 @@
 }
 
 SerialConnection::SerialConnection(const std::string& port, int bitrate,
+                                   serial::DataBit databit,
+                                   serial::ParityBit parity,
+                                   serial::StopBit stopbit,
                                    const std::string& owner_extension_id)
     : ApiResource(owner_extension_id), port_(port), bitrate_(bitrate),
+      databit_(databit), parity_(parity), stopbit_(stopbit),
       file_(base::kInvalidPlatformFileValue) {
   CHECK_GE(bitrate, 0);
 }
diff --git a/chrome/browser/extensions/api/serial/serial_connection.h b/chrome/browser/extensions/api/serial/serial_connection.h
index 3b838ba..f881301 100644
--- a/chrome/browser/extensions/api/serial/serial_connection.h
+++ b/chrome/browser/extensions/api/serial/serial_connection.h
@@ -12,11 +12,14 @@
 #include "base/platform_file.h"
 #include "chrome/browser/extensions/api/api_resource.h"
 #include "chrome/browser/extensions/api/api_resource_manager.h"
+#include "chrome/common/extensions/api/serial.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/base/io_buffer.h"
 
 using content::BrowserThread;
 
+namespace serial = extensions::api::serial;
+
 namespace extensions {
 
 extern const char kSerialConnectionNotFoundError[];
@@ -25,8 +28,9 @@
 // _win and _posix versions of the the .cc file.
 class SerialConnection : public ApiResource {
  public:
-  SerialConnection(const std::string& port,
-                   int bitrate,
+  SerialConnection(const std::string& port, int bitrate,
+                   serial::DataBit databit, serial::ParityBit parity,
+                   serial::StopBit stopbit,
                    const std::string& owner_extension_id);
   virtual ~SerialConnection();
 
@@ -71,6 +75,9 @@
   }
   std::string port_;
   int bitrate_;
+  serial::DataBit databit_;
+  serial::ParityBit parity_;
+  serial::StopBit stopbit_;
   base::PlatformFile file_;
 };
 
diff --git a/chrome/browser/extensions/api/serial/serial_connection_posix.cc b/chrome/browser/extensions/api/serial/serial_connection_posix.cc
index 25c73e3..3d24700 100644
--- a/chrome/browser/extensions/api/serial/serial_connection_posix.cc
+++ b/chrome/browser/extensions/api/serial/serial_connection_posix.cc
@@ -9,6 +9,67 @@
 
 namespace extensions {
 
+namespace {
+  int getBaudRate(int bitrate_) {
+    switch (bitrate_) {
+      case 0:
+        return B0;
+      case 50:
+        return B50;
+      case 75:
+        return B75;
+      case 110:
+        return B110;
+      case 134:
+        return B134;
+      case 150:
+        return B150;
+      case 200:
+        return B200;
+      case 300:
+        return B300;
+      case 600:
+        return B600;
+      case 1200:
+        return B1200;
+      case 1800:
+        return B1800;
+      case 2400:
+        return B2400;
+      case 4800:
+        return B4800;
+      case 9600:
+        return B9600;
+      case 19200:
+        return B19200;
+      case 38400:
+        return B38400;
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+      case 57600:
+        return B57600;
+      case 115200:
+        return B115200;
+      case 230400:
+        return B230400;
+      case 460800:
+        return B460800;
+      case 576000:
+        return B576000;
+      case 921600:
+        return B921600;
+      default:
+        return B9600;
+#else
+// MACOSX doesn't define constants bigger than 38400.
+// So if it is MACOSX and the value doesn't fit any of the defined constants
+// It will setup the bitrate with 'bitrate_' (just forwarding the value)
+      default:
+        return bitrate_;
+#endif
+    }
+  }
+}  // namespace
+
 bool SerialConnection::PostOpen() {
   struct termios options;
 
@@ -17,95 +78,44 @@
 
   // Bitrate (sometimes erroneously referred to as baud rate).
   if (bitrate_ >= 0) {
-    int bitrate_opt_;
-    switch (bitrate_) {
-      case 0:
-        bitrate_opt_ = B0;
-        break;
-      case 50:
-        bitrate_opt_ = B50;
-        break;
-      case 75:
-        bitrate_opt_ = B75;
-        break;
-      case 110:
-        bitrate_opt_ = B110;
-        break;
-      case 134:
-        bitrate_opt_ = B134;
-        break;
-      case 150:
-        bitrate_opt_ = B150;
-        break;
-      case 200:
-        bitrate_opt_ = B200;
-        break;
-      case 300:
-        bitrate_opt_ = B300;
-        break;
-      case 600:
-        bitrate_opt_ = B600;
-        break;
-      case 1200:
-        bitrate_opt_ = B1200;
-        break;
-      case 1800:
-        bitrate_opt_ = B1800;
-        break;
-      case 2400:
-        bitrate_opt_ = B2400;
-        break;
-      case 4800:
-        bitrate_opt_ = B4800;
-        break;
-      case 9600:
-        bitrate_opt_ = B9600;
-        break;
-      case 19200:
-        bitrate_opt_ = B19200;
-        break;
-      case 38400:
-        bitrate_opt_ = B38400;
-        break;
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
-      case 57600:
-        bitrate_opt_ = B57600;
-        break;
-      case 115200:
-        bitrate_opt_ = B115200;
-        break;
-      case 230400:
-        bitrate_opt_ = B230400;
-        break;
-      case 460800:
-        bitrate_opt_ = B460800;
-        break;
-      case 576000:
-        bitrate_opt_ = B576000;
-        break;
-      case 921600:
-        bitrate_opt_ = B921600;
-        break;
-      default:
-        bitrate_opt_ = B9600;
-#else
-// MACOSX doesn't define constants bigger than 38400.
-// So if it is MACOSX and the value doesn't fit any of the defined constants
-// It will setup the bitrate with 'bitrate_' (just forwarding the value)
-      default:
-        bitrate_opt_ = bitrate_;
-#endif
-    }
+    int bitrate_opt_ = getBaudRate(bitrate_);
 
     cfsetispeed(&options, bitrate_opt_);
     cfsetospeed(&options, bitrate_opt_);
   }
 
-  // 8N1
-  options.c_cflag &= ~PARENB;
-  options.c_cflag &= ~CSTOPB;
   options.c_cflag &= ~CSIZE;
-  options.c_cflag |= CS8;
+  switch (databit_) {
+    case serial::DATA_BIT_SEVENBIT:
+      options.c_cflag |= CS7;
+      break;
+    case serial::DATA_BIT_EIGHTBIT:
+    default:
+      options.c_cflag |= CS8;
+      break;
+  }
+  switch (stopbit_) {
+    case serial::STOP_BIT_TWOSTOPBIT:
+      options.c_cflag |= CSTOPB;
+      break;
+    case serial::STOP_BIT_ONESTOPBIT:
+    default:
+      options.c_cflag &= ~CSTOPB;
+      break;
+  }
+  switch (parity_) {
+    case serial::PARITY_BIT_EVENPARITY:
+      options.c_cflag |= PARENB;
+      options.c_cflag &= ~PARODD;
+      break;
+    case serial::PARITY_BIT_ODDPARITY:
+      options.c_cflag |= (PARENB | PARODD);
+      break;
+    case serial::PARITY_BIT_NOPARITY:
+    default:
+      options.c_cflag &= ~(PARENB | PARODD);
+      break;
+  }
   options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
 
   // Enable receiver and set local mode
diff --git a/chrome/browser/extensions/api/serial/serial_connection_win.cc b/chrome/browser/extensions/api/serial/serial_connection_win.cc
index 87a146d..d6d9b12 100644
--- a/chrome/browser/extensions/api/serial/serial_connection_win.cc
+++ b/chrome/browser/extensions/api/serial/serial_connection_win.cc
@@ -10,6 +10,60 @@
 
 namespace extensions {
 
+namespace {
+int getBaudRate(int bitrate_) {
+  switch (bitrate_) {
+    case 110: return CBR_110;
+    case 300: return CBR_300;
+    case 600: return CBR_600;
+    case 1200: return CBR_1200;
+    case 2400: return CBR_2400;
+    case 4800: return CBR_4800;
+    case 9600: return CBR_9600;
+    case 14400: return CBR_14400;
+    case 19200: return CBR_19200;
+    case 38400: return CBR_38400;
+    case 57600: return CBR_57600;
+    case 115200: return CBR_115200;
+    case 128000: return CBR_128000;
+    case 256000: return CBR_256000;
+    default: return CBR_9600;
+  }
+}
+
+int getDataBit(serial::DataBit databit) {
+  switch (databit) {
+    case serial::DATA_BIT_SEVENBIT:
+      return 7;
+    case serial::DATA_BIT_EIGHTBIT:
+    default:
+      return 8;
+  }
+}
+
+int getParity(serial::ParityBit parity) {
+  switch (parity) {
+    case serial::PARITY_BIT_EVENPARITY:
+      return EVENPARITY;
+    case serial::PARITY_BIT_ODDPARITY:
+      return SPACEPARITY;
+    case serial::PARITY_BIT_NOPARITY:
+    default:
+      return NOPARITY;
+  }
+}
+
+int getStopBit(serial::StopBit stopbit) {
+  switch (stopbit) {
+    case serial::STOP_BIT_TWOSTOPBIT:
+      return TWOSTOPBITS;
+    case serial::STOP_BIT_ONESTOPBIT:
+    default:
+      return ONESTOPBIT;
+  }
+}
+}  // namespace
+
 bool SerialConnection::PostOpen() {
   // Set timeouts so that reads return immediately with whatever could be read
   // without blocking.
@@ -23,32 +77,11 @@
   if (!GetCommState(file_, &dcb))
     return false;
 
-  if (bitrate_ >= 0) {
-    bool speed_found = true;
-    DWORD speed = CBR_9600;
-    switch (bitrate_) {
-      case 110: speed = CBR_110; break;
-      case 300: speed = CBR_300; break;
-      case 600: speed = CBR_600; break;
-      case 1200: speed = CBR_1200; break;
-      case 2400: speed = CBR_2400; break;
-      case 4800: speed = CBR_4800; break;
-      case 9600: speed = CBR_9600; break;
-      case 14400: speed = CBR_14400; break;
-      case 19200: speed = CBR_19200; break;
-      case 38400: speed = CBR_38400; break;
-      case 57600: speed = CBR_57600; break;
-      case 115200: speed = CBR_115200; break;
-      case 128000: speed = CBR_128000; break;
-      case 256000: speed = CBR_256000; break;
-      default: speed_found = false; break;
-    }
-    if (speed_found)
-      dcb.BaudRate = speed;
-  }
-  dcb.ByteSize = 8;
-  dcb.StopBits = ONESTOPBIT;
-  dcb.Parity = NOPARITY;
+  dcb.BaudRate = getBaudRate(bitrate_);
+  dcb.ByteSize = getDataBit(databit_);
+  dcb.Parity = getParity(parity_);
+  dcb.StopBits = getStopBit(stopbit_);
+
   if (!SetCommState(file_, &dcb))
     return false;
 
diff --git a/chrome/browser/extensions/api/sessions/sessions_apitest.cc b/chrome/browser/extensions/api/sessions/sessions_apitest.cc
index 4bfc9a0..4643a4c 100644
--- a/chrome/browser/extensions/api/sessions/sessions_apitest.cc
+++ b/chrome/browser/extensions/api/sessions/sessions_apitest.cc
@@ -221,7 +221,9 @@
   EXPECT_EQ(0u, devices->GetSize());
 }
 
-IN_PROC_BROWSER_TEST_F(ExtensionSessionsTest, RestoreForeignSessionWindow) {
+// Flaky timeout: http://crbug.com/278372
+IN_PROC_BROWSER_TEST_F(ExtensionSessionsTest,
+                       DISABLED_RestoreForeignSessionWindow) {
   CreateSessionModels();
 
   scoped_ptr<base::DictionaryValue> restored_window_session(utils::ToDictionary(
diff --git a/chrome/browser/extensions/api/signedin_devices/id_mapping_helper.cc b/chrome/browser/extensions/api/signedin_devices/id_mapping_helper.cc
index a370dd2..7068c0e 100644
--- a/chrome/browser/extensions/api/signedin_devices/id_mapping_helper.cc
+++ b/chrome/browser/extensions/api/signedin_devices/id_mapping_helper.cc
@@ -4,11 +4,15 @@
 

 #include "chrome/browser/extensions/api/signedin_devices/id_mapping_helper.h"

 

+#include "base/memory/scoped_ptr.h"

+#include "base/memory/scoped_vector.h"

 #include "base/rand_util.h"

 #include "base/strings/string_number_conversions.h"

 #include "base/values.h"

-

+#include "chrome/browser/extensions/api/signedin_devices/signedin_devices_api.h"

+#include "chrome/browser/profiles/profile.h"

 #include "chrome/browser/sync/glue/device_info.h"

+#include "chrome/common/extensions/extension.h"

 

 using base::DictionaryValue;

 using base::Value;

@@ -80,4 +84,24 @@
   }

 }

 

+scoped_ptr<DeviceInfo> GetDeviceInfoForClientId(

+    const std::string& client_id,

+    const std::string& extension_id,

+    Profile* profile) {

+  DCHECK(Extension::IdIsValid(extension_id)) << extension_id

+                                             << " is not valid";

+  ScopedVector<DeviceInfo> devices = GetAllSignedinDevices(extension_id,

+                                                           profile);

+  for (ScopedVector<DeviceInfo>::iterator it = devices.begin();

+       it != devices.end();

+       ++it) {

+    if ((*it)->guid() == client_id) {

+      scoped_ptr<DeviceInfo> device(*it);

+      devices.weak_erase(it);

+      return device.Pass();

+    }

+  }

+  return scoped_ptr<DeviceInfo>();

+}

+

 }  // namespace  extensions

diff --git a/chrome/browser/extensions/api/signedin_devices/id_mapping_helper.h b/chrome/browser/extensions/api/signedin_devices/id_mapping_helper.h
index e1ec845..bc19218 100644
--- a/chrome/browser/extensions/api/signedin_devices/id_mapping_helper.h
+++ b/chrome/browser/extensions/api/signedin_devices/id_mapping_helper.h
@@ -8,6 +8,8 @@
 #include <string>

 #include <vector>

 

+#include "base/memory/scoped_ptr.h"

+

 namespace base {

 class DictionaryValue;

 }  // namespace base

@@ -16,6 +18,8 @@
 class DeviceInfo;

 }  // namespace browser_sync

 

+class Profile;

+

 namespace extensions {

 

 // In order to not expose unique GUIDs for devices to third pary apps,

@@ -44,6 +48,13 @@
     std::vector<browser_sync::DeviceInfo*>* device_info,

     base::DictionaryValue* value);

 

+// Gets the device info for a given client id. If the device is not found

+// the returned pointer would be null.

+scoped_ptr<browser_sync::DeviceInfo> GetDeviceInfoForClientId(

+    const std::string& client_id,

+    const std::string& extension_id,

+    Profile* profile);

+

 }  // namespace extensions

 

 #endif  // CHROME_BROWSER_EXTENSIONS_API_SIGNEDIN_DEVICES_ID_MAPPING_HELPER_H__

diff --git a/chrome/browser/extensions/api/signedin_devices/signed_in_devices_manager.cc b/chrome/browser/extensions/api/signedin_devices/signed_in_devices_manager.cc
new file mode 100644
index 0000000..a87fe4d
--- /dev/null
+++ b/chrome/browser/extensions/api/signedin_devices/signed_in_devices_manager.cc
@@ -0,0 +1,152 @@
+// 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 "chrome/browser/extensions/api/signedin_devices/signed_in_devices_manager.h"
+
+#include <string>
+
+#include "base/lazy_instance.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/values.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/api/signedin_devices/signedin_devices_api.h"
+#include "chrome/browser/extensions/event_router.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/glue/device_info.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/common/extensions/api/signedin_devices.h"
+#include "chrome/common/extensions/extension.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+
+using browser_sync::DeviceInfo;
+namespace extensions {
+
+SignedInDevicesChangeObserver::SignedInDevicesChangeObserver(
+    const std::string& extension_id,
+    Profile* profile) : extension_id_(extension_id),
+                        profile_(profile) {
+  ProfileSyncService* pss = ProfileSyncServiceFactory::GetForProfile(profile_);
+  if (pss) {
+    pss->AddObserverForDeviceInfoChange(this);
+  }
+}
+
+SignedInDevicesChangeObserver::~SignedInDevicesChangeObserver() {
+  ProfileSyncService* pss = ProfileSyncServiceFactory::GetForProfile(profile_);
+  if (pss) {
+    pss->RemoveObserverForDeviceInfoChange(this);
+  }
+
+}
+
+void SignedInDevicesChangeObserver::OnDeviceInfoChange() {
+  // There is a change in the list of devices. Get all devices and send them to
+  // the listener.
+  ScopedVector<DeviceInfo> devices = GetAllSignedinDevices(extension_id_,
+                                                           profile_);
+
+  scoped_ptr<base::ListValue> result(new base::ListValue());
+
+  for (ScopedVector<DeviceInfo>::const_iterator it = devices.begin();
+       it != devices.end();
+       ++it) {
+    result->Append((*it)->ToValue());
+  }
+
+  scoped_ptr<Event> event(new Event(
+      api::signedin_devices::OnDeviceInfoChange::kEventName,
+      result.Pass()));
+
+  event->restrict_to_profile = profile_;
+
+  ExtensionSystem::Get(profile_)->event_router()->DispatchEventToExtension(
+      extension_id_, event.Pass());
+}
+
+static base::LazyInstance<ProfileKeyedAPIFactory<SignedInDevicesManager> >
+g_factory = LAZY_INSTANCE_INITIALIZER;
+
+// static
+ProfileKeyedAPIFactory<SignedInDevicesManager>*
+    SignedInDevicesManager::GetFactoryInstance() {
+  return &g_factory.Get();
+}
+
+SignedInDevicesManager::SignedInDevicesManager()
+    : profile_(NULL) {}
+
+SignedInDevicesManager::SignedInDevicesManager(Profile* profile)
+    : profile_(profile) {
+  extensions::EventRouter* router = extensions::ExtensionSystem::Get(
+      profile_)->event_router();
+
+  if (router) {
+    router->RegisterObserver(
+        this, api::signedin_devices::OnDeviceInfoChange::kEventName);
+  }
+
+  // Register for unload event so we could clear all our listeners when
+  // extensions have unloaded.
+  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
+                 content::Source<Profile>(profile_->GetOriginalProfile()));
+}
+
+SignedInDevicesManager::~SignedInDevicesManager() {}
+
+void SignedInDevicesManager::OnListenerAdded(
+    const EventListenerInfo& details) {
+  for (ScopedVector<SignedInDevicesChangeObserver>::const_iterator it =
+           change_observers_.begin();
+           it != change_observers_.end();
+           ++it) {
+    if ((*it)->extension_id() == details.extension_id) {
+      DCHECK(false) <<"OnListenerAded fired twice for same extension";
+      return;
+    }
+  }
+
+  change_observers_.push_back(new SignedInDevicesChangeObserver(
+      details.extension_id,
+      profile_));
+}
+
+void SignedInDevicesManager::OnListenerRemoved(
+    const EventListenerInfo& details) {
+  RemoveChangeObserverForExtension(details.extension_id);
+}
+
+
+void SignedInDevicesManager::RemoveChangeObserverForExtension(
+    const std::string& extension_id) {
+  for (ScopedVector<SignedInDevicesChangeObserver>::iterator it =
+           change_observers_.begin();
+           it != change_observers_.end();
+           ++it) {
+    if ((*it)->extension_id() == extension_id) {
+      change_observers_.erase(it);
+      return;
+    }
+  }
+}
+
+void SignedInDevicesManager::Observe(
+    int type,
+    const content::NotificationSource& source,
+    const content::NotificationDetails& details) {
+  DCHECK_EQ(type, chrome::NOTIFICATION_EXTENSION_UNLOADED);
+  UnloadedExtensionInfo* reason =
+      content::Details<UnloadedExtensionInfo>(details).ptr();
+  RemoveChangeObserverForExtension(reason->extension->id());
+}
+
+}  // namespace extensions
+
diff --git a/chrome/browser/extensions/api/signedin_devices/signed_in_devices_manager.h b/chrome/browser/extensions/api/signedin_devices/signed_in_devices_manager.h
new file mode 100644
index 0000000..bc7b77e
--- /dev/null
+++ b/chrome/browser/extensions/api/signedin_devices/signed_in_devices_manager.h
@@ -0,0 +1,99 @@
+// 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 CHROME_BROWSER_EXTENSIONS_API_SIGNEDIN_DEVICES_SIGNED_IN_DEVICES_MANAGER_H__
+#define CHROME_BROWSER_EXTENSIONS_API_SIGNEDIN_DEVICES_SIGNED_IN_DEVICES_MANAGER_H__
+
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_vector.h"
+#include "chrome/browser/extensions/api/profile_keyed_api_factory.h"
+#include "chrome/browser/extensions/event_router.h"
+#include "chrome/browser/sync/glue/synced_device_tracker.h"
+
+class Profile;
+namespace content {
+  class NotificationDetails;
+  class NotificationObserver;
+  class NotificationRegistrar;
+}  // namespace content
+
+namespace extensions {
+class ProfileKeyedAPI;
+
+struct EventListenerInfo;
+
+// An object of this class is created for each extension that has registered
+// to be notified for device info change. The objects listen for notification
+// from sync on device info change. On receiving the notification the
+// new list of devices is constructed and passed back to the extension.
+// The extension id is part of this object as it is needed to fill in the
+// public ids for devices(public ids for a device, is not the same for
+// all extensions).
+class SignedInDevicesChangeObserver
+    : public browser_sync::SyncedDeviceTracker::Observer {
+ public:
+  SignedInDevicesChangeObserver(const std::string& extension_id,
+                                Profile* profile);
+  virtual ~SignedInDevicesChangeObserver();
+
+  virtual void OnDeviceInfoChange() OVERRIDE;
+
+  const std::string& extension_id() {
+    return extension_id_;
+  }
+
+ private:
+  std::string extension_id_;
+  Profile* const profile_;
+  content::NotificationRegistrar registrar_;
+};
+
+class SignedInDevicesManager
+    : public ProfileKeyedAPI,
+      public content::NotificationObserver,
+      public EventRouter::Observer {
+ public:
+  // Default constructor used for testing.
+  SignedInDevicesManager();
+  explicit SignedInDevicesManager(Profile* profile);
+  virtual ~SignedInDevicesManager();
+
+  // ProfileKeyedAPI implementation.
+  static ProfileKeyedAPIFactory<SignedInDevicesManager>* GetFactoryInstance();
+
+  // NotificationObserver:
+  virtual void Observe(int type,
+                       const content::NotificationSource& source,
+                       const content::NotificationDetails& details) OVERRIDE;
+
+  // EventRouter::Observer:
+  virtual void OnListenerAdded(const EventListenerInfo& details) OVERRIDE;
+  virtual void OnListenerRemoved(const EventListenerInfo& details) OVERRIDE;
+
+ private:
+  friend class ProfileKeyedAPIFactory<SignedInDevicesManager>;
+
+  // ProfileKeyedAPI implementation.
+  static const char* service_name() {
+    return "SignedInDevicesManager";
+  }
+  static const bool kServiceHasOwnInstanceInIncognito = true;
+
+  void RemoveChangeObserverForExtension(const std::string& extension_id);
+
+  Profile* const profile_;
+  content::NotificationRegistrar registrar_;
+  ScopedVector<SignedInDevicesChangeObserver> change_observers_;
+
+  FRIEND_TEST_ALL_PREFIXES(SignedInDevicesManager, UpdateListener);
+
+  DISALLOW_COPY_AND_ASSIGN(SignedInDevicesManager);
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_EXTENSIONS_API_SIGNEDIN_DEVICES_SIGNED_IN_DEVICES_MANAGER_H__
+
diff --git a/chrome/browser/extensions/api/signedin_devices/signed_in_devices_manager_unittest.cc b/chrome/browser/extensions/api/signedin_devices/signed_in_devices_manager_unittest.cc
new file mode 100644
index 0000000..9b0e19d
--- /dev/null
+++ b/chrome/browser/extensions/api/signedin_devices/signed_in_devices_manager_unittest.cc
@@ -0,0 +1,47 @@
+// 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 "base/memory/scoped_ptr.h"
+#include "base/prefs/pref_service.h"
+#include "base/prefs/testing_pref_store.h"
+#include "chrome/browser/extensions/api/signedin_devices/signed_in_devices_manager.h"
+#include "chrome/browser/extensions/event_router.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/common/extensions/api/signedin_devices.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_profile.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+namespace {
+BrowserContextKeyedService* CreateProfileSyncServiceMock(
+    content::BrowserContext* profile) {
+  return NULL;
+}
+}  // namespace
+
+// Adds a listener and removes it.
+TEST(SignedInDevicesManager, UpdateListener) {
+  scoped_ptr<TestingProfile> profile(new TestingProfile());
+  profile->GetPrefs()->SetString(prefs::kGoogleServicesUsername, "foo");
+  ProfileSyncServiceFactory::GetInstance()->SetTestingFactory(
+      profile.get(), CreateProfileSyncServiceMock);
+  SignedInDevicesManager manager(profile.get());
+
+  EventListenerInfo info(
+      api::signedin_devices::OnDeviceInfoChange::kEventName,
+      "extension1");
+
+  // Add a listener.
+  manager.OnListenerAdded(info);
+  EXPECT_EQ(manager.change_observers_.size(), 1U);
+  EXPECT_EQ(manager.change_observers_[0]->extension_id(), info.extension_id);
+
+  // Remove the listener.
+  manager.OnListenerRemoved(info);
+  EXPECT_TRUE(manager.change_observers_.empty());
+}
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/storage/settings_apitest.cc b/chrome/browser/extensions/api/storage/settings_apitest.cc
index 45e3386..df8feb9 100644
--- a/chrome/browser/extensions/api/storage/settings_apitest.cc
+++ b/chrome/browser/extensions/api/storage/settings_apitest.cc
@@ -514,7 +514,8 @@
   EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
 }
 
-#if defined(OS_CHROMEOS) || defined(OS_WIN)
+#if defined(OS_CHROMEOS) || defined(OS_WIN) || \
+    (defined(OS_LINUX) && defined(USE_AURA))
 // Flakily times out. http://crbug.com/171477
 #define MAYBE_ManagedStorageEvents DISABLED_ManagedStorageEvents
 #else
diff --git a/chrome/browser/extensions/api/sync_file_system/sync_file_system_api.cc b/chrome/browser/extensions/api/sync_file_system/sync_file_system_api.cc
index a400971..1ae598b 100644
--- a/chrome/browser/extensions/api/sync_file_system/sync_file_system_api.cc
+++ b/chrome/browser/extensions/api/sync_file_system/sync_file_system_api.cc
@@ -189,7 +189,7 @@
   fileapi::FileSystemURL file_system_url(
       file_system_context->CrackURL(GURL(url)));
 
-  SyncFileSystemServiceFactory::GetForProfile(profile())->GetFileSyncStatus(
+  GetSyncFileSystemService(profile())->GetFileSyncStatus(
       file_system_url,
       Bind(&SyncFileSystemGetFileStatusFunction::DidGetFileStatus,
            this));
@@ -234,7 +234,7 @@
   num_results_received_ = 0;
   file_sync_statuses_.clear();
   sync_file_system::SyncFileSystemService* sync_file_system_service =
-      SyncFileSystemServiceFactory::GetForProfile(profile());
+      GetSyncFileSystemService(profile());
   for (unsigned int i = 0; i < num_expected_results_; i++) {
     std::string url;
     file_entry_urls->GetString(i, &url);
@@ -385,4 +385,12 @@
   return true;
 }
 
+bool SyncFileSystemGetServiceStatusFunction::RunImpl() {
+  sync_file_system::SyncFileSystemService* service = GetSyncFileSystemService(
+      profile());
+  results_ = api::sync_file_system::GetServiceStatus::Results::Create(
+      SyncServiceStateToExtensionEnum(service->GetSyncServiceState()));
+  return true;
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/sync_file_system/sync_file_system_api.h b/chrome/browser/extensions/api/sync_file_system/sync_file_system_api.h
index dd9026e..6e3fbf5 100644
--- a/chrome/browser/extensions/api/sync_file_system/sync_file_system_api.h
+++ b/chrome/browser/extensions/api/sync_file_system/sync_file_system_api.h
@@ -142,6 +142,16 @@
   virtual bool RunImpl() OVERRIDE;
 };
 
+class SyncFileSystemGetServiceStatusFunction : public SyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("syncFileSystem.getServiceStatus",
+                             SYNCFILESYSTEM_GETSERVICESTATUS)
+
+ protected:
+  virtual ~SyncFileSystemGetServiceStatusFunction() {}
+  virtual bool RunImpl() OVERRIDE;
+};
+
 }  // namespace extensions
 
 #endif  // CHROME_BROWSER_EXTENSIONS_API_SYNC_FILE_SYSTEM_SYNC_FILE_SYSTEM_API_H_
diff --git a/chrome/browser/extensions/api/sync_file_system/sync_file_system_apitest.cc b/chrome/browser/extensions/api/sync_file_system/sync_file_system_apitest.cc
index b375641..c9fc95f 100644
--- a/chrome/browser/extensions/api/sync_file_system/sync_file_system_apitest.cc
+++ b/chrome/browser/extensions/api/sync_file_system/sync_file_system_apitest.cc
@@ -200,4 +200,11 @@
       << message_;
 }
 
+IN_PROC_BROWSER_TEST_F(SyncFileSystemApiTest, GetServiceStatus) {
+  mock_remote_service()->SetServiceState(
+      sync_file_system::REMOTE_SERVICE_AUTHENTICATION_REQUIRED);
+  ASSERT_TRUE(RunPlatformAppTest("sync_file_system/get_service_status"))
+      << message_;
+}
+
 }  // namespace chrome
diff --git a/chrome/browser/extensions/api/system_display/display_info_provider.cc b/chrome/browser/extensions/api/system_display/display_info_provider.cc
index e01bb72..064688d 100644
--- a/chrome/browser/extensions/api/system_display/display_info_provider.cc
+++ b/chrome/browser/extensions/api/system_display/display_info_provider.cc
@@ -4,8 +4,56 @@
 
 #include "chrome/browser/extensions/api/system_display/display_info_provider.h"
 
+#include "base/strings/string_number_conversions.h"
+#include "ui/gfx/display.h"
+#include "ui/gfx/screen.h"
+
 namespace extensions {
 
+namespace {
+
+// Converts Rotation enum to integer.
+int RotationToDegrees(gfx::Display::Rotation rotation) {
+  switch (rotation) {
+    case gfx::Display::ROTATE_0:
+      return 0;
+    case gfx::Display::ROTATE_90:
+      return 90;
+    case gfx::Display::ROTATE_180:
+      return 180;
+    case gfx::Display::ROTATE_270:
+      return 270;
+  }
+  return 0;
+}
+
+// Creates new DisplayUnitInfo struct for |display| and adds it at the end of
+// |list|.
+extensions::api::system_display::DisplayUnitInfo*
+CreateDisplayUnitInfo(const gfx::Display& display,
+               int64 primary_display_id) {
+  extensions::api::system_display::DisplayUnitInfo* unit =
+      new extensions::api::system_display::DisplayUnitInfo();
+  const gfx::Rect& bounds = display.bounds();
+  const gfx::Rect& work_area = display.work_area();
+  unit->id = base::Int64ToString(display.id());
+  unit->is_primary = (display.id() == primary_display_id);
+  unit->is_internal = display.IsInternal();
+  unit->is_enabled = true;
+  unit->rotation = RotationToDegrees(display.rotation());
+  unit->bounds.left = bounds.x();
+  unit->bounds.top = bounds.y();
+  unit->bounds.width = bounds.width();
+  unit->bounds.height = bounds.height();
+  unit->work_area.left = work_area.x();
+  unit->work_area.top = work_area.y();
+  unit->work_area.width = work_area.width();
+  unit->work_area.height = work_area.height();
+  return unit;
+}
+
+}  // namespace
+
 DisplayInfoProvider::DisplayInfoProvider() {
 }
 
@@ -26,6 +74,32 @@
   provider_.Get() = provider;
 }
 
+void DisplayInfoProvider::RequestInfo(const RequestInfoCallback& callback) {
+  bool success = QueryInfo();
+
+  base::MessageLoopProxy::current()->PostTask(
+      FROM_HERE,
+      base::Bind(callback, success));
+}
+
+#if !defined(OS_WIN)
+bool DisplayInfoProvider::QueryInfo() {
+  info_.clear();
+
+  // TODO(scottmg): Native is wrong http://crbug.com/133312
+  gfx::Screen* screen = gfx::Screen::GetNativeScreen();
+  int64 primary_id = screen->GetPrimaryDisplay().id();
+  std::vector<gfx::Display> displays = screen->GetAllDisplays();
+  for (int i = 0; i < screen->GetNumDisplays(); ++i) {
+    linked_ptr<extensions::api::system_display::DisplayUnitInfo> unit(
+        CreateDisplayUnitInfo(displays[i], primary_id));
+    UpdateDisplayUnitInfoForPlatform(displays[i], unit.get());
+    info_.push_back(unit);
+  }
+  return true;
+}
+#endif
+
 // static
 DisplayInfoProvider* DisplayInfoProvider::Get() {
   if (provider_.Get().get() == NULL)
diff --git a/chrome/browser/extensions/api/system_display/display_info_provider.h b/chrome/browser/extensions/api/system_display/display_info_provider.h
index ac9c654..65f94eb 100644
--- a/chrome/browser/extensions/api/system_display/display_info_provider.h
+++ b/chrome/browser/extensions/api/system_display/display_info_provider.h
@@ -11,6 +11,10 @@
 #include "chrome/browser/extensions/api/system_info/system_info_provider.h"
 #include "chrome/common/extensions/api/system_display.h"
 
+namespace gfx {
+class Display;
+}
+
 namespace extensions {
 
 typedef std::vector<linked_ptr<
@@ -65,6 +69,12 @@
   // The implementation is platform specific.
   virtual bool QueryInfo() OVERRIDE;
 
+  // Update the content of the |unit| obtained for |display| using
+  // platform specific method.
+  void UpdateDisplayUnitInfoForPlatform(
+      const gfx::Display& display,
+      extensions::api::system_display::DisplayUnitInfo* unit);
+
   static base::LazyInstance<scoped_refptr<DisplayInfoProvider> > provider_;
 
   DISALLOW_COPY_AND_ASSIGN(DisplayInfoProvider);
diff --git a/chrome/browser/extensions/api/system_display/display_info_provider_chromeos.cc b/chrome/browser/extensions/api/system_display/display_info_provider_chromeos.cc
index 712bc52..dba99e0 100644
--- a/chrome/browser/extensions/api/system_display/display_info_provider_chromeos.cc
+++ b/chrome/browser/extensions/api/system_display/display_info_provider_chromeos.cc
@@ -35,21 +35,6 @@
   return rotation == 0 || rotation == 90 || rotation == 180 || rotation == 270;
 }
 
-// Converts Rotation enum to integer.
-int RotationToDegrees(gfx::Display::Rotation rotation) {
-  switch (rotation) {
-    case gfx::Display::ROTATE_0:
-      return 0;
-    case gfx::Display::ROTATE_90:
-      return 90;
-    case gfx::Display::ROTATE_180:
-      return 180;
-    case gfx::Display::ROTATE_270:
-      return 270;
-  }
-  return 0;
-}
-
 // Converts integer integer value in degrees to Rotation enum value.
 gfx::Display::Rotation DegreesToRotation(int degrees) {
   DCHECK(IsValidRotationValue(degrees));
@@ -67,48 +52,6 @@
   }
 }
 
-// Creates new DisplayUnitInfo struct for |display| and adds it at the end of
-// |list|.
-void AddInfoForDisplay(const gfx::Display& display,
-                       DisplayManager* display_manager,
-                       int64 primary_display_id,
-                       DisplayInfo* list) {
-  linked_ptr<extensions::api::system_display::DisplayUnitInfo> unit(
-      new extensions::api::system_display::DisplayUnitInfo());
-  const gfx::Rect& bounds = display.bounds();
-  const gfx::Rect& work_area = display.work_area();
-  const float dpi = display.device_scale_factor() * kDpi96;
-  const gfx::Insets overscan_insets =
-        display_manager->GetOverscanInsets(display.id());
-
-  unit->id = base::Int64ToString(display.id());
-  unit->name = display_manager->GetDisplayNameForId(display.id());
-  unit->is_primary = (display.id() == primary_display_id);
-  unit->is_internal = display.IsInternal();
-  unit->is_enabled = true;
-  if (display_manager->IsMirrored()) {
-    unit->mirroring_source_id =
-        base::Int64ToString(display_manager->mirrored_display().id());
-  }
-  unit->dpi_x = dpi;
-  unit->dpi_y = dpi;
-  unit->rotation = RotationToDegrees(display.rotation());
-  unit->bounds.left = bounds.x();
-  unit->bounds.top = bounds.y();
-  unit->bounds.width = bounds.width();
-  unit->bounds.height = bounds.height();
-  unit->overscan.left = overscan_insets.left();
-  unit->overscan.top = overscan_insets.top();
-  unit->overscan.right = overscan_insets.right();
-  unit->overscan.bottom = overscan_insets.bottom();
-  unit->work_area.left = work_area.x();
-  unit->work_area.top = work_area.y();
-  unit->work_area.width = work_area.width();
-  unit->work_area.height = work_area.height();
-
-  list->push_back(unit);
-}
-
 // Checks if the given point is over the radius vector described by it's end
 // point |vector|. The point is over a vector if it's on its positive (left)
 // side. The method sees a point on the same line as the vector as being over
@@ -359,7 +302,9 @@
   }
 
   int64 display_id = target.id();
-  const gfx::Display& primary = ash::Shell::GetScreen()->GetPrimaryDisplay();
+  // TODO(scottmg): Native is wrong http://crbug.com/133312
+  const gfx::Display& primary =
+      gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
 
   if (!ValidateParamsForDisplay(info, target, display_manager, primary.id(),
                                 error)) {
@@ -410,14 +355,6 @@
 
 }  // namespace
 
-void DisplayInfoProvider::RequestInfo(const RequestInfoCallback& callback) {
-  bool success = QueryInfo();
-
-  base::MessageLoopProxy::current()->PostTask(
-      FROM_HERE,
-      base::Bind(callback, success));
-}
-
 void DisplayInfoProvider::SetInfo(const std::string& display_id,
                                   const DisplayProperties& info,
                                   const SetInfoCallback& callback) {
@@ -428,20 +365,28 @@
       base::Bind(callback, success, error));
 }
 
-bool DisplayInfoProvider::QueryInfo() {
-  info_.clear();
+void DisplayInfoProvider::UpdateDisplayUnitInfoForPlatform(
+    const gfx::Display& display,
+    extensions::api::system_display::DisplayUnitInfo* unit) {
 
-  DisplayManager* display_manager =
-      ash::Shell::GetInstance()->display_manager();
-  DCHECK(display_manager);
-
-  int64 primary_id = ash::Shell::GetScreen()->GetPrimaryDisplay().id();
-  for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
-    AddInfoForDisplay(display_manager->GetDisplayAt(i), display_manager,
-                      primary_id, &info_);
+  ash::internal::DisplayManager* display_manager
+      = ash::Shell::GetInstance()->display_manager();
+  unit->name = display_manager->GetDisplayNameForId(display.id());
+  if (display_manager->IsMirrored()) {
+    unit->mirroring_source_id =
+        base::Int64ToString(display_manager->mirrored_display().id());
   }
 
-  return true;
+  const float dpi = display.device_scale_factor() * kDpi96;
+  unit->dpi_x = dpi;
+  unit->dpi_y = dpi;
+
+  const gfx::Insets overscan_insets =
+      display_manager->GetOverscanInsets(display.id());
+  unit->overscan.left = overscan_insets.left();
+  unit->overscan.top = overscan_insets.top();
+  unit->overscan.right = overscan_insets.right();
+  unit->overscan.bottom = overscan_insets.bottom();
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/system_display/display_info_provider_mac.cc b/chrome/browser/extensions/api/system_display/display_info_provider_mac.cc
index 2b22e1e..3776bf9 100644
--- a/chrome/browser/extensions/api/system_display/display_info_provider_mac.cc
+++ b/chrome/browser/extensions/api/system_display/display_info_provider_mac.cc
@@ -6,11 +6,6 @@
 
 namespace extensions {
 
-void DisplayInfoProvider::RequestInfo(const RequestInfoCallback& callback) {
-  // Redirect the request to a worker pool thread.
-  StartQueryInfo(callback);
-}
-
 void DisplayInfoProvider::SetInfo(
     const std::string& display_id,
     const api::system_display::DisplayProperties& info,
@@ -20,9 +15,10 @@
       base::Bind(callback, false, "Not implemented"));
 }
 
-// TODO(hongbo): implement display info querying on Mac OS X.
-bool DisplayInfoProvider::QueryInfo() {
-  return false;
+void DisplayInfoProvider::UpdateDisplayUnitInfoForPlatform(
+    const gfx::Display& display,
+    extensions::api::system_display::DisplayUnitInfo* unit) {
+  NOTIMPLEMENTED();
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/system_display/display_info_provider_win.cc b/chrome/browser/extensions/api/system_display/display_info_provider_win.cc
index 9d21278..846d777 100644
--- a/chrome/browser/extensions/api/system_display/display_info_provider_win.cc
+++ b/chrome/browser/extensions/api/system_display/display_info_provider_win.cc
@@ -67,11 +67,6 @@
 
 }  // namespace
 
-void DisplayInfoProvider::RequestInfo(const RequestInfoCallback& callback) {
-  // Redirect the request to a worker pool thread.
-  StartQueryInfo(callback);
-}
-
 void DisplayInfoProvider::SetInfo(
     const std::string& display_id,
     const api::system_display::DisplayProperties& info,
@@ -81,6 +76,7 @@
       base::Bind(callback, false, "Not implemented"));
 }
 
+// TODO(hongbo): consolidate implementation using gfx::Display/gfx::Screen.
 bool DisplayInfoProvider::QueryInfo() {
   info_.clear();
 
@@ -90,4 +86,10 @@
   return false;
 }
 
+void DisplayInfoProvider::UpdateDisplayUnitInfoForPlatform(
+    const gfx::Display& display,
+    extensions::api::system_display::DisplayUnitInfo* unit) {
+  NOTIMPLEMENTED();
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/system_display/display_info_provider_x11.cc b/chrome/browser/extensions/api/system_display/display_info_provider_x11.cc
index adfdb14..3776bf9 100644
--- a/chrome/browser/extensions/api/system_display/display_info_provider_x11.cc
+++ b/chrome/browser/extensions/api/system_display/display_info_provider_x11.cc
@@ -6,11 +6,6 @@
 
 namespace extensions {
 
-void DisplayInfoProvider::RequestInfo(const RequestInfoCallback& callback) {
-  // Redirect the request to a worker pool thread.
-  StartQueryInfo(callback);
-}
-
 void DisplayInfoProvider::SetInfo(
     const std::string& display_id,
     const api::system_display::DisplayProperties& info,
@@ -20,9 +15,10 @@
       base::Bind(callback, false, "Not implemented"));
 }
 
-// TODO(hongbo): implement X11 display info querying.
-bool DisplayInfoProvider::QueryInfo() {
-  return false;
+void DisplayInfoProvider::UpdateDisplayUnitInfoForPlatform(
+    const gfx::Display& display,
+    extensions::api::system_display::DisplayUnitInfo* unit) {
+  NOTIMPLEMENTED();
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc
index c636038..b95bc1f 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc
@@ -234,7 +234,8 @@
 }  // namespace
 
 
-IN_PROC_BROWSER_TEST_P(TabCapturePerformanceTest, Performance) {
+// Disabled due to failures on GPU bots (crbug.com/279443)
+IN_PROC_BROWSER_TEST_P(TabCapturePerformanceTest, DISABLED_Performance) {
   RunTest("TabCapturePerformance");
 }
 
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index d6e22c6..1060436 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -110,10 +110,7 @@
 
 namespace extensions {
 
-namespace Get = api::windows::Get;
-namespace GetAll = api::windows::GetAll;
-namespace GetCurrent = api::windows::GetCurrent;
-namespace GetLastFocused = api::windows::GetLastFocused;
+namespace windows = api::windows;
 namespace errors = extension_manifest_errors;
 namespace keys = tabs_constants;
 namespace tabs = api::tabs;
@@ -227,7 +224,7 @@
 // Windows ---------------------------------------------------------------------
 
 bool WindowsGetFunction::RunImpl() {
-  scoped_ptr<Get::Params> params(Get::Params::Create(*args_));
+  scoped_ptr<windows::Get::Params> params(windows::Get::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
   bool populate_tabs = false;
@@ -249,7 +246,8 @@
 }
 
 bool WindowsGetCurrentFunction::RunImpl() {
-  scoped_ptr<GetCurrent::Params> params(GetCurrent::Params::Create(*args_));
+  scoped_ptr<windows::GetCurrent::Params> params(
+      windows::GetCurrent::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
   bool populate_tabs = false;
@@ -270,8 +268,8 @@
 }
 
 bool WindowsGetLastFocusedFunction::RunImpl() {
-  scoped_ptr<GetLastFocused::Params> params(
-      GetLastFocused::Params::Create(*args_));
+  scoped_ptr<windows::GetLastFocused::Params> params(
+      windows::GetLastFocused::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
   bool populate_tabs = false;
@@ -297,7 +295,8 @@
 }
 
 bool WindowsGetAllFunction::RunImpl() {
-  scoped_ptr<GetAll::Params> params(GetAll::Params::Create(*args_));
+  scoped_ptr<windows::GetAll::Params> params(
+      windows::GetAll::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
   bool populate_tabs = false;
@@ -322,16 +321,14 @@
 }
 
 bool WindowsCreateFunction::ShouldOpenIncognitoWindow(
-    const base::DictionaryValue* args,
-    std::vector<GURL>* urls,
-    bool* is_error) {
+    const windows::Create::Params::CreateData* create_data,
+    std::vector<GURL>* urls, bool* is_error) {
   *is_error = false;
   const IncognitoModePrefs::Availability incognito_availability =
       IncognitoModePrefs::GetAvailability(profile_->GetPrefs());
   bool incognito = false;
-  if (args && args->HasKey(keys::kIncognitoKey)) {
-    EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kIncognitoKey,
-                                                 &incognito));
+  if (create_data && create_data->incognito) {
+    incognito = *create_data->incognito;
     if (incognito && incognito_availability == IncognitoModePrefs::DISABLED) {
       error_ = keys::kIncognitoModeIsDisabled;
       *is_error = true;
@@ -372,69 +369,49 @@
 }
 
 bool WindowsCreateFunction::RunImpl() {
-  base::DictionaryValue* args = NULL;
+  scoped_ptr<windows::Create::Params> params(
+      windows::Create::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params);
   std::vector<GURL> urls;
   TabStripModel* source_tab_strip = NULL;
   int tab_index = -1;
 
-  if (HasOptionalArgument(0))
-    EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args));
+  windows::Create::Params::CreateData* create_data = params->create_data.get();
 
   // Look for optional url.
-  if (args) {
-    if (args->HasKey(keys::kUrlKey)) {
-      Value* url_value;
-      std::vector<std::string> url_strings;
-      args->Get(keys::kUrlKey, &url_value);
+  if (create_data && create_data->url) {
+    std::vector<std::string> url_strings;
+    // First, get all the URLs the client wants to open.
+    if (create_data->url->as_string)
+      url_strings.push_back(*create_data->url->as_string);
+    else if (create_data->url->as_strings)
+      url_strings.swap(*create_data->url->as_strings);
 
-      // First, get all the URLs the client wants to open.
-      if (url_value->IsType(Value::TYPE_STRING)) {
-        std::string url_string;
-        url_value->GetAsString(&url_string);
-        url_strings.push_back(url_string);
-      } else if (url_value->IsType(Value::TYPE_LIST)) {
-        const base::ListValue* url_list =
-            static_cast<const base::ListValue*>(url_value);
-        for (size_t i = 0; i < url_list->GetSize(); ++i) {
-          std::string url_string;
-          EXTENSION_FUNCTION_VALIDATE(url_list->GetString(i, &url_string));
-          url_strings.push_back(url_string);
-        }
+    // Second, resolve, validate and convert them to GURLs.
+    for (std::vector<std::string>::iterator i = url_strings.begin();
+         i != url_strings.end(); ++i) {
+      GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL(
+          *i, GetExtension());
+      if (!url.is_valid()) {
+        error_ = ErrorUtils::FormatErrorMessage(keys::kInvalidUrlError, *i);
+        return false;
       }
-
-      // Second, resolve, validate and convert them to GURLs.
-      for (std::vector<std::string>::iterator i = url_strings.begin();
-           i != url_strings.end(); ++i) {
-        GURL url = ExtensionTabUtil::ResolvePossiblyRelativeURL(
-            *i, GetExtension());
-        if (!url.is_valid()) {
-          error_ = ErrorUtils::FormatErrorMessage(
-              keys::kInvalidUrlError, *i);
-          return false;
-        }
-        // Don't let the extension crash the browser or renderers.
-        if (ExtensionTabUtil::IsCrashURL(url)) {
-          error_ = keys::kNoCrashBrowserError;
-          return false;
-        }
-        urls.push_back(url);
+      // Don't let the extension crash the browser or renderers.
+      if (ExtensionTabUtil::IsCrashURL(url)) {
+        error_ = keys::kNoCrashBrowserError;
+        return false;
       }
+      urls.push_back(url);
     }
   }
 
   // Look for optional tab id.
-  if (args) {
-    int tab_id = -1;
-    if (args->HasKey(keys::kTabIdKey)) {
-      EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kTabIdKey, &tab_id));
-
-      // Find the tab. |source_tab_strip| and |tab_index| will later be used to
-      // move the tab into the created window.
-      if (!GetTabById(tab_id, profile(), include_incognito(),
-                      NULL, &source_tab_strip,
-                      NULL, &tab_index, &error_))
-        return false;
-    }
+  if (create_data && create_data->tab_id) {
+    // Find the tab. |source_tab_strip| and |tab_index| will later be used to
+    // move the tab into the created window.
+    if (!GetTabById(*create_data->tab_id, profile(), include_incognito(), NULL,
+                    &source_tab_strip, NULL, &tab_index, &error_))
+      return false;
   }
 
   Profile* window_profile = profile();
@@ -451,7 +428,7 @@
 
   // Decide whether we are opening a normal window or an incognito window.
   bool is_error = true;
-  bool open_incognito_window = ShouldOpenIncognitoWindow(args, &urls,
+  bool open_incognito_window = ShouldOpenIncognitoWindow(create_data, &urls,
                                                          &is_error);
   if (is_error) {
     // error_ member variable is set inside of ShouldOpenIncognitoWindow.
@@ -461,18 +438,16 @@
     window_profile = window_profile->GetOffTheRecordProfile();
   }
 
-  if (args) {
+  if (create_data) {
     // Figure out window type before figuring out bounds so that default
     // bounds can be set according to the window type.
-    std::string type_str;
-    if (args->HasKey(keys::kWindowTypeKey)) {
-      EXTENSION_FUNCTION_VALIDATE(args->GetString(keys::kWindowTypeKey,
-                                                  &type_str));
-      if (type_str == keys::kWindowTypeValuePopup) {
+    switch (create_data->type) {
+      case windows::Create::Params::CreateData::TYPE_POPUP:
         window_type = Browser::TYPE_POPUP;
         extension_id = GetExtension()->id();
-      } else if (type_str == keys::kWindowTypeValuePanel ||
-                 type_str == keys::kWindowTypeValueDetachedPanel) {
+        break;
+      case windows::Create::Params::CreateData::TYPE_PANEL:
+      case windows::Create::Params::CreateData::TYPE_DETACHED_PANEL: {
         extension_id = GetExtension()->id();
         bool use_panels = false;
 #if !defined(OS_ANDROID)
@@ -482,16 +457,22 @@
           create_panel = true;
 #if !defined(OS_CHROMEOS)
           // Non-ChromeOS has both docked and detached panel types.
-          if (type_str == keys::kWindowTypeValueDetachedPanel)
+          if (create_data->type ==
+              windows::Create::Params::CreateData::TYPE_DETACHED_PANEL) {
             panel_create_mode = PanelManager::CREATE_AS_DETACHED;
+          }
 #endif
         } else {
           window_type = Browser::TYPE_POPUP;
         }
-      } else if (type_str != keys::kWindowTypeValueNormal) {
+        break;
+      }
+      case windows::Create::Params::CreateData::TYPE_NONE:
+      case windows::Create::Params::CreateData::TYPE_NORMAL:
+        break;
+      default:
         error_ = keys::kInvalidWindowTypeError;
         return false;
-      }
     }
 
     // Initialize default window bounds according to window type.
@@ -519,34 +500,20 @@
     }
 
     // Any part of the bounds can optionally be set by the caller.
-    int bounds_val = -1;
-    if (args->HasKey(keys::kLeftKey)) {
-      EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kLeftKey,
-                                                   &bounds_val));
-      window_bounds.set_x(bounds_val);
-    }
+    if (create_data->left)
+      window_bounds.set_x(*create_data->left);
 
-    if (args->HasKey(keys::kTopKey)) {
-      EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kTopKey,
-                                                   &bounds_val));
-      window_bounds.set_y(bounds_val);
-    }
+    if (create_data->top)
+      window_bounds.set_y(*create_data->top);
 
-    if (args->HasKey(keys::kWidthKey)) {
-      EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kWidthKey,
-                                                   &bounds_val));
-      window_bounds.set_width(bounds_val);
-    }
+    if (create_data->width)
+      window_bounds.set_width(*create_data->width);
 
-    if (args->HasKey(keys::kHeightKey)) {
-      EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kHeightKey,
-                                                   &bounds_val));
-      window_bounds.set_height(bounds_val);
-    }
+    if (create_data->height)
+      window_bounds.set_height(*create_data->height);
 
-    if (args->HasKey(keys::kFocusedKey)) {
-      EXTENSION_FUNCTION_VALIDATE(args->GetBoolean(keys::kFocusedKey,
-                                                   &focused));
+    if (create_data->focused) {
+      focused = *create_data->focused;
       saw_focus_key = true;
     }
   }
@@ -662,13 +629,13 @@
 }
 
 bool WindowsUpdateFunction::RunImpl() {
-  int window_id = extension_misc::kUnknownWindowId;
-  EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id));
-  base::DictionaryValue* update_props;
-  EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &update_props));
+  scoped_ptr<windows::Update::Params> params(
+      windows::Update::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params);
 
   WindowController* controller;
-  if (!windows_util::GetWindowFromWindowID(this, window_id, &controller))
+  if (!windows_util::GetWindowFromWindowID(this, params->window_id,
+                                            &controller))
     return false;
 
 #if defined(OS_WIN)
@@ -680,22 +647,24 @@
 #endif
 
   ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT;  // No change.
-  std::string state_str;
-  if (update_props->HasKey(keys::kShowStateKey)) {
-    EXTENSION_FUNCTION_VALIDATE(update_props->GetString(keys::kShowStateKey,
-                                                        &state_str));
-    if (state_str == keys::kShowStateValueNormal) {
+  switch (params->update_info.state) {
+    case windows::Update::Params::UpdateInfo::STATE_NORMAL:
       show_state = ui::SHOW_STATE_NORMAL;
-    } else if (state_str == keys::kShowStateValueMinimized) {
+      break;
+    case windows::Update::Params::UpdateInfo::STATE_MINIMIZED:
       show_state = ui::SHOW_STATE_MINIMIZED;
-    } else if (state_str == keys::kShowStateValueMaximized) {
+      break;
+    case windows::Update::Params::UpdateInfo::STATE_MAXIMIZED:
       show_state = ui::SHOW_STATE_MAXIMIZED;
-    } else if (state_str == keys::kShowStateValueFullscreen) {
+      break;
+    case windows::Update::Params::UpdateInfo::STATE_FULLSCREEN:
       show_state = ui::SHOW_STATE_FULLSCREEN;
-    } else {
+      break;
+    case windows::Update::Params::UpdateInfo::STATE_NONE:
+      break;
+    default:
       error_ = keys::kInvalidWindowStateError;
       return false;
-    }
   }
 
   if (show_state != ui::SHOW_STATE_FULLSCREEN &&
@@ -730,36 +699,23 @@
   bool set_bounds = false;
 
   // Any part of the bounds can optionally be set by the caller.
-  int bounds_val;
-  if (update_props->HasKey(keys::kLeftKey)) {
-    EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
-        keys::kLeftKey,
-        &bounds_val));
-    bounds.set_x(bounds_val);
+  if (params->update_info.left) {
+    bounds.set_x(*params->update_info.left);
     set_bounds = true;
   }
 
-  if (update_props->HasKey(keys::kTopKey)) {
-    EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
-        keys::kTopKey,
-        &bounds_val));
-    bounds.set_y(bounds_val);
+  if (params->update_info.top) {
+    bounds.set_y(*params->update_info.top);
     set_bounds = true;
   }
 
-  if (update_props->HasKey(keys::kWidthKey)) {
-    EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
-        keys::kWidthKey,
-        &bounds_val));
-    bounds.set_width(bounds_val);
+  if (params->update_info.width) {
+    bounds.set_width(*params->update_info.width);
     set_bounds = true;
   }
 
-  if (update_props->HasKey(keys::kHeightKey)) {
-    EXTENSION_FUNCTION_VALIDATE(update_props->GetInteger(
-        keys::kHeightKey,
-        &bounds_val));
-    bounds.set_height(bounds_val);
+  if (params->update_info.height) {
+    bounds.set_height(*params->update_info.height);
     set_bounds = true;
   }
 
@@ -775,11 +731,8 @@
     controller->window()->SetBounds(bounds);
   }
 
-  bool active_val = false;
-  if (update_props->HasKey(keys::kFocusedKey)) {
-    EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(
-        keys::kFocusedKey, &active_val));
-    if (active_val) {
+  if (params->update_info.focused) {
+    if (*params->update_info.focused) {
       if (show_state == ui::SHOW_STATE_MINIMIZED) {
         error_ = keys::kInvalidWindowStateError;
         return false;
@@ -795,12 +748,8 @@
     }
   }
 
-  bool draw_attention = false;
-  if (update_props->HasKey(keys::kDrawAttentionKey)) {
-    EXTENSION_FUNCTION_VALIDATE(update_props->GetBoolean(
-        keys::kDrawAttentionKey, &draw_attention));
-    controller->window()->FlashFrame(draw_attention);
-  }
+  if (params->update_info.draw_attention)
+    controller->window()->FlashFrame(*params->update_info.draw_attention);
 
   SetResult(controller->CreateWindowValue());
 
@@ -808,11 +757,13 @@
 }
 
 bool WindowsRemoveFunction::RunImpl() {
-  int window_id = -1;
-  EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &window_id));
+  scoped_ptr<windows::Remove::Params> params(
+      windows::Remove::Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params);
 
   WindowController* controller;
-  if (!windows_util::GetWindowFromWindowID(this, window_id, &controller))
+  if (!windows_util::GetWindowFromWindowID(this, params->window_id,
+                                           &controller))
     return false;
 
 #if defined(OS_WIN)
@@ -1395,7 +1346,8 @@
             ScriptExecutor::TOP_FRAME,
             UserScript::DOCUMENT_IDLE,
             ScriptExecutor::MAIN_WORLD,
-            false /* is_web_view */,
+            ScriptExecutor::DEFAULT_PROCESS,
+            ScriptExecutor::NO_RESULT,
             base::Bind(&TabsUpdateFunction::OnExecuteCodeFinished, this));
 
     *is_async = true;
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.h b/chrome/browser/extensions/api/tabs/tabs_api.h
index 1313b71..a3ee038 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.h
+++ b/chrome/browser/extensions/api/tabs/tabs_api.h
@@ -66,14 +66,16 @@
   virtual ~WindowsCreateFunction() {}
   virtual bool RunImpl() OVERRIDE;
   // Returns whether the window should be created in incognito mode.
+  // |create_data| are the options passed by the extension. It may be NULL.
   // |urls| is the list of urls to open. If we are creating an incognito window,
   // the function will remove these urls which may not be opened in incognito
   // mode.  If window creation leads the browser into an erroneous state,
   // |is_error| is set to true (also, error_ member variable is assigned
   // the proper error message).
-  bool ShouldOpenIncognitoWindow(const base::DictionaryValue* args,
-                                 std::vector<GURL>* urls,
-                                 bool* is_error);
+  bool ShouldOpenIncognitoWindow(
+      const api::windows::Create::Params::CreateData* create_data,
+      std::vector<GURL>* urls,
+      bool* is_error);
   DECLARE_EXTENSION_FUNCTION("windows.create", WINDOWS_CREATE)
 };
 class WindowsUpdateFunction : public SyncExtensionFunction {
diff --git a/chrome/browser/extensions/api/tabs/tabs_constants.cc b/chrome/browser/extensions/api/tabs/tabs_constants.cc
index 457361e..6eda488 100644
--- a/chrome/browser/extensions/api/tabs/tabs_constants.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_constants.cc
@@ -13,7 +13,6 @@
 const char kBypassCache[] = "bypassCache";
 const char kCodeKey[] = "code";
 const char kCurrentWindowKey[] = "currentWindow";
-const char kDrawAttentionKey[] = "drawAttention";
 const char kFaviconUrlKey[] = "favIconUrl";
 const char kFileKey[] = "file";
 const char kFocusedKey[] = "focused";
diff --git a/chrome/browser/extensions/api/tabs/tabs_constants.h b/chrome/browser/extensions/api/tabs/tabs_constants.h
index 67e862a..83b7d98 100644
--- a/chrome/browser/extensions/api/tabs/tabs_constants.h
+++ b/chrome/browser/extensions/api/tabs/tabs_constants.h
@@ -17,7 +17,6 @@
 extern const char kBypassCache[];
 extern const char kCodeKey[];
 extern const char kCurrentWindowKey[];
-extern const char kDrawAttentionKey[];
 extern const char kFaviconUrlKey[];
 extern const char kFileKey[];
 extern const char kFocusedKey[];
diff --git a/chrome/browser/extensions/api/usb/usb_api.cc b/chrome/browser/extensions/api/usb/usb_api.cc
index 6fa3aa2..c8ec848 100644
--- a/chrome/browser/extensions/api/usb/usb_api.cc
+++ b/chrome/browser/extensions/api/usb/usb_api.cc
@@ -65,7 +65,7 @@
 static const char kErrorCannotReleaseInterface[] = "Error releasing interface.";
 static const char kErrorCannotSetInterfaceAlternateSetting[] =
     "Error setting alternate interface setting.";
-static const char kErrorConvertDirection[] = "Invalid transsfer direction.";
+static const char kErrorConvertDirection[] = "Invalid transfer direction.";
 static const char kErrorConvertRecipient[] = "Invalid transfer recipient.";
 static const char kErrorConvertRequestType[] = "Invalid request type.";
 static const char kErrorConvertSynchronizationType[] =
@@ -89,7 +89,7 @@
 static const int kMaxPackets = 4 * 1024 * 1024;
 static const int kMaxPacketLength = 64 * 1024;
 
-static UsbDevice* device_for_test_ = NULL;
+static UsbDevice* g_device_for_test = NULL;
 
 static bool ConvertDirectionToApi(const UsbEndpointDirection& input,
                                   Direction* output) {
@@ -326,14 +326,6 @@
   return descriptor.ToValue().release();
 }
 
-void GetUsbService(base::Callback<void(UsbService* service)> callback) {
-  BrowserThread::PostTaskAndReplyWithResult(
-      BrowserThread::FILE,
-      FROM_HERE,
-      base::Bind(UsbService::GetInstance),
-      callback);
-}
-
 }  // namespace
 
 namespace extensions {
@@ -347,6 +339,7 @@
 
 bool UsbAsyncApiFunction::PrePrepare() {
   manager_ = ApiResourceManager<UsbDeviceResource>::Get(profile());
+  set_work_thread_id(BrowserThread::FILE);
   return manager_ != NULL;
 }
 
@@ -411,7 +404,7 @@
 UsbFindDevicesFunction::~UsbFindDevicesFunction() {}
 
 void UsbFindDevicesFunction::SetDeviceForTest(UsbDevice* device) {
-  device_for_test_ = device;
+  g_device_for_test = device;
 }
 
 bool UsbFindDevicesFunction::Prepare() {
@@ -423,10 +416,10 @@
 void UsbFindDevicesFunction::AsyncWorkStart() {
   result_.reset(new base::ListValue());
 
-  if (device_for_test_) {
+  if (g_device_for_test) {
     UsbDeviceResource* const resource = new UsbDeviceResource(
         extension_->id(),
-        device_for_test_->Open());
+        g_device_for_test->Open());
 
     Device device;
     result_->Append(PopulateDevice(manager_->Add(resource), 0, 0));
@@ -448,32 +441,15 @@
     return;
   }
 
-  GetUsbService(base::Bind(&UsbFindDevicesFunction::EnumerateDevices,
-                           this,
-                           vendor_id,
-                           product_id,
-                           interface_id));
+  UsbService *service = UsbService::GetInstance();
+  service->FindDevices(
+      vendor_id, product_id, interface_id,
+      base::Bind(&UsbFindDevicesFunction::EnumerationCompletedFileThread,
+                 this));
 }
 
-void UsbFindDevicesFunction::EnumerateDevices(
-    uint16_t vendor_id,
-    uint16_t product_id,
-    int interface_id,
-    UsbService* service) {
-  BrowserThread::PostTask(
-      BrowserThread::FILE,
-      FROM_HERE,
-      base::Bind(&UsbService::FindDevices,
-                 base::Unretained(service),
-                 vendor_id,
-                 product_id,
-                 interface_id,
-                 base::Bind(&UsbFindDevicesFunction::OnEnumerationCompleted,
-                            this)));
-}
-
-void UsbFindDevicesFunction::OnEnumerationCompleted(
-    ScopedDeviceVector devices) {
+void UsbFindDevicesFunction::EnumerationCompletedFileThread(
+    scoped_ptr<std::vector<scoped_refptr<UsbDevice> > > devices) {
   for (size_t i = 0; i < devices->size(); ++i) {
     scoped_refptr<UsbDeviceHandle> device_handle =
       devices->at(i)->Open();
@@ -481,13 +457,6 @@
       device_handles_.push_back(device_handle);
   }
 
-  BrowserThread::PostTask(
-      BrowserThread::IO,
-      FROM_HERE,
-      base::Bind(&UsbFindDevicesFunction::OnCompleted, this));
-}
-
-void UsbFindDevicesFunction::OnCompleted() {
   for (size_t i = 0; i < device_handles_.size(); ++i) {
     UsbDeviceHandle* const device_handle = device_handles_[i].get();
     UsbDeviceResource* const resource =
@@ -520,18 +489,10 @@
     return;
   }
 
-  config_ = new UsbConfigDescriptor();
-  BrowserThread::PostTaskAndReplyWithResult(
-      BrowserThread::FILE,
-      FROM_HERE,
-      base::Bind(&UsbDevice::ListInterfaces,
-                 resource->device()->device(),
-                 config_),
-      base::Bind(&UsbListInterfacesFunction::OnCompleted, this));
-}
+  scoped_refptr<UsbConfigDescriptor> config =
+      resource->device()->device()->ListInterfaces();
 
-void UsbListInterfacesFunction::OnCompleted(bool success) {
-  if (!success) {
+  if (!config) {
     SetError(kErrorCannotListInterfaces);
     AsyncWorkCompleted();
     return;
@@ -539,16 +500,17 @@
 
   result_.reset(new base::ListValue());
 
-  for (size_t i = 0, numInterfaces = config_->GetNumInterfaces();
-      i < numInterfaces; ++i) {
-    scoped_refptr<const UsbInterface> usbInterface(config_->GetInterface(i));
-    for (size_t j = 0, numDescriptors = usbInterface->GetNumAltSettings();
-            j < numDescriptors; ++j) {
-      scoped_refptr<const UsbInterfaceDescriptor> descriptor
-          = usbInterface->GetAltSetting(j);
+  for (size_t i = 0, num_interfaces = config->GetNumInterfaces();
+      i < num_interfaces; ++i) {
+    scoped_refptr<const UsbInterfaceDescriptor>
+        usb_interface(config->GetInterface(i));
+    for (size_t j = 0, num_descriptors = usb_interface->GetNumAltSettings();
+            j < num_descriptors; ++j) {
+      scoped_refptr<const UsbInterfaceAltSettingDescriptor> descriptor
+          = usb_interface->GetAltSetting(j);
       std::vector<linked_ptr<EndpointDescriptor> > endpoints;
-      for (size_t k = 0, numEndpoints = descriptor->GetNumEndpoints();
-          k < numEndpoints; k++) {
+      for (size_t k = 0, num_endpoints = descriptor->GetNumEndpoints();
+          k < num_endpoints; k++) {
         scoped_refptr<const UsbEndpointDescriptor> endpoint
             = descriptor->GetEndpoint(k);
         linked_ptr<EndpointDescriptor> endpoint_desc(new EndpointDescriptor());
@@ -650,14 +612,7 @@
     return;
   }
 
-  BrowserThread::PostTaskAndReply(
-      BrowserThread::FILE,
-      FROM_HERE,
-      base::Bind(&UsbDeviceHandle::Close, resource->device()),
-      base::Bind(&UsbCloseDeviceFunction::OnCompleted, this));
-}
-
-void UsbCloseDeviceFunction::OnCompleted() {
+  resource->device()->Close();
   RemoveUsbDeviceResource(parameters_->device.handle);
   AsyncWorkCompleted();
 }
@@ -680,16 +635,9 @@
     return;
   }
 
-  BrowserThread::PostTaskAndReplyWithResult(
-      BrowserThread::FILE,
-      FROM_HERE,
-      base::Bind(&UsbDeviceHandle::ClaimInterface,
-                 resource->device(),
-                 parameters_->interface_number),
-      base::Bind(&UsbClaimInterfaceFunction::OnCompleted, this));
-}
+  bool success =
+      resource->device()->ClaimInterface(parameters_->interface_number);
 
-void UsbClaimInterfaceFunction::OnCompleted(bool success) {
   if (!success)
     SetError(kErrorCannotClaimInterface);
   AsyncWorkCompleted();
@@ -712,17 +660,8 @@
     CompleteWithError(kErrorNoDevice);
     return;
   }
-
-  BrowserThread::PostTaskAndReplyWithResult(
-      BrowserThread::FILE,
-      FROM_HERE,
-      base::Bind(&UsbDeviceHandle::ReleaseInterface,
-                 resource->device(),
-                 parameters_->interface_number),
-      base::Bind(&UsbReleaseInterfaceFunction::OnCompleted, this));
-}
-
-void UsbReleaseInterfaceFunction::OnCompleted(bool success) {
+  bool success =
+      resource->device()->ReleaseInterface(parameters_->interface_number);
   if (!success)
     SetError(kErrorCannotReleaseInterface);
   AsyncWorkCompleted();
@@ -748,19 +687,12 @@
     return;
   }
 
-  BrowserThread::PostTaskAndReplyWithResult(
-      BrowserThread::FILE,
-      FROM_HERE,
-      base::Bind(&UsbDeviceHandle::SetInterfaceAlternateSetting,
-                 resource->device(),
-                 parameters_->interface_number,
-                 parameters_->alternate_setting),
-      base::Bind(&UsbSetInterfaceAlternateSettingFunction::OnCompleted, this));
-}
-
-void UsbSetInterfaceAlternateSettingFunction::OnCompleted(bool success) {
+  bool success = resource->device()->SetInterfaceAlternateSetting(
+      parameters_->interface_number,
+      parameters_->alternate_setting);
   if (!success)
     SetError(kErrorCannotSetInterfaceAlternateSetting);
+
   AsyncWorkCompleted();
 }
 
@@ -1004,26 +936,7 @@
     return;
   }
 
-  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
-                          base::Bind(&UsbResetDeviceFunction::OnStartResest,
-                                     this, resource));
-}
-
-void UsbResetDeviceFunction::OnStartResest(UsbDeviceResource* resource) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
-  OnCompletedFileThread(resource->device()->ResetDevice());
-}
-
-void UsbResetDeviceFunction::OnCompletedFileThread(bool success) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
-  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
-                          base::Bind(&UsbResetDeviceFunction::OnCompleted,
-                                     this, success));
-  return;
-}
-
-void UsbResetDeviceFunction::OnCompleted(bool success) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  bool success = resource->device()->ResetDevice();
   if (!success) {
     UsbDeviceResource* const resource = GetUsbDeviceResource(
         parameters_->device.handle);
@@ -1031,25 +944,15 @@
       CompleteWithError(kErrorNoDevice);
       return;
     }
-    // Close the device now because the handle is invalid after an
-    // unsuccessful reset.
-    BrowserThread::PostTaskAndReply(
-      BrowserThread::FILE,
-      FROM_HERE,
-      base::Bind(&UsbDeviceHandle::Close,
-                 resource->device()),
-      base::Bind(&UsbResetDeviceFunction::OnError, this));
+    resource->device()->Close();
+    RemoveUsbDeviceResource(parameters_->device.handle);
+    SetError(kErrorResetDevice);
+    SetResult(new base::FundamentalValue(false));
+    AsyncWorkCompleted();
     return;
   }
   SetResult(new base::FundamentalValue(true));
   AsyncWorkCompleted();
 }
 
-void UsbResetDeviceFunction::OnError() {
-  RemoveUsbDeviceResource(parameters_->device.handle);
-  SetError(kErrorResetDevice);
-  SetResult(new base::FundamentalValue(false));
-  AsyncWorkCompleted();
-}
-
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/usb/usb_api.h b/chrome/browser/extensions/api/usb/usb_api.h
index 2f779ff..a778503 100644
--- a/chrome/browser/extensions/api/usb/usb_api.h
+++ b/chrome/browser/extensions/api/usb/usb_api.h
@@ -73,22 +73,8 @@
   virtual void AsyncWorkStart() OVERRIDE;
 
  private:
-  typedef scoped_ptr<std::vector<scoped_refptr<UsbDevice> > >
-      ScopedDeviceVector;
-
-  // This should be run on the FILE thread.
-  // Wait for GetDeviceService to return and start enumeration on FILE thread.
-  void EnumerateDevices(uint16_t vendor_id,
-                        uint16_t product_id,
-                        int interface_id,
-                        UsbService* service);
-
-  // Relay the result on IO thread to OnCompleted.
-  void OnEnumerationCompleted(ScopedDeviceVector devices);
-
-  // This should be run on the IO thread.
-  // Create ApiResources and reply.
-  void OnCompleted();
+  void EnumerationCompletedFileThread(
+      scoped_ptr<std::vector<scoped_refptr<UsbDevice> > > devices);
 
   scoped_ptr<base::ListValue> result_;
   std::vector<scoped_refptr<UsbDeviceHandle> > device_handles_;
@@ -108,8 +94,6 @@
   virtual void AsyncWorkStart() OVERRIDE;
 
  private:
-  void OnCompleted(bool success);
-
   bool ConvertDirectionSafely(const UsbEndpointDirection& input,
                               extensions::api::usb::Direction* output);
   bool ConvertSynchronizationTypeSafely(
@@ -121,7 +105,6 @@
                               extensions::api::usb::UsageType* output);
 
   scoped_ptr<base::ListValue> result_;
-  scoped_refptr<UsbConfigDescriptor> config_;
   scoped_ptr<extensions::api::usb::ListInterfaces::Params> parameters_;
 };
 
@@ -137,8 +120,6 @@
   virtual bool Prepare() OVERRIDE;
   virtual void AsyncWorkStart() OVERRIDE;
 
-  void OnCompleted();
-
  private:
   scoped_ptr<extensions::api::usb::CloseDevice::Params> parameters_;
 };
@@ -156,8 +137,6 @@
   virtual void AsyncWorkStart() OVERRIDE;
 
  private:
-  void OnCompleted(bool success);
-
   scoped_ptr<extensions::api::usb::ClaimInterface::Params> parameters_;
 };
 
@@ -174,8 +153,6 @@
   virtual void AsyncWorkStart() OVERRIDE;
 
  private:
-  void OnCompleted(bool success);
-
   scoped_ptr<extensions::api::usb::ReleaseInterface::Params> parameters_;
 };
 
@@ -192,8 +169,6 @@
   virtual bool Prepare() OVERRIDE;
   virtual void AsyncWorkStart() OVERRIDE;
 
-  void OnCompleted(bool success);
-
   scoped_ptr<extensions::api::usb::SetInterfaceAlternateSetting::Params>
       parameters_;
 };
@@ -276,14 +251,6 @@
   virtual void AsyncWorkStart() OVERRIDE;
 
  private:
-  // This should be run on the FILE thread.
-  void OnStartResest(UsbDeviceResource* resource);
-  void OnCompletedFileThread(bool success);
-
-  // This should be run on the IO thread.
-  void OnCompleted(bool success);
-  void OnError();
-
   scoped_ptr<extensions::api::usb::ResetDevice::Params> parameters_;
 };
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/usb/usb_apitest.cc b/chrome/browser/extensions/api/usb/usb_apitest.cc
index 3af2e2e..8fa2621 100644
--- a/chrome/browser/extensions/api/usb/usb_apitest.cc
+++ b/chrome/browser/extensions/api/usb/usb_apitest.cc
@@ -80,7 +80,7 @@
     return false;
   }
 
-  MOCK_METHOD1(ListInterfaces, bool(UsbConfigDescriptor* config));
+  MOCK_METHOD0(ListInterfaces, scoped_refptr<UsbConfigDescriptor>());
 
  private:
   MockUsbDeviceHandle* mock_handle_;
@@ -118,8 +118,8 @@
 }
 
 IN_PROC_BROWSER_TEST_F(UsbApiTest, ListInterfaces) {
-  EXPECT_CALL(*mock_device_.get(), ListInterfaces(_))
-      .WillOnce(Return(false));
+  EXPECT_CALL(*mock_device_.get(), ListInterfaces())
+      .WillOnce(Return(scoped_refptr<UsbConfigDescriptor>()));
   EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(AnyNumber());
   ASSERT_TRUE(RunExtensionTest("usb/list_interfaces"));
 }
diff --git a/chrome/browser/extensions/api/usb/usb_device_resource.h b/chrome/browser/extensions/api/usb/usb_device_resource.h
index 6592548..fd6e7ce 100644
--- a/chrome/browser/extensions/api/usb/usb_device_resource.h
+++ b/chrome/browser/extensions/api/usb/usb_device_resource.h
@@ -16,6 +16,7 @@
 #include "chrome/browser/extensions/api/api_resource_manager.h"
 #include "chrome/browser/usb/usb_device_handle.h"
 #include "chrome/common/extensions/api/usb.h"
+#include "content/public/browser/browser_thread.h"
 
 class UsbDeviceHandle;
 
@@ -36,6 +37,9 @@
     return device_;
   }
 
+  static const content::BrowserThread::ID kThreadId =
+      content::BrowserThread::FILE;
+
  private:
   friend class ApiResourceManager<UsbDeviceResource>;
   static const char* service_name() {
diff --git a/chrome/browser/extensions/api/web_navigation/frame_navigation_state.cc b/chrome/browser/extensions/api/web_navigation/frame_navigation_state.cc
index 11092cb..8df798b 100644
--- a/chrome/browser/extensions/api/web_navigation/frame_navigation_state.cc
+++ b/chrome/browser/extensions/api/web_navigation/frame_navigation_state.cc
@@ -16,7 +16,7 @@
 const char* kValidSchemes[] = {
   chrome::kChromeUIScheme,
   chrome::kHttpScheme,
-  chrome::kHttpsScheme,
+  content::kHttpsScheme,
   chrome::kFileScheme,
   chrome::kFtpScheme,
   content::kJavaScriptScheme,
diff --git a/chrome/browser/extensions/api/web_request/web_request_api.cc b/chrome/browser/extensions/api/web_request/web_request_api.cc
index 46fc10a..efb0924 100644
--- a/chrome/browser/extensions/api/web_request/web_request_api.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_api.cc
@@ -1241,6 +1241,7 @@
     void* profile,
     const std::string& extension_id,
     int embedder_process_id,
+    int embedder_routing_id,
     int webview_instance_id) {
   // Iterate over all listeners of all WebRequest events to delete
   // any listeners that belong to the provided <webview>.
@@ -1252,7 +1253,10 @@
     for (std::set<EventListener>::iterator listener_iter = listeners.begin();
          listener_iter != listeners.end(); ++listener_iter) {
       const EventListener& listener = *listener_iter;
+      // TODO(fsamuel): Investigate making <webview> instance IDs unique within
+      // a process.
       if (listener.embedder_process_id == embedder_process_id &&
+          listener.embedder_routing_id == embedder_routing_id &&
           listener.webview_instance_id == webview_instance_id)
         listeners_to_delete.push_back(listener);
     }
diff --git a/chrome/browser/extensions/api/web_request/web_request_api.h b/chrome/browser/extensions/api/web_request/web_request_api.h
index e0339fc..fba59b0 100644
--- a/chrome/browser/extensions/api/web_request/web_request_api.h
+++ b/chrome/browser/extensions/api/web_request/web_request_api.h
@@ -261,6 +261,7 @@
       void* profile,
       const std::string& extension_id,
       int embedder_process_id,
+      int embedder_routing_id,
       int web_view_instance_id);
 
   // Called when an incognito profile is created or destroyed.
diff --git a/chrome/browser/extensions/api/web_request/web_request_permissions.cc b/chrome/browser/extensions/api/web_request/web_request_permissions.cc
index 480bf84..4bad267 100644
--- a/chrome/browser/extensions/api/web_request/web_request_permissions.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_permissions.cc
@@ -71,7 +71,7 @@
           url.SchemeIs(chrome::kFileSystemScheme) ||
           url.SchemeIs(chrome::kFtpScheme) ||
           url.SchemeIs(chrome::kHttpScheme) ||
-          url.SchemeIs(chrome::kHttpsScheme) ||
+          url.SchemeIs(content::kHttpsScheme) ||
           url.SchemeIs(extensions::kExtensionScheme));
 }
 
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
index d245bd7..bea515c 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
@@ -463,7 +463,7 @@
   // entry is only valid for some number of minutes.
   scoped_ptr<WebstoreInstaller::Approval> approval(
       WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
-          profile(), params_->details.id, parsed_manifest_.Pass()));
+          profile(), params_->details.id, parsed_manifest_.Pass(), false));
   approval->use_app_installed_bubble = params_->details.app_install_bubble;
   approval->enable_launcher = params_->details.enable_launcher;
   // If we are enabling the launcher, we should not show the app list in order
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc b/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
index c2565f8..1b3f2ab 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
@@ -104,7 +104,7 @@
     // API functions.
     host_resolver()->AddRule("www.example.com", "127.0.0.1");
     ASSERT_TRUE(test_server()->Start());
-    ExtensionInstallUI::DisableFailureUIForTests();
+    ExtensionInstallUI::set_disable_failure_ui_for_tests();
   }
 
  protected:
diff --git a/chrome/browser/extensions/api/webview/webview_api.cc b/chrome/browser/extensions/api/webview/webview_api.cc
index 706e4ac..bee377d 100644
--- a/chrome/browser/extensions/api/webview/webview_api.cc
+++ b/chrome/browser/extensions/api/webview/webview_api.cc
@@ -74,6 +74,7 @@
 // TODO(lazyboy): Parameters in this extension function are similar (or a
 // sub-set) to BrowsingDataRemoverFunction. How can we share this code?
 bool WebviewClearDataFunction::RunImpl() {
+  content::RecordAction(content::UserMetricsAction("WebView.ClearData"));
   int instance_id = 0;
   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &instance_id));
 
@@ -209,6 +210,7 @@
 }
 
 bool WebviewGoFunction::RunImpl() {
+  content::RecordAction(content::UserMetricsAction("WebView.Go"));
   scoped_ptr<webview::Go::Params> params(webview::Go::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
 
@@ -228,6 +230,7 @@
 }
 
 bool WebviewReloadFunction::RunImpl() {
+  content::RecordAction(content::UserMetricsAction("WebView.Reload"));
   scoped_ptr<webview::Reload::Params> params(
       webview::Reload::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
@@ -271,6 +274,7 @@
 }
 
 bool WebviewStopFunction::RunImpl() {
+  content::RecordAction(content::UserMetricsAction("WebView.Stop"));
   scoped_ptr<webview::Stop::Params> params(
       webview::Stop::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
@@ -291,6 +295,7 @@
 }
 
 bool WebviewTerminateFunction::RunImpl() {
+  content::RecordAction(content::UserMetricsAction("WebView.Terminate"));
   scoped_ptr<webview::Terminate::Params> params(
       webview::Terminate::Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params.get());
diff --git a/chrome/browser/extensions/bundle_installer.cc b/chrome/browser/extensions/bundle_installer.cc
index 2249e2a..1e30f94 100644
--- a/chrome/browser/extensions/bundle_installer.cc
+++ b/chrome/browser/extensions/bundle_installer.cc
@@ -162,7 +162,7 @@
             profile_,
             i->first,
             scoped_ptr<base::DictionaryValue>(
-                parsed_manifests_[i->first]->DeepCopy())));
+                parsed_manifests_[i->first]->DeepCopy()), true));
     approval->use_app_installed_bubble = false;
     approval->skip_post_install_ui = true;
 
diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc
index fac1e88..2f793a2 100644
--- a/chrome/browser/extensions/crx_installer.cc
+++ b/chrome/browser/extensions/crx_installer.cc
@@ -40,6 +40,8 @@
 #include "chrome/common/extensions/feature_switch.h"
 #include "chrome/common/extensions/manifest_handlers/shared_module_info.h"
 #include "chrome/common/extensions/manifest_url_handler.h"
+#include "chrome/common/extensions/permissions/permission_set.h"
+#include "chrome/common/extensions/permissions/permissions_data.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/resource_dispatcher_host.h"
@@ -101,6 +103,7 @@
     : install_directory_(service_weak->install_directory()),
       install_source_(Manifest::INTERNAL),
       approved_(false),
+      expected_manifest_strict_checking_(true),
       extensions_enabled_(service_weak->extensions_enabled()),
       delete_source_(false),
       create_app_shortcut_(false),
@@ -127,7 +130,8 @@
   if (client_) {
     client_->install_ui()->SetUseAppInstalledBubble(
         approval->use_app_installed_bubble);
-    client_->install_ui()->SetSkipPostInstallUI(approval->skip_post_install_ui);
+    client_->install_ui()->set_skip_post_install_ui(
+        approval->skip_post_install_ui);
   }
 
   if (approval->skip_install_dialog) {
@@ -135,6 +139,7 @@
     // so we can check that they match the CRX's.
     approved_ = true;
     expected_manifest_.reset(approval->manifest->DeepCopy());
+    expected_manifest_strict_checking_ = approval->strict_manifest_check;
     expected_id_ = approval->extension_id;
   }
 
@@ -247,11 +252,30 @@
   }
 
   // Make sure the manifests match if we want to bypass the prompt.
-  if (approved_ &&
-      (!expected_manifest_.get() ||
-       !expected_manifest_->Equals(original_manifest_.get()))) {
-    return CrxInstallerError(
-        l10n_util::GetStringUTF16(IDS_EXTENSION_MANIFEST_INVALID));
+  if (approved_) {
+    bool valid = false;
+    if (expected_manifest_.get()) {
+      valid = expected_manifest_->Equals(original_manifest_.get());
+      if (!valid && !expected_manifest_strict_checking_) {
+        std::string error;
+        scoped_refptr<Extension> dummy_extension =
+            Extension::Create(base::FilePath(),
+                              install_source_,
+                              *expected_manifest_->value(),
+                              creation_flags_,
+                              &error);
+        if (error.empty()) {
+          scoped_refptr<const PermissionSet> expected_permissions =
+              PermissionsData::GetActivePermissions(dummy_extension.get());
+          valid = !(expected_permissions->HasLessPrivilegesThan(
+              PermissionsData::GetActivePermissions(extension),
+              extension->GetType()));
+        }
+      }
+    }
+    if (!valid)
+      return CrxInstallerError(
+          l10n_util::GetStringUTF16(IDS_EXTENSION_MANIFEST_INVALID));
   }
 
   // The checks below are skipped for themes and external installs.
@@ -494,7 +518,7 @@
     // because the WebStore already shows an error dialog itself.
     // Note: |client_| can be NULL in unit_tests!
     if (extension()->from_webstore() && client_)
-      client_->install_ui()->SetSkipPostInstallUI(true);
+      client_->install_ui()->set_skip_post_install_ui(true);
     ReportFailureFromUIThread(CrxInstallerError(error));
     return;
   }
diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h
index 1d40e2a..fde15b6 100644
--- a/chrome/browser/extensions/crx_installer.h
+++ b/chrome/browser/extensions/crx_installer.h
@@ -288,6 +288,11 @@
   // extension's manifest must match this for the install to proceed.
   scoped_ptr<Manifest> expected_manifest_;
 
+  // Set to true if we want a strict, exact match check between the actual and
+  // expected manifest, rather than just a check that the effective permissions
+  // are the same.
+  bool expected_manifest_strict_checking_;
+
   // If non-NULL, contains the expected version of the extension we're
   // installing.  Important for external sources, where claiming the wrong
   // version could cause unnecessary unpacking of an extension at every
diff --git a/chrome/browser/extensions/crx_installer_browsertest.cc b/chrome/browser/extensions/crx_installer_browsertest.cc
index c288691..8ebbfbe 100644
--- a/chrome/browser/extensions/crx_installer_browsertest.cc
+++ b/chrome/browser/extensions/crx_installer_browsertest.cc
@@ -129,41 +129,56 @@
 
 class ExtensionCrxInstallerTest : public ExtensionBrowserTest {
  public:
-  // Installs a crx from |crx_relpath| (a path relative to the extension test
-  // data dir) with expected id |id|. Returns the installer.
-  scoped_refptr<CrxInstaller> InstallWithPrompt(
-      const std::string& ext_relpath,
+  scoped_ptr<WebstoreInstaller::Approval> GetApproval(
+      const char* manifest_dir,
       const std::string& id,
-      scoped_refptr<MockPromptProxy> mock_install_prompt) {
+      bool strict_manifest_checks) {
+    scoped_ptr<WebstoreInstaller::Approval> result;
+
+    base::FilePath ext_path = test_data_dir_.AppendASCII(manifest_dir);
+    std::string error;
+    scoped_ptr<base::DictionaryValue> parsed_manifest(
+        extension_file_util::LoadManifest(ext_path, &error));
+    if (!parsed_manifest.get() || !error.empty())
+      return result.Pass();
+
+    return WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
+        browser()->profile(),
+        id,
+        parsed_manifest.Pass(),
+        strict_manifest_checks);
+  }
+
+  void RunCrxInstaller(const WebstoreInstaller::Approval* approval,
+                       scoped_ptr<ExtensionInstallPrompt> prompt,
+                       const base::FilePath& crx_path) {
     ExtensionService* service = extensions::ExtensionSystem::Get(
         browser()->profile())->extension_service();
-    base::FilePath ext_path = test_data_dir_.AppendASCII(ext_relpath);
-
-    std::string error;
-    base::DictionaryValue* parsed_manifest =
-        extension_file_util::LoadManifest(ext_path, &error);
-    if (!parsed_manifest)
-      return scoped_refptr<CrxInstaller>();
-
-    scoped_ptr<WebstoreInstaller::Approval> approval;
-    if (!id.empty()) {
-      approval = WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
-          browser()->profile(),
-          id,
-          scoped_ptr<base::DictionaryValue>(parsed_manifest));
-    }
-
     scoped_refptr<CrxInstaller> installer(
-        CrxInstaller::Create(service,
-                             mock_install_prompt->CreatePrompt(),
-                             approval.get()       /* keep ownership */));
+        CrxInstaller::Create(service, prompt.Pass(), approval));
     installer->set_allow_silent_install(true);
     installer->set_is_gallery_install(true);
-    installer->InstallCrx(PackExtension(ext_path));
+    installer->InstallCrx(crx_path);
     content::RunMessageLoop();
+  }
+
+  // Installs a crx from |crx_relpath| (a path relative to the extension test
+  // data dir) with expected id |id|.
+  void InstallWithPrompt(const char* ext_relpath,
+                         const std::string& id,
+                         scoped_refptr<MockPromptProxy> mock_install_prompt) {
+    base::FilePath ext_path = test_data_dir_.AppendASCII(ext_relpath);
+
+    scoped_ptr<WebstoreInstaller::Approval> approval;
+    if (!id.empty())
+      approval = GetApproval(ext_relpath, id, true);
+
+    base::FilePath crx_path = PackExtension(ext_path);
+    EXPECT_FALSE(crx_path.empty());
+    RunCrxInstaller(approval.get(), mock_install_prompt->CreatePrompt(),
+                    crx_path);
 
     EXPECT_TRUE(mock_install_prompt->did_succeed());
-    return installer;
   }
 
   // Installs an extension and checks that it has scopes granted IFF
@@ -180,8 +195,7 @@
         CreateMockPromptProxyForBrowser(browser());
 
     mock_prompt->set_record_oauth2_grant(record_oauth2_grant);
-    scoped_refptr<CrxInstaller> installer =
-        InstallWithPrompt("browsertest/scopes", std::string(), mock_prompt);
+    InstallWithPrompt("browsertest/scopes", std::string(), mock_prompt);
 
     scoped_refptr<PermissionSet> permissions =
         service->extension_prefs()->GetGrantedPermissions(
@@ -226,8 +240,7 @@
   // Even whitelisted extensions with NPAPI should not prompt.
   scoped_refptr<MockPromptProxy> mock_prompt =
       CreateMockPromptProxyForBrowser(browser());
-  scoped_refptr<CrxInstaller> installer =
-      InstallWithPrompt("uitest/plugins", id, mock_prompt);
+  InstallWithPrompt("uitest/plugins", id, mock_prompt);
   EXPECT_FALSE(mock_prompt->confirmation_requested());
   EXPECT_TRUE(service->GetExtensionById(id, false));
 }
@@ -447,4 +460,22 @@
   EXPECT_FALSE(InstallExtension(crx_path, 0));
 }
 
+IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, NonStrictManifestCheck) {
+  scoped_refptr<MockPromptProxy> mock_prompt =
+      CreateMockPromptProxyForBrowser(browser());
+
+  // We want to simulate the case where the webstore sends a more recent
+  // version of the manifest, but the downloaded .crx file is old since
+  // the newly published version hasn't fully propagated to all the download
+  // servers yet. So load the v2 manifest, but then install the v1 crx file.
+  std::string id = "lhnaeclnpobnlbjbgogdanmhadigfnjp";
+  scoped_ptr<WebstoreInstaller::Approval> approval =
+      GetApproval("crx_installer/v2_no_permission_change/", id, false);
+
+  RunCrxInstaller(approval.get(), mock_prompt->CreatePrompt(),
+                  test_data_dir_.AppendASCII("crx_installer/v1.crx"));
+
+  EXPECT_TRUE(mock_prompt->did_succeed());
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/error_console/error_console.cc b/chrome/browser/extensions/error_console/error_console.cc
index cff063c..2b0432f 100644
--- a/chrome/browser/extensions/error_console/error_console.cc
+++ b/chrome/browser/extensions/error_console/error_console.cc
@@ -6,12 +6,17 @@
 
 #include <list>
 
+#include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/lazy_instance.h"
+#include "base/prefs/pref_service.h"
 #include "base/stl_util.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/extension_system.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/feature_switch.h"
+#include "chrome/common/pref_names.h"
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
@@ -45,13 +50,26 @@
 void ErrorConsole::Observer::OnErrorConsoleDestroyed() {
 }
 
-ErrorConsole::ErrorConsole(Profile* profile) : profile_(profile) {
-  registrar_.Add(this,
-                 chrome::NOTIFICATION_PROFILE_DESTROYED,
-                 content::NotificationService::AllBrowserContextsAndSources());
-  registrar_.Add(this,
-                 chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
-                 content::Source<Profile>(profile_));
+ErrorConsole::ErrorConsole(Profile* profile) : enabled_(false),
+                                               profile_(profile) {
+// TODO(rdevlin.cronin): Remove once crbug.com/159265 is fixed.
+#if !defined(ENABLE_EXTENSIONS)
+  return;
+#endif
+
+  // If we don't have the necessary FeatureSwitch enabled, then return
+  // immediately. Since we never register for any notifications, this ensures
+  // the ErrorConsole will never be enabled.
+  if (!FeatureSwitch::error_console()->IsEnabled())
+    return;
+
+  pref_registrar_.Init(profile_->GetPrefs());
+  pref_registrar_.Add(prefs::kExtensionsUIDeveloperMode,
+                      base::Bind(&ErrorConsole::OnPrefChanged,
+                                 base::Unretained(this)));
+
+  if (profile_->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode))
+    Enable();
 }
 
 ErrorConsole::~ErrorConsole() {
@@ -64,20 +82,40 @@
   return ExtensionSystem::Get(profile)->error_console();
 }
 
-void ErrorConsole::ReportError(scoped_ptr<const ExtensionError> scoped_error) {
+void ErrorConsole::ReportError(scoped_ptr<ExtensionError> error) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  const ExtensionError* error = scoped_error.release();
+  if (!enabled_ || !Extension::IdIsValid(error->extension_id()))
+    return;
+
+  ErrorList* extension_errors = &errors_[error->extension_id()];
+
+  // First, check if it's a duplicate.
+  for (ErrorList::iterator iter = extension_errors->begin();
+       iter != extension_errors->end(); ++iter) {
+    // If we find a duplicate error, remove the old error and add the new one,
+    // incrementing the occurrence count of the error. We use the new error
+    // for runtime errors, so we can link to the latest context, inspectable
+    // view, etc.
+    if (error->IsEqual(*iter)) {
+      error->set_occurrences((*iter)->occurrences() + 1);
+      delete *iter;
+      extension_errors->erase(iter);
+      break;
+    }
+  }
+
   // If there are too many errors for an extension already, limit ourselves to
   // the most recent ones.
-  ErrorList* error_list = &errors_[error->extension_id()];
-  if (error_list->size() >= kMaxErrorsPerExtension) {
-    delete error_list->front();
-    error_list->pop_front();
+  if (extension_errors->size() >= kMaxErrorsPerExtension) {
+    delete extension_errors->front();
+    extension_errors->pop_front();
   }
-  error_list->push_back(error);
 
-  FOR_EACH_OBSERVER(Observer, observers_, OnErrorAdded(error));
+  extension_errors->push_back(error.release());
+
+  FOR_EACH_OBSERVER(
+      Observer, observers_, OnErrorAdded(extension_errors->back()));
 }
 
 const ErrorConsole::ErrorList& ErrorConsole::GetErrorsForExtension(
@@ -98,6 +136,35 @@
   observers_.RemoveObserver(observer);
 }
 
+void ErrorConsole::OnPrefChanged() {
+  bool developer_mode =
+      profile_->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode);
+
+  if (developer_mode && !enabled_)
+    Enable();
+  else if (!developer_mode && enabled_)
+    Disable();
+}
+
+void ErrorConsole::Enable() {
+  enabled_ = true;
+
+  notification_registrar_.Add(
+      this,
+      chrome::NOTIFICATION_PROFILE_DESTROYED,
+      content::NotificationService::AllBrowserContextsAndSources());
+  notification_registrar_.Add(
+      this,
+      chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
+      content::Source<Profile>(profile_));
+}
+
+void ErrorConsole::Disable() {
+  notification_registrar_.RemoveAll();
+  RemoveAllErrors();
+  enabled_ = false;
+}
+
 void ErrorConsole::RemoveIncognitoErrors() {
   for (ErrorMap::iterator iter = errors_.begin();
        iter != errors_.end(); ++iter) {
diff --git a/chrome/browser/extensions/error_console/error_console.h b/chrome/browser/extensions/error_console/error_console.h
index c60bda9..6c8ebb5 100644
--- a/chrome/browser/extensions/error_console/error_console.h
+++ b/chrome/browser/extensions/error_console/error_console.h
@@ -11,6 +11,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/observer_list.h"
+#include "base/prefs/pref_change_registrar.h"
 #include "base/strings/string16.h"
 #include "base/threading/thread_checker.h"
 #include "content/public/browser/notification_observer.h"
@@ -55,7 +56,7 @@
   static ErrorConsole* Get(Profile* profile);
 
   // Report an extension error, and add it to the list.
-  void ReportError(scoped_ptr<const ExtensionError> error);
+  void ReportError(scoped_ptr<ExtensionError> error);
 
   // Get a collection of weak pointers to all errors relating to the extension
   // with the given |extension_id|.
@@ -66,11 +67,22 @@
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
-  const ErrorMap& errors() { return errors_; }
+  const ErrorMap& errors() const { return errors_; }
 
  private:
   FRIEND_TEST_ALL_PREFIXES(ErrorConsoleUnitTest, AddAndRemoveErrors);
 
+  // Enable the error console for error collection and retention.
+  void Enable();
+  // Disable the error console, removing the subscriptions to notifications and
+  // removing all current errors.
+  void Disable();
+
+  // Called when the Developer Mode preference is changed; this is important
+  // since we use this as a heuristic to determine if the console is enabled or
+  // not.
+  void OnPrefChanged();
+
   // Remove all errors which happened while incognito; we have to do this once
   // the incognito profile is destroyed.
   void RemoveIncognitoErrors();
@@ -86,6 +98,11 @@
                        const content::NotificationSource& source,
                        const content::NotificationDetails& details) OVERRIDE;
 
+  // Whether or not the error console is enabled; it is enabled if the
+  // FeatureSwitch (FeatureSwitch::error_console) is enabled and the user is
+  // in Developer Mode.
+  bool enabled_;
+
   // Needed because ObserverList is not thread-safe.
   base::ThreadChecker thread_checker_;
 
@@ -100,7 +117,8 @@
   // incognito fellow).
   Profile* profile_;
 
-  content::NotificationRegistrar registrar_;
+  content::NotificationRegistrar notification_registrar_;
+  PrefChangeRegistrar pref_registrar_;
 
   DISALLOW_COPY_AND_ASSIGN(ErrorConsole);
 };
diff --git a/chrome/browser/extensions/error_console/error_console_unittest.cc b/chrome/browser/extensions/error_console/error_console_unittest.cc
index 62b5320..e79c96f 100644
--- a/chrome/browser/extensions/error_console/error_console_unittest.cc
+++ b/chrome/browser/extensions/error_console/error_console_unittest.cc
@@ -7,13 +7,18 @@
 #include "base/json/json_writer.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/prefs/pref_service.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/common/extensions/feature_switch.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/common/url_constants.h"
 #include "extensions/browser/extension_error.h"
 #include "extensions/common/constants.h"
+#include "extensions/common/id_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using base::string16;
@@ -39,11 +44,11 @@
   return UTF8ToUTF16(json_utf8);
 }
 
-scoped_ptr<const ExtensionError> CreateNewRuntimeError(
+scoped_ptr<ExtensionError> CreateNewRuntimeError(
     bool from_incognito,
     const std::string& extension_id,
     const string16& message) {
-  return scoped_ptr<const ExtensionError>(new JavascriptRuntimeError(
+  return scoped_ptr<ExtensionError>(new RuntimeError(
       from_incognito,
       UTF8ToUTF16("source"),
       message,
@@ -55,12 +60,21 @@
 
 class ErrorConsoleUnitTest : public testing::Test {
  public:
-  ErrorConsoleUnitTest() :
-      profile_(new TestingProfile),
-      error_console_(ErrorConsole::Get(profile_.get())) {
-  }
+  ErrorConsoleUnitTest() : error_console_(NULL) { }
   virtual ~ErrorConsoleUnitTest() { }
 
+  virtual void SetUp() OVERRIDE {
+    testing::Test::SetUp();
+
+    // Errors are only kept if we have the FeatureSwitch and have Developer Mode
+    // enabled.
+    FeatureSwitch::error_console()->SetOverrideValue(
+        FeatureSwitch::OVERRIDE_ENABLED);
+    profile_.reset(new TestingProfile);
+    profile_->GetPrefs()->SetBoolean(prefs::kExtensionsUIDeveloperMode, true);
+    error_console_ = ErrorConsole::Get(profile_.get());
+  }
+
  protected:
   scoped_ptr<TestingProfile> profile_;
   ErrorConsole* error_console_;
@@ -73,11 +87,11 @@
 
   const size_t kNumTotalErrors = 6;
   const size_t kNumNonIncognitoErrors = 3;
-  const char kId[] = "id";
+  const std::string kId = id_util::GenerateId("id");
   // Populate with both incognito and non-incognito errors (evenly distributed).
   for (size_t i = 0; i < kNumTotalErrors; ++i) {
     error_console_->ReportError(
-        CreateNewRuntimeError(i % 2 == 0, kId, string16()));
+        CreateNewRuntimeError(i % 2 == 0, kId, base::UintToString16(i)));
   }
 
   // There should only be one entry in the map, since errors are stored in lists
@@ -96,7 +110,7 @@
     ASSERT_FALSE(errors[i]->from_incognito());
 
   // Add another error for a different extension id.
-  const char kSecondId[] = "id2";
+  const std::string kSecondId = id_util::GenerateId("id2");
   error_console_->ReportError(
       CreateNewRuntimeError(false, kSecondId, string16()));
 
@@ -127,7 +141,7 @@
   // This constant matches one of the same name in error_console.cc.
   const size_t kMaxErrorsPerExtension = 100;
   const size_t kNumExtraErrors = 5;
-  const char kId[] = "id";
+  const std::string kId = id_util::GenerateId("id");
 
   // Add new errors, with each error's message set to its number.
   for (size_t i = 0; i < kMaxErrorsPerExtension + kNumExtraErrors; ++i) {
@@ -143,11 +157,47 @@
 
   // We should have popped off errors in the order they arrived, so the
   // first stored error should be the 6th reported (zero-based)...
-  ASSERT_EQ(errors.front()->message(),
-            base::UintToString16(kNumExtraErrors));
+  ASSERT_EQ(base::UintToString16(kNumExtraErrors),
+            errors.front()->message());
   // ..and the last stored should be the 105th reported.
-  ASSERT_EQ(errors.back()->message(),
-            base::UintToString16(kMaxErrorsPerExtension + kNumExtraErrors - 1));
+  ASSERT_EQ(base::UintToString16(kMaxErrorsPerExtension + kNumExtraErrors - 1),
+            errors.back()->message());
+}
+
+// Test to ensure that the error console will not add duplicate errors, but will
+// keep the latest version of an error.
+TEST_F(ErrorConsoleUnitTest, DuplicateErrorsAreReplaced) {
+  ASSERT_EQ(0u, error_console_->errors().size());
+
+  const std::string kId = id_util::GenerateId("id");
+  const size_t kNumErrors = 3u;
+
+  // Report three errors.
+  for (size_t i = 0; i < kNumErrors; ++i) {
+    error_console_->ReportError(
+        CreateNewRuntimeError(false, kId, base::UintToString16(i)));
+  }
+
+  // Create an error identical to the second error reported, and save its
+  // location.
+  scoped_ptr<ExtensionError> runtime_error2 =
+      CreateNewRuntimeError(false, kId, base::UintToString16(1u));
+  const ExtensionError* weak_error = runtime_error2.get();
+
+  // Add it to the error console.
+  error_console_->ReportError(runtime_error2.Pass());
+
+  // We should only have three errors stored, since two of the four reported
+  // were identical, and the older should have been replaced.
+  ASSERT_EQ(1u, error_console_->errors().size());
+  const ErrorConsole::ErrorList& errors =
+      error_console_->GetErrorsForExtension(kId);
+  ASSERT_EQ(kNumErrors, errors.size());
+
+  // The duplicate error should be the last reported (pointer comparison)...
+  ASSERT_EQ(weak_error, errors.back());
+  // ... and should have two reported occurrences.
+  ASSERT_EQ(2u, errors.back()->occurrences());
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_action.cc b/chrome/browser/extensions/extension_action.cc
index 7d3ecd1..a5712c8 100644
--- a/chrome/browser/extensions/extension_action.cc
+++ b/chrome/browser/extensions/extension_action.cc
@@ -15,8 +15,8 @@
 #include "grit/theme_resources.h"
 #include "grit/ui_resources.h"
 #include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
 #include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkDevice.h"
 #include "third_party/skia/include/core/SkPaint.h"
 #include "third_party/skia/include/effects/SkGradientShader.h"
 #include "ui/base/animation/animation_delegate.h"
@@ -113,7 +113,7 @@
   if (!device_.get() ||
       (device_->width() != icon.width()) ||
       (device_->height() != icon.height())) {
-    device_.reset(new SkDevice(
+    device_.reset(new SkBitmapDevice(
       SkBitmap::kARGB_8888_Config, icon.width(), icon.height(), true));
   }
 
diff --git a/chrome/browser/extensions/extension_action.h b/chrome/browser/extensions/extension_action.h
index 943a3eb..c7f1fac 100644
--- a/chrome/browser/extensions/extension_action.h
+++ b/chrome/browser/extensions/extension_action.h
@@ -18,11 +18,12 @@
 #include "chrome/common/extensions/api/extension_action/action_info.h"
 #include "chrome/common/extensions/extension_icon_set.h"
 #include "third_party/skia/include/core/SkColor.h"
+// TODO(robertphillips): change this to "class SkBaseDevice;"
+#include "third_party/skia/include/core/SkDevice.h"
 #include "ui/base/animation/linear_animation.h"
 
 class GURL;
 class SkBitmap;
-class SkDevice;
 
 namespace gfx {
 class Canvas;
@@ -103,7 +104,7 @@
     virtual void AnimateToState(double state) OVERRIDE;
 
     // Device we use to paint icons to.
-    mutable scoped_ptr<SkDevice> device_;
+    mutable scoped_ptr<SkBaseDevice> device_;
 
     ObserverList<Observer> observers_;
 
diff --git a/chrome/browser/extensions/extension_function_histogram_value.h b/chrome/browser/extensions/extension_function_histogram_value.h
index ae24aee..edcdd52 100644
--- a/chrome/browser/extensions/extension_function_histogram_value.h
+++ b/chrome/browser/extensions/extension_function_histogram_value.h
@@ -368,7 +368,7 @@
   TYPES_CHROMESETTING_SET,
   BROWSERACTION_SETICON,
   EXPERIMENTAL_ACCESSIBILITY_SETACCESSIBILITYENABLED,
-  FILEBROWSERPRIVATE_VIEWFILES,
+  DELETED_FILEBROWSERPRIVATE_VIEWFILES,
   BLUETOOTH_GETSERVICES,
   TABS_UPDATE,
   BROWSINGDATA_REMOVEFORMDATA,
@@ -614,6 +614,10 @@
   SESSIONS_GETRECENTLYCLOSED,
   SESSIONS_GETDEVICES,
   SESSIONS_RESTORE,
+  SYNCFILESYSTEM_GETSERVICESTATUS,
+  ECHOPRIVATE_SETOFFERINFO,
+  ECHOPRIVATE_GETOFFERINFO,
+  DEVELOPERPRIVATE_ISPROFILEMANAGED,
   ENUM_BOUNDARY // Last entry: Add new entries above.
 };
 
diff --git a/chrome/browser/extensions/extension_host.cc b/chrome/browser/extensions/extension_host.cc
index 7388727..d7cd528 100644
--- a/chrome/browser/extensions/extension_host.cc
+++ b/chrome/browser/extensions/extension_host.cc
@@ -44,6 +44,7 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_view.h"
@@ -287,6 +288,34 @@
       content::Details<ExtensionHost>(this));
 }
 
+#if !defined(OS_ANDROID)
+web_modal::WebContentsModalDialogHost*
+ExtensionHost::GetWebContentsModalDialogHost() {
+  return this;
+}
+
+gfx::NativeView ExtensionHost::GetHostView() const {
+  return view_ ? view_->native_view() : NULL;
+}
+
+gfx::Point ExtensionHost::GetDialogPosition(const gfx::Size& size) {
+  if (!GetVisibleWebContents())
+    return gfx::Point();
+  gfx::Rect bounds = GetVisibleWebContents()->GetView()->GetViewBounds();
+  return gfx::Point(
+      std::max(0, (bounds.width() - size.width()) / 2),
+      std::max(0, (bounds.height() - size.height()) / 2));
+}
+
+void ExtensionHost::AddObserver(
+    web_modal::WebContentsModalDialogHostObserver* observer) {
+}
+
+void ExtensionHost::RemoveObserver(
+    web_modal::WebContentsModalDialogHostObserver* observer) {
+}
+#endif
+
 void ExtensionHost::Observe(int type,
                             const content::NotificationSource& source,
                             const content::NotificationDetails& details) {
diff --git a/chrome/browser/extensions/extension_host.h b/chrome/browser/extensions/extension_host.h
index f4e8886..fe97ef2 100644
--- a/chrome/browser/extensions/extension_host.h
+++ b/chrome/browser/extensions/extension_host.h
@@ -10,7 +10,7 @@
 
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/perftimer.h"
+#include "base/test/perftimer.h"
 #include "chrome/browser/extensions/extension_function_dispatcher.h"
 #include "content/public/browser/javascript_dialog_manager.h"
 #include "content/public/browser/notification_observer.h"
@@ -31,6 +31,7 @@
 
 #if !defined(OS_ANDROID)
 #include "chrome/browser/ui/chrome_web_modal_dialog_manager_delegate.h"
+#include "components/web_modal/web_contents_modal_dialog_host.h"
 #endif
 
 class Browser;
@@ -53,6 +54,7 @@
 class ExtensionHost : public content::WebContentsDelegate,
 #if !defined(OS_ANDROID)
                       public ChromeWebModalDialogManagerDelegate,
+                      public web_modal::WebContentsModalDialogHost,
 #endif
                       public content::WebContentsObserver,
                       public ExtensionFunctionDispatcher::Delegate,
@@ -197,6 +199,20 @@
   // Closes this host (results in deletion).
   void Close();
 
+#if !defined(OS_ANDROID)
+  // ChromeWebModalDialogManagerDelegate
+  virtual web_modal::WebContentsModalDialogHost*
+      GetWebContentsModalDialogHost() OVERRIDE;
+
+  // web_modal::WebContentsModalDialogHost
+  virtual gfx::NativeView GetHostView() const OVERRIDE;
+  virtual gfx::Point GetDialogPosition(const gfx::Size& size) OVERRIDE;
+  virtual void AddObserver(
+      web_modal::WebContentsModalDialogHostObserver* observer) OVERRIDE;
+  virtual void RemoveObserver(
+      web_modal::WebContentsModalDialogHostObserver* observer) OVERRIDE;
+#endif
+
   // ExtensionFunctionDispatcher::Delegate
   virtual WindowController* GetExtensionWindowController() const OVERRIDE;
 
diff --git a/chrome/browser/extensions/extension_install_prompt.cc b/chrome/browser/extensions/extension_install_prompt.cc
index 544c04e..7045625 100644
--- a/chrome/browser/extensions/extension_install_prompt.cc
+++ b/chrome/browser/extensions/extension_install_prompt.cc
@@ -188,6 +188,7 @@
 
 ExtensionInstallPrompt::Prompt::Prompt(PromptType type)
     : type_(type),
+      is_showing_details_for_retained_files_(false),
       extension_(NULL),
       bundle_(NULL),
       average_rating_(0.0),
@@ -206,10 +207,34 @@
 void ExtensionInstallPrompt::Prompt::SetPermissionsDetails(
     const std::vector<string16>& details) {
   details_ = details;
+  is_showing_details_for_permissions_.clear();
+  for (size_t i = 0; i < details.size(); ++i)
+    is_showing_details_for_permissions_.push_back(false);
+}
+
+void ExtensionInstallPrompt::Prompt::SetIsShowingDetails(
+    DetailsType type,
+    size_t index,
+    bool is_showing_details) {
+  switch (type) {
+    case PERMISSIONS_DETAILS:
+      is_showing_details_for_permissions_[index] = is_showing_details;
+      break;
+    case OAUTH_DETAILS:
+      is_showing_details_for_oauth_[index] = is_showing_details;
+      break;
+    case RETAINED_FILES_DETAILS:
+      is_showing_details_for_retained_files_ = is_showing_details;
+      break;
+  }
 }
 
 void ExtensionInstallPrompt::Prompt::SetOAuthIssueAdvice(
     const IssueAdviceInfo& issue_advice) {
+  is_showing_details_for_oauth_.clear();
+  for (size_t i = 0; i < issue_advice.size(); ++i)
+    is_showing_details_for_oauth_.push_back(false);
+
   oauth_issue_advice_ = issue_advice;
 }
 
@@ -407,6 +432,21 @@
   return details_[index];
 }
 
+bool ExtensionInstallPrompt::Prompt::GetIsShowingDetails(
+    DetailsType type, size_t index) const {
+  switch (type) {
+    case PERMISSIONS_DETAILS:
+      CHECK_LT(index, is_showing_details_for_permissions_.size());
+      return is_showing_details_for_permissions_[index];
+    case OAUTH_DETAILS:
+      CHECK_LT(index, is_showing_details_for_oauth_.size());
+      return is_showing_details_for_oauth_[index];
+    case RETAINED_FILES_DETAILS:
+      return is_showing_details_for_retained_files_;
+  }
+  return false;
+}
+
 size_t ExtensionInstallPrompt::Prompt::GetOAuthIssueCount() const {
   return oauth_issue_advice_.size();
 }
diff --git a/chrome/browser/extensions/extension_install_prompt.h b/chrome/browser/extensions/extension_install_prompt.h
index 1a98bad..9ad8019 100644
--- a/chrome/browser/extensions/extension_install_prompt.h
+++ b/chrome/browser/extensions/extension_install_prompt.h
@@ -14,9 +14,9 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/strings/string16.h"
 #include "chrome/browser/extensions/crx_installer_error.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "extensions/common/url_pattern.h"
 #include "google_apis/gaia/oauth2_mint_token_flow.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_skia.h"
@@ -63,6 +63,12 @@
     NUM_PROMPT_TYPES
   };
 
+  enum DetailsType {
+    PERMISSIONS_DETAILS = 0,
+    OAUTH_DETAILS,
+    RETAINED_FILES_DETAILS,
+  };
+
   // Extra information needed to display an installation or uninstallation
   // prompt. Gets populated with raw data and exposes getters for formatted
   // strings so that the GTK/views/Cocoa install dialogs don't have to repeat
@@ -76,6 +82,9 @@
     void SetPermissions(const std::vector<string16>& permissions);
     // Sets the permission list details for this prompt.
     void SetPermissionsDetails(const std::vector<string16>& details);
+    void SetIsShowingDetails(DetailsType type,
+                             size_t index,
+                             bool is_showing_details);
     void SetInlineInstallWebstoreData(const std::string& localized_user_count,
                                       bool show_user_count,
                                       double average_rating,
@@ -116,6 +125,7 @@
     size_t GetPermissionsDetailsCount() const;
     string16 GetPermission(size_t index) const;
     string16 GetPermissionsDetails(size_t index) const;
+    bool GetIsShowingDetails(DetailsType type, size_t index) const;
     size_t GetOAuthIssueCount() const;
     const IssueAdviceInfoEntry& GetOAuthIssue(size_t index) const;
     size_t GetRetainedFileCount() const;
@@ -150,6 +160,9 @@
     // permissions if only additional ones are being requested)
     std::vector<string16> permissions_;
     std::vector<string16> details_;
+    std::vector<bool> is_showing_details_for_permissions_;
+    std::vector<bool> is_showing_details_for_oauth_;
+    bool is_showing_details_for_retained_files_;
 
     // Descriptions and details for OAuth2 permissions to display to the user.
     // These correspond to permission scopes.
diff --git a/chrome/browser/extensions/extension_install_ui.cc b/chrome/browser/extensions/extension_install_ui.cc
index b4c8868..55ea085 100644
--- a/chrome/browser/extensions/extension_install_ui.cc
+++ b/chrome/browser/extensions/extension_install_ui.cc
@@ -4,6 +4,11 @@
 
 #include "chrome/browser/extensions/extension_install_ui.h"
 
-ExtensionInstallUI::ExtensionInstallUI(Profile* profile) : profile_(profile) {}
+// static
+bool ExtensionInstallUI::disable_failure_ui_for_tests_ = false;
+
+ExtensionInstallUI::ExtensionInstallUI(Profile* profile)
+    : profile_(profile),
+      skip_post_install_ui_(false) {}
 
 ExtensionInstallUI::~ExtensionInstallUI() {}
diff --git a/chrome/browser/extensions/extension_install_ui.h b/chrome/browser/extensions/extension_install_ui.h
index a0211ca..dc43b78 100644
--- a/chrome/browser/extensions/extension_install_ui.h
+++ b/chrome/browser/extensions/extension_install_ui.h
@@ -35,8 +35,6 @@
   // Called when an extension failed to install.
   virtual void OnInstallFailure(const extensions::CrxInstallerError& error) = 0;
 
-  // Whether or not to show the default UI after completing the installation.
-  virtual void SetSkipPostInstallUI(bool skip_ui) = 0;
 
   // TODO(asargent) Normally we navigate to the new tab page when an app is
   // installed, but we're experimenting with instead showing a bubble when
@@ -44,12 +42,19 @@
   // the default behavior in the future.
   virtual void SetUseAppInstalledBubble(bool use_bubble) = 0;
 
+  // Whether or not to show the default UI after completing the installation.
+  void set_skip_post_install_ui(bool skip_ui) {
+    skip_post_install_ui_ = skip_ui;
+  }
+
   // Opens apps UI and animates the app icon for the app with id |app_id|.
   static void OpenAppInstalledUI(Profile* profile, const std::string& app_id);
 
-  // Disables showing UI (ErrorBox, etc.) for install failures. To be used only
-  // in tests.
-  static void DisableFailureUIForTests();
+#if defined(UNIT_TEST)
+  static void set_disable_failure_ui_for_tests() {
+    disable_failure_ui_for_tests_ = true;
+  }
+#endif
 
   // Creates an ExtensionInstallPrompt from |browser|.
   // Caller assumes ownership.
@@ -67,9 +72,20 @@
  protected:
   explicit ExtensionInstallUI(Profile* profile);
 
+  static bool disable_failure_ui_for_tests() {
+    return disable_failure_ui_for_tests_;
+  }
+
+  bool skip_post_install_ui() const { return skip_post_install_ui_; }
+
  private:
+  static bool disable_failure_ui_for_tests_;
+
   Profile* profile_;
 
+  // Whether or not to show the default UI after completing the installation.
+  bool skip_post_install_ui_;
+
   DISALLOW_COPY_AND_ASSIGN(ExtensionInstallUI);
 };
 
diff --git a/chrome/browser/extensions/external_provider_impl.cc b/chrome/browser/extensions/external_provider_impl.cc
index 811adc2..9f78b2b 100644
--- a/chrome/browser/extensions/external_provider_impl.cc
+++ b/chrome/browser/extensions/external_provider_impl.cc
@@ -371,6 +371,7 @@
   bool is_chromeos_demo_session = false;
   int bundled_extension_creation_flags = Extension::NO_FLAGS;
 #if defined(OS_CHROMEOS)
+  typedef chromeos::ExternalPrefCacheLoader PrefLoader;
   chromeos::UserManager* user_manager = chromeos::UserManager::Get();
   is_chromeos_demo_session =
       user_manager && user_manager->IsLoggedInAsDemoUser() &&
@@ -378,33 +379,58 @@
           policy::DEVICE_MODE_RETAIL_KIOSK;
   bundled_extension_creation_flags = Extension::FROM_WEBSTORE |
       Extension::WAS_INSTALLED_BY_DEFAULT;
-#endif
-
-  bool is_managed_profile = profile->IsManaged();
-  int external_apps_path_id = chrome::DIR_EXTERNAL_EXTENSIONS;
-  if (is_managed_profile)
-    external_apps_path_id = chrome::DIR_MANAGED_USERS_DEFAULT_APPS;
-
-#if defined(OS_CHROMEOS)
-  typedef chromeos::ExternalPrefCacheLoader PrefLoader;
 #else
   typedef ExternalPrefLoader PrefLoader;
 #endif
 
+#if defined(OS_LINUX)
   if (!is_chromeos_demo_session) {
+    int external_apps_path_id = profile->IsManaged() ?
+        chrome::DIR_MANAGED_USERS_DEFAULT_APPS :
+        chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS;
+
     provider_list->push_back(
         linked_ptr<ExternalProviderInterface>(
             new ExternalProviderImpl(
                 service,
                 new PrefLoader(external_apps_path_id,
-                               check_admin_permissions_on_mac),
+                               ExternalPrefLoader::NONE),
                 profile,
                 Manifest::EXTERNAL_PREF,
                 Manifest::EXTERNAL_PREF_DOWNLOAD,
                 bundled_extension_creation_flags)));
   }
+#endif
 
-  if (!is_managed_profile) {
+#if defined(OS_CHROMEOS)
+  policy::AppPackUpdater* app_pack_updater =
+      g_browser_process->browser_policy_connector()->GetAppPackUpdater();
+  if (is_chromeos_demo_session && app_pack_updater &&
+      !app_pack_updater->created_external_loader()) {
+    provider_list->push_back(
+        linked_ptr<ExternalProviderInterface>(
+          new ExternalProviderImpl(
+              service,
+              app_pack_updater->CreateExternalLoader(),
+              profile,
+              Manifest::EXTERNAL_PREF,
+              Manifest::INVALID_LOCATION,
+              Extension::NO_FLAGS)));
+  }
+#endif
+
+  if (!profile->IsManaged() && !is_chromeos_demo_session) {
+    provider_list->push_back(
+        linked_ptr<ExternalProviderInterface>(
+            new ExternalProviderImpl(
+                service,
+                new ExternalPrefLoader(chrome::DIR_EXTERNAL_EXTENSIONS,
+                                       check_admin_permissions_on_mac),
+                profile,
+                Manifest::EXTERNAL_PREF,
+                Manifest::EXTERNAL_PREF_DOWNLOAD,
+                bundled_extension_creation_flags)));
+
 #if defined(OS_CHROMEOS) || defined (OS_MACOSX)
     // Define a per-user source of external extensions.
     // On Chrome OS, this serves as a source for OEM customization.
@@ -413,7 +439,7 @@
             new ExternalProviderImpl(
                 service,
                 new ExternalPrefLoader(chrome::DIR_USER_EXTERNAL_EXTENSIONS,
-                                      ExternalPrefLoader::NONE),
+                                       ExternalPrefLoader::NONE),
                 profile,
                 Manifest::EXTERNAL_PREF,
                 Manifest::EXTERNAL_PREF_DOWNLOAD,
@@ -432,20 +458,6 @@
                 Extension::NO_FLAGS)));
 #endif
 
-#if defined(OS_LINUX)
-    provider_list->push_back(
-        linked_ptr<ExternalProviderInterface>(
-            new ExternalProviderImpl(
-                service,
-                new ExternalPrefLoader(
-                    chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS,
-                    ExternalPrefLoader::NONE),
-                profile,
-                Manifest::EXTERNAL_PREF,
-                Manifest::EXTERNAL_PREF_DOWNLOAD,
-                bundled_extension_creation_flags)));
-#endif
-
 #if !defined(OS_CHROMEOS)
     // The default apps are installed as INTERNAL but use the external
     // extension installer codeflow.
@@ -462,25 +474,7 @@
                     Extension::WAS_INSTALLED_BY_DEFAULT)));
 #endif
 
-#if defined(OS_CHROMEOS)
-    policy::AppPackUpdater* app_pack_updater =
-        g_browser_process->browser_policy_connector()->GetAppPackUpdater();
-    if (is_chromeos_demo_session && app_pack_updater &&
-        !app_pack_updater->created_external_loader()) {
-      provider_list->push_back(
-          linked_ptr<ExternalProviderInterface>(
-            new ExternalProviderImpl(
-                service,
-                app_pack_updater->CreateExternalLoader(),
-                profile,
-                Manifest::EXTERNAL_PREF,
-                Manifest::INVALID_LOCATION,
-                Extension::NO_FLAGS)));
-    }
-#endif
-  }
-
-  provider_list->push_back(
+    provider_list->push_back(
       linked_ptr<ExternalProviderInterface>(
         new ExternalProviderImpl(
             service,
@@ -489,6 +483,7 @@
             Manifest::INVALID_LOCATION,
             Manifest::EXTERNAL_POLICY_DOWNLOAD,
             Extension::FROM_WEBSTORE | Extension::WAS_INSTALLED_BY_DEFAULT)));
+  }
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/lazy_background_page_apitest.cc b/chrome/browser/extensions/lazy_background_page_apitest.cc
index e428704..db903db 100644
--- a/chrome/browser/extensions/lazy_background_page_apitest.cc
+++ b/chrome/browser/extensions/lazy_background_page_apitest.cc
@@ -458,5 +458,50 @@
   EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
 }
 
+// Tests that the lazy background page updates the chrome://extensions page
+// when it is destroyed.
+IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, UpdateExtensionsPage) {
+  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIExtensionsURL));
+
+  ResultCatcher catcher;
+  base::FilePath extdir = test_data_dir_.AppendASCII("lazy_background_page").
+      AppendASCII("wait_for_view");
+  const Extension* extension = LoadExtension(extdir);
+  ASSERT_TRUE(extension);
+  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
+
+  // The extension should've opened a new tab to an extension page.
+  EXPECT_EQ(extension->GetResourceURL("extension_page.html").spec(),
+            browser()->tab_strip_model()->GetActiveWebContents()->
+                GetURL().spec());
+
+  // Lazy Background Page still exists, because the extension created a new tab
+  // to an extension page.
+  ExtensionProcessManager* pm =
+      extensions::ExtensionSystem::Get(browser()->profile())->process_manager();
+  EXPECT_TRUE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
+
+  // Close the new tab.
+  LazyBackgroundObserver page_complete;
+  browser()->tab_strip_model()->CloseWebContentsAt(
+      browser()->tab_strip_model()->active_index(), TabStripModel::CLOSE_NONE);
+  page_complete.WaitUntilClosed();
+
+  // Lazy Background Page has been shut down.
+  EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
+
+  // Verify that extensions page shows that the lazy background page is
+  // inactive.
+  bool is_inactive;
+  EXPECT_TRUE(content::ExecuteScriptInFrameAndExtractBool(
+      browser()->tab_strip_model()->GetActiveWebContents(),
+      "//iframe[starts-with(@src, 'chrome://extension')]",
+      "var ele = document.querySelectorAll('div.active-views');"
+      "window.domAutomationController.send("
+      "    ele[0].innerHTML.search('(Inactive)') > 0);",
+      &is_inactive));
+  EXPECT_TRUE(is_inactive);
+}
+
 // TODO: background page with timer.
 // TODO: background page that interacts with popup.
diff --git a/chrome/browser/extensions/script_executor.cc b/chrome/browser/extensions/script_executor.cc
index 9e4aecd..41ffd49 100644
--- a/chrome/browser/extensions/script_executor.cc
+++ b/chrome/browser/extensions/script_executor.cc
@@ -115,7 +115,8 @@
     ScriptExecutor::FrameScope frame_scope,
     UserScript::RunLocation run_at,
     ScriptExecutor::WorldType world_type,
-    bool is_web_view,
+    ScriptExecutor::ProcessType process_type,
+    ScriptExecutor::ResultType result_type,
     const ExecuteScriptCallback& callback) {
   ExtensionMsg_ExecuteCode_Params params;
   params.request_id = next_request_id_++;
@@ -125,7 +126,8 @@
   params.all_frames = (frame_scope == ALL_FRAMES);
   params.run_at = static_cast<int>(run_at);
   params.in_main_world = (world_type == MAIN_WORLD);
-  params.is_web_view = is_web_view;
+  params.is_web_view = (process_type == WEB_VIEW_PROCESS);
+  params.wants_result = (result_type == JSON_SERIALIZED_RESULT);
 
   // Handler handles IPCs and deletes itself on completion.
   new Handler(script_observers_, web_contents_, params, callback);
diff --git a/chrome/browser/extensions/script_executor.h b/chrome/browser/extensions/script_executor.h
index d463bf0..5db316f 100644
--- a/chrome/browser/extensions/script_executor.h
+++ b/chrome/browser/extensions/script_executor.h
@@ -55,6 +55,18 @@
     ISOLATED_WORLD,
   };
 
+  // The type of process the target is.
+  enum ProcessType {
+    DEFAULT_PROCESS,
+    WEB_VIEW_PROCESS,
+  };
+
+  // The type of result the caller is interested in.
+  enum ResultType {
+    NO_RESULT,
+    JSON_SERIALIZED_RESULT,
+  };
+
   // Callback from ExecuteScript. The arguments are (error, on_page_id, on_url,
   // result). Success is implied by an empty error.
   typedef base::Callback<void(const std::string&, int32, const GURL&,
@@ -73,7 +85,8 @@
                      FrameScope frame_scope,
                      UserScript::RunLocation run_at,
                      WorldType world_type,
-                     bool is_web_view,
+                     ProcessType process_type,
+                     ResultType result_type,
                      const ExecuteScriptCallback& callback);
 
  private:
diff --git a/chrome/browser/extensions/webstore_installer.cc b/chrome/browser/extensions/webstore_installer.cc
index 3d455ad..9ebf8f7 100644
--- a/chrome/browser/extensions/webstore_installer.cc
+++ b/chrome/browser/extensions/webstore_installer.cc
@@ -163,7 +163,8 @@
       use_app_installed_bubble(false),
       skip_post_install_ui(false),
       skip_install_dialog(false),
-      enable_launcher(false) {
+      enable_launcher(false),
+      strict_manifest_check(true) {
 }
 
 scoped_ptr<WebstoreInstaller::Approval>
@@ -177,7 +178,8 @@
 WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
     Profile* profile,
     const std::string& extension_id,
-    scoped_ptr<base::DictionaryValue> parsed_manifest) {
+    scoped_ptr<base::DictionaryValue> parsed_manifest,
+    bool strict_manifest_check) {
   scoped_ptr<Approval> result(new Approval());
   result->extension_id = extension_id;
   result->profile = profile;
@@ -185,6 +187,7 @@
       new Manifest(Manifest::INVALID_LOCATION,
                    scoped_ptr<DictionaryValue>(parsed_manifest->DeepCopy())));
   result->skip_install_dialog = true;
+  result->strict_manifest_check = strict_manifest_check;
   return result.Pass();
 }
 
diff --git a/chrome/browser/extensions/webstore_installer.h b/chrome/browser/extensions/webstore_installer.h
index 0f9727f..cfb88f8 100644
--- a/chrome/browser/extensions/webstore_installer.h
+++ b/chrome/browser/extensions/webstore_installer.h
@@ -76,10 +76,17 @@
   // be checked further for additional details.
   struct Approval : public base::SupportsUserData::Data {
     static scoped_ptr<Approval> CreateWithInstallPrompt(Profile* profile);
+
+    // Creates an Approval that will skip putting up an install confirmation
+    // prompt if the actual manifest from the extension to be installed matches
+    // |parsed_manifest|. The |strict_manifest_check| controls whether we want
+    // to require an exact manifest match, or are willing to tolerate a looser
+    // check just that the effective permissions are the same.
     static scoped_ptr<Approval> CreateWithNoInstallPrompt(
         Profile* profile,
         const std::string& extension_id,
-        scoped_ptr<base::DictionaryValue> parsed_manifest);
+        scoped_ptr<base::DictionaryValue> parsed_manifest,
+        bool strict_manifest_check);
 
     virtual ~Approval();
 
@@ -108,6 +115,10 @@
     // Whether we should enable the launcher before installing the app.
     bool enable_launcher;
 
+    // Whether we want a strict manifest equality check, or just want a match
+    // for effective permissions.
+    bool strict_manifest_check;
+
     // Used to show the install dialog.
     ExtensionInstallPrompt::ShowDialogCallback show_dialog_callback;
 
diff --git a/chrome/browser/extensions/webstore_standalone_installer.cc b/chrome/browser/extensions/webstore_standalone_installer.cc
index 20f8893..d7185a1 100644
--- a/chrome/browser/extensions/webstore_standalone_installer.cc
+++ b/chrome/browser/extensions/webstore_standalone_installer.cc
@@ -209,7 +209,8 @@
       WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
           profile_,
           id_,
-          scoped_ptr<base::DictionaryValue>(manifest_.get()->DeepCopy())));
+          scoped_ptr<base::DictionaryValue>(manifest_.get()->DeepCopy()),
+          true));
   approval->skip_post_install_ui = !ShouldShowPostInstallUI();
   approval->use_app_installed_bubble = ShouldShowAppInstalledBubble();
   approval->installing_icon = gfx::ImageSkia::CreateFrom1xBitmap(icon_);
diff --git a/chrome/browser/extensions/webstore_startup_installer_browsertest.cc b/chrome/browser/extensions/webstore_startup_installer_browsertest.cc
index 953ad08..25ed277 100644
--- a/chrome/browser/extensions/webstore_startup_installer_browsertest.cc
+++ b/chrome/browser/extensions/webstore_startup_installer_browsertest.cc
@@ -278,7 +278,7 @@
 
   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
     WebstoreStartupInstallerTest::SetUpInProcessBrowserTestFixture();
-    ExtensionInstallUI::DisableFailureUIForTests();
+    ExtensionInstallUI::set_disable_failure_ui_for_tests();
   }
 };
 
diff --git a/chrome/browser/geolocation/geolocation_infobar_delegate.cc b/chrome/browser/geolocation/geolocation_infobar_delegate.cc
index c14594f..0d1e5aa 100644
--- a/chrome/browser/geolocation/geolocation_infobar_delegate.cc
+++ b/chrome/browser/geolocation/geolocation_infobar_delegate.cc
@@ -64,9 +64,11 @@
 
 void GeolocationInfoBarDelegate::SetPermission(bool update_content_setting,
                                                bool allowed) {
-  controller_->OnPermissionSet(id_, requesting_frame_,
-                               web_contents()->GetURL(),
-                               update_content_setting, allowed);
+  if (web_contents()) {
+    controller_->OnPermissionSet(id_, requesting_frame_,
+                                 web_contents()->GetURL(),
+                                 update_content_setting, allowed);
+  }
 }
 
 void GeolocationInfoBarDelegate::InfoBarDismissed() {
diff --git a/chrome/browser/google_apis/DEPS b/chrome/browser/google_apis/DEPS
index 49f3249..5dce014 100644
--- a/chrome/browser/google_apis/DEPS
+++ b/chrome/browser/google_apis/DEPS
@@ -3,11 +3,3 @@
   "-content",
   "+chrome/browser/google_apis",
 ]
-
-# Exceptions are temporarily needed. crbug.com/146989
-specific_include_rules = {
-  # AuthService should be gone. crbug.com/162157
-  "auth_service\.(h|cc)": [
-    "!chrome/browser/signin/oauth2_token_service.h",
-  ],
-}
diff --git a/chrome/browser/google_apis/auth_service.h b/chrome/browser/google_apis/auth_service.h
index ce22550..302ce88 100644
--- a/chrome/browser/google_apis/auth_service.h
+++ b/chrome/browser/google_apis/auth_service.h
@@ -12,7 +12,7 @@
 #include "base/observer_list.h"
 #include "base/threading/thread_checker.h"
 #include "chrome/browser/google_apis/auth_service_interface.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 
 namespace net {
 class URLRequestContextGetter;
diff --git a/chrome/browser/google_apis/drive_api_requests.cc b/chrome/browser/google_apis/drive_api_requests.cc
index 7fb418c..bb9c15d 100644
--- a/chrome/browser/google_apis/drive_api_requests.cc
+++ b/chrome/browser/google_apis/drive_api_requests.cc
@@ -7,8 +7,11 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/json/json_writer.h"
+#include "base/location.h"
+#include "base/task_runner_util.h"
 #include "base/values.h"
 #include "chrome/browser/google_apis/drive_api_parser.h"
+#include "chrome/browser/google_apis/request_sender.h"
 #include "chrome/browser/google_apis/request_util.h"
 #include "chrome/browser/google_apis/time_util.h"
 
@@ -41,6 +44,48 @@
   callback.Run(error, resource.Pass());
 }
 
+// Thin adapter of T::CreateFrom.
+template<typename T>
+scoped_ptr<T> ParseJsonOnBlockingPool(scoped_ptr<base::Value> value) {
+  return T::CreateFrom(*value);
+}
+
+// Runs |callback| with given |error| and |value|. If |value| is null,
+// overwrites |error| to GDATA_PARSE_ERROR.
+template<typename T>
+void ParseJsonOnBlockingPoolAndRunAfterBlockingPoolTask(
+    const base::Callback<void(GDataErrorCode, scoped_ptr<T>)>& callback,
+    GDataErrorCode error, scoped_ptr<T> value) {
+  if (!value)
+    error = GDATA_PARSE_ERROR;
+  callback.Run(error, value.Pass());
+}
+
+// Parses the JSON value to a resource typed |T| and runs |callback| on
+// blocking pool, and then run on the current thread.
+// TODO(hidehiko): Move this and ParseJsonAndRun defined above into base with
+// merging the tasks running on blocking pool into one.
+template<typename T>
+void ParseJsonOnBlockingPoolAndRun(
+    scoped_refptr<base::TaskRunner> blocking_task_runner,
+    const base::Callback<void(GDataErrorCode, scoped_ptr<T>)>& callback,
+    GDataErrorCode error,
+    scoped_ptr<base::Value> value) {
+  DCHECK(!callback.is_null());
+
+  if (!value) {
+    callback.Run(error, scoped_ptr<T>());
+    return;
+  }
+
+  base::PostTaskAndReplyWithResult(
+      blocking_task_runner,
+      FROM_HERE,
+      base::Bind(&ParseJsonOnBlockingPool<T>, base::Passed(&value)),
+      base::Bind(&ParseJsonOnBlockingPoolAndRunAfterBlockingPoolTask<T>,
+                 callback, error));
+}
+
 // Parses the JSON value to FileResource instance and runs |callback| on the
 // UI thread once parsing is done.
 // This is customized version of ParseJsonAndRun defined above to adapt the
@@ -69,108 +114,301 @@
 
 }  // namespace
 
-//============================== GetAboutRequest =============================
+namespace drive {
 
-GetAboutRequest::GetAboutRequest(
+//=============================== FilesGetRequest =============================
+
+FilesGetRequest::FilesGetRequest(
     RequestSender* sender,
     const DriveApiUrlGenerator& url_generator,
-    const GetAboutResourceCallback& callback)
+    const FileResourceCallback& callback)
+    : GetDataRequest(sender,
+                     base::Bind(&ParseJsonAndRun<FileResource>, callback)),
+      url_generator_(url_generator) {
+  DCHECK(!callback.is_null());
+}
+
+FilesGetRequest::~FilesGetRequest() {}
+
+GURL FilesGetRequest::GetURL() const {
+  return url_generator_.GetFilesGetUrl(file_id_);
+}
+
+//============================ FilesInsertRequest ============================
+
+FilesInsertRequest::FilesInsertRequest(
+    RequestSender* sender,
+    const DriveApiUrlGenerator& url_generator,
+    const FileResourceCallback& callback)
+    : GetDataRequest(sender,
+                     base::Bind(&ParseJsonAndRun<FileResource>, callback)),
+      url_generator_(url_generator) {
+  DCHECK(!callback.is_null());
+}
+
+FilesInsertRequest::~FilesInsertRequest() {}
+
+net::URLFetcher::RequestType FilesInsertRequest::GetRequestType() const {
+  return net::URLFetcher::POST;
+}
+
+GURL FilesInsertRequest::GetURL() const {
+  return url_generator_.GetFilesInsertUrl();
+}
+
+bool FilesInsertRequest::GetContentData(std::string* upload_content_type,
+                                        std::string* upload_content) {
+  *upload_content_type = kContentTypeApplicationJson;
+
+  base::DictionaryValue root;
+
+  if (!mime_type_.empty())
+    root.SetString("mimeType", mime_type_);
+
+  if (!parents_.empty()) {
+    base::ListValue* parents_value = new base::ListValue;
+    for (size_t i = 0; i < parents_.size(); ++i) {
+      base::DictionaryValue* parent = new base::DictionaryValue;
+      parent->SetString("id", parents_[i]);
+      parents_value->Append(parent);
+    }
+    root.Set("parents", parents_value);
+  }
+
+  if (!title_.empty())
+    root.SetString("title", title_);
+
+  base::JSONWriter::Write(&root, upload_content);
+  DVLOG(1) << "FilesInsert data: " << *upload_content_type << ", ["
+           << *upload_content << "]";
+  return true;
+}
+
+//============================== FilesPatchRequest ============================
+
+FilesPatchRequest::FilesPatchRequest(
+    RequestSender* sender,
+    const DriveApiUrlGenerator& url_generator,
+    const FileResourceCallback& callback)
+    : GetDataRequest(sender,
+                     base::Bind(&ParseJsonAndRun<FileResource>, callback)),
+      url_generator_(url_generator),
+      set_modified_date_(false),
+      update_viewed_date_(true) {
+  DCHECK(!callback.is_null());
+}
+
+FilesPatchRequest::~FilesPatchRequest() {}
+
+net::URLFetcher::RequestType FilesPatchRequest::GetRequestType() const {
+  return net::URLFetcher::PATCH;
+}
+
+std::vector<std::string> FilesPatchRequest::GetExtraRequestHeaders() const {
+  std::vector<std::string> headers;
+  headers.push_back(util::kIfMatchAllHeader);
+  return headers;
+}
+
+GURL FilesPatchRequest::GetURL() const {
+  return url_generator_.GetFilesPatchUrl(
+      file_id_, set_modified_date_, update_viewed_date_);
+}
+
+bool FilesPatchRequest::GetContentData(std::string* upload_content_type,
+                                       std::string* upload_content) {
+  if (title_.empty() &&
+      modified_date_.is_null() &&
+      last_viewed_by_me_date_.is_null() &&
+      parents_.empty())
+    return false;
+
+  *upload_content_type = kContentTypeApplicationJson;
+
+  base::DictionaryValue root;
+  if (!title_.empty())
+    root.SetString("title", title_);
+
+  if (!modified_date_.is_null())
+    root.SetString("modifiedDate", util::FormatTimeAsString(modified_date_));
+
+  if (!last_viewed_by_me_date_.is_null()) {
+    root.SetString("lastViewedByMeDate",
+                   util::FormatTimeAsString(last_viewed_by_me_date_));
+  }
+
+  if (!parents_.empty()) {
+    base::ListValue* parents_value = new base::ListValue;
+    for (size_t i = 0; i < parents_.size(); ++i) {
+      base::DictionaryValue* parent = new base::DictionaryValue;
+      parent->SetString("id", parents_[i]);
+      parents_value->Append(parent);
+    }
+    root.Set("parents", parents_value);
+  }
+
+  base::JSONWriter::Write(&root, upload_content);
+  DVLOG(1) << "FilesPatch data: " << *upload_content_type << ", ["
+           << *upload_content << "]";
+  return true;
+}
+
+//============================= FilesCopyRequest ==============================
+
+FilesCopyRequest::FilesCopyRequest(
+    RequestSender* sender,
+    const DriveApiUrlGenerator& url_generator,
+    const FileResourceCallback& callback)
+    : GetDataRequest(sender,
+                     base::Bind(&ParseJsonAndRun<FileResource>, callback)),
+      url_generator_(url_generator) {
+  DCHECK(!callback.is_null());
+}
+
+FilesCopyRequest::~FilesCopyRequest() {
+}
+
+net::URLFetcher::RequestType FilesCopyRequest::GetRequestType() const {
+  return net::URLFetcher::POST;
+}
+
+GURL FilesCopyRequest::GetURL() const {
+  return url_generator_.GetFilesCopyUrl(file_id_);
+}
+
+bool FilesCopyRequest::GetContentData(std::string* upload_content_type,
+                                      std::string* upload_content) {
+  if (parents_.empty() && title_.empty())
+    return false;
+
+  *upload_content_type = kContentTypeApplicationJson;
+
+  base::DictionaryValue root;
+
+  if (!parents_.empty()) {
+    base::ListValue* parents_value = new base::ListValue;
+    for (size_t i = 0; i < parents_.size(); ++i) {
+      base::DictionaryValue* parent = new base::DictionaryValue;
+      parent->SetString("id", parents_[i]);
+      parents_value->Append(parent);
+    }
+    root.Set("parents", parents_value);
+  }
+
+  if (!title_.empty())
+    root.SetString("title", title_);
+
+  base::JSONWriter::Write(&root, upload_content);
+  DVLOG(1) << "FilesCopy data: " << *upload_content_type << ", ["
+           << *upload_content << "]";
+  return true;
+}
+
+//============================= FilesListRequest =============================
+
+FilesListRequest::FilesListRequest(
+    RequestSender* sender,
+    const DriveApiUrlGenerator& url_generator,
+    const FileListCallback& callback)
+    : GetDataRequest(
+          sender,
+          base::Bind(&ParseJsonOnBlockingPoolAndRun<FileList>,
+                     make_scoped_refptr(sender->blocking_task_runner()),
+                     callback)),
+      url_generator_(url_generator),
+      max_results_(100) {
+  DCHECK(!callback.is_null());
+}
+
+FilesListRequest::~FilesListRequest() {}
+
+GURL FilesListRequest::GetURL() const {
+  return url_generator_.GetFilesListUrl(max_results_, page_token_, q_);
+}
+
+//============================ FilesTrashRequest =============================
+
+FilesTrashRequest::FilesTrashRequest(
+    RequestSender* sender,
+    const DriveApiUrlGenerator& url_generator,
+    const FileResourceCallback& callback)
+    : GetDataRequest(sender,
+                     base::Bind(&ParseJsonAndRun<FileResource>, callback)),
+      url_generator_(url_generator) {
+  DCHECK(!callback.is_null());
+}
+
+FilesTrashRequest::~FilesTrashRequest() {}
+
+net::URLFetcher::RequestType FilesTrashRequest::GetRequestType() const {
+  return net::URLFetcher::POST;
+}
+
+GURL FilesTrashRequest::GetURL() const {
+  return url_generator_.GetFilesTrashUrl(file_id_);
+}
+
+//============================== AboutGetRequest =============================
+
+AboutGetRequest::AboutGetRequest(
+    RequestSender* sender,
+    const DriveApiUrlGenerator& url_generator,
+    const AboutResourceCallback& callback)
     : GetDataRequest(sender,
                      base::Bind(&ParseJsonAndRun<AboutResource>, callback)),
       url_generator_(url_generator) {
   DCHECK(!callback.is_null());
 }
 
-GetAboutRequest::~GetAboutRequest() {}
+AboutGetRequest::~AboutGetRequest() {}
 
-GURL GetAboutRequest::GetURL() const {
-  return url_generator_.GetAboutUrl();
+GURL AboutGetRequest::GetURL() const {
+  return url_generator_.GetAboutGetUrl();
 }
 
-//============================== GetApplistRequest ===========================
+//============================ ChangesListRequest ===========================
 
-GetApplistRequest::GetApplistRequest(
+ChangesListRequest::ChangesListRequest(
     RequestSender* sender,
     const DriveApiUrlGenerator& url_generator,
-    const GetDataCallback& callback)
-    : GetDataRequest(sender, callback),
+    const ChangeListCallback& callback)
+    : GetDataRequest(
+          sender,
+          base::Bind(&ParseJsonOnBlockingPoolAndRun<ChangeList>,
+                     make_scoped_refptr(sender->blocking_task_runner()),
+                     callback)),
+      url_generator_(url_generator),
+      include_deleted_(true),
+      max_results_(100),
+      start_change_id_(0) {
+  DCHECK(!callback.is_null());
+}
+
+ChangesListRequest::~ChangesListRequest() {}
+
+GURL ChangesListRequest::GetURL() const {
+  return url_generator_.GetChangesListUrl(
+      include_deleted_, max_results_, page_token_, start_change_id_);
+}
+
+//============================== AppsListRequest ===========================
+
+AppsListRequest::AppsListRequest(
+    RequestSender* sender,
+    const DriveApiUrlGenerator& url_generator,
+    const AppListCallback& callback)
+    : GetDataRequest(sender,
+                     base::Bind(&ParseJsonAndRun<AppList>, callback)),
       url_generator_(url_generator) {
   DCHECK(!callback.is_null());
 }
 
-GetApplistRequest::~GetApplistRequest() {}
+AppsListRequest::~AppsListRequest() {}
 
-GURL GetApplistRequest::GetURL() const {
-  return url_generator_.GetApplistUrl();
+GURL AppsListRequest::GetURL() const {
+  return url_generator_.GetAppsListUrl();
 }
 
-//============================ GetChangelistRequest ==========================
-
-GetChangelistRequest::GetChangelistRequest(
-    RequestSender* sender,
-    const DriveApiUrlGenerator& url_generator,
-    bool include_deleted,
-    int64 start_changestamp,
-    int max_results,
-    const GetDataCallback& callback)
-    : GetDataRequest(sender, callback),
-      url_generator_(url_generator),
-      include_deleted_(include_deleted),
-      start_changestamp_(start_changestamp),
-      max_results_(max_results) {
-  DCHECK(!callback.is_null());
-}
-
-GetChangelistRequest::~GetChangelistRequest() {}
-
-GURL GetChangelistRequest::GetURL() const {
-  return url_generator_.GetChangelistUrl(
-      include_deleted_, start_changestamp_, max_results_);
-}
-
-//============================= GetFilelistRequest ===========================
-
-GetFilelistRequest::GetFilelistRequest(
-    RequestSender* sender,
-    const DriveApiUrlGenerator& url_generator,
-    const std::string& search_string,
-    int max_results,
-    const GetDataCallback& callback)
-    : GetDataRequest(sender, callback),
-      url_generator_(url_generator),
-      search_string_(search_string),
-      max_results_(max_results) {
-  DCHECK(!callback.is_null());
-}
-
-GetFilelistRequest::~GetFilelistRequest() {}
-
-GURL GetFilelistRequest::GetURL() const {
-  return url_generator_.GetFilelistUrl(search_string_, max_results_);
-}
-
-//=============================== GetFileRequest =============================
-
-GetFileRequest::GetFileRequest(
-    RequestSender* sender,
-    const DriveApiUrlGenerator& url_generator,
-    const std::string& file_id,
-    const FileResourceCallback& callback)
-    : GetDataRequest(sender,
-                     base::Bind(&ParseJsonAndRun<FileResource>, callback)),
-      url_generator_(url_generator),
-      file_id_(file_id) {
-  DCHECK(!callback.is_null());
-}
-
-GetFileRequest::~GetFileRequest() {}
-
-GURL GetFileRequest::GetURL() const {
-  return url_generator_.GetFileUrl(file_id_);
-}
-
-namespace drive {
-
 //======================= ContinueGetFileListRequest =========================
 
 ContinueGetFileListRequest::ContinueGetFileListRequest(
@@ -188,56 +426,6 @@
   return url_;
 }
 
-//========================== CreateDirectoryRequest ==========================
-
-CreateDirectoryRequest::CreateDirectoryRequest(
-    RequestSender* sender,
-    const DriveApiUrlGenerator& url_generator,
-    const std::string& parent_resource_id,
-    const std::string& directory_title,
-    const FileResourceCallback& callback)
-    : GetDataRequest(sender,
-                     base::Bind(&ParseJsonAndRun<FileResource>, callback)),
-      url_generator_(url_generator),
-      parent_resource_id_(parent_resource_id),
-      directory_title_(directory_title) {
-  DCHECK(!callback.is_null());
-  DCHECK(!parent_resource_id_.empty());
-  DCHECK(!directory_title_.empty());
-}
-
-CreateDirectoryRequest::~CreateDirectoryRequest() {}
-
-GURL CreateDirectoryRequest::GetURL() const {
-  return url_generator_.GetFilesUrl();
-}
-
-net::URLFetcher::RequestType CreateDirectoryRequest::GetRequestType() const {
-  return net::URLFetcher::POST;
-}
-
-bool CreateDirectoryRequest::GetContentData(std::string* upload_content_type,
-                                            std::string* upload_content) {
-  *upload_content_type = kContentTypeApplicationJson;
-
-  base::DictionaryValue root;
-  root.SetString("title", directory_title_);
-  {
-    base::DictionaryValue* parent_value = new base::DictionaryValue;
-    parent_value->SetString("id", parent_resource_id_);
-    base::ListValue* parent_list_value = new base::ListValue;
-    parent_list_value->Append(parent_value);
-    root.Set("parents", parent_list_value);
-  }
-  root.SetString("mimeType", kDirectoryMimeType);
-
-  base::JSONWriter::Write(&root, upload_content);
-
-  DVLOG(1) << "CreateDirectory data: " << *upload_content_type << ", ["
-           << *upload_content << "]";
-  return true;
-}
-
 //=========================== TouchResourceRequest ===========================
 
 TouchResourceRequest::TouchResourceRequest(
@@ -290,58 +478,6 @@
   return true;
 }
 
-//=========================== CopyResourceRequest ============================
-
-CopyResourceRequest::CopyResourceRequest(
-    RequestSender* sender,
-    const DriveApiUrlGenerator& url_generator,
-    const std::string& resource_id,
-    const std::string& parent_resource_id,
-    const std::string& new_title,
-    const FileResourceCallback& callback)
-    : GetDataRequest(sender,
-                     base::Bind(&ParseJsonAndRun<FileResource>, callback)),
-      url_generator_(url_generator),
-      resource_id_(resource_id),
-      parent_resource_id_(parent_resource_id),
-      new_title_(new_title) {
-  DCHECK(!callback.is_null());
-}
-
-CopyResourceRequest::~CopyResourceRequest() {
-}
-
-net::URLFetcher::RequestType CopyResourceRequest::GetRequestType() const {
-  return net::URLFetcher::POST;
-}
-
-GURL CopyResourceRequest::GetURL() const {
-  return url_generator_.GetFileCopyUrl(resource_id_);
-}
-
-bool CopyResourceRequest::GetContentData(std::string* upload_content_type,
-                                         std::string* upload_content) {
-  *upload_content_type = kContentTypeApplicationJson;
-
-  base::DictionaryValue root;
-  root.SetString("title", new_title_);
-
-  if (!parent_resource_id_.empty()) {
-    // Set the parent resource (destination directory) of the new resource.
-    base::ListValue* parents = new base::ListValue;
-    root.Set("parents", parents);
-    base::DictionaryValue* parent_value = new base::DictionaryValue;
-    parents->Append(parent_value);
-    parent_value->SetString("id", parent_resource_id_);
-  }
-
-  base::JSONWriter::Write(&root, upload_content);
-
-  DVLOG(1) << "CopyResource data: " << *upload_content_type << ", ["
-           << *upload_content << "]";
-  return true;
-}
-
 //=========================== MoveResourceRequest ============================
 
 MoveResourceRequest::MoveResourceRequest(
@@ -374,7 +510,11 @@
 }
 
 GURL MoveResourceRequest::GetURL() const {
-  return url_generator_.GetFileUrl(resource_id_);
+  // TODO(hidehiko): This temporarily shares the URL with "Files: get" method.
+  // After the refactoring, this class will be merged with TouchResourceRequest
+  // into FilesPatchRequest. Then, url_generator_ will have the method
+  // for the new class.
+  return url_generator_.GetFilesGetUrl(resource_id_);
 }
 
 bool MoveResourceRequest::GetContentData(std::string* upload_content_type,
@@ -400,29 +540,6 @@
   return true;
 }
 
-//=========================== TrashResourceRequest ===========================
-
-TrashResourceRequest::TrashResourceRequest(
-    RequestSender* sender,
-    const DriveApiUrlGenerator& url_generator,
-    const std::string& resource_id,
-    const EntryActionCallback& callback)
-    : EntryActionRequest(sender, callback),
-      url_generator_(url_generator),
-      resource_id_(resource_id) {
-  DCHECK(!callback.is_null());
-}
-
-TrashResourceRequest::~TrashResourceRequest() {}
-
-GURL TrashResourceRequest::GetURL() const {
-  return url_generator_.GetFileTrashUrl(resource_id_);
-}
-
-net::URLFetcher::RequestType TrashResourceRequest::GetRequestType() const {
-  return net::URLFetcher::POST;
-}
-
 //========================== InsertResourceRequest ===========================
 
 InsertResourceRequest::InsertResourceRequest(
diff --git a/chrome/browser/google_apis/drive_api_requests.h b/chrome/browser/google_apis/drive_api_requests.h
index e6128ea..b61ff2b 100644
--- a/chrome/browser/google_apis/drive_api_requests.h
+++ b/chrome/browser/google_apis/drive_api_requests.h
@@ -14,7 +14,9 @@
 
 namespace google_apis {
 
+class ChangeList;
 class FileResource;
+class FileList;
 
 // Callback used for requests that the server returns FileResource data
 // formatted into JSON value.
@@ -22,117 +24,33 @@
                             scoped_ptr<FileResource> entry)>
     FileResourceCallback;
 
+// Callback used for requests that the server returns FileList data
+// formatted into JSON value.
+typedef base::Callback<void(GDataErrorCode error,
+                            scoped_ptr<FileList> entry)> FileListCallback;
 
-//============================== GetAboutRequest =============================
+// Callback used for requests that the server returns ChangeList data
+// formatted into JSON value.
+typedef base::Callback<void(GDataErrorCode error,
+                            scoped_ptr<ChangeList> entry)> ChangeListCallback;
 
-// This class performs the request for fetching About data.
-class GetAboutRequest : public GetDataRequest {
- public:
-  GetAboutRequest(RequestSender* sender,
-                  const DriveApiUrlGenerator& url_generator,
-                  const GetAboutResourceCallback& callback);
-  virtual ~GetAboutRequest();
+namespace drive {
 
- protected:
-  // Overridden from GetDataRequest.
-  virtual GURL GetURL() const OVERRIDE;
-
- private:
-  const DriveApiUrlGenerator url_generator_;
-
-  DISALLOW_COPY_AND_ASSIGN(GetAboutRequest);
-};
-
-//============================= GetApplistRequest ============================
-
-// This class performs the request for fetching Applist.
-class GetApplistRequest : public GetDataRequest {
- public:
-  GetApplistRequest(RequestSender* sender,
-                    const DriveApiUrlGenerator& url_generator,
-                    const GetDataCallback& callback);
-  virtual ~GetApplistRequest();
-
- protected:
-  // Overridden from GetDataRequest.
-  virtual GURL GetURL() const OVERRIDE;
-
- private:
-  const DriveApiUrlGenerator url_generator_;
-
-  DISALLOW_COPY_AND_ASSIGN(GetApplistRequest);
-};
-
-//============================ GetChangelistRequest ==========================
-
-// This class performs the request for fetching changelist.
-// The result may contain only first part of the result. The remaining result
-// should be able to be fetched by ContinueGetFileListRequest defined below.
-class GetChangelistRequest : public GetDataRequest {
- public:
-  // |include_deleted| specifies if the response should contain the changes
-  // for deleted entries or not.
-  // |start_changestamp| specifies the starting point of change list or 0 if
-  // all changes are necessary.
-  // |max_results| specifies the max of the number of files resource in the
-  // response.
-  GetChangelistRequest(RequestSender* sender,
-                       const DriveApiUrlGenerator& url_generator,
-                       bool include_deleted,
-                       int64 start_changestamp,
-                       int max_results,
-                       const GetDataCallback& callback);
-  virtual ~GetChangelistRequest();
-
- protected:
-  // Overridden from GetDataRequest.
-  virtual GURL GetURL() const OVERRIDE;
-
- private:
-  const DriveApiUrlGenerator url_generator_;
-  const bool include_deleted_;
-  const int64 start_changestamp_;
-  const int max_results_;
-
-  DISALLOW_COPY_AND_ASSIGN(GetChangelistRequest);
-};
-
-//============================= GetFilelistRequest ===========================
-
-// This class performs the request for fetching Filelist.
-// The result may contain only first part of the result. The remaining result
-// should be able to be fetched by ContinueGetFileListRequest defined below.
-class GetFilelistRequest : public GetDataRequest {
- public:
-  GetFilelistRequest(RequestSender* sender,
-                     const DriveApiUrlGenerator& url_generator,
-                     const std::string& search_string,
-                     int max_results,
-                     const GetDataCallback& callback);
-  virtual ~GetFilelistRequest();
-
- protected:
-  // Overridden from GetDataRequest.
-  virtual GURL GetURL() const OVERRIDE;
-
- private:
-  const DriveApiUrlGenerator url_generator_;
-  const std::string search_string_;
-  const int max_results_;
-
-  DISALLOW_COPY_AND_ASSIGN(GetFilelistRequest);
-};
-
-//=============================== GetFileRequest =============================
+//=============================== FilesGetRequest =============================
 
 // This class performs the request for fetching a file.
-class GetFileRequest : public GetDataRequest {
+// This request is mapped to
+// https://developers.google.com/drive/v2/reference/files/get
+class FilesGetRequest : public GetDataRequest {
  public:
-  GetFileRequest(RequestSender* sender,
-                 const DriveApiUrlGenerator& url_generator,
-                 const std::string& file_id,
-                 const FileResourceCallback& callback);
-  virtual ~GetFileRequest();
+  FilesGetRequest(RequestSender* sender,
+                  const DriveApiUrlGenerator& url_generator,
+                  const FileResourceCallback& callback);
+  virtual ~FilesGetRequest();
+
+  // Required parameter.
+  const std::string& file_id() const { return file_id_; }
+  void set_file_id(const std::string& file_id) { file_id_ = file_id; }
 
  protected:
   // Overridden from GetDataRequest.
@@ -142,16 +60,324 @@
   const DriveApiUrlGenerator url_generator_;
   std::string file_id_;
 
-  DISALLOW_COPY_AND_ASSIGN(GetFileRequest);
+  DISALLOW_COPY_AND_ASSIGN(FilesGetRequest);
 };
 
-// This namespace is introduced to avoid class name confliction between
-// the requests for Drive API v2 and GData WAPI for transition.
-// And, when the migration is done and GData WAPI's code is cleaned up,
-// classes inside this namespace should be moved to the google_apis namespace.
-// TODO(hidehiko): Move all the requests defined in this file into drive
-// namespace.  crbug.com/180808
-namespace drive {
+//============================ FilesInsertRequest =============================
+
+// This class performs the request for creating a resource.
+// This request is mapped to
+// https://developers.google.com/drive/v2/reference/files/insert
+// See also https://developers.google.com/drive/manage-uploads and
+// https://developers.google.com/drive/folder
+class FilesInsertRequest : public GetDataRequest {
+ public:
+  FilesInsertRequest(RequestSender* sender,
+                     const DriveApiUrlGenerator& url_generator,
+                     const FileResourceCallback& callback);
+  virtual ~FilesInsertRequest();
+
+  // Optional request body.
+  const std::string& mime_type() const { return mime_type_; }
+  void set_mime_type(const std::string& mime_type) {
+    mime_type_ = mime_type;
+  }
+
+  const std::vector<std::string>& parents() const { return parents_; }
+  void add_parent(const std::string& parent) { parents_.push_back(parent); }
+
+  const std::string& title() const { return title_; }
+  void set_title(const std::string& title) { title_ = title; }
+
+ protected:
+  // Overridden from GetDataRequest.
+  virtual net::URLFetcher::RequestType GetRequestType() const OVERRIDE;
+  virtual GURL GetURL() const OVERRIDE;
+  virtual bool GetContentData(std::string* upload_content_type,
+                              std::string* upload_content) OVERRIDE;
+
+ private:
+  const DriveApiUrlGenerator url_generator_;
+
+  std::string mime_type_;
+  std::vector<std::string> parents_;
+  std::string title_;
+
+  DISALLOW_COPY_AND_ASSIGN(FilesInsertRequest);
+};
+
+//============================== FilesPatchRequest ============================
+
+// This class performs the request for patching file metadata.
+// This request is mapped to
+// https://developers.google.com/drive/v2/reference/files/patch
+class FilesPatchRequest : public GetDataRequest {
+ public:
+  FilesPatchRequest(RequestSender* sender,
+                    const DriveApiUrlGenerator& url_generator,
+                    const FileResourceCallback& callback);
+  virtual ~FilesPatchRequest();
+
+  // Required parameter.
+  const std::string& file_id() const { return file_id_; }
+  void set_file_id(const std::string& file_id) { file_id_ = file_id; }
+
+  // Optional parameter.
+  bool set_modified_date() const { return set_modified_date_; }
+  void set_set_modified_date(bool set_modified_date) {
+    set_modified_date_ = set_modified_date;
+  }
+
+  bool update_viewed_date() const { return update_viewed_date_; }
+  void set_update_viewed_date(bool update_viewed_date) {
+    update_viewed_date_ = update_viewed_date;
+  }
+
+  // Optional request body.
+  // Note: "Files: patch" accepts any "Files resource" data, but this class
+  // only supports limited members of it for now. We can extend it upon
+  // requirments.
+  const std::string& title() const { return title_; }
+  void set_title(const std::string& title) { title_ = title; }
+
+  const base::Time& modified_date() const { return modified_date_; }
+  void set_modified_date(const base::Time& modified_date) {
+    modified_date_ = modified_date;
+  }
+
+  const base::Time& last_viewed_by_me_date() const {
+    return last_viewed_by_me_date_;
+  }
+  void set_last_viewed_by_me_date(const base::Time& last_viewed_by_me_date) {
+    last_viewed_by_me_date_ = last_viewed_by_me_date;
+  }
+
+  const std::vector<std::string>& parents() const { return parents_; }
+  void add_parent(const std::string& parent) { parents_.push_back(parent); }
+
+ protected:
+  // UrlFetchRequestBase overrides.
+  virtual net::URLFetcher::RequestType GetRequestType() const OVERRIDE;
+  virtual std::vector<std::string> GetExtraRequestHeaders() const OVERRIDE;
+  virtual GURL GetURL() const OVERRIDE;
+  virtual bool GetContentData(std::string* upload_content_type,
+                              std::string* upload_content) OVERRIDE;
+
+ private:
+  const DriveApiUrlGenerator url_generator_;
+
+  std::string file_id_;
+  bool set_modified_date_;
+  bool update_viewed_date_;
+
+  std::string title_;
+  base::Time modified_date_;
+  base::Time last_viewed_by_me_date_;
+  std::vector<std::string> parents_;
+
+  DISALLOW_COPY_AND_ASSIGN(FilesPatchRequest);
+};
+
+//============================= FilesCopyRequest ==============================
+
+// This class performs the request for copying a resource.
+// This request is mapped to
+// https://developers.google.com/drive/v2/reference/files/copy
+class FilesCopyRequest : public GetDataRequest {
+ public:
+  // Upon completion, |callback| will be called. |callback| must not be null.
+  FilesCopyRequest(RequestSender* sender,
+                   const DriveApiUrlGenerator& url_generator,
+                   const FileResourceCallback& callback);
+  virtual ~FilesCopyRequest();
+
+  // Required parameter.
+  const std::string& file_id() const { return file_id_; }
+  void set_file_id(const std::string& file_id) { file_id_ = file_id; }
+
+  // Optional request body.
+  const std::vector<std::string>& parents() const { return parents_; }
+  void add_parent(const std::string& parent) { parents_.push_back(parent); }
+
+  const std::string& title() const { return title_; }
+  void set_title(const std::string& title) { title_ = title; }
+
+ protected:
+  virtual net::URLFetcher::RequestType GetRequestType() const OVERRIDE;
+  virtual GURL GetURL() const OVERRIDE;
+  virtual bool GetContentData(std::string* upload_content_type,
+                              std::string* upload_content) OVERRIDE;
+
+ private:
+  const DriveApiUrlGenerator url_generator_;
+
+  std::string file_id_;
+  std::vector<std::string> parents_;
+  std::string title_;
+
+  DISALLOW_COPY_AND_ASSIGN(FilesCopyRequest);
+};
+
+//============================= FilesListRequest =============================
+
+// This class performs the request for fetching FileList.
+// The result may contain only first part of the result. The remaining result
+// should be able to be fetched by ContinueGetFileListRequest defined below,
+// or by FilesListRequest with setting page token.
+// This request is mapped to
+// https://developers.google.com/drive/v2/reference/files/list
+class FilesListRequest : public GetDataRequest {
+ public:
+  FilesListRequest(RequestSender* sender,
+                   const DriveApiUrlGenerator& url_generator,
+                   const FileListCallback& callback);
+  virtual ~FilesListRequest();
+
+  // Optional parameter
+  int max_results() const { return max_results_; }
+  void set_max_results(int max_results) { max_results_ = max_results; }
+
+  const std::string& page_token() const { return page_token_; }
+  void set_page_token(const std::string& page_token) {
+    page_token_ = page_token;
+  }
+
+  const std::string& q() const { return q_; }
+  void set_q(const std::string& q) { q_ = q; }
+
+ protected:
+  // Overridden from GetDataRequest.
+  virtual GURL GetURL() const OVERRIDE;
+
+ private:
+  const DriveApiUrlGenerator url_generator_;
+  int max_results_;
+  std::string page_token_;
+  std::string q_;
+
+  DISALLOW_COPY_AND_ASSIGN(FilesListRequest);
+};
+
+//=========================== TrashResourceRequest ===========================
+
+// This class performs the request for trashing a resource.
+// This request is mapped to
+// https://developers.google.com/drive/v2/reference/files/trash
+class FilesTrashRequest : public GetDataRequest {
+ public:
+  FilesTrashRequest(RequestSender* sender,
+                    const DriveApiUrlGenerator& url_generator,
+                    const FileResourceCallback& callback);
+  virtual ~FilesTrashRequest();
+
+  // Required parameter.
+  const std::string& file_id() const { return file_id_; }
+  void set_file_id(const std::string& file_id) { file_id_ = file_id; }
+
+ protected:
+  // UrlFetchRequestBase overrides.
+  virtual net::URLFetcher::RequestType GetRequestType() const OVERRIDE;
+  virtual GURL GetURL() const OVERRIDE;
+
+ private:
+  const DriveApiUrlGenerator url_generator_;
+  std::string file_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(FilesTrashRequest);
+};
+
+
+//============================== AboutGetRequest =============================
+
+// This class performs the request for fetching About data.
+// This request is mapped to
+// https://developers.google.com/drive/v2/reference/about/get
+class AboutGetRequest : public GetDataRequest {
+ public:
+  AboutGetRequest(RequestSender* sender,
+                  const DriveApiUrlGenerator& url_generator,
+                  const AboutResourceCallback& callback);
+  virtual ~AboutGetRequest();
+
+ protected:
+  // Overridden from GetDataRequest.
+  virtual GURL GetURL() const OVERRIDE;
+
+ private:
+  const DriveApiUrlGenerator url_generator_;
+
+  DISALLOW_COPY_AND_ASSIGN(AboutGetRequest);
+};
+
+//============================ ChangesListRequest ============================
+
+// This class performs the request for fetching ChangeList.
+// The result may contain only first part of the result. The remaining result
+// should be able to be fetched by ContinueGetFileListRequest defined below.
+// or by ChangesListRequest with setting page token.
+// This request is mapped to
+// https://developers.google.com/drive/v2/reference/changes/list
+class ChangesListRequest : public GetDataRequest {
+ public:
+  ChangesListRequest(RequestSender* sender,
+                     const DriveApiUrlGenerator& url_generator,
+                     const ChangeListCallback& callback);
+  virtual ~ChangesListRequest();
+
+  // Optional parameter
+  bool include_deleted() const { return include_deleted_; }
+  void set_include_deleted(bool include_deleted) {
+    include_deleted_ = include_deleted;
+  }
+
+  int max_results() const { return max_results_; }
+  void set_max_results(int max_results) { max_results_ = max_results; }
+
+  const std::string& page_token() const { return page_token_; }
+  void set_page_token(const std::string& page_token) {
+    page_token_ = page_token;
+  }
+
+  int64 start_change_id() const { return start_change_id_; }
+  void set_start_change_id(int64 start_change_id) {
+    start_change_id_ = start_change_id;
+  }
+
+ protected:
+  // Overridden from GetDataRequest.
+  virtual GURL GetURL() const OVERRIDE;
+
+ private:
+  const DriveApiUrlGenerator url_generator_;
+  bool include_deleted_;
+  int max_results_;
+  std::string page_token_;
+  int64 start_change_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChangesListRequest);
+};
+
+//============================= AppsListRequest ============================
+
+// This class performs the request for fetching AppList.
+// This request is mapped to
+// https://developers.google.com/drive/v2/reference/apps/list
+class AppsListRequest : public GetDataRequest {
+ public:
+  AppsListRequest(RequestSender* sender,
+                  const DriveApiUrlGenerator& url_generator,
+                  const AppListCallback& callback);
+  virtual ~AppsListRequest();
+
+ protected:
+  // Overridden from GetDataRequest.
+  virtual GURL GetURL() const OVERRIDE;
+
+ private:
+  const DriveApiUrlGenerator url_generator_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppsListRequest);
+};
 
 //======================= ContinueGetFileListRequest =========================
 
@@ -172,33 +398,6 @@
   DISALLOW_COPY_AND_ASSIGN(ContinueGetFileListRequest);
 };
 
-//========================== CreateDirectoryRequest ==========================
-
-// This class performs the request for creating a directory.
-class CreateDirectoryRequest : public GetDataRequest {
- public:
-  CreateDirectoryRequest(RequestSender* sender,
-                         const DriveApiUrlGenerator& url_generator,
-                         const std::string& parent_resource_id,
-                         const std::string& directory_title,
-                         const FileResourceCallback& callback);
-  virtual ~CreateDirectoryRequest();
-
- protected:
-  // Overridden from GetDataRequest.
-  virtual GURL GetURL() const OVERRIDE;
-  virtual net::URLFetcher::RequestType GetRequestType() const OVERRIDE;
-  virtual bool GetContentData(std::string* upload_content_type,
-                              std::string* upload_content) OVERRIDE;
-
- private:
-  const DriveApiUrlGenerator url_generator_;
-  const std::string parent_resource_id_;
-  const std::string directory_title_;
-
-  DISALLOW_COPY_AND_ASSIGN(CreateDirectoryRequest);
-};
-
 //=========================== TouchResourceRequest ===========================
 
 // This class performs the request to touch a document/file/directory.
@@ -234,43 +433,6 @@
   DISALLOW_COPY_AND_ASSIGN(TouchResourceRequest);
 };
 
-//=========================== CopyResourceRequest ============================
-
-// This class performs the request for copying a resource.
-//
-// Copies the resource with |resource_id| into a directory with
-// |parent_resource_id|. The new resource will be named as |new_title|.
-// |parent_resource_id| can be empty. In the case, the copy will be created
-// directly under the default root directory (this is the default behavior
-// of Drive API v2's copy request).
-//
-// This request corresponds to "Files: copy" request on Drive API v2. See
-// also: https://developers.google.com/drive/v2/reference/files/copy
-class CopyResourceRequest : public GetDataRequest {
- public:
-  // Upon completion, |callback| will be called. |callback| must not be null.
-  CopyResourceRequest(RequestSender* sender,
-                      const DriveApiUrlGenerator& url_generator,
-                      const std::string& resource_id,
-                      const std::string& parent_resource_id,
-                      const std::string& new_title,
-                      const FileResourceCallback& callback);
-  virtual ~CopyResourceRequest();
-
- protected:
-  virtual net::URLFetcher::RequestType GetRequestType() const OVERRIDE;
-  virtual GURL GetURL() const OVERRIDE;
-  virtual bool GetContentData(std::string* upload_content_type,
-                              std::string* upload_content) OVERRIDE;
- private:
-  const DriveApiUrlGenerator url_generator_;
-  const std::string resource_id_;
-  const std::string parent_resource_id_;
-  const std::string new_title_;
-
-  DISALLOW_COPY_AND_ASSIGN(CopyResourceRequest);
-};
-
 //=========================== MoveResourceRequest ============================
 
 // This class performs the request for moving a resource.
@@ -308,40 +470,6 @@
   DISALLOW_COPY_AND_ASSIGN(MoveResourceRequest);
 };
 
-//=========================== TrashResourceRequest ===========================
-
-// This class performs the request for trashing a resource.
-//
-// According to the document:
-// https://developers.google.com/drive/v2/reference/files/trash
-// the file resource will be returned from the server, which is not in the
-// response from WAPI server. For the transition, we simply ignore the result,
-// because now we do not handle resources in trash.
-// Note for the naming: the name "trash" comes from the server's request
-// name. In order to be consistent with the server, we chose "trash" here,
-// although we are preferring the term "remove" in drive/google_api code.
-// TODO(hidehiko): Replace the base class to GetDataRequest.
-class TrashResourceRequest : public EntryActionRequest {
- public:
-  // |callback| must not be null.
-  TrashResourceRequest(RequestSender* sender,
-                       const DriveApiUrlGenerator& url_generator,
-                       const std::string& resource_id,
-                       const EntryActionCallback& callback);
-  virtual ~TrashResourceRequest();
-
- protected:
-  // UrlFetchRequestBase overrides.
-  virtual GURL GetURL() const OVERRIDE;
-  virtual net::URLFetcher::RequestType GetRequestType() const OVERRIDE;
-
- private:
-  const DriveApiUrlGenerator url_generator_;
-  const std::string resource_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(TrashResourceRequest);
-};
-
 //========================== InsertResourceRequest ===========================
 
 // This class performs the request for inserting a resource to a directory.
diff --git a/chrome/browser/google_apis/drive_api_requests_unittest.cc b/chrome/browser/google_apis/drive_api_requests_unittest.cc
index ea71b78..07bfffa 100644
--- a/chrome/browser/google_apis/drive_api_requests_unittest.cc
+++ b/chrome/browser/google_apis/drive_api_requests_unittest.cc
@@ -345,7 +345,101 @@
   int64 content_length_;
 };
 
-TEST_F(DriveApiRequestsTest, GetAboutRequest_ValidJson) {
+TEST_F(DriveApiRequestsTest, FilesInsertRequest) {
+  // Set an expected data file containing the directory's entry data.
+  expected_data_file_path_ =
+      test_util::GetTestFilePath("drive/directory_entry.json");
+
+  GDataErrorCode error = GDATA_OTHER_ERROR;
+  scoped_ptr<FileResource> file_resource;
+
+  // Create "new directory" in the root directory.
+  {
+    base::RunLoop run_loop;
+    drive::FilesInsertRequest* request = new drive::FilesInsertRequest(
+        request_sender_.get(),
+        *url_generator_,
+        test_util::CreateQuitCallback(
+            &run_loop,
+            test_util::CreateCopyResultCallback(&error, &file_resource)));
+    request->set_mime_type("application/vnd.google-apps.folder");
+    request->add_parent("root");
+    request->set_title("new directory");
+    request_sender_->StartRequestWithRetry(request);
+    run_loop.Run();
+  }
+
+  EXPECT_EQ(HTTP_SUCCESS, error);
+  EXPECT_EQ(net::test_server::METHOD_POST, http_request_.method);
+  EXPECT_EQ("/drive/v2/files", http_request_.relative_url);
+  EXPECT_EQ("application/json", http_request_.headers["Content-Type"]);
+
+  EXPECT_TRUE(http_request_.has_content);
+
+  scoped_ptr<FileResource> expected(
+      FileResource::CreateFrom(
+          *test_util::LoadJSONFile("drive/directory_entry.json")));
+
+  // Sanity check.
+  ASSERT_TRUE(file_resource.get());
+
+  EXPECT_EQ(expected->file_id(), file_resource->file_id());
+  EXPECT_EQ(expected->title(), file_resource->title());
+  EXPECT_EQ(expected->mime_type(), file_resource->mime_type());
+  EXPECT_EQ(expected->parents().size(), file_resource->parents().size());
+}
+
+TEST_F(DriveApiRequestsTest, FilesPatchRequest) {
+  const base::Time::Exploded kModifiedDate = {2012, 7, 0, 19, 15, 59, 13, 123};
+  const base::Time::Exploded kLastViewedByMeDate =
+      {2013, 7, 0, 19, 15, 59, 13, 123};
+
+  // Set an expected data file containing valid result.
+  expected_data_file_path_ =
+      test_util::GetTestFilePath("drive/file_entry.json");
+
+  GDataErrorCode error = GDATA_OTHER_ERROR;
+  scoped_ptr<FileResource> file_resource;
+
+  {
+    base::RunLoop run_loop;
+    drive::FilesPatchRequest* request = new drive::FilesPatchRequest(
+        request_sender_.get(),
+        *url_generator_,
+        test_util::CreateQuitCallback(
+            &run_loop,
+            test_util::CreateCopyResultCallback(&error, &file_resource)));
+    request->set_file_id("resource_id");
+    request->set_set_modified_date(true);
+    request->set_update_viewed_date(false);
+
+    request->set_title("new title");
+    request->set_modified_date(base::Time::FromUTCExploded(kModifiedDate));
+    request->set_last_viewed_by_me_date(
+        base::Time::FromUTCExploded(kLastViewedByMeDate));
+    request->add_parent("parent_resource_id");
+
+    request_sender_->StartRequestWithRetry(request);
+    run_loop.Run();
+  }
+
+  EXPECT_EQ(HTTP_SUCCESS, error);
+  EXPECT_EQ(net::test_server::METHOD_PATCH, http_request_.method);
+  EXPECT_EQ("/drive/v2/files/resource_id"
+            "?setModifiedDate=true&updateViewedDate=false",
+            http_request_.relative_url);
+
+  EXPECT_EQ("application/json", http_request_.headers["Content-Type"]);
+  EXPECT_TRUE(http_request_.has_content);
+  EXPECT_EQ("{\"lastViewedByMeDate\":\"2013-07-19T15:59:13.123Z\","
+            "\"modifiedDate\":\"2012-07-19T15:59:13.123Z\","
+            "\"parents\":[{\"id\":\"parent_resource_id\"}],"
+            "\"title\":\"new title\"}",
+            http_request_.content);
+  EXPECT_TRUE(file_resource);
+}
+
+TEST_F(DriveApiRequestsTest, AboutGetRequest_ValidJson) {
   // Set an expected data file containing valid result.
   expected_data_file_path_ = test_util::GetTestFilePath(
       "drive/about.json");
@@ -355,7 +449,7 @@
 
   {
     base::RunLoop run_loop;
-    GetAboutRequest* request = new GetAboutRequest(
+    drive::AboutGetRequest* request = new drive::AboutGetRequest(
         request_sender_.get(),
         *url_generator_,
         test_util::CreateQuitCallback(
@@ -379,7 +473,7 @@
   EXPECT_EQ(expected->root_folder_id(), about_resource->root_folder_id());
 }
 
-TEST_F(DriveApiRequestsTest, GetAboutRequest_InvalidJson) {
+TEST_F(DriveApiRequestsTest, AboutGetRequest_InvalidJson) {
   // Set an expected data file containing invalid result.
   expected_data_file_path_ = test_util::GetTestFilePath(
       "gdata/testfile.txt");
@@ -389,7 +483,7 @@
 
   {
     base::RunLoop run_loop;
-    GetAboutRequest* request = new GetAboutRequest(
+    drive::AboutGetRequest* request = new drive::AboutGetRequest(
         request_sender_.get(),
         *url_generator_,
         test_util::CreateQuitCallback(
@@ -403,25 +497,25 @@
   EXPECT_EQ(GDATA_PARSE_ERROR, error);
   EXPECT_EQ(net::test_server::METHOD_GET, http_request_.method);
   EXPECT_EQ("/drive/v2/about", http_request_.relative_url);
-  EXPECT_FALSE(about_resource.get());
+  EXPECT_FALSE(about_resource);
 }
 
-TEST_F(DriveApiRequestsTest, GetApplistRequest) {
+TEST_F(DriveApiRequestsTest, AppsListRequest) {
   // Set an expected data file containing valid result.
   expected_data_file_path_ = test_util::GetTestFilePath(
       "drive/applist.json");
 
   GDataErrorCode error = GDATA_OTHER_ERROR;
-  scoped_ptr<base::Value> result;
+  scoped_ptr<AppList> app_list;
 
   {
     base::RunLoop run_loop;
-    GetApplistRequest* request = new GetApplistRequest(
+    drive::AppsListRequest* request = new drive::AppsListRequest(
         request_sender_.get(),
         *url_generator_,
         test_util::CreateQuitCallback(
             &run_loop,
-            test_util::CreateCopyResultCallback(&error, &result)));
+            test_util::CreateCopyResultCallback(&error, &app_list)));
     request_sender_->StartRequestWithRetry(request);
     run_loop.Run();
   }
@@ -429,57 +523,126 @@
   EXPECT_EQ(HTTP_SUCCESS, error);
   EXPECT_EQ(net::test_server::METHOD_GET, http_request_.method);
   EXPECT_EQ("/drive/v2/apps", http_request_.relative_url);
-  EXPECT_TRUE(result);
+  EXPECT_TRUE(app_list);
 }
 
-TEST_F(DriveApiRequestsTest, GetChangelistRequest) {
+TEST_F(DriveApiRequestsTest, ChangesListRequest) {
   // Set an expected data file containing valid result.
   expected_data_file_path_ = test_util::GetTestFilePath(
       "drive/changelist.json");
 
   GDataErrorCode error = GDATA_OTHER_ERROR;
-  scoped_ptr<base::Value> result;
+  scoped_ptr<ChangeList> result;
 
   {
     base::RunLoop run_loop;
-    GetChangelistRequest* request = new GetChangelistRequest(
-        request_sender_.get(),
-        *url_generator_,
-        true,  // include deleted
-        100,  // start changestamp
-        500,  // max results
+    drive::ChangesListRequest* request = new drive::ChangesListRequest(
+        request_sender_.get(), *url_generator_,
         test_util::CreateQuitCallback(
             &run_loop,
             test_util::CreateCopyResultCallback(&error, &result)));
+    request->set_include_deleted(true);
+    request->set_start_change_id(100);
+    request->set_max_results(500);
     request_sender_->StartRequestWithRetry(request);
     run_loop.Run();
   }
 
   EXPECT_EQ(HTTP_SUCCESS, error);
   EXPECT_EQ(net::test_server::METHOD_GET, http_request_.method);
-  EXPECT_EQ("/drive/v2/changes?startChangeId=100&maxResults=500",
+  EXPECT_EQ("/drive/v2/changes?maxResults=500&startChangeId=100",
             http_request_.relative_url);
   EXPECT_TRUE(result);
 }
 
-TEST_F(DriveApiRequestsTest, GetFilelistRequest) {
+TEST_F(DriveApiRequestsTest, FilesCopyRequest) {
+  // Set an expected data file containing the dummy file entry data.
+  // It'd be returned if we copy a file.
+  expected_data_file_path_ =
+      test_util::GetTestFilePath("drive/file_entry.json");
+
+  GDataErrorCode error = GDATA_OTHER_ERROR;
+  scoped_ptr<FileResource> file_resource;
+
+  // Copy the file to a new file named "new title".
+  {
+    base::RunLoop run_loop;
+    drive::FilesCopyRequest* request = new drive::FilesCopyRequest(
+        request_sender_.get(),
+        *url_generator_,
+        test_util::CreateQuitCallback(
+            &run_loop,
+            test_util::CreateCopyResultCallback(&error, &file_resource)));
+    request->set_file_id("resource_id");
+    request->add_parent("parent_resource_id");
+    request->set_title("new title");
+    request_sender_->StartRequestWithRetry(request);
+    run_loop.Run();
+  }
+
+  EXPECT_EQ(HTTP_SUCCESS, error);
+  EXPECT_EQ(net::test_server::METHOD_POST, http_request_.method);
+  EXPECT_EQ("/drive/v2/files/resource_id/copy", http_request_.relative_url);
+  EXPECT_EQ("application/json", http_request_.headers["Content-Type"]);
+
+  EXPECT_TRUE(http_request_.has_content);
+  EXPECT_EQ(
+      "{\"parents\":[{\"id\":\"parent_resource_id\"}],\"title\":\"new title\"}",
+      http_request_.content);
+  EXPECT_TRUE(file_resource);
+}
+
+TEST_F(DriveApiRequestsTest, FilesCopyRequest_EmptyParentResourceId) {
+  // Set an expected data file containing the dummy file entry data.
+  // It'd be returned if we copy a file.
+  expected_data_file_path_ =
+      test_util::GetTestFilePath("drive/file_entry.json");
+
+  GDataErrorCode error = GDATA_OTHER_ERROR;
+  scoped_ptr<FileResource> file_resource;
+
+  // Copy the file to a new file named "new title".
+  {
+    base::RunLoop run_loop;
+    drive::FilesCopyRequest* request = new drive::FilesCopyRequest(
+        request_sender_.get(),
+        *url_generator_,
+        test_util::CreateQuitCallback(
+            &run_loop,
+            test_util::CreateCopyResultCallback(&error, &file_resource)));
+    request->set_file_id("resource_id");
+    request->set_title("new title");
+    request_sender_->StartRequestWithRetry(request);
+    run_loop.Run();
+  }
+
+  EXPECT_EQ(HTTP_SUCCESS, error);
+  EXPECT_EQ(net::test_server::METHOD_POST, http_request_.method);
+  EXPECT_EQ("/drive/v2/files/resource_id/copy", http_request_.relative_url);
+  EXPECT_EQ("application/json", http_request_.headers["Content-Type"]);
+
+  EXPECT_TRUE(http_request_.has_content);
+  EXPECT_EQ("{\"title\":\"new title\"}", http_request_.content);
+  EXPECT_TRUE(file_resource);
+}
+
+TEST_F(DriveApiRequestsTest, FilesListRequest) {
   // Set an expected data file containing valid result.
   expected_data_file_path_ = test_util::GetTestFilePath(
       "drive/filelist.json");
 
   GDataErrorCode error = GDATA_OTHER_ERROR;
-  scoped_ptr<base::Value> result;
+  scoped_ptr<FileList> result;
 
   {
     base::RunLoop run_loop;
-    GetFilelistRequest* request = new GetFilelistRequest(
-        request_sender_.get(),
-        *url_generator_,
-        "\"abcde\" in parents",
-        50,  // max results
+    drive::FilesListRequest* request = new drive::FilesListRequest(
+        request_sender_.get(), *url_generator_,
         test_util::CreateQuitCallback(
             &run_loop,
             test_util::CreateCopyResultCallback(&error, &result)));
+    request->set_max_results(50);
+    request->set_q("\"abcde\" in parents");
     request_sender_->StartRequestWithRetry(request);
     run_loop.Run();
   }
@@ -518,48 +681,34 @@
   EXPECT_TRUE(result);
 }
 
-TEST_F(DriveApiRequestsTest, CreateDirectoryRequest) {
-  // Set an expected data file containing the directory's entry data.
+TEST_F(DriveApiRequestsTest, FilesTrashRequest) {
+  // Set data for the expected result. Directory entry should be returned
+  // if the trashing entry is a directory, so using it here should be fine.
   expected_data_file_path_ =
       test_util::GetTestFilePath("drive/directory_entry.json");
 
   GDataErrorCode error = GDATA_OTHER_ERROR;
   scoped_ptr<FileResource> file_resource;
 
-  // Create "new directory" in the root directory.
+  // Trash a resource with the given resource id.
   {
     base::RunLoop run_loop;
-    drive::CreateDirectoryRequest* request =
-        new drive::CreateDirectoryRequest(
-            request_sender_.get(),
-            *url_generator_,
-            "root",
-            "new directory",
-            test_util::CreateQuitCallback(
-                &run_loop,
-                test_util::CreateCopyResultCallback(&error, &file_resource)));
+    drive::FilesTrashRequest* request = new drive::FilesTrashRequest(
+        request_sender_.get(),
+        *url_generator_,
+        test_util::CreateQuitCallback(
+            &run_loop,
+            test_util::CreateCopyResultCallback(&error, &file_resource)));
+    request->set_file_id("resource_id");
     request_sender_->StartRequestWithRetry(request);
     run_loop.Run();
   }
 
   EXPECT_EQ(HTTP_SUCCESS, error);
   EXPECT_EQ(net::test_server::METHOD_POST, http_request_.method);
-  EXPECT_EQ("/drive/v2/files", http_request_.relative_url);
-  EXPECT_EQ("application/json", http_request_.headers["Content-Type"]);
-
+  EXPECT_EQ("/drive/v2/files/resource_id/trash", http_request_.relative_url);
   EXPECT_TRUE(http_request_.has_content);
-
-  scoped_ptr<FileResource> expected(
-      FileResource::CreateFrom(
-          *test_util::LoadJSONFile("drive/directory_entry.json")));
-
-  // Sanity check.
-  ASSERT_TRUE(file_resource.get());
-
-  EXPECT_EQ(expected->file_id(), file_resource->file_id());
-  EXPECT_EQ(expected->title(), file_resource->title());
-  EXPECT_EQ(expected->mime_type(), file_resource->mime_type());
-  EXPECT_EQ(expected->parents().size(), file_resource->parents().size());
+  EXPECT_TRUE(http_request_.content.empty());
 }
 
 TEST_F(DriveApiRequestsTest, TouchResourceRequest) {
@@ -603,80 +752,6 @@
             http_request_.content);
 }
 
-TEST_F(DriveApiRequestsTest, CopyResourceRequest) {
-  // Set an expected data file containing the dummy file entry data.
-  // It'd be returned if we copy a file.
-  expected_data_file_path_ =
-      test_util::GetTestFilePath("drive/file_entry.json");
-
-  GDataErrorCode error = GDATA_OTHER_ERROR;
-  scoped_ptr<FileResource> file_resource;
-
-  // Copy the file to a new file named "new title".
-  {
-    base::RunLoop run_loop;
-    drive::CopyResourceRequest* request =
-        new drive::CopyResourceRequest(
-            request_sender_.get(),
-            *url_generator_,
-            "resource_id",
-            "parent_resource_id",
-            "new title",
-            test_util::CreateQuitCallback(
-                &run_loop,
-                test_util::CreateCopyResultCallback(&error, &file_resource)));
-    request_sender_->StartRequestWithRetry(request);
-    run_loop.Run();
-  }
-
-  EXPECT_EQ(HTTP_SUCCESS, error);
-  EXPECT_EQ(net::test_server::METHOD_POST, http_request_.method);
-  EXPECT_EQ("/drive/v2/files/resource_id/copy", http_request_.relative_url);
-  EXPECT_EQ("application/json", http_request_.headers["Content-Type"]);
-
-  EXPECT_TRUE(http_request_.has_content);
-  EXPECT_EQ(
-      "{\"parents\":[{\"id\":\"parent_resource_id\"}],\"title\":\"new title\"}",
-      http_request_.content);
-  EXPECT_TRUE(file_resource);
-}
-
-TEST_F(DriveApiRequestsTest, CopyResourceRequest_EmptyParentResourceId) {
-  // Set an expected data file containing the dummy file entry data.
-  // It'd be returned if we copy a file.
-  expected_data_file_path_ =
-      test_util::GetTestFilePath("drive/file_entry.json");
-
-  GDataErrorCode error = GDATA_OTHER_ERROR;
-  scoped_ptr<FileResource> file_resource;
-
-  // Copy the file to a new file named "new title".
-  {
-    base::RunLoop run_loop;
-    drive::CopyResourceRequest* request =
-        new drive::CopyResourceRequest(
-            request_sender_.get(),
-            *url_generator_,
-            "resource_id",
-            std::string(),  // parent resource id.
-            "new title",
-            test_util::CreateQuitCallback(
-                &run_loop,
-                test_util::CreateCopyResultCallback(&error, &file_resource)));
-    request_sender_->StartRequestWithRetry(request);
-    run_loop.Run();
-  }
-
-  EXPECT_EQ(HTTP_SUCCESS, error);
-  EXPECT_EQ(net::test_server::METHOD_POST, http_request_.method);
-  EXPECT_EQ("/drive/v2/files/resource_id/copy", http_request_.relative_url);
-  EXPECT_EQ("application/json", http_request_.headers["Content-Type"]);
-
-  EXPECT_TRUE(http_request_.has_content);
-  EXPECT_EQ("{\"title\":\"new title\"}", http_request_.content);
-  EXPECT_TRUE(file_resource);
-}
-
 TEST_F(DriveApiRequestsTest, MoveResourceRequest) {
   // Set an expected data file containing the dummy file entry data.
   // It'd be returned if we move a file.
@@ -752,36 +827,6 @@
   EXPECT_TRUE(file_resource);
 }
 
-TEST_F(DriveApiRequestsTest, TrashResourceRequest) {
-  // Set data for the expected result. Directory entry should be returned
-  // if the trashing entry is a directory, so using it here should be fine.
-  expected_data_file_path_ =
-      test_util::GetTestFilePath("drive/directory_entry.json");
-
-  GDataErrorCode error = GDATA_OTHER_ERROR;
-
-  // Trash a resource with the given resource id.
-  {
-    base::RunLoop run_loop;
-    drive::TrashResourceRequest* request =
-        new drive::TrashResourceRequest(
-            request_sender_.get(),
-            *url_generator_,
-            "resource_id",
-            test_util::CreateQuitCallback(
-                &run_loop,
-                test_util::CreateCopyResultCallback(&error)));
-    request_sender_->StartRequestWithRetry(request);
-    run_loop.Run();
-  }
-
-  EXPECT_EQ(HTTP_SUCCESS, error);
-  EXPECT_EQ(net::test_server::METHOD_POST, http_request_.method);
-  EXPECT_EQ("/drive/v2/files/resource_id/trash", http_request_.relative_url);
-  EXPECT_TRUE(http_request_.has_content);
-  EXPECT_TRUE(http_request_.content.empty());
-}
-
 TEST_F(DriveApiRequestsTest, InsertResourceRequest) {
   // Set an expected data file containing the children entry.
   expected_content_type_ = "application/json";
diff --git a/chrome/browser/google_apis/drive_api_url_generator.cc b/chrome/browser/google_apis/drive_api_url_generator.cc
index 53a6200..1f65916 100644
--- a/chrome/browser/google_apis/drive_api_url_generator.cc
+++ b/chrome/browser/google_apis/drive_api_url_generator.cc
@@ -16,7 +16,7 @@
 
 // Hard coded URLs for communication with a google drive server.
 const char kDriveV2AboutUrl[] = "/drive/v2/about";
-const char kDriveV2ApplistUrl[] = "/drive/v2/apps";
+const char kDriveV2AppsUrl[] = "/drive/v2/apps";
 const char kDriveV2ChangelistUrl[] = "/drive/v2/changes";
 const char kDriveV2FilesUrl[] = "/drive/v2/files";
 const char kDriveV2FileUrlPrefix[] = "/drive/v2/files/";
@@ -57,55 +57,67 @@
 const char DriveApiUrlGenerator::kBaseDownloadUrlForProduction[] =
     "https://www.googledrive.com/host/";
 
-GURL DriveApiUrlGenerator::GetAboutUrl() const {
+GURL DriveApiUrlGenerator::GetAboutGetUrl() const {
   return base_url_.Resolve(kDriveV2AboutUrl);
 }
 
-GURL DriveApiUrlGenerator::GetApplistUrl() const {
-  return base_url_.Resolve(kDriveV2ApplistUrl);
+GURL DriveApiUrlGenerator::GetAppsListUrl() const {
+  return base_url_.Resolve(kDriveV2AppsUrl);
 }
 
-GURL DriveApiUrlGenerator::GetChangelistUrl(
-    bool include_deleted, int64 start_changestamp, int max_results) const {
-  DCHECK_GE(start_changestamp, 0);
-
-  GURL url = base_url_.Resolve(kDriveV2ChangelistUrl);
-  if (!include_deleted) {
-    // If include_deleted is set to "false", set the query parameter,
-    // because its default parameter is "true".
-    url = net::AppendOrReplaceQueryParameter(url, "includeDeleted", "false");
-  }
-
-  if (start_changestamp > 0) {
-    url = net::AppendOrReplaceQueryParameter(
-        url, "startChangeId", base::Int64ToString(start_changestamp));
-  }
-
-  return AddMaxResultParam(url, max_results);
-}
-
-GURL DriveApiUrlGenerator::GetFilesUrl() const {
-  return base_url_.Resolve(kDriveV2FilesUrl);
-}
-
-GURL DriveApiUrlGenerator::GetFilelistUrl(
-    const std::string& search_string, int max_results) const {
-  GURL url = base_url_.Resolve(kDriveV2FilesUrl);
-  url = AddMaxResultParam(url, max_results);
-  return search_string.empty() ?
-      url :
-      net::AppendOrReplaceQueryParameter(url, "q", search_string);
-}
-
-GURL DriveApiUrlGenerator::GetFileUrl(const std::string& file_id) const {
+GURL DriveApiUrlGenerator::GetFilesGetUrl(const std::string& file_id) const {
   return base_url_.Resolve(kDriveV2FileUrlPrefix + net::EscapePath(file_id));
 }
 
-GURL DriveApiUrlGenerator::GetFileCopyUrl(
-    const std::string& resource_id) const {
-  return base_url_.Resolve(
-      base::StringPrintf(kDriveV2FileCopyUrlFormat,
-                         net::EscapePath(resource_id).c_str()));
+GURL DriveApiUrlGenerator::GetFilesInsertUrl() const {
+  return base_url_.Resolve(kDriveV2FilesUrl);
+}
+
+GURL DriveApiUrlGenerator::GetFilesPatchUrl(const std::string& file_id,
+                                            bool set_modified_date,
+                                            bool update_viewed_date) const {
+  GURL url =
+      base_url_.Resolve(kDriveV2FileUrlPrefix + net::EscapePath(file_id));
+
+  // setModifiedDate is "false" by default.
+  if (set_modified_date)
+    url = net::AppendOrReplaceQueryParameter(url, "setModifiedDate", "true");
+
+  // updateViewedDate is "true" by default.
+  if (!update_viewed_date)
+    url = net::AppendOrReplaceQueryParameter(url, "updateViewedDate", "false");
+
+  return url;
+}
+
+GURL DriveApiUrlGenerator::GetFilesCopyUrl(const std::string& file_id) const {
+  return base_url_.Resolve(base::StringPrintf(
+      kDriveV2FileCopyUrlFormat, net::EscapePath(file_id).c_str()));
+}
+
+GURL DriveApiUrlGenerator::GetFilesListUrl(int max_results,
+                                           const std::string& page_token,
+                                           const std::string& q) const {
+  GURL url = base_url_.Resolve(kDriveV2FilesUrl);
+
+  // maxResults is 100 by default.
+  if (max_results != 100) {
+    url = net::AppendOrReplaceQueryParameter(
+        url, "maxResults", base::IntToString(max_results));
+  }
+
+  if (!page_token.empty())
+    url = net::AppendOrReplaceQueryParameter(url, "pageToken", page_token);
+
+  if (!q.empty())
+    url = net::AppendOrReplaceQueryParameter(url, "q", q);
+
+  return url;
+}
+
+GURL DriveApiUrlGenerator::GetFilesTrashUrl(const std::string& file_id) const {
+  return base_url_.Resolve(base::StringPrintf(
+      kDriveV2FileTrashUrlFormat, net::EscapePath(file_id).c_str()));
 }
 
 GURL DriveApiUrlGenerator::GetFileTouchUrl(
@@ -123,10 +135,32 @@
   return url;
 }
 
-GURL DriveApiUrlGenerator::GetFileTrashUrl(const std::string& file_id) const {
-  return base_url_.Resolve(
-      base::StringPrintf(kDriveV2FileTrashUrlFormat,
-                         net::EscapePath(file_id).c_str()));
+GURL DriveApiUrlGenerator::GetChangesListUrl(bool include_deleted,
+                                             int max_results,
+                                             const std::string& page_token,
+                                             int64 start_change_id) const {
+  DCHECK_GE(start_change_id, 0);
+
+  GURL url = base_url_.Resolve(kDriveV2ChangelistUrl);
+
+  // includeDeleted is "true" by default.
+  if (!include_deleted)
+    url = net::AppendOrReplaceQueryParameter(url, "includeDeleted", "false");
+
+  // maxResults is "100" by default.
+  if (max_results != 100) {
+    url = net::AppendOrReplaceQueryParameter(
+        url, "maxResults", base::IntToString(max_results));
+  }
+
+  if (!page_token.empty())
+    url = net::AppendOrReplaceQueryParameter(url, "pageToken", page_token);
+
+  if (start_change_id > 0)
+    url = net::AppendOrReplaceQueryParameter(
+        url, "startChangeId", base::Int64ToString(start_change_id));
+
+  return url;
 }
 
 GURL DriveApiUrlGenerator::GetChildrenUrl(
diff --git a/chrome/browser/google_apis/drive_api_url_generator.h b/chrome/browser/google_apis/drive_api_url_generator.h
index f121969..1a64a50 100644
--- a/chrome/browser/google_apis/drive_api_url_generator.h
+++ b/chrome/browser/google_apis/drive_api_url_generator.h
@@ -26,47 +26,42 @@
   // The base URL for the file download server for production.
   static const char kBaseDownloadUrlForProduction[];
 
-  // Returns a URL to fetch "about" data.
-  GURL GetAboutUrl() const;
+  // Returns a URL to invoke "About: get" method.
+  GURL GetAboutGetUrl() const;
 
-  // Returns a URL to fetch "applist" data.
-  GURL GetApplistUrl() const;
+  // Returns a URL to invoke "Apps: list" method.
+  GURL GetAppsListUrl() const;
 
-  // Returns a URL to fetch a list of changes.
-  // include_deleted:
-  //   Set to true if the requesting change list should contain the deleted
-  //   entries. Otherwise false.
-  // start_changestamp:
-  //   The starting point of the requesting change list, or 0 if all changes
-  //   are necessary.
-  // max_results:
-  //   The max of the number of files resource in the response.
-  GURL GetChangelistUrl(
-      bool include_deleted, int64 start_changestamp, int max_results) const;
+  // Returns a URL to fetch a file metadata.
+  GURL GetFilesGetUrl(const std::string& file_id) const;
 
-  // Returns a URL to edit (especially add) a resource, such as inserting
-  // a file metadata or creating a new directory.
-  GURL GetFilesUrl() const;
+  // Returns a URL to create a resource.
+  GURL GetFilesInsertUrl() const;
 
-  // Returns a URL to fetch a list of files with the given |search_string|.
-  // search_string: The search query.
-  // max_results: The max of the number of files resource in the response.
-  GURL GetFilelistUrl(const std::string& search_string, int max_results) const;
+  // Returns a URL to patch file metadata.
+  GURL GetFilesPatchUrl(const std::string& file_id,
+                        bool set_modified_date,
+                        bool update_viewed_date) const;
 
-  // Returns a URL to fetch a file content.
-  GURL GetFileUrl(const std::string& file_id) const;
+  // Returns a URL to copy a resource specified by |file_id|.
+  GURL GetFilesCopyUrl(const std::string& file_id) const;
 
-  // Returns a URL to copy a file specified by |resource_id|.
-  GURL GetFileCopyUrl(const std::string& resource_id) const;
+  // Returns a URL to fetch file list.
+  GURL GetFilesListUrl(int max_results,
+                       const std::string& page_token,
+                       const std::string& q) const;
+
+  // Returns a URL to trash a resource with the given |file_id|.
+  GURL GetFilesTrashUrl(const std::string& file_id) const;
 
   // Returns a URL to touch a resource specified by |resource_id|.
   GURL GetFileTouchUrl(const std::string& resource_id) const;
 
-  // Returns a URL to trash a resource with the given |resource_id|.
-  // Note that the |resource_id| is corresponding to the "file id" in the
-  // document: https://developers.google.com/drive/v2/reference/files/trash
-  // but we use the term "resource" for consistency in our code.
-  GURL GetFileTrashUrl(const std::string& resource_id) const;
+  // Returns a URL to fetch a list of changes.
+  GURL GetChangesListUrl(bool include_deleted,
+                         int max_results,
+                         const std::string& page_token,
+                         int64 start_change_id) const;
 
   // Returns a URL to add a resource to a directory with |resource_id|.
   // Note that the |resource_id| is corresponding to the "folder id" in the
diff --git a/chrome/browser/google_apis/drive_api_url_generator_unittest.cc b/chrome/browser/google_apis/drive_api_url_generator_unittest.cc
index cb6212d..e877103 100644
--- a/chrome/browser/google_apis/drive_api_url_generator_unittest.cc
+++ b/chrome/browser/google_apis/drive_api_url_generator_unittest.cc
@@ -27,106 +27,180 @@
 };
 
 // Make sure the hard-coded urls are returned.
-TEST_F(DriveApiUrlGeneratorTest, GetAboutUrl) {
+TEST_F(DriveApiUrlGeneratorTest, GetAboutGetUrl) {
   EXPECT_EQ("https://www.googleapis.com/drive/v2/about",
-            url_generator_.GetAboutUrl().spec());
+            url_generator_.GetAboutGetUrl().spec());
   EXPECT_EQ("http://127.0.0.1:12345/drive/v2/about",
-            test_url_generator_.GetAboutUrl().spec());
+            test_url_generator_.GetAboutGetUrl().spec());
 }
 
-TEST_F(DriveApiUrlGeneratorTest, GetApplistUrl) {
+TEST_F(DriveApiUrlGeneratorTest, GetAppsListUrl) {
   EXPECT_EQ("https://www.googleapis.com/drive/v2/apps",
-            url_generator_.GetApplistUrl().spec());
+            url_generator_.GetAppsListUrl().spec());
   EXPECT_EQ("http://127.0.0.1:12345/drive/v2/apps",
-            test_url_generator_.GetApplistUrl().spec());
+            test_url_generator_.GetAppsListUrl().spec());
 }
 
-TEST_F(DriveApiUrlGeneratorTest, GetChangelistUrl) {
-  // Do not add startChangeId parameter if |start_changestamp| is 0.
-  EXPECT_EQ("https://www.googleapis.com/drive/v2/changes?maxResults=500",
-            url_generator_.GetChangelistUrl(true, 0, 500).spec());
-  EXPECT_EQ("http://127.0.0.1:12345/drive/v2/changes?maxResults=500",
-            test_url_generator_.GetChangelistUrl(true, 0, 500).spec());
-
-  // Set includeDeleted parameter if |include_deleted| is set to false.
-  EXPECT_EQ("https://www.googleapis.com/drive/v2/changes"
-            "?includeDeleted=false&maxResults=500",
-            url_generator_.GetChangelistUrl(false, 0, 500).spec());
-  EXPECT_EQ("http://127.0.0.1:12345/drive/v2/changes"
-            "?includeDeleted=false&maxResults=500",
-            test_url_generator_.GetChangelistUrl(false, 0, 500).spec());
-
-  // Set startChangeId parameter if |start_changestamp| is given.
-  EXPECT_EQ("https://www.googleapis.com/drive/v2/changes"
-            "?startChangeId=100&maxResults=500",
-            url_generator_.GetChangelistUrl(true, 100, 500).spec());
-  EXPECT_EQ("http://127.0.0.1:12345/drive/v2/changes"
-            "?startChangeId=100&maxResults=500",
-            test_url_generator_.GetChangelistUrl(true, 100, 500).spec());
-
-  // includeDeleted and startChangeId parameter can be set at the same time.
-  EXPECT_EQ(
-      "https://www.googleapis.com/drive/v2/changes"
-      "?includeDeleted=false&startChangeId=100&maxResults=500",
-      url_generator_.GetChangelistUrl(false, 100, 500).spec());
-  EXPECT_EQ("http://127.0.0.1:12345/drive/v2/changes?"
-            "includeDeleted=false&startChangeId=100&maxResults=500",
-            test_url_generator_.GetChangelistUrl(false, 100, 500).spec());
-}
-
-TEST_F(DriveApiUrlGeneratorTest, GetFilesUrl) {
-  EXPECT_EQ("https://www.googleapis.com/drive/v2/files",
-            url_generator_.GetFilesUrl().spec());
-  EXPECT_EQ("http://127.0.0.1:12345/drive/v2/files",
-            test_url_generator_.GetFilesUrl().spec());
-}
-
-TEST_F(DriveApiUrlGeneratorTest, GetFilelistUrl) {
-  // Do not add q parameter if |search_string| is empty.
-  EXPECT_EQ("https://www.googleapis.com/drive/v2/files?maxResults=50",
-            url_generator_.GetFilelistUrl(std::string(), 50).spec());
-  EXPECT_EQ("http://127.0.0.1:12345/drive/v2/files?maxResults=50",
-            test_url_generator_.GetFilelistUrl(std::string(), 50).spec());
-
-  // Set q parameter if non-empty |search_string| is given.
-  EXPECT_EQ("https://www.googleapis.com/drive/v2/files?maxResults=50&q=query",
-            url_generator_.GetFilelistUrl("query", 50).spec());
-  EXPECT_EQ("http://127.0.0.1:12345/drive/v2/files?maxResults=50&q=query",
-            test_url_generator_.GetFilelistUrl("query", 50).spec());
-}
-
-TEST_F(DriveApiUrlGeneratorTest, GetFileUrl) {
+TEST_F(DriveApiUrlGeneratorTest, GetFilesGetUrl) {
   // |file_id| should be embedded into the url.
   EXPECT_EQ("https://www.googleapis.com/drive/v2/files/0ADK06pfg",
-            url_generator_.GetFileUrl("0ADK06pfg").spec());
+            url_generator_.GetFilesGetUrl("0ADK06pfg").spec());
   EXPECT_EQ("https://www.googleapis.com/drive/v2/files/0Bz0bd074",
-            url_generator_.GetFileUrl("0Bz0bd074").spec());
+            url_generator_.GetFilesGetUrl("0Bz0bd074").spec());
   EXPECT_EQ("https://www.googleapis.com/drive/v2/files/file%3Afile_id",
-            url_generator_.GetFileUrl("file:file_id").spec());
+            url_generator_.GetFilesGetUrl("file:file_id").spec());
 
   EXPECT_EQ("http://127.0.0.1:12345/drive/v2/files/0ADK06pfg",
-            test_url_generator_.GetFileUrl("0ADK06pfg").spec());
+            test_url_generator_.GetFilesGetUrl("0ADK06pfg").spec());
   EXPECT_EQ("http://127.0.0.1:12345/drive/v2/files/0Bz0bd074",
-            test_url_generator_.GetFileUrl("0Bz0bd074").spec());
+            test_url_generator_.GetFilesGetUrl("0Bz0bd074").spec());
   EXPECT_EQ("http://127.0.0.1:12345/drive/v2/files/file%3Afile_id",
-            test_url_generator_.GetFileUrl("file:file_id").spec());
+            test_url_generator_.GetFilesGetUrl("file:file_id").spec());
 }
 
-TEST_F(DriveApiUrlGeneratorTest, GetFileCopyUrl) {
+TEST_F(DriveApiUrlGeneratorTest, GetFilesInsertUrl) {
+  EXPECT_EQ("https://www.googleapis.com/drive/v2/files",
+            url_generator_.GetFilesInsertUrl().spec());
+  EXPECT_EQ("http://127.0.0.1:12345/drive/v2/files",
+            test_url_generator_.GetFilesInsertUrl().spec());
+}
+
+TEST_F(DriveApiUrlGeneratorTest, GetFilePatchUrl) {
+  struct TestPattern {
+    bool set_modified_date;
+    bool update_viewed_date;
+    const std::string expected_query;
+  };
+  const TestPattern kTestPatterns[] = {
+    { false, true, "" },
+    { true, true, "?setModifiedDate=true" },
+    { false, false, "?updateViewedDate=false" },
+    { true, false, "?setModifiedDate=true&updateViewedDate=false" },
+  };
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestPatterns); ++i) {
+    EXPECT_EQ(
+        "https://www.googleapis.com/drive/v2/files/0ADK06pfg" +
+            kTestPatterns[i].expected_query,
+        url_generator_.GetFilesPatchUrl(
+            "0ADK06pfg",
+            kTestPatterns[i].set_modified_date,
+            kTestPatterns[i].update_viewed_date).spec());
+
+    EXPECT_EQ(
+        "https://www.googleapis.com/drive/v2/files/0Bz0bd074" +
+            kTestPatterns[i].expected_query,
+        url_generator_.GetFilesPatchUrl(
+            "0Bz0bd074",
+            kTestPatterns[i].set_modified_date,
+            kTestPatterns[i].update_viewed_date).spec());
+
+    EXPECT_EQ(
+        "https://www.googleapis.com/drive/v2/files/file%3Afile_id" +
+            kTestPatterns[i].expected_query,
+        url_generator_.GetFilesPatchUrl(
+            "file:file_id",
+            kTestPatterns[i].set_modified_date,
+            kTestPatterns[i].update_viewed_date).spec());
+
+
+    EXPECT_EQ(
+        "http://127.0.0.1:12345/drive/v2/files/0ADK06pfg" +
+            kTestPatterns[i].expected_query,
+        test_url_generator_.GetFilesPatchUrl(
+            "0ADK06pfg",
+            kTestPatterns[i].set_modified_date,
+            kTestPatterns[i].update_viewed_date).spec());
+
+    EXPECT_EQ(
+        "http://127.0.0.1:12345/drive/v2/files/0Bz0bd074" +
+            kTestPatterns[i].expected_query,
+        test_url_generator_.GetFilesPatchUrl(
+            "0Bz0bd074",
+            kTestPatterns[i].set_modified_date,
+            kTestPatterns[i].update_viewed_date).spec());
+
+    EXPECT_EQ(
+        "http://127.0.0.1:12345/drive/v2/files/file%3Afile_id" +
+            kTestPatterns[i].expected_query,
+        test_url_generator_.GetFilesPatchUrl(
+            "file:file_id",
+            kTestPatterns[i].set_modified_date,
+            kTestPatterns[i].update_viewed_date).spec());
+  }
+}
+
+TEST_F(DriveApiUrlGeneratorTest, GetFilesCopyUrl) {
   // |file_id| should be embedded into the url.
   EXPECT_EQ("https://www.googleapis.com/drive/v2/files/0ADK06pfg/copy",
-            url_generator_.GetFileCopyUrl("0ADK06pfg").spec());
+            url_generator_.GetFilesCopyUrl("0ADK06pfg").spec());
   EXPECT_EQ("https://www.googleapis.com/drive/v2/files/0Bz0bd074/copy",
-            url_generator_.GetFileCopyUrl("0Bz0bd074").spec());
+            url_generator_.GetFilesCopyUrl("0Bz0bd074").spec());
   EXPECT_EQ("https://www.googleapis.com/drive/v2/files/file%3Afile_id/copy",
-            url_generator_.GetFileCopyUrl("file:file_id").spec());
+            url_generator_.GetFilesCopyUrl("file:file_id").spec());
 
   EXPECT_EQ("http://127.0.0.1:12345/drive/v2/files/0ADK06pfg/copy",
-            test_url_generator_.GetFileCopyUrl("0ADK06pfg").spec());
+            test_url_generator_.GetFilesCopyUrl("0ADK06pfg").spec());
   EXPECT_EQ("http://127.0.0.1:12345/drive/v2/files/0Bz0bd074/copy",
-            test_url_generator_.GetFileCopyUrl("0Bz0bd074").spec());
+            test_url_generator_.GetFilesCopyUrl("0Bz0bd074").spec());
   EXPECT_EQ("http://127.0.0.1:12345/drive/v2/files/file%3Afile_id/copy",
-            test_url_generator_.GetFileCopyUrl("file:file_id").spec());
+            test_url_generator_.GetFilesCopyUrl("file:file_id").spec());
+}
+
+TEST_F(DriveApiUrlGeneratorTest, GetFilesListUrl) {
+  struct TestPattern {
+    int max_results;
+    const std::string page_token;
+    const std::string q;
+    const std::string expected_query;
+  };
+  const TestPattern kTestPatterns[] = {
+    { 100, "", "", "" },
+    { 150, "", "", "?maxResults=150" },
+    { 10, "", "", "?maxResults=10" },
+    { 100, "token", "", "?pageToken=token" },
+    { 150, "token", "", "?maxResults=150&pageToken=token" },
+    { 10, "token", "", "?maxResults=10&pageToken=token" },
+    { 100, "", "query", "?q=query" },
+    { 150, "", "query", "?maxResults=150&q=query" },
+    { 10, "", "query", "?maxResults=10&q=query" },
+    { 100, "token", "query", "?pageToken=token&q=query" },
+    { 150, "token", "query", "?maxResults=150&pageToken=token&q=query" },
+    { 10, "token", "query", "?maxResults=10&pageToken=token&q=query" },
+  };
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestPatterns); ++i) {
+    EXPECT_EQ(
+        "https://www.googleapis.com/drive/v2/files" +
+            kTestPatterns[i].expected_query,
+        url_generator_.GetFilesListUrl(
+            kTestPatterns[i].max_results, kTestPatterns[i].page_token,
+            kTestPatterns[i].q).spec());
+
+    EXPECT_EQ(
+        "http://127.0.0.1:12345/drive/v2/files" +
+            kTestPatterns[i].expected_query,
+        test_url_generator_.GetFilesListUrl(
+            kTestPatterns[i].max_results, kTestPatterns[i].page_token,
+            kTestPatterns[i].q).spec());
+  }
+}
+
+TEST_F(DriveApiUrlGeneratorTest, GetFilesTrashUrl) {
+  // |file_id| should be embedded into the url.
+  EXPECT_EQ("https://www.googleapis.com/drive/v2/files/0ADK06pfg/trash",
+            url_generator_.GetFilesTrashUrl("0ADK06pfg").spec());
+  EXPECT_EQ("https://www.googleapis.com/drive/v2/files/0Bz0bd074/trash",
+            url_generator_.GetFilesTrashUrl("0Bz0bd074").spec());
+  EXPECT_EQ("https://www.googleapis.com/drive/v2/files/file%3Afile_id/trash",
+            url_generator_.GetFilesTrashUrl("file:file_id").spec());
+
+  EXPECT_EQ("http://127.0.0.1:12345/drive/v2/files/0ADK06pfg/trash",
+            test_url_generator_.GetFilesTrashUrl("0ADK06pfg").spec());
+  EXPECT_EQ("http://127.0.0.1:12345/drive/v2/files/0Bz0bd074/trash",
+            test_url_generator_.GetFilesTrashUrl("0Bz0bd074").spec());
+  EXPECT_EQ("http://127.0.0.1:12345/drive/v2/files/file%3Afile_id/trash",
+            test_url_generator_.GetFilesTrashUrl("file:file_id").spec());
 }
 
 TEST_F(DriveApiUrlGeneratorTest, GetFileTouchUrl) {
@@ -152,6 +226,76 @@
             test_url_generator_.GetFileTouchUrl("file:file_id").spec());
 }
 
+TEST_F(DriveApiUrlGeneratorTest, GetChangesListUrl) {
+  struct TestPattern {
+    bool include_deleted;
+    int max_results;
+    const std::string page_token;
+    int64 start_change_id;
+    const std::string expected_query;
+  };
+  const TestPattern kTestPatterns[] = {
+    { true, 100, "", 0, "" },
+    { false, 100, "", 0, "?includeDeleted=false" },
+    { true, 150, "", 0, "?maxResults=150" },
+    { false, 150, "", 0, "?includeDeleted=false&maxResults=150" },
+    { true, 10, "", 0, "?maxResults=10" },
+    { false, 10, "", 0, "?includeDeleted=false&maxResults=10" },
+
+    { true, 100, "token", 0, "?pageToken=token" },
+    { false, 100, "token", 0, "?includeDeleted=false&pageToken=token" },
+    { true, 150, "token", 0, "?maxResults=150&pageToken=token" },
+    { false, 150, "token", 0,
+      "?includeDeleted=false&maxResults=150&pageToken=token" },
+    { true, 10, "token", 0, "?maxResults=10&pageToken=token" },
+    { false, 10, "token", 0,
+      "?includeDeleted=false&maxResults=10&pageToken=token" },
+
+    { true, 100, "", 12345, "?startChangeId=12345" },
+    { false, 100, "", 12345, "?includeDeleted=false&startChangeId=12345" },
+    { true, 150, "", 12345, "?maxResults=150&startChangeId=12345" },
+    { false, 150, "", 12345,
+      "?includeDeleted=false&maxResults=150&startChangeId=12345" },
+    { true, 10, "", 12345, "?maxResults=10&startChangeId=12345" },
+    { false, 10, "", 12345,
+      "?includeDeleted=false&maxResults=10&startChangeId=12345" },
+
+    { true, 100, "token", 12345, "?pageToken=token&startChangeId=12345" },
+    { false, 100, "token", 12345,
+      "?includeDeleted=false&pageToken=token&startChangeId=12345" },
+    { true, 150, "token", 12345,
+      "?maxResults=150&pageToken=token&startChangeId=12345" },
+    { false, 150, "token", 12345,
+      "?includeDeleted=false&maxResults=150&pageToken=token"
+      "&startChangeId=12345" },
+    { true, 10, "token", 12345,
+      "?maxResults=10&pageToken=token&startChangeId=12345" },
+    { false, 10, "token", 12345,
+      "?includeDeleted=false&maxResults=10&pageToken=token"
+      "&startChangeId=12345" },
+  };
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestPatterns); ++i) {
+    EXPECT_EQ(
+        "https://www.googleapis.com/drive/v2/changes" +
+            kTestPatterns[i].expected_query,
+        url_generator_.GetChangesListUrl(
+            kTestPatterns[i].include_deleted,
+            kTestPatterns[i].max_results,
+            kTestPatterns[i].page_token,
+            kTestPatterns[i].start_change_id).spec());
+
+    EXPECT_EQ(
+        "http://127.0.0.1:12345/drive/v2/changes" +
+            kTestPatterns[i].expected_query,
+        test_url_generator_.GetChangesListUrl(
+            kTestPatterns[i].include_deleted,
+            kTestPatterns[i].max_results,
+            kTestPatterns[i].page_token,
+            kTestPatterns[i].start_change_id).spec());
+  }
+}
+
 TEST_F(DriveApiUrlGeneratorTest, GetChildrenUrl) {
   // |file_id| should be embedded into the url.
   EXPECT_EQ("https://www.googleapis.com/drive/v2/files/0ADK06pfg/children",
@@ -201,23 +345,6 @@
           "file:folder_id", "file:child_id").spec());
 }
 
-TEST_F(DriveApiUrlGeneratorTest, GetFileTrashUrl) {
-  // |file_id| should be embedded into the url.
-  EXPECT_EQ("https://www.googleapis.com/drive/v2/files/0ADK06pfg/trash",
-            url_generator_.GetFileTrashUrl("0ADK06pfg").spec());
-  EXPECT_EQ("https://www.googleapis.com/drive/v2/files/0Bz0bd074/trash",
-            url_generator_.GetFileTrashUrl("0Bz0bd074").spec());
-  EXPECT_EQ("https://www.googleapis.com/drive/v2/files/file%3Afile_id/trash",
-            url_generator_.GetFileTrashUrl("file:file_id").spec());
-
-  EXPECT_EQ("http://127.0.0.1:12345/drive/v2/files/0ADK06pfg/trash",
-            test_url_generator_.GetFileTrashUrl("0ADK06pfg").spec());
-  EXPECT_EQ("http://127.0.0.1:12345/drive/v2/files/0Bz0bd074/trash",
-            test_url_generator_.GetFileTrashUrl("0Bz0bd074").spec());
-  EXPECT_EQ("http://127.0.0.1:12345/drive/v2/files/file%3Afile_id/trash",
-            test_url_generator_.GetFileTrashUrl("file:file_id").spec());
-}
-
 TEST_F(DriveApiUrlGeneratorTest, GetInitiateUploadNewFileUrl) {
   EXPECT_EQ(
       "https://www.googleapis.com/upload/drive/v2/files?uploadType=resumable",
diff --git a/chrome/browser/google_apis/drive_common_callbacks.h b/chrome/browser/google_apis/drive_common_callbacks.h
index 24e125c..a7a35a2 100644
--- a/chrome/browser/google_apis/drive_common_callbacks.h
+++ b/chrome/browser/google_apis/drive_common_callbacks.h
@@ -30,7 +30,7 @@
 // Callback used for getting AboutResource.
 typedef base::Callback<void(GDataErrorCode error,
                             scoped_ptr<AboutResource> about_resource)>
-    GetAboutResourceCallback;
+    AboutResourceCallback;
 
 // Callback used for getting ShareUrl.
 typedef base::Callback<void(GDataErrorCode error,
@@ -38,7 +38,7 @@
 
 // Callback used for getting AppList.
 typedef base::Callback<void(GDataErrorCode error,
-                            scoped_ptr<AppList> app_list)> GetAppListCallback;
+                            scoped_ptr<AppList> app_list)> AppListCallback;
 
 // Callback used for handling UploadRangeResponse.
 typedef base::Callback<void(
diff --git a/chrome/browser/guestview/guestview.cc b/chrome/browser/guestview/guestview.cc
index 414962e..9f3ab44 100644
--- a/chrome/browser/guestview/guestview.cc
+++ b/chrome/browser/guestview/guestview.cc
@@ -43,6 +43,7 @@
     : guest_web_contents_(guest_web_contents),
       embedder_web_contents_(NULL),
       embedder_render_process_id_(0),
+      embedder_routing_id_(MSG_ROUTING_NONE),
       browser_context_(guest_web_contents->GetBrowserContext()),
       guest_instance_id_(guest_web_contents->GetEmbeddedInstanceID()),
       view_instance_id_(guestview::kInstanceIDNone) {
@@ -71,6 +72,7 @@
   embedder_web_contents_ = embedder_web_contents;
   embedder_render_process_id_ =
       embedder_web_contents->GetRenderProcessHost()->GetID();
+  embedder_routing_id_ = embedder_web_contents->GetRoutingID();
   extension_id_ = extension_id;
   args.GetInteger(guestview::kParameterInstanceId, &view_instance_id_);
 
diff --git a/chrome/browser/guestview/guestview.h b/chrome/browser/guestview/guestview.h
index 09b8efc..b83dbfd 100644
--- a/chrome/browser/guestview/guestview.h
+++ b/chrome/browser/guestview/guestview.h
@@ -85,6 +85,9 @@
   // Returns the embedder's process ID.
   int embedder_render_process_id() const { return embedder_render_process_id_; }
 
+  // Returns the embedder's routing ID.
+  int embedder_routing_id() const { return embedder_routing_id_; }
+
  protected:
   virtual ~GuestView();
 
@@ -98,6 +101,7 @@
   content::WebContents* embedder_web_contents_;
   std::string extension_id_;
   int embedder_render_process_id_;
+  int embedder_routing_id_;
   content::BrowserContext* browser_context_;
   // |guest_instance_id_| is a profile-wide unique identifier for a guest
   // WebContents.
diff --git a/chrome/browser/guestview/webview/webview_guest.cc b/chrome/browser/guestview/webview/webview_guest.cc
index 7900c66..4fdb81b 100644
--- a/chrome/browser/guestview/webview/webview_guest.cc
+++ b/chrome/browser/guestview/webview/webview_guest.cc
@@ -70,10 +70,15 @@
     void* profile,
     const std::string& extension_id,
     int embedder_process_id,
+    int embedder_routing_id,
     int guest_instance_id) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
   ExtensionWebRequestEventRouter::GetInstance()->RemoveWebViewEventListeners(
-      profile, extension_id, embedder_process_id, guest_instance_id);
+      profile,
+      extension_id,
+      embedder_process_id,
+      embedder_routing_id,
+      guest_instance_id);
 }
 
 }  // namespace
@@ -188,7 +193,7 @@
                              const std::string& error_type) {
   scoped_ptr<DictionaryValue> args(new DictionaryValue());
   args->SetBoolean(guestview::kIsTopLevel, is_top_level);
-  args->SetString(guestview::kUrl, url.spec());
+  args->SetString(guestview::kUrl, url.possibly_invalid_spec());
   args->SetString(guestview::kReason, error_type);
   DispatchEvent(new GuestView::Event(webview::kEventLoadAbort, args.Pass()));
 }
@@ -380,6 +385,11 @@
 
 void WebViewGuest::WebContentsDestroyed(WebContents* web_contents) {
   RemoveWebViewFromExtensionRendererState(web_contents);
+  // TODO(fsamuel): WebRequest event listeners for <webview> should survive
+  // reparenting of a <webview> within a single embedder. The lifetime of
+  // WebRequest event listeners should be equal to the lifetime of the embedder
+  // WebContents rather than the guest until http://crbug.com/156219 is
+  // resolved.
   content::BrowserThread::PostTask(
       content::BrowserThread::IO,
       FROM_HERE,
@@ -387,6 +397,7 @@
           &RemoveWebViewEventListenersOnIOThread,
           browser_context(), extension_id(),
           embedder_render_process_id(),
+          embedder_routing_id(),
           view_instance_id()));
 }
 
diff --git a/chrome/browser/history/DEPS b/chrome/browser/history/DEPS
index 677d9b6..dd13811 100644
--- a/chrome/browser/history/DEPS
+++ b/chrome/browser/history/DEPS
@@ -7,6 +7,7 @@
   "+chrome/browser/common",
   "+chrome/browser/favicon",
   "+chrome/browser/history",
+  "+chrome/browser/network_time",
 
   # TODO(erikwright): Bring this list to zero.
   #
diff --git a/chrome/browser/history/history_backend.cc b/chrome/browser/history/history_backend.cc
index 7cdecc5..e313002 100644
--- a/chrome/browser/history/history_backend.cc
+++ b/chrome/browser/history/history_backend.cc
@@ -413,7 +413,7 @@
     const GURL& origin_url(has_redirects ?
         request.redirects[0] : request.url);
     if (origin_url.SchemeIs(chrome::kHttpScheme) ||
-        origin_url.SchemeIs(chrome::kHttpsScheme) ||
+        origin_url.SchemeIs(content::kHttpsScheme) ||
         origin_url.SchemeIs(chrome::kFtpScheme)) {
       std::string host(origin_url.host());
       size_t registry_length =
diff --git a/chrome/browser/history/history_tab_helper.cc b/chrome/browser/history/history_tab_helper.cc
index 96c545e..adbd892 100644
--- a/chrome/browser/history/history_tab_helper.cc
+++ b/chrome/browser/history/history_tab_helper.cc
@@ -8,6 +8,9 @@
 
 #include "chrome/browser/history/history_service.h"
 #include "chrome/browser/history/history_service_factory.h"
+#if !defined(OS_ANDROID)
+#include "chrome/browser/network_time/navigation_time_helper.h"
+#endif
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/prerender/prerender_manager_factory.h"
@@ -96,13 +99,21 @@
   if (!params.should_update_history)
     return;
 
+#if !defined(OS_ANDROID)
+  base::Time navigation_time =
+      NavigationTimeHelper::FromWebContents(web_contents())->GetNavigationTime(
+          details.entry);
+#else
+  base::Time navigation_time = details.entry->GetTimestamp();
+#endif
+
   // Most of the time, the displayURL matches the loaded URL, but for about:
   // URLs, we use a data: URL as the real value.  We actually want to save the
   // about: URL to the history db and keep the data: URL hidden. This is what
   // the WebContents' URL getter does.
   const history::HistoryAddPageArgs& add_page_args =
       CreateHistoryAddPageArgs(
-          web_contents()->GetURL(), details.entry->GetTimestamp(),
+          web_contents()->GetURL(), navigation_time,
           details.did_replace_entry, params);
 
   prerender::PrerenderManager* prerender_manager =
diff --git a/chrome/browser/history/in_memory_url_index.cc b/chrome/browser/history/in_memory_url_index.cc
index 9eba62e..d9b17e2 100644
--- a/chrome/browser/history/in_memory_url_index.cc
+++ b/chrome/browser/history/in_memory_url_index.cc
@@ -44,7 +44,7 @@
   whitelist->insert(std::string(chrome::kFileScheme));
   whitelist->insert(std::string(chrome::kFtpScheme));
   whitelist->insert(std::string(chrome::kHttpScheme));
-  whitelist->insert(std::string(chrome::kHttpsScheme));
+  whitelist->insert(std::string(content::kHttpsScheme));
   whitelist->insert(std::string(content::kMailToScheme));
 }
 
diff --git a/chrome/browser/history/most_visited_tiles_experiment_unittest.cc b/chrome/browser/history/most_visited_tiles_experiment_unittest.cc
index cb66710..ccaee6f 100644
--- a/chrome/browser/history/most_visited_tiles_experiment_unittest.cc
+++ b/chrome/browser/history/most_visited_tiles_experiment_unittest.cc
@@ -14,7 +14,7 @@
 #include "base/values.h"
 #include "chrome/browser/history/history_types.h"
 #include "chrome/common/instant_types.h"
-#include "chrome/common/metrics/entropy_provider.h"
+#include "components/variations/entropy_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
diff --git a/chrome/browser/history/url_database.cc b/chrome/browser/history/url_database.cc
index 4f5a72d..a2eb07d 100644
--- a/chrome/browser/history/url_database.cc
+++ b/chrome/browser/history/url_database.cc
@@ -312,7 +312,7 @@
 bool URLDatabase::IsTypedHost(const std::string& host) {
   const char* schemes[] = {
     chrome::kHttpScheme,
-    chrome::kHttpsScheme,
+    content::kHttpsScheme,
     chrome::kFtpScheme
   };
   URLRows dummy;
diff --git a/chrome/browser/history/visit_database.cc b/chrome/browser/history/visit_database.cc
index 5e04c02..f758a36 100644
--- a/chrome/browser/history/visit_database.cc
+++ b/chrome/browser/history/visit_database.cc
@@ -491,7 +491,8 @@
 bool VisitDatabase::GetVisibleVisitCountToHost(const GURL& url,
                                                int* count,
                                                base::Time* first_visit) {
-  if (!url.SchemeIs(chrome::kHttpScheme) && !url.SchemeIs(chrome::kHttpsScheme))
+  if (!url.SchemeIs(chrome::kHttpScheme) &&
+      !url.SchemeIs(content::kHttpsScheme))
     return false;
 
   // We need to search for URLs with a matching host/port. One way to query for
diff --git a/chrome/browser/history/web_history_service.cc b/chrome/browser/history/web_history_service.cc
index 8e91d69..3def0b6 100644
--- a/chrome/browser/history/web_history_service.cc
+++ b/chrome/browser/history/web_history_service.cc
@@ -11,11 +11,11 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "chrome/browser/signin/profile_oauth2_token_service.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 #include "net/base/load_flags.h"
 #include "net/base/url_util.h"
 #include "net/http/http_status_code.h"
diff --git a/chrome/browser/infobars/infobar.cc b/chrome/browser/infobars/infobar.cc
index 2dc1391..1f2cdb9 100644
--- a/chrome/browser/infobars/infobar.cc
+++ b/chrome/browser/infobars/infobar.cc
@@ -30,9 +30,6 @@
       kWarningBackgroundColorBottom : kPageActionBackgroundColorBottom;
 }
 
-// TODO(pkasting): Port Mac to use this.
-#if defined(TOOLKIT_VIEWS) || defined(TOOLKIT_GTK) || defined(OS_ANDROID)
-
 InfoBar::InfoBar(InfoBarService* owner, InfoBarDelegate* delegate)
     : owner_(owner),
       delegate_(delegate),
@@ -181,5 +178,3 @@
     delegate_ = NULL;
   }
 }
-
-#endif  // TOOLKIT_VIEWS || TOOLKIT_GTK || OS_ANDROID
diff --git a/chrome/browser/infobars/infobar.h b/chrome/browser/infobars/infobar.h
index 1c0fbb9..179ee18 100644
--- a/chrome/browser/infobars/infobar.h
+++ b/chrome/browser/infobars/infobar.h
@@ -26,9 +26,6 @@
 typedef std::pair<InfoBarDelegate*, bool> InfoBarRemovedDetails;
 typedef std::pair<InfoBarDelegate*, InfoBarDelegate*> InfoBarReplacedDetails;
 
-// TODO(pkasting): Port Mac to use this.
-#if defined(TOOLKIT_VIEWS) || defined(TOOLKIT_GTK) || defined(OS_ANDROID)
-
 class InfoBarContainer;
 class InfoBarService;
 
@@ -67,6 +64,9 @@
   // delegate once it is invisible.
   void CloseSoon();
 
+  // Changes the target height of the main ("bar") portion of the infobar.
+  void SetBarTargetHeight(int height);
+
   const ui::SlideAnimation& animation() const { return animation_; }
   int arrow_height() const { return arrow_height_; }
   int arrow_target_height() const { return arrow_target_height_; }
@@ -81,9 +81,6 @@
   // NOTE: Subclasses should not call this if we're already unowned.
   void RemoveSelf();
 
-  // Changes the target height of the main ("bar") portion of the infobar.
-  void SetBarTargetHeight(int height);
-
   // Given a control with size |prefsize|, returns the centered y position
   // within us, taking into account animation so the control "slides in" (or
   // out) as we animate open and closed.
@@ -135,8 +132,4 @@
   DISALLOW_COPY_AND_ASSIGN(InfoBar);
 };
 
-#elif defined(OS_MACOSX)
-#include "chrome/browser/ui/cocoa/infobars/infobar.h"
-#endif
-
 #endif  // CHROME_BROWSER_INFOBARS_INFOBAR_H_
diff --git a/chrome/browser/infobars/infobar_container.cc b/chrome/browser/infobars/infobar_container.cc
index 0ae38e0..3c8660c 100644
--- a/chrome/browser/infobars/infobar_container.cc
+++ b/chrome/browser/infobars/infobar_container.cc
@@ -4,9 +4,6 @@
 
 #include "build/build_config.h"
 
-// TODO(pkasting): Port Mac to use this.
-#if defined(TOOLKIT_VIEWS) || defined(TOOLKIT_GTK) || defined(OS_ANDROID)
-
 #include "chrome/browser/infobars/infobar_container.h"
 
 #include <algorithm>
@@ -235,5 +232,3 @@
       (InfoBar::kDefaultArrowTargetHeight - top_arrow_target_height_) *
           first_infobar_animation.GetCurrentValue());
 }
-
-#endif  // TOOLKIT_VIEWS || defined(TOOLKIT_GTK) || defined(OS_ANDROID)
diff --git a/chrome/browser/invalidation/ticl_invalidation_service.h b/chrome/browser/invalidation/ticl_invalidation_service.h
index 2403817..a117e98 100644
--- a/chrome/browser/invalidation/ticl_invalidation_service.h
+++ b/chrome/browser/invalidation/ticl_invalidation_service.h
@@ -10,11 +10,11 @@
 #include "base/timer/timer.h"
 #include "chrome/browser/invalidation/invalidation_service.h"
 #include "chrome/browser/invalidation/invalidator_storage.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "chrome/browser/signin/token_service.h"
 #include "components/browser_context_keyed_service/browser_context_keyed_service.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 #include "net/base/backoff_entry.h"
 #include "sync/notifier/invalidation_handler.h"
 #include "sync/notifier/invalidator_registrar.h"
diff --git a/chrome/browser/local_discovery/cloud_print_account_manager.cc b/chrome/browser/local_discovery/cloud_print_account_manager.cc
new file mode 100644
index 0000000..72f446d
--- /dev/null
+++ b/chrome/browser/local_discovery/cloud_print_account_manager.cc
@@ -0,0 +1,101 @@
+// 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 "chrome/browser/local_discovery/cloud_print_account_manager.h"
+
+#include "base/json/json_reader.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "chrome/common/cloud_print/cloud_print_constants.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_status.h"
+#include "url/gurl.h"
+
+namespace local_discovery {
+
+namespace {
+// URL relative to cloud print root
+const char kCloudPrintRequestURLFormat[] = "%s/list?proxy=none&user=%d";
+const char kCloudPrintKeyUsers[] = "request.users";
+const char kCloudPrintKeyXsrfToken[] = "xsrf_token";
+}  // namespace
+
+CloudPrintAccountManager::CloudPrintAccountManager(
+    net::URLRequestContextGetter* request_context,
+    const std::string& cloud_print_url,
+    int token_user_index,
+    const AccountsCallback& callback)
+    : request_context_(request_context), cloud_print_url_(cloud_print_url),
+      token_user_index_(token_user_index), callback_(callback) {
+}
+
+CloudPrintAccountManager::~CloudPrintAccountManager() {
+}
+
+void CloudPrintAccountManager::Start() {
+  GURL url(base::StringPrintf(kCloudPrintRequestURLFormat,
+                              cloud_print_url_.c_str(),
+                              token_user_index_));
+  url_fetcher_.reset(net::URLFetcher::Create(url, net::URLFetcher::POST, this));
+  url_fetcher_->SetRequestContext(request_context_.get());
+  url_fetcher_->SetUploadData("", "");
+  url_fetcher_->AddExtraRequestHeader(
+      cloud_print::kChromeCloudPrintProxyHeader);
+  url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
+  url_fetcher_->Start();
+}
+
+// If an error occurs or the user is not logged in, return an empty user list to
+// signify cookie-based accounts should not be used.
+void CloudPrintAccountManager::ReportEmptyUserList() {
+  callback_.Run(std::vector<std::string>(), "");
+}
+
+void CloudPrintAccountManager::OnURLFetchComplete(
+    const net::URLFetcher* source) {
+  std::string response_str;
+
+  if (source->GetStatus().status() != net::URLRequestStatus::SUCCESS ||
+      source->GetResponseCode() != net::HTTP_OK ||
+      !source->GetResponseAsString(&response_str)) {
+    ReportEmptyUserList();
+    return;
+  }
+
+  base::JSONReader reader;
+  scoped_ptr<const base::Value> value(reader.Read(response_str));
+  const base::DictionaryValue* dictionary_value;
+  bool success = false;
+
+  std::string xsrf_token;
+  const base::ListValue* users = NULL;
+  std::vector<std::string> users_vector;
+
+  if (!value.get() ||
+      !value->GetAsDictionary(&dictionary_value) ||
+      !dictionary_value->GetBoolean(cloud_print::kSuccessValue, &success) ||
+      !dictionary_value->GetList(kCloudPrintKeyUsers, &users) ||
+      !dictionary_value->GetString(kCloudPrintKeyXsrfToken, &xsrf_token) ||
+      !success) {
+    ReportEmptyUserList();
+    return;
+  }
+
+  for (size_t i = 0; i < users->GetSize(); i++) {
+    std::string user;
+    if (!users->GetString(i, &user)) {
+      // If we can't read a user from the list, send the users we do recognize
+      // and the XSRF token from the server.
+      break;
+    }
+
+    users_vector.push_back(user);
+  }
+
+  callback_.Run(users_vector, xsrf_token);
+}
+
+}  // namespace local_discovery
diff --git a/chrome/browser/local_discovery/cloud_print_account_manager.h b/chrome/browser/local_discovery/cloud_print_account_manager.h
new file mode 100644
index 0000000..7667b39
--- /dev/null
+++ b/chrome/browser/local_discovery/cloud_print_account_manager.h
@@ -0,0 +1,47 @@
+// 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 CHROME_BROWSER_LOCAL_DISCOVERY_CLOUD_PRINT_ACCOUNT_MANAGER_H_
+#define CHROME_BROWSER_LOCAL_DISCOVERY_CLOUD_PRINT_ACCOUNT_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_delegate.h"
+
+namespace local_discovery {
+
+class CloudPrintAccountManager : public net::URLFetcherDelegate {
+ public:
+  typedef base::Callback<void(
+      const std::vector<std::string>& /*accounts*/,
+      const std::string& /*xsrf_token*/)> AccountsCallback;
+
+  CloudPrintAccountManager(net::URLRequestContextGetter* request_context,
+                           const std::string& cloud_print_url,
+                           int token_user_index,
+                           const AccountsCallback& callback);
+  virtual ~CloudPrintAccountManager();
+
+  void Start();
+
+  // URLFetcher implementation:
+  virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
+
+ private:
+  void ReportEmptyUserList();
+
+  scoped_refptr<net::URLRequestContextGetter> request_context_;
+  std::string cloud_print_url_;
+  int token_user_index_;
+  AccountsCallback callback_;
+  scoped_ptr<net::URLFetcher> url_fetcher_;
+};
+
+}  // namespace local_discovery
+
+#endif  // CHROME_BROWSER_LOCAL_DISCOVERY_CLOUD_PRINT_ACCOUNT_MANAGER_H_
diff --git a/chrome/browser/local_discovery/cloud_print_account_manager_unittest.cc b/chrome/browser/local_discovery/cloud_print_account_manager_unittest.cc
new file mode 100644
index 0000000..dcbc34e
--- /dev/null
+++ b/chrome/browser/local_discovery/cloud_print_account_manager_unittest.cc
@@ -0,0 +1,104 @@
+// 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 "base/message_loop/message_loop.h"
+#include "chrome/browser/local_discovery/cloud_print_account_manager.h"
+#include "net/http/http_request_headers.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace local_discovery {
+
+namespace {
+
+const char kSampleResponse[] = "{"
+    "   \"success\": true,"
+    "   \"xsrf_token\": \"sample\","
+    "   \"request\" : { "
+    "     \"users\": [\"first@gmail.com\", \"second@gmail.com\"]"
+    "   } "
+    "}";
+
+const char kSampleResponseFailure[] = "{"
+    "   \"success\": false,"
+    "}";
+
+class MockCallback {
+ public:
+  MOCK_METHOD2(CloudPrintAccountsResolved, void(
+      const std::vector<std::string>& account,
+      const std::string& xsrf_token));
+};
+
+class CloudPrintAccountManagerTest : public testing::Test {
+ public:
+  CloudPrintAccountManagerTest()
+      : request_context_(
+            new net::TestURLRequestContextGetter(
+                base::MessageLoopProxy::current())),
+        account_manager_(
+            request_context_.get(),
+            "https://www.google.com/cloudprint",
+            1,
+            base::Bind(
+                &MockCallback::CloudPrintAccountsResolved,
+                base::Unretained(&mock_callback_))) {
+  }
+
+  virtual ~CloudPrintAccountManagerTest() {
+  }
+
+ protected:
+  base::MessageLoop message_loop_;
+  scoped_refptr<net::TestURLRequestContextGetter> request_context_;
+  net::TestURLFetcherFactory fetcher_factory_;
+  MockCallback mock_callback_;
+  CloudPrintAccountManager account_manager_;
+};
+
+TEST_F(CloudPrintAccountManagerTest, Success) {
+  account_manager_.Start();
+  net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0);
+
+  net::HttpRequestHeaders headers;
+  std::string proxy;
+  fetcher->GetExtraRequestHeaders(&headers);
+  EXPECT_TRUE(headers.GetHeader("X-Cloudprint-Proxy", &proxy));
+  EXPECT_EQ("Chrome", proxy);
+  EXPECT_EQ(GURL("https://www.google.com/cloudprint/list?proxy=none&user=1"),
+            fetcher->GetOriginalURL());
+
+  fetcher->SetResponseString(kSampleResponse);
+  fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::SUCCESS,
+                                            net::OK));
+  fetcher->set_response_code(200);
+
+  std::vector<std::string> expected_users;
+  expected_users.push_back("first@gmail.com");
+  expected_users.push_back("second@gmail.com");
+
+  EXPECT_CALL(mock_callback_,
+              CloudPrintAccountsResolved(expected_users, "sample"));
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+}
+
+TEST_F(CloudPrintAccountManagerTest, FailureJSON) {
+  account_manager_.Start();
+  net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0);
+
+  fetcher->SetResponseString(kSampleResponseFailure);
+  fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::SUCCESS,
+                                            net::OK));
+  fetcher->set_response_code(200);
+
+  EXPECT_CALL(mock_callback_,
+              CloudPrintAccountsResolved(std::vector<std::string>(), ""));
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+}
+
+}  // namespace
+
+}  // namespace local_discovery
diff --git a/chrome/browser/local_discovery/privet_confirm_api_flow.cc b/chrome/browser/local_discovery/privet_confirm_api_flow.cc
index 8f9f6ce..1917e4a 100644
--- a/chrome/browser/local_discovery/privet_confirm_api_flow.cc
+++ b/chrome/browser/local_discovery/privet_confirm_api_flow.cc
@@ -16,6 +16,7 @@
 
 namespace {
 const char kCloudPrintOAuthHeaderFormat[] = "Authorization: Bearer %s";
+const char kCookieURLFormat[] = "%s&xsrf=%s&user=%d";
 }
 
 PrivetConfirmApiCallFlow::PrivetConfirmApiCallFlow(
@@ -29,28 +30,52 @@
       callback_(callback) {
 }
 
+PrivetConfirmApiCallFlow::PrivetConfirmApiCallFlow(
+    net::URLRequestContextGetter* request_context,
+    int  user_index,
+    const std::string& xsrf_token,
+    const GURL& automated_claim_url,
+    const ResponseCallback& callback)
+    : request_context_(request_context),
+      token_service_(NULL),
+      user_index_(user_index),
+      xsrf_token_(xsrf_token),
+      automated_claim_url_(automated_claim_url),
+      callback_(callback) {
+}
+
 PrivetConfirmApiCallFlow::~PrivetConfirmApiCallFlow() {
 }
 
 void PrivetConfirmApiCallFlow::Start() {
-  OAuth2TokenService::ScopeSet oauth_scopes;
-  oauth_scopes.insert(cloud_print::kCloudPrintAuth);
-  oauth_request_ = token_service_->StartRequest(oauth_scopes, this);
+  if (UseOAuth2()) {
+    OAuth2TokenService::ScopeSet oauth_scopes;
+    oauth_scopes.insert(cloud_print::kCloudPrintAuth);
+    oauth_request_ = token_service_->StartRequest(oauth_scopes, this);
+  } else {
+    GURL cookie_url(
+        base::StringPrintf(kCookieURLFormat,
+                           automated_claim_url_.spec().c_str(),
+                           xsrf_token_.c_str(),
+                           user_index_));
+
+    CreateRequest(cookie_url);
+
+    url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
+
+    url_fetcher_->Start();
+  }
 }
 
 void PrivetConfirmApiCallFlow::OnGetTokenSuccess(
     const OAuth2TokenService::Request* request,
     const std::string& access_token,
     const base::Time& expiration_time) {
-  url_fetcher_.reset(net::URLFetcher::Create(automated_claim_url_,
-                                             net::URLFetcher::GET,
-                                             this));
-  url_fetcher_->SetRequestContext(request_context_.get());
+  CreateRequest(automated_claim_url_);
+
   std::string authorization_header =
       base::StringPrintf(kCloudPrintOAuthHeaderFormat, access_token.c_str());
 
-  url_fetcher_->AddExtraRequestHeader(
-      cloud_print::kChromeCloudPrintProxyHeader);
   url_fetcher_->AddExtraRequestHeader(authorization_header);
   url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
                              net::LOAD_DO_NOT_SEND_COOKIES);
@@ -63,6 +88,17 @@
   callback_.Run(ERROR_TOKEN);
 }
 
+void PrivetConfirmApiCallFlow::CreateRequest(const GURL& url) {
+  url_fetcher_.reset(net::URLFetcher::Create(url,
+                                             net::URLFetcher::GET,
+                                             this));
+
+  url_fetcher_->SetRequestContext(request_context_.get());
+
+  url_fetcher_->AddExtraRequestHeader(
+      cloud_print::kChromeCloudPrintProxyHeader);
+}
+
 void PrivetConfirmApiCallFlow::OnURLFetchComplete(
     const net::URLFetcher* source) {
   // TODO(noamsml): Error logging.
diff --git a/chrome/browser/local_discovery/privet_confirm_api_flow.h b/chrome/browser/local_discovery/privet_confirm_api_flow.h
index dadcf83..f61117f 100644
--- a/chrome/browser/local_discovery/privet_confirm_api_flow.h
+++ b/chrome/browser/local_discovery/privet_confirm_api_flow.h
@@ -8,7 +8,7 @@
 #include <string>
 
 #include "chrome/browser/local_discovery/privet_http.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 #include "net/url_request/url_fetcher.h"
 #include "net/url_request/url_fetcher_delegate.h"
 #include "net/url_request/url_request_context_getter.h"
@@ -30,10 +30,19 @@
   };
   typedef base::Callback<void(Status /*success*/)> ResponseCallback;
 
+  // Create an OAuth2-based confirmation
   PrivetConfirmApiCallFlow(net::URLRequestContextGetter* request_context,
                            OAuth2TokenService* token_service_,
                            const GURL& automated_claim_url,
                            const ResponseCallback& callback);
+
+  // Create a cookie-based confirmation
+  PrivetConfirmApiCallFlow(net::URLRequestContextGetter* request_context,
+                           int  user_index,
+                           const std::string& xsrf_token,
+                           const GURL& automated_claim_url,
+                           const ResponseCallback& callback);
+
   virtual ~PrivetConfirmApiCallFlow();
 
   void Start();
@@ -49,10 +58,16 @@
                                  const GoogleServiceAuthError& error) OVERRIDE;
 
  private:
+  bool UseOAuth2() { return token_service_ != NULL; }
+
+  void CreateRequest(const GURL& url);
+
   scoped_ptr<net::URLFetcher> url_fetcher_;
   scoped_ptr<OAuth2TokenService::Request> oauth_request_;
   scoped_refptr<net::URLRequestContextGetter> request_context_;
   OAuth2TokenService* token_service_;
+  int user_index_;
+  std::string xsrf_token_;
   GURL automated_claim_url_;
   ResponseCallback callback_;
 };
diff --git a/chrome/browser/local_discovery/privet_confirm_api_flow_unittest.cc b/chrome/browser/local_discovery/privet_confirm_api_flow_unittest.cc
index 0ec365e..3ab9da0 100644
--- a/chrome/browser/local_discovery/privet_confirm_api_flow_unittest.cc
+++ b/chrome/browser/local_discovery/privet_confirm_api_flow_unittest.cc
@@ -29,7 +29,6 @@
     "   \"success\": false"
     "}";
 
-
 const char kFailedConfirmResponseBadJson[] = "["
     "   \"success\""
     "]";
@@ -85,12 +84,14 @@
   MockableConfirmCallback callback_;
 };
 
-TEST_F(PrivetConfirmApiFlowTest, Success) {
+TEST_F(PrivetConfirmApiFlowTest, SuccessOAuth2) {
   PrivetConfirmApiCallFlow confirm_flow(request_context_.get(),
                                         &token_service_,
                                         GURL("http://SoMeUrL.com"),
                                         callback_.callback());
 
+  confirm_flow.Start();
+
   confirm_flow.OnGetTokenSuccess(NULL, "SomeToken", base::Time());
   net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0);
 
@@ -115,12 +116,44 @@
   fetcher->delegate()->OnURLFetchComplete(fetcher);
 }
 
+TEST_F(PrivetConfirmApiFlowTest, SuccessCookies) {
+  PrivetConfirmApiCallFlow confirm_flow(request_context_.get(),
+                                        1,
+                                        "SomeToken",
+                                        GURL("http://SoMeUrL.com?token=tkn"),
+                                        callback_.callback());
+
+  confirm_flow.Start();
+
+  net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0);
+
+  EXPECT_EQ(GURL("http://SoMeUrL.com?token=tkn&xsrf=SomeToken&user=1"),
+            fetcher->GetOriginalURL());
+
+  net::HttpRequestHeaders headers;
+  fetcher->GetExtraRequestHeaders(&headers);
+  std::string proxy;
+  EXPECT_TRUE(headers.GetHeader("X-Cloudprint-Proxy", &proxy));
+  EXPECT_EQ("Chrome", proxy);
+
+  fetcher->SetResponseString(kSampleConfirmResponse);
+  fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::SUCCESS,
+                                            net::OK));
+  fetcher->set_response_code(200);
+
+  EXPECT_CALL(callback_, ConfirmCallback(PrivetConfirmApiCallFlow::SUCCESS));
+
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+}
+
 TEST_F(PrivetConfirmApiFlowTest, BadToken) {
   PrivetConfirmApiCallFlow confirm_flow(request_context_.get(),
                                         &token_service_,
                                         GURL("http://SoMeUrL.com"),
                                         callback_.callback());
 
+  confirm_flow.Start();
+
   EXPECT_CALL(callback_,
               ConfirmCallback(PrivetConfirmApiCallFlow::ERROR_TOKEN));
   confirm_flow.OnGetTokenFailure(NULL, GoogleServiceAuthError(
@@ -133,6 +166,8 @@
                                         GURL("http://SoMeUrL.com"),
                                         callback_.callback());
 
+  confirm_flow.Start();
+
   confirm_flow.OnGetTokenSuccess(NULL, "SomeToken", base::Time());
   net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0);
 
@@ -155,6 +190,8 @@
                                         GURL("http://SoMeUrL.com"),
                                         callback_.callback());
 
+  confirm_flow.Start();
+
   confirm_flow.OnGetTokenSuccess(NULL, "SomeToken", base::Time());
   net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0);
 
diff --git a/chrome/browser/local_discovery/privet_http_asynchronous_factory.cc b/chrome/browser/local_discovery/privet_http_asynchronous_factory.cc
index 16c9e93..94511f6 100644
--- a/chrome/browser/local_discovery/privet_http_asynchronous_factory.cc
+++ b/chrome/browser/local_discovery/privet_http_asynchronous_factory.cc
@@ -74,12 +74,20 @@
 }
 
 void PrivetHTTPAsynchronousFactoryImpl::ResolutionImpl::ResolveComplete(
-    bool success, const net::IPAddressNumber& address) {
+    bool success,
+    const net::IPAddressNumber& address_ipv4,
+    const net::IPAddressNumber& address_ipv6) {
   if (!success) {
     callback_.Run(scoped_ptr<PrivetHTTPClient>());
     return;
   }
 
+  net::IPAddressNumber address = address_ipv4;
+  if (address.empty())
+    address = address_ipv6;
+
+  DCHECK(!address.empty());
+
   net::HostPortPair new_address = net::HostPortPair(
       IPAddressToHostString(address), hostport_.port());
   callback_.Run(scoped_ptr<PrivetHTTPClient>(
diff --git a/chrome/browser/local_discovery/privet_http_asynchronous_factory.h b/chrome/browser/local_discovery/privet_http_asynchronous_factory.h
index ac2552b..c02dbed 100644
--- a/chrome/browser/local_discovery/privet_http_asynchronous_factory.h
+++ b/chrome/browser/local_discovery/privet_http_asynchronous_factory.h
@@ -55,7 +55,9 @@
 
     virtual void Start() OVERRIDE;
    private:
-    void ResolveComplete(bool success, const net::IPAddressNumber& address);
+    void ResolveComplete(bool success,
+                         const net::IPAddressNumber& address_ipv4,
+                         const net::IPAddressNumber& address_ipv6);
 
     std::string name_;
     scoped_ptr<LocalDomainResolver> resolver_;
diff --git a/chrome/browser/local_discovery/privet_notifications.cc b/chrome/browser/local_discovery/privet_notifications.cc
index 3b40669..99a406b 100644
--- a/chrome/browser/local_discovery/privet_notifications.cc
+++ b/chrome/browser/local_discovery/privet_notifications.cc
@@ -10,9 +10,20 @@
 #include "chrome/browser/local_discovery/privet_device_lister_impl.h"
 #include "chrome/browser/notifications/notification.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/host_desktop.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h"
 #include "content/public/browser/browser_context.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/page_transition_types.h"
 #include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/message_center/notifier_settings.h"
 
 namespace local_discovery {
 
@@ -149,22 +160,36 @@
     const std::string& device_name,
     const std::string& human_readable_name,
     const std::string& description) {
-  Profile* profile_object = Profile::FromBrowserContext(profile_);
-  Notification notification(
-      GURL(kPrivetNotificationOriginUrl),
-      GURL(),
-      l10n_util::GetStringUTF16(IDS_LOCAL_DISOCVERY_NOTIFICATION_TITLE_PRINTER),
-      l10n_util::GetStringFUTF16(
-          IDS_LOCAL_DISOCVERY_NOTIFICATION_CONTENTS_PRINTER,
-          UTF8ToUTF16(human_readable_name)),
-      WebKit::WebTextDirectionDefault,
-      l10n_util::GetStringUTF16(
-          IDS_LOCAL_DISOCVERY_NOTIFICATION_DISPLAY_SOURCE_PRINTER),
-      UTF8ToUTF16(kPrivetNotificationIDPrefix +
-                  device_name),
-      new PrivetNotificationDelegate(device_name));
+  if (!LocalDiscoveryUIHandler::GetHasVisible()) {
+    Profile* profile_object = Profile::FromBrowserContext(profile_);
+    message_center::RichNotificationData rich_notification_data;
 
-  notification_manager_->Add(notification, profile_object);
+    rich_notification_data.buttons.push_back(
+        message_center::ButtonInfo(l10n_util::GetStringUTF16(
+            IDS_LOCAL_DISOCVERY_NOTIFICATION_BUTTON_PRINTER)));
+
+    Notification notification(
+        message_center::NOTIFICATION_TYPE_SIMPLE,
+        GURL(kPrivetNotificationOriginUrl),
+        l10n_util::GetStringUTF16(
+            IDS_LOCAL_DISOCVERY_NOTIFICATION_TITLE_PRINTER),
+        l10n_util::GetStringFUTF16(
+            IDS_LOCAL_DISOCVERY_NOTIFICATION_CONTENTS_PRINTER,
+            UTF8ToUTF16(human_readable_name)),
+        ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+            IDR_LOCAL_DISCOVERY_CLOUDPRINT_ICON),
+        WebKit::WebTextDirectionDefault,
+        message_center::NotifierId(
+            message_center::NotifierId::SYSTEM_COMPONENT),
+        l10n_util::GetStringUTF16(
+            IDS_LOCAL_DISOCVERY_NOTIFICATION_DISPLAY_SOURCE_PRINTER),
+        UTF8ToUTF16(kPrivetNotificationIDPrefix +
+                    device_name),
+        rich_notification_data,
+        new PrivetNotificationDelegate(device_name, profile_));
+
+    notification_manager_->Add(notification, profile_object);
+  }
 }
 
 void PrivetNotificationService::PrivetRemoveNotification(
@@ -187,7 +212,8 @@
 }
 
 PrivetNotificationDelegate::PrivetNotificationDelegate(
-    const std::string& device_id) : device_id_(device_id) {
+    const std::string& device_id, content::BrowserContext* profile)
+    : device_id_(device_id), profile_(profile) {
 }
 
 PrivetNotificationDelegate::~PrivetNotificationDelegate() {
@@ -214,4 +240,29 @@
 void PrivetNotificationDelegate::Click() {
 }
 
+void PrivetNotificationDelegate::ButtonClick(int button_index) {
+  if (button_index == 0) {
+    // TODO(noamsml): Direct-to-register URL
+    OpenTab(GURL(kPrivetNotificationOriginUrl));
+  }
+}
+
+void PrivetNotificationDelegate::OpenTab(const GURL& url) {
+  Profile* profile_obj = Profile::FromBrowserContext(profile_);
+
+  Browser* browser = FindOrCreateTabbedBrowser(profile_obj,
+                                               chrome::GetActiveDesktop());
+  content::WebContents::CreateParams create_params(profile_obj);
+
+  scoped_ptr<content::WebContents> contents(
+      content::WebContents::Create(create_params));
+  contents->GetController().LoadURL(url,
+                                    content::Referrer(),
+                                    content::PAGE_TRANSITION_AUTO_TOPLEVEL,
+                                    "");
+
+  browser->tab_strip_model()->AppendWebContents(contents.release(), true);
+}
+
+
 }  // namespace local_discovery
diff --git a/chrome/browser/local_discovery/privet_notifications.h b/chrome/browser/local_discovery/privet_notifications.h
index d0c87de..d803935 100644
--- a/chrome/browser/local_discovery/privet_notifications.h
+++ b/chrome/browser/local_discovery/privet_notifications.h
@@ -120,7 +120,8 @@
 
 class PrivetNotificationDelegate : public NotificationDelegate {
  public:
-  explicit PrivetNotificationDelegate(const std::string& device_id);
+  explicit PrivetNotificationDelegate(const std::string& device_id,
+                                      content::BrowserContext* profile);
 
   // NotificationDelegate implementation.
   virtual std::string id() const OVERRIDE;
@@ -129,10 +130,15 @@
   virtual void Error() OVERRIDE;
   virtual void Close(bool by_user) OVERRIDE;
   virtual void Click() OVERRIDE;
+  virtual void ButtonClick(int button_index) OVERRIDE;
+
  private:
+  void OpenTab(const GURL& url);
+
   virtual ~PrivetNotificationDelegate();
 
   std::string device_id_;
+  content::BrowserContext* profile_;
 };
 
 }  // namespace local_discovery
diff --git a/chrome/browser/local_discovery/service_discovery_host_client.cc b/chrome/browser/local_discovery/service_discovery_host_client.cc
index 71ab0ce..0434d9c 100644
--- a/chrome/browser/local_discovery/service_discovery_host_client.cc
+++ b/chrome/browser/local_discovery/service_discovery_host_client.cc
@@ -293,12 +293,13 @@
 void ServiceDiscoveryHostClient::OnLocalDomainResolverCallback(
     uint64 id,
     bool success,
-    const net::IPAddressNumber& ip_address) {
+    const net::IPAddressNumber& ip_address_ipv4,
+    const net::IPAddressNumber& ip_address_ipv6) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   callback_runner_->PostTask(
       FROM_HERE,
       base::Bind(&ServiceDiscoveryHostClient::RunLocalDomainResolverCallback,
-                 this, id, success, ip_address));
+                 this, id, success, ip_address_ipv4, ip_address_ipv6));
 }
 
 void ServiceDiscoveryHostClient::RunWatcherCallback(
@@ -324,11 +325,12 @@
 void ServiceDiscoveryHostClient::RunLocalDomainResolverCallback(
     uint64 id,
     bool success,
-    const net::IPAddressNumber& ip_address) {
+    const net::IPAddressNumber& ip_address_ipv4,
+    const net::IPAddressNumber& ip_address_ipv6) {
   DCHECK(CalledOnValidThread());
   DomainResolverCallbacks::iterator it = domain_resolver_callbacks_.find(id);
   if (it != domain_resolver_callbacks_.end() && !it->second.is_null())
-    it->second.Run(success, ip_address);
+    it->second.Run(success, ip_address_ipv4, ip_address_ipv6);
 }
 
 ServiceDiscoveryHostClientFactory::ServiceDiscoveryHostClientFactory()
diff --git a/chrome/browser/local_discovery/service_discovery_host_client.h b/chrome/browser/local_discovery/service_discovery_host_client.h
index 1a3278d..31f2a09 100644
--- a/chrome/browser/local_discovery/service_discovery_host_client.h
+++ b/chrome/browser/local_discovery/service_discovery_host_client.h
@@ -92,7 +92,8 @@
                           const ServiceDescription& description);
   void OnLocalDomainResolverCallback(uint64 id,
                                      bool success,
-                                     const net::IPAddressNumber& address);
+                                     const net::IPAddressNumber& address_ipv4,
+                                     const net::IPAddressNumber& address_ipv6);
 
 
   // Runs watcher callback on owning thread.
@@ -106,7 +107,8 @@
   // Runs local domain resolver callback on owning thread.
   void RunLocalDomainResolverCallback(uint64 id,
                                       bool success,
-                                      const net::IPAddressNumber& address);
+                                      const net::IPAddressNumber& address_ipv4,
+                                      const net::IPAddressNumber& address_ipv6);
 
 
   base::WeakPtr<content::UtilityProcessHost> utility_host_;
diff --git a/chrome/browser/managed_mode/managed_user_refresh_token_fetcher.cc b/chrome/browser/managed_mode/managed_user_refresh_token_fetcher.cc
index 4cd6258..1d9e410 100644
--- a/chrome/browser/managed_mode/managed_user_refresh_token_fetcher.cc
+++ b/chrome/browser/managed_mode/managed_user_refresh_token_fetcher.cc
@@ -9,12 +9,12 @@
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "google_apis/gaia/gaia_constants.h"
 #include "google_apis/gaia/gaia_oauth_client.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "google_apis/gaia/oauth2_api_call_flow.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 #include "net/base/escape.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
diff --git a/chrome/browser/managed_mode/managed_user_refresh_token_fetcher_unittest.cc b/chrome/browser/managed_mode/managed_user_refresh_token_fetcher_unittest.cc
index fb79edc..e85fc26 100644
--- a/chrome/browser/managed_mode/managed_user_refresh_token_fetcher_unittest.cc
+++ b/chrome/browser/managed_mode/managed_user_refresh_token_fetcher_unittest.cc
@@ -7,12 +7,12 @@
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/managed_mode/managed_user_refresh_token_fetcher.h"
 #include "chrome/browser/signin/fake_profile_oauth2_token_service.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "google_apis/gaia/gaia_oauth_client.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 #include "net/base/net_errors.h"
 #include "net/base/url_util.h"
 #include "net/http/http_request_headers.h"
diff --git a/chrome/browser/managed_mode/managed_user_registration_utility.cc b/chrome/browser/managed_mode/managed_user_registration_utility.cc
index e1f8691..72404c6 100644
--- a/chrome/browser/managed_mode/managed_user_registration_utility.cc
+++ b/chrome/browser/managed_mode/managed_user_registration_utility.cc
@@ -27,15 +27,151 @@
 
 using base::DictionaryValue;
 
+namespace {
+
 const char kAcknowledged[] = "acknowledged";
 const char kName[] = "name";
 const char kMasterKey[] = "masterKey";
 
+ManagedUserRegistrationUtility* g_instance_for_tests = NULL;
+
+// Actual implementation of ManagedUserRegistrationUtility.
+class ManagedUserRegistrationUtilityImpl
+    : public ManagedUserRegistrationUtility,
+      public ManagedUserSyncServiceObserver {
+ public:
+  ManagedUserRegistrationUtilityImpl(
+      PrefService* prefs,
+      scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher,
+      ManagedUserSyncService* service);
+
+  virtual ~ManagedUserRegistrationUtilityImpl();
+
+  // Registers a new managed user with the server. |managed_user_id| is a new
+  // unique ID for the new managed user. If its value is the same as that of
+  // of one of the existing managed users, then the same user will be created
+  // on this machine. |info| contains necessary information like the display
+  // name of the  the user. |callback| is called with the result of the
+  // registration. We use the info here and not the profile, because on
+  // Chrome OS the profile of the managed user does not yet exist.
+  virtual void Register(const std::string& managed_user_id,
+                        const ManagedUserRegistrationInfo& info,
+                        const RegistrationCallback& callback) OVERRIDE;
+
+  // ManagedUserSyncServiceObserver:
+  virtual void OnManagedUserAcknowledged(const std::string& managed_user_id)
+      OVERRIDE;
+  virtual void OnManagedUsersSyncingStopped() OVERRIDE;
+
+ private:
+  // Fetches the managed user token when we have the device name.
+  void FetchToken(const std::string& client_name);
+
+  // Called when we have received a token for the managed user.
+  void OnReceivedToken(const GoogleServiceAuthError& error,
+                       const std::string& token);
+
+  // Dispatches the callback and cleans up if all the conditions have been met.
+  void CompleteRegistrationIfReady();
+
+  // Aborts any registration currently in progress. If |run_callback| is true,
+  // calls the callback specified in Register() with the given |error|.
+  void AbortPendingRegistration(bool run_callback,
+                                const GoogleServiceAuthError& error);
+
+  // If |run_callback| is true, dispatches the callback with the saved token
+  // (which may be empty) and the given |error|. In any case, resets internal
+  // variables to be ready for the next registration.
+  void CompleteRegistration(bool run_callback,
+                            const GoogleServiceAuthError& error);
+
+  // Cancels any registration currently in progress, without calling the
+  // callback or reporting an error.
+  void CancelPendingRegistration();
+
+  base::WeakPtrFactory<ManagedUserRegistrationUtilityImpl> weak_ptr_factory_;
+  PrefService* prefs_;
+  scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher_;
+
+  // A |BrowserContextKeyedService| owned by the custodian profile.
+  ManagedUserSyncService* managed_user_sync_service_;
+
+  std::string pending_managed_user_id_;
+  std::string pending_managed_user_token_;
+  bool pending_managed_user_acknowledged_;
+  bool is_existing_managed_user_;
+  RegistrationCallback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(ManagedUserRegistrationUtilityImpl);
+};
+
+} // namespace
+
 ManagedUserRegistrationInfo::ManagedUserRegistrationInfo(const string16& name)
     : name(name) {
 }
 
-ManagedUserRegistrationUtility::ManagedUserRegistrationUtility(
+ScopedTestingManagedUserRegistrationUtility::
+    ScopedTestingManagedUserRegistrationUtility(
+        ManagedUserRegistrationUtility* instance) {
+  ManagedUserRegistrationUtility::SetUtilityForTests(instance);
+}
+
+ScopedTestingManagedUserRegistrationUtility::
+    ~ScopedTestingManagedUserRegistrationUtility() {
+  ManagedUserRegistrationUtility::SetUtilityForTests(NULL);
+}
+
+// static
+scoped_ptr<ManagedUserRegistrationUtility>
+ManagedUserRegistrationUtility::Create(Profile* profile) {
+  if (g_instance_for_tests) {
+    ManagedUserRegistrationUtility* result = g_instance_for_tests;
+    g_instance_for_tests = NULL;
+    return make_scoped_ptr(result);
+  }
+  scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher =
+      ManagedUserRefreshTokenFetcher::Create(
+          ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
+          profile->GetRequestContext());
+  ManagedUserSyncService* managed_user_sync_service =
+      ManagedUserSyncServiceFactory::GetForProfile(profile);
+  return make_scoped_ptr(ManagedUserRegistrationUtility::CreateImpl(
+      profile->GetPrefs(),
+      token_fetcher.Pass(),
+      managed_user_sync_service));
+}
+
+// static
+std::string ManagedUserRegistrationUtility::GenerateNewManagedUserId() {
+  std::string new_managed_user_id;
+  bool success = base::Base64Encode(base::RandBytesAsString(8),
+                                    &new_managed_user_id);
+  DCHECK(success);
+  return new_managed_user_id;
+}
+
+// static
+void ManagedUserRegistrationUtility::SetUtilityForTests(
+    ManagedUserRegistrationUtility* utility) {
+  if (g_instance_for_tests)
+    delete g_instance_for_tests;
+  g_instance_for_tests = utility;
+}
+
+// static
+ManagedUserRegistrationUtility* ManagedUserRegistrationUtility::CreateImpl(
+      PrefService* prefs,
+      scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher,
+      ManagedUserSyncService* service) {
+  return new ManagedUserRegistrationUtilityImpl(prefs,
+                                                token_fetcher.Pass(),
+                                                service);
+}
+
+namespace {
+
+ManagedUserRegistrationUtilityImpl::ManagedUserRegistrationUtilityImpl(
     PrefService* prefs,
     scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher,
     ManagedUserSyncService* service)
@@ -48,34 +184,12 @@
   managed_user_sync_service_->AddObserver(this);
 }
 
-ManagedUserRegistrationUtility::~ManagedUserRegistrationUtility() {
+ManagedUserRegistrationUtilityImpl::~ManagedUserRegistrationUtilityImpl() {
   managed_user_sync_service_->RemoveObserver(this);
   CancelPendingRegistration();
 }
 
-// static
-scoped_ptr<ManagedUserRegistrationUtility>
-ManagedUserRegistrationUtility::Create(Profile* profile) {
-  scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher =
-      ManagedUserRefreshTokenFetcher::Create(
-          ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
-          profile->GetRequestContext());
-  ManagedUserSyncService* managed_user_sync_service =
-      ManagedUserSyncServiceFactory::GetForProfile(profile);
-  return make_scoped_ptr(new ManagedUserRegistrationUtility(
-      profile->GetPrefs(), token_fetcher.Pass(), managed_user_sync_service));
-}
-
-// static
-std::string ManagedUserRegistrationUtility::GenerateNewManagedUserId() {
-  std::string new_managed_user_id;
-  bool success = base::Base64Encode(base::RandBytesAsString(8),
-                                    &new_managed_user_id);
-  DCHECK(success);
-  return new_managed_user_id;
-}
-
-void ManagedUserRegistrationUtility::Register(
+void ManagedUserRegistrationUtilityImpl::Register(
     const std::string& managed_user_id,
     const ManagedUserRegistrationInfo& info,
     const RegistrationCallback& callback) {
@@ -95,17 +209,17 @@
   }
 
   browser_sync::DeviceInfo::GetClientName(
-      base::Bind(&ManagedUserRegistrationUtility::FetchToken,
+      base::Bind(&ManagedUserRegistrationUtilityImpl::FetchToken,
                  weak_ptr_factory_.GetWeakPtr()));
 }
 
-void ManagedUserRegistrationUtility::CancelPendingRegistration() {
+void ManagedUserRegistrationUtilityImpl::CancelPendingRegistration() {
   AbortPendingRegistration(
       false,  // Don't run the callback. The error will be ignored.
       GoogleServiceAuthError(GoogleServiceAuthError::NONE));
 }
 
-void ManagedUserRegistrationUtility::OnManagedUserAcknowledged(
+void ManagedUserRegistrationUtilityImpl::OnManagedUserAcknowledged(
     const std::string& managed_user_id) {
   DCHECK_EQ(pending_managed_user_id_, managed_user_id);
   DCHECK(!pending_managed_user_acknowledged_);
@@ -113,21 +227,21 @@
   CompleteRegistrationIfReady();
 }
 
-void ManagedUserRegistrationUtility::OnManagedUsersSyncingStopped() {
+void ManagedUserRegistrationUtilityImpl::OnManagedUsersSyncingStopped() {
   AbortPendingRegistration(
       true,  // Run the callback.
       GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED));
 }
 
-void ManagedUserRegistrationUtility::FetchToken(
+void ManagedUserRegistrationUtilityImpl::FetchToken(
     const std::string& client_name) {
   token_fetcher_->Start(
       pending_managed_user_id_, client_name,
-      base::Bind(&ManagedUserRegistrationUtility::OnReceivedToken,
+      base::Bind(&ManagedUserRegistrationUtilityImpl::OnReceivedToken,
                  weak_ptr_factory_.GetWeakPtr()));
 }
 
-void ManagedUserRegistrationUtility::OnReceivedToken(
+void ManagedUserRegistrationUtilityImpl::OnReceivedToken(
     const GoogleServiceAuthError& error,
     const std::string& token) {
   if (error.state() != GoogleServiceAuthError::NONE) {
@@ -140,7 +254,7 @@
   CompleteRegistrationIfReady();
 }
 
-void ManagedUserRegistrationUtility::CompleteRegistrationIfReady() {
+void ManagedUserRegistrationUtilityImpl::CompleteRegistrationIfReady() {
   bool require_acknowledgment =
       !pending_managed_user_acknowledged_ &&
       !CommandLine::ForCurrentProcess()->HasSwitch(
@@ -152,14 +266,14 @@
   CompleteRegistration(true, error);
 }
 
-void ManagedUserRegistrationUtility::AbortPendingRegistration(
+void ManagedUserRegistrationUtilityImpl::AbortPendingRegistration(
     bool run_callback,
     const GoogleServiceAuthError& error) {
   pending_managed_user_token_.clear();
   CompleteRegistration(run_callback, error);
 }
 
-void ManagedUserRegistrationUtility::CompleteRegistration(
+void ManagedUserRegistrationUtilityImpl::CompleteRegistration(
     bool run_callback,
     const GoogleServiceAuthError& error) {
   if (callback_.is_null())
@@ -182,3 +296,5 @@
     callback_.Run(error, pending_managed_user_token_);
   callback_.Reset();
 }
+
+} // namespace
diff --git a/chrome/browser/managed_mode/managed_user_registration_utility.h b/chrome/browser/managed_mode/managed_user_registration_utility.h
index 5e6fe52..716f18c 100644
--- a/chrome/browser/managed_mode/managed_user_registration_utility.h
+++ b/chrome/browser/managed_mode/managed_user_registration_utility.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <string>
 
+#include "base/basictypes.h"
 #include "base/callback.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/weak_ptr.h"
@@ -39,8 +40,7 @@
 // management server and associating it with its custodian. Each instance
 // of this class handles registering a single managed user and should not
 // be used afterwards.
-class ManagedUserRegistrationUtility
-    : public ManagedUserSyncServiceObserver {
+class ManagedUserRegistrationUtility {
  public:
   // Callback for Register() below. If registration is successful, |token| will
   // contain an OAuth2 refresh token for the newly registered managed user,
@@ -50,8 +50,9 @@
                               const std::string& /* token */)>
       RegistrationCallback;
 
-  virtual ~ManagedUserRegistrationUtility();
+  virtual ~ManagedUserRegistrationUtility() {}
 
+  // Creates ManagedUserRegistrationUtility for a given |profile|.
   static scoped_ptr<ManagedUserRegistrationUtility> Create(Profile* profile);
 
   static std::string GenerateNewManagedUserId();
@@ -63,68 +64,38 @@
   // name of the  the user. |callback| is called with the result of the
   // registration. We use the info here and not the profile, because on
   // Chrome OS the profile of the managed user does not yet exist.
-  void Register(const std::string& managed_user_id,
-                const ManagedUserRegistrationInfo& info,
-                const RegistrationCallback& callback);
+  virtual void Register(const std::string& managed_user_id,
+                        const ManagedUserRegistrationInfo& info,
+                        const RegistrationCallback& callback) = 0;
 
-  // ManagedUserSyncServiceObserver:
-  virtual void OnManagedUserAcknowledged(const std::string& managed_user_id)
-      OVERRIDE;
-  virtual void OnManagedUsersSyncingStopped() OVERRIDE;
+ protected:
+  ManagedUserRegistrationUtility() {}
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(ManagedUserRegistrationUtilityTest, Register);
-  FRIEND_TEST_ALL_PREFIXES(ManagedUserRegistrationUtilityTest,
-                           RegisterBeforeInitialSync);
-  FRIEND_TEST_ALL_PREFIXES(ManagedUserRegistrationUtilityTest,
-                           SyncServiceShutdownBeforeRegFinish);
-  FRIEND_TEST_ALL_PREFIXES(ManagedUserRegistrationUtilityTest,
-                           StopSyncingBeforeRegFinish);
+  friend class ScopedTestingManagedUserRegistrationUtility;
+  friend class ManagedUserRegistrationUtilityTest;
 
-  // Use the |Create(...)| method to get instances of this class.
-  ManagedUserRegistrationUtility(
+  // Creates implementation with explicit dependencies, can be used for testing.
+  static ManagedUserRegistrationUtility* CreateImpl(
       PrefService* prefs,
       scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher,
       ManagedUserSyncService* service);
-  // Fetches the managed user token when we have the device name.
-  void FetchToken(const std::string& client_name);
 
-  // Called when we have received a token for the managed user.
-  void OnReceivedToken(const GoogleServiceAuthError& error,
-                       const std::string& token);
+  // Set the instance of ManagedUserRegistrationUtility that will be returned
+  // by next Create() call. Takes ownership of the |utility|.
+  static void SetUtilityForTests(ManagedUserRegistrationUtility* utility);
+};
 
-  // Dispatches the callback and cleans up if all the conditions have been met.
-  void CompleteRegistrationIfReady();
+// Class that sets the instance of ManagedUserRegistrationUtility that will be
+// returned by next Create() call, and correctly destroys it if Create() was
+// not called.
+class ScopedTestingManagedUserRegistrationUtility {
+ public:
+  // Delegates ownership of the |instance| to ManagedUserRegistrationUtility.
+  ScopedTestingManagedUserRegistrationUtility(
+      ManagedUserRegistrationUtility* instance);
 
-  // Aborts any registration currently in progress. If |run_callback| is true,
-  // calls the callback specified in Register() with the given |error|.
-  void AbortPendingRegistration(bool run_callback,
-                                const GoogleServiceAuthError& error);
-
-  // If |run_callback| is true, dispatches the callback with the saved token
-  // (which may be empty) and the given |error|. In any case, resets internal
-  // variables to be ready for the next registration.
-  void CompleteRegistration(bool run_callback,
-                            const GoogleServiceAuthError& error);
-
-  // Cancels any registration currently in progress, without calling the
-  // callback or reporting an error.
-  void CancelPendingRegistration();
-
-  base::WeakPtrFactory<ManagedUserRegistrationUtility> weak_ptr_factory_;
-  PrefService* prefs_;
-  scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher_;
-
-  // A |BrowserContextKeyedService| owned by the custodian profile.
-  ManagedUserSyncService* managed_user_sync_service_;
-
-  std::string pending_managed_user_id_;
-  std::string pending_managed_user_token_;
-  bool pending_managed_user_acknowledged_;
-  bool is_existing_managed_user_;
-  RegistrationCallback callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(ManagedUserRegistrationUtility);
+  ~ScopedTestingManagedUserRegistrationUtility();
 };
 
 #endif  // CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_REGISTRATION_UTILITY_H_
diff --git a/chrome/browser/managed_mode/managed_user_registration_utility_stub.cc b/chrome/browser/managed_mode/managed_user_registration_utility_stub.cc
new file mode 100644
index 0000000..1149649
--- /dev/null
+++ b/chrome/browser/managed_mode/managed_user_registration_utility_stub.cc
@@ -0,0 +1,47 @@
+// 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 "chrome/browser/managed_mode/managed_user_registration_utility_stub.h"
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+
+ManagedUserRegistrationUtilityStub::ManagedUserRegistrationUtilityStub()
+    : register_was_called_(false) {
+}
+
+ManagedUserRegistrationUtilityStub::~ManagedUserRegistrationUtilityStub() {
+}
+
+void ManagedUserRegistrationUtilityStub::Register(
+    const std::string& managed_user_id,
+    const ManagedUserRegistrationInfo& info,
+    const RegistrationCallback& callback) {
+  DCHECK(!register_was_called_);
+  register_was_called_ = true;
+  callback_ = callback;
+  managed_user_id_ = managed_user_id;
+  display_name_ = info.name;
+  master_key_ = info.master_key;
+}
+
+void ManagedUserRegistrationUtilityStub::RunSuccessCallback(
+    const std::string& token) {
+  if (callback_.is_null())
+    return;
+  callback_.Run(GoogleServiceAuthError(GoogleServiceAuthError::NONE), token);
+  callback_.Reset();
+}
+
+void ManagedUserRegistrationUtilityStub::RunFailureCallback(
+    const GoogleServiceAuthError::State state) {
+  if (callback_.is_null())
+    return;
+  GoogleServiceAuthError error(state);
+  callback_.Run(error, std::string());
+  callback_.Reset();
+}
diff --git a/chrome/browser/managed_mode/managed_user_registration_utility_stub.h b/chrome/browser/managed_mode/managed_user_registration_utility_stub.h
new file mode 100644
index 0000000..59f4734
--- /dev/null
+++ b/chrome/browser/managed_mode/managed_user_registration_utility_stub.h
@@ -0,0 +1,47 @@
+// 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 CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_REGISTRATION_UTILITY_STUB_H_
+#define CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_REGISTRATION_UTILITY_STUB_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/strings/string16.h"
+#include "chrome/browser/managed_mode/managed_user_registration_utility.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+
+class ManagedUserRegistrationUtilityStub
+    : public ManagedUserRegistrationUtility {
+ public:
+  ManagedUserRegistrationUtilityStub();
+  virtual ~ManagedUserRegistrationUtilityStub();
+
+  virtual void Register(const std::string& managed_user_id,
+                        const ManagedUserRegistrationInfo& info,
+                        const RegistrationCallback& callback) OVERRIDE;
+
+  bool register_was_called() { return register_was_called_; }
+
+  std::string managed_user_id() { return managed_user_id_; }
+
+  string16 display_name() { return display_name_; }
+
+  std::string master_key() { return master_key_; }
+
+  void RunSuccessCallback(const std::string& token);
+  void RunFailureCallback(GoogleServiceAuthError::State error);
+
+ private:
+   RegistrationCallback callback_;
+   bool register_was_called_;
+   std::string managed_user_id_;
+   string16 display_name_;
+   std::string master_key_;
+
+  DISALLOW_COPY_AND_ASSIGN(ManagedUserRegistrationUtilityStub);
+};
+
+#endif  // CHROME_BROWSER_MANAGED_MODE_MANAGED_USER_REGISTRATION_UTILITY_STUB_H_
diff --git a/chrome/browser/managed_mode/managed_user_registration_utility_unittest.cc b/chrome/browser/managed_mode/managed_user_registration_utility_unittest.cc
index 5bb75de..124b5e6 100644
--- a/chrome/browser/managed_mode/managed_user_registration_utility_unittest.cc
+++ b/chrome/browser/managed_mode/managed_user_registration_utility_unittest.cc
@@ -94,6 +94,8 @@
   ManagedUserRegistrationUtility::RegistrationCallback
       GetRegistrationCallback();
 
+  ManagedUserRegistrationUtility* GetRegistrationUtility();
+
   void Acknowledge();
 
   PrefService* prefs() { return profile_.GetTestingPrefService(); }
@@ -113,6 +115,7 @@
   base::WeakPtrFactory<ManagedUserRegistrationUtilityTest> weak_ptr_factory_;
   TestingProfile profile_;
   ManagedUserSyncService* service_;
+  scoped_ptr<ManagedUserRegistrationUtility> registration_utility_;
 
   // Owned by the ManagedUserSyncService.
   MockChangeProcessor* change_processor_;
@@ -176,6 +179,20 @@
       weak_ptr_factory_.GetWeakPtr());
 }
 
+ManagedUserRegistrationUtility*
+ManagedUserRegistrationUtilityTest::GetRegistrationUtility() {
+  if (registration_utility_.get())
+    return registration_utility_.get();
+
+  scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher(
+      new MockManagedUserRefreshTokenFetcher);
+  registration_utility_.reset(
+      ManagedUserRegistrationUtility::CreateImpl(prefs(),
+                                                 token_fetcher.Pass(),
+                                                 service()));
+  return registration_utility_.get();
+}
+
 void ManagedUserRegistrationUtilityTest::Acknowledge() {
   SyncChangeList new_changes;
   const SyncChangeList& changes = change_processor()->changes();
@@ -207,12 +224,7 @@
 
 TEST_F(ManagedUserRegistrationUtilityTest, Register) {
   StartInitialSync();
-  scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher(
-      new MockManagedUserRefreshTokenFetcher);
-  ManagedUserRegistrationUtility registration_utility(prefs(),
-                                                      token_fetcher.Pass(),
-                                                      service());
-  registration_utility.Register(
+  GetRegistrationUtility()->Register(
       ManagedUserRegistrationUtility::GenerateNewManagedUserId(),
       ManagedUserRegistrationInfo(ASCIIToUTF16("Dug")),
       GetRegistrationCallback());
@@ -225,12 +237,7 @@
 }
 
 TEST_F(ManagedUserRegistrationUtilityTest, RegisterBeforeInitialSync) {
-  scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher(
-      new MockManagedUserRefreshTokenFetcher);
-  ManagedUserRegistrationUtility registration_utility(prefs(),
-                                                      token_fetcher.Pass(),
-                                                      service());
-  registration_utility.Register(
+  GetRegistrationUtility()->Register(
       ManagedUserRegistrationUtility::GenerateNewManagedUserId(),
       ManagedUserRegistrationInfo(ASCIIToUTF16("Nemo")),
       GetRegistrationCallback());
@@ -245,12 +252,7 @@
 
 TEST_F(ManagedUserRegistrationUtilityTest, SyncServiceShutdownBeforeRegFinish) {
   StartInitialSync();
-  scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher(
-      new MockManagedUserRefreshTokenFetcher);
-  ManagedUserRegistrationUtility registration_utility(prefs(),
-                                                      token_fetcher.Pass(),
-                                                      service());
-  registration_utility.Register(
+  GetRegistrationUtility()->Register(
       ManagedUserRegistrationUtility::GenerateNewManagedUserId(),
       ManagedUserRegistrationInfo(ASCIIToUTF16("Remy")),
       GetRegistrationCallback());
@@ -264,12 +266,7 @@
 
 TEST_F(ManagedUserRegistrationUtilityTest, StopSyncingBeforeRegFinish) {
   StartInitialSync();
-  scoped_ptr<ManagedUserRefreshTokenFetcher> token_fetcher(
-      new MockManagedUserRefreshTokenFetcher);
-  ManagedUserRegistrationUtility registration_utility(prefs(),
-                                                      token_fetcher.Pass(),
-                                                      service());
-  registration_utility.Register(
+  GetRegistrationUtility()->Register(
       ManagedUserRegistrationUtility::GenerateNewManagedUserId(),
       ManagedUserRegistrationInfo(ASCIIToUTF16("Mike")),
       GetRegistrationCallback());
diff --git a/chrome/browser/managed_mode/managed_user_service.cc b/chrome/browser/managed_mode/managed_user_service.cc
index 0c0da8c..d2ab84a 100644
--- a/chrome/browser/managed_mode/managed_user_service.cc
+++ b/chrome/browser/managed_mode/managed_user_service.cc
@@ -316,33 +316,30 @@
   if (ExtensionManagementPolicyImpl(extension, &tmp_error))
     return true;
 
-  // |extension| can be NULL in a unit test.
-  if (extension) {
-    // If the extension is already loaded, we allow it, otherwise we'd unload
-    // all existing extensions.
-    ExtensionService* extension_service =
-        extensions::ExtensionSystem::Get(profile_)->extension_service();
+  // If the extension is already loaded, we allow it, otherwise we'd unload
+  // all existing extensions.
+  ExtensionService* extension_service =
+      extensions::ExtensionSystem::Get(profile_)->extension_service();
 
-    // |extension_service| can be NULL in a unit test.
-    if (extension_service &&
-        extension_service->GetInstalledExtension(extension->id()))
-      return true;
+  // |extension_service| can be NULL in a unit test.
+  if (extension_service &&
+      extension_service->GetInstalledExtension(extension->id()))
+    return true;
 
-    bool was_installed_by_default = extension->was_installed_by_default();
+  bool was_installed_by_default = extension->was_installed_by_default();
 #if defined(OS_CHROMEOS)
-    // On Chrome OS all external sources are controlled by us so it means that
-    // they are "default". Method was_installed_by_default returns false because
-    // extensions creation flags are ignored in case of default extensions with
-    // update URL(the flags aren't passed to OnExternalExtensionUpdateUrlFound).
-    // TODO(dpolukhin): remove this Chrome OS specific code as soon as creation
-    // flags are not ignored.
-    was_installed_by_default =
-        extensions::Manifest::IsExternalLocation(extension->location());
+  // On Chrome OS all external sources are controlled by us so it means that
+  // they are "default". Method was_installed_by_default returns false because
+  // extensions creation flags are ignored in case of default extensions with
+  // update URL(the flags aren't passed to OnExternalExtensionUpdateUrlFound).
+  // TODO(dpolukhin): remove this Chrome OS specific code as soon as creation
+  // flags are not ignored.
+  was_installed_by_default =
+      extensions::Manifest::IsExternalLocation(extension->location());
 #endif
-    if (extension->location() == extensions::Manifest::COMPONENT ||
-        was_installed_by_default) {
-      return true;
-    }
+  if (extension->location() == extensions::Manifest::COMPONENT ||
+      was_installed_by_default) {
+    return true;
   }
 
   if (error)
diff --git a/chrome/browser/managed_mode/managed_user_service_unittest.cc b/chrome/browser/managed_mode/managed_user_service_unittest.cc
index 6a51281..d40f50b 100644
--- a/chrome/browser/managed_mode/managed_user_service_unittest.cc
+++ b/chrome/browser/managed_mode/managed_user_service_unittest.cc
@@ -14,10 +14,13 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/common/chrome_paths.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_builder.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/test_browser_thread.h"
 #include "content/public/test/test_utils.h"
+#include "extensions/common/manifest_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using content::MessageLoopRunner;
@@ -73,39 +76,6 @@
 
 }  // namespace
 
-TEST_F(ManagedUserServiceTest, ExtensionManagementPolicyProviderUnmanaged) {
-  EXPECT_FALSE(managed_user_service_->ProfileIsManaged());
-
-  string16 error_1;
-  EXPECT_TRUE(managed_user_service_->UserMayLoad(NULL, &error_1));
-  EXPECT_EQ(string16(), error_1);
-
-  string16 error_2;
-  EXPECT_TRUE(managed_user_service_->UserMayModifySettings(NULL, &error_2));
-  EXPECT_EQ(string16(), error_2);
-}
-
-TEST_F(ManagedUserServiceTest, ExtensionManagementPolicyProviderManaged) {
-  ManagedModeURLFilterObserver observer(
-      managed_user_service_->GetURLFilterForUIThread());
-  managed_user_service_->InitForTesting();
-  EXPECT_TRUE(managed_user_service_->ProfileIsManaged());
-
-  string16 error_1;
-  EXPECT_FALSE(managed_user_service_->UserMayLoad(NULL, &error_1));
-  EXPECT_FALSE(error_1.empty());
-
-  string16 error_2;
-  EXPECT_FALSE(managed_user_service_->UserMayModifySettings(NULL, &error_2));
-  EXPECT_FALSE(error_2.empty());
-
-#ifndef NDEBUG
-  EXPECT_FALSE(managed_user_service_->GetDebugPolicyProviderName().empty());
-#endif
-  // Wait for the initial update to finish (otherwise we'll get leaks).
-  observer.Wait();
-}
-
 TEST_F(ManagedUserServiceTest, GetManualExceptionsForHost) {
   GURL kExampleFooURL("http://www.example.com/foo");
   GURL kExampleBarURL("http://www.example.com/bar");
@@ -182,8 +152,83 @@
       ManagedUserService* managed_user_service) {
     return managed_user_service->GetActiveSiteLists();
   }
+
+  scoped_refptr<extensions::Extension> MakeThemeExtension() {
+    scoped_ptr<DictionaryValue> source(new DictionaryValue());
+    source->SetString(extensions::manifest_keys::kName, "Theme");
+    source->Set(extensions::manifest_keys::kTheme, new DictionaryValue());
+    source->SetString(extensions::manifest_keys::kVersion, "1.0");
+    extensions::ExtensionBuilder builder;
+    scoped_refptr<extensions::Extension> extension =
+        builder.SetManifest(source.Pass()).Build();
+    return extension;
+  }
+
+  scoped_refptr<extensions::Extension> MakeExtension() {
+    scoped_ptr<DictionaryValue> manifest = extensions::DictionaryBuilder()
+      .Set(extensions::manifest_keys::kName, "Extension")
+      .Set(extensions::manifest_keys::kVersion, "1.0")
+      .Build();
+    extensions::ExtensionBuilder builder;
+    scoped_refptr<extensions::Extension> extension =
+        builder.SetManifest(manifest.Pass()).Build();
+    return extension;
+  }
 };
 
+TEST_F(ManagedUserServiceExtensionTest,
+       ExtensionManagementPolicyProviderUnmanaged) {
+  ManagedUserService* managed_user_service =
+      ManagedUserServiceFactory::GetForProfile(profile_.get());
+  EXPECT_FALSE(profile_->IsManaged());
+
+  scoped_refptr<extensions::Extension> extension = MakeExtension();
+  string16 error_1;
+  EXPECT_TRUE(managed_user_service->UserMayLoad(extension.get(), &error_1));
+  EXPECT_EQ(string16(), error_1);
+
+  string16 error_2;
+  EXPECT_TRUE(
+      managed_user_service->UserMayModifySettings(extension.get(), &error_2));
+  EXPECT_EQ(string16(), error_2);
+}
+
+TEST_F(ManagedUserServiceExtensionTest,
+       ExtensionManagementPolicyProviderManaged) {
+  ManagedUserService* managed_user_service =
+      ManagedUserServiceFactory::GetForProfile(profile_.get());
+  managed_user_service->InitForTesting();
+  ManagedModeURLFilterObserver observer(
+      managed_user_service->GetURLFilterForUIThread());
+  EXPECT_TRUE(profile_->IsManaged());
+  // Wait for the initial update to finish (otherwise we'll get leaks).
+  observer.Wait();
+
+  // Check that a supervised user can install a theme.
+  scoped_refptr<extensions::Extension> theme = MakeThemeExtension();
+  string16 error_1;
+  EXPECT_TRUE(managed_user_service->UserMayLoad(theme.get(), &error_1));
+  EXPECT_TRUE(error_1.empty());
+  EXPECT_TRUE(
+      managed_user_service->UserMayModifySettings(theme.get(), &error_1));
+  EXPECT_TRUE(error_1.empty());
+
+  // Now check a different kind of extension.
+  scoped_refptr<extensions::Extension> extension = MakeExtension();
+  EXPECT_FALSE(managed_user_service->UserMayLoad(extension.get(), &error_1));
+  EXPECT_FALSE(error_1.empty());
+
+  string16 error_2;
+  EXPECT_FALSE(
+      managed_user_service->UserMayModifySettings(extension.get(), &error_2));
+  EXPECT_FALSE(error_2.empty());
+
+#ifndef NDEBUG
+  EXPECT_FALSE(managed_user_service->GetDebugPolicyProviderName().empty());
+#endif
+}
+
+
 TEST_F(ManagedUserServiceExtensionTest, NoContentPacks) {
   ManagedUserService* managed_user_service =
       ManagedUserServiceFactory::GetForProfile(profile_.get());
diff --git a/chrome/browser/media/chrome_media_stream_infobar_browsertest.cc b/chrome/browser/media/chrome_media_stream_infobar_browsertest.cc
index 11bec08..055be9e 100644
--- a/chrome/browser/media/chrome_media_stream_infobar_browsertest.cc
+++ b/chrome/browser/media/chrome_media_stream_infobar_browsertest.cc
@@ -60,7 +60,9 @@
 
 // Actual tests ---------------------------------------------------------------
 
-#if defined(OS_CHROMEOS) && !defined(NDEBUG)
+// Failing on ChromiumOS Debug and Win Aura, so disabling on both.
+// See http://crbug.com/263333.
+#if (defined(OS_CHROMEOS) && !defined(NDEBUG)) || defined(USE_AURA)
 #define MAYBE_TestAllowingUserMedia DISABLED_TestAllowingUserMedia
 #else
 #define MAYBE_TestAllowingUserMedia TestAllowingUserMedia
@@ -112,7 +114,14 @@
   EXPECT_EQ(0u, infobar_service->infobar_count());
 }
 
-IN_PROC_BROWSER_TEST_F(MediaStreamInfoBarTest, TestAcceptIsNotSticky) {
+// Failing on Win Aura, so disabling on that.
+// See http://crbug.com/263333.
+#if defined(USE_AURA)
+#define MAYBE_TestAcceptIsNotSticky DISABLED_TestAcceptIsNotSticky
+#else
+#define MAYBE_TestAcceptIsNotSticky TestAcceptIsNotSticky
+#endif
+IN_PROC_BROWSER_TEST_F(MediaStreamInfoBarTest, MAYBE_TestAcceptIsNotSticky) {
   content::WebContents* tab_contents = LoadTestPageInTab();
 
   // If accept were sticky the second call would hang because it hangs if an
diff --git a/chrome/browser/media/chrome_webrtc_audio_quality_browsertest.cc b/chrome/browser/media/chrome_webrtc_audio_quality_browsertest.cc
index 1ca2cf2..90647aa 100644
--- a/chrome/browser/media/chrome_webrtc_audio_quality_browsertest.cc
+++ b/chrome/browser/media/chrome_webrtc_audio_quality_browsertest.cc
@@ -2,11 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <ctime>
+
 #include "base/command_line.h"
 #include "base/file_util.h"
 #include "base/path_service.h"
 #include "base/process/launch.h"
+#include "base/scoped_native_library.h"
 #include "base/strings/stringprintf.h"
+#include "base/win/windows_version.h"
 #include "chrome/browser/media/webrtc_browsertest_base.h"
 #include "chrome/browser/media/webrtc_browsertest_common.h"
 #include "chrome/browser/profiles/profile.h"
@@ -28,17 +32,25 @@
     FILE_PATH_LITERAL("peerconnection_server");
 #endif
 
-static const base::FilePath::CharType kMediaPath[] =
-    FILE_PATH_LITERAL("pyauto_private/webrtc/");
-static const base::FilePath::CharType kToolsPath[] =
-    FILE_PATH_LITERAL("pyauto_private/media/tools");
 static const base::FilePath::CharType kReferenceFile[] =
 #if defined (OS_WIN)
-    FILE_PATH_LITERAL("human-voice-win.wav");
+    FILE_PATH_LITERAL("pyauto_private/webrtc/human-voice-win.wav");
 #else
-    FILE_PATH_LITERAL("human-voice-linux.wav");
+    FILE_PATH_LITERAL("pyauto_private/webrtc/human-voice-linux.wav");
 #endif
 
+// The javascript will load the reference file relative to its location,
+// which is in /webrtc on the web server. Therefore, prepend a '..' traversal.
+static const char kReferenceFileRelativeUrl[] =
+#if defined (OS_WIN)
+    "../pyauto_private/webrtc/human-voice-win.wav";
+#else
+    "../pyauto_private/webrtc/human-voice-linux.wav";
+#endif
+
+static const base::FilePath::CharType kToolsPath[] =
+    FILE_PATH_LITERAL("pyauto_private/media/tools");
+
 static const char kMainWebrtcTestHtmlPage[] =
     "files/webrtc/webrtc_audio_quality_test.html";
 
@@ -85,17 +97,8 @@
 //    the recording.
 class WebrtcAudioQualityBrowserTest : public WebRtcTestBase {
  public:
-  WebrtcAudioQualityBrowserTest()
-      : peerconnection_server_(base::kNullProcessHandle) {}
-
-  virtual void SetUp() OVERRIDE {
-    RunPeerConnectionServer();
-    InProcessBrowserTest::SetUp();
-  }
-
-  virtual void TearDown() OVERRIDE {
-    ShutdownPeerConnectionServer();
-    InProcessBrowserTest::TearDown();
+  virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
+    PeerConnectionServerRunner::KillAllPeerConnectionServersOnCurrentSystem();
   }
 
   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
@@ -108,20 +111,23 @@
         switches::kUseFakeDeviceForMediaStream));
     EXPECT_FALSE(command_line->HasSwitch(
         switches::kUseFakeUIForMediaStream));
-
-    // Ensure we have the stuff we need.
-    base::FilePath reference_file =
-        GetTestDataDir().Append(kMediaPath).Append(kReferenceFile);
-    EXPECT_TRUE(base::PathExists(reference_file))
-        << "Cannot find the reference file to be used for audio quality "
-        << "comparison: " << reference_file.value();
   }
 
-  void AddAudioFile(const base::FilePath& input_file_relative,
+  bool HasAllRequiredResources() {
+    base::FilePath reference_file =
+        GetTestDataDir().Append(kReferenceFile);
+    if (!base::PathExists(reference_file)) {
+      LOG(ERROR) << "Cannot find the reference file to be used for audio "
+          << "quality comparison: " << reference_file.value();
+      return false;
+    }
+    return true;
+  }
+
+  void AddAudioFile(const std::string& input_file_relative_url,
                     content::WebContents* tab_contents) {
     EXPECT_EQ("ok-added", ExecuteJavascript(
-        base::StringPrintf("addAudioFile('%s')",
-                           input_file_relative.value().c_str()), tab_contents));
+        "addAudioFile('" + input_file_relative_url + "')", tab_contents));
   }
 
   void PlayAudioFile(content::WebContents* tab_contents) {
@@ -186,27 +192,7 @@
     return wav_filename;
   }
 
- private:
-  void RunPeerConnectionServer() {
-    base::FilePath peerconnection_server;
-    EXPECT_TRUE(PathService::Get(base::DIR_MODULE, &peerconnection_server));
-    peerconnection_server = peerconnection_server.Append(kPeerConnectionServer);
-
-    EXPECT_TRUE(base::PathExists(peerconnection_server)) <<
-        "Missing peerconnection_server. You must build "
-        "it so it ends up next to the browser test binary.";
-    EXPECT_TRUE(base::LaunchProcess(
-        CommandLine(peerconnection_server),
-        base::LaunchOptions(),
-        &peerconnection_server_)) << "Failed to launch peerconnection_server.";
-  }
-
-  void ShutdownPeerConnectionServer() {
-    EXPECT_TRUE(base::KillProcess(peerconnection_server_, 0, false)) <<
-        "Failed to shut down peerconnection_server!";
-  }
-
-  base::ProcessHandle peerconnection_server_;
+  PeerConnectionServerRunner peerconnection_server_;
 };
 
 class AudioRecorder {
@@ -214,14 +200,45 @@
   AudioRecorder(): recording_application_(base::kNullProcessHandle) {}
   ~AudioRecorder() {}
 
-  void StartRecording(int duration_sec, const base::FilePath& output_file,
+  // Starts the recording program for the specified duration. Returns true
+  // on success.
+  bool StartRecording(int duration_sec, const base::FilePath& output_file,
                       bool mono) {
     EXPECT_EQ(base::kNullProcessHandle, recording_application_)
         << "Tried to record, but is already recording.";
 
     CommandLine command_line(CommandLine::NO_PROGRAM);
 #if defined(OS_WIN)
-    NOTREACHED();  // TODO(phoglund): implement.
+    // This disable is required to run SoundRecorder.exe on 64-bit Windows
+    // from a 32-bit binary. We need to load the wow64 disable function from
+    // the DLL since it doesn't exist on Windows XP.
+    // TODO(phoglund): find some cleaner solution than using SoundRecorder.exe.
+    base::ScopedNativeLibrary kernel32_lib(base::FilePath(L"kernel32"));
+    if (kernel32_lib.is_valid()) {
+      typedef BOOL (WINAPI* Wow64DisableWow64FSRedirection)(PVOID*);
+      Wow64DisableWow64FSRedirection wow_64_disable_wow_64_fs_redirection;
+      wow_64_disable_wow_64_fs_redirection =
+          reinterpret_cast<Wow64DisableWow64FSRedirection>(
+              kernel32_lib.GetFunctionPointer(
+                  "Wow64DisableWow64FsRedirection"));
+      if (wow_64_disable_wow_64_fs_redirection != NULL) {
+        PVOID* ignored = NULL;
+        wow_64_disable_wow_64_fs_redirection(ignored);
+      }
+    }
+
+    char duration_in_hms[128] = {0};
+    struct tm duration_tm = {0};
+    duration_tm.tm_sec = duration_sec;
+    EXPECT_NE(0u, strftime(duration_in_hms, arraysize(duration_in_hms),
+                           "%H:%M:%S", &duration_tm));
+
+    command_line.SetProgram(
+        base::FilePath(FILE_PATH_LITERAL("SoundRecorder.exe")));
+    command_line.AppendArg("/FILE");
+    command_line.AppendArgPath(output_file);
+    command_line.AppendArg("/DURATION");
+    command_line.AppendArg(duration_in_hms);
 #else
     int num_channels = mono ? 1 : 2;
     command_line.SetProgram(base::FilePath("arecord"));
@@ -235,17 +252,15 @@
 #endif
 
     LOG(INFO) << "Running " << command_line.GetCommandLineString();
-    EXPECT_TRUE(base::LaunchProcess(
-        command_line,
-        base::LaunchOptions(),
-        &recording_application_)) << "Failed to launch recording application.";
+    return base::LaunchProcess(command_line, base::LaunchOptions(),
+                               &recording_application_);
   }
 
-  void WaitForRecordingToEnd() {
-    int exit_code;
-    EXPECT_TRUE(base::WaitForExitCode(recording_application_, &exit_code)) <<
-        "Failed to wait for recording to end.";
-    EXPECT_EQ(0, exit_code);
+  // Joins the recording program. Returns true on success.
+  bool WaitForRecordingToEnd() {
+    int exit_code = -1;
+    base::WaitForExitCode(recording_application_, &exit_code);
+    return exit_code == 0;
   }
  private:
   base::ProcessHandle recording_application_;
@@ -253,7 +268,8 @@
 
 void ForceMicrophoneVolumeTo100Percent() {
 #if defined(OS_WIN)
-  NOTREACHED();  // TODO(phoglund): implement.
+  CommandLine command_line(GetTestDataDir().Append(kToolsPath).Append(
+      FILE_PATH_LITERAL("force_mic_volume_max.exe")));
 #else
   const std::string kRecordingDeviceId = "render.monitor";
   const std::string kHundredPercentVolume = "65536";
@@ -262,6 +278,8 @@
   command_line.AppendArg("set-source-volume");
   command_line.AppendArg(kRecordingDeviceId);
   command_line.AppendArg(kHundredPercentVolume);
+#endif
+
   LOG(INFO) << "Running " << command_line.GetCommandLineString();
   std::string result;
   if (!base::GetAppOutput(command_line, &result)) {
@@ -272,12 +290,11 @@
         "The test may fail or have results distorted; please ensure that " <<
         "your mic level is 100% manually.";
   }
-#endif
 }
 
 // Removes silence from beginning and end of the |input_audio_file| and writes
-// the result to the |output_audio_file|.
-void RemoveSilence(const base::FilePath& input_file,
+// the result to the |output_audio_file|. Returns true on success.
+bool RemoveSilence(const base::FilePath& input_file,
                    const base::FilePath& output_file) {
   // SOX documentation for silence command: http://sox.sourceforge.net/sox.html
   // To remove the silence from both beginning and end of the audio file, we
@@ -293,7 +310,12 @@
   const char* kDuration = "2";
   const char* kTreshold = "5%";
 
+#if defined(OS_WIN)
+  CommandLine command_line(GetTestDataDir().Append(kToolsPath).Append(
+      FILE_PATH_LITERAL("sox.exe")));
+#else
   CommandLine command_line(base::FilePath(FILE_PATH_LITERAL("sox")));
+#endif
   command_line.AppendArgPath(input_file);
   command_line.AppendArgPath(output_file);
   command_line.AppendArg("silence");
@@ -309,9 +331,9 @@
 
   LOG(INFO) << "Running " << command_line.GetCommandLineString();
   std::string result;
-  EXPECT_TRUE(base::GetAppOutput(command_line, &result))
-      << "Failed to launch sox.";
+  bool ok = base::GetAppOutput(command_line, &result);
   LOG(INFO) << "Output was:\n\n" << result;
+  return ok;
 }
 
 bool CanParseAsFloat(const std::string& value) {
@@ -328,16 +350,27 @@
 //
 // The raw score in MOS is written to |raw_mos|, whereas the MOS-LQO score is
 // written to mos_lqo. The scores are returned as floats in string form (e.g.
-// "3.145", etc).
-void RunPesq(const base::FilePath& reference_file,
+// "3.145", etc). Returns true on success.
+bool RunPesq(const base::FilePath& reference_file,
              const base::FilePath& actual_file,
              int sample_rate, std::string* raw_mos, std::string* mos_lqo) {
   // PESQ will break if the paths are too long (!).
   EXPECT_LT(reference_file.value().length(), 128u);
   EXPECT_LT(actual_file.value().length(), 128u);
 
+#if defined(OS_WIN)
+  base::FilePath pesq_path =
+      GetTestDataDir().Append(kToolsPath).Append(FILE_PATH_LITERAL("pesq.exe"));
+#else
   base::FilePath pesq_path =
       GetTestDataDir().Append(kToolsPath).Append(FILE_PATH_LITERAL("pesq"));
+#endif
+
+  if (!base::PathExists(pesq_path)) {
+    LOG(ERROR) << "Missing PESQ binary in " << pesq_path.value();
+    return false;
+  }
+
   CommandLine command_line(pesq_path);
   command_line.AppendArg(base::StringPrintf("+%d", sample_rate));
   command_line.AppendArgPath(reference_file);
@@ -345,13 +378,19 @@
 
   LOG(INFO) << "Running " << command_line.GetCommandLineString();
   std::string result;
-  EXPECT_TRUE(base::GetAppOutput(command_line, &result))
-      << "Failed to launch pesq.";
+  if (!base::GetAppOutput(command_line, &result)) {
+    LOG(ERROR) << "Failed to run PESQ.";
+    return false;
+  }
   LOG(INFO) << "Output was:\n\n" << result;
 
   const std::string result_anchor = "Prediction (Raw MOS, MOS-LQO):  = ";
   std::size_t anchor_pos = result.find(result_anchor);
-  EXPECT_NE(std::string::npos, anchor_pos);
+  if (anchor_pos == std::string::npos) {
+    LOG(ERROR) << "PESQ was not able to compute a score; we probably recorded "
+        << "only silence.";
+    return false;
+  }
 
   // There are two tab-separated numbers on the format x.xxx, e.g. 5 chars each.
   std::size_t first_number_pos = anchor_pos + result_anchor.length();
@@ -359,10 +398,12 @@
   EXPECT_TRUE(CanParseAsFloat(*raw_mos)) << "Failed to parse raw MOS number.";
   *mos_lqo = result.substr(first_number_pos + 5 + 1, 5);
   EXPECT_TRUE(CanParseAsFloat(*mos_lqo)) << "Failed to parse MOS LQO number.";
+
+  return true;
 }
 
-#if defined(OS_LINUX)
-// Only implemented on Linux for now.
+#if defined(OS_LINUX) || defined(OS_WIN)
+// Only implemented on Linux and Windows for now.
 #define MAYBE_MANUAL_TestAudioQuality MANUAL_TestAudioQuality
 #else
 #define MAYBE_MANUAL_TestAudioQuality DISABLED_MANUAL_TestAudioQuality
@@ -370,7 +411,18 @@
 
 IN_PROC_BROWSER_TEST_F(WebrtcAudioQualityBrowserTest,
                        MAYBE_MANUAL_TestAudioQuality) {
-  EXPECT_TRUE(test_server()->Start());
+#if defined(OS_WIN)
+  if (base::win::GetVersion() < base::win::VERSION_VISTA) {
+    // It would take work to implement this on XP; not prioritized right now.
+    LOG(INFO) << "This test is not implemented for Windows XP.";
+    return;
+  }
+#endif
+  ASSERT_TRUE(HasAllRequiredResources());
+  // TODO(phoglund): make this use embedded_test_server when that test server
+  // can handle files > ~400Kb.
+  ASSERT_TRUE(test_server()->Start());
+  ASSERT_TRUE(peerconnection_server_.Start());
 
   ForceMicrophoneVolumeTo100Percent();
 
@@ -383,7 +435,7 @@
   content::WebContents* right_tab =
       browser()->tab_strip_model()->GetActiveWebContents();
   ui_test_utils::NavigateToURL(
-        browser(), test_server()->GetURL(kMainWebrtcTestHtmlPage));
+      browser(), test_server()->GetURL(kMainWebrtcTestHtmlPage));
 
   ConnectToPeerConnectionServer("peer 1", left_tab);
   ConnectToPeerConnectionServer("peer 2", right_tab);
@@ -391,13 +443,7 @@
   EXPECT_EQ("ok-peerconnection-created",
             ExecuteJavascript("preparePeerConnection()", left_tab));
 
-  base::FilePath reference_file =
-      base::FilePath(kMediaPath).Append(kReferenceFile);
-
-  // The javascript will load the reference file relative to its location,
-  // which is in /webrtc on the web server. Therefore, prepend a '..' traversal.
-  AddAudioFile(base::FilePath(FILE_PATH_LITERAL("..")).Append(reference_file),
-               left_tab);
+  AddAudioFile(kReferenceFileRelativeUrl, left_tab);
 
   EstablishCall(left_tab, right_tab);
 
@@ -413,11 +459,11 @@
   // safety margins on each side.
   AudioRecorder recorder;
   static int kRecordingTimeSeconds = 15;
-  recorder.StartRecording(kRecordingTimeSeconds, recording, true);
+  ASSERT_TRUE(recorder.StartRecording(kRecordingTimeSeconds, recording, true));
 
   PlayAudioFile(left_tab);
 
-  recorder.WaitForRecordingToEnd();
+  ASSERT_TRUE(recorder.WaitForRecordingToEnd());
   LOG(INFO) << "Done recording to " << recording.value() << std::endl;
 
   AssertNoAsynchronousErrors(left_tab);
@@ -432,19 +478,21 @@
 
   base::FilePath trimmed_recording = CreateTemporaryWaveFile();
 
-  RemoveSilence(recording, trimmed_recording);
+  ASSERT_TRUE(RemoveSilence(recording, trimmed_recording));
   LOG(INFO) << "Trimmed silence: " << trimmed_recording.value() << std::endl;
 
   std::string raw_mos;
   std::string mos_lqo;
   base::FilePath reference_file_in_test_dir =
-      GetTestDataDir().Append(reference_file);
-  RunPesq(reference_file_in_test_dir, trimmed_recording, 16000, &raw_mos,
-          &mos_lqo);
+      GetTestDataDir().Append(kReferenceFile);
+  ASSERT_TRUE(RunPesq(reference_file_in_test_dir, trimmed_recording, 16000,
+                      &raw_mos, &mos_lqo));
 
   perf_test::PrintResult("audio_pesq", "", "raw_mos", raw_mos, "score", true);
   perf_test::PrintResult("audio_pesq", "", "mos_lqo", mos_lqo, "score", true);
 
   EXPECT_TRUE(base::DeleteFile(recording, false));
   EXPECT_TRUE(base::DeleteFile(trimmed_recording, false));
+
+  ASSERT_TRUE(peerconnection_server_.Stop());
 }
diff --git a/chrome/browser/media/chrome_webrtc_browsertest.cc b/chrome/browser/media/chrome_webrtc_browsertest.cc
index de56fc1..e67100e 100644
--- a/chrome/browser/media/chrome_webrtc_browsertest.cc
+++ b/chrome/browser/media/chrome_webrtc_browsertest.cc
@@ -46,39 +46,8 @@
 // test suite since normal tester machines do not have webcams.
 class WebrtcBrowserTest : public WebRtcTestBase {
  public:
-  // See comment in test where this class is used for purpose.
-  class BrowserMessageFilter : public content::BrowserMessageFilter {
-   public:
-    explicit BrowserMessageFilter(
-        const base::Closure& on_channel_closing)
-        : on_channel_closing_(on_channel_closing) {}
-
-    virtual void OnChannelClosing() OVERRIDE {
-      // Posting on the file thread ensures that the callback is run after
-      // WebRtcLogUploader::UploadLog has finished. See also comment in
-      // MANUAL_RunsAudioVideoWebRTCCallInTwoTabsWithLogging test.
-      content::BrowserThread::PostTask(content::BrowserThread::FILE,
-          FROM_HERE, on_channel_closing_);
-    }
-
-    virtual bool OnMessageReceived(const IPC::Message& message,
-                                   bool* message_was_ok) OVERRIDE {
-      return false;
-    }
-
-  private:
-    virtual ~BrowserMessageFilter() {}
-
-    base::Closure on_channel_closing_;
-  };
-
   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
     PeerConnectionServerRunner::KillAllPeerConnectionServersOnCurrentSystem();
-    peerconnection_server_.Start();
-  }
-
-  virtual void TearDownInProcessBrowserTestFixture() OVERRIDE {
-    peerconnection_server_.Stop();
   }
 
   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
@@ -208,13 +177,13 @@
     }
   }
 
- private:
   PeerConnectionServerRunner peerconnection_server_;
 };
 
 IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
                        MANUAL_RunsAudioVideoWebRTCCallInTwoTabs) {
-  EXPECT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+  ASSERT_TRUE(peerconnection_server_.Start());
 
   ui_test_utils::NavigateToURL(
       browser(), embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
@@ -249,13 +218,16 @@
 
   AssertNoAsynchronousErrors(left_tab);
   AssertNoAsynchronousErrors(right_tab);
+
+  ASSERT_TRUE(peerconnection_server_.Stop());
 }
 
 // TODO(phoglund): figure out how to do mach port brokering on Mac.
 #if !defined(OS_MACOSX)
 IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
                        MANUAL_RendererCpuUsage20Seconds) {
-  EXPECT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+  ASSERT_TRUE(peerconnection_server_.Start());
 
   base::FilePath results_file;
   EXPECT_TRUE(file_util::CreateTemporaryFile(&results_file));
@@ -306,12 +278,15 @@
 
   AssertNoAsynchronousErrors(left_tab);
   AssertNoAsynchronousErrors(right_tab);
+
+  ASSERT_TRUE(peerconnection_server_.Stop());
 }
 #endif
 
 IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
                        MANUAL_TestMediaStreamTrackEnableDisable) {
-  EXPECT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+  ASSERT_TRUE(peerconnection_server_.Start());
 
   ui_test_utils::NavigateToURL(
       browser(), embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
@@ -354,8 +329,36 @@
 
   AssertNoAsynchronousErrors(left_tab);
   AssertNoAsynchronousErrors(right_tab);
+
+  ASSERT_TRUE(peerconnection_server_.Stop());
 }
 
+// See comment in test where this class is used for purpose.
+class BrowserMessageFilter : public content::BrowserMessageFilter {
+ public:
+  explicit BrowserMessageFilter(
+      const base::Closure& on_channel_closing)
+      : on_channel_closing_(on_channel_closing) {}
+
+  virtual void OnChannelClosing() OVERRIDE {
+    // Posting on the file thread ensures that the callback is run after
+    // WebRtcLogUploader::UploadLog has finished. See also comment in
+    // MANUAL_RunsAudioVideoWebRTCCallInTwoTabsWithLogging test.
+    content::BrowserThread::PostTask(content::BrowserThread::FILE,
+        FROM_HERE, on_channel_closing_);
+  }
+
+  virtual bool OnMessageReceived(const IPC::Message& message,
+                                 bool* message_was_ok) OVERRIDE {
+    return false;
+  }
+
+private:
+  virtual ~BrowserMessageFilter() {}
+
+  base::Closure on_channel_closing_;
+};
+
 // Tests WebRTC diagnostic logging. Sets up the browser to save the multipart
 // contents to a buffer instead of uploading it, then verifies it after a call.
 // Example of multipart contents:
@@ -392,6 +395,7 @@
 IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
                        MANUAL_RunsAudioVideoWebRTCCallInTwoTabsWithLogging) {
   EXPECT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+  ASSERT_TRUE(peerconnection_server_.Start());
 
   // Add command line switch that forces allowing log uploads.
   CommandLine* command_line = CommandLine::ForCurrentProcess();
@@ -524,4 +528,6 @@
   final_delimiter += "--";
   EXPECT_STREQ(final_delimiter.c_str(), multipart_lines[29].c_str());
   EXPECT_TRUE(multipart_lines[30].empty());
+
+  ASSERT_TRUE(peerconnection_server_.Stop());
 }
diff --git a/chrome/browser/media/chrome_webrtc_video_quality_browsertest.cc b/chrome/browser/media/chrome_webrtc_video_quality_browsertest.cc
index c1e0056..f4713d0 100644
--- a/chrome/browser/media/chrome_webrtc_video_quality_browsertest.cc
+++ b/chrome/browser/media/chrome_webrtc_video_quality_browsertest.cc
@@ -27,8 +27,8 @@
 #include "chrome/test/ui/ui_test.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/test/browser_test_utils.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/python_utils.h"
-#include "net/test/spawned_test_server/spawned_test_server.h"
 
 static const base::FilePath::CharType kFrameAnalyzerExecutable[] =
 #if defined(OS_WIN)
@@ -61,9 +61,9 @@
 static const base::FilePath::CharType kStatsFileName[] =
     FILE_PATH_LITERAL("stats.txt");
 static const char kMainWebrtcTestHtmlPage[] =
-    "files/webrtc/webrtc_jsep01_test.html";
+    "/webrtc/webrtc_jsep01_test.html";
 static const char kCapturingWebrtcHtmlPage[] =
-    "files/webrtc/webrtc_video_quality_test.html";
+    "/webrtc/webrtc_video_quality_test.html";
 static const int kVgaWidth = 640;
 static const int kVgaHeight = 480;
 
@@ -99,22 +99,7 @@
         environment_(base::Environment::Create()) {}
 
   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
-    peerconnection_server_.Start();
-
-    // Ensure we have the stuff we need.
-    EXPECT_TRUE(base::PathExists(GetWorkingDir()))
-        << "Cannot find the working directory for the reference video and "
-           "the temporary files:" << GetWorkingDir().value();
-    base::FilePath reference_file =
-        GetWorkingDir().Append(kReferenceYuvFileName);
-    EXPECT_TRUE(base::PathExists(reference_file))
-        << "Cannot find the reference file to be used for video quality "
-        << "comparison: " << reference_file.value();
-  }
-
-  virtual void TearDownInProcessBrowserTestFixture() OVERRIDE {
-    peerconnection_server_.Stop();
-    ShutdownPyWebSocketServer();
+    PeerConnectionServerRunner::KillAllPeerConnectionServersOnCurrentSystem();
   }
 
   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
@@ -131,7 +116,23 @@
     command_line->AppendSwitch(switches::kUseGpuInTests);
   }
 
-  void StartPyWebSocketServer() {
+  bool HasAllRequiredResources() {
+    if (!base::PathExists(GetWorkingDir())) {
+      LOG(ERROR) << "Cannot find the working directory for the reference video "
+          "and the temporary files:" << GetWorkingDir().value();
+      return false;
+    }
+    base::FilePath reference_file =
+        GetWorkingDir().Append(kReferenceYuvFileName);
+    if (!base::PathExists(reference_file)) {
+      LOG(ERROR) << "Cannot find the reference file to be used for video "
+          << "quality comparison: " << reference_file.value();
+      return false;
+    }
+    return true;
+  }
+
+  bool StartPyWebSocketServer() {
     base::FilePath path_pywebsocket_dir =
         GetSourceDir().Append(FILE_PATH_LITERAL("third_party/pywebsocket/src"));
     base::FilePath pywebsocket_server = path_pywebsocket_dir.Append(
@@ -139,10 +140,14 @@
     base::FilePath path_to_data_handler =
         GetSourceDir().Append(FILE_PATH_LITERAL("chrome/test/functional"));
 
-    EXPECT_TRUE(base::PathExists(pywebsocket_server))
-        << "Fatal: missing pywebsocket server.";
-    EXPECT_TRUE(base::PathExists(path_to_data_handler))
-        << "Fatal: missing data handler for pywebsocket server.";
+    if (!base::PathExists(pywebsocket_server)) {
+      LOG(ERROR) << "Missing pywebsocket server.";
+      return false;
+    }
+    if (!base::PathExists(path_to_data_handler)) {
+      LOG(ERROR) << "Missing data handler for pywebsocket server.";
+      return false;
+    }
 
     AppendToPythonPath(path_pywebsocket_dir);
 
@@ -158,14 +163,12 @@
     pywebsocket_command.AppendArgPath(path_to_data_handler);
 
     LOG(INFO) << "Running " << pywebsocket_command.GetCommandLineString();
-    EXPECT_TRUE(base::LaunchProcess(
-        pywebsocket_command, base::LaunchOptions(), &pywebsocket_server_))
-        << "Failed to launch pywebsocket server.";
+    return base::LaunchProcess(pywebsocket_command, base::LaunchOptions(),
+                               &pywebsocket_server_);
   }
 
-  void ShutdownPyWebSocketServer() {
-    EXPECT_TRUE(base::KillProcess(pywebsocket_server_, 0, false))
-        << "Failed to shut down pywebsocket server!";
+  bool ShutdownPyWebSocketServer() {
+    return base::KillProcess(pywebsocket_server_, 0, false);
   }
 
   // Convenience method which executes the provided javascript in the context
@@ -225,14 +228,17 @@
   // The rgba_to_i420_converter is part of the webrtc_test_tools target which
   // should be build prior to running this test. The resulting binary should
   // live next to Chrome.
-  void RunARGBtoI420Converter(int width,
+  bool RunARGBtoI420Converter(int width,
                               int height,
                               const base::FilePath& captured_video_filename) {
     base::FilePath path_to_converter = base::MakeAbsoluteFilePath(
         GetBrowserDir().Append(kArgbToI420ConverterExecutable));
-    EXPECT_TRUE(base::PathExists(path_to_converter))
-        << "Missing ARGB->I420 converter: should be in "
-        << path_to_converter.value();
+
+    if (!base::PathExists(path_to_converter)) {
+      LOG(ERROR) << "Missing ARGB->I420 converter: should be in "
+          << path_to_converter.value();
+      return false;
+    }
 
     CommandLine converter_command(path_to_converter);
     converter_command.AppendSwitchPath("--frames_dir", GetWorkingDir());
@@ -247,8 +253,9 @@
     // barcode decoder and frame analyzer tools.
     LOG(INFO) << "Running " << converter_command.GetCommandLineString();
     std::string result;
-    EXPECT_TRUE(base::GetAppOutput(converter_command, &result));
+    bool ok = base::GetAppOutput(converter_command, &result);
     LOG(INFO) << "Output was:\n\n" << result;
+    return ok;
   }
 
   // Compares the |captured_video_filename| with the |reference_video_filename|.
@@ -258,21 +265,28 @@
   // produces a set of PNG images and a |stats_file| that maps each captured
   // frame to a frame in the reference video. The frames should be of size
   // |width| x |height|. The output of compare_videos.py is returned.
-  std::string CompareVideos(int width,
-                            int height,
-                            const base::FilePath& captured_video_filename,
-                            const base::FilePath& reference_video_filename,
-                            const base::FilePath& stats_file) {
+  bool CompareVideos(int width,
+                     int height,
+                     const base::FilePath& captured_video_filename,
+                     const base::FilePath& reference_video_filename,
+                     const base::FilePath& stats_file,
+                     std::string* result) {
+
     base::FilePath path_to_analyzer = base::MakeAbsoluteFilePath(
         GetBrowserDir().Append(kFrameAnalyzerExecutable));
     base::FilePath path_to_compare_script = GetSourceDir().Append(
         FILE_PATH_LITERAL("third_party/webrtc/tools/compare_videos.py"));
 
-    EXPECT_TRUE(base::PathExists(path_to_analyzer))
-        << "Missing frame analyzer: should be in " << path_to_analyzer.value();
-    EXPECT_TRUE(base::PathExists(path_to_compare_script))
-        << "Missing video compare script: should be in "
-        << path_to_compare_script.value();
+    if (!base::PathExists(path_to_analyzer)) {
+      LOG(ERROR) << "Missing frame analyzer: should be in "
+          << path_to_analyzer.value();
+      return false;
+    }
+    if (!base::PathExists(path_to_compare_script)) {
+      LOG(ERROR) << "Missing video compare script: should be in "
+          << path_to_compare_script.value();
+      return false;
+    }
 
     // Note: don't append switches to this command since it will mess up the
     // -u in the python invocation!
@@ -294,10 +308,9 @@
     compare_command.AppendArgPath(stats_file);
 
     LOG(INFO) << "Running " << compare_command.GetCommandLineString();
-    std::string result;
-    EXPECT_TRUE(base::GetAppOutput(compare_command, &result));
-    LOG(INFO) << "Output was:\n\n" << result;
-    return result;
+    bool ok = base::GetAppOutput(compare_command, result);
+    LOG(INFO) << "Output was:\n\n" << *result;
+    return ok;
   }
 
   // Processes the |frame_analyzer_output| for the different frame counts.
@@ -367,7 +380,10 @@
       psnr_value_list.append(psnr_and_ssim.first).append(",");
       ssim_value_list.append(psnr_and_ssim.second).append(",");
     }
+
     // Nuke last comma.
+    ASSERT_GT(psnr_value_list.size(), 0u) << "Received no valid PSNR values.";
+    ASSERT_GT(ssim_value_list.size(), 0u) << "Received no valid SSIM values.";
     psnr_value_list.erase(psnr_value_list.size() - 1);
     ssim_value_list.erase(ssim_value_list.size() - 1);
 
@@ -383,6 +399,8 @@
     return base::FilePath(native_home_dir).Append(kWorkingDirName);
   }
 
+  PeerConnectionServerRunner peerconnection_server_;
+
  private:
   base::FilePath GetSourceDir() {
     base::FilePath source_dir;
@@ -396,19 +414,19 @@
     return browser_dir;
   }
 
-  PeerConnectionServerRunner peerconnection_server_;
   base::ProcessHandle pywebsocket_server_;
   scoped_ptr<base::Environment> environment_;
 };
 
 IN_PROC_BROWSER_TEST_F(WebrtcVideoQualityBrowserTest,
                        MANUAL_TestVGAVideoQuality) {
-  StartPyWebSocketServer();
+  ASSERT_TRUE(HasAllRequiredResources());
+  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+  ASSERT_TRUE(StartPyWebSocketServer());
+  ASSERT_TRUE(peerconnection_server_.Start());
 
-  EXPECT_TRUE(test_server()->Start());
-
-  ui_test_utils::NavigateToURL(browser(),
-                               test_server()->GetURL(kMainWebrtcTestHtmlPage));
+  ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage));
   content::WebContents* left_tab =
       browser()->tab_strip_model()->GetActiveWebContents();
   GetUserMediaAndAccept(left_tab);
@@ -416,8 +434,8 @@
   chrome::AddBlankTabAt(browser(), -1, true);
   content::WebContents* right_tab =
       browser()->tab_strip_model()->GetActiveWebContents();
-  ui_test_utils::NavigateToURL(browser(),
-                               test_server()->GetURL(kCapturingWebrtcHtmlPage));
+  ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL(kCapturingWebrtcHtmlPage));
   GetUserMediaAndAccept(right_tab);
 
   ConnectToPeerConnectionServer("peer 1", left_tab);
@@ -449,13 +467,18 @@
 
   RunARGBtoI420Converter(
       kVgaWidth, kVgaHeight, GetWorkingDir().Append(kCapturedYuvFileName));
-  std::string output =
+  std::string output;
+  ASSERT_TRUE(
       CompareVideos(kVgaWidth,
                     kVgaHeight,
                     GetWorkingDir().Append(kCapturedYuvFileName),
                     GetWorkingDir().Append(kReferenceYuvFileName),
-                    GetWorkingDir().Append(kStatsFileName));
+                    GetWorkingDir().Append(kStatsFileName),
+                    &output));
 
   PrintFramesCountPerfResults(output);
   PrintPsnrAndSsimPerfResults(output);
+
+  ASSERT_TRUE(peerconnection_server_.Stop());
+  ASSERT_TRUE(ShutdownPyWebSocketServer());
 }
diff --git a/chrome/browser/media/desktop_media_picker_model_unittest.cc b/chrome/browser/media/desktop_media_picker_model_unittest.cc
index d8f5825..e5d4c52 100644
--- a/chrome/browser/media/desktop_media_picker_model_unittest.cc
+++ b/chrome/browser/media/desktop_media_picker_model_unittest.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/media/desktop_media_picker_model.h"
 
 #include "base/message_loop/message_loop.h"
-#include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/lock.h"
 #include "content/public/test/test_browser_thread.h"
@@ -16,7 +15,6 @@
 #include "third_party/webrtc/modules/desktop_capture/window_capturer.h"
 
 using testing::_;
-using testing::AtMost;
 using testing::DoAll;
 
 namespace {
@@ -63,19 +61,18 @@
   FakeWindowCapturer()
       : callback_(NULL) {
   }
-  virtual ~FakeWindowCapturer() {
-    STLDeleteContainerPairSecondPointers(frames_.begin(), frames_.end());
-  }
+  virtual ~FakeWindowCapturer() {}
 
   void SetWindowList(const WindowList& list) {
     base::AutoLock lock(window_list_lock_);
     window_list_ = list;
   }
 
-  void SetNextFrame(WindowId window_id,
-                    scoped_ptr<webrtc::DesktopFrame> frame) {
-    base::AutoLock lock(frames_lock_);
-    frames_[window_id] = frame.release();
+  // Sets |value| thats going to be used to memset() content of the frames
+  // generated for |window_id|. By default generated frames are set to zeros.
+  void SetNextFrameValue(WindowId window_id, int8_t value) {
+    base::AutoLock lock(frame_values_lock_);
+    frame_values_[window_id] = value;
   }
 
   // webrtc::WindowCapturer implementation.
@@ -86,18 +83,14 @@
   virtual void Capture(const webrtc::DesktopRegion& region) OVERRIDE {
     DCHECK(callback_);
 
-    base::AutoLock lock(frames_lock_);
+    base::AutoLock lock(frame_values_lock_);
 
-    webrtc::DesktopFrame* frame;
-    std::map<WindowId, webrtc::DesktopFrame*>::iterator it =
-        frames_.find(selected_window_id_);
-    if (it == frames_.end()) {
-      frame = new webrtc::BasicDesktopFrame(webrtc::DesktopSize(10, 10));
-      memset(frame->data(), 0, frame->stride() * frame->size().height());
-    } else {
-      frame = it->second;
-      frames_.erase(it);
-    }
+    std::map<WindowId, int8_t>::iterator it =
+        frame_values_.find(selected_window_id_);
+    int8_t value = (it != frame_values_.end()) ? it->second : 0;
+    webrtc::DesktopFrame* frame =
+        new webrtc::BasicDesktopFrame(webrtc::DesktopSize(10, 10));
+    memset(frame->data(), value, frame->stride() * frame->size().height());
     callback_->OnCaptureCompleted(frame);
   }
 
@@ -120,8 +113,8 @@
   WindowId selected_window_id_;
 
   // Frames to be captured per window.
-  std::map<WindowId, webrtc::DesktopFrame*> frames_;
-  base::Lock frames_lock_;
+  std::map<WindowId, int8_t> frame_values_;
+  base::Lock frame_values_lock_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeWindowCapturer);
 };
@@ -366,8 +359,7 @@
   EXPECT_EQ(model_->source(1).name, base::UTF8ToUTF16(kTestTitle));
 }
 
-// Disabled due to flakiness on all platforms, see http://crbug.com/275260.
-TEST_F(DesktopMediaPickerModelTest, DISABLED_UpdateThumbnail) {
+TEST_F(DesktopMediaPickerModelTest, UpdateThumbnail) {
   CreateWithDefaultCapturers();
 
   webrtc::WindowCapturer::WindowList list;
@@ -403,10 +395,7 @@
     .WillOnce(QuitMessageLoop(&message_loop_));
 
   // Update frame for the window and verify that we get notification about it.
-  scoped_ptr<webrtc::DesktopFrame> frame(
-      new webrtc::BasicDesktopFrame(webrtc::DesktopSize(10, 10)));
-  memset(frame->data(), 1, frame->stride() * frame->size().height());
-  window_capturer_->SetNextFrame(0, frame.Pass());
+  window_capturer_->SetNextFrameValue(0, 1);
 
   message_loop_.Run();
 }
diff --git a/chrome/browser/media/media_capture_devices_dispatcher.cc b/chrome/browser/media/media_capture_devices_dispatcher.cc
index 1ef3a23..3b080f4 100644
--- a/chrome/browser/media/media_capture_devices_dispatcher.cc
+++ b/chrome/browser/media/media_capture_devices_dispatcher.cc
@@ -199,9 +199,13 @@
       request.audio_type == content::MEDIA_SYSTEM_AUDIO_CAPTURE) {
     ProcessDesktopCaptureAccessRequest(
         web_contents, request, callback, extension);
-  } else if (extension) {
+  } else if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE ||
+             request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE) {
+    ProcessTabCaptureAccessRequest(
+        web_contents, request, callback, extension);
+  } else if (extension && extension->is_platform_app()) {
     // For extensions access is approved based on extension permissions.
-    ProcessMediaAccessRequestFromExtension(
+    ProcessMediaAccessRequestFromPlatformApp(
         web_contents, request, callback, extension);
   } else {
     ProcessRegularMediaAccessRequest(web_contents, request, callback);
@@ -361,7 +365,49 @@
   callback.Run(devices, ui.Pass());
 }
 
-void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequestFromExtension(
+void MediaCaptureDevicesDispatcher::ProcessTabCaptureAccessRequest(
+    content::WebContents* web_contents,
+    const content::MediaStreamRequest& request,
+    const content::MediaResponseCallback& callback,
+    const extensions::Extension* extension) {
+  content::MediaStreamDevices devices;
+  scoped_ptr<content::MediaStreamUI> ui;
+
+#if defined(OS_ANDROID)
+  // Tab capture is not supported on Android.
+  callback.Run(devices, ui.Pass());
+#else  // defined(OS_ANDROID)
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
+  extensions::TabCaptureRegistry* tab_capture_registry =
+      extensions::TabCaptureRegistryFactory::GetForProfile(profile);
+  bool tab_capture_allowed =
+      tab_capture_registry->VerifyRequest(request.render_process_id,
+                                          request.render_view_id);
+
+  if (request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE &&
+      tab_capture_allowed &&
+      extension->HasAPIPermission(extensions::APIPermission::kTabCapture)) {
+    devices.push_back(content::MediaStreamDevice(
+        content::MEDIA_TAB_AUDIO_CAPTURE, std::string(), std::string()));
+  }
+
+  if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE &&
+      tab_capture_allowed &&
+      extension->HasAPIPermission(extensions::APIPermission::kTabCapture)) {
+    devices.push_back(content::MediaStreamDevice(
+        content::MEDIA_TAB_VIDEO_CAPTURE, std::string(), std::string()));
+  }
+
+  if (!devices.empty()) {
+    ui = media_stream_capture_indicator_->RegisterMediaStream(
+        web_contents, devices);
+  }
+  callback.Run(devices, ui.Pass());
+#endif  // !defined(OS_ANDROID)
+}
+
+void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequestFromPlatformApp(
     content::WebContents* web_contents,
     const content::MediaStreamRequest& request,
     const content::MediaResponseCallback& callback,
@@ -370,35 +416,13 @@
   Profile* profile =
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
 
-#if defined(OS_ANDROID)
-  // Tab capture is not supported on Android.
-  bool tab_capture_allowed = false;
-#else
-  extensions::TabCaptureRegistry* tab_capture_registry =
-      extensions::TabCaptureRegistryFactory::GetForProfile(profile);
-  bool tab_capture_allowed =
-      tab_capture_registry->VerifyRequest(request.render_process_id,
-                                          request.render_view_id);
-#endif
-
-  if (request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE &&
-      tab_capture_allowed &&
-      extension->HasAPIPermission(extensions::APIPermission::kTabCapture)) {
-    devices.push_back(content::MediaStreamDevice(
-        content::MEDIA_TAB_AUDIO_CAPTURE, std::string(), std::string()));
-  } else if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE &&
-             extension->HasAPIPermission(
-                 extensions::APIPermission::kAudioCapture)) {
+  if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE &&
+      extension->HasAPIPermission(extensions::APIPermission::kAudioCapture)) {
     GetDefaultDevicesForProfile(profile, true, false, &devices);
   }
 
-  if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE &&
-      tab_capture_allowed &&
-      extension->HasAPIPermission(extensions::APIPermission::kTabCapture)) {
-    devices.push_back(content::MediaStreamDevice(
-        content::MEDIA_TAB_VIDEO_CAPTURE, std::string(), std::string()));
-  } else if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE &&
-       extension->HasAPIPermission(extensions::APIPermission::kVideoCapture)) {
+  if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE &&
+      extension->HasAPIPermission(extensions::APIPermission::kVideoCapture)) {
     GetDefaultDevicesForProfile(profile, false, true, &devices);
   }
 
diff --git a/chrome/browser/media/media_capture_devices_dispatcher.h b/chrome/browser/media/media_capture_devices_dispatcher.h
index b36889f..4f4abd3 100644
--- a/chrome/browser/media/media_capture_devices_dispatcher.h
+++ b/chrome/browser/media/media_capture_devices_dispatcher.h
@@ -171,7 +171,12 @@
       const content::MediaStreamRequest& request,
       const content::MediaResponseCallback& callback,
       const extensions::Extension* extension);
-  void ProcessMediaAccessRequestFromExtension(
+  void ProcessTabCaptureAccessRequest(
+      content::WebContents* web_contents,
+      const content::MediaStreamRequest& request,
+      const content::MediaResponseCallback& callback,
+      const extensions::Extension* extension);
+  void ProcessMediaAccessRequestFromPlatformApp(
       content::WebContents* web_contents,
       const content::MediaStreamRequest& request,
       const content::MediaResponseCallback& callback,
diff --git a/chrome/browser/media/media_stream_capture_indicator.cc b/chrome/browser/media/media_stream_capture_indicator.cc
index e92304a..6a375d3 100644
--- a/chrome/browser/media/media_stream_capture_indicator.cc
+++ b/chrome/browser/media/media_stream_capture_indicator.cc
@@ -279,23 +279,6 @@
   return usage->RegisterMediaStream(devices);
 }
 
-bool MediaStreamCaptureIndicator::IsCommandIdChecked(
-    int command_id) const {
-  NOTIMPLEMENTED() << "There are no checked items in the MediaStream menu.";
-  return false;
-}
-
-bool MediaStreamCaptureIndicator::IsCommandIdEnabled(
-    int command_id) const {
-  return command_id != IDC_MinimumLabelValue;
-}
-
-bool MediaStreamCaptureIndicator::GetAcceleratorForCommandId(
-    int command_id, ui::Accelerator* accelerator) {
-  // No accelerators for status icon context menu.
-  return false;
-}
-
 void MediaStreamCaptureIndicator::ExecuteCommand(int command_id,
                                                  int event_flags) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -409,7 +392,7 @@
 
 void MediaStreamCaptureIndicator::UpdateNotificationUserInterface() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  scoped_ptr<ui::SimpleMenuModel> menu(new ui::SimpleMenuModel(this));
+  scoped_ptr<StatusIconMenuModel> menu(new StatusIconMenuModel(this));
 
   bool audio = false;
   bool video = false;
@@ -435,6 +418,10 @@
       command_targets_.push_back(web_contents);
       menu->AddItem(command_id, GetTitle(web_contents));
 
+      // If the menu item is not a label, enable it.
+      menu->SetCommandIdEnabled(command_id,
+                                command_id != IDC_MinimumLabelValue);
+
       // If reaching the maximum number, no more item will be added to the menu.
       if (command_id == IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_LAST)
         break;
@@ -450,7 +437,7 @@
   // The icon will take the ownership of the passed context menu.
   MaybeCreateStatusTrayIcon(audio, video);
   if (status_icon_) {
-    status_icon_->SetContextMenu(menu.release());
+    status_icon_->SetContextMenu(menu.Pass());
   }
 }
 
diff --git a/chrome/browser/media/media_stream_capture_indicator.h b/chrome/browser/media/media_stream_capture_indicator.h
index 0d9d369..30eb338 100644
--- a/chrome/browser/media/media_stream_capture_indicator.h
+++ b/chrome/browser/media/media_stream_capture_indicator.h
@@ -12,8 +12,8 @@
 
 #include "base/callback_forward.h"
 #include "base/memory/ref_counted.h"
+#include "chrome/browser/status_icons/status_icon_menu_model.h"
 #include "content/public/common/media_stream_request.h"
-#include "ui/base/models/simple_menu_model.h"
 
 namespace content {
 class WebContents;
@@ -30,7 +30,7 @@
 // (MediaCaptureDevicesDispatcher is a singleton).
 class MediaStreamCaptureIndicator
     : public base::RefCountedThreadSafe<MediaStreamCaptureIndicator>,
-      public ui::SimpleMenuModel::Delegate {
+      public StatusIconMenuModel::Delegate {
  public:
   MediaStreamCaptureIndicator();
 
@@ -40,12 +40,7 @@
       content::WebContents* web_contents,
       const content::MediaStreamDevices& devices);
 
-  // Overrides from SimpleMenuModel::Delegate implementation.
-  virtual bool IsCommandIdChecked(int command_id) const OVERRIDE;
-  virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE;
-  virtual bool GetAcceleratorForCommandId(
-      int command_id,
-      ui::Accelerator* accelerator) OVERRIDE;
+  // Overrides from StatusIconMenuModel::Delegate implementation.
   virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE;
 
   // Returns true if the |web_contents| is capturing user media (e.g., webcam or
diff --git a/chrome/browser/media/media_stream_devices_controller.cc b/chrome/browser/media/media_stream_devices_controller.cc
index 08fde6c..9f2b857 100644
--- a/chrome/browser/media/media_stream_devices_controller.cc
+++ b/chrome/browser/media/media_stream_devices_controller.cc
@@ -22,6 +22,7 @@
 #include "components/user_prefs/pref_registry_syncable.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/media_stream_request.h"
+#include "extensions/common/constants.h"
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/login/user_manager.h"
@@ -424,7 +425,8 @@
 }
 
 bool MediaStreamDevicesController::IsSchemeSecure() const {
-  return (request_.security_origin.SchemeIsSecure());
+  return request_.security_origin.SchemeIsSecure() ||
+      request_.security_origin.SchemeIs(extensions::kExtensionScheme);
 }
 
 bool MediaStreamDevicesController::ShouldAlwaysAllowOrigin() const {
diff --git a/chrome/browser/media/media_stream_infobar_delegate.cc b/chrome/browser/media/media_stream_infobar_delegate.cc
index ae5edbf..d421476 100644
--- a/chrome/browser/media/media_stream_infobar_delegate.cc
+++ b/chrome/browser/media/media_stream_infobar_delegate.cc
@@ -43,6 +43,13 @@
 
   InfoBarService* infobar_service =
       InfoBarService::FromWebContents(web_contents);
+  if (!infobar_service) {
+    // Deny the request if there is no place to show the infobar, e.g. when
+    // the request comes from a background extension page.
+    controller->Deny(false);
+    return false;
+  }
+
   scoped_ptr<InfoBarDelegate> infobar(
       new MediaStreamInfoBarDelegate(infobar_service, controller.Pass()));
   for (size_t i = 0; i < infobar_service->infobar_count(); ++i) {
diff --git a/chrome/browser/media/webrtc_logging_handler_host.cc b/chrome/browser/media/webrtc_logging_handler_host.cc
index 7339ccf..401e3a9 100644
--- a/chrome/browser/media/webrtc_logging_handler_host.cc
+++ b/chrome/browser/media/webrtc_logging_handler_host.cc
@@ -23,9 +23,9 @@
 #include "chrome/common/pref_names.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/gpu_data_manager.h"
 #include "content/public/browser/render_process_host.h"
 #include "gpu/config/gpu_info.h"
-#include "gpu/config/gpu_info_collector.h"
 #include "net/base/address_family.h"
 #include "net/base/net_util.h"
 #include "net/url_request/url_request_context_getter.h"
@@ -225,8 +225,7 @@
   pcb.Write(info.c_str(), info.length());
 
   // GPU
-  gpu::GPUInfo gpu_info;
-  gpu::CollectBasicGraphicsInfo(&gpu_info);
+  gpu::GPUInfo gpu_info = content::GpuDataManager::GetInstance()->GetGPUInfo();
   info = "Gpu: machine-model='" + gpu_info.machine_model +
          "', vendor-id=" + IntToString(gpu_info.gpu.vendor_id) +
          ", device-id=" + IntToString(gpu_info.gpu.device_id) +
diff --git a/chrome/browser/media_galleries/fileapi/itunes_data_provider_browsertest.cc b/chrome/browser/media_galleries/fileapi/itunes_data_provider_browsertest.cc
index 2f1742a..8d1982e 100644
--- a/chrome/browser/media_galleries/fileapi/itunes_data_provider_browsertest.cc
+++ b/chrome/browser/media_galleries/fileapi/itunes_data_provider_browsertest.cc
@@ -176,7 +176,12 @@
       return;
     std::string xml = "<plist><dict><key>Tracks</key><dict>\n";
     for (size_t i = 0; i < entries.size(); ++i) {
-      GURL location("file://localhost/" + entries[i].location.AsUTF8Unsafe());
+      std::string seperator;
+#if defined(OS_WIN)
+      seperator = "/";
+#endif
+      GURL location("file://localhost" + seperator +
+                    entries[i].location.AsUTF8Unsafe());
       std::string entry_string = base::StringPrintf(
           "<key>%" PRIuS "</key><dict>\n"
           "  <key>Track ID</key><integer>%" PRIuS "</integer>\n"
diff --git a/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc b/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc
index 102179d..3c586bd 100644
--- a/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc
+++ b/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc
@@ -115,12 +115,6 @@
                  base::PLATFORM_FILE_ERROR_SECURITY));
 }
 
-fileapi::FileSystemFileUtil* MediaFileSystemBackend::GetFileUtil(
-    fileapi::FileSystemType type) {
-  NOTREACHED();
-  return NULL;
-}
-
 fileapi::AsyncFileUtil* MediaFileSystemBackend::GetAsyncFileUtil(
     fileapi::FileSystemType type) {
   switch (type) {
diff --git a/chrome/browser/media_galleries/fileapi/media_file_system_backend.h b/chrome/browser/media_galleries/fileapi/media_file_system_backend.h
index c14695e..06475e0 100644
--- a/chrome/browser/media_galleries/fileapi/media_file_system_backend.h
+++ b/chrome/browser/media_galleries/fileapi/media_file_system_backend.h
@@ -39,8 +39,6 @@
       fileapi::FileSystemType type,
       fileapi::OpenFileSystemMode mode,
       const OpenFileSystemCallback& callback) OVERRIDE;
-  virtual fileapi::FileSystemFileUtil* GetFileUtil(
-      fileapi::FileSystemType type) OVERRIDE;
   virtual fileapi::AsyncFileUtil* GetAsyncFileUtil(
       fileapi::FileSystemType type) OVERRIDE;
   virtual fileapi::CopyOrMoveFileValidatorFactory*
diff --git a/chrome/browser/memory_details.cc b/chrome/browser/memory_details.cc
index c589ef2..643e211 100644
--- a/chrome/browser/memory_details.cc
+++ b/chrome/browser/memory_details.cc
@@ -501,8 +501,8 @@
 
 #if defined(OS_CHROMEOS)
 void MemoryDetails::UpdateSwapHistograms() {
-  UMA_HISTOGRAM_BOOLEAN("Memory.Swap.HaveSwapped", swap_data_.num_writes > 0);
-  if (swap_data_.num_writes == 0)
+  UMA_HISTOGRAM_BOOLEAN("Memory.Swap.HaveSwapped", swap_info_.num_writes > 0);
+  if (swap_info_.num_writes == 0)
     return;
 
   // Only record swap info when any swaps have happened, to give us more
@@ -575,25 +575,25 @@
   UMA_HISTOGRAM_MEMORY_MB("Memory.Swap.Total", total_sample);
 
   UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.CompressedDataSize",
-                              swap_data_.compr_data_size / (1024 * 1024),
+                              swap_info_.compr_data_size / (1024 * 1024),
                               1, 4096, 50);
   UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.OriginalDataSize",
-                              swap_data_.orig_data_size / (1024 * 1024),
+                              swap_info_.orig_data_size / (1024 * 1024),
                               1, 4096, 50);
   UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.MemUsedTotal",
-                              swap_data_.mem_used_total / (1024 * 1024),
+                              swap_info_.mem_used_total / (1024 * 1024),
                               1, 4096, 50);
   UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.NumReads",
-                              swap_data_.num_reads,
+                              swap_info_.num_reads,
                               1, 100000000, 100);
   UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.NumWrites",
-                              swap_data_.num_writes,
+                              swap_info_.num_writes,
                               1, 100000000, 100);
 
-  if (swap_data_.orig_data_size > 0 && swap_data_.compr_data_size > 0) {
+  if (swap_info_.orig_data_size > 0 && swap_info_.compr_data_size > 0) {
     UMA_HISTOGRAM_CUSTOM_COUNTS(
         "Memory.Swap.CompressionRatio",
-        swap_data_.orig_data_size / swap_data_.compr_data_size,
+        swap_info_.orig_data_size / swap_info_.compr_data_size,
         1, 20, 20);
   }
 }
diff --git a/chrome/browser/memory_details.h b/chrome/browser/memory_details.h
index cfbd68e..fb2ebb3 100644
--- a/chrome/browser/memory_details.h
+++ b/chrome/browser/memory_details.h
@@ -87,24 +87,6 @@
 class ProcessInfoSnapshot;
 #endif
 
-#if defined(OS_CHROMEOS)
-struct SwapData {
-  SwapData()
-      : num_reads(0),
-        num_writes(0),
-        compr_data_size(0),
-        orig_data_size(0),
-        mem_used_total(0) {
-  }
-
-  uint64 num_reads;
-  uint64 num_writes;
-  uint64 compr_data_size;
-  uint64 orig_data_size;
-  uint64 mem_used_total;
-};
-#endif
-
 // MemoryDetails fetches memory details about current running browsers.
 // Because this data can only be fetched asynchronously, callers use
 // this class via a callback.
@@ -206,7 +188,7 @@
   UserMetricsMode user_metrics_mode_;
 
 #if defined(OS_CHROMEOS)
-  SwapData swap_data_;
+  base::SwapInfo swap_info_;
 #endif
 
   DISALLOW_COPY_AND_ASSIGN(MemoryDetails);
diff --git a/chrome/browser/memory_details_linux.cc b/chrome/browser/memory_details_linux.cc
index ec149da..03f000e 100644
--- a/chrome/browser/memory_details_linux.cc
+++ b/chrome/browser/memory_details_linux.cc
@@ -163,41 +163,6 @@
   return children;
 }
 
-#if defined(OS_CHROMEOS)
-static uint64 ReadFileToUint64(const base::FilePath file) {
-  std::string file_as_string;
-  if (!file_util::ReadFileToString(file, &file_as_string))
-    return 0;
-  TrimWhitespaceASCII(file_as_string, TRIM_ALL, &file_as_string);
-  uint64 file_as_uint64 = 0;
-  if (!base::StringToUint64(file_as_string, &file_as_uint64))
-    return 0;
-  return file_as_uint64;
-}
-
-static void GetSwapData(SwapData* swap_data) {
-  base::FilePath zram_path("/sys/block/zram0");
-  uint64 orig_data_size = ReadFileToUint64(zram_path.Append("orig_data_size"));
-  if (orig_data_size <= 4096) {
-    // A single page is compressed at startup, and has a high compression
-    // ratio. We ignore this as it doesn't indicate any real swapping.
-    swap_data->orig_data_size = 0;
-    swap_data->num_reads = 0;
-    swap_data->num_writes = 0;
-    swap_data->compr_data_size = 0;
-    swap_data->mem_used_total = 0;
-    return;
-  }
-  swap_data->orig_data_size = orig_data_size;
-  swap_data->num_reads = ReadFileToUint64(zram_path.Append("num_reads"));
-  swap_data->num_writes = ReadFileToUint64(zram_path.Append("num_writes"));
-  swap_data->compr_data_size =
-      ReadFileToUint64(zram_path.Append("compr_data_size"));
-  swap_data->mem_used_total =
-      ReadFileToUint64(zram_path.Append("mem_used_total"));
-}
-#endif
-
 void MemoryDetails::CollectProcessData(
     const std::vector<ProcessMemoryInformation>& child_info) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
@@ -269,7 +234,7 @@
   }
 
 #if defined(OS_CHROMEOS)
-  GetSwapData(&swap_data_);
+  base::GetSwapInfo(&swap_info_);
 #endif
 
   // Finally return to the browser thread.
diff --git a/chrome/browser/metrics/metrics_log.cc b/chrome/browser/metrics/metrics_log.cc
index bebdad2..7a0f210 100644
--- a/chrome/browser/metrics/metrics_log.cc
+++ b/chrome/browser/metrics/metrics_log.cc
@@ -13,7 +13,6 @@
 #include "base/cpu.h"
 #include "base/lazy_instance.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/perftimer.h"
 #include "base/prefs/pref_registry_simple.h"
 #include "base/prefs/pref_service.h"
 #include "base/profiler/alternate_timer.h"
@@ -21,6 +20,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/sys_info.h"
+#include "base/test/perftimer.h"
 #include "base/third_party/nspr/prtime.h"
 #include "base/time/time.h"
 #include "base/tracked_objects.h"
@@ -150,8 +150,8 @@
       return OmniboxEventProto::NEW_TAB_PAGE;
     case AutocompleteInput::BLANK:
       return OmniboxEventProto::BLANK;
-    case AutocompleteInput::HOMEPAGE:
-      return OmniboxEventProto::HOMEPAGE;
+    case AutocompleteInput::HOME_PAGE:
+      return OmniboxEventProto::HOME_PAGE;
     case AutocompleteInput::OTHER:
       return OmniboxEventProto::OTHER;
     case AutocompleteInput::SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT:
diff --git a/chrome/browser/metrics/metrics_service.cc b/chrome/browser/metrics/metrics_service.cc
index dfbcbdf..b356a00 100644
--- a/chrome/browser/metrics/metrics_service.cc
+++ b/chrome/browser/metrics/metrics_service.cc
@@ -196,11 +196,11 @@
 #include "chrome/common/chrome_result_codes.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/metrics/caching_permuted_entropy_provider.h"
-#include "chrome/common/metrics/entropy_provider.h"
 #include "chrome/common/metrics/metrics_log_manager.h"
 #include "chrome/common/net/test_server_locations.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/render_messages.h"
+#include "components/variations/entropy_provider.h"
 #include "content/public/browser/child_process_data.h"
 #include "content/public/browser/histogram_fetcher.h"
 #include "content/public/browser/load_notification_details.h"
diff --git a/chrome/browser/metrics/variations/variations_http_header_provider.cc b/chrome/browser/metrics/variations/variations_http_header_provider.cc
index 7be3775..10f2173 100644
--- a/chrome/browser/metrics/variations/variations_http_header_provider.cc
+++ b/chrome/browser/metrics/variations/variations_http_header_provider.cc
@@ -10,7 +10,6 @@
 #include "base/strings/string_util.h"
 #include "chrome/browser/google/google_util.h"
 #include "chrome/common/metrics/proto/chrome_experiments.pb.h"
-#include "chrome/common/metrics/variations/variations_associated_data.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/http/http_request_headers.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/metrics/variations/variations_http_header_provider.h b/chrome/browser/metrics/variations/variations_http_header_provider.h
index 06b8c69..b432572 100644
--- a/chrome/browser/metrics/variations/variations_http_header_provider.h
+++ b/chrome/browser/metrics/variations/variations_http_header_provider.h
@@ -12,7 +12,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/metrics/field_trial.h"
 #include "base/synchronization/lock.h"
-#include "chrome/common/metrics/variations/variations_associated_data.h"
+#include "components/variations/variations_associated_data.h"
 
 namespace content {
 class ResourceContext;
diff --git a/chrome/browser/metrics/variations/variations_seed_processor.cc b/chrome/browser/metrics/variations/variations_seed_processor.cc
deleted file mode 100644
index 3600a7e..0000000
--- a/chrome/browser/metrics/variations/variations_seed_processor.cc
+++ /dev/null
@@ -1,343 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/metrics/variations/variations_seed_processor.h"
-
-#include <map>
-#include <set>
-#include <vector>
-
-#include "base/command_line.h"
-#include "base/metrics/field_trial.h"
-#include "base/stl_util.h"
-#include "base/version.h"
-#include "chrome/browser/metrics/proto/trials_seed.pb.h"
-#include "chrome/common/metrics/variations/variations_associated_data.h"
-
-namespace chrome_variations {
-
-namespace {
-
-Study_Platform GetCurrentPlatform() {
-#if defined(OS_WIN)
-  return Study_Platform_PLATFORM_WINDOWS;
-#elif defined(OS_IOS)
-  return Study_Platform_PLATFORM_IOS;
-#elif defined(OS_MACOSX)
-  return Study_Platform_PLATFORM_MAC;
-#elif defined(OS_CHROMEOS)
-  return Study_Platform_PLATFORM_CHROMEOS;
-#elif defined(OS_ANDROID)
-  return Study_Platform_PLATFORM_ANDROID;
-#elif defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS)
-  // Default BSD and SOLARIS to Linux to not break those builds, although these
-  // platforms are not officially supported by Chrome.
-  return Study_Platform_PLATFORM_LINUX;
-#else
-#error Unknown platform
-#endif
-}
-
-// Converts |date_time| in Study date format to base::Time.
-base::Time ConvertStudyDateToBaseTime(int64 date_time) {
-  return base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(date_time);
-}
-
-}  // namespace
-
-VariationsSeedProcessor::VariationsSeedProcessor() {
-}
-
-VariationsSeedProcessor::~VariationsSeedProcessor() {
-}
-
-void VariationsSeedProcessor::CreateTrialsFromSeed(
-    const TrialsSeed& seed,
-    const std::string& locale,
-    const base::Time& reference_date,
-    const base::Version& version,
-    Study_Channel channel) {
-  DCHECK(version.IsValid());
-
-  // Add expired studies (in a disabled state) only after all the non-expired
-  // studies have been added (and do not add an expired study if a corresponding
-  // non-expired study got added). This way, if there's both an expired and a
-  // non-expired study that applies, the non-expired study takes priority.
-  std::set<std::string> created_studies;
-  std::vector<const Study*> expired_studies;
-
-  for (int i = 0; i < seed.study_size(); ++i) {
-    const Study& study = seed.study(i);
-    if (!ShouldAddStudy(study, locale, reference_date, version, channel))
-      continue;
-
-    if (IsStudyExpired(study, reference_date)) {
-      expired_studies.push_back(&study);
-    } else {
-      CreateTrialFromStudy(study, false);
-      created_studies.insert(study.name());
-    }
-  }
-
-  for (size_t i = 0; i < expired_studies.size(); ++i) {
-    if (!ContainsKey(created_studies, expired_studies[i]->name()))
-      CreateTrialFromStudy(*expired_studies[i], true);
-  }
-}
-
-bool VariationsSeedProcessor::CheckStudyChannel(const Study_Filter& filter,
-                                                Study_Channel channel) {
-  // An empty channel list matches all channels.
-  if (filter.channel_size() == 0)
-    return true;
-
-  for (int i = 0; i < filter.channel_size(); ++i) {
-    if (filter.channel(i) == channel)
-      return true;
-  }
-  return false;
-}
-
-bool VariationsSeedProcessor::CheckStudyLocale(
-    const Study_Filter& filter,
-    const std::string& locale) {
-  // An empty locale list matches all locales.
-  if (filter.locale_size() == 0)
-    return true;
-
-  for (int i = 0; i < filter.locale_size(); ++i) {
-    if (filter.locale(i) == locale)
-      return true;
-  }
-  return false;
-}
-
-bool VariationsSeedProcessor::CheckStudyPlatform(
-    const Study_Filter& filter,
-    Study_Platform platform) {
-  // An empty platform list matches all platforms.
-  if (filter.platform_size() == 0)
-    return true;
-
-  for (int i = 0; i < filter.platform_size(); ++i) {
-    if (filter.platform(i) == platform)
-      return true;
-  }
-  return false;
-}
-
-bool VariationsSeedProcessor::CheckStudyStartDate(
-    const Study_Filter& filter,
-    const base::Time& date_time) {
-  if (filter.has_start_date()) {
-    const base::Time start_date =
-        ConvertStudyDateToBaseTime(filter.start_date());
-    return date_time >= start_date;
-  }
-
-  return true;
-}
-
-bool VariationsSeedProcessor::CheckStudyVersion(
-    const Study_Filter& filter,
-    const base::Version& version) {
-  if (filter.has_min_version()) {
-    if (version.CompareToWildcardString(filter.min_version()) < 0)
-      return false;
-  }
-
-  if (filter.has_max_version()) {
-    if (version.CompareToWildcardString(filter.max_version()) > 0)
-      return false;
-  }
-
-  return true;
-}
-
-void VariationsSeedProcessor::CreateTrialFromStudy(const Study& study,
-                                                   bool is_expired) {
-  base::FieldTrial::Probability total_probability = 0;
-  if (!ValidateStudyAndComputeTotalProbability(study, &total_probability))
-    return;
-
-  // Check if any experiments need to be forced due to a command line
-  // flag. Force the first experiment with an existing flag.
-  CommandLine* command_line = CommandLine::ForCurrentProcess();
-  for (int i = 0; i < study.experiment_size(); ++i) {
-    const Study_Experiment& experiment = study.experiment(i);
-    if (experiment.has_forcing_flag() &&
-        command_line->HasSwitch(experiment.forcing_flag())) {
-      base::FieldTrialList::CreateFieldTrial(study.name(), experiment.name());
-      DVLOG(1) << "Trial " << study.name() << " forced by flag: "
-               << experiment.forcing_flag();
-      return;
-    }
-  }
-
-  uint32 randomization_seed = 0;
-  base::FieldTrial::RandomizationType randomization_type =
-      base::FieldTrial::SESSION_RANDOMIZED;
-  if (study.has_consistency() &&
-      study.consistency() == Study_Consistency_PERMANENT) {
-    randomization_type = base::FieldTrial::ONE_TIME_RANDOMIZED;
-    if (study.has_randomization_seed())
-      randomization_seed = study.randomization_seed();
-  }
-
-  // The trial is created without specifying an expiration date because the
-  // expiration check in field_trial.cc is based on the build date. Instead,
-  // the expiration check using |reference_date| is done explicitly below.
-  scoped_refptr<base::FieldTrial> trial(
-      base::FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
-          study.name(), total_probability, study.default_experiment_name(),
-          base::FieldTrialList::kNoExpirationYear, 1, 1, randomization_type,
-          randomization_seed, NULL));
-
-  for (int i = 0; i < study.experiment_size(); ++i) {
-    const Study_Experiment& experiment = study.experiment(i);
-
-    std::map<std::string, std::string> params;
-    for (int j = 0; j < experiment.param_size(); j++) {
-      if (experiment.param(j).has_name() && experiment.param(j).has_value())
-        params[experiment.param(j).name()] = experiment.param(j).value();
-    }
-    if (!params.empty())
-      AssociateVariationParams(study.name(), experiment.name(), params);
-
-    // Groups with flags can't be selected randomly, so we don't add them to
-    // the field trial.
-    if (experiment.has_forcing_flag())
-      continue;
-
-    if (experiment.name() != study.default_experiment_name())
-      trial->AppendGroup(experiment.name(), experiment.probability_weight());
-
-    if (experiment.has_google_web_experiment_id()) {
-      const VariationID variation_id =
-          static_cast<VariationID>(experiment.google_web_experiment_id());
-      AssociateGoogleVariationIDForce(GOOGLE_WEB_PROPERTIES,
-                                      study.name(),
-                                      experiment.name(),
-                                      variation_id);
-    }
-    if (experiment.has_google_update_experiment_id()) {
-      const VariationID variation_id =
-          static_cast<VariationID>(experiment.google_update_experiment_id());
-      AssociateGoogleVariationIDForce(GOOGLE_UPDATE_SERVICE,
-                                      study.name(),
-                                      experiment.name(),
-                                      variation_id);
-    }
-  }
-
-  trial->SetForced();
-  if (is_expired)
-    trial->Disable();
-}
-
-bool VariationsSeedProcessor::IsStudyExpired(const Study& study,
-                                       const base::Time& date_time) {
-  if (study.has_expiry_date()) {
-    const base::Time expiry_date =
-        ConvertStudyDateToBaseTime(study.expiry_date());
-    return date_time >= expiry_date;
-  }
-
-  return false;
-}
-
-bool VariationsSeedProcessor::ShouldAddStudy(
-    const Study& study,
-    const std::string& locale,
-    const base::Time& reference_date,
-    const base::Version& version,
-    Study_Channel channel) {
-  if (study.has_filter()) {
-    if (!CheckStudyChannel(study.filter(), channel)) {
-      DVLOG(1) << "Filtered out study " << study.name() << " due to channel.";
-      return false;
-    }
-
-    if (!CheckStudyLocale(study.filter(), locale)) {
-      DVLOG(1) << "Filtered out study " << study.name() << " due to locale.";
-      return false;
-    }
-
-    if (!CheckStudyPlatform(study.filter(), GetCurrentPlatform())) {
-      DVLOG(1) << "Filtered out study " << study.name() << " due to platform.";
-      return false;
-    }
-
-    if (!CheckStudyVersion(study.filter(), version)) {
-      DVLOG(1) << "Filtered out study " << study.name() << " due to version.";
-      return false;
-    }
-
-    if (!CheckStudyStartDate(study.filter(), reference_date)) {
-      DVLOG(1) << "Filtered out study " << study.name() <<
-                  " due to start date.";
-      return false;
-    }
-  }
-
-  DVLOG(1) << "Kept study " << study.name() << ".";
-  return true;
-}
-
-bool VariationsSeedProcessor::ValidateStudyAndComputeTotalProbability(
-    const Study& study,
-    base::FieldTrial::Probability* total_probability) {
-  // At the moment, a missing default_experiment_name makes the study invalid.
-  if (study.default_experiment_name().empty()) {
-    DVLOG(1) << study.name() << " has no default experiment defined.";
-    return false;
-  }
-  if (study.filter().has_min_version() &&
-      !Version::IsValidWildcardString(study.filter().min_version())) {
-    DVLOG(1) << study.name() << " has invalid min version: "
-             << study.filter().min_version();
-    return false;
-  }
-  if (study.filter().has_max_version() &&
-      !Version::IsValidWildcardString(study.filter().max_version())) {
-    DVLOG(1) << study.name() << " has invalid max version: "
-             << study.filter().max_version();
-    return false;
-  }
-
-  const std::string& default_group_name = study.default_experiment_name();
-  base::FieldTrial::Probability divisor = 0;
-
-  bool found_default_group = false;
-  std::set<std::string> experiment_names;
-  for (int i = 0; i < study.experiment_size(); ++i) {
-    if (study.experiment(i).name().empty()) {
-      DVLOG(1) << study.name() << " is missing experiment " << i << " name";
-      return false;
-    }
-    if (!experiment_names.insert(study.experiment(i).name()).second) {
-      DVLOG(1) << study.name() << " has a repeated experiment name "
-               << study.experiment(i).name();
-      return false;
-    }
-
-    if (!study.experiment(i).has_forcing_flag())
-      divisor += study.experiment(i).probability_weight();
-    if (study.experiment(i).name() == default_group_name)
-      found_default_group = true;
-  }
-
-  if (!found_default_group) {
-    DVLOG(1) << study.name() << " is missing default experiment in its "
-             << "experiment list";
-    // The default group was not found in the list of groups. This study is not
-    // valid.
-    return false;
-  }
-
-  *total_probability = divisor;
-  return true;
-}
-
-}  // namespace chrome_variations
diff --git a/chrome/browser/metrics/variations/variations_seed_processor.h b/chrome/browser/metrics/variations/variations_seed_processor.h
deleted file mode 100644
index 074d1af..0000000
--- a/chrome/browser/metrics/variations/variations_seed_processor.h
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_METRICS_VARIATIONS_VARIATIONS_SEED_PROCESSOR_H_
-#define CHROME_BROWSER_METRICS_VARIATIONS_VARIATIONS_SEED_PROCESSOR_H_
-
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "base/gtest_prod_util.h"
-#include "base/metrics/field_trial.h"
-#include "base/time/time.h"
-#include "base/version.h"
-#include "chrome/browser/metrics/proto/study.pb.h"
-#include "chrome/browser/metrics/proto/trials_seed.pb.h"
-
-namespace chrome_variations {
-
-// Helper class to instantiate field trials from a variations seed.
-class VariationsSeedProcessor {
- public:
-  VariationsSeedProcessor();
-  virtual ~VariationsSeedProcessor();
-
-  // Creates field trials from the specified variations |seed|, based on the
-  // specified configuration (locale, current date, version and channel).
-  void CreateTrialsFromSeed(const TrialsSeed& seed,
-                            const std::string& locale,
-                            const base::Time& reference_date,
-                            const base::Version& version,
-                            Study_Channel channel);
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, CheckStudyChannel);
-  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, CheckStudyLocale);
-  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, CheckStudyPlatform);
-  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, CheckStudyStartDate);
-  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, CheckStudyVersion);
-  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest,
-                           CheckStudyVersionWildcards);
-  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, ForceGroupWithFlag1);
-  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, ForceGroupWithFlag2);
-  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest,
-                           ForceGroup_ChooseFirstGroupWithFlag);
-  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest,
-                           ForceGroup_DontChooseGroupWithFlag);
-  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, IsStudyExpired);
-  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, ValidateStudy);
-  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, VariationParams);
-
-  // Checks whether a study is applicable for the given |channel| per |filter|.
-  bool CheckStudyChannel(const Study_Filter& filter, Study_Channel channel);
-
-  // Checks whether a study is applicable for the given |locale| per |filter|.
-  bool CheckStudyLocale(const Study_Filter& filter, const std::string& locale);
-
-  // Checks whether a study is applicable for the given |platform| per |filter|.
-  bool CheckStudyPlatform(const Study_Filter& filter, Study_Platform platform);
-
-  // Checks whether a study is applicable for the given date/time per |filter|.
-  bool CheckStudyStartDate(const Study_Filter& filter,
-                           const base::Time& date_time);
-
-  // Checks whether a study is applicable for the given version per |filter|.
-  bool CheckStudyVersion(const Study_Filter& filter,
-                         const base::Version& version);
-
-  // Creates and registers a field trial from the |study| data. Disables the
-  // trial if |is_expired| is true.
-  void CreateTrialFromStudy(const Study& study, bool is_expired);
-
-  // Checks whether |study| is expired using the given date/time.
-  bool IsStudyExpired(const Study& study, const base::Time& date_time);
-
-  // Returns whether |study| should be disabled according to its restriction
-  // parameters. Uses |version_info| for min / max version checks,
-  // |reference_date| for the start date check and |channel| for channel
-  // checks.
-  bool ShouldAddStudy(const Study& study,
-                      const std::string& locale,
-                      const base::Time& reference_date,
-                      const base::Version& version,
-                      Study_Channel channel);
-
-  // Validates the sanity of |study| and computes the total probability.
-  bool ValidateStudyAndComputeTotalProbability(
-      const Study& study,
-      base::FieldTrial::Probability* total_probability);
-
-  DISALLOW_COPY_AND_ASSIGN(VariationsSeedProcessor);
-};
-
-}  // namespace chrome_variations
-
-#endif  // CHROME_BROWSER_METRICS_VARIATIONS_VARIATIONS_SEED_PROCESSOR_H_
diff --git a/chrome/browser/metrics/variations/variations_seed_processor_unittest.cc b/chrome/browser/metrics/variations/variations_seed_processor_unittest.cc
deleted file mode 100644
index 1ab7c62..0000000
--- a/chrome/browser/metrics/variations/variations_seed_processor_unittest.cc
+++ /dev/null
@@ -1,531 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/metrics/variations/variations_seed_processor.h"
-
-#include <vector>
-
-#include "base/command_line.h"
-#include "base/strings/string_split.h"
-#include "chrome/common/metrics/variations/variations_associated_data.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace chrome_variations {
-
-namespace {
-
-// Converts |time| to Study proto format.
-int64 TimeToProtoTime(const base::Time& time) {
-  return (time - base::Time::UnixEpoch()).InSeconds();
-}
-
-// Constants for testing associating command line flags with trial groups.
-const char kFlagStudyName[] = "flag_test_trial";
-const char kFlagGroup1Name[] = "flag_group1";
-const char kFlagGroup2Name[] = "flag_group2";
-const char kNonFlagGroupName[] = "non_flag_group";
-const char kForcingFlag1[] = "flag_test1";
-const char kForcingFlag2[] = "flag_test2";
-
-// Adds an experiment to |study| with the specified |name| and |probability|.
-Study_Experiment* AddExperiment(const std::string& name, int probability,
-                                Study* study) {
-  Study_Experiment* experiment = study->add_experiment();
-  experiment->set_name(name);
-  experiment->set_probability_weight(probability);
-  return experiment;
-}
-
-// Populates |study| with test data used for testing associating command line
-// flags with trials groups. The study will contain three groups, a default
-// group that isn't associated with a flag, and two other groups, both
-// associated with different flags.
-Study CreateStudyWithFlagGroups(int default_group_probability,
-                                int flag_group1_probability,
-                                int flag_group2_probability) {
-  DCHECK_GE(default_group_probability, 0);
-  DCHECK_GE(flag_group1_probability, 0);
-  DCHECK_GE(flag_group2_probability, 0);
-  Study study;
-  study.set_name(kFlagStudyName);
-  study.set_default_experiment_name(kNonFlagGroupName);
-
-  AddExperiment(kNonFlagGroupName, default_group_probability, &study);
-  AddExperiment(kFlagGroup1Name, flag_group1_probability, &study)
-      ->set_forcing_flag(kForcingFlag1);
-  AddExperiment(kFlagGroup2Name, flag_group2_probability, &study)
-      ->set_forcing_flag(kForcingFlag2);
-
-  return study;
-}
-
-}  // namespace
-
-TEST(VariationsSeedProcessorTest, CheckStudyChannel) {
-  VariationsSeedProcessor seed_processor;
-
-  const Study_Channel channels[] = {
-    Study_Channel_CANARY,
-    Study_Channel_DEV,
-    Study_Channel_BETA,
-    Study_Channel_STABLE,
-  };
-  bool channel_added[arraysize(channels)] = { 0 };
-
-  Study_Filter filter;
-
-  // Check in the forwarded order. The loop cond is <= arraysize(channels)
-  // instead of < so that the result of adding the last channel gets checked.
-  for (size_t i = 0; i <= arraysize(channels); ++i) {
-    for (size_t j = 0; j < arraysize(channels); ++j) {
-      const bool expected = channel_added[j] || filter.channel_size() == 0;
-      const bool result = seed_processor.CheckStudyChannel(filter, channels[j]);
-      EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!";
-    }
-
-    if (i < arraysize(channels)) {
-      filter.add_channel(channels[i]);
-      channel_added[i] = true;
-    }
-  }
-
-  // Do the same check in the reverse order.
-  filter.clear_channel();
-  memset(&channel_added, 0, sizeof(channel_added));
-  for (size_t i = 0; i <= arraysize(channels); ++i) {
-    for (size_t j = 0; j < arraysize(channels); ++j) {
-      const bool expected = channel_added[j] || filter.channel_size() == 0;
-      const bool result = seed_processor.CheckStudyChannel(filter, channels[j]);
-      EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!";
-    }
-
-    if (i < arraysize(channels)) {
-      const int index = arraysize(channels) - i - 1;
-      filter.add_channel(channels[index]);
-      channel_added[index] = true;
-    }
-  }
-}
-
-TEST(VariationsSeedProcessorTest, CheckStudyLocale) {
-  VariationsSeedProcessor seed_processor;
-
-  struct {
-    const char* filter_locales;
-    bool en_us_result;
-    bool en_ca_result;
-    bool fr_result;
-  } test_cases[] = {
-    {"en-US", true, false, false},
-    {"en-US,en-CA,fr", true, true, true},
-    {"en-US,en-CA,en-GB", true, true, false},
-    {"en-GB,en-CA,en-US", true, true, false},
-    {"ja,kr,vi", false, false, false},
-    {"fr-CA", false, false, false},
-    {"", true, true, true},
-  };
-
-  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
-    std::vector<std::string> filter_locales;
-    Study_Filter filter;
-    base::SplitString(test_cases[i].filter_locales, ',', &filter_locales);
-    for (size_t j = 0; j < filter_locales.size(); ++j)
-      filter.add_locale(filter_locales[j]);
-    EXPECT_EQ(test_cases[i].en_us_result,
-              seed_processor.CheckStudyLocale(filter, "en-US"));
-    EXPECT_EQ(test_cases[i].en_ca_result,
-              seed_processor.CheckStudyLocale(filter, "en-CA"));
-    EXPECT_EQ(test_cases[i].fr_result,
-              seed_processor.CheckStudyLocale(filter, "fr"));
-  }
-}
-
-TEST(VariationsSeedProcessorTest, CheckStudyPlatform) {
-  VariationsSeedProcessor seed_processor;
-
-  const Study_Platform platforms[] = {
-    Study_Platform_PLATFORM_WINDOWS,
-    Study_Platform_PLATFORM_MAC,
-    Study_Platform_PLATFORM_LINUX,
-    Study_Platform_PLATFORM_CHROMEOS,
-    Study_Platform_PLATFORM_ANDROID,
-    Study_Platform_PLATFORM_IOS,
-  };
-  ASSERT_EQ(Study_Platform_Platform_ARRAYSIZE,
-            static_cast<int>(arraysize(platforms)));
-  bool platform_added[arraysize(platforms)] = { 0 };
-
-  Study_Filter filter;
-
-  // Check in the forwarded order. The loop cond is <= arraysize(platforms)
-  // instead of < so that the result of adding the last channel gets checked.
-  for (size_t i = 0; i <= arraysize(platforms); ++i) {
-    for (size_t j = 0; j < arraysize(platforms); ++j) {
-      const bool expected = platform_added[j] || filter.platform_size() == 0;
-      const bool result = seed_processor.CheckStudyPlatform(filter,
-                                                            platforms[j]);
-      EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!";
-    }
-
-    if (i < arraysize(platforms)) {
-      filter.add_platform(platforms[i]);
-      platform_added[i] = true;
-    }
-  }
-
-  // Do the same check in the reverse order.
-  filter.clear_platform();
-  memset(&platform_added, 0, sizeof(platform_added));
-  for (size_t i = 0; i <= arraysize(platforms); ++i) {
-    for (size_t j = 0; j < arraysize(platforms); ++j) {
-      const bool expected = platform_added[j] || filter.platform_size() == 0;
-      const bool result = seed_processor.CheckStudyPlatform(filter,
-                                                            platforms[j]);
-      EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!";
-    }
-
-    if (i < arraysize(platforms)) {
-      const int index = arraysize(platforms) - i - 1;
-      filter.add_platform(platforms[index]);
-      platform_added[index] = true;
-    }
-  }
-}
-
-TEST(VariationsSeedProcessorTest, CheckStudyStartDate) {
-  VariationsSeedProcessor seed_processor;
-
-  const base::Time now = base::Time::Now();
-  const base::TimeDelta delta = base::TimeDelta::FromHours(1);
-  const struct {
-    const base::Time start_date;
-    bool expected_result;
-  } start_test_cases[] = {
-    { now - delta, true },
-    { now, true },
-    { now + delta, false },
-  };
-
-  Study_Filter filter;
-
-  // Start date not set should result in true.
-  EXPECT_TRUE(seed_processor.CheckStudyStartDate(filter, now));
-
-  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(start_test_cases); ++i) {
-    filter.set_start_date(TimeToProtoTime(start_test_cases[i].start_date));
-    const bool result = seed_processor.CheckStudyStartDate(filter, now);
-    EXPECT_EQ(start_test_cases[i].expected_result, result)
-        << "Case " << i << " failed!";
-  }
-}
-
-TEST(VariationsSeedProcessorTest, CheckStudyVersion) {
-  VariationsSeedProcessor seed_processor;
-
-  const struct {
-    const char* min_version;
-    const char* version;
-    bool expected_result;
-  } min_test_cases[] = {
-    { "1.2.2", "1.2.3", true },
-    { "1.2.3", "1.2.3", true },
-    { "1.2.4", "1.2.3", false },
-    { "1.3.2", "1.2.3", false },
-    { "2.1.2", "1.2.3", false },
-    { "0.3.4", "1.2.3", true },
-    // Wildcards.
-    { "1.*", "1.2.3", true },
-    { "1.2.*", "1.2.3", true },
-    { "1.2.3.*", "1.2.3", true },
-    { "1.2.4.*", "1.2.3", false },
-    { "2.*", "1.2.3", false },
-    { "0.3.*", "1.2.3", true },
-  };
-
-  const struct {
-    const char* max_version;
-    const char* version;
-    bool expected_result;
-  } max_test_cases[] = {
-    { "1.2.2", "1.2.3", false },
-    { "1.2.3", "1.2.3", true },
-    { "1.2.4", "1.2.3", true },
-    { "2.1.1", "1.2.3", true },
-    { "2.1.1", "2.3.4", false },
-    // Wildcards
-    { "2.1.*", "2.3.4", false },
-    { "2.*", "2.3.4", true },
-    { "2.3.*", "2.3.4", true },
-    { "2.3.4.*", "2.3.4", true },
-    { "2.3.4.0.*", "2.3.4", true },
-    { "2.4.*", "2.3.4", true },
-    { "1.3.*", "2.3.4", false },
-    { "1.*", "2.3.4", false },
-  };
-
-  Study_Filter filter;
-
-  // Min/max version not set should result in true.
-  EXPECT_TRUE(seed_processor.CheckStudyVersion(filter, base::Version("1.2.3")));
-
-  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(min_test_cases); ++i) {
-    filter.set_min_version(min_test_cases[i].min_version);
-    const bool result =
-        seed_processor.CheckStudyVersion(filter,
-                                         Version(min_test_cases[i].version));
-    EXPECT_EQ(min_test_cases[i].expected_result, result) <<
-        "Min. version case " << i << " failed!";
-  }
-  filter.clear_min_version();
-
-  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(max_test_cases); ++i) {
-    filter.set_max_version(max_test_cases[i].max_version);
-    const bool result =
-        seed_processor.CheckStudyVersion(filter,
-                                         Version(max_test_cases[i].version));
-    EXPECT_EQ(max_test_cases[i].expected_result, result) <<
-        "Max version case " << i << " failed!";
-  }
-
-  // Check intersection semantics.
-  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(min_test_cases); ++i) {
-    for (size_t j = 0; j < ARRAYSIZE_UNSAFE(max_test_cases); ++j) {
-      filter.set_min_version(min_test_cases[i].min_version);
-      filter.set_max_version(max_test_cases[j].max_version);
-
-      if (!min_test_cases[i].expected_result) {
-        const bool result =
-            seed_processor.CheckStudyVersion(
-                filter, Version(min_test_cases[i].version));
-        EXPECT_FALSE(result) << "Case " << i << "," << j << " failed!";
-      }
-
-      if (!max_test_cases[j].expected_result) {
-        const bool result =
-            seed_processor.CheckStudyVersion(
-                filter, Version(max_test_cases[j].version));
-        EXPECT_FALSE(result) << "Case " << i << "," << j << " failed!";
-      }
-    }
-  }
-}
-
-// Test that the group for kForcingFlag1 is forced.
-TEST(VariationsSeedProcessorTest, ForceGroupWithFlag1) {
-  CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
-
-  base::FieldTrialList field_trial_list(NULL);
-
-  Study study = CreateStudyWithFlagGroups(100, 0, 0);
-  VariationsSeedProcessor().CreateTrialFromStudy(study, false);
-
-  EXPECT_EQ(kFlagGroup1Name,
-            base::FieldTrialList::FindFullName(kFlagStudyName));
-}
-
-// Test that the group for kForcingFlag2 is forced.
-TEST(VariationsSeedProcessorTest, ForceGroupWithFlag2) {
-  CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag2);
-
-  base::FieldTrialList field_trial_list(NULL);
-
-  Study study = CreateStudyWithFlagGroups(100, 0, 0);
-  VariationsSeedProcessor().CreateTrialFromStudy(study, false);
-
-  EXPECT_EQ(kFlagGroup2Name,
-            base::FieldTrialList::FindFullName(kFlagStudyName));
-}
-
-TEST(VariationsSeedProcessorTest, ForceGroup_ChooseFirstGroupWithFlag) {
-  // Add the flag to the command line arguments so the flag group is forced.
-  CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
-  CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag2);
-
-  base::FieldTrialList field_trial_list(NULL);
-
-  Study study = CreateStudyWithFlagGroups(100, 0, 0);
-  VariationsSeedProcessor().CreateTrialFromStudy(study, false);
-
-  EXPECT_EQ(kFlagGroup1Name,
-            base::FieldTrialList::FindFullName(kFlagStudyName));
-}
-
-TEST(VariationsSeedProcessorTest, ForceGroup_DontChooseGroupWithFlag) {
-  base::FieldTrialList field_trial_list(NULL);
-
-  // The two flag groups are given high probability, which would normally make
-  // them very likely to be chosen. They won't be chosen since flag groups are
-  // never chosen when their flag isn't present.
-  Study study = CreateStudyWithFlagGroups(1, 999, 999);
-  VariationsSeedProcessor().CreateTrialFromStudy(study, false);
-  EXPECT_EQ(kNonFlagGroupName,
-            base::FieldTrialList::FindFullName(kFlagStudyName));
-}
-
-TEST(VariationsSeedProcessorTest, IsStudyExpired) {
-  VariationsSeedProcessor seed_processor;
-
-  const base::Time now = base::Time::Now();
-  const base::TimeDelta delta = base::TimeDelta::FromHours(1);
-  const struct {
-    const base::Time expiry_date;
-    bool expected_result;
-  } expiry_test_cases[] = {
-    { now - delta, true },
-    { now, true },
-    { now + delta, false },
-  };
-
-  Study study;
-
-  // Expiry date not set should result in false.
-  EXPECT_FALSE(seed_processor.IsStudyExpired(study, now));
-
-  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(expiry_test_cases); ++i) {
-    study.set_expiry_date(TimeToProtoTime(expiry_test_cases[i].expiry_date));
-    const bool result = seed_processor.IsStudyExpired(study, now);
-    EXPECT_EQ(expiry_test_cases[i].expected_result, result)
-        << "Case " << i << " failed!";
-  }
-}
-
-TEST(VariationsSeedProcessorTest, NonExpiredStudyPrioritizedOverExpiredStudy) {
-  VariationsSeedProcessor seed_processor;
-
-  const std::string kTrialName = "A";
-  const std::string kGroup1Name = "Group1";
-
-  TrialsSeed seed;
-  Study* study1 = seed.add_study();
-  study1->set_name(kTrialName);
-  study1->set_default_experiment_name("Default");
-  AddExperiment(kGroup1Name, 100, study1);
-  AddExperiment("Default", 0, study1);
-  Study* study2 = seed.add_study();
-  *study2 = *study1;
-  ASSERT_EQ(seed.study(0).name(), seed.study(1).name());
-
-  const base::Time year_ago =
-      base::Time::Now() - base::TimeDelta::FromDays(365);
-
-  const base::Version version("20.0.0.0");
-
-  // Check that adding [expired, non-expired] activates the non-expired one.
-  ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName));
-  {
-    base::FieldTrialList field_trial_list(NULL);
-    study1->set_expiry_date(TimeToProtoTime(year_ago));
-    seed_processor.CreateTrialsFromSeed(seed, "en-CA", base::Time::Now(),
-                                        version, Study_Channel_STABLE);
-    EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
-  }
-
-  // Check that adding [non-expired, expired] activates the non-expired one.
-  ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName));
-  {
-    base::FieldTrialList field_trial_list(NULL);
-    study1->clear_expiry_date();
-    study2->set_expiry_date(TimeToProtoTime(year_ago));
-    seed_processor.CreateTrialsFromSeed(seed, "en-CA", base::Time::Now(),
-                                        version, Study_Channel_STABLE);
-    EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
-  }
-}
-
-TEST(VariationsSeedProcessorTest, ValidateStudy) {
-  VariationsSeedProcessor seed_processor;
-
-  Study study;
-  study.set_default_experiment_name("def");
-  AddExperiment("abc", 100, &study);
-  Study_Experiment* default_group = AddExperiment("def", 200, &study);
-
-  base::FieldTrial::Probability total_probability = 0;
-  bool valid = seed_processor.ValidateStudyAndComputeTotalProbability(
-      study, &total_probability);
-  EXPECT_TRUE(valid);
-  EXPECT_EQ(300, total_probability);
-
-  // Min version checks.
-  study.mutable_filter()->set_min_version("1.2.3.*");
-  valid = seed_processor.ValidateStudyAndComputeTotalProbability(
-      study, &total_probability);
-  EXPECT_TRUE(valid);
-  study.mutable_filter()->set_min_version("1.*.3");
-  valid = seed_processor.ValidateStudyAndComputeTotalProbability(
-      study, &total_probability);
-  EXPECT_FALSE(valid);
-  study.mutable_filter()->set_min_version("1.2.3");
-  valid = seed_processor.ValidateStudyAndComputeTotalProbability(
-      study, &total_probability);
-  EXPECT_TRUE(valid);
-
-  // Max version checks.
-  study.mutable_filter()->set_max_version("2.3.4.*");
-  valid = seed_processor.ValidateStudyAndComputeTotalProbability(
-      study, &total_probability);
-  EXPECT_TRUE(valid);
-  study.mutable_filter()->set_max_version("*.3");
-  valid = seed_processor.ValidateStudyAndComputeTotalProbability(
-      study, &total_probability);
-  EXPECT_FALSE(valid);
-  study.mutable_filter()->set_max_version("2.3.4");
-  valid = seed_processor.ValidateStudyAndComputeTotalProbability(
-      study, &total_probability);
-  EXPECT_TRUE(valid);
-
-  study.clear_default_experiment_name();
-  valid = seed_processor.ValidateStudyAndComputeTotalProbability(study,
-      &total_probability);
-  EXPECT_FALSE(valid);
-
-  study.set_default_experiment_name("xyz");
-  valid = seed_processor.ValidateStudyAndComputeTotalProbability(study,
-      &total_probability);
-  EXPECT_FALSE(valid);
-
-  study.set_default_experiment_name("def");
-  default_group->clear_name();
-  valid = seed_processor.ValidateStudyAndComputeTotalProbability(study,
-      &total_probability);
-  EXPECT_FALSE(valid);
-
-  default_group->set_name("def");
-  valid = seed_processor.ValidateStudyAndComputeTotalProbability(study,
-      &total_probability);
-  ASSERT_TRUE(valid);
-  Study_Experiment* repeated_group = study.add_experiment();
-  repeated_group->set_name("abc");
-  repeated_group->set_probability_weight(1);
-  valid = seed_processor.ValidateStudyAndComputeTotalProbability(study,
-      &total_probability);
-  EXPECT_FALSE(valid);
-}
-
-TEST(VariationsSeedProcessorTest, VariationParams) {
-  base::FieldTrialList field_trial_list(NULL);
-  VariationsSeedProcessor seed_processor;
-
-  Study study;
-  study.set_name("Study1");
-  study.set_default_experiment_name("B");
-
-  Study_Experiment* experiment1 = AddExperiment("A", 1, &study);
-  Study_Experiment_Param* param = experiment1->add_param();
-  param->set_name("x");
-  param->set_value("y");
-
-  Study_Experiment* experiment2 = AddExperiment("B", 0, &study);
-
-  seed_processor.CreateTrialFromStudy(study, false);
-  EXPECT_EQ("y", GetVariationParamValue("Study1", "x"));
-
-  study.set_name("Study2");
-  experiment1->set_probability_weight(0);
-  experiment2->set_probability_weight(1);
-  seed_processor.CreateTrialFromStudy(study, false);
-  EXPECT_EQ(std::string(), GetVariationParamValue("Study2", "x"));
-}
-
-}  // namespace chrome_variations
diff --git a/chrome/browser/metrics/variations/variations_service.cc b/chrome/browser/metrics/variations/variations_service.cc
index 973d009..63b579a 100644
--- a/chrome/browser/metrics/variations/variations_service.cc
+++ b/chrome/browser/metrics/variations/variations_service.cc
@@ -18,12 +18,12 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/version.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/metrics/proto/trials_seed.pb.h"
-#include "chrome/browser/metrics/variations/variations_seed_processor.h"
 #include "chrome/browser/net/network_time_tracker.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/metrics/variations/variations_util.h"
 #include "chrome/common/pref_names.h"
+#include "components/variations/proto/trials_seed.pb.h"
+#include "components/variations/variations_seed_processor.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/url_fetcher.h"
 #include "net/base/load_flags.h"
diff --git a/chrome/browser/metrics/variations/variations_service.h b/chrome/browser/metrics/variations/variations_service.h
index f87d333..973a8ba 100644
--- a/chrome/browser/metrics/variations/variations_service.h
+++ b/chrome/browser/metrics/variations/variations_service.h
@@ -12,8 +12,6 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/metrics/field_trial.h"
 #include "base/time/time.h"
-#include "chrome/browser/metrics/proto/study.pb.h"
-#include "chrome/browser/metrics/proto/trials_seed.pb.h"
 #include "chrome/browser/metrics/variations/variations_request_scheduler.h"
 #include "chrome/browser/web_resource/resource_request_allowed_notifier.h"
 #include "chrome/common/chrome_version_info.h"
@@ -29,6 +27,8 @@
 
 namespace chrome_variations {
 
+class TrialsSeed;
+
 // Used to setup field trials based on stored variations seed data, and fetch
 // new seed data from the variations server.
 class VariationsService
diff --git a/chrome/browser/metrics/variations/variations_service_unittest.cc b/chrome/browser/metrics/variations/variations_service_unittest.cc
index 8da383b..1f7990f 100644
--- a/chrome/browser/metrics/variations/variations_service_unittest.cc
+++ b/chrome/browser/metrics/variations/variations_service_unittest.cc
@@ -11,10 +11,11 @@
 #include "base/sha1.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
-#include "chrome/browser/metrics/proto/study.pb.h"
 #include "chrome/browser/web_resource/resource_request_allowed_notifier_test_util.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
+#include "components/variations/proto/study.pb.h"
+#include "components/variations/proto/trials_seed.pb.h"
 #include "content/public/test/test_browser_thread.h"
 #include "net/base/url_util.h"
 #include "net/http/http_response_headers.h"
diff --git a/chrome/browser/metro_viewer/chrome_metro_viewer_process_host_aurawin.cc b/chrome/browser/metro_viewer/chrome_metro_viewer_process_host_aurawin.cc
index ef86c41..fe8a46e 100644
--- a/chrome/browser/metro_viewer/chrome_metro_viewer_process_host_aurawin.cc
+++ b/chrome/browser/metro_viewer/chrome_metro_viewer_process_host_aurawin.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/host_desktop.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/env_vars.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/page_navigator.h"
@@ -67,6 +68,11 @@
   // TODO(cpu): At some point we only close the browser. Right now this
   // is very convenient for developing.
   DLOG(INFO) << "viewer channel error : Quitting browser";
+
+  // Unset environment variable to let breakpad know that metro process wasn't
+  // connected.
+  ::SetEnvironmentVariableA(env_vars::kMetroConnected, NULL);
+
   aura::RemoteRootWindowHostWin::Instance()->Disconnected();
   g_browser_process->ReleaseModule();
   CloseOpenAshBrowsers();
@@ -82,6 +88,13 @@
   g_browser_process->platform_part()->OnMetroViewerProcessTerminated();
 }
 
+void  ChromeMetroViewerProcessHost::OnChannelConnected(int32 /*peer_pid*/) {
+  DLOG(INFO) << "ChromeMetroViewerProcessHost::OnChannelConnected: ";
+  // Set environment variable to let breakpad know that metro process was
+  // connected.
+  ::SetEnvironmentVariableA(env_vars::kMetroConnected, "1");
+}
+
 void ChromeMetroViewerProcessHost::OnSetTargetSurface(
     gfx::NativeViewId target_surface) {
   DLOG(INFO) << __FUNCTION__ << ", target_surface = " << target_surface;
diff --git a/chrome/browser/metro_viewer/chrome_metro_viewer_process_host_aurawin.h b/chrome/browser/metro_viewer/chrome_metro_viewer_process_host_aurawin.h
index 96ff005..e108e8a 100644
--- a/chrome/browser/metro_viewer/chrome_metro_viewer_process_host_aurawin.h
+++ b/chrome/browser/metro_viewer/chrome_metro_viewer_process_host_aurawin.h
@@ -14,6 +14,8 @@
  private:
   // win8::MetroViewerProcessHost implementation
   virtual void OnChannelError() OVERRIDE;
+  // IPC::Listener implementation
+  virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;
   virtual void OnSetTargetSurface(gfx::NativeViewId target_surface) OVERRIDE;
   virtual void OnOpenURL(const string16& url) OVERRIDE;
   virtual void OnHandleSearchRequest(const string16& search_string) OVERRIDE;
diff --git a/chrome/browser/nacl_host/nacl_process_host.cc b/chrome/browser/nacl_host/nacl_process_host.cc
index 1b4243d..44641b8 100644
--- a/chrome/browser/nacl_host/nacl_process_host.cc
+++ b/chrome/browser/nacl_host/nacl_process_host.cc
@@ -263,15 +263,18 @@
 }
 
 NaClProcessHost::~NaClProcessHost() {
-  int exit_code;
-  process_->GetTerminationStatus(&exit_code);
-  std::string message =
-      base::StringPrintf("NaCl process exited with status %i (0x%x)",
-                         exit_code, exit_code);
-  if (exit_code == 0) {
-    LOG(INFO) << message;
-  } else {
-    LOG(ERROR) << message;
+  // Report exit status only if the process was successfully started.
+  if (process_->GetData().handle != base::kNullProcessHandle) {
+    int exit_code = 0;
+    process_->GetTerminationStatus(false /* known_dead */, &exit_code);
+    std::string message =
+        base::StringPrintf("NaCl process exited with status %i (0x%x)",
+                           exit_code, exit_code);
+    if (exit_code == 0) {
+      LOG(INFO) << message;
+    } else {
+      LOG(ERROR) << message;
+    }
   }
 
   if (internal_->socket_for_renderer != NACL_INVALID_HANDLE) {
@@ -662,29 +665,29 @@
 static const int kDebugStubPort = 4014;
 
 #if defined(OS_POSIX)
-SocketDescriptor NaClProcessHost::GetDebugStubSocketHandle() {
+net::SocketDescriptor NaClProcessHost::GetDebugStubSocketHandle() {
   NaClBrowser* nacl_browser = NaClBrowser::GetInstance();
-  SocketDescriptor s;
+  net::SocketDescriptor s = net::kInvalidSocket;
   // We allocate currently unused TCP port for debug stub tests. The port
   // number is passed to the test via debug stub port listener.
   if (nacl_browser->HasGdbDebugStubPortListener()) {
     int port;
     s = net::TCPListenSocket::CreateAndBindAnyPort("127.0.0.1", &port);
-    if (s != net::TCPListenSocket::kInvalidSocket) {
+    if (s != net::kInvalidSocket) {
       nacl_browser->FireGdbDebugStubPortOpened(port);
     }
   } else {
     s = net::TCPListenSocket::CreateAndBind("127.0.0.1", kDebugStubPort);
   }
-  if (s == net::TCPListenSocket::kInvalidSocket) {
+  if (s == net::kInvalidSocket) {
     LOG(ERROR) << "failed to open socket for debug stub";
-    return net::TCPListenSocket::kInvalidSocket;
+    return net::kInvalidSocket;
   }
   if (listen(s, 1)) {
     LOG(ERROR) << "listen() failed on debug stub socket";
     if (HANDLE_EINTR(close(s)) < 0)
       PLOG(ERROR) << "failed to close debug stub socket";
-    return net::TCPListenSocket::kInvalidSocket;
+    return net::kInvalidSocket;
   }
   return s;
 }
@@ -745,8 +748,8 @@
 
 #if defined(OS_POSIX)
   if (params.enable_debug_stub) {
-    SocketDescriptor server_bound_socket = GetDebugStubSocketHandle();
-    if (server_bound_socket != net::TCPListenSocket::kInvalidSocket) {
+    net::SocketDescriptor server_bound_socket = GetDebugStubSocketHandle();
+    if (server_bound_socket != net::kInvalidSocket) {
       params.debug_stub_server_bound_socket =
           nacl::FileDescriptor(server_bound_socket, true);
     }
@@ -791,7 +794,6 @@
         permissions_,
         process_->GetData().handle,
         ipc_proxy_channel_.get(),
-        nacl_host_message_filter_->GetHostResolver(),
         nacl_host_message_filter_->render_process_id(),
         render_view_id_,
         profile_directory_));
diff --git a/chrome/browser/nacl_host/nacl_process_host.h b/chrome/browser/nacl_host/nacl_process_host.h
index e802680..8a884fc 100644
--- a/chrome/browser/nacl_host/nacl_process_host.h
+++ b/chrome/browser/nacl_host/nacl_process_host.h
@@ -17,7 +17,7 @@
 #include "content/public/browser/browser_child_process_host_delegate.h"
 #include "content/public/browser/browser_child_process_host_iterator.h"
 #include "ipc/ipc_channel_handle.h"
-#include "net/socket/tcp_listen_socket.h"
+#include "net/socket/socket_descriptor.h"
 #include "ppapi/shared_impl/ppapi_permissions.h"
 #include "url/gurl.h"
 
@@ -122,7 +122,7 @@
   // Create bound TCP socket in the browser process so that the NaCl GDB debug
   // stub can use it to accept incoming connections even when the Chrome sandbox
   // is enabled.
-  SocketDescriptor GetDebugStubSocketHandle();
+  net::SocketDescriptor GetDebugStubSocketHandle();
 #endif
   bool LaunchSelLdr();
 
diff --git a/chrome/browser/nacl_host/pnacl_host.cc b/chrome/browser/nacl_host/pnacl_host.cc
index 86546bd..2c3a082 100644
--- a/chrome/browser/nacl_host/pnacl_host.cc
+++ b/chrome/browser/nacl_host/pnacl_host.cc
@@ -113,40 +113,41 @@
 
 // Create a temporary file on the blocking pool
 // static
-base::PlatformFile PnaclHost::DoCreateTemporaryFile(base::FilePath temp_dir) {
+void PnaclHost::DoCreateTemporaryFile(base::FilePath temp_dir,
+                                      TempFileCallback cb) {
   DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
 
   base::FilePath file_path;
+  base::PlatformFile file_handle(base::kInvalidPlatformFileValue);
   bool rv = temp_dir.empty()
                 ? file_util::CreateTemporaryFile(&file_path)
                 : file_util::CreateTemporaryFileInDir(temp_dir, &file_path);
   if (!rv) {
     PLOG(ERROR) << "Temp file creation failed.";
-    return base::kInvalidPlatformFileValue;
-  }
-  base::PlatformFileError error;
-  base::PlatformFile file_handle(base::CreatePlatformFile(
-      file_path,
-      base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_READ |
-          base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_TEMPORARY |
-          base::PLATFORM_FILE_DELETE_ON_CLOSE,
-      NULL,
-      &error));
+  } else {
+    base::PlatformFileError error;
+    file_handle = base::CreatePlatformFile(
+        file_path,
+        base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_READ |
+            base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_TEMPORARY |
+            base::PLATFORM_FILE_DELETE_ON_CLOSE,
+        NULL,
+        &error);
 
-  if (error != base::PLATFORM_FILE_OK) {
-    PLOG(ERROR) << "Temp file open failed: " << error;
-    return base::kInvalidPlatformFileValue;
+    if (error != base::PLATFORM_FILE_OK) {
+      PLOG(ERROR) << "Temp file open failed: " << error;
+      file_handle = base::kInvalidPlatformFileValue;
+    }
   }
-
-  return file_handle;
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE, base::Bind(cb, file_handle));
 }
 
 void PnaclHost::CreateTemporaryFile(TempFileCallback cb) {
-  if (!base::PostTaskAndReplyWithResult(
-          BrowserThread::GetBlockingPool(),
-          FROM_HERE,
-          base::Bind(&PnaclHost::DoCreateTemporaryFile, temp_dir_),
-          cb)) {
+  if (!BrowserThread::PostBlockingPoolSequencedTask(
+           "PnaclHostCreateTempFile",
+           FROM_HERE,
+           base::Bind(&PnaclHost::DoCreateTemporaryFile, temp_dir_, cb))) {
     DCHECK(thread_checker_.CalledOnValidThread());
     cb.Run(base::kInvalidPlatformFileValue);
   }
@@ -311,13 +312,13 @@
   }
 
   if (!base::PostTaskAndReplyWithResult(
-          BrowserThread::GetBlockingPool(),
-          FROM_HERE,
-          base::Bind(
-              &PnaclHost::CopyBufferToFile, pt->nexe_fd, pt->nexe_read_buffer),
-          base::Bind(&PnaclHost::OnBufferCopiedToTempFile,
-                     weak_factory_.GetWeakPtr(),
-                     entry->first))) {
+           BrowserThread::GetBlockingPool(),
+           FROM_HERE,
+           base::Bind(
+               &PnaclHost::CopyBufferToFile, pt->nexe_fd, pt->nexe_read_buffer),
+           base::Bind(&PnaclHost::OnBufferCopiedToTempFile,
+                      weak_factory_.GetWeakPtr(),
+                      entry->first))) {
     pt->callback.Run(base::kInvalidPlatformFileValue, false);
   }
 }
@@ -386,13 +387,13 @@
       !success || entry->second.is_incognito) {
     store_nexe = false;
   } else if (!base::PostTaskAndReplyWithResult(
-                 BrowserThread::GetBlockingPool(),
-                 FROM_HERE,
-                 base::Bind(&PnaclHost::CopyFileToBuffer,
-                            entry->second.nexe_fd),
-                 base::Bind(&PnaclHost::StoreTranslatedNexe,
-                            weak_factory_.GetWeakPtr(),
-                            id))) {
+                  BrowserThread::GetBlockingPool(),
+                  FROM_HERE,
+                  base::Bind(&PnaclHost::CopyFileToBuffer,
+                             entry->second.nexe_fd),
+                  base::Bind(&PnaclHost::StoreTranslatedNexe,
+                             weak_factory_.GetWeakPtr(),
+                             id))) {
     store_nexe = false;
   }
 
diff --git a/chrome/browser/nacl_host/pnacl_host.h b/chrome/browser/nacl_host/pnacl_host.h
index 3c0d2e8..b2f6984 100644
--- a/chrome/browser/nacl_host/pnacl_host.h
+++ b/chrome/browser/nacl_host/pnacl_host.h
@@ -129,7 +129,8 @@
   void InitForTest(base::FilePath temp_dir);
   void OnCacheInitialized(int net_error);
 
-  static base::PlatformFile DoCreateTemporaryFile(base::FilePath temp_dir_);
+  static void DoCreateTemporaryFile(base::FilePath temp_dir_,
+                                    TempFileCallback cb);
 
   // GetNexeFd common steps
   void SendCacheQueryAndTempFileRequest(const std::string& key,
diff --git a/chrome/browser/nacl_host/pnacl_host_unittest.cc b/chrome/browser/nacl_host/pnacl_host_unittest.cc
index e2d529d..0ad9cd5 100644
--- a/chrome/browser/nacl_host/pnacl_host_unittest.cc
+++ b/chrome/browser/nacl_host/pnacl_host_unittest.cc
@@ -195,8 +195,7 @@
   EXPECT_EQ(0U, host_->pending_translations());
 }
 
-// crbug.com/272492; flaky on all platforms.
-TEST_F(PnaclHostTest, DISABLED_OverlappedMissesAfterTempReturn) {
+TEST_F(PnaclHostTest, OverlappedMissesAfterTempReturn) {
   nacl::PnaclCacheInfo info = GetTestCacheInfo();
   GET_NEXE_FD(0, 0, false, info, false);
   FlushQueues();
@@ -216,8 +215,7 @@
   EXPECT_EQ(0U, host_->pending_translations());
 }
 
-// crbug.com/272492; flaky on all platforms.
-TEST_F(PnaclHostTest, DISABLED_OverlappedMissesBeforeTempReturn) {
+TEST_F(PnaclHostTest, OverlappedMissesBeforeTempReturn) {
   nacl::PnaclCacheInfo info = GetTestCacheInfo();
   GET_NEXE_FD(0, 0, false, info, false);
   // Send the 2nd fd request before the first one returns a temp file.
@@ -268,8 +266,7 @@
   EXPECT_EQ(0U, host_->pending_translations());
 }
 
-// crbug.com/272492; flaky on all platforms.
-TEST_F(PnaclHostTest, DISABLED_OverlappedMissesRendererClosing) {
+TEST_F(PnaclHostTest, OverlappedMissesRendererClosing) {
   nacl::PnaclCacheInfo info = GetTestCacheInfo();
   GET_NEXE_FD(0, 0, false, info, false);
   // Send the 2nd fd request from a different renderer.
@@ -307,8 +304,7 @@
   EXPECT_EQ(3, temp_callback_count_);
 }
 
-// crbug.com/272492; flaky on all platforms.
-TEST_F(PnaclHostTest, DISABLED_IncognitoOverlappedMiss) {
+TEST_F(PnaclHostTest, IncognitoOverlappedMiss) {
   nacl::PnaclCacheInfo info = GetTestCacheInfo();
   GET_NEXE_FD(0, 0, true, info, false);
   GET_NEXE_FD(0, 1, false, info, false);
@@ -332,8 +328,7 @@
   host_->RendererClosing(0);
 }
 
-// crbug.com/272492; flaky on all platforms.
-TEST_F(PnaclHostTest, DISABLED_IncognitoSecondOverlappedMiss) {
+TEST_F(PnaclHostTest, IncognitoSecondOverlappedMiss) {
   // If the non-incognito request comes first, it should
   // behave exactly like OverlappedMissBeforeTempReturn
   nacl::PnaclCacheInfo info = GetTestCacheInfo();
diff --git a/chrome/browser/net/pref_proxy_config_tracker_impl.cc b/chrome/browser/net/pref_proxy_config_tracker_impl.cc
index 5dd3e5d..fbf5e0c 100644
--- a/chrome/browser/net/pref_proxy_config_tracker_impl.cc
+++ b/chrome/browser/net/pref_proxy_config_tracker_impl.cc
@@ -79,7 +79,7 @@
   pref_config_state_ = config_state;
   pref_config_ = config;
 
-  if (!observers_.size())
+  if (!observers_.might_have_observers())
     return;
 
   // Evaluate the proxy configuration. If GetLatestProxyConfig returns
diff --git a/chrome/browser/network_time/navigation_time_helper.cc b/chrome/browser/network_time/navigation_time_helper.cc
new file mode 100644
index 0000000..fd3fafd
--- /dev/null
+++ b/chrome/browser/network_time/navigation_time_helper.cc
@@ -0,0 +1,73 @@
+// 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 "chrome/browser/network_time/navigation_time_helper.h"
+
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+
+DEFINE_WEB_CONTENTS_USER_DATA_KEY(NavigationTimeHelper);
+
+NavigationTimeHelper::NavigationTimeHelper()
+    : web_contents_(NULL) {}
+
+NavigationTimeHelper::NavigationTimeHelper(content::WebContents* web_contents)
+    : web_contents_(web_contents) {
+  registrar_.Add(this, content::NOTIFICATION_NAV_LIST_PRUNED,
+                 content::NotificationService::AllSources());
+}
+
+NavigationTimeHelper::~NavigationTimeHelper() {}
+
+base::Time NavigationTimeHelper::GetNavigationTime(
+    const content::NavigationEntry* entry) {
+  const void* entry_key = entry;
+  base::Time local_time = entry->GetTimestamp();
+
+  NavigationTimeCache::iterator iter = time_cache_.find(entry_key);
+  if (iter == time_cache_.end() || iter->second.local_time != local_time) {
+    // Calculate navigation time for new entry or existing entry that has new
+    // navigation.
+    base::Time navigation_time = GetNetworkTime(local_time);
+
+    if (iter == time_cache_.end()) {
+      time_cache_.insert(std::make_pair(entry_key,
+                                        NavigationTimeInfo(local_time,
+                                                           navigation_time)));
+    } else {
+      iter->second = NavigationTimeInfo(local_time, navigation_time);
+    }
+
+    return navigation_time;
+  } else {
+    // Use the navigation time calculated before for unchanged entry.
+    return iter->second.network_time;
+  }
+}
+
+void NavigationTimeHelper::Observe(
+    int type,
+    const content::NotificationSource& source,
+    const content::NotificationDetails& details) {
+  DCHECK_EQ(type, content::NOTIFICATION_NAV_LIST_PRUNED);
+
+  // Drop pruned entries from cache.
+  const content::NavigationController& controller =
+      web_contents_->GetController();
+  NavigationTimeCache new_cache;
+  for (int i = 0; i < controller.GetEntryCount(); ++i) {
+    const void* entry_key = controller.GetEntryAtIndex(i);
+    NavigationTimeCache::const_iterator iter = time_cache_.find(entry_key);
+    if (iter != time_cache_.end()) {
+      new_cache.insert(std::make_pair(entry_key,  iter->second));
+    }
+  }
+  time_cache_.swap(new_cache);
+}
+
+base::Time NavigationTimeHelper::GetNetworkTime(base::Time local_time) {
+  // TODO(haitaol): calculate network time based on local_time.
+  return local_time;
+}
diff --git a/chrome/browser/network_time/navigation_time_helper.h b/chrome/browser/network_time/navigation_time_helper.h
new file mode 100644
index 0000000..a597180
--- /dev/null
+++ b/chrome/browser/network_time/navigation_time_helper.h
@@ -0,0 +1,62 @@
+// 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 CHROME_BROWSER_NETWORK_TIME_NAVIGATION_TIME_HELPER_H_
+#define CHROME_BROWSER_NETWORK_TIME_NAVIGATION_TIME_HELPER_H_
+
+#include <map>
+
+#include "base/time/time.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace content {
+class WebContents;
+}
+
+// NavigationTimeHelper returns the navigation time in network time for
+// given navigation entry.
+class NavigationTimeHelper
+    : public content::NotificationObserver,
+      public content::WebContentsUserData<NavigationTimeHelper> {
+ public:
+  virtual ~NavigationTimeHelper();
+
+  base::Time GetNavigationTime(const content::NavigationEntry* entry);
+
+ protected:
+  // Tests only.
+  NavigationTimeHelper();
+
+  // Return network time for given |local_time|.
+  virtual base::Time GetNetworkTime(base::Time local_time);
+
+ private:
+  explicit NavigationTimeHelper(content::WebContents* web_contents);
+  friend class content::WebContentsUserData<NavigationTimeHelper>;
+
+  // content::NotificationObserver implementation.
+  virtual void Observe(int type,
+                       const content::NotificationSource& source,
+                       const content::NotificationDetails& details) OVERRIDE;
+
+  content::NotificationRegistrar registrar_;
+
+  content::WebContents* web_contents_;
+
+  // Map from navigation entries to the navigation times calculated for them.
+  struct NavigationTimeInfo {
+    NavigationTimeInfo(base::Time local_time, base::Time network_time)
+        : local_time(local_time), network_time(network_time) {}
+    base::Time local_time;
+    base::Time network_time;
+  };
+  typedef std::map<const void*, NavigationTimeInfo> NavigationTimeCache;
+  NavigationTimeCache time_cache_;
+
+  DISALLOW_COPY_AND_ASSIGN(NavigationTimeHelper);
+};
+
+#endif  // CHROME_BROWSER_NETWORK_TIME_NAVIGATION_TIME_HELPER_H_
diff --git a/chrome/browser/network_time/navigation_time_helper_unittest.cc b/chrome/browser/network_time/navigation_time_helper_unittest.cc
new file mode 100644
index 0000000..ab7ac8e
--- /dev/null
+++ b/chrome/browser/network_time/navigation_time_helper_unittest.cc
@@ -0,0 +1,50 @@
+// 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 "chrome/browser/network_time/navigation_time_helper.h"
+
+#include "content/public/browser/navigation_entry.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class TestNavigationTimeHelper : public NavigationTimeHelper {
+ public:
+  void SetDelta(base::TimeDelta delta) {
+    delta_ = delta;
+  }
+  virtual base::Time GetNetworkTime(base::Time t) OVERRIDE {
+    return t + delta_;
+  }
+
+ private:
+  base::TimeDelta delta_;
+};
+
+TEST(NavigationTimeHelperTest, QueryNavigationTime) {
+  TestNavigationTimeHelper time_helper;
+  time_helper.SetDelta(base::TimeDelta::FromHours(1));
+
+  scoped_ptr<content::NavigationEntry> entry1(
+      content::NavigationEntry::Create());
+  entry1->SetTimestamp(base::Time::Now());
+
+  EXPECT_EQ(entry1->GetTimestamp() + base::TimeDelta::FromHours(1),
+            time_helper.GetNavigationTime(entry1.get()));
+
+  // Adjusting delta shouldn't affect navigation time of unchanged entry.
+  time_helper.SetDelta(base::TimeDelta::FromHours(2));
+  EXPECT_EQ(entry1->GetTimestamp() + base::TimeDelta::FromHours(1),
+            time_helper.GetNavigationTime(entry1.get()));
+
+  // New delta is applied to new entry even if it has same local time.
+  scoped_ptr<content::NavigationEntry> entry2(
+      content::NavigationEntry::Create(*entry1));
+  EXPECT_EQ(entry2->GetTimestamp() + base::TimeDelta::FromHours(2),
+            time_helper.GetNavigationTime(entry2.get()));
+
+  // New delta is applied if existing entry has new navigation.
+  entry1->SetTimestamp(
+      entry1->GetTimestamp() + base::TimeDelta::FromSeconds(1));
+  EXPECT_EQ(entry1->GetTimestamp() + base::TimeDelta::FromHours(2),
+            time_helper.GetNavigationTime(entry1.get()));
+}
diff --git a/chrome/browser/notifications/desktop_notification_service.cc b/chrome/browser/notifications/desktop_notification_service.cc
index 45eb30c..814949f 100644
--- a/chrome/browser/notifications/desktop_notification_service.cc
+++ b/chrome/browser/notifications/desktop_notification_service.cc
@@ -48,6 +48,10 @@
 #include "ui/message_center/notifier_settings.h"
 #include "ui/webui/web_ui_util.h"
 
+#if defined(OS_CHROMEOS)
+#include "ash/system/system_notifier.h"
+#endif
+
 using content::BrowserThread;
 using content::RenderViewHost;
 using content::WebContents;
@@ -566,9 +570,15 @@
     case NotifierId::WEB_PAGE:
       return GetContentSetting(notifier_id.url) == CONTENT_SETTING_ALLOW;
     case NotifierId::SYSTEM_COMPONENT:
+#if defined(OS_CHROMEOS)
       return disabled_system_component_ids_.find(
-          message_center::ToString(notifier_id.system_component_type)) ==
-          disabled_system_component_ids_.end();
+          ash::SystemComponentTypeToString(
+              static_cast<ash::AshSystemComponentNotifierType>(
+                  notifier_id.system_component_type)))
+          == disabled_system_component_ids_.end();
+#else
+      return false;
+#endif
     case NotifierId::SYNCED_NOTIFICATION_SERVICE:
       return enabled_sync_notifier_ids_.find(notifier_id.id) !=
           enabled_sync_notifier_ids_.end();
@@ -593,10 +603,15 @@
       id.reset(new base::StringValue(notifier_id.id));
       break;
     case NotifierId::SYSTEM_COMPONENT:
+#if defined(OS_CHROMEOS)
       pref_name = prefs::kMessageCenterDisabledSystemComponentIds;
       add_new_item = !enabled;
-      id.reset(new base::StringValue(
-          message_center::ToString(notifier_id.system_component_type)));
+      id.reset(new base::StringValue(ash::SystemComponentTypeToString(
+          static_cast<ash::AshSystemComponentNotifierType>(
+              notifier_id.system_component_type))));
+#else
+      return;
+#endif
       break;
     case NotifierId::SYNCED_NOTIFICATION_SERVICE:
       pref_name = prefs::kMessageCenterEnabledSyncNotifierIds;
diff --git a/chrome/browser/notifications/message_center_notification_manager.cc b/chrome/browser/notifications/message_center_notification_manager.cc
index 2054a7f..86b741f 100644
--- a/chrome/browser/notifications/message_center_notification_manager.cc
+++ b/chrome/browser/notifications/message_center_notification_manager.cc
@@ -24,6 +24,7 @@
 #include "content/public/browser/user_metrics.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/url_constants.h"
+#include "ui/gfx/image/image_skia.h"
 #include "ui/message_center/message_center_style.h"
 #include "ui/message_center/message_center_tray.h"
 #include "ui/message_center/notifier_settings.h"
@@ -218,8 +219,6 @@
       // Now pass a copy to message center.
       scoped_ptr<message_center::Notification> message_center_notification(
           make_scoped_ptr(new message_center::Notification(notification)));
-      message_center_notification->set_extension_id(
-          new_notification->GetExtensionId());
       message_center_->UpdateNotification(old_id,
                                           message_center_notification.Pass());
 
@@ -233,47 +232,6 @@
 ////////////////////////////////////////////////////////////////////////////////
 // MessageCenter::Delegate
 
-void MessageCenterNotificationManager::DisableExtension(
-    const std::string& notification_id) {
-  ProfileNotification* profile_notification =
-      FindProfileNotification(notification_id);
-  if (!profile_notification)
-    return;
-
-  std::string extension_id = profile_notification->GetExtensionId();
-  DCHECK(!extension_id.empty());  // or UI should not have enabled the command.
-  DesktopNotificationService* service =
-      DesktopNotificationServiceFactory::GetForProfile(
-          profile_notification->profile());
-  message_center::NotifierId notifier_id(
-      message_center::NotifierId::APPLICATION, extension_id);
-  service->SetNotifierEnabled(notifier_id, false);
-}
-
-void MessageCenterNotificationManager::DisableNotificationsFromSource(
-    const std::string& notification_id) {
-  ProfileNotification* profile_notification =
-      FindProfileNotification(notification_id);
-  if (!profile_notification)
-    return;
-
-  // UI should not have enabled the command if there is no valid source.
-  DCHECK(profile_notification->notification().origin_url().is_valid());
-  DesktopNotificationService* service =
-      DesktopNotificationServiceFactory::GetForProfile(
-          profile_notification->profile());
-  if (profile_notification->notification().origin_url().scheme() ==
-      chrome::kChromeUIScheme) {
-    const std::string name =
-        profile_notification->notification().origin_url().host();
-    message_center::NotifierId notifier_id(
-        message_center::ParseSystemComponentName(name));
-    service->SetNotifierEnabled(notifier_id, false);
-  } else {
-    service->DenyPermission(profile_notification->notification().origin_url());
-  }
-}
-
 void MessageCenterNotificationManager::ShowSettings(
     const std::string& notification_id) {
   // The per-message-center Settings button passes an empty string.
@@ -541,8 +499,6 @@
   // Create the copy for message center, and ensure the extension ID is correct.
   scoped_ptr<message_center::Notification> message_center_notification(
       new message_center::Notification(notification));
-  message_center_notification->set_extension_id(
-      profile_notification->GetExtensionId());
   message_center_->AddNotification(message_center_notification.Pass());
 
   profile_notification->StartDownloads();
diff --git a/chrome/browser/notifications/message_center_notification_manager.h b/chrome/browser/notifications/message_center_notification_manager.h
index 4589645..cb5dc79 100644
--- a/chrome/browser/notifications/message_center_notification_manager.h
+++ b/chrome/browser/notifications/message_center_notification_manager.h
@@ -58,9 +58,6 @@
                                   Profile* profile) OVERRIDE;
 
   // MessageCenter::Delegate
-  virtual void DisableExtension(const std::string& notification_id) OVERRIDE;
-  virtual void DisableNotificationsFromSource(
-      const std::string& notification_id) OVERRIDE;
   virtual void ShowSettings(const std::string& notification_id) OVERRIDE;
 
   // MessageCenterObserver
diff --git a/chrome/browser/notifications/message_center_notifications_browsertest.cc b/chrome/browser/notifications/message_center_notifications_browsertest.cc
index 03f8012..84cde47 100644
--- a/chrome/browser/notifications/message_center_notifications_browsertest.cc
+++ b/chrome/browser/notifications/message_center_notifications_browsertest.cc
@@ -140,6 +140,9 @@
                         ASCIIToUTF16("message"),
                         gfx::Image(),
                         WebKit::WebTextDirectionDefault,
+                        message_center::NotifierId(
+                            message_center::NotifierId::APPLICATION,
+                            "extension_id"),
                         UTF8ToUTF16("chrome-test://testing/"),
                         UTF8ToUTF16("REPLACE-ME"),
                         data,
diff --git a/chrome/browser/notifications/message_center_settings_controller.cc b/chrome/browser/notifications/message_center_settings_controller.cc
index 66175a7..e1d017a 100644
--- a/chrome/browser/notifications/message_center_settings_controller.cc
+++ b/chrome/browser/notifications/message_center_settings_controller.cc
@@ -35,6 +35,10 @@
 #include "ui/gfx/image/image.h"
 #include "ui/message_center/message_center_style.h"
 
+#if defined(OS_CHROMEOS)
+#include "ash/system/system_notifier.h"
+#endif
+
 using message_center::Notifier;
 using message_center::NotifierId;
 
@@ -258,7 +262,7 @@
 #if defined(OS_CHROMEOS)
   const string16 screenshot_name =
       l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_NOTIFIER_SCREENSHOT_NAME);
-  NotifierId screenshot_notifier_id(NotifierId::SCREENSHOT);
+  NotifierId screenshot_notifier_id(ash::NOTIFIER_SCREENSHOT);
   Notifier* const screenshot_notifier = new Notifier(
       screenshot_notifier_id,
       screenshot_name,
diff --git a/chrome/browser/notifications/notification.cc b/chrome/browser/notifications/notification.cc
index 315e12b..61110a1 100644
--- a/chrome/browser/notifications/notification.cc
+++ b/chrome/browser/notifications/notification.cc
@@ -20,7 +20,7 @@
                                    EmptyString16(),
                                    gfx::Image(),
                                    display_source,
-                                   origin_url.spec(),
+                                   message_center::NotifierId(origin_url),
                                    message_center::RichNotificationData(),
                                    delegate),
       origin_url_(origin_url),
@@ -43,7 +43,7 @@
                                    body,
                                    gfx::Image(),
                                    display_source,
-                                   origin_url.spec(),
+                                   message_center::NotifierId(origin_url),
                                    message_center::RichNotificationData(),
                                    delegate),
       origin_url_(origin_url),
@@ -63,6 +63,7 @@
     const string16& body,
     const gfx::Image& icon,
     WebKit::WebTextDirection dir,
+    const message_center::NotifierId& notifier_id,
     const string16& display_source,
     const string16& replace_id,
     const message_center::RichNotificationData& rich_notification_data,
@@ -73,7 +74,7 @@
                                    body,
                                    icon,
                                    display_source,
-                                   origin_url.spec(),
+                                   notifier_id,
                                    rich_notification_data,
                                    delegate),
       origin_url_(origin_url),
@@ -105,7 +106,7 @@
                                    body,
                                    icon,
                                    display_source,
-                                   origin_url.spec(),
+                                   message_center::NotifierId(origin_url),
                                    message_center::RichNotificationData(),
                                    delegate),
       origin_url_(origin_url),
diff --git a/chrome/browser/notifications/notification.h b/chrome/browser/notifications/notification.h
index 3e4685c..94850a1 100644
--- a/chrome/browser/notifications/notification.h
+++ b/chrome/browser/notifications/notification.h
@@ -60,6 +60,7 @@
       const string16& body,
       const gfx::Image& icon,
       WebKit::WebTextDirection dir,
+      const message_center::NotifierId& notifier_id,
       const string16& display_source,
       const string16& replace_id,
       const message_center::RichNotificationData& rich_notification_data,
diff --git a/chrome/browser/notifications/sync_notifier/chrome_notifier_service.cc b/chrome/browser/notifications/sync_notifier/chrome_notifier_service.cc
index ae1347e..5a4afc8 100644
--- a/chrome/browser/notifications/sync_notifier/chrome_notifier_service.cc
+++ b/chrome/browser/notifications/sync_notifier/chrome_notifier_service.cc
@@ -260,8 +260,7 @@
   bool is_well_formed_read_notification =
       (static_cast<SyncedNotification::ReadState>(
           specifics.coalesced_notification().read_state()) ==
-       SyncedNotification::kRead &&
-       specifics.coalesced_notification().has_render_info());
+       SyncedNotification::kRead);
   bool is_well_formed_dismissed_notification =
       (static_cast<SyncedNotification::ReadState>(
           specifics.coalesced_notification().read_state()) ==
diff --git a/chrome/browser/notifications/sync_notifier/synced_notification.cc b/chrome/browser/notifications/sync_notifier/synced_notification.cc
index abffe93..00e8df2 100644
--- a/chrome/browser/notifications/sync_notifier/synced_notification.cc
+++ b/chrome/browser/notifications/sync_notifier/synced_notification.cc
@@ -292,6 +292,7 @@
                                  notification_text,
                                  icon_bitmap,
                                  WebKit::WebTextDirectionDefault,
+                                 message_center::NotifierId(GetOriginUrl()),
                                  display_source,
                                  replace_key,
                                  rich_notification_data,
diff --git a/chrome/browser/omnibox/omnibox_field_trial.cc b/chrome/browser/omnibox/omnibox_field_trial.cc
index 0376e73..7f53c60 100644
--- a/chrome/browser/omnibox/omnibox_field_trial.cc
+++ b/chrome/browser/omnibox/omnibox_field_trial.cc
@@ -13,9 +13,9 @@
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/autocomplete/autocomplete_input.h"
 #include "chrome/browser/search/search.h"
-#include "chrome/common/metrics/metrics_util.h"
 #include "chrome/common/metrics/variations/variation_ids.h"
 #include "chrome/common/metrics/variations/variations_util.h"
+#include "components/variations/metrics_util.h"
 
 namespace {
 
diff --git a/chrome/browser/omnibox/omnibox_field_trial_unittest.cc b/chrome/browser/omnibox/omnibox_field_trial_unittest.cc
index 9439bd2..82522b6 100644
--- a/chrome/browser/omnibox/omnibox_field_trial_unittest.cc
+++ b/chrome/browser/omnibox/omnibox_field_trial_unittest.cc
@@ -12,8 +12,8 @@
 #include "chrome/browser/autocomplete/autocomplete_input.h"
 #include "chrome/browser/search/search.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/metrics/entropy_provider.h"
 #include "chrome/common/metrics/variations/variations_util.h"
+#include "components/variations/entropy_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 class OmniboxFieldTrialTest : public testing::Test {
@@ -179,7 +179,7 @@
   VerifyDemotion(demotions_by_type, AutocompleteMatchType::HISTORY_URL, 0.5);
   VerifyDemotion(demotions_by_type, AutocompleteMatchType::HISTORY_TITLE, 0.0);
   OmniboxFieldTrial::GetDemotionsByType(
-      AutocompleteInput::HOMEPAGE, &demotions_by_type);
+      AutocompleteInput::HOME_PAGE, &demotions_by_type);
   ASSERT_EQ(1u, demotions_by_type.size());
   VerifyDemotion(demotions_by_type, AutocompleteMatchType::NAVSUGGEST, 1.0);
   OmniboxFieldTrial::GetDemotionsByType(
@@ -196,7 +196,7 @@
     std::map<std::string, std::string> params;
     // Rule 1 has some exact matches and fallbacks at every level.
     params["rule1:1:0"] = "rule1-1-0-value";  // NEW_TAB_PAGE
-    params["rule1:3:0"] = "rule1-3-0-value";  // HOMEPAGE
+    params["rule1:3:0"] = "rule1-3-0-value";  // HOME_PAGE
     params["rule1:4:1"] = "rule1-4-1-value";  // OTHER
     params["rule1:4:*"] = "rule1-4-*-value";  // OTHER
     params["rule1:*:1"] = "rule1-*-1-value";  // global
@@ -225,38 +225,38 @@
   ExpectRuleValue("rule1-1-0-value",
                   "rule1", AutocompleteInput::NEW_TAB_PAGE);  // exact match
   ExpectRuleValue("rule1-*-*-value",
-                  "rule1", AutocompleteInput::BLANK);     // fallback to global
+                  "rule1", AutocompleteInput::BLANK);      // fallback to global
   ExpectRuleValue("rule1-3-0-value",
-                  "rule1", AutocompleteInput::HOMEPAGE);  // exact match
+                  "rule1", AutocompleteInput::HOME_PAGE);  // exact match
   ExpectRuleValue("rule1-4-*-value",
-                  "rule1", AutocompleteInput::OTHER);     // partial fallback
+                  "rule1", AutocompleteInput::OTHER);      // partial fallback
   ExpectRuleValue("rule1-*-*-value",
                   "rule1",
-                  AutocompleteInput::                     // fallback to global
+                  AutocompleteInput::                      // fallback to global
                       SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT);
   // Tests for rule 2.
   ExpectRuleValue("rule2-*-0-value",
-                  "rule2", AutocompleteInput::HOMEPAGE);  // partial fallback
+                  "rule2", AutocompleteInput::HOME_PAGE);  // partial fallback
   ExpectRuleValue("rule2-*-0-value",
-                  "rule2", AutocompleteInput::OTHER);     // partial fallback
+                  "rule2", AutocompleteInput::OTHER);      // partial fallback
 
   // Tests for rule 3.
   ExpectRuleValue("rule3-*-*-value",
-                  "rule3", AutocompleteInput::HOMEPAGE);  // fallback to global
+                  "rule3", AutocompleteInput::HOME_PAGE);  // fallback to global
   ExpectRuleValue("rule3-*-*-value",
-                  "rule3", AutocompleteInput::OTHER);     // fallback to global
+                  "rule3", AutocompleteInput::OTHER);      // fallback to global
 
   // Tests for rule 4.
   ExpectRuleValue("",
-                  "rule4", AutocompleteInput::BLANK);     // no global fallback
+                  "rule4", AutocompleteInput::BLANK);      // no global fallback
   ExpectRuleValue("",
-                  "rule4", AutocompleteInput::HOMEPAGE);  // no global fallback
+                  "rule4", AutocompleteInput::HOME_PAGE);  // no global fallback
   ExpectRuleValue("rule4-4-0-value",
-                  "rule4", AutocompleteInput::OTHER);     // exact match
+                  "rule4", AutocompleteInput::OTHER);      // exact match
 
   // Tests for rule 5 (a missing rule).
   ExpectRuleValue("",
-                  "rule5", AutocompleteInput::OTHER);     // no rule at all
+                  "rule5", AutocompleteInput::OTHER);      // no rule at all
 
   // Now change the Instant Extended state and run analogous tests.
   // Instant Extended only works on non-mobile platforms.
@@ -268,34 +268,34 @@
   // Tests with Instant Extended enabled.
   // Tests for rule 1.
   ExpectRuleValue("rule1-4-1-value",
-                  "rule1", AutocompleteInput::OTHER);     // exact match
+                  "rule1", AutocompleteInput::OTHER);      // exact match
   ExpectRuleValue("rule1-*-1-value",
-                  "rule1", AutocompleteInput::BLANK);     // partial fallback
+                  "rule1", AutocompleteInput::BLANK);      // partial fallback
   ExpectRuleValue("rule1-*-1-value",
                   "rule1",
-                  AutocompleteInput::NEW_TAB_PAGE);       // partial fallback
+                  AutocompleteInput::NEW_TAB_PAGE);        // partial fallback
 
   // Tests for rule 2.
   ExpectRuleValue("rule2-1-*-value",
                   "rule2",
-                  AutocompleteInput::NEW_TAB_PAGE);       // partial fallback
+                  AutocompleteInput::NEW_TAB_PAGE);        // partial fallback
   ExpectRuleValue("rule2-*-*-value",
-                  "rule2", AutocompleteInput::OTHER);     // global fallback
+                  "rule2", AutocompleteInput::OTHER);      // global fallback
 
   // Tests for rule 3.
   ExpectRuleValue("rule3-*-*-value",
-                  "rule3", AutocompleteInput::HOMEPAGE);  // global fallback
+                  "rule3", AutocompleteInput::HOME_PAGE);  // global fallback
   ExpectRuleValue("rule3-*-*-value",
-                  "rule3", AutocompleteInput::OTHER);     // global fallback
+                  "rule3", AutocompleteInput::OTHER);      // global fallback
 
   // Tests for rule 4.
   ExpectRuleValue("",
-                  "rule4", AutocompleteInput::BLANK);     // no global fallback
+                  "rule4", AutocompleteInput::BLANK);      // no global fallback
   ExpectRuleValue("",
-                  "rule4", AutocompleteInput::HOMEPAGE);  // no global fallback
+                  "rule4", AutocompleteInput::HOME_PAGE);  // no global fallback
 
   // Tests for rule 5 (a missing rule).
   ExpectRuleValue("",
-                  "rule5", AutocompleteInput::OTHER);     // no rule at all
+                  "rule5", AutocompleteInput::OTHER);      // no rule at all
 #endif  // !defined(OS_IOS) && !defined(OS_ANDROID)
 }
diff --git a/chrome/browser/platform_util_chromeos.cc b/chrome/browser/platform_util_chromeos.cc
index e803c69..94e8ea7 100644
--- a/chrome/browser/platform_util_chromeos.cc
+++ b/chrome/browser/platform_util_chromeos.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/platform_util.h"
 
 #include "base/bind.h"
-#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
+#include "chrome/browser/chromeos/extensions/file_manager/open_util.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_navigator.h"
diff --git a/chrome/browser/plugins/plugin_installer.cc b/chrome/browser/plugins/plugin_installer.cc
index 099711e..6801d13 100644
--- a/chrome/browser/plugins/plugin_installer.cc
+++ b/chrome/browser/plugins/plugin_installer.cc
@@ -67,7 +67,8 @@
 }  // namespace
 
 PluginInstaller::PluginInstaller()
-    : state_(INSTALLER_STATE_IDLE) {
+    : state_(INSTALLER_STATE_IDLE),
+      strong_observer_count_(0) {
 }
 
 PluginInstaller::~PluginInstaller() {
@@ -109,12 +110,14 @@
 }
 
 void PluginInstaller::AddObserver(PluginInstallerObserver* observer) {
+  strong_observer_count_++;
   observers_.AddObserver(observer);
 }
 
 void PluginInstaller::RemoveObserver(PluginInstallerObserver* observer) {
+  strong_observer_count_--;
   observers_.RemoveObserver(observer);
-  if (observers_.size() == weak_observers_.size()) {
+  if (strong_observer_count_ == 0) {
     FOR_EACH_OBSERVER(WeakPluginInstallerObserver, weak_observers_,
                       OnlyWeakObserversLeft());
   }
diff --git a/chrome/browser/plugins/plugin_installer.h b/chrome/browser/plugins/plugin_installer.h
index 978fe1a..abcfe6f 100644
--- a/chrome/browser/plugins/plugin_installer.h
+++ b/chrome/browser/plugins/plugin_installer.h
@@ -58,6 +58,7 @@
 
   InstallerState state_;
   ObserverList<PluginInstallerObserver> observers_;
+  int strong_observer_count_;
   ObserverList<WeakPluginInstallerObserver> weak_observers_;
 
   DISALLOW_COPY_AND_ASSIGN(PluginInstaller);
diff --git a/chrome/browser/policy/browser_policy_connector.cc b/chrome/browser/policy/browser_policy_connector.cc
index f8e8805..2949dac 100644
--- a/chrome/browser/policy/browser_policy_connector.cc
+++ b/chrome/browser/policy/browser_policy_connector.cc
@@ -62,7 +62,6 @@
 #include "chrome/browser/chromeos/policy/device_status_collector.h"
 #include "chrome/browser/chromeos/policy/enterprise_install_attributes.h"
 #include "chrome/browser/chromeos/policy/network_configuration_updater.h"
-#include "chrome/browser/chromeos/policy/network_configuration_updater_impl.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/settings/cros_settings_provider.h"
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
@@ -231,11 +230,13 @@
   policy_statistics_collector_->Initialize();
 
 #if defined(OS_CHROMEOS)
-  network_configuration_updater_.reset(new NetworkConfigurationUpdaterImpl(
-      GetPolicyService(),
-      chromeos::NetworkHandler::Get()->managed_network_configuration_handler(),
-      scoped_ptr<chromeos::onc::CertificateImporter>(
-          new chromeos::onc::CertificateImporterImpl)));
+  network_configuration_updater_ =
+      NetworkConfigurationUpdater::CreateForDevicePolicy(
+          scoped_ptr<chromeos::onc::CertificateImporter>(
+              new chromeos::onc::CertificateImporterImpl),
+          GetPolicyService(),
+          chromeos::NetworkHandler::Get()
+              ->managed_network_configuration_handler());
 #endif
 
   is_initialized_ = true;
@@ -363,11 +364,6 @@
   return app_pack_updater_.get();
 }
 
-net::CertTrustAnchorProvider*
-BrowserPolicyConnector::GetCertTrustAnchorProvider() {
-  return network_configuration_updater()->GetCertTrustAnchorProvider();
-}
-
 void BrowserPolicyConnector::SetUserPolicyDelegate(
     ConfigurationPolicyProvider* user_policy_provider) {
   global_user_cloud_policy_provider_.SetDelegate(user_policy_provider);
diff --git a/chrome/browser/policy/browser_policy_connector.h b/chrome/browser/policy/browser_policy_connector.h
index 9f37bdb..82be663 100644
--- a/chrome/browser/policy/browser_policy_connector.h
+++ b/chrome/browser/policy/browser_policy_connector.h
@@ -23,7 +23,6 @@
 class PrefService;
 
 namespace net {
-class CertTrustAnchorProvider;
 class URLRequestContextGetter;
 }
 
@@ -111,12 +110,6 @@
 #if defined(OS_CHROMEOS)
   AppPackUpdater* GetAppPackUpdater();
 
-  NetworkConfigurationUpdater* network_configuration_updater() {
-    return network_configuration_updater_.get();
-  }
-
-  net::CertTrustAnchorProvider* GetCertTrustAnchorProvider();
-
   DeviceCloudPolicyManagerChromeOS* GetDeviceCloudPolicyManager() {
     return device_cloud_policy_manager_.get();
   }
diff --git a/chrome/browser/policy/cloud/cloud_policy_client_registration_helper.cc b/chrome/browser/policy/cloud/cloud_policy_client_registration_helper.cc
index 0f0e7be..104ee85 100644
--- a/chrome/browser/policy/cloud/cloud_policy_client_registration_helper.cc
+++ b/chrome/browser/policy/cloud/cloud_policy_client_registration_helper.cc
@@ -11,10 +11,10 @@
 #include "base/logging.h"
 #include "base/time/time.h"
 #include "base/values.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "google_apis/gaia/gaia_constants.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/signin/android_profile_oauth2_token_service.h"
diff --git a/chrome/browser/policy/configuration_policy_handler_list.cc b/chrome/browser/policy/configuration_policy_handler_list.cc
index 39eec6d..fbfa513 100644
--- a/chrome/browser/policy/configuration_policy_handler_list.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list.cc
@@ -27,6 +27,10 @@
 #include "chrome/browser/policy/configuration_policy_handler_android.h"
 #endif  // defined(OS_ANDROID)
 
+#if !defined(OS_MACOSX)
+#include "apps/pref_names.h"
+#endif
+
 namespace policy {
 
 namespace {
@@ -346,6 +350,15 @@
     prefs::kManagedUserCreationAllowed,
     Value::TYPE_BOOLEAN },
 
+#if !defined(OS_MACOSX)
+  { key::kFullscreenAllowed,
+    prefs::kFullscreenAllowed,
+    Value::TYPE_BOOLEAN },
+  { key::kFullscreenAllowed,
+    apps::prefs::kAppFullscreenAllowed,
+    Value::TYPE_BOOLEAN },
+#endif  // !defined(OS_MACOSX)
+
 #if defined(OS_CHROMEOS)
   { key::kChromeOsLockOnIdleSuspend,
     prefs::kEnableScreenLock,
diff --git a/chrome/browser/policy/configuration_policy_pref_store.cc b/chrome/browser/policy/configuration_policy_pref_store.cc
index 63ca6a9..3e4dd16 100644
--- a/chrome/browser/policy/configuration_policy_pref_store.cc
+++ b/chrome/browser/policy/configuration_policy_pref_store.cc
@@ -55,8 +55,8 @@
   observers_.RemoveObserver(observer);
 }
 
-size_t ConfigurationPolicyPrefStore::NumberOfObservers() const {
-  return observers_.size();
+bool ConfigurationPolicyPrefStore::HasObservers() const {
+  return observers_.might_have_observers();
 }
 
 bool ConfigurationPolicyPrefStore::IsInitializationComplete() const {
diff --git a/chrome/browser/policy/configuration_policy_pref_store.h b/chrome/browser/policy/configuration_policy_pref_store.h
index 62a6850..9aaacd4 100644
--- a/chrome/browser/policy/configuration_policy_pref_store.h
+++ b/chrome/browser/policy/configuration_policy_pref_store.h
@@ -39,7 +39,7 @@
   // PrefStore methods:
   virtual void AddObserver(PrefStore::Observer* observer) OVERRIDE;
   virtual void RemoveObserver(PrefStore::Observer* observer) OVERRIDE;
-  virtual size_t NumberOfObservers() const OVERRIDE;
+  virtual bool HasObservers() const OVERRIDE;
   virtual bool IsInitializationComplete() const OVERRIDE;
   virtual bool GetValue(const std::string& key,
                         const Value** result) const OVERRIDE;
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index cefed43..93c6043 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -1794,6 +1794,23 @@
   CheckURLIsBlocked(browser(), file_path2.c_str());
 }
 
+#if !defined(OS_MACOSX)
+IN_PROC_BROWSER_TEST_F(PolicyTest, FullscreenAllowed) {
+  PolicyMap policies;
+  policies.Set(key::kFullscreenAllowed,
+               POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               base::Value::CreateBooleanValue(false), NULL);
+  UpdateProviderPolicy(policies);
+
+  BrowserWindow* browser_window = browser()->window();
+  ASSERT_TRUE(browser_window);
+
+  EXPECT_FALSE(browser_window->IsFullscreen());
+  chrome::ToggleFullscreenMode(browser());
+  EXPECT_FALSE(browser_window->IsFullscreen());
+}
+#endif
+
 #if defined(OS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(PolicyTest, DisableScreenshotsFile) {
   int screenshot_count = CountScreenshots();
diff --git a/chrome/browser/policy/policy_service_impl.cc b/chrome/browser/policy/policy_service_impl.cc
index 6af7605..2cfdcbb 100644
--- a/chrome/browser/policy/policy_service_impl.cc
+++ b/chrome/browser/policy/policy_service_impl.cc
@@ -56,7 +56,7 @@
     return;
   }
   it->second->RemoveObserver(observer);
-  if (it->second->size() == 0) {
+  if (!it->second->might_have_observers()) {
     delete it->second;
     observers_.erase(it);
   }
diff --git a/chrome/browser/policy/profile_policy_connector.cc b/chrome/browser/policy/profile_policy_connector.cc
index 9ea5c81..311d743 100644
--- a/chrome/browser/policy/profile_policy_connector.cc
+++ b/chrome/browser/policy/profile_policy_connector.cc
@@ -23,12 +23,14 @@
 #include "chrome/browser/chromeos/login/user_manager.h"
 #include "chrome/browser/chromeos/policy/device_local_account_policy_provider.h"
 #include "chrome/browser/chromeos/policy/login_profile_policy_provider.h"
-#include "chrome/browser/chromeos/policy/network_configuration_updater.h"
 #include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
 #include "chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.h"
+#include "chrome/browser/chromeos/policy/user_network_configuration_updater.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/policy/policy_service.h"
 #include "chrome/common/pref_names.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/network/onc/onc_certificate_importer_impl.h"
 #else
 #include "chrome/browser/policy/cloud/user_cloud_policy_manager.h"
 #include "chrome/browser/policy/cloud/user_cloud_policy_manager_factory.h"
@@ -37,11 +39,12 @@
 namespace policy {
 
 ProfilePolicyConnector::ProfilePolicyConnector(Profile* profile)
-    : profile_(profile),
+    :
 #if defined(OS_CHROMEOS)
       is_primary_user_(false),
+      weak_ptr_factory_(this),
 #endif
-      weak_ptr_factory_(this) {}
+      profile_(profile) {}
 
 ProfilePolicyConnector::~ProfilePolicyConnector() {}
 
@@ -111,9 +114,16 @@
       connector->SetUserPolicyDelegate(special_user_policy_provider_.get());
 
     // A reference to |user| is stored by the NetworkConfigurationUpdater until
-    // UnsetUserPolicyService during Shutdown is called.
-    connector->network_configuration_updater()->SetUserPolicyService(
-        allow_trusted_certs_from_policy, user, policy_service());
+    // the Updater is destructed during Shutdown.
+    network_configuration_updater_ =
+        UserNetworkConfigurationUpdater::CreateForUserPolicy(
+            allow_trusted_certs_from_policy,
+            *user,
+            scoped_ptr<chromeos::onc::CertificateImporter>(
+                new chromeos::onc::CertificateImporterImpl),
+            policy_service(),
+            chromeos::NetworkHandler::Get()
+                ->managed_network_configuration_handler());
   }
 #endif
 }
@@ -124,12 +134,9 @@
 
 void ProfilePolicyConnector::Shutdown() {
 #if defined(OS_CHROMEOS)
-  if (is_primary_user_) {
-    BrowserPolicyConnector* connector =
-        g_browser_process->browser_policy_connector();
-    connector->SetUserPolicyDelegate(NULL);
-    connector->network_configuration_updater()->UnsetUserPolicyService();
-  }
+  if (is_primary_user_)
+    g_browser_process->browser_policy_connector()->SetUserPolicyDelegate(NULL);
+  network_configuration_updater_.reset();
   if (special_user_policy_provider_)
     special_user_policy_provider_->Shutdown();
 #endif
@@ -140,6 +147,26 @@
 #endif
 }
 
+#if defined(OS_CHROMEOS)
+void ProfilePolicyConnector::SetPolicyCertVerifier(
+    PolicyCertVerifier* cert_verifier) {
+  if (network_configuration_updater_)
+    network_configuration_updater_->SetPolicyCertVerifier(cert_verifier);
+}
+
+base::Closure ProfilePolicyConnector::GetPolicyCertTrustedCallback() {
+  return base::Bind(&ProfilePolicyConnector::SetUsedPolicyCertificatesOnce,
+                    weak_ptr_factory_.GetWeakPtr());
+}
+
+void ProfilePolicyConnector::GetWebTrustedCertificates(
+    net::CertificateList* certs) const {
+  certs->clear();
+  if (network_configuration_updater_)
+    network_configuration_updater_->GetWebTrustedCertificates(certs);
+}
+#endif
+
 bool ProfilePolicyConnector::UsedPolicyCertificates() {
 #if defined(OS_CHROMEOS)
   return profile_->GetPrefs()->GetBoolean(prefs::kUsedPolicyCertificatesOnce);
@@ -149,6 +176,10 @@
 }
 
 #if defined(OS_CHROMEOS)
+void ProfilePolicyConnector::SetUsedPolicyCertificatesOnce() {
+  profile_->GetPrefs()->SetBoolean(prefs::kUsedPolicyCertificatesOnce, true);
+}
+
 void ProfilePolicyConnector::InitializeDeviceLocalAccountPolicyProvider(
     const std::string& username) {
   BrowserPolicyConnector* connector =
diff --git a/chrome/browser/policy/profile_policy_connector.h b/chrome/browser/policy/profile_policy_connector.h
index 1fc3531..209a053 100644
--- a/chrome/browser/policy/profile_policy_connector.h
+++ b/chrome/browser/policy/profile_policy_connector.h
@@ -6,8 +6,11 @@
 #define CHROME_BROWSER_POLICY_PROFILE_POLICY_CONNECTOR_H_
 
 #include <string>
+#include <vector>
 
 #include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "components/browser_context_keyed_service/browser_context_keyed_service.h"
@@ -22,11 +25,22 @@
 class SequencedTaskRunner;
 }
 
+namespace net {
+class CertTrustAnchorProvider;
+}
+
+namespace net {
+class X509Certificate;
+typedef std::vector<scoped_refptr<X509Certificate> > CertificateList;
+}
+
 namespace policy {
 
 class ConfigurationPolicyProvider;
 class ManagedModePolicyProvider;
+class UserNetworkConfigurationUpdater;
 class PolicyService;
+class PolicyCertVerifier;
 
 // A BrowserContextKeyedService that creates and manages the per-Profile policy
 // components.
@@ -53,6 +67,25 @@
   }
 #endif
 
+#if defined(OS_CHROMEOS)
+  // Sets the CertVerifier on which the current list of Web trusted server and
+  // CA certificates will be set. Policy updates will trigger further calls to
+  // |cert_verifier| later. |cert_verifier| must be valid until
+  // SetPolicyCertVerifier is called again (with another CertVerifier or NULL)
+  // or until this Connector is destructed. |cert_verifier|'s methods are only
+  // called on the IO thread. This function must be called on the UI thread.
+  void SetPolicyCertVerifier(PolicyCertVerifier* cert_verifier);
+
+  // Returns a callback that should be called if a policy installed certificate
+  // was trusted for the associated profile. The closure can be safely used (on
+  // the UI thread) even after this Connector is destructed.
+  base::Closure GetPolicyCertTrustedCallback();
+
+  // Sets |certs| to the list of Web trusted server and CA certificates from the
+  // last received ONC user policy.
+  void GetWebTrustedCertificates(net::CertificateList* certs) const;
+#endif
+
   // Returns true if |profile()| has used certificates installed via policy
   // to establish a secure connection before. This means that it may have
   // cached content from an untrusted source.
@@ -62,11 +95,10 @@
 #if defined(ENABLE_CONFIGURATION_POLICY)
 
 #if defined(OS_CHROMEOS)
+  void SetUsedPolicyCertificatesOnce();
   void InitializeDeviceLocalAccountPolicyProvider(const std::string& username);
 #endif
 
-  Profile* profile_;
-
 #if defined(OS_CHROMEOS)
   // Some of the user policy configuration affects browser global state, and
   // can only come from one Profile. |is_primary_user_| is true if this
@@ -76,13 +108,17 @@
   bool is_primary_user_;
 
   scoped_ptr<ConfigurationPolicyProvider> special_user_policy_provider_;
+  scoped_ptr<UserNetworkConfigurationUpdater> network_configuration_updater_;
+
+  base::WeakPtrFactory<ProfilePolicyConnector> weak_ptr_factory_;
 #endif
 
+  Profile* profile_;
+
 #if defined(ENABLE_MANAGED_USERS) && defined(ENABLE_CONFIGURATION_POLICY)
   scoped_ptr<ManagedModePolicyProvider> managed_mode_policy_provider_;
 #endif
 
-  base::WeakPtrFactory<ProfilePolicyConnector> weak_ptr_factory_;
 #endif  // ENABLE_CONFIGURATION_POLICY
 
   scoped_ptr<PolicyService> policy_service_;
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 9284479..6a4b7d6 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -128,6 +128,7 @@
 #include "chrome/browser/chromeos/audio/audio_devices_pref_handler_impl.h"
 #include "chrome/browser/chromeos/customization_document.h"
 #include "chrome/browser/chromeos/display/display_preferences.h"
+#include "chrome/browser/chromeos/extensions/echo_private_api.h"
 #include "chrome/browser/chromeos/login/default_pinned_apps_field_trial.h"
 #include "chrome/browser/chromeos/login/login_utils.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
@@ -277,6 +278,7 @@
   chromeos::UserManager::RegisterPrefs(registry);
   chromeos::WallpaperManager::RegisterPrefs(registry);
   chromeos::StartupUtils::RegisterPrefs(registry);
+  chromeos::echo_offer::RegisterPrefs(registry);
   policy::AutoEnrollmentClient::RegisterPrefs(registry);
   policy::DeviceCloudPolicyManagerChromeOS::RegisterPrefs(registry);
   policy::DeviceStatusCollector::RegisterPrefs(registry);
diff --git a/chrome/browser/prefs/pref_service_syncable.cc b/chrome/browser/prefs/pref_service_syncable.cc
index 58d040b..bd644ad 100644
--- a/chrome/browser/prefs/pref_service_syncable.cc
+++ b/chrome/browser/prefs/pref_service_syncable.cc
@@ -92,6 +92,7 @@
   PrefServiceSyncable* incognito_service = new PrefServiceSyncable(
       pref_notifier,
       pref_value_store_->CloneAndSpecialize(NULL,  // managed
+                                            NULL,  // supervised_user
                                             incognito_extension_prefs,
                                             NULL,  // command_line_prefs
                                             incognito_pref_store,
diff --git a/chrome/browser/prefs/pref_service_syncable_builder.cc b/chrome/browser/prefs/pref_service_syncable_builder.cc
index 4b58eb7..701be66 100644
--- a/chrome/browser/prefs/pref_service_syncable_builder.cc
+++ b/chrome/browser/prefs/pref_service_syncable_builder.cc
@@ -59,6 +59,7 @@
   PrefServiceSyncable* pref_service = new PrefServiceSyncable(
       pref_notifier,
       new PrefValueStore(managed_prefs_.get(),
+                         supervised_user_prefs_.get(),
                          extension_prefs_.get(),
                          command_line_prefs_.get(),
                          user_prefs_.get(),
diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc
index e049827..e9f38fe 100644
--- a/chrome/browser/prerender/prerender_contents.cc
+++ b/chrome/browser/prerender/prerender_contents.cc
@@ -505,7 +505,7 @@
 
 bool PrerenderContents::AddAliasURL(const GURL& url) {
   const bool http = url.SchemeIs(chrome::kHttpScheme);
-  const bool https = url.SchemeIs(chrome::kHttpsScheme);
+  const bool https = url.SchemeIs(content::kHttpsScheme);
   if (!http && !https) {
     DCHECK_NE(MATCH_COMPLETE_REPLACEMENT_PENDING, match_complete_status_);
     Destroy(FINAL_STATUS_UNSUPPORTED_SCHEME);
diff --git a/chrome/browser/prerender/prerender_local_predictor.cc b/chrome/browser/prerender/prerender_local_predictor.cc
index 5f98227..70a9f1d 100644
--- a/chrome/browser/prerender/prerender_local_predictor.cc
+++ b/chrome/browser/prerender/prerender_local_predictor.cc
@@ -293,12 +293,13 @@
       : url_id(url_id),
         url(url),
         priority(priority),
-        start_time(start_time) {
+        start_time(start_time),
+        would_have_matched(false) {
   }
 
   // Default constructor for dummy element
   PrerenderProperties()
-      : priority(0.0) {
+      : priority(0.0), would_have_matched(false) {
   }
 
   double GetCurrentDecayedPriority() {
diff --git a/chrome/browser/printing/print_dialog_gtk.cc b/chrome/browser/printing/print_dialog_gtk.cc
index 5b8f748..a06b3ec 100644
--- a/chrome/browser/printing/print_dialog_gtk.cc
+++ b/chrome/browser/printing/print_dialog_gtk.cc
@@ -76,7 +76,7 @@
  private:
   // Callback function used by gtk_enumerate_printers() to get all printer.
   static gboolean SetPrinter(GtkPrinter* printer, gpointer data) {
-    GtkPrinterList *printer_list = (GtkPrinterList*)data;
+    GtkPrinterList* printer_list = reinterpret_cast<GtkPrinterList*>(data);
     if (gtk_printer_is_default(printer))
       printer_list->default_printer_ = printer;
 
@@ -300,6 +300,10 @@
 }
 
 void PrintDialogGtk::OnResponse(GtkWidget* dialog, int response_id) {
+  int num_matched_handlers = g_signal_handlers_disconnect_by_func(
+      dialog_, reinterpret_cast<gpointer>(&OnResponseThunk), this);
+  CHECK_EQ(1, num_matched_handlers);
+
   gtk_widget_hide(dialog_);
 
   switch (response_id) {
diff --git a/chrome/browser/printing/print_preview_test.cc b/chrome/browser/printing/print_preview_test.cc
index e6136a1..c4223a7 100644
--- a/chrome/browser/printing/print_preview_test.cc
+++ b/chrome/browser/printing/print_preview_test.cc
@@ -62,8 +62,6 @@
     return NULL;
   }
 
-  Browser* browser_;
-
   DISALLOW_COPY_AND_ASSIGN(PrintPreviewTestBrowserWindow);
 };
 }  // namespace
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index b72421d..978e36e 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -45,6 +45,7 @@
 #include "chrome/browser/extensions/api/push_messaging/push_messaging_api.h"
 #include "chrome/browser/extensions/api/serial/serial_connection.h"
 #include "chrome/browser/extensions/api/sessions/sessions_api.h"
+#include "chrome/browser/extensions/api/signedin_devices/signed_in_devices_manager.h"
 #include "chrome/browser/extensions/api/socket/socket.h"
 #include "chrome/browser/extensions/api/socket/udp_socket.h"
 #include "chrome/browser/extensions/api/sockets_udp/udp_socket_event_dispatcher.h"
@@ -250,6 +251,7 @@
   extensions::ProcessesAPI::GetFactoryInstance();
   extensions::PushMessagingAPI::GetFactoryInstance();
   extensions::SessionsAPI::GetFactoryInstance();
+  extensions::SignedInDevicesManager::GetFactoryInstance();
 #if defined(ENABLE_SPELLCHECK)
   extensions::SpellcheckAPI::GetFactoryInstance();
 #endif
diff --git a/chrome/browser/profiles/profile_downloader.h b/chrome/browser/profiles/profile_downloader.h
index 8c5c465..bbec99d 100644
--- a/chrome/browser/profiles/profile_downloader.h
+++ b/chrome/browser/profiles/profile_downloader.h
@@ -12,7 +12,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/strings/string16.h"
 #include "chrome/browser/image_decoder.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 #include "net/url_request/url_fetcher_delegate.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index 660a0b3..0e8e732 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -9,6 +9,7 @@
 #include "base/basictypes.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/callback.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/debug/alias.h"
@@ -90,7 +91,8 @@
 #include "chrome/browser/chromeos/policy/policy_cert_verifier.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/chromeos/settings/cros_settings_names.h"
-#include "chrome/browser/policy/browser_policy_connector.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
+#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #endif  // defined(OS_CHROMEOS)
 
 using content::BrowserContext;
@@ -221,6 +223,22 @@
 };
 #endif  // defined(DEBUG_DEVTOOLS)
 
+#if defined(OS_CHROMEOS)
+scoped_ptr<policy::PolicyCertVerifier> CreatePolicyCertVerifier(
+    Profile* profile) {
+  policy::ProfilePolicyConnector* connector =
+      policy::ProfilePolicyConnectorFactory::GetForProfile(profile);
+  base::Closure policy_cert_trusted_callback =
+      base::Bind(base::IgnoreResult(&content::BrowserThread::PostTask),
+                 content::BrowserThread::UI,
+                 FROM_HERE,
+                 connector->GetPolicyCertTrustedCallback());
+  scoped_ptr<policy::PolicyCertVerifier> cert_verifier(
+      new policy::PolicyCertVerifier(policy_cert_trusted_callback));
+  connector->SetPolicyCertVerifier(cert_verifier.get());
+  return cert_verifier.Pass();
+}
+#endif
 }  // namespace
 
 void ProfileIOData::InitializeOnUIThread(Profile* profile) {
@@ -277,9 +295,7 @@
       managed_user_service->GetURLFilterForIOThread();
 #endif
 #if defined(OS_CHROMEOS)
-  policy::BrowserPolicyConnector* connector =
-      g_browser_process->browser_policy_connector();
-  params->trust_anchor_provider = connector->GetCertTrustAnchorProvider();
+  params->cert_verifier = CreatePolicyCertVerifier(profile);
 #endif
 
   params->profile = profile;
@@ -390,9 +406,6 @@
 #if defined(ENABLE_NOTIFICATIONS)
       notification_service(NULL),
 #endif
-#if defined(OS_CHROMEOS)
-      trust_anchor_provider(NULL),
-#endif
       profile(NULL) {
 }
 
@@ -810,8 +823,8 @@
 #endif
 
 #if defined(OS_CHROMEOS)
-  cert_verifier_.reset(new policy::PolicyCertVerifier(
-      profile_params_->profile, profile_params_->trust_anchor_provider));
+  profile_params_->cert_verifier->InitializeOnIOThread();
+  cert_verifier_ = profile_params_->cert_verifier.Pass();
   main_request_context_->set_cert_verifier(cert_verifier_.get());
 #else
   main_request_context_->set_cert_verifier(
diff --git a/chrome/browser/profiles/profile_io_data.h b/chrome/browser/profiles/profile_io_data.h
index cec73bc..724a728 100644
--- a/chrome/browser/profiles/profile_io_data.h
+++ b/chrome/browser/profiles/profile_io_data.h
@@ -44,7 +44,6 @@
 }
 
 namespace net {
-class CertTrustAnchorProvider;
 class CertVerifier;
 class CookieStore;
 class FraudulentCertificateReporter;
@@ -60,6 +59,7 @@
 }  // namespace net
 
 namespace policy {
+class PolicyCertVerifier;
 class URLBlacklistManager;
 }  // namespace policy
 
@@ -275,9 +275,7 @@
 #endif
 
 #if defined(OS_CHROMEOS)
-    // This is used to build the CertVerifier on the IO thread, and is a shared
-    // provider used by all profiles for now.
-    net::CertTrustAnchorProvider* trust_anchor_provider;
+    scoped_ptr<policy::PolicyCertVerifier> cert_verifier;
 #endif
 
     // The profile this struct was populated from. It's passed as a void* to
diff --git a/chrome/browser/renderer_host/chrome_render_view_host_observer.cc b/chrome/browser/renderer_host/chrome_render_view_host_observer.cc
index e5e96e5..e410cbb 100644
--- a/chrome/browser/renderer_host/chrome_render_view_host_observer.cc
+++ b/chrome/browser/renderer_host/chrome_render_view_host_observer.cc
@@ -35,6 +35,7 @@
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/gfx/codec/jpeg_codec.h"
+#include "ui/gfx/size.h"
 
 #if defined(OS_WIN)
 #include "base/win/win_util.h"
@@ -80,7 +81,7 @@
   if (!predictor_)
     return;
   if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kChromeFrame) &&
-     (url.SchemeIs(chrome::kHttpScheme) || url.SchemeIs(chrome::kHttpsScheme)))
+     (url.SchemeIs(chrome::kHttpScheme) || url.SchemeIs(content::kHttpsScheme)))
     predictor_->PreconnectUrlAndSubresources(url, GURL());
 }
 
@@ -197,7 +198,10 @@
 // Handles the image thumbnail for the context node, composes a image search
 // request based on the received thumbnail and opens the request in a new tab.
 void ChromeRenderViewHostObserver::OnRequestThumbnailForContextNodeACK(
-    const SkBitmap& bitmap) {
+    const SkBitmap& bitmap,
+    const gfx::Size& original_size) {
+  if (bitmap.isNull())
+    return;
   WebContents* web_contents =
       WebContents::FromRenderViewHost(render_view_host());
   const TemplateURL* const default_provider =
@@ -221,6 +225,7 @@
   // TODO(jnd): Add a method in WebContentsViewDelegate to get the image URL
   // from the ContextMenuParams which creates current context menu.
   search_args.image_url = GURL();
+  search_args.image_original_size = original_size;
   TemplateURLRef::PostContent post_content;
   GURL result(default_provider->image_url_ref().ReplaceSearchTerms(
       search_args, &post_content));
diff --git a/chrome/browser/renderer_host/chrome_render_view_host_observer.h b/chrome/browser/renderer_host/chrome_render_view_host_observer.h
index 0ce4c4b..213fc0d 100644
--- a/chrome/browser/renderer_host/chrome_render_view_host_observer.h
+++ b/chrome/browser/renderer_host/chrome_render_view_host_observer.h
@@ -20,6 +20,10 @@
 class Extension;
 }
 
+namespace gfx {
+class Size;
+}
+
 // This class holds the Chrome specific parts of RenderViewHost, and has the
 // same lifetime.
 class ChromeRenderViewHostObserver : public content::RenderViewHostObserver {
@@ -45,7 +49,8 @@
 
   void OnFocusedNodeTouched(bool editable);
 
-  void OnRequestThumbnailForContextNodeACK(const SkBitmap& bitmap);
+  void OnRequestThumbnailForContextNodeACK(const SkBitmap& bitmap,
+                                           const gfx::Size& original_size);
 
   Profile* profile_;
   chrome_browser_net::Predictor* predictor_;
diff --git a/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc b/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc
index 1f91bf5..266d8e1 100644
--- a/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc
+++ b/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/content_settings/host_content_settings_map.h"
 #include "chrome/browser/download/download_request_limiter.h"
 #include "chrome/browser/download/download_resource_throttle.h"
-#include "chrome/browser/download/download_util.h"
 #include "chrome/browser/extensions/api/streams_private/streams_private_api.h"
 #include "chrome/browser/extensions/extension_info_map.h"
 #include "chrome/browser/extensions/user_script_listener.h"
@@ -37,6 +36,7 @@
 #include "chrome/common/render_messages.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
+#include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/resource_context.h"
 #include "content/public/browser/resource_dispatcher_host.h"
@@ -422,6 +422,10 @@
     return false;
   }
 
+  RenderViewHost* view = RenderViewHost::FromID(child_id, route_id);
+  if (view && view->GetProcess()->IsGuest())
+    return false;
+
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
       base::Bind(&ExternalProtocolHandler::LaunchUrl, url, child_id, route_id));
diff --git a/chrome/browser/renderer_host/offline_resource_throttle.cc b/chrome/browser/renderer_host/offline_resource_throttle.cc
index ceaffca..34f8c42 100644
--- a/chrome/browser/renderer_host/offline_resource_throttle.cc
+++ b/chrome/browser/renderer_host/offline_resource_throttle.cc
@@ -117,10 +117,9 @@
 }
 
 bool OfflineResourceThrottle::IsRemote(const GURL& url) const {
-  return !net::IsLocalhost(url.host()) &&
-    (url.SchemeIs(chrome::kFtpScheme) ||
-     url.SchemeIs(chrome::kHttpScheme) ||
-     url.SchemeIs(chrome::kHttpsScheme));
+  return !net::IsLocalhost(url.host()) && (url.SchemeIs(chrome::kFtpScheme) ||
+                                           url.SchemeIs(chrome::kHttpScheme) ||
+                                           url.SchemeIs(content::kHttpsScheme));
 }
 
 bool OfflineResourceThrottle::ShouldShowOfflinePage(const GURL& url) const {
diff --git a/chrome/browser/resources/apps_debugger/css/items.css b/chrome/browser/resources/apps_debugger/css/items.css
index 4f7f6d1..b3a4404 100644
--- a/chrome/browser/resources/apps_debugger/css/items.css
+++ b/chrome/browser/resources/apps_debugger/css/items.css
@@ -19,9 +19,9 @@
 /* Override the visual style of tabs. */
 
 #tabs-header-container {
-  margin: 0 auto;
+  margin: 0 20px;
   position: relative;
-  width: 720px;
+  width: calc(100% - 40px);
 }
 
 #tabs {
@@ -77,12 +77,17 @@
 /* Header */
 
 #header {
-  border-bottom: 1px solid #d1d1d1;
   border-top: 1px solid #d1d1d1;
   height: 58px;
   width: 100%;
 }
 
+#header-bottom-separator {
+  border-bottom: 1px solid #d1d1d1;
+  margin: 0 20px;
+  width: calc(100% - 40px);
+}
+
 #developer-controls {
   margin-top: 0;
   max-width: none;
@@ -104,15 +109,15 @@
   background-image: -webkit-image-set(
       url(../images/search.png) 1x,
       url(../images/2x/search.png) 2x);
-  background-position: 0 8px;
+  background-position: 10px 8px;
   background-repeat: no-repeat;
   border: none;
   font-size: 13px;
   height: 18px;
   position: absolute;
-  right: 16px;
-  top: 15px;
-  width: 220px;
+  right: 0;
+  top: 16px;
+  width: 230px;
 }
 
 html[dir='rtl'] #search {
@@ -213,13 +218,12 @@
 #extensions-tab {
   height: 100%;
   overflow-y: scroll;
-  width: 720px;
 }
 
 .packed-list,
 .unpacked-list {
-  margin: 0 auto;
-  width: 720px;
+  margin: 0 20px;
+  width: calc(100% - 40px);
 }
 
 .loading #no-extensions,
@@ -287,6 +291,10 @@
   color: inherit;
 }
 
+.extension-title-container {
+  margin-bottom: 5px;
+}
+
 .extension-version {
   -webkit-padding-end: 7px;
   -webkit-user-select: text;
@@ -333,7 +341,6 @@
 }
 
 .enable-checkbox-text {
-  /* Matches width of trash. */
   -webkit-margin-end: 30px;
   min-width: 62px;
 }
@@ -377,6 +384,7 @@
   display: inline-block;
 }
 
+.extension-warnings,
 .install-warnings {
   background: pink;
   border-radius: 3px;
@@ -384,16 +392,23 @@
   padding: 2px 5px;
 }
 
+.extension-warnings a,
 .install-warnings a {
   -webkit-margin-start: 0;
 }
 
+.extension-warnings ul,
 .install-warnings ul {
   margin: 0;
 }
 
+.extension-warnings li,
+.install-warnings li {
+  word-wrap: break-word;
+}
+
 .extension-details-summary {
-  margin-bottom: 10px;
+  margin-bottom: 5px;
 }
 
 .extension-details-all {
@@ -428,40 +443,44 @@
 /* Overlays */
 
 #overlay {
+  background-color: rgba(0, 0, 0, 0.6);
   z-index: 5;
 }
 
+#overlay .page {
+  -webkit-border-radius: 0;
+  border: 1px solid rgba(0, 0, 0, 0.5);
+  box-shadow: 0 1px 5px 1px rgba(0, 0, 0, 0.25);
+  padding: 25px;
+}
+
 #overlay .page:not(.showing) {
   display: none;
 }
 
+#overlay .page h1 {
+  font-size: 21px;
+  padding: 0;
+}
+
+#overlay .page .content-area,
+#overlay .page .action-area {
+  font-size: 13px;
+  margin-top: 40px;
+  padding: 0;
+}
+
+/* Pack dialog button size and delete dialog button size */
+#packItemOverlay .button-strip button,
+#item-private-key-container button,
+#alertOverlay .button-strip button {
+  width: 90px;
+}
+
 .extension-id {
   -webkit-user-select: text;
 }
 
-#extension-settings .trash {
-  -webkit-transition: opacity 200ms;
-  height: 22px;
-  opacity: 0.8;
-  position: absolute;
-  right: 0;
-  top: 3px;
-  vertical-align: middle;
-}
-
-html[dir='rtl'] #extension-settings .trash {
-  left: 0;
-  right: auto;
-}
-
-.extension-list-item:not(:hover) .trash:not(:focus) {
-  opacity: 0;
-}
-
-.extension-list-item-wrapper.may-not-disable .trash {
-  visibility: hidden;
-}
-
 .may-not-disable .optional-controls .optional-controls-disableable {
   display: none;
 }
@@ -523,3 +542,22 @@
   box-shadow: none;
   color: #888;
 }
+
+/* Pack dialog styling */
+#item-private-key-container {
+  display: -webkit-box;
+}
+
+#item-private-key-label {
+  margin: 20px 0 10px;
+  text-align: start;
+}
+
+#item-private-key {
+  -webkit-box-flex: 1;
+  display: block;
+}
+
+#browse-private-key {
+  margin-left: 10px;
+}
diff --git a/chrome/browser/resources/apps_debugger/js/items.js b/chrome/browser/resources/apps_debugger/js/items.js
index 8d09464..5ff959e 100644
--- a/chrome/browser/resources/apps_debugger/js/items.js
+++ b/chrome/browser/resources/apps_debugger/js/items.js
@@ -96,6 +96,23 @@
     chrome.developerPrivate.getStrings(function(strings) {
       loadTimeData.data = strings;
       i18nTemplate.process(document, loadTimeData);
+
+      // Check managed profiles.
+      chrome.developerPrivate.isProfileManaged(function(isManaged) {
+        if (!isManaged)
+          return;
+        alertOverlay.setValues(
+            loadTimeData.getString('managedProfileDialogTitle'),
+            loadTimeData.getString('managedProfileDialogDescription'),
+            loadTimeData.getString('managedProfileDialogCloseButton'),
+            null,
+            function() {
+              AppsDevTool.showOverlay(null);
+              window.close();
+            },
+            null);
+        AppsDevTool.showOverlay($('alertOverlay'));
+      });
     });
   };
 
diff --git a/chrome/browser/resources/apps_debugger/js/items_list.js b/chrome/browser/resources/apps_debugger/js/items_list.js
index 2f60b18..46c7887 100644
--- a/chrome/browser/resources/apps_debugger/js/items_list.js
+++ b/chrome/browser/resources/apps_debugger/js/items_list.js
@@ -243,15 +243,6 @@
           ItemsList.launchApp(item.id);
         });
         launch.hidden = false;
-
-        // The 'Restart' link.
-        var restart = node.querySelector('.restart-link');
-        restart.addEventListener('click', function(e) {
-          chrome.developerPrivate.restart(item.id, function() {
-            ItemsList.loadItemsInfo();
-          });
-        });
-        restart.hidden = false;
       }
       // The terminated reload link.
       if (!item.terminated)
@@ -599,17 +590,16 @@
 
     // Scroll relatively to the position of the first item.
     var header = tabNode.querySelector('.unpacked-list .list-header');
-    var container = $('container');
-    if (node.offsetTop - header.offsetTop < container.scrollTop) {
+    if (node.offsetTop - header.offsetTop < tabNode.scrollTop) {
       // Some padding between the top edge and the node is already provided
       // by the HTML layout.
-      container.scrollTop = node.offsetTop - header.offsetTop;
-    } else if (node.offsetTop + node.offsetHeight > container.scrollTop +
-        container.offsetHeight + 20) {
+      tabNode.scrollTop = node.offsetTop - header.offsetTop;
+    } else if (node.offsetTop + node.offsetHeight > tabNode.scrollTop +
+        tabNode.offsetHeight + 20) {
       // Adds padding of 20px between the bottom edge and the bottom of the
       // node.
-      container.scrollTop = node.offsetTop + node.offsetHeight -
-          container.offsetHeight + 20;
+      tabNode.scrollTop = node.offsetTop + node.offsetHeight -
+          tabNode.offsetHeight + 20;
     }
   };
 
diff --git a/chrome/browser/resources/apps_debugger/main.html b/chrome/browser/resources/apps_debugger/main.html
index defc2ec..a1e149d 100644
--- a/chrome/browser/resources/apps_debugger/main.html
+++ b/chrome/browser/resources/apps_debugger/main.html
@@ -36,6 +36,8 @@
                 i18n-values=".placeholder:appsDevtoolSearch"
                 spellcheck="false">
           </div>
+          <div id="header-bottom-separator">
+          </div>
         </div>
         <tabpanels id="tab-panels">
           <!-- Apps Tab -->
@@ -187,8 +189,6 @@
               </a>
               <a class="launch-link" i18n-content="extensionSettingsLaunch"
                   href="#" hidden></a>
-              <a class="restart-link" i18n-content="extensionSettingsRestart"
-                  href="#" hidden></a>
               <a class="permissions-link"
                   i18n-content="extensionSettingsPermissions" href="#"></a>
               <a class="site-link" target="_blank" hidden></a>
diff --git a/chrome/browser/resources/apps_debugger/pack_item_overlay.html b/chrome/browser/resources/apps_debugger/pack_item_overlay.html
index fd9bb8a..1bf1f63 100644
--- a/chrome/browser/resources/apps_debugger/pack_item_overlay.html
+++ b/chrome/browser/resources/apps_debugger/pack_item_overlay.html
@@ -4,10 +4,15 @@
     <div class="pack-item-heading" id="pack-heading"></div>
     <input class="pack-item-text-area" id="item-root-dir" type="hidden">
     <div class="pack-item-text-boxes">
-      <label i18n-content="packExtensionPrivateKey"></label>
-      <input class="pack-item-text-area" id="item-private-key" type="text">
-      <button id="browse-private-key"
-              i18n-content="packExtensionBrowseButton"></button>
+      <div id="item-private-key-label">
+        <label i18n-content="packExtensionPrivateKey"></label>
+      </div>
+      <div id="item-private-key-container">
+        <input class="pack-item-text-area" id="item-private-key" type="text"
+            disabled>
+        <button id="browse-private-key"
+            i18n-content="packExtensionBrowseButton"></button>
+      </div>
     </div>
   </div>
   <div class="action-area">
diff --git a/chrome/browser/resources/chromeos/about_os_credits.html b/chrome/browser/resources/chromeos/about_os_credits.html
index 2cdde1a..26f99e1 100644
--- a/chrome/browser/resources/chromeos/about_os_credits.html
+++ b/chrome/browser/resources/chromeos/about_os_credits.html
@@ -78,7 +78,9 @@
 <body>
 <span class="page-title" style="float:left;">Credits</span>
 <a href="javascript:window.print();" style="float:right;">Print</a>
-<div style="clear:both; overflow:auto;"><!-- Chromium <3s the following projects -->
+<div style="clear:both; overflow:auto;">
+<h2>List of packages used in Google Chrome OS:</h2>
+<!-- Chromium <3s the following projects -->
 <div class="product">
 <span class="title">Linux-2.6</span>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
@@ -10762,11 +10764,9 @@
 
 </div>
 
-<h2>List of shared stock Gentoo Licenses used in ChromeOS:</h2>
+<h2>List of shared stock Gentoo Licenses used in Google Chrome OS:</h2>
 <div class="product">
-<span class="title">
-<a name="AFL-2.1">Gentoo Package Provided Stock License AFL-2.1</a>
-</span>
+<a name="AFL-2.1" class="title">Gentoo Package Provided Stock License AFL-2.1</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -10824,15 +10824,13 @@
 
 </pre>
 <div class="license-packages">
-Used by these packages: dev-libs/dbus-glib-0.100 sys-apps/dbus-1.6.8-r3
+Used by these packages: sys-apps/dbus-1.6.8-r3 dev-libs/dbus-glib-0.100
 </div>
 </div>
 </div>
 
 <div class="product">
-<span class="title">
-<a name="Apache-2.0">Gentoo Package Provided Stock License Apache-2.0</a>
-</span>
+<a name="Apache-2.0" class="title">Gentoo Package Provided Stock License Apache-2.0</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -11517,15 +11515,13 @@
 
 </pre>
 <div class="license-packages">
-Used by these packages: app-admin/rsyslog-5.8.11 app-i18n/chinese-input-1.0.0.0-r1 app-i18n/chromeos-hangul-1.0.0.12 app-i18n/chromeos-keyboards-1.1.3.0-r1 app-i18n/ibus-english-m-0.0.1-r3 dev-libs/protobuf-2.3.0-r4 dev-python/jsonrpclib-0_pre20110820-r1 dev-util/stressapptest-1.0.4 media-fonts/croscorefonts-1.23.0 media-fonts/crosextrafonts-20130214 media-fonts/droidfonts-cros-20121206 media-fonts/notofonts-20130514
+Used by these packages: app-i18n/chinese-input-1.0.0.0-r1 app-i18n/chromeos-hangul-1.0.0.12 app-i18n/chromeos-keyboards-1.1.3.0-r1 media-fonts/croscorefonts-1.23.0 media-fonts/crosextrafonts-20130214 media-fonts/droidfonts-cros-20121206 app-i18n/ibus-english-m-0.0.1-r3 dev-python/jsonrpclib-0_pre20110820-r1 media-fonts/notofonts-20130514 dev-libs/protobuf-2.3.0-r4 app-admin/rsyslog-5.8.11 dev-util/stressapptest-1.0.4
 </div>
 </div>
 </div>
 
 <div class="product">
-<span class="title">
-<a name="Artistic-2">Gentoo Package Provided Stock License Artistic-2</a>
-</span>
+<a name="Artistic-2" class="title">Gentoo Package Provided Stock License Artistic-2</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -11739,9 +11735,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="Atheros">Gentoo Package Provided Stock License Atheros</a>
-</span>
+<a name="Atheros" class="title">Gentoo Package Provided Stock License Atheros</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -11773,9 +11767,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="BSD-Google">Gentoo Package Provided Stock License BSD-Google</a>
-</span>
+<a name="BSD-Google" class="title">Gentoo Package Provided Stock License BSD-Google</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -11810,15 +11802,13 @@
 
 </pre>
 <div class="license-packages">
-Used by these packages: app-i18n/input-tools-0.0.1 app-i18n/nacl-mozc-1.10.1389.104-r10 dev-python/unittest2-0.5.1 media-plugins/o3d-203170
+Used by these packages: app-i18n/input-tools-0.0.1 app-i18n/nacl-mozc-1.10.1389.104-r10 media-plugins/o3d-203170 dev-python/unittest2-0.5.1
 </div>
 </div>
 </div>
 
 <div class="product">
-<span class="title">
-<a name="BSD-bsdiff">Gentoo Package Provided Stock License BSD-bsdiff</a>
-</span>
+<a name="BSD-bsdiff" class="title">Gentoo Package Provided Stock License BSD-bsdiff</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -11854,9 +11844,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="BSD-dhcpcd">Gentoo Package Provided Stock License BSD-dhcpcd</a>
-</span>
+<a name="BSD-dhcpcd" class="title">Gentoo Package Provided Stock License BSD-dhcpcd</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -11892,9 +11880,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="BSD-iputils">Gentoo Package Provided Stock License BSD-iputils</a>
-</span>
+<a name="BSD-iputils" class="title">Gentoo Package Provided Stock License BSD-iputils</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -11940,9 +11926,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="BSD-libevent">Gentoo Package Provided Stock License BSD-libevent</a>
-</span>
+<a name="BSD-libevent" class="title">Gentoo Package Provided Stock License BSD-libevent</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -11979,9 +11963,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="BZIP2">Gentoo Package Provided Stock License BZIP2</a>
-</span>
+<a name="BZIP2" class="title">Gentoo Package Provided Stock License BZIP2</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -12027,9 +12009,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="BitstreamVera">Gentoo Package Provided Stock License BitstreamVera</a>
-</span>
+<a name="BitstreamVera" class="title">Gentoo Package Provided Stock License BitstreamVera</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -12166,9 +12146,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="CPL-1.0">Gentoo Package Provided Stock License CPL-1.0</a>
-</span>
+<a name="CPL-1.0" class="title">Gentoo Package Provided Stock License CPL-1.0</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -12267,9 +12245,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="DES">Gentoo Package Provided Stock License DES</a>
-</span>
+<a name="DES" class="title">Gentoo Package Provided Stock License DES</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -12333,9 +12309,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="FDL-1.1">Gentoo Package Provided Stock License FDL-1.1</a>
-</span>
+<a name="FDL-1.1" class="title">Gentoo Package Provided Stock License FDL-1.1</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -12703,9 +12677,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="FLEX">Gentoo Package Provided Stock License FLEX</a>
-</span>
+<a name="FLEX" class="title">Gentoo Package Provided Stock License FLEX</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -12756,9 +12728,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="FTL">Gentoo Package Provided Stock License FTL</a>
-</span>
+<a name="FTL" class="title">Gentoo Package Provided Stock License FTL</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -12929,9 +12899,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="GPL-2">Gentoo Package Provided Stock License GPL-2</a>
-</span>
+<a name="GPL-2" class="title">Gentoo Package Provided Stock License GPL-2</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -13278,15 +13246,13 @@
 
 </pre>
 <div class="license-packages">
-Used by these packages: app-admin/eselect-opengl-1.2.4 app-admin/eselect-python-20100321 app-admin/python-updater-0.10 app-arch/xz-utils-4.999.9_beta app-i18n/ibus-m17n-1.3.3-r6 app-laptop/laptop-mode-tools-1.59-r17 app-misc/ddccontrol-0.4.2-r1 app-misc/ddccontrol-db-20061014 app-misc/mime-types-8 app-misc/pax-utils-0.4 app-text/sgml-common-0.6.3-r5 dev-libs/dbus-glib-0.100 dev-libs/libatomic_ops-7.2d dev-libs/libchewing-0.3.2-r1 dev-libs/libgpg-error-1.10-r1 dev-libs/lzo-2.06 dev-libs/nspr-4.9.5-r2 dev-libs/nss-3.14.3 dev-libs/pkcs11-helper-1.07 dev-util/gtk-doc-am-1.18 dev-util/intltool-0.41.0 dev-util/perf-3.4-r2417 dev-util/ragel-6.7-r2 dev-util/xxd-1.10 media-gfx/ply-image-0.0.1-r40 media-libs/freetype-2.4.12 media-libs/sbc-1.0 media-plugins/alsa-plugins-1.0.25-r1 media-sound/adhd-0.0.1-r570 media-sound/alsa-headers-1.0.25 media-sound/alsa-utils-1.0.25-r3 net-dialup/xl2tpd-1.3.0-r1 net-firewall/iptables-1.4.8-r2 net-libs/libnetfilter_queue-1.0.1 net-libs/libnfnetlink-1.0.1 net-misc/htpdate-1.0.4-r6 net-misc/openvpn-2.1.12-r2 net-misc/strongswan-5.0.2-r5 net-wireless/bluez-5.4-r8 net-wireless/wpa_supplicant-0.7.2-r130 sys-apps/baselayout-2.0.1-r230 sys-apps/busybox-1.21.0-r5 sys-apps/chvt-0.0.1-r1 sys-apps/dbus-1.6.8-r3 sys-apps/diffutils-3.2 sys-apps/dmidecode-2.10 sys-apps/dtc-1.3.0-r23 sys-apps/eject-2.1.5-r2 sys-apps/ethtool-6 sys-apps/findutils-4.4.2-r1 sys-apps/flashrom-0.9.4-r293 sys-apps/hwids-20120922 sys-apps/iotools-1.4 sys-apps/kbd-1.15.3 sys-apps/keyutils-1.1 sys-apps/mawk-1.3.4_p20100625 sys-apps/memtester-4.2.2 sys-apps/module-init-tools-3.16-r4 sys-apps/net-tools-1.60_p20110409135728 sys-apps/pciutils-3.1.10 sys-apps/portage-2.1.11.50 sys-apps/sandbox-2.6-r1 sys-apps/shadow-4.1.2.2-r4 sys-apps/smartmontools-5.42 sys-apps/upstart-1.2-r7 sys-apps/ureadahead-0.100.0-r2 sys-apps/usbutils-006 sys-apps/util-linux-2.21.2-r1 sys-auth/pam_pwdfile-0.99-r1 sys-auth/pambase-20101024-r2 sys-boot/syslinux-3.83-r5 sys-devel/autoconf-2.13 sys-devel/autoconf-wrapper-10-r1 sys-devel/automake-1.11.1 sys-devel/automake-wrapper-5 sys-devel/binutils-config-3-r3 sys-devel/libtool-2.4-r1 sys-devel/patch-2.6.1 sys-fs/avfs-1.0.1 sys-fs/e2fsprogs-1.42 sys-fs/ecryptfs-utils-101 sys-fs/fuse-2.8.6-r3 sys-fs/lvm2-2.02.88 sys-fs/ntfs3g-2012.1.15-r2 sys-fs/udev-171-r3 sys-kernel/linux-headers-3.4-r6 sys-libs/e2fsprogs-libs-1.42 sys-libs/libcap-2.17 sys-libs/libnih-1.0.3 sys-libs/pam-1.1.5 sys-process/procps-3.3.4 x11-misc/shared-mime-info-0.90
+Used by these packages: sys-kernel/Linux-2.6 media-sound/adhd-0.0.1-r570 media-sound/alsa-headers-1.0.25 media-plugins/alsa-plugins-1.0.25-r1 media-sound/alsa-utils-1.0.25-r3 sys-devel/autoconf-2.13 sys-devel/autoconf-wrapper-10-r1 sys-devel/automake-1.11.1 sys-devel/automake-wrapper-5 sys-fs/avfs-1.0.1 sys-apps/baselayout-2.0.1-r230 sys-devel/binutils-config-3-r3 net-wireless/bluez-5.4-r8 sys-apps/busybox-1.21.0-r5 sys-apps/chvt-0.0.1-r1 sys-apps/dbus-1.6.8-r3 dev-libs/dbus-glib-0.100 app-misc/ddccontrol-0.4.2-r1 app-misc/ddccontrol-db-20061014 sys-apps/diffutils-3.2 sys-apps/dmidecode-2.10 sys-apps/dtc-1.3.0-r23 sys-fs/e2fsprogs-1.42 sys-libs/e2fsprogs-libs-1.42 sys-fs/ecryptfs-utils-101 sys-apps/eject-2.1.5-r2 app-admin/eselect-opengl-1.2.4 app-admin/eselect-python-20100321 sys-apps/ethtool-6 sys-apps/findutils-4.4.2-r1 sys-apps/flashrom-0.9.4-r293 media-libs/freetype-2.4.12 sys-fs/fuse-2.8.6-r3 dev-util/gtk-doc-am-1.18 net-misc/htpdate-1.0.4-r6 sys-apps/hwids-20120922 app-i18n/ibus-m17n-1.3.3-r6 dev-util/intltool-0.41.0 sys-apps/iotools-1.4 net-firewall/iptables-1.4.8-r2 sys-apps/kbd-1.15.3 sys-apps/keyutils-1.1 app-laptop/laptop-mode-tools-1.59-r17 dev-libs/libatomic_ops-7.2d sys-libs/libcap-2.17 dev-libs/libchewing-0.3.2-r1 dev-libs/libgpg-error-1.10-r1 net-libs/libnetfilter_queue-1.0.1 net-libs/libnfnetlink-1.0.1 sys-libs/libnih-1.0.3 sys-devel/libtool-2.4-r1 sys-kernel/linux-headers-3.4-r6 sys-fs/lvm2-2.02.88 dev-libs/lzo-2.06 sys-apps/mawk-1.3.4_p20100625 sys-apps/memtester-4.2.2 app-misc/mime-types-8 sys-apps/module-init-tools-3.16-r4 sys-apps/net-tools-1.60_p20110409135728 dev-libs/nspr-4.9.5-r2 dev-libs/nss-3.14.3 sys-fs/ntfs3g-2012.1.15-r2 net-misc/openvpn-2.1.12-r2 sys-libs/pam-1.1.5 sys-auth/pam_pwdfile-0.99-r1 sys-auth/pambase-20101024-r2 sys-devel/patch-2.6.1 app-misc/pax-utils-0.4 sys-apps/pciutils-3.1.10 dev-util/perf-3.4-r2417 dev-libs/pkcs11-helper-1.07 media-gfx/ply-image-0.0.1-r40 sys-apps/portage-2.1.11.50 sys-process/procps-3.3.4 app-admin/python-updater-0.10 dev-util/ragel-6.7-r2 sys-apps/sandbox-2.6-r1 media-libs/sbc-1.0 app-text/sgml-common-0.6.3-r5 sys-apps/shadow-4.1.2.2-r4 x11-misc/shared-mime-info-0.90 sys-apps/smartmontools-5.42 net-misc/strongswan-5.0.2-r5 sys-boot/syslinux-3.83-r5 sys-fs/udev-171-r3 sys-apps/upstart-1.2-r7 sys-apps/ureadahead-0.100.0-r2 sys-apps/usbutils-006 sys-apps/util-linux-2.21.2-r1 net-wireless/wpa_supplicant-0.7.2-r130 net-dialup/xl2tpd-1.3.0-r1 dev-util/xxd-1.10 app-arch/xz-utils-4.999.9_beta
 </div>
 </div>
 </div>
 
 <div class="product">
-<span class="title">
-<a name="GPL-2+">Gentoo Package Provided Stock License GPL-2+</a>
-</span>
+<a name="GPL-2+" class="title">Gentoo Package Provided Stock License GPL-2+</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -13301,9 +13267,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="GPL-2-with-exceptions">Gentoo Package Provided Stock License GPL-2-with-exceptions</a>
-</span>
+<a name="GPL-2-with-exceptions" class="title">Gentoo Package Provided Stock License GPL-2-with-exceptions</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -13669,9 +13633,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="GPL-3">Gentoo Package Provided Stock License GPL-3</a>
-</span>
+<a name="GPL-3" class="title">Gentoo Package Provided Stock License GPL-3</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -14352,15 +14314,13 @@
 
 </pre>
 <div class="license-packages">
-Used by these packages: app-admin/rsyslog-5.8.11 app-arch/gzip-1.5 app-arch/sharutils-4.7 app-arch/tar-1.26-r1 app-shells/bash-4.2_p20 dev-lang/swig-2.0.4-r1 net-misc/rsync-3.0.8 net-misc/wget-1.12-r2 sys-apps/coreutils-8.20-r1 sys-apps/grep-2.14 sys-apps/sed-4.2.1-r1 sys-apps/util-linux-2.21.2-r1 sys-apps/which-2.20 sys-block/parted-3.1-r1 sys-devel/autoconf-2.68 sys-devel/binutils-2.22-r17 sys-devel/make-3.82-r1 sys-fs/dosfstools-3.0.9 sys-fs/exfat-utils-0.9.8 sys-fs/fuse-exfat-0.9.8-r1 sys-fs/mtools-4.0.15 sys-libs/gdbm-1.9.1-r2 sys-libs/readline-6.2_p1
+Used by these packages: sys-devel/autoconf-2.68 app-shells/bash-4.2_p20 sys-devel/binutils-2.22-r17 sys-apps/coreutils-8.20-r1 sys-fs/dosfstools-3.0.9 sys-fs/exfat-utils-0.9.8 sys-fs/fuse-exfat-0.9.8-r1 sys-libs/gdbm-1.9.1-r2 sys-apps/grep-2.14 app-arch/gzip-1.5 sys-devel/make-3.82-r1 sys-fs/mtools-4.0.15 sys-block/parted-3.1-r1 sys-libs/readline-6.2_p1 net-misc/rsync-3.0.8 app-admin/rsyslog-5.8.11 sys-apps/sed-4.2.1-r1 app-arch/sharutils-4.7 dev-lang/swig-2.0.4-r1 app-arch/tar-1.26-r1 sys-apps/util-linux-2.21.2-r1 net-misc/wget-1.12-r2 sys-apps/which-2.20
 </div>
 </div>
 </div>
 
 <div class="product">
-<span class="title">
-<a name="ISC">Gentoo Package Provided Stock License ISC</a>
-</span>
+<a name="ISC" class="title">Gentoo Package Provided Stock License ISC</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -14380,15 +14340,13 @@
 
 </pre>
 <div class="license-packages">
-Used by these packages: app-admin/sudo-1.8.6_p7 media-libs/harfbuzz-0.9.18-r2
+Used by these packages: media-libs/harfbuzz-0.9.18-r2 app-admin/sudo-1.8.6_p7
 </div>
 </div>
 </div>
 
 <div class="product">
-<span class="title">
-<a name="LGPL-2">Gentoo Package Provided Stock License LGPL-2</a>
-</span>
+<a name="LGPL-2" class="title">Gentoo Package Provided Stock License LGPL-2</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -14877,15 +14835,13 @@
 
 </pre>
 <div class="license-packages">
-Used by these packages: dev-libs/atk-1.32.0-r1 dev-libs/dbus-c++-0.0.2-r41 dev-libs/glib-2.34.3-r1 dev-libs/libaio-0.3.109-r3 dev-libs/libusb-0.1.12-r7 x11-libs/gtk+-2.20.1 x11-libs/pango-1.32.5-r1
+Used by these packages: dev-libs/atk-1.32.0-r1 dev-libs/dbus-c++-0.0.2-r41 dev-libs/glib-2.34.3-r1 x11-libs/gtk+-2.20.1 dev-libs/libaio-0.3.109-r3 dev-libs/libusb-0.1.12-r7 x11-libs/pango-1.32.5-r1
 </div>
 </div>
 </div>
 
 <div class="product">
-<span class="title">
-<a name="LGPL-2+">Gentoo Package Provided Stock License LGPL-2+</a>
-</span>
+<a name="LGPL-2+" class="title">Gentoo Package Provided Stock License LGPL-2+</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -14900,9 +14856,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="LGPL-2.1">Gentoo Package Provided Stock License LGPL-2.1</a>
-</span>
+<a name="LGPL-2.1" class="title">Gentoo Package Provided Stock License LGPL-2.1</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -15413,15 +15367,13 @@
 
 </pre>
 <div class="license-packages">
-Used by these packages: app-arch/xz-utils-4.999.9_beta app-i18n/ibus-1.4.99.20120314-r5 app-i18n/libhangul-0.0.10 dev-db/m17n-contrib-1.1.10-r1 dev-db/m17n-db-1.6.1-r3 dev-libs/engine_pkcs11-0.1.8 dev-libs/libgcrypt-1.4.6 dev-libs/libgpg-error-1.10-r1 dev-libs/libnl-1.1-r1 dev-libs/libnl-3.2.14 dev-libs/libp11-0.2.8-r1 dev-libs/libusb-1.0.9 dev-libs/m17n-lib-1.6.1-r1 dev-libs/nspr-4.9.5-r2 dev-libs/nss-3.14.3 dev-util/shflags-1.0.3-r1 media-libs/alsa-lib-1.0.25-r1 media-libs/ladspa-sdk-1.13-r1 media-libs/libmtp-0.0.1-r15 media-libs/sbc-1.0 media-plugins/alsa-plugins-1.0.25-r1 net-libs/libmnl-1.0.3-r1 net-misc/modemmanager-classic-interfaces-0.0.1 net-misc/modemmanager-next-0.5.999-r156 net-wireless/bluez-5.4-r8 sys-apps/acl-2.2.51 sys-apps/attr-2.4.46 sys-apps/keyutils-1.1 sys-apps/util-linux-2.21.2-r1 x11-libs/cairo-1.12.12
+Used by these packages: sys-apps/acl-2.2.51 media-libs/alsa-lib-1.0.25-r1 media-plugins/alsa-plugins-1.0.25-r1 sys-apps/attr-2.4.46 net-wireless/bluez-5.4-r8 x11-libs/cairo-1.12.12 dev-libs/engine_pkcs11-0.1.8 app-i18n/ibus-1.4.99.20120314-r5 sys-apps/keyutils-1.1 media-libs/ladspa-sdk-1.13-r1 dev-libs/libgcrypt-1.4.6 dev-libs/libgpg-error-1.10-r1 app-i18n/libhangul-0.0.10 net-libs/libmnl-1.0.3-r1 media-libs/libmtp-0.0.1-r15 dev-libs/libnl-1.1-r1 dev-libs/libnl-3.2.14 dev-libs/libp11-0.2.8-r1 dev-libs/libusb-1.0.9 dev-db/m17n-contrib-1.1.10-r1 dev-db/m17n-db-1.6.1-r3 dev-libs/m17n-lib-1.6.1-r1 net-misc/modemmanager-classic-interfaces-0.0.1 net-misc/modemmanager-next-0.5.999-r156 dev-libs/nspr-4.9.5-r2 dev-libs/nss-3.14.3 media-libs/sbc-1.0 dev-util/shflags-1.0.3-r1 sys-apps/util-linux-2.21.2-r1 app-arch/xz-utils-4.999.9_beta
 </div>
 </div>
 </div>
 
 <div class="product">
-<span class="title">
-<a name="LGPL-3">Gentoo Package Provided Stock License LGPL-3</a>
-</span>
+<a name="LGPL-3" class="title">Gentoo Package Provided Stock License LGPL-3</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -15593,15 +15545,13 @@
 
 </pre>
 <div class="license-packages">
-Used by these packages: app-admin/rsyslog-5.8.11 dev-libs/gmp-5.0.2_p1 media-libs/mesa-9.1-r9 sys-devel/binutils-2.22-r17
+Used by these packages: sys-devel/binutils-2.22-r17 dev-libs/gmp-5.0.2_p1 media-libs/mesa-9.1-r9 app-admin/rsyslog-5.8.11
 </div>
 </div>
 </div>
 
 <div class="product">
-<span class="title">
-<a name="MIT-MIT">Gentoo Package Provided Stock License MIT-MIT</a>
-</span>
+<a name="MIT-MIT" class="title">Gentoo Package Provided Stock License MIT-MIT</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -15627,9 +15577,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="MIT-Mesa">Gentoo Package Provided Stock License MIT-Mesa</a>
-</span>
+<a name="MIT-Mesa" class="title">Gentoo Package Provided Stock License MIT-Mesa</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -15712,9 +15660,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="MPL-1.1">Gentoo Package Provided Stock License MPL-1.1</a>
-</span>
+<a name="MPL-1.1" class="title">Gentoo Package Provided Stock License MPL-1.1</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -16294,9 +16240,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="MPL-2.0">Gentoo Package Provided Stock License MPL-2.0</a>
-</span>
+<a name="MPL-2.0" class="title">Gentoo Package Provided Stock License MPL-2.0</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -16683,9 +16627,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="Marvell">Gentoo Package Provided Stock License Marvell</a>
-</span>
+<a name="Marvell" class="title">Gentoo Package Provided Stock License Marvell</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -16706,9 +16648,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="OFL">Gentoo Package Provided Stock License OFL</a>
-</span>
+<a name="OFL" class="title">Gentoo Package Provided Stock License OFL</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -16820,9 +16760,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="OFL-1.1">Gentoo Package Provided Stock License OFL-1.1</a>
-</span>
+<a name="OFL-1.1" class="title">Gentoo Package Provided Stock License OFL-1.1</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -16926,9 +16864,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="PSF-2">Gentoo Package Provided Stock License PSF-2</a>
-</span>
+<a name="PSF-2" class="title">Gentoo Package Provided Stock License PSF-2</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -16981,15 +16917,13 @@
 
 </pre>
 <div class="license-packages">
-Used by these packages: dev-lang/python-2.7.3-r5 dev-python/argparse-1.2.1 dev-python/setuptools-0.6.14
+Used by these packages: dev-python/argparse-1.2.1 dev-lang/python-2.7.3-r5 dev-python/setuptools-0.6.14
 </div>
 </div>
 </div>
 
 <div class="product">
-<span class="title">
-<a name="RSA">Gentoo Package Provided Stock License RSA</a>
-</span>
+<a name="RSA" class="title">Gentoo Package Provided Stock License RSA</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -17020,9 +16954,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="SGI-B-2.0">Gentoo Package Provided Stock License SGI-B-2.0</a>
-</span>
+<a name="SGI-B-2.0" class="title">Gentoo Package Provided Stock License SGI-B-2.0</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -17056,15 +16988,13 @@
 
 </pre>
 <div class="license-packages">
-Used by these packages: media-libs/glu-9.0.0 media-libs/mesa-9.1-r9 x11-proto/glproto-1.4.14-r1
+Used by these packages: x11-proto/glproto-1.4.14-r1 media-libs/glu-9.0.0 media-libs/mesa-9.1-r9
 </div>
 </div>
 </div>
 
 <div class="product">
-<span class="title">
-<a name="UoI-NCSA">Gentoo Package Provided Stock License UoI-NCSA</a>
-</span>
+<a name="UoI-NCSA" class="title">Gentoo Package Provided Stock License UoI-NCSA</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -17106,9 +17036,1863 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="ZLIB">Gentoo Package Provided Stock License ZLIB</a>
-</span>
+<a name="X" class="title">Gentoo Package Provided Stock License X</a>
+<a class="show" href="#" onclick="return toggle(this);">show license text</a>
+<div class="licence">
+<pre>
+The following is the 'standard copyright' agreed upon by most contributors,
+and is currently the canonical license preferred by the X.Org Foundation.
+This is a slight variant of the common MIT license form published by the
+Open Source Initiative at http://www.opensource.org/licenses/mit-license.php
+
+Copyright holders of new code should use this license statement where
+possible, and insert their name to this list.  Please sort by surname
+for people, and by the full name for other entities (e.g.  Juliusz
+Chroboczek sorts before Intel Corporation sorts before Daniel Stone).
+
+Copyright © 2000-2001 Juliusz Chroboczek
+Copyright © 1998 Egbert Eich
+Copyright © 2006-2007 Intel Corporation
+Copyright © 2006 Nokia Corporation
+Copyright © 2006-2008 Peter Hutterer
+Copyright © 2006 Adam Jackson
+Copyright © 2009 NVIDIA Corporation
+Copyright © 1999 Keith Packard
+Copyright © 2007-2009 Red Hat, Inc.
+Copyright © 2005-2008 Daniel Stone
+Copyright © 2006-2009 Simon Thum
+Copyright © 1987, 2003-2006, 2008-2009 Sun Microsystems, Inc.
+Copyright © 2006 Luc Verhaegen
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+
+
+
+The following licenses are 'legacy': usually MIT/X11 licenses with the name
+of the copyright holder(s) in the license statement, but also some BSD-like
+licenses.
+
+
+Copyright (C) 1994-2003 The XFree86 Project, Inc.  All Rights Reserved.
+Copyright (C) Colin Harrison 2005-2008
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
+NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the XFree86 Project shall not
+be used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the XFree86 Project.
+
+
+Copyright 1997 by The XFree86 Project, Inc.
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of the XFree86 Project, Inc.
+not be used in advertising or publicity pertaining to distribution of
+the software without specific, written prior permission.  The Xfree86
+Project, Inc. makes no representations about the suitability of this
+software for any purpose.  It is provided "as is" without express or
+implied warranty.
+
+THE XFREE86 PROJECT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD
+TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL OREST ZBOROWSKI OR DAVID WEXELBLAT BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 1985-1998, 2001  The Open Group
+Copyright 2002 Red Hat Inc., Durham, North Carolina.
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+
+Copyright (c) 1987, 1989-1990, 1992-1995  X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright © 1999-2000 SuSE, Inc.
+Copyright © 2007 Red Hat, Inc.
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of SuSE not be used in advertising or
+publicity pertaining to distribution of the software without specific,
+written prior permission.  SuSE makes no representations about the
+suitability of this software for any purpose.  It is provided "as is"
+without express or implied warranty.
+
+SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE
+BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 1987-1991, 1993 by Digital Equipment Corporation, Maynard, Massachusetts.
+Copyright 1991 Massachusetts Institute of Technology, Cambridge, Massachusetts.
+Copyright 1991, 1993 Olivetti Research Limited, Cambridge, England.
+
+                        All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
+Copyright 1994 Quarterdeck Office Systems.
+
+                        All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the names of Digital and
+Quarterdeck not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+DIGITAL AND QUARTERDECK DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT
+OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
+OR PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 1997 Digital Equipment Corporation.
+All rights reserved.
+
+This software is furnished under license and may be used and copied only in
+accordance with the following terms and conditions.  Subject to these
+conditions, you may download, copy, install, use, modify and distribute
+this software in source and/or binary form. No title or ownership is
+transferred hereby.
+
+1) Any source code used, modified or distributed must reproduce and retain
+   this copyright notice and list of conditions as they appear in the
+   source file.
+
+2) No right is granted to use any trade name, trademark, or logo of Digital
+   Equipment Corporation. Neither the "Digital Equipment Corporation"
+   name nor any trademark or logo of Digital Equipment Corporation may be
+   used to endorse or promote products derived from this software without
+   the prior written permission of Digital Equipment Corporation.
+
+3) This software is provided "AS-IS" and any express or implied warranties,
+   including but not limited to, any implied warranties of merchantability,
+   fitness for a particular purpose, or non-infringement are disclaimed.
+   In no event shall DIGITAL be liable for any damages whatsoever, and in
+   particular, DIGITAL shall not be liable for special, indirect,
+   consequential, or incidental damages or damages for lost profits, loss
+   of revenue or loss of use, whether such damages arise in contract,
+   negligence, tort, under statute, in equity, at law or otherwise, even
+   if advised of the possibility of such damage.
+
+
+Copyright (c) 1991, 1996-1997 Digital Equipment Corporation, Maynard, Massachusetts.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+DIGITAL EQUIPMENT CORPORATION BE LIABLE FOR ANY CLAIM, DAMAGES, INCLUDING,
+BUT NOT LIMITED TO CONSEQUENTIAL OR INCIDENTAL DAMAGES, OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of Digital Equipment Corporation
+shall not be used in advertising or otherwise to promote the sale, use or other
+dealings in this Software without prior written authorization from Digital
+Equipment Corporation.
+
+
+SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice including the dates of first publication and
+either this permission notice or a reference to
+http://oss.sgi.com/projects/FreeB/
+shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+Copyright (c) 1994, 1995  Hewlett-Packard Company
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL HEWLETT-PACKARD COMPANY BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the Hewlett-Packard
+Company shall not be used in advertising or otherwise to promote the
+sale, use or other dealings in this Software without prior written
+authorization from the Hewlett-Packard Company.
+
+
+Copyright 1989 by Hewlett-Packard Company, Palo Alto, California.
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Hewlett-Packard not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+HEWLETT-PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+HEWLETT-PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+
+Copyright 2001-2004 Red Hat Inc., Durham, North Carolina.
+Copyright (c) 2003 by the XFree86 Project, Inc.
+Copyright 2004-2005 Red Hat Inc., Raleigh, North Carolina.
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation on the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of the Software,
+and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice (including the
+next paragraph) shall be included in all copies or substantial
+portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+Copyright © 2008 Red Hat, Inc.
+Partly based on code Copyright © 2000 SuSE, Inc.
+
+Permission to use, copy, modify, distribute, and sell this software
+and its documentation for any purpose is hereby granted without
+fee, provided that the above copyright notice appear in all copies
+and that both that copyright notice and this permission notice
+appear in supporting documentation, and that the name of Red Hat
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.  Red
+Hat makes no representations about the suitability of this software
+for any purpose.  It is provided "as is" without express or implied
+warranty.
+
+Red Hat DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
+NO EVENT SHALL Red Hat BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of SuSE not be used in advertising or
+publicity pertaining to distribution of the software without specific,
+written prior permission.  SuSE makes no representations about the
+suitability of this software for any purpose.  It is provided "as is"
+without express or implied warranty.
+
+SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE
+BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright © 2006  Red Hat, Inc.
+(C) Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas.
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sub license,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
+RED HAT, INC, OR PRECISION INSIGHT AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+Copyright (c) 1995  X Consortium
+Copyright 2004 Red Hat Inc., Durham, North Carolina.
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation on the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of the Software,
+and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT, THE X CONSORTIUM,
+AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium
+shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in this Software without prior written
+authorization from the X Consortium.
+
+
+Copyright 1998-2000 Precision Insight, Inc., Cedar Park, Texas.
+Copyright 2000 VA Linux Systems, Inc.
+Copyright (c) 2002, 2008, 2009 Apple Computer, Inc.
+Copyright (c) 2003-2004 Torrey T. Lyons.
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sub license, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice (including the
+next paragraph) shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+(C) Copyright IBM Corporation 2003
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+on the rights to use, copy, modify, merge, publish, distribute, sub
+license, and/or sell copies of the Software, and to permit persons to whom
+the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
+VA LINUX SYSTEM, IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+(C) Copyright IBM Corporation 2004-2005
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sub license,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
+IBM,
+AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+Copyright (c) 1997  Metro Link Incorporated
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+Except as contained in this notice, the name of the Metro Link shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from Metro Link.
+
+
+Copyright 1995-1998 by Metro Link, Inc.
+Copyright (c) 1997 Matthieu Herrb
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Metro Link, Inc. not be used in
+advertising or publicity pertaining to distribution of the software without
+specific, written prior permission.  Metro Link, Inc. makes no
+representations about the suitability of this software for any purpose.
+ It is provided "as is" without express or implied warranty.
+
+METRO LINK, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL METRO LINK, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 1998 by Metro Link Incorporated
+
+Permission to use, copy, modify, distribute, and sell this software
+and its documentation for any purpose is hereby granted without fee,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Metro Link
+Incorporated not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.  Metro Link Incorporated makes no representations
+about the suitability of this software for any purpose.  It is
+provided "as is" without express or implied warranty.
+
+METRO LINK INCORPORATED DISCLAIMS ALL WARRANTIES WITH REGARD
+TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS, IN NO EVENT SHALL METRO LINK INCORPORATED BE
+LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
+DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+
+Copyright (c) 2000 by Conectiva S.A. (http://www.conectiva.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+CONECTIVA LINUX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+Except as contained in this notice, the name of Conectiva Linux shall
+not be used in advertising or otherwise to promote the sale, use or other
+dealings in this Software without prior written authorization from
+Conectiva Linux.
+
+
+Copyright (c) 2001, Andy Ritger  aritger@nvidia.com
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+o Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+o Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer
+  in the documentation and/or other materials provided with the
+  distribution.
+o Neither the name of NVIDIA nor the names of its contributors
+  may be used to endorse or promote products derived from this
+  software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
+NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+
+Copyright 1992 Vrije Universiteit, The Netherlands
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of the Vrije Universiteit not be used in
+advertising or publicity pertaining to distribution of the software without
+specific, written prior permission.  The Vrije Universiteit makes no
+representations about the suitability of this software for any purpose.
+It is provided "as is" without express or implied warranty.
+
+The Vrije Universiteit DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL The Vrije Universiteit BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 1998 by Concurrent Computer Corporation
+
+Permission to use, copy, modify, distribute, and sell this software
+and its documentation for any purpose is hereby granted without fee,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Concurrent Computer
+Corporation not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.  Concurrent Computer Corporation makes no representations
+about the suitability of this software for any purpose.  It is
+provided "as is" without express or implied warranty.
+
+CONCURRENT COMPUTER CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD
+TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS, IN NO EVENT SHALL CONCURRENT COMPUTER CORPORATION BE
+LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
+DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+
+Copyright © 2004 Nokia
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Nokia not be used in
+advertising or publicity pertaining to distribution of the software without
+specific, written prior permission. Nokia makes no
+representations about the suitability of this software for any purpose.  It
+is provided "as is" without express or implied warranty.
+
+NOKIA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL NOKIA BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+
+(c)Copyright 1988,1991 Adobe Systems Incorporated.
+All rights reserved.
+
+Permission to use, copy, modify, distribute, and sublicense this software and its
+documentation for any purpose and without fee is hereby granted, provided that
+the above copyright notices appear in all copies and that both those copyright
+notices and this permission notice appear in supporting documentation and that
+the name of Adobe Systems Incorporated not be used in advertising or publicity
+pertaining to distribution of the software without specific, written prior
+permission.  No trademark license to use the Adobe trademarks is hereby
+granted.  If the Adobe trademark "Display PostScript"(tm) is used to describe
+this software, its functionality or for any other purpose, such use shall be
+limited to a statement that this software works in conjunction with the Display
+PostScript system.  Proper trademark attribution to reflect Adobe's ownership
+of the trademark shall be given whenever any such reference to the Display
+PostScript system is made.
+
+ADOBE MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE SOFTWARE FOR ANY
+PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.  ADOBE
+DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-
+INFRINGEMENT OF THIRD PARTY RIGHTS.  IN NO EVENT SHALL ADOBE BE LIABLE TO YOU
+OR ANY OTHER PARTY FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+DAMAGES WHATSOEVER WHETHER IN AN ACTION OF CONTRACT,NEGLIGENCE, STRICT
+LIABILITY OR ANY OTHER ACTION ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.  ADOBE WILL NOT PROVIDE ANY TRAINING OR OTHER
+SUPPORT FOR THE SOFTWARE.
+
+Adobe, PostScript, and Display PostScript are trademarks of Adobe Systems
+Incorporated which may be registered in certain jurisdictions.
+
+
+Copyright 1989 Network Computing Devices, Inc., Mountain View, California.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted, provided
+that the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of N.C.D. not be used in advertising or
+publicity pertaining to distribution of the software without specific,
+written prior permission.  N.C.D. makes no representations about the
+suitability of this software for any purpose.  It is provided "as is"
+without express or implied warranty.
+
+
+Copyright (c) 1987 by the Regents of the University of California
+
+Permission to use, copy, modify, and distribute this
+software and its documentation for any purpose and without
+fee is hereby granted, provided that the above copyright
+notice appear in all copies.  The University of California
+makes no representations about the suitability of this
+software for any purpose.  It is provided "as is" without
+express or implied warranty.
+
+
+Copyright 1992, 1993 Data General Corporation;
+Copyright 1992, 1993 OMRON Corporation
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that the
+above copyright notice appear in all copies and that both that copyright
+notice and this permission notice appear in supporting documentation, and that
+neither the name OMRON or DATA GENERAL be used in advertising or publicity
+pertaining to distribution of the software without specific, written prior
+permission of the party whose name is to be used.  Neither OMRON or
+DATA GENERAL make any representation about the suitability of this software
+for any purpose.  It is provided "as is" without express or implied warranty.
+
+OMRON AND DATA GENERAL EACH DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OMRON OR DATA GENERAL BE LIABLE FOR ANY SPECIAL, INDIRECT
+OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+OF THIS SOFTWARE.
+
+
+Copyright © 1998-2004, 2006 Keith Packard
+Copyright © 2000-2002 Keith Packard, member of The XFree86 Project, Inc.
+Copyright (c) 2002 Apple Computer, Inc.
+Copyright (c) 2003 Torrey T. Lyons.
+All Rights Reserved.
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Keith Packard not be used in
+advertising or publicity pertaining to distribution of the software without
+specific, written prior permission.  Keith Packard makes no
+representations about the suitability of this software for any purpose.  It
+is provided "as is" without express or implied warranty.
+
+KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright © 1999 Keith Packard
+Copyright © 2000 Compaq Computer Corporation
+Copyright © 2002 MontaVista Software Inc.
+Copyright © 2005 OpenedHand Ltd.
+Copyright © 2006 Nokia Corporation
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of the authors and/or copyright holders
+not be used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.  The authors and/or
+copyright holders make no representations about the suitability of this
+software for any purpose.  It is provided "as is" without express or
+implied warranty.
+
+THE AUTHORS AND/OR COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD
+TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS, IN NO EVENT SHALL THE AUTHORS AND/OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 1993 by Davor Matic
+
+Permission to use, copy, modify, distribute, and sell this software
+and its documentation for any purpose is hereby granted without fee,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation.  Davor Matic makes no representations about
+the suitability of this software for any purpose.  It is provided "as
+is" without express or implied warranty.
+
+
+Copyright (C) 2001-2004 Harold L Hunt II All Rights Reserved.
+Copyright (C) Colin Harrison 2005-2008
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL HAROLD L HUNT II BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of Harold L Hunt II
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+from Harold L Hunt II.
+
+
+Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Thomas Roell not be used in
+advertising or publicity pertaining to distribution of the software without
+specific, written prior permission.  Thomas Roell makes no representations
+about the suitability of this software for any purpose.  It is provided
+"as is" without express or implied warranty.
+
+THOMAS ROELL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL THOMAS ROELL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany
+Copyright 1993 by David Wexelblat <dwex@goblin.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the names of Thomas Roell and David Wexelblat
+not be used in advertising or publicity pertaining to distribution of
+the software without specific, written prior permission.  Thomas Roell and
+David Wexelblat makes no representations about the suitability of this
+software for any purpose.  It is provided "as is" without express or
+implied warranty.
+
+THOMAS ROELL AND DAVID WEXELBLAT DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL THOMAS ROELL OR DAVID WEXELBLAT BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 1990,91,92,93 by Thomas Roell, Germany.
+Copyright 1991,92,93    by SGCS (Snitily Graphics Consulting Services), USA.
+
+Permission to use, copy, modify, distribute, and sell this software
+and its documentation for any purpose is hereby granted without fee,
+provided that the above copyright notice appear in all copies and
+that both that copyright notice and this  permission notice appear
+in supporting documentation, and that the name of Thomas Roell nor
+SGCS be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+Thomas Roell nor SGCS makes no representations about the suitability
+of this software for any purpose. It is provided "as is" without
+express or implied warranty.
+
+THOMAS ROELL AND SGCS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL THOMAS ROELL OR SGCS BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 1998 by Alan Hourihane, Wigan, England.
+Copyright 2000-2002 by Alan Hourihane, Flint Mountain, North Wales.
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Alan Hourihane not be used in
+advertising or publicity pertaining to distribution of the software without
+specific, written prior permission.  Alan Hourihane makes no representations
+about the suitability of this software for any purpose.  It is provided
+"as is" without express or implied warranty.
+
+ALAN HOURIHANE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL ALAN HOURIHANE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 1995  Kaleb S. KEITHLEY
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL Kaleb S. KEITHLEY BE LIABLE FOR ANY CLAIM, DAMAGES
+OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of Kaleb S. KEITHLEY
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+from Kaleb S. KEITHLEY
+
+
+Copyright (c) 1997 Matthieu Herrb
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Matthieu Herrb not be used in
+advertising or publicity pertaining to distribution of the software without
+specific, written prior permission.  Matthieu Herrb makes no
+representations about the suitability of this software for any purpose.
+ It is provided "as is" without express or implied warranty.
+
+MATTHIEU HERRB DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL MATTHIEU HERRB BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 2004, Egbert Eich
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+EGBERT EICH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
+NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of Egbert Eich shall not
+be used in advertising or otherwise to promote the sale, use or other deal-
+ings in this Software without prior written authorization from Egbert Eich.
+
+
+Copyright 1993 by David Wexelblat <dwex@goblin.org>
+Copyright 2005 by Kean Johnston <jkj@sco.com>
+Copyright 1993 by David McCullough <davidm@stallion.oz.au>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of David Wexelblat not be used in
+advertising or publicity pertaining to distribution of the software without
+specific, written prior permission.  David Wexelblat makes no representations
+about the suitability of this software for any purpose.  It is provided
+"as is" without express or implied warranty.
+
+DAVID WEXELBLAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL DAVID WEXELBLAT BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 1992 by Orest Zborowski <obz@Kodak.com>
+Copyright 1993 by David Wexelblat <dwex@goblin.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the names of Orest Zborowski and David Wexelblat
+not be used in advertising or publicity pertaining to distribution of
+the software without specific, written prior permission.  Orest Zborowski
+and David Wexelblat make no representations about the suitability of this
+software for any purpose.  It is provided "as is" without express or
+implied warranty.
+
+OREST ZBOROWSKI AND DAVID WEXELBLAT DISCLAIMS ALL WARRANTIES WITH REGARD
+TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL OREST ZBOROWSKI OR DAVID WEXELBLAT BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 1992 by Orest Zborowski <obz@Kodak.com>
+Copyright 1993 by David Dawes <dawes@xfree86.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the names of Orest Zborowski and David Dawes
+not be used in advertising or publicity pertaining to distribution of
+the software without specific, written prior permission.  Orest Zborowski
+and David Dawes make no representations about the suitability of this
+software for any purpose.  It is provided "as is" without express or
+implied warranty.
+
+OREST ZBOROWSKI AND DAVID DAWES DISCLAIMS ALL WARRANTIES WITH REGARD
+TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL OREST ZBOROWSKI OR DAVID DAWES BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 1995-1999 by Frederic Lepied, France. <fred@sugix.frmug.fr.net>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is  hereby granted without fee, provided that
+the  above copyright   notice appear  in   all  copies and  that both  that
+copyright  notice   and   this  permission   notice  appear  in  supporting
+documentation, and that   the  name of  Frederic   Lepied not  be  used  in
+advertising or publicity pertaining to distribution of the software without
+specific,  written      prior  permission.     Frederic  Lepied   makes  no
+representations about the suitability of this software for any purpose.  It
+is provided "as is" without express or implied warranty.
+
+FREDERIC  LEPIED DISCLAIMS ALL   WARRANTIES WITH REGARD  TO  THIS SOFTWARE,
+INCLUDING ALL IMPLIED   WARRANTIES OF MERCHANTABILITY  AND   FITNESS, IN NO
+EVENT  SHALL FREDERIC  LEPIED BE   LIABLE   FOR ANY  SPECIAL, INDIRECT   OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA  OR PROFITS, WHETHER  IN  AN ACTION OF  CONTRACT,  NEGLIGENCE OR OTHER
+TORTIOUS  ACTION, ARISING    OUT OF OR   IN  CONNECTION  WITH THE USE    OR
+PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 1992 by Rich Murphey <Rich@Rice.edu>
+Copyright 1993 by David Wexelblat <dwex@goblin.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the names of Rich Murphey and David Wexelblat
+not be used in advertising or publicity pertaining to distribution of
+the software without specific, written prior permission.  Rich Murphey and
+David Wexelblat make no representations about the suitability of this
+software for any purpose.  It is provided "as is" without express or
+implied warranty.
+
+RICH MURPHEY AND DAVID WEXELBLAT DISCLAIM ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL RICH MURPHEY OR DAVID WEXELBLAT BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 1992 by Rich Murphey <Rich@Rice.edu>
+Copyright 1993 by David Dawes <dawes@xfree86.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the names of Rich Murphey and David Dawes
+not be used in advertising or publicity pertaining to distribution of
+the software without specific, written prior permission.  Rich Murphey and
+David Dawes make no representations about the suitability of this
+software for any purpose.  It is provided "as is" without express or
+implied warranty.
+
+RICH MURPHEY AND DAVID DAWES DISCLAIM ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL RICH MURPHEY OR DAVID DAWES BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright © 2003-2004 Anders Carlsson
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Anders Carlsson not be used in
+advertising or publicity pertaining to distribution of the software without
+specific, written prior permission.  Anders Carlsson makes no
+representations about the suitability of this software for any purpose.  It
+is provided "as is" without express or implied warranty.
+
+ANDERS CARLSSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL ANDERS CARLSSON BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright (C) 2003 Anders Carlsson
+Copyright © 2003-2004 Eric Anholt
+Copyright © 2004 Keith Packard
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Eric Anholt not be used in
+advertising or publicity pertaining to distribution of the software without
+specific, written prior permission.  Eric Anholt makes no
+representations about the suitability of this software for any purpose.  It
+is provided "as is" without express or implied warranty.
+
+ERIC ANHOLT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL ERIC ANHOLT BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright © 2004 PillowElephantBadgerBankPond
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of PillowElephantBadgerBankPond not be used in
+advertising or publicity pertaining to distribution of the software without
+specific, written prior permission.  PillowElephantBadgerBankPond makes no
+representations about the suitability of this software for any purpose.  It
+is provided "as is" without express or implied warranty.
+
+PillowElephantBadgerBankPond DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL PillowElephantBadgerBankPond BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
+WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
+FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright © 2003-2004 Philip Blundell
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Philip Blundell not be used in
+advertising or publicity pertaining to distribution of the software without
+specific, written prior permission.  Philip Blundell makes no
+representations about the suitability of this software for any purpose.  It
+is provided "as is" without express or implied warranty.
+
+PHILIP BLUNDELL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL PHILIP BLUNDELL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+
+
+Copyright (c) 1994-2003 by The XFree86 Project, Inc.
+Copyright 1997 by Metro Link, Inc.
+Copyright 2003 by David H. Dawes.
+Copyright 2003 by X-Oz Technologies.
+Copyright (c) 2004, X.Org Foundation
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the copyright holder(s)
+and author(s) shall not be used in advertising or otherwise to promote
+the sale, use or other dealings in this Software without prior written
+authorization from the copyright holder(s) and author(s).
+
+
+Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany
+Copyright 1993 by David Wexelblat <dwex@goblin.org>
+Copyright 1999 by David Holland <davidh@iquest.net>
+Copyright © 2000 Compaq Computer Corporation
+Copyright © 2002 Hewlett-Packard Company
+Copyright © 2004, 2005 Red Hat, Inc.
+Copyright © 2004 Nicholas Miell
+Copyright © 2005 Trolltech AS
+Copyright © 2006 Intel Corporation
+Copyright © 2006-2007 Keith Packard
+Copyright © 2008 Red Hat, Inc
+Copyright © 2008 George Sapountzis <gsap7@yahoo.gr>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that copyright
+notice and this permission notice appear in supporting documentation, and
+that the name of the copyright holders not be used in advertising or
+publicity pertaining to distribution of the software without specific,
+written prior permission.  The copyright holders make no representations
+about the suitability of this software for any purpose.  It is provided "as
+is" without express or implied warranty.
+
+THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+
+Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
+            2005 Lars Knoll & Zack Rusin, Trolltech
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of Keith Packard not be used in
+advertising or publicity pertaining to distribution of the software without
+specific, written prior permission.  Keith Packard makes no
+representations about the suitability of this software for any purpose.  It
+is provided "as is" without express or implied warranty.
+
+THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+
+Copyright 1987, 1998  The Open Group
+Copyright © 1998-1999, 2001 The XFree86 Project, Inc.
+Copyright © 2000 VA Linux Systems, Inc.
+Copyright (c) 2000, 2001 Nokia Home Communications
+Copyright © 2007, 2008 Red Hat, Inc.
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, and/or sell copies of the Software, and to permit persons
+to whom the Software is furnished to do so, provided that the above
+copyright notice(s) and this permission notice appear in all copies of
+the Software and that both the above copyright notice(s) and this
+permission notice appear in supporting documentation.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
+INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
+FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+of the copyright holder.
+
+
+Copyright 1996 by Thomas E. Dickey <dickey@clark.net>
+
+                        All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of the above listed
+copyright holder(s) not be used in advertising or publicity pertaining
+to distribution of the software without specific, written prior
+permission.
+
+THE ABOVE LISTED COPYRIGHT HOLDER(S) DISCLAIM ALL WARRANTIES WITH REGARD
+TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS, IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE
+LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas.
+Copyright (c) 2001 Andreas Monitzer.
+Copyright (c) 2001-2004 Greg Parker.
+Copyright (c) 2001-2004 Torrey T. Lyons
+Copyright (c) 2002-2003 Apple Computer, Inc.
+Copyright (c) 2004-2005 Alexander Gottwald
+Copyright (c) 2002-2009 Apple Inc.
+Copyright (c) 2007 Jeremy Huddleston
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name(s) of the above copyright
+holders shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in this Software without prior written authorization.
+
+
+Copyright (C) 1999,2000 by Eric Sunshine <sunshine@sunshineco.com>
+Copyright (C) 2001-2005 by Thomas Winischhofer, Vienna, Austria.
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. The name of the author may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+Copyright (C) 2005 Bogdan D. bogdand@users.sourceforge.net
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the author shall not be used in
+advertising or otherwise to promote the sale, use or other dealings in this
+Software without prior written authorization from the author.
+
+
+Copyright © 2002 David Dawes
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+Except as contained in this notice, the name of the author(s) shall
+not be used in advertising or otherwise to promote the sale, use or other
+dealings in this Software without prior written authorization from
+the author(s).
+
+
+Copyright (C) 1996-1999 SciTech Software, Inc.
+Copyright (C) David Mosberger-Tang
+Copyright (C) 1999 Egbert Eich
+Copyright (C) 2008 Bart Trojanowski, Symbio Technologies, LLC
+
+Permission to use, copy, modify, distribute, and sell this software and
+its documentation for any purpose is hereby granted without fee,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of the authors not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.  The authors makes no
+representations about the suitability of this software for any purpose.
+It is provided "as is" without express or implied warranty.
+
+THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 2005-2006 Luc Verhaegen.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+
+Copyright 1995 by Robin Cutshaw <robin@XFree86.Org>
+Copyright 2000 by Egbert Eich
+Copyright 2002 by David Dawes
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the names of the above listed copyright holder(s)
+not be used in advertising or publicity pertaining to distribution of
+the software without specific, written prior permission.  The above listed
+copyright holder(s) make(s) no representations about the suitability of this
+software for any purpose.  It is provided "as is" without express or
+implied warranty.
+
+THE ABOVE LISTED COPYRIGHT HOLDER(S) DISCLAIM(S) ALL WARRANTIES WITH REGARD
+TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS, IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE
+LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
+DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 1997-2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that copyright
+notice and this permission notice appear in supporting documentation, and
+that the name of Marc Aurele La France not be used in advertising or
+publicity pertaining to distribution of the software without specific,
+written prior permission.  Marc Aurele La France makes no representations
+about the suitability of this software for any purpose.  It is provided
+"as-is" without express or implied warranty.
+
+MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO
+EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+OF THIS SOFTWARE.
+
+
+Copyright 1990, 1991 by Thomas Roell, Dinkelscherben, Germany
+Copyright 1992 by David Dawes <dawes@XFree86.org>
+Copyright 1992 by Jim Tsillas <jtsilla@damon.ccs.northeastern.edu>
+Copyright 1992 by Rich Murphey <Rich@Rice.edu>
+Copyright 1992 by Robert Baron <Robert.Baron@ernst.mach.cs.cmu.edu>
+Copyright 1992 by Orest Zborowski <obz@eskimo.com>
+Copyright 1993 by Vrije Universiteit, The Netherlands
+Copyright 1993 by David Wexelblat <dwex@XFree86.org>
+Copyright 1994, 1996 by Holger Veit <Holger.Veit@gmd.de>
+Copyright 1997 by Takis Psarogiannakopoulos <takis@dpmms.cam.ac.uk>
+Copyright 1994-2003 by The XFree86 Project, Inc
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the names of the above listed copyright holders
+not be used in advertising or publicity pertaining to distribution of
+the software without specific, written prior permission.  The above listed
+copyright holders make no representations about the suitability of this
+software for any purpose.  It is provided "as is" without express or
+implied warranty.
+
+THE ABOVE LISTED COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD
+TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS, IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDERS BE
+LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
+DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright 2001-2005 by J. Kean Johnston <jkj@sco.com>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name J. Kean Johnston not be used in
+advertising or publicity pertaining to distribution of the software without
+specific, written prior permission.  J. Kean Johnston makes no
+representations about the suitability of this software for any purpose.
+It is provided "as is" without express or implied warranty.
+
+J. KEAN JOHNSTON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL J. KEAN JOHNSTON BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright (C) 2000 Jakub Jelinek (jakub@redhat.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+JAKUB JELINEK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+Copyright 1997,1998 by UCHIYAMA Yasushi
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of UCHIYAMA Yasushi not be used in
+advertising or publicity pertaining to distribution of the software without
+specific, written prior permission.  UCHIYAMA Yasushi makes no representations
+about the suitability of this software for any purpose.  It is provided
+"as is" without express or implied warranty.
+
+UCHIYAMA YASUSHI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL UCHIYAMA YASUSHI BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright (C) 2000 Keith Packard
+              2004 Eric Anholt
+              2005 Zack Rusin
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of copyright holders not be used in
+advertising or publicity pertaining to distribution of the software without
+specific, written prior permission. Copyright holders make no
+representations about the suitability of this software for any purpose.  It
+is provided "as is" without express or implied warranty.
+
+THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+
+(C) Copyright IBM Corporation 2002-2007
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+on the rights to use, copy, modify, merge, publish, distribute, sub
+license, and/or sell copies of the Software, and to permit persons to whom
+the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
+THE COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+this permission notice appear in supporting documentation.  This permission
+notice shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+Copyright © 2007 OpenedHand Ltd
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation, and that the name of OpenedHand Ltd not be used in
+advertising or publicity pertaining to distribution of the software without
+specific, written prior permission. OpenedHand Ltd makes no
+representations about the suitability of this software for any purpose.  It
+is provided "as is" without express or implied warranty.
+
+OpenedHand Ltd DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL OpenedHand Ltd BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+
+Copyright (c) 1989, 1990, 1993, 1994
+     The Regents of the University of California.  All rights reserved.
+
+This code is derived from software contributed to Berkeley by
+Chris Torek.
+
+This code is derived from software contributed to Berkeley by
+Michael Rendell of Memorial University of Newfoundland.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+ 4. Neither the name of the University nor the names of its contributors
+    may be used to endorse or promote products derived from this software
+    without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+</pre>
+<div class="license-packages">
+Used by these packages: x11-base/X.Org-1.9.3
+</div>
+</div>
+</div>
+
+<div class="product">
+<a name="ZLIB" class="title">Gentoo Package Provided Stock License ZLIB</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -17138,9 +18922,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="as-is">Gentoo Package Provided Stock License as-is</a>
-</span>
+<a name="as-is" class="title">Gentoo Package Provided Stock License as-is</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -17180,15 +18962,13 @@
 
 </pre>
 <div class="license-packages">
-Used by these packages: app-arch/xz-utils-4.999.9_beta app-text/docbook-xml-dtd-4.1.2-r6 dev-db/sqlite-3.6.22-r3 dev-lang/swig-2.0.4-r1 media-libs/libpng-1.2.49-r1 net-misc/openssh-5.2_p1-r10 net-wireless/crda-1.1.1-r1 net-wireless/iw-3.6-r1 net-wireless/wireless-regdb-20101124-r1 sys-apps/hdparm-9.20
+Used by these packages: net-wireless/crda-1.1.1-r1 app-text/docbook-xml-dtd-4.1.2-r6 sys-apps/hdparm-9.20 net-wireless/iw-3.6-r1 media-libs/libpng-1.2.49-r1 net-misc/openssh-5.2_p1-r10 dev-db/sqlite-3.6.22-r3 dev-lang/swig-2.0.4-r1 net-wireless/wireless-regdb-20101124-r1 app-arch/xz-utils-4.999.9_beta
 </div>
 </div>
 </div>
 
 <div class="product">
-<span class="title">
-<a name="boehm-gc">Gentoo Package Provided Stock License boehm-gc</a>
-</span>
+<a name="boehm-gc" class="title">Gentoo Package Provided Stock License boehm-gc</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -17211,9 +18991,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="fontconfig">Gentoo Package Provided Stock License fontconfig</a>
-</span>
+<a name="fontconfig" class="title">Gentoo Package Provided Stock License fontconfig</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -17248,9 +19026,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="icu">Gentoo Package Provided Stock License icu</a>
-</span>
+<a name="icu" class="title">Gentoo Package Provided Stock License icu</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -17297,9 +19073,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="lsof">Gentoo Package Provided Stock License lsof</a>
-</span>
+<a name="lsof" class="title">Gentoo Package Provided Stock License lsof</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -17340,9 +19114,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="ncurses">Gentoo Package Provided Stock License ncurses</a>
-</span>
+<a name="ncurses" class="title">Gentoo Package Provided Stock License ncurses</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -17380,9 +19152,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="netiface">Gentoo Package Provided Stock License netiface</a>
-</span>
+<a name="netiface" class="title">Gentoo Package Provided Stock License netiface</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -17414,9 +19184,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="openssl">Gentoo Package Provided Stock License openssl</a>
-</span>
+<a name="openssl" class="title">Gentoo Package Provided Stock License openssl</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -17556,9 +19324,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="ppp-2.4.4">Gentoo Package Provided Stock License ppp-2.4.4</a>
-</span>
+<a name="ppp-2.4.4" class="title">Gentoo Package Provided Stock License ppp-2.4.4</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -20382,9 +22148,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="public-domain">Gentoo Package Provided Stock License public-domain</a>
-</span>
+<a name="public-domain" class="title">Gentoo Package Provided Stock License public-domain</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -20392,15 +22156,13 @@
 
 </pre>
 <div class="license-packages">
-Used by these packages: app-arch/xz-utils-4.999.9_beta sys-apps/util-linux-2.21.2-r1 sys-libs/timezone-data-2012j
+Used by these packages: sys-libs/timezone-data-2012j sys-apps/util-linux-2.21.2-r1 app-arch/xz-utils-4.999.9_beta
 </div>
 </div>
 </div>
 
 <div class="product">
-<span class="title">
-<a name="ralink-firmware">Gentoo Package Provided Stock License ralink-firmware</a>
-</span>
+<a name="ralink-firmware" class="title">Gentoo Package Provided Stock License ralink-firmware</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -20452,9 +22214,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="unRAR">Gentoo Package Provided Stock License unRAR</a>
-</span>
+<a name="unRAR" class="title">Gentoo Package Provided Stock License unRAR</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
@@ -20509,9 +22269,7 @@
 </div>
 
 <div class="product">
-<span class="title">
-<a name="vim">Gentoo Package Provided Stock License vim</a>
-</span>
+<a name="vim" class="title">Gentoo Package Provided Stock License vim</a>
 <a class="show" href="#" onclick="return toggle(this);">show license text</a>
 <div class="licence">
 <pre>
diff --git a/chrome/browser/resources/chromeos/login/screen_locally_managed_user_creation.js b/chrome/browser/resources/chromeos/login/screen_locally_managed_user_creation.js
index e27102a..09753e0 100644
--- a/chrome/browser/resources/chromeos/login/screen_locally_managed_user_creation.js
+++ b/chrome/browser/resources/chromeos/login/screen_locally_managed_user_creation.js
@@ -502,7 +502,6 @@
           [managerId, managerPassword]);
     },
 
-
     /**
      * Does sanity check and calls backend with user display name/password pair
      * to create a user.
diff --git a/chrome/browser/resources/chromeos/network.html b/chrome/browser/resources/chromeos/network.html
index b991ce0..cfcb7c3 100644
--- a/chrome/browser/resources/chromeos/network.html
+++ b/chrome/browser/resources/chromeos/network.html
@@ -23,6 +23,8 @@
     <label for="log-event" i18n-content="logLevelEventText"></label>
     <input id="log-debug" type="checkbox">
     <label for="log-debug" i18n-content="logLevelDebugText"></label>
+    <input id="log-fileinfo" type="checkbox">
+    <label for="log-fileinfo" i18n-content="logLevelFileinfoText"></label>
   </div>
   <div id="network-log-container">
   </div>
diff --git a/chrome/browser/resources/chromeos/network.js b/chrome/browser/resources/chromeos/network.js
index 593c6eb..9c55d57 100644
--- a/chrome/browser/resources/chromeos/network.js
+++ b/chrome/browser/resources/chromeos/network.js
@@ -51,9 +51,13 @@
       return null;
     var res = document.createElement('p');
     var textWrapper = document.createElement('span');
+    var fileinfo = '';
+    if ($('log-fileinfo').checked)
+      fileinfo = logEntry['file'];
     textWrapper.textContent = loadTimeData.getStringF(
       'logEntryFormat',
       logEntry['timestamp'],
+      fileinfo,
       logEntry['event'],
       logEntry['description']);
     res.appendChild(createLevelTag(level));
@@ -162,6 +166,8 @@
     $('log-event').onclick = sendRefresh;
     $('log-debug').checked = false;
     $('log-debug').onclick = sendRefresh;
+    $('log-fileinfo').checked = false;
+    $('log-fileinfo').onclick = sendRefresh;
     setRefresh();
     sendRefresh();
   });
diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd
index cba4ccc..a779bca 100644
--- a/chrome/browser/resources/component_extension_resources.grd
+++ b/chrome/browser/resources/component_extension_resources.grd
@@ -70,7 +70,7 @@
         <include name="IDR_FILE_MANAGER_ACTION_CHOICE_SCRIPT_JS" file="file_manager/js/action_choice_scripts.js" flattenhtml="true" type="BINDATA" />
 
         <!-- Scripts working in background page. -->
-        <include name="IDR_FILE_MANAGER_FILE_COPY_MANAGER_JS" file="file_manager/js/file_copy_manager.js" flattenhtml="false" type="BINDATA" />
+        <include name="IDR_FILE_MANAGER_FILE_OPERATION_MANAGER_JS" file="file_manager/js/file_operation_manager.js" flattenhtml="false" type="BINDATA" />
         <include name="IDR_FILE_MANAGER_ASYNC_UTIL_JS" file="file_manager/js/async_util.js" flattenhtml="false" type="BINDATA" />
         <include name="IDR_FILE_MANAGER_PATH_UTIL_JS" file="file_manager/js/path_util.js" flattenhtml="false" type="BINDATA" />
         <include name="IDR_FILE_MANAGER_UTIL_JS" file="file_manager/js/util.js" flattenhtml="false" type="BINDATA" />
diff --git a/chrome/browser/resources/extensions/extension_list.js b/chrome/browser/resources/extensions/extension_list.js
index 59f5d00..605c810 100644
--- a/chrome/browser/resources/extensions/extension_list.js
+++ b/chrome/browser/resources/extensions/extension_list.js
@@ -207,13 +207,6 @@
             chrome.send('extensionSettingsLaunch', [extension.id]);
           });
           launch.hidden = false;
-
-          // The 'Restart' link.
-          var restart = node.querySelector('.restart-link');
-          restart.addEventListener('click', function(e) {
-            chrome.send('extensionSettingsRestart', [extension.id]);
-          });
-          restart.hidden = false;
         }
       }
 
diff --git a/chrome/browser/resources/extensions/extensions.css b/chrome/browser/resources/extensions/extensions.css
index 98bbdfe..b0745d7 100644
--- a/chrome/browser/resources/extensions/extensions.css
+++ b/chrome/browser/resources/extensions/extensions.css
@@ -2,18 +2,6 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
 
-html,
-body {
-  height: 100%;
-  overflow: hidden;
-}
-
-@media print {
-  body {
-    height: auto;
-  }
-}
-
 html.loading * {
   -webkit-transition-duration: 0 !important;
 }
@@ -24,6 +12,10 @@
   border-color: rgba(0, 0, 0, 0.25);
 }
 
+.no-scroll {
+  overflow-y: hidden;
+}
+
 /* Developer mode */
 
 #dev-controls {
@@ -102,11 +94,6 @@
 
 /* Contents */
 
-#page-container {
-  height: 100%;
-  overflow-y: auto;
-}
-
 #extension-settings {
   max-width: 738px;
 }
diff --git a/chrome/browser/resources/extensions/extensions.html b/chrome/browser/resources/extensions/extensions.html
index a381365..5522ad9 100644
--- a/chrome/browser/resources/extensions/extensions.html
+++ b/chrome/browser/resources/extensions/extensions.html
@@ -54,62 +54,60 @@
   </div>
 </div>
 
-<div id="page-container">
-  <div class="page" id="extension-settings">
-    <header id="page-header"><h1 i18n-content="extensionSettings"></h1>
-      <div id="header-controls" class="header-extras">
-        <div id="dev-toggle" class="checkbox">
-          <label>
-            <input id="toggle-dev-on" type="checkbox">
-            <span i18n-content="extensionSettingsDeveloperMode"></span>
-          </label>
-        </div>
+<div class="page" id="extension-settings">
+  <header id="page-header"><h1 i18n-content="extensionSettings"></h1>
+    <div id="header-controls" class="header-extras">
+      <div id="dev-toggle" class="checkbox">
+        <label>
+          <input id="toggle-dev-on" type="checkbox">
+          <span i18n-content="extensionSettingsDeveloperMode"></span>
+        </label>
       </div>
-      <div class="page-banner profile-is-managed-banner">
-        <div class="page-banner-gradient">
-          <span class="page-banner-text"
-              i18n-content="extensionSettingsManagedMode"></span>
-        </div>
+    </div>
+    <div class="page-banner profile-is-managed-banner">
+      <div class="page-banner-gradient">
+        <span class="page-banner-text"
+            i18n-content="extensionSettingsManagedMode"></span>
       </div>
-    </header>
-    <div id="dev-controls" hidden>
-      <div id="apps-dev-tool-banner">
-        <div class="apps-dev-tool-banner-text"
-            i18n-content="extensionSettingsUseAppsDevTools"></div>
-        <button id="open-apps-dev-tools"
-            i18n-content="extensionSettingsOpenAppsDevTools"></button>
-      </div>
-      <div class="buttons-container">
-        <button id="load-unpacked"
-            i18n-content="extensionSettingsLoadUnpackedButton"></button>
-        <button id="pack-extension"
-            i18n-content="extensionSettingsPackButton"></button>
+    </div>
+  </header>
+  <div id="dev-controls" hidden>
+    <div id="apps-dev-tool-banner">
+      <div class="apps-dev-tool-banner-text"
+          i18n-content="extensionSettingsUseAppsDevTools"></div>
+      <button id="open-apps-dev-tools"
+          i18n-content="extensionSettingsOpenAppsDevTools"></button>
+    </div>
+    <div class="buttons-container">
+      <button id="load-unpacked"
+          i18n-content="extensionSettingsLoadUnpackedButton"></button>
+      <button id="pack-extension"
+          i18n-content="extensionSettingsPackButton"></button>
 <if expr="pp_ifdef('chromeos')">
-        <button id="add-kiosk-app"
-            i18n-content="addKioskAppButton" hidden></button>
+      <button id="add-kiosk-app"
+          i18n-content="addKioskAppButton" hidden></button>
 </if>
-        <div id="dev-controls-spacer"></div>
-        <button id="update-extensions-now"
-            i18n-content="extensionSettingsUpdateButton"></button>
-      </div>
+      <div id="dev-controls-spacer"></div>
+      <button id="update-extensions-now"
+          i18n-content="extensionSettingsUpdateButton"></button>
     </div>
-    <div id="extension-settings-list" class="empty-extension-list"></div>
-    <div id="no-extensions">
-      <span id="no-extensions-message"
-          i18n-content="extensionSettingsNoExtensions"></span>
-      <span id="suggest-gallery" class="more-extensions-link"
-          i18n-values=".innerHTML:extensionSettingsSuggestGallery">
-      </span>
-    </div>
-    <div id="footer-section">
-      <a target="_blank" class="more-extensions-link"
-          i18n-values="href:extensionSettingsGetMoreExtensionsUrl"
-          i18n-content="extensionSettingsGetMoreExtensions"></a>
-      <a target="_blank" hidden
-          class="extension-commands-config"
-          i18n-content="extensionSettingsCommandsLink"
-          href="#"></a>
-    </div>
+  </div>
+  <div id="extension-settings-list" class="empty-extension-list"></div>
+  <div id="no-extensions">
+    <span id="no-extensions-message"
+        i18n-content="extensionSettingsNoExtensions"></span>
+    <span id="suggest-gallery" class="more-extensions-link"
+        i18n-values=".innerHTML:extensionSettingsSuggestGallery">
+    </span>
+  </div>
+  <div id="footer-section">
+    <a target="_blank" class="more-extensions-link"
+        i18n-values="href:extensionSettingsGetMoreExtensionsUrl"
+        i18n-content="extensionSettingsGetMoreExtensions"></a>
+    <a target="_blank" hidden
+        class="extension-commands-config"
+        i18n-content="extensionSettingsCommandsLink"
+        href="#"></a>
   </div>
 </div>
 
@@ -176,8 +174,6 @@
                 href="#" hidden></a>
             <a class="launch-link" i18n-content="extensionSettingsLaunch"
                 href="#" hidden></a>
-            <a class="restart-link" i18n-content="extensionSettingsRestart"
-                href="#" hidden></a>
           </span>
           <a class="options-link" i18n-content="extensionSettingsOptions"
               href="#" hidden></a>
diff --git a/chrome/browser/resources/extensions/extensions.js b/chrome/browser/resources/extensions/extensions.js
index b1d5a57..f71292e 100644
--- a/chrome/browser/resources/extensions/extensions.js
+++ b/chrome/browser/resources/extensions/extensions.js
@@ -356,6 +356,15 @@
    *     are hidden.
    */
   ExtensionSettings.showOverlay = function(node) {
+    var pageDiv = $('extension-settings');
+    if (node) {
+      pageDiv.style.width = window.getComputedStyle(pageDiv).width;
+      document.body.classList.add('no-scroll');
+    } else {
+      document.body.classList.remove('no-scroll');
+      pageDiv.style.width = '';
+    }
+
     var currentlyShowingOverlay = ExtensionSettings.getCurrentOverlay();
     if (currentlyShowingOverlay)
       currentlyShowingOverlay.classList.remove('showing');
diff --git a/chrome/browser/resources/file_manager/css/common.css b/chrome/browser/resources/file_manager/css/common.css
index 5cff23a..c60b844 100644
--- a/chrome/browser/resources/file_manager/css/common.css
+++ b/chrome/browser/resources/file_manager/css/common.css
@@ -172,7 +172,7 @@
   overflow: hidden;
   padding: 5px 0;
   transition: opacity 200ms ease-in;
-  z-index: 15;
+  z-index: 600;  /* Must be below the overlay pane (1000). */
 }
 
 menu.chrome-menu[hidden] {
diff --git a/chrome/browser/resources/file_manager/css/file_manager.css b/chrome/browser/resources/file_manager/css/file_manager.css
index 8597e93..9cab41f 100644
--- a/chrome/browser/resources/file_manager/css/file_manager.css
+++ b/chrome/browser/resources/file_manager/css/file_manager.css
@@ -59,7 +59,7 @@
   right: 0;
   top: 0;
   width: 10px;
-  z-index: 1000;
+  z-index: 500;  /* Must be below the contextmenu (600). */
 }
 
 .scrollbar-button {
@@ -169,7 +169,7 @@
 }
 
 /* Directory tree at the left. */
-.dialog-sidebar {
+.dialog-navigation-list {
   -webkit-border-end: 1px solid rgb(225, 225, 225);
   -webkit-box-flex: 0;
   -webkit-box-orient: vertical;
@@ -182,7 +182,7 @@
   width: 150px;
 }
 
-.dialog-sidebar-header {
+.dialog-navigation-list-header {
   -webkit-app-region: drag;
   -webkit-box-flex: 0;
   display: -webkit-box;
@@ -190,19 +190,19 @@
   line-height: 45px;
 }
 
-.dialog-sidebar-header #app-name {
+.dialog-navigation-list-header #app-name {
   -webkit-margin-start: 15px;
   color: #303030;
   font-size: 130%;
 }
 
-.dialog-sidebar-contents {
+.dialog-navigation-list-contents {
   -webkit-box-flex: 1;
   display: -webkit-box;
   position: relative;
 }
 
-.dialog-sidebar-footer {
+.dialog-navigation-list-footer {
   -webkit-box-flex: 0;
   display: -webkit-box;
 }
@@ -216,38 +216,38 @@
   margin-right: -3px;
   position: relative;
   width: 6px;
-  z-index: 100;
+  z-index: 500;  /* Must be below the contextmenu (600). */
 }
 
-#volume-list {
+#navigation-list {
   -webkit-box-flex: 1;
   -webkit-box-orient: vertical;
   display: -webkit-box;
 }
 
-#volume-list > * {
+#navigation-list > * {
   height: 40px;
   padding: 0 5px;
 }
 
-#volume-list > .accepts,
-#volume-list > [lead][selected],
-#volume-list > [lead],
-#volume-list > [selected],
-#volume-list > [anchor] {
+#navigation-list > .accepts,
+#navigation-list > [lead][selected],
+#navigation-list > [lead],
+#navigation-list > [selected],
+#navigation-list > [anchor] {
   background-color: rgb(225, 225, 225);
 }
 
-#volume-list:focus > .accepts,
-#volume-list:focus > [lead][selected],
-#volume-list:focus > [lead],
-#volume-list:focus > [selected],
-#volume-list:focus > [anchor] {
+#navigation-list:focus > .accepts,
+#navigation-list:focus > [lead][selected],
+#navigation-list:focus > [lead],
+#navigation-list:focus > [selected],
+#navigation-list:focus > [anchor] {
   background-color: rgb(66, 129, 244);
   color: white;
 }
 
-#volume-list li.root-item {
+#navigation-list li.root-item {
   -webkit-box-align: center;
   display: -webkit-box;
   line-height: 22px;  /* To accomodate for icons. */
@@ -256,7 +256,7 @@
 
 /* TODO(yoshiki): Animation is temporary disabled for volumes. Add animation
    back. crbug.com/276132. */
-#volume-list li[item-type=shortcut].root-item {
+#navigation-list li.root-item.fadein {
   -webkit-animation: fadeIn 150ms ease-in-out;
 }
 
@@ -264,12 +264,12 @@
    added to elements.
    Must keep the class name 'fadeout' and the animation name 'fadeOut' in sync
    with JavaScript. */
-#volume-list li.root-item.fadeout {
+#navigation-list li.root-item.fadeout {
   -webkit-animation: fadeOut 150ms ease-in-out 0 1 normal both;
   opacity: 0;
 }
 
-#volume-list li.root-item > .root-label {
+#navigation-list li.root-item > .root-label {
   -webkit-box-flex: 1;
   margin: 0 2px;
   overflow: hidden;
@@ -277,7 +277,7 @@
   white-space: nowrap;
 }
 
-#volume-list .volume-icon {
+#navigation-list .volume-icon {
   background-position: center 2px;
   background-repeat: no-repeat;
   height: 24px;
@@ -383,7 +383,7 @@
   color: white;
 }
 
-#volume-list .root-item > div.root-eject {
+#navigation-list .root-item > div.root-eject {
   background-image: -webkit-image-set(
     url('../images/files/ui/eject.png') 1x,
     url('../images/files/ui/2x/eject.png') 2x);
@@ -398,7 +398,7 @@
   width: 20px;
 }
 
-#volume-list:focus .root-item[selected] > div.root-eject {
+#navigation-list:focus .root-item[selected] > div.root-eject {
   -webkit-filter: brightness(0) invert();
   opacity: 1;
 }
@@ -958,7 +958,7 @@
 }
 
 #directory-tree .tree-item.accepts > .tree-row,
-#volume-list > .accepts,
+#navigation-list > .accepts,
 #list-container list > li.accepts,
 #list-container grid > li.accepts {
   -webkit-animation: acceptsBlink 200ms linear 1s 3;
@@ -1443,7 +1443,7 @@
   position: absolute;
   top: 0;
   width: 100%;
-  z-index: 100;
+  z-index: 1000;  /* Must be above all elements in file manager container. */
 }
 
 /* When the overlay pane is visible hide everything else so that the tab order
@@ -1507,7 +1507,7 @@
 }
 
 menu.file-context-menu {
-  z-index: 4;
+  z-index: 600;  /* Must be below the overlay pane (1000). */
 }
 
 menu.chrome-menu hr {
@@ -1638,7 +1638,7 @@
   text-align: center;
   top: 5px;
   white-space: nowrap;
-  z-index: 15;
+  z-index: 600;  /* Must be below the overlay pane (1000). */
 }
 
 .tooltip::after,
@@ -1842,6 +1842,54 @@
   z-index: 101;
 }
 
+#suggest-app-dialog {
+  background-color: #fff;
+  border: 0;
+  padding: 0;
+  width: auto;
+}
+
+#suggest-app-dialog .cr-dialog-title {
+  /* Entire height: 44px (content-box 22px + padding 11px * 2) */
+  font-size: 16px;
+  height: 22px;
+  margin: 0;
+  padding: 11px 18px;
+}
+
+#suggest-app-dialog #webview-container {
+  border-bottom: solid 2px #bbb;
+  border-top: solid 2px #bbb;
+}
+
+#suggest-app-dialog .cr-dialog-buttons,
+#suggest-app-dialog .cr-dialog-text,
+#suggest-app-dialog .cr-dialog-ok,
+#suggest-app-dialog .cr-dialog-cancel {
+  display: none;
+}
+
+#suggest-app-dialog #buttons {
+  background: #eee;
+  width: 100%;
+}
+
+#suggest-app-dialog #buttons > #webstore-button {
+  -webkit-padding-after: 10px;
+  -webkit-padding-before: 10px;
+  -webkit-padding-end: 10px;
+  -webkit-padding-start: 36px;
+  background-image: -webkit-image-set(
+    url('chrome://theme/IDR_WEBSTORE_ICON_16') 1x,
+    url('chrome://theme/IDR_WEBSTORE_ICON_16@2x') 2x);
+  background-position: 12px center;
+  background-repeat: no-repeat;
+  color: #00f;
+  cursor: pointer;
+  display: inline-block;
+  height: 16px;
+}
+
 .cr-dialog-frame.error-dialog-frame {
   width: 300px;
 }
@@ -1850,8 +1898,8 @@
   background-image: -webkit-image-set(
     url('chrome://theme/IDR_ERROR_NETWORK_GENERIC') 1x,
     url('chrome://theme/IDR_ERROR_NETWORK_GENERIC@2x') 2x);
-  background-repeat: no-repeat;
   background-position: center;
+  background-repeat: no-repeat;
   height: 40px;
 }
 
diff --git a/chrome/browser/resources/file_manager/gallery.html b/chrome/browser/resources/file_manager/gallery.html
index fc7e486..412b8be 100644
--- a/chrome/browser/resources/file_manager/gallery.html
+++ b/chrome/browser/resources/file_manager/gallery.html
@@ -44,6 +44,7 @@
     <script src="js/file_type.js"></script>
     <script src="js/async_util.js"></script>
     <script src="js/util.js"></script>
+    <script src="js/path_util.js"></script>
     <script src="js/volume_manager.js"></script>
 
     <script src="js/image_editor/image_util.js"></script>
diff --git a/chrome/browser/resources/file_manager/js/app_installer.js b/chrome/browser/resources/file_manager/js/app_installer.js
new file mode 100644
index 0000000..c1cad7c
--- /dev/null
+++ b/chrome/browser/resources/file_manager/js/app_installer.js
@@ -0,0 +1,71 @@
+// 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.
+
+'use strict';
+
+/**
+ * Manage the installation of apps.
+ *
+ * @param {string} itemId Item id to be installed.
+ * @constructor
+ * @extends {cr.EventType}
+ */
+function AppInstaller(itemId) {
+  this.itemId_ = itemId;
+  this.callback_ = null;
+
+  Object.seal(this);
+}
+
+AppInstaller.prototype = {
+};
+
+/**
+ * Type of result.
+ *
+ * @enum {number}
+ * @const
+ */
+AppInstaller.Result = {
+  SUCCESS: 0,
+  CANCELLED: 1,
+  ERROR: 2
+};
+/**
+ * Error message for user cancellation. This must be match with the constant
+ * 'kUserCancelledError' in C/B/extensions/webstore_standalone_installer.cc.
+ * @type {string}
+ * @const
+ * @private
+ */
+AppInstaller.USER_CANCELLED_ERROR_STR_ = 'User cancelled install';
+
+/**
+ * Start an installation.
+ * @param {function(boolean, string)} callback Called when the installation is
+ *     finished.
+ */
+AppInstaller.prototype.install = function(callback) {
+  this.callback_ = callback;
+  // TODO(yoshiki): Calls the API method to install an app.
+  // Until then, the installation is always failed.
+  setTimeout(this.onInstallCompleted_.bind(this, {result: false}), 1000);
+};
+
+/**
+ * Called when the installation is completed.
+ *
+ * @param {boolean} status True if the installation is success, false if failed.
+ * @private
+ */
+AppInstaller.prototype.onInstallCompleted_ = function(status) {
+  var result = AppInstaller.Result.SUCCESS;
+  if (!status.result) {
+    result = (status.error == AppInstaller.USER_CANCELLED_ERROR_STR_) ?
+             AppInstaller.Result.CANCELLED :
+             AppInstaller.Result.ERROR;
+  }
+  this.callback_(result, status.error);
+  this.callback_ = null;
+};
diff --git a/chrome/browser/resources/file_manager/js/background.js b/chrome/browser/resources/file_manager/js/background.js
index 2f7b877..c5fad17 100644
--- a/chrome/browser/resources/file_manager/js/background.js
+++ b/chrome/browser/resources/file_manager/js/background.js
@@ -523,7 +523,7 @@
  */
 function maybeCloseBackgroundPage() {
   if (Object.keys(appWindows).length === 0 &&
-      !FileCopyManager.getInstance().hasQueuedTasks())
+      !FileOperationManager.getInstance().hasQueuedTasks())
     close();
 }
 
diff --git a/chrome/browser/resources/file_manager/js/butter_bar.js b/chrome/browser/resources/file_manager/js/butter_bar.js
index 2be649a..ea954bf 100644
--- a/chrome/browser/resources/file_manager/js/butter_bar.js
+++ b/chrome/browser/resources/file_manager/js/butter_bar.js
@@ -8,14 +8,15 @@
  * Butter bar is shown on top of the file list and is used to show the copy
  * progress and other messages.
  * @param {HTMLElement} dialogDom FileManager top-level div.
- * @param {FileCopyManagerWrapper} copyManager The copy manager.
+ * @param {FileOperationManagerWrapper} fileOperationManager The operation
+ *     manager.
  * @constructor
  */
-function ButterBar(dialogDom, copyManager) {
+function ButterBar(dialogDom, fileOperationManager) {
   this.dialogDom_ = dialogDom;
   this.butter_ = this.dialogDom_.querySelector('#butter-bar');
   this.document_ = this.butter_.ownerDocument;
-  this.copyManager_ = copyManager;
+  this.fileOperationManager_ = fileOperationManager;
   this.hideTimeout_ = null;
   this.showTimeout_ = null;
   this.lastShowTime_ = 0;
@@ -26,10 +27,10 @@
   this.alert_ = new ErrorDialog(this.dialogDom_);
 
   this.onCopyProgressBound_ = this.onCopyProgress_.bind(this);
-  this.copyManager_.addEventListener(
+  this.fileOperationManager_.addEventListener(
       'copy-progress', this.onCopyProgressBound_);
   this.onDeleteBound_ = this.onDelete_.bind(this);
-  this.copyManager_.addEventListener('delete', this.onDeleteBound_);
+  this.fileOperationManager_.addEventListener('delete', this.onDeleteBound_);
 }
 
 /**
@@ -62,10 +63,10 @@
  * invocation.
  */
 ButterBar.prototype.dispose = function() {
-  // Unregister listeners from FileCopyManager.
-  this.copyManager_.removeEventListener(
+  // Unregister listeners from FileOperationManager.
+  this.fileOperationManager_.removeEventListener(
       'copy-progress', this.onCopyProgressBound_);
-  this.copyManager_.removeEventListener('delete', this.onDeleteBound_);
+  this.fileOperationManager_.removeEventListener('delete', this.onDeleteBound_);
 };
 
 /**
@@ -249,7 +250,7 @@
  * Set up butter bar for showing copy progress.
  *
  * @param {Object} progress Copy status object created by
- *     FileCopyManager.getStatus().
+ *     FileOperationManager.getStatus().
  * @private
  */
 ButterBar.prototype.showProgress_ = function(progress) {
@@ -270,14 +271,15 @@
     this.update_(progressString, options);
   } else {
     options.actions[ButterBar.ACTION_X] =
-        this.copyManager_.requestCancel.bind(this.copyManager_);
+        this.fileOperationManager_.requestCancel.bind(
+            this.fileOperationManager_);
     this.show(ButterBar.Mode.COPY, progressString, options);
   }
 };
 
 /**
  * 'copy-progress' event handler. Show progress or an appropriate message.
- * @param {cr.Event} event A 'copy-progress' event from FileCopyManager.
+ * @param {cr.Event} event A 'copy-progress' event from FileOperationManager.
  * @private
  */
 ButterBar.prototype.onCopyProgress_ = function(event) {
@@ -340,7 +342,7 @@
 
 /**
  * 'delete' event handler. Shows information about deleting files.
- * @param {cr.Event} event A 'delete' event from FileCopyManager.
+ * @param {cr.Event} event A 'delete' event from FileOperationManager.
  * @private
  */
 ButterBar.prototype.onDelete_ = function(event) {
diff --git a/chrome/browser/resources/file_manager/js/cws_container_client.js b/chrome/browser/resources/file_manager/js/cws_container_client.js
new file mode 100644
index 0000000..9e7718f
--- /dev/null
+++ b/chrome/browser/resources/file_manager/js/cws_container_client.js
@@ -0,0 +1,210 @@
+// 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.
+
+'use strict';
+
+/**
+ * @param {WebView} webView Web View tag.
+ * @param {string} ext File extension.
+ * @param {string} mime File mime type.
+ * @param {number} width Width of the CWS widget.
+ * @param {number} height Height of the CWS widget.
+ * @param {string} url Share Url for an entry.
+ * @param {string} target Target (scheme + host + port) of the widget.
+ * @constructor
+ */
+function CWSContainerClient(webView, ext, mime, width, height, url, target) {
+  this.webView_ = webView;
+  this.ext_ = ext;
+  this.mime_ = mime;
+  this.width_ = width;
+  this.height_ = height;
+  this.url_ = url;
+  this.target_ = target;
+
+  this.loaded_ = false;
+  this.loading_ = false;
+
+  this.onMessageBound_ = this.onMessage_.bind(this);
+  this.onLoadStopBound_ = this.onLoadStop_.bind(this);
+  this.onLoadAbortBound_ = this.onLoadAbort_.bind(this);
+}
+
+CWSContainerClient.prototype = {
+  __proto__: cr.EventTarget.prototype
+};
+
+/**
+ * Handles messages from the widget
+ * @param {Event} event Message event.
+ * @private
+ */
+CWSContainerClient.prototype.onMessage_ = function(event) {
+  if (event.origin != this.target_)
+    return;
+
+  var data = event.data;
+  switch (data['message']) {
+    case 'widget_loaded':
+      // Do nothing. Waits for user action and next message.
+      break;
+    case 'before_install':
+      this.sendInstallRequest_(data['item_id']);
+      break;
+    default:
+      console.error('Unexpected message: ' + data['message'], data);
+  }
+};
+
+/**
+ * Called when receiving 'loadstop' event from the <wevview>.
+ * @param {Event} event Message event.
+ * @private
+ */
+CWSContainerClient.prototype.onLoadStop_ = function(event) {
+  if (this.url_ == this.webView_.src && !this.loaded_) {
+    this.loaded_ = true;
+    this.postInitializeMessage_();
+  }
+};
+
+/**
+ * Called when receiving the 'loadabort' event from <webview>.
+ * @param {Event} event Message event.
+ * @private
+ */
+CWSContainerClient.prototype.onLoadAbort_ = function(event) {
+  this.sendWebviewLoadAbort_();
+};
+
+/**
+ * Called when the installation is completed from the suggest-app dialog.
+ *
+ * @param {boolean} result True if the installation is success, false if failed.
+ * @param {string} itemId Item id to be installed.
+ */
+CWSContainerClient.prototype.onInstallCompleted = function(result, itemId) {
+  if (result)
+    this.postInstallSuccessMessage_(itemId);
+  else
+    this.postInstallFailureMessage_(itemId);
+};
+
+/**
+ * Send the abort event to the suggest-app dialog.
+ * @private
+ */
+CWSContainerClient.prototype.sendWebviewLoadAbort_ = function() {
+  this.dispatchEvent(new cr.Event('webview-load-abort'));
+};
+
+/**
+ * Send the install request to the suggest-app dialog.
+ *
+ * @param {string} itemId Item id to be installed.
+ * @private
+ */
+CWSContainerClient.prototype.sendInstallRequest_ = function(itemId) {
+  var event = new cr.Event('install-request');
+  event.itemId = itemId;
+  this.dispatchEvent(event);
+};
+
+/**
+ * Send the 'install_failure' message to the widget.
+ *
+ * @param {string} itemId Item id to be installed.
+ * @private
+ */
+CWSContainerClient.prototype.postInstallFailureMessage_ = function(itemId) {
+  var message = {
+    message: 'install_failure',
+    item_id: itemId,
+    v: 1
+  };
+
+  this.postMessage_(message);
+};
+
+/**
+ * Send the 'install_success' message to the widget.
+ *
+ * @param {string} itemId Item id to be installed.
+ * @private
+ */
+CWSContainerClient.prototype.postInstallSuccessMessage_ = function(itemId) {
+  var message = {
+    message: 'install_success',
+    item_id: itemId,
+    v: 1
+  };
+
+  this.postMessage_(message);
+};
+
+/**
+ * Send the 'initialize' message to the widget.
+ * @private
+ */
+CWSContainerClient.prototype.postInitializeMessage_ = function() {
+  var message = {
+    message: 'initialize',
+    hl: 'en',
+    widgth: this.width_,
+    height: this.height_,
+    file_extension: this.ext_,
+    mime_type: this.mime_,
+    v: 1
+  };
+
+  this.postMessage_(message);
+};
+
+/**
+ * Send a message to the widget. This method shouldn't be called directly,
+ * should from more specified posting function (eg. postXyzMessage_()).
+ *
+ * @param {object} message Message object to be posted.
+ * @private
+ */
+CWSContainerClient.prototype.postMessage_ = function(message) {
+  if (!this.webView_.contentWindow)
+    return;
+
+  this.webView_.contentWindow.postMessage(message, this.target_);
+};
+
+/**
+ * Loads the page to <webview>. Can be called only once.
+ */
+CWSContainerClient.prototype.load = function() {
+  if (this.loading_ || this.loaded_)
+    throw new Error('Already loaded.');
+  this.loading_ = true;
+  this.loaded_ = false;
+
+  window.addEventListener('message', this.onMessageBound_);
+  this.webView_.addEventListener('loadstop', this.onLoadStopBound_);
+  this.webView_.addEventListener('loadabort', this.onLoadAbortBound_);
+  this.webView_.setAttribute('src', this.url_);
+};
+
+/**
+ * Aborts loading of the embedded dialog and performs cleanup.
+ */
+CWSContainerClient.prototype.abort = function() {
+  window.removeEventListener('message', this.onMessageBound_);
+  this.webView_.removeEventListener('loadstop', this.onLoadStopBound_);
+  this.webView_.removeEventListener(
+      'loadabort', this.onLoadAbortBound_);
+  this.webView_.stop();
+};
+
+/**
+ * Cleans the dialog by removing all handlers.
+ */
+CWSContainerClient.prototype.dispose = function() {
+  this.abort();
+};
+
diff --git a/chrome/browser/resources/file_manager/js/directory_model.js b/chrome/browser/resources/file_manager/js/directory_model.js
index ebae107..6cdf698 100644
--- a/chrome/browser/resources/file_manager/js/directory_model.js
+++ b/chrome/browser/resources/file_manager/js/directory_model.js
@@ -25,7 +25,7 @@
     drive: [],
     downloads: [],
     archives: [],
-    removables: [],
+    removables: []
   };
 
   // Add the entry at path as a root.
@@ -373,7 +373,8 @@
  * If updateFunc returns true, it force to dispatch the change event even if the
  * selection index is not changed.
  *
- * @param {cr.ui.ListSingleSelectionModel} selection Selection to be updated.
+ * @param {cr.ui.ListSelectionModel|cr.ui.ListSingleSelectionModel} selection
+ *     Selection to be updated.
  * @param {function(): boolean} updateFunc Function updating the selection.
  * @private
  */
@@ -395,7 +396,11 @@
 
   // If the change evnet have been already dispatched, dispatchNeeded is false.
   if (dispatchNeeded) {
-    selection.dispatchEvent(selection.createChangeEvent('change'));
+    var event = new Event('change');
+    // The selection status (selected or not) is not changed because
+    // this event is caused by the change of selected item.
+    event.changes = [];
+    selection.dispatchEvent(event);
   }
 };
 
diff --git a/chrome/browser/resources/file_manager/js/directory_tree.js b/chrome/browser/resources/file_manager/js/directory_tree.js
index 2d14314..a41502a 100644
--- a/chrome/browser/resources/file_manager/js/directory_tree.js
+++ b/chrome/browser/resources/file_manager/js/directory_tree.js
@@ -424,8 +424,8 @@
 // DirectoryTree
 
 /**
- * Tree of directories on the sidebar. This element is also the root of items,
- * in other words, this is the parent of the top-level items.
+ * Tree of directories on the middle bar. This element is also the root of
+ * items, in other words, this is the parent of the top-level items.
  *
  * @constructor
  * @extends {cr.ui.Tree}
diff --git a/chrome/browser/resources/file_manager/js/file_copy_manager.js b/chrome/browser/resources/file_manager/js/file_copy_manager.js
deleted file mode 100644
index 908804a..0000000
--- a/chrome/browser/resources/file_manager/js/file_copy_manager.js
+++ /dev/null
@@ -1,1527 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-'use strict';
-
-/**
- * Utilities for FileCopyManager.
- */
-var fileOperationUtil = {};
-
-/**
- * Simple wrapper for util.deduplicatePath. On error, this method translates
- * the FileError to FileCopyManager.Error object.
- *
- * @param {DirectoryEntry} dirEntry The target directory entry.
- * @param {string} relativePath The path to be deduplicated.
- * @param {function(string)} successCallback Callback run with the deduplicated
- *     path on success.
- * @param {function(FileCopyManager.Error)} errorCallback Callback run on error.
- */
-fileOperationUtil.deduplicatePath = function(
-    dirEntry, relativePath, successCallback, errorCallback) {
-  util.deduplicatePath(
-      dirEntry, relativePath, successCallback,
-      function(err) {
-        var onFileSystemError = function(error) {
-          errorCallback(new FileCopyManager.Error(
-              util.FileOperationErrorType.FILESYSTEM_ERROR, error));
-        };
-
-        if (err.code == FileError.PATH_EXISTS_ERR) {
-          // Failed to uniquify the file path. There should be an existing
-          // entry, so return the error with it.
-          util.resolvePath(
-              dirEntry, relativePath,
-              function(entry) {
-                errorCallback(new FileCopyManager.Error(
-                    util.FileOperationErrorType.TARGET_EXISTS, entry));
-              },
-              onFileSystemError);
-          return;
-        }
-        onFileSystemError(err);
-      });
-};
-
-/**
- * Sets last modified date to the entry.
- * @param {Entry} entry The entry to which the last modified is set.
- * @param {Date} modificationTime The last modified time.
- */
-fileOperationUtil.setLastModified = function(entry, modificationTime) {
-  chrome.fileBrowserPrivate.setLastModified(
-      entry.toURL(), '' + Math.round(modificationTime.getTime() / 1000));
-};
-
-/**
- * Copies source to parent with the name newName recursively.
- *
- * @param {Entry} source The entry to be copied.
- * @param {DirectoryEntry} parent The entry of the destination directory.
- * @param {string} newName The name of copied file.
- * @param {function(util.EntryChangedKind, Entry)} entryChangedCallback
- *     Callback invoked when an entry is changed.
- * @param {function(Entry, number)} progressCallback Callback invoked
- *     periodically during the copying. It takes source and the number of
- *     copied bytes since the last invocation.
- * @param {function(Entry)} successCallback Callback invoked when the copy
- *     is successfully done with the entry of the created entry.
- * @param {function(FileError)} errorCallback Callback invoked when an error
- *     is found.
- * @return {function()} Callback to cancel the current file copy operation.
- *     When the cancel is done, errorCallback will be called. The returned
- *     callback must not be called more than once.
- */
-fileOperationUtil.copyRecursively = function(
-    source, parent, newName, entryChangedCallback, progressCallback,
-    successCallback, errorCallback) {
-  // Notify that the copy begins for each entry.
-  progressCallback(source, 0);
-
-  // If the entry is a file, redirect it to copyFile_().
-  if (source.isFile) {
-    return fileOperationUtil.copyFile_(
-        source, parent, newName, progressCallback,
-        function(entry) {
-          entryChangedCallback(util.EntryChangedKind.CREATED, entry);
-          successCallback(entry);
-        },
-        errorCallback);
-  }
-
-  // Hereafter, the source is directory.
-  var cancelRequested = false;
-  var cancelCallback = null;
-
-  // First, we create the directory copy.
-  parent.getDirectory(
-      newName, {create: true, exclusive: true},
-      function(dirEntry) {
-        entryChangedCallback(util.EntryChangedKind.CREATED, dirEntry);
-        if (cancelRequested) {
-          errorCallback(util.createFileError(FileError.ABORT_ERR));
-          return;
-        }
-
-        // Iterate on children, and copy them recursively.
-        util.forEachDirEntry(
-            source,
-            function(child, callback) {
-              if (cancelRequested) {
-                errorCallback(util.createFileError(FileError.ABORT_ERR));
-                return;
-              }
-
-              cancelCallback = fileOperationUtil.copyRecursively(
-                  child, dirEntry, child.name, entryChangedCallback,
-                  progressCallback,
-                  function() {
-                    cancelCallback = null;
-                    callback();
-                  },
-                  function(error) {
-                    cancelCallback = null;
-                    errorCallback(error);
-                  });
-            },
-            function() {
-              successCallback(dirEntry);
-            },
-            errorCallback);
-      },
-      errorCallback);
-
-  return function() {
-    cancelRequested = true;
-    if (cancelCallback) {
-      cancelCallback();
-      cancelCallback = null;
-    }
-  };
-};
-
-/**
- * Copies a file from source to the parent directory with newName.
- * See also copyFileByStream_ and copyFileOnDrive_ for the implementation
- * details.
- *
- * @param {FileEntry} source The file entry to be copied.
- * @param {DirectoryEntry} parent The entry of the destination directory.
- * @param {string} newName The name of copied file.
- * @param {function(FileEntry, number)} progressCallback Callback invoked
- *     periodically during the file writing. It takes source and the number of
- *     copied bytes since the last invocation. This is also called just before
- *     starting the operation (with '0' bytes) and just after the finishing the
- *     operation (with the total copied size).
- * @param {function(FileEntry)} successCallback Callback invoked when the copy
- *     is successfully done with the entry of the created file.
- * @param {function(FileError)} errorCallback Callback invoked when an error
- *     is found.
- * @return {function()} Callback to cancel the current file copy operation.
- *     When the cancel is done, errorCallback will be called. The returned
- *     callback must not be called more than once.
- * @private
- */
-fileOperationUtil.copyFile_ = function(
-    source, parent, newName, progressCallback, successCallback, errorCallback) {
-  if (!PathUtil.isDriveBasedPath(source.fullPath) &&
-      !PathUtil.isDriveBasedPath(parent.fullPath)) {
-    // Copying a file between non-Drive file systems.
-    return fileOperationUtil.copyFileByStream_(
-        source, parent, newName, progressCallback, successCallback,
-        errorCallback);
-  } else {
-    // Copying related to the Drive file system.
-    return fileOperationUtil.copyFileOnDrive_(
-        source, parent, newName, progressCallback, successCallback,
-        errorCallback);
-  }
-};
-
-/**
- * Copies a file by using File and FileWriter objects.
- *
- * This is a js-implementation of FileEntry.copyTo(). Unfortunately, copyTo
- * doesn't support periodical progress updating nor cancelling. To support
- * these operations, this method implements copyTo by streaming way in
- * JavaScript.
- *
- * Note that this is designed for file copying on local file system. We have
- * some special cases about copying on Drive file system. See also
- * copyFileOnDrive_() for more details.
- *
- * @param {FileEntry} source The file entry to be copied.
- * @param {DirectoryEntry} parent The entry of the destination directory.
- * @param {string} newName The name of copied file.
- * @param {function(FileEntry, number)} progressCallback Callback invoked
- *     periodically during the file writing. It takes source and the number of
- *     copied bytes since the last invocation. This is also called just before
- *     starting the operation (with '0' bytes) and just after the finishing the
- *     operation (with the total copied size).
- * @param {function(FileEntry)} successCallback Callback invoked when the copy
- *     is successfully done with the entry of the created file.
- * @param {function(FileError)} errorCallback Callback invoked when an error
- *     is found.
- * @return {function()} Callback to cancel the current file copy operation.
- *     When the cancel is done, errorCallback will be called. The returned
- *     callback must not be called more than once.
- * @private
- */
-fileOperationUtil.copyFileByStream_ = function(
-    source, parent, newName, progressCallback, successCallback, errorCallback) {
-  // Set to true when cancel is requested.
-  var cancelRequested = false;
-
-  source.file(function(file) {
-    if (cancelRequested) {
-      errorCallback(util.createFileError(FileError.ABORT_ERR));
-      return;
-    }
-
-    parent.getFile(newName, {create: true, exclusive: true}, function(target) {
-      if (cancelRequested) {
-        errorCallback(util.createFileError(FileError.ABORT_ERR));
-        return;
-      }
-
-      target.createWriter(function(writer) {
-        if (cancelRequested) {
-          errorCallback(util.createFileError(FileError.ABORT_ERR));
-          return;
-        }
-
-        writer.onerror = writer.onabort = function(progress) {
-          errorCallback(cancelRequested ?
-              util.createFileError(FileError.ABORT_ERR) :
-                  writer.error);
-        };
-
-        var reportedProgress = 0;
-        writer.onprogress = function(progress) {
-          if (cancelRequested) {
-            // If the copy was cancelled, we should abort the operation.
-            // The errorCallback will be called by writer.onabort after the
-            // termination.
-            writer.abort();
-            return;
-          }
-
-          // |progress.loaded| will contain total amount of data copied by now.
-          // |progressCallback| expects data amount delta from the last progress
-          // update.
-          progressCallback(target, progress.loaded - reportedProgress);
-          reportedProgress = progress.loaded;
-        };
-
-        writer.onwrite = function() {
-          if (cancelRequested) {
-            errorCallback(util.createFileError(FileError.ABORT_ERR));
-            return;
-          }
-
-          source.getMetadata(function(metadata) {
-            if (cancelRequested) {
-              errorCallback(util.createFileError(FileError.ABORT_ERR));
-              return;
-            }
-
-            fileOperationUtil.setLastModified(
-                target, metadata.modificationTime);
-            successCallback(target);
-          }, errorCallback);
-        };
-
-        writer.write(file);
-      }, errorCallback);
-    }, errorCallback);
-  }, errorCallback);
-
-  return function() { cancelRequested = true; };
-};
-
-/**
- * Copies a file a) from Drive to local, b) from local to Drive, or c) from
- * Drive to Drive.
- * Currently, we need to take care about following two things for Drive:
- *
- * 1) Copying hosted document.
- * In theory, it is impossible to actual copy a hosted document to other
- * file system. Thus, instead, Drive file system backend creates a JSON file
- * referring to the hosted document. Also, when it is uploaded by copyTo,
- * the hosted document is copied on the server. Note that, this doesn't work
- * when a user creates a file by FileWriter (as copyFileEntry_ does).
- *
- * 2) File transfer between local and Drive server.
- * There are two directions of file transfer; from local to Drive and from
- * Drive to local.
- * The file transfer from local to Drive is done as a part of file system
- * background sync (kicked after the copy operation is done). So we don't need
- * to take care about it here. To copy the file from Drive to local (or Drive
- * to Drive with GData WAPI), we need to download the file content (if it is
- * not locally cached). During the downloading, we can listen the periodical
- * updating and cancel the downloding via private API.
- *
- * This function supports progress updating and cancelling partially.
- * Unfortunately, FileEntry.copyTo doesn't support progress updating nor
- * cancelling, so we support them only during file downloading.
- *
- * Note: we're planning to move copyTo logic into c++ side. crbug.com/261492
- *
- * @param {FileEntry} source The entry of the file to be copied.
- * @param {DirectoryEntry} parent The entry of the destination directory.
- * @param {string} newName The name of the copied file.
- * @param {function(FileEntry, number)} progressCallback Callback periodically
- *     invoked during file transfer with the source and the number of
- *     transferred bytes from the last call.
- * @param {function(FileEntry)} successCallback Callback invoked when the
- *     file copy is successfully done with the entry of the copied file.
- * @param {function(FileError)} errorCallback Callback invoked when an error
- *     is found.
- * @return {function()} Callback to cancel the current file copy operation.
- *     When the cancel is done, errorCallback will be called. The returned
- *     callback must not be called more than once.
- * @private
- */
-fileOperationUtil.copyFileOnDrive_ = function(
-    source, parent, newName, progressCallback, successCallback, errorCallback) {
-  // Set to true when cancel is requested.
-  var cancelRequested = false;
-  var cancelCallback = null;
-
-  var onCopyToCompleted = null;
-
-  // Progress callback.
-  // Because the uploading the file from local cache to Drive server will be
-  // done as a part of background Drive file system sync, so for this copy
-  // operation, what we need to take care about is only file downloading.
-  var numTransferredBytes = 0;
-  if (PathUtil.isDriveBasedPath(source.fullPath)) {
-    var sourceUrl = source.toURL();
-    var sourcePath = util.extractFilePath(sourceUrl);
-    var onFileTransfersUpdated = function(statusList) {
-      for (var i = 0; i < statusList.length; i++) {
-        var status = statusList[i];
-
-        // Comparing urls is unreliable, since they may use different
-        // url encoding schemes (eg. rfc2396 vs. rfc3986).
-        var filePath = util.extractFilePath(status.fileUrl);
-        if (filePath == sourcePath) {
-          var processed = status.processed;
-          if (processed > numTransferredBytes) {
-            progressCallback(source, processed - numTransferredBytes);
-            numTransferredBytes = processed;
-          }
-          return;
-        }
-      }
-    };
-
-    // Subscribe to listen file transfer updating notifications.
-    chrome.fileBrowserPrivate.onFileTransfersUpdated.addListener(
-        onFileTransfersUpdated);
-
-    // Currently, we do NOT upload the file during the copy operation.
-    // It will be done as a part of file system sync after copy operation.
-    // So, we can cancel only file downloading.
-    cancelCallback = function() {
-      chrome.fileBrowserPrivate.cancelFileTransfers(
-          [sourceUrl], function() {});
-    };
-
-    // We need to clean up on copyTo completion regardless if it is
-    // successfully done or not.
-    onCopyToCompleted = function() {
-      cancelCallback = null;
-      chrome.fileBrowserPrivate.onFileTransfersUpdated.removeListener(
-          onFileTransfersUpdated);
-    };
-  }
-
-  source.copyTo(
-      parent, newName,
-      function(entry) {
-        if (onCopyToCompleted)
-          onCopyToCompleted();
-
-        if (cancelRequested) {
-          errorCallback(util.createFileError(FileError.ABORT_ERR));
-          return;
-        }
-
-        entry.getMetadata(function(metadata) {
-          if (metadata.size > numTransferredBytes)
-            progressCallback(source, metadata.size - numTransferredBytes);
-          successCallback(entry);
-        }, errorCallback);
-      },
-      function(error) {
-        if (onCopyToCompleted)
-          onCopyToCompleted();
-
-        errorCallback(error);
-      });
-
-  return function() {
-    cancelRequested = true;
-    if (cancelCallback) {
-      cancelCallback();
-      cancelCallback = null;
-    }
-  };
-};
-
-/**
- * Thin wrapper of chrome.fileBrowserPrivate.zipSelection to adapt its
- * interface similar to copyTo().
- *
- * @param {Array.<Entry>} sources The array of entries to be archived.
- * @param {DirectoryEntry} parent The entry of the destination directory.
- * @param {string} newName The name of the archive to be created.
- * @param {function(FileEntry)} successCallback Callback invoked when the
- *     operation is successfully done with the entry of the created archive.
- * @param {function(FileError)} errorCallback Callback invoked when an error
- *     is found.
- */
-fileOperationUtil.zipSelection = function(
-    sources, parent, newName, successCallback, errorCallback) {
-  chrome.fileBrowserPrivate.zipSelection(
-      parent.toURL(),
-      sources.map(function(e) { return e.toURL(); }),
-      newName, function(success) {
-        if (!success) {
-          // Failed to create a zip archive.
-          errorCallback(
-              util.createFileError(FileError.INVALID_MODIFICATION_ERR));
-          return;
-        }
-
-        // Returns the created entry via callback.
-        parent.getFile(
-            newName, {create: false}, successCallback, errorCallback);
-      });
-};
-
-/**
- * @constructor
- */
-function FileCopyManager() {
-  this.copyTasks_ = [];
-  this.deleteTasks_ = [];
-  this.cancelObservers_ = [];
-  this.cancelRequested_ = false;
-  this.cancelCallback_ = null;
-  this.unloadTimeout_ = null;
-
-  this.eventRouter_ = new FileCopyManager.EventRouter();
-}
-
-/**
- * Get FileCopyManager instance. In case is hasn't been initialized, a new
- * instance is created.
- *
- * @return {FileCopyManager} A FileCopyManager instance.
- */
-FileCopyManager.getInstance = function() {
-  if (!FileCopyManager.instance_)
-    FileCopyManager.instance_ = new FileCopyManager();
-
-  return FileCopyManager.instance_;
-};
-
-/**
- * Manages cr.Event dispatching.
- * Currently this can send three types of events: "copy-progress",
- * "copy-operation-completed" and "delete".
- *
- * TODO(hidehiko): Reorganize the event dispatching mechanism.
- * @constructor
- * @extends {cr.EventTarget}
- */
-FileCopyManager.EventRouter = function() {
-};
-
-/**
- * Extends cr.EventTarget.
- */
-FileCopyManager.EventRouter.prototype.__proto__ = cr.EventTarget.prototype;
-
-/**
- * Dispatches a simple "copy-progress" event with reason and current
- * FileCopyManager status. If it is an ERROR event, error should be set.
- *
- * @param {string} reason Event type. One of "BEGIN", "PROGRESS", "SUCCESS",
- *     "ERROR" or "CANCELLED". TODO(hidehiko): Use enum.
- * @param {Object} status Current FileCopyManager's status. See also
- *     FileCopyManager.getStatus().
- * @param {FileCopyManager.Error=} opt_error The info for the error. This
- *     should be set iff the reason is "ERROR".
- */
-FileCopyManager.EventRouter.prototype.sendProgressEvent = function(
-    reason, status, opt_error) {
-  var event = new cr.Event('copy-progress');
-  event.reason = reason;
-  event.status = status;
-  if (opt_error)
-    event.error = opt_error;
-  this.dispatchEvent(event);
-};
-
-/**
- * Dispatches an event to notify that an entry is changed (created or deleted).
- * @param {util.EntryChangedKind} kind The enum to represent if the entry is
- *     created or deleted.
- * @param {Entry} entry The changed entry.
- */
-FileCopyManager.EventRouter.prototype.sendEntryChangedEvent = function(
-    kind, entry) {
-  var event = new cr.Event('entry-changed');
-  event.kind = kind;
-  event.entry = entry;
-  this.dispatchEvent(event);
-};
-
-/**
- * Dispatches an event to notify entries are changed for delete task.
- *
- * @param {string} reason Event type. One of "BEGIN", "PROGRESS", "SUCCESS",
- *     or "ERROR". TODO(hidehiko): Use enum.
- * @param {Array.<string>} urls An array of URLs which are affected by delete
- *     operation.
- */
-FileCopyManager.EventRouter.prototype.sendDeleteEvent = function(
-    reason, urls) {
-  var event = new cr.Event('delete');
-  event.reason = reason;
-  event.urls = urls;
-  this.dispatchEvent(event);
-};
-
-/**
- * A record of a queued copy operation.
- *
- * Multiple copy operations may be queued at any given time.  Additional
- * Tasks may be added while the queue is being serviced.  Though a
- * cancel operation cancels everything in the queue.
- *
- * @param {Array.<Entry>} sourceEntries Array of source entries.
- * @param {DirectoryEntry} targetDirEntry Target directory.
- * @constructor
- */
-FileCopyManager.Task = function(sourceEntries, targetDirEntry) {
-  this.sourceEntries = sourceEntries;
-  this.targetDirEntry = targetDirEntry;
-
-  // TODO(hidehiko): When we support recursive copy, we should be able to
-  // rely on originalEntries. Then remove this.
-  this.entries = [];
-
-  /**
-   * The number of entries, whose processing is completed.
-   * @type {number}
-   */
-  this.numCompletedEntries = 0;
-  this.totalBytes = 0;
-  this.completedBytes = 0;
-
-  /**
-   * The entry currently being processed.
-   * @type {Entry}
-   */
-  this.processingEntry = null;
-
-  this.deleteAfterCopy = false;
-  this.move = false;
-  this.zip = false;
-
-  /**
-   * Set to true when cancel is requested.
-   * @private {boolean}
-   */
-  this.cancelRequested_ = false;
-
-  /**
-   * Callback to cancel the running process.
-   * @private {function()}
-   */
-  this.cancelCallback_ = null;
-
-  // TODO(hidehiko): After we support recursive copy, we don't need this.
-  // If directory already exists, we try to make a copy named 'dir (X)',
-  // where X is a number. When we do this, all subsequent copies from
-  // inside the subtree should be mapped to the new directory name.
-  // For example, if 'dir' was copied as 'dir (1)', then 'dir\file.txt' should
-  // become 'dir (1)\file.txt'.
-  this.renamedDirectories_ = [];
-};
-
-/**
- * @param {function()} callback When entries resolved.
- */
-FileCopyManager.Task.prototype.initialize = function(callback) {
-  // When moving directories, FileEntry.moveTo() is used if both source
-  // and target are on Drive. There is no need to recurse into directories.
-  util.recurseAndResolveEntries(
-      this.sourceEntries, !this.move,
-      function(result) {
-        if (this.move) {
-          // This may be moving from search results, where it fails if we
-          // move parent entries earlier than child entries. We should
-          // process the deepest entry first. Since move of each entry is
-          // done by a single moveTo() call, we don't need to care about the
-          // recursive traversal order.
-          this.entries = result.dirEntries.concat(result.fileEntries).sort(
-              function(entry1, entry2) {
-                return entry2.fullPath.length - entry1.fullPath.length;
-              });
-        } else {
-          // Copying tasks are recursively processed. So, directories must be
-          // processed earlier than their child files. Since
-          // util.recurseAndResolveEntries is already listing entries in the
-          // recursive traversal order, we just keep the ordering.
-          this.entries = result.dirEntries.concat(result.fileEntries);
-        }
-
-        this.totalBytes = result.fileBytes;
-        callback();
-      }.bind(this));
-};
-
-/**
- * Updates copy progress status for the entry.
- *
- * @param {number} size Number of bytes that has been copied since last update.
- */
-FileCopyManager.Task.prototype.updateFileCopyProgress = function(size) {
-  this.completedBytes += size;
-};
-
-/**
- * @param {string} fromName Old name.
- * @param {string} toName New name.
- */
-FileCopyManager.Task.prototype.registerRename = function(fromName, toName) {
-  this.renamedDirectories_.push({from: fromName + '/', to: toName + '/'});
-};
-
-/**
- * @param {string} path A path.
- * @return {string} Path after renames.
- */
-FileCopyManager.Task.prototype.applyRenames = function(path) {
-  // Directories are processed in pre-order, so we will store only the first
-  // renaming point:
-  // x   -> x (1)    -- new directory created.
-  // x\y -> x (1)\y  -- no more renames inside the new directory, so
-  //                    this one will not be stored.
-  // x\y\a.txt       -- only one rename will be applied.
-  for (var index = 0; index < this.renamedDirectories_.length; ++index) {
-    var rename = this.renamedDirectories_[index];
-    if (path.indexOf(rename.from) == 0) {
-      path = rename.to + path.substr(rename.from.length);
-    }
-  }
-  return path;
-};
-
-/**
- * Requests cancellation of this task.
- * When the cancellation is done, it is notified via callbacks of run().
- */
-FileCopyManager.Task.prototype.requestCancel = function() {
-  this.cancelRequested_ = true;
-  if (this.cancelCallback_) {
-    this.cancelCallback_();
-    this.cancelCallback_ = null;
-  }
-};
-
-/**
- * Runs the task. Sub classes must implement this method.
- *
- * @param {function(util.EntryChangedKind, Entry)} entryChangedCallback
- *     Callback invoked when an entry is changed.
- * @param {function()} progressCallback Callback invoked periodically during
- *     the operation.
- * @param {function()} successCallback Callback run on success.
- * @param {function(FileCopyManager.Error)} errorCallback Callback run on error.
- */
-FileCopyManager.Task.prototype.run = function(
-    entryChangedCallback, progressCallback, successCallback, errorCallback) {
-};
-
-/**
- * Task to copy entries.
- *
- * @param {Array.<Entry>} sourceEntries Array of source entries.
- * @param {DirectoryEntry} targetDirEntry Target directory.
- * @constructor
- * @extends {FileCopyManager.Task}
- */
-FileCopyManager.CopyTask = function(sourceEntries, targetDirEntry) {
-  FileCopyManager.Task.call(this, sourceEntries, targetDirEntry);
-};
-
-/**
- * Extends FileCopyManager.Task.
- */
-FileCopyManager.CopyTask.prototype.__proto__ = FileCopyManager.Task.prototype;
-
-/**
- * Copies all entries to the target directory.
- * Note: this method contains also the operation of "Move" due to historical
- * reason.
- *
- * @param {function(util.EntryChangedKind, Entry)} entryChangedCallback
- *     Callback invoked when an entry is changed.
- * @param {function()} progressCallback Callback invoked periodically during
- *     the copying.
- * @param {function()} successCallback On success.
- * @param {function(FileCopyManager.Error)} errorCallback On error.
- * @override
- */
-FileCopyManager.CopyTask.prototype.run = function(
-    entryChangedCallback, progressCallback, successCallback, errorCallback) {
-  // TODO(hidehiko): We should be able to share the code to iterate on entries
-  // with serviceMoveTask_().
-  if (this.entries.length == 0) {
-    successCallback();
-    return;
-  }
-
-  // TODO(hidehiko): Delete after copy is the implementation of Move.
-  // Migrate the part into MoveTask.run().
-  var deleteOriginals = function() {
-    var count = this.sourceEntries.length;
-
-    var onEntryDeleted = function(entry) {
-      entryChangedCallback(util.EntryChangedKind.DELETED, entry);
-      count--;
-      if (!count)
-        successCallback();
-    };
-
-    var onFilesystemError = function(err) {
-      errorCallback(new FileCopyManager.Error(
-          util.FileOperationErrorType.FILESYSTEM_ERROR, err));
-    };
-
-    for (var i = 0; i < this.sourceEntries.length; i++) {
-      var entry = this.sourceEntries[i];
-      util.removeFileOrDirectory(
-          entry, onEntryDeleted.bind(null, entry), onFilesystemError);
-    }
-  }.bind(this);
-
-  AsyncUtil.forEach(
-      this.sourceEntries,
-      function(callback, entry, index) {
-        if (this.cancelRequested_) {
-          errorCallback(new FileCopyManager.Error(
-              util.FileOperationErrorType.FILESYSTEM_ERROR,
-              util.createFileError(FileError.ABORT_ERR)));
-          return;
-        }
-        progressCallback();
-        this.cancelCallback_ = FileCopyManager.CopyTask.processEntry_(
-            entry, this.targetDirEntry,
-            function(type, entry) {
-              this.numCompletedEntries++;
-              entryChangedCallback(type, entry);
-            }.bind(this),
-            function(entry, size) {
-              this.processingEntry = entry;
-              this.updateFileCopyProgress(size);
-              progressCallback();
-            }.bind(this),
-            function() {
-              this.cancelCallback_ = null;
-              callback();
-            }.bind(this),
-            function(error) {
-              this.cancelCallback_ = null;
-              errorCallback(error);
-            }.bind(this));
-      },
-      function() {
-        if (this.deleteAfterCopy) {
-          deleteOriginals();
-        } else {
-          successCallback();
-        }
-      }.bind(this),
-      this);
-};
-
-/**
- * Copies the source entry to the target directory.
- *
- * @param {Entry} sourceEntry An entry to be copied.
- * @param {DirectoryEntry} destinationEntry The entry which will contain the
- *     copied entry.
- * @param {function(util.EntryChangedKind, Entry)} entryChangedCallback
- *     Callback invoked when an entry is changed.
- * @param {function(Entry, number)} progressCallback Callback invoked
- *     periodically during the copying.
- * @param {function()} successCallback On success.
- * @param {function(FileCopyManager.Error)} errorCallback On error.
- * @return {function()} Callback to cancel the current file copy operation.
- *     When the cancel is done, errorCallback will be called. The returned
- *     callback must not be called more than once.
- * @private
- */
-FileCopyManager.CopyTask.processEntry_ = function(
-    sourceEntry, destinationEntry, entryChangedCallback, progressCallback,
-    successCallback, errorCallback) {
-  var cancelRequested = false;
-  var cancelCallback = null;
-  fileOperationUtil.deduplicatePath(
-      destinationEntry, sourceEntry.name,
-      function(destinationName) {
-        if (cancelRequested) {
-          errorCallback(new FileCopyManager.Error(
-              util.FileOperationErrorType.FILESYSTEM_ERROR,
-              util.createFileError(FileError.ABORT_ERR)));
-          return;
-        }
-
-        cancelCallback = fileOperationUtil.copyRecursively(
-            sourceEntry, destinationEntry, destinationName,
-            entryChangedCallback, progressCallback,
-            function(entry) {
-              cancelCallback = null;
-              successCallback();
-            },
-            function(error) {
-              cancelCallback = null;
-              errorCallback(new FileCopyManager.Error(
-                  util.FileOperationErrorType.FILESYSTEM_ERROR, error));
-            });
-      },
-      errorCallback);
-
-  return function() {
-    cancelRequested = true;
-    if (cancelCallback) {
-      cancelCallback();
-      cancelCallback = null;
-    }
-  };
-};
-
-/**
- * Task to move entries.
- *
- * @param {Array.<Entry>} sourceEntries Array of source entries.
- * @param {DirectoryEntry} targetDirEntry Target directory.
- * @constructor
- * @extends {FileCopyManager.Task}
- */
-FileCopyManager.MoveTask = function(sourceEntries, targetDirEntry) {
-  FileCopyManager.Task.call(this, sourceEntries, targetDirEntry);
-  // TODO(hidehiko): We should handle dispatching copy/move/zip more nicely.
-  this.move = true;
-};
-
-/**
- * Extends FileCopyManager.Task.
- */
-FileCopyManager.MoveTask.prototype.__proto__ = FileCopyManager.Task.prototype;
-
-/**
- * Moves all entries in the task.
- *
- * @param {function(util.EntryChangedKind, Entry)} entryChangedCallback
- *     Callback invoked when an entry is changed.
- * @param {function()} progressCallback Callback invoked periodically during
- *     the moving.
- * @param {function()} successCallback On success.
- * @param {function(FileCopyManager.Error)} errorCallback On error.
- * @override
- */
-FileCopyManager.MoveTask.prototype.run = function(
-    entryChangedCallback, progressCallback, successCallback, errorCallback) {
-  if (this.entries.length == 0) {
-    successCallback();
-    return;
-  }
-
-  AsyncUtil.forEach(
-      this.entries,
-      function(callback, entry, index) {
-        if (this.cancelRequested_) {
-          errorCallback(new FileCopyManager.Error(
-              util.FileOperationErrorType.FILESYSTEM_ERROR,
-              util.createFileError(FileError.ABORT_ERR)));
-          return;
-        }
-        this.processingEntry = entry;
-        progressCallback();
-        FileCopyManager.MoveTask.processEntry_(
-            entry, this.targetDirEntry, entryChangedCallback,
-            function() {
-              this.numCompletedEntries++;
-              callback();
-            }.bind(this),
-            errorCallback);
-      },
-      function() {
-        successCallback();
-      }.bind(this),
-      this);
-};
-
-/**
- * Moves the sourceEntry to the targetDirEntry in this task.
- *
- * @param {Entry} sourceEntry An entry to be moved.
- * @param {DirectoryEntry} destinationEntry The entry of the destination
- *     directory.
- * @param {function(util.EntryChangedKind, Entry)} entryChangedCallback
- *     Callback invoked when an entry is changed.
- * @param {function()} successCallback On success.
- * @param {function(FileCopyManager.Error)} errorCallback On error.
- * @private
- */
-FileCopyManager.MoveTask.processEntry_ = function(
-    sourceEntry, destinationEntry, entryChangedCallback, successCallback,
-    errorCallback) {
-  fileOperationUtil.deduplicatePath(
-      destinationEntry,
-      sourceEntry.name,
-      function(destinationName) {
-        sourceEntry.moveTo(
-            destinationEntry, destinationName,
-            function(movedEntry) {
-              entryChangedCallback(util.EntryChangedKind.CREATED, movedEntry);
-              entryChangedCallback(util.EntryChangedKind.DELETED, sourceEntry);
-              successCallback();
-            },
-            function(error) {
-              errorCallback(new FileCopyManager.Error(
-                  util.FileOperationErrorType.FILESYSTEM_ERROR, error));
-            });
-      },
-      errorCallback);
-};
-
-/**
- * Task to create a zip archive.
- *
- * @param {Array.<Entry>} sourceEntries Array of source entries.
- * @param {DirectoryEntry} targetDirEntry Target directory.
- * @param {DirectoryEntry} zipBaseDirEntry Base directory dealt as a root
- *     in ZIP archive.
- * @constructor
- * @extends {FileCopyManager.Task}
- */
-FileCopyManager.ZipTask = function(
-    sourceEntries, targetDirEntry, zipBaseDirEntry) {
-  FileCopyManager.Task.call(this, sourceEntries, targetDirEntry);
-  this.zipBaseDirEntry = zipBaseDirEntry;
-  this.zip = true;
-};
-
-/**
- * Extends FileCopyManager.Task.
- */
-FileCopyManager.ZipTask.prototype.__proto__ = FileCopyManager.Task.prototype;
-
-/**
- * Runs a zip file creation task.
- *
- * @param {function(util.EntryChangedKind, Entry)} entryChangedCallback
- *     Callback invoked when an entry is changed.
- * @param {function()} progressCallback Callback invoked periodically during
- *     the moving.
- * @param {function()} successCallback On complete.
- * @param {function(FileCopyManager.Error)} errorCallback On error.
- * @override
- */
-FileCopyManager.ZipTask.prototype.run = function(
-    entryChangedCallback, progressCallback, successCallback, errorCallback) {
-  // TODO(hidehiko): we should localize the name.
-  var destName = 'Archive';
-  if (this.sourceEntries.length == 1) {
-    var entryPath = this.sourceEntries[0].fullPath;
-    var i = entryPath.lastIndexOf('/');
-    var basename = (i < 0) ? entryPath : entryPath.substr(i + 1);
-    i = basename.lastIndexOf('.');
-    destName = ((i < 0) ? basename : basename.substr(0, i));
-  }
-
-  fileOperationUtil.deduplicatePath(
-      this.targetDirEntry, destName + '.zip',
-      function(destPath) {
-        // TODO: per-entry zip progress update with accurate byte count.
-        // For now just set completedBytes to same value as totalBytes so
-        // that the progress bar is full.
-        this.completedBytes = this.totalBytes;
-        progressCallback();
-
-        fileOperationUtil.zipSelection(
-            this.entries,
-            this.zipBaseDirEntry,
-            destPath,
-            function(entry) {
-              entryChangedCallback(util.EntryChangedKind.CREATE, entry);
-              successCallback();
-            },
-            function(error) {
-              errorCallback(new FileCopyManager.Error(
-                  util.FileOperationErrorType.FILESYSTEM_ERROR, error));
-            });
-      }.bind(this),
-      errorCallback);
-};
-
-/**
- * Error class used to report problems with a copy operation.
- * If the code is UNEXPECTED_SOURCE_FILE, data should be a path of the file.
- * If the code is TARGET_EXISTS, data should be the existing Entry.
- * If the code is FILESYSTEM_ERROR, data should be the FileError.
- *
- * @param {util.FileOperationErrorType} code Error type.
- * @param {string|Entry|FileError} data Additional data.
- * @constructor
- */
-FileCopyManager.Error = function(code, data) {
-  this.code = code;
-  this.data = data;
-};
-
-// FileCopyManager methods.
-
-/**
- * Called before a new method is run in the manager. Prepares the manager's
- * state for running a new method.
- */
-FileCopyManager.prototype.willRunNewMethod = function() {
-  // Cancel any pending close actions so the file copy manager doesn't go away.
-  if (this.unloadTimeout_)
-    clearTimeout(this.unloadTimeout_);
-  this.unloadTimeout_ = null;
-};
-
-/**
- * @return {Object} Status object.
- */
-FileCopyManager.prototype.getStatus = function() {
-  // TODO(hidehiko): Reorganize the structure when delete queue is merged
-  // into copy task queue.
-  var rv = {
-    totalItems: 0,
-    completedItems: 0,
-
-    totalBytes: 0,
-    completedBytes: 0,
-
-    pendingCopies: 0,
-    pendingMoves: 0,
-    pendingZips: 0,
-
-    // In case the number of the incompleted entry is exactly one.
-    filename: '',
-  };
-
-  var pendingEntry = null;
-  for (var i = 0; i < this.copyTasks_.length; i++) {
-    var task = this.copyTasks_[i];
-    rv.totalItems += task.entries.length;
-    rv.completedItems += task.numCompletedEntries;
-
-    rv.totalBytes += task.totalBytes;
-    rv.completedBytes += task.completedBytes;
-
-    var numPendingEntries = task.entries.length - task.numCompletedEntries;
-    if (task.zip) {
-      rv.pendingZips += numPendingEntries;
-    } else if (task.move || task.deleteAfterCopy) {
-      rv.pendingMoves += numPendingEntries;
-    } else {
-      rv.pendingCopies += numPendingEntries;
-    }
-
-    if (task.processingEntry)
-      pendingEntry = task.processingEntry;
-  }
-
-  if (rv.totalItems - rv.completedItems == 1 && pendingEntry)
-    rv.filename = pendingEntry.name;
-
-  return rv;
-};
-
-/**
- * Adds an event listener for the tasks.
- * @param {string} type The name of the event.
- * @param {function(cr.Event)} handler The handler for the event.
- *     This is called when the event is dispatched.
- */
-FileCopyManager.prototype.addEventListener = function(type, handler) {
-  this.eventRouter_.addEventListener(type, handler);
-};
-
-/**
- * Removes an event listener for the tasks.
- * @param {string} type The name of the event.
- * @param {function(cr.Event)} handler The handler to be removed.
- */
-FileCopyManager.prototype.removeEventListener = function(type, handler) {
-  this.eventRouter_.removeEventListener(type, handler);
-};
-
-/**
- * Says if there are any tasks in the queue.
- * @return {boolean} True, if there are any tasks.
- */
-FileCopyManager.prototype.hasQueuedTasks = function() {
-  return this.copyTasks_.length > 0 || this.deleteTasks_.length > 0;
-};
-
-/**
- * Unloads the host page in 5 secs of idleing. Need to be called
- * each time this.copyTasks_.length or this.deleteTasks_.length
- * changed.
- *
- * @private
- */
-FileCopyManager.prototype.maybeScheduleCloseBackgroundPage_ = function() {
-  if (!this.hasQueuedTasks()) {
-    if (this.unloadTimeout_ === null)
-      this.unloadTimeout_ = setTimeout(maybeCloseBackgroundPage, 5000);
-  } else if (this.unloadTimeout_) {
-    clearTimeout(this.unloadTimeout_);
-    this.unloadTimeout_ = null;
-  }
-};
-
-/**
- * Completely clear out the copy queue, either because we encountered an error
- * or completed successfully.
- *
- * @private
- */
-FileCopyManager.prototype.resetQueue_ = function() {
-  for (var i = 0; i < this.cancelObservers_.length; i++)
-    this.cancelObservers_[i]();
-
-  this.copyTasks_ = [];
-  this.cancelObservers_ = [];
-  this.maybeScheduleCloseBackgroundPage_();
-};
-
-/**
- * Request that the current copy queue be abandoned.
- *
- * @param {function()=} opt_callback On cancel.
- */
-FileCopyManager.prototype.requestCancel = function(opt_callback) {
-  this.cancelRequested_ = true;
-  if (this.cancelCallback_) {
-    this.cancelCallback_();
-    this.cancelCallback_ = null;
-  }
-  if (opt_callback)
-    this.cancelObservers_.push(opt_callback);
-
-  // If there is any active task it will eventually call maybeCancel_.
-  // Otherwise call it right now.
-  if (this.copyTasks_.length == 0)
-    this.doCancel_();
-  else
-    this.copyTasks_[0].requestCancel();
-};
-
-/**
- * Perform the bookkeeping required to cancel.
- *
- * @private
- */
-FileCopyManager.prototype.doCancel_ = function() {
-  this.resetQueue_();
-  this.cancelRequested_ = false;
-  this.eventRouter_.sendProgressEvent('CANCELLED', this.getStatus());
-};
-
-/**
- * Used internally to check if a cancel has been requested, and handle
- * it if so.
- *
- * @return {boolean} If canceled.
- * @private
- */
-FileCopyManager.prototype.maybeCancel_ = function() {
-  if (!this.cancelRequested_)
-    return false;
-
-  this.doCancel_();
-  return true;
-};
-
-/**
- * Kick off pasting.
- *
- * @param {Array.<string>} sourcePaths Path of the source files.
- * @param {string} targetPath The destination path of the target directory.
- * @param {boolean} isMove True if the operation is "move", otherwise (i.e.
- *     if the operation is "copy") false.
- */
-FileCopyManager.prototype.paste = function(sourcePaths, targetPath, isMove) {
-  // Do nothing if sourcePaths is empty.
-  if (sourcePaths.length == 0)
-    return;
-
-  var errorCallback = function(error) {
-    this.eventRouter_.sendProgressEvent(
-        'ERROR',
-        this.getStatus(),
-        new FileCopyManager.Error(
-            util.FileOperationErrorType.FILESYSTEM_ERROR, error));
-  }.bind(this);
-
-  var targetEntry = null;
-  var entries = [];
-
-  // Resolve paths to entries.
-  var resolveGroup = new AsyncUtil.Group();
-  resolveGroup.add(function(callback) {
-    webkitResolveLocalFileSystemURL(
-        util.makeFilesystemUrl(targetPath),
-        function(entry) {
-          if (!entry.isDirectory) {
-            // Found a non directory entry.
-            errorCallback(util.createFileError(FileError.TYPE_MISMATCH_ERR));
-            return;
-          }
-
-          targetEntry = entry;
-          callback();
-        },
-        errorCallback);
-  });
-
-  for (var i = 0; i < sourcePaths.length; i++) {
-    resolveGroup.add(function(sourcePath, callback) {
-      webkitResolveLocalFileSystemURL(
-          util.makeFilesystemUrl(sourcePath),
-          function(entry) {
-            entries.push(entry);
-            callback();
-          },
-          errorCallback);
-    }.bind(this, sourcePaths[i]));
-  }
-
-  resolveGroup.run(function() {
-    if (isMove) {
-      // Moving to the same directory is a redundant operation.
-      entries = entries.filter(function(entry) {
-        return targetEntry.fullPath + '/' + entry.name != entry.fullPath;
-      });
-
-      // Do nothing, if we have no entries to be moved.
-      if (entries.length == 0)
-        return;
-    }
-
-    this.queueCopy_(targetEntry, entries, isMove);
-  }.bind(this));
-};
-
-/**
- * Checks if the move operation is avaiable between the given two locations.
- *
- * @param {DirectoryEntry} sourceEntry An entry from the source.
- * @param {DirectoryEntry} targetDirEntry Directory entry for the target.
- * @return {boolean} Whether we can move from the source to the target.
- */
-FileCopyManager.prototype.isMovable = function(sourceEntry,
-                                               targetDirEntry) {
-  return (PathUtil.isDriveBasedPath(sourceEntry.fullPath) &&
-          PathUtil.isDriveBasedPath(targetDirEntry.fullPath)) ||
-         (PathUtil.getRootPath(sourceEntry.fullPath) ==
-          PathUtil.getRootPath(targetDirEntry.fullPath));
-};
-
-/**
- * Initiate a file copy.
- *
- * @param {DirectoryEntry} targetDirEntry Target directory.
- * @param {Array.<Entry>} entries Entries to copy.
- * @param {boolean} isMove In case of move.
- * @return {FileCopyManager.Task} Copy task.
- * @private
- */
-FileCopyManager.prototype.queueCopy_ = function(
-    targetDirEntry, entries, isMove) {
-  // When copying files, null can be specified as source directory.
-  var task;
-  if (isMove) {
-    if (this.isMovable(entries[0], targetDirEntry)) {
-      task = new FileCopyManager.MoveTask(entries, targetDirEntry);
-    } else {
-      task = new FileCopyManager.CopyTask(entries, targetDirEntry);
-      task.deleteAfterCopy = true;
-    }
-  } else {
-    task = new FileCopyManager.CopyTask(entries, targetDirEntry);
-  }
-
-  task.initialize(function() {
-    this.copyTasks_.push(task);
-    this.maybeScheduleCloseBackgroundPage_();
-    if (this.copyTasks_.length == 1) {
-      // Assume this.cancelRequested_ == false.
-      // This moved us from 0 to 1 active tasks, let the servicing begin!
-      this.serviceAllTasks_();
-    } else {
-      // Force to update the progress of butter bar when there are new tasks
-      // coming while servicing current task.
-      this.eventRouter_.sendProgressEvent('PROGRESS', this.getStatus());
-    }
-  }.bind(this));
-
-  return task;
-};
-
-/**
- * Service all pending tasks, as well as any that might appear during the
- * copy.
- *
- * @private
- */
-FileCopyManager.prototype.serviceAllTasks_ = function() {
-  var self = this;
-
-  var onTaskProgress = function() {
-    self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus());
-  };
-
-  var onEntryChanged = function(kind, entry) {
-    self.eventRouter_.sendEntryChangedEvent(kind, entry);
-  };
-
-  var onTaskError = function(err) {
-    if (self.maybeCancel_())
-      return;
-    self.eventRouter_.sendProgressEvent('ERROR', self.getStatus(), err);
-    self.resetQueue_();
-  };
-
-  var onTaskSuccess = function() {
-    if (self.maybeCancel_())
-      return;
-
-    // The task at the front of the queue is completed. Pop it from the queue.
-    self.copyTasks_.shift();
-    self.maybeScheduleCloseBackgroundPage_();
-
-    if (!self.copyTasks_.length) {
-      // All tasks have been serviced, clean up and exit.
-      self.eventRouter_.sendProgressEvent('SUCCESS', self.getStatus());
-      self.resetQueue_();
-      return;
-    }
-
-    // We want to dispatch a PROGRESS event when there are more tasks to serve
-    // right after one task finished in the queue. We treat all tasks as one
-    // big task logically, so there is only one BEGIN/SUCCESS event pair for
-    // these continuous tasks.
-    self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus());
-    self.copyTasks_[0].run(
-        onEntryChanged, onTaskProgress, onTaskSuccess, onTaskError);
-  };
-
-  // If the queue size is 1 after pushing our task, it was empty before,
-  // so we need to kick off queue processing and dispatch BEGIN event.
-  this.eventRouter_.sendProgressEvent('BEGIN', this.getStatus());
-  this.copyTasks_[0].run(
-      onEntryChanged, onTaskProgress, onTaskSuccess, onTaskError);
-};
-
-/**
- * Timeout before files are really deleted (to allow undo).
- */
-FileCopyManager.DELETE_TIMEOUT = 30 * 1000;
-
-/**
- * Schedules the files deletion.
- *
- * @param {Array.<Entry>} entries The entries.
- */
-FileCopyManager.prototype.deleteEntries = function(entries) {
-  var task = { entries: entries };
-  this.deleteTasks_.push(task);
-  this.maybeScheduleCloseBackgroundPage_();
-  if (this.deleteTasks_.length == 1)
-    this.serviceAllDeleteTasks_();
-};
-
-/**
- * Service all pending delete tasks, as well as any that might appear during the
- * deletion.
- *
- * @private
- */
-FileCopyManager.prototype.serviceAllDeleteTasks_ = function() {
-  var self = this;
-
-  var onTaskSuccess = function() {
-    var task = self.deleteTasks_[0];
-    self.deleteTasks_.shift();
-    if (!self.deleteTasks_.length) {
-      // All tasks have been serviced, clean up and exit.
-      self.eventRouter_.sendDeleteEvent(
-          'SUCCESS',
-          task.entries.map(function(e) {
-            return util.makeFilesystemUrl(e.fullPath);
-          }));
-      self.maybeScheduleCloseBackgroundPage_();
-      return;
-    }
-
-    // We want to dispatch a PROGRESS event when there are more tasks to serve
-    // right after one task finished in the queue. We treat all tasks as one
-    // big task logically, so there is only one BEGIN/SUCCESS event pair for
-    // these continuous tasks.
-    self.eventRouter_.sendDeleteEvent(
-        'PROGRESS',
-        task.entries.map(function(e) {
-          return util.makeFilesystemUrl(e.fullPath);
-        }));
-    self.serviceDeleteTask_(self.deleteTasks_[0], onTaskSuccess, onTaskFailure);
-  };
-
-  var onTaskFailure = function(task) {
-    self.deleteTasks_ = [];
-    self.eventRouter_.sendDeleteEvent(
-        'ERROR',
-        task.entries.map(function(e) {
-          return util.makeFilesystemUrl(e.fullPath);
-        }));
-    self.maybeScheduleCloseBackgroundPage_();
-  };
-
-  // If the queue size is 1 after pushing our task, it was empty before,
-  // so we need to kick off queue processing and dispatch BEGIN event.
-  this.eventRouter_.sendDeleteEvent(
-      'BEGIN',
-      this.deleteTasks_[0].entries.map(function(e) {
-        return util.makeFilesystemUrl(e.fullPath);
-      }));
-  this.serviceDeleteTask_(this.deleteTasks_[0], onTaskSuccess, onTaskFailure);
-};
-
-/**
- * Performs the deletion.
- *
- * @param {Object} task The delete task (see deleteEntries function).
- * @param {function()} successCallback Callback run on success.
- * @param {function(FileCopyManager.Error)} errorCallback Callback run on error.
- * @private
- */
-FileCopyManager.prototype.serviceDeleteTask_ = function(
-    task, successCallback, errorCallback) {
-  var downcount = task.entries.length;
-  if (downcount == 0) {
-    successCallback();
-    return;
-  }
-
-  var filesystemError = null;
-  var onComplete = function() {
-    if (--downcount > 0)
-      return;
-
-    // All remove operations are processed. Run callback.
-    if (filesystemError) {
-      errorCallback(new FileCopyManager.Error(
-          util.FileOperationErrorType.FILESYSTEM_ERROR, filesystemError));
-    } else {
-      successCallback();
-    }
-  };
-
-  for (var i = 0; i < task.entries.length; i++) {
-    var entry = task.entries[i];
-    util.removeFileOrDirectory(
-        entry,
-        function(currentEntry) {
-          this.eventRouter_.sendEntryChangedEvent(
-              util.EntryChangedKind.DELETED, currentEntry);
-          onComplete();
-        }.bind(this, entry),
-        function(error) {
-          if (!filesystemError)
-            filesystemError = error;
-          onComplete();
-        });
-  }
-};
-
-/**
- * Creates a zip file for the selection of files.
- *
- * @param {Entry} dirEntry The directory containing the selection.
- * @param {Array.<Entry>} selectionEntries The selected entries.
- */
-FileCopyManager.prototype.zipSelection = function(dirEntry, selectionEntries) {
-  var self = this;
-  var zipTask = new FileCopyManager.ZipTask(
-      selectionEntries, dirEntry, dirEntry);
-  zipTask.zip = true;
-  zipTask.initialize(function() {
-    self.copyTasks_.push(zipTask);
-    if (self.copyTasks_.length == 1) {
-      // Assume self.cancelRequested_ == false.
-      // This moved us from 0 to 1 active tasks, let the servicing begin!
-      self.serviceAllTasks_();
-    } else {
-      // Force to update the progress of butter bar when there are new tasks
-      // coming while servicing current task.
-      self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus());
-    }
-  });
-};
diff --git a/chrome/browser/resources/file_manager/js/file_copy_manager_wrapper.js b/chrome/browser/resources/file_manager/js/file_copy_manager_wrapper.js
deleted file mode 100644
index 767aaad..0000000
--- a/chrome/browser/resources/file_manager/js/file_copy_manager_wrapper.js
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-'use strict';
-
-/**
- * While FileCopyManager is run in the background page, this class is used to
- * communicate with it.
- * @constructor
- */
-function FileCopyManagerWrapper() {
-  this.fileCopyManager_ = null;
-
-  /**
-   * In the constructor, it tries to start loading the background page, and
-   * its FileCopyManager instance, asynchronously. Even during the
-   * initialization, there may be some method invocation. In such a case,
-   * FileCopyManagerWrapper keeps it as a pending task in pendingTasks_,
-   * and once FileCopyManager is obtained, run the pending tasks in the order.
-   *
-   * @private {Array.<function(FileCopyManager)>}
-   */
-  this.pendingTasks_ = [];
-
-  chrome.runtime.getBackgroundPage(function(backgroundPage) {
-    this.fileCopyManager_ = backgroundPage.FileCopyManager.getInstance();
-
-    // Run pending tasks.
-    for (var i = 0; i < this.pendingTasks_.length; i++) {
-      this.pendingTasks_[i](this.fileCopyManager_);
-    }
-    this.pendingTasks_ = [];
-  }.bind(this));
-}
-
-/**
- * Create a new instance or get existing instance of FCMW.
- * @return {FileCopyManagerWrapper}  A FileCopyManagerWrapper instance.
- */
-FileCopyManagerWrapper.getInstance = function() {
-  if (!FileCopyManagerWrapper.instance_)
-    FileCopyManagerWrapper.instance_ = new FileCopyManagerWrapper();
-
-  return FileCopyManagerWrapper.instance_;
-};
-
-/**
- * @return {boolean} True if there is a running task.
- */
-FileCopyManagerWrapper.prototype.isRunning = function() {
-  // Note: until the background page is loaded, this method returns false
-  // even if there is a running task in FileCopyManager. (Though the period
-  // should be very short).
-  // TODO(hidehiko): Fix the race condition. It is necessary to change the
-  // FileCopyManager implementation as well as the clients (FileManager/
-  // ButterBar) implementation.
-  return this.fileCopyManager_ && this.fileCopyManager_.hasQueuedTasks();
-};
-
-/**
- * Decorates a FileCopyManager method, so it will be executed after initializing
- * the FileCopyManager instance in background page.
- * @param {string} method The method name.
- */
-FileCopyManagerWrapper.decorateAsyncMethod = function(method) {
-  FileCopyManagerWrapper.prototype[method] = function() {
-    var args = Array.prototype.slice.call(arguments);
-    var operation = function(fileCopyManager) {
-      fileCopyManager.willRunNewMethod();
-      fileCopyManager[method].apply(fileCopyManager, args);
-    };
-    if (this.fileCopyManager_)
-      operation(this.fileCopyManager_);
-    else
-      this.pendingTasks_.push(operation);
-  };
-};
-
-FileCopyManagerWrapper.decorateAsyncMethod('requestCancel');
-FileCopyManagerWrapper.decorateAsyncMethod('paste');
-FileCopyManagerWrapper.decorateAsyncMethod('deleteEntries');
-FileCopyManagerWrapper.decorateAsyncMethod('forceDeleteTask');
-FileCopyManagerWrapper.decorateAsyncMethod('cancelDeleteTask');
-FileCopyManagerWrapper.decorateAsyncMethod('zipSelection');
-FileCopyManagerWrapper.decorateAsyncMethod('addEventListener');
-FileCopyManagerWrapper.decorateAsyncMethod('removeEventListener');
diff --git a/chrome/browser/resources/file_manager/js/file_manager.js b/chrome/browser/resources/file_manager/js/file_manager.js
index 7297b23..bab9f52 100644
--- a/chrome/browser/resources/file_manager/js/file_manager.js
+++ b/chrome/browser/resources/file_manager/js/file_manager.js
@@ -246,6 +246,16 @@
       }.bind(this));
     }.bind(this));
 
+    // TODO(yoshiki): Remove the flag when the feature is launched.
+    this.enableExperimentalWebstoreIntegration_ = false;
+    group.add(function(done) {
+      chrome.commandLinePrivate.hasSwitch(
+          'file-manager-enable-webstore-integration', function(flag) {
+        this.enableExperimentalWebstoreIntegration_ = flag;
+        done();
+      }.bind(this));
+    }.bind(this));
+
     group.run(callback);
   };
 
@@ -419,28 +429,28 @@
    * @private
    */
   FileManager.prototype.initDataTransferOperations_ = function() {
-    this.copyManager_ = new FileCopyManagerWrapper.getInstance();
+    this.fileOperationManager_ = new FileOperationManagerWrapper.getInstance();
 
-    this.butterBar_ = new ButterBar(this.dialogDom_, this.copyManager_);
+    this.butterBar_ = new ButterBar(
+        this.dialogDom_, this.fileOperationManager_);
 
     // CopyManager and ButterBar are required for 'Delete' operation in
     // Open and Save dialogs. But drag-n-drop and copy-paste are not needed.
     if (this.dialogType != DialogType.FULL_PAGE) return;
 
-    // TODO(hidehiko): Extract FileCopyManager related code from FileManager
-    // to simplify it.
+    // TODO(hidehiko): Extract FileOperationManager related code from
+    // FileManager to simplify it.
     this.onCopyProgressBound_ = this.onCopyProgress_.bind(this);
-    this.copyManager_.addEventListener(
+    this.fileOperationManager_.addEventListener(
         'copy-progress', this.onCopyProgressBound_);
 
-    this.onCopyManagerEntryChangedBound_ =
-        this.onCopyManagerEntryChanged_.bind(this);
-    this.copyManager_.addEventListener(
-        'entry-changed', this.onCopyManagerEntryChangedBound_);
+    this.onEntryChangedBound_ = this.onEntryChanged_.bind(this);
+    this.fileOperationManager_.addEventListener(
+        'entry-changed', this.onEntryChangedBound_);
 
     var controller = this.fileTransferController_ =
         new FileTransferController(this.document_,
-                                   this.copyManager_,
+                                   this.fileOperationManager_,
                                    this.metadataCache_,
                                    this.directoryModel_);
     controller.attachDragSource(this.table_.list);
@@ -856,6 +866,8 @@
     this.shareDialog_ = new ShareDialog(this.dialogDom_);
     this.defaultTaskPicker =
         new cr.filebrowser.DefaultActionDialog(this.dialogDom_);
+    this.suggestAppsDialog =
+        new SuggestAppsDialog(this.dialogDom_);
   };
 
   /**
@@ -935,9 +947,9 @@
     this.cancelButton_.addEventListener('click', this.onCancelBound_);
 
     this.decorateSplitter(
-        this.dialogDom_.querySelector('div#sidebar-splitter'));
+        this.dialogDom_.querySelector('#navigation-list-splitter'));
     this.decorateSplitter(
-        this.dialogDom_.querySelector('div#middlebar-splitter'));
+        this.dialogDom_.querySelector('#middlebar-splitter'));
 
     this.dialogContainer_ = this.dialogDom_.querySelector('.dialog-container');
 
@@ -1156,9 +1168,9 @@
     // published at the end of drag selection.
     this.table_.list.addEventListener('dragselectionend', dragEndBound);
 
-    // TODO(mtomasz, yoshiki): Create sidebar earlier, and here just attach
-    // the directory model.
-    this.initSidebar_();
+    // TODO(mtomasz, yoshiki): Create navigation list earlier, and here just
+    // attach the directory model.
+    this.initNavigationList_();
 
     this.table_.addEventListener('column-resize-end',
                                  this.updateStartupPrefs_.bind(this));
@@ -1197,11 +1209,11 @@
   /**
    * @private
    */
-  FileManager.prototype.initSidebar_ = function() {
+  FileManager.prototype.initNavigationList_ = function() {
     this.directoryTree_ = this.dialogDom_.querySelector('#directory-tree');
     DirectoryTree.decorate(this.directoryTree_, this.directoryModel_);
 
-    this.navigationList_ = this.dialogDom_.querySelector('#volume-list');
+    this.navigationList_ = this.dialogDom_.querySelector('#navigation-list');
     NavigationList.decorate(this.navigationList_,
                             this.volumeManager_,
                             this.directoryModel_);
@@ -1416,12 +1428,12 @@
    * This updates directory model to reflect operation result immediately (not
    * waiting for directory update event). Also, preloads thumbnails for the
    * images of new entries.
-   * See also FileCopyManager.EventRouter.
+   * See also FileOperationManager.EventRouter.
    *
    * @param {cr.Event} event An event for the entry change.
    * @private
    */
-  FileManager.prototype.onCopyManagerEntryChanged_ = function(event) {
+  FileManager.prototype.onEntryChanged_ = function(event) {
     var kind = event.kind;
     var entry = event.entry;
     this.directoryModel_.onEntryChanged(kind, entry);
@@ -1968,7 +1980,9 @@
       self.initDateTimeFormatters_();
       self.refreshCurrentDirectoryMetadata_();
 
-      self.volumeManager_.setDriveEnabled(self.isDriveEnabled());
+      var isDriveEnabled = self.isDriveEnabled();
+      self.volumeManager_.setDriveEnabled(isDriveEnabled);
+      self.navigationList_.dataModel.showShortcuts(isDriveEnabled);
 
       if (prefs.cellularDisabled)
         self.syncButton.setAttribute('checked', '');
@@ -2159,7 +2173,7 @@
         strf('GALLERY_CONFIRM_DELETE_ONE', entries[0].name) :
         strf('GALLERY_CONFIRM_DELETE_SOME', entries.length);
     this.confirm.show(message, function() {
-      this.copyManager_.deleteEntries(entries);
+      this.fileOperationManager_.deleteEntries(entries);
     }.bind(this));
   };
 
@@ -2301,8 +2315,52 @@
    */
   FileManager.prototype.dispatchSelectionAction_ = function() {
     if (this.dialogType == DialogType.FULL_PAGE) {
-      var tasks = this.getSelection().tasks;
-      if (tasks) tasks.executeDefault();
+      var selection = this.getSelection();
+      var tasks = selection.tasks;
+      var urls = selection.urls;
+      var mimeTypes = selection.mimeTypes;
+      if (tasks) {
+        tasks.executeDefault(function(result) {
+          if (result)
+            return;
+
+          var showAlert = function() {
+            var filename = decodeURIComponent(urls[0]);
+            if (filename.indexOf('/') != -1)
+              filename = filename.substr(filename.lastIndexOf('/') + 1);
+            var extension = filename.lastIndexOf('.') != -1 ?
+                filename.substr(filename.lastIndexOf('.') + 1) : '';
+            var mimeType = mimeTypes && mimeTypes[0];
+
+            var messageString =
+                extension == 'exe' ? 'NO_ACTION_FOR_EXECUTABLE' :
+                                     'NO_ACTION_FOR_FILE';
+            var webStoreUrl = FileTasks.createWebStoreLink(extension, mimeType);
+            var text = loadTimeData.getStringF(
+                messageString,
+                webStoreUrl,
+                FileTasks.NO_ACTION_FOR_FILE_URL);
+            this.alert.showHtml(filename, text, function() {});
+          }.bind(this);
+
+          if (!this.enableExperimentalWebstoreIntegration_) {
+            showAlert();
+            return;
+          }
+
+          this.openSuggestAppsDialog_(urls,
+            // Success callback.
+            function() {
+              var tasks = new FileTasks(this);
+              tasks.init(urls, mimeTypes);
+              tasks.executeDefault();
+            }.bind(this),
+            // Failure callback.
+            function() {},
+            // Cancelled callback.
+            showAlert);
+        }.bind(this));
+      }
       return true;
     }
     if (!this.okButton_.disabled) {
@@ -2313,6 +2371,42 @@
   };
 
   /**
+   * Opens the suggest file dialog.
+   *
+   * @param {Array.<string>} urls List of URLs of files.
+   * @param {function()} onSuccess Success callback.
+   * @param {function()} onCancelled User-cancelled callback.
+   * @param {function()} onFailure Failure callback.
+   * @private
+   */
+  FileManager.prototype.openSuggestAppsDialog_ =
+      function(urls, onSuccess, onCancelled, onFailure) {
+    if (!urls || urls.length != 1) {
+      onFailure();
+      return;
+    }
+
+    this.metadataCache_.get(urls, 'drive', function(props) {
+      if (!props || !props[0] || !props[0].contentMimeType) {
+        onFailure();
+        return;
+      }
+
+      var filename = util.extractFilePath(urls[0]);
+      var extension = PathUtil.extractExtension(filename);
+      var mime = props[0].contentMimeType;
+      this.suggestAppsDialog.show(
+          extension, mime,
+          function(installed) {
+            if (installed)
+              onSuccess();
+            else
+              onCancelled();
+          });
+    }.bind(this));
+  };
+
+  /**
    * Executes directory action (i.e. changes directory).
    *
    * @param {DirectoryEntry} entry Directory entry to which directory should be
@@ -2485,14 +2579,14 @@
       this.filePopup_.contentWindow.unload(true /* exiting */);
     if (this.butterBar_)
       this.butterBar_.dispose();
-    if (this.copyManager_) {
+    if (this.fileOperationManager_) {
       if (this.onCopyProgressBound_) {
-        this.copyManager_.removeEventListener(
+        this.fileOperationManager_.removeEventListener(
             'copy-progress', this.onCopyProgressBound_);
       }
-      if (this.onCopyManagerEntryChangedBound_) {
-        this.copyManager_.removeEventListener(
-            'entry-changed', this.onCopyManagerEntryChangedBound_);
+      if (this.onEntryChangedBound_) {
+        this.fileOperationManager_.removeEventListener(
+            'entry-changed', this.onEntryChangedBound_);
       }
     }
   };
@@ -2931,10 +3025,11 @@
         return;
 
       case '27':  // Escape => Cancel dialog.
-        if (this.copyManager_ && this.copyManager_.isRunning()) {
+        if (this.fileOperationManager_ &&
+            this.fileOperationManager_.isRunning()) {
           // If there is a copy in progress, ESC will cancel it.
           event.preventDefault();
-          this.copyManager_.requestCancel();
+          this.fileOperationManager_.requestCancel();
           return;
         }
 
diff --git a/chrome/browser/resources/file_manager/js/file_manager_commands.js b/chrome/browser/resources/file_manager/js/file_manager_commands.js
index db2d454..1de221f 100644
--- a/chrome/browser/resources/file_manager/js/file_manager_commands.js
+++ b/chrome/browser/resources/file_manager/js/file_manager_commands.js
@@ -48,6 +48,35 @@
 };
 
 /**
+ * Extracts path on which command event was dispatched.
+ * TODO(yoshiki): Remove this method after adding automatically removing an
+ *                inactive shortcut on M31.
+ *
+ * @param {NavigationList|NavigationListItem} element Directory to extract a
+ *     path from.
+ * @return {string} Path of the found node.
+ */
+CommandUtil.getCommandPath = function(element) {
+  if (element instanceof NavigationList) {
+    // element is a NavigationList.
+
+    /** @type {NavigationModelItem} */
+    var selectedItem = element.selectedItem;
+    return selectedItem && selectedItem.path;
+  } else if (element instanceof NavigationListItem) {
+    // element is a subitem of NavigationList.
+    /** @type {NavigationList} */
+    var navigationList = element.parentElement;
+    var index = navigationList.getIndexOfListItem(element);
+    /** @type {NavigationModelItem} */
+    var item = (index != -1) ? navigationList.dataModel.item(index) : null;
+    return item && item.path;
+  }
+
+  console.error('Unsupported element.');
+};
+
+/**
  * @param {NavigationList} navigationList navigation list to extract root node.
  * @return {?RootType} Type of the found root.
  */
@@ -348,9 +377,9 @@
 Commands.volumeHelpCommand = {
   execute: function() {
     if (fileManager.isOnDrive())
-      chrome.windows.create({url: FileManager.GOOGLE_DRIVE_HELP});
+      util.visitURL(FileManager.GOOGLE_DRIVE_HELP);
     else
-      chrome.windows.create({url: FileManager.FILES_APP_HELP});
+      util.visitURL(FileManager.FILES_APP_HELP);
   },
   canExecute: CommandUtil.canExecuteAlways
 };
@@ -360,7 +389,7 @@
  */
 Commands.driveBuySpaceCommand = {
   execute: function() {
-    chrome.windows.create({url: FileManager.GOOGLE_DRIVE_BUY_STORAGE});
+    util.visitURL(FileManager.GOOGLE_DRIVE_BUY_STORAGE);
   },
   canExecute: CommandUtil.canExecuteVisibleOnDriveOnly
 };
@@ -380,7 +409,7 @@
  */
 Commands.driveGoToDriveCommand = {
   execute: function() {
-    chrome.windows.create({url: FileManager.GOOGLE_DRIVE_ROOT});
+    util.visitURL(FileManager.GOOGLE_DRIVE_ROOT);
   },
   canExecute: CommandUtil.canExecuteVisibleOnDriveOnly
 };
@@ -534,7 +563,7 @@
   execute: function(event, fileManager, directoryModel) {
     var dirEntry = directoryModel.getCurrentDirEntry();
     var selectionEntries = fileManager.getSelection().entries;
-    fileManager.copyManager_.zipSelection(dirEntry, selectionEntries);
+    fileManager.fileOperationManager_.zipSelection(dirEntry, selectionEntries);
   },
   canExecute: function(event, fileManager) {
     var selection = fileManager.getSelection();
@@ -619,8 +648,10 @@
    */
   execute: function(event, fileManager) {
     var entry = CommandUtil.getCommandEntry(event.target);
-    if (entry)
-      fileManager.removeFolderShortcut(entry.fullPath);
+    var path =
+        entry ? entry.fullPath : CommandUtil.getCommandPath(event.target);
+    if (path)
+      fileManager.removeFolderShortcut(path);
   },
 
   /**
@@ -638,9 +669,11 @@
     }
 
     var entry = CommandUtil.getCommandEntry(target);
-    var eligible = entry &&
-                   PathUtil.isEligibleForFolderShortcut(entry.fullPath);
-    var isShortcut = entry && fileManager.folderShortcutExists(entry.fullPath);
+    var path =
+        entry ? entry.fullPath : CommandUtil.getCommandPath(event.target);
+
+    var eligible = path && PathUtil.isEligibleForFolderShortcut(path);
+    var isShortcut = path && fileManager.folderShortcutExists(path);
     event.canExecute = isShortcut && eligible;
     event.command.setHidden(!event.canExecute);
   }
diff --git a/chrome/browser/resources/file_manager/js/file_operation_manager.js b/chrome/browser/resources/file_manager/js/file_operation_manager.js
new file mode 100644
index 0000000..adb9ba8
--- /dev/null
+++ b/chrome/browser/resources/file_manager/js/file_operation_manager.js
@@ -0,0 +1,1528 @@
+// 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.
+
+'use strict';
+
+/**
+ * Utilities for FileOperationManager.
+ */
+var fileOperationUtil = {};
+
+/**
+ * Simple wrapper for util.deduplicatePath. On error, this method translates
+ * the FileError to FileOperationManager.Error object.
+ *
+ * @param {DirectoryEntry} dirEntry The target directory entry.
+ * @param {string} relativePath The path to be deduplicated.
+ * @param {function(string)} successCallback Callback run with the deduplicated
+ *     path on success.
+ * @param {function(FileOperationManager.Error)} errorCallback Callback run on
+ *     error.
+ */
+fileOperationUtil.deduplicatePath = function(
+    dirEntry, relativePath, successCallback, errorCallback) {
+  util.deduplicatePath(
+      dirEntry, relativePath, successCallback,
+      function(err) {
+        var onFileSystemError = function(error) {
+          errorCallback(new FileOperationManager.Error(
+              util.FileOperationErrorType.FILESYSTEM_ERROR, error));
+        };
+
+        if (err.code == FileError.PATH_EXISTS_ERR) {
+          // Failed to uniquify the file path. There should be an existing
+          // entry, so return the error with it.
+          util.resolvePath(
+              dirEntry, relativePath,
+              function(entry) {
+                errorCallback(new FileOperationManager.Error(
+                    util.FileOperationErrorType.TARGET_EXISTS, entry));
+              },
+              onFileSystemError);
+          return;
+        }
+        onFileSystemError(err);
+      });
+};
+
+/**
+ * Sets last modified date to the entry.
+ * @param {Entry} entry The entry to which the last modified is set.
+ * @param {Date} modificationTime The last modified time.
+ */
+fileOperationUtil.setLastModified = function(entry, modificationTime) {
+  chrome.fileBrowserPrivate.setLastModified(
+      entry.toURL(), '' + Math.round(modificationTime.getTime() / 1000));
+};
+
+/**
+ * Copies source to parent with the name newName recursively.
+ *
+ * @param {Entry} source The entry to be copied.
+ * @param {DirectoryEntry} parent The entry of the destination directory.
+ * @param {string} newName The name of copied file.
+ * @param {function(util.EntryChangedKind, Entry)} entryChangedCallback
+ *     Callback invoked when an entry is changed.
+ * @param {function(Entry, number)} progressCallback Callback invoked
+ *     periodically during the copying. It takes source and the number of
+ *     copied bytes since the last invocation.
+ * @param {function(Entry)} successCallback Callback invoked when the copy
+ *     is successfully done with the entry of the created entry.
+ * @param {function(FileError)} errorCallback Callback invoked when an error
+ *     is found.
+ * @return {function()} Callback to cancel the current file copy operation.
+ *     When the cancel is done, errorCallback will be called. The returned
+ *     callback must not be called more than once.
+ */
+fileOperationUtil.copyRecursively = function(
+    source, parent, newName, entryChangedCallback, progressCallback,
+    successCallback, errorCallback) {
+  // Notify that the copy begins for each entry.
+  progressCallback(source, 0);
+
+  // If the entry is a file, redirect it to copyFile_().
+  if (source.isFile) {
+    return fileOperationUtil.copyFile_(
+        source, parent, newName, progressCallback,
+        function(entry) {
+          entryChangedCallback(util.EntryChangedKind.CREATED, entry);
+          successCallback(entry);
+        },
+        errorCallback);
+  }
+
+  // Hereafter, the source is directory.
+  var cancelRequested = false;
+  var cancelCallback = null;
+
+  // First, we create the directory copy.
+  parent.getDirectory(
+      newName, {create: true, exclusive: true},
+      function(dirEntry) {
+        entryChangedCallback(util.EntryChangedKind.CREATED, dirEntry);
+        if (cancelRequested) {
+          errorCallback(util.createFileError(FileError.ABORT_ERR));
+          return;
+        }
+
+        // Iterate on children, and copy them recursively.
+        util.forEachDirEntry(
+            source,
+            function(child, callback) {
+              if (cancelRequested) {
+                errorCallback(util.createFileError(FileError.ABORT_ERR));
+                return;
+              }
+
+              cancelCallback = fileOperationUtil.copyRecursively(
+                  child, dirEntry, child.name, entryChangedCallback,
+                  progressCallback,
+                  function() {
+                    cancelCallback = null;
+                    callback();
+                  },
+                  function(error) {
+                    cancelCallback = null;
+                    errorCallback(error);
+                  });
+            },
+            function() {
+              successCallback(dirEntry);
+            },
+            errorCallback);
+      },
+      errorCallback);
+
+  return function() {
+    cancelRequested = true;
+    if (cancelCallback) {
+      cancelCallback();
+      cancelCallback = null;
+    }
+  };
+};
+
+/**
+ * Copies a file from source to the parent directory with newName.
+ * See also copyFileByStream_ and copyFileOnDrive_ for the implementation
+ * details.
+ *
+ * @param {FileEntry} source The file entry to be copied.
+ * @param {DirectoryEntry} parent The entry of the destination directory.
+ * @param {string} newName The name of copied file.
+ * @param {function(FileEntry, number)} progressCallback Callback invoked
+ *     periodically during the file writing. It takes source and the number of
+ *     copied bytes since the last invocation. This is also called just before
+ *     starting the operation (with '0' bytes) and just after the finishing the
+ *     operation (with the total copied size).
+ * @param {function(FileEntry)} successCallback Callback invoked when the copy
+ *     is successfully done with the entry of the created file.
+ * @param {function(FileError)} errorCallback Callback invoked when an error
+ *     is found.
+ * @return {function()} Callback to cancel the current file copy operation.
+ *     When the cancel is done, errorCallback will be called. The returned
+ *     callback must not be called more than once.
+ * @private
+ */
+fileOperationUtil.copyFile_ = function(
+    source, parent, newName, progressCallback, successCallback, errorCallback) {
+  if (!PathUtil.isDriveBasedPath(source.fullPath) &&
+      !PathUtil.isDriveBasedPath(parent.fullPath)) {
+    // Copying a file between non-Drive file systems.
+    return fileOperationUtil.copyFileByStream_(
+        source, parent, newName, progressCallback, successCallback,
+        errorCallback);
+  } else {
+    // Copying related to the Drive file system.
+    return fileOperationUtil.copyFileOnDrive_(
+        source, parent, newName, progressCallback, successCallback,
+        errorCallback);
+  }
+};
+
+/**
+ * Copies a file by using File and FileWriter objects.
+ *
+ * This is a js-implementation of FileEntry.copyTo(). Unfortunately, copyTo
+ * doesn't support periodical progress updating nor cancelling. To support
+ * these operations, this method implements copyTo by streaming way in
+ * JavaScript.
+ *
+ * Note that this is designed for file copying on local file system. We have
+ * some special cases about copying on Drive file system. See also
+ * copyFileOnDrive_() for more details.
+ *
+ * @param {FileEntry} source The file entry to be copied.
+ * @param {DirectoryEntry} parent The entry of the destination directory.
+ * @param {string} newName The name of copied file.
+ * @param {function(FileEntry, number)} progressCallback Callback invoked
+ *     periodically during the file writing. It takes source and the number of
+ *     copied bytes since the last invocation. This is also called just before
+ *     starting the operation (with '0' bytes) and just after the finishing the
+ *     operation (with the total copied size).
+ * @param {function(FileEntry)} successCallback Callback invoked when the copy
+ *     is successfully done with the entry of the created file.
+ * @param {function(FileError)} errorCallback Callback invoked when an error
+ *     is found.
+ * @return {function()} Callback to cancel the current file copy operation.
+ *     When the cancel is done, errorCallback will be called. The returned
+ *     callback must not be called more than once.
+ * @private
+ */
+fileOperationUtil.copyFileByStream_ = function(
+    source, parent, newName, progressCallback, successCallback, errorCallback) {
+  // Set to true when cancel is requested.
+  var cancelRequested = false;
+
+  source.file(function(file) {
+    if (cancelRequested) {
+      errorCallback(util.createFileError(FileError.ABORT_ERR));
+      return;
+    }
+
+    parent.getFile(newName, {create: true, exclusive: true}, function(target) {
+      if (cancelRequested) {
+        errorCallback(util.createFileError(FileError.ABORT_ERR));
+        return;
+      }
+
+      target.createWriter(function(writer) {
+        if (cancelRequested) {
+          errorCallback(util.createFileError(FileError.ABORT_ERR));
+          return;
+        }
+
+        writer.onerror = writer.onabort = function(progress) {
+          errorCallback(cancelRequested ?
+              util.createFileError(FileError.ABORT_ERR) :
+                  writer.error);
+        };
+
+        var reportedProgress = 0;
+        writer.onprogress = function(progress) {
+          if (cancelRequested) {
+            // If the copy was cancelled, we should abort the operation.
+            // The errorCallback will be called by writer.onabort after the
+            // termination.
+            writer.abort();
+            return;
+          }
+
+          // |progress.loaded| will contain total amount of data copied by now.
+          // |progressCallback| expects data amount delta from the last progress
+          // update.
+          progressCallback(target, progress.loaded - reportedProgress);
+          reportedProgress = progress.loaded;
+        };
+
+        writer.onwrite = function() {
+          if (cancelRequested) {
+            errorCallback(util.createFileError(FileError.ABORT_ERR));
+            return;
+          }
+
+          source.getMetadata(function(metadata) {
+            if (cancelRequested) {
+              errorCallback(util.createFileError(FileError.ABORT_ERR));
+              return;
+            }
+
+            fileOperationUtil.setLastModified(
+                target, metadata.modificationTime);
+            successCallback(target);
+          }, errorCallback);
+        };
+
+        writer.write(file);
+      }, errorCallback);
+    }, errorCallback);
+  }, errorCallback);
+
+  return function() { cancelRequested = true; };
+};
+
+/**
+ * Copies a file a) from Drive to local, b) from local to Drive, or c) from
+ * Drive to Drive.
+ * Currently, we need to take care about following two things for Drive:
+ *
+ * 1) Copying hosted document.
+ * In theory, it is impossible to actual copy a hosted document to other
+ * file system. Thus, instead, Drive file system backend creates a JSON file
+ * referring to the hosted document. Also, when it is uploaded by copyTo,
+ * the hosted document is copied on the server. Note that, this doesn't work
+ * when a user creates a file by FileWriter (as copyFileEntry_ does).
+ *
+ * 2) File transfer between local and Drive server.
+ * There are two directions of file transfer; from local to Drive and from
+ * Drive to local.
+ * The file transfer from local to Drive is done as a part of file system
+ * background sync (kicked after the copy operation is done). So we don't need
+ * to take care about it here. To copy the file from Drive to local (or Drive
+ * to Drive with GData WAPI), we need to download the file content (if it is
+ * not locally cached). During the downloading, we can listen the periodical
+ * updating and cancel the downloding via private API.
+ *
+ * This function supports progress updating and cancelling partially.
+ * Unfortunately, FileEntry.copyTo doesn't support progress updating nor
+ * cancelling, so we support them only during file downloading.
+ *
+ * Note: we're planning to move copyTo logic into c++ side. crbug.com/261492
+ *
+ * @param {FileEntry} source The entry of the file to be copied.
+ * @param {DirectoryEntry} parent The entry of the destination directory.
+ * @param {string} newName The name of the copied file.
+ * @param {function(FileEntry, number)} progressCallback Callback periodically
+ *     invoked during file transfer with the source and the number of
+ *     transferred bytes from the last call.
+ * @param {function(FileEntry)} successCallback Callback invoked when the
+ *     file copy is successfully done with the entry of the copied file.
+ * @param {function(FileError)} errorCallback Callback invoked when an error
+ *     is found.
+ * @return {function()} Callback to cancel the current file copy operation.
+ *     When the cancel is done, errorCallback will be called. The returned
+ *     callback must not be called more than once.
+ * @private
+ */
+fileOperationUtil.copyFileOnDrive_ = function(
+    source, parent, newName, progressCallback, successCallback, errorCallback) {
+  // Set to true when cancel is requested.
+  var cancelRequested = false;
+  var cancelCallback = null;
+
+  var onCopyToCompleted = null;
+
+  // Progress callback.
+  // Because the uploading the file from local cache to Drive server will be
+  // done as a part of background Drive file system sync, so for this copy
+  // operation, what we need to take care about is only file downloading.
+  var numTransferredBytes = 0;
+  if (PathUtil.isDriveBasedPath(source.fullPath)) {
+    var sourceUrl = source.toURL();
+    var sourcePath = util.extractFilePath(sourceUrl);
+    var onFileTransfersUpdated = function(statusList) {
+      for (var i = 0; i < statusList.length; i++) {
+        var status = statusList[i];
+
+        // Comparing urls is unreliable, since they may use different
+        // url encoding schemes (eg. rfc2396 vs. rfc3986).
+        var filePath = util.extractFilePath(status.fileUrl);
+        if (filePath == sourcePath) {
+          var processed = status.processed;
+          if (processed > numTransferredBytes) {
+            progressCallback(source, processed - numTransferredBytes);
+            numTransferredBytes = processed;
+          }
+          return;
+        }
+      }
+    };
+
+    // Subscribe to listen file transfer updating notifications.
+    chrome.fileBrowserPrivate.onFileTransfersUpdated.addListener(
+        onFileTransfersUpdated);
+
+    // Currently, we do NOT upload the file during the copy operation.
+    // It will be done as a part of file system sync after copy operation.
+    // So, we can cancel only file downloading.
+    cancelCallback = function() {
+      chrome.fileBrowserPrivate.cancelFileTransfers(
+          [sourceUrl], function() {});
+    };
+
+    // We need to clean up on copyTo completion regardless if it is
+    // successfully done or not.
+    onCopyToCompleted = function() {
+      cancelCallback = null;
+      chrome.fileBrowserPrivate.onFileTransfersUpdated.removeListener(
+          onFileTransfersUpdated);
+    };
+  }
+
+  source.copyTo(
+      parent, newName,
+      function(entry) {
+        if (onCopyToCompleted)
+          onCopyToCompleted();
+
+        if (cancelRequested) {
+          errorCallback(util.createFileError(FileError.ABORT_ERR));
+          return;
+        }
+
+        entry.getMetadata(function(metadata) {
+          if (metadata.size > numTransferredBytes)
+            progressCallback(source, metadata.size - numTransferredBytes);
+          successCallback(entry);
+        }, errorCallback);
+      },
+      function(error) {
+        if (onCopyToCompleted)
+          onCopyToCompleted();
+
+        errorCallback(error);
+      });
+
+  return function() {
+    cancelRequested = true;
+    if (cancelCallback) {
+      cancelCallback();
+      cancelCallback = null;
+    }
+  };
+};
+
+/**
+ * Thin wrapper of chrome.fileBrowserPrivate.zipSelection to adapt its
+ * interface similar to copyTo().
+ *
+ * @param {Array.<Entry>} sources The array of entries to be archived.
+ * @param {DirectoryEntry} parent The entry of the destination directory.
+ * @param {string} newName The name of the archive to be created.
+ * @param {function(FileEntry)} successCallback Callback invoked when the
+ *     operation is successfully done with the entry of the created archive.
+ * @param {function(FileError)} errorCallback Callback invoked when an error
+ *     is found.
+ */
+fileOperationUtil.zipSelection = function(
+    sources, parent, newName, successCallback, errorCallback) {
+  chrome.fileBrowserPrivate.zipSelection(
+      parent.toURL(),
+      sources.map(function(e) { return e.toURL(); }),
+      newName, function(success) {
+        if (!success) {
+          // Failed to create a zip archive.
+          errorCallback(
+              util.createFileError(FileError.INVALID_MODIFICATION_ERR));
+          return;
+        }
+
+        // Returns the created entry via callback.
+        parent.getFile(
+            newName, {create: false}, successCallback, errorCallback);
+      });
+};
+
+/**
+ * @constructor
+ */
+function FileOperationManager() {
+  this.copyTasks_ = [];
+  this.deleteTasks_ = [];
+  this.cancelObservers_ = [];
+  this.cancelRequested_ = false;
+  this.cancelCallback_ = null;
+  this.unloadTimeout_ = null;
+
+  this.eventRouter_ = new FileOperationManager.EventRouter();
+}
+
+/**
+ * Get FileOperationManager instance. In case is hasn't been initialized, a new
+ * instance is created.
+ *
+ * @return {FileOperationManager} A FileOperationManager instance.
+ */
+FileOperationManager.getInstance = function() {
+  if (!FileOperationManager.instance_)
+    FileOperationManager.instance_ = new FileOperationManager();
+
+  return FileOperationManager.instance_;
+};
+
+/**
+ * Manages cr.Event dispatching.
+ * Currently this can send three types of events: "copy-progress",
+ * "copy-operation-completed" and "delete".
+ *
+ * TODO(hidehiko): Reorganize the event dispatching mechanism.
+ * @constructor
+ * @extends {cr.EventTarget}
+ */
+FileOperationManager.EventRouter = function() {
+};
+
+/**
+ * Extends cr.EventTarget.
+ */
+FileOperationManager.EventRouter.prototype.__proto__ = cr.EventTarget.prototype;
+
+/**
+ * Dispatches a simple "copy-progress" event with reason and current
+ * FileOperationManager status. If it is an ERROR event, error should be set.
+ *
+ * @param {string} reason Event type. One of "BEGIN", "PROGRESS", "SUCCESS",
+ *     "ERROR" or "CANCELLED". TODO(hidehiko): Use enum.
+ * @param {Object} status Current FileOperationManager's status. See also
+ *     FileOperationManager.getStatus().
+ * @param {FileOperationManager.Error=} opt_error The info for the error. This
+ *     should be set iff the reason is "ERROR".
+ */
+FileOperationManager.EventRouter.prototype.sendProgressEvent = function(
+    reason, status, opt_error) {
+  var event = new cr.Event('copy-progress');
+  event.reason = reason;
+  event.status = status;
+  if (opt_error)
+    event.error = opt_error;
+  this.dispatchEvent(event);
+};
+
+/**
+ * Dispatches an event to notify that an entry is changed (created or deleted).
+ * @param {util.EntryChangedKind} kind The enum to represent if the entry is
+ *     created or deleted.
+ * @param {Entry} entry The changed entry.
+ */
+FileOperationManager.EventRouter.prototype.sendEntryChangedEvent = function(
+    kind, entry) {
+  var event = new cr.Event('entry-changed');
+  event.kind = kind;
+  event.entry = entry;
+  this.dispatchEvent(event);
+};
+
+/**
+ * Dispatches an event to notify entries are changed for delete task.
+ *
+ * @param {string} reason Event type. One of "BEGIN", "PROGRESS", "SUCCESS",
+ *     or "ERROR". TODO(hidehiko): Use enum.
+ * @param {Array.<string>} urls An array of URLs which are affected by delete
+ *     operation.
+ */
+FileOperationManager.EventRouter.prototype.sendDeleteEvent = function(
+    reason, urls) {
+  var event = new cr.Event('delete');
+  event.reason = reason;
+  event.urls = urls;
+  this.dispatchEvent(event);
+};
+
+/**
+ * A record of a queued copy operation.
+ *
+ * Multiple copy operations may be queued at any given time.  Additional
+ * Tasks may be added while the queue is being serviced.  Though a
+ * cancel operation cancels everything in the queue.
+ *
+ * @param {Array.<Entry>} sourceEntries Array of source entries.
+ * @param {DirectoryEntry} targetDirEntry Target directory.
+ * @constructor
+ */
+FileOperationManager.Task = function(sourceEntries, targetDirEntry) {
+  this.sourceEntries = sourceEntries;
+  this.targetDirEntry = targetDirEntry;
+
+  // TODO(hidehiko): When we support recursive copy, we should be able to
+  // rely on originalEntries. Then remove this.
+  this.entries = [];
+
+  /**
+   * The number of entries, whose processing is completed.
+   * @type {number}
+   */
+  this.numCompletedEntries = 0;
+  this.totalBytes = 0;
+  this.completedBytes = 0;
+
+  /**
+   * The entry currently being processed.
+   * @type {Entry}
+   */
+  this.processingEntry = null;
+
+  this.deleteAfterCopy = false;
+  this.move = false;
+  this.zip = false;
+
+  /**
+   * Set to true when cancel is requested.
+   * @private {boolean}
+   */
+  this.cancelRequested_ = false;
+
+  /**
+   * Callback to cancel the running process.
+   * @private {function()}
+   */
+  this.cancelCallback_ = null;
+
+  // TODO(hidehiko): After we support recursive copy, we don't need this.
+  // If directory already exists, we try to make a copy named 'dir (X)',
+  // where X is a number. When we do this, all subsequent copies from
+  // inside the subtree should be mapped to the new directory name.
+  // For example, if 'dir' was copied as 'dir (1)', then 'dir\file.txt' should
+  // become 'dir (1)\file.txt'.
+  this.renamedDirectories_ = [];
+};
+
+/**
+ * @param {function()} callback When entries resolved.
+ */
+FileOperationManager.Task.prototype.initialize = function(callback) {
+  // When moving directories, FileEntry.moveTo() is used if both source
+  // and target are on Drive. There is no need to recurse into directories.
+  util.recurseAndResolveEntries(
+      this.sourceEntries, !this.move,
+      function(result) {
+        if (this.move) {
+          // This may be moving from search results, where it fails if we
+          // move parent entries earlier than child entries. We should
+          // process the deepest entry first. Since move of each entry is
+          // done by a single moveTo() call, we don't need to care about the
+          // recursive traversal order.
+          this.entries = result.dirEntries.concat(result.fileEntries).sort(
+              function(entry1, entry2) {
+                return entry2.fullPath.length - entry1.fullPath.length;
+              });
+        } else {
+          // Copying tasks are recursively processed. So, directories must be
+          // processed earlier than their child files. Since
+          // util.recurseAndResolveEntries is already listing entries in the
+          // recursive traversal order, we just keep the ordering.
+          this.entries = result.dirEntries.concat(result.fileEntries);
+        }
+
+        this.totalBytes = result.fileBytes;
+        callback();
+      }.bind(this));
+};
+
+/**
+ * Updates copy progress status for the entry.
+ *
+ * @param {number} size Number of bytes that has been copied since last update.
+ */
+FileOperationManager.Task.prototype.updateFileCopyProgress = function(size) {
+  this.completedBytes += size;
+};
+
+/**
+ * @param {string} fromName Old name.
+ * @param {string} toName New name.
+ */
+FileOperationManager.Task.prototype.registerRename = function(
+    fromName, toName) {
+  this.renamedDirectories_.push({from: fromName + '/', to: toName + '/'});
+};
+
+/**
+ * @param {string} path A path.
+ * @return {string} Path after renames.
+ */
+FileOperationManager.Task.prototype.applyRenames = function(path) {
+  // Directories are processed in pre-order, so we will store only the first
+  // renaming point:
+  // x   -> x (1)    -- new directory created.
+  // x\y -> x (1)\y  -- no more renames inside the new directory, so
+  //                    this one will not be stored.
+  // x\y\a.txt       -- only one rename will be applied.
+  for (var index = 0; index < this.renamedDirectories_.length; ++index) {
+    var rename = this.renamedDirectories_[index];
+    if (path.indexOf(rename.from) == 0) {
+      path = rename.to + path.substr(rename.from.length);
+    }
+  }
+  return path;
+};
+
+/**
+ * Requests cancellation of this task.
+ * When the cancellation is done, it is notified via callbacks of run().
+ */
+FileOperationManager.Task.prototype.requestCancel = function() {
+  this.cancelRequested_ = true;
+  if (this.cancelCallback_) {
+    this.cancelCallback_();
+    this.cancelCallback_ = null;
+  }
+};
+
+/**
+ * Runs the task. Sub classes must implement this method.
+ *
+ * @param {function(util.EntryChangedKind, Entry)} entryChangedCallback
+ *     Callback invoked when an entry is changed.
+ * @param {function()} progressCallback Callback invoked periodically during
+ *     the operation.
+ * @param {function()} successCallback Callback run on success.
+ * @param {function(FileOperationManager.Error)} errorCallback Callback run on
+ *     error.
+ */
+FileOperationManager.Task.prototype.run = function(
+    entryChangedCallback, progressCallback, successCallback, errorCallback) {
+};
+
+/**
+ * Task to copy entries.
+ *
+ * @param {Array.<Entry>} sourceEntries Array of source entries.
+ * @param {DirectoryEntry} targetDirEntry Target directory.
+ * @constructor
+ * @extends {FileOperationManager.Task}
+ */
+FileOperationManager.CopyTask = function(sourceEntries, targetDirEntry) {
+  FileOperationManager.Task.call(this, sourceEntries, targetDirEntry);
+};
+
+/**
+ * Extends FileOperationManager.Task.
+ */
+FileOperationManager.CopyTask.prototype.__proto__ =
+    FileOperationManager.Task.prototype;
+
+/**
+ * Copies all entries to the target directory.
+ * Note: this method contains also the operation of "Move" due to historical
+ * reason.
+ *
+ * @param {function(util.EntryChangedKind, Entry)} entryChangedCallback
+ *     Callback invoked when an entry is changed.
+ * @param {function()} progressCallback Callback invoked periodically during
+ *     the copying.
+ * @param {function()} successCallback On success.
+ * @param {function(FileOperationManager.Error)} errorCallback On error.
+ * @override
+ */
+FileOperationManager.CopyTask.prototype.run = function(
+    entryChangedCallback, progressCallback, successCallback, errorCallback) {
+  // TODO(hidehiko): We should be able to share the code to iterate on entries
+  // with serviceMoveTask_().
+  if (this.entries.length == 0) {
+    successCallback();
+    return;
+  }
+
+  // TODO(hidehiko): Delete after copy is the implementation of Move.
+  // Migrate the part into MoveTask.run().
+  var deleteOriginals = function() {
+    var count = this.sourceEntries.length;
+
+    var onEntryDeleted = function(entry) {
+      entryChangedCallback(util.EntryChangedKind.DELETED, entry);
+      count--;
+      if (!count)
+        successCallback();
+    };
+
+    var onFilesystemError = function(err) {
+      errorCallback(new FileOperationManager.Error(
+          util.FileOperationErrorType.FILESYSTEM_ERROR, err));
+    };
+
+    for (var i = 0; i < this.sourceEntries.length; i++) {
+      var entry = this.sourceEntries[i];
+      util.removeFileOrDirectory(
+          entry, onEntryDeleted.bind(null, entry), onFilesystemError);
+    }
+  }.bind(this);
+
+  AsyncUtil.forEach(
+      this.sourceEntries,
+      function(callback, entry, index) {
+        if (this.cancelRequested_) {
+          errorCallback(new FileOperationManager.Error(
+              util.FileOperationErrorType.FILESYSTEM_ERROR,
+              util.createFileError(FileError.ABORT_ERR)));
+          return;
+        }
+        progressCallback();
+        this.cancelCallback_ = FileOperationManager.CopyTask.processEntry_(
+            entry, this.targetDirEntry,
+            function(type, entry) {
+              this.numCompletedEntries++;
+              entryChangedCallback(type, entry);
+            }.bind(this),
+            function(entry, size) {
+              this.processingEntry = entry;
+              this.updateFileCopyProgress(size);
+              progressCallback();
+            }.bind(this),
+            function() {
+              this.cancelCallback_ = null;
+              callback();
+            }.bind(this),
+            function(error) {
+              this.cancelCallback_ = null;
+              errorCallback(error);
+            }.bind(this));
+      },
+      function() {
+        if (this.deleteAfterCopy) {
+          deleteOriginals();
+        } else {
+          successCallback();
+        }
+      }.bind(this),
+      this);
+};
+
+/**
+ * Copies the source entry to the target directory.
+ *
+ * @param {Entry} sourceEntry An entry to be copied.
+ * @param {DirectoryEntry} destinationEntry The entry which will contain the
+ *     copied entry.
+ * @param {function(util.EntryChangedKind, Entry)} entryChangedCallback
+ *     Callback invoked when an entry is changed.
+ * @param {function(Entry, number)} progressCallback Callback invoked
+ *     periodically during the copying.
+ * @param {function()} successCallback On success.
+ * @param {function(FileOperationManager.Error)} errorCallback On error.
+ * @return {function()} Callback to cancel the current file copy operation.
+ *     When the cancel is done, errorCallback will be called. The returned
+ *     callback must not be called more than once.
+ * @private
+ */
+FileOperationManager.CopyTask.processEntry_ = function(
+    sourceEntry, destinationEntry, entryChangedCallback, progressCallback,
+    successCallback, errorCallback) {
+  var cancelRequested = false;
+  var cancelCallback = null;
+  fileOperationUtil.deduplicatePath(
+      destinationEntry, sourceEntry.name,
+      function(destinationName) {
+        if (cancelRequested) {
+          errorCallback(new FileOperationManager.Error(
+              util.FileOperationErrorType.FILESYSTEM_ERROR,
+              util.createFileError(FileError.ABORT_ERR)));
+          return;
+        }
+
+        cancelCallback = fileOperationUtil.copyRecursively(
+            sourceEntry, destinationEntry, destinationName,
+            entryChangedCallback, progressCallback,
+            function(entry) {
+              cancelCallback = null;
+              successCallback();
+            },
+            function(error) {
+              cancelCallback = null;
+              errorCallback(new FileOperationManager.Error(
+                  util.FileOperationErrorType.FILESYSTEM_ERROR, error));
+            });
+      },
+      errorCallback);
+
+  return function() {
+    cancelRequested = true;
+    if (cancelCallback) {
+      cancelCallback();
+      cancelCallback = null;
+    }
+  };
+};
+
+/**
+ * Task to move entries.
+ *
+ * @param {Array.<Entry>} sourceEntries Array of source entries.
+ * @param {DirectoryEntry} targetDirEntry Target directory.
+ * @constructor
+ * @extends {FileOperationManager.Task}
+ */
+FileOperationManager.MoveTask = function(sourceEntries, targetDirEntry) {
+  FileOperationManager.Task.call(this, sourceEntries, targetDirEntry);
+  // TODO(hidehiko): We should handle dispatching copy/move/zip more nicely.
+  this.move = true;
+};
+
+/**
+ * Extends FileOperationManager.Task.
+ */
+FileOperationManager.MoveTask.prototype.__proto__ =
+    FileOperationManager.Task.prototype;
+
+/**
+ * Moves all entries in the task.
+ *
+ * @param {function(util.EntryChangedKind, Entry)} entryChangedCallback
+ *     Callback invoked when an entry is changed.
+ * @param {function()} progressCallback Callback invoked periodically during
+ *     the moving.
+ * @param {function()} successCallback On success.
+ * @param {function(FileOperationManager.Error)} errorCallback On error.
+ * @override
+ */
+FileOperationManager.MoveTask.prototype.run = function(
+    entryChangedCallback, progressCallback, successCallback, errorCallback) {
+  if (this.entries.length == 0) {
+    successCallback();
+    return;
+  }
+
+  AsyncUtil.forEach(
+      this.entries,
+      function(callback, entry, index) {
+        if (this.cancelRequested_) {
+          errorCallback(new FileOperationManager.Error(
+              util.FileOperationErrorType.FILESYSTEM_ERROR,
+              util.createFileError(FileError.ABORT_ERR)));
+          return;
+        }
+        this.processingEntry = entry;
+        progressCallback();
+        FileOperationManager.MoveTask.processEntry_(
+            entry, this.targetDirEntry, entryChangedCallback,
+            function() {
+              this.numCompletedEntries++;
+              callback();
+            }.bind(this),
+            errorCallback);
+      },
+      function() {
+        successCallback();
+      }.bind(this),
+      this);
+};
+
+/**
+ * Moves the sourceEntry to the targetDirEntry in this task.
+ *
+ * @param {Entry} sourceEntry An entry to be moved.
+ * @param {DirectoryEntry} destinationEntry The entry of the destination
+ *     directory.
+ * @param {function(util.EntryChangedKind, Entry)} entryChangedCallback
+ *     Callback invoked when an entry is changed.
+ * @param {function()} successCallback On success.
+ * @param {function(FileOperationManager.Error)} errorCallback On error.
+ * @private
+ */
+FileOperationManager.MoveTask.processEntry_ = function(
+    sourceEntry, destinationEntry, entryChangedCallback, successCallback,
+    errorCallback) {
+  fileOperationUtil.deduplicatePath(
+      destinationEntry,
+      sourceEntry.name,
+      function(destinationName) {
+        sourceEntry.moveTo(
+            destinationEntry, destinationName,
+            function(movedEntry) {
+              entryChangedCallback(util.EntryChangedKind.CREATED, movedEntry);
+              entryChangedCallback(util.EntryChangedKind.DELETED, sourceEntry);
+              successCallback();
+            },
+            function(error) {
+              errorCallback(new FileOperationManager.Error(
+                  util.FileOperationErrorType.FILESYSTEM_ERROR, error));
+            });
+      },
+      errorCallback);
+};
+
+/**
+ * Task to create a zip archive.
+ *
+ * @param {Array.<Entry>} sourceEntries Array of source entries.
+ * @param {DirectoryEntry} targetDirEntry Target directory.
+ * @param {DirectoryEntry} zipBaseDirEntry Base directory dealt as a root
+ *     in ZIP archive.
+ * @constructor
+ * @extends {FileOperationManager.Task}
+ */
+FileOperationManager.ZipTask = function(
+    sourceEntries, targetDirEntry, zipBaseDirEntry) {
+  FileOperationManager.Task.call(this, sourceEntries, targetDirEntry);
+  this.zipBaseDirEntry = zipBaseDirEntry;
+  this.zip = true;
+};
+
+/**
+ * Extends FileOperationManager.Task.
+ */
+FileOperationManager.ZipTask.prototype.__proto__ =
+    FileOperationManager.Task.prototype;
+
+/**
+ * Runs a zip file creation task.
+ *
+ * @param {function(util.EntryChangedKind, Entry)} entryChangedCallback
+ *     Callback invoked when an entry is changed.
+ * @param {function()} progressCallback Callback invoked periodically during
+ *     the moving.
+ * @param {function()} successCallback On complete.
+ * @param {function(FileOperationManager.Error)} errorCallback On error.
+ * @override
+ */
+FileOperationManager.ZipTask.prototype.run = function(
+    entryChangedCallback, progressCallback, successCallback, errorCallback) {
+  // TODO(hidehiko): we should localize the name.
+  var destName = 'Archive';
+  if (this.sourceEntries.length == 1) {
+    var entryPath = this.sourceEntries[0].fullPath;
+    var i = entryPath.lastIndexOf('/');
+    var basename = (i < 0) ? entryPath : entryPath.substr(i + 1);
+    i = basename.lastIndexOf('.');
+    destName = ((i < 0) ? basename : basename.substr(0, i));
+  }
+
+  fileOperationUtil.deduplicatePath(
+      this.targetDirEntry, destName + '.zip',
+      function(destPath) {
+        // TODO: per-entry zip progress update with accurate byte count.
+        // For now just set completedBytes to same value as totalBytes so
+        // that the progress bar is full.
+        this.completedBytes = this.totalBytes;
+        progressCallback();
+
+        fileOperationUtil.zipSelection(
+            this.entries,
+            this.zipBaseDirEntry,
+            destPath,
+            function(entry) {
+              entryChangedCallback(util.EntryChangedKind.CREATE, entry);
+              successCallback();
+            },
+            function(error) {
+              errorCallback(new FileOperationManager.Error(
+                  util.FileOperationErrorType.FILESYSTEM_ERROR, error));
+            });
+      }.bind(this),
+      errorCallback);
+};
+
+/**
+ * Error class used to report problems with a copy operation.
+ * If the code is UNEXPECTED_SOURCE_FILE, data should be a path of the file.
+ * If the code is TARGET_EXISTS, data should be the existing Entry.
+ * If the code is FILESYSTEM_ERROR, data should be the FileError.
+ *
+ * @param {util.FileOperationErrorType} code Error type.
+ * @param {string|Entry|FileError} data Additional data.
+ * @constructor
+ */
+FileOperationManager.Error = function(code, data) {
+  this.code = code;
+  this.data = data;
+};
+
+// FileOperationManager methods.
+
+/**
+ * Called before a new method is run in the manager. Prepares the manager's
+ * state for running a new method.
+ */
+FileOperationManager.prototype.willRunNewMethod = function() {
+  // Cancel any pending close actions so the file copy manager doesn't go away.
+  if (this.unloadTimeout_)
+    clearTimeout(this.unloadTimeout_);
+  this.unloadTimeout_ = null;
+};
+
+/**
+ * @return {Object} Status object.
+ */
+FileOperationManager.prototype.getStatus = function() {
+  // TODO(hidehiko): Reorganize the structure when delete queue is merged
+  // into copy task queue.
+  var rv = {
+    totalItems: 0,
+    completedItems: 0,
+
+    totalBytes: 0,
+    completedBytes: 0,
+
+    pendingCopies: 0,
+    pendingMoves: 0,
+    pendingZips: 0,
+
+    // In case the number of the incompleted entry is exactly one.
+    filename: '',
+  };
+
+  var pendingEntry = null;
+  for (var i = 0; i < this.copyTasks_.length; i++) {
+    var task = this.copyTasks_[i];
+    rv.totalItems += task.entries.length;
+    rv.completedItems += task.numCompletedEntries;
+
+    rv.totalBytes += task.totalBytes;
+    rv.completedBytes += task.completedBytes;
+
+    var numPendingEntries = task.entries.length - task.numCompletedEntries;
+    if (task.zip) {
+      rv.pendingZips += numPendingEntries;
+    } else if (task.move || task.deleteAfterCopy) {
+      rv.pendingMoves += numPendingEntries;
+    } else {
+      rv.pendingCopies += numPendingEntries;
+    }
+
+    if (task.processingEntry)
+      pendingEntry = task.processingEntry;
+  }
+
+  if (rv.totalItems - rv.completedItems == 1 && pendingEntry)
+    rv.filename = pendingEntry.name;
+
+  return rv;
+};
+
+/**
+ * Adds an event listener for the tasks.
+ * @param {string} type The name of the event.
+ * @param {function(cr.Event)} handler The handler for the event.
+ *     This is called when the event is dispatched.
+ */
+FileOperationManager.prototype.addEventListener = function(type, handler) {
+  this.eventRouter_.addEventListener(type, handler);
+};
+
+/**
+ * Removes an event listener for the tasks.
+ * @param {string} type The name of the event.
+ * @param {function(cr.Event)} handler The handler to be removed.
+ */
+FileOperationManager.prototype.removeEventListener = function(type, handler) {
+  this.eventRouter_.removeEventListener(type, handler);
+};
+
+/**
+ * Says if there are any tasks in the queue.
+ * @return {boolean} True, if there are any tasks.
+ */
+FileOperationManager.prototype.hasQueuedTasks = function() {
+  return this.copyTasks_.length > 0 || this.deleteTasks_.length > 0;
+};
+
+/**
+ * Unloads the host page in 5 secs of idleing. Need to be called
+ * each time this.copyTasks_.length or this.deleteTasks_.length
+ * changed.
+ *
+ * @private
+ */
+FileOperationManager.prototype.maybeScheduleCloseBackgroundPage_ = function() {
+  if (!this.hasQueuedTasks()) {
+    if (this.unloadTimeout_ === null)
+      this.unloadTimeout_ = setTimeout(maybeCloseBackgroundPage, 5000);
+  } else if (this.unloadTimeout_) {
+    clearTimeout(this.unloadTimeout_);
+    this.unloadTimeout_ = null;
+  }
+};
+
+/**
+ * Completely clear out the copy queue, either because we encountered an error
+ * or completed successfully.
+ *
+ * @private
+ */
+FileOperationManager.prototype.resetQueue_ = function() {
+  for (var i = 0; i < this.cancelObservers_.length; i++)
+    this.cancelObservers_[i]();
+
+  this.copyTasks_ = [];
+  this.cancelObservers_ = [];
+  this.maybeScheduleCloseBackgroundPage_();
+};
+
+/**
+ * Request that the current copy queue be abandoned.
+ *
+ * @param {function()=} opt_callback On cancel.
+ */
+FileOperationManager.prototype.requestCancel = function(opt_callback) {
+  this.cancelRequested_ = true;
+  if (this.cancelCallback_) {
+    this.cancelCallback_();
+    this.cancelCallback_ = null;
+  }
+  if (opt_callback)
+    this.cancelObservers_.push(opt_callback);
+
+  // If there is any active task it will eventually call maybeCancel_.
+  // Otherwise call it right now.
+  if (this.copyTasks_.length == 0)
+    this.doCancel_();
+  else
+    this.copyTasks_[0].requestCancel();
+};
+
+/**
+ * Perform the bookkeeping required to cancel.
+ *
+ * @private
+ */
+FileOperationManager.prototype.doCancel_ = function() {
+  this.resetQueue_();
+  this.cancelRequested_ = false;
+  this.eventRouter_.sendProgressEvent('CANCELLED', this.getStatus());
+};
+
+/**
+ * Used internally to check if a cancel has been requested, and handle
+ * it if so.
+ *
+ * @return {boolean} If canceled.
+ * @private
+ */
+FileOperationManager.prototype.maybeCancel_ = function() {
+  if (!this.cancelRequested_)
+    return false;
+
+  this.doCancel_();
+  return true;
+};
+
+/**
+ * Kick off pasting.
+ *
+ * @param {Array.<string>} sourcePaths Path of the source files.
+ * @param {string} targetPath The destination path of the target directory.
+ * @param {boolean} isMove True if the operation is "move", otherwise (i.e.
+ *     if the operation is "copy") false.
+ */
+FileOperationManager.prototype.paste = function(
+    sourcePaths, targetPath, isMove) {
+  // Do nothing if sourcePaths is empty.
+  if (sourcePaths.length == 0)
+    return;
+
+  var errorCallback = function(error) {
+    this.eventRouter_.sendProgressEvent(
+        'ERROR',
+        this.getStatus(),
+        new FileOperationManager.Error(
+            util.FileOperationErrorType.FILESYSTEM_ERROR, error));
+  }.bind(this);
+
+  var targetEntry = null;
+  var entries = [];
+
+  // Resolve paths to entries.
+  var resolveGroup = new AsyncUtil.Group();
+  resolveGroup.add(function(callback) {
+    webkitResolveLocalFileSystemURL(
+        util.makeFilesystemUrl(targetPath),
+        function(entry) {
+          if (!entry.isDirectory) {
+            // Found a non directory entry.
+            errorCallback(util.createFileError(FileError.TYPE_MISMATCH_ERR));
+            return;
+          }
+
+          targetEntry = entry;
+          callback();
+        },
+        errorCallback);
+  });
+
+  for (var i = 0; i < sourcePaths.length; i++) {
+    resolveGroup.add(function(sourcePath, callback) {
+      webkitResolveLocalFileSystemURL(
+          util.makeFilesystemUrl(sourcePath),
+          function(entry) {
+            entries.push(entry);
+            callback();
+          },
+          errorCallback);
+    }.bind(this, sourcePaths[i]));
+  }
+
+  resolveGroup.run(function() {
+    if (isMove) {
+      // Moving to the same directory is a redundant operation.
+      entries = entries.filter(function(entry) {
+        return targetEntry.fullPath + '/' + entry.name != entry.fullPath;
+      });
+
+      // Do nothing, if we have no entries to be moved.
+      if (entries.length == 0)
+        return;
+    }
+
+    this.queueCopy_(targetEntry, entries, isMove);
+  }.bind(this));
+};
+
+/**
+ * Checks if the move operation is avaiable between the given two locations.
+ *
+ * @param {DirectoryEntry} sourceEntry An entry from the source.
+ * @param {DirectoryEntry} targetDirEntry Directory entry for the target.
+ * @return {boolean} Whether we can move from the source to the target.
+ */
+FileOperationManager.prototype.isMovable = function(sourceEntry,
+                                               targetDirEntry) {
+  return (PathUtil.isDriveBasedPath(sourceEntry.fullPath) &&
+          PathUtil.isDriveBasedPath(targetDirEntry.fullPath)) ||
+         (PathUtil.getRootPath(sourceEntry.fullPath) ==
+          PathUtil.getRootPath(targetDirEntry.fullPath));
+};
+
+/**
+ * Initiate a file copy.
+ *
+ * @param {DirectoryEntry} targetDirEntry Target directory.
+ * @param {Array.<Entry>} entries Entries to copy.
+ * @param {boolean} isMove In case of move.
+ * @return {FileOperationManager.Task} Copy task.
+ * @private
+ */
+FileOperationManager.prototype.queueCopy_ = function(
+    targetDirEntry, entries, isMove) {
+  // When copying files, null can be specified as source directory.
+  var task;
+  if (isMove) {
+    if (this.isMovable(entries[0], targetDirEntry)) {
+      task = new FileOperationManager.MoveTask(entries, targetDirEntry);
+    } else {
+      task = new FileOperationManager.CopyTask(entries, targetDirEntry);
+      task.deleteAfterCopy = true;
+    }
+  } else {
+    task = new FileOperationManager.CopyTask(entries, targetDirEntry);
+  }
+
+  task.initialize(function() {
+    this.copyTasks_.push(task);
+    this.maybeScheduleCloseBackgroundPage_();
+    if (this.copyTasks_.length == 1) {
+      // Assume this.cancelRequested_ == false.
+      // This moved us from 0 to 1 active tasks, let the servicing begin!
+      this.serviceAllTasks_();
+    } else {
+      // Force to update the progress of butter bar when there are new tasks
+      // coming while servicing current task.
+      this.eventRouter_.sendProgressEvent('PROGRESS', this.getStatus());
+    }
+  }.bind(this));
+
+  return task;
+};
+
+/**
+ * Service all pending tasks, as well as any that might appear during the
+ * copy.
+ *
+ * @private
+ */
+FileOperationManager.prototype.serviceAllTasks_ = function() {
+  var self = this;
+
+  var onTaskProgress = function() {
+    self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus());
+  };
+
+  var onEntryChanged = function(kind, entry) {
+    self.eventRouter_.sendEntryChangedEvent(kind, entry);
+  };
+
+  var onTaskError = function(err) {
+    if (self.maybeCancel_())
+      return;
+    self.eventRouter_.sendProgressEvent('ERROR', self.getStatus(), err);
+    self.resetQueue_();
+  };
+
+  var onTaskSuccess = function() {
+    if (self.maybeCancel_())
+      return;
+
+    // The task at the front of the queue is completed. Pop it from the queue.
+    self.copyTasks_.shift();
+    self.maybeScheduleCloseBackgroundPage_();
+
+    if (!self.copyTasks_.length) {
+      // All tasks have been serviced, clean up and exit.
+      self.eventRouter_.sendProgressEvent('SUCCESS', self.getStatus());
+      self.resetQueue_();
+      return;
+    }
+
+    // We want to dispatch a PROGRESS event when there are more tasks to serve
+    // right after one task finished in the queue. We treat all tasks as one
+    // big task logically, so there is only one BEGIN/SUCCESS event pair for
+    // these continuous tasks.
+    self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus());
+    self.copyTasks_[0].run(
+        onEntryChanged, onTaskProgress, onTaskSuccess, onTaskError);
+  };
+
+  // If the queue size is 1 after pushing our task, it was empty before,
+  // so we need to kick off queue processing and dispatch BEGIN event.
+  this.eventRouter_.sendProgressEvent('BEGIN', this.getStatus());
+  this.copyTasks_[0].run(
+      onEntryChanged, onTaskProgress, onTaskSuccess, onTaskError);
+};
+
+/**
+ * Timeout before files are really deleted (to allow undo).
+ */
+FileOperationManager.DELETE_TIMEOUT = 30 * 1000;
+
+/**
+ * Schedules the files deletion.
+ *
+ * @param {Array.<Entry>} entries The entries.
+ */
+FileOperationManager.prototype.deleteEntries = function(entries) {
+  var task = { entries: entries };
+  this.deleteTasks_.push(task);
+  this.maybeScheduleCloseBackgroundPage_();
+  if (this.deleteTasks_.length == 1)
+    this.serviceAllDeleteTasks_();
+};
+
+/**
+ * Service all pending delete tasks, as well as any that might appear during the
+ * deletion.
+ *
+ * Must not be called if there is an in-flight delete task.
+ *
+ * @private
+ */
+FileOperationManager.prototype.serviceAllDeleteTasks_ = function() {
+  // Returns the urls of the given task's entries.
+  var getTaskUrls = function(task) {
+    return task.entries.map(function(entry) {
+      return util.makeFilesystemUrl(entry.fullPath);
+    });
+  };
+
+  var onTaskSuccess = function() {
+    var urls = getTaskUrls(this.deleteTasks_.shift());
+    if (!this.deleteTasks_.length) {
+      // All tasks have been serviced, clean up and exit.
+      this.eventRouter_.sendDeleteEvent('SUCCESS', urls);
+      this.maybeScheduleCloseBackgroundPage_();
+      return;
+    }
+
+    // We want to dispatch a PROGRESS event when there are more tasks to serve
+    // right after one task finished in the queue. We treat all tasks as one
+    // big task logically, so there is only one BEGIN/SUCCESS event pair for
+    // these continuous tasks.
+    this.eventRouter_.sendDeleteEvent('PROGRESS', urls);
+
+    this.serviceDeleteTask_(this.deleteTasks_[0], onTaskSuccess, onTaskFailure);
+  }.bind(this);
+
+  var onTaskFailure = function(error) {
+    var urls = getTaskUrls(this.deleteTasks_[0]);
+    this.deleteTasks_ = [];
+    this.eventRouter_.sendDeleteEvent('ERROR', urls);
+    this.maybeScheduleCloseBackgroundPage_();
+  }.bind(this);
+
+  // If the queue size is 1 after pushing our task, it was empty before,
+  // so we need to kick off queue processing and dispatch BEGIN event.
+  this.eventRouter_.sendDeleteEvent('BEGIN', getTaskUrls(this.deleteTasks_[0]));
+  this.serviceDeleteTask_(this.deleteTasks_[0], onTaskSuccess, onTaskFailure);
+};
+
+/**
+ * Performs the deletion.
+ *
+ * @param {Object} task The delete task (see deleteEntries function).
+ * @param {function()} successCallback Callback run on success.
+ * @param {function(FileOperationManager.Error)} errorCallback Callback run on
+ *     error.
+ * @private
+ */
+FileOperationManager.prototype.serviceDeleteTask_ = function(
+    task, successCallback, errorCallback) {
+  var downcount = task.entries.length;
+  if (downcount == 0) {
+    successCallback();
+    return;
+  }
+
+  var filesystemError = null;
+  var onComplete = function() {
+    if (--downcount > 0)
+      return;
+
+    // All remove operations are processed. Run callback.
+    if (filesystemError) {
+      errorCallback(new FileOperationManager.Error(
+          util.FileOperationErrorType.FILESYSTEM_ERROR, filesystemError));
+    } else {
+      successCallback();
+    }
+  };
+
+  for (var i = 0; i < task.entries.length; i++) {
+    var entry = task.entries[i];
+    util.removeFileOrDirectory(
+        entry,
+        function(currentEntry) {
+          this.eventRouter_.sendEntryChangedEvent(
+              util.EntryChangedKind.DELETED, currentEntry);
+          onComplete();
+        }.bind(this, entry),
+        function(error) {
+          if (!filesystemError)
+            filesystemError = error;
+          onComplete();
+        });
+  }
+};
+
+/**
+ * Creates a zip file for the selection of files.
+ *
+ * @param {Entry} dirEntry The directory containing the selection.
+ * @param {Array.<Entry>} selectionEntries The selected entries.
+ */
+FileOperationManager.prototype.zipSelection = function(
+    dirEntry, selectionEntries) {
+  var self = this;
+  var zipTask = new FileOperationManager.ZipTask(
+      selectionEntries, dirEntry, dirEntry);
+  zipTask.zip = true;
+  zipTask.initialize(function() {
+    self.copyTasks_.push(zipTask);
+    if (self.copyTasks_.length == 1) {
+      // Assume self.cancelRequested_ == false.
+      // This moved us from 0 to 1 active tasks, let the servicing begin!
+      self.serviceAllTasks_();
+    } else {
+      // Force to update the progress of butter bar when there are new tasks
+      // coming while servicing current task.
+      self.eventRouter_.sendProgressEvent('PROGRESS', self.getStatus());
+    }
+  });
+};
diff --git a/chrome/browser/resources/file_manager/js/file_operation_manager_wrapper.js b/chrome/browser/resources/file_manager/js/file_operation_manager_wrapper.js
new file mode 100644
index 0000000..398a74a
--- /dev/null
+++ b/chrome/browser/resources/file_manager/js/file_operation_manager_wrapper.js
@@ -0,0 +1,90 @@
+// 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.
+
+'use strict';
+
+/**
+ * While FileOperationManager is run in the background page, this class is
+ * used to communicate with it.
+ * @constructor
+ */
+function FileOperationManagerWrapper() {
+  this.fileOperationManager_ = null;
+
+  /**
+   * In the constructor, it tries to start loading the background page, and
+   * its FileOperationManager instance, asynchronously. Even during the
+   * initialization, there may be some method invocation. In such a case,
+   * FileOperationManagerWrapper keeps it as a pending task in pendingTasks_,
+   * and once FileOperationManager is obtained, run the pending tasks in the
+   * order.
+   *
+   * @private {Array.<function(FileOperationManager)>}
+   */
+  this.pendingTasks_ = [];
+
+  chrome.runtime.getBackgroundPage(function(backgroundPage) {
+    this.fileOperationManager_ =
+        backgroundPage.FileOperationManager.getInstance();
+
+    // Run pending tasks.
+    for (var i = 0; i < this.pendingTasks_.length; i++) {
+      this.pendingTasks_[i](this.fileOperationManager_);
+    }
+    this.pendingTasks_ = [];
+  }.bind(this));
+}
+
+/**
+ * Create a new instance or get existing instance of FCMW.
+ * @return {FileOperationManagerWrapper}  FileOperationManagerWrapper instance.
+ */
+FileOperationManagerWrapper.getInstance = function() {
+  if (!FileOperationManagerWrapper.instance_)
+    FileOperationManagerWrapper.instance_ = new FileOperationManagerWrapper();
+
+  return FileOperationManagerWrapper.instance_;
+};
+
+/**
+ * @return {boolean} True if there is a running task.
+ */
+FileOperationManagerWrapper.prototype.isRunning = function() {
+  // Note: until the background page is loaded, this method returns false
+  // even if there is a running task in FileOperationManager. (Though the
+  // period should be very short).
+  // TODO(hidehiko): Fix the race condition. It is necessary to change the
+  // FileOperationManager implementation as well as the clients (FileManager/
+  // ButterBar) implementation.
+  return this.fileOperationManager_ &&
+         this.fileOperationManager_.hasQueuedTasks();
+};
+
+/**
+ * Decorates a FileOperationManager method, so it will be executed after
+ * initializing the FileOperationManager instance in background page.
+ * @param {string} method The method name.
+ */
+FileOperationManagerWrapper.decorateAsyncMethod = function(method) {
+  FileOperationManagerWrapper.prototype[method] = function() {
+    var args = Array.prototype.slice.call(arguments);
+    var operation = function(fileOperationManager) {
+      fileOperationManager.willRunNewMethod();
+      fileOperationManager[method].apply(fileOperationManager, args);
+    };
+    if (this.fileOperationManager_)
+      operation(this.fileOperationManager_);
+    else
+      this.pendingTasks_.push(operation);
+  };
+};
+
+FileOperationManagerWrapper.decorateAsyncMethod('requestCancel');
+FileOperationManagerWrapper.decorateAsyncMethod('paste');
+FileOperationManagerWrapper.decorateAsyncMethod('deleteEntries');
+FileOperationManagerWrapper.decorateAsyncMethod('forceDeleteTask');
+FileOperationManagerWrapper.decorateAsyncMethod('cancelDeleteTask');
+FileOperationManagerWrapper.decorateAsyncMethod('zipSelection');
+FileOperationManagerWrapper.decorateAsyncMethod('addEventListener');
+FileOperationManagerWrapper.decorateAsyncMethod('removeEventListener');
diff --git a/chrome/browser/resources/file_manager/js/file_table.js b/chrome/browser/resources/file_manager/js/file_table.js
index c52818b..b5a8bca 100644
--- a/chrome/browser/resources/file_manager/js/file_table.js
+++ b/chrome/browser/resources/file_manager/js/file_table.js
@@ -657,9 +657,9 @@
       this.updateSize_(item, entry, props);
     });
   } else if (type == 'drive') {
-    forEachCell('.table-row-cell > .offline',
+    // The cell name does not matter as the entire list item is needed.
+    forEachCell('.table-row-cell > .date',
                 function(item, entry, props, listItem) {
-      this.updateOffline_(item, props);
       filelist.updateListItemDriveProps(listItem, props);
     });
   }
@@ -934,6 +934,8 @@
 filelist.decorateListItem = function(li, entry, metadataCache) {
   li.classList.add(entry.isDirectory ? 'directory' : 'file');
   if (FileType.isOnDrive(entry)) {
+    // The metadata may not yet be ready. In that case, the list item will be
+    // updated when the metadata is ready via updateListItemsMetadata.
     var driveProps = metadataCache.getCached(entry, 'drive');
     if (driveProps)
       filelist.updateListItemDriveProps(li, driveProps);
@@ -1001,24 +1003,10 @@
     // crbug.com/246611.
   }
 
-  if (driveProps.driveApps.length > 0) {
+  if (driveProps.customIconUrl) {
     var iconDiv = li.querySelector('.detail-icon');
     if (!iconDiv)
       return;
-    // Find the default app for this file.  If there is none, then
-    // leave it as the base icon for the file type.
-    var url;
-    for (var i = 0; i < driveProps.driveApps.length; ++i) {
-      var app = driveProps.driveApps[i];
-      if (app && app.docIcon && app.isPrimary) {
-        url = app.docIcon;
-        break;
-      }
-    }
-    if (url) {
-      iconDiv.style.backgroundImage = 'url(' + url + ')';
-    } else {
-      iconDiv.style.backgroundImage = null;
-    }
+    iconDiv.style.backgroundImage = 'url(' + driveProps.customIconUrl + ')';
   }
 };
diff --git a/chrome/browser/resources/file_manager/js/file_tasks.js b/chrome/browser/resources/file_manager/js/file_tasks.js
index 8e908e6..b282393 100644
--- a/chrome/browser/resources/file_manager/js/file_tasks.js
+++ b/chrome/browser/resources/file_manager/js/file_tasks.js
@@ -260,59 +260,45 @@
 /**
  * Executes default task.
  *
+ * @param {function(boolean, Array.<string>)=} opt_callback Called wheh the
+ *     default task is executed, or the error is occured.
  * @private
  */
-FileTasks.prototype.executeDefault_ = function() {
+FileTasks.prototype.executeDefault_ = function(opt_callback) {
   var urls = this.urls_;
   FileTasks.recordViewingFileTypeUMA_(urls);
-  this.executeDefaultInternal_(urls);
+  this.executeDefaultInternal_(urls, opt_callback);
 };
 
 /**
  * Executes default task.
  *
  * @param {Array.<string>} urls Urls to execute.
+ * @param {function(boolean, Array.<string>)=} opt_callback Called wheh the
+ *     default task is executed, or the error is occured.
  * @private
  */
-FileTasks.prototype.executeDefaultInternal_ = function(urls) {
+FileTasks.prototype.executeDefaultInternal_ = function(urls, opt_callback) {
+  var callback = opt_callback || function(arg1, arg2) {};
+
   if (this.defaultTask_ != null) {
     this.executeInternal_(this.defaultTask_.taskId, urls);
+    callback(true, urls);
     return;
   }
 
   // We don't have tasks, so try to show a file in a browser tab.
   // We only do that for single selection to avoid confusion.
-  if (urls.length == 1) {
-    var callback = function(success) {
-      if (!success) {
-        var filename = decodeURIComponent(urls[0]);
-        if (filename.indexOf('/') != -1)
-          filename = filename.substr(filename.lastIndexOf('/') + 1);
-        var extension = filename.lastIndexOf('.') != -1 ?
-            filename.substr(filename.lastIndexOf('.') + 1) : '';
+  if (urls.length != 1)
+    return;
 
-        this.fileManager_.metadataCache_.get(urls, 'drive', function(props) {
-          var mimeType;
-          if (props && props[0] && props[0].contentMimeType)
-            mimeType = props[0].contentMimeType;
+  var onViewFiles = function(success) {
+    callback(success, urls);
+  }.bind(this);
 
-          var messageString = extension == 'exe' ? 'NO_ACTION_FOR_EXECUTABLE' :
-                                                   'NO_ACTION_FOR_FILE';
-          var webStoreUrl = FileTasks.createWebStoreLink(extension, mimeType);
-          var text = loadTimeData.getStringF(messageString,
-                                             webStoreUrl,
-                                             FileTasks.NO_ACTION_FOR_FILE_URL);
-          this.fileManager_.alert.showHtml(filename, text, function() {});
-        }.bind(this));
-      }
-    }.bind(this);
-
-    this.checkAvailability_(function() {
-      util.viewFilesInBrowser(urls, callback);
-    }.bind(this));
-  }
-
-  // Do nothing for multiple urls.
+  this.checkAvailability_(function() {
+    util.viewFilesInBrowser(urls, onViewFiles);
+  }.bind(this));
 };
 
 /**
@@ -570,7 +556,9 @@
     // is writable or not.
     var readonly = fm.isOnReadonlyDirectory();
     var currentDir = fm.directoryModel_.getCurrentDirEntry();
-    var downloadsDir = fm.directoryModel_.getRootsList().item(0);
+    var downloadsVolume =
+        fm.volumeManager_.getVolumeInfo(RootDirectory.DOWNLOADS);
+    var downloadsDir = downloadsVolume && downloadsVolume.root;
     var readonlyDirName = null;
     if (readonly) {
       readonlyDirName = fm.isOnDrive() ?
diff --git a/chrome/browser/resources/file_manager/js/file_transfer_controller.js b/chrome/browser/resources/file_manager/js/file_transfer_controller.js
index 46a1b9d..f889e39 100644
--- a/chrome/browser/resources/file_manager/js/file_transfer_controller.js
+++ b/chrome/browser/resources/file_manager/js/file_transfer_controller.js
@@ -14,17 +14,18 @@
 
 /**
  * @param {HTMLDocument} doc Owning document.
- * @param {FileCopyManager} copyManager Copy manager instance.
+ * @param {FileOperationManager} fileOperationManager File operation manager
+ *     instance.
  * @param {MetadataCache} metadataCache Metadata cache service.
  * @param {DirectoryModel} directoryModel Directory model instance.
  * @constructor
  */
 function FileTransferController(doc,
-                                copyManager,
+                                fileOperationManager,
                                 metadataCache,
                                 directoryModel) {
   this.document_ = doc;
-  this.copyManager_ = copyManager;
+  this.fileOperationManager_ = fileOperationManager;
   this.metadataCache_ = metadataCache;
   this.directoryModel_ = directoryModel;
 
@@ -206,7 +207,7 @@
         (effectAllowed == 'copyMove' && opt_effect == 'move');
 
     // Start the pasting operation.
-    this.copyManager_.paste(sourcePaths, destinationPath, toMove);
+    this.fileOperationManager_.paste(sourcePaths, destinationPath, toMove);
     return toMove ? 'move' : 'copy';
   },
 
@@ -538,8 +539,9 @@
     if (!this.isDocumentWideEvent_())
       return;
 
-    // queryCommandEnabled returns true if event.returnValue is false.
-    event.returnValue = !this.canCopyOrDrag_();
+    // queryCommandEnabled returns true if event.defaultPrevented is true.
+    if (this.canCopyOrDrag_())
+      event.preventDefault();
   },
 
   /**
@@ -574,8 +576,9 @@
   onBeforeCut_: function(event) {
     if (!this.isDocumentWideEvent_())
       return;
-    // queryCommandEnabled returns true if event.returnValue is false.
-    event.returnValue = !this.canCutOrDrag_();
+    // queryCommandEnabled returns true if event.defaultPrevented is true.
+    if (this.canCutOrDrag_())
+      event.preventDefault();
   },
 
   /**
@@ -616,9 +619,11 @@
   onBeforePaste_: function(event) {
     if (!this.isDocumentWideEvent_())
       return;
-    // queryCommandEnabled returns true if event.returnValue is false.
-    event.returnValue = !this.canPasteOrDrop_(
-        event.clipboardData, this.currentDirectoryContentPath);
+    // queryCommandEnabled returns true if event.defaultPrevented is true.
+    if (this.canPasteOrDrop_(event.clipboardData,
+                             this.currentDirectoryContentPath)) {
+      event.preventDefault();
+    }
   },
 
   /**
diff --git a/chrome/browser/resources/file_manager/js/main_scripts.js b/chrome/browser/resources/file_manager/js/main_scripts.js
index bfa7fbd..078ab8b 100644
--- a/chrome/browser/resources/file_manager/js/main_scripts.js
+++ b/chrome/browser/resources/file_manager/js/main_scripts.js
@@ -69,19 +69,21 @@
 //<include src="combobutton.js"/>
 //<include src="commandbutton.js"/>
 //
+//<include src="app_installer.js"/>
 //<include src="async_util.js"/>
 //<include src="path_util.js"/>
 //<include src="util.js"/>
 //<include src="action_choice_util.js"/>
 //<include src="breadcrumbs_controller.js"/>
 //<include src="butter_bar.js"/>
+//<include src="cws_container_client.js"/>
 //<include src="directory_contents.js"/>
 //<include src="directory_model.js"/>
 //<include src="directory_tree.js"/>
 //<include src="drag_selector.js"/>
 //<include src="drive_banners.js" />
 //<include src="error_dialog.js"/>
-//<include src="file_copy_manager_wrapper.js"/>
+//<include src="file_operation_manager_wrapper.js"/>
 //<include src="file_grid.js"/>
 //<include src="file_manager.js"/>
 //<include src="file_manager_pyauto.js"/>
@@ -96,6 +98,7 @@
 //<include src="scrollbar.js"/>
 //<include src="share_client.js"/>
 //<include src="share_dialog.js"/>
+//<include src="suggest_apps_dialog.js"/>
 //<include src="tree.css.js"/>
 //<include src="volume_manager.js"/>
 //<include src="media/media_util.js"/>
@@ -112,7 +115,7 @@
 
 // Exports
 window.util = util;
-window.FileCopyManagerWrapper = FileCopyManagerWrapper;
+window.FileOperationManagerWrapper = FileOperationManagerWrapper;
 
 window.unload = unload;
 
diff --git a/chrome/browser/resources/file_manager/js/media/media_util.js b/chrome/browser/resources/file_manager/js/media/media_util.js
index e4e0e59..eb64b64 100644
--- a/chrome/browser/resources/file_manager/js/media/media_util.js
+++ b/chrome/browser/resources/file_manager/js/media/media_util.js
@@ -35,14 +35,8 @@
 
   this.fallbackUrl_ = null;
   this.thumbnailUrl_ = null;
-  if (opt_metadata.drive) {
-    var apps = opt_metadata.drive.driveApps;
-    for (var i = 0; i < apps.length; ++i) {
-      if (apps[i].docIcon && apps[i].isPrimary) {
-        this.fallbackUrl_ = apps[i].docIcon;
-        break;
-      }
-    }
+  if (opt_metadata.drive && opt_metadata.drive.customIconUrl) {
+    this.fallbackUrl_ = opt_metadata.drive.customIconUrl;
   }
 
   if (opt_metadata.thumbnail && opt_metadata.thumbnail.url &&
diff --git a/chrome/browser/resources/file_manager/js/media/mediaplayer_scripts.js b/chrome/browser/resources/file_manager/js/media/mediaplayer_scripts.js
index 6aae530..c6de625 100644
--- a/chrome/browser/resources/file_manager/js/media/mediaplayer_scripts.js
+++ b/chrome/browser/resources/file_manager/js/media/mediaplayer_scripts.js
@@ -17,6 +17,7 @@
 
 //<include src="../async_util.js"/>
 //<include src="../util.js"/>
+//<include src="../path_util.js"/>
 //<include src="../file_type.js"/>
 //<include src="../volume_manager.js">
 //<include src="../metadata/metadata_cache.js"/>
diff --git a/chrome/browser/resources/file_manager/js/media/video_player_scripts.js b/chrome/browser/resources/file_manager/js/media/video_player_scripts.js
index 2166b77..14e571b 100644
--- a/chrome/browser/resources/file_manager/js/media/video_player_scripts.js
+++ b/chrome/browser/resources/file_manager/js/media/video_player_scripts.js
@@ -17,6 +17,7 @@
 
 //<include src="../async_util.js"/>
 //<include src="../util.js"/>
+//<include src="../path_util.js"/>
 //<include src="../file_type.js"/>
 //<include src="../volume_manager.js">
 //<include src="../metadata/metadata_cache.js"/>
diff --git a/chrome/browser/resources/file_manager/js/metadata/metadata_cache.js b/chrome/browser/resources/file_manager/js/metadata/metadata_cache.js
index e64fba3..08cce4f 100644
--- a/chrome/browser/resources/file_manager/js/metadata/metadata_cache.js
+++ b/chrome/browser/resources/file_manager/js/metadata/metadata_cache.js
@@ -689,7 +689,8 @@
 /**
  * Provider of drive metadata.
  * This provider returns the following objects:
- *     drive: { pinned, hosted, present, dirty, editUrl, contentUrl, driveApps }
+ *     drive: { pinned, hosted, present, dirty, editUrl, contentUrl,
+ *              customIconUrl }
  *     thumbnail: { url, transform }
  *     streaming: { }
  * @constructor
@@ -818,7 +819,7 @@
     dirty: data.isDirty,
     availableOffline: DriveProvider.isAvailableOffline(data, url),
     availableWhenMetered: DriveProvider.isAvailableWhenMetered(data),
-    driveApps: data.driveApps || [],
+    customIconUrl: data.customIconUrl || '',
     contentMimeType: data.contentMimeType || '',
     sharedWithMe: data.sharedWithMe
   };
diff --git a/chrome/browser/resources/file_manager/js/navigation_list.js b/chrome/browser/resources/file_manager/js/navigation_list.js
index fbd42c5..8fa40e9 100644
--- a/chrome/browser/resources/file_manager/js/navigation_list.js
+++ b/chrome/browser/resources/file_manager/js/navigation_list.js
@@ -116,56 +116,93 @@
  * A navigation list model. This model combines the 2 lists.
  * @param {FileSystem} filesystem FileSystem.
  * @param {cr.ui.ArrayDataModel} volumesList The first list of the model.
- * @param {cr.ui.ArrayDataModel} pinnedList The second list of the model.
+ * @param {cr.ui.ArrayDataModel} shortcutList The second list of the model.
  * @constructor
  * @extends {cr.EventTarget}
  */
-function NavigationListModel(filesystem, volumesList, pinnedList) {
+function NavigationListModel(filesystem, volumesList, shortcutList) {
   this.volumesListModel_ = volumesList;
-  this.pinnedListModel_ = pinnedList;
+  this.shortcutListModel_ = shortcutList;
+
+  // On default, shortcut list is disabled. It may be enabled on initializetion.
+  this.showShortcuts_ = false;
+
+  var entryToModelItem = function(entry) {
+    return NavigationModelItem.createWithEntry(entry);
+  };
+  var pathToModelItem = function(path) {
+    return NavigationModelItem.createWithPath(filesystem, path);
+  };
 
   /**
-   * (Re)generates this.volumesList_ and this.pinnedList_ from the models.
-   * This should be called whenever the models are updated.
+   * Type of updated list.
+   * @enum {number}
+   * @const
    */
-  var generateLists = function() {
-    this.volumesList_ =
-        this.volumesListModel_.slice(0).map(function(entry) {
-          return NavigationModelItem.createWithEntry(entry);
-        });
-    this.pinnedList_ =
-        this.pinnedListModel_.slice(0).map(function(path) {
-            return NavigationModelItem.createWithPath(filesystem, path);
-        });
-  }.bind(this);
+  var ListType = {
+    VOLUME_LIST: 1,
+    SHORTCUT_LIST: 2
+  };
+  Object.freeze(ListType);
 
-  generateLists();
+  // Generates this.volumesList_ and this.shortcutList_ from the models.
+  this.volumesList_ = this.volumesListModel_.slice(0).map(entryToModelItem);
+  this.shortcutList_ = this.shortcutListModel_.slice(0).map(pathToModelItem);
 
   // Generates a combined 'permuted' event from an event of either list.
-  var permutedHandler = function(listNum, e) {
-    generateLists();
-
+  var permutedHandler = function(listType, event) {
     var permutedEvent = new Event('permuted');
     var newPermutation = [];
     var newLength;
-    if (listNum == 1) {
-      newLength = e.newLength + this.pinnedList_.length;
-      for (var i = 0; i < e.permutation.length; i++) {
-        newPermutation[i] = e.permutation[i];
+    if (listType == ListType.VOLUME_LIST) {
+      // Creates new permutation array.
+      newLength = event.newLength;
+      for (var i = 0; i < event.permutation.length; i++) {
+        newPermutation[i] = event.permutation[i];
       }
-      for (var i = 0; i < this.pinnedList_.length; i++) {
-        newPermutation[i + e.permutation.length] = i + e.newLength;
+      if (this.showShortcuts_) {
+        newLength += this.shortcutList_.length;
+        for (var i = 0; i < this.shortcutList_.length; i++) {
+          newPermutation[i + event.permutation.length] = i + event.newLength;
+        }
       }
+
+      // Updates the list.
+      var newList = [];
+      for (var i = 0; i < event.permutation.length; i++) {
+        newList[event.permutation[i]] = this.volumesList_[i];
+      }
+      for (var i = 0; i < event.newLength; i++) {
+        if (!newList[i])
+          newList[i] = entryToModelItem(this.volumesListModel_.item(i));
+      }
+      this.volumesList_ = newList;
     } else {
+      // Creates new permutation array.
       var volumesLen = this.volumesList_.length;
-      newLength = e.newLength + volumesLen;
+      newLength = volumesLen;
       for (var i = 0; i < volumesLen; i++) {
         newPermutation[i] = i;
       }
-      for (var i = 0; i < e.permutation.length; i++) {
-        newPermutation[i + volumesLen] =
-            (e.permutation[i] !== -1) ? (e.permutation[i] + volumesLen) : -1;
+      if (this.showShortcuts_) {
+        newLength += event.newLength;
+        for (var i = 0; i < event.permutation.length; i++) {
+          newPermutation[i + volumesLen] = (event.permutation[i] !== -1) ?
+                                           (event.permutation[i] + volumesLen) :
+                                           -1;
+        }
       }
+
+      // Updates the list.
+      var newList = [];
+      for (var i = 0; i < event.permutation.length; i++) {
+        newList[event.permutation[i]] = this.shortcutList_[i];
+      }
+      for (var i = 0; i < event.newLength; i++) {
+        if (!newList[i])
+          newList[i] = pathToModelItem(this.shortcutListModel_.item(i));
+      }
+      this.shortcutList_ = newList;
     }
 
     permutedEvent.newLength = newLength;
@@ -173,23 +210,32 @@
     this.dispatchEvent(permutedEvent);
   };
   this.volumesListModel_.addEventListener(
-      'permuted', permutedHandler.bind(this, 1));
-  this.pinnedListModel_.addEventListener(
-      'permuted', permutedHandler.bind(this, 2));
+      'permuted', permutedHandler.bind(this, ListType.VOLUME_LIST));
+  this.shortcutListModel_.addEventListener(
+      'permuted', permutedHandler.bind(this, ListType.SHORTCUT_LIST));
 
   // Generates a combined 'change' event from an event of either list.
-  var changeHandler = function(listNum, e) {
-    generateLists();
+  var changeHandler = function(listType, event) {
+    var i = event.index;
+
+    // Updates the list.
+    if (listType == ListType.VOLUME_LIST)
+      this.volumesList_[i] = entryToModelItem(this.volumesListModel_.item(i));
+    else
+      this.shortcutList_[i] = pathToModelItem(this.shortcutListModel_.item(i));
+
+    if (this.showShortcuts_ && listType == ListType.SHORTCUT_LIST)
+      return;
 
     var changeEvent = new Event('change');
     changeEvent.index =
-        (listNum == 1) ? e.index : (e.index + this.volumesList_.length);
+        (listType == ListType.VOLUME_LIST) ? i : (i + this.volumesList_.length);
     this.dispatchEvent(changeEvent);
   };
   this.volumesListModel_.addEventListener(
-      'change', changeHandler.bind(this, 1));
-  this.pinnedListModel_.addEventListener(
-      'change', changeHandler.bind(this, 2));
+      'change', changeHandler.bind(this, ListType.VOLUME_LIST));
+  this.shortcutListModel_.addEventListener(
+      'change', changeHandler.bind(this, ListType.SHORTCUT_LIST));
 
   // 'splice' and 'sorted' events are not implemented, since they are not used
   // in list.js.
@@ -201,7 +247,7 @@
 NavigationListModel.prototype = {
   __proto__: cr.EventTarget.prototype,
   get length() { return this.length_(); },
-  get folderShortcutList() { return this.pinnedList_; }
+  get folderShortcutList() { return this.shortcutList_; }
 };
 
 /**
@@ -213,8 +259,45 @@
   var offset = this.volumesList_.length;
   if (index < offset)
     return this.volumesList_[index];
-  else
-    return this.pinnedList_[index - offset];
+  if (this.showShortcuts_)
+    return this.shortcutList_[index - offset];
+  return null;
+};
+
+/**
+ * Show/hide the shortcut list.
+ * @param {boolean} show True to show, false otherwise.
+ */
+NavigationListModel.prototype.showShortcuts = function(show) {
+  if (this.showShortcuts_ == show)
+    return;
+
+  this.showShortcuts_ = show;
+
+  if (show) {
+    var permutedEvent = new Event('permuted');
+    var permutation = [];
+    for (var i = 0; i < this.volumesList_.length; i++) {
+      permutation[i] = i;
+    }
+    permutedEvent.newLength =
+        this.volumesList_.length + this.shortcutList_.length;
+    permutedEvent.permutation = permutation;
+    this.dispatchEvent(permutedEvent);
+  } else {
+    var permutedEvent = new Event('permuted');
+    var permutation = [];
+    for (var i = 0; i < this.volumesList_.length; i++) {
+      permutation[i] = i;
+    }
+    var offset = this.volumesList_.length;
+    for (var i = 0; i < this.shortcutList_.length; i++) {
+      permutation[i + offset] = -1;
+    }
+    permutedEvent.newLength = this.volumesList_.length;
+    permutedEvent.permutation = permutation;
+    this.dispatchEvent(permutedEvent);
+  }
 };
 
 /**
@@ -223,7 +306,10 @@
  * @private
  */
 NavigationListModel.prototype.length_ = function() {
-  return this.volumesList_.length + this.pinnedList_.length;
+  var length = this.volumesList_.length;
+  if (this.showShortcuts_)
+    length += this.shortcutList_.length;
+  return length;
 };
 
 /**
@@ -290,9 +376,6 @@
 
   this.modelItem_ = modelItem;
 
-  this.setAttribute('item-type',
-                    modelItem.isShortcut() ? 'shortcut' : 'volume');
-
   var rootType = PathUtil.getRootType(modelItem.path);
   this.iconDiv_.setAttribute('volume-type-icon', rootType);
   if (opt_deviceType) {
@@ -305,9 +388,9 @@
     this.eject_ = cr.doc.createElement('div');
     // Block other mouse handlers.
     this.eject_.addEventListener(
-        'mouseup', function(e) { e.stopPropagation() });
+        'mouseup', function(event) { event.stopPropagation() });
     this.eject_.addEventListener(
-        'mousedown', function(e) { e.stopPropagation() });
+        'mousedown', function(event) { event.stopPropagation() });
 
     this.eject_.className = 'root-eject';
     this.eject_.addEventListener('click', function(event) {
@@ -320,6 +403,14 @@
 };
 
 /**
+ * Add/remove fade-in animation.
+ * @param {boolean} animation True if adding animation, false if removing.
+ */
+NavigationListItem.prototype.setFadeinAnimation = function(animation) {
+  this.classList.toggle('fadein', animation);
+};
+
+/**
  * Associate a context menu with this item.
  * @param {cr.ui.Menu} menu Menu this item.
  */
@@ -422,6 +513,23 @@
 };
 
 /**
+ * This overrides cr.ui.List.measureItem().
+ * In the method, a temporary element is added/removed from the list, and we
+ * need to omit animations for such temporary items.
+ *
+ * @param {ListItem=} opt_item The list item to be measured.
+ * @return {{height: number, marginTop: number, marginBottom:number,
+ *     width: number, marginLeft: number, marginRight:number}} Size.
+ * @override
+ */
+NavigationList.prototype.measureItem = function(opt_item) {
+  this.measuringTemporaryItemNow_ = true;
+  var result = cr.ui.List.prototype.measureItem.call(this, opt_item);
+  this.measuringTemporaryItemNow_ = false;
+  return result;
+};
+
+/**
  * This overrides Node.removeChild().
  * Instead of just removing the given child, this method adds a fade-out
  * animation and removes the child after animation completes.
@@ -432,14 +540,14 @@
 NavigationList.prototype.removeChild = function(item) {
   // TODO(yoshiki): Animation is temporary disabled for volumes. Add animation
   // back. crbug.com/276132.
-  if (!item.modelItem.isShortcut()) {
+  if (!item.modelItem.isShortcut() || this.measuringTemporaryItemNow_) {
     Node.prototype.removeChild.call(this, item);
     return;
   }
 
-  var removeElement = function(e) {
+  var removeElement = function(event) {
     // Must keep the animation name 'fadeOut' in sync with the css.
-    if (e.animationName == 'fadeOut')
+    if (event.animationName == 'fadeOut')
       // Checks if the element is still alive on the DOM tree.
       if (item.parentElement && item.parentElement == this)
         Node.prototype.removeChild.call(this, item);
@@ -465,6 +573,11 @@
       this.volumeManager_.getVolumeInfo(modelItem.path);
   item.setModelItem(modelItem, volumeInfo && volumeInfo.deviceType);
 
+  // TODO(yoshiki): Animation is temporary disabled for volumes. Add animation
+  // back. crbug.com/276132.
+  item.setFadeinAnimation(modelItem.isShortcut() &&
+                          !this.measuringTemporaryItemNow_);
+
   var handleClick = function() {
     if (item.selected &&
         modelItem.path !== this.directoryModel_.getCurrentDirPath()) {
@@ -499,10 +612,8 @@
  */
 NavigationList.prototype.changeDirectory_ = function(path) {
   var onErrorCallback = function() {
-    var e = new Event('shortcut-target-not-found');
-    e.path = path;
-    e.label = PathUtil.getFolderLabel(path);
-    this.dispatchEvent(e);
+    // TODO(yoshiki): Remove this if the teporary patch is merged to the M30.
+    // crbug.com/270436
   }.bind(this);
 
   this.directoryModel_.changeDirectory(path, onErrorCallback);
@@ -600,7 +711,7 @@
   if (!path)
     return;
 
-  // (1) Select the nearest parent directory (including the pinned directories).
+  // (1) Select the nearest parent directory (including the shortcut folders).
   var bestMatchIndex = -1;
   var bestMatchSubStringLen = 0;
   for (var i = 0; i < this.dataModel.length; i++) {
diff --git a/chrome/browser/resources/file_manager/js/path_util.js b/chrome/browser/resources/file_manager/js/path_util.js
index 1517377..76ae5cf 100644
--- a/chrome/browser/resources/file_manager/js/path_util.js
+++ b/chrome/browser/resources/file_manager/js/path_util.js
@@ -351,3 +351,17 @@
          !PathUtil.isRootPath(directoryPath) &&
          PathUtil.isDriveBasedPath(directoryPath);
 };
+
+/**
+ * Returns the extension of the filename
+ *
+ * @param {string} filename Filename to be extracted.
+ * @return {string} Extension of the given filename.
+ */
+PathUtil.extractExtension = function(filename) {
+  if (filename.indexOf('/') != -1)
+    filename = filename.substr(filename.lastIndexOf('/') + 1);
+  var extension = filename.lastIndexOf('.') != -1 ?
+      filename.substr(filename.lastIndexOf('.') + 1) : '';
+  return extension;
+};
diff --git a/chrome/browser/resources/file_manager/js/photo/gallery_scripts.js b/chrome/browser/resources/file_manager/js/photo/gallery_scripts.js
index 57cbdd8..7daf49c 100644
--- a/chrome/browser/resources/file_manager/js/photo/gallery_scripts.js
+++ b/chrome/browser/resources/file_manager/js/photo/gallery_scripts.js
@@ -34,6 +34,7 @@
 //<include src="../file_type.js">
 //<include src="../async_util.js">
 //<include src="../util.js">
+//<include src="../path_util.js">
 //<include src="../volume_manager.js">
 
 //<include src="../image_editor/image_util.js"/>
diff --git a/chrome/browser/resources/file_manager/js/photo/importing_dialog.js b/chrome/browser/resources/file_manager/js/photo/importing_dialog.js
index 88d1232..ce9e6c3 100644
--- a/chrome/browser/resources/file_manager/js/photo/importing_dialog.js
+++ b/chrome/browser/resources/file_manager/js/photo/importing_dialog.js
@@ -7,16 +7,17 @@
 /**
  * ImportingDialog manages the import process (which is really a copying).
  * @param {HTMLElement} parentNode Node to be parent for this dialog.
- * @param {FileCopyManager} copyManager Copy manager isntance.
+ * @param {FileOperationManager} fileOperationManager File operation manager
+ *     isntance.
  * @param {MetadataCache} metadataCache Metadata cache.
  * @param {number=} opt_parentWindowId Id of the parent window.
  * @constructor
  */
 function ImportingDialog(
-    parentNode, copyManager, metadataCache, opt_parentWindowId) {
+    parentNode, fileOperationManager, metadataCache, opt_parentWindowId) {
   cr.ui.dialogs.BaseDialog.call(this, parentNode);
   this.parentWindowId_ = opt_parentWindowId;
-  this.copyManager_ = copyManager;
+  this.fileOperationManager_ = fileOperationManager;
   this.metadataCache_ = metadataCache;
   this.onCopyProgressBound_ = this.onCopyProgress_.bind(this);
 }
@@ -79,7 +80,7 @@
   this.move_ = move;
 
   this.progress_.querySelector('.progress-track').style.width = '0';
-  this.copyManager_.addEventListener('copy-progress',
+  this.fileOperationManager_.addEventListener('copy-progress',
       this.onCopyProgressBound_);
 
   this.previewEntry_(0);
@@ -91,7 +92,7 @@
  */
 ImportingDialog.prototype.start = function(destination) {
   this.destination_ = destination;
-  this.copyManager_.paste(
+  this.fileOperationManager_.paste(
       this.entries_.map(function(e) { return e.fullPath }),
       this.destination_.fullPath,
       this.move_);
@@ -119,7 +120,7 @@
  * @param {function()=} opt_onHide Completion callback.
  */
 ImportingDialog.prototype.hide = function(opt_onHide) {
-  this.copyManager_.removeEventListener('copy-progress',
+  this.fileOperationManager_.removeEventListener('copy-progress',
       this.onCopyProgressBound_);
   cr.ui.dialogs.BaseDialog.prototype.hide.call(this, opt_onHide);
 };
@@ -129,7 +130,7 @@
  * @private
  */
 ImportingDialog.prototype.onCancelClick_ = function() {
-  this.copyManager_.requestCancel();
+  this.fileOperationManager_.requestCancel();
   this.hide();
 };
 
@@ -175,14 +176,14 @@
 
 /**
  * 'copy-progress' event handler. Show progress.
- * @param {cr.Event} event A 'copy-progress' event from FileCopyManager.
+ * @param {cr.Event} event A 'copy-progress' event from FileOperationManager.
  * @private
  */
 ImportingDialog.prototype.onCopyProgress_ = function(event) {
   switch (event.reason) {
     case 'BEGIN':
     case 'PROGRESS':
-      var progress = this.copyManager_.getStatus().percentage;
+      var progress = this.fileOperationManager_.getStatus().percentage;
       this.progress_.querySelector('.progress-track').style.width =
           (progress * 100) + '%';
       var index = Math.round(this.entries_.length * progress);
diff --git a/chrome/browser/resources/file_manager/js/photo/photo_import.js b/chrome/browser/resources/file_manager/js/photo/photo_import.js
index 48a2c1c..6701b7e 100644
--- a/chrome/browser/resources/file_manager/js/photo/photo_import.js
+++ b/chrome/browser/resources/file_manager/js/photo/photo_import.js
@@ -21,7 +21,8 @@
   this.document_ = this.dom_.ownerDocument;
   this.metadataCache_ = params.metadataCache;
   this.volumeManager_ = new VolumeManager();
-  this.copyManager_ = FileCopyManagerWrapper.getInstance(this.filesystem_.root);
+  this.fileOperationManager_ =
+      FileOperationManagerWrapper.getInstance(this.filesystem_.root);
   this.mediaFilesList_ = null;
   this.destination_ = null;
   this.myPhotosDirectory_ = null;
@@ -119,7 +120,8 @@
       this.onSelectionChanged_.bind(this));
   this.onSelectionChanged_();
 
-  this.importingDialog_ = new ImportingDialog(this.dom_, this.copyManager_,
+  this.importingDialog_ = new ImportingDialog(
+      this.dom_, this.fileOperationManager_,
       this.metadataCache_, this.parentWindowId_);
 
   var dialogs = cr.ui.dialogs;
diff --git a/chrome/browser/resources/file_manager/js/photo/photo_import_scripts.js b/chrome/browser/resources/file_manager/js/photo/photo_import_scripts.js
index e0bf1dc..28d7eb4 100644
--- a/chrome/browser/resources/file_manager/js/photo/photo_import_scripts.js
+++ b/chrome/browser/resources/file_manager/js/photo/photo_import_scripts.js
@@ -33,11 +33,11 @@
 
 //<include src="../async_util.js"/>
 //<include src="../util.js"/>
+//<include src="../path_util.js"/>
 //<include src="../file_type.js"/>
 //<include src="../directory_contents.js"/>
 //<include src="../volume_manager.js"/>
-//<include src="../path_util.js"/>
-//<include src="../file_copy_manager_wrapper.js"/>
+//<include src="../file_operation_manager_wrapper.js"/>
 //<include src="../metadata/metadata_cache.js"/>
 //<include src="../metrics.js"/>
 //<include src="../image_editor/image_util.js"/>
@@ -49,6 +49,6 @@
 
 // Exports
 window.ImageUtil = ImageUtil;
-window.FileCopyManagerWrapper = FileCopyManagerWrapper;
+window.FileOperationManagerWrapper = FileOperationManagerWrapper;
 
 })();
diff --git a/chrome/browser/resources/file_manager/js/share_dialog.js b/chrome/browser/resources/file_manager/js/share_dialog.js
index 42c0000..1fb0f52 100644
--- a/chrome/browser/resources/file_manager/js/share_dialog.js
+++ b/chrome/browser/resources/file_manager/js/share_dialog.js
@@ -223,7 +223,7 @@
     this.webView_.addEventListener('newwindow', function(e) {
       // Discard the window object and reopen in an external window.
       e.window.discard();
-      chrome.windows.create({url: e.targetUrl});
+      util.visitURL(e.targetUrl);
       e.preventDefault();
     });
     cr.ui.dialogs.BaseDialog.prototype.show.call(this, '', null, null, null);
diff --git a/chrome/browser/resources/file_manager/js/suggest_apps_dialog.js b/chrome/browser/resources/file_manager/js/suggest_apps_dialog.js
new file mode 100644
index 0000000..2fbdde1
--- /dev/null
+++ b/chrome/browser/resources/file_manager/js/suggest_apps_dialog.js
@@ -0,0 +1,331 @@
+// Copyright (c) 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.
+
+'use strict';
+
+/**
+ * SuggestAppsDialog contains a list box to select an app to be opened the file
+ * with. This dialog should be used as action picker for file operations.
+ */
+
+/**
+ * The width of the widget (in pixel).
+ * @type {number}
+ * @const
+ */
+var WEBVIEW_WIDTH = 400;
+/**
+ * The height of the widget (in pixel).
+ * @type {number}
+ * @const
+ */
+var WEBVIEW_HEIGHT = 480;
+
+/**
+ * The URL of the widget.
+ * @type {string}
+ * @const
+ */
+var CWS_WIDGET_URL =
+    'https://clients5.google.com/webstore/wall/cros-widget-container';
+/**
+ * The origin of the widget.
+ * @type {string}
+ * @const
+ */
+var CWS_WIDGET_ORIGIN = 'https://clients5.google.com';
+
+/**
+ * RegExp to extract the origin (shema, host and port) from the URL.
+ * TODO(yoshiki): Remove this before ShareDialog launches or M31 branch cut.
+ *
+ * @type {RegExp}
+ * @const
+ */
+var REGEXP_EXTRACT_HOST = /^https?:\/\/[\w\.\-]+(?:\:\d{1,5})?(?=\/)/;
+
+/**
+ * RegExp to check if the origin is google host or not.
+ * Google hosts must be on https and default port.
+ * TODO(yoshiki): Remove this before ShareDialog launches or M31 branch cut.
+ *
+ * @type {RegExp}
+ * @const
+ */
+var REGEXP_GOOGLE_MATCH = /^https:\/\/[\w\.\-]+\.google\.com$/;
+
+/**
+ * RegExp to check if the origin is localhost or not.
+ * TODO(yoshiki): Remove this before ShareDialog launches or M31 branch cut.
+ *
+ * @type {RegExp}
+ * @const
+ */
+var REGEXP_LOCALHOST_MATCH = /^https?:\/\/localhost(?:\:\d{1,5})?$/;
+
+/**
+ * Creates dialog in DOM tree.
+ *
+ * @param {HTMLElement} parentNode Node to be parent for this dialog.
+ * @constructor
+ * @extends {cr.ui.dialogs.BaseDialog}
+ */
+function SuggestAppsDialog(parentNode) {
+  cr.ui.dialogs.BaseDialog.call(this, parentNode);
+
+  this.frame_.id = 'suggest-app-dialog';
+
+  this.webviewContainer_ = this.document_.createElement('div');
+  this.webviewContainer_.id = 'webview-container';
+  this.frame_.insertBefore(this.webviewContainer_, this.text_.nextSibling);
+
+  this.buttons_ = this.document_.createElement('div');
+  this.buttons_.id = 'buttons';
+  this.frame_.appendChild(this.buttons_);
+
+  this.webstoreButton_ = this.document_.createElement('div');
+  this.webstoreButton_.id = 'webstore-button';
+  this.webstoreButton_.innerHTML = str('SUGGEST_DIALOG_LINK_TO_WEBSTORE');
+  this.webstoreButton_.addEventListener(
+      'click', this.onWebstoreLinkClicked_.bind(this));
+  this.buttons_.appendChild(this.webstoreButton_);
+
+  this.initialFocusElement_ = this.webviewContainer_;
+
+  this.accessToken_ = null;
+  this.widgetUrl_ = CWS_WIDGET_URL;
+  this.widgetOrigin_ = CWS_WIDGET_ORIGIN;
+
+  // For development, we provide the feature to override the URL of the widget.
+  // TODO(yoshiki): Remove this before ShareDialog launches or M31 branch cut.
+  this.urlOverrided_ = false;
+  chrome.storage.local.get(
+      ['widgetUrlOverride'],
+      function(items) {
+        if (items['widgetUrlOverride']) {
+          this.widgetUrl_ = items['widgetUrlOverride'];
+          var match = REGEXP_EXTRACT_HOST.exec(this.widgetUrl_);
+          // Overriding URL must be on either localhost or .google.com.
+          if (!match ||
+              (!REGEXP_GOOGLE_MATCH.test(match[0]) &&
+               !REGEXP_LOCALHOST_MATCH.test(match[0])))
+            throw new Error('The widget URL is invalid.');
+          this.widgetOrigin_ = match[0];
+          this.urlOverrided_ = true;
+        }
+      }.bind(this));
+
+  this.extension_ = null;
+  this.mime_ = null;
+  this.installingItemId_ = null;
+  this.state_ = SuggestAppsDialog.State.UNINITIALIZED;
+
+  this.initializationTask_ = new AsyncUtil.Group();
+  this.initializationTask_.add(this.retrieveAuthorizeToken_.bind(this));
+  this.initializationTask_.run();
+}
+
+SuggestAppsDialog.prototype = {
+  __proto__: cr.ui.dialogs.BaseDialog.prototype
+};
+
+/**
+ * @enum {number}
+ * @const
+ */
+SuggestAppsDialog.State = {
+  UNINITIALIZED: 0,
+  INITIALIZED: 1,
+  INSTALLING: 2,
+  INSTALLED: 3,
+  INSTALL_FAILED: 4,
+};
+
+/**
+ * @override
+ */
+SuggestAppsDialog.prototype.onInputFocus = function() {
+  this.webviewContainer_.select();
+};
+
+/**
+ * Injects headers into the passed request.
+ *
+ * @param {Event} e Request event.
+ * @return {{requestHeaders: HttpHeaders}} Modified headers.
+ * @private
+ */
+SuggestAppsDialog.prototype.authorizeRequest_ = function(e) {
+  e.requestHeaders.push({
+    name: 'Authorization',
+    value: 'Bearer ' + this.accessToken_
+  });
+  return {requestHeaders: e.requestHeaders};
+};
+
+/**
+ * Retrieves the authorize token. This method should be called in
+ * initialization of the dialog.
+ *
+ * @param {function()} callback Called when the token is retrieved.
+ * @private
+ */
+SuggestAppsDialog.prototype.retrieveAuthorizeToken_ = function(callback) {
+  // TODO(yoshiki): Share the access token with ShareDialog.
+  if (this.accessToken_) {
+    callback();
+    return;
+  }
+
+  // Fetch or update the access token.
+  chrome.fileBrowserPrivate.requestAccessToken(
+      false,  // force_refresh
+      function(inAccessToken) {
+        this.accessToken_ = inAccessToken;
+        callback();
+      }.bind(this));
+};
+
+/**
+ * Shows dialog.
+ *
+ * @param {string} extension Extension of the file.
+ * @param {string} mime Mime of the file.
+ * @param {function(boolean)} onDialogClosed Called when the dialog is closed.
+ *     The argument is the result of installation: true if an app is installed,
+ *     false otherwise.
+ */
+SuggestAppsDialog.prototype.show = function(extension, mime, onDialogClosed) {
+  this.extension_ = extension;
+  this.mimeType_ = mime;
+
+  // Makes it sure that the initialization is completed.
+  this.initializationTask_.run(function() {
+    var title = str('SUGGEST_DIALOG_TITLE');
+
+    // TODO(yoshiki): Remove this before ShareDialog launches.
+    if (this.urlOverrided_)
+      title += ' [OVERRIDED]';
+
+    cr.ui.dialogs.BaseDialog.prototype.showWithTitle.apply(
+        this, [title, '', function() {}, null, null]);
+
+    this.onDialogClosed_ = onDialogClosed;
+
+    this.webviewContainer_.innerHTML =
+        '<webview id="cws-widget" partition="persist:cwswidgets"></webview>';
+    this.webviewContainer_.style.width = WEBVIEW_WIDTH + 'px';
+    this.webviewContainer_.style.height = WEBVIEW_HEIGHT + 'px';
+
+    var webview = this.container_.querySelector('#cws-widget');
+    webview.style.width = WEBVIEW_WIDTH + 'px';
+    webview.style.height = WEBVIEW_HEIGHT + 'px';
+    webview.request.onBeforeSendHeaders.addListener(
+        this.authorizeRequest_.bind(this),
+        {urls: [this.widgetOrigin_ + '/*']},
+        ['blocking', 'requestHeaders']);
+    webview.addEventListener('newwindow', function(event) {
+      // Discard the window object and reopen in an external window.
+      event.window.discard();
+      util.visitURL(e.targetUrl);
+      event.preventDefault();
+    });
+    webview.focus();
+
+    this.webviewClient_ = new CWSContainerClient(
+        webview,
+        extension, mime,
+        WEBVIEW_WIDTH, WEBVIEW_HEIGHT,
+        this.widgetUrl_, this.widgetOrigin_);
+    this.webviewClient_.addEventListener('install-request',
+                                         this.onInstallRequest_.bind(this));
+    this.webviewClient_.load();
+
+    this.state_ = SuggestAppsDialog.State.INITIALIZED;
+  }.bind(this));
+};
+
+/**
+ * Called when the 'See more...' link is clicked to be navigated to Webstore.
+ * @param {Event} e Evnet.
+ * @private
+ */
+SuggestAppsDialog.prototype.onWebstoreLinkClicked_ = function(e) {
+  var webStoreUrl =
+      FileTasks.createWebStoreLink(this.extension_, this.mimeType_);
+  chrome.windows.create({url: webStoreUrl});
+  this.hide();
+};
+
+/**
+ * Called when receiving the install request from the webview client.
+ * @param {Event} e Evnet.
+ * @private
+ */
+SuggestAppsDialog.prototype.onInstallRequest_ = function(e) {
+  var itemId = e.itemId;
+  this.installingItemId_ = itemId;
+
+  this.appInstaller_ = new AppInstaller(itemId);
+  this.appInstaller_.install(this.onInstallCompleted_.bind(this));
+
+  this.state_ = SuggestAppsDialog.State.INSTALLING;
+};
+
+/**
+ * Called when the installation is completed from the app installer.
+ * @param {AppInstaller.Result} result Result of the installation.
+ * @param {string} error Detail of the error.
+ * @private
+ */
+SuggestAppsDialog.prototype.onInstallCompleted_ = function(result, error) {
+  this.state_ = (result === AppInstaller.Result.SUCCESS) ?
+                SuggestAppsDialog.State.INSTALLED :
+                SuggestAppsDialog.State.INSTALL_FAILED;
+  var success = (result === AppInstaller.Result.SUCCESS);
+  this.webviewClient_.onInstallCompleted(success, this.installingItemId_);
+  this.installingItemId_ = null;
+
+  switch (result) {
+  case AppInstaller.Result.SUCCESS:
+    this.hide();
+    break;
+  case AppInstaller.Result.CANCELLED:
+    // User cancelled the installation. Do nothing.
+    break;
+  case AppInstaller.Result.ERROR:
+    fileManager.error.show(str('SUGGEST_DIALOG_INSTALLATION_FAILED'));
+    break;
+  }
+};
+
+/**
+ * @override
+ */
+SuggestAppsDialog.prototype.hide = function() {
+  // Install is being aborted. Send the failure result.
+  if (this.state_ == SuggestAppsDialog.State.INSTALLING) {
+    if (this.webviewClient_)
+      this.webviewClient_.onInstallCompleted(false, this.installingItemId_);
+    this.state_ = SuggestAppsDialog.State.INSTALL_FAILED;
+    this.installingItemId_ = null;
+  }
+
+  if (this.webviewClient_) {
+    this.webviewClient_.dispose();
+    this.webviewClient_ = null;
+  }
+
+  this.webviewContainer_.innerHTML = '';
+  this.extension_ = null;
+  this.mime_ = null;
+
+  cr.ui.dialogs.BaseDialog.prototype.hide.call(this);
+
+  // Calls the callback after the dialog hides.
+  setTimeout(function() {
+    var installed = this.state_ == SuggestAppsDialog.State.INSTALLED;
+    this.onDialogClosed_(installed);
+  }.bind(this), 0);
+};
diff --git a/chrome/browser/resources/file_manager/js/util.js b/chrome/browser/resources/file_manager/js/util.js
index 843584c..c7afd3f 100644
--- a/chrome/browser/resources/file_manager/js/util.js
+++ b/chrome/browser/resources/file_manager/js/util.js
@@ -1199,3 +1199,19 @@
   var taskId = chrome.runtime.id + '|file|view-in-browser';
   chrome.fileBrowserPrivate.executeTask(taskId, urls, callback);
 };
+
+/**
+ * Visit the URL.
+ *
+ * If the browser is opening, the url is opened in a new tag, otherwise the url
+ * is opened in a new window.
+ *
+ * @param {string} url URL to visit.
+ */
+util.visitURL = function(url) {
+  var params = {url: url};
+  chrome.tabs.create(params, function() {
+    if (chrome.runtime.lastError)
+      chrome.windows.create(params);
+  });
+};
diff --git a/chrome/browser/resources/file_manager/js/volume_manager.js b/chrome/browser/resources/file_manager/js/volume_manager.js
index b00eb36..eb5503a 100644
--- a/chrome/browser/resources/file_manager/js/volume_manager.js
+++ b/chrome/browser/resources/file_manager/js/volume_manager.js
@@ -9,6 +9,7 @@
  * flush storage", or "mounted zip archive" etc.
  *
  * @param {string} mountPath Where the volume is mounted.
+ * @param {DirectoryEntry} root The root directory entry of this volume.
  * @param {string} error The error if an error is found.
  * @param {string} deviceType The type of device ('usb'|'sd'|'optical'|'mobile'
  *     |'unknown') (as defined in chromeos/disks/disk_mount_manager.cc).
@@ -16,20 +17,17 @@
  * @param {boolean} isReadOnly True if the volume is read only.
  * @constructor
  */
-function VolumeInfo(mountPath, error, deviceType, isReadOnly) {
+function VolumeInfo(mountPath, root, error, deviceType, isReadOnly) {
   // TODO(hidehiko): This should include FileSystem instance.
-  this.mountPath_ = mountPath;
-  this.error_ = error;
-  this.deviceType_ = deviceType;
-  this.isReadOnly_ = !!isReadOnly;
-}
+  this.mountPath = mountPath;
+  this.root = root;
+  this.error = error;
+  this.deviceType = deviceType;
+  this.isReadOnly = isReadOnly;
 
-VolumeInfo.prototype = {
-  get mountPath() { return this.mountPath_; },
-  get error() { return this.error_; },
-  get deviceType() { return this.deviceType_; },
-  get isReadOnly() { return this.isReadOnly_; },
-};
+  // VolumeInfo is immutable.
+  Object.freeze(this);
+}
 
 /**
  * Utilities for volume manager implementation.
@@ -93,14 +91,169 @@
       function(metadata) {
         if (chrome.runtime.lastError && !error)
           error = VolumeManager.Error.UNKNOWN;
-        callback(new VolumeInfo(
-            mountPath, error,
-            metadata && metadata.deviceType,
-            !!metadata && metadata.isReadOnly));
+
+        var deviceType = null;
+        var isReadOnly = false;
+        if (metadata) {
+          deviceType = metadata.deviceType;
+          isReadOnly = metadata.isReadOnly;
+        }
+
+        // TODO(hidehiko): After multi-filesystem is supported,
+        // FileSystem object holds its root entry. So, we won't need this
+        // resolving.
+        webkitResolveLocalFileSystemURL(
+            util.makeFilesystemUrl(mountPath),
+            function(entry) {
+              callback(new VolumeInfo(
+                  mountPath, entry, error, deviceType, isReadOnly));
+            },
+            function(error) {
+              callback(new VolumeInfo(
+                  mountPath, null, error, deviceType, isReadOnly));
+            });
       });
 };
 
 /**
+ * The order of the volume list based on root type.
+ * @type {Array.<string>}
+ * @const
+ * @private
+ */
+volumeManagerUtil.volumeListOrder_ = [
+  RootType.DRIVE, RootType.DOWNLOADS, RootType.ARCHIVE, RootType.REMOVABLE
+];
+
+/**
+ * Compares mount paths to sort the volume list order.
+ * @param {string} mountPath1 The mount path for the first volume.
+ * @param {string} mountPath2 The mount path for the second volume.
+ * @return {number} 0 if mountPath1 and mountPath2 are same, -1 if VolumeInfo
+ *     mounted at mountPath1 should be listed before the one mounted at
+ *     mountPath2, otherwise 1.
+ */
+volumeManagerUtil.compareMountPath = function(mountPath1, mountPath2) {
+  var order1 = volumeManagerUtil.volumeListOrder_.indexOf(
+      PathUtil.getRootType(mountPath1));
+  var order2 = volumeManagerUtil.volumeListOrder_.indexOf(
+      PathUtil.getRootType(mountPath2));
+  if (order1 != order2)
+    return order1 < order2 ? -1 : 1;
+
+  if (mountPath1 != mountPath2)
+    return mountPath1 < mountPath2 ? -1 : 1;
+
+  // The path is same.
+  return 0;
+};
+
+/**
+ * The container of the VolumeInfo for each mounted volume.
+ * @constructor
+ */
+function VolumeInfoList() {
+  /**
+   * Holds VolumeInfo instances.
+   * @type {cr.ui.ArrayDataModel}
+   * @private
+   */
+  this.model_ = new cr.ui.ArrayDataModel([]);
+
+  Object.freeze(this);
+}
+
+VolumeInfoList.prototype = {
+  get length() { return this.model_.length; }
+};
+
+/**
+ * Adds the event listener to listen the change of volume info.
+ * @param {string} type The name of the event.
+ * @param {function(cr.Event)} handler The handler for the event.
+ */
+VolumeInfoList.prototype.addEventListener = function(type, handler) {
+  this.model_.addEventListener(type, handler);
+};
+
+/**
+ * Removes the event listener.
+ * @param {string} type The name of the event.
+ * @param {function(cr.Event)} handler The handler to be removed.
+ */
+VolumeInfoList.prototype.removeEventListener = function(type, handler) {
+  this.model_.removeEventListener(type, handler);
+};
+
+/**
+ * Adds the volumeInfo to the appropriate position. If there already exists,
+ * just replaces it.
+ * @param {VolumeInfo} volumeInfo The information of the new volume.
+ */
+VolumeInfoList.prototype.add = function(volumeInfo) {
+  var index = this.findLowerBoundIndex_(volumeInfo.mountPath);
+  if (index < this.length &&
+      this.item(index).mountPath == volumeInfo.mountPath) {
+    // Replace the VolumeInfo.
+    this.model_.splice(index, 1, volumeInfo);
+  } else {
+    // Insert the VolumeInfo.
+    this.model_.splice(index, 0, volumeInfo);
+  }
+};
+
+/**
+ * Removes the VolumeInfo of the volume mounted at mountPath.
+ * @param {string} mountPath The path to the location where the volume is
+ *     mounted.
+ */
+VolumeInfoList.prototype.remove = function(mountPath) {
+  var index = this.findLowerBoundIndex_(mountPath);
+  if (index < this.length && this.item(index).mountPath == mountPath)
+    this.model_.splice(index, 1);
+};
+
+/**
+ * Searches the information of the volume mounted at mountPath.
+ * @param {string} mountPath The path to the location where the volume is
+ *     mounted.
+ * @return {VolumeInfo} The volume's information, or null if not found.
+ */
+VolumeInfoList.prototype.find = function(mountPath) {
+  var index = this.findLowerBoundIndex_(mountPath);
+  if (index < this.length && this.item(index).mountPath == mountPath)
+    return this.item(index);
+
+  // Not found.
+  return null;
+};
+
+/**
+ * @param {string} mountPath The mount path of searched volume.
+ * @return {number} The index of the volumee if found, or the inserting
+ *     position of the volume.
+ * @private
+ */
+VolumeInfoList.prototype.findLowerBoundIndex_ = function(mountPath) {
+  // Assuming the number of elements in the array data model is very small
+  // in most cases, use simple linear search, here.
+  for (var i = 0; i < this.length; i++) {
+    if (volumeManagerUtil.compareMountPath(
+            this.item(i).mountPath, mountPath) >= 0)
+      return i;
+  }
+  return this.length;
+};
+
+/**
+ * @param {number} index The index of the volume in the list.
+ * @return {VolumeInfo} The VolumeInfo instance.
+ */
+VolumeInfoList.prototype.item = function(index) {
+  return this.model_.item(index);
+};
+
+/**
  * VolumeManager is responsible for tracking list of mounted volumes.
  *
  * @param {Entry} root The root of file system.
@@ -125,11 +278,10 @@
   this.requests_ = {};
 
   /**
-   * TODO(hidehiko): Replace Object with cr.ui.ArrayDataModel.
-   * @type {Object.<string, VolumeInfo>}
-   * @private
+   * The list of VolumeInfo instances for each mounted volume.
+   * @type {VolumeInfoList}
    */
-  this.mountedVolumes_ = {};
+  this.volumeInfoList = new VolumeInfoList();
 
   /**
    * True, if mount points have been initialized.
@@ -333,7 +485,7 @@
  */
 VolumeManager.prototype.isMounted = function(mountPath) {
   volumeManagerUtil.validateMountPath(mountPath);
-  return mountPath in this.mountedVolumes_;
+  return !!this.volumeInfoList.find(mountPath);
 };
 
 /**
@@ -371,7 +523,7 @@
         volumeManagerUtil.createVolumeInfo(
             '/' + mountPoint.mountPath, error,
             function(volumeInfo) {
-              this.mountedVolumes_[volumeInfo.mountPath] = volumeInfo;
+              this.volumeInfoList.add(volumeInfo);
               callback();
             }.bind(this));
       }.bind(this, mountPoint, error));
@@ -414,7 +566,7 @@
       var error = event.status == 'success' ? '' : event.status;
       volumeManagerUtil.createVolumeInfo(
           event.mountPath, error, function(volume) {
-            this.mountedVolumes_[volume.mountPath] = volume;
+            this.volumeInfoList.add(volume);
             this.finishRequest_(requestKey, event.status, event.mountPath);
             cr.dispatchSimpleEvent(this, 'change');
           }.bind(this));
@@ -433,7 +585,7 @@
     var requestKey = this.makeRequestKey_('unmount', '', event.mountPath);
     var requested = requestKey in this.requests_;
     if (event.status == 'success' && !requested &&
-        mountPath in this.mountedVolumes_) {
+        this.volumeInfoList.find(mountPath)) {
       console.warn('Mounted volume without a request: ', mountPath);
       var e = new cr.Event('externally-unmounted');
       e.mountPath = mountPath;
@@ -442,7 +594,7 @@
     this.finishRequest_(requestKey, status);
 
     if (event.status == 'success') {
-      delete this.mountedVolumes_[mountPath];
+      this.volumeInfoList.remove(mountPath);
       cr.dispatchSimpleEvent(this, 'change');
     }
   }
@@ -569,7 +721,7 @@
     return;
   }
 
-  var volumeInfo = this.mountedVolumes_[mountPath];
+  var volumeInfo = this.volumeInfoList.find(mountPath);
   if (!volumeInfo) {
     errorCallback(VolumeManager.Error.NOT_MOUNTED);
     return;
@@ -586,7 +738,7 @@
  */
 VolumeManager.prototype.getVolumeInfo = function(mountPath) {
   volumeManagerUtil.validateMountPath(mountPath);
-  return this.mountedVolumes_[mountPath];
+  return this.volumeInfoList.find(mountPath);
 };
 
 /**
diff --git a/chrome/browser/resources/file_manager/main.html b/chrome/browser/resources/file_manager/main.html
index 7fc55c0..052f53d 100644
--- a/chrome/browser/resources/file_manager/main.html
+++ b/chrome/browser/resources/file_manager/main.html
@@ -82,19 +82,21 @@
       <script src="js/combobutton.js"></script>
       <script src="js/commandbutton.js"></script>
 
+      <script src="js/app_installer.js"></script>
       <script src="js/async_util.js"></script>
       <script src="js/path_util.js"></script>
       <script src="js/util.js"></script>
       <script src="js/action_choice_util.js"></script>
       <script src="js/breadcrumbs_controller.js"></script>
       <script src="js/butter_bar.js"></script>
+      <script src="js/cws_container_client.js"></script>
       <script src="js/directory_contents.js"></script>
       <script src="js/directory_model.js"></script>
       <script src="js/directory_tree.js"></script>
       <script src="js/drag_selector.js"></script>
       <script src="js/drive_banners.js"></script>
       <script src="js/error_dialog.js"></script>
-      <script src="js/file_copy_manager_wrapper.js"></script>
+      <script src="js/file_operation_manager_wrapper.js"></script>
       <script src="js/file_grid.js"></script>
       <script src="js/file_manager.js"></script>
       <script src="js/file_manager_pyauto.js"></script>
@@ -109,6 +111,7 @@
       <script src="js/scrollbar.js"></script>
       <script src="js/share_client.js"></script>
       <script src="js/share_dialog.js"></script>
+      <script src="js/suggest_apps_dialog.js"></script>
       <script src="js/tree.css.js"></script>
       <script src="js/volume_manager.js"></script>
       <script src="js/media/media_util.js"></script>
@@ -267,15 +270,15 @@
         <menuitem command="#delete" i18n-content=DELETE_BUTTON_LABEL></menuitem>
     </menu>
 
-    <div class=dialog-container>
-      <div class=dialog-sidebar>
-        <div class=dialog-sidebar-header>
+    <div class="dialog-container">
+      <div class="dialog-navigation-list">
+        <div class="dialog-navigation-list-header">
           <span id="app-name"></span>
         </div>
-        <div class=dialog-sidebar-contents>
-          <list id="volume-list" tabindex="8"></list>
+        <div class="dialog-navigation-list-contents">
+          <list id="navigation-list" tabindex="8"></list>
         </div>
-        <div class=dialog-sidebar-footer>
+        <div class="dialog-navigation-list-footer">
           <div id="butter-bar-container">
             <div id="butter-bar">
               <div class="content">
@@ -289,7 +292,7 @@
           </div>
         </div>
       </div>
-      <div class="splitter" id="sidebar-splitter"></div>
+      <div class="splitter" id="navigation-list-splitter"></div>
       <div class="dialog-main">
         <div class="dialog-header">
           <div class="search-box-wrapper">
diff --git a/chrome/browser/resources/file_manager/manifest.json b/chrome/browser/resources/file_manager/manifest.json
index 72b0db2..f696e9e 100644
--- a/chrome/browser/resources/file_manager/manifest.json
+++ b/chrome/browser/resources/file_manager/manifest.json
@@ -224,7 +224,7 @@
       "scripts": [
         "chrome://resources/js/cr.js",
         "chrome://resources/js/cr/event_target.js",
-        "js/file_copy_manager.js",
+        "js/file_operation_manager.js",
         "js/async_util.js",
         "js/path_util.js",
         "js/util.js",
diff --git a/chrome/browser/resources/file_manager/mediaplayer.html b/chrome/browser/resources/file_manager/mediaplayer.html
index 5c60121..9e22bbe 100644
--- a/chrome/browser/resources/file_manager/mediaplayer.html
+++ b/chrome/browser/resources/file_manager/mediaplayer.html
@@ -28,6 +28,7 @@
     <script src="chrome://resources/js/cr/event_target.js"></script>
     <script src="js/async_util.js"></script>
     <script src="js/util.js"></script>
+    <script src="js/path_util.js"></script>
     <script src="js/file_type.js"></script>
     <script src="js/volume_manager.js"></script>
     <script src="js/metadata/metadata_cache.js"></script>
diff --git a/chrome/browser/resources/file_manager/photo_import.html b/chrome/browser/resources/file_manager/photo_import.html
index 054cf34..d3976de 100644
--- a/chrome/browser/resources/file_manager/photo_import.html
+++ b/chrome/browser/resources/file_manager/photo_import.html
@@ -49,11 +49,11 @@
 
     <script src="js/async_util.js"></script>
     <script src="js/util.js"></script>
+    <script src="js/path_util.js"></script>
     <script src="js/file_type.js"></script>
     <script src="js/directory_contents.js"></script>
     <script src="js/volume_manager.js"></script>
-    <script src="js/file_copy_manager_wrapper.js"></script>
-    <script src="js/path_util.js"></script>
+    <script src="js/file_operation_manager_wrapper.js"></script>
     <script src="js/metadata/metadata_cache.js"></script>
     <script src="js/metrics.js"></script>
     <script src="js/image_editor/image_util.js"></script>
diff --git a/chrome/browser/resources/file_manager/video_player.html b/chrome/browser/resources/file_manager/video_player.html
index 41ded04..a039aff 100644
--- a/chrome/browser/resources/file_manager/video_player.html
+++ b/chrome/browser/resources/file_manager/video_player.html
@@ -27,6 +27,7 @@
 
     <script src="js/async_util.js"></script>
     <script src="js/util.js"></script>
+    <script src="js/path_util.js"></script>
     <script src="js/file_type.js"></script>
     <script src="js/volume_manager.js"></script>
     <script src="js/metadata/metadata_cache.js"></script>
diff --git a/chrome/browser/resources/gesture_config.js b/chrome/browser/resources/gesture_config.js
index 2efb710..8ba0b45 100644
--- a/chrome/browser/resources/gesture_config.js
+++ b/chrome/browser/resources/gesture_config.js
@@ -330,8 +330,13 @@
       units: '%'
     },
     {
+      key: 'minimum_threshold_start_touchpad',
+      label: 'Start overscroll gesture (horizontal; touchpad)',
+      units: 'pixels'
+    },
+    {
       key: 'minimum_threshold_start',
-      label: 'Start overscroll gesture (horizontal)',
+      label: 'Start overscroll gesture (horizontal; touchscreen)',
       units: 'pixels'
     },
     {
diff --git a/chrome/browser/resources/google_now/background.js b/chrome/browser/resources/google_now/background.js
index 399ed96..ba36c2a 100644
--- a/chrome/browser/resources/google_now/background.js
+++ b/chrome/browser/resources/google_now/background.js
@@ -116,29 +116,29 @@
 var tasks = buildTaskManager(areTasksConflicting);
 
 // Add error processing to API calls.
-tasks.instrumentChromeApiFunction('location.onLocationUpdate.addListener', 0);
-tasks.instrumentChromeApiFunction('metricsPrivate.getVariationParams', 1);
-tasks.instrumentChromeApiFunction('notifications.create', 2);
-tasks.instrumentChromeApiFunction('notifications.update', 2);
-tasks.instrumentChromeApiFunction('notifications.getAll', 0);
-tasks.instrumentChromeApiFunction(
+wrapper.instrumentChromeApiFunction('location.onLocationUpdate.addListener', 0);
+wrapper.instrumentChromeApiFunction('metricsPrivate.getVariationParams', 1);
+wrapper.instrumentChromeApiFunction('notifications.create', 2);
+wrapper.instrumentChromeApiFunction('notifications.update', 2);
+wrapper.instrumentChromeApiFunction('notifications.getAll', 0);
+wrapper.instrumentChromeApiFunction(
     'notifications.onButtonClicked.addListener', 0);
-tasks.instrumentChromeApiFunction('notifications.onClicked.addListener', 0);
-tasks.instrumentChromeApiFunction('notifications.onClosed.addListener', 0);
-tasks.instrumentChromeApiFunction('omnibox.onInputEntered.addListener', 0);
-tasks.instrumentChromeApiFunction(
+wrapper.instrumentChromeApiFunction('notifications.onClicked.addListener', 0);
+wrapper.instrumentChromeApiFunction('notifications.onClosed.addListener', 0);
+wrapper.instrumentChromeApiFunction('omnibox.onInputEntered.addListener', 0);
+wrapper.instrumentChromeApiFunction(
     'preferencesPrivate.googleGeolocationAccessEnabled.get',
     1);
-tasks.instrumentChromeApiFunction(
+wrapper.instrumentChromeApiFunction(
     'preferencesPrivate.googleGeolocationAccessEnabled.onChange.addListener',
     0);
-tasks.instrumentChromeApiFunction('permissions.contains', 1);
-tasks.instrumentChromeApiFunction('permissions.remove', 1);
-tasks.instrumentChromeApiFunction('permissions.request', 1);
-tasks.instrumentChromeApiFunction('runtime.onInstalled.addListener', 0);
-tasks.instrumentChromeApiFunction('runtime.onStartup.addListener', 0);
-tasks.instrumentChromeApiFunction('tabs.create', 1);
-tasks.instrumentChromeApiFunction('storage.local.get', 1);
+wrapper.instrumentChromeApiFunction('permissions.contains', 1);
+wrapper.instrumentChromeApiFunction('permissions.remove', 1);
+wrapper.instrumentChromeApiFunction('permissions.request', 1);
+wrapper.instrumentChromeApiFunction('runtime.onInstalled.addListener', 0);
+wrapper.instrumentChromeApiFunction('runtime.onStartup.addListener', 0);
+wrapper.instrumentChromeApiFunction('tabs.create', 1);
+wrapper.instrumentChromeApiFunction('storage.local.get', 1);
 
 var updateCardsAttempts = buildAttemptManager(
     'cards-update',
@@ -208,7 +208,7 @@
 
     // Instrument onloadend to remove stale auth tokens.
     var originalOnLoadEnd = request.onloadend;
-    request.onloadend = tasks.wrapCallback(function(event) {
+    request.onloadend = wrapper.wrapCallback(function(event) {
       if (request.status == HTTP_FORBIDDEN ||
           request.status == HTTP_UNAUTHORIZED) {
         tasks.debugSetStepName('setAuthorization-removeToken');
diff --git a/chrome/browser/resources/google_now/background_test_util.js b/chrome/browser/resources/google_now/background_test_util.js
index 3d59e0f..d50207b 100644
--- a/chrome/browser/resources/google_now/background_test_util.js
+++ b/chrome/browser/resources/google_now/background_test_util.js
@@ -6,11 +6,10 @@
 
 function emptyMock() {}
 
+var wrapper = {instrumentChromeApiFunction: emptyMock};
+
 function buildTaskManager() {
-  return {
-    debugSetStepName: emptyMock,
-    instrumentChromeApiFunction: emptyMock,
-  };
+  return {debugSetStepName: emptyMock};
 }
 
 function buildAuthenticationManager() {
@@ -40,4 +39,3 @@
   onInstalled: emptyListener,
   onStartup: emptyListener
 };
-
diff --git a/chrome/browser/resources/google_now/utility.js b/chrome/browser/resources/google_now/utility.js
index 7878ac4..c068da0 100644
--- a/chrome/browser/resources/google_now/utility.js
+++ b/chrome/browser/resources/google_now/utility.js
@@ -6,6 +6,20 @@
 
 /**
  * @fileoverview Utility objects and functions for Google Now extension.
+ * Most important entities here:
+ * (1) 'wrapper' is a module used to add error handling and other services to
+ *     callbacks for HTML and Chrome functions and Chrome event listeners.
+ *     Chrome invokes extension code through event listeners. Once entered via
+ *     an event listener, the extension may call a Chrome/HTML API method
+ *     passing a callback (and so forth), and that callback must occur later,
+ *     otherwise, we generate an error. Chrome may unload event pages waiting
+ *     for an event. When the event fires, Chrome will reload the event page. We
+ *     don't require event listeners to fire because they are generally not
+ *     predictable (like a location change event).
+ * (2) Task Manager (built with buildTaskManager() call) provides controlling
+ *     mutually excluding chains of callbacks called tasks. Task Manager uses
+ *     WrapperPlugins to add instrumentation code to 'wrapper' to determine
+ *     when a task completes.
  */
 
 // TODO(vadimt): Figure out the server name. Use it in the manifest and for
@@ -58,10 +72,313 @@
   return request;
 }
 
+/**
+ * Sends an error report to the server.
+ * @param {Error} error Error to send.
+ */
+function sendErrorReport(error) {
+  // Don't remove 'error.stack.replace' below!
+  var filteredStack = error.canSendMessageToServer ?
+      error.stack : error.stack.replace(/.*\n/, '(message removed)\n');
+  var file;
+  var line;
+  var topFrameLineMatch = filteredStack.match(/\n    at .*\n/);
+  var topFrame = topFrameLineMatch && topFrameLineMatch[0];
+  if (topFrame) {
+    // Examples of a frame:
+    // 1. '\n    at someFunction (chrome-extension://
+    //     pmofbkohncoogjjhahejjfbppikbjigm/background.js:915:15)\n'
+    // 2. '\n    at chrome-extension://pmofbkohncoogjjhahejjfbppikbjigm/
+    //     utility.js:269:18\n'
+    // 3. '\n    at Function.target.(anonymous function) (extensions::
+    //     SafeBuiltins:19:14)\n'
+    // 4. '\n    at Event.dispatchToListener (event_bindings:382:22)\n'
+    var errorLocation;
+    // Find the the parentheses at the end of the line, if any.
+    var parenthesesMatch = topFrame.match(/\(.*\)\n/);
+    if (parenthesesMatch && parenthesesMatch[0]) {
+      errorLocation =
+          parenthesesMatch[0].substring(1, parenthesesMatch[0].length - 2);
+    } else {
+      errorLocation = topFrame;
+    }
+
+    var topFrameElements = errorLocation.split(':');
+    // topFrameElements is an array that ends like:
+    // [N-3] //pmofbkohncoogjjhahejjfbppikbjigm/utility.js
+    // [N-2] 308
+    // [N-1] 19
+    if (topFrameElements.length >= 3) {
+      file = topFrameElements[topFrameElements.length - 3];
+      line = topFrameElements[topFrameElements.length - 2];
+    }
+  }
+
+  var requestParameters =
+      'error=' + encodeURIComponent(error.name) +
+      '&script=' + encodeURIComponent(file) +
+      '&line=' + encodeURIComponent(line) +
+      '&trace=' + encodeURIComponent(filteredStack);
+  var request = buildServerRequest('jserror',
+                                   'application/x-www-form-urlencoded');
+  request.onloadend = function(event) {
+    console.log('sendErrorReport status: ' + request.status);
+  };
+  request.send(requestParameters);
+}
+
+// Limiting 1 error report per background page load.
+var errorReported = false;
+
+/**
+ * Reports an error to the server and the user, as appropriate.
+ * @param {Error} error Error to report.
+ */
+function reportError(error) {
+  var message = 'Critical error:\n' + error.stack;
+  console.error(message);
+  if (!errorReported) {
+    errorReported = true;
+    chrome.metricsPrivate.getIsCrashReportingEnabled(function(isEnabled) {
+      if (isEnabled)
+        sendErrorReport(error);
+      if (DEBUG_MODE)
+        alert(message);
+    });
+  }
+}
+
 // Partial mirror of chrome.* for all instrumented functions.
 var instrumented = {};
 
 /**
+ * Wrapper plugin. These plugins extend instrumentation added by
+ * wrapper.wrapCallback by adding code that executes before and after the call
+ * to the original callback provided by the extension.
+ *
+ * @typedef {{
+ *   prologue: function (),
+ *   epilogue: function ()
+ * }}
+ */
+var WrapperPlugin;
+
+/**
+ * Wrapper for callbacks. Used to add error handling and other services to
+ * callbacks for HTML and Chrome functions and events.
+ */
+var wrapper = (function() {
+  /**
+   * Factory for wrapper plugins. If specified, it's used to generate an
+   * instance of WrapperPlugin each time we wrap a callback (which corresponds
+   * to addListener call for Chrome events, and to every API call that specifies
+   * a callback). WrapperPlugin's lifetime ends when the callback for which it
+   * was generated, exits. It's possible to have several instances of
+   * WrapperPlugin at the same time.
+   * An instance of WrapperPlugin can have state that can be shared by its
+   * constructor, prologue() and epilogue(). Also WrapperPlugins can change
+   * state of other objects, for example, to do refcounting.
+   * @type {function(): WrapperPlugin}
+   */
+  var wrapperPluginFactory = null;
+
+  /**
+   * Registers a wrapper plugin factory.
+   * @param {function(): WrapperPlugin} factory Wrapper plugin factory.
+   */
+  function registerWrapperPluginFactory(factory) {
+    if (wrapperPluginFactory) {
+      reportError(buildErrorWithMessageForServer(
+          'registerWrapperPluginFactory: factory is already registered.'));
+    }
+
+    wrapperPluginFactory = factory;
+  }
+
+  /**
+   * True if currently executed code runs in a callback or event handler that
+   * was instrumented by wrapper.wrapCallback() call.
+   * @type {boolean}
+   */
+  var isInWrappedCallback = false;
+
+  /**
+   * Required callbacks that are not yet called. Includes both task and non-task
+   * callbacks. This is a map from unique callback id to the stack at the moment
+   * when the callback was wrapped. This stack identifies the callback.
+   * Used only for diagnostics.
+   * @type {Object.<number, string>}
+   */
+  var pendingCallbacks = {};
+
+  /**
+   * Unique ID of the next callback.
+   * @type {number}
+   */
+  var nextCallbackId = 0;
+
+  /**
+   * Gets diagnostic string with the status of the wrapper.
+   * @return {string} Diagnostic string.
+   */
+  function debugGetStateString() {
+    return 'pendingCallbacks = ' + JSON.stringify(pendingCallbacks);
+  }
+
+  /**
+   * Checks that we run in a wrapped callback.
+   */
+  function checkInWrappedCallback() {
+    if (!isInWrappedCallback) {
+      reportError(buildErrorWithMessageForServer(
+          'Not in instrumented callback'));
+    }
+  }
+
+  /**
+   * Adds error processing to an API callback.
+   * @param {Function} callback Callback to instrument.
+   * @param {boolean=} opt_isEventListener True if the callback is a listener to
+   *     a Chrome API event.
+   * @return {Function} Instrumented callback.
+   */
+  function wrapCallback(callback, opt_isEventListener) {
+    var callbackId = nextCallbackId++;
+
+    if (!opt_isEventListener) {
+      checkInWrappedCallback();
+      pendingCallbacks[callbackId] = new Error().stack;
+    }
+
+    // wrapperPluginFactory may be null before task manager is built, and in
+    // tests.
+    var wrapperPluginInstance = wrapperPluginFactory && wrapperPluginFactory();
+
+    return function() {
+      // This is the wrapper for the callback.
+      try {
+        verify(!isInWrappedCallback, 'Re-entering instrumented callback');
+        isInWrappedCallback = true;
+
+        if (!opt_isEventListener)
+          delete pendingCallbacks[callbackId];
+
+        if (wrapperPluginInstance)
+          wrapperPluginInstance.prologue();
+
+        // Call the original callback.
+        callback.apply(null, arguments);
+
+        if (wrapperPluginInstance)
+          wrapperPluginInstance.epilogue();
+
+        verify(isInWrappedCallback,
+               'Instrumented callback is not instrumented upon exit');
+        isInWrappedCallback = false;
+      } catch (error) {
+        reportError(error);
+      }
+    };
+  }
+
+  /**
+   * Returns an instrumented function.
+   * @param {!Array.<string>} functionIdentifierParts Path to the chrome.*
+   *     function.
+   * @param {string} functionName Name of the chrome API function.
+   * @param {number} callbackParameter Index of the callback parameter to this
+   *     API function.
+   * @return {Function} An instrumented function.
+   */
+  function createInstrumentedFunction(
+      functionIdentifierParts,
+      functionName,
+      callbackParameter) {
+    return function() {
+      // This is the wrapper for the API function. Pass the wrapped callback to
+      // the original function.
+      var callback = arguments[callbackParameter];
+      if (typeof callback != 'function') {
+        reportError(buildErrorWithMessageForServer(
+            'Argument ' + callbackParameter + ' of ' +
+            functionIdentifierParts.join('.') + '.' + functionName +
+            ' is not a function'));
+      }
+      arguments[callbackParameter] = wrapCallback(
+          callback, functionName == 'addListener');
+
+      var chromeContainer = chrome;
+      functionIdentifierParts.forEach(function(fragment) {
+        chromeContainer = chromeContainer[fragment];
+      });
+      return chromeContainer[functionName].
+          apply(chromeContainer, arguments);
+    };
+  }
+
+  /**
+   * Instruments an API function to add error processing to its user
+   * code-provided callback.
+   * @param {string} functionIdentifier Full identifier of the function without
+   *     the 'chrome.' portion.
+   * @param {number} callbackParameter Index of the callback parameter to this
+   *     API function.
+   */
+  function instrumentChromeApiFunction(functionIdentifier, callbackParameter) {
+    var functionIdentifierParts = functionIdentifier.split('.');
+    var functionName = functionIdentifierParts.pop();
+    var chromeContainer = chrome;
+    var instrumentedContainer = instrumented;
+    functionIdentifierParts.forEach(function(fragment) {
+      chromeContainer = chromeContainer[fragment];
+      if (!chromeContainer) {
+        reportError(buildErrorWithMessageForServer(
+            'Cannot instrument ' + functionIdentifier));
+      }
+
+      if (!(fragment in instrumentedContainer))
+        instrumentedContainer[fragment] = {};
+
+      instrumentedContainer = instrumentedContainer[fragment];
+    });
+
+    var targetFunction = chromeContainer[functionName];
+    if (!targetFunction) {
+      reportError(buildErrorWithMessageForServer(
+          'Cannot instrument ' + functionIdentifier));
+    }
+
+    instrumentedContainer[functionName] = createInstrumentedFunction(
+        functionIdentifierParts,
+        functionName,
+        callbackParameter);
+  }
+
+  instrumentChromeApiFunction('runtime.onSuspend.addListener', 0);
+
+  instrumented.runtime.onSuspend.addListener(function() {
+    var stringifiedPendingCallbacks = JSON.stringify(pendingCallbacks);
+    verify(
+        stringifiedPendingCallbacks == '{}',
+        'Pending callbacks when unloading event page:' +
+        stringifiedPendingCallbacks);
+  });
+
+  return {
+    wrapCallback: wrapCallback,
+    instrumentChromeApiFunction: instrumentChromeApiFunction,
+    registerWrapperPluginFactory: registerWrapperPluginFactory,
+    checkInWrappedCallback: checkInWrappedCallback,
+    debugGetStateString: debugGetStateString
+  };
+})();
+
+wrapper.instrumentChromeApiFunction('alarms.get', 1);
+wrapper.instrumentChromeApiFunction('alarms.onAlarm.addListener', 0);
+wrapper.instrumentChromeApiFunction('identity.getAuthToken', 1);
+wrapper.instrumentChromeApiFunction('identity.removeCachedAuthToken', 1);
+
+/**
  * Builds the object to manage tasks (mutually exclusive chains of events).
  * @param {function(string, string): boolean} areConflicting Function that
  *     checks if a new task can't be added to a task queue that contains an
@@ -83,37 +400,12 @@
   var taskPendingCallbackCount = 0;
 
   /**
-   * Required callbacks that are not yet called. Includes both task and non-task
-   * callbacks. This is a map from unique callback id to the stack at the moment
-   * when the callback was wrapped. This stack identifies the callback.
-   * Used only for diagnostics.
-   * @type {Object.<number, string>}
-   */
-  var pendingCallbacks = {};
-
-  /**
    * True if currently executed code is a part of a task.
    * @type {boolean}
    */
   var isInTask = false;
 
   /**
-   * True if currently executed code runs in an instrumented callback.
-   * @type {boolean}
-   */
-  var isInInstrumentedCallback = false;
-
-  /**
-   * Checks that we run in an instrumented callback.
-   */
-  function checkInInstrumentedCallback() {
-    if (!isInInstrumentedCallback) {
-      reportError(buildErrorWithMessageForServer(
-          'Not in instrumented callback'));
-    }
-  }
-
-  /**
    * Starts the first queued task.
    */
   function startFirst() {
@@ -126,8 +418,8 @@
         taskPendingCallbackCount == 0,
         'tasks.startFirst: still have pending task callbacks: ' +
         taskPendingCallbackCount +
-        ', queue = ' + JSON.stringify(queue) +
-        ', pendingCallbacks = ' + JSON.stringify(pendingCallbacks));
+        ', queue = ' + JSON.stringify(queue) + ', ' +
+        wrapper.debugGetStateString());
     var entry = queue[0];
     console.log('Starting task ' + entry.name);
 
@@ -165,7 +457,7 @@
    *     parameter. Call this callback on completion.
    */
   function add(taskName, task) {
-    checkInInstrumentedCallback();
+    wrapper.checkInWrappedCallback();
     console.log('Adding task ' + taskName);
     if (!canQueue(taskName))
       return;
@@ -190,230 +482,56 @@
       startFirst();
   }
 
-  // Limiting 1 error report per background page load.
-  var errorReported = false;
-
-  /**
-   * Sends an error report to the server.
-   * @param {Error} error Error to send.
-   */
-  function sendErrorReport(error) {
-    var filteredStack = error.canSendMessageToServer ?
-        error.stack : error.stack.replace(/.*\n/, '(message removed)\n');
-    var file;
-    var line;
-    var topFrameLineMatch = filteredStack.match(/\n    at .*\n/);
-    var topFrame = topFrameLineMatch && topFrameLineMatch[0];
-    if (topFrame) {
-      // Examples of a frame:
-      // 1. '\n    at someFunction (chrome-extension://
-      //     pmofbkohncoogjjhahejjfbppikbjigm/background.js:915:15)\n'
-      // 2. '\n    at chrome-extension://pmofbkohncoogjjhahejjfbppikbjigm/
-      //     utility.js:269:18\n'
-      // 3. '\n    at Function.target.(anonymous function) (extensions::
-      //     SafeBuiltins:19:14)\n'
-      // 4. '\n    at Event.dispatchToListener (event_bindings:382:22)\n'
-      var errorLocation;
-      // Find the the parentheses at the end of the line, if any.
-      var parenthesesMatch = topFrame.match(/\(.*\)\n/);
-      if (parenthesesMatch && parenthesesMatch[0]) {
-        errorLocation =
-            parenthesesMatch[0].substring(1, parenthesesMatch[0].length - 2);
-      } else {
-        errorLocation = topFrame;
-      }
-
-      var topFrameElements = errorLocation.split(':');
-      // topFrameElements is an array that ends like:
-      // [N-3] //pmofbkohncoogjjhahejjfbppikbjigm/utility.js
-      // [N-2] 308
-      // [N-1] 19
-      if (topFrameElements.length >= 3) {
-        file = topFrameElements[topFrameElements.length - 3];
-        line = topFrameElements[topFrameElements.length - 2];
-      }
-    }
-
-    var requestParameters =
-        'error=' + encodeURIComponent(error.name) +
-        '&script=' + encodeURIComponent(file) +
-        '&line=' + encodeURIComponent(line) +
-        '&trace=' + encodeURIComponent(filteredStack);
-    var request = buildServerRequest('jserror',
-                                     'application/x-www-form-urlencoded');
-    request.onloadend = function(event) {
-      console.log('sendErrorReport status: ' + request.status);
-    };
-    request.send(requestParameters);
-  }
-
-  /**
-   * Reports an error to the server and the user, as appropriate.
-   * @param {Error} error Error to report.
-   */
-  function reportError(error) {
-    var message = 'Critical error:\n' + error.stack;
-    console.error(message);
-    if (!errorReported) {
-      errorReported = true;
-      chrome.metricsPrivate.getIsCrashReportingEnabled(function(isEnabled) {
-        if (isEnabled)
-          sendErrorReport(error);
-        if (DEBUG_MODE)
-          alert(message);
-      });
-    }
-  }
-
-  /**
-   * Unique ID of the next callback.
-   * @type {number}
-   */
-  var nextCallbackId = 0;
-
-  /**
-   * Adds error processing to an API callback.
-   * @param {Function} callback Callback to instrument.
-   * @param {boolean=} opt_isEventListener True if the callback is an event
-   *      listener.
-   * @return {Function} Instrumented callback.
-   */
-  function wrapCallback(callback, opt_isEventListener) {
-    verify(!(opt_isEventListener && isInTask),
-           'Unrequired callback in a task.');
-    var callbackId = nextCallbackId++;
-    var isTaskCallback = isInTask;
-    if (isTaskCallback)
-      ++taskPendingCallbackCount;
-    if (!opt_isEventListener) {
-      checkInInstrumentedCallback();
-      pendingCallbacks[callbackId] = new Error().stack;
-    }
-
-    return function() {
-      // This is the wrapper for the callback.
-      try {
-        verify(!isInInstrumentedCallback, 'Re-entering instrumented callback');
-        isInInstrumentedCallback = true;
-
-        if (isTaskCallback) {
-          verify(!isInTask, 'wrapCallback: already in task');
-          isInTask = true;
-        }
-        if (!opt_isEventListener)
-          delete pendingCallbacks[callbackId];
-
-        // Call the original callback.
-        callback.apply(null, arguments);
-
-        if (isTaskCallback) {
-          verify(isInTask, 'wrapCallback: not in task at exit');
-          isInTask = false;
-          if (--taskPendingCallbackCount == 0)
-            finish();
-        }
-
-        verify(isInInstrumentedCallback,
-               'Instrumented callback is not instrumented upon exit');
-        isInInstrumentedCallback = false;
-      } catch (error) {
-        reportError(error);
-      }
-    };
-  }
-
-  /**
-   * Returns an instrumented function.
-   * @param {array} functionIdentifierParts Path to the chrome.* function.
-   * @param {string} functionName Name of the chrome API function.
-   * @param {number} callbackParameter Index of the callback parameter to this
-   *     API function.
-   * @return {function} An instrumented function.
-   */
-  function createInstrumentedFunction(
-      functionIdentifierParts,
-      functionName,
-      callbackParameter) {
-    return function() {
-      // This is the wrapper for the API function. Pass the wrapped callback to
-      // the original function.
-      var callback = arguments[callbackParameter];
-      if (typeof callback != 'function') {
-        reportError(buildErrorWithMessageForServer(
-            'Argument ' + callbackParameter + ' of ' +
-            functionIdentifierParts.join('.') + '.' + functionName +
-            ' is not a function'));
-      }
-      arguments[callbackParameter] = wrapCallback(
-          callback, functionName == 'addListener');
-
-      var chromeContainer = chrome;
-      functionIdentifierParts.map(function(fragment) {
-        chromeContainer = chromeContainer[fragment];
-      });
-      return chromeContainer[functionName].
-          apply(chromeContainer, arguments);
-    };
-  }
-
-  /**
-   * Instruments an API function to add error processing to its user
-   * code-provided callback.
-   * @param {string} functionIdentifier Full identifier of the function without
-   *     the 'chrome.' portion.
-   * @param {number} callbackParameter Index of the callback parameter to this
-   *     API function.
-   */
-  function instrumentChromeApiFunction(functionIdentifier, callbackParameter) {
-    var functionIdentifierParts = functionIdentifier.split('.');
-    var functionName = functionIdentifierParts.pop();
-    var chromeContainer = chrome;
-    var instrumentedContainer = instrumented;
-    functionIdentifierParts.map(function(fragment) {
-      chromeContainer = chromeContainer[fragment];
-      if (!chromeContainer) {
-        reportError(buildErrorWithMessageForServer(
-            'Cannot instrument ' + functionIdentifier));
-      }
-
-      if (!(fragment in instrumentedContainer))
-        instrumentedContainer[fragment] = {};
-
-      instrumentedContainer = instrumentedContainer[fragment];
-    });
-
-    var targetFunction = chromeContainer[functionName];
-    if (!targetFunction) {
-      reportError(buildErrorWithMessageForServer(
-          'Cannot instrument ' + functionIdentifier));
-    }
-
-    instrumentedContainer[functionName] = createInstrumentedFunction(
-        functionIdentifierParts,
-        functionName,
-        callbackParameter);
-  }
-
-  instrumentChromeApiFunction('alarms.get', 1);
-  instrumentChromeApiFunction('alarms.onAlarm.addListener', 0);
-  instrumentChromeApiFunction('identity.getAuthToken', 1);
-  instrumentChromeApiFunction('identity.removeCachedAuthToken', 1);
-  instrumentChromeApiFunction('runtime.onSuspend.addListener', 0);
-
-  chrome.runtime.onSuspend.addListener(function() {
-    var stringifiedPendingCallbacks = JSON.stringify(pendingCallbacks);
+  instrumented.runtime.onSuspend.addListener(function() {
     verify(
-        queue.length == 0 && stringifiedPendingCallbacks == '{}',
-        'Incomplete task or pending callbacks when unloading event page,' +
-        ' queue = ' + JSON.stringify(queue) +
-        ', pendingCallbacks = ' + stringifiedPendingCallbacks);
+        queue.length == 0,
+        'Incomplete task when unloading event page,' +
+        ' queue = ' + JSON.stringify(queue) + ', ' +
+        wrapper.debugGetStateString());
+  });
+
+
+  /**
+   * Wrapper plugin for tasks.
+   * @constructor
+   */
+  function TasksWrapperPlugin() {
+    this.isTaskCallback = isInTask;
+    if (this.isTaskCallback)
+      ++taskPendingCallbackCount;
+  }
+
+  TasksWrapperPlugin.prototype = {
+    /**
+     * Plugin code to be executed before invoking the original callback.
+     */
+    prologue: function() {
+      if (this.isTaskCallback) {
+        verify(!isInTask, 'TasksWrapperPlugin.prologue: already in task');
+        isInTask = true;
+      }
+    },
+
+    /**
+     * Plugin code to be executed after invoking the original callback.
+     */
+    epilogue: function() {
+      if (this.isTaskCallback) {
+        verify(isInTask, 'TasksWrapperPlugin.epilogue: not in task at exit');
+        isInTask = false;
+        if (--taskPendingCallbackCount == 0)
+          finish();
+      }
+    }
+  };
+
+  wrapper.registerWrapperPluginFactory(function() {
+    return new TasksWrapperPlugin();
   });
 
   return {
     add: add,
-    debugSetStepName: function() {},  // TODO(vadimt): remove
-    instrumentChromeApiFunction: instrumentChromeApiFunction,
-    wrapCallback: wrapCallback
+    debugSetStepName: function() {}  // TODO(vadimt): remove
   };
 }
 
@@ -581,19 +699,24 @@
     listeners.push(callback);
   }
 
-  // Tracks the last answer of isSignedIn. checkAndNotifyListeners will not
-  // notify the listeners if this is null because technically, no sign in
-  // state change occurred.
-  var lastReturnedSignedInState = null;
-
+  /**
+   * Checks if the last signed in state matches the specified one.
+   * If it doesn't, it notifies the listeners of the change.
+   * @param {boolean} currentSignedInState The current signed in state.
+   */
   function checkAndNotifyListeners(currentSignedInState) {
-    if ((lastReturnedSignedInState !== currentSignedInState) &&
-        (lastReturnedSignedInState !== null)) {
-      for (var listenerIndex in listeners) {
-        listeners[listenerIndex]();
+    instrumented.storage.local.get('lastSignedInState', function(items) {
+      items = items || {};
+      if (items.lastSignedInState != currentSignedInState) {
+        chrome.storage.local.set(
+            {lastSignedInState: currentSignedInState});
+        if (items.lastSignedInState != undefined) {
+          listeners.forEach(function(callback) {
+            callback();
+          });
+        }
       }
-    }
-    lastReturnedSignedInState = currentSignedInState;
+    });
   }
 
   instrumented.alarms.onAlarm.addListener(function(alarm) {
diff --git a/chrome/browser/resources/history/history.html b/chrome/browser/resources/history/history.html
index a58e433..bc6d787 100644
--- a/chrome/browser/resources/history/history.html
+++ b/chrome/browser/resources/history/history.html
@@ -69,72 +69,74 @@
 </if>
 
 <div id="history-page" class="page">
-  <header>
-    <h1 i18n-content="history"></h1>
-    <div id="search-form" class="search-field-container">
-      <input type="search" id="search-field" required>
-      <input type="submit" id="search-button" i18n-values="value:searchButton">
-    </div>
-    <div id="filter-controls" hidden>
-      <button id="range-today" i18n-content="rangeToday" disabled></button>
-      <button id="range-previous" i18n-values="alt:rangePrevious" disabled>
-      </button>
-      <button id="range-next" i18n-values="alt:rangeNext" disabled>
-      </button>
-      <div id="display-filter-controls">
-        <div class="display-filter-button">
-          <label for="timeframe-filter-all">
-            <input id="timeframe-filter-all" type="radio"
-                name="timeframe-filter" value="0" checked>
-            <span i18n-content="rangeAllTime" class="first-button-component">
-            </span>
-          </label>
-        </div>
-        <div class="display-filter-button">
-          <label for="timeframe-filter-week">
-            <input id="timeframe-filter-week" type="radio"
-                name="timeframe-filter" value="1">
-            <span i18n-content="rangeWeek"></span>
-          </label>
-        </div>
-        <div class="display-filter-button">
-          <label for="timeframe-filter-month">
-            <input id="timeframe-filter-month" type="radio"
-                name="timeframe-filter" value="2">
-            <span i18n-content="rangeMonth" class="last-button-component">
-            </span>
-          </label>
+  <div id="scrolling-container">
+    <header>
+      <h1 i18n-content="history"></h1>
+      <div id="search-form" class="search-field-container">
+        <input type="search" id="search-field" required>
+        <input type="submit" id="search-button" i18n-values="value:searchButton">
+      </div>
+      <div id="filter-controls" hidden>
+        <button id="range-today" i18n-content="rangeToday" disabled></button>
+        <button id="range-previous" i18n-values="alt:rangePrevious" disabled>
+        </button>
+        <button id="range-next" i18n-values="alt:rangeNext" disabled>
+        </button>
+        <div id="display-filter-controls">
+          <div class="display-filter-button">
+            <label for="timeframe-filter-all">
+              <input id="timeframe-filter-all" type="radio"
+                  name="timeframe-filter" value="0" checked>
+              <span i18n-content="rangeAllTime" class="first-button-component">
+              </span>
+            </label>
+          </div>
+          <div class="display-filter-button">
+            <label for="timeframe-filter-week">
+              <input id="timeframe-filter-week" type="radio"
+                  name="timeframe-filter" value="1">
+              <span i18n-content="rangeWeek"></span>
+            </label>
+          </div>
+          <div class="display-filter-button">
+            <label for="timeframe-filter-month">
+              <input id="timeframe-filter-month" type="radio"
+                  name="timeframe-filter" value="2">
+              <span i18n-content="rangeMonth" class="last-button-component">
+              </span>
+            </label>
+          </div>
         </div>
       </div>
-    </div>
-  </header>
+    </header>
 <if expr="not is_android">
-  <div id="other-devices" class="other-devices"></div>
+    <div id="other-devices" class="other-devices"></div>
 </if>
-  <div id="top-container">
-    <div id="editing-controls">
-      <button id="clear-browsing-data" i18n-content="clearAllHistory"></button>
-      <button id="remove-selected" disabled="disabled"
-          i18n-content="removeSelected"></button>
+    <div id="top-container">
+      <div id="editing-controls">
+        <button id="clear-browsing-data" i18n-content="clearAllHistory"></button>
+        <button id="remove-selected" disabled="disabled"
+            i18n-content="removeSelected"></button>
+      </div>
+      <div id="notification-bar" hidden></div>
     </div>
-    <div id="notification-bar" hidden></div>
-  </div>
 
-  <div id="results-display"></div>
-  <div id="loading-spinner" hidden>
-    <span id="loading">
-      <div id="spinner" class="inline-spinner"></div>
-      <span i18n-content="loading"></span>
-    </span>
-  </div>
-  <div id="results-pagination">
-    <button id="newest-button" class="link-button" i18n-content="newest"
-        hidden>
-    </button>
-    <button id="newer-button" class="link-button" i18n-content="newer" hidden>
-    </button>
-    <button id="older-button" class="link-button" i18n-content="older" hidden>
-    </button>
+    <div id="results-display"></div>
+    <div id="loading-spinner" hidden>
+      <span id="loading">
+        <div id="spinner" class="inline-spinner"></div>
+        <span i18n-content="loading"></span>
+      </span>
+    </div>
+    <div id="results-pagination">
+      <button id="newest-button" class="link-button" i18n-content="newest"
+          hidden>
+      </button>
+      <button id="newer-button" class="link-button" i18n-content="newer" hidden>
+      </button>
+      <button id="older-button" class="link-button" i18n-content="older" hidden>
+      </button>
+    </div>
   </div>
 </div>
 
diff --git a/chrome/browser/resources/history/history_mobile.css b/chrome/browser/resources/history/history_mobile.css
index bb5b82e..bb67407 100644
--- a/chrome/browser/resources/history/history_mobile.css
+++ b/chrome/browser/resources/history/history_mobile.css
@@ -4,12 +4,27 @@
 
 /* This file contains styles specific to Android and iOS. */
 
+html {
+  height: 100%;
+}
+
 body {
   color: rgb(76, 76, 76);
+  height: 100%;
   margin: 0;
   margin-bottom: 46px;
 }
 
+.page {
+  height: 100%;
+}
+
+#scrolling-container {
+  -webkit-overflow-scrolling: touch;
+  height: -webkit-calc(100% - 46px);
+  overflow-y: auto;
+}
+
 h1 {
   font-weight: bold;
   margin-bottom: 12px;
diff --git a/chrome/browser/resources/inspect/inspect.css b/chrome/browser/resources/inspect/inspect.css
index e2d3ab2..9167ba2 100644
--- a/chrome/browser/resources/inspect/inspect.css
+++ b/chrome/browser/resources/inspect/inspect.css
@@ -14,7 +14,6 @@
 }
 
 img {
-  float: left;
   height: 16px;
   padding-left: 2px;
   padding-right: 5px;
@@ -127,17 +126,32 @@
 }
 
 .browser-header {
+  -webkit-box-align: center;
+  -webkit-box-orient: horizontal;
+  display: -webkit-box;
+  padding-top: 10px;
+}
+
+.browser-header > .browser-name {
+  font-size: 110%;
   font-weight: bold;
-  margin: 5px 0 0;
-  padding: 2px 0;
 }
 
 .row {
-  padding: 5px 0;
+  padding: 6px 0;
 }
 
 .subrow {
-  padding: 5px 24px;
+  -webkit-box-orient: horizontal;
+  display: -webkit-box;
+}
+
+.subrow > div {
+  margin-right: 0.5em;
+}
+
+.subrow.webview {
+  margin-top: -2px;
 }
 
 .invisible-view {
@@ -149,26 +163,29 @@
 }
 
 .list {
-  margin-bottom: 20px;
   margin-top: 5px;
 }
 
 a {
   color: rgb(17, 85, 204);
-  padding: 0 3px;
+  margin-right: 6px;
   text-decoration: none;
 }
 
+.list.pages .subrow + a,
+.subrow.webview > div:first-child {
+  margin-left: 24px;  /* Align with the favicon */
+}
+
+.list:not(.pages) .subrow {
+  height: 19px;
+}
+
 a.disabled {
   opacity: 0.5;
   pointer-events: none;
 }
 
-.open {
-  margin-bottom: -4px;
-  margin-top: 5px;
-}
-
 .open > input {
   border: 1px solid #aaa;
   height: 17px;
@@ -177,6 +194,12 @@
   padding: 0 2px;
 }
 
+.open > input:focus {
+  -webkit-transition: border-color 200ms;
+  border-color: rgb(77, 144, 254);
+  outline: none;
+}
+
 .open > button {
   line-height: 13px;
 }
@@ -187,6 +210,11 @@
   top: 15px;
 }
 
+#port-forwarding-settings input {
+  position: relative;
+  top: 2px;
+}
+
 #port-forwarding-overlay {
   -webkit-box-align: center;
   -webkit-box-pack: center;
diff --git a/chrome/browser/resources/inspect/inspect.html b/chrome/browser/resources/inspect/inspect.html
index 861fcdf..55d1116 100644
--- a/chrome/browser/resources/inspect/inspect.html
+++ b/chrome/browser/resources/inspect/inspect.html
@@ -21,7 +21,7 @@
     <div id="caption">DevTools</div>
   </div>
   <div id="content">
-    <div id="devices-tab">
+    <div id="devices">
       <div class="content-header">Devices</div>
       <div id="port-forwarding-settings">
         <label for="port-forwarding-enable">
@@ -31,27 +31,27 @@
         <button id="port-forwarding-config-open" disabled>
           Configure port forwarding</button>
       </div>
-      <div id="devices"></div>
+      <div id="devices-list"></div>
     </div>
-    <div id="pages-tab">
+    <div id="pages">
       <div class="content-header">Pages</div>
-      <div id="pages" class="list"></div>
+      <div id="pages-list" class="list pages"></div>
     </div>
-    <div id="extensions-tab">
+    <div id="extensions">
       <div class="content-header">Extensions</div>
-      <div id="extensions" class="list"></div>
+      <div id="extensions-list" class="list"></div>
     </div>
-    <div id="apps-tab">
+    <div id="apps">
       <div class="content-header">Apps</div>
-      <div id="apps" class="list"></div>
+      <div id="apps-list" class="list"></div>
     </div>
-    <div id="workers-tab">
+    <div id="workers">
       <div class="content-header">Shared workers</div>
-      <div id="workers" class="list"></div>
+      <div id="workers-list" class="list"></div>
     </div>
-    <div id="other-tab">
+    <div id="other">
       <div class="content-header">Other</div>
-      <div id="others" class="list"></div>
+      <div id="others-list" class="list"></div>
     </div>
   </div>
 </div>
diff --git a/chrome/browser/resources/inspect/inspect.js b/chrome/browser/resources/inspect/inspect.js
index a066005..7663ed6 100644
--- a/chrome/browser/resources/inspect/inspect.js
+++ b/chrome/browser/resources/inspect/inspect.js
@@ -2,10 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+var MIN_VERSION_TAB_CLOSE = 25;
+var MIN_VERSION_TARGET_ID = 26;
+var MIN_VERSION_NEW_TAB = 29;
+var MIN_VERSION_TAB_ACTIVATE = 30;
+
 function inspect(data) {
   chrome.send('inspect', [data]);
 }
 
+function activate(data) {
+  chrome.send('activate', [data]);
+}
+
 function terminate(data) {
   chrome.send('terminate', [data]);
 }
@@ -38,7 +47,7 @@
     $('navigation').appendChild(tabHeader);
   }
   var selectedTabName = window.location.hash.slice(1) || 'devices';
-  selectTab(selectedTabName + '-tab');
+  selectTab(selectedTabName);
   initPortForwarding();
   chrome.send('init-ui');
 }
@@ -57,13 +66,14 @@
       tabHeader.classList.remove('selected');
     }
   }
+  window.location.hash = id;
 }
 
 function populateLists(data) {
-  removeChildren('pages');
-  removeChildren('extensions');
-  removeChildren('apps');
-  removeChildren('others');
+  removeChildren('pages-list');
+  removeChildren('extensions-list');
+  removeChildren('apps-list');
+  removeChildren('others-list');
 
   for (var i = 0; i < data.length; i++) {
     if (data[i].type === 'page')
@@ -78,7 +88,7 @@
 }
 
 function populateWorkersList(data) {
-  removeChildren('workers');
+  removeChildren('workers-list');
 
   for (var i = 0; i < data.length; i++)
     addToWorkersList(data[i]);
@@ -108,7 +118,7 @@
     parent.appendChild(child);
   }
 
-  var deviceList = $('devices');
+  var deviceList = $('devices-list');
   if (alreadyDisplayed(deviceList, devices))
     return;
 
@@ -134,7 +144,7 @@
     } else {
       deviceSection = document.createElement('div');
       deviceSection.id = device.adbGlobalId;
-      deviceSection.className = 'device list';
+      deviceSection.className = 'device';
       deviceList.appendChild(deviceSection);
 
       var deviceHeader = document.createElement('div');
@@ -200,6 +210,13 @@
       var isChrome = browser.adbBrowserProduct &&
           browser.adbBrowserProduct.match(/^Chrome/);
 
+      var majorChromeVersion = 0;
+      if (isChrome && browser.adbBrowserVersion) {
+        var match = browser.adbBrowserVersion.match(/^(\d+)/);
+        if (match)
+          majorChromeVersion = parseInt(match[1]);
+      }
+
       var pageList;
       var browserSection = $(browser.adbGlobalId);
       if (browserSection) {
@@ -212,22 +229,19 @@
 
         var browserHeader = document.createElement('div');
         browserHeader.className = 'browser-header';
+
+        var browserName = document.createElement('div');
+        browserName.className = 'browser-name';
+        browserHeader.appendChild(browserName);
         if (browser.adbBrowserPackage && !isChrome)
-          browserHeader.textContent = browser.adbBrowserPackage;
+          browserName.textContent = browser.adbBrowserPackage;
         else
-          browserHeader.textContent = browser.adbBrowserProduct;
-        var majorChromeVersion = 0;
-        if (browser.adbBrowserVersion) {
-          browserHeader.textContent += ' (' + browser.adbBrowserVersion + ')';
-          if (isChrome) {
-            var match = browser.adbBrowserVersion.match(/^(\d+)/);
-            if (match)
-              majorChromeVersion = parseInt(match[1]);
-          }
-        }
+          browserName.textContent = browser.adbBrowserProduct;
+        if (browser.adbBrowserVersion)
+          browserName.textContent += ' (' + browser.adbBrowserVersion + ')';
         browserSection.appendChild(browserHeader);
 
-        if (majorChromeVersion >= 29) {
+        if (majorChromeVersion >= MIN_VERSION_NEW_TAB) {
           var newPage = document.createElement('div');
           newPage.className = 'open';
 
@@ -250,7 +264,7 @@
           newPage.appendChild(newPageButton);
           newPageButton.addEventListener('click', openHandler, true);
 
-          browserSection.appendChild(newPage);
+          browserHeader.appendChild(newPage);
         }
 
         pageList = document.createElement('div');
@@ -264,13 +278,27 @@
       pageList.textContent = '';
       for (var p = 0; p < browser.pages.length; p++) {
         var page = browser.pages[p];
+        // Attached targets have no unique id until Chrome 26.
+        // For such targets it is impossible:
+        //  - to issue 'close' command,
+        //  - to activate existing DevTools window.
+        page.hasUniqueId = !page.attached ||
+            majorChromeVersion >= MIN_VERSION_TARGET_ID;
         var row = addTargetToList(
             page, pageList, ['faviconUrl', 'name', 'url', 'description']);
         if (isChrome) {
+          if (majorChromeVersion >= MIN_VERSION_TAB_ACTIVATE) {
+            row.appendChild(createActionLink(
+                'activate', activate.bind(null, page), false));
+          }
           row.appendChild(createActionLink(
               'reload', reload.bind(null, page), page.attached));
-          row.appendChild(createActionLink(
-              'close', terminate.bind(null, page), page.attached));
+          if (majorChromeVersion >= MIN_VERSION_TAB_CLOSE) {
+            row.appendChild(createActionLink(
+                'close',
+                terminate.bind(null, page),
+                page.attached || !page.hasUniqueId));
+          }
         }
       }
     }
@@ -278,25 +306,25 @@
 }
 
 function addToPagesList(data) {
-  addTargetToList(data, $('pages'), ['faviconUrl', 'name', 'url']);
+  addTargetToList(data, $('pages-list'), ['faviconUrl', 'name', 'url']);
 }
 
 function addToExtensionsList(data) {
-  addTargetToList(data, $('extensions'), ['name', 'url']);
+  addTargetToList(data, $('extensions-list'), ['name', 'url']);
 }
 
 function addToAppsList(data) {
-  addTargetToList(data, $('apps'), ['name', 'url']);
+  addTargetToList(data, $('apps-list'), ['name', 'url']);
 }
 
 function addToWorkersList(data) {
-  var row = addTargetToList(data, $('workers'), ['name', 'url', 'pid']);
+  var row = addTargetToList(data, $('workers-list'), ['pid', 'url']);
   row.appendChild(createActionLink(
       'terminate', terminate.bind(null, data), data.attached));
 }
 
 function addToOthersList(data) {
-  addTargetToList(data, $('others'), ['url']);
+  addTargetToList(data, $('others-list'), ['url']);
 }
 
 function formatValue(data, property) {
@@ -320,8 +348,8 @@
   if (property == 'pid')
     text = 'Pid:' + text;
 
-  var span = document.createElement('span');
-  span.textContent = ' ' + text + ' ';
+  var span = document.createElement('div');
+  span.textContent = text;
   span.className = property;
   return span;
 }
@@ -341,7 +369,7 @@
   }
 
   var row = document.createElement('div');
-  row.className = 'subrow';
+  row.className = 'subrow webview';
   if (webview.empty || !webview.attached || !webview.visible)
     row.className += ' invisible-view';
   row.appendChild(formatValue(viewStatus, 'visibility'));
@@ -354,10 +382,15 @@
 function addTargetToList(data, list, properties) {
   var row = document.createElement('div');
   row.className = 'row';
+
+  var subrow = document.createElement('div');
+  subrow.className = 'subrow';
+  row.appendChild(subrow);
+
   var description = null;
   for (var j = 0; j < properties.length; j++) {
     if (properties[j] != 'description')
-      row.appendChild(formatValue(data, properties[j]));
+      subrow.appendChild(formatValue(data, properties[j]));
     else if (data['description']) {
       try {
         description = JSON.parse(data['description']);
@@ -365,13 +398,11 @@
     }
   }
 
-  row.appendChild(createActionLink('inspect', inspect.bind(null, data)));
-
   if (description)
     addWebViewDescription(description, row);
 
-  row.processId = data.processId;
-  row.routeId = data.routeId;
+  row.appendChild(createActionLink(
+      'inspect', inspect.bind(null, data), !data.hasUniqueId));
 
   list.appendChild(row);
   return row;
diff --git a/chrome/browser/resources/local_discovery/local_discovery.css b/chrome/browser/resources/local_discovery/local_discovery.css
index 2433e43..59205da 100644
--- a/chrome/browser/resources/local_discovery/local_discovery.css
+++ b/chrome/browser/resources/local_discovery/local_discovery.css
@@ -2,30 +2,38 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
 
-thead {
-  white-space: nowrap;
+.device-info {
+  -webkit-box-flex: 1;
+  -webkit-padding-start: 55px;
 }
 
-th {
-  background-color: #c0c0c0;
+.device {
+  display: -webkit-box;
+  max-width: 738px;
 }
 
-td {
-  background-color: #f0f0f0;
+.device .button-container {
+  -webkit-padding-end: 10px;
+  display: inline-block;
+  width: 70px;
 }
 
-#info-console {
-  background-color: #f0f0f0;
-  height: 100px;
-  overflow: auto;
+.device .button-container button {
+  margin-top: 0.5em;
+  padding-bottom: 10px;
+  padding-top: 10px;
 }
 
-.info-item {
-  border-bottom: 2px #f0f0f0 solid;
-  margin-bottom: 5px;
+.register-page {
+  padding: 15px;
+  width: 600px;
 }
 
-.info-item .info-item-name {
-  font-weight: bold;
-  padding-right: 20px;
+.register-page .button-list {
+  padding-top: 15px;
+  text-align: right;
+}
+
+html[dir='rtl'] .register-page .button-list {
+  text-align: left;
 }
\ No newline at end of file
diff --git a/chrome/browser/resources/local_discovery/local_discovery.html b/chrome/browser/resources/local_discovery/local_discovery.html
index 007524f..a1418eb 100644
--- a/chrome/browser/resources/local_discovery/local_discovery.html
+++ b/chrome/browser/resources/local_discovery/local_discovery.html
@@ -1,32 +1,81 @@
+<!-- TODO(noamsml): i18n before submitting -->
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
-  <title>Local Discovery Debug Page</title>
+  <title i18n-content="devicesTitle"></title>
+  <link rel="stylesheet" href="chrome://resources/css/chrome_shared.css">
+  <link rel="stylesheet" href="chrome://resources/css/overlay.css">
   <link rel="stylesheet" href="local_discovery.css">
+  <link rel="stylesheet" href="../uber/uber_shared.css">
+
   <script src="chrome://resources/js/cr.js"></script>
   <script src="chrome://resources/js/util.js"></script>
   <script src="chrome://resources/js/load_time_data.js"></script>
+  <script src="chrome://resources/js/cr/ui/overlay.js"></script>
   <script src="local_discovery.js"></script>
   <script src="strings.js"></script>
 </head>
-<body>
-  <div>
-    <div id="info-console"></div>
-    <table id="devices-table" width="100%">
-      <thead>
-        <th i18n-content="serviceName"></th>
-        <th i18n-content="serviceDomain"></th>
-        <th i18n-content="servicePort"></th>
-        <th i18n-content="serviceIp"></th>
-        <th i18n-content="serviceLastseen"></th>
-        <th i18n-content="serviceRegister"></th>
-        <th i18n-content="serviceInfo"></th>
-      </thead>
-    </table>
-  </div>
+<body class="uber-frame"
+      i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize">
+  <div class="page">
+    <div class="overlay" id="overlay" hidden>
+      <div id="register-overlay" class="page">
+        <div class="close-button"></div>
 
-  <div id="info-pane">
+        <div id="register-page-choose" class="register-page">
+          <h1 i18n-content="confirmRegistration"></h1>
+          <div id="register-message">
+
+          </div>
+          <div id="register-picker">
+            <label i18n-content="registerUser" for="register-user-list"></label>
+            <select id="register-user-list"></select>
+          </div>
+          <div class="button-list">
+            <button class="register-cancel">Cancel</button>
+            <button class="register-continue"
+                    id="register-confirmation-continue">Continue</button>
+          </div>
+        </div>
+
+        <div id="register-page-adding1" class="register-page">
+          <h1 i18n-content="addingPrinter"></h1>
+          <div i18n-content="addingMessage1"></div>
+          <div class="button-list">
+            <button class="register-cancel">Cancel</button>
+          </div>
+        </div>
+
+        <div id="register-page-adding2" class="register-page">
+          <h1 i18n-content="addingPrinter"></h1>
+          <div i18n-content="addingMessage2"></div>
+          <div class="button-list">
+            <button class="register-cancel">Cancel</button>
+          </div>
+        </div>
+
+        <div id="register-page-error" class="register-page">
+          <h1 i18n-content="addingError"></h1>
+          <div i18n-content="addingErrorMessage"></div>
+          <div class="button-list">
+            <button id="register-error-exit">OK</button>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <header>
+      <h1 i18n-content="devicesTitle"></h1>
+    </header>
+
+    <h2 i18n-content="registeredDevicesTitle"></h2>
+    <div id="registered-devices" class="device-list">
+    </div>
+
+    <h2 i18n-content="unregisteredDevicesTitle"></h2>
+    <div id="unregistered-devices" class="device-list">
+    </div>
   </div>
 
   <script src="chrome://resources/js/i18n_template2.js"></script>
diff --git a/chrome/browser/resources/local_discovery/local_discovery.js b/chrome/browser/resources/local_discovery/local_discovery.js
index a926a5a..c6fa5f1 100644
--- a/chrome/browser/resources/local_discovery/local_discovery.js
+++ b/chrome/browser/resources/local_discovery/local_discovery.js
@@ -10,106 +10,153 @@
  * callbacks from the C++ code saying that a new device is available.
  */
 
+
+<include src="../uber/uber_utils.js" />
+
 cr.define('local_discovery', function() {
   'use strict';
 
   /**
-   * Add a text TD to a TR.
-   * @param {HTMLTableRowElement} row Row in table to be filled.
-   * @param {string} text Text of TD.
+   * Map of service names to corresponding service objects.
+   * @type {Object.<string,Service>}
    */
-  function textTD(row, text) {
-    var td = document.createElement('td');
-    td.textContent = text;
-    row.appendChild(td);
-  }
+  var devices = {};
+
 
   /**
-   * Add a button TD to a TR.
-   * @param {HTMLTableRowElement} row Row in table to be filled.
-   * @param {string} text Text of button.
-   * @param {function()} action Action to be taken when button is pressed.
-   */
-  function buttonTD(row, text, action) {
-    var td = document.createElement('td');
-    var button = document.createElement('button');
-    button.textContent = text;
-    button.addEventListener('click', action);
-    td.appendChild(button);
-    row.appendChild(td);
-  }
-
-  /**
-   * Fill a table row from the provided information.
-   * @param {HTMLTableRowElement} row Row in table to be filled.
-   * @param {string} name Name of the device.
+   * Object that represents a device in the device list.
    * @param {Object} info Information about the device.
+   * @constructor
    */
-  function fillRow(row, name, info) {
-    textTD(row, name);
-    textTD(row, info.domain);
-    textTD(row, info.port);
-    textTD(row, info.ip);
-    textTD(row, info.lastSeen);
-
-    if (!info.registered) {
-      buttonTD(row, loadTimeData.getString('serviceRegister'),
-               sendRegisterDevice.bind(null, name));
-    } else {
-      textTD(row, loadTimeData.getString('registered'));
-    }
-
-    buttonTD(row, loadTimeData.getString('serviceInfo'),
-             sendInfoRequest.bind(null, name));
+  function Device(info) {
+    this.info = info;
+    this.domElement = null;
   }
 
+  Device.prototype = {
+    /**
+     * Update the device.
+     * @param {Object} info New information about the device.
+     */
+    updateDevice: function(info) {
+      if (this.domElement && info.is_mine != this.info.is_mine) {
+        this.deviceContainer(this.info.is_mine).removeChild(this.domElement);
+        this.deviceContainer(info.is_mine).appendChild(this.domElement);
+      }
+      this.info = info;
+      this.renderDevice();
+    },
+
+    /**
+     * Delete the device.
+     */
+    removeDevice: function() {
+      this.deviceContainer(this.info.is_mine).removeChild(this.domElement);
+    },
+
+    /**
+     * Render the device to the device list.
+     */
+    renderDevice: function() {
+      if (this.domElement) {
+        clearElement(this.domElement);
+      } else {
+        this.domElement = document.createElement('div');
+        this.deviceContainer(this.info.is_mine).appendChild(this.domElement);
+      }
+
+      this.domElement.classList.add('device');
+      this.domElement.classList.add('printer-active');
+
+      var deviceInfo = document.createElement('div');
+      deviceInfo.className = 'device-info';
+      this.domElement.appendChild(deviceInfo);
+
+      var deviceName = document.createElement('h3');
+      deviceName.className = 'device-name';
+      deviceName.textContent = this.info.human_readable_name;
+      deviceInfo.appendChild(deviceName);
+
+      var deviceDescription = document.createElement('div');
+      deviceDescription.className = 'device-description';
+      deviceDescription.textContent = this.info.description;
+      deviceInfo.appendChild(deviceDescription);
+
+      var buttonContainer = document.createElement('div');
+      buttonContainer.className = 'button-container';
+      this.domElement.appendChild(buttonContainer);
+
+      var button = document.createElement('button');
+      button.textContent = loadTimeData.getString('serviceRegister');
+
+      if (this.info.registered) {
+        button.disabled = 'disabled';
+      } else {
+        button.addEventListener(
+          'click',
+          sendRegisterDevice.bind(null, this.info.service_name));
+      }
+
+      buttonContainer.appendChild(button);
+    },
+
+    /**
+     * Return the correct container for the device.
+     * @param {boolean} is_mine Whether or not the device is in the 'Registered'
+     *    section.
+     */
+    deviceContainer: function(is_mine) {
+      if (is_mine) return $('registered-devices');
+      return $('unregistered-devices');
+    }
+  };
+
   /**
    * Appends a row to the output table listing the new device.
    * @param {string} name Name of the device.
    * @param {string} info Additional info of the device, if empty device need to
    *                      be deleted.
    */
-  function onServiceUpdate(name, info) {
-    name = name.replace(/[\r\n]/g, '');
-    var table = $('devices-table');
-
-    for (var i = 0, row; row = table.rows[i]; i++) {
-      if (row.cells[0].textContent == name) {
-        if (!info) {
-          // Delete service from the row.
-          table.removeChild(row);
-        } else {
-          // Replace existing service.
-          while (row.firstChild) {
-            row.removeChild(row.firstChild);
-          }
-
-          fillRow(row, name, info);
-          return;
-        }
+  function onDeviceUpdate(name, info) {
+    if (info) {
+      if (devices.hasOwnProperty(name)) {
+        devices[name].updateDevice(info);
+      } else {
+        devices[name] = new Device(info);
+        devices[name].renderDevice();
       }
+    } else {
+      devices[name].removeDevice();
+      delete devices[name];
     }
-
-    if (!info) {
-      // Service could not be found in the table.
-      return;
-    }
-
-    // Row does not exist. Create it.
-    var tr = document.createElement('tr');
-
-    fillRow(tr, name, info);
-    table.appendChild(tr);
   }
 
   /**
-   * Adds a row to the logging console.
-   * @param {string} msg The message to log.
+   * Hide the register overlay.
    */
-  function logToInfoConsole(msg) {
-    var div = document.createElement('div');
-    div.textContent = msg;
-    $('info-console').appendChild(div);
+  function showRegisterOverlay() {
+    $('register-overlay').classList.add('showing');
+    $('overlay').hidden = false;
+    uber.invokeMethodOnParent('beginInterceptingEvents');
+  }
+
+  /**
+   * Show the register overlay.
+   */
+  function hideRegisterOverlay() {
+    $('register-overlay').classList.remove('showing');
+    $('overlay').hidden = true;
+    uber.invokeMethodOnParent('stopInterceptingEvents');
+  }
+
+  /**
+   * Clear a DOM element of all children.
+   * @param {HTMLElement} element DOM element to clear.
+   */
+  function clearElement(element) {
+    while (element.firstChild) {
+      element.removeChild(element.firstChild);
+    }
   }
 
   /**
@@ -118,162 +165,119 @@
    */
   function sendRegisterDevice(device) {
     chrome.send('registerDevice', [device]);
-    logToInfoConsole(loadTimeData.getStringF('registeringService', device));
   }
 
   /**
    * Announce that a registration failed.
-   * @param {string} reason The error message.
    */
-  function registrationFailed(reason) {
-    logToInfoConsole(loadTimeData.getStringF('registrationFailed', reason));
+  function registrationFailed() {
+    setRegisterPage('register-page-error');
   }
 
   /**
-   * Request the contents of a device's /info page.
-   * @param {string} device The device to query.
+   * Update UI to reflect that registration has been confirmed on the printer.
    */
-  function sendInfoRequest(device) {
-    chrome.send('info', [device]);
-    logToInfoConsole(loadTimeData.getStringF('infoStarted', device));
+  function registrationConfirmedOnPrinter() {
+    setRegisterPage('register-page-adding2');
   }
 
   /**
    * Announce that a registration succeeeded.
-   * @param {string} id The id of the newly registered device.
    */
-  function registrationSuccess(id) {
-    logToInfoConsole(loadTimeData.getStringF('registrationSucceeded', id));
+  function registrationSuccess() {
+    hideRegisterOverlay();
   }
 
   /**
-   * Render an info item onto the info pane.
-   * @param {string} name Name of the item.
-   * @param {?} value Value of the item.
-   * @param {function(?):string} render_type Render function for value
-   *     datatype.
-   * @return {HTMLElement} Rendered info item.
+   * Update visibility status for page.
    */
-  function renderInfoItem(name, value, render_type) {
-    var container = document.createElement('div');
-    container.classList.add('info-item');
-    var nameElem = document.createElement('span');
-    nameElem.classList.add('info-item-name');
-    nameElem.textContent = name;
-    container.appendChild(nameElem);
-    var valueElem = document.createElement('span');
-    valueElem.textContent = render_type(value);
-    container.appendChild(valueElem);
-    return container;
+  function updateVisibility() {
+    chrome.send('isVisible', [!document.webkitHidden]);
   }
 
   /**
-   * Render and append an info item to the info pane, if it exists.
-   * @param {Object} info Info response.
-   * @param {string} name Name of property.
-   * @param {function(?):string} render_type Render function for value.
+   * Set the page that the register wizard is on.
+   * @param {string} page_id ID string for page.
    */
-  function infoItem(info, name, render_type) {
-    if (name in info) {
-      $('info-pane').appendChild(renderInfoItem(name, info[name], render_type));
+  function setRegisterPage(page_id) {
+    var pages = $('register-overlay').querySelectorAll('.register-page');
+    var pagesLength = pages.length;
+    for (var i = 0; i < pagesLength; i++) {
+      pages[i].hidden = true;
     }
+
+    $(page_id).hidden = false;
   }
 
   /**
-   * Render a string to an info-pane-displayable string.
-   * @param {?} value Value; not guaranteed to be a string.
-   * @return {string} Rendered value.
+   * Request a user account from a list.
+   * @param {Array} users Array of (index, username) tuples. Username may be
+   *    displayed to the user; index must be passed opaquely to the UI handler.
    */
-  function renderTypeString(value) {
-    if (typeof value != 'string') {
-      return 'INVALID';
+  function requestUser(users, printerName) {
+    clearElement($('register-user-list'));
+
+    var usersLength = users.length;
+    for (var i = 0; i < usersLength; i++) {
+      var userIndex = users[i][0];
+      var userName = users[i][1];
+
+      var option = document.createElement('option');
+      option.textContent = userName;
+      option.userData = { userIndex: userIndex, userName: userName };
+      $('register-user-list').appendChild(option);
     }
-    return value;
+
+    showRegisterOverlay();
+    setRegisterPage('register-page-choose');
+    $('register-message').textContent =
+      loadTimeData.getStringF('registerConfirmMessage', printerName);
   }
 
   /**
-   * Render a integer to an info-pane-displayable string.
-   * @param {?} value Value; not guaranteed to be an integer.
-   * @return {string} Rendered value.
+   * Send user selection and begin registration.
    */
-  function renderTypeInt(value) {
-    if (typeof value != 'number') {
-      return 'INVALID';
-    }
-
-    return value.toString();
-  }
-
-  /**
-   * Render an array to an info-pane-displayable string.
-   * @param {?} value Value; not guaranteed to be an array.
-   * @return {string} Rendered value.
-   */
-  function renderTypeStringList(value) {
-    if (!Array.isArray(value)) {
-      return 'INVALID';
-    }
-
-    var returnValue = '';
-    var valueLength = value.length;
-    for (var i = 0; i < valueLength - 1; i++) {
-      returnValue += value[i];
-      returnValue += ', ';
-    }
-
-    if (value.length != 0) {
-      returnValue += value[value.length - 1];
-    }
-
-    return returnValue;
-  }
-
-  /**
-   * Render info response from JSON.
-   * @param {Object} info Info response.
-   */
-  function renderInfo(info) {
-    // Clear info
-    while ($('info-pane').firstChild) {
-      $('info-pane').removeChild($('info-pane').firstChild);
-    }
-
-    infoItem(info, 'x-privet-token', renderTypeString);
-    infoItem(info, 'id', renderTypeString);
-    infoItem(info, 'name', renderTypeString);
-    infoItem(info, 'description', renderTypeString);
-    infoItem(info, 'type', renderTypeStringList);
-    infoItem(info, 'api', renderTypeStringList);
-    infoItem(info, 'connection_state', renderTypeString);
-    infoItem(info, 'device_state', renderTypeString);
-    infoItem(info, 'manufacturer', renderTypeString);
-    infoItem(info, 'url', renderTypeString);
-    infoItem(info, 'model', renderTypeString);
-    infoItem(info, 'serial_number', renderTypeString);
-    infoItem(info, 'firmware', renderTypeString);
-    infoItem(info, 'uptime', renderTypeInt);
-    infoItem(info, 'setup_url', renderTypeString);
-    infoItem(info, 'support_url', renderTypeString);
-    infoItem(info, 'update_url', renderTypeString);
-  }
-
-  /**
-   * Announce that an info request failed.
-   * @param {string} reason The error message.
-   */
-  function infoFailed(reason) {
-    logToInfoConsole(loadTimeData.getStringF('infoFailed', reason));
+  function beginRegistration() {
+    var userList = $('register-user-list');
+    var selectedOption = userList.options[userList.selectedIndex];
+    var userData = selectedOption.userData;
+    chrome.send('chooseUser', [userData.userIndex, userData.userName]);
+    setRegisterPage('register-page-adding1');
   }
 
   document.addEventListener('DOMContentLoaded', function() {
+    uber.onContentFrameLoaded();
+
+    cr.ui.overlay.setupOverlay($('overlay'));
+    cr.ui.overlay.globalInitialization();
+    $('overlay').addEventListener('cancelOverlay', hideRegisterOverlay);
+
+    var cancelButtons = document.querySelectorAll('.register-cancel');
+    var cancelButtonsLength = cancelButtons.length;
+    for (var i = 0; i < cancelButtonsLength; i++) {
+      cancelButtons[i].addEventListener('click', hideRegisterOverlay);
+    }
+
+    $('register-error-exit').addEventListener('click', hideRegisterOverlay);
+
+    $('register-confirmation-continue').addEventListener(
+      'click', beginRegistration);
+
+    updateVisibility();
+    document.addEventListener('webkitvisibilitychange', updateVisibility,
+                              false);
+
+    var title = loadTimeData.getString('devicesTitle');
+    uber.invokeMethodOnParent('setTitle', {title: title});
+
     chrome.send('start');
   });
 
   return {
     registrationSuccess: registrationSuccess,
     registrationFailed: registrationFailed,
-    onServiceUpdate: onServiceUpdate,
-    infoFailed: infoFailed,
-    renderInfo: renderInfo
+    onDeviceUpdate: onDeviceUpdate,
+    requestUser: requestUser,
+    registrationConfirmedOnPrinter: registrationConfirmedOnPrinter
   };
 });
diff --git a/chrome/browser/resources/memory_internals/memory_internals.js b/chrome/browser/resources/memory_internals/memory_internals.js
index eff4702..8124b6b 100644
--- a/chrome/browser/resources/memory_internals/memory_internals.js
+++ b/chrome/browser/resources/memory_internals/memory_internals.js
@@ -66,8 +66,8 @@
             if (process['type'].match(/^Tab/)) {
               // Append each tab's history.
               for (var j = 0; j < process['history'].length; ++j) {
-                value += '<dl><dt>Hisotry ' + j + ':' +
-                    JoinLinks(process['history'][j]);
+                value += '<dl><dt>History ' + j + ':' +
+                    JoinLinks(process['history'][j]) + '</dl>';
               }
             } else {
               value += '<br>' + process['titles'].join('<br>');
@@ -96,7 +96,8 @@
      */
     updateExtensions: function(extensions) {
       // Remove existing information.
-      var size = $('extension-view').getElementsByClassName('extension').length;
+      var size =
+          $('extension-view').getElementsByClassName('extension').length;
       for (var i = 0; i < size; ++i) {
         $('extension-view').deleteRow(-1);
       }
diff --git a/chrome/browser/resources/options/browser_options.js b/chrome/browser/resources/options/browser_options.js
index b84defe..b68e33a 100644
--- a/chrome/browser/resources/options/browser_options.js
+++ b/chrome/browser/resources/options/browser_options.js
@@ -367,9 +367,6 @@
       $('language-button').onclick = showLanguageOptions;
       $('manage-languages').onclick = showLanguageOptions;
 
-      if (!loadTimeData.getBoolean('enableTranslateSettings'))
-        $('manage-languages').hidden = true;
-
       // Downloads section.
       Preferences.getInstance().addEventListener('download.default_directory',
           this.onDefaultDownloadDirectoryChanged_.bind(this));
@@ -734,9 +731,15 @@
       customizeSyncButton.hidden = !this.signedIn_ ||
                                    syncData.managed ||
                                    !syncData.syncSystemEnabled;
-      customizeSyncButton.textContent = syncData.setupCompleted ?
+
+      // Only modify the customize button's text if the new text is different.
+      // Otherwise, it can affect search-highlighting in the settings page.
+      // See http://crbug.com/268265.
+      var customizeSyncButtonNewText = syncData.setupCompleted ?
           loadTimeData.getString('customizeSync') :
           loadTimeData.getString('syncButtonTextStart');
+      if (customizeSyncButton.textContent != customizeSyncButtonNewText)
+        customizeSyncButton.textContent = customizeSyncButtonNewText;
 
       // Disable the "sign in" button if we're currently signing in, or if we're
       // already signed in and signout is not allowed.
diff --git a/chrome/browser/resources/options/chromeos/display_options.js b/chrome/browser/resources/options/chromeos/display_options.js
index 7769fb0..f1ca4ec 100644
--- a/chrome/browser/resources/options/chromeos/display_options.js
+++ b/chrome/browser/resources/options/chromeos/display_options.js
@@ -835,8 +835,6 @@
       }
 
       this.layout_ = layout;
-      this.offset_ = offset;
-      this.dirty_ = false;
 
       $('display-options-toggle-mirroring').textContent =
           loadTimeData.getString(
diff --git a/chrome/browser/resources/options/chromeos/internet_detail.js b/chrome/browser/resources/options/chromeos/internet_detail.js
index a36df41..05958eb 100644
--- a/chrome/browser/resources/options/chromeos/internet_detail.js
+++ b/chrome/browser/resources/options/chromeos/internet_detail.js
@@ -389,8 +389,12 @@
       updateHidden('#details-internet-page .wimax-details', !this.wimax);
       updateHidden('#details-internet-page .vpn-details', !this.vpn);
       updateHidden('#details-internet-page .proxy-details', !this.showProxy);
-      updateHidden('#details-internet-page .gsm-only', !this.gsm);
-      updateHidden('#details-internet-page .cdma-only', this.gsm);
+      // Conditionally call updateHidden on .gsm-only, so that we don't unhide
+      // a previously hidden element.
+      if (this.gsm)
+        updateHidden('#details-internet-page .cdma-only', true);
+      else
+        updateHidden('#details-internet-page .gsm-only', true);
       /* Network information merged into the Wifi tab for wireless networks
          unless the option is set for enabling a static IP configuration. */
       updateHidden('#details-internet-page .network-details',
@@ -782,6 +786,25 @@
     }
   };
 
+  DetailsInternetPage.updateConnectionButtonVisibilty = function(data) {
+    $('details-internet-login').hidden = data.connected;
+    $('details-internet-login').disabled = data.disableConnectButton;
+
+    if (!data.connected &&
+        ((data.type == Constants.TYPE_WIFI && data.encryption) ||
+          data.type == Constants.TYPE_WIMAX ||
+          data.type == Constants.TYPE_VPN)) {
+      $('details-internet-configure').hidden = false;
+    } else {
+      $('details-internet-configure').hidden = true;
+    }
+
+    if (data.type == Constants.TYPE_ETHERNET)
+      $('details-internet-disconnect').hidden = true;
+    else
+      $('details-internet-disconnect').hidden = !data.connected;
+  };
+
   DetailsInternetPage.updateConnectionData = function(update) {
     var detailsPage = DetailsInternetPage.getInstance();
     if (!detailsPage.visible)
@@ -802,8 +825,7 @@
     detailsPage.connected = data.connected;
     $('connection-state').textContent = data.connectionState;
 
-    $('details-internet-login').hidden = data.connected;
-    $('details-internet-login').disabled = data.disableConnectButton;
+    this.updateConnectionButtonVisibilty(data);
 
     if (data.type == Constants.TYPE_WIFI) {
       $('wifi-connection-state').textContent = data.connectionState;
@@ -820,18 +842,8 @@
         $('details-internet-login').hidden = true;
     }
 
-    if (data.type != Constants.TYPE_ETHERNET)
-      $('details-internet-disconnect').hidden = !data.connected;
-
-    if ((data.type == Constants.TYPE_WIFI && data.encryption) ||
-        data.type == Constants.TYPE_WIMAX ||
-        data.type == Constants.TYPE_VPN) {
-      $('details-internet-configure').hidden = false;
-    } else {
-      $('details-internet-configure').hidden = true;
-    }
     $('connection-state').data = data;
-  }
+  };
 
   DetailsInternetPage.showDetailedInfo = function(data) {
     var detailsPage = DetailsInternetPage.getInstance();
@@ -878,19 +890,9 @@
     $('buyplan-details').hidden = true;
     $('activate-details').hidden = true;
     $('view-account-details').hidden = true;
-    $('details-internet-login').hidden = data.connected;
-    $('details-internet-login').disabled = data.disableConnectButton;
-    if (data.type == Constants.TYPE_ETHERNET)
-      $('details-internet-disconnect').hidden = true;
-    else
-      $('details-internet-disconnect').hidden = !data.connected;
-    if ((data.type == Constants.TYPE_WIFI && data.encryption) ||
-        data.type == Constants.TYPE_WIMAX ||
-        data.type == Constants.TYPE_VPN) {
-      $('details-internet-configure').hidden = false;
-    } else {
-      $('details-internet-configure').hidden = true;
-    }
+
+    this.updateConnectionButtonVisibilty(data);
+
     $('web-proxy-auto-discovery').hidden = true;
 
     detailsPage.deviceConnected = data.deviceConnected;
@@ -1099,11 +1101,15 @@
       $('firmware-revision').textContent = data.firmwareRevision;
       $('hardware-revision').textContent = data.hardwareRevision;
       $('mdn').textContent = data.mdn;
-      $('min').textContent = data.min;
       $('operator-name').textContent = data.operatorName;
       $('operator-code').textContent = data.operatorCode;
 
-      // Show IMEI/ESN/MEID only if they are available.
+      // Make sure that GSM/CDMA specific properties that shouldn't be hidden
+      // are visible.
+      updateHidden('#details-internet-page .gsm-only', false);
+      updateHidden('#details-internet-page .cdma-only', false);
+
+      // Show IMEI/ESN/MEID/MIN/PRL only if they are available.
       (function() {
         var setContentOrHide = function(property) {
           var value = data[property];
@@ -1115,6 +1121,7 @@
         setContentOrHide('esn');
         setContentOrHide('imei');
         setContentOrHide('meid');
+        setContentOrHide('min');
         setContentOrHide('prl-version');
       })();
       detailsPage.gsm = data.gsm;
diff --git a/chrome/browser/resources/options/language_options.css b/chrome/browser/resources/options/language_options.css
index 21817c9..4e86bbb 100644
--- a/chrome/browser/resources/options/language_options.css
+++ b/chrome/browser/resources/options/language_options.css
@@ -122,16 +122,6 @@
   z-index: 10;
 }
 
-<if expr="is_macosx">
-.language-options[enabled='false'] #language-options-details {
-  display: none;
-}
-
-.language-options[enabled='false'] #language-options-languages {
-  background-color: transparent !important;
-}
-</if>
-
 /* TODO(kochi): This is temporary copy from new_tab.css */
 /* Notification */
 
diff --git a/chrome/browser/resources/options/language_options.html b/chrome/browser/resources/options/language_options.html
index b9d77c4..e2d420c 100644
--- a/chrome/browser/resources/options/language_options.html
+++ b/chrome/browser/resources/options/language_options.html
@@ -12,7 +12,7 @@
       <div i18n-content="inputMethodInstructions"></div>
 </if>
     </div>
-    <div class="language-options" i18n-values="enabled:enableTranslateSettings">
+    <div class="language-options">
       <div id="language-options-languages">
         <h3 i18n-content="languages"></h3>
         <list id="language-options-list"></list>
@@ -66,13 +66,13 @@
 </if>
         </div>
 </if>
-        <div id="language-options-dont-translate"
+        <div id="language-options-offer-to-translate"
             class="language-options-contents" hidden>
           <div class="checkbox">
             <label>
-              <input type="checkbox" id="dont-translate-in-this-language">
-              <span class="dont-translate-label"
-                  i18n-content="dontTranslateInThisLanguage"></span>
+              <input type="checkbox" id="offer-to-translate-in-this-language">
+              <span class="offer-to-translate-label"
+                  i18n-content="offerToTranslateInThisLanguage"></span>
             </label>
           </div>
           <span id="cannot-translate-in-this-language"
diff --git a/chrome/browser/resources/options/language_options.js b/chrome/browser/resources/options/language_options.js
index 8b3c7a6..4e8e2fc 100644
--- a/chrome/browser/resources/options/language_options.js
+++ b/chrome/browser/resources/options/language_options.js
@@ -181,9 +181,9 @@
         this.initializeLanguageCodeToInputMethodIdsMap_();
       }
 
-      var checkbox = $('dont-translate-in-this-language');
+      var checkbox = $('offer-to-translate-in-this-language');
       checkbox.addEventListener('click',
-          this.handleDontTranslateCheckboxClick_.bind(this));
+          this.handleOfferToTranslateCheckboxClick_.bind(this));
 
       Preferences.getInstance().addEventListener(
           TRANSLATE_BLOCKED_LANGUAGES_PREF,
@@ -438,7 +438,7 @@
         }
       }
 
-      this.updateDontTranslateCheckbox_(languageCode);
+      this.updateOfferToTranslateCheckbox_(languageCode);
 
       if (cr.isWindows || cr.isChromeOS)
         this.updateUiLanguageButton_(languageCode);
@@ -677,13 +677,8 @@
      * @param {string} languageCode Language code (ex. "fr").
      * @private
      */
-    updateDontTranslateCheckbox_: function(languageCode) {
-      var div = $('language-options-dont-translate');
-
-      if (!loadTimeData.getBoolean('enableTranslateSettings')) {
-        div.hidden = true;
-        return;
-      }
+    updateOfferToTranslateCheckbox_: function(languageCode) {
+      var div = $('language-options-offer-to-translate');
 
       // Translation server supports Chinese (Transitional) and Chinese
       // (Simplified) but not 'general' Chinese. To avoid ambiguity, we don't
@@ -695,9 +690,9 @@
         return;
       }
 
-      var dontTranslate = div.querySelector('div');
+      var offerToTranslate = div.querySelector('div');
       var cannotTranslate = $('cannot-translate-in-this-language');
-      var nodes = [dontTranslate, cannotTranslate];
+      var nodes = [offerToTranslate, cannotTranslate];
 
       var convertedLangCode = this.convertLangCodeForTranslation_(languageCode);
       if (this.translateSupportedLanguages_.indexOf(convertedLangCode) != -1) {
@@ -707,23 +702,23 @@
         return;
       }
 
-      var checkbox = $('dont-translate-in-this-language');
+      var checkbox = $('offer-to-translate-in-this-language');
 
       // If the language corresponds to the default target language (in most
-      // cases, the user's locale language), "Don't Translate" checkbox should
-      // be always checked.
+      // cases, the user's locale language), "Offer to translate" checkbox
+      // should be always unchecked.
       var defaultTargetLanguage =
           loadTimeData.getString('defaultTargetLanguage');
       if (convertedLangCode == defaultTargetLanguage) {
         checkbox.disabled = true;
-        checkbox.checked = true;
+        checkbox.checked = false;
         return;
       }
 
       checkbox.disabled = false;
 
       var blockedLanguages = this.translateBlockedLanguages_;
-      var checked = blockedLanguages.indexOf(convertedLangCode) != -1;
+      var checked = blockedLanguages.indexOf(convertedLangCode) == -1;
       checkbox.checked = checked;
     },
 
@@ -822,11 +817,11 @@
     },
 
     /**
-     * Handles don't-translate checkbox's click event.
+     * Handles offer-to-translate checkbox's click event.
      * @param {Event} e Click event.
      * @private
      */
-    handleDontTranslateCheckboxClick_: function(e) {
+    handleOfferToTranslateCheckboxClick_: function(e) {
       var checkbox = e.target;
       var checked = checkbox.checked;
 
@@ -834,9 +829,9 @@
       var selectedLanguageCode = languageOptionsList.getSelectedLanguageCode();
 
       if (checked)
-        this.addBlockedLanguage_(selectedLanguageCode);
-      else
         this.removeBlockedLanguage_(selectedLanguageCode);
+      else
+        this.addBlockedLanguage_(selectedLanguageCode);
     },
 
     /**
@@ -915,7 +910,7 @@
       var languageOptionsList = $('language-options-list');
       var selectedLanguageCode = languageOptionsList.getSelectedLanguageCode();
       this.translateBlockedLanguages_ = e.value.value;
-      this.updateDontTranslateCheckbox_(selectedLanguageCode);
+      this.updateOfferToTranslateCheckbox_(selectedLanguageCode);
     },
 
     /**
diff --git a/chrome/browser/resources/options/options_bundle.js b/chrome/browser/resources/options/options_bundle.js
index 50062b9..16bc4f1 100644
--- a/chrome/browser/resources/options/options_bundle.js
+++ b/chrome/browser/resources/options/options_bundle.js
@@ -93,8 +93,8 @@
 <include src="language_list.js"></include>
 <include src="language_options.js"></include>
 <include src="manage_profile_overlay.js"></include>
-<include src="managed_user_create_confirm.js"</include>
-<include src="managed_user_learn_more.js"</include>
+<include src="managed_user_create_confirm.js"></include>
+<include src="managed_user_learn_more.js"></include>
 <include src="media_galleries_list.js"></include>
 <include src="media_galleries_manager_overlay.js"></include>
 <include src="options_focus_manager.js"></include>
diff --git a/chrome/browser/resources/sync_setup_overlay.js b/chrome/browser/resources/sync_setup_overlay.js
index 770eecf..07ec814 100644
--- a/chrome/browser/resources/sync_setup_overlay.js
+++ b/chrome/browser/resources/sync_setup_overlay.js
@@ -90,6 +90,12 @@
     closeOverlay_: function() {
       this.syncConfigureArgs_ = null;
       this.dataTypeBoxes_ = {};
+
+      // Required in order to determine whether to give focus to the OK button
+      // or passphrase field. See crbug.com/279770.
+      $('confirm-sync-preferences').hidden = true;
+      $('customize-sync-preferences').hidden = true;
+
       var overlay = $('sync-setup-overlay');
       if (!overlay.hidden)
         OptionsPage.closeOverlay();
@@ -461,13 +467,8 @@
                           DataTypeSelection.SYNC_EVERYTHING :
                           DataTypeSelection.CHOOSE_WHAT_TO_SYNC;
           this.showCustomizePage_(args, index);
-          if (args.showPassphrase)
-            $('passphrase').focus();
-          else
-            $('choose-datatypes-ok').focus();
         } else {
           this.showSyncEverythingPage_();
-          $('confirm-everything-ok').focus();
         }
       }
     },
@@ -483,6 +484,9 @@
     },
 
     showSyncEverythingPage_: function() {
+      // Determine whether to bring the OK button into focus.
+      var wasConfirmPageHidden = $('confirm-sync-preferences').hidden;
+
       $('confirm-sync-preferences').hidden = false;
       $('customize-sync-preferences').hidden = true;
 
@@ -497,6 +501,10 @@
 
       if (!this.useEncryptEverything_ && !this.usePassphrase_)
         $('basic-encryption-option').checked = true;
+
+      // Give the OK button focus only when the dialog wasn't already visible.
+      if (wasConfirmPageHidden)
+        $('confirm-everything-ok').focus();
     },
 
     /**
@@ -571,6 +579,9 @@
      * @private
      */
     showCustomizePage_: function(args, index) {
+      // Determine whether to bring the OK button field into focus.
+      var wasCustomizePageHidden = $('customize-sync-preferences').hidden;
+
       $('confirm-sync-preferences').hidden = true;
       $('customize-sync-preferences').hidden = false;
 
@@ -584,8 +595,16 @@
       this.setDataTypeCheckboxesEnabled_(
           index == DataTypeSelection.CHOOSE_WHAT_TO_SYNC);
 
+      // Give the OK button focus only when the dialog wasn't already visible.
+      if (wasCustomizePageHidden)
+        $('choose-datatypes-ok').focus();
+
       if (args && args.showPassphrase) {
         this.showPassphraseContainer_(args);
+        // Give the passphrase field focus only when the dialog wasn't already
+        // visible.
+        if (wasCustomizePageHidden)
+          $('passphrase').focus();
       } else {
         // We only show the 'Use Default' link if we're not prompting for an
         // existing passphrase.
diff --git a/chrome/browser/resources/translate_internals/translate_internals.js b/chrome/browser/resources/translate_internals/translate_internals.js
index 2774ff3..36434ad5 100644
--- a/chrome/browser/resources/translate_internals/translate_internals.js
+++ b/chrome/browser/resources/translate_internals/translate_internals.js
@@ -26,12 +26,6 @@
       var button = $('detection-logs-dump');
       button.addEventListener('click', onDetectionLogsDump);
 
-      var enableTranslateSettings = templateData['enable-translate-settings'];
-      if (!enableTranslateSettings) {
-        $('prefs-blocked-languages').hidden = true;
-        $('prefs-language-blacklist').querySelector('h2 span').hidden = true;
-      }
-
       var tabpanelNodeList = document.getElementsByTagName('tabpanel');
       var tabpanels = Array.prototype.slice.call(tabpanelNodeList, 0);
       var tabpanelIds = tabpanels.map(function(tab) {
diff --git a/chrome/browser/safe_browsing/client_side_detection_host.cc b/chrome/browser/safe_browsing/client_side_detection_host.cc
index b87a87d..eace117 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host.cc
@@ -449,6 +449,41 @@
         ui_manager_->DoDisplayBlockingPage(resource);
       }
     }
+    // If there is true phishing verdict, invalidate weakptr so that no longer
+    // consider the malware vedict.
+    weak_factory_.InvalidateWeakPtrs();
+  }
+}
+
+void ClientSideDetectionHost::MaybeShowMalwareWarning(GURL original_url,
+                                                      GURL malware_url,
+                                                      bool is_malware) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  VLOG(2) << "Received server malawre IP verdict for URL:" << malware_url
+          << " is_malware:" << is_malware;
+  if (is_malware && malware_url.is_valid() && original_url.is_valid()) {
+    DCHECK(web_contents());
+    if (ui_manager_.get()) {
+      SafeBrowsingUIManager::UnsafeResource resource;
+      resource.url = malware_url;
+      resource.original_url = original_url;
+      resource.is_subresource = (malware_url.host() != original_url.host());
+      resource.threat_type = SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL;
+      resource.render_process_host_id =
+          web_contents()->GetRenderProcessHost()->GetID();
+      resource.render_view_id =
+          web_contents()->GetRenderViewHost()->GetRoutingID();
+      if (!ui_manager_->IsWhitelisted(resource)) {
+        // We need to stop any pending navigations, otherwise the interstital
+        // might not get created properly.
+        web_contents()->GetController().DiscardNonCommittedEntries();
+        resource.callback = base::Bind(&EmptyUrlCheckCallback);
+        ui_manager_->DoDisplayBlockingPage(resource);
+      }
+    }
+    // If there is true malware verdict, invalidate weakptr so that no longer
+    // consider the phishing vedict.
+    weak_factory_.InvalidateWeakPtrs();
   }
 }
 
@@ -487,9 +522,11 @@
   if (request->feature_map_size() > 0) {
     VLOG(1) << "Start sending client malware request.";
     ClientSideDetectionService::ClientReportMalwareRequestCallback callback;
+    callback = base::Bind(&ClientSideDetectionHost::MaybeShowMalwareWarning,
+                          weak_factory_.GetWeakPtr());
     csd_service_->SendClientReportMalwareRequest(
         request.release(),  // The service takes ownership of the request object
-        callback);  // no action after request sent for now
+        callback);
   }
 }
 
diff --git a/chrome/browser/safe_browsing/client_side_detection_host.h b/chrome/browser/safe_browsing/client_side_detection_host.h
index 9543652..e4e47cf 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host.h
+++ b/chrome/browser/safe_browsing/client_side_detection_host.h
@@ -71,6 +71,12 @@
   // Otherwise, we do nothing.  Called in UI thread.
   void MaybeShowPhishingWarning(GURL phishing_url, bool is_phishing);
 
+  // Callback that is called when the malware IP server ping back is
+  // done. Display an interstitial if |is_malware| is true.
+  // Otherwise, we do nothing.  Called in UI thread.
+  void MaybeShowMalwareWarning(GURL original_url, GURL malware_url,
+                               bool is_malware);
+
   // Callback that is called when the browser feature extractor is done.
   // This method is responsible for deleting the request object.  Called on
   // the UI thread.
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
index 8fa8da6..0d04ab8 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
@@ -738,6 +738,65 @@
   ASSERT_FALSE(cb.is_null());
 }
 
+TEST_F(ClientSideDetectionHostTest,
+       OnPhishingDetectionDoneShowMalwareInterstitial) {
+  // Case 10: client thinks the page match malware IP and so does the server.
+  // We show an sub-resource malware interstitial.
+  MockBrowserFeatureExtractor* mock_extractor = new MockBrowserFeatureExtractor(
+      web_contents(),
+      csd_service_.get());
+  SetFeatureExtractor(mock_extractor);  // The host class takes ownership.
+
+  ClientPhishingRequest verdict;
+  verdict.set_url("http://not-phishing.com/");
+  verdict.set_client_score(0.1f);
+  verdict.set_is_phishing(false);
+
+  ClientSideDetectionService::ClientReportMalwareRequestCallback cb;
+  GURL malware_landing_url("http://malware.com/");
+  GURL malware_ip_url("http://badip.com");
+  ClientMalwareRequest malware_verdict;
+  malware_verdict.set_url("http://malware.com/");
+  ClientMalwareRequest::Feature* feature = malware_verdict.add_feature_map();
+  feature->set_name("malwareip1.2.3.4");
+  feature->set_value(1.0);
+  feature->add_metainfo("http://badip.com");
+
+  EXPECT_CALL(*mock_extractor, ExtractMalwareFeatures(_, _))
+      .WillOnce(SetArgumentPointee<1>(malware_verdict));
+  EXPECT_CALL(*csd_service_,
+              SendClientReportMalwareRequest(
+                  Pointee(PartiallyEqualMalwareVerdict(malware_verdict)), _))
+      .WillOnce(DoAll(DeleteArg<0>(),
+                      SaveArg<1>(&cb)));
+  OnPhishingDetectionDone(verdict.SerializeAsString());
+  EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
+  ASSERT_FALSE(cb.is_null());
+
+  UnsafeResource resource;
+  EXPECT_CALL(*ui_manager_.get(), DoDisplayBlockingPage(_))
+      .WillOnce(SaveArg<0>(&resource));
+  cb.Run(malware_landing_url, malware_ip_url, true);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
+  EXPECT_EQ(malware_ip_url, resource.url);
+  EXPECT_EQ(malware_landing_url, resource.original_url);
+  EXPECT_TRUE(resource.is_subresource);
+  EXPECT_EQ(SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL, resource.threat_type);
+  EXPECT_EQ(web_contents()->GetRenderProcessHost()->GetID(),
+            resource.render_process_host_id);
+  EXPECT_EQ(web_contents()->GetRenderViewHost()->GetRoutingID(),
+            resource.render_view_id);
+
+  // Make sure the client object will be deleted.
+  BrowserThread::PostTask(
+      BrowserThread::IO,
+      FROM_HERE,
+      base::Bind(&MockSafeBrowsingUIManager::InvokeOnBlockingPageComplete,
+                 ui_manager_, resource.callback));
+}
+
 TEST_F(ClientSideDetectionHostTest, NavigationCancelsShouldClassifyUrl) {
   // Test that canceling pending should classify requests works as expected.
 
diff --git a/chrome/browser/safe_browsing/client_side_detection_service.cc b/chrome/browser/safe_browsing/client_side_detection_service.cc
index 0f20535..bd73045 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service.cc
@@ -83,6 +83,12 @@
   GURL phishing_url;
 };
 
+struct ClientSideDetectionService::ClientMalwareReportInfo {
+  ClientReportMalwareRequestCallback callback;
+  // This is the original landing url, may not be the malware url.
+  GURL original_url;
+};
+
 ClientSideDetectionService::CacheState::CacheState(bool phish, base::Time time)
     : is_phishing(phish),
       timestamp(time) {}
@@ -146,12 +152,12 @@
     STLDeleteContainerPairPointers(client_phishing_reports_.begin(),
                                    client_phishing_reports_.end());
     client_phishing_reports_.clear();
-    for (std::map<const net::URLFetcher*, ClientReportInfo*>::iterator it =
-             client_malware_reports_.begin();
+    for (std::map<const net::URLFetcher*, ClientMalwareReportInfo*>::iterator it
+             = client_malware_reports_.begin();
          it != client_malware_reports_.end(); ++it) {
-      ClientReportInfo* info = it->second;
+      ClientMalwareReportInfo* info = it->second;
       if (!info->callback.is_null())
-        info->callback.Run(info->phishing_url, false);
+        info->callback.Run(info->original_url, info->original_url, false);
     }
     STLDeleteContainerPairPointers(client_malware_reports_.begin(),
                                    client_malware_reports_.end());
@@ -386,7 +392,7 @@
 
   if (!enabled_) {
     if (!callback.is_null())
-      callback.Run(GURL(request->url()), false);
+      callback.Run(GURL(request->url()), GURL(request->url()), false);
     return;
   }
 
@@ -395,7 +401,7 @@
     DVLOG(1) << "Too many malware report requests sent recently."
              << "Skip sending malware report for " << GURL(request->url());
     if (!callback.is_null())
-      callback.Run(GURL(request->url()), false);
+      callback.Run(GURL(request->url()), GURL(request->url()), false);
     return;
   }
 
@@ -404,7 +410,7 @@
     UpdateEnumUMAHistogram(REPORT_FAILED_SERIALIZATION);
     DVLOG(1) << "Unable to serialize the CSD request. Proto file changed?";
     if (!callback.is_null())
-      callback.Run(GURL(request->url()), false);
+      callback.Run(GURL(request->url()), GURL(request->url()), false);
     return;
   }
 
@@ -414,10 +420,9 @@
       net::URLFetcher::POST, this);
 
   // Remember which callback and URL correspond to the current fetcher object.
-  // TODO: need to modify to malware specific code
-  ClientReportInfo* info = new ClientReportInfo;
+  ClientMalwareReportInfo* info = new ClientMalwareReportInfo;
   info->callback = callback;
-  info->phishing_url = GURL(request->url());
+  info->original_url = GURL(request->url());
   client_malware_reports_[fetcher] = info;
 
   fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE);
@@ -508,18 +513,25 @@
     const net::ResponseCookies& cookies,
     const std::string& data) {
   ClientMalwareResponse response;
-  scoped_ptr<ClientReportInfo> info(client_malware_reports_[source]);
+  scoped_ptr<ClientMalwareReportInfo> info(client_malware_reports_[source]);
   bool should_blacklist = false;
   if (status.is_success() && net::HTTP_OK == response_code &&
       response.ParseFromString(data)) {
     should_blacklist = response.blacklist();
   } else {
     DLOG(ERROR) << "Unable to get the server verdict for URL: "
-                << info->phishing_url << " status: " << status.status() << " "
+                << info->original_url << " status: " << status.status() << " "
                 << "response_code:" << response_code;
   }
-  if (!info->callback.is_null())
-    info->callback.Run(info->phishing_url, should_blacklist);
+
+  if (!info->callback.is_null()) {
+    if (response.has_bad_url())
+      info->callback.Run(info->original_url, GURL(response.bad_url()),
+                         should_blacklist);
+    else
+      info->callback.Run(info->original_url, info->original_url, false);
+  }
+
   client_malware_reports_.erase(source);
   delete source;
 }
diff --git a/chrome/browser/safe_browsing/client_side_detection_service.h b/chrome/browser/safe_browsing/client_side_detection_service.h
index ccc3c66..e442de6 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service.h
+++ b/chrome/browser/safe_browsing/client_side_detection_service.h
@@ -62,7 +62,9 @@
  public:
   // void(GURL phishing_url, bool is_phishing).
   typedef base::Callback<void(GURL, bool)> ClientReportPhishingRequestCallback;
-  typedef base::Callback<void(GURL, bool)> ClientReportMalwareRequestCallback;
+  // void(GURL original_url, GURL malware_url, bool is_malware).
+  typedef base::Callback<void(GURL, GURL, bool)>
+      ClientReportMalwareRequestCallback;
 
   virtual ~ClientSideDetectionService();
 
@@ -300,7 +302,10 @@
   struct ClientReportInfo;
   std::map<const net::URLFetcher*, ClientReportInfo*>
       client_phishing_reports_;
-  std::map<const net::URLFetcher*, ClientReportInfo*>
+  // Map of client malware ip request to the corresponding callback that
+  // has to be invoked when the request is done.
+  struct ClientMalwareReportInfo;
+  std::map<const net::URLFetcher*, ClientMalwareReportInfo*>
       client_malware_reports_;
 
   // Cache of completed requests. Used to satisfy requests for the same urls
diff --git a/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
index f6f941e..70a18ed 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
@@ -213,6 +213,10 @@
     feature->set_value(value);
   }
 
+  void CheckConfirmedMalwareUrl(GURL url) {
+    ASSERT_EQ(confirmed_malware_url_, url);
+  }
+
  protected:
   scoped_ptr<ClientSideDetectionService> csd_service_;
   scoped_ptr<net::FakeURLFetcherFactory> factory_;
@@ -225,15 +229,19 @@
     msg_loop_.Quit();
   }
 
-  void SendMalwareRequestDone(GURL url, bool is_malware) {
-    ASSERT_EQ(phishing_url_, url);
+  void SendMalwareRequestDone(GURL original_url, GURL malware_url,
+                              bool is_malware) {
+    ASSERT_EQ(phishing_url_, original_url);
+    confirmed_malware_url_ = malware_url;
     is_malware_ = is_malware;
     msg_loop_.Quit();
   }
+
   scoped_ptr<content::TestBrowserThread> browser_thread_;
   scoped_ptr<content::TestBrowserThread> file_thread_;
 
   GURL phishing_url_;
+  GURL confirmed_malware_url_;
   bool is_phishing_;
   bool is_malware_;
 };
@@ -418,16 +426,22 @@
   GURL url("http://a.com/");
 
   base::Time before = base::Time::Now();
-
   // Invalid response body from the server.
   SetClientReportMalwareResponse("invalid proto response", true /* success */);
   EXPECT_FALSE(SendClientReportMalwareRequest(url));
 
-  // Normal behavior.
+  // Missing bad_url.
   ClientMalwareResponse response;
   response.set_blacklist(true);
   SetClientReportMalwareResponse(response.SerializeAsString(), true);
+  EXPECT_FALSE(SendClientReportMalwareRequest(url));
+
+  // Normal behavior.
+  response.set_blacklist(true);
+  response.set_bad_url("http://response-bad.com/");
+  SetClientReportMalwareResponse(response.SerializeAsString(), true);
   EXPECT_TRUE(SendClientReportMalwareRequest(url));
+  CheckConfirmedMalwareUrl(GURL("http://response-bad.com/"));
 
   // This request will fail
   response.set_blacklist(false);
diff --git a/chrome/browser/safe_browsing/database_manager.cc b/chrome/browser/safe_browsing/database_manager.cc
index db0963a..ac606f2 100644
--- a/chrome/browser/safe_browsing/database_manager.cc
+++ b/chrome/browser/safe_browsing/database_manager.cc
@@ -193,7 +193,7 @@
 bool SafeBrowsingDatabaseManager::CanCheckUrl(const GURL& url) const {
   return url.SchemeIs(chrome::kFtpScheme) ||
          url.SchemeIs(chrome::kHttpScheme) ||
-         url.SchemeIs(chrome::kHttpsScheme);
+         url.SchemeIs(content::kHttpsScheme);
 }
 
 bool SafeBrowsingDatabaseManager::CheckDownloadUrl(
diff --git a/chrome/browser/safe_browsing/ping_manager.cc b/chrome/browser/safe_browsing/ping_manager.cc
index 89d88ad..41404d0 100644
--- a/chrome/browser/safe_browsing/ping_manager.cc
+++ b/chrome/browser/safe_browsing/ping_manager.cc
@@ -103,7 +103,8 @@
          threat_type == SB_THREAT_TYPE_URL_PHISHING ||
          threat_type == SB_THREAT_TYPE_BINARY_MALWARE_URL ||
          threat_type == SB_THREAT_TYPE_BINARY_MALWARE_HASH ||
-         threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL);
+         threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL ||
+         threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL);
   std::string url = SafeBrowsingProtocolManagerHelper::ComposeUrl(
       url_prefix_, "report", client_name_, version_, std::string());
   std::string threat_list = "none";
@@ -123,6 +124,9 @@
     case SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL:
       threat_list = "phishcsdhit";
       break;
+    case SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL:
+      threat_list = "malcsdhit";
+      break;
     default:
       NOTREACHED();
   }
diff --git a/chrome/browser/safe_browsing/ping_manager_unittest.cc b/chrome/browser/safe_browsing/ping_manager_unittest.cc
index 585b31b..c40fca3 100644
--- a/chrome/browser/safe_browsing/ping_manager_unittest.cc
+++ b/chrome/browser/safe_browsing/ping_manager_unittest.cc
@@ -87,6 +87,15 @@
             pm.SafeBrowsingHitUrl(
                 malicious_url, page_url, referrer_url,
                 false, SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL).spec());
+
+    EXPECT_EQ("https://prefix.com/foo/report?client=unittest&appver=1.0&"
+            "pver=2.2" + key_param_ + "&evts=malcsdhit&"
+            "evtd=http%3A%2F%2Fmalicious.url.com%2F&"
+            "evtr=http%3A%2F%2Fpage.url.com%2F&evhr=http%3A%2F%2Freferrer."
+            "url.com%2F&evtb=1",
+            pm.SafeBrowsingHitUrl(
+                malicious_url, page_url, referrer_url,
+                true, SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL).spec());
 }
 
 TEST_F(SafeBrowsingPingManagerTest, TestMalwareDetailsUrl) {
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc
index c93be27..51e7536 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page.cc
@@ -156,7 +156,11 @@
     // V2 yet.
     if (unsafe_resources.size() == 1 &&
         (unsafe_resources[0].threat_type == SB_THREAT_TYPE_URL_MALWARE ||
-         unsafe_resources[0].threat_type == SB_THREAT_TYPE_URL_PHISHING)) {
+         unsafe_resources[0].threat_type ==
+         SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL ||
+         unsafe_resources[0].threat_type == SB_THREAT_TYPE_URL_PHISHING ||
+         unsafe_resources[0].threat_type ==
+         SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL)) {
       return new SafeBrowsingBlockingPageV2(ui_manager, web_contents,
           unsafe_resources);
     }
@@ -196,7 +200,8 @@
        iter != unsafe_resources_.end(); ++iter) {
     const UnsafeResource& resource = *iter;
     SBThreatType threat_type = resource.threat_type;
-    if (threat_type == SB_THREAT_TYPE_URL_MALWARE) {
+    if (threat_type == SB_THREAT_TYPE_URL_MALWARE ||
+        threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL) {
       malware = true;
     } else {
       DCHECK(threat_type == SB_THREAT_TYPE_URL_PHISHING ||
@@ -264,7 +269,8 @@
     // User pressed "Learn more".
     GURL url;
     SBThreatType threat_type = unsafe_resources_[0].threat_type;
-    if (threat_type == SB_THREAT_TYPE_URL_MALWARE) {
+    if (threat_type == SB_THREAT_TYPE_URL_MALWARE ||
+        threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL) {
       url = google_util::AppendGoogleLocaleParam(GURL(kLearnMoreMalwareUrl));
     } else if (threat_type == SB_THREAT_TYPE_URL_PHISHING ||
                threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL) {
@@ -283,7 +289,8 @@
     // User pressed "Learn more".
     GURL url;
     SBThreatType threat_type = unsafe_resources_[0].threat_type;
-    if (threat_type == SB_THREAT_TYPE_URL_MALWARE) {
+    if (threat_type == SB_THREAT_TYPE_URL_MALWARE ||
+        threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL) {
       url = google_util::AppendGoogleLocaleParam(GURL(kLearnMoreMalwareUrlV2));
     } else if (threat_type == SB_THREAT_TYPE_URL_PHISHING ||
                threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL) {
@@ -390,7 +397,9 @@
     GURL diagnostic_url(diagnostic);
     diagnostic_url = google_util::AppendGoogleLocaleParam(diagnostic_url);
     DCHECK(unsafe_resources_[element_index].threat_type ==
-           SB_THREAT_TYPE_URL_MALWARE);
+           SB_THREAT_TYPE_URL_MALWARE ||
+           unsafe_resources_[element_index].threat_type ==
+           SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL);
     OpenURLParams params(
         diagnostic_url, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_LINK,
         false);
@@ -848,7 +857,8 @@
     const UnsafeResource& resource = *iter;
     SBThreatType threat_type = resource.threat_type;
     DictionaryValue* current_error_strings = new DictionaryValue;
-    if (threat_type == SB_THREAT_TYPE_URL_MALWARE) {
+    if (threat_type == SB_THREAT_TYPE_URL_MALWARE ||
+        threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL) {
       current_error_strings->SetString("type", "malware");
       current_error_strings->SetString("typeLabel", malware_label);
       current_error_strings->SetString("errorLink", malware_link);
@@ -930,7 +940,9 @@
     WebContents* web_contents,
     const UnsafeResourceList& unsafe_resources)
   : SafeBrowsingBlockingPage(ui_manager, web_contents, unsafe_resources) {
-    if (unsafe_resources_[0].threat_type == SB_THREAT_TYPE_URL_MALWARE) {
+    if (unsafe_resources_[0].threat_type == SB_THREAT_TYPE_URL_MALWARE ||
+        unsafe_resources_[0].threat_type ==
+        SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL) {
       trialCondition_ =
           base::FieldTrialList::FindFullName(kMalwareStudyName);
     } else if (unsafe_resources_[0].threat_type ==
@@ -959,7 +971,8 @@
     NOTREACHED();
   } else {
     SBThreatType threat_type = unsafe_resources_[0].threat_type;
-    if (threat_type == SB_THREAT_TYPE_URL_MALWARE) {
+    if (threat_type == SB_THREAT_TYPE_URL_MALWARE ||
+        threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL) {
       PopulateMalwareStringDictionary(&strings);
     } else {  // Phishing.
       DCHECK(threat_type == SB_THREAT_TYPE_URL_PHISHING ||
diff --git a/chrome/browser/safe_browsing/safe_browsing_util.h b/chrome/browser/safe_browsing/safe_browsing_util.h
index ec41959..3416422 100644
--- a/chrome/browser/safe_browsing/safe_browsing_util.h
+++ b/chrome/browser/safe_browsing/safe_browsing_util.h
@@ -151,6 +151,10 @@
 
   // The Chrome extension or app (given by its ID) is malware.
   SB_THREAT_TYPE_EXTENSION,
+
+  // Url detected by the client-side malware IP list. This IP list is part
+  // of the client side detection model.
+  SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL,
 };
 
 // SBEntry ---------------------------------------------------------------------
diff --git a/chrome/browser/safe_browsing/ui_manager.cc b/chrome/browser/safe_browsing/ui_manager.cc
index 3b3ba70..241930d 100644
--- a/chrome/browser/safe_browsing/ui_manager.cc
+++ b/chrome/browser/safe_browsing/ui_manager.cc
@@ -269,15 +269,19 @@
     const WhiteListedEntry& entry = white_listed_entries_[i];
     if (entry.render_process_host_id == resource.render_process_host_id &&
         entry.render_view_id == resource.render_view_id &&
-        // Threat type must be the same or in the case of phishing they can
-        // either be client-side phishing URL or a SafeBrowsing phishing URL.
-        // If we show one type of phishing warning we don't want to show a
-        // second phishing warning.
+        // Threat type must be the same or they can either be client-side
+        // phishing/malware URL or a SafeBrowsing phishing/malware URL.
+        // If we show one type of phishing/malware warning we don't want to show
+        // a second phishing/malware warning.
         (entry.threat_type == resource.threat_type ||
          (entry.threat_type == SB_THREAT_TYPE_URL_PHISHING &&
           resource.threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL) ||
          (entry.threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL &&
-          resource.threat_type == SB_THREAT_TYPE_URL_PHISHING))) {
+          resource.threat_type == SB_THREAT_TYPE_URL_PHISHING) ||
+         (entry.threat_type == SB_THREAT_TYPE_URL_MALWARE &&
+          resource.threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL) ||
+         (entry.threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL &&
+          resource.threat_type == SB_THREAT_TYPE_URL_MALWARE))) {
       return entry.domain ==
           net::registry_controlled_domains::GetDomainAndRegistry(
               resource.url,
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index 196c8a3..47d36d3 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -332,21 +332,17 @@
   theme_info_->header_color = SkColorToRGBAColor(header_color);
   theme_info_->section_border_color = SkColorToRGBAColor(section_border_color);
 
-  // Set logo for the theme. By default, use alternate logo.
-  theme_info_->logo_alternate = true;
-  int logo_alternate = 0;
-  if (theme_service->GetDisplayProperty(
-      ThemeProperties::NTP_LOGO_ALTERNATE, &logo_alternate))
-    theme_info_->logo_alternate = logo_alternate == 1;
+  int logo_alternate = theme_service->GetDisplayProperty(
+      ThemeProperties::NTP_LOGO_ALTERNATE);
+  theme_info_->logo_alternate = logo_alternate == 1;
 
   if (theme_service->HasCustomImage(IDR_THEME_NTP_BACKGROUND)) {
     // Set theme id for theme background image url.
     theme_info_->theme_id = theme_service->GetThemeID();
 
     // Set theme background image horizontal alignment.
-    int alignment = 0;
-    theme_service->GetDisplayProperty(
-        ThemeProperties::NTP_BACKGROUND_ALIGNMENT, &alignment);
+    int alignment = theme_service->GetDisplayProperty(
+        ThemeProperties::NTP_BACKGROUND_ALIGNMENT);
     if (alignment & ThemeProperties::ALIGN_LEFT)
       theme_info_->image_horizontal_alignment = THEME_BKGRND_IMAGE_ALIGN_LEFT;
     else if (alignment & ThemeProperties::ALIGN_RIGHT)
@@ -363,9 +359,8 @@
       theme_info_->image_vertical_alignment = THEME_BKGRND_IMAGE_ALIGN_CENTER;
 
     // Set theme backgorund image tiling.
-    int tiling = 0;
-    theme_service->GetDisplayProperty(ThemeProperties::NTP_BACKGROUND_TILING,
-                                      &tiling);
+    int tiling = theme_service->GetDisplayProperty(
+        ThemeProperties::NTP_BACKGROUND_TILING);
     switch (tiling) {
       case ThemeProperties::NO_REPEAT:
         theme_info_->image_tiling = THEME_BKGRND_IMAGE_NO_REPEAT;
diff --git a/chrome/browser/search/search.cc b/chrome/browser/search/search.cc
index f42fafb..de725fb 100644
--- a/chrome/browser/search/search.cc
+++ b/chrome/browser/search/search.cc
@@ -63,6 +63,7 @@
 const char kShowNtpFlagName[] = "show_ntp";
 const char kRecentTabsOnNTPFlagName[] = "show_recent_tabs";
 const char kUseCacheableNTP[] = "use_cacheable_ntp";
+const char kPrefetchSearchResultsOnSRP[] = "prefetch_results_srp";
 
 // Constants for the field trial name and group prefix.
 const char kInstantExtendedFieldTrialName[] = "InstantExtended";
@@ -140,7 +141,7 @@
   return my_url.host() == other_url.host() &&
          my_url.port() == other_url.port() &&
          (my_url.scheme() == other_url.scheme() ||
-          (my_url.SchemeIs(chrome::kHttpsScheme) &&
+          (my_url.SchemeIs(content::kHttpsScheme) &&
            other_url.SchemeIs(chrome::kHttpScheme)));
 }
 
@@ -430,7 +431,7 @@
       google_util::StartsWithCommandLineGoogleBaseURL(instant_url))
     return instant_url;
   GURL::Replacements replacements;
-  const std::string secure_scheme(chrome::kHttpsScheme);
+  const std::string secure_scheme(content::kHttpsScheme);
   replacements.SetSchemeStr(secure_scheme);
   return instant_url.ReplaceComponents(replacements);
 }
@@ -632,6 +633,25 @@
   return StringToInstantSupportState(value);
 }
 
+bool ShouldPrefetchSearchResultsOnSRP() {
+  // Check the command-line/about:flags setting first, which should have
+  // precedence and allows the trial to not be reported (if it's never queried).
+  const CommandLine* command_line = CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kDisableInstantExtendedAPI) ||
+      command_line->HasSwitch(switches::kEnableInstantExtendedAPI)) {
+    return false;
+  }
+
+  FieldTrialFlags flags;
+  if (GetFieldTrialInfo(
+          base::FieldTrialList::FindFullName(kInstantExtendedFieldTrialName),
+          &flags, NULL)) {
+    return GetBoolValueForFlagWithDefault(kPrefetchSearchResultsOnSRP, false,
+                                          flags);
+  }
+  return false;
+}
+
 void EnableInstantExtendedAPIForTesting() {
   CommandLine* cl = CommandLine::ForCurrentProcess();
   cl->AppendSwitch(switches::kEnableInstantExtendedAPI);
diff --git a/chrome/browser/search/search.h b/chrome/browser/search/search.h
index 5888529..716fa95 100644
--- a/chrome/browser/search/search.h
+++ b/chrome/browser/search/search.h
@@ -186,6 +186,9 @@
 InstantSupportState GetInstantSupportStateFromNavigationEntry(
     const content::NavigationEntry& entry);
 
+// Returns true if the field trial flag is enabled to prefetch results on SRP.
+bool ShouldPrefetchSearchResultsOnSRP();
+
 // -----------------------------------------------------
 // The following APIs are exposed for use in tests only.
 // -----------------------------------------------------
diff --git a/chrome/browser/search/search_unittest.cc b/chrome/browser/search/search_unittest.cc
index 35de837..a93532f 100644
--- a/chrome/browser/search/search_unittest.cc
+++ b/chrome/browser/search/search_unittest.cc
@@ -16,11 +16,11 @@
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/metrics/entropy_provider.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/variations/entropy_provider.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/site_instance.h"
diff --git a/chrome/browser/search_engines/prepopulated_engines.json b/chrome/browser/search_engines/prepopulated_engines.json
index e0a8daa..2f4075f 100644
--- a/chrome/browser/search_engines/prepopulated_engines.json
+++ b/chrome/browser/search_engines/prepopulated_engines.json
@@ -26,7 +26,7 @@
 
     // Increment this if you change the data in ways that mean users with
     // existing data should get a new version.
-    "kCurrentDataVersion": 62
+    "kCurrentDataVersion": 63
   },
 
   // The following engines are included in country lists and are added to the
@@ -491,7 +491,7 @@
       "instant_url": "{google:baseURL}webhp?sourceid=chrome-instant&{google:RLZ}{google:instantEnabledParameter}{google:instantExtendedEnabledParameter}{google:ntpIsThemedParameter}{google:omniboxStartMarginParameter}ie={inputEncoding}",
       "image_url": "{google:baseURL}searchbyimage/upload",
       "new_tab_url": "{google:baseURL}newtab?{google:RLZ}{google:instantExtendedEnabledParameter}{google:ntpIsThemedParameter}ie={inputEncoding}",
-      "image_url_post_params": "encoded_image={google:imageThumbnail},image_url={google:imageURL},sbisrc={google:imageSearchSource}",
+      "image_url_post_params": "encoded_image={google:imageThumbnail},image_url={google:imageURL},sbisrc={google:imageSearchSource},original_width={google:imageOriginalWidth},original_height={google:imageOriginalHeight}",
       "alternate_urls": [
         "{google:baseURL}#q={searchTerms}",
         "{google:baseURL}search#q={searchTerms}",
diff --git a/chrome/browser/search_engines/template_url.cc b/chrome/browser/search_engines/template_url.cc
index a0a7c63..7d964e4 100644
--- a/chrome/browser/search_engines/template_url.cc
+++ b/chrome/browser/search_engines/template_url.cc
@@ -90,11 +90,10 @@
     "{google:unescapedSearchTerms}";
 
 const char kGoogleImageSearchSource[] = "google:imageSearchSource";
-const char kGoogleImageSearchSourceFull[] = "{google:imageSearchSource}";
 const char kGoogleImageThumbnailParameter[] = "google:imageThumbnail";
-const char kGoogleImageThumbnailParameterFull[] = "{google:imageThumbnail}";
 const char kGoogleImageURLParameter[] = "google:imageURL";
-const char kGoogleImageURLParameterFull[] = "{google:imageURL}";
+const char kGoogleImageOriginalWidth[] = "google:imageOriginalWidth";
+const char kGoogleImageOriginalHeight[] = "google:imageOriginalHeight";
 
 // Display value for kSearchTermsParameter.
 const char kDisplaySearchTerms[] = "%s";
@@ -531,18 +530,6 @@
   } else if (parameter == kCountParameter) {
     if (!optional)
       url->insert(start, kDefaultCount);
-  } else if ((parameter == kStartIndexParameter) ||
-             (parameter == kStartPageParameter)) {
-    // We don't support these.
-    if (!optional)
-      url->insert(start, "1");
-  } else if (parameter == kLanguageParameter) {
-    replacements->push_back(Replacement(LANGUAGE, start));
-  } else if (parameter == kInputEncodingParameter) {
-    replacements->push_back(Replacement(ENCODING, start));
-  } else if (parameter == kOutputEncodingParameter) {
-    if (!optional)
-      url->insert(start, kOutputEncodingType);
   } else if (parameter == kGoogleAssistedQueryStatsParameter) {
     replacements->push_back(Replacement(GOOGLE_ASSISTED_QUERY_STATS, start));
   } else if (parameter == kGoogleBaseURLParameter) {
@@ -551,6 +538,20 @@
     replacements->push_back(Replacement(GOOGLE_BASE_SUGGEST_URL, start));
   } else if (parameter == kGoogleCursorPositionParameter) {
     replacements->push_back(Replacement(GOOGLE_CURSOR_POSITION, start));
+  } else if (parameter == kGoogleImageOriginalHeight) {
+    replacements->push_back(
+        Replacement(TemplateURLRef::GOOGLE_IMAGE_ORIGINAL_HEIGHT, start));
+  } else if (parameter == kGoogleImageOriginalWidth) {
+    replacements->push_back(
+        Replacement(TemplateURLRef::GOOGLE_IMAGE_ORIGINAL_WIDTH, start));
+  } else if (parameter == kGoogleImageSearchSource) {
+    url->insert(start, GetGoogleImageSearchSource());
+  } else if (parameter == kGoogleImageThumbnailParameter) {
+    replacements->push_back(
+        Replacement(TemplateURLRef::GOOGLE_IMAGE_THUMBNAIL, start));
+  } else if (parameter == kGoogleImageURLParameter) {
+    replacements->push_back(Replacement(TemplateURLRef::GOOGLE_IMAGE_URL,
+                                        start));
   } else if (parameter == kGoogleInstantEnabledParameter) {
     replacements->push_back(Replacement(GOOGLE_INSTANT_ENABLED, start));
   } else if (parameter == kGoogleInstantExtendedEnabledParameter) {
@@ -573,29 +574,33 @@
     replacements->push_back(Replacement(GOOGLE_SEARCH_CLIENT, start));
   } else if (parameter == kGoogleSearchFieldtrialParameter) {
     replacements->push_back(Replacement(GOOGLE_SEARCH_FIELDTRIAL_GROUP, start));
-  } else if (parameter == kGoogleSuggestClient) {
-    replacements->push_back(Replacement(GOOGLE_SUGGEST_CLIENT, start));
-  } else if (parameter == kGoogleZeroPrefixUrlParameter) {
-    replacements->push_back(Replacement(GOOGLE_ZERO_PREFIX_URL, start));
-  } else if (parameter == kGoogleSuggestAPIKeyParameter) {
-    url->insert(start,
-                net::EscapeQueryParamValue(google_apis::GetAPIKey(), false));
   } else if (parameter == kGoogleSourceIdParameter) {
 #if defined(OS_ANDROID)
     url->insert(start, "sourceid=chrome-mobile&");
 #else
     url->insert(start, "sourceid=chrome&");
 #endif
+  } else if (parameter == kGoogleSuggestAPIKeyParameter) {
+    url->insert(start,
+                net::EscapeQueryParamValue(google_apis::GetAPIKey(), false));
+  } else if (parameter == kGoogleSuggestClient) {
+    replacements->push_back(Replacement(GOOGLE_SUGGEST_CLIENT, start));
   } else if (parameter == kGoogleUnescapedSearchTermsParameter) {
     replacements->push_back(Replacement(GOOGLE_UNESCAPED_SEARCH_TERMS, start));
-  } else if (parameter == kGoogleImageSearchSource) {
-    url->insert(start, GetGoogleImageSearchSource());
-  } else if (parameter == kGoogleImageThumbnailParameter) {
-    replacements->push_back(
-        Replacement(TemplateURLRef::GOOGLE_IMAGE_THUMBNAIL, start));
-  } else if (parameter == kGoogleImageURLParameter) {
-    replacements->push_back(Replacement(TemplateURLRef::GOOGLE_IMAGE_URL,
-                                        start));
+  } else if (parameter == kGoogleZeroPrefixUrlParameter) {
+    replacements->push_back(Replacement(GOOGLE_ZERO_PREFIX_URL, start));
+  } else if (parameter == kInputEncodingParameter) {
+    replacements->push_back(Replacement(ENCODING, start));
+  } else if (parameter == kLanguageParameter) {
+    replacements->push_back(Replacement(LANGUAGE, start));
+  } else if (parameter == kOutputEncodingParameter) {
+    if (!optional)
+      url->insert(start, kOutputEncodingType);
+  } else if ((parameter == kStartIndexParameter) ||
+             (parameter == kStartPageParameter)) {
+    // We don't support these.
+    if (!optional)
+      url->insert(start, "1");
   } else if (!prepopulated_) {
     // If it's a prepopulated URL, we know that it's safe to remove unknown
     // parameters, so just ignore this and return true below. Otherwise it could
@@ -803,7 +808,7 @@
           search_terms_args_without_aqs.assisted_query_stats.clear();
           GURL base_url(ReplaceSearchTermsUsingTermsData(
               search_terms_args_without_aqs, search_terms_data, NULL));
-          if (base_url.SchemeIs(chrome::kHttpsScheme)) {
+          if (base_url.SchemeIs(content::kHttpsScheme)) {
             HandleReplacement(
                 "aqs", search_terms_args.assisted_query_stats, *i, &url);
           }
@@ -955,6 +960,24 @@
         }
         break;
 
+      case GOOGLE_IMAGE_ORIGINAL_WIDTH:
+        if (!search_terms_args.image_original_size.IsEmpty()) {
+          HandleReplacement(
+              std::string(),
+              base::IntToString(search_terms_args.image_original_size.width()),
+              *i, &url);
+        }
+        break;
+
+      case GOOGLE_IMAGE_ORIGINAL_HEIGHT:
+        if (!search_terms_args.image_original_size.IsEmpty()) {
+          HandleReplacement(
+              std::string(),
+              base::IntToString(search_terms_args.image_original_size.height()),
+              *i, &url);
+        }
+        break;
+
       default:
         NOTREACHED();
         break;
diff --git a/chrome/browser/search_engines/template_url.h b/chrome/browser/search_engines/template_url.h
index 0a06d03..1798e1a 100644
--- a/chrome/browser/search_engines/template_url.h
+++ b/chrome/browser/search_engines/template_url.h
@@ -13,6 +13,7 @@
 #include "base/time/time.h"
 #include "chrome/browser/autocomplete/autocomplete_input.h"
 #include "chrome/browser/search_engines/template_url_id.h"
+#include "ui/gfx/size.h"
 #include "url/gurl.h"
 #include "url/url_parse.h"
 
@@ -116,6 +117,9 @@
     // When searching for an image, the URL of the original image. Callers
     // should leave this empty for images specified via data: URLs.
     GURL image_url;
+
+    // When searching for an image, the original size of the image.
+    gfx::Size image_original_size;
   };
 
   TemplateURLRef(TemplateURL* owner, Type type);
@@ -235,6 +239,8 @@
     GOOGLE_BASE_URL,
     GOOGLE_BASE_SUGGEST_URL,
     GOOGLE_CURSOR_POSITION,
+    GOOGLE_IMAGE_ORIGINAL_HEIGHT,
+    GOOGLE_IMAGE_ORIGINAL_WIDTH,
     GOOGLE_IMAGE_SEARCH_SOURCE,
     GOOGLE_IMAGE_THUMBNAIL,
     GOOGLE_IMAGE_URL,
diff --git a/chrome/browser/search_engines/template_url_parser.cc b/chrome/browser/search_engines/template_url_parser.cc
index 64978a7..9fa4c4a 100644
--- a/chrome/browser/search_engines/template_url_parser.cc
+++ b/chrome/browser/search_engines/template_url_parser.cc
@@ -101,7 +101,7 @@
     return true;
   GURL gurl(url);
   return gurl.is_valid() && (gurl.SchemeIs(chrome::kHttpScheme) ||
-                             gurl.SchemeIs(chrome::kHttpsScheme));
+                             gurl.SchemeIs(content::kHttpsScheme));
 }
 
 }  // namespace
@@ -259,7 +259,7 @@
         context->derive_image_from_url_ = true;
       } else if (context->image_is_valid_for_favicon_ && image_url.is_valid() &&
                  (image_url.SchemeIs(chrome::kHttpScheme) ||
-                  image_url.SchemeIs(chrome::kHttpsScheme))) {
+                  image_url.SchemeIs(content::kHttpsScheme))) {
         context->data_.favicon_url = image_url;
       }
       context->image_is_valid_for_favicon_ = false;
diff --git a/chrome/browser/search_engines/template_url_service.cc b/chrome/browser/search_engines/template_url_service.cc
index 55df617..a8e7dcf 100644
--- a/chrome/browser/search_engines/template_url_service.cc
+++ b/chrome/browser/search_engines/template_url_service.cc
@@ -374,7 +374,7 @@
     if (result.compare(0, scheme_component.end(),
                        ASCIIToUTF16(chrome::kHttpScheme)) &&
         result.compare(0, scheme_component.end(),
-                       ASCIIToUTF16(chrome::kHttpsScheme)))
+                       ASCIIToUTF16(content::kHttpsScheme)))
       return string16();
 
     // Include trailing ':'.
diff --git a/chrome/browser/search_engines/template_url_unittest.cc b/chrome/browser/search_engines/template_url_unittest.cc
index 7dbff02..5882e5d 100644
--- a/chrome/browser/search_engines/template_url_unittest.cc
+++ b/chrome/browser/search_engines/template_url_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "base/base_paths.h"
 #include "base/command_line.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
@@ -175,7 +176,7 @@
   const char kValidPostParamsString[] =
       "image_content={google:imageThumbnail},image_url={google:imageURL},"
       "sbisrc={google:imageSearchSource},language={language},empty_param=,"
-      "constant_param=constant";
+      "constant_param=constant,width={google:imageOriginalWidth}";
   const char KImageSearchURL[] = "http://foo.com/sbi";
 
   TemplateURLData data;
@@ -203,6 +204,7 @@
   TemplateURLRef::SearchTermsArgs search_args(ASCIIToUTF16("X"));
   search_args.image_thumbnail_content = "dummy-image-thumbnail";
   search_args.image_url = GURL("http://dummyimage.com/dummy.jpg");
+  search_args.image_original_size = gfx::Size(10, 10);
   // Replacement operation with no post_data buffer should still return
   // the parsed URL.
   GURL result(url.image_url_ref().ReplaceSearchTerms(search_args));
@@ -222,7 +224,7 @@
       url.image_url_ref().replacements_;
   const TemplateURLRef::PostParams& post_params =
       url.image_url_ref().post_params_;
-  EXPECT_EQ(6U, post_params.size());
+  EXPECT_EQ(7U, post_params.size());
   for (TemplateURLRef::PostParams::const_iterator i = post_params.begin();
        i != post_params.end(); ++i) {
     TemplateURLRef::Replacements::const_iterator j = replacements.begin();
@@ -230,6 +232,12 @@
       if (j->is_post_param && j->index ==
           static_cast<size_t>(i - post_params.begin())) {
         switch (j->type) {
+          case TemplateURLRef::GOOGLE_IMAGE_ORIGINAL_WIDTH:
+            EXPECT_EQ("width", i->first);
+            EXPECT_EQ(
+                base::IntToString(search_args.image_original_size.width()),
+                i->second);
+            break;
           case TemplateURLRef::GOOGLE_IMAGE_THUMBNAIL:
             EXPECT_EQ("image_content", i->first);
             EXPECT_EQ(search_args.image_thumbnail_content, i->second);
@@ -1187,7 +1195,7 @@
   EXPECT_EQ("http://www.google.com/?pgcl=1&q=foo", result);
 
   search_terms_args.page_classification =
-      AutocompleteInput::HOMEPAGE;
+      AutocompleteInput::HOME_PAGE;
   result = url.url_ref().ReplaceSearchTerms(search_terms_args);
   EXPECT_EQ("http://www.google.com/?pgcl=3&q=foo", result);
 }
diff --git a/chrome/browser/sessions/persistent_tab_restore_service_browsertest.cc b/chrome/browser/sessions/persistent_tab_restore_service_browsertest.cc
index 8dfb56b..4c2d3d9 100644
--- a/chrome/browser/sessions/persistent_tab_restore_service_browsertest.cc
+++ b/chrome/browser/sessions/persistent_tab_restore_service_browsertest.cc
@@ -624,6 +624,50 @@
   }
 }
 
+// Makes sure we restore status codes correctly.
+TEST_F(PersistentTabRestoreServiceTest, StatusCodesSurviveRestore) {
+  AddThreeNavigations();
+
+  // Have the service record the tab.
+  service_->CreateHistoricalTab(web_contents(), -1);
+
+  // Make sure an entry was created.
+  ASSERT_EQ(1U, service_->entries().size());
+
+  // Make sure the entry matches.
+  std::vector<sessions::SerializedNavigationEntry> old_navigations;
+  {
+    // |entry|/|tab| doesn't survive after RecreateService().
+    TabRestoreService::Entry* entry = service_->entries().front();
+    ASSERT_EQ(TabRestoreService::TAB, entry->type);
+    Tab* tab = static_cast<Tab*>(entry);
+    old_navigations = tab->navigations;
+  }
+
+  EXPECT_EQ(3U, old_navigations.size());
+  for (size_t i = 0; i < old_navigations.size(); ++i) {
+    EXPECT_EQ(200, old_navigations[i].http_status_code());
+  }
+
+  // Set this, otherwise previous session won't be loaded.
+  profile()->set_last_session_exited_cleanly(false);
+
+  RecreateService();
+
+  // One entry should be created.
+  ASSERT_EQ(1U, service_->entries().size());
+
+  // And verify the entry.
+  TabRestoreService::Entry* restored_entry = service_->entries().front();
+  ASSERT_EQ(TabRestoreService::TAB, restored_entry->type);
+  Tab* restored_tab =
+      static_cast<Tab*>(restored_entry);
+  ASSERT_EQ(old_navigations.size(), restored_tab->navigations.size());
+  for (size_t i = 0; i < restored_tab->navigations.size(); ++i) {
+    EXPECT_EQ(200, restored_tab->navigations[i].http_status_code());
+  }
+}
+
 TEST_F(PersistentTabRestoreServiceTest, PruneEntries) {
   service_->ClearEntries();
   ASSERT_TRUE(service_->entries().empty());
diff --git a/chrome/browser/sessions/session_restore_browsertest.cc b/chrome/browser/sessions/session_restore_browsertest.cc
index ad0f998..9e04903 100644
--- a/chrome/browser/sessions/session_restore_browsertest.cc
+++ b/chrome/browser/sessions/session_restore_browsertest.cc
@@ -308,9 +308,8 @@
   GURL url1(ui_test_utils::GetTestUrl(
       base::FilePath(base::FilePath::kCurrentDirectory),
       base::FilePath(FILE_PATH_LITERAL("title1.html"))));
-  GURL url2(ui_test_utils::GetTestUrl(
-      base::FilePath(base::FilePath::kCurrentDirectory),
-      base::FilePath(FILE_PATH_LITERAL("title2.html"))));
+  // Any page that will yield a 200 status code will work here.
+  GURL url2("about:version");
   GURL url3(ui_test_utils::GetTestUrl(
       base::FilePath(base::FilePath::kCurrentDirectory),
       base::FilePath(FILE_PATH_LITERAL("title3.html"))));
@@ -352,12 +351,14 @@
   // Find the SessionID for entry2. Since the session service was destroyed,
   // there is no guarantee that the SessionID for the tab has remained the same.
   base::Time timestamp;
+  int http_status_code = 0;
   for (std::vector<TabRestoreService::Tab>::const_iterator it =
            window->tabs.begin(); it != window->tabs.end(); ++it) {
     const TabRestoreService::Tab& tab = *it;
     // If this tab held url2, then restore this single tab.
     if (tab.navigations[0].virtual_url() == url2) {
       timestamp = tab.navigations[0].timestamp();
+      http_status_code = tab.navigations[0].http_status_code();
       std::vector<content::WebContents*> content =
           service->RestoreEntryById(NULL, tab.id, host_desktop_type, UNKNOWN);
       ASSERT_EQ(1U, content.size());
@@ -367,6 +368,7 @@
     }
   }
   EXPECT_FALSE(timestamp.is_null());
+  EXPECT_EQ(200, http_status_code);
 
   // Make sure that the restored tab is removed from the service.
   ASSERT_EQ(1U, service->entries().size());
@@ -375,7 +377,7 @@
   EXPECT_EQ(2U, window->tabs.size());
 
   // Make sure that the restored tab was restored with the correct
-  // timestamp.
+  // timestamp and status code.
   const content::WebContents* contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   ASSERT_TRUE(contents);
@@ -383,6 +385,7 @@
       contents->GetController().GetActiveEntry();
   ASSERT_TRUE(entry);
   EXPECT_EQ(timestamp, entry->GetTimestamp());
+  EXPECT_EQ(http_status_code, entry->GetHttpStatusCode());
 }
 
 IN_PROC_BROWSER_TEST_F(SessionRestoreTest, WindowWithOneTab) {
diff --git a/chrome/browser/signin/oauth2_token_service.cc b/chrome/browser/signin/oauth2_token_service.cc
deleted file mode 100644
index 1773871..0000000
--- a/chrome/browser/signin/oauth2_token_service.cc
+++ /dev/null
@@ -1,610 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/signin/oauth2_token_service.h"
-
-#include <vector>
-
-#include "base/bind.h"
-#include "base/memory/weak_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "base/rand_util.h"
-#include "base/stl_util.h"
-#include "base/time/time.h"
-#include "base/timer/timer.h"
-#include "content/public/browser/browser_thread.h"
-#include "google_apis/gaia/gaia_urls.h"
-#include "google_apis/gaia/google_service_auth_error.h"
-#include "google_apis/gaia/oauth2_access_token_consumer.h"
-#include "google_apis/gaia/oauth2_access_token_fetcher.h"
-#include "net/url_request/url_request_context_getter.h"
-
-int OAuth2TokenService::max_fetch_retry_num_ = 5;
-
-OAuth2TokenService::RequestImpl::RequestImpl(
-    OAuth2TokenService::Consumer* consumer)
-    : consumer_(consumer) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-}
-
-OAuth2TokenService::RequestImpl::~RequestImpl() {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-}
-
-void OAuth2TokenService::RequestImpl::InformConsumer(
-    const GoogleServiceAuthError& error,
-    const std::string& access_token,
-    const base::Time& expiration_date) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  if (error.state() == GoogleServiceAuthError::NONE)
-    consumer_->OnGetTokenSuccess(this, access_token, expiration_date);
-  else
-    consumer_->OnGetTokenFailure(this, error);
-}
-
-// Class that fetches OAuth2 access tokens for given scopes and refresh token.
-//
-// It aims to meet OAuth2TokenService's requirements on token fetching. Retry
-// mechanism is used to handle failures.
-//
-// To use this class, call CreateAndStart() to create and start a Fetcher.
-//
-// The Fetcher will call back the service by calling
-// OAuth2TokenService::OnFetchComplete() when it completes fetching, if it is
-// not destructed before it completes fetching; if the Fetcher is destructed
-// before it completes fetching, the service will never be called back. The
-// Fetcher destructs itself after calling back the service when finishes
-// fetching.
-//
-// Requests that are waiting for the fetching results of this Fetcher can be
-// added to the Fetcher by calling
-// OAuth2TokenService::Fetcher::AddWaitingRequest() before the Fetcher completes
-// fetching.
-//
-// The waiting requests are taken as weak pointers and they can be deleted. The
-// waiting requests will be called back with fetching results if they are not
-// deleted
-// - when the Fetcher completes fetching, if the Fetcher is not destructed
-//   before it completes fetching, or
-// - when the Fetcher is destructed if the Fetcher is destructed before it
-//   completes fetching (in this case, the waiting requests will be called back
-//   with error).
-class OAuth2TokenService::Fetcher : public OAuth2AccessTokenConsumer {
- public:
-  // Creates a Fetcher and starts fetching an OAuth2 access token for
-  // |refresh_token| and |scopes| in the request context obtained by |getter|.
-  // The given |oauth2_token_service| will be informed when fetching is done.
-  static Fetcher* CreateAndStart(OAuth2TokenService* oauth2_token_service,
-                                 net::URLRequestContextGetter* getter,
-                                 const std::string& chrome_client_id,
-                                 const std::string& chrome_client_secret,
-                                 const std::string& refresh_token,
-                                 const OAuth2TokenService::ScopeSet& scopes,
-                                 base::WeakPtr<RequestImpl> waiting_request);
-  virtual ~Fetcher();
-
-  // Add a request that is waiting for the result of this Fetcher.
-  void AddWaitingRequest(base::WeakPtr<RequestImpl> waiting_request);
-
-  void Cancel();
-
-  const OAuth2TokenService::ScopeSet& GetScopeSet() const;
-  const std::string& GetRefreshToken() const;
-
-  // The error result from this fetcher.
-  const GoogleServiceAuthError& error() const { return error_; }
-
- protected:
-   // OAuth2AccessTokenConsumer
-  virtual void OnGetTokenSuccess(const std::string& access_token,
-                                 const base::Time& expiration_date) OVERRIDE;
-  virtual void OnGetTokenFailure(const GoogleServiceAuthError& error) OVERRIDE;
-
- private:
-  Fetcher(OAuth2TokenService* oauth2_token_service,
-          net::URLRequestContextGetter* getter,
-          const std::string& chrome_client_id,
-          const std::string& chrome_client_secret,
-          const std::string& refresh_token,
-          const OAuth2TokenService::ScopeSet& scopes,
-          base::WeakPtr<RequestImpl> waiting_request);
-  void Start();
-  void InformWaitingRequests();
-  void InformWaitingRequestsAndDelete();
-  static bool ShouldRetry(const GoogleServiceAuthError& error);
-  int64 ComputeExponentialBackOffMilliseconds(int retry_num);
-
-  // |oauth2_token_service_| remains valid for the life of this Fetcher, since
-  // this Fetcher is destructed in the dtor of the OAuth2TokenService or is
-  // scheduled for deletion at the end of OnGetTokenFailure/OnGetTokenSuccess
-  // (whichever comes first).
-  OAuth2TokenService* const oauth2_token_service_;
-  scoped_refptr<net::URLRequestContextGetter> getter_;
-  const std::string refresh_token_;
-  const OAuth2TokenService::ScopeSet scopes_;
-  std::vector<base::WeakPtr<RequestImpl> > waiting_requests_;
-
-  int retry_number_;
-  base::OneShotTimer<OAuth2TokenService::Fetcher> retry_timer_;
-  scoped_ptr<OAuth2AccessTokenFetcher> fetcher_;
-
-  // Variables that store fetch results.
-  // Initialized to be GoogleServiceAuthError::SERVICE_UNAVAILABLE to handle
-  // destruction.
-  GoogleServiceAuthError error_;
-  std::string access_token_;
-  base::Time expiration_date_;
-  // OAuth2 client id and secret.
-  std::string chrome_client_id_;
-  std::string chrome_client_secret_;
-
-  DISALLOW_COPY_AND_ASSIGN(Fetcher);
-};
-
-// static
-OAuth2TokenService::Fetcher* OAuth2TokenService::Fetcher::CreateAndStart(
-    OAuth2TokenService* oauth2_token_service,
-    net::URLRequestContextGetter* getter,
-    const std::string& chrome_client_id,
-    const std::string& chrome_client_secret,
-    const std::string& refresh_token,
-    const OAuth2TokenService::ScopeSet& scopes,
-    base::WeakPtr<RequestImpl> waiting_request) {
-  OAuth2TokenService::Fetcher* fetcher = new Fetcher(
-      oauth2_token_service,
-      getter,
-      chrome_client_id,
-      chrome_client_secret,
-      refresh_token,
-      scopes,
-      waiting_request);
-  fetcher->Start();
-  return fetcher;
-}
-
-OAuth2TokenService::Fetcher::Fetcher(
-    OAuth2TokenService* oauth2_token_service,
-    net::URLRequestContextGetter* getter,
-    const std::string& chrome_client_id,
-    const std::string& chrome_client_secret,
-    const std::string& refresh_token,
-    const OAuth2TokenService::ScopeSet& scopes,
-    base::WeakPtr<RequestImpl> waiting_request)
-    : oauth2_token_service_(oauth2_token_service),
-      getter_(getter),
-      refresh_token_(refresh_token),
-      scopes_(scopes),
-      retry_number_(0),
-      error_(GoogleServiceAuthError::SERVICE_UNAVAILABLE),
-      chrome_client_id_(chrome_client_id),
-      chrome_client_secret_(chrome_client_secret) {
-  DCHECK(oauth2_token_service_);
-  DCHECK(getter_.get());
-  DCHECK(refresh_token_.length());
-  waiting_requests_.push_back(waiting_request);
-}
-
-OAuth2TokenService::Fetcher::~Fetcher() {
-  // Inform the waiting requests if it has not done so.
-  if (waiting_requests_.size())
-    InformWaitingRequests();
-}
-
-void OAuth2TokenService::Fetcher::Start() {
-  fetcher_.reset(new OAuth2AccessTokenFetcher(this, getter_.get()));
-  fetcher_->Start(chrome_client_id_,
-                  chrome_client_secret_,
-                  refresh_token_,
-                  std::vector<std::string>(scopes_.begin(), scopes_.end()));
-  retry_timer_.Stop();
-}
-
-void OAuth2TokenService::Fetcher::OnGetTokenSuccess(
-    const std::string& access_token,
-    const base::Time& expiration_date) {
-  fetcher_.reset();
-
-  // Fetch completes.
-  error_ = GoogleServiceAuthError::AuthErrorNone();
-  access_token_ = access_token;
-  expiration_date_ = expiration_date;
-
-  // Subclasses may override this method to skip caching in some cases, but
-  // we still inform all waiting Consumers of a successful token fetch below.
-  // This is intentional -- some consumers may need the token for cleanup
-  // tasks. https://chromiumcodereview.appspot.com/11312124/
-  oauth2_token_service_->RegisterCacheEntry(refresh_token_,
-                                            scopes_,
-                                            access_token_,
-                                            expiration_date_);
-  InformWaitingRequestsAndDelete();
-}
-
-void OAuth2TokenService::Fetcher::OnGetTokenFailure(
-    const GoogleServiceAuthError& error) {
-  fetcher_.reset();
-
-  if (ShouldRetry(error) && retry_number_ < max_fetch_retry_num_) {
-    int64 backoff = ComputeExponentialBackOffMilliseconds(retry_number_);
-    ++retry_number_;
-    retry_timer_.Stop();
-    retry_timer_.Start(FROM_HERE,
-                       base::TimeDelta::FromMilliseconds(backoff),
-                       this,
-                       &OAuth2TokenService::Fetcher::Start);
-    return;
-  }
-
-  error_ = error;
-  InformWaitingRequestsAndDelete();
-}
-
-// Returns an exponential backoff in milliseconds including randomness less than
-// 1000 ms when retrying fetching an OAuth2 access token.
-int64 OAuth2TokenService::Fetcher::ComputeExponentialBackOffMilliseconds(
-    int retry_num) {
-  DCHECK(retry_num < max_fetch_retry_num_);
-  int64 exponential_backoff_in_seconds = 1 << retry_num;
-  // Returns a backoff with randomness < 1000ms
-  return (exponential_backoff_in_seconds + base::RandDouble()) * 1000;
-}
-
-// static
-bool OAuth2TokenService::Fetcher::ShouldRetry(
-    const GoogleServiceAuthError& error) {
-  GoogleServiceAuthError::State error_state = error.state();
-  return error_state == GoogleServiceAuthError::CONNECTION_FAILED ||
-         error_state == GoogleServiceAuthError::REQUEST_CANCELED ||
-         error_state == GoogleServiceAuthError::SERVICE_UNAVAILABLE;
-}
-
-void OAuth2TokenService::Fetcher::InformWaitingRequests() {
-  std::vector<base::WeakPtr<RequestImpl> >::const_iterator iter =
-      waiting_requests_.begin();
-  for (; iter != waiting_requests_.end(); ++iter) {
-    base::WeakPtr<RequestImpl> waiting_request = *iter;
-    if (waiting_request.get())
-      waiting_request->InformConsumer(error_, access_token_, expiration_date_);
-  }
-  waiting_requests_.clear();
-}
-
-void OAuth2TokenService::Fetcher::InformWaitingRequestsAndDelete() {
-  // Deregisters itself from the service to prevent more waiting requests to
-  // be added when it calls back the waiting requests.
-  oauth2_token_service_->OnFetchComplete(this);
-  InformWaitingRequests();
-  base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
-}
-
-void OAuth2TokenService::Fetcher::AddWaitingRequest(
-    base::WeakPtr<OAuth2TokenService::RequestImpl> waiting_request) {
-  waiting_requests_.push_back(waiting_request);
-}
-
-void OAuth2TokenService::Fetcher::Cancel() {
-  fetcher_.reset();
-  retry_timer_.Stop();
-  error_ = GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED);
-  InformWaitingRequestsAndDelete();
-}
-
-const OAuth2TokenService::ScopeSet& OAuth2TokenService::Fetcher::GetScopeSet()
-    const {
-  return scopes_;
-}
-
-const std::string& OAuth2TokenService::Fetcher::GetRefreshToken() const {
-  return refresh_token_;
-}
-
-OAuth2TokenService::Request::Request() {
-}
-
-OAuth2TokenService::Request::~Request() {
-}
-
-OAuth2TokenService::Consumer::Consumer() {
-}
-
-OAuth2TokenService::Consumer::~Consumer() {
-}
-
-OAuth2TokenService::OAuth2TokenService() {
-}
-
-OAuth2TokenService::~OAuth2TokenService() {
-  // Release all the pending fetchers.
-  STLDeleteContainerPairSecondPointers(
-      pending_fetchers_.begin(), pending_fetchers_.end());
-}
-
-void OAuth2TokenService::AddObserver(Observer* observer) {
-  observer_list_.AddObserver(observer);
-}
-
-void OAuth2TokenService::RemoveObserver(Observer* observer) {
-  observer_list_.RemoveObserver(observer);
-}
-
-bool OAuth2TokenService::RefreshTokenIsAvailable() {
-  return !GetRefreshToken().empty();
-}
-
-scoped_ptr<OAuth2TokenService::Request> OAuth2TokenService::StartRequest(
-    const OAuth2TokenService::ScopeSet& scopes,
-    OAuth2TokenService::Consumer* consumer) {
-  return StartRequestForClientWithContext(
-      GetRequestContext(),
-      GaiaUrls::GetInstance()->oauth2_chrome_client_id(),
-      GaiaUrls::GetInstance()->oauth2_chrome_client_secret(),
-      scopes,
-      consumer);
-}
-
-scoped_ptr<OAuth2TokenService::Request>
-OAuth2TokenService::StartRequestForClient(
-    const std::string& client_id,
-    const std::string& client_secret,
-    const OAuth2TokenService::ScopeSet& scopes,
-    OAuth2TokenService::Consumer* consumer) {
-  return StartRequestForClientWithContext(
-      GetRequestContext(),
-      client_id,
-      client_secret,
-      scopes,
-      consumer);
-}
-
-scoped_ptr<OAuth2TokenService::Request>
-OAuth2TokenService::StartRequestWithContext(
-    net::URLRequestContextGetter* getter,
-    const ScopeSet& scopes,
-    Consumer* consumer) {
-  return StartRequestForClientWithContext(
-      getter,
-      GaiaUrls::GetInstance()->oauth2_chrome_client_id(),
-      GaiaUrls::GetInstance()->oauth2_chrome_client_secret(),
-      scopes,
-      consumer);
-}
-
-scoped_ptr<OAuth2TokenService::Request>
-OAuth2TokenService::StartRequestForClientWithContext(
-    net::URLRequestContextGetter* getter,
-    const std::string& client_id,
-    const std::string& client_secret,
-    const ScopeSet& scopes,
-    Consumer* consumer) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
-  scoped_ptr<RequestImpl> request(new RequestImpl(consumer));
-
-  if (!RefreshTokenIsAvailable()) {
-    base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
-        &RequestImpl::InformConsumer,
-        request->AsWeakPtr(),
-        GoogleServiceAuthError(GoogleServiceAuthError::USER_NOT_SIGNED_UP),
-        std::string(),
-        base::Time()));
-    return request.PassAs<Request>();
-  }
-
-  if (HasCacheEntry(scopes)) {
-    StartCacheLookupRequest(request.get(), scopes, consumer);
-  } else {
-    FetchOAuth2Token(request.get(),
-                     getter,
-                     client_id,
-                     client_secret,
-                     scopes);
-  }
-  return request.PassAs<Request>();
-}
-
-void OAuth2TokenService::FetchOAuth2Token(RequestImpl* request,
-                                          net::URLRequestContextGetter* getter,
-                                          const std::string& client_id,
-                                          const std::string& client_secret,
-                                          const ScopeSet& scopes) {
-  std::string refresh_token = GetRefreshToken();
-
-  // If there is already a pending fetcher for |scopes| and |refresh_token|,
-  // simply register this |request| for those results rather than starting
-  // a new fetcher.
-  FetchParameters fetch_parameters = std::make_pair(refresh_token, scopes);
-  std::map<FetchParameters, Fetcher*>::iterator iter =
-      pending_fetchers_.find(fetch_parameters);
-  if (iter != pending_fetchers_.end()) {
-    iter->second->AddWaitingRequest(request->AsWeakPtr());
-    return;
-  }
-
-  pending_fetchers_[fetch_parameters] =
-      Fetcher::CreateAndStart(this,
-                              getter,
-                              client_id,
-                              client_secret,
-                              refresh_token,
-                              scopes,
-                              request->AsWeakPtr());
-}
-
-void OAuth2TokenService::StartCacheLookupRequest(
-    RequestImpl* request,
-    const OAuth2TokenService::ScopeSet& scopes,
-    OAuth2TokenService::Consumer* consumer) {
-  CHECK(HasCacheEntry(scopes));
-  const CacheEntry* cache_entry = GetCacheEntry(scopes);
-  base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
-      &RequestImpl::InformConsumer,
-      request->AsWeakPtr(),
-      GoogleServiceAuthError(GoogleServiceAuthError::NONE),
-      cache_entry->access_token,
-      cache_entry->expiration_date));
-}
-
-void OAuth2TokenService::InvalidateToken(const ScopeSet& scopes,
-                                         const std::string& invalid_token) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  RemoveCacheEntry(scopes, invalid_token);
-}
-
-void OAuth2TokenService::OnFetchComplete(Fetcher* fetcher) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
-  // Update the auth error state so auth errors are appropriately communicated
-  // to the user.
-  UpdateAuthError(fetcher->error());
-
-  // Note |fetcher| is recorded in |pending_fetcher_| mapped to its refresh
-  // token and scope set. This is guaranteed as follows; here a Fetcher is said
-  // to be uncompleted if it has not finished calling back
-  // OAuth2TokenService::OnFetchComplete().
-  //
-  // (1) All the live Fetchers are created by this service.
-  //     This is because (1) all the live Fetchers are created by a live
-  //     service, as all the fetchers created by a service are destructed in the
-  //     service's dtor.
-  //
-  // (2) All the uncompleted Fetchers created by this service are recorded in
-  //     |pending_fetchers_|.
-  //     This is because (1) all the created Fetchers are added to
-  //     |pending_fetchers_| (in method StartRequest()) and (2) method
-  //     OnFetchComplete() is the only place where a Fetcher is erased from
-  //     |pending_fetchers_|. Note no Fetcher is erased in method
-  //     StartRequest().
-  //
-  // (3) Each of the Fetchers recorded in |pending_fetchers_| is mapped to its
-  //     refresh token and ScopeSet. This is guaranteed by Fetcher creation in
-  //     method StartRequest().
-  //
-  // When this method is called, |fetcher| is alive and uncompleted.
-  // By (1), |fetcher| is created by this service.
-  // Then by (2), |fetcher| is recorded in |pending_fetchers_|.
-  // Then by (3), |fetcher_| is mapped to its refresh token and ScopeSet.
-  std::map<FetchParameters, Fetcher*>::iterator iter =
-    pending_fetchers_.find(std::make_pair(
-        fetcher->GetRefreshToken(), fetcher->GetScopeSet()));
-  DCHECK(iter != pending_fetchers_.end());
-  DCHECK_EQ(fetcher, iter->second);
-  pending_fetchers_.erase(iter);
-}
-
-bool OAuth2TokenService::HasCacheEntry(
-    const OAuth2TokenService::ScopeSet& scopes) {
-  const CacheEntry* cache_entry = GetCacheEntry(scopes);
-  return cache_entry && cache_entry->access_token.length();
-}
-
-const OAuth2TokenService::CacheEntry* OAuth2TokenService::GetCacheEntry(
-    const OAuth2TokenService::ScopeSet& scopes) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  TokenCache::iterator token_iterator = token_cache_.find(scopes);
-  if (token_iterator == token_cache_.end())
-    return NULL;
-  if (token_iterator->second.expiration_date <= base::Time::Now()) {
-    token_cache_.erase(token_iterator);
-    return NULL;
-  }
-  return &token_iterator->second;
-}
-
-bool OAuth2TokenService::RemoveCacheEntry(
-    const OAuth2TokenService::ScopeSet& scopes,
-    const std::string& token_to_remove) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  TokenCache::iterator token_iterator = token_cache_.find(scopes);
-  if (token_iterator != token_cache_.end() &&
-      token_iterator->second.access_token == token_to_remove) {
-    token_cache_.erase(token_iterator);
-    return true;
-  }
-  return false;
-}
-
-void OAuth2TokenService::RegisterCacheEntry(
-    const std::string& refresh_token,
-    const OAuth2TokenService::ScopeSet& scopes,
-    const std::string& access_token,
-    const base::Time& expiration_date) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
-  CacheEntry& token = token_cache_[scopes];
-  token.access_token = access_token;
-  token.expiration_date = expiration_date;
-}
-
-void OAuth2TokenService::UpdateAuthError(const GoogleServiceAuthError& error) {
-  // Default implementation does nothing.
-}
-
-void OAuth2TokenService::ClearCache() {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  token_cache_.clear();
-}
-
-void OAuth2TokenService::CancelAllRequests() {
-  std::vector<Fetcher*> fetchers_to_cancel;
-  for (std::map<FetchParameters, Fetcher*>::iterator iter =
-           pending_fetchers_.begin();
-       iter != pending_fetchers_.end();
-       ++iter) {
-    fetchers_to_cancel.push_back(iter->second);
-  }
-  CancelFetchers(fetchers_to_cancel);
-}
-
-void OAuth2TokenService::CancelRequestsForToken(
-    const std::string& refresh_token) {
-  std::vector<Fetcher*> fetchers_to_cancel;
-  for (std::map<FetchParameters, Fetcher*>::iterator iter =
-           pending_fetchers_.begin();
-       iter != pending_fetchers_.end();
-       ++iter) {
-    if (iter->first.first == refresh_token)
-      fetchers_to_cancel.push_back(iter->second);
-  }
-  CancelFetchers(fetchers_to_cancel);
-}
-
-void OAuth2TokenService::CancelFetchers(
-    std::vector<Fetcher*> fetchers_to_cancel) {
-  for (std::vector<OAuth2TokenService::Fetcher*>::iterator iter =
-           fetchers_to_cancel.begin();
-       iter != fetchers_to_cancel.end();
-       ++iter) {
-    (*iter)->Cancel();
-  }
-}
-
-void OAuth2TokenService::FireRefreshTokenAvailable(
-    const std::string& account_id) {
-  FOR_EACH_OBSERVER(Observer, observer_list_,
-                    OnRefreshTokenAvailable(account_id));
-}
-
-void OAuth2TokenService::FireRefreshTokenRevoked(
-    const std::string& account_id) {
-  FOR_EACH_OBSERVER(Observer, observer_list_,
-                    OnRefreshTokenRevoked(account_id));
-}
-
-void OAuth2TokenService::FireRefreshTokensLoaded() {
-  FOR_EACH_OBSERVER(Observer, observer_list_, OnRefreshTokensLoaded());
-}
-
-void OAuth2TokenService::FireRefreshTokensCleared() {
-  FOR_EACH_OBSERVER(Observer, observer_list_, OnRefreshTokensCleared());
-}
-
-int OAuth2TokenService::cache_size_for_testing() const {
-  return token_cache_.size();
-}
-
-void OAuth2TokenService::set_max_authorization_token_fetch_retries_for_testing(
-    int max_retries) {
-  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-  max_fetch_retry_num_ = max_retries;
-}
diff --git a/chrome/browser/signin/oauth2_token_service.h b/chrome/browser/signin/oauth2_token_service.h
deleted file mode 100644
index ac993de..0000000
--- a/chrome/browser/signin/oauth2_token_service.h
+++ /dev/null
@@ -1,289 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SIGNIN_OAUTH2_TOKEN_SERVICE_H_
-#define CHROME_BROWSER_SIGNIN_OAUTH2_TOKEN_SERVICE_H_
-
-#include <map>
-#include <set>
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/observer_list.h"
-#include "base/threading/non_thread_safe.h"
-#include "base/time/time.h"
-#include "google_apis/gaia/google_service_auth_error.h"
-
-namespace base {
-class Time;
-}
-
-namespace net {
-class URLRequestContextGetter;
-}
-
-class GoogleServiceAuthError;
-
-// Abstract base class for a service that fetches and caches OAuth2 access
-// tokens. Concrete subclasses should implement GetRefreshToken to return
-// the appropriate refresh token.
-//
-// All calls are expected from the UI thread.
-//
-// To use this service, call StartRequest() with a given set of scopes and a
-// consumer of the request results. The consumer is required to outlive the
-// request. The request can be deleted. The consumer may be called back
-// asynchronously with the fetch results.
-//
-// - If the consumer is not called back before the request is deleted, it will
-//   never be called back.
-//   Note in this case, the actual network requests are not canceled and the
-//   cache will be populated with the fetched results; it is just the consumer
-//   callback that is aborted.
-//
-// - Otherwise the consumer will be called back with the request and the fetch
-//   results.
-//
-// The caller of StartRequest() owns the returned request and is responsible to
-// delete the request even once the callback has been invoked.
-class OAuth2TokenService : public base::NonThreadSafe {
- public:
-  // Class representing a request that fetches an OAuth2 access token.
-  class Request {
-   public:
-    virtual ~Request();
-   protected:
-    Request();
-  };
-
-  // Class representing the consumer of a Request passed to |StartRequest|,
-  // which will be called back when the request completes.
-  class Consumer {
-   public:
-    Consumer();
-    virtual ~Consumer();
-    // |request| is a Request that is started by this consumer and has
-    // completed.
-    virtual void OnGetTokenSuccess(const Request* request,
-                                   const std::string& access_token,
-                                   const base::Time& expiration_time) = 0;
-    virtual void OnGetTokenFailure(const Request* request,
-                                   const GoogleServiceAuthError& error) = 0;
-  };
-
-  // Classes that want to listen for token availability should implement this
-  // interface and register with the AddObserver() call.
-  // TODO(rogerta): may get rid of |error| argument for OnRefreshTokenRevoked()
-  // once we stop supporting ClientLogin.  Need to evaluate if its still useful.
-  class Observer {
-   public:
-    // Called whenever a new login-scoped refresh token is available for
-    // account |account_id|. Once available, access tokens can be retrieved for
-    // this account.  This is called during initial startup for each token
-    // loaded.
-    virtual void OnRefreshTokenAvailable(const std::string& account_id) {}
-    // Called whenever the login-scoped refresh token becomes unavailable for
-    // account |account_id|.
-    virtual void OnRefreshTokenRevoked(const std::string& account_id) {}
-    // Called after all refresh tokens are loaded during OAuth2TokenService
-    // startup.
-    virtual void OnRefreshTokensLoaded() {}
-    // Called after all refresh tokens are removed from OAuth2TokenService.
-    virtual void OnRefreshTokensCleared() {}
-   protected:
-    virtual ~Observer() {}
-  };
-
-  // A set of scopes in OAuth2 authentication.
-  typedef std::set<std::string> ScopeSet;
-
-  OAuth2TokenService();
-  virtual ~OAuth2TokenService();
-
-  // Add or remove observers of this token service.
-  void AddObserver(Observer* observer);
-  void RemoveObserver(Observer* observer);
-
-  // Checks in the cache for a valid access token, and if not found starts
-  // a request for an OAuth2 access token using the OAuth2 refresh token
-  // maintained by this instance. The caller owns the returned Request.
-  // |scopes| is the set of scopes to get an access token for, |consumer| is
-  // the object that will be called back with results if the returned request
-  // is not deleted.
-  // TODO(atwilson): Make this non-virtual when we change
-  // ProfileOAuth2TokenServiceRequestTest to use FakeProfileOAuth2TokenService.
-  virtual scoped_ptr<Request> StartRequest(const ScopeSet& scopes,
-                                           Consumer* consumer);
-
-  // This method does the same as |StartRequest| except it uses |client_id| and
-  // |client_secret| to identify OAuth client app instead of using
-  // Chrome's default values.
-  scoped_ptr<Request> StartRequestForClient(
-      const std::string& client_id,
-      const std::string& client_secret,
-      const ScopeSet& scopes,
-      Consumer* consumer);
-
-  // This method does the same as |StartRequest| except it uses the request
-  // context given by |getter| instead of using the one returned by
-  // |GetRequestContext| implemented by derived classes.
-  scoped_ptr<Request> StartRequestWithContext(
-      net::URLRequestContextGetter* getter,
-      const ScopeSet& scopes,
-      Consumer* consumer);
-
-  // Returns true if a refresh token exists. If false, calls to
-  // |StartRequest| will result in a Consumer::OnGetTokenFailure callback.
-  virtual bool RefreshTokenIsAvailable();
-
-  // Mark an OAuth2 access token as invalid. This should be done if the token
-  // was received from this class, but was not accepted by the server (e.g.,
-  // the server returned 401 Unauthorized). The token will be removed from the
-  // cache for the given scopes.
-  virtual void InvalidateToken(const ScopeSet& scopes,
-                               const std::string& invalid_token);
-
-  // Return the current number of entries in the cache.
-  int cache_size_for_testing() const;
-  void set_max_authorization_token_fetch_retries_for_testing(int max_retries);
-
- protected:
-  // Implements a cancelable |OAuth2TokenService::Request|, which should be
-  // operated on the UI thread.
-  // TODO(davidroche): move this out of header file.
-  class RequestImpl : public base::SupportsWeakPtr<RequestImpl>,
-                      public Request {
-   public:
-    // |consumer| is required to outlive this.
-    explicit RequestImpl(Consumer* consumer);
-    virtual ~RequestImpl();
-
-    // Informs |consumer_| that this request is completed.
-    void InformConsumer(const GoogleServiceAuthError& error,
-                        const std::string& access_token,
-                        const base::Time& expiration_date);
-
-   private:
-    // |consumer_| to call back when this request completes.
-    Consumer* const consumer_;
-  };
-
-  // Subclasses should return the refresh token maintained.
-  // If no token is available, return an empty string.
-  virtual std::string GetRefreshToken() = 0;
-
-  // Subclasses can override if they want to report errors to the user.
-  virtual void UpdateAuthError(const GoogleServiceAuthError& error);
-
-  // Add a new entry to the cache.
-  // Subclasses can override if there are implementation-specific reasons
-  // that an access token should ever not be cached.
-  virtual void RegisterCacheEntry(const std::string& refresh_token,
-                                  const ScopeSet& scopes,
-                                  const std::string& access_token,
-                                  const base::Time& expiration_date);
-
-  // Returns true if GetCacheEntry would return a valid cache entry for the
-  // given scopes.
-  bool HasCacheEntry(const ScopeSet& scopes);
-
-  // Posts a task to fire the Consumer callback with the cached token.  Must
-  // Must only be called if HasCacheEntry() returns true.
-  void StartCacheLookupRequest(RequestImpl* request,
-                               const ScopeSet& scopes,
-                               Consumer* consumer);
-
-  // Clears the internal token cache.
-  void ClearCache();
-
-  // Cancels all requests that are currently in progress.
-  void CancelAllRequests();
-
-  // Cancels all requests related to a given refresh token.
-  void CancelRequestsForToken(const std::string& refresh_token);
-
-  // Called by subclasses to notify observers.
-  void FireRefreshTokenAvailable(const std::string& account_id);
-  void FireRefreshTokenRevoked(const std::string& account_id);
-  void FireRefreshTokensLoaded();
-  void FireRefreshTokensCleared();
-
-  // Derived classes must provide a request context used for fetching access
-  // tokens with the |StartRequest| method.
-  virtual net::URLRequestContextGetter* GetRequestContext() = 0;
-
-  // Fetches an OAuth token for the specified client/scopes. Virtual so it can
-  // be overridden for tests and for platform-specific behavior on Android.
-  virtual void FetchOAuth2Token(RequestImpl* request,
-                                net::URLRequestContextGetter* getter,
-                                const std::string& client_id,
-                                const std::string& client_secret,
-                                const ScopeSet& scopes);
-
- private:
-  // Class that fetches an OAuth2 access token for a given set of scopes and
-  // OAuth2 refresh token.
-  class Fetcher;
-  friend class Fetcher;
-
-  // Struct that contains the information of an OAuth2 access token.
-  struct CacheEntry {
-    std::string access_token;
-    base::Time expiration_date;
-  };
-
-  // This method does the same as |StartRequestWithContext| except it
-  // uses |client_id| and |client_secret| to identify OAuth
-  // client app instead of using Chrome's default values.
-  scoped_ptr<Request> StartRequestForClientWithContext(
-      net::URLRequestContextGetter* getter,
-      const std::string& client_id,
-      const std::string& client_secret,
-      const ScopeSet& scopes,
-      Consumer* consumer);
-
-  // Returns a currently valid OAuth2 access token for the given set of scopes,
-  // or NULL if none have been cached. Note the user of this method should
-  // ensure no entry with the same |scopes| is added before the usage of the
-  // returned entry is done.
-  const CacheEntry* GetCacheEntry(const ScopeSet& scopes);
-
-
-  // Removes an access token for the given set of scopes from the cache.
-  // Returns true if the entry was removed, otherwise false.
-  bool RemoveCacheEntry(const OAuth2TokenService::ScopeSet& scopes,
-                        const std::string& token_to_remove);
-
-
-  // Called when |fetcher| finishes fetching.
-  void OnFetchComplete(Fetcher* fetcher);
-
-  // Called when a number of fetchers need to be canceled.
-  void CancelFetchers(std::vector<Fetcher*> fetchers_to_cancel);
-
-  // The cache of currently valid tokens.
-  typedef std::map<ScopeSet, CacheEntry> TokenCache;
-  TokenCache token_cache_;
-
-  // The parameters (refresh token and scope set) used to fetch an OAuth2 access
-  // token.
-  typedef std::pair<std::string, ScopeSet> FetchParameters;
-  // A map from fetch parameters to a fetcher that is fetching an OAuth2 access
-  // token using these parameters.
-  std::map<FetchParameters, Fetcher*> pending_fetchers_;
-
-  // List of observers to notify when token availiability changes.
-  // Makes sure list is empty on destruction.
-  ObserverList<Observer, true> observer_list_;
-
-  // Maximum number of retries in fetching an OAuth2 access token.
-  static int max_fetch_retry_num_;
-
-  DISALLOW_COPY_AND_ASSIGN(OAuth2TokenService);
-};
-
-#endif  // CHROME_BROWSER_SIGNIN_OAUTH2_TOKEN_SERVICE_H_
diff --git a/chrome/browser/signin/oauth2_token_service_test_util.cc b/chrome/browser/signin/oauth2_token_service_test_util.cc
deleted file mode 100644
index dd03199..0000000
--- a/chrome/browser/signin/oauth2_token_service_test_util.cc
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/signin/oauth2_token_service_test_util.h"
-
-const char kValidTokenResponse[] =
-    "{"
-    "  \"access_token\": \"%s\","
-    "  \"expires_in\": %d,"
-    "  \"token_type\": \"Bearer\""
-    "}";
-
-std::string GetValidTokenResponse(std::string token, int expiration) {
-  return base::StringPrintf(kValidTokenResponse, token.c_str(), expiration);
-}
-
-TestingOAuth2TokenServiceConsumer::TestingOAuth2TokenServiceConsumer()
-    : number_of_successful_tokens_(0),
-      last_error_(GoogleServiceAuthError::AuthErrorNone()),
-      number_of_errors_(0) {
-}
-
-TestingOAuth2TokenServiceConsumer::~TestingOAuth2TokenServiceConsumer() {
-}
-
-void TestingOAuth2TokenServiceConsumer::OnGetTokenSuccess(
-    const OAuth2TokenService::Request* request,
-    const std::string& token,
-    const base::Time& expiration_date) {
-  last_token_ = token;
-  ++number_of_successful_tokens_;
-}
-
-void TestingOAuth2TokenServiceConsumer::OnGetTokenFailure(
-    const OAuth2TokenService::Request* request,
-    const GoogleServiceAuthError& error) {
-  last_error_ = error;
-  ++number_of_errors_;
-}
diff --git a/chrome/browser/signin/oauth2_token_service_test_util.h b/chrome/browser/signin/oauth2_token_service_test_util.h
deleted file mode 100644
index 6f670aa..0000000
--- a/chrome/browser/signin/oauth2_token_service_test_util.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SIGNIN_OAUTH2_TOKEN_SERVICE_TEST_UTIL_H_
-#define CHROME_BROWSER_SIGNIN_OAUTH2_TOKEN_SERVICE_TEST_UTIL_H_
-
-#include "chrome/browser/signin/oauth2_token_service.h"
-
-#include <string>
-
-#include "base/strings/stringprintf.h"
-#include "google_apis/gaia/google_service_auth_error.h"
-
-extern const char kValidTokenResponse[];
-
-std::string GetValidTokenResponse(std::string token, int expiration);
-
-// A simple testing consumer.
-class TestingOAuth2TokenServiceConsumer : public OAuth2TokenService::Consumer {
- public:
-  TestingOAuth2TokenServiceConsumer();
-  virtual ~TestingOAuth2TokenServiceConsumer();
-
-  // OAuth2TokenService::Consumer overrides.
-  virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
-                                 const std::string& token,
-                                 const base::Time& expiration_date) OVERRIDE;
-  virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request,
-                                 const GoogleServiceAuthError& error) OVERRIDE;
-
-  std::string last_token_;
-  int number_of_successful_tokens_;
-  GoogleServiceAuthError last_error_;
-  int number_of_errors_;
-};
-
-#endif  // CHROME_BROWSER_SIGNIN_OAUTH2_TOKEN_SERVICE_TEST_UTIL_H_
diff --git a/chrome/browser/signin/oauth2_token_service_unittest.cc b/chrome/browser/signin/oauth2_token_service_unittest.cc
deleted file mode 100644
index 730c303..0000000
--- a/chrome/browser/signin/oauth2_token_service_unittest.cc
+++ /dev/null
@@ -1,525 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <string>
-
-#include "base/run_loop.h"
-#include "base/strings/stringprintf.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
-#include "chrome/browser/signin/oauth2_token_service_test_util.h"
-#include "chrome/browser/signin/token_service_factory.h"
-#include "chrome/browser/signin/token_service_unittest.h"
-#include "chrome/test/base/testing_browser_process.h"
-#include "content/public/browser/browser_thread.h"
-#include "google_apis/gaia/gaia_constants.h"
-#include "google_apis/gaia/google_service_auth_error.h"
-#include "google_apis/gaia/oauth2_access_token_consumer.h"
-#include "net/http/http_status_code.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_status.h"
-#include "net/url_request/url_request_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using content::BrowserThread;
-
-// A testing consumer that retries on error.
-class RetryingTestingOAuth2TokenServiceConsumer
-    : public TestingOAuth2TokenServiceConsumer {
- public:
-  RetryingTestingOAuth2TokenServiceConsumer(
-      OAuth2TokenService* oauth2_service)
-      : oauth2_service_(oauth2_service) {}
-  virtual ~RetryingTestingOAuth2TokenServiceConsumer() {}
-
-  virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request,
-                                 const GoogleServiceAuthError& error) OVERRIDE {
-    TestingOAuth2TokenServiceConsumer::OnGetTokenFailure(request, error);
-    request_.reset(oauth2_service_->StartRequest(
-        std::set<std::string>(), this).release());
-  }
-
-  OAuth2TokenService* oauth2_service_;
-  scoped_ptr<OAuth2TokenService::Request> request_;
-};
-
-class TestOAuth2TokenService : public OAuth2TokenService {
- public:
-  explicit TestOAuth2TokenService(net::TestURLRequestContextGetter* getter)
-      : request_context_getter_(getter) {
-  }
-
-  void CancelAllRequestsForTest() { CancelAllRequests(); }
-
-  void CancelRequestsForTokenForTest(const std::string& refresh_token) {
-    CancelRequestsForToken(refresh_token);
-  }
-
-  // For testing: set the refresh token to be used.
-  void set_refresh_token(const std::string& refresh_token) {
-    refresh_token_ = refresh_token;
-  }
-
- protected:
-  virtual std::string GetRefreshToken() OVERRIDE { return refresh_token_; }
-
- private:
-  // OAuth2TokenService implementation.
-  virtual net::URLRequestContextGetter* GetRequestContext() OVERRIDE {
-    return request_context_getter_.get();
-  }
-
-  std::string refresh_token_;
-  scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
-};
-
-class OAuth2TokenServiceTest : public TokenServiceTestHarness {
- public:
-  virtual void SetUp() OVERRIDE {
-    TokenServiceTestHarness::SetUp();
-    oauth2_service_.reset(
-        new TestOAuth2TokenService(new net::TestURLRequestContextGetter(
-            BrowserThread::GetMessageLoopProxyForThread(
-                BrowserThread::IO))));
-  }
-
- protected:
-  net::TestURLFetcherFactory factory_;
-  scoped_ptr<TestOAuth2TokenService> oauth2_service_;
-  TestingOAuth2TokenServiceConsumer consumer_;
-};
-
-TEST_F(OAuth2TokenServiceTest, NoOAuth2RefreshToken) {
-  scoped_ptr<OAuth2TokenService::Request> request(
-      oauth2_service_->StartRequest(std::set<std::string>(), &consumer_));
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(1, consumer_.number_of_errors_);
-}
-
-TEST_F(OAuth2TokenServiceTest, FailureShouldNotRetry) {
-  oauth2_service_->set_refresh_token("refreshToken");
-  scoped_ptr<OAuth2TokenService::Request> request(
-      oauth2_service_->StartRequest(std::set<std::string>(), &consumer_));
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  EXPECT_TRUE(fetcher);
-  fetcher->set_response_code(net::HTTP_UNAUTHORIZED);
-  fetcher->SetResponseString(std::string());
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
-  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(1, consumer_.number_of_errors_);
-  EXPECT_EQ(fetcher, factory_.GetFetcherByID(0));
-}
-
-TEST_F(OAuth2TokenServiceTest, SuccessWithoutCaching) {
-  oauth2_service_->set_refresh_token("refreshToken");
-  scoped_ptr<OAuth2TokenService::Request> request(
-      oauth2_service_->StartRequest(std::set<std::string>(), &consumer_));
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  EXPECT_TRUE(fetcher);
-  fetcher->set_response_code(net::HTTP_OK);
-  fetcher->SetResponseString(GetValidTokenResponse("token", 3600));
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
-  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  EXPECT_EQ("token", consumer_.last_token_);
-}
-
-TEST_F(OAuth2TokenServiceTest, SuccessWithCaching) {
-  std::set<std::string> scopes1;
-  scopes1.insert("s1");
-  scopes1.insert("s2");
-  std::set<std::string> scopes1_same;
-  scopes1_same.insert("s2");
-  scopes1_same.insert("s1");
-  std::set<std::string> scopes2;
-  scopes2.insert("s3");
-
-  oauth2_service_->set_refresh_token("refreshToken");
-
-  // First request.
-  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
-      scopes1, &consumer_));
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  EXPECT_TRUE(fetcher);
-  fetcher->set_response_code(net::HTTP_OK);
-  fetcher->SetResponseString(GetValidTokenResponse("token", 3600));
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
-  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  EXPECT_EQ("token", consumer_.last_token_);
-
-  // Second request to the same set of scopes, should return the same token
-  // without needing a network request.
-  scoped_ptr<OAuth2TokenService::Request> request2(
-      oauth2_service_->StartRequest(scopes1_same, &consumer_));
-  base::RunLoop().RunUntilIdle();
-
-  // No new network fetcher.
-  EXPECT_EQ(fetcher, factory_.GetFetcherByID(0));
-  EXPECT_EQ(2, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  EXPECT_EQ("token", consumer_.last_token_);
-
-  // Third request to a new set of scopes, should return another token.
-  scoped_ptr<OAuth2TokenService::Request> request3(
-      oauth2_service_->StartRequest(scopes2, &consumer_));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(2, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  fetcher = factory_.GetFetcherByID(0);
-  EXPECT_TRUE(fetcher);
-  fetcher->set_response_code(net::HTTP_OK);
-  fetcher->SetResponseString(GetValidTokenResponse("token2", 3600));
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
-  EXPECT_EQ(3, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  EXPECT_EQ("token2", consumer_.last_token_);
-}
-
-TEST_F(OAuth2TokenServiceTest, SuccessAndExpirationAndFailure) {
-  oauth2_service_->set_refresh_token("refreshToken");
-
-  // First request.
-  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
-      std::set<std::string>(), &consumer_));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  EXPECT_TRUE(fetcher);
-  fetcher->set_response_code(net::HTTP_OK);
-  fetcher->SetResponseString(GetValidTokenResponse("token", 0));
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
-  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  EXPECT_EQ("token", consumer_.last_token_);
-
-  // Second request must try to access the network as the token has expired.
-  scoped_ptr<OAuth2TokenService::Request> request2(
-      oauth2_service_->StartRequest(std::set<std::string>(), &consumer_));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-
-  // Network failure.
-  fetcher = factory_.GetFetcherByID(0);
-  EXPECT_TRUE(fetcher);
-  fetcher->set_response_code(net::HTTP_UNAUTHORIZED);
-  fetcher->SetResponseString(std::string());
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
-  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(1, consumer_.number_of_errors_);
-}
-
-TEST_F(OAuth2TokenServiceTest, SuccessAndExpirationAndSuccess) {
-  oauth2_service_->set_refresh_token("refreshToken");
-
-  // First request.
-  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
-      std::set<std::string>(), &consumer_));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  EXPECT_TRUE(fetcher);
-  fetcher->set_response_code(net::HTTP_OK);
-  fetcher->SetResponseString(GetValidTokenResponse("token", 0));
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
-  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  EXPECT_EQ("token", consumer_.last_token_);
-
-  // Second request must try to access the network as the token has expired.
-  scoped_ptr<OAuth2TokenService::Request> request2(
-      oauth2_service_->StartRequest(std::set<std::string>(), &consumer_));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-
-  fetcher = factory_.GetFetcherByID(0);
-  EXPECT_TRUE(fetcher);
-  fetcher->set_response_code(net::HTTP_OK);
-  fetcher->SetResponseString(GetValidTokenResponse("another token", 0));
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
-  EXPECT_EQ(2, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  EXPECT_EQ("another token", consumer_.last_token_);
-}
-
-TEST_F(OAuth2TokenServiceTest, RequestDeletedBeforeCompletion) {
-  oauth2_service_->set_refresh_token("refreshToken");
-
-  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
-      std::set<std::string>(), &consumer_));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  EXPECT_TRUE(fetcher);
-
-  request.reset();
-
-  fetcher->set_response_code(net::HTTP_OK);
-  fetcher->SetResponseString(GetValidTokenResponse("token", 3600));
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
-  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-}
-
-TEST_F(OAuth2TokenServiceTest, RequestDeletedAfterCompletion) {
-  oauth2_service_->set_refresh_token("refreshToken");
-
-  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
-      std::set<std::string>(), &consumer_));
-  base::RunLoop().RunUntilIdle();
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  fetcher->set_response_code(net::HTTP_OK);
-  fetcher->SetResponseString(GetValidTokenResponse("token", 3600));
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
-  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  EXPECT_EQ("token", consumer_.last_token_);
-
-  request.reset();
-
-  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  EXPECT_EQ("token", consumer_.last_token_);
-}
-
-TEST_F(OAuth2TokenServiceTest, MultipleRequestsForTheSameScopesWithOneDeleted) {
-  oauth2_service_->set_refresh_token("refreshToken");
-
-  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
-      std::set<std::string>(), &consumer_));
-  base::RunLoop().RunUntilIdle();
-  scoped_ptr<OAuth2TokenService::Request> request2(
-      oauth2_service_->StartRequest(std::set<std::string>(), &consumer_));
-  base::RunLoop().RunUntilIdle();
-
-  request.reset();
-
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  fetcher->set_response_code(net::HTTP_OK);
-  fetcher->SetResponseString(GetValidTokenResponse("token", 3600));
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
-  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-}
-
-TEST_F(OAuth2TokenServiceTest, ClearedRefreshTokenFailsSubsequentRequests) {
-  // We have a valid refresh token; the first request is successful.
-  oauth2_service_->set_refresh_token("refreshToken");
-  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
-      std::set<std::string>(), &consumer_));
-  base::RunLoop().RunUntilIdle();
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  fetcher->set_response_code(net::HTTP_OK);
-  fetcher->SetResponseString(GetValidTokenResponse("token", 3600));
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
-  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  EXPECT_EQ("token", consumer_.last_token_);
-
-  // The refresh token is no longer available; subsequent requests fail.
-  oauth2_service_->set_refresh_token("");
-  request = oauth2_service_->StartRequest(std::set<std::string>(), &consumer_);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(fetcher, factory_.GetFetcherByID(0));
-  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(1, consumer_.number_of_errors_);
-}
-
-TEST_F(OAuth2TokenServiceTest,
-       ChangedRefreshTokenDoesNotAffectInFlightRequests) {
-  oauth2_service_->set_refresh_token("first refreshToken");
-  std::set<std::string> scopes;
-  scopes.insert("s1");
-  scopes.insert("s2");
-
-  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
-      scopes, &consumer_));
-  base::RunLoop().RunUntilIdle();
-  net::TestURLFetcher* fetcher1 = factory_.GetFetcherByID(0);
-
-  // Note |request| is still pending when the refresh token changes.
-  oauth2_service_->set_refresh_token("second refreshToken");
-
-  // A 2nd request (using the new refresh token) that occurs and completes
-  // while the 1st request is in flight is successful.
-  TestingOAuth2TokenServiceConsumer consumer2;
-  scoped_ptr<OAuth2TokenService::Request> request2(
-      oauth2_service_->StartRequest(scopes, &consumer2));
-  base::RunLoop().RunUntilIdle();
-
-  net::TestURLFetcher* fetcher2 = factory_.GetFetcherByID(0);
-  fetcher2->set_response_code(net::HTTP_OK);
-  fetcher2->SetResponseString(GetValidTokenResponse("second token", 3600));
-  fetcher2->delegate()->OnURLFetchComplete(fetcher2);
-  EXPECT_EQ(1, consumer2.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer2.number_of_errors_);
-  EXPECT_EQ("second token", consumer2.last_token_);
-
-  fetcher1->set_response_code(net::HTTP_OK);
-  fetcher1->SetResponseString(GetValidTokenResponse("first token", 3600));
-  fetcher1->delegate()->OnURLFetchComplete(fetcher1);
-  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  EXPECT_EQ("first token", consumer_.last_token_);
-}
-
-TEST_F(OAuth2TokenServiceTest, ServiceShutDownBeforeFetchComplete) {
-  oauth2_service_->set_refresh_token("refreshToken");
-  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
-      std::set<std::string>(), &consumer_));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-
-  // The destructor should cancel all in-flight fetchers.
-  oauth2_service_.reset(NULL);
-
-  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(1, consumer_.number_of_errors_);
-}
-
-TEST_F(OAuth2TokenServiceTest, RetryingConsumer) {
-  oauth2_service_->set_refresh_token("refreshToken");
-  RetryingTestingOAuth2TokenServiceConsumer consumer(oauth2_service_.get());
-  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
-      std::set<std::string>(), &consumer));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(0, consumer.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer.number_of_errors_);
-
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  ASSERT_TRUE(fetcher);
-  fetcher->set_response_code(net::HTTP_UNAUTHORIZED);
-  fetcher->SetResponseString(std::string());
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
-  EXPECT_EQ(0, consumer.number_of_successful_tokens_);
-  EXPECT_EQ(1, consumer.number_of_errors_);
-
-  fetcher = factory_.GetFetcherByID(0);
-  ASSERT_TRUE(fetcher);
-  fetcher->set_response_code(net::HTTP_UNAUTHORIZED);
-  fetcher->SetResponseString(std::string());
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
-  EXPECT_EQ(0, consumer.number_of_successful_tokens_);
-  EXPECT_EQ(2, consumer.number_of_errors_);
-}
-
-TEST_F(OAuth2TokenServiceTest, InvalidateToken) {
-  std::set<std::string> scopes;
-  oauth2_service_->set_refresh_token("refreshToken");
-
-  // First request.
-  scoped_ptr<OAuth2TokenService::Request> request(
-      oauth2_service_->StartRequest(scopes, &consumer_));
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  EXPECT_TRUE(fetcher);
-  fetcher->set_response_code(net::HTTP_OK);
-  fetcher->SetResponseString(GetValidTokenResponse("token", 3600));
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
-  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  EXPECT_EQ("token", consumer_.last_token_);
-
-  // Second request, should return the same token without needing a network
-  // request.
-  scoped_ptr<OAuth2TokenService::Request> request2(
-      oauth2_service_->StartRequest(scopes, &consumer_));
-  base::RunLoop().RunUntilIdle();
-
-  // No new network fetcher.
-  EXPECT_EQ(fetcher, factory_.GetFetcherByID(0));
-  EXPECT_EQ(2, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  EXPECT_EQ("token", consumer_.last_token_);
-
-  // Invalidating the token should return a new token on the next request.
-  oauth2_service_->InvalidateToken(scopes, consumer_.last_token_);
-  scoped_ptr<OAuth2TokenService::Request> request3(
-      oauth2_service_->StartRequest(scopes, &consumer_));
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(2, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  fetcher = factory_.GetFetcherByID(0);
-  EXPECT_TRUE(fetcher);
-  fetcher->set_response_code(net::HTTP_OK);
-  fetcher->SetResponseString(GetValidTokenResponse("token2", 3600));
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
-  EXPECT_EQ(3, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-  EXPECT_EQ("token2", consumer_.last_token_);
-}
-
-TEST_F(OAuth2TokenServiceTest, CancelAllRequests) {
-  oauth2_service_->set_refresh_token("refreshToken");
-  scoped_ptr<OAuth2TokenService::Request> request(
-      oauth2_service_->StartRequest(std::set<std::string>(), &consumer_));
-
-  oauth2_service_->set_refresh_token("refreshToken2");
-  scoped_ptr<OAuth2TokenService::Request> request2(
-      oauth2_service_->StartRequest(std::set<std::string>(), &consumer_));
-
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-
-  oauth2_service_->CancelAllRequestsForTest();
-
-  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(2, consumer_.number_of_errors_);
-}
-
-TEST_F(OAuth2TokenServiceTest, CancelRequestsForToken) {
-  std::set<std::string> scope_set_1;
-  scope_set_1.insert("scope1");
-  scope_set_1.insert("scope2");
-  std::set<std::string> scope_set_2(scope_set_1.begin(), scope_set_1.end());
-  scope_set_2.insert("scope3");
-
-  oauth2_service_->set_refresh_token("refreshToken");
-  scoped_ptr<OAuth2TokenService::Request> request1(
-      oauth2_service_->StartRequest(scope_set_1, &consumer_));
-  scoped_ptr<OAuth2TokenService::Request> request2(
-      oauth2_service_->StartRequest(scope_set_2, &consumer_));
-
-  oauth2_service_->set_refresh_token("refreshToken2");
-  scoped_ptr<OAuth2TokenService::Request> request3(
-      oauth2_service_->StartRequest(scope_set_1, &consumer_));
-
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(0, consumer_.number_of_errors_);
-
-  oauth2_service_->CancelRequestsForTokenForTest("refreshToken");
-
-  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(2, consumer_.number_of_errors_);
-
-  oauth2_service_->CancelRequestsForTokenForTest("refreshToken2");
-
-  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
-  EXPECT_EQ(3, consumer_.number_of_errors_);
-}
diff --git a/chrome/browser/signin/profile_oauth2_token_service.h b/chrome/browser/signin/profile_oauth2_token_service.h
index a920e5e..79a5329 100644
--- a/chrome/browser/signin/profile_oauth2_token_service.h
+++ b/chrome/browser/signin/profile_oauth2_token_service.h
@@ -8,12 +8,12 @@
 #include <string>
 
 #include "base/gtest_prod_util.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "chrome/browser/signin/signin_global_error.h"
 #include "components/browser_context_keyed_service/browser_context_keyed_service.h"
 #include "components/webdata/common/web_data_service_consumer.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 
 namespace net {
 class URLRequestContextGetter;
diff --git a/chrome/browser/signin/profile_oauth2_token_service_request.h b/chrome/browser/signin/profile_oauth2_token_service_request.h
index bcb1ff8..022623a 100644
--- a/chrome/browser/signin/profile_oauth2_token_service_request.h
+++ b/chrome/browser/signin/profile_oauth2_token_service_request.h
@@ -9,7 +9,7 @@
 #include <string>
 
 #include "base/threading/non_thread_safe.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 
 class Profile;
 
diff --git a/chrome/browser/signin/profile_oauth2_token_service_request_unittest.cc b/chrome/browser/signin/profile_oauth2_token_service_request_unittest.cc
index 6fb6e5b..6c1336e 100644
--- a/chrome/browser/signin/profile_oauth2_token_service_request_unittest.cc
+++ b/chrome/browser/signin/profile_oauth2_token_service_request_unittest.cc
@@ -7,13 +7,13 @@
 #include <string>
 #include <vector>
 #include "base/threading/thread.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "chrome/browser/signin/profile_oauth2_token_service.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/token_service_factory.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/test_browser_thread.h"
 #include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
diff --git a/chrome/browser/signin/profile_oauth2_token_service_unittest.cc b/chrome/browser/signin/profile_oauth2_token_service_unittest.cc
index 2092755..50f5914 100644
--- a/chrome/browser/signin/profile_oauth2_token_service_unittest.cc
+++ b/chrome/browser/signin/profile_oauth2_token_service_unittest.cc
@@ -3,13 +3,13 @@
 // found in the LICENSE file.
 
 #include "base/run_loop.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
-#include "chrome/browser/signin/oauth2_token_service_test_util.h"
 #include "chrome/browser/signin/profile_oauth2_token_service.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/token_service_unittest.h"
 #include "content/public/browser/browser_thread.h"
 #include "google_apis/gaia/gaia_constants.h"
+#include "google_apis/gaia/oauth2_token_service.h"
+#include "google_apis/gaia/oauth2_token_service_test_util.h"
 #include "net/http/http_status_code.h"
 #include "net/url_request/test_url_fetcher_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/signin/ubertoken_fetcher.h b/chrome/browser/signin/ubertoken_fetcher.h
index b7dd6d0..649f41c 100644
--- a/chrome/browser/signin/ubertoken_fetcher.h
+++ b/chrome/browser/signin/ubertoken_fetcher.h
@@ -6,8 +6,8 @@
 #define CHROME_BROWSER_SIGNIN_UBERTOKEN_FETCHER_H_
 
 #include "base/memory/scoped_ptr.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "google_apis/gaia/gaia_auth_consumer.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 
 // Allow to retrieves an uber-auth token for the user. This class uses the
 // |OAuth2TokenService| and considers that the user is already logged in. It
diff --git a/chrome/browser/speech/tts_controller.cc b/chrome/browser/speech/tts_controller.cc
index 2ae1678..219c241 100644
--- a/chrome/browser/speech/tts_controller.cc
+++ b/chrome/browser/speech/tts_controller.cc
@@ -74,6 +74,7 @@
       id_(next_utterance_id_++),
       src_id_(-1),
       event_delegate_(NULL),
+      gender_(TTS_GENDER_NONE),
       can_enqueue_(false),
       char_index_(0),
       finished_(false) {
diff --git a/chrome/browser/spellchecker/feedback_sender_unittest.cc b/chrome/browser/spellchecker/feedback_sender_unittest.cc
index 5c0b1db..ed6ac5b 100644
--- a/chrome/browser/spellchecker/feedback_sender_unittest.cc
+++ b/chrome/browser/spellchecker/feedback_sender_unittest.cc
@@ -15,11 +15,11 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/metrics/entropy_provider.h"
 #include "chrome/common/spellcheck_common.h"
 #include "chrome/common/spellcheck_marker.h"
 #include "chrome/common/spellcheck_result.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/variations/entropy_provider.h"
 #include "content/public/test/test_browser_thread.h"
 #include "net/url_request/test_url_fetcher_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/spellchecker/spellcheck_message_filter_mac.cc b/chrome/browser/spellchecker/spellcheck_message_filter_mac.cc
index c0e469b..829a426 100644
--- a/chrome/browser/spellchecker/spellcheck_message_filter_mac.cc
+++ b/chrome/browser/spellchecker/spellcheck_message_filter_mac.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <functional>
 
+#include "base/barrier_closure.h"
 #include "base/bind.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/spellchecker/spellcheck_factory.h"
@@ -60,8 +61,8 @@
   std::vector<SpellCheckResult> local_results_;
   std::vector<SpellCheckResult> remote_results_;
 
-  bool local_pending_;
-  bool remote_pending_;
+  // Barrier closure for completion of both remote and local check.
+  base::Closure completion_barrier_;
   bool remote_success_;
 
   SpellingServiceClient* client_;  // Owned by |destination|.
@@ -77,9 +78,7 @@
 SpellingRequest::SpellingRequest(SpellingServiceClient* client,
                                  content::BrowserMessageFilter* destination,
                                  int render_process_id)
-    : local_pending_(true),
-      remote_pending_(true),
-      remote_success_(false),
+    : remote_success_(false),
       client_(client),
       destination_(destination),
       render_process_id_(render_process_id),
@@ -104,6 +103,10 @@
   markers_ = markers;
 
   // Send the remote query out.
+  completion_barrier_ =
+      BarrierClosure(2,
+                     base::Bind(&SpellingRequest::OnCheckCompleted,
+                     base::Owned(this)));
   RequestRemoteCheck(text);
   RequestLocalCheck(text, document_tag_);
 }
@@ -134,10 +137,6 @@
 
 void SpellingRequest::OnCheckCompleted() {
   // Final completion can happen on any thread - don't DCHECK thread.
-
-  if (local_pending_ || remote_pending_)
-    return;
-
   const std::vector<SpellCheckResult>* check_results = &local_results_;
   if (remote_success_) {
     std::sort(remote_results_.begin(), remote_results_.end(), CompareLocation);
@@ -155,7 +154,6 @@
   destination_->Release();
 
   // Object is self-managed - at this point, its life span is over.
-  delete this;
 }
 
 void SpellingRequest::OnRemoteCheckCompleted(
@@ -165,7 +163,6 @@
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
   remote_success_ = success;
   remote_results_ = results;
-  remote_pending_ = false;
 
   SpellcheckService* spellcheck_service =
       SpellcheckServiceFactory::GetForRenderProcessId(render_process_id_);
@@ -177,17 +174,14 @@
         &remote_results_);
   }
 
-  OnCheckCompleted();
+  completion_barrier_.Run();
 }
 
 void SpellingRequest::OnLocalCheckCompleted(
     const std::vector<SpellCheckResult>& results) {
   // Local checking can happen on any thread - don't DCHECK thread.
-
   local_results_ = results;
-  local_pending_ = false;
-
-  OnCheckCompleted();
+  completion_barrier_.Run();
 }
 
 
diff --git a/chrome/browser/ssl/ssl_browser_tests.cc b/chrome/browser/ssl/ssl_browser_tests.cc
index 334f0c1..c8226f3 100644
--- a/chrome/browser/ssl/ssl_browser_tests.cc
+++ b/chrome/browser/ssl/ssl_browser_tests.cc
@@ -398,8 +398,8 @@
                                  false);  // No interstitial showing
 }
 
-#if defined(OS_WIN)
-// Flaky on Windows (http://crbug.com/267653).
+#ifndef NEDBUG
+// Flaky on Windows debug (http://crbug.com/280537).
 #define MAYBE_TestHTTPSExpiredCertAndDontProceed \
         DISABLED_TestHTTPSExpiredCertAndDontProceed
 #else
@@ -409,7 +409,7 @@
 
 // Visits a page with https error and don't proceed (and ensure we can still
 // navigate at that point):
-IN_PROC_BROWSER_TEST_F(SSLUITest, TestHTTPSExpiredCertAndDontProceed) {
+IN_PROC_BROWSER_TEST_F(SSLUITest, MAYBE_TestHTTPSExpiredCertAndDontProceed) {
   ASSERT_TRUE(test_server()->Start());
   ASSERT_TRUE(https_server_.Start());
   ASSERT_TRUE(https_server_expired_.Start());
@@ -1522,15 +1522,6 @@
   CheckAuthenticatedState(tab, false);
 }
 
-#if defined(OS_WIN)
-// Flaky on Windows (http://crbug.com/267653).
-#define MAYBE_TestUnsafeContentsInWorker \
-        DISABLED_TestUnsafeContentsInWorker
-#else
-#define MAYBE_TestUnsafeContentsInWorker \
-        TestUnsafeContentsInWorker
-#endif
-
 IN_PROC_BROWSER_TEST_F(SSLUITest, TestUnsafeContentsInWorker) {
   ASSERT_TRUE(https_server_.Start());
   ASSERT_TRUE(https_server_expired_.Start());
@@ -1578,15 +1569,6 @@
       browser()->tab_strip_model()->GetActiveWebContents(), false);
 }
 
-#if defined(OS_WIN)
-// Flaky on Windows (http://crbug.com/267653).
-#define MAYBE_TestBlockDisplayingInsecureIframe \
-        DISABLED_TestBlockDisplayingInsecureIframe
-#else
-#define MAYBE_TestBlockDisplayingInsecureIframe \
-        TestBlockDisplayingInsecureIframe
-#endif
-
 // Test that when the browser blocks displaying insecure content (iframes), the
 // indicator shows a secure page, because the blocking made the otherwise
 // unsafe page safe (the notification of this state is handled by other means)
diff --git a/chrome/browser/status_icons/status_icon.cc b/chrome/browser/status_icons/status_icon.cc
index 00270d2..458fb35 100644
--- a/chrome/browser/status_icons/status_icon.cc
+++ b/chrome/browser/status_icons/status_icon.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/status_icons/status_icon.h"
 
 #include "chrome/browser/status_icons/status_icon_observer.h"
-#include "ui/base/models/menu_model.h"
 
 StatusIcon::StatusIcon() {
 }
@@ -22,7 +21,7 @@
 }
 
 bool StatusIcon::HasObservers() const {
-  return observers_.size() > 0;
+  return observers_.might_have_observers();
 }
 
 void StatusIcon::DispatchClickEvent() {
@@ -35,10 +34,10 @@
 }
 #endif
 
-void StatusIcon::SetContextMenu(ui::MenuModel* menu) {
+void StatusIcon::SetContextMenu(scoped_ptr<StatusIconMenuModel> menu) {
   // The UI may been showing a menu for the current model, don't destroy it
   // until we've notified the UI of the change.
-  scoped_ptr<ui::MenuModel> old_menu = context_menu_contents_.Pass();
-  context_menu_contents_.reset(menu);
-  UpdatePlatformContextMenu(menu);
+  scoped_ptr<StatusIconMenuModel> old_menu = context_menu_contents_.Pass();
+  context_menu_contents_ = menu.Pass();
+  UpdatePlatformContextMenu(context_menu_contents_.get());
 }
diff --git a/chrome/browser/status_icons/status_icon.h b/chrome/browser/status_icons/status_icon.h
index ccc8c2e..5e50961 100644
--- a/chrome/browser/status_icons/status_icon.h
+++ b/chrome/browser/status_icons/status_icon.h
@@ -9,15 +9,12 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/observer_list.h"
 #include "base/strings/string16.h"
+#include "chrome/browser/status_icons/status_icon_menu_model.h"
 
 namespace gfx {
 class ImageSkia;
 }
 
-namespace ui {
-class MenuModel;
-}
-
 class StatusIconObserver;
 
 class StatusIcon {
@@ -45,7 +42,7 @@
 
   // Set the context menu for this icon. The icon takes ownership of the passed
   // context menu. Passing NULL results in no menu at all.
-  void SetContextMenu(ui::MenuModel* menu);
+  void SetContextMenu(scoped_ptr<StatusIconMenuModel> menu);
 
   // Adds/Removes an observer for clicks on the status icon. If an observer is
   // registered, then left clicks on the status icon will result in the observer
@@ -67,13 +64,13 @@
   // Invoked after a call to SetContextMenu() to let the platform-specific
   // subclass update the native context menu based on the new model. If NULL is
   // passed, subclass should destroy the native context menu.
-  virtual void UpdatePlatformContextMenu(ui::MenuModel* model) = 0;
+  virtual void UpdatePlatformContextMenu(StatusIconMenuModel* model) = 0;
 
  private:
   ObserverList<StatusIconObserver> observers_;
 
   // Context menu, if any.
-  scoped_ptr<ui::MenuModel> context_menu_contents_;
+  scoped_ptr<StatusIconMenuModel> context_menu_contents_;
 
   DISALLOW_COPY_AND_ASSIGN(StatusIcon);
 };
diff --git a/chrome/browser/status_icons/status_icon_menu_model.cc b/chrome/browser/status_icons/status_icon_menu_model.cc
new file mode 100644
index 0000000..bfb7a4b
--- /dev/null
+++ b/chrome/browser/status_icons/status_icon_menu_model.cc
@@ -0,0 +1,181 @@
+// 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 "chrome/browser/status_icons/status_icon_menu_model.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/image/image.h"
+
+struct StatusIconMenuModel::ItemState {
+  ItemState()
+      : checked(false),
+        enabled(true),
+        visible(true),
+        is_dynamic(false) {}
+  bool checked;
+  bool enabled;
+  bool visible;
+  bool is_dynamic;
+  ui::Accelerator accelerator;
+  base::string16 label;
+  base::string16 sublabel;
+  gfx::Image icon;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// StatusIconMenuModel::Delegate, public:
+
+void StatusIconMenuModel::Delegate::CommandIdHighlighted(int command_id) {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// StatusIconMenuModel, public:
+
+StatusIconMenuModel::StatusIconMenuModel(Delegate* delegate)
+    : ui::SimpleMenuModel(this), delegate_(delegate) {
+}
+
+StatusIconMenuModel::~StatusIconMenuModel() {
+}
+
+void StatusIconMenuModel::SetCommandIdChecked(int command_id, bool checked) {
+  item_states_[command_id].checked = checked;
+  NotifyMenuStateChanged();
+}
+
+void StatusIconMenuModel::SetCommandIdEnabled(int command_id, bool enabled) {
+  item_states_[command_id].enabled = enabled;
+  NotifyMenuStateChanged();
+}
+
+void StatusIconMenuModel::SetCommandIdVisible(int command_id, bool visible) {
+  item_states_[command_id].visible = visible;
+  NotifyMenuStateChanged();
+}
+
+void StatusIconMenuModel::SetAcceleratorForCommandId(
+    int command_id, const ui::Accelerator* accelerator) {
+  item_states_[command_id].accelerator = *accelerator;
+  NotifyMenuStateChanged();
+}
+
+void StatusIconMenuModel::ChangeLabelForCommandId(int command_id,
+                                                  const base::string16& label) {
+  item_states_[command_id].is_dynamic = true;
+  item_states_[command_id].label = label;
+  NotifyMenuStateChanged();
+}
+
+void StatusIconMenuModel::ChangeSublabelForCommandId(
+    int command_id, const base::string16& sublabel) {
+  item_states_[command_id].is_dynamic = true;
+  item_states_[command_id].sublabel = sublabel;
+  NotifyMenuStateChanged();
+}
+
+void StatusIconMenuModel::ChangeIconForCommandId(
+    int command_id, const gfx::Image& icon) {
+  item_states_[command_id].is_dynamic = true;
+  item_states_[command_id].icon = icon;
+  NotifyMenuStateChanged();
+}
+
+void StatusIconMenuModel::AddObserver(Observer* observer) {
+  observer_list_.AddObserver(observer);
+}
+
+void StatusIconMenuModel::RemoveObserver(Observer* observer) {
+  observer_list_.RemoveObserver(observer);
+}
+
+bool StatusIconMenuModel::IsCommandIdChecked(int command_id) const {
+  ItemStateMap::const_iterator iter = item_states_.find(command_id);
+  if (iter != item_states_.end())
+    return iter->second.checked;
+  return false;
+}
+
+bool StatusIconMenuModel::IsCommandIdEnabled(int command_id) const {
+  ItemStateMap::const_iterator iter = item_states_.find(command_id);
+  if (iter != item_states_.end())
+    return iter->second.enabled;
+  return true;
+}
+
+bool StatusIconMenuModel::IsCommandIdVisible(int command_id) const {
+  ItemStateMap::const_iterator iter = item_states_.find(command_id);
+  if (iter != item_states_.end())
+    return iter->second.visible;
+  return true;
+}
+
+bool StatusIconMenuModel::GetAcceleratorForCommandId(
+    int command_id, ui::Accelerator* accelerator) {
+  ItemStateMap::const_iterator iter = item_states_.find(command_id);
+  if (iter != item_states_.end() &&
+      iter->second.accelerator.key_code() != ui::VKEY_UNKNOWN) {
+    *accelerator = iter->second.accelerator;
+    return true;
+  }
+  return false;
+}
+
+bool StatusIconMenuModel::IsItemForCommandIdDynamic(int command_id) const {
+  ItemStateMap::const_iterator iter = item_states_.find(command_id);
+  if (iter != item_states_.end())
+    return iter->second.is_dynamic;
+  return false;
+}
+
+base::string16 StatusIconMenuModel::GetLabelForCommandId(int command_id) const {
+  ItemStateMap::const_iterator iter = item_states_.find(command_id);
+  if (iter != item_states_.end())
+    return iter->second.label;
+  return base::string16();
+}
+
+base::string16 StatusIconMenuModel::GetSublabelForCommandId(
+    int command_id) const {
+  ItemStateMap::const_iterator iter = item_states_.find(command_id);
+  if (iter != item_states_.end())
+    return iter->second.sublabel;
+  return base::string16();
+}
+
+bool StatusIconMenuModel::GetIconForCommandId(int command_id,
+                                              gfx::Image* image_skia) const {
+  ItemStateMap::const_iterator iter = item_states_.find(command_id);
+  if (iter != item_states_.end() && !iter->second.icon.IsEmpty()) {
+    *image_skia = iter->second.icon;
+    return true;
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// StatusIconMenuModel, protected:
+
+void StatusIconMenuModel::MenuItemsChanged() {
+  NotifyMenuStateChanged();
+}
+
+void StatusIconMenuModel::NotifyMenuStateChanged() {
+  FOR_EACH_OBSERVER(Observer, observer_list_, OnMenuStateChanged());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// StatusIconMenuModel, private:
+
+void StatusIconMenuModel::CommandIdHighlighted(int command_id) {
+  if (delegate_)
+    delegate_->CommandIdHighlighted(command_id);
+}
+
+void StatusIconMenuModel::ExecuteCommand(int command_id, int event_flags) {
+  if (delegate_)
+    delegate_->ExecuteCommand(command_id, event_flags);
+}
diff --git a/chrome/browser/status_icons/status_icon_menu_model.h b/chrome/browser/status_icons/status_icon_menu_model.h
new file mode 100644
index 0000000..fa43874
--- /dev/null
+++ b/chrome/browser/status_icons/status_icon_menu_model.h
@@ -0,0 +1,119 @@
+// 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 CHROME_BROWSER_STATUS_ICONS_STATUS_ICON_MENU_MODEL_H_
+#define CHROME_BROWSER_STATUS_ICONS_STATUS_ICON_MENU_MODEL_H_
+
+#include <map>
+
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "ui/base/models/simple_menu_model.h"
+
+namespace gfx {
+class Image;
+}
+
+class StatusIconMenuModelTest;
+
+// StatusIconMenuModel contains the state of the SimpleMenuModel as well as that
+// of its delegate. This is done so that we can easily identify when the menu
+// model state has changed and can tell the status icon to update the menu. This
+// is necessary some platforms which do not notify us before showing the menu
+// (like Ubuntu Unity).
+class StatusIconMenuModel
+    : public ui::SimpleMenuModel,
+      public ui::SimpleMenuModel::Delegate,
+      public base::SupportsWeakPtr<StatusIconMenuModel> {
+ public:
+  class Delegate {
+   public:
+    // Notifies the delegate that the item with the specified command id was
+    // visually highlighted within the menu.
+    virtual void CommandIdHighlighted(int command_id);
+
+    // Performs the action associates with the specified command id.
+    // The passed |event_flags| are the flags from the event which issued this
+    // command and they can be examined to find modifier keys.
+    virtual void ExecuteCommand(int command_id, int event_flags) = 0;
+
+   protected:
+    virtual ~Delegate() {}
+  };
+
+  class Observer {
+   public:
+    // Invoked when the menu model has changed.
+    virtual void OnMenuStateChanged() {}
+
+   protected:
+    virtual ~Observer() {}
+  };
+
+  // The Delegate can be NULL.
+  explicit StatusIconMenuModel(Delegate* delegate);
+  virtual ~StatusIconMenuModel();
+
+  // Methods for seting the state of specific command ids.
+  void SetCommandIdChecked(int command_id, bool checked);
+  void SetCommandIdEnabled(int command_id, bool enabled);
+  void SetCommandIdVisible(int command_id, bool visible);
+
+  // Sets the accelerator for the specified command id.
+  void SetAcceleratorForCommandId(
+      int command_id, const ui::Accelerator* accelerator);
+
+  // Calling any of these "change" methods will mark the menu item as "dynamic"
+  // (see menu_model.h:IsItemDynamicAt) which many platforms take as a cue to
+  // refresh the label, sublabel and icon of the menu item each time the menu is
+  // shown.
+  void ChangeLabelForCommandId(int command_id, const base::string16& label);
+  void ChangeSublabelForCommandId(
+      int command_id, const base::string16& sublabel);
+  void ChangeIconForCommandId(int command_id, const gfx::Image& icon);
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+  // Overridden from ui::SimpleMenuModel::Delegate:
+  virtual bool IsCommandIdChecked(int command_id) const OVERRIDE;
+  virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE;
+  virtual bool IsCommandIdVisible(int command_id) const OVERRIDE;
+  virtual bool GetAcceleratorForCommandId(
+      int command_id, ui::Accelerator* accelerator) OVERRIDE;
+  virtual bool IsItemForCommandIdDynamic(int command_id) const OVERRIDE;
+  virtual base::string16 GetLabelForCommandId(int command_id) const OVERRIDE;
+  virtual base::string16 GetSublabelForCommandId(int command_id) const OVERRIDE;
+  virtual bool GetIconForCommandId(int command_id, gfx::Image* icon) const
+      OVERRIDE;
+
+ protected:
+  // Overriden from ui::SimpleMenuModel:
+  virtual void MenuItemsChanged() OVERRIDE;
+
+  void NotifyMenuStateChanged();
+
+  void set_delegate(Delegate* delegate) { delegate_ = delegate; }
+  Delegate* delegate() { return delegate_; }
+
+ private:
+  // Overridden from ui::SimpleMenuModel::Delegate:
+  virtual void CommandIdHighlighted(int command_id) OVERRIDE;
+  virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE;
+
+  struct ItemState;
+
+  // Map the properties to the command id (used as key).
+  typedef std::map<int, ItemState> ItemStateMap;
+
+  ItemStateMap item_states_;
+
+  ObserverList<Observer> observer_list_;
+
+  Delegate* delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(StatusIconMenuModel);
+};
+
+#endif  // CHROME_BROWSER_STATUS_ICONS_STATUS_ICON_MENU_MODEL_H_
diff --git a/chrome/browser/status_icons/status_icon_menu_model_unittest.cc b/chrome/browser/status_icons/status_icon_menu_model_unittest.cc
new file mode 100644
index 0000000..a81d0fa
--- /dev/null
+++ b/chrome/browser/status_icons/status_icon_menu_model_unittest.cc
@@ -0,0 +1,118 @@
+// 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 "chrome/browser/status_icons/status_icon_menu_model.h"
+
+#include "base/compiler_specific.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/status_icons/status_icon.h"
+#include "chrome/browser/status_icons/status_tray.h"
+#include "grit/chrome_unscaled_resources.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image.h"
+
+class StatusIconMenuModelTest : public testing::Test,
+                                public StatusIconMenuModel::Observer {
+ public:
+  virtual void SetUp() OVERRIDE {
+    menu_.reset(new StatusIconMenuModel(NULL));
+    menu_->AddObserver(this);
+    changed_count_ = 0;
+  }
+
+  virtual void TearDown() OVERRIDE {
+    menu_->RemoveObserver(this);
+  }
+
+  virtual int changed_count() {
+    return changed_count_;
+  }
+
+  StatusIconMenuModel* menu_model() {
+    return menu_.get();
+  }
+
+ private:
+  virtual void OnMenuStateChanged() OVERRIDE {
+    ++changed_count_;
+  }
+
+  scoped_ptr<StatusIconMenuModel> menu_;
+  int changed_count_;
+};
+
+TEST_F(StatusIconMenuModelTest, ToggleBooleanProperties) {
+  menu_model()->AddItem(0, ASCIIToUTF16("foo"));
+
+  menu_model()->SetCommandIdChecked(0, true);
+  EXPECT_TRUE(menu_model()->IsCommandIdChecked(0));
+  menu_model()->SetCommandIdChecked(0, false);
+  EXPECT_FALSE(menu_model()->IsCommandIdChecked(0));
+
+  menu_model()->SetCommandIdEnabled(0, true);
+  EXPECT_TRUE(menu_model()->IsCommandIdEnabled(0));
+  menu_model()->SetCommandIdEnabled(0, false);
+  EXPECT_FALSE(menu_model()->IsCommandIdEnabled(0));
+
+  menu_model()->SetCommandIdVisible(0, true);
+  EXPECT_TRUE(menu_model()->IsCommandIdVisible(0));
+  menu_model()->SetCommandIdVisible(0, false);
+  EXPECT_FALSE(menu_model()->IsCommandIdVisible(0));
+
+  // Menu state should have changed 7 times in this test.
+  EXPECT_EQ(7, changed_count());
+}
+
+TEST_F(StatusIconMenuModelTest, SetProperties) {
+  menu_model()->AddItem(0, ASCIIToUTF16("foo1"));
+  menu_model()->AddItem(1, ASCIIToUTF16("foo2"));
+
+  ui::Accelerator test_accel(ui::VKEY_A, ui::EF_NONE);
+  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+  gfx::Image test_image1(*rb.GetImageSkiaNamed(IDR_STATUS_TRAY_ICON));
+  gfx::Image test_image2(*rb.GetImageSkiaNamed(IDR_STATUS_TRAY_ICON_PRESSED));
+  ui::Accelerator accel_arg;
+  gfx::Image image_arg;
+
+  EXPECT_FALSE(menu_model()->GetAcceleratorForCommandId(0, &accel_arg));
+  EXPECT_FALSE(menu_model()->GetIconForCommandId(0, &image_arg));
+  EXPECT_FALSE(menu_model()->IsItemForCommandIdDynamic(0));
+
+  // Set the accelerator and label for the first menu item.
+  menu_model()->SetAcceleratorForCommandId(0, &test_accel);
+  EXPECT_TRUE(menu_model()->GetAcceleratorForCommandId(0, &accel_arg));
+  EXPECT_EQ(test_accel, accel_arg);
+
+  // Try setting label and changing it. Also ensure that menu item is marked
+  // dynamic since the label has changed.
+  menu_model()->ChangeLabelForCommandId(0, ASCIIToUTF16("label1"));
+  EXPECT_TRUE(menu_model()->IsItemForCommandIdDynamic(0));
+  EXPECT_EQ(ASCIIToUTF16("label1"), menu_model()->GetLabelForCommandId(0));
+  menu_model()->ChangeLabelForCommandId(0, ASCIIToUTF16("label2"));
+  EXPECT_EQ(ASCIIToUTF16("label2"), menu_model()->GetLabelForCommandId(0));
+
+  // Set the sublabel and icon image for the second menu item.
+  menu_model()->ChangeSublabelForCommandId(1, ASCIIToUTF16("sublabel"));
+  EXPECT_EQ(ASCIIToUTF16("sublabel"), menu_model()->GetSublabelForCommandId(1));
+
+  // Try setting icon image and changing it.
+  menu_model()->ChangeIconForCommandId(1, test_image1);
+  EXPECT_TRUE(menu_model()->GetIconForCommandId(1, &image_arg));
+  EXPECT_EQ(image_arg.ToImageSkia(), test_image1.ToImageSkia());
+  menu_model()->ChangeIconForCommandId(1, test_image2);
+  EXPECT_TRUE(menu_model()->GetIconForCommandId(1, &image_arg));
+  EXPECT_EQ(image_arg.ToImageSkia(), test_image2.ToImageSkia());
+
+  // Ensure changes to one menu item does not affect the other menu item.
+  EXPECT_FALSE(menu_model()->GetAcceleratorForCommandId(1, &accel_arg));
+  EXPECT_EQ(string16(), menu_model()->GetLabelForCommandId(1));
+  EXPECT_EQ(string16(), menu_model()->GetSublabelForCommandId(0));
+  EXPECT_FALSE(menu_model()->GetIconForCommandId(0, &image_arg));
+
+  // Menu state should have changed 8 times in this test.
+  EXPECT_EQ(8, changed_count());
+}
diff --git a/chrome/browser/status_icons/status_icon_unittest.cc b/chrome/browser/status_icons/status_icon_unittest.cc
index 8078665..cdc22b7 100644
--- a/chrome/browser/status_icons/status_icon_unittest.cc
+++ b/chrome/browser/status_icons/status_icon_unittest.cc
@@ -21,7 +21,8 @@
   virtual void SetImage(const gfx::ImageSkia& image) OVERRIDE {}
   virtual void SetPressedImage(const gfx::ImageSkia& image) OVERRIDE {}
   virtual void SetToolTip(const string16& tool_tip) OVERRIDE {}
-  virtual void UpdatePlatformContextMenu(ui::MenuModel* menu) OVERRIDE {}
+  virtual void UpdatePlatformContextMenu(
+      StatusIconMenuModel* menu) OVERRIDE {}
   virtual void DisplayBalloon(const gfx::ImageSkia& icon,
                               const string16& title,
                               const string16& contents) OVERRIDE {}
diff --git a/chrome/browser/status_icons/status_tray_unittest.cc b/chrome/browser/status_icons/status_tray_unittest.cc
index 238d8b7..674c8fc 100644
--- a/chrome/browser/status_icons/status_tray_unittest.cc
+++ b/chrome/browser/status_icons/status_tray_unittest.cc
@@ -19,7 +19,8 @@
   virtual void DisplayBalloon(const gfx::ImageSkia& icon,
                               const string16& title,
                               const string16& contents) OVERRIDE {}
-  virtual void UpdatePlatformContextMenu(ui::MenuModel* menu) OVERRIDE {}
+  virtual void UpdatePlatformContextMenu(
+      StatusIconMenuModel* menu) OVERRIDE {}
 };
 
 class TestStatusTray : public StatusTray {
diff --git a/chrome/browser/sync/glue/session_model_associator.cc b/chrome/browser/sync/glue/session_model_associator.cc
index 0598340..10e8feb 100644
--- a/chrome/browser/sync/glue/session_model_associator.cc
+++ b/chrome/browser/sync/glue/session_model_associator.cc
@@ -17,6 +17,9 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/history/history_service.h"
+#if !defined(OS_ANDROID)
+#include "chrome/browser/network_time/navigation_time_helper.h"
+#endif
 #include "chrome/browser/prefs/pref_service_syncable.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sessions/session_id.h"
@@ -503,6 +506,15 @@
                                  tab_delegate.GetEntryCount());
   bool is_managed = tab_delegate.ProfileIsManaged();
   session_tab->navigations.clear();
+
+#if !defined(OS_ANDROID)
+  // For getting navigation time in network time.
+  NavigationTimeHelper* nav_time_helper =
+      tab_delegate.HasWebContents() ?
+          NavigationTimeHelper::FromWebContents(tab_delegate.GetWebContents()) :
+          NULL;
+#endif
+
   for (int i = min_index; i < max_index; ++i) {
     const NavigationEntry* entry = (i == pending_index) ?
         tab_delegate.GetPendingEntry() : tab_delegate.GetEntryAtIndex(i);
@@ -510,8 +522,17 @@
     if (!entry->GetVirtualURL().is_valid())
       continue;
 
+    scoped_ptr<content::NavigationEntry> network_time_entry(
+        content::NavigationEntry::Create(*entry));
+#if !defined(OS_ANDROID)
+    if (nav_time_helper) {
+      network_time_entry->SetTimestamp(
+          nav_time_helper->GetNavigationTime(entry));
+    }
+#endif
+
     session_tab->navigations.push_back(
-        SerializedNavigationEntry::FromNavigationEntry(i, *entry));
+        SerializedNavigationEntry::FromNavigationEntry(i, *network_time_entry));
     if (is_managed) {
       session_tab->navigations.back().set_blocked_state(
           SerializedNavigationEntry::STATE_ALLOWED);
@@ -526,6 +547,7 @@
       session_tab->navigations.push_back(
           SerializedNavigationEntry::FromNavigationEntry(
               i + offset, *blocked_navigations[i]));
+      // Blocked navigations already use network navigation time.
       session_tab->navigations.back().set_blocked_state(
           SerializedNavigationEntry::STATE_BLOCKED);
       // TODO(bauerb): Add categories
diff --git a/chrome/browser/sync/glue/session_model_associator_unittest.cc b/chrome/browser/sync/glue/session_model_associator_unittest.cc
index a883546..a380168 100644
--- a/chrome/browser/sync/glue/session_model_associator_unittest.cc
+++ b/chrome/browser/sync/glue/session_model_associator_unittest.cc
@@ -190,6 +190,7 @@
                      const std::vector<const content::NavigationEntry*>*());
   MOCK_CONST_METHOD0(IsPinned, bool());
   MOCK_CONST_METHOD0(HasWebContents, bool());
+  MOCK_CONST_METHOD0(GetWebContents, content::WebContents*());
   MOCK_CONST_METHOD0(GetSyncId, int());
   MOCK_METHOD1(SetSyncId, void(int));
 };
@@ -325,14 +326,17 @@
       content::NavigationEntry::Create());
   entry1->SetVirtualURL(GURL("http://www.google.com"));
   entry1->SetTimestamp(kTime1);
+  entry1->SetHttpStatusCode(200);
   scoped_ptr<content::NavigationEntry> entry2(
       content::NavigationEntry::Create());
   entry2->SetVirtualURL(GURL("http://www.noodle.com"));
   entry2->SetTimestamp(kTime2);
+  entry2->SetHttpStatusCode(201);
   scoped_ptr<content::NavigationEntry> entry3(
       content::NavigationEntry::Create());
   entry3->SetVirtualURL(GURL("http://www.doodle.com"));
   entry3->SetTimestamp(kTime3);
+  entry3->SetHttpStatusCode(202);
   EXPECT_CALL(tab_mock, GetCurrentEntryIndex()).WillRepeatedly(Return(2));
   EXPECT_CALL(tab_mock, GetEntryAtIndex(0)).WillRepeatedly(
       Return(entry1.get()));
@@ -377,6 +381,9 @@
   EXPECT_EQ(kTime1, session_tab.navigations[0].timestamp());
   EXPECT_EQ(kTime2, session_tab.navigations[1].timestamp());
   EXPECT_EQ(kTime3, session_tab.navigations[2].timestamp());
+  EXPECT_EQ(200, session_tab.navigations[0].http_status_code());
+  EXPECT_EQ(201, session_tab.navigations[1].http_status_code());
+  EXPECT_EQ(202, session_tab.navigations[2].http_status_code());
   EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
             session_tab.navigations[0].blocked_state());
   EXPECT_EQ(SerializedNavigationEntry::STATE_INVALID,
diff --git a/chrome/browser/sync/glue/sync_backend_host.cc b/chrome/browser/sync/glue/sync_backend_host.cc
index 7843081..2f91728 100644
--- a/chrome/browser/sync/glue/sync_backend_host.cc
+++ b/chrome/browser/sync/glue/sync_backend_host.cc
@@ -655,6 +655,14 @@
       GetDataTypesInState(CONFIGURE_INACTIVE, config_state_map);
   types_to_purge.RemoveAll(inactive_types);
 
+  // If a type has already been disabled and unapplied or journaled, it will
+  // not be part of the |types_to_purge| set, and therefore does not need
+  // to be acted on again.
+  fatal_types.RetainAll(types_to_purge);
+  syncer::ModelTypeSet unapply_types =
+      syncer::Union(crypto_types, clean_first_types);
+  unapply_types.RetainAll(types_to_purge);
+
   DCHECK(syncer::Intersection(current_types, fatal_types).Empty());
   DCHECK(syncer::Intersection(current_types, crypto_types).Empty());
   DCHECK(current_types.HasAll(types_to_download));
@@ -678,7 +686,7 @@
                          types_to_download,
                          types_to_purge,
                          fatal_types,
-                         syncer::Union(crypto_types, clean_first_types),
+                         unapply_types,
                          inactive_types,
                          routing_info,
                          ready_task,
diff --git a/chrome/browser/sync/glue/synced_device_tracker.cc b/chrome/browser/sync/glue/synced_device_tracker.cc
index 3db32a9..e4fd61a 100644
--- a/chrome/browser/sync/glue/synced_device_tracker.cc
+++ b/chrome/browser/sync/glue/synced_device_tracker.cc
@@ -32,6 +32,7 @@
     user_share_(user_share),
     cache_guid_(cache_guid),
     local_device_info_tag_(DeviceInfoLookupString(cache_guid)) {
+  observers_ = new ObserverListThreadSafe<Observer>;
 }
 
 SyncedDeviceTracker::~SyncedDeviceTracker() {
@@ -49,7 +50,7 @@
 }
 
 void SyncedDeviceTracker::CommitChangesFromSyncModel() {
-  // TODO(sync): notify our listeners.
+  observers_->Notify(&Observer::OnDeviceInfoChange);
 }
 
 scoped_ptr<DeviceInfo> SyncedDeviceTracker::ReadLocalDeviceInfo() const {
@@ -132,6 +133,14 @@
   }
 }
 
+void SyncedDeviceTracker::AddObserver(Observer* observer) {
+  observers_->AddObserver(observer);
+}
+
+void SyncedDeviceTracker::RemoveObserver(Observer* observer) {
+  observers_->RemoveObserver(observer);
+}
+
 void SyncedDeviceTracker::InitLocalDeviceInfo(const base::Closure& callback) {
   DeviceInfo::CreateLocalDeviceInfo(
       cache_guid_,
diff --git a/chrome/browser/sync/glue/synced_device_tracker.h b/chrome/browser/sync/glue/synced_device_tracker.h
index 877d833..ce72aea 100644
--- a/chrome/browser/sync/glue/synced_device_tracker.h
+++ b/chrome/browser/sync/glue/synced_device_tracker.h
@@ -11,6 +11,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/scoped_vector.h"
 #include "base/memory/weak_ptr.h"
+#include "base/observer_list_threadsafe.h"
 #include "chrome/browser/sync/glue/change_processor.h"
 
 namespace syncer {
@@ -27,6 +28,12 @@
                       const std::string& cache_guid);
   virtual ~SyncedDeviceTracker();
 
+  // Observer class for listening to device info changes.
+  class Observer {
+   public:
+    virtual void OnDeviceInfoChange() = 0;
+  };
+
   // ChangeProcessor methods
   virtual void StartImpl(Profile* profile) OVERRIDE;
   virtual void ApplyChangesFromSyncModel(
@@ -46,6 +53,12 @@
   virtual void GetAllSyncedDeviceInfo(
       ScopedVector<DeviceInfo>* device_info) const;
 
+  // Can be called on any thread. Will be notified back on the same thread
+  // they were called on. Observer will be called on every device info
+  // change.
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
  private:
   friend class SyncedDeviceTrackerTest;
 
@@ -66,6 +79,11 @@
   const std::string cache_guid_;
   const std::string local_device_info_tag_;
 
+  // The |ObserverList| has to be thread safe as the changes happen
+  // on sync thread and the observers could be on any thread.
+  typedef ObserverListThreadSafe<Observer> ObserverList;
+  scoped_refptr<ObserverList> observers_;
+
   DISALLOW_COPY_AND_ASSIGN(SyncedDeviceTracker);
 };
 
diff --git a/chrome/browser/sync/glue/synced_tab_delegate.h b/chrome/browser/sync/glue/synced_tab_delegate.h
index a4033a4..008748e 100644
--- a/chrome/browser/sync/glue/synced_tab_delegate.h
+++ b/chrome/browser/sync/glue/synced_tab_delegate.h
@@ -53,6 +53,7 @@
 
   virtual bool IsPinned() const = 0;
   virtual bool HasWebContents() const = 0;
+  virtual content::WebContents* GetWebContents() const = 0;
 
   // Session sync related methods.
   virtual int GetSyncId() const = 0;
diff --git a/chrome/browser/sync/glue/synced_tab_delegate_android.cc b/chrome/browser/sync/glue/synced_tab_delegate_android.cc
index 300b90a..8585537 100644
--- a/chrome/browser/sync/glue/synced_tab_delegate_android.cc
+++ b/chrome/browser/sync/glue/synced_tab_delegate_android.cc
@@ -30,7 +30,7 @@
 }
 
 SessionID::id_type SyncedTabDelegateAndroid::GetSessionId() const {
-  return tab_android_->id().id();
+  return tab_android_->session_id().id();
 }
 
 bool SyncedTabDelegateAndroid::IsBeingDestroyed() const {
@@ -87,6 +87,10 @@
   return web_contents_ != NULL;
 }
 
+content::WebContents* SyncedTabDelegateAndroid::GetWebContents() const {
+  return web_contents_;
+}
+
 void SyncedTabDelegateAndroid::SetWebContents(
     content::WebContents* web_contents) {
   web_contents_ = web_contents;
diff --git a/chrome/browser/sync/glue/synced_tab_delegate_android.h b/chrome/browser/sync/glue/synced_tab_delegate_android.h
index 3550277..90d239a 100644
--- a/chrome/browser/sync/glue/synced_tab_delegate_android.h
+++ b/chrome/browser/sync/glue/synced_tab_delegate_android.h
@@ -38,6 +38,7 @@
   virtual content::NavigationEntry* GetActiveEntry() const OVERRIDE;
   virtual bool IsPinned() const OVERRIDE;
   virtual bool HasWebContents() const OVERRIDE;
+  virtual content::WebContents* GetWebContents() const OVERRIDE;
   virtual int GetSyncId() const OVERRIDE;
   virtual void SetSyncId(int sync_id) OVERRIDE;
 
diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc
index 4c0426b..004fcba 100644
--- a/chrome/browser/sync/profile_sync_service.cc
+++ b/chrome/browser/sync/profile_sync_service.cc
@@ -422,6 +422,30 @@
   return devices.Pass();
 }
 
+// Notifies the observer of any device info changes.
+void ProfileSyncService::AddObserverForDeviceInfoChange(
+    browser_sync::SyncedDeviceTracker::Observer* observer) {
+  if (backend_) {
+    browser_sync::SyncedDeviceTracker* device_tracker =
+        backend_->GetSyncedDeviceTracker();
+    if (device_tracker) {
+      device_tracker->AddObserver(observer);
+    }
+  }
+}
+
+// Removes the observer from device info change notification.
+void ProfileSyncService::RemoveObserverForDeviceInfoChange(
+    browser_sync::SyncedDeviceTracker::Observer* observer) {
+  if (backend_) {
+    browser_sync::SyncedDeviceTracker* device_tracker =
+        backend_->GetSyncedDeviceTracker();
+    if (device_tracker) {
+      device_tracker->RemoveObserver(observer);
+    }
+  }
+}
+
 void ProfileSyncService::GetDataTypeControllerStates(
   browser_sync::DataTypeController::StateMap* state_map) const {
     for (browser_sync::DataTypeController::TypeMap::const_iterator iter =
diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h
index f0bf397..c7a19a9 100644
--- a/chrome/browser/sync/profile_sync_service.h
+++ b/chrome/browser/sync/profile_sync_service.h
@@ -20,7 +20,6 @@
 #include "base/strings/string16.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "chrome/browser/signin/signin_global_error.h"
 #include "chrome/browser/sync/backend_unrecoverable_error_handler.h"
 #include "chrome/browser/sync/glue/data_type_controller.h"
@@ -29,6 +28,7 @@
 #include "chrome/browser/sync/glue/data_type_manager_observer.h"
 #include "chrome/browser/sync/glue/failed_data_types_handler.h"
 #include "chrome/browser/sync/glue/sync_backend_host.h"
+#include "chrome/browser/sync/glue/synced_device_tracker.h"
 #include "chrome/browser/sync/profile_sync_service_base.h"
 #include "chrome/browser/sync/profile_sync_service_observer.h"
 #include "chrome/browser/sync/sync_prefs.h"
@@ -37,6 +37,7 @@
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/notification_types.h"
 #include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 #include "net/base/backoff_entry.h"
 #include "sync/internal_api/public/base/model_type.h"
 #include "sync/internal_api/public/engine/model_safe_worker.h"
@@ -54,8 +55,8 @@
 namespace browser_sync {
 class BackendMigrator;
 class ChangeProcessor;
-class DeviceInfo;
 class DataTypeManager;
+class DeviceInfo;
 class JsController;
 class SessionModelAssociator;
 
@@ -285,6 +286,14 @@
   // with this profile.
   virtual ScopedVector<browser_sync::DeviceInfo> GetAllSignedinDevices() const;
 
+  // Notifies the observer of any device info changes.
+  virtual void AddObserverForDeviceInfoChange(
+      browser_sync::SyncedDeviceTracker::Observer* observer);
+
+  // Removes the observer from device info notification.
+  virtual void RemoveObserverForDeviceInfoChange(
+      browser_sync::SyncedDeviceTracker::Observer* observer);
+
   // Fills state_map with a map of current data types that are possible to
   // sync, as well as their states.
   void GetDataTypeControllerStates(
diff --git a/chrome/browser/sync/profile_sync_service_android.cc b/chrome/browser/sync/profile_sync_service_android.cc
index 8abc53b..b391e89 100644
--- a/chrome/browser/sync/profile_sync_service_android.cc
+++ b/chrome/browser/sync/profile_sync_service_android.cc
@@ -17,7 +17,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "chrome/browser/signin/signin_manager.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/signin/token_service.h"
@@ -33,6 +32,7 @@
 #include "google/cacheinvalidation/types.pb.h"
 #include "google_apis/gaia/gaia_constants.h"
 #include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 #include "grit/generated_resources.h"
 #include "jni/ProfileSyncService_jni.h"
 #include "sync/internal_api/public/base/model_type_invalidation_map.h"
diff --git a/chrome/browser/sync/profile_sync_service_startup_unittest.cc b/chrome/browser/sync/profile_sync_service_startup_unittest.cc
index a1bdf31..c500634 100644
--- a/chrome/browser/sync/profile_sync_service_startup_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_startup_unittest.cc
@@ -10,7 +10,6 @@
 #include "base/run_loop.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/signin/fake_signin_manager.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "chrome/browser/signin/profile_oauth2_token_service.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/signin_manager.h"
@@ -32,6 +31,7 @@
 #include "content/public/test/test_utils.h"
 #include "google_apis/gaia/gaia_auth_consumer.h"
 #include "google_apis/gaia/gaia_constants.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 using browser_sync::DataTypeManager;
diff --git a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
index 607b022..2028de6 100644
--- a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
@@ -95,3 +95,31 @@
   }
   ASSERT_EQ(1, found_navigations);
 }
+
+IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, ResponseCodeIsPreserved) {
+  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+
+  ASSERT_TRUE(CheckInitialState(0));
+
+  // We want a URL that doesn't 404 and has a non-empty title.
+  // about:version is simple to render, too.
+  const GURL url("about:version");
+
+  ScopedWindowMap windows;
+  ASSERT_TRUE(OpenTabAndGetLocalWindows(0, url, windows.GetMutable()));
+
+  int found_navigations = 0;
+  for (SessionWindowMap::const_iterator it = windows.Get()->begin();
+       it != windows.Get()->end(); ++it) {
+    for (std::vector<SessionTab*>::const_iterator it2 =
+             it->second->tabs.begin(); it2 != it->second->tabs.end(); ++it2) {
+      for (std::vector<sessions::SerializedNavigationEntry>::const_iterator
+               it3 = (*it2)->navigations.begin();
+           it3 != (*it2)->navigations.end(); ++it3) {
+        EXPECT_EQ(200, it3->http_status_code());
+        ++found_navigations;
+      }
+    }
+  }
+  ASSERT_EQ(1, found_navigations);
+}
diff --git a/chrome/browser/sync/test_profile_sync_service.h b/chrome/browser/sync/test_profile_sync_service.h
index 0034429..3eb6317 100644
--- a/chrome/browser/sync/test_profile_sync_service.h
+++ b/chrome/browser/sync/test_profile_sync_service.h
@@ -11,7 +11,6 @@
 #include "base/compiler_specific.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/invalidation/invalidator_storage.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "chrome/browser/signin/profile_oauth2_token_service.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/sync/glue/data_type_manager_impl.h"
@@ -19,6 +18,7 @@
 #include "chrome/browser/sync/profile_sync_service.h"
 #include "chrome/browser/sync/sync_prefs.h"
 #include "chrome/test/base/profile_mock.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 #include "sync/internal_api/public/test/test_internal_components_factory.h"
 #include "sync/test/engine/test_id_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/sync_file_system/drive_backend/api_util.cc b/chrome/browser/sync_file_system/drive_backend/api_util.cc
index f859d44..6d975e7 100644
--- a/chrome/browser/sync_file_system/drive_backend/api_util.cc
+++ b/chrome/browser/sync_file_system/drive_backend/api_util.cc
@@ -497,13 +497,13 @@
       base::Bind(&APIUtil::DidGetResourceList, AsWeakPtr(), callback));
 }
 
-void APIUtil::ContinueListing(const GURL& feed_url,
+void APIUtil::ContinueListing(const std::string& page_token,
                               const ResourceListCallback& callback) {
   DCHECK(CalledOnValidThread());
-  DVLOG(2) << "Continue listing on feed: " << feed_url;
+  DVLOG(2) << "Continue listing on feed: " << page_token;
 
-  drive_service_->ContinueGetResourceList(
-      feed_url,
+  drive_service_->GetRemainingFileList(
+      page_token,
       base::Bind(&APIUtil::DidGetResourceList, AsWeakPtr(), callback));
 }
 
@@ -613,7 +613,7 @@
 GURL APIUtil::ResourceIdToResourceLink(const std::string& resource_id) const {
   return IsDriveAPIDisabled()
       ? wapi_url_generator_.GenerateEditUrl(resource_id)
-      : drive_api_url_generator_.GetFileUrl(resource_id);
+      : drive_api_url_generator_.GetFilesGetUrl(resource_id);
 }
 
 void APIUtil::EnsureSyncRootIsNotInMyDrive(
@@ -718,6 +718,12 @@
     return;
   }
 
+  if (entry->deleted()) {
+    DVLOG(2) << "Got resource entry, the entry was trashed.";
+    callback.Run(google_apis::HTTP_NOT_FOUND, entry.Pass());
+    return;
+  }
+
   DVLOG(2) << "Got resource entry";
   DCHECK(entry);
   callback.Run(error, entry.Pass());
diff --git a/chrome/browser/sync_file_system/drive_backend/api_util.h b/chrome/browser/sync_file_system/drive_backend/api_util.h
index 5cb7abe..f3cc229 100644
--- a/chrome/browser/sync_file_system/drive_backend/api_util.h
+++ b/chrome/browser/sync_file_system/drive_backend/api_util.h
@@ -71,7 +71,7 @@
                          const ResourceListCallback& callback) OVERRIDE;
   virtual void ListChanges(int64 start_changestamp,
                            const ResourceListCallback& callback) OVERRIDE;
-  virtual void ContinueListing(const GURL& feed_url,
+  virtual void ContinueListing(const std::string& page_token,
                                const ResourceListCallback& callback) OVERRIDE;
   virtual void DownloadFile(const std::string& resource_id,
                             const std::string& local_file_md5,
diff --git a/chrome/browser/sync_file_system/drive_backend/api_util_interface.h b/chrome/browser/sync_file_system/drive_backend/api_util_interface.h
index e4a2ca7..68eccd3 100644
--- a/chrome/browser/sync_file_system/drive_backend/api_util_interface.h
+++ b/chrome/browser/sync_file_system/drive_backend/api_util_interface.h
@@ -121,9 +121,9 @@
   virtual void ListChanges(int64 start_changestamp,
                            const ResourceListCallback& callback) = 0;
 
-  // Fetches the next chunk of ResourceList identified by |feed_url|.
+  // Fetches the next chunk of ResourceList identified by |page_token|.
   // Upon completion, invokes |callback|.
-  virtual void ContinueListing(const GURL& feed_url,
+  virtual void ContinueListing(const std::string& page_token,
                                const ResourceListCallback& callback) = 0;
 
   // Downloads the file identified by |resource_id| from Drive to
diff --git a/chrome/browser/sync_file_system/drive_backend/api_util_unittest.cc b/chrome/browser/sync_file_system/drive_backend/api_util_unittest.cc
index 951ee05..e67309f 100644
--- a/chrome/browser/sync_file_system/drive_backend/api_util_unittest.cc
+++ b/chrome/browser/sync_file_system/drive_backend/api_util_unittest.cc
@@ -527,6 +527,7 @@
   EXPECT_EQ(google_apis::HTTP_SUCCESS, error);
   EXPECT_EQ(3U, document_feed->entries().size());
 
+  // TODO(hidehiko): Use page token instead of feed url.
   GURL feed_url;
   ASSERT_TRUE(document_feed->GetNextFeedURL(&feed_url));
 
@@ -534,7 +535,8 @@
   document_feed.reset();
 
   api_util()->ContinueListing(
-      feed_url, base::Bind(&DidGetResourceList, &error, &document_feed));
+      feed_url.spec(),
+      base::Bind(&DidGetResourceList, &error, &document_feed));
   base::MessageLoop::current()->RunUntilIdle();
 
   EXPECT_EQ(google_apis::HTTP_SUCCESS, error);
diff --git a/chrome/browser/sync_file_system/drive_backend/drive_file_sync_service.cc b/chrome/browser/sync_file_system/drive_backend/drive_file_sync_service.cc
index d2bdf15..bcb8aed 100644
--- a/chrome/browser/sync_file_system/drive_backend/drive_file_sync_service.cc
+++ b/chrome/browser/sync_file_system/drive_backend/drive_file_sync_service.cc
@@ -893,10 +893,11 @@
     AppendFetchChange(origin, path, entry.resource_id(), file_type);
   }
 
+  // TODO(hidehiko): Use page token instead of next feed url.
   GURL next_feed_url;
   if (feed->GetNextFeedURL(&next_feed_url)) {
     api_util_->ContinueListing(
-        next_feed_url,
+        next_feed_url.spec(),
         base::Bind(&DriveFileSyncService::DidGetDirectoryContentForBatchSync,
                    AsWeakPtr(),
                    callback,
diff --git a/chrome/browser/sync_file_system/drive_backend/fake_api_util.cc b/chrome/browser/sync_file_system/drive_backend/fake_api_util.cc
index 88ce414..3c33171 100644
--- a/chrome/browser/sync_file_system/drive_backend/fake_api_util.cc
+++ b/chrome/browser/sync_file_system/drive_backend/fake_api_util.cc
@@ -139,7 +139,7 @@
           callback, google_apis::HTTP_SUCCESS, base::Passed(&change_feed)));
 }
 
-void FakeAPIUtil::ContinueListing(const GURL& feed_url,
+void FakeAPIUtil::ContinueListing(const std::string& page_token,
                                   const ResourceListCallback& callback) {
   NOTREACHED();
 }
diff --git a/chrome/browser/sync_file_system/drive_backend/fake_api_util.h b/chrome/browser/sync_file_system/drive_backend/fake_api_util.h
index 1ffc36f..d36dfb9 100644
--- a/chrome/browser/sync_file_system/drive_backend/fake_api_util.h
+++ b/chrome/browser/sync_file_system/drive_backend/fake_api_util.h
@@ -75,7 +75,7 @@
                          const ResourceListCallback& callback) OVERRIDE;
   virtual void ListChanges(int64 start_changestamp,
                            const ResourceListCallback& callback) OVERRIDE;
-  virtual void ContinueListing(const GURL& feed_url,
+  virtual void ContinueListing(const std::string& page_token,
                                const ResourceListCallback& callback) OVERRIDE;
   virtual void DownloadFile(const std::string& resource_id,
                             const std::string& local_file_md5,
diff --git a/chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.cc b/chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.cc
index b23ceb1..8e9ff62 100644
--- a/chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.cc
+++ b/chrome/browser/sync_file_system/drive_backend/fake_drive_service_helper.cc
@@ -273,14 +273,15 @@
       *itr = NULL;
     }
 
+    // TODO(hidehiko): Use page token instead of next feed.
     GURL next_feed;
     if (!list->GetNextFeedURL(&next_feed))
       return google_apis::HTTP_SUCCESS;
 
     GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
     list.reset();
-    fake_drive_service_->ContinueGetResourceList(
-        next_feed,
+    fake_drive_service_->GetRemainingFileList(
+        next_feed.spec(),
         base::Bind(&ResourceListResultCallback, &error, &list));
     FlushMessageLoop();
     if (error != google_apis::HTTP_SUCCESS)
diff --git a/chrome/browser/sync_file_system/drive_backend/local_sync_delegate.cc b/chrome/browser/sync_file_system/drive_backend/local_sync_delegate.cc
index e3d587b..e91b749 100644
--- a/chrome/browser/sync_file_system/drive_backend/local_sync_delegate.cc
+++ b/chrome/browser/sync_file_system/drive_backend/local_sync_delegate.cc
@@ -413,9 +413,13 @@
   metadata_store()->UpdateEntry(url_, drive_metadata_, callback);
 }
 
-void LocalSyncDelegate::ResetMetadataMD5(const SyncStatusCallback& callback) {
+void LocalSyncDelegate::ResetMetadataForStartOver(
+    const SyncStatusCallback& callback) {
   has_drive_metadata_ = true;
+  DCHECK(!drive_metadata_.resource_id().empty());
   drive_metadata_.set_md5_checksum(std::string());
+  drive_metadata_.set_conflicted(false);
+  drive_metadata_.set_to_be_fetched(false);
   metadata_store()->UpdateEntry(url_, drive_metadata_, callback);
 }
 
@@ -538,8 +542,8 @@
     return;
   }
 
-  ResetMetadataMD5(base::Bind(&LocalSyncDelegate::StartOver,
-                              weak_factory_.GetWeakPtr(), callback));
+  ResetMetadataForStartOver(base::Bind(&LocalSyncDelegate::StartOver,
+                                       weak_factory_.GetWeakPtr(), callback));
 }
 
 void LocalSyncDelegate::HandleRemoteWinCase(
diff --git a/chrome/browser/sync_file_system/drive_backend/local_sync_delegate.h b/chrome/browser/sync_file_system/drive_backend/local_sync_delegate.h
index 064d244..6d7aa03 100644
--- a/chrome/browser/sync_file_system/drive_backend/local_sync_delegate.h
+++ b/chrome/browser/sync_file_system/drive_backend/local_sync_delegate.h
@@ -86,7 +86,7 @@
                       const std::string& md5,
                       DriveMetadata::ResourceType type,
                       const SyncStatusCallback& callback);
-  void ResetMetadataMD5(const SyncStatusCallback& callback);
+  void ResetMetadataForStartOver(const SyncStatusCallback& callback);
   void SetMetadataToBeFetched(DriveMetadata::ResourceType type,
                               const SyncStatusCallback& callback);
   void DeleteMetadata(const SyncStatusCallback& callback);
diff --git a/chrome/browser/sync_file_system/drive_backend/metadata_database.cc b/chrome/browser/sync_file_system/drive_backend/metadata_database.cc
index 98df266..4e668c8 100644
--- a/chrome/browser/sync_file_system/drive_backend/metadata_database.cc
+++ b/chrome/browser/sync_file_system/drive_backend/metadata_database.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
 
+#include <algorithm>
 #include <stack>
 
 #include "base/bind.h"
diff --git a/chrome/browser/sync_file_system/drive_backend/metadata_database.h b/chrome/browser/sync_file_system/drive_backend/metadata_database.h
index 8b02892..fdb8341 100644
--- a/chrome/browser/sync_file_system/drive_backend/metadata_database.h
+++ b/chrome/browser/sync_file_system/drive_backend/metadata_database.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <set>
 #include <string>
+#include <vector>
 
 #include "base/callback_forward.h"
 #include "base/memory/scoped_ptr.h"
diff --git a/chrome/browser/sync_file_system/local/canned_syncable_file_system.cc b/chrome/browser/sync_file_system/local/canned_syncable_file_system.cc
index 15e3419..8bbb303 100644
--- a/chrome/browser/sync_file_system/local/canned_syncable_file_system.cc
+++ b/chrome/browser/sync_file_system/local/canned_syncable_file_system.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/sync_file_system/local/canned_syncable_file_system.h"
 
+#include <algorithm>
 #include <iterator>
 
 #include "base/bind.h"
@@ -601,7 +602,7 @@
   EXPECT_TRUE(is_filesystem_opened_);
   operation_runner()->CreateSnapshotFile(
       url,
-      base::Bind(&OnCreateSnapshotFileAndVerifyData,expected_data, callback));
+      base::Bind(&OnCreateSnapshotFileAndVerifyData, expected_data, callback));
 }
 
 void CannedSyncableFileSystem::DoGetMetadataAndPlatformPath(
diff --git a/chrome/browser/sync_file_system/local/local_file_change_tracker.cc b/chrome/browser/sync_file_system/local/local_file_change_tracker.cc
index d522b4d..7cc1653 100644
--- a/chrome/browser/sync_file_system/local/local_file_change_tracker.cc
+++ b/chrome/browser/sync_file_system/local/local_file_change_tracker.cc
@@ -218,7 +218,7 @@
     return status;
 
   FileSystemFileUtil* file_util =
-      file_system_context->GetFileUtil(fileapi::kFileSystemTypeSyncable);
+      file_system_context->sandbox_delegate()->sync_file_util();
   DCHECK(file_util);
   scoped_ptr<FileSystemOperationContext> context(
       new FileSystemOperationContext(file_system_context));
diff --git a/chrome/browser/sync_file_system/local/local_file_change_tracker_unittest.cc b/chrome/browser/sync_file_system/local/local_file_change_tracker_unittest.cc
index 869675c..23cd37d 100644
--- a/chrome/browser/sync_file_system/local/local_file_change_tracker_unittest.cc
+++ b/chrome/browser/sync_file_system/local/local_file_change_tracker_unittest.cc
@@ -102,8 +102,9 @@
 
   ScopedEnableSyncFSDirectoryOperation enable_directory_operation_;
   base::MessageLoop message_loop_;
-
   CannedSyncableFileSystem file_system_;
+
+ private:
   scoped_refptr<LocalFileSyncContext> sync_context_;
 
   DISALLOW_COPY_AND_ASSIGN(LocalFileChangeTrackerTest);
diff --git a/chrome/browser/sync_file_system/local/local_file_sync_context.cc b/chrome/browser/sync_file_system/local/local_file_sync_context.cc
index baff590..bfea72b 100644
--- a/chrome/browser/sync_file_system/local/local_file_sync_context.cc
+++ b/chrome/browser/sync_file_system/local/local_file_sync_context.cc
@@ -667,7 +667,8 @@
 
   base::FilePath platform_path;
   base::PlatformFileInfo file_info;
-  FileSystemFileUtil* file_util = file_system_context->GetFileUtil(url.type());
+  FileSystemFileUtil* file_util =
+      file_system_context->sandbox_delegate()->sync_file_util();
   DCHECK(file_util);
   base::PlatformFileError file_error = file_util->GetFileInfo(
       make_scoped_ptr(
diff --git a/chrome/browser/sync_file_system/local/local_file_sync_service_unittest.cc b/chrome/browser/sync_file_system/local/local_file_sync_service_unittest.cc
index ce97422..499ea67 100644
--- a/chrome/browser/sync_file_system/local/local_file_sync_service_unittest.cc
+++ b/chrome/browser/sync_file_system/local/local_file_sync_service_unittest.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 <vector>
+
 #include "base/basictypes.h"
 #include "base/bind.h"
 #include "base/file_util.h"
diff --git a/chrome/browser/sync_file_system/local/local_file_sync_status.h b/chrome/browser/sync_file_system/local/local_file_sync_status.h
index c6e0315..ed8a487 100644
--- a/chrome/browser/sync_file_system/local/local_file_sync_status.h
+++ b/chrome/browser/sync_file_system/local/local_file_sync_status.h
@@ -73,7 +73,7 @@
   void RemoveObserver(Observer* observer);
 
  private:
-  typedef std::map<fileapi::FileSystemURL,int64,
+  typedef std::map<fileapi::FileSystemURL, int64,
                    fileapi::FileSystemURL::Comparator> URLCountMap;
 
   bool IsChildOrParentWriting(const fileapi::FileSystemURL& url) const;
diff --git a/chrome/browser/sync_file_system/local/sync_file_system_backend.cc b/chrome/browser/sync_file_system/local/sync_file_system_backend.cc
index ac426c9..4796762 100644
--- a/chrome/browser/sync_file_system/local/sync_file_system_backend.cc
+++ b/chrome/browser/sync_file_system/local/sync_file_system_backend.cc
@@ -61,12 +61,6 @@
                             GetSyncableFileSystemRootURI(origin_url));
 }
 
-fileapi::FileSystemFileUtil* SyncFileSystemBackend::GetFileUtil(
-    fileapi::FileSystemType type) {
-  DCHECK(delegate_);
-  return delegate_->sync_file_util();
-}
-
 fileapi::AsyncFileUtil* SyncFileSystemBackend::GetAsyncFileUtil(
     fileapi::FileSystemType type) {
   DCHECK(delegate_);
diff --git a/chrome/browser/sync_file_system/local/sync_file_system_backend.h b/chrome/browser/sync_file_system/local/sync_file_system_backend.h
index fdba979..71322b2 100644
--- a/chrome/browser/sync_file_system/local/sync_file_system_backend.h
+++ b/chrome/browser/sync_file_system/local/sync_file_system_backend.h
@@ -28,8 +28,6 @@
       fileapi::FileSystemType type,
       fileapi::OpenFileSystemMode mode,
       const OpenFileSystemCallback& callback) OVERRIDE;
-  virtual fileapi::FileSystemFileUtil* GetFileUtil(
-      fileapi::FileSystemType type) OVERRIDE;
   virtual fileapi::AsyncFileUtil* GetAsyncFileUtil(
       fileapi::FileSystemType type) OVERRIDE;
   virtual fileapi::CopyOrMoveFileValidatorFactory*
diff --git a/chrome/browser/sync_file_system/local/syncable_file_operation_runner_unittest.cc b/chrome/browser/sync_file_system/local/syncable_file_operation_runner_unittest.cc
index 0fc3f07..524c82f 100644
--- a/chrome/browser/sync_file_system/local/syncable_file_operation_runner_unittest.cc
+++ b/chrome/browser/sync_file_system/local/syncable_file_operation_runner_unittest.cc
@@ -142,6 +142,7 @@
 
   MockBlobURLRequestContext url_request_context_;
 
+ private:
   base::WeakPtrFactory<SyncableFileOperationRunnerTest> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SyncableFileOperationRunnerTest);
diff --git a/chrome/browser/sync_file_system/local/syncable_file_system_operation.cc b/chrome/browser/sync_file_system/local/syncable_file_system_operation.cc
index 9d3e943..72d63d2 100644
--- a/chrome/browser/sync_file_system/local/syncable_file_system_operation.cc
+++ b/chrome/browser/sync_file_system/local/syncable_file_system_operation.cc
@@ -18,8 +18,6 @@
 #include "webkit/common/blob/shareable_file_reference.h"
 
 using fileapi::FileSystemURL;
-using fileapi::FileSystemOperationContext;
-using fileapi::FileSystemOperationImpl;
 
 namespace sync_file_system {
 
@@ -47,6 +45,8 @@
   }
 
   virtual void Run() OVERRIDE {
+    if (!operation_)
+      return;
     DCHECK(!task_.is_null());
     task_.Run();
     operation_.reset();
@@ -86,11 +86,11 @@
   target_paths_.push_back(url);
   completion_callback_ = callback;
   scoped_ptr<SyncableFileOperationRunner::Task> task(new QueueableTask(
-      AsWeakPtr(),
+      weak_factory_.GetWeakPtr(),
       base::Bind(&FileSystemOperation::CreateFile,
-                 NewOperation()->AsWeakPtr(),
+                 base::Unretained(impl_.get()),
                  url, exclusive,
-                 base::Bind(&self::DidFinish, AsWeakPtr()))));
+                 base::Bind(&self::DidFinish, weak_factory_.GetWeakPtr()))));
   operation_runner_->PostOperationTask(task.Pass());
 }
 
@@ -112,11 +112,11 @@
   target_paths_.push_back(url);
   completion_callback_ = callback;
   scoped_ptr<SyncableFileOperationRunner::Task> task(new QueueableTask(
-      AsWeakPtr(),
+      weak_factory_.GetWeakPtr(),
       base::Bind(&FileSystemOperation::CreateDirectory,
-                 NewOperation()->AsWeakPtr(),
+                 base::Unretained(impl_.get()),
                  url, exclusive, recursive,
-                 base::Bind(&self::DidFinish, AsWeakPtr()))));
+                 base::Bind(&self::DidFinish, weak_factory_.GetWeakPtr()))));
   operation_runner_->PostOperationTask(task.Pass());
 }
 
@@ -133,11 +133,11 @@
   target_paths_.push_back(dest_url);
   completion_callback_ = callback;
   scoped_ptr<SyncableFileOperationRunner::Task> task(new QueueableTask(
-      AsWeakPtr(),
+      weak_factory_.GetWeakPtr(),
       base::Bind(&FileSystemOperation::Copy,
-                 NewOperation()->AsWeakPtr(),
+                 base::Unretained(impl_.get()),
                  src_url, dest_url,
-                 base::Bind(&self::DidFinish, AsWeakPtr()))));
+                 base::Bind(&self::DidFinish, weak_factory_.GetWeakPtr()))));
   operation_runner_->PostOperationTask(task.Pass());
 }
 
@@ -155,11 +155,11 @@
   target_paths_.push_back(dest_url);
   completion_callback_ = callback;
   scoped_ptr<SyncableFileOperationRunner::Task> task(new QueueableTask(
-      AsWeakPtr(),
+      weak_factory_.GetWeakPtr(),
       base::Bind(&FileSystemOperation::Move,
-                 NewOperation()->AsWeakPtr(),
+                 base::Unretained(impl_.get()),
                  src_url, dest_url,
-                 base::Bind(&self::DidFinish, AsWeakPtr()))));
+                 base::Bind(&self::DidFinish, weak_factory_.GetWeakPtr()))));
   operation_runner_->PostOperationTask(task.Pass());
 }
 
@@ -167,21 +167,21 @@
     const FileSystemURL& url,
     const StatusCallback& callback) {
   DCHECK(CalledOnValidThread());
-  NewOperation()->DirectoryExists(url, callback);
+  impl_->DirectoryExists(url, callback);
 }
 
 void SyncableFileSystemOperation::FileExists(
     const FileSystemURL& url,
     const StatusCallback& callback) {
   DCHECK(CalledOnValidThread());
-  NewOperation()->FileExists(url, callback);
+  impl_->FileExists(url, callback);
 }
 
 void SyncableFileSystemOperation::GetMetadata(
     const FileSystemURL& url,
     const GetMetadataCallback& callback) {
   DCHECK(CalledOnValidThread());
-  NewOperation()->GetMetadata(url, callback);
+  impl_->GetMetadata(url, callback);
 }
 
 void SyncableFileSystemOperation::ReadDirectory(
@@ -191,7 +191,7 @@
   // This is a read operation and there'd be no hard to let it go even if
   // directory operation is disabled. (And we should allow this if it's made
   // on the root directory)
-  NewOperation()->ReadDirectory(url, callback);
+  impl_->ReadDirectory(url, callback);
 }
 
 void SyncableFileSystemOperation::Remove(
@@ -206,11 +206,11 @@
   target_paths_.push_back(url);
   completion_callback_ = callback;
   scoped_ptr<SyncableFileOperationRunner::Task> task(new QueueableTask(
-      AsWeakPtr(),
+      weak_factory_.GetWeakPtr(),
       base::Bind(&FileSystemOperation::Remove,
-                 NewOperation()->AsWeakPtr(),
+                 base::Unretained(impl_.get()),
                  url, recursive,
-                 base::Bind(&self::DidFinish, AsWeakPtr()))));
+                 base::Bind(&self::DidFinish, weak_factory_.GetWeakPtr()))));
   operation_runner_->PostOperationTask(task.Pass());
 }
 
@@ -228,13 +228,14 @@
   target_paths_.push_back(url);
   completion_callback_ = base::Bind(&WriteCallbackAdapter, callback);
   scoped_ptr<SyncableFileOperationRunner::Task> task(new QueueableTask(
-      AsWeakPtr(),
+      weak_factory_.GetWeakPtr(),
       base::Bind(&FileSystemOperation::Write,
-                 NewOperation()->AsWeakPtr(),
+                 base::Unretained(impl_.get()),
                  url,
                  base::Passed(&writer_delegate),
                  base::Passed(&blob_request),
-                 base::Bind(&self::DidWrite, AsWeakPtr(), callback))));
+                 base::Bind(&self::DidWrite, weak_factory_.GetWeakPtr(),
+                            callback))));
   operation_runner_->PostOperationTask(task.Pass());
 }
 
@@ -250,11 +251,11 @@
   target_paths_.push_back(url);
   completion_callback_ = callback;
   scoped_ptr<SyncableFileOperationRunner::Task> task(new QueueableTask(
-      AsWeakPtr(),
+      weak_factory_.GetWeakPtr(),
       base::Bind(&FileSystemOperation::Truncate,
-                 NewOperation()->AsWeakPtr(),
+                 base::Unretained(impl_.get()),
                  url, length,
-                 base::Bind(&self::DidFinish, AsWeakPtr()))));
+                 base::Bind(&self::DidFinish, weak_factory_.GetWeakPtr()))));
   operation_runner_->PostOperationTask(task.Pass());
 }
 
@@ -264,8 +265,7 @@
     const base::Time& last_modified_time,
     const StatusCallback& callback) {
   DCHECK(CalledOnValidThread());
-  NewOperation()->TouchFile(url, last_access_time,
-                            last_modified_time, callback);
+  impl_->TouchFile(url, last_access_time, last_modified_time, callback);
 }
 
 void SyncableFileSystemOperation::OpenFile(
@@ -279,15 +279,14 @@
 void SyncableFileSystemOperation::Cancel(
     const StatusCallback& cancel_callback) {
   DCHECK(CalledOnValidThread());
-  DCHECK(inflight_operation_);
-  inflight_operation_->Cancel(cancel_callback);
+  impl_->Cancel(cancel_callback);
 }
 
 void SyncableFileSystemOperation::CreateSnapshotFile(
     const FileSystemURL& path,
     const SnapshotFileCallback& callback) {
   DCHECK(CalledOnValidThread());
-  NewOperation()->CreateSnapshotFile(path, callback);
+  impl_->CreateSnapshotFile(path, callback);
 }
 
 void SyncableFileSystemOperation::CopyInForeignFile(
@@ -303,21 +302,56 @@
   target_paths_.push_back(dest_url);
   completion_callback_ = callback;
   scoped_ptr<SyncableFileOperationRunner::Task> task(new QueueableTask(
-      AsWeakPtr(),
-      base::Bind(&FileSystemOperationImpl::CopyInForeignFile,
-                 NewOperation()->AsWeakPtr(),
+      weak_factory_.GetWeakPtr(),
+      base::Bind(&FileSystemOperation::CopyInForeignFile,
+                 base::Unretained(impl_.get()),
                  src_local_disk_path, dest_url,
-                 base::Bind(&self::DidFinish, AsWeakPtr()))));
+                 base::Bind(&self::DidFinish, weak_factory_.GetWeakPtr()))));
   operation_runner_->PostOperationTask(task.Pass());
 }
 
+void SyncableFileSystemOperation::RemoveFile(
+    const FileSystemURL& url,
+    const StatusCallback& callback) {
+  DCHECK(CalledOnValidThread());
+  impl_->RemoveFile(url, callback);
+}
+
+void SyncableFileSystemOperation::RemoveDirectory(
+    const FileSystemURL& url,
+    const StatusCallback& callback) {
+  DCHECK(CalledOnValidThread());
+  impl_->RemoveDirectory(url, callback);
+}
+
+void SyncableFileSystemOperation::CopyFileLocal(
+    const FileSystemURL& src_url,
+    const FileSystemURL& dest_url,
+    const StatusCallback& callback) {
+  DCHECK(CalledOnValidThread());
+  impl_->CopyFileLocal(src_url, dest_url, callback);
+}
+
+void SyncableFileSystemOperation::MoveFileLocal(
+    const FileSystemURL& src_url,
+    const FileSystemURL& dest_url,
+    const StatusCallback& callback) {
+  DCHECK(CalledOnValidThread());
+  impl_->MoveFileLocal(src_url, dest_url, callback);
+}
+
+base::PlatformFileError SyncableFileSystemOperation::SyncGetPlatformPath(
+    const FileSystemURL& url,
+    base::FilePath* platform_path) {
+  return impl_->SyncGetPlatformPath(url, platform_path);
+}
+
 SyncableFileSystemOperation::SyncableFileSystemOperation(
     const FileSystemURL& url,
     fileapi::FileSystemContext* file_system_context,
-    scoped_ptr<FileSystemOperationContext> operation_context)
-    : FileSystemOperationImpl(url, file_system_context,
-                              operation_context.Pass()),
-      url_(url) {
+    scoped_ptr<fileapi::FileSystemOperationContext> operation_context)
+    : url_(url),
+      weak_factory_(this) {
   DCHECK(file_system_context);
   SyncFileSystemBackend* backend =
       SyncFileSystemBackend::GetBackend(file_system_context);
@@ -328,18 +362,12 @@
     // Returning here to leave operation_runner_ as NULL.
     return;
   }
+  impl_.reset(new fileapi::FileSystemOperationImpl(url_, file_system_context,
+                                                   operation_context.Pass()));
   operation_runner_ = backend->sync_context()->operation_runner();
   is_directory_operation_enabled_ = IsSyncFSDirectoryOperationEnabled();
 }
 
-FileSystemOperationImpl* SyncableFileSystemOperation::NewOperation() {
-  DCHECK(operation_context_);
-  inflight_operation_.reset(new FileSystemOperationImpl(
-      url_, file_system_context(), operation_context_.Pass()));
-  DCHECK(inflight_operation_);
-  return inflight_operation_.get();
-}
-
 void SyncableFileSystemOperation::DidFinish(base::PlatformFileError status) {
   DCHECK(CalledOnValidThread());
   DCHECK(!completion_callback_.is_null());
diff --git a/chrome/browser/sync_file_system/local/syncable_file_system_operation.h b/chrome/browser/sync_file_system/local/syncable_file_system_operation.h
index 211e533..f0c8afd 100644
--- a/chrome/browser/sync_file_system/local/syncable_file_system_operation.h
+++ b/chrome/browser/sync_file_system/local/syncable_file_system_operation.h
@@ -12,7 +12,8 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/non_thread_safe.h"
-#include "webkit/browser/fileapi/file_system_operation_impl.h"
+#include "webkit/browser/fileapi/file_system_operation.h"
+#include "webkit/browser/fileapi/file_system_url.h"
 
 namespace fileapi {
 class FileSystemContext;
@@ -24,10 +25,9 @@
 
 class SyncableFileOperationRunner;
 
-// A wrapper class of FileSystemOperationImpl for syncable file system.
+// A wrapper class of FileSystemOperation for syncable file system.
 class SyncableFileSystemOperation
-    : public fileapi::FileSystemOperationImpl,
-      public base::SupportsWeakPtr<SyncableFileSystemOperation>,
+    : public NON_EXPORTED_BASE(fileapi::FileSystemOperation),
       public base::NonThreadSafe {
  public:
   virtual ~SyncableFileSystemOperation();
@@ -74,13 +74,22 @@
   virtual void CreateSnapshotFile(
       const fileapi::FileSystemURL& path,
       const SnapshotFileCallback& callback) OVERRIDE;
-
-  // FileSystemOperationImpl overrides.
   virtual void CopyInForeignFile(const base::FilePath& src_local_disk_path,
                                  const fileapi::FileSystemURL& dest_url,
                                  const StatusCallback& callback) OVERRIDE;
-
-  using base::SupportsWeakPtr<SyncableFileSystemOperation>::AsWeakPtr;
+  virtual void RemoveFile(const fileapi::FileSystemURL& url,
+                          const StatusCallback& callback) OVERRIDE;
+  virtual void RemoveDirectory(const fileapi::FileSystemURL& url,
+                               const StatusCallback& callback) OVERRIDE;
+  virtual void CopyFileLocal(const fileapi::FileSystemURL& src_url,
+                             const fileapi::FileSystemURL& dest_url,
+                             const StatusCallback& callback) OVERRIDE;
+  virtual void MoveFileLocal(const fileapi::FileSystemURL& src_url,
+                             const fileapi::FileSystemURL& dest_url,
+                             const StatusCallback& callback) OVERRIDE;
+  virtual base::PlatformFileError SyncGetPlatformPath(
+      const fileapi::FileSystemURL& url,
+      base::FilePath* platform_path) OVERRIDE;
 
  private:
   typedef SyncableFileSystemOperation self;
@@ -93,7 +102,6 @@
       const fileapi::FileSystemURL& url,
       fileapi::FileSystemContext* file_system_context,
       scoped_ptr<fileapi::FileSystemOperationContext> operation_context);
-  fileapi::FileSystemOperationImpl* NewOperation();
 
   void DidFinish(base::PlatformFileError status);
   void DidWrite(const WriteCallback& callback,
@@ -105,14 +113,16 @@
 
   const fileapi::FileSystemURL url_;
 
+  scoped_ptr<fileapi::FileSystemOperation> impl_;
   base::WeakPtr<SyncableFileOperationRunner> operation_runner_;
-  scoped_ptr<fileapi::FileSystemOperationImpl> inflight_operation_;
   std::vector<fileapi::FileSystemURL> target_paths_;
 
   StatusCallback completion_callback_;
 
   bool is_directory_operation_enabled_;
 
+  base::WeakPtrFactory<SyncableFileSystemOperation> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(SyncableFileSystemOperation);
 };
 
diff --git a/chrome/browser/sync_file_system/local/syncable_file_system_unittest.cc b/chrome/browser/sync_file_system/local/syncable_file_system_unittest.cc
index f889cc5..3d8e94b 100644
--- a/chrome/browser/sync_file_system/local/syncable_file_system_unittest.cc
+++ b/chrome/browser/sync_file_system/local/syncable_file_system_unittest.cc
@@ -94,8 +94,9 @@
 
   base::ScopedTempDir data_dir_;
   base::MessageLoop message_loop_;
-
   CannedSyncableFileSystem file_system_;
+
+ private:
   scoped_refptr<LocalFileSyncContext> sync_context_;
 
   base::WeakPtrFactory<SyncableFileSystemTest> weak_factory_;
diff --git a/chrome/browser/sync_file_system/logger_unittest.cc b/chrome/browser/sync_file_system/logger_unittest.cc
index 157828c..085ee1a 100644
--- a/chrome/browser/sync_file_system/logger_unittest.cc
+++ b/chrome/browser/sync_file_system/logger_unittest.cc
@@ -33,7 +33,7 @@
     util::ClearLog();
   }
 
- protected:
+ private:
   DISALLOW_COPY_AND_ASSIGN(LoggerTest);
 };
 
diff --git a/chrome/browser/sync_file_system/mock_remote_file_sync_service.cc b/chrome/browser/sync_file_system/mock_remote_file_sync_service.cc
index a537454..e708ad9 100644
--- a/chrome/browser/sync_file_system/mock_remote_file_sync_service.cc
+++ b/chrome/browser/sync_file_system/mock_remote_file_sync_service.cc
@@ -19,7 +19,8 @@
 namespace sync_file_system {
 
 MockRemoteFileSyncService::MockRemoteFileSyncService()
-    : conflict_resolution_policy_(CONFLICT_RESOLUTION_POLICY_MANUAL) {
+    : conflict_resolution_policy_(CONFLICT_RESOLUTION_POLICY_MANUAL),
+      state_(REMOTE_SERVICE_OK) {
   typedef MockRemoteFileSyncService self;
   ON_CALL(*this, AddServiceObserver(_))
       .WillByDefault(Invoke(this, &self::AddServiceObserverStub));
@@ -40,7 +41,7 @@
   ON_CALL(*this, IsConflicting(_))
       .WillByDefault(Return(false));
   ON_CALL(*this, GetCurrentState())
-      .WillByDefault(Return(REMOTE_SERVICE_OK));
+      .WillByDefault(Invoke(this, &self::GetCurrentStateStub));
   ON_CALL(*this, SetConflictResolutionPolicy(_))
       .WillByDefault(Invoke(this, &self::SetConflictResolutionPolicyStub));
   ON_CALL(*this, GetConflictResolutionPolicy())
@@ -55,6 +56,10 @@
   return scoped_ptr<base::ListValue>();
 }
 
+void MockRemoteFileSyncService::SetServiceState(RemoteServiceState state) {
+  state_ = state;
+}
+
 void MockRemoteFileSyncService::NotifyRemoteChangeQueueUpdated(
     int64 pending_changes) {
   FOR_EACH_OBSERVER(Observer, service_observers_,
@@ -130,4 +135,8 @@
   return conflict_resolution_policy_;
 }
 
+RemoteServiceState MockRemoteFileSyncService::GetCurrentStateStub() const {
+  return state_;
+}
+
 }  // namespace sync_file_system
diff --git a/chrome/browser/sync_file_system/mock_remote_file_sync_service.h b/chrome/browser/sync_file_system/mock_remote_file_sync_service.h
index 3f24cd0..9f9cc15 100644
--- a/chrome/browser/sync_file_system/mock_remote_file_sync_service.h
+++ b/chrome/browser/sync_file_system/mock_remote_file_sync_service.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_SYNC_FILE_SYSTEM_MOCK_REMOTE_FILE_SYNC_SERVICE_H_
 #define CHROME_BROWSER_SYNC_FILE_SYSTEM_MOCK_REMOTE_FILE_SYNC_SERVICE_H_
 
-#include <map>
+#include <string>
 
 #include "base/memory/scoped_ptr.h"
 #include "base/observer_list.h"
@@ -52,7 +52,7 @@
                      RemoteServiceState());
   MOCK_METHOD1(GetOriginStatusMap,
                void(RemoteFileSyncService::OriginStatusMap* status_map));
-  MOCK_METHOD1(SetSyncEnabled, void(bool));
+  MOCK_METHOD1(SetSyncEnabled, void(bool enabled));
   MOCK_METHOD1(SetConflictResolutionPolicy,
                SyncStatusCode(ConflictResolutionPolicy));
   MOCK_CONST_METHOD0(GetConflictResolutionPolicy,
@@ -67,6 +67,8 @@
 
   virtual scoped_ptr<base::ListValue> DumpFiles(const GURL& origin) OVERRIDE;
 
+  void SetServiceState(RemoteServiceState state);
+
   // Send notifications to the observers.
   // Can be used in the mock implementation.
   void NotifyRemoteChangeQueueUpdated(int64 pending_changes);
@@ -92,6 +94,7 @@
   SyncStatusCode SetConflictResolutionPolicyStub(
       ConflictResolutionPolicy policy);
   ConflictResolutionPolicy GetConflictResolutionPolicyStub() const;
+  RemoteServiceState GetCurrentStateStub() const;
 
   // For default implementation.
   ::testing::NiceMock<MockLocalChangeProcessor> mock_local_change_processor_;
@@ -101,6 +104,8 @@
 
   ConflictResolutionPolicy conflict_resolution_policy_;
 
+  RemoteServiceState state_;
+
   DISALLOW_COPY_AND_ASSIGN(MockRemoteFileSyncService);
 };
 
diff --git a/chrome/browser/sync_file_system/sync_file_status.h b/chrome/browser/sync_file_system/sync_file_status.h
index 55d55aa..b27646c 100644
--- a/chrome/browser/sync_file_system/sync_file_status.h
+++ b/chrome/browser/sync_file_system/sync_file_status.h
@@ -24,6 +24,6 @@
   SYNC_FILE_STATUS_CONFLICTING,
 };
 
-}  // namespace fileapi
+}  // namespace sync_file_system
 
 #endif  // CHROME_BROWSER_SYNC_FILE_SYSTEM_SYNC_FILE_STATUS_H_
diff --git a/chrome/browser/sync_file_system/sync_file_system_service.h b/chrome/browser/sync_file_system/sync_file_system_service.h
index 8ed5ae9..4553ade 100644
--- a/chrome/browser/sync_file_system/sync_file_system_service.h
+++ b/chrome/browser/sync_file_system/sync_file_system_service.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_SYNC_FILE_SYSTEM_SYNC_FILE_SYSTEM_SERVICE_H_
 #define CHROME_BROWSER_SYNC_FILE_SYSTEM_SYNC_FILE_SYSTEM_SERVICE_H_
 
-#include <set>
+#include <map>
 #include <string>
 
 #include "base/basictypes.h"
diff --git a/chrome/browser/sync_file_system/syncable_file_system_util.cc b/chrome/browser/sync_file_system/syncable_file_system_util.cc
index 28c9181..a6b2550 100644
--- a/chrome/browser/sync_file_system/syncable_file_system_util.cc
+++ b/chrome/browser/sync_file_system/syncable_file_system_util.cc
@@ -30,7 +30,7 @@
 
 bool is_directory_operation_enabled = false;
 
-}
+}  // namespace
 
 void RegisterSyncableFileSystem() {
   ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
diff --git a/chrome/browser/tab_contents/render_view_context_menu.cc b/chrome/browser/tab_contents/render_view_context_menu.cc
index 46118e2..ab3b312 100644
--- a/chrome/browser/tab_contents/render_view_context_menu.cc
+++ b/chrome/browser/tab_contents/render_view_context_menu.cc
@@ -865,7 +865,8 @@
   const TemplateURL* const default_provider =
       TemplateURLServiceFactory::GetForProfile(profile_)->
           GetDefaultSearchProvider();
-  if (default_provider && !default_provider->image_url().empty() &&
+  if (params_.has_image_contents && default_provider &&
+      !default_provider->image_url().empty() &&
       default_provider->image_url_ref().IsValid()) {
     menu_model_.AddItem(
         IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE,
@@ -1288,7 +1289,7 @@
           (params_.src_url.scheme() != chrome::kChromeUIScheme);
 
     case IDC_CONTENT_CONTEXT_COPYIMAGE:
-      return !params_.is_image_blocked;
+      return params_.has_image_contents;
 
     // Media control commands should all be disabled if the player is in an
     // error state.
diff --git a/chrome/browser/task_manager/tab_contents_resource_provider.cc b/chrome/browser/task_manager/tab_contents_resource_provider.cc
index 87659b1..831a140 100644
--- a/chrome/browser/task_manager/tab_contents_resource_provider.cc
+++ b/chrome/browser/task_manager/tab_contents_resource_provider.cc
@@ -6,6 +6,7 @@
 
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/devtools/devtools_window.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/favicon/favicon_tab_helper.h"
 #include "chrome/browser/prerender/prerender_manager.h"
@@ -148,6 +149,7 @@
 gfx::ImageSkia TabContentsResource::GetIcon() const {
   if (IsContentsPrerendering(web_contents_))
     return *prerender_icon_;
+  FaviconTabHelper::CreateForWebContents(web_contents_);
   return FaviconTabHelper::FromWebContents(web_contents_)->
       GetFavicon().AsImageSkia();
 }
@@ -291,7 +293,8 @@
   if (!chrome::FindBrowserWithWebContents(web_contents) &&
       !IsContentsPrerendering(web_contents) &&
       !chrome::IsPreloadedInstantExtendedNTP(web_contents) &&
-      !IsContentsBackgroundPrinted(web_contents)) {
+      !IsContentsBackgroundPrinted(web_contents) &&
+      !DevToolsWindow::IsDevToolsWindow(web_contents->GetRenderViewHost())) {
     return;
   }
 
diff --git a/chrome/browser/task_manager/task_manager_browsertest.cc b/chrome/browser/task_manager/task_manager_browsertest.cc
index d3e3a52..6851bc4 100644
--- a/chrome/browser/task_manager/task_manager_browsertest.cc
+++ b/chrome/browser/task_manager/task_manager_browsertest.cc
@@ -9,6 +9,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/devtools/devtools_window.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_system.h"
@@ -496,4 +497,14 @@
   EXPECT_GE(result, minimal_heap_size);
 }
 
+IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeInTabDevToolsWindow) {
+  DevToolsWindow* dev_tools = DevToolsWindow::ToggleDevToolsWindow(
+      model()->GetResourceWebContents(1)->GetRenderViewHost(),
+      true,
+      DEVTOOLS_TOGGLE_ACTION_INSPECT);
+  // Dock side bottom should be the default.
+  ASSERT_EQ(DEVTOOLS_DOCK_SIDE_BOTTOM, dev_tools->dock_side());
+  TaskManagerBrowserTestUtil::WaitForWebResourceChange(2);
+}
+
 #endif
diff --git a/chrome/browser/task_manager/task_manager_util.cc b/chrome/browser/task_manager/task_manager_util.cc
index a79b639..27df180 100644
--- a/chrome/browser/task_manager/task_manager_util.cc
+++ b/chrome/browser/task_manager/task_manager_util.cc
@@ -41,6 +41,8 @@
     return IDS_TASK_MANAGER_PRERENDER_PREFIX;
   if (is_instant_overlay)
     return IDS_TASK_MANAGER_INSTANT_OVERLAY_PREFIX;
+  if (is_incognito)
+    return IDS_TASK_MANAGER_TAB_INCOGNITO_PREFIX;
 
   return IDS_TASK_MANAGER_TAB_PREFIX;
 }
diff --git a/chrome/browser/task_manager/task_manager_util_unittest.cc b/chrome/browser/task_manager/task_manager_util_unittest.cc
new file mode 100644
index 0000000..1949df5
--- /dev/null
+++ b/chrome/browser/task_manager/task_manager_util_unittest.cc
@@ -0,0 +1,49 @@
+// 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 "chrome/browser/task_manager/task_manager_util.h"
+
+#include "base/basictypes.h"
+#include "grit/generated_resources.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace task_manager {
+
+namespace util {
+
+TEST(TaskManagerUtilTest, GetMessagePrefixID) {
+  struct Configuration {
+    bool is_app;
+    bool is_extension;
+    bool is_incognito;
+    bool is_prerender;
+    bool is_instant_overlay;
+    bool is_background;
+    int expected_result;
+  };
+  const Configuration configs[] = {
+      // Use implicit int->bool conversion to save space and keep alignment.
+      {1, 0, 0, 0, 0, 1, IDS_TASK_MANAGER_BACKGROUND_PREFIX},
+      {1, 0, 1, 0, 0, 0, IDS_TASK_MANAGER_APP_INCOGNITO_PREFIX},
+      {1, 0, 0, 0, 0, 0, IDS_TASK_MANAGER_APP_PREFIX},
+      {0, 1, 1, 0, 0, 0, IDS_TASK_MANAGER_EXTENSION_INCOGNITO_PREFIX},
+      {0, 1, 0, 0, 0, 0, IDS_TASK_MANAGER_EXTENSION_PREFIX},
+      {0, 0, 0, 1, 0, 0, IDS_TASK_MANAGER_PRERENDER_PREFIX},
+      {0, 0, 0, 0, 1, 0, IDS_TASK_MANAGER_INSTANT_OVERLAY_PREFIX},
+      {0, 0, 1, 0, 0, 0, IDS_TASK_MANAGER_TAB_INCOGNITO_PREFIX},
+      {0, 0, 0, 0, 0, 0, IDS_TASK_MANAGER_TAB_PREFIX}};
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(configs); ++i) {
+    EXPECT_EQ(configs[i].expected_result,
+              GetMessagePrefixID(configs[i].is_app,
+                                 configs[i].is_extension,
+                                 configs[i].is_incognito,
+                                 configs[i].is_prerender,
+                                 configs[i].is_instant_overlay,
+                                 configs[i].is_background));
+  }
+}
+
+} //  namespace util
+
+} //  namespace task_manager
diff --git a/chrome/browser/themes/theme_properties.cc b/chrome/browser/themes/theme_properties.cc
index ec777b6..c08479d 100644
--- a/chrome/browser/themes/theme_properties.cc
+++ b/chrome/browser/themes/theme_properties.cc
@@ -71,10 +71,11 @@
 
 // Default display properties.
 const int kDefaultDisplayPropertyNTPAlignment =
-    ThemeProperties::ALIGN_BOTTOM;
+    ThemeProperties::ALIGN_CENTER;
 const int kDefaultDisplayPropertyNTPTiling =
     ThemeProperties::NO_REPEAT;
-const int kDefaultDisplayPropertyNTPInverseLogo = 0;
+// By default, we do not use the ntp alternate logo.
+const int kDefaultDisplayPropertyNTPAlternateLogo = 0;
 
 // ----------------------------------------------------------------------------
 // Defaults for properties which are not stored in the browser theme pack.
@@ -302,18 +303,15 @@
 }
 
 // static
-bool ThemeProperties::GetDefaultDisplayProperty(int id, int* result) {
+int ThemeProperties::GetDefaultDisplayProperty(int id) {
   switch (id) {
     case NTP_BACKGROUND_ALIGNMENT:
-      *result = kDefaultDisplayPropertyNTPAlignment;
-      return true;
+      return kDefaultDisplayPropertyNTPAlignment;
     case NTP_BACKGROUND_TILING:
-      *result = kDefaultDisplayPropertyNTPTiling;
-      return true;
+      return kDefaultDisplayPropertyNTPTiling;
     case NTP_LOGO_ALTERNATE:
-      *result = kDefaultDisplayPropertyNTPInverseLogo;
-      return true;
+      return kDefaultDisplayPropertyNTPAlternateLogo;
   }
 
-  return false;
+  return -1;
 }
diff --git a/chrome/browser/themes/theme_properties.h b/chrome/browser/themes/theme_properties.h
index 2bea265..0f3ee65 100644
--- a/chrome/browser/themes/theme_properties.h
+++ b/chrome/browser/themes/theme_properties.h
@@ -140,9 +140,9 @@
   // Returns SK_ColorRED if |id| is invalid.
   static SkColor GetDefaultColor(int id);
 
-  // Returns true and sets |result| to the requested default property, if |id|
-  // is valid.
-  static bool GetDefaultDisplayProperty(int id, int* result);
+  // Returns the default value for the given property |id|. Returns -1 if |id|
+  // is invalid.
+  static int GetDefaultDisplayProperty(int id);
 
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(ThemeProperties);
diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc
index 056b9dc..f51a4a6 100644
--- a/chrome/browser/themes/theme_service.cc
+++ b/chrome/browser/themes/theme_service.cc
@@ -169,11 +169,22 @@
   return Properties::GetDefaultColor(id);
 }
 
-bool ThemeService::GetDisplayProperty(int id, int* result) const {
-  if (theme_supplier_.get())
-    return theme_supplier_->GetDisplayProperty(id, result);
+int ThemeService::GetDisplayProperty(int id) const {
+  int result = 0;
+  if (theme_supplier_.get() &&
+      theme_supplier_->GetDisplayProperty(id, &result)) {
+    return result;
+  }
 
-  return Properties::GetDefaultDisplayProperty(id, result);
+  if (id == Properties::NTP_LOGO_ALTERNATE &&
+      !UsingDefaultTheme() &&
+      !UsingNativeTheme()) {
+    // Use the alternate logo for themes from the web store except for
+    // |kDefaultThemeGalleryID|.
+    return 1;
+  }
+
+  return Properties::GetDefaultDisplayProperty(id);
 }
 
 bool ThemeService::ShouldUseNativeFrame() const {
@@ -200,8 +211,7 @@
     int id,
     ui::ScaleFactor scale_factor) const {
   // Check to see whether we should substitute some images.
-  int ntp_alternate;
-  GetDisplayProperty(Properties::NTP_LOGO_ALTERNATE, &ntp_alternate);
+  int ntp_alternate = GetDisplayProperty(Properties::NTP_LOGO_ALTERNATE);
   if (id == IDR_PRODUCT_LOGO && ntp_alternate != 0)
     id = IDR_PRODUCT_LOGO_WHITE;
 
@@ -364,7 +374,7 @@
 bool ThemeService::UsingDefaultTheme() const {
   std::string id = GetThemeID();
   return id == ThemeService::kDefaultThemeID ||
-      (id == kDefaultThemeGalleryID && !IsManagedUser());
+      id == kDefaultThemeGalleryID;
 }
 
 bool ThemeService::UsingNativeTheme() const {
diff --git a/chrome/browser/themes/theme_service.h b/chrome/browser/themes/theme_service.h
index 22abfde..73db1d4 100644
--- a/chrome/browser/themes/theme_service.h
+++ b/chrome/browser/themes/theme_service.h
@@ -78,7 +78,7 @@
   // Overridden from ui::ThemeProvider:
   virtual gfx::ImageSkia* GetImageSkiaNamed(int id) const OVERRIDE;
   virtual SkColor GetColor(int id) const OVERRIDE;
-  virtual bool GetDisplayProperty(int id, int* result) const OVERRIDE;
+  virtual int GetDisplayProperty(int id) const OVERRIDE;
   virtual bool ShouldUseNativeFrame() const OVERRIDE;
   virtual bool HasCustomImage(int id) const OVERRIDE;
   virtual base::RefCountedMemory* GetRawData(
diff --git a/chrome/browser/translate/translate_accept_languages.cc b/chrome/browser/translate/translate_accept_languages.cc
index 5a098be..d056447 100644
--- a/chrome/browser/translate/translate_accept_languages.cc
+++ b/chrome/browser/translate/translate_accept_languages.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/translate/translate_accept_languages.h"
 
 #include "base/bind.h"
-#include "base/command_line.h"
 #include "base/prefs/pref_change_registrar.h"
 #include "base/prefs/pref_service.h"
 #include "base/strings/string_split.h"
@@ -14,7 +13,6 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/translate/translate_manager.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/translate/translate_util.h"
 #include "content/public/browser/notification_source.h"
@@ -88,13 +86,6 @@
   LanguageSet accept_langs_set;
   base::SplitString(accept_langs_str, ',', &accept_langs_list);
   std::vector<std::string>::const_iterator iter;
-  std::string app_locale = g_browser_process->GetApplicationLocale();
-  std::string ui_lang = TranslateManager::GetLanguageCode(app_locale);
-  bool is_ui_english = StartsWithASCII(ui_lang, "en-", false);
-
-  CommandLine* command_line = CommandLine::ForCurrentProcess();
-  bool enable_translate_settings =
-      command_line->HasSwitch(switches::kEnableTranslateSettings);
 
   for (iter = accept_langs_list.begin();
        iter != accept_langs_list.end(); ++iter) {
@@ -105,18 +96,6 @@
     if (index != std::string::npos && *iter != "zh-CN" && *iter != "zh-TW")
       accept_lang = iter->substr(0, index);
 
-    // Special-case English until we resolve bug 36182 properly.
-    // Add English only if the UI language is not English. This will annoy
-    // users of non-English Chrome who can comprehend English until English is
-    // black-listed.
-    // TODO(jungshik): Once we determine that it's safe to remove English from
-    // the default Accept-Language values for most locales, remove this
-    // special-casing.
-    // TODO(hajimehoshi): We can remove this special-casing if the Translate
-    // settings UI is enabled by default.
-    if (!enable_translate_settings && accept_lang == "en" && !is_ui_english)
-      continue;
-
     accept_langs_set.insert(accept_lang);
   }
   accept_languages_[prefs] = accept_langs_set;
diff --git a/chrome/browser/translate/translate_manager.cc b/chrome/browser/translate/translate_manager.cc
index 9188acf..41432a5 100644
--- a/chrome/browser/translate/translate_manager.cc
+++ b/chrome/browser/translate/translate_manager.cc
@@ -352,8 +352,6 @@
   std::string target_lang = GetTargetLanguage(prefs);
   std::string language_code = GetLanguageCode(page_lang);
 
-  CommandLine* command_line = CommandLine::ForCurrentProcess();
-
   // Don't translate similar languages (ex: en-US to en).
   if (language_code == target_lang) {
     TranslateBrowserMetrics::ReportInitiationStatus(
@@ -361,17 +359,6 @@
     return;
   }
 
-  // Don't translate any language the user configured as accepted languages.
-  // When the flag --enable-translate-settings is on, the condition is
-  // different. In this case, even though a language is an Accept language,
-  // it could be translated due to the blacklist.
-  if (!command_line->HasSwitch(switches::kEnableTranslateSettings) &&
-      accept_languages_->IsAcceptLanguage(original_profile, language_code)) {
-    TranslateBrowserMetrics::ReportInitiationStatus(
-        TranslateBrowserMetrics::INITIATION_STATUS_ACCEPT_LANGUAGES);
-    return;
-  }
-
   // Nothing to do if either the language Chrome is in or the language of the
   // page is not supported by the translation server.
   if (target_lang.empty() || !IsSupportedLanguage(language_code)) {
diff --git a/chrome/browser/translate/translate_manager_browsertest.cc b/chrome/browser/translate/translate_manager_browsertest.cc
index f10bb02..60e42e6 100644
--- a/chrome/browser/translate/translate_manager_browsertest.cc
+++ b/chrome/browser/translate/translate_manager_browsertest.cc
@@ -334,7 +334,7 @@
     params.media_type = WebKit::WebContextMenuData::MediaTypeNone;
     params.x = 0;
     params.y = 0;
-    params.is_image_blocked = false;
+    params.has_image_contents = true;
     params.media_flags = 0;
     params.spellcheck_enabled = false;
     params.is_editable = false;
@@ -1028,12 +1028,12 @@
   PrefService* prefs = profile->GetPrefs();
   PrefChangeRegistrar registrar;
   registrar.Init(prefs);
-  registrar.Add(TranslatePrefs::kPrefTranslateLanguageBlacklist,
+  registrar.Add(TranslatePrefs::kPrefTranslateBlockedLanguages,
                 pref_callback_);
   TranslatePrefs translate_prefs(prefs);
   EXPECT_FALSE(translate_prefs.IsBlockedLanguage("fr"));
   EXPECT_TRUE(translate_prefs.CanTranslateLanguage(profile, "fr"));
-  SetPrefObserverExpectation(TranslatePrefs::kPrefTranslateLanguageBlacklist);
+  SetPrefObserverExpectation(TranslatePrefs::kPrefTranslateBlockedLanguages);
   translate_prefs.BlockLanguage("fr");
   EXPECT_TRUE(translate_prefs.IsBlockedLanguage("fr"));
   EXPECT_FALSE(translate_prefs.IsSiteBlacklisted(url.host()));
@@ -1048,7 +1048,7 @@
   EXPECT_TRUE(GetTranslateInfoBar() == NULL);
 
   // Remove the language from the blacklist.
-  SetPrefObserverExpectation(TranslatePrefs::kPrefTranslateLanguageBlacklist);
+  SetPrefObserverExpectation(TranslatePrefs::kPrefTranslateBlockedLanguages);
   translate_prefs.UnblockLanguage("fr");
   EXPECT_FALSE(translate_prefs.IsBlockedLanguage("fr"));
   EXPECT_FALSE(translate_prefs.IsSiteBlacklisted(url.host()));
diff --git a/chrome/browser/translate/translate_prefs.cc b/chrome/browser/translate/translate_prefs.cc
index 25541c7..6d5fabf 100644
--- a/chrome/browser/translate/translate_prefs.cc
+++ b/chrome/browser/translate/translate_prefs.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/translate/translate_prefs.h"
 
-#include "base/command_line.h"
 #include "base/prefs/pref_service.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
@@ -13,7 +12,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/translate/translate_accept_languages.h"
 #include "chrome/browser/translate/translate_manager.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/translate/translate_util.h"
 #include "components/user_prefs/pref_registry_syncable.h"
@@ -80,37 +78,20 @@
 
 bool TranslatePrefs::IsBlockedLanguage(
     const std::string& original_language) const {
-  CommandLine* command_line = CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kEnableTranslateSettings)) {
-    return IsValueBlacklisted(kPrefTranslateBlockedLanguages,
-                              original_language);
-  } else {
-    return IsValueBlacklisted(kPrefTranslateLanguageBlacklist,
-                              original_language);
-  }
+  return IsValueBlacklisted(kPrefTranslateBlockedLanguages,
+                            original_language);
 }
 
 void TranslatePrefs::BlockLanguage(
     const std::string& original_language) {
-  CommandLine* command_line = CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kEnableTranslateSettings)) {
-    BlacklistValue(kPrefTranslateBlockedLanguages, original_language);
-    AppendLanguageToAcceptLanguages(prefs_, original_language);
-  } else {
-    BlacklistValue(kPrefTranslateLanguageBlacklist, original_language);
-  }
+  BlacklistValue(kPrefTranslateBlockedLanguages, original_language);
+  AppendLanguageToAcceptLanguages(prefs_, original_language);
 }
 
 void TranslatePrefs::UnblockLanguage(
     const std::string& original_language) {
-  CommandLine* command_line = CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kEnableTranslateSettings)) {
-    RemoveValueFromBlacklist(kPrefTranslateBlockedLanguages,
-                             original_language);
-  } else {
-    RemoveValueFromBlacklist(kPrefTranslateLanguageBlacklist,
-                             original_language);
-  }
+  RemoveValueFromBlacklist(kPrefTranslateBlockedLanguages,
+                           original_language);
 }
 
 void TranslatePrefs::RemoveLanguageFromLegacyBlacklist(
@@ -169,19 +150,11 @@
 }
 
 bool TranslatePrefs::HasBlacklistedLanguages() const {
-  CommandLine* command_line = CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kEnableTranslateSettings))
-    return !IsListEmpty(kPrefTranslateBlockedLanguages);
-  else
-    return !IsListEmpty(kPrefTranslateLanguageBlacklist);
+  return !IsListEmpty(kPrefTranslateBlockedLanguages);
 }
 
 void TranslatePrefs::ClearBlacklistedLanguages() {
-  CommandLine* command_line = CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kEnableTranslateSettings))
-    prefs_->ClearPref(kPrefTranslateBlockedLanguages);
-  else
-    prefs_->ClearPref(kPrefTranslateLanguageBlacklist);
+  prefs_->ClearPref(kPrefTranslateBlockedLanguages);
 }
 
 bool TranslatePrefs::HasBlacklistedSites() const {
@@ -253,27 +226,20 @@
   TranslatePrefs translate_prefs(profile->GetPrefs());
   bool blocked = translate_prefs.IsBlockedLanguage(language);
 
-  CommandLine* command_line = CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kEnableTranslateSettings)) {
-    bool is_accept_language =
-        TranslateManager::IsAcceptLanguage(profile, language);
-    bool can_be_accept_language =
-        TranslateAcceptLanguages::CanBeAcceptLanguage(language);
+  bool is_accept_language =
+      TranslateManager::IsAcceptLanguage(profile, language);
+  bool can_be_accept_language =
+      TranslateAcceptLanguages::CanBeAcceptLanguage(language);
 
-    // Don't translate any user black-listed languages. Checking
-    // |is_accept_language| is necessary because if the user eliminates the
-    // language from the preference, it is natural to forget whether or not
-    // the language should be translated. Checking |cannot_be_accept_language|
-    // is also necessary because some minor languages can't be selected in the
-    // language preference even though the language is available in Translate
-    // server.
-    if (blocked && (is_accept_language || !can_be_accept_language))
-      return false;
-  } else {
-    // Don't translate any user user selected language.
-    if (blocked)
-      return false;
-  }
+  // Don't translate any user black-listed languages. Checking
+  // |is_accept_language| is necessary because if the user eliminates the
+  // language from the preference, it is natural to forget whether or not
+  // the language should be translated. Checking |cannot_be_accept_language|
+  // is also necessary because some minor languages can't be selected in the
+  // language preference even though the language is available in Translate
+  // server.
+  if (blocked && (is_accept_language || !can_be_accept_language))
+    return false;
 
   return true;
 }
@@ -354,11 +320,7 @@
   // enable settings for users.
   bool merged = user_prefs->HasPrefPath(kPrefTranslateBlockedLanguages);
 
-  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
-  bool enabled_translate_settings =
-      command_line.HasSwitch(switches::kEnableTranslateSettings);
-
-  if (!merged && enabled_translate_settings) {
+  if (!merged) {
     std::vector<std::string> blacklisted_languages;
     GetBlacklistedLanguages(user_prefs, &blacklisted_languages);
 
diff --git a/chrome/browser/ui/android/autofill/autofill_dialog_controller_android.cc b/chrome/browser/ui/android/autofill/autofill_dialog_controller_android.cc
index 00f5da8..e9948ca 100644
--- a/chrome/browser/ui/android/autofill/autofill_dialog_controller_android.cc
+++ b/chrome/browser/ui/android/autofill/autofill_dialog_controller_android.cc
@@ -110,6 +110,33 @@
       form_structure, full_wallet, email_address);
 }
 
+// Returns true if |input_type| in |section| is needed for |form_structure|.
+bool IsSectionInputUsedInFormStructure(DialogSection section,
+                                       ServerFieldType input_type,
+                                       const FormStructure& form_structure) {
+  const DetailInput input = { 0, input_type };
+  for (size_t i = 0; i < form_structure.field_count(); ++i) {
+    const AutofillField* field = form_structure.field(i);
+    if (field && common::DetailInputMatchesField(section, input, *field))
+      return true;
+  }
+  return false;
+}
+
+// Returns true if one of |inputs| in |section| is needed for |form_structure|.
+bool IsSectionInputsUsedInFormStructure(DialogSection section,
+                                        const ServerFieldType* input_types,
+                                        const size_t input_types_size,
+                                        const FormStructure& form_structure) {
+  for (size_t i = 0; i < input_types_size; ++i) {
+    if (IsSectionInputUsedInFormStructure(
+        section, input_types[i], form_structure)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 }  // namespace
 
 
@@ -205,33 +232,40 @@
     return;
   }
 
-  bool request_full_billing_address = true;
+  const ServerFieldType full_billing_is_necessary_if[] = {
+      ADDRESS_BILLING_LINE1,
+      ADDRESS_BILLING_LINE2,
+      ADDRESS_BILLING_CITY,
+      ADDRESS_BILLING_STATE,
+      PHONE_BILLING_WHOLE_NUMBER
+  };
+  const bool request_full_billing_address =
+      IsSectionInputsUsedInFormStructure(
+          SECTION_BILLING,
+          full_billing_is_necessary_if,
+          arraysize(full_billing_is_necessary_if),
+          form_structure_);
+  const bool request_phone_numbers =
+      IsSectionInputUsedInFormStructure(
+          SECTION_BILLING,
+          PHONE_BILLING_WHOLE_NUMBER,
+          form_structure_) ||
+      IsSectionInputUsedInFormStructure(
+          SECTION_SHIPPING,
+          PHONE_HOME_WHOLE_NUMBER,
+          form_structure_);
+
   bool request_shipping_address = false;
-  bool request_phone_numbers = false;
-
-  for (size_t i = 0; i < form_structure_.field_count(); ++i) {
-    const ServerFieldType type =
-        form_structure_.field(i)->Type().GetStorableType();
-    if (type == PHONE_HOME_WHOLE_NUMBER || type == PHONE_BILLING_WHOLE_NUMBER) {
-      request_phone_numbers = true;
-    }
-    if (type == NAME_FULL ||
-        type == ADDRESS_HOME_LINE1 || type == ADDRESS_HOME_LINE2 ||
-        type == ADDRESS_HOME_CITY || type == ADDRESS_HOME_STATE ||
-        type == ADDRESS_HOME_ZIP || type == ADDRESS_HOME_COUNTRY ||
-        type == PHONE_HOME_WHOLE_NUMBER) {
-      request_shipping_address = true;
-    }
-    if (type == ADDRESS_BILLING_LINE1 || type == ADDRESS_BILLING_LINE2 ||
-        type == ADDRESS_BILLING_CITY || type == ADDRESS_BILLING_STATE ||
-        type == PHONE_BILLING_WHOLE_NUMBER) {
-      request_full_billing_address = true;
-    }
+  {
+    DetailInputs inputs;
+    common::BuildInputsForSection(SECTION_SHIPPING, &inputs);
+    EmptyDataModelWrapper empty_wrapper;
+    request_shipping_address = empty_wrapper.FillFormStructure(
+        inputs,
+        base::Bind(common::DetailInputMatchesField, SECTION_SHIPPING),
+        &form_structure_);
   }
 
-  if (request_shipping_address)
-    request_full_billing_address = true;
-
   const bool incognito_mode = profile_->IsOffTheRecord();
 
   bool last_used_choice_is_autofill = false;
@@ -454,7 +488,7 @@
 }
 
 bool AutofillDialogControllerAndroid::TransmissionWillBeSecure() const {
-  return source_url_.SchemeIs(chrome::kHttpsScheme);
+  return source_url_.SchemeIs(content::kHttpsScheme);
 }
 
 void AutofillDialogControllerAndroid::SetAutocheckoutState(
diff --git a/chrome/browser/ui/android/extensions/extension_install_ui_android.cc b/chrome/browser/ui/android/extensions/extension_install_ui_android.cc
index 25e6b4d..91cce79 100644
--- a/chrome/browser/ui/android/extensions/extension_install_ui_android.cc
+++ b/chrome/browser/ui/android/extensions/extension_install_ui_android.cc
@@ -17,10 +17,6 @@
   NOTIMPLEMENTED();
 }
 
-void ExtensionInstallUIAndroid::SetSkipPostInstallUI(bool skip_ui) {
-  NOTIMPLEMENTED();
-}
-
 // static
 ExtensionInstallUI* ExtensionInstallUI::Create(Profile* profile) {
   NOTIMPLEMENTED();
@@ -34,11 +30,6 @@
 }
 
 // static
-void ExtensionInstallUI::DisableFailureUIForTests() {
-  NOTIMPLEMENTED();
-}
-
-// static
 ExtensionInstallPrompt* ExtensionInstallUI::CreateInstallPromptWithBrowser(
     Browser* browser) {
   NOTIMPLEMENTED();
diff --git a/chrome/browser/ui/android/extensions/extension_install_ui_android.h b/chrome/browser/ui/android/extensions/extension_install_ui_android.h
index 7fc5d1e..5f972de 100644
--- a/chrome/browser/ui/android/extensions/extension_install_ui_android.h
+++ b/chrome/browser/ui/android/extensions/extension_install_ui_android.h
@@ -19,7 +19,6 @@
                                 SkBitmap* icon) OVERRIDE;
   virtual void OnInstallFailure(
       const extensions::CrxInstallerError& error) OVERRIDE;
-  virtual void SetSkipPostInstallUI(bool skip_ui) OVERRIDE;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ExtensionInstallUIAndroid);
diff --git a/chrome/browser/ui/android/ssl_client_certificate_request.cc b/chrome/browser/ui/android/ssl_client_certificate_request.cc
index 2b87b41..ea80270 100644
--- a/chrome/browser/ui/android/ssl_client_certificate_request.cc
+++ b/chrome/browser/ui/android/ssl_client_certificate_request.cc
@@ -7,6 +7,7 @@
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/compiler_specific.h"
@@ -115,7 +116,7 @@
     return;
   }
 
-  guard.Release();
+  ignore_result(guard.Release());
 
   // Ownership was transferred to Java.
   chrome::SelectCertificateCallback* ALLOW_UNUSED dummy =
@@ -187,7 +188,7 @@
     return;
   }
 
-  guard.Release();
+  ignore_result(guard.Release());
 
   // RecordClientCertificateKey() must be called on the I/O thread,
   // before the callback is called with the selected certificate on
diff --git a/chrome/browser/ui/android/tab_model/tab_model.cc b/chrome/browser/ui/android/tab_model/tab_model.cc
index d21b827..4402b68 100644
--- a/chrome/browser/ui/android/tab_model/tab_model.cc
+++ b/chrome/browser/ui/android/tab_model/tab_model.cc
@@ -41,13 +41,6 @@
   }
 }
 
-TabModel::TabModel()
-  : profile_(NULL),
-    is_off_the_record_(false),
-    synced_window_delegate_(
-        new browser_sync::SyncedWindowDelegateAndroid(this)) {
-}
-
 TabModel::~TabModel() {
 }
 
diff --git a/chrome/browser/ui/android/tab_model/tab_model.h b/chrome/browser/ui/android/tab_model/tab_model.h
index 3d7bf51..620bdc9 100644
--- a/chrome/browser/ui/android/tab_model/tab_model.h
+++ b/chrome/browser/ui/android/tab_model/tab_model.h
@@ -32,9 +32,6 @@
                  public ToolbarModelDelegate {
  public:
   explicit TabModel(Profile* profile);
-  // Deprecated: This constructor is deprecated and should be removed once
-  // downstream Android uses new constructor. See crbug.com/159704.
-  TabModel();
   virtual ~TabModel();
 
   // Implementation of ToolbarDelegate
diff --git a/chrome/browser/ui/android/tab_model/tab_model_list.cc b/chrome/browser/ui/android/tab_model/tab_model_list.cc
index aecd9e4..00dc288 100644
--- a/chrome/browser/ui/android/tab_model/tab_model_list.cc
+++ b/chrome/browser/ui/android/tab_model/tab_model_list.cc
@@ -4,8 +4,10 @@
 
 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
 
+#include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/android/tab_model/tab_model.h"
+#include "chrome/browser/ui/browser_navigator.h"
 
 namespace {
 
@@ -32,6 +34,15 @@
     tab_models().erase(remove_tab_model);
 }
 
+void TabModelList::HandlePopupNavigation(chrome::NavigateParams* params) {
+  TabAndroid* tab = TabAndroid::FromWebContents(params->source_contents);
+
+  // NOTE: If this fails contact dtrainor@.
+  DCHECK(tab);
+  tab->HandlePopupNavigation(params);
+}
+
+
 TabModel* TabModelList::GetTabModelWithProfile(
     Profile* profile) {
   if (!profile)
diff --git a/chrome/browser/ui/android/tab_model/tab_model_list.h b/chrome/browser/ui/android/tab_model/tab_model_list.h
index 0d8e0df..b4c1cd9 100644
--- a/chrome/browser/ui/android/tab_model/tab_model_list.h
+++ b/chrome/browser/ui/android/tab_model/tab_model_list.h
@@ -12,6 +12,10 @@
 class Profile;
 class TabModel;
 
+namespace chrome {
+struct NavigateParams;
+}
+
 // Stores a list of all TabModel objects.
 class TabModelList {
  public:
@@ -19,6 +23,7 @@
   typedef TabModelVector::iterator iterator;
   typedef TabModelVector::const_iterator const_iterator;
 
+  static void HandlePopupNavigation(chrome::NavigateParams* params);
   static void AddTabModel(TabModel* tab_model);
   static void RemoveTabModel(TabModel* tab_model);
 
diff --git a/chrome/browser/ui/app_list/app_list_controller_browsertest.cc b/chrome/browser/ui/app_list/app_list_controller_browsertest.cc
index e773383..05e0799 100644
--- a/chrome/browser/ui/app_list/app_list_controller_browsertest.cc
+++ b/chrome/browser/ui/app_list/app_list_controller_browsertest.cc
@@ -4,12 +4,17 @@
 
 #include "base/command_line.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_loop.h"
 #include "base/prefs/pref_service.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "chrome/browser/ui/app_list/app_list_service.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/host_desktop.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -37,7 +42,27 @@
   DISALLOW_COPY_AND_ASSIGN(AppListControllerBrowserTest);
 };
 
-#if !defined(OS_CHROMEOS) && !defined(USE_AURA)
+// Test the CreateNewWindow function of the controller delegate.
+IN_PROC_BROWSER_TEST_F(AppListControllerBrowserTest, CreateNewWindow) {
+  const chrome::HostDesktopType desktop = chrome::GetActiveDesktop();
+  AppListService* service = AppListService::Get();
+  scoped_ptr<AppListControllerDelegate> controller(
+      service->CreateControllerDelegate());
+  ASSERT_TRUE(controller);
+
+  EXPECT_EQ(1U, chrome::GetBrowserCount(browser()->profile(), desktop));
+  EXPECT_EQ(0U, chrome::GetBrowserCount(
+      browser()->profile()->GetOffTheRecordProfile(), desktop));
+
+  controller->CreateNewWindow(browser()->profile(), false);
+  EXPECT_EQ(2U, chrome::GetBrowserCount(browser()->profile(), desktop));
+
+  controller->CreateNewWindow(browser()->profile(), true);
+  EXPECT_EQ(1U, chrome::GetBrowserCount(
+      browser()->profile()->GetOffTheRecordProfile(), desktop));
+}
+
+#if !defined(OS_CHROMEOS)
 // Show the app list, then dismiss it.
 IN_PROC_BROWSER_TEST_F(AppListControllerBrowserTest, ShowAndDismiss) {
   AppListService* service = AppListService::Get();
@@ -94,4 +119,4 @@
   CreateBrowser(service->GetCurrentAppListProfile());
   service->DismissAppList();
 }
-#endif  // !defined(OS_CHROMEOS) && !defined(USE_AURA)
+#endif  // !defined(OS_CHROMEOS)
diff --git a/chrome/browser/ui/app_list/app_list_service_impl.cc b/chrome/browser/ui/app_list/app_list_service_impl.cc
index 8b8b90b..d6ec6d2 100644
--- a/chrome/browser/ui/app_list/app_list_service_impl.cc
+++ b/chrome/browser/ui/app_list/app_list_service_impl.cc
@@ -153,10 +153,6 @@
       profile_path.BaseName().MaybeAsASCII());
 }
 
-AppListControllerDelegate* AppListServiceImpl::CreateControllerDelegate() {
-  return NULL;
-}
-
 void AppListServiceImpl::CreateShortcut() {}
 
 // We need to watch for profile removal to keep kAppListProfile updated.
diff --git a/chrome/browser/ui/app_list/app_list_service_impl.h b/chrome/browser/ui/app_list/app_list_service_impl.h
index 86ed93d..8118547 100644
--- a/chrome/browser/ui/app_list/app_list_service_impl.h
+++ b/chrome/browser/ui/app_list/app_list_service_impl.h
@@ -57,7 +57,6 @@
   virtual void SetProfilePath(const base::FilePath& profile_path) OVERRIDE;
   virtual void Show() OVERRIDE;
   virtual void EnableAppList(Profile* initial_profile) OVERRIDE;
-  virtual AppListControllerDelegate* CreateControllerDelegate() OVERRIDE;
 
  private:
   // Loads a profile asynchronously and calls OnProfileLoaded() when done.
diff --git a/chrome/browser/ui/app_list/app_list_service_mac.mm b/chrome/browser/ui/app_list/app_list_service_mac.mm
index 592f71d..0fc1053 100644
--- a/chrome/browser/ui/app_list/app_list_service_mac.mm
+++ b/chrome/browser/ui/app_list/app_list_service_mac.mm
@@ -72,6 +72,7 @@
   virtual void DismissAppList() OVERRIDE;
   virtual bool IsAppListVisible() const OVERRIDE;
   virtual gfx::NativeWindow GetAppListWindow() OVERRIDE;
+  virtual AppListControllerDelegate* CreateControllerDelegate() OVERRIDE;
 
   // AppListServiceImpl overrides:
   virtual void CreateShortcut() OVERRIDE;
@@ -394,6 +395,10 @@
   return [window_controller_ window];
 }
 
+AppListControllerDelegate* AppListServiceMac::CreateControllerDelegate() {
+  return new AppListControllerDelegateCocoa();
+}
+
 void AppListServiceMac::OnShimLaunch(apps::AppShimHandler::Host* host,
                                      apps::AppShimLaunchType launch_type) {
   if (IsAppListVisible())
diff --git a/chrome/browser/ui/app_list/app_list_service_mac_browsertest.mm b/chrome/browser/ui/app_list/app_list_service_mac_browsertest.mm
index a5089b1..99b8375 100644
--- a/chrome/browser/ui/app_list/app_list_service_mac_browsertest.mm
+++ b/chrome/browser/ui/app_list/app_list_service_mac_browsertest.mm
@@ -37,6 +37,7 @@
   virtual void OnAppClosed() OVERRIDE {
     NOTREACHED();
   }
+  virtual void OnAppRequestUserAttention() OVERRIDE {}
   virtual base::FilePath GetProfilePath() const OVERRIDE {
     NOTREACHED();  // Currently unused in this test.
     return base::FilePath();
diff --git a/chrome/browser/ui/app_list/search/common/json_response_fetcher.cc b/chrome/browser/ui/app_list/search/common/json_response_fetcher.cc
new file mode 100644
index 0000000..4588e4e
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/common/json_response_fetcher.cc
@@ -0,0 +1,90 @@
+// 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 "chrome/browser/ui/app_list/search/common/json_response_fetcher.h"
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "chrome/browser/safe_json_parser.h"
+#include "net/base/load_flags.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_status.h"
+#include "url/gurl.h"
+
+namespace app_list {
+
+const char kBadResponse[] = "Bad Web Service search response";
+
+JSONResponseFetcher::JSONResponseFetcher(
+    const Callback& callback,
+    net::URLRequestContextGetter* context_getter)
+    : callback_(callback),
+      context_getter_(context_getter),
+      weak_factory_(this) {
+  DCHECK(!callback_.is_null());
+}
+
+JSONResponseFetcher::~JSONResponseFetcher() {}
+
+void JSONResponseFetcher::Start(const GURL& query_url) {
+  Stop();
+
+  fetcher_.reset(net::URLFetcher::Create(
+      query_url,
+      net::URLFetcher::GET,
+      this));
+  fetcher_->SetRequestContext(context_getter_);
+  fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
+                         net::LOAD_DISABLE_CACHE);
+  fetcher_->Start();
+}
+
+void JSONResponseFetcher::Stop() {
+  fetcher_.reset();
+  weak_factory_.InvalidateWeakPtrs();
+}
+
+void JSONResponseFetcher::OnJsonParseSuccess(
+    scoped_ptr<base::Value> parsed_json) {
+  if (!parsed_json->IsType(base::Value::TYPE_DICTIONARY)) {
+    OnJsonParseError(kBadResponse);
+    return;
+  }
+
+  callback_.Run(make_scoped_ptr(
+      static_cast<base::DictionaryValue*>(parsed_json.release())));
+}
+
+void JSONResponseFetcher::OnJsonParseError(const std::string& error) {
+  callback_.Run(scoped_ptr<base::DictionaryValue>());
+}
+
+void JSONResponseFetcher::OnURLFetchComplete(
+    const net::URLFetcher* source) {
+  CHECK_EQ(fetcher_.get(), source);
+
+  scoped_ptr<net::URLFetcher> fetcher(fetcher_.Pass());
+
+  if (!fetcher->GetStatus().is_success() ||
+      fetcher->GetResponseCode() != 200) {
+    OnJsonParseError(kBadResponse);
+    return;
+  }
+
+  std::string webstore_json_data;
+  fetcher->GetResponseAsString(&webstore_json_data);
+
+  scoped_refptr<SafeJsonParser> parser =
+      new SafeJsonParser(webstore_json_data,
+                         base::Bind(
+                             &JSONResponseFetcher::OnJsonParseSuccess,
+                             weak_factory_.GetWeakPtr()),
+                         base::Bind(
+                             &JSONResponseFetcher::OnJsonParseError,
+                             weak_factory_.GetWeakPtr()));
+  // The parser will call us back via one of the callbacks.
+  parser->Start();
+}
+
+}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/common/json_response_fetcher.h b/chrome/browser/ui/app_list/search/common/json_response_fetcher.h
new file mode 100644
index 0000000..04b9fdf
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/common/json_response_fetcher.h
@@ -0,0 +1,65 @@
+// 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 CHROME_BROWSER_UI_APP_LIST_SEARCH_COMMON_JSON_RESPONSE_FETCHER_H_
+#define CHROME_BROWSER_UI_APP_LIST_SEARCH_COMMON_JSON_RESPONSE_FETCHER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "net/url_request/url_fetcher_delegate.h"
+
+class GURL;
+
+namespace base {
+class DictionaryValue;
+class Value;
+}
+
+namespace net {
+class URLFetcher;
+class URLRequestContextGetter;
+}
+
+namespace app_list {
+
+// A class that fetches a JSON formatted response from a server and uses a
+// sandboxed utility process to parse it to a DictionaryValue.
+class JSONResponseFetcher : public net::URLFetcherDelegate {
+ public:
+  // Callback to pass back the parsed json dictionary returned from the server.
+  // Invoked with NULL if there is an error.
+  typedef base::Callback<void(scoped_ptr<base::DictionaryValue>)> Callback;
+
+  JSONResponseFetcher(const Callback& callback,
+                      net::URLRequestContextGetter* context_getter);
+  virtual ~JSONResponseFetcher();
+
+  // Starts to fetch results for the given |query_url|.
+  void Start(const GURL& query_url);
+  void Stop();
+
+ private:
+  // Callbacks for SafeJsonParser.
+  void OnJsonParseSuccess(scoped_ptr<base::Value> parsed_json);
+  void OnJsonParseError(const std::string& error);
+
+  // net::URLFetcherDelegate overrides:
+  virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
+
+  Callback callback_;
+  net::URLRequestContextGetter* context_getter_;
+
+  scoped_ptr<net::URLFetcher> fetcher_;
+  base::WeakPtrFactory<JSONResponseFetcher> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(JSONResponseFetcher);
+};
+
+}  // namespace app_list
+
+#endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_COMMON_JSON_RESPONSE_FETCHER_H_
diff --git a/chrome/browser/ui/app_list/search/common/url_icon_source.cc b/chrome/browser/ui/app_list/search/common/url_icon_source.cc
new file mode 100644
index 0000000..443da12
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/common/url_icon_source.cc
@@ -0,0 +1,98 @@
+// 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 "chrome/browser/ui/app_list/search/common/url_icon_source.h"
+
+#include <string>
+
+#include "content/public/browser/browser_thread.h"
+#include "net/base/load_flags.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_status.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image_skia_operations.h"
+
+using content::BrowserThread;
+
+namespace app_list {
+
+UrlIconSource::UrlIconSource(
+    const IconLoadedCallback& icon_loaded_callback,
+    net::URLRequestContextGetter* context_getter,
+    const GURL& icon_url,
+    int icon_size,
+    int default_icon_resource_id)
+    : icon_loaded_callback_(icon_loaded_callback),
+      context_getter_(context_getter),
+      icon_url_(icon_url),
+      icon_size_(icon_size),
+      default_icon_resource_id_(default_icon_resource_id),
+      icon_fetch_attempted_(false) {
+  DCHECK(!icon_loaded_callback_.is_null());
+}
+
+UrlIconSource::~UrlIconSource() {
+  if (image_decoder_.get())
+    image_decoder_->set_delegate(NULL);
+}
+
+void UrlIconSource::StartIconFetch() {
+  icon_fetch_attempted_ = true;
+
+  icon_fetcher_.reset(
+      net::URLFetcher::Create(icon_url_, net::URLFetcher::GET, this));
+  icon_fetcher_->SetRequestContext(context_getter_);
+  icon_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
+  icon_fetcher_->Start();
+}
+
+gfx::ImageSkiaRep UrlIconSource::GetImageForScale(
+    ui::ScaleFactor scale_factor) {
+  if (!icon_fetch_attempted_)
+    StartIconFetch();
+
+  if (!icon_.isNull())
+    return icon_.GetRepresentation(scale_factor);
+
+  return ui::ResourceBundle::GetSharedInstance()
+      .GetImageSkiaNamed(default_icon_resource_id_)->GetRepresentation(
+          scale_factor);
+}
+
+void UrlIconSource::OnURLFetchComplete(
+    const net::URLFetcher* source) {
+  CHECK_EQ(icon_fetcher_.get(), source);
+
+  scoped_ptr<net::URLFetcher> fetcher(icon_fetcher_.Pass());
+
+  if (!fetcher->GetStatus().is_success() ||
+      fetcher->GetResponseCode() != 200) {
+    return;
+  }
+
+  std::string unsafe_icon_data;
+  fetcher->GetResponseAsString(&unsafe_icon_data);
+
+  image_decoder_ =
+      new ImageDecoder(this, unsafe_icon_data, ImageDecoder::DEFAULT_CODEC);
+  image_decoder_->Start(
+      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI));
+}
+
+void UrlIconSource::OnImageDecoded(const ImageDecoder* decoder,
+                                   const SkBitmap& decoded_image) {
+  icon_ = gfx::ImageSkiaOperations::CreateResizedImage(
+      gfx::ImageSkia::CreateFrom1xBitmap(decoded_image),
+      skia::ImageOperations::RESIZE_BEST,
+      gfx::Size(icon_size_, icon_size_));
+
+  icon_loaded_callback_.Run();
+}
+
+void UrlIconSource::OnDecodeImageFailed(
+    const ImageDecoder* decoder) {
+  // Failed to decode image. Do nothing and just use the default icon.
+}
+
+}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/common/url_icon_source.h b/chrome/browser/ui/app_list/search/common/url_icon_source.h
new file mode 100644
index 0000000..de94f28
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/common/url_icon_source.h
@@ -0,0 +1,77 @@
+// 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 CHROME_BROWSER_UI_APP_LIST_SEARCH_COMMON_URL_ICON_SOURCE_H_
+#define CHROME_BROWSER_UI_APP_LIST_SEARCH_COMMON_URL_ICON_SOURCE_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/image_decoder.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/image/image_skia_source.h"
+#include "url/gurl.h"
+
+namespace net {
+class URLFetcher;
+class URLRequestContextGetter;
+}
+
+namespace app_list {
+
+// An ImageSkiaSource for icons fetched from a URL. Till the URL icon is
+// fetched, the default icon (specified by it's resource id) is shown.
+class UrlIconSource : public gfx::ImageSkiaSource,
+                      public net::URLFetcherDelegate,
+                      public ImageDecoder::Delegate {
+ public:
+  typedef base::Closure IconLoadedCallback;
+
+  // Create a URL Icon source with the given URL. The post_process parameter
+  // specifies a function to post-process the result icon before displaying it.
+  UrlIconSource(const IconLoadedCallback& icon_loaded_callback,
+                net::URLRequestContextGetter* context_getter,
+                const GURL& icon_url,
+                int icon_size,
+                int default_icon_resource_id);
+  virtual ~UrlIconSource();
+
+ private:
+  // Invoked from GetImageForScale to download the app icon when the hosting
+  // ImageSkia gets painted on screen.
+  void StartIconFetch();
+
+  // gfx::ImageSkiaSource overrides:
+  virtual gfx::ImageSkiaRep GetImageForScale(
+      ui::ScaleFactor scale_factor) OVERRIDE;
+
+  // net::URLFetcherDelegate overrides:
+  virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
+
+  // ImageDecoder::Delegate overrides:
+  virtual void OnImageDecoded(const ImageDecoder* decoder,
+                              const SkBitmap& decoded_image) OVERRIDE;
+  virtual void OnDecodeImageFailed(const ImageDecoder* decoder) OVERRIDE;
+
+  IconLoadedCallback icon_loaded_callback_;
+  net::URLRequestContextGetter* context_getter_;
+  const GURL icon_url_;
+  const int icon_size_;
+  const int default_icon_resource_id_;
+
+  bool icon_fetch_attempted_;
+  scoped_ptr<net::URLFetcher> icon_fetcher_;
+
+  scoped_refptr<ImageDecoder> image_decoder_;
+
+  gfx::ImageSkia icon_;
+
+  DISALLOW_COPY_AND_ASSIGN(UrlIconSource);
+};
+
+}  // namespace app_list
+
+#endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_COMMON_URL_ICON_SOURCE_H_
diff --git a/chrome/browser/ui/app_list/search/common/webservice_search_provider.cc b/chrome/browser/ui/app_list/search/common/webservice_search_provider.cc
new file mode 100644
index 0000000..b651e5d
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/common/webservice_search_provider.cc
@@ -0,0 +1,81 @@
+// 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 "chrome/browser/ui/app_list/search/common/webservice_search_provider.h"
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/strings/string_util.h"
+#include "chrome/common/url_constants.h"
+#include "url/gurl.h"
+
+namespace app_list {
+
+namespace {
+
+const int kWebserviceQueryThrottleIntrevalInMs = 100;
+
+}  // namespace
+
+WebserviceSearchProvider::WebserviceSearchProvider() : use_throttling_(true) {}
+
+WebserviceSearchProvider::~WebserviceSearchProvider() {}
+
+void WebserviceSearchProvider::StartThrottledQuery(
+    const base::Closure& start_query) {
+  base::TimeDelta interval =
+      base::TimeDelta::FromMilliseconds(kWebserviceQueryThrottleIntrevalInMs);
+  if (!use_throttling_ || base::Time::Now() - last_keytyped_ > interval) {
+    query_throttler_.Stop();
+    start_query.Run();
+  } else {
+    query_throttler_.Start(FROM_HERE, interval, start_query);
+  }
+  last_keytyped_ = base::Time::Now();
+}
+
+// Returns whether or not the user's input string, |query|, might contain any
+// sensitive information, based purely on its value and not where it came from.
+bool WebserviceSearchProvider::IsSensitiveInput(const string16& query) {
+  const GURL query_as_url(query);
+  if (!query_as_url.is_valid())
+    return false;
+
+  // The input can be interpreted as a URL. Check to see if it is potentially
+  // sensitive. (Code shamelessly copied from search_provider.cc's
+  // IsQuerySuitableForSuggest function.)
+
+  // First we check the scheme: if this looks like a URL with a scheme that is
+  // file, we shouldn't send it. Sending such things is a waste of time and a
+  // disclosure of potentially private, local data. If the scheme is OK, we
+  // still need to check other cases below.
+  if (LowerCaseEqualsASCII(query_as_url.scheme(), chrome::kFileScheme))
+    return true;
+
+  // Don't send URLs with usernames, queries or refs. Some of these are
+  // private, and the Suggest server is unlikely to have any useful results
+  // for any of them. Also don't send URLs with ports, as we may initially
+  // think that a username + password is a host + port (and we don't want to
+  // send usernames/passwords), and even if the port really is a port, the
+  // server is once again unlikely to have and useful results.
+  if (!query_as_url.username().empty() ||
+      !query_as_url.port().empty() ||
+      !query_as_url.query().empty() ||
+      !query_as_url.ref().empty()) {
+    return true;
+  }
+
+  // Don't send anything for https except the hostname. Hostnames are OK
+  // because they are visible when the TCP connection is established, but the
+  // specific path may reveal private information.
+  if (LowerCaseEqualsASCII(query_as_url.scheme(), content::kHttpsScheme) &&
+      !query_as_url.path().empty() && query_as_url.path() != "/") {
+    return true;
+  }
+
+  return false;
+}
+
+}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/common/webservice_search_provider.h b/chrome/browser/ui/app_list/search/common/webservice_search_provider.h
new file mode 100644
index 0000000..1b74b8e
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/common/webservice_search_provider.h
@@ -0,0 +1,42 @@
+// 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 CHROME_BROWSER_UI_APP_LIST_SEARCH_COMMON_WEBSERVICE_SEARCH_PROVIDER_H_
+#define CHROME_BROWSER_UI_APP_LIST_SEARCH_COMMON_WEBSERVICE_SEARCH_PROVIDER_H_
+
+#include "base/callback_forward.h"
+#include "base/strings/string16.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/ui/app_list/search/search_provider.h"
+
+namespace app_list {
+
+// Helper class for webservice based searches.
+class WebserviceSearchProvider : public SearchProvider {
+ public:
+  WebserviceSearchProvider();
+  virtual ~WebserviceSearchProvider();
+
+  void StartThrottledQuery(const base::Closure& start_query);
+  bool IsSensitiveInput(const string16& query);
+
+  void set_use_throttling(bool use) { use_throttling_ = use; }
+
+ private:
+  // The timestamp when the last key event happened.
+  base::Time last_keytyped_;
+
+  // The timer to throttle QPS to the webstore search .
+  base::OneShotTimer<WebserviceSearchProvider> query_throttler_;
+
+  // The flag for tests. It prevents the throttling If set to false.
+  bool use_throttling_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebserviceSearchProvider);
+};
+
+}  // namespace app_list
+
+#endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_COMMON_WEBSERVICE_SEARCH_PROVIDER_H_
diff --git a/chrome/browser/ui/app_list/search/webstore_provider.cc b/chrome/browser/ui/app_list/search/webstore_provider.cc
index ec8cdd4..2032423 100644
--- a/chrome/browser/ui/app_list/search/webstore_provider.cc
+++ b/chrome/browser/ui/app_list/search/webstore_provider.cc
@@ -8,17 +8,15 @@
 
 #include "base/bind.h"
 #include "base/metrics/field_trial.h"
-#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/search.h"
+#include "chrome/browser/ui/app_list/search/common/json_response_fetcher.h"
 #include "chrome/browser/ui/app_list/search/search_webstore_result.h"
 #include "chrome/browser/ui/app_list/search/webstore_result.h"
-#include "chrome/browser/ui/app_list/search/webstore_search_fetcher.h"
 #include "chrome/common/extensions/extension_constants.h"
-#include "chrome/common/url_constants.h"
 #include "url/gurl.h"
 
 namespace app_list {
@@ -30,7 +28,6 @@
 const char kKeyLocalizedName[] = "localized_name";
 const char kKeyIconUrl[] = "icon_url";
 const size_t kMinimumQueryLength = 3u;
-const int kWebstoreQueryThrottleIntrevalInMs = 100;
 
 // Returns true if the launcher should send queries to the web store server.
 bool UseWebstoreSearch() {
@@ -39,56 +36,12 @@
   return base::FieldTrialList::FindFullName(kFieldTrialName) == kEnable;
 }
 
-// Returns whether or not the user's input string, |query|, might contain any
-// sensitive information, based purely on its value and not where it came from.
-bool IsSensitiveInput(const string16& query) {
-  const GURL query_as_url(query);
-  if (!query_as_url.is_valid())
-    return false;
-
-  // The input can be interpreted as a URL. Check to see if it is potentially
-  // sensitive. (Code shamelessly copied from search_provider.cc's
-  // IsQuerySuitableForSuggest function.)
-
-  // First we check the scheme: if this looks like a URL with a scheme that is
-  // file, we shouldn't send it. Sending such things is a waste of time and a
-  // disclosure of potentially private, local data. If the scheme is OK, we
-  // still need to check other cases below.
-  if (LowerCaseEqualsASCII(query_as_url.scheme(), chrome::kFileScheme))
-    return true;
-
-  // Don't send URLs with usernames, queries or refs. Some of these are
-  // private, and the Suggest server is unlikely to have any useful results
-  // for any of them. Also don't send URLs with ports, as we may initially
-  // think that a username + password is a host + port (and we don't want to
-  // send usernames/passwords), and even if the port really is a port, the
-  // server is once again unlikely to have and useful results.
-  if (!query_as_url.username().empty() ||
-      !query_as_url.port().empty() ||
-      !query_as_url.query().empty() ||
-      !query_as_url.ref().empty()) {
-    return true;
-  }
-
-  // Don't send anything for https except the hostname. Hostnames are OK
-  // because they are visible when the TCP connection is established, but the
-  // specific path may reveal private information.
-  if (LowerCaseEqualsASCII(query_as_url.scheme(), chrome::kHttpsScheme) &&
-      !query_as_url.path().empty() &&
-      query_as_url.path() != "/") {
-    return true;
-  }
-
-  return false;
-}
-
 }  // namespace
 
 WebstoreProvider::WebstoreProvider(Profile* profile,
                                    AppListControllerDelegate* controller)
   : profile_(profile),
-    controller_(controller),
-    use_throttling_(true) {}
+    controller_(controller) {}
 
 WebstoreProvider::~WebstoreProvider() {}
 
@@ -120,24 +73,14 @@
 
   if (UseWebstoreSearch() && chrome::IsSuggestPrefEnabled(profile_)) {
     if (!webstore_search_) {
-      webstore_search_.reset(new WebstoreSearchFetcher(
+      webstore_search_.reset(new JSONResponseFetcher(
           base::Bind(&WebstoreProvider::OnWebstoreSearchFetched,
                      base::Unretained(this)),
           profile_->GetRequestContext()));
     }
 
-    base::TimeDelta interval =
-        base::TimeDelta::FromMilliseconds(kWebstoreQueryThrottleIntrevalInMs);
-    if (!use_throttling_ || base::Time::Now() - last_keytyped_ > interval) {
-      query_throttler_.Stop();
-      StartQuery();
-    } else {
-      query_throttler_.Start(
-          FROM_HERE,
-          interval,
-          base::Bind(&WebstoreProvider::StartQuery, base::Unretained(this)));
-    }
-    last_keytyped_ = base::Time::Now();
+    StartThrottledQuery(base::Bind(&WebstoreProvider::StartQuery,
+                                   base::Unretained(this)));
   }
 
   // Add a placeholder result which when clicked will run the user's query in a
@@ -156,7 +99,8 @@
   if (!webstore_search_ || query_.empty())
     return;
 
-  webstore_search_->Start(query_, g_browser_process->GetApplicationLocale());
+  webstore_search_->Start(extension_urls::GetWebstoreJsonSearchUrl(
+      query_, g_browser_process->GetApplicationLocale()));
 }
 
 void WebstoreProvider::OnWebstoreSearchFetched(
diff --git a/chrome/browser/ui/app_list/search/webstore_provider.h b/chrome/browser/ui/app_list/search/webstore_provider.h
index aa6e8d4..f55b63d 100644
--- a/chrome/browser/ui/app_list/search/webstore_provider.h
+++ b/chrome/browser/ui/app_list/search/webstore_provider.h
@@ -10,7 +10,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
-#include "chrome/browser/ui/app_list/search/search_provider.h"
+#include "chrome/browser/ui/app_list/search/common/webservice_search_provider.h"
 #include "chrome/browser/ui/app_list/search/webstore_cache.h"
 
 class AppListControllerDelegate;
@@ -27,12 +27,12 @@
 }
 
 class ChromeSearchResult;
-class WebstoreSearchFetcher;
+class JSONResponseFetcher;
 
 // WebstoreProvider fetches search results from the web store server.
 // A "Search in web store" result will be returned if the server does not
 // return any results.
-class WebstoreProvider : public SearchProvider {
+class WebstoreProvider : public WebserviceSearchProvider{
  public:
   WebstoreProvider(Profile* profile, AppListControllerDelegate* controller);
   virtual ~WebstoreProvider();
@@ -56,11 +56,9 @@
     webstore_search_fetched_callback_ = callback;
   }
 
-  void set_use_throttling(bool use) { use_throttling_ = use; }
-
   Profile* profile_;
   AppListControllerDelegate* controller_;
-  scoped_ptr<WebstoreSearchFetcher> webstore_search_;
+  scoped_ptr<JSONResponseFetcher> webstore_search_;
   base::Closure webstore_search_fetched_callback_;
 
   // The cache of the search result which will be valid only in a single
@@ -76,9 +74,6 @@
   // The current query.
   std::string query_;
 
-  // The flag for tests. It prevents the throttling If set to false.
-  bool use_throttling_;
-
   DISALLOW_COPY_AND_ASSIGN(WebstoreProvider);
 };
 
diff --git a/chrome/browser/ui/app_list/search/webstore_result.cc b/chrome/browser/ui/app_list/search/webstore_result.cc
index 87812a1..86cc477 100644
--- a/chrome/browser/ui/app_list/search/webstore_result.cc
+++ b/chrome/browser/ui/app_list/search/webstore_result.cc
@@ -15,14 +15,47 @@
 #include "chrome/browser/extensions/install_tracker_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
+#include "chrome/browser/ui/app_list/search/common/url_icon_source.h"
 #include "chrome/browser/ui/app_list/search/webstore_installer.h"
-#include "chrome/browser/ui/app_list/search/webstore_result_icon_source.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/common/extensions/extension.h"
 #include "grit/chromium_strings.h"
 #include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/canvas_image_source.h"
+
+
+namespace {
+
+const int kIconSize = 32;
+
+// BadgedImageSource adds a webstore badge to a webstore app icon.
+class BadgedIconSource : public gfx::CanvasImageSource {
+ public:
+  explicit BadgedIconSource(const gfx::ImageSkia& icon)
+      : CanvasImageSource(gfx::Size(kIconSize, kIconSize), false),
+        icon_(icon) {
+  }
+
+  virtual void Draw(gfx::Canvas* canvas) OVERRIDE {
+    canvas->DrawImageInt(icon_, 0, 0);
+    const gfx::ImageSkia& badge = *ui::ResourceBundle::GetSharedInstance().
+         GetImageSkiaNamed(IDR_WEBSTORE_ICON_16);
+    canvas->DrawImageInt(
+        badge, icon_.width() - badge.width(), icon_.height() - badge.height());
+  }
+
+ private:
+  gfx::ImageSkia icon_;
+
+  DISALLOW_COPY_AND_ASSIGN(BadgedIconSource);
+};
+
+}  // namespace
 
 namespace app_list {
 
@@ -46,14 +79,13 @@
 
   UpdateActions();
 
-  const int kIconSize = 32;
   icon_ = gfx::ImageSkia(
-      new WebstoreResultIconSource(
-          base::Bind(&WebstoreResult::OnIconLoaded,
-                     weak_factory_.GetWeakPtr()),
-          profile_->GetRequestContext(),
-          icon_url_,
-          kIconSize),
+      new UrlIconSource(base::Bind(&WebstoreResult::OnIconLoaded,
+                                   weak_factory_.GetWeakPtr()),
+                        profile_->GetRequestContext(),
+                        icon_url_,
+                        kIconSize,
+                        IDR_WEBSTORE_ICON_32),
       gfx::Size(kIconSize, kIconSize));
   SetIcon(icon_);
 
@@ -117,6 +149,9 @@
   for (size_t i = 0; i < image_reps.size(); ++i)
     icon_.RemoveRepresentation(image_reps[i].scale_factor());
 
+  icon_ = gfx::ImageSkia(new BadgedIconSource(icon_),
+                         gfx::Size(kIconSize, kIconSize));
+
   SetIcon(icon_);
 }
 
diff --git a/chrome/browser/ui/app_list/search/webstore_result_icon_source.cc b/chrome/browser/ui/app_list/search/webstore_result_icon_source.cc
deleted file mode 100644
index 66b3a70..0000000
--- a/chrome/browser/ui/app_list/search/webstore_result_icon_source.cc
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/app_list/search/webstore_result_icon_source.h"
-
-#include <string>
-
-#include "content/public/browser/browser_thread.h"
-#include "grit/theme_resources.h"
-#include "net/base/load_flags.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_status.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/image/image_skia_operations.h"
-
-using content::BrowserThread;
-
-namespace app_list {
-
-WebstoreResultIconSource::WebstoreResultIconSource(
-    const IconLoadedCallback& icon_loaded_callback,
-    net::URLRequestContextGetter* context_getter,
-    const GURL& icon_url,
-    int icon_size)
-    : icon_loaded_callback_(icon_loaded_callback),
-      context_getter_(context_getter),
-      icon_url_(icon_url),
-      icon_size_(icon_size),
-      icon_fetch_attempted_(false) {
-  DCHECK(!icon_loaded_callback_.is_null());
-}
-
-WebstoreResultIconSource::~WebstoreResultIconSource() {
-  if (image_decoder_.get())
-    image_decoder_->set_delegate(NULL);
-}
-
-void WebstoreResultIconSource::StartIconFetch() {
-  icon_fetch_attempted_ = true;
-
-  icon_fetcher_.reset(
-      net::URLFetcher::Create(icon_url_, net::URLFetcher::GET, this));
-  icon_fetcher_->SetRequestContext(context_getter_);
-  icon_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
-  icon_fetcher_->Start();
-}
-
-gfx::ImageSkiaRep WebstoreResultIconSource::CreateBadgedIcon(
-    ui::ScaleFactor scale_factor) {
-  gfx::Canvas canvas(gfx::Size(icon_size_, icon_size_), scale_factor, false);
-
-  canvas.DrawImageInt(icon_, 0, 0);
-
-  const gfx::ImageSkia& badge = *ui::ResourceBundle::GetSharedInstance().
-       GetImageSkiaNamed(IDR_WEBSTORE_ICON_16);
-  canvas.DrawImageInt(
-      badge, icon_.width() - badge.width(), icon_.height() - badge.height());
-
-  return canvas.ExtractImageRep();
-}
-
-gfx::ImageSkiaRep WebstoreResultIconSource::GetImageForScale(
-    ui::ScaleFactor scale_factor) {
-  if (!icon_fetch_attempted_)
-    StartIconFetch();
-
-  if (!icon_.isNull())
-    return CreateBadgedIcon(scale_factor);
-
-  return ui::ResourceBundle::GetSharedInstance()
-      .GetImageSkiaNamed(IDR_WEBSTORE_ICON_32)->GetRepresentation(scale_factor);
-}
-
-void WebstoreResultIconSource::OnURLFetchComplete(
-    const net::URLFetcher* source) {
-  CHECK_EQ(icon_fetcher_.get(), source);
-
-  scoped_ptr<net::URLFetcher> fetcher(icon_fetcher_.Pass());
-
-  if (!fetcher->GetStatus().is_success() ||
-      fetcher->GetResponseCode() != 200) {
-    return;
-  }
-
-  std::string unsafe_icon_data;
-  fetcher->GetResponseAsString(&unsafe_icon_data);
-
-  image_decoder_ =
-      new ImageDecoder(this, unsafe_icon_data, ImageDecoder::DEFAULT_CODEC);
-  image_decoder_->Start(
-      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI));
-}
-
-void WebstoreResultIconSource::OnImageDecoded(const ImageDecoder* decoder,
-                                              const SkBitmap& decoded_image) {
-  icon_ = gfx::ImageSkiaOperations::CreateResizedImage(
-      gfx::ImageSkia::CreateFrom1xBitmap(decoded_image),
-      skia::ImageOperations::RESIZE_BEST,
-      gfx::Size(icon_size_, icon_size_));
-  icon_loaded_callback_.Run();
-}
-
-void WebstoreResultIconSource::OnDecodeImageFailed(
-    const ImageDecoder* decoder) {
-  // Failed to decode image. Do nothing and just use web store icon.
-}
-
-}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/webstore_result_icon_source.h b/chrome/browser/ui/app_list/search/webstore_result_icon_source.h
deleted file mode 100644
index 3b43335..0000000
--- a/chrome/browser/ui/app_list/search/webstore_result_icon_source.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_WEBSTORE_RESULT_ICON_SOURCE_H_
-#define CHROME_BROWSER_UI_APP_LIST_SEARCH_WEBSTORE_RESULT_ICON_SOURCE_H_
-
-#include "base/basictypes.h"
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "chrome/browser/image_decoder.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "ui/gfx/image/image_skia.h"
-#include "ui/gfx/image/image_skia_source.h"
-#include "url/gurl.h"
-
-namespace net {
-class URLFetcher;
-class URLRequestContextGetter;
-}
-
-namespace app_list {
-
-// An ImageSkiaSource for the icon of web store result. It shows web store
-// icon before the app icon is fetched. When the app icon is fetched
-// successfully, it creates an representation using the app icon and web store
-// icon as a badge.
-class WebstoreResultIconSource : public gfx::ImageSkiaSource,
-                                 public net::URLFetcherDelegate,
-                                 public ImageDecoder::Delegate {
- public:
-  typedef base::Closure IconLoadedCallback;
-
-  WebstoreResultIconSource(const IconLoadedCallback& icon_loaded_callback,
-                           net::URLRequestContextGetter* context_getter,
-                           const GURL& icon_url,
-                           int icon_size);
-  virtual ~WebstoreResultIconSource();
-
- private:
-  // Invoked from GetImageForScale to download the app icon when the hosting
-  // ImageSkia gets painted on screen.
-  void StartIconFetch();
-
-  // Creates the result icon by putting a small web store icon on the bottom
-  // right corner as a badge.
-  gfx::ImageSkiaRep CreateBadgedIcon(ui::ScaleFactor scale_factor);
-
-  // gfx::ImageSkiaSource overrides:
-  virtual gfx::ImageSkiaRep GetImageForScale(
-      ui::ScaleFactor scale_factor) OVERRIDE;
-
-  // net::URLFetcherDelegate overrides:
-  virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
-
-  // ImageDecoder::Delegate overrides:
-  virtual void OnImageDecoded(const ImageDecoder* decoder,
-                              const SkBitmap& decoded_image) OVERRIDE;
-  virtual void OnDecodeImageFailed(const ImageDecoder* decoder) OVERRIDE;
-
-  IconLoadedCallback icon_loaded_callback_;
-  net::URLRequestContextGetter* context_getter_;
-  const GURL icon_url_;
-  const int icon_size_;
-
-  bool icon_fetch_attempted_;
-  scoped_ptr<net::URLFetcher> icon_fetcher_;
-
-  scoped_refptr<ImageDecoder> image_decoder_;
-
-  gfx::ImageSkia icon_;
-
-  DISALLOW_COPY_AND_ASSIGN(WebstoreResultIconSource);
-};
-
-}  // namespace app_list
-
-#endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_WEBSTORE_RESULT_ICON_SOURCE_H_
diff --git a/chrome/browser/ui/app_list/search/webstore_search_fetcher.cc b/chrome/browser/ui/app_list/search/webstore_search_fetcher.cc
deleted file mode 100644
index 27dff35..0000000
--- a/chrome/browser/ui/app_list/search/webstore_search_fetcher.cc
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/app_list/search/webstore_search_fetcher.h"
-
-#include "base/bind.h"
-#include "base/values.h"
-#include "chrome/browser/safe_json_parser.h"
-#include "chrome/common/extensions/extension_constants.h"
-#include "net/base/load_flags.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_status.h"
-
-namespace app_list {
-
-const char kBadResponse[] = "Bad Web Store search response";
-
-WebstoreSearchFetcher::WebstoreSearchFetcher(
-    const Callback& callback,
-    net::URLRequestContextGetter* context_getter)
-    : callback_(callback),
-      context_getter_(context_getter),
-      weak_factory_(this) {
-  DCHECK(!callback_.is_null());
-}
-
-WebstoreSearchFetcher::~WebstoreSearchFetcher() {}
-
-void WebstoreSearchFetcher::Start(const std::string& query,
-                                  const std::string& hl) {
-  Stop();
-
-  fetcher_.reset(net::URLFetcher::Create(
-      extension_urls::GetWebstoreJsonSearchUrl(query, hl),
-      net::URLFetcher::GET,
-      this));
-  fetcher_->SetRequestContext(context_getter_);
-  fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
-                         net::LOAD_DISABLE_CACHE);
-  fetcher_->Start();
-}
-
-void WebstoreSearchFetcher::Stop() {
-  fetcher_.reset();
-  weak_factory_.InvalidateWeakPtrs();
-}
-
-void WebstoreSearchFetcher::OnJsonParseSuccess(
-    scoped_ptr<base::Value> parsed_json) {
-  if (!parsed_json->IsType(base::Value::TYPE_DICTIONARY)) {
-    OnJsonParseError(kBadResponse);
-    return;
-  }
-
-  callback_.Run(make_scoped_ptr(
-      static_cast<base::DictionaryValue*>(parsed_json.release())));
-}
-
-void WebstoreSearchFetcher::OnJsonParseError(const std::string& error) {
-  callback_.Run(scoped_ptr<base::DictionaryValue>());
-}
-
-void WebstoreSearchFetcher::OnURLFetchComplete(const net::URLFetcher* source) {
-  CHECK_EQ(fetcher_.get(), source);
-
-  scoped_ptr<net::URLFetcher> fetcher(fetcher_.Pass());
-
-  if (!fetcher->GetStatus().is_success() ||
-      fetcher->GetResponseCode() != 200) {
-    OnJsonParseError(kBadResponse);
-    return;
-  }
-
-  std::string webstore_json_data;
-  fetcher->GetResponseAsString(&webstore_json_data);
-
-  scoped_refptr<SafeJsonParser> parser =
-      new SafeJsonParser(webstore_json_data,
-                         base::Bind(&WebstoreSearchFetcher::OnJsonParseSuccess,
-                                    weak_factory_.GetWeakPtr()),
-                         base::Bind(&WebstoreSearchFetcher::OnJsonParseError,
-                                    weak_factory_.GetWeakPtr()));
-  // The parser will call us back via one of the callbacks.
-  parser->Start();
-}
-
-}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/webstore_search_fetcher.h b/chrome/browser/ui/app_list/search/webstore_search_fetcher.h
deleted file mode 100644
index 442c196..0000000
--- a/chrome/browser/ui/app_list/search/webstore_search_fetcher.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_WEBSTORE_SEARCH_FETCHER_H_
-#define CHROME_BROWSER_UI_APP_LIST_SEARCH_WEBSTORE_SEARCH_FETCHER_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/callback.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "net/url_request/url_fetcher_delegate.h"
-
-namespace base {
-class DictionaryValue;
-class Value;
-}
-
-namespace net {
-class URLFetcher;
-class URLRequestContextGetter;
-}
-
-namespace app_list {
-
-// A class to fetch web store search result.
-class WebstoreSearchFetcher : public net::URLFetcherDelegate {
- public:
-  // Callback to pass back the parsed json dictionary returned from the server.
-  // Invoked with NULL if there is an error.
-  typedef base::Callback<void(scoped_ptr<base::DictionaryValue>)> Callback;
-
-  WebstoreSearchFetcher(const Callback& callback,
-                        net::URLRequestContextGetter* context_getter);
-  virtual ~WebstoreSearchFetcher();
-
-  // Starts to fetch results for the given |query| and the language code |hl|.
-  void Start(const std::string& query, const std::string& hl);
-  void Stop();
-
- private:
-  // Callbacks for SafeJsonParser.
-  void OnJsonParseSuccess(scoped_ptr<base::Value> parsed_json);
-  void OnJsonParseError(const std::string& error);
-
-  // net::URLFetcherDelegate overrides:
-  virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
-
-  Callback callback_;
-  net::URLRequestContextGetter* context_getter_;
-
-  scoped_ptr<net::URLFetcher> fetcher_;
-  base::WeakPtrFactory<WebstoreSearchFetcher> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(WebstoreSearchFetcher);
-};
-
-}  // namespace app_list
-
-#endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_WEBSTORE_SEARCH_FETCHER_H_
diff --git a/chrome/browser/ui/app_modal_dialogs/javascript_dialog_manager.cc b/chrome/browser/ui/app_modal_dialogs/javascript_dialog_manager.cc
index 682a2d9..37e6859 100644
--- a/chrome/browser/ui/app_modal_dialogs/javascript_dialog_manager.cc
+++ b/chrome/browser/ui/app_modal_dialogs/javascript_dialog_manager.cc
@@ -47,6 +47,7 @@
       content::JavaScriptMessageType message_type,
       const string16& message_text,
       const string16& default_prompt_text,
+      bool user_gesture,
       const DialogClosedCallback& callback,
       bool* did_suppress_message) OVERRIDE;
 
@@ -130,6 +131,7 @@
     content::JavaScriptMessageType message_type,
     const string16& message_text,
     const string16& default_prompt_text,
+    bool user_gesture,
     const DialogClosedCallback& callback,
     bool* did_suppress_message)  {
   *did_suppress_message = false;
@@ -146,10 +148,11 @@
       extra_data->last_javascript_message_dismissal_;
   bool display_suppress_checkbox = false;
   // Show a checkbox offering to suppress further messages if this message is
-  // being displayed within kJavaScriptMessageExpectedDelay of the last one.
+  // being displayed within kJavaScriptMessageExpectedDelay of the last one and
+  // the dialog was not requested via an explicit user gesture.
   if (time_since_last_message <
       base::TimeDelta::FromMilliseconds(
-          chrome::kJavaScriptMessageExpectedDelay)) {
+          chrome::kJavaScriptMessageExpectedDelay) && !user_gesture) {
     display_suppress_checkbox = true;
   } else {
     display_suppress_checkbox = false;
diff --git a/chrome/browser/ui/ash/app_list/app_list_controller_ash.cc b/chrome/browser/ui/ash/app_list/app_list_controller_ash.cc
index 7bf0587..0beb95f 100644
--- a/chrome/browser/ui/ash/app_list/app_list_controller_ash.cc
+++ b/chrome/browser/ui/ash/app_list/app_list_controller_ash.cc
@@ -32,7 +32,7 @@
 }
 
 void AppListControllerDelegateAsh::UnpinApp(const std::string& extension_id) {
-  ChromeLauncherController::instance()->UnpinAppsWithID(extension_id);
+  ChromeLauncherController::instance()->UnpinAppWithID(extension_id);
 }
 
 bool AppListControllerDelegateAsh::CanPin() {
diff --git a/chrome/browser/ui/ash/app_list/app_list_service_ash.cc b/chrome/browser/ui/ash/app_list/app_list_service_ash.cc
index 1e9d69f..232e1b1 100644
--- a/chrome/browser/ui/ash/app_list/app_list_service_ash.cc
+++ b/chrome/browser/ui/ash/app_list/app_list_service_ash.cc
@@ -9,6 +9,7 @@
 #include "base/memory/singleton.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_list_service_impl.h"
+#include "chrome/browser/ui/ash/app_list/app_list_controller_ash.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 
 namespace {
@@ -34,6 +35,7 @@
   virtual void DismissAppList() OVERRIDE;
   virtual void EnableAppList(Profile* initial_profile) OVERRIDE;
   virtual gfx::NativeWindow GetAppListWindow() OVERRIDE;
+  virtual AppListControllerDelegate* CreateControllerDelegate() OVERRIDE;
 
   DISALLOW_COPY_AND_ASSIGN(AppListServiceAsh);
 };
@@ -70,6 +72,10 @@
   return NULL;
 }
 
+AppListControllerDelegate* AppListServiceAsh::CreateControllerDelegate() {
+  return new AppListControllerDelegateAsh();
+}
+
 }  // namespace
 
 namespace chrome {
diff --git a/chrome/browser/ui/ash/launcher/browser_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/browser_launcher_item_controller.cc
deleted file mode 100644
index 04668bb..0000000
--- a/chrome/browser/ui/ash/launcher/browser_launcher_item_controller.cc
+++ /dev/null
@@ -1,268 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/ash/launcher/browser_launcher_item_controller.h"
-
-#include "ash/launcher/launcher.h"
-#include "ash/launcher/launcher_model.h"
-#include "ash/shell.h"
-#include "ash/wm/window_util.h"
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/tab_helper.h"
-#include "chrome/browser/favicon/favicon_tab_helper.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
-#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/web_applications/web_app.h"
-#include "chrome/common/extensions/manifest_handlers/icons_handler.h"
-#include "content/public/browser/web_contents.h"
-#include "grit/ui_resources.h"
-#include "ui/aura/client/aura_constants.h"
-#include "ui/aura/window.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/views/widget/widget.h"
-
-BrowserLauncherItemController::BrowserLauncherItemController(
-    Type type,
-    aura::Window* window,
-    TabStripModel* tab_model,
-    ChromeLauncherController* launcher_controller,
-    const std::string& app_id)
-    : LauncherItemController(type, app_id, launcher_controller),
-      window_(window),
-      tab_model_(tab_model),
-      is_incognito_(tab_model->profile()->GetOriginalProfile() !=
-                    tab_model->profile() &&
-                    !tab_model->profile()->IsGuestSession()) {
-  DCHECK(window_);
-  window_->AddObserver(this);
-}
-
-BrowserLauncherItemController::~BrowserLauncherItemController() {
-  tab_model_->RemoveObserver(this);
-  window_->RemoveObserver(this);
-  if (launcher_id() > 0)
-    launcher_controller()->CloseLauncherItem(launcher_id());
-  if (type() == TYPE_WINDOWED_APP)
-    launcher_controller()->UnlockV1AppWithID(LauncherItemController::app_id());
-}
-
-const std::string& BrowserLauncherItemController::app_id() const {
-  if (type() == TYPE_WINDOWED_APP)
-    return empty_app_id_;
-  return LauncherItemController::app_id();
-}
-
-void BrowserLauncherItemController::Init() {
-  tab_model_->AddObserver(this);
-  ash::LauncherItemStatus app_status =
-      ash::wm::IsActiveWindow(window_) ?
-      ash::STATUS_ACTIVE : ash::STATUS_RUNNING;
-  if (type() != TYPE_TABBED && type() != TYPE_WINDOWED_APP)
-    launcher_controller()->CreateAppLauncherItem(this, app_id(), app_status);
-  else if (type() == TYPE_WINDOWED_APP)
-    launcher_controller()->LockV1AppWithID(LauncherItemController::app_id());
-
-  // In testing scenarios we can get tab strips with no active contents.
-  if (tab_model_->active_index() != TabStripModel::kNoTab)
-    UpdateLauncher(tab_model_->GetActiveWebContents());
-}
-
-// static
-BrowserLauncherItemController* BrowserLauncherItemController::Create(
-    Browser* browser) {
-  // Under testing this can be called before the controller is created.
-  if (!ChromeLauncherController::instance())
-    return NULL;
-
-  Type type;
-  std::string app_id;
-  if (browser->is_type_tabbed() || browser->is_type_popup()) {
-    type = TYPE_TABBED;
-    if (!browser->is_type_tabbed() &&
-        browser->is_type_popup() &&
-        browser->is_app()) {
-      app_id = web_app::GetExtensionIdFromApplicationName(
-                    browser->app_name());
-      // Only allow this for known applications. Some unit tests for example
-      // do not have one.
-      if (!app_id.empty())
-        type = TYPE_WINDOWED_APP;
-    }
-  } else if (browser->is_app()) {
-    type = TYPE_TABBED;
-    app_id = web_app::GetExtensionIdFromApplicationName(browser->app_name());
-  } else {
-    return NULL;
-  }
-  BrowserLauncherItemController* controller =
-      new BrowserLauncherItemController(type,
-                                        browser->window()->GetNativeWindow(),
-                                        browser->tab_strip_model(),
-                                        ChromeLauncherController::instance(),
-                                        app_id);
-  controller->Init();
-  return controller;
-}
-
-void BrowserLauncherItemController::BrowserActivationStateChanged() {
-  content::WebContents* active_contents = tab_model_->GetActiveWebContents();
-  if (active_contents)
-    UpdateAppState(active_contents);
-  UpdateItemStatus();
-}
-
-string16 BrowserLauncherItemController::GetTitle() {
-  if (type() == TYPE_TABBED) {
-    if (tab_model_->active_index() != TabStripModel::kNoTab) {
-      const content::WebContents* contents = tab_model_->GetActiveWebContents();
-      if (contents)
-        return contents->GetTitle();
-    }
-  }
-  return GetAppTitle();
-}
-
-bool BrowserLauncherItemController::IsCurrentlyShownInWindow(
-    aura::Window* window) const {
-  return window_ == window;
-}
-
-bool BrowserLauncherItemController::IsOpen() const {
-  return true;
-}
-
-bool BrowserLauncherItemController::IsVisible() const {
-  return window_->IsVisible();
-}
-
-void BrowserLauncherItemController::Launch(int event_flags) {
-  DCHECK(!app_id().empty());
-  launcher_controller()->LaunchApp(app_id(), event_flags);
-}
-
-void BrowserLauncherItemController::Activate() {
-  window_->Show();
-  ash::wm::ActivateWindow(window_);
-}
-
-void BrowserLauncherItemController::Close() {
-  views::Widget* widget = views::Widget::GetWidgetForNativeView(window_);
-  if (widget)
-    widget->Close();
-}
-
-void BrowserLauncherItemController::Clicked(const ui::Event& event) {
-  views::Widget* widget =
-      views::Widget::GetWidgetForNativeView(window_);
-  if (widget && widget->IsActive()) {
-    widget->Minimize();
-  } else {
-    Activate();
-  }
-}
-
-void BrowserLauncherItemController::OnRemoved() {
-}
-
-void BrowserLauncherItemController::LauncherItemChanged(
-    int index,
-    const ash::LauncherItem& old_item) {
-}
-
-ChromeLauncherAppMenuItems
-BrowserLauncherItemController::GetApplicationList(int event_flags) {
-  // This will never be called and the entire class will go away.
-  ChromeLauncherAppMenuItems items;
-  return items.Pass();
-}
-
-void BrowserLauncherItemController::ActiveTabChanged(
-    content::WebContents* old_contents,
-    content::WebContents* new_contents,
-    int index,
-    int reason) {
-  // Update immediately on a tab change.
-  if (old_contents &&
-      TabStripModel::kNoTab !=
-          tab_model_->GetIndexOfWebContents(old_contents))
-    UpdateAppState(old_contents);
-  UpdateAppState(new_contents);
-  UpdateLauncher(new_contents);
-}
-
-void BrowserLauncherItemController::TabInsertedAt(
-    content::WebContents* contents,
-    int index,
-    bool foreground) {
-  UpdateAppState(contents);
-}
-
-void BrowserLauncherItemController::TabDetachedAt(
-    content::WebContents* contents,
-    int index) {
-  launcher_controller()->UpdateAppState(
-      contents, ChromeLauncherController::APP_STATE_REMOVED);
-}
-
-void BrowserLauncherItemController::TabChangedAt(
-    content::WebContents* contents,
-    int index,
-    TabStripModelObserver::TabChangeType change_type) {
-  UpdateAppState(contents);
-  if (index != tab_model_->active_index() ||
-      !(change_type != TabStripModelObserver::LOADING_ONLY &&
-        change_type != TabStripModelObserver::TITLE_NOT_LOADING)) {
-    return;
-  }
-
-  UpdateLauncher(contents);
-}
-
-void BrowserLauncherItemController::TabReplacedAt(
-    TabStripModel* tab_strip_model,
-    content::WebContents* old_contents,
-    content::WebContents* new_contents,
-    int index) {
-  launcher_controller()->UpdateAppState(
-      old_contents,
-      ChromeLauncherController::APP_STATE_REMOVED);
-  UpdateAppState(new_contents);
-}
-
-void BrowserLauncherItemController::OnWindowPropertyChanged(
-    aura::Window* window,
-    const void* key,
-    intptr_t old) {
-  if (key == aura::client::kDrawAttentionKey)
-    UpdateItemStatus();
-}
-
-void BrowserLauncherItemController::UpdateItemStatus() {
-}
-
-void BrowserLauncherItemController::UpdateLauncher(content::WebContents* tab) {
-}
-
-void BrowserLauncherItemController::UpdateAppState(content::WebContents* tab) {
-  ChromeLauncherController::AppState app_state;
-
-  if (tab_model_->GetActiveWebContents() == tab) {
-    if (ash::wm::IsActiveWindow(window_))
-      app_state = ChromeLauncherController::APP_STATE_WINDOW_ACTIVE;
-    else
-      app_state = ChromeLauncherController::APP_STATE_ACTIVE;
-  } else {
-    app_state = ChromeLauncherController::APP_STATE_INACTIVE;
-  }
-  launcher_controller()->UpdateAppState(tab, app_state);
-}
-
-ash::LauncherModel* BrowserLauncherItemController::launcher_model() {
-  return launcher_controller()->model();
-}
diff --git a/chrome/browser/ui/ash/launcher/browser_launcher_item_controller.h b/chrome/browser/ui/ash/launcher/browser_launcher_item_controller.h
deleted file mode 100644
index 51fbc33..0000000
--- a/chrome/browser/ui/ash/launcher/browser_launcher_item_controller.h
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_BROWSER_LAUNCHER_ITEM_CONTROLLER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_BROWSER_LAUNCHER_ITEM_CONTROLLER_H_
-
-#include <string>
-
-#include "ash/launcher/launcher_types.h"
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "base/gtest_prod_util.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/strings/string16.h"
-#include "chrome/browser/ui/ash/launcher/launcher_favicon_loader.h"
-#include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
-#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
-#include "ui/aura/window_observer.h"
-
-class Browser;
-
-namespace ash {
-class LauncherModel;
-}
-
-// BrowserLauncherItemController is responsible for keeping the launcher
-// representation of a window up to date as the active tab changes.
-class BrowserLauncherItemController : public LauncherItemController,
-                                      public TabStripModelObserver,
-                                      public aura::WindowObserver {
- public:
-  // This API is to be used as part of testing only.
-  class TestApi {
-   public:
-    explicit TestApi(BrowserLauncherItemController* controller)
-        : controller_(controller) {}
-    ~TestApi() {}
-
-    // Returns the launcher id for the browser window.
-    ash::LauncherID item_id() const { return controller_->launcher_id(); }
-
-   private:
-    BrowserLauncherItemController* controller_;
-  };
-
-  BrowserLauncherItemController(Type type,
-                                aura::Window* window,
-                                TabStripModel* tab_model,
-                                ChromeLauncherController* launcher_controller,
-                                const std::string& app_id);
-  virtual ~BrowserLauncherItemController();
-
-  // Overriding the app id for V1 apps.
-  virtual const std::string& app_id() const OVERRIDE;
-
-  // Sets up this BrowserLauncherItemController.
-  void Init();
-
-  // Creates and returns a new BrowserLauncherItemController for |browser|. This
-  // returns NULL if a BrowserLauncherItemController is not needed for the
-  // specified browser.
-  static BrowserLauncherItemController* Create(Browser* browser);
-
-  // Call to indicate that the window the tabcontents are in has changed its
-  // activation state.
-  void BrowserActivationStateChanged();
-
-  // LauncherItemController overrides:
-  virtual string16 GetTitle() OVERRIDE;
-  virtual bool IsCurrentlyShownInWindow(aura::Window* window) const OVERRIDE;
-  virtual bool IsOpen() const OVERRIDE;
-  virtual bool IsVisible() const OVERRIDE;
-  virtual void Launch(int event_flags) OVERRIDE;
-  virtual void Activate() OVERRIDE;
-  virtual void Close() OVERRIDE;
-  virtual void Clicked(const ui::Event& event) OVERRIDE;
-  virtual void OnRemoved() OVERRIDE;
-  virtual void LauncherItemChanged(int index,
-                                   const ash::LauncherItem& old_item) OVERRIDE;
-  virtual ChromeLauncherAppMenuItems GetApplicationList(
-      int event_flags) OVERRIDE;
-
-  // TabStripModel overrides:
-  virtual void ActiveTabChanged(content::WebContents* old_contents,
-                                content::WebContents* new_contents,
-                                int index,
-                                int reason) OVERRIDE;
-  virtual void TabInsertedAt(content::WebContents* contents,
-                             int index,
-                             bool foreground) OVERRIDE;
-  virtual void TabDetachedAt(content::WebContents* contents,
-                             int index) OVERRIDE;
-  virtual void TabChangedAt(
-      content::WebContents* contents,
-      int index,
-      TabStripModelObserver::TabChangeType change_type) OVERRIDE;
-  virtual void TabReplacedAt(TabStripModel* tab_strip_model,
-                             content::WebContents* old_contents,
-                             content::WebContents* new_contents,
-                             int index) OVERRIDE;
-
-  // aura::WindowObserver overrides:
-  virtual void OnWindowPropertyChanged(aura::Window* window,
-                                       const void* key,
-                                       intptr_t old) OVERRIDE;
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(BrowserLauncherItemControllerTest, PanelItem);
-
-  // Used to identify what an update corresponds to.
-  enum UpdateType {
-    UPDATE_TAB_REMOVED,
-    UPDATE_TAB_CHANGED,
-    UPDATE_TAB_INSERTED,
-  };
-
-  // Updates the launcher item status base on the activation and attention
-  // state of the window.
-  void UpdateItemStatus();
-
-  // Updates the launcher from |tab|.
-  void UpdateLauncher(content::WebContents* tab);
-
-  void UpdateAppState(content::WebContents* tab);
-
-  ash::LauncherModel* launcher_model();
-
-  // Browser window we're in.
-  aura::Window* window_;
-
-  // If running a windowed V1 app with the new launcher, this (empty) app id
-  // will be returned by app_id().
-  std::string empty_app_id_;
-
-  TabStripModel* tab_model_;
-
-  // Whether this is associated with an incognito profile.
-  const bool is_incognito_;
-
-  DISALLOW_COPY_AND_ASSIGN(BrowserLauncherItemController);
-};
-
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_BROWSER_LAUNCHER_ITEM_CONTROLLER_H_
diff --git a/chrome/browser/ui/ash/launcher/browser_status_monitor.cc b/chrome/browser/ui/ash/launcher/browser_status_monitor.cc
new file mode 100644
index 0000000..5636e70
--- /dev/null
+++ b/chrome/browser/ui/ash/launcher/browser_status_monitor.cc
@@ -0,0 +1,201 @@
+// 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 "chrome/browser/ui/ash/launcher/browser_status_monitor.h"
+
+#include "ash/shell.h"
+#include "ash/wm/window_util.h"
+#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/web_applications/web_app.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_view.h"
+#include "ui/aura/client/activation_client.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/window.h"
+#include "ui/gfx/screen.h"
+
+BrowserStatusMonitor::BrowserStatusMonitor(
+    ChromeLauncherController* launcher_controller)
+    : launcher_controller_(launcher_controller),
+      observed_activation_clients_(this),
+      observed_root_windows_(this) {
+  DCHECK(launcher_controller_);
+  BrowserList* browser_list =
+      BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
+
+  browser_list->AddObserver(this);
+
+  // This check needs for win7_aura. Without this, all tests in
+  // ChromeLauncherController will fail in win7_aura.
+  if (ash::Shell::HasInstance()) {
+    // We can't assume all RootWindows have the same ActivationClient.
+    // Add a RootWindow and its ActivationClient to the observed list.
+    ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows();
+    ash::Shell::RootWindowList::const_iterator iter = root_windows.begin();
+    for (; iter != root_windows.end(); ++iter) {
+      // |observed_activation_clients_| can have the same activation client
+      // multiple times - which would be handled by the used
+      // |ScopedObserverWithDuplicatedSources|.
+      observed_activation_clients_.Add(
+          aura::client::GetActivationClient(*iter));
+      observed_root_windows_.Add(static_cast<aura::Window*>(*iter));
+    }
+    ash::Shell::GetInstance()->GetScreen()->AddObserver(this);
+  }
+}
+
+BrowserStatusMonitor::~BrowserStatusMonitor() {
+  // This check needs for win7_aura. Without this, all tests in
+  // ChromeLauncherController will fail in win7_aura.
+  if (ash::Shell::HasInstance())
+    ash::Shell::GetInstance()->GetScreen()->RemoveObserver(this);
+
+  BrowserList* browser_list =
+      BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
+
+  browser_list->RemoveObserver(this);
+}
+
+void BrowserStatusMonitor::OnWindowActivated(aura::Window* gained_active,
+                                             aura::Window* lost_active) {
+  Browser* browser = chrome::FindBrowserWithWindow(lost_active);
+  content::WebContents* active_contents = NULL;
+
+  if (browser) {
+    active_contents = browser->tab_strip_model()->GetActiveWebContents();
+    if (active_contents)
+      UpdateAppState(active_contents);
+  }
+
+  browser = chrome::FindBrowserWithWindow(gained_active);
+  if (browser) {
+    active_contents = browser->tab_strip_model()->GetActiveWebContents();
+    if (active_contents)
+      UpdateAppState(active_contents);
+  }
+}
+
+void BrowserStatusMonitor::OnWindowDestroyed(aura::Window* window) {
+  // Remove RootWindow and its ActivationClient from observed list.
+  observed_root_windows_.Remove(window);
+  observed_activation_clients_.Remove(aura::client::GetActivationClient(
+      static_cast<aura::RootWindow*>(window)));
+}
+
+void BrowserStatusMonitor::OnBrowserAdded(Browser* browser) {
+  browser->tab_strip_model()->AddObserver(this);
+
+  if (browser->is_type_popup() && browser->is_app()) {
+    std::string app_id =
+        web_app::GetExtensionIdFromApplicationName(browser->app_name());
+    if (!app_id.empty()) {
+      browser_to_app_id_map_[browser] = app_id;
+      launcher_controller_->LockV1AppWithID(app_id);
+    }
+  }
+}
+
+void BrowserStatusMonitor::OnBrowserRemoved(Browser* browser) {
+  browser->tab_strip_model()->RemoveObserver(this);
+
+  if (browser_to_app_id_map_.find(browser) != browser_to_app_id_map_.end()) {
+    launcher_controller_->UnlockV1AppWithID(browser_to_app_id_map_[browser]);
+    browser_to_app_id_map_.erase(browser);
+  }
+  launcher_controller_->UpdateBrowserItemStatus();
+}
+
+void BrowserStatusMonitor::OnDisplayBoundsChanged(
+    const gfx::Display& display) {
+  // Do nothing here.
+}
+
+void BrowserStatusMonitor::OnDisplayAdded(const gfx::Display& new_display) {
+  // Add a new RootWindow and its ActivationClient to observed list.
+  aura::RootWindow* root_window = ash::Shell::GetInstance()->
+      display_controller()->GetRootWindowForDisplayId(new_display.id());
+  // When the primary root window's display get removed, the existing root
+  // window is taken over by the new display and the observer is already set.
+  if (!observed_root_windows_.IsObserving(root_window)) {
+    observed_root_windows_.Add(static_cast<aura::Window*>(root_window));
+    observed_activation_clients_.Add(
+        aura::client::GetActivationClient(root_window));
+  }
+}
+
+void BrowserStatusMonitor::OnDisplayRemoved(const gfx::Display& old_display) {
+  // When this is called, RootWindow of |old_display| is already removed.
+  // Instead, we can remove RootWindow and its ActivationClient in the
+  // OnWindowRemoved().
+  // Do nothing here.
+}
+
+void BrowserStatusMonitor::ActiveTabChanged(content::WebContents* old_contents,
+                                            content::WebContents* new_contents,
+                                            int index,
+                                            int reason) {
+  Browser* browser = NULL;
+  if (old_contents)
+    browser = chrome::FindBrowserWithWebContents(old_contents);
+
+  // Update immediately on a tab change.
+  if (browser &&
+      (TabStripModel::kNoTab !=
+           browser->tab_strip_model()->GetIndexOfWebContents(old_contents)))
+    UpdateAppState(old_contents);
+
+  UpdateAppState(new_contents);
+}
+
+void BrowserStatusMonitor::TabInsertedAt(content::WebContents* contents,
+                                         int index,
+                                         bool foreground) {
+  UpdateAppState(contents);
+}
+
+void BrowserStatusMonitor::TabDetachedAt(content::WebContents* contents,
+                                         int index) {
+  launcher_controller_->UpdateAppState(
+      contents, ChromeLauncherController::APP_STATE_REMOVED);
+}
+
+void BrowserStatusMonitor::TabChangedAt(
+    content::WebContents* contents,
+    int index,
+    TabStripModelObserver::TabChangeType change_type) {
+  UpdateAppState(contents);
+}
+
+void BrowserStatusMonitor::TabReplacedAt(TabStripModel* tab_strip_model,
+                                         content::WebContents* old_contents,
+                                         content::WebContents* new_contents,
+                                         int index) {
+  launcher_controller_->UpdateAppState(
+      old_contents,
+      ChromeLauncherController::APP_STATE_REMOVED);
+  UpdateAppState(new_contents);
+}
+
+void BrowserStatusMonitor::UpdateAppState(content::WebContents* contents) {
+  if (!contents)
+    return;
+
+  ChromeLauncherController::AppState app_state =
+      ChromeLauncherController::APP_STATE_INACTIVE;
+
+  Browser* browser = chrome::FindBrowserWithWebContents(contents);
+  if (browser->tab_strip_model()->GetActiveWebContents() == contents) {
+    if (browser->window()->IsActive())
+      app_state = ChromeLauncherController::APP_STATE_WINDOW_ACTIVE;
+    else
+      app_state = ChromeLauncherController::APP_STATE_ACTIVE;
+  }
+
+  launcher_controller_->UpdateAppState(contents, app_state);
+}
diff --git a/chrome/browser/ui/ash/launcher/browser_status_monitor.h b/chrome/browser/ui/ash/launcher/browser_status_monitor.h
new file mode 100644
index 0000000..50840b4
--- /dev/null
+++ b/chrome/browser/ui/ash/launcher/browser_status_monitor.h
@@ -0,0 +1,99 @@
+// 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 CHROME_BROWSER_UI_ASH_LAUNCHER_BROWSER_STATUS_MONITOR_H_
+#define CHROME_BROWSER_UI_ASH_LAUNCHER_BROWSER_STATUS_MONITOR_H_
+
+#include <map>
+#include <string>
+
+#include "ash/shelf/scoped_observer_with_duplicated_sources.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/ui/browser_list_observer.h"
+#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
+#include "ui/aura/client/activation_change_observer.h"
+#include "ui/aura/window_observer.h"
+#include "ui/gfx/display_observer.h"
+
+namespace aura {
+class Window;
+
+namespace client {
+class ActivationClient;
+}
+}  // namespace aura
+
+class Browser;
+class ChromeLauncherController;
+
+// BrowserStatusMonitor monitors creation/deletion of Browser and its
+// TabStripModel to keep the launcher representation up to date as the
+// active tab changes.
+class BrowserStatusMonitor : public aura::client::ActivationChangeObserver,
+                             public aura::WindowObserver,
+                             public chrome::BrowserListObserver,
+                             public gfx::DisplayObserver,
+                             public TabStripModelObserver {
+ public:
+  explicit BrowserStatusMonitor(ChromeLauncherController* launcher_controller);
+  virtual ~BrowserStatusMonitor();
+
+  // aura::client::ActivationChangeObserver overrides:
+  virtual void OnWindowActivated(aura::Window* gained_active,
+                                 aura::Window* lost_active) OVERRIDE;
+
+  // aura::WindowObserver overrides:
+  virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
+
+  // chrome::BrowserListObserver overrides:
+  virtual void OnBrowserAdded(Browser* browser) OVERRIDE;
+  virtual void OnBrowserRemoved(Browser* browser) OVERRIDE;
+
+  // gfx::DisplayObserver overrides:
+  virtual void OnDisplayBoundsChanged(const gfx::Display& display) OVERRIDE;
+  virtual void OnDisplayAdded(const gfx::Display& new_display) OVERRIDE;
+  virtual void OnDisplayRemoved(const gfx::Display& old_display) OVERRIDE;
+
+  // TabStripModelObserver overrides:
+  virtual void ActiveTabChanged(content::WebContents* old_contents,
+                                content::WebContents* new_contents,
+                                int index,
+                                int reason) OVERRIDE;
+  virtual void TabInsertedAt(content::WebContents* contents,
+                             int index,
+                             bool foreground) OVERRIDE;
+  virtual void TabDetachedAt(content::WebContents* contents,
+                             int index) OVERRIDE;
+  virtual void TabChangedAt(
+      content::WebContents* contents,
+      int index,
+      TabStripModelObserver::TabChangeType change_type) OVERRIDE;
+  virtual void TabReplacedAt(TabStripModel* tab_strip_model,
+                             content::WebContents* old_contents,
+                             content::WebContents* new_contents,
+                             int index) OVERRIDE;
+
+ private:
+  typedef std::map<Browser*, std::string> BrowserToAppIDMap;
+
+  // Update app state for |contents|.
+  void UpdateAppState(content::WebContents* contents);
+
+  ChromeLauncherController* launcher_controller_;
+
+  // Hold all observed activation clients.
+  ScopedObserverWithDuplicatedSources<aura::client::ActivationClient,
+      aura::client::ActivationChangeObserver> observed_activation_clients_;
+
+  // Hold all observed root windows.
+  ScopedObserver<aura::Window, aura::WindowObserver> observed_root_windows_;
+
+  BrowserToAppIDMap browser_to_app_id_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowserStatusMonitor);
+};
+
+#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_BROWSER_STATUS_MONITOR_H_
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
index 9f1759e..9b0bc44 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -8,6 +8,7 @@
 
 #include "ash/ash_switches.h"
 #include "ash/launcher/launcher.h"
+#include "ash/launcher/launcher_item_delegate_manager.h"
 #include "ash/launcher/launcher_model.h"
 #include "ash/launcher/launcher_util.h"
 #include "ash/root_window_controller.h"
@@ -35,6 +36,7 @@
 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
 #include "chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h"
 #include "chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h"
+#include "chrome/browser/ui/ash/launcher/browser_status_monitor.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_browser.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.h"
@@ -207,8 +209,8 @@
       app_sync_ui_state_->AddObserver(this);
   }
 
+  browser_status_monitor_.reset(new BrowserStatusMonitor(this));
   model_->AddObserver(this);
-  BrowserList::AddObserver(this);
   // Right now ash::Shell isn't created for tests.
   // TODO(mukai): Allows it to observe display change and write tests.
   if (ash::Shell::HasInstance())
@@ -243,6 +245,11 @@
       prefs::kShelfPreferences,
       base::Bind(&ChromeLauncherController::SetShelfBehaviorsFromPrefs,
                  base::Unretained(this)));
+
+  // This check is needed for win7_aura. Without this, all tests in
+  // ChromeLauncherControllerTest will fail by win7_aura.
+  if (ash::Shell::HasInstance())
+    RegisterLauncherItemDelegate();
 }
 
 ChromeLauncherController::~ChromeLauncherController() {
@@ -255,15 +262,11 @@
     (*iter)->shelf_widget()->shelf_layout_manager()->RemoveObserver(this);
 
   model_->RemoveObserver(this);
-  BrowserList::RemoveObserver(this);
   if (ash::Shell::HasInstance())
     ash::Shell::GetInstance()->display_controller()->RemoveObserver(this);
   for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
        i != id_to_item_controller_map_.end(); ++i) {
     i->second->OnRemoved();
-    // TODO(skuhne): After getting rid of the old launcher, get also rid of the
-    // BrowserLauncherItemController (since it is only used for activation
-    // tracking at that point.
     int index = model_->ItemIndexByID(i->first);
     // A "browser proxy" is not known to the model and this removal does
     // therefore not need to be propagated to the model.
@@ -588,7 +591,7 @@
   return 0;
 }
 
-std::string ChromeLauncherController::GetAppIDForLauncherID(
+const std::string& ChromeLauncherController::GetAppIDForLauncherID(
     ash::LauncherID id) {
   CHECK(HasItemController(id));
   return id_to_item_controller_map_[id]->app_id();
@@ -597,7 +600,6 @@
 void ChromeLauncherController::SetAppImage(const std::string& id,
                                            const gfx::ImageSkia& image) {
   // TODO: need to get this working for shortcuts.
-
   for (IDToItemControllerMap::const_iterator i =
            id_to_item_controller_map_.begin();
        i != id_to_item_controller_map_.end(); ++i) {
@@ -670,9 +672,9 @@
       id_to_item_controller_map_[id]->app_id(), launch_type);
 }
 
-void ChromeLauncherController::UnpinAppsWithID(const std::string& app_id) {
+void ChromeLauncherController::UnpinAppWithID(const std::string& app_id) {
   if (CanPin())
-    DoUnpinAppsWithID(app_id);
+    DoUnpinAppWithID(app_id);
   else
     NOTREACHED();
 }
@@ -1033,7 +1035,7 @@
 
       if (IsAppPinned(id)) {
         if (unload_info->reason == extension_misc::UNLOAD_REASON_UNINSTALL) {
-          DoUnpinAppsWithID(id);
+          DoUnpinAppWithID(id);
           app_icon_loader_->ClearImage(id);
         } else {
           app_icon_loader_->UpdateImage(id);
@@ -1201,13 +1203,6 @@
   return l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE);
 }
 
-void ChromeLauncherController::OnBrowserRemoved(Browser* browser) {
-  // When called by a unit test it is possible that there is no shell.
-  // In that case, the following function should not get called.
-  if (ash::Shell::HasInstance())
-    UpdateBrowserItemStatus();
-}
-
 ash::LauncherID ChromeLauncherController::CreateAppShortcutLauncherItem(
     const std::string& app_id,
     int index) {
@@ -1242,6 +1237,12 @@
 }
 
 void ChromeLauncherController::UpdateBrowserItemStatus() {
+  // This check needs for win7_aura. UpdateBrowserItemStatus() access Shell.
+  // Without this ChromeLauncherControllerTest.BrowserMenuGeneration test will
+  // fail.
+  if (!ash::Shell::HasInstance())
+    return;
+
   // Determine the new browser's active state and change if necessary.
   size_t browser_index = ash::launcher::GetBrowserItemIndex(*model_);
   DCHECK_GE(browser_index, 0u);
@@ -1319,14 +1320,10 @@
   }
 }
 
-void ChromeLauncherController::DoUnpinAppsWithID(const std::string& app_id) {
-  for (IDToItemControllerMap::iterator i = id_to_item_controller_map_.begin();
-       i != id_to_item_controller_map_.end(); ) {
-    IDToItemControllerMap::iterator current(i);
-    ++i;
-    if (current->second->app_id() == app_id && IsPinned(current->first))
-      Unpin(current->first);
-  }
+void ChromeLauncherController::DoUnpinAppWithID(const std::string& app_id) {
+  ash::LauncherID launcher_id = GetLauncherIDForAppID(app_id);
+  if (launcher_id && IsPinned(launcher_id))
+    Unpin(launcher_id);
 }
 
 void ChromeLauncherController::UpdateAppLaunchersFromPref() {
@@ -1525,7 +1522,6 @@
 
   ash::LauncherItem item;
   item.type = launcher_item_type;
-  item.is_incognito = false;
   item.image = extensions::IconsInfo::GetDefaultAppIcon();
 
   WebContents* active_tab = GetLastActiveWebContents(app_id);
@@ -1589,7 +1585,6 @@
 ash::LauncherID ChromeLauncherController::CreateBrowserShortcutLauncherItem() {
   ash::LauncherItem browser_shortcut;
   browser_shortcut.type = ash::TYPE_BROWSER_SHORTCUT;
-  browser_shortcut.is_incognito = false;
   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
   browser_shortcut.image = *rb.GetImageSkiaNamed(IDR_PRODUCT_LOGO_32);
   ash::LauncherID id = model_->next_id();
@@ -1656,3 +1651,15 @@
   base::AutoReset<bool> auto_reset(&ignore_persist_pinned_state_change_, true);
   model_->Move(source_index, target_index);
 }
+
+void ChromeLauncherController::RegisterLauncherItemDelegate() {
+  // TODO(simon.hong81): Register LauncherItemDelegate when LauncherItemDelegate
+  // is created.
+  ash::LauncherItemDelegateManager* manager =
+      ash::Shell::GetInstance()->launcher_item_delegate_manager();
+  manager->RegisterLauncherItemDelegate(ash::TYPE_APP_PANEL, this);
+  manager->RegisterLauncherItemDelegate(ash::TYPE_APP_SHORTCUT, this);
+  manager->RegisterLauncherItemDelegate(ash::TYPE_BROWSER_SHORTCUT, this);
+  manager->RegisterLauncherItemDelegate(ash::TYPE_PLATFORM_APP, this);
+  manager->RegisterLauncherItemDelegate(ash::TYPE_WINDOWED_APP, this);
+}
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
index cdf7105..4cd78e9 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h
@@ -13,6 +13,7 @@
 
 #include "ash/display/display_controller.h"
 #include "ash/launcher/launcher_delegate.h"
+#include "ash/launcher/launcher_item_delegate.h"
 #include "ash/launcher/launcher_model_observer.h"
 #include "ash/launcher/launcher_types.h"
 #include "ash/shelf/shelf_layout_manager_observer.h"
@@ -28,7 +29,6 @@
 #include "chrome/browser/prefs/pref_service_syncable_observer.h"
 #include "chrome/browser/ui/ash/app_sync_ui_state_observer.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
-#include "chrome/browser/ui/browser_list_observer.h"
 #include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h"
 #include "content/public/browser/notification_observer.h"
 #include "ui/aura/window_observer.h"
@@ -36,6 +36,7 @@
 class AppSyncUIState;
 class Browser;
 class BrowserShortcutLauncherItemController;
+class BrowserStatusMonitor;
 class ExtensionEnableFlow;
 class GURL;
 class LauncherItemController;
@@ -71,7 +72,10 @@
 // * App shell windows have ShellWindowLauncherItemController, owned by
 //   ShellWindowLauncherController.
 // * Shortcuts have no LauncherItemController.
+// TODO(simon.hong81): Move LauncherItemDelegate out from
+// ChromeLauncherController and makes separate subclass with it.
 class ChromeLauncherController : public ash::LauncherDelegate,
+                                 public ash::LauncherItemDelegate,
                                  public ash::LauncherModelObserver,
                                  public ash::ShellObserver,
                                  public ash::DisplayController::Observer,
@@ -80,7 +84,6 @@
                                  public PrefServiceSyncableObserver,
                                  public AppSyncUIStateObserver,
                                  public ExtensionEnableFlowDelegate,
-                                 public chrome::BrowserListObserver,
                                  public ash::ShelfLayoutManagerObserver {
  public:
   // Indicates if a launcher item is incognito or not.
@@ -195,8 +198,6 @@
   // Returns the id of the app for the specified tab.
   std::string GetAppID(content::WebContents* tab);
 
-  std::string GetAppIDForLauncherID(ash::LauncherID id);
-
   // Set the image for a specific launcher item (e.g. when set by the app).
   void SetLauncherItemImage(ash::LauncherID launcher_id,
                             const gfx::ImageSkia& image);
@@ -267,6 +268,17 @@
                                         bool allow_minimize);
 
   // ash::LauncherDelegate overrides:
+  virtual ash::LauncherID GetIDByWindow(aura::Window* window) OVERRIDE;
+  virtual void OnLauncherCreated(ash::Launcher* launcher) OVERRIDE;
+  virtual void OnLauncherDestroyed(ash::Launcher* launcher) OVERRIDE;
+  virtual ash::LauncherID GetLauncherIDForAppID(
+      const std::string& app_id) OVERRIDE;
+  virtual const std::string& GetAppIDForLauncherID(ash::LauncherID id) OVERRIDE;
+  virtual void PinAppWithID(const std::string& app_id) OVERRIDE;
+  virtual bool IsAppPinned(const std::string& app_id) OVERRIDE;
+  virtual void UnpinAppWithID(const std::string& app_id) OVERRIDE;
+
+  // ash::LauncherItemDelegate overrides:
   virtual void ItemSelected(const ash::LauncherItem& item,
                            const ui::Event& event) OVERRIDE;
   virtual string16 GetTitle(const ash::LauncherItem& item) OVERRIDE;
@@ -275,16 +287,8 @@
   virtual ash::LauncherMenuModel* CreateApplicationMenu(
       const ash::LauncherItem& item,
       int event_flags) OVERRIDE;
-  virtual ash::LauncherID GetIDByWindow(aura::Window* window) OVERRIDE;
   virtual bool IsDraggable(const ash::LauncherItem& item) OVERRIDE;
   virtual bool ShouldShowTooltip(const ash::LauncherItem& item) OVERRIDE;
-  virtual void OnLauncherCreated(ash::Launcher* launcher) OVERRIDE;
-  virtual void OnLauncherDestroyed(ash::Launcher* launcher) OVERRIDE;
-  virtual ash::LauncherID GetLauncherIDForAppID(
-      const std::string& app_id) OVERRIDE;
-  virtual void PinAppWithID(const std::string& app_id) OVERRIDE;
-  virtual bool IsAppPinned(const std::string& app_id) OVERRIDE;
-  virtual void UnpinAppsWithID(const std::string& app_id) OVERRIDE;
 
   // ash::LauncherModelObserver overrides:
   virtual void LauncherItemAdded(int index) OVERRIDE;
@@ -356,9 +360,6 @@
   // If |web_contents| has not loaded, returns "Net Tab".
   string16 GetAppListTitle(content::WebContents* web_contents) const;
 
-  // Overridden from chrome::BrowserListObserver.
-  virtual void OnBrowserRemoved(Browser* browser) OVERRIDE;
-
   // Returns true when the given |browser| is listed in the browser application
   // list.
   bool IsBrowserRepresentedInBrowserList(Browser* browser);
@@ -366,6 +367,9 @@
   // Returns the LauncherItemController of BrowserShortcut.
   LauncherItemController* GetBrowserShortcutLauncherItemController();
 
+  // Updates the activation state of the Broswer item.
+  void UpdateBrowserItemStatus();
+
  protected:
   // Creates a new app shortcut item and controller on the launcher at |index|.
   // Use kInsertItemAtEnd to add a shortcut as the last item.
@@ -396,9 +400,6 @@
       int index,
       ash::LauncherItemType launcher_item_type);
 
-  // Updates the activation state of the Broswer item.
-  void UpdateBrowserItemStatus();
-
   // Returns the profile used for new windows.
   Profile* GetProfileForNewWindows();
 
@@ -408,7 +409,7 @@
   // Internal helpers for pinning and unpinning that handle both
   // client-triggered and internal pinning operations.
   void DoPinAppWithID(const std::string& app_id);
-  void DoUnpinAppsWithID(const std::string& app_id);
+  void DoUnpinAppWithID(const std::string& app_id);
 
   // Re-syncs launcher model with prefs::kPinnedLauncherApps.
   void UpdateAppLaunchersFromPref();
@@ -466,6 +467,9 @@
   void MoveItemWithoutPinnedStateChangeNotification(int source_index,
                                                     int target_index);
 
+  // Register LauncherItemDelegate.
+  void RegisterLauncherItemDelegate();
+
   static ChromeLauncherController* instance_;
 
   ash::LauncherModel* model_;
@@ -505,6 +509,9 @@
   // The owned browser shortcut item.
   scoped_ptr<BrowserShortcutLauncherItemController> browser_item_controller_;
 
+  // The owned browser status monitor.
+  scoped_ptr<BrowserStatusMonitor> browser_status_monitor_;
+
   // If true, incoming pinned state changes should be ignored.
   bool ignore_persist_pinned_state_change_;
 
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
index 3b448be..54df0b1 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
@@ -10,6 +10,7 @@
 #include "ash/ash_switches.h"
 #include "ash/display/display_controller.h"
 #include "ash/launcher/launcher.h"
+#include "ash/launcher/launcher_button.h"
 #include "ash/launcher/launcher_model.h"
 #include "ash/launcher/launcher_util.h"
 #include "ash/launcher/launcher_view.h"
@@ -170,6 +171,17 @@
   DISALLOW_COPY_AND_ASSIGN(LauncherPlatformAppBrowserTest);
 };
 
+enum RipOffCommand {
+  // Drag the item off the shelf and let the mouse go.
+  RIP_OFF_ITEM,
+  // Drag the item off the shelf, move the mouse back and then let go.
+  RIP_OFF_ITEM_AND_RETURN,
+  // Drag the item off the shelf and then issue a cancel command.
+  RIP_OFF_ITEM_AND_CANCEL,
+  // Drag the item off the shelf and do not release the mouse.
+  RIP_OFF_ITEM_AND_DONT_RELEASE_MOUSE,
+};
+
 class LauncherAppBrowserTest : public ExtensionBrowserTest {
  protected:
   LauncherAppBrowserTest() : launcher_(NULL), model_(NULL), controller_(NULL) {
@@ -235,6 +247,10 @@
     return item.id;
   }
 
+  void RemoveShortcut(ash::LauncherID id) {
+    controller_->Unpin(id);
+  }
+
   // Activate the launcher item with the given |id|.
   void ActivateLauncherItem(int id) {
     launcher_->ActivateLauncherItem(id);
@@ -245,6 +261,44 @@
         name, model_->item_count());
   }
 
+  // Get the index of an item which has the given type.
+  int GetIndexOfLauncherItemType(ash::LauncherItemType type) {
+    for (int i = 0; i < model_->item_count(); i++) {
+      if (model_->items()[i].type == type)
+        return i;
+    }
+    return -1;
+  }
+
+  // Try to rip off |item_index|.
+  void RipOffItemIndex(int index,
+                       aura::test::EventGenerator* generator,
+                       ash::test::LauncherViewTestAPI* test,
+                       RipOffCommand command) {
+    ash::internal::LauncherButton* button = test->GetButton(index);
+    gfx::Point start_point = button->GetBoundsInScreen().CenterPoint();
+    gfx::Point rip_off_point(0, 0);
+    generator->MoveMouseTo(start_point.x(), start_point.y());
+    base::MessageLoop::current()->RunUntilIdle();
+    generator->PressLeftButton();
+    base::MessageLoop::current()->RunUntilIdle();
+    generator->MoveMouseTo(rip_off_point.x(), rip_off_point.y());
+    base::MessageLoop::current()->RunUntilIdle();
+    test->RunMessageLoopUntilAnimationsDone();
+    if (command == RIP_OFF_ITEM_AND_RETURN) {
+      generator->MoveMouseTo(start_point.x(), start_point.y());
+      base::MessageLoop::current()->RunUntilIdle();
+      test->RunMessageLoopUntilAnimationsDone();
+    } else if (command == RIP_OFF_ITEM_AND_CANCEL) {
+      // This triggers an internal cancel. Using VKEY_ESCAPE was too unreliable.
+      button->OnMouseCaptureLost();
+    }
+    if (command != RIP_OFF_ITEM_AND_DONT_RELEASE_MOUSE) {
+      generator->ReleaseLeftButton();
+      test->RunMessageLoopUntilAnimationsDone();
+    }
+  }
+
   ash::Launcher* launcher_;
   ash::LauncherModel* model_;
   ChromeLauncherController* controller_;
@@ -1268,8 +1322,7 @@
 }
 
 // Test that we can launch a platform app panel and get a running item.
-IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest,
-    LaunchPanelWindow) {
+IN_PROC_BROWSER_TEST_F(LauncherPlatformAppBrowserTest, LaunchPanelWindow) {
   int item_count = launcher_model()->item_count();
   const Extension* extension = LoadAndLaunchPlatformApp("launch");
   ShellWindow::CreateParams params;
@@ -1537,6 +1590,72 @@
   EXPECT_EQ(3, model_->item_count());  // And it remains that way.
 }
 
+// Do tests for removal of items from the shelf by dragging.
+IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, DragOffShelf) {
+  // Set the command line option to enable the new functionality.
+  CommandLine::ForCurrentProcess()->AppendSwitch(
+      ash::switches::kAshEnableDragOffShelf);
+  aura::test::EventGenerator generator(
+      ash::Shell::GetPrimaryRootWindow(), gfx::Point());
+  ash::test::LauncherViewTestAPI test(launcher_->GetLauncherViewForTest());
+
+  // Create a known application and check that we have 3 items in the launcher.
+  CreateShortcut("app1");
+  EXPECT_EQ(3, model_->item_count());
+
+  // Test #1: Ripping out the browser item should not change anything.
+  int browser_index = GetIndexOfLauncherItemType(ash::TYPE_BROWSER_SHORTCUT);
+  EXPECT_LE(0, browser_index);
+  RipOffItemIndex(browser_index, &generator, &test, RIP_OFF_ITEM);
+  // => It should not have been removed and the location should be unchanged.
+  EXPECT_EQ(3, model_->item_count());
+  EXPECT_EQ(browser_index,
+            GetIndexOfLauncherItemType(ash::TYPE_BROWSER_SHORTCUT));
+  // Make sure that the hide state has been unset after the snap back animation
+  // finished.
+  ash::internal::LauncherButton* button = test.GetButton(browser_index);
+  EXPECT_FALSE(button->state() & ash::internal::LauncherButton::STATE_HIDDEN);
+
+  // Test #2: Ripping out the application and canceling the operation should
+  // not change anything.
+  int app_index = GetIndexOfLauncherItemType(ash::TYPE_APP_SHORTCUT);
+  EXPECT_LE(0, app_index);
+  RipOffItemIndex(app_index, &generator, &test, RIP_OFF_ITEM_AND_CANCEL);
+  // => It should not have been removed and the location should be unchanged.
+  ASSERT_EQ(3, model_->item_count());
+  EXPECT_EQ(app_index, GetIndexOfLauncherItemType(ash::TYPE_APP_SHORTCUT));
+
+  // Test #3: Ripping out the application and moving it back in should not
+  // change anything.
+  RipOffItemIndex(app_index, &generator, &test, RIP_OFF_ITEM_AND_RETURN);
+  // => It should not have been removed and the location should be unchanged.
+  ASSERT_EQ(3, model_->item_count());
+  // Through the operation the index might have changed.
+  app_index = GetIndexOfLauncherItemType(ash::TYPE_APP_SHORTCUT);
+
+  // Test #4: Ripping out the application should remove the item.
+  RipOffItemIndex(app_index, &generator, &test, RIP_OFF_ITEM);
+  // => It should not have been removed and the location should be unchanged.
+  EXPECT_EQ(2, model_->item_count());
+  EXPECT_EQ(-1, GetIndexOfLauncherItemType(ash::TYPE_APP_SHORTCUT));
+
+  // Test #5: Uninstalling an application while it is being ripped off should
+  // not crash.
+  ash::LauncherID app_id = CreateShortcut("app2");
+  int app2_index = GetIndexOfLauncherItemType(ash::TYPE_APP_SHORTCUT);
+  EXPECT_EQ(3, model_->item_count());  // And it remains that way.
+  RipOffItemIndex(app2_index,
+                  &generator,
+                  &test,
+                  RIP_OFF_ITEM_AND_DONT_RELEASE_MOUSE);
+  RemoveShortcut(app_id);
+  EXPECT_EQ(2, model_->item_count());  // The item should now be gone.
+  generator.ReleaseLeftButton();
+  base::MessageLoop::current()->RunUntilIdle();
+  EXPECT_EQ(2, model_->item_count());  // And it remains that way.
+  EXPECT_EQ(-1, GetIndexOfLauncherItemType(ash::TYPE_APP_SHORTCUT));
+}
+
 // Check that clicking on an app launcher item launches a new browser.
 IN_PROC_BROWSER_TEST_F(LauncherAppBrowserTest, ClickItem) {
   // Get a number of interfaces we need.
@@ -1658,7 +1777,7 @@
   EXPECT_TRUE(launcher_->IsShowingOverflowBubble());
 
   // Unpin first pinned app and there should be no crash.
-  controller_->UnpinAppsWithID(std::string("fake_app_0"));
+  controller_->UnpinAppWithID(std::string("fake_app_0"));
 
   test.RunMessageLoopUntilAnimationsDone();
   EXPECT_FALSE(launcher_->IsShowingOverflowBubble());
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
index c08d2f6..b48158e 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -851,7 +851,7 @@
   EXPECT_FALSE(
       launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
 
-  launcher_controller_->UnpinAppsWithID(extension1_->id());
+  launcher_controller_->UnpinAppWithID(extension1_->id());
 
   EXPECT_EQ(2, model_->item_count());
 }
@@ -881,7 +881,7 @@
   EXPECT_FALSE(
       launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
 
-  launcher_controller_->UnpinAppsWithID(extension1_->id());
+  launcher_controller_->UnpinAppWithID(extension1_->id());
 
   EXPECT_EQ(3, model_->item_count());
   EXPECT_EQ(ash::TYPE_WINDOWED_APP, model_->items()[1].type);
@@ -925,7 +925,7 @@
   EXPECT_FALSE(
       launcher_controller_->IsWindowedAppInLauncher(extension1_->id()));
 
-  launcher_controller_->UnpinAppsWithID(extension1_->id());
+  launcher_controller_->UnpinAppWithID(extension1_->id());
 
   EXPECT_EQ(2, model_->item_count());
 }
@@ -1525,6 +1525,6 @@
   EXPECT_FALSE(launcher_controller_->IsAppPinned("0"));
   EXPECT_EQ(ash::TYPE_APP_SHORTCUT, model_->items()[app_index].type);
 
-  launcher_controller_->UnpinAppsWithID("1");
+  launcher_controller_->UnpinAppWithID("1");
   ASSERT_EQ(initial_size, model_->items().size());
 }
diff --git a/chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.h b/chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.h
index 46a483f..1db85e5 100644
--- a/chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.h
+++ b/chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_ASH_LAUNCHER_LAUNCHER_APPLICATION_MENU_ITEM_MODEL_H_
 
 #include "ash/launcher/launcher_delegate.h"
+#include "ash/launcher/launcher_item_delegate.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/scoped_vector.h"
 
diff --git a/chrome/browser/ui/ash/launcher/launcher_favicon_loader.cc b/chrome/browser/ui/ash/launcher/launcher_favicon_loader.cc
index 442e458..2b63cfd 100644
--- a/chrome/browser/ui/ash/launcher/launcher_favicon_loader.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_favicon_loader.cc
@@ -4,9 +4,9 @@
 
 #include "chrome/browser/ui/ash/launcher/launcher_favicon_loader.h"
 
+#include "ash/launcher/launcher_types.h"
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
-#include "chrome/browser/ui/ash/launcher/browser_launcher_item_controller.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
diff --git a/chrome/browser/ui/ash/launcher/launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/launcher_item_controller.cc
index d0cb781..f8858bc 100644
--- a/chrome/browser/ui/ash/launcher/launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_item_controller.cc
@@ -48,8 +48,6 @@
       return ash::TYPE_PLATFORM_APP;
     case LauncherItemController::TYPE_APP_PANEL:
       return ash::TYPE_APP_PANEL;
-    case LauncherItemController::TYPE_TABBED:
-      return ash::TYPE_TABBED;
   }
   NOTREACHED();
   return ash::TYPE_APP_SHORTCUT;
diff --git a/chrome/browser/ui/ash/launcher/launcher_item_controller.h b/chrome/browser/ui/ash/launcher/launcher_item_controller.h
index 631a66f..c374b1f 100644
--- a/chrome/browser/ui/ash/launcher/launcher_item_controller.h
+++ b/chrome/browser/ui/ash/launcher/launcher_item_controller.h
@@ -32,7 +32,6 @@
     TYPE_APP,
     TYPE_APP_PANEL,
     TYPE_SHORTCUT,
-    TYPE_TABBED,
     TYPE_WINDOWED_APP
   };
 
diff --git a/chrome/browser/ui/ash/screenshot_taker.cc b/chrome/browser/ui/ash/screenshot_taker.cc
index 13cf3b9..48f8892 100644
--- a/chrome/browser/ui/ash/screenshot_taker.cc
+++ b/chrome/browser/ui/ash/screenshot_taker.cc
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "ash/shell.h"
+#include "ash/system/system_notifier.h"
 #include "base/bind.h"
 #include "base/file_util.h"
 #include "base/i18n/time_formatting.h"
@@ -42,7 +43,7 @@
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/drive/file_system_util.h"
-#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
+#include "chrome/browser/chromeos/extensions/file_manager/open_util.h"
 #include "chrome/browser/chromeos/login/user_manager.h"
 #include "chrome/browser/notifications/desktop_notification_service.h"
 #include "chrome/browser/notifications/desktop_notification_service_factory.h"
@@ -269,9 +270,6 @@
 }
 
 bool GetScreenshotDirectory(base::FilePath* directory) {
-  if (g_browser_process->local_state()->GetBoolean(prefs::kDisableScreenshots))
-    return false;
-
   bool is_logged_in = true;
 
 #if defined(OS_CHROMEOS)
@@ -291,6 +289,30 @@
   return true;
 }
 
+const int GetScreenshotNotificationTitle(
+    ScreenshotTakerObserver::Result screenshot_result) {
+  switch (screenshot_result) {
+    case ScreenshotTakerObserver::SCREENSHOTS_DISABLED:
+      return IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_DISABLED;
+    case ScreenshotTakerObserver::SCREENSHOT_SUCCESS:
+      return IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_SUCCESS;
+    default:
+      return IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_FAIL;
+  }
+}
+
+const int GetScreenshotNotificationText(
+    ScreenshotTakerObserver::Result screenshot_result) {
+  switch (screenshot_result) {
+    case ScreenshotTakerObserver::SCREENSHOTS_DISABLED:
+      return IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_DISABLED;
+    case ScreenshotTakerObserver::SCREENSHOT_SUCCESS:
+      return IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_SUCCESS;
+    default:
+      return IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_FAIL;
+  }
+}
+
 }  // namespace
 
 ScreenshotTaker::ScreenshotTaker()
@@ -302,6 +324,12 @@
 }
 
 void ScreenshotTaker::HandleTakeScreenshotForAllRootWindows() {
+  if (g_browser_process->local_state()->
+          GetBoolean(prefs::kDisableScreenshots)) {
+    ShowNotification(ScreenshotTakerObserver::SCREENSHOTS_DISABLED,
+                     base::FilePath());
+    return;
+  }
   base::FilePath screenshot_directory;
   if (!screenshot_directory_for_test_.empty()) {
     screenshot_directory = screenshot_directory_for_test_;
@@ -348,6 +376,12 @@
 
 void ScreenshotTaker::HandleTakePartialScreenshot(
     aura::Window* window, const gfx::Rect& rect) {
+  if (g_browser_process->local_state()->
+          GetBoolean(prefs::kDisableScreenshots)) {
+    ShowNotification(ScreenshotTakerObserver::SCREENSHOTS_DISABLED,
+                     base::FilePath());
+    return;
+  }
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
 
   base::FilePath screenshot_directory;
@@ -398,20 +432,19 @@
   bool success =
       (screenshot_result == ScreenshotTakerObserver::SCREENSHOT_SUCCESS);
   return new Notification(
+      message_center::NOTIFICATION_TYPE_SIMPLE,
       GURL(kNotificationOriginUrl),
+      l10n_util::GetStringUTF16(
+          GetScreenshotNotificationTitle(screenshot_result)),
+      l10n_util::GetStringUTF16(
+          GetScreenshotNotificationText(screenshot_result)),
       ui::ResourceBundle::GetSharedInstance().GetImageNamed(
           IDR_SCREENSHOT_NOTIFICATION_ICON),
-      l10n_util::GetStringUTF16(
-          success ?
-          IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_SUCCESS :
-          IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_FAIL),
-      l10n_util::GetStringUTF16(
-          success ?
-          IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_SUCCESS :
-          IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_FAIL),
       WebKit::WebTextDirectionDefault,
+      message_center::NotifierId(ash::NOTIFIER_SCREENSHOT),
       l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_NOTIFIER_SCREENSHOT_NAME),
       replace_id,
+      message_center::RichNotificationData(),
       new ScreenshotTakerNotificationDelegate(success, screenshot_path));
 }
 
@@ -430,7 +463,7 @@
   DesktopNotificationService* const service =
       DesktopNotificationServiceFactory::GetForProfile(GetProfile());
   if (service->IsNotifierEnabled(
-          message_center::NotifierId(message_center::NotifierId::SCREENSHOT))) {
+          message_center::NotifierId(ash::NOTIFIER_SCREENSHOT))) {
     scoped_ptr<Notification> notification(
         CreateNotification(screenshot_result, screenshot_path));
     g_browser_process->notification_ui_manager()->Add(*notification,
diff --git a/chrome/browser/ui/ash/screenshot_taker.h b/chrome/browser/ui/ash/screenshot_taker.h
index 5411719..7583ea1 100644
--- a/chrome/browser/ui/ash/screenshot_taker.h
+++ b/chrome/browser/ui/ash/screenshot_taker.h
@@ -37,6 +37,7 @@
     SCREENSHOT_CHECK_DIR_FAILED,
     SCREENSHOT_CREATE_FILE_FAILED,
     SCREENSHOT_WRITE_FILE_FAILED,
+    SCREENSHOTS_DISABLED,
     SCREENSHOT_RESULT_COUNT
   };
 
diff --git a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc
index 3fec283..4508781 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.cc
@@ -76,6 +76,7 @@
 #include "grit/webkit_resources.h"
 #include "net/cert/cert_status_flags.h"
 #include "ui/base/base_window.h"
+#include "ui/base/events/event.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/combobox_model.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -160,7 +161,8 @@
                             iter->second,
                             g_browser_process->GetApplicationLocale());
       } else {
-        form_group->SetRawInfo(type, iter->second);
+        form_group->SetRawInfo(
+            AutofillType(type).GetStorableType(), iter->second);
       }
     }
   }
@@ -199,7 +201,8 @@
         if (card)
           card->SetRawInfo(it->first->type, trimmed);
       } else if (profile) {
-        profile->SetRawInfo(it->first->type, trimmed);
+        profile->SetRawInfo(
+            AutofillType(it->first->type).GetStorableType(), trimmed);
       }
     }
   }
@@ -295,7 +298,7 @@
     ServerFieldType type = requested_fields[i].type;
     if (type != ADDRESS_HOME_LINE2 &&
         type != CREDIT_CARD_VERIFICATION_CODE &&
-        data_model.GetRawInfo(type).empty()) {
+        data_model.GetRawInfo(AutofillType(type).GetStorableType()).empty()) {
       return false;
     }
   }
@@ -1769,6 +1772,7 @@
       content_bounds,
       base::i18n::IsRTL() ?
           base::i18n::RIGHT_TO_LEFT : base::i18n::LEFT_TO_RIGHT);
+  popup_controller_->set_hide_on_outside_click(true);
   popup_controller_->Show(popup_values,
                           popup_labels,
                           popup_icons,
@@ -2013,13 +2017,19 @@
 // AutofillPopupDelegate implementation.
 
 void AutofillDialogControllerImpl::OnPopupShown(
-    content::KeyboardListener* listener) {
+    content::RenderWidgetHost::KeyPressEventCallback* callback) {
   GetMetricLogger().LogDialogPopupEvent(
       GetDialogType(), AutofillMetrics::DIALOG_POPUP_SHOWN);
 }
 
 void AutofillDialogControllerImpl::OnPopupHidden(
-    content::KeyboardListener* listener) {}
+    content::RenderWidgetHost::KeyPressEventCallback* callback) {}
+
+bool AutofillDialogControllerImpl::ShouldRepostEvent(
+    const ui::MouseEvent& event) {
+  // If the event would be reposted inside |input_showing_popup_|, just ignore.
+  return !view_->HitTestInput(*input_showing_popup_, event.location());
+}
 
 void AutofillDialogControllerImpl::DidSelectSuggestion(int identifier) {
   // TODO(estade): implement.
@@ -2321,7 +2331,7 @@
 }
 
 bool AutofillDialogControllerImpl::TransmissionWillBeSecure() const {
-  return source_url_.SchemeIs(chrome::kHttpsScheme);
+  return source_url_.SchemeIs(content::kHttpsScheme);
 }
 
 void AutofillDialogControllerImpl::ShowNewCreditCardBubble(
@@ -3497,8 +3507,8 @@
 #if !defined(OS_ANDROID)
   GeneratedCreditCardBubbleController::Show(
       web_contents(),
-      backing_last_four,
-      full_wallet_->TypeAndLastFourDigits());
+      full_wallet_->TypeAndLastFourDigits(),
+      backing_last_four);
 #endif
 }
 
diff --git a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h
index bd3dcdb..54fb490 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h
+++ b/chrome/browser/ui/autofill/autofill_dialog_controller_impl.h
@@ -172,8 +172,11 @@
   virtual content::WebContents* GetWebContents() OVERRIDE;
 
   // AutofillPopupDelegate implementation.
-  virtual void OnPopupShown(content::KeyboardListener* listener) OVERRIDE;
-  virtual void OnPopupHidden(content::KeyboardListener* listener) OVERRIDE;
+  virtual void OnPopupShown(
+      content::RenderWidgetHost::KeyPressEventCallback* callback) OVERRIDE;
+  virtual void OnPopupHidden(
+      content::RenderWidgetHost::KeyPressEventCallback* callback) OVERRIDE;
+  virtual bool ShouldRepostEvent(const ui::MouseEvent& event) OVERRIDE;
   virtual void DidSelectSuggestion(int identifier) OVERRIDE;
   virtual void DidAcceptSuggestion(const string16& value,
                                    int identifier) OVERRIDE;
diff --git a/chrome/browser/ui/autofill/autofill_dialog_controller_unittest.cc b/chrome/browser/ui/autofill/autofill_dialog_controller_unittest.cc
index f7d8798..3fcd4b6 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_controller_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_dialog_controller_unittest.cc
@@ -172,6 +172,10 @@
   }
 
   virtual string16 GetCvc() OVERRIDE { return string16(); }
+  virtual bool HitTestInput(const DetailInput& input,
+                            const gfx::Point& screen_point) OVERRIDE {
+    return false;
+  }
   virtual bool SaveDetailsLocally() OVERRIDE { return true; }
   virtual const content::NavigationController* ShowSignIn() OVERRIDE {
     return NULL;
@@ -1163,7 +1167,7 @@
   EXPECT_EQ(PHONE_BILLING, form_structure()->field(1)->Type().group());
   EXPECT_EQ(shipping_profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER),
             form_structure()->field(0)->value);
-  EXPECT_EQ(billing_profile.GetRawInfo(PHONE_BILLING_WHOLE_NUMBER),
+  EXPECT_EQ(billing_profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER),
             form_structure()->field(1)->value);
   EXPECT_NE(form_structure()->field(1)->value,
             form_structure()->field(0)->value);
diff --git a/chrome/browser/ui/autofill/autofill_dialog_view.h b/chrome/browser/ui/autofill/autofill_dialog_view.h
index 9466c0b..295f429 100644
--- a/chrome/browser/ui/autofill/autofill_dialog_view.h
+++ b/chrome/browser/ui/autofill/autofill_dialog_view.h
@@ -12,6 +12,7 @@
 }
 
 namespace gfx {
+class Point;
 class Size;
 }
 
@@ -78,6 +79,10 @@
   // relevant.
   virtual string16 GetCvc() = 0;
 
+  // Whether or not |point| is within |input|'s bounds.
+  virtual bool HitTestInput(const DetailInput& input,
+                            const gfx::Point& screen_point) = 0;
+
   // Returns true if new or edited autofill details should be saved.
   virtual bool SaveDetailsLocally() = 0;
 
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller.h b/chrome/browser/ui/autofill/autofill_popup_controller.h
index 63a7549..4b4f7c6 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller.h
+++ b/chrome/browser/ui/autofill/autofill_popup_controller.h
@@ -18,6 +18,10 @@
 class RectF;
 }
 
+namespace ui {
+class MouseEvent;
+}
+
 namespace autofill {
 
 // This interface provides data to an AutofillPopupView.
@@ -41,6 +45,9 @@
   // The user has moved the mouse outside of the popup.
   virtual void MouseExitedPopup() = 0;
 
+  // Whether |event| should be reposted to the native window management.
+  virtual bool ShouldRepostEvent(const ui::MouseEvent& event) = 0;
+
   // Accepts the suggestion at |index|.
   virtual void AcceptSuggestion(size_t index) = 0;
 
@@ -100,6 +107,9 @@
   // hovered or has keyboard focus.
   virtual int selected_line() const = 0;
 
+  // Whether the view should be hidden on outside mouse presses.
+  virtual bool hide_on_outside_click() const = 0;
+
  protected:
   virtual ~AutofillPopupController() {}
 };
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_browsertest.cc b/chrome/browser/ui/autofill/autofill_popup_controller_browsertest.cc
deleted file mode 100644
index 2598972..0000000
--- a/chrome/browser/ui/autofill/autofill_popup_controller_browsertest.cc
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright (c) 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 "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "chrome/browser/ui/autofill/autofill_popup_view.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "components/autofill/content/browser/autofill_driver_impl.h"
-#include "components/autofill/core/browser/autofill_manager.h"
-#include "components/autofill/core/browser/test_autofill_external_delegate.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/test/test_utils.h"
-#include "ui/gfx/rect.h"
-#include "ui/gfx/vector2d.h"
-
-namespace autofill {
-namespace {
-
-class TestAutofillExternalDelegate : public AutofillExternalDelegate {
- public:
-  TestAutofillExternalDelegate(content::WebContents* web_contents,
-                               AutofillManager* autofill_manager,
-                               AutofillDriver* autofill_driver)
-      : AutofillExternalDelegate(web_contents, autofill_manager,
-                                 autofill_driver),
-        popup_hidden_(true) {}
-  virtual ~TestAutofillExternalDelegate() {}
-
-  virtual void OnPopupShown(content::KeyboardListener* listener) OVERRIDE {
-    popup_hidden_ = false;
-
-    AutofillExternalDelegate::OnPopupShown(listener);
-  }
-
-  virtual void OnPopupHidden(content::KeyboardListener* listener) OVERRIDE {
-    popup_hidden_ = true;
-
-    if (message_loop_runner_.get())
-      message_loop_runner_->Quit();
-
-    AutofillExternalDelegate::OnPopupHidden(listener);
-  }
-
-  void WaitForPopupHidden() {
-    if (popup_hidden_)
-      return;
-
-    message_loop_runner_ = new content::MessageLoopRunner;
-    message_loop_runner_->Run();
-  }
-
-  bool popup_hidden() const { return popup_hidden_; }
-
- private:
-  bool popup_hidden_;
-  scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestAutofillExternalDelegate);
-};
-
-}  // namespace
-
-class AutofillPopupControllerBrowserTest
-    : public InProcessBrowserTest,
-      public content::WebContentsObserver {
- public:
-  AutofillPopupControllerBrowserTest() {}
-  virtual ~AutofillPopupControllerBrowserTest() {}
-
-  virtual void SetUpOnMainThread() OVERRIDE {
-    web_contents_ = browser()->tab_strip_model()->GetActiveWebContents();
-    ASSERT_TRUE(web_contents_ != NULL);
-    Observe(web_contents_);
-
-    AutofillDriverImpl* driver =
-        AutofillDriverImpl::FromWebContents(web_contents_);
-    autofill_external_delegate_.reset(
-       new TestAutofillExternalDelegate(
-           web_contents_,
-           driver->autofill_manager(),
-           driver));
-  }
-
-  // Normally the WebContents will automatically delete the delegate, but here
-  // the delegate is owned by this test, so we have to manually destroy.
-  virtual void WebContentsDestroyed(content::WebContents* web_contents)
-      OVERRIDE {
-    DCHECK_EQ(web_contents_, web_contents);
-
-    autofill_external_delegate_.reset();
-  }
-
- protected:
-  content::WebContents* web_contents_;
-
-  scoped_ptr<TestAutofillExternalDelegate> autofill_external_delegate_;
-};
-
-#if defined(OS_LINUX)
-#define MAYBE_HidePopupOnWindowConfiguration DISABLED_HidePopupOnWindowConfiguration
-#else
-#define MAYBE_HidePopupOnWindowConfiguration HidePopupOnWindowConfiguration
-#endif
-// Autofill UI isn't currently hidden on window move on Mac.
-// http://crbug.com/180566
-#if !defined(OS_MACOSX)
-IN_PROC_BROWSER_TEST_F(AutofillPopupControllerBrowserTest,
-                       MAYBE_HidePopupOnWindowConfiguration) {
-  GenerateTestAutofillPopup(autofill_external_delegate_.get());
-
-  EXPECT_FALSE(autofill_external_delegate_->popup_hidden());
-
-  // Resize the window, which should cause the popup to hide.
-  gfx::Rect new_bounds = browser()->window()->GetBounds() - gfx::Vector2d(1, 1);
-  browser()->window()->SetBounds(new_bounds);
-
-  autofill_external_delegate_->WaitForPopupHidden();
-  EXPECT_TRUE(autofill_external_delegate_->popup_hidden());
-}
-#endif // !defined(OS_MACOSX)
-
-// This test checks that the browser doesn't crash if the delegate is deleted
-// before the popup is hidden.
-IN_PROC_BROWSER_TEST_F(AutofillPopupControllerBrowserTest,
-                       DeleteDelegateBeforePopupHidden){
-  GenerateTestAutofillPopup(autofill_external_delegate_.get());
-
-  // Delete the external delegate here so that is gets deleted before popup is
-  // hidden. This can happen if the web_contents are destroyed before the popup
-  // is hidden. See http://crbug.com/232475
-  autofill_external_delegate_.reset();
-}
-
-}  // namespace autofill
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
index d7bfd83..043e8c1 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
@@ -100,7 +100,11 @@
       container_view_(container_view),
       element_bounds_(element_bounds),
       text_direction_(text_direction),
-      weak_ptr_factory_(this) {
+      hide_on_outside_click_(false),
+      weak_ptr_factory_(this),
+      key_press_event_callback_(
+          base::Bind(&AutofillPopupControllerImpl::HandleKeyPressEvent,
+                     base::Unretained(this))) {
   ClearState();
 #if !defined(OS_ANDROID)
   subtext_font_ = name_font_.DeriveFont(kLabelFontSizeDelta);
@@ -167,7 +171,7 @@
     UpdateBoundsAndRedrawPopup();
   }
 
-  delegate_->OnPopupShown(this);
+  delegate_->OnPopupShown(&key_press_event_callback_);
 }
 
 void AutofillPopupControllerImpl::UpdateDataListValues(
@@ -228,7 +232,7 @@
 
 void AutofillPopupControllerImpl::Hide() {
   if (delegate_.get())
-    delegate_->OnPopupHidden(this);
+    delegate_->OnPopupHidden(&key_press_event_callback_);
 
   if (view_)
     view_->Hide();
@@ -302,6 +306,11 @@
   SetSelectedLine(kNoSelection);
 }
 
+bool AutofillPopupControllerImpl::ShouldRepostEvent(
+    const ui::MouseEvent& event) {
+  return delegate_->ShouldRepostEvent(event);
+}
+
 void AutofillPopupControllerImpl::AcceptSuggestion(size_t index) {
   delegate_->DidAcceptSuggestion(full_names_[index], identifiers_[index]);
 }
@@ -363,6 +372,10 @@
   return text_direction_ == base::i18n::RIGHT_TO_LEFT;
 }
 
+bool AutofillPopupControllerImpl::hide_on_outside_click() const {
+  return hide_on_outside_click_;
+}
+
 const std::vector<string16>& AutofillPopupControllerImpl::names() const {
   return names_;
 }
@@ -397,6 +410,11 @@
   return selected_line_;
 }
 
+void AutofillPopupControllerImpl::set_hide_on_outside_click(
+    bool hide_on_outside_click) {
+  hide_on_outside_click_ = hide_on_outside_click;
+}
+
 void AutofillPopupControllerImpl::SetSelectedLine(int selected_line) {
   if (selected_line_ == selected_line)
     return;
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.h b/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
index 8509a9f..43f5a1d 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.h
@@ -10,11 +10,15 @@
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
 #include "chrome/browser/ui/autofill/autofill_popup_controller.h"
-#include "content/public/browser/keyboard_listener.h"
+#include "content/public/browser/render_widget_host.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/rect.h"
 #include "ui/gfx/rect_f.h"
 
+namespace content {
+struct NativeWebKeyboardEvent;
+}
+
 namespace gfx {
 class Display;
 }
@@ -31,8 +35,7 @@
 // This class is a controller for an AutofillPopupView. It implements
 // AutofillPopupController to allow calls from AutofillPopupView. The
 // other, public functions are available to its instantiator.
-class AutofillPopupControllerImpl : public AutofillPopupController,
-                                    public content::KeyboardListener {
+class AutofillPopupControllerImpl : public AutofillPopupController {
  public:
   // Creates a new |AutofillPopupControllerImpl|, or reuses |previous| if
   // the construction arguments are the same. |previous| may be invalidated by
@@ -61,9 +64,10 @@
   // Invoked when the view was destroyed by by someone other than this class.
   virtual void ViewDestroyed() OVERRIDE;
 
-  // KeyboardListener implementation.
-  virtual bool HandleKeyPressEvent(
-      const content::NativeWebKeyboardEvent& event) OVERRIDE;
+  bool HandleKeyPressEvent(const content::NativeWebKeyboardEvent& event);
+
+  // Tells the view to capture mouse events. Must be called before |Show()|.
+  void set_hide_on_outside_click(bool hide_on_outside_click);
 
  protected:
   FRIEND_TEST_ALL_PREFIXES(AutofillExternalDelegateBrowserTest,
@@ -82,6 +86,7 @@
   virtual void MouseHovered(int x, int y) OVERRIDE;
   virtual void MouseClicked(int x, int y) OVERRIDE;
   virtual void MouseExitedPopup() OVERRIDE;
+  virtual bool ShouldRepostEvent(const ui::MouseEvent& event) OVERRIDE;
   virtual void AcceptSuggestion(size_t index) OVERRIDE;
   virtual int GetIconResourceID(const string16& resource_name) OVERRIDE;
   virtual bool CanDelete(size_t index) const OVERRIDE;
@@ -102,6 +107,7 @@
   virtual const gfx::Font& subtext_font() const OVERRIDE;
 #endif
   virtual int selected_line() const OVERRIDE;
+  virtual bool hide_on_outside_click() const OVERRIDE;
 
   // Change which line is currently selected by the user.
   void SetSelectedLine(int selected_line);
@@ -224,7 +230,12 @@
   // |kNoSelection| indicates that no line is currently selected.
   int selected_line_;
 
+  // Whether the popup view should hide on mouse presses outside of it.
+  bool hide_on_outside_click_;
+
   base::WeakPtrFactory<AutofillPopupControllerImpl> weak_ptr_factory_;
+
+  content::RenderWidgetHost::KeyPressEventCallback key_press_event_callback_;
 };
 
 }  // namespace autofill
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_interactive_uitest.cc b/chrome/browser/ui/autofill/autofill_popup_controller_interactive_uitest.cc
new file mode 100644
index 0000000..45e0e18
--- /dev/null
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_interactive_uitest.cc
@@ -0,0 +1,136 @@
+// 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 "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/ui/autofill/autofill_popup_view.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "components/autofill/content/browser/autofill_driver_impl.h"
+#include "components/autofill/core/browser/autofill_manager.h"
+#include "components/autofill/core/browser/test_autofill_external_delegate.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/test/test_utils.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/vector2d.h"
+
+namespace autofill {
+namespace {
+
+class TestAutofillExternalDelegate : public AutofillExternalDelegate {
+ public:
+  TestAutofillExternalDelegate(content::WebContents* web_contents,
+                               AutofillManager* autofill_manager,
+                               AutofillDriver* autofill_driver)
+      : AutofillExternalDelegate(web_contents, autofill_manager,
+                                 autofill_driver),
+        popup_hidden_(true) {}
+  virtual ~TestAutofillExternalDelegate() {}
+
+  virtual void OnPopupShown(
+      content::RenderWidgetHost::KeyPressEventCallback* callback) OVERRIDE {
+    popup_hidden_ = false;
+
+    AutofillExternalDelegate::OnPopupShown(callback);
+  }
+
+  virtual void OnPopupHidden(
+      content::RenderWidgetHost::KeyPressEventCallback* callback) OVERRIDE {
+    popup_hidden_ = true;
+
+    if (message_loop_runner_.get())
+      message_loop_runner_->Quit();
+
+    AutofillExternalDelegate::OnPopupHidden(callback);
+  }
+
+  void WaitForPopupHidden() {
+    if (popup_hidden_)
+      return;
+
+    message_loop_runner_ = new content::MessageLoopRunner;
+    message_loop_runner_->Run();
+  }
+
+  bool popup_hidden() const { return popup_hidden_; }
+
+ private:
+  bool popup_hidden_;
+  scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestAutofillExternalDelegate);
+};
+
+}  // namespace
+
+class AutofillPopupControllerBrowserTest
+    : public InProcessBrowserTest,
+      public content::WebContentsObserver {
+ public:
+  AutofillPopupControllerBrowserTest() {}
+  virtual ~AutofillPopupControllerBrowserTest() {}
+
+  virtual void SetUpOnMainThread() OVERRIDE {
+    web_contents_ = browser()->tab_strip_model()->GetActiveWebContents();
+    ASSERT_TRUE(web_contents_ != NULL);
+    Observe(web_contents_);
+
+    AutofillDriverImpl* driver =
+        AutofillDriverImpl::FromWebContents(web_contents_);
+    autofill_external_delegate_.reset(
+       new TestAutofillExternalDelegate(
+           web_contents_,
+           driver->autofill_manager(),
+           driver));
+  }
+
+  // Normally the WebContents will automatically delete the delegate, but here
+  // the delegate is owned by this test, so we have to manually destroy.
+  virtual void WebContentsDestroyed(content::WebContents* web_contents)
+      OVERRIDE {
+    DCHECK_EQ(web_contents_, web_contents);
+
+    autofill_external_delegate_.reset();
+  }
+
+ protected:
+  content::WebContents* web_contents_;
+
+  scoped_ptr<TestAutofillExternalDelegate> autofill_external_delegate_;
+};
+
+// Autofill UI isn't currently hidden on window move on Mac.
+// http://crbug.com/180566
+#if !defined(OS_MACOSX)
+IN_PROC_BROWSER_TEST_F(AutofillPopupControllerBrowserTest,
+                       HidePopupOnWindowConfiguration) {
+  GenerateTestAutofillPopup(autofill_external_delegate_.get());
+
+  EXPECT_FALSE(autofill_external_delegate_->popup_hidden());
+
+  // Resize the window, which should cause the popup to hide.
+  gfx::Rect new_bounds = browser()->window()->GetBounds() - gfx::Vector2d(1, 1);
+  browser()->window()->SetBounds(new_bounds);
+
+  autofill_external_delegate_->WaitForPopupHidden();
+  EXPECT_TRUE(autofill_external_delegate_->popup_hidden());
+}
+#endif // !defined(OS_MACOSX)
+
+// This test checks that the browser doesn't crash if the delegate is deleted
+// before the popup is hidden.
+IN_PROC_BROWSER_TEST_F(AutofillPopupControllerBrowserTest,
+                       DeleteDelegateBeforePopupHidden){
+  GenerateTestAutofillPopup(autofill_external_delegate_.get());
+
+  // Delete the external delegate here so that is gets deleted before popup is
+  // hidden. This can happen if the web_contents are destroyed before the popup
+  // is hidden. See http://crbug.com/232475
+  autofill_external_delegate_.reset();
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/ui/autofill/data_model_wrapper.cc b/chrome/browser/ui/autofill/data_model_wrapper.cc
index 859131b..7a6a43c 100644
--- a/chrome/browser/ui/autofill/data_model_wrapper.cc
+++ b/chrome/browser/ui/autofill/data_model_wrapper.cc
@@ -168,6 +168,9 @@
 
 base::string16 AutofillCreditCardWrapper::GetInfo(const AutofillType& type)
     const {
+  if (type.group() != CREDIT_CARD)
+    return base::string16();
+
   if (type.GetStorableType() == CREDIT_CARD_EXP_MONTH)
     return MonthComboboxModel::FormatMonth(card_->expiration_month());
 
diff --git a/chrome/browser/ui/autofill/generated_credit_card_bubble_controller.h b/chrome/browser/ui/autofill/generated_credit_card_bubble_controller.h
index 0f6f638..89dff3e 100644
--- a/chrome/browser/ui/autofill/generated_credit_card_bubble_controller.h
+++ b/chrome/browser/ui/autofill/generated_credit_card_bubble_controller.h
@@ -60,8 +60,8 @@
   // Show a bubble to educate the user about generated (fronting) cards and how
   // they are used to bill their original (backing) card.
   static void Show(content::WebContents* contents,
-                   const base::string16& backing_card_name,
-                   const base::string16& fronting_card_name);
+                   const base::string16& fronting_card_name,
+                   const base::string16& backing_card_name);
 
   // content::WebContentsObserver:
   virtual void DidNavigateMainFrame(
diff --git a/chrome/browser/ui/autofill/generated_credit_card_bubble_controller_unittest.cc b/chrome/browser/ui/autofill/generated_credit_card_bubble_controller_unittest.cc
index 206f354..924e547 100644
--- a/chrome/browser/ui/autofill/generated_credit_card_bubble_controller_unittest.cc
+++ b/chrome/browser/ui/autofill/generated_credit_card_bubble_controller_unittest.cc
@@ -104,8 +104,8 @@
   void Show() {
     ASSERT_TRUE(controller()->IsInstalled());
     TestGeneratedCreditCardBubbleController::Show(test_web_contents_.get(),
-                                                  BackingCard(),
-                                                  FrontingCard());
+                                                  FrontingCard(),
+                                                  BackingCard());
   }
 
   void Navigate() {
@@ -173,10 +173,10 @@
 
   ASSERT_EQ(3U, ranges.size());
 
-  EXPECT_EQ(BackingCard(), RangeOfString(contents_text, ranges[0].range));
+  EXPECT_EQ(FrontingCard(), RangeOfString(contents_text, ranges[0].range));
   EXPECT_FALSE(ranges[0].is_link);
 
-  EXPECT_EQ(FrontingCard(), RangeOfString(contents_text, ranges[1].range));
+  EXPECT_EQ(BackingCard(), RangeOfString(contents_text, ranges[1].range));
   EXPECT_FALSE(ranges[1].is_link);
 
   EXPECT_TRUE(ranges[2].is_link);
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc b/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc
index 0d27929..551d831 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker_tab_helper.cc
@@ -20,6 +20,10 @@
 #include "content/public/browser/web_contents_view.h"
 #include "third_party/WebKit/public/web/WebWindowFeatures.h"
 
+#if defined(OS_ANDROID)
+#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
+#endif
+
 using WebKit::WebWindowFeatures;
 
 DEFINE_WEB_CONTENTS_USER_DATA_KEY(PopupBlockerTabHelper);
@@ -139,7 +143,11 @@
   BlockedRequest* popup = blocked_popups_.Lookup(id);
   if (!popup)
     return;
+#if defined(OS_ANDROID)
+  TabModelList::HandlePopupNavigation(&popup->params);
+#else
   chrome::Navigate(&popup->params);
+#endif
   if (popup->params.target_contents) {
     popup->params.target_contents->Send(new ChromeViewMsg_SetWindowFeatures(
         popup->params.target_contents->GetRoutingID(), popup->window_features));
diff --git a/chrome/browser/ui/bookmarks/bookmark_prompt_controller.cc b/chrome/browser/ui/bookmarks/bookmark_prompt_controller.cc
index 386669d..4e76dcd 100644
--- a/chrome/browser/ui/bookmarks/bookmark_prompt_controller.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_prompt_controller.cc
@@ -22,8 +22,8 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_version_info.h"
 #include "chrome/common/metrics/variations/variation_ids.h"
-#include "chrome/common/metrics/variations/variations_associated_data.h"
 #include "chrome/common/pref_names.h"
+#include "components/variations/variations_associated_data.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index cbf706b..9c12887 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -1685,6 +1685,26 @@
   return true;
 }
 
+gfx::Size Browser::GetSizeForNewRenderView(
+    const WebContents* web_contents) const {
+  // When navigating away from NTP with unpinned bookmark bar, the bookmark bar
+  // would disappear on non-NTP pages, resulting in a bigger size for the new
+  // render view.
+  gfx::Size size = web_contents->GetView()->GetContainerSize();
+  // Don't change render view size if bookmark bar is currently not detached,
+  // or there's no pending entry, or navigating to a NTP page.
+  if (size.IsEmpty() || bookmark_bar_state_ != BookmarkBar::DETACHED)
+    return size;
+  const NavigationEntry* pending_entry =
+      web_contents->GetController().GetPendingEntry();
+  if (pending_entry &&
+      !chrome::IsNTPURL(pending_entry->GetVirtualURL(), profile_)) {
+    size.Enlarge(
+        0, window()->GetRenderViewHeightInsetWithDetachedBookmarkBar());
+  }
+  return size;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Browser, CoreTabHelperDelegate implementation:
 
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 7710549..ffe9677 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -653,6 +653,8 @@
       const GURL& url,
       const base::FilePath& plugin_path,
       const base::Callback<void(bool)>& callback) OVERRIDE;
+  virtual gfx::Size GetSizeForNewRenderView(
+      const content::WebContents* web_contents) const OVERRIDE;
 
   // Overridden from CoreTabHelperDelegate:
   // Note that the caller is responsible for deleting |old_contents|.
diff --git a/chrome/browser/ui/browser_browsertest.cc b/chrome/browser/ui/browser_browsertest.cc
index e176b52..70a2609 100644
--- a/chrome/browser/ui/browser_browsertest.cc
+++ b/chrome/browser/ui/browser_browsertest.cc
@@ -67,6 +67,7 @@
 #include "content/public/browser/resource_context.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_view.h"
 #include "content/public/common/page_transition_types.h"
 #include "content/public/common/renderer_preferences.h"
 #include "content/public/common/url_constants.h"
@@ -196,7 +197,7 @@
       content::ResourceContext* resource_context,
       const GURL& current_url,
       const GURL& new_url) OVERRIDE {
-    return new_url.SchemeIs(chrome::kHttpsScheme);
+    return new_url.SchemeIs(content::kHttpsScheme);
   }
 };
 
@@ -237,6 +238,89 @@
   InterstitialPage* interstitial_page_;  // Owns us.
 };
 
+class RenderViewSizeObserver : public content::WebContentsObserver {
+ public:
+  RenderViewSizeObserver(content::WebContents* web_contents,
+                         BrowserWindow* browser_window)
+      : WebContentsObserver(web_contents),
+        browser_window_(browser_window) {
+  }
+
+  void GetSizeForRenderViewHost(
+      content::RenderViewHost* render_view_host,
+      gfx::Size* rwhv_create_size,
+      gfx::Size* rwhv_commit_size,
+      gfx::Size* wcv_commit_size) {
+    RenderViewSizes::const_iterator result = render_view_sizes_.end();
+    result = render_view_sizes_.find(render_view_host);
+    if (result != render_view_sizes_.end()) {
+      *rwhv_create_size = result->second.rwhv_create_size;
+      *rwhv_commit_size = result->second.rwhv_commit_size;
+      *wcv_commit_size = result->second.wcv_commit_size;
+    }
+  }
+
+  void set_wcv_resize_insets(const gfx::Size& wcv_resize_insets) {
+    wcv_resize_insets_ = wcv_resize_insets;
+  }
+
+  // Cache the size when RenderViewHost is first created.
+  virtual void RenderViewCreated(
+      content::RenderViewHost* render_view_host) OVERRIDE {
+    render_view_sizes_[render_view_host].rwhv_create_size =
+        render_view_host->GetView()->GetViewBounds().size();
+  }
+
+  // Enlarge WebContentsView by |wcv_resize_insets_| while the navigation entry
+  // is pending.
+  virtual void NavigateToPendingEntry(
+      const GURL& url,
+      NavigationController::ReloadType reload_type) OVERRIDE {
+    if (wcv_resize_insets_.IsEmpty())
+      return;
+    // Resizing the main browser window by |wcv_resize_insets_| will
+    // automatically resize the WebContentsView by the same amount.
+    // Just resizing WebContentsView directly doesn't work on Linux, because the
+    // next automatic layout of the browser window will resize WebContentsView
+    // back to the previous size.  To make it consistent, resize main browser
+    // window on all platforms.
+    gfx::Rect bounds(browser_window_->GetBounds());
+    gfx::Size size(bounds.size());
+    size.Enlarge(wcv_resize_insets_.width(), wcv_resize_insets_.height());
+    bounds.set_size(size);
+    browser_window_->SetBounds(bounds);
+    // Let the message loop run so that resize actually takes effect.
+    content::RunAllPendingInMessageLoop();
+  }
+
+  // Cache the sizes of RenderWidgetHostView and WebContentsView when the
+  // navigation entry is committed, which is before
+  // WebContentsDelegate::DidNavigateMainFramePostCommit is called.
+  virtual void NavigationEntryCommitted(
+      const content::LoadCommittedDetails& details) OVERRIDE {
+    content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
+    render_view_sizes_[rvh].rwhv_commit_size =
+        web_contents()->GetRenderWidgetHostView()->GetViewBounds().size();
+    render_view_sizes_[rvh].wcv_commit_size =
+        web_contents()->GetView()->GetContainerSize();
+  }
+
+ private:
+  struct Sizes {
+    gfx::Size rwhv_create_size;  // Size of RenderWidgetHostView when created.
+    gfx::Size rwhv_commit_size;  // Size of RenderWidgetHostView when committed.
+    gfx::Size wcv_commit_size;   // Size of WebContentsView when committed.
+  };
+
+  typedef std::map<content::RenderViewHost*, Sizes> RenderViewSizes;
+  RenderViewSizes render_view_sizes_;
+  // Enlarge WebContentsView by this size insets in NavigateToPendingEntry.
+  gfx::Size wcv_resize_insets_;
+  BrowserWindow* browser_window_;  // Weak ptr.
+
+  DISALLOW_COPY_AND_ASSIGN(RenderViewSizeObserver);
+};
+
 }  // namespace
 
 class BrowserTest : public ExtensionBrowserTest {
@@ -871,7 +955,7 @@
                                      base::FilePath(kDocRoot));
   ASSERT_TRUE(test_server.Start());
   GURL https_url(test_server.GetURL("/"));
-  ASSERT_TRUE(https_url.SchemeIs(chrome::kHttpsScheme));
+  ASSERT_TRUE(https_url.SchemeIs(content::kHttpsScheme));
   ui_test_utils::NavigateToURL(browser(), https_url);
   EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_CREATE_SHORTCUTS));
 }
@@ -2243,3 +2327,97 @@
   WindowOpenDisposition disposition = NEW_FOREGROUND_TAB;
   RunTest(browser(), GetHrefURL(), modifiers, button, disposition);
 }
+
+// TODO(sail): enable this for MAC when
+// BrowserWindowCocoa::GetRenderViewHeightInsetWithDetachedBookmarkBar
+// is fixed.
+#if defined(OS_MACOSX)
+#define MAYBE_GetSizeForNewRenderView DISABLED_GetSizeForNewRenderView
+#else
+#define MAYBE_GetSizeForNewRenderView GetSizeForNewRenderView
+#endif
+IN_PROC_BROWSER_TEST_F(BrowserTest, MAYBE_GetSizeForNewRenderView) {
+  // Start with NTP.
+  ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab"));
+  ASSERT_EQ(BookmarkBar::DETACHED, browser()->bookmark_bar_state());
+  WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  content::RenderViewHost* prev_rvh = web_contents->GetRenderViewHost();
+  const int height_inset =
+      browser()->window()->GetRenderViewHeightInsetWithDetachedBookmarkBar();
+  const gfx::Size initial_wcv_size =
+      web_contents->GetView()->GetContainerSize();
+  RenderViewSizeObserver observer(web_contents, browser()->window());
+
+  // Navigate to a non-NTP page, without resizing WebContentsView.
+  ui_test_utils::NavigateToURL(browser(), GURL("http://foo0.com"));
+  ASSERT_EQ(BookmarkBar::HIDDEN, browser()->bookmark_bar_state());
+  // A new RenderViewHost should be created.
+  EXPECT_NE(prev_rvh, web_contents->GetRenderViewHost());
+  prev_rvh = web_contents->GetRenderViewHost();
+  gfx::Size rwhv_create_size0, rwhv_commit_size0, wcv_commit_size0;
+  observer.GetSizeForRenderViewHost(web_contents->GetRenderViewHost(),
+                                    &rwhv_create_size0,
+                                    &rwhv_commit_size0,
+                                    &wcv_commit_size0);
+  // The create height of RenderWidgetHostView should include the height inset.
+  EXPECT_EQ(gfx::Size(initial_wcv_size.width(),
+                      initial_wcv_size.height() + height_inset),
+            rwhv_create_size0);
+  // When a navigation entry is committed, the size of RenderWidgetHostView
+  // should be the same as when it was first created.
+  EXPECT_EQ(rwhv_create_size0, rwhv_commit_size0);
+  // Sizes of the current RenderWidgetHostView and WebContentsView should not
+  // change before and after WebContentsDelegate::DidNavigateMainFramePostCommit
+  // (implemented by Browser); we obtain the sizes before PostCommit via
+  // WebContentsObserver::NavigationEntryCommitted (implemented by
+  // RenderViewSizeObserver).
+  EXPECT_EQ(rwhv_commit_size0,
+            web_contents->GetRenderWidgetHostView()->GetViewBounds().size());
+  EXPECT_EQ(wcv_commit_size0, web_contents->GetView()->GetContainerSize());
+
+  // Navigate to another non-NTP page, without resizing WebContentsView.
+  ui_test_utils::NavigateToURL(browser(), GURL("http://foo1.com"));
+  ASSERT_EQ(BookmarkBar::HIDDEN, browser()->bookmark_bar_state());
+  // A new RenderVieHost should be created.
+  EXPECT_NE(prev_rvh, web_contents->GetRenderViewHost());
+  gfx::Size rwhv_create_size1, rwhv_commit_size1, wcv_commit_size1;
+  observer.GetSizeForRenderViewHost(web_contents->GetRenderViewHost(),
+                                    &rwhv_create_size1,
+                                    &rwhv_commit_size1,
+                                    &wcv_commit_size1);
+  EXPECT_EQ(rwhv_create_size1, rwhv_commit_size1);
+  EXPECT_EQ(rwhv_commit_size1,
+            web_contents->GetRenderWidgetHostView()->GetViewBounds().size());
+  EXPECT_EQ(wcv_commit_size1, web_contents->GetView()->GetContainerSize());
+
+  // Navigate from NTP to a non-NTP page, resizing WebContentsView while
+  // navigation entry is pending.
+  ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab"));
+  gfx::Size wcv_resize_insets(-34, -57);
+  observer.set_wcv_resize_insets(wcv_resize_insets);
+  ui_test_utils::NavigateToURL(browser(), GURL("http://foo2.com"));
+  ASSERT_EQ(BookmarkBar::HIDDEN, browser()->bookmark_bar_state());
+  gfx::Size rwhv_create_size2, rwhv_commit_size2, wcv_commit_size2;
+  observer.GetSizeForRenderViewHost(web_contents->GetRenderViewHost(),
+                                    &rwhv_create_size2,
+                                    &rwhv_commit_size2,
+                                    &wcv_commit_size2);
+  // The create height of RenderWidgetHostView should include the height inset.
+  EXPECT_EQ(gfx::Size(initial_wcv_size.width(),
+                      initial_wcv_size.height() + height_inset),
+            rwhv_create_size2);
+  // WebContentsView was resized in
+  // RenderViewSizeObserver::NavigateToPendingEntry after RenderWidgetHostView
+  // was created, so the commit size should be resized accordingly.
+  gfx::Size exp_commit_size(initial_wcv_size);
+  exp_commit_size.Enlarge(wcv_resize_insets.width(),
+                          wcv_resize_insets.height() + height_inset);
+  EXPECT_EQ(exp_commit_size, rwhv_commit_size2);
+  EXPECT_EQ(exp_commit_size, wcv_commit_size2);
+  // Sizes of RenderWidgetHostView and WebContentsView before and after
+  // WebContentsDelegate::DidNavigateMainFramePostCommit should be the same.
+  EXPECT_EQ(rwhv_commit_size2,
+            web_contents->GetRenderWidgetHostView()->GetViewBounds().size());
+  EXPECT_EQ(wcv_commit_size2, web_contents->GetView()->GetContainerSize());
+}
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index 16d4f41..27ed93c 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -60,6 +60,18 @@
 
 namespace {
 
+enum WindowState {
+  // Not in fullscreen mode.
+  WINDOW_STATE_NOT_FULLSCREEN,
+
+  // Fullscreen mode, occupying the whole screen.
+  WINDOW_STATE_FULLSCREEN,
+
+  // Fullscreen mode for metro snap, occupying the full height and 20% of
+  // the screen width.
+  WINDOW_STATE_METRO_SNAP,
+};
+
 // Returns |true| if entry has an internal chrome:// URL, |false| otherwise.
 bool HasInternalURL(const NavigationEntry* entry) {
   if (!entry)
@@ -197,6 +209,12 @@
       prefs::kPrintingEnabled,
       base::Bind(&BrowserCommandController::UpdatePrintingState,
                  base::Unretained(this)));
+#if !defined(OS_MACOSX)
+  profile_pref_registrar_.Add(
+      prefs::kFullscreenAllowed,
+      base::Bind(&BrowserCommandController::UpdateCommandsForFullscreenMode,
+                 base::Unretained(this)));
+#endif
   pref_signin_allowed_.Init(
       prefs::kSigninAllowed,
       profile()->GetOriginalProfile()->GetPrefs(),
@@ -286,16 +304,7 @@
 }
 
 void BrowserCommandController::FullscreenStateChanged() {
-  FullScreenMode fullscreen_mode = FULLSCREEN_DISABLED;
-  if (window()->IsFullscreen()) {
-#if defined(OS_WIN)
-    fullscreen_mode = window()->IsInMetroSnapMode() ? FULLSCREEN_METRO_SNAP :
-                                                      FULLSCREEN_NORMAL;
-#else
-    fullscreen_mode = FULLSCREEN_NORMAL;
-#endif
-  }
-  UpdateCommandsForFullscreenMode(fullscreen_mode);
+  UpdateCommandsForFullscreenMode();
 }
 
 void BrowserCommandController::PrintingStateChanged() {
@@ -936,7 +945,7 @@
   command_updater_.UpdateCommandEnabled(IDC_TOGGLE_SPEECH_INPUT, true);
 
   // Initialize other commands whose state changes based on fullscreen mode.
-  UpdateCommandsForFullscreenMode(FULLSCREEN_DISABLED);
+  UpdateCommandsForFullscreenMode();
 
   UpdateCommandsForContentRestrictionState();
 
@@ -1087,11 +1096,18 @@
   UpdateOpenFileState(&command_updater_);
 }
 
-void BrowserCommandController::UpdateCommandsForFullscreenMode(
-    FullScreenMode fullscreen_mode) {
+void BrowserCommandController::UpdateCommandsForFullscreenMode() {
+  WindowState window_state = WINDOW_STATE_NOT_FULLSCREEN;
+  if (window() && window()->IsFullscreen()) {
+    window_state = WINDOW_STATE_FULLSCREEN;
+#if defined(OS_WIN)
+    if (window()->IsInMetroSnapMode())
+      window_state = WINDOW_STATE_METRO_SNAP;
+#endif
+  }
   bool show_main_ui = IsShowingMainUI();
-  bool main_not_fullscreen = show_main_ui &&
-                             (fullscreen_mode == FULLSCREEN_DISABLED);
+  bool main_not_fullscreen =
+      show_main_ui && window_state == WINDOW_STATE_NOT_FULLSCREEN;
 
   // Navigation commands
   command_updater_.UpdateCommandEnabled(IDC_OPEN_CURRENT_URL, show_main_ui);
@@ -1099,7 +1115,8 @@
   // Window management commands
   command_updater_.UpdateCommandEnabled(
       IDC_SHOW_AS_TAB,
-      !browser_->is_type_tabbed() && fullscreen_mode == FULLSCREEN_DISABLED);
+      !browser_->is_type_tabbed() &&
+          window_state == WINDOW_STATE_NOT_FULLSCREEN);
 
   // Focus various bits of UI
   command_updater_.UpdateCommandEnabled(IDC_FOCUS_TOOLBAR, show_main_ui);
@@ -1138,13 +1155,19 @@
 #endif
 
   // Disable explicit fullscreen toggling when in metro snap mode.
-  bool fullscreen_enabled = fullscreen_mode != FULLSCREEN_METRO_SNAP;
+  bool fullscreen_enabled = window_state != WINDOW_STATE_METRO_SNAP;
 #if defined(OS_MACOSX)
   // The Mac implementation doesn't support switching to fullscreen while
   // a tab modal dialog is displayed.
   int tab_index = chrome::IndexOfFirstBlockedTab(browser_->tab_strip_model());
   bool has_blocked_tab = tab_index != browser_->tab_strip_model()->count();
   fullscreen_enabled &= !has_blocked_tab;
+#else
+  if (window_state == WINDOW_STATE_NOT_FULLSCREEN &&
+      !profile()->GetPrefs()->GetBoolean(prefs::kFullscreenAllowed)) {
+    // Disable toggling into fullscreen mode if disallowed by pref.
+    fullscreen_enabled = false;
+  }
 #endif
 
   command_updater_.UpdateCommandEnabled(IDC_FULLSCREEN, fullscreen_enabled);
diff --git a/chrome/browser/ui/browser_command_controller.h b/chrome/browser/ui/browser_command_controller.h
index 7c6a8d4..f46751c 100644
--- a/chrome/browser/ui/browser_command_controller.h
+++ b/chrome/browser/ui/browser_command_controller.h
@@ -79,18 +79,6 @@
  private:
   class InterstitialObserver;
 
-  enum FullScreenMode {
-    // Not in fullscreen mode.
-    FULLSCREEN_DISABLED,
-
-    // Fullscreen mode, occupying the whole screen.
-    FULLSCREEN_NORMAL,
-
-    // Fullscreen mode for metro snap, occupying the full height and 20% of
-    // the screen width.
-    FULLSCREEN_METRO_SNAP,
-  };
-
   // Overridden from CommandUpdaterDelegate:
   virtual void ExecuteCommandWithDisposition(
       int id,
@@ -151,7 +139,7 @@
 
   // Update commands whose state depends on the type of fullscreen mode the
   // window is in.
-  void UpdateCommandsForFullscreenMode(FullScreenMode fullscreen_mode);
+  void UpdateCommandsForFullscreenMode();
 
   // Update commands whose state depends on whether multiple profiles are
   // allowed.
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index 46ce8f1..48c7076 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -41,6 +41,7 @@
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/browser/ui/find_bar/find_bar.h"
 #include "chrome/browser/ui/find_bar/find_bar_controller.h"
 #include "chrome/browser/ui/find_bar/find_tab_helper.h"
 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h"
@@ -71,10 +72,6 @@
 #include "net/base/escape.h"
 #include "webkit/common/user_agent/user_agent_util.h"
 
-#if defined(OS_MACOSX)
-#include "ui/base/cocoa/find_pasteboard.h"
-#endif
-
 #if defined(OS_WIN)
 #include "chrome/browser/ui/metro_pin_tab_helper_win.h"
 #include "win8/util/win8_util.h"
@@ -829,12 +826,12 @@
     FindTabHelper* find_helper = FindTabHelper::FromWebContents(
         browser->tab_strip_model()->GetActiveWebContents());
 #if defined(OS_MACOSX)
-    // We always want to search for the contents of the find pasteboard on OS X.
-    // But Incognito window doesn't write to the find pboard. Therefore, its own
-    // find text has higher priority.
-    if (!browser->profile()->IsOffTheRecord() ||
-        find_helper->find_text().empty())
-      find_text = GetFindPboardText();
+    // We always want to search for the current contents of the find bar on
+    // OS X. For regular profile it's always the current find pboard. For
+    // Incognito window it's the newest value of the find pboard content and
+    // user-typed text.
+    FindBar* find_bar = browser->GetFindBarController()->find_bar();
+    find_text = find_bar->GetFindText();
 #endif
     find_helper->StartFinding(find_text, forward_direction, false);
   }
diff --git a/chrome/browser/ui/browser_instant_controller.cc b/chrome/browser/ui/browser_instant_controller.cc
index 2e6c12d..662eca7 100644
--- a/chrome/browser/ui/browser_instant_controller.cc
+++ b/chrome/browser/ui/browser_instant_controller.cc
@@ -240,6 +240,11 @@
   instant_.SetOmniboxBounds(bounds);
 }
 
+void BrowserInstantController::SetSuggestionToPrefetch(
+    const InstantSuggestion& suggestion) {
+  instant_.SetSuggestionToPrefetch(suggestion);
+}
+
 void BrowserInstantController::ToggleVoiceSearch() {
   instant_.ToggleVoiceSearch();
 }
diff --git a/chrome/browser/ui/browser_instant_controller.h b/chrome/browser/ui/browser_instant_controller.h
index 8ddcb48..66598ef 100644
--- a/chrome/browser/ui/browser_instant_controller.h
+++ b/chrome/browser/ui/browser_instant_controller.h
@@ -80,6 +80,9 @@
   // Sets the stored omnibox bounds.
   void SetOmniboxBounds(const gfx::Rect& bounds);
 
+  // Sets the current query to prefetch if any.
+  void SetSuggestionToPrefetch(const InstantSuggestion& suggestion);
+
   // Notifies |instant_| to toggle voice search.
   void ToggleVoiceSearch();
 
diff --git a/chrome/browser/ui/browser_tab_contents.cc b/chrome/browser/ui/browser_tab_contents.cc
index 56b4433..6697a95 100644
--- a/chrome/browser/ui/browser_tab_contents.cc
+++ b/chrome/browser/ui/browser_tab_contents.cc
@@ -17,6 +17,9 @@
 #include "chrome/browser/net/load_time_stats.h"
 #include "chrome/browser/net/net_error_tab_helper.h"
 #include "chrome/browser/net/predictor_tab_helper.h"
+#if !defined(OS_ANDROID)
+#include "chrome/browser/network_time/navigation_time_helper.h"
+#endif
 #include "chrome/browser/password_manager/password_generation_manager.h"
 #include "chrome/browser/password_manager/password_manager.h"
 #include "chrome/browser/password_manager/password_manager_delegate_impl.h"
@@ -153,6 +156,9 @@
   ThumbnailTabHelper::CreateForWebContents(web_contents);
   TranslateTabHelper::CreateForWebContents(web_contents);
   ZoomController::CreateForWebContents(web_contents);
+#if !defined(OS_ANDROID)
+  NavigationTimeHelper::CreateForWebContents(web_contents);
+#endif
 
 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
   captive_portal::CaptivePortalTabHelper::CreateForWebContents(web_contents);
diff --git a/chrome/browser/ui/browser_ui_prefs.cc b/chrome/browser/ui/browser_ui_prefs.cc
index 7334b2c..d17ec12 100644
--- a/chrome/browser/ui/browser_ui_prefs.cc
+++ b/chrome/browser/ui/browser_ui_prefs.cc
@@ -191,6 +191,12 @@
       prefs::kHideWebStoreIcon,
       false,
       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+#if !defined(OS_MACOSX)
+  registry->RegisterBooleanPref(
+      prefs::kFullscreenAllowed,
+      true,
+      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+#endif
 }
 
 void RegisterAppPrefs(const std::string& app_name, Profile* profile) {
diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h
index ad1259d..39c2650 100644
--- a/chrome/browser/ui/browser_window.h
+++ b/chrome/browser/ui/browser_window.h
@@ -362,6 +362,11 @@
   // amount of overscroll that has occured in the y-direction.
   virtual void OverscrollUpdate(int delta_y) {}
 
+  // Returns the height inset for RenderView when detached bookmark bar is
+  // shown.  Invoked when a new RenderHostView is created for a non-NTP
+  // navigation entry and the bookmark bar is detached.
+  virtual int GetRenderViewHeightInsetWithDetachedBookmarkBar() = 0;
+
  protected:
   friend class BrowserCloseManager;
   friend class BrowserView;
diff --git a/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac.h b/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac.h
new file mode 100644
index 0000000..111b9de
--- /dev/null
+++ b/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac.h
@@ -0,0 +1,28 @@
+// 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 CHROME_BROWSER_UI_COCOA_APPS_APP_SHIM_MENU_CONTROLLER_MAC_H_
+#define CHROME_BROWSER_UI_COCOA_APPS_APP_SHIM_MENU_CONTROLLER_MAC_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/mac/scoped_nsobject.h"
+
+// This controller listens to NSWindowDidBecomeMainNotification and
+// NSWindowDidResignMainNotification and modifies the main menu bar to mimic a
+// main menu for the app. When an app window becomes main, all Chrome menu items
+// are hidden and menu items for the app are appended to the main menu. When the
+// app window resigns main, its menu items are removed and all Chrome menu items
+// are unhidden.
+@interface AppShimMenuController : NSObject {
+ @private
+  // The extension id of the currently focused packaged app.
+  base::scoped_nsobject<NSString> appId_;
+  // A menu item for the currently focused packaged app.
+  base::scoped_nsobject<NSMenuItem> appMenuItem_;
+}
+
+@end
+
+#endif  // CHROME_BROWSER_UI_COCOA_APPS_APP_SHIM_MENU_CONTROLLER_MAC_H_
diff --git a/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac.mm b/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac.mm
new file mode 100644
index 0000000..6ecfb4e
--- /dev/null
+++ b/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac.mm
@@ -0,0 +1,129 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac.h"
+
+#include "apps/app_shim/extension_app_shim_handler_mac.h"
+#include "apps/shell_window.h"
+#include "apps/shell_window_registry.h"
+#include "base/strings/sys_string_conversions.h"
+#import "chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h"
+#include "chrome/common/extensions/extension.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+
+@interface AppShimMenuController ()
+// Construct the NSMenuItems for apps.
+- (void)buildAppMenuItems;
+// Register for NSWindow notifications.
+- (void)registerEventHandlers;
+// If the window is an app window, add or remove menu items.
+- (void)windowMainStatusChanged:(NSNotification*)notification;
+// Add menu items for an app and hide Chrome menu items.
+- (void)addMenuItems:(const extensions::Extension*)app;
+// If the window belongs to the currently focused app, remove the menu items and
+// unhide Chrome menu items.
+- (void)removeMenuItems:(NSString*)appId;
+@end
+
+@implementation AppShimMenuController
+
+- (id)init {
+  if ((self = [super init])) {
+    [self buildAppMenuItems];
+    [self registerEventHandlers];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  [[NSNotificationCenter defaultCenter] removeObserver:self];
+  [super dealloc];
+}
+
+- (void)buildAppMenuItems {
+  appMenuItem_.reset([[NSMenuItem alloc] initWithTitle:@""
+                                                action:nil
+                                         keyEquivalent:@""]);
+  base::scoped_nsobject<NSMenu> appMenu([[NSMenu alloc] initWithTitle:@""]);
+  [appMenuItem_ setSubmenu:appMenu];
+}
+
+- (void)registerEventHandlers {
+  [[NSNotificationCenter defaultCenter]
+      addObserver:self
+         selector:@selector(windowMainStatusChanged:)
+             name:NSWindowDidBecomeMainNotification
+           object:nil];
+
+  [[NSNotificationCenter defaultCenter]
+      addObserver:self
+         selector:@selector(windowMainStatusChanged:)
+             name:NSWindowDidResignMainNotification
+           object:nil];
+
+  [[NSNotificationCenter defaultCenter]
+      addObserver:self
+         selector:@selector(windowMainStatusChanged:)
+             name:NSWindowWillCloseNotification
+           object:nil];
+}
+
+- (void)windowMainStatusChanged:(NSNotification*)notification {
+  id window = [notification object];
+  id windowController = [window windowController];
+  if (![windowController isKindOfClass:[NativeAppWindowController class]])
+    return;
+
+  apps::ShellWindow* shellWindow =
+      apps::ShellWindowRegistry::GetShellWindowForNativeWindowAnyProfile(
+          window);
+  if (!shellWindow)
+    return;
+
+  NSString* name = [notification name];
+  if ([name isEqualToString:NSWindowDidBecomeMainNotification]) {
+    [self addMenuItems:shellWindow->extension()];
+  } else if ([name isEqualToString:NSWindowDidResignMainNotification] ||
+             [name isEqualToString:NSWindowWillCloseNotification]) {
+    [self removeMenuItems:base::SysUTF8ToNSString(shellWindow->extension_id())];
+  } else {
+    NOTREACHED();
+  }
+}
+
+- (void)addMenuItems:(const extensions::Extension*)app {
+  NSString* appId = base::SysUTF8ToNSString(app->id());
+  NSString* title = base::SysUTF8ToNSString(app->name());
+
+  if ([appId_ isEqualToString:appId])
+    return;
+
+  [self removeMenuItems:appId_];
+  appId_.reset([appId copy]);
+
+  NSMenu* mainMenu = [NSApp mainMenu];
+  for (NSMenuItem* item in [mainMenu itemArray])
+    [item setHidden:YES];
+
+  [appMenuItem_ setTitle:appId];
+  [[appMenuItem_ submenu] setTitle:title];
+  [mainMenu addItem:appMenuItem_];
+}
+
+- (void)removeMenuItems:(NSString*)appId {
+  if (![appId_ isEqualToString:appId])
+    return;
+
+  appId_.reset();
+
+  NSMenu* mainMenu = [NSApp mainMenu];
+  [mainMenu removeItem:appMenuItem_];
+
+  // Restore the Chrome main menu bar.
+  for (NSMenuItem* item in [mainMenu itemArray])
+    [item setHidden:NO];
+}
+
+@end
diff --git a/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac_browsertest.mm b/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac_browsertest.mm
new file mode 100644
index 0000000..f9a3031
--- /dev/null
+++ b/chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac_browsertest.mm
@@ -0,0 +1,132 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac.h"
+
+#import <Cocoa/Cocoa.h>
+
+#include "apps/shell_window_registry.h"
+#include "base/command_line.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/strings/sys_string_conversions.h"
+#include "chrome/browser/apps/app_browsertest_util.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_test_message_listener.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/extension.h"
+
+namespace {
+
+class AppShimMenuControllerBrowserTest
+    : public extensions::PlatformAppBrowserTest {
+ protected:
+  AppShimMenuControllerBrowserTest()
+      : app_1_(NULL),
+        app_2_(NULL),
+        initial_menu_item_count_(0) {}
+
+  virtual ~AppShimMenuControllerBrowserTest() {}
+
+  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+    PlatformAppBrowserTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitch(switches::kEnableAppShims);
+  }
+
+  // Start two apps and wait for them to be launched.
+  void SetUpApps() {
+    ExtensionTestMessageListener listener_1("Launched", false);
+    app_1_ = InstallAndLaunchPlatformApp("minimal_id");
+    ASSERT_TRUE(listener_1.WaitUntilSatisfied());
+    ExtensionTestMessageListener listener_2("Launched", false);
+    app_2_ = InstallAndLaunchPlatformApp("minimal");
+    ASSERT_TRUE(listener_2.WaitUntilSatisfied());
+
+    initial_menu_item_count_ = [[[NSApp mainMenu] itemArray] count];
+  }
+
+  void CheckHasAppMenu(const extensions::Extension* app) const {
+    NSArray* item_array = [[NSApp mainMenu] itemArray];
+    EXPECT_EQ(initial_menu_item_count_ + 1, [item_array count]);
+    for (NSUInteger i = 0; i < initial_menu_item_count_; ++i)
+      EXPECT_TRUE([[item_array objectAtIndex:i] isHidden]);
+    NSMenuItem* last_item = [item_array lastObject];
+    EXPECT_EQ(app->id(), base::SysNSStringToUTF8([last_item title]));
+    EXPECT_EQ(app->name(),
+              base::SysNSStringToUTF8([[last_item submenu] title]));
+    EXPECT_FALSE([last_item isHidden]);
+  }
+
+  void CheckNoAppMenu() const {
+    NSArray* item_array = [[NSApp mainMenu] itemArray];
+    EXPECT_EQ(initial_menu_item_count_, [item_array count]);
+    for (NSUInteger i = 0; i < initial_menu_item_count_; ++i)
+      EXPECT_FALSE([[item_array objectAtIndex:i] isHidden]);
+  }
+
+  const extensions::Extension* app_1_;
+  const extensions::Extension* app_2_;
+  NSUInteger initial_menu_item_count_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppShimMenuControllerBrowserTest);
+};
+
+// Test that focusing an app window changes the menu bar.
+IN_PROC_BROWSER_TEST_F(AppShimMenuControllerBrowserTest,
+                       PlatformAppFocusUpdatesMenuBar) {
+  SetUpApps();
+  // When an app is focused, all Chrome menu items should be hidden, and a menu
+  // item for the app should be added.
+  apps::ShellWindow* app_1_shell_window =
+      apps::ShellWindowRegistry::Get(profile())->
+          GetShellWindowsForApp(app_1_->id()).front();
+  [[NSNotificationCenter defaultCenter]
+      postNotificationName:NSWindowDidBecomeMainNotification
+                    object:app_1_shell_window->GetNativeWindow()];
+  CheckHasAppMenu(app_1_);
+
+  // When another app is focused, the menu item for the app should change.
+  apps::ShellWindow* app_2_shell_window =
+      apps::ShellWindowRegistry::Get(profile())->
+          GetShellWindowsForApp(app_2_->id()).front();
+  [[NSNotificationCenter defaultCenter]
+      postNotificationName:NSWindowDidBecomeMainNotification
+                    object:app_2_shell_window->GetNativeWindow()];
+  CheckHasAppMenu(app_2_);
+
+  // When the app window loses focus, the menu items for the app should be
+  // removed.
+  [[NSNotificationCenter defaultCenter]
+      postNotificationName:NSWindowDidResignMainNotification
+                    object:app_2_shell_window->GetNativeWindow()];
+  CheckNoAppMenu();
+
+  // When an app window is closed, the menu items for the app should be removed.
+  [[NSNotificationCenter defaultCenter]
+      postNotificationName:NSWindowDidBecomeMainNotification
+                    object:app_2_shell_window->GetNativeWindow()];
+  CheckHasAppMenu(app_2_);
+  [[NSNotificationCenter defaultCenter]
+      postNotificationName:NSWindowWillCloseNotification
+                    object:app_2_shell_window->GetNativeWindow()];
+  CheckNoAppMenu();
+}
+
+IN_PROC_BROWSER_TEST_F(AppShimMenuControllerBrowserTest,
+                       ExtensionUninstallUpdatesMenuBar) {
+  SetUpApps();
+
+  apps::ShellWindow* app_1_shell_window =
+      apps::ShellWindowRegistry::Get(profile())->
+          GetShellWindowsForApp(app_1_->id()).front();
+  [[NSNotificationCenter defaultCenter]
+      postNotificationName:NSWindowDidBecomeMainNotification
+                    object:app_1_shell_window->GetNativeWindow()];
+
+  CheckHasAppMenu(app_1_);
+  ExtensionService::UninstallExtensionHelper(extension_service(), app_1_->id());
+  CheckNoAppMenu();
+}
+
+}  // namespace
diff --git a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h
index 89182fa..9a2eaf3 100644
--- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h
+++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h
@@ -102,6 +102,7 @@
   // the draggable region.
   bool IsWithinDraggableRegion(NSPoint point) const;
 
+  NSRect restored_bounds() const { return restored_bounds_; }
   bool use_system_drag() const { return use_system_drag_; }
 
  protected:
@@ -119,6 +120,12 @@
   virtual void RenderViewHostChanged() OVERRIDE;
   virtual gfx::Insets GetFrameInsets() const OVERRIDE;
 
+  // These are used to simulate Mac-style hide/show. Since windows can be hidden
+  // and shown using the app.window API, this sets is_hidden_with_app_ to
+  // differentiate the reason a window was hidden.
+  virtual void ShowWithApp() OVERRIDE;
+  virtual void HideWithApp() OVERRIDE;
+
   // WebContentsModalDialogHost implementation.
   virtual gfx::NativeView GetHostView() const OVERRIDE;
   virtual gfx::Point GetDialogPosition(const gfx::Size& size) OVERRIDE;
@@ -154,10 +161,23 @@
   void UpdateDraggableRegionsForCustomDrag(
       const std::vector<extensions::DraggableRegion>& regions);
 
+  // Cache |restored_bounds_| only if the window is currently restored.
+  void UpdateRestoredBounds();
+
+  // Hides the window unconditionally. Used by Hide and HideWithApp.
+  void HideWithoutMarkingHidden();
+
   apps::ShellWindow* shell_window_; // weak - ShellWindow owns NativeAppWindow.
 
   bool has_frame_;
 
+  // Whether this window is hidden according to the app.window API. This is set
+  // by Hide, Show, and ShowInactive.
+  bool is_hidden_;
+  // Whether this window last became hidden due to a request to hide the entire
+  // app, e.g. via the dock menu or Cmd+H. This is set by Hide/ShowWithApp.
+  bool is_hidden_with_app_;
+
   bool is_maximized_;
   bool is_fullscreen_;
   NSRect restored_bounds_;
diff --git a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm
index 4904243..a3281d8 100644
--- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm
+++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h"
 
+#include "apps/app_shim/extension_app_shim_handler_mac.h"
 #include "base/command_line.h"
 #include "base/mac/mac_util.h"
 #include "base/strings/sys_string_conversions.h"
@@ -22,6 +23,24 @@
 #include "content/public/browser/web_contents_view.h"
 #include "third_party/skia/include/core/SkRegion.h"
 
+// NOTE: State Before Update.
+//
+// Internal state, such as |is_maximized_|, must be set before the window
+// state is changed so that it is accurate when e.g. a resize results in a call
+// to |OnNativeWindowChanged|.
+
+// NOTE: Maximize and Zoom.
+//
+// Zooming is implemented manually in order to implement maximize functionality
+// and to support non resizable windows. The window will be resized explicitly
+// in the |WindowWillZoom| call.
+//
+// Attempting maximize and restore functionality with non resizable windows
+// using the native zoom method did not work, even with
+// windowWillUseStandardFrame, as the window would not restore back to the
+// desired size.
+
+
 using apps::ShellWindow;
 
 @interface NSWindow (NSPrivateApis)
@@ -85,7 +104,7 @@
                  toFrame:(NSRect)newFrame {
   if (appWindow_)
     appWindow_->WindowWillZoom();
-  return YES;
+  return NO;  // See top of file NOTE: Maximize and Zoom.
 }
 
 // Allow non resizable windows (without NSResizableWindowMask) to enter
@@ -216,6 +235,8 @@
     const ShellWindow::CreateParams& params)
     : shell_window_(shell_window),
       has_frame_(params.frame == ShellWindow::FRAME_CHROME),
+      is_hidden_(false),
+      is_hidden_with_app_(false),
       is_maximized_(false),
       is_fullscreen_(false),
       attention_request_id_(0),
@@ -236,6 +257,9 @@
         floor((NSHeight(main_screen_rect) - NSHeight(cocoa_bounds)) / 2);
   }
 
+  // Initialize |restored_bounds_| after |cocoa_bounds| have been sanitized.
+  restored_bounds_ = cocoa_bounds;
+
   resizable_ = params.resizable;
   base::scoped_nsobject<NSWindow> window;
   Class window_class;
@@ -399,7 +423,7 @@
   // add it back afterwards.
   UninstallView();
   if (fullscreen) {
-    restored_bounds_ = [window() frame];
+    UpdateRestoredBounds();
     [window() setStyleMask:NSBorderlessWindowMask];
     [window() setFrame:[window()
         frameRectForContentRect:[[window() screen] frame]]
@@ -435,7 +459,7 @@
 gfx::Rect NativeAppWindowCocoa::GetRestoredBounds() const {
   // Flip coordinates based on the primary screen.
   NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
-  NSRect frame = [window() frame];
+  NSRect frame = restored_bounds_;
   gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
   bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
   return bounds;
@@ -448,20 +472,38 @@
 }
 
 gfx::Rect NativeAppWindowCocoa::GetBounds() const {
-  return GetRestoredBounds();
+  // Flip coordinates based on the primary screen.
+  NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
+  NSRect frame = [window() frame];
+  gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame));
+  bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame));
+  return bounds;
 }
 
 void NativeAppWindowCocoa::Show() {
+  is_hidden_ = false;
+
+  if (is_hidden_with_app_) {
+    // If there is a shim to gently request attention, return here. Otherwise
+    // show the window as usual.
+    if (apps::ExtensionAppShimHandler::RequestUserAttentionForWindow(
+            shell_window_)) {
+      return;
+    }
+  }
+
   [window_controller_ showWindow:nil];
   [window() makeKeyAndOrderFront:window_controller_];
 }
 
 void NativeAppWindowCocoa::ShowInactive() {
+  is_hidden_ = false;
   [window() orderFront:window_controller_];
 }
 
 void NativeAppWindowCocoa::Hide() {
-  [window() orderOut:window_controller_];
+  is_hidden_ = true;
+  HideWithoutMarkingHidden();
 }
 
 void NativeAppWindowCocoa::Close() {
@@ -478,10 +520,9 @@
 }
 
 void NativeAppWindowCocoa::Maximize() {
-  // Zoom toggles so only call if not already maximized.
-  if (![window() isZoomed])
-    [window() zoom:window_controller_];
-  is_maximized_ = true;
+  UpdateRestoredBounds();
+  is_maximized_ = true;  // See top of file NOTE: State Before Update.
+  [window() setFrame:[[window() screen] visibleFrame] display:YES animate:YES];
 }
 
 void NativeAppWindowCocoa::Minimize() {
@@ -489,11 +530,15 @@
 }
 
 void NativeAppWindowCocoa::Restore() {
-  if ([window() isZoomed])
-    [window() zoom:window_controller_];  // Toggles zoom mode.
-  else if (IsMinimized())
+  DCHECK(!IsFullscreenOrPending());   // SetFullscreen, not Restore, expected.
+
+  if (IsMaximized()) {
+    is_maximized_ = false;  // See top of file NOTE: State Before Update.
+    [window() setFrame:restored_bounds() display:YES animate:YES];
+  } else if (IsMinimized()) {
+    is_maximized_ = false;  // See top of file NOTE: State Before Update.
     [window() deminiaturize:window_controller_];
-  is_maximized_ = false;
+  }
 }
 
 void NativeAppWindowCocoa::SetBounds(const gfx::Rect& bounds) {
@@ -788,6 +833,17 @@
 }
 
 void NativeAppWindowCocoa::WindowDidResize() {
+  // Update |is_maximized_| if needed:
+  // - Exit maximized state if resized.
+  // - Consider us maximized if resize places us back to maximized location.
+  //   This happens when returning from fullscreen.
+  NSRect frame = [window() frame];
+  NSRect screen = [[window() screen] visibleFrame];
+  if (!NSEqualSizes(frame.size, screen.size))
+    is_maximized_ = false;
+  else if (NSEqualPoints(frame.origin, screen.origin))
+    is_maximized_ = true;
+
   shell_window_->OnNativeWindowChanged();
 }
 
@@ -804,7 +860,11 @@
 }
 
 void NativeAppWindowCocoa::WindowWillZoom() {
-  is_maximized_ = ![window() isZoomed];
+  // See top of file NOTE: Maximize and Zoom.
+  if (IsMaximized())
+    Restore();
+  else
+    Maximize();
 }
 
 bool NativeAppWindowCocoa::HandledByExtensionCommand(NSEvent* event) {
@@ -838,6 +898,21 @@
   return draggable_region_->contains(point.x, webViewHeight - point.y);
 }
 
+void NativeAppWindowCocoa::HideWithApp() {
+  is_hidden_with_app_ = true;
+  HideWithoutMarkingHidden();
+}
+
+void NativeAppWindowCocoa::ShowWithApp() {
+  is_hidden_with_app_ = false;
+  if (!is_hidden_)
+    ShowInactive();
+}
+
+void NativeAppWindowCocoa::HideWithoutMarkingHidden() {
+  [window() orderOut:window_controller_];
+}
+
 NativeAppWindowCocoa::~NativeAppWindowCocoa() {
 }
 
@@ -846,3 +921,8 @@
   CHECK(!window || [window isKindOfClass:[ShellNSWindow class]]);
   return static_cast<ShellNSWindow*>(window);
 }
+
+void NativeAppWindowCocoa::UpdateRestoredBounds() {
+  if (IsRestored(*this))
+    restored_bounds_ = [window() frame];
+}
diff --git a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm
new file mode 100644
index 0000000..9b203a8
--- /dev/null
+++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm
@@ -0,0 +1,105 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h"
+
+#import <Cocoa/Cocoa.h>
+
+#include "apps/shell_window_registry.h"
+#include "chrome/browser/apps/app_browsertest_util.h"
+#include "chrome/browser/ui/extensions/application_launch.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/test/test_utils.h"
+
+using extensions::PlatformAppBrowserTest;
+
+namespace {
+
+class NativeAppWindowCocoaBrowserTest : public PlatformAppBrowserTest {
+ protected:
+  NativeAppWindowCocoaBrowserTest() {}
+
+  void SetUpAppWithWindows(int num_windows) {
+    app_ = InstallExtension(
+        test_data_dir_.AppendASCII("platform_apps").AppendASCII("minimal"), 1);
+    EXPECT_TRUE(app_);
+
+    for (int i = 0; i < num_windows; ++i) {
+      content::WindowedNotificationObserver app_loaded_observer(
+          content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
+          content::NotificationService::AllSources());
+      chrome::OpenApplication(chrome::AppLaunchParams(
+          profile(), app_, extension_misc::LAUNCH_NONE, NEW_WINDOW));
+      app_loaded_observer.Wait();
+    }
+  }
+
+  const extensions::Extension* app_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NativeAppWindowCocoaBrowserTest);
+};
+
+}  // namespace
+
+// Test interaction of Hide/Show() with Hide/ShowWithApp().
+IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, HideShowWithApp) {
+  SetUpAppWithWindows(2);
+  apps::ShellWindowRegistry::ShellWindowList windows =
+      apps::ShellWindowRegistry::Get(profile())->shell_windows();
+  apps::NativeAppWindow* window = windows.front()->GetBaseWindow();
+  NSWindow* ns_window = window->GetNativeWindow();
+  apps::NativeAppWindow* other_window = windows.back()->GetBaseWindow();
+  NSWindow* other_ns_window = other_window->GetNativeWindow();
+
+  // Normal Hide/Show.
+  window->Hide();
+  EXPECT_FALSE([ns_window isVisible]);
+  window->Show();
+  EXPECT_TRUE([ns_window isVisible]);
+
+  // Normal Hide/ShowWithApp.
+  window->HideWithApp();
+  EXPECT_FALSE([ns_window isVisible]);
+  window->ShowWithApp();
+  EXPECT_TRUE([ns_window isVisible]);
+
+  // HideWithApp, Hide, ShowWithApp does not show.
+  window->HideWithApp();
+  window->Hide();
+  window->ShowWithApp();
+  EXPECT_FALSE([ns_window isVisible]);
+
+  // Hide, HideWithApp, ShowWithApp does not show.
+  window->HideWithApp();
+  window->ShowWithApp();
+  EXPECT_FALSE([ns_window isVisible]);
+
+  // Return to shown state.
+  window->Show();
+  EXPECT_TRUE([ns_window isVisible]);
+
+  // HideWithApp the other window.
+  EXPECT_TRUE([other_ns_window isVisible]);
+  other_window->HideWithApp();
+  EXPECT_FALSE([other_ns_window isVisible]);
+
+  // HideWithApp, Show shows all windows for this app.
+  window->HideWithApp();
+  EXPECT_FALSE([ns_window isVisible]);
+  window->Show();
+  EXPECT_TRUE([ns_window isVisible]);
+  EXPECT_TRUE([other_ns_window isVisible]);
+
+  // Hide the other window.
+  other_window->Hide();
+  EXPECT_FALSE([other_ns_window isVisible]);
+
+  // HideWithApp, ShowWithApp does not show the other window.
+  window->HideWithApp();
+  EXPECT_FALSE([ns_window isVisible]);
+  window->ShowWithApp();
+  EXPECT_TRUE([ns_window isVisible]);
+  EXPECT_FALSE([other_ns_window isVisible]);
+}
diff --git a/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.h b/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.h
index 1e8c4c5..00149bc 100644
--- a/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.h
+++ b/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.h
@@ -54,6 +54,8 @@
   virtual void GetUserInput(DialogSection section,
                             DetailOutputMap* output) OVERRIDE;
   virtual string16 GetCvc() OVERRIDE;
+  virtual bool HitTestInput(const DetailInput& input,
+                            const gfx::Point& screen_point) OVERRIDE;
   virtual bool SaveDetailsLocally() OVERRIDE;
   virtual const content::NavigationController* ShowSignIn() OVERRIDE;
   virtual void HideSignIn() OVERRIDE;
@@ -110,6 +112,7 @@
   base::scoped_nsobject<AutofillMainContainer> mainContainer_;
   base::scoped_nsobject<AutofillSignInContainer> signInContainer_;
   base::scoped_nsobject<AutofillAccountChooser> accountChooser_;
+  base::scoped_nsobject<NSTextField> loadingShieldTextField_;
 }
 
 // Designated initializer. The WebContents cannot be NULL.
diff --git a/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.mm b/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.mm
index 306ba33..fc40da2 100644
--- a/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.mm
+++ b/chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.mm
@@ -19,13 +19,15 @@
 #import "chrome/browser/ui/cocoa/autofill/autofill_main_container.h"
 #import "chrome/browser/ui/cocoa/autofill/autofill_section_container.h"
 #import "chrome/browser/ui/cocoa/autofill/autofill_sign_in_container.h"
-#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_button.h"
 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_window.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_view.h"
+#include "grit/generated_resources.h"
 #import "ui/base/cocoa/flipped_view.h"
 #include "ui/base/cocoa/window_size_constants.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/platform_font.h"
 
 namespace {
 
@@ -126,6 +128,12 @@
   return string16();
 }
 
+bool AutofillDialogCocoa::HitTestInput(const DetailInput& input,
+                                       const gfx::Point& screen_point) {
+  // TODO(dbeam): implement.
+  return false;
+}
+
 bool AutofillDialogCocoa::SaveDetailsLocally() {
   return [sheet_delegate_ saveDetailsLocally];
 }
@@ -204,6 +212,24 @@
 
 }  // autofill
 
+#pragma mark "Loading" Shield
+
+@interface AutofillOpaqueView : NSView
+@end
+
+@implementation AutofillOpaqueView
+
+- (BOOL)isOpaque {
+  return YES;
+}
+
+- (void)drawRect:(NSRect)dirtyRect {
+  [[[self window] backgroundColor] setFill];
+  [NSBezierPath fillRect:[self bounds]];
+}
+
+@end
+
 #pragma mark Window Controller
 
 @interface AutofillDialogWindowController ()
@@ -249,13 +275,31 @@
                               initWithFrame:headerRect
                                  delegate:autofillDialog->delegate()]);
 
+    loadingShieldTextField_.reset(
+        [[NSTextField alloc] initWithFrame:NSZeroRect]);
+    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+    NSFont* loadingFont = rb.GetFont(
+        ui::ResourceBundle::BaseFont).DeriveFont(15).GetNativeFont();
+    [loadingShieldTextField_ setFont:loadingFont];
+    [loadingShieldTextField_ setEditable:NO];
+    [loadingShieldTextField_ setBordered:NO];
+    [loadingShieldTextField_ setDrawsBackground:NO];
+
+    base::scoped_nsobject<AutofillOpaqueView> loadingShieldView(
+        [[AutofillOpaqueView alloc] initWithFrame:NSZeroRect]);
+    [loadingShieldView setHidden:YES];
+    [loadingShieldView addSubview:loadingShieldTextField_];
+
     // This needs a flipped content view because otherwise the size
     // animation looks odd. However, replacing the contentView for constrained
     // windows does not work - it does custom rendering.
     base::scoped_nsobject<NSView> flippedContentView(
         [[FlippedView alloc] initWithFrame:NSZeroRect]);
     [flippedContentView setSubviews:
-        @[accountChooser_, [mainContainer_ view], [signInContainer_ view]]];
+        @[accountChooser_,
+          [mainContainer_ view],
+          [signInContainer_ view],
+          loadingShieldView]];
     [flippedContentView setAutoresizingMask:
         (NSViewWidthSizable | NSViewHeightSizable)];
     [[[self window] contentView] addSubview:flippedContentView];
@@ -345,6 +389,15 @@
     [[signInContainer_ view] setFrame:mainRect];
   }
 
+  // Loading shield has text centered in the content rect.
+  NSRect textFrame = [loadingShieldTextField_ frame];
+  textFrame.origin.x =
+      std::ceil((NSWidth(contentRect) - NSWidth(textFrame)) / 2.0);
+  textFrame.origin.y =
+    std::ceil((NSHeight(contentRect) - NSHeight(textFrame)) / 2.0);
+  [loadingShieldTextField_ setFrame:textFrame];
+  [[loadingShieldTextField_ superview] setFrame:contentRect];
+
   NSRect frameRect = [[self window] frameRectForContentRect:contentRect];
   [[self window] setFrame:frameRect display:YES];
 }
@@ -371,6 +424,19 @@
 - (void)updateAccountChooser {
   [accountChooser_ update];
   [mainContainer_ updateLegalDocuments];
+  // TODO(estade): replace this with a better loading image/animation.
+  // See http://crbug.com/230932
+  NSString* newLoadingMessage = @"";
+  if (autofillDialog_->delegate()->ShouldShowSpinner())
+    newLoadingMessage = l10n_util::GetNSStringWithFixup(IDS_TAB_LOADING_TITLE);
+  if (![newLoadingMessage isEqualToString:
+       [loadingShieldTextField_ stringValue]]) {
+    [loadingShieldTextField_ setStringValue:newLoadingMessage];
+    [loadingShieldTextField_ sizeToFit];
+    [[loadingShieldTextField_ superview] setHidden:
+        [newLoadingMessage length] == 0];
+    [self requestRelayout];
+  }
 }
 
 - (void)updateSection:(autofill::DialogSection)section {
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller_unittest.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller_unittest.mm
index 8a9b7f8..1e01e9d 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller_unittest.mm
@@ -210,8 +210,8 @@
     return NULL;
   }
   virtual SkColor GetColor(int id) const OVERRIDE { return SkColor(); }
-  virtual bool GetDisplayProperty(int id, int* result) const OVERRIDE {
-    return false;
+  virtual int GetDisplayProperty(int id) const OVERRIDE {
+    return -1;
   }
   virtual bool ShouldUseNativeFrame() const OVERRIDE { return false; }
   virtual bool HasCustomImage(int id) const OVERRIDE { return false; }
diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.h b/chrome/browser/ui/cocoa/browser_window_cocoa.h
index 334d273..d030f11 100644
--- a/chrome/browser/ui/cocoa/browser_window_cocoa.h
+++ b/chrome/browser/ui/cocoa/browser_window_cocoa.h
@@ -145,6 +145,7 @@
       const gfx::Rect& rect,
       const content::PasswordForm& form,
       autofill::PasswordGenerator* password_generator) OVERRIDE;
+  virtual int GetRenderViewHeightInsetWithDetachedBookmarkBar() OVERRIDE;
 
   // Overridden from ExtensionKeybindingRegistry::Delegate:
   virtual extensions::ActiveTabPermissionGranter*
diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.mm b/chrome/browser/ui/cocoa/browser_window_cocoa.mm
index 6b7e00b..eea1e77 100644
--- a/chrome/browser/ui/cocoa/browser_window_cocoa.mm
+++ b/chrome/browser/ui/cocoa/browser_window_cocoa.mm
@@ -732,3 +732,40 @@
                forForm:form];
   [controller showWindow:nil];
 }
+
+int
+BrowserWindowCocoa::GetRenderViewHeightInsetWithDetachedBookmarkBar() {
+  if (browser_->bookmark_bar_state() != BookmarkBar::DETACHED)
+    return 0;
+  // TODO(sail): please make this work with cocoa, then enable
+  // BrowserTest.GetSizeForNewRenderView and
+  // WebContentsImplBrowserTest.GetSizeForNewRenderView.
+  // This function should return the extra height of the render view when
+  // detached bookmark bar is hidden.
+  // However, I (kuan) return 0 for now to retain the original behavior,
+  // because I encountered the following problem on cocoa:
+  // 1) When a navigation is requested,
+  //    WebContentsImpl::CreateRenderViewForRenderManager creates the new
+  //    RenderWidgetHostView at the size specified by
+  //    WebContentsDelegate::GetSizeForNewRenderView implemented by Browser.
+  // 2) When the pending navigation entry is committed,
+  //    WebContentsImpl::UpdateRenderViewSizeForRenderManager udpates the size
+  //    of WebContentsView to the size in (1).
+  // 3) WebContentsImpl::DidNavigateMainFramePostCommit() is called, where
+  //    the detached bookmark bar is hidden, resulting in relayout of tab
+  //    contents area.
+  // On cocoa, (2) causes RenderWidgetHostView to resize (enlarge) further.
+  // e.g. if size in (1) is size A, and this function returns height H, height
+  // of RenderWidgetHostView after (2) becomes A.height() + H; it's supposed to
+  // stay at A.height().
+  // Then, in (3), WebContentsView and RenderWidgetHostView enlarge even
+  // further, both by another H, i.e. WebContentsView's height becomes
+  // A.height() + H and RenderWidgetHostView's height becomes A.height() + 2H.
+  // Strangely, the RenderWidgetHostView for the previous navigation entry also
+  // gets enlarged by H.
+  // I believe these "automatic" resizing are caused by setAutoresizingMask of
+  // of the cocoa view in WebContentsViewMac, which defeats the purpose of
+  // WebContentsDelegate::GetSizeForNewRenderView i.e. to prevent resizing of
+  // RenderWidgetHostView in (2) and (3).
+  return 0;
+}
diff --git a/chrome/browser/ui/cocoa/browser_window_controller.h b/chrome/browser/ui/cocoa/browser_window_controller.h
index e117b06..28ee5f7 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller.h
+++ b/chrome/browser/ui/cocoa/browser_window_controller.h
@@ -496,6 +496,9 @@
 // positioned relative to.
 - (NSRect)omniboxPopupAnchorRect;
 
+// Force a layout of info bars.
+- (void)layoutInfoBars;
+
 @end  // @interface BrowserWindowController (TestingAPI)
 
 
diff --git a/chrome/browser/ui/cocoa/browser_window_controller.mm b/chrome/browser/ui/cocoa/browser_window_controller.mm
index 7717c7e..cc8c86e 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller.mm
@@ -1947,6 +1947,10 @@
   return [[toolbarView superview] convertRect:anchorRect toView:nil];
 }
 
+- (void)layoutInfoBars {
+  [self layoutSubviews];
+}
+
 - (void)sheetDidEnd:(NSWindow*)sheet
          returnCode:(NSInteger)code
             context:(void*)context {
diff --git a/chrome/browser/ui/cocoa/browser_window_controller_browsertest.mm b/chrome/browser/ui/cocoa/browser_window_controller_browsertest.mm
index 3e9a785..47c046d 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller_browsertest.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller_browsertest.mm
@@ -21,6 +21,7 @@
 #import "chrome/browser/ui/cocoa/browser_window_controller_private.h"
 #import "chrome/browser/ui/cocoa/fast_resize_view.h"
 #import "chrome/browser/ui/cocoa/history_overlay_controller.h"
+#import "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
 #import "chrome/browser/ui/cocoa/nsview_additions.h"
 #import "chrome/browser/ui/cocoa/tab_contents/overlayable_contents_controller.h"
@@ -108,10 +109,8 @@
         browser()->tab_strip_model()->GetActiveWebContents();
     InfoBarService* service =
         InfoBarService::FromWebContents(web_contents);
-    info_bar_delegate_.reset(new DummyInfoBar(service));
-    [[controller() infoBarContainerController]
-        addInfoBar:info_bar_delegate_->CreateInfoBar(service)
-           animate:NO];
+    scoped_ptr<InfoBarDelegate> info_bar_delegate(new DummyInfoBar(service));
+    service->AddInfoBar(info_bar_delegate.Pass());
   }
 
   NSView* GetViewWithID(ViewID view_id) const {
@@ -163,8 +162,6 @@
   }
 
  private:
-  scoped_ptr<InfoBarDelegate> info_bar_delegate_;
-
   DISALLOW_COPY_AND_ASSIGN(BrowserWindowControllerTest);
 };
 
@@ -371,10 +368,7 @@
       popup_browser->tab_strip_model()->GetActiveWebContents();
   InfoBarService* service = InfoBarService::FromWebContents(web_contents);
   scoped_ptr<InfoBarDelegate> info_bar_delegate(new DummyInfoBar(service));
-  [[popupController infoBarContainerController]
-      addInfoBar:info_bar_delegate->CreateInfoBar(service)
-         animate:NO];
-
+  service->AddInfoBar(info_bar_delegate.Pass());
   EXPECT_TRUE(
       [[popupController infoBarContainerController]
           shouldSuppressTopInfoBarTip]);
@@ -386,7 +380,7 @@
                        AllowOverlappingViewsHistoryOverlay) {
   content::WebContentsView* web_contents_view =
       browser()->tab_strip_model()->GetActiveWebContents()->GetView();
-  EXPECT_FALSE(web_contents_view->GetAllowOverlappingViews());
+  EXPECT_TRUE(web_contents_view->GetAllowOverlappingViews());
 
   base::scoped_nsobject<HistoryOverlayController> overlay(
       [[HistoryOverlayController alloc] initForMode:kHistoryOverlayModeBack]);
@@ -394,5 +388,5 @@
   EXPECT_TRUE(web_contents_view->GetAllowOverlappingViews());
 
   overlay.reset();
-  EXPECT_FALSE(web_contents_view->GetAllowOverlappingViews());
+  EXPECT_TRUE(web_contents_view->GetAllowOverlappingViews());
 }
diff --git a/chrome/browser/ui/cocoa/browser_window_controller_private.mm b/chrome/browser/ui/cocoa/browser_window_controller_private.mm
index f2238d3..a144dfb 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller_private.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller_private.mm
@@ -972,6 +972,11 @@
   BOOL allowOverlappingViews =
       [self shouldAllowOverlappingViews:inPresentationMode];
 
+  // The rendering path with overlapping views disabled causes bugs when
+  // transitioning between composited and non-composited mode.
+  // http://crbug.com/279472
+  allowOverlappingViews = YES;
+
   if (allowOverlappingViews &&
       [self coreAnimationStatus] ==
           browser_window_controller::kCoreAnimationEnabledLazy) {
diff --git a/chrome/browser/ui/cocoa/dev_tools_controller_browsertest.mm b/chrome/browser/ui/cocoa/dev_tools_controller_browsertest.mm
index cffd2a7..fdc12b5 100644
--- a/chrome/browser/ui/cocoa/dev_tools_controller_browsertest.mm
+++ b/chrome/browser/ui/cocoa/dev_tools_controller_browsertest.mm
@@ -40,7 +40,7 @@
       dev_tools->web_contents()->GetView();
 
   // Without the find bar.
-  EXPECT_FALSE(dev_tools_view->GetAllowOverlappingViews());
+  EXPECT_TRUE(dev_tools_view->GetAllowOverlappingViews());
 
   // With the find bar.
   browser()->GetFindBarController()->find_bar()->Show(false);
@@ -48,5 +48,5 @@
 
   // Without the find bar.
   browser()->GetFindBarController()->find_bar()->Hide(false);
-  EXPECT_FALSE(dev_tools_view->GetAllowOverlappingViews());
+  EXPECT_TRUE(dev_tools_view->GetAllowOverlappingViews());
 }
diff --git a/chrome/browser/ui/cocoa/download/background_theme.h b/chrome/browser/ui/cocoa/download/background_theme.h
index 201b52e..bd08c01 100644
--- a/chrome/browser/ui/cocoa/download/background_theme.h
+++ b/chrome/browser/ui/cocoa/download/background_theme.h
@@ -18,7 +18,7 @@
   // Overridden from ui::ThemeProvider:
   virtual gfx::ImageSkia* GetImageSkiaNamed(int id) const OVERRIDE;
   virtual SkColor GetColor(int id) const OVERRIDE;
-  virtual bool GetDisplayProperty(int id, int* result) const OVERRIDE;
+  virtual int GetDisplayProperty(int id) const OVERRIDE;
   virtual bool ShouldUseNativeFrame() const OVERRIDE;
   virtual bool HasCustomImage(int id) const OVERRIDE;
   virtual base::RefCountedMemory* GetRawData(
diff --git a/chrome/browser/ui/cocoa/download/background_theme.mm b/chrome/browser/ui/cocoa/download/background_theme.mm
index eb7ffcf..dec064c 100644
--- a/chrome/browser/ui/cocoa/download/background_theme.mm
+++ b/chrome/browser/ui/cocoa/download/background_theme.mm
@@ -35,8 +35,8 @@
   return SkColor();
 }
 
-bool BackgroundTheme::GetDisplayProperty(int id, int* result) const {
-  return false;
+int BackgroundTheme::GetDisplayProperty(int id) const {
+  return -1;
 }
 
 bool BackgroundTheme::ShouldUseNativeFrame() const {
diff --git a/chrome/browser/ui/cocoa/download/download_item_cell.mm b/chrome/browser/ui/cocoa/download/download_item_cell.mm
index 20ec1a9..c4e5e4d 100644
--- a/chrome/browser/ui/cocoa/download/download_item_cell.mm
+++ b/chrome/browser/ui/cocoa/download/download_item_cell.mm
@@ -7,7 +7,6 @@
 #include "base/strings/sys_string_conversions.h"
 #include "chrome/browser/download/download_item_model.h"
 #include "chrome/browser/download/download_shelf.h"
-#include "chrome/browser/download/download_util.h"
 #import "chrome/browser/themes/theme_properties.h"
 #import "chrome/browser/ui/cocoa/download/background_theme.h"
 #import "chrome/browser/ui/cocoa/themed_window.h"
diff --git a/chrome/browser/ui/cocoa/download/download_item_controller.mm b/chrome/browser/ui/cocoa/download/download_item_controller.mm
index eeadce3..ab2be05 100644
--- a/chrome/browser/ui/cocoa/download/download_item_controller.mm
+++ b/chrome/browser/ui/cocoa/download/download_item_controller.mm
@@ -14,7 +14,6 @@
 #include "chrome/browser/download/chrome_download_manager_delegate.h"
 #include "chrome/browser/download/download_item_model.h"
 #include "chrome/browser/download/download_shelf_context_menu.h"
-#include "chrome/browser/download/download_util.h"
 #import "chrome/browser/themes/theme_properties.h"
 #import "chrome/browser/themes/theme_service.h"
 #import "chrome/browser/ui/cocoa/download/download_item_button.h"
diff --git a/chrome/browser/ui/cocoa/download/download_item_drag_mac.mm b/chrome/browser/ui/cocoa/download/download_item_drag_mac.mm
new file mode 100644
index 0000000..6e6af12
--- /dev/null
+++ b/chrome/browser/ui/cocoa/download/download_item_drag_mac.mm
@@ -0,0 +1,40 @@
+// 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 "chrome/browser/download/drag_download_item.h"
+
+#include "chrome/browser/ui/cocoa/download/download_util_mac.h"
+#include "content/public/browser/download_item.h"
+#include "ui/gfx/image/image.h"
+
+void DragDownloadItem(const content::DownloadItem* download,
+                      gfx::Image* icon,
+                      gfx::NativeView view) {
+  DCHECK_EQ(content::DownloadItem::COMPLETE, download->GetState());
+  NSPasteboard* pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
+  download_util::AddFileToPasteboard(pasteboard, download->GetTargetFilePath());
+
+  // Synthesize a drag event, since we don't have access to the actual event
+  // that initiated a drag (possibly consumed by the Web UI, for example).
+  NSPoint position = [[view window] mouseLocationOutsideOfEventStream];
+  NSTimeInterval eventTime = [[NSApp currentEvent] timestamp];
+  NSEvent* dragEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
+                                          location:position
+                                     modifierFlags:NSLeftMouseDraggedMask
+                                         timestamp:eventTime
+                                      windowNumber:[[view window] windowNumber]
+                                           context:nil
+                                       eventNumber:0
+                                        clickCount:1
+                                          pressure:1.0];
+
+  // Run the drag operation.
+  [[view window] dragImage:icon->ToNSImage()
+                        at:position
+                    offset:NSZeroSize
+                     event:dragEvent
+                pasteboard:pasteboard
+                    source:view
+                 slideBack:YES];
+}
diff --git a/chrome/browser/ui/cocoa/download/download_show_all_cell.h b/chrome/browser/ui/cocoa/download/download_show_all_cell.h
index 4f90ad6..80dd574 100644
--- a/chrome/browser/ui/cocoa/download/download_show_all_cell.h
+++ b/chrome/browser/ui/cocoa/download/download_show_all_cell.h
@@ -6,7 +6,6 @@
 #define CHROME_BROWSER_UI_COCOA_DOWNLOAD_DOWNLOAD_SHOW_ALL_CELL_H_
 
 #include "base/memory/scoped_ptr.h"
-#include "chrome/browser/download/download_util.h"
 #import "chrome/browser/ui/cocoa/gradient_button_cell.h"
 
 // The cell of the "Show All" button on the download shelf.
diff --git a/chrome/browser/ui/cocoa/download/download_util_mac.h b/chrome/browser/ui/cocoa/download/download_util_mac.h
index b873cc8..4f7c789 100644
--- a/chrome/browser/ui/cocoa/download/download_util_mac.h
+++ b/chrome/browser/ui/cocoa/download/download_util_mac.h
@@ -1,8 +1,6 @@
 // Copyright (c) 2010 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.
-//
-// Download utility functions for Mac OS X.
 
 #ifndef CHROME_BROWSER_UI_COCOA_DOWNLOAD_DOWNLOAD_UTIL_MAC_H_
 #define CHROME_BROWSER_UI_COCOA_DOWNLOAD_DOWNLOAD_UTIL_MAC_H_
diff --git a/chrome/browser/ui/cocoa/download/download_util_mac.mm b/chrome/browser/ui/cocoa/download/download_util_mac.mm
index 363c13d..28dd06a 100644
--- a/chrome/browser/ui/cocoa/download/download_util_mac.mm
+++ b/chrome/browser/ui/cocoa/download/download_util_mac.mm
@@ -1,19 +1,11 @@
 // Copyright (c) 2011 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.
-//
-// Download utility implementation for Mac OS X.
 
 #include "chrome/browser/ui/cocoa/download/download_util_mac.h"
 
-#include "base/logging.h"
+#include "base/files/file_path.h"
 #include "base/strings/sys_string_conversions.h"
-#include "content/public/browser/download_item.h"
-#include "content/public/browser/download_manager.h"
-#include "ui/gfx/image/image.h"
-#include "ui/gfx/native_widget_types.h"
-
-using content::DownloadItem;
 
 namespace download_util {
 
@@ -26,35 +18,4 @@
   [pasteboard setPropertyList:fileList forType:NSFilenamesPboardType];
 }
 
-void DragDownload(const DownloadItem* download,
-                  gfx::Image* icon,
-                  gfx::NativeView view) {
-  DCHECK_EQ(DownloadItem::COMPLETE, download->GetState());
-  NSPasteboard* pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
-  AddFileToPasteboard(pasteboard, download->GetTargetFilePath());
-
-  // Synthesize a drag event, since we don't have access to the actual event
-  // that initiated a drag (possibly consumed by the Web UI, for example).
-  NSPoint position = [[view window] mouseLocationOutsideOfEventStream];
-  NSTimeInterval eventTime = [[NSApp currentEvent] timestamp];
-  NSEvent* dragEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
-                                          location:position
-                                     modifierFlags:NSLeftMouseDraggedMask
-                                         timestamp:eventTime
-                                      windowNumber:[[view window] windowNumber]
-                                           context:nil
-                                       eventNumber:0
-                                        clickCount:1
-                                          pressure:1.0];
-
-  // Run the drag operation.
-  [[view window] dragImage:icon->ToNSImage()
-                        at:position
-                    offset:NSZeroSize
-                     event:dragEvent
-                pasteboard:pasteboard
-                    source:view
-                 slideBack:YES];
-}
-
 }  // namespace download_util
diff --git a/chrome/browser/ui/cocoa/extensions/browser_action_button.h b/chrome/browser/ui/cocoa/extensions/browser_action_button.h
index fa5a46b..66827da 100644
--- a/chrome/browser/ui/cocoa/extensions/browser_action_button.h
+++ b/chrome/browser/ui/cocoa/extensions/browser_action_button.h
@@ -13,6 +13,7 @@
 
 class Browser;
 class ExtensionAction;
+@class ExtensionActionContextMenuController;
 class ExtensionActionIconFactoryBridge;
 
 namespace extensions {
@@ -24,7 +25,7 @@
 // Fired when the user drops the button.
 extern NSString* const kBrowserActionButtonDragEndNotification;
 
-@interface BrowserActionButton : NSButton {
+@interface BrowserActionButton : NSButton<NSMenuDelegate> {
  @private
   // Bridge to proxy Chrome notifications to the Obj-C class as well as load the
   // extension's icon.
@@ -50,6 +51,9 @@
   // The point where the mouse down event occurred. Used to prevent a drag from
   // starting until it moves at least kMinimumDragDistance.
   NSPoint dragStartPoint_;
+
+  base::scoped_nsobject<
+      ExtensionActionContextMenuController> contextMenuController_;
 }
 
 - (id)initWithFrame:(NSRect)frame
diff --git a/chrome/browser/ui/cocoa/extensions/browser_action_button.mm b/chrome/browser/ui/cocoa/extensions/browser_action_button.mm
index aefec8f..1fc217c 100644
--- a/chrome/browser/ui/cocoa/extensions/browser_action_button.mm
+++ b/chrome/browser/ui/cocoa/extensions/browser_action_button.mm
@@ -14,7 +14,7 @@
 #include "chrome/browser/extensions/extension_action_icon_factory.h"
 #include "chrome/browser/extensions/extension_action_manager.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/cocoa/extensions/extension_action_context_menu.h"
+#include "chrome/browser/ui/cocoa/extensions/extension_action_context_menu_controller.h"
 #include "chrome/common/extensions/extension.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
@@ -149,10 +149,14 @@
     [self setButtonType:NSMomentaryChangeButton];
     [self setShowsBorderOnlyWhileMouseInside:YES];
 
-    [self setMenu:[[[ExtensionActionContextMenu alloc]
+    contextMenuController_.reset([[ExtensionActionContextMenuController alloc]
         initWithExtension:extension
                   browser:browser
-          extensionAction:browser_action] autorelease]];
+          extensionAction:browser_action]);
+    base::scoped_nsobject<NSMenu> contextMenu(
+        [[NSMenu alloc] initWithTitle:@""]);
+    [contextMenu setDelegate:self];
+    [self setMenu:contextMenu];
 
     tabId_ = tabId;
     extension_ = extension;
@@ -309,6 +313,11 @@
   return image;
 }
 
+- (void)menuNeedsUpdate:(NSMenu*)menu {
+  [menu removeAllItems];
+  [contextMenuController_ populateMenu:menu];
+}
+
 @end
 
 @implementation BrowserActionCell
diff --git a/chrome/browser/ui/cocoa/extensions/extension_action_context_menu.h b/chrome/browser/ui/cocoa/extensions/extension_action_context_menu.h
deleted file mode 100644
index 35d5ccf..0000000
--- a/chrome/browser/ui/cocoa/extensions/extension_action_context_menu.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_ACTION_CONTEXT_MENU_H_
-#define CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_ACTION_CONTEXT_MENU_H_
-
-#import <Cocoa/Cocoa.h>
-
-#include "base/memory/scoped_ptr.h"
-
-class AsyncUninstaller;
-class Browser;
-class ExtensionAction;
-
-namespace extensions {
-class Extension;
-}
-
-namespace extension_action_context_menu {
-
-// Enum of menu item choices to their respective indices. Exposed for use in
-// tests.
-// NOTE: This must be kept in sync with the implementation of the menu.
-typedef enum {
-  kExtensionContextName = 0,
-  kExtensionContextOptions = 2,
-  kExtensionContextUninstall = 3,
-  kExtensionContextHide = 4,
-  kExtensionContextManage = 6,
-  kExtensionContextInspect = 7
-} ExtensionType;
-
-class DevmodeObserver;
-
-}  // namespace extension_action_context_menu
-
-// A context menu used by any extension UI components that require it.
-@interface ExtensionActionContextMenu : NSMenu {
- @private
-  // The extension that this menu belongs to. Weak.
-  const extensions::Extension* extension_;
-
-  // The extension action this menu belongs to. Weak.
-  ExtensionAction* action_;
-
-  // The browser that contains this extension. Weak.
-  Browser* browser_;
-
-  // The observer used to listen for pref changed notifications.
-  scoped_ptr<extension_action_context_menu::DevmodeObserver> observer_;
-
-  // Used to load the extension icon asynchronously on the I/O thread then show
-  // the uninstall confirmation dialog.
-  scoped_ptr<AsyncUninstaller> uninstaller_;
-}
-
-// Initializes and returns a context menu for the given extension and browser.
-- (id)initWithExtension:(const extensions::Extension*)extension
-                browser:(Browser*)browser
-        extensionAction:(ExtensionAction*)action;
-@end
-
-typedef ExtensionActionContextMenu ExtensionActionContextMenuMac;
-
-#endif  // CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_ACTION_CONTEXT_MENU_H_
diff --git a/chrome/browser/ui/cocoa/extensions/extension_action_context_menu.mm b/chrome/browser/ui/cocoa/extensions/extension_action_context_menu.mm
deleted file mode 100644
index 3f2f585..0000000
--- a/chrome/browser/ui/cocoa/extensions/extension_action_context_menu.mm
+++ /dev/null
@@ -1,285 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "chrome/browser/ui/cocoa/extensions/extension_action_context_menu.h"
-
-#include "base/prefs/pref_change_registrar.h"
-#include "base/prefs/pref_service.h"
-#include "base/strings/sys_string_conversions.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
-#include "chrome/browser/extensions/extension_action.h"
-#include "chrome/browser/extensions/extension_action_manager.h"
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_system.h"
-#include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/extensions/extension_uninstall_dialog.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/cocoa/browser_window_cocoa.h"
-#include "chrome/browser/ui/cocoa/browser_window_controller.h"
-#include "chrome/browser/ui/cocoa/extensions/browser_actions_controller.h"
-#include "chrome/browser/ui/cocoa/extensions/extension_popup_controller.h"
-#include "chrome/browser/ui/cocoa/info_bubble_view.h"
-#include "chrome/browser/ui/cocoa/last_active_browser_cocoa.h"
-#import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
-#include "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/extensions/extension.h"
-#include "chrome/common/extensions/extension_constants.h"
-#include "chrome/common/extensions/manifest_url_handler.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/common/url_constants.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_source.h"
-#include "content/public/browser/web_contents.h"
-#include "grit/chromium_strings.h"
-#include "grit/generated_resources.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/l10n/l10n_util_mac.h"
-
-using content::OpenURLParams;
-using content::Referrer;
-using content::WebContents;
-using extensions::Extension;
-
-// A class that loads the extension icon on the I/O thread before showing the
-// confirmation dialog to uninstall the given extension.
-// Also acts as the extension's UI delegate in order to display the dialog.
-class AsyncUninstaller : public ExtensionUninstallDialog::Delegate {
- public:
-  AsyncUninstaller(const Extension* extension, Browser* browser)
-      : extension_(extension),
-        profile_(browser->profile()) {
-    extension_uninstall_dialog_.reset(
-        ExtensionUninstallDialog::Create(profile_, browser, this));
-    extension_uninstall_dialog_->ConfirmUninstall(extension_);
-  }
-
-  virtual ~AsyncUninstaller() {}
-
-  // ExtensionUninstallDialog::Delegate:
-  virtual void ExtensionUninstallAccepted() OVERRIDE {
-    extensions::ExtensionSystem::Get(profile_)->extension_service()->
-        UninstallExtension(extension_->id(), false, NULL);
-  }
-  virtual void ExtensionUninstallCanceled() OVERRIDE {}
-
- private:
-  // The extension that we're loading the icon for. Weak.
-  const Extension* extension_;
-
-  // The current profile. Weak.
-  Profile* profile_;
-
-  scoped_ptr<ExtensionUninstallDialog> extension_uninstall_dialog_;
-
-  DISALLOW_COPY_AND_ASSIGN(AsyncUninstaller);
-};
-
-@interface ExtensionActionContextMenu(Private)
-// Callback for the context menu items.
-- (void)dispatch:(id)menuItem;
-
-// Runs the action for |menuItem|.
-- (void)updateInspectorItem;
-@end
-
-namespace extension_action_context_menu {
-
-class DevmodeObserver {
- public:
-  DevmodeObserver(ExtensionActionContextMenu* menu,
-                  PrefService* service)
-      : menu_(menu), pref_service_(service) {
-    registrar_.Init(pref_service_);
-    registrar_.Add(
-        prefs::kExtensionsUIDeveloperMode,
-        base::Bind(&DevmodeObserver::OnExtensionsUIDeveloperModeChanged,
-                   base::Unretained(this)));
-  }
-  virtual ~DevmodeObserver() {}
-
-  void OnExtensionsUIDeveloperModeChanged() {
-    [menu_ updateInspectorItem];
-  }
-
- private:
-  ExtensionActionContextMenu* menu_;
-  PrefService* pref_service_;
-  PrefChangeRegistrar registrar_;
-};
-
-}  // namespace extension_action_context_menu
-
-@implementation ExtensionActionContextMenu
-
-namespace {
-// Enum of menu item choices to their respective indices.
-// NOTE: You MUST keep this in sync with the |menuItems| NSArray below.
-enum {
-  kExtensionContextName = 0,
-  kExtensionContextOptions = 2,
-  kExtensionContextUninstall = 3,
-  kExtensionContextHide = 4,
-  kExtensionContextManage = 6,
-  kExtensionContextInspect = 7
-};
-
-}  // namespace
-
-- (id)initWithExtension:(const Extension*)extension
-                browser:(Browser*)browser
-        extensionAction:(ExtensionAction*)action{
-  if ((self = [super initWithTitle:@""])) {
-    action_ = action;
-    extension_ = extension;
-    browser_ = browser;
-
-    // NOTE: You MUST keep this in sync with the enumeration in the header file.
-    NSArray* menuItems = [NSArray arrayWithObjects:
-        base::SysUTF8ToNSString(extension->name()),
-        [NSMenuItem separatorItem],
-        l10n_util::GetNSStringWithFixup(IDS_EXTENSIONS_OPTIONS_MENU_ITEM),
-        l10n_util::GetNSStringWithFixup(IDS_EXTENSIONS_UNINSTALL),
-        l10n_util::GetNSStringWithFixup(IDS_EXTENSIONS_HIDE_BUTTON),
-        [NSMenuItem separatorItem],
-        l10n_util::GetNSStringWithFixup(IDS_MANAGE_EXTENSION),
-        l10n_util::GetNSStringWithFixup(IDS_EXTENSION_ACTION_INSPECT_POPUP),
-        nil];
-
-    for (id item in menuItems) {
-      if ([item isKindOfClass:[NSMenuItem class]]) {
-        [self addItem:item];
-      } else if ([item isKindOfClass:[NSString class]]) {
-        NSMenuItem* itemObj = [self addItemWithTitle:item
-                                              action:@selector(dispatch:)
-                                       keyEquivalent:@""];
-        // The tag should correspond to the enum above.
-        // NOTE: The enum and the order of the menu items MUST be in sync.
-        [itemObj setTag:[self indexOfItem:itemObj]];
-
-        // Only browser actions can have their button hidden. Page actions
-        // should never show the "Hide" menu item.
-        if ([itemObj tag] == kExtensionContextHide &&
-            !extensions::ExtensionActionManager::Get(browser_->profile())->
-            GetBrowserAction(*extension)) {
-          [itemObj setTarget:nil];  // Item is disabled.
-          [itemObj setHidden:YES];  // Item is hidden.
-        } else {
-          [itemObj setTarget:self];
-        }
-      }
-    }
-
-    PrefService* service = browser_->profile()->GetPrefs();
-    observer_.reset(
-        new extension_action_context_menu::DevmodeObserver(self, service));
-
-    [self updateInspectorItem];
-    return self;
-  }
-  return nil;
-}
-
-- (void)updateInspectorItem {
-  PrefService* service = browser_->profile()->GetPrefs();
-  bool devmode = service->GetBoolean(prefs::kExtensionsUIDeveloperMode);
-  NSMenuItem* item = [self itemWithTag:kExtensionContextInspect];
-  [item setHidden:!devmode];
-}
-
-- (void)dispatch:(id)menuItem {
-  NSMenuItem* item = (NSMenuItem*)menuItem;
-  switch ([item tag]) {
-    case kExtensionContextName: {
-      GURL url(std::string(extension_urls::kGalleryBrowsePrefix) +
-               std::string("/detail/") + extension_->id());
-      OpenURLParams params(
-          url, Referrer(), NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_LINK,
-           false);
-      browser_->OpenURL(params);
-      break;
-    }
-    case kExtensionContextOptions: {
-      DCHECK(!extensions::ManifestURL::GetOptionsPage(extension_).is_empty());
-      ExtensionTabUtil::OpenOptionsPage(extension_, browser_);
-      break;
-    }
-    case kExtensionContextUninstall: {
-      uninstaller_.reset(new AsyncUninstaller(extension_, browser_));
-      break;
-    }
-    case kExtensionContextHide: {
-      extensions::ExtensionActionAPI::SetBrowserActionVisibility(
-          extensions::ExtensionSystem::Get(browser_->profile())->
-              extension_service()->extension_prefs(),
-          extension_->id(),
-          false);
-      break;
-    }
-    case kExtensionContextManage: {
-      chrome::ShowExtensions(browser_, extension_->id());
-      break;
-    }
-    case kExtensionContextInspect: {
-      BrowserWindowCocoa* window =
-          static_cast<BrowserWindowCocoa*>(browser_->window());
-      ToolbarController* toolbarController =
-          [window->cocoa_controller() toolbarController];
-      LocationBarViewMac* locationBarView =
-          [toolbarController locationBarBridge];
-
-      extensions::ExtensionActionManager* action_manager =
-          extensions::ExtensionActionManager::Get(browser_->profile());
-      NSPoint popupPoint = NSZeroPoint;
-      if (action_manager->GetPageAction(*extension_) == action_) {
-        popupPoint = locationBarView->GetPageActionBubblePoint(action_);
-
-      } else if (action_manager->GetBrowserAction(*extension_) == action_) {
-        BrowserActionsController* controller =
-            [toolbarController browserActionsController];
-        popupPoint = [controller popupPointForBrowserAction:extension_];
-
-      } else {
-        NOTREACHED() << "action_ is not a page action or browser action?";
-      }
-
-      content::WebContents* active_tab =
-          browser_->tab_strip_model()->GetActiveWebContents();
-      if (!active_tab)
-         break;
-
-      int tabId = ExtensionTabUtil::GetTabId(active_tab);
-
-      GURL url = action_->GetPopupUrl(tabId);
-      if (!url.is_valid())
-        return;
-
-      [ExtensionPopupController showURL:url
-                              inBrowser:browser_
-                             anchoredAt:popupPoint
-                          arrowLocation:info_bubble::kTopRight
-                                devMode:YES];
-      break;
-    }
-    default:
-      NOTREACHED();
-      break;
-  }
-}
-
-- (BOOL)validateMenuItem:(NSMenuItem*)menuItem {
-  if ([menuItem tag] == kExtensionContextOptions) {
-    // Disable 'Options' if there are no options to set.
-    return extensions::ManifestURL::
-        GetOptionsPage(extension_).spec().length() > 0;
-  }
-  return YES;
-}
-
-@end
diff --git a/chrome/browser/ui/cocoa/extensions/extension_action_context_menu_browsertest.mm b/chrome/browser/ui/cocoa/extensions/extension_action_context_menu_browsertest.mm
deleted file mode 100644
index 29ccb02..0000000
--- a/chrome/browser/ui/cocoa/extensions/extension_action_context_menu_browsertest.mm
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "chrome/browser/ui/cocoa/extensions/extension_action_context_menu.h"
-
-#include "base/files/file_path.h"
-#include "base/json/json_file_value_serializer.h"
-#include "base/path_service.h"
-#include "base/prefs/pref_service.h"
-#include "chrome/browser/extensions/browser_action_test_util.h"
-#include "chrome/browser/extensions/extension_action.h"
-#include "chrome/browser/extensions/extension_action_manager.h"
-#include "chrome/browser/extensions/extension_browsertest.h"
-#include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/cocoa/browser_window_cocoa.h"
-#include "chrome/browser/ui/cocoa/browser_window_controller.h"
-#import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
-#include "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/common/extensions/extension.h"
-#include "chrome/common/pref_names.h"
-#include "content/public/browser/devtools_manager.h"
-#include "content/public/test/test_utils.h"
-
-using extensions::Extension;
-
-class ExtensionActionContextMenuTest : public ExtensionBrowserTest {
-public:
-  ExtensionActionContextMenuTest() : extension_(NULL), action_(NULL) {}
-
- protected:
-  void SetupPageAction() {
-    extension_ = InstallExtension(
-        test_data_dir_.AppendASCII("browsertest")
-                      .AppendASCII("page_action_popup"),
-        1);
-    EXPECT_TRUE(extension_);
-    extensions::ExtensionActionManager* action_manager =
-        extensions::ExtensionActionManager::Get(browser()->profile());
-    action_ = action_manager->GetPageAction(*extension_);
-    EXPECT_TRUE(action_);
-
-    content::WebContents* contents =
-        browser()->tab_strip_model()->GetActiveWebContents();
-    action_->SetAppearance(ExtensionTabUtil::GetTabId(contents),
-                           ExtensionAction::ACTIVE);
-
-    BrowserWindowCocoa* window =
-        static_cast<BrowserWindowCocoa*>(browser()->window());
-    ToolbarController* toolbarController =
-        [window->cocoa_controller() toolbarController];
-    LocationBarViewMac* locationBarView =
-        [toolbarController locationBarBridge];
-    locationBarView->Update(NULL);
-  }
-
-  const Extension* extension_;
-  ExtensionAction* action_;
-};
-
-IN_PROC_BROWSER_TEST_F(ExtensionActionContextMenuTest, BasicTest) {
-  SetupPageAction();
-  base::scoped_nsobject<ExtensionActionContextMenu> menu;
-  menu.reset([[ExtensionActionContextMenu alloc] initWithExtension:extension_
-                                                           browser:browser()
-                                                   extensionAction:action_]);
-
-  NSMenuItem* inspectItem = [menu itemWithTag:
-        extension_action_context_menu::kExtensionContextInspect];
-  EXPECT_TRUE(inspectItem);
-
-  PrefService* service = browser()->profile()->GetPrefs();
-  bool original = service->GetBoolean(prefs::kExtensionsUIDeveloperMode);
-
-  service->SetBoolean(prefs::kExtensionsUIDeveloperMode, true);
-  EXPECT_FALSE([inspectItem isHidden]);
-
-  service->SetBoolean(prefs::kExtensionsUIDeveloperMode, false);
-  EXPECT_TRUE([inspectItem isHidden]);
-
-  service->SetBoolean(prefs::kExtensionsUIDeveloperMode, original);
-}
-
-// Test that browser action context menus work. Browser actions have their
-// menus created during browser initialization, when there is no tab. This
-// test simulates that and checks the menu is operational.
-IN_PROC_BROWSER_TEST_F(ExtensionActionContextMenuTest, BrowserAction) {
-  extension_ = InstallExtension(
-      test_data_dir_.AppendASCII("browsertest")
-                    .AppendASCII("browser_action_popup"),
-      1);
-  EXPECT_TRUE(extension_);
-  extensions::ExtensionActionManager* action_manager =
-      extensions::ExtensionActionManager::Get(browser()->profile());
-  action_ = action_manager->GetBrowserAction(*extension_);
-  EXPECT_TRUE(action_);
-
-  Browser* empty_browser(
-       new Browser(Browser::CreateParams(browser()->profile(),
-                                         browser()->host_desktop_type())));
-
-  base::scoped_nsobject<ExtensionActionContextMenu> menu;
-  menu.reset([[ExtensionActionContextMenu alloc]
-      initWithExtension:extension_
-                browser:empty_browser
-        extensionAction:action_]);
-
-  NSMenuItem* inspectItem = [menu itemWithTag:
-        extension_action_context_menu::kExtensionContextInspect];
-  EXPECT_TRUE(inspectItem);
-
-  // Close the empty browser. Can't just free it directly because there are
-  // dangling references in the various native controllers that must be
-  // cleaned up first.
-  NSWindow* window = empty_browser->window()->GetNativeWindow();
-  BrowserWindowController* wc =
-    [BrowserWindowController browserWindowControllerForWindow:window];
-  ASSERT_TRUE(wc != NULL);
-  [wc destroyBrowser];
-}
-
-namespace {
-
-class DevToolsAttachedObserver {
- public:
-  DevToolsAttachedObserver(const base::Closure& callback)
-      : callback_(callback),
-        devtools_callback_(base::Bind(
-            &DevToolsAttachedObserver::OnDevToolsStateChanged,
-            base::Unretained(this))) {
-    content::DevToolsManager::GetInstance()->AddAgentStateCallback(
-        devtools_callback_);
-  }
-
-  ~DevToolsAttachedObserver() {
-    content::DevToolsManager::GetInstance()->RemoveAgentStateCallback(
-        devtools_callback_);
-  }
-
-  void OnDevToolsStateChanged(content::DevToolsAgentHost*, bool attached) {
-    if (attached)
-      callback_.Run();
-  }
-
- private:
-  base::Closure callback_;
-  base::Callback<void(content::DevToolsAgentHost*, bool)> devtools_callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(DevToolsAttachedObserver);
-};
-
-}  // namespace
-
-IN_PROC_BROWSER_TEST_F(
-    ExtensionActionContextMenuTest, DISABLED_RunInspectPopup) {
-  SetupPageAction();
-  base::scoped_nsobject<ExtensionActionContextMenu> menu;
-  menu.reset([[ExtensionActionContextMenu alloc] initWithExtension:extension_
-                                                           browser:browser()
-                                                   extensionAction:action_]);
-
-  NSMenuItem* inspectItem = [menu itemWithTag:
-        extension_action_context_menu::kExtensionContextInspect];
-  EXPECT_TRUE(inspectItem);
-
-  PrefService* service = browser()->profile()->GetPrefs();
-  bool original = service->GetBoolean(prefs::kExtensionsUIDeveloperMode);
-
-  service->SetBoolean(prefs::kExtensionsUIDeveloperMode, true);
-  EXPECT_FALSE([inspectItem isHidden]);
-
-  scoped_refptr<content::MessageLoopRunner> loop_runner(
-      new content::MessageLoopRunner);
-  DevToolsAttachedObserver observer(loop_runner->QuitClosure());
-  [NSApp sendAction:[inspectItem action]
-                 to:[inspectItem target]
-               from:inspectItem];
-  loop_runner->Run();
-
-  service->SetBoolean(prefs::kExtensionsUIDeveloperMode, original);
-}
diff --git a/chrome/browser/ui/cocoa/extensions/extension_action_context_menu_controller.h b/chrome/browser/ui/cocoa/extensions/extension_action_context_menu_controller.h
new file mode 100644
index 0000000..4ebb430
--- /dev/null
+++ b/chrome/browser/ui/cocoa/extensions/extension_action_context_menu_controller.h
@@ -0,0 +1,50 @@
+// 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 CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_ACTION_CONTEXT_MENU_CONTROLLER_H_
+#define CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_ACTION_CONTEXT_MENU_CONTROLLER_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/memory/scoped_ptr.h"
+
+class AsyncUninstaller;
+class Browser;
+class ExtensionAction;
+
+namespace extensions {
+class Extension;
+}
+
+// Controller for extension context menu. This class builds the context menu
+// and handles actions. This is mostly used when the user right clicks on
+// page and browser actions in the toolbar.
+@interface ExtensionActionContextMenuController : NSObject {
+ @private
+  // The extension that the menu belongs to. Weak.
+  const extensions::Extension* extension_;
+
+  // The extension action the menu belongs to. Weak.
+  ExtensionAction* action_;
+
+  // The browser that contains this extension. Weak.
+  Browser* browser_;
+
+  // Used to load the extension icon asynchronously on the I/O thread then show
+  // the uninstall confirmation dialog.
+  scoped_ptr<AsyncUninstaller> uninstaller_;
+}
+
+// Initializes and returns a context menu controller for the given extension and
+// browser.
+- (id)initWithExtension:(const extensions::Extension*)extension
+                browser:(Browser*)browser
+        extensionAction:(ExtensionAction*)action;
+
+// Adds context menu items to the given menu.
+- (void)populateMenu:(NSMenu*)menu;
+
+@end
+
+#endif  // CHROME_BROWSER_UI_COCOA_EXTENSIONS_EXTENSION_ACTION_CONTEXT_MENU_H_
diff --git a/chrome/browser/ui/cocoa/extensions/extension_action_context_menu_controller.mm b/chrome/browser/ui/cocoa/extensions/extension_action_context_menu_controller.mm
new file mode 100644
index 0000000..a869916
--- /dev/null
+++ b/chrome/browser/ui/cocoa/extensions/extension_action_context_menu_controller.mm
@@ -0,0 +1,225 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "chrome/browser/ui/cocoa/extensions/extension_action_context_menu_controller.h"
+
+#include "base/strings/sys_string_conversions.h"
+#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
+#include "chrome/browser/extensions/extension_action_manager.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/extensions/extension_uninstall_dialog.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/browser/ui/cocoa/browser_window_cocoa.h"
+#include "chrome/browser/ui/cocoa/browser_window_controller.h"
+#include "chrome/browser/ui/cocoa/extensions/browser_actions_controller.h"
+#include "chrome/browser/ui/cocoa/extensions/extension_popup_controller.h"
+#import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
+#include "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/extensions/manifest_url_handler.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
+#include "content/public/browser/web_contents.h"
+#include "grit/chromium_strings.h"
+#include "grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+
+using content::OpenURLParams;
+using content::Referrer;
+using content::WebContents;
+using extensions::Extension;
+
+// A class that shows a confirmation dialog to uninstall the given extension.
+// Also acts as the extension's UI delegate in order to display the dialog.
+class AsyncUninstaller : public ExtensionUninstallDialog::Delegate {
+ public:
+  AsyncUninstaller(const Extension* extension, Browser* browser)
+      : extension_(extension),
+        profile_(browser->profile()) {
+    extension_uninstall_dialog_.reset(
+        ExtensionUninstallDialog::Create(profile_, browser, this));
+    extension_uninstall_dialog_->ConfirmUninstall(extension_);
+  }
+
+  virtual ~AsyncUninstaller() {}
+
+  // ExtensionUninstallDialog::Delegate:
+  virtual void ExtensionUninstallAccepted() OVERRIDE {
+    extensions::ExtensionSystem::Get(profile_)->extension_service()->
+        UninstallExtension(extension_->id(), false, NULL);
+  }
+  virtual void ExtensionUninstallCanceled() OVERRIDE {}
+
+ private:
+  // The extension that's being uninstalled.
+  const Extension* extension_;
+
+  // The current profile. Weak.
+  Profile* profile_;
+
+  scoped_ptr<ExtensionUninstallDialog> extension_uninstall_dialog_;
+
+  DISALLOW_COPY_AND_ASSIGN(AsyncUninstaller);
+};
+
+@interface ExtensionActionContextMenuController ()
+- (void)onExtensionName:(id)sender;
+- (void)onOptions:(id)sender;
+- (void)onUninstall:(id)sender;
+- (void)onHide:(id)sender;
+- (void)onManage:(id)sender;
+- (void)onInspect:(id)sender;
+@end
+
+@implementation ExtensionActionContextMenuController
+
+- (id)initWithExtension:(const Extension*)extension
+                browser:(Browser*)browser
+        extensionAction:(ExtensionAction*)action{
+  if ((self = [super init])) {
+    action_ = action;
+    extension_ = extension;
+    browser_ = browser;
+  }
+  return self;
+}
+
+- (void)populateMenu:(NSMenu*)menu {
+  [menu setAutoenablesItems:NO];
+
+  // Extension name.
+  NSMenuItem* item =
+      [menu addItemWithTitle:base::SysUTF8ToNSString(extension_->name())
+                      action:@selector(onExtensionName:)
+               keyEquivalent:@""];
+  [item setTarget:self];
+
+  // Separator.
+  [menu addItem:[NSMenuItem separatorItem]];
+
+  // Options.
+  item = [menu addItemWithTitle:
+              l10n_util::GetNSStringWithFixup(IDS_EXTENSIONS_OPTIONS_MENU_ITEM)
+                         action:@selector(onOptions:)
+                  keyEquivalent:@""];
+  [item setTarget:self];
+  [item setEnabled:extensions::ManifestURL::GetOptionsPage(
+      extension_).spec().length() > 0];
+
+
+  // Uninstall.
+  item = [menu addItemWithTitle:
+              l10n_util::GetNSStringWithFixup(IDS_EXTENSIONS_UNINSTALL)
+                         action:@selector(onUninstall:)
+                  keyEquivalent:@""];
+  [item setTarget:self];
+
+  // Hide. Only used for browser actions.
+  if (extensions::ExtensionActionManager::Get(
+          browser_->profile())->GetBrowserAction(*extension_)) {
+    item = [menu addItemWithTitle:
+                l10n_util::GetNSStringWithFixup(IDS_EXTENSIONS_HIDE_BUTTON)
+                           action:@selector(onHide:)
+                    keyEquivalent:@""];
+    [item setTarget:self];
+  }
+
+  // Separator.
+  [menu addItem:[NSMenuItem separatorItem]];
+
+  // Manage.
+  item = [menu addItemWithTitle:
+              l10n_util::GetNSStringWithFixup(IDS_MANAGE_EXTENSION)
+                         action:@selector(onManage:)
+                  keyEquivalent:@""];
+  [item setTarget:self];
+
+  // Inspect.
+  PrefService* service = browser_->profile()->GetPrefs();
+  bool devMode = service->GetBoolean(prefs::kExtensionsUIDeveloperMode);
+  if (devMode) {
+    item = [menu addItemWithTitle:l10n_util::GetNSStringWithFixup(
+                IDS_EXTENSION_ACTION_INSPECT_POPUP)
+                           action:@selector(onInspect:)
+                    keyEquivalent:@""];
+    [item setTarget:self];
+  }
+}
+
+- (void)onExtensionName:(id)sender {
+  GURL url(std::string(extension_urls::kGalleryBrowsePrefix) +
+           std::string("/detail/") + extension_->id());
+  OpenURLParams params(url,
+                       Referrer(),
+                       NEW_FOREGROUND_TAB,
+                       content::PAGE_TRANSITION_LINK,
+                       false);
+  browser_->OpenURL(params);
+}
+
+- (void)onOptions:(id)sender {
+  DCHECK(!extensions::ManifestURL::GetOptionsPage(extension_).is_empty());
+  ExtensionTabUtil::OpenOptionsPage(extension_, browser_);
+}
+
+- (void)onUninstall:(id)sender {
+  uninstaller_.reset(new AsyncUninstaller(extension_, browser_));
+}
+
+- (void)onHide:(id)sender {
+  extensions::ExtensionActionAPI::SetBrowserActionVisibility(
+      extensions::ExtensionSystem::Get(
+          browser_->profile())->extension_service()->extension_prefs(),
+      extension_->id(),
+      false);
+}
+
+- (void)onManage:(id)sender {
+  chrome::ShowExtensions(browser_, extension_->id());
+}
+
+- (void)onInspect:(id)sender {
+  BrowserWindowCocoa* window =
+      static_cast<BrowserWindowCocoa*>(browser_->window());
+  ToolbarController* toolbarController =
+      [window->cocoa_controller() toolbarController];
+  LocationBarViewMac* locationBarView = [toolbarController locationBarBridge];
+
+  extensions::ExtensionActionManager* actionManager =
+      extensions::ExtensionActionManager::Get(browser_->profile());
+  NSPoint popupPoint = NSZeroPoint;
+  if (actionManager->GetPageAction(*extension_) == action_) {
+    popupPoint = locationBarView->GetPageActionBubblePoint(action_);
+  } else if (actionManager->GetBrowserAction(*extension_) == action_) {
+    BrowserActionsController* controller =
+        [toolbarController browserActionsController];
+    popupPoint = [controller popupPointForBrowserAction:extension_];
+  } else {
+    NOTREACHED() << "action_ is not a page action or browser action?";
+  }
+
+  content::WebContents* activeTab =
+      browser_->tab_strip_model()->GetActiveWebContents();
+  if (!activeTab)
+    return;
+
+  int tabId = ExtensionTabUtil::GetTabId(activeTab);
+
+  GURL url = action_->GetPopupUrl(tabId);
+  if (!url.is_valid())
+    return;
+
+  [ExtensionPopupController showURL:url
+                          inBrowser:browser_
+                         anchoredAt:popupPoint
+                      arrowLocation:info_bubble::kTopRight
+                            devMode:YES];
+}
+
+@end
diff --git a/chrome/browser/ui/cocoa/extensions/extension_action_context_menu_controller_browsertest.mm b/chrome/browser/ui/cocoa/extensions/extension_action_context_menu_controller_browsertest.mm
new file mode 100644
index 0000000..711a4bc
--- /dev/null
+++ b/chrome/browser/ui/cocoa/extensions/extension_action_context_menu_controller_browsertest.mm
@@ -0,0 +1,201 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "chrome/browser/ui/cocoa/extensions/extension_action_context_menu_controller.h"
+
+#include "base/files/file_path.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/path_service.h"
+#include "base/prefs/pref_service.h"
+#include "chrome/browser/extensions/browser_action_test_util.h"
+#include "chrome/browser/extensions/extension_action.h"
+#include "chrome/browser/extensions/extension_action_manager.h"
+#include "chrome/browser/extensions/extension_browsertest.h"
+#include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/cocoa/browser_window_cocoa.h"
+#include "chrome/browser/ui/cocoa/browser_window_controller.h"
+#import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
+#include "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/pref_names.h"
+#include "content/public/browser/devtools_manager.h"
+#include "content/public/test/test_utils.h"
+#include "grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+
+using extensions::Extension;
+
+namespace {
+
+NSMenuItem* GetInspectItem(NSMenu* menu) {
+  return [menu itemWithTitle:
+      l10n_util::GetNSStringWithFixup(IDS_EXTENSION_ACTION_INSPECT_POPUP)];
+}
+
+}  // namespace
+
+class ExtensionActionContextMenuControllerTest : public ExtensionBrowserTest {
+ public:
+  ExtensionActionContextMenuControllerTest() : extension_(NULL), action_(NULL) {
+  }
+
+ protected:
+  void SetupPageAction() {
+    extension_ = InstallExtension(
+        test_data_dir_.AppendASCII("browsertest")
+                      .AppendASCII("page_action_popup"),
+        1);
+    EXPECT_TRUE(extension_);
+    extensions::ExtensionActionManager* action_manager =
+        extensions::ExtensionActionManager::Get(browser()->profile());
+    action_ = action_manager->GetPageAction(*extension_);
+    EXPECT_TRUE(action_);
+
+    content::WebContents* contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
+    action_->SetAppearance(ExtensionTabUtil::GetTabId(contents),
+                           ExtensionAction::ACTIVE);
+
+    BrowserWindowCocoa* window =
+        static_cast<BrowserWindowCocoa*>(browser()->window());
+    ToolbarController* toolbarController =
+        [window->cocoa_controller() toolbarController];
+    LocationBarViewMac* locationBarView =
+        [toolbarController locationBarBridge];
+    locationBarView->Update(NULL);
+  }
+
+  const Extension* extension_;
+  ExtensionAction* action_;
+};
+
+IN_PROC_BROWSER_TEST_F(ExtensionActionContextMenuControllerTest, BasicTest) {
+  SetupPageAction();
+  base::scoped_nsobject<ExtensionActionContextMenuController> controller(
+      [[ExtensionActionContextMenuController alloc] initWithExtension:extension_
+                                                              browser:browser()
+                                                      extensionAction:action_]);
+
+
+  PrefService* service = browser()->profile()->GetPrefs();
+  bool original = service->GetBoolean(prefs::kExtensionsUIDeveloperMode);
+
+  service->SetBoolean(prefs::kExtensionsUIDeveloperMode, true);
+  base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@""]);
+  [controller populateMenu:menu];
+  EXPECT_GT([menu numberOfItems], 0);
+  EXPECT_TRUE(GetInspectItem(menu));
+
+  service->SetBoolean(prefs::kExtensionsUIDeveloperMode, false);
+  [menu removeAllItems];
+  [controller populateMenu:menu];
+  EXPECT_GT([menu numberOfItems], 0);
+  EXPECT_FALSE(GetInspectItem(menu));
+
+  service->SetBoolean(prefs::kExtensionsUIDeveloperMode, original);
+}
+
+// Test that browser action context menus work. Browser actions have their
+// menus created during browser initialization, when there is no tab. This
+// test simulates that and checks the menu is operational.
+IN_PROC_BROWSER_TEST_F(ExtensionActionContextMenuControllerTest,
+                       BrowserAction) {
+  extension_ =
+      InstallExtension(test_data_dir_.AppendASCII("browsertest").AppendASCII(
+                           "browser_action_popup"),
+                       1);
+  EXPECT_TRUE(extension_);
+  extensions::ExtensionActionManager* action_manager =
+      extensions::ExtensionActionManager::Get(browser()->profile());
+  action_ = action_manager->GetBrowserAction(*extension_);
+  EXPECT_TRUE(action_);
+
+  Browser* empty_browser(
+       new Browser(Browser::CreateParams(browser()->profile(),
+                                         browser()->host_desktop_type())));
+
+  base::scoped_nsobject<ExtensionActionContextMenuController> controller(
+      [[ExtensionActionContextMenuController alloc]
+          initWithExtension:extension_
+                    browser:empty_browser
+            extensionAction:action_]);
+  base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@""]);
+  [controller populateMenu:menu];
+  EXPECT_GT([menu numberOfItems], 0);
+
+  // Close the empty browser. Can't just free it directly because there are
+  // dangling references in the various native controllers that must be
+  // cleaned up first.
+  NSWindow* window = empty_browser->window()->GetNativeWindow();
+  BrowserWindowController* wc =
+    [BrowserWindowController browserWindowControllerForWindow:window];
+  ASSERT_TRUE(wc != NULL);
+  [wc destroyBrowser];
+}
+
+namespace {
+
+class DevToolsAttachedObserver {
+ public:
+  DevToolsAttachedObserver(const base::Closure& callback)
+      : callback_(callback),
+        devtools_callback_(base::Bind(
+            &DevToolsAttachedObserver::OnDevToolsStateChanged,
+            base::Unretained(this))) {
+    content::DevToolsManager::GetInstance()->AddAgentStateCallback(
+        devtools_callback_);
+  }
+
+  ~DevToolsAttachedObserver() {
+    content::DevToolsManager::GetInstance()->RemoveAgentStateCallback(
+        devtools_callback_);
+  }
+
+  void OnDevToolsStateChanged(content::DevToolsAgentHost*, bool attached) {
+    if (attached)
+      callback_.Run();
+  }
+
+ private:
+  base::Closure callback_;
+  base::Callback<void(content::DevToolsAgentHost*, bool)> devtools_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(DevToolsAttachedObserver);
+};
+
+}  // namespace
+
+IN_PROC_BROWSER_TEST_F(
+    ExtensionActionContextMenuControllerTest, DISABLED_RunInspectPopup) {
+  SetupPageAction();
+  base::scoped_nsobject<ExtensionActionContextMenuController> controller(
+      [[ExtensionActionContextMenuController alloc] initWithExtension:extension_
+                                                              browser:browser()
+                                                      extensionAction:action_]);
+
+  PrefService* service = browser()->profile()->GetPrefs();
+  bool original = service->GetBoolean(prefs::kExtensionsUIDeveloperMode);
+
+  service->SetBoolean(prefs::kExtensionsUIDeveloperMode, true);
+
+  base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@""]);
+  [controller populateMenu:menu];
+  EXPECT_GT([menu numberOfItems], 0);
+  NSMenuItem* inspectItem = GetInspectItem(menu);
+  EXPECT_TRUE(inspectItem);
+
+  scoped_refptr<content::MessageLoopRunner> loop_runner(
+      new content::MessageLoopRunner);
+  DevToolsAttachedObserver observer(loop_runner->QuitClosure());
+  [NSApp sendAction:[inspectItem action]
+                 to:[inspectItem target]
+               from:inspectItem];
+  loop_runner->Run();
+
+  service->SetBoolean(prefs::kExtensionsUIDeveloperMode, original);
+}
diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_view_controller.mm b/chrome/browser/ui/cocoa/extensions/extension_install_view_controller.mm
index 4e69bdd..7fae834 100644
--- a/chrome/browser/ui/cocoa/extensions/extension_install_view_controller.mm
+++ b/chrome/browser/ui/cocoa/extensions/extension_install_view_controller.mm
@@ -27,33 +27,68 @@
 using content::Referrer;
 using extensions::BundleInstaller;
 
+namespace {
+
+// A collection of attributes (bitmask) for how to draw a cell, the expand
+// marker and the text in the cell.
+enum CellAttributesMask {
+  kBoldText                = 1 << 0,
+  kNoExpandMarker          = 1 << 1,
+  kUseBullet               = 1 << 2,
+  kAutoExpandCell          = 1 << 3,
+  kUseCustomLinkCell       = 1 << 4,
+  kCanExpand               = 1 << 5,
+};
+
+typedef NSUInteger CellAttributes;
+
+}  // namespace.
+
 @interface ExtensionInstallViewController ()
 - (BOOL)isBundleInstall;
 - (BOOL)isInlineInstall;
 - (void)appendRatingStar:(const gfx::ImageSkia*)skiaImage;
 - (void)onOutlineViewRowCountDidChange;
 - (NSDictionary*)buildItemWithTitle:(NSString*)title
-                        isGroupItem:(BOOL)isGroupItem
+                     cellAttributes:(CellAttributes)cellAttributes
                            children:(NSArray*)children;
-- (NSDictionary*)buildIssue:(const IssueAdviceInfoEntry&)issue;
+- (NSDictionary*)buildDetailToggleItem:(size_t)type
+                 permissionsDetailIndex:(size_t)index;
 - (NSArray*)buildWarnings:(const ExtensionInstallPrompt::Prompt&)prompt;
 - (void)updateViewFrame:(NSRect)frame;
 @end
 
+@interface DetailToggleHyperlinkButtonCell : HyperlinkButtonCell {
+  NSUInteger permissionsDetailIndex_;
+  ExtensionInstallPrompt::DetailsType permissionsDetailType_;
+  SEL linkClickedAction_;
+}
+
+@property(assign, nonatomic) NSUInteger permissionsDetailIndex;
+@property(assign, nonatomic)
+    ExtensionInstallPrompt::DetailsType permissionsDetailType;
+@property(assign, nonatomic) SEL linkClickedAction;
+
+@end
+
 namespace {
 
 // Padding above the warnings separator, we must also subtract this when hiding
 // it.
 const CGFloat kWarningsSeparatorPadding = 14;
 
+// The left padding for the link cell.
+const CGFloat kLinkCellPaddingLeft = 3;
+
 // Maximum height we will adjust controls to when trying to accomodate their
 // contents.
-const CGFloat kMaxControlHeight = 400;
+const CGFloat kMaxControlHeight = 250;
 
 NSString* const kTitleKey = @"title";
-NSString* const kIsGroupItemKey = @"isGroupItem";
 NSString* const kChildrenKey = @"children";
-NSString* const kCanExpandKey = @"canExpand";
+NSString* const kCellAttributesKey = @"cellAttributes";
+NSString* const kPermissionsDetailIndex = @"permissionsDetailIndex";
+NSString* const kPermissionsDetailType = @"permissionsDetailType";
 
 // Adjust the |control|'s height so that its content is not clipped.
 // This also adds the change in height to the |totalOffset| and shifts the
@@ -122,6 +157,10 @@
   [[NSBezierPath bezierPathWithOvalInRect:rect] fill];
 }
 
+bool HasAttribute(id item, CellAttributesMask attributeMask) {
+  return [[item objectForKey:kCellAttributesKey] intValue] & attributeMask;
+}
+
 }  // namespace
 
 @implementation ExtensionInstallViewController
@@ -277,10 +316,9 @@
     spacing.height += 2;
     [outlineView_ setIntercellSpacing:spacing];
     [[[[outlineView_ tableColumns] objectAtIndex:0] dataCell] setWraps:YES];
-    for (id item in warnings_.get()) {
-      if ([[item objectForKey:kIsGroupItemKey] boolValue])
-        [outlineView_ expandItem:item expandChildren:NO];
-    }
+    for (id item in warnings_.get())
+      [self expandItemAndChildren:item];
+
     // Adjust the outline view to fit the warnings.
     OffsetOutlineViewVerticallyToFitContent(outlineView_, &totalOffset);
   } else if ([self isInlineInstall] || [self isBundleInstall]) {
@@ -362,8 +400,10 @@
   numberOfChildrenOfItem:(id)item {
   if (!item)
     return [warnings_ count];
+
   if ([item isKindOfClass:[NSDictionary class]])
     return [[item objectForKey:kChildrenKey] count];
+
   NOTREACHED();
   return 0;
 }
@@ -376,7 +416,7 @@
 
 - (BOOL)outlineView:(NSOutlineView *)outlineView
    shouldExpandItem:(id)item {
-  return [[item objectForKey:kCanExpandKey] boolValue];
+  return HasAttribute(item, kCanExpand);
 }
 
 - (void)outlineViewItemDidExpand:sender {
@@ -412,16 +452,21 @@
 
 - (BOOL)outlineView:(NSOutlineView*)outlineView
     shouldShowOutlineCellForItem:(id)item {
-  // The top most group header items are always expanded so hide their
-  // disclosure trianggles.
-  return ![[item objectForKey:kIsGroupItemKey] boolValue];
+  return !HasAttribute(item, kNoExpandMarker);
+}
+
+- (BOOL)outlineView:(NSOutlineView*)outlineView
+    shouldTrackCell:(NSCell*)cell
+     forTableColumn:(NSTableColumn*)tableColumn
+               item:(id)item {
+  return HasAttribute(item, kUseCustomLinkCell);
 }
 
 - (void)outlineView:(NSOutlineView*)outlineView
     willDisplayCell:(id)cell
      forTableColumn:(NSTableColumn *)tableColumn
                item:(id)item {
-  if ([[item objectForKey:kIsGroupItemKey] boolValue])
+  if (HasAttribute(item, kBoldText))
     [cell setFont:[NSFont boldSystemFontOfSize:12.0]];
   else
     [cell setFont:[NSFont systemFontOfSize:12.0]];
@@ -431,15 +476,21 @@
     willDisplayOutlineCell:(id)cell
             forTableColumn:(NSTableColumn *)tableColumn
                       item:(id)item {
-  // Replace disclosure triangles with bullet lists for leaf nodes.
-  if (![[item objectForKey:kCanExpandKey] boolValue]) {
+  if (HasAttribute(item, kNoExpandMarker)) {
+    [cell setImagePosition:NSNoImage];
+    return;
+  }
+
+  if (HasAttribute(item, kUseBullet)) {
+    // Replace disclosure triangles with bullet lists for leaf nodes.
     [cell setImagePosition:NSNoImage];
     DrawBulletInFrame([outlineView_ frameOfOutlineCellAtRow:
         [outlineView_ rowForItem:item]]);
-  } else {
-    // Reset image to default value.
-    [cell setImagePosition:NSImageOverlaps];
+    return;
   }
+
+  // Reset image to default value.
+  [cell setImagePosition:NSImageOverlaps];
 }
 
 - (BOOL)outlineView:(NSOutlineView *)outlineView
@@ -447,59 +498,128 @@
   return false;
 }
 
+- (NSCell*)outlineView:(NSOutlineView*)outlineView
+    dataCellForTableColumn:(NSTableColumn*)tableColumn
+                  item:(id)item {
+  if (HasAttribute(item, kUseCustomLinkCell)) {
+    base::scoped_nsobject<DetailToggleHyperlinkButtonCell> cell(
+        [[DetailToggleHyperlinkButtonCell alloc] initTextCell:@""]);
+    [cell setTarget:self];
+    [cell setLinkClickedAction:@selector(onToggleDetailsLinkClicked:)];
+    [cell setAlignment:NSLeftTextAlignment];
+    [cell setUnderlineOnHover:YES];
+    [cell setTextColor:
+        gfx::SkColorToCalibratedNSColor(chrome_style::GetLinkColor())];
+
+    size_t detailsIndex =
+        [[item objectForKey:kPermissionsDetailIndex] unsignedIntegerValue];
+    [cell setPermissionsDetailIndex:detailsIndex];
+
+    ExtensionInstallPrompt::DetailsType detailsType =
+        static_cast<ExtensionInstallPrompt::DetailsType>(
+            [[item objectForKey:kPermissionsDetailType] unsignedIntegerValue]);
+    [cell setPermissionsDetailType:detailsType];
+
+    if (prompt_->GetIsShowingDetails(detailsType, detailsIndex)) {
+      [cell setTitle:
+          l10n_util::GetNSStringWithFixup(IDS_EXTENSIONS_HIDE_DETAILS)];
+    } else {
+      [cell setTitle:
+          l10n_util::GetNSStringWithFixup(IDS_EXTENSIONS_SHOW_DETAILS)];
+    }
+
+    return cell.autorelease();
+  } else {
+    return [tableColumn dataCell];
+  }
+}
+
+- (void)expandItemAndChildren:(id)item {
+  if (HasAttribute(item, kAutoExpandCell))
+    [outlineView_ expandItem:item expandChildren:NO];
+
+  for (id child in [item objectForKey:kChildrenKey])
+    [self expandItemAndChildren:child];
+}
+
+- (void)onToggleDetailsLinkClicked:(id)sender {
+  size_t index = [sender permissionsDetailIndex];
+  ExtensionInstallPrompt::DetailsType type = [sender permissionsDetailType];
+  prompt_->SetIsShowingDetails(
+      type, index, !prompt_->GetIsShowingDetails(type, index));
+
+  warnings_.reset([[self buildWarnings:*prompt_] retain]);
+  [outlineView_ reloadData];
+
+  for (id item in warnings_.get())
+    [self expandItemAndChildren:item];
+}
+
 - (NSDictionary*)buildItemWithTitle:(NSString*)title
-                        isGroupItem:(BOOL)isGroupItem
+                     cellAttributes:(CellAttributes)cellAttributes
                            children:(NSArray*)children {
-  BOOL canExpand = YES;
-  if (!children) {
+  if (!children || ([children count] == 0 && cellAttributes & kUseBullet)) {
     // Add a dummy child even though this is a leaf node. This will cause
     // the outline view to show a disclosure triangle for this item.
     // This is later overriden in willDisplayOutlineCell: to draw a bullet
     // instead. (The bullet could be placed in the title instead but then
     // the bullet wouldn't line up with disclosure triangles of sibling nodes.)
     children = [NSArray arrayWithObject:[NSDictionary dictionary]];
-    canExpand = NO;
+  } else {
+    cellAttributes = cellAttributes | kCanExpand;
   }
 
   return @{
-      kTitleKey:       title,
-      kIsGroupItemKey: [NSNumber numberWithBool:isGroupItem],
-      kChildrenKey:    children,
-      kCanExpandKey:   [NSNumber numberWithBool:canExpand]
+    kTitleKey : title,
+    kChildrenKey : children,
+    kCellAttributesKey : [NSNumber numberWithInt:cellAttributes],
+    kPermissionsDetailIndex : @0ul,
+    kPermissionsDetailType : @0ul,
   };
 }
 
-- (NSDictionary*)buildIssue:(const IssueAdviceInfoEntry&)issue {
-  if (issue.details.empty()) {
-    return [self buildItemWithTitle:SysUTF16ToNSString(issue.description)
-                        isGroupItem:NO
-                           children:nil];
-  }
-
-  NSMutableArray* details = [NSMutableArray array];
-  for (size_t j = 0; j < issue.details.size(); ++j) {
-    [details addObject:
-        [self buildItemWithTitle:SysUTF16ToNSString(issue.details[j])
-                     isGroupItem:NO
-                        children:nil]];
-   }
-  return [self buildItemWithTitle:SysUTF16ToNSString(issue.description)
-                      isGroupItem:NO
-                         children:details];
+- (NSDictionary*)buildDetailToggleItem:(size_t)type
+                permissionsDetailIndex:(size_t)index {
+  return @{
+    kTitleKey : @"",
+    kChildrenKey : @[ @{} ],
+    kCellAttributesKey : [NSNumber numberWithInt:kUseCustomLinkCell |
+                                                 kNoExpandMarker],
+    kPermissionsDetailIndex : [NSNumber numberWithUnsignedInteger:index],
+    kPermissionsDetailType : [NSNumber numberWithUnsignedInteger:type],
+  };
 }
 
 - (NSArray*)buildWarnings:(const ExtensionInstallPrompt::Prompt&)prompt {
   NSMutableArray* warnings = [NSMutableArray array];
   NSString* heading = nil;
 
+  ExtensionInstallPrompt::DetailsType type =
+      ExtensionInstallPrompt::PERMISSIONS_DETAILS;
   if (prompt.ShouldShowPermissions()) {
     NSMutableArray* children = [NSMutableArray array];
     if (prompt.GetPermissionCount() > 0) {
       for (size_t i = 0; i < prompt.GetPermissionCount(); ++i) {
         [children addObject:
             [self buildItemWithTitle:SysUTF16ToNSString(prompt.GetPermission(i))
-                         isGroupItem:NO
+                      cellAttributes:kUseBullet
                             children:nil]];
+
+        // If there are additional details, add them below this item.
+        if (!prompt.GetPermissionsDetails(i).empty()) {
+          if (prompt.GetIsShowingDetails(
+              ExtensionInstallPrompt::PERMISSIONS_DETAILS, i)) {
+            [children addObject:
+                [self buildItemWithTitle:SysUTF16ToNSString(
+                    prompt.GetPermissionsDetails(i))
+                          cellAttributes:kNoExpandMarker
+                                children:nil]];
+          }
+
+          // Add a row for the link.
+          [children addObject:
+              [self buildDetailToggleItem:type permissionsDetailIndex:i]];
+        }
       }
 
       heading = SysUTF16ToNSString(prompt.GetPermissionsHeading());
@@ -507,39 +627,76 @@
       [children addObject:
           [self buildItemWithTitle:
               l10n_util::GetNSString(IDS_EXTENSION_NO_SPECIAL_PERMISSIONS)
-                       isGroupItem:NO
+                    cellAttributes:kUseBullet
                           children:nil]];
       heading = @"";
     }
+
     [warnings addObject:[self
         buildItemWithTitle:heading
-               isGroupItem:YES
+            cellAttributes:kBoldText | kAutoExpandCell | kNoExpandMarker
                   children:children]];
   }
 
   if (prompt.GetOAuthIssueCount() > 0) {
+    type = ExtensionInstallPrompt::OAUTH_DETAILS;
+
     NSMutableArray* children = [NSMutableArray array];
-    for (size_t i = 0; i < prompt.GetOAuthIssueCount(); ++i)
-      [children addObject:[self buildIssue:prompt.GetOAuthIssue(i)]];
+
+    for (size_t i = 0; i < prompt.GetOAuthIssueCount(); ++i) {
+      NSMutableArray* details = [NSMutableArray array];
+      const IssueAdviceInfoEntry& issue = prompt.GetOAuthIssue(i);
+      if (!issue.details.empty() && prompt.GetIsShowingDetails(type, i)) {
+        for (size_t j = 0; j < issue.details.size(); ++j) {
+          [details addObject:
+              [self buildItemWithTitle:SysUTF16ToNSString(issue.details[j])
+                        cellAttributes:kNoExpandMarker
+                              children:nil]];
+        }
+      }
+
+      [children addObject:
+          [self buildItemWithTitle:SysUTF16ToNSString(issue.description)
+                    cellAttributes:kUseBullet | kAutoExpandCell
+                          children:details]];
+
+      if (!issue.details.empty()) {
+        // Add a row for the link.
+        [children addObject:
+            [self buildDetailToggleItem:type permissionsDetailIndex:i]];
+      }
+    }
+
     [warnings addObject:
-        [self buildItemWithTitle:SysUTF16ToNSString(prompt.GetOAuthHeading())
-                     isGroupItem:YES
-                        children:children]];
+    [self buildItemWithTitle:SysUTF16ToNSString(prompt.GetOAuthHeading())
+              cellAttributes:kBoldText | kAutoExpandCell| kNoExpandMarker
+                    children:children]];
   }
 
   if (prompt.GetRetainedFileCount() > 0) {
+    type = ExtensionInstallPrompt::RETAINED_FILES_DETAILS;
+
     NSMutableArray* children = [NSMutableArray array];
-    for (size_t i = 0; i < prompt.GetRetainedFileCount(); ++i) {
-      [children addObject:
-          [self buildItemWithTitle:SysUTF16ToNSString(prompt.GetRetainedFile(i))
-                       isGroupItem:NO
-                          children:nil]];
+
+    if (prompt.GetIsShowingDetails(type, 0)) {
+      for (size_t i = 0; i < prompt.GetRetainedFileCount(); ++i) {
+        [children addObject:
+            [self buildItemWithTitle:SysUTF16ToNSString(
+                prompt.GetRetainedFile(i))
+                      cellAttributes:kUseBullet
+                            children:nil]];
+      }
     }
+
     [warnings addObject:
         [self buildItemWithTitle:SysUTF16ToNSString(
-            prompt.GetRetainedFilesHeading())
-                     isGroupItem:YES
+            prompt.GetRetainedFilesHeadingWithCount())
+                  cellAttributes:kBoldText | kAutoExpandCell | kNoExpandMarker
                         children:children]];
+
+    // Add a row for the link.
+    [warnings addObject:
+        [self buildDetailToggleItem:type permissionsDetailIndex:0]];
   }
 
   return warnings;
@@ -552,3 +709,71 @@
 }
 
 @end
+
+
+@implementation DetailToggleHyperlinkButtonCell
+
+@synthesize permissionsDetailIndex = permissionsDetailIndex_;
+@synthesize permissionsDetailType = permissionsDetailType_;
+@synthesize linkClickedAction = linkClickedAction_;
+
++ (BOOL)prefersTrackingUntilMouseUp {
+  return YES;
+}
+
+- (NSRect)drawingRectForBounds:(NSRect)rect {
+  NSRect rectInset = NSMakeRect(rect.origin.x + kLinkCellPaddingLeft,
+                                rect.origin.y,
+                                rect.size.width - kLinkCellPaddingLeft,
+                                rect.size.height);
+  return [super drawingRectForBounds:rectInset];
+}
+
+- (NSUInteger)hitTestForEvent:(NSEvent*)event
+                       inRect:(NSRect)cellFrame
+                       ofView:(NSView*)controlView {
+  NSUInteger hitTestResult =
+      [super hitTestForEvent:event inRect:cellFrame ofView:controlView];
+  if ((hitTestResult & NSCellHitContentArea) != 0)
+    hitTestResult |= NSCellHitTrackableArea;
+  return hitTestResult;
+}
+
+- (void)handleLinkClicked {
+  [NSApp sendAction:linkClickedAction_ to:[self target] from:self];
+}
+
+- (BOOL)trackMouse:(NSEvent*)event
+            inRect:(NSRect)cellFrame
+            ofView:(NSView*)controlView
+      untilMouseUp:(BOOL)flag {
+  BOOL result = YES;
+  NSUInteger hitTestResult =
+      [self hitTestForEvent:event inRect:cellFrame ofView:controlView];
+  if ((hitTestResult & NSCellHitContentArea) != 0) {
+    result = [super trackMouse:event
+                        inRect:cellFrame
+                        ofView:controlView
+                  untilMouseUp:flag];
+    event = [NSApp currentEvent];
+    hitTestResult =
+        [self hitTestForEvent:event inRect:cellFrame ofView:controlView];
+    if ((hitTestResult & NSCellHitContentArea) != 0)
+      [self handleLinkClicked];
+  }
+  return result;
+}
+
+- (NSArray*)accessibilityActionNames {
+  return [[super accessibilityActionNames]
+      arrayByAddingObject:NSAccessibilityPressAction];
+}
+
+- (void)accessibilityPerformAction:(NSString*)action {
+  if ([action isEqualToString:NSAccessibilityPressAction])
+    [self handleLinkClicked];
+  else
+    [super accessibilityPerformAction:action];
+}
+
+@end
diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_view_controller_unittest.mm b/chrome/browser/ui/cocoa/extensions/extension_install_view_controller_unittest.mm
index 9e2cb81..d1caf8e 100644
--- a/chrome/browser/ui/cocoa/extensions/extension_install_view_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/extensions/extension_install_view_controller_unittest.mm
@@ -39,6 +39,10 @@
   std::vector<string16> permissions;
   permissions.push_back(UTF8ToUTF16("warning 1"));
   prompt.SetPermissions(permissions);
+  // No details provided with this permission.
+  std::vector<string16> details;
+  details.push_back(string16());
+  prompt.SetPermissionsDetails(details);
 
   base::scoped_nsobject<ExtensionInstallViewController> controller(
       [[ExtensionInstallViewController alloc] initWithNavigator:browser()
@@ -63,10 +67,10 @@
   NSOutlineView* outlineView = [controller outlineView];
   EXPECT_TRUE(outlineView);
   EXPECT_EQ(2, [outlineView numberOfRows]);
-  EXPECT_NSEQ([[outlineView dataSource] outlineView:outlineView
+  EXPECT_NSEQ(base::SysUTF16ToNSString(prompt.GetPermission(0)),
+              [[outlineView dataSource] outlineView:outlineView
                           objectValueForTableColumn:nil
-                                             byItem:[outlineView itemAtRow:1]],
-              base::SysUTF16ToNSString(prompt.GetPermission(0)));
+                                             byItem:[outlineView itemAtRow:1]]);
 
   EXPECT_TRUE([controller cancelButton]);
   EXPECT_NE(0u, [[[controller cancelButton] stringValue] length]);
@@ -82,7 +86,6 @@
   EXPECT_EQ(0, delegate.proceed_count());
 }
 
-
 TEST_F(ExtensionInstallViewControllerTest, BasicsNormalOK) {
   chrome::MockExtensionInstallPromptDelegate delegate;
 
@@ -91,6 +94,10 @@
   std::vector<string16> permissions;
   permissions.push_back(UTF8ToUTF16("warning 1"));
   prompt.SetPermissions(permissions);
+  // No details provided with this permission.
+  std::vector<string16> details;
+  details.push_back(string16());
+  prompt.SetPermissionsDetails(details);
 
   base::scoped_nsobject<ExtensionInstallViewController> controller(
       [[ExtensionInstallViewController alloc] initWithNavigator:browser()
@@ -115,11 +122,18 @@
   std::vector<string16> permissions;
   permissions.push_back(UTF8ToUTF16("warning 1"));
   one_warning_prompt.SetPermissions(permissions);
+  // No details provided with this permission.
+  std::vector<string16> details;
+  details.push_back(string16());
+  one_warning_prompt.SetPermissionsDetails(details);
 
   ExtensionInstallPrompt::Prompt two_warnings_prompt =
       chrome::BuildExtensionInstallPrompt(extension_.get());
   permissions.push_back(UTF8ToUTF16("warning 2"));
   two_warnings_prompt.SetPermissions(permissions);
+  // No details provided with this permission.
+  details.push_back(string16());
+  two_warnings_prompt.SetPermissionsDetails(details);
 
   base::scoped_nsobject<ExtensionInstallViewController> controller1(
       [[ExtensionInstallViewController alloc]
@@ -255,12 +269,19 @@
   std::vector<string16> permissions;
   permissions.push_back(UTF8ToUTF16("warning 1"));
   prompt.SetPermissions(permissions);
+  // No details provided with this permission.
+  std::vector<string16> details;
+  details.push_back(string16());
+  prompt.SetPermissionsDetails(details);
+
   IssueAdviceInfoEntry issue;
   issue.description = UTF8ToUTF16("issue description 1");
   issue.details.push_back(UTF8ToUTF16("issue detail 1"));
   IssueAdviceInfo issues;
   issues.push_back(issue);
   prompt.SetOAuthIssueAdvice(issues);
+  prompt.SetIsShowingDetails(
+      ExtensionInstallPrompt::OAUTH_DETAILS, 0, true);
 
   base::scoped_nsobject<ExtensionInstallViewController> controller(
       [[ExtensionInstallViewController alloc] initWithNavigator:browser()
@@ -270,11 +291,16 @@
   [controller view];  // Force nib load.
   NSOutlineView* outlineView = [controller outlineView];
   EXPECT_TRUE(outlineView);
-  EXPECT_EQ(4, [outlineView numberOfRows]);
-  EXPECT_NSEQ([[outlineView dataSource] outlineView:outlineView
+  EXPECT_EQ(6, [outlineView numberOfRows]);
+  EXPECT_NSEQ(base::SysUTF16ToNSString(prompt.GetOAuthIssue(0).description),
+              [[outlineView dataSource] outlineView:outlineView
                           objectValueForTableColumn:nil
-                                             byItem:[outlineView itemAtRow:3]],
-              base::SysUTF16ToNSString(prompt.GetOAuthIssue(0).description));
+                                             byItem:[outlineView itemAtRow:3]]);
+
+  EXPECT_NSEQ(base::SysUTF16ToNSString(prompt.GetOAuthIssue(0).details[0]),
+              [[outlineView dataSource] outlineView:outlineView
+                          objectValueForTableColumn:nil
+                                             byItem:[outlineView itemAtRow:4]]);
 }
 
 TEST_F(ExtensionInstallViewControllerTest, PostInstallPermissionsPrompt) {
@@ -285,6 +311,10 @@
   std::vector<string16> permissions;
   permissions.push_back(UTF8ToUTF16("warning 1"));
   prompt.SetPermissions(permissions);
+  // No details provided with this permission.
+  std::vector<string16> details;
+  details.push_back(string16());
+  prompt.SetPermissionsDetails(details);
 
   base::scoped_nsobject<ExtensionInstallViewController> controller(
       [[ExtensionInstallViewController alloc] initWithNavigator:browser()
@@ -299,3 +329,35 @@
   [controller cancel:nil];
   EXPECT_EQ(1, delegate.abort_count());
 }
+
+// Test that permission details show up.
+TEST_F(ExtensionInstallViewControllerTest, PermissionsDetails) {
+  chrome::MockExtensionInstallPromptDelegate delegate;
+
+  ExtensionInstallPrompt::Prompt prompt =
+      chrome::BuildExtensionInstallPrompt(extension_.get());
+
+  std::vector<string16> permissions;
+  permissions.push_back(UTF8ToUTF16("warning 1"));
+  std::vector<string16> permissions_details;
+  permissions_details.push_back(UTF8ToUTF16("Detail 1"));
+  prompt.SetPermissions(permissions);
+  prompt.SetPermissionsDetails(permissions_details);
+  prompt.SetIsShowingDetails(
+      ExtensionInstallPrompt::PERMISSIONS_DETAILS, 0, true);
+
+  base::scoped_nsobject<ExtensionInstallViewController> controller(
+      [[ExtensionInstallViewController alloc] initWithNavigator:browser()
+                                                       delegate:&delegate
+                                                         prompt:prompt]);
+
+  [controller view];  // Force nib load.
+
+  NSOutlineView* outlineView = [controller outlineView];
+  EXPECT_TRUE(outlineView);
+  EXPECT_EQ(4, [outlineView numberOfRows]);
+  EXPECT_NSEQ(base::SysUTF16ToNSString(prompt.GetPermissionsDetails(0)),
+              [[outlineView dataSource] outlineView:outlineView
+                          objectValueForTableColumn:nil
+                                             byItem:[outlineView itemAtRow:2]]);
+}
diff --git a/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm b/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm
index 735420f..d255753 100644
--- a/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm
+++ b/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.mm
@@ -122,18 +122,15 @@
     icon_.reset([gfx::SkBitmapToNSImage(icon) retain]);
     pageActionPreviewShowing_ = NO;
 
-    extensions::ExtensionActionManager* extension_action_manager =
-        extensions::ExtensionActionManager::Get(browser_->profile());
-
     if (bundle_) {
       type_ = extension_installed_bubble::kBundle;
     } else if (extension->is_app()) {
       type_ = extension_installed_bubble::kApp;
     } else if (!extensions::OmniboxInfo::GetKeyword(extension).empty()) {
       type_ = extension_installed_bubble::kOmniboxKeyword;
-    } else if (extension_action_manager->GetBrowserAction(*extension)) {
+    } else if (extensions::ActionInfo::GetBrowserActionInfo(extension)) {
       type_ = extension_installed_bubble::kBrowserAction;
-    } else if (extension_action_manager->GetPageAction(*extension) &&
+    } else if (extensions::ActionInfo::GetPageActionInfo(extension) &&
                extensions::ActionInfo::IsVerboseInstallMessage(extension)) {
       type_ = extension_installed_bubble::kPageAction;
     } else {
diff --git a/chrome/browser/ui/cocoa/extensions/extension_popup_controller.h b/chrome/browser/ui/cocoa/extensions/extension_popup_controller.h
index 121ca64..84bf9c2 100644
--- a/chrome/browser/ui/cocoa/extensions/extension_popup_controller.h
+++ b/chrome/browser/ui/cocoa/extensions/extension_popup_controller.h
@@ -51,6 +51,10 @@
   // Whether the popup has a devtools window attached to it.
   BOOL beingInspected_;
 
+  // There's an extra windowDidResignKey: notification right after a
+  // ConstrainedWindow closes that should be ignored.
+  BOOL ignoreWindowDidResignKey_;
+
   // The size once the ExtensionView has loaded.
   NSSize pendingSize_;
 }
diff --git a/chrome/browser/ui/cocoa/extensions/extension_popup_controller.mm b/chrome/browser/ui/cocoa/extensions/extension_popup_controller.mm
index 8cdb921..13493e4 100644
--- a/chrome/browser/ui/cocoa/extensions/extension_popup_controller.mm
+++ b/chrome/browser/ui/cocoa/extensions/extension_popup_controller.mm
@@ -17,6 +17,7 @@
 #import "chrome/browser/ui/cocoa/browser_window_cocoa.h"
 #import "chrome/browser/ui/cocoa/extensions/extension_view_mac.h"
 #import "chrome/browser/ui/cocoa/info_bubble_window.h"
+#include "components/web_modal/web_contents_modal_dialog_manager.h"
 #include "content/public/browser/devtools_agent_host.h"
 #include "content/public/browser/devtools_manager.h"
 #include "content/public/browser/notification_details.h"
@@ -163,6 +164,7 @@
                          anchoredAt:anchoredAt])) {
     host_.reset(host);
     beingInspected_ = devMode;
+    ignoreWindowDidResignKey_ = NO;
 
     InfoBubbleView* view = self.bubble;
     [view setArrowLocation:arrowLocation];
@@ -201,6 +203,20 @@
   DevToolsWindow::OpenDevToolsWindow(host_->render_view_host());
 }
 
+- (void)close {
+  // |windowWillClose:| could have already been called. http://crbug.com/279505
+  if (host_) {
+    web_modal::WebContentsModalDialogManager* modalDialogManager =
+        web_modal::WebContentsModalDialogManager::FromWebContents(
+            host_->host_contents());
+    if (modalDialogManager &&
+        modalDialogManager->IsShowingDialog()) {
+      return;
+    }
+  }
+  [super close];
+}
+
 - (void)windowWillClose:(NSNotification *)notification {
   [super windowWillClose:notification];
   gPopup = nil;
@@ -210,6 +226,25 @@
 }
 
 - (void)windowDidResignKey:(NSNotification*)notification {
+  // |windowWillClose:| could have already been called. http://crbug.com/279505
+  if (host_) {
+    // When a modal dialog is opened on top of the popup and when it's closed,
+    // it steals key-ness from the popup. Don't close the popup when this
+    // happens. There's an extra windowDidResignKey: notification after the
+    // modal dialog closes that should also be ignored.
+    web_modal::WebContentsModalDialogManager* modalDialogManager =
+        web_modal::WebContentsModalDialogManager::FromWebContents(
+            host_->host_contents());
+    if (modalDialogManager &&
+        modalDialogManager->IsShowingDialog()) {
+      ignoreWindowDidResignKey_ = YES;
+      return;
+    }
+    if (ignoreWindowDidResignKey_) {
+      ignoreWindowDidResignKey_ = NO;
+      return;
+    }
+  }
   if (!beingInspected_)
     [super windowDidResignKey:notification];
 }
diff --git a/chrome/browser/ui/cocoa/find_bar/find_bar_bridge.h b/chrome/browser/ui/cocoa/find_bar/find_bar_bridge.h
index 1a3b60a..9a50da8 100644
--- a/chrome/browser/ui/cocoa/find_bar/find_bar_bridge.h
+++ b/chrome/browser/ui/cocoa/find_bar/find_bar_bridge.h
@@ -59,6 +59,7 @@
   virtual void ClearResults(const FindNotificationDetails& results) OVERRIDE;
   virtual void StopAnimation() OVERRIDE;
   virtual void SetFindText(const string16& find_text) OVERRIDE;
+  virtual string16 GetFindText() OVERRIDE;
   virtual void UpdateUIForFindResult(const FindNotificationDetails& result,
                                      const string16& find_text) OVERRIDE;
   virtual void AudibleAlert() OVERRIDE;
@@ -72,7 +73,6 @@
   // Methods from FindBarTesting.
   virtual bool GetFindBarWindowInfo(gfx::Point* position,
                                     bool* fully_visible) OVERRIDE;
-  virtual string16 GetFindText() OVERRIDE;
   virtual string16 GetFindSelectedText() OVERRIDE;
   virtual string16 GetMatchCountText() OVERRIDE;
   virtual int GetWidth() OVERRIDE;
diff --git a/chrome/browser/ui/cocoa/find_bar/find_bar_bridge.mm b/chrome/browser/ui/cocoa/find_bar/find_bar_bridge.mm
index 1e0e184..a33fbd3 100644
--- a/chrome/browser/ui/cocoa/find_bar/find_bar_bridge.mm
+++ b/chrome/browser/ui/cocoa/find_bar/find_bar_bridge.mm
@@ -56,6 +56,10 @@
   [cocoa_controller_ setFindText:base::SysUTF16ToNSString(find_text)];
 }
 
+string16 FindBarBridge::GetFindText() {
+  return base::SysNSStringToUTF16([cocoa_controller_ findText]);
+}
+
 void FindBarBridge::UpdateUIForFindResult(const FindNotificationDetails& result,
                                           const string16& find_text) {
   [cocoa_controller_ updateUIForFindResult:result withText:find_text];
@@ -109,10 +113,6 @@
   return window_visible;
 }
 
-string16 FindBarBridge::GetFindText() {
-  return base::SysNSStringToUTF16([cocoa_controller_ findText]);
-}
-
 string16 FindBarBridge::GetFindSelectedText() {
   // This function is currently only used in Views.
   NOTIMPLEMENTED();
diff --git a/chrome/browser/ui/cocoa/infobars/alternate_nav_infobar_controller.mm b/chrome/browser/ui/cocoa/infobars/alternate_nav_infobar_controller.mm
index a8b784d..9d64080 100644
--- a/chrome/browser/ui/cocoa/infobars/alternate_nav_infobar_controller.mm
+++ b/chrome/browser/ui/cocoa/infobars/alternate_nav_infobar_controller.mm
@@ -7,7 +7,7 @@
 #include "base/logging.h"
 #include "base/strings/sys_string_conversions.h"
 #import "chrome/browser/ui/cocoa/hyperlink_text_view.h"
-#include "chrome/browser/ui/cocoa/infobars/infobar.h"
+#include "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
 #include "chrome/browser/ui/omnibox/alternate_nav_infobar_delegate.h"
 #import "ui/base/cocoa/cocoa_event_utils.h"
 #include "ui/base/window_open_disposition.h"
@@ -25,7 +25,7 @@
   [self removeButtons];
 
   AlternateNavInfoBarDelegate* delegate =
-      static_cast<AlternateNavInfoBarDelegate*>(delegate_);
+      static_cast<AlternateNavInfoBarDelegate*>([self delegate]);
   DCHECK(delegate);
   size_t offset = string16::npos;
   string16 message = delegate->GetMessageTextWithOffset(&offset);
@@ -50,7 +50,7 @@
   WindowOpenDisposition disposition =
       ui::WindowOpenDispositionFromNSEvent([NSApp currentEvent]);
   AlternateNavInfoBarDelegate* delegate =
-      static_cast<AlternateNavInfoBarDelegate*>(delegate_);
+      static_cast<AlternateNavInfoBarDelegate*>([self delegate]);
   if (delegate->LinkClicked(disposition))
     [self removeSelf];
 }
@@ -58,7 +58,9 @@
 @end
 
 InfoBar* AlternateNavInfoBarDelegate::CreateInfoBar(InfoBarService* owner) {
-  AlternateNavInfoBarController* controller =
-      [[AlternateNavInfoBarController alloc] initWithDelegate:this owner:owner];
-  return new InfoBar(controller, this);
+  scoped_ptr<InfoBarCocoa> infobar(new InfoBarCocoa(owner, this));
+  base::scoped_nsobject<AlternateNavInfoBarController> controller(
+      [[AlternateNavInfoBarController alloc] initWithInfoBar:infobar.get()]);
+  infobar->set_controller(controller);
+  return infobar.release();
 }
diff --git a/chrome/browser/ui/cocoa/infobars/before_translate_infobar_controller.mm b/chrome/browser/ui/cocoa/infobars/before_translate_infobar_controller.mm
index 145b41e..fb1af15 100644
--- a/chrome/browser/ui/cocoa/infobars/before_translate_infobar_controller.mm
+++ b/chrome/browser/ui/cocoa/infobars/before_translate_infobar_controller.mm
@@ -37,9 +37,8 @@
   [super dealloc];
 }
 
-- (id)initWithDelegate:(InfoBarDelegate*)delegate
-                  owner:(InfoBarService*)owner {
-  if ((self = [super initWithDelegate:delegate owner:owner])) {
+- (id)initWithInfoBar:(InfoBarCocoa*)infobar {
+  if ((self = [super initWithInfoBar:infobar])) {
     [self initializeExtraControls];
   }
   return self;
diff --git a/chrome/browser/ui/cocoa/infobars/confirm_infobar_controller.mm b/chrome/browser/ui/cocoa/infobars/confirm_infobar_controller.mm
index 8b73673..b237083 100644
--- a/chrome/browser/ui/cocoa/infobars/confirm_infobar_controller.mm
+++ b/chrome/browser/ui/cocoa/infobars/confirm_infobar_controller.mm
@@ -8,7 +8,7 @@
 #include "base/strings/sys_string_conversions.h"
 #include "chrome/browser/infobars/confirm_infobar_delegate.h"
 #import "chrome/browser/ui/cocoa/hyperlink_text_view.h"
-#include "chrome/browser/ui/cocoa/infobars/infobar.h"
+#include "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
 #include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
 #import "ui/base/cocoa/cocoa_event_utils.h"
 #include "ui/base/window_open_disposition.h"
@@ -19,7 +19,7 @@
 - (IBAction)ok:(id)sender {
   if (![self isOwned])
     return;
-  if (delegate_->AsConfirmInfoBarDelegate()->Accept())
+  if ([self delegate]->AsConfirmInfoBarDelegate()->Accept())
     [self removeSelf];
 }
 
@@ -27,7 +27,7 @@
 - (IBAction)cancel:(id)sender {
   if (![self isOwned])
     return;
-  if (delegate_->AsConfirmInfoBarDelegate()->Cancel())
+  if ([self delegate]->AsConfirmInfoBarDelegate()->Cancel())
     [self removeSelf];
 }
 
@@ -35,7 +35,8 @@
 // the return value of GetButtons().  We create each button if
 // required and position them to the left of the close button.
 - (void)addAdditionalControls {
-  ConfirmInfoBarDelegate* delegate = delegate_->AsConfirmInfoBarDelegate();
+  ConfirmInfoBarDelegate* delegate =
+      [self delegate]->AsConfirmInfoBarDelegate();
   DCHECK(delegate);
   int visibleButtons = delegate->GetButtons();
 
@@ -130,14 +131,16 @@
     return;
   WindowOpenDisposition disposition =
       ui::WindowOpenDispositionFromNSEvent([NSApp currentEvent]);
-  if (delegate_->AsConfirmInfoBarDelegate()->LinkClicked(disposition))
+  if ([self delegate]->AsConfirmInfoBarDelegate()->LinkClicked(disposition))
     [self removeSelf];
 }
 
 @end
 
 InfoBar* ConfirmInfoBarDelegate::CreateInfoBar(InfoBarService* owner) {
-  ConfirmInfoBarController* controller =
-      [[ConfirmInfoBarController alloc] initWithDelegate:this owner:owner];
-  return new InfoBar(controller, this);
+  scoped_ptr<InfoBarCocoa> infobar(new InfoBarCocoa(owner, this));
+  base::scoped_nsobject<ConfirmInfoBarController> controller(
+      [[ConfirmInfoBarController alloc] initWithInfoBar:infobar.get()]);
+  infobar->set_controller(controller);
+  return infobar.release();
 }
diff --git a/chrome/browser/ui/cocoa/infobars/confirm_infobar_controller_unittest.mm b/chrome/browser/ui/cocoa/infobars/confirm_infobar_controller_unittest.mm
index 2ef1404..9af8601 100644
--- a/chrome/browser/ui/cocoa/infobars/confirm_infobar_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/infobars/confirm_infobar_controller_unittest.mm
@@ -10,6 +10,7 @@
 #include "chrome/browser/infobars/confirm_infobar_delegate.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
+#import "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
 #include "chrome/browser/ui/cocoa/infobars/mock_confirm_infobar_delegate.h"
 #include "chrome/browser/ui/cocoa/run_loop_testing.h"
@@ -35,15 +36,16 @@
 @end
 
 
-@interface InfoBarContainerTest : NSObject<InfoBarContainer> {
+@interface InfoBarContainerTest : NSObject<InfoBarContainerControllerBase> {
   InfoBarController* controller_;
 }
+
 - (id)initWithController:(InfoBarController*)controller;
-- (void)willRemoveController:(InfoBarController*)controller;
-- (void)removeController:(InfoBarController*)controller;
+
 @end
 
 @implementation InfoBarContainerTest
+
 - (id)initWithController:(InfoBarController*)controller {
   if ((self = [super init])) {
     controller_ = controller;
@@ -51,17 +53,18 @@
   return self;
 }
 
-- (void)willRemoveController:(InfoBarController*)controller {
-}
-
-- (void)removeController:(InfoBarController*)controller {
-  DCHECK(controller_ == controller);
-  controller_ = nil;
-}
-
 - (BrowserWindowController*)browserWindowController {
   return nil;
 }
+
+- (BOOL)shouldSuppressTopInfoBarTip {
+  return NO;
+}
+
+- (CGFloat)infobarArrowX {
+  return 0;
+}
+
 @end
 
 @interface TestConfirmInfoBarController : ConfirmInfoBarController
@@ -70,7 +73,9 @@
 
 @implementation TestConfirmInfoBarController
 - (void)removeSelf {
-  [self close];
+  [self infobarWillClose];
+  if ([self infobar])
+    [self infobar]->CloseSoon();
 }
 @end
 
@@ -79,7 +84,7 @@
 class ConfirmInfoBarControllerTest : public CocoaProfileTest,
                                      public MockConfirmInfoBarDelegate::Owner {
  public:
-  virtual void SetUp() {
+  virtual void SetUp() OVERRIDE {
     CocoaProfileTest::SetUp();
     web_contents_.reset(
         WebContents::Create(WebContents::CreateParams(profile())));
@@ -88,8 +93,12 @@
     InfoBarService* infobar_service =
         InfoBarService::FromWebContents(web_contents_.get());
     delegate_ = new MockConfirmInfoBarDelegate(this);
+    infobar_.reset(new InfoBarCocoa(infobar_service, delegate_));
+
     controller_.reset([[TestConfirmInfoBarController alloc]
-        initWithDelegate:delegate_ owner:infobar_service]);
+        initWithInfoBar:infobar_.get()]);
+    infobar_->set_controller(controller_);
+
     container_.reset(
         [[InfoBarContainerTest alloc] initWithController:controller_]);
     [controller_ setContainerController:container_];
@@ -99,7 +108,8 @@
     closed_delegate_link_clicked_ = false;
   }
 
-  virtual void TearDown() {
+  virtual void TearDown() OVERRIDE {
+    [controller_ removeSelf];
     if (delegate_)
       delete delegate_;
     CocoaProfileTest::TearDown();
@@ -125,6 +135,7 @@
   }
 
   scoped_ptr<WebContents> web_contents_;
+  scoped_ptr<InfoBarCocoa> infobar_;
 };
 
 
diff --git a/chrome/browser/ui/cocoa/infobars/extension_infobar_controller.h b/chrome/browser/ui/cocoa/infobars/extension_infobar_controller.h
index 809aa31..3f35f94 100644
--- a/chrome/browser/ui/cocoa/infobars/extension_infobar_controller.h
+++ b/chrome/browser/ui/cocoa/infobars/extension_infobar_controller.h
@@ -12,11 +12,11 @@
 #import "base/mac/scoped_nsobject.h"
 #include "base/memory/scoped_ptr.h"
 
-@class ExtensionActionContextMenu;
+@class ExtensionActionContextMenuController;
 class InfobarBridge;
 @class MenuButton;
 
-@interface ExtensionInfoBarController : InfoBarController {
+@interface ExtensionInfoBarController : InfoBarController<NSMenuDelegate> {
   // The native extension view retrieved from the extension host. Weak.
   NSView* extensionView_;
 
@@ -27,8 +27,9 @@
   // menu.
   base::scoped_nsobject<MenuButton> dropdownButton_;
 
-  // The context menu that pops up when the left button is clicked.
-  base::scoped_nsobject<ExtensionActionContextMenu> contextMenu_;
+  // Controller for the context menu when the left button is clicked.
+  base::scoped_nsobject<
+      ExtensionActionContextMenuController> contextMenuController_;
 
   // Helper class to bridge C++ and ObjC functionality together for the infobar.
   scoped_ptr<InfobarBridge> bridge_;
diff --git a/chrome/browser/ui/cocoa/infobars/extension_infobar_controller.mm b/chrome/browser/ui/cocoa/infobars/extension_infobar_controller.mm
index 8aa2ae5..2df80ef 100644
--- a/chrome/browser/ui/cocoa/infobars/extension_infobar_controller.mm
+++ b/chrome/browser/ui/cocoa/infobars/extension_infobar_controller.mm
@@ -12,8 +12,8 @@
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/ui/browser_finder.h"
 #import "chrome/browser/ui/cocoa/animatable_view.h"
-#import "chrome/browser/ui/cocoa/extensions/extension_action_context_menu.h"
-#include "chrome/browser/ui/cocoa/infobars/infobar.h"
+#import "chrome/browser/ui/cocoa/extensions/extension_action_context_menu_controller.h"
+#include "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
 #import "chrome/browser/ui/cocoa/menu_button.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_constants.h"
@@ -141,29 +141,28 @@
 
 @implementation ExtensionInfoBarController
 
-- (id)initWithDelegate:(InfoBarDelegate*)delegate
-                 owner:(InfoBarService*)owner
-                window:(NSWindow*)window {
-  if ((self = [super initWithDelegate:delegate owner:owner])) {
+- (id)initWithInfoBar:(InfoBarCocoa*)infobar
+               window:(NSWindow*)window {
+  if ((self = [super initWithInfoBar:infobar])) {
     window_ = window;
     dropdownButton_.reset([[MenuButton alloc] init]);
     [dropdownButton_ setOpenMenuOnClick:YES];
 
     extensions::ExtensionHost* extensionHost =
-        delegate_->AsExtensionInfoBarDelegate()->extension_host();
-    Browser* browser =
-        chrome::FindBrowserWithWebContents(owner->web_contents());
-    contextMenu_.reset([[ExtensionActionContextMenu alloc]
+        [self delegate]->AsExtensionInfoBarDelegate()->extension_host();
+    Browser* browser = chrome::FindBrowserWithWebContents(
+        [self infobar]->OwnerCocoa()->web_contents());
+    contextMenuController_.reset([[ExtensionActionContextMenuController alloc]
         initWithExtension:extensionHost->extension()
                   browser:browser
           extensionAction:NULL]);
+
+    base::scoped_nsobject<NSMenu> contextMenu(
+        [[NSMenu alloc] initWithTitle:@""]);
+    [contextMenu setDelegate:self];
     // See menu_button.h for documentation on why this is needed.
-    NSMenuItem* dummyItem =
-        [[[NSMenuItem alloc] initWithTitle:@""
-                                    action:nil
-                             keyEquivalent:@""] autorelease];
-    [contextMenu_ insertItem:dummyItem atIndex:0];
-    [dropdownButton_ setAttachedMenu:contextMenu_.get()];
+    [contextMenu addItemWithTitle:@"" action:NULL keyEquivalent:@""];
+    [dropdownButton_ setAttachedMenu:contextMenu];
 
     bridge_.reset(new InfobarBridge(self));
   }
@@ -178,8 +177,8 @@
 - (void)addAdditionalControls {
   [self removeButtons];
 
-  extensionView_ = delegate_->AsExtensionInfoBarDelegate()->extension_host()->
-      view()->native_view();
+  extensionView_ = [self delegate]->AsExtensionInfoBarDelegate()
+      ->extension_host()->view()->native_view();
 
   // Add the extension's RenderWidgetHostView to the view hierarchy of the
   // InfoBar and make sure to place it below the Close button.
@@ -210,13 +209,8 @@
   // needed is because the extension view's frame will not have changed in the
   // above case, so the NSViewFrameDidChangeNotification registered below will
   // never fire.
-  if (NSHeight(extensionFrame) > 0.0) {
-    NSSize infoBarSize = [[self view] frame].size;
-    infoBarSize.height = [self clampedExtensionViewHeight] +
-        kBottomBorderHeightPx;
-    [[self view] setFrameSize:infoBarSize];
-    [infoBarView_ setFrameSize:infoBarSize];
-  }
+  if (NSHeight(extensionFrame) > 0.0)
+    [self infobar]->SetBarTargetHeight([self clampedExtensionViewHeight]);
 
   [self adjustExtensionViewSize];
 
@@ -237,25 +231,17 @@
 }
 
 - (void)infobarWillClose {
-  [self disablePopUpMenu:contextMenu_.get()];
+  [self disablePopUpMenu:[dropdownButton_ menu]];
   [super infobarWillClose];
 }
 
 - (void)extensionViewFrameChanged {
   [self adjustExtensionViewSize];
-
-  AnimatableView* view = [self animatableView];
-  NSRect infoBarFrame = [view frame];
-  CGFloat newHeight = [self clampedExtensionViewHeight] + kBottomBorderHeightPx;
-  [infoBarView_ setPostsFrameChangedNotifications:NO];
-  infoBarFrame.size.height = newHeight;
-  [infoBarView_ setFrame:infoBarFrame];
-  [infoBarView_ setPostsFrameChangedNotifications:YES];
-  [view animateToNewHeight:newHeight duration:kAnimationDuration];
+  [self infobar]->SetBarTargetHeight([self clampedExtensionViewHeight]);
 }
 
 - (CGFloat)clampedExtensionViewHeight {
-  CGFloat height = delegate_->AsExtensionInfoBarDelegate()->height();
+  CGFloat height = [self delegate]->AsExtensionInfoBarDelegate()->height();
   return std::max(kToolbarMinHeightPx, std::min(height, kToolbarMaxHeightPx));
 }
 
@@ -272,15 +258,21 @@
   [dropdownButton_ setImage:image];
 }
 
+- (void)menuNeedsUpdate:(NSMenu*)menu {
+  [menu removeAllItems];
+  [contextMenuController_ populateMenu:menu];
+}
+
 @end
 
 InfoBar* ExtensionInfoBarDelegate::CreateInfoBar(InfoBarService* owner) {
+  scoped_ptr<InfoBarCocoa> infobar(new InfoBarCocoa(owner, this));
   NSWindow* window =
       [(NSView*)owner->web_contents()->GetView()->GetContentNativeView()
           window];
-  ExtensionInfoBarController* controller =
-      [[ExtensionInfoBarController alloc] initWithDelegate:this
-                                                     owner:owner
-                                                    window:window];
-  return new InfoBar(controller, this);
+  base::scoped_nsobject<ExtensionInfoBarController> controller(
+      [[ExtensionInfoBarController alloc] initWithInfoBar:infobar.get()
+                                                   window:window]);
+  infobar->set_controller(controller);
+  return infobar.release();
 }
diff --git a/chrome/browser/ui/cocoa/infobars/infobar.h b/chrome/browser/ui/cocoa/infobars/infobar.h
deleted file mode 100644
index 3d70739..0000000
--- a/chrome/browser/ui/cocoa/infobars/infobar.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2011 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_COCOA_INFOBARS_INFOBAR_H_
-#define CHROME_BROWSER_UI_COCOA_INFOBARS_INFOBAR_H_
-
-#include "base/logging.h"  // for DCHECK
-
-#if defined(__OBJC__)
-@class InfoBarController;
-#else
-class InfoBarController;
-#endif
-
-// A C++ wrapper around an Objective-C InfoBarController.  This class
-// exists solely to be the return value for InfoBarDelegate::CreateInfoBar(),
-// as defined in chrome/browser/infobars/confirm_infobar_delegate.h.  This
-// class would be analogous to the various bridge classes we already
-// have, but since there is no pre-defined InfoBar interface, it is
-// easier to simply throw away this object and deal with the
-// controller directly rather than pass messages through a bridge.
-//
-// Callers should delete the returned InfoBar immediately after
-// calling CreateInfoBar(), as the returned InfoBar* object is not
-// pointed to by anyone.  Expected usage:
-//
-// scoped_ptr<InfoBar> infobar(delegate->CreateInfoBar());
-// InfoBarController* controller = infobar->controller();
-// // Do something with the controller, and save a pointer so it can be
-// // deleted later.  |infobar| will be deleted automatically.
-
-class InfoBar {
- public:
-  InfoBar(InfoBarController* controller, InfoBarDelegate* delegate)
-      : controller_(controller), delegate_(delegate) {
-    DCHECK(controller);
-    DCHECK(delegate);
-  }
-
-  InfoBarController* controller() {
-    return controller_;
-  }
-
-  InfoBarDelegate* delegate() {
-    return delegate_;
-  }
-
- private:
-  // Pointer to the infobar controller.  Is never null.
-  InfoBarController* controller_;  // weak
-  InfoBarDelegate* delegate_;
-
-  DISALLOW_COPY_AND_ASSIGN(InfoBar);
-};
-
-#endif  // CHROME_BROWSER_UI_COCOA_INFOBARS_INFOBAR_H_
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_cocoa.h b/chrome/browser/ui/cocoa/infobars/infobar_cocoa.h
new file mode 100644
index 0000000..e8f1a83
--- /dev/null
+++ b/chrome/browser/ui/cocoa/infobars/infobar_cocoa.h
@@ -0,0 +1,38 @@
+// 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 CHROME_BROWSER_UI_COCOA_INFOBARS_INFOBAR_COCOA_H_
+#define CHROME_BROWSER_UI_COCOA_INFOBARS_INFOBAR_COCOA_H_
+
+#include "base/mac/scoped_nsobject.h"
+#include "chrome/browser/infobars/infobar.h"
+
+@class InfoBarController;
+
+// The cocoa specific implementation of InfoBar. The real info bar logic is
+// actually in InfoBarController.
+class InfoBarCocoa : public InfoBar {
+ public:
+  InfoBarCocoa(InfoBarService* owner, InfoBarDelegate* delegate);
+
+  virtual ~InfoBarCocoa();
+
+  InfoBarController* controller() const { return controller_; }
+
+  void set_controller(InfoBarController* controller) {
+    controller_.reset([controller retain]);
+  }
+
+  // These functions allow access to protected InfoBar functions.
+  void RemoveSelfCocoa();
+  InfoBarService* OwnerCocoa();
+
+ private:
+  // The Objective-C class that contains most of the info bar logic.
+  base::scoped_nsobject<InfoBarController> controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(InfoBarCocoa);
+};
+
+#endif  // CHROME_BROWSER_UI_COCOA_INFOBARS_INFOBAR_COCOA_H_
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_cocoa.mm b/chrome/browser/ui/cocoa/infobars/infobar_cocoa.mm
new file mode 100644
index 0000000..c015171
--- /dev/null
+++ b/chrome/browser/ui/cocoa/infobars/infobar_cocoa.mm
@@ -0,0 +1,27 @@
+// 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 "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
+
+const int InfoBar::kSeparatorLineHeight = 1;
+const int InfoBar::kDefaultArrowTargetHeight = 11;
+const int InfoBar::kMaximumArrowTargetHeight = 24;
+const int InfoBar::kDefaultArrowTargetHalfWidth = kDefaultArrowTargetHeight;
+const int InfoBar::kMaximumArrowTargetHalfWidth = 14;
+const int InfoBar::kDefaultBarTargetHeight = 36;
+
+InfoBarCocoa::InfoBarCocoa(InfoBarService* owner, InfoBarDelegate* delegate)
+    : InfoBar(owner, delegate) {
+}
+
+InfoBarCocoa::~InfoBarCocoa() {
+}
+
+void InfoBarCocoa::RemoveSelfCocoa() {
+  RemoveSelf();
+}
+
+InfoBarService* InfoBarCocoa::OwnerCocoa() {
+  return owner();
+}
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_container_cocoa.h b/chrome/browser/ui/cocoa/infobars/infobar_container_cocoa.h
new file mode 100644
index 0000000..fe65762
--- /dev/null
+++ b/chrome/browser/ui/cocoa/infobars/infobar_container_cocoa.h
@@ -0,0 +1,36 @@
+// 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 CHROME_BROWSER_UI_COCOA_INFOBARS_INFOBAR_CONTAINER_COCOA_H_
+#define CHROME_BROWSER_UI_COCOA_INFOBARS_INFOBAR_CONTAINER_COCOA_H_
+
+#include "chrome/browser/infobars/infobar_container.h"
+
+@class InfoBarContainerController;
+
+// The cocoa specific implementation of InfoBarContainer. This mostly serves as
+// a bridge for InfoBarContainerController.
+class InfoBarContainerCocoa : public InfoBarContainer,
+                              public InfoBarContainer::Delegate {
+ public:
+  explicit InfoBarContainerCocoa(InfoBarContainerController* controller);
+  virtual ~InfoBarContainerCocoa();
+
+ private:
+  // InfoBarContainer:
+  virtual void PlatformSpecificAddInfoBar(InfoBar* infobar,
+                                          size_t position) OVERRIDE;
+  virtual void PlatformSpecificRemoveInfoBar(InfoBar* infobar) OVERRIDE;
+
+  // InfoBarContainer::Delegate:
+  virtual SkColor GetInfoBarSeparatorColor() const OVERRIDE;
+  virtual void InfoBarContainerStateChanged(bool is_animating) OVERRIDE;
+  virtual bool DrawInfoBarArrows(int* x) const OVERRIDE;
+
+  InfoBarContainerController* controller_;  // weak, owns us.
+
+  DISALLOW_COPY_AND_ASSIGN(InfoBarContainerCocoa);
+};
+
+#endif  // CHROME_BROWSER_UI_COCOA_INFOBARS_INFOBAR_CONTAINER_COCOA_H_
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_container_cocoa.mm b/chrome/browser/ui/cocoa/infobars/infobar_container_cocoa.mm
new file mode 100644
index 0000000..969b18e
--- /dev/null
+++ b/chrome/browser/ui/cocoa/infobars/infobar_container_cocoa.mm
@@ -0,0 +1,43 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "chrome/browser/ui/cocoa/infobars/infobar_container_cocoa.h"
+
+#import "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
+#import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
+
+InfoBarContainerCocoa::InfoBarContainerCocoa(
+    InfoBarContainerController* controller)
+    : InfoBarContainer(this),
+      controller_(controller) {
+}
+
+InfoBarContainerCocoa::~InfoBarContainerCocoa() {
+  RemoveAllInfoBarsForDestruction();
+}
+
+void InfoBarContainerCocoa::PlatformSpecificAddInfoBar(InfoBar* infobar,
+                                                       size_t position) {
+  InfoBarCocoa* infobar_cocoa = static_cast<InfoBarCocoa*>(infobar);
+  [controller_ addInfoBar:infobar_cocoa position:position];
+}
+
+void InfoBarContainerCocoa::PlatformSpecificRemoveInfoBar(InfoBar* infobar) {
+  InfoBarCocoa* infobar_cocoa = static_cast<InfoBarCocoa*>(infobar);
+  [controller_ removeInfoBar:infobar_cocoa];
+}
+
+SkColor InfoBarContainerCocoa::GetInfoBarSeparatorColor() const {
+  return SK_ColorBLACK;
+}
+
+void InfoBarContainerCocoa::InfoBarContainerStateChanged(bool is_animating) {
+  [controller_ positionInfoBarsAndRedraw:is_animating];
+}
+
+bool InfoBarContainerCocoa::DrawInfoBarArrows(int* x) const {
+  if (x)
+    *x = [controller_ infobarArrowX];
+  return ![controller_ shouldSuppressTopInfoBarTip];
+}
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_container_controller.h b/chrome/browser/ui/cocoa/infobars/infobar_container_controller.h
index f645b79..d8044c5 100644
--- a/chrome/browser/ui/cocoa/infobars/infobar_container_controller.h
+++ b/chrome/browser/ui/cocoa/infobars/infobar_container_controller.h
@@ -10,13 +10,12 @@
 #include "base/mac/scoped_nsobject.h"
 #include "base/memory/scoped_ptr.h"
 #import "chrome/browser/ui/cocoa/view_resizer.h"
-#include "content/public/browser/notification_registrar.h"
 
 @class BrowserWindowController;
 @class InfoBarController;
-class InfoBar;
+class InfoBarCocoa;
+class InfoBarContainerCocoa;
 class InfoBarDelegate;
-class InfoBarNotificationObserver;
 class TabStripModel;
 
 namespace content {
@@ -25,31 +24,19 @@
 
 // Protocol for basic container methods, as needed by an InfoBarController.
 // This protocol exists to make mocking easier in unittests.
-@protocol InfoBarContainer
-- (void)willRemoveController:(InfoBarController*)controller;
-- (void)removeController:(InfoBarController*)controller;
+@protocol InfoBarContainerControllerBase
 - (BrowserWindowController*)browserWindowController;
+- (BOOL)shouldSuppressTopInfoBarTip;
+- (CGFloat)infobarArrowX;
 @end
 
-
-namespace infobars {
-
-// The height of an infobar without the tip.
-const CGFloat kBaseHeight = 36.0;
-
-// The height of the infobar tip.
-const CGFloat kTipHeight = 12.0;
-
-};  // namespace infobars
-
-
 // Controller for the infobar container view, which is the superview
 // of all the infobar views.  This class owns zero or more
 // InfoBarControllers, which manage the infobar views.  This class
 // also receives tab strip model notifications and handles
 // adding/removing infobars when needed.
-@interface InfoBarContainerController : NSViewController <ViewResizer,
-                                                          InfoBarContainer> {
+@interface InfoBarContainerController
+    : NSViewController<InfoBarContainerControllerBase> {
  @private
   // Needed to send resize messages when infobars are added or removed.
   id<ViewResizer> resizeDelegate_;  // weak
@@ -60,83 +47,47 @@
   // Holds the InfoBarControllers currently owned by this container.
   base::scoped_nsobject<NSMutableArray> infobarControllers_;
 
-  // Holds InfoBarControllers when they are in the process of animating out.
-  base::scoped_nsobject<NSMutableSet> closingInfoBars_;
-
-  // Lets us registers for INFOBAR_ADDED/INFOBAR_REMOVED
-  // notifications.  The actual notifications are sent to the
-  // InfoBarNotificationObserver object, which proxies them back to us.
-  content::NotificationRegistrar registrar_;
-  scoped_ptr<InfoBarNotificationObserver> infoBarObserver_;
+  // The C++ instance that bridges to the cross platform code.
+  scoped_ptr<InfoBarContainerCocoa> containerCocoa_;
 
   // If YES then the first info bar doesn't draw a tip.
   BOOL shouldSuppressTopInfoBarTip_;
+
+  // If YES then an infobar animation is in progress.
+  BOOL isAnimating_;
+
+  // The last overlap tip height. This is used to ensure that the info bar
+  // position is updated if the infobar height doesn't change but the overlap
+  // does change.
+  int oldOverlappingTipHeight_;
 }
 
 @property(nonatomic, assign) BOOL shouldSuppressTopInfoBarTip;
 
 - (id)initWithResizeDelegate:(id<ViewResizer>)resizeDelegate;
 
-// Informs the container that the |controller| is going to close. It adds the
-// controller to |closingInfoBars_|. Once the animation is complete, the
-// controller calls |-removeController:| to finalize cleanup.
-- (void)willRemoveController:(InfoBarController*)controller;
-
-// Removes |controller| from the list of controllers in this container and
-// removes its view from the view hierarchy.  This method is safe to call while
-// |controller| is still on the call stack.
-- (void)removeController:(InfoBarController*)controller;
-
-// Modifies this container to display infobars for the given
-// |contents|.  Registers for INFOBAR_ADDED and INFOBAR_REMOVED
-// notifications for |contents|.  If we are currently showing any
-// infobars, removes them first and deregisters for any
-// notifications.  |contents| can be NULL, in which case no infobars
-// are shown and no notifications are registered for.
+// Modifies this container to display infobars for the given |contents|.
 - (void)changeWebContents:(content::WebContents*)contents;
 
 // Stripped down version of TabStripModelObserverBridge:tabDetachedWithContents.
-// Forwarded by BWC. Removes all infobars and deregisters for any notifications
-// if |contents| is the current tab contents.
+// Forwarded by BWC. Removes all infobars if |contents| is the current tab
+// contents.
 - (void)tabDetachedWithContents:(content::WebContents*)contents;
 
-// Returns the number of active infobars. This is
-// |infobarControllers_ - closingInfoBars_|.
-- (NSUInteger)infobarCount;
-
 // Returns the amount of additional height the container view needs to draw the
-// anti-spoofing tip. This will return 0 if |-infobarCount| is 0. This is the
-// total amount of overlap for all infobars.
+// anti-spoofing tip. This is the total amount of overlap for all infobars.
 - (CGFloat)overlappingTipHeight;
 
-@end
+// Adds the given infobar.
+- (void)addInfoBar:(InfoBarCocoa*)infobar
+          position:(NSUInteger)position;
 
-
-@interface InfoBarContainerController (ForTheObserverAndTesting)
-
-// Adds the given infobar.  Takes ownership of |infobar|.
-- (void)addInfoBar:(InfoBar*)infobar animate:(BOOL)animate;
-
-// Closes all the infobar views for a given delegate, either immediately or by
-// starting a close animation.
-- (void)closeInfoBarsForDelegate:(InfoBarDelegate*)delegate
-                         animate:(BOOL)animate;
+// Removes the given infobar.
+- (void)removeInfoBar:(InfoBarCocoa*)infobar;
 
 // Positions the infobar views in the container view and notifies
 // |browser_controller_| that it needs to resize the container view.
-- (void)positionInfoBarsAndRedraw;
-
-@end
-
-
-@interface InfoBarContainerController (JustForTesting)
-
-// Removes all infobar views.  Infobars which were already closing will be
-// completely closed (i.e. the InfoBarDelegate will be deleted and we'll get a
-// callback to removeController).  Other infobars will simply stop animating and
-// disappear.  Callers must call positionInfoBarsAndRedraw() after calling this
-// method.
-- (void)removeAllInfoBars;
+- (void)positionInfoBarsAndRedraw:(BOOL)isAnimating;
 
 @end
 
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_container_controller.mm b/chrome/browser/ui/cocoa/infobars/infobar_container_controller.mm
index f68a15e..f611090 100644
--- a/chrome/browser/ui/cocoa/infobars/infobar_container_controller.mm
+++ b/chrome/browser/ui/cocoa/infobars/infobar_container_controller.mm
@@ -3,81 +3,25 @@
 // found in the LICENSE file.
 
 #include "base/logging.h"
-#include "base/mac/bundle_locations.h"
 #include "base/mac/mac_util.h"
-#include "chrome/browser/chrome_notification_types.h"
+#include "base/message_loop/message_loop.h"
 #include "chrome/browser/infobars/confirm_infobar_delegate.h"
 #include "chrome/browser/infobars/infobar.h"
+#include "chrome/browser/infobars/infobar_container.h"
 #include "chrome/browser/infobars/infobar_service.h"
-#import "chrome/browser/ui/cocoa/animatable_view.h"
 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
+#import "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
+#import "chrome/browser/ui/cocoa/infobars/infobar_container_cocoa.h"
 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
 #import "chrome/browser/ui/cocoa/infobars/infobar_controller.h"
+#import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
 #import "chrome/browser/ui/cocoa/view_id_util.h"
-#include "content/public/browser/notification_details.h"
-#include "content/public/browser/notification_source.h"
-#include "skia/ext/skia_utils_mac.h"
 
-// C++ class that receives INFOBAR_ADDED and INFOBAR_REMOVED
-// notifications and proxies them back to |controller|.
-class InfoBarNotificationObserver : public content::NotificationObserver {
- public:
-  InfoBarNotificationObserver(InfoBarContainerController* controller)
-      : controller_(controller) {
-  }
-
- private:
-  // NotificationObserver implementation
-  virtual void Observe(
-      int type,
-      const content::NotificationSource& source,
-      const content::NotificationDetails& details) OVERRIDE {
-    InfoBarService* infobar_service =
-        content::Source<InfoBarService>(source).ptr();
-    switch (type) {
-      case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED:
-        [controller_ addInfoBar:content::Details<InfoBarAddedDetails>(details)->
-                                    CreateInfoBar(infobar_service)
-                        animate:YES];
-        break;
-
-      case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED: {
-        InfoBarRemovedDetails* removed_details =
-            content::Details<InfoBarRemovedDetails>(details).ptr();
-        [controller_
-            closeInfoBarsForDelegate:removed_details->first
-                             animate:(removed_details->second ? YES : NO)];
-        break;
-      }
-
-      case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REPLACED: {
-        InfoBarReplacedDetails* replaced_details =
-            content::Details<InfoBarReplacedDetails>(details).ptr();
-        [controller_ closeInfoBarsForDelegate:replaced_details->first
-                                      animate:NO];
-        [controller_ addInfoBar:replaced_details->second->
-                                    CreateInfoBar(infobar_service)
-                        animate:NO];
-        break;
-      }
-
-      default:
-        NOTREACHED();  // we don't ask for anything else!
-        break;
-    }
-
-    [controller_ positionInfoBarsAndRedraw];
-  }
-
-  InfoBarContainerController* controller_;  // weak, owns us.
-};
-
-
-@interface InfoBarContainerController (PrivateMethods)
-// Returns the desired height of the container view, computed by
-// adding together the heights of all its subviews.
-- (CGFloat)desiredHeight;
-
+@interface InfoBarContainerController ()
+// Removes |controller| from the list of controllers in this container and
+// removes its view from the view hierarchy.  This method is safe to call while
+// |controller| is still on the call stack.
+- (void)removeController:(InfoBarController*)controller;
 @end
 
 
@@ -87,34 +31,124 @@
 
 - (id)initWithResizeDelegate:(id<ViewResizer>)resizeDelegate {
   DCHECK(resizeDelegate);
-  if ((self = [super initWithNibName:@"InfoBarContainer"
-                              bundle:base::mac::FrameworkBundle()])) {
-    resizeDelegate_ = resizeDelegate;
-    infoBarObserver_.reset(new InfoBarNotificationObserver(self));
+  if ((self = [super initWithNibName:nil bundle:nil])) {
+    base::scoped_nsobject<NSView> view(
+        [[NSView alloc] initWithFrame:NSZeroRect]);
+    [view setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin];
+    view_id_util::SetID(view, VIEW_ID_INFO_BAR_CONTAINER);
+    [self setView:view];
 
-    // NSMutableArray needs an initial capacity, and we rarely ever see
-    // more than two infobars at a time, so that seems like a good choice.
-    infobarControllers_.reset([[NSMutableArray alloc] initWithCapacity:2]);
-    closingInfoBars_.reset([[NSMutableSet alloc] initWithCapacity:2]);
+    resizeDelegate_ = resizeDelegate;
+    containerCocoa_.reset(new InfoBarContainerCocoa(self));
+    infobarControllers_.reset([[NSMutableArray alloc] init]);
   }
   return self;
 }
 
 - (void)dealloc {
+  // Delete the container so that any remaining infobars are removed.
+  containerCocoa_.reset();
   DCHECK_EQ([infobarControllers_ count], 0U);
-  DCHECK_EQ([closingInfoBars_ count], 0U);
   view_id_util::UnsetID([self view]);
   [super dealloc];
 }
 
-- (void)awakeFromNib {
-  // The info bar container view is an ordinary NSView object, so we set its
-  // ViewID here.
-  view_id_util::SetID([self view], VIEW_ID_INFO_BAR_CONTAINER);
+- (BrowserWindowController*)browserWindowController {
+  id controller = [[[self view] window] windowController];
+  if (![controller isKindOfClass:[BrowserWindowController class]])
+    return nil;
+  return controller;
 }
 
-- (void)willRemoveController:(InfoBarController*)controller {
-  [closingInfoBars_ addObject:controller];
+- (CGFloat)infobarArrowX {
+  LocationBarViewMac* locationBar =
+      [[self browserWindowController] locationBarBridge];
+  return locationBar->GetPageInfoBubblePoint().x;
+}
+
+- (void)changeWebContents:(content::WebContents*)contents {
+  if (contents) {
+    containerCocoa_->ChangeInfoBarService(
+        InfoBarService::FromWebContents(contents));
+  } else {
+    containerCocoa_->ChangeInfoBarService(NULL);
+  }
+}
+
+- (void)tabDetachedWithContents:(content::WebContents*)contents {
+  if (currentWebContents_ == contents)
+    [self changeWebContents:NULL];
+}
+
+- (CGFloat)overlappingTipHeight {
+  return containerCocoa_->GetVerticalOverlap(NULL);
+}
+
+- (void)addInfoBar:(InfoBarCocoa*)infobar
+          position:(NSUInteger)position {
+  InfoBarController* controller = infobar->controller();
+  [controller setContainerController:self];
+  [infobarControllers_ insertObject:controller atIndex:position];
+
+  NSView* relativeView = nil;
+  if (position > 0)
+    relativeView = [[infobarControllers_ objectAtIndex:position - 1] view];
+  [[self view] addSubview:[controller view]
+               positioned:NSWindowAbove
+               relativeTo:relativeView];
+}
+
+- (void)removeInfoBar:(InfoBarCocoa*)infobar {
+  [infobar->controller() infobarWillClose];
+  [self removeController:infobar->controller()];
+  base::MessageLoop::current()->DeleteSoon(FROM_HERE, infobar);
+}
+
+- (void)positionInfoBarsAndRedraw:(BOOL)isAnimating {
+  if (isAnimating_ != isAnimating) {
+    isAnimating_ = isAnimating;
+    if ([resizeDelegate_ respondsToSelector:@selector(setAnimationInProgress:)])
+      [resizeDelegate_ setAnimationInProgress:isAnimating_];
+  }
+
+  NSRect containerBounds = [[self view] bounds];
+  int minY = 0;
+
+  // Stack the infobars at the bottom of the view, starting with the
+  // last infobar and working our way to the front of the array.  This
+  // way we ensure that the first infobar added shows up on top, with
+  // the others below.
+  for (InfoBarController* controller in
+           [infobarControllers_ reverseObjectEnumerator]) {
+    NSRect frame;
+    frame.origin.x = NSMinX(containerBounds);
+    frame.origin.y = minY;
+    frame.size.width = NSWidth(containerBounds);
+    frame.size.height = [controller infobar]->total_height();
+    [[controller view] setFrame:frame];
+
+    minY += NSHeight(frame) - [controller infobar]->arrow_height();
+    [controller layoutArrow];
+  }
+
+  int totalHeight = 0;
+  int overlap = containerCocoa_->GetVerticalOverlap(&totalHeight);
+
+  if (NSHeight([[self view] frame]) != totalHeight) {
+    [resizeDelegate_ resizeView:[self view] newHeight:totalHeight];
+  } else if (oldOverlappingTipHeight_ != overlap) {
+    // If the infobar overlap changed but the height didn't change then
+    // explicitly ask for a layout.
+    [[self browserWindowController] layoutInfoBars];
+  }
+  oldOverlappingTipHeight_ = overlap;
+}
+
+- (void)setShouldSuppressTopInfoBarTip:(BOOL)flag {
+  if (shouldSuppressTopInfoBarTip_ == flag)
+    return;
+  shouldSuppressTopInfoBarTip_ = flag;
+  [self positionInfoBarsAndRedraw:isAnimating_];
 }
 
 - (void)removeController:(InfoBarController*)controller {
@@ -127,164 +161,6 @@
   [[controller retain] autorelease];
   [[controller view] removeFromSuperview];
   [infobarControllers_ removeObject:controller];
-  [closingInfoBars_ removeObject:controller];
-  [self positionInfoBarsAndRedraw];
-}
-
-- (BrowserWindowController*)browserWindowController {
-  id controller = [[[self view] window] windowController];
-  if (![controller isKindOfClass:[BrowserWindowController class]])
-    return nil;
-  return controller;
-}
-
-- (void)changeWebContents:(content::WebContents*)contents {
-  registrar_.RemoveAll();
-  [self removeAllInfoBars];
-
-  currentWebContents_ = contents;
-  if (currentWebContents_) {
-    InfoBarService* infobarService =
-        InfoBarService::FromWebContents(currentWebContents_);
-    for (size_t i = 0; i < infobarService->infobar_count(); ++i) {
-      InfoBar* infobar =
-          infobarService->infobar_at(i)->CreateInfoBar(infobarService);
-      [self addInfoBar:infobar animate:NO];
-    }
-
-    content::Source<InfoBarService> source(infobarService);
-    registrar_.Add(infoBarObserver_.get(),
-                   chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED, source);
-    registrar_.Add(infoBarObserver_.get(),
-                   chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, source);
-    registrar_.Add(infoBarObserver_.get(),
-                   chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REPLACED, source);
-  }
-
-  [self positionInfoBarsAndRedraw];
-}
-
-- (void)tabDetachedWithContents:(content::WebContents*)contents {
-  if (currentWebContents_ == contents)
-    [self changeWebContents:NULL];
-}
-
-- (NSUInteger)infobarCount {
-  return [infobarControllers_ count] - [closingInfoBars_ count];
-}
-
-- (CGFloat)overlappingTipHeight {
-  return [self infobarCount] ? infobars::kTipHeight : 0;
-}
-
-- (void)resizeView:(NSView*)view newHeight:(CGFloat)height {
-  NSRect frame = [view frame];
-  frame.size.height = height;
-  [view setFrame:frame];
-  [self positionInfoBarsAndRedraw];
-}
-
-- (void)setAnimationInProgress:(BOOL)inProgress {
-  if ([resizeDelegate_ respondsToSelector:@selector(setAnimationInProgress:)])
-    [resizeDelegate_ setAnimationInProgress:inProgress];
-}
-
-- (void)setShouldSuppressTopInfoBarTip:(BOOL)flag {
-  if (shouldSuppressTopInfoBarTip_ == flag)
-    return;
-  shouldSuppressTopInfoBarTip_ = flag;
-  [self positionInfoBarsAndRedraw];
-}
-
-@end
-
-@implementation InfoBarContainerController (PrivateMethods)
-
-- (CGFloat)desiredHeight {
-  CGFloat height = 0;
-
-  // Take out the height of the tip from the total size of the infobar so that
-  // the tip overlaps the preceding infobar when there is more than one infobar.
-  for (InfoBarController* controller in infobarControllers_.get())
-    height += NSHeight([[controller view] frame]) - infobars::kTipHeight;
-
-  // If there are any infobars, add a little extra room for the tip of the first
-  // infobar.
-  if (height)
-    height += infobars::kTipHeight;
-
-  return height;
-}
-
-- (void)addInfoBar:(InfoBar*)infobar animate:(BOOL)animate {
-  InfoBarController* controller = infobar->controller();
-  [controller setContainerController:self];
-  [[controller animatableView] setResizeDelegate:self];
-  [[self view] addSubview:[controller view]];
-  [infobarControllers_ addObject:[controller autorelease]];
-
-  if (animate)
-    [controller animateOpen];
-  else
-    [controller open];
-
-  delete infobar;
-}
-
-- (void)closeInfoBarsForDelegate:(InfoBarDelegate*)delegate
-                         animate:(BOOL)animate {
-  for (InfoBarController* controller in
-       [NSArray arrayWithArray:infobarControllers_.get()]) {
-    if ([controller delegate] == delegate) {
-      [controller infobarWillClose];
-      if (animate)
-        [controller animateClosed];
-      else
-        [controller close];
-    }
-  }
-}
-
-- (void)removeAllInfoBars {
-  // stopAnimation can remove the infobar from infobarControllers_ if it was in
-  // the midst of closing, so copy the array so mutations won't cause problems.
-  for (InfoBarController* controller in
-       [NSArray arrayWithArray:infobarControllers_.get()]) {
-    [[controller animatableView] stopAnimation];
-    // This code can be executed while InfoBarController is still on the stack,
-    // so we retain and autorelease the controller to prevent it from being
-    // dealloc'ed too early.
-    [[controller retain] autorelease];
-    [[controller view] removeFromSuperview];
-  }
-  [infobarControllers_ removeAllObjects];
-  [closingInfoBars_ removeAllObjects];
-}
-
-- (void)positionInfoBarsAndRedraw {
-  NSRect containerBounds = [[self view] bounds];
-  int minY = 0;
-
-  // Stack the infobars at the bottom of the view, starting with the
-  // last infobar and working our way to the front of the array.  This
-  // way we ensure that the first infobar added shows up on top, with
-  // the others below.
-  for (InfoBarController* controller in
-           [infobarControllers_ reverseObjectEnumerator]) {
-    NSView* view = [controller view];
-    NSRect frame = [view frame];
-    frame.origin.x = NSMinX(containerBounds);
-    frame.origin.y = minY;
-    frame.size.width = NSWidth(containerBounds);
-    [view setFrame:frame];
-
-    minY += NSHeight(frame) - infobars::kTipHeight;
-
-    BOOL isTop = [controller isEqual:[infobarControllers_ objectAtIndex:0]];
-    [controller setHasTip:!shouldSuppressTopInfoBarTip_ || !isTop];
-  }
-
-  [resizeDelegate_ resizeView:[self view] newHeight:[self desiredHeight]];
 }
 
 @end
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_container_controller_unittest.mm b/chrome/browser/ui/cocoa/infobars/infobar_container_controller_unittest.mm
index 617cf3f..c40e891 100644
--- a/chrome/browser/ui/cocoa/infobars/infobar_container_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/infobars/infobar_container_controller_unittest.mm
@@ -7,34 +7,42 @@
 #import <Cocoa/Cocoa.h>
 
 #include "base/mac/scoped_nsobject.h"
-#import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
+#include "chrome/browser/infobars/infobar_service.h"
+#include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
+#include "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
 #include "chrome/browser/ui/cocoa/infobars/mock_confirm_infobar_delegate.h"
 #import "chrome/browser/ui/cocoa/view_resizer_pong.h"
+#import "content/public/browser/web_contents.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
 namespace {
 
-class InfoBarContainerControllerTest : public CocoaTest {
-  virtual void SetUp() {
-    CocoaTest::SetUp();
+class InfoBarContainerControllerTest : public CocoaProfileTest {
+  virtual void SetUp() OVERRIDE {
+    CocoaProfileTest::SetUp();
+    web_contents_.reset(content::WebContents::Create(
+        content::WebContents::CreateParams(profile())));
+    InfoBarService::CreateForWebContents(web_contents_.get());
+
     resizeDelegate_.reset([[ViewResizerPong alloc] init]);
     ViewResizerPong *viewResizer = resizeDelegate_.get();
-    controller_ =
-        [[InfoBarContainerController alloc] initWithResizeDelegate:viewResizer];
+    controller_.reset([[InfoBarContainerController alloc]
+        initWithResizeDelegate:viewResizer]);
     NSView* view = [controller_ view];
     [[test_window() contentView] addSubview:view];
   }
 
-  virtual void TearDown() {
+  virtual void TearDown() OVERRIDE {
     [[controller_ view] removeFromSuperviewWithoutNeedingDisplay];
-    [controller_ release];
-    CocoaTest::TearDown();
+    controller_.reset();
+    CocoaProfileTest::TearDown();
   }
 
  public:
   base::scoped_nsobject<ViewResizerPong> resizeDelegate_;
-  InfoBarContainerController* controller_;
+  base::scoped_nsobject<InfoBarContainerController> controller_;
+  scoped_ptr<content::WebContents> web_contents_;
 };
 
 TEST_VIEW(InfoBarContainerControllerTest, [controller_ view])
@@ -42,46 +50,25 @@
 TEST_F(InfoBarContainerControllerTest, BWCPong) {
   // Call positionInfoBarsAndResize and check that |resizeDelegate_| got a
   // resize message.
-  [resizeDelegate_ setHeight:-1];
-  [controller_ positionInfoBarsAndRedraw];
+  [resizeDelegate_ resizeView:[controller_ view] newHeight:-1];
+  [controller_ positionInfoBarsAndRedraw:NO];
   EXPECT_NE(-1, [resizeDelegate_ height]);
 }
 
 TEST_F(InfoBarContainerControllerTest, AddAndRemoveInfoBars) {
   NSView* view = [controller_ view];
 
-  // Add three infobars and then remove them.
-  // After each step check to make sure we have the correct number of
-  // infobar subviews.
-
   // This delegate deletes itself when they're told their infobars have closed.
   InfoBarDelegate* confirmDelegate = new MockConfirmInfoBarDelegate(NULL);
 
-  [controller_ addInfoBar:confirmDelegate->CreateInfoBar(NULL) animate:NO];
+  InfoBarService* infobar_service =
+      InfoBarService::FromWebContents(web_contents_.get());
+  scoped_ptr<InfoBarCocoa> infobar(static_cast<InfoBarCocoa*>(
+      confirmDelegate->CreateInfoBar(infobar_service)));
+  [controller_ addInfoBar:infobar.get() position:0];
   EXPECT_EQ(1U, [[view subviews] count]);
 
-  // Just to mix things up, remove them in a different order.
-  [controller_ closeInfoBarsForDelegate:confirmDelegate animate:NO];
-  EXPECT_EQ(0U, [[view subviews] count]);
-}
-
-TEST_F(InfoBarContainerControllerTest, RemoveAllInfoBars) {
-  NSView* view = [controller_ view];
-
-  // Add three infobars and then remove them all.
-
-  // removeAllInfobars does not close these, so we stack-allocate them so
-  // they'll get cleaned up.
-  MockConfirmInfoBarDelegate confirmDelegate(NULL);
-  MockConfirmInfoBarDelegate confirmDelegate2(NULL);
-  InfoBarDelegate* confirmDelegatePtr = &confirmDelegate;
-  InfoBarDelegate* confirmDelegate2Ptr = &confirmDelegate2;
-
-  [controller_ addInfoBar:confirmDelegatePtr->CreateInfoBar(NULL) animate:NO];
-  [controller_ addInfoBar:confirmDelegate2Ptr->CreateInfoBar(NULL) animate:NO];
-  EXPECT_EQ(2U, [[view subviews] count]);
-
-  [controller_ removeAllInfoBars];
+  [controller_ removeInfoBar:infobar.release()];
   EXPECT_EQ(0U, [[view subviews] count]);
 }
 
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_controller.h b/chrome/browser/ui/cocoa/infobars/infobar_controller.h
index f96e7b1..5948169 100644
--- a/chrome/browser/ui/cocoa/infobars/infobar_controller.h
+++ b/chrome/browser/ui/cocoa/infobars/infobar_controller.h
@@ -6,8 +6,8 @@
 
 #include "base/mac/scoped_nsobject.h"
 
-@class AnimatableView;
-@protocol InfoBarContainer;
+@protocol InfoBarContainerControllerBase;
+class InfoBarCocoa;
 class InfoBarDelegate;
 class InfoBarService;
 @class InfoBarGradientView;
@@ -18,9 +18,8 @@
 // override addAdditionalControls to customize the UI.
 @interface InfoBarController : NSViewController<NSTextViewDelegate> {
  @private
-  id<InfoBarContainer> containerController_;  // weak, owns us
-  InfoBarService* owner_;  // weak
-  BOOL infoBarClosing_;
+  id<InfoBarContainerControllerBase> containerController_;  // weak, owns us
+  InfoBarCocoa* infobar_;  // weak, owns us
 
  @protected
   IBOutlet InfoBarGradientView* infoBarView_;
@@ -30,21 +29,20 @@
   IBOutlet NSButton* cancelButton_;
   IBOutlet NSButton* closeButton_;
 
-  // In rare instances, it can be possible for |delegate_| to delete itself
-  // while this controller is still alive.  Always check |delegate_| against
-  // NULL before using it.
-  InfoBarDelegate* delegate_;  // weak, can be NULL
-
   // Text fields don't work as well with embedded links as text views, but
   // text views cannot conveniently be created in IB. The xib file contains
   // a text field |labelPlaceholder_| that's replaced by this text view |label_|
   // in -awakeFromNib.
   base::scoped_nsobject<NSTextView> label_;
-};
+}
+
+@property(nonatomic, assign)
+    id<InfoBarContainerControllerBase> containerController;
+@property(nonatomic, readonly) InfoBarDelegate* delegate;
+@property(nonatomic, readonly) InfoBarCocoa* infobar;
 
 // Initializes a new InfoBarController.
-- (id)initWithDelegate:(InfoBarDelegate*)delegate
-                 owner:(InfoBarService*)owner;
+- (id)initWithInfoBar:(InfoBarCocoa*)infobar;
 
 // Returns YES if the infobar is owned.  If this is NO, it is not safe to call
 // any delegate functions, since they might attempt to access the owner.  Code
@@ -67,17 +65,6 @@
 // call will trigger a notification that starts the infobar animating closed.
 - (void)removeSelf;
 
-// Returns a pointer to this controller's view, cast as an AnimatableView.
-- (AnimatableView*)animatableView;
-
-// Open or animate open the infobar.
-- (void)open;
-- (void)animateOpen;
-
-// Close or animate close the infobar.
-- (void)close;
-- (void)animateClosed;
-
 // Subclasses can override this method to add additional controls to
 // the infobar view.  This method is called by awakeFromNib.  The
 // default implementation does nothing.
@@ -91,10 +78,8 @@
 // space.
 - (void)removeButtons;
 
-- (void)setHasTip:(BOOL)hasTip;
-
-@property(nonatomic, assign) id<InfoBarContainer> containerController;
-@property(nonatomic, readonly) InfoBarDelegate* delegate;
+// Updates the view's arrow position.
+- (void)layoutArrow;
 
 @end
 
@@ -108,6 +93,3 @@
 // InfoBarController subclasses, one for each InfoBarDelegate
 // subclass.  Each of these subclasses overrides addAdditionalControls to
 // configure its view as necessary.
-
-
-
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_controller.mm b/chrome/browser/ui/cocoa/infobars/infobar_controller.mm
index 29e25d5..3a7d0d5 100644
--- a/chrome/browser/ui/cocoa/infobars/infobar_controller.mm
+++ b/chrome/browser/ui/cocoa/infobars/infobar_controller.mm
@@ -12,7 +12,8 @@
 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
 #import "chrome/browser/ui/cocoa/hyperlink_text_view.h"
 #import "chrome/browser/ui/cocoa/image_button_cell.h"
-#include "chrome/browser/ui/cocoa/infobars/infobar.h"
+#include "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
+#import "chrome/browser/ui/cocoa/infobars/infobar_container_cocoa.h"
 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
 #import "chrome/browser/ui/cocoa/infobars/infobar_gradient_view.h"
 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
@@ -20,38 +21,21 @@
 #include "grit/ui_resources.h"
 #include "ui/gfx/image/image.h"
 
-namespace {
-// Durations set to match the default SlideAnimation duration.
-const float kAnimateOpenDuration = 0.12;
-const float kAnimateCloseDuration = 0.12;
-}
-
-@interface InfoBarController (PrivateMethods)
+@interface InfoBarController ()
 // Sets |label_| based on |labelPlaceholder_|, sets |labelPlaceholder_| to nil.
 - (void)initializeLabel;
-
-// Performs final cleanup after an animation is finished or stopped, including
-// notifying the InfoBarDelegate that the infobar was closed and removing the
-// infobar from its container, if necessary.
-- (void)cleanUpAfterAnimation:(BOOL)finished;
-
-// Returns the point, in window coordinates, at which the apex of the infobar
-// tip should be drawn.
-- (NSPoint)pointForTipApex;
 @end
 
 @implementation InfoBarController
 
 @synthesize containerController = containerController_;
-@synthesize delegate = delegate_;
+@synthesize infobar = infobar_;
 
-- (id)initWithDelegate:(InfoBarDelegate*)delegate
-                 owner:(InfoBarService*)owner {
-  DCHECK(delegate);
+- (id)initWithInfoBar:(InfoBarCocoa*)infobar {
   if ((self = [super initWithNibName:@"InfoBar"
                               bundle:base::mac::FrameworkBundle()])) {
-    delegate_ = delegate;
-    owner_ = owner;
+    DCHECK(infobar);
+    infobar_ = infobar;
   }
   return self;
 }
@@ -59,8 +43,6 @@
 // All infobars have an icon, so we set up the icon in the base class
 // awakeFromNib.
 - (void)awakeFromNib {
-  DCHECK(delegate_);
-
   [[closeButton_ cell] setImageID:IDR_CLOSE_1
                    forButtonState:image_button_cell::kDefaultState];
   [[closeButton_ cell] setImageID:IDR_CLOSE_1_H
@@ -70,8 +52,8 @@
   [[closeButton_ cell] setImageID:IDR_CLOSE_1
                    forButtonState:image_button_cell::kDisabledState];
 
-  if (!delegate_->GetIcon().IsEmpty()) {
-    [image_ setImage:delegate_->GetIcon().ToNSImage()];
+  if (![self delegate]->GetIcon().IsEmpty()) {
+    [image_ setImage:[self delegate]->GetIcon().ToNSImage()];
   } else {
     // No icon, remove it from the view and grow the textfield to include the
     // space.
@@ -87,8 +69,7 @@
 
   [self addAdditionalControls];
 
-  infoBarView_.tipApex = [self pointForTipApex];
-  [infoBarView_ setInfobarType:delegate_->GetInfoBarType()];
+  [infoBarView_ setInfobarType:[self delegate]->GetInfoBarType()];
 }
 
 - (void)dealloc {
@@ -108,7 +89,7 @@
 }
 
 - (BOOL)isOwned {
-  return !!owner_;
+  return infobar_->OwnerCocoa() != NULL;
 }
 
 // Called when someone clicks on the ok button.
@@ -127,63 +108,12 @@
 - (void)dismiss:(id)sender {
   if (![self isOwned])
     return;
-  delegate_->InfoBarDismissed();
+  [self delegate]->InfoBarDismissed();
   [self removeSelf];
 }
 
 - (void)removeSelf {
-  // |owner_| should never be NULL here.  If it is, then someone violated what
-  // they were supposed to do -- e.g. a ConfirmInfoBarDelegate subclass returned
-  // true from Accept() or Cancel() even though the infobar was already closing.
-  // In the worst case, if we also switched tabs during that process, then
-  // |this| has already been destroyed.  But if that's the case, then we're
-  // going to deref a garbage |this| pointer here whether we check |owner_| or
-  // not, and in other cases (where we're still closing and |this| is valid),
-  // checking |owner_| here will avoid a NULL deref.
-  if (owner_)
-    owner_->RemoveInfoBar(delegate_);
-}
-
-- (AnimatableView*)animatableView {
-  return static_cast<AnimatableView*>([self view]);
-}
-
-- (void)open {
-  // Simply reset the frame size to its opened size, forcing a relayout.
-  CGFloat finalHeight = [[self view] frame].size.height;
-  [[self animatableView] setHeight:finalHeight];
-}
-
-- (void)animateOpen {
-  // Force the frame size to be 0 and then start an animation.
-  NSRect frame = [[self view] frame];
-  CGFloat finalHeight = frame.size.height;
-  frame.size.height = 0;
-  [[self view] setFrame:frame];
-  [[self animatableView] animateToNewHeight:finalHeight
-                                   duration:kAnimateOpenDuration];
-}
-
-- (void)close {
-  // Stop any running animations.
-  [[self animatableView] stopAnimation];
-  infoBarClosing_ = YES;
-  [self cleanUpAfterAnimation:YES];
-}
-
-- (void)animateClosed {
-  // Notify the container of our intentions.
-  [containerController_ willRemoveController:self];
-
-  // Start animating closed.  We will receive a notification when the animation
-  // is done, at which point we can remove our view from the hierarchy and
-  // notify the delegate that the infobar was closed.
-  [[self animatableView] animateToNewHeight:0 duration:kAnimateCloseDuration];
-
-  // The above call may trigger an animationDidStop: notification for any
-  // currently-running animations, so do not set |infoBarClosing_| until after
-  // starting the animation.
-  infoBarClosing_ = YES;
+  infobar_->RemoveSelfCocoa();
 }
 
 - (void)addAdditionalControls {
@@ -191,7 +121,6 @@
 }
 
 - (void)infobarWillClose {
-  owner_ = NULL;
 }
 
 - (void)removeButtons {
@@ -205,8 +134,15 @@
   [label_.get() setFrame:labelFrame];
 }
 
-- (void)setHasTip:(BOOL)hasTip {
-  [infoBarView_ setHasTip:hasTip];
+- (void)layoutArrow {
+  [infoBarView_ setArrowHeight:infobar_->arrow_height()];
+  [infoBarView_ setArrowHalfWidth:infobar_->arrow_half_width()];
+  [infoBarView_ setHasTip:![containerController_ shouldSuppressTopInfoBarTip]];
+
+  // Convert from window to view coordinates.
+  NSPoint point = NSMakePoint([containerController_ infobarArrowX], 0);
+  point = [infoBarView_ convertPoint:point fromView:nil];
+  [infoBarView_ setArrowX:point.x];
 }
 
 - (void)disablePopUpMenu:(NSMenu*)menu {
@@ -223,9 +159,9 @@
   }
 }
 
-@end
-
-@implementation InfoBarController (PrivateMethods)
+- (InfoBarDelegate*)delegate {
+  return infobar_->delegate();
+}
 
 - (void)initializeLabel {
   // Replace the label placeholder NSTextField with the real label NSTextView.
@@ -241,42 +177,4 @@
   [label_.get() setDelegate:self];
 }
 
-- (void)cleanUpAfterAnimation:(BOOL)finished {
-  // Don't need to do any cleanup if the bar was animating open.
-  if (!infoBarClosing_)
-    return;
-
-  if (delegate_) {
-    delete delegate_;
-    delegate_ = NULL;
-  }
-
-  // If the animation ran to completion, then we need to remove ourselves from
-  // the container.  If the animation was interrupted, then the container will
-  // take care of removing us.
-  // TODO(rohitrao): UGH!  This works for now, but should be cleaner.
-  if (finished)
-    [containerController_ removeController:self];
-}
-
-- (void)animationDidStop:(NSAnimation*)animation {
-  [self cleanUpAfterAnimation:NO];
-}
-
-- (void)animationDidEnd:(NSAnimation*)animation {
-  [self cleanUpAfterAnimation:YES];
-}
-
-- (NSPoint)pointForTipApex {
-  BrowserWindowController* windowController =
-      [containerController_ browserWindowController];
-  if (!windowController) {
-    // This should only happen in unit tests.
-    return NSZeroPoint;
-  }
-
-  LocationBarViewMac* locationBar = [windowController locationBarBridge];
-  return locationBar->GetPageInfoBubblePoint();
-}
-
 @end
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_gradient_view.h b/chrome/browser/ui/cocoa/infobars/infobar_gradient_view.h
index 48fefc8..f78ec6a 100644
--- a/chrome/browser/ui/cocoa/infobars/infobar_gradient_view.h
+++ b/chrome/browser/ui/cocoa/infobars/infobar_gradient_view.h
@@ -13,13 +13,15 @@
 // A custom view that draws the background gradient for an infobar.
 @interface InfoBarGradientView : VerticalGradientView {
  @private
-  NSPoint tipApex_;
+  CGFloat arrowHeight_;
+  CGFloat arrowHalfWidth_;
+  CGFloat arrowX_;
   BOOL hasTip_;
 }
 
-// The point, in window coordinates, at which the infobar tip is the highest and
-// pointing at the omnibox decoration.
-@property(assign, nonatomic) NSPoint tipApex;
+@property(assign, nonatomic) CGFloat arrowHeight;
+@property(assign, nonatomic) CGFloat arrowHalfWidth;
+@property(assign, nonatomic) CGFloat arrowX;
 @property(assign, nonatomic) BOOL hasTip;
 
 // Sets the infobar type. This will change the view's gradient.
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_gradient_view.mm b/chrome/browser/ui/cocoa/infobars/infobar_gradient_view.mm
index c86f463..12222de 100644
--- a/chrome/browser/ui/cocoa/infobars/infobar_gradient_view.mm
+++ b/chrome/browser/ui/cocoa/infobars/infobar_gradient_view.mm
@@ -14,15 +14,11 @@
 #include "skia/ext/skia_utils_mac.h"
 #include "ui/base/theme_provider.h"
 
-namespace {
-
-const CGFloat kTipWidth = 23;
-
-}  // namespace
-
 @implementation InfoBarGradientView
 
-@synthesize tipApex = tipApex_;
+@synthesize arrowHeight = arrowHeight_;
+@synthesize arrowHalfWidth = arrowHalfWidth_;
+@synthesize arrowX = arrowX_;
 @synthesize hasTip = hasTip_;
 
 - (id)initWithFrame:(NSRect)frame {
@@ -61,11 +57,9 @@
 
 - (void)drawRect:(NSRect)rect {
   NSRect bounds = [self bounds];
-  bounds.size.height = infobars::kBaseHeight;
+  bounds.size.height = InfoBar::kDefaultBarTargetHeight;
 
-  const CGFloat kHalfWidth = kTipWidth / 2.0;
-  NSPoint localApex = [self convertPoint:self.tipApex fromView:nil];
-  CGFloat tipXOffset = localApex.x - kHalfWidth;
+  CGFloat tipXOffset = arrowX_ - arrowHalfWidth_;
 
   // Around the bounds of the infobar, continue drawing the path into which the
   // gradient will be drawn.
@@ -75,10 +69,10 @@
   // Draw the tip.
   if (hasTip_) {
     [infoBarPath lineToPoint:NSMakePoint(tipXOffset, NSMaxY(bounds))];
-    [infoBarPath relativeLineToPoint:NSMakePoint(kHalfWidth,
-                                                 infobars::kTipHeight)];
-    [infoBarPath relativeLineToPoint:NSMakePoint(kHalfWidth,
-                                                 -infobars::kTipHeight)];
+    [infoBarPath relativeLineToPoint:NSMakePoint(arrowHalfWidth_,
+                                                 arrowHeight_)];
+    [infoBarPath relativeLineToPoint:NSMakePoint(arrowHalfWidth_,
+                                                 -arrowHeight_)];
   }
   [infoBarPath lineToPoint:NSMakePoint(NSMaxX(bounds), NSMaxY(bounds))];
 
@@ -107,14 +101,14 @@
   }
 
   // Add an inner stroke.
-  const CGFloat kHighlightTipHeight = infobars::kTipHeight - 1;
+  const CGFloat kHighlightTipHeight = arrowHeight_ - 1;
   NSBezierPath* highlightPath = [NSBezierPath bezierPath];
   [highlightPath moveToPoint:NSMakePoint(NSMinX(bounds), NSMaxY(bounds) - 1)];
   if (hasTip_) {
     [highlightPath relativeLineToPoint:NSMakePoint(tipXOffset + 1, 0)];
-    [highlightPath relativeLineToPoint:NSMakePoint(kHalfWidth - 1,
+    [highlightPath relativeLineToPoint:NSMakePoint(arrowHalfWidth_ - 1,
                                                    kHighlightTipHeight)];
-    [highlightPath relativeLineToPoint:NSMakePoint(kHalfWidth - 1,
+    [highlightPath relativeLineToPoint:NSMakePoint(arrowHalfWidth_ - 1,
                                                    -kHighlightTipHeight)];
   }
   [highlightPath lineToPoint:NSMakePoint(NSMaxX(bounds), NSMaxY(bounds) - 1)];
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_utilities.mm b/chrome/browser/ui/cocoa/infobars/infobar_utilities.mm
index c5e5e63..964e1ed 100644
--- a/chrome/browser/ui/cocoa/infobars/infobar_utilities.mm
+++ b/chrome/browser/ui/cocoa/infobars/infobar_utilities.mm
@@ -5,6 +5,7 @@
 #import "chrome/browser/ui/cocoa/infobars/infobar_utilities.h"
 
 #include "base/mac/scoped_nsobject.h"
+#import "chrome/browser/infobars/infobar.h"
 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
 #import "chrome/browser/ui/cocoa/infobars/infobar_gradient_view.h"
 
@@ -47,7 +48,7 @@
   // rather than in the total height (which includes the bulge).
   CGFloat superHeight = NSHeight(superViewFrame);
   if ([[toMove superview] isKindOfClass:[InfoBarGradientView class]])
-    superHeight = infobars::kBaseHeight;
+    superHeight = InfoBar::kDefaultBarTargetHeight;
   viewFrame.origin.y =
       floor((superHeight - NSHeight(viewFrame)) / 2.0);
   [toMove setFrame:viewFrame];
diff --git a/chrome/browser/ui/cocoa/infobars/translate_infobar_base.mm b/chrome/browser/ui/cocoa/infobars/translate_infobar_base.mm
index 8d8f2b7..5f9f7ba 100644
--- a/chrome/browser/ui/cocoa/infobars/translate_infobar_base.mm
+++ b/chrome/browser/ui/cocoa/infobars/translate_infobar_base.mm
@@ -11,7 +11,7 @@
 #import "chrome/browser/ui/cocoa/hover_close_button.h"
 #include "chrome/browser/ui/cocoa/infobars/after_translate_infobar_controller.h"
 #import "chrome/browser/ui/cocoa/infobars/before_translate_infobar_controller.h"
-#include "chrome/browser/ui/cocoa/infobars/infobar.h"
+#include "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
 #import "chrome/browser/ui/cocoa/infobars/infobar_controller.h"
 #import "chrome/browser/ui/cocoa/infobars/infobar_gradient_view.h"
@@ -29,28 +29,27 @@
 
 // TranslateInfoBarDelegate views specific method:
 InfoBar* TranslateInfoBarDelegate::CreateInfoBar(InfoBarService* owner) {
-  TranslateInfoBarControllerBase* infobar_controller = NULL;
+  scoped_ptr<InfoBarCocoa> infobar(new InfoBarCocoa(owner, this));
+  base::scoped_nsobject<TranslateInfoBarControllerBase> infobar_controller;
   switch (infobar_type_) {
     case BEFORE_TRANSLATE:
-      infobar_controller =
-          [[BeforeTranslateInfobarController alloc] initWithDelegate:this
-                                                               owner:owner];
+      infobar_controller.reset([[BeforeTranslateInfobarController alloc]
+          initWithInfoBar:infobar.get()]);
       break;
     case AFTER_TRANSLATE:
-      infobar_controller =
-          [[AfterTranslateInfobarController alloc] initWithDelegate:this
-                                                              owner:owner];
+      infobar_controller.reset([[AfterTranslateInfobarController alloc]
+          initWithInfoBar:infobar.get()]);
       break;
     case TRANSLATING:
     case TRANSLATION_ERROR:
-      infobar_controller =
-          [[TranslateMessageInfobarController alloc] initWithDelegate:this
-                                                                owner:owner];
+      infobar_controller.reset([[TranslateMessageInfobarController alloc]
+          initWithInfoBar:infobar.get()]);
       break;
     default:
       NOTREACHED();
   }
-  return new InfoBar(infobar_controller, this);
+  infobar->set_controller(infobar_controller);
+  return infobar.release();
 }
 
 @implementation TranslateInfoBarControllerBase (FrameChangeObserver)
@@ -99,7 +98,7 @@
 @implementation TranslateInfoBarControllerBase
 
 - (TranslateInfoBarDelegate*)delegate {
-  return reinterpret_cast<TranslateInfoBarDelegate*>(delegate_);
+  return reinterpret_cast<TranslateInfoBarDelegate*>([super delegate]);
 }
 
 - (void)constructViews {
diff --git a/chrome/browser/ui/cocoa/infobars/translate_infobar_unittest.mm b/chrome/browser/ui/cocoa/infobars/translate_infobar_unittest.mm
index 218e031..3195887 100644
--- a/chrome/browser/ui/cocoa/infobars/translate_infobar_unittest.mm
+++ b/chrome/browser/ui/cocoa/infobars/translate_infobar_unittest.mm
@@ -13,7 +13,7 @@
 #include "chrome/browser/translate/translate_language_list.h"
 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
 #import "chrome/browser/ui/cocoa/infobars/before_translate_infobar_controller.h"
-#import "chrome/browser/ui/cocoa/infobars/infobar.h"
+#import "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
 #import "chrome/browser/ui/cocoa/infobars/translate_infobar_base.h"
 #import "content/public/browser/web_contents.h"
 #include "ipc/ipc_message.h"
@@ -88,10 +88,14 @@
     infobar_delegate_.reset(new MockTranslateInfoBarDelegate(
         infobar_service, type, error, profile->GetPrefs(), config));
     [[infobar_controller_ view] removeFromSuperview];
-    scoped_ptr<InfoBar> infobar(static_cast<InfoBarDelegate*>(
-        infobar_delegate_.get())->CreateInfoBar(infobar_service));
-    infobar_controller_.reset(reinterpret_cast<TranslateInfoBarControllerBase*>(
-        infobar->controller()));
+
+    InfoBarDelegate* base =
+        static_cast<InfoBarDelegate*>(infobar_delegate_.get());
+    infobar_.reset(
+        static_cast<InfoBarCocoa*>(base->CreateInfoBar(infobar_service)));
+    infobar_controller_.reset([static_cast<TranslateInfoBarControllerBase*>(
+        infobar_->controller()) retain]);
+
     // We need to set the window to be wide so that the options button
     // doesn't overlap the other buttons.
     [test_window() setContentSize:NSMakeSize(2000, 500)];
@@ -101,6 +105,7 @@
 
   scoped_ptr<WebContents> web_contents_;
   scoped_ptr<MockTranslateInfoBarDelegate> infobar_delegate_;
+  scoped_ptr<InfoBarCocoa> infobar_;
   base::scoped_nsobject<TranslateInfoBarControllerBase> infobar_controller_;
 };
 
diff --git a/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.mm b/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.mm
index 9e67806..ba78df8 100644
--- a/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.mm
+++ b/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.mm
@@ -258,10 +258,11 @@
                keyEquivalent:@""];
     }
 
+    [menu addItem:[NSMenuItem separatorItem]];
+
     NSString* search_engine_label =
         l10n_util::GetNSStringWithFixup(IDS_EDIT_SEARCH_ENGINES);
     DCHECK([search_engine_label length]);
-    [menu addItem:[NSMenuItem separatorItem]];
     NSMenuItem* item = [menu addItemWithTitle:search_engine_label
                                        action:@selector(commandDispatch:)
                                 keyEquivalent:@""];
diff --git a/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor_unittest.mm b/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor_unittest.mm
index b2382f3..e7b98b5 100644
--- a/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor_unittest.mm
+++ b/chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor_unittest.mm
@@ -250,8 +250,8 @@
   [editor_ pasteAndGo:nil];
 }
 
-// Test that the menu is constructed correctly when CanPasteAndGo().
-TEST_F(AutocompleteTextFieldEditorObserverTest, CanPasteAndGoMenu) {
+// Test that the menu is constructed correctly.
+TEST_F(AutocompleteTextFieldEditorObserverTest, Menu) {
   EXPECT_CALL(field_observer_, GetPasteActionStringId()).
       WillOnce(Return(IDS_PASTE_AND_GO));
 
@@ -266,32 +266,8 @@
   EXPECT_EQ([[items objectAtIndex:i++] action], @selector(paste:));
   EXPECT_EQ([[items objectAtIndex:i++] action], @selector(pasteAndGo:));
   EXPECT_TRUE([[items objectAtIndex:i++] isSeparatorItem]);
-
-  EXPECT_EQ([[items objectAtIndex:i] action], @selector(commandDispatch:));
   EXPECT_EQ([[items objectAtIndex:i] tag], IDC_EDIT_SEARCH_ENGINES);
-  i++;
-}
-
-// Test that the menu is constructed correctly when !CanPasteAndGo().
-TEST_F(AutocompleteTextFieldEditorObserverTest, CannotPasteAndGoMenu) {
-  EXPECT_CALL(field_observer_, GetPasteActionStringId()).
-      WillOnce(Return(IDS_PASTE_AND_GO));
-
-  NSMenu* menu = [editor_ menuForEvent:nil];
-  NSArray* items = [menu itemArray];
-  ASSERT_EQ([items count], 7U);
-  // TODO(shess): Check the titles, too?
-  NSUInteger i = 0;  // Use an index to make future changes easier.
-  EXPECT_EQ([[items objectAtIndex:i++] action], @selector(cut:));
-  EXPECT_EQ([[items objectAtIndex:i++] action], @selector(copy:));
-  EXPECT_EQ([[items objectAtIndex:i++] action], @selector(copyURL:));
-  EXPECT_EQ([[items objectAtIndex:i++] action], @selector(paste:));
-  EXPECT_EQ([[items objectAtIndex:i++] action], @selector(pasteAndGo:));
-  EXPECT_TRUE([[items objectAtIndex:i++] isSeparatorItem]);
-
-  EXPECT_EQ([[items objectAtIndex:i] action], @selector(commandDispatch:));
-  EXPECT_EQ([[items objectAtIndex:i] tag], IDC_EDIT_SEARCH_ENGINES);
-  i++;
+  EXPECT_EQ([[items objectAtIndex:i++] action], @selector(commandDispatch:));
 }
 
 // Test that the menu is constructed correctly when field isn't
@@ -313,28 +289,6 @@
   EXPECT_EQ([[items objectAtIndex:i++] action], @selector(paste:));
 }
 
-// Test that the menu is constructed correctly when ShouldEnableCopyURL()
-// returns true.
-TEST_F(AutocompleteTextFieldEditorObserverTest, ShouldEnableCopyURLMenu) {
-  EXPECT_CALL(field_observer_, GetPasteActionStringId()).
-      WillOnce(Return(IDS_PASTE_AND_GO));
-
-  NSMenu* menu = [editor_ menuForEvent:nil];
-  NSArray* items = [menu itemArray];
-  ASSERT_EQ([items count], 7U);
-  // TODO(shess): Check the titles, too?
-  NSUInteger i = 0;  // Use an index to make future changes easier.
-  EXPECT_EQ([[items objectAtIndex:i++] action], @selector(cut:));
-  EXPECT_EQ([[items objectAtIndex:i++] action], @selector(copy:));
-  EXPECT_EQ([[items objectAtIndex:i++] action], @selector(copyURL:));
-  EXPECT_EQ([[items objectAtIndex:i++] action], @selector(paste:));
-  EXPECT_EQ([[items objectAtIndex:i++] action], @selector(pasteAndGo:));
-  EXPECT_TRUE([[items objectAtIndex:i++] isSeparatorItem]);
-
-  EXPECT_EQ([[items objectAtIndex:i] action], @selector(commandDispatch:));
-  EXPECT_EQ([[items objectAtIndex:i] tag], IDC_EDIT_SEARCH_ENGINES);
-}
-
 // Test that the menu validation works as expected when CanPasteAndGo().
 TEST_F(AutocompleteTextFieldEditorObserverTest, CanPasteAndGoValidate) {
   EXPECT_CALL(field_observer_, GetPasteActionStringId())
diff --git a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h
index cc146d9..32cc984 100644
--- a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h
+++ b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h
@@ -104,10 +104,10 @@
   // redrawn and laid out if necessary.
   void OnDecorationsChanged();
 
-  // Updates the location bar.  Resets the bar's permanent text and security
-  // style, and if |tab| is non-NULL, restores saved state from the tab (for tab
-  // switching).
-  void Update(const content::WebContents* tab);
+  // Updates the location bar.  We also reset the bar's permanent text and
+  // security style, and, if |contents| is non-NULL, also restore saved state
+  // that the tab holds.
+  void Update(const content::WebContents* contents);
 
   // Layout the various decorations which live in the field.
   void Layout();
diff --git a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm
index f1bed4f..441e8f0 100644
--- a/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm
+++ b/chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.mm
@@ -29,7 +29,6 @@
 #include "chrome/browser/ui/browser_instant_controller.h"
 #include "chrome/browser/ui/browser_list.h"
 #import "chrome/browser/ui/cocoa/content_settings/content_setting_bubble_cocoa.h"
-#import "chrome/browser/ui/cocoa/extensions/extension_action_context_menu.h"
 #import "chrome/browser/ui/cocoa/extensions/extension_popup_controller.h"
 #import "chrome/browser/ui/cocoa/first_run_bubble_controller.h"
 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.h"
@@ -238,7 +237,10 @@
   RefreshPageActionDecorations();
   RefreshContentSettingsDecorations();
   UpdateMicSearchDecorationVisibility();
-  omnibox_view_->Update(contents);
+  if (contents)
+    omnibox_view_->OnTabChanged(contents);
+  else
+    omnibox_view_->Update();
   OnChanged();
 }
 
diff --git a/chrome/browser/ui/cocoa/location_bar/page_action_decoration.h b/chrome/browser/ui/cocoa/location_bar/page_action_decoration.h
index 55a4cd4..7c70030 100644
--- a/chrome/browser/ui/cocoa/location_bar/page_action_decoration.h
+++ b/chrome/browser/ui/cocoa/location_bar/page_action_decoration.h
@@ -12,7 +12,7 @@
 #include "content/public/browser/notification_registrar.h"
 #include "url/gurl.h"
 
-@class ExtensionActionContextMenu;
+@class ExtensionActionContextMenuController;
 class Browser;
 class LocationBarViewMac;
 
@@ -106,8 +106,9 @@
   // The string to show for a tooltip.
   base::scoped_nsobject<NSString> tooltip_;
 
-  // The context menu for the Page Action.
-  base::scoped_nsobject<ExtensionActionContextMenu> menu_;
+  // The context menu controller for the Page Action.
+  base::scoped_nsobject<
+      ExtensionActionContextMenuController> contextMenuController_;
 
   // This is used for post-install visual feedback. The page_action
   // icon is briefly shown even if it hasn't been enabled by its
diff --git a/chrome/browser/ui/cocoa/location_bar/page_action_decoration.mm b/chrome/browser/ui/cocoa/location_bar/page_action_decoration.mm
index 74f86ee..330b60d 100644
--- a/chrome/browser/ui/cocoa/location_bar/page_action_decoration.mm
+++ b/chrome/browser/ui/cocoa/location_bar/page_action_decoration.mm
@@ -17,7 +17,7 @@
 #include "chrome/browser/sessions/session_id.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
-#import "chrome/browser/ui/cocoa/extensions/extension_action_context_menu.h"
+#import "chrome/browser/ui/cocoa/extensions/extension_action_context_menu_controller.h"
 #import "chrome/browser/ui/cocoa/extensions/extension_popup_controller.h"
 #include "chrome/browser/ui/cocoa/last_active_browser_cocoa.h"
 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
@@ -232,12 +232,15 @@
   DCHECK(extension);
   if (!extension)
     return nil;
-  menu_.reset([[ExtensionActionContextMenu alloc]
+
+  contextMenuController_.reset([[ExtensionActionContextMenuController alloc]
       initWithExtension:extension
                 browser:browser_
         extensionAction:page_action_]);
 
-  return menu_.get();
+  base::scoped_nsobject<NSMenu> contextMenu([[NSMenu alloc] initWithTitle:@""]);
+  [contextMenuController_ populateMenu:contextMenu];
+  return contextMenu.autorelease();
 }
 
 void PageActionDecoration::ShowPopup(const NSRect& frame,
diff --git a/chrome/browser/ui/cocoa/location_bar/zoom_decoration.mm b/chrome/browser/ui/cocoa/location_bar/zoom_decoration.mm
index 0d60f5b..d6a3fd2 100644
--- a/chrome/browser/ui/cocoa/location_bar/zoom_decoration.mm
+++ b/chrome/browser/ui/cocoa/location_bar/zoom_decoration.mm
@@ -101,11 +101,8 @@
 }
 
 bool ZoomDecoration::ShouldShowDecoration() const {
-  if (owner_->GetToolbarModel()->input_in_progress())
-    return false;
-  if (bubble_)
-    return true;
-  return !IsAtDefaultZoom();
+  return !owner_->GetToolbarModel()->input_in_progress() &&
+      (bubble_ || !IsAtDefaultZoom());
 }
 
 bool ZoomDecoration::AcceptsMousePress() {
diff --git a/chrome/browser/ui/cocoa/notifications/message_center_tray_bridge.mm b/chrome/browser/ui/cocoa/notifications/message_center_tray_bridge.mm
index e157927..acb9edc 100644
--- a/chrome/browser/ui/cocoa/notifications/message_center_tray_bridge.mm
+++ b/chrome/browser/ui/cocoa/notifications/message_center_tray_bridge.mm
@@ -63,14 +63,7 @@
   if (tray_controller_)
     return false;
 
-  // Post a task to open the window, because in
-  // MessageCenterTray::ShowMessageCenterBubble, the unread count gets set to
-  // 0 after it calls this delegate. In order show the window at the correct
-  // position after the unread count is updated, opening the window must be
-  // performed after the return of this method.
-  base::MessageLoop::current()->PostTask(FROM_HERE,
-      base::Bind(&MessageCenterTrayBridge::OpenTrayWindow,
-                 weak_ptr_factory_.GetWeakPtr()));
+  OpenTrayWindow();
   return true;
 }
 
diff --git a/chrome/browser/ui/cocoa/notifications/message_center_tray_bridge_unittest.mm b/chrome/browser/ui/cocoa/notifications/message_center_tray_bridge_unittest.mm
index 57008c0..e8755f7 100644
--- a/chrome/browser/ui/cocoa/notifications/message_center_tray_bridge_unittest.mm
+++ b/chrome/browser/ui/cocoa/notifications/message_center_tray_bridge_unittest.mm
@@ -51,7 +51,7 @@
           ASCIIToUTF16("This is a simple test."),
           gfx::Image(),
           string16(),
-          std::string(),
+          message_center::NotifierId(),
           data,
           NULL));
   center_->AddNotification(notification.Pass());
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h
index 5cb76b6..481cad1 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h
@@ -30,8 +30,8 @@
 
   // OmniboxView:
   virtual void SaveStateToTab(content::WebContents* tab) OVERRIDE;
-  virtual void Update(
-      const content::WebContents* tab_for_state_restoring) OVERRIDE;
+  virtual void OnTabChanged(const content::WebContents* web_contents) OVERRIDE;
+  virtual void Update() OVERRIDE;
   virtual string16 GetText() const OVERRIDE;
   virtual void SetWindowTextAndCaretPos(const string16& text,
                                         size_t caret_pos,
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm
index 5edacd7..80a87d0 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm
@@ -183,43 +183,32 @@
   StoreStateToTab(tab, state);
 }
 
-void OmniboxViewMac::Update(const WebContents* tab_for_state_restoring) {
-  // TODO(shess): It seems like if the tab is non-NULL, then this code
-  // shouldn't need to be called at all.  When coded that way, I find
-  // that the field isn't always updated correctly.  Figure out why
-  // this is.  Maybe this method should be refactored into more
-  // specific cases.
-  bool user_visible = model()->UpdatePermanentText(
-      controller()->GetToolbarModel()->GetText(true));
-
-  if (tab_for_state_restoring) {
-    RevertAll();
-
-    const OmniboxViewMacState* state = GetStateFromTab(tab_for_state_restoring);
-    if (state) {
-      // Should restore the user's text via SetUserText().
-      model()->RestoreState(state->model_state);
-
-      // Restore focus and selection if they were present when the tab
-      // was switched away.
-      if (state->has_focus) {
-        // TODO(shess): Unfortunately, there is no safe way to update
-        // this because TabStripController -selectTabWithContents:* is
-        // also messing with focus.  Both parties need to agree to
-        // store existing state before anyone tries to setup the new
-        // state.  Anyhow, it would look something like this.
+void OmniboxViewMac::OnTabChanged(const WebContents* web_contents) {
+  const OmniboxViewMacState* state = GetStateFromTab(web_contents);
+  model()->RestoreState(state ? &state->model_state : NULL);
+  // Restore focus and selection if they were present when the tab
+  // was switched away.
+  if (state && state->has_focus) {
+    // TODO(shess): Unfortunately, there is no safe way to update
+    // this because TabStripController -selectTabWithContents:* is
+    // also messing with focus.  Both parties need to agree to
+    // store existing state before anyone tries to setup the new
+    // state.  Anyhow, it would look something like this.
 #if 0
-        [[field_ window] makeFirstResponder:field_];
-        [[field_ currentEditor] setSelectedRange:state->selection];
+    [[field_ window] makeFirstResponder:field_];
+    [[field_ currentEditor] setSelectedRange:state->selection];
 #endif
-      }
-    }
-  } else if (user_visible) {
+  }
+}
+
+void OmniboxViewMac::Update() {
+  if (model()->UpdatePermanentText(
+      controller()->GetToolbarModel()->GetText(true))) {
     // Restore everything to the baseline look.
     RevertAll();
+
     // TODO(shess): Figure out how this case is used, to make sure
     // we're getting the selection and popup right.
-
   } else {
     // TODO(shess): This corresponds to _win and _gtk, except those
     // guard it with a test for whether the security level changed.
diff --git a/chrome/browser/ui/cocoa/one_click_signin_view_controller.mm b/chrome/browser/ui/cocoa/one_click_signin_view_controller.mm
index 07029d1..8f710d4 100644
--- a/chrome/browser/ui/cocoa/one_click_signin_view_controller.mm
+++ b/chrome/browser/ui/cocoa/one_click_signin_view_controller.mm
@@ -168,7 +168,7 @@
 
   if (isSyncDialog_) {
     [messageTextField_ setStringValue:l10n_util::GetNSStringWithFixup(
-        IDS_ONE_CLICK_SIGNIN_DIALOG_TITLE_NEW)];
+        IDS_ONE_CLICK_SIGNIN_DIALOG_TITLE)];
   } else if ([errorMessage_ length] != 0) {
     [messageTextField_ setStringValue:errorMessage_];
   }
diff --git a/chrome/browser/ui/cocoa/panels/panel_titlebar_view_cocoa.mm b/chrome/browser/ui/cocoa/panels/panel_titlebar_view_cocoa.mm
index 3a9dde6..223161b 100644
--- a/chrome/browser/ui/cocoa/panels/panel_titlebar_view_cocoa.mm
+++ b/chrome/browser/ui/cocoa/panels/panel_titlebar_view_cocoa.mm
@@ -407,7 +407,7 @@
   if ([event clickCount] == 1)
     [controller_ onTitlebarMouseClicked:[event modifierFlags]];
   else if ([event clickCount] == 2)
-    [controller_ minimizeButtonClicked:[event modifierFlags]];
+    [controller_ onTitlebarDoubleClicked:[event modifierFlags]];
 }
 
 - (void)mouseDragged:(NSEvent*)event {
diff --git a/chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.h b/chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.h
index 6627170..52462db 100644
--- a/chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.h
+++ b/chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.h
@@ -101,6 +101,9 @@
 // Minimized/Restored states.
 - (void)onTitlebarMouseClicked:(int)modifierFlags;
 
+// Invoked when user double-clicks on the titlebar.
+- (void)onTitlebarDoubleClicked:(int)modifierFlags;
+
 // NSAnimationDelegate method, invoked when bounds animation is finished.
 - (void)animationDidEnd:(NSAnimation*)animation;
 // Terminates current bounds animation, if any.
diff --git a/chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.mm b/chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.mm
index 126a8ff..c93ead6 100644
--- a/chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.mm
+++ b/chrome/browser/ui/cocoa/panels/panel_window_controller_cocoa.mm
@@ -774,6 +774,15 @@
                            panel::APPLY_TO_ALL : panel::NO_MODIFIER);
 }
 
+- (void)onTitlebarDoubleClicked:(int)modifierFlags {
+  // Double-clicking is only allowed to minimize docked panels.
+  Panel* panel = windowShim_->panel();
+  if (panel->collection()->type() != PanelCollection::DOCKED ||
+      panel->IsMinimized())
+    return;
+  [self minimizeButtonClicked:modifierFlags];
+}
+
 - (int)titlebarHeightInScreenCoordinates {
   NSView* titlebar = [self titlebarView];
   return NSHeight([titlebar convertRect:[titlebar bounds] toView:nil]);
@@ -831,8 +840,18 @@
     if ([contentView superview])
       [contentView removeFromSuperview];
   } else {
-    if (![contentView superview])
+    if (![contentView superview]) {
       [[[self window] contentView] addSubview:contentView];
+
+      // When the web contents view is put back, we need to tell its render
+      // widget host view to accept focus.
+      content::RenderWidgetHostView* rwhv =
+          webContents->GetRenderWidgetHostView();
+      if (rwhv) {
+        [[self window] makeFirstResponder:rwhv->GetNativeView()];
+        rwhv->SetActive([[self window] isMainWindow]);
+      }
+    }
   }
 }
 
diff --git a/chrome/browser/ui/cocoa/status_icons/status_icon_mac.h b/chrome/browser/ui/cocoa/status_icons/status_icon_mac.h
index 8b380c4..6bd6570 100644
--- a/chrome/browser/ui/cocoa/status_icons/status_icon_mac.h
+++ b/chrome/browser/ui/cocoa/status_icons/status_icon_mac.h
@@ -35,7 +35,8 @@
 
  protected:
   // Overridden from StatusIcon.
-  virtual void UpdatePlatformContextMenu(ui::MenuModel* model) OVERRIDE;
+  virtual void UpdatePlatformContextMenu(
+      StatusIconMenuModel* model) OVERRIDE;
 
  private:
   FRIEND_TEST_ALL_PREFIXES(StatusIconMacTest, CreateMenu);
diff --git a/chrome/browser/ui/cocoa/status_icons/status_icon_mac.mm b/chrome/browser/ui/cocoa/status_icons/status_icon_mac.mm
index d02c792..cc79fc6 100644
--- a/chrome/browser/ui/cocoa/status_icons/status_icon_mac.mm
+++ b/chrome/browser/ui/cocoa/status_icons/status_icon_mac.mm
@@ -101,7 +101,7 @@
   return menu_.get() != nil;
 }
 
-void StatusIconMac::UpdatePlatformContextMenu(ui::MenuModel* model) {
+void StatusIconMac::UpdatePlatformContextMenu(StatusIconMenuModel* model) {
   if (!model) {
     menu_.reset();
   } else {
diff --git a/chrome/browser/ui/cocoa/status_icons/status_icon_mac_unittest.mm b/chrome/browser/ui/cocoa/status_icons/status_icon_mac_unittest.mm
index 7675b98..c3ae23d 100644
--- a/chrome/browser/ui/cocoa/status_icons/status_icon_mac_unittest.mm
+++ b/chrome/browser/ui/cocoa/status_icons/status_icon_mac_unittest.mm
@@ -5,12 +5,12 @@
 #include "base/strings/string_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/status_icons/status_icon_menu_model.h"
 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
 #include "chrome/browser/ui/cocoa/status_icons/status_icon_mac.h"
 #include "grit/chrome_unscaled_resources.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/models/simple_menu_model.h"
 #include "ui/base/resource/resource_bundle.h"
 
 class SkBitmap;
@@ -32,7 +32,7 @@
 TEST_F(StatusIconMacTest, CreateMenu) {
   // Create a menu and verify by getting the title of the first menu item.
   const char* menu_title = "Menu Title";
-  scoped_ptr<ui::SimpleMenuModel> model(new ui::SimpleMenuModel(NULL));
+  scoped_ptr<StatusIconMenuModel> model(new StatusIconMenuModel(NULL));
   model->AddItem(0, ASCIIToUTF16(menu_title));
 
   scoped_ptr<StatusIconMac> icon(new StatusIconMac());
@@ -48,7 +48,7 @@
   // first menu item.
   const char* menu_title = "Menu Title";
   const char* tool_tip = "Tool tip";
-  scoped_ptr<ui::SimpleMenuModel> model(new ui::SimpleMenuModel(NULL));
+  scoped_ptr<StatusIconMenuModel> model(new StatusIconMenuModel(NULL));
   model->AddItem(0, ASCIIToUTF16(menu_title));
 
   scoped_ptr<StatusIconMac> icon(new StatusIconMac());
diff --git a/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.mm b/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.mm
index 5d54f76..2e48592 100644
--- a/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.mm
+++ b/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.mm
@@ -63,6 +63,10 @@
   }
   [contentsNativeView setAutoresizingMask:NSViewWidthSizable|
                                           NSViewHeightSizable];
+  // The rendering path with overlapping views disabled causes bugs when
+  // transitioning between composited and non-composited mode.
+  // http://crbug.com/279472
+  contents_->GetView()->SetAllowOverlappingViews(true);
 }
 
 - (void)changeWebContents:(WebContents*)newContents {
diff --git a/chrome/browser/ui/cocoa/tab_modal_confirm_dialog_mac.mm b/chrome/browser/ui/cocoa/tab_modal_confirm_dialog_mac.mm
index 40a949a..7301374 100644
--- a/chrome/browser/ui/cocoa/tab_modal_confirm_dialog_mac.mm
+++ b/chrome/browser/ui/cocoa/tab_modal_confirm_dialog_mac.mm
@@ -112,8 +112,13 @@
 
 void TabModalConfirmDialogMac::OnConstrainedWindowClosed(
     ConstrainedWindowMac* window) {
+  // If this method should mistakenly be called during Delegate::Close(),
+  // prevent a double-delete by moving delegate_ to a stack variable.
+  if (!delegate_)
+    return;
+  scoped_ptr<TabModalConfirmDialogDelegate> delegate(delegate_.Pass());
   // Provide a disposition in case the dialog was closed without accepting or
   // cancelling.
-  delegate_->Close();
+  delegate->Close();
   delete this;
 }
diff --git a/chrome/browser/ui/extensions/extension_install_ui_default.cc b/chrome/browser/ui/extensions/extension_install_ui_default.cc
index 36bc8bc..41efdff 100644
--- a/chrome/browser/ui/extensions/extension_install_ui_default.cc
+++ b/chrome/browser/ui/extensions/extension_install_ui_default.cc
@@ -53,8 +53,6 @@
 
 // Helpers --------------------------------------------------------------------
 
-bool disable_failure_ui_for_tests = false;
-
 Browser* FindOrCreateVisibleBrowser(Profile* profile) {
   // TODO(mpcomplete): remove this workaround for http://crbug.com/244246
   // after fixing http://crbug.com/38676.
@@ -179,11 +177,6 @@
 }
 
 // static
-void ExtensionInstallUI::DisableFailureUIForTests() {
-  disable_failure_ui_for_tests = true;
-}
-
-// static
 ExtensionInstallPrompt* ExtensionInstallUI::CreateInstallPromptWithBrowser(
     Browser* browser) {
   content::WebContents* web_contents = NULL;
@@ -205,7 +198,6 @@
 
 ExtensionInstallUIDefault::ExtensionInstallUIDefault(Profile* profile)
     : ExtensionInstallUI(profile),
-      skip_post_install_ui_(false),
       previous_using_native_theme_(false),
       use_app_installed_bubble_(false) {
   // |profile| can be NULL during tests.
@@ -224,7 +216,7 @@
 
 void ExtensionInstallUIDefault::OnInstallSuccess(const Extension* extension,
                                                  SkBitmap* icon) {
-  if (skip_post_install_ui_)
+  if (skip_post_install_ui())
     return;
 
   if (!profile()) {
@@ -277,7 +269,7 @@
 void ExtensionInstallUIDefault::OnInstallFailure(
     const extensions::CrxInstallerError& error) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  if (disable_failure_ui_for_tests || skip_post_install_ui_)
+  if (disable_failure_ui_for_tests() || skip_post_install_ui())
     return;
 
   Browser* browser =
@@ -292,10 +284,6 @@
                                error);
 }
 
-void ExtensionInstallUIDefault::SetSkipPostInstallUI(bool skip_ui) {
-  skip_post_install_ui_ = skip_ui;
-}
-
 void ExtensionInstallUIDefault::SetUseAppInstalledBubble(bool use_bubble) {
   use_app_installed_bubble_ = use_bubble;
 }
diff --git a/chrome/browser/ui/extensions/extension_install_ui_default.h b/chrome/browser/ui/extensions/extension_install_ui_default.h
index 52ce0f7..60bfc0f 100644
--- a/chrome/browser/ui/extensions/extension_install_ui_default.h
+++ b/chrome/browser/ui/extensions/extension_install_ui_default.h
@@ -21,13 +21,9 @@
                                 SkBitmap* icon) OVERRIDE;
   virtual void OnInstallFailure(
       const extensions::CrxInstallerError& error) OVERRIDE;
-  virtual void SetSkipPostInstallUI(bool skip_ui) OVERRIDE;
   virtual void SetUseAppInstalledBubble(bool use_bubble) OVERRIDE;
 
  private:
-  // Whether or not to show the default UI after completing the installation.
-  bool skip_post_install_ui_;
-
   // Used to undo theme installation.
   std::string previous_theme_id_;
   bool previous_using_native_theme_;
diff --git a/chrome/browser/ui/find_bar/find_bar.h b/chrome/browser/ui/find_bar/find_bar.h
index e34b62a..5ca9231 100644
--- a/chrome/browser/ui/find_bar/find_bar.h
+++ b/chrome/browser/ui/find_bar/find_bar.h
@@ -55,6 +55,9 @@
   // Set the text in the find box.
   virtual void SetFindText(const string16& find_text) = 0;
 
+  // Gets the search string currently visible in the find box.
+  virtual string16 GetFindText() = 0;
+
   // Updates the FindBar with the find result details contained within the
   // specified |result|.
   virtual void UpdateUIForFindResult(const FindNotificationDetails& result,
@@ -93,9 +96,6 @@
   virtual bool GetFindBarWindowInfo(gfx::Point* position,
                                     bool* fully_visible) = 0;
 
-  // Gets the search string currently visible in the Find box.
-  virtual string16 GetFindText() = 0;
-
   // Gets the search string currently selected in the Find box.
   virtual string16 GetFindSelectedText() = 0;
 
diff --git a/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc b/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc
index 103960c..c4030c8 100644
--- a/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc
+++ b/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc
@@ -91,8 +91,7 @@
   }
 
   string16 GetFindBarTextForBrowser(Browser* browser) {
-    FindBarTesting* find_bar =
-        browser->GetFindBarController()->find_bar()->GetFindBarTesting();
+    FindBar* find_bar = browser->GetFindBarController()->find_bar();
     return find_bar->GetFindText();
   }
 
@@ -169,7 +168,7 @@
     return start_x_position;
   }
 
-  GURL GetURL(const std::string filename) {
+  GURL GetURL(const std::string& filename) {
     return ui_test_utils::GetTestUrl(
         base::FilePath().AppendASCII("find_in_page"),
         base::FilePath().AppendASCII(filename));
@@ -1568,6 +1567,11 @@
   EXPECT_EQ(ASCIIToUTF16("1 of 2"),
       GetFindBarMatchCountTextForBrowser(browser_incognito));
 
+  // Close the find bar.
+  FindTabHelper* find_tab_helper =
+      FindTabHelper::FromWebContents(web_contents_incognito);
+  find_tab_helper->StopFinding(FindBarController::kActivateSelectionOnPage);
+
   // Cmd + G triggers IDC_FIND_NEXT command. Thus we test FindInPage()
   // method from browser_commands.cc. FindInPageWchar() bypasses it.
   EXPECT_TRUE(chrome::ExecuteCommand(browser_incognito, IDC_FIND_NEXT));
diff --git a/chrome/browser/ui/fullscreen/fullscreen_controller.cc b/chrome/browser/ui/fullscreen/fullscreen_controller.cc
index 7c1621e..d275cfe 100644
--- a/chrome/browser/ui/fullscreen/fullscreen_controller.cc
+++ b/chrome/browser/ui/fullscreen/fullscreen_controller.cc
@@ -28,6 +28,9 @@
 
 #if defined(OS_MACOSX)
 #include "base/mac/mac_util.h"
+#else
+#include "base/prefs/pref_service.h"
+#include "chrome/common/pref_names.h"
 #endif
 
 using content::RenderViewHost;
@@ -552,6 +555,16 @@
   if (chrome::IsRunningInAppMode() && window_->IsFullscreen())
     return;
 
+#if !defined(OS_MACOSX)
+  // Do not enter fullscreen mode if disallowed by pref. This prevents the user
+  // from manually entering fullscreen mode and also disables kiosk mode on
+  // desktop platforms.
+  if (enter_fullscreen &&
+      !profile_->GetPrefs()->GetBoolean(prefs::kFullscreenAllowed)) {
+    return;
+  }
+#endif
+
   if (enter_fullscreen)
     EnterFullscreenModeInternal(option);
   else
diff --git a/chrome/browser/ui/gesture_prefs_observer_factory_aura.cc b/chrome/browser/ui/gesture_prefs_observer_factory_aura.cc
index 4e73dcc..f49e341 100644
--- a/chrome/browser/ui/gesture_prefs_observer_factory_aura.cc
+++ b/chrome/browser/ui/gesture_prefs_observer_factory_aura.cc
@@ -50,7 +50,9 @@
       { prefs::kOverscrollVerticalThresholdComplete,
         OVERSCROLL_CONFIG_VERT_THRESHOLD_COMPLETE },
       { prefs::kOverscrollMinimumThresholdStart,
-        OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START },
+        OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN },
+      { prefs::kOverscrollMinimumThresholdStartTouchpad,
+        OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD },
       { prefs::kOverscrollVerticalThresholdStart,
         OVERSCROLL_CONFIG_VERT_THRESHOLD_START },
       { prefs::kOverscrollHorizontalResistThreshold,
diff --git a/chrome/browser/ui/gtk/apps/native_app_window_gtk.cc b/chrome/browser/ui/gtk/apps/native_app_window_gtk.cc
index 43d9490..d2c5925 100644
--- a/chrome/browser/ui/gtk/apps/native_app_window_gtk.cc
+++ b/chrome/browser/ui/gtk/apps/native_app_window_gtk.cc
@@ -368,6 +368,9 @@
       rect_with_decorations.width - current_width - left_inset);
 }
 
+void NativeAppWindowGtk::HideWithApp() {}
+void NativeAppWindowGtk::ShowWithApp() {}
+
 gfx::NativeView NativeAppWindowGtk::GetHostView() const {
   NOTIMPLEMENTED();
   return NULL;
diff --git a/chrome/browser/ui/gtk/apps/native_app_window_gtk.h b/chrome/browser/ui/gtk/apps/native_app_window_gtk.h
index b43c8fa..a665438 100644
--- a/chrome/browser/ui/gtk/apps/native_app_window_gtk.h
+++ b/chrome/browser/ui/gtk/apps/native_app_window_gtk.h
@@ -71,6 +71,8 @@
       const std::vector<extensions::DraggableRegion>& regions) OVERRIDE;
   virtual void RenderViewHostChanged() OVERRIDE;
   virtual gfx::Insets GetFrameInsets() const OVERRIDE;
+  virtual void HideWithApp() OVERRIDE;
+  virtual void ShowWithApp() OVERRIDE;
 
   // web_modal::WebContentsModalDialogHost implementation.
   virtual gfx::NativeView GetHostView() const OVERRIDE;
diff --git a/chrome/browser/ui/gtk/bookmarks/bookmark_bar_gtk.h b/chrome/browser/ui/gtk/bookmarks/bookmark_bar_gtk.h
index 2d78d00..28065ed 100644
--- a/chrome/browser/ui/gtk/bookmarks/bookmark_bar_gtk.h
+++ b/chrome/browser/ui/gtk/bookmarks/bookmark_bar_gtk.h
@@ -91,6 +91,8 @@
 
   const ui::Animation* animation() { return &slide_animation_; }
 
+  int max_height() const { return max_height_; }
+
  private:
   FRIEND_TEST_ALL_PREFIXES(BookmarkBarGtkUnittest, DisplaysHelpMessageOnEmpty);
   FRIEND_TEST_ALL_PREFIXES(BookmarkBarGtkUnittest,
diff --git a/chrome/browser/ui/gtk/browser_window_gtk.cc b/chrome/browser/ui/gtk/browser_window_gtk.cc
index 47fa0e7..1835e50 100644
--- a/chrome/browser/ui/gtk/browser_window_gtk.cc
+++ b/chrome/browser/ui/gtk/browser_window_gtk.cc
@@ -1187,6 +1187,15 @@
       GetNativeWindow(), download_count, dialog_type, app_modal, callback);
 }
 
+int
+BrowserWindowGtk::GetRenderViewHeightInsetWithDetachedBookmarkBar() {
+  if (!bookmark_bar_.get() ||
+      browser_->bookmark_bar_state() != BookmarkBar::DETACHED) {
+    return 0;
+  }
+  return bookmark_bar_->max_height();
+}
+
 void BrowserWindowGtk::Observe(int type,
                                const content::NotificationSource& source,
                                const content::NotificationDetails& details) {
diff --git a/chrome/browser/ui/gtk/browser_window_gtk.h b/chrome/browser/ui/gtk/browser_window_gtk.h
index dadf6ca..b7ce475 100644
--- a/chrome/browser/ui/gtk/browser_window_gtk.h
+++ b/chrome/browser/ui/gtk/browser_window_gtk.h
@@ -176,6 +176,7 @@
       const gfx::Rect& rect,
       const content::PasswordForm& form,
       autofill::PasswordGenerator* password_generator) OVERRIDE;
+  virtual int GetRenderViewHeightInsetWithDetachedBookmarkBar() OVERRIDE;
 
   // Overridden from NotificationObserver:
   virtual void Observe(int type,
diff --git a/chrome/browser/ui/gtk/download/download_item_gtk.cc b/chrome/browser/ui/gtk/download/download_item_gtk.cc
index 8719858..fb53c7a 100644
--- a/chrome/browser/ui/gtk/download/download_item_gtk.cc
+++ b/chrome/browser/ui/gtk/download/download_item_gtk.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/download/chrome_download_manager_delegate.h"
 #include "chrome/browser/download/download_item_model.h"
-#include "chrome/browser/download/download_util.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/gtk/custom_drag.h"
diff --git a/chrome/browser/ui/gtk/extensions/extension_installed_bubble_gtk.cc b/chrome/browser/ui/gtk/extensions/extension_installed_bubble_gtk.cc
index b27c4c5..b153106 100644
--- a/chrome/browser/ui/gtk/extensions/extension_installed_bubble_gtk.cc
+++ b/chrome/browser/ui/gtk/extensions/extension_installed_bubble_gtk.cc
@@ -81,14 +81,11 @@
       animation_wait_retries_(kAnimationWaitRetries),
       bubble_(NULL),
       weak_factory_(this) {
-  extensions::ExtensionActionManager* extension_action_manager =
-      ExtensionActionManager::Get(browser_->profile());
-
   if (!extensions::OmniboxInfo::GetKeyword(extension_).empty())
     type_ = OMNIBOX_KEYWORD;
-  else if (extension_action_manager->GetBrowserAction(*extension_))
+  else if (extensions::ActionInfo::GetBrowserActionInfo(extension_))
     type_ = BROWSER_ACTION;
-  else if (extension_action_manager->GetPageAction(*extension) &&
+  else if (extensions::ActionInfo::GetPageActionInfo(extension) &&
            extensions::ActionInfo::IsVerboseInstallMessage(extension))
     type_ = PAGE_ACTION;
   else
diff --git a/chrome/browser/ui/gtk/find_bar_gtk.cc b/chrome/browser/ui/gtk/find_bar_gtk.cc
index ba9071c..9f512f9 100644
--- a/chrome/browser/ui/gtk/find_bar_gtk.cc
+++ b/chrome/browser/ui/gtk/find_bar_gtk.cc
@@ -377,6 +377,11 @@
   ignore_changed_signal_ = false;
 }
 
+string16 FindBarGtk::GetFindText() {
+  std::string contents(gtk_entry_get_text(GTK_ENTRY(text_entry_)));
+  return UTF8ToUTF16(contents);
+}
+
 void FindBarGtk::UpdateUIForFindResult(const FindNotificationDetails& result,
                                        const string16& find_text) {
   selection_rect_ = result.selection_rect();
@@ -572,11 +577,6 @@
   return true;
 }
 
-string16 FindBarGtk::GetFindText() {
-  std::string contents(gtk_entry_get_text(GTK_ENTRY(text_entry_)));
-  return UTF8ToUTF16(contents);
-}
-
 string16 FindBarGtk::GetFindSelectedText() {
   gint cursor_pos;
   gint selection_bound;
diff --git a/chrome/browser/ui/gtk/find_bar_gtk.h b/chrome/browser/ui/gtk/find_bar_gtk.h
index 2c90616..11973c6 100644
--- a/chrome/browser/ui/gtk/find_bar_gtk.h
+++ b/chrome/browser/ui/gtk/find_bar_gtk.h
@@ -51,6 +51,7 @@
   virtual void MoveWindowIfNecessary(const gfx::Rect& selection_rect,
                                      bool no_redraw) OVERRIDE;
   virtual void SetFindText(const string16& find_text) OVERRIDE;
+  virtual string16 GetFindText() OVERRIDE;
   virtual void UpdateUIForFindResult(const FindNotificationDetails& result,
                                      const string16& find_text) OVERRIDE;
   virtual void AudibleAlert() OVERRIDE;
@@ -63,7 +64,6 @@
   // Methods from FindBarTesting.
   virtual bool GetFindBarWindowInfo(gfx::Point* position,
                                     bool* fully_visible) OVERRIDE;
-  virtual string16 GetFindText() OVERRIDE;
   virtual string16 GetFindSelectedText() OVERRIDE;
   virtual string16 GetMatchCountText() OVERRIDE;
   virtual int GetWidth() OVERRIDE;
diff --git a/chrome/browser/ui/gtk/location_bar_view_gtk.cc b/chrome/browser/ui/gtk/location_bar_view_gtk.cc
index fe478aa..42e5c0d 100644
--- a/chrome/browser/ui/gtk/location_bar_view_gtk.cc
+++ b/chrome/browser/ui/gtk/location_bar_view_gtk.cc
@@ -666,7 +666,10 @@
   UpdateSiteTypeArea();
   UpdateContentSettingsIcons();
   UpdatePageActions();
-  location_entry_->Update(contents);
+  if (contents)
+    location_entry_->OnTabChanged(contents);
+  else
+    location_entry_->Update();
   // The security level (background color) could have changed, etc.
   if (theme_service_->UsingNativeTheme()) {
     // In GTK mode, we need our parent to redraw, as it draws the text entry
@@ -926,7 +929,7 @@
   }
 
   if (!page_action_views_.empty() && contents) {
-    GURL url = browser()->tab_strip_model()->GetActiveWebContents()->GetURL();
+    GURL url = GetWebContents()->GetURL();
 
     for (size_t i = 0; i < page_action_views_.size(); i++) {
       page_action_views_[i]->UpdateVisibility(
diff --git a/chrome/browser/ui/gtk/location_bar_view_gtk.h b/chrome/browser/ui/gtk/location_bar_view_gtk.h
index f728a89..c4c1192 100644
--- a/chrome/browser/ui/gtk/location_bar_view_gtk.h
+++ b/chrome/browser/ui/gtk/location_bar_view_gtk.h
@@ -90,9 +90,9 @@
   GtkWidget* GetPageActionWidget(ExtensionAction* page_action);
 
   // Updates the location bar.  We also reset the bar's permanent text and
-  // security style, and, if |tab_for_state_restoring| is non-NULL, also
-  // restore saved state that the tab holds.
-  void Update(const content::WebContents* tab_for_state_restoring);
+  // security style, and, if |contents| is non-NULL, also restore saved state
+  // that the tab holds.
+  void Update(const content::WebContents* contents);
 
   // Show the bookmark bubble.
   void ShowStarBubble(const GURL& url, bool newly_boomkarked);
diff --git a/chrome/browser/ui/gtk/omnibox/omnibox_popup_view_gtk.cc b/chrome/browser/ui/gtk/omnibox/omnibox_popup_view_gtk.cc
index 23ab592..c0f6dc2 100644
--- a/chrome/browser/ui/gtk/omnibox/omnibox_popup_view_gtk.cc
+++ b/chrome/browser/ui/gtk/omnibox/omnibox_popup_view_gtk.cc
@@ -31,7 +31,6 @@
 #include "ui/base/gtk/gtk_compat.h"
 #include "ui/base/gtk/gtk_hig_constants.h"
 #include "ui/base/gtk/gtk_screen_util.h"
-#include "ui/base/gtk/gtk_signal_registrar.h"
 #include "ui/base/gtk/gtk_windowing.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/font.h"
@@ -267,8 +266,7 @@
                                          OmniboxView* omnibox_view,
                                          OmniboxEditModel* edit_model,
                                          GtkWidget* location_bar)
-    : signal_registrar_(new ui::GtkSignalRegistrar),
-      model_(new OmniboxPopupModel(this, edit_model)),
+    : model_(new OmniboxPopupModel(this, edit_model)),
       omnibox_view_(omnibox_view),
       location_bar_(location_bar),
       window_(gtk_window_new(GTK_WINDOW_POPUP)),
@@ -298,14 +296,14 @@
                                  GDK_POINTER_MOTION_MASK |
                                  GDK_BUTTON_PRESS_MASK |
                                  GDK_BUTTON_RELEASE_MASK);
-  signal_registrar_->Connect(window_, "motion-notify-event",
-                             G_CALLBACK(HandleMotionThunk), this);
-  signal_registrar_->Connect(window_, "button-press-event",
-                             G_CALLBACK(HandleButtonPressThunk), this);
-  signal_registrar_->Connect(window_, "button-release-event",
-                             G_CALLBACK(HandleButtonReleaseThunk), this);
-  signal_registrar_->Connect(window_, "expose-event",
-                             G_CALLBACK(HandleExposeThunk), this);
+  g_signal_connect(window_, "motion-notify-event",
+                   G_CALLBACK(HandleMotionThunk), this);
+  g_signal_connect(window_, "button-press-event",
+                   G_CALLBACK(HandleButtonPressThunk), this);
+  g_signal_connect(window_, "button-release-event",
+                   G_CALLBACK(HandleButtonReleaseThunk), this);
+  g_signal_connect(window_, "expose-event",
+                   G_CALLBACK(HandleExposeThunk), this);
 
   registrar_.Add(this,
                  chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
@@ -326,10 +324,6 @@
 }
 
 OmniboxPopupViewGtk::~OmniboxPopupViewGtk() {
-  // Stop listening to our signals before we destroy the model. I suspect that
-  // we can race window destruction, otherwise.
-  signal_registrar_.reset();
-
   // Explicitly destroy our model here, before we destroy our GTK widgets.
   // This is because the model destructor can call back into us, and we need
   // to make sure everything is still valid when it does.
@@ -467,6 +461,7 @@
 }
 
 size_t OmniboxPopupViewGtk::LineFromY(int y) {
+  DCHECK_NE(0U, model_->result().size());
   size_t line = std::max(y - kBorderThickness, 0) / kHeightPerResult;
   return std::min(line, model_->result().size() - 1);
 }
@@ -542,6 +537,9 @@
 
 gboolean OmniboxPopupViewGtk::HandleMotion(GtkWidget* widget,
                                            GdkEventMotion* event) {
+  if (!IsOpen())
+    return FALSE;
+
   // TODO(deanm): Windows has a bunch of complicated logic here.
   size_t line = LineFromY(static_cast<int>(event->y));
   // There is both a hovered and selected line, hovered just means your mouse
@@ -555,6 +553,9 @@
 
 gboolean OmniboxPopupViewGtk::HandleButtonPress(GtkWidget* widget,
                                                 GdkEventButton* event) {
+  if (!IsOpen())
+    return FALSE;
+
   ignore_mouse_drag_ = false;
   // Very similar to HandleMotion.
   size_t line = LineFromY(static_cast<int>(event->y));
@@ -566,6 +567,9 @@
 
 gboolean OmniboxPopupViewGtk::HandleButtonRelease(GtkWidget* widget,
                                                   GdkEventButton* event) {
+  if (!IsOpen())
+    return FALSE;
+
   if (ignore_mouse_drag_) {
     // See header comment about this flag.
     ignore_mouse_drag_ = false;
diff --git a/chrome/browser/ui/gtk/omnibox/omnibox_popup_view_gtk.h b/chrome/browser/ui/gtk/omnibox/omnibox_popup_view_gtk.h
index 0b6d4b2..fcb6611 100644
--- a/chrome/browser/ui/gtk/omnibox/omnibox_popup_view_gtk.h
+++ b/chrome/browser/ui/gtk/omnibox/omnibox_popup_view_gtk.h
@@ -31,10 +31,6 @@
 class Image;
 }
 
-namespace ui {
-class GtkSignalRegistrar;
-}
-
 class OmniboxPopupViewGtk : public OmniboxPopupView,
                             public content::NotificationObserver {
  public:
@@ -104,7 +100,6 @@
   CHROMEGTK_CALLBACK_1(OmniboxPopupViewGtk, gboolean, HandleExpose,
                        GdkEventExpose*);
 
-  scoped_ptr<ui::GtkSignalRegistrar> signal_registrar_;
   scoped_ptr<OmniboxPopupModel> model_;
   OmniboxView* omnibox_view_;
   GtkWidget* location_bar_;
diff --git a/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.cc b/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.cc
index 28e0462..62d8831 100644
--- a/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.cc
+++ b/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.cc
@@ -450,38 +450,32 @@
       new AutocompleteEditState(model_state, ViewState(GetSelection())));
 }
 
-void OmniboxViewGtk::Update(const WebContents* contents) {
-  // NOTE: We're getting the URL text here from the ToolbarModel.
-  bool visibly_changed_permanent_text = model()->UpdatePermanentText(
-      controller()->GetToolbarModel()->GetText(true));
+void OmniboxViewGtk::OnTabChanged(const WebContents* web_contents) {
+  security_level_ = controller()->GetToolbarModel()->GetSecurityLevel(false);
+  selected_text_.clear();
 
-  ToolbarModel::SecurityLevel security_level =
-        controller()->GetToolbarModel()->GetSecurityLevel(false);
-  bool changed_security_level = (security_level != security_level_);
-  security_level_ = security_level;
-
-  if (contents) {
-    selected_text_.clear();
-    RevertAll();
-    const AutocompleteEditState* state = static_cast<AutocompleteEditState*>(
-        contents->GetUserData(&kAutocompleteEditStateKey));
-    if (state) {
-      model()->RestoreState(state->model_state);
-
-      // Move the marks for the cursor and the other end of the selection to
-      // the previously-saved offsets (but preserve PRIMARY).
-      StartUpdatingHighlightedText();
-      SetSelectedRange(state->view_state.selection_range);
-      FinishUpdatingHighlightedText();
-    }
-  } else if (visibly_changed_permanent_text) {
-    RevertAll();
-    // TODO(deanm): There should be code to restore select all here.
-  } else if (changed_security_level) {
-    EmphasizeURLComponents();
+  const AutocompleteEditState* state = static_cast<AutocompleteEditState*>(
+      web_contents->GetUserData(&kAutocompleteEditStateKey));
+  model()->RestoreState(state ? &state->model_state : NULL);
+  if (state) {
+    // Move the marks for the cursor and the other end of the selection to the
+    // previously-saved offsets (but preserve PRIMARY).
+    StartUpdatingHighlightedText();
+    SetSelectedRange(state->view_state.selection_range);
+    FinishUpdatingHighlightedText();
   }
 }
 
+void OmniboxViewGtk::Update() {
+  const ToolbarModel::SecurityLevel old_security_level = security_level_;
+  security_level_ = controller()->GetToolbarModel()->GetSecurityLevel(false);
+  if (model()->UpdatePermanentText(
+      controller()->GetToolbarModel()->GetText(true)))
+    RevertAll();
+  else if (old_security_level != security_level_)
+    EmphasizeURLComponents();
+}
+
 string16 OmniboxViewGtk::GetText() const {
   GtkTextIter start, end;
   GetTextBufferBounds(&start, &end);
@@ -1225,35 +1219,37 @@
   gtk_menu_shell_append(GTK_MENU_SHELL(menu), separator);
   gtk_widget_show(separator);
 
-  // Search Engine menu item.
-  GtkWidget* search_engine_menuitem = gtk_menu_item_new_with_mnemonic(
-      ui::ConvertAcceleratorsFromWindowsStyle(
-          l10n_util::GetStringUTF8(IDS_EDIT_SEARCH_ENGINES)).c_str());
-  gtk_menu_shell_append(GTK_MENU_SHELL(menu), search_engine_menuitem);
-  g_signal_connect(search_engine_menuitem, "activate",
-                   G_CALLBACK(HandleEditSearchEnginesThunk), this);
-  gtk_widget_set_sensitive(search_engine_menuitem,
-      command_updater()->IsCommandEnabled(IDC_EDIT_SEARCH_ENGINES));
-  gtk_widget_show(search_engine_menuitem);
-
+  // Paste and Go menu item.
   GtkClipboard* x_clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
   gchar* text = gtk_clipboard_wait_for_text(x_clipboard);
   sanitized_text_for_paste_and_go_ = text ?
       StripJavascriptSchemas(CollapseWhitespace(UTF8ToUTF16(text), true)) :
       string16();
   g_free(text);
+  GtkWidget* paste_and_go_menuitem = gtk_menu_item_new_with_mnemonic(
+      ui::ConvertAcceleratorsFromWindowsStyle(l10n_util::GetStringUTF8(
+          model()->IsPasteAndSearch(sanitized_text_for_paste_and_go_) ?
+              IDS_PASTE_AND_SEARCH : IDS_PASTE_AND_GO)).c_str());
+  // Detect the stock Paste menu item by searching for the stock label
+  // GTK_STOCK_PASTE.  If we don't find it, the Paste and Go item will be
+  // appended at the end of the popup menu.
+  gtk_menu_shell_insert(GTK_MENU_SHELL(menu), paste_and_go_menuitem,
+                        GetPopupMenuIndexForStockLabel(GTK_STOCK_PASTE, menu));
+  g_signal_connect(paste_and_go_menuitem, "activate",
+                   G_CALLBACK(HandlePasteAndGoThunk), this);
+  gtk_widget_set_sensitive(
+      paste_and_go_menuitem,
+      model()->CanPasteAndGo(sanitized_text_for_paste_and_go_));
+  gtk_widget_show(paste_and_go_menuitem);
 
   // Copy URL menu item.
   if (chrome::IsQueryExtractionEnabled()) {
     GtkWidget* copy_url_menuitem = gtk_menu_item_new_with_mnemonic(
         ui::ConvertAcceleratorsFromWindowsStyle(
             l10n_util::GetStringUTF8(IDS_COPY_URL)).c_str());
-
-    // Detect the Paste and Copy menu items by searching for the ones that use
-    // the stock labels (i.e. GTK_STOCK_PASTE and GTK_STOCK_COPY).
-
-    // If we don't find the stock Copy menu item, the Copy URL item will be
-    // appended at the end of the popup menu.
+    // Detect the stock Copy menu item by searching for the stock label
+    // GTK_STOCK_COPY.  If we don't find it, the Copy URL item will be appended
+    // at the end of the popup menu.
     gtk_menu_shell_insert(GTK_MENU_SHELL(menu), copy_url_menuitem,
                           GetPopupMenuIndexForStockLabel(GTK_STOCK_COPY, menu));
     g_signal_connect(copy_url_menuitem, "activate",
@@ -1265,35 +1261,35 @@
     gtk_widget_show(copy_url_menuitem);
   }
 
-  // Paste and Go menu item.
-  GtkWidget* paste_go_menuitem = gtk_menu_item_new_with_mnemonic(
-      ui::ConvertAcceleratorsFromWindowsStyle(l10n_util::GetStringUTF8(
-          model()->IsPasteAndSearch(sanitized_text_for_paste_and_go_) ?
-              IDS_PASTE_AND_SEARCH : IDS_PASTE_AND_GO)).c_str());
-
-  // If we don't find the stock Paste menu item, the Paste and Go item will be
-  // appended at the end of the popup menu.
-  gtk_menu_shell_insert(GTK_MENU_SHELL(menu), paste_go_menuitem,
-                        GetPopupMenuIndexForStockLabel(GTK_STOCK_PASTE, menu));
-
-  g_signal_connect(paste_go_menuitem, "activate",
-                   G_CALLBACK(HandlePasteAndGoThunk), this);
-  gtk_widget_set_sensitive(paste_go_menuitem,
-      model()->CanPasteAndGo(sanitized_text_for_paste_and_go_));
-  gtk_widget_show(paste_go_menuitem);
+  // Edit Search Engines menu item.
+  GtkWidget* edit_search_engines_menuitem = gtk_menu_item_new_with_mnemonic(
+      ui::ConvertAcceleratorsFromWindowsStyle(
+          l10n_util::GetStringUTF8(IDS_EDIT_SEARCH_ENGINES)).c_str());
+  gtk_menu_shell_append(GTK_MENU_SHELL(menu), edit_search_engines_menuitem);
+  g_signal_connect(edit_search_engines_menuitem, "activate",
+                   G_CALLBACK(HandleEditSearchEnginesThunk), this);
+  gtk_widget_set_sensitive(
+      edit_search_engines_menuitem,
+      command_updater()->IsCommandEnabled(IDC_EDIT_SEARCH_ENGINES));
+  gtk_widget_show(edit_search_engines_menuitem);
 
   g_signal_connect(menu, "deactivate",
                    G_CALLBACK(HandlePopupMenuDeactivateThunk), this);
 }
 
-void OmniboxViewGtk::HandleEditSearchEngines(GtkWidget* sender) {
-  command_updater()->ExecuteCommand(IDC_EDIT_SEARCH_ENGINES);
-}
-
 void OmniboxViewGtk::HandlePasteAndGo(GtkWidget* sender) {
   model()->PasteAndGo(sanitized_text_for_paste_and_go_);
 }
 
+void OmniboxViewGtk::HandleCopyURLClipboard(GtkWidget* sender) {
+  DoWriteToClipboard(controller()->GetToolbarModel()->GetURL(),
+                     controller()->GetToolbarModel()->GetText(false));
+}
+
+void OmniboxViewGtk::HandleEditSearchEngines(GtkWidget* sender) {
+  command_updater()->ExecuteCommand(IDC_EDIT_SEARCH_ENGINES);
+}
+
 void OmniboxViewGtk::HandleMarkSet(GtkTextBuffer* buffer,
                                    GtkTextIter* location,
                                    GtkTextMark* mark) {
@@ -1563,11 +1559,6 @@
   HandleCopyOrCutClipboard(true);
 }
 
-void OmniboxViewGtk::HandleCopyURLClipboard(GtkWidget* sender) {
-  DoWriteToClipboard(controller()->GetToolbarModel()->GetURL(),
-                     controller()->GetToolbarModel()->GetText(false));
-}
-
 void OmniboxViewGtk::HandleCutClipboard(GtkWidget* sender) {
   HandleCopyOrCutClipboard(false);
 }
diff --git a/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.h b/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.h
index eff5b7d..d37239c 100644
--- a/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.h
+++ b/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.h
@@ -69,8 +69,8 @@
 
   // OmniboxView:
   virtual void SaveStateToTab(content::WebContents* tab) OVERRIDE;
-  virtual void Update(
-      const content::WebContents* tab_for_state_restoring) OVERRIDE;
+  virtual void OnTabChanged(const content::WebContents* web_contents) OVERRIDE;
+  virtual void Update() OVERRIDE;
   virtual string16 GetText() const OVERRIDE;
   virtual void SetWindowTextAndCaretPos(const string16& text,
                                         size_t caret_pos,
@@ -157,8 +157,9 @@
   CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandleViewSizeRequest,
                        GtkRequisition*);
   CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandlePopulatePopup, GtkMenu*);
-  CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleEditSearchEngines);
   CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandlePasteAndGo);
+  CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleCopyURLClipboard);
+  CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleEditSearchEngines);
   CHROMEGTK_CALLBACK_6(OmniboxViewGtk, void, HandleDragDataReceived,
                        GdkDragContext*, gint, gint, GtkSelectionData*,
                        guint, guint);
@@ -170,7 +171,6 @@
                        GdkDragContext*);
   CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleBackSpace);
   CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleCopyClipboard);
-  CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleCopyURLClipboard);
   CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleCutClipboard);
   CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandlePasteClipboard);
   CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleExposeEvent,
diff --git a/chrome/browser/ui/gtk/one_click_signin_bubble_gtk.cc b/chrome/browser/ui/gtk/one_click_signin_bubble_gtk.cc
index 629b9c7..48234df 100644
--- a/chrome/browser/ui/gtk/one_click_signin_bubble_gtk.cc
+++ b/chrome/browser/ui/gtk/one_click_signin_bubble_gtk.cc
@@ -211,7 +211,7 @@
 
   // The email is always set for the sync dialog.
   header_label_ = theme_provider->BuildLabel(
-      l10n_util::GetStringUTF8(IDS_ONE_CLICK_SIGNIN_DIALOG_TITLE_NEW),
+      l10n_util::GetStringUTF8(IDS_ONE_CLICK_SIGNIN_DIALOG_TITLE),
       ui::kGdkBlack);
 
   PangoAttrList* attributes = pango_attr_list_new();
diff --git a/chrome/browser/ui/gtk/screen_capture_notification_ui_gtk.cc b/chrome/browser/ui/gtk/screen_capture_notification_ui_gtk.cc
index 27d4499..24f5f8e 100644
--- a/chrome/browser/ui/gtk/screen_capture_notification_ui_gtk.cc
+++ b/chrome/browser/ui/gtk/screen_capture_notification_ui_gtk.cc
@@ -39,8 +39,6 @@
 
   base::Closure stop_callback_;
   GtkWidget* window_;
-  GtkWidget* message_;
-  GtkWidget* button_;
 
   // Used to distinguish resize events from other types of "configure-event"
   // notifications.
@@ -115,23 +113,23 @@
 
   std::string button_label =
       l10n_util::GetStringUTF8(IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_STOP);
-  button_ = gtk_button_new_with_label(button_label.c_str());
-  gtk_box_pack_end(GTK_BOX(button_row), button_, FALSE, FALSE, 0);
+  GtkWidget* button = gtk_button_new_with_label(button_label.c_str());
+  gtk_box_pack_end(GTK_BOX(button_row), button, FALSE, FALSE, 0);
 
-  g_signal_connect(button_, "clicked", G_CALLBACK(OnClickedThunk), this);
+  g_signal_connect(button, "clicked", G_CALLBACK(OnClickedThunk), this);
 
-  message_ = gtk_label_new(NULL);
-  gtk_box_pack_end(GTK_BOX(button_row), message_, FALSE, FALSE, 0);
+  GtkWidget* message = gtk_label_new(NULL);
+  gtk_box_pack_end(GTK_BOX(button_row), message, FALSE, FALSE, 0);
 
   // Override any theme setting for the text color, so that the text is
   // readable against the window's background pixmap.
   PangoAttrList* attributes = pango_attr_list_new();
   PangoAttribute* text_color = pango_attr_foreground_new(0, 0, 0);
   pango_attr_list_insert(attributes, text_color);
-  gtk_label_set_attributes(GTK_LABEL(message_), attributes);
+  gtk_label_set_attributes(GTK_LABEL(message), attributes);
   pango_attr_list_unref(attributes);
 
-  gtk_label_set_text(GTK_LABEL(message_), text_.c_str());
+  gtk_label_set_text(GTK_LABEL(message), text_.c_str());
 
   gtk_widget_show_all(window_);
   gtk_window_present(GTK_WINDOW(window_));
diff --git a/chrome/browser/ui/gtk/status_icons/status_icon_gtk.cc b/chrome/browser/ui/gtk/status_icons/status_icon_gtk.cc
index 185f93d..8916cdd 100644
--- a/chrome/browser/ui/gtk/status_icons/status_icon_gtk.cc
+++ b/chrome/browser/ui/gtk/status_icons/status_icon_gtk.cc
@@ -54,7 +54,7 @@
   DispatchClickEvent();
 }
 
-void StatusIconGtk::UpdatePlatformContextMenu(ui::MenuModel* model) {
+void StatusIconGtk::UpdatePlatformContextMenu(StatusIconMenuModel* model) {
   if (!model)
     menu_.reset();
   else
diff --git a/chrome/browser/ui/gtk/status_icons/status_icon_gtk.h b/chrome/browser/ui/gtk/status_icons/status_icon_gtk.h
index b85edde..2e7f010 100644
--- a/chrome/browser/ui/gtk/status_icons/status_icon_gtk.h
+++ b/chrome/browser/ui/gtk/status_icons/status_icon_gtk.h
@@ -32,7 +32,8 @@
 
  protected:
   // Overridden from StatusIcon.
-  virtual void UpdatePlatformContextMenu(ui::MenuModel* menu) OVERRIDE;
+  virtual void UpdatePlatformContextMenu(
+      StatusIconMenuModel* menu) OVERRIDE;
 
  private:
   // Callback invoked when user right-clicks on the status icon.
diff --git a/chrome/browser/ui/gtk/status_icons/status_tray_gtk_unittest.cc b/chrome/browser/ui/gtk/status_icons/status_tray_gtk_unittest.cc
index 3ca1df2..e548db6 100644
--- a/chrome/browser/ui/gtk/status_icons/status_tray_gtk_unittest.cc
+++ b/chrome/browser/ui/gtk/status_icons/status_tray_gtk_unittest.cc
@@ -6,12 +6,12 @@
 
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/status_icons/status_icon_menu_model.h"
 #include "chrome/browser/status_icons/status_icon_observer.h"
 #include "chrome/browser/ui/gtk/status_icons/status_icon_gtk.h"
 #include "grit/chrome_unscaled_resources.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/models/simple_menu_model.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/image/image_skia.h"
 
@@ -35,9 +35,9 @@
   StatusIcon* icon = tray.CreateStatusIcon(
       StatusTray::OTHER_ICON, *image, ASCIIToUTF16("tool tip"));
   icon->SetPressedImage(*image);
-  ui::SimpleMenuModel* menu = new ui::SimpleMenuModel(NULL);
+  scoped_ptr<StatusIconMenuModel> menu(new StatusIconMenuModel(NULL));
   menu->AddItem(0, ASCIIToUTF16("foo"));
-  icon->SetContextMenu(menu);
+  icon->SetContextMenu(menu.Pass());
 }
 
 TEST(StatusTrayGtkTest, ClickOnIcon) {
diff --git a/chrome/browser/ui/libgtk2ui/app_indicator_icon.cc b/chrome/browser/ui/libgtk2ui/app_indicator_icon.cc
index 1e4a7bb..326149e 100644
--- a/chrome/browser/ui/libgtk2ui/app_indicator_icon.cc
+++ b/chrome/browser/ui/libgtk2ui/app_indicator_icon.cc
@@ -140,8 +140,6 @@
 AppIndicatorIcon::~AppIndicatorIcon() {
   if (icon_) {
     app_indicator_set_status(icon_, APP_INDICATOR_STATUS_PASSIVE);
-    if (menu_model_)
-      menu_model_->MenuClosed();
     if (gtk_menu_)
       DestroyMenu();
     g_object_unref(icon_);
@@ -218,6 +216,11 @@
     SetMenu();
 }
 
+void AppIndicatorIcon::RefreshPlatformContextMenu() {
+  gtk_container_foreach(
+      GTK_CONTAINER(gtk_menu_), SetMenuItemInfo, &block_activation_);
+}
+
 void AppIndicatorIcon::SetImageFromFile(base::FilePath icon_file_path) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
   if (!icon_file_path.empty()) {
@@ -268,8 +271,7 @@
                           G_CALLBACK(OnMenuItemActivatedThunk),
                           &block_activation_,
                           this);
-    UpdateMenu();
-    menu_model_->MenuWillShow();
+    RefreshPlatformContextMenu();
   }
   app_indicator_set_menu(icon_, GTK_MENU(gtk_menu_));
 }
@@ -287,8 +289,6 @@
 }
 
 void AppIndicatorIcon::DestroyMenu() {
-  if (menu_model_)
-    menu_model_->MenuClosed();
   gtk_widget_destroy(gtk_menu_);
   gtk_menu_ = NULL;
   menu_model_ = NULL;
@@ -335,11 +335,6 @@
   }
 }
 
-void AppIndicatorIcon::UpdateMenu() {
-  gtk_container_foreach(
-      GTK_CONTAINER(gtk_menu_), SetMenuItemInfo, &block_activation_);
-}
-
 void AppIndicatorIcon::OnClick(GtkWidget* menu_item) {
   if (delegate())
     delegate()->OnClick();
@@ -373,7 +368,6 @@
   // The menu item can still be activated by hotkeys even if it is disabled.
   if (menu_model_->IsEnabledAt(id))
     ExecuteCommand(model, id);
-  UpdateMenu();
 }
 
 }  // namespace libgtk2ui
diff --git a/chrome/browser/ui/libgtk2ui/app_indicator_icon.h b/chrome/browser/ui/libgtk2ui/app_indicator_icon.h
index 5e2e60c..840f579 100644
--- a/chrome/browser/ui/libgtk2ui/app_indicator_icon.h
+++ b/chrome/browser/ui/libgtk2ui/app_indicator_icon.h
@@ -8,6 +8,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/scoped_ptr.h"
 #include "chrome/browser/ui/libgtk2ui/gtk2_signal.h"
+#include "ui/base/models/menu_model.h"
 #include "ui/linux_ui/status_icon_linux.h"
 
 typedef struct _AppIndicator AppIndicator;
@@ -31,14 +32,12 @@
   // Indicates whether libappindicator so could be opened.
   static bool CouldOpen();
 
-  // Overridden from StatusIcon:
+  // Overridden from StatusIconLinux:
   virtual void SetImage(const gfx::ImageSkia& image) OVERRIDE;
   virtual void SetPressedImage(const gfx::ImageSkia& image) OVERRIDE;
   virtual void SetToolTip(const string16& tool_tip) OVERRIDE;
-
- protected:
-  // Overridden from StatusIcon.
   virtual void UpdatePlatformContextMenu(ui::MenuModel* menu) OVERRIDE;
+  virtual void RefreshPlatformContextMenu() OVERRIDE;
 
  private:
   void SetImageFromFile(base::FilePath icon_file_path);
@@ -56,9 +55,6 @@
                                             std::string id);
   static void DeletePath(base::FilePath icon_file_path);
 
-  // Updates all the enabled/checked states and the dynamic labels.
-  void UpdateMenu();
-
   // Callback for when the status icon click replacement menu item is clicked.
   CHROMEGTK_CALLBACK_0(AppIndicatorIcon, void, OnClick);
 
diff --git a/chrome/browser/ui/ntp_background_util.cc b/chrome/browser/ui/ntp_background_util.cc
index 853b23d..d10ea28 100644
--- a/chrome/browser/ui/ntp_background_util.cc
+++ b/chrome/browser/ui/ntp_background_util.cc
@@ -74,16 +74,13 @@
   canvas->FillRect(area, tp->GetColor(ThemeProperties::COLOR_NTP_BACKGROUND));
 
   if (tp->HasCustomImage(IDR_THEME_NTP_BACKGROUND)) {
-    int tiling = ThemeProperties::NO_REPEAT;
-    tp->GetDisplayProperty(ThemeProperties::NTP_BACKGROUND_TILING, &tiling);
-    int alignment;
-    if (tp->GetDisplayProperty(ThemeProperties::NTP_BACKGROUND_ALIGNMENT,
-                               &alignment)) {
-      gfx::ImageSkia* ntp_background =
-          tp->GetImageSkiaNamed(IDR_THEME_NTP_BACKGROUND);
+    int tiling = tp->GetDisplayProperty(ThemeProperties::NTP_BACKGROUND_TILING);
+    int alignment = tp->GetDisplayProperty(
+        ThemeProperties::NTP_BACKGROUND_ALIGNMENT);
+    gfx::ImageSkia* ntp_background =
+        tp->GetImageSkiaNamed(IDR_THEME_NTP_BACKGROUND);
 
-      PaintThemeBackground(
-          canvas, ntp_background, tiling, alignment, area, tab_contents_height);
-    }
+    PaintThemeBackground(
+        canvas, ntp_background, tiling, alignment, area, tab_contents_height);
   }
 }
diff --git a/chrome/browser/ui/omnibox/omnibox_controller.cc b/chrome/browser/ui/omnibox/omnibox_controller.cc
index a15dd14..94ba872 100644
--- a/chrome/browser/ui/omnibox/omnibox_controller.cc
+++ b/chrome/browser/ui/omnibox/omnibox_controller.cc
@@ -19,9 +19,38 @@
 #include "chrome/browser/ui/omnibox/omnibox_edit_model.h"
 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
 #include "chrome/browser/ui/omnibox/omnibox_popup_view.h"
+#include "chrome/browser/ui/search/instant_controller.h"
+#include "chrome/common/instant_types.h"
 #include "extensions/common/constants.h"
 #include "ui/gfx/rect.h"
 
+namespace {
+
+// Returns the AutocompleteMatch that the InstantController should prefetch, if
+// any.
+//
+// The SearchProvider may mark some suggestions to be prefetched based on
+// instructions from the suggest server. If such a match ranks sufficiently
+// highly, we'll return it. We only care about matches that are the default or
+// else the very first entry in the dropdown (which can happen for non-default
+// matches only if we're hiding a top verbatim match); for other matches, we
+// think the likelihood of the user selecting them is low enough that
+// prefetching isn't worth doing.
+const AutocompleteMatch* GetMatchToPrefetch(const AutocompleteResult& result) {
+  const AutocompleteResult::const_iterator default_match(
+      result.default_match());
+  if (default_match == result.end())
+    return NULL;
+
+  if (SearchProvider::ShouldPrefetch(*default_match))
+    return &(*default_match);
+
+  return (result.ShouldHideTopMatch() && (result.size() > 1) &&
+      SearchProvider::ShouldPrefetch(result.match_at(1))) ?
+          &result.match_at(1) : NULL;
+}
+
+}  // namespace
 
 OmniboxController::OmniboxController(OmniboxEditModel* omnibox_edit_model,
                                      Profile* profile)
@@ -68,6 +97,22 @@
       if (!prerender::IsOmniboxEnabled(profile_))
         DoPreconnect(*match);
       omnibox_edit_model_->OnCurrentMatchChanged();
+
+      if (chrome::IsInstantExtendedAPIEnabled() &&
+          omnibox_edit_model_->GetInstantController()) {
+        InstantSuggestion prefetch_suggestion;
+        const AutocompleteMatch* match_to_prefetch = GetMatchToPrefetch(result);
+        if (match_to_prefetch) {
+          prefetch_suggestion.text = match_to_prefetch->contents;
+          prefetch_suggestion.metadata =
+              SearchProvider::GetSuggestMetadata(*match_to_prefetch);
+        }
+        // Send the prefetch suggestion unconditionally to the InstantPage. If
+        // there is no suggestion to prefetch, we need to send a blank query to
+        // clear the prefetched results.
+        omnibox_edit_model_->GetInstantController()->SetSuggestionToPrefetch(
+            prefetch_suggestion);
+      }
     } else {
       InvalidateCurrentMatch();
       popup_->OnResultChanged();
diff --git a/chrome/browser/ui/omnibox/omnibox_edit_model.cc b/chrome/browser/ui/omnibox/omnibox_edit_model.cc
index d8d89b8..6b0241a 100644
--- a/chrome/browser/ui/omnibox/omnibox_edit_model.cc
+++ b/chrome/browser/ui/omnibox/omnibox_edit_model.cc
@@ -215,18 +215,25 @@
                focus_source_);
 }
 
-void OmniboxEditModel::RestoreState(const State& state) {
-  SetFocusState(state.focus_state, OMNIBOX_FOCUS_CHANGE_TAB_SWITCH);
-  focus_source_ = state.focus_source;
+void OmniboxEditModel::RestoreState(const State* state) {
+  // We need to update the permanent text correctly and revert the view
+  // regardless of whether there is saved state.
+  permanent_text_ = controller_->GetToolbarModel()->GetText(true);
+  view_->RevertAll();
+  if (!state)
+    return;
+
+  SetFocusState(state->focus_state, OMNIBOX_FOCUS_CHANGE_TAB_SWITCH);
+  focus_source_ = state->focus_source;
   // Restore any user editing.
-  if (state.user_input_in_progress) {
+  if (state->user_input_in_progress) {
     // NOTE: Be sure and set keyword-related state BEFORE invoking
     // DisplayTextFromUserText(), as its result depends upon this state.
-    keyword_ = state.keyword;
-    is_keyword_hint_ = state.is_keyword_hint;
-    view_->SetUserText(state.user_text,
-        DisplayTextFromUserText(state.user_text), false);
-    view_->SetGrayTextAutocompletion(state.gray_text);
+    keyword_ = state->keyword;
+    is_keyword_hint_ = state->is_keyword_hint;
+    view_->SetUserText(state->user_text,
+        DisplayTextFromUserText(state->user_text), false);
+    view_->SetGrayTextAutocompletion(state->gray_text);
   }
 }
 
@@ -886,7 +893,7 @@
   // stopped.  If the user presses Escape while stopped, we clear it.
   if (delegate_->CurrentPageExists() && !delegate_->IsLoading()) {
     delegate_->GetNavigationController().DiscardNonCommittedEntries();
-    view_->Update(NULL);
+    view_->Update();
   }
 
   // If the user wasn't editing, but merely had focus in the edit, allow <esc>
@@ -1313,14 +1320,12 @@
   if (url == content::kAboutBlankURL)
     return AutocompleteInput::BLANK;
   if (url == profile()->GetPrefs()->GetString(prefs::kHomePage))
-    return AutocompleteInput::HOMEPAGE;
+    return AutocompleteInput::HOME_PAGE;
   if (controller_->GetToolbarModel()->WouldReplaceSearchURLWithSearchTerms(
-      true)) {
+      true))
     return AutocompleteInput::SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT;
-  }
-  if (delegate_->IsSearchResultsPage()) {
+  if (delegate_->IsSearchResultsPage())
     return AutocompleteInput::SEARCH_RESULT_PAGE_NO_SEARCH_TERM_REPLACEMENT;
-  }
   return AutocompleteInput::OTHER;
 }
 
diff --git a/chrome/browser/ui/omnibox/omnibox_edit_model.h b/chrome/browser/ui/omnibox/omnibox_edit_model.h
index afc9be7..7599f6b 100644
--- a/chrome/browser/ui/omnibox/omnibox_edit_model.h
+++ b/chrome/browser/ui/omnibox/omnibox_edit_model.h
@@ -99,8 +99,9 @@
   // the internal state appropriately.
   const State GetStateForTabSwitch();
 
-  // Restores local state from the saved |state|.
-  void RestoreState(const State& state);
+  // Resets the tab state, then restores local state from the saved |state|.
+  // |state| may be NULL if there is no saved state.
+  void RestoreState(const State* state);
 
   // Returns the match for the current text. If the user has not edited the text
   // this is the match corresponding to the permanent text. Returns the
diff --git a/chrome/browser/ui/omnibox/omnibox_edit_unittest.cc b/chrome/browser/ui/omnibox/omnibox_edit_unittest.cc
index 2b4be29..4dd7d09 100644
--- a/chrome/browser/ui/omnibox/omnibox_edit_unittest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_edit_unittest.cc
@@ -25,7 +25,8 @@
       : OmniboxView(NULL, controller, NULL) {}
 
   virtual void SaveStateToTab(WebContents* tab) OVERRIDE {}
-  virtual void Update(const WebContents* tab_for_state_restoring) OVERRIDE {}
+  virtual void OnTabChanged(const WebContents* web_contents) OVERRIDE {}
+  virtual void Update() OVERRIDE {}
   virtual void OpenMatch(const AutocompleteMatch& match,
                          WindowOpenDisposition disposition,
                          const GURL& alternate_nav_url,
diff --git a/chrome/browser/ui/omnibox/omnibox_view.cc b/chrome/browser/ui/omnibox/omnibox_view.cc
index 883ec68..2adaa74 100644
--- a/chrome/browser/ui/omnibox/omnibox_view.cc
+++ b/chrome/browser/ui/omnibox/omnibox_view.cc
@@ -96,13 +96,10 @@
 }
 
 int OmniboxView::GetIcon() const {
-  if (IsEditingOrEmpty()) {
-    return AutocompleteMatch::TypeToLocationBarIcon(model_.get() ?
-          model_->CurrentTextType() :
-              AutocompleteMatchType::URL_WHAT_YOU_TYPED);
-  } else {
+  if (!IsEditingOrEmpty())
     return controller_->GetToolbarModel()->GetIcon();
-  }
+  return AutocompleteMatch::TypeToLocationBarIcon(model_.get() ?
+        model_->CurrentTextType() : AutocompleteMatchType::URL_WHAT_YOU_TYPED);
 }
 
 void OmniboxView::SetUserText(const string16& text) {
diff --git a/chrome/browser/ui/omnibox/omnibox_view.h b/chrome/browser/ui/omnibox/omnibox_view.h
index 62c4c63..825786d 100644
--- a/chrome/browser/ui/omnibox/omnibox_view.h
+++ b/chrome/browser/ui/omnibox/omnibox_view.h
@@ -63,10 +63,11 @@
   // that it can be restored during a later call to Update().
   virtual void SaveStateToTab(content::WebContents* tab) = 0;
 
-  // Called when any LocationBarView state changes. If
-  // |tab_for_state_restoring| is non-NULL, it points to a WebContents whose
-  // state we should restore.
-  virtual void Update(const content::WebContents* tab_for_state_restoring) = 0;
+  // Called when the window's active tab changes.
+  virtual void OnTabChanged(const content::WebContents* web_contents) = 0;
+
+  // Called when any relevant state changes other than changing tabs.
+  virtual void Update() = 0;
 
   // Asks the browser to load the specified match's |destination_url|, which
   // is assumed to be one of the popup entries, using the supplied disposition
diff --git a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
index 353f5ae..b208b1e 100644
--- a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
@@ -168,9 +168,9 @@
       OmniboxView** omnibox_view) {
     BrowserWindow* window = browser->window();
     ASSERT_TRUE(window);
-    LocationBar* loc_bar = window->GetLocationBar();
-    ASSERT_TRUE(loc_bar);
-    *omnibox_view = loc_bar->GetLocationEntry();
+    LocationBar* location_bar = window->GetLocationBar();
+    ASSERT_TRUE(location_bar);
+    *omnibox_view = location_bar->GetLocationEntry();
     ASSERT_TRUE(*omnibox_view);
   }
 
diff --git a/chrome/browser/ui/panels/display_settings_provider.cc b/chrome/browser/ui/panels/display_settings_provider.cc
index adf5ff2..42d76cc 100644
--- a/chrome/browser/ui/panels/display_settings_provider.cc
+++ b/chrome/browser/ui/panels/display_settings_provider.cc
@@ -43,9 +43,10 @@
 void DisplaySettingsProvider::AddFullScreenObserver(
     FullScreenObserver* observer) {
   is_full_screen_ = IsFullScreen();
+  bool already_started = full_screen_observers_.might_have_observers();
   full_screen_observers_.AddObserver(observer);
 
-  if (full_screen_observers_.size() == 1 && NeedsPeriodicFullScreenCheck()) {
+  if (!already_started && NeedsPeriodicFullScreenCheck()) {
     full_screen_mode_timer_.Start(FROM_HERE,
         base::TimeDelta::FromMilliseconds(kFullScreenModeCheckIntervalMs),
         base::Bind(&DisplaySettingsProvider::CheckFullScreenMode,
@@ -58,7 +59,7 @@
     FullScreenObserver* observer) {
   full_screen_observers_.RemoveObserver(observer);
 
-  if (full_screen_observers_.size() == 0)
+  if (!full_screen_observers_.might_have_observers())
     full_screen_mode_timer_.Stop();
 }
 
diff --git a/chrome/browser/ui/panels/panel_mouse_watcher.cc b/chrome/browser/ui/panels/panel_mouse_watcher.cc
index b55ca76..1774658 100644
--- a/chrome/browser/ui/panels/panel_mouse_watcher.cc
+++ b/chrome/browser/ui/panels/panel_mouse_watcher.cc
@@ -14,15 +14,16 @@
 }
 
 void PanelMouseWatcher::AddObserver(PanelMouseWatcherObserver* observer) {
+  bool already_started = observers_.might_have_observers();
   observers_.AddObserver(observer);
-  if (observers_.size() == 1)
+  if (!already_started)
     Start();
 }
 
 void PanelMouseWatcher::RemoveObserver(PanelMouseWatcherObserver* observer) {
   DCHECK(observers_.HasObserver(observer));
   observers_.RemoveObserver(observer);
-  if (observers_.size() == 0)
+  if (!observers_.might_have_observers())
     Stop();
 }
 
diff --git a/chrome/browser/ui/search/instant_controller.cc b/chrome/browser/ui/search/instant_controller.cc
index 3b42999..2c43c22 100644
--- a/chrome/browser/ui/search/instant_controller.cc
+++ b/chrome/browser/ui/search/instant_controller.cc
@@ -122,6 +122,12 @@
     instant_tab_->sender()->SetOmniboxBounds(omnibox_bounds_);
 }
 
+void InstantController::SetSuggestionToPrefetch(
+    const InstantSuggestion& suggestion) {
+  if (instant_tab_ && search_mode_.is_search())
+    instant_tab_->sender()->SetSuggestionToPrefetch(suggestion);
+}
+
 void InstantController::ToggleVoiceSearch() {
   if (instant_tab_)
     instant_tab_->sender()->ToggleVoiceSearch();
diff --git a/chrome/browser/ui/search/instant_controller.h b/chrome/browser/ui/search/instant_controller.h
index 412a9b3..d088111 100644
--- a/chrome/browser/ui/search/instant_controller.h
+++ b/chrome/browser/ui/search/instant_controller.h
@@ -58,6 +58,9 @@
   // Sets the stored start-edge margin and width of the omnibox.
   void SetOmniboxBounds(const gfx::Rect& bounds);
 
+  // Sends the current SearchProvider suggestion to the Instant page if any.
+  void SetSuggestionToPrefetch(const InstantSuggestion& suggestion);
+
   // Notifies |instant_Tab_| to toggle voice search.
   void ToggleVoiceSearch();
 
diff --git a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
index dd6544b..c69e17c 100644
--- a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
+++ b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/favicon/favicon_tab_helper.h"
+#include "chrome/browser/google/google_url_tracker.h"
 #include "chrome/browser/history/history_db_task.h"
 #include "chrome/browser/history/history_service.h"
 #include "chrome/browser/history/history_service_factory.h"
@@ -74,6 +75,8 @@
 #include "content/public/test/test_utils.h"
 #include "grit/generated_resources.h"
 #include "net/base/network_change_notifier.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_fetcher_impl.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -155,7 +158,7 @@
     ASSERT_TRUE(https_test_server().Start());
     GURL instant_url = https_test_server().GetURL(
         "files/instant_extended.html?strk=1&");
-    InstantTestBase::Init(instant_url);
+    InstantTestBase::Init(instant_url, false);
   }
 
   int64 GetHistogramCount(const char* name) {
@@ -209,7 +212,9 @@
            GetBoolFromJS(contents, "isFocused",
                          &is_focused_) &&
            GetIntFromJS(contents, "onToggleVoiceSearchCalls",
-                        &on_toggle_voice_search_calls_);
+                        &on_toggle_voice_search_calls_) &&
+           GetStringFromJS(contents, "prefetchQuery", &prefetch_query_value_);
+
   }
 
   TemplateURL* GetDefaultSearchProviderTemplateURL() {
@@ -264,6 +269,34 @@
   int on_focus_changed_calls_;
   bool is_focused_;
   int on_toggle_voice_search_calls_;
+  std::string prefetch_query_value_;
+};
+
+class InstantExtendedPrefetchTest : public InstantExtendedTest {
+ public:
+  InstantExtendedPrefetchTest()
+      : factory_(new net::URLFetcherImplFactory()),
+        fake_factory_(new net::FakeURLFetcherFactory(factory_.get())) {
+  }
+
+  virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
+    chrome::EnableInstantExtendedAPIForTesting();
+    ASSERT_TRUE(https_test_server().Start());
+    GURL instant_url = https_test_server().GetURL(
+        "files/instant_extended.html?strk=1&");
+    InstantTestBase::Init(instant_url, true);
+  }
+
+  net::FakeURLFetcherFactory* fake_factory() { return fake_factory_.get(); }
+
+ private:
+  // Used to instantiate FakeURLFetcherFactory.
+  scoped_ptr<net::URLFetcherImplFactory> factory_;
+
+  // Used to mock default search provider suggest response.
+  scoped_ptr<net::FakeURLFetcherFactory> fake_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(InstantExtendedPrefetchTest);
 };
 
 class InstantExtendedNetworkTest : public InstantExtendedTest {
@@ -302,7 +335,7 @@
     ASSERT_TRUE(https_test_server().Start());
     GURL instant_url = https_test_server().GetURL(
         "files/instant_extended.html?strk=1&");
-    InstantTestBase::Init(instant_url);
+    InstantTestBase::Init(instant_url, false);
   }
 
   void InstallThemeSource() {
@@ -1653,3 +1686,93 @@
   // Make sure the URL remains the same.
   EXPECT_EQ(ntp_url, ntp_contents->GetURL());
 }
+
+IN_PROC_BROWSER_TEST_F(InstantExtendedPrefetchTest, SetPrefetchQuery) {
+  ASSERT_NO_FATAL_FAILURE(SetupInstant(browser()));
+  FocusOmniboxAndWaitForInstantNTPSupport();
+
+  content::WindowedNotificationObserver new_tab_observer(
+      content::NOTIFICATION_NAV_ENTRY_COMMITTED,
+      content::NotificationService::AllSources());
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(),
+      GURL(chrome::kChromeUINewTabURL),
+      CURRENT_TAB,
+      ui_test_utils::BROWSER_TEST_NONE);
+  new_tab_observer.Wait();
+
+  omnibox()->model()->autocomplete_controller()->search_provider()->
+      kMinimumTimeBetweenSuggestQueriesMs = 0;
+
+  // Set the fake response for suggest request. Response has prefetch details.
+  // Ensure that the page received the prefetch query.
+  fake_factory()->SetFakeResponse(
+      instant_url().spec() + "#q=pupp",
+      "[\"pupp\",[\"puppy\", \"puppies\"],[],[],"
+      "{\"google:clientdata\":{\"phi\": 0},"
+          "\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
+          "\"google:suggestrelevance\":[1400, 9]}]",
+      true);
+
+  SetOmniboxText("pupp");
+  while (!omnibox()->model()->autocomplete_controller()->done()) {
+    content::WindowedNotificationObserver ready_observer(
+        chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY,
+        content::Source<AutocompleteController>(
+            omnibox()->model()->autocomplete_controller()));
+    ready_observer.Wait();
+  }
+
+  ASSERT_EQ(3, CountSearchProviderSuggestions());
+  content::WebContents* active_tab =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(UpdateSearchState(active_tab));
+  ASSERT_TRUE(SearchProvider::ShouldPrefetch(*(
+      omnibox()->model()->result().default_match())));
+  ASSERT_EQ("puppy", prefetch_query_value_);
+}
+
+IN_PROC_BROWSER_TEST_F(InstantExtendedPrefetchTest, ClearPrefetchedResults) {
+  ASSERT_NO_FATAL_FAILURE(SetupInstant(browser()));
+  FocusOmniboxAndWaitForInstantNTPSupport();
+
+  content::WindowedNotificationObserver new_tab_observer(
+      content::NOTIFICATION_NAV_ENTRY_COMMITTED,
+      content::NotificationService::AllSources());
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(),
+      GURL(chrome::kChromeUINewTabURL),
+      CURRENT_TAB,
+      ui_test_utils::BROWSER_TEST_NONE);
+  new_tab_observer.Wait();
+
+  omnibox()->model()->autocomplete_controller()->search_provider()->
+      kMinimumTimeBetweenSuggestQueriesMs = 0;
+
+  // Set the fake response for suggest request. Response has no prefetch
+  // details. Ensure that the page received a blank query to clear the
+  // prefetched results.
+  fake_factory()->SetFakeResponse(
+      instant_url().spec() + "#q=dogs",
+      "[\"dogs\",[\"https://dogs.com\"],[],[],"
+          "{\"google:suggesttype\":[\"NAVIGATION\"],"
+          "\"google:suggestrelevance\":[2]}]",
+      true);
+
+  SetOmniboxText("dogs");
+  while (!omnibox()->model()->autocomplete_controller()->done()) {
+    content::WindowedNotificationObserver ready_observer(
+        chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY,
+        content::Source<AutocompleteController>(
+            omnibox()->model()->autocomplete_controller()));
+    ready_observer.Wait();
+  }
+
+  ASSERT_EQ(2, CountSearchProviderSuggestions());
+  ASSERT_FALSE(SearchProvider::ShouldPrefetch(*(
+      omnibox()->model()->result().default_match())));
+  content::WebContents* active_tab =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(UpdateSearchState(active_tab));
+  ASSERT_EQ("", prefetch_query_value_);
+}
diff --git a/chrome/browser/ui/search/instant_ipc_sender.cc b/chrome/browser/ui/search/instant_ipc_sender.cc
index 62387e6..3744743 100644
--- a/chrome/browser/ui/search/instant_ipc_sender.cc
+++ b/chrome/browser/ui/search/instant_ipc_sender.cc
@@ -35,6 +35,12 @@
         routing_id(), is_app_launcher_enabled));
   }
 
+  virtual void SetSuggestionToPrefetch(
+      const InstantSuggestion& suggestion) OVERRIDE {
+    Send(new ChromeViewMsg_SearchBoxSetSuggestionToPrefetch(routing_id(),
+                                                            suggestion));
+  }
+
   virtual void SendThemeBackgroundInfo(
       const ThemeBackgroundInfo& theme_info) OVERRIDE {
     Send(new ChromeViewMsg_SearchBoxThemeChanged(routing_id(), theme_info));
diff --git a/chrome/browser/ui/search/instant_ipc_sender.h b/chrome/browser/ui/search/instant_ipc_sender.h
index d9e4cd8..af43445 100644
--- a/chrome/browser/ui/search/instant_ipc_sender.h
+++ b/chrome/browser/ui/search/instant_ipc_sender.h
@@ -45,6 +45,9 @@
   // Tells the page information it needs to display promos.
   virtual void SetPromoInformation(bool is_app_launcher_enabled) {}
 
+  // Tells the page the suggestion to be prefetched if any.
+  virtual void SetSuggestionToPrefetch(const InstantSuggestion& suggestion) {}
+
   // Tells the page about the current theme background.
   virtual void SendThemeBackgroundInfo(
       const ThemeBackgroundInfo& theme_info) {}
diff --git a/chrome/browser/ui/search/instant_test_utils.cc b/chrome/browser/ui/search/instant_test_utils.cc
index 50eb25c..67d1d19 100644
--- a/chrome/browser/ui/search/instant_test_utils.cc
+++ b/chrome/browser/ui/search/instant_test_utils.cc
@@ -45,9 +45,12 @@
   TemplateURLData data;
   // Necessary to use exact URL for both the main URL and the alternate URL for
   // search term extraction to work in InstantExtended.
+  data.short_name = ASCIIToUTF16("name");
   data.SetURL(instant_url_.spec() +
               "q={searchTerms}&is_search&{google:omniboxStartMarginParameter}");
   data.instant_url = instant_url_.spec();
+  if (init_suggestions_url_)
+    data.suggestions_url = instant_url_.spec() + "#q={searchTerms}";
   data.alternate_urls.push_back(instant_url_.spec() + "#q={searchTerms}");
   data.search_terms_replacement_key = "strk";
 
@@ -67,6 +70,7 @@
   ui_test_utils::WaitForTemplateURLServiceToLoad(service);
 
   TemplateURLData data;
+  data.short_name = ASCIIToUTF16("name");
   data.SetURL(url);
   data.instant_url = url;
 
@@ -75,8 +79,9 @@
   service->SetDefaultSearchProvider(template_url);
 }
 
-void InstantTestBase::Init(const GURL& instant_url) {
+void InstantTestBase::Init(const GURL& instant_url, bool init_suggestions_url) {
   instant_url_ = instant_url;
+  init_suggestions_url_ = init_suggestions_url;
 }
 
 void InstantTestBase::FocusOmnibox() {
diff --git a/chrome/browser/ui/search/instant_test_utils.h b/chrome/browser/ui/search/instant_test_utils.h
index 47c273d..c232b7d 100644
--- a/chrome/browser/ui/search/instant_test_utils.h
+++ b/chrome/browser/ui/search/instant_test_utils.h
@@ -37,13 +37,14 @@
       : https_test_server_(
             net::SpawnedTestServer::TYPE_HTTPS,
             net::BaseTestServer::SSLOptions(),
-            base::FilePath(FILE_PATH_LITERAL("chrome/test/data"))) {
+            base::FilePath(FILE_PATH_LITERAL("chrome/test/data"))),
+        init_suggestions_url_(false) {
   }
   virtual ~InstantTestBase() {}
 
  protected:
   void SetupInstant(Browser* browser);
-  void Init(const GURL& instant_url);
+  void Init(const GURL& instant_url, bool init_suggestions_url);
 
   void SetInstantURL(const std::string& url);
 
@@ -109,6 +110,9 @@
   // HTTPS Testing server, started on demand.
   net::SpawnedTestServer https_test_server_;
 
+  // Set to true to initialize suggestions URL in default search provider.
+  bool init_suggestions_url_;
+
   DISALLOW_COPY_AND_ASSIGN(InstantTestBase);
 };
 
diff --git a/chrome/browser/ui/search/local_ntp_browsertest.cc b/chrome/browser/ui/search/local_ntp_browsertest.cc
index da42ae6..19cf564 100644
--- a/chrome/browser/ui/search/local_ntp_browsertest.cc
+++ b/chrome/browser/ui/search/local_ntp_browsertest.cc
@@ -26,7 +26,7 @@
     ASSERT_TRUE(https_test_server().Start());
     GURL instant_url = https_test_server().GetURL(
         "files/local_ntp_browsertest.html?strk=1&");
-    InstantTestBase::Init(instant_url);
+    InstantTestBase::Init(instant_url, false);
   }
 };
 
diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc
index 077e7f6..7eb3791 100644
--- a/chrome/browser/ui/search/search_tab_helper.cc
+++ b/chrome/browser/ui/search/search_tab_helper.cc
@@ -152,6 +152,17 @@
   if (!load_details->is_main_frame)
     return;
 
+  // TODO(kmadhusu): Set the page initial states (such as omnibox margin, etc)
+  // from here. Please refer to crbug.com/247517 for more details.
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents_->GetBrowserContext());
+  if (chrome::ShouldAssignURLToInstantRenderer(web_contents_->GetURL(),
+                                               profile)) {
+    Send(new ChromeViewMsg_SearchBoxSetDisplayInstantResults(
+         routing_id(),
+         chrome::ShouldPrefetchSearchResultsOnSRP()));
+  }
+
   UpdateMode(true, false);
 
   content::NavigationEntry* entry =
diff --git a/chrome/browser/ui/sync/one_click_signin_helper.cc b/chrome/browser/ui/sync/one_click_signin_helper.cc
index 2cea5e4..3586f20 100644
--- a/chrome/browser/ui/sync/one_click_signin_helper.cc
+++ b/chrome/browser/ui/sync/one_click_signin_helper.cc
@@ -147,6 +147,107 @@
 }
 
 
+// ConfirmEmailDialogDelegate -------------------------------------------------
+
+class ConfirmEmailDialogDelegate : public TabModalConfirmDialogDelegate {
+ public:
+  enum Action {
+    CREATE_NEW_USER,
+    START_SYNC,
+    CLOSE
+  };
+
+  // Callback indicating action performed by the user.
+  typedef base::Callback<void(Action)> Callback;
+
+  // Ask the user for confirmation before starting to sync.
+  static void AskForConfirmation(content::WebContents* contents,
+                                 const std::string& last_email,
+                                 const std::string& email,
+                                 Callback callback);
+
+ private:
+  ConfirmEmailDialogDelegate(content::WebContents* contents,
+                             const std::string& last_email,
+                             const std::string& email,
+                             Callback callback);
+  virtual ~ConfirmEmailDialogDelegate();
+
+  // TabModalConfirmDialogDelegate:
+  virtual string16 GetTitle() OVERRIDE;
+  virtual string16 GetMessage() OVERRIDE;
+  virtual string16 GetAcceptButtonTitle() OVERRIDE;
+  virtual string16 GetCancelButtonTitle() OVERRIDE;
+  virtual void OnAccepted() OVERRIDE;
+  virtual void OnCanceled() OVERRIDE;
+  virtual void OnClosed() OVERRIDE;
+
+  std::string last_email_;
+  std::string email_;
+  Callback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConfirmEmailDialogDelegate);
+};
+
+// static
+void ConfirmEmailDialogDelegate::AskForConfirmation(
+    content::WebContents* contents,
+    const std::string& last_email,
+    const std::string& email,
+    Callback callback) {
+  TabModalConfirmDialog::Create(
+      new ConfirmEmailDialogDelegate(contents, last_email, email,
+                                     callback), contents);
+}
+
+ConfirmEmailDialogDelegate::ConfirmEmailDialogDelegate(
+    content::WebContents* contents,
+    const std::string& last_email,
+    const std::string& email,
+    Callback callback)
+  : TabModalConfirmDialogDelegate(contents),
+    last_email_(last_email),
+    email_(email),
+    callback_(callback) {
+}
+
+ConfirmEmailDialogDelegate::~ConfirmEmailDialogDelegate() {
+}
+
+string16 ConfirmEmailDialogDelegate::GetTitle() {
+  return l10n_util::GetStringUTF16(
+      IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_TITLE);
+}
+
+string16 ConfirmEmailDialogDelegate::GetMessage() {
+  return l10n_util::GetStringFUTF16(
+      IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_MESSAGE,
+      UTF8ToUTF16(last_email_), UTF8ToUTF16(email_));
+}
+
+string16 ConfirmEmailDialogDelegate::GetAcceptButtonTitle() {
+  return l10n_util::GetStringUTF16(
+      IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_OK_BUTTON);
+}
+
+string16 ConfirmEmailDialogDelegate::GetCancelButtonTitle() {
+  return l10n_util::GetStringUTF16(
+      IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_CANCEL_BUTTON);
+}
+
+void ConfirmEmailDialogDelegate::OnAccepted() {
+  base::ResetAndReturn(&callback_).Run(CREATE_NEW_USER);
+}
+
+void ConfirmEmailDialogDelegate::OnCanceled() {
+  base::ResetAndReturn(&callback_).Run(START_SYNC);
+}
+
+void ConfirmEmailDialogDelegate::OnClosed() {
+  base::ResetAndReturn(&callback_).Run(CLOSE);
+}
+
+
 // Helpers --------------------------------------------------------------------
 
 // Add a specific email to the list of emails rejected for one-click
@@ -279,16 +380,17 @@
 void StartExplicitSync(const StartSyncArgs& args,
                        content::WebContents* contents,
                        OneClickSigninSyncStarter::StartSyncMode start_mode,
-                       int button) {
-  if (button == IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_OK_BUTTON) {
-    contents->GetController().LoadURL(
-        GURL(chrome::kChromeUINewTabURL), content::Referrer(),
-        content::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
-    chrome::ShowSettingsSubPage(args.browser,
-                                std::string(chrome::kSearchUsersSubPage));
-  } else {
+                       ConfirmEmailDialogDelegate::Action action) {
+  if (action == ConfirmEmailDialogDelegate::START_SYNC) {
     StartSync(args, start_mode);
     RedirectToNtpOrAppsPageIfNecessary(contents, args.source);
+  } else {
+    if (signin::IsContinueUrlForWebBasedSigninFlow(contents->GetVisibleURL()))
+      RedirectToNtpOrAppsPage(contents, args.source);
+    if (action == ConfirmEmailDialogDelegate::CREATE_NEW_USER) {
+      chrome::ShowSettingsSubPage(args.browser,
+                                  std::string(chrome::kSearchUsersSubPage));
+    }
   }
 }
 
@@ -380,100 +482,6 @@
        !email.empty());
 }
 
-
-// ConfirmEmailDialogDelegate -------------------------------------------------
-
-class ConfirmEmailDialogDelegate : public TabModalConfirmDialogDelegate {
- public:
-  // Callback indicating action performed by the user.  The argument to the
-  // callback is the ID of the button pressed by the user.
-  typedef base::Callback<void(int)> Callback;
-
-  // Ask the user for confirmation before starting to sync.
-  static void AskForConfirmation(content::WebContents* contents,
-                                 const std::string& last_email,
-                                 const std::string& email,
-                                 Callback callback);
-
- private:
-  ConfirmEmailDialogDelegate(content::WebContents* contents,
-                             const std::string& last_email,
-                             const std::string& email,
-                             Callback callback);
-  virtual ~ConfirmEmailDialogDelegate();
-
-  // TabModalConfirmDialogDelegate:
-  virtual string16 GetTitle() OVERRIDE;
-  virtual string16 GetMessage() OVERRIDE;
-  virtual string16 GetAcceptButtonTitle() OVERRIDE;
-  virtual string16 GetCancelButtonTitle() OVERRIDE;
-  virtual void OnAccepted() OVERRIDE;
-  virtual void OnCanceled() OVERRIDE;
-
-  std::string last_email_;
-  std::string email_;
-  Callback callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(ConfirmEmailDialogDelegate);
-};
-
-// static
-void ConfirmEmailDialogDelegate::AskForConfirmation(
-    content::WebContents* contents,
-    const std::string& last_email,
-    const std::string& email,
-    Callback callback) {
-  TabModalConfirmDialog::Create(
-      new ConfirmEmailDialogDelegate(contents, last_email, email,
-                                     callback), contents);
-}
-
-ConfirmEmailDialogDelegate::ConfirmEmailDialogDelegate(
-    content::WebContents* contents,
-    const std::string& last_email,
-    const std::string& email,
-    Callback callback)
-  : TabModalConfirmDialogDelegate(contents),
-    last_email_(last_email),
-    email_(email),
-    callback_(callback) {
-}
-
-ConfirmEmailDialogDelegate::~ConfirmEmailDialogDelegate() {
-}
-
-string16 ConfirmEmailDialogDelegate::GetTitle() {
-  return l10n_util::GetStringUTF16(
-      IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_TITLE);
-}
-
-string16 ConfirmEmailDialogDelegate::GetMessage() {
-  return l10n_util::GetStringFUTF16(
-      IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_MESSAGE,
-      UTF8ToUTF16(last_email_), UTF8ToUTF16(email_));
-}
-
-string16 ConfirmEmailDialogDelegate::GetAcceptButtonTitle() {
-  return l10n_util::GetStringUTF16(
-      IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_OK_BUTTON);
-}
-
-string16 ConfirmEmailDialogDelegate::GetCancelButtonTitle() {
-  return l10n_util::GetStringUTF16(
-      IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_CANCEL_BUTTON);
-}
-
-void ConfirmEmailDialogDelegate::OnAccepted() {
-  base::ResetAndReturn(&callback_).Run(
-      IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_OK_BUTTON);
-}
-
-void ConfirmEmailDialogDelegate::OnCanceled() {
-  base::ResetAndReturn(&callback_).Run(
-      IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_CANCEL_BUTTON);
-}
-
-
 // CurrentHistoryCleaner ------------------------------------------------------
 
 // Watch a webcontents and remove URL from the history once loading is complete.
@@ -586,15 +594,9 @@
 }
 
 OneClickSigninHelper::~OneClickSigninHelper() {
-  content::WebContents* contents = web_contents();
-  if (contents) {
-    Profile* profile =
-        Profile::FromBrowserContext(contents->GetBrowserContext());
-    ProfileSyncService* sync_service =
-        ProfileSyncServiceFactory::GetForProfile(profile);
-    if (sync_service && sync_service->HasObserver(this))
-      sync_service->RemoveObserver(this);
-  }
+  // WebContentsDestroyed() should always be called before the object is
+  // deleted.
+  DCHECK(!web_contents());
 }
 
 // static
@@ -1270,8 +1272,8 @@
 
         // No need to display a second confirmation so pass false below.
         // TODO(atwilson): Move this into OneClickSigninSyncStarter.
-        // If |contents| is deleted before the callback execution,
-        // the tab modal dialog is closed and the callback is never executed.
+        // The tab modal dialog always executes its callback before |contents|
+        // is deleted.
         ConfirmEmailDialogDelegate::AskForConfirmation(
             contents,
             last_email,
@@ -1325,6 +1327,17 @@
   CleanTransientState();
 }
 
+// It is guaranteed that this method is called before the object is deleted.
+void OneClickSigninHelper::WebContentsDestroyed(
+    content::WebContents* contents) {
+  Profile* profile =
+      Profile::FromBrowserContext(contents->GetBrowserContext());
+  ProfileSyncService* sync_service =
+      ProfileSyncServiceFactory::GetForProfile(profile);
+  if (sync_service)
+    sync_service->RemoveObserver(this);
+}
+
 void OneClickSigninHelper::OnStateChanged() {
   // We only add observer for ProfileSyncService when original_continue_url_ is
   // not empty.
diff --git a/chrome/browser/ui/sync/one_click_signin_helper.h b/chrome/browser/ui/sync/one_click_signin_helper.h
index da9a138..337b282 100644
--- a/chrome/browser/ui/sync/one_click_signin_helper.h
+++ b/chrome/browser/ui/sync/one_click_signin_helper.h
@@ -83,8 +83,6 @@
       content::WebContents* contents,
       PasswordManager* password_manager);
 
-  virtual ~OneClickSigninHelper();
-
   // Returns true if the one-click signin feature can be offered at this time.
   // If |email| is not empty, then the profile is checked to see if it's
   // already connected to a google account or if the user has already rejected
@@ -140,6 +138,8 @@
   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest, SigninFailed);
   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest,
                            CleanTransientStateOnNavigate);
+  FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest,
+                           RemoveObserverFromProfileSyncService);
   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest, CanOfferOnIOThread);
   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
                            CanOfferOnIOThreadIncognito);
@@ -174,6 +174,8 @@
   OneClickSigninHelper(content::WebContents* web_contents,
                        PasswordManager* password_manager);
 
+  virtual ~OneClickSigninHelper();
+
   // Returns true if the one-click signin feature can be offered at this time.
   // It can be offered if the io_data is not in an incognito window and if the
   // origin of |url| is a valid Gaia sign in origin.  This function is meant
@@ -226,6 +228,7 @@
       const content::FrameNavigateParams& params) OVERRIDE;
   virtual void DidStopLoading(
       content::RenderViewHost* render_view_host) OVERRIDE;
+  virtual void WebContentsDestroyed(content::WebContents* contents) OVERRIDE;
 
   // ProfileSyncServiceObserver.
   virtual void OnStateChanged() OVERRIDE;
diff --git a/chrome/browser/ui/sync/one_click_signin_helper_unittest.cc b/chrome/browser/ui/sync/one_click_signin_helper_unittest.cc
index d8ceb51..a0b1d03 100644
--- a/chrome/browser/ui/sync/one_click_signin_helper_unittest.cc
+++ b/chrome/browser/ui/sync/one_click_signin_helper_unittest.cc
@@ -302,17 +302,13 @@
       ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
           profile(),
           ProfileSyncServiceMock::BuildMockProfileSyncService));
-  EXPECT_CALL(*sync_service, SetSetupInProgress(true));
-  EXPECT_CALL(*sync_service, AddObserver(_)).Times(AtLeast(1));
   EXPECT_CALL(*sync_service, FirstSetupInProgress()).WillRepeatedly(
       Return(false));
   EXPECT_CALL(*sync_service, sync_initialized()).WillRepeatedly(Return(true));
-  EXPECT_CALL(*sync_service, RemoveObserver(_)).Times(AtLeast(1));
   EXPECT_CALL(*sync_service, GetAuthError()).
       WillRepeatedly(::testing::ReturnRef(no_error_));
   EXPECT_CALL(*sync_service, sync_initialized()).WillRepeatedly(Return(false));
   sync_service->Initialize();
-  EXPECT_CALL(*sync_service, sync_initialized()).WillRepeatedly(Return(true));
   return sync_service;
 }
 
@@ -657,7 +653,11 @@
   EXPECT_CALL(*signin_manager_, IsAllowedUsername(_))
       .WillRepeatedly(Return(true));
 
-  CreateProfileSyncServiceMock();
+  ProfileSyncServiceMock* sync_service = CreateProfileSyncServiceMock();
+  EXPECT_CALL(*sync_service, SetSetupInProgress(true));
+  EXPECT_CALL(*sync_service, AddObserver(_)).Times(AtLeast(1));
+  EXPECT_CALL(*sync_service, RemoveObserver(_)).Times(AtLeast(1));
+  EXPECT_CALL(*sync_service, sync_initialized()).WillRepeatedly(Return(true));
 
   content::WebContents* contents = web_contents();
 
@@ -704,6 +704,22 @@
   EXPECT_EQ(OneClickSigninHelper::AUTO_ACCEPT_NONE, helper->auto_accept_);
 }
 
+// Checks that OneClickSigninHelper doesn't stay an observer of the profile
+// sync service after it's deleted.
+TEST_F(OneClickSigninHelperTest, RemoveObserverFromProfileSyncService) {
+  content::WebContents* contents = web_contents();
+
+  ProfileSyncServiceMock* sync_service = CreateProfileSyncServiceMock();
+
+  OneClickSigninHelper::CreateForWebContentsWithPasswordManager(contents, NULL);
+  OneClickSigninHelper* helper =
+      OneClickSigninHelper::FromWebContents(contents);
+  helper->SetDoNotClearPendingEmailForTesting();
+
+  EXPECT_CALL(*sync_service, RemoveObserver(helper));
+  SetContents(NULL);
+}
+
 // I/O thread tests
 
 TEST_F(OneClickSigninHelperIOTest, CanOfferOnIOThread) {
diff --git a/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.cc b/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.cc
index 33748be..0e55fa0 100644
--- a/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.cc
+++ b/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.cc
@@ -101,6 +101,10 @@
 
 bool TabContentsSyncedTabDelegate::HasWebContents() const { return true; }
 
+content::WebContents* TabContentsSyncedTabDelegate::GetWebContents() const {
+  return web_contents_;
+}
+
 int TabContentsSyncedTabDelegate::GetSyncId() const {
   return sync_session_id_;
 }
diff --git a/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h b/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h
index 6f98ae4..5525f16 100644
--- a/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h
+++ b/chrome/browser/ui/sync/tab_contents_synced_tab_delegate.h
@@ -37,6 +37,7 @@
       GetBlockedNavigations() const OVERRIDE;
   virtual bool IsPinned() const OVERRIDE;
   virtual bool HasWebContents() const OVERRIDE;
+  virtual content::WebContents* GetWebContents() const OVERRIDE;
   virtual int GetSyncId() const OVERRIDE;
   virtual void SetSyncId(int sync_id) OVERRIDE;
 
diff --git a/chrome/browser/ui/tabs/pinned_tab_service_unittest.cc b/chrome/browser/ui/tabs/pinned_tab_service_unittest.cc
index 8d0f6d2..298b932 100644
--- a/chrome/browser/ui/tabs/pinned_tab_service_unittest.cc
+++ b/chrome/browser/ui/tabs/pinned_tab_service_unittest.cc
@@ -31,7 +31,7 @@
 
 class PinnedTabServiceTest : public BrowserWithTestWindowTest {
  public:
-  PinnedTabServiceTest() {}
+  PinnedTabServiceTest() : pinned_tab_service_(NULL) {}
 
  protected:
   virtual TestingProfile* CreateProfile() OVERRIDE {
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc
index 7b117fa..fa83e5c 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_order_controller.h"
 #include "chrome/common/url_constants.h"
+#include "components/web_modal/web_contents_modal_dialog_manager.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
 #include "content/public/browser/render_process_host.h"
@@ -188,6 +189,11 @@
     data->opener = active_contents;
   }
 
+  web_modal::WebContentsModalDialogManager* modal_dialog_manager =
+      web_modal::WebContentsModalDialogManager::FromWebContents(contents);
+  if (modal_dialog_manager)
+    data->blocked = modal_dialog_manager->IsShowingDialog();
+
   contents_data_.insert(contents_data_.begin() + index, data);
 
   selection_model_.IncrementFrom(index);
diff --git a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
index 8aac04f..1d64284 100644
--- a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
@@ -29,6 +29,7 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/web_modal/web_contents_modal_dialog_manager.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/notification_details.h"
@@ -41,6 +42,7 @@
 using content::SiteInstance;
 using content::WebContents;
 using extensions::Extension;
+using web_modal::NativeWebContentsModalDialog;
 
 namespace {
 
@@ -111,6 +113,69 @@
   int id_;
 };
 
+class DummyNativeWebContentsModalDialogManager
+    : public web_modal::NativeWebContentsModalDialogManager {
+ public:
+  explicit DummyNativeWebContentsModalDialogManager(
+      web_modal::NativeWebContentsModalDialogManagerDelegate* delegate)
+      : delegate_(delegate) {}
+  virtual ~DummyNativeWebContentsModalDialogManager() {}
+
+  virtual void ManageDialog(NativeWebContentsModalDialog dialog) OVERRIDE {}
+  virtual void ShowDialog(NativeWebContentsModalDialog dialog) OVERRIDE {}
+  virtual void HideDialog(NativeWebContentsModalDialog dialog) OVERRIDE {}
+  virtual void CloseDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
+    delegate_->WillClose(dialog);
+  }
+  virtual void FocusDialog(NativeWebContentsModalDialog dialog) OVERRIDE {}
+  virtual void PulseDialog(NativeWebContentsModalDialog dialog) OVERRIDE {}
+  virtual void HostChanged(
+      web_modal::WebContentsModalDialogHost* new_host) OVERRIDE {}
+
+ private:
+  web_modal::NativeWebContentsModalDialogManagerDelegate* delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(DummyNativeWebContentsModalDialogManager);
+};
+
+// Test Browser-like class for TabStripModelTest.TabBlockedState.
+class TabBlockedStateTestBrowser
+    : public TabStripModelObserver,
+      public web_modal::WebContentsModalDialogManagerDelegate {
+ public:
+  explicit TabBlockedStateTestBrowser(TabStripModel* tab_strip_model)
+      : tab_strip_model_(tab_strip_model) {
+    tab_strip_model_->AddObserver(this);
+  }
+
+  virtual ~TabBlockedStateTestBrowser() {
+    tab_strip_model_->RemoveObserver(this);
+  }
+
+ private:
+  // TabStripModelObserver
+  virtual void TabInsertedAt(WebContents* contents,
+                             int index,
+                             bool foreground) OVERRIDE {
+    web_modal::WebContentsModalDialogManager* manager =
+        web_modal::WebContentsModalDialogManager::FromWebContents(contents);
+    if (manager)
+      manager->SetDelegate(this);
+  }
+
+  // WebContentsModalDialogManagerDelegate
+  virtual void SetWebContentsBlocked(content::WebContents* contents,
+                                     bool blocked) OVERRIDE {
+    int index = tab_strip_model_->GetIndexOfWebContents(contents);
+    ASSERT_GE(index, 0);
+    tab_strip_model_->SetTabBlocked(index, blocked);
+  }
+
+  TabStripModel* tab_strip_model_;
+
+  DISALLOW_COPY_AND_ASSIGN(TabBlockedStateTestBrowser);
+};
+
 }  // namespace
 
 class TabStripModelTest : public ChromeRenderViewHostTestHarness {
@@ -2366,3 +2431,52 @@
   strip.RemoveObserver(&observer);
   strip.CloseAllTabs();
 }
+
+// Verifies a newly inserted tab retains its previous blocked state.
+// http://crbug.com/276334
+TEST_F(TabStripModelTest, TabBlockedState) {
+  // Start with a source tab strip.
+  TabStripDummyDelegate dummy_tab_strip_delegate;
+  TabStripModel strip_src(&dummy_tab_strip_delegate, profile());
+  TabBlockedStateTestBrowser browser_src(&strip_src);
+
+  // Add a tab.
+  WebContents* contents1 = CreateWebContents();
+  web_modal::WebContentsModalDialogManager::CreateForWebContents(contents1);
+  strip_src.AppendWebContents(contents1, false);
+
+  // Add another tab.
+  WebContents* contents2 = CreateWebContents();
+  web_modal::WebContentsModalDialogManager::CreateForWebContents(contents2);
+  strip_src.AppendWebContents(contents2, false);
+
+  // Create a destination tab strip.
+  TabStripModel strip_dst(&dummy_tab_strip_delegate, profile());
+  TabBlockedStateTestBrowser browser_dst(&strip_dst);
+
+  // Setup a NativeWebContentsModalDialogManager for tab |contents2|.
+  web_modal::WebContentsModalDialogManager* modal_dialog_manager =
+      web_modal::WebContentsModalDialogManager::FromWebContents(contents2);
+  web_modal::WebContentsModalDialogManager::TestApi test_api(
+      modal_dialog_manager);
+  test_api.ResetNativeManager(
+      new DummyNativeWebContentsModalDialogManager(modal_dialog_manager));
+
+  // Show a dialog that blocks tab |contents2|.
+  // DummyNativeWebContentsModalDialogManager doesn't care about the
+  // NativeWebContentsModalDialog value, so any dummy value works.
+  modal_dialog_manager->ShowDialog(
+      reinterpret_cast<NativeWebContentsModalDialog>(0));
+  EXPECT_TRUE(strip_src.IsTabBlocked(1));
+
+  // Detach the tab.
+  WebContents* moved_contents = strip_src.DetachWebContentsAt(1);
+  EXPECT_EQ(contents2, moved_contents);
+
+  // Attach the tab to the destination tab strip.
+  strip_dst.AppendWebContents(moved_contents, true);
+  EXPECT_TRUE(strip_dst.IsTabBlocked(0));
+
+  strip_dst.CloseAllTabs();
+  strip_src.CloseAllTabs();
+}
diff --git a/chrome/browser/ui/toolbar/wrench_menu_model.cc b/chrome/browser/ui/toolbar/wrench_menu_model.cc
index 38118fa..cf4c163 100644
--- a/chrome/browser/ui/toolbar/wrench_menu_model.cc
+++ b/chrome/browser/ui/toolbar/wrench_menu_model.cc
@@ -518,12 +518,12 @@
 #endif
 
   AddItemWithStringId(IDC_NEW_TAB, IDS_NEW_TAB);
-  if (ShouldShowNewIncognitoWindowMenuItem())
-    AddItemWithStringId(IDC_NEW_INCOGNITO_WINDOW, IDS_NEW_INCOGNITO_WINDOW);
-
   if (ShouldShowNewWindowMenuItem())
     AddItemWithStringId(IDC_NEW_WINDOW, IDS_NEW_WINDOW);
 
+  if (ShouldShowNewIncognitoWindowMenuItem())
+    AddItemWithStringId(IDC_NEW_INCOGNITO_WINDOW, IDS_NEW_INCOGNITO_WINDOW);
+
 #if defined(OS_WIN) && !defined(NDEBUG) && defined(USE_ASH)
   if (base::win::GetVersion() < base::win::VERSION_WIN8 &&
       chrome::HOST_DESKTOP_TYPE_NATIVE != chrome::HOST_DESKTOP_TYPE_ASH) {
diff --git a/chrome/browser/ui/views/accessibility/accessibility_event_router_views.cc b/chrome/browser/ui/views/accessibility/accessibility_event_router_views.cc
index db193be..931cf0d 100644
--- a/chrome/browser/ui/views/accessibility/accessibility_event_router_views.cc
+++ b/chrome/browser/ui/views/accessibility/accessibility_event_router_views.cc
@@ -305,7 +305,6 @@
   ui::AccessibleViewState state;
   view->GetAccessibleState(&state);
   std::string name = UTF16ToUTF8(state.name);
-  std::string value = UTF16ToUTF8(state.value);
   std::string context = GetViewContext(view);
   AccessibilityCheckboxInfo info(
       profile,
diff --git a/chrome/browser/ui/views/app_list/app_list_controller_win.cc b/chrome/browser/ui/views/app_list/app_list_controller_win.cc
index 2574a61..7e6349b 100644
--- a/chrome/browser/ui/views/app_list/app_list_controller_win.cc
+++ b/chrome/browser/ui/views/app_list/app_list_controller_win.cc
@@ -756,8 +756,19 @@
   }
 
   void FreeAnyKeepAliveForView() {
-    if (keep_alive_)
-      keep_alive_.reset(NULL);
+    if (keep_alive_) {
+      // We may end up here as the result of the OS deleting the AppList's
+      // widget (WidgetObserver::OnWidgetDestroyed). If this happens and there
+      // are no browsers around then deleting the keep alive will result in
+      // deleting the Widget again (by way of CloseAllSecondaryWidgets). When
+      // the stack unravels we end up back in the Widget that was deleted and
+      // crash. By delaying deletion of the keep alive we ensure the Widget has
+      // correctly been destroyed before ending the keep alive so that
+      // CloseAllSecondaryWidgets() won't attempt to delete the AppList's Widget
+      // again.
+      base::MessageLoop::current()->DeleteSoon(FROM_HERE,
+                                               keep_alive_.release());
+    }
   }
 
   scoped_ptr<AppListViewFactory> factory_;
diff --git a/chrome/browser/ui/views/app_list/app_list_controller_win_browsertest.cc b/chrome/browser/ui/views/app_list/app_list_controller_win_browsertest.cc
deleted file mode 100644
index 93cf597..0000000
--- a/chrome/browser/ui/views/app_list/app_list_controller_win_browsertest.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
-#include "chrome/browser/ui/app_list/app_list_service.h"
-#include "chrome/browser/ui/app_list/app_list_service_win.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/host_desktop.h"
-#include "chrome/test/base/in_process_browser_test.h"
-
-class AppListControllerWinTest : public InProcessBrowserTest {
-};
-
-// Test the CreateNewWindow function of the controller delegate.
-IN_PROC_BROWSER_TEST_F(AppListControllerWinTest, CreateNewWindow) {
-  AppListService* service = chrome::GetAppListServiceWin();
-  AppListControllerDelegate* controller = service->CreateControllerDelegate();
-  chrome::HostDesktopType desktop = chrome::GetActiveDesktop();
-
-  EXPECT_EQ(1U, chrome::GetBrowserCount(browser()->profile(), desktop));
-  EXPECT_EQ(0U, chrome::GetBrowserCount(
-      browser()->profile()->GetOffTheRecordProfile(), desktop));
-
-  controller->CreateNewWindow(browser()->profile(), false);
-  EXPECT_EQ(2U, chrome::GetBrowserCount(browser()->profile(), desktop));
-
-  controller->CreateNewWindow(browser()->profile(), true);
-  EXPECT_EQ(1U, chrome::GetBrowserCount(
-      browser()->profile()->GetOffTheRecordProfile(), desktop));
-}
diff --git a/chrome/browser/ui/views/apps/native_app_window_views.cc b/chrome/browser/ui/views/apps/native_app_window_views.cc
index b278b0d..3de096b 100644
--- a/chrome/browser/ui/views/apps/native_app_window_views.cc
+++ b/chrome/browser/ui/views/apps/native_app_window_views.cc
@@ -423,6 +423,9 @@
   return window_bounds.InsetsFrom(client_bounds);
 }
 
+void NativeAppWindowViews::HideWithApp() {}
+void NativeAppWindowViews::ShowWithApp() {}
+
 gfx::NativeView NativeAppWindowViews::GetHostView() const {
   return window_->GetNativeView();
 }
diff --git a/chrome/browser/ui/views/apps/native_app_window_views.h b/chrome/browser/ui/views/apps/native_app_window_views.h
index 5456bb2..e7b790f 100644
--- a/chrome/browser/ui/views/apps/native_app_window_views.h
+++ b/chrome/browser/ui/views/apps/native_app_window_views.h
@@ -142,6 +142,8 @@
       const content::NativeWebKeyboardEvent& event) OVERRIDE;
   virtual void RenderViewHostChanged() OVERRIDE;
   virtual gfx::Insets GetFrameInsets() const OVERRIDE;
+  virtual void HideWithApp() OVERRIDE;
+  virtual void ShowWithApp() OVERRIDE;
 
   // web_modal::WebContentsModalDialogHost implementation.
   virtual gfx::NativeView GetHostView() const OVERRIDE;
diff --git a/chrome/browser/ui/views/autofill/autofill_dialog_views.cc b/chrome/browser/ui/views/autofill/autofill_dialog_views.cc
index 0d45f12..7ff0aab 100644
--- a/chrome/browser/ui/views/autofill/autofill_dialog_views.cc
+++ b/chrome/browser/ui/views/autofill/autofill_dialog_views.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/location.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/autofill/autofill_dialog_sign_in_delegate.h"
@@ -32,6 +33,7 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/path.h"
+#include "ui/gfx/point.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/views/background.h"
 #include "ui/views/border.h"
@@ -39,9 +41,9 @@
 #include "ui/views/bubble/bubble_frame_view.h"
 #include "ui/views/controls/button/blue_button.h"
 #include "ui/views/controls/button/checkbox.h"
-#include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/button/label_button_border.h"
+#include "ui/views/controls/button/menu_button.h"
 #include "ui/views/controls/combobox/combobox.h"
 #include "ui/views/controls/focusable_border.h"
 #include "ui/views/controls/image_view.h"
@@ -115,6 +117,7 @@
 
 const char kNotificationAreaClassName[] = "autofill/NotificationArea";
 const char kOverlayViewClassName[] = "autofill/OverlayView";
+const char kSuggestedButtonClassName[] = "autofill/SuggestedButton";
 
 typedef ui::MultiAnimation::Part Part;
 typedef ui::MultiAnimation::Parts Parts;
@@ -507,19 +510,17 @@
 AutofillDialogViews::AccountChooser::AccountChooser(
     AutofillDialogViewDelegate* delegate)
     : image_(new views::ImageView()),
-      label_(new views::Label()),
-      arrow_(new views::ImageView()),
+      menu_button_(new views::MenuButton(NULL, base::string16(), this, true)),
       link_(new views::Link()),
       delegate_(delegate) {
   SetLayoutManager(
       new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0,
                            kAroundTextPadding));
   AddChildView(image_);
-  AddChildView(label_);
 
-  arrow_->SetImage(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
-      IDR_MENU_DROPARROW).ToImageSkia());
-  AddChildView(arrow_);
+  menu_button_->set_background(NULL);
+  menu_button_->set_border(NULL);
+  AddChildView(menu_button_);
 
   link_->set_listener(this);
   AddChildView(link_);
@@ -532,11 +533,10 @@
 
   gfx::Image icon = delegate_->AccountChooserImage();
   image_->SetImage(icon.AsImageSkia());
-  label_->SetText(delegate_->AccountChooserText());
+  menu_button_->SetText(delegate_->AccountChooserText());
 
   bool show_link = !delegate_->MenuModelForAccountChooser();
-  label_->SetVisible(!show_link);
-  arrow_->SetVisible(!show_link);
+  menu_button_->SetVisible(!show_link);
   link_->SetText(delegate_->SignInLinkText());
   link_->SetVisible(show_link);
 
@@ -545,32 +545,24 @@
   PreferredSizeChanged();
 }
 
-bool AutofillDialogViews::AccountChooser::OnMousePressed(
-    const ui::MouseEvent& event) {
-  // Return true so we get the release event.
-  if (delegate_->MenuModelForAccountChooser())
-    return event.IsOnlyLeftMouseButton();
-
-  return false;
-}
-
-void AutofillDialogViews::AccountChooser::OnMouseReleased(
-    const ui::MouseEvent& event) {
-  if (!HitTestPoint(event.location()))
-    return;
+void AutofillDialogViews::AccountChooser::OnMenuButtonClicked(
+    views::View* source,
+    const gfx::Point& point) {
+  DCHECK_EQ(menu_button_, source);
 
   ui::MenuModel* model = delegate_->MenuModelForAccountChooser();
   if (!model)
     return;
 
   menu_runner_.reset(new views::MenuRunner(model));
-  ignore_result(
-      menu_runner_->RunMenuAt(GetWidget(),
+  if (menu_runner_->RunMenuAt(source->GetWidget(),
                               NULL,
-                              GetBoundsInScreen(),
+                              source->GetBoundsInScreen(),
                               views::MenuItemView::TOPRIGHT,
-                              ui::MENU_SOURCE_MOUSE,
-                              0));
+                              ui::MENU_SOURCE_NONE,
+                              0) == views::MenuRunner::MENU_DELETED) {
+    return;
+  }
 }
 
 void AutofillDialogViews::AccountChooser::LinkClicked(views::Link* source,
@@ -1003,7 +995,53 @@
   return event_copy;
 }
 
-// AutofilDialogViews::SuggestionView ------------------------------------------
+// AutofillDialogViews::SuggestedButton ----------------------------------------
+
+AutofillDialogViews::SuggestedButton::SuggestedButton(
+    views::MenuButtonListener* listener)
+    : views::MenuButton(NULL, base::string16(), listener, false) {
+  set_border(views::Border::CreateEmptyBorder(kMenuButtonTopInset,
+                                              kDialogEdgePadding,
+                                              kMenuButtonBottomInset,
+                                              0));
+}
+
+AutofillDialogViews::SuggestedButton::~SuggestedButton() {}
+
+gfx::Size AutofillDialogViews::SuggestedButton::GetPreferredSize() {
+  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+  gfx::Size size = rb.GetImageNamed(ResourceIDForState()).Size();
+  const gfx::Insets insets = GetInsets();
+  size.Enlarge(insets.width(), insets.height());
+  return size;
+}
+
+const char* AutofillDialogViews::SuggestedButton::GetClassName() const {
+  return kSuggestedButtonClassName;
+}
+
+void AutofillDialogViews::SuggestedButton::PaintChildren(gfx::Canvas* canvas) {}
+
+void AutofillDialogViews::SuggestedButton::OnPaint(gfx::Canvas* canvas) {
+  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+  const gfx::Insets insets = GetInsets();
+  canvas->DrawImageInt(*rb.GetImageSkiaNamed(ResourceIDForState()),
+                       insets.left(), insets.top());
+}
+
+int AutofillDialogViews::SuggestedButton::ResourceIDForState() const {
+  views::Button::ButtonState button_state = state();
+  if (button_state == views::Button::STATE_PRESSED)
+    return IDR_AUTOFILL_DIALOG_MENU_BUTTON_P;
+  else if (button_state == views::Button::STATE_HOVERED)
+    return IDR_AUTOFILL_DIALOG_MENU_BUTTON_H;
+  else if (button_state == views::Button::STATE_DISABLED)
+    return IDR_AUTOFILL_DIALOG_MENU_BUTTON_D;
+  DCHECK_EQ(views::Button::STATE_NORMAL, button_state);
+  return IDR_AUTOFILL_DIALOG_MENU_BUTTON;
+}
+
+// AutofillDialogViews::SuggestionView -----------------------------------------
 
 AutofillDialogViews::SuggestionView::SuggestionView(
     AutofillDialogViews* autofill_dialog)
@@ -1393,6 +1431,22 @@
       decorated_textfield()->text();
 }
 
+bool AutofillDialogViews::HitTestInput(const DetailInput& input,
+                                       const gfx::Point& screen_point) {
+  views::View* view = TextfieldForInput(input);
+  if (!view)
+    view = ComboboxForInput(input);
+
+  if (view) {
+    gfx::Point target_point(screen_point);
+    views::View::ConvertPointFromScreen(view, &target_point);
+    return view->HitTestPoint(target_point);
+  }
+
+  NOTREACHED();
+  return false;
+}
+
 bool AutofillDialogViews::SaveDetailsLocally() {
   DCHECK(save_in_chrome_checkbox_->visible());
   return save_in_chrome_checkbox_->checked();
@@ -1646,43 +1700,8 @@
 
 void AutofillDialogViews::ButtonPressed(views::Button* sender,
                                         const ui::Event& event) {
-  if (sender->GetAncestorWithClassName(kOverlayViewClassName)) {
-    delegate_->OverlayButtonPressed();
-    return;
-  }
-
-  // TODO(estade): Should the menu be shown on mouse down?
-  DetailsGroup* group = NULL;
-  for (DetailGroupMap::iterator iter = detail_groups_.begin();
-       iter != detail_groups_.end(); ++iter) {
-    if (sender == iter->second.suggested_button) {
-      group = &iter->second;
-      break;
-    }
-  }
-  DCHECK(group);
-
-  if (!group->suggested_button->visible())
-    return;
-
-  menu_runner_.reset(new views::MenuRunner(
-                         delegate_->MenuModelForSection(group->section)));
-
-  group->container->SetActive(true);
-  views::Button::ButtonState state = group->suggested_button->state();
-  group->suggested_button->SetState(views::Button::STATE_PRESSED);
-  // Ignore the result since we don't need to handle a deleted menu specially.
-  gfx::Rect bounds = group->suggested_button->GetBoundsInScreen();
-  bounds.Inset(group->suggested_button->GetInsets());
-  ignore_result(
-      menu_runner_->RunMenuAt(sender->GetWidget(),
-                              NULL,
-                              bounds,
-                              views::MenuItemView::TOPRIGHT,
-                              ui::GetMenuSourceTypeForEvent(event),
-                              0));
-  group->container->SetActive(false);
-  group->suggested_button->SetState(state);
+  DCHECK(sender->GetAncestorWithClassName(kOverlayViewClassName));
+  delegate_->OverlayButtonPressed();
 }
 
 void AutofillDialogViews::ContentsChanged(views::Textfield* sender,
@@ -1747,6 +1766,45 @@
   delegate_->LegalDocumentLinkClicked(range);
 }
 
+void AutofillDialogViews::OnMenuButtonClicked(views::View* source,
+                                              const gfx::Point& point) {
+  DCHECK_EQ(kSuggestedButtonClassName, source->GetClassName());
+
+  DetailsGroup* group = NULL;
+  for (DetailGroupMap::iterator iter = detail_groups_.begin();
+       iter != detail_groups_.end(); ++iter) {
+    if (source == iter->second.suggested_button) {
+      group = &iter->second;
+      break;
+    }
+  }
+  DCHECK(group);
+
+  if (!group->suggested_button->visible())
+    return;
+
+  menu_runner_.reset(new views::MenuRunner(
+                         delegate_->MenuModelForSection(group->section)));
+
+  group->container->SetActive(true);
+  views::Button::ButtonState state = group->suggested_button->state();
+  group->suggested_button->SetState(views::Button::STATE_PRESSED);
+
+  gfx::Rect screen_bounds = source->GetBoundsInScreen();
+  screen_bounds.Inset(source->GetInsets());
+  if (menu_runner_->RunMenuAt(source->GetWidget(),
+                              NULL,
+                              screen_bounds,
+                              views::MenuItemView::TOPRIGHT,
+                              ui::MENU_SOURCE_NONE,
+                              0) == views::MenuRunner::MENU_DELETED) {
+    return;
+  }
+
+  group->container->SetActive(false);
+  group->suggested_button->SetState(state);
+}
+
 gfx::Size AutofillDialogViews::CalculatePreferredSize() {
   gfx::Insets insets = GetInsets();
   gfx::Size scroll_size = scrollable_area_->contents()->GetPreferredSize();
@@ -1909,28 +1967,13 @@
   SuggestionView* suggested_info = new SuggestionView(this);
   info_view->AddChildView(suggested_info);
 
+  DetailsGroup* group = GroupForSection(section);
   // TODO(estade): It might be slightly more OO if this button were created
   // and listened to by the section container.
-  views::ImageButton* menu_button = new views::ImageButton(this);
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  menu_button->SetImage(views::Button::STATE_NORMAL,
-      rb.GetImageSkiaNamed(IDR_AUTOFILL_DIALOG_MENU_BUTTON));
-  menu_button->SetImage(views::Button::STATE_PRESSED,
-      rb.GetImageSkiaNamed(IDR_AUTOFILL_DIALOG_MENU_BUTTON_P));
-  menu_button->SetImage(views::Button::STATE_HOVERED,
-      rb.GetImageSkiaNamed(IDR_AUTOFILL_DIALOG_MENU_BUTTON_H));
-  menu_button->SetImage(views::Button::STATE_DISABLED,
-      rb.GetImageSkiaNamed(IDR_AUTOFILL_DIALOG_MENU_BUTTON_D));
-  menu_button->set_border(views::Border::CreateEmptyBorder(
-      kMenuButtonTopInset,
-      kDialogEdgePadding,
-      kMenuButtonBottomInset,
-      0));
-
-  DetailsGroup* group = GroupForSection(section);
-  group->suggested_button = menu_button;
+  group->suggested_button = new SuggestedButton(this);
   group->manual_input = manual_inputs;
   group->suggested_info = suggested_info;
+
   return info_view;
 }
 
@@ -2249,11 +2292,11 @@
     decorated = iter->second;
     if (decorated == textfield) {
       delegate_->UserEditedOrActivatedInput(group->section,
-                                              iter->first,
-                                              GetWidget()->GetNativeView(),
-                                              textfield->GetBoundsInScreen(),
-                                              textfield->text(),
-                                              was_edit);
+                                            iter->first,
+                                            GetWidget()->GetNativeView(),
+                                            textfield->GetBoundsInScreen(),
+                                            textfield->text(),
+                                            was_edit);
       type = iter->first->type;
       break;
     }
diff --git a/chrome/browser/ui/views/autofill/autofill_dialog_views.h b/chrome/browser/ui/views/autofill/autofill_dialog_views.h
index 71cdb94..c5b37c4 100644
--- a/chrome/browser/ui/views/autofill/autofill_dialog_views.h
+++ b/chrome/browser/ui/views/autofill/autofill_dialog_views.h
@@ -17,6 +17,7 @@
 #include "chrome/browser/ui/autofill/testable_autofill_dialog_view.h"
 #include "ui/base/animation/animation_delegate.h"
 #include "ui/views/controls/button/button.h"
+#include "ui/views/controls/button/menu_button.h"
 #include "ui/views/controls/button/menu_button_listener.h"
 #include "ui/views/controls/combobox/combobox_listener.h"
 #include "ui/views/controls/link_listener.h"
@@ -28,10 +29,6 @@
 #include "ui/views/widget/widget_observer.h"
 #include "ui/views/window/dialog_delegate.h"
 
-namespace content {
-class KeyboardListener;
-}
-
 namespace gfx {
 class Image;
 }
@@ -41,7 +38,6 @@
 class Checkbox;
 class Combobox;
 class FocusManager;
-class ImageButton;
 class ImageView;
 class Label;
 class LabelButton;
@@ -73,7 +69,8 @@
                             public views::TextfieldController,
                             public views::FocusChangeListener,
                             public views::ComboboxListener,
-                            public views::StyledLabelListener {
+                            public views::StyledLabelListener,
+                            public views::MenuButtonListener {
  public:
   explicit AutofillDialogViews(AutofillDialogViewDelegate* delegate);
   virtual ~AutofillDialogViews();
@@ -95,6 +92,8 @@
   virtual void GetUserInput(DialogSection section,
                             DetailOutputMap* output) OVERRIDE;
   virtual base::string16 GetCvc() OVERRIDE;
+  virtual bool HitTestInput(const DetailInput& input,
+                            const gfx::Point& screen_point) OVERRIDE;
   virtual bool SaveDetailsLocally() OVERRIDE;
   virtual const content::NavigationController* ShowSignIn() OVERRIDE;
   virtual void HideSignIn() OVERRIDE;
@@ -169,6 +168,10 @@
   virtual void StyledLabelLinkClicked(const ui::Range& range, int event_flags)
       OVERRIDE;
 
+  // views::MenuButtonListener implementation.
+  virtual void OnMenuButtonClicked(views::View* source,
+                                   const gfx::Point& point) OVERRIDE;
+
  private:
   // A class that creates and manages a widget for error messages.
   class ErrorBubble : public views::WidgetObserver {
@@ -204,6 +207,7 @@
   // switch accounts.
   class AccountChooser : public views::View,
                          public views::LinkListener,
+                         public views::MenuButtonListener,
                          public base::SupportsWeakPtr<AccountChooser> {
    public:
     explicit AccountChooser(AutofillDialogViewDelegate* delegate);
@@ -212,22 +216,19 @@
     // Updates the view based on the state that |delegate_| reports.
     void Update();
 
-    // views::View implementation.
-    virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
-    virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE;
-
     // views::LinkListener implementation.
     virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
 
+    // views::MenuButtonListener implementation.
+    virtual void OnMenuButtonClicked(views::View* source,
+                                     const gfx::Point& point) OVERRIDE;
+
    private:
     // The icon for the currently in-use account.
     views::ImageView* image_;
 
-    // The label for the currently in-use account.
-    views::Label* label_;
-
-    // The drop arrow.
-    views::ImageView* arrow_;
+    // The button for showing a menu to change the currently in-use account.
+    views::MenuButton* menu_button_;
 
     // The sign in link.
     views::Link* link_;
@@ -379,6 +380,25 @@
     DISALLOW_COPY_AND_ASSIGN(SectionContainer);
   };
 
+  // A button to show address or billing suggestions.
+  class SuggestedButton : public views::MenuButton {
+   public:
+    explicit SuggestedButton(views::MenuButtonListener* listener);
+    virtual ~SuggestedButton();
+
+    // views::MenuButton implementation.
+    virtual gfx::Size GetPreferredSize() OVERRIDE;
+    virtual const char* GetClassName() const OVERRIDE;
+    virtual void PaintChildren(gfx::Canvas* canvas) OVERRIDE;
+    virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+
+   private:
+    // Returns the corred resource ID (i.e. IDR_*) for the current |state()|.
+    int ResourceIDForState() const;
+
+    DISALLOW_COPY_AND_ASSIGN(SuggestedButton);
+  };
+
   // A view that contains a suggestion (such as a known address) and a link to
   // edit the suggestion.
   class SuggestionView : public views::View {
@@ -456,7 +476,7 @@
     // visible IFF |manual_input| is not visible.
     SuggestionView* suggested_info;
     // The view that allows selecting other data suggestions.
-    views::ImageButton* suggested_button;
+    SuggestedButton* suggested_button;
   };
 
   // Area for displaying that status of various steps in an Autocheckout flow.
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_views.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_views.cc
index 661ef09..2c725f5 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_views.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_views.cc
@@ -11,9 +11,12 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/image/image.h"
+#include "ui/gfx/native_widget_types.h"
 #include "ui/gfx/point.h"
 #include "ui/gfx/rect.h"
+#include "ui/gfx/screen.h"
 #include "ui/views/border.h"
+#include "ui/views/event_utils.h"
 #include "ui/views/widget/widget.h"
 
 using WebKit::WebAutofillClient;
@@ -108,28 +111,61 @@
 }
 
 void AutofillPopupViewViews::OnMouseMoved(const ui::MouseEvent& event) {
-  if (controller_)
+  if (!controller_)
+    return;
+
+  if (HitTestPoint(event.location()))
     controller_->MouseHovered(event.x(), event.y());
+  else
+    controller_->MouseExitedPopup();
 }
 
 bool AutofillPopupViewViews::OnMousePressed(const ui::MouseEvent& event) {
-  // We must return true in order to get the OnMouseReleased event later.
-  return true;
+  if (HitTestPoint(event.location()))
+    return true;
+
+  if (controller_->hide_on_outside_click()) {
+    GetWidget()->ReleaseCapture();
+
+    gfx::Point screen_loc = event.location();
+    views::View::ConvertPointToScreen(this, &screen_loc);
+
+    ui::MouseEvent mouse_event = event;
+    mouse_event.set_location(screen_loc);
+
+    if (controller_->ShouldRepostEvent(mouse_event)) {
+      gfx::NativeView native_view = GetWidget()->GetNativeView();
+      gfx::Screen* screen = gfx::Screen::GetScreenFor(native_view);
+      gfx::NativeWindow window = screen->GetWindowAtScreenPoint(screen_loc);
+      views::RepostLocatedEvent(window, mouse_event);
+    }
+
+    controller_->Hide();
+    // |this| is now deleted.
+  }
+
+  return false;
 }
 
 void AutofillPopupViewViews::OnMouseReleased(const ui::MouseEvent& event) {
   if (!controller_)
     return;
 
+  // Because this view can can be shown in response to a mouse press, it can
+  // receive an OnMouseReleased event just after showing. This breaks the mouse
+  // capture, so restart capturing here.
+  if (controller_->hide_on_outside_click() && GetWidget())
+    GetWidget()->SetCapture(this);
+
   // We only care about the left click.
-  if (event.IsOnlyLeftMouseButton() &&
-      HitTestPoint(event.location()))
+  if (event.IsOnlyLeftMouseButton() && HitTestPoint(event.location()))
     controller_->MouseClicked(event.x(), event.y());
 }
 
 void AutofillPopupViewViews::OnWidgetBoundsChanged(
     views::Widget* widget,
     const gfx::Rect& new_bounds) {
+  DCHECK_EQ(widget, observing_widget_);
   controller_->Hide();
 }
 
@@ -152,6 +188,9 @@
 
   UpdateBoundsAndRedrawPopup();
   GetWidget()->Show();
+
+  if (controller_->hide_on_outside_click())
+    GetWidget()->SetCapture(this);
 }
 
 void AutofillPopupViewViews::InvalidateRow(size_t row) {
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
index 09546a4..1204ae6 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -42,6 +42,7 @@
 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_instructions_view.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_context_menu.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.h"
+#include "chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h"
 #include "chrome/browser/ui/views/event_utils.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
@@ -470,6 +471,7 @@
   if (bookmark_menu_) {
     bookmark_menu_->set_observer(NULL);
     bookmark_menu_->SetPageNavigator(NULL);
+    bookmark_menu_->clear_bookmark_bar();
   }
   if (context_menu_.get())
     context_menu_->SetPageNavigator(NULL);
@@ -607,14 +609,14 @@
   int max_width = views::TooltipManager::GetMaxWidth(screen_loc.x(),
                                                      screen_loc.y(),
                                                      context);
-  gfx::Font tt_font = views::TooltipManager::GetDefaultFont();
+  const gfx::FontList& tt_fonts = views::TooltipManager::GetDefaultFontList();
   string16 result;
 
   // First the title.
   if (!title.empty()) {
     string16 localized_title = title;
     base::i18n::AdjustStringForLocaleDirection(&localized_title);
-    result.append(ui::ElideText(localized_title, tt_font, max_width,
+    result.append(ui::ElideText(localized_title, tt_fonts, max_width,
                                 ui::ELIDE_AT_END));
   }
 
@@ -631,7 +633,7 @@
     // default.
     std::string languages = profile->GetPrefs()->GetString(
         prefs::kAcceptLanguages);
-    string16 elided_url(ui::ElideUrl(url, tt_font, max_width, languages));
+    string16 elided_url(ui::ElideUrl(url, tt_fonts, max_width, languages));
     elided_url = base::i18n::GetDisplayStringInLTRDirectionality(elided_url);
     result.append(elided_url);
   }
@@ -940,7 +942,8 @@
   }
 }
 
-void BookmarkBarView::BookmarkMenuDeleted(BookmarkMenuController* controller) {
+void BookmarkBarView::BookmarkMenuControllerDeleted(
+    BookmarkMenuController* controller) {
   if (controller == bookmark_menu_)
     bookmark_menu_ = NULL;
   else if (controller == bookmark_drop_menu_)
@@ -1167,8 +1170,8 @@
   }
 
   bookmark_utils::RecordBookmarkFolderOpen(GetBookmarkLaunchLocation());
-  bookmark_menu_ = new BookmarkMenuController(browser_,
-      page_navigator_, GetWidget(), node, start_index);
+  bookmark_menu_ = new BookmarkMenuController(
+      browser_, page_navigator_, GetWidget(), node, start_index);
   bookmark_menu_->set_observer(this);
   bookmark_menu_->RunMenuAt(this, false);
 }
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h
index afea414..f61e061 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.h
@@ -19,12 +19,13 @@
 #include "chrome/browser/ui/bookmarks/bookmark_bar.h"
 #include "chrome/browser/ui/bookmarks/bookmark_bar_instructions_delegate.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_bubble_view_observer.h"
-#include "chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h"
+#include "chrome/browser/ui/views/bookmarks/bookmark_menu_controller_observer.h"
 #include "chrome/browser/ui/views/detachable_toolbar_view.h"
 #include "ui/base/animation/animation_delegate.h"
 #include "ui/views/context_menu_controller.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/menu_button_listener.h"
+#include "ui/views/controls/menu/menu_item_view.h"
 #include "ui/views/drag_controller.h"
 
 class BookmarkContextMenu;
@@ -42,7 +43,6 @@
 namespace views {
 class CustomButton;
 class MenuButton;
-class MenuItemView;
 class TextButton;
 }
 
@@ -60,7 +60,7 @@
                         public views::ContextMenuController,
                         public views::DragController,
                         public ui::AnimationDelegate,
-                        public BookmarkMenuController::Observer,
+                        public BookmarkMenuControllerObserver,
                         public chrome::BookmarkBarInstructionsDelegate,
                         public BookmarkBubbleViewObserver {
  public:
@@ -118,9 +118,8 @@
   views::MenuButton* GetMenuButtonForNode(const BookmarkNode* node);
 
   // Returns the position to anchor the menu for |button| at.
-  void GetAnchorPositionForButton(
-      views::MenuButton* button,
-      views::MenuItemView::AnchorPosition* anchor);
+  void GetAnchorPositionForButton(views::MenuButton* button,
+                                  views::MenuItemView::AnchorPosition* anchor);
 
   // Returns the button responsible for showing bookmarks in the other bookmark
   // folder.
@@ -189,8 +188,8 @@
   virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE;
   virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE;
 
-  // BookmarkMenuController::Observer:
-  virtual void BookmarkMenuDeleted(
+  // BookmarkMenuControllerObserver:
+  virtual void BookmarkMenuControllerDeleted(
       BookmarkMenuController* controller) OVERRIDE;
 
   // chrome::BookmarkBarInstructionsDelegate:
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
index bb5f2de..9ee143a 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
@@ -116,7 +116,7 @@
         model_(NULL) {}
 
   virtual void SetUp() OVERRIDE {
-    views::MenuController::TurnOffContextMenuSelectionHoldForTest();
+    views::MenuController::TurnOffMenuSelectionHoldForTest();
     BookmarkBarView::DisableAnimationsForTesting(true);
 
     profile_.reset(new TestingProfile());
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_observer.h b/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_observer.h
new file mode 100644
index 0000000..f6ca016
--- /dev/null
+++ b/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_observer.h
@@ -0,0 +1,20 @@
+// 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 CHROME_BROWSER_UI_VIEWS_BOOKMARKS_BOOKMARK_MENU_CONTROLLER_OBSERVER_H_
+#define CHROME_BROWSER_UI_VIEWS_BOOKMARKS_BOOKMARK_MENU_CONTROLLER_OBSERVER_H_
+
+class BookmarkMenuController;
+
+// The observer is notified prior to the menu being deleted.
+class BookmarkMenuControllerObserver {
+ public:
+  virtual void BookmarkMenuControllerDeleted(
+      BookmarkMenuController* controller) = 0;
+
+ protected:
+  virtual ~BookmarkMenuControllerObserver() {}
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_BOOKMARKS_BOOKMARK_MENU_CONTROLLER_OBSERVER_H_
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.cc b/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.cc
index ffbeb43..d3a4614 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/bookmarks/bookmark_model.h"
 #include "chrome/browser/bookmarks/bookmark_utils.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
+#include "chrome/browser/ui/views/bookmarks/bookmark_menu_controller_observer.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h"
 #include "chrome/common/pref_names.h"
 #include "content/public/browser/page_navigator.h"
@@ -193,5 +194,5 @@
 BookmarkMenuController::~BookmarkMenuController() {
   menu_delegate_->GetBookmarkModel()->RemoveObserver(this);
   if (observer_)
-    observer_->BookmarkMenuDeleted(this);
+    observer_->BookmarkMenuControllerDeleted(this);
 }
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h b/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h
index 269738b..4536b13 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h
@@ -14,6 +14,7 @@
 #include "ui/views/controls/menu/menu_item_view.h"
 
 class BookmarkBarView;
+class BookmarkMenuControllerObserver;
 class BookmarkMenuDelegate;
 class BookmarkNode;
 class Browser;
@@ -24,13 +25,13 @@
 
 namespace ui {
 class OSExchangeData;
-}  // namespace ui
+}
 
 namespace views {
 class MenuButton;
 class MenuRunner;
 class Widget;
-}  // namespace views
+}
 
 // BookmarkMenuController is responsible for showing a menu of bookmarks,
 // each item in the menu represents a bookmark.
@@ -39,15 +40,6 @@
 class BookmarkMenuController : public BaseBookmarkModelObserver,
                                public views::MenuDelegate {
  public:
-  // The observer is notified prior to the menu being deleted.
-  class Observer {
-   public:
-    virtual void BookmarkMenuDeleted(BookmarkMenuController* controller) = 0;
-
-   protected:
-    virtual ~Observer() {}
-  };
-
   // Creates a BookmarkMenuController showing the children of |node| starting
   // at |start_child_index|.
   BookmarkMenuController(Browser* browser,
@@ -58,6 +50,10 @@
 
   void RunMenuAt(BookmarkBarView* bookmark_bar, bool for_drop);
 
+  void clear_bookmark_bar() {
+    bookmark_bar_ = NULL;
+  }
+
   // Hides the menu.
   void Cancel();
 
@@ -73,9 +69,11 @@
   // Sets the page navigator.
   void SetPageNavigator(content::PageNavigator* navigator);
 
-  void set_observer(Observer* observer) { observer_ = observer; }
+  void set_observer(BookmarkMenuControllerObserver* observer) {
+    observer_ = observer;
+  }
 
-  // MenuDelegate methods.
+  // views::MenuDelegate:
   virtual string16 GetTooltipText(int id, const gfx::Point& p) const OVERRIDE;
   virtual bool IsTriggerableEvent(views::MenuItemView* view,
                                   const ui::Event& e) OVERRIDE;
@@ -113,7 +111,7 @@
       views::MenuButton** button) OVERRIDE;
   virtual int GetMaxWidthForMenu(views::MenuItemView* view) OVERRIDE;
 
-  // BookmarkModelObserver methods.
+  // BaseBookmarkModelObserver:
   virtual void BookmarkModelChanged() OVERRIDE;
 
  private:
@@ -131,13 +129,14 @@
   BookmarkNodeData drop_data_;
 
   // The observer, may be null.
-  Observer* observer_;
+  BookmarkMenuControllerObserver* observer_;
 
   // Is the menu being shown for a drop?
   bool for_drop_;
 
-  // The bookmark bar. This is only non-null if we're showing a menu item
-  // for a folder on the bookmark bar and not for drop.
+  // The bookmark bar. This is only non-null if we're showing a menu item for a
+  // folder on the bookmark bar and not for drop, or if the BookmarkBarView has
+  // been destroyed before the menu.
   BookmarkBarView* bookmark_bar_;
 
   DISALLOW_COPY_AND_ASSIGN(BookmarkMenuController);
diff --git a/chrome/browser/ui/views/constrained_window_views_browsertest.cc b/chrome/browser/ui/views/constrained_window_views_browsertest.cc
index fd337fa..6f63e2e 100644
--- a/chrome/browser/ui/views/constrained_window_views_browsertest.cc
+++ b/chrome/browser/ui/views/constrained_window_views_browsertest.cc
@@ -291,6 +291,57 @@
   EXPECT_TRUE(test_dialog->done());
 }
 
+// Tests that the constrained window behaves properly when moving its tab
+// between browser windows.
+IN_PROC_BROWSER_TEST_F(ConstrainedWindowViewTest, TabMoveTest) {
+  // Open a second browser.
+  Browser* browser2 = CreateBrowser(browser()->profile());
+
+  // Create a second WebContents in the second browser, so that moving the
+  // WebContents does not trigger the browser to close immediately. This mimics
+  // the behavior when a user drags tabs between browsers.
+  content::WebContents* web_contents = content::WebContents::Create(
+      content::WebContents::CreateParams(browser()->profile()));
+  browser2->tab_strip_model()->AppendWebContents(web_contents, true);
+  ASSERT_EQ(web_contents, browser2->tab_strip_model()->GetActiveWebContents());
+
+  // Create a constrained dialog.  It will attach itself to web_contents.
+  scoped_ptr<TestConstrainedDialog> test_dialog(new TestConstrainedDialog);
+  WebContentsModalDialogManager* web_contents_modal_dialog_manager =
+      WebContentsModalDialogManager::FromWebContents(web_contents);
+  WebContentsModalDialogManagerDelegate* modal_delegate =
+      web_contents_modal_dialog_manager->delegate();
+  ASSERT_TRUE(modal_delegate != NULL);
+  views::Widget* window = views::Widget::CreateWindowAsFramelessChild(
+      test_dialog.get(),
+      web_contents->GetView()->GetNativeView(),
+      modal_delegate->GetWebContentsModalDialogHost()->GetHostView());
+  web_contents_modal_dialog_manager->ShowDialog(window->GetNativeView());
+  EXPECT_TRUE(window->IsVisible());
+
+  // Detach the web contents from the second browser's tab strip.
+  browser2->tab_strip_model()->DetachWebContentsAt(
+      browser2->tab_strip_model()->GetIndexOfWebContents(web_contents));
+
+  // Append the web contents to the first browser.
+  browser()->tab_strip_model()->AppendWebContents(web_contents, true);
+  EXPECT_TRUE(window->IsVisible());
+
+  // Close the second browser.
+  browser2->tab_strip_model()->CloseAllTabs();
+  content::RunAllPendingInMessageLoop();
+  EXPECT_TRUE(window->IsVisible());
+
+  // Close the dialog's tab.
+  bool closed =
+      browser()->tab_strip_model()->CloseWebContentsAt(
+          browser()->tab_strip_model()->GetIndexOfWebContents(web_contents),
+          TabStripModel::CLOSE_NONE);
+  EXPECT_TRUE(closed);
+  content::RunAllPendingInMessageLoop();
+  EXPECT_TRUE(test_dialog->done());
+}
+
 #if defined(OS_WIN) || (defined(USE_AURA) && defined(USE_X11))
 
 // Forwards the key event which has |key_code| to the renderer.
diff --git a/chrome/browser/ui/views/create_application_shortcut_view.cc b/chrome/browser/ui/views/create_application_shortcut_view.cc
index f4ca66c..7c070eb 100644
--- a/chrome/browser/ui/views/create_application_shortcut_view.cc
+++ b/chrome/browser/ui/views/create_application_shortcut_view.cc
@@ -419,7 +419,8 @@
     : CreateApplicationShortcutView(
           Profile::FromBrowserContext(web_contents->GetBrowserContext())),
       web_contents_(web_contents),
-      pending_download_id_(-1)  {
+      pending_download_id_(-1),
+      weak_ptr_factory_(this)  {
 
   web_app::GetShortcutInfoForTab(web_contents_, &shortcut_info_);
   const WebApplicationInfo& app_info =
@@ -467,7 +468,7 @@
       preferred_size,
       0,  // no maximum size
       base::Bind(&CreateUrlApplicationShortcutView::DidDownloadFavicon,
-                 base::Unretained(this)));
+                 weak_ptr_factory_.GetWeakPtr()));
 
   unprocessed_icons_.pop_back();
 }
diff --git a/chrome/browser/ui/views/create_application_shortcut_view.h b/chrome/browser/ui/views/create_application_shortcut_view.h
index 74aa877..74e5c6c 100644
--- a/chrome/browser/ui/views/create_application_shortcut_view.h
+++ b/chrome/browser/ui/views/create_application_shortcut_view.h
@@ -111,6 +111,8 @@
   // Unprocessed icons from the WebApplicationInfo passed in.
   web_app::IconInfoList unprocessed_icons_;
 
+  base::WeakPtrFactory<CreateUrlApplicationShortcutView> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(CreateUrlApplicationShortcutView);
 };
 
diff --git a/chrome/browser/ui/views/download/download_item_view.cc b/chrome/browser/ui/views/download/download_item_view.cc
index c10bdd0..e618537 100644
--- a/chrome/browser/ui/views/download/download_item_view.cc
+++ b/chrome/browser/ui/views/download/download_item_view.cc
@@ -20,7 +20,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/download/chrome_download_manager_delegate.h"
 #include "chrome/browser/download/download_item_model.h"
-#include "chrome/browser/download/download_util.h"
+#include "chrome/browser/download/drag_download_item.h"
 #include "chrome/browser/safe_browsing/download_feedback_service.h"
 #include "chrome/browser/safe_browsing/download_protection_service.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
@@ -416,8 +416,8 @@
           download()->GetTargetFilePath(), IconLoader::SMALL);
       if (icon) {
         views::Widget* widget = GetWidget();
-        download_util::DragDownload(download(), icon,
-                                    widget ? widget->GetNativeView() : NULL);
+        DragDownloadItem(
+            download(), icon, widget ? widget->GetNativeView() : NULL);
       }
     }
   } else if (ExceededDragThreshold(event.location() - drag_start_point_)) {
diff --git a/chrome/browser/ui/views/extensions/extension_installed_bubble.cc b/chrome/browser/ui/views/extensions/extension_installed_bubble.cc
index 5ffff39..e64e261 100644
--- a/chrome/browser/ui/views/extensions/extension_installed_bubble.cc
+++ b/chrome/browser/ui/views/extensions/extension_installed_bubble.cc
@@ -543,13 +543,11 @@
       icon_(icon),
       animation_wait_retries_(0),
       weak_factory_(this) {
-  extensions::ExtensionActionManager* extension_action_manager =
-      extensions::ExtensionActionManager::Get(browser_->profile());
   if (!extensions::OmniboxInfo::GetKeyword(extension).empty())
     type_ = OMNIBOX_KEYWORD;
-  else if (extension_action_manager->GetBrowserAction(*extension_))
+  else if (extensions::ActionInfo::GetBrowserActionInfo(extension))
     type_ = BROWSER_ACTION;
-  else if (extension_action_manager->GetPageAction(*extension) &&
+  else if (extensions::ActionInfo::GetPageActionInfo(extension) &&
            extensions::ActionInfo::IsVerboseInstallMessage(extension))
     type_ = PAGE_ACTION;
   else
diff --git a/chrome/browser/ui/views/find_bar_host.cc b/chrome/browser/ui/views/find_bar_host.cc
index a369196..3f7233a 100644
--- a/chrome/browser/ui/views/find_bar_host.cc
+++ b/chrome/browser/ui/views/find_bar_host.cc
@@ -134,6 +134,10 @@
   find_bar_view()->SetFindText(find_text);
 }
 
+string16 FindBarHost::GetFindText() {
+  return find_bar_view()->GetFindText();
+}
+
 void FindBarHost::UpdateUIForFindResult(const FindNotificationDetails& result,
                                         const string16& find_text) {
   // Make sure match count is clear. It may get set again in UpdateForResult
@@ -235,10 +239,6 @@
   return true;
 }
 
-string16 FindBarHost::GetFindText() {
-  return find_bar_view()->GetFindText();
-}
-
 string16 FindBarHost::GetFindSelectedText() {
   return find_bar_view()->GetFindSelectedText();
 }
diff --git a/chrome/browser/ui/views/find_bar_host.h b/chrome/browser/ui/views/find_bar_host.h
index 5781f55..ad6595e 100644
--- a/chrome/browser/ui/views/find_bar_host.h
+++ b/chrome/browser/ui/views/find_bar_host.h
@@ -57,6 +57,7 @@
   virtual void MoveWindowIfNecessary(const gfx::Rect& selection_rect,
                                      bool no_redraw) OVERRIDE;
   virtual void SetFindText(const string16& find_text) OVERRIDE;
+  virtual string16 GetFindText() OVERRIDE;
   virtual void UpdateUIForFindResult(const FindNotificationDetails& result,
                                      const string16& find_text) OVERRIDE;
   virtual void AudibleAlert() OVERRIDE;
@@ -73,7 +74,6 @@
   // FindBarTesting implementation:
   virtual bool GetFindBarWindowInfo(gfx::Point* position,
                                     bool* fully_visible) OVERRIDE;
-  virtual string16 GetFindText() OVERRIDE;
   virtual string16 GetFindSelectedText() OVERRIDE;
   virtual string16 GetMatchCountText() OVERRIDE;
   virtual int GetWidth() OVERRIDE;
diff --git a/chrome/browser/ui/views/find_bar_host_interactive_uitest.cc b/chrome/browser/ui/views/find_bar_host_interactive_uitest.cc
index 11103c1..251ed6d 100644
--- a/chrome/browser/ui/views/find_bar_host_interactive_uitest.cc
+++ b/chrome/browser/ui/views/find_bar_host_interactive_uitest.cc
@@ -39,8 +39,7 @@
   }
 
   string16 GetFindBarText() {
-    FindBarTesting* find_bar =
-        browser()->GetFindBarController()->find_bar()->GetFindBarTesting();
+    FindBar* find_bar = browser()->GetFindBarController()->find_bar();
     return find_bar->GetFindText();
   }
 
diff --git a/chrome/browser/ui/views/frame/app_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/app_non_client_frame_view_ash.cc
index f4491b1..a70e813 100644
--- a/chrome/browser/ui/views/frame/app_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/app_non_client_frame_view_ash.cc
@@ -116,9 +116,13 @@
 AppNonClientFrameViewAsh::AppNonClientFrameViewAsh(
     BrowserFrame* frame, BrowserView* browser_view)
     : BrowserNonClientFrameView(frame, browser_view),
-      control_view_(new ash::FrameCaptionButtonContainerView(this, frame)),
+      control_view_(NULL),
       control_widget_(NULL),
       frame_observer_(new FrameObserver(this)) {
+  control_view_ = new ash::FrameCaptionButtonContainerView(this, frame,
+      ash::FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
+  control_view_->set_header_style(
+      ash::FrameCaptionButtonContainerView::HEADER_STYLE_MAXIMIZED_HOSTED_APP);
   control_view_->set_border(new ControlViewBorder());
   control_view_->set_background(new ControlViewBackground(
       browser_view->IsOffTheRecord()));
diff --git a/chrome/browser/ui/views/frame/app_non_client_frame_view_ash.h b/chrome/browser/ui/views/frame/app_non_client_frame_view_ash.h
index 2c0058f..4c60d94 100644
--- a/chrome/browser/ui/views/frame/app_non_client_frame_view_ash.h
+++ b/chrome/browser/ui/views/frame/app_non_client_frame_view_ash.h
@@ -12,6 +12,10 @@
 class Window;
 }
 
+namespace ash {
+class FrameCaptionButtonContainerView;
+}
+
 // NonClientFrameViewAsh implementation for maximized apps.
 class AppNonClientFrameViewAsh : public BrowserNonClientFrameView {
  public:
@@ -54,7 +58,7 @@
   void CloseControlWidget();
 
   // The View containing the restore and close buttons.
-  views::View* control_view_;
+  ash::FrameCaptionButtonContainerView* control_view_;
   // The widget holding the control_view_.
   views::Widget* control_widget_;
   // Observer for browser frame close.
diff --git a/chrome/browser/ui/views/frame/browser_desktop_root_window_host_win.cc b/chrome/browser/ui/views/frame/browser_desktop_root_window_host_win.cc
index 9a86155..975844b 100644
--- a/chrome/browser/ui/views/frame/browser_desktop_root_window_host_win.cc
+++ b/chrome/browser/ui/views/frame/browser_desktop_root_window_host_win.cc
@@ -45,8 +45,8 @@
   virtual SkColor GetColor(int id) const OVERRIDE {
     return delegate_->GetColor(id);
   }
-  virtual bool GetDisplayProperty(int id, int* result) const OVERRIDE {
-    return delegate_->GetDisplayProperty(id, result);
+  virtual int GetDisplayProperty(int id) const OVERRIDE {
+    return delegate_->GetDisplayProperty(id);
   }
   virtual bool ShouldUseNativeFrame() const OVERRIDE {
     return delegate_->ShouldUseNativeFrame();
diff --git a/chrome/browser/ui/views/frame/browser_frame.cc b/chrome/browser/ui/views/frame/browser_frame.cc
index 23b2005..5b99f02 100644
--- a/chrome/browser/ui/views/frame/browser_frame.cc
+++ b/chrome/browser/ui/views/frame/browser_frame.cc
@@ -71,7 +71,7 @@
 #if defined(USE_ASH)
   if (browser_view_->browser()->host_desktop_type() ==
       chrome::HOST_DESKTOP_TYPE_ASH || chrome::ShouldOpenAshOnStartup()) {
-    params.context = ash::Shell::GetAllRootWindows()[0];
+    params.context = ash::Shell::GetPrimaryRootWindow();
   }
 #endif
   Init(params);
diff --git a/chrome/browser/ui/views/frame/browser_frame_aura.cc b/chrome/browser/ui/views/frame/browser_frame_aura.cc
index 26c80b6..6e4902d 100644
--- a/chrome/browser/ui/views/frame/browser_frame_aura.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_aura.cc
@@ -101,6 +101,9 @@
   GetNativeWindow()->SetName(kWindowName);
   GetNativeWindow()->AddObserver(window_property_watcher_.get());
 #if defined(USE_ASH)
+  if (browser_view->browser()->is_type_tabbed())
+    ash::wm::SetAnimateToFullscreen(GetNativeWindow(), false);
+
   // Turn on auto window management if we don't need an explicit bounds.
   // This way the requested bounds are honored.
   if (!browser_view->browser()->bounds_overridden() &&
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index 67a8e69..010e8aa 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -4,11 +4,9 @@
 
 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h"
 
-#include "ash/shell_delegate.h"
 #include "ash/wm/frame_painter.h"
-#include "ash/wm/workspace/frame_maximize_button.h"
+#include "ash/wm/workspace/frame_caption_button_container_view.h"
 #include "chrome/browser/themes/theme_properties.h"
-#include "chrome/browser/ui/ash/chrome_shell_delegate.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/immersive_fullscreen_configuration.h"
 #include "chrome/browser/ui/views/avatar_menu_button.h"
@@ -19,7 +17,6 @@
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "content/public/browser/web_contents.h"
 #include "grit/ash_resources.h"
-#include "grit/generated_resources.h"  // Accessibility names
 #include "grit/theme_resources.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
@@ -30,10 +27,8 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/theme_provider.h"
 #include "ui/compositor/layer_animator.h"
-#include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/image/image_skia.h"
-#include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/layout_constants.h"
 #include "ui/views/widget/widget.h"
@@ -77,28 +72,18 @@
 BrowserNonClientFrameViewAsh::BrowserNonClientFrameViewAsh(
     BrowserFrame* frame, BrowserView* browser_view)
     : BrowserNonClientFrameView(frame, browser_view),
-      size_button_(NULL),
-      close_button_(NULL),
+      caption_button_container_(NULL),
       window_icon_(NULL),
-      frame_painter_(new ash::FramePainter),
-      size_button_minimizes_(false) {
+      frame_painter_(new ash::FramePainter) {
 }
 
 BrowserNonClientFrameViewAsh::~BrowserNonClientFrameViewAsh() {
 }
 
 void BrowserNonClientFrameViewAsh::Init() {
-  // Panels only minimize.
-  ash::FramePainter::SizeButtonBehavior size_button_behavior;
-  size_button_ = new ash::FrameMaximizeButton(this, this);
-  size_button_behavior = ash::FramePainter::SIZE_BUTTON_MAXIMIZES;
-  size_button_->SetAccessibleName(
-      l10n_util::GetStringUTF16(IDS_ACCNAME_MAXIMIZE));
-  AddChildView(size_button_);
-  close_button_ = new views::ImageButton(this);
-  close_button_->SetAccessibleName(
-      l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE));
-  AddChildView(close_button_);
+  caption_button_container_ = new ash::FrameCaptionButtonContainerView(this,
+      frame(), ash::FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
+  AddChildView(caption_button_container_);
 
   // Initializing the TabIconView is expensive, so only do it if we need to.
   if (browser_view()->ShouldShowWindowIcon()) {
@@ -111,9 +96,8 @@
   // Create incognito icon if necessary.
   UpdateAvatarInfo();
 
-  // Frame painter handles layout of these buttons.
-  frame_painter_->Init(frame(), window_icon_, size_button_, close_button_,
-                       size_button_behavior);
+  // Frame painter handles layout.
+  frame_painter_->Init(frame(), window_icon_, caption_button_container_);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -195,11 +179,9 @@
   // is visible because it's confusing when the user hovers or clicks in the
   // top-right of the screen and hits one.
   bool button_visibility = !UseImmersiveLightbarHeaderStyle();
-  size_button_->SetVisible(button_visibility);
-  close_button_->SetVisible(button_visibility);
+  caption_button_container_->SetVisible(button_visibility);
 
-  size_button_->SetState(views::CustomButton::STATE_NORMAL);
-  // The close button isn't affected by this constraint.
+  caption_button_container_->ResetWindowControls();
 }
 
 void BrowserNonClientFrameViewAsh::UpdateWindowIcon() {
@@ -328,48 +310,6 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// views::ButtonListener overrides:
-
-void BrowserNonClientFrameViewAsh::ButtonPressed(views::Button* sender,
-                                                 const ui::Event& event) {
-  // When shift-clicking slow down animations for visual debugging.
-  // We used to do this via an event filter that looked for the shift key being
-  // pressed but this interfered with several normal keyboard shortcuts.
-  scoped_ptr<ui::ScopedAnimationDurationScaleMode> slow_duration_mode;
-  if (event.IsShiftDown()) {
-    slow_duration_mode.reset(new ui::ScopedAnimationDurationScaleMode(
-        ui::ScopedAnimationDurationScaleMode::SLOW_DURATION));
-  }
-
-  ash::UserMetricsAction action =
-      ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE;
-
-  if (sender == size_button_) {
-    // The maximize button may move out from under the cursor.
-    ResetWindowControls();
-    if (size_button_minimizes_) {
-      frame()->Minimize();
-      action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE;
-    } else if (frame()->IsFullscreen()) { // Can be clicked in immersive mode.
-      frame()->SetFullscreen(false);
-      action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN;
-    } else if (frame()->IsMaximized()) {
-      frame()->Restore();
-      action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE;
-    } else {
-      frame()->Maximize();
-    }
-    // |this| may be deleted - some windows delete their frames on maximize.
-  } else if (sender == close_button_) {
-    frame()->Close();
-    action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK;
-  } else {
-    return;
-  }
-  ChromeShellDelegate::instance()->RecordUserMetricsAction(action);
-}
-
-///////////////////////////////////////////////////////////////////////////////
 // chrome::TabIconViewModel overrides:
 
 bool BrowserNonClientFrameViewAsh::ShouldTabIconViewAnimate() const {
@@ -390,7 +330,6 @@
 ///////////////////////////////////////////////////////////////////////////////
 // BrowserNonClientFrameViewAsh, private:
 
-
 int BrowserNonClientFrameViewAsh::NonClientTopBorderHeight(
     bool force_restored) const {
   if (force_restored)
@@ -405,7 +344,7 @@
   }
   // For windows without a tab strip (popups, etc.) ensure we have enough space
   // to see the window caption buttons.
-  return close_button_->bounds().bottom() - kContentShadowHeight;
+  return caption_button_container_->bounds().bottom() - kContentShadowHeight;
 }
 
 bool BrowserNonClientFrameViewAsh::UseShortHeader() const {
@@ -414,8 +353,8 @@
   // Fullscreen browser, no immersive reveal -> hidden or super short light bar
   // Fullscreen browser, immersive reveal -> short header
   // Popup&App window -> tall header
-  // Panel -> short header
-  // Dialogs use short header and are handled via CustomFrameViewAsh.
+  // Panels use short header and are handled via ash::PanelFrameView.
+  // Dialogs use short header and are handled via ash::CustomFrameViewAsh.
   Browser* browser = browser_view()->browser();
   switch (browser->type()) {
     case Browser::TYPE_TABBED:
@@ -558,7 +497,7 @@
 }
 
 void BrowserNonClientFrameViewAsh::PaintContentEdge(gfx::Canvas* canvas) {
-  canvas->FillRect(gfx::Rect(0, close_button_->bounds().bottom(),
+  canvas->FillRect(gfx::Rect(0, caption_button_container_->bounds().bottom(),
                              width(), kClientEdgeThickness),
       ThemeProperties::GetDefaultColor(
           ThemeProperties::COLOR_TOOLBAR_SEPARATOR));
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
index 5b914ee..b2bf31f 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
@@ -9,11 +9,11 @@
 #include "base/memory/scoped_ptr.h"
 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h"
 #include "chrome/browser/ui/views/tab_icon_view_model.h"
-#include "ui/views/controls/button/button.h"  // ButtonListener
 
 class TabIconView;
 
 namespace ash {
+class FrameCaptionButtonContainerView;
 class FramePainter;
 }
 namespace views {
@@ -23,7 +23,6 @@
 
 class BrowserNonClientFrameViewAsh
     : public BrowserNonClientFrameView,
-      public views::ButtonListener,
       public chrome::TabIconViewModel {
  public:
   static const char kViewClassName[];
@@ -59,10 +58,6 @@
   virtual gfx::Size GetMinimumSize() OVERRIDE;
   virtual void OnThemeChanged() OVERRIDE;
 
-  // views::ButtonListener overrides:
-  virtual void ButtonPressed(views::Button* sender,
-                             const ui::Event& event) OVERRIDE;
-
   // Overridden from chrome::TabIconViewModel:
   virtual bool ShouldTabIconViewAnimate() const OVERRIDE;
   virtual gfx::ImageSkia GetFaviconForTabIconView() OVERRIDE;
@@ -111,10 +106,8 @@
   // Returns 0 if no overlay image should be used.
   int GetThemeFrameOverlayImageId() const;
 
-  // Window controls. The |size_button_| either toggles maximized or toggles
-  // minimized. The exact behavior is determined by |size_button_minimizes_|.
-  views::ImageButton* size_button_;
-  views::ImageButton* close_button_;
+  // View which contains the window controls.
+  ash::FrameCaptionButtonContainerView* caption_button_container_;
 
   // For popups, the window icon.
   TabIconView* window_icon_;
@@ -122,10 +115,6 @@
   // Painter for the frame header.
   scoped_ptr<ash::FramePainter> frame_painter_;
 
-  // If true the |size_button_| minimizes, otherwise it toggles between
-  // maximized and restored.
-  bool size_button_minimizes_;
-
   DISALLOW_COPY_AND_ASSIGN(BrowserNonClientFrameViewAsh);
 };
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
index 9bf74a2..06365fa 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -6,6 +6,7 @@
 
 #include "ash/ash_constants.h"
 #include "ash/ash_switches.h"
+#include "ash/wm/workspace/frame_caption_button_container_view.h"
 #include "base/command_line.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -17,7 +18,6 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "ui/base/hit_test.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
-#include "ui/views/controls/button/image_button.h"
 #include "ui/views/widget/widget.h"
 
 using views::Widget;
@@ -123,8 +123,7 @@
     waiter->Wait();
   }
   EXPECT_TRUE(frame_view->ShouldPaint());
-  EXPECT_TRUE(frame_view->size_button_->visible());
-  EXPECT_TRUE(frame_view->close_button_->visible());
+  EXPECT_TRUE(frame_view->caption_button_container_->visible());
 }
 
 IN_PROC_BROWSER_TEST_F(BrowserNonClientFrameViewAshTest, ImmersiveFullscreen) {
@@ -160,8 +159,7 @@
   immersive_mode_controller->StartRevealForTest(true);
   EXPECT_TRUE(immersive_mode_controller->IsRevealed());
   EXPECT_TRUE(frame_view->ShouldPaint());
-  EXPECT_TRUE(frame_view->size_button_->visible());
-  EXPECT_TRUE(frame_view->close_button_->visible());
+  EXPECT_TRUE(frame_view->caption_button_container_->visible());
   EXPECT_TRUE(frame_view->UseShortHeader());
   EXPECT_FALSE(frame_view->UseImmersiveLightbarHeaderStyle());
 
@@ -182,8 +180,7 @@
   immersive_mode_controller->StartRevealForTest(true);
   EXPECT_TRUE(immersive_mode_controller->IsRevealed());
   EXPECT_TRUE(frame_view->ShouldPaint());
-  EXPECT_TRUE(frame_view->size_button_->visible());
-  EXPECT_TRUE(frame_view->close_button_->visible());
+  EXPECT_TRUE(frame_view->caption_button_container_->visible());
   EXPECT_TRUE(frame_view->UseShortHeader());
   EXPECT_FALSE(frame_view->UseImmersiveLightbarHeaderStyle());
 
@@ -191,8 +188,7 @@
   // be in the lightbar style.
   immersive_mode_controller->SetMouseHoveredForTest(false);
   EXPECT_TRUE(frame_view->ShouldPaint());
-  EXPECT_FALSE(frame_view->size_button_->visible());
-  EXPECT_FALSE(frame_view->close_button_->visible());
+  EXPECT_FALSE(frame_view->caption_button_container_->visible());
   EXPECT_TRUE(frame_view->UseShortHeader());
   EXPECT_TRUE(frame_view->UseImmersiveLightbarHeaderStyle());
 
@@ -202,7 +198,6 @@
 
   // Exiting immersive mode makes controls and frame visible again.
   EXPECT_TRUE(frame_view->ShouldPaint());
-  EXPECT_TRUE(frame_view->size_button_->visible());
-  EXPECT_TRUE(frame_view->close_button_->visible());
+  EXPECT_TRUE(frame_view->caption_button_container_->visible());
   EXPECT_FALSE(frame_view->UseImmersiveLightbarHeaderStyle());
 }
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 6ad8447..975abcc 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -36,6 +36,7 @@
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog.h"
 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h"
+#include "chrome/browser/ui/bookmarks/bookmark_bar_constants.h"
 #include "chrome/browser/ui/bookmarks/bookmark_bubble_delegate.h"
 #include "chrome/browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.h"
 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
@@ -131,9 +132,6 @@
 #include "ash/launcher/launcher_model.h"
 #include "ash/shell.h"
 #include "chrome/browser/ui/ash/ash_util.h"
-#include "chrome/browser/ui/ash/chrome_shell_delegate.h"
-#include "chrome/browser/ui/ash/launcher/browser_launcher_item_controller.h"
-#include "chrome/browser/ui/ash/window_positioner.h"
 #endif
 
 #if defined(USE_AURA)
@@ -423,12 +421,6 @@
 }
 
 BrowserView::~BrowserView() {
-#if defined(USE_ASH)
-  // Destroy BrowserLauncherItemController early on as it listens to the
-  // TabstripModel, which is destroyed by the browser.
-  launcher_item_controller_.reset();
-#endif
-
   // Immersive mode may need to reparent views before they are removed/deleted.
   immersive_mode_controller_.reset();
 
@@ -635,8 +627,6 @@
     return;
   }
 
-  CreateLauncherIcon();
-
   // Showing the window doesn't make the browser window active right away.
   // This can cause SetFocusToLocationBar() to skip setting focus to the
   // location bar. To avoid this we explicilty let SetFocusToLocationBar()
@@ -667,7 +657,6 @@
 void BrowserView::ShowInactive() {
   if (frame_->IsVisible())
     return;
-  CreateLauncherIcon();
   frame_->ShowInactive();
 }
 
@@ -1605,21 +1594,6 @@
     return false;
   chrome::GetSavedWindowBoundsAndShowState(browser_.get(), bounds, show_state);
 
-#if defined(USE_ASH)
-  if (chrome::IsNativeWindowInAsh(
-          const_cast<BrowserView*>(this)->GetNativeWindow())) {
-    if (browser_->is_type_popup()) {
-      // In case of a popup with an 'unspecified' location we are
-      // looking for a good screen location. We are interpreting (0,0) as an
-      // unspecified location.
-      if (bounds->x() == 0 && bounds->y() == 0) {
-        *bounds = ChromeShellDelegate::instance()->window_positioner()->
-            GetPopupPosition(*bounds);
-      }
-    }
-  }
-#endif
-
   if (browser_->is_type_popup() &&
       !browser_->is_app() &&
       !browser_->is_devtools()) {
@@ -1672,11 +1646,6 @@
 
 void BrowserView::OnWidgetActivationChanged(views::Widget* widget,
                                             bool active) {
-#if defined(USE_ASH)
-  if (launcher_item_controller_.get())
-    launcher_item_controller_->BrowserActivationStateChanged();
-#endif
-
   if (active)
     BrowserList::SetLastActive(browser_.get());
 }
@@ -2540,16 +2509,6 @@
 #endif
 }
 
-void BrowserView::CreateLauncherIcon() {
-#if defined(USE_ASH)
-  if (chrome::IsNativeWindowInAsh(GetNativeWindow()) &&
-      !launcher_item_controller_.get()) {
-    launcher_item_controller_.reset(
-        BrowserLauncherItemController::Create(browser_.get()));
-  }
-#endif  // defined(USE_ASH)
-}
-
 // static
 BrowserWindow* BrowserWindow::CreateBrowserWindow(Browser* browser) {
   // Create the view and the frame. The frame will attach itself via the view
@@ -2613,6 +2572,17 @@
     scroll_end_effect_controller_->OverscrollUpdate(delta_y);
 }
 
+int BrowserView::GetRenderViewHeightInsetWithDetachedBookmarkBar() {
+  if (browser_->bookmark_bar_state() != BookmarkBar::DETACHED ||
+      !bookmark_bar_view_.get() || !bookmark_bar_view_->IsDetached()) {
+    return 0;
+  }
+  // Don't use bookmark_bar_view_->height() which won't be the final height if
+  // the bookmark bar is animating.
+  return chrome::kNTPBookmarkBarHeight -
+      bookmark_bar_view_->GetFullyDetachedToolbarOverlap();
+}
+
 void BrowserView::DoCutCopyPaste(void (content::RenderWidgetHost::*method)(),
 #if defined(OS_WIN)
                                  int windows_msg_id,
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index f34f37f..72e94e7 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -61,10 +61,6 @@
 class JumpList;
 #endif
 
-#if defined(USE_ASH)
-class BrowserLauncherItemController;
-#endif
-
 namespace autofill {
 class PasswordGenerator;
 }
@@ -256,17 +252,6 @@
   // animations.
   void ToolbarSizeChanged(bool is_animating);
 
-#if defined(USE_ASH)
-  // Test support.
-  // Note: This is only needed to be BrowserLauncherItemController instead of
-  // LauncherItemController because of the "favicon_loader" member - to be more
-  // exact that member function is the only one being called.
-  // TODO(skuhne): Remove once per-app is default.
-  BrowserLauncherItemController* launcher_item_controller() const {
-    return launcher_item_controller_.get();
-  }
-#endif
-
   // Overridden from BrowserWindow:
   virtual void Show() OVERRIDE;
   virtual void ShowInactive() OVERRIDE;
@@ -379,6 +364,7 @@
       const content::PasswordForm& form,
       autofill::PasswordGenerator* password_generator) OVERRIDE;
   virtual void OverscrollUpdate(int delta_y) OVERRIDE;
+  virtual int GetRenderViewHeightInsetWithDetachedBookmarkBar() OVERRIDE;
 
   // Overridden from BrowserWindowTesting:
   virtual BookmarkBarView* GetBookmarkBarView() const OVERRIDE;
@@ -582,9 +568,6 @@
   void UpdateAcceleratorMetrics(const ui::Accelerator& accelerator,
                                 int command_id);
 
-  // Create an icon for this window in the launcher (currently only for Ash).
-  void CreateLauncherIcon();
-
   // Calls |method| which is either RenderWidgetHost::Cut, ::Copy, or ::Paste,
   // first trying the content WebContents, then the devtools WebContents, and
   // lastly the Views::Textfield if one is focused.
@@ -744,13 +727,6 @@
   scoped_refptr<JumpList> jumplist_;
 #endif
 
-#if defined(USE_ASH)
-  // Needs to be BrowserLauncerItemController for
-  // "BrowserActivationStateChanged" and "favicon_loader".
-  // TODO(skuhne): Remove once per-app is default.
-  scoped_ptr<BrowserLauncherItemController> launcher_item_controller_;
-#endif
-
   // The timer used to update frames for the Loading Animation.
   base::RepeatingTimer<BrowserView> loading_animation_timer_;
 
diff --git a/chrome/browser/ui/views/frame/browser_view_layout.cc b/chrome/browser/ui/views/frame/browser_view_layout.cc
index 571a098..70e63c9 100644
--- a/chrome/browser/ui/views/frame/browser_view_layout.cc
+++ b/chrome/browser/ui/views/frame/browser_view_layout.cc
@@ -447,6 +447,9 @@
 }
 
 int BrowserViewLayout::LayoutBookmarkAndInfoBars(int top, int browser_view_y) {
+  web_contents_modal_dialog_top_y_ =
+      top + browser_view_y - kConstrainedWindowOverlap;
+
   if (bookmark_bar_) {
     // If we're showing the Bookmark bar in detached style, then we
     // need to show any Info bar _above_ the Bookmark bar, since the
@@ -460,9 +463,6 @@
     top = std::max(toolbar_->bounds().bottom(), LayoutBookmarkBar(top));
   }
 
-  web_contents_modal_dialog_top_y_ =
-      top + browser_view_y - kConstrainedWindowOverlap;
-
   return LayoutInfoBar(top);
 }
 
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index 5dc19f8..4b2dbd2 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -494,7 +494,7 @@
   animation_offset_ = offset;
 }
 
-void LocationBarView::Update(const WebContents* tab_for_state_restoring) {
+void LocationBarView::Update(const WebContents* contents) {
   mic_search_view_->SetVisible(
       !GetToolbarModel()->input_in_progress() && browser_ &&
       browser_->search_model()->voice_search_supported());
@@ -517,7 +517,10 @@
   if (star_view_)
     star_view_->SetVisible(star_enabled);
 
-  location_entry_->Update(tab_for_state_restoring);
+  if (contents)
+    location_entry_->OnTabChanged(contents);
+  else
+    location_entry_->Update();
 
   OnChanged();
 }
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.h b/chrome/browser/ui/views/location_bar/location_bar_view.h
index a39c901..f846263 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.h
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.h
@@ -161,9 +161,9 @@
                    ColorKind kind) const;
 
   // Updates the location bar.  We also reset the bar's permanent text and
-  // security style, and, if |tab_for_state_restoring| is non-NULL, also restore
-  // saved state that the tab holds.
-  void Update(const content::WebContents* tab_for_state_restoring);
+  // security style, and, if |contents| is non-NULL, also restore saved state
+  // that the tab holds.
+  void Update(const content::WebContents* contents);
 
   // Returns corresponding profile.
   Profile* profile() const { return profile_; }
diff --git a/chrome/browser/ui/views/message_center/web_notification_tray.cc b/chrome/browser/ui/views/message_center/web_notification_tray.cc
index 5fcf10a..ac31aa9 100644
--- a/chrome/browser/ui/views/message_center/web_notification_tray.cc
+++ b/chrome/browser/ui/views/message_center/web_notification_tray.cc
@@ -9,6 +9,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/status_icons/status_icon.h"
+#include "chrome/browser/status_icons/status_icon_menu_model.h"
 #include "chrome/browser/status_icons/status_tray.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/user_metrics.h"
@@ -39,6 +40,11 @@
 // Number of pixels the message center is offset from the mouse.
 const int kMouseOffset = 5;
 
+// Menu commands
+const int kToggleQuietMode = 0;
+const int kEnableQuietModeHour = 1;
+const int kEnableQuietModeDay = 2;
+
 gfx::ImageSkia* GetIcon(int unread_count, bool is_quiet_mode) {
   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
   int resource_id = IDR_NOTIFICATION_TRAY_EMPTY;
@@ -128,10 +134,12 @@
 WebNotificationTray::WebNotificationTray()
     : message_center_delegate_(NULL),
       status_icon_(NULL),
+      status_icon_menu_(NULL),
       message_center_visible_(false),
       should_update_tray_content_(true) {
   message_center_tray_.reset(
       new MessageCenterTray(this, g_browser_process->message_center()));
+  last_quiet_mode_state_ = message_center()->IsQuietMode();
 }
 
 WebNotificationTray::~WebNotificationTray() {
@@ -189,6 +197,17 @@
 }
 
 void WebNotificationTray::OnMessageCenterTrayChanged() {
+  if (status_icon_) {
+    bool quiet_mode_state = message_center()->IsQuietMode();
+    if (last_quiet_mode_state_ != quiet_mode_state) {
+      last_quiet_mode_state_ = quiet_mode_state;
+
+      // Quiet mode has changed, update the quiet mode menu.
+      status_icon_menu_->SetCommandIdChecked(kToggleQuietMode,
+                                             quiet_mode_state);
+    }
+  }
+
   // See the comments in ash/system/web_notification/web_notification_tray.cc
   // for why PostTask.
   should_update_tray_content_ = true;
@@ -204,6 +223,18 @@
   message_center_tray_->ToggleMessageCenterBubble();
 }
 
+void WebNotificationTray::ExecuteCommand(int command_id, int event_flags) {
+  if (command_id == kToggleQuietMode) {
+    bool in_quiet_mode = message_center()->IsQuietMode();
+    message_center()->SetQuietMode(!in_quiet_mode);
+    return;
+  }
+  base::TimeDelta expires_in = command_id == kEnableQuietModeDay
+                                   ? base::TimeDelta::FromDays(1)
+                                   : base::TimeDelta::FromHours(1);
+  message_center()->EnterQuietModeWithExpire(expires_in);
+}
+
 void WebNotificationTray::UpdateStatusIcon() {
   if (!should_update_tray_content_)
     return;
@@ -319,12 +350,24 @@
   StatusTray* status_tray = g_browser_process->status_tray();
   if (status_tray)
     status_tray->RemoveStatusIcon(status_icon_);
+  status_icon_menu_ = NULL;
   status_icon_ = NULL;
 }
 
 void WebNotificationTray::AddQuietModeMenu(StatusIcon* status_icon) {
   DCHECK(status_icon);
-  status_icon->SetContextMenu(message_center_tray_->CreateQuietModeMenu());
+
+  scoped_ptr<StatusIconMenuModel> menu(new StatusIconMenuModel(this));
+  menu->AddCheckItem(kToggleQuietMode,
+                     l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_QUIET_MODE));
+  menu->SetCommandIdChecked(kToggleQuietMode, message_center()->IsQuietMode());
+  menu->AddItem(kEnableQuietModeHour,
+                l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_QUIET_MODE_1HOUR));
+  menu->AddItem(kEnableQuietModeDay,
+                l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_QUIET_MODE_1DAY));
+
+  status_icon_menu_ = menu.get();
+  status_icon->SetContextMenu(menu.Pass());
 }
 
 MessageCenterWidgetDelegate*
diff --git a/chrome/browser/ui/views/message_center/web_notification_tray.h b/chrome/browser/ui/views/message_center/web_notification_tray.h
index 040f9a6..a37dd9e 100644
--- a/chrome/browser/ui/views/message_center/web_notification_tray.h
+++ b/chrome/browser/ui/views/message_center/web_notification_tray.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_VIEWS_MESSAGE_CENTER_WEB_NOTIFICATION_TRAY_H_
 
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/status_icons/status_icon_menu_model.h"
 #include "chrome/browser/status_icons/status_icon_observer.h"
 #include "chrome/browser/ui/views/message_center/message_center_widget_delegate.h"
 #include "content/public/browser/notification_observer.h"
@@ -39,7 +40,8 @@
 // tray icon on click.
 class WebNotificationTray : public message_center::MessageCenterTrayDelegate,
                             public StatusIconObserver,
-                            public base::SupportsWeakPtr<WebNotificationTray> {
+                            public base::SupportsWeakPtr<WebNotificationTray>,
+                            public StatusIconMenuModel::Delegate {
  public:
   WebNotificationTray();
   virtual ~WebNotificationTray();
@@ -64,6 +66,9 @@
   void DisplayFirstRunBalloon();
 #endif
 
+  // StatusIconMenuModel::Delegate implementation.
+  virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE;
+
   // Changes the icon and hovertext based on number of unread notifications.
   void UpdateStatusIcon();
   void SendHideMessageCenter();
@@ -96,11 +101,13 @@
   scoped_ptr<message_center::MessagePopupCollection> popup_collection_;
 
   StatusIcon* status_icon_;
+  StatusIconMenuModel* status_icon_menu_;
   bool message_center_visible_;
   scoped_ptr<MessageCenterTray> message_center_tray_;
   gfx::Point mouse_click_point_;
 
   bool should_update_tray_content_;
+  bool last_quiet_mode_state_;
 
   DISALLOW_COPY_AND_ASSIGN(WebNotificationTray);
 };
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
index 2d9094d..e7114af 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_contents_view.cc
@@ -41,10 +41,18 @@
     : public views::Widget,
       public base::SupportsWeakPtr<AutocompletePopupWidget> {
  public:
-  AutocompletePopupWidget() {}
-  virtual ~AutocompletePopupWidget() {}
+  AutocompletePopupWidget() : crash_if_destroyed_(false) {}
+  virtual ~AutocompletePopupWidget() {
+    CHECK(!crash_if_destroyed_);
+  }
+
+  void set_crash_if_destroyed(bool value) { crash_if_destroyed_ = value; }
 
  private:
+  // For debugging a crash.
+  // TODO(sky): nuke this when we figure out 275794.
+  bool crash_if_destroyed_;
+
   DISALLOW_COPY_AND_ASSIGN(AutocompletePopupWidget);
 };
 
@@ -215,7 +223,9 @@
     params.parent = popup_parent;
     params.bounds = GetPopupBounds();
     params.context = popup_parent;
+    popup_->set_crash_if_destroyed(true);
     popup_->Init(params);
+    popup_->set_crash_if_destroyed(false);
 #if defined(USE_AURA)
     views::corewm::SetWindowVisibilityAnimationType(
         popup_->GetNativeView(),
@@ -247,7 +257,7 @@
     popup_->SetBounds(GetPopupBounds());
   }
 
-  SchedulePaint();
+  Layout();
 }
 
 gfx::Rect OmniboxPopupContentsView::GetTargetBounds() {
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index c58534a..a87be22 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -364,31 +364,24 @@
   tab->SetUserData(OmniboxState::kKey, new OmniboxState(state, selection));
 }
 
-void OmniboxViewViews::Update(const content::WebContents* contents) {
-  // NOTE: We're getting the URL text here from the ToolbarModel.
-  bool visibly_changed_permanent_text = model()->UpdatePermanentText(
-      controller()->GetToolbarModel()->GetText(true));
-  ToolbarModel::SecurityLevel security_level =
-        controller()->GetToolbarModel()->GetSecurityLevel(false);
-  bool changed_security_level = (security_level != security_level_);
-  security_level_ = security_level;
+void OmniboxViewViews::OnTabChanged(const content::WebContents* web_contents) {
+  security_level_ = controller()->GetToolbarModel()->GetSecurityLevel(false);
 
-  if (contents) {
-    RevertAll();
-    const OmniboxState* state = static_cast<OmniboxState*>(
-        contents->GetUserData(&OmniboxState::kKey));
-    if (state) {
-      // Restore the saved state and selection.
-      model()->RestoreState(state->model_state);
-      SelectSelectionModel(state->selection_model);
-      // TODO(msw|oshima): Consider saving/restoring edit history.
-      ClearEditHistory();
-    }
-  } else if (visibly_changed_permanent_text) {
-    // Not switching tabs, just updating the permanent text.  (In the case where
-    // we _were_ switching tabs, the RevertAll() above already drew the new
-    // permanent text.)
+  const OmniboxState* state = static_cast<OmniboxState*>(
+      web_contents->GetUserData(&OmniboxState::kKey));
+  model()->RestoreState(state ? &state->model_state : NULL);
+  if (state)
+    SelectSelectionModel(state->selection_model);
 
+  // TODO(msw|oshima): Consider saving/restoring edit history.
+  ClearEditHistory();
+}
+
+void OmniboxViewViews::Update() {
+  const ToolbarModel::SecurityLevel old_security_level = security_level_;
+  security_level_ = controller()->GetToolbarModel()->GetSecurityLevel(false);
+  if (model()->UpdatePermanentText(
+      controller()->GetToolbarModel()->GetText(true))) {
     // Tweak: if the user had all the text selected, select all the new text.
     // This makes one particular case better: the user clicks in the box to
     // change it right before the permanent URL is changed.  Since the new URL
@@ -409,7 +402,7 @@
     // things when the omnibox isn't focused to begin with.
     if (was_select_all && model()->has_focus())
       SelectAll(range.is_reversed());
-  } else if (changed_security_level) {
+  } else if (old_security_level != security_level_) {
     EmphasizeURLComponents();
   }
 }
@@ -756,23 +749,25 @@
 }
 
 void OmniboxViewViews::UpdateContextMenu(ui::SimpleMenuModel* menu_contents) {
-  // Minor note: We use IDC_ for command id here while the underlying textfield
-  // is using IDS_ for all its command ids. This is because views cannot depend
-  // on IDC_ for now.
-  menu_contents->AddItemWithStringId(IDC_EDIT_SEARCH_ENGINES,
-      IDS_EDIT_SEARCH_ENGINES);
-
   if (chrome::IsQueryExtractionEnabled()) {
     int copy_position = menu_contents->GetIndexOfCommandId(IDS_APP_COPY);
-    DCHECK(copy_position >= 0);
+    DCHECK_GE(copy_position, 0);
     menu_contents->InsertItemWithStringIdAt(
         copy_position + 1, IDC_COPY_URL, IDS_COPY_URL);
   }
 
   int paste_position = menu_contents->GetIndexOfCommandId(IDS_APP_PASTE);
-  DCHECK(paste_position >= 0);
+  DCHECK_GE(paste_position, 0);
   menu_contents->InsertItemWithStringIdAt(
       paste_position + 1, IDS_PASTE_AND_GO, IDS_PASTE_AND_GO);
+
+  menu_contents->AddSeparator(ui::NORMAL_SEPARATOR);
+
+  // Minor note: We use IDC_ for command id here while the underlying textfield
+  // is using IDS_ for all its command ids. This is because views cannot depend
+  // on IDC_ for now.
+  menu_contents->AddItemWithStringId(IDC_EDIT_SEARCH_ENGINES,
+      IDS_EDIT_SEARCH_ENGINES);
 }
 
 bool OmniboxViewViews::IsCommandIdEnabled(int command_id) const {
@@ -789,13 +784,10 @@
 }
 
 string16 OmniboxViewViews::GetLabelForCommandId(int command_id) const {
-  if (command_id == IDS_PASTE_AND_GO) {
-    return l10n_util::GetStringUTF16(
-        model()->IsPasteAndSearch(GetClipboardText()) ?
-        IDS_PASTE_AND_SEARCH : IDS_PASTE_AND_GO);
-  }
-
-  return string16();
+  DCHECK_EQ(IDS_PASTE_AND_GO, command_id);
+  return l10n_util::GetStringUTF16(
+      model()->IsPasteAndSearch(GetClipboardText()) ?
+          IDS_PASTE_AND_SEARCH : IDS_PASTE_AND_GO);
 }
 
 bool OmniboxViewViews::HandlesCommand(int command_id) const {
@@ -806,24 +798,23 @@
 void OmniboxViewViews::ExecuteCommand(int command_id, int event_flags) {
   switch (command_id) {
     // These commands don't invoke the popup via OnBefore/AfterPossibleChange().
+    case IDC_COPY_URL:
+      DoCopyURL(controller()->GetToolbarModel()->GetURL(),
+                controller()->GetToolbarModel()->GetText(false));
+      break;
     case IDS_PASTE_AND_GO:
       model()->PasteAndGo(GetClipboardText());
       break;
     case IDC_EDIT_SEARCH_ENGINES:
       command_updater()->ExecuteCommand(command_id);
       break;
-    case IDC_COPY_URL:
-      CopyURL();
-      break;
 
-    case IDS_APP_PASTE:
-      OnBeforePossibleChange();
-      OnPaste();
-      OnAfterPossibleChange();
-      break;
     default:
       OnBeforePossibleChange();
-      command_updater()->ExecuteCommand(command_id);
+      if (command_id == IDS_APP_PASTE)
+        OnPaste();
+      else
+        command_updater()->ExecuteCommand(command_id);
       OnAfterPossibleChange();
       break;
   }
@@ -899,11 +890,6 @@
   return views::Textfield::GetSelectedText();
 }
 
-void OmniboxViewViews::CopyURL() {
-  DoCopyURL(controller()->GetToolbarModel()->GetURL(),
-            controller()->GetToolbarModel()->GetText(false));
-}
-
 void OmniboxViewViews::OnPaste() {
   const string16 text(GetClipboardText());
   if (!text.empty()) {
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.h b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
index dff230c..5491880 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
@@ -72,8 +72,8 @@
 
   // OmniboxView:
   virtual void SaveStateToTab(content::WebContents* tab) OVERRIDE;
-  virtual void Update(
-      const content::WebContents* tab_for_state_restoring) OVERRIDE;
+  virtual void OnTabChanged(const content::WebContents* web_contents) OVERRIDE;
+  virtual void Update() OVERRIDE;
   virtual string16 GetText() const OVERRIDE;
   virtual void SetWindowTextAndCaretPos(const string16& text,
                                         size_t caret_pos,
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_win.cc b/chrome/browser/ui/views/omnibox/omnibox_view_win.cc
index 2ecd259..11c2f4f 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_win.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_win.cc
@@ -131,13 +131,6 @@
       gfx::Point(current) - gfx::Point(origin));
 }
 
-// Copies |selected_text| as text to the primary clipboard.
-void DoCopyText(const string16& selected_text) {
-  ui::ScopedClipboardWriter scw(ui::Clipboard::GetForCurrentThread(),
-                                ui::Clipboard::BUFFER_STANDARD);
-  scw.WriteText(selected_text);
-}
-
 // Writes |url| and |text| to the clipboard as a well-formed URL.
 void DoCopyURL(const GURL& url, const string16& text) {
   BookmarkNodeData data;
@@ -596,47 +589,26 @@
           State(selection, saved_selection_for_focus_change_)));
 }
 
-void OmniboxViewWin::Update(const WebContents* tab_for_state_restoring) {
-  const bool visibly_changed_permanent_text = model()->UpdatePermanentText(
-      controller()->GetToolbarModel()->GetText(true));
-
-  const ToolbarModel::SecurityLevel security_level =
-      controller()->GetToolbarModel()->GetSecurityLevel(false);
-  const bool changed_security_level = (security_level != security_level_);
-
-  // Bail early when no visible state will actually change (prevents an
-  // unnecessary ScopedFreeze, and thus UpdateWindow()).
-  if (!changed_security_level && !visibly_changed_permanent_text &&
-      !tab_for_state_restoring)
-    return;
-
-  // Update our local state as desired.  We set security_level_ here so it will
-  // already be correct before we get to any RevertAll()s below and use it.
-  security_level_ = security_level;
-
-  // When we're switching to a new tab, restore its state, if any.
+void OmniboxViewWin::OnTabChanged(const content::WebContents* web_contents) {
   ScopedFreeze freeze(this, GetTextObjectModel());
-  if (tab_for_state_restoring) {
-    // Make sure we reset our own state first.  The new tab may not have any
-    // saved state, or it may not have had input in progress, in which case we
-    // won't overwrite all our local state.
-    RevertAll();
+  security_level_ = controller()->GetToolbarModel()->GetSecurityLevel(false);
 
-    const AutocompleteEditState* state = static_cast<AutocompleteEditState*>(
-        tab_for_state_restoring->GetUserData(&kAutocompleteEditStateKey));
-    if (state) {
-      model()->RestoreState(state->model_state);
+  const AutocompleteEditState* state = static_cast<AutocompleteEditState*>(
+      web_contents->GetUserData(&kAutocompleteEditStateKey));
+  model()->RestoreState(state ? &state->model_state : NULL);
+  if (state) {
+    SetSelectionRange(state->view_state.selection);
+    saved_selection_for_focus_change_ =
+        state->view_state.saved_selection_for_focus_change;
+  }
+}
 
-      // Restore user's selection.  We do this after restoring the user_text
-      // above so we're selecting in the correct string.
-      SetSelectionRange(state->view_state.selection);
-      saved_selection_for_focus_change_ =
-          state->view_state.saved_selection_for_focus_change;
-    }
-  } else if (visibly_changed_permanent_text) {
-    // Not switching tabs, just updating the permanent text.  (In the case where
-    // we _were_ switching tabs, the RevertAll() above already drew the new
-    // permanent text.)
+void OmniboxViewWin::Update() {
+  const ToolbarModel::SecurityLevel old_security_level = security_level_;
+  security_level_ = controller()->GetToolbarModel()->GetSecurityLevel(false);
+  if (model()->UpdatePermanentText(
+      controller()->GetToolbarModel()->GetText(true))) {
+    ScopedFreeze freeze(this, GetTextObjectModel());
 
     // Tweak: if the user had all the text selected, select all the new text.
     // This makes one particular case better: the user clicks in the box to
@@ -659,8 +631,7 @@
     // things when the omnibox isn't focused to begin with.
     if (was_select_all && model()->has_focus())
       SelectAll(sel.cpMin > sel.cpMax);
-  } else if (changed_security_level) {
-    // Only the security style changed, nothing else.  Redraw our text using it.
+  } else if (old_security_level != security_level_) {
     EmphasizeURLComponents();
   }
 }
@@ -1102,11 +1073,6 @@
   return ui::DragDropTypes::DRAG_NONE;
 }
 
-void OmniboxViewWin::CopyURL() {
-  DoCopyURL(controller()->GetToolbarModel()->GetURL(),
-            controller()->GetToolbarModel()->GetText(false));
-}
-
 bool OmniboxViewWin::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
   // Skip processing of [Alt]+<num-pad digit> Unicode alt key codes.
   if (event.IsUnicodeKeyCode())
@@ -1214,18 +1180,22 @@
 void OmniboxViewWin::ExecuteCommand(int command_id, int event_flags) {
   ScopedFreeze freeze(this, GetTextObjectModel());
   // These commands don't invoke the popup via OnBefore/AfterPossibleChange().
+  if (command_id == IDC_COPY) {
+    Copy();
+    return;
+  }
+  if (command_id == IDC_COPY_URL) {
+    DoCopyURL(controller()->GetToolbarModel()->GetURL(),
+              controller()->GetToolbarModel()->GetText(false));
+    return;
+  }
   if (command_id == IDS_PASTE_AND_GO) {
     model()->PasteAndGo(GetClipboardText());
     return;
-  } else if (command_id == IDC_EDIT_SEARCH_ENGINES) {
+  }
+  if (command_id == IDC_EDIT_SEARCH_ENGINES) {
     command_updater()->ExecuteCommand(command_id);
     return;
-  } else if (command_id == IDC_COPY) {
-    Copy();
-    return;
-  } else if (command_id == IDC_COPY_URL) {
-    CopyURL();
-    return;
   }
 
   OnBeforePossibleChange();
@@ -1430,10 +1400,13 @@
   // GetSel() doesn't preserve selection direction, so sel.cpMin will always be
   // the smaller value.
   model()->AdjustTextForCopy(sel.cpMin, IsSelectAll(), &text, &url, &write_url);
-  if (write_url)
+  if (write_url) {
     DoCopyURL(url, text);
-  else
-    DoCopyText(text);
+  } else {
+    ui::ScopedClipboardWriter scw(ui::Clipboard::GetForCurrentThread(),
+                                  ui::Clipboard::BUFFER_STANDARD);
+    scw.WriteText(text);
+  }
 }
 
 LRESULT OmniboxViewWin::OnCreate(const CREATESTRUCTW* /*create_struct*/) {
@@ -2772,22 +2745,24 @@
 
   context_menu_contents_.reset(new ui::SimpleMenuModel(this));
   // Set up context menu.
-  if (popup_window_mode_) {
-    context_menu_contents_->AddItemWithStringId(IDC_COPY, IDS_COPY);
-  } else {
+  if (!popup_window_mode_) {
     context_menu_contents_->AddItemWithStringId(IDS_UNDO, IDS_UNDO);
     context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
     context_menu_contents_->AddItemWithStringId(IDC_CUT, IDS_CUT);
-    context_menu_contents_->AddItemWithStringId(IDC_COPY, IDS_COPY);
-    if (chrome::IsQueryExtractionEnabled())
-      context_menu_contents_->AddItemWithStringId(IDC_COPY_URL, IDS_COPY_URL);
+  }
+  context_menu_contents_->AddItemWithStringId(IDC_COPY, IDS_COPY);
+  if (chrome::IsQueryExtractionEnabled())
+    context_menu_contents_->AddItemWithStringId(IDC_COPY_URL, IDS_COPY_URL);
+  if (!popup_window_mode_) {
     context_menu_contents_->AddItemWithStringId(IDC_PASTE, IDS_PASTE);
     // GetContextualLabel() will override this next label with the
     // IDS_PASTE_AND_SEARCH label as needed.
     context_menu_contents_->AddItemWithStringId(IDS_PASTE_AND_GO,
                                                 IDS_PASTE_AND_GO);
-    context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
-    context_menu_contents_->AddItemWithStringId(IDS_SELECT_ALL, IDS_SELECT_ALL);
+  }
+  context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
+  context_menu_contents_->AddItemWithStringId(IDS_SELECT_ALL, IDS_SELECT_ALL);
+  if (!popup_window_mode_) {
     context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
     context_menu_contents_->AddItemWithStringId(IDC_EDIT_SEARCH_ENGINES,
                                                 IDS_EDIT_SEARCH_ENGINES);
@@ -2799,8 +2774,7 @@
   // When the user has clicked and released to give us focus, select all unless
   // we're doing search term replacement (in which case refining the existing
   // query is common enough that we do click-to-place-cursor).
-  if (tracking_click_[button] &&
-      !IsDrag(click_point_[button], point) &&
+  if (tracking_click_[button] && !IsDrag(click_point_[button], point) &&
       !controller()->GetToolbarModel()->WouldReplaceSearchURLWithSearchTerms(
           false)) {
     // Select all in the reverse direction so as not to scroll the caret
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_win.h b/chrome/browser/ui/views/omnibox/omnibox_view_win.h
index df1a239..64ec95b 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_win.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_win.h
@@ -74,8 +74,8 @@
 
   // OmniboxView:
   virtual void SaveStateToTab(content::WebContents* tab) OVERRIDE;
-  virtual void Update(
-      const content::WebContents* tab_for_state_restoring) OVERRIDE;
+  virtual void OnTabChanged(const content::WebContents* web_contents) OVERRIDE;
+  virtual void Update() OVERRIDE;
   virtual void OpenMatch(const AutocompleteMatch& match,
                          WindowOpenDisposition disposition,
                          const GURL& alternate_nav_url,
diff --git a/chrome/browser/ui/views/panels/panel_stack_view.cc b/chrome/browser/ui/views/panels/panel_stack_view.cc
index 90dcf48..f88b986 100644
--- a/chrome/browser/ui/views/panels/panel_stack_view.cc
+++ b/chrome/browser/ui/views/panels/panel_stack_view.cc
@@ -431,7 +431,7 @@
   // window on the taskbar.
   ::SetWindowLongPtr(native_panel_window,
                      GWLP_HWNDPARENT,
-                     reinterpret_cast<LONG>(native_stack_window));
+                     reinterpret_cast<LONG_PTR>(native_stack_window));
 
   // Make sure the background stack window always stays behind the panel window.
   if (native_stack_window) {
diff --git a/chrome/browser/ui/views/panels/panel_view.cc b/chrome/browser/ui/views/panels/panel_view.cc
index d122392..fb765f1 100644
--- a/chrome/browser/ui/views/panels/panel_view.cc
+++ b/chrome/browser/ui/views/panels/panel_view.cc
@@ -973,7 +973,7 @@
   if (focused_ && panel_->IsMinimized() &&
       panel_->collection()->type() == PanelCollection::DOCKED &&
       gfx::Screen::GetScreenFor(widget->GetNativeWindow())->
-          GetWindowAtCursorScreenPoint() != widget->GetNativeWindow()) {
+          GetWindowUnderCursor() != widget->GetNativeWindow()) {
     panel_->Restore();
   }
 #endif
diff --git a/chrome/browser/ui/views/select_file_dialog_extension.cc b/chrome/browser/ui/views/select_file_dialog_extension.cc
index 362024a..1f74de0 100644
--- a/chrome/browser/ui/views/select_file_dialog_extension.cc
+++ b/chrome/browser/ui/views/select_file_dialog_extension.cc
@@ -14,9 +14,8 @@
 #include "base/memory/singleton.h"
 #include "base/message_loop/message_loop.h"
 #include "chrome/browser/chromeos/extensions/file_manager/app_id.h"
-#include "chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.h"
-#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
 #include "chrome/browser/chromeos/extensions/file_manager/fileapi_util.h"
+#include "chrome/browser/chromeos/extensions/file_manager/select_file_dialog_util.h"
 #include "chrome/browser/chromeos/extensions/file_manager/url_util.h"
 #include "chrome/browser/extensions/extension_host.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -361,7 +360,7 @@
       kFileManagerWidth,
       kFileManagerHeight,
 #if defined(USE_AURA)
-      file_manager::util::GetTitleFromType(type),
+      file_manager::util::GetSelectFileDialogTitle(type),
 #else
       // HTML-based header used.
       string16(),
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc
index 931abef..3656bd5 100644
--- a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc
@@ -1,15 +1,20 @@
+
 // 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 "chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h"
 
-StatusIconLinuxWrapper::StatusIconLinuxWrapper(StatusIconLinux* status_icon) {
+StatusIconLinuxWrapper::StatusIconLinuxWrapper(StatusIconLinux* status_icon)
+    : menu_model_(NULL) {
   status_icon_.reset(status_icon);
   status_icon_->set_delegate(this);
 }
 
-StatusIconLinuxWrapper::~StatusIconLinuxWrapper() {}
+StatusIconLinuxWrapper::~StatusIconLinuxWrapper() {
+  if (menu_model_)
+    menu_model_->RemoveObserver(this);
+}
 
 void StatusIconLinuxWrapper::SetImage(const gfx::ImageSkia& image) {
   status_icon_->SetImage(image);
@@ -37,6 +42,10 @@
   return HasObservers();
 }
 
+void StatusIconLinuxWrapper::OnMenuStateChanged() {
+  status_icon_->RefreshPlatformContextMenu();
+}
+
 StatusIconLinuxWrapper* StatusIconLinuxWrapper::CreateWrappedStatusIcon(
     const gfx::ImageSkia& image,
     const string16& tool_tip) {
@@ -50,6 +59,15 @@
   return NULL;
 }
 
-void StatusIconLinuxWrapper::UpdatePlatformContextMenu(ui::MenuModel* model) {
+void StatusIconLinuxWrapper::UpdatePlatformContextMenu(
+    StatusIconMenuModel* model) {
+  // If a menu already exists, remove ourself from its oberver list.
+  if (menu_model_)
+    menu_model_->RemoveObserver(this);
+
   status_icon_->UpdatePlatformContextMenu(model);
+  menu_model_ = model;
+
+  if (model)
+    model->AddObserver(this);
 }
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h
index 153ce37..30ccf1f 100644
--- a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h
@@ -13,7 +13,8 @@
 // Wrapper class for StatusIconLinux that implements the standard StatusIcon
 // interface. Also handles callbacks from StatusIconLinux.
 class StatusIconLinuxWrapper : public StatusIcon,
-                               public StatusIconLinux::Delegate {
+                               public StatusIconLinux::Delegate,
+                               public StatusIconMenuModel::Observer {
  public:
   virtual ~StatusIconLinuxWrapper();
 
@@ -29,6 +30,9 @@
   virtual void OnClick() OVERRIDE;
   virtual bool HasClickAction() OVERRIDE;
 
+  // StatusIconMenuModel::Observer overrides:
+  virtual void OnMenuStateChanged() OVERRIDE;
+
   static StatusIconLinuxWrapper* CreateWrappedStatusIcon(
       const gfx::ImageSkia& image,
       const string16& tool_tip);
@@ -38,7 +42,8 @@
   // Invoked after a call to SetContextMenu() to let the platform-specific
   // subclass update the native context menu based on the new model. If NULL is
   // passed, subclass should destroy the native context menu.
-  virtual void UpdatePlatformContextMenu(ui::MenuModel* model) OVERRIDE;
+  virtual void UpdatePlatformContextMenu(
+      StatusIconMenuModel* model) OVERRIDE;
 
  private:
   // A status icon wrapper should only be created by calling
@@ -47,8 +52,11 @@
 
   // Notification balloon.
   DesktopNotificationBalloon notification_;
+
   scoped_ptr<StatusIconLinux> status_icon_;
 
+  StatusIconMenuModel* menu_model_;
+
   DISALLOW_COPY_AND_ASSIGN(StatusIconLinuxWrapper);
 };
 
diff --git a/chrome/browser/ui/views/status_icons/status_icon_win.cc b/chrome/browser/ui/views/status_icons/status_icon_win.cc
index a5af417..f49642e 100644
--- a/chrome/browser/ui/views/status_icons/status_icon_win.cc
+++ b/chrome/browser/ui/views/status_icons/status_icon_win.cc
@@ -147,7 +147,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 // StatusIconWin, private:
 
-void StatusIconWin::UpdatePlatformContextMenu(ui::MenuModel* menu) {
+void StatusIconWin::UpdatePlatformContextMenu(StatusIconMenuModel* menu) {
   // |menu_model_| is about to be destroyed. Destroy the menu (which closes it)
   // so that it doesn't attempt to continue using |menu_model_|.
   menu_runner_.reset();
@@ -210,7 +210,7 @@
   }
 }
 
-void StatusIconMetro::UpdatePlatformContextMenu(ui::MenuModel* menu) {
+void StatusIconMetro::UpdatePlatformContextMenu(StatusIconMenuModel* menu) {
   DVLOG(1) << __FUNCTION__
            << " This functionality is not supported in Windows 8 metro";
 }
diff --git a/chrome/browser/ui/views/status_icons/status_icon_win.h b/chrome/browser/ui/views/status_icons/status_icon_win.h
index 8f4cf1b..8576f5d 100644
--- a/chrome/browser/ui/views/status_icons/status_icon_win.h
+++ b/chrome/browser/ui/views/status_icons/status_icon_win.h
@@ -52,7 +52,8 @@
 
  protected:
   // Overridden from StatusIcon:
-  virtual void UpdatePlatformContextMenu(ui::MenuModel* menu) OVERRIDE;
+  virtual void UpdatePlatformContextMenu(
+      StatusIconMenuModel* menu) OVERRIDE;
 
  private:
   void InitIconData(NOTIFYICONDATA* icon_data);
@@ -96,7 +97,8 @@
                               const string16& title,
                               const string16& contents) OVERRIDE;
  protected:
-  virtual void UpdatePlatformContextMenu(ui::MenuModel* menu) OVERRIDE;
+  virtual void UpdatePlatformContextMenu(
+      StatusIconMenuModel* menu) OVERRIDE;
 
  private:
   string16 tool_tip_;
diff --git a/chrome/browser/ui/views/status_icons/status_tray_win_unittest.cc b/chrome/browser/ui/views/status_icons/status_tray_win_unittest.cc
index 15ce2f8..cbff6d1 100644
--- a/chrome/browser/ui/views/status_icons/status_tray_win_unittest.cc
+++ b/chrome/browser/ui/views/status_icons/status_tray_win_unittest.cc
@@ -8,11 +8,11 @@
 
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/status_icons/status_icon_menu_model.h"
 #include "chrome/browser/status_icons/status_icon_observer.h"
 #include "chrome/browser/ui/views/status_icons/status_icon_win.h"
 #include "grit/chrome_unscaled_resources.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/models/simple_menu_model.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/image/image_skia.h"
 
@@ -49,9 +49,9 @@
   StatusIcon* icon = tray.CreateStatusIcon(
       StatusTray::OTHER_ICON, *image, ASCIIToUTF16("tool tip"));
   icon->SetPressedImage(*image);
-  ui::SimpleMenuModel* menu = new ui::SimpleMenuModel(NULL);
+  scoped_ptr<StatusIconMenuModel> menu(new StatusIconMenuModel(NULL));
   menu->AddItem(0, L"foo");
-  icon->SetContextMenu(menu);
+  icon->SetContextMenu(menu.Pass());
 }
 
 #if !defined(USE_AURA)  // http://crbug.com/156370
diff --git a/chrome/browser/ui/views/sync/one_click_signin_bubble_view.cc b/chrome/browser/ui/views/sync/one_click_signin_bubble_view.cc
index 3e9efc6..97beda1 100644
--- a/chrome/browser/ui/views/sync/one_click_signin_bubble_view.cc
+++ b/chrome/browser/ui/views/sync/one_click_signin_bubble_view.cc
@@ -208,7 +208,7 @@
     layout->StartRow(0, COLUMN_SET_TITLE_BAR);
 
     views::Label* label = new views::Label(
-        l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_TITLE_NEW));
+        l10n_util::GetStringUTF16(IDS_ONE_CLICK_SIGNIN_DIALOG_TITLE));
     label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
     label->SetFont(label->font().DeriveFont(3, gfx::Font::BOLD));
     layout->AddView(label);
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
index 6aedb9b..b9ad12f 100644
--- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
+++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
@@ -26,6 +26,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
+#include "components/web_modal/web_contents_modal_dialog_manager.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/user_metrics.h"
 #include "content/public/browser/web_contents.h"
@@ -38,6 +39,7 @@
 
 using content::UserMetricsAction;
 using content::WebContents;
+using web_modal::WebContentsModalDialogManager;
 
 namespace {
 
@@ -364,6 +366,15 @@
       static_cast<int>(tabstrip_->layout_type()));
 }
 
+bool BrowserTabStripController::IsTabShowingWebViewModalDialog(int index) {
+  DCHECK(model_->ContainsIndex(index));
+  WebContentsModalDialogManager* contents_modal_dialog_manager =
+      WebContentsModalDialogManager::FromWebContents(
+          model_->GetWebContentsAt(index));
+  return contents_modal_dialog_manager &&
+      contents_modal_dialog_manager->IsShowingDialog();
+}
+
 void BrowserTabStripController::OnStartedDraggingTabs() {
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser_);
   if (browser_view && !immersive_reveal_lock_.get()) {
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h
index 62e5f8f..c761330 100644
--- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h
+++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h
@@ -71,6 +71,7 @@
   virtual void CreateNewTab() OVERRIDE;
   virtual bool IsIncognito() OVERRIDE;
   virtual void LayoutTypeMaybeChanged() OVERRIDE;
+  virtual bool IsTabShowingWebViewModalDialog(int index) OVERRIDE;
   virtual void OnStartedDraggingTabs() OVERRIDE;
   virtual void OnStoppedDraggingTabs() OVERRIDE;
 
diff --git a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
index 4100601..eaf03aa 100644
--- a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
+++ b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
@@ -115,6 +115,10 @@
 void FakeBaseTabStripController::LayoutTypeMaybeChanged() {
 }
 
+bool FakeBaseTabStripController::IsTabShowingWebViewModalDialog(int index) {
+  return false;
+}
+
 void FakeBaseTabStripController::OnStartedDraggingTabs() {
 }
 
diff --git a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h
index 12d4fa6..ff3d6f1 100644
--- a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h
+++ b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h
@@ -48,6 +48,7 @@
   virtual void CreateNewTab() OVERRIDE;
   virtual bool IsIncognito() OVERRIDE;
   virtual void LayoutTypeMaybeChanged() OVERRIDE;
+  virtual bool IsTabShowingWebViewModalDialog(int index) OVERRIDE;
   virtual void OnStartedDraggingTabs() OVERRIDE;
   virtual void OnStoppedDraggingTabs() OVERRIDE;
 
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
index ebb15ad..7ceb529 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -10,6 +10,7 @@
 #include "base/auto_reset.h"
 #include "base/callback.h"
 #include "base/command_line.h"
+#include "base/debug/alias.h"
 #include "base/i18n/rtl.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/extension_function_dispatcher.h"
@@ -375,11 +376,13 @@
       tab_strip_to_attach_to_after_exit_(NULL),
       move_loop_widget_(NULL),
       destroyed_(NULL),
-      is_mutating_(false) {
+      is_mutating_(false),
+      saving_focus_(false) {
   instance_ = this;
 }
 
 TabDragController::~TabDragController() {
+  CHECK(!saving_focus_);
   if (instance_ == this)
     instance_ = NULL;
 
@@ -746,6 +749,14 @@
   DCHECK(!old_focused_view_);  // This should only be invoked once.
   DCHECK(source_tabstrip_);
   old_focused_view_ = source_tabstrip_->GetFocusManager()->GetFocusedView();
+  // TODO(sky): used in tracking crash; see 275931.
+  const char* old_focused_view_class_name =
+      old_focused_view_ ? old_focused_view_->GetClassName() : NULL;
+  base::debug::Alias(old_focused_view_class_name);
+  const int old_focused_view_id =
+      old_focused_view_ ? old_focused_view_->id() : 0;
+  base::debug::Alias(&old_focused_view_id);
+  base::AutoReset<bool> setter(&saving_focus_, true);
   source_tabstrip_->GetFocusManager()->SetFocusedView(source_tabstrip_);
 }
 
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.h b/chrome/browser/ui/views/tabs/tab_drag_controller.h
index 9dc0bb2..e3c1f5d 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.h
@@ -618,6 +618,9 @@
   // See description above getter.
   bool is_mutating_;
 
+  // TODO(sky): used in tracking crash; see 275931.
+  bool saving_focus_;
+
   DISALLOW_COPY_AND_ASSIGN(TabDragController);
 };
 
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 321fe2c..6841fd9 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -1127,6 +1127,16 @@
       gfx::Screen::GetScreenFor(widget->GetNativeView())->GetNumDisplays() == 1)
     detach_behavior = TabDragController::NOT_DETACHABLE;
 
+#if defined(OS_CHROMEOS)
+  // TODO(wittman): This is a temporary workaround to avoid crbug.com/275274 and
+  // crbug.com/274856 in M30 and should be reverted after merge, as we have a
+  // solution to these bugs in trunk.
+  //
+  // Don't allow detaching if the tab has a WebContentsModalDialogView opened.
+  if (controller()->IsTabShowingWebViewModalDialog(GetModelIndexOfTab(tab)))
+    detach_behavior = TabDragController::NOT_DETACHABLE;
+#endif
+
 #if defined(OS_WIN)
   // It doesn't make sense to drag tabs out on Win8's single window Metro mode.
   if (win8::IsSingleWindowMetroMode())
diff --git a/chrome/browser/ui/views/tabs/tab_strip_controller.h b/chrome/browser/ui/views/tabs/tab_strip_controller.h
index 4e53c1b..3a11ae0 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_strip_controller.h
@@ -99,6 +99,10 @@
   // Invoked if the layout type might have changed.
   virtual void LayoutTypeMaybeChanged() = 0;
 
+  // Returns true if the tab at the given index is showing a web view modal
+  // dialog.
+  virtual bool IsTabShowingWebViewModalDialog(int index) = 0;
+
   // Notifies controller that the user started dragging this tabstrip's tabs.
   virtual void OnStartedDraggingTabs() = 0;
 
diff --git a/chrome/browser/ui/views/web_contents_modal_dialog_manager_views.cc b/chrome/browser/ui/views/web_contents_modal_dialog_manager_views.cc
index 9798a64..689066b 100644
--- a/chrome/browser/ui/views/web_contents_modal_dialog_manager_views.cc
+++ b/chrome/browser/ui/views/web_contents_modal_dialog_manager_views.cc
@@ -46,20 +46,12 @@
 class NativeWebContentsModalDialogManagerViews
     : public NativeWebContentsModalDialogManager,
       public WebContentsModalDialogHostObserver,
-      public views::WidgetObserver
-#if defined(USE_AURA)
-      , public aura::WindowObserver
-#endif
-                                   {
+      public views::WidgetObserver {
  public:
   NativeWebContentsModalDialogManagerViews(
       NativeWebContentsModalDialogManagerDelegate* native_delegate)
       : native_delegate_(native_delegate),
         host_(NULL) {
-#if defined(USE_AURA)
-    native_delegate_->GetWebContents()->GetView()->GetNativeView()->
-        AddObserver(this);
-#endif
   }
 
   virtual ~NativeWebContentsModalDialogManagerViews() {
@@ -205,40 +197,25 @@
 
     host_ = new_host;
 
-    if (host_) {
+    if (host_)
       host_->AddObserver(this);
+
+    // Old-style dialogs are parented to the web contents view and don't need
+    // reparenting.  The host_ may be null during tab drag under Views/Win32 or
+    // when destroying the WebContents.
+    if (views::DialogDelegate::UseNewStyle() && host_) {
+      for (std::set<views::Widget*>::iterator it = observed_widgets_.begin();
+           it != observed_widgets_.end();
+           ++it) {
+        views::Widget::ReparentNativeView((*it)->GetNativeView(),
+                                          host_->GetHostView());
+      }
+
+      OnPositionRequiresUpdate();
     }
   }
 
  private:
-#if defined(USE_AURA)
-  // aura::WindowObserver overrides
-  virtual void OnWindowHierarchyChanged(
-      const aura::WindowObserver::HierarchyChangeParams& params) OVERRIDE {
-    // We are called during the teardown of the WebContents' view by which time
-    // GetWebContents() will return NULL. We can safely ignore this case.
-    if (!native_delegate_->GetWebContents())
-      return;
-    if (params.target ==
-        native_delegate_->GetWebContents()->GetView()->GetNativeView()) {
-      std::set<views::Widget*>::const_iterator it = observed_widgets_.begin();
-      for (; it != observed_widgets_.end(); ++it) {
-        // WebContents Modal dialogs don't have their own focus manager, they
-        // rely on the focus manager of the attached frame. When we reparent
-        // them to a different window we need to update the original FM in case
-        // a view in the WCMD is focused to avoid crashing.
-        if ((*it)->GetFocusManager())
-          (*it)->GetFocusManager()->ViewRemoved((*it)->GetRootView());
-        params.new_parent->AddChild((*it)->GetNativeWindow());
-      }
-
-      // Host may be null when destroying the WebContents.
-      if (host_)
-        OnPositionRequiresUpdate();
-    }
-  }
-#endif
-
   static views::Widget* GetWidget(NativeWebContentsModalDialog dialog) {
     views::Widget* widget = views::Widget::GetWidgetForNativeWindow(dialog);
     DCHECK(widget);
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
index b398ef1..c3cb66c 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -340,7 +340,7 @@
   if (input_method.empty())
     return false;
 
-  if (!manager->IsFullLatinKeyboard(input_method)) {
+  if (!manager->IsLoginKeyboard(input_method)) {
     LOG(WARNING) << "SetUserInputMethod('" << username
                  << "'): stored user LRU input method '" << input_method
                  << "' is no longer Full Latin Keyboard Language"
@@ -933,7 +933,7 @@
 }
 
 void SigninScreenHandler::ShowSigninUI(const std::string& email) {
-
+  core_oobe_actor_->ShowSignInUI(email);
 }
 
 void SigninScreenHandler::ShowGaiaPasswordChanged(const std::string& username) {
@@ -1018,21 +1018,11 @@
   ShowSigninScreenIfReady();
 }
 
-void SigninScreenHandler::SetUserInputMethodHWDefault() {
-  chromeos::input_method::InputMethodManager* manager =
-      chromeos::input_method::InputMethodManager::Get();
-  manager->ChangeInputMethod(
-      manager->GetInputMethodUtil()->GetHardwareInputMethodId());
-}
-
 // Update keyboard layout to least recently used by the user.
 void SigninScreenHandler::SetUserInputMethod(const std::string& username) {
   chromeos::input_method::InputMethodManager* const manager =
       chromeos::input_method::InputMethodManager::Get();
 
-  const chromeos::input_method::InputMethodUtil& ime_util =
-      *manager->GetInputMethodUtil();
-
   const bool succeed = SetUserInputMethodImpl(username, manager);
 
   // This is also a case when LRU layout is set only for a few local users,
@@ -1040,10 +1030,9 @@
   // Otherwise they will end up using another user's locale to log in.
   if (!succeed) {
     DLOG(INFO) << "SetUserInputMethod('" << username
-               << "'): failed to set user layout. Switching to default '"
-               << ime_util.GetHardwareInputMethodId() << "'";
+               << "'): failed to set user layout. Switching to default.";
 
-    SetUserInputMethodHWDefault();
+    manager->SetInputMethodDefault();
   }
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
index 33d8915..df9bf26 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
@@ -387,9 +387,6 @@
   // Update current input method (namely keyboard layout) to LRU by this user.
   void SetUserInputMethod(const std::string& username);
 
-  // Update current input method to HW default.
-  void SetUserInputMethodHWDefault();
-
   // Current UI state of the signin screen.
   UIState ui_state_;
 
diff --git a/chrome/browser/ui/webui/chromeos/mobile_setup_dialog.cc b/chrome/browser/ui/webui/chromeos/mobile_setup_dialog.cc
index 634679d..1ad3328 100644
--- a/chrome/browser/ui/webui/chromeos/mobile_setup_dialog.cc
+++ b/chrome/browser/ui/webui/chromeos/mobile_setup_dialog.cc
@@ -23,15 +23,13 @@
 #include "ui/views/widget/widget.h"
 #include "ui/web_dialogs/web_dialog_delegate.h"
 
-using chromeos::CellularNetwork;
 using chromeos::MobileActivator;
 using content::BrowserThread;
 using content::WebContents;
 using content::WebUIMessageHandler;
 using ui::WebDialogDelegate;
 
-class MobileSetupDialogDelegate : public WebDialogDelegate,
-                                  public MobileActivator::Observer {
+class MobileSetupDialogDelegate : public WebDialogDelegate {
  public:
   static MobileSetupDialogDelegate* GetInstance();
   void ShowDialog(const std::string& service_path);
@@ -62,12 +60,6 @@
   virtual bool HandleContextMenu(
       const content::ContextMenuParams& params) OVERRIDE;
 
-  // MobileActivator::Observer overrides.
-  virtual void OnActivationStateChanged(
-      CellularNetwork* network,
-      MobileActivator::PlanActivationState state,
-      const std::string& error_description) OVERRIDE;
-
  private:
   gfx::NativeWindow dialog_window_;
   // Cellular network service path.
@@ -91,7 +83,6 @@
 }
 
 MobileSetupDialogDelegate::~MobileSetupDialogDelegate() {
-  MobileActivator::GetInstance()->RemoveObserver(this);
 }
 
 void MobileSetupDialogDelegate::ShowDialog(const std::string& service_path) {
@@ -142,12 +133,10 @@
 
 void MobileSetupDialogDelegate::OnDialogShown(
     content::WebUI* webui, content::RenderViewHost* render_view_host) {
-  MobileActivator::GetInstance()->AddObserver(this);
 }
 
 
 void MobileSetupDialogDelegate::OnDialogClosed(const std::string& json_retval) {
-  MobileActivator::GetInstance()->RemoveObserver(this);
   dialog_window_ = NULL;
 }
 
@@ -178,9 +167,3 @@
     const content::ContextMenuParams& params) {
   return true;
 }
-
-void MobileSetupDialogDelegate::OnActivationStateChanged(
-    CellularNetwork* network,
-    MobileActivator::PlanActivationState state,
-    const std::string& error_description) {
-}
diff --git a/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc b/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc
index b85a4b9..c34c8fa 100644
--- a/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc
@@ -19,13 +19,16 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
-#include "chrome/browser/chromeos/cros/network_library.h"
 #include "chrome/browser/chromeos/mobile/mobile_activator.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/render_messages.h"
 #include "chrome/common/url_constants.h"
+#include "chromeos/network/device_state.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
+#include "chromeos/network/network_state_handler_observer.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_view_host_observer.h"
 #include "content/public/browser/url_data_source.h"
@@ -36,15 +39,16 @@
 #include "grit/chromium_strings.h"
 #include "grit/generated_resources.h"
 #include "grit/locale_settings.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/webui/jstemplate_builder.h"
 #include "ui/webui/web_ui_util.h"
 #include "url/gurl.h"
 
-using chromeos::CellularNetwork;
 using chromeos::MobileActivator;
-using chromeos::NetworkLibrary;
+using chromeos::NetworkHandler;
+using chromeos::NetworkState;
 using content::BrowserThread;
 using content::RenderViewHost;
 using content::WebContents;
@@ -145,7 +149,7 @@
 class MobileSetupHandler
   : public WebUIMessageHandler,
     public MobileActivator::Observer,
-    public NetworkLibrary::NetworkManagerObserver,
+    public chromeos::NetworkStateHandlerObserver,
     public base::SupportsWeakPtr<MobileSetupHandler> {
  public:
   MobileSetupHandler();
@@ -167,9 +171,9 @@
     TYPE_PORTAL_LTE
   };
 
-  // Changes internal state.
+  // MobileActivator::Observer.
   virtual void OnActivationStateChanged(
-      CellularNetwork* network,
+      const NetworkState* network,
       MobileActivator::PlanActivationState new_state,
       const std::string& error_description) OVERRIDE;
 
@@ -179,21 +183,22 @@
   void HandlePaymentPortalLoad(const ListValue* args);
   void HandleGetDeviceInfo(const ListValue* args);
 
-  // NetworkLibrary::NetworkManagerObserver implementation.
-  virtual void OnNetworkManagerChanged(NetworkLibrary* network_lib) OVERRIDE;
+  // NetworkStateHandlerObserver implementation.
+  virtual void NetworkManagerChanged() OVERRIDE;
+  virtual void DefaultNetworkChanged(
+      const NetworkState* default_network) OVERRIDE;
 
   // Updates |lte_portal_reachable_| for lte network |network| and notifies
   // webui of the new state if the reachability changed or |force_notification|
   // is set.
-  void UpdatePortalReachability(NetworkLibrary* network_lib,
-                                CellularNetwork* network,
+  void UpdatePortalReachability(const NetworkState* network,
                                 bool force_notification);
 
   // Sends message to host registration page with system/user info data.
   void SendDeviceInfo();
 
   // Converts the currently active CellularNetwork device into a JS object.
-  static void GetDeviceInfo(CellularNetwork* network,
+  static void GetDeviceInfo(const NetworkState* network,
                             DictionaryValue* value);
 
   // Type of the mobilesetup webui deduced from received messages.
@@ -224,12 +229,15 @@
     int render_process_id,
     int render_view_id,
     const content::URLDataSource::GotDataCallback& callback) {
-  CellularNetwork* network = NULL;
+  const NetworkState* network = NULL;
   if (!path.empty()) {
-    network = NetworkLibrary::Get()-> FindCellularNetworkByPath(path);
+    network = NetworkHandler::Get()->network_state_handler()->GetNetworkState(
+        path);
   }
 
-  if (!network || (!network->SupportsActivation() && !network->activated())) {
+  if (!network ||
+      (network->payment_url().empty() && network->usage_url().empty() &&
+       network->activation_state() != flimflam::kActivationStateActivated)) {
     LOG(WARNING) << "Can't find device to activate for service path " << path;
     scoped_refptr<base::RefCountedBytes> html_bytes(new base::RefCountedBytes);
     callback.Run(html_bytes.get());
@@ -269,7 +277,7 @@
   // network is activated, the webui goes straight to portal. Otherwise the
   // webui is used for activation flow.
   std::string full_html;
-  if (network->activated()) {
+  if (network->activation_state() == flimflam::kActivationStateActivated) {
     static const base::StringPiece html_for_activated(
         ResourceBundle::GetSharedInstance().GetRawDataResource(
             IDR_MOBILE_SETUP_PORTAL_PAGE_HTML));
@@ -299,12 +307,13 @@
     MobileActivator::GetInstance()->RemoveObserver(this);
     MobileActivator::GetInstance()->TerminateActivation();
   } else if (type_ == TYPE_PORTAL_LTE) {
-    NetworkLibrary::Get()->RemoveNetworkManagerObserver(this);
+    NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
+                                                                   FROM_HERE);
   }
 }
 
 void MobileSetupHandler::OnActivationStateChanged(
-    CellularNetwork* network,
+    const NetworkState* network,
     MobileActivator::PlanActivationState state,
     const std::string& error_description) {
   DCHECK_EQ(TYPE_ACTIVATION, type_);
@@ -396,9 +405,11 @@
   if (path.empty())
     return;
 
-  NetworkLibrary* network_lib = NetworkLibrary::Get();
-  CellularNetwork* network =
-      network_lib->FindCellularNetworkByPath(path.substr(1));
+  chromeos::NetworkStateHandler* nsh =
+      NetworkHandler::Get()->network_state_handler();
+  // TODO: Figure out why the path has an extra '/' in the front. (e.g. It is
+  // '//service/5' instead of '/service/5'.
+  const NetworkState* network = nsh->GetNetworkState(path.substr(1));
   if (!network) {
     web_ui()->GetWebContents()->Close();
     return;
@@ -408,16 +419,15 @@
   // network changes, but only for LTE networks. The other networks should
   // ignore network status.
   if (type_ == TYPE_UNDETERMINED) {
-    if (network->network_technology() == chromeos::NETWORK_TECHNOLOGY_LTE ||
+    if (network->network_technology() == flimflam::kNetworkTechnologyLte ||
         network->network_technology() ==
-            chromeos::NETWORK_TECHNOLOGY_LTE_ADVANCED) {
+            flimflam::kNetworkTechnologyLteAdvanced) {
       type_ = TYPE_PORTAL_LTE;
-      network_lib->AddNetworkManagerObserver(this);
+      nsh->AddObserver(this, FROM_HERE);
       // Update the network status and notify the webui. This is the initial
       // network state so the webui should be notified no matter what.
-      UpdatePortalReachability(network_lib,
-                               network,
-                               true /*force notification*/);
+      UpdatePortalReachability(network,
+                               true /* force notification */);
     } else {
       type_ = TYPE_PORTAL;
       // For non-LTE networks network state is ignored, so report the portal is
@@ -432,7 +442,7 @@
   web_ui()->CallJavascriptFunction(kJsGetDeviceInfoCallback, device_info);
 }
 
-void MobileSetupHandler::OnNetworkManagerChanged(NetworkLibrary* network_lib) {
+void MobileSetupHandler::NetworkManagerChanged() {
   if (!web_ui())
     return;
 
@@ -440,27 +450,37 @@
   if (path.empty())
     return;
 
-  CellularNetwork* network =
-      network_lib->FindCellularNetworkByPath(path.substr(1));
+  const NetworkState* network =
+      NetworkHandler::Get()->network_state_handler()->GetNetworkState(
+          path.substr(1));
   if (!network) {
     LOG(ERROR) << "Service path lost";
     web_ui()->GetWebContents()->Close();
     return;
   }
 
-  UpdatePortalReachability(network_lib, network, false /*force notification*/);
+  UpdatePortalReachability(network,
+                           false /* do not force notification */);
 }
 
-void MobileSetupHandler::UpdatePortalReachability(NetworkLibrary* network_lib,
-                                                  CellularNetwork* network,
-                                                  bool force_notification) {
+void MobileSetupHandler::DefaultNetworkChanged(
+    const NetworkState* default_network) {
+  NetworkManagerChanged();
+}
+
+void MobileSetupHandler::UpdatePortalReachability(
+    const NetworkState* network,
+    bool force_notification) {
   DCHECK(web_ui());
 
   DCHECK_EQ(type_, TYPE_PORTAL_LTE);
 
-  bool portal_reachable = network->connected() ||
-                          (network_lib->connected_network() &&
-                           network_lib->connected_network()->online());
+  chromeos::NetworkStateHandler* nsh =
+      NetworkHandler::Get()->network_state_handler();
+  bool portal_reachable =
+      (network->IsConnectedState() ||
+       (nsh->DefaultNetwork() &&
+        nsh->DefaultNetwork()->connection_state() == flimflam::kStateOnline));
 
   if (force_notification || portal_reachable != lte_portal_reachable_) {
     web_ui()->CallJavascriptFunction(kJsConnectivityChangedCallback,
@@ -470,22 +490,20 @@
   lte_portal_reachable_ = portal_reachable;
 }
 
-void MobileSetupHandler::GetDeviceInfo(CellularNetwork* network,
+void MobileSetupHandler::GetDeviceInfo(const NetworkState* network,
                                        DictionaryValue* value) {
   DCHECK(network);
-  chromeos::NetworkLibrary* cros =
-      chromeos::NetworkLibrary::Get();
-  if (!cros)
-    return;
   value->SetBoolean("activate_over_non_cellular_network",
-                    network->activate_over_non_cellular_network());
+                    network->activate_over_non_cellular_networks());
   value->SetString("carrier", network->name());
   value->SetString("payment_url", network->payment_url());
-  if (network->using_post() && network->post_data().length())
+  if (LowerCaseEqualsASCII(network->post_method(), "post") &&
+      !network->post_data().empty())
     value->SetString("post_data", network->post_data());
 
-  const chromeos::NetworkDevice* device =
-      cros->FindNetworkDeviceByPath(network->device_path());
+  const chromeos::DeviceState* device =
+      NetworkHandler::Get()->network_state_handler()->GetDeviceState(
+          network->device_path());
   if (device) {
     value->SetString("MEID", device->meid());
     value->SetString("IMEI", device->imei());
diff --git a/chrome/browser/ui/webui/chromeos/network_ui.cc b/chrome/browser/ui/webui/chromeos/network_ui.cc
index 523a351..4478131 100644
--- a/chrome/browser/ui/webui/chromeos/network_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/network_ui.cc
@@ -78,7 +78,7 @@
 }
 
 std::string NetworkMessageHandler::GetNetworkEventLog() const {
-  std::string format = "time,desc,json";
+  std::string format = "json";
   return chromeos::network_event_log::GetAsString(
       chromeos::network_event_log::NEWEST_FIRST,
       format,
@@ -121,6 +121,8 @@
   html->AddLocalizedString("logLevelUserText", IDS_NETWORK_LOG_LEVEL_USER);
   html->AddLocalizedString("logLevelEventText", IDS_NETWORK_LOG_LEVEL_EVENT);
   html->AddLocalizedString("logLevelDebugText", IDS_NETWORK_LOG_LEVEL_DEBUG);
+  html->AddLocalizedString("logLevelFileinfoText",
+                           IDS_NETWORK_LOG_LEVEL_FILEINFO);
   html->AddLocalizedString("logEntryFormat", IDS_NETWORK_LOG_ENTRY);
   html->SetJsonPath(kStringsJsFile);
 
diff --git a/chrome/browser/ui/webui/devtools_ui.cc b/chrome/browser/ui/webui/devtools_ui.cc
index 0d70e13..0a71d19 100644
--- a/chrome/browser/ui/webui/devtools_ui.cc
+++ b/chrome/browser/ui/webui/devtools_ui.cc
@@ -40,7 +40,8 @@
 
 #if defined(DEBUG_DEVTOOLS)
 // Local frontend url provided by InspectUI.
-const char kLocalFrontendURLPrefix[] = "https://localhost:9222/";
+const char kLocalFrontendURL[] =
+    "chrome-devtools://devtools/bundled/devtools.html";
 #endif  // defined(DEBUG_DEVTOOLS)
 
 class FetchRequest : public net::URLFetcherDelegate {
@@ -190,19 +191,8 @@
 GURL DevToolsUI::GetProxyURL(const std::string& frontend_url) {
   GURL url(frontend_url);
 #if defined(DEBUG_DEVTOOLS)
-  if (frontend_url.find(kLocalFrontendURLPrefix) == 0) {
-    std::string path = url.path();
-    std::string local_path_prefix = "/";
-    local_path_prefix += chrome::kChromeUIDevToolsHost;
-    local_path_prefix += "/";
-    if (StartsWithASCII(path, local_path_prefix, false)) {
-      std::string local_path = path.substr(local_path_prefix.length());
-      return GURL(base::StringPrintf("%s://%s/%s/%s",
-                                     chrome::kChromeDevToolsScheme,
-                                     chrome::kChromeUIDevToolsHost,
-                                     chrome::kChromeUIDevToolsBundledPath,
-                                     local_path.c_str()));
-    }
+  if (!url.is_valid() || url.host() != kRemoteFrontendDomain) {
+    return GURL(kLocalFrontendURL);
   }
 #endif  // defined(DEBUG_DEVTOOLS)
   CHECK(url.is_valid());
diff --git a/chrome/browser/ui/webui/downloads_dom_handler.cc b/chrome/browser/ui/webui/downloads_dom_handler.cc
index 028dc58..562856f 100644
--- a/chrome/browser/ui/webui/downloads_dom_handler.cc
+++ b/chrome/browser/ui/webui/downloads_dom_handler.cc
@@ -31,7 +31,7 @@
 #include "chrome/browser/download/download_query.h"
 #include "chrome/browser/download/download_service.h"
 #include "chrome/browser/download/download_service_factory.h"
-#include "chrome/browser/download/download_util.h"
+#include "chrome/browser/download/drag_download_item.h"
 #include "chrome/browser/extensions/api/downloads/downloads_api.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_system.h"
@@ -405,7 +405,7 @@
     // Enable nested tasks during DnD, while |DragDownload()| blocks.
     base::MessageLoop::ScopedNestableTaskAllower allow(
         base::MessageLoop::current());
-    download_util::DragDownload(file, icon, view);
+    DragDownloadItem(file, icon, view);
   }
 }
 
diff --git a/chrome/browser/ui/webui/extensions/OWNERS b/chrome/browser/ui/webui/extensions/OWNERS
new file mode 100644
index 0000000..b41144f
--- /dev/null
+++ b/chrome/browser/ui/webui/extensions/OWNERS
@@ -0,0 +1 @@
+finnur@chromium.org
diff --git a/chrome/browser/ui/webui/extensions/extension_settings_handler.cc b/chrome/browser/ui/webui/extensions/extension_settings_handler.cc
index 37c8d21..dacfd43 100644
--- a/chrome/browser/ui/webui/extensions/extension_settings_handler.cc
+++ b/chrome/browser/ui/webui/extensions/extension_settings_handler.cc
@@ -14,6 +14,8 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop.h"
 #include "base/prefs/pref_service.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -318,8 +320,6 @@
       l10n_util::GetStringUTF16(IDS_EXTENSIONS_RELOAD_TERMINATED));
   source->AddString("extensionSettingsLaunch",
       l10n_util::GetStringUTF16(IDS_EXTENSIONS_LAUNCH));
-  source->AddString("extensionSettingsRestart",
-      l10n_util::GetStringUTF16(IDS_EXTENSIONS_RESTART));
   source->AddString("extensionSettingsReloadUnpacked",
       l10n_util::GetStringUTF16(IDS_EXTENSIONS_RELOAD_UNPACKED));
   source->AddString("extensionSettingsOptions",
@@ -418,9 +418,6 @@
   web_ui()->RegisterMessageCallback("extensionSettingsLaunch",
       base::Bind(&ExtensionSettingsHandler::HandleLaunchMessage,
                  base::Unretained(this)));
-  web_ui()->RegisterMessageCallback("extensionSettingsRestart",
-      base::Bind(&ExtensionSettingsHandler::HandleRestartMessage,
-                 base::Unretained(this)));
   web_ui()->RegisterMessageCallback("extensionSettingsReload",
       base::Bind(&ExtensionSettingsHandler::HandleReloadMessage,
                  base::Unretained(this)));
@@ -499,6 +496,15 @@
     case chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED:
       MaybeUpdateAfterNotification();
       break;
+    case chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED:
+       // This notification is sent when the extension host destruction begins,
+       // not when it finishes. We use PostTask to delay the update until after
+       // the destruction finishes.
+       base::MessageLoop::current()->PostTask(
+           FROM_HERE,
+           base::Bind(&ExtensionSettingsHandler::MaybeUpdateAfterNotification,
+                      base::Unretained(this)));
+       break;
     default:
       NOTREACHED();
   }
@@ -721,15 +727,6 @@
                                                   NEW_WINDOW));
 }
 
-void ExtensionSettingsHandler::HandleRestartMessage(
-    const base::ListValue* args) {
-  CHECK_EQ(1U, args->GetSize());
-  std::string extension_id;
-  CHECK(args->GetString(0, &extension_id));
-  apps::AppLoadService::Get(extension_service_->profile())->RestartApplication(
-      extension_id);
-}
-
 void ExtensionSettingsHandler::HandleReloadMessage(
     const base::ListValue* args) {
   std::string extension_id = UTF16ToUTF8(ExtractStringValue(args));
@@ -772,10 +769,6 @@
                      AsWeakPtr(), extension_id));
     } else {
       extension_service_->EnableExtension(extension_id);
-
-      // Make sure any browser action contained within it is not hidden.
-      ExtensionActionAPI::SetBrowserActionVisibility(
-          prefs, extension->id(), true);
     }
   } else {
     extension_service_->DisableExtension(
@@ -984,6 +977,9 @@
       chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
       content::Source<ExtensionPrefs>(
           profile->GetExtensionService()->extension_prefs()));
+  registrar_.Add(this,
+                 chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
+                 content::NotificationService::AllBrowserContextsAndSources());
 
   content::RenderViewHost::AddCreatedCallback(rvh_created_callback_);
 
@@ -1009,6 +1005,7 @@
   ExtensionProcessManager* process_manager =
       ExtensionSystem::Get(extension_service_->profile())->process_manager();
   GetInspectablePagesForExtensionProcess(
+      extension,
       process_manager->GetRenderViewHostsForExtension(extension->id()),
       &result);
 
@@ -1036,6 +1033,7 @@
         ExtensionSystem::Get(extension_service_->profile()->
             GetOffTheRecordProfile())->process_manager();
     GetInspectablePagesForExtensionProcess(
+        extension,
         process_manager->GetRenderViewHostsForExtension(extension->id()),
         &result);
 
@@ -1055,8 +1053,11 @@
 }
 
 void ExtensionSettingsHandler::GetInspectablePagesForExtensionProcess(
+    const Extension* extension,
     const std::set<RenderViewHost*>& views,
     std::vector<ExtensionPage>* result) {
+  bool has_generated_background_page =
+      BackgroundInfo::HasGeneratedBackgroundPage(extension);
   for (std::set<RenderViewHost*>::const_iterator iter = views.begin();
        iter != views.end(); ++iter) {
     RenderViewHost* host = *iter;
@@ -1069,9 +1070,14 @@
 
     GURL url = web_contents->GetURL();
     content::RenderProcessHost* process = host->GetProcess();
+    bool is_background_page =
+        (url == BackgroundInfo::GetBackgroundURL(extension));
     result->push_back(
-        ExtensionPage(url, process->GetID(), host->GetRoutingID(),
-                      process->GetBrowserContext()->IsOffTheRecord(), false));
+        ExtensionPage(url,
+                      process->GetID(),
+                      host->GetRoutingID(),
+                      process->GetBrowserContext()->IsOffTheRecord(),
+                      is_background_page && has_generated_background_page));
   }
 }
 
@@ -1085,16 +1091,22 @@
   const apps::ShellWindowRegistry::ShellWindowList windows =
       registry->GetShellWindowsForApp(extension->id());
 
+  bool has_generated_background_page =
+      BackgroundInfo::HasGeneratedBackgroundPage(extension);
   for (apps::ShellWindowRegistry::const_iterator it = windows.begin();
        it != windows.end(); ++it) {
     WebContents* web_contents = (*it)->web_contents();
     RenderViewHost* host = web_contents->GetRenderViewHost();
     content::RenderProcessHost* process = host->GetProcess();
 
+    bool is_background_page =
+        (web_contents->GetURL() == BackgroundInfo::GetBackgroundURL(extension));
     result->push_back(
-        ExtensionPage(web_contents->GetURL(), process->GetID(),
+        ExtensionPage(web_contents->GetURL(),
+                      process->GetID(),
                       host->GetRoutingID(),
-                      process->GetBrowserContext()->IsOffTheRecord(), false));
+                      process->GetBrowserContext()->IsOffTheRecord(),
+                      is_background_page && has_generated_background_page));
   }
 }
 
diff --git a/chrome/browser/ui/webui/extensions/extension_settings_handler.h b/chrome/browser/ui/webui/extensions/extension_settings_handler.h
index 07d45ce..17bf2f5 100644
--- a/chrome/browser/ui/webui/extensions/extension_settings_handler.h
+++ b/chrome/browser/ui/webui/extensions/extension_settings_handler.h
@@ -146,9 +146,6 @@
   // Callback for "launch" message.
   void HandleLaunchMessage(const ListValue* args);
 
-  // Callback for "restart" message.
-  void HandleRestartMessage(const ListValue* args);
-
   // Callback for "reload" message.
   void HandleReloadMessage(const base::ListValue* args);
 
@@ -196,6 +193,7 @@
   std::vector<ExtensionPage> GetInspectablePagesForExtension(
       const Extension* extension, bool extension_is_enabled);
   void GetInspectablePagesForExtensionProcess(
+      const Extension* extension,
       const std::set<content::RenderViewHost*>& views,
       std::vector<ExtensionPage>* result);
   void GetShellWindowPagesForExtensionProfile(
diff --git a/chrome/browser/ui/webui/inspect_ui.cc b/chrome/browser/ui/webui/inspect_ui.cc
index d580104..f1e3d5e 100644
--- a/chrome/browser/ui/webui/inspect_ui.cc
+++ b/chrome/browser/ui/webui/inspect_ui.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/ui/webui/theme_source.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
+#include "content/public/browser/browser_child_process_observer.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/child_process_data.h"
 #include "content/public/browser/devtools_agent_host.h"
@@ -74,6 +75,7 @@
 
 static const char kInitUICommand[]  = "init-ui";
 static const char kInspectCommand[]  = "inspect";
+static const char kActivateCommand[]  = "activate";
 static const char kTerminateCommand[]  = "terminate";
 static const char kReloadCommand[]  = "reload";
 static const char kOpenCommand[]  = "open";
@@ -134,6 +136,15 @@
   return agent->IsAttached();
 }
 
+bool HasClientHost(int process_id, int route_id) {
+  if (!DevToolsAgentHost::HasForWorker(process_id, route_id))
+    return false;
+
+  scoped_refptr<DevToolsAgentHost> agent(
+      DevToolsAgentHost::GetForWorker(process_id, route_id));
+  return agent->IsAttached();
+}
+
 DictionaryValue* BuildTargetDescriptor(RenderViewHost* rvh, bool is_tab) {
   WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
   std::string title;
@@ -188,6 +199,7 @@
 
   void HandleInitUICommand(const ListValue* args);
   void HandleInspectCommand(const ListValue* args);
+  void HandleActivateCommand(const ListValue* args);
   void HandleTerminateCommand(const ListValue* args);
   void HandleReloadCommand(const ListValue* args);
   void HandleOpenCommand(const ListValue* args);
@@ -212,6 +224,9 @@
   web_ui()->RegisterMessageCallback(kInspectCommand,
       base::Bind(&InspectMessageHandler::HandleInspectCommand,
                  base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(kActivateCommand,
+      base::Bind(&InspectMessageHandler::HandleActivateCommand,
+                 base::Unretained(this)));
   web_ui()->RegisterMessageCallback(kTerminateCommand,
       base::Bind(&InspectMessageHandler::HandleTerminateCommand,
                  base::Unretained(this)));
@@ -265,6 +280,12 @@
   DevToolsWindow::OpenDevToolsWindowForWorker(profile, agent_host.get());
 }
 
+void InspectMessageHandler::HandleActivateCommand(const ListValue* args) {
+  std::string page_id;
+  if (GetRemotePageId(args, &page_id))
+    inspect_ui_->ActivateRemotePage(page_id);
+}
+
 static void TerminateWorker(int process_id, int route_id) {
   WorkerService::GetInstance()->TerminateWorker(process_id, route_id);
 }
@@ -351,6 +372,7 @@
 
 class InspectUI::WorkerCreationDestructionListener
     : public WorkerServiceObserver,
+      public content::BrowserChildProcessObserver,
       public base::RefCountedThreadSafe<WorkerCreationDestructionListener> {
  public:
   WorkerCreationDestructionListener()
@@ -360,6 +382,7 @@
     DCHECK(workers_ui);
     DCHECK(!discovery_ui_);
     discovery_ui_ = workers_ui;
+    BrowserChildProcessObserver::Add(this);
     BrowserThread::PostTask(
         BrowserThread::IO, FROM_HERE,
         base::Bind(&WorkerCreationDestructionListener::RegisterObserver,
@@ -369,13 +392,14 @@
   void InspectUIDestroyed() {
     DCHECK(discovery_ui_);
     discovery_ui_ = NULL;
+    BrowserChildProcessObserver::Remove(this);
     BrowserThread::PostTask(
         BrowserThread::IO, FROM_HERE,
         base::Bind(&WorkerCreationDestructionListener::UnregisterObserver,
                    this));
   }
 
-  void InitUI() {
+  void UpdateUI() {
     BrowserThread::PostTask(
         BrowserThread::IO, FROM_HERE,
         base::Bind(&WorkerCreationDestructionListener::CollectWorkersData,
@@ -398,28 +422,24 @@
     CollectWorkersData();
   }
 
+  virtual void BrowserChildProcessHostConnected(
+      const content::ChildProcessData& data) OVERRIDE {
+    if (data.process_type == content::PROCESS_TYPE_WORKER)
+      UpdateUI();
+  }
+
+  virtual void BrowserChildProcessHostDisconnected(
+      const content::ChildProcessData& data) OVERRIDE {
+    if (data.process_type == content::PROCESS_TYPE_WORKER)
+      UpdateUI();
+  }
+
   void CollectWorkersData() {
     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-    scoped_ptr<ListValue> target_list(new ListValue());
-    std::vector<WorkerService::WorkerInfo> worker_info =
-        WorkerService::GetInstance()->GetWorkers();
-    for (size_t i = 0; i < worker_info.size(); ++i) {
-      target_list->Append(BuildTargetDescriptor(
-          kWorkerTargetType,
-          false,
-          worker_info[i].url,
-          UTF16ToUTF8(worker_info[i].name),
-          GURL(),
-          "",
-          worker_info[i].process_id,
-          worker_info[i].route_id,
-          worker_info[i].handle));
-    }
-
     BrowserThread::PostTask(
         BrowserThread::UI, FROM_HERE,
         base::Bind(&WorkerCreationDestructionListener::PopulateWorkersList,
-                   this, base::Owned(target_list.release())));
+                   this, WorkerService::GetInstance()->GetWorkers()));
   }
 
   void RegisterObserver() {
@@ -430,12 +450,29 @@
     WorkerService::GetInstance()->RemoveObserver(this);
   }
 
-  void PopulateWorkersList(ListValue* target_list) {
+  void PopulateWorkersList(
+      const std::vector<WorkerService::WorkerInfo>& worker_info) {
     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-    if (discovery_ui_) {
-      discovery_ui_->web_ui()->CallJavascriptFunction(
-          "populateWorkersList", *target_list);
+    if (!discovery_ui_)
+      return;
+
+    ListValue target_list;
+    for (size_t i = 0; i < worker_info.size(); ++i) {
+      if (!worker_info[i].handle)
+        continue;  // Process is still being created.
+      target_list.Append(BuildTargetDescriptor(
+          kWorkerTargetType,
+          HasClientHost(worker_info[i].process_id, worker_info[i].route_id),
+          worker_info[i].url,
+          UTF16ToUTF8(worker_info[i].name),
+          GURL(),
+          "",
+          worker_info[i].process_id,
+          worker_info[i].route_id,
+          worker_info[i].handle));
     }
+    discovery_ui_->web_ui()->CallJavascriptFunction(
+        "populateWorkersList", target_list);
   }
 
   InspectUI* discovery_ui_;
@@ -461,7 +498,7 @@
   PopulateLists();
   UpdatePortForwardingEnabled();
   UpdatePortForwardingConfig();
-  observer_->InitUI();
+  observer_->UpdateUI();
 }
 
 void InspectUI::InspectRemotePage(const std::string& id) {
@@ -472,6 +509,12 @@
   }
 }
 
+void InspectUI::ActivateRemotePage(const std::string& id) {
+  RemotePages::iterator it = remote_pages_.find(id);
+  if (it != remote_pages_.end())
+    it->second->Activate();
+}
+
 void InspectUI::ReloadRemotePage(const std::string& id) {
   RemotePages::iterator it = remote_pages_.find(id);
   if (it != remote_pages_.end())
diff --git a/chrome/browser/ui/webui/inspect_ui.h b/chrome/browser/ui/webui/inspect_ui.h
index 9647b76..1d9b56b 100644
--- a/chrome/browser/ui/webui/inspect_ui.h
+++ b/chrome/browser/ui/webui/inspect_ui.h
@@ -25,6 +25,7 @@
 
   void InitUI();
   void InspectRemotePage(const std::string& page_id);
+  void ActivateRemotePage(const std::string& page_id);
   void CloseRemotePage(const std::string& page_id);
   void ReloadRemotePage(const std::string& page_id);
   void OpenRemotePage(const std::string& browser_id, const std::string& url);
diff --git a/chrome/browser/ui/webui/local_discovery/local_discovery_ui.cc b/chrome/browser/ui/webui/local_discovery/local_discovery_ui.cc
index 8906099..e6a63d1 100644
--- a/chrome/browser/ui/webui/local_discovery/local_discovery_ui.cc
+++ b/chrome/browser/ui/webui/local_discovery/local_discovery_ui.cc
@@ -23,33 +23,36 @@
   source->AddResourcePath("local_discovery.js", IDR_LOCAL_DISCOVERY_JS);
 
   source->SetUseJsonJSFormatV2();
-  source->AddLocalizedString("serviceName",
-                             IDS_LOCAL_DISCOVERY_SERVICE_NAME);
-  source->AddLocalizedString("serviceDomain",
-                             IDS_LOCAL_DISCOVERY_SERVICE_DOMAIN);
-  source->AddLocalizedString("servicePort",
-                             IDS_LOCAL_DISCOVERY_SERVICE_PORT);
-  source->AddLocalizedString("serviceIp",
-                             IDS_LOCAL_DISCOVERY_SERVICE_IP);
-  source->AddLocalizedString("serviceLastseen",
-                             IDS_LOCAL_DISCOVERY_SERVICE_LASTSEEN);
   source->AddLocalizedString("serviceRegister",
                              IDS_LOCAL_DISCOVERY_SERVICE_REGISTER);
-  source->AddLocalizedString("registeringService",
-                             IDS_LOCAL_DISCOVERY_REGISTERING_SERVICE);
-  source->AddLocalizedString("registrationFailed",
-                             IDS_LOCAL_DISCOVERY_REGISTRATION_FAILED);
-  source->AddLocalizedString("registrationSucceeded",
-                             IDS_LOCAL_DISCOVERY_REGISTRATION_SUCCEEDED);
-  source->AddLocalizedString("registered",
-                             IDS_LOCAL_DISCOVERY_REGISTERED);
-  source->AddLocalizedString("infoStarted", IDS_LOCAL_DISCOVERY_INFO_STARTED);
-  source->AddLocalizedString("infoFailed", IDS_LOCAL_DISCOVERY_INFO_FAILED);
-  source->AddLocalizedString("serviceInfo",
-                             IDS_LOCAL_DISCOVERY_SERVICE_INFO);
+
+  source->AddLocalizedString("registerConfirmMessage",
+                             IDS_LOCAL_DISCOVERY_REGISTER_CONFIRMATION);
+  source->AddLocalizedString("registerUser",
+                             IDS_LOCAL_DISCOVERY_REGISTER_USER);
+  source->AddLocalizedString("confirmRegistration",
+                             IDS_LOCAL_DISCOVERY_CONFIRM_REGISTRATION);
+  source->AddLocalizedString("addingPrinter",
+                             IDS_LOCAL_DISCOVERY_ADDING_PRINTER);
+  source->AddLocalizedString("addingError",
+                             IDS_LOCAL_DISCOVERY_ERROR_OCURRED);
+  source->AddLocalizedString("addingErrorMessage",
+                             IDS_LOCAL_DISCOVERY_ERROR_OCURRED_MESSAGE);
+  source->AddLocalizedString("addingMessage1",
+                             IDS_LOCAL_DISCOVERY_ADDING_PRINTER_MESSAGE1);
+  source->AddLocalizedString("addingMessage2",
+                             IDS_LOCAL_DISCOVERY_ADDING_PRINTER_MESSAGE2);
+  source->AddLocalizedString("registeredDevicesTitle",
+                             IDS_LOCAL_DISCOVERY_REGISTERED_DEVICES_TITLE);
+  source->AddLocalizedString("unregisteredDevicesTitle",
+                             IDS_LOCAL_DISCOVERY_UNREGISTERED_DEVICES_TITLE);
+  source->AddLocalizedString("devicesTitle",
+                             IDS_LOCAL_DISCOVERY_DEVICES_PAGE_TITLE);
 
   source->SetJsonPath("strings.js");
 
+  source->DisableDenyXFrameOptions();
+
   return source;
 }
 
diff --git a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc
index 8282ece..9e53316 100644
--- a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_browsertest.cc
@@ -160,7 +160,7 @@
 IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest, EmptyTest) {
   ui_test_utils::NavigateToURL(browser(), GURL(kChromeDevicesPage));
   condition_devices_listed().Wait();
-  EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkTableHasNoRows"));
+  EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkNoDevices"));
 }
 
 IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest, AddRowTest) {
@@ -169,21 +169,18 @@
   DeviceDescription description;
 
   description.id = kSampleDeviceID;
-  description.address = net::HostPortPair(kSampleDeviceHost, 8888);
-  description.ip_address.push_back(1);
-  description.ip_address.push_back(2);
-  description.ip_address.push_back(3);
-  description.ip_address.push_back(4);
+  description.name = "Sample device";
+  description.description = "Sample device description";
 
   ui_factory()->privet_lister()->delegate()->DeviceChanged(
       true, kSampleServiceName, description);
 
-  EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkTableHasOneRow"));
+  EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkOneDevice"));
 
   ui_factory()->privet_lister()->delegate()->DeviceRemoved(
       kSampleServiceName);
 
-  EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkTableHasNoRows"));
+  EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkNoDevices"));
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc
index 0d26427..f58167b 100644
--- a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc
+++ b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc
@@ -26,11 +26,13 @@
 // TODO(noamsml): This is a temporary shim until automated_url is in the
 // response.
 const char kPrivetAutomatedClaimURLFormat[] = "%s/confirm?token=%s";
+const int kAccountIndexUseOAuth2 = -1;
 
 LocalDiscoveryUIHandler::Factory* g_factory = NULL;
+int g_num_visible = 0;
 }  // namespace
 
-LocalDiscoveryUIHandler::LocalDiscoveryUIHandler() {
+LocalDiscoveryUIHandler::LocalDiscoveryUIHandler() : is_visible_(false) {
 }
 
 LocalDiscoveryUIHandler::LocalDiscoveryUIHandler(
@@ -39,6 +41,7 @@
 }
 
 LocalDiscoveryUIHandler::~LocalDiscoveryUIHandler() {
+  SetIsVisible(false);
   if (service_discovery_client_.get()) {
     service_discovery_client_ = NULL;
     ServiceDiscoveryHostClientFactory::ReleaseClient();
@@ -56,15 +59,23 @@
   g_factory = factory;
 }
 
+// static
+bool LocalDiscoveryUIHandler::GetHasVisible() {
+  return g_num_visible != 0;
+}
+
 void LocalDiscoveryUIHandler::RegisterMessages() {
   web_ui()->RegisterMessageCallback("start", base::Bind(
       &LocalDiscoveryUIHandler::HandleStart,
       base::Unretained(this)));
+  web_ui()->RegisterMessageCallback("isVisible", base::Bind(
+      &LocalDiscoveryUIHandler::HandleIsVisible,
+      base::Unretained(this)));
   web_ui()->RegisterMessageCallback("registerDevice", base::Bind(
       &LocalDiscoveryUIHandler::HandleRegisterDevice,
       base::Unretained(this)));
-  web_ui()->RegisterMessageCallback("info", base::Bind(
-      &LocalDiscoveryUIHandler::HandleInfoRequested,
+  web_ui()->RegisterMessageCallback("chooseUser", base::Bind(
+      &LocalDiscoveryUIHandler::HandleChooseUser,
       base::Unretained(this)));
 }
 
@@ -91,95 +102,108 @@
   bool rv = args->GetString(0, &device_name);
   DCHECK(rv);
 
-  privet_resolution_ = privet_http_factory_->CreatePrivetHTTP(
-      device_name,
-      device_descriptions_[device_name].address,
-      base::Bind(&LocalDiscoveryUIHandler::StartRegisterHTTP,
-                 base::Unretained(this)));
-  privet_resolution_->Start();
+  current_register_device_ = device_name;
+
+  cloud_print_account_manager_.reset(new CloudPrintAccountManager(
+      Profile::FromWebUI(web_ui())->GetRequestContext(),
+      GetCloudPrintBaseUrl(device_name),
+      0 /* Get XSRF token for primary user */,
+      base::Bind(&LocalDiscoveryUIHandler::OnCloudPrintAccountsResolved,
+                 base::Unretained(this))));
+
+  cloud_print_account_manager_->Start();
 }
 
-void LocalDiscoveryUIHandler::HandleInfoRequested(const base::ListValue* args) {
-  std::string device_name;
-  args->GetString(0, &device_name);
+void LocalDiscoveryUIHandler::HandleIsVisible(const base::ListValue* args) {
+  bool is_visible = false;
+  bool rv = args->GetBoolean(0, &is_visible);
+  DCHECK(rv);
+  SetIsVisible(is_visible);
+}
+
+void LocalDiscoveryUIHandler::HandleChooseUser(const base::ListValue* args) {
+  std::string user;
+
+  bool rv = args->GetInteger(0, &current_register_user_index_);
+  DCHECK(rv);
+  rv = args->GetString(1, &user);
+  DCHECK(rv);
 
   privet_resolution_ = privet_http_factory_->CreatePrivetHTTP(
-      device_name,
-      device_descriptions_[device_name].address,
-      base::Bind(&LocalDiscoveryUIHandler::StartInfoHTTP,
-                 base::Unretained(this)));
+      current_register_device_,
+      device_descriptions_[current_register_device_].address,
+      base::Bind(&LocalDiscoveryUIHandler::StartRegisterHTTP,
+                 base::Unretained(this), user));
   privet_resolution_->Start();
 }
 
 void LocalDiscoveryUIHandler::StartRegisterHTTP(
+    const std::string& user,
     scoped_ptr<PrivetHTTPClient> http_client) {
   current_http_client_.swap(http_client);
 
   if (!current_http_client_) {
-    LogRegisterErrorToWeb("Resolution failed");
+    SendRegisterError();
     return;
   }
 
-  Profile* profile = Profile::FromWebUI(web_ui());
-  SigninManagerBase* signin_manager =
-      SigninManagerFactory::GetForProfileIfExists(profile);
-
-  if (!signin_manager) {
-    LogRegisterErrorToWeb("You must be signed in");
-    return;
-  }
-
-  std::string username = signin_manager->GetAuthenticatedUsername();
-
   current_register_operation_ =
-      current_http_client_->CreateRegisterOperation(username, this);
+      current_http_client_->CreateRegisterOperation(user, this);
   current_register_operation_->Start();
 }
 
-void LocalDiscoveryUIHandler::StartInfoHTTP(
-    scoped_ptr<PrivetHTTPClient> http_client) {
-  current_http_client_.swap(http_client);
-  if (!current_http_client_) {
-    LogRegisterErrorToWeb("Resolution failed");
-    return;
-  }
-
-  current_info_operation_ = current_http_client_->CreateInfoOperation(this);
-  current_info_operation_->Start();
-}
-
 void LocalDiscoveryUIHandler::OnPrivetRegisterClaimToken(
     PrivetRegisterOperation* operation,
     const std::string& token,
     const GURL& url) {
+  web_ui()->CallJavascriptFunction(
+      "local_discovery.registrationConfirmedOnPrinter");
   if (device_descriptions_.count(current_http_client_->GetName()) == 0) {
-    LogRegisterErrorToWeb("Device no longer exists");
+    SendRegisterError();
     return;
   }
 
+  std::string base_url = GetCloudPrintBaseUrl(current_http_client_->GetName());
+
   GURL automated_claim_url(base::StringPrintf(
       kPrivetAutomatedClaimURLFormat,
-      device_descriptions_[current_http_client_->GetName()].url.c_str(),
+      base_url.c_str(),
       token.c_str()));
 
   Profile* profile = Profile::FromWebUI(web_ui());
 
-  OAuth2TokenService* token_service =
-      ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
+  if (current_register_user_index_ == kAccountIndexUseOAuth2) {
+    OAuth2TokenService* token_service =
+        ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
 
-  if (!token_service) {
-    LogRegisterErrorToWeb("Could not get token service");
-    return;
+    if (!token_service) {
+      SendRegisterError();
+      return;
+    }
+
+    confirm_api_call_flow_.reset(new PrivetConfirmApiCallFlow(
+        profile->GetRequestContext(),
+        token_service,
+        automated_claim_url,
+        base::Bind(&LocalDiscoveryUIHandler::OnConfirmDone,
+                   base::Unretained(this))));
+    confirm_api_call_flow_->Start();
+  } else {
+    if (current_register_user_index_ == 0) {
+      StartCookieConfirmFlow(current_register_user_index_,
+                             xsrf_token_for_primary_user_,
+                             automated_claim_url);
+    } else {
+      cloud_print_account_manager_.reset(new CloudPrintAccountManager(
+          Profile::FromWebUI(web_ui())->GetRequestContext(),
+          base_url,
+          current_register_user_index_,
+          base::Bind(&LocalDiscoveryUIHandler::OnXSRFTokenForSecondaryAccount,
+                     base::Unretained(this), automated_claim_url)));
+
+      cloud_print_account_manager_->Start();
+    }
   }
-
-  confirm_api_call_flow_.reset(new PrivetConfirmApiCallFlow(
-      profile->GetRequestContext(),
-      token_service,
-      automated_claim_url,
-      base::Bind(&LocalDiscoveryUIHandler::OnConfirmDone,
-                 base::Unretained(this))));
-
-  confirm_api_call_flow_->Start();
 }
 
 void LocalDiscoveryUIHandler::OnPrivetRegisterError(
@@ -189,7 +213,7 @@
     int printer_http_code,
     const DictionaryValue* json) {
   // TODO(noamsml): Add detailed error message.
-  LogRegisterErrorToWeb("Registration error");
+  SendRegisterError();
 }
 
 void LocalDiscoveryUIHandler::OnPrivetRegisterDone(
@@ -198,7 +222,7 @@
   current_register_operation_.reset();
   current_http_client_.reset();
 
-  LogRegisterDoneToWeb(device_id);
+  SendRegisterDone();
 }
 
 void LocalDiscoveryUIHandler::OnConfirmDone(
@@ -209,7 +233,7 @@
     current_register_operation_->CompleteRegistration();
   } else {
     // TODO(noamsml): Add detailed error message.
-    LogRegisterErrorToWeb("Confirm error");
+    SendRegisterError();
   }
 }
 
@@ -219,19 +243,17 @@
     const DeviceDescription& description) {
   device_descriptions_[name] = description;
 
-  base::StringValue service_name(name);
   base::DictionaryValue info;
-  info.SetString("domain", description.address.host());
-  info.SetInteger("port", description.address.port());
-  std::string ip_addr_string;
-  if (!description.ip_address.empty())
-    ip_addr_string = net::IPAddressToString(description.ip_address);
 
-  info.SetString("ip", ip_addr_string);
-  info.SetString("lastSeen", "unknown");
+  base::StringValue service_name(name);
+
+  info.SetString("service_name", name);
+  info.SetString("human_readable_name", description.name);
+  info.SetString("description", description.description);
   info.SetBoolean("registered", !description.id.empty());
+  info.SetBoolean("is_mine", !description.id.empty());
 
-  web_ui()->CallJavascriptFunction("local_discovery.onServiceUpdate",
+  web_ui()->CallJavascriptFunction("local_discovery.onDeviceUpdate",
                                    service_name, info);
 }
 
@@ -240,40 +262,95 @@
   scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
   base::StringValue name_value(name);
 
-  web_ui()->CallJavascriptFunction("local_discovery.onServiceUpdate",
+  web_ui()->CallJavascriptFunction("local_discovery.onDeviceUpdate",
                                    name_value, *null_value);
 }
 
-void LocalDiscoveryUIHandler::LogRegisterErrorToWeb(const std::string& error) {
-  base::StringValue error_value(error);
-  web_ui()->CallJavascriptFunction("local_discovery.registrationFailed",
-                                   error_value);
-  DLOG(ERROR) << error;
+void LocalDiscoveryUIHandler::SendRegisterError() {
+  web_ui()->CallJavascriptFunction("local_discovery.registrationFailed");
 }
 
-void LocalDiscoveryUIHandler::LogRegisterDoneToWeb(const std::string& id) {
-  base::StringValue id_value(id);
-  web_ui()->CallJavascriptFunction("local_discovery.registrationSuccess",
-                                   id_value);
-  DLOG(INFO) << "Registered " << id;
+void LocalDiscoveryUIHandler::SendRegisterDone() {
+  web_ui()->CallJavascriptFunction("local_discovery.registrationSuccess");
 }
 
-void LocalDiscoveryUIHandler::LogInfoErrorToWeb(const std::string& error) {
-  base::StringValue error_value(error);
-  web_ui()->CallJavascriptFunction("local_discovery.infoFailed", error_value);
-  LOG(ERROR) << error;
-}
+void LocalDiscoveryUIHandler::OnCloudPrintAccountsResolved(
+    const std::vector<std::string>& accounts,
+    const std::string& xsrf_token) {
+  xsrf_token_for_primary_user_ = xsrf_token;
 
-void LocalDiscoveryUIHandler::OnPrivetInfoDone(
-    PrivetInfoOperation* operation,
-    int http_code,
-    const base::DictionaryValue* json_value) {
-  if (http_code != net::HTTP_OK || !json_value) {
-    LogInfoErrorToWeb(base::StringPrintf("HTTP error %d", http_code));
-    return;
+  std::string sync_account = GetSyncAccount();
+  base::ListValue accounts_annotated_list;
+
+  if (!sync_account.empty()) {
+    scoped_ptr<base::ListValue> account_annotated(new base::ListValue);
+    account_annotated->AppendInteger(kAccountIndexUseOAuth2);
+    account_annotated->AppendString(sync_account);
+    accounts_annotated_list.Append(account_annotated.release());
   }
 
-  web_ui()->CallJavascriptFunction("local_discovery.renderInfo", *json_value);
+  int account_index = 0;
+  for (std::vector<std::string>::const_iterator i = accounts.begin();
+       i != accounts.end(); i++, account_index++) {
+    if (*i == sync_account) continue;
+
+    scoped_ptr<base::ListValue> account_annotated(new base::ListValue);
+    account_annotated->AppendInteger(account_index);
+    account_annotated->AppendString(*i);
+    accounts_annotated_list.Append(account_annotated.release());
+  }
+
+  base::StringValue device_name(current_register_device_);
+  web_ui()->CallJavascriptFunction("local_discovery.requestUser",
+                                   accounts_annotated_list, device_name);
+}
+
+void LocalDiscoveryUIHandler::OnXSRFTokenForSecondaryAccount(
+    const GURL& automated_claim_url,
+    const std::vector<std::string>& accounts,
+    const std::string& xsrf_token) {
+  StartCookieConfirmFlow(current_register_user_index_,
+                         xsrf_token,
+                         automated_claim_url);
+}
+
+void LocalDiscoveryUIHandler::SetIsVisible(bool visible) {
+  if (visible != is_visible_) {
+    g_num_visible += visible ? 1 : -1;
+    is_visible_ = visible;
+  }
+}
+
+std::string LocalDiscoveryUIHandler::GetSyncAccount() {
+  Profile* profile = Profile::FromWebUI(web_ui());
+  SigninManagerBase* signin_manager =
+      SigninManagerFactory::GetForProfileIfExists(profile);
+
+  if (!signin_manager) {
+    return "";
+  }
+
+  return signin_manager->GetAuthenticatedUsername();
+}
+
+const std::string& LocalDiscoveryUIHandler::GetCloudPrintBaseUrl(
+    const std::string& device_name) {
+  return device_descriptions_[device_name].url;
+}
+
+void LocalDiscoveryUIHandler::StartCookieConfirmFlow(
+    int user_index,
+    const std::string& xsrf_token,
+    const GURL& automated_claim_url) {
+  confirm_api_call_flow_.reset(new PrivetConfirmApiCallFlow(
+      Profile::FromWebUI(web_ui())->GetRequestContext(),
+      user_index,
+      xsrf_token,
+      automated_claim_url,
+      base::Bind(&LocalDiscoveryUIHandler::OnConfirmDone,
+                 base::Unretained(this))));
+
+  confirm_api_call_flow_->Start();
 }
 
 }  // namespace local_discovery
diff --git a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h
index 42b594e..59d4316 100644
--- a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h
+++ b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h
@@ -7,7 +7,9 @@
 
 #include <map>
 #include <string>
+#include <vector>
 
+#include "chrome/browser/local_discovery/cloud_print_account_manager.h"
 #include "chrome/browser/local_discovery/privet_confirm_api_flow.h"
 #include "chrome/browser/local_discovery/privet_device_lister.h"
 #include "chrome/browser/local_discovery/privet_http.h"
@@ -17,6 +19,7 @@
 #include "content/public/browser/user_metrics.h"
 #include "content/public/browser/web_ui_message_handler.h"
 
+// TODO(noamsml): Factor out full registration flow into single class
 namespace local_discovery {
 
 // UI Handler for chrome://devices/
@@ -24,8 +27,7 @@
 // into the Javascript to update the page.
 class LocalDiscoveryUIHandler : public content::WebUIMessageHandler,
                                 public PrivetRegisterOperation::Delegate,
-                                public PrivetDeviceLister::Delegate,
-                                public PrivetInfoOperation::Delegate {
+                                public PrivetDeviceLister::Delegate {
  public:
   class Factory {
    public:
@@ -41,6 +43,7 @@
 
   static LocalDiscoveryUIHandler* Create();
   static void SetFactory(Factory* factory);
+  static bool GetHasVisible();
 
   // WebUIMessageHandler implementation.
   virtual void RegisterMessages() OVERRIDE;
@@ -69,46 +72,65 @@
       const DeviceDescription& description) OVERRIDE;
   virtual void DeviceRemoved(const std::string& name) OVERRIDE;
 
-  // PrivetInfoOperation::Delegate implementation:
-  virtual void OnPrivetInfoDone(
-      PrivetInfoOperation* operation,
-      int http_code,
-      const base::DictionaryValue* json_value) OVERRIDE;
-
  private:
   // Message handlers:
   // For registering a device.
   void HandleRegisterDevice(const base::ListValue* args);
+
   // For when the page is ready to recieve device notifications.
-
   void HandleStart(const base::ListValue* args);
-  // For when info for a device is requested.
-  void HandleInfoRequested(const base::ListValue* args);
+
+  // For when a visibility change occurs.
+  void HandleIsVisible(const base::ListValue* args);
+
+  // For when a user choice is made.
+  void HandleChooseUser(const base::ListValue* args);
 
   // For when the IP address of the printer has been resolved for registration.
-  void StartRegisterHTTP(scoped_ptr<PrivetHTTPClient> http_client);
-
-  // For when the IP address of the printer has been resolved for registration.
-  void StartInfoHTTP(scoped_ptr<PrivetHTTPClient> http_client);
+  void StartRegisterHTTP(
+      const std::string& user,
+      scoped_ptr<PrivetHTTPClient> http_client);
 
   // For when the confirm operation on the cloudprint server has finished
   // executing.
   void OnConfirmDone(PrivetConfirmApiCallFlow::Status status);
 
-  // Log an error to the web interface.
-  void LogRegisterErrorToWeb(const std::string& error);
+  // For when the cloud print account list is resolved.
+  void OnCloudPrintAccountsResolved(const std::vector<std::string>& accounts,
+                                    const std::string& xsrf_token);
 
-  // Log a successful registration to the web inteface.
-  void LogRegisterDoneToWeb(const std::string& id);
+  // For when XSRF token is received for a secondary account.
+  void OnXSRFTokenForSecondaryAccount(
+      const GURL& automated_claim_url,
+      const std::vector<std::string>& accounts,
+      const std::string& xsrf_token);
 
-  // Log an error to the web interface.
-  void LogInfoErrorToWeb(const std::string& error);
+  // Signal to the web interface an error has ocurred while registering.
+  void SendRegisterError();
+
+  // Singal to the web interface that registration has finished.
+  void SendRegisterDone();
+
+  // Set the visibility of the page.
+  void SetIsVisible(bool visible);
+
+  // Get the sync account email.
+  std::string GetSyncAccount();
+
+  // Get the base cloud print URL for a given device.
+  const std::string& GetCloudPrintBaseUrl(const std::string& device_name);
+
+  // Start the confirm flow for a cookie based authentication.
+  void StartCookieConfirmFlow(
+      int user_index,
+      const std::string& xsrf_token,
+      const GURL& automatic_claim_url);
 
   // The current HTTP client (used for the current operation).
   scoped_ptr<PrivetHTTPClient> current_http_client_;
 
-  // The current info operation (operations are currently exclusive).
-  scoped_ptr<PrivetInfoOperation> current_info_operation_;
+  // Device currently registering.
+  std::string current_register_device_;
 
   // The current register operation. Only one allowed at any time.
   scoped_ptr<PrivetRegisterOperation> current_register_operation_;
@@ -131,6 +153,19 @@
   // A map of current device descriptions provided by the PrivetDeviceLister.
   std::map<std::string, DeviceDescription> device_descriptions_;
 
+  // Whether or not the page is marked as visible.
+  bool is_visible_;
+
+  // Cloud print account manager to enumerate accounts and get XSRF token.
+  scoped_ptr<CloudPrintAccountManager> cloud_print_account_manager_;
+
+  // XSRF token.
+  std::string xsrf_token_for_primary_user_;
+
+  // Current user index (for multi-login), or kAccountIndexUseOAuth2 for sync
+  // credentials.
+  int current_register_user_index_;
+
   DISALLOW_COPY_AND_ASSIGN(LocalDiscoveryUIHandler);
 };
 
diff --git a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
index 62715e8..00a9553 100644
--- a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
+++ b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
@@ -1624,12 +1624,13 @@
   DCHECK(!list);
   DictionaryValue* status_dict = new DictionaryValue();
 
+  Value* pipelined_connection_info = NULL;
   net::HttpNetworkSession* http_network_session =
       GetHttpNetworkSession(GetMainContext());
-  status_dict->Set("pipelining_enabled", Value::CreateBooleanValue(
-      http_network_session->params().http_pipelining_enabled));
-  Value* pipelined_connection_info = NULL;
   if (http_network_session) {
+    status_dict->Set("pipelining_enabled", Value::CreateBooleanValue(
+        http_network_session->params().http_pipelining_enabled));
+
     pipelined_connection_info =
         http_network_session->http_stream_factory()->PipelineInfoToValue();
   }
diff --git a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
index 73ccf8d..2ecaebf 100644
--- a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
+++ b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
@@ -127,10 +127,6 @@
 // states when the bar is attached or detached.
 std::string GetNewTabBackgroundCSS(const ui::ThemeProvider* theme_provider,
                                    bool bar_attached) {
-  int alignment;
-  theme_provider->GetDisplayProperty(
-      ThemeProperties::NTP_BACKGROUND_ALIGNMENT, &alignment);
-
   // TODO(glen): This is a quick workaround to hide the notused.png image when
   // no image is provided - we don't have time right now to figure out why
   // this is painting as white.
@@ -139,6 +135,9 @@
     return "-64px";
   }
 
+  int alignment = theme_provider->GetDisplayProperty(
+      ThemeProperties::NTP_BACKGROUND_ALIGNMENT);
+
   if (bar_attached)
     return ThemeProperties::AlignmentToString(alignment);
 
@@ -161,9 +160,8 @@
 // masks in theme_service.h).
 std::string GetNewTabBackgroundTilingCSS(
     const ui::ThemeProvider* theme_provider) {
-  int repeat_mode;
-  theme_provider->GetDisplayProperty(
-      ThemeProperties::NTP_BACKGROUND_TILING, &repeat_mode);
+  int repeat_mode = theme_provider->GetDisplayProperty(
+      ThemeProperties::NTP_BACKGROUND_TILING);
   return ThemeProperties::TilingToString(repeat_mode);
 }
 
@@ -506,10 +504,9 @@
   // Control fade and resize animations.
   load_time_data.SetBoolean("anim", ui::Animation::ShouldRenderRichAnimation());
 
-  int alignment;
   ui::ThemeProvider* tp = ThemeServiceFactory::GetForProfile(profile_);
-  tp->GetDisplayProperty(ThemeProperties::NTP_BACKGROUND_ALIGNMENT,
-                         &alignment);
+  int alignment = tp->GetDisplayProperty(
+      ThemeProperties::NTP_BACKGROUND_ALIGNMENT);
   load_time_data.SetString("themegravity",
       (alignment & ThemeProperties::ALIGN_RIGHT) ? "right" : "");
 
diff --git a/chrome/browser/ui/webui/ntp/ntp_user_data_logger.cc b/chrome/browser/ui/webui/ntp/ntp_user_data_logger.cc
index cd05553..95d86ed 100644
--- a/chrome/browser/ui/webui/ntp/ntp_user_data_logger.cc
+++ b/chrome/browser/ui/webui/ntp/ntp_user_data_logger.cc
@@ -11,22 +11,17 @@
 
 DEFINE_WEB_CONTENTS_USER_DATA_KEY(NTPUserDataLogger);
 
-NTPUserDataLogger::NTPUserDataLogger(content::WebContents* contents)
-    : content::WebContentsObserver(contents),
-      number_of_mouseovers_(0),
-      number_of_thumbnail_attempts_(0),
-      number_of_thumbnail_errors_(0) {
-}
-
 NTPUserDataLogger::~NTPUserDataLogger() {}
 
 void NTPUserDataLogger::EmitThumbnailErrorRate() {
-    DCHECK_LE(number_of_thumbnail_errors_, number_of_thumbnail_attempts_);
-    int error_rate =
-        (100 * number_of_thumbnail_errors_) / number_of_thumbnail_attempts_;
-    UMA_HISTOGRAM_PERCENTAGE("NewTabPage.ThumbnailErrorRate", error_rate);
-    number_of_thumbnail_attempts_ = 0;
-    number_of_thumbnail_errors_ = 0;
+  DCHECK_LE(number_of_thumbnail_errors_, number_of_thumbnail_attempts_);
+  if (number_of_thumbnail_attempts_ != 0) {
+    UMA_HISTOGRAM_PERCENTAGE("NewTabPage.ThumbnailErrorRate",
+                             GetPercentError(number_of_thumbnail_errors_,
+                                             number_of_thumbnail_attempts_));
+  }
+  number_of_thumbnail_attempts_ = 0;
+  number_of_thumbnail_errors_ = 0;
 }
 
 void NTPUserDataLogger::EmitMouseoverCount() {
@@ -64,3 +59,14 @@
       EmitThumbnailErrorRate();
   }
 }
+
+NTPUserDataLogger::NTPUserDataLogger(content::WebContents* contents)
+    : content::WebContentsObserver(contents),
+      number_of_mouseovers_(0),
+      number_of_thumbnail_attempts_(0),
+      number_of_thumbnail_errors_(0) {
+}
+
+size_t NTPUserDataLogger::GetPercentError(size_t errors, size_t events) const {
+  return (100 * errors) / events;
+}
diff --git a/chrome/browser/ui/webui/ntp/ntp_user_data_logger.h b/chrome/browser/ui/webui/ntp/ntp_user_data_logger.h
index 0103a58..41cd59d 100644
--- a/chrome/browser/ui/webui/ntp/ntp_user_data_logger.h
+++ b/chrome/browser/ui/webui/ntp/ntp_user_data_logger.h
@@ -43,21 +43,26 @@
   virtual void NavigationEntryCommitted(
       const content::LoadCommittedDetails& load_details) OVERRIDE;
 
- private:
+ protected:
   explicit NTPUserDataLogger(content::WebContents* contents);
+
+  // Returns the percent error given |events| occurrences and |errors| errors.
+  virtual size_t GetPercentError(size_t errors, size_t events) const;
+
+ private:
   friend class content::WebContentsUserData<NTPUserDataLogger>;
 
   // Total number of mouseovers for this NTP session.
-  int number_of_mouseovers_;
+  size_t number_of_mouseovers_;
 
   // Total number of attempts made to load thumbnail images for this NTP
   // session.
-  int number_of_thumbnail_attempts_;
+  size_t number_of_thumbnail_attempts_;
 
   // Total number of errors that occurred when trying to load thumbnail images
   // for this NTP session. When these errors occur a grey tile is shown instead
   // of a thumbnail image.
-  int number_of_thumbnail_errors_;
+  size_t number_of_thumbnail_errors_;
 
   // The URL of this New Tab Page - varies based on NTP version.
   GURL ntp_url_;
diff --git a/chrome/browser/ui/webui/ntp/ntp_user_data_logger_unittest.cc b/chrome/browser/ui/webui/ntp/ntp_user_data_logger_unittest.cc
new file mode 100644
index 0000000..d38b1bf
--- /dev/null
+++ b/chrome/browser/ui/webui/ntp/ntp_user_data_logger_unittest.cc
@@ -0,0 +1,30 @@
+// 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 "chrome/browser/ui/webui/ntp/ntp_user_data_logger.h"
+
+#include "base/basictypes.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class MockNTPUserDataLogger : public NTPUserDataLogger {
+ public:
+  MockNTPUserDataLogger() : NTPUserDataLogger(NULL) {}
+  virtual ~MockNTPUserDataLogger() {}
+
+  MOCK_CONST_METHOD2(GetPercentError, size_t(size_t errors, size_t events));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockNTPUserDataLogger);
+};
+
+}  // namespace
+
+TEST(NTPUserDataLoggerTest, ThumbnailErrorRateDoesNotDivideByZero) {
+  MockNTPUserDataLogger logger;
+  EXPECT_CALL(logger, GetPercentError(testing::_, testing::_)).Times(0);
+  logger.EmitThumbnailErrorRate();
+}
diff --git a/chrome/browser/ui/webui/options/browser_options_handler.cc b/chrome/browser/ui/webui/options/browser_options_handler.cc
index 99ef39a..490c495 100644
--- a/chrome/browser/ui/webui/options/browser_options_handler.cc
+++ b/chrome/browser/ui/webui/options/browser_options_handler.cc
@@ -510,7 +510,7 @@
 
   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
   values->SetBoolean("enableStickyKeys",
-                     !command_line.HasSwitch(switches::kDisableStickyKeys));
+                     command_line.HasSwitch(switches::kEnableStickyKeys));
 #endif
 
 #if defined(OS_MACOSX)
diff --git a/chrome/browser/ui/webui/options/certificate_manager_browsertest.cc b/chrome/browser/ui/webui/options/certificate_manager_browsertest.cc
index 309313b..3932a5b 100644
--- a/chrome/browser/ui/webui/options/certificate_manager_browsertest.cc
+++ b/chrome/browser/ui/webui/options/certificate_manager_browsertest.cc
@@ -21,9 +21,9 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if defined(OS_CHROMEOS)
+#include "base/files/scoped_temp_dir.h"
+#include "chrome/browser/chromeos/policy/cros_enterprise_test_utils.h"
 #include "chrome/browser/chromeos/policy/network_configuration_updater.h"
-#include "chrome/browser/policy/profile_policy_connector.h"
-#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chromeos/network/onc/onc_test_utils.h"
 #include "crypto/nss_util.h"
 #endif
@@ -39,6 +39,12 @@
 
  protected:
   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
+#if defined(OS_CHROMEOS)
+    const char kFakeUsername[] = "fake_username@example.com";
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    policy::test_utils::MarkAsEnterpriseOwned(kFakeUsername,
+                                              temp_dir_.path());
+#endif
     // Setup the policy provider for injecting certs through ONC policy.
     EXPECT_CALL(provider_, IsInitializationComplete(_))
         .WillRepeatedly(Return(true));
@@ -47,16 +53,6 @@
   }
 
   virtual void SetUpOnMainThread() OVERRIDE {
-#if defined(OS_CHROMEOS)
-    Profile* profile = browser()->profile();
-    policy::ProfilePolicyConnector* connector =
-        policy::ProfilePolicyConnectorFactory::GetForProfile(profile);
-
-    // Enable web trust certs from policy.
-    g_browser_process->browser_policy_connector()->
-        network_configuration_updater()->SetUserPolicyService(
-            true, NULL /* no user */, connector->policy_service());
-#endif
     content::RunAllPendingInMessageLoop();
   }
 
@@ -95,6 +91,7 @@
 
   policy::MockConfigurationPolicyProvider provider_;
 #if defined(OS_CHROMEOS)
+  base::ScopedTempDir temp_dir_;
   crypto::ScopedTestNSSDB test_nssdb_;
 #endif
 };
diff --git a/chrome/browser/ui/webui/options/certificate_manager_handler.cc b/chrome/browser/ui/webui/options/certificate_manager_handler.cc
index 31393c6..b34ff7a 100644
--- a/chrome/browser/ui/webui/options/certificate_manager_handler.cc
+++ b/chrome/browser/ui/webui/options/certificate_manager_handler.cc
@@ -19,7 +19,7 @@
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/certificate_viewer.h"
-#include "chrome/browser/policy/browser_policy_connector.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/certificate_dialogs.h"
 #include "chrome/browser/ui/chrome_select_file_policy.h"
 #include "chrome/browser/ui/crypto_module_password_dialog.h"
@@ -29,11 +29,12 @@
 #include "grit/generated_resources.h"
 #include "net/base/crypto_module.h"
 #include "net/base/net_errors.h"
-#include "net/cert/cert_trust_anchor_provider.h"
 #include "net/cert/x509_certificate.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if defined(OS_CHROMEOS)
+#include "chrome/browser/policy/profile_policy_connector.h"
+#include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chromeos/dbus/cryptohome_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #endif
@@ -120,26 +121,6 @@
   const net::X509Certificate* cert_;
 };
 
-#if defined(OS_CHROMEOS)
-net::CertificateList CopyPolicyWebTrustCerts(
-    net::CertTrustAnchorProvider* provider) {
-  // Return a copy.
-  return provider->GetAdditionalTrustAnchors();
-}
-
-void RetrievePolicyWebTrustCerts(
-    base::Callback<void(const net::CertificateList&)> on_completion) {
-  net::CertTrustAnchorProvider* provider =
-      g_browser_process->browser_policy_connector()->
-          GetCertTrustAnchorProvider();
-  // Retrieve the anchors on the IO thread.
-  BrowserThread::PostTaskAndReplyWithResult(
-      BrowserThread::IO, FROM_HERE,
-      base::Bind(&CopyPolicyWebTrustCerts, base::Unretained(provider)),
-      on_completion);
-}
-#endif
-
 // Determine whether a certificate was stored with web trust by a policy.
 bool IsPolicyInstalledWithWebTrust(
     const net::CertificateList& web_trust_certs,
@@ -507,13 +488,16 @@
 }
 
 void CertificateManagerHandler::CertificatesRefreshed() {
+  net::CertificateList web_trusted_certs;
 #if defined(OS_CHROMEOS)
-  RetrievePolicyWebTrustCerts(
-      base::Bind(&CertificateManagerHandler::OnPolicyWebTrustCertsRetrieved,
-                 weak_ptr_factory_.GetWeakPtr()));
-#else
-  OnPolicyWebTrustCertsRetrieved(net::CertificateList());
+  policy::ProfilePolicyConnectorFactory::GetForProfile(
+      Profile::FromWebUI(web_ui()))->GetWebTrustedCertificates(
+          &web_trusted_certs);
 #endif
+  PopulateTree("personalCertsTab", net::USER_CERT, web_trusted_certs);
+  PopulateTree("serverCertsTab", net::SERVER_CERT, web_trusted_certs);
+  PopulateTree("caCertsTab", net::CA_CERT, web_trusted_certs);
+  PopulateTree("otherCertsTab", net::UNKNOWN_CERT, web_trusted_certs);
 }
 
 void CertificateManagerHandler::FileSelected(const base::FilePath& path,
@@ -1076,15 +1060,6 @@
   }
 }
 
-void CertificateManagerHandler::OnPolicyWebTrustCertsRetrieved(
-    const net::CertificateList& web_trust_certs) {
-  PopulateTree("personalCertsTab", net::USER_CERT, web_trust_certs);
-  PopulateTree("serverCertsTab", net::SERVER_CERT, web_trust_certs);
-  PopulateTree("caCertsTab", net::CA_CERT, web_trust_certs);
-  PopulateTree("otherCertsTab", net::UNKNOWN_CERT, web_trust_certs);
-  VLOG(1) << "populating finished";
-}
-
 void CertificateManagerHandler::ShowError(const std::string& title,
                                           const std::string& error) const {
   ScopedVector<const Value> args;
diff --git a/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler_unittest.cc b/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler_unittest.cc
index 7ba873e..8e4ca5a 100644
--- a/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler_unittest.cc
+++ b/chrome/browser/ui/webui/options/chromeos/cros_language_options_handler_unittest.cc
@@ -50,6 +50,7 @@
                                  "",  // name
                                  layouts,
                                  languages,
+                                 true,  // use on login screen.
                                  GURL());  // options page url
   }
 };
diff --git a/chrome/browser/ui/webui/options/language_options_handler_common.cc b/chrome/browser/ui/webui/options/language_options_handler_common.cc
index 9493a06..d927376 100644
--- a/chrome/browser/ui/webui/options/language_options_handler_common.cc
+++ b/chrome/browser/ui/webui/options/language_options_handler_common.cc
@@ -79,8 +79,8 @@
     { "addLanguageTitle", IDS_OPTIONS_LANGUAGES_ADD_TITLE },
     { "addLanguageSelectLabel", IDS_OPTIONS_LANGUAGES_ADD_SELECT_LABEL },
     { "restartButton", IDS_OPTIONS_SETTINGS_LANGUAGES_RELAUNCH_BUTTON },
-    { "dontTranslateInThisLanguage",
-      IDS_OPTIONS_LANGUAGES_DONT_TRANSLATE_IN_THIS_LANGUAGE },
+    { "offerToTranslateInThisLanguage",
+      IDS_OPTIONS_LANGUAGES_OFFER_TO_TRANSLATE_IN_THIS_LANGUAGE },
     { "cannotTranslateInThisLanguage",
       IDS_OPTIONS_LANGUAGES_CANNOT_TRANSLATE_IN_THIS_LANGUAGE },
   };
@@ -120,10 +120,6 @@
       command_line.HasSwitch(switches::kEnableSpellingAutoCorrect);
   localized_strings->SetBoolean("enableSpellingAutoCorrect",
                                 enable_spelling_auto_correct);
-  bool enable_translate_settings =
-      command_line.HasSwitch(switches::kEnableTranslateSettings);
-  localized_strings->SetBoolean("enableTranslateSettings",
-                                enable_translate_settings);
 
   Profile* profile = Profile::FromWebUI(web_ui());
   PrefService* prefs = profile->GetPrefs();
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index d6dae2b..c1f15cd 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -36,7 +36,6 @@
 #include "chrome/browser/printing/print_view_manager.h"
 #include "chrome/browser/printing/printer_manager_dialog.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/oauth2_token_service.h"
 #include "chrome/browser/signin/profile_oauth2_token_service.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -57,6 +56,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_view.h"
 #include "content/public/browser/web_ui.h"
+#include "google_apis/gaia/oauth2_token_service.h"
 #include "printing/backend/print_backend.h"
 #include "printing/metafile.h"
 #include "printing/metafile_impl.h"
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
index ca4c7e4..4e8e8ab 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -520,8 +520,12 @@
   }
   base::FundamentalValue ui_identifier(id_);
   base::FundamentalValue ui_preview_request_id(preview_request_id);
-  web_ui()->CallJavascriptFunction("updatePrintPreview", ui_identifier,
-                                   ui_preview_request_id);
+  if (AutoCancelForTesting()) {
+    OnClosePrintPreviewDialog();
+  } else {
+    web_ui()->CallJavascriptFunction("updatePrintPreview", ui_identifier,
+                                     ui_preview_request_id);
+  }
 }
 
 void PrintPreviewUI::OnPrintPreviewDialogDestroyed() {
@@ -582,3 +586,13 @@
 void PrintPreviewUI::OnPrintPreviewScalingDisabled() {
   web_ui()->CallJavascriptFunction("printScalingDisabledForSourcePDF");
 }
+
+static bool g_auto_cancel_for_testing_ = false;
+
+void PrintPreviewUI::SetAutoCancelForTesting(bool auto_cancel) {
+  g_auto_cancel_for_testing_ = auto_cancel;
+}
+
+bool PrintPreviewUI::AutoCancelForTesting() {
+  return g_auto_cancel_for_testing_;
+}
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.h b/chrome/browser/ui/webui/print_preview/print_preview_ui.h
index 1cd81e3..c54edbc 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.h
@@ -147,6 +147,10 @@
   // default.
   void OnPrintPreviewScalingDisabled();
 
+  // Manages the global auto-cancel mode used only for testing.
+  static void SetAutoCancelForTesting(bool auto_cancel);
+  static bool AutoCancelForTesting();
+
  private:
   friend class PrintPreviewHandlerTest;
   FRIEND_TEST_ALL_PREFIXES(PrintPreviewHandlerTest, StickyMarginsCustom);
diff --git a/chrome/browser/ui/webui/translate_internals/translate_internals_handler.cc b/chrome/browser/ui/webui/translate_internals/translate_internals_handler.cc
index 91f9e03..7a052b7 100644
--- a/chrome/browser/ui/webui/translate_internals/translate_internals_handler.cc
+++ b/chrome/browser/ui/webui/translate_internals/translate_internals_handler.cc
@@ -9,14 +9,12 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/command_line.h"
 #include "base/prefs/pref_service.h"
 #include "base/values.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/translate/translate_error_details.h"
 #include "chrome/browser/translate/translate_event_details.h"
 #include "chrome/browser/translate/translate_prefs.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/translate/language_detection_details.h"
 #include "content/public/browser/web_contents.h"
@@ -146,12 +144,7 @@
   std::vector<std::string> keys;
   keys.push_back(prefs::kEnableTranslate);
 
-  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
-  bool enable_translate_settings =
-      command_line.HasSwitch(switches::kEnableTranslateSettings);
-  if (enable_translate_settings)
-    keys.push_back(TranslatePrefs::kPrefTranslateBlockedLanguages);
-
+  keys.push_back(TranslatePrefs::kPrefTranslateBlockedLanguages);
   keys.push_back(TranslatePrefs::kPrefTranslateLanguageBlacklist);
   keys.push_back(TranslatePrefs::kPrefTranslateSiteBlacklist);
   keys.push_back(TranslatePrefs::kPrefTranslateWhitelists);
diff --git a/chrome/browser/ui/webui/translate_internals/translate_internals_ui.cc b/chrome/browser/ui/webui/translate_internals/translate_internals_ui.cc
index be6c1ee..85ca934 100644
--- a/chrome/browser/ui/webui/translate_internals/translate_internals_ui.cc
+++ b/chrome/browser/ui/webui/translate_internals/translate_internals_ui.cc
@@ -61,11 +61,6 @@
     source->AddString(key, value);
   }
 
-  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
-  bool enable_translate_settings =
-      command_line.HasSwitch(switches::kEnableTranslateSettings);
-  source->AddBoolean("enable-translate-settings", enable_translate_settings);
-
   std::string cld_version = LanguageDetectionUtil::GetCLDVersion();
   source->AddString("cld-version", cld_version);
 
diff --git a/chrome/browser/ui/window_sizer/window_sizer.cc b/chrome/browser/ui/window_sizer/window_sizer.cc
index 2300a55..a2c6635 100644
--- a/chrome/browser/ui/window_sizer/window_sizer.cc
+++ b/chrome/browser/ui/window_sizer/window_sizer.cc
@@ -9,7 +9,6 @@
 #include "base/prefs/pref_service.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/ash/ash_init.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
@@ -19,31 +18,17 @@
 #include "chrome/common/pref_names.h"
 #include "ui/gfx/screen.h"
 
+#if defined(USE_ASH)
+#include "chrome/browser/ui/ash/ash_init.h"
+#include "chrome/browser/ui/ash/chrome_shell_delegate.h"
+#include "chrome/browser/ui/ash/window_positioner.h"
+#endif
+
 // Minimum height of the visible part of a window.
 const int kMinVisibleHeight = 30;
 // Minimum width of the visible part of a window.
 const int kMinVisibleWidth = 30;
 
-class DefaultMonitorInfoProvider : public MonitorInfoProvider {
- public:
-  explicit DefaultMonitorInfoProvider(const gfx::Screen* screen)
-      : screen_(screen) {}
-  // Overridden from MonitorInfoProvider:
-  virtual gfx::Rect GetPrimaryDisplayWorkArea() const OVERRIDE {
-    return screen_->GetPrimaryDisplay().work_area();
-  }
-  virtual gfx::Rect GetPrimaryDisplayBounds() const OVERRIDE {
-    return screen_->GetPrimaryDisplay().bounds();
-  }
-  virtual gfx::Rect GetMonitorWorkAreaMatching(
-      const gfx::Rect& match_rect) const OVERRIDE {
-    return screen_->GetDisplayMatching(match_rect).work_area();
-  }
- private:
-  const gfx::Screen* screen_;
-  DISALLOW_COPY_AND_ASSIGN(DefaultMonitorInfoProvider);
-};
-
 ///////////////////////////////////////////////////////////////////////////////
 // An implementation of WindowSizer::StateProvider that gets the last active
 // and persistent state from the browser window and the user's profile.
@@ -163,18 +148,18 @@
 
 WindowSizer::WindowSizer(StateProvider* state_provider, const Browser* browser)
     : state_provider_(state_provider),
-      monitor_info_provider_(new DefaultMonitorInfoProvider(
-          // TODO(scottmg): NativeScreen is wrong. http://crbug.com/133312
-          gfx::Screen::GetNativeScreen())),
+      // TODO(scottmg): NativeScreen is wrong. http://crbug.com/133312
+      screen_(gfx::Screen::GetNativeScreen()),
       browser_(browser) {
 }
 
 WindowSizer::WindowSizer(StateProvider* state_provider,
-                         MonitorInfoProvider* monitor_info_provider,
+                         gfx::Screen* screen,
                          const Browser* browser)
     : state_provider_(state_provider),
-      monitor_info_provider_(monitor_info_provider),
+      screen_(screen),
       browser_(browser) {
+  DCHECK(screen_);
 }
 
 WindowSizer::~WindowSizer() {
@@ -224,14 +209,25 @@
     // the user's screen size.
     GetDefaultWindowBounds(bounds);
   } else {
+#if defined(USE_ASH)
+    // In case of a popup with an 'unspecified' location in ash, we are
+    // looking for a good screen location. We are interpreting (0,0) as an
+    // unspecified location.
+    if (chrome::ShouldOpenAshOnStartup() &&
+        browser_ && browser_->is_type_popup() &&
+        bounds->x() == 0 && bounds->y() == 0) {
+      *bounds = ChromeShellDelegate::instance()->window_positioner()->
+          GetPopupPosition(*bounds);
+      return;
+    }
+#endif
     // In case that there was a bound given we need to make sure that it is
     // visible and fits on the screen.
     // Find the size of the work area of the monitor that intersects the bounds
     // of the anchor window. Note: AdjustBoundsToBeVisibleOnMonitorContaining
     // does not exactly what we want: It makes only sure that "a minimal part"
     // is visible on the screen.
-    gfx::Rect work_area =
-        monitor_info_provider_->GetMonitorWorkAreaMatching(*bounds);
+    gfx::Rect work_area = screen_->GetDisplayMatching(*bounds).work_area();
     // Resize so that it fits.
     bounds->AdjustToFit(work_area);
   }
@@ -275,9 +271,8 @@
   }
 #endif
   DCHECK(default_bounds);
-  DCHECK(monitor_info_provider_.get());
 
-  gfx::Rect work_area = monitor_info_provider_->GetPrimaryDisplayWorkArea();
+  gfx::Rect work_area = screen_->GetPrimaryDisplay().work_area();
 
   // The default size is either some reasonably wide width, or if the work
   // area is narrower, then the work area width less some aesthetic padding.
@@ -286,7 +281,7 @@
 
   // For wider aspect ratio displays at higher resolutions, we might size the
   // window narrower to allow two windows to easily be placed side-by-side.
-  gfx::Rect screen_size = monitor_info_provider_->GetPrimaryDisplayBounds();
+  gfx::Rect screen_size = screen_->GetPrimaryDisplay().bounds();
   double width_to_height =
     static_cast<double>(screen_size.width()) / screen_size.height();
 
@@ -312,12 +307,10 @@
     const gfx::Rect& saved_work_area,
     gfx::Rect* bounds) const {
   DCHECK(bounds);
-  DCHECK(monitor_info_provider_.get());
 
   // Find the size of the work area of the monitor that intersects the bounds
   // of the anchor window.
-  gfx::Rect work_area =
-      monitor_info_provider_->GetMonitorWorkAreaMatching(other_bounds);
+  gfx::Rect work_area = screen_->GetDisplayMatching(other_bounds).work_area();
 
   // If height or width are 0, reset to the default size.
   gfx::Rect default_bounds;
diff --git a/chrome/browser/ui/window_sizer/window_sizer.h b/chrome/browser/ui/window_sizer/window_sizer.h
index 391bdbc..7648104 100644
--- a/chrome/browser/ui/window_sizer/window_sizer.h
+++ b/chrome/browser/ui/window_sizer/window_sizer.h
@@ -13,23 +13,9 @@
 
 class Browser;
 
-// An interface implemented by an object that can retrieve information about
-// the monitors on the system.
-class MonitorInfoProvider {
- public:
-  virtual ~MonitorInfoProvider() {}
-
-  // Returns the bounds of the work area of the primary monitor.
-  virtual gfx::Rect GetPrimaryDisplayWorkArea() const = 0;
-
-  // Returns the bounds of the primary monitor.
-  virtual gfx::Rect GetPrimaryDisplayBounds() const = 0;
-
-  // Returns the bounds of the work area of the monitor that most closely
-  // intersects the provided bounds.
-  virtual gfx::Rect GetMonitorWorkAreaMatching(
-      const gfx::Rect& match_rect) const = 0;
-};
+namespace gfx {
+class Screen;
+}
 
 ///////////////////////////////////////////////////////////////////////////////
 // WindowSizer
@@ -50,10 +36,10 @@
   // MonitorInfoProvider using the physical screen.
   WindowSizer(StateProvider* state_provider, const Browser* browser);
 
-  // WindowSizer owns |state_provider| and |monitor_info_provider|.
-  // It will use the supplied monitor info provider. Used only for testing.
+  // WindowSizer owns |state_provider| and will use the supplied |screen|.
+  // Used only for testing.
   WindowSizer(StateProvider* state_provider,
-              MonitorInfoProvider* monitor_info_provider,
+              gfx::Screen* screen,
               const Browser* browser);
 
   virtual ~WindowSizer();
@@ -185,7 +171,7 @@
 
   // Providers for persistent storage and monitor metrics.
   scoped_ptr<StateProvider> state_provider_;
-  scoped_ptr<MonitorInfoProvider> monitor_info_provider_;
+  gfx::Screen* screen_;  // not owned.
 
   // Note that this browser handle might be NULL.
   const Browser* browser_;
diff --git a/chrome/browser/ui/window_sizer/window_sizer_ash.cc b/chrome/browser/ui/window_sizer/window_sizer_ash.cc
index a4ee1d1..aa8b535 100644
--- a/chrome/browser/ui/window_sizer/window_sizer_ash.cc
+++ b/chrome/browser/ui/window_sizer/window_sizer_ash.cc
@@ -8,6 +8,7 @@
 #include "ash/shell.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/window_util.h"
+#include "ash/wm/workspace/auto_window_management.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "chrome/browser/browser_process.h"
@@ -40,66 +41,6 @@
               browser->window()->GetNativeWindow()->GetBoundsInScreen()));
 }
 
-// Check if the window was not created as popup or as panel, it is
-// on the screen defined by |bounds_in_screen| and visible.
-bool IsValidToplevelWindow(aura::Window* window,
-                           const gfx::Rect& bounds_in_screen) {
-  const BrowserList* ash_browser_list =
-      BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
-  for (BrowserList::const_iterator iter = ash_browser_list->begin();
-       iter != ash_browser_list->end();
-       ++iter) {
-    Browser* browser = *iter;
-    if (browser && browser->window() &&
-        browser->window()->GetNativeWindow() == window) {
-      return IsValidBrowser(browser, bounds_in_screen);
-    }
-  }
-  // A window which has no browser associated with it is probably not a window
-  // of which we want to copy the size from.
-  return false;
-}
-
-// Get the first open (non minimized) window which is on the screen defined
-// by |bounds_in_screen| and visible.
-aura::Window* GetTopWindow(const gfx::Rect& bounds_in_screen) {
-  // Get the active window.
-  aura::Window* window = ash::wm::GetActiveWindow();
-  if (window && window->type() == aura::client::WINDOW_TYPE_NORMAL &&
-      window->IsVisible() && IsValidToplevelWindow(window, bounds_in_screen)) {
-    return window;
-  }
-
-  // Get a list of all windows.
-  const std::vector<aura::Window*> windows =
-      ash::MruWindowTracker::BuildWindowList(false);
-
-  if (windows.empty())
-    return NULL;
-
-  aura::Window::Windows::const_iterator iter = windows.begin();
-  // Find the index of the current window.
-  if (window)
-    iter = std::find(windows.begin(), windows.end(), window);
-
-  int index = (iter == windows.end()) ? 0 : (iter - windows.begin());
-
-  // Scan the cycle list backwards to see which is the second topmost window
-  // (and so on). Note that we might cycle a few indices twice if there is no
-  // suitable window. However - since the list is fairly small this should be
-  // very fast anyways.
-  for (int i = index + windows.size(); i >= 0; i--) {
-    aura::Window* window = windows[i % windows.size()];
-    if (window && window->type() == aura::client::WINDOW_TYPE_NORMAL &&
-        bounds_in_screen.Intersects(window->GetBoundsInScreen()) &&
-        window->IsVisible()
-        && IsValidToplevelWindow(window, bounds_in_screen)) {
-      return window;
-    }
-  }
-  return NULL;
-}
-
 // Return the number of valid top level windows on the screen defined by
 // the |bounds_in_screen| rectangle.
 int GetNumberOfValidTopLevelBrowserWindows(const gfx::Rect& bounds_in_screen) {
@@ -180,13 +121,12 @@
   if (browser_ && browser_->is_type_tabbed()) {
     aura::RootWindow* active = ash::Shell::GetActiveRootWindow();
     // Always open new window in the active display.
-    gfx::Rect active_area = active->GetBoundsInScreen();
     gfx::Rect work_area =
-        monitor_info_provider_->GetMonitorWorkAreaMatching(active_area);
+        screen_->GetDisplayMatching(active->GetBoundsInScreen()).work_area();
 
     // This is a window / app. See if there is no window and try to place it.
     int count = GetNumberOfValidTopLevelBrowserWindows(work_area);
-    aura::Window* top_window = GetTopWindow(work_area);
+    aura::Window* top_window = ash::GetTopWindowForNewWindow(active);
     // Our window should not have any impact if we are already on top.
     if (browser_->window() &&
         top_window == browser_->window()->GetNativeWindow())
@@ -220,16 +160,10 @@
     if (maximized)
       return true;
 
-    // Use the size of the other window, and mirror the location to the
-    // opposite side. Then make sure that it is inside our work area
-    // (if possible).
+    // Use the size of the other window. The window's bound will be rearranged
+    // in ash::WorkspaceLayoutManager using this location.
     *bounds_in_screen = top_window->GetBoundsInScreen();
 
-    bool move_right =
-        bounds_in_screen->CenterPoint().x() < work_area.CenterPoint().x();
-
-    MoveRect(work_area, *bounds_in_screen, move_right);
-    bounds_in_screen->AdjustToFit(work_area);
     return true;
   }
 
@@ -238,9 +172,8 @@
 
 void WindowSizer::GetDefaultWindowBoundsAsh(gfx::Rect* default_bounds) const {
   DCHECK(default_bounds);
-  DCHECK(monitor_info_provider_.get());
 
-  gfx::Rect work_area = monitor_info_provider_->GetPrimaryDisplayWorkArea();
+  gfx::Rect work_area = screen_->GetPrimaryDisplay().work_area();
 
   // There should be a 'desktop' border around the window at the left and right
   // side.
diff --git a/chrome/browser/ui/window_sizer/window_sizer_ash_uitest.cc b/chrome/browser/ui/window_sizer/window_sizer_ash_uitest.cc
index 61f702d..2b54530 100644
--- a/chrome/browser/ui/window_sizer/window_sizer_ash_uitest.cc
+++ b/chrome/browser/ui/window_sizer/window_sizer_ash_uitest.cc
@@ -167,7 +167,7 @@
   // Don't shutdown when closing the last browser window.
   chrome::StartKeepAlive();
 
-  views::MenuController::TurnOffContextMenuSelectionHoldForTest();
+  views::MenuController::TurnOffMenuSelectionHoldForTest();
 
   ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows();
 
diff --git a/chrome/browser/ui/window_sizer/window_sizer_ash_unittest.cc b/chrome/browser/ui/window_sizer/window_sizer_ash_unittest.cc
index a66d5c9..b3d460b 100644
--- a/chrome/browser/ui/window_sizer/window_sizer_ash_unittest.cc
+++ b/chrome/browser/ui/window_sizer/window_sizer_ash_unittest.cc
@@ -2,14 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/window_sizer/window_sizer_common_unittest.h"
-
+#include "ash/screen_ash.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/test_shell_delegate.h"
 #include "ash/wm/window_resizer.h"
+#include "ash/wm/window_util.h"
 #include "base/compiler_specific.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/window_sizer/window_sizer_common_unittest.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/render_view_test.h"
@@ -20,7 +21,7 @@
 #include "ui/aura/root_window.h"
 #include "ui/aura/test/test_windows.h"
 
-typedef ash::test::AshTestBase WindowSizerTest;
+typedef ash::test::AshTestBase WindowSizerAshTest;
 
 namespace {
 
@@ -30,57 +31,70 @@
  public:
   // |native_window| will still be owned by the caller after the constructor
   // was called.
-  explicit TestBrowserWindowAura(aura::Window* native_window);
-  virtual ~TestBrowserWindowAura();
+  explicit TestBrowserWindowAura(aura::Window* native_window)
+      : native_window_(native_window) {
+  }
+  virtual ~TestBrowserWindowAura() {}
 
   // TestBrowserWindow overrides:
-  virtual void Show() OVERRIDE;
-  virtual void Activate() OVERRIDE;
-  virtual gfx::NativeWindow GetNativeWindow() OVERRIDE;
-  virtual gfx::Rect GetBounds() const OVERRIDE;
+  virtual void Show() OVERRIDE {
+    native_window_->Show();
+    Activate();
+  }
+  virtual void Hide() OVERRIDE {
+    native_window_->Hide();
+  }
+  virtual void Activate() OVERRIDE {
+    GetActivationClient(
+        native_window_->GetRootWindow())->ActivateWindow(native_window_.get());
+  }
+  virtual gfx::NativeWindow GetNativeWindow() OVERRIDE {
+    return native_window_.get();
+  }
+  virtual gfx::Rect GetBounds() const OVERRIDE {
+    return native_window_->bounds();
+  }
+
+  Browser* browser() { return browser_.get(); }
+
+  void CreateBrowser(const Browser::CreateParams& params) {
+    Browser::CreateParams create_params = params;
+    create_params.window = this;
+    browser_.reset(new Browser(create_params));
+    if (browser_->is_type_tabbed() || browser_->is_app())
+      ash::wm::SetWindowPositionManaged(native_window_.get(), true);
+  }
 
  private:
-  // The |native_window_| is still owned by the creator of this class.
-  aura::Window* native_window_;
+  scoped_ptr<Browser> browser_;
+  scoped_ptr<aura::Window> native_window_;
 
   DISALLOW_COPY_AND_ASSIGN(TestBrowserWindowAura);
 };
 
-TestBrowserWindowAura::TestBrowserWindowAura(aura::Window *native_window)
-    : native_window_(native_window) {
-}
-
-TestBrowserWindowAura::~TestBrowserWindowAura() {}
-
-void TestBrowserWindowAura::Show() {
-  native_window_->Show();
-  Activate();
-}
-
-void TestBrowserWindowAura::Activate() {
-  GetActivationClient(
-      native_window_->GetRootWindow())->ActivateWindow(native_window_);
-}
-
-gfx::NativeWindow TestBrowserWindowAura::GetNativeWindow() {
-    return native_window_;
-}
-
-gfx::Rect TestBrowserWindowAura::GetBounds() const {
-  return native_window_->bounds();
-}
-
 int AlignToGridRoundDown(int location, int grid_size) {
   if (grid_size <= 1 || location % grid_size == 0)
     return location;
   return location / grid_size * grid_size;
 }
 
+scoped_ptr<TestBrowserWindowAura> CreateTestBrowserWindow(
+    aura::Window* window,
+    const gfx::Rect& bounds,
+    const Browser::CreateParams& params) {
+  if (!bounds.IsEmpty())
+    window->SetBounds(bounds);
+  scoped_ptr<TestBrowserWindowAura> browser_window(
+      new TestBrowserWindowAura(window));
+  browser_window->CreateBrowser(params);
+  return browser_window.Pass();
+}
+
 }  // namespace
 
 // Test that the window is sized appropriately for the first run experience
 // where the default window bounds calculation is invoked.
-TEST_F(WindowSizerTest, DefaultSizeCase) {
+TEST_F(WindowSizerAshTest, DefaultSizeCase) {
   int grid = WindowSizer::kDesktopBorderSize;
   { // 4:3 monitor case, 1024x768, no taskbar
     gfx::Rect window_bounds;
@@ -202,7 +216,7 @@
 
 // Test that the next opened window is positioned appropriately given the
 // bounds of an existing window of the same type.
-TEST_F(WindowSizerTest, LastWindowBoundsCase) {
+TEST_F(WindowSizerAshTest, LastWindowBoundsCase) {
   { // normal, in the middle of the screen somewhere.
     gfx::Rect window_bounds;
     GetWindowBounds(p1024x768, p1024x768, gfx::Rect(),
@@ -260,7 +274,7 @@
 }
 
 // Test that the window opened is sized appropriately given persisted sizes.
-TEST_F(WindowSizerTest, PersistedBoundsCase) {
+TEST_F(WindowSizerAshTest, PersistedBoundsCase) {
   { // normal, in the middle of the screen somewhere.
     gfx::Rect initial_bounds(WindowSizer::kDesktopBorderSize,
                              WindowSizer::kDesktopBorderSize, 500, 400);
@@ -355,7 +369,7 @@
 // are run on Mac, and the *WithNonAggressiveRepositioning tests are run on
 // other platforms.
 
-TEST_F(WindowSizerTest, LastWindowOffscreenWithNonAggressiveRepositioning) {
+TEST_F(WindowSizerAshTest, LastWindowOffscreenWithNonAggressiveRepositioning) {
   { // taskbar on left.
     gfx::Rect window_bounds;
     GetWindowBounds(p1024x768, taskbar_left_work_area, gfx::Rect(),
@@ -430,17 +444,7 @@
 }
 
 // Test the placement of newly created windows.
-TEST_F(WindowSizerTest, PlaceNewWindows) {
-  // Create a dummy window.
-  scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
-  window->SetBounds(gfx::Rect(16, 32, 640, 320));
-
-  scoped_ptr<aura::Window> popup(CreateTestWindowInShellWithId(1));
-  popup->SetBounds(gfx::Rect(16, 32, 128, 256));
-
-  scoped_ptr<aura::Window> panel(CreateTestWindowInShellWithId(2));
-  panel->SetBounds(gfx::Rect(32, 48, 256, 512));
-
+TEST_F(WindowSizerAshTest, PlaceNewWindows) {
   // Create a browser which we can use to pass into the GetWindowBounds
   // function.
   scoped_ptr<TestingProfile> profile(new TestingProfile());
@@ -453,75 +457,35 @@
 
   // Creating a popup handler here to make sure it does not interfere with the
   // existing windows.
-  scoped_ptr<BrowserWindow> browser_window(
-      new TestBrowserWindowAura(window.get()));
-  Browser::CreateParams window_params(profile.get(),
-                                      chrome::HOST_DESKTOP_TYPE_ASH);
-  window_params.window = browser_window.get();
-  scoped_ptr<Browser> window_owning_browser(new Browser(window_params));
+  scoped_ptr<BrowserWindow> browser_window(CreateTestBrowserWindow(
+      CreateTestWindowInShellWithId(0),
+      gfx::Rect(16, 32, 640, 320),
+      Browser::CreateParams(profile.get(), chrome::HOST_DESKTOP_TYPE_ASH)));
 
   // Creating a popup to make sure it does not interfere with the positioning.
-  scoped_ptr<BrowserWindow> browser_popup(
-      new TestBrowserWindowAura(popup.get()));
-  Browser::CreateParams popup_params(Browser::TYPE_POPUP, profile.get(),
-                                     chrome::HOST_DESKTOP_TYPE_ASH);
-  popup_params.window = browser_popup.get();
-  scoped_ptr<Browser> popup_owning_browser(new Browser(popup_params));
+  scoped_ptr<TestBrowserWindowAura> browser_popup(CreateTestBrowserWindow(
+      CreateTestWindowInShellWithId(1),
+      gfx::Rect(16, 32, 128, 256),
+      Browser::CreateParams(Browser::TYPE_POPUP, profile.get(),
+                            chrome::HOST_DESKTOP_TYPE_ASH)));
 
   // Creating a panel to make sure it does not interfere with the positioning.
-  scoped_ptr<BrowserWindow> browser_panel(
-      new TestBrowserWindowAura(panel.get()));
-  Browser::CreateParams panel_params(Browser::TYPE_POPUP, profile.get(),
-                                     chrome::HOST_DESKTOP_TYPE_ASH);
-  panel_params.window = browser_panel.get();
-  scoped_ptr<Browser> panel_owning_browser(new Browser(panel_params));
-
-  window->Show();
-  { // With a shown window it's size should get returned.
-    gfx::Rect window_bounds;
-    GetWindowBounds(p1600x1200, p1600x1200, gfx::Rect(),
-                    gfx::Rect(50, 100, 300, 150), bottom_s1600x1200,
-                    PERSISTED, browser.get(), gfx::Rect(), &window_bounds);
-    // The position should be right flush.
-    EXPECT_EQ("960,32 640x320", window_bounds.ToString());
-  }
-
-  { // With the window shown - but more on the right side then on the left
-    // side (and partially out of the screen), it should default to the other
-    // side and inside the screen.
-    window->SetBounds(gfx::Rect(1000, 600, 640, 320));
-    gfx::Rect window_bounds;
-    GetWindowBounds(p1600x1200, p1600x1200, gfx::Rect(),
-                    gfx::Rect(50, 100, 300, 150), bottom_s1600x1200,
-                    PERSISTED, browser.get(), gfx::Rect(), &window_bounds);
-    // The position should be left & bottom flush.
-    EXPECT_EQ("0,600 640x320", window_bounds.ToString());
-  }
-
-  { // If the second windows right side is already over the right side of the
-    // screen, it will not move back into the screen.
-    window->SetBounds(gfx::Rect(1000, 600, 640, 320));
-    gfx::Rect window_bounds;
-    GetWindowBounds(p1600x1200, p1600x1200, gfx::Rect(),
-                    gfx::Rect(50, 100, 300, 150), bottom_s1600x1200,
-                    PERSISTED, browser.get(), gfx::Rect(), &window_bounds);
-    // The position should be left & bottom flush.
-    EXPECT_EQ("0,600 640x320", window_bounds.ToString());
-    // If the other window was already beyond the point to get right flush
-    // it will remain where it is.
-    EXPECT_EQ("1000,600 640x320", window->bounds().ToString());
-  }
-
+  scoped_ptr<BrowserWindow> browser_panel(CreateTestBrowserWindow(
+      CreateTestWindowInShellWithId(2),
+      gfx::Rect(32, 48, 256, 512),
+      Browser::CreateParams(Browser::TYPE_POPUP, profile.get(),
+                            chrome::HOST_DESKTOP_TYPE_ASH)));
+  browser_window->Show();
   { // Make sure that popups do not get changed.
     gfx::Rect window_bounds;
     GetWindowBounds(p1600x1200, p1600x1200, gfx::Rect(),
                     gfx::Rect(50, 100, 300, 150), bottom_s1600x1200,
-                    PERSISTED, popup_owning_browser.get(),
+                    PERSISTED, browser_popup->browser(),
                     gfx::Rect(), &window_bounds);
     EXPECT_EQ("50,100 300x150", window_bounds.ToString());
   }
 
-  window->Hide();
+  browser_window->Hide();
   { // If a window is there but not shown the persisted default should be used.
     gfx::Rect window_bounds;
     GetWindowBounds(p1600x1200, p1600x1200, gfx::Rect(),
@@ -550,7 +514,7 @@
 // Test the placement of newly created windows on an empty desktop.
 // This test supplements "PlaceNewWindows" by testing the creation of a newly
 // created browser window on an empty desktop.
-TEST_F(WindowSizerTest, PlaceNewBrowserWindowOnEmptyDesktop) {
+TEST_F(WindowSizerAshTest, PlaceNewBrowserWindowOnEmptyDesktop) {
   // Create a browser which we can use to pass into the GetWindowBounds
   // function.
   scoped_ptr<TestingProfile> profile(new TestingProfile());
@@ -626,9 +590,11 @@
 #endif
 
 // Test the placement of newly created windows on multiple dislays.
-TEST_F(WindowSizerTest, MAYBE_PlaceNewWindowsOnMultipleDisplays) {
+TEST_F(WindowSizerAshTest, MAYBE_PlaceNewWindowsOnMultipleDisplays) {
   UpdateDisplay("1600x1200,1600x1200");
-  const gfx::Rect secondary(1600, 0, 1600, 1200);
+  gfx::Rect primary_bounds = ash::Shell::GetInstance()->GetScreen()->
+      GetPrimaryDisplay().bounds();
+  gfx::Rect secondary_bounds = ash::ScreenAsh::GetSecondaryDisplay().bounds();
 
   ash::Shell::GetInstance()->set_active_root_window(
       ash::Shell::GetPrimaryRootWindow());
@@ -636,36 +602,26 @@
   scoped_ptr<TestingProfile> profile(new TestingProfile());
 
   // Create browser windows that are used as reference.
-  scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
-  window->SetBounds(gfx::Rect(10, 10, 200, 200));
-  scoped_ptr<BrowserWindow> browser_window(
-      new TestBrowserWindowAura(window.get()));
-  Browser::CreateParams window_params(profile.get(),
-                                      chrome::HOST_DESKTOP_TYPE_ASH);
-  window_params.window = browser_window.get();
-  scoped_ptr<Browser> window_owning_browser(new Browser(window_params));
+  scoped_ptr<BrowserWindow> browser_window(CreateTestBrowserWindow(
+      CreateTestWindowInShellWithId(0),
+      gfx::Rect(10, 10, 200, 200),
+      Browser::CreateParams(profile.get(), chrome::HOST_DESKTOP_TYPE_ASH)));
   browser_window->Show();
-  EXPECT_EQ(window->GetRootWindow(), ash::Shell::GetActiveRootWindow());
+  EXPECT_EQ(browser_window->GetNativeWindow()->GetRootWindow(),
+            ash::Shell::GetActiveRootWindow());
 
-  scoped_ptr<aura::Window> another_window(CreateTestWindowInShellWithId(1));
-  another_window->SetBounds(gfx::Rect(1600 - 200, 10, 300, 300));
-  scoped_ptr<BrowserWindow> another_browser_window(
-      new TestBrowserWindowAura(another_window.get()));
-  Browser::CreateParams another_window_params(profile.get(),
-                                              chrome::HOST_DESKTOP_TYPE_ASH);
-  another_window_params.window = another_browser_window.get();
-  scoped_ptr<Browser> another_window_owning_browser(
-      new Browser(another_window_params));
+  scoped_ptr<BrowserWindow> another_browser_window(CreateTestBrowserWindow(
+      CreateTestWindowInShellWithId(1),
+      gfx::Rect(400, 10, 300, 300),
+      Browser::CreateParams(profile.get(), chrome::HOST_DESKTOP_TYPE_ASH)));
   another_browser_window->Show();
 
   // Creating a new window to verify the new placement.
-  scoped_ptr<aura::Window> new_window(CreateTestWindowInShellWithId(0));
-  scoped_ptr<BrowserWindow> new_browser_window(
-      new TestBrowserWindowAura(new_window.get()));
-  Browser::CreateParams new_window_params(profile.get(),
-                                          chrome::HOST_DESKTOP_TYPE_ASH);
-  new_window_params.window = new_browser_window.get();
-  scoped_ptr<Browser> new_browser(new Browser(new_window_params));
+  scoped_ptr<TestBrowserWindowAura> new_browser_window(CreateTestBrowserWindow(
+      CreateTestWindowInShellWithId(0),
+      gfx::Rect(),
+      Browser::CreateParams(profile.get(),
+                            chrome::HOST_DESKTOP_TYPE_ASH)));
 
   // Make sure the primary root is active.
   ASSERT_EQ(ash::Shell::GetPrimaryRootWindow(),
@@ -674,29 +630,36 @@
   // First new window should be in the primary.
   {
     gfx::Rect window_bounds;
-    GetWindowBounds(p1600x1200, p1600x1200, secondary,
-                    gfx::Rect(), secondary,
-                    PERSISTED, new_browser.get(), gfx::Rect(), &window_bounds);
-    EXPECT_EQ("0,10 300x300", window_bounds.ToString());
+    GetWindowBounds(p1600x1200, p1600x1200, secondary_bounds,
+                    gfx::Rect(), secondary_bounds,
+                    PERSISTED, new_browser_window->browser(),
+                    gfx::Rect(), &window_bounds);
+    // TODO(oshima): Use exact bounds when the window_sizer_ash is
+    // moved to ash and changed to include the result from
+    // RearrangeVisibleWindowOnShow.
+    EXPECT_TRUE(primary_bounds.Contains(window_bounds));
   }
 
   // Move the window to the right side of the secondary display and create a new
-  // window. It should be opened then on the left side on the secondary display.
+  // window. It should be opened then on the secondary display.
   {
-    gfx::Display second_display = gfx::Screen::GetScreenFor(window.get())->
+    gfx::Display second_display = ash::Shell::GetScreen()->
         GetDisplayNearestPoint(gfx::Point(1600 + 100,10));
-    window->SetBoundsInScreen(
-       gfx::Rect(secondary.CenterPoint().x() + 300, 10, 200, 200),
+    browser_window->GetNativeWindow()->SetBoundsInScreen(
+        gfx::Rect(secondary_bounds.CenterPoint().x() - 100, 10, 200, 200),
         second_display);
     browser_window->Activate();
     EXPECT_NE(ash::Shell::GetPrimaryRootWindow(),
               ash::Shell::GetActiveRootWindow());
-
     gfx::Rect window_bounds;
-    GetWindowBounds(p1600x1200, p1600x1200, secondary,
-                    gfx::Rect(), secondary,
-                    PERSISTED, new_browser.get(), gfx::Rect(), &window_bounds);
-    EXPECT_EQ("1600,10 200x200", window_bounds.ToString());
+    GetWindowBounds(p1600x1200, p1600x1200, secondary_bounds,
+                    gfx::Rect(), secondary_bounds,
+                    PERSISTED, new_browser_window->browser(),
+                    gfx::Rect(), &window_bounds);
+    // TODO(oshima): Use exact bounds when the window_sizer_ash is
+    // moved to ash and changed to include the result from
+    // RearrangeVisibleWindowOnShow.
+    EXPECT_TRUE(secondary_bounds.Contains(window_bounds));
   }
 
   // Activate another window in the primary display and create a new window.
@@ -707,38 +670,34 @@
               ash::Shell::GetActiveRootWindow());
 
     gfx::Rect window_bounds;
-    GetWindowBounds(p1600x1200, p1600x1200, secondary,
-                    gfx::Rect(), secondary,
-                    PERSISTED, new_browser.get(), gfx::Rect(), &window_bounds);
-    EXPECT_EQ("0,10 300x300", window_bounds.ToString());
+    GetWindowBounds(p1600x1200, p1600x1200, secondary_bounds,
+                    gfx::Rect(), secondary_bounds,
+                    PERSISTED, new_browser_window->browser(),
+                    gfx::Rect(), &window_bounds);
+    // TODO(oshima): Use exact bounds when the window_sizer_ash is
+    // moved to ash and changed to include the result from
+    // RearrangeVisibleWindowOnShow.
+    EXPECT_TRUE(primary_bounds.Contains(window_bounds));
   }
 }
 
 // Test that the show state is properly returned for non default cases.
-TEST_F(WindowSizerTest, TestShowState) {
-  // Creating a browser & window to play with.
-  scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
-  window->SetBounds(gfx::Rect(16, 32, 640, 320));
-
+TEST_F(WindowSizerAshTest, TestShowState) {
   scoped_ptr<TestingProfile> profile(new TestingProfile());
 
-  scoped_ptr<BrowserWindow> browser_window(
-      new TestBrowserWindowAura(window.get()));
-  Browser::CreateParams window_params(Browser::TYPE_TABBED, profile.get(),
-                                      chrome::HOST_DESKTOP_TYPE_ASH);
-  window_params.window = browser_window.get();
-  scoped_ptr<Browser> browser(new Browser(window_params));
+  // Creating a browser & window to play with.
+  scoped_ptr<TestBrowserWindowAura> browser_window(CreateTestBrowserWindow(
+      CreateTestWindowInShellWithId(0),
+      gfx::Rect(16, 32, 640, 320),
+      Browser::CreateParams(Browser::TYPE_TABBED, profile.get(),
+                            chrome::HOST_DESKTOP_TYPE_ASH)));
 
   // Create also a popup browser since that behaves different.
-  scoped_ptr<aura::Window> popup(CreateTestWindowInShellWithId(1));
-  popup->SetBounds(gfx::Rect(16, 32, 128, 256));
-
-  scoped_ptr<BrowserWindow> browser_popup(
-      new TestBrowserWindowAura(popup.get()));
-  Browser::CreateParams popup_params(Browser::TYPE_POPUP, profile.get(),
-                                     chrome::HOST_DESKTOP_TYPE_ASH);
-  popup_params.window = browser_window.get();
-  scoped_ptr<Browser> popup_browser(new Browser(popup_params));
+  scoped_ptr<TestBrowserWindowAura> browser_popup(CreateTestBrowserWindow(
+      CreateTestWindowInShellWithId(1),
+      gfx::Rect(16, 32, 640, 320),
+      Browser::CreateParams(Browser::TYPE_POPUP, profile.get(),
+                            chrome::HOST_DESKTOP_TYPE_ASH)));
 
   // Tabbed windows should retrieve the saved window state - since there is a
   // top window.
@@ -746,55 +705,51 @@
             GetWindowShowState(ui::SHOW_STATE_MAXIMIZED,
                                ui::SHOW_STATE_NORMAL,
                                BOTH,
-                               browser.get(),
+                               browser_window->browser(),
                                p1600x1200));
   EXPECT_EQ(ui::SHOW_STATE_DEFAULT,
             GetWindowShowState(ui::SHOW_STATE_DEFAULT,
                                ui::SHOW_STATE_NORMAL,
                                BOTH,
-                               browser.get(),
+                               browser_window->browser(),
                                p1600x1200));
   // Non tabbed windows should always follow the window saved visibility state.
   EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED,
             GetWindowShowState(ui::SHOW_STATE_MAXIMIZED,
                                ui::SHOW_STATE_NORMAL,
                                BOTH,
-                               popup_browser.get(),
+                               browser_popup->browser(),
                                p1600x1200));
   // The non tabbed window will take the status of the last active of its kind.
   EXPECT_EQ(ui::SHOW_STATE_NORMAL,
             GetWindowShowState(ui::SHOW_STATE_DEFAULT,
                                ui::SHOW_STATE_NORMAL,
                                BOTH,
-                               popup_browser.get(),
+                               browser_popup->browser(),
                                p1600x1200));
 
   // Now create a top level window and check again for both. Only the tabbed
   // window should follow the top level window's state.
   // Creating a browser & window to play with.
-  scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(0));
-  window->SetBounds(gfx::Rect(16, 32, 640, 320));
-
-  scoped_ptr<BrowserWindow> browser_window2(
-      new TestBrowserWindowAura(window2.get()));
-  Browser::CreateParams window2_params(Browser::TYPE_TABBED, profile.get(),
-                                       chrome::HOST_DESKTOP_TYPE_ASH);
-  window2_params.window = browser_window2.get();
-  scoped_ptr<Browser> browser2(new Browser(window2_params));
+  scoped_ptr<TestBrowserWindowAura> browser_window2(CreateTestBrowserWindow(
+      CreateTestWindowInShellWithId(3),
+      gfx::Rect(16, 32, 640, 320),
+      Browser::CreateParams(Browser::TYPE_TABBED, profile.get(),
+                            chrome::HOST_DESKTOP_TYPE_ASH)));
 
   // A tabbed window should now take the top level window state.
   EXPECT_EQ(ui::SHOW_STATE_DEFAULT,
             GetWindowShowState(ui::SHOW_STATE_MAXIMIZED,
                                ui::SHOW_STATE_DEFAULT,
                                BOTH,
-                               browser2.get(),
+                               browser_window->browser(),
                                p1600x1200));
   // Non tabbed windows should always follow the window saved visibility state.
   EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED,
             GetWindowShowState(ui::SHOW_STATE_MAXIMIZED,
                                ui::SHOW_STATE_MINIMIZED,
                                BOTH,
-                               popup_browser.get(),
+                               browser_popup->browser(),
                                p1600x1200));
 
   // In smaller screen resolutions we default to maximized if there is no other
@@ -806,87 +761,79 @@
               GetWindowShowState(ui::SHOW_STATE_MAXIMIZED,
                                  ui::SHOW_STATE_DEFAULT,
                                  BOTH,
-                                 browser2.get(),
+                                 browser_window->browser(),
                                  tiny_screen));
-    window->Hide();
+    browser_window->Hide();
     EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED,
               GetWindowShowState(ui::SHOW_STATE_MAXIMIZED,
                                  ui::SHOW_STATE_DEFAULT,
                                  BOTH,
-                                 browser2.get(),
+                                 browser_window2->browser(),
                                  tiny_screen));
 
   }
 }
 
 // Test that the default show state override behavior is properly handled.
-TEST_F(WindowSizerTest, TestShowStateDefaults) {
+TEST_F(WindowSizerAshTest, TestShowStateDefaults) {
   // Creating a browser & window to play with.
-  scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
-  window->SetBounds(gfx::Rect(16, 32, 640, 320));
-
   scoped_ptr<TestingProfile> profile(new TestingProfile());
 
-  scoped_ptr<BrowserWindow> browser_window(
-      new TestBrowserWindowAura(window.get()));
-  Browser::CreateParams window_params(Browser::TYPE_TABBED, profile.get(),
-                                      chrome::HOST_DESKTOP_TYPE_ASH);
-  window_params.window = browser_window.get();
-  scoped_ptr<Browser> browser(new Browser(window_params));
+  scoped_ptr<TestBrowserWindowAura> browser_window(CreateTestBrowserWindow(
+      CreateTestWindowInShellWithId(0),
+      gfx::Rect(16, 32, 640, 320),
+      Browser::CreateParams(Browser::TYPE_TABBED, profile.get(),
+                            chrome::HOST_DESKTOP_TYPE_ASH)));
 
   // Create also a popup browser since that behaves slightly different for
   // defaults.
-  scoped_ptr<aura::Window> popup(CreateTestWindowInShellWithId(1));
-  popup->SetBounds(gfx::Rect(16, 32, 128, 256));
-
-  scoped_ptr<BrowserWindow> browser_popup(
-      new TestBrowserWindowAura(popup.get()));
-  Browser::CreateParams popup_params(Browser::TYPE_POPUP, profile.get(),
-                                     chrome::HOST_DESKTOP_TYPE_ASH);
-  popup_params.window = browser_window.get();
-  scoped_ptr<Browser> popup_browser(new Browser(popup_params));
+  scoped_ptr<TestBrowserWindowAura> browser_popup(CreateTestBrowserWindow(
+      CreateTestWindowInShellWithId(1),
+      gfx::Rect(16, 32, 128, 256),
+      Browser::CreateParams(Browser::TYPE_POPUP, profile.get(),
+                            chrome::HOST_DESKTOP_TYPE_ASH)));
 
   // Check that a browser creation state always get used if not given as
   // SHOW_STATE_DEFAULT.
   EXPECT_EQ(GetWindowShowState(ui::SHOW_STATE_MAXIMIZED,
                                ui::SHOW_STATE_MAXIMIZED,
                                DEFAULT,
-                               browser.get(),
+                               browser_window->browser(),
                                p1600x1200), ui::SHOW_STATE_DEFAULT);
-  browser->set_initial_show_state(ui::SHOW_STATE_MINIMIZED);
+  browser_window->browser()->set_initial_show_state(ui::SHOW_STATE_MINIMIZED);
   EXPECT_EQ(GetWindowShowState(ui::SHOW_STATE_MAXIMIZED,
                                ui::SHOW_STATE_MAXIMIZED,
                                BOTH,
-                               browser.get(),
+                               browser_window->browser(),
                                p1600x1200), ui::SHOW_STATE_MINIMIZED);
-  browser->set_initial_show_state(ui::SHOW_STATE_NORMAL);
+  browser_window->browser()->set_initial_show_state(ui::SHOW_STATE_NORMAL);
   EXPECT_EQ(GetWindowShowState(ui::SHOW_STATE_MAXIMIZED,
                                ui::SHOW_STATE_MAXIMIZED,
                                BOTH,
-                               browser.get(),
+                               browser_window->browser(),
                                p1600x1200), ui::SHOW_STATE_NORMAL);
-  browser->set_initial_show_state(ui::SHOW_STATE_MAXIMIZED);
+  browser_window->browser()->set_initial_show_state(ui::SHOW_STATE_MAXIMIZED);
   EXPECT_EQ(GetWindowShowState(ui::SHOW_STATE_NORMAL,
                                ui::SHOW_STATE_NORMAL,
                                BOTH,
-                               browser.get(),
+                               browser_window->browser(),
                                p1600x1200), ui::SHOW_STATE_MAXIMIZED);
 
   // Check that setting the maximized command line option is forcing the
   // maximized state.
   CommandLine::ForCurrentProcess()->AppendSwitch(switches::kStartMaximized);
 
-  browser->set_initial_show_state(ui::SHOW_STATE_NORMAL);
+  browser_window->browser()->set_initial_show_state(ui::SHOW_STATE_NORMAL);
   EXPECT_EQ(GetWindowShowState(ui::SHOW_STATE_NORMAL,
                                ui::SHOW_STATE_NORMAL,
                                BOTH,
-                               browser.get(),
+                               browser_window->browser(),
                                p1600x1200), ui::SHOW_STATE_MAXIMIZED);
 
   // The popup should favor the initial show state over the command line.
   EXPECT_EQ(GetWindowShowState(ui::SHOW_STATE_NORMAL,
                                ui::SHOW_STATE_NORMAL,
                                BOTH,
-                               popup_browser.get(),
+                               browser_popup->browser(),
                                p1600x1200), ui::SHOW_STATE_NORMAL);
 }
diff --git a/chrome/browser/ui/window_sizer/window_sizer_common_unittest.cc b/chrome/browser/ui/window_sizer/window_sizer_common_unittest.cc
index 954f89e..f0984a5 100644
--- a/chrome/browser/ui/window_sizer/window_sizer_common_unittest.cc
+++ b/chrome/browser/ui/window_sizer/window_sizer_common_unittest.cc
@@ -10,49 +10,101 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/testing_profile.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/display.h"
+#include "ui/gfx/screen.h"
 
-TestMonitorInfoProvider::TestMonitorInfoProvider() {};
+namespace {
 
-TestMonitorInfoProvider::~TestMonitorInfoProvider() {};
+class TestScreen : public gfx::Screen {
+ public:
+  TestScreen() {}
+  virtual ~TestScreen() {}
 
-void TestMonitorInfoProvider::AddMonitor(const gfx::Rect& bounds,
-                                         const gfx::Rect& work_area) {
-  DCHECK(bounds.Contains(work_area));
-  monitor_bounds_.push_back(bounds);
-  work_areas_.push_back(work_area);
-}
-
-// Overridden from WindowSizer::MonitorInfoProvider:
-gfx::Rect TestMonitorInfoProvider::GetPrimaryDisplayWorkArea() const {
-  return work_areas_[0];
-}
-
-gfx::Rect TestMonitorInfoProvider::GetPrimaryDisplayBounds() const {
-  return monitor_bounds_[0];
-}
-
-gfx::Rect TestMonitorInfoProvider::GetMonitorWorkAreaMatching(
-    const gfx::Rect& match_rect) const {
-  return work_areas_[GetMonitorIndexMatchingBounds(match_rect)];
-}
-
-size_t TestMonitorInfoProvider::GetMonitorIndexMatchingBounds(
-    const gfx::Rect& match_rect) const {
-  int max_area = 0;
-  size_t max_area_index = 0;
-  // Loop through all the monitors, finding the one that intersects the
-  // largest area of the supplied match rect.
-  for (size_t i = 0; i < work_areas_.size(); ++i) {
-    gfx::Rect overlap = work_areas_[i];
-    overlap.Intersect(match_rect);
-    int area = overlap.width() * overlap.height();
-    if (area > max_area) {
-      max_area = area;
-      max_area_index = i;
-    }
+  // Overridden from gfx::Screen:
+  virtual bool IsDIPEnabled() OVERRIDE {
+    NOTREACHED();
+    return false;
   }
-  return max_area_index;
-}
+
+  virtual gfx::Point GetCursorScreenPoint() OVERRIDE {
+    NOTREACHED();
+    return gfx::Point();
+  }
+
+  virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE {
+    NOTREACHED();
+    return NULL;
+  }
+
+  virtual gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point)
+      OVERRIDE {
+    NOTREACHED();
+    return NULL;
+  }
+
+  virtual int GetNumDisplays() const OVERRIDE {
+    return displays_.size();
+  }
+
+  virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE {
+    return displays_;
+  }
+
+  virtual gfx::Display GetDisplayNearestWindow(
+      gfx::NativeView view) const OVERRIDE {
+    NOTREACHED();
+    return gfx::Display();
+  }
+
+  virtual gfx::Display GetDisplayNearestPoint(
+      const gfx::Point& point) const OVERRIDE {
+    NOTREACHED();
+    return gfx::Display();
+  }
+
+  virtual gfx::Display GetDisplayMatching(
+      const gfx::Rect& match_rect) const OVERRIDE {
+    int max_area = 0;
+    size_t max_area_index = 0;
+
+    for (size_t i = 0; i < displays_.size(); ++i) {
+      gfx::Rect overlap = displays_[i].bounds();
+      overlap.Intersect(match_rect);
+      int area = overlap.width() * overlap.height();
+      if (area > max_area) {
+        max_area = area;
+        max_area_index = i;
+      }
+    }
+    return displays_[max_area_index];
+  }
+
+  virtual gfx::Display GetPrimaryDisplay() const OVERRIDE {
+    return displays_[0];
+  }
+
+  virtual void AddObserver(gfx::DisplayObserver* observer) OVERRIDE {
+    NOTREACHED();
+  }
+
+  virtual void RemoveObserver(gfx::DisplayObserver* observer) OVERRIDE {
+    NOTREACHED();
+  }
+
+  void AddDisplay(const gfx::Rect& bounds,
+                          const gfx::Rect& work_area) {
+    gfx::Display display(displays_.size(), bounds);
+    display.set_work_area(work_area);
+    displays_.push_back(display);
+  }
+
+ private:
+  std::vector<gfx::Display> displays_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestScreen);
+};
+
+}  // namespace
 
 TestStateProvider::TestStateProvider():
     has_persistent_data_(false),
@@ -118,17 +170,17 @@
     gfx::Rect* out_bounds,
     ui::WindowShowState* out_show_state) {
   DCHECK(out_show_state);
-  TestMonitorInfoProvider* mip = new TestMonitorInfoProvider;
-  mip->AddMonitor(monitor1_bounds, monitor1_work_area);
+  TestScreen test_screen;
+  test_screen.AddDisplay(monitor1_bounds, monitor1_work_area);
   if (!monitor2_bounds.IsEmpty())
-    mip->AddMonitor(monitor2_bounds, monitor2_bounds);
+    test_screen.AddDisplay(monitor2_bounds, monitor2_bounds);
   TestStateProvider* sp = new TestStateProvider;
   if (source == PERSISTED || source == BOTH)
     sp->SetPersistentState(bounds, work_area, show_state_persisted, true);
   if (source == LAST_ACTIVE || source == BOTH)
     sp->SetLastActiveState(bounds, show_state_last, true);
 
-  WindowSizer sizer(sp, mip, browser);
+  WindowSizer sizer(sp, &test_screen, browser);
   sizer.DetermineWindowBoundsAndShowState(passed_in,
                                           out_bounds,
                                           out_show_state);
@@ -158,15 +210,15 @@
     const gfx::Rect& display_config) {
   gfx::Rect bounds = display_config;
   gfx::Rect work_area = display_config;
-  TestMonitorInfoProvider* mip = new TestMonitorInfoProvider;
-  mip->AddMonitor(display_config, display_config);
+  TestScreen test_screen;
+  test_screen.AddDisplay(display_config, display_config);
   TestStateProvider* sp = new TestStateProvider;
   if (source == PERSISTED || source == BOTH)
     sp->SetPersistentState(bounds, work_area, show_state_persisted, true);
   if (source == LAST_ACTIVE || source == BOTH)
     sp->SetLastActiveState(bounds, show_state_last, true);
 
-  WindowSizer sizer(sp, mip, browser);
+  WindowSizer sizer(sp, &test_screen, browser);
 
   ui::WindowShowState out_show_state = ui::SHOW_STATE_DEFAULT;
   gfx::Rect out_bounds;
diff --git a/chrome/browser/ui/window_sizer/window_sizer_common_unittest.h b/chrome/browser/ui/window_sizer/window_sizer_common_unittest.h
index 5ad48a9..efab710 100644
--- a/chrome/browser/ui/window_sizer/window_sizer_common_unittest.h
+++ b/chrome/browser/ui/window_sizer/window_sizer_common_unittest.h
@@ -48,32 +48,6 @@
 
 extern int kWindowTilePixels;
 
-// Testing implementation of WindowSizer::MonitorInfoProvider that we can use
-// to fake various monitor layouts and sizes.
-class TestMonitorInfoProvider : public MonitorInfoProvider {
- public:
-  TestMonitorInfoProvider();
-  virtual ~TestMonitorInfoProvider();
-
-  void AddMonitor(const gfx::Rect& bounds, const gfx::Rect& work_area);
-
-  // Overridden from WindowSizer::MonitorInfoProvider:
-  virtual gfx::Rect GetPrimaryDisplayWorkArea() const OVERRIDE;
-
-  virtual gfx::Rect GetPrimaryDisplayBounds() const OVERRIDE;
-
-  virtual gfx::Rect GetMonitorWorkAreaMatching(
-      const gfx::Rect& match_rect) const OVERRIDE;
-
- private:
-  size_t GetMonitorIndexMatchingBounds(const gfx::Rect& match_rect) const;
-
-  std::vector<gfx::Rect> monitor_bounds_;
-  std::vector<gfx::Rect> work_areas_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestMonitorInfoProvider);
-};
-
 // Testing implementation of WindowSizer::StateProvider that we use to fake
 // persistent storage and existing windows.
 class TestStateProvider : public WindowSizer::StateProvider {
diff --git a/chrome/browser/unload_browsertest.cc b/chrome/browser/unload_browsertest.cc
index 2eff4af..588f91a 100644
--- a/chrome/browser/unload_browsertest.cc
+++ b/chrome/browser/unload_browsertest.cc
@@ -563,12 +563,16 @@
   chrome::CloseWindow(browser());
   window_observer.Wait();
 }
-IN_PROC_BROWSER_TEST_F(FastUnloadTest, WindowCloseFinishesUnload) {
+
+// Flaky on Windows bots (http://crbug.com/279267).
 #if defined(OS_WIN)
-  // Flaky on Win7+ bots (http://crbug.com/267597).
-  if (base::win::GetVersion() >= base::win::VERSION_WIN7)
-    return;
+#define MAYBE_WindowCloseFinishesUnload \
+    DISABLED_WindowCloseFinishesUnload
+#else
+#define MAYBE_WindowCloseFinishesUnload \
+    WindowCloseFinishesUnload
 #endif
+IN_PROC_BROWSER_TEST_F(FastUnloadTest, MAYBE_WindowCloseFinishesUnload) {
   // Check for cookie set in unload during PRE_ test.
   NavigateToPage("no_listeners");
   EXPECT_EQ("unloaded=ohyeah", GetCookies("no_listeners"));
diff --git a/chrome/browser/upgrade_detector_impl.cc b/chrome/browser/upgrade_detector_impl.cc
index 0bfdbf5..1ed7c56 100644
--- a/chrome/browser/upgrade_detector_impl.cc
+++ b/chrome/browser/upgrade_detector_impl.cc
@@ -12,7 +12,6 @@
 #include "base/files/file_path.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/singleton.h"
-#include "base/metrics/field_trial.h"
 #include "base/path_service.h"
 #include "base/process/launch.h"
 #include "base/strings/string_number_conversions.h"
@@ -56,10 +55,6 @@
 // The number of days after which we identify a build/install as outdated.
 const uint64 kOutdatedBuildAgeInDays = 12 * 7;
 
-// Finch Experiment strings to identify if we should check for outdated install.
-const char kOutdatedInstallCheckTrialName[] = "OutdatedInstallCheck";
-const char kOutdatedInstallCheck12WeeksGroupName[] = "12WeeksOutdatedInstall";
-
 std::string CmdLineInterval() {
   const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
   return cmd_line.GetSwitchValueASCII(switches::kCheckForUpdateIntervalSec);
@@ -322,11 +317,6 @@
   static bool simulate_outdated = CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kSimulateOutdated);
   if (!simulate_outdated) {
-    if (base::FieldTrialList::FindFullName(kOutdatedInstallCheckTrialName) !=
-            kOutdatedInstallCheck12WeeksGroupName) {
-      return false;
-    }
-
     // Also don't show the bubble if we have a brand code that is NOT organic.
     std::string brand;
     if (google_util::GetBrand(&brand) && !google_util::IsOrganic(brand))
diff --git a/chrome/browser/usb/usb_device.cc b/chrome/browser/usb/usb_device.cc
index 6041e68..a488380 100644
--- a/chrome/browser/usb/usb_device.cc
+++ b/chrome/browser/usb/usb_device.cc
@@ -50,8 +50,11 @@
   PlatformUsbDeviceHandle handle;
   int rv = libusb_open(platform_device_, &handle);
   if (LIBUSB_SUCCESS == rv) {
+    scoped_refptr<UsbConfigDescriptor> interfaces = ListInterfaces();
+    if (!interfaces)
+      return NULL;
     scoped_refptr<UsbDeviceHandle> device_handle =
-        new UsbDeviceHandle(context_, this, handle);
+        new UsbDeviceHandle(context_, this, handle, interfaces);
     handles_.push_back(device_handle);
     return device_handle;
   }
@@ -73,16 +76,16 @@
   return false;
 }
 
-bool UsbDevice::ListInterfaces(UsbConfigDescriptor* config) {
+scoped_refptr<UsbConfigDescriptor> UsbDevice::ListInterfaces() {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   PlatformUsbConfigDescriptor platform_config;
   const int list_result =
       libusb_get_active_config_descriptor(platform_device_, &platform_config);
   if (list_result == 0)
-    config->Reset(platform_config);
+    return new UsbConfigDescriptor(platform_config);
 
-  return list_result == 0;
+  return NULL;
 }
 
 void UsbDevice::OnDisconnect() {
diff --git a/chrome/browser/usb/usb_device.h b/chrome/browser/usb/usb_device.h
index 524cee3..8891ad0 100644
--- a/chrome/browser/usb/usb_device.h
+++ b/chrome/browser/usb/usb_device.h
@@ -42,7 +42,7 @@
   // Lists the interfaces provided by the device and fills the given
   // UsbConfigDescriptor.
   // Blocking method. Must be called on FILE thread.
-  virtual bool ListInterfaces(UsbConfigDescriptor* config);
+  virtual scoped_refptr<UsbConfigDescriptor> ListInterfaces();
 
  protected:
   friend class UsbService;
diff --git a/chrome/browser/usb/usb_device_handle.cc b/chrome/browser/usb/usb_device_handle.cc
index 32903ba..6d32be4 100644
--- a/chrome/browser/usb/usb_device_handle.cc
+++ b/chrome/browser/usb/usb_device_handle.cc
@@ -178,12 +178,15 @@
 UsbDeviceHandle::UsbDeviceHandle(
     scoped_refptr<UsbContext> context,
     UsbDevice* device,
-    PlatformUsbDeviceHandle handle)
-    : device_(device), handle_(handle), context_(context) {
+    PlatformUsbDeviceHandle handle,
+    scoped_refptr<UsbConfigDescriptor> interfaces)
+    : device_(device),
+      handle_(handle),
+      interfaces_(interfaces),
+      context_(context) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(handle) << "Cannot create device with NULL handle.";
-  interfaces_ = new UsbConfigDescriptor();
-  device->ListInterfaces(interfaces_.get());
+  DCHECK(interfaces_) << "Unabled to list interfaces";
 }
 
 UsbDeviceHandle::UsbDeviceHandle() : device_(NULL), handle_(NULL) {
@@ -524,7 +527,7 @@
   endpoint_map_.clear();
   for (ClaimedInterfaceMap::iterator it = claimed_interfaces_.begin();
       it != claimed_interfaces_.end(); ++it) {
-    scoped_refptr<const UsbInterfaceDescriptor> interface_desc =
+    scoped_refptr<const UsbInterfaceAltSettingDescriptor> interface_desc =
         interfaces_->GetInterface(it->first)->GetAltSetting(
             it->second->alternate_setting());
     for (size_t i = 0; i < interface_desc->GetNumEndpoints(); i++) {
diff --git a/chrome/browser/usb/usb_device_handle.h b/chrome/browser/usb/usb_device_handle.h
index f7aa43f..3a5bd47 100644
--- a/chrome/browser/usb/usb_device_handle.h
+++ b/chrome/browser/usb/usb_device_handle.h
@@ -28,7 +28,7 @@
 class UsbContext;
 class UsbConfigDescriptor;
 class UsbDevice;
-class UsbInterface;
+class UsbInterfaceDescriptor;
 
 namespace base {
   class MessageLoopProxy;
@@ -119,7 +119,8 @@
 
   // This constructor is called by UsbDevice.
   UsbDeviceHandle(scoped_refptr<UsbContext> context,
-                  UsbDevice* device, PlatformUsbDeviceHandle handle);
+                  UsbDevice* device, PlatformUsbDeviceHandle handle,
+                  scoped_refptr<UsbConfigDescriptor> interfaces);
 
   // This constructor variant is for use in testing only.
   UsbDeviceHandle();
diff --git a/chrome/browser/usb/usb_interface.cc b/chrome/browser/usb/usb_interface.cc
index b3ff83c..d5a1e0f 100644
--- a/chrome/browser/usb/usb_interface.cc
+++ b/chrome/browser/usb/usb_interface.cc
@@ -85,63 +85,63 @@
   return descriptor_->bInterval;
 }
 
-UsbInterfaceDescriptor::UsbInterfaceDescriptor(
+UsbInterfaceAltSettingDescriptor::UsbInterfaceAltSettingDescriptor(
     scoped_refptr<const UsbConfigDescriptor> config,
     PlatformUsbInterfaceDescriptor descriptor)
     : config_(config), descriptor_(descriptor) {
 }
 
-UsbInterfaceDescriptor::~UsbInterfaceDescriptor() {}
+UsbInterfaceAltSettingDescriptor::~UsbInterfaceAltSettingDescriptor() {}
 
-size_t UsbInterfaceDescriptor::GetNumEndpoints() const {
+size_t UsbInterfaceAltSettingDescriptor::GetNumEndpoints() const {
   return descriptor_->bNumEndpoints;
 }
 
 scoped_refptr<const UsbEndpointDescriptor>
-    UsbInterfaceDescriptor::GetEndpoint(size_t index) const {
-  return make_scoped_refptr(new UsbEndpointDescriptor(config_,
-      &descriptor_->endpoint[index]));
+    UsbInterfaceAltSettingDescriptor::GetEndpoint(size_t index) const {
+  return new UsbEndpointDescriptor(config_, &descriptor_->endpoint[index]);
 }
 
-int UsbInterfaceDescriptor::GetInterfaceNumber() const {
+int UsbInterfaceAltSettingDescriptor::GetInterfaceNumber() const {
   return descriptor_->bInterfaceNumber;
 }
 
-int UsbInterfaceDescriptor::GetAlternateSetting() const {
+int UsbInterfaceAltSettingDescriptor::GetAlternateSetting() const {
   return descriptor_->bAlternateSetting;
 }
 
-int UsbInterfaceDescriptor::GetInterfaceClass() const {
+int UsbInterfaceAltSettingDescriptor::GetInterfaceClass() const {
   return descriptor_->bInterfaceClass;
 }
 
-int UsbInterfaceDescriptor::GetInterfaceSubclass() const {
+int UsbInterfaceAltSettingDescriptor::GetInterfaceSubclass() const {
   return descriptor_->bInterfaceSubClass;
 }
 
-int UsbInterfaceDescriptor::GetInterfaceProtocol() const {
+int UsbInterfaceAltSettingDescriptor::GetInterfaceProtocol() const {
   return descriptor_->bInterfaceProtocol;
 }
 
-UsbInterface::UsbInterface(scoped_refptr<const UsbConfigDescriptor> config,
+UsbInterfaceDescriptor::UsbInterfaceDescriptor(
+    scoped_refptr<const UsbConfigDescriptor> config,
     PlatformUsbInterface usbInterface)
     : config_(config), interface_(usbInterface) {
 }
 
-UsbInterface::~UsbInterface() {}
+UsbInterfaceDescriptor::~UsbInterfaceDescriptor() {}
 
-size_t UsbInterface::GetNumAltSettings() const {
+size_t UsbInterfaceDescriptor::GetNumAltSettings() const {
   return interface_->num_altsetting;
 }
 
-scoped_refptr<const UsbInterfaceDescriptor>
-    UsbInterface::GetAltSetting(size_t index) const {
-  return make_scoped_refptr(new UsbInterfaceDescriptor(config_,
-      &interface_->altsetting[index]));
+scoped_refptr<const UsbInterfaceAltSettingDescriptor>
+    UsbInterfaceDescriptor::GetAltSetting(size_t index) const {
+  return new UsbInterfaceAltSettingDescriptor(config_,
+                                              &interface_->altsetting[index]);
 }
 
-UsbConfigDescriptor::UsbConfigDescriptor()
-    : config_(NULL) {
+UsbConfigDescriptor::UsbConfigDescriptor(PlatformUsbConfigDescriptor config)
+    : config_(config) {
 }
 
 UsbConfigDescriptor::~UsbConfigDescriptor() {
@@ -151,16 +151,11 @@
   }
 }
 
-void UsbConfigDescriptor::Reset(PlatformUsbConfigDescriptor config) {
-  config_ = config;
-}
-
 size_t UsbConfigDescriptor::GetNumInterfaces() const {
   return config_->bNumInterfaces;
 }
 
-scoped_refptr<const UsbInterface>
+scoped_refptr<const UsbInterfaceDescriptor>
     UsbConfigDescriptor::GetInterface(size_t index) const {
-  return make_scoped_refptr(new UsbInterface(make_scoped_refptr(this),
-          &config_->interface[index]));
+  return new UsbInterfaceDescriptor(this, &config_->interface[index]);
 }
diff --git a/chrome/browser/usb/usb_interface.h b/chrome/browser/usb/usb_interface.h
index ca01210..e73b044 100644
--- a/chrome/browser/usb/usb_interface.h
+++ b/chrome/browser/usb/usb_interface.h
@@ -6,7 +6,6 @@
 #define CHROME_BROWSER_USB_USB_INTERFACE_H_
 
 #include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
 
 struct libusb_config_descriptor;
 struct libusb_endpoint_descriptor;
@@ -18,8 +17,6 @@
 typedef const libusb_interface* PlatformUsbInterface;
 typedef const libusb_interface_descriptor* PlatformUsbInterfaceDescriptor;
 
-class UsbDeviceHandle;
-
 enum UsbTransferType {
   USB_TRANSFER_CONTROL = 0,
   USB_TRANSFER_ISOCHRONOUS,
@@ -45,13 +42,14 @@
   USB_USAGE_EXPLICIT_FEEDBACK
 };
 
+class UsbDevice;
 class UsbConfigDescriptor;
+class UsbInterfaceDescriptor;
+class UsbInterfaceAltSettingDescriptor;
 
-class UsbEndpointDescriptor : public base::RefCounted<UsbEndpointDescriptor> {
+class UsbEndpointDescriptor
+    : public base::RefCounted<const UsbEndpointDescriptor> {
  public:
-  UsbEndpointDescriptor(scoped_refptr<const UsbConfigDescriptor> config,
-      PlatformUsbEndpointDescriptor descriptor);
-
   int GetAddress() const;
   UsbEndpointDirection GetDirection() const;
   int GetMaximumPacketSize() const;
@@ -61,22 +59,25 @@
   int GetPollingInterval() const;
 
  private:
-  friend class base::RefCounted<UsbEndpointDescriptor>;
+  friend class base::RefCounted<const UsbEndpointDescriptor>;
+  friend class UsbInterfaceAltSettingDescriptor;
+
+  UsbEndpointDescriptor(
+      scoped_refptr<const UsbConfigDescriptor> config,
+      PlatformUsbEndpointDescriptor descriptor);
   ~UsbEndpointDescriptor();
 
   scoped_refptr<const UsbConfigDescriptor> config_;
   PlatformUsbEndpointDescriptor descriptor_;
+
+  DISALLOW_COPY_AND_ASSIGN(UsbEndpointDescriptor);
 };
 
-class UsbInterfaceDescriptor
-    : public base::RefCounted<UsbInterfaceDescriptor> {
+class UsbInterfaceAltSettingDescriptor
+    : public base::RefCounted<const UsbInterfaceAltSettingDescriptor> {
  public:
-  UsbInterfaceDescriptor(scoped_refptr<const UsbConfigDescriptor> config,
-      PlatformUsbInterfaceDescriptor descriptor);
-
   size_t GetNumEndpoints() const;
-  scoped_refptr<const UsbEndpointDescriptor>
-      GetEndpoint(size_t index) const;
+  scoped_refptr<const UsbEndpointDescriptor> GetEndpoint(size_t index) const;
 
   int GetInterfaceNumber() const;
   int GetAlternateSetting() const;
@@ -85,46 +86,56 @@
   int GetInterfaceProtocol() const;
 
  private:
-  friend class base::RefCounted<UsbInterfaceDescriptor>;
-  ~UsbInterfaceDescriptor();
+  friend class base::RefCounted<const UsbInterfaceAltSettingDescriptor>;
+  friend class UsbInterfaceDescriptor;
+
+  UsbInterfaceAltSettingDescriptor(
+      scoped_refptr<const UsbConfigDescriptor> config,
+      PlatformUsbInterfaceDescriptor descriptor);
+  ~UsbInterfaceAltSettingDescriptor();
 
   scoped_refptr<const UsbConfigDescriptor> config_;
   PlatformUsbInterfaceDescriptor descriptor_;
+
+  DISALLOW_COPY_AND_ASSIGN(UsbInterfaceAltSettingDescriptor);
 };
 
-class UsbInterface : public base::RefCounted<UsbInterface> {
+class UsbInterfaceDescriptor
+    : public base::RefCounted<const UsbInterfaceDescriptor> {
  public:
-  UsbInterface(scoped_refptr<const UsbConfigDescriptor> config,
-      PlatformUsbInterface usbInterface);
-
   size_t GetNumAltSettings() const;
-  scoped_refptr<const UsbInterfaceDescriptor>
-      GetAltSetting(size_t index) const;
+  scoped_refptr<const UsbInterfaceAltSettingDescriptor> GetAltSetting(
+      size_t index) const;
 
  private:
-  friend class base::RefCounted<UsbInterface>;
-  ~UsbInterface();
+  friend class base::RefCounted<const UsbInterfaceDescriptor>;
+  friend class UsbConfigDescriptor;
+
+  UsbInterfaceDescriptor(scoped_refptr<const UsbConfigDescriptor> config,
+               PlatformUsbInterface usbInterface);
+  ~UsbInterfaceDescriptor();
 
   scoped_refptr<const UsbConfigDescriptor> config_;
   PlatformUsbInterface interface_;
+
+  DISALLOW_COPY_AND_ASSIGN(UsbInterfaceDescriptor);
 };
 
 class UsbConfigDescriptor : public base::RefCounted<UsbConfigDescriptor> {
  public:
-  UsbConfigDescriptor();
-
-  void Reset(PlatformUsbConfigDescriptor config);
-
   size_t GetNumInterfaces() const;
-
-  scoped_refptr<const UsbInterface>
-      GetInterface(size_t index) const;
+  scoped_refptr<const UsbInterfaceDescriptor> GetInterface(size_t index) const;
 
  private:
   friend class base::RefCounted<UsbConfigDescriptor>;
+  friend class UsbDevice;
+
+  explicit UsbConfigDescriptor(PlatformUsbConfigDescriptor config);
   ~UsbConfigDescriptor();
 
   PlatformUsbConfigDescriptor config_;
+
+  DISALLOW_COPY_AND_ASSIGN(UsbConfigDescriptor);
 };
 
 #endif  // CHROME_BROWSER_USB_USB_INTERFACE_H_
diff --git a/chrome/browser/web_applications/web_app.cc b/chrome/browser/web_applications/web_app.cc
index 8ab546d..10c2c99 100644
--- a/chrome/browser/web_applications/web_app.cc
+++ b/chrome/browser/web_applications/web_app.cc
@@ -199,7 +199,7 @@
       chrome::kFileSystemScheme,
       chrome::kFtpScheme,
       chrome::kHttpScheme,
-      chrome::kHttpsScheme,
+      content::kHttpsScheme,
       extensions::kExtensionScheme,
   };
 
diff --git a/chrome/browser/web_applications/web_app_mac.mm b/chrome/browser/web_applications/web_app_mac.mm
index 3811329..8c5cd75 100644
--- a/chrome/browser/web_applications/web_app_mac.mm
+++ b/chrome/browser/web_applications/web_app_mac.mm
@@ -17,6 +17,8 @@
 #include "base/mac/mac_util.h"
 #include "base/mac/scoped_cftyperef.h"
 #include "base/path_service.h"
+#include "base/process/process_handle.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/string16.h"
 #include "base/strings/sys_string_conversions.h"
@@ -210,7 +212,9 @@
     return;
 
   CommandLine command_line(CommandLine::NO_PROGRAM);
-  command_line.AppendSwitch(app_mode::kNoLaunchApp);
+  command_line.AppendSwitchASCII(
+      app_mode::kLaunchedByChromeProcessId,
+      base::IntToString(base::GetCurrentProcId()));
   // Launch without activating (kLSLaunchDontSwitch).
   base::mac::OpenApplicationWithPath(
       shim_path, command_line, kLSLaunchDefaults | kLSLaunchDontSwitch, NULL);
diff --git a/chrome/browser/web_resource/promo_resource_service_unittest.cc b/chrome/browser/web_resource/promo_resource_service_unittest.cc
index 0f0ae62..7ac89cc 100644
--- a/chrome/browser/web_resource/promo_resource_service_unittest.cc
+++ b/chrome/browser/web_resource/promo_resource_service_unittest.cc
@@ -530,7 +530,7 @@
   GURL promo_server_url = NotificationPromo::PromoServerURL();
   EXPECT_FALSE(promo_server_url.is_empty());
   EXPECT_TRUE(promo_server_url.is_valid());
-  EXPECT_TRUE(promo_server_url.SchemeIs(chrome::kHttpsScheme));
+  EXPECT_TRUE(promo_server_url.SchemeIs(content::kHttpsScheme));
   // TODO(achuith): Test this better.
 }
 
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index b9988b8..2202c03 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -136,17 +136,7 @@
                   ]
                 }
               ],
-            }],
-            ['OS=="linux" and chromeos==1 and branding=="Chrome"', {
-              'copies': [
-                {
-                  'destination': '<(PRODUCT_DIR)',
-                  'files': [
-                    'browser/extensions/default_extensions/chromeos/extensions/'
-                  ]
-                }
-              ],
-            }],
+            }]
           ],
         },
         {
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 45ed2d1..c0301ef 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -26,11 +26,14 @@
         'probe_message_proto',
         'safe_browsing_proto',
         'safe_browsing_report_proto',
-        'variations_seed_proto',
         '../components/components.gyp:browser_context_keyed_service',
         '../components/components.gyp:encryptor',
         '../components/components.gyp:sessions',
         '../components/components.gyp:user_prefs',
+        # The direct variations_seed_proto dependency is needed, rather than
+        # using common's dependency on variations, because the proto sources
+        # need to be generated before code in this target can start building.
+        '../components/components.gyp:variations_seed_proto',
         '../components/components.gyp:visitedlink_browser',
         '../components/components.gyp:visitedlink_common',
         '../components/components.gyp:web_modal',
@@ -40,7 +43,6 @@
         '../crypto/crypto.gyp:crypto',
         '../google_apis/google_apis.gyp:google_apis',
         '../jingle/jingle.gyp:notifier',
-        '../media/media.gyp:media',
         '../skia/skia.gyp:skia',
         '../sql/sql.gyp:sql',
         '../sync/sync.gyp:sync',
@@ -408,6 +410,8 @@
         'browser/component_updater/component_updater_service.cc',
         'browser/component_updater/component_updater_service.h',
         'browser/component_updater/crx_update_item.h',
+        'browser/component_updater/default_component_installer.cc',
+        'browser/component_updater/default_component_installer.h',
         'browser/component_updater/flash_component_installer.h',
         'browser/component_updater/pepper_flash_component_installer.cc',
         'browser/component_updater/pnacl/pnacl_component_installer.cc',
@@ -476,6 +480,8 @@
         'browser/defaults.h',
         'browser/diagnostics/diagnostics_controller.cc',
         'browser/diagnostics/diagnostics_controller.h',
+        'browser/diagnostics/diagnostics_metrics.cc',
+        'browser/diagnostics/diagnostics_metrics.h',
         'browser/diagnostics/diagnostics_model.cc',
         'browser/diagnostics/diagnostics_model.h',
         'browser/diagnostics/diagnostics_test.cc',
@@ -486,6 +492,8 @@
         'browser/diagnostics/recon_diagnostics.h',
         'browser/diagnostics/sqlite_diagnostics.cc',
         'browser/diagnostics/sqlite_diagnostics.h',
+        'browser/download/all_download_item_notifier.cc',
+        'browser/download/all_download_item_notifier.h',
         'browser/download/chrome_download_manager_delegate.cc',
         'browser/download/chrome_download_manager_delegate.h',
         'browser/download/download_completion_blocker.cc',
@@ -538,10 +546,9 @@
         'browser/download/download_target_determiner_delegate.h',
         'browser/download/download_ui_controller.cc',
         'browser/download/download_ui_controller.h',
-        'browser/download/download_util.cc',
-        'browser/download/download_util.h',
-        'browser/download/all_download_item_notifier.cc',
-        'browser/download/all_download_item_notifier.h',
+        'browser/download/drag_download_item.h',
+        'browser/download/drag_download_item_gtk.cc',
+        'browser/download/drag_download_item_views.cc',
         'browser/download/save_package_file_picker.cc',
         'browser/download/save_package_file_picker.h',
         'browser/drive/drive_api_service.cc',
@@ -917,6 +924,8 @@
         'browser/lifetime/application_lifetime_win.cc',
         'browser/lifetime/browser_close_manager.cc',
         'browser/lifetime/browser_close_manager.h',
+        'browser/local_discovery/cloud_print_account_manager.cc',
+        'browser/local_discovery/cloud_print_account_manager.h',
         'browser/local_discovery/privet_confirm_api_flow.cc',
         'browser/local_discovery/privet_confirm_api_flow.h',
         'browser/local_discovery/privet_constants.h',
@@ -960,6 +969,8 @@
         'browser/managed_mode/managed_user_refresh_token_fetcher.h',
         'browser/managed_mode/managed_user_registration_utility.cc',
         'browser/managed_mode/managed_user_registration_utility.h',
+        'browser/managed_mode/managed_user_registration_utility_stub.cc',
+        'browser/managed_mode/managed_user_registration_utility_stub.h',
         'browser/managed_mode/managed_user_service.cc',
         'browser/managed_mode/managed_user_service.h',
         'browser/managed_mode/managed_user_service_factory.cc',
@@ -1113,8 +1124,6 @@
         'browser/metrics/variations/variations_request_scheduler.h',
         'browser/metrics/variations/variations_request_scheduler_mobile.cc',
         'browser/metrics/variations/variations_request_scheduler_mobile.h',
-        'browser/metrics/variations/variations_seed_processor.cc',
-        'browser/metrics/variations/variations_seed_processor.h',
         'browser/metrics/variations/variations_service.cc',
         'browser/metrics/variations/variations_service.h',
         'browser/metro_viewer/chrome_metro_viewer_process_host_aurawin.cc',
@@ -1204,6 +1213,8 @@
         'browser/net/transport_security_persister.h',
         'browser/net/url_info.cc',
         'browser/net/url_info.h',
+        'browser/network_time/navigation_time_helper.cc',
+        'browser/network_time/navigation_time_helper.h',
         'browser/notifications/balloon.cc',
         'browser/notifications/balloon.h',
         'browser/notifications/balloon_collection.cc',
@@ -1898,8 +1909,6 @@
         'browser/signin/about_signin_internals_factory.h',
         'browser/signin/chrome_signin_manager_delegate.cc',
         'browser/signin/chrome_signin_manager_delegate.h',
-        'browser/signin/oauth2_token_service.cc',
-        'browser/signin/oauth2_token_service.h',
         'browser/signin/profile_oauth2_token_service.cc',
         'browser/signin/profile_oauth2_token_service.h',
         'browser/signin/profile_oauth2_token_service_factory.cc',
@@ -2006,6 +2015,8 @@
         'browser/status_icons/desktop_notification_balloon.h',
         'browser/status_icons/status_icon.cc',
         'browser/status_icons/status_icon.h',
+        'browser/status_icons/status_icon_menu_model.cc',
+        'browser/status_icons/status_icon_menu_model.h',
         'browser/status_icons/status_icon_observer.h',
         'browser/status_icons/status_tray.cc',
         'browser/status_icons/status_tray.h',
@@ -2484,13 +2495,13 @@
             '../cc/cc.gyp:cc',
             '../components/components.gyp:autofill_content_browser',
             '../components/components.gyp:navigation_interception',
+            '../media/media.gyp:media',
             '../net/net.gyp:net_with_v8',
             '../printing/printing.gyp:printing',
             '../third_party/adobe/flash/flash_player.gyp:flapper_version_h',
             '../third_party/expat/expat.gyp:expat',
             '../third_party/hunspell/hunspell.gyp:hunspell',
             '../third_party/leveldatabase/leveldatabase.gyp:leveldatabase',
-            '../third_party/libphonenumber/libphonenumber.gyp:libphonenumber',
             '../third_party/libusb/libusb.gyp:libusb',
             '../third_party/libyuv/libyuv.gyp:libyuv',
             '../third_party/npapi/npapi.gyp:npapi',
@@ -3363,20 +3374,6 @@
       'includes': [ '../build/protoc.gypi' ]
     },
     {
-      # Protobuf compiler / generator for Chrome Variations seed.
-      'target_name': 'variations_seed_proto',
-      'type': 'static_library',
-      'sources': [
-        'browser/metrics/proto/trials_seed.proto',
-        'browser/metrics/proto/study.proto',
-      ],
-      'variables': {
-        'proto_in_dir': 'browser/metrics/proto',
-        'proto_out_dir': 'chrome/browser/metrics/proto',
-      },
-      'includes': [ '../build/protoc.gypi' ]
-    },
-    {
       # Protobuf compiler / generator for Sync FileSystem protocol buffer.
       'target_name': 'sync_file_system_proto',
       'type': 'static_library',
diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi
index 71f3030..18759a1 100644
--- a/chrome/chrome_browser_chromeos.gypi
+++ b/chrome/chrome_browser_chromeos.gypi
@@ -43,7 +43,6 @@
         'installer_util',
         'safe_browsing_proto',
         'safe_browsing_report_proto',
-        'variations_seed_proto',
         '../breakpad/breakpad.gyp:breakpad_client',
         '../build/linux/system.gyp:dbus',
         '../chromeos/chromeos.gyp:chromeos',
@@ -51,6 +50,10 @@
         # browser_chromeos #includes power_supply_properties.pb.h directly.
         '../chromeos/chromeos.gyp:power_manager_proto',
         '../chromeos/ime/input_method.gyp:gencode',
+        # The direct variations_seed_proto dependency is needed, rather than
+        # using common's dependency on variations, because the proto sources
+        # need to be generated before code in this target can start building.
+        '../components/components.gyp:variations_seed_proto',
         '../content/content.gyp:content_browser',
         '../content/content.gyp:content_common',
         '../crypto/crypto.gyp:crypto',
@@ -72,7 +75,6 @@
         '../third_party/leveldatabase/leveldatabase.gyp:leveldatabase',
         '../third_party/libevent/libevent.gyp:libevent',
         '../third_party/libjingle/libjingle.gyp:libjingle',
-        '../third_party/libphonenumber/libphonenumber.gyp:libphonenumber',
         '../third_party/libusb/libusb.gyp:libusb',
         '../third_party/libxml/libxml.gyp:libxml',
         '../third_party/npapi/npapi.gyp:npapi',
@@ -330,16 +332,22 @@
         'browser/chromeos/extensions/file_manager/event_router.h',
         'browser/chromeos/extensions/file_manager/file_browser_handlers.cc',
         'browser/chromeos/extensions/file_manager/file_browser_handlers.h',
-        'browser/chromeos/extensions/file_manager/file_manager_util.cc',
-        'browser/chromeos/extensions/file_manager/file_manager_util.h',
         'browser/chromeos/extensions/file_manager/file_tasks.cc',
         'browser/chromeos/extensions/file_manager/file_tasks.h',
         'browser/chromeos/extensions/file_manager/file_watcher.cc',
         'browser/chromeos/extensions/file_manager/file_watcher.h',
         'browser/chromeos/extensions/file_manager/fileapi_util.cc',
         'browser/chromeos/extensions/file_manager/fileapi_util.h',
+        'browser/chromeos/extensions/file_manager/mime_util.cc',
+        'browser/chromeos/extensions/file_manager/mime_util.h',
         'browser/chromeos/extensions/file_manager/mounted_disk_monitor.cc',
         'browser/chromeos/extensions/file_manager/mounted_disk_monitor.h',
+        'browser/chromeos/extensions/file_manager/open_util.cc',
+        'browser/chromeos/extensions/file_manager/open_util.h',
+        'browser/chromeos/extensions/file_manager/open_with_browser.cc',
+        'browser/chromeos/extensions/file_manager/open_with_browser.h',
+        'browser/chromeos/extensions/file_manager/select_file_dialog_util.cc',
+        'browser/chromeos/extensions/file_manager/select_file_dialog_util.h',
         'browser/chromeos/extensions/file_manager/url_util.cc',
         'browser/chromeos/extensions/file_manager/url_util.h',
         'browser/chromeos/extensions/file_manager/zip_file_creator.cc',
@@ -383,8 +391,6 @@
         'browser/chromeos/input_method/hidable_area.h',
         'browser/chromeos/input_method/ibus_controller.cc',
         'browser/chromeos/input_method/ibus_controller.h',
-        'browser/chromeos/input_method/ibus_controller_base.cc',
-        'browser/chromeos/input_method/ibus_controller_base.h',
         'browser/chromeos/input_method/ibus_controller_impl.cc',
         'browser/chromeos/input_method/ibus_controller_impl.h',
         'browser/chromeos/input_method/infolist_window_view.cc',
@@ -399,8 +405,6 @@
         'browser/chromeos/input_method/input_method_engine.h',
         'browser/chromeos/input_method/input_method_manager_impl.cc',
         'browser/chromeos/input_method/input_method_manager_impl.h',
-        'browser/chromeos/input_method/input_method_manager_impl_ll.cc',
-        'browser/chromeos/input_method/input_method_manager_impl_ll.h',
         'browser/chromeos/input_method/input_method_persistence.cc',
         'browser/chromeos/input_method/input_method_persistence.h',
         'browser/chromeos/input_method/input_method_util.cc',
@@ -667,8 +671,6 @@
         'browser/chromeos/policy/login_screen_power_management_policy.h',
         'browser/chromeos/policy/network_configuration_updater.cc',
         'browser/chromeos/policy/network_configuration_updater.h',
-        'browser/chromeos/policy/network_configuration_updater_impl.cc',
-        'browser/chromeos/policy/network_configuration_updater_impl.h',
         'browser/chromeos/policy/policy_cert_verifier.cc',
         'browser/chromeos/policy/policy_cert_verifier.h',
         'browser/chromeos/policy/policy_oauth2_token_fetcher.cc',
@@ -689,6 +691,8 @@
         'browser/chromeos/policy/user_cloud_policy_token_forwarder.h',
         'browser/chromeos/policy/user_cloud_policy_token_forwarder_factory.cc',
         'browser/chromeos/policy/user_cloud_policy_token_forwarder_factory.h',
+        'browser/chromeos/policy/user_network_configuration_updater.cc',
+        'browser/chromeos/policy/user_network_configuration_updater.h',
         'browser/chromeos/policy/user_policy_disk_cache.cc',
         'browser/chromeos/policy/user_policy_disk_cache.h',
         'browser/chromeos/policy/user_policy_token_loader.cc',
@@ -890,10 +894,10 @@
             ['exclude', 'browser/chromeos/extensions/file_manager/file_browser_private_api.h'],
             ['exclude', 'browser/chromeos/extensions/file_manager/file_browser_private_api_factory.cc'],
             ['exclude', 'browser/chromeos/extensions/file_manager/file_browser_private_api_factory.h'],
-            ['exclude', 'browser/chromeos/extensions/file_manager/file_manager_util.cc'],
-            ['exclude', 'browser/chromeos/extensions/file_manager/file_manager_util.h'],
             ['exclude', 'browser/chromeos/extensions/file_manager/file_tasks.cc'],
             ['exclude', 'browser/chromeos/extensions/file_manager/file_tasks.h'],
+            ['exclude', 'browser/chromeos/extensions/file_manager/open_util.cc'],
+            ['exclude', 'browser/chromeos/extensions/file_manager/open_util.h'],
             ['exclude', 'browser/chromeos/extensions/file_manager/zip_file_creator.cc'],
             ['exclude', 'browser/chromeos/extensions/file_manager/zip_file_creator.h'],
             ['exclude', 'browser/chromeos/extensions/media_player_api.cc'],
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index c9a4060..99a6581 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -419,6 +419,8 @@
         'browser/extensions/api/signedin_devices/id_mapping_helper.h',
         'browser/extensions/api/signedin_devices/signedin_devices_api.cc',
         'browser/extensions/api/signedin_devices/signedin_devices_api.h',
+        'browser/extensions/api/signedin_devices/signed_in_devices_manager.cc',
+        'browser/extensions/api/signedin_devices/signed_in_devices_manager.h',
         'browser/extensions/api/socket/socket.cc',
         'browser/extensions/api/socket/socket.h',
         'browser/extensions/api/socket/socket_api.cc',
@@ -492,10 +494,16 @@
         'browser/extensions/api/system_indicator/system_indicator_manager.h',
         'browser/extensions/api/system_indicator/system_indicator_manager_factory.cc',
         'browser/extensions/api/system_indicator/system_indicator_manager_factory.h',
+        'browser/extensions/api/system_info/system_info_api.cc',
+        'browser/extensions/api/system_info/system_info_api.h',
+        'browser/extensions/api/system_info/system_info_provider.cc',
+        'browser/extensions/api/system_info/system_info_provider.h',
         'browser/extensions/api/system_memory/memory_info_provider.cc',
         'browser/extensions/api/system_memory/memory_info_provider.h',
         'browser/extensions/api/system_memory/system_memory_api.cc',
         'browser/extensions/api/system_memory/system_memory_api.h',
+        'browser/extensions/api/system_private/system_private_api.cc',
+        'browser/extensions/api/system_private/system_private_api.h',
         'browser/extensions/api/system_storage/storage_info_provider.cc',
         'browser/extensions/api/system_storage/storage_info_provider.h',
         'browser/extensions/api/system_storage/system_storage_api.cc',
@@ -763,12 +771,6 @@
         'browser/extensions/state_store.h',
         'browser/extensions/suggest_permission_util.h',
         'browser/extensions/suggest_permission_util.cc',
-        'browser/extensions/api/system_info/system_info_api.cc',
-        'browser/extensions/api/system_info/system_info_api.h',
-        'browser/extensions/api/system_info/system_info_provider.cc',
-        'browser/extensions/api/system_info/system_info_provider.h',
-        'browser/extensions/api/system_private/system_private_api.cc',
-        'browser/extensions/api/system_private/system_private_api.h',
         'browser/extensions/tab_helper.cc',
         'browser/extensions/tab_helper.h',
         'browser/extensions/theme_installed_infobar_delegate.cc',
@@ -859,7 +861,6 @@
             ['exclude', '^browser/extensions/browser_event_router.cc'],
             ['include', '^browser/extensions/api/activity_log_private/activity_log_private_api.cc'],
             ['include', '^browser/extensions/api/activity_log_private/activity_log_private_api.h'],
-            ['include', '^browser/extensions/api/alarms/alarm_manager.cc'],
             ['include', '^browser/extensions/api/content_settings/content_settings_api_constants.cc'],
             ['include', '^browser/extensions/api/content_settings/content_settings_helpers.cc'],
             ['include', '^browser/extensions/api/content_settings/content_settings_store.cc'],
@@ -876,24 +877,15 @@
             ['include', '^browser/extensions/api/declarative_webrequest/webrequest_rules_registry.cc'],
             ['include', '^browser/extensions/api/extension_action/extension_action_api.cc'],
             ['include', '^browser/extensions/api/extension_action/extension_page_actions_api_constants.cc'],
-            ['include', '^browser/extensions/api/file_handlers/app_file_handler_util.cc'],
-            ['include', '^browser/extensions/api/location/location_manager.cc'],
             ['include', '^browser/extensions/api/messaging/extension_message_port.cc'],
             ['include', '^browser/extensions/api/messaging/message_service.cc'],
             ['include', '^browser/extensions/api/module/module.cc'],
             ['include', '^browser/extensions/api/omnibox/omnibox_api.cc'],
-            ['include', '^browser/extensions/api/preference/chrome_direct_setting.cc'],
-            ['include', '^browser/extensions/api/preference/chrome_direct_setting_api.cc'],
             ['include', '^browser/extensions/api/preference/preference_api.cc'],
-            ['include', '^browser/extensions/api/processes/processes_api.cc'],
-            ['include', '^browser/extensions/api/processes/processes_api_constants.cc'],
             ['include', '^browser/extensions/api/proxy/proxy_api.cc'],
             ['include', '^browser/extensions/api/proxy/proxy_api_constants.cc'],
-            ['include', '^browser/extensions/api/push_messaging/push_messaging_api.cc'],
             ['include', '^browser/extensions/api/runtime/runtime_api.cc'],
             ['include', '^browser/extensions/api/storage/.*\.cc'],
-            ['include', '^browser/extensions/api/system_cpu/cpu_info_provider.cc'],
-            ['include', '^browser/extensions/api/system_storage/storage_info_provider.cc'],
             ['include', '^browser/extensions/api/tabs/tabs_constants.cc'],
             ['include', '^browser/extensions/api/web_navigation/frame_navigation_state.cc'],
             ['include', '^browser/extensions/api/web_navigation/web_navigation_api.cc'],
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index 4ce2d2b..2e78d95 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -29,12 +29,10 @@
         'safe_browsing_proto',
         'safe_browsing_report_proto',
         'feedback_proto',
-        'variations_seed_proto',
         '../components/components.gyp:auto_login_parser',
         '../content/content.gyp:content_browser',
         '../content/content.gyp:content_common',
         '../crypto/crypto.gyp:crypto',
-        '../media/media.gyp:media',
         '../skia/skia.gyp:skia',
         '../sync/sync.gyp:sync',
         '../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation',
@@ -136,6 +134,12 @@
         'browser/ui/app_list/search/app_search_provider.cc',
         'browser/ui/app_list/search/app_search_provider.h',
         'browser/ui/app_list/search/chrome_search_result.h',
+        'browser/ui/app_list/search/common/json_response_fetcher.cc',
+        'browser/ui/app_list/search/common/json_response_fetcher.h',
+        'browser/ui/app_list/search/common/url_icon_source.cc',
+        'browser/ui/app_list/search/common/url_icon_source.h',
+        'browser/ui/app_list/search/common/webservice_search_provider.cc',
+        'browser/ui/app_list/search/common/webservice_search_provider.h',
         'browser/ui/app_list/search/history.cc',
         'browser/ui/app_list/search/history.h',
         'browser/ui/app_list/search/history_data.cc',
@@ -171,10 +175,6 @@
         'browser/ui/app_list/search/webstore_provider.h',
         'browser/ui/app_list/search/webstore_result.cc',
         'browser/ui/app_list/search/webstore_result.h',
-        'browser/ui/app_list/search/webstore_result_icon_source.cc',
-        'browser/ui/app_list/search/webstore_result_icon_source.h',
-        'browser/ui/app_list/search/webstore_search_fetcher.cc',
-        'browser/ui/app_list/search/webstore_search_fetcher.h',
         'browser/ui/app_modal_dialogs/app_modal_dialog.cc',
         'browser/ui/app_modal_dialogs/app_modal_dialog.h',
         'browser/ui/app_modal_dialogs/app_modal_dialog_queue.cc',
@@ -224,10 +224,10 @@
         'browser/ui/ash/ime_controller_chromeos.h',
         'browser/ui/ash/launcher/app_shortcut_launcher_item_controller.cc',
         'browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h',
-        'browser/ui/ash/launcher/browser_launcher_item_controller.cc',
-        'browser/ui/ash/launcher/browser_launcher_item_controller.h',
         'browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc',
         'browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h',
+        'browser/ui/ash/launcher/browser_status_monitor.cc',
+        'browser/ui/ash/launcher/browser_status_monitor.h',
         'browser/ui/ash/launcher/chrome_launcher_app_menu_item.cc',
         'browser/ui/ash/launcher/chrome_launcher_app_menu_item.h',
         'browser/ui/ash/launcher/chrome_launcher_app_menu_item_browser.cc',
@@ -437,6 +437,8 @@
         'browser/ui/cocoa/applescript/tab_applescript.mm',
         'browser/ui/cocoa/applescript/window_applescript.h',
         'browser/ui/cocoa/applescript/window_applescript.mm',
+        'browser/ui/cocoa/apps/app_shim_menu_controller_mac.h',
+        'browser/ui/cocoa/apps/app_shim_menu_controller_mac.mm',
         'browser/ui/cocoa/apps/chrome_shell_window_delegate_cocoa.mm',
         'browser/ui/cocoa/apps/native_app_window_cocoa.h',
         'browser/ui/cocoa/apps/native_app_window_cocoa.mm',
@@ -616,6 +618,7 @@
         'browser/ui/cocoa/download/download_item_cell.mm',
         'browser/ui/cocoa/download/download_item_controller.h',
         'browser/ui/cocoa/download/download_item_controller.mm',
+        'browser/ui/cocoa/download/download_item_drag_mac.mm',
         'browser/ui/cocoa/download/download_item_mac.h',
         'browser/ui/cocoa/download/download_item_mac.mm',
         'browser/ui/cocoa/download/download_shelf_context_menu_controller.h',
@@ -650,8 +653,8 @@
         'browser/ui/cocoa/extensions/browser_actions_controller.mm',
         'browser/ui/cocoa/extensions/browser_actions_controller_prefs.h',
         'browser/ui/cocoa/extensions/browser_actions_controller_prefs.cc',
-        'browser/ui/cocoa/extensions/extension_action_context_menu.h',
-        'browser/ui/cocoa/extensions/extension_action_context_menu.mm',
+        'browser/ui/cocoa/extensions/extension_action_context_menu_controller.h',
+        'browser/ui/cocoa/extensions/extension_action_context_menu_controller.mm',
         'browser/ui/cocoa/extensions/extension_install_dialog_controller.h',
         'browser/ui/cocoa/extensions/extension_install_dialog_controller.mm',
         'browser/ui/cocoa/extensions/extension_install_view_controller.h',
@@ -731,6 +734,10 @@
         'browser/ui/cocoa/infobars/confirm_infobar_controller.mm',
         'browser/ui/cocoa/infobars/extension_infobar_controller.h',
         'browser/ui/cocoa/infobars/extension_infobar_controller.mm',
+        'browser/ui/cocoa/infobars/infobar_cocoa.h',
+        'browser/ui/cocoa/infobars/infobar_cocoa.mm',
+        'browser/ui/cocoa/infobars/infobar_container_cocoa.h',
+        'browser/ui/cocoa/infobars/infobar_container_cocoa.mm',
         'browser/ui/cocoa/infobars/infobar_container_controller.h',
         'browser/ui/cocoa/infobars/infobar_container_controller.mm',
         'browser/ui/cocoa/infobars/infobar_controller.h',
@@ -739,9 +746,6 @@
         'browser/ui/cocoa/infobars/infobar_gradient_view.mm',
         'browser/ui/cocoa/infobars/infobar_utilities.h',
         'browser/ui/cocoa/infobars/infobar_utilities.mm',
-        'browser/ui/cocoa/infobars/infobar.h',
-        'browser/ui/cocoa/infobars/mock_confirm_infobar_delegate.cc',
-        'browser/ui/cocoa/infobars/mock_confirm_infobar_delegate.h',
         'browser/ui/cocoa/infobars/translate_infobar_base.h',
         'browser/ui/cocoa/infobars/translate_infobar_base.mm',
         'browser/ui/cocoa/infobars/translate_message_infobar_controller.h',
@@ -1576,6 +1580,7 @@
         'browser/ui/views/bookmarks/bookmark_drag_drop_views.h',
         'browser/ui/views/bookmarks/bookmark_editor_view.cc',
         'browser/ui/views/bookmarks/bookmark_editor_view.h',
+        'browser/ui/views/bookmarks/bookmark_menu_controller_observer.h',
         'browser/ui/views/bookmarks/bookmark_menu_controller_views.cc',
         'browser/ui/views/bookmarks/bookmark_menu_controller_views.h',
         'browser/ui/views/bookmarks/bookmark_menu_delegate.cc',
@@ -2414,6 +2419,7 @@
             '../components/components.gyp:autofill_content_risk_proto',
             '../components/component_strings.gyp:component_strings',
             '../device/bluetooth/bluetooth.gyp:device_bluetooth',
+            '../media/media.gyp:media',
             '../net/net.gyp:net_with_v8',
             '../printing/printing.gyp:printing',
             '../third_party/adobe/flash/flash_player.gyp:flapper_version_h',
@@ -2421,7 +2427,6 @@
             '../third_party/hunspell/hunspell.gyp:hunspell',
             '../third_party/leveldatabase/leveldatabase.gyp:leveldatabase',
             '../third_party/libjingle/libjingle.gyp:libjingle',
-            '../third_party/libphonenumber/libphonenumber.gyp:libphonenumber',
             '../third_party/npapi/npapi.gyp:npapi',
             '../third_party/re2/re2.gyp:re2',
             '../ui/compositor/compositor.gyp:compositor',
@@ -2816,8 +2821,6 @@
             # There's no Browser/BrowserList on Android.
             'browser/ui/active_tab_tracker.cc',
             'browser/ui/active_tab_tracker.h',
-            'browser/ui/blocked_content/popup_blocker_tab_helper.cc',
-            'browser/ui/blocked_content/popup_blocker_tab_helper.h',
             'browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.cc',
             'browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.h',
             'browser/ui/bookmarks/bookmark_context_menu_controller.cc',
@@ -2827,7 +2830,6 @@
             'browser/ui/browser_finder.cc',
             'browser/ui/browser_iterator.cc',
             'browser/ui/browser_list.cc',
-            'browser/ui/browser_navigator.cc',
             'browser/ui/browser_otr_state.cc',
             'browser/ui/browser_tab_contents.cc',
             'browser/ui/browser_tab_restore_service_delegate.cc',
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi
index 09c2912..157d2e6 100644
--- a/chrome/chrome_common.gypi
+++ b/chrome/chrome_common.gypi
@@ -39,6 +39,7 @@
         '<(DEPTH)/chrome/common_constants.gyp:common_constants',
         '<(DEPTH)/components/components.gyp:json_schema',
         '<(DEPTH)/components/components.gyp:policy_component',
+        '<(DEPTH)/components/components.gyp:variations',
         '<(DEPTH)/components/components.gyp:visitedlink_common',
         '<(DEPTH)/content/content.gyp:content_common',
         '<(DEPTH)/net/net.gyp:net',
@@ -46,7 +47,6 @@
         '<(DEPTH)/third_party/icu/icu.gyp:icui18n',
         '<(DEPTH)/third_party/icu/icu.gyp:icuuc',
         '<(DEPTH)/third_party/libxml/libxml.gyp:libxml',
-        '<(DEPTH)/third_party/mt19937ar/mt19937ar.gyp:mt19937ar',
         '<(DEPTH)/third_party/sqlite/sqlite.gyp:sqlite',
         '<(DEPTH)/third_party/zlib/google/zip.gyp:zip',
         '<(DEPTH)/ui/ui.gyp:ui_resources',
@@ -396,20 +396,14 @@
         'common/media/webrtc_logging_messages.h',
         'common/metrics/caching_permuted_entropy_provider.cc',
         'common/metrics/caching_permuted_entropy_provider.h',
-        'common/metrics/entropy_provider.cc',
-        'common/metrics/entropy_provider.h',
         'common/metrics/metrics_log_base.cc',
         'common/metrics/metrics_log_base.h',
         'common/metrics/metrics_log_manager.cc',
         'common/metrics/metrics_log_manager.h',
         'common/metrics/metrics_service_base.cc',
         'common/metrics/metrics_service_base.h',
-        'common/metrics/metrics_util.cc',
-        'common/metrics/metrics_util.h',
         'common/metrics/variations/uniformity_field_trials.cc',
         'common/metrics/variations/uniformity_field_trials.h',
-        'common/metrics/variations/variations_associated_data.cc',
-        'common/metrics/variations/variations_associated_data.h',
         'common/metrics/variations/variations_util.cc',
         'common/metrics/variations/variations_util.h',
         'common/multi_process_lock.h',
@@ -500,10 +494,6 @@
             ['include', 'common/extensions/api/extension_api_stub.cc'],
             ['include', 'common/extensions/api/extension_action/action_info.cc'],
             ['include', 'common/extensions/api/extension_action/action_info.h'],
-            ['include', 'common/extensions/api/extension_action/browser_action_handler.cc'],
-            ['include', 'common/extensions/api/extension_action/browser_action_handler.h'],
-            ['include', 'common/extensions/api/extension_action/page_action_handler.cc'],
-            ['include', 'common/extensions/api/extension_action/page_action_handler.h'],
             ['include', 'common/extensions/api/i18n/default_locale_handler.cc'],
             ['include', 'common/extensions/api/i18n/default_locale_handler.h'],
             ['include', 'common/extensions/api/identity/oauth2_manifest_handler.cc'],
@@ -512,8 +502,6 @@
             ['include', 'common/extensions/api/managed_mode_private/managed_mode_handler.h'],
             ['include', 'common/extensions/api/plugins/plugins_handler.cc'],
             ['include', 'common/extensions/api/plugins/plugins_handler.h'],
-            ['include', 'common/extensions/api/spellcheck/spellcheck_handler.cc'],
-            ['include', 'common/extensions/api/spellcheck/spellcheck_handler.h'],
             ['include', 'common/extensions/api/storage/storage_schema_manifest_handler.cc'],
             ['include', 'common/extensions/api/storage/storage_schema_manifest_handler.h'],
           ],
diff --git a/chrome/chrome_nibs.gyp b/chrome/chrome_nibs.gyp
index 2e12fc6..cdc4c52 100644
--- a/chrome/chrome_nibs.gyp
+++ b/chrome/chrome_nibs.gyp
@@ -167,18 +167,6 @@
         'browser/ui/cocoa/info_bubble_view.mm',
         'browser/ui/cocoa/info_bubble_window.h',
         'browser/ui/cocoa/info_bubble_window.mm',
-        'browser/ui/cocoa/infobars/after_translate_infobar_controller.h',
-        'browser/ui/cocoa/infobars/after_translate_infobar_controller.mm',
-        'browser/ui/cocoa/infobars/alternate_nav_infobar_controller.h',
-        'browser/ui/cocoa/infobars/alternate_nav_infobar_controller.mm',
-        'browser/ui/cocoa/infobars/before_translate_infobar_controller.h',
-        'browser/ui/cocoa/infobars/before_translate_infobar_controller.mm',
-        'browser/ui/cocoa/infobars/confirm_infobar_controller.h',
-        'browser/ui/cocoa/infobars/confirm_infobar_controller.mm',
-        'browser/ui/cocoa/infobars/extension_infobar_controller.h',
-        'browser/ui/cocoa/infobars/extension_infobar_controller.mm',
-        'browser/ui/cocoa/infobars/infobar_container_controller.h',
-        'browser/ui/cocoa/infobars/infobar_container_controller.mm',
         'browser/ui/cocoa/infobars/infobar_controller.h',
         'browser/ui/cocoa/infobars/infobar_controller.mm',
         'browser/ui/cocoa/infobars/infobar_gradient_view.h',
diff --git a/chrome/chrome_nibs.gypi b/chrome/chrome_nibs.gypi
index 7c7aa33..aa4b251 100644
--- a/chrome/chrome_nibs.gypi
+++ b/chrome/chrome_nibs.gypi
@@ -60,7 +60,6 @@
       'app/nibs/GlobalErrorBubble.xib',
       'app/nibs/HungRendererDialog.xib',
       'app/nibs/InfoBar.xib',
-      'app/nibs/InfoBarContainer.xib',
       'app/nibs/Notification.xib',
       'app/nibs/Panel.xib',
       'app/nibs/ScreenCaptureNotification.xib',
diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi
index fde9706..2f5d94d 100644
--- a/chrome/chrome_renderer.gypi
+++ b/chrome/chrome_renderer.gypi
@@ -77,6 +77,8 @@
         'renderer/extensions/content_watcher.h',
         'renderer/extensions/context_menus_custom_bindings.cc',
         'renderer/extensions/context_menus_custom_bindings.h',
+        'renderer/extensions/css_native_handler.cc',
+        'renderer/extensions/css_native_handler.h',
         'renderer/extensions/dispatcher.cc',
         'renderer/extensions/dispatcher.h',
         'renderer/extensions/document_custom_bindings.cc',
diff --git a/chrome/chrome_syzygy.gypi b/chrome/chrome_syzygy.gypi
index 6dbbd34..a3201cc 100644
--- a/chrome/chrome_syzygy.gypi
+++ b/chrome/chrome_syzygy.gypi
@@ -58,7 +58,7 @@
             '<(dest_dir)/<(dll_name).dll.pdb',
             '<(dest_dir)/asan_rtl.dll',
             '<(dest_dir)/asan_rtl.dll.pdb',
-            '<(dest_dir)/win-syzyasan-filter.txt.json',
+            '<(dest_dir)/win-syzyasan-filter-<(dll_name).txt.json',
           ],
           'action': [
             'python',
@@ -68,6 +68,8 @@
             '--input_symbol', '<(PRODUCT_DIR)/<(dll_name).dll.pdb',
             '--filter',
             '<(DEPTH)/chrome/tools/build/win/win-syzyasan-filter.txt',
+            '--output-filter-file',
+            '<(dest_dir)/win-syzyasan-filter-<(dll_name).txt.json',
             '--destination_dir', '<(dest_dir)',
           ],
         },
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 74c95df..a090420 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -231,6 +231,7 @@
         'browser/notifications/notification_browsertest.cc',
         'browser/printing/print_dialog_cloud_interative_uitest.cc',
         'browser/task_manager/task_manager_browsertest_util.cc',
+        'browser/ui/autofill/autofill_popup_controller_interactive_uitest.cc',
         'browser/ui/browser_focus_uitest.cc',
         'browser/ui/cocoa/panels/panel_cocoa_browsertest.mm',
         'browser/ui/fullscreen/fullscreen_controller_interactive_browsertest.cc',
@@ -403,6 +404,9 @@
             'browser/chromeos/login/captive_portal_window_browsertest.cc',
             'browser/chromeos/login/eula_browsertest.cc',
             'browser/chromeos/login/login_browsertest.cc',
+            'browser/chromeos/login/login_manager_test.cc',
+            'browser/chromeos/login/login_manager_test.h',
+            'browser/chromeos/login/login_ui_browsertest.cc',
             'browser/chromeos/login/oobe_browsertest.cc',
             'browser/chromeos/login/screen_locker_browsertest.cc',
             'browser/chromeos/login/screen_locker_tester.cc',
@@ -799,6 +803,8 @@
         'test/chromedriver/chrome/chrome_android_impl.h',
         'test/chromedriver/chrome/chrome_desktop_impl.cc',
         'test/chromedriver/chrome/chrome_desktop_impl.h',
+        'test/chromedriver/chrome/chrome_existing_impl.cc',
+        'test/chromedriver/chrome/chrome_existing_impl.h',
         'test/chromedriver/chrome/chrome_finder.cc',
         'test/chromedriver/chrome/chrome_finder.h',
         'test/chromedriver/chrome/chrome_finder_mac.mm',
@@ -945,6 +951,7 @@
         'chrome_devtools_lib',
         '../base/base.gyp:base',
         '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+        '../crypto/crypto.gyp:crypto',
         '../net/net.gyp:net',
         '../ui/ui.gyp:ui',
       ],
@@ -1162,6 +1169,7 @@
         '../components/autofill/content/renderer/test_password_autofill_agent.cc',
         '../remoting/test/auth_browsertest.cc',
         '../remoting/test/launch_browsertest.cc',
+        '../remoting/test/me2me_browsertest.cc',
         '../remoting/test/remote_desktop_browsertest.cc',
         '../remoting/test/remote_desktop_browsertest.h',
         'app/breakpad_mac_stubs.mm',
@@ -1227,6 +1235,7 @@
         'browser/chromeos/login/login_utils_browsertest.cc',
         'browser/chromeos/login/login_manager_test.cc',
         'browser/chromeos/login/login_manager_test.h',
+        'browser/chromeos/login/managed/supervised_user_creation_browsertest.cc',
         'browser/chromeos/login/mock_authenticator.cc',
         'browser/chromeos/login/mock_authenticator.h',
         'browser/chromeos/login/session_login_browsertest.cc',
@@ -1562,7 +1571,6 @@
         'browser/ui/ash/shelf_browsertest.cc',
         'browser/ui/ash/volume_controller_browsertest_chromeos.cc',
         'browser/ui/autofill/autofill_dialog_controller_browsertest.cc',
-        'browser/ui/autofill/autofill_popup_controller_browsertest.cc',
         'browser/ui/blocked_content/popup_blocker_browsertest.cc',
         'browser/ui/bookmarks/bookmark_browsertest.cc',
         'browser/ui/browser_browsertest.cc',
@@ -1573,7 +1581,9 @@
         'browser/ui/browser_navigator_browsertest_chromeos.cc',
         'browser/ui/cocoa/applescript/browsercrapplication+applescript_test.mm',
         'browser/ui/cocoa/applescript/window_applescript_test.mm',
-        'browser/ui/cocoa//autofill/autofill_dialog_cocoa_browsertest.mm',
+        'browser/ui/cocoa/apps/app_shim_menu_controller_mac_browsertest.mm',
+        'browser/ui/cocoa/apps/native_app_window_cocoa_browsertest.mm',
+        'browser/ui/cocoa/autofill/autofill_dialog_cocoa_browsertest.mm',
         'browser/ui/cocoa/browser_window_cocoa_browsertest.mm',
         'browser/ui/cocoa/browser_window_controller_browsertest.mm',
         'browser/ui/cocoa/certificate_viewer_mac_browsertest.mm',
@@ -1581,7 +1591,7 @@
         'browser/ui/cocoa/content_settings/collected_cookies_mac_browsertest.mm',
         'browser/ui/cocoa/content_settings/content_setting_bubble_cocoa_unittest.mm',
         'browser/ui/cocoa/dev_tools_controller_browsertest.mm',
-        'browser/ui/cocoa/extensions/extension_action_context_menu_browsertest.mm',
+        'browser/ui/cocoa/extensions/extension_action_context_menu_controller_browsertest.mm',
         'browser/ui/cocoa/extensions/extension_install_dialog_controller_browsertest.mm',
         'browser/ui/cocoa/extensions/extension_install_prompt_test_utils.h',
         'browser/ui/cocoa/extensions/extension_install_prompt_test_utils.mm',
@@ -1612,7 +1622,6 @@
         'browser/ui/tab_modal_confirm_dialog_browsertest.h',
         'browser/ui/toolbar/test_toolbar_model.cc',
         'browser/ui/toolbar/test_toolbar_model.h',
-        'browser/ui/views/app_list/app_list_controller_win_browsertest.cc',
         'browser/ui/views/avatar_menu_button_browsertest.cc',
         'browser/ui/views/browser_actions_container_browsertest.cc',
         'browser/ui/views/frame/app_non_client_frame_view_ash_browsertest.cc',
@@ -1780,6 +1789,11 @@
             'browser/ui/sync/one_click_signin_bubble_links_delegate_browsertest.cc',
           ]
         }],
+        ['enable_autofill_dialog==0', {
+          'sources!': [
+            'browser/ui/autofill/autofill_dialog_controller_browsertest.cc',
+          ]
+        }],
         ['disable_nacl==0', {
           'sources':[
             'browser/extensions/extension_nacl_browsertest.cc',
@@ -3162,7 +3176,6 @@
             # Disabling for now (enabled on linux/windows below).
             # 'browser_tests',
             '../ipc/ipc.gyp:ipc_tests',
-            '../media/media.gyp:media_unittests',
             '../net/net.gyp:net_unittests',
             '../printing/printing.gyp:printing_unittests',
             '../remoting/remoting.gyp:remoting_unittests',
@@ -3172,6 +3185,10 @@
             '../sync/sync.gyp:sync_unit_tests',
           ],  # 'dependencies'
           'conditions': [
+            ['OS!="ios"', {
+              'dependencies': [
+                '../media/media.gyp:media_unittests',
+              ]}],
             ['OS=="win"', {
               'dependencies': [
                 # Courgette has not been ported from Windows.
@@ -3294,6 +3311,7 @@
           'dependencies': [
             'chrome_java',
             'chromium_testshell_java',
+            'chrome_java_test_support',
             '../base/base.gyp:base',
             '../base/base.gyp:base_java_test_support',
             '../content/content.gyp:content_java_test_support',
@@ -3328,6 +3346,19 @@
           ],
           'includes': [ '../build/uiautomator_test.gypi' ],
         },
+        {
+          'target_name': 'chrome_java_test_support',
+          'type': 'none',
+          'variables': {
+            'package_name': 'chrome_java_test_support',
+            'java_in_dir': '../chrome/test/android/javatests',
+          },
+          'dependencies': [
+            'chrome_java',
+            '../content/content.gyp:content_java_test_support',
+          ],
+          'includes': [ '../build/java.gypi' ],
+        },
       ],
     }],
     ['test_isolation_mode != "noop"', {
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 72d7e96..957dd28 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -21,7 +21,6 @@
         '../components/components.gyp:sessions_test_support',
         '../content/content.gyp:content_app_both',
         '../content/content.gyp:test_support_content',
-        '../media/media.gyp:media_test_support',
         '../net/net.gyp:net',
         '../net/net.gyp:net_test_support',
         '../skia/skia.gyp:skia',
@@ -305,6 +304,7 @@
             '../content/content.gyp:content_worker',
             '../components/components.gyp:autofill_core_test_support',
             '../ipc/ipc.gyp:test_support_ipc',
+            '../media/media.gyp:media_test_support',
             '../ppapi/ppapi_internal.gyp:ppapi_shared',
             '../third_party/leveldatabase/leveldatabase.gyp:leveldatabase',
             '../webkit/support/webkit_support.gyp:glue_child',
@@ -456,7 +456,6 @@
         # 2) test-specific support libraries:
         '../base/base.gyp:test_support_base',
         '../content/content.gyp:content_app_both',
-        '../media/media.gyp:media_test_support',
         '../net/net.gyp:net',
         '../net/net.gyp:net_test_support',
         '../sync/sync.gyp:test_support_sync_api',
@@ -659,9 +658,9 @@
         'browser/chromeos/drive/write_on_cache_file_unittest.cc',
         'browser/chromeos/extensions/default_app_order_unittest.cc',
         'browser/chromeos/extensions/file_manager/desktop_notifications_unittest.cc',
-        'browser/chromeos/extensions/file_manager/file_manager_util_unittest.cc',
         'browser/chromeos/extensions/file_manager/file_tasks_unittest.cc',
         'browser/chromeos/extensions/file_manager/file_watcher_unittest.cc',
+        'browser/chromeos/extensions/file_manager/mime_util_unittest.cc',
         'browser/chromeos/extensions/file_manager/url_util_unittest.cc',
         'browser/chromeos/extensions/wallpaper_private_api_unittest.cc',
         'browser/chromeos/external_metrics_unittest.cc',
@@ -672,7 +671,6 @@
         'browser/chromeos/input_method/browser_state_monitor_unittest.cc',
         'browser/chromeos/input_method/candidate_window_controller_impl_unittest.cc',
         'browser/chromeos/input_method/candidate_window_view_unittest.cc',
-        'browser/chromeos/input_method/ibus_controller_base_unittest.cc',
         'browser/chromeos/input_method/ibus_controller_impl_unittest.cc',
         'browser/chromeos/input_method/ibus_controller_unittest.cc',
         'browser/chromeos/input_method/input_method_configuration_unittest.cc',
@@ -707,7 +705,7 @@
         'browser/chromeos/policy/device_local_account_policy_service_unittest.cc',
         'browser/chromeos/policy/enterprise_install_attributes_unittest.cc',
         'browser/chromeos/policy/login_screen_power_management_policy_unittest.cc',
-        'browser/chromeos/policy/network_configuration_updater_impl_unittest.cc',
+        'browser/chromeos/policy/network_configuration_updater_unittest.cc',
         'browser/chromeos/policy/proxy_policy_provider_unittest.cc',
         'browser/chromeos/policy/recommendation_restorer_unittest.cc',
         'browser/chromeos/policy/user_cloud_policy_manager_chromeos_unittest.cc',
@@ -826,6 +824,7 @@
         'browser/extensions/api/serial/serial_port_enumerator_unittest.cc',
         'browser/extensions/api/signedin_devices/id_mapping_helper_unittest.cc',
         'browser/extensions/api/signedin_devices/signedin_devices_api_unittest.cc',
+        'browser/extensions/api/signedin_devices/signed_in_devices_manager_unittest.cc',
         'browser/extensions/api/socket/socket_api_unittest.cc',
         'browser/extensions/api/socket/tcp_socket_unittest.cc',
         'browser/extensions/api/socket/udp_socket_unittest.cc',
@@ -962,6 +961,7 @@
         'browser/local_discovery/privet_confirm_api_flow_unittest.cc',
         'browser/local_discovery/privet_http_unittest.cc',
         'browser/local_discovery/privet_url_fetcher_unittest.cc',
+        'browser/local_discovery/cloud_print_account_manager_unittest.cc',
         'browser/mac/keystone_glue_unittest.mm',
         'browser/managed_mode/managed_mode_url_filter_unittest.cc',
         'browser/managed_mode/managed_user_service_unittest.cc',
@@ -988,7 +988,6 @@
         'browser/metrics/thread_watcher_unittest.cc',
         'browser/metrics/time_ticks_experiment_unittest.cc',
         'browser/metrics/variations/variations_http_header_provider_unittest.cc',
-        'browser/metrics/variations/variations_seed_processor_unittest.cc',
         'browser/metrics/variations/variations_service_unittest.cc',
         'browser/metrics/variations/variations_request_scheduler_unittest.cc',
         'browser/nacl_host/nacl_file_host_unittest.cc',
@@ -1018,6 +1017,7 @@
         'browser/net/ssl_config_service_manager_pref_unittest.cc',
         'browser/net/transport_security_persister_unittest.cc',
         'browser/net/url_info_unittest.cc',
+        'browser/network_time/navigation_time_helper_unittest.cc',
         'browser/notifications/desktop_notification_service_unittest.cc',
         'browser/notifications/message_center_notifications_unittest_win.cc',
         'browser/notifications/message_center_settings_controller_unittest.cc',
@@ -1183,9 +1183,6 @@
         'browser/shell_integration_win_unittest.cc',
         'browser/signin/fake_auth_status_provider.cc',
         'browser/signin/fake_auth_status_provider.h',
-        'browser/signin/oauth2_token_service_test_util.cc',
-        'browser/signin/oauth2_token_service_test_util.h',
-        'browser/signin/oauth2_token_service_unittest.cc',
         'browser/signin/profile_oauth2_token_service_request_unittest.cc',
         'browser/signin/profile_oauth2_token_service_unittest.cc',
         'browser/signin/signin_global_error_unittest.cc',
@@ -1210,6 +1207,7 @@
         'browser/spellchecker/spellcheck_service_unittest.cc',
         'browser/spellchecker/spelling_service_client_unittest.cc',
         'browser/spellchecker/word_trimmer_unittest.cc',
+        'browser/status_icons/status_icon_menu_model_unittest.cc',
         'browser/status_icons/status_icon_unittest.cc',
         'browser/status_icons/status_tray_unittest.cc',
         'browser/storage_monitor/image_capture_device_manager_unittest.mm',
@@ -1343,6 +1341,7 @@
         'browser/tab_contents/render_view_context_menu_test_util.h',
         'browser/tab_contents/render_view_context_menu_unittest.cc',
         'browser/task_manager/task_manager_unittest.cc',
+        'browser/task_manager/task_manager_util_unittest.cc',
         'browser/task_profiler/task_profiler_data_serializer_unittest.cc',
         'browser/themes/browser_theme_pack_unittest.cc',
         'browser/themes/theme_properties_unittest.cc',
@@ -1506,6 +1505,8 @@
         'browser/ui/cocoa/infobars/confirm_infobar_controller_unittest.mm',
         'browser/ui/cocoa/infobars/infobar_container_controller_unittest.mm',
         'browser/ui/cocoa/infobars/infobar_gradient_view_unittest.mm',
+        'browser/ui/cocoa/infobars/mock_confirm_infobar_delegate.cc',
+        'browser/ui/cocoa/infobars/mock_confirm_infobar_delegate.h',
         'browser/ui/cocoa/infobars/translate_infobar_unittest.mm',
         'browser/ui/cocoa/location_bar/autocomplete_text_field_cell_unittest.mm',
         'browser/ui/cocoa/location_bar/autocomplete_text_field_editor_unittest.mm',
@@ -1651,6 +1652,7 @@
         'browser/ui/webui/fileicon_source_unittest.cc',
         'browser/ui/webui/history_ui_unittest.cc',
         'browser/ui/webui/ntp/android/partner_bookmarks_shim_unittest.cc',
+        'browser/ui/webui/ntp/ntp_user_data_logger_unittest.cc',
         'browser/ui/webui/ntp/suggestions_combiner_unittest.cc',
         'browser/ui/webui/options/chromeos/cros_language_options_handler_unittest.cc',
         'browser/ui/webui/options/language_options_handler_unittest.cc',
@@ -1771,11 +1773,8 @@
         'common/mac/objc_method_swizzle_unittest.mm',
         'common/mac/objc_zombie_unittest.mm',
         'common/metrics/caching_permuted_entropy_provider_unittest.cc',
-        'common/metrics/entropy_provider_unittest.cc',
         'common/metrics/metrics_log_base_unittest.cc',
         'common/metrics/metrics_log_manager_unittest.cc',
-        'common/metrics/metrics_util_unittest.cc',
-        'common/metrics/variations/variations_associated_data_unittest.cc',
         'common/metrics/variations/variations_util_unittest.cc',
         'common/multi_process_lock_unittest.cc',
         'common/net/url_fixer_upper_unittest.cc',
@@ -1925,6 +1924,9 @@
         '../google_apis/gaia/oauth2_access_token_fetcher_unittest.cc',
         '../google_apis/gaia/oauth2_api_call_flow_unittest.cc',
         '../google_apis/gaia/oauth2_mint_token_flow_unittest.cc',
+        '../google_apis/gaia/oauth2_token_service_test_util.cc',
+        '../google_apis/gaia/oauth2_token_service_test_util.h',
+        '../google_apis/gaia/oauth2_token_service_unittest.cc',
         '../skia/ext/analysis_canvas_unittest.cc',
         '../skia/ext/bitmap_platform_device_mac_unittest.cc',
         '../skia/ext/convolver_unittest.cc',
@@ -1973,6 +1975,7 @@
             '../components/component_strings.gyp:component_strings',
             '../device/bluetooth/bluetooth.gyp:device_bluetooth_mocks',
             '../gpu/gpu.gyp:gpu_unittest_utils',
+            '../media/media.gyp:media_test_support',
             '../ppapi/ppapi_internal.gyp:ppapi_unittest_shared',
             '../third_party/cld/cld.gyp:cld',
             '../third_party/leveldatabase/leveldatabase.gyp:leveldatabase',
@@ -2047,6 +2050,7 @@
         ['disable_nacl==1', {
           'sources!':[
             'browser/nacl_host/nacl_file_host_unittest.cc',
+            'browser/nacl_host/nacl_process_host_unittest.cc',
             'browser/nacl_host/nacl_validation_cache_unittest.cc',
             'browser/nacl_host/pnacl_host_unittest.cc',
             'browser/nacl_host/pnacl_translation_cache_unittest.cc',
@@ -2061,6 +2065,7 @@
             ['exclude', '^../extensions/'],
             ['exclude', '^browser/extensions/activity_log/'],
             ['exclude', '^browser/extensions/api/'],
+            ['exclude', '^browser/extensions/error_console/'],
             ['exclude', '^browser/sync/glue/extensions_activity_monitor_unittest.cc'],
             ['exclude', '^common/extensions/api/'],
             ['exclude', '^common/extensions/manifest_handlers/'],
diff --git a/chrome/common/cancelable_task_tracker.cc b/chrome/common/cancelable_task_tracker.cc
index f27d116..4f2c9af 100644
--- a/chrome/common/cancelable_task_tracker.cc
+++ b/chrome/common/cancelable_task_tracker.cc
@@ -7,7 +7,7 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
 #include "base/compiler_specific.h"
 #include "base/location.h"
 #include "base/memory/ref_counted.h"
diff --git a/chrome/common/child_process_logging.h b/chrome/common/child_process_logging.h
index 21a12bb..d0a5327 100644
--- a/chrome/common/child_process_logging.h
+++ b/chrome/common/child_process_logging.h
@@ -15,10 +15,6 @@
 
 class CommandLine;
 
-namespace gpu {
-struct GPUInfo;
-}
-
 // The maximum number of active extensions we will report.
 // Also used in chrome/app, but we define it here to avoid a common->app
 // dependency.
@@ -52,13 +48,6 @@
 extern char g_channel[];
 extern char g_client_id[];
 extern char g_extension_ids[];
-extern char g_gpu_vendor_id[];
-extern char g_gpu_device_id[];
-extern char g_gpu_gl_vendor[];
-extern char g_gpu_gl_renderer[];
-extern char g_gpu_driver_ver[];
-extern char g_gpu_ps_ver[];
-extern char g_gpu_vs_ver[];
 extern char g_num_extensions[];
 extern char g_num_switches[];
 extern char g_num_variations[];
@@ -95,9 +84,6 @@
 // Sets a number of views/tabs opened in this process.
 void SetNumberOfViews(int number_of_views);
 
-// Sets the data on the gpu to send along with crash reports.
-void SetGpuInfo(const gpu::GPUInfo& gpu_info);
-
 // Sets the data on the printer to send along with crash reports. Data may be
 // separated by ';' up to kMaxReportedPrinterRecords strings. Each substring
 // would be cut to 63 chars.
diff --git a/chrome/common/child_process_logging_mac.mm b/chrome/common/child_process_logging_mac.mm
index da7c7fe..50cbfda 100644
--- a/chrome/common/child_process_logging_mac.mm
+++ b/chrome/common/child_process_logging_mac.mm
@@ -15,7 +15,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/common/metrics/variations/variations_util.h"
 #include "chrome/installer/util/google_update_settings.h"
-#include "gpu/config/gpu_info.h"
 
 namespace child_process_logging {
 
@@ -25,12 +24,6 @@
 using base::debug::ClearCrashKey;
 
 const char* kGuidParamName = "guid";
-const char* kGPUVendorIdParamName = "gpu-venid";
-const char* kGPUDeviceIdParamName = "gpu-devid";
-const char* kGPUDriverVersionParamName = "gpu-driver";
-const char* kGPUPixelShaderVersionParamName = "gpu-psver";
-const char* kGPUVertexShaderVersionParamName = "gpu-vsver";
-const char* kGPUGLVersionParamName = "gpu-glver";
 const char* kNumberOfViews = "num-views";
 const char* kNumExtensionsName = "num-extensions";
 const char* kExtensionNameFormat = "extension-%zu";
@@ -79,37 +72,6 @@
   }
 }
 
-void SetGpuKeyValue(const char* param_name, const std::string& value_str,
-                    SetCrashKeyValueFuncT set_key_func) {
-  set_key_func(param_name, value_str);
-}
-
-void SetGpuInfoImpl(const gpu::GPUInfo& gpu_info,
-                    SetCrashKeyValueFuncT set_key_func) {
-  SetGpuKeyValue(kGPUVendorIdParamName,
-                 base::StringPrintf("0x%04x", gpu_info.gpu.vendor_id),
-                 set_key_func);
-  SetGpuKeyValue(kGPUDeviceIdParamName,
-                 base::StringPrintf("0x%04x", gpu_info.gpu.device_id),
-                 set_key_func);
-  SetGpuKeyValue(kGPUDriverVersionParamName,
-                 gpu_info.driver_version,
-                 set_key_func);
-  SetGpuKeyValue(kGPUPixelShaderVersionParamName,
-                 gpu_info.pixel_shader_version,
-                 set_key_func);
-  SetGpuKeyValue(kGPUVertexShaderVersionParamName,
-                 gpu_info.vertex_shader_version,
-                 set_key_func);
-  SetGpuKeyValue(kGPUGLVersionParamName,
-                 gpu_info.gl_version,
-                 set_key_func);
-}
-
-void SetGpuInfo(const gpu::GPUInfo& gpu_info) {
-  SetGpuInfoImpl(gpu_info, SetCrashKeyValue);
-}
-
 void SetPrinterInfo(const char* printer_info) {
   std::vector<std::string> info;
   base::SplitString(printer_info, ';', &info);
diff --git a/chrome/common/child_process_logging_posix.cc b/chrome/common/child_process_logging_posix.cc
index fa1748b..95dce00 100644
--- a/chrome/common/child_process_logging_posix.cc
+++ b/chrome/common/child_process_logging_posix.cc
@@ -12,8 +12,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/common/metrics/variations/variations_util.h"
 #include "chrome/installer/util/google_update_settings.h"
-#include "gpu/config/gpu_info.h"
-#include "url/gurl.h"
 
 namespace child_process_logging {
 
@@ -28,15 +26,6 @@
 
 char g_channel[kChannelSize] = "";
 
-static const size_t kGpuStringSize = 32;
-char g_gpu_vendor_id[kGpuStringSize] = "";
-char g_gpu_device_id[kGpuStringSize] = "";
-char g_gpu_gl_vendor[kGpuStringSize] = "";
-char g_gpu_gl_renderer[kGpuStringSize] = "";
-char g_gpu_driver_ver[kGpuStringSize] = "";
-char g_gpu_ps_ver[kGpuStringSize] = "";
-char g_gpu_vs_ver[kGpuStringSize] = "";
-
 char g_printer_info[kPrinterInfoStrLen * kMaxReportedPrinterRecords + 1] = "";
 
 static const size_t kNumSize = 32;
@@ -88,23 +77,6 @@
                 arraysize(g_extension_ids));
 }
 
-void SetGpuInfo(const gpu::GPUInfo& gpu_info) {
-  snprintf(g_gpu_vendor_id, arraysize(g_gpu_vendor_id), "0x%04x",
-           gpu_info.gpu.vendor_id);
-  snprintf(g_gpu_device_id, arraysize(g_gpu_device_id), "0x%04x",
-           gpu_info.gpu.device_id);
-  base::strlcpy(g_gpu_gl_vendor, gpu_info.gl_vendor.c_str(),
-                arraysize(g_gpu_gl_vendor));
-  base::strlcpy(g_gpu_gl_renderer, gpu_info.gl_renderer.c_str(),
-                arraysize(g_gpu_gl_renderer));
-  base::strlcpy(g_gpu_driver_ver, gpu_info.driver_version.c_str(),
-                arraysize(g_gpu_driver_ver));
-  base::strlcpy(g_gpu_ps_ver, gpu_info.pixel_shader_version.c_str(),
-                arraysize(g_gpu_ps_ver));
-  base::strlcpy(g_gpu_vs_ver,  gpu_info.vertex_shader_version.c_str(),
-                arraysize(g_gpu_vs_ver));
-}
-
 void SetPrinterInfo(const char* printer_info) {
   std::string printer_info_str;
   std::vector<std::string> info;
diff --git a/chrome/common/child_process_logging_win.cc b/chrome/common/child_process_logging_win.cc
index 8e6c9b5..d64b66f 100644
--- a/chrome/common/child_process_logging_win.cc
+++ b/chrome/common/child_process_logging_win.cc
@@ -9,13 +9,11 @@
 #include "base/command_line.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/crash_keys.h"
 #include "chrome/common/metrics/variations/variations_util.h"
 #include "chrome/installer/util/google_update_settings.h"
-#include "gpu/config/gpu_info.h"
 
 namespace child_process_logging {
 
@@ -32,11 +30,6 @@
 // void __declspec(dllexport) __cdecl SetExtensionID.
 typedef void (__cdecl *MainSetExtensionID)(size_t, const wchar_t*);
 
-// exported in breakpad_win.cc: void __declspec(dllexport) __cdecl SetGpuInfo.
-typedef void (__cdecl *MainSetGpuInfo)(const wchar_t*, const wchar_t*,
-                                       const wchar_t*, const wchar_t*,
-                                       const wchar_t*);
-
 // exported in breakpad_win.cc:
 //     void __declspec(dllexport) __cdecl SetPrinterInfo.
 typedef void (__cdecl *MainSetPrinterInfo)(const wchar_t*);
@@ -147,26 +140,6 @@
   }
 }
 
-void SetGpuInfo(const gpu::GPUInfo& gpu_info) {
-  static MainSetGpuInfo set_gpu_info = NULL;
-  // note: benign race condition on set_gpu_info.
-  if (!set_gpu_info) {
-    HMODULE exe_module = GetModuleHandle(chrome::kBrowserProcessExecutableName);
-    if (!exe_module)
-      return;
-    set_gpu_info = reinterpret_cast<MainSetGpuInfo>(
-        GetProcAddress(exe_module, "SetGpuInfo"));
-    if (!set_gpu_info)
-      return;
-  }
-  (set_gpu_info)(
-      base::StringPrintf(L"0x%04x", gpu_info.gpu.vendor_id).c_str(),
-      base::StringPrintf(L"0x%04x", gpu_info.gpu.device_id).c_str(),
-      UTF8ToUTF16(gpu_info.driver_version).c_str(),
-      UTF8ToUTF16(gpu_info.pixel_shader_version).c_str(),
-      UTF8ToUTF16(gpu_info.vertex_shader_version).c_str());
-}
-
 void SetPrinterInfo(const char* printer_info) {
   static MainSetPrinterInfo set_printer_info = NULL;
   // note: benign race condition on set_printer_info.
diff --git a/chrome/common/chrome_content_client.cc b/chrome/common/chrome_content_client.cc
index cfaa40e..3cd880d 100644
--- a/chrome/common/chrome_content_client.cc
+++ b/chrome/common/chrome_content_client.cc
@@ -29,6 +29,7 @@
 #include "content/public/common/pepper_plugin_info.h"
 #include "content/public/common/url_constants.h"
 #include "extensions/common/constants.h"
+#include "gpu/config/gpu_info.h"
 #include "grit/common_resources.h"
 #include "ppapi/shared_impl/ppapi_permissions.h"
 #include "remoting/client/plugin/pepper_entrypoints.h"
@@ -393,7 +394,24 @@
 }
 
 void ChromeContentClient::SetGpuInfo(const gpu::GPUInfo& gpu_info) {
-  child_process_logging::SetGpuInfo(gpu_info);
+#if !defined(OS_ANDROID)
+  base::debug::SetCrashKeyValue(crash_keys::kGPUVendorID,
+      base::StringPrintf("0x%04x", gpu_info.gpu.vendor_id));
+  base::debug::SetCrashKeyValue(crash_keys::kGPUDeviceID,
+      base::StringPrintf("0x%04x", gpu_info.gpu.device_id));
+#endif
+  base::debug::SetCrashKeyValue(crash_keys::kGPUDriverVersion,
+      gpu_info.driver_version);
+  base::debug::SetCrashKeyValue(crash_keys::kGPUPixelShaderVersion,
+      gpu_info.pixel_shader_version);
+  base::debug::SetCrashKeyValue(crash_keys::kGPUVertexShaderVersion,
+      gpu_info.vertex_shader_version);
+#if defined(OS_LINUX)
+  base::debug::SetCrashKeyValue(crash_keys::kGPUVendor, gpu_info.gl_vendor);
+  base::debug::SetCrashKeyValue(crash_keys::kGPURenderer, gpu_info.gl_renderer);
+#elif defined(OS_MACOSX)
+  base::debug::SetCrashKeyValue(crash_keys::kGPUGLVersion, gpu_info.gl_version);
+#endif
 }
 
 void ChromeContentClient::AddPepperPlugins(
diff --git a/chrome/common/chrome_paths.cc b/chrome/common/chrome_paths.cc
index d323aa7..904fbef 100644
--- a/chrome/common/chrome_paths.cc
+++ b/chrome/common/chrome_paths.cc
@@ -395,9 +395,9 @@
       cur = cur.Append(FILE_PATH_LITERAL("custom_wallpapers"));
       break;
 #endif
-#if defined(ENABLE_MANAGED_USERS)
+#if defined(OS_LINUX) && defined(ENABLE_MANAGED_USERS)
     case chrome::DIR_MANAGED_USERS_DEFAULT_APPS:
-      if (!PathService::Get(chrome::DIR_EXTERNAL_EXTENSIONS, &cur))
+      if (!PathService::Get(chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS, &cur))
         return false;
       cur = cur.Append(FILE_PATH_LITERAL("managed_users"));
       break;
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index b8d00b3..a72ea43 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -741,9 +741,6 @@
 // Enables Translate experimental new UX which replaces the infobar.
 const char kEnableTranslateNewUX[]         = "enable-translate-new-ux";
 
-// Enables Translate settings in chrome://settings/languages.
-const char kEnableTranslateSettings[]      = "enable-translate-settings";
-
 // Enables unrestricted SSL 3.0 fallback.
 // With this switch, SSL 3.0 fallback will be enabled for all sites.
 // Without this switch, SSL 3.0 fallback will be disabled for a site
@@ -1565,12 +1562,6 @@
 // Windows 8 and higher.  Used when relaunching metro Chrome.
 const char kForceDesktop[]                  = "force-desktop";
 
-// Allows for disabling the overlapped I/O for TCP reads.
-// Possible values are "on" or "off".
-// The default is "on" which matches the existing behavior.
-// "off" switches to use non-blocking reads and WSAEventSelect.
-const char kOverlappedRead[]                = "overlapped-reads";
-
 // Relaunches metro Chrome on Windows 8 and higher using a given shortcut.
 const char kRelaunchShortcut[]              = "relaunch-shortcut";
 
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 32203e3..cebada9 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -212,7 +212,6 @@
 extern const char kEnableTabGroupsContextMenu[];
 extern const char kEnableThumbnailRetargeting[];
 extern const char kEnableTranslateNewUX[];
-extern const char kEnableTranslateSettings[];
 extern const char kEnableUnrestrictedSSL3Fallback[];
 extern const char kEnableUserAlternateProtocolPorts[];
 extern const char kEnableWatchdog[];
@@ -439,7 +438,6 @@
 #if defined(OS_WIN)
 extern const char kForceImmersive[];
 extern const char kForceDesktop[];
-extern const char kOverlappedRead[];
 extern const char kPrintRaster[];
 extern const char kRelaunchShortcut[];
 extern const char kWaitForMutex[];
diff --git a/chrome/common/content_settings_pattern.cc b/chrome/common/content_settings_pattern.cc
index 01860c5..d5e971c 100644
--- a/chrome/common/content_settings_pattern.cc
+++ b/chrome/common/content_settings_pattern.cc
@@ -24,7 +24,7 @@
 std::string GetDefaultPort(const std::string& scheme) {
   if (scheme == chrome::kHttpScheme)
     return "80";
-  if (scheme == chrome::kHttpsScheme)
+  if (scheme == content::kHttpsScheme)
     return "443";
   return std::string();
 }
@@ -244,7 +244,7 @@
   // Test if the scheme is supported or a wildcard.
   if (!parts.is_scheme_wildcard &&
       parts.scheme != std::string(chrome::kHttpScheme) &&
-      parts.scheme != std::string(chrome::kHttpsScheme)) {
+      parts.scheme != std::string(content::kHttpsScheme)) {
     return false;
   }
   return true;
@@ -279,7 +279,7 @@
   // Test if the scheme is supported or a wildcard.
   if (!parts.is_scheme_wildcard &&
       parts.scheme != std::string(chrome::kHttpScheme) &&
-      parts.scheme != std::string(chrome::kHttpsScheme)) {
+      parts.scheme != std::string(content::kHttpsScheme)) {
     return false;
   }
   return true;
@@ -342,15 +342,15 @@
     } else if (local_url->SchemeIs(chrome::kHttpScheme)) {
       builder->WithSchemeWildcard()->WithDomainWildcard()->WithHost(
           local_url->host());
-    } else if (local_url->SchemeIs(chrome::kHttpsScheme)) {
+    } else if (local_url->SchemeIs(content::kHttpsScheme)) {
       builder->WithScheme(local_url->scheme())->WithDomainWildcard()->WithHost(
           local_url->host());
     } else {
       // Unsupported scheme
     }
     if (local_url->port().empty()) {
-      if (local_url->SchemeIs(chrome::kHttpsScheme))
-        builder->WithPort(GetDefaultPort(chrome::kHttpsScheme));
+      if (local_url->SchemeIs(content::kHttpsScheme))
+        builder->WithPort(GetDefaultPort(content::kHttpsScheme));
       else
         builder->WithPortWildcard();
     } else {
diff --git a/chrome/common/crash_keys.cc b/chrome/common/crash_keys.cc
index eae2403..3153979 100644
--- a/chrome/common/crash_keys.cc
+++ b/chrome/common/crash_keys.cc
@@ -46,6 +46,19 @@
 size_t RegisterChromeCrashKeys() {
   base::debug::CrashKey keys[] = {
     { kActiveURL, kLargeSize },
+#if !defined(OS_ANDROID)
+    { kGPUVendorID, kSmallSize },
+    { kGPUDeviceID, kSmallSize },
+#endif
+    { kGPUDriverVersion, kSmallSize },
+    { kGPUPixelShaderVersion, kSmallSize },
+    { kGPUVertexShaderVersion, kSmallSize },
+#if defined(OS_LINUX)
+    { kGPUVendor, kSmallSize },
+    { kGPURenderer, kSmallSize },
+#elif defined(OS_MACOSX)
+    { kGPUGLVersion, kSmallSize },
+#endif
 
     // content/:
     { "ppapi_path", kMediumSize },
@@ -74,6 +87,20 @@
 
 const char kActiveURL[] = "url-chunk";
 
+#if !defined(OS_ANDROID)
+const char kGPUVendorID[] = "gpu-venid";
+const char kGPUDeviceID[] = "gpu-devid";
+#endif
+const char kGPUDriverVersion[] = "gpu-driver";
+const char kGPUPixelShaderVersion[] = "gpu-psver";
+const char kGPUVertexShaderVersion[] = "gpu-vsver";
+#if defined(OS_LINUX)
+const char kGPUVendor[] = "gpu-gl-vendor";
+const char kGPURenderer[] = "gpu-gl-renderer";
+#elif defined(OS_MACOSX)
+const char kGPUGLVersion[] = "gpu-glver";
+#endif
+
 #if defined(OS_MACOSX)
 namespace mac {
 
diff --git a/chrome/common/crash_keys.h b/chrome/common/crash_keys.h
index 941a828..45bb677 100644
--- a/chrome/common/crash_keys.h
+++ b/chrome/common/crash_keys.h
@@ -18,6 +18,21 @@
 // The URL of the active tab.
 extern const char kActiveURL[];
 
+// GPU information.
+#if !defined(OS_ANDROID)
+extern const char kGPUVendorID[];
+extern const char kGPUDeviceID[];
+#endif
+extern const char kGPUDriverVersion[];
+extern const char kGPUPixelShaderVersion[];
+extern const char kGPUVertexShaderVersion[];
+#if defined(OS_LINUX)
+extern const char kGPUVendor[];
+extern const char kGPURenderer[];
+#elif defined(OS_MACOSX)
+extern const char kGPUGLVersion[];
+#endif
+
 #if defined(OS_MACOSX)
 namespace mac {
 
diff --git a/chrome/common/env_vars.cc b/chrome/common/env_vars.cc
index 063d15b..5499dc3 100644
--- a/chrome/common/env_vars.cc
+++ b/chrome/common/env_vars.cc
@@ -14,6 +14,10 @@
 // The name of the log file.
 const char kLogFileName[] = "CHROME_LOG_FILE";
 
+// Flag indicating if metro viewer is connected to browser instance.
+// As of now there is only one metro viewer instance per browser.
+const char kMetroConnected[] = "CHROME_METRO_CONNECTED";
+
 // The name of the session log directory when logged in to ChromeOS.
 const char kSessionLogDir[] = "CHROMEOS_SESSION_LOG_DIR";
 
diff --git a/chrome/common/env_vars.h b/chrome/common/env_vars.h
index 34e1e17..b1afa0b 100644
--- a/chrome/common/env_vars.h
+++ b/chrome/common/env_vars.h
@@ -11,6 +11,7 @@
 
 extern const char kHeadless[];
 extern const char kLogFileName[];
+extern const char kMetroConnected[];
 extern const char kSessionLogDir[];
 extern const char kShowRestart[];
 extern const char kRestartInfo[];
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
index 33e9c27..d29892f 100644
--- a/chrome/common/extensions/api/_api_features.json
+++ b/chrome/common/extensions/api/_api_features.json
@@ -20,7 +20,7 @@
   },
   "app": {
     "channel": "stable",
-    "extension_types": ["hosted_app", "extension", "packaged_app"],
+    "extension_types": ["hosted_app", "extension", "legacy_packaged_app"],
     "contexts": [
       "blessed_extension", "unblessed_extension", "content_script", "web_page"
     ],
@@ -219,7 +219,7 @@
   },
   "extension": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"],
+    "extension_types": ["extension", "legacy_packaged_app"],
     "contexts": ["blessed_extension"]
   },
   "extension.getURL": {
@@ -275,7 +275,7 @@
   },
   "i18n": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"],
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
     "contexts": ["blessed_extension", "unblessed_extension", "content_script"]
   },
   "identity": {
@@ -310,7 +310,7 @@
   },
   "logPrivate": {
     "dependencies": ["permission:logPrivate"],
-    "extension_types": ["extension", "packaged_app"],
+    "extension_types": ["extension", "legacy_packaged_app"],
     "contexts": ["blessed_extension"]
   },
   "management": {
@@ -320,12 +320,12 @@
   "management.getPermissionWarningsByManifest": {
     "dependencies": [],
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"]
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
   },
   "management.uninstallSelf": {
     "dependencies": [],
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"]
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
   },
   // This is not a real API, only here for documentation purposes.
   // See http://crbug.com/275944 for background.
@@ -382,7 +382,7 @@
   },
   "permissions": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"],
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
     "contexts": "all",
     "matches": ["<all_urls>"]
   },
@@ -421,7 +421,7 @@
   },
   "runtime": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"],
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
     "contexts": ["blessed_extension"]
   },
   "runtime.connect": {
@@ -524,7 +524,7 @@
   },
   "tabs": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"],
+    "extension_types": ["extension", "legacy_packaged_app"],
     "contexts": ["blessed_extension"]
   },
   "terminalPrivate": {
@@ -551,7 +551,7 @@
   },
   "types": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"],
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
     "contexts": ["blessed_extension"]
   },
   "types.private": {
@@ -594,6 +594,7 @@
     "contexts": ["blessed_extension"]
   },
   "webview": {
+    "internal": true,
     "dependencies": ["permission:webview"],
     "contexts": ["blessed_extension"]
   },
diff --git a/chrome/common/extensions/api/_manifest_features.json b/chrome/common/extensions/api/_manifest_features.json
index 81d821b..5b27040 100644
--- a/chrome/common/extensions/api/_manifest_features.json
+++ b/chrome/common/extensions/api/_manifest_features.json
@@ -8,7 +8,7 @@
 {
   "app": {
     "channel": "stable",
-    "extension_types": ["packaged_app", "hosted_app", "platform_app"]
+    "extension_types": ["legacy_packaged_app", "hosted_app", "platform_app"]
   },
   // The default platform app CSP can only be overridden by whitelisted apps.
   // This is a separate key from the top-level content_security_policy one since
@@ -36,11 +36,11 @@
     "channel": "stable",
     // Platform apps always have isolated storage, thus they cannot specify it
     // via the manifest.
-    "extension_types": ["packaged_app", "hosted_app"]
+    "extension_types": ["legacy_packaged_app", "hosted_app"]
   },
   "app.launch": {
     "channel": "stable",
-    "extension_types": ["packaged_app", "hosted_app"]
+    "extension_types": ["legacy_packaged_app", "hosted_app"]
   },
   "author": {
     "channel": "stable",
@@ -50,20 +50,20 @@
     "channel": "stable",
     "extension_types": [
       // Platform apps specify their background page via app.background.
-      "extension", "packaged_app", "hosted_app"
+      "extension", "legacy_packaged_app", "hosted_app"
     ]
   },
   "background.persistent": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app"
+      "extension", "legacy_packaged_app"
     ],
     "min_manifest_version": 2
   },
   "background_page": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "hosted_app"
+      "extension", "legacy_packaged_app", "hosted_app"
     ],
     "max_manifest_version": 1
   },
@@ -73,7 +73,7 @@
   },
   "chrome_url_overrides": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "commands": {
     "channel": "stable",
@@ -89,16 +89,16 @@
     // Platform apps have a restricted content security policy that cannot be
     // overriden (except for a whitelist of exceptions, see the
     // app.content_security_policy whitelist).
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "content_scripts": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "converted_from_user_script": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "hosted_app"
+      "extension", "legacy_packaged_app", "hosted_app"
     ],
     "no_doc": true
   },
@@ -116,17 +116,17 @@
   },
   "devtools_page": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "display_in_launcher": [
     {
       "channel": "stable",
-      "extension_types": ["packaged_app", "platform_app"],
+      "extension_types": ["legacy_packaged_app", "platform_app"],
       "location": "component"
     },
     {
       "channel": "stable",
-      "extension_types": ["packaged_app", "platform_app"],
+      "extension_types": ["legacy_packaged_app", "platform_app"],
       "whitelist": [
         "nmmhkkegccagdldgiimedpiccmgmieda"
       ]
@@ -135,12 +135,12 @@
   "display_in_new_tab_page": [
     {
       "channel": "stable",
-      "extension_types": ["packaged_app", "platform_app"],
+      "extension_types": ["legacy_packaged_app", "platform_app"],
       "location": "component"
     },
     {
       "channel": "stable",
-      "extension_types": ["packaged_app", "platform_app"],
+      "extension_types": ["legacy_packaged_app", "platform_app"],
       "whitelist": [
         "nmmhkkegccagdldgiimedpiccmgmieda"
       ]
@@ -163,12 +163,12 @@
   "externally_connectable": {
     "channel": "stable",
     "extension_types": [
-      "extension", "hosted_app", "packaged_app", "platform_app"
+      "extension", "hosted_app", "legacy_packaged_app", "platform_app"
     ]
   },
   "file_browser_handlers": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "file_handlers": {
     "channel": "stable",
@@ -176,7 +176,7 @@
   },
   "homepage_url": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "icons": {
     "channel": "stable",
@@ -188,11 +188,11 @@
   },
   "incognito": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "input_components": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "key": {
     "channel": "stable",
@@ -214,7 +214,7 @@
   },
   "mime_types": {
     "channel": "stable",
-    "extension_types": [ "extension", "packaged_app", "platform_app" ],
+    "extension_types": [ "extension", "legacy_packaged_app", "platform_app" ],
     "whitelist": [
       "oickdpebdnfbgkcaoklfcdhjniefkcji",  // browser_tests
       "gbkeegbaiigmenfmjfclcdgdpimamgkj",  // QuickOffice
@@ -226,13 +226,13 @@
   "minimum_chrome_version": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "hosted_app", "platform_app"
+      "extension", "legacy_packaged_app", "hosted_app", "platform_app"
     ]
   },
   "nacl_modules": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "hosted_app", "platform_app"
+      "extension", "legacy_packaged_app", "hosted_app", "platform_app"
     ]
   },
   "name": {
@@ -242,7 +242,7 @@
   "oauth2": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "platform_app"
+      "extension", "legacy_packaged_app", "platform_app"
     ]
   },
   "oauth2.auto_approve": {
@@ -254,29 +254,31 @@
       "mdbihdcgjmagbcapkhhkjbbdlkflmbfo",  // unit_tests
       "pmofbkohncoogjjhahejjfbppikbjigm",  // Google Now
       "hkhhlkdconhgemhegnplaldnmnmkaemd",  // Get Started App
-      "nmmhkkegccagdldgiimedpiccmgmieda"  // In-app payments support app.
+      "nmmhkkegccagdldgiimedpiccmgmieda",  // In-app payments support app.
+      "4B1D0E19C6C43C008C44A8278C8B5BFE15ABEB3C",
+      "F7FA7ABC1ECB89BA8EE6656847EFABBF43BB9BCA"
     ]
   },
   "offline_enabled": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "hosted_app", "platform_app"
+      "extension", "legacy_packaged_app", "hosted_app", "platform_app"
     ]
   },
   "omnibox": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "optional_permissions": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "hosted_app", "platform_app"
+      "extension", "legacy_packaged_app", "hosted_app", "platform_app"
     ]
   },
   "options_page": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "hosted_app"
+      "extension", "legacy_packaged_app", "hosted_app"
     ]
   },
   "page_action": {
@@ -291,7 +293,7 @@
   "permissions": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "hosted_app", "platform_app"
+      "extension", "legacy_packaged_app", "hosted_app", "platform_app"
     ]
   },
   "platforms": {
@@ -300,18 +302,18 @@
   },
   "plugins": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "hosted_app"]
+    "extension_types": ["extension", "legacy_packaged_app", "hosted_app"]
   },
   "requirements": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "hosted_app", "platform_app"
+      "extension", "legacy_packaged_app", "hosted_app", "platform_app"
     ]
   },
   "sandbox": {
     "channel": "stable",
     "extension_types": [
-      "extension", "platform_app", "packaged_app"
+      "extension", "platform_app", "legacy_packaged_app"
     ],
     "min_manifest_version": 2
   },
@@ -319,6 +321,10 @@
     "channel": "trunk",
     "extension_types": ["extension"]
   },
+  "short_name": {
+    "channel": "stable",
+    "extension_types": "all"
+  },
   "signature": {
     "channel": "stable",
     "extension_types": "all"
@@ -329,17 +335,17 @@
   },
   "storage": {
     "channel": "dev",
-    "extension_types": ["extension", "packaged_app", "platform_app"],
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
     "min_manifest_version": 2
   },
   "storage.managed_schema": {
     "channel": "dev",
-    "extension_types": ["extension", "packaged_app", "platform_app"],
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
     "min_manifest_version": 2
   },
   "system_indicator": {
     "channel": "dev",
-    "extension_types": ["extension", "packaged_app", "platform_app"]
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
   },
   "theme": {
     "channel": "stable",
@@ -347,7 +353,7 @@
   },
   "tts_engine": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "update_url": {
     "channel": "stable",
@@ -360,7 +366,7 @@
   "web_accessible_resources": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "hosted_app"
+      "extension", "legacy_packaged_app", "hosted_app"
     ]
   }
 }
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index a533a68..c5ca17b 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -8,7 +8,7 @@
 {
   "activeTab": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"],
+    "extension_types": ["extension", "legacy_packaged_app"],
     "min_manifest_version": 2
   },
   "activityLogPrivate": {
@@ -25,7 +25,7 @@
   },
   "alarms": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"],
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
     "min_manifest_version": 2
   },
   "app.runtime": {
@@ -52,7 +52,7 @@
   ],
   "autotestPrivate": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"],
+    "extension_types": ["extension", "legacy_packaged_app"],
     "location": "component"
   },
   "audioCapture": {
@@ -62,7 +62,7 @@
   "background": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "hosted_app"
+      "extension", "legacy_packaged_app", "hosted_app"
     ]
   },
   "bluetooth": {
@@ -75,20 +75,20 @@
   },
   "bookmarkManagerPrivate": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"],
+    "extension_types": ["extension", "legacy_packaged_app"],
     "location": "component"
   },
   "bookmarks": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "browsingData": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "chromePrivate": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"],
+    "extension_types": ["extension", "legacy_packaged_app"],
     "whitelist": [
       "haiffjcadagjlijoggckpgfnoeiflnem",  // Citrix Receiver
       "gnedhmakppccajfpfiihfcdlnpgomkcf",  // Citrix Receiver Beta
@@ -97,7 +97,7 @@
   },
   "chromeosInfoPrivate": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"],
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
     "whitelist": [
       "8C3741E3AF0B93B6E8E0DDD499BB0B74839EA578",  // http://crbug.com/234235
       "E703483CEF33DEC18B4B6DD84B5C776FB9182BDB"   // http://crbug.com/234235
@@ -106,37 +106,37 @@
   "clipboardRead": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "hosted_app", "platform_app"
+      "extension", "legacy_packaged_app", "hosted_app", "platform_app"
     ]
   },
   "clipboardWrite": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "hosted_app", "platform_app"
+      "extension", "legacy_packaged_app", "hosted_app", "platform_app"
     ]
   },
   "cloudPrintPrivate": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"],
+    "extension_types": ["extension", "legacy_packaged_app"],
     // CloudPrint
     "whitelist": ["mfehgcgbbipciphmccgaenjidiccnmng"]
   },
   "commandLinePrivate": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"],
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
     "location": "component"
   },
   "contentSettings": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "contextMenus": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"]
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
   },
   "cookies": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "diagnostics": [
     {
@@ -155,7 +155,7 @@
   ],
   "debugger": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "developerPrivate": {
     "channel": "dev",
@@ -169,7 +169,7 @@
   },
   "devtools": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "declarativeContent": {
     "channel": "trunk",
@@ -178,11 +178,11 @@
   "declarativeWebRequest": [
     {
       "channel": "beta",
-      "extension_types": ["extension", "packaged_app"]
+      "extension_types": ["extension", "legacy_packaged_app"]
     },
     {
       "channel": "stable",
-      "extension_types": ["extension", "packaged_app"],
+      "extension_types": ["extension", "legacy_packaged_app"],
       // Legacy Browser Support (remove once this API hits stable).
       "whitelist": ["heildphpnddilhkemkielfhnkaagiabh"]
     }
@@ -199,15 +199,15 @@
     }
   ],
   "downloads": {
-    "channel": "stable",
+    "channel": "beta",
     "extension_types": ["extension"]
   },
   "downloads.open": {
-    "channel": "stable",
+    "channel": "beta",
     "extension_types": ["extension"]
   },
   "downloads.shelf": {
-    "channel": "stable",
+    "channel": "beta",
     "extension_types": ["extension"]
   },
   "dial": {
@@ -223,7 +223,7 @@
   },
   "enterprise.platformKeysPrivate": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"],
+    "extension_types": ["extension", "legacy_packaged_app"],
     "whitelist": [
       "inmdanhiiibfpdlljnjjbchhjgelojnn",  // Dogfood
       "cbpmgnfekbgbgpkmokfppmldaccjcbnb"   // Test
@@ -232,7 +232,7 @@
   "experimental": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "hosted_app", "platform_app"
+      "extension", "legacy_packaged_app", "hosted_app", "platform_app"
     ]
   },
   "feedbackPrivate": {
@@ -246,11 +246,11 @@
   },
   "fileBrowserHandler": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"]
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
   },
   "fileBrowserPrivate": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"],
+    "extension_types": ["extension", "legacy_packaged_app"],
     "location": "component"
   },
   "fileSystem": [{
@@ -261,6 +261,10 @@
     "extension_types": ["extension"],
     "whitelist": [ "2FC374607C2DF285634B67C64A2E356C607091C3" ]
   }],
+  "fileSystem.directory": [{
+    "channel": "trunk",
+    "extension_types": ["platform_app"]
+  }],
   "fileSystem.retainEntries": [{
     "channel": "dev",
     "extension_types": ["platform_app"]
@@ -275,7 +279,7 @@
   }],
   "fontSettings": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "fullscreen": {
     "channel": "stable",
@@ -284,12 +288,12 @@
   "geolocation": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "hosted_app", "platform_app"
+      "extension", "legacy_packaged_app", "hosted_app", "platform_app"
     ]
   },
   "history": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "identity": {
     "channel": "stable",
@@ -298,26 +302,26 @@
   "identityPrivate": {
     "channel": "stable",
     "extension_types": [
-      "packaged_app"
+      "legacy_packaged_app"
     ],
     "location": "component"
   },
   "idle": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"]
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
   },
   "infobars": {
     "channel": "dev",
-    "extension_types": ["extension", "packaged_app", "platform_app"]
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
   },
   "input": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"],
+    "extension_types": ["extension", "legacy_packaged_app"],
     "inplies_full_url_access": true
   },
   "inputMethodPrivate": [{
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"],
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
     "whitelist": [
       "haiffjcadagjlijoggckpgfnoeiflnem",  // Citrix Receiver
       "gnedhmakppccajfpfiihfcdlnpgomkcf",  // Citrix Receiver Beta
@@ -327,33 +331,33 @@
     ]
   },{
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"],
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
     "location": "component"
   }],
   "location": [
     {
       "channel": "dev",
       "extension_types": [
-        "extension", "packaged_app", "platform_app"
+        "extension", "legacy_packaged_app", "platform_app"
       ]
     },
     {
       "channel": "stable",
       "extension_types": [
-        "extension", "packaged_app", "hosted_app", "platform_app"
+        "extension", "legacy_packaged_app", "hosted_app", "platform_app"
       ],
       "location": "component"
     }
   ],
   "logPrivate": {
     "channel": "dev",
-    "extension_types": ["extension", "packaged_app"],
+    "extension_types": ["extension", "legacy_packaged_app"],
     "location": "component"
   },
   "management": [
     {
       "channel": "stable",
-      "extension_types": ["extension", "packaged_app"]
+      "extension_types": ["extension", "legacy_packaged_app"]
     },
     {
       "channel": "stable",
@@ -395,12 +399,12 @@
   },
   "mediaPlayerPrivate": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"],
+    "extension_types": ["extension", "legacy_packaged_app"],
     "location": "component"
   },
   "metricsPrivate": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"],
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
     "whitelist": [
       // The file manager and Google Now are component extensions, and they can
       // currently use whitelisted interfaces without being on the corresponding
@@ -431,12 +435,12 @@
   "nativeMessaging": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "platform_app"
+      "extension", "legacy_packaged_app", "platform_app"
     ]
   },
   "networkingPrivate": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"],
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
     "whitelist": [
       "pkedcjkdefgpdelpbcmbmeomcjbeemfm",  // Trusted Tester
       "fmfcbgogabcbclcofgocippekhfcmgfj",  // Staging
@@ -457,17 +461,17 @@
     // notifications permission with WebKit/Blink notifications.
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "hosted_app", "platform_app"
+      "extension", "legacy_packaged_app", "hosted_app", "platform_app"
     ]
   },
   "echoPrivate": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"],
+    "extension_types": ["extension", "legacy_packaged_app"],
     "location": "component"
   },
   "pageCapture": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "pointerLock": {
     "channel": "stable",
@@ -475,12 +479,12 @@
   },
   "plugin": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "power": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "platform_app"
+      "extension", "legacy_packaged_app", "platform_app"
     ]
   },
   "preferencesPrivate":  {
@@ -492,7 +496,7 @@
   },
   "privacy": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "processes": {
     "channel": "dev",
@@ -500,7 +504,7 @@
   },
   "proxy": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "pushMessaging": {
     "channel": "stable",
@@ -515,7 +519,7 @@
   },
   "rtcPrivate": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"],
+    "extension_types": ["extension", "legacy_packaged_app"],
     "whitelist": [
       "nckgahadagoaajjgafhacjanaoiihapd",  // Google Talk prod
       "eggnbpckecmjlblplehfpjjdhhidfdoj",  // Google Talk beta
@@ -527,11 +531,11 @@
   // values to verify restrictions.
   "runtime": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"]
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
   },
   "screensaver": {
     "channel": "stable",
-    "extension_types": ["packaged_app", "hosted_app", "platform_app"]
+    "extension_types": ["legacy_packaged_app", "hosted_app", "platform_app"]
   },
   "serial": {
     "channel": "stable",
@@ -539,11 +543,11 @@
   },
   "sessions": {
     "channel": "dev",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "streamsPrivate": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"],
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
     "whitelist": [
       "oickdpebdnfbgkcaoklfcdhjniefkcji",  // browser_tests
       "gbkeegbaiigmenfmjfclcdgdpimamgkj",  // QuickOffice
@@ -575,44 +579,44 @@
   },
   "systemIndicator": {
     "channel": "dev",
-    "extension_types": ["extension", "packaged_app", "platform_app"]
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
   },
   "storage": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"],
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
     "min_manifest_version": 2
   },
   "system.cpu": {
     "channel": "dev",
-    "extension_types": ["extension", "packaged_app", "platform_app"]
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
   },
   "system.memory": {
     "channel": "dev",
-    "extension_types": ["extension", "packaged_app", "platform_app"]
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
   },
   "system.storage": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"]
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
   },
   "system.display": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"]
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
   },
   "systemPrivate": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"],
+    "extension_types": ["extension", "legacy_packaged_app"],
     "location": "component"
   },
   "tabs": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "tabCapture": [{
     "channel": "dev",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   }, {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"],
+    "extension_types": ["extension", "legacy_packaged_app"],
     "whitelist": [
       "pkedcjkdefgpdelpbcmbmeomcjbeemfm",  // Trusted Tester
       "fmfcbgogabcbclcofgocippekhfcmgfj",  // Staging
@@ -623,7 +627,7 @@
   }],
   "terminalPrivate": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"],
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
     "whitelist": [
       "pnhechapfaindjhompbnflcldabbghjo",  // HTerm
       "okddffdblfhhnmhodogpojmfkjmhinfp"   // HTerm dev
@@ -631,20 +635,20 @@
   },
   "topSites": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "tts": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app", "platform_app"]
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
   },
   "ttsEngine": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "unlimitedStorage": {
     "channel": "stable",
     "extension_types": [
-      "extension", "packaged_app", "hosted_app", "platform_app"
+      "extension", "legacy_packaged_app", "hosted_app", "platform_app"
     ]
   },
   "usb": {
@@ -666,11 +670,11 @@
   },
   "webNavigation": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "webstorePrivate": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"],
+    "extension_types": ["extension", "legacy_packaged_app"],
     "whitelist": [
       "ahfgeienlihckogmohjhadlkjgocpleb",  // Web Store
       "afchcafgojfnemjkcbhfekplkmjaldaa"   // Enterprise Web Store
@@ -678,11 +682,11 @@
   },
   "webRequest": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "webRequestBlocking": {
     "channel": "stable",
-    "extension_types": ["extension", "packaged_app"]
+    "extension_types": ["extension", "legacy_packaged_app"]
   },
   "webview": {
     "channel": "stable",
diff --git a/chrome/common/extensions/api/activity_log_private.json b/chrome/common/extensions/api/activity_log_private.json
index 3e915d8..6dcea69 100644
--- a/chrome/common/extensions/api/activity_log_private.json
+++ b/chrome/common/extensions/api/activity_log_private.json
@@ -25,12 +25,15 @@
         }
       },
       {
-        "id": "ActivityFilter",
+        "id": "Filter",
         "type": "object",
-        "description": "The result set will be limited to rows that match the specification. All matches will be exact except for URL fields, which are considered prefixes.",
+        "description": "Used to specify values for a lookup.",
         "properties": {
-          "daysAgo": {"type": "string", "optional": true},
-          "activityDetail": {"$ref": "ExtensionActivity", "optional": true}
+          "extensionId": {"type": "string", "optional": true, "description": "Exact match"},
+          "activityType": {"type": "string", "enum": ["api_call", "api_event", "content_script", "dom_access", "dom_event", "web_request", "any"], "description": "Exact match or any"},
+          "apiCall": {"type": "string", "optional": true, "description": "Exact match"},
+          "pageUrl": {"type": "string", "optional": true, "description": "Treated as a prefix"},
+          "argUrl": {"type": "string", "optional": true, "description": "Treated as a prefix"}
         }
       },
       {
@@ -38,8 +41,7 @@
         "type": "object",
         "description": "This holds the results of a lookup, the filter of the lookup, the time of the lookup, and whether there are more results that match.",
         "properties": {
-          "result": {"type": "array", "items": {"$ref": "ExtensionActivity"}},
-          "filter": {"$ref": "ActivityFilter"},
+          "activities": {"type": "array", "items": {"$ref": "ExtensionActivity"}},
           "maxTime": {"type": "integer", "optional": true},
           "moreResults": {"type": "boolean"}
         }
@@ -53,7 +55,8 @@
         "parameters": [
           {
             "name": "filter",
-            "$ref": "ActivityFilter"
+            "$ref": "Filter",
+            "description": "Fill out the fields that you want to search for in the database."
           },
           {
             "name": "maxTime",
diff --git a/chrome/common/extensions/api/developer_private.idl b/chrome/common/extensions/api/developer_private.idl
index 9329723..57307e3 100644
--- a/chrome/common/extensions/api/developer_private.idl
+++ b/chrome/common/extensions/api/developer_private.idl
@@ -162,9 +162,6 @@
     // Reloads a given item with |itemId|.
     static void reload(DOMString itemId, optional VoidCallback callback);
 
-    // Restarts a given item with |itemId|.
-    static void restart(DOMString itemId, optional VoidCallback callback);
-
     // Enable / Disable a given item with id |itemId|.
     static void enable(DOMString itemId,
                        boolean enable,
@@ -207,6 +204,9 @@
     // strings as a dictionary mapping from string identifier to the
     // translated string to use in the apps_debugger app UI.
     static void getStrings(GetStringsCallback callback);
+
+    // Returns true if the profile is managed.
+    static void isProfileManaged(BooleanCallback callback);
   };
 
   interface Events {
diff --git a/chrome/common/extensions/api/echo_private.json b/chrome/common/extensions/api/echo_private.json
index 2d1a40f..e32a96a 100644
--- a/chrome/common/extensions/api/echo_private.json
+++ b/chrome/common/extensions/api/echo_private.json
@@ -12,6 +12,48 @@
     "platforms": ["chromeos"],
     "functions": [
       {
+        "name": "setOfferInfo",
+        "description": "Sets the offer info in Local State.",
+        "type": "function",
+        "parameters": [
+          {
+            "name": "id",
+            "type": "string",
+            "description": "The service id of the echo offer."
+          },
+          {
+            "name": "offerInfo",
+            "type": "object",
+            "additionalProperties": { "type": "any" },
+            "description": "The offer info."
+          }
+        ]
+      },
+      {
+        "name": "getOfferInfo",
+        "description": "Check in Local State for the offer info.",
+        "type": "function",
+        "parameters": [
+          {
+            "name": "id",
+            "type": "string",
+            "description": "The service id of the offer eligibility check."
+          },
+          {
+            "name": "callback",
+            "type": "function",
+            "parameters": [
+              {
+                "name": "result",
+                "type": "object",
+                "additionalProperties": { "type": "any" },
+                "description": "The returned offer info. If the offer info is not available, api will raise error."
+              }
+            ]
+          }
+        ]
+      },
+      {
         "name": "getRegistrationCode",
         "description": "Get the group or coupon code from underlying storage.",
         "type": "function",
diff --git a/chrome/common/extensions/api/file_browser_private.json b/chrome/common/extensions/api/file_browser_private.json
index 98fb729..fb6588a 100644
--- a/chrome/common/extensions/api/file_browser_private.json
+++ b/chrome/common/extensions/api/file_browser_private.json
@@ -6,6 +6,7 @@
   {
     "namespace":"fileBrowserPrivate",
     "description": "none",
+    "platforms": ["chromeos"],
     "types": [
       {
         "id": "FileTask",
@@ -67,39 +68,6 @@
         }
       },
       {
-        "id": "DriveWebApp",
-        "type": "object",
-        "description": "Drive WebApp properties.",
-        "properties": {
-          "appId": {
-            "type": "string",
-            "description": "WebApp ID."
-          },
-          "appName": {
-            "type": "string",
-            "description": "WebApp name."
-          },
-          "appIcon": {
-            "type": "string",
-            "optional": true,
-            "description": "URL to the Drive application icon for this application."
-          },
-          "docIcon": {
-            "type": "string",
-            "optional": true,
-            "description": "URL to the Drive document icon for documents associated with this application."
-          },
-          "objectType": {
-            "type": "string",
-            "description": "Object (file) type description."
-          },
-          "isPrimary": {
-            "type": "boolean",
-            "description": "True if this WebApp is the primary (default) open action for this file."
-          }
-        }
-      },
-      {
         "id": "DriveEntryProperties",
         "type": "object",
         "description": "Drive file properties.",
@@ -139,11 +107,10 @@
             "optional": true,
             "description": "The error code (from base::PlatformFileError) if fetching the properties for this file had an error."
           },
-          "driveApps" : {
-            "type": "array",
+          "customIconUrl": {
+            "type": "string",
             "optional": true,
-            "items": {"$ref": "DriveWebApp"},
-            "description": "An array of WebApps capable of opening this file."
+            "description": "URL to the custom icon for this file."
           },
           "contentMimeType": {
             "type": "string",
@@ -169,10 +136,12 @@
           },
           "sourcePath": {
             "type": "string",
+            "optional": true,
             "description": "The path to the mounted device, archive file or network resource."
           },
           "mountType": {
             "type": "string",
+            "optional": true,
             "enum": ["device", "file", "network"],
             "description": "Type of the mount."
           },
@@ -208,31 +177,38 @@
           },
           "devicePath": {
             "type": "string",
+            "optional": true,
             "description": "Disk volume device path."
           },
           "systemPath": {
             "type": "string",
+            "optional": true,
             "description": "Disk volume system path."
           },
           "filePath": {
             "type": "string",
+            "optional": true,
             "description": "Disk volume file path."
           },
           "deviceLabel": {
             "type": "string",
+            "optional": true,
             "description": "Volume label."
           },
           "driveLabel": {
             "type": "string",
+            "optional": true,
             "description": "Volume's disk label."
           },
           "deviceType": {
             "type": "string",
+            "optional": true,
             "enum": ["usb", "sd", "optical", "mobile", "unknown"],
             "description": "Device type."
           },
           "isParent": {
             "type": "boolean",
+            "optional": true,
             "description": "Flag that specifies if volume is a parent device."
           },
           "isReadOnly": {
@@ -241,14 +217,17 @@
           },
           "hasMedia": {
             "type": "boolean",
+            "optional": true,
             "description": "Flag that specifies if volume has any media."
           },
           "isOnBootDevice": {
             "type": "boolean",
+            "optional": true,
             "description": "Flag that specifies if volume is on boot device."
           },
           "totalSize": {
             "type": "integer",
+            "optional": true,
             "description": "Total disk volume size."
           }
         }
@@ -573,6 +552,7 @@
                 "name" : "fileSystem",
                 "type": "object",
                 "optional": true,
+                "additionalProperties": { "type": "any" },
                 "description": "A DOMFileSystem instance for local file system access. null if the caller has no appropriate permissions."
               }
             ]
@@ -635,30 +615,6 @@
         ]
       },
       {
-        "name": "viewFiles",
-        "type": "function",
-        "description": "Views multiple files.",
-        "parameters": [
-          {
-            "name": "fileUrls",
-            "type": "array",
-            "description": "Array of selected paths",
-            "items": {"type": "string"}
-          },
-          {
-            "name": "callback",
-            "type": "function",
-            "parameters": [
-              {
-                "name" : "success",
-                "type": "boolean",
-                "description": "True if the selected files can be viewed by the browser."
-              }
-            ]
-          }
-        ]
-      },
-      {
         "name": "getDriveEntryProperties",
         "description": "Requests Drive file properties for a file",
         "parameters": [
@@ -768,6 +724,7 @@
           {
             "name": "options",
             "type": "object",
+            "additionalProperties": { "type": "any" },
             "description": "Name/value pairs for source specific options"
           },
           {
@@ -961,6 +918,7 @@
                 "items": {
                   "type": "object",
                   "isInstanceOf": "Entry",
+                  "additionalProperties": { "type": "any" },
                   "description": "An Entry object which represents a Drive file. The conversion into a kind of FileEntry object is done in file_browser_handler_custom_bindings.cc. For filesystem API's Entry interface, see <a href='http://www.w3.org/TR/file-system-api/#the-entry-interface'>The Entry interface</a>."
                 }
               },
@@ -1013,6 +971,7 @@
                     "entry": {
                       "type": "object",
                       "isInstanceOf": "Entry",
+                      "additionalProperties": { "type": "any" },
                       "description": "A dictionary object which represents a Drive file. This will be converted into a kind of FileEntry object. See file_browser_handler_custom_bindings.cc for details. For filesystem API's Entry interface, see <a href='http://www.w3.org/TR/file-system-api/#the-entry-interface'>The Entry interface</a>."
                     },
                     "highlightedBaseName": {
diff --git a/chrome/common/extensions/api/file_system.idl b/chrome/common/extensions/api/file_system.idl
index 50f099c..7e3d6d6 100644
--- a/chrome/common/extensions/api/file_system.idl
+++ b/chrome/common/extensions/api/file_system.idl
@@ -24,8 +24,10 @@
 
   enum ChooseEntryType {
 
-    // Prompts the user to open an existing file and returns a read-only
-    // FileEntry on success.
+    // Prompts the user to open an existing file and returns a FileEntry on
+    // success. From Chrome 31 onwards, the FileEntry will be writable if the
+    // application has the 'write' permission under 'fileSystem'; otherwise, the
+    // FileEntry will be read-only.
     openFile,
 
     // Prompts the user to open an existing file and returns a writable
@@ -36,11 +38,21 @@
     // Prompts the user to open an existing file or a new file and returns a
     // writable FileEntry on success. Calls using this type will fail unless the
     // application has the 'write' permission under 'fileSystem'.
-    saveFile
+    saveFile,
+
+    // Prompts the user to open a directory and returns a DirectoryEntry on
+    // success. Calls using this type will fail unless the application has the
+    // 'directory' permission under 'fileSystem'. If the application has the
+    // 'write' permission under 'fileSystem', the returned DirectoryEntry will
+    // be writable; otherwise it will be read-only. New in Chrome 31.
+    openDirectory
   };
 
   dictionary ChooseEntryOptions {
-    // Type of the prompt to show. The default is 'openFile'.
+    // Type of the prompt to show. The default is 'openFile'. From Chrome 31
+    // onwards, 'openWritableFile' is deprecated and 'openFile' will return a
+    // writable file entry for apps with the 'write' permission under
+    // 'fileSystem'.
     ChooseEntryType? type;
 
     // The suggested file name that will be presented to the user as the
@@ -63,36 +75,38 @@
     boolean? acceptsMultiple;
   };
   callback GetDisplayPathCallback = void (DOMString displayPath);
-  callback FileEntryCallback = void ([instanceOf=FileEntry] object fileEntry);
-  callback FileEntriesCallback = void (
-      [instanceOf=FileEntry] optional object fileEntry,
+  callback EntryCallback = void ([instanceOf=Entry] object entry);
+  callback EntriesCallback = void (
+      [instanceOf=Entry] optional object entry,
       [instanceOf=FileEntry] optional object[] fileEntries);
   callback IsWritableCallback = void (boolean isWritable);
   callback IsRestorableCallback = void (boolean isRestorable);
 
   interface Functions {
-    // Get the display path of a FileEntry object. The display path is based on
-    // the full path of the file on the local file system, but may be made more
-    // readable for display purposes.
-    static void getDisplayPath([instanceOf=FileEntry] object fileEntry,
+    // Get the display path of an Entry object. The display path is based on
+    // the full path of the file or directory on the local file system, but may
+    // be made more readable for display purposes.
+    static void getDisplayPath([instanceOf=Entry] object entry,
                                GetDisplayPathCallback callback);
 
-    // Get a writable FileEntry from another FileEntry. This call will fail if
-    // the application does not have the 'write' permission under 'fileSystem'.
-    static void getWritableEntry([instanceOf=FileEntry] object fileEntry,
-                                 FileEntryCallback callback);
+    // Get a writable Entry from another Entry. This call will fail if the
+    // application does not have the 'write' permission under 'fileSystem'. If
+    // entry is a DirectoryEntry, this call will fail if the application does
+    // not have the 'directory' permission under 'fileSystem'.
+    static void getWritableEntry([instanceOf=Entry] object entry,
+                                 EntryCallback callback);
 
-    // Gets whether this FileEntry is writable or not.
-    static void isWritableEntry([instanceOf=FileEntry] object fileEntry,
+    // Gets whether this Entry is writable or not.
+    static void isWritableEntry([instanceOf=Entry] object entry,
                                 IsWritableCallback callback);
 
-    // Ask the user to choose a file.
+    // Ask the user to choose a file or directory.
     static void chooseEntry(optional ChooseEntryOptions options,
-                            FileEntriesCallback callback);
+                            EntriesCallback callback);
 
     // Returns the file entry with the given id if it can be restored. This call
     // will fail otherwise. This method is new in Chrome 30.
-    static void restoreEntry(DOMString id, FileEntryCallback callback);
+    static void restoreEntry(DOMString id, EntryCallback callback);
 
     // Returns whether a file entry for the given id can be restored, i.e.
     // whether restoreEntry would succeed with this id now. This method is new
@@ -106,6 +120,6 @@
     // to dev channel), entries are retained indefinitely. Otherwise, entries
     // are retained only while the app is running and across restarts. This
     // method is new in Chrome 30.
-    static DOMString retainEntry([instanceOf=FileEntry] object fileEntry);
+    static DOMString retainEntry([instanceOf=Entry] object entry);
   };
 };
diff --git a/chrome/common/extensions/api/serial.idl b/chrome/common/extensions/api/serial.idl
index 1df7550..c6429ff 100644
--- a/chrome/common/extensions/api/serial.idl
+++ b/chrome/common/extensions/api/serial.idl
@@ -8,14 +8,25 @@
 
   callback GetPortsCallback = void (DOMString[] ports);
 
+  enum DataBit { sevenbit, eightbit };
+  enum ParityBit { noparity, oddparity, evenparity };
+  enum StopBit { onestopbit, twostopbit };
+
   dictionary OpenOptions {
     // The requested bitrate of the connection to be opened. For compatibility
     // with the widest range of hardware, this number should match one of
     // commonly-available bitrates, such as 110, 300, 1200, 2400, 4800, 9600,
     // 14400, 19200, 38400, 57600, 115200. There is no guarantee, of course,
     // that the device connected to the serial port will support the requested
-    // bitrate, even if the port itself supports that bitrate.
-    long bitrate;
+    // bitrate, even if the port itself supports that bitrate. <code>9600</code>
+    // will be passed by default.
+    long? bitrate;
+    // <code>"eightbit"</code> will be passed by default.
+    DataBit? dataBit;
+    // <code>"noparity"</code> will be passed by default.
+    ParityBit? parityBit;
+    // <code>"onestopbit"</code> will be passed by default.
+    StopBit? stopBit;
   };
 
   dictionary OpenInfo {
diff --git a/chrome/common/extensions/api/sync_file_system.idl b/chrome/common/extensions/api/sync_file_system.idl
index 8cfbb99..9a02587 100644
--- a/chrome/common/extensions/api/sync_file_system.idl
+++ b/chrome/common/extensions/api/sync_file_system.idl
@@ -123,6 +123,9 @@
   // A callback type for getFileStatuses.
   callback GetFileStatusesCallback = void (FileStatusInfo[] status);
 
+  // A callback type for getServiceStatus.
+  callback GetServiceStatusCallback = void (ServiceStatus status);
+
   // A callback type for getConflictResolutionPolicy.
   callback GetConflictResolutionPolicyCallback =
       void (ConflictResolutionPolicy policy);
@@ -172,6 +175,9 @@
     // Typically called with the result from dirReader.readEntries().
     static void getFileStatuses(object[] fileEntries,
                                 GetFileStatusesCallback callback);
+
+    // Returns the current sync backend status.
+    static void getServiceStatus(GetServiceStatusCallback callback);
   };
 
   interface Events {
diff --git a/chrome/common/extensions/api/test.json b/chrome/common/extensions/api/test.json
index 94742b5..134a3ed 100644
--- a/chrome/common/extensions/api/test.json
+++ b/chrome/common/extensions/api/test.json
@@ -230,7 +230,7 @@
             "optional": true
           },
           {"type": "array", "items": {"type": "any"}, "name": "args"},
-          {"type": "string", "name": "message", "optional": true}
+          {"choices": [ {"type": "string"}, {"type": "object", "isInstanceOf": "RegExp"} ], "name": "message", "optional": true}
         ]
       },
       {
diff --git a/chrome/common/extensions/api/web_request.json b/chrome/common/extensions/api/web_request.json
index 6fd2b14..786625d 100644
--- a/chrome/common/extensions/api/web_request.json
+++ b/chrome/common/extensions/api/web_request.json
@@ -278,7 +278,7 @@
         "parameters": [
           {
             "type": "object",
-            "name": "deails",
+            "name": "details",
             "properties": {
               "requestId": {"type": "string", "description": "The ID of the request. Request IDs are unique within a browser session. As a result, they could be used to relate different events of the same request."},
               "url": {"type": "string"},
diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_manager/manifest.json b/chrome/common/extensions/docs/examples/api/downloads/download_manager/manifest.json
index 638e094..156e69c 100644
--- a/chrome/common/extensions/docs/examples/api/downloads/download_manager/manifest.json
+++ b/chrome/common/extensions/docs/examples/api/downloads/download_manager/manifest.json
@@ -1,5 +1,5 @@
 {"name": "__MSG_extName__",
- "version": "0.2",
+ "version": "0.3",
  "manifest_version": 2,
  "description": "__MSG_extDesc__",
  "icons": {"128": "icon128.png"},
diff --git a/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.js b/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.js
index 4a64dab..c9dfd16 100644
--- a/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.js
+++ b/chrome/common/extensions/docs/examples/api/downloads/download_manager/popup.js
@@ -501,11 +501,18 @@
   ratchetWidth(400);
   ratchetHeight(200);
   DownloadItem.prototype.maybeAccept.accepting_danger = true;
-  chrome.downloads.acceptDanger(this.id, function() {
-    DownloadItem.prototype.maybeAccept.accepting_danger = false;
-    arrayFrom(document.getElementById('items').childNodes).forEach(
-      function(item_div) { item_div.item.maybeAccept(); });
-  });
+  // On Mac, window.onload is run while the popup is animating in, before it is
+  // considered "visible". Prompts will not be displayed over an invisible
+  // window, so the popup will become stuck. Just wait a little bit for the
+  // window to finish animating in. http://crbug.com/280107
+  var id = this.id;
+  setTimeout(function() {
+    chrome.downloads.acceptDanger(id, function() {
+      DownloadItem.prototype.maybeAccept.accepting_danger = false;
+      arrayFrom(document.getElementById('items').childNodes).forEach(
+        function(item_div) { item_div.item.maybeAccept(); });
+    });
+  }, 500);
 };
 DownloadItem.prototype.maybeAccept.accepting_danger = false;
 
diff --git a/chrome/common/extensions/docs/server2/api_data_source.py b/chrome/common/extensions/docs/server2/api_data_source.py
index eec18e6..3a9e7ef 100644
--- a/chrome/common/extensions/docs/server2/api_data_source.py
+++ b/chrome/common/extensions/docs/server2/api_data_source.py
@@ -3,17 +3,18 @@
 # found in the LICENSE file.
 
 import copy
+import json
 import logging
 import os
 from collections import defaultdict, Mapping
 
-from branch_utility import BranchUtility
 import svn_constants
-from third_party.handlebar import Handlebar
 import third_party.json_schema_compiler.json_parse as json_parse
 import third_party.json_schema_compiler.model as model
 import third_party.json_schema_compiler.idl_schema as idl_schema
 import third_party.json_schema_compiler.idl_parser as idl_parser
+from third_party.handlebar import Handlebar
+
 
 def _RemoveNoDocs(item):
   if json_parse.IsDict(item):
@@ -31,10 +32,11 @@
       item.remove(i)
   return False
 
+
 def _DetectInlineableTypes(schema):
-  """Look for documents that are only referenced once and mark them as inline.
+  '''Look for documents that are only referenced once and mark them as inline.
   Actual inlining is done by _InlineDocs.
-  """
+  '''
   if not schema.get('types'):
     return
 
@@ -57,9 +59,10 @@
       if refcounts[type_['id']] == 1:
         type_['inline_doc'] = True
 
+
 def _InlineDocs(schema):
-  """Replace '$ref's that refer to inline_docs with the json for those docs.
-  """
+  '''Replace '$ref's that refer to inline_docs with the json for those docs.
+  '''
   types = schema.get('types')
   if types is None:
     return
@@ -91,21 +94,24 @@
 
   apply_inline(schema)
 
+
 def _CreateId(node, prefix):
   if node.parent is not None and not isinstance(node.parent, model.Namespace):
     return '-'.join([prefix, node.parent.simple_name, node.simple_name])
   return '-'.join([prefix, node.simple_name])
 
+
 def _FormatValue(value):
-  """Inserts commas every three digits for integer values. It is magic.
-  """
+  '''Inserts commas every three digits for integer values. It is magic.
+  '''
   s = str(value)
   return ','.join([s[max(0, i - 3):i] for i in range(len(s), 0, -3)][::-1])
 
+
 def _GetAddRulesDefinitionFromEvents(events):
-  """Parses the dictionary |events| to find the definition of the method
+  '''Parses the dictionary |events| to find the definition of the method
   addRules among functions of the type Event.
-  """
+  '''
   assert 'types' in events, \
       'The dictionary |events| must contain the key "types".'
   event_list = [t for t in events['types']
@@ -119,15 +125,18 @@
       'Exactly one function must be called "addRules".'
   return result_list[0]
 
+
 class _JSCModel(object):
-  """Uses a Model from the JSON Schema Compiler and generates a dict that
+  '''Uses a Model from the JSON Schema Compiler and generates a dict that
   a Handlebar template can use for a data source.
-  """
+  '''
+
   def __init__(self,
                json,
                ref_resolver,
                disable_refs,
                availability_finder,
+               branch_utility,
                parse_cache,
                template_data_source,
                add_rules_schema_function,
@@ -135,6 +144,9 @@
     self._ref_resolver = ref_resolver
     self._disable_refs = disable_refs
     self._availability_finder = availability_finder
+    self._branch_utility = branch_utility
+    self._api_availabilities = parse_cache.GetFromFile(
+        '%s/api_availabilities.json' % svn_constants.JSON_PATH)
     self._intro_tables = parse_cache.GetFromFile(
         '%s/intro_tables.json' % svn_constants.JSON_PATH)
     self._api_features = parse_cache.GetFromFile(
@@ -183,127 +195,14 @@
           (item['name'], item) for item in as_dict[item_type])
     return as_dict
 
-  def _GetIntroTableList(self):
-    """Create a generic data structure that can be traversed by the templates
-    to create an API intro table.
-    """
-    intro_rows = [
-      self._GetIntroDescriptionRow(),
-      self._GetIntroAvailabilityRow()
-    ] + self._GetIntroDependencyRows()
-
-    # Add rows using data from intro_tables.json, overriding any existing rows
-    # if they share the same 'title' attribute.
-    row_titles = [row['title'] for row in intro_rows]
-    for misc_row in self._GetMiscIntroRows():
-      if misc_row['title'] in row_titles:
-        intro_rows[row_titles.index(misc_row['title'])] = misc_row
-      else:
-        intro_rows.append(misc_row)
-
-    return intro_rows
-
-  def _GetIntroDescriptionRow(self):
-    """ Generates the 'Description' row data for an API intro table.
-    """
-    return {
-      'title': 'Description',
-      'content': [
-        { 'text': self._FormatDescription(self._namespace.description) }
-      ]
-    }
-
-  def _GetIntroAvailabilityRow(self):
-    """ Generates the 'Availability' row data for an API intro table.
-    """
-    if self._IsExperimental():
-      status = 'experimental'
-      version = None
-    else:
-      availability = self._GetApiAvailability()
-      status = availability.channel
-      version = availability.version
-    return {
-      'title': 'Availability',
-      'content': [{
-        'partial': self._template_data_source.get(
-            'intro_tables/%s_message.html' % status),
-        'version': version
-      }]
-    }
-
-  def _GetIntroDependencyRows(self):
-    # Devtools aren't in _api_features. If we're dealing with devtools, bail.
-    if 'devtools' in self._namespace.name:
-      return []
-    feature = self._api_features.get(self._namespace.name)
-    assert feature, ('"%s" not found in _api_features.json.'
-                     % self._namespace.name)
-
-    dependencies = feature.get('dependencies')
-    if dependencies is None:
-      return []
-
-    def make_code_node(text):
-      return { 'class': 'code', 'text': text }
-
-    permissions_content = []
-    manifest_content = []
-
-    def categorize_dependency(dependency):
-      context, name = dependency.split(':', 1)
-      if context == 'permission':
-        permissions_content.append(make_code_node('"%s"' % name))
-      elif context == 'manifest':
-        manifest_content.append(make_code_node('"%s": {...}' % name))
-      elif context == 'api':
-        transitive_dependencies = (
-            self._api_features.get(name, {}).get('dependencies', []))
-        for transitive_dependency in transitive_dependencies:
-          categorize_dependency(transitive_dependency)
-      else:
-        raise ValueError('Unrecognized dependency for %s: %s' % (
-            self._namespace.name, context))
-
-    for dependency in dependencies:
-      categorize_dependency(dependency)
-
-    dependency_rows = []
-    if permissions_content:
-      dependency_rows.append({
-        'title': 'Permissions',
-        'content': permissions_content
-      })
-    if manifest_content:
-      dependency_rows.append({
-        'title': 'Manifest',
-        'content': manifest_content
-      })
-    return dependency_rows
-
-  def _GetMiscIntroRows(self):
-    """ Generates miscellaneous intro table row data, such as 'Permissions',
-    'Samples', and 'Learn More', using intro_tables.json.
-    """
-    misc_rows = []
-    # Look up the API name in intro_tables.json, which is structured
-    # similarly to the data structure being created. If the name is found, loop
-    # through the attributes and add them to this structure.
-    table_info = self._intro_tables.get(self._namespace.name)
-    if table_info is None:
-      return misc_rows
-
-    for category in table_info.keys():
-      content = copy.deepcopy(table_info[category])
-      for node in content:
-        # If there is a 'partial' argument and it hasn't already been
-        # converted to a Handlebar object, transform it to a template.
-        if 'partial' in node:
-          node['partial'] = self._template_data_source.get(node['partial'])
-      misc_rows.append({ 'title': category, 'content': content })
-    return misc_rows
-
   def _GetApiAvailability(self):
+    # Check for a predetermined availability for this API.
+    api_info = self._api_availabilities.get(self._namespace.name)
+    if api_info is not None:
+      channel = api_info['channel']
+      if channel == 'stable':
+        return self._branch_utility.GetStableChannelInfo(api_info['version'])
+      return self._branch_utility.GetChannelInfo(channel)
     return self._availability_finder.GetApiAvailability(self._namespace.name)
 
   def _GetChannelWarning(self):
@@ -499,10 +398,132 @@
     else:
       dst_dict['simple_type'] = type_.property_type.name.lower()
 
+  def _GetIntroTableList(self):
+    '''Create a generic data structure that can be traversed by the templates
+    to create an API intro table.
+    '''
+    intro_rows = [
+      self._GetIntroDescriptionRow(),
+      self._GetIntroAvailabilityRow()
+    ] + self._GetIntroDependencyRows()
+
+    # Add rows using data from intro_tables.json, overriding any existing rows
+    # if they share the same 'title' attribute.
+    row_titles = [row['title'] for row in intro_rows]
+    for misc_row in self._GetMiscIntroRows():
+      if misc_row['title'] in row_titles:
+        intro_rows[row_titles.index(misc_row['title'])] = misc_row
+      else:
+        intro_rows.append(misc_row)
+
+    return intro_rows
+
+  def _GetIntroDescriptionRow(self):
+    ''' Generates the 'Description' row data for an API intro table.
+    '''
+    return {
+      'title': 'Description',
+      'content': [
+        { 'text': self._FormatDescription(self._namespace.description) }
+      ]
+    }
+
+  def _GetIntroAvailabilityRow(self):
+    ''' Generates the 'Availability' row data for an API intro table.
+    '''
+    if self._IsExperimental():
+      status = 'experimental'
+      version = None
+    else:
+      availability = self._GetApiAvailability()
+      status = availability.channel
+      version = availability.version
+    return {
+      'title': 'Availability',
+      'content': [{
+        'partial': self._template_data_source.get(
+            'intro_tables/%s_message.html' % status),
+        'version': version
+      }]
+    }
+
+  def _GetIntroDependencyRows(self):
+    # Devtools aren't in _api_features. If we're dealing with devtools, bail.
+    if 'devtools' in self._namespace.name:
+      return []
+    feature = self._api_features.get(self._namespace.name)
+    assert feature, ('"%s" not found in _api_features.json.'
+                     % self._namespace.name)
+
+    dependencies = feature.get('dependencies')
+    if dependencies is None:
+      return []
+
+    def make_code_node(text):
+      return { 'class': 'code', 'text': text }
+
+    permissions_content = []
+    manifest_content = []
+
+    def categorize_dependency(dependency):
+      context, name = dependency.split(':', 1)
+      if context == 'permission':
+        permissions_content.append(make_code_node('"%s"' % name))
+      elif context == 'manifest':
+        manifest_content.append(make_code_node('"%s": {...}' % name))
+      elif context == 'api':
+        transitive_dependencies = (
+            self._api_features.get(name, {}).get('dependencies', []))
+        for transitive_dependency in transitive_dependencies:
+          categorize_dependency(transitive_dependency)
+      else:
+        raise ValueError('Unrecognized dependency for %s: %s' % (
+            self._namespace.name, context))
+
+    for dependency in dependencies:
+      categorize_dependency(dependency)
+
+    dependency_rows = []
+    if permissions_content:
+      dependency_rows.append({
+        'title': 'Permissions',
+        'content': permissions_content
+      })
+    if manifest_content:
+      dependency_rows.append({
+        'title': 'Manifest',
+        'content': manifest_content
+      })
+    return dependency_rows
+
+  def _GetMiscIntroRows(self):
+    ''' Generates miscellaneous intro table row data, such as 'Permissions',
+    'Samples', and 'Learn More', using intro_tables.json.
+    '''
+    misc_rows = []
+    # Look up the API name in intro_tables.json, which is structured
+    # similarly to the data structure being created. If the name is found, loop
+    # through the attributes and add them to this structure.
+    table_info = self._intro_tables.get(self._namespace.name)
+    if table_info is None:
+      return misc_rows
+
+    for category in table_info.keys():
+      content = copy.deepcopy(table_info[category])
+      for node in content:
+        # If there is a 'partial' argument and it hasn't already been
+        # converted to a Handlebar object, transform it to a template.
+        if 'partial' in node:
+          node['partial'] = self._template_data_source.get(node['partial'])
+      misc_rows.append({ 'title': category, 'content': content })
+    return misc_rows
+
+
 class _LazySamplesGetter(object):
-  """This class is needed so that an extensions API page does not have to fetch
+  '''This class is needed so that an extensions API page does not have to fetch
   the apps samples page and vice versa.
-  """
+  '''
+
   def __init__(self, api_name, samples):
     self._api_name = api_name
     self._samples = samples
@@ -510,15 +531,18 @@
   def get(self, key):
     return self._samples.FilterSamples(key, self._api_name)
 
+
 class APIDataSource(object):
-  """This class fetches and loads JSON APIs from the FileSystem passed in with
+  '''This class fetches and loads JSON APIs from the FileSystem passed in with
   |compiled_fs_factory|, so the APIs can be plugged into templates.
-  """
+  '''
+
   class Factory(object):
     def __init__(self,
                  compiled_fs_factory,
                  base_path,
-                 availability_finder_factory):
+                 availability_finder,
+                 branch_utility):
       def create_compiled_fs(fn, category):
         return compiled_fs_factory.Create(fn, APIDataSource, category=category)
 
@@ -543,7 +567,8 @@
       self._names_cache = create_compiled_fs(self._GetAllNames, 'names')
 
       self._base_path = base_path
-      self._availability_finder = availability_finder_factory.Create()
+      self._availability_finder = availability_finder
+      self._branch_utility = branch_utility
       self._parse_cache = create_compiled_fs(
           lambda _, json: json_parse.Parse(json),
           'intro-cache')
@@ -565,10 +590,10 @@
       self._template_data_source = template_data_source_factory.Create(None, '')
 
     def Create(self, request, disable_refs=False):
-      """Create an APIDataSource. |disable_refs| specifies whether $ref's in
+      '''Create an APIDataSource. |disable_refs| specifies whether $ref's in
       APIs being processed by the |ToDict| method of _JSCModel follows $ref's
       in the API. This prevents endless recursion in ReferenceResolver.
-      """
+      '''
       if self._samples_data_source_factory is None:
         # Only error if there is a request, which means this APIDataSource is
         # actually being used to render a page.
@@ -606,6 +631,7 @@
           self._ref_resolver_factory.Create() if not disable_refs else None,
           disable_refs,
           self._availability_finder,
+          self._branch_utility,
           self._parse_cache,
           self._template_data_source,
           self._LoadAddRulesSchema).ToDict()
@@ -617,6 +643,7 @@
           self._ref_resolver_factory.Create() if not disable_refs else None,
           disable_refs,
           self._availability_finder,
+          self._branch_utility,
           self._parse_cache,
           self._template_data_source,
           self._LoadAddRulesSchema,
diff --git a/chrome/common/extensions/docs/server2/api_data_source_test.py b/chrome/common/extensions/docs/server2/api_data_source_test.py
index 8f452b9..9864fb7 100755
--- a/chrome/common/extensions/docs/server2/api_data_source_test.py
+++ b/chrome/common/extensions/docs/server2/api_data_source_test.py
@@ -15,37 +15,42 @@
                              _DetectInlineableTypes,
                              _InlineDocs,
                              _GetAddRulesDefinitionFromEvents)
+from branch_utility import ChannelInfo
 from collections import namedtuple
 from compiled_file_system import CompiledFileSystem
 from file_system import FileNotFoundError
 from object_store_creator import ObjectStoreCreator
 from reference_resolver import ReferenceResolver
+from test_branch_utility import TestBranchUtility
 from test_data.canned_data import CANNED_TEST_FILE_SYSTEM_DATA
 from test_file_system import TestFileSystem
 import third_party.json_schema_compiler.json_parse as json_parse
 
+
 def _MakeLink(href, text):
   return '<a href="%s">%s</a>' % (href, text)
 
+
 def _GetType(dict_, name):
   for type_ in dict_['types']:
     if type_['name'] == name:
       return type_
 
+
 class FakeAvailabilityFinder(object):
-  AvailabilityInfo = namedtuple('AvailabilityInfo', 'channel version')
 
   def GetApiAvailability(self, version):
-    return FakeAvailabilityFinder.AvailabilityInfo('trunk', 'trunk')
+    return ChannelInfo('stable', 396, 5)
 
-  def StringifyAvailability(self, availability):
-    return availability.channel
 
 class FakeSamplesDataSource(object):
+
   def Create(self, request):
     return {}
 
+
 class FakeAPIAndListDataSource(object):
+
   def __init__(self, json_data):
     self._json = json_data
 
@@ -60,11 +65,15 @@
   def GetAllNames(self):
     return self._json.keys()
 
+
 class FakeTemplateDataSource(object):
+
   def get(self, key):
     return 'handlebar %s' % key
 
+
 class APIDataSourceTest(unittest.TestCase):
+
   def setUp(self):
     self._base_path = os.path.join(sys.path[0], 'test_data', 'test_json')
     self._compiled_fs_factory = CompiledFileSystem.Factory(
@@ -96,6 +105,7 @@
                       self._CreateRefResolver('test_file_data_source.json'),
                       False,
                       FakeAvailabilityFinder(),
+                      TestBranchUtility.CreateWithCannedData(),
                       self._json_cache,
                       FakeTemplateDataSource(),
                       None).ToDict()
@@ -115,6 +125,7 @@
                       self._CreateRefResolver('test_file_data_source.json'),
                       False,
                       FakeAvailabilityFinder(),
+                      TestBranchUtility.CreateWithCannedData(),
                       self._json_cache,
                       FakeTemplateDataSource(),
                       None).ToDict()
@@ -130,6 +141,7 @@
                       self._CreateRefResolver('ref_test_data_source.json'),
                       False,
                       FakeAvailabilityFinder(),
+                      TestBranchUtility.CreateWithCannedData(),
                       self._json_cache,
                       FakeTemplateDataSource(),
                       None).ToDict()
@@ -149,11 +161,44 @@
     _RemoveNoDocs(d)
     self.assertEquals(self._LoadJSON('expected_nodoc.json'), d)
 
+  def testGetApiAvailability(self):
+    model = _JSCModel(self._LoadJSON('test_file.json')[0],
+                      self._CreateRefResolver('test_file_data_source.json'),
+                      False,
+                      FakeAvailabilityFinder(),
+                      TestBranchUtility.CreateWithCannedData(),
+                      self._json_cache,
+                      FakeTemplateDataSource(),
+                      None)
+    # The model namespace is "tester". No predetermined availability is found,
+    # so the FakeAvailabilityFinder instance is used to find availability.
+    self.assertEqual(ChannelInfo('stable', 396, 5),
+                     model._GetApiAvailability())
+
+    # These APIs have predetermined availabilities in the
+    # api_availabilities.json file within CANNED_DATA.
+    model._namespace.name = 'trunk_api'
+    self.assertEqual(ChannelInfo('trunk', 'trunk', 'trunk'),
+                     model._GetApiAvailability())
+
+    model._namespace.name = 'dev_api'
+    self.assertEqual(ChannelInfo('dev', 1500, 28),
+                     model._GetApiAvailability())
+
+    model._namespace.name = 'beta_api'
+    self.assertEqual(ChannelInfo('beta', 1453, 27),
+                     model._GetApiAvailability())
+
+    model._namespace.name = 'stable_api'
+    self.assertEqual(ChannelInfo('stable', 1132, 20),
+                     model._GetApiAvailability())
+
   def testGetIntroList(self):
     model = _JSCModel(self._LoadJSON('test_file.json')[0],
                       self._CreateRefResolver('test_file_data_source.json'),
                       False,
                       FakeAvailabilityFinder(),
+                      TestBranchUtility.CreateWithCannedData(),
                       self._json_cache,
                       FakeTemplateDataSource(),
                       None)
@@ -165,8 +210,8 @@
       },
       { 'title': 'Availability',
         'content': [
-          { 'partial': 'handlebar intro_tables/trunk_message.html',
-            'version': 'trunk'
+          { 'partial': 'handlebar intro_tables/stable_message.html',
+            'version': 5
           }
         ]
       },
@@ -327,6 +372,7 @@
                       self._CreateRefResolver('test_file_data_source.json'),
                       False,
                       FakeAvailabilityFinder(),
+                      TestBranchUtility.CreateWithCannedData(),
                       self._json_cache,
                       FakeTemplateDataSource(),
                       self._FakeLoadAddRulesSchema).ToDict()
diff --git a/chrome/common/extensions/docs/server2/app.yaml b/chrome/common/extensions/docs/server2/app.yaml
index 224939f..2fe805b 100644
--- a/chrome/common/extensions/docs/server2/app.yaml
+++ b/chrome/common/extensions/docs/server2/app.yaml
@@ -1,5 +1,5 @@
 application: chrome-apps-doc
-version: 2-27-0
+version: 2-27-2
 runtime: python27
 api_version: 1
 threadsafe: false
diff --git a/chrome/common/extensions/docs/server2/availability_finder.py b/chrome/common/extensions/docs/server2/availability_finder.py
index a0b6c45..b4ab31a 100644
--- a/chrome/common/extensions/docs/server2/availability_finder.py
+++ b/chrome/common/extensions/docs/server2/availability_finder.py
@@ -5,24 +5,14 @@
 import collections
 import os
 
+import svn_constants
 from branch_utility import BranchUtility
 from compiled_file_system import CompiledFileSystem
 from file_system import FileNotFoundError
-import svn_constants
-from third_party.json_schema_compiler import json_parse, model
+from third_party.json_schema_compiler import json_parse
 from third_party.json_schema_compiler.memoize import memoize
+from third_party.json_schema_compiler.model import UnixName
 
-_API_AVAILABILITIES = svn_constants.JSON_PATH + '/api_availabilities.json'
-_API_FEATURES = svn_constants.API_PATH + '/_api_features.json'
-_EXTENSION_API = svn_constants.API_PATH + '/extension_api.json'
-_MANIFEST_FEATURES = svn_constants.API_PATH + '/_manifest_features.json'
-_PERMISSION_FEATURES = svn_constants.API_PATH + '/_permission_features.json'
-_STABLE = 'stable'
-
-class AvailabilityInfo(object):
-  def __init__(self, channel, version):
-    self.channel = channel
-    self.version = version
 
 def _GetChannelFromFeatures(api_name, file_system, path):
   '''Finds API channel information within _features.json files at the given
@@ -40,29 +30,27 @@
   # purposes. Take the newest channel out of all of the entries.
   return BranchUtility.NewestChannel(entry.get('channel') for entry in feature)
 
-def _GetChannelFromApiFeatures(api_name, file_system):
-  try:
-    return _GetChannelFromFeatures(api_name, file_system, _API_FEATURES)
-  except FileNotFoundError:
-    # TODO(epeterson) Remove except block once _api_features is in all channels.
-    return None
 
-def _GetChannelFromPermissionFeatures(api_name, file_system):
-  return _GetChannelFromFeatures(api_name, file_system, _PERMISSION_FEATURES)
+def _GetChannelFromApiFeatures(api_name, file_system):
+  return _GetChannelFromFeatures(
+      api_name,
+      file_system,
+      '%s/_api_features.json' % svn_constants.API_PATH)
+
 
 def _GetChannelFromManifestFeatures(api_name, file_system):
-  return _GetChannelFromFeatures(#_manifest_features uses unix_style API names
-                                 model.UnixName(api_name),
-                                 file_system,
-                                 _MANIFEST_FEATURES)
+  return _GetChannelFromFeatures(
+      UnixName(api_name), #_manifest_features uses unix_style API names
+      file_system,
+      '%s/_manifest_features.json' % svn_constants.API_PATH)
 
-def _ExistsInFileSystem(api_name, file_system):
-  '''Checks for existence of |api_name| within the list of files in the api/
-  directory found using the given file system.
-  '''
-  file_names = file_system.GetFromFileListing(svn_constants.API_PATH)
-  # File names switch from unix_hacker_style to camelCase at versions <= 20.
-  return model.UnixName(api_name) in file_names or api_name in file_names
+
+def _GetChannelFromPermissionFeatures(api_name, file_system):
+  return _GetChannelFromFeatures(
+      api_name,
+      file_system,
+      '%s/_permission_features.json' % svn_constants.API_PATH)
+
 
 def _ExistsInExtensionApi(api_name, file_system):
   '''Parses the api/extension_api.json file (available in Chrome versions
@@ -70,195 +58,135 @@
   is considered to have been 'stable' for the given version.
   '''
   try:
-    extension_api_json = file_system.GetFromFile(_EXTENSION_API)
+    extension_api_json = file_system.GetFromFile(
+        '%s/extension_api.json' % svn_constants.API_PATH)
     api_rows = [row.get('namespace') for row in extension_api_json
                 if 'namespace' in row]
-    return True if api_name in api_rows else False
+    return api_name in api_rows
   except FileNotFoundError:
     # This should only happen on preview.py since extension_api.json is no
     # longer present in trunk.
     return False
 
-class AvailabilityFinder(object):
-  '''Uses API data sources generated by a ChromeVersionDataSource in order to
-  search the filesystem for the earliest existence of a specified API throughout
-  the different versions of Chrome; this constitutes an API's availability.
-  '''
-  class Factory(object):
-    def __init__(self,
-                 object_store_creator,
-                 compiled_host_fs_factory,
-                 branch_utility,
-                 host_file_system_creator):
-      self._object_store_creator = object_store_creator
-      self._compiled_host_fs_factory = compiled_host_fs_factory
-      self._branch_utility = branch_utility
-      self._host_file_system_creator = host_file_system_creator
 
-    def Create(self):
-      return AvailabilityFinder(self._object_store_creator,
-                                self._compiled_host_fs_factory,
-                                self._branch_utility,
-                                self._host_file_system_creator)
+class AvailabilityFinder(object):
+  '''Generates availability information for APIs by looking at API schemas and
+  _features files over multiple release versions of Chrome.
+  '''
 
   def __init__(self,
+               file_system_iterator,
                object_store_creator,
-               compiled_host_fs_factory,
-               branch_utility,
-               host_file_system_creator):
+               branch_utility):
+    self._file_system_iterator = file_system_iterator
     self._object_store_creator = object_store_creator
-    self._json_cache = compiled_host_fs_factory.Create(
-        lambda _, json: json_parse.Parse(json),
-        AvailabilityFinder,
-        'json-cache')
+    self._object_store = self._object_store_creator.Create(AvailabilityFinder)
     self._branch_utility = branch_utility
-    self._host_file_system_creator = host_file_system_creator
-    self._object_store = object_store_creator.Create(AvailabilityFinder)
 
-  @memoize
-  def _CreateFeaturesAndNamesFileSystems(self, version):
-    '''The 'features' compiled file system's populate function parses and
-    returns the contents of a _features.json file. The 'names' compiled file
-    system's populate function creates a list of file names with .json or .idl
-    extensions.
+  def _ExistsInFileSystem(self, api_name, file_system):
+    '''Checks for existence of |api_name| within the list of api files in the
+    api/ directory found using the given |file_system|.
     '''
-    fs_factory = CompiledFileSystem.Factory(
-        self._host_file_system_creator.Create(
-            self._branch_utility.GetBranchForVersion(version)),
-        self._object_store_creator)
+    file_names = file_system.ReadSingle('%s/' % svn_constants.API_PATH)
+    api_names = tuple(os.path.splitext(name)[0] for name in file_names
+                      if os.path.splitext(name)[1][1:] in ['json', 'idl'])
+
+    # API file names in api/ are unix_name at every version except for versions
+    # 18, 19, and 20. Since unix_name is the more common format, check it first.
+    return (UnixName(api_name) in api_names) or (api_name in api_names)
+
+  def _CheckStableAvailability(self, api_name, file_system, version):
+    '''Checks for availability of an API, |api_name|, on the stable channel.
+    Considers several _features.json files, file system existence, and
+    extension_api.json depending on the given |version|.
+    '''
+    if version < 5:
+      # SVN data isn't available below version 5.
+      return False
+    available_channel = None
+    fs_factory = CompiledFileSystem.Factory(file_system,
+                                            self._object_store_creator)
     features_fs = fs_factory.Create(lambda _, json: json_parse.Parse(json),
                                     AvailabilityFinder,
                                     category='features')
-    names_fs = fs_factory.Create(self._GetExtNames,
-                                 AvailabilityFinder,
-                                 category='names')
-    return (features_fs, names_fs)
+    if version >= 28:
+      # The _api_features.json file first appears in version 28 and should be
+      # the most reliable for finding API availability.
+      available_channel = _GetChannelFromApiFeatures(api_name, features_fs)
+    if version >= 20:
+      # The _permission_features.json and _manifest_features.json files are
+      # present in Chrome 20 and onwards. Use these if no information could be
+      # found using _api_features.json.
+      available_channel = available_channel or (
+          _GetChannelFromPermissionFeatures(api_name, features_fs)
+          or _GetChannelFromManifestFeatures(api_name, features_fs))
+      if available_channel is not None:
+        return available_channel == 'stable'
+    if version >= 18:
+      # Fall back to a check for file system existence if the API is not
+      # stable in any of the _features.json files, OR if we're dealing with
+      # version 18 or 19, which don't contain relevant _features information.
+      return self._ExistsInFileSystem(api_name, file_system)
+    if version >= 5:
+      # Versions 17 down to 5 have an extension_api.json file which
+      # contains namespaces for each API that was available at the time.
+      return _ExistsInExtensionApi(api_name, features_fs)
 
-  def _GetExtNames(self, base_path, apis):
-    return [os.path.splitext(api)[0] for api in apis
-            if os.path.splitext(api)[1][1:] in ['json', 'idl']]
-
-  def _FindEarliestStableAvailability(self, api_name, version):
-    '''Searches in descending order through filesystem caches tied to specific
-    chrome version numbers and looks for the availability of an API, |api_name|,
-    on the stable channel. When a version is found where the API is no longer
-    available on stable, returns the previous version number (the last known
-    version where the API was stable).
+  def _CheckChannelAvailability(self, api_name, file_system, channel_name):
+    '''Searches through the _features files in a given |file_system| and
+    determines whether or not an API is available on the given channel,
+    |channel_name|.
     '''
-    available = True
-    while available:
-      if version < 5:
-        # SVN data isn't available below version 5.
-        return version + 1
-      available = False
-      available_channel = None
-      features_fs, names_fs = self._CreateFeaturesAndNamesFileSystems(version)
-      if version >= 28:
-        # The _api_features.json file first appears in version 28 and should be
-        # the most reliable for finding API availabilities, so it gets checked
-        # first. The _permission_features.json and _manifest_features.json files
-        # are present in Chrome 20 and onwards. Fall back to a check for file
-        # system existence if the API is not stable in any of the _features.json
-        # files.
-        available_channel = _GetChannelFromApiFeatures(api_name, features_fs)
-      if version >= 20:
-        # Check other _features.json files/file existence if the API wasn't
-        # found in _api_features.json, or if _api_features.json wasn't present.
-        available_channel = available_channel or (
-            _GetChannelFromPermissionFeatures(api_name, features_fs)
-            or _GetChannelFromManifestFeatures(api_name, features_fs))
-        if available_channel is None:
-          available = _ExistsInFileSystem(api_name, names_fs)
-        else:
-          available = available_channel == _STABLE
-      elif version >= 18:
-        # These versions are a little troublesome. Version 19 has
-        # _permission_features.json, but it lacks 'channel' information.
-        # Version 18 lacks all of the _features.json files. For now, we're using
-        # a simple check for filesystem existence here.
-        available = _ExistsInFileSystem(api_name, names_fs)
-      elif version >= 5:
-        # Versions 17 and down to 5 have an extension_api.json file which
-        # contains namespaces for each API that was available at the time. We
-        # can use this file to check for API existence.
-        available = _ExistsInExtensionApi(api_name, features_fs)
-
-      if not available:
-        return version + 1
-      version -= 1
-
-  def _GetAvailableChannelForVersion(self, api_name, version):
-    '''Searches through the _features files for a given |version| and returns
-    the channel that the given API is determined to be available on.
-    '''
-    features_fs, names_fs = self._CreateFeaturesAndNamesFileSystems(version)
+    fs_factory = CompiledFileSystem.Factory(file_system,
+                                            self._object_store_creator)
+    features_fs = fs_factory.Create(lambda _, json: json_parse.Parse(json),
+                                    AvailabilityFinder,
+                                    category='features')
     available_channel = (_GetChannelFromApiFeatures(api_name, features_fs)
         or _GetChannelFromPermissionFeatures(api_name, features_fs)
         or _GetChannelFromManifestFeatures(api_name, features_fs))
-    if available_channel is None and _ExistsInFileSystem(api_name, names_fs):
+    if (available_channel is None and
+        self._ExistsInFileSystem(api_name, file_system)):
       # If an API is not represented in any of the _features files, but exists
       # in the filesystem, then assume it is available in this version.
       # The windows API is an example of this.
-      return self._branch_utility.GetChannelForVersion(version)
+      available_channel = channel_name
+    # If the channel we're checking is the same as or newer than the
+    # |available_channel| then the API is available at this channel.
+    return (available_channel is not None and
+            BranchUtility.NewestChannel((available_channel, channel_name))
+                == channel_name)
 
-    return available_channel
+  def _CheckApiAvailability(self, api_name, file_system, channel_info):
+    '''Determines the availability for an API at a certain version of Chrome.
+    Two branches of logic are used depending on whether or not the API is
+    determined to be 'stable' at the given version.
+    '''
+    if channel_info.channel == 'stable':
+      return self._CheckStableAvailability(api_name,
+                                           file_system,
+                                           channel_info.version)
+    return self._CheckChannelAvailability(api_name,
+                                          file_system,
+                                          channel_info.channel)
 
   def GetApiAvailability(self, api_name):
-    '''Determines the availability for an API by testing several scenarios.
-    (i.e. Is the API experimental? Only available on certain development
-    channels? If it's stable, when did it first become stable? etc.)
+    '''Performs a search for an API's top-level availability by using a
+    HostFileSystemIterator instance to traverse multiple version of the
+    SVN filesystem.
     '''
     availability = self._object_store.Get(api_name).Get()
     if availability is not None:
       return availability
 
-    # Check for a predetermined availability for this API.
-    api_info = self._json_cache.GetFromFile(_API_AVAILABILITIES).get(api_name)
-    if api_info is not None:
-      channel = api_info.get('channel')
-      if channel == _STABLE:
-        version = api_info.get('version')
-      else:
-        version = self._branch_utility.GetChannelInfo(channel).version
-      # The file data for predetermined availabilities is already cached, so
-      # skip caching this result.
-      return AvailabilityInfo(channel, version)
+    def check_api_availability(file_system, channel_info):
+      return self._CheckApiAvailability(api_name, file_system, channel_info)
 
-    # Check for the API in the development channels.
-    availability = None
-
-    for channel_info in self._branch_utility.GetAllChannelInfo():
-      if channel_info.channel == 'trunk':
-        # Don't check trunk, since (a) there's no point, we know it's going to
-        # be available there, and (b) there is a bug with the current
-        # architecture and design of HostFileSystemCreator, where creating
-        # 'trunk' ignores the pinned revision (in fact, it bypasses every
-        # difference including whether the file system is patched).
-        # TODO(kalman): Fix HostFileSystemCreator and update this comment.
-        break
-
-      available_channel = self._GetAvailableChannelForVersion(
-          api_name,
-          channel_info.version)
-      # If the |available_channel| for the API is the same as, or older than,
-      # the channel we're checking, then the API is available on this channel.
-      if (available_channel is not None and
-          BranchUtility.NewestChannel((available_channel, channel_info.channel))
-              == channel_info.channel):
-        availability = AvailabilityInfo(channel_info.channel,
-                                        channel_info.version)
-        break
-
+    availability = self._file_system_iterator.Descending(
+        self._branch_utility.GetChannelInfo('dev'),
+        check_api_availability)
     if availability is None:
-      trunk_info = self._branch_utility.GetChannelInfo('trunk')
-      availability = AvailabilityInfo(trunk_info.channel, trunk_info.version)
-
-    # If the API is in stable, find the chrome version in which it became
-    # stable.
-    if availability.channel == _STABLE:
-      availability.version = self._FindEarliestStableAvailability(
-          api_name,
-          availability.version)
-
+      # The API wasn't available on 'dev', so it must be a 'trunk'-only API.
+      availability = self._branch_utility.GetChannelInfo('trunk')
     self._object_store.Set(api_name, availability)
     return availability
diff --git a/chrome/common/extensions/docs/server2/availability_finder_test.py b/chrome/common/extensions/docs/server2/availability_finder_test.py
index 25cfd14..83ab7c7 100755
--- a/chrome/common/extensions/docs/server2/availability_finder_test.py
+++ b/chrome/common/extensions/docs/server2/availability_finder_test.py
@@ -11,28 +11,34 @@
 from branch_utility import BranchUtility
 from compiled_file_system import CompiledFileSystem
 from fake_url_fetcher import FakeUrlFetcher
+from host_file_system_iterator import HostFileSystemIterator
 from object_store_creator import ObjectStoreCreator
 from test_file_system import TestFileSystem
 from test_data.canned_data import (CANNED_API_FILE_SYSTEM_DATA, CANNED_BRANCHES)
 
+
 class FakeHostFileSystemCreator(object):
+
   def Create(self, branch):
     return TestFileSystem(CANNED_API_FILE_SYSTEM_DATA[str(branch)])
 
+
 class AvailabilityFinderTest(unittest.TestCase):
+
   def setUp(self):
-    self._avail_finder_factory = AvailabilityFinder.Factory(
-        ObjectStoreCreator.ForTest(),
-        CompiledFileSystem.Factory(
-            TestFileSystem(CANNED_API_FILE_SYSTEM_DATA['trunk']),
-            ObjectStoreCreator.ForTest()),
-        BranchUtility(
-            os.path.join('branch_utility', 'first.json'),
-            os.path.join('branch_utility', 'second.json'),
-            FakeUrlFetcher(os.path.join(sys.path[0], 'test_data')),
-            ObjectStoreCreator.ForTest()),
-        FakeHostFileSystemCreator())
-    self._avail_finder = self._avail_finder_factory.Create()
+    branch_utility = BranchUtility(
+        os.path.join('branch_utility', 'first.json'),
+        os.path.join('branch_utility', 'second.json'),
+        FakeUrlFetcher(os.path.join(sys.path[0], 'test_data')),
+        ObjectStoreCreator.ForTest())
+    fake_host_file_system_creator = FakeHostFileSystemCreator()
+    file_system_iterator = HostFileSystemIterator(
+      fake_host_file_system_creator,
+      fake_host_file_system_creator.Create('trunk'),
+      branch_utility)
+    self._avail_finder = AvailabilityFinder(file_system_iterator,
+                                            ObjectStoreCreator.ForTest(),
+                                            branch_utility)
 
   def testGetApiAvailability(self):
     # Key: Using 'channel' (i.e. 'beta') to represent an availability listing
@@ -40,21 +46,6 @@
     # represent the development channel, or phase of development, where an API's
     # availability is being checked.
 
-    # Testing the predetermined APIs found in
-    # templates/json/api_availabilities.json.
-    self.assertEqual('stable',
-        self._avail_finder.GetApiAvailability('jsonAPI1').channel)
-    self.assertEqual(10,
-        self._avail_finder.GetApiAvailability('jsonAPI1').version)
-    self.assertEqual('trunk',
-        self._avail_finder.GetApiAvailability('jsonAPI2').channel)
-    self.assertEqual('trunk',
-        self._avail_finder.GetApiAvailability('jsonAPI2').version)
-    self.assertEqual('dev',
-        self._avail_finder.GetApiAvailability('jsonAPI3').channel)
-    self.assertEqual(28,
-        self._avail_finder.GetApiAvailability('jsonAPI3').version)
-
     # Testing whitelisted API
     self.assertEquals('beta',
         self._avail_finder.GetApiAvailability('declarativeWebRequest').channel)
diff --git a/chrome/common/extensions/docs/server2/branch_utility.py b/chrome/common/extensions/docs/server2/branch_utility.py
index d449263..eb1dffc 100644
--- a/chrome/common/extensions/docs/server2/branch_utility.py
+++ b/chrome/common/extensions/docs/server2/branch_utility.py
@@ -9,13 +9,33 @@
 from appengine_url_fetcher import AppEngineUrlFetcher
 import url_constants
 
+
 class ChannelInfo(object):
+  '''Represents a Chrome channel with three pieces of information. |channel| is
+  one of 'stable', 'beta', 'dev', or 'trunk'. |branch| and |version| correspond
+  with each other, and represent different releases of Chrome. Note that
+  |branch| and |version| can occasionally be the same for separate channels
+  (i.e. 'beta' and 'dev'), so all three fields are required to uniquely
+  identify a channel.
+  '''
+
   def __init__(self, channel, branch, version):
     self.channel = channel
     self.branch = branch
     self.version = version
 
+  def __eq__(self, other):
+    return self.__dict__ == other.__dict__
+
+  def __ne__(self, other):
+    return not (self == other)
+
+
 class BranchUtility(object):
+  '''Provides methods for working with Chrome channel, branch, and version
+  data served from OmahaProxy.
+  '''
+
   def __init__(self, fetch_url, history_url, fetcher, object_store_creator):
     self._fetcher = fetcher
     def create_object_store(category):
@@ -26,6 +46,13 @@
     self._history_result = self._fetcher.FetchAsync(history_url)
 
   @staticmethod
+  def Create(object_store_creator):
+    return BranchUtility(url_constants.OMAHA_PROXY_URL,
+                         url_constants.OMAHA_DEV_HISTORY,
+                         AppEngineUrlFetcher(),
+                         object_store_creator)
+
+  @staticmethod
   def GetAllChannelNames():
     return ('stable', 'beta', 'dev', 'trunk')
 
@@ -36,12 +63,30 @@
       if channel in channels:
         return channel
 
-  @staticmethod
-  def Create(object_store_creator):
-    return BranchUtility(url_constants.OMAHA_PROXY_URL,
-                         url_constants.OMAHA_DEV_HISTORY,
-                         AppEngineUrlFetcher(),
-                         object_store_creator)
+  def Newer(self, channel_info):
+    '''Given a ChannelInfo object, returns a new ChannelInfo object
+    representing the next most recent Chrome version/branch combination.
+    '''
+    if channel_info.channel == 'trunk':
+      return None
+    if channel_info.channel == 'stable':
+      stable_info = self.GetChannelInfo('stable')
+      if channel_info.version < stable_info.version:
+        return self.GetStableChannelInfo(channel_info.version + 1)
+    names = self.GetAllChannelNames()
+    return self.GetAllChannelInfo()[names.index(channel_info.channel) + 1]
+
+  def Older(self, channel_info):
+    '''Given a ChannelInfo object, returns a new ChannelInfo object
+    representing the previous Chrome version/branch combination.
+    '''
+    if channel_info.channel == 'stable':
+      if channel_info.version <= 5:
+        # BranchUtility can't access branch data from before Chrome version 5.
+        return None
+      return self.GetStableChannelInfo(channel_info.version - 1)
+    names = self.GetAllChannelNames()
+    return self.GetAllChannelInfo()[names.index(channel_info.channel) - 1]
 
   @staticmethod
   def SplitChannelNameFromPath(path):
@@ -57,16 +102,16 @@
       return (first, second)
     return (None, path)
 
-  def GetAllBranchNumbers(self):
-    return ((channel, self.GetChannelInfo(channel).branch)
+  def GetAllBranches(self):
+    return tuple((channel, self.GetChannelInfo(channel).branch)
             for channel in BranchUtility.GetAllChannelNames())
 
-  def GetAllVersionNumbers(self):
-    return (self.GetChannelInfo(channel).version
+  def GetAllVersions(self):
+    return tuple(self.GetChannelInfo(channel).version
             for channel in BranchUtility.GetAllChannelNames())
 
   def GetAllChannelInfo(self):
-    return (self.GetChannelInfo(channel)
+    return tuple(self.GetChannelInfo(channel)
             for channel in BranchUtility.GetAllChannelNames())
 
 
@@ -75,6 +120,12 @@
                        self._ExtractFromVersionJson(channel, 'branch'),
                        self._ExtractFromVersionJson(channel, 'version'))
 
+  def GetStableChannelInfo(self, version):
+    '''Given a |version| corresponding to a 'stable' version of Chrome, returns
+    a ChannelInfo object representing that version.
+    '''
+    return ChannelInfo('stable', self.GetBranchForVersion(version), version)
+
   def _ExtractFromVersionJson(self, channel_name, data_type):
     '''Returns the branch or version number for a channel name.
     '''
diff --git a/chrome/common/extensions/docs/server2/branch_utility_test.py b/chrome/common/extensions/docs/server2/branch_utility_test.py
index 95ee507..a9b76a8 100755
--- a/chrome/common/extensions/docs/server2/branch_utility_test.py
+++ b/chrome/common/extensions/docs/server2/branch_utility_test.py
@@ -7,11 +7,13 @@
 import sys
 import unittest
 
-from branch_utility import BranchUtility
+from branch_utility import BranchUtility, ChannelInfo
 from fake_url_fetcher import FakeUrlFetcher
 from object_store_creator import ObjectStoreCreator
 
+
 class BranchUtilityTest(unittest.TestCase):
+
   def setUp(self):
     self._branch_util = BranchUtility(
         os.path.join('branch_utility', 'first.json'),
@@ -63,31 +65,59 @@
     self.assertEquals('dev', self._branch_util.NewestChannel(('dev',)))
     self.assertEquals('trunk', self._branch_util.NewestChannel(('trunk',)))
 
+  def testNewer(self):
+    oldest_stable_info = ChannelInfo('stable', 963, 17)
+    older_stable_info = ChannelInfo('stable', 1025, 18)
+    old_stable_info = ChannelInfo('stable', 1084, 19)
+    sort_of_old_stable_info = ChannelInfo('stable', 1364, 25)
+    stable_info = ChannelInfo('stable', 1410, 26)
+    beta_info = ChannelInfo('beta', 1453, 27)
+    dev_info = ChannelInfo('dev', 1500, 28)
+    trunk_info = ChannelInfo('trunk', 'trunk', 'trunk')
+
+    self.assertEquals(older_stable_info,
+                      self._branch_util.Newer(oldest_stable_info))
+    self.assertEquals(old_stable_info,
+                      self._branch_util.Newer(older_stable_info))
+    self.assertEquals(stable_info,
+                      self._branch_util.Newer(sort_of_old_stable_info))
+    self.assertEquals(beta_info, self._branch_util.Newer(stable_info))
+    self.assertEquals(dev_info, self._branch_util.Newer(beta_info))
+    self.assertEquals(trunk_info, self._branch_util.Newer(dev_info))
+    # Test the upper limit.
+    self.assertEquals(None, self._branch_util.Newer(trunk_info))
+
+
+  def testOlder(self):
+    trunk_info = ChannelInfo('trunk', 'trunk', 'trunk')
+    dev_info = ChannelInfo('dev', 1500, 28)
+    beta_info = ChannelInfo('beta', 1453, 27)
+    stable_info = ChannelInfo('stable', 1410, 26)
+    old_stable_info = ChannelInfo('stable', 1364, 25)
+    older_stable_info = ChannelInfo('stable', 1312, 24)
+    oldest_stable_info = ChannelInfo('stable', 396, 5)
+
+    self.assertEquals(dev_info, self._branch_util.Older(trunk_info))
+    self.assertEquals(beta_info, self._branch_util.Older(dev_info))
+    self.assertEquals(stable_info, self._branch_util.Older(beta_info))
+    self.assertEquals(old_stable_info, self._branch_util.Older(stable_info))
+    self.assertEquals(older_stable_info,
+                      self._branch_util.Older(old_stable_info))
+    # Test the lower limit.
+    self.assertEquals(None, self._branch_util.Older(oldest_stable_info))
+
   def testGetChannelInfo(self):
-    self.assertEquals('trunk',
-      self._branch_util.GetChannelInfo('trunk').channel)
-    self.assertEquals('trunk',
-      self._branch_util.GetChannelInfo('trunk').branch)
-    self.assertEquals('trunk',
-      self._branch_util.GetChannelInfo('trunk').version)
-    self.assertEquals('dev',
-      self._branch_util.GetChannelInfo('dev').channel)
-    self.assertEquals(1500,
-      self._branch_util.GetChannelInfo('dev').branch)
-    self.assertEquals(28,
-      self._branch_util.GetChannelInfo('dev').version)
-    self.assertEquals('beta',
-      self._branch_util.GetChannelInfo('beta').channel)
-    self.assertEquals(1453,
-      self._branch_util.GetChannelInfo('beta').branch)
-    self.assertEquals(27,
-      self._branch_util.GetChannelInfo('beta').version)
-    self.assertEquals('stable',
-      self._branch_util.GetChannelInfo('stable').channel)
-    self.assertEquals(1410,
-      self._branch_util.GetChannelInfo('stable').branch)
-    self.assertEquals(26,
-      self._branch_util.GetChannelInfo('stable').version)
+    trunk_info = ChannelInfo('trunk', 'trunk', 'trunk')
+    self.assertEquals(trunk_info, self._branch_util.GetChannelInfo('trunk'))
+
+    dev_info = ChannelInfo('dev', 1500, 28)
+    self.assertEquals(dev_info, self._branch_util.GetChannelInfo('dev'))
+
+    beta_info = ChannelInfo('beta', 1453, 27)
+    self.assertEquals(beta_info, self._branch_util.GetChannelInfo('beta'))
+
+    stable_info = ChannelInfo('stable', 1410, 26)
+    self.assertEquals(stable_info, self._branch_util.GetChannelInfo('stable'))
 
   def testGetLatestVersionNumber(self):
     self.assertEquals(28, self._branch_util.GetLatestVersionNumber())
diff --git a/chrome/common/extensions/docs/server2/cron.yaml b/chrome/common/extensions/docs/server2/cron.yaml
index 2a5bc64..364a5ea 100644
--- a/chrome/common/extensions/docs/server2/cron.yaml
+++ b/chrome/common/extensions/docs/server2/cron.yaml
@@ -2,4 +2,4 @@
 - description: Repopulates all cached data.
   url: /_cron
   schedule: every 5 minutes
-  target: 2-27-0
+  target: 2-27-2
diff --git a/chrome/common/extensions/docs/server2/cron_servlet.py b/chrome/common/extensions/docs/server2/cron_servlet.py
index 3a6b325..66344ad 100644
--- a/chrome/common/extensions/docs/server2/cron_servlet.py
+++ b/chrome/common/extensions/docs/server2/cron_servlet.py
@@ -11,7 +11,7 @@
     GetAppVersion, IsDeadlineExceededError, IsDevServer, logservice)
 from branch_utility import BranchUtility
 from compiled_file_system import CompiledFileSystem
-from data_source_registry import DataSourceRegistry
+from data_source_registry import CreateDataSources
 from empty_dir_file_system import EmptyDirFileSystem
 from file_system_util import CreateURLsFromPaths
 from github_file_system import GithubFileSystem
@@ -193,8 +193,7 @@
           _cronlog.info(
               '%s: took %s seconds' % (title, time.time() - start_time))
 
-      for data_source in DataSourceRegistry.AsTemplateData(
-          server_instance).values():
+      for data_source in CreateDataSources(server_instance).values():
         run_cron(data_source)
 
       run_cron(server_instance.redirector)
diff --git a/chrome/common/extensions/docs/server2/cron_servlet_test.py b/chrome/common/extensions/docs/server2/cron_servlet_test.py
index dc60ab5..6188dbb 100755
--- a/chrome/common/extensions/docs/server2/cron_servlet_test.py
+++ b/chrome/common/extensions/docs/server2/cron_servlet_test.py
@@ -79,6 +79,9 @@
 
   def testSafeRevision(self):
     test_data = {
+      'api': {
+        '_manifest_features.json': '{}'
+      },
       'docs': {
         'examples': {
           'examples.txt': 'examples.txt contents'
@@ -99,6 +102,7 @@
             },
           },
           'json': {
+            'manifest.json': '{}',
             'strings.json': '{}'
           },
         }
diff --git a/chrome/common/extensions/docs/server2/data_source.py b/chrome/common/extensions/docs/server2/data_source.py
index 6ca3da7..3e1676e 100644
--- a/chrome/common/extensions/docs/server2/data_source.py
+++ b/chrome/common/extensions/docs/server2/data_source.py
@@ -2,16 +2,18 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+
 class DataSource(object):
-  '''A DataSource presents data to the template system.
+  '''
+  Defines an abstraction for all DataSources.
 
-  DataSources are created with a ServerInstance. Anything in the ServerInstance
-  can be used by the DataSource to create or modify data into a form more
-  appropriate for usage by the template system.
+  DataSources must have two public methods, get and Cron. A DataSource is
+  initialized with a ServerInstance. Anything in the ServerInstance can be used
+  by the DataSource.
 
-  DataSources may not share data with other DataSources. The output of a
-  DataSource is only useful to templates. Any data that must be shared, or is
-  useful in general, should be moved into ServerInstance.
+  DataSources are used to provide templates with access to data. DataSources may
+  not access other DataSources and any logic or data that is useful to other
+  DataSources must be moved to a different class.
   '''
   def Cron(self):
     '''Must cache all files needed by |get| to persist them. Called on a live
diff --git a/chrome/common/extensions/docs/server2/data_source_registry.py b/chrome/common/extensions/docs/server2/data_source_registry.py
index 5aafeb6..703fc22 100644
--- a/chrome/common/extensions/docs/server2/data_source_registry.py
+++ b/chrome/common/extensions/docs/server2/data_source_registry.py
@@ -2,19 +2,18 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+from manifest_data_source import ManifestDataSource
 from strings_data_source import StringsDataSource
 
-class DataSourceRegistry(object):
-  '''Collects all DataSources and creates a context for templates to be rendered
-  in. All DataSources must conform to the DataSource interface.
-  '''
-  @staticmethod
-  def AsTemplateData(server_instance):
-    '''Return a context that can be used to render templates.
-    '''
-    registry = {
-      'strings': StringsDataSource
-    }
+_all_data_sources = {
+  'manifest_source': ManifestDataSource,
+  'strings': StringsDataSource
+}
 
-    return dict(
-        (name, cls(server_instance)) for name, cls in registry.iteritems())
+def CreateDataSources(server_instance):
+  '''Yields tuples of a name and an instantiated DataSource. The name is the
+  string that templates use to access the DataSource. Each DataSource is
+  initialized with |server_instance|.
+  '''
+  return dict(
+    (name, cls(server_instance)) for name, cls in _all_data_sources.iteritems())
diff --git a/chrome/common/extensions/docs/server2/features_utility.py b/chrome/common/extensions/docs/server2/features_utility.py
index 8db2819..f962691 100644
--- a/chrome/common/extensions/docs/server2/features_utility.py
+++ b/chrome/common/extensions/docs/server2/features_utility.py
@@ -2,21 +2,26 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+'''
+Utility functions for working with the Feature abstraction. Features are grouped
+into a dictionary by name. Each Feature is guaranteed to have the following two
+keys:
+  name - a string, the name of the feature
+  platform - a list containing 'app' or 'extension', both, or neither.
+
+A Feature may have other keys from a _features.json file as well. Features with
+a whitelist are ignored as they are only useful to specific apps or extensions.
+'''
+
 from copy import deepcopy
 
 def Parse(features_json):
-  '''Standardize the raw features file json |features_json| into a more regular
-  format. Return a dictionary of features, keyed by name. Each feature is
-  guaranteed to have a 'name' attribute and a 'platforms' attribute that is a
-  list of platforms the feature is relevant to. Valid platforms are 'app' or
-  'extension'. Other optional keys may be present in each feature.
-
-  Features with a 'whitelist' are only relevant to apps or extensions on that
-  whitelist and are not included in the retured features dictionary.
+  '''Process JSON from a _features.json file, standardizing it into a dictionary
+  of Features.
   '''
   features = {}
 
-  for name, value in features_json.iteritems():
+  for name, value in deepcopy(features_json).iteritems():
     # Some feature names corrispond to a list; force a list down to a single
     # feature by removing entries that have a 'whitelist'.
     if isinstance(value, list):
@@ -43,7 +48,7 @@
   return features
 
 def Filtered(features, platform=None):
-  '''Create a new features dictionary from |features| that contains only items
+  '''Create a new Features dictionary from |features| that contains only items
   relevant to |platform|. Items retained are deepcopied. Returns new features
   dictionary.
   '''
@@ -59,7 +64,7 @@
   '''Merge |features| with an additional dictionary to create a new features
   dictionary. If a feature is common to both |features| and |other|, then it is
   merged using the standard dictionary update instead of being overwritten.
-  Returns the new features dictionary.
+  Returns the new Features dictionary.
   '''
   for key, value in other.iteritems():
     if key in features:
@@ -67,4 +72,10 @@
     else:
       features[key] = value
 
+    # Ensure the Feature schema is enforced for all added items.
+    if not 'name' in features[key]:
+      features[key]['name'] = key
+    if not 'platforms' in features[key]:
+      features[key]['platforms'] = []
+
   return features
diff --git a/chrome/common/extensions/docs/server2/features_utility_test.py b/chrome/common/extensions/docs/server2/features_utility_test.py
index 313e451..d844f90 100755
--- a/chrome/common/extensions/docs/server2/features_utility_test.py
+++ b/chrome/common/extensions/docs/server2/features_utility_test.py
@@ -101,7 +101,8 @@
         'platforms': ['extension']
       },
       'doc2': {
-        'name': 'doc2'
+        'name': 'doc2',
+        'platforms': []
       },
       'doc3': {
         'name': 'doc3',
diff --git a/chrome/common/extensions/docs/server2/host_file_system_iterator.py b/chrome/common/extensions/docs/server2/host_file_system_iterator.py
new file mode 100644
index 0000000..20c0741
--- /dev/null
+++ b/chrome/common/extensions/docs/server2/host_file_system_iterator.py
@@ -0,0 +1,55 @@
+# 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.
+
+from third_party.json_schema_compiler.memoize import memoize
+
+
+class HostFileSystemIterator(object):
+  '''Provides methods for iterating through host file systems, in both
+  ascending (oldest to newest version) and descending order.
+  '''
+
+  def __init__(self, file_system_creator, host_file_system, branch_utility):
+    self._file_system_creator = file_system_creator
+    self._host_file_system = host_file_system
+    self._branch_utility = branch_utility
+
+  @memoize
+  def _GetFileSystem(self, branch):
+    '''To avoid overwriting the persistent data store entry for the 'trunk'
+    host file system, hold on to a reference of this file system and return it
+    instead of creating a file system for 'trunk'.
+      Also of note: File systems are going to be iterated over multiple times
+    at each call of ForEach, but the data isn't going to change between calls.
+    Use |branch| to memoize the created file systems.
+    '''
+    if branch == 'trunk':
+      # Don't create a new file system for trunk, since there is a bug with the
+      # current architecture and design of HostFileSystemCreator, where
+      # creating 'trunk' ignores the pinned revision (in fact, it bypasses
+      # every difference including whether the file system is patched).
+      # TODO(kalman): Fix HostFileSystemCreator and update this comment.
+      return self._host_file_system
+    return self._file_system_creator.Create(branch)
+
+  def _ForEach(self, channel_info, callback, get_next):
+    '''Iterates through a sequence of file systems defined by |get_next| until
+    |callback| returns False, or until the end of the sequence of file systems
+    is reached. Returns the BranchUtility.ChannelInfo of the last file system
+    for which |callback| returned True.
+    '''
+    last_true = None
+    while channel_info is not None:
+      file_system = self._GetFileSystem(channel_info.branch)
+      if not callback(file_system, channel_info):
+        return last_true
+      last_true = channel_info
+      channel_info = get_next(channel_info)
+    return last_true
+
+  def Ascending(self, channel_info, callback):
+    return self._ForEach(channel_info, callback, self._branch_utility.Newer)
+
+  def Descending(self, channel_info, callback):
+    return self._ForEach(channel_info, callback, self._branch_utility.Older)
diff --git a/chrome/common/extensions/docs/server2/host_file_system_iterator_test.py b/chrome/common/extensions/docs/server2/host_file_system_iterator_test.py
new file mode 100755
index 0000000..22801c9
--- /dev/null
+++ b/chrome/common/extensions/docs/server2/host_file_system_iterator_test.py
@@ -0,0 +1,177 @@
+#!/usr/bin/env python
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from host_file_system_creator import HostFileSystemCreator
+from host_file_system_creator_test import ConstructorForTest
+from host_file_system_iterator import HostFileSystemIterator
+from object_store_creator import ObjectStoreCreator
+from test_branch_utility import TestBranchUtility
+
+
+def _GetIterationTracker(version):
+  '''Adds the ChannelInfo object from each iteration to a list, and signals the
+  loop to stop when |version| is reached.
+  '''
+  iterations = []
+  def callback(file_system, channel_info):
+    if channel_info.version == version:
+      return False
+    iterations.append(channel_info)
+    return True
+  return (iterations, callback)
+
+
+class HostFileSystemIteratorTest(unittest.TestCase):
+
+  def setUp(self):
+    host_file_system_creator = HostFileSystemCreator(
+        ObjectStoreCreator.ForTest(),
+        constructor_for_test=ConstructorForTest)
+    self._branch_utility = TestBranchUtility.CreateWithCannedData()
+    self._iterator = HostFileSystemIterator(
+        host_file_system_creator,
+        host_file_system_creator.Create('trunk'),
+        self._branch_utility)
+
+  def _GetStableChannelInfo(self,version):
+    return self._branch_utility.GetStableChannelInfo(version)
+
+  def _GetChannelInfo(self, channel_name):
+    return self._branch_utility.GetChannelInfo(channel_name)
+
+  def testAscending(self):
+    # Start at |stable| version 5, and move up towards |trunk|.
+    # Total: 25 file systems.
+    iterations, callback = _GetIterationTracker(0)
+    self.assertEqual(
+        self._iterator.Ascending(self._GetStableChannelInfo(5), callback),
+        self._GetChannelInfo('trunk'))
+    self.assertEqual(len(iterations), 25)
+
+    # Start at |stable| version 5, and move up towards |trunk|. The callback
+    # fails at |beta|, so the last successful callback was the latest version
+    # of |stable|. Total: 22 file systems.
+    iterations, callback = _GetIterationTracker(
+        self._GetChannelInfo('beta').version)
+    self.assertEqual(
+        self._iterator.Ascending(self._GetStableChannelInfo(5), callback),
+        self._GetChannelInfo('stable'))
+    self.assertEqual(len(iterations), 22)
+
+    # Start at |stable| version 5, and the callback fails immediately. Since
+    # no file systems are successfully processed, expect a return of None.
+    iterations, callback = _GetIterationTracker(5)
+    self.assertEqual(
+        self._iterator.Ascending(self._GetStableChannelInfo(5), callback),
+        None)
+    self.assertEqual([], iterations)
+
+    # Start at |stable| version 5, and the callback fails at version 6.
+    # The return should represent |stable| version 5.
+    iterations, callback = _GetIterationTracker(6)
+    self.assertEqual(
+        self._iterator.Ascending(self._GetStableChannelInfo(5), callback),
+        self._GetStableChannelInfo(5))
+    self.assertEqual([self._GetStableChannelInfo(5)], iterations)
+
+    # Start at the latest version of |stable|, and the callback fails at
+    # |trunk|. Total: 3 file systems.
+    iterations, callback = _GetIterationTracker('trunk')
+    self.assertEqual(
+        self._iterator.Ascending(self._GetChannelInfo('stable'), callback),
+        self._GetChannelInfo('dev'))
+    self.assertEqual([self._GetChannelInfo('stable'),
+                      self._GetChannelInfo('beta'),
+                      self._GetChannelInfo('dev')], iterations)
+
+    # Start at |stable| version 10, and the callback fails at |trunk|.
+    iterations, callback = _GetIterationTracker('trunk')
+    self.assertEqual(
+        self._iterator.Ascending(self._GetStableChannelInfo(10), callback),
+        self._GetChannelInfo('dev'))
+    self.assertEqual([self._GetStableChannelInfo(10),
+                      self._GetStableChannelInfo(11),
+                      self._GetStableChannelInfo(12),
+                      self._GetStableChannelInfo(13),
+                      self._GetStableChannelInfo(14),
+                      self._GetStableChannelInfo(15),
+                      self._GetStableChannelInfo(16),
+                      self._GetStableChannelInfo(17),
+                      self._GetStableChannelInfo(18),
+                      self._GetStableChannelInfo(19),
+                      self._GetStableChannelInfo(20),
+                      self._GetStableChannelInfo(21),
+                      self._GetStableChannelInfo(22),
+                      self._GetStableChannelInfo(23),
+                      self._GetStableChannelInfo(24),
+                      self._GetStableChannelInfo(25),
+                      self._GetChannelInfo('stable'),
+                      self._GetChannelInfo('beta'),
+                      self._GetChannelInfo('dev')], iterations)
+
+  def testDescending(self):
+    # Start at |trunk|, and the callback fails immediately. No file systems
+    # are successfully processed, so Descending() will return None.
+    iterations, callback = _GetIterationTracker('trunk')
+    self.assertEqual(
+        self._iterator.Descending(self._GetChannelInfo('trunk'), callback),
+        None)
+    self.assertEqual([], iterations)
+
+    # Start at |trunk|, and the callback fails at |dev|. Last good iteration
+    # should be |trunk|.
+    iterations, callback = _GetIterationTracker(
+        self._GetChannelInfo('dev').version)
+    self.assertEqual(
+        self._iterator.Descending(self._GetChannelInfo('trunk'), callback),
+        self._GetChannelInfo('trunk'))
+    self.assertEqual([self._GetChannelInfo('trunk')], iterations)
+
+    # Start at |trunk|, and then move from |dev| down to |stable| at version 5.
+    # Total: 25 file systems.
+    iterations, callback = _GetIterationTracker(0)
+    self.assertEqual(
+        self._iterator.Descending(self._GetChannelInfo('trunk'), callback),
+        self._GetStableChannelInfo(5))
+    self.assertEqual(len(iterations), 25)
+
+    # Start at the latest version of |stable|, and move down to |stable| at
+    # version 5. Total: 22 file systems.
+    iterations, callback = _GetIterationTracker(0)
+    self.assertEqual(
+        self._iterator.Descending(self._GetChannelInfo('stable'), callback),
+        self._GetStableChannelInfo(5))
+    self.assertEqual(len(iterations), 22)
+
+    # Start at |dev| and iterate down through |stable| versions. The callback
+    # fails at version 10. Total: 18 file systems.
+    iterations, callback = _GetIterationTracker(10)
+    self.assertEqual(
+        self._iterator.Descending(self._GetChannelInfo('dev'), callback),
+        self._GetStableChannelInfo(11))
+    self.assertEqual([self._GetChannelInfo('dev'),
+                      self._GetChannelInfo('beta'),
+                      self._GetStableChannelInfo(26),
+                      self._GetStableChannelInfo(25),
+                      self._GetStableChannelInfo(24),
+                      self._GetStableChannelInfo(23),
+                      self._GetStableChannelInfo(22),
+                      self._GetStableChannelInfo(21),
+                      self._GetStableChannelInfo(20),
+                      self._GetStableChannelInfo(19),
+                      self._GetStableChannelInfo(18),
+                      self._GetStableChannelInfo(17),
+                      self._GetStableChannelInfo(16),
+                      self._GetStableChannelInfo(15),
+                      self._GetStableChannelInfo(14),
+                      self._GetStableChannelInfo(13),
+                      self._GetStableChannelInfo(12),
+                      self._GetStableChannelInfo(11)], iterations)
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/chrome/common/extensions/docs/server2/manifest_data_source.py b/chrome/common/extensions/docs/server2/manifest_data_source.py
index 104fb12..6fac731 100644
--- a/chrome/common/extensions/docs/server2/manifest_data_source.py
+++ b/chrome/common/extensions/docs/server2/manifest_data_source.py
@@ -4,7 +4,9 @@
 
 import json
 
-import features_utility as features
+from data_source import DataSource
+import features_utility
+from manifest_features import CreateManifestFeatures, ConvertDottedKeysToNested
 from third_party.json_schema_compiler.json_parse import Parse
 
 def _ListifyAndSortDocs(features, app_name):
@@ -94,56 +96,16 @@
   annotate('required', features)
   return features
 
-def _RestructureChildren(features):
-  '''Features whose names are of the form 'parent.child' are moved to be part
-  of the 'parent' dictionary under the key 'children'. Names are changed to
-  the 'child' section of the original name. Applied recursively so that
-  children can have children.
-  '''
-  def add_child(features, parent, child_name, value):
-    value['name'] = child_name
-    if not 'children' in features[parent]:
-      features[parent]['children'] = {}
-    features[parent]['children'][child_name] = value
-
-  def insert_children(features):
-    for name in features.keys():
-      if '.' in name:
-        value = features.pop(name)
-        parent, child_name = name.split('.', 1)
-        add_child(features, parent, child_name, value)
-
-    for value in features.values():
-      if 'children' in value:
-        insert_children(value['children'])
-
-  insert_children(features)
-  return features
-
-class ManifestDataSource(object):
+class ManifestDataSource(DataSource):
   '''Provides access to the properties in manifest features.
   '''
-  def __init__(self,
-               compiled_fs_factory,
-               file_system,
-               manifest_path,
-               features_path):
-    self._manifest_path = manifest_path
-    self._features_path = features_path
-    self._file_system = file_system
-    self._cache = compiled_fs_factory.Create(
+  def __init__(self, server_instance):
+    self._manifest_path = server_instance.manifest_json_path
+    self._features_path = server_instance.manifest_features_path
+    self._file_system = server_instance.host_file_system
+    self._cache = server_instance.compiled_host_fs_factory.Create(
         self._CreateManifestData, ManifestDataSource)
 
-  def GetFeatures(self):
-    '''Returns a dictionary of the contents of |_features_path| merged with
-    |_manifest_path|.
-    '''
-    manifest_json = Parse(self._file_system.ReadSingle(self._manifest_path))
-    manifest_features = features.Parse(
-        Parse(self._file_system.ReadSingle(self._features_path)))
-
-    return features.MergedWith(manifest_features, manifest_json)
-
   def _CreateManifestData(self, _, content):
     '''Combine the contents of |_manifest_path| and |_features_path| and filter
     the results into lists specific to apps or extensions for templates. Marks
@@ -152,18 +114,22 @@
     def for_templates(manifest_features, platform):
       return _AddLevelAnnotations(
           _ListifyAndSortDocs(
-              _RestructureChildren(
-                  features.Filtered(manifest_features, platform)),
+              ConvertDottedKeysToNested(
+                  features_utility.Filtered(manifest_features, platform)),
               app_name=platform.capitalize()))
 
     manifest_json = Parse(self._file_system.ReadSingle(self._manifest_path))
-    manifest_features = features.MergedWith(
-        features.Parse(Parse(content)), manifest_json)
+    manifest_features = CreateManifestFeatures(
+        features_json=Parse(content), manifest_json=manifest_json)
 
     return {
       'apps': for_templates(manifest_features, 'app'),
       'extensions': for_templates(manifest_features, 'extension')
     }
 
+  def Cron(self):
+    self._cache.GetFromFile(self._features_path)
+    self._file_system.ReadSingle(self._manifest_path)
+
   def get(self, key):
     return self._cache.GetFromFile(self._features_path)[key]
diff --git a/chrome/common/extensions/docs/server2/manifest_data_source_test.py b/chrome/common/extensions/docs/server2/manifest_data_source_test.py
index 00e0c12..95ab8fd 100755
--- a/chrome/common/extensions/docs/server2/manifest_data_source_test.py
+++ b/chrome/common/extensions/docs/server2/manifest_data_source_test.py
@@ -7,7 +7,10 @@
 import json
 import unittest
 
+from compiled_file_system import CompiledFileSystem
 import manifest_data_source
+from object_store_creator import ObjectStoreCreator
+from test_file_system import TestFileSystem
 
 convert_and_annotate_docs = {
   'name': {
@@ -156,50 +159,6 @@
     manifest_data_source._AddLevelAnnotations(annotated)
     self.assertEqual(expected_docs, annotated)
 
-  def testRestructureChildren(self):
-    docs = {
-      'doc1.sub2': {
-        'name': 'doc1.sub2'
-      },
-      'doc1': {
-        'name': 'doc1'
-      },
-      'doc2': {
-        'name': 'doc2'
-      },
-      'doc1.sub1.subsub1': {
-        'name': 'doc1.sub1.subsub1'
-      },
-      'doc1.sub1': {
-        'name': 'doc1.sub1'
-      }
-    }
-
-    expected_docs = {
-      'doc1': {
-        'name': 'doc1',
-        'children': {
-          'sub1': {
-            'name': 'sub1',
-            'children': {
-              'subsub1': {
-                'name' :'subsub1'
-              }
-            }
-          },
-          'sub2': {
-            'name': 'sub2'
-          }
-        }
-      },
-      'doc2': {
-        'name': 'doc2'
-      }
-    }
-
-    self.assertEqual(
-        expected_docs, manifest_data_source._RestructureChildren(docs))
-
   def testExpandedExamples(self):
     docs = {
       'doc1': {
@@ -240,5 +199,65 @@
     self.assertEqual(
         expected_docs, manifest_data_source._ListifyAndSortDocs(docs, 'app'))
 
+  def testManifestDataSource(self):
+    file_system = TestFileSystem({
+      '_manifest_features.json': json.dumps({
+        'doc1': {
+          'extension_types': 'all'
+        },
+        'doc1.sub1': {
+          'extension_types': ['platform_app']
+        },
+        'doc2': {
+          'extension_types': ['extension']
+        }
+      }),
+      'manifest.json': json.dumps({
+        'doc1': {
+          'example': {},
+          'level': 'required'
+        },
+        'doc1.sub1': {
+          'annotations': ['important!'],
+          'level': 'recommended'
+        }
+      })
+    })
+
+    expected_app = [
+      {
+        'example': '{...}',
+        'has_example': True,
+        'level': 'required',
+        'name': 'doc1',
+        'platforms': ['app', 'extension'],
+        'children': [
+          {
+            'annotations': [
+              'Recommended',
+              'important!'
+            ],
+            'level': 'recommended',
+            'name': 'sub1',
+            'platforms': ['app'],
+            'is_last': True
+          }
+        ],
+        'is_last': True
+      }
+    ]
+
+    class FakeServerInstance(object):
+      def __init__(self):
+        self.host_file_system = file_system
+        self.compiled_host_fs_factory = CompiledFileSystem.Factory(
+            file_system, ObjectStoreCreator.ForTest())
+        self.manifest_json_path = 'manifest.json'
+        self.manifest_features_path = '_manifest_features.json'
+
+    mds = manifest_data_source.ManifestDataSource(FakeServerInstance())
+    self.maxDiff = None
+    self.assertEqual(expected_app, mds.get('apps'))
+
 if __name__ == '__main__':
   unittest.main()
diff --git a/chrome/common/extensions/docs/server2/manifest_features.py b/chrome/common/extensions/docs/server2/manifest_features.py
new file mode 100644
index 0000000..e535592
--- /dev/null
+++ b/chrome/common/extensions/docs/server2/manifest_features.py
@@ -0,0 +1,62 @@
+# 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.
+
+'''
+Provides a Manifest Feature abstraction, similar to but more strict than the
+Feature schema (see feature_utility.py).
+
+Each Manifest Feature has a 'level' in addition to the keys defined in a
+Feature. 'level' can be 'required', 'only_one', 'recommended', or 'optional',
+indicating how an app or extension should define a manifest property. If 'level'
+is missing, 'optional' is assumed.
+'''
+
+import features_utility
+
+def CreateManifestFeatures(features_json, manifest_json, filter_platform=None):
+  '''Create a manifest features dictionary by normalizing |features_json| and
+  merging it with |manifest_json|. If filter_platform is 'app' or 'extension'
+  then irrelevant features will be removed.
+  '''
+  assert filter_platform in ['app', 'extension', None]
+  manifest_features = features_utility.MergedWith(
+      features_utility.Parse(features_json), manifest_json)
+
+  if filter_platform:
+    manifest_features = features_utility.Filtered(
+        manifest_features, filter_platform)
+
+  return manifest_features
+
+
+def ConvertDottedKeysToNested(features):
+  '''Some Manifest Features are subordinate to others, such as app.background to
+  app. Subordinate Features can be moved inside the parent Feature under the key
+  'children'.
+
+  Modifies |features|, a Manifest Features dictionary, by moving subordinate
+  Features with names of the form 'parent.child' into the 'parent' Feature.
+  Child features are renamed to the 'child' section of their previous name.
+
+  Applied recursively so that children can be nested arbitrarily.
+  '''
+  def add_child(features, parent, child_name, value):
+    value['name'] = child_name
+    if not 'children' in features[parent]:
+      features[parent]['children'] = {}
+    features[parent]['children'][child_name] = value
+
+  def insert_children(features):
+    for name in features.keys():
+      if '.' in name:
+        value = features.pop(name)
+        parent, child_name = name.split('.', 1)
+        add_child(features, parent, child_name, value)
+
+    for value in features.values():
+      if 'children' in value:
+        insert_children(value['children'])
+
+  insert_children(features)
+  return features
diff --git a/chrome/common/extensions/docs/server2/manifest_features_test.py b/chrome/common/extensions/docs/server2/manifest_features_test.py
new file mode 100755
index 0000000..1d0074a
--- /dev/null
+++ b/chrome/common/extensions/docs/server2/manifest_features_test.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from manifest_features import CreateManifestFeatures, ConvertDottedKeysToNested
+
+class ManifestFeaturesTest(unittest.TestCase):
+  def testConvertDottedKeysToNested(self):
+    docs = {
+      'doc1.sub2': {
+        'name': 'doc1.sub2'
+      },
+      'doc1': {
+        'name': 'doc1'
+      },
+      'doc2': {
+        'name': 'doc2'
+      },
+      'doc1.sub1.subsub1': {
+        'name': 'doc1.sub1.subsub1'
+      },
+      'doc1.sub1': {
+        'name': 'doc1.sub1'
+      }
+    }
+
+    expected_docs = {
+      'doc1': {
+        'name': 'doc1',
+        'children': {
+          'sub1': {
+            'name': 'sub1',
+            'children': {
+              'subsub1': {
+                'name' :'subsub1'
+              }
+            }
+          },
+          'sub2': {
+            'name': 'sub2'
+          }
+        }
+      },
+      'doc2': {
+        'name': 'doc2'
+      }
+    }
+
+    self.assertEqual(expected_docs, ConvertDottedKeysToNested(docs))
+
+  def testCreateManifestFeatures(self):
+    features_json = {
+      'doc1': { 'extension_types': 'all' },
+      'doc2': { 'extension_types': ['extension', 'package_app'] }
+    }
+
+    manifest_json = {
+      'doc1': { 'example': {} },
+      'doc1.sub1': { 'example': [] }
+    }
+
+    expected = {
+      'doc1': {
+        'name': 'doc1',
+        'example': {},
+        'platforms': ['app', 'extension']
+      },
+      'doc1.sub1': {
+        'example': [],
+        'name': 'doc1.sub1',
+        'platforms': []
+      },
+      'doc2': {
+        'name': 'doc2',
+        'platforms': ['extension']
+      }
+    }
+
+    expected_filtered = {
+      'doc1': {
+        'example': {},
+        'name': 'doc1',
+        'platforms': ['app', 'extension']
+      }
+    }
+
+    self.assertEqual(expected, CreateManifestFeatures(
+        features_json=features_json,
+        manifest_json=manifest_json,
+        filter_platform=None))
+
+    self.assertEqual(expected_filtered, CreateManifestFeatures(
+        features_json=features_json,
+        manifest_json=manifest_json,
+        filter_platform='app'))
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/chrome/common/extensions/docs/server2/server_instance.py b/chrome/common/extensions/docs/server2/server_instance.py
index 80adefb..a2b936a 100644
--- a/chrome/common/extensions/docs/server2/server_instance.py
+++ b/chrome/common/extensions/docs/server2/server_instance.py
@@ -7,11 +7,11 @@
 from appengine_wrappers import IsDevServer
 from availability_finder import AvailabilityFinder
 from compiled_file_system import CompiledFileSystem
-from data_source_registry import DataSourceRegistry
+from data_source_registry import CreateDataSources
 from empty_dir_file_system import EmptyDirFileSystem
 from example_zipper import ExampleZipper
-from manifest_data_source import ManifestDataSource
 from host_file_system_creator import HostFileSystemCreator
+from host_file_system_iterator import HostFileSystemIterator
 from intro_data_source import IntroDataSource
 from object_store_creator import ObjectStoreCreator
 from path_canonicalizer import PathCanonicalizer
@@ -25,7 +25,9 @@
 from test_branch_utility import TestBranchUtility
 from test_object_store import TestObjectStore
 
+
 class ServerInstance(object):
+
   def __init__(self,
                object_store_creator,
                host_file_system,
@@ -44,11 +46,15 @@
 
     self.host_file_system_creator = host_file_system_creator
 
-    self.availability_finder_factory = AvailabilityFinder.Factory(
+    self.host_file_system_iterator = HostFileSystemIterator(
+        host_file_system_creator,
+        host_file_system,
+        branch_utility)
+
+    self.availability_finder = AvailabilityFinder(
+        self.host_file_system_iterator,
         object_store_creator,
-        self.compiled_host_fs_factory,
-        branch_utility,
-        host_file_system_creator)
+        branch_utility)
 
     self.api_list_data_source_factory = APIListDataSource.Factory(
         self.compiled_host_fs_factory,
@@ -59,7 +65,8 @@
     self.api_data_source_factory = APIDataSource.Factory(
         self.compiled_host_fs_factory,
         svn_constants.API_PATH,
-        self.availability_finder_factory)
+        self.availability_finder,
+        branch_utility)
 
     self.ref_resolver_factory = ReferenceResolver.Factory(
         self.api_data_source_factory,
@@ -97,12 +104,6 @@
         self.compiled_host_fs_factory,
         svn_constants.JSON_PATH)
 
-    self.manifest_data_source = ManifestDataSource(
-        self.compiled_host_fs_factory,
-        host_file_system,
-        '/'.join((svn_constants.JSON_PATH, 'manifest.json')),
-        '/'.join((svn_constants.API_PATH, '_manifest_features.json')))
-
     self.permissions_data_source = PermissionsDataSource(
         self.compiled_host_fs_factory,
         self.host_file_system,
@@ -123,6 +124,10 @@
         svn_constants.PUBLIC_TEMPLATE_PATH)
 
     self.strings_json_path = '/'.join((svn_constants.JSON_PATH, 'strings.json'))
+    self.manifest_json_path = '/'.join(
+        (svn_constants.JSON_PATH, 'manifest.json'))
+    self.manifest_features_path = '/'.join(
+        (svn_constants.API_PATH, '_manifest_features.json'))
 
     self.template_data_source_factory = TemplateDataSource.Factory(
         self.api_data_source_factory,
@@ -132,14 +137,13 @@
         self.sidenav_data_source_factory,
         self.compiled_host_fs_factory,
         self.ref_resolver_factory,
-        self.manifest_data_source,
         self.permissions_data_source,
         svn_constants.PUBLIC_TEMPLATE_PATH,
         svn_constants.PRIVATE_TEMPLATE_PATH,
         base_path,
         # TODO(jshumway): Remove this hack after data source registry
         # transition, ServerInstance should not know about DataSourceRegistry.
-        DataSourceRegistry.AsTemplateData(self))
+        CreateDataSources(self))
 
     self.api_data_source_factory.SetTemplateDataSource(
         self.template_data_source_factory)
diff --git a/chrome/common/extensions/docs/server2/template_data_source.py b/chrome/common/extensions/docs/server2/template_data_source.py
index 6faecc8..1bf9ad6 100644
--- a/chrome/common/extensions/docs/server2/template_data_source.py
+++ b/chrome/common/extensions/docs/server2/template_data_source.py
@@ -35,7 +35,6 @@
                  sidenav_data_source_factory,
                  compiled_fs_factory,
                  ref_resolver_factory,
-                 manifest_data_source,
                  permissions_data_source,
                  public_template_path,
                  private_template_path,
@@ -49,7 +48,6 @@
       self._cache = compiled_fs_factory.Create(self._CreateTemplate,
                                                TemplateDataSource)
       self._ref_resolver = ref_resolver_factory.Create()
-      self._manifest_data_source = manifest_data_source
       self._permissions_data_source = permissions_data_source
       self._public_template_path = public_template_path
       self._private_template_path = private_template_path
@@ -69,7 +67,6 @@
           self._samples_data_source_factory.Create(request),
           self._sidenav_data_source_factory.Create(path),
           self._cache,
-          self._manifest_data_source,
           self._permissions_data_source,
           self._public_template_path,
           self._private_template_path,
@@ -83,7 +80,6 @@
                samples_data_source,
                sidenav_data_source,
                cache,
-               manifest_data_source,
                permissions_data_source,
                public_template_path,
                private_template_path,
@@ -97,7 +93,6 @@
     self._cache = cache
     self._public_template_path = public_template_path
     self._private_template_path = private_template_path
-    self._manifest_data_source = manifest_data_source
     self._permissions_data_source = permissions_data_source
     self._base_path = base_path
     self._data_sources = data_sources
@@ -117,7 +112,6 @@
       'intros': self._intro_data_source,
       'sidenavs': self._sidenav_data_source,
       'partials': self,
-      'manifest_source': self._manifest_data_source,
       'permissions': self._permissions_data_source,
       'samples': self._samples_data_source,
       'apps_samples_url': url_constants.GITHUB_BASE,
diff --git a/chrome/common/extensions/docs/server2/template_data_source_test.py b/chrome/common/extensions/docs/server2/template_data_source_test.py
index 41cddab..a1deacd 100755
--- a/chrome/common/extensions/docs/server2/template_data_source_test.py
+++ b/chrome/common/extensions/docs/server2/template_data_source_test.py
@@ -11,17 +11,18 @@
 from api_data_source import APIDataSource
 from compiled_file_system import CompiledFileSystem
 from local_file_system import LocalFileSystem
-from manifest_data_source import ManifestDataSource
 from object_store_creator import ObjectStoreCreator
 from permissions_data_source import PermissionsDataSource
 from reference_resolver import ReferenceResolver
 from template_data_source import TemplateDataSource
-from test_file_system import TestFileSystem
+from test_branch_utility import TestBranchUtility
 from test_util import DisableLogging
-from third_party.handlebar import Handlebar
 from servlet import Request
+from third_party.handlebar import Handlebar
+
 
 class _FakeFactory(object):
+
   def __init__(self, input_dict=None):
     if input_dict is None:
       self._input_dict = {}
@@ -31,7 +32,9 @@
   def Create(self, *args, **optargs):
     return self._input_dict
 
+
 class TemplateDataSourceTest(unittest.TestCase):
+
   def setUp(self):
     self._base_path = os.path.join(sys.path[0],
                                    'test_data',
@@ -40,8 +43,6 @@
     self._fake_intro_data_source_factory = _FakeFactory()
     self._fake_samples_data_source_factory = _FakeFactory()
     self._fake_sidenav_data_source_factory = _FakeFactory()
-    self._manifest_data_source = ManifestDataSource(
-      _FakeFactory(), LocalFileSystem.Create(), '', '')
     self._permissions_data_source = PermissionsDataSource(
       _FakeFactory(), LocalFileSystem.Create(), '', '', '')
 
@@ -58,9 +59,11 @@
 
   def _CreateTemplateDataSource(self, compiled_fs_factory, api_data=None):
     if api_data is None:
-      api_data_factory = APIDataSource.Factory(compiled_fs_factory,
-                                               'fake_path',
-                                               _FakeFactory())
+      api_data_factory = APIDataSource.Factory(
+      compiled_fs_factory,
+      'fake_path',
+      _FakeFactory(),
+      TestBranchUtility.CreateWithCannedData())
     else:
       api_data_factory = _FakeFactory(api_data)
     reference_resolver_factory = ReferenceResolver.Factory(
@@ -80,7 +83,6 @@
         compiled_fs_factory,
         reference_resolver_factory,
         self._permissions_data_source,
-        self._manifest_data_source,
         '.',
         '.',
         '',
diff --git a/chrome/common/extensions/docs/server2/test_branch_utility.py b/chrome/common/extensions/docs/server2/test_branch_utility.py
index 5dec476..da91f49 100644
--- a/chrome/common/extensions/docs/server2/test_branch_utility.py
+++ b/chrome/common/extensions/docs/server2/test_branch_utility.py
@@ -5,15 +5,17 @@
 from branch_utility import BranchUtility, ChannelInfo
 from test_data.canned_data import (CANNED_BRANCHES, CANNED_CHANNELS)
 
+
 class TestBranchUtility(object):
   '''Mimics BranchUtility to return valid-ish data without needing omahaproxy
   data.
   '''
-  def __init__(self, branches, channels):
-    ''' Parameters: |branches| is a mapping of versions to branches, and
+
+  def __init__(self, versions, channels):
+    ''' Parameters: |version| is a mapping of versions to branches, and
     |channels| is a mapping of channels to versions.
     '''
-    self._branches = branches
+    self._versions = versions
     self._channels = channels
 
   @staticmethod
@@ -24,17 +26,42 @@
     return TestBranchUtility(CANNED_BRANCHES, CANNED_CHANNELS)
 
   def GetAllChannelInfo(self):
-    return [self.GetChannelInfo(channel)
-            for channel in BranchUtility.GetAllChannelNames()]
+    return tuple(self.GetChannelInfo(channel)
+            for channel in BranchUtility.GetAllChannelNames())
 
   def GetChannelInfo(self, channel):
     version = self._channels[channel]
     return ChannelInfo(channel, self.GetBranchForVersion(version), version)
 
+  def GetStableChannelInfo(self, version):
+    return ChannelInfo('stable', self.GetBranchForVersion(version), version)
+
   def GetBranchForVersion(self, version):
-    return self._branches[version]
+    return self._versions[version]
 
   def GetChannelForVersion(self, version):
+    if version <= self._channels['stable']:
+      return 'stable'
     for channel in self._channels.iterkeys():
       if self._channels[channel] == version:
         return channel
+
+  def Older(self, channel_info):
+    versions = self._versions.keys()
+    index = versions.index(channel_info.version)
+    if index == len(versions) - 1:
+      return None
+    version = versions[index + 1]
+    return ChannelInfo(self.GetChannelForVersion(version),
+                       self.GetBranchForVersion(version),
+                       version)
+
+  def Newer(self, channel_info):
+    versions = self._versions.keys()
+    index = versions.index(channel_info.version)
+    if not index:
+      return None
+    version = versions[index - 1]
+    return ChannelInfo(self.GetChannelForVersion(version),
+                       self.GetBranchForVersion(version),
+                       version)
diff --git a/chrome/common/extensions/docs/server2/test_data/canned_data.py b/chrome/common/extensions/docs/server2/test_data/canned_data.py
index 5301563..b3a0136 100644
--- a/chrome/common/extensions/docs/server2/test_data/canned_data.py
+++ b/chrome/common/extensions/docs/server2/test_data/canned_data.py
@@ -3,41 +3,45 @@
 # found in the LICENSE file.
 
 import json
+from third_party.json_schema_compiler.json_parse import OrderedDict
 
-CANNED_CHANNELS = {
-  'trunk': 'trunk',
-  'dev': 28,
-  'beta': 27,
-  'stable': 26
-}
 
-CANNED_BRANCHES = {
-  'trunk': 'trunk',
-  28: 1500,
-  27: 1453,
-  26: 1410,
-  25: 1364,
-  24: 1312,
-  23: 1271,
-  22: 1229,
-  21: 1180,
-  20: 1132,
-  19: 1084,
-  18: 1025,
-  17: 963,
-  16: 912,
-  15: 874,
-  14: 835,
-  13: 782,
-  12: 742,
-  11: 696,
-  10: 648,
-   9: 597,
-   8: 552,
-   7: 544,
-   6: 495,
-   5: 396
-}
+CANNED_CHANNELS = OrderedDict([
+  ('trunk', 'trunk'),
+  ('dev', 28),
+  ('beta', 27),
+  ('stable', 26)
+])
+
+
+CANNED_BRANCHES = OrderedDict([
+  ('trunk', 'trunk'),
+  (28, 1500),
+  (27, 1453),
+  (26, 1410),
+  (25, 1364),
+  (24, 1312),
+  (23, 1271),
+  (22, 1229),
+  (21, 1180),
+  (20, 1132),
+  (19, 1084),
+  (18, 1025),
+  (17, 963),
+  (16, 912),
+  (15, 874),
+  (14, 835),
+  (13, 782),
+  (12, 742),
+  (11, 696),
+  (10, 648),
+  ( 9, 597),
+  ( 8, 552),
+  ( 7, 544),
+  ( 6, 495),
+  ( 5, 396)
+])
+
 
 CANNED_TEST_FILE_SYSTEM_DATA = {
   'api': {
@@ -59,11 +63,20 @@
       },
       'json': {
         'api_availabilities.json': json.dumps({
-          'tester': {
-              'channel': 'stable',
-              'version': 42
-            }
-          }),
+          'trunk_api': {
+            'channel': 'trunk'
+          },
+          'dev_api': {
+            'channel': 'dev'
+          },
+          'beta_api': {
+            'channel': 'beta'
+          },
+          'stable_api': {
+            'channel': 'stable',
+            'version': 20
+          }
+        }),
         'intro_tables.json': json.dumps({
           'tester': {
             'Permissions': [
@@ -93,6 +106,7 @@
   }
 }
 
+
 CANNED_API_FILE_SYSTEM_DATA = {
   'trunk': {
     'api': {
@@ -105,12 +119,24 @@
         },
         'extension': {
           'channel': 'stable'
+        },
+        'systemInfo.cpu': {
+          'channel': 'stable'
+        },
+        'systemInfo.stuff': {
+          'channel': 'dev'
         }
       }),
       '_manifest_features.json': json.dumps({
         'history': {
           'channel': 'beta'
         },
+        'notifications': {
+          'channel': 'beta'
+        },
+        'page_action': {
+          'channel': 'stable'
+        },
         'runtime': {
           'channel': 'stable'
         },
@@ -119,6 +145,9 @@
         },
         'sync': {
           'channel': 'trunk'
+        },
+        'web_request': {
+          'channel': 'stable'
         }
       }),
       '_permission_features.json': json.dumps({
@@ -145,6 +174,9 @@
         'falseBetaAPI': {
           'channel': 'beta'
         },
+        'systemInfo.display': {
+          'channel': 'stable'
+        },
         'trunkAPI': {
           'channel': 'trunk'
         }
@@ -190,6 +222,9 @@
         'extension': {
           'channel': 'stable'
         },
+        'systemInfo.cpu': {
+          'channel': 'stable'
+        },
         'systemInfo.stuff': {
           'channel': 'dev'
         }
@@ -201,6 +236,9 @@
         'notifications': {
           'channel': 'beta'
         },
+        'page_action': {
+          'channel': 'stable'
+        },
         'runtime': {
           'channel': 'stable'
         },
@@ -209,6 +247,12 @@
         },
         'sync': {
           'channel': 'trunk'
+        },
+        'system_info_display': {
+          'channel': 'stable'
+        },
+        'web_request': {
+          'channel': 'stable'
         }
       }),
       '_permission_features.json': json.dumps({
@@ -263,11 +307,20 @@
         'notifications': {
           'channel': 'dev'
         },
+        'page_action': {
+          'channel': 'stable'
+        },
         'runtime': {
           'channel': 'stable'
         },
         'storage': {
           'channel': 'dev'
+        },
+        'system_info_display': {
+          'channel': 'stable'
+        },
+        'web_request': {
+          'channel': 'stable'
         }
       }),
       '_permission_features.json': json.dumps({
diff --git a/chrome/common/extensions/docs/templates/articles/manifest/externally_connectable.html b/chrome/common/extensions/docs/templates/articles/manifest/externally_connectable.html
index bb7033d..6eab3fe 100644
--- a/chrome/common/extensions/docs/templates/articles/manifest/externally_connectable.html
+++ b/chrome/common/extensions/docs/templates/articles/manifest/externally_connectable.html
@@ -8,20 +8,28 @@
 For a tutorial on message passing see <a href="../messaging.html#external">cross-extension and app messaging</a> and <a href="../messaging.html#external-webpage">sending messages from web pages</a>.
 </p>
 
+<h2 id="without-externally-connectable">Connecting without externally_connectable</h2>
+
+<p>
+If <code>externally_connectable</code> is not declared in your {{platform}}'s manifest, all extensions and apps can connect, but no webpages can connect. As a consequence, when updating your manifest to use <code>externally_connectable</code>, if <code>"ids": ["*"]</code> is not specified then other extensions and apps will lose the ability to connect to your {{platform}}. This may be an unintended consequence, so keep it in mind.
+</p>
+
 <h2 id="manifest">Sample manifest.json</h2>
 <pre>
 {
   "name": "My externally connectable {{platform}}",
   "externally_connectable": {
-    // Extension and app IDs:
+    // Extension and app IDs. If this field is not specified, no extensions or
+    // apps can connect.
     "ids": [
       "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
       "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
       ...
+      // Alternatively, to match all extensions and apps, specify only "*".
+      "*"
     ],
-    // Alternatively, for all extensions and apps:
-    "ids": ["*"],
-    // Match patterns for <em>web pages</em>, not content scripts:
+    // Match patterns for web pages. Does not affect content scripts. If this
+    // field is not specified, no webpages can connect.
     "matches": [
       "https://*.google.com",
       "*://*.chromium.org",
diff --git a/chrome/common/extensions/docs/templates/intros/commands.html b/chrome/common/extensions/docs/templates/intros/commands.html
index 10059c1..8720da7 100644
--- a/chrome/common/extensions/docs/templates/intros/commands.html
+++ b/chrome/common/extensions/docs/templates/intros/commands.html
@@ -44,9 +44,9 @@
     },
     "_execute_page_action": {
       "suggested_key": {
-        "default": "Ctrl+E"
+        "default": "Ctrl+E",
         "windows": "Alt+P",
-        "mac": "Option+P",
+        "mac": "Option+P"
       }
     }
   }</b>,
diff --git a/chrome/common/extensions/docs/templates/intros/notifications.html b/chrome/common/extensions/docs/templates/intros/notifications.html
index 88084d8..95a82b9 100644
--- a/chrome/common/extensions/docs/templates/intros/notifications.html
+++ b/chrome/common/extensions/docs/templates/intros/notifications.html
@@ -1,6 +1,6 @@
-<p class="warning">
-<b>Warning: </b>
-Currently this API only works on ChromeOS and Windows.
+<p class="note">
+<b>Note: </b>
+This API is currently available on ChromeOS, Windows, and Mac.
 </p>
 
 <h2 id="usage">Usage</h2>
diff --git a/chrome/common/extensions/docs/templates/json/api_availabilities.json b/chrome/common/extensions/docs/templates/json/api_availabilities.json
index 9dbc4d6..bc196b0 100644
--- a/chrome/common/extensions/docs/templates/json/api_availabilities.json
+++ b/chrome/common/extensions/docs/templates/json/api_availabilities.json
@@ -19,6 +19,10 @@
     "channel": "stable",
     "version": 21
   },
+  "mediaGalleries": {
+    "channel": "stable",
+    "version": 23
+  },
   "notifications": {
     "channel": "stable",
     "version": 28
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc
index 7ed252d..4a066c0 100644
--- a/chrome/common/extensions/extension.cc
+++ b/chrome/common/extensions/extension.cc
@@ -736,7 +736,8 @@
 
 bool Extension::LoadSharedFeatures(string16* error) {
   if (!LoadDescription(error) ||
-      !ManifestHandler::ParseExtension(this, error))
+      !ManifestHandler::ParseExtension(this, error) ||
+      !LoadShortName(error))
     return false;
 
   return true;
@@ -779,6 +780,23 @@
   return true;
 }
 
+bool Extension::LoadShortName(string16* error) {
+  if (manifest_->HasKey(keys::kShortName)) {
+    string16 localized_short_name;
+    if (!manifest_->GetString(keys::kShortName, &localized_short_name) ||
+        localized_short_name.empty()) {
+      *error = ASCIIToUTF16(errors::kInvalidShortName);
+      return false;
+    }
+
+    base::i18n::AdjustStringForLocaleDirection(&localized_short_name);
+    short_name_ = UTF16ToUTF8(localized_short_name);
+  } else {
+    short_name_ = name_;
+  }
+  return true;
+}
+
 ExtensionInfo::ExtensionInfo(const base::DictionaryValue* manifest,
                              const std::string& id,
                              const base::FilePath& path,
diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h
index 4d3b310..21cd609 100644
--- a/chrome/common/extensions/extension.h
+++ b/chrome/common/extensions/extension.h
@@ -274,6 +274,7 @@
   const base::Version* version() const { return version_.get(); }
   const std::string VersionString() const;
   const std::string& name() const { return name_; }
+  const std::string& short_name() const { return short_name_; }
   const std::string& non_localized_name() const { return non_localized_name_; }
   // Base64-encoded version of the key used to sign this extension.
   // In pseudocode, returns
@@ -367,6 +368,7 @@
   bool LoadSharedFeatures(string16* error);
   bool LoadDescription(string16* error);
   bool LoadManifestVersion(string16* error);
+  bool LoadShortName(string16* error);
 
   bool CheckMinimumChromeVersion(string16* error) const;
 
@@ -380,6 +382,12 @@
   // debug output.
   std::string non_localized_name_;
 
+  // A short version of the extension's name. This can be used as an alternative
+  // to the name where there is insufficient space to display the full name. If
+  // an extension has not explicitly specified a short name, the value of this
+  // member variable will be the full name rather than an empty string.
+  std::string short_name_;
+
   // The version of this extension's manifest. We increase the manifest
   // version when making breaking changes to the extension system.
   // Version 1 was the first manifest version (implied by a lack of a
diff --git a/chrome/common/extensions/extension_manifest_constants.cc b/chrome/common/extensions/extension_manifest_constants.cc
index 69d51e8..0967243 100644
--- a/chrome/common/extensions/extension_manifest_constants.cc
+++ b/chrome/common/extensions/extension_manifest_constants.cc
@@ -364,6 +364,8 @@
     "Invalid value for 'sandbox.content_security_policy'.";
 const char kInvalidScriptBadge[] =
     "Invalid value for 'script_badge'.";
+const char kInvalidShortName[] =
+    "Invalid value for 'short_name'.";
 const char kInvalidSignature[] =
     "Value 'signature' is missing or invalid.";
 const char kInvalidSpellcheck[] =
diff --git a/chrome/common/extensions/extension_manifest_constants.h b/chrome/common/extensions/extension_manifest_constants.h
index 1d92c71..bf38691 100644
--- a/chrome/common/extensions/extension_manifest_constants.h
+++ b/chrome/common/extensions/extension_manifest_constants.h
@@ -211,6 +211,7 @@
   extern const char kInvalidSandboxedPage[];
   extern const char kInvalidSandboxedPagesCSP[];
   extern const char kInvalidScriptBadge[];
+  extern const char kInvalidShortName[];
   extern const char kInvalidSignature[];
   extern const char kInvalidSpellcheck[];
   extern const char kInvalidSpellcheckDictionaryFormat[];
diff --git a/chrome/common/extensions/extension_messages.h b/chrome/common/extensions/extension_messages.h
index c2fd98d..8d3b3e1 100644
--- a/chrome/common/extensions/extension_messages.h
+++ b/chrome/common/extensions/extension_messages.h
@@ -117,6 +117,11 @@
 
   // Whether the request is coming from a <webview>.
   IPC_STRUCT_MEMBER(bool, is_web_view)
+
+  // Whether the caller is interested in the result value. Manifest-declared
+  // content scripts and executeScript() calls without a response callback
+  // are examples of when this will be false.
+  IPC_STRUCT_MEMBER(bool, wants_result)
 IPC_STRUCT_END()
 
 // Struct containing the data for external connections to extensions. Used to
diff --git a/chrome/common/extensions/feature_switch.cc b/chrome/common/extensions/feature_switch.cc
index 860c782..a082b1a 100644
--- a/chrome/common/extensions/feature_switch.cc
+++ b/chrome/common/extensions/feature_switch.cc
@@ -9,6 +9,7 @@
 #include "base/metrics/field_trial.h"
 #include "base/strings/string_util.h"
 #include "chrome/common/chrome_switches.h"
+#include "extensions/common/switches.h"
 
 namespace extensions {
 
@@ -18,26 +19,30 @@
  public:
   CommonSwitches()
       : easy_off_store_install(
-            switches::kEasyOffStoreExtensionInstall,
+            ::switches::kEasyOffStoreExtensionInstall,
             FeatureSwitch::DEFAULT_DISABLED),
         script_badges(
-            switches::kScriptBadges,
+            ::switches::kScriptBadges,
             FeatureSwitch::DEFAULT_DISABLED),
         script_bubble(
-            switches::kScriptBubble,
+            ::switches::kScriptBubble,
             FeatureSwitch::DEFAULT_DISABLED),
         prompt_for_external_extensions(
-            switches::kPromptForExternalExtensions,
+            ::switches::kPromptForExternalExtensions,
 #if defined(OS_WIN)
-            FeatureSwitch::DEFAULT_ENABLED) {}
+            FeatureSwitch::DEFAULT_ENABLED),
 #else
-            FeatureSwitch::DEFAULT_DISABLED) {}
+            FeatureSwitch::DEFAULT_DISABLED),
 #endif
+        error_console(
+            switches::kErrorConsole,
+            FeatureSwitch::DEFAULT_DISABLED) {}
 
   FeatureSwitch easy_off_store_install;
   FeatureSwitch script_badges;
   FeatureSwitch script_bubble;
   FeatureSwitch prompt_for_external_extensions;
+  FeatureSwitch error_console;
 };
 
 base::LazyInstance<CommonSwitches> g_common_switches =
@@ -57,6 +62,9 @@
 FeatureSwitch* FeatureSwitch::prompt_for_external_extensions() {
   return &g_common_switches.Get().prompt_for_external_extensions;
 }
+FeatureSwitch* FeatureSwitch::error_console() {
+  return &g_common_switches.Get().error_console;
+}
 
 FeatureSwitch::ScopedOverride::ScopedOverride(FeatureSwitch* feature,
                                               bool override_value)
diff --git a/chrome/common/extensions/feature_switch.h b/chrome/common/extensions/feature_switch.h
index acb0465..cb73682 100644
--- a/chrome/common/extensions/feature_switch.h
+++ b/chrome/common/extensions/feature_switch.h
@@ -21,7 +21,7 @@
   static FeatureSwitch* script_badges();
   static FeatureSwitch* script_bubble();
   static FeatureSwitch* prompt_for_external_extensions();
-  static FeatureSwitch* tab_capture();
+  static FeatureSwitch* error_console();
 
   enum DefaultValue {
     DEFAULT_ENABLED,
diff --git a/chrome/common/extensions/features/base_feature_provider_unittest.cc b/chrome/common/extensions/features/base_feature_provider_unittest.cc
index 30f6abb..d31d3de 100644
--- a/chrome/common/extensions/features/base_feature_provider_unittest.cc
+++ b/chrome/common/extensions/features/base_feature_provider_unittest.cc
@@ -146,15 +146,15 @@
 TEST(BaseFeatureProviderTest, ComplexFeatures) {
   scoped_ptr<base::DictionaryValue> rule(
       DictionaryBuilder()
-      .Set("feature1",
-           ListBuilder().Append(DictionaryBuilder()
-                                .Set("channel", "beta")
-                                .Set("extension_types",
-                                     ListBuilder().Append("extension")))
-                        .Append(DictionaryBuilder()
-                                .Set("channel", "beta")
-                                .Set("extension_types",
-                                     ListBuilder().Append("packaged_app"))))
+      .Set("feature1", ListBuilder()
+          .Append(DictionaryBuilder()
+              .Set("channel", "beta")
+              .Set("extension_types", ListBuilder()
+                  .Append("extension")))
+                  .Append(DictionaryBuilder()
+                      .Set("channel", "beta")
+                      .Set("extension_types", ListBuilder()
+                          .Append("legacy_packaged_app"))))
       .Build());
 
   scoped_ptr<BaseFeatureProvider> provider(
diff --git a/chrome/common/extensions/features/complex_feature_unittest.cc b/chrome/common/extensions/features/complex_feature_unittest.cc
index 6e7ed65..51f26f6 100644
--- a/chrome/common/extensions/features/complex_feature_unittest.cc
+++ b/chrome/common/extensions/features/complex_feature_unittest.cc
@@ -41,15 +41,17 @@
   scoped_ptr<base::DictionaryValue> rule(
       DictionaryBuilder()
       .Set("whitelist", ListBuilder().Append(kIdFoo))
-      .Set("extension_types", ListBuilder().Append("extension")).Build());
+      .Set("extension_types", ListBuilder()
+          .Append("extension")).Build());
   simple_feature->Parse(rule.get());
   features->push_back(simple_feature.release());
 
-  // Rule: "packaged_app", whitelist "bar".
+  // Rule: "legacy_packaged_app", whitelist "bar".
   simple_feature.reset(new SimpleFeature());
   rule = DictionaryBuilder()
       .Set("whitelist", ListBuilder().Append(kIdBar))
-      .Set("extension_types", ListBuilder().Append("packaged_app")).Build();
+      .Set("extension_types", ListBuilder()
+          .Append("legacy_packaged_app")).Build();
   simple_feature->Parse(rule.get());
   features->push_back(simple_feature.release());
 
@@ -98,11 +100,12 @@
   simple_feature->Parse(rule.get());
   features->push_back(simple_feature.release());
 
-  // Rule: "packaged_app", channel stable.
+  // Rule: "legacy_packaged_app", channel stable.
   simple_feature.reset(new SimpleFeature());
   rule = DictionaryBuilder()
       .Set("channel", "stable")
-      .Set("extension_types", ListBuilder().Append("packaged_app")).Build();
+      .Set("extension_types", ListBuilder()
+          .Append("legacy_packaged_app")).Build();
   simple_feature->Parse(rule.get());
   features->push_back(simple_feature.release());
 
diff --git a/chrome/common/extensions/features/simple_feature.cc b/chrome/common/extensions/features/simple_feature.cc
index e8ee3d5..81f50c9 100644
--- a/chrome/common/extensions/features/simple_feature.cc
+++ b/chrome/common/extensions/features/simple_feature.cc
@@ -26,7 +26,7 @@
   Mappings() {
     extension_types["extension"] = Manifest::TYPE_EXTENSION;
     extension_types["theme"] = Manifest::TYPE_THEME;
-    extension_types["packaged_app"] = Manifest::TYPE_LEGACY_PACKAGED_APP;
+    extension_types["legacy_packaged_app"] = Manifest::TYPE_LEGACY_PACKAGED_APP;
     extension_types["hosted_app"] = Manifest::TYPE_HOSTED_APP;
     extension_types["platform_app"] = Manifest::TYPE_PLATFORM_APP;
     extension_types["shared_module"] = Manifest::TYPE_SHARED_MODULE;
diff --git a/chrome/common/extensions/features/simple_feature_unittest.cc b/chrome/common/extensions/features/simple_feature_unittest.cc
index fc7967f..4129d6f 100644
--- a/chrome/common/extensions/features/simple_feature_unittest.cc
+++ b/chrome/common/extensions/features/simple_feature_unittest.cc
@@ -376,7 +376,7 @@
   base::ListValue* extension_types = new base::ListValue();
   extension_types->Append(new base::StringValue("extension"));
   extension_types->Append(new base::StringValue("theme"));
-  extension_types->Append(new base::StringValue("packaged_app"));
+  extension_types->Append(new base::StringValue("legacy_packaged_app"));
   extension_types->Append(new base::StringValue("hosted_app"));
   extension_types->Append(new base::StringValue("platform_app"));
   extension_types->Append(new base::StringValue("shared_module"));
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_initvalue_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_initvalue_unittest.cc
index 92bdf4b..4b50af1 100644
--- a/chrome/common/extensions/manifest_tests/extension_manifests_initvalue_unittest.cc
+++ b/chrome/common/extensions/manifest_tests/extension_manifests_initvalue_unittest.cc
@@ -71,6 +71,10 @@
              errors::kInvalidMinimumChromeVersion),
     Testcase("init_invalid_chrome_version_too_low.json",
              errors::kChromeVersionTooLow),
+    Testcase("init_invalid_short_name_empty.json",
+             errors::kInvalidShortName),
+    Testcase("init_invalid_short_name_type.json",
+             errors::kInvalidShortName),
   };
 
   RunTestcases(testcases, arraysize(testcases),
@@ -88,6 +92,7 @@
   EXPECT_TRUE(Extension::IdIsValid(extension->id()));
   EXPECT_EQ("1.0.0.0", extension->VersionString());
   EXPECT_EQ("my extension", extension->name());
+  EXPECT_EQ(extension->name(), extension->short_name());
   EXPECT_EQ(extension->id(), extension->url().host());
   EXPECT_EQ(extension->path(), path);
   EXPECT_EQ(path, extension->path());
@@ -104,6 +109,11 @@
   EXPECT_EQ("/options.html",
             ManifestURL::GetOptionsPage(extension.get()).path());
 
+  // Test optional short_name field.
+  extension = LoadAndExpectSuccess("init_valid_short_name.json");
+  EXPECT_EQ("a very descriptive extension name", extension->name());
+  EXPECT_EQ("concise name", extension->short_name());
+
   Testcase testcases[] = {
     // Test that an empty list of page actions does not stop a browser action
     // from being loaded.
diff --git a/chrome/common/extensions/permissions/api_permission.h b/chrome/common/extensions/permissions/api_permission.h
index a92cab9..3bae6c8 100644
--- a/chrome/common/extensions/permissions/api_permission.h
+++ b/chrome/common/extensions/permissions/api_permission.h
@@ -79,6 +79,7 @@
     kFileBrowserHandlerInternal,
     kFileBrowserPrivate,
     kFileSystem,
+    kFileSystemDirectory,
     kFileSystemRetainEntries,
     kFileSystemWrite,
     kFontSettings,
diff --git a/chrome/common/extensions/permissions/chrome_api_permissions.cc b/chrome/common/extensions/permissions/chrome_api_permissions.cc
index 53cf6b2..9b9347e 100644
--- a/chrome/common/extensions/permissions/chrome_api_permissions.cc
+++ b/chrome/common/extensions/permissions/chrome_api_permissions.cc
@@ -268,11 +268,16 @@
       APIPermissionInfo::kFlagNone,
       IDS_EXTENSION_PROMPT_WARNING_VIDEO_CAPTURE,
       PermissionMessage::kVideoCapture },
-    // The permission string for "fileSystem" is only shown when "write" is
-    // present. Read-only access is only granted after the user has been shown
-    // a file chooser dialog and selected a file. Selecting the file is
-    // considered consent to read it.
+    // The permission string for "fileSystem" is only shown when "write" or
+    // "directory" is present. Read-only access is only granted after the user
+    // has been shown a file or directory  chooser dialog and selected a file or
+    // directory . Selecting the file or directory  is considered consent to
+    // read it.
     { APIPermission::kFileSystem, "fileSystem" },
+    { APIPermission::kFileSystemDirectory, "fileSystem.directory",
+      APIPermissionInfo::kFlagNone,
+      IDS_EXTENSION_PROMPT_WARNING_FILE_SYSTEM_DIRECTORY,
+      PermissionMessage::kFileSystemDirectory },
     { APIPermission::kFileSystemRetainEntries, "fileSystem.retainEntries" },
     { APIPermission::kFileSystemWrite, "fileSystem.write",
       APIPermissionInfo::kFlagNone,
diff --git a/chrome/common/extensions/permissions/permission_message.cc b/chrome/common/extensions/permissions/permission_message.cc
index 7840b57..4523725 100644
--- a/chrome/common/extensions/permissions/permission_message.cc
+++ b/chrome/common/extensions/permissions/permission_message.cc
@@ -45,7 +45,7 @@
     default:
       message_id = kHosts4OrMore;
 
-#if defined(TOOLKIT_VIEWS)
+#if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX)
     message = l10n_util::GetStringFUTF16(
         IDS_EXTENSION_PROMPT_WARNING_HOSTS,
         base::IntToString16(host_list.size()));
diff --git a/chrome/common/extensions/permissions/permission_message.h b/chrome/common/extensions/permissions/permission_message.h
index dd35286..5118315 100644
--- a/chrome/common/extensions/permissions/permission_message.h
+++ b/chrome/common/extensions/permissions/permission_message.h
@@ -70,6 +70,7 @@
     kDownloadsOpen,
     kNetworkingPrivate,
     kDeclarativeWebRequest,
+    kFileSystemDirectory,
     kEnumBoundary,
   };
   COMPILE_ASSERT(PermissionMessage::kNone > PermissionMessage::kUnknown,
diff --git a/chrome/common/extensions/permissions/permissions_data_unittest.cc b/chrome/common/extensions/permissions/permissions_data_unittest.cc
index 0127ce6..3b8b857 100644
--- a/chrome/common/extensions/permissions/permissions_data_unittest.cc
+++ b/chrome/common/extensions/permissions/permissions_data_unittest.cc
@@ -175,7 +175,7 @@
       PermissionsData::GetPermissionMessageDetailsStrings(extension.get());
   ASSERT_EQ(1u, warnings.size());
   ASSERT_EQ(1u, warnings_details.size());
-#if defined(TOOLKIT_VIEWS)
+#if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX)
   EXPECT_EQ("Access your data on 5 website(s)", UTF16ToUTF8(warnings[0]));
   EXPECT_EQ("- www.a.com\n- www.b.com\n- www.c.com\n- www.d.com\n- www.e.com",
             UTF16ToUTF8(warnings_details[0]));
diff --git a/chrome/common/instant_types.cc b/chrome/common/instant_types.cc
index 9fa7149..ea744018 100644
--- a/chrome/common/instant_types.cc
+++ b/chrome/common/instant_types.cc
@@ -4,35 +4,18 @@
 
 #include "chrome/common/instant_types.h"
 
-InstantSuggestion::InstantSuggestion()
-    : behavior(INSTANT_COMPLETE_NOW),
-      type(INSTANT_SUGGESTION_SEARCH),
-      autocomplete_match_index(kNoMatchIndex) {
+InstantSuggestion::InstantSuggestion() {
 }
 
 InstantSuggestion::InstantSuggestion(const string16& in_text,
-                                     InstantCompleteBehavior in_behavior,
-                                     InstantSuggestionType in_type,
-                                     const string16& in_query,
-                                     size_t in_autocomplete_match_index)
+                                     const std::string& in_metadata)
     : text(in_text),
-      behavior(in_behavior),
-      type(in_type),
-      query(in_query),
-      autocomplete_match_index(in_autocomplete_match_index) {
+      metadata(in_metadata) {
 }
 
 InstantSuggestion::~InstantSuggestion() {
 }
 
-InstantAutocompleteResult::InstantAutocompleteResult()
-    : transition(content::PAGE_TRANSITION_LINK),
-      relevance(0) {
-}
-
-InstantAutocompleteResult::~InstantAutocompleteResult() {
-}
-
 RGBAColor::RGBAColor()
     : r(0),
       g(0),
@@ -69,7 +52,6 @@
 ThemeBackgroundInfo::~ThemeBackgroundInfo() {
 }
 
-
 bool ThemeBackgroundInfo::operator==(const ThemeBackgroundInfo& rhs) const {
   return using_default_theme == rhs.using_default_theme &&
       background_color == rhs.background_color &&
diff --git a/chrome/common/instant_types.h b/chrome/common/instant_types.h
index c88e1a9..8ef078b 100644
--- a/chrome/common/instant_types.h
+++ b/chrome/common/instant_types.h
@@ -18,59 +18,19 @@
 // Visited items) that the Instant page needs access to.
 typedef int InstantRestrictedID;
 
-const size_t kNoMatchIndex = -1;
-
-// Ways that the Instant suggested text is autocompleted into the omnibox.
-enum InstantCompleteBehavior {
-  // Autocomplete the suggestion immediately.
-  INSTANT_COMPLETE_NOW,
-
-  // Do not autocomplete the suggestion. The suggestion may still be displayed
-  // in the omnibox, but not made a part of the omnibox text by default (e.g.,
-  // by displaying the suggestion as non-highlighted, non-selected gray text).
-  INSTANT_COMPLETE_NEVER,
-
-  // Treat the suggested text as the entire omnibox text, effectively replacing
-  // whatever the user has typed.
-  INSTANT_COMPLETE_REPLACE,
-};
-
-// The type of suggestion provided by Instant. For example, if Instant suggests
-// "yahoo.com", should that be considered a search string or a URL?
-enum InstantSuggestionType {
-  INSTANT_SUGGESTION_SEARCH,
-  INSTANT_SUGGESTION_URL,
-};
-
-// A wrapper to hold Instant suggested text and its metadata.
+// A wrapper to hold Instant suggested text and its metadata. Used to tell the
+// server what suggestion to prefetch.
 struct InstantSuggestion {
   InstantSuggestion();
-  InstantSuggestion(const string16& text,
-                    InstantCompleteBehavior behavior,
-                    InstantSuggestionType type,
-                    const string16& query,
-                    size_t autocomplete_match_index);
+  InstantSuggestion(const string16& in_text,
+                    const std::string& in_metadata);
   ~InstantSuggestion();
 
   // Full suggested text.
   string16 text;
 
-  // Completion behavior for the suggestion.
-  InstantCompleteBehavior behavior;
-
-  // Is this a search or a URL suggestion?
-  InstantSuggestionType type;
-
-  // Query for which this suggestion was generated. May be set to empty string
-  // if unknown.
-  string16 query;
-
-  // Index of the AutocompleteMatch in AutocompleteResult. Used to get the
-  // metadata details of the suggested text from AutocompleteResult. Set to a
-  // positive value if the suggestion is displayed on the Local NTP and
-  // set to kNoMatchIndex if the suggestion is displayed on the
-  // Instant NTP.
-  size_t autocomplete_match_index;
+  // JSON metadata from the server response which produced this suggestion.
+  std::string metadata;
 };
 
 // Omnibox dropdown matches provided by the native autocomplete providers.
diff --git a/chrome/common/local_discovery/local_discovery_messages.h b/chrome/common/local_discovery/local_discovery_messages.h
index b98756e..4c75766 100644
--- a/chrome/common/local_discovery/local_discovery_messages.h
+++ b/chrome/common/local_discovery/local_discovery_messages.h
@@ -80,8 +80,9 @@
     local_discovery::ServiceDescription /* description */)
 
 // Notifies browser process about local domain resolution results.
-IPC_MESSAGE_CONTROL3(
+IPC_MESSAGE_CONTROL4(
     LocalDiscoveryHostMsg_LocalDomainResolverCallback,
     uint64 /* id */,
     bool /* success */,
-    net::IPAddressNumber /* ip_address */)
+    net::IPAddressNumber /* ip_address_ipv4 */,
+    net::IPAddressNumber /* ip_address_ipv6 */)
diff --git a/chrome/common/local_discovery/service_discovery_client.h b/chrome/common/local_discovery/service_discovery_client.h
index 30dcbd2..94f5a9b 100644
--- a/chrome/common/local_discovery/service_discovery_client.h
+++ b/chrome/common/local_discovery/service_discovery_client.h
@@ -94,7 +94,9 @@
 
 class LocalDomainResolver {
  public:
-  typedef base::Callback<void(bool, const net::IPAddressNumber&)>
+  typedef base::Callback<void(bool /*success*/,
+                              const net::IPAddressNumber& /*address_ipv4*/,
+                              const net::IPAddressNumber& /*address_ipv6*/)>
       IPAddressCallback;
 
   virtual ~LocalDomainResolver() {}
diff --git a/chrome/common/localized_error.cc b/chrome/common/localized_error.cc
index 149d81a..893e379 100644
--- a/chrome/common/localized_error.cc
+++ b/chrome/common/localized_error.cc
@@ -475,6 +475,18 @@
   return standard_menu_items_text;
 }
 
+// Gets the icon class for a given |error_domain| and |error_code|.
+const char* GetIconClassForError(const std::string& error_domain,
+                                 int error_code) {
+  if ((error_code == net::ERR_INTERNET_DISCONNECTED &&
+       error_domain == net::kErrorDomain) ||
+      (error_code == chrome_common_net::DNS_PROBE_FINISHED_NO_INTERNET &&
+       error_domain == chrome_common_net::kDnsProbeErrorDomain))
+    return "icon-offline";
+
+  return "icon-generic";
+}
+
 }  // namespace
 
 const char LocalizedError::kHttpErrorDomain[] = "http";
@@ -529,10 +541,7 @@
   error_strings->SetString("heading",
       l10n_util::GetStringUTF16(options.heading_resource_id));
 
-  std::string icon_class = (error_code == net::ERR_INTERNET_DISCONNECTED &&
-                            error_domain == net::kErrorDomain)
-                               ? "icon-offline"
-                               : "icon-generic";
+  std::string icon_class = GetIconClassForError(error_domain, error_code);
   error_strings->SetString("iconClass", icon_class);
 
   base::DictionaryValue* summary = new base::DictionaryValue;
diff --git a/chrome/common/mac/app_mode_common.h b/chrome/common/mac/app_mode_common.h
index d397c4b..de82e24 100644
--- a/chrome/common/mac/app_mode_common.h
+++ b/chrome/common/mac/app_mode_common.h
@@ -29,9 +29,10 @@
 // Special app mode id used for the App Launcher.
 extern const char kAppListModeId[];
 
-// Instructs the app shim to send LaunchApp with launch_now = false. This
-// associates the shim without launching the app.
-extern const char kNoLaunchApp[];
+// The process ID of the Chrome process that launched the app shim.
+// The presence of this switch instructs the app shim to send LaunchApp with
+// launch_now = false. This associates the shim without launching the app.
+extern const char kLaunchedByChromeProcessId[];
 
 // The display name of the bundle as shown in Finder and the Dock. For localized
 // bundles, this overrides the bundle's file name.
diff --git a/chrome/common/mac/app_mode_common.mm b/chrome/common/mac/app_mode_common.mm
index f67eb2f..317dec3 100644
--- a/chrome/common/mac/app_mode_common.mm
+++ b/chrome/common/mac/app_mode_common.mm
@@ -10,7 +10,7 @@
 
 const char kAppListModeId[] = "app_list";
 
-const char kNoLaunchApp[] = "no-launch-app";
+const char kLaunchedByChromeProcessId[] = "launched-by-chrome-process-id";
 
 NSString* const kCFBundleDisplayNameKey = @"CFBundleDisplayName";
 NSString* const kLSHasLocalizedDisplayNameKey = @"LSHasLocalizedDisplayName";
diff --git a/chrome/common/mac/objc_zombie.mm b/chrome/common/mac/objc_zombie.mm
index 8f64840..ab62cae 100644
--- a/chrome/common/mac/objc_zombie.mm
+++ b/chrome/common/mac/objc_zombie.mm
@@ -175,7 +175,7 @@
 // easy to use DCHECK to dump only in debug builds.
 BOOL DumpDeallocTrace(const void* const* array, int size) {
   fprintf(stderr, "Backtrace from -dealloc:\n");
-  base::debug::StackTrace(array, size).PrintBacktrace();
+  base::debug::StackTrace(array, size).Print();
 
   return YES;
 }
diff --git a/chrome/common/metrics/caching_permuted_entropy_provider.h b/chrome/common/metrics/caching_permuted_entropy_provider.h
index 6416d91..8592830 100644
--- a/chrome/common/metrics/caching_permuted_entropy_provider.h
+++ b/chrome/common/metrics/caching_permuted_entropy_provider.h
@@ -8,8 +8,8 @@
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
 #include "base/threading/thread_checker.h"
-#include "chrome/common/metrics/entropy_provider.h"
 #include "chrome/common/metrics/proto/permuted_entropy_cache.pb.h"
+#include "components/variations/entropy_provider.h"
 
 class PrefService;
 class PrefRegistrySimple;
diff --git a/chrome/common/metrics/entropy_provider.cc b/chrome/common/metrics/entropy_provider.cc
deleted file mode 100644
index 385f418..0000000
--- a/chrome/common/metrics/entropy_provider.cc
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/common/metrics/entropy_provider.h"
-
-#include <algorithm>
-#include <limits>
-#include <vector>
-
-#include "base/logging.h"
-#include "base/rand_util.h"
-#include "base/sha1.h"
-#include "base/sys_byteorder.h"
-#include "chrome/common/metrics/metrics_util.h"
-
-namespace metrics {
-
-namespace internal {
-
-SeededRandGenerator::SeededRandGenerator(uint32 seed) {
-  mersenne_twister_.init_genrand(seed);
-}
-
-SeededRandGenerator::~SeededRandGenerator() {
-}
-
-uint32 SeededRandGenerator::operator()(uint32 range) {
-  // Based on base::RandGenerator().
-  DCHECK_GT(range, 0u);
-
-  // We must discard random results above this number, as they would
-  // make the random generator non-uniform (consider e.g. if
-  // MAX_UINT64 was 7 and |range| was 5, then a result of 1 would be twice
-  // as likely as a result of 3 or 4).
-  uint32 max_acceptable_value =
-      (std::numeric_limits<uint32>::max() / range) * range - 1;
-
-  uint32 value;
-  do {
-    value = mersenne_twister_.genrand_int32();
-  } while (value > max_acceptable_value);
-
-  return value % range;
-}
-
-void PermuteMappingUsingRandomizationSeed(uint32 randomization_seed,
-                                          std::vector<uint16>* mapping) {
-  for (size_t i = 0; i < mapping->size(); ++i)
-    (*mapping)[i] = static_cast<uint16>(i);
-
-  SeededRandGenerator generator(randomization_seed);
-  std::random_shuffle(mapping->begin(), mapping->end(), generator);
-}
-
-}  // namespace internal
-
-SHA1EntropyProvider::SHA1EntropyProvider(const std::string& entropy_source)
-    : entropy_source_(entropy_source) {
-}
-
-SHA1EntropyProvider::~SHA1EntropyProvider() {
-}
-
-double SHA1EntropyProvider::GetEntropyForTrial(
-    const std::string& trial_name,
-    uint32 randomization_seed) const {
-  // Given enough input entropy, SHA-1 will produce a uniformly random spread
-  // in its output space. In this case, the input entropy that is used is the
-  // combination of the original |entropy_source_| and the |trial_name|.
-  //
-  // Note: If |entropy_source_| has very low entropy, such as 13 bits or less,
-  // it has been observed that this method does not result in a uniform
-  // distribution given the same |trial_name|. When using such a low entropy
-  // source, PermutedEntropyProvider should be used instead.
-  std::string input(entropy_source_ + trial_name);
-  unsigned char sha1_hash[base::kSHA1Length];
-  base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()),
-                      input.size(),
-                      sha1_hash);
-
-  uint64 bits;
-  COMPILE_ASSERT(sizeof(bits) < sizeof(sha1_hash), need_more_data);
-  memcpy(&bits, sha1_hash, sizeof(bits));
-  bits = base::ByteSwapToLE64(bits);
-
-  return base::BitsToOpenEndedUnitInterval(bits);
-}
-
-PermutedEntropyProvider::PermutedEntropyProvider(
-    uint16 low_entropy_source,
-    size_t low_entropy_source_max)
-    : low_entropy_source_(low_entropy_source),
-      low_entropy_source_max_(low_entropy_source_max) {
-  DCHECK_LT(low_entropy_source, low_entropy_source_max);
-  DCHECK_LE(low_entropy_source_max, std::numeric_limits<uint16>::max());
-}
-
-PermutedEntropyProvider::~PermutedEntropyProvider() {
-}
-
-double PermutedEntropyProvider::GetEntropyForTrial(
-    const std::string& trial_name,
-    uint32 randomization_seed) const {
-  if (randomization_seed == 0)
-    randomization_seed = HashName(trial_name);
-
-  return GetPermutedValue(randomization_seed) /
-         static_cast<double>(low_entropy_source_max_);
-}
-
-uint16 PermutedEntropyProvider::GetPermutedValue(
-    uint32 randomization_seed) const {
-  std::vector<uint16> mapping(low_entropy_source_max_);
-  internal::PermuteMappingUsingRandomizationSeed(randomization_seed, &mapping);
-  return mapping[low_entropy_source_];
-}
-
-}  // namespace metrics
diff --git a/chrome/common/metrics/entropy_provider.h b/chrome/common/metrics/entropy_provider.h
deleted file mode 100644
index 494c689..0000000
--- a/chrome/common/metrics/entropy_provider.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_COMMON_METRICS_ENTROPY_PROVIDER_H_
-#define CHROME_COMMON_METRICS_ENTROPY_PROVIDER_H_
-
-#include <functional>
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "base/metrics/field_trial.h"
-#include "third_party/mt19937ar/mt19937ar.h"
-
-namespace metrics {
-
-// Internals of entropy_provider.cc exposed for testing.
-namespace internal {
-
-// A functor that generates random numbers based on a seed, using the Mersenne
-// Twister algorithm. Suitable for use with std::random_shuffle().
-struct SeededRandGenerator : std::unary_function<uint32, uint32> {
-  explicit SeededRandGenerator(uint32 seed);
-  ~SeededRandGenerator();
-
-  // Returns a random number in range [0, range).
-  uint32 operator()(uint32 range);
-
-  MersenneTwister mersenne_twister_;
-};
-
-// Fills |mapping| to create a bijection of values in the range of
-// [0, |mapping.size()|), permuted based on |randomization_seed|.
-void PermuteMappingUsingRandomizationSeed(uint32 randomization_seed,
-                                          std::vector<uint16>* mapping);
-
-}  // namespace internal
-
-// SHA1EntropyProvider is an entropy provider suitable for high entropy
-// sources. It works by taking the first 64 bits of the SHA1 hash of the
-// entropy source concatenated with the trial name and using that for the
-// final entropy value.
-class SHA1EntropyProvider : public base::FieldTrial::EntropyProvider {
- public:
-  // Creates a SHA1EntropyProvider with the given |entropy_source|, which
-  // should contain a large amount of entropy - for example, a textual
-  // representation of a persistent randomly-generated 128-bit value.
-  explicit SHA1EntropyProvider(const std::string& entropy_source);
-  virtual ~SHA1EntropyProvider();
-
-  // base::FieldTrial::EntropyProvider implementation:
-  virtual double GetEntropyForTrial(const std::string& trial_name,
-                                    uint32 randomization_seed) const OVERRIDE;
-
- private:
-  std::string entropy_source_;
-
-  DISALLOW_COPY_AND_ASSIGN(SHA1EntropyProvider);
-};
-
-// PermutedEntropyProvider is an entropy provider suitable for low entropy
-// sources (below 16 bits). It uses the field trial name to generate a
-// permutation of a mapping array from an initial entropy value to a new value.
-// Note: This provider's performance is O(2^n), where n is the number of bits
-// in the entropy source.
-class PermutedEntropyProvider : public base::FieldTrial::EntropyProvider {
- public:
-  // Creates a PermutedEntropyProvider with the given |low_entropy_source|,
-  // which should have a value in the range of [0, low_entropy_source_max).
-  PermutedEntropyProvider(uint16 low_entropy_source,
-                          size_t low_entropy_source_max);
-  virtual ~PermutedEntropyProvider();
-
-  // base::FieldTrial::EntropyProvider implementation:
-  virtual double GetEntropyForTrial(const std::string& trial_name,
-                                    uint32 randomization_seed) const OVERRIDE;
-
- protected:
-  // Performs the permutation algorithm and returns the permuted value that
-  // corresponds to |low_entropy_source_|.
-  virtual uint16 GetPermutedValue(uint32 randomization_seed) const;
-
- private:
-  uint16 low_entropy_source_;
-  size_t low_entropy_source_max_;
-
-  DISALLOW_COPY_AND_ASSIGN(PermutedEntropyProvider);
-};
-
-}  // namespace metrics
-
-#endif  // CHROME_COMMON_METRICS_ENTROPY_PROVIDER_H_
diff --git a/chrome/common/metrics/entropy_provider_unittest.cc b/chrome/common/metrics/entropy_provider_unittest.cc
deleted file mode 100644
index 2503d00..0000000
--- a/chrome/common/metrics/entropy_provider_unittest.cc
+++ /dev/null
@@ -1,369 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/common/metrics/entropy_provider.h"
-
-#include <cmath>
-#include <limits>
-#include <numeric>
-
-#include "base/basictypes.h"
-#include "base/guid.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/rand_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "chrome/common/metrics/metrics_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace metrics {
-
-namespace {
-
-// Size of the low entropy source to use for the permuted entropy provider
-// in tests.
-const size_t kMaxLowEntropySize = 8000;
-
-// Field trial names used in unit tests.
-const char* const kTestTrialNames[] = { "TestTrial", "AnotherTestTrial",
-                                        "NewTabButton" };
-
-// Computes the Chi-Square statistic for |values| assuming they follow a uniform
-// distribution, where each entry has expected value |expected_value|.
-//
-// The Chi-Square statistic is defined as Sum((O-E)^2/E) where O is the observed
-// value and E is the expected value.
-double ComputeChiSquare(const std::vector<int>& values,
-                        double expected_value) {
-  double sum = 0;
-  for (size_t i = 0; i < values.size(); ++i) {
-    const double delta = values[i] - expected_value;
-    sum += (delta * delta) / expected_value;
-  }
-  return sum;
-}
-
-// Computes SHA1-based entropy for the given |trial_name| based on
-// |entropy_source|
-double GenerateSHA1Entropy(const std::string& entropy_source,
-                           const std::string& trial_name) {
-  SHA1EntropyProvider sha1_provider(entropy_source);
-  return sha1_provider.GetEntropyForTrial(trial_name, 0);
-}
-
-// Generates permutation-based entropy for the given |trial_name| based on
-// |entropy_source| which must be in the range [0, entropy_max).
-double GeneratePermutedEntropy(uint16 entropy_source,
-                               size_t entropy_max,
-                               const std::string& trial_name) {
-  PermutedEntropyProvider permuted_provider(entropy_source, entropy_max);
-  return permuted_provider.GetEntropyForTrial(trial_name, 0);
-}
-
-// Helper interface for testing used to generate entropy values for a given
-// field trial. Unlike EntropyProvider, which keeps the low/high entropy source
-// value constant and generates entropy for different trial names, instances
-// of TrialEntropyGenerator keep the trial name constant and generate low/high
-// entropy source values internally to produce each output entropy value.
-class TrialEntropyGenerator {
- public:
-  virtual ~TrialEntropyGenerator() {}
-  virtual double GenerateEntropyValue() const = 0;
-};
-
-// An TrialEntropyGenerator that uses the SHA1EntropyProvider with the high
-// entropy source (random GUID with 128 bits of entropy + 13 additional bits of
-// entropy corresponding to a low entropy source).
-class SHA1EntropyGenerator : public TrialEntropyGenerator {
- public:
-  explicit SHA1EntropyGenerator(const std::string& trial_name)
-      : trial_name_(trial_name) {
-  }
-
-  virtual ~SHA1EntropyGenerator() {
-  }
-
-  virtual double GenerateEntropyValue() const OVERRIDE {
-    // Use a random GUID + 13 additional bits of entropy to match how the
-    // SHA1EntropyProvider is used in metrics_service.cc.
-    const int low_entropy_source =
-        static_cast<uint16>(base::RandInt(0, kMaxLowEntropySize - 1));
-    const std::string high_entropy_source =
-        base::GenerateGUID() + base::IntToString(low_entropy_source);
-    return GenerateSHA1Entropy(high_entropy_source, trial_name_);
-  }
-
- private:
-  std::string trial_name_;
-
-  DISALLOW_COPY_AND_ASSIGN(SHA1EntropyGenerator);
-};
-
-// An TrialEntropyGenerator that uses the permuted entropy provider algorithm,
-// using 13-bit low entropy source values.
-class PermutedEntropyGenerator : public TrialEntropyGenerator {
- public:
-  explicit PermutedEntropyGenerator(const std::string& trial_name)
-      : mapping_(kMaxLowEntropySize) {
-    // Note: Given a trial name, the computed mapping will be the same.
-    // As a performance optimization, pre-compute the mapping once per trial
-    // name and index into it for each entropy value.
-    const uint32 randomization_seed = HashName(trial_name);
-    internal::PermuteMappingUsingRandomizationSeed(randomization_seed,
-                                                   &mapping_);
-  }
-
-  virtual ~PermutedEntropyGenerator() {
-  }
-
-  virtual double GenerateEntropyValue() const OVERRIDE {
-    const int low_entropy_source =
-        static_cast<uint16>(base::RandInt(0, kMaxLowEntropySize - 1));
-    return mapping_[low_entropy_source] /
-           static_cast<double>(kMaxLowEntropySize);
-  }
-
- private:
-  std::vector<uint16> mapping_;
-
-  DISALLOW_COPY_AND_ASSIGN(PermutedEntropyGenerator);
-};
-
-// Tests uniformity of a given |entropy_generator| using the Chi-Square Goodness
-// of Fit Test.
-void PerformEntropyUniformityTest(
-    const std::string& trial_name,
-    const TrialEntropyGenerator& entropy_generator) {
-  // Number of buckets in the simulated field trials.
-  const size_t kBucketCount = 20;
-  // Max number of iterations to perform before giving up and failing.
-  const size_t kMaxIterationCount = 100000;
-  // The number of iterations to perform before each time the statistical
-  // significance of the results is checked.
-  const size_t kCheckIterationCount = 10000;
-  // This is the Chi-Square threshold from the Chi-Square statistic table for
-  // 19 degrees of freedom (based on |kBucketCount|) with a 99.9% confidence
-  // level. See: http://www.medcalc.org/manual/chi-square-table.php
-  const double kChiSquareThreshold = 43.82;
-
-  std::vector<int> distribution(kBucketCount);
-
-  for (size_t i = 1; i <= kMaxIterationCount; ++i) {
-    const double entropy_value = entropy_generator.GenerateEntropyValue();
-    const size_t bucket = static_cast<size_t>(kBucketCount * entropy_value);
-    ASSERT_LT(bucket, kBucketCount);
-    distribution[bucket] += 1;
-
-    // After |kCheckIterationCount| iterations, compute the Chi-Square
-    // statistic of the distribution. If the resulting statistic is greater
-    // than |kChiSquareThreshold|, we can conclude with 99.9% confidence
-    // that the observed samples do not follow a uniform distribution.
-    //
-    // However, since 99.9% would still result in a false negative every
-    // 1000 runs of the test, do not treat it as a failure (else the test
-    // will be flaky). Instead, perform additional iterations to determine
-    // if the distribution will converge, up to |kMaxIterationCount|.
-    if ((i % kCheckIterationCount) == 0) {
-      const double expected_value_per_bucket =
-          static_cast<double>(i) / kBucketCount;
-      const double chi_square =
-          ComputeChiSquare(distribution, expected_value_per_bucket);
-      if (chi_square < kChiSquareThreshold)
-        break;
-
-      // If |i == kMaxIterationCount|, the Chi-Square statistic did not
-      // converge after |kMaxIterationCount|.
-      EXPECT_NE(i, kMaxIterationCount) << "Failed for trial " <<
-          trial_name << " with chi_square = " << chi_square <<
-          " after " << kMaxIterationCount << " iterations.";
-    }
-  }
-}
-
-}  // namespace
-
-TEST(EntropyProviderTest, UseOneTimeRandomizationSHA1) {
-  // Simply asserts that two trials using one-time randomization
-  // that have different names, normally generate different results.
-  //
-  // Note that depending on the one-time random initialization, they
-  // _might_ actually give the same result, but we know that given
-  // the particular client_id we use for unit tests they won't.
-  base::FieldTrialList field_trial_list(new SHA1EntropyProvider("client_id"));
-  const int kNoExpirationYear = base::FieldTrialList::kNoExpirationYear;
-  scoped_refptr<base::FieldTrial> trials[] = {
-      base::FieldTrialList::FactoryGetFieldTrial(
-          "one", 100, "default", kNoExpirationYear, 1, 1,
-          base::FieldTrial::ONE_TIME_RANDOMIZED, NULL),
-      base::FieldTrialList::FactoryGetFieldTrial(
-          "two", 100, "default", kNoExpirationYear, 1, 1,
-          base::FieldTrial::ONE_TIME_RANDOMIZED, NULL),
-  };
-
-  for (size_t i = 0; i < arraysize(trials); ++i) {
-    for (int j = 0; j < 100; ++j)
-      trials[i]->AppendGroup(std::string(), 1);
-  }
-
-  // The trials are most likely to give different results since they have
-  // different names.
-  EXPECT_NE(trials[0]->group(), trials[1]->group());
-  EXPECT_NE(trials[0]->group_name(), trials[1]->group_name());
-}
-
-TEST(EntropyProviderTest, UseOneTimeRandomizationPermuted) {
-  // Simply asserts that two trials using one-time randomization
-  // that have different names, normally generate different results.
-  //
-  // Note that depending on the one-time random initialization, they
-  // _might_ actually give the same result, but we know that given
-  // the particular client_id we use for unit tests they won't.
-  base::FieldTrialList field_trial_list(
-      new PermutedEntropyProvider(1234, kMaxLowEntropySize));
-  const int kNoExpirationYear = base::FieldTrialList::kNoExpirationYear;
-  scoped_refptr<base::FieldTrial> trials[] = {
-      base::FieldTrialList::FactoryGetFieldTrial(
-          "one", 100, "default", kNoExpirationYear, 1, 1,
-          base::FieldTrial::ONE_TIME_RANDOMIZED, NULL),
-      base::FieldTrialList::FactoryGetFieldTrial(
-          "two", 100, "default", kNoExpirationYear, 1, 1,
-          base::FieldTrial::ONE_TIME_RANDOMIZED, NULL),
-  };
-
-  for (size_t i = 0; i < arraysize(trials); ++i) {
-    for (int j = 0; j < 100; ++j)
-      trials[i]->AppendGroup(std::string(), 1);
-  }
-
-  // The trials are most likely to give different results since they have
-  // different names.
-  EXPECT_NE(trials[0]->group(), trials[1]->group());
-  EXPECT_NE(trials[0]->group_name(), trials[1]->group_name());
-}
-
-TEST(EntropyProviderTest, UseOneTimeRandomizationWithCustomSeedPermuted) {
-  // Ensures that two trials with different names but the same custom seed used
-  // for one time randomization produce the same group assignments.
-  base::FieldTrialList field_trial_list(
-      new PermutedEntropyProvider(1234, kMaxLowEntropySize));
-  const int kNoExpirationYear = base::FieldTrialList::kNoExpirationYear;
-  const uint32 kCustomSeed = 9001;
-  scoped_refptr<base::FieldTrial> trials[] = {
-      base::FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
-          "one", 100, "default", kNoExpirationYear, 1, 1,
-          base::FieldTrial::ONE_TIME_RANDOMIZED, kCustomSeed, NULL),
-      base::FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
-          "two", 100, "default", kNoExpirationYear, 1, 1,
-          base::FieldTrial::ONE_TIME_RANDOMIZED, kCustomSeed, NULL),
-  };
-
-  for (size_t i = 0; i < arraysize(trials); ++i) {
-    for (int j = 0; j < 100; ++j)
-      trials[i]->AppendGroup(std::string(), 1);
-  }
-
-  // Normally, these trials should produce different groups, but if the same
-  // custom seed is used, they should produce the same group assignment.
-  EXPECT_EQ(trials[0]->group(), trials[1]->group());
-  EXPECT_EQ(trials[0]->group_name(), trials[1]->group_name());
-}
-
-TEST(EntropyProviderTest, SHA1Entropy) {
-  const double results[] = { GenerateSHA1Entropy("hi", "1"),
-                             GenerateSHA1Entropy("there", "1") };
-
-  EXPECT_NE(results[0], results[1]);
-  for (size_t i = 0; i < arraysize(results); ++i) {
-    EXPECT_LE(0.0, results[i]);
-    EXPECT_GT(1.0, results[i]);
-  }
-
-  EXPECT_EQ(GenerateSHA1Entropy("yo", "1"),
-            GenerateSHA1Entropy("yo", "1"));
-  EXPECT_NE(GenerateSHA1Entropy("yo", "something"),
-            GenerateSHA1Entropy("yo", "else"));
-}
-
-TEST(EntropyProviderTest, PermutedEntropy) {
-  const double results[] = {
-      GeneratePermutedEntropy(1234, kMaxLowEntropySize, "1"),
-      GeneratePermutedEntropy(4321, kMaxLowEntropySize, "1") };
-
-  EXPECT_NE(results[0], results[1]);
-  for (size_t i = 0; i < arraysize(results); ++i) {
-    EXPECT_LE(0.0, results[i]);
-    EXPECT_GT(1.0, results[i]);
-  }
-
-  EXPECT_EQ(GeneratePermutedEntropy(1234, kMaxLowEntropySize, "1"),
-            GeneratePermutedEntropy(1234, kMaxLowEntropySize, "1"));
-  EXPECT_NE(GeneratePermutedEntropy(1234, kMaxLowEntropySize, "something"),
-            GeneratePermutedEntropy(1234, kMaxLowEntropySize, "else"));
-}
-
-TEST(EntropyProviderTest, PermutedEntropyProviderResults) {
-  // Verifies that PermutedEntropyProvider produces expected results. This
-  // ensures that the results are the same between platforms and ensures that
-  // changes to the implementation do not regress this accidentally.
-
-  EXPECT_DOUBLE_EQ(2194 / static_cast<double>(kMaxLowEntropySize),
-                   GeneratePermutedEntropy(1234, kMaxLowEntropySize, "XYZ"));
-  EXPECT_DOUBLE_EQ(5676 / static_cast<double>(kMaxLowEntropySize),
-                   GeneratePermutedEntropy(1, kMaxLowEntropySize, "Test"));
-  EXPECT_DOUBLE_EQ(1151 / static_cast<double>(kMaxLowEntropySize),
-                   GeneratePermutedEntropy(5000, kMaxLowEntropySize, "Foo"));
-}
-
-TEST(EntropyProviderTest, SHA1EntropyIsUniform) {
-  for (size_t i = 0; i < arraysize(kTestTrialNames); ++i) {
-    SHA1EntropyGenerator entropy_generator(kTestTrialNames[i]);
-    PerformEntropyUniformityTest(kTestTrialNames[i], entropy_generator);
-  }
-}
-
-TEST(EntropyProviderTest, PermutedEntropyIsUniform) {
-  for (size_t i = 0; i < arraysize(kTestTrialNames); ++i) {
-    PermutedEntropyGenerator entropy_generator(kTestTrialNames[i]);
-    PerformEntropyUniformityTest(kTestTrialNames[i], entropy_generator);
-  }
-}
-
-TEST(EntropyProviderTest, SeededRandGeneratorIsUniform) {
-  // Verifies that SeededRandGenerator has a uniform distribution.
-  //
-  // Mirrors RandUtilTest.RandGeneratorIsUniform in base/rand_util_unittest.cc.
-
-  const uint32 kTopOfRange = (std::numeric_limits<uint32>::max() / 4ULL) * 3ULL;
-  const uint32 kExpectedAverage = kTopOfRange / 2ULL;
-  const uint32 kAllowedVariance = kExpectedAverage / 50ULL;  // +/- 2%
-  const int kMinAttempts = 1000;
-  const int kMaxAttempts = 1000000;
-
-  for (size_t i = 0; i < arraysize(kTestTrialNames); ++i) {
-    const uint32 seed = HashName(kTestTrialNames[i]);
-    internal::SeededRandGenerator rand_generator(seed);
-
-    double cumulative_average = 0.0;
-    int count = 0;
-    while (count < kMaxAttempts) {
-      uint32 value = rand_generator(kTopOfRange);
-      cumulative_average = (count * cumulative_average + value) / (count + 1);
-
-      // Don't quit too quickly for things to start converging, or we may have
-      // a false positive.
-      if (count > kMinAttempts &&
-          kExpectedAverage - kAllowedVariance < cumulative_average &&
-          cumulative_average < kExpectedAverage + kAllowedVariance) {
-        break;
-      }
-
-      ++count;
-    }
-
-    ASSERT_LT(count, kMaxAttempts) << "Expected average was " <<
-        kExpectedAverage << ", average ended at " << cumulative_average <<
-        ", for trial " << kTestTrialNames[i];
-  }
-}
-
-}  // namespace metrics
diff --git a/chrome/common/metrics/metrics_util.cc b/chrome/common/metrics/metrics_util.cc
deleted file mode 100644
index 775501d..0000000
--- a/chrome/common/metrics/metrics_util.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/common/metrics/metrics_util.h"
-
-#include "base/sha1.h"
-#include "base/sys_byteorder.h"
-
-namespace metrics {
-
-uint32 HashName(const std::string& name) {
-  // SHA-1 is designed to produce a uniformly random spread in its output space,
-  // even for nearly-identical inputs.
-  unsigned char sha1_hash[base::kSHA1Length];
-  base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(name.c_str()),
-                      name.size(),
-                      sha1_hash);
-
-  uint32 bits;
-  COMPILE_ASSERT(sizeof(bits) < sizeof(sha1_hash), need_more_data);
-  memcpy(&bits, sha1_hash, sizeof(bits));
-
-  return base::ByteSwapToLE32(bits);
-}
-
-}  // namespace metrics
diff --git a/chrome/common/metrics/metrics_util.h b/chrome/common/metrics/metrics_util.h
deleted file mode 100644
index 306ed09..0000000
--- a/chrome/common/metrics/metrics_util.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_COMMON_METRICS_METRICS_UTIL_H_
-#define CHROME_COMMON_METRICS_METRICS_UTIL_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-
-namespace metrics {
-
-// Computes a uint32 hash of a given string based on its SHA1 hash. Suitable for
-// uniquely identifying field trial names and group names.
-uint32 HashName(const std::string& name);
-
-}  // namespace metrics
-
-#endif  // CHROME_COMMON_METRICS_METRICS_UTIL_H_
diff --git a/chrome/common/metrics/metrics_util_unittest.cc b/chrome/common/metrics/metrics_util_unittest.cc
deleted file mode 100644
index 7c21dfd..0000000
--- a/chrome/common/metrics/metrics_util_unittest.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/common/metrics/metrics_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace metrics {
-
-TEST(MetricsUtilTest, HashName) {
-  // Checks that hashing is stable on all platforms.
-  struct {
-    const char* name;
-    uint32 hash_value;
-  } known_hashes[] = {
-    {"a", 937752454u},
-    {"1", 723085877u},
-    {"Trial Name", 2713117220u},
-    {"Group Name", 3201815843u},
-    {"My Favorite Experiment", 3722155194u},
-    {"My Awesome Group Name", 4109503236u},
-    {"abcdefghijklmonpqrstuvwxyz", 787728696u},
-    {"0123456789ABCDEF", 348858318U}
-  };
-
-  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(known_hashes); ++i)
-    EXPECT_EQ(known_hashes[i].hash_value, HashName(known_hashes[i].name));
-}
-
-}  // namespace metrics
diff --git a/chrome/common/metrics/proto/omnibox_event.proto b/chrome/common/metrics/proto/omnibox_event.proto
index ebe0ab6..8f36337 100644
--- a/chrome/common/metrics/proto/omnibox_event.proto
+++ b/chrome/common/metrics/proto/omnibox_event.proto
@@ -64,27 +64,46 @@
 
   // The type of page currently displayed when the user used the omnibox.
   enum PageClassification {
-    INVALID_SPEC = 0;   // invalid URI; shouldn't happen
-    NEW_TAB_PAGE = 1;   // chrome://newtab/
-    // Note that chrome://newtab/ doesn't have to be the built-in
-    // version; it could be replaced by an extension.
-    BLANK = 2;          // about:blank
-    HOMEPAGE = 3;       // user switched settings to "open this page" mode.
-    // Note that if the homepage is set to the new tab page or about blank,
-    // then we'll classify the web page into those categories, not HOMEPAGE.
-    OTHER = 4;          // everything not included somewhere else on this list
+    // An invalid URL; shouldn't happen.
+    INVALID_SPEC = 0;
+
+    // chrome://newtab/.  This can be either the built-in version or a
+    // replacement new tab page from an extension.  Note that when Instant
+    // Extended is enabled, the new tab page will be reported as either
+    // INSTANT_NEW_TAB_PAGE_WITH_OMNIBOX_AS_STARTING_FOCUS or
+    // INSTANT_NEW_TAB_PAGE_WITH_FAKEBOX_AS_STARTING_FOCUS below,
+    // unless an extension is replacing the new tab page, in which case
+    // it will still be reported as NEW_TAB_PAGE.
+    NEW_TAB_PAGE = 1;
+
+    // about:blank.
+    BLANK = 2;
+
+    // The user's home page.  Note that if the home page is set to any
+    // of the new tab page versions or to about:blank, then we'll
+    // classify the page into those categories, not HOME_PAGE.
+    HOME_PAGE = 3;
+
+    // The catch-all entry of everything not included somewhere else
+    // on this list.
+    OTHER = 4;
+
     // The instant new tab page enum value was deprecated on August 2, 2013.
     OBSOLETE_INSTANT_NEW_TAB_PAGE = 5;
+
     // The user is on a search result page that's doing search term
     // replacement, meaning the search terms should've appeared in the omnibox
     // before the user started editing it, not the URL of the page.
     SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT = 6;
+
     // The new tab page in which this omnibox interaction first started
     // with the user having focus in the omnibox.
     INSTANT_NEW_TAB_PAGE_WITH_OMNIBOX_AS_STARTING_FOCUS = 7;
+
     // The new tab page in which this omnibox interaction first started
     // with the user having focus in the fakebox.
     INSTANT_NEW_TAB_PAGE_WITH_FAKEBOX_AS_STARTING_FOCUS = 8;
+
     // The user is on a search result page that's not doing search term
     // replacement, meaning the URL of the page should've appeared in the
     // omnibox before the user started editing it, not the search terms.
diff --git a/chrome/common/metrics/variations/variation_ids.h b/chrome/common/metrics/variations/variation_ids.h
index 4e1e9da..e7ff03d 100644
--- a/chrome/common/metrics/variations/variation_ids.h
+++ b/chrome/common/metrics/variations/variation_ids.h
@@ -230,10 +230,14 @@
   COOKIE_RETENTION_PRIORITY_STUDY_EXPERIMENT_OFF = 3310869,
   COOKIE_RETENTION_PRIORITY_STUDY_EXPERIMENT_ON = 3310870,
 
+  // QUIC field trial.
+  QUIC_FIELD_TRIAL_ID_MIN = 3311871,
+  QUIC_FIELD_TRIAL_ID_MAX = 3311920,
+
   // NEXT ID: When adding new IDs, please add them above this section, starting
   // with the value of NEXT_ID, and updating NEXT_ID to (end of your reserved
   // range) + 1.
-  NEXT_ID = 3311871,
+  NEXT_ID = 3311921,
 
   // USABLE IDs END HERE.
   //
diff --git a/chrome/common/metrics/variations/variations_associated_data.cc b/chrome/common/metrics/variations/variations_associated_data.cc
deleted file mode 100644
index 9a1a56e..0000000
--- a/chrome/common/metrics/variations/variations_associated_data.cc
+++ /dev/null
@@ -1,236 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/common/metrics/variations/variations_associated_data.h"
-
-#include <map>
-#include <vector>
-
-#include "base/memory/singleton.h"
-#include "chrome/common/metrics/metrics_util.h"
-#include "chrome/common/metrics/variations/variation_ids.h"
-
-namespace chrome_variations {
-
-namespace {
-
-// The internal singleton accessor for the map, used to keep it thread-safe.
-class GroupMapAccessor {
- public:
-  typedef std::map<ActiveGroupId, VariationID, ActiveGroupIdCompare>
-      GroupToIDMap;
-
-  // Retrieve the singleton.
-  static GroupMapAccessor* GetInstance() {
-    return Singleton<GroupMapAccessor>::get();
-  }
-
-  // Note that this normally only sets the ID for a group the first time, unless
-  // |force| is set to true, in which case it will always override it.
-  void AssociateID(IDCollectionKey key,
-                   const ActiveGroupId& group_identifier,
-                   const VariationID id,
-                   const bool force) {
-#if !defined(NDEBUG)
-    // Validate that all collections with this |group_identifier| have the same
-    // associated ID.
-    DCHECK_EQ(2, ID_COLLECTION_COUNT);
-    IDCollectionKey other_key = GOOGLE_WEB_PROPERTIES;
-    if (key == GOOGLE_WEB_PROPERTIES)
-      other_key = GOOGLE_UPDATE_SERVICE;
-    VariationID other_id = GetID(other_key, group_identifier);
-    DCHECK(other_id == EMPTY_ID || other_id == id);
-#endif
-
-    base::AutoLock scoped_lock(lock_);
-
-    GroupToIDMap* group_to_id_map = GetGroupToIDMap(key);
-    if (force ||
-        group_to_id_map->find(group_identifier) == group_to_id_map->end())
-      (*group_to_id_map)[group_identifier] = id;
-  }
-
-  VariationID GetID(IDCollectionKey key,
-                    const ActiveGroupId& group_identifier) {
-    base::AutoLock scoped_lock(lock_);
-    GroupToIDMap* group_to_id_map = GetGroupToIDMap(key);
-    GroupToIDMap::const_iterator it = group_to_id_map->find(group_identifier);
-    if (it == group_to_id_map->end())
-      return EMPTY_ID;
-    return it->second;
-  }
-
-  void ClearAllMapsForTesting() {
-    base::AutoLock scoped_lock(lock_);
-
-    for (int i = 0; i < ID_COLLECTION_COUNT; ++i) {
-      GroupToIDMap* map = GetGroupToIDMap(static_cast<IDCollectionKey>(i));
-      DCHECK(map);
-      map->clear();
-    }
-  }
-
- private:
-  friend struct DefaultSingletonTraits<GroupMapAccessor>;
-
-  // Retrieves the GroupToIDMap for |key|.
-  GroupToIDMap* GetGroupToIDMap(IDCollectionKey key) {
-    return &group_to_id_maps_[key];
-  }
-
-  GroupMapAccessor() {
-    group_to_id_maps_.resize(ID_COLLECTION_COUNT);
-  }
-  ~GroupMapAccessor() {}
-
-  base::Lock lock_;
-  std::vector<GroupToIDMap> group_to_id_maps_;
-
-  DISALLOW_COPY_AND_ASSIGN(GroupMapAccessor);
-};
-
-// Singleton helper class that keeps track of the parameters of all variations
-// and ensures access to these is thread-safe.
-class VariationsParamAssociator {
- public:
-  typedef std::pair<std::string, std::string> VariationKey;
-  typedef std::map<std::string, std::string> VariationParams;
-
-  // Retrieve the singleton.
-  static VariationsParamAssociator* GetInstance() {
-    return Singleton<VariationsParamAssociator>::get();
-  }
-
-  bool AssociateVariationParams(const std::string& trial_name,
-                                const std::string& group_name,
-                                const VariationParams& params) {
-    base::AutoLock scoped_lock(lock_);
-
-    if (IsFieldTrialActive(trial_name))
-      return false;
-
-    const VariationKey key(trial_name, group_name);
-    if (ContainsKey(variation_params_, key))
-      return false;
-
-    variation_params_[key] = params;
-    return true;
-  }
-
-  bool GetVariationParams(const std::string& trial_name,
-                          VariationParams* params) {
-    base::AutoLock scoped_lock(lock_);
-
-    const std::string group_name =
-        base::FieldTrialList::FindFullName(trial_name);
-    const VariationKey key(trial_name, group_name);
-    if (!ContainsKey(variation_params_, key))
-      return false;
-
-    *params = variation_params_[key];
-    return true;
-  }
-
-  void ClearAllParamsForTesting() {
-    base::AutoLock scoped_lock(lock_);
-    variation_params_.clear();
-  }
-
- private:
-  friend struct DefaultSingletonTraits<VariationsParamAssociator>;
-
-  VariationsParamAssociator() {}
-  ~VariationsParamAssociator() {}
-
-  // Tests whether a field trial is active (i.e. group() has been called on it).
-  // TODO(asvitkine): Expose this as an API on base::FieldTrial.
-  bool IsFieldTrialActive(const std::string& trial_name) {
-    base::FieldTrial::ActiveGroups active_groups;
-    base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
-    for (size_t i = 0; i < active_groups.size(); ++i) {
-      if (active_groups[i].trial_name == trial_name)
-        return true;
-    }
-    return false;
-  }
-
-  base::Lock lock_;
-  std::map<VariationKey, VariationParams> variation_params_;
-
-  DISALLOW_COPY_AND_ASSIGN(VariationsParamAssociator);
-};
-
-}  // namespace
-
-ActiveGroupId MakeActiveGroupId(const std::string& trial_name,
-                                const std::string& group_name) {
-  ActiveGroupId id;
-  id.name = metrics::HashName(trial_name);
-  id.group = metrics::HashName(group_name);
-  return id;
-}
-
-void AssociateGoogleVariationID(IDCollectionKey key,
-                                const std::string& trial_name,
-                                const std::string& group_name,
-                                VariationID id) {
-  GroupMapAccessor::GetInstance()->AssociateID(
-      key, MakeActiveGroupId(trial_name, group_name), id, false);
-}
-
-void AssociateGoogleVariationIDForce(IDCollectionKey key,
-                                     const std::string& trial_name,
-                                     const std::string& group_name,
-                                     VariationID id) {
-  GroupMapAccessor::GetInstance()->AssociateID(
-      key, MakeActiveGroupId(trial_name, group_name), id, true);
-}
-
-VariationID GetGoogleVariationID(IDCollectionKey key,
-                                 const std::string& trial_name,
-                                 const std::string& group_name) {
-  return GroupMapAccessor::GetInstance()->GetID(
-      key, MakeActiveGroupId(trial_name, group_name));
-}
-
-bool AssociateVariationParams(
-    const std::string& trial_name,
-    const std::string& group_name,
-    const std::map<std::string, std::string>& params) {
-  return VariationsParamAssociator::GetInstance()->AssociateVariationParams(
-      trial_name, group_name, params);
-}
-
-bool GetVariationParams(const std::string& trial_name,
-                        std::map<std::string, std::string>* params) {
-  return VariationsParamAssociator::GetInstance()->GetVariationParams(
-      trial_name, params);
-}
-
-std::string GetVariationParamValue(const std::string& trial_name,
-                                   const std::string& param_name) {
-  std::map<std::string, std::string> params;
-  if (GetVariationParams(trial_name, &params)) {
-    std::map<std::string, std::string>::iterator it = params.find(param_name);
-    if (it != params.end())
-      return it->second;
-  }
-  return std::string();
-}
-
-// Functions below are exposed for testing explicitly behind this namespace.
-// They simply wrap existing functions in this file.
-namespace testing {
-
-void ClearAllVariationIDs() {
-  GroupMapAccessor::GetInstance()->ClearAllMapsForTesting();
-}
-
-void ClearAllVariationParams() {
-  VariationsParamAssociator::GetInstance()->ClearAllParamsForTesting();
-}
-
-}  // namespace testing
-
-}  // namespace chrome_variations
diff --git a/chrome/common/metrics/variations/variations_associated_data.h b/chrome/common/metrics/variations/variations_associated_data.h
deleted file mode 100644
index 78d89b6..0000000
--- a/chrome/common/metrics/variations/variations_associated_data.h
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_COMMON_METRICS_VARIATIONS_VARIATIONS_ASSOCIATED_DATA_H_
-#define CHROME_COMMON_METRICS_VARIATIONS_VARIATIONS_ASSOCIATED_DATA_H_
-
-#include <map>
-#include <string>
-
-#include "base/metrics/field_trial.h"
-
-// This file provides various helpers that extend the functionality around
-// base::FieldTrial.
-//
-// This includes several simple APIs to handle getting and setting additional
-// data related to Chrome variations, such as parameters and Google variation
-// IDs. These APIs are meant to extend the base::FieldTrial APIs to offer extra
-// functionality that is not offered by the simpler base::FieldTrial APIs.
-//
-// The AssociateGoogleVariationID and AssociateVariationParams functions are
-// generally meant to be called by the VariationsService based on server-side
-// variation configs, but may also be used for client-only field trials by
-// invoking them directly after appending all the groups to a FieldTrial.
-//
-// Experiment code can then use the getter APIs to retrieve variation parameters
-// or IDs:
-//
-//  std::map<std::string, std::string> params;
-//  if (GetVariationParams("trial", &params)) {
-//    // use |params|
-//  }
-//
-//  std::string value = GetVariationParamValue("trial", "param_x");
-//  // use |value|, which will be "" if it does not exist
-//
-// VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial",
-//                                       "group1");
-// if (id != chrome_variations::kEmptyID) {
-//   // use |id|
-// }
-
-namespace chrome_variations {
-
-typedef int VariationID;
-
-const VariationID EMPTY_ID = 0;
-
-// The Unique ID of a trial and its active group, where the name and group
-// identifiers are hashes of the trial and group name strings.
-struct ActiveGroupId {
-  uint32 name;
-  uint32 group;
-};
-
-// Returns an ActiveGroupId struct for the given trial and group names.
-ActiveGroupId MakeActiveGroupId(const std::string& trial_name,
-                                const std::string& group_name);
-
-// We need to supply a Compare class for templates since ActiveGroupId is a
-// user-defined type.
-struct ActiveGroupIdCompare {
-  bool operator() (const ActiveGroupId& lhs, const ActiveGroupId& rhs) const {
-    // The group and name fields are just SHA-1 Hashes, so we just need to treat
-    // them as IDs and do a less-than comparison. We test group first, since
-    // name is more likely to collide.
-    if (lhs.group != rhs.group)
-      return lhs.group < rhs.group;
-    return lhs.name < rhs.name;
-  }
-};
-
-// A key into the Associate/Get methods for VariationIDs. This is used to create
-// separate ID associations for separate parties interested in VariationIDs.
-enum IDCollectionKey {
-  // This collection is used by Google web properties, transmitted through the
-  // X-Chrome-Variations header.
-  GOOGLE_WEB_PROPERTIES,
-  // This collection is used by Google update services, transmitted through the
-  // Google Update experiment labels.
-  GOOGLE_UPDATE_SERVICE,
-  // The total count of collections.
-  ID_COLLECTION_COUNT,
-};
-
-// Associate a chrome_variations::VariationID value with a FieldTrial group for
-// collection |key|. If an id was previously set for |trial_name| and
-// |group_name|, this does nothing. The group is denoted by |trial_name| and
-// |group_name|. This must be called whenever a FieldTrial is prepared (create
-// the trial and append groups) and needs to have a
-// chrome_variations::VariationID associated with it so Google servers can
-// recognize the FieldTrial. Thread safe.
-void AssociateGoogleVariationID(IDCollectionKey key,
-                                const std::string& trial_name,
-                                const std::string& group_name,
-                                VariationID id);
-
-// As above, but overwrites any previously set id. Thread safe.
-void AssociateGoogleVariationIDForce(IDCollectionKey key,
-                                     const std::string& trial_name,
-                                     const std::string& group_name,
-                                     VariationID id);
-
-// Retrieve the chrome_variations::VariationID associated with a FieldTrial
-// group for collection |key|. The group is denoted by |trial_name| and
-// |group_name|. This will return chrome_variations::kEmptyID if there is
-// currently no associated ID for the named group. This API can be nicely
-// combined with FieldTrial::GetActiveFieldTrialGroups() to enumerate the
-// variation IDs for all active FieldTrial groups. Thread safe.
-VariationID GetGoogleVariationID(IDCollectionKey key,
-                                 const std::string& trial_name,
-                                 const std::string& group_name);
-
-// Associates the specified set of key-value |params| with the variation
-// specified by |trial_name| and |group_name|. Fails and returns false if the
-// specified variation already has params associated with it or the field trial
-// is already active (group() has been called on it). Thread safe.
-bool AssociateVariationParams(const std::string& trial_name,
-                              const std::string& group_name,
-                              const std::map<std::string, std::string>& params);
-
-// Retrieves the set of key-value |params| for the variation associated with
-// the specified field trial, based on its selected group. If the field trial
-// does not exist or its selected group does not have any parameters associated
-// with it, returns false and does not modify |params|. Calling this function
-// will result in the field trial being marked as active if found (i.e. group()
-// will be called on it), if it wasn't already. Currently, this information is
-// only available from the browser process. Thread safe.
-bool GetVariationParams(const std::string& trial_name,
-                        std::map<std::string, std::string>* params);
-
-// Retrieves a specific parameter value corresponding to |param_name| for the
-// variation associated with the specified field trial, based on its selected
-// group. If the field trial does not exist or the specified parameter does not
-// exist, returns an empty string. Calling this function will result in the
-// field trial being marked as active if found (i.e. group() will be called on
-// it), if it wasn't already. Currently, this information is only available from
-// the browser process. Thread safe.
-std::string GetVariationParamValue(const std::string& trial_name,
-                                   const std::string& param_name);
-
-// Expose some functions for testing.
-namespace testing {
-
-// Clears all of the mapped associations.
-void ClearAllVariationIDs();
-
-// Clears all of the associated params.
-void ClearAllVariationParams();
-
-}  // namespace testing
-
-}  // namespace chrome_variations
-
-#endif  // CHROME_COMMON_METRICS_VARIATIONS_VARIATIONS_ASSOCIATED_DATA_H_
diff --git a/chrome/common/metrics/variations/variations_associated_data_unittest.cc b/chrome/common/metrics/variations/variations_associated_data_unittest.cc
deleted file mode 100644
index b3832e6..0000000
--- a/chrome/common/metrics/variations/variations_associated_data_unittest.cc
+++ /dev/null
@@ -1,310 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/common/metrics/variations/variations_associated_data.h"
-
-#include "base/metrics/field_trial.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace chrome_variations {
-
-namespace {
-
-const VariationID TEST_VALUE_A = 3300200;
-const VariationID TEST_VALUE_B = 3300201;
-
-// Convenience helper to retrieve the chrome_variations::VariationID for a
-// FieldTrial. Note that this will do the group assignment in |trial| if not
-// already done.
-VariationID GetIDForTrial(IDCollectionKey key, base::FieldTrial* trial) {
-  return GetGoogleVariationID(key, trial->trial_name(), trial->group_name());
-}
-
-// Tests whether a field trial is active (i.e. group() has been called on it).
-bool IsFieldTrialActive(const std::string& trial_name) {
-  base::FieldTrial::ActiveGroups active_groups;
-  base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
-  for (size_t i = 0; i < active_groups.size(); ++i) {
-    if (active_groups[i].trial_name == trial_name)
-      return true;
-  }
-  return false;
-}
-
-// Call FieldTrialList::FactoryGetFieldTrial() with a future expiry date.
-scoped_refptr<base::FieldTrial> CreateFieldTrial(
-    const std::string& trial_name,
-    int total_probability,
-    const std::string& default_group_name,
-    int* default_group_number) {
-  return base::FieldTrialList::FactoryGetFieldTrial(
-      trial_name, total_probability, default_group_name,
-      base::FieldTrialList::kNoExpirationYear, 1, 1,
-      base::FieldTrial::SESSION_RANDOMIZED, default_group_number);
-}
-
-}  // namespace
-
-class VariationsAssociatedDataTest : public ::testing::Test {
- public:
-  VariationsAssociatedDataTest() : field_trial_list_(NULL) {
-  }
-
-  virtual ~VariationsAssociatedDataTest() {
-    // Ensure that the maps are cleared between tests, since they are stored as
-    // process singletons.
-    testing::ClearAllVariationIDs();
-  }
-
- private:
-  base::FieldTrialList field_trial_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(VariationsAssociatedDataTest);
-};
-
-// Test that if the trial is immediately disabled, GetGoogleVariationID just
-// returns the empty ID.
-TEST_F(VariationsAssociatedDataTest, DisableImmediately) {
-  int default_group_number = -1;
-  scoped_refptr<base::FieldTrial> trial(
-      CreateFieldTrial("trial", 100, "default", &default_group_number));
-
-  ASSERT_EQ(default_group_number, trial->group());
-  ASSERT_EQ(EMPTY_ID, GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial.get()));
-}
-
-// Test that successfully associating the FieldTrial with some ID, and then
-// disabling the FieldTrial actually makes GetGoogleVariationID correctly
-// return the empty ID.
-TEST_F(VariationsAssociatedDataTest, DisableAfterInitialization) {
-  const std::string default_name = "default";
-  const std::string non_default_name = "non_default";
-
-  scoped_refptr<base::FieldTrial> trial(
-      CreateFieldTrial("trial", 100, default_name, NULL));
-
-  trial->AppendGroup(non_default_name, 100);
-  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial->trial_name(),
-      default_name, TEST_VALUE_A);
-  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial->trial_name(),
-      non_default_name, TEST_VALUE_B);
-  trial->Disable();
-  ASSERT_EQ(default_name, trial->group_name());
-  ASSERT_EQ(TEST_VALUE_A, GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial.get()));
-}
-
-// Test various successful association cases.
-TEST_F(VariationsAssociatedDataTest, AssociateGoogleVariationID) {
-  const std::string default_name1 = "default";
-  scoped_refptr<base::FieldTrial> trial_true(
-      CreateFieldTrial("d1", 10, default_name1, NULL));
-  const std::string winner = "TheWinner";
-  int winner_group = trial_true->AppendGroup(winner, 10);
-
-  // Set GoogleVariationIDs so we can verify that they were chosen correctly.
-  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_true->trial_name(),
-      default_name1, TEST_VALUE_A);
-  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_true->trial_name(),
-      winner, TEST_VALUE_B);
-
-  EXPECT_EQ(winner_group, trial_true->group());
-  EXPECT_EQ(winner, trial_true->group_name());
-  EXPECT_EQ(TEST_VALUE_B,
-            GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get()));
-
-  const std::string default_name2 = "default2";
-  scoped_refptr<base::FieldTrial> trial_false(
-      CreateFieldTrial("d2", 10, default_name2, NULL));
-  const std::string loser = "ALoser";
-  const int loser_group = trial_false->AppendGroup(loser, 0);
-
-  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_false->trial_name(),
-      default_name2, TEST_VALUE_A);
-  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_false->trial_name(),
-      loser, TEST_VALUE_B);
-
-  EXPECT_NE(loser_group, trial_false->group());
-  EXPECT_EQ(TEST_VALUE_A,
-            GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_false.get()));
-}
-
-// Test that not associating a FieldTrial with any IDs ensure that the empty ID
-// will be returned.
-TEST_F(VariationsAssociatedDataTest, NoAssociation) {
-  const std::string default_name = "default";
-  scoped_refptr<base::FieldTrial> no_id_trial(
-      CreateFieldTrial("d3", 10, default_name, NULL));
-
-  const std::string winner = "TheWinner";
-  const int winner_group = no_id_trial->AppendGroup(winner, 10);
-
-  // Ensure that despite the fact that a normal winner is elected, it does not
-  // have a valid VariationID associated with it.
-  EXPECT_EQ(winner_group, no_id_trial->group());
-  EXPECT_EQ(winner, no_id_trial->group_name());
-  EXPECT_EQ(EMPTY_ID, GetIDForTrial(GOOGLE_WEB_PROPERTIES, no_id_trial.get()));
-}
-
-// Ensure that the AssociateGoogleVariationIDForce works as expected.
-TEST_F(VariationsAssociatedDataTest, ForceAssociation) {
-  EXPECT_EQ(EMPTY_ID,
-            GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group"));
-  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group",
-                             TEST_VALUE_A);
-  EXPECT_EQ(TEST_VALUE_A,
-            GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group"));
-  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group",
-                             TEST_VALUE_B);
-  EXPECT_EQ(TEST_VALUE_A,
-            GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group"));
-  AssociateGoogleVariationIDForce(GOOGLE_WEB_PROPERTIES, "trial", "group",
-                                  TEST_VALUE_B);
-  EXPECT_EQ(TEST_VALUE_B,
-            GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group"));
-}
-
-// Ensure that two collections can coexist without affecting each other.
-TEST_F(VariationsAssociatedDataTest, CollectionsCoexist) {
-  const std::string default_name = "default";
-  int default_group_number = -1;
-  scoped_refptr<base::FieldTrial> trial_true(
-      CreateFieldTrial("d1", 10, default_name, &default_group_number));
-  ASSERT_EQ(default_group_number, trial_true->group());
-  ASSERT_EQ(default_name, trial_true->group_name());
-
-  EXPECT_EQ(EMPTY_ID,
-            GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get()));
-  EXPECT_EQ(EMPTY_ID,
-            GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get()));
-
-  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_true->trial_name(),
-      default_name, TEST_VALUE_A);
-  EXPECT_EQ(TEST_VALUE_A,
-            GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get()));
-  EXPECT_EQ(EMPTY_ID,
-            GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get()));
-
-  AssociateGoogleVariationID(GOOGLE_UPDATE_SERVICE, trial_true->trial_name(),
-      default_name, TEST_VALUE_A);
-  EXPECT_EQ(TEST_VALUE_A,
-            GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get()));
-  EXPECT_EQ(TEST_VALUE_A,
-            GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get()));
-}
-
-TEST_F(VariationsAssociatedDataTest, AssociateVariationParams) {
-  const std::string kTrialName = "AssociateVariationParams";
-
-  {
-    std::map<std::string, std::string> params;
-    params["a"] = "10";
-    params["b"] = "test";
-    ASSERT_TRUE(AssociateVariationParams(kTrialName, "A", params));
-  }
-  {
-    std::map<std::string, std::string> params;
-    params["a"] = "5";
-    ASSERT_TRUE(AssociateVariationParams(kTrialName, "B", params));
-  }
-
-  base::FieldTrialList::CreateFieldTrial(kTrialName, "B");
-  EXPECT_EQ("5", GetVariationParamValue(kTrialName, "a"));
-  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "b"));
-  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x"));
-
-  std::map<std::string, std::string> params;
-  EXPECT_TRUE(GetVariationParams(kTrialName, &params));
-  EXPECT_EQ(1U, params.size());
-  EXPECT_EQ("5", params["a"]);
-}
-
-TEST_F(VariationsAssociatedDataTest, AssociateVariationParams_Fail) {
-  const std::string kTrialName = "AssociateVariationParams_Fail";
-  const std::string kGroupName = "A";
-
-  std::map<std::string, std::string> params;
-  params["a"] = "10";
-  ASSERT_TRUE(AssociateVariationParams(kTrialName, kGroupName, params));
-  params["a"] = "1";
-  params["b"] = "2";
-  ASSERT_FALSE(AssociateVariationParams(kTrialName, kGroupName, params));
-
-  base::FieldTrialList::CreateFieldTrial(kTrialName, kGroupName);
-  EXPECT_EQ("10", GetVariationParamValue(kTrialName, "a"));
-  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "b"));
-}
-
-TEST_F(VariationsAssociatedDataTest, AssociateVariationParams_TrialActiveFail) {
-  const std::string kTrialName = "AssociateVariationParams_TrialActiveFail";
-  base::FieldTrialList::CreateFieldTrial(kTrialName, "A");
-  ASSERT_EQ("A", base::FieldTrialList::FindFullName(kTrialName));
-
-  std::map<std::string, std::string> params;
-  params["a"] = "10";
-  EXPECT_FALSE(AssociateVariationParams(kTrialName, "B", params));
-  EXPECT_FALSE(AssociateVariationParams(kTrialName, "A", params));
-}
-
-TEST_F(VariationsAssociatedDataTest,
-       AssociateVariationParams_DoesntActivateTrial) {
-  const std::string kTrialName = "AssociateVariationParams_DoesntActivateTrial";
-
-  ASSERT_FALSE(IsFieldTrialActive(kTrialName));
-  scoped_refptr<base::FieldTrial> trial(
-      CreateFieldTrial(kTrialName, 100, "A", NULL));
-  ASSERT_FALSE(IsFieldTrialActive(kTrialName));
-
-  std::map<std::string, std::string> params;
-  params["a"] = "10";
-  EXPECT_TRUE(AssociateVariationParams(kTrialName, "A", params));
-  ASSERT_FALSE(IsFieldTrialActive(kTrialName));
-}
-
-TEST_F(VariationsAssociatedDataTest, GetVariationParams_NoTrial) {
-  const std::string kTrialName = "GetVariationParams_NoParams";
-
-  std::map<std::string, std::string> params;
-  EXPECT_FALSE(GetVariationParams(kTrialName, &params));
-  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x"));
-  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "y"));
-}
-
-TEST_F(VariationsAssociatedDataTest, GetVariationParams_NoParams) {
-  const std::string kTrialName = "GetVariationParams_NoParams";
-
-  base::FieldTrialList::CreateFieldTrial(kTrialName, "A");
-
-  std::map<std::string, std::string> params;
-  EXPECT_FALSE(GetVariationParams(kTrialName, &params));
-  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x"));
-  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "y"));
-}
-
-TEST_F(VariationsAssociatedDataTest, GetVariationParams_ActivatesTrial) {
-  const std::string kTrialName = "GetVariationParams_ActivatesTrial";
-
-  ASSERT_FALSE(IsFieldTrialActive(kTrialName));
-  scoped_refptr<base::FieldTrial> trial(
-      CreateFieldTrial(kTrialName, 100, "A", NULL));
-  ASSERT_FALSE(IsFieldTrialActive(kTrialName));
-
-  std::map<std::string, std::string> params;
-  EXPECT_FALSE(GetVariationParams(kTrialName, &params));
-  ASSERT_TRUE(IsFieldTrialActive(kTrialName));
-}
-
-TEST_F(VariationsAssociatedDataTest, GetVariationParamValue_ActivatesTrial) {
-  const std::string kTrialName = "GetVariationParamValue_ActivatesTrial";
-
-  ASSERT_FALSE(IsFieldTrialActive(kTrialName));
-  scoped_refptr<base::FieldTrial> trial(
-      CreateFieldTrial(kTrialName, 100, "A", NULL));
-  ASSERT_FALSE(IsFieldTrialActive(kTrialName));
-
-  std::map<std::string, std::string> params;
-  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x"));
-  ASSERT_TRUE(IsFieldTrialActive(kTrialName));
-}
-
-}  // namespace chrome_variations
diff --git a/chrome/common/metrics/variations/variations_util.h b/chrome/common/metrics/variations/variations_util.h
index f6e29ed..cd0c787 100644
--- a/chrome/common/metrics/variations/variations_util.h
+++ b/chrome/common/metrics/variations/variations_util.h
@@ -9,7 +9,7 @@
 
 #include "base/metrics/field_trial.h"
 #include "base/strings/string16.h"
-#include "chrome/common/metrics/variations/variations_associated_data.h"
+#include "components/variations/variations_associated_data.h"
 
 namespace chrome_variations {
 
diff --git a/chrome/common/metrics/variations/variations_util_unittest.cc b/chrome/common/metrics/variations/variations_util_unittest.cc
index e5845e5..1f9e140 100644
--- a/chrome/common/metrics/variations/variations_util_unittest.cc
+++ b/chrome/common/metrics/variations/variations_util_unittest.cc
@@ -11,7 +11,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
-#include "chrome/common/metrics/metrics_util.h"
+#include "components/variations/metrics_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chrome_variations {
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 1376a4c..06af5ac 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1305,6 +1305,11 @@
 // loaded.
 const char kExtensionsLastChromeVersion[] = "extensions.last_chrome_version";
 
+// Boolean pref that determines whether the user can enter fullscreen mode.
+// Disabling fullscreen mode also makes kiosk mode unavailable on desktop
+// platforms.
+extern const char kFullscreenAllowed[] = "fullscreen.allowed";
+
 // *************** LOCAL STATE ***************
 // These are attached to the machine/installation
 
@@ -2199,6 +2204,10 @@
 // Dictionary of per-user Least Recently Used input method (used at login
 // screen).
 extern const char kUsersLRUInputMethod[] = "UsersLRUInputMethod";
+
+// A dictionary pref of the echo offer check flag. It sets offer info when
+// an offer is checked.
+extern const char kEchoCheckedOffers[] = "EchoCheckedOffers";
 #endif
 
 // Whether there is a Flash version installed that supports clearing LSO data.
@@ -2500,6 +2509,8 @@
     "overscroll.vertical_threshold_complete";
 const char kOverscrollMinimumThresholdStart[] =
     "overscroll.minimum_threshold_start";
+const char kOverscrollMinimumThresholdStartTouchpad[] =
+    "overscroll.minimum_threshold_start_touchpad";
 const char kOverscrollVerticalThresholdStart[] =
     "overscroll.vertical_threshold_start";
 const char kOverscrollHorizontalResistThreshold[] =
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 73f6f7b..a325e16 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -446,6 +446,8 @@
 extern const char kExtensionsPref[];
 extern const char kExtensionsLastChromeVersion[];
 
+extern const char kFullscreenAllowed[];
+
 // Local state prefs. Please add Profile prefs above instead.
 extern const char kCertRevocationCheckingEnabled[];
 extern const char kCertRevocationCheckingRequiredLocalAnchors[];
@@ -789,6 +791,7 @@
 extern const char kDeviceEnrollmentAutoStart[];
 extern const char kDeviceEnrollmentCanExit[];
 extern const char kUsersLRUInputMethod[];
+extern const char kEchoCheckedOffers[];
 #endif
 
 extern const char kClearPluginLSODataEnabled[];
@@ -909,6 +912,7 @@
 extern const char kOverscrollHorizontalThresholdComplete[];
 extern const char kOverscrollVerticalThresholdComplete[];
 extern const char kOverscrollMinimumThresholdStart[];
+extern const char kOverscrollMinimumThresholdStartTouchpad[];
 extern const char kOverscrollVerticalThresholdStart[];
 extern const char kOverscrollHorizontalResistThreshold[];
 extern const char kOverscrollVerticalResistThreshold[];
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h
index 41de013..8c8c881 100644
--- a/chrome/common/render_messages.h
+++ b/chrome/common/render_messages.h
@@ -162,6 +162,11 @@
   IPC_STRUCT_TRAITS_MEMBER(incognito)
 IPC_STRUCT_TRAITS_END()
 
+IPC_STRUCT_TRAITS_BEGIN(InstantSuggestion)
+  IPC_STRUCT_TRAITS_MEMBER(text)
+  IPC_STRUCT_TRAITS_MEMBER(metadata)
+IPC_STRUCT_TRAITS_END()
+
 IPC_STRUCT_TRAITS_BEGIN(InstantMostVisitedItem)
   IPC_STRUCT_TRAITS_MEMBER(url)
   IPC_STRUCT_TRAITS_MEMBER(title)
@@ -291,6 +296,9 @@
 
 IPC_MESSAGE_ROUTED0(ChromeViewMsg_DetermineIfPageSupportsInstant)
 
+IPC_MESSAGE_ROUTED1(ChromeViewMsg_SearchBoxSetDisplayInstantResults,
+                    bool /* display_instant_results */)
+
 IPC_MESSAGE_ROUTED2(ChromeViewMsg_SearchBoxFocusChanged,
                     OmniboxFocusState /* new_focus_state */,
                     OmniboxFocusChangeReason /* reason */)
@@ -312,6 +320,9 @@
 IPC_MESSAGE_ROUTED1(ChromeViewMsg_SearchBoxSetInputInProgress,
                     bool /* input_in_progress */)
 
+IPC_MESSAGE_ROUTED1(ChromeViewMsg_SearchBoxSetSuggestionToPrefetch,
+                    InstantSuggestion /* suggestion */)
+
 IPC_MESSAGE_ROUTED1(ChromeViewMsg_SearchBoxSubmit,
                     string16 /* value */)
 
@@ -392,8 +403,9 @@
 IPC_MESSAGE_ROUTED1(ChromeViewMsg_SetWindowFeatures,
                     WebKit::WebWindowFeatures /* window_features */)
 
-IPC_MESSAGE_ROUTED1(ChromeViewHostMsg_RequestThumbnailForContextNode_ACK,
-                    SkBitmap /* thumbnail */)
+IPC_MESSAGE_ROUTED2(ChromeViewHostMsg_RequestThumbnailForContextNode_ACK,
+                    SkBitmap /* thumbnail */,
+                    gfx::Size /* original size of the image */)
 
 #if defined(OS_ANDROID)
 // Asks the renderer to return information about whether the current page can
@@ -629,9 +641,10 @@
 
 #if defined(OS_ANDROID)
 // Contains info about whether the current page can be treated as a webapp.
-IPC_MESSAGE_ROUTED2(ChromeViewHostMsg_DidRetrieveWebappInformation,
+IPC_MESSAGE_ROUTED3(ChromeViewHostMsg_DidRetrieveWebappInformation,
                     bool /* success */,
-                    bool /* is_webapp_capable */)
+                    bool /* is_webapp_capable */,
+                    GURL /* expected_url */)
 #endif  // defined(OS_ANDROID)
 
 // Message sent from renderer to the browser when the element that is focused
diff --git a/chrome/common/safe_browsing/csd.proto b/chrome/common/safe_browsing/csd.proto
index 044b9a4..16ffbfc 100644
--- a/chrome/common/safe_browsing/csd.proto
+++ b/chrome/common/safe_browsing/csd.proto
@@ -115,11 +115,13 @@
 }
 
 message ClientMalwareResponse {
-    required bool blacklist = 1;
-    // The confirmed blacklisted bad IP, which will be shown in malware warning.
-    // This IP string could be either in IPv4 or IPv6 format, which is the same
-    // as the ones client sent to server.
-    optional string bad_ip = 2;
+  required bool blacklist = 1;
+  // The confirmed blacklisted bad IP and its url, which will be shown in
+  // malware warning, if the blacklist verdict is true.
+  // This IP string could be either in IPv4 or IPv6 format, which is the same
+  // as the ones client sent to server.
+  optional string bad_ip = 2;
+  optional string bad_url = 3;
 }
 
 message ClientDownloadRequest {
diff --git a/chrome/installer/mini_installer/chrome.release b/chrome/installer/mini_installer/chrome.release
index bbf4da9..5286b3a 100644
--- a/chrome/installer/mini_installer/chrome.release
+++ b/chrome/installer/mini_installer/chrome.release
@@ -38,6 +38,8 @@
 
 [TOUCH]
 chrome_touch_100_percent.pak: %(VersionDir)s\
+chrome_touch_140_percent.pak: %(VersionDir)s\
+chrome_touch_180_percent.pak: %(VersionDir)s\
 
 [GOOGLE_CHROME]
 SecondaryTile.png: %(VersionDir)s\
diff --git a/chrome/installer/mini_installer/mini_installer.cc b/chrome/installer/mini_installer/mini_installer.cc
index 4414ea2..28a8057 100644
--- a/chrome/installer/mini_installer/mini_installer.cc
+++ b/chrome/installer/mini_installer/mini_installer.cc
@@ -751,6 +751,15 @@
 // Main function. First gets a working dir, unpacks the resources and finally
 // executes setup.exe to do the install/upgrade.
 int WMain(HMODULE module) {
+#if defined(COMPONENT_BUILD)
+  static const wchar_t kComponentBuildIncompatibleMessage[] =
+      L"mini_installer.exe is incompatible with the component build, please run"
+      L" setup.exe with the same command line instead. See"
+      L" http://crbug.com/127233#c17 for details.";
+  ::MessageBox(NULL, kComponentBuildIncompatibleMessage, NULL, MB_ICONERROR);
+  return 1;
+#endif
+
   // Always start with deleting potential leftovers from previous installations.
   // This can make the difference between success and failure.  We've seen
   // many installations out in the field fail due to out of disk space problems
diff --git a/chrome/renderer/autofill/form_autofill_browsertest.cc b/chrome/renderer/autofill/form_autofill_browsertest.cc
index 68fa4c7..aa9565e 100644
--- a/chrome/renderer/autofill/form_autofill_browsertest.cc
+++ b/chrome/renderer/autofill/form_autofill_browsertest.cc
@@ -10,12 +10,12 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/common/metrics/entropy_provider.h"
 #include "chrome/test/base/chrome_render_view_test.h"
 #include "components/autofill/content/renderer/form_autofill_util.h"
 #include "components/autofill/content/renderer/form_cache.h"
 #include "components/autofill/core/common/form_data.h"
 #include "components/autofill/core/common/web_element_descriptor.h"
+#include "components/variations/entropy_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/WebKit/public/platform/WebString.h"
 #include "third_party/WebKit/public/platform/WebVector.h"
diff --git a/chrome/renderer/chrome_render_view_observer.cc b/chrome/renderer/chrome_render_view_observer.cc
index 096fa50..4519910 100644
--- a/chrome/renderer/chrome_render_view_observer.cc
+++ b/chrome/renderer/chrome_render_view_observer.cc
@@ -378,7 +378,8 @@
 
   Send(new ChromeViewHostMsg_DidRetrieveWebappInformation(routing_id(),
                                                           success,
-                                                          webapp_capable));
+                                                          webapp_capable,
+                                                          expected_url));
 }
 #endif
 
@@ -423,15 +424,17 @@
     int thumbnail_min_area_pixels, gfx::Size thumbnail_max_size_pixels) {
   WebNode context_node = render_view()->GetContextMenuNode();
   SkBitmap thumbnail;
+  gfx::Size original_size;
   CHECK(!context_node.isNull());
   if (context_node.isElementNode()) {
     WebKit::WebImage image = context_node.to<WebElement>().imageContents();
+    original_size = image.size();
     thumbnail = Downscale(image,
                           thumbnail_min_area_pixels,
                           thumbnail_max_size_pixels);
   }
-  Send(new ChromeViewHostMsg_RequestThumbnailForContextNode_ACK(routing_id(),
-                                                                thumbnail));
+  Send(new ChromeViewHostMsg_RequestThumbnailForContextNode_ACK(
+      routing_id(), thumbnail, original_size));
 }
 
 void ChromeRenderViewObserver::OnStartFrameSniffer(const string16& frame_name) {
diff --git a/chrome/renderer/extensions/css_native_handler.cc b/chrome/renderer/extensions/css_native_handler.cc
new file mode 100644
index 0000000..31f67c7
--- /dev/null
+++ b/chrome/renderer/extensions/css_native_handler.cc
@@ -0,0 +1,35 @@
+// 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 "chrome/renderer/extensions/css_native_handler.h"
+
+#include "chrome/renderer/extensions/chrome_v8_context.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/web/WebScriptBindings.h"
+#include "third_party/WebKit/public/web/WebSelector.h"
+
+namespace extensions {
+
+using WebKit::WebString;
+
+CssNativeHandler::CssNativeHandler(ChromeV8Context* context)
+    : ObjectBackedNativeHandler(context) {
+  RouteFunction("CanonicalizeCompoundSelector",
+                base::Bind(&CssNativeHandler::CanonicalizeCompoundSelector,
+                           base::Unretained(this)));
+}
+
+void CssNativeHandler::CanonicalizeCompoundSelector(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  CHECK_EQ(args.Length(), 1);
+  CHECK(args[0]->IsString());
+  WebString input_selector =
+      WebKit::WebScriptBindings::toWebString(args[0].As<v8::String>());
+  WebString output_selector = WebKit::canonicalizeSelector(
+      input_selector, WebKit::WebSelectorTypeCompound);
+  args.GetReturnValue().Set(WebKit::WebScriptBindings::toV8String(
+      output_selector, context()->v8_context()->GetIsolate()));
+}
+
+}  // namespace extensions
diff --git a/chrome/renderer/extensions/css_native_handler.h b/chrome/renderer/extensions/css_native_handler.h
new file mode 100644
index 0000000..97ea62a
--- /dev/null
+++ b/chrome/renderer/extensions/css_native_handler.h
@@ -0,0 +1,27 @@
+// 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 CHROME_RENDERER_EXTENSIONS_CSS_NATIVE_HANDLER_H_
+#define CHROME_RENDERER_EXTENSIONS_CSS_NATIVE_HANDLER_H_
+
+#include "chrome/renderer/extensions/object_backed_native_handler.h"
+
+namespace extensions {
+
+class CssNativeHandler : public ObjectBackedNativeHandler {
+ public:
+  explicit CssNativeHandler(ChromeV8Context* context);
+
+ private:
+  // Expects one string argument that's a comma-separated list of compound CSS
+  // selectors (http://dev.w3.org/csswg/selectors4/#compound), and returns its
+  // Blink-canonicalized form. If the selector is invalid, returns an empty
+  // string.
+  void CanonicalizeCompoundSelector(
+      const v8::FunctionCallbackInfo<v8::Value>& args);
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_RENDERER_EXTENSIONS_CSS_NATIVE_HANDLER_H_
diff --git a/chrome/renderer/extensions/dispatcher.cc b/chrome/renderer/extensions/dispatcher.cc
index f3df304..7ed9437 100644
--- a/chrome/renderer/extensions/dispatcher.cc
+++ b/chrome/renderer/extensions/dispatcher.cc
@@ -41,6 +41,7 @@
 #include "chrome/renderer/extensions/chrome_v8_extension.h"
 #include "chrome/renderer/extensions/content_watcher.h"
 #include "chrome/renderer/extensions/context_menus_custom_bindings.h"
+#include "chrome/renderer/extensions/css_native_handler.h"
 #include "chrome/renderer/extensions/document_custom_bindings.h"
 #include "chrome/renderer/extensions/dom_activity_logger.h"
 #include "chrome/renderer/extensions/event_bindings.h"
@@ -855,6 +856,8 @@
   module_system->RegisterNativeHandler("context_menus",
       scoped_ptr<NativeHandler>(
           new ContextMenusCustomBindings(this, context)));
+  module_system->RegisterNativeHandler(
+      "css_natives", scoped_ptr<NativeHandler>(new CssNativeHandler(context)));
   module_system->RegisterNativeHandler("document_natives",
       scoped_ptr<NativeHandler>(
           new DocumentCustomBindings(this, context)));
@@ -1276,7 +1279,7 @@
        i != origins.end(); ++i) {
     const char* schemes[] = {
       chrome::kHttpScheme,
-      chrome::kHttpsScheme,
+      content::kHttpsScheme,
       chrome::kFileScheme,
       chrome::kChromeUIScheme,
     };
diff --git a/chrome/renderer/extensions/user_script_scheduler.cc b/chrome/renderer/extensions/user_script_scheduler.cc
index f3ef86f..558ada0 100644
--- a/chrome/renderer/extensions/user_script_scheduler.cc
+++ b/chrome/renderer/extensions/user_script_scheduler.cc
@@ -11,6 +11,7 @@
 #include "chrome/common/extensions/extension_messages.h"
 #include "chrome/common/extensions/permissions/permissions_data.h"
 #include "chrome/renderer/chrome_render_process_observer.h"
+#include "chrome/renderer/extensions/chrome_v8_context.h"
 #include "chrome/renderer/extensions/dispatcher.h"
 #include "chrome/renderer/extensions/dom_activity_logger.h"
 #include "chrome/renderer/extensions/extension_groups.h"
@@ -199,12 +200,11 @@
       }
 
       WebScriptSource source(WebString::fromUTF8(params.code));
-      v8::Isolate* isolate = v8::Isolate::GetCurrent();
-      v8::HandleScope scope(isolate);
+      v8::HandleScope scope;
 
       scoped_ptr<content::V8ValueConverter> v8_converter(
           content::V8ValueConverter::create());
-      v8::Handle<v8::Value> script_value;
+      v8::Local<v8::Value> script_value;
 
       if (params.in_main_world) {
         DOMActivityLogger::AttachToWorld(
@@ -232,15 +232,17 @@
         if (results.size() == 1 && !results[0].IsEmpty())
           script_value = results[0];
       }
-      if (!script_value.IsEmpty()) {
-        v8::Local<v8::Context> context = v8::Context::New(isolate);
-        base::Value* base_val =
-            v8_converter->FromV8Value(script_value, context);
+
+      if (params.wants_result && !script_value.IsEmpty()) {
+        // It's safe to always use the main world context when converting here.
+        // V8ValueConverterImpl shouldn't actually care about the context scope,
+        // and it switches to v8::Object's creation context when encountered.
+        v8::Local<v8::Context> context = child_frame->mainWorldScriptContext();
+        base::Value* result = v8_converter->FromV8Value(script_value, context);
         // Always append an execution result (i.e. no result == null result) so
         // that |execution_results| lines up with the frames.
-        execution_results.Append(base_val ? base_val :
-                                            base::Value::CreateNullValue());
-        script_value.Clear();
+        execution_results.Append(
+            result ? result : base::Value::CreateNullValue());
       }
     } else {
       child_frame->document().insertUserStyleSheet(
diff --git a/chrome/renderer/extensions/user_script_slave.cc b/chrome/renderer/extensions/user_script_slave.cc
index d309188..0b53c17 100644
--- a/chrome/renderer/extensions/user_script_slave.cc
+++ b/chrome/renderer/extensions/user_script_slave.cc
@@ -10,9 +10,9 @@
 #include "base/logging.h"
 #include "base/memory/shared_memory.h"
 #include "base/metrics/histogram.h"
-#include "base/perftimer.h"
 #include "base/pickle.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/perftimer.h"
 #include "chrome/common/extensions/csp_handler.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/extension_messages.h"
@@ -105,7 +105,7 @@
        i != permissions.end(); ++i) {
     const char* schemes[] = {
       chrome::kHttpScheme,
-      chrome::kHttpsScheme,
+      content::kHttpsScheme,
       chrome::kFileScheme,
       chrome::kChromeUIScheme,
     };
diff --git a/chrome/renderer/page_load_histograms.cc b/chrome/renderer/page_load_histograms.cc
index d7795a8..b712d7f 100644
--- a/chrome/renderer/page_load_histograms.cc
+++ b/chrome/renderer/page_load_histograms.cc
@@ -728,43 +728,6 @@
     }
   }
 
-  // Histograms to determine if disabling overlapped TCP reads
-  // has an impact on PLT.
-  static const bool use_overlapped_read_histogram =
-      base::FieldTrialList::TrialExists("OverlappedReadImpact");
-  if (use_overlapped_read_histogram) {
-    UMA_HISTOGRAM_ENUMERATION(
-        base::FieldTrial::MakeName("PLT.Abandoned", "OverlappedReadImpact"),
-        abandoned_page ? 1 : 0, 2);
-    UMA_HISTOGRAM_ENUMERATION(
-        base::FieldTrial::MakeName("PLT.LoadType", "OverlappedReadImpact"),
-        load_type, DocumentState::kLoadTypeMax);
-    switch (load_type) {
-      case DocumentState::NORMAL_LOAD:
-        PLT_HISTOGRAM(base::FieldTrial::MakeName(
-            "PLT.BeginToFinish_NormalLoad", "OverlappedReadImpact"),
-            begin_to_finish_all_loads);
-        break;
-      case DocumentState::LINK_LOAD_NORMAL:
-        PLT_HISTOGRAM(base::FieldTrial::MakeName(
-            "PLT.BeginToFinish_LinkLoadNormal", "OverlappedReadImpact"),
-            begin_to_finish_all_loads);
-        break;
-      case DocumentState::LINK_LOAD_RELOAD:
-        PLT_HISTOGRAM(base::FieldTrial::MakeName(
-            "PLT.BeginToFinish_LinkLoadReload", "OverlappedReadImpact"),
-            begin_to_finish_all_loads);
-        break;
-      case DocumentState::LINK_LOAD_CACHE_STALE_OK:
-        PLT_HISTOGRAM(base::FieldTrial::MakeName(
-            "PLT.BeginToFinish_LinkLoadStaleOk", "OverlappedReadImpact"),
-            begin_to_finish_all_loads);
-        break;
-      default:
-        break;
-    }
-  }
-
   // Site isolation metrics.
   UMA_HISTOGRAM_COUNTS("SiteIsolation.PageLoadsWithCrossSiteFrameAccess",
                        cross_origin_access_count_);
diff --git a/chrome/renderer/plugins/plugin_placeholder.cc b/chrome/renderer/plugins/plugin_placeholder.cc
index eae3e0e..0e58e72 100644
--- a/chrome/renderer/plugins/plugin_placeholder.cc
+++ b/chrome/renderer/plugins/plugin_placeholder.cc
@@ -323,7 +323,7 @@
 
   // The plug-in has been removed from the page. Destroy the old plug-in
   // (which will destroy us).
-  if (element.parentNode().isNull()) {
+  if (!element.pluginContainer()) {
     plugin_->destroy();
     return;
   }
diff --git a/chrome/renderer/printing/print_web_view_helper_linux.cc b/chrome/renderer/printing/print_web_view_helper_linux.cc
index 312acd2..af965a9 100644
--- a/chrome/renderer/printing/print_web_view_helper_linux.cc
+++ b/chrome/renderer/printing/print_web_view_helper_linux.cc
@@ -167,8 +167,9 @@
   gfx::Rect canvas_area =
       params.params.display_header_footer ? gfx::Rect(page_size) : content_area;
 
-  SkDevice* device = metafile->StartPageForVectorCanvas(page_size, canvas_area,
-                                                        scale_factor);
+  SkBaseDevice* device = metafile->StartPageForVectorCanvas(page_size,
+                                                            canvas_area,
+                                                            scale_factor);
   if (!device)
     return;
 
diff --git a/chrome/renderer/printing/print_web_view_helper_mac.mm b/chrome/renderer/printing/print_web_view_helper_mac.mm
index fc2f5ab..6d15f26 100644
--- a/chrome/renderer/printing/print_web_view_helper_mac.mm
+++ b/chrome/renderer/printing/print_web_view_helper_mac.mm
@@ -118,7 +118,7 @@
       params.display_header_footer ? gfx::Rect(*page_size) : content_area;
 
   {
-    SkDevice* device = metafile->StartPageForVectorCanvas(
+    SkBaseDevice* device = metafile->StartPageForVectorCanvas(
         *page_size, canvas_area, scale_factor);
     if (!device)
       return;
diff --git a/chrome/renderer/printing/print_web_view_helper_win.cc b/chrome/renderer/printing/print_web_view_helper_win.cc
index f2203f2..ba3fd1a 100644
--- a/chrome/renderer/printing/print_web_view_helper_win.cc
+++ b/chrome/renderer/printing/print_web_view_helper_win.cc
@@ -171,7 +171,7 @@
   gfx::Rect canvas_area =
       params.display_header_footer ? gfx::Rect(page_size) : content_area;
 
-  SkDevice* device = metafile->StartPageForVectorCanvas(
+  SkBaseDevice* device = metafile->StartPageForVectorCanvas(
       page_size, canvas_area, scale_factor);
   DCHECK(device);
   // The printPage method may take a reference to the canvas we pass down, so it
diff --git a/chrome/renderer/resources/extensions/declarative_content_custom_bindings.js b/chrome/renderer/resources/extensions/declarative_content_custom_bindings.js
index beb5409..fe154f7 100644
--- a/chrome/renderer/resources/extensions/declarative_content_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/declarative_content_custom_bindings.js
@@ -8,6 +8,8 @@
 
 var utils = require('utils');
 var validate = require('schemaUtils').validate;
+var canonicalizeCompoundSelector =
+    requireNative('css_natives').CanonicalizeCompoundSelector;
 
 binding.registerCustomHook( function(api) {
   var declarativeContent = api.compiledApi;
@@ -35,9 +37,25 @@
     validate([instance], [schema]);
   }
 
+  function canonicalizeCssSelectors(selectors) {
+    for (var i = 0; i < selectors.length; i++) {
+      var canonicalizedSelector = canonicalizeCompoundSelector(selectors[i]);
+      if (canonicalizedSelector == '') {
+        throw new Error(
+            'Element of \'css\' array must be a ' +
+            'list of valid compound selectors: ' +
+            selectors[i]);
+      }
+      selectors[i] = canonicalizedSelector;
+    }
+  }
+
   // Setup all data types for the declarative content API.
   declarativeContent.PageStateMatcher = function(parameters) {
     setupInstance(this, parameters, 'PageStateMatcher');
+    if ($Object.hasOwnProperty(this, 'css')) {
+      canonicalizeCssSelectors(this.css);
+    }
   };
   declarativeContent.ShowPageAction = function(parameters) {
     setupInstance(this, parameters, 'ShowPageAction');
diff --git a/chrome/renderer/resources/extensions/file_system_custom_bindings.js b/chrome/renderer/resources/extensions/file_system_custom_bindings.js
index 921ff23..6fdf89d 100644
--- a/chrome/renderer/resources/extensions/file_system_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/file_system_custom_bindings.js
@@ -33,6 +33,17 @@
         var entries = [];
         var hasError = false;
 
+        var getEntryError = function(fileError) {
+          if (!hasError) {
+            hasError = true;
+            lastError.run(
+                'fileSystem.' + functionName,
+                'Error getting fileEntry, code: ' + fileError.code,
+                request.stack,
+                callback);
+          }
+        }
+
         // Loop through the response entries and asynchronously get the
         // FileEntry for each. We use hasError to ensure that only the first
         // error is reported. Note that an error can occur either during the
@@ -46,10 +57,7 @@
           var fs = GetIsolatedFileSystem(fileSystemId);
 
           try {
-            // TODO(koz): fs.root.getFile() makes a trip to the browser process,
-            // but it might be possible avoid that by calling
-            // WebFrame::createFileEntry().
-            fs.root.getFile(baseName, {}, function(fileEntry) {
+            var getEntryCallback = function(fileEntry) {
               if (hasError)
                 return;
               entryIdManager.registerEntry(id, fileEntry);
@@ -64,16 +72,16 @@
                   callback(entries[0]);
                 }
               }
-            }, function(fileError) {
-              if (!hasError) {
-                hasError = true;
-                lastError.run(
-                    'fileSystem.' + functionName,
-                    'Error getting fileEntry, code: ' + fileError.code,
-                    request.stack,
-                    callback);
-              }
-            });
+            }
+            // TODO(koz): fs.root.getFile() makes a trip to the browser process,
+            // but it might be possible avoid that by calling
+            // WebFrame::createFileEntry().
+            if (entry.isDirectory) {
+              fs.root.getDirectory(baseName, {}, getEntryCallback,
+                                   getEntryError);
+            } else {
+              fs.root.getFile(baseName, {}, getEntryCallback, getEntryError);
+            }
           } catch (e) {
             if (!hasError) {
               hasError = true;
diff --git a/chrome/renderer/resources/extensions/searchbox_api.js b/chrome/renderer/resources/extensions/searchbox_api.js
index 3aa3d53..48a9188 100644
--- a/chrome/renderer/resources/extensions/searchbox_api.js
+++ b/chrome/renderer/resources/extensions/searchbox_api.js
@@ -13,12 +13,15 @@
       // =======================================================================
       //                            Private functions
       // =======================================================================
+      native function Focus();
+      native function GetDisplayInstantResults();
       native function GetFont();
       native function GetFontSize();
       native function GetMostVisitedItemData();
       native function GetQuery();
       native function GetRightToLeft();
       native function GetStartMargin();
+      native function GetSuggestionToPrefetch();
       native function IsFocused();
       native function IsKeyCaptureEnabled();
       native function Paste();
@@ -29,14 +32,20 @@
       // =======================================================================
       //                           Exported functions
       // =======================================================================
+      this.__defineGetter__('displayInstantResults', GetDisplayInstantResults);
       this.__defineGetter__('font', GetFont);
       this.__defineGetter__('fontSize', GetFontSize);
       this.__defineGetter__('isFocused', IsFocused);
       this.__defineGetter__('isKeyCaptureEnabled', IsKeyCaptureEnabled);
       this.__defineGetter__('rtl', GetRightToLeft);
       this.__defineGetter__('startMargin', GetStartMargin);
+      this.__defineGetter__('suggestion', GetSuggestionToPrefetch);
       this.__defineGetter__('value', GetQuery);
 
+      this.focus = function() {
+        Focus();
+      };
+
       // This method is restricted to chrome-search://most-visited pages by
       // checking the invoking context's origin in searchbox_extension.cc.
       this.getMostVisitedItemData = function(restrictedId) {
@@ -63,10 +72,10 @@
       this.onkeycapturechange = null;
       this.onmarginchange = null;
       this.onsubmit = null;
+      this.onsuggestionchange = null;
       this.ontogglevoicesearch = null;
 
-      // TODO(jered): Remove these when google no longer requires them.
-      this.displayInstantResults = false;
+      //TODO(jered): Remove this empty method when google no longer requires it.
       this.setRestrictedValue = function() {};
     };
 
diff --git a/chrome/renderer/resources/extensions/test_custom_bindings.js b/chrome/renderer/resources/extensions/test_custom_bindings.js
index cc2681e..7df6f5d 100644
--- a/chrome/renderer/resources/extensions/test_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/test_custom_bindings.js
@@ -235,8 +235,14 @@
       fn.apply(self, args);
       chromeTest.fail('Did not throw error: ' + fn);
     } catch (e) {
-      if (message !== undefined)
-        chromeTest.assertEq(message, e.message);
+      if (e != failureException && message !== undefined) {
+        if (message instanceof RegExp) {
+          chromeTest.assertTrue(message.test(e.message),
+                                e.message + ' should match ' + message)
+        } else {
+          chromeTest.assertEq(message, e.message);
+        }
+      }
     }
   });
 
diff --git a/chrome/renderer/resources/extensions/web_view.js b/chrome/renderer/resources/extensions/web_view.js
index fbd0684..64cdf50 100644
--- a/chrome/renderer/resources/extensions/web_view.js
+++ b/chrome/renderer/resources/extensions/web_view.js
@@ -9,12 +9,13 @@
 
 'use strict';
 
-var eventBindings = require('event_bindings');
 var DocumentNatives = requireNative('document_natives');
-var messagingNatives = requireNative('messaging_natives');
+var EventBindings = require('event_bindings');
+var MessagingNatives = requireNative('messaging_natives');
 var WebRequestEvent = require('webRequestInternal').WebRequestEvent;
-var webRequestSchema =
+var WebRequestSchema =
     requireNative('schema_registry').GetSchema('webRequest');
+var WebView = require('binding').Binding.create('webview').generate();
 
 // This secret enables hiding <webview> private members from the outside scope.
 // Outside of this file, |secret| is inaccessible. The only way to access the
@@ -23,69 +24,75 @@
 // API can access it and not external developers.
 var secret = {};
 
+var WEB_VIEW_ATTRIBUTE_MAXHEIGHT = 'maxheight';
+var WEB_VIEW_ATTRIBUTE_MAXWIDTH = 'maxwidth';
+var WEB_VIEW_ATTRIBUTE_MINHEIGHT = 'minheight';
+var WEB_VIEW_ATTRIBUTE_MINWIDTH = 'minwidth';
+
 /** @type {Array.<string>} */
-var WEB_VIEW_ATTRIBUTES = ['name', 'src', 'partition', 'autosize', 'minheight',
-    'minwidth', 'maxheight', 'maxwidth'];
+var WEB_VIEW_ATTRIBUTES = [
+    'name',
+    'partition',
+    'autosize',
+    WEB_VIEW_ATTRIBUTE_MINHEIGHT,
+    WEB_VIEW_ATTRIBUTE_MINWIDTH,
+    WEB_VIEW_ATTRIBUTE_MAXHEIGHT,
+    WEB_VIEW_ATTRIBUTE_MAXWIDTH
+];
 
 var webViewInstanceIdCounter = 0;
 
-var createEvent = function(name) {
+var CreateEvent = function(name) {
   var eventOpts = {supportsListeners: true, supportsFilters: true};
-  return new eventBindings.Event(name, undefined, eventOpts);
+  return new EventBindings.Event(name, undefined, eventOpts);
 };
 
-var WEB_VIEW_EXT_EVENTS = {
+var WEB_VIEW_EVENTS = {
   'close': {
-    evt: createEvent('webview.onClose'),
+    evt: CreateEvent('webview.onClose'),
     fields: []
   },
   'consolemessage': {
-    evt: createEvent('webview.onConsoleMessage'),
+    evt: CreateEvent('webview.onConsoleMessage'),
     fields: ['level', 'message', 'line', 'sourceId']
   },
   'contentload': {
-    evt: createEvent('webview.onContentLoad'),
+    evt: CreateEvent('webview.onContentLoad'),
     fields: []
   },
   'exit': {
-     evt: createEvent('webview.onExit'),
+     evt: CreateEvent('webview.onExit'),
      fields: ['processId', 'reason']
   },
   'loadabort': {
-    evt: createEvent('webview.onLoadAbort'),
+    evt: CreateEvent('webview.onLoadAbort'),
     fields: ['url', 'isTopLevel', 'reason']
   },
   'loadcommit': {
-    customHandler: function(webview, event, webviewEvent) {
-      webview.currentEntryIndex_ = event.currentEntryIndex;
-      webview.entryCount_ = event.entryCount;
-      webview.processId_ = event.processId;
-      if (event.isTopLevel) {
-        webview.browserPluginNode_.setAttribute('src', event.url);
-      }
-      webview.webviewNode_.dispatchEvent(webviewEvent);
+    customHandler: function(webViewInternal, event, webViewEvent) {
+      webViewInternal.handleLoadCommitEvent_(event, webViewEvent);
     },
-    evt: createEvent('webview.onLoadCommit'),
+    evt: CreateEvent('webview.onLoadCommit'),
     fields: ['url', 'isTopLevel']
   },
   'loadredirect': {
-    evt: createEvent('webview.onLoadRedirect'),
+    evt: CreateEvent('webview.onLoadRedirect'),
     fields: ['isTopLevel', 'oldUrl', 'newUrl']
   },
   'loadstart': {
-    evt: createEvent('webview.onLoadStart'),
+    evt: CreateEvent('webview.onLoadStart'),
     fields: ['url', 'isTopLevel']
   },
   'loadstop': {
-    evt: createEvent('webview.onLoadStop'),
+    evt: CreateEvent('webview.onLoadStop'),
     fields: []
   },
   'newwindow': {
     cancelable: true,
-    customHandler: function(webview, event, webviewEvent) {
-      webview.setupExtNewWindowEvent_(event, webviewEvent);
+    customHandler: function(webViewInternal, event, webViewEvent) {
+      webViewInternal.handleNewWindowEvent_(event, webViewEvent);
     },
-    evt: createEvent('webview.onNewWindow'),
+    evt: CreateEvent('webview.onNewWindow'),
     fields: [
       'initialHeight',
       'initialWidth',
@@ -96,10 +103,10 @@
   },
   'permissionrequest': {
     cancelable: true,
-    customHandler: function(webview, event, webviewEvent) {
-      webview.setupExtPermissionEvent_(event, webviewEvent);
+    customHandler: function(webViewInternal, event, webViewEvent) {
+      webViewInternal.handlePermissionEvent_(event, webViewEvent);
     },
-    evt: createEvent('webview.onPermissionRequest'),
+    evt: CreateEvent('webview.onPermissionRequest'),
     fields: [
       'lastUnlockedBySelf',
       'permission',
@@ -109,15 +116,18 @@
     ]
   },
   'responsive': {
-    evt: createEvent('webview.onResponsive'),
+    evt: CreateEvent('webview.onResponsive'),
     fields: ['processId']
   },
   'sizechanged': {
-    evt: createEvent('webview.onSizeChanged'),
+    evt: CreateEvent('webview.onSizeChanged'),
+    customHandler: function(webViewInternal, event, webViewEvent) {
+      webViewInternal.handleSizeChangedEvent_(event, webViewEvent);
+    },
     fields: ['oldHeight', 'oldWidth', 'newHeight', 'newWidth']
   },
   'unresponsive': {
-    evt: createEvent('webview.onUnresponsive'),
+    evt: CreateEvent('webview.onUnresponsive'),
     fields: ['processId']
   }
 };
@@ -158,7 +168,8 @@
     }.bind(this)
   });
 
-  $Array.forEach(WEB_VIEW_ATTRIBUTES, function(attributeName) {
+  var ALL_ATTRIBUTES = WEB_VIEW_ATTRIBUTES.concat(['src']);
+  $Array.forEach(ALL_ATTRIBUTES, function(attributeName) {
     // Only copy attributes that have been assigned values, rather than copying
     // a series of undefined attributes to BrowserPlugin.
     if (this.webviewNode_.hasAttribute(attributeName)) {
@@ -228,7 +239,7 @@
   if (!this.instanceId_) {
     return;
   }
-  chrome.webview.go(this.instanceId_, relativeIndex);
+  WebView.go(this.instanceId_, relativeIndex);
 };
 
 /**
@@ -238,7 +249,7 @@
   if (!this.instanceId_) {
     return;
   }
-  chrome.webview.reload(this.instanceId_);
+  WebView.reload(this.instanceId_);
 };
 
 /**
@@ -248,7 +259,7 @@
   if (!this.instanceId_) {
     return;
   }
-  chrome.webview.stop(this.instanceId_);
+  WebView.stop(this.instanceId_);
 };
 
 /**
@@ -258,7 +269,7 @@
   if (!this.instanceId_) {
     return;
   }
-  chrome.webview.terminate(this.instanceId_);
+  WebView.terminate(this.instanceId_);
 };
 
 /**
@@ -278,7 +289,7 @@
 WebViewInternal.prototype.executeScript_ = function(var_args) {
   this.validateExecuteCodeCall_();
   var args = $Array.concat([this.instanceId_], $Array.slice(arguments));
-  $Function.apply(chrome.webview.executeScript, null, args);
+  $Function.apply(WebView.executeScript, null, args);
 };
 
 /**
@@ -287,7 +298,7 @@
 WebViewInternal.prototype.insertCSS_ = function(var_args) {
   this.validateExecuteCodeCall_();
   var args = $Array.concat([this.instanceId_], $Array.slice(arguments));
-  $Function.apply(chrome.webview.insertCSS, null, args);
+  $Function.apply(WebView.insertCSS, null, args);
 };
 
 /**
@@ -298,6 +309,7 @@
     'contentWindow is not available at this time. It will become available ' +
         'when the page has finished loading.';
 
+  var self = this;
   var browserPluginNode = this.browserPluginNode_;
   // Expose getters and setters for the attributes.
   $Array.forEach(WEB_VIEW_ATTRIBUTES, function(attributeName) {
@@ -323,6 +335,20 @@
     });
   }, this);
 
+  // <webview> src does not quite behave the same as BrowserPlugin src, and so
+  // we don't simply keep the two in sync.
+  this.src_ = this.webviewNode_.getAttribute('src');
+  Object.defineProperty(this.webviewNode_, 'src', {
+    get: function() {
+      return self.src_;
+    },
+    set: function(value) {
+      self.webviewNode_.setAttribute('src', value);
+    },
+    // No setter.
+    enumerable: true
+  });
+
   // We cannot use {writable: true} property descriptor because we want a
   // dynamic getter value.
   Object.defineProperty(this.webviewNode_, 'contentWindow', {
@@ -363,13 +389,15 @@
   // where the webview guest has crashed and navigating to the same address
   // spawns off a new process.
   var self = this;
-  var observer = new MutationObserver(function(mutations) {
+  this.srcObserver_ = new MutationObserver(function(mutations) {
     $Array.forEach(mutations, function(mutation) {
+      var oldValue = mutation.oldValue;
       var newValue = self.webviewNode_.getAttribute(mutation.attributeName);
-      if (mutation.oldValue != newValue) {
+      if (oldValue != newValue) {
         return;
       }
-      self.handleWebviewAttributeMutation_(mutation.attributeName, newValue);
+      self.handleWebviewAttributeMutation_(
+          mutation.attributeName, oldValue, newValue);
     });
   });
   var params = {
@@ -377,19 +405,43 @@
     attributeOldValue: true,
     attributeFilter: ['src']
   };
-  observer.observe(this.webviewNode_, params);
+  this.srcObserver_.observe(this.webviewNode_, params);
 };
 
 /**
  * @private
  */
 WebViewInternal.prototype.handleWebviewAttributeMutation_ =
-      function(name, newValue) {
+      function(name, oldValue, newValue) {
   // This observer monitors mutations to attributes of the <webview> and
   // updates the BrowserPlugin properties accordingly. In turn, updating
   // a BrowserPlugin property will update the corresponding BrowserPlugin
   // attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more
   // details.
+  if (name == 'src') {
+    // We treat null attribute (attribute removed) and the empty string as
+    // one case.
+    oldValue = oldValue || '';
+    newValue = newValue || '';
+    // Once we have navigated, we don't allow clearing the src attribute.
+    // Once <webview> enters a navigated state, it cannot be return back to a
+    // placeholder state.
+    if (newValue == '' && oldValue != '') {
+      // src attribute changes normally initiate a navigation. We suppress
+      // the next src attribute handler call to avoid reloading the page
+      // on every guest-initiated navigation.
+      this.ignoreNextSrcAttributeChange_ = true;
+      this.webviewNode_.setAttribute('src', oldValue);
+      return;
+    }
+    this.src_ = newValue;
+    if (this.ignoreNextSrcAttributeChange_) {
+      // Don't allow the src mutation observer to see this change.
+      this.srcObserver_.takeRecords();
+      this.ignoreNextSrcAttributeChange_ = false;
+      return;
+    }
+  }
   if (this.browserPluginNode_.hasOwnProperty(name)) {
     this.browserPluginNode_[name] = newValue;
   } else {
@@ -401,7 +453,7 @@
  * @private
  */
 WebViewInternal.prototype.handleBrowserPluginAttributeMutation_ =
-    function(name, oldValue, newValue) {
+    function(name, newValue) {
   // This observer monitors mutations to attributes of the BrowserPlugin and
   // updates the <webview> attributes accordingly.
   // |newValue| is null if the attribute |name| has been removed.
@@ -413,7 +465,7 @@
     // again (such as navigation when crashed), this could end up in an infinite
     // loop. Thus, we avoid this loop by only updating the <webview> attribute
     // if the BrowserPlugin attributes differs from it.
-    if (newValue != oldValue) {
+    if (newValue != this.webviewNode_.getAttribute(name)) {
       this.webviewNode_.setAttribute(name, newValue);
     }
   } else {
@@ -426,12 +478,60 @@
 /**
  * @private
  */
-WebViewInternal.prototype.getWebviewExtEvents_ = function() {
-  var experimentalExtEvents = this.maybeGetWebviewExperimentalExtEvents_();
-  for (var eventName in experimentalExtEvents) {
-    WEB_VIEW_EXT_EVENTS[eventName] = experimentalExtEvents[eventName];
+WebViewInternal.prototype.getEvents_ = function() {
+  var experimentalEvents = this.maybeGetExperimentalEvents_();
+  for (var eventName in experimentalEvents) {
+    WEB_VIEW_EVENTS[eventName] = experimentalEvents[eventName];
   }
-  return WEB_VIEW_EXT_EVENTS;
+  return WEB_VIEW_EVENTS;
+};
+
+WebViewInternal.prototype.handleSizeChangedEvent_ =
+    function(event, webViewEvent) {
+  var node = this.webviewNode_;
+
+  // Check the current bounds to make sure we do not resize <webview>
+  // outside of current constraints.
+  var minWidth = 0;
+  if (node.hasAttribute(WEB_VIEW_ATTRIBUTE_MINWIDTH) &&
+      node[WEB_VIEW_ATTRIBUTE_MINWIDTH]) {
+    minWidth = node[WEB_VIEW_ATTRIBUTE_MINWIDTH];
+  }
+  var maxWidth;
+  if (node.hasAttribute(WEB_VIEW_ATTRIBUTE_MAXWIDTH) &&
+      node[WEB_VIEW_ATTRIBUTE_MAXWIDTH]) {
+    maxWidth = node[WEB_VIEW_ATTRIBUTE_MAXWIDTH];
+  } else {
+    maxWidth = node.offsetWidth;
+  }
+  if (minWidth > maxWidth) {
+    minWidth = maxWidth;
+  }
+
+  var minHeight = 0;
+  if (node.hasAttribute(WEB_VIEW_ATTRIBUTE_MINHEIGHT) &&
+      node[WEB_VIEW_ATTRIBUTE_MINHEIGHT]) {
+    minHeight = node[WEB_VIEW_ATTRIBUTE_MINHEIGHT];
+  }
+  var maxHeight;
+  if (node.hasAttribute(WEB_VIEW_ATTRIBUTE_MAXHEIGHT) &&
+      node[WEB_VIEW_ATTRIBUTE_MAXHEIGHT]) {
+    maxHeight = node[WEB_VIEW_ATTRIBUTE_MAXHEIGHT];
+  } else {
+    maxHeight = node.offsetHeight;
+  }
+  if (minHeight > maxHeight) {
+    minHeight = maxHeight;
+  }
+
+  if (webViewEvent.newWidth >= minWidth &&
+      webViewEvent.newWidth <= maxWidth &&
+      webViewEvent.newHeight >= minHeight &&
+      webViewEvent.newHeight <= maxHeight) {
+    node.style.width = webViewEvent.newWidth + 'px';
+    node.style.height = webViewEvent.newHeight + 'px';
+  }
+  node.dispatchEvent(webViewEvent);
 };
 
 /**
@@ -449,9 +549,9 @@
     };
     self.browserPluginNode_['-internal-attach'](params);
 
-    var extEvents = self.getWebviewExtEvents_();
-    for (var eventName in extEvents) {
-      self.setupExtEvent_(eventName, extEvents[eventName]);
+    var events = self.getEvents_();
+    for (var eventName in events) {
+      self.setupEvent_(eventName, events[eventName]);
     }
   };
   this.browserPluginNode_.addEventListener('-internal-instanceid-allocated',
@@ -462,32 +562,59 @@
 /**
  * @private
  */
-WebViewInternal.prototype.setupExtEvent_ = function(eventName, eventInfo) {
+WebViewInternal.prototype.setupEvent_ = function(eventName, eventInfo) {
   var self = this;
   var webviewNode = this.webviewNode_;
   eventInfo.evt.addListener(function(event) {
     var details = {bubbles:true};
     if (eventInfo.cancelable)
       details.cancelable = true;
-    var webviewEvent = new Event(eventName, details);
+    var webViewEvent = new Event(eventName, details);
     $Array.forEach(eventInfo.fields, function(field) {
       if (event[field] !== undefined) {
-        webviewEvent[field] = event[field];
+        webViewEvent[field] = event[field];
       }
     });
     if (eventInfo.customHandler) {
-      eventInfo.customHandler(self, event, webviewEvent);
+      eventInfo.customHandler(self, event, webViewEvent);
       return;
     }
-    webviewNode.dispatchEvent(webviewEvent);
+    webviewNode.dispatchEvent(webViewEvent);
   }, {instanceId: self.instanceId_});
 };
 
 /**
  * @private
  */
-WebViewInternal.prototype.setupExtNewWindowEvent_ =
-    function(event, webviewEvent) {
+WebViewInternal.prototype.getPermissionTypes_ = function() {
+  return ['media', 'geolocation', 'pointerLock', 'download'];
+};
+
+/**
+ * @private
+ */
+WebViewInternal.prototype.handleLoadCommitEvent_ =
+    function(event, webViewEvent) {
+  this.currentEntryIndex_ = event.currentEntryIndex;
+  this.entryCount_ = event.entryCount;
+  this.processId_ = event.processId;
+  var oldValue = this.webviewNode_.getAttribute('src');
+  var newValue = event.url;
+  if (event.isTopLevel && (oldValue != newValue)) {
+    // Touching the src attribute triggers a navigation. To avoid
+    // triggering a page reload on every guest-initiated navigation,
+    // we use the flag ignoreNextSrcAttributeChange_ here.
+    this.ignoreNextSrcAttributeChange_ = true;
+    this.webviewNode_.setAttribute('src', newValue);
+  }
+  this.webviewNode_.dispatchEvent(webViewEvent);
+}
+
+/**
+ * @private
+ */
+WebViewInternal.prototype.handleNewWindowEvent_ =
+    function(event, webViewEvent) {
   var ERROR_MSG_NEWWINDOW_ACTION_ALREADY_TAKEN = '<webview>: ' +
       'An action has already been taken for this "newwindow" event.';
 
@@ -536,45 +663,41 @@
         // then we will fail and it will be treated as if the new window
         // was rejected. The permission API plumbing is used here to clean
         // up the state created for the new window if attaching fails.
-        chrome.webview.setPermission(self.instanceId_, requestId, attached, '');
+        WebView.setPermission(self.instanceId_, requestId, attached, '');
       }, 0);
     },
     discard: function() {
       validateCall();
-      chrome.webview.setPermission(self.instanceId_, requestId, false, '');
+      WebView.setPermission(self.instanceId_, requestId, false, '');
     }
   };
-  webviewEvent.window = window;
+  webViewEvent.window = window;
 
-  var defaultPrevented = !webviewNode.dispatchEvent(webviewEvent);
+  var defaultPrevented = !webviewNode.dispatchEvent(webViewEvent);
   if (actionTaken) {
     return;
   }
 
   if (defaultPrevented) {
     // Make browser plugin track lifetime of |window|.
-    messagingNatives.BindToGC(window, function() {
+    MessagingNatives.BindToGC(window, function() {
       // Avoid showing a warning message if the decision has already been made.
       if (actionTaken) {
         return;
       }
-      chrome.webview.setPermission(self.instanceId_, requestId, false, '');
+      WebView.setPermission(self.instanceId_, requestId, false, '');
       showWarningMessage();
     });
   } else {
     actionTaken = true;
     // The default action is to discard the window.
-    chrome.webview.setPermission(self.instanceId_, requestId, false, '');
+    WebView.setPermission(self.instanceId_, requestId, false, '');
     showWarningMessage();
   }
 };
 
-WebViewInternal.prototype.getPermissionTypes_ = function() {
-  return ['media', 'geolocation', 'pointerLock', 'download'];
-};
-
-WebViewInternal.prototype.setupExtPermissionEvent_ =
-    function(event, webviewEvent) {
+WebViewInternal.prototype.handlePermissionEvent_ =
+    function(event, webViewEvent) {
   var ERROR_MSG_PERMISSION_ALREADY_DECIDED = '<webview>: ' +
       'Permission has already been decided for this "permissionrequest" event.';
 
@@ -604,33 +727,33 @@
   var request = {
     allow: function() {
       validateCall();
-      chrome.webview.setPermission(self.instanceId_, requestId, true, '');
+      WebView.setPermission(self.instanceId_, requestId, true, '');
     },
     deny: function() {
       validateCall();
-      chrome.webview.setPermission(self.instanceId_, requestId, false, '');
+      WebView.setPermission(self.instanceId_, requestId, false, '');
     }
   };
-  webviewEvent.request = request;
+  webViewEvent.request = request;
 
-  var defaultPrevented = !webviewNode.dispatchEvent(webviewEvent);
+  var defaultPrevented = !webviewNode.dispatchEvent(webViewEvent);
   if (decisionMade) {
     return;
   }
 
   if (defaultPrevented) {
     // Make browser plugin track lifetime of |request|.
-    messagingNatives.BindToGC(request, function() {
+    MessagingNatives.BindToGC(request, function() {
       // Avoid showing a warning message if the decision has already been made.
       if (decisionMade) {
         return;
       }
-      chrome.webview.setPermission(self.instanceId_, requestId, false, '');
+      WebView.setPermission(self.instanceId_, requestId, false, '');
       showWarningMessage(event.permission);
     });
   } else {
     decisionMade = true;
-    chrome.webview.setPermission(self.instanceId_, requestId, false, '');
+    WebView.setPermission(self.instanceId_, requestId, false, '');
     showWarningMessage(event.permission);
   }
 };
@@ -656,17 +779,17 @@
   };
 
   // Populate the WebRequest events from the API definition.
-  for (var i = 0; i < webRequestSchema.events.length; ++i) {
-    var webRequestEvent = createWebRequestEvent(webRequestSchema.events[i]);
+  for (var i = 0; i < WebRequestSchema.events.length; ++i) {
+    var webRequestEvent = createWebRequestEvent(WebRequestSchema.events[i]);
     Object.defineProperty(
         request,
-        webRequestSchema.events[i].name,
+        WebRequestSchema.events[i].name,
         {
           get: webRequestEvent,
           enumerable: true
         }
     );
-    this.maybeAttachWebRequestEventToWebview_(webRequestSchema.events[i].name,
+    this.maybeAttachWebRequestEventToWebview_(WebRequestSchema.events[i].name,
                                               webRequestEvent);
   }
   Object.defineProperty(
@@ -696,7 +819,7 @@
       return;
     }
     var internal = this.internal_(secret);
-    internal.handleBrowserPluginAttributeMutation_(name, oldValue, newValue);
+    internal.handleBrowserPluginAttributeMutation_(name, newValue);
   };
 
   WebViewInternal.BrowserPlugin =
@@ -718,7 +841,7 @@
 
   proto.attributeChangedCallback = function(name, oldValue, newValue) {
     var internal = this.internal_(secret);
-    internal.handleWebviewAttributeMutation_(name, newValue);
+    internal.handleWebviewAttributeMutation_(name, oldValue, newValue);
   };
 
   proto.back = function() {
@@ -793,13 +916,7 @@
  * Implemented when the experimental API is available.
  * @private
  */
-WebViewInternal.prototype.maybeSetupExtDialogEvent_ = function() {};
-
-/**
- * Implemented when the experimental API is available.
- * @private
- */
-WebViewInternal.prototype.maybeGetWebviewExperimentalExtEvents_ = function() {};
+WebViewInternal.prototype.maybeGetExperimentalEvents_ = function() {};
 
 /**
  * Implemented when the experimental API is available.
@@ -807,5 +924,6 @@
  */
 WebViewInternal.prototype.maybeAttachWebRequestEventToWebview_ = function() {};
 
+exports.WebView = WebView;
 exports.WebViewInternal = WebViewInternal;
-exports.CreateEvent = createEvent;
+exports.CreateEvent = CreateEvent;
diff --git a/chrome/renderer/resources/extensions/web_view_experimental.js b/chrome/renderer/resources/extensions/web_view_experimental.js
index 7b3dd83..afa20b1 100644
--- a/chrome/renderer/resources/extensions/web_view_experimental.js
+++ b/chrome/renderer/resources/extensions/web_view_experimental.js
@@ -11,20 +11,21 @@
 // permission API would only be available for channels CHANNEL_DEV and
 // CHANNEL_CANARY.
 
-var createEvent = require('webView').CreateEvent;
-var messagingNatives = requireNative('messaging_natives');
+var CreateEvent = require('webView').CreateEvent;
+var MessagingNatives = requireNative('messaging_natives');
 var WebRequestEvent = require('webRequestInternal').WebRequestEvent;
-var webRequestSchema =
+var WebRequestSchema =
     requireNative('schema_registry').GetSchema('webRequest');
 var WebViewInternal = require('webView').WebViewInternal;
+var WebView = require('webView').WebView;
 
-var WEB_VIEW_EXPERIMENTAL_EXT_EVENTS = {
+var WEB_VIEW_EXPERIMENTAL_EVENTS = {
   'dialog': {
     cancelable: true,
-    customHandler: function(webview, event, webviewEvent) {
-      webview.maybeSetupExtDialogEvent_(event, webviewEvent);
+    customHandler: function(webViewInternal, event, webViewEvent) {
+      webViewInternal.handleDialogEvent_(event, webViewEvent);
     },
-    evt: createEvent('webview.onDialog'),
+    evt: CreateEvent('webview.onDialog'),
     fields: ['defaultPromptText', 'messageText', 'messageType', 'url']
   }
 };
@@ -47,8 +48,8 @@
 /**
  * @private
  */
-WebViewInternal.prototype.maybeSetupExtDialogEvent_ =
-    function(event, webviewEvent) {
+WebViewInternal.prototype.handleDialogEvent_ =
+    function(event, webViewEvent) {
   var showWarningMessage = function(dialogType) {
     var VOWELS = ['a', 'e', 'i', 'o', 'u'];
     var WARNING_MSG_DIALOG_BLOCKED = '<webview>: %1 %2 dialog was blocked.';
@@ -79,17 +80,16 @@
     ok: function(user_input) {
       validateCall();
       user_input = user_input || '';
-      chrome.webview.setPermission(
-          self.instanceId_, requestId, true, user_input);
+      WebView.setPermission(self.instanceId_, requestId, true, user_input);
     },
     cancel: function() {
       validateCall();
-      chrome.webview.setPermission(self.instanceId_, requestId, false, '');
+      WebView.setPermission(self.instanceId_, requestId, false, '');
     }
   };
-  webviewEvent.dialog = dialog;
+  webViewEvent.dialog = dialog;
 
-  var defaultPrevented = !webviewNode.dispatchEvent(webviewEvent);
+  var defaultPrevented = !webviewNode.dispatchEvent(webViewEvent);
   if (actionTaken) {
     return;
   }
@@ -97,18 +97,18 @@
   if (defaultPrevented) {
     // Tell the JavaScript garbage collector to track lifetime of |dialog| and
     // call back when the dialog object has been collected.
-    messagingNatives.BindToGC(dialog, function() {
+    MessagingNatives.BindToGC(dialog, function() {
       // Avoid showing a warning message if the decision has already been made.
       if (actionTaken) {
         return;
       }
-      chrome.webview.setPermission(self.instanceId_, requestId, false, '');
+      WebView.setPermission(self.instanceId_, requestId, false, '');
       showWarningMessage(event.messageType);
     });
   } else {
     actionTaken = true;
     // The default action is equivalent to canceling the dialog.
-    chrome.webview.setPermission(self.instanceId_, requestId, false, '');
+    WebView.setPermission(self.instanceId_, requestId, false, '');
     showWarningMessage(event.messageType);
   }
 };
@@ -116,8 +116,8 @@
 /**
  * @private
  */
-WebViewInternal.prototype.maybeGetWebviewExperimentalExtEvents_ = function() {
-  return WEB_VIEW_EXPERIMENTAL_EXT_EVENTS;
+WebViewInternal.prototype.maybeGetExperimentalEvents_ = function() {
+  return WEB_VIEW_EXPERIMENTAL_EVENTS;
 };
 
 WebViewInternal.prototype.clearData_ = function(var_args) {
@@ -125,7 +125,7 @@
     return;
   }
   var args = $Array.concat([this.instanceId_], $Array.slice(arguments));
-  $Function.apply(chrome.webview.clearData, null, args);
+  $Function.apply(WebView.clearData, null, args);
 };
 
 WebViewInternal.maybeRegisterExperimentalAPIs = function(proto, secret) {
diff --git a/chrome/renderer/resources/neterror.html b/chrome/renderer/resources/neterror.html
index d28c508..5fe2862 100644
--- a/chrome/renderer/resources/neterror.html
+++ b/chrome/renderer/resources/neterror.html
@@ -7,44 +7,48 @@
   <link rel="stylesheet" href="neterror.css">
   <script src="neterror.js"></script>
 </head>
-
 <body id="t">
-<div id="main-frame-error">
- <div id="box">
-  <div id="content-top">
-    <h1>
-     <div><img class="icon" jseval="this.classList.add(iconClass)"></div>
-     <span i18n-content="heading"></span>
-    </h1>
-
-    <button id="reload-button" onclick="location = this.url" jsselect="reload" jsvalues=".url:reloadUrl" jscontent="msg"></button>
-    <button id="more-less-button" onclick="toggleHelpBox()" jsdisplay="more" jsvalues=".moreText:more; .lessText:less;" jscontent="more"></button>
-  </div>
-
-  <!-- Outer and inner divs are needed both for margins and sizing. -->
-  <div id="help-box-outer" class="hidden">
-   <div id="help-box-inner">
-    <div jsselect="summary">
-     <span jsvalues=".innerHTML:msg"></span>
+  <div id="main-frame-error">
+    <div id="box">
+      <div id="content-top">
+        <h1>
+          <div>
+            <img class="icon"
+                jseval="updateIconClass(this.classList, iconClass)">
+          </div>
+          <span i18n-content="heading"></span>
+        </h1>
+         <button id="reload-button" onclick="location = this.url"
+            jsselect="reload" jsvalues=".url:reloadUrl"
+            jscontent="msg"></button>
+        <button id="more-less-button" onclick="toggleHelpBox()" jsdisplay="more"
+            jsvalues=".moreText:more; .lessText:less;"
+            jscontent="more"></button>
+      </div>
+       <!-- Outer and inner divs are needed both for margins and sizing. -->
+      <div id="help-box-outer" class="hidden">
+        <div id="help-box-inner">
+          <div jsselect="summary">
+            <span jsvalues=".innerHTML:msg"></span>
+          </div>
+          <div class="suggestions" jsselect="suggestions">
+            <div class="suggestion-header" jsvalues=".innerHTML:header"></div>
+            <div class="suggestion-body" jsvalues=".innerHTML:body"></div>
+          </div>
+          <button id="diagnose-button" onclick="diagnoseErrors()"
+            jscontent="diagnose" jsdisplay="diagnose"></button>
+          <div id="diagnose-frame" class="hidden"></div>
+          <div class="error-code" jscontent="errorCode"></div>
+        </div>
+      </div>
     </div>
-
-    <div class="suggestions" jsselect="suggestions">
-      <div class="suggestion-header" jsvalues=".innerHTML:header"></div>
-      <div class="suggestion-body" jsvalues=".innerHTML:body"></div>
-    </div>
-    <button id="diagnose-button" onclick="diagnoseErrors()"
-      jscontent="diagnose" jsdisplay="diagnose"></button>
-    <div id="diagnose-frame" class="hidden"></div>
-    <div class="error-code" jscontent="errorCode"></div>
-   </div>
   </div>
- </div>
-</div>
-<div id="sub-frame-error">
-  <!-- Show details when hovering over the icon, in case the details are
-       hidden because they're too large. -->
-  <img class="icon" jseval="this.classList.add(iconClass)" jsvalues=".title:errorDetails">
-  <div id="sub-frame-error-details" jsvalues=".innerHTML:errorDetails"></div>
-</div>
+  <div id="sub-frame-error">
+    <!-- Show details when hovering over the icon, in case the details are
+         hidden because they're too large. -->
+    <img class="icon" jseval="updateIconClass(this.classList, iconClass)"
+        jsvalues=".title:errorDetails">
+    <div id="sub-frame-error-details" jsvalues=".innerHTML:errorDetails"></div>
+  </div>
 </body>
 </html>
diff --git a/chrome/renderer/resources/neterror.js b/chrome/renderer/resources/neterror.js
index 2dd8934..99f19e3 100644
--- a/chrome/renderer/resources/neterror.js
+++ b/chrome/renderer/resources/neterror.js
@@ -27,10 +27,30 @@
 if (window.top.location != window.location)
   document.documentElement.setAttribute('subframe', '');
 
+// Re-renders the error page using |strings| as the dictionary of values.
+// Used by NetErrorTabHelper to update DNS error pages with probe results.
 function updateForDnsProbe(strings) {
+  i18nTemplate.process(document, strings);
   var context = new JsEvalContext(strings);
-  jstProcess(context, document.getElementById('help-box-outer'));
-  jstProcess(context, document.getElementById('details'));
+  jstProcess(context, document.getElementById('t'));
+}
+
+// Given the classList property of an element, adds an icon class to the list
+// and removes the previously-
+function updateIconClass(classList, newClass) {
+  var oldClass;
+
+  if (classList.hasOwnProperty('last_icon_class')) {
+    oldClass = classList['last_icon_class']
+    if (oldClass == newClass)
+      return;
+  }
+
+  classList.add(newClass);
+  if (oldClass !== undefined)
+    classList.remove(oldClass);
+
+  classList['last_icon_class'] = newClass;
 }
 
 <if expr="is_macosx or is_ios or is_linux or is_android">
diff --git a/chrome/renderer/safe_browsing/phishing_url_feature_extractor.cc b/chrome/renderer/safe_browsing/phishing_url_feature_extractor.cc
index 5fda969..a43f0ca 100644
--- a/chrome/renderer/safe_browsing/phishing_url_feature_extractor.cc
+++ b/chrome/renderer/safe_browsing/phishing_url_feature_extractor.cc
@@ -10,9 +10,9 @@
 
 #include "base/logging.h"
 #include "base/metrics/histogram.h"
-#include "base/perftimer.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
+#include "base/test/perftimer.h"
 #include "chrome/renderer/safe_browsing/features.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "url/gurl.h"
diff --git a/chrome/renderer/searchbox/searchbox.cc b/chrome/renderer/searchbox/searchbox.cc
index ad5d08b..be0f880 100644
--- a/chrome/renderer/searchbox/searchbox.cc
+++ b/chrome/renderer/searchbox/searchbox.cc
@@ -147,6 +147,7 @@
     is_focused_(false),
     is_input_in_progress_(false),
     is_key_capture_enabled_(false),
+    display_instant_results_(false),
     most_visited_items_cache_(kMaxInstantMostVisitedItemCacheSize),
     omnibox_font_(),
     omnibox_font_size_(12),
@@ -222,6 +223,12 @@
   return theme_info_;
 }
 
+void SearchBox::Focus() {
+  render_view()->Send(new ChromeViewHostMsg_FocusOmnibox(
+      render_view()->GetRoutingID(), render_view()->GetPageId(),
+      OMNIBOX_FOCUS_VISIBLE));
+}
+
 void SearchBox::NavigateToURL(const GURL& url,
                               content::PageTransition transition,
                               WindowOpenDisposition disposition,
@@ -279,8 +286,12 @@
                         OnMostVisitedChanged)
     IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxPromoInformation,
                         OnPromoInformationReceived)
+    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetDisplayInstantResults,
+                        OnSetDisplayInstantResults)
     IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetInputInProgress,
                         OnSetInputInProgress)
+    IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetSuggestionToPrefetch,
+                        OnSetSuggestionToPrefetch)
     IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSubmit, OnSubmit)
     IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxThemeChanged,
                         OnThemeChanged)
@@ -368,6 +379,10 @@
   app_launcher_enabled_ = is_app_launcher_enabled;
 }
 
+void SearchBox::OnSetDisplayInstantResults(bool display_instant_results) {
+  display_instant_results_ = display_instant_results;
+}
+
 void SearchBox::OnSetInputInProgress(bool is_input_in_progress) {
   if (is_input_in_progress_ != is_input_in_progress) {
     is_input_in_progress_ = is_input_in_progress;
@@ -385,6 +400,15 @@
   }
 }
 
+void SearchBox::OnSetSuggestionToPrefetch(const InstantSuggestion& suggestion) {
+  suggestion_ = suggestion;
+  if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
+    DVLOG(1) << render_view() << " OnSetSuggestionToPrefetch";
+    extensions_v8::SearchBoxExtension::DispatchSuggestionChange(
+        render_view()->GetWebView()->mainFrame());
+  }
+}
+
 void SearchBox::OnSubmit(const string16& query) {
   query_ = query;
   if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) {
@@ -422,6 +446,7 @@
 
 void SearchBox::Reset() {
   query_.clear();
+  suggestion_ = InstantSuggestion();
   start_margin_ = 0;
   width_ = 0;
   is_focused_ = false;
diff --git a/chrome/renderer/searchbox/searchbox.h b/chrome/renderer/searchbox/searchbox.h
index 267c190..454dd73 100644
--- a/chrome/renderer/searchbox/searchbox.h
+++ b/chrome/renderer/searchbox/searchbox.h
@@ -66,6 +66,9 @@
   bool GetMostVisitedItemWithID(InstantRestrictedID most_visited_item_id,
                                 InstantMostVisitedItem* item) const;
 
+  // Sends ChromeViewHostMsg_FocusOmnibox to the browser.
+  void Focus();
+
   // Sends ChromeViewHostMsg_SearchBoxNavigate to the browser.
   void NavigateToURL(const GURL& url,
                      content::PageTransition transition,
@@ -97,10 +100,12 @@
   bool is_focused() const { return is_focused_; }
   bool is_input_in_progress() const { return is_input_in_progress_; }
   bool is_key_capture_enabled() const { return is_key_capture_enabled_; }
+  bool display_instant_results() const { return display_instant_results_; }
   const string16& omnibox_font() const { return omnibox_font_; }
+  size_t omnibox_font_size() const { return omnibox_font_size_; }
   const string16& query() const { return query_; }
   int start_margin() const { return start_margin_; }
-  size_t omnibox_font_size() const { return omnibox_font_size_; }
+  const InstantSuggestion& suggestion() const { return suggestion_; }
 
  private:
   // Overridden from content::RenderViewObserver:
@@ -115,7 +120,9 @@
   void OnMostVisitedChanged(
       const std::vector<InstantMostVisitedItem>& items);
   void OnPromoInformationReceived(bool is_app_launcher_enabled);
+  void OnSetDisplayInstantResults(bool display_instant_results);
   void OnSetInputInProgress(bool input_in_progress);
+  void OnSetSuggestionToPrefetch(const InstantSuggestion& suggestion);
   void OnSubmit(const string16& query);
   void OnThemeChanged(const ThemeBackgroundInfo& theme_info);
   void OnToggleVoiceSearch();
@@ -133,12 +140,14 @@
   bool is_focused_;
   bool is_input_in_progress_;
   bool is_key_capture_enabled_;
+  bool display_instant_results_;
   InstantRestrictedIDCache<InstantMostVisitedItem> most_visited_items_cache_;
   ThemeBackgroundInfo theme_info_;
   string16 omnibox_font_;
   size_t omnibox_font_size_;
   string16 query_;
   int start_margin_;
+  InstantSuggestion suggestion_;
   int width_;
 
   DISALLOW_COPY_AND_ASSIGN(SearchBox);
diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc
index 39c780d..948aaea 100644
--- a/chrome/renderer/searchbox/searchbox_extension.cc
+++ b/chrome/renderer/searchbox/searchbox_extension.cc
@@ -270,6 +270,17 @@
     "  true;"
     "}";
 
+static const char kDispatchSuggestionChangeEventScript[] =
+    "if (window.chrome &&"
+    "    window.chrome.embeddedSearch &&"
+    "    window.chrome.embeddedSearch.searchBox &&"
+    "    window.chrome.embeddedSearch.searchBox.onsuggestionchange &&"
+    "    typeof window.chrome.embeddedSearch.searchBox.onsuggestionchange =="
+    "        'function') {"
+    "  window.chrome.embeddedSearch.searchBox.onsuggestionchange();"
+    "  true;"
+    "}";
+
 static const char kDispatchThemeChangeEventScript[] =
     "if (window.chrome &&"
     "    window.chrome.embeddedSearch &&"
@@ -310,6 +321,9 @@
   static void DeleteMostVisitedItem(
       const v8::FunctionCallbackInfo<v8::Value>& args);
 
+  // Focuses the omnibox.
+  static void Focus(const v8::FunctionCallbackInfo<v8::Value>& args);
+
   // Gets whether or not the app launcher is enabled.
   static void GetAppLauncherEnabled(
       const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -339,6 +353,10 @@
   // Gets the start-edge margin to use with extended Instant.
   static void GetStartMargin(const v8::FunctionCallbackInfo<v8::Value>& args);
 
+  // Gets the current top suggestion to prefetch search results.
+  static void GetSuggestionToPrefetch(
+      const v8::FunctionCallbackInfo<v8::Value>& args);
+
   // Gets the background info of the theme currently adopted by browser.
   // Call only when overlay is showing NTP page.
   static void GetThemeBackgroundInfo(
@@ -386,6 +404,10 @@
   static void UndoMostVisitedDeletion(
       const v8::FunctionCallbackInfo<v8::Value>& args);
 
+  // Indicates whether the page supports Instant.
+  static void GetDisplayInstantResults(
+      const v8::FunctionCallbackInfo<v8::Value>& args);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(SearchBoxExtensionWrapper);
 };
@@ -442,6 +464,11 @@
 }
 
 // static
+void SearchBoxExtension::DispatchSuggestionChange(WebKit::WebFrame* frame) {
+  Dispatch(frame, kDispatchSuggestionChangeEventScript);
+}
+
+// static
 void SearchBoxExtension::DispatchThemeChange(WebKit::WebFrame* frame) {
   Dispatch(frame, kDispatchThemeChangeEventScript);
 }
@@ -461,6 +488,8 @@
     v8::Handle<v8::String> name) {
   if (name->Equals(v8::String::New("DeleteMostVisitedItem")))
     return v8::FunctionTemplate::New(DeleteMostVisitedItem);
+  if (name->Equals(v8::String::New("Focus")))
+    return v8::FunctionTemplate::New(Focus);
   if (name->Equals(v8::String::New("GetAppLauncherEnabled")))
     return v8::FunctionTemplate::New(GetAppLauncherEnabled);
   if (name->Equals(v8::String::New("GetFont")))
@@ -477,6 +506,8 @@
     return v8::FunctionTemplate::New(GetRightToLeft);
   if (name->Equals(v8::String::New("GetStartMargin")))
     return v8::FunctionTemplate::New(GetStartMargin);
+  if (name->Equals(v8::String::New("GetSuggestionToPrefetch")))
+    return v8::FunctionTemplate::New(GetSuggestionToPrefetch);
   if (name->Equals(v8::String::New("GetThemeBackgroundInfo")))
     return v8::FunctionTemplate::New(GetThemeBackgroundInfo);
   if (name->Equals(v8::String::New("IsFocused")))
@@ -501,6 +532,8 @@
     return v8::FunctionTemplate::New(UndoAllMostVisitedDeletions);
   if (name->Equals(v8::String::New("UndoMostVisitedDeletion")))
     return v8::FunctionTemplate::New(UndoMostVisitedDeletion);
+  if (name->Equals(v8::String::New("GetDisplayInstantResults")))
+    return v8::FunctionTemplate::New(GetDisplayInstantResults);
   return v8::Handle<v8::FunctionTemplate>();
 }
 
@@ -526,6 +559,16 @@
 }
 
 // static
+void SearchBoxExtensionWrapper::Focus(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  content::RenderView* render_view = GetRenderView();
+  if (!render_view) return;
+
+  DVLOG(1) << render_view << " Focus";
+  SearchBox::Get(render_view)->Focus();
+}
+
+// static
 void SearchBoxExtensionWrapper::GetAppLauncherEnabled(
     const v8::FunctionCallbackInfo<v8::Value>& args) {
   content::RenderView* render_view = GetRenderView();
@@ -625,6 +668,20 @@
 }
 
 // static
+void SearchBoxExtensionWrapper::GetSuggestionToPrefetch(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  content::RenderView* render_view = GetRenderView();
+  if (!render_view) return;
+
+  const InstantSuggestion& suggestion =
+      SearchBox::Get(render_view)->suggestion();
+  v8::Handle<v8::Object> data = v8::Object::New();
+  data->Set(v8::String::New("text"), UTF16ToV8String(suggestion.text));
+  data->Set(v8::String::New("metadata"), UTF8ToV8String(suggestion.metadata));
+  args.GetReturnValue().Set(data);
+}
+
+// static
 void SearchBoxExtensionWrapper::GetThemeBackgroundInfo(
     const v8::FunctionCallbackInfo<v8::Value>& args) {
   content::RenderView* render_view = GetRenderView();
@@ -920,4 +977,17 @@
   SearchBox::Get(render_view)->UndoMostVisitedDeletion(args[0]->IntegerValue());
 }
 
+// static
+void SearchBoxExtensionWrapper::GetDisplayInstantResults(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  content::RenderView* render_view = GetRenderView();
+  if (!render_view) return;
+
+  bool display_instant_results =
+      SearchBox::Get(render_view)->display_instant_results();
+  DVLOG(1) << render_view << " GetDisplayInstantResults" <<
+      display_instant_results;
+  args.GetReturnValue().Set(display_instant_results);
+}
+
 }  // namespace extensions_v8
diff --git a/chrome/renderer/searchbox/searchbox_extension.h b/chrome/renderer/searchbox/searchbox_extension.h
index 8e04103..5135d64 100644
--- a/chrome/renderer/searchbox/searchbox_extension.h
+++ b/chrome/renderer/searchbox/searchbox_extension.h
@@ -37,6 +37,7 @@
   static void DispatchMarginChange(WebKit::WebFrame* frame);
   static void DispatchMostVisitedChanged(WebKit::WebFrame* frame);
   static void DispatchSubmit(WebKit::WebFrame* frame);
+  static void DispatchSuggestionChange(WebKit::WebFrame* frame);
   static void DispatchThemeChange(WebKit::WebFrame* frame);
   static void DispatchToggleVoiceSearch(WebKit::WebFrame* frame);
 
diff --git a/chrome/test/android/OWNERS b/chrome/test/android/OWNERS
new file mode 100644
index 0000000..a223621
--- /dev/null
+++ b/chrome/test/android/OWNERS
@@ -0,0 +1,6 @@
+aruslan@chromium.org
+bulach@chromium.org
+dtrainor@chromium.org
+nyquist@chromium.org
+tedchoc@chromium.org
+yfriedman@chromium.org
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ApplicationData.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ApplicationData.java
new file mode 100644
index 0000000..06fd89e
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/ApplicationData.java
@@ -0,0 +1,112 @@
+// 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.
+
+package org.chromium.chrome.test.util;
+
+import android.content.Context;
+
+import org.chromium.content.browser.test.util.Criteria;
+import org.chromium.content.browser.test.util.CriteriaHelper;
+
+import java.io.File;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A utility class to do operations on the application data directory, such as clearing the
+ * application data.
+ */
+public final class ApplicationData {
+
+    private static final long MAX_CLEAR_APP_DATA_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(3);
+    private static final long CLEAR_APP_DATA_POLL_INTERVAL_MS = MAX_CLEAR_APP_DATA_TIMEOUT_MS / 10;
+
+    private ApplicationData() {}
+
+    /**
+     * Clear all files in target contexts application directory, except the directory 'lib'.
+     * The timeout is specified in |MAX_CLEAR_APP_DATA_TIMEOUT_MS|.
+     *
+     * When deleting all files is complete, the 'cache' directory is recreated, to ensure that the
+     * framework does not try to create it for sandbox processes and fail.
+     *
+     * When this is invoked from tests, the target context from the instrumentation must be used.
+     *
+     * @param targetContext the target Context.
+     *
+     * @return Whether clearing the application data was successful.
+     */
+    public static boolean clearAppData(Context targetContext) throws InterruptedException {
+        final String appDir = getAppDirFromTargetContext(targetContext);
+        return CriteriaHelper.pollForCriteria(
+                new Criteria() {
+                    private boolean mDataRemoved = false;
+
+                    @Override
+                    public boolean isSatisfied() {
+                        if (!mDataRemoved && !removeAppData(appDir)) {
+                            return false;
+                        }
+                        mDataRemoved = true;
+                        // We have to make sure the cache directory still exists, as the framework
+                        // will try to create it otherwise and will fail for sandbox processes with
+                        // a NullPointerException.
+                        File cacheDir = new File(appDir, "cache");
+                        return cacheDir.exists() || cacheDir.mkdir();
+                    }
+                },
+                MAX_CLEAR_APP_DATA_TIMEOUT_MS, CLEAR_APP_DATA_POLL_INTERVAL_MS);
+    }
+
+    /**
+     * Find the absolute path of the application data directory for the given target context.
+     *
+     * When this is invoked from tests, the target context from the instrumentation must be used.
+     *
+     * @param targetContext the target Context.
+     *
+     * @return the absolute path of the application data directory.
+     */
+    public static String getAppDirFromTargetContext(Context targetContext) {
+        String cacheDir = targetContext.getCacheDir().getAbsolutePath();
+        return cacheDir.substring(0, cacheDir.lastIndexOf('/'));
+    }
+
+    /**
+     * Remove all files and directories under the given application directory, except 'lib'.
+     *
+     * @param appDir the application directory to remove.
+     *
+     * @return whether removal succeeded.
+     */
+    private static boolean removeAppData(String appDir) {
+        File[] files = new File(appDir).listFiles();
+        if (files == null)
+            return true;
+        for (File file : files) {
+            if (!file.getAbsolutePath().endsWith("/lib") && !removeFile(file))
+                return false;
+        }
+        return true;
+    }
+
+    /**
+     * Remove the given file or directory.
+     *
+     * @param file the file or directory to remove.
+     *
+     * @return whether removal succeeded.
+     */
+    private static boolean removeFile(File file) {
+        if (file.isDirectory()) {
+            File[] files = file.listFiles();
+            if (files == null)
+                return true;
+            for (File sub_file : files) {
+                if (!removeFile(sub_file))
+                    return false;
+            }
+        }
+        return file.delete();
+    }
+}
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/TestHttpServerClient.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/TestHttpServerClient.java
new file mode 100644
index 0000000..5f7e70d
--- /dev/null
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/TestHttpServerClient.java
@@ -0,0 +1,68 @@
+// 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.
+
+package org.chromium.chrome.test.util;
+
+import junit.framework.Assert;
+
+import org.chromium.chrome.browser.util.StreamUtil;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * A test utility class to get URLs that point to the test HTTP server, and to verify that it is up
+ * and giving expected responses to given URLs.
+ */
+public final class TestHttpServerClient {
+    private static final int SERVER_PORT = 8000;
+
+    private TestHttpServerClient() {
+    }
+
+    /**
+     * Construct a suitable URL for loading a test data file from the hosts' HTTP server.
+     *
+     * @param path path relative to the document root.
+     * @return an HTTP url.
+     */
+    public static String getUrl(String path) {
+        return "http://localhost:" + SERVER_PORT + "/" + path;
+    }
+
+    /**
+     * Establishes a connection with the test server at default URL and verifies that it is running.
+     */
+    public static void checkServerIsUp() {
+        checkServerIsUp(getUrl("chrome/test/data/android/ok.txt"), "OK Computer");
+    }
+
+    /**
+     * Establishes a connection with the test server at a given URL and verifies that it is running
+     * by making sure that the expected response is received.
+     */
+    public static void checkServerIsUp(String serverUrl, String expectedResponse) {
+        InputStream is = null;
+        try {
+            URL testUrl = new URL(serverUrl);
+            is = testUrl.openStream();
+            byte[] buffer = new byte[128];
+            int length = is.read(buffer);
+            Assert.assertNotSame("Failed to access test HTTP Server at URL: " + serverUrl,
+                    -1, expectedResponse.length());
+            Assert.assertEquals("Failed to access test HTTP Server at URL: " + serverUrl,
+                    expectedResponse, new String(buffer, 0, length).trim());
+        } catch (MalformedURLException e) {
+            Assert.fail(
+                    "Failed to check test HTTP server at URL: " + serverUrl + ". Status: " + e);
+        } catch (IOException e) {
+            Assert.fail(
+                    "Failed to check test HTTP server at URL: " + serverUrl + ". Status: " + e);
+        } finally {
+            StreamUtil.closeQuietly(is);
+        }
+    }
+}
diff --git a/chrome/test/base/test_browser_window.cc b/chrome/test/base/test_browser_window.cc
index 2b5f05a..ae158f8 100644
--- a/chrome/test/base/test_browser_window.cc
+++ b/chrome/test/base/test_browser_window.cc
@@ -138,6 +138,11 @@
   return NULL;
 }
 
+int
+TestBrowserWindow::GetRenderViewHeightInsetWithDetachedBookmarkBar() {
+  return 0;
+}
+
 namespace chrome {
 
 namespace {
diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h
index 40cff07..347e2a3 100644
--- a/chrome/test/base/test_browser_window.h
+++ b/chrome/test/base/test_browser_window.h
@@ -140,6 +140,7 @@
       const gfx::Rect& rect,
       const content::PasswordForm& form,
       autofill::PasswordGenerator* generator) OVERRIDE {}
+  virtual int GetRenderViewHeightInsetWithDetachedBookmarkBar() OVERRIDE;
 
  protected:
   virtual void DestroyBrowser() OVERRIDE {}
diff --git a/chrome/test/base/test_location_bar.h b/chrome/test/base/test_location_bar.h
index 22ef364b..11efa96 100644
--- a/chrome/test/base/test_location_bar.h
+++ b/chrome/test/base/test_location_bar.h
@@ -26,7 +26,7 @@
     transition_ = transition;
   }
 
-  // Overridden from LocationBar:
+  // LocationBar:
   virtual void ShowFirstRunBubble() OVERRIDE {}
   virtual string16 GetInputString() const OVERRIDE;
   virtual WindowOpenDisposition GetWindowOpenDisposition() const OVERRIDE;
@@ -46,7 +46,6 @@
   virtual LocationBarTesting* GetLocationBarForTesting() OVERRIDE;
 
  private:
-
   // Test-supplied values that will be returned through the LocationBar
   // interface.
   string16 input_string_;
diff --git a/chrome/test/base/testing_pref_service_syncable.cc b/chrome/test/base/testing_pref_service_syncable.cc
index 3addba3..4d012e9 100644
--- a/chrome/test/base/testing_pref_service_syncable.cc
+++ b/chrome/test/base/testing_pref_service_syncable.cc
@@ -20,8 +20,9 @@
     : PrefServiceSyncable(
           pref_notifier,
           new PrefValueStore(managed_prefs,
-                             NULL,
-                             NULL,
+                             NULL,  // supervised_user_prefs
+                             NULL,  // extension_prefs
+                             NULL,  // command_line_prefs
                              user_prefs,
                              recommended_prefs,
                              pref_registry->defaults().get(),
diff --git a/chrome/test/chromedriver/alert_commands.cc b/chrome/test/chromedriver/alert_commands.cc
index 85836b9..de7118d 100644
--- a/chrome/test/chromedriver/alert_commands.cc
+++ b/chrome/test/chromedriver/alert_commands.cc
@@ -31,8 +31,8 @@
   if (status.IsError())
     return status;
 
-  status = web_view->WaitForPendingNavigations(session->GetCurrentFrameId(),
-                                               session->page_load_timeout);
+  status = web_view->WaitForPendingNavigations(
+      session->GetCurrentFrameId(), session->page_load_timeout, true);
   if (status.IsError() && status.code() != kUnexpectedAlertOpen)
     return status;
 
diff --git a/chrome/test/chromedriver/capabilities.cc b/chrome/test/chromedriver/capabilities.cc
index b052f0f..7444641 100644
--- a/chrome/test/chromedriver/capabilities.cc
+++ b/chrome/test/chromedriver/capabilities.cc
@@ -8,21 +8,38 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_tokenizer.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
 #include "chrome/test/chromedriver/chrome/log.h"
 #include "chrome/test/chromedriver/chrome/status.h"
+#include "net/base/net_util.h"
 
 namespace {
 
 typedef base::Callback<Status(const base::Value&, Capabilities*)> Parser;
 
-Status ParseDetach(
+Status ParseBoolean(
+    bool* to_set,
     const base::Value& option,
     Capabilities* capabilities) {
-  if (!option.GetAsBoolean(&capabilities->detach))
-    return Status(kUnknownError, "'detach' must be a boolean");
+  if (!option.GetAsBoolean(to_set))
+    return Status(kUnknownError, "value must be a boolean");
+  return Status(kOk);
+}
+
+Status ParseString(std::string* to_set,
+                   const base::Value& option,
+                   Capabilities* capabilities) {
+  std::string str;
+  if (!option.GetAsString(&str))
+    return Status(kUnknownError, "value must be a string");
+  if (str.empty())
+    return Status(kUnknownError, "value cannot be empty");
+  *to_set = str;
   return Status(kOk);
 }
 
@@ -37,6 +54,10 @@
   return Status(kOk);
 }
 
+Status IgnoreCapability(const base::Value& option, Capabilities* capabilities) {
+  return Status(kOk);
+}
+
 Status ParseChromeBinary(
     const base::Value& option,
     Capabilities* capabilities) {
@@ -198,7 +219,39 @@
   return Status(kOk);
 }
 
-Status ParseDesktopChromeCapabilities(
+Status ParseUseExistingBrowser(const base::Value& option,
+                               Capabilities* capabilities) {
+  std::string server_addr;
+  if (!option.GetAsString(&server_addr))
+    return Status(kUnknownError, "must be 'host:port'");
+
+  std::vector<std::string> values;
+  base::SplitString(server_addr, ':', &values);
+  if (values.size() != 2)
+    return Status(kUnknownError, "must be 'host:port'");
+
+  int port = 0;
+  base::StringToInt(values[1], &port);
+  if (port <= 0)
+    return Status(kUnknownError, "port must be >= 0");
+
+  capabilities->use_existing_browser = NetAddress(values[0], port);
+  return Status(kOk);
+}
+
+Status ParseLoggingPrefs(const base::Value& option,
+                         Capabilities* capabilities) {
+  const base::DictionaryValue* logging_prefs_dict = NULL;
+  if (!option.GetAsDictionary(&logging_prefs_dict))
+    return Status(kUnknownError, "'loggingPrefs' must be a dictionary");
+
+  // TODO(klm): verify log types.
+  // TODO(klm): verify log levels.
+  capabilities->logging_prefs.reset(logging_prefs_dict->DeepCopy());
+  return Status(kOk);
+}
+
+Status ParseChromeOptions(
     Log* log,
     const base::Value& capability,
     Capabilities* capabilities) {
@@ -206,18 +259,41 @@
   if (!capability.GetAsDictionary(&chrome_options))
     return Status(kUnknownError, "'chromeOptions' must be a dictionary");
 
-  std::map<std::string, Parser> parser_map;
+  bool is_android = chrome_options->HasKey("androidPackage");
+  bool is_existing = chrome_options->HasKey("useExistingBrowser");
 
-  parser_map["detach"] = base::Bind(&ParseDetach);
-  parser_map["loadAsync"] =
-      base::Bind(&IgnoreDeprecatedOption, log, "loadAsync");
-  parser_map["binary"] = base::Bind(&ParseChromeBinary);
-  parser_map["logPath"] = base::Bind(&ParseLogPath);
-  parser_map["args"] = base::Bind(&ParseArgs, false);
-  parser_map["prefs"] = base::Bind(&ParsePrefs);
-  parser_map["localState"] = base::Bind(&ParseLocalState);
-  parser_map["extensions"] = base::Bind(&ParseExtensions);
-  parser_map["excludeSwitches"] = base::Bind(&ParseExcludeSwitches);
+  std::map<std::string, Parser> parser_map;
+  // Ignore 'binary' and 'extensions' capability, since the Java client
+  // always passes them.
+  parser_map["binary"] = base::Bind(&IgnoreCapability);
+  parser_map["extensions"] = base::Bind(&IgnoreCapability);
+  if (is_android) {
+    parser_map["androidActivity"] =
+        base::Bind(&ParseString, &capabilities->android_activity);
+    parser_map["androidDeviceSerial"] =
+        base::Bind(&ParseString, &capabilities->android_device_serial);
+    parser_map["androidPackage"] =
+        base::Bind(&ParseString, &capabilities->android_package);
+    parser_map["androidProcess"] =
+        base::Bind(&ParseString, &capabilities->android_process);
+    parser_map["args"] = base::Bind(&ParseArgs, true);
+  } else if (is_existing) {
+    parser_map["args"] = base::Bind(&IgnoreCapability);
+    parser_map["useExistingBrowser"] = base::Bind(&ParseUseExistingBrowser);
+  } else {
+    parser_map["forceDevToolsScreenshot"] = base::Bind(
+        &ParseBoolean, &capabilities->force_devtools_screenshot);
+    parser_map["args"] = base::Bind(&ParseArgs, false);
+    parser_map["binary"] = base::Bind(&ParseChromeBinary);
+    parser_map["detach"] = base::Bind(&ParseBoolean, &capabilities->detach);
+    parser_map["excludeSwitches"] = base::Bind(&ParseExcludeSwitches);
+    parser_map["extensions"] = base::Bind(&ParseExtensions);
+    parser_map["loadAsync"] =
+        base::Bind(&IgnoreDeprecatedOption, log, "loadAsync");
+    parser_map["localState"] = base::Bind(&ParseLocalState);
+    parser_map["logPath"] = base::Bind(&ParseLogPath);
+    parser_map["prefs"] = base::Bind(&ParsePrefs);
+  }
 
   for (base::DictionaryValue::Iterator it(*chrome_options); !it.IsAtEnd();
        it.Advance()) {
@@ -227,79 +303,7 @@
     }
     Status status = parser_map[it.key()].Run(it.value(), capabilities);
     if (status.IsError())
-      return status;
-  }
-  return Status(kOk);
-}
-
-Status ParseAndroidChromeCapabilities(const base::DictionaryValue& desired_caps,
-                                      Capabilities* capabilities) {
-  const base::Value* chrome_options = NULL;
-  if (desired_caps.Get("chromeOptions", &chrome_options)) {
-    const base::DictionaryValue* chrome_options_dict = NULL;
-    if (!chrome_options->GetAsDictionary(&chrome_options_dict))
-      return Status(kUnknownError, "'chromeOptions' must be a dictionary");
-
-    const base::Value* android_package_value;
-    if (chrome_options_dict->Get("androidPackage", &android_package_value)) {
-      if (!android_package_value->GetAsString(&capabilities->android_package) ||
-          capabilities->android_package.empty()) {
-        return Status(kUnknownError,
-                      "'androidPackage' must be a non-empty string");
-      }
-
-      const base::Value* device_serial_value;
-      if (chrome_options_dict->Get("androidDeviceSerial",
-                                   &device_serial_value)) {
-        if (!device_serial_value->GetAsString(
-            &capabilities->android_device_serial) ||
-            capabilities->android_device_serial.empty()) {
-          return Status(kUnknownError,
-                        "'androidDeviceSerial' must be a non-empty string");
-        }
-      }
-
-      const base::Value* activity_value;
-      if (chrome_options_dict->Get("androidActivity",
-                                   &activity_value)) {
-        if (!activity_value->GetAsString(
-            &capabilities->android_activity) ||
-            capabilities->android_activity.empty()) {
-          return Status(kUnknownError,
-                        "'androidActivity' must be a non-empty string");
-        }
-      }
-
-      const base::Value* process_value;
-      if (chrome_options_dict->Get("androidProcess",
-                                   &process_value)) {
-        if (!process_value->GetAsString(
-            &capabilities->android_process) ||
-            capabilities->android_process.empty()) {
-          return Status(kUnknownError,
-                        "'androidProcess' must be a non-empty string");
-        }
-      }
-
-      const base::Value* args_value;
-      if (chrome_options_dict->Get("args", &args_value))
-        return ParseArgs(true, *args_value, capabilities);
-    }
-  }
-  return Status(kOk);
-}
-
-Status ParseLoggingPrefs(const base::DictionaryValue& desired_caps,
-                         Capabilities* capabilities) {
-  const base::Value* logging_prefs = NULL;
-  if (desired_caps.Get("loggingPrefs", &logging_prefs)) {
-    const base::DictionaryValue* logging_prefs_dict = NULL;
-    if (!logging_prefs->GetAsDictionary(&logging_prefs_dict)) {
-      return Status(kUnknownError, "'loggingPrefs' must be a dictionary");
-    }
-    // TODO(klm): verify log types.
-    // TODO(klm): verify log levels.
-    capabilities->logging_prefs.reset(logging_prefs_dict->DeepCopy());
+      return Status(kUnknownError, "cannot parse " + it.key(), status);
   }
   return Status(kOk);
 }
@@ -307,7 +311,8 @@
 }  // namespace
 
 Capabilities::Capabilities()
-    : detach(false),
+    : force_devtools_screenshot(false),
+      detach(false),
       command(CommandLine::NO_PROGRAM) {}
 
 Capabilities::~Capabilities() {}
@@ -316,29 +321,26 @@
   return !android_package.empty();
 }
 
+bool Capabilities::IsExistingBrowser() const {
+  return use_existing_browser.IsValid();
+}
+
 Status Capabilities::Parse(
     const base::DictionaryValue& desired_caps,
     Log* log) {
-  Status status = ParseLoggingPrefs(desired_caps, this);
-  if (status.IsError())
-    return status;
-  status = ParseAndroidChromeCapabilities(desired_caps, this);
-  if (status.IsError())
-    return status;
-  if (IsAndroid())
-    return Status(kOk);
-
   std::map<std::string, Parser> parser_map;
+  parser_map["chromeOptions"] = base::Bind(&ParseChromeOptions, log);
+  parser_map["loggingPrefs"] = base::Bind(&ParseLoggingPrefs);
   parser_map["proxy"] = base::Bind(&ParseProxy);
-  parser_map["chromeOptions"] =
-      base::Bind(&ParseDesktopChromeCapabilities, log);
   for (std::map<std::string, Parser>::iterator it = parser_map.begin();
        it != parser_map.end(); ++it) {
     const base::Value* capability = NULL;
     if (desired_caps.Get(it->first, &capability)) {
-      status = it->second.Run(*capability, this);
-      if (status.IsError())
-        return status;
+      Status status = it->second.Run(*capability, this);
+      if (status.IsError()) {
+        return Status(
+            kUnknownError, "cannot parse capability: " + it->first, status);
+      }
     }
   }
   return Status(kOk);
diff --git a/chrome/test/chromedriver/capabilities.h b/chrome/test/chromedriver/capabilities.h
index c5e243e..4fc5734 100644
--- a/chrome/test/chromedriver/capabilities.h
+++ b/chrome/test/chromedriver/capabilities.h
@@ -12,6 +12,7 @@
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/memory/scoped_ptr.h"
+#include "chrome/test/chromedriver/net/net_util.h"
 
 namespace base {
 class DictionaryValue;
@@ -24,11 +25,18 @@
   Capabilities();
   ~Capabilities();
 
+  // Return true if existing host:port session is to be used.
+  bool IsExistingBrowser() const;
+
   // Return true if android package is specified.
   bool IsAndroid() const;
 
   Status Parse(const base::DictionaryValue& desired_caps, Log* log);
 
+  // True if should always use DevTools for taking screenshots.
+  // This is experimental and may be removed at a later point.
+  bool force_devtools_screenshot;
+
   // Whether the lifetime of the started Chrome browser process should be
   // bound to ChromeDriver's process. If true, Chrome will not quit if
   // ChromeDriver dies.
@@ -50,6 +58,9 @@
   // Set of switches which should be removed from default list when launching
   // Chrome.
   std::set<std::string> exclude_switches;
+
+  // If provided, the remote debugging address to connect to.
+  NetAddress use_existing_browser;
 };
 
 #endif  // CHROME_TEST_CHROMEDRIVER_CAPABILITIES_H_
diff --git a/chrome/test/chromedriver/capabilities_unittest.cc b/chrome/test/chromedriver/capabilities_unittest.cc
index 7596468..9717c59 100644
--- a/chrome/test/chromedriver/capabilities_unittest.cc
+++ b/chrome/test/chromedriver/capabilities_unittest.cc
@@ -333,3 +333,15 @@
   ASSERT_TRUE(switches.find("switch1") != switches.end());
   ASSERT_TRUE(switches.find("switch2") != switches.end());
 }
+
+TEST(ParseCapabilities, UseExistingBrowser) {
+  Capabilities capabilities;
+  base::DictionaryValue caps;
+  caps.SetString("chromeOptions.useExistingBrowser", "abc:123");
+  Logger log(Log::kError);
+  Status status = capabilities.Parse(caps, &log);
+  ASSERT_TRUE(status.IsOk());
+  ASSERT_TRUE(capabilities.IsExistingBrowser());
+  ASSERT_EQ("abc", capabilities.use_existing_browser.host());
+  ASSERT_EQ(123, capabilities.use_existing_browser.port());
+}
diff --git a/chrome/test/chromedriver/chrome/automation_extension.cc b/chrome/test/chromedriver/chrome/automation_extension.cc
index 65e035a..27999c8 100644
--- a/chrome/test/chromedriver/chrome/automation_extension.cc
+++ b/chrome/test/chromedriver/chrome/automation_extension.cc
@@ -14,6 +14,22 @@
 
 AutomationExtension::~AutomationExtension() {}
 
+Status AutomationExtension::CaptureScreenshot(std::string* screenshot) {
+  base::ListValue args;
+  scoped_ptr<base::Value> result;
+  Status status = web_view_->CallAsyncFunction(
+      std::string(),
+      "captureScreenshot",
+      args,
+      base::TimeDelta::FromSeconds(10),
+      &result);
+  if (status.IsError())
+    return Status(kUnknownError, "cannot take screenshot", status);
+  if (!result->GetAsString(screenshot))
+    return Status(kUnknownError, "screenshot is not a string");
+  return Status(kOk);
+}
+
 Status AutomationExtension::GetWindowPosition(int* x, int* y) {
   int temp_width, temp_height;
   return GetWindowInfo(x, y, &temp_width, &temp_height);
diff --git a/chrome/test/chromedriver/chrome/automation_extension.h b/chrome/test/chromedriver/chrome/automation_extension.h
index aea732d..b89ff5a 100644
--- a/chrome/test/chromedriver/chrome/automation_extension.h
+++ b/chrome/test/chromedriver/chrome/automation_extension.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_TEST_CHROMEDRIVER_CHROME_AUTOMATION_EXTENSION_H_
 #define CHROME_TEST_CHROMEDRIVER_CHROME_AUTOMATION_EXTENSION_H_
 
+#include <string>
+
 #include "base/basictypes.h"
 #include "base/memory/scoped_ptr.h"
 
@@ -21,6 +23,9 @@
   explicit AutomationExtension(scoped_ptr<WebView> web_view);
   ~AutomationExtension();
 
+  // Captures the visible part of the current tab as a base64-encoded PNG.
+  Status CaptureScreenshot(std::string* screenshot);
+
   // Gets the position of the current window.
   Status GetWindowPosition(int* x, int* y);
 
diff --git a/chrome/test/chromedriver/chrome/chrome.h b/chrome/test/chromedriver/chrome/chrome.h
index 899fa4c..ef4ce83 100644
--- a/chrome/test/chromedriver/chrome/chrome.h
+++ b/chrome/test/chromedriver/chrome/chrome.h
@@ -16,6 +16,14 @@
  public:
   virtual ~Chrome() {}
 
+  enum Type {
+    DESKTOP,
+    ANDROID,
+    EXISTING
+  };
+
+  virtual Type GetType() = 0;
+
   virtual std::string GetVersion() = 0;
 
   virtual int GetBuildNo() = 0;
@@ -29,6 +37,9 @@
   // Closes the specified WebView.
   virtual Status CloseWebView(const std::string& id) = 0;
 
+  // Activates the specified WebView.
+  virtual Status ActivateWebView(const std::string& id) = 0;
+
   // Gets the automation extension.
   virtual Status GetAutomationExtension(AutomationExtension** extension) = 0;
 
diff --git a/chrome/test/chromedriver/chrome/chrome_android_impl.cc b/chrome/test/chromedriver/chrome/chrome_android_impl.cc
index 3301e02..986606b 100644
--- a/chrome/test/chromedriver/chrome/chrome_android_impl.cc
+++ b/chrome/test/chromedriver/chrome/chrome_android_impl.cc
@@ -18,6 +18,10 @@
 
 ChromeAndroidImpl::~ChromeAndroidImpl() {}
 
+Chrome::Type ChromeAndroidImpl::GetType() {
+  return ANDROID;
+}
+
 std::string ChromeAndroidImpl::GetOperatingSystemName() {
   return "ANDROID";
 }
diff --git a/chrome/test/chromedriver/chrome/chrome_android_impl.h b/chrome/test/chromedriver/chrome/chrome_android_impl.h
index 139a0e0..ef27341 100644
--- a/chrome/test/chromedriver/chrome/chrome_android_impl.h
+++ b/chrome/test/chromedriver/chrome/chrome_android_impl.h
@@ -24,6 +24,7 @@
   virtual ~ChromeAndroidImpl();
 
   // Overridden from Chrome:
+  virtual Type GetType() OVERRIDE;
   virtual std::string GetOperatingSystemName() OVERRIDE;
   virtual Status Quit() OVERRIDE;
 
diff --git a/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc b/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc
index c454917..415d7e4 100644
--- a/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc
+++ b/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc
@@ -87,42 +87,58 @@
   base::CloseProcessHandle(process_);
 }
 
+Status ChromeDesktopImpl::WaitForPageToLoad(const std::string& url,
+                                            const base::TimeDelta& timeout,
+                                            scoped_ptr<WebView>* web_view) {
+  base::Time deadline = base::Time::Now() + timeout;
+  std::string id;
+  while (base::Time::Now() < deadline) {
+    WebViewsInfo views_info;
+    Status status = devtools_http_client_->GetWebViewsInfo(&views_info);
+    if (status.IsError())
+      return status;
+
+    for (size_t i = 0; i < views_info.GetSize(); ++i) {
+      if (views_info.Get(i).url.find(url) == 0) {
+        id = views_info.Get(i).id;
+        break;
+      }
+    }
+    if (!id.empty())
+      break;
+    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
+  }
+  if (id.empty())
+    return Status(kUnknownError, "page could not be found: " + url);
+
+  scoped_ptr<WebView> web_view_tmp(new WebViewImpl(
+      id, GetBuildNo(), devtools_http_client_->CreateClient(id), log_));
+  Status status = web_view_tmp->ConnectIfNecessary();
+  if (status.IsError())
+    return status;
+
+  status = web_view_tmp->WaitForPendingNavigations(
+      std::string(), deadline - base::Time::Now(), false);
+  if (status.IsOk())
+    *web_view = web_view_tmp.Pass();
+  return status;
+}
+
+Chrome::Type ChromeDesktopImpl::GetType() {
+  return DESKTOP;
+}
+
 Status ChromeDesktopImpl::GetAutomationExtension(
     AutomationExtension** extension) {
   if (!automation_extension_) {
-    base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(10);
-    std::string id;
-    while (base::Time::Now() < deadline) {
-      WebViewsInfo views_info;
-      Status status = devtools_http_client_->GetWebViewsInfo(&views_info);
-      if (status.IsError())
-        return status;
-
-      for (size_t i = 0; i < views_info.GetSize(); ++i) {
-        if (views_info.Get(i).url.find(
-                "chrome-extension://aapnijgdinlhnhlmodcfapnahmbfebeb") == 0) {
-          id = views_info.Get(i).id;
-          break;
-        }
-      }
-      if (!id.empty())
-        break;
-      base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
-    }
-    if (id.empty())
-      return Status(kUnknownError, "automation extension cannot be found");
-
-    scoped_ptr<WebView> web_view(new WebViewImpl(
-        id, GetBuildNo(), devtools_http_client_->CreateClient(id), log_));
-    Status status = web_view->ConnectIfNecessary();
+    scoped_ptr<WebView> web_view;
+    Status status = WaitForPageToLoad(
+        "chrome-extension://aapnijgdinlhnhlmodcfapnahmbfebeb/"
+        "_generated_background_page.html",
+        base::TimeDelta::FromSeconds(10),
+        &web_view);
     if (status.IsError())
-      return status;
-
-    // Wait for the extension background page to load.
-    status = web_view->WaitForPendingNavigations(
-        std::string(), 5 * 60 * 1000);
-    if (status.IsError())
-      return status;
+      return Status(kUnknownError, "cannot get automation extension", status);
 
     automation_extension_.reset(new AutomationExtension(web_view.Pass()));
   }
diff --git a/chrome/test/chromedriver/chrome/chrome_desktop_impl.h b/chrome/test/chromedriver/chrome/chrome_desktop_impl.h
index bc17620..52c9b98 100644
--- a/chrome/test/chromedriver/chrome/chrome_desktop_impl.h
+++ b/chrome/test/chromedriver/chrome/chrome_desktop_impl.h
@@ -9,12 +9,18 @@
 
 #include "base/compiler_specific.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/process/process.h"
 #include "chrome/test/chromedriver/chrome/chrome_impl.h"
 
+namespace base {
+class TimeDelta;
+}
+
 class AutomationExtension;
 class DevToolsHttpClient;
 class Status;
+class WebView;
 
 class ChromeDesktopImpl : public ChromeImpl {
  public:
@@ -27,7 +33,14 @@
       base::ScopedTempDir* extension_dir);
   virtual ~ChromeDesktopImpl();
 
+  // Waits for a page with the given URL to appear and finish loading.
+  // Returns an error if the timeout is exceeded.
+  Status WaitForPageToLoad(const std::string& url,
+                           const base::TimeDelta& timeout,
+                           scoped_ptr<WebView>* web_view);
+
   // Overridden from Chrome:
+  virtual Type GetType() OVERRIDE;
   virtual Status GetAutomationExtension(
       AutomationExtension** extension) OVERRIDE;
   virtual std::string GetOperatingSystemName() OVERRIDE;
diff --git a/chrome/test/chromedriver/chrome/chrome_existing_impl.cc b/chrome/test/chromedriver/chrome/chrome_existing_impl.cc
new file mode 100644
index 0000000..64ead8d
--- /dev/null
+++ b/chrome/test/chromedriver/chrome/chrome_existing_impl.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 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 "chrome/test/chromedriver/chrome/chrome_existing_impl.h"
+#include "chrome/test/chromedriver/chrome/devtools_http_client.h"
+#include "chrome/test/chromedriver/chrome/status.h"
+
+ChromeExistingImpl::ChromeExistingImpl(
+    scoped_ptr<DevToolsHttpClient> client,
+    ScopedVector<DevToolsEventListener>& devtools_event_listeners,
+    Log* log)
+    : ChromeImpl(client.Pass(), devtools_event_listeners, log) {}
+
+ChromeExistingImpl::~ChromeExistingImpl() {}
+
+Chrome::Type ChromeExistingImpl::GetType() {
+  return EXISTING;
+}
+
+std::string ChromeExistingImpl::GetOperatingSystemName() {
+ return std::string();
+}
+
+Status ChromeExistingImpl::Quit() {
+  return Status(kOk);
+}
+
diff --git a/chrome/test/chromedriver/chrome/chrome_existing_impl.h b/chrome/test/chromedriver/chrome/chrome_existing_impl.h
new file mode 100644
index 0000000..4362dfe
--- /dev/null
+++ b/chrome/test/chromedriver/chrome/chrome_existing_impl.h
@@ -0,0 +1,30 @@
+// Copyright (c) 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 CHROME_TEST_CHROMEDRIVER_CHROME_CHROME_EXISTING_IMPL_H_
+#define CHROME_TEST_CHROMEDRIVER_CHROME_CHROME_EXISTING_IMPL_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/test/chromedriver/chrome/chrome_impl.h"
+
+class DevToolsHttpClient;
+
+class ChromeExistingImpl : public ChromeImpl {
+ public:
+  ChromeExistingImpl(
+      scoped_ptr<DevToolsHttpClient> client,
+      ScopedVector<DevToolsEventListener>& devtools_event_listeners,
+      Log* log);
+  virtual ~ChromeExistingImpl();
+
+  // Overridden from Chrome.
+  virtual Type GetType() OVERRIDE;
+  virtual std::string GetOperatingSystemName() OVERRIDE;
+  virtual Status Quit() OVERRIDE;
+};
+
+#endif  // CHROME_TEST_CHROMEDRIVER_CHROME_CHROME_EXISTING_IMPL_H_
diff --git a/chrome/test/chromedriver/chrome/chrome_finder.cc b/chrome/test/chromedriver/chrome/chrome_finder.cc
index a6e9426..2cb69c1 100644
--- a/chrome/test/chromedriver/chrome/chrome_finder.cc
+++ b/chrome/test/chromedriver/chrome/chrome_finder.cc
@@ -101,10 +101,18 @@
 #endif
   std::vector<base::FilePath> browser_exes(
       browser_exes_array, browser_exes_array + arraysize(browser_exes_array));
-  std::vector<base::FilePath> locations;
   base::FilePath module_dir;
-  if (PathService::Get(base::DIR_MODULE, &module_dir))
-    locations.push_back(module_dir);
+  if (PathService::Get(base::DIR_MODULE, &module_dir)) {
+    for (size_t i = 0; i < browser_exes.size(); ++i) {
+      base::FilePath path = module_dir.Append(browser_exes[i]);
+      if (base::PathExists(path)) {
+        *browser_exe = path;
+        return true;
+      }
+    }
+  }
+
+  std::vector<base::FilePath> locations;
   GetApplicationDirs(&locations);
   return internal::FindExe(
       base::Bind(&base::PathExists),
diff --git a/chrome/test/chromedriver/chrome/chrome_impl.cc b/chrome/test/chromedriver/chrome/chrome_impl.cc
index 720c8db..e4c4971 100644
--- a/chrome/test/chromedriver/chrome/chrome_impl.cc
+++ b/chrome/test/chromedriver/chrome/chrome_impl.cc
@@ -99,6 +99,10 @@
   return Status(kOk);
 }
 
+Status ChromeImpl::ActivateWebView(const std::string& id) {
+  return devtools_http_client_->ActivateWebView(id);
+}
+
 Status ChromeImpl::GetAutomationExtension(AutomationExtension** extension) {
   return Status(kUnknownError, "automation extension not supported");
 }
diff --git a/chrome/test/chromedriver/chrome/chrome_impl.h b/chrome/test/chromedriver/chrome/chrome_impl.h
index 3659dbb..be43cb0 100644
--- a/chrome/test/chromedriver/chrome/chrome_impl.h
+++ b/chrome/test/chromedriver/chrome/chrome_impl.h
@@ -34,6 +34,7 @@
   virtual Status GetWebViewById(const std::string& id,
                                 WebView** web_view) OVERRIDE;
   virtual Status CloseWebView(const std::string& id) OVERRIDE;
+  virtual Status ActivateWebView(const std::string& id) OVERRIDE;
   virtual Status GetAutomationExtension(
       AutomationExtension** extension) OVERRIDE;
 
diff --git a/chrome/test/chromedriver/chrome/devtools_client.h b/chrome/test/chromedriver/chrome/devtools_client.h
index db79210..05f5dfc 100644
--- a/chrome/test/chromedriver/chrome/devtools_client.h
+++ b/chrome/test/chromedriver/chrome/devtools_client.h
@@ -43,8 +43,8 @@
   // Handles events until the given function reports the condition is met
   // and there are no more received events to handle. If the given
   // function ever returns an error, returns immediately with the error.
-  // If the condition is not met within |timeout|ms, kTimeout status
-  // is returned eventually.
+  // If the condition is not met within |timeout|, kTimeout status
+  // is returned eventually. If |timeout| is 0, this function will not block.
   virtual Status HandleEventsUntil(const ConditionalFunc& conditional_func,
                                    const base::TimeDelta& timeout) = 0;
 
diff --git a/chrome/test/chromedriver/chrome/devtools_client_impl.cc b/chrome/test/chromedriver/chrome/devtools_client_impl.cc
index 4077112..c919191 100644
--- a/chrome/test/chromedriver/chrome/devtools_client_impl.cc
+++ b/chrome/test/chromedriver/chrome/devtools_client_impl.cc
@@ -47,6 +47,11 @@
   int* count_;
 };
 
+Status ConditionIsMet(bool* is_condition_met) {
+  *is_condition_met = true;
+  return Status(kOk);
+}
+
 }  // namespace
 
 namespace internal {
@@ -158,17 +163,7 @@
 }
 
 Status DevToolsClientImpl::HandleReceivedEvents() {
-  if (!socket_->IsConnected())
-    return Status(kDisconnected, "not connected to DevTools");
-
-  while (true) {
-    if (!socket_->HasNextMessage())
-      return Status(kOk);
-
-    Status status = ProcessNextMessage(-1);
-    if (status.IsError())
-      return status;
-  }
+  return HandleEventsUntil(base::Bind(&ConditionIsMet), base::TimeDelta());
 }
 
 Status DevToolsClientImpl::HandleEventsUntil(
@@ -177,14 +172,10 @@
     return Status(kDisconnected, "not connected to DevTools");
 
   base::TimeTicks deadline = base::TimeTicks::Now() + timeout;
-
+  base::TimeDelta next_message_timeout = timeout;
   while (true) {
-    if (base::TimeTicks::Now() >= deadline)
-      return Status(kTimeout, base::StringPrintf(
-          "exceeded %dms", static_cast<int>(timeout.InMilliseconds())));
-
     if (!socket_->HasNextMessage()) {
-      bool is_condition_met;
+      bool is_condition_met = false;
       Status status = conditional_func.Run(&is_condition_met);
       if (status.IsError())
         return status;
@@ -192,12 +183,10 @@
         return Status(kOk);
     }
 
-    // To keep this simple, we don't pass the delta time to
-    // ProcessNextMessage. As a result, we may not immediately
-    // return after |timeout|ms.
-    Status status = ProcessNextMessage(-1);
-    if (status.IsError() && status.code() != kTimeout)
+    Status status = ProcessNextMessage(-1, next_message_timeout);
+    if (status.IsError())
       return status;
+    next_message_timeout = deadline - base::TimeTicks::Now();
   }
 }
 
@@ -228,7 +217,8 @@
       make_linked_ptr(new ResponseInfo(method));
   response_info_map_[command_id] = response_info;
   while (response_info->state == kWaiting) {
-    Status status = ProcessNextMessage(command_id);
+    Status status = ProcessNextMessage(
+        command_id, base::TimeDelta::FromMinutes(10));
     if (status.IsError()) {
       if (response_info->state == kReceived)
         response_info_map_.erase(command_id);
@@ -247,7 +237,9 @@
   return Status(kOk);
 }
 
-Status DevToolsClientImpl::ProcessNextMessage(int expected_id) {
+Status DevToolsClientImpl::ProcessNextMessage(
+    int expected_id,
+    const base::TimeDelta& timeout) {
   ScopedIncrementer increment_stack_count(&stack_count_);
 
   Status status = EnsureListenersNotifiedOfConnect();
@@ -266,8 +258,7 @@
     return Status(kOk);
 
   std::string message;
-  switch (socket_->ReceiveNextMessage(&message,
-                                      base::TimeDelta::FromMinutes(1))) {
+  switch (socket_->ReceiveNextMessage(&message, timeout)) {
     case SyncWebSocket::kOk:
       log_->AddEntry(Log::kDebug, "received Inspector response " + message);
       break;
diff --git a/chrome/test/chromedriver/chrome/devtools_client_impl.h b/chrome/test/chromedriver/chrome/devtools_client_impl.h
index 4dad6a5..1fe1f0a 100644
--- a/chrome/test/chromedriver/chrome/devtools_client_impl.h
+++ b/chrome/test/chromedriver/chrome/devtools_client_impl.h
@@ -118,7 +118,7 @@
       const std::string& method,
       const base::DictionaryValue& params,
       scoped_ptr<base::DictionaryValue>* result);
-  Status ProcessNextMessage(int expected_id);
+  Status ProcessNextMessage(int expected_id, const base::TimeDelta& timeout);
   Status ProcessEvent(const internal::InspectorEvent& event);
   Status ProcessCommandResponse(
       const internal::InspectorCommandResponse& response);
diff --git a/chrome/test/chromedriver/chrome/devtools_client_impl_unittest.cc b/chrome/test/chromedriver/chrome/devtools_client_impl_unittest.cc
index 4ccaa2d..6e9fa55 100644
--- a/chrome/test/chromedriver/chrome/devtools_client_impl_unittest.cc
+++ b/chrome/test/chromedriver/chrome/devtools_client_impl_unittest.cc
@@ -66,6 +66,8 @@
   virtual SyncWebSocket::StatusCode ReceiveNextMessage(
       std::string* message,
       const base::TimeDelta& timeout) OVERRIDE {
+    if (timeout <= base::TimeDelta())
+      return SyncWebSocket::kTimeout;
     base::DictionaryValue response;
     response.SetInteger("id", id_);
     base::DictionaryValue result;
diff --git a/chrome/test/chromedriver/chrome/devtools_http_client.cc b/chrome/test/chromedriver/chrome/devtools_http_client.cc
index 5b3cad0..6b8e89b 100644
--- a/chrome/test/chromedriver/chrome/devtools_http_client.cc
+++ b/chrome/test/chromedriver/chrome/devtools_http_client.cc
@@ -65,16 +65,16 @@
 }
 
 DevToolsHttpClient::DevToolsHttpClient(
-    int port,
+    const NetAddress& address,
     scoped_refptr<URLRequestContextGetter> context_getter,
     const SyncWebSocketFactory& socket_factory,
     Log* log)
     : context_getter_(context_getter),
       socket_factory_(socket_factory),
       log_(log),
-      server_url_(base::StringPrintf("http://127.0.0.1:%d", port)),
-      web_socket_url_prefix_(
-          base::StringPrintf("ws://127.0.0.1:%d/devtools/page/", port)) {}
+      server_url_("http://" + address.ToString()),
+      web_socket_url_prefix_(base::StringPrintf(
+          "ws://%s/devtools/page/", address.ToString().c_str())) {}
 
 DevToolsHttpClient::~DevToolsHttpClient() {}
 
@@ -166,6 +166,14 @@
   return Status(kUnknownError, "failed to close window in 20 seconds");
 }
 
+Status DevToolsHttpClient::ActivateWebView(const std::string& id) {
+  std::string data;
+  if (!FetchUrlAndLog(
+          server_url_ + "/json/activate/" + id, context_getter_.get(), &data))
+    return Status(kUnknownError, "cannot activate web view");
+  return Status(kOk);
+}
+
 const std::string& DevToolsHttpClient::version() const {
   return version_;
 }
diff --git a/chrome/test/chromedriver/chrome/devtools_http_client.h b/chrome/test/chromedriver/chrome/devtools_http_client.h
index 552419d..8886521 100644
--- a/chrome/test/chromedriver/chrome/devtools_http_client.h
+++ b/chrome/test/chromedriver/chrome/devtools_http_client.h
@@ -18,6 +18,7 @@
 
 class DevToolsClient;
 class Log;
+class NetAddress;
 class Status;
 class URLRequestContextGetter;
 
@@ -58,7 +59,7 @@
 class DevToolsHttpClient {
  public:
   DevToolsHttpClient(
-      int port,
+      const NetAddress& address,
       scoped_refptr<URLRequestContextGetter> context_getter,
       const SyncWebSocketFactory& socket_factory,
       Log* log);
@@ -72,6 +73,8 @@
 
   Status CloseWebView(const std::string& id);
 
+  Status ActivateWebView(const std::string& id);
+
   const std::string& version() const;
   int build_no() const;
 
diff --git a/chrome/test/chromedriver/chrome/stub_chrome.cc b/chrome/test/chromedriver/chrome/stub_chrome.cc
index 4e93bb2..4f8fc45 100644
--- a/chrome/test/chromedriver/chrome/stub_chrome.cc
+++ b/chrome/test/chromedriver/chrome/stub_chrome.cc
@@ -10,6 +10,10 @@
 
 StubChrome::~StubChrome() {}
 
+Chrome::Type StubChrome::GetType() {
+  return DESKTOP;
+}
+
 std::string StubChrome::GetVersion() {
   return std::string();
 }
@@ -30,6 +34,10 @@
   return Status(kOk);
 }
 
+Status StubChrome::ActivateWebView(const std::string& id) {
+  return Status(kOk);
+}
+
 Status StubChrome::GetAutomationExtension(AutomationExtension** extension) {
   return Status(kOk);
 }
diff --git a/chrome/test/chromedriver/chrome/stub_chrome.h b/chrome/test/chromedriver/chrome/stub_chrome.h
index 73f41ee..e4e4628 100644
--- a/chrome/test/chromedriver/chrome/stub_chrome.h
+++ b/chrome/test/chromedriver/chrome/stub_chrome.h
@@ -19,12 +19,14 @@
   virtual ~StubChrome();
 
   // Overridden from Chrome:
+  virtual Type GetType() OVERRIDE;
   virtual std::string GetVersion() OVERRIDE;
   virtual int GetBuildNo() OVERRIDE;
   virtual Status GetWebViewIds(std::list<std::string>* web_view_ids) OVERRIDE;
   virtual Status GetWebViewById(const std::string& id,
                                 WebView** web_view) OVERRIDE;
   virtual Status CloseWebView(const std::string& id) OVERRIDE;
+  virtual Status ActivateWebView(const std::string& id) OVERRIDE;
   virtual Status GetAutomationExtension(
       AutomationExtension** extension) OVERRIDE;
   virtual std::string GetOperatingSystemName() OVERRIDE;
diff --git a/chrome/test/chromedriver/chrome/stub_web_view.cc b/chrome/test/chromedriver/chrome/stub_web_view.cc
index a40fb60..9d90fd7 100644
--- a/chrome/test/chromedriver/chrome/stub_web_view.cc
+++ b/chrome/test/chromedriver/chrome/stub_web_view.cc
@@ -91,7 +91,8 @@
 }
 
 Status StubWebView::WaitForPendingNavigations(const std::string& frame_id,
-                                              int timeout) {
+                                              const base::TimeDelta& timeout,
+                                              bool stop_load_on_timeout) {
   return Status(kOk);
 }
 
diff --git a/chrome/test/chromedriver/chrome/stub_web_view.h b/chrome/test/chromedriver/chrome/stub_web_view.h
index 952b1b5..b19baf3 100644
--- a/chrome/test/chromedriver/chrome/stub_web_view.h
+++ b/chrome/test/chromedriver/chrome/stub_web_view.h
@@ -54,7 +54,8 @@
   virtual Status DeleteCookie(const std::string& name,
                               const std::string& url) OVERRIDE;
   virtual Status WaitForPendingNavigations(const std::string& frame_id,
-                                           int timeout) OVERRIDE;
+                                           const base::TimeDelta& timeout,
+                                           bool stop_load_on_timeout) OVERRIDE;
   virtual Status IsPendingNavigation(
       const std::string& frame_id, bool* is_pending) OVERRIDE;
   virtual JavaScriptDialogManager* GetJavaScriptDialogManager() OVERRIDE;
diff --git a/chrome/test/chromedriver/chrome/web_view.h b/chrome/test/chromedriver/chrome/web_view.h
index 5e11d59..10decb0 100644
--- a/chrome/test/chromedriver/chrome/web_view.h
+++ b/chrome/test/chromedriver/chrome/web_view.h
@@ -50,6 +50,7 @@
   // the result. |frame| is a frame ID or an empty string for the main frame.
   // If the expression evaluates to a element, it will be bound to a unique ID
   // (per frame) and the ID will be returned.
+  // |result| will never be NULL on success.
   virtual Status EvaluateScript(const std::string& frame,
                                 const std::string& expression,
                                 scoped_ptr<base::Value>* result) = 0;
@@ -59,6 +60,7 @@
   // frame. |args| may contain IDs that refer to previously returned elements.
   // These will be translated back to their referred objects before invoking the
   // function.
+  // |result| will never be NULL on success.
   virtual Status CallFunction(const std::string& frame,
                               const std::string& function,
                               const base::ListValue& args,
@@ -68,6 +70,7 @@
   // two callbacks. The first may be invoked with a value to return to the user.
   // The second may be used to report an error. This function waits until
   // one of the callbacks is invoked or the timeout occurs.
+  // |result| will never be NULL on success.
   virtual Status CallAsyncFunction(const std::string& frame,
                                    const std::string& function,
                                    const base::ListValue& args,
@@ -77,6 +80,7 @@
   // Same as |CallAsyncFunction|, except no additional error callback is passed
   // to the function. Also, |kJavaScriptError| or |kScriptTimeout| is used
   // as the error code instead of |kUnknownError| in appropriate cases.
+  // |result| will never be NULL on success.
   virtual Status CallUserAsyncFunction(const std::string& frame,
                                        const std::string& function,
                                        const base::ListValue& args,
@@ -112,10 +116,12 @@
   // If |frame_id| is "", waits for navigations on the main frame.
   // If a modal dialog appears while waiting, kUnexpectedAlertOpen will be
   // returned.
-  // If there are still pending navigations after |timeout|ms,
-  // page load is stopped, and kTimeout status is returned.
+  // If timeout is exceeded, will return a timeout status.
+  // If |stop_load_on_timeout| is true, will attempt to stop the page load on
+  // timeout before returning the timeout status.
   virtual Status WaitForPendingNavigations(const std::string& frame_id,
-                                           int timeout) = 0;
+                                           const base::TimeDelta& timeout,
+                                           bool stop_load_on_timeout) = 0;
 
   // Returns whether the frame is pending navigation.
   virtual Status IsPendingNavigation(
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.cc b/chrome/test/chromedriver/chrome/web_view_impl.cc
index b463b0e..dd5d8ac 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.cc
+++ b/chrome/test/chromedriver/chrome/web_view_impl.cc
@@ -320,13 +320,15 @@
 }
 
 Status WebViewImpl::WaitForPendingNavigations(const std::string& frame_id,
-                                              int timeout) {
+                                              const base::TimeDelta& timeout,
+                                              bool stop_load_on_timeout) {
   log_->AddEntry(Log::kLog, "waiting for pending navigations...");
   Status status = client_->HandleEventsUntil(
-      base::Bind(&WebViewImpl::IsNotPendingNavigation, base::Unretained(this),
+      base::Bind(&WebViewImpl::IsNotPendingNavigation,
+                 base::Unretained(this),
                  frame_id),
-      base::TimeDelta::FromMilliseconds(timeout));
-  if (status.code() == kTimeout) {
+      timeout);
+  if (status.code() == kTimeout && stop_load_on_timeout) {
     log_->AddEntry(Log::kLog, "timed out. stopping navigations...");
     scoped_ptr<base::Value> unused_value;
     EvaluateScript(std::string(), "window.stop();", &unused_value);
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.h b/chrome/test/chromedriver/chrome/web_view_impl.h
index 239d7b4..1067ad0 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.h
+++ b/chrome/test/chromedriver/chrome/web_view_impl.h
@@ -74,7 +74,8 @@
   virtual Status DeleteCookie(const std::string& name,
                               const std::string& url) OVERRIDE;
   virtual Status WaitForPendingNavigations(const std::string& frame_id,
-                                           int timeout) OVERRIDE;
+                                           const base::TimeDelta& timeout,
+                                           bool stop_load_on_timeout) OVERRIDE;
   virtual Status IsPendingNavigation(
       const std::string& frame_id, bool* is_pending) OVERRIDE;
   virtual JavaScriptDialogManager* GetJavaScriptDialogManager() OVERRIDE;
diff --git a/chrome/test/chromedriver/chrome_launcher.cc b/chrome/test/chromedriver/chrome_launcher.cc
index 6987a26..c9b0e49 100644
--- a/chrome/test/chromedriver/chrome_launcher.cc
+++ b/chrome/test/chromedriver/chrome_launcher.cc
@@ -27,6 +27,7 @@
 #include "base/values.h"
 #include "chrome/test/chromedriver/chrome/chrome_android_impl.h"
 #include "chrome/test/chromedriver/chrome/chrome_desktop_impl.h"
+#include "chrome/test/chromedriver/chrome/chrome_existing_impl.h"
 #include "chrome/test/chromedriver/chrome/chrome_finder.h"
 #include "chrome/test/chromedriver/chrome/device_manager.h"
 #include "chrome/test/chromedriver/chrome/devtools_http_client.h"
@@ -35,8 +36,11 @@
 #include "chrome/test/chromedriver/chrome/status.h"
 #include "chrome/test/chromedriver/chrome/user_data_dir.h"
 #include "chrome/test/chromedriver/chrome/version.h"
+#include "chrome/test/chromedriver/chrome/web_view.h"
 #include "chrome/test/chromedriver/chrome/zip.h"
+#include "chrome/test/chromedriver/net/net_util.h"
 #include "chrome/test/chromedriver/net/url_request_context_getter.h"
+#include "crypto/sha2.h"
 
 namespace {
 
@@ -78,7 +82,8 @@
                           const Capabilities& capabilities,
                           CommandLine* prepared_command,
                           base::ScopedTempDir* user_data_dir,
-                          base::ScopedTempDir* extension_dir) {
+                          base::ScopedTempDir* extension_dir,
+                          std::vector<std::string>* extension_bg_pages) {
   CommandLine command = capabilities.command;
   base::FilePath program = command.GetProgram();
   if (program.empty()) {
@@ -134,8 +139,11 @@
     return Status(kUnknownError,
                   "cannot create temp dir for unpacking extensions");
   }
-  Status status = internal::ProcessExtensions(
-      capabilities.extensions, extension_dir->path(), true, &command);
+  Status status = internal::ProcessExtensions(capabilities.extensions,
+                                              extension_dir->path(),
+                                              true,
+                                              &command,
+                                              extension_bg_pages);
   if (status.IsError())
     return status;
 
@@ -144,13 +152,13 @@
 }
 
 Status WaitForDevToolsAndCheckVersion(
-    int port,
+    const NetAddress& address,
     URLRequestContextGetter* context_getter,
     const SyncWebSocketFactory& socket_factory,
     Log* log,
     scoped_ptr<DevToolsHttpClient>* user_client) {
   scoped_ptr<DevToolsHttpClient> client(new DevToolsHttpClient(
-      port, context_getter, socket_factory, log));
+      address, context_getter, socket_factory, log));
   base::Time deadline = base::Time::Now() + base::TimeDelta::FromSeconds(20);
   Status status = client->Init(deadline - base::Time::Now());
   if (status.IsError())
@@ -172,6 +180,29 @@
   return Status(kUnknownError, "unable to discover open pages");
 }
 
+Status LaunchExistingChromeSession(
+    URLRequestContextGetter* context_getter,
+    const SyncWebSocketFactory& socket_factory,
+    Log* log,
+    const Capabilities& capabilities,
+    ScopedVector<DevToolsEventListener>& devtools_event_listeners,
+    scoped_ptr<Chrome>* chrome) {
+  Status status(kOk);
+  scoped_ptr<DevToolsHttpClient> devtools_client;
+  status = WaitForDevToolsAndCheckVersion(
+      capabilities.use_existing_browser, context_getter, socket_factory, log,
+      &devtools_client);
+  if (status.IsError()) {
+    return Status(kUnknownError, "cannot connect to chrome at " +
+                      capabilities.use_existing_browser.ToString(),
+                  status);
+  }
+  chrome->reset(new ChromeExistingImpl(devtools_client.Pass(),
+      devtools_event_listeners,
+      log));
+  return Status(kOk);
+}
+
 Status LaunchDesktopChrome(
     URLRequestContextGetter* context_getter,
     int port,
@@ -183,8 +214,13 @@
   CommandLine command(CommandLine::NO_PROGRAM);
   base::ScopedTempDir user_data_dir;
   base::ScopedTempDir extension_dir;
-  Status status = PrepareCommandLine(port, capabilities,
-                                     &command, &user_data_dir, &extension_dir);
+  std::vector<std::string> extension_bg_pages;
+  Status status = PrepareCommandLine(port,
+                                     capabilities,
+                                     &command,
+                                     &user_data_dir,
+                                     &extension_dir,
+                                     &extension_bg_pages);
   if (status.IsError())
     return status;
 
@@ -214,7 +250,7 @@
 
   scoped_ptr<DevToolsHttpClient> devtools_client;
   status = WaitForDevToolsAndCheckVersion(
-      port, context_getter, socket_factory, log, &devtools_client);
+      NetAddress(port), context_getter, socket_factory, log, &devtools_client);
 
   if (status.IsError()) {
     int exit_code;
@@ -250,12 +286,25 @@
     }
     return status;
   }
-  chrome->reset(new ChromeDesktopImpl(devtools_client.Pass(),
-                                      devtools_event_listeners,
-                                      log,
-                                      process,
-                                      &user_data_dir,
-                                      &extension_dir));
+  scoped_ptr<ChromeDesktopImpl> chrome_desktop(
+      new ChromeDesktopImpl(devtools_client.Pass(),
+                            devtools_event_listeners,
+                            log,
+                            process,
+                            &user_data_dir,
+                            &extension_dir));
+  for (size_t i = 0; i < extension_bg_pages.size(); ++i) {
+    scoped_ptr<WebView> web_view;
+    Status status = chrome_desktop->WaitForPageToLoad(
+        extension_bg_pages[i], base::TimeDelta::FromSeconds(10), &web_view);
+    if (status.IsError()) {
+      return Status(kUnknownError,
+                    "failed to wait for extension background page to load: " +
+                        extension_bg_pages[i],
+                    status);
+    }
+  }
+  *chrome = chrome_desktop.Pass();
   return Status(kOk);
 }
 
@@ -294,7 +343,7 @@
   }
 
   scoped_ptr<DevToolsHttpClient> devtools_client;
-  status = WaitForDevToolsAndCheckVersion(port,
+  status = WaitForDevToolsAndCheckVersion(NetAddress(port),
                                           context_getter,
                                           socket_factory,
                                           log,
@@ -311,13 +360,22 @@
 
 Status LaunchChrome(
     URLRequestContextGetter* context_getter,
-    int port,
     const SyncWebSocketFactory& socket_factory,
     Log* log,
     DeviceManager* device_manager,
     const Capabilities& capabilities,
     ScopedVector<DevToolsEventListener>& devtools_event_listeners,
     scoped_ptr<Chrome>* chrome) {
+  if (capabilities.IsExistingBrowser()) {
+    return LaunchExistingChromeSession(
+        context_getter, socket_factory,
+        log, capabilities, devtools_event_listeners, chrome);
+  }
+
+  int port;
+  if (!FindOpenPort(&port))
+    return Status(kUnknownError, "failed to find an open port for Chrome");
+
   if (capabilities.IsAndroid()) {
     return LaunchAndroidChrome(
         context_getter, port, socket_factory, log, capabilities,
@@ -331,43 +389,137 @@
 
 namespace internal {
 
+void ConvertHexadecimalToIDAlphabet(std::string* id) {
+  for (size_t i = 0; i < id->size(); ++i) {
+    int val;
+    if (base::HexStringToInt(base::StringPiece(id->begin() + i,
+                                               id->begin() + i + 1),
+                             &val)) {
+      (*id)[i] = val + 'a';
+    } else {
+      (*id)[i] = 'a';
+    }
+  }
+}
+
+std::string GenerateExtensionId(const std::string& input) {
+  uint8 hash[16];
+  crypto::SHA256HashString(input, hash, sizeof(hash));
+  std::string output = StringToLowerASCII(base::HexEncode(hash, sizeof(hash)));
+  ConvertHexadecimalToIDAlphabet(&output);
+  return output;
+}
+
+Status GetExtensionBackgroundPage(const base::DictionaryValue* manifest,
+                                  const std::string& id,
+                                  std::string* bg_page) {
+  std::string bg_page_name;
+  bool persistent = true;
+  manifest->GetBoolean("background.persistent", &persistent);
+  const base::Value* unused_value;
+  if (manifest->Get("background.scripts", &unused_value))
+    bg_page_name = "_generated_background_page.html";
+  manifest->GetString("background.page", &bg_page_name);
+  manifest->GetString("background_page", &bg_page_name);
+  if (bg_page_name.empty() || !persistent)
+    return Status(kOk);
+  *bg_page = "chrome-extension://" + id + "/" + bg_page_name;
+  return Status(kOk);
+}
+
+Status ProcessExtension(const std::string& extension,
+                        const base::FilePath& temp_dir,
+                        base::FilePath* path,
+                        std::string* bg_page) {
+  // Decodes extension string.
+  // Some WebDriver client base64 encoders follow RFC 1521, which require that
+  // 'encoded lines be no more than 76 characters long'. Just remove any
+  // newlines.
+  std::string extension_base64;
+  RemoveChars(extension, "\n", &extension_base64);
+  std::string decoded_extension;
+  if (!base::Base64Decode(extension_base64, &decoded_extension))
+    return Status(kUnknownError, "cannot base64 decode");
+
+  // Get extension's ID from public key in crx file.
+  // Assumes crx v2. See http://developer.chrome.com/extensions/crx.html.
+  std::string key_len_str = decoded_extension.substr(8, 4);
+  if (key_len_str.size() != 4)
+    return Status(kUnknownError, "cannot extract public key length");
+  uint32 key_len = *reinterpret_cast<const uint32*>(key_len_str.c_str());
+  std::string public_key = decoded_extension.substr(16, key_len);
+  if (key_len != public_key.size())
+    return Status(kUnknownError, "invalid public key length");
+  std::string public_key_base64;
+  if (!base::Base64Encode(public_key, &public_key_base64))
+    return Status(kUnknownError, "cannot base64 encode public key");
+  std::string id = GenerateExtensionId(public_key);
+
+  // Unzip the crx file.
+  base::ScopedTempDir temp_crx_dir;
+  if (!temp_crx_dir.CreateUniqueTempDir())
+    return Status(kUnknownError, "cannot create temp dir");
+  base::FilePath extension_crx = temp_crx_dir.path().AppendASCII("temp.crx");
+  int size = static_cast<int>(decoded_extension.length());
+  if (file_util::WriteFile(extension_crx, decoded_extension.c_str(), size) !=
+      size) {
+    return Status(kUnknownError, "cannot write file");
+  }
+  base::FilePath extension_dir = temp_dir.AppendASCII("extension_" + id);
+  if (!zip::Unzip(extension_crx, extension_dir))
+    return Status(kUnknownError, "cannot unzip");
+
+  // Parse the manifest and set the 'key' if not already present.
+  base::FilePath manifest_path(extension_dir.AppendASCII("manifest.json"));
+  std::string manifest_data;
+  if (!file_util::ReadFileToString(manifest_path, &manifest_data))
+    return Status(kUnknownError, "cannot read manifest");
+  scoped_ptr<base::Value> manifest_value(base::JSONReader::Read(manifest_data));
+  base::DictionaryValue* manifest;
+  if (!manifest_value || !manifest_value->GetAsDictionary(&manifest))
+    return Status(kUnknownError, "invalid manifest");
+  if (!manifest->HasKey("key")) {
+    manifest->SetString("key", public_key_base64);
+    base::JSONWriter::Write(manifest, &manifest_data);
+    if (file_util::WriteFile(
+            manifest_path, manifest_data.c_str(), manifest_data.size()) !=
+        static_cast<int>(manifest_data.size())) {
+      return Status(kUnknownError, "cannot add 'key' to manifest");
+    }
+  }
+
+  // Get extension's background page URL, if there is one.
+  std::string bg_page_tmp;
+  Status status = GetExtensionBackgroundPage(manifest, id, &bg_page_tmp);
+  if (status.IsError())
+    return status;
+
+  *path = extension_dir;
+  if (bg_page_tmp.size())
+    *bg_page = bg_page_tmp;
+  return Status(kOk);
+}
+
 Status ProcessExtensions(const std::vector<std::string>& extensions,
                          const base::FilePath& temp_dir,
                          bool include_automation_extension,
-                         CommandLine* command) {
+                         CommandLine* command,
+                         std::vector<std::string>* bg_pages) {
+  std::vector<std::string> bg_pages_tmp;
   std::vector<base::FilePath::StringType> extension_paths;
-  size_t count = 0;
-  for (std::vector<std::string>::const_iterator it = extensions.begin();
-       it != extensions.end(); ++it) {
-    std::string extension_base64;
-    // Decodes extension string.
-    // Some WebDriver client base64 encoders follow RFC 1521, which require that
-    // 'encoded lines be no more than 76 characters long'. Just remove any
-    // newlines.
-    RemoveChars(*it, "\n", &extension_base64);
-    std::string decoded_extension;
-    if (!base::Base64Decode(extension_base64, &decoded_extension))
-      return Status(kUnknownError, "failed to base64 decode extension");
-
-    // Writes decoded extension into a temporary .crx file.
-    base::ScopedTempDir temp_crx_dir;
-    if (!temp_crx_dir.CreateUniqueTempDir())
-      return Status(kUnknownError,
-                    "cannot create temp dir for writing extension CRX file");
-    base::FilePath extension_crx = temp_crx_dir.path().AppendASCII("temp.crx");
-    int size = static_cast<int>(decoded_extension.length());
-    if (file_util::WriteFile(extension_crx, decoded_extension.c_str(), size)
-        != size) {
-      return Status(kUnknownError, "failed to write extension file");
+  for (size_t i = 0; i < extensions.size(); ++i) {
+    base::FilePath path;
+    std::string bg_page;
+    Status status = ProcessExtension(extensions[i], temp_dir, &path, &bg_page);
+    if (status.IsError()) {
+      return Status(
+          kUnknownError,
+          base::StringPrintf("cannot process extension #%" PRIuS, i + 1),
+          status);
     }
-
-    // Unzips the temporary .crx file.
-    count++;
-    base::FilePath extension_dir = temp_dir.AppendASCII(
-        base::StringPrintf("extension%" PRIuS, count));
-    if (!zip::Unzip(extension_crx, extension_dir))
-      return Status(kUnknownError, "failed to unzip the extension CRX file");
-    extension_paths.push_back(extension_dir.value());
+    extension_paths.push_back(path.value());
+    if (bg_page.length())
+      bg_pages_tmp.push_back(bg_page);
   }
 
   if (include_automation_extension) {
@@ -388,6 +540,7 @@
         extension_paths, FILE_PATH_LITERAL(','));
     command->AppendSwitchNative("load-extension", extension_paths_value);
   }
+  bg_pages->swap(bg_pages_tmp);
   return Status(kOk);
 }
 
diff --git a/chrome/test/chromedriver/chrome_launcher.h b/chrome/test/chromedriver/chrome_launcher.h
index 405c275..a226d7e 100644
--- a/chrome/test/chromedriver/chrome_launcher.h
+++ b/chrome/test/chromedriver/chrome_launcher.h
@@ -30,7 +30,6 @@
 
 Status LaunchChrome(
     URLRequestContextGetter* context_getter,
-    int port,
     const SyncWebSocketFactory& socket_factory,
     Log* log,
     DeviceManager* device_manager,
@@ -42,7 +41,8 @@
 Status ProcessExtensions(const std::vector<std::string>& extensions,
                          const base::FilePath& temp_dir,
                          bool include_automation_extension,
-                         CommandLine* command);
+                         CommandLine* command,
+                         std::vector<std::string>* bg_pages);
 Status PrepareUserDataDir(
     const base::FilePath& user_data_dir,
     const base::DictionaryValue* custom_prefs,
diff --git a/chrome/test/chromedriver/chrome_launcher_unittest.cc b/chrome/test/chromedriver/chrome_launcher_unittest.cc
index 369fb9c..c613460 100644
--- a/chrome/test/chromedriver/chrome_launcher_unittest.cc
+++ b/chrome/test/chromedriver/chrome_launcher_unittest.cc
@@ -21,62 +21,80 @@
   CommandLine command(CommandLine::NO_PROGRAM);
   std::vector<std::string> extensions;
   base::FilePath extension_dir;
+  std::vector<std::string> bg_pages;
   Status status = internal::ProcessExtensions(extensions, extension_dir,
-                                              false, &command);
+                                              false, &command, &bg_pages);
   ASSERT_TRUE(status.IsOk());
   ASSERT_FALSE(command.HasSwitch("load-extension"));
+  ASSERT_EQ(0u, bg_pages.size());
 }
 
-TEST(ProcessExtensions, SingleExtension) {
+bool AddExtensionForInstall(const std::string& relative_path,
+                            std::vector<std::string>* extensions) {
   base::FilePath source_root;
   PathService::Get(base::DIR_SOURCE_ROOT, &source_root);
   base::FilePath crx_file_path = source_root.AppendASCII(
-      "chrome/test/data/chromedriver/ext_test_1.crx");
+      "chrome/test/data/chromedriver/" + relative_path);
   std::string crx_contents;
-  ASSERT_TRUE(file_util::ReadFileToString(crx_file_path, &crx_contents));
+  if (!file_util::ReadFileToString(crx_file_path, &crx_contents))
+    return false;
 
-  std::vector<std::string> extensions;
   std::string crx_encoded;
-  ASSERT_TRUE(base::Base64Encode(crx_contents, &crx_encoded));
-  extensions.push_back(crx_encoded);
+  if (!base::Base64Encode(crx_contents, &crx_encoded))
+    return false;
+  extensions->push_back(crx_encoded);
+  return true;
+}
+
+TEST(ProcessExtensions, SingleExtensionWithBgPage) {
+  std::vector<std::string> extensions;
+  ASSERT_TRUE(AddExtensionForInstall("ext_slow_loader.crx", &extensions));
 
   base::ScopedTempDir extension_dir;
   ASSERT_TRUE(extension_dir.CreateUniqueTempDir());
 
   CommandLine command(CommandLine::NO_PROGRAM);
+  std::vector<std::string> bg_pages;
   Status status = internal::ProcessExtensions(extensions, extension_dir.path(),
-                                              false, &command);
+                                              false, &command, &bg_pages);
   ASSERT_TRUE(status.IsOk());
   ASSERT_TRUE(command.HasSwitch("load-extension"));
   base::FilePath temp_ext_path = command.GetSwitchValuePath("load-extension");
   ASSERT_TRUE(base::PathExists(temp_ext_path));
+  std::string manifest_txt;
+  ASSERT_TRUE(file_util::ReadFileToString(
+      temp_ext_path.AppendASCII("manifest.json"), &manifest_txt));
+  scoped_ptr<base::Value> manifest(base::JSONReader::Read(manifest_txt));
+  ASSERT_TRUE(manifest);
+  base::DictionaryValue* manifest_dict = NULL;
+  ASSERT_TRUE(manifest->GetAsDictionary(&manifest_dict));
+  std::string key;
+  ASSERT_TRUE(manifest_dict->GetString("key", &key));
+  ASSERT_EQ(
+      "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8qhZthEHjTIA3IYMzi79s2KFepVziY0du"
+      "JzHcqRUB/YHSGseIUqcYXGazJhDz/"
+      "4FbRg8ef9fQazL1UbMMGBIf4za1kJ2os2MsRrNXzHslkbtcLVj2VfofhuHJmu+"
+      "CnKJ77UWamJiNAaQSiclu4duwnEWrkx+g/8ChQfhZzC4jvQIDAQAB",
+      key);
+  ASSERT_EQ(1u, bg_pages.size());
+  ASSERT_EQ(
+      "chrome-extension://jijhlkpcmmeckhlgdipjhnchhoabdjae/"
+      "_generated_background_page.html",
+      bg_pages[0]);
 }
 
-TEST(ProcessExtensions, MultipleExtensions) {
-  base::FilePath source_root;
-  PathService::Get(base::DIR_SOURCE_ROOT, &source_root);
-  base::FilePath test_ext_path = source_root.AppendASCII(
-      "chrome/test/data/chromedriver");
-  base::FilePath test_crx_1 = test_ext_path.AppendASCII("ext_test_1.crx");
-  base::FilePath test_crx_2 = test_ext_path.AppendASCII("ext_test_2.crx");
-
-  std::string crx_1_contents, crx_2_contents;
-  ASSERT_TRUE(file_util::ReadFileToString(test_crx_1, &crx_1_contents));
-  ASSERT_TRUE(file_util::ReadFileToString(test_crx_2, &crx_2_contents));
-
+TEST(ProcessExtensions, MultipleExtensionsNoBgPages) {
   std::vector<std::string> extensions;
-  std::string crx_1_encoded, crx_2_encoded;
-  ASSERT_TRUE(base::Base64Encode(crx_1_contents, &crx_1_encoded));
-  ASSERT_TRUE(base::Base64Encode(crx_2_contents, &crx_2_encoded));
-  extensions.push_back(crx_1_encoded);
-  extensions.push_back(crx_2_encoded);
+  ASSERT_TRUE(AddExtensionForInstall("ext_test_1.crx", &extensions));
+  ASSERT_TRUE(AddExtensionForInstall("ext_test_2.crx", &extensions));
 
   base::ScopedTempDir extension_dir;
   ASSERT_TRUE(extension_dir.CreateUniqueTempDir());
 
   CommandLine command(CommandLine::NO_PROGRAM);
+  std::vector<std::string> bg_pages;
   Status status = internal::ProcessExtensions(extensions, extension_dir.path(),
-                                              false, &command);
+                                              false, &command, &bg_pages);
   ASSERT_TRUE(status.IsOk());
   ASSERT_TRUE(command.HasSwitch("load-extension"));
   CommandLine::StringType ext_paths = command.GetSwitchValueNative(
@@ -86,6 +104,7 @@
   ASSERT_EQ(2u, ext_path_list.size());
   ASSERT_TRUE(base::PathExists(base::FilePath(ext_path_list[0])));
   ASSERT_TRUE(base::PathExists(base::FilePath(ext_path_list[1])));
+  ASSERT_EQ(0u, bg_pages.size());
 }
 
 namespace {
diff --git a/chrome/test/chromedriver/client/chromedriver.py b/chrome/test/chromedriver/client/chromedriver.py
index ac5c478..ae2ecd8 100644
--- a/chrome/test/chromedriver/client/chromedriver.py
+++ b/chrome/test/chromedriver/client/chromedriver.py
@@ -62,7 +62,7 @@
 
   def __init__(self, server_url, chrome_binary=None, android_package=None,
                chrome_switches=None, chrome_extensions=None,
-               chrome_log_path=None):
+               chrome_log_path=None, chrome_existing_browser=None):
     self._executor = command_executor.CommandExecutor(server_url)
 
     options = {}
@@ -83,13 +83,17 @@
       assert type(chrome_log_path) is str
       options['logPath'] = chrome_log_path
 
+    if chrome_existing_browser:
+      assert type(chrome_existing_browser) is str
+      options['useExistingBrowser'] = chrome_existing_browser
+
     params = {
       'desiredCapabilities': {
         'chromeOptions': options
       }
     }
 
-    self._session_id = self._executor.Execute(
+    self._session_id = self._ExecuteCommand(
         Command.NEW_SESSION, params)['sessionId']
 
   def _WrapValue(self, value):
@@ -122,12 +126,16 @@
     else:
       return value
 
-  def ExecuteCommand(self, command, params={}):
-    params['sessionId'] = self._session_id
+  def _ExecuteCommand(self, command, params={}):
     params = self._WrapValue(params)
     response = self._executor.Execute(command, params)
     if response['status'] != 0:
       raise _ExceptionForResponse(response)
+    return response
+
+  def ExecuteCommand(self, command, params={}):
+    params['sessionId'] = self._session_id
+    response = self._ExecuteCommand(command, params)
     return self._UnwrapValue(response['value'])
 
   def GetWindowHandles(self):
diff --git a/chrome/test/chromedriver/commands.cc b/chrome/test/chromedriver/commands.cc
index ac0eeea..480c157 100644
--- a/chrome/test/chromedriver/commands.cc
+++ b/chrome/test/chromedriver/commands.cc
@@ -79,10 +79,6 @@
     const base::DictionaryValue& params,
     const std::string& session_id,
     scoped_ptr<base::Value>* out_value) {
-  int port;
-  if (!FindOpenPort(&port))
-    return Status(kUnknownError, "failed to find an open port for Chrome");
-
   const base::DictionaryValue* desired_caps;
   if (!params.GetDictionary("desiredCapabilities", &desired_caps))
     return Status(kUnknownError, "cannot find dict 'desiredCapabilities'");
@@ -102,7 +98,6 @@
 
   scoped_ptr<Chrome> chrome;
   status = LaunchChrome(bound_params.context_getter.get(),
-                        port,
                         bound_params.socket_factory,
                         bound_params.log,
                         bound_params.device_manager,
@@ -124,6 +119,7 @@
   session->devtools_logs.swap(devtools_logs);
   session->window = web_view_ids.front();
   session->detach = capabilities.detach;
+  session->force_devtools_screenshot = capabilities.force_devtools_screenshot;
   out_value->reset(session->capabilities->DeepCopy());
   lazy_tls_session.Pointer()->Set(session.release());
   return Status(kOk);
diff --git a/chrome/test/chromedriver/commands_unittest.cc b/chrome/test/chromedriver/commands_unittest.cc
index 064fb95..e377d29 100644
--- a/chrome/test/chromedriver/commands_unittest.cc
+++ b/chrome/test/chromedriver/commands_unittest.cc
@@ -338,7 +338,7 @@
 TEST(CommandsTest, SuccessfulFindElement) {
   FindElementWebView web_view(true, kElementExistsQueryTwice);
   Session session("id");
-  session.implicit_wait = 1000;
+  session.implicit_wait = base::TimeDelta::FromSeconds(1);
   session.SwitchToSubFrame("frame_id1", std::string());
   base::DictionaryValue params;
   params.SetString("using", "id");
@@ -367,7 +367,7 @@
 TEST(CommandsTest, SuccessfulFindElements) {
   FindElementWebView web_view(false, kElementExistsQueryTwice);
   Session session("id");
-  session.implicit_wait = 1000;
+  session.implicit_wait = base::TimeDelta::FromSeconds(1);
   session.SwitchToSubFrame("frame_id2", std::string());
   base::DictionaryValue params;
   params.SetString("using", "name");
@@ -401,7 +401,7 @@
 TEST(CommandsTest, SuccessfulFindChildElement) {
   FindElementWebView web_view(true, kElementExistsQueryTwice);
   Session session("id");
-  session.implicit_wait = 1000;
+  session.implicit_wait = base::TimeDelta::FromSeconds(1);
   session.SwitchToSubFrame("frame_id3", std::string());
   base::DictionaryValue params;
   params.SetString("using", "tag name");
@@ -439,7 +439,7 @@
 TEST(CommandsTest, SuccessfulFindChildElements) {
   FindElementWebView web_view(false, kElementExistsQueryTwice);
   Session session("id");
-  session.implicit_wait = 1000;
+  session.implicit_wait = base::TimeDelta::FromSeconds(1);
   session.SwitchToSubFrame("frame_id4", std::string());
   base::DictionaryValue params;
   params.SetString("using", "class name");
@@ -480,7 +480,7 @@
 TEST(CommandsTest, TimeoutInFindElement) {
   Session session("id");
   FindElementWebView web_view(true, kElementExistsTimeout);
-  session.implicit_wait = 2;
+  session.implicit_wait = base::TimeDelta::FromMilliseconds(2);
   base::DictionaryValue params;
   params.SetString("using", "id");
   params.SetString("value", "a");
diff --git a/chrome/test/chromedriver/element_commands.cc b/chrome/test/chromedriver/element_commands.cc
index 81e83a3..989ed72 100644
--- a/chrome/test/chromedriver/element_commands.cc
+++ b/chrome/test/chromedriver/element_commands.cc
@@ -47,8 +47,7 @@
       return status;
     if (is_focused)
       break;
-    if ((base::Time::Now() - start_time).InMilliseconds() >=
-        session->implicit_wait) {
+    if (base::Time::Now() - start_time >= session->implicit_wait) {
       return Status(kElementNotVisible);
     }
     base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
diff --git a/chrome/test/chromedriver/element_util.cc b/chrome/test/chromedriver/element_util.cc
index 6c4bca0..0ba4246 100644
--- a/chrome/test/chromedriver/element_util.cc
+++ b/chrome/test/chromedriver/element_util.cc
@@ -271,8 +271,7 @@
       }
     }
 
-    if ((base::Time::Now() - start_time).InMilliseconds() >=
-        session->implicit_wait) {
+    if (base::Time::Now() - start_time >= session->implicit_wait) {
       if (only_one) {
         return Status(kNoSuchElement);
       } else {
diff --git a/chrome/test/chromedriver/extension/background.js b/chrome/test/chromedriver/extension/background.js
index 56d7946..16ea857 100644
--- a/chrome/test/chromedriver/extension/background.js
+++ b/chrome/test/chromedriver/extension/background.js
@@ -2,13 +2,49 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+/*
+ * Checks for an extension error that occurred during the asynchronous call.
+ * If an error occurs, will invoke the error callback and throw an exception.
+ *
+ * @param {function(!Error)} errCallback The callback to invoke for error
+ *     reporting.
+ */
+function checkForExtensionError(errCallback) {
+  if (typeof(chrome.extension.lastError) != 'undefined') {
+    var error = new Error(chrome.extension.lastError.message);
+    errCallback(error);
+    throw error;
+  }
+}
+
+/**
+ * Captures a screenshot of the visible tab.
+ *
+ * @param {function(string)} callback The callback to invoke with the base64
+ *     encoded PNG.
+ * @param {function(!Error)} errCallback The callback to invoke for error
+ *     reporting.
+ */
+function captureScreenshot(callback, errCallback) {
+  chrome.tabs.captureVisibleTab({format:'png'}, function(dataUrl) {
+    checkForExtensionError(errCallback);
+    var base64 = ';base64,';
+    callback(dataUrl.substr(dataUrl.indexOf(base64) + base64.length))
+  });
+}
+
 /**
  * Gets info about the current window.
  *
  * @param {function(*)} callback The callback to invoke with the window info.
+ * @param {function(!Error)} errCallback The callback to invoke for error
+ *     reporting.
  */
-function getWindowInfo(callback) {
-  chrome.windows.getCurrent({populate: true}, callback);
+function getWindowInfo(callback, errCallback) {
+  chrome.windows.getCurrent({populate: true}, function(window) {
+    checkForExtensionError(errCallback);
+    callback(window);
+  });
 }
 
 /**
@@ -16,9 +52,15 @@
  *
  * @param {Object} updateInfo Update info to pass to chrome.windows.update.
  * @param {function()} callback Invoked when the updating is complete.
+ * @param {function(!Error)} errCallback The callback to invoke for error
+ *     reporting.
  */
-function updateWindow(updateInfo, callback) {
+function updateWindow(updateInfo, callback, errCallback) {
   chrome.windows.getCurrent({}, function(window) {
-    chrome.windows.update(window.id, updateInfo, callback);
+    checkForExtensionError(errCallback);
+    chrome.windows.update(window.id, updateInfo, function(window) {
+      checkForExtensionError(errCallback);
+      callback();
+    });
   });
 }
diff --git a/chrome/test/chromedriver/extension/manifest.json b/chrome/test/chromedriver/extension/manifest.json
index 79319b2..f058112 100644
--- a/chrome/test/chromedriver/extension/manifest.json
+++ b/chrome/test/chromedriver/extension/manifest.json
@@ -8,6 +8,6 @@
     "scripts": ["background.js"]
   },
   "permissions": [
-     "tabs"
+     "tabs", "<all_urls>"
   ]
 }
diff --git a/chrome/test/chromedriver/js/execute_async_script.js b/chrome/test/chromedriver/js/execute_async_script.js
index ac8d5a7..c55c65b 100644
--- a/chrome/test/chromedriver/js/execute_async_script.js
+++ b/chrome/test/chromedriver/js/execute_async_script.js
@@ -68,7 +68,11 @@
   function reportScriptError(error) {
     var code = isUserSupplied ? StatusCode.JAVASCRIPT_ERROR :
                                 (error.code || StatusCode.UNKNOWN_ERROR);
-    report(code, error.message);
+    var message = error.message;
+    if (error.stack) {
+      message += "\nJavaScript stack:\n" + error.stack;
+    }
+    report(code, message);
   }
   args.push(reportValue);
   if (!isUserSupplied)
diff --git a/chrome/test/chromedriver/js/execute_async_script_test.html b/chrome/test/chromedriver/js/execute_async_script_test.html
index 48a1cd8..d1ebe26 100644
--- a/chrome/test/chromedriver/js/execute_async_script_test.html
+++ b/chrome/test/chromedriver/js/execute_async_script_test.html
@@ -52,12 +52,12 @@
 
   executeAsyncScript('arguments[2](new Error("ERR"))', [33], false);
   assertEquals(StatusCode.UNKNOWN_ERROR, info.result.status);
-  assertEquals('ERR', info.result.value);
+  assertEquals(0, info.result.value.indexOf('ERR'));
 
   executeAsyncScript('var e = new Error("ERR"); e.code = 111; arguments[1](e)',
                      [], false);
   assertEquals(111, info.result.status);
-  assertEquals('ERR', info.result.value);
+  assertEquals(0, info.result.value.indexOf('ERR'));
 }
 
 function testNoResultBeforeTimeout() {
diff --git a/chrome/test/chromedriver/keycode_text_conversion_win.cc b/chrome/test/chromedriver/keycode_text_conversion_win.cc
index 56a912b..b51187c 100644
--- a/chrome/test/chromedriver/keycode_text_conversion_win.cc
+++ b/chrome/test/chromedriver/keycode_text_conversion_win.cc
@@ -40,8 +40,8 @@
     std::string* error_msg) {
   short vkey_and_modifiers = ::VkKeyScanW(key);
   bool translated = vkey_and_modifiers != -1 &&
-                    LOBYTE(vkey_and_modifiers) != -1 &&
-                    HIBYTE(vkey_and_modifiers) != -1;
+                    LOBYTE(vkey_and_modifiers) != 0xFF &&
+                    HIBYTE(vkey_and_modifiers) != 0xFF;
   *error_msg = std::string();
   if (translated) {
     *key_code = static_cast<ui::KeyboardCode>(LOBYTE(vkey_and_modifiers));
diff --git a/chrome/test/chromedriver/net/net_util.cc b/chrome/test/chromedriver/net/net_util.cc
index 2bc7006..490afb8 100644
--- a/chrome/test/chromedriver/net/net_util.cc
+++ b/chrome/test/chromedriver/net/net_util.cc
@@ -2,12 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/test/chromedriver/net/net_util.h"
+
 #include "base/basictypes.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/compiler_specific.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_loop.h"
+#include "base/strings/stringprintf.h"
 #include "base/synchronization/waitable_event.h"
 #include "chrome/test/chromedriver/net/url_request_context_getter.h"
 #include "net/base/ip_endpoint.h"
@@ -63,6 +66,31 @@
 
 }  // namespace
 
+NetAddress::NetAddress() : port_(-1) {}
+
+NetAddress::NetAddress(int port) : host_("127.0.0.1"), port_(port) {}
+
+NetAddress::NetAddress(const std::string& host, int port)
+    : host_(host), port_(port) {}
+
+NetAddress::~NetAddress() {}
+
+bool NetAddress::IsValid() const {
+  return port_ >= 0 && port_ < (1 << 16);
+}
+
+std::string NetAddress::ToString() const {
+  return host_ + base::StringPrintf(":%d", port_);
+}
+
+const std::string& NetAddress::host() const {
+  return host_;
+}
+
+int NetAddress::port() const {
+  return port_;
+}
+
 bool FetchUrl(const std::string& url,
               URLRequestContextGetter* getter,
               std::string* response) {
diff --git a/chrome/test/chromedriver/net/net_util.h b/chrome/test/chromedriver/net/net_util.h
index 723e748..bd6a906 100644
--- a/chrome/test/chromedriver/net/net_util.h
+++ b/chrome/test/chromedriver/net/net_util.h
@@ -9,6 +9,26 @@
 
 class URLRequestContextGetter;
 
+class NetAddress {
+ public:
+  NetAddress();  // Creates an invalid address.
+  explicit NetAddress(int port);  // Host is set to 127.0.0.1.
+  NetAddress(const std::string& host, int port);
+  ~NetAddress();
+
+  bool IsValid() const;
+
+  // Returns host:port.
+  std::string ToString() const;
+
+  const std::string& host() const;
+  int port() const;
+
+ private:
+  std::string host_;
+  int port_;
+};
+
 // Synchronously fetches data from a GET HTTP request to the given URL.
 // Returns true if response is 200 OK and sets response body to |response|.
 bool FetchUrl(const std::string& url,
diff --git a/chrome/test/chromedriver/net/sync_websocket_impl.cc b/chrome/test/chromedriver/net/sync_websocket_impl.cc
index 88814e4..2451fca 100644
--- a/chrome/test/chromedriver/net/sync_websocket_impl.cc
+++ b/chrome/test/chromedriver/net/sync_websocket_impl.cc
@@ -72,17 +72,17 @@
   return success;
 }
 
-SyncWebSocket::StatusCode
-SyncWebSocketImpl::Core::ReceiveNextMessage(
+SyncWebSocket::StatusCode SyncWebSocketImpl::Core::ReceiveNextMessage(
     std::string* message,
     const base::TimeDelta& timeout) {
   base::AutoLock lock(lock_);
   base::TimeTicks deadline = base::TimeTicks::Now() + timeout;
+  base::TimeDelta next_wait = timeout;
   while (received_queue_.empty() && is_connected_) {
-    base::TimeDelta delta = deadline - base::TimeTicks::Now();
-    if (delta <= base::TimeDelta())
+    if (next_wait <= base::TimeDelta())
       return SyncWebSocket::kTimeout;
-    on_update_event_.TimedWait(delta);
+    on_update_event_.TimedWait(next_wait);
+    next_wait = deadline - base::TimeTicks::Now();
   }
   if (!is_connected_)
     return SyncWebSocket::kDisconnected;
diff --git a/chrome/test/chromedriver/net/websocket.cc b/chrome/test/chromedriver/net/websocket.cc
index 4398f9b..76d17da 100644
--- a/chrome/test/chromedriver/net/websocket.cc
+++ b/chrome/test/chromedriver/net/websocket.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/test/chromedriver/net/websocket.h"
 
+#include <string.h>
+
 #include "base/base64.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
@@ -17,24 +19,49 @@
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_util.h"
+#include "net/base/sys_addrinfo.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_util.h"
 #include "net/websockets/websocket_frame.h"
 
+#if defined(OS_WIN)
+#include <Winsock2.h>
+#endif
+
+namespace {
+
+bool ResolveHost(const std::string& host, net::IPAddressNumber* address) {
+  struct addrinfo hints;
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+
+  struct addrinfo* result;
+  if (getaddrinfo(host.c_str(), NULL, &hints, &result))
+    return false;
+
+  for (struct addrinfo* addr = result; addr; addr = addr->ai_next) {
+    if (addr->ai_family == AF_INET || addr->ai_family == AF_INET6) {
+      net::IPEndPoint end_point;
+      if (!end_point.FromSockAddr(addr->ai_addr, addr->ai_addrlen)) {
+        freeaddrinfo(result);
+        return false;
+      }
+      *address = end_point.address();
+    }
+  }
+  freeaddrinfo(result);
+  return true;
+}
+
+}  // namespace
+
 WebSocket::WebSocket(const GURL& url, WebSocketListener* listener)
     : url_(url),
       listener_(listener),
       state_(INITIALIZED),
       write_buffer_(new net::DrainableIOBuffer(new net::IOBuffer(0), 0)),
-      read_buffer_(new net::IOBufferWithSize(4096)) {
-  net::IPAddressNumber address;
-  CHECK(net::ParseIPLiteralToNumber(url_.HostNoBrackets(), &address));
-  int port = 80;
-  base::StringToInt(url_.port(), &port);
-  net::AddressList addresses(net::IPEndPoint(address, port));
-  net::NetLog::Source source;
-  socket_.reset(new net::TCPClientSocket(addresses, NULL, source));
-}
+      read_buffer_(new net::IOBufferWithSize(4096)) {}
 
 WebSocket::~WebSocket() {
   CHECK(thread_checker_.CalledOnValidThread());
@@ -43,6 +70,20 @@
 void WebSocket::Connect(const net::CompletionCallback& callback) {
   CHECK(thread_checker_.CalledOnValidThread());
   CHECK_EQ(INITIALIZED, state_);
+
+  net::IPAddressNumber address;
+  if (!net::ParseIPLiteralToNumber(url_.HostNoBrackets(), &address)) {
+    if (!ResolveHost(url_.HostNoBrackets(), &address)) {
+      callback.Run(net::ERR_ADDRESS_UNREACHABLE);
+      return;
+    }
+  }
+  int port = 80;
+  base::StringToInt(url_.port(), &port);
+  net::AddressList addresses(net::IPEndPoint(address, port));
+  net::NetLog::Source source;
+  socket_.reset(new net::TCPClientSocket(addresses, NULL, source));
+
   state_ = CONNECTING;
   connect_callback_ = callback;
   int code = socket_->Connect(base::Bind(
diff --git a/chrome/test/chromedriver/run_buildbot_steps.py b/chrome/test/chromedriver/run_buildbot_steps.py
index 2b1765d..871011b 100755
--- a/chrome/test/chromedriver/run_buildbot_steps.py
+++ b/chrome/test/chromedriver/run_buildbot_steps.py
@@ -5,10 +5,13 @@
 
 """Runs all the buildbot steps for ChromeDriver except for update/compile."""
 
+import csv
+import datetime
 import optparse
 import os
 import platform
 import shutil
+import StringIO
 import subprocess
 import sys
 import tempfile
@@ -78,6 +81,14 @@
   os.chmod(os.path.join(build_dir, 'chromedriver2_server'), 0700)
 
 
+def GetDownloads():
+  site = 'https://code.google.com/p/chromedriver/downloads/list'
+  s = urllib2.urlopen(site)
+  downloads = s.read()
+  s.close()
+  return downloads
+
+
 def MaybeRelease(revision):
   # Version is embedded as: const char kChromeDriverVersion[] = "0.1";
   # Minimum supported Chrome version is embedded as:
@@ -98,12 +109,7 @@
   zip_name = 'chromedriver_%s%s_%s.zip' % (
       util.GetPlatformName(), bitness, version)
 
-  site = 'https://code.google.com/p/chromedriver/downloads/list'
-  s = urllib2.urlopen(site)
-  downloads = s.read()
-  s.close()
-
-  if zip_name in downloads:
+  if zip_name in GetDownloads():
     return 0
 
   util.MarkBuildStepStart('releasing %s' % zip_name)
@@ -128,7 +134,7 @@
       os.path.join(_THIS_DIR, 'third_party', 'googlecode',
                    'googlecode_upload.py'),
       '--summary',
-      'ChromeDriver server for %s%s (v%s.%s.dyu) supports Chrome v%s-%s' % (
+      'ChromeDriver server for %s%s (v%s.%s) supports Chrome v%s-%s' % (
           util.GetPlatformName(), bitness, version, revision,
           chrome_min_version, chrome_max_version),
       '--project', 'chromedriver',
@@ -138,6 +144,51 @@
   with open(os.devnull, 'wb') as no_output:
     if subprocess.Popen(cmd, stdout=no_output, stderr=no_output).wait():
       util.MarkBuildStepError()
+  MaybeUploadReleaseNotes(version)
+
+def MaybeUploadReleaseNotes(version):
+  name_template = 'release_notes_%s.txt'
+  new_name = name_template % version
+  prev_version = '.'.join([version.split('.')[0],
+                          str(int(version.split('.')[1]) - 1)])
+  old_name = name_template % prev_version
+
+  fixed_issues = []
+  query = ('https://code.google.com/p/chromedriver/issues/csv?'
+           'q=status%3AToBeReleased&colspec=ID%20Summary')
+  issues = StringIO.StringIO(urllib2.urlopen(query).read().split('\n', 1)[1])
+  for issue in csv.reader(issues):
+    if not issue:
+      continue
+    id = issue[0]
+    desc = issue[1]
+    labels = issue[2]
+    fixed_issues += ['Resolved issue %s: %s [%s]' % (id, desc, labels)]
+
+  old_notes = urllib2.urlopen(
+      'https://chromedriver.googlecode.com/files/%s' % old_name).read()
+  new_notes = '----------ChromeDriver v%s (%s)----------\n%s\n\n%s' % (
+      version, datetime.date.today().isoformat(),
+      '\n'.join(fixed_issues),
+      old_notes)
+  release_notes_txt = os.path.join(util.MakeTempDir(), new_name)
+  with open(release_notes_txt, 'w') as f:
+    f.write(new_notes)
+
+  if new_name in GetDownloads():
+    return
+  cmd = [
+      sys.executable,
+      os.path.join(_THIS_DIR, 'third_party', 'googlecode',
+                   'googlecode_upload.py'),
+      '--summary', 'Release notes',
+      '--project', 'chromedriver',
+      '--user', 'chromedriver.bot@gmail.com',
+      release_notes_txt
+  ]
+  with open(os.devnull, 'wb') as no_output:
+    if subprocess.Popen(cmd, stdout=no_output, stderr=no_output).wait():
+      util.MarkBuildStepError()
 
 
 def KillChromes():
diff --git a/chrome/test/chromedriver/session.cc b/chrome/test/chromedriver/session.cc
index ea6da2c..3dc1e01 100644
--- a/chrome/test/chromedriver/session.cc
+++ b/chrome/test/chromedriver/session.cc
@@ -20,31 +20,28 @@
       frame_id(frame_id),
       chromedriver_frame_id(chromedriver_frame_id) {}
 
-const int Session::kDefaultPageLoadTimeoutMs = 5 * 60 * 1000;
+const base::TimeDelta Session::kDefaultPageLoadTimeout =
+    base::TimeDelta::FromMinutes(5);
 
 Session::Session(const std::string& id)
     : id(id),
       quit(false),
       detach(false),
+      force_devtools_screenshot(false),
       sticky_modifiers(0),
       mouse_position(0, 0),
-      implicit_wait(0),
-      page_load_timeout(kDefaultPageLoadTimeoutMs),
-      script_timeout(0) {
-}
+      page_load_timeout(kDefaultPageLoadTimeout) {}
 
 Session::Session(const std::string& id, scoped_ptr<Chrome> chrome)
     : id(id),
       quit(false),
       detach(false),
+      force_devtools_screenshot(false),
       chrome(chrome.Pass()),
       sticky_modifiers(0),
       mouse_position(0, 0),
-      implicit_wait(0),
-      page_load_timeout(kDefaultPageLoadTimeoutMs),
-      script_timeout(0),
-      capabilities(CreateCapabilities()) {
-}
+      page_load_timeout(kDefaultPageLoadTimeout),
+      capabilities(CreateCapabilities()) {}
 
 Session::~Session() {}
 
diff --git a/chrome/test/chromedriver/session.h b/chrome/test/chromedriver/session.h
index 40ec9e9..a93c10a 100644
--- a/chrome/test/chromedriver/session.h
+++ b/chrome/test/chromedriver/session.h
@@ -12,6 +12,7 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/scoped_vector.h"
+#include "base/time/time.h"
 #include "chrome/test/chromedriver/basic_types.h"
 #include "chrome/test/chromedriver/chrome/geoposition.h"
 
@@ -35,7 +36,7 @@
 };
 
 struct Session {
-  static const int kDefaultPageLoadTimeoutMs;
+  static const base::TimeDelta kDefaultPageLoadTimeout;
 
   explicit Session(const std::string& id);
   Session(const std::string& id, scoped_ptr<Chrome> chrome);
@@ -51,6 +52,7 @@
   const std::string id;
   bool quit;
   bool detach;
+  bool force_devtools_screenshot;
   scoped_ptr<Chrome> chrome;
   std::string window;
   int sticky_modifiers;
@@ -59,9 +61,9 @@
   // this list will be empty.
   std::list<FrameInfo> frames;
   WebPoint mouse_position;
-  int implicit_wait;
-  int page_load_timeout;
-  int script_timeout;
+  base::TimeDelta implicit_wait;
+  base::TimeDelta page_load_timeout;
+  base::TimeDelta script_timeout;
   scoped_ptr<std::string> prompt_text;
   scoped_ptr<Geoposition> overridden_geoposition;
   // Logs that populate from DevTools events.
diff --git a/chrome/test/chromedriver/session_commands.cc b/chrome/test/chromedriver/session_commands.cc
index 23f362b..93bb1ad 100644
--- a/chrome/test/chromedriver/session_commands.cc
+++ b/chrome/test/chromedriver/session_commands.cc
@@ -212,18 +212,21 @@
   if (!params.GetString("type", &type))
     return Status(kUnknownError, "'type' must be a string");
 
-  int ms = static_cast<int>(ms_double);
+  base::TimeDelta timeout =
+      base::TimeDelta::FromMilliseconds(static_cast<int>(ms_double));
   // TODO(frankf): implicit and script timeout should be cleared
   // if negative timeout is specified.
-  if (type == "implicit")
-    session->implicit_wait = ms;
-  else if (type == "script")
-    session->script_timeout = ms;
-  else if (type == "page load")
+  if (type == "implicit") {
+    session->implicit_wait = timeout;
+  } else if (type == "script") {
+    session->script_timeout = timeout;
+  } else if (type == "page load") {
     session->page_load_timeout =
-        ((ms < 0) ? Session::kDefaultPageLoadTimeoutMs : ms);
-  else
+        ((timeout < base::TimeDelta()) ? Session::kDefaultPageLoadTimeout
+                                       : timeout);
+  } else {
     return Status(kUnknownError, "unknown type of timeout:" + type);
+  }
   return Status(kOk);
 }
 
@@ -234,7 +237,8 @@
   double ms;
   if (!params.GetDouble("ms", &ms) || ms < 0)
     return Status(kUnknownError, "'ms' must be a non-negative number");
-  session->script_timeout = static_cast<int>(ms);
+  session->script_timeout =
+      base::TimeDelta::FromMilliseconds(static_cast<int>(ms));
   return Status(kOk);
 }
 
@@ -245,7 +249,8 @@
   double ms;
   if (!params.GetDouble("ms", &ms) || ms < 0)
     return Status(kUnknownError, "'ms' must be a non-negative number");
-  session->implicit_wait = static_cast<int>(ms);
+  session->implicit_wait =
+      base::TimeDelta::FromMilliseconds(static_cast<int>(ms));
   return Status(kOk);
 }
 
diff --git a/chrome/test/chromedriver/test/run_all_tests.py b/chrome/test/chromedriver/test/run_all_tests.py
index d8d6852..41c37ee 100755
--- a/chrome/test/chromedriver/test/run_all_tests.py
+++ b/chrome/test/chromedriver/test/run_all_tests.py
@@ -41,7 +41,8 @@
                          ref_chromedriver=None,
                          chrome=None,
                          chrome_version=None,
-                         android_package=None):
+                         android_package=None,
+                         verbose=False):
   cmd = [
       sys.executable,
       os.path.join(_THIS_DIR, script),
@@ -54,6 +55,9 @@
   if chrome_version:
     cmd.append('--chrome-version=' + chrome_version)
 
+  if verbose:
+    cmd.append('--verbose')
+
   if android_package:
     cmd.insert(0, 'xvfb-run')
     cmd.append('--android-package=' + android_package)
@@ -80,7 +84,8 @@
 
 
 def RunJavaTests(chromedriver, chrome=None, chrome_version=None,
-                 chrome_version_name=None, android_package=None):
+                 chrome_version_name=None, android_package=None,
+                 verbose=False):
   version_info = ''
   if chrome_version_name:
     version_info = '(%s)' % chrome_version_name
@@ -91,7 +96,8 @@
                            ref_chromedriver=None,
                            chrome=chrome,
                            chrome_version=chrome_version,
-                           android_package=android_package))
+                           android_package=android_package,
+                           verbose=verbose))
   if code:
     util.MarkBuildStepError()
   return code
@@ -165,7 +171,8 @@
                              android_package=package)
       code2 = RunJavaTests(chromedriver,
                            chrome_version_name=package,
-                           android_package=package)
+                           android_package=package,
+                           verbose=True)
       code = code or code1 or code2
     return code
   else:
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index c347ce2..2b01b5a 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -7,9 +7,12 @@
 
 import base64
 import optparse
+import subprocess
 import os
 import sys
+import socket
 import tempfile
+import threading
 import time
 import unittest
 
@@ -104,6 +107,7 @@
         'ChromeDriverTest.testWindowSize',
         'ChromeDriverTest.testWindowMaximize',
         'ChromeLogPathCapabilityTest.testChromeLogPath',
+        'ExistingBrowserTest.*',
         # Don't enable perf testing on Android yet.
         'PerfTest.testSessionStartTime',
         'PerfTest.testSessionStopTime',
@@ -635,14 +639,32 @@
 class ChromeExtensionsCapabilityTest(ChromeDriverBaseTest):
   """Tests that chromedriver properly processes chromeOptions.extensions."""
 
+  def _PackExtension(self, ext_path):
+    return base64.b64encode(open(ext_path, 'rb').read())
+
   def testExtensionsInstall(self):
     """Checks that chromedriver can take the extensions."""
     crx_1 = os.path.join(_TEST_DATA_DIR, 'ext_test_1.crx')
     crx_2 = os.path.join(_TEST_DATA_DIR, 'ext_test_2.crx')
-    crx_1_encoded = base64.b64encode(open(crx_1, 'rb').read())
-    crx_2_encoded = base64.b64encode(open(crx_2, 'rb').read())
-    extensions = [crx_1_encoded, crx_2_encoded]
-    self.CreateDriver(chrome_extensions=extensions)
+    self.CreateDriver(chrome_extensions=[self._PackExtension(crx_1),
+                                         self._PackExtension(crx_2)])
+
+  def testWaitsForExtensionToLoad(self):
+    did_load_event = threading.Event()
+    server = webserver.SyncWebServer()
+    def RunServer():
+      time.sleep(5)
+      server.RespondWithContent('<html>iframe</html>')
+      did_load_event.set()
+
+    thread = threading.Thread(target=RunServer)
+    thread.daemon = True
+    thread.start()
+    crx = os.path.join(_TEST_DATA_DIR, 'ext_slow_loader.crx')
+    driver = self.CreateDriver(
+        chrome_switches=['user-agent=' + server.GetUrl()],
+        chrome_extensions=[self._PackExtension(crx)])
+    self.assertTrue(did_load_event.is_set())
 
 
 class ChromeLogPathCapabilityTest(ChromeDriverBaseTest):
@@ -670,6 +692,36 @@
     driver.Quit()
 
 
+class ExistingBrowserTest(ChromeDriverBaseTest):
+  """Tests for ChromeDriver existing browser capability."""
+  def setUp(self):
+    self.assertTrue(_CHROME_BINARY is not None,
+                    'must supply a chrome binary arg')
+
+  def testConnectToExistingBrowser(self):
+    port = self.FindFreePort()
+    temp_dir = util.MakeTempDir()
+    process = subprocess.Popen([_CHROME_BINARY,
+                                '--remote-debugging-port=%d' % port,
+                                '--user-data-dir=%s' % temp_dir])
+    if process is None:
+      raise RuntimeError('Chrome could not be started with debugging port')
+    try:
+      hostAndPort = '127.0.0.1:%d' % port
+      driver = self.CreateDriver(chrome_existing_browser=hostAndPort)
+      driver.ExecuteScript('console.info("%s")' % 'connecting at %d!' % port)
+      driver.Quit()
+    finally:
+      process.terminate()
+
+  def FindFreePort(self):
+    for port in range(10000, 10100):
+      try:
+        socket.create_connection(('127.0.0.1', port), 0.2).close()
+      except socket.error:
+        return port
+    raise RuntimeError('Cannot find open port')
+
 class PerfTest(ChromeDriverBaseTest):
   """Tests for ChromeDriver perf."""
   def setUp(self):
diff --git a/chrome/test/chromedriver/test/webserver.py b/chrome/test/chromedriver/test/webserver.py
index fb48cc5..716e0f4 100644
--- a/chrome/test/chromedriver/test/webserver.py
+++ b/chrome/test/chromedriver/test/webserver.py
@@ -117,6 +117,7 @@
     self._root_dir = os.path.abspath(root_dir)
     self._server = _BaseServer(self._OnRequest, server_cert_and_key_path)
     self._thread = threading.Thread(target=self._server.serve_forever)
+    self._thread.daemon = True
     self._thread.start()
     self._path_data_map = {}
     self._path_data_lock = threading.Lock()
diff --git a/chrome/test/chromedriver/util.cc b/chrome/test/chromedriver/util.cc
index 3bde66e..454949e 100644
--- a/chrome/test/chromedriver/util.cc
+++ b/chrome/test/chromedriver/util.cc
@@ -117,6 +117,8 @@
   }
 
   void WriteBytes(const void* bytes, int size) {
+    if (!size)
+      return;
     size_t next = buffer_.length();
     buffer_.resize(next + size);
     memcpy(&buffer_[next], bytes, size);
@@ -150,6 +152,8 @@
     if (iter_ + length > size_)
       return false;
     data->resize(length);
+    if (length == 0)
+      return true;
     return ReadBytes(&(*data)[0], length);
   }
 
diff --git a/chrome/test/chromedriver/window_commands.cc b/chrome/test/chromedriver/window_commands.cc
index ad62a9e..cf376da 100644
--- a/chrome/test/chromedriver/window_commands.cc
+++ b/chrome/test/chromedriver/window_commands.cc
@@ -14,6 +14,7 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "chrome/test/chromedriver/basic_types.h"
+#include "chrome/test/chromedriver/chrome/automation_extension.h"
 #include "chrome/test/chromedriver/chrome/chrome.h"
 #include "chrome/test/chromedriver/chrome/devtools_client.h"
 #include "chrome/test/chromedriver/chrome/geoposition.h"
@@ -215,18 +216,16 @@
       else
         break;
     }
-    nav_status =
-        web_view->WaitForPendingNavigations(session->GetCurrentFrameId(),
-                                            session->page_load_timeout);
+    nav_status = web_view->WaitForPendingNavigations(
+        session->GetCurrentFrameId(), session->page_load_timeout, true);
     if (nav_status.IsError())
       return nav_status;
 
     status = command.Run(session, web_view, params, value);
   }
 
-  nav_status =
-      web_view->WaitForPendingNavigations(session->GetCurrentFrameId(),
-                                          session->page_load_timeout);
+  nav_status = web_view->WaitForPendingNavigations(
+      session->GetCurrentFrameId(), session->page_load_timeout, true);
 
   if (status.IsOk() && nav_status.IsError() &&
       nav_status.code() != kDisconnected &&
@@ -278,7 +277,7 @@
 
   return web_view->CallUserAsyncFunction(
       session->GetCurrentFrameId(), "function(){" + script + "}", *args,
-      base::TimeDelta::FromMilliseconds(session->script_timeout), value);
+      session->script_timeout, value);
 }
 
 Status ExecuteSwitchToFrame(
@@ -732,10 +731,25 @@
     WebView* web_view,
     const base::DictionaryValue& params,
     scoped_ptr<base::Value>* value) {
-  std::string screenshot;
-  Status status = web_view->CaptureScreenshot(&screenshot);
+  Status status = session->chrome->ActivateWebView(web_view->GetId());
   if (status.IsError())
     return status;
+
+  std::string screenshot;
+  if (session->chrome->GetType() == Chrome::DESKTOP &&
+      !session->force_devtools_screenshot) {
+    AutomationExtension* extension = NULL;
+    Status status = session->chrome->GetAutomationExtension(&extension);
+    if (status.IsError())
+      return status;
+    status = extension->CaptureScreenshot(&screenshot);
+    if (status.IsError())
+      return status;
+  } else {
+    Status status = web_view->CaptureScreenshot(&screenshot);
+    if (status.IsError())
+      return status;
+  }
   value->reset(new base::StringValue(screenshot));
   return Status(kOk);
 }
diff --git a/chrome/test/functional/PYAUTO_TESTS b/chrome/test/functional/PYAUTO_TESTS
index 9cac8a9..d80c8e3 100644
--- a/chrome/test/functional/PYAUTO_TESTS
+++ b/chrome/test/functional/PYAUTO_TESTS
@@ -218,7 +218,6 @@
       'chromeos_browser',
       'chromeos_crosh',
       'chromeos_file_browser',
-      'chromeos_offline',
       'chromeos_power',
       'chromeos_prefs',
       'chromeos_security',
@@ -274,8 +273,6 @@
       '-prefs.PrefsTest.testSessionRestore',
       # Deal with i18n chars.  crosbug.com/12639
       '-omnibox.OmniboxTest.testCrazyFilenames',
-      # crosbug.com/16977
-      '-chromeos_wifi_sanity.ChromeosWifiSanity.testConnectToHiddenWiFiNonExistent',
       # crosbug.com/20025
       '-chromeos_browser.ChromeosBrowserTest.testFullScreen',
       # Chrome driver does not work in Chrome OS.
@@ -380,15 +377,6 @@
   'EMPTY': {
   },
 
-  'CHROMEOS_CONNECTIVITY': {
-    'chromeos': [
-      'chromeos_wifi_functional',
-      'chromeos_wifi_compliance',
-      'wifi_downloads',
-      'wifi_notification',
-    ],
-  },
-
   # ChromeOS flash tests.
   'CHROMEOS_FLASH': {
     'chromeos': [
@@ -396,28 +384,6 @@
     ],
   },
 
-  # ChromeOS login tests.
-  'CHROMEOS_LOGIN': {
-    'chromeos': [
-      'chromeos_login',
-      'chromeos_oobe',
-
-      # crosbug.com/32583
-      '-chromeos_login.ChromeosLoginCachedCredentialsUserPod.testCachedCredentialsUserPod',
-    ],
-  },
-
-  # ChromeOS policy tests. Some of these are shared with Desktop Chrome tests
-  # but require custom setup on ChromeOS. Others are specific to ChromeOS only.
-  'CHROMEOS_POLICY': {
-    'chromeos': [
-      'chromeos_ephemeral',
-      'chromeos_device_policy',
-      'chromeos_onc',
-      'chromeos_retail_mode',
-    ],
-  },
-
   # ChromeOS volume tests.
   'CHROMEOS_VOLUME': {
     'chromeos': [
diff --git a/chrome/test/functional/chromeos_cellular_sanity.py b/chrome/test/functional/chromeos_cellular_sanity.py
deleted file mode 100755
index 2f06d48..0000000
--- a/chrome/test/functional/chromeos_cellular_sanity.py
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2011 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 logging
-import os
-import sys
-import time
-
-import pyauto_functional
-import pyauto # pyauto_functional must come before pyauto
-
-class ChromeosCellularSanity(pyauto.PyUITest):
-  """Tests for ChromeOS network related functions."""
-
-  assert os.popen('modem status').read(), 'Device needs modem to run test.'
-
-  def testConnectCellularNetwork(self):
-    """Connect to the cellular network if present."""
-
-    self.ConnectToCellularNetwork()
-    self.assertTrue(self.NetworkScan().get('connected_cellular'),
-                   'Failed to connect to cellular network.')
-    self.DisconnectFromCellularNetwork()
-    self.assertFalse(self.NetworkScan().get('connected_cellular'),
-                     'Failed to disconnect from cellular network.')
-
-
-if __name__ == '__main__':
-  pyauto_functional.Main()
diff --git a/chrome/test/functional/chromeos_device_policy.py b/chrome/test/functional/chromeos_device_policy.py
deleted file mode 100644
index b801d65..0000000
--- a/chrome/test/functional/chromeos_device_policy.py
+++ /dev/null
@@ -1,216 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import pyauto_functional  # Must come before pyauto (and thus, policy_base).
-import policy_base
-
-
-class ChromeosDevicePolicy(policy_base.PolicyTestBase):
-  """Tests various ChromeOS device policies."""
-
-  # Cache user credentials for easy lookup.
-  private_info = policy_base.PolicyTestBase.GetPrivateInfo()
-  credentials = (private_info['prod_enterprise_test_user'],
-                 private_info['prod_enterprise_executive_user'],
-                 private_info['prod_enterprise_sales_user'])
-  _usernames = [credential['username'] for credential in credentials]
-  _passwords = [credential['password'] for credential in credentials]
-
-  def LoginAsGuest(self):
-    self.assertFalse(self.GetLoginInfo()['is_logged_in'],
-                     msg='Expected to be logged out.')
-    policy_base.PolicyTestBase.LoginAsGuest(self)
-    self.assertTrue(self.GetLoginInfo()['is_logged_in'],
-                    msg='Expected to be logged in.')
-
-  def _Login(self, user_index, expect_success):
-    self.assertFalse(self.GetLoginInfo()['is_logged_in'],
-                     msg='Expected to be logged out.')
-    policy_base.PolicyTestBase.Login(self,
-                                     self._usernames[user_index],
-                                     self._passwords[user_index])
-    if expect_success:
-      self.assertTrue(self.GetLoginInfo()['is_logged_in'],
-                      msg='Expected to be logged in.')
-    else:
-      self.assertFalse(self.GetLoginInfo()['is_logged_in'],
-                       msg='Expected to not be logged in.')
-
-  def _CheckGuestModeAvailableInLoginWindow(self):
-    return self.ExecuteJavascriptInOOBEWebUI(
-        """window.domAutomationController.send(
-               !document.getElementById('guestSignin').hidden);
-        """)
-
-  def _CheckGuestModeAvailableInAccountPicker(self):
-    return self.ExecuteJavascriptInOOBEWebUI(
-        """window.domAutomationController.send(
-               !!document.getElementById('pod-row').getPodWithUsername_(''));
-        """)
-
-  def _CheckPodVisible(self, username):
-    javascript = """
-        var pod = document.getElementById('pod-row').getPodWithUsername_('%s');
-        window.domAutomationController.send(!!pod && !pod.hidden);
-        """
-    return self.ExecuteJavascriptInOOBEWebUI(javascript % username)
-
-  def _WaitForPodVisibility(self, username, visible):
-    self.assertTrue(
-        self.WaitUntil(function=lambda: self._CheckPodVisible(username),
-                       expect_retval=visible),
-        msg='Expected pod for user %s to %s be visible.' %
-            (username, '' if visible else 'not'))
-
-  def testGuestModeEnabled(self):
-    """Checks that guest mode login can be enabled/disabled."""
-    self.SetDevicePolicy({'guest_mode_enabled': True})
-    self.assertTrue(self._CheckGuestModeAvailableInLoginWindow(),
-                    msg='Expected guest mode to be available.')
-    self.LoginAsGuest()
-    self.Logout()
-
-    self.SetDevicePolicy({'guest_mode_enabled': False})
-    self.assertFalse(self._CheckGuestModeAvailableInLoginWindow(),
-                     msg='Expected guest mode to not be available.')
-
-    # Log in as a regular so that the pod row contains at least one pod and the
-    # account picker is shown.
-    self._Login(user_index=0, expect_success=True)
-    self.Logout()
-
-    self.SetDevicePolicy({'guest_mode_enabled': True})
-    self.assertTrue(self._CheckGuestModeAvailableInAccountPicker(),
-                    msg='Expected guest mode to be available.')
-    self.LoginAsGuest()
-    self.Logout()
-
-    self.SetDevicePolicy({'guest_mode_enabled': False})
-    self.assertFalse(self._CheckGuestModeAvailableInAccountPicker(),
-                     msg='Expected guest mode to not be available.')
-
-  def testShowUserNamesOnSignin(self):
-    """Checks that the account picker can be enabled/disabled."""
-    # Log in as a regular user so that the pod row contains at least one pod and
-    # the account picker can be shown.
-    self._Login(user_index=0, expect_success=True)
-    self.Logout()
-
-    self.SetDevicePolicy({'show_user_names': False})
-    self._WaitForLoginScreenId('gaia-signin')
-
-    self.SetDevicePolicy({'show_user_names': True})
-    self._WaitForLoginScreenId('account-picker')
-
-  def testUserWhitelistAndAllowNewUsers(self):
-    """Checks that login can be (dis)allowed by whitelist and allow-new-users.
-
-    The test verifies that these two interrelated policies behave as documented
-    in the chrome/browser/policy/proto/chrome_device_policy.proto file. Cases
-    for which the current behavior is marked as "broken" are intentionally
-    ommitted since the broken behavior should be fixed rather than protected by
-    tests.
-    """
-    # No whitelist
-    self.SetDevicePolicy({'allow_new_users': True})
-    self._Login(user_index=0, expect_success=True)
-    self.Logout()
-
-    # Empty whitelist
-    self.SetDevicePolicy({'user_whitelist': []})
-    self._Login(user_index=0, expect_success=True)
-    self.Logout()
-
-    self.SetDevicePolicy({'allow_new_users': True,
-                          'user_whitelist': []})
-    self._Login(user_index=0, expect_success=True)
-    self.Logout()
-
-    # Populated whitelist
-    self.SetDevicePolicy({'user_whitelist': [self._usernames[0]]})
-    self._Login(user_index=0, expect_success=True)
-    self.Logout()
-    self._Login(user_index=1, expect_success=False)
-
-    self.SetDevicePolicy({'allow_new_users': True,
-                          'user_whitelist': [self._usernames[0]]})
-    self._Login(user_index=0, expect_success=True)
-    self.Logout()
-    self._Login(user_index=1, expect_success=True)
-    self.Logout()
-
-    # New users not allowed, populated whitelist
-    self.SetDevicePolicy({'allow_new_users': False,
-                          'user_whitelist': [self._usernames[0]]})
-    self._Login(user_index=0, expect_success=True)
-    self.Logout()
-    self._Login(user_index=1, expect_success=False)
-
-  def testUserWhitelistInAccountPicker(self):
-    """Checks that setting a whitelist removes non-whitelisted user pods."""
-    # Disable the account picker so that the login form is shown and the Login()
-    # automation call can be used.
-    self.PrepareToWaitForLoginFormReload()
-    self.SetDevicePolicy({'show_user_names': False})
-    self.WaitForLoginFormReload()
-
-    # Log in to populate the list of existing users.
-    self._Login(user_index=0, expect_success=True)
-    self.Logout()
-    self._Login(user_index=1, expect_success=True)
-    self.Logout()
-
-    # Enable the account picker.
-    self.SetDevicePolicy({'show_user_names': True})
-    self._WaitForLoginScreenId('account-picker')
-
-    # Check pod visibility with and without a whitelist.
-    self._WaitForPodVisibility(username=self._usernames[0], visible=True)
-    self._WaitForPodVisibility(username=self._usernames[1], visible=True)
-
-    self.SetDevicePolicy({'show_user_names': True,
-                          'user_whitelist': [self._usernames[1]]})
-    self._WaitForPodVisibility(username=self._usernames[0], visible=False)
-    self._WaitForPodVisibility(username=self._usernames[1], visible=True)
-
-    self.SetDevicePolicy({'show_user_names': True})
-    self._WaitForPodVisibility(username=self._usernames[0], visible=True)
-    self._WaitForPodVisibility(username=self._usernames[1], visible=True)
-
-  _timezones = ['America/Barbados', 'Europe/Helsinki']
-
-  def testTimezoneSettingWithoutPolicy(self):
-    """Without timezone policy, timezone changes by user are persistent."""
-    self.SetDevicePolicy(refresh=False)
-
-    for timezone in self._timezones:
-      self._Login(user_index=1, expect_success=True)
-      self.SetTimezone(timezone)
-      self.assertEqual(timezone, self.GetTimeInfo()['timezone'])
-
-      self.Logout()
-      self.assertEqual(timezone, self.GetTimeInfo()['timezone'])
-
-
-  def testTimezoneSettingWithPolicy(self):
-    """With timezone policy, timezone changes by user are reset on logout."""
-    self.SetDevicePolicy({'timezone': self._timezones[0]}, refresh=True)
-
-    # Timezones are set on startup, i.e. everytime when loading the login
-    # screen. Something like a browser restart may work, too.
-    self._Login(user_index=1, expect_success=True)
-    self.Logout()
-
-    self.assertEqual(self._timezones[0], self.GetTimeInfo()['timezone'])
-
-    self._Login(user_index=1, expect_success=True)
-    self.SetTimezone(self._timezones[1])
-    self.assertEqual(self._timezones[1], self.GetTimeInfo()['timezone'])
-
-    self.Logout()
-    self.assertEqual(self._timezones[0], self.GetTimeInfo()['timezone'])
-
-
-if __name__ == '__main__':
-  pyauto_functional.Main()
diff --git a/chrome/test/functional/chromeos_ephemeral.py b/chrome/test/functional/chromeos_ephemeral.py
deleted file mode 100644
index cffd316..0000000
--- a/chrome/test/functional/chromeos_ephemeral.py
+++ /dev/null
@@ -1,172 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import logging
-import os
-import sys
-
-import pyauto_functional  # Must come before pyauto (and thus, policy_base).
-import policy_base
-
-sys.path.append('/usr/local')  # Required to import autotest libs.
-from autotest.cros import constants
-from autotest.cros import cryptohome
-
-
-class ChromeosEphemeral(policy_base.PolicyTestBase):
-  """Tests a policy that makes users ephemeral.
-
-  When this policy is enabled, no persistent information in the form of
-  cryptohome shadow directories or local state prefs should be created for
-  users. Additionally, any persistent information previously accumulated should
-  be cleared when a user first logs in after enabling the policy."""
-
-  _usernames = ('alice@example.com', 'bob@example.com')
-
-  def _SetEphemeralUsersEnabled(self, enabled):
-    """Sets the ephemeral users device policy.
-
-    The show_user_names policy is set to False to ensure that even if the local
-    state is not being automatically cleared, the login screen never shows user
-    pods. This is required by the Login browser automation call.
-    """
-    self.SetDevicePolicy({'ephemeral_users_enabled': enabled,
-                          'show_user_names': False})
-
-  def _DoesVaultDirectoryExist(self, user_index):
-    user_hash = cryptohome.get_user_hash(self._usernames[user_index])
-    return os.path.exists(os.path.join(constants.SHADOW_ROOT, user_hash))
-
-  def _AssertLocalStatePrefsSet(self, user_indexes):
-    expected = sorted([self._usernames[index] for index in user_indexes])
-    # The OAuthTokenStatus pref is populated asynchronously. Checking whether it
-    # is set would lead to an ugly race.
-    for pref in ['LoggedInUsers', 'UserImages', 'UserDisplayEmail', ]:
-      actual = sorted(self.GetLocalStatePrefsInfo().Prefs(pref))
-      self.assertEqual(actual, expected,
-                       msg='Expected to find prefs in local state for users.')
-
-  def _AssertLocalStatePrefsEmpty(self):
-    for pref in ['LoggedInUsers',
-                 'UserImages',
-                 'UserDisplayEmail',
-                 'OAuthTokenStatus']:
-      self.assertFalse(self.GetLocalStatePrefsInfo().Prefs(pref),
-          msg='Expected to not find prefs in local state for any user.')
-
-  def _AssertVaultDirectoryExists(self, user_index):
-    self.assertTrue(self._DoesVaultDirectoryExist(user_index=user_index),
-                    msg='Expected vault shadow directory to exist.')
-
-  def _AssertVaultDirectoryDoesNotExist(self, user_index):
-    self.assertFalse(self._DoesVaultDirectoryExist(user_index=user_index),
-                     msg='Expected vault shadow directory to not exist.')
-
-  def _AssertVaultMounted(self, user_index, ephemeral):
-    if ephemeral:
-      device_regex = constants.CRYPTOHOME_DEV_REGEX_REGULAR_USER_EPHEMERAL
-      fs_regex = constants.CRYPTOHOME_FS_REGEX_TMPFS
-    else:
-      device_regex = constants.CRYPTOHOME_DEV_REGEX_REGULAR_USER_SHADOW
-      fs_regex = constants.CRYPTOHOME_FS_REGEX_ANY
-    self.assertTrue(
-        cryptohome.is_vault_mounted(device_regex=device_regex,
-                                    fs_regex=fs_regex,
-                                    user=self._usernames[user_index],
-                                    allow_fail=True),
-        msg='Expected vault backed by %s to be mounted.' %
-            'tmpfs' if ephemeral else 'shadow directory')
-
-  def _AssertNoVaultMounted(self):
-    self.assertFalse(cryptohome.is_vault_mounted(allow_fail=True),
-                     msg='Did not expect any vault to be mounted.')
-
-  def Login(self, user_index):
-    """Convenience method to login to the usr at the given index."""
-    self.assertFalse(self.GetLoginInfo()['is_logged_in'],
-                     msg='Expected to be logged out.')
-    policy_base.PolicyTestBase.Login(self,
-                                     self._usernames[user_index],
-                                     'dummy_password')
-    self.assertTrue(self.GetLoginInfo()['is_logged_in'],
-                    msg='Expected to be logged in.')
-
-  def testEnablingBeforeSession(self):
-    """Checks that a new session can be made ephemeral."""
-    self.PrepareToWaitForLoginFormReload()
-    self._SetEphemeralUsersEnabled(True)
-    self.WaitForLoginFormReload()
-
-    self.Login(user_index=0)
-    self._AssertLocalStatePrefsEmpty()
-    self._AssertVaultMounted(user_index=0, ephemeral=True)
-    self.Logout()
-
-    self._AssertLocalStatePrefsEmpty()
-    self._AssertNoVaultMounted()
-    self._AssertVaultDirectoryDoesNotExist(user_index=0)
-
-  def testEnablingDuringSession(self):
-    """Checks that an existing non-ephemeral session is not made ephemeral."""
-    self.PrepareToWaitForLoginFormReload()
-    self._SetEphemeralUsersEnabled(False)
-    self.WaitForLoginFormReload()
-
-    self.Login(user_index=0)
-    self._AssertLocalStatePrefsSet(user_indexes=[0])
-    self._AssertVaultMounted(user_index=0, ephemeral=False)
-    self._SetEphemeralUsersEnabled(True)
-    self._AssertLocalStatePrefsSet(user_indexes=[0])
-    self._AssertVaultMounted(user_index=0, ephemeral=False)
-    self.Logout()
-
-    self._AssertLocalStatePrefsEmpty()
-    self._AssertNoVaultMounted()
-    self._AssertVaultDirectoryDoesNotExist(user_index=0)
-
-  def testDisablingDuringSession(self):
-    """Checks that an existing ephemeral session is not made non-ephemeral."""
-    self.PrepareToWaitForLoginFormReload()
-    self._SetEphemeralUsersEnabled(True)
-    self.WaitForLoginFormReload()
-
-    self.Login(user_index=0)
-    self._AssertVaultMounted(user_index=0, ephemeral=True)
-    self._SetEphemeralUsersEnabled(False)
-    self._AssertVaultMounted(user_index=0, ephemeral=True)
-    self.Logout()
-
-    self._AssertLocalStatePrefsEmpty()
-    self._AssertNoVaultMounted()
-    self._AssertVaultDirectoryDoesNotExist(user_index=0)
-
-  def testEnablingEphemeralUsersCleansUp(self):
-    """Checks that persistent information is cleared."""
-    self.PrepareToWaitForLoginFormReload()
-    self._SetEphemeralUsersEnabled(False)
-    self.WaitForLoginFormReload()
-
-    self.Login(user_index=0)
-    self.Logout()
-    self._AssertLocalStatePrefsSet(user_indexes=[0])
-
-    self.Login(user_index=1)
-    self.Logout()
-    self._AssertLocalStatePrefsSet(user_indexes=[0, 1])
-
-    self._AssertVaultDirectoryExists(user_index=0)
-    self._AssertVaultDirectoryExists(user_index=1)
-
-    self._SetEphemeralUsersEnabled(True)
-
-    self.Login(user_index=0)
-    self._AssertVaultMounted(user_index=0, ephemeral=True)
-    self.Logout()
-
-    self._AssertVaultDirectoryDoesNotExist(user_index=0)
-    self._AssertVaultDirectoryDoesNotExist(user_index=1)
-
-
-if __name__ == '__main__':
-  pyauto_functional.Main()
diff --git a/chrome/test/functional/chromeos_gsm_compliance.py b/chrome/test/functional/chromeos_gsm_compliance.py
deleted file mode 100755
index 5644356..0000000
--- a/chrome/test/functional/chromeos_gsm_compliance.py
+++ /dev/null
@@ -1,110 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2011 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 subprocess as sub
-
-import pyauto_functional  # Must be imported before pyauto
-import pyauto
-
-
-class ChromeosGSMCompliance(pyauto.PyUITest):
-  """Tests for ChromeOS GSM compliance.
-
-  Fakes connection to a network and verifies that the network name is correct.
-  """
-
-  _ethernet = None
-
-  def _GetEthernet(self):
-    """Get the ethernet to which the device is connected."""
-    result = sub.Popen('ifconfig | cut -d\' \' -f 1 | grep eth',
-                        stdout=sub.PIPE, shell=True).communicate()
-    self.assertNotEqual(result[0], '', msg='Not connected to Ethernet.')
-    self._ethernet = result[0].rstrip()
-
-  def setUp(self):
-    pyauto.PyUITest.setUp(self)
-    sub.call(['sudo', 'stop', 'cromo'])
-    self._GetEthernet()
-    sub.call(['sudo', '/bin/sh', '/usr/local/lib/flimflam/test/backchannel',
-              'setup', self._ethernet, 'pseudo-modem0'])
-
-  def tearDown(self):
-    if self._ethernet:
-      sub.call(['sudo', '/bin/sh', '/usr/local/lib/flimflam/test/backchannel',
-                'teardown', self._ethernet, 'pseudo-modem0'])
-    pyauto.PyUITest.tearDown(self)
-
-  def _IsFakeGSMRunning(self):
-    """Check if fake-gsm-modem is running.
-
-    Returns:
-      True if fake-gsm-modem process is running and False if not.  
-    """
-    ps = sub.Popen('ps -ef | grep fake-gsm-modem | grep -v grep',
-                     shell=True, stdout=sub.PIPE).communicate()
-    return len(ps[0].split('fake-gsm-modem')) > 0
-
-  def _VerifyBasicCarrierCompliance(self, carrier_name, cellular_name):
-    """Faking the specified carrier and checking the name.
-
-    Args:
-      carrier_name: The name of the carrier.
-      cellular_name: The name in the icon of the cellular network.
-    """
-    fake_gsm = sub.Popen(['sudo', '/usr/bin/python', 
-                          '/usr/local/lib/flimflam/test/fake-gsm-modem',
-                          '-c', carrier_name])
-    self.assertTrue(self._IsFakeGSMRunning(), msg='Fake GSM is not running.')
-    # Wait for the fake GSM to connect.
-    cellular = 'cellular_networks'
-    try:
-      self.assertTrue(self.WaitUntil(lambda: cellular in
-                      self.NetworkScan().keys(),timeout=10, retry_sleep=1))
-    except AssertionError:
-      fake_gsm.terminate()
-      self.assertTrue(False, msg='No cellular networks appeared on scan.')
-    network_info = self.NetworkScan()[cellular]
-    result = any([network_info[key]['name'] == cellular_name
-                  for key in network_info.keys()])
-    fake_gsm.terminate()
-    self.assertTrue(result, msg='The cellular network name did not match %s.'
-                    % cellular_name)
-
-  def testConnectThree(self):
-    """Testing connection to Three."""
-    self._VerifyBasicCarrierCompliance('three', '3')
-
-  def testConnectSFR(self):
-    """Testing connection to SFR."""
-    self._VerifyBasicCarrierCompliance('SFR', 'SFR')
-
-  def testConnectKPN(self):
-    """Testing connection to KPN."""
-    self._VerifyBasicCarrierCompliance('KPN', 'KPN NL')
-
-  def testConnectSimyo(self):
-    """Testing connection to Simyo."""
-    self._VerifyBasicCarrierCompliance('Simyo', 'E-Plus')
-
-  def testConnectMovistar(self):
-    """Testing connection to Movistar."""
-    self._VerifyBasicCarrierCompliance('Movistar', 'Movistar')
-
-  def testConnectThreeita(self):
-    """Testing connection to threeita."""
-    self._VerifyBasicCarrierCompliance('threeita', '3ITA')
-
-  def testConnectAtt(self):
-    """Testing connection to att."""
-    self._VerifyBasicCarrierCompliance('att', 'AT&T')
-
-  def testConnectTmobile(self):
-    """Testing connection to Tmobile."""
-    self._VerifyBasicCarrierCompliance('Tmobile', 'T-Mobile')
-
-
-if __name__ == '__main__':
-  pyauto_functional.Main()
diff --git a/chrome/test/functional/chromeos_login.py b/chrome/test/functional/chromeos_login.py
deleted file mode 100755
index 786b071..0000000
--- a/chrome/test/functional/chromeos_login.py
+++ /dev/null
@@ -1,371 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import os
-import subprocess
-import sys
-
-import pyauto_functional  # Must be imported before pyauto
-import pyauto
-import pyauto_errors
-import test_utils
-
-
-sys.path.append('/usr/local')  # To make autotest libs importable.
-from autotest.cros import cros_ui
-from autotest.cros import cryptohome
-
-
-class ChromeosLogin(pyauto.PyUITest):
-  """TestCases for Logging into ChromeOS."""
-
-  assert os.geteuid() == 0, 'Need to run this test as root'
-
-  def ShouldAutoLogin(self):
-    return False
-
-  def setUp(self):
-    # We want a clean session_manager instance for every run,
-    # so restart ui now.
-    cros_ui.stop(allow_fail=True)
-    cryptohome.remove_all_vaults()
-    cros_ui.start(wait_for_login_prompt=False)
-    pyauto.PyUITest.setUp(self)
-
-  def _ValidCredentials(self, account_type='test_google_account'):
-    """Obtains a valid username and password from a data file.
-
-    Returns:
-      A dictionary with the keys 'username' and 'password'
-    """
-    return self.GetPrivateInfo()[account_type]
-
-  def testExecuteJavascriptInOOBEWebUI(self):
-    """Test that javascript can be executed at the login page."""
-    msg = 'test success'
-    ret = self.ExecuteJavascriptInOOBEWebUI(
-              'window.domAutomationController.send("%s");' % msg)
-    self.assertEqual(ret, msg)
-
-  def testGoodLogin(self):
-    """Test that login is successful with valid credentials."""
-    credentials = self._ValidCredentials()
-    self.Login(credentials['username'], credentials['password'])
-    login_info = self.GetLoginInfo()
-    self.assertTrue(login_info['is_logged_in'], msg='Login failed.')
-
-  def testBadUsername(self):
-    """Test that login fails when passed an invalid username."""
-    self.assertRaises(
-        pyauto_errors.JSONInterfaceError,
-        lambda: self.Login('doesnotexist@fakedomain.org', 'badpassword'))
-    login_info = self.GetLoginInfo()
-    self.assertFalse(login_info['is_logged_in'],
-                     msg='Login succeeded, with bad credentials.')
-
-  def testBadPassword(self):
-    """Test that login fails when passed an invalid password."""
-    credentials = self._ValidCredentials()
-    self.assertRaises(
-        pyauto_errors.JSONInterfaceError,
-        lambda: self.Login(credentials['username'], 'badpassword'))
-    login_info = self.GetLoginInfo()
-    self.assertFalse(login_info['is_logged_in'],
-                     msg='Login succeeded, with bad credentials.')
-
-  def testLoginAsGuest(self):
-    """Test we can login with guest mode."""
-    self.LoginAsGuest()
-    login_info = self.GetLoginInfo()
-    self.assertTrue(login_info['is_logged_in'], msg='Not logged in at all.')
-    self.assertTrue(login_info['is_guest'], msg='Not logged in as guest.')
-
-  def testLockScreenAfterLogin(self):
-    """Test after logging in that the screen can be locked."""
-    self.testGoodLogin()
-    self.assertFalse(self.GetLoginInfo()['is_screen_locked'],
-                     msg='Screen is locked, but the screen was not locked.')
-    self.LockScreen()
-    login_info = self.GetLoginInfo()
-    self.assertTrue(login_info['is_screen_locked'], msg='The screen is not '
-                    'locked after attempting to lock the screen.')
-
-  def testLockAndUnlockScreenAfterLogin(self):
-    """Test locking and unlocking the screen after logging in."""
-    self.testLockScreenAfterLogin()
-    self.UnlockScreen(self._ValidCredentials()['password'])
-    login_info = self.GetLoginInfo()
-    self.assertFalse(login_info['is_screen_locked'],
-                     msg='Screen is locked, but it should have been unlocked.')
-
-  def testLockAndUnlockScreenAfterLoginWithBadPassword(self):
-    """Test locking and unlocking the screen with the wrong password."""
-    self.testLockScreenAfterLogin()
-    self.UnlockScreen('not_the_right_password')
-    login_info = self.GetLoginInfo()
-    self.assertTrue(login_info['is_screen_locked'],
-                     msg='Screen is unlock, but it should have been unlocked '
-                         'since we attempted to unlock with a bad password')
-
-  def testLoginToCreateNewAccount(self):
-    """Test we can login as a guest and create a new account."""
-    self.ShowCreateAccountUI()
-    # The login hook does not wait for the first tab to load, so we wait here.
-    self.assertTrue(
-      self.WaitUntil(self.GetActiveTabTitle, expect_retval='Google Accounts'),
-                     msg='Could not verify that the Accounts tab was opened.')
-    login_info = self.GetLoginInfo()
-    self.assertTrue(login_info['is_guest'], msg='Not logged in as guest.')
-
-  def testGoodLoginForTransitionedDomainAccount(self):
-    """Test that login is successful with valid credentials for a domain.
-
-    ChromeOS only allows GA+ accounts to login, there are also known as
-    transitioned accounts.
-
-    """
-    credentials = self._ValidCredentials(account_type='test_domain_account')
-    self.Login(credentials['username'], credentials['password'])
-    login_info = self.GetLoginInfo()
-    self.assertTrue(login_info['is_logged_in'], msg='Login failed.')
-
-  def testNavigateAfterLogin(self):
-    """Test that page navigation is successful after logging in."""
-    self.testGoodLogin()
-    self.NavigateToURL("http://www.google.com")
-    self.assertEqual(self.GetActiveTabTitle(), 'Google',
-                     msg='Unable to navigate to Google and verify tab title.')
-
-  def testSigningOutFromLockedScreen(self):
-    """Test logout can be performed from the lock screen."""
-    self.testLockScreenAfterLogin()
-    self.SignoutInScreenLocker()
-    self.assertFalse(self.GetLoginInfo()['is_logged_in'],
-                     msg='Still logged in when we should be logged out.')
-
-  def testLoginSequenceSanity(self):
-    """Test that the interface can maintain a connection after multiple logins.
-
-    This test is to verify the stability of the automation interface.
-
-    """
-    self.testGoodLogin()
-    self.Logout()
-    self.testBadPassword()
-    self.testLoginAsGuest()
-    self.Logout()
-    self.testLoginToCreateNewAccount()
-
-  def testLogoutWithNoWindows(self):
-    """Verify logout when no browser windows are present."""
-    self.testGoodLogin()
-    for i in range(5):
-      self.OpenNewBrowserWindow(True)
-    for _ in range(self.GetBrowserWindowCount()):
-      self.CloseBrowserWindow(0)
-    self.assertEqual(0, self.GetBrowserWindowCount(),
-                     msg='Could not close all browser windows')
-    self.Logout()
-    self.testGoodLogin()
-
-  def testInitialLoginState(self):
-    """Verify basic state of browser windows at initial login."""
-    self.testGoodLogin()
-    # Should have 1 browser window with 1 tab.
-    info = self.GetBrowserInfo()
-    self.assertEqual(1, len(info['windows']))
-    self.assertFalse(info['windows'][0]['incognito'],
-        msg='Did not expect incognito window after login')
-    self.assertEqual(1, len(info['windows'][0]['tabs']))
-
-    self.OpenNewBrowserWindow(True)
-    # Should have 2 regular browser windows.
-    info = self.GetBrowserInfo()
-    self.assertEqual(2, len(info['windows']))
-    self.assertFalse(info['windows'][0]['incognito'])
-    self.assertFalse(info['windows'][1]['incognito'],
-        msg='Expected a regular new window.')
-
-  def testProfilePreservedBetweenLogins(self):
-    """Verify that profile is preserved between two login sessions.
-
-    Also verify Local State.
-    """
-    self.testGoodLogin()
-
-    # Build up some history and setup state in "Local State".
-    url = self.GetHttpURLForDataPath('title2.html')
-    self.NavigateToURL(url)
-    # chromeos often takes a while to register URLs into history.
-    self.assertTrue(self.WaitUntil(lambda: self.GetHistoryInfo().History()),
-                    msg='Could not open %s successfully' % url)
-    open('/home/chronos/__magic__', 'w').close()
-    open('/home/chronos/user/__magic__', 'w').close()
-
-    def _VerifyProfile():
-      history = self.GetHistoryInfo().History()
-      self.assertEqual(1, len(history))
-      self.assertEqual(url, history[0]['url'])
-      self.assertTrue(os.path.exists('/home/chronos/__magic__'),
-          msg='/home/chronos/__magic__ did not persist across login sessions')
-      self.assertTrue(os.path.exists('/home/chronos/user/__magic__'),
-          msg='/home/chronos/user/__magic__ did not persist across '
-              'login sessions')
-
-    _VerifyProfile()
-    self.Logout()
-    self.testGoodLogin()  # Re-login with same account.
-    _VerifyProfile()
-
-  def testGuestCrosh(self):
-    """Verify we can use crosh in guest mode."""
-    self.LoginAsGuest()
-    login_info = self.GetLoginInfo()
-    self.assertTrue(login_info['is_logged_in'], msg='Not logged in at all.')
-    self.assertTrue(login_info['is_guest'], msg='Not logged in as guest.')
-    for _ in range(self.GetBrowserWindowCount()):
-      self.CloseBrowserWindow(0)
-    test_utils.OpenCroshVerification(self)
-
-    # Verify crosh prompt.
-    self.WaitForHtermText(text='crosh> ',
-        msg='Could not find "crosh> " prompt')
-    self.assertTrue(
-        self.GetHtermRowsText(start=0, end=2).endswith('crosh> '),
-        msg='Could not find "crosh> " prompt')
-
-    # Run a crosh command.
-    self.SendKeysToHterm('help\\n')
-    self.WaitForHtermText(text='help_advanced',
-        msg='Could not find "help_advanced" in help output.')
-
-    # Exit crosh and close tab.
-    self.SendKeysToHterm('exit\\n')
-    self.WaitForHtermText(text='command crosh completed with exit code 0',
-        msg='Could not exit crosh.')
-
-  def testCroshPreservedBetweenLogins(self):
-    """Verify user can continue after re-login."""
-    self.testGoodLogin()
-    self.CloseBrowserWindow(0)
-    test_utils.OpenCroshVerification(self)
-
-    # Verify crosh prompt.
-    self.WaitForHtermText(text='crosh> ',
-        msg='Could not find "crosh> " prompt')
-    self.assertTrue(
-        self.GetHtermRowsText(start=0, end=2).endswith('crosh> '),
-        msg='Could not find "crosh> " prompt')
-
-    # Open 2 other tabs.
-    self.AppendTab(self.GetHttpURLForDataPath('title2.html'))
-    self.assertEqual('Title Of Awesomeness', self.GetActiveTabTitle(),
-                     msg='Unable to naviage to title2.html and '
-                         'verify tab title.')
-    self.AppendTab(self.GetHttpURLForDataPath('settings', 'image_page.html'))
-    self.assertEqual('Show an image', self.GetActiveTabTitle(),
-                     msg='Unable to navigate to image_page and '
-                         'verify tab title.')
-    self.Logout()
-    self.testGoodLogin()  # Re-Login with same account.
-
-    # Verify 3 tabs are still open after re-login.
-    self.assertEqual(3, len(self.GetBrowserInfo()['windows'][0]['tabs']))
-
-
-class ChromeosLoginCachedCredentialsAddUser(pyauto.PyUITest):
-  """TestCase for failing to add a user  with invalid proxy settings."""
-  assert os.geteuid() == 0, 'Need to run this test as root'
-
-  def ShouldAutoLogin(self):
-    return False
-
-  def setUp(self):
-    # We want a clean session_manager instance for every run,
-    # so restart ui now.
-    cros_ui.stop(allow_fail=True)
-    cryptohome.remove_all_vaults()
-    cros_ui.start(wait_for_login_prompt=False)
-    pyauto.PyUITest.setUp(self)
-
-  def tearDown(self):
-    self.ResetProxySettingsOnChromeOS()
-    pyauto.PyUITest.tearDown(self)
-
-  def _ValidCredentials(self, account_type='test_google_account'):
-    """Obtains a valid username and password from a data file.
-
-    Returns:
-      A dictionary with the keys 'username' and 'password'
-    """
-    return self.GetPrivateInfo()[account_type]
-
-  def testCachedCredentialsAddUser(self):
-    self.SetSharedProxies(True)
-    proxy_config = {
-        'mode': 'fixed_servers',
-        'server': '127.0.0.1'
-    }
-    self.SetProxySettingOnChromeOS(proxy_config);
-
-    """Test that login fails."""
-    credentials = self._ValidCredentials()
-    self.assertRaises(
-        pyauto_errors.JSONInterfaceError,
-        lambda: self.Login(credentials['username'],
-                           credentials['password'])
-    )
-
-class ChromeosLoginCachedCredentialsUserPod(ChromeosLogin):
-  """TestCase for Logging into ChromeOS with cached credentials and
-  invalid proxy settings.
-  """
-  assert os.geteuid() == 0, 'Need to run this test as root'
-
-  def ShouldAutoLogin(self):
-    return False
-
-  def setUp(self):
-    # We want a clean session_manager instance for every run,
-    # so restart ui now.
-    cros_ui.stop(allow_fail=True)
-    cryptohome.remove_all_vaults()
-    cros_ui.start(wait_for_login_prompt=False)
-    pyauto.PyUITest.setUp(self)
-
-  def tearDown(self):
-    self.ResetProxySettingsOnChromeOS()
-    pyauto.PyUITest.tearDown(self)
-
-  def _ValidCredentials(self, account_type='test_google_account'):
-    """Obtains a valid username and password from a data file.
-
-    Returns:
-      A dictionary with the keys 'username' and 'password'
-    """
-    return self.GetPrivateInfo()[account_type]
-
-  def testCachedCredentialsUserPod(self):
-    """Test that we can login without connectivity if we have so before.
-
-    This test is currently disabled because testGoodLogin tries to
-    add a user after setting proxies, which is supposed to fail. To
-    make it pass we need a hook that simply calls Login on the delegate
-    in webui_login_display.cc ::ShowSigninScreenForCreds.
-    """
-    self.testGoodLogin()
-    self.Logout()
-    self.SetSharedProxies(True)
-    proxy_config = {
-        'mode': 'fixed_servers',
-        'server': '127.0.0.1'
-    }
-    self.SetProxySettingOnChromeOS(proxy_config);
-    self.testGoodLogin()
-    self.ResetProxySettingsOnChromeOS()
-
-
-if __name__ == '__main__':
-  pyauto_functional.Main()
diff --git a/chrome/test/functional/chromeos_offline.py b/chrome/test/functional/chromeos_offline.py
deleted file mode 100755
index a412080..0000000
--- a/chrome/test/functional/chromeos_offline.py
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import pyauto_functional
-import pyauto
-
-
-class TestOffline(pyauto.PyUITest):
-  """Tests the offline detection for ChromeOS."""
-
-  assert pyauto.PyUITest.IsChromeOS(), 'Works on ChromeOS only.'
-
-  def tearDown(self):
-    self.RestoreOnline()
-    pyauto.PyUITest.tearDown(self)
-
-  def testGoOffline(self):
-    """Tests the GoOffline pyauto method."""
-
-    self.GoOffline()
-    # Device takes a little bit of time to realize it's offline/online.
-    self.assertTrue(self.WaitUntil(
-                    lambda: self.NetworkScan().get('offline_mode')),
-                    msg='We are not offline.')
-
-
-if __name__ == '__main__':
-  pyauto_functional.Main()
diff --git a/chrome/test/functional/chromeos_onc.py b/chrome/test/functional/chromeos_onc.py
deleted file mode 100755
index 1a7d7e0..0000000
--- a/chrome/test/functional/chromeos_onc.py
+++ /dev/null
@@ -1,144 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import os
-
-import pyauto_functional  # must come before pyauto.
-import policy_base
-import pyauto
-
-
-class ChromeosONC(policy_base.PolicyTestBase):
-  """
-  Tests for Open Network Configuration (ONC).
-
-  Open Network Configuration (ONC) files is a json dictionary
-  that contains network configurations and is pulled via policies.
-  These tests verify that ONC files that are formatted correctly
-  add the network/certificate to the device.
-  """
-
-  ONC_PATH = os.path.join(pyauto.PyUITest.ChromeOSDataDir(), 'network')
-
-  def setUp(self):
-    self.CleanupFlimflamDirsOnChromeOS()
-    policy_base.PolicyTestBase.setUp(self)
-    self.LoginWithTestAccount()
-
-  def _ReadONCFileAndSet(self, filename):
-    """Reads the specified ONC file and sends it as a policy.
-
-    Inputs:
-      filename: The filename of the ONC file.  ONC files should
-                all be stored in the path defined by ONC_PATH.
-    """
-    with open(os.path.join(self.ONC_PATH, filename)) as fp:
-      self.SetUserPolicy({'OpenNetworkConfiguration': fp.read()})
-
-  def _VerifyRememberedWifiNetworks(self, wifi_expect):
-    """Verify the list of remembered networks contains those in wifi_expect.
-
-    Inputs:
-      wifi_expect: A dictionary of wifi networks where the key is the ssid
-                      and the value is the encryption type of the network.
-    """
-    # Sometimes there is a race condition where upon restarting chrome
-    # NetworkScan has not populated the network lists yet.  We should
-    # scan until the device is online.
-    self.WaitUntil(lambda: not self.NetworkScan().get('offline_mode', True))
-    networks = self.NetworkScan()
-
-    # Temprorary dictionary to keep track of which wifi networks
-    # have been visited by removing them as we see them.
-    wifi_expect_temp = dict(wifi_expect)
-
-    for service, wifi_dict in networks['remembered_wifi'].iteritems():
-      if isinstance(wifi_dict, dict) and \
-         'encryption' in wifi_dict and \
-         'name' in wifi_dict:
-
-        msg = ('Wifi network %s was in the remembered_network list but '
-               'shouldn\'t be.' % wifi_dict['name'])
-
-        # wifi_dict['encryption'] will always be a string and not None.
-        self.assertTrue(wifi_expect.get(wifi_dict['name'], None) ==
-                        wifi_dict['encryption'], msg)
-
-        del wifi_expect_temp[wifi_dict['name']]
-
-    # Error if wifi_expect_temp is not empty.
-    self.assertFalse(wifi_expect_temp, 'The following networks '
-                     'were not remembered: %s' % self.pformat(wifi_expect_temp))
-
-  def testONCAddOpenWifi(self):
-    """Test adding open network."""
-    wifi_networks = {
-        'ssid-none': '',
-    }
-
-    self._ReadONCFileAndSet('toplevel_wifi_open.onc')
-    self._VerifyRememberedWifiNetworks(wifi_networks)
-
-  def testONCAddWEPWifi(self):
-    """Test adding WEP network."""
-    wifi_networks = {
-        'ssid-wep': 'WEP',
-    }
-
-    self._ReadONCFileAndSet('toplevel_wifi_wep_proxy.onc')
-    self._VerifyRememberedWifiNetworks(wifi_networks)
-
-  def testONCAddPSKWifi(self):
-    """Test adding WPA network."""
-    wifi_networks = {
-        'ssid-wpa': 'WPA',
-    }
-    self._ReadONCFileAndSet('toplevel_wifi_wpa_psk.onc')
-    self._VerifyRememberedWifiNetworks(wifi_networks)
-
-  def testAddBacktoBackONC(self):
-    """Test adding three different ONC files one after the other."""
-    test_dict = {
-      'toplevel_wifi_open.onc': { 'ssid-none': '' },
-      'toplevel_wifi_wep_proxy.onc': { 'ssid-wep': 'WEP' },
-      'toplevel_wifi_wpa_psk.onc': { 'ssid-wpa': 'WPA' },
-    }
-
-    for onc, wifi_networks in test_dict.iteritems():
-      self._ReadONCFileAndSet(onc)
-      self._VerifyRememberedWifiNetworks(wifi_networks)
-
-  def testAddBacktoBackONC2(self):
-    """Test adding three different ONC files one after the other.
-
-    Due to inconsistent behaviors as addressed in crosbug.com/27862
-    this test does not perform a network scan/verification between
-    the setting of policies.
-    """
-
-    wifi_networks = {
-      'ssid-wpa': 'WPA',
-    }
-
-    self._ReadONCFileAndSet('toplevel_wifi_open.onc')
-    self._ReadONCFileAndSet('toplevel_wifi_wep_proxy.onc')
-    self._ReadONCFileAndSet('toplevel_wifi_wpa_psk.onc')
-
-    # Verify that only the most recent onc is updated.
-    self._VerifyRememberedWifiNetworks(wifi_networks)
-
-  def testAddONCWithUnknownFields(self):
-    """Test adding an ONC file with unknown fields."""
-    wifi_networks = {
-        'ssid-none': '',
-        'ssid-wpa': 'WPA'
-    }
-
-    self._ReadONCFileAndSet('toplevel_with_unknown_fields.onc')
-    self._VerifyRememberedWifiNetworks(wifi_networks)
-
-
-if __name__ == '__main__':
-  pyauto_functional.Main()
diff --git a/chrome/test/functional/chromeos_oobe.py b/chrome/test/functional/chromeos_oobe.py
deleted file mode 100755
index d7804d8..0000000
--- a/chrome/test/functional/chromeos_oobe.py
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import os
-import subprocess
-import sys
-
-import pyauto_functional  # Must be imported before pyauto
-import pyauto
-
-
-sys.path.append('/usr/local')  # To make autotest libs importable.
-from autotest.cros import cros_ui
-from autotest.cros import cryptohome
-
-
-class ChromeosOOBE(pyauto.PyUITest):
-  """TestCases for ChromeOS OOBE wizard flow."""
-
-  assert os.geteuid() == 0, 'Need to run this test as root'
-
-  def ShouldOOBESkipToLogin(self):
-    """Do not skip OOBE."""
-    return False
-
-  def setUp(self):
-    # We want a clean session_manager instance for every run,
-    # so restart ui now.
-    cros_ui.stop(allow_fail=True)
-    cryptohome.remove_all_vaults()
-    cros_ui.start(wait_for_login_prompt=False)
-    pyauto.PyUITest.setUp(self)
-
-  def _AssertCurrentScreen(self, screen_name):
-    """Verifies current OOBE screen.
-
-    Args:
-      screen_name: expected current screen name.
-    """
-    self.assertEqual(screen_name, self.GetOOBEScreenInfo()['screen_name'])
-
-  def testBasicFlow(self):
-    """Test that basic OOBE flow works."""
-    self._AssertCurrentScreen('network')
-    # Network -> EULA (on Google Chrome builds, Update on Chromium).
-    ret = self.AcceptOOBENetworkScreen()
-    if self.GetBrowserInfo()['properties']['branding'] == 'Google Chrome':
-      self.assertEquals('eula', ret['next_screen'])
-      self._AssertCurrentScreen('eula')
-      # EULA (accepted) -> Update.
-      ret = self.AcceptOOBEEula(accepted=True)
-    # Update may have already been completed, so don't check for it.
-    # Update (canceled) -> Login.
-    ret = self.CancelOOBEUpdate()
-    self.assertEquals('login', ret['next_screen'])
-    self._AssertCurrentScreen('login')
-    # Login -> User picker.
-    credentials = self.GetPrivateInfo()['test_google_account']
-    self.Login(credentials['username'], credentials['password'])
-    login_info = self.GetLoginInfo()
-    self.assertTrue(login_info['is_logged_in'], msg='Login after OOBE failed.')
-    # User Picker -> normal browser session.
-    ret = self.PickUserImage(3)
-    self.assertEquals('session', ret['next_screen'])
-    # Should have 1 browser windows ("Getting started").
-    self.assertEqual(1, len(self.GetBrowserInfo()['windows']))
-    # Verify user image selection.
-    self.assertEqual(3, self.GetLoginInfo()['user_image'])
-
-
-if __name__ == '__main__':
-  pyauto_functional.Main()
diff --git a/chrome/test/functional/chromeos_retail_mode.py b/chrome/test/functional/chromeos_retail_mode.py
deleted file mode 100644
index 133e662..0000000
--- a/chrome/test/functional/chromeos_retail_mode.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import pyauto_functional  # Must come before pyauto (and thus, policy_base).
-import policy_base
-
-
-class ChromeosRetailMode(policy_base.PolicyTestBase):
-  """Tests for retail mode."""
-
-  # The inherited setUp() method fakes enterprise enrollment. Setting the mode
-  # to 'kiosk' causes enrollment into retail mode instead of the default
-  # enterprise mode.
-  mode = 'kiosk'
-  machine_id = 'KIOSK'
-
-  def ShouldOOBESkipToLogin(self):
-    # There's no OOBE to skip.
-    return False
-
-  def _CheckOnRetailModeLoginScreen(self):
-    """Checks that the retail mode login screen is visible."""
-    return self.ExecuteJavascriptInOOBEWebUI(
-        """window.domAutomationController.send(
-               !!document.getElementById('demo-login-text'));
-        """)
-
-  def Login(self):
-    """Login to retail mode by simulating a mouse click."""
-    self.ExecuteJavascriptInOOBEWebUI(
-        """var event = document.createEvent("MouseEvent");
-           event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0,
-               false, false, false, false, 0, null);
-           window.domAutomationController.send(
-              document.getElementById('page').dispatchEvent(event));
-        """)
-
-  def testLogin(self):
-    """Tests retail mode login."""
-    self.assertTrue(self._CheckOnRetailModeLoginScreen(),
-                    msg='Expected to be on the retail mode login screen.')
-
-    self.Login()
-    self.assertTrue(self.GetLoginInfo()['is_logged_in'],
-                    msg='Expected to be logged in.')
-    self.Logout()
-
-
-if __name__ == '__main__':
-  pyauto_functional.Main()
diff --git a/chrome/test/functional/chromeos_txt_msg_functional.py b/chrome/test/functional/chromeos_txt_msg_functional.py
deleted file mode 100755
index 9019e24..0000000
--- a/chrome/test/functional/chromeos_txt_msg_functional.py
+++ /dev/null
@@ -1,161 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2011 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.
-
-from email.MIMEText import MIMEText
-import logging
-import os
-import re
-import smtplib
-import sys
-import urllib
-
-import pyauto_functional
-import pyauto
-
-sys.path.append(os.path.join(pyauto.PyUITest.DataDir(), 'pyauto_private',
-                'chromeos', 'network'))
-from gsm_sim_info import SIM, PROVIDER_TXT_SERVER
-
-
-class ChromeosTxtMsgSanity(pyauto.PyUITest):
-    """Tests for ChromeOS text message handling"""
-
-    def _SendText(self, mail_server, sender, phone_number,
-                   mobile_provider, msg):
-        """Sends a text message to a specific phone
-
-        Args:
-            mail_server: An SMTP instance.
-            sender: Sender's email address.
-            phone_number: The phone number the txt message is directed to.
-            mobile_provider: A cellular provider defined in
-                             gsm_sim_info.PROVIDER_TXT_SERVER
-            msg: The message to be sent.
-
-        """
-        recipient = ('%s@%s' % (phone_number,
-                     PROVIDER_TXT_SERVER[mobile_provider]))
-        self._SendMail(mail_server, sender, recipient, None, msg)
-
-    def _SendMail(self, mail_server, sender, recipients,
-                   msg_subject, msg_body):
-        """Sends an email using the provided smtp connection
-
-        Args:
-            mail_server: An SMTP instace.
-            sender: Senders email address.
-            recipients: Recipients email address.
-            msg_subject: The subject line of the email.
-            msg_body: The body of the email.
-        """
-        msg = MIMEText(msg_body)
-        msg['To'] = recipients
-        msg['From'] = sender
-        if msg_subject:
-            msg['Subject'] = msg_subject
-        mail_server.sendmail(sender, recipients, msg.as_string())
-
-    def _GetGmailServerInstance(self, email, password):
-        """Creates an SMTP connection with the gmail mail server
-
-        Args:
-            email: A gmail address.
-            password: The password for the gmail address.
-
-        Returns:
-            An SMTP connection instance.
-        """
-        mail_server = smtplib.SMTP('smtp.gmail.com', 587)
-        mail_server.starttls()
-        mail_server.ehlo()
-        mail_server.login(email, password)
-        return mail_server
-
-    def _GetIMSI(self):
-        """Obtains the IMSI by running modem status
-
-        Returns:
-            IMSI of device
-        """
-        modem_status = os.popen('modem status').read()
-        imsi = re.search('IMSI:\s(\d+)', modem_status)
-        if not imsi:
-            raise Exception('GSM Modem not detected in device')
-        return imsi.groups()[0]
-
-    def _GetSIMInfo(self):
-        """Returns information necessary to send messages
-
-        Returns:
-            A dictionary with the following format
-            {
-                'mdn' : <phone number>,
-                'carrier': <carrier name>
-            }
-        """
-        imsi = self._GetIMSI()
-        sim_info = SIM.get(imsi, {})
-        if not sim_info:
-            raise Exception('Phone number for sim with IMSI=%s is not '
-                            'recognized within config file' % imsi)
-        return sim_info
-
-    def setUp(self):
-        # Connect to cellular service if not already connected.
-        pyauto.PyUITest.setUp(self)
-        connected_cellular = self.NetworkScan().get('connected_cellular')
-        if not connected_cellular:
-            self.ConnectToCellularNetwork()
-            if not self.NetworkScan().get('connected_cellular'):
-                raise Exception('Could not connect to cellular service.')
-        else:
-            logging.debug('Already connected to cellular service %s' %
-                          connected_cellular)
-
-        # Obtain sender, recipient, and SMTP instance.
-        self.credentials = self.GetPrivateInfo()['test_account_with_smtp']
-        self.sim = self._GetSIMInfo()
-        self.mail_server = self._GetGmailServerInstance(
-                             self.credentials['username'],
-                             self.credentials['password'])
-
-    def tearDown(self):
-        self.DisconnectFromCellularNetwork()
-        self.mail_server.close()
-        for window in range(len(self.GetActiveNotifications())):
-            self.CloseNotification(window)
-        pyauto.PyUITest.tearDown(self)
-
-    def testTxtMsgNotification(self):
-        """Notifications are displayed for text messages"""
-        msg = 'This is the text message'
-        self._SendText(self.mail_server, self.credentials['username'],
-                        self.sim['mdn'], self.sim['carrier'], msg)
-        self.WaitForNotificationCount(1)
-        notification_result = self.GetActiveNotifications()[0]['content_url']
-        self.assertTrue(re.search(urllib.pathname2url(msg),
-                        notification_result), 'Invalid message was displayed.  '
-                        'Expected "%s" but did not find it"' % msg)
-
-    def testLongTxtMsgNotification(self):
-        """Notifications are displayed for long (>160 char) text messages."""
-        long_msg = 'This is a really long message with spaces. Testing to '\
-                   'make sure that chromeos is able to catch it and '\
-                   'create a notifications for this message.'
-        self._SendText(self.mail_server, self.credentials['username'],
-                        self.sim['mdn'], self.sim['carrier'], long_msg)
-        self.WaitForNotificationCount(1)
-
-        # GetActiveNotifications throws an exception if the text message never
-        # arrives.
-        txt_msg = self.GetActiveNotifications()[0]
-        txt_msg = txt_windows[0]['content_url']
-        self.assertTrue(re.search(urllib.pathname2url(long_msg),
-                        txt_msg), 'Invalid message was displayed. '
-                        'Expected "%s" but did not find it"' % long_msg)
-
-
-if __name__ == '__main__':
-    pyauto_functional.Main()
diff --git a/chrome/test/functional/chromeos_vpn.py b/chrome/test/functional/chromeos_vpn.py
deleted file mode 100755
index 2cecf72..0000000
--- a/chrome/test/functional/chromeos_vpn.py
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2011 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 os
-import subprocess
-
-import pyauto_functional
-import pyauto
-import chromeos_network
-
-
-class PrivateNetworkTest(chromeos_network.PyNetworkUITest):
-  """Tests for VPN.
-
-  Expected to be run with access to the lab setup as defined in
-  vpn_testbed_config.
-  """
-
-  def _PingTest(self, hostname, timeout=10):
-    """Attempt to ping a remote host.
-
-    Returns:
-      True if the ping succeeds.
-      False otherwise.
-    """
-    return subprocess.call(['ping', '-c', '1', '-W',
-                           str(timeout), hostname]) == 0
-
-  def testCanAddNetwork(self):
-    """Test to add a VPN network, connect and disconnect."""
-    # Load VPN config data from file.
-    vpn_info_file = os.path.join(pyauto.PyUITest.DataDir(),
-                                 'pyauto_private/chromeos/network',
-                                 'vpn_testbed_config')
-    self.assertTrue(os.path.exists(vpn_info_file))
-    vpn = self.EvalDataFrom(vpn_info_file)
-
-    # Connect to wifi.
-    self.NetworkScan()
-    self.WaitUntilWifiNetworkAvailable(vpn['wifi'])
-    wifi_vpn = self.GetServicePath(vpn['wifi'])
-    self.assertTrue(wifi_vpn)
-    self.assertTrue(self.ConnectToWifiNetwork(wifi_vpn) is None)
-    self.assertFalse(self._PingTest(vpn['ping']),
-                     msg='VPN ping succeeded when not connected.')
-
-    # Connect to the VPN.
-    self.AddPrivateNetwork(hostname=vpn['hostname'],
-                           service_name=vpn['service_name'],
-                           provider_type=vpn['provider_type'],
-                           username=vpn['username'],
-                           password=vpn['password'],
-                           key=vpn['key'])
-
-    # Get private network info.
-    result = self.GetPrivateNetworkInfo()
-    self.assertTrue('connected' in result, msg='Could not connect to VPN')
-    connected = result['connected']
-    self.assertTrue(self._PingTest(vpn['ping']), msg='VPN ping failed.')
-    self.DisconnectFromPrivateNetwork()
-    self.assertFalse(self._PingTest(vpn['ping']),
-                     msg='VPN ping succeeded when not connected.')
-    # Connect to the remembered private network.
-    self.ConnectToPrivateNetwork(connected)
-    self.assertTrue(self._PingTest(vpn['ping']), msg='VPN ping failed.')
-    self.DisconnectFromPrivateNetwork()
-    self.assertFalse(self._PingTest(vpn['ping']),
-                     msg='VPN ping succeeded when not connected.')
-
-
-if __name__ == '__main__':
-  pyauto_functional.Main()
diff --git a/chrome/test/functional/chromeos_wifi_compliance.py b/chrome/test/functional/chromeos_wifi_compliance.py
deleted file mode 100755
index 9e6d04b..0000000
--- a/chrome/test/functional/chromeos_wifi_compliance.py
+++ /dev/null
@@ -1,110 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2011 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 pyauto_functional
-import chromeos_network  # pyauto_functional must come before chromeos_network
-
-
-class ChromeosWifiCompliance(chromeos_network.PyNetworkUITest):
-  """Tests for ChromeOS wifi complaince.
-
-  These tests should be run within vacinity of the power strip where the wifi
-  routers are attached.
-  """
-
-  def _BasicConnectRouterCompliance(self, router_name):
-    """Generic basic test routine for connecting to a router.
-
-    Args:
-      router_name: The name of the router.
-    """
-    self.InitWifiPowerStrip()
-    router = self.GetRouterConfig(router_name)
-    self.RouterPower(router_name, True)
-
-    # If the wifi network is expected to be invisible, the following
-    # line should timeout which is expected.
-    wifi_visible = self.WaitUntilWifiNetworkAvailable(router['ssid'],
-                                                 is_hidden=router.get('hidden'))
-
-    # Note, we expect wifi_visible and 'hidden' status to be opposites.
-    # The test fails if the network visibility is not as expected.
-    if wifi_visible == router.get('hidden', False):
-      self.fail('We expected wifi network "%s" to be %s, but it was not.' %
-                (router['ssid'],
-                 {True: 'hidden', False: 'visible'}[router.get('hidden',
-                 False)]))
-
-    # Verify connect did not have any errors.
-    error = self.ConnectToWifiRouter(router_name)
-    self.assertFalse(error, 'Failed to connect to wifi network %s. '
-                            'Reason: %s.' % (router['ssid'], error))
-
-    # Verify the network we connected to.
-    ssid = self.GetConnectedWifi()
-    self.assertEqual(ssid, router['ssid'],
-                     'Did not successfully connect to wifi network %s.' % ssid)
-
-    self.DisconnectFromWifiNetwork()
-
-  def testConnectBelkinG(self):
-    """Test connecting to the Belkin G router."""
-    self._BasicConnectRouterCompliance('Belkin_G')
-
-  def testConnectBelkinNPlus(self):
-    """Test connecting to the Belkin N+ router."""
-    self._BasicConnectRouterCompliance('Belkin_N+')
-
-  def testConnectDLinkN150(self):
-    """Test connecting to the D-Link N150 router."""
-    self._BasicConnectRouterCompliance('D-Link_N150')
-
-  def testConnectLinksysE3000(self):
-    """Test connecting to the Linksys E3000 router.
-
-    The LinksysE3000 supports broadcasting of up to 2 SSID's.
-    This test will try connecting to each of them one at a time.
-    """
-    self._BasicConnectRouterCompliance('LinksysE3000')
-    self._BasicConnectRouterCompliance('LinksysE3000_2')
-
-  def testConnectLinksysWRT54G2(self):
-    """Test connecting to the Linksys WRT54G2 router."""
-    self._BasicConnectRouterCompliance('Linksys_WRT54G2')
-
-  def testConnectLinksysWRT54GL(self):
-    """Test connecting to the LinksysWRT54GL router."""
-    self._BasicConnectRouterCompliance('Linksys_WRT54GL')
-
-  def testConnectNetgearN300(self):
-    """Test connecting to the Netgear N300 router."""
-    self._BasicConnectRouterCompliance('Netgear_N300')
-
-  def testConnectNetgearWGR614(self):
-    """Test connecting to the Netgear WGR 614 router."""
-    self._BasicConnectRouterCompliance('Netgear_WGR614')
-
-  def testConnectNfiniti(self):
-    """Test connecting to the Nfiniti router."""
-    self._BasicConnectRouterCompliance('Nfiniti')
-
-  def testConnectSMCWBR145(self):
-    """Test connecting to the SMC WBR 145 router."""
-    self._BasicConnectRouterCompliance('SMC_WBR145')
-
-  def testConnectTrendnet_639gr(self):
-    """Test connecting to the Trendnet 639gr router.
-
-    The LinksysE3000 supports broadcasting of up to 4 SSID's.
-    This test will try connecting to each of them one at a time.
-    """
-    self._BasicConnectRouterCompliance('Trendnet_639gr')
-    self._BasicConnectRouterCompliance('Trendnet_639gr_2')
-    self._BasicConnectRouterCompliance('Trendnet_639gr_3')
-    self._BasicConnectRouterCompliance('Trendnet_639gr_4')
-
-
-if __name__ == '__main__':
-  pyauto_functional.Main()
diff --git a/chrome/test/functional/chromeos_wifi_functional.py b/chrome/test/functional/chromeos_wifi_functional.py
deleted file mode 100755
index 0cd941f..0000000
--- a/chrome/test/functional/chromeos_wifi_functional.py
+++ /dev/null
@@ -1,191 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import os
-import test_utils
-
-import pyauto_functional
-import chromeos_network  # pyauto_functional must come before chromeos_network
-import pyauto_utils
-
-
-class ChromeosWifiFunctional(chromeos_network.PyNetworkUITest):
-  """Tests for ChromeOS wifi functionality.
-
-  These tests should be run within vacinity of the power strip where the wifi
-  routers are attached.
-  """
-
-  def setUp(self):
-    chromeos_network.PyNetworkUITest.setUp(self)
-    if self.GetLoginInfo().get('is_logged_in'):
-      self.Logout()
-
-  def _SetupRouter(self, router_name):
-    """Turn on the router and wait for it to come on.
-
-    Args:
-      router_name: The name of the router as defined in wifi_testbed_config.
-
-    Returns:
-      A dictionary of the router and its attributes.  The format is same as
-      the definition in wifi_testbed_config
-    """
-    self.InitWifiPowerStrip()
-    router = self.GetRouterConfig(router_name)
-    self.RouterPower(router_name, True)
-
-    # When we connect to a wifi service, it should be added to the
-    # remembered_wifi list.
-    self.WaitUntilWifiNetworkAvailable(router['ssid'],
-                                       is_hidden=router.get('hidden'))
-    return router
-
-  def _VerifyIfConnectedToNetwork(self, network_ssid, status='Online state'):
-    """Verify if we are connected to the network.
-
-    The test calling this function will fail for one of these three reasons:
-    1. The server path for the SSID is not found.
-    2. If we are not connected to the network.
-    3. If we did not find the network in the wifi_networks list.
-
-    Args:
-      newtork_ssid: The network to which we are supposed to be connected to.
-      status: The status that we expect the network to have, by default it
-              would be 'Online state'.
-    """
-    service_path = self.GetServicePath(network_ssid)
-    self.assertTrue(service_path is not None,
-                    msg='Could not find a service path for the given ssid %s.' %
-                    network_ssid)
-    wifi_network = self.NetworkScan()['wifi_networks']
-    for path in wifi_network:
-      if path == service_path:
-        self.assertTrue(
-            wifi_network[path]['status'] == status,
-            msg='Unexpected network status %s, Network %s should have '
-                'status %s.' % (wifi_network[path]['status'],
-                network_ssid, status))
-        break;
-    else:
-      self.fail(msg='Did not find the network %s in the '
-                    'wifi_networks list.' % network_ssid)
-
-  def testConnectShareEncryptedNetwork(self):
-    """A shared encrypted network can connect and is remembered.
-
-    Note: This test does not verify that the network is added to the public
-    profile
-    """
-    router_name = 'D-Link_N150'
-    test_utils.LoginToDevice(self)
-    router = self._SetupRouter(router_name)
-    error = self.ConnectToWifiRouter(router_name, shared=True)
-    self.assertFalse(error, 'Failed to connect to wifi network %s. '
-                            'Reason: %s.' % (router['ssid'], error))
-    service_path = self.GetServicePath(router['ssid'])
-    self.assertTrue(service_path in self.GetNetworkInfo()['remembered_wifi'],
-                    'Connected wifi was not added to the remembered list.')
-    self.ForgetWifiNetwork(service_path)
-    self.assertFalse(service_path in self.GetNetworkInfo()['remembered_wifi'],
-                     'Connected wifi was not removed from the remembered list.')
-
-  def testConnectNoShareEncryptedNetwork(self):
-    """A non-shared encrypted network can connect and is remembered.
-
-    Note: This test does not verify that the network is added to the private
-    profile
-    """
-    router_name = 'D-Link_N150'
-    test_utils.LoginToDevice(self)
-    router = self._SetupRouter(router_name)
-    error = self.ConnectToWifiRouter(router_name, shared=False)
-    self.assertFalse(error, 'Failed to connect to wifi network %s. '
-                            'Reason: %s.' % (router['ssid'], error))
-    service_path = self.GetServicePath(router['ssid'])
-    self.assertTrue(service_path in self.GetNetworkInfo()['remembered_wifi'],
-                    'Connected wifi was not added to the remembered list.')
-    self.ForgetWifiNetwork(service_path)
-    self.assertFalse(service_path in self.GetNetworkInfo()['remembered_wifi'],
-                     'Connected wifi was not removed from the remembered list.')
-
-  def testConnectToSharedOpenNetwork(self):
-    """Can connect to a shared open network.
-
-    Verify that the connected network is in the remembered network list
-    for all the users.
-    """
-    router_name = 'Trendnet_639gr_4'
-    test_utils.LoginToDevice(self)
-    router = self._SetupRouter(router_name)
-    error = self.ConnectToWifiRouter(router_name)
-    self.assertFalse(error, msg='Failed to connect to wifi network %s. '
-                            'Reason: %s.' % (router['ssid'], error))
-    service_path = self.GetServicePath(router['ssid'])
-    self.assertTrue(service_path in self.GetNetworkInfo()['remembered_wifi'],
-                    msg='Open wifi is not remembered for the current user.')
-    self.Logout()
-    test_utils.LoginToDevice(self, test_account='test_google_account_2')
-    self.assertTrue(service_path in self.NetworkScan()['remembered_wifi'],
-                    msg='Open network is not shared with other users.')
-
-  def testConnectToSharedHiddenNetwork(self):
-    """Can connect to shared hidden network and verify that it's shared."""
-    router_name = 'Netgear_WGR614'
-    test_utils.LoginToDevice(self)
-    router = self._SetupRouter(router_name)
-    error = self.ConnectToWifiRouter(router_name)
-    self.assertFalse(error, msg='Failed to connect to hidden network %s. '
-                            'Reason: %s.' % (router['ssid'], error))
-    service_path = self.GetServicePath(router['ssid'])
-    self.assertTrue(service_path in self.NetworkScan()['remembered_wifi'],
-                    msg='Hidden network is not added to the remembered list.')
-    self.Logout()
-    test_utils.LoginToDevice(self, test_account='test_google_account_2')
-    self.assertTrue(service_path in self.NetworkScan()['remembered_wifi'],
-                    msg='Shared hidden network is not in other user\'s '
-                    'remembered list.')
-
-  def testConnectToNonSharedHiddenNetwork(self):
-    """Can connect to a non-shared hidden network.
-
-    Verify that it is not shared with other users.
-    """
-    router_name = 'Linksys_WRT54GL'
-    test_utils.LoginToDevice(self)
-    router = self._SetupRouter(router_name)
-    error = self.ConnectToWifiRouter(router_name, shared=False)
-    self.assertFalse(error, msg='Failed to connect to hidden network %s. '
-                            'Reason: %s.' % (router['ssid'], error))
-    service_path = self.GetServicePath(router['ssid'])
-    self.assertTrue(service_path in self.NetworkScan()['remembered_wifi'],
-                    msg='Hidden network is not added to the remembered list.')
-    self.Logout()
-    test_utils.LoginToDevice(self, test_account='test_google_account_2')
-    self.assertFalse(service_path in self.NetworkScan()['remembered_wifi'],
-                     msg='Non-shared hidden network %s is shared.'
-                     % router['ssid'])
-
-  def testConnectToEncryptedNetworkInLoginScreen(self):
-    """Can connect to encrypted network in login screen.
-
-    Verify that this network is in the remembered list after login.
-    """
-    router_name = 'Belkin_G'
-    if self.GetLoginInfo()['is_logged_in']:
-      self.Logout()
-    router = self._SetupRouter(router_name)
-    error = self.ConnectToWifiRouter(router_name)
-    self.assertFalse(error, 'Failed to connect to wifi network %s. '
-                            'Reason: %s.' % (router['ssid'], error))
-    service_path = self.GetServicePath(router['ssid'])
-    self._VerifyIfConnectedToNetwork(router['ssid'], 'Connected')
-    test_utils.LoginToDevice(self)
-    self.assertTrue(service_path in self.NetworkScan()['remembered_wifi'],
-                    msg='Network is not added to the remembered list.')
-
-
-if __name__ == '__main__':
-  pyauto_functional.Main()
diff --git a/chrome/test/functional/webpagereplay.py b/chrome/test/functional/webpagereplay.py
index 6d2dc5c..0cfba58 100755
--- a/chrome/test/functional/webpagereplay.py
+++ b/chrome/test/functional/webpagereplay.py
@@ -35,6 +35,7 @@
       '--ignore-certificate-errors',
       ]
 
+
 # Signal masks on Linux are inherited from parent processes.  If anything
 # invoking us accidentally masks SIGINT (e.g. by putting a process in the
 # background from a shell script), sending a SIGINT to the child will fail
@@ -43,10 +44,12 @@
 def ResetInterruptHandler():
   signal.signal(signal.SIGINT, signal.SIG_DFL)
 
+
 class ReplayError(Exception):
   """Catch-all exception for the module."""
   pass
 
+
 class ReplayNotFoundError(ReplayError):
   def __init__(self, label, path):
     self.args = (label, path)
@@ -55,6 +58,7 @@
     label, path = self.args
     return 'Path does not exist for %s: %s' % (label, path)
 
+
 class ReplayNotStartedError(ReplayError):
   pass
 
@@ -78,6 +82,7 @@
     WPR_RECORD: if set, puts Web Page Replay in record mode instead of replay.
     WPR_REPLAY_DIR: path to alternate Web Page Replay source.
   """
+
   def __init__(self, archive_path, replay_host, http_port, https_port,
                replay_options=None, replay_dir=None,
                log_path=None):
@@ -88,7 +93,7 @@
       replay_options: an iterable of options strings to forward to replay.py.
       replay_dir: directory that has replay.py and related modules.
       log_path: a path to a log file.
-   """
+    """
     self.archive_path = os.environ.get('WPR_ARCHIVE_PATH', archive_path)
     self.replay_options = list(replay_options or ())
     self.replay_dir = os.environ.get('WPR_REPLAY_DIR', replay_dir or REPLAY_DIR)
@@ -116,6 +121,7 @@
   def _AddDefaultReplayOptions(self):
     """Set WPR command-line options. Can be overridden if needed."""
     self.replay_options += [
+        '--host', str(self._replay_host),
         '--port', str(self._http_port),
         '--ssl_port', str(self._https_port),
         '--use_closest_match',
@@ -140,9 +146,9 @@
         # The process has exited.
         break
       try:
-        up_url = '%s://localhost:%s/web-page-replay-generate-200'
-        http_up_url = up_url % ('http', self._http_port)
-        https_up_url = up_url % ('https', self._https_port)
+        up_url = '%s://%s:%s/web-page-replay-generate-200'
+        http_up_url = up_url % ('http', self._replay_host, self._http_port)
+        https_up_url = up_url % ('https', self._replay_host, self._https_port)
         if (200 == urllib.urlopen(http_up_url, None, {}).getcode() and
             200 == urllib.urlopen(https_up_url, None, {}).getcode()):
           return True
@@ -154,14 +160,14 @@
     """Start Web Page Replay and verify that it started.
 
     Raises:
-      ReplayNotStartedError if Replay start-up fails.
+      ReplayNotStartedError: if Replay start-up fails.
     """
     cmd_line = [sys.executable, self.replay_py]
     cmd_line.extend(self.replay_options)
     cmd_line.append(self.archive_path)
     self.log_fh = self._OpenLogFile()
     logging.debug('Starting Web-Page-Replay: %s', cmd_line)
-    kwargs = { 'stdout': self.log_fh, 'stderr': subprocess.STDOUT }
+    kwargs = {'stdout': self.log_fh, 'stderr': subprocess.STDOUT}
     if sys.platform.startswith('linux') or sys.platform == 'darwin':
       kwargs['preexec_fn'] = ResetInterruptHandler
     self.replay_process = subprocess.Popen(cmd_line, **kwargs)
@@ -173,14 +179,32 @@
   def StopServer(self):
     """Stop Web Page Replay."""
     if self.replay_process:
-      logging.debug('Stopping Web-Page-Replay')
-      # Use a SIGINT so that it can do graceful cleanup. On Windows, we are left
-      # with no other option than terminate().
+      logging.debug('Trying to stop Web-Page-Replay gracefully')
       try:
-        self.replay_process.send_signal(signal.SIGINT)
-      except:
-        self.replay_process.terminate()
-      self.replay_process.wait()
+        url = 'http://localhost:%s/web-page-replay-command-exit'
+        urllib.urlopen(url % self._http_port, None, {})
+      except IOError:
+        # IOError is possible because the server might exit without response.
+        pass
+
+      start_time = time.time()
+      while time.time() - start_time < 10:  # Timeout after 10 seconds.
+        if self.replay_process.poll() is not None:
+          break
+        time.sleep(1)
+      else:
+        try:
+          # Use a SIGINT so that it can do graceful cleanup.
+          self.replay_process.send_signal(signal.SIGINT)
+        except:  # pylint: disable=W0702
+          # On Windows, we are left with no other option than terminate().
+          if 'no-dns_forwarding' not in self.replay_options:
+            logging.warning('DNS configuration might not be restored!')
+          try:
+            self.replay_process.terminate()
+          except:  # pylint: disable=W0702
+            pass
+        self.replay_process.wait()
     if self.log_fh:
       self.log_fh.close()
 
diff --git a/chrome/test/functional/wifi_downloads.py b/chrome/test/functional/wifi_downloads.py
deleted file mode 100755
index f998934..0000000
--- a/chrome/test/functional/wifi_downloads.py
+++ /dev/null
@@ -1,201 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import hashlib
-import logging
-import os
-import time
-import urllib2
-
-import pyauto_functional  # Must be imported before pyauto
-import pyauto
-import pyauto_utils
-import chromeos_network
-
-
-MAX_WAIT_TIME_IN_MSEC = 15 * 60 * 1000
-
-
-class WifiDownloadsTest(chromeos_network.PyNetworkUITest):
-  """TestCase for ChromeOS Wifi Downloads
-
-     This test makes a few assumptions.  It needs to have access to the power
-     strip used in pyautolib/chromeos/wifi_downloads.py.  It also assumes access
-     to the server 172.22.12.98:8080.  If the server is passed a filname in the
-     format <integer>.lf, it will generate a file of size <integer> in KB.  In
-     addition the name of the file returned is the md5 checksum of the file.
-
-     In addition the download times are written to a file in
-     /tmp/wifi_download_time.csv.  All times are appended to the file if it
-     already exists.
-  """
-
-  def setUp(self):
-    chromeos_network.PyNetworkUITest.setUp(self)
-    self.InitWifiPowerStrip()
-    # The power strip is a shared resource and if we every crash in the middle
-    # of a test we will be in an unknown state.  This returns us to 'all off'.
-    self.TurnOffAllRouters()
-    # Downloading files of a large size, can take a while, bump the timeout
-    self.changer = pyauto.PyUITest.ActionTimeoutChanger(self, 4 * 1000 * 60)
-    self.log_file_path = '/tmp/wifi_download_time.csv'
-
-  def _WriteTimeToFile(self, output_file, router_name, file_size, dl_time):
-    """Write or append a time into a csv file.
-
-    This method will create or append the amount of time a download took for a
-    given filesize and router to a file at the given path.
-
-    The format of the output file is as follows:
-    <router name A>,<file size A>,time,time,time,time,time,time,time,time
-    <router name A>,<file size B>,time,time,time,time,time,time,time,time
-    <router name B>,<file size C>,time,time,time,time,time,time,time,time
-
-    Args:
-      output_file: the complete path of the file to write to
-      file_size: the size of the file, this is the row header
-      dl_time: the amount of time in seconds
-    """
-    file_data = []
-    if os.path.exists(output_file):
-      file_handle = open(output_file)
-      lines = file_handle.readlines()
-      file_handle.close()
-      # Convert the file to a full data structure.
-      for line in lines:
-        values = line.strip().split(',')
-        file_data.append(values)
-      for values in file_data:
-        found_existing_time = False
-        if values[0] == router_name and values[1] == file_size:
-          values.append('%2.2f' % dl_time)
-          found_existing_time = True
-          break
-      if not found_existing_time:
-        new_line = [router_name, file_size, ('%2.2f' % dl_time)]
-        file_data.append(new_line)
-    else:
-      file_data = [[router_name, file_size, ('%2.2f' % dl_time)]]
-    # Write the data back out
-    file_handle = open(output_file, 'w')
-    for line in file_data:
-      if len(line) > 2:
-        file_handle.write(','.join(line))
-        file_handle.write('\n')
-    file_handle.close()
-
-  def _Md5Checksum(self, file_path):
-    """Returns the md5 checksum of a file at a given path.
-
-    Args:
-      file_path: The complete path of the file to generate the md5 checksum for.
-    """
-    file_handle = open(file_path, 'rb')
-    m = hashlib.md5()
-    while True:
-      data = file_handle.read(8192)
-      if not data:
-        break
-      m.update(data)
-    file_handle.close()
-    return m.hexdigest()
-
-  def _ConnectToRouterAndVerify(self, router_name):
-    """Generic routine for connecting to a router.
-
-    Args:
-      router_name: The name of the router to connect to.
-    """
-    router = self.GetRouterConfig(router_name)
-    self.RouterPower(router_name, True)
-
-    self.assertTrue(self.WaitUntilWifiNetworkAvailable(router['ssid']),
-                    'Wifi network %s never showed up.' % router['ssid'])
-
-    # Verify connect did not have any errors.
-    error = self.ConnectToWifiRouter(router_name)
-    self.assertFalse(error, 'Failed to connect to wifi network %s. '
-                            'Reason: %s.' % (router['ssid'], error))
-
-    # Verify the network we connected to.
-    ssid = self.GetConnectedWifi()
-    self.assertEqual(ssid, router['ssid'],
-                     'Did not successfully connect to wifi network %s.' % ssid)
-
-  def _DownloadAndVerifyFile(self, download_url):
-    """Downloads a file at a given URL and validates it
-
-    This method downloads a file from a server whose filename matches the md5
-    checksum.  Then we manually generate the md5 and check it against the
-    filename.
-
-    Args:
-      download_url: URL of the file to download.
-
-    Returns:
-      The download time in seconds.
-    """
-    start = time.time()
-    # Make a copy of the download directory now to work around segfault
-    downloads_dir = self.GetDownloadDirectory().value()
-    try:
-      self.DownloadAndWaitForStart(download_url)
-    except AssertionError:
-      # We need to redo this since the external server may not respond the
-      # first time.
-      logging.info('Could not start download. Retrying ...')
-      self.DownloadAndWaitForStart(download_url)
-    # Maximum wait time is set as 15 mins as an 100MB file may take somewhere
-    # between 8-12 mins to download.
-    self.WaitForAllDownloadsToComplete(timeout=MAX_WAIT_TIME_IN_MSEC)
-    end = time.time()
-    logging.info('Download took %2.2f seconds to complete' % (end - start))
-    downloaded_files = os.listdir(downloads_dir)
-    self.assertEquals(len(downloaded_files), 1,
-                      msg='Expected only one file in the Downloads folder. '
-                      'but got this instead: %s' % ', '.join(downloaded_files))
-    filename = os.path.splitext(downloaded_files[0])[0]
-    file_path = os.path.join(self.GetDownloadDirectory().value(),
-                             downloaded_files[0])
-    md5_sum = self._Md5Checksum(file_path)
-    md5_url = download_url[:-4] + '.md5'  # replacing .slf with .md5
-    md5_file = urllib2.urlopen(md5_url).readlines()[0]
-    self.assertTrue(md5_file.rstrip().endswith(md5_sum.encode()), 
-                    msg='Unexpected checksum. The download is incomplete.')
-    return end - start
-
-  def testDownload1MBFile(self):
-    """Test downloading a 1MB file from a wireless router."""
-    download_url = 'http://172.22.12.98:80/downloads/1M.slf'
-    router_name = 'Nfiniti'
-    self._ConnectToRouterAndVerify(router_name)
-    download_time = self._DownloadAndVerifyFile(download_url)
-    self._WriteTimeToFile(self.log_file_path, router_name, '1MB',
-                          download_time)
-    self.DisconnectFromWifiNetwork()
-
-  def testDownload10MBFile(self):
-    """Test downloading a 10MB file from a wireless router."""
-    download_url = 'http://172.22.12.98:80/downloads/10M.slf'
-    router_name = 'Linksys_WRT54G2'
-    self._ConnectToRouterAndVerify(router_name)
-    download_time = self._DownloadAndVerifyFile(download_url)
-    self._WriteTimeToFile(self.log_file_path, router_name, '10MB',
-                          download_time)
-    self.DisconnectFromWifiNetwork()
-
-  def testDownload100MBFile(self):
-    """Test downloading a 100MB file from a wireless router."""
-    download_url = 'http://172.22.12.98:80/downloads/100M.slf'
-    router_name = 'Trendnet_639gr_4'
-    self._ConnectToRouterAndVerify(router_name)
-    download_time = self._DownloadAndVerifyFile(download_url)
-    self._WriteTimeToFile(self.log_file_path, router_name, '100MB',
-                          download_time)
-    self.DisconnectFromWifiNetwork()
-
-
-if __name__ == '__main__':
-  pyauto_functional.Main()
diff --git a/chrome/test/functional/wifi_notification.py b/chrome/test/functional/wifi_notification.py
deleted file mode 100755
index 316d4a6..0000000
--- a/chrome/test/functional/wifi_notification.py
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2011 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 re
-
-import pyauto_functional
-import chromeos_network  # pyauto_functional must come before chromeos_network
-
-
-class WifiNotification(chromeos_network.PyNetworkUITest):
-  """Test for ChromeOS wifi Network Disconnected Notification.
-
-  These tests will be testing Network Disconnected Notification on
-  various network encryptions (WEP,RSN, WPA) and various password lengths.
-  """
-
-  password1 = 'wrongpasswor'
-  password5 = 'tente'
-  password10 = 'tententent'
-  password13 = 'thirteenthirt'
-  password26 = 'twentysixtwentysixtwentysi'
-  password64 = \
-    'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl'
-
-  def _WifiNotification(self, router_name, password):
-    """Basic test for wifi notification.
-
-    Args:
-      router_name: The name of the router.
-      password: invalid password.
-    """
-    self.InitWifiPowerStrip()
-    router_config = self.GetRouterConfig(router_name)
-    self.RouterPower(router_name, True)
-
-    self.assertTrue(self.WaitUntilWifiNetworkAvailable(router_config['ssid']),
-                    'Wifi network %s never showed up.' % router_config['ssid'])
-
-    service_path = self.GetServicePath(router_config['ssid'])
-    self.ConnectToWifiNetwork(service_path, password=password)
-    self.WaitForNotificationCount(1)
-
-    notification_result = self.GetActiveNotifications()[0]['content_url']
-    result_error = re.search('Error|Failed', notification_result)
-    self.assertTrue(result_error, 'Expected to find Error/Failed in '
-                                  'notification, not found as expected.')
-    result_ssid = re.search(router_config['ssid'], notification_result)
-    self.assertTrue(result_ssid,
-                    'SSID is not found. Notification text is: "%s"'
-                    % notification_result)
-
-  def testWifiNotificationWEP_Linksys_WRT54G2_wrongpassword(self):
-    """wifi disconnect notification-Linksys_WRT54G2.(WEP)-invalid password"""
-    self._WifiNotification('Linksys_WRT54G2', WifiNotification.password1)
-
-  def testWifiNotificationWEP_Linksys_WRT54G2_five_char(self):
-    """wifi disconnect notification for Linksys_WRT54G2.(WEP)-5 password """
-    self._WifiNotification('Linksys_WRT54G2', WifiNotification.password5)
-
-  def testWifiNotificationWEP_Linksys_WRT54G2_ten_char(self):
-    """wifi disconnect notification for Linksys_WRT54G2.(WEP)-10 password"""
-    self._WifiNotification('Linksys_WRT54G2', WifiNotification.password10)
-
-  def testWifiNotificationWEP_Linksys_WRT54G2_thirteen_char(self):
-    """wifi disconnect notification for Linksys_WRT54G2.(WEP)-13 password"""
-    self._WifiNotification('Linksys_WRT54G2', WifiNotification.password13)
-
-  def testWifiNotificationWEP_Linksys_WRT54G2_twentysix_char(self):
-    """wifi disconnect notification for Linksys_WRT54G2.(WEP)-26 password"""
-    self._WifiNotification('Linksys_WRT54G2', WifiNotification.password26)
-
-  def testWifiNotificationRSN_Belkin_G_wrongpassword(self):
-    """wifi disconnect notification for Belkin_G (rsn)-wrong password"""
-    self._WifiNotification('Belkin_G', WifiNotification.password1)
-
-  def testWifiNotificationWPA_Trendnet_639gr_wrongpassword(self):
-    """wifi disconnect notification for Trendnet_639gr (WPA)-wrong password"""
-    self._WifiNotification('Trendnet_639gr', WifiNotification.password1)
-
-  def testWifiNotificationWPA_Trendnet_639gr_five_char(self):
-    """wifi disconnect notification for Trendnet_639gr (WPA)-5 password"""
-    self._WifiNotification('Trendnet_639gr', WifiNotification.password5)
-
-  def testWifiNotificationWPA_Trendnet_639gr_sixtyfour_char(self):
-    """wifi disconnect notification for Trendnet_639gr (WPA)-64 password"""
-    self._WifiNotification('Trendnet_639gr', WifiNotification.password64)
-
-
-if __name__ == '__main__':
-  pyauto_functional.Main()
diff --git a/chrome/test/mini_installer/chrome_helper.py b/chrome/test/mini_installer/chrome_helper.py
new file mode 100644
index 0000000..00e892a
--- /dev/null
+++ b/chrome/test/mini_installer/chrome_helper.py
@@ -0,0 +1,82 @@
+# 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.
+
+"""Common helper module for working with Chrome's processes and windows."""
+
+import psutil
+import re
+import win32gui
+import win32process
+
+import path_resolver
+
+
+def GetProcessIDs(process_path):
+  """Returns a list of IDs of processes whose path is |process_path|.
+
+  Args:
+    process_path: The path to the process.
+
+  Returns:
+    A list of process IDs.
+  """
+  process_ids = []
+  for process in psutil.process_iter():
+    try:
+      found_process_path = process.exe
+      if found_process_path == process_path:
+        process_ids.append(process.pid)
+    except psutil.AccessDenied:
+      # It's normal that some processes are not accessible.
+      pass
+  return process_ids
+
+
+def GetWindowHandles(process_ids):
+  """Returns a list of handles of windows owned by processes in |process_ids|.
+
+  Args:
+    process_ids: A list of process IDs.
+
+  Returns:
+    A list of handles of windows owned by processes in |process_ids|.
+  """
+  hwnds = []
+  def EnumerateWindowCallback(hwnd, _):
+    _, found_process_id = win32process.GetWindowThreadProcessId(hwnd)
+    if found_process_id in process_ids and win32gui.IsWindowVisible(hwnd):
+      hwnds.append(hwnd)
+  # Enumerate all the top-level windows and call the callback with the hwnd as
+  # the first parameter.
+  win32gui.EnumWindows(EnumerateWindowCallback, None)
+  return hwnds
+
+
+def WindowExists(process_ids, class_pattern):
+  """Returns whether there exists a window with the specified criteria.
+
+  This method returns whether there exists a window that is owned by a process
+  in |process_ids| and has a class name that matches |class_pattern|.
+
+  Args:
+    process_ids: A list of process IDs.
+    class_pattern: The regular expression pattern of the window class name.
+
+  Returns:
+    A boolean indicating whether such window exists.
+  """
+  for hwnd in GetWindowHandles(process_ids):
+    if re.match(class_pattern, win32gui.GetClassName(hwnd)):
+      return True
+  return False
+
+
+def GetChromePath(system_level):
+  """Returns the path to Chrome, at the |system_level| or user level."""
+  chrome_path = None
+  if system_level:
+    chrome_path = '$PROGRAM_FILES\\$CHROME_DIR\\Application\\chrome.exe'
+  else:
+    chrome_path = '$LOCAL_APPDATA\\$CHROME_DIR\\Application\\chrome.exe'
+  return path_resolver.ResolvePath(chrome_path)
diff --git a/chrome/test/mini_installer/config/chrome_installed.prop b/chrome/test/mini_installer/config/chrome_installed.prop
index 1b95385..6fe8aca 100644
--- a/chrome/test/mini_installer/config/chrome_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_installed.prop
@@ -1,15 +1,19 @@
 {
   "Files": {
-    "$LOCAL_APPDATA\\Google\\Chrome\\Application\\chrome.exe": {"exists": true},
-    "$LOCAL_APPDATA\\Google\\Chrome\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome.dll":
+    "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\chrome.exe": {"exists": true},
+    "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\chrome.dll":
         {"exists": true},
-    "$LOCAL_APPDATA\\Google\\Chrome\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\chrome.7z":
+    "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\chrome.7z":
         {"exists": true},
-    "$LOCAL_APPDATA\\Google\\Chrome\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe":
+    "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\$MINI_INSTALLER_FILE_VERSION\\Installer\\setup.exe":
         {"exists": true}
   },
   "RegistryEntries": {
-    "HKEY_CURRENT_USER\\Software\\Google\\Update\\Clients\\{8A69D345-D564-463c-AFF1-A69D9E530F96}":
-        {"exists": true}
+    "HKEY_CURRENT_USER\\$CHROME_UPDATE_REGISTRY_SUBKEY": {
+      "exists": true,
+      "values": {
+        "pv": {"type": "SZ", "data": "$MINI_INSTALLER_FILE_VERSION"}
+      }
+    }
   }
 }
diff --git a/chrome/test/mini_installer/config/chrome_inuse.prop b/chrome/test/mini_installer/config/chrome_inuse.prop
new file mode 100644
index 0000000..ef2ee63
--- /dev/null
+++ b/chrome/test/mini_installer/config/chrome_inuse.prop
@@ -0,0 +1,5 @@
+{
+  "Processes": {
+    "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\chrome.exe": {"running": true}
+  }
+}
diff --git a/chrome/test/mini_installer/config/chrome_not_installed.prop b/chrome/test/mini_installer/config/chrome_not_installed.prop
index dfa4ffd..26987a7 100644
--- a/chrome/test/mini_installer/config/chrome_not_installed.prop
+++ b/chrome/test/mini_installer/config/chrome_not_installed.prop
@@ -1,9 +1,8 @@
 {
   "Files": {
-    "$LOCAL_APPDATA\\Google\\Chrome\\Application": {"exists": false}
+    "$LOCAL_APPDATA\\$CHROME_DIR\\Application": {"exists": false}
   },
   "RegistryEntries": {
-    "HKEY_CURRENT_USER\\Software\\Google\\Update\\Clients\\{8A69D345-D564-463c-AFF1-A69D9E530F96}":
-        {"exists": false}
+    "HKEY_CURRENT_USER\\$CHROME_UPDATE_REGISTRY_SUBKEY": {"exists": false}
   }
 }
diff --git a/chrome/test/mini_installer/config/chrome_not_inuse.prop b/chrome/test/mini_installer/config/chrome_not_inuse.prop
new file mode 100644
index 0000000..1439478
--- /dev/null
+++ b/chrome/test/mini_installer/config/chrome_not_inuse.prop
@@ -0,0 +1,5 @@
+{
+  "Processes": {
+    "$LOCAL_APPDATA\\$CHROME_DIR\\Application\\chrome.exe": {"running": false}
+  }
+}
diff --git a/chrome/test/mini_installer/config/config.config b/chrome/test/mini_installer/config/config.config
index 94725f6..88ac92a 100644
--- a/chrome/test/mini_installer/config/config.config
+++ b/chrome/test/mini_installer/config/config.config
@@ -1,14 +1,22 @@
 {
   "states": [
-    ["clean", ["chrome_not_installed.prop"]],
-    ["chrome_installed", ["chrome_installed.prop"]]
+    ["clean", ["chrome_not_installed.prop", "chrome_not_inuse.prop"]],
+    ["chrome_installed_not_inuse", ["chrome_installed.prop",
+        "chrome_not_inuse.prop"]],
+    ["chrome_installed_inuse", ["chrome_installed.prop", "chrome_inuse.prop"]]
   ],
   "actions": [
-    ["install chrome",
-        "\"$MINI_INSTALLER\" --chrome --multi-install --do-not-launch-chrome"],
-    ["uninstall chrome", "python uninstall_chrome.py"]
+    ["install_chrome_at_user_level",
+        "mini_installer.exe --chrome --multi-install --do-not-launch-chrome"],
+    ["launch_chrome_at_user_level", "python launch_chrome.py"],
+    ["quit_chrome_at_user_level", "python quit_chrome.py"],
+    ["uninstall_chrome", "python uninstall_chrome.py"]
   ],
   "tests": [
-    ["clean", "install chrome", "chrome_installed", "uninstall chrome", "clean"]
+    ["clean",
+        "install_chrome_at_user_level", "chrome_installed_not_inuse",
+        "launch_chrome_at_user_level", "chrome_installed_inuse",
+        "quit_chrome_at_user_level", "chrome_installed_not_inuse",
+        "uninstall_chrome", "clean"]
   ]
 }
diff --git a/chrome/test/mini_installer/launch_chrome.py b/chrome/test/mini_installer/launch_chrome.py
new file mode 100644
index 0000000..7440d47
--- /dev/null
+++ b/chrome/test/mini_installer/launch_chrome.py
@@ -0,0 +1,50 @@
+# 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.
+
+"""Launches Chrome.
+
+This script launches Chrome and waits until its window shows up.
+"""
+
+import optparse
+import subprocess
+import sys
+import time
+
+import chrome_helper
+
+
+def WaitForWindow(process_id, class_pattern):
+  """Waits until a window specified by |process_id| and class name shows up.
+
+  Args:
+    process_id: The ID of the process that owns the window.
+    class_pattern: The regular expression pattern of the window class name.
+
+  Returns:
+    A boolean value indicating whether the specified window shows up within
+    30 seconds.
+  """
+  start_time = time.time()
+  while time.time() - start_time < 30:
+    if chrome_helper.WindowExists([process_id], class_pattern):
+      return True
+    time.sleep(0)
+  return False
+
+
+def main():
+  parser = optparse.OptionParser(description='Launch Chrome.')
+  parser.add_option('--system-level', action='store_true', dest='system_level',
+                    default=False, help='Launch Chrome at system level.')
+  options, _ = parser.parse_args()
+  chrome_path = chrome_helper.GetChromePath(options.system_level)
+  process = subprocess.Popen(chrome_path)
+  if not WaitForWindow(process.pid, 'Chrome_WidgetWin_'):
+    raise Exception('Could not launch Chrome.')
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/chrome/test/mini_installer/path_resolver.py b/chrome/test/mini_installer/path_resolver.py
index 570a584..3cc178d 100644
--- a/chrome/test/mini_installer/path_resolver.py
+++ b/chrome/test/mini_installer/path_resolver.py
@@ -9,8 +9,27 @@
 from win32com.shell import shell, shellcon
 
 
+def _GetProductName(file_path):
+  """Returns the product name of the given file.
+
+  Args:
+    file_path: The absolute or relative path to the file.
+
+  Returns:
+    A string representing the product name of the file, or None if the product
+    name was not found.
+  """
+  language_and_codepage_pairs = win32api.GetFileVersionInfo(
+      file_path, '\\VarFileInfo\\Translation')
+  if not language_and_codepage_pairs:
+    return None
+  product_name_entry = ('\\StringFileInfo\\%04x%04x\\ProductName' %
+                        language_and_codepage_pairs[0])
+  return win32api.GetFileVersionInfo(file_path, product_name_entry)
+
+
 def ResolvePath(path):
-  """Resolve variables in a file path, and return the resolved path.
+  """Resolves variables in a file path, and returns the resolved path.
 
   This method resolves only variables defined below. It does not resolve
   environment variables. Any dollar signs that are not part of variables must be
@@ -23,23 +42,48 @@
     A new path created by replacing
         * $PROGRAM_FILES with the path to the Program Files folder,
         * $LOCAL_APPDATA with the path to the Local Application Data folder,
-        * $MINI_INSTALLER with the path to the mini_installer, and
+        * $MINI_INSTALLER with the path to the mini_installer,
         * $MINI_INSTALLER_FILE_VERSION with the file version of the
-            mini_installer.
+            mini_installer,
+        * $CHROME_SHORT_NAME with 'Chrome' (or 'Chromium'),
+        * $CHROME_LONG_NAME with 'Google Chrome' (or 'Chromium'),
+        * $CHROME_DIR with the directory of Chrome (or Chromium) from the base
+            installation directory, and
+        * $CHROME_UPDATE_REGISTRY_SUBKEY with the registry key, excluding the
+            root key, of Chrome for Google Update.
   """
   program_files_path = shell.SHGetFolderPath(0, shellcon.CSIDL_PROGRAM_FILES,
                                              None, 0)
   local_appdata_path = shell.SHGetFolderPath(0, shellcon.CSIDL_LOCAL_APPDATA,
                                              None, 0)
-  #TODO(sukolsak): Copy the mini_installer.exe from the build output into here.
+  # TODO(sukolsak): Copy the mini_installer.exe from the build output into here.
   mini_installer_path = os.path.abspath('mini_installer.exe')
   mini_installer_file_version = win32com.client.Dispatch(
       'Scripting.FileSystemObject').GetFileVersion(mini_installer_path)
+  mini_installer_product_name = _GetProductName(mini_installer_path)
+  if mini_installer_product_name == 'Google Chrome':
+    chrome_short_name = 'Chrome'
+    chrome_long_name = 'Google Chrome'
+    chrome_dir = 'Google\\Chrome'
+    chrome_update_registry_subkey = ('Software\\Google\\Update\\Clients\\'
+                                     '{8A69D345-D564-463c-AFF1-A69D9E530F96}')
+  elif mini_installer_product_name == 'Chromium':
+    chrome_short_name = 'Chromium'
+    chrome_long_name = 'Chromium'
+    chrome_dir = 'Chromium'
+    chrome_update_registry_subkey = 'Software\\Chromium'
+  else:
+    raise KeyError("Unknown mini_installer product name '%s'" %
+                   mini_installer_product_name)
 
   variable_mapping = {
       'PROGRAM_FILES': program_files_path,
       'LOCAL_APPDATA': local_appdata_path,
       'MINI_INSTALLER': mini_installer_path,
       'MINI_INSTALLER_FILE_VERSION': mini_installer_file_version,
+      'CHROME_SHORT_NAME': chrome_short_name,
+      'CHROME_LONG_NAME': chrome_long_name,
+      'CHROME_DIR': chrome_dir,
+      'CHROME_UPDATE_REGISTRY_SUBKEY': chrome_update_registry_subkey,
   }
   return string.Template(path).substitute(variable_mapping)
diff --git a/chrome/test/mini_installer/process_verifier.py b/chrome/test/mini_installer/process_verifier.py
new file mode 100644
index 0000000..a236056
--- /dev/null
+++ b/chrome/test/mini_installer/process_verifier.py
@@ -0,0 +1,36 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import psutil
+
+import path_resolver
+
+
+def VerifyProcesses(processes):
+  """Verifies that the running processes match the expectation dictionaries.
+
+  This method will throw an AssertionError if process state doesn't match the
+  provided expectation.
+
+  Args:
+    processes: A dictionary whose keys are paths to processes and values are
+        expectation dictionaries. An expectation dictionary is a dictionary with
+        the following key and value:
+            'running' a boolean indicating whether the process should be
+                running.
+  """
+  # Create a list of paths of all running processes.
+  running_process_paths = []
+  for process in psutil.process_iter():
+    try:
+      running_process_paths.append(process.exe)
+    except psutil.AccessDenied:
+      pass
+
+  for process_path, expectation in processes.iteritems():
+    process_resolved_path = path_resolver.ResolvePath(process_path)
+    is_running = process_resolved_path in running_process_paths
+    assert expectation['running'] == is_running, \
+        ('Process %s is running' % process_path) if is_running else \
+        ('Process %s is not running' % process_path)
diff --git a/chrome/test/mini_installer/quit_chrome.py b/chrome/test/mini_installer/quit_chrome.py
new file mode 100644
index 0000000..1725104
--- /dev/null
+++ b/chrome/test/mini_installer/quit_chrome.py
@@ -0,0 +1,54 @@
+# 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.
+
+"""Quits Chrome.
+
+This script sends a WM_CLOSE message to each window of Chrome and waits until
+the process terminates.
+"""
+
+import optparse
+import sys
+import time
+import win32con
+import win32gui
+
+import chrome_helper
+
+
+def CloseWindows(process_path):
+  """Closes all windows owned by processes whose path is |process_path|.
+
+  Args:
+    process_path: The path to the process.
+
+  Returns:
+    A boolean indicating whether the processes successfully terminate within
+    30 seconds.
+  """
+  start_time = time.time()
+  while time.time() - start_time < 30:
+    process_ids = chrome_helper.GetProcessIDs(process_path)
+    if not process_ids:
+      return True
+
+    for hwnd in chrome_helper.GetWindowHandles(process_ids):
+      win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0)
+    time.sleep(0)
+  return False
+
+
+def main():
+  parser = optparse.OptionParser(description='Quit Chrome.')
+  parser.add_option('--system-level', action='store_true', dest='system_level',
+                    default=False, help='Quit Chrome at system level.')
+  options, _ = parser.parse_args()
+  chrome_path = chrome_helper.GetChromePath(options.system_level)
+  if not CloseWindows(chrome_path):
+    raise Exception('Could not quit Chrome.')
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/chrome/test/mini_installer/registry_verifier.py b/chrome/test/mini_installer/registry_verifier.py
index da7909e..55941dd 100644
--- a/chrome/test/mini_installer/registry_verifier.py
+++ b/chrome/test/mini_installer/registry_verifier.py
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import _winreg
+import path_resolver
 
 
 def VerifyRegistryEntries(entries):
@@ -16,41 +17,91 @@
     VerifyRegistryEntry(key, expectation)
 
 
-def RootKeyConstant(key):
+def RootKeyConstant(root_key):
   """Converts a root registry key string into a _winreg.HKEY_* constant."""
-  if key == 'HKEY_CLASSES_ROOT':
-    return _winreg.HKEY_CLASSES_ROOT
-  if key == 'HKEY_CURRENT_USER':
-    return _winreg.HKEY_CURRENT_USER
-  if key == 'HKEY_LOCAL_MACHINE':
-    return _winreg.HKEY_LOCAL_MACHINE
-  if key == 'HKEY_USERS':
-    return _winreg.HKEY_USERS
-  raise KeyError("Unknown root registry key '%s'" % key)
+  root_key_mapping = {
+      'HKEY_CLASSES_ROOT': _winreg.HKEY_CLASSES_ROOT,
+      'HKEY_CURRENT_USER': _winreg.HKEY_CURRENT_USER,
+      'HKEY_LOCAL_MACHINE': _winreg.HKEY_LOCAL_MACHINE,
+      'HKEY_USERS': _winreg.HKEY_USERS,
+  }
+  if root_key not in root_key_mapping:
+    raise KeyError("Unknown root registry key '%s'" % root_key)
+  return root_key_mapping[root_key]
+
+
+def ValueTypeConstant(value_type):
+  """Converts a registry value type string into a _winreg.REG_* constant."""
+  value_type_mapping = {
+      'BINARY': _winreg.REG_BINARY,
+      'DWORD': _winreg.REG_DWORD,
+      'DWORD_LITTLE_ENDIAN': _winreg.REG_DWORD_LITTLE_ENDIAN,
+      'DWORD_BIG_ENDIAN': _winreg.REG_DWORD_BIG_ENDIAN,
+      'EXPAND_SZ': _winreg.REG_EXPAND_SZ,
+      'LINK': _winreg.REG_LINK,
+      'MULTI_SZ': _winreg.REG_MULTI_SZ,
+      'NONE': _winreg.REG_NONE,
+      'SZ': _winreg.REG_SZ,
+  }
+  if value_type not in value_type_mapping:
+    raise KeyError("Unknown registry value type '%s'" % value_type)
+  return value_type_mapping[value_type]
 
 
 def VerifyRegistryEntry(key, expectation):
   """Verifies a registry key according to the |expectation|.
 
   The |expectation| specifies whether or not the registry key should exist
-  (under 'exists') and optionally specifies an expected 'value' for the key.
+  (under 'exists') and optionally specifies expected 'values' for the key.
 
   Args:
-    key: Name of the registry key.
+    key: Name of the registry key. It is expanded using ResolvePath.
     expectation: A dictionary with the following keys and values:
-        'exists' a boolean indicating whether the registry entry should exist.
-        'value' (optional) a string representing the expected value for
-            the key.
+        'exists' a boolean indicating whether the registry key should exist.
+        'values' (optional) a dictionary where each key is a registry value and
+            its associated value is a dictionary with the following key and
+            values:
+                'type' a string indicating the type of the registry value.
+                'data' the associated data of the registry value. If it is a
+                    string, it is expanded using ResolvePath.
   """
-  root_key, sub_key = key.split('\\', 1)
+  resolved_key = path_resolver.ResolvePath(key)
+  root_key, sub_key = resolved_key.split('\\', 1)
   try:
     # Query the Windows registry for the registry key. It will throw a
     # WindowsError if the key doesn't exist.
-    _ = _winreg.OpenKey(RootKeyConstant(root_key), sub_key, 0, _winreg.KEY_READ)
+    key_handle = _winreg.OpenKey(RootKeyConstant(root_key), sub_key, 0,
+                                 _winreg.KEY_QUERY_VALUE)
   except WindowsError:
     # Key doesn't exist. See that it matches the expectation.
-    assert not expectation['exists'], 'Registry entry %s is missing' % key
+    assert not expectation['exists'], ('Registry key %s is missing' %
+                                       resolved_key)
     return
   # The key exists, see that it matches the expectation.
-  assert expectation['exists'], 'Registry entry %s exists' % key
-  # TODO(sukolsak): Verify the expected value.
+  assert expectation['exists'], ('Registry key %s exists' % resolved_key)
+
+  # Verify the expected values.
+  if 'values' not in expectation:
+    return
+  for value, value_expectation in expectation['values'].iteritems():
+    # Query the value. It will throw a WindowsError if the value doesn't exist.
+    try:
+      data, value_type = _winreg.QueryValueEx(key_handle, value)
+    except WindowsError:
+      raise KeyError("Value '%s' of registry key %s is missing" % (
+          value, resolved_key))
+
+    # Verify the type of the value.
+    expected_value_type = value_expectation['type']
+    assert ValueTypeConstant(expected_value_type) == value_type, \
+        "Value '%s' of registry key %s has unexpected type '%s'" % (
+            value, resolved_key, expected_value_type)
+
+    # Verify the associated data of the value.
+    expected_data = value_expectation['data']
+    if isinstance(expected_data, basestring):
+      expected_data = path_resolver.ResolvePath(expected_data)
+    assert expected_data == data, \
+        ("Value '%s' of registry key %s has unexpected data.\n"
+         "  Expected: %s\n"
+         "  Actual: %s" % (value, resolved_key, expected_data, data))
diff --git a/chrome/test/mini_installer/test_installer.py b/chrome/test/mini_installer/test_installer.py
index 8f5d9af..0b842e8 100644
--- a/chrome/test/mini_installer/test_installer.py
+++ b/chrome/test/mini_installer/test_installer.py
@@ -9,10 +9,11 @@
 the design documentation at http://goo.gl/Q0rGM6
 """
 
-import argparse
 import json
+import optparse
 import os
 import subprocess
+import sys
 import unittest
 
 import path_resolver
@@ -189,14 +190,16 @@
 
 
 def main():
-  parser = argparse.ArgumentParser(description='Test the installer.')
-  parser.add_argument('config_filename',
-                      help='The relative/absolute path to the config file.')
-  args = parser.parse_args()
+  usage = 'usage: %prog config_filename'
+  parser = optparse.OptionParser(usage, description='Test the installer.')
+  _, args = parser.parse_args()
+  if len(args) != 1:
+    parser.error('Incorrect number of arguments.')
 
-  config = ParseConfigFile(args.config_filename)
+  config = ParseConfigFile(args[0])
   RunTests(config)
+  return 0
 
 
 if __name__ == '__main__':
-  main()
+  sys.exit(main())
diff --git a/chrome/test/mini_installer/uninstall_chrome.py b/chrome/test/mini_installer/uninstall_chrome.py
index 3680940..c7b8996 100644
--- a/chrome/test/mini_installer/uninstall_chrome.py
+++ b/chrome/test/mini_installer/uninstall_chrome.py
@@ -2,34 +2,36 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Uninstall Chrome.
+"""Uninstalls Chrome.
 
 This script reads the uninstall command from registry, calls it, and verifies
 the output status code.
 """
 
 import _winreg
-import argparse
+import optparse
 import subprocess
 import sys
 
+import path_resolver
+
 
 def main():
-  parser = argparse.ArgumentParser(description='Uninstall Chrome.')
-  parser.add_argument('--system-level', dest='system_level',
-                      action='store_const', const=True, default=False,
-                      help='Uninstall Chrome at system level.')
-  args = parser.parse_args()
+  parser = optparse.OptionParser(description='Uninstall Chrome.')
+  parser.add_option('--system-level', action='store_true', dest='system_level',
+                    default=False, help='Uninstall Chrome at system level.')
+  options, _ = parser.parse_args()
 
   # TODO(sukolsak): Add support for uninstalling MSI-based Chrome installs when
   # we support testing MSIs.
-  if args.system_level:
+  if options.system_level:
     root_key = _winreg.HKEY_LOCAL_MACHINE
   else:
     root_key = _winreg.HKEY_CURRENT_USER
   sub_key = ('SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\'
-             'Google Chrome')
-  key = _winreg.OpenKey(root_key, sub_key, 0, _winreg.KEY_QUERY_VALUE)
+             '$CHROME_LONG_NAME')
+  resolved_sub_key = path_resolver.ResolvePath(sub_key)
+  key = _winreg.OpenKey(root_key, resolved_sub_key, 0, _winreg.KEY_QUERY_VALUE)
   uninstall_string, _ = _winreg.QueryValueEx(key, 'UninstallString')
   exit_status = subprocess.call(uninstall_string, shell=True)
   # The exit status for successful uninstallation of Chrome is 19 (see
diff --git a/chrome/test/mini_installer/verifier.py b/chrome/test/mini_installer/verifier.py
index 1436040..e6b466e 100644
--- a/chrome/test/mini_installer/verifier.py
+++ b/chrome/test/mini_installer/verifier.py
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import file_verifier
+import process_verifier
 import registry_verifier
 
 
@@ -19,6 +20,8 @@
   for verifier_name, value in property.iteritems():
     if verifier_name == 'Files':
       file_verifier.VerifyFiles(value)
+    elif verifier_name == 'Processes':
+      process_verifier.VerifyProcesses(value)
     elif verifier_name == 'RegistryEntries':
       registry_verifier.VerifyRegistryEntries(value)
     else:
diff --git a/chrome/test/perf/feature_startup_test.cc b/chrome/test/perf/feature_startup_test.cc
index d75e4d2..73b4fa3 100644
--- a/chrome/test/perf/feature_startup_test.cc
+++ b/chrome/test/perf/feature_startup_test.cc
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #include "base/path_service.h"
-#include "base/perftimer.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/perftimer.h"
 #include "base/time/time.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/common/chrome_paths.h"
diff --git a/chrome/test/perf/rendering/throughput_tests.cc b/chrome/test/perf/rendering/throughput_tests.cc
index a21c685..b2fc892 100644
--- a/chrome/test/perf/rendering/throughput_tests.cc
+++ b/chrome/test/perf/rendering/throughput_tests.cc
@@ -543,7 +543,9 @@
   RunTest("canvas2d_balls_with_shadow", kNone | kIsGpuCanvasTest | kIsFlaky);
 }
 
-IN_PROC_BROWSER_TEST_F(ThroughputTestThread, DrawImageShadowGPU) {
+// Intermittent failure, should be fixed by converting to telemetry.
+// See crbug.com/276500 for more details.
+IN_PROC_BROWSER_TEST_F(ThroughputTestThread, DISABLED_DrawImageShadowGPU) {
   // TODO(junov): Fix test flakiness crbug.com/272383
   RunTest("canvas2d_balls_with_shadow", kNone | kIsGpuCanvasTest | kIsFlaky);
 }
diff --git a/chrome/test/perf/url_parse_perftest.cc b/chrome/test/perf/url_parse_perftest.cc
index 20662f7..3aadd06 100644
--- a/chrome/test/perf/url_parse_perftest.cc
+++ b/chrome/test/perf/url_parse_perftest.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/perftimer.h"
+#include "base/test/perftimer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 #include "url/url_canon.h"
@@ -18,8 +18,8 @@
 #include "webkit/third_party/WebCore/platform/CString.h"
 
 #define KURL WebKitKURL
-#include "KURL.h"
 #include "KURL.cpp"
+#include "KURL.h"
 #pragma warning(pop)
 
 TEST(URLParse, FullURL) {
@@ -45,7 +45,7 @@
 const char typical_url3[] = "http://store.apple.com/1-800-MY-APPLE/WebObjects/AppleStore.woa/wa/RSLID?nnmm=browse&mco=578E9744&node=home/desktop/mac_pro";
 int typical_url3_len = static_cast<int>(strlen(typical_url3));
 
-}
+}  // namespace
 
 TEST(URLParse, TypicalURLParse) {
   url_parse::Parsed parsed1;
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc
index 5952b08..e7e0260 100644
--- a/chrome/test/ppapi/ppapi_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_browsertest.cc
@@ -286,15 +286,27 @@
 TEST_PPAPI_NACL(Graphics2D_FlushOffscreenUpdate)
 TEST_PPAPI_NACL(Graphics2D_BindNull)
 
-#if defined(OS_WIN) && !defined(USE_AURA)
+#if defined(OS_WIN)
+// In-process and NaCl tests are having flaky failures on Win: crbug.com/242252
+#define MAYBE_IN_Graphics3D DISABLED_Graphics3D
+#define MAYBE_OUT_Graphics3D Graphics3D
+#define MAYBE_NACL_Graphics3D DISABLED_Graphics3D
+#elif defined(OS_WIN) && defined(USE_AURA)
 // These tests fail with the test compositor which is what's used by default for
 // browser tests on Windows Aura. Renable when the software compositor is
 // available.
-// In-process and NaCl tests are having flaky failures on Win: crbug.com/242252
-TEST_PPAPI_IN_PROCESS(DISABLED_Graphics3D)
-TEST_PPAPI_OUT_OF_PROCESS(Graphics3D)
-TEST_PPAPI_NACL(DISABLED_Graphics3D)
+#define MAYBE_IN_Graphics3D DISABLED_Graphics3D
+#define MAYBE_OUT_Graphics3D DISABLED_Graphics3D
+#define MAYBE_NACL_Graphics3D DISABLED_Graphics3D
+#else
+// The tests are failing in-process. crbug.com/280282
+#define MAYBE_IN_Graphics3D DISABLED_Graphics3D
+#define MAYBE_OUT_Graphics3D Graphics3D
+#define MAYBE_NACL_Graphics3D Graphics3D
 #endif
+TEST_PPAPI_IN_PROCESS(MAYBE_IN_Graphics3D)
+TEST_PPAPI_OUT_OF_PROCESS(MAYBE_OUT_Graphics3D)
+TEST_PPAPI_NACL(MAYBE_NACL_Graphics3D)
 
 TEST_PPAPI_IN_PROCESS(ImageData)
 TEST_PPAPI_OUT_OF_PROCESS(ImageData)
@@ -337,11 +349,9 @@
 }
 
 TEST_PPAPI_OUT_OF_PROCESS_WITH_SSL_SERVER(TCPSocketPrivate)
-TEST_PPAPI_IN_PROCESS_WITH_SSL_SERVER(TCPSocketPrivate)
 TEST_PPAPI_NACL_WITH_SSL_SERVER(TCPSocketPrivate)
 
 TEST_PPAPI_OUT_OF_PROCESS_WITH_SSL_SERVER(TCPSocketPrivateTrusted)
-TEST_PPAPI_IN_PROCESS_WITH_SSL_SERVER(TCPSocketPrivateTrusted)
 
 // UDPSocket tests.
 // UDPSocket_Broadcast is disabled for OSX because it requires root permissions
@@ -620,7 +630,9 @@
 TEST_PPAPI_OUT_OF_PROCESS_VIA_HTTP(URLRequest_AppendDataToBody)
 TEST_PPAPI_NACL(URLRequest_AppendDataToBody)
 TEST_PPAPI_IN_PROCESS_VIA_HTTP(URLRequest_AppendFileToBody)
+#if !defined(OS_MACOSX)  // TODO(teravest): http://crbug.com/280570
 TEST_PPAPI_OUT_OF_PROCESS_VIA_HTTP(URLRequest_AppendFileToBody)
+#endif
 TEST_PPAPI_NACL(URLRequest_AppendFileToBody)
 TEST_PPAPI_IN_PROCESS_VIA_HTTP(URLRequest_Stress)
 TEST_PPAPI_OUT_OF_PROCESS_VIA_HTTP(URLRequest_Stress)
@@ -762,7 +774,9 @@
       LIST_TEST(FileIO_NotAllowMixedReadWrite)
       LIST_TEST(FileIO_ReadWriteSetLength)
       LIST_TEST(FileIO_ReadToArrayWriteSetLength)
+#if !defined(OS_MACOSX)  // TODO(teravest): http://crbug.com/280570
       LIST_TEST(FileIO_TouchQuery)
+#endif
       LIST_TEST(FileIO_WillWriteWillSetLength)
       LIST_TEST(FileIO_RequestOSFileHandle)
       LIST_TEST(FileIO_RequestOSFileHandleWithOpenExclusive)
diff --git a/chrome/test/pyautolib/chromeos_network.py b/chrome/test/pyautolib/chromeos_network.py
index 5e8b9ef..32c86cd 100644
--- a/chrome/test/pyautolib/chromeos_network.py
+++ b/chrome/test/pyautolib/chromeos_network.py
@@ -12,112 +12,6 @@
 import pyauto
 import pyauto_errors
 
-class WifiPowerStrip(PowerStrip):
-  """Manages the power state of wifi routers connected to a power strip.
-
-  This class provides additional functionality over PowerStrip by providing
-  a timeout feature for wifi routers connected to the strip.  This is to prevent
-  repeated on/off calls to the same router which may put the router in an
-  undesired state.
-  """
-
-  def __init__ (self, host, routers):
-    """Initializes a WifiPowerStrip object.
-
-    Args:
-      host: IP of the switch that the routers are attached to.
-      routers: Dictionary of wifi routers in the following format:
-               {
-                 '< router name >': {
-                   'strip_id' : '.aX'  # where X is the port number
-                   < additional fields may be added here for each router >
-                 }
-               }
-    """
-    self._router_dict = routers
-
-    # Each router will have a timestamp associated to it regarding whether
-    # or not an action can be performed on it yet.   This is to prevent
-    # the spamming of power on/off calls on a particular router.
-    # The WifiPowerStrip_UsableTime field specifies the earliest time
-    # after which the router may be used.  We will initialize it to now
-    # since they should all be usable at init.
-    for router_info in self._router_dict.values():
-      router_info['WifiPowerStrip_UsableTime'] = time.time()
-
-    # _routers_used keeps track of which routers were used during the lifetime
-    # of the WifiPowerStrip instance.  Adding used routers occurs when
-    # a wifi router has been turned on.  Otherwise, it get clears upon
-    # the TurnOffUsedRouters call.
-    self._routers_used = set()
-    PowerStrip.__init__(self, host)
-
-  def GetRouterConfig(self, router_name):
-    """Returns the configuration for the specified router.
-
-    Args:
-      router_name: A string specifying the router.
-
-    Returns:
-      The config dictionary for the given router if the router is defined.
-      None otherwise.
-    """
-    return copy.deepcopy(self._router_dict.get(router_name))
-
-  def RouterPower(self, router_name, power_state, pause_after=5):
-    """Executes PowerStrip commands.
-
-    Args:
-      router_name: The name of the router to perform the action on.
-      power_state: A boolean value where True represents turning the router on
-                   and False represents turning the router off.
-      pause_after: Specified in seconds, and specifies the time to sleep
-                   after a command is run.  This is to prevent spamming of
-                   power on/off of the same router which has put the router
-                   in an undesirable state.
-
-    Raises:
-      Exception if router_name is not a valid router.
-    """
-    router = self.GetRouterConfig(router_name)
-    if not router: raise Exception('Invalid router name \'%s\'.' % router_name)
-
-    # Hidden routers will always be on.  Don't allow controlling of the power
-    # for these networks.
-    if router.get('hidden'):
-      return
-
-    sleep_time = router['WifiPowerStrip_UsableTime'] - time.time()
-    if sleep_time > 0:
-      time.sleep(sleep_time)
-
-    if power_state:
-      self._routers_used |= set([router_name])
-      logging.debug('Turning on router %s:%s.' %
-                    (router['strip_id'], router_name))
-      self.PowerOn(router['strip_id'])
-    else:
-      logging.debug('Turning off router %s:%s.' %
-                    (router['strip_id'], router_name))
-      self.PowerOff(router['strip_id'])
-
-    # Set the Usable time of the particular router to pause_after
-    # seconds after the current time.
-    router['WifiPowerStrip_UsableTime'] = time.time() + pause_after
-
-  def TurnOffAllRouters(self):
-    """Turns off all the routers."""
-    for router in self._router_dict:
-      self.RouterPower(router, False, pause_after=0)
-
-  def TurnOffUsedRouters(self):
-    """Turns off the routers that were once turned on."""
-    for router in self._routers_used:
-      self.RouterPower(router, False, pause_after=0)
-
-    self._routers_used = set()
-
-
 class PyNetworkUITest(pyauto.PyUITest):
   """A subclass of PyUITest for Chrome OS network tests.
 
@@ -139,14 +33,11 @@
     self._ParseDefaultRoutingTable()
     pyauto.PyUITest.setUp(self)
     self.ForgetAllRememberedNetworks()
-    self._wifi_power_strip = None
 
   def tearDown(self):
     self.ForgetAllRememberedNetworks()
     pyauto.PyUITest.tearDown(self)
     self._PopServiceOrder()
-    if self._wifi_power_strip:
-      self._wifi_power_strip.TurnOffUsedRouters()
     # Remove the route entry for the power strip.
     if hasattr(self, 'ps_route_entry'):
       os.system('route del -net %(ipaddress)s gateway %(gateway)s netmask '
@@ -209,74 +100,3 @@
     assert old_service_order == set_service_order, \
         'Flimflam service order not set properly. %s != %s' % \
         (old_service_order, set_service_order)
-
-  def _SetupRouteForPowerStrip(self, ipaddress, iface='eth'):
-    """Create a route table entry for the power strip."""
-
-    # Assume device has only one interface that is prepended with
-    # $iface and use that one.
-    try:
-      iface = [ key for key in self.default_routes.keys() if iface in key ][0]
-    except:
-      assert 'Unable to find interface of type %s.' % iface
-
-    self.ps_route_entry = {
-      'iface' : iface,
-      'gateway' : self.default_routes[iface]['gateway'],
-      'netmask' : '255.255.255.255',
-      'ipaddress' : ipaddress
-    }
-
-    os.system('route add -net %(ipaddress)s gateway %(gateway)s netmask '
-              '%(netmask)s dev %(iface)s' % self.ps_route_entry)
-
-    # Verify the route was added.
-    assert os.system('route -n | egrep "^%(ipaddress)s[[:space:]]+%(gateway)s'
-                     '[[:space:]]+%(netmask)s"' % self.ps_route_entry) == 0, \
-                     'Failed to create default route for powerstrip.'
-
-  def InitWifiPowerStrip(self):
-    """Initializes the router controller using the specified config file."""
-
-    assert os.path.exists(PyNetworkUITest._ROUTER_CONFIG_FILE), \
-           'Router configuration file does not exist.'
-
-    config = pyauto.PyUITest.EvalDataFrom(self._ROUTER_CONFIG_FILE)
-    strip_ip, routers = config['strip_ip'], config['routers']
-
-    self._SetupRouteForPowerStrip(strip_ip)
-    self._wifi_power_strip = WifiPowerStrip(strip_ip, routers)
-
-    self.RouterPower = self._wifi_power_strip.RouterPower
-    self.TurnOffAllRouters = self._wifi_power_strip.TurnOffAllRouters
-    self.GetRouterConfig = self._wifi_power_strip.GetRouterConfig
-
-  def ConnectToWifiRouter(self, router_name, shared=True):
-    """Connects to a router by name.
-
-    Args:
-      router_name: The name of the router that is specified in the
-                   configuration file.
-    """
-    router = self._wifi_power_strip.GetRouterConfig(router_name)
-    assert router, 'Router with name %s is not defined ' \
-                   'in the router configuration.' % router_name
-    security = router.get('security', 'SECURITY_NONE')
-    passphrase = router.get('passphrase', '')
-
-    # Branch off the connect calls depending on if the wifi network is hidden
-    # or not.
-    error_string = None
-    if router.get('hidden'):
-      error_string = self.ConnectToHiddenWifiNetwork(router['ssid'], security,
-                                                     passphrase)
-    else:
-      service_path = self.GetServicePath(router['ssid'])
-      assert service_path, 'Service with SSID %s is not present.' % \
-             router['ssid']
-
-      logging.debug('Connecting to router %s.' % router_name)
-      error_string = self.ConnectToWifiNetwork(service_path,
-                                               password=passphrase,
-                                               shared=shared)
-    return error_string
diff --git a/chrome/test/pyautolib/pyauto.py b/chrome/test/pyautolib/pyauto.py
index 7d81e0a..83bc88f 100755
--- a/chrome/test/pyautolib/pyauto.py
+++ b/chrome/test/pyautolib/pyauto.py
@@ -4496,42 +4496,6 @@
 
     return panels
 
-  def RestoreOnline(self):
-    """Returns the device from offline mode if GoOffline was used."""
-
-    assert PyUITest.IsChromeOS()
-
-    # Restores etherent connection
-    stdout, stderr = self.RunSuperuserActionOnChromeOS('TeardownBackchannel')
-
-    if hasattr(self, 'bc_cellular_enabled') and self.bc_cellular_enabled:
-      self.ToggleNetworkDevice('cellular', True)
-    if hasattr(self, 'bc_wifi_enabled') and self.bc_wifi_enabled:
-      self.ToggleNetworkDevice('wifi', True)
-
-    assert 'RuntimeError' not in stderr, stderr
-
-  def GoOffline(self):
-    """Puts device in offline mode.
-
-    The device is put into offline mode by disabling all network interfaces
-    but keeping the the wired ethernet interface up and faking shill/flimflam
-    into thinking there is no ethernet interface by renaming the interface.
-    This is so we can keep ssh connections over the wired connection alive.
-    """
-    assert PyUITest.IsChromeOS()
-    net_info = self.GetNetworkInfo()
-    self.bc_wifi_enabled = net_info.get('wifi_enabled')
-    self.bc_cellular_enabled = net_info.get('cellular_enabled')
-
-    if self.bc_cellular_enabled:
-      self.ToggleNetworkDevice('cellular', False)
-    if self.bc_wifi_enabled:
-      self.ToggleNetworkDevice('wifi', False)
-
-    stdout, stderr = self.RunSuperuserActionOnChromeOS('SetupBackchannel')
-    assert 'RuntimeError' not in stderr, stderr
-
   def GetNetworkInfo(self):
     """Get details about ethernet, wifi, and cellular networks on chromeos.
 
@@ -4596,52 +4560,6 @@
 
     return network_info
 
-  def GetConnectedWifi(self):
-    """Returns the SSID of the currently connected wifi network.
-
-    Returns:
-      The SSID of the connected network or None if we're not connected.
-    """
-    service_list = self.GetNetworkInfo()
-    connected_service_path = service_list.get('connected_wifi')
-    if 'wifi_networks' in service_list and \
-       connected_service_path in service_list['wifi_networks']:
-       return service_list['wifi_networks'][connected_service_path]['name']
-
-  def GetServicePath(self, ssid, encryption=None, timeout=30):
-    """Waits until the SSID is observed and returns its service path.
-
-    Args:
-      ssid: String defining the SSID we are searching for.
-      encryption: Encryption type of the network; either None to return the
-                  first instance of network that matches the ssid, '' for
-                  an empty network, 'PSK', 'WEP' or '8021X'.
-      timeout: Duration to wait for ssid to appear.
-
-    Returns:
-      The service path or None if SSID does not exist after timeout period.
-    """
-    def _GetServicePath():
-      service_list = self.GetNetworkInfo().get('wifi_networks', [])
-      for service_path, service_obj in service_list.iteritems():
-        if not (isinstance(service_obj, dict) and
-                'encryption' in service_obj and
-                'name' in service_obj):
-          continue
-
-        service_encr = 'PSK' if service_obj['encryption'] in ['WPA', 'RSN']\
-                       else service_obj['encryption']
-
-        if service_obj['name'] == ssid and \
-           (encryption == None or service_encr == encryption):
-          return service_path
-      self.NetworkScan()
-      return None
-
-    service_path = self.WaitUntil(_GetServicePath, timeout=timeout,
-                                  retry_sleep=1, return_retval=True)
-    return service_path or None
-
   def NetworkScan(self):
     """Causes ChromeOS to scan for available wifi networks.
 
@@ -4672,90 +4590,6 @@
     }
     return self._GetResultFromJSONRequest(cmd_dict, windex=None)
 
-  PROXY_TYPE_DIRECT = 1
-  PROXY_TYPE_MANUAL = 2
-  PROXY_TYPE_PAC = 3
-
-  def WaitUntilWifiNetworkAvailable(self, ssid, timeout=60, is_hidden=False):
-    """Waits until the given network is available.
-
-    Routers that are just turned on may take up to 1 minute upon turning them
-    on to broadcast their SSID.
-
-    Args:
-      ssid: SSID of the service we want to connect to.
-      timeout: timeout (in seconds)
-
-    Raises:
-      Exception if timeout duration has been hit before wifi router is seen.
-
-    Returns:
-      True, when the wifi network is seen within the timout period.
-      False, otherwise.
-    """
-    def _GotWifiNetwork():
-      # Returns non-empty array if desired SSID is available.
-      try:
-        return [wifi for wifi in
-                self.NetworkScan().get('wifi_networks', {}).values()
-                if wifi.get('name') == ssid]
-      except pyauto_errors.JSONInterfaceError:
-        # Temporary fix until crosbug.com/14174 is fixed.
-        # NetworkScan is only used in updating the list of networks so errors
-        # thrown by it are not critical to the results of wifi tests that use
-        # this method.
-        return False
-
-    # The hidden AP's will always be on, thus we will assume it is ready to
-    # connect to.
-    if is_hidden:
-      return bool(_GotWifiNetwork())
-
-    return self.WaitUntil(_GotWifiNetwork, timeout=timeout, retry_sleep=1)
-
-  def ResetProxySettingsOnChromeOS(self):
-    """Public wrapper around proxysettings teardown functions."""
-    self.SetSharedProxies(False)
-    proxy_dict = {
-        'mode': 'direct'
-    }
-    self.SetProxySettingOnChromeOS(proxy_dict)
-
-  def SetProxySettingOnChromeOS(self, proxy_config):
-    """Set the proxy config of the current network.
-
-    Owner must be logged in for these to persist.
-    If user is not logged in or is logged in as non-owner or guest,
-    proxy settings do not persist across browser restarts or login/logout.
-
-    Args:
-      proxy_config:   A dictionary following the format described in
-                      prefs/proxy_config_dictionary.h.
-
-    Raises:
-      pyauto_errors.JSONInterfaceError if the automation call returns an error.
-    """
-    cmd_dict = {
-        'command': 'SetProxySettings',
-        'proxy_config': json.dumps(proxy_config)
-    }
-    return self._GetResultFromJSONRequest(cmd_dict, windex=None)
-
-  def SetSharedProxies(self, value):
-    """Allows proxies on the shared networks.
-
-    Args:
-      value: True/False to set and clear respectively.
-
-    Raises:
-      pyauto_errors.JSONInterfaceError if the automation call returns an error.
-    """
-    cmd_dict = {
-        'command': 'SetSharedProxies',
-        'value': value,
-    }
-    return self._GetResultFromJSONRequest(cmd_dict, windex=None)
-
   def ForgetAllRememberedNetworks(self):
     """Forgets all networks that the device has marked as remembered."""
     for service in self.GetNetworkInfo()['remembered_wifi']:
@@ -4786,72 +4620,6 @@
     }
     self._GetResultFromJSONRequest(cmd_dict, windex=None, timeout=50000)
 
-  def ConnectToCellularNetwork(self):
-    """Connects to the available cellular network.
-
-    Blocks until connection succeeds or fails.
-
-    Returns:
-      An error string if an error occured.
-      None otherwise.
-
-    Raises:
-      pyauto_errors.JSONInterfaceError if the automation call returns an error.
-    """
-    # Every device should only have one cellular network present, so we can
-    # scan for it.
-    cellular_networks = self.NetworkScan().get('cellular_networks', {}).keys()
-    self.assertTrue(cellular_networks, 'Could not find cellular service.')
-    service_path = cellular_networks[0]
-
-    cmd_dict = {
-        'command': 'ConnectToCellularNetwork',
-        'service_path': service_path,
-    }
-    result = self._GetResultFromJSONRequest(
-        cmd_dict, windex=None, timeout=50000)
-    return result.get('error_string')
-
-  def DisconnectFromCellularNetwork(self):
-    """Disconnect from the connected cellular network.
-
-    Blocks until disconnect is complete.
-
-    Raises:
-      pyauto_errors.JSONInterfaceError if the automation call returns an error.
-    """
-    cmd_dict = {
-        'command': 'DisconnectFromCellularNetwork',
-    }
-    self._GetResultFromJSONRequest(cmd_dict, windex=None)
-
-  def ConnectToWifiNetwork(self, service_path, password='', shared=True):
-    """Connect to a wifi network by its service path.
-
-    Blocks until connection succeeds or fails.
-
-    Args:
-      service_path: Flimflam path that defines the wifi network.
-      password: Passphrase for connecting to the wifi network.
-      shared: Boolean value specifying whether the network should be shared.
-
-    Returns:
-      An error string if an error occured.
-      None otherwise.
-
-    Raises:
-      pyauto_errors.JSONInterfaceError if the automation call returns an error.
-    """
-    cmd_dict = {
-        'command': 'ConnectToWifiNetwork',
-        'service_path': service_path,
-        'password': password,
-        'shared': shared,
-    }
-    result = self._GetResultFromJSONRequest(
-        cmd_dict, windex=None, timeout=50000)
-    return result.get('error_string')
-
   def ConnectToHiddenWifiNetwork(self, ssid, security, password='',
                                  shared=True, save_credentials=False):
     """Connect to a wifi network by its service path.
@@ -4888,134 +4656,6 @@
         cmd_dict, windex=None, timeout=50000)
     return result.get('error_string')
 
-  def DisconnectFromWifiNetwork(self):
-    """Disconnect from the connected wifi network.
-
-    Blocks until disconnect is complete.
-
-    Raises:
-      pyauto_errors.JSONInterfaceError if the automation call returns an error.
-    """
-    cmd_dict = {
-        'command': 'DisconnectFromWifiNetwork',
-    }
-    self._GetResultFromJSONRequest(cmd_dict, windex=None)
-
-  def AddPrivateNetwork(self,
-                        hostname,
-                        service_name,
-                        provider_type,
-                        username,
-                        password,
-                        cert_id='',
-                        key=''):
-    """Add and connect to a private network.
-
-    Blocks until connection succeeds or fails. This is equivalent to
-    'Add Private Network' in the network menu UI.
-
-    Args:
-      hostname: Server hostname for the private network.
-      service_name: Service name that defines the private network. Do not
-                    add multiple services with the same name.
-      provider_type: Types are L2TP_IPSEC_PSK and L2TP_IPSEC_USER_CERT.
-                     Provider type OPEN_VPN is not yet supported.
-                     Type names returned by GetPrivateNetworkInfo will
-                     also work.
-      username: Username for connecting to the virtual network.
-      password: Passphrase for connecting to the virtual network.
-      cert_id: Certificate id for a L2TP_IPSEC_USER_CERT network.
-      key: Pre-shared key for a L2TP_IPSEC_PSK network.
-
-    Returns:
-      An error string if an error occured.
-      None otherwise.
-
-    Raises:
-      pyauto_errors.JSONInterfaceError if the automation call returns an error.
-    """
-    cmd_dict = {
-        'command': 'AddPrivateNetwork',
-        'hostname': hostname,
-        'service_name': service_name,
-        'provider_type': provider_type,
-        'username': username,
-        'password': password,
-        'cert_id': cert_id,
-        'key': key,
-    }
-    result = self._GetResultFromJSONRequest(
-        cmd_dict, windex=None, timeout=50000)
-    return result.get('error_string')
-
-  def GetPrivateNetworkInfo(self):
-    """Get details about private networks on chromeos.
-
-    Returns:
-      A dictionary including information about all remembered virtual networks
-      as well as the currently connected virtual network, if any.
-      Sample:
-      { u'connected': u'/service/vpn_123_45_67_89_test_vpn'}
-        u'/service/vpn_123_45_67_89_test_vpn':
-          { u'username': u'vpn_user',
-            u'name': u'test_vpn',
-            u'hostname': u'123.45.67.89',
-            u'key': u'abcde',
-            u'cert_id': u'',
-            u'password': u'zyxw123',
-            u'provider_type': u'L2TP_IPSEC_PSK'},
-        u'/service/vpn_111_11_11_11_test_vpn2':
-          { u'username': u'testerman',
-            u'name': u'test_vpn2',
-            u'hostname': u'111.11.11.11',
-            u'key': u'fghijklm',
-            u'cert_id': u'',
-            u'password': u'789mnop',
-            u'provider_type': u'L2TP_IPSEC_PSK'},
-
-    Raises:
-      pyauto_errors.JSONInterfaceError if the automation call returns an error.
-    """
-    cmd_dict = { 'command': 'GetPrivateNetworkInfo' }
-    return self._GetResultFromJSONRequest(cmd_dict, windex=None)
-
-  def ConnectToPrivateNetwork(self, service_path):
-    """Connect to a remembered private network by its service path.
-
-    Blocks until connection succeeds or fails. The network must have been
-    previously added with all necessary connection details.
-
-    Args:
-      service_path: Service name that defines the private network.
-
-    Returns:
-      An error string if an error occured.
-      None otherwise.
-
-    Raises:
-      pyauto_errors.JSONInterfaceError if the automation call returns an error.
-    """
-    cmd_dict = {
-        'command': 'ConnectToPrivateNetwork',
-        'service_path': service_path,
-    }
-    result = self._GetResultFromJSONRequest(
-        cmd_dict, windex=None, timeout=50000)
-    return result.get('error_string')
-
-  def DisconnectFromPrivateNetwork(self):
-    """Disconnect from the active private network.
-
-    Expects a private network to be active.
-
-    Raises:
-      pyauto_errors.JSONInterfaceError if the automation call returns an error.
-    """
-    cmd_dict = {
-        'command': 'DisconnectFromPrivateNetwork',
-    }
-    return self._GetResultFromJSONRequest(cmd_dict, windex=None)
-
   def EnableSpokenFeedback(self, enabled):
     """Enables or disables spoken feedback accessibility mode.
 
diff --git a/chrome/test/telemetry/chromeos/login_unittest.py b/chrome/test/telemetry/chromeos/login_unittest.py
index da293b0..92b3b55 100644
--- a/chrome/test/telemetry/chromeos/login_unittest.py
+++ b/chrome/test/telemetry/chromeos/login_unittest.py
@@ -10,8 +10,7 @@
 from telemetry.core import extension_to_load
 from telemetry.core import util
 from telemetry.core.chrome import cros_interface
-from telemetry.core.chrome import cros_util
-from telemetry.test import options_for_unittests
+from telemetry.unittest import options_for_unittests
 
 class CrOSAutoTest(unittest.TestCase):
   def setUp(self):
@@ -36,13 +35,17 @@
 
     if with_autotest_ext:
       extension_path = os.path.join(os.path.dirname(__file__), 'autotest_ext')
-      self._load_extension = extension_to_load.ExtensionToLoad(extension_path,
-                                                               True)
+      self._load_extension = extension_to_load.ExtensionToLoad(
+          path=extension_path,
+          browser_type=options.browser_type,
+          is_component=True)
       options.extensions_to_load = [self._load_extension]
 
     browser_to_create = browser_finder.FindBrowser(options)
     self.assertTrue(browser_to_create)
-    return browser_to_create.Create()
+    b = browser_to_create.Create()
+    b.Start()
+    return b
 
   def _GetAutotestExtension(self, browser):
     """Returns the autotest extension instance"""
diff --git a/chrome/tools/build/win/FILES.cfg b/chrome/tools/build/win/FILES.cfg
index 4fba1cd..58a67db 100644
--- a/chrome/tools/build/win/FILES.cfg
+++ b/chrome/tools/build/win/FILES.cfg
@@ -77,6 +77,16 @@
     'optional': ['dev', 'official'],
   },
   {
+    'filename': 'chrome_touch_140_percent.pak',
+    'buildtype': ['dev', 'official'],
+    'optional': ['dev', 'official'],
+  },
+  {
+    'filename': 'chrome_touch_180_percent.pak',
+    'buildtype': ['dev', 'official'],
+    'optional': ['dev', 'official'],
+  },
+  {
     'filename': 'crash_service.exe',
     'buildtype': ['dev', 'official'],
   },
diff --git a/chrome/tools/build/win/syzygy_instrument.py b/chrome/tools/build/win/syzygy_instrument.py
index 75eaf2d..dfde310 100755
--- a/chrome/tools/build/win/syzygy_instrument.py
+++ b/chrome/tools/build/win/syzygy_instrument.py
@@ -42,12 +42,11 @@
   return stdout, stderr
 
 
-def _CompileFilter(syzygy_dir, executable, symbol, dst_dir, filter_file):
+def _CompileFilter(syzygy_dir, executable, symbol, filter_file,
+                   output_filter_file):
   """Compiles the provided filter writing the compiled filter file to
-  dst_dir. Returns the absolute path of the compiled filter.
+  output_filter_file.
   """
-  output_filter_file = os.path.abspath(os.path.join(
-      dst_dir, os.path.basename(filter_file) + '.json'))
   cmd = [os.path.abspath(os.path.join(syzygy_dir, _GENFILTER_EXE)),
          '--action=compile',
          '--input-image=%s' % executable,
@@ -59,7 +58,7 @@
   _Shell(*cmd)
   if not os.path.exists(output_filter_file):
     raise RuntimeError('Compiled filter file missing: %s' % output_filter_file)
-  return output_filter_file
+  return
 
 
 def _InstrumentBinary(syzygy_dir, mode, executable, symbol, dst_dir,
@@ -114,13 +113,12 @@
     os.makedirs(options.destination_dir)
 
   # Compile the filter if one was provided.
-  filter_file = None
   if options.filter:
-    filter_file = _CompileFilter(options.syzygy_dir,
-                                 options.input_executable,
-                                 options.input_symbol,
-                                 options.destination_dir,
-                                 options.filter)
+    _CompileFilter(options.syzygy_dir,
+                   options.input_executable,
+                   options.input_symbol,
+                   options.filter,
+                   options.output_filter_file)
 
   # Instruments the binaries into the destination directory.
   _InstrumentBinary(options.syzygy_dir,
@@ -128,7 +126,7 @@
                     options.input_executable,
                     options.input_symbol,
                     options.destination_dir,
-                    filter_file)
+                    options.output_filter_file)
 
   # Copy the agent DLL and PDB to the destination directory.
   _CopyAgentDLL(options.agent_dll, options.destination_dir);
@@ -152,6 +150,9 @@
   option_parser.add_option('--filter',
       help='An optional filter. This will be compiled and passed to the '
            'instrumentation executable.')
+  option_parser.add_option('--output-filter-file',
+      help='The path where the compiled filter will be written. This is '
+           'required if --filter is specified.')
   options, args = option_parser.parse_args()
 
   if not options.mode:
@@ -162,6 +163,8 @@
     option_parser.error('You must provide an input symbol file.')
   if not options.destination_dir:
     option_parser.error('You must provide a destination directory.')
+  if options.filter and not options.output_filter_file:
+    option_parser.error('You must provide a filter output file.')
 
   if not options.agent_dll:
     if not options.mode in _DEFAULT_AGENT_DLLS:
diff --git a/chrome/tools/ipclist/ipclist.cc b/chrome/tools/ipclist/ipclist.cc
index dfb6cf1..49a3d25 100644
--- a/chrome/tools/ipclist/ipclist.cc
+++ b/chrome/tools/ipclist/ipclist.cc
@@ -19,7 +19,7 @@
   int in_count;
   int out_count;
 
-  bool operator< (const msginfo other) const {
+  bool operator< (const msginfo& other) const {
     return id < other.id;
   }
 };
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc
index 02bd5e5..a044449 100644
--- a/chrome/utility/chrome_content_utility_client.cc
+++ b/chrome/utility/chrome_content_utility_client.cc
@@ -356,7 +356,6 @@
 
     LOGFONT logfont;
     if (GetObject(font, sizeof(LOGFONT), &logfont)) {
-      std::vector<char> font_data;
       content::UtilityThread::Get()->PreCacheFont(logfont);
       rv = GetFontData(hdc, table, offset, buffer, length);
       content::UtilityThread::Get()->ReleaseCachedFonts();
diff --git a/chrome/utility/importer/ie_importer_win.cc b/chrome/utility/importer/ie_importer_win.cc
index 11a61c2..0c7c1ea 100644
--- a/chrome/utility/importer/ie_importer_win.cc
+++ b/chrome/utility/importer/ie_importer_win.cc
@@ -471,7 +471,7 @@
 
 void IEImporter::ImportHistory() {
   const std::string kSchemes[] = {chrome::kHttpScheme,
-                                  chrome::kHttpsScheme,
+                                  content::kHttpsScheme,
                                   chrome::kFtpScheme,
                                   chrome::kFileScheme};
   int total_schemes = arraysize(kSchemes);
@@ -605,7 +605,7 @@
 
     GURL url(ac_list[i].key.c_str());
     if (!(LowerCaseEqualsASCII(url.scheme(), chrome::kHttpScheme) ||
-        LowerCaseEqualsASCII(url.scheme(), chrome::kHttpsScheme))) {
+        LowerCaseEqualsASCII(url.scheme(), content::kHttpsScheme))) {
       continue;
     }
 
diff --git a/chrome/utility/local_discovery/local_domain_resolver_unittest.cc b/chrome/utility/local_discovery/local_domain_resolver_unittest.cc
index 1dc9fc3..828af27 100644
--- a/chrome/utility/local_discovery/local_domain_resolver_unittest.cc
+++ b/chrome/utility/local_discovery/local_domain_resolver_unittest.cc
@@ -73,12 +73,17 @@
     mdns_client_.StartListening();
   }
 
-  void AddressCallback(bool resolved, const net::IPAddressNumber& address) {
-    if (address == net::IPAddressNumber()) {
-      AddressCallbackInternal(resolved, "");
-    } else {
-      AddressCallbackInternal(resolved, net::IPAddressToString(address));
-    }
+  std::string IPAddressToStringWithEmpty(const net::IPAddressNumber& address) {
+    if (address.empty()) return "";
+    return net::IPAddressToString(address);
+  }
+
+  void AddressCallback(bool resolved,
+                       const net::IPAddressNumber& address_ipv4,
+                       const net::IPAddressNumber& address_ipv6) {
+      AddressCallbackInternal(resolved,
+                              IPAddressToStringWithEmpty(address_ipv4),
+                              IPAddressToStringWithEmpty(address_ipv6));
   }
 
   void RunFor(base::TimeDelta time_period) {
@@ -92,8 +97,10 @@
     callback.Cancel();
   }
 
-  MOCK_METHOD2(AddressCallbackInternal,
-               void(bool resolved, std::string address));
+  MOCK_METHOD3(AddressCallbackInternal,
+               void(bool resolved,
+                    std::string address_ipv4,
+                    std::string address_ipv6));
 
   net::MockMDnsSocketFactory* socket_factory_;
   net::MDnsClientImpl mdns_client_;
@@ -111,7 +118,7 @@
 
   resolver.Start();
 
-  EXPECT_CALL(*this, AddressCallbackInternal(true, "1.2.3.4"));
+  EXPECT_CALL(*this, AddressCallbackInternal(true, "1.2.3.4", ""));
 
   socket_factory_->SimulateReceive(
       kSamplePacketA, sizeof(kSamplePacketA));
@@ -128,13 +135,13 @@
 
   resolver.Start();
 
-  EXPECT_CALL(*this, AddressCallbackInternal(true, "a::1:2:3:4"));
+  EXPECT_CALL(*this, AddressCallbackInternal(true, "", "a::1:2:3:4"));
 
   socket_factory_->SimulateReceive(
       kSamplePacketAAAA, sizeof(kSamplePacketAAAA));
 }
 
-TEST_F(LocalDomainResolverTest, ResolveDomainAny) {
+TEST_F(LocalDomainResolverTest, ResolveDomainAnyOneAvailable) {
   LocalDomainResolverImpl resolver(
       "myhello.local", net::ADDRESS_FAMILY_UNSPECIFIED,
       base::Bind(&LocalDomainResolverTest::AddressCallback,
@@ -145,10 +152,33 @@
 
   resolver.Start();
 
-  EXPECT_CALL(*this, AddressCallbackInternal(true, "a::1:2:3:4"));
+  socket_factory_->SimulateReceive(
+      kSamplePacketAAAA, sizeof(kSamplePacketAAAA));
+
+  EXPECT_CALL(*this, AddressCallbackInternal(true, "", "a::1:2:3:4"));
+
+  RunFor(base::TimeDelta::FromMilliseconds(150));
+}
+
+
+TEST_F(LocalDomainResolverTest, ResolveDomainAnyBothAvailable) {
+  LocalDomainResolverImpl resolver(
+      "myhello.local", net::ADDRESS_FAMILY_UNSPECIFIED,
+      base::Bind(&LocalDomainResolverTest::AddressCallback,
+                 base::Unretained(this)), &mdns_client_);
+
+  EXPECT_CALL(*socket_factory_, OnSendTo(_))
+      .Times(4);  // Twice per query
+
+  resolver.Start();
+
+  EXPECT_CALL(*this, AddressCallbackInternal(true, "1.2.3.4", "a::1:2:3:4"));
 
   socket_factory_->SimulateReceive(
       kSamplePacketAAAA, sizeof(kSamplePacketAAAA));
+
+  socket_factory_->SimulateReceive(
+      kSamplePacketA, sizeof(kSamplePacketA));
 }
 
 TEST_F(LocalDomainResolverTest, ResolveDomainNone) {
@@ -162,7 +192,7 @@
 
   resolver.Start();
 
-  EXPECT_CALL(*this, AddressCallbackInternal(false, ""));
+  EXPECT_CALL(*this, AddressCallbackInternal(false, "", ""));
 
   RunFor(base::TimeDelta::FromSeconds(4));
 }
diff --git a/chrome/utility/local_discovery/service_discovery_client_impl.cc b/chrome/utility/local_discovery/service_discovery_client_impl.cc
index 524f176..e8fc595 100644
--- a/chrome/utility/local_discovery/service_discovery_client_impl.cc
+++ b/chrome/utility/local_discovery/service_discovery_client_impl.cc
@@ -14,6 +14,12 @@
 
 namespace local_discovery {
 
+namespace {
+// TODO(noamsml): Make this configurable through the LocalDomainResolver
+// interface.
+const int kLocalDomainSecondAddressTimeoutMs = 100;
+}
+
 ServiceDiscoveryClientImpl::ServiceDiscoveryClientImpl(
     net::MDnsClient* mdns_client) : mdns_client_(mdns_client) {
 }
@@ -393,10 +399,11 @@
     const IPAddressCallback& callback,
     net::MDnsClient* mdns_client)
     : domain_(domain), address_family_(address_family), callback_(callback),
-      transaction_failures_(0), mdns_client_(mdns_client) {
+      transactions_finished_(0), mdns_client_(mdns_client) {
 }
 
 LocalDomainResolverImpl::~LocalDomainResolverImpl() {
+  timeout_callback_.Cancel();
 }
 
 void LocalDomainResolverImpl::Start() {
@@ -425,31 +432,44 @@
 
 void LocalDomainResolverImpl::OnTransactionComplete(
     net::MDnsTransaction::Result result, const net::RecordParsed* record) {
-  if (result != net::MDnsTransaction::RESULT_RECORD &&
-      address_family_ == net::ADDRESS_FAMILY_UNSPECIFIED) {
-    transaction_failures_++;
+  transactions_finished_++;
 
-    if (transaction_failures_ < 2) {
-      return;
-    }
-  }
-
-  transaction_a_.reset();
-  transaction_aaaa_.reset();
-
-  net::IPAddressNumber address;
   if (result == net::MDnsTransaction::RESULT_RECORD) {
     if (record->type() == net::dns_protocol::kTypeA) {
       const net::ARecordRdata* rdata = record->rdata<net::ARecordRdata>();
-      address = rdata->address();
+      address_ipv4_ = rdata->address();
     } else {
       DCHECK_EQ(net::dns_protocol::kTypeAAAA, record->type());
       const net::AAAARecordRdata* rdata = record->rdata<net::AAAARecordRdata>();
-      address = rdata->address();
+      address_ipv6_ = rdata->address();
     }
   }
 
-  callback_.Run(result == net::MDnsTransaction::RESULT_RECORD, address);
+  if (transactions_finished_ == 1 &&
+      address_family_ == net::ADDRESS_FAMILY_UNSPECIFIED) {
+    timeout_callback_.Reset(base::Bind(
+        &LocalDomainResolverImpl::SendResolvedAddresses,
+        base::Unretained(this)));
+
+    base::MessageLoop::current()->PostDelayedTask(
+        FROM_HERE,
+        timeout_callback_.callback(),
+        base::TimeDelta::FromMilliseconds(kLocalDomainSecondAddressTimeoutMs));
+  } else if (transactions_finished_ == 2
+      || address_family_ != net::ADDRESS_FAMILY_UNSPECIFIED) {
+    SendResolvedAddresses();
+  }
+}
+
+bool LocalDomainResolverImpl::IsSuccess() {
+  return !address_ipv4_.empty() || !address_ipv6_.empty();
+}
+
+void LocalDomainResolverImpl::SendResolvedAddresses() {
+  transaction_a_.reset();
+  transaction_aaaa_.reset();
+  timeout_callback_.Cancel();
+  callback_.Run(IsSuccess(), address_ipv4_, address_ipv6_);
 }
 
 }  // namespace local_discovery
diff --git a/chrome/utility/local_discovery/service_discovery_client_impl.h b/chrome/utility/local_discovery/service_discovery_client_impl.h
index f04f311..efe0d43 100644
--- a/chrome/utility/local_discovery/service_discovery_client_impl.h
+++ b/chrome/utility/local_discovery/service_discovery_client_impl.h
@@ -212,6 +212,10 @@
 
   scoped_ptr<net::MDnsTransaction> CreateTransaction(uint16 type);
 
+  bool IsSuccess();
+
+  void SendResolvedAddresses();
+
   std::string domain_;
   net::AddressFamily address_family_;
   IPAddressCallback callback_;
@@ -219,10 +223,15 @@
   scoped_ptr<net::MDnsTransaction> transaction_a_;
   scoped_ptr<net::MDnsTransaction> transaction_aaaa_;
 
-  int transaction_failures_;
+  int transactions_finished_;
 
   net::MDnsClient* mdns_client_;
 
+  net::IPAddressNumber address_ipv4_;
+  net::IPAddressNumber address_ipv6_;
+
+  base::CancelableCallback<void()> timeout_callback_;
+
   DISALLOW_COPY_AND_ASSIGN(LocalDomainResolverImpl);
 };
 
diff --git a/chrome/utility/local_discovery/service_discovery_message_handler.cc b/chrome/utility/local_discovery/service_discovery_message_handler.cc
index e6c84dd..d07abeb 100644
--- a/chrome/utility/local_discovery/service_discovery_message_handler.cc
+++ b/chrome/utility/local_discovery/service_discovery_message_handler.cc
@@ -7,18 +7,12 @@
 #include <algorithm>
 
 #include "base/command_line.h"
+#include "base/lazy_instance.h"
 #include "chrome/common/local_discovery/local_discovery_messages.h"
 #include "chrome/utility/local_discovery/service_discovery_client_impl.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/utility/utility_thread.h"
-
-#if defined(OS_WIN)
-
-#include "base/lazy_instance.h"
-#include "net/base/winsock_init.h"
-#include "net/base/winsock_util.h"
-
-#endif  // OS_WIN
+#include "net/socket/socket_descriptor.h"
 
 namespace local_discovery {
 
@@ -37,11 +31,8 @@
   SocketFactory()
       : socket_v4_(NULL),
         socket_v6_(NULL) {
-    net::EnsureWinsockInit();
-    socket_v4_ = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0,
-                           WSA_FLAG_OVERLAPPED);
-    socket_v6_ = WSASocket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, NULL, 0,
-                           WSA_FLAG_OVERLAPPED);
+    socket_v4_ = net::CreatePlatformSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+    socket_v6_ = net::CreatePlatformSocket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
   }
 
   void Reset() {
@@ -135,10 +126,11 @@
 }
 
 void SendLocalDomainResolved(uint64 id, bool success,
-                             const net::IPAddressNumber& address) {
+                             const net::IPAddressNumber& address_ipv4,
+                             const net::IPAddressNumber& address_ipv6) {
   content::UtilityThread::Get()->Send(
       new LocalDiscoveryHostMsg_LocalDomainResolverCallback(
-          id, success, address));
+          id, success, address_ipv4, address_ipv6));
 }
 
 }  // namespace
@@ -381,10 +373,13 @@
 void ServiceDiscoveryMessageHandler::OnLocalDomainResolved(
     uint64 id,
     bool success,
-    const net::IPAddressNumber& address) {
+    const net::IPAddressNumber& address_ipv4,
+    const net::IPAddressNumber& address_ipv6) {
   DCHECK(service_discovery_client_);
   utility_task_runner_->PostTask(FROM_HERE, base::Bind(&SendLocalDomainResolved,
-                                                       id, success, address));
+                                                       id, success,
+                                                       address_ipv4,
+                                                       address_ipv6));
 }
 
 
diff --git a/chrome/utility/local_discovery/service_discovery_message_handler.h b/chrome/utility/local_discovery/service_discovery_message_handler.h
index 35c6b03..917d525 100644
--- a/chrome/utility/local_discovery/service_discovery_message_handler.h
+++ b/chrome/utility/local_discovery/service_discovery_message_handler.h
@@ -87,7 +87,8 @@
   // Is called by LocalDomainResolver as callback.
   void OnLocalDomainResolved(uint64 id,
                              bool success,
-                             const net::IPAddressNumber& address);
+                             const net::IPAddressNumber& address_ipv4,
+                             const net::IPAddressNumber& address_ipv6);
 
   ServiceWatchers service_watchers_;
   ServiceResolvers service_resolvers_;
diff --git a/chrome/utility/media_galleries/itunes_library_parser.cc b/chrome/utility/media_galleries/itunes_library_parser.cc
index 5f14a0e..a7d49d0 100644
--- a/chrome/utility/media_galleries/itunes_library_parser.cc
+++ b/chrome/utility/media_galleries/itunes_library_parser.cc
@@ -102,7 +102,7 @@
       string16 location(decoded_location.data(), decoded_location.length());
 #else
       string16 location16(decoded_location.data(), decoded_location.length());
-      std::string location = UTF16ToUTF8(location16);
+      std::string location = "/" + UTF16ToUTF8(location16);
 #endif
       result->location = base::FilePath(location);
       found_location = true;
diff --git a/chrome/utility/media_galleries/itunes_library_parser_unittest.cc b/chrome/utility/media_galleries/itunes_library_parser_unittest.cc
index ccdeb94..cb772f4 100644
--- a/chrome/utility/media_galleries/itunes_library_parser_unittest.cc
+++ b/chrome/utility/media_galleries/itunes_library_parser_unittest.cc
@@ -33,7 +33,7 @@
 
 void CompareTrack(const parser::Track& a, const parser::Track& b) {
   EXPECT_EQ(a.id, b.id);
-  EXPECT_EQ(a.location, b.location);
+  EXPECT_EQ(a.location.value(), b.location.value());
 }
 
 void CompareAlbum(const parser::Album& a, const parser::Album& b) {
@@ -90,7 +90,13 @@
 
   void AddExpectedTrack(uint32 id, const std::string& location,
                         const std::string& artist, const std::string& album) {
-    parser::Track track(id, base::FilePath::FromUTF8Unsafe(location));
+    // On Mac this pretends that C: is a directory.
+#if defined(OS_MACOSX)
+    std::string os_location = "/" + location;
+#else
+    const std::string& os_location = location;
+#endif
+    parser::Track track(id, base::FilePath::FromUTF8Unsafe(os_location));
     expected_library_[artist][album].insert(track);
   }
 
@@ -264,6 +270,17 @@
       SIMPLE_FOOTER());
 }
 
+TEST_F(ITunesLibraryParserTest, MacPath) {
+  AddExpectedTrack(1, "dir/Song With Space.mp3", "Artist A", "Album A");
+  TestParser(
+      true,
+      SIMPLE_HEADER()
+      // This path is concatenated with "http://localhost/", so no leading
+      // slash should be used.
+      SIMPLE_TRACK(1, 1, "dir/Song%20With%20Space.mp3", "Artist A", "Album A")
+      SIMPLE_FOOTER());
+}
+
 }  // namespace
 
 }  // namespace itunes
diff --git a/chrome_frame/chrome_frame.gyp b/chrome_frame/chrome_frame.gyp
index f9d13a5..06648be 100644
--- a/chrome_frame/chrome_frame.gyp
+++ b/chrome_frame/chrome_frame.gyp
@@ -350,7 +350,7 @@
       ],
       'sources': [
         '../base/test/perf_test_suite.h',
-        '../base/perftimer.cc',
+        '../base/test/perftimer.cc',
         '../base/test/test_file_util.h',
         '../chrome/test/base/chrome_process_util.cc',
         '../chrome/test/base/chrome_process_util.h',
diff --git a/chrome_frame/chrome_tab.cc b/chrome_frame/chrome_tab.cc
index 6ddfd49..70c188b 100644
--- a/chrome_frame/chrome_tab.cc
+++ b/chrome_frame/chrome_tab.cc
@@ -29,7 +29,6 @@
 #include "base/win/windows_version.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/metrics/entropy_provider.h"
 #include "chrome/installer/util/google_update_settings.h"
 #include "chrome_frame/bho.h"
 #include "chrome_frame/chrome_active_document.h"
@@ -43,6 +42,7 @@
 #include "chrome_frame/pin_module.h"
 #include "chrome_frame/resource.h"
 #include "chrome_frame/utils.h"
+#include "components/variations/entropy_provider.h"
 #include "grit/chrome_frame_resources.h"
 #include "url/url_util.h"
 
diff --git a/chrome_frame/navigation_constraints.cc b/chrome_frame/navigation_constraints.cc
index 33f4216..92524e4 100644
--- a/chrome_frame/navigation_constraints.cc
+++ b/chrome_frame/navigation_constraints.cc
@@ -26,8 +26,7 @@
   if (!url.is_valid())
     return false;
 
-  if (url.SchemeIs(chrome::kHttpScheme) ||
-      url.SchemeIs(chrome::kHttpsScheme))
+  if (url.SchemeIs(chrome::kHttpScheme) || url.SchemeIs(content::kHttpsScheme))
     return true;
 
   // Additional checking for view-source. Allow only http and https
@@ -35,7 +34,7 @@
   if (url.SchemeIs(content::kViewSourceScheme)) {
     GURL sub_url(url.path());
     if (sub_url.SchemeIs(chrome::kHttpScheme) ||
-        sub_url.SchemeIs(chrome::kHttpsScheme))
+        sub_url.SchemeIs(content::kHttpsScheme))
       return true;
   }
 
diff --git a/chrome_frame/test/perf/chrome_frame_perftest.h b/chrome_frame/test/perf/chrome_frame_perftest.h
index 370ae5c..8209846 100644
--- a/chrome_frame/test/perf/chrome_frame_perftest.h
+++ b/chrome_frame/test/perf/chrome_frame_perftest.h
@@ -4,7 +4,7 @@
 #ifndef CHROME_FRAME_TEST_PERF_CHROME_FRAME_PERFTEST_H_
 #define CHROME_FRAME_TEST_PERF_CHROME_FRAME_PERFTEST_H_
 #include <atlbase.h>
-#include "base/perftimer.h"
+#include "base/test/perftimer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 class SimpleModule : public CAtlExeModuleT<SimpleModule> {
diff --git a/chrome_frame/test/run_all_unittests.cc b/chrome_frame/test/run_all_unittests.cc
index 97fb40b..8d42783 100644
--- a/chrome_frame/test/run_all_unittests.cc
+++ b/chrome_frame/test/run_all_unittests.cc
@@ -11,7 +11,6 @@
 #include "base/threading/platform_thread.h"
 #include "base/win/scoped_com_initializer.h"
 #include "chrome/common/chrome_paths.h"
-#include "chrome/common/metrics/entropy_provider.h"
 #include "chrome/test/logging/win/test_log_collector.h"
 #include "chrome_frame/crash_server_init.h"
 #include "chrome_frame/test/chrome_frame_test_utils.h"
@@ -20,6 +19,7 @@
 #include "chrome_frame/test/test_scrubber.h"
 #include "chrome_frame/test_utils.h"
 #include "chrome_frame/utils.h"
+#include "components/variations/entropy_provider.h"
 
 // To enable ATL-based code to run in this module
 class ChromeFrameUnittestsModule
diff --git a/chrome_frame/utils.cc b/chrome_frame/utils.cc
index 02101ed..940ab1f 100644
--- a/chrome_frame/utils.cc
+++ b/chrome_frame/utils.cc
@@ -1002,7 +1002,7 @@
     return false;
 
   if (url.SchemeIs(chrome::kHttpScheme) ||
-      url.SchemeIs(chrome::kHttpsScheme) ||
+      url.SchemeIs(content::kHttpsScheme) ||
       url.SchemeIs(chrome::kAboutScheme))
     return true;
 
@@ -1011,7 +1011,7 @@
   if (url.SchemeIs(content::kViewSourceScheme)) {
     GURL sub_url(url.path());
     if (sub_url.SchemeIs(chrome::kHttpScheme) ||
-        sub_url.SchemeIs(chrome::kHttpsScheme))
+        sub_url.SchemeIs(content::kHttpsScheme))
       return true;
     else
       return false;
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 5cfbdbe..9f57ade 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-4580.0.0
\ No newline at end of file
+4615.0.0
\ No newline at end of file
diff --git a/chromeos/chromeos.gyp b/chromeos/chromeos.gyp
index dae10d7..83cd921 100644
--- a/chromeos/chromeos.gyp
+++ b/chromeos/chromeos.gyp
@@ -233,6 +233,8 @@
         'network/managed_network_configuration_handler_impl.h',
         'network/managed_state.cc',
         'network/managed_state.h',
+	'network/network_activation_handler.cc',
+	'network/network_activation_handler.h',
         'network/network_cert_migrator.cc',
         'network/network_cert_migrator.h',
         'network/network_change_notifier_chromeos.cc',
diff --git a/chromeos/dbus/blocking_method_caller.cc b/chromeos/dbus/blocking_method_caller.cc
index 3c288f1..e4ddac5 100644
--- a/chromeos/dbus/blocking_method_caller.cc
+++ b/chromeos/dbus/blocking_method_caller.cc
@@ -5,6 +5,7 @@
 #include "chromeos/dbus/blocking_method_caller.h"
 
 #include "base/bind.h"
+#include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/threading/thread_restrictions.h"
 #include "dbus/bus.h"
diff --git a/chromeos/dbus/bluetooth_adapter_client.cc b/chromeos/dbus/bluetooth_adapter_client.cc
index c822e3a..d7c383f 100644
--- a/chromeos/dbus/bluetooth_adapter_client.cc
+++ b/chromeos/dbus/bluetooth_adapter_client.cc
@@ -51,16 +51,7 @@
     : public BluetoothAdapterClient,
       public dbus::ObjectManager::Interface {
  public:
-  explicit BluetoothAdapterClientImpl(dbus::Bus* bus)
-      : bus_(bus),
-        weak_ptr_factory_(this) {
-    object_manager_ = bus_->GetObjectManager(
-        bluetooth_object_manager::kBluetoothObjectManagerServiceName,
-        dbus::ObjectPath(
-            bluetooth_object_manager::kBluetoothObjectManagerServicePath));
-    object_manager_->RegisterInterface(
-        bluetooth_adapter::kBluetoothAdapterInterface, this);
-  }
+  BluetoothAdapterClientImpl() : weak_ptr_factory_(this) {}
 
   virtual ~BluetoothAdapterClientImpl() {
     object_manager_->UnregisterInterface(
@@ -186,6 +177,16 @@
                    weak_ptr_factory_.GetWeakPtr(), error_callback));
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    object_manager_ = bus->GetObjectManager(
+        bluetooth_object_manager::kBluetoothObjectManagerServiceName,
+        dbus::ObjectPath(
+            bluetooth_object_manager::kBluetoothObjectManagerServicePath));
+    object_manager_->RegisterInterface(
+        bluetooth_adapter::kBluetoothAdapterInterface, this);
+  }
+
  private:
   // Called by dbus::ObjectManager when an object with the adapter interface
   // is created. Informs observers.
@@ -236,7 +237,6 @@
     error_callback.Run(error_name, error_message);
   }
 
-  dbus::Bus* bus_;
   dbus::ObjectManager* object_manager_;
 
   // List of observers interested in event notifications from us.
@@ -259,10 +259,9 @@
 }
 
 BluetoothAdapterClient* BluetoothAdapterClient::Create(
-    DBusClientImplementationType type,
-    dbus::Bus* bus) {
+    DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new BluetoothAdapterClientImpl(bus);
+    return new BluetoothAdapterClientImpl();
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new FakeBluetoothAdapterClient();
 }
diff --git a/chromeos/dbus/bluetooth_adapter_client.h b/chromeos/dbus/bluetooth_adapter_client.h
index 60eaff8..5873e6b 100644
--- a/chromeos/dbus/bluetooth_adapter_client.h
+++ b/chromeos/dbus/bluetooth_adapter_client.h
@@ -12,19 +12,16 @@
 #include "base/observer_list.h"
 #include "base/values.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 #include "dbus/object_path.h"
 #include "dbus/property.h"
 
-namespace dbus {
-class Bus;
-}  // namespace dbus
-
 namespace chromeos {
 
 // BluetoothAdapterClient is used to communicate with objects representing
 // local Bluetooth Adapters.
-class CHROMEOS_EXPORT BluetoothAdapterClient {
+class CHROMEOS_EXPORT BluetoothAdapterClient : public DBusClient {
  public:
   // Structure of properties associated with bluetooth adapters.
   struct Properties : public dbus::PropertySet {
@@ -142,8 +139,7 @@
                             const ErrorCallback& error_callback) = 0;
 
   // Creates the instance.
-  static BluetoothAdapterClient* Create(DBusClientImplementationType type,
-                                        dbus::Bus* bus);
+  static BluetoothAdapterClient* Create(DBusClientImplementationType type);
 
   // Constants used to indicate exceptional error conditions.
   static const char kNoResponseError[];
diff --git a/chromeos/dbus/bluetooth_agent_manager_client.cc b/chromeos/dbus/bluetooth_agent_manager_client.cc
index 7f96406..5bf534f 100644
--- a/chromeos/dbus/bluetooth_agent_manager_client.cc
+++ b/chromeos/dbus/bluetooth_agent_manager_client.cc
@@ -22,15 +22,7 @@
 class BluetoothAgentManagerClientImpl
     : public BluetoothAgentManagerClient {
  public:
-  explicit BluetoothAgentManagerClientImpl(dbus::Bus* bus)
-      : bus_(bus),
-        weak_ptr_factory_(this) {
-    DCHECK(bus_);
-    object_proxy_ = bus_->GetObjectProxy(
-        bluetooth_agent_manager::kBluetoothAgentManagerServiceName,
-        dbus::ObjectPath(
-            bluetooth_agent_manager::kBluetoothAgentManagerServicePath));
-  }
+  BluetoothAgentManagerClientImpl() : weak_ptr_factory_(this) {}
 
   virtual ~BluetoothAgentManagerClientImpl() {
   }
@@ -99,6 +91,15 @@
                    weak_ptr_factory_.GetWeakPtr(), error_callback));
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    DCHECK(bus);
+    object_proxy_ = bus->GetObjectProxy(
+        bluetooth_agent_manager::kBluetoothAgentManagerServiceName,
+        dbus::ObjectPath(
+            bluetooth_agent_manager::kBluetoothAgentManagerServicePath));
+  }
+
  private:
   // Called when a response for successful method call is received.
   void OnSuccess(const base::Closure& callback,
@@ -124,7 +125,6 @@
     error_callback.Run(error_name, error_message);
   }
 
-  dbus::Bus* bus_;
   dbus::ObjectProxy* object_proxy_;
 
   // Weak pointer factory for generating 'this' pointers that might live longer
@@ -144,10 +144,9 @@
 }
 
 BluetoothAgentManagerClient* BluetoothAgentManagerClient::Create(
-    DBusClientImplementationType type,
-    dbus::Bus* bus) {
+    DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new BluetoothAgentManagerClientImpl(bus);
+    return new BluetoothAgentManagerClientImpl();
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new FakeBluetoothAgentManagerClient();
 }
diff --git a/chromeos/dbus/bluetooth_agent_manager_client.h b/chromeos/dbus/bluetooth_agent_manager_client.h
index acf0465..b3e481e 100644
--- a/chromeos/dbus/bluetooth_agent_manager_client.h
+++ b/chromeos/dbus/bluetooth_agent_manager_client.h
@@ -11,18 +11,15 @@
 #include "base/callback.h"
 #include "base/values.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 #include "dbus/object_path.h"
 
-namespace dbus {
-class Bus;
-}  // namespace dbus
-
 namespace chromeos {
 
 // BluetoothAgentManagerClient is used to communicate with the agent manager
 // object of the Bluetooth daemon.
-class CHROMEOS_EXPORT BluetoothAgentManagerClient {
+class CHROMEOS_EXPORT BluetoothAgentManagerClient : public DBusClient {
  public:
   virtual ~BluetoothAgentManagerClient();
 
@@ -54,9 +51,8 @@
                                    const base::Closure& callback,
                                    const ErrorCallback& error_callback) = 0;
 
- // Creates the instance.
-  static BluetoothAgentManagerClient* Create(DBusClientImplementationType type,
-                                             dbus::Bus* bus);
+  // Creates the instance.
+  static BluetoothAgentManagerClient* Create(DBusClientImplementationType type);
 
   // Constants used to indicate exceptional error conditions.
   static const char kNoResponseError[];
diff --git a/chromeos/dbus/bluetooth_device_client.cc b/chromeos/dbus/bluetooth_device_client.cc
index 62fbab8..605872c 100644
--- a/chromeos/dbus/bluetooth_device_client.cc
+++ b/chromeos/dbus/bluetooth_device_client.cc
@@ -53,16 +53,7 @@
     : public BluetoothDeviceClient,
       public dbus::ObjectManager::Interface {
  public:
-  explicit BluetoothDeviceClientImpl(dbus::Bus* bus)
-      : bus_(bus),
-        weak_ptr_factory_(this) {
-    object_manager_ = bus_->GetObjectManager(
-        bluetooth_object_manager::kBluetoothObjectManagerServiceName,
-        dbus::ObjectPath(
-            bluetooth_object_manager::kBluetoothObjectManagerServicePath));
-    object_manager_->RegisterInterface(
-        bluetooth_device::kBluetoothDeviceInterface, this);
-  }
+  BluetoothDeviceClientImpl() : weak_ptr_factory_(this) {}
 
   virtual ~BluetoothDeviceClientImpl() {
     object_manager_->UnregisterInterface(
@@ -277,6 +268,16 @@
                    weak_ptr_factory_.GetWeakPtr(), error_callback));
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    object_manager_ = bus->GetObjectManager(
+        bluetooth_object_manager::kBluetoothObjectManagerServiceName,
+        dbus::ObjectPath(
+            bluetooth_object_manager::kBluetoothObjectManagerServicePath));
+    object_manager_->RegisterInterface(
+        bluetooth_device::kBluetoothDeviceInterface, this);
+  }
+
  private:
   // Called by dbus::ObjectManager when an object with the device interface
   // is created. Informs observers.
@@ -327,7 +328,6 @@
     error_callback.Run(error_name, error_message);
   }
 
-  dbus::Bus* bus_;
   dbus::ObjectManager* object_manager_;
 
   // List of observers interested in event notifications from us.
@@ -349,10 +349,9 @@
 }
 
 BluetoothDeviceClient* BluetoothDeviceClient::Create(
-    DBusClientImplementationType type,
-    dbus::Bus* bus) {
+    DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new BluetoothDeviceClientImpl(bus);
+    return new BluetoothDeviceClientImpl();
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new FakeBluetoothDeviceClient();
 }
diff --git a/chromeos/dbus/bluetooth_device_client.h b/chromeos/dbus/bluetooth_device_client.h
index a0066e0..fcb0483 100644
--- a/chromeos/dbus/bluetooth_device_client.h
+++ b/chromeos/dbus/bluetooth_device_client.h
@@ -12,19 +12,16 @@
 #include "base/observer_list.h"
 #include "base/values.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 #include "dbus/object_path.h"
 #include "dbus/property.h"
 
-namespace dbus {
-class Bus;
-}  // namespace dbus
-
 namespace chromeos {
 
 // BluetoothDeviceClient is used to communicate with objects representing
 // remote Bluetooth Devices.
-class CHROMEOS_EXPORT BluetoothDeviceClient {
+class CHROMEOS_EXPORT BluetoothDeviceClient : public DBusClient {
  public:
   // Structure of properties associated with bluetooth devices.
   struct Properties : public dbus::PropertySet {
@@ -174,8 +171,7 @@
                              const ErrorCallback& error_callback) = 0;
 
   // Creates the instance.
-  static BluetoothDeviceClient* Create(DBusClientImplementationType type,
-                                       dbus::Bus* bus);
+  static BluetoothDeviceClient* Create(DBusClientImplementationType type);
 
   // Constants used to indicate exceptional error conditions.
   static const char kNoResponseError[];
diff --git a/chromeos/dbus/bluetooth_input_client.cc b/chromeos/dbus/bluetooth_input_client.cc
index 0d1eb53..ddbc7a4 100644
--- a/chromeos/dbus/bluetooth_input_client.cc
+++ b/chromeos/dbus/bluetooth_input_client.cc
@@ -35,16 +35,7 @@
     : public BluetoothInputClient,
       public dbus::ObjectManager::Interface {
  public:
-  explicit BluetoothInputClientImpl(dbus::Bus* bus)
-      : bus_(bus),
-        weak_ptr_factory_(this) {
-    object_manager_ = bus_->GetObjectManager(
-        bluetooth_object_manager::kBluetoothObjectManagerServiceName,
-        dbus::ObjectPath(
-            bluetooth_object_manager::kBluetoothObjectManagerServicePath));
-    object_manager_->RegisterInterface(
-        bluetooth_input::kBluetoothInputInterface, this);
-  }
+  BluetoothInputClientImpl() : weak_ptr_factory_(this) {}
 
   virtual ~BluetoothInputClientImpl() {
     object_manager_->UnregisterInterface(
@@ -88,6 +79,16 @@
             bluetooth_input::kBluetoothInputInterface));
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    object_manager_ = bus->GetObjectManager(
+        bluetooth_object_manager::kBluetoothObjectManagerServiceName,
+        dbus::ObjectPath(
+            bluetooth_object_manager::kBluetoothObjectManagerServicePath));
+    object_manager_->RegisterInterface(
+        bluetooth_input::kBluetoothInputInterface, this);
+  }
+
  private:
   // Called by dbus::ObjectManager when an object with the input interface
   // is created. Informs observers.
@@ -114,7 +115,6 @@
                       InputPropertyChanged(object_path, property_name));
   }
 
-  dbus::Bus* bus_;
   dbus::ObjectManager* object_manager_;
 
   // List of observers interested in event notifications from us.
@@ -136,10 +136,9 @@
 }
 
 BluetoothInputClient* BluetoothInputClient::Create(
-    DBusClientImplementationType type,
-    dbus::Bus* bus) {
+    DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new BluetoothInputClientImpl(bus);
+    return new BluetoothInputClientImpl();
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new FakeBluetoothInputClient();
 }
diff --git a/chromeos/dbus/bluetooth_input_client.h b/chromeos/dbus/bluetooth_input_client.h
index d4d25d3..0e0bee5 100644
--- a/chromeos/dbus/bluetooth_input_client.h
+++ b/chromeos/dbus/bluetooth_input_client.h
@@ -11,19 +11,16 @@
 #include "base/callback.h"
 #include "base/observer_list.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 #include "dbus/object_path.h"
 #include "dbus/property.h"
 
-namespace dbus {
-class Bus;
-}  // namespace dbus
-
 namespace chromeos {
 
 // BluetoothInputClient is used to communicate with objects representing
 // Bluetooth Input (HID) devices.
-class CHROMEOS_EXPORT BluetoothInputClient {
+class CHROMEOS_EXPORT BluetoothInputClient : public DBusClient {
  public:
   // Structure of properties associated with bluetooth input devices.
   struct Properties : public dbus::PropertySet {
@@ -71,8 +68,7 @@
   virtual Properties* GetProperties(const dbus::ObjectPath& object_path) = 0;
 
   // Creates the instance.
-  static BluetoothInputClient* Create(DBusClientImplementationType type,
-                                      dbus::Bus* bus);
+  static BluetoothInputClient* Create(DBusClientImplementationType type);
 
  protected:
   BluetoothInputClient();
diff --git a/chromeos/dbus/bluetooth_profile_manager_client.cc b/chromeos/dbus/bluetooth_profile_manager_client.cc
index 6f2875b..aebd0d8 100644
--- a/chromeos/dbus/bluetooth_profile_manager_client.cc
+++ b/chromeos/dbus/bluetooth_profile_manager_client.cc
@@ -33,15 +33,7 @@
 class BluetoothProfileManagerClientImpl
     : public BluetoothProfileManagerClient {
  public:
-  explicit BluetoothProfileManagerClientImpl(dbus::Bus* bus)
-      : bus_(bus),
-        weak_ptr_factory_(this) {
-    DCHECK(bus_);
-    object_proxy_ = bus_->GetObjectProxy(
-        bluetooth_profile_manager::kBluetoothProfileManagerServiceName,
-        dbus::ObjectPath(
-            bluetooth_profile_manager::kBluetoothProfileManagerServicePath));
-  }
+  BluetoothProfileManagerClientImpl() : weak_ptr_factory_(this) {}
 
   virtual ~BluetoothProfileManagerClientImpl() {
   }
@@ -191,6 +183,15 @@
                    weak_ptr_factory_.GetWeakPtr(), error_callback));
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    DCHECK(bus);
+    object_proxy_ = bus->GetObjectProxy(
+        bluetooth_profile_manager::kBluetoothProfileManagerServiceName,
+        dbus::ObjectPath(
+            bluetooth_profile_manager::kBluetoothProfileManagerServicePath));
+  }
+
  private:
   // Called when a response for successful method call is received.
   void OnSuccess(const base::Closure& callback,
@@ -216,7 +217,6 @@
     error_callback.Run(error_name, error_message);
   }
 
-  dbus::Bus* bus_;
   dbus::ObjectProxy* object_proxy_;
 
   // Weak pointer factory for generating 'this' pointers that might live longer
@@ -235,10 +235,9 @@
 }
 
 BluetoothProfileManagerClient* BluetoothProfileManagerClient::Create(
-    DBusClientImplementationType type,
-    dbus::Bus* bus) {
+    DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new BluetoothProfileManagerClientImpl(bus);
+    return new BluetoothProfileManagerClientImpl();
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new FakeBluetoothProfileManagerClient();
 }
diff --git a/chromeos/dbus/bluetooth_profile_manager_client.h b/chromeos/dbus/bluetooth_profile_manager_client.h
index bcf92dd..771fbaf 100644
--- a/chromeos/dbus/bluetooth_profile_manager_client.h
+++ b/chromeos/dbus/bluetooth_profile_manager_client.h
@@ -11,18 +11,15 @@
 #include "base/callback.h"
 #include "base/values.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 #include "dbus/object_path.h"
 
-namespace dbus {
-class Bus;
-}  // namespace dbus
-
 namespace chromeos {
 
 // BluetoothProfileManagerClient is used to communicate with the profile
 // manager object of the Bluetooth daemon.
-class CHROMEOS_EXPORT BluetoothProfileManagerClient {
+class CHROMEOS_EXPORT BluetoothProfileManagerClient : public DBusClient {
  public:
   // Species the role of the object within the profile. SYMMETRIC should be
   // usually used unless the profile requires you specify as a CLIENT or as a
@@ -99,8 +96,7 @@
 
   // Creates the instance.
   static BluetoothProfileManagerClient* Create(
-      DBusClientImplementationType type,
-      dbus::Bus* bus);
+      DBusClientImplementationType type);
 
   // Constants used to indicate exceptional error conditions.
   static const char kNoResponseError[];
diff --git a/chromeos/dbus/cras_audio_client.cc b/chromeos/dbus/cras_audio_client.cc
index 411314e..7a0b1c3 100644
--- a/chromeos/dbus/cras_audio_client.cc
+++ b/chromeos/dbus/cras_audio_client.cc
@@ -19,63 +19,7 @@
 // The CrasAudioClient implementation used in production.
 class CrasAudioClientImpl : public CrasAudioClient {
  public:
-  explicit CrasAudioClientImpl(dbus::Bus* bus)
-      : cras_proxy_(NULL),
-        weak_ptr_factory_(this) {
-    cras_proxy_ = bus->GetObjectProxy(
-        cras::kCrasServiceName,
-        dbus::ObjectPath(cras::kCrasServicePath));
-
-    // Monitor NameOwnerChanged signal.
-    cras_proxy_->SetNameOwnerChangedCallback(
-        base::Bind(&CrasAudioClientImpl::NameOwnerChangedReceived,
-                   weak_ptr_factory_.GetWeakPtr()));
-
-    // Monitor the D-Bus signal for output mute change.
-    cras_proxy_->ConnectToSignal(
-        cras::kCrasControlInterface,
-        cras::kOutputMuteChanged,
-        base::Bind(&CrasAudioClientImpl::OutputMuteChangedReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&CrasAudioClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-
-    // Monitor the D-Bus signal for input mute change.
-    cras_proxy_->ConnectToSignal(
-        cras::kCrasControlInterface,
-        cras::kInputMuteChanged,
-        base::Bind(&CrasAudioClientImpl::InputMuteChangedReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&CrasAudioClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-
-    // Monitor the D-Bus signal for nodes change.
-    cras_proxy_->ConnectToSignal(
-        cras::kCrasControlInterface,
-        cras::kNodesChanged,
-        base::Bind(&CrasAudioClientImpl::NodesChangedReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&CrasAudioClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-
-    // Monitor the D-Bus signal for active output node change.
-    cras_proxy_->ConnectToSignal(
-        cras::kCrasControlInterface,
-        cras::kActiveOutputNodeChanged,
-        base::Bind(&CrasAudioClientImpl::ActiveOutputNodeChangedReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&CrasAudioClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-
-    // Monitor the D-Bus signal for active input node change.
-    cras_proxy_->ConnectToSignal(
-        cras::kCrasControlInterface,
-        cras::kActiveInputNodeChanged,
-        base::Bind(&CrasAudioClientImpl::ActiveInputNodeChangedReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&CrasAudioClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-  }
+  CrasAudioClientImpl() : cras_proxy_(NULL), weak_ptr_factory_(this) {}
 
   virtual ~CrasAudioClientImpl() {
   }
@@ -181,6 +125,62 @@
         dbus::ObjectProxy::EmptyResponseCallback());
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    cras_proxy_ = bus->GetObjectProxy(cras::kCrasServiceName,
+                                      dbus::ObjectPath(cras::kCrasServicePath));
+
+    // Monitor NameOwnerChanged signal.
+    cras_proxy_->SetNameOwnerChangedCallback(
+        base::Bind(&CrasAudioClientImpl::NameOwnerChangedReceived,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    // Monitor the D-Bus signal for output mute change.
+    cras_proxy_->ConnectToSignal(
+        cras::kCrasControlInterface,
+        cras::kOutputMuteChanged,
+        base::Bind(&CrasAudioClientImpl::OutputMuteChangedReceived,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&CrasAudioClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    // Monitor the D-Bus signal for input mute change.
+    cras_proxy_->ConnectToSignal(
+        cras::kCrasControlInterface,
+        cras::kInputMuteChanged,
+        base::Bind(&CrasAudioClientImpl::InputMuteChangedReceived,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&CrasAudioClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    // Monitor the D-Bus signal for nodes change.
+    cras_proxy_->ConnectToSignal(
+        cras::kCrasControlInterface,
+        cras::kNodesChanged,
+        base::Bind(&CrasAudioClientImpl::NodesChangedReceived,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&CrasAudioClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    // Monitor the D-Bus signal for active output node change.
+    cras_proxy_->ConnectToSignal(
+        cras::kCrasControlInterface,
+        cras::kActiveOutputNodeChanged,
+        base::Bind(&CrasAudioClientImpl::ActiveOutputNodeChangedReceived,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&CrasAudioClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    // Monitor the D-Bus signal for active input node change.
+    cras_proxy_->ConnectToSignal(
+        cras::kCrasControlInterface,
+        cras::kActiveInputNodeChanged,
+        base::Bind(&CrasAudioClientImpl::ActiveInputNodeChangedReceived,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&CrasAudioClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+  }
+
  private:
   // Called when the cras signal is initially connected.
   void SignalConnected(const std::string& interface_name,
@@ -379,11 +379,9 @@
 }
 
 // static
-CrasAudioClient* CrasAudioClient::Create(
-    DBusClientImplementationType type,
-    dbus::Bus* bus) {
+CrasAudioClient* CrasAudioClient::Create(DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION) {
-    return new CrasAudioClientImpl(bus);
+    return new CrasAudioClientImpl();
   }
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new CrasAudioClientStubImpl();
diff --git a/chromeos/dbus/cras_audio_client.h b/chromeos/dbus/cras_audio_client.h
index f056099..23063d8 100644
--- a/chromeos/dbus/cras_audio_client.h
+++ b/chromeos/dbus/cras_audio_client.h
@@ -9,17 +9,14 @@
 #include "base/observer_list.h"
 #include "chromeos/chromeos_export.h"
 #include "chromeos/dbus/audio_node.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 #include "chromeos/dbus/volume_state.h"
 
-namespace dbus {
-class Bus;
-}
-
 namespace chromeos {
 
 // CrasAudioClient is used to communicate with the cras audio dbus interface.
-class CHROMEOS_EXPORT CrasAudioClient {
+class CHROMEOS_EXPORT CrasAudioClient : public DBusClient {
  public:
   // Interface for observing changes from the cras audio changes.
   class Observer {
@@ -93,8 +90,7 @@
   virtual void SetActiveInputNode(uint64 node_id) = 0;
 
   // Creates the instance.
-  static CrasAudioClient* Create(DBusClientImplementationType type,
-                                 dbus::Bus* bus);
+  static CrasAudioClient* Create(DBusClientImplementationType type);
 
  protected:
   // Create() should be used instead.
diff --git a/chromeos/dbus/cras_audio_client_stub_impl.cc b/chromeos/dbus/cras_audio_client_stub_impl.cc
index 4efa107..6c7a155 100644
--- a/chromeos/dbus/cras_audio_client_stub_impl.cc
+++ b/chromeos/dbus/cras_audio_client_stub_impl.cc
@@ -6,7 +6,9 @@
 
 namespace chromeos {
 
-CrasAudioClientStubImpl::CrasAudioClientStubImpl() {
+CrasAudioClientStubImpl::CrasAudioClientStubImpl() {}
+
+void CrasAudioClientStubImpl::Init(dbus::Bus* bus) {
   VLOG(1) << "CrasAudioClientStubImpl is created";
 
   // Fake audio output nodes.
diff --git a/chromeos/dbus/cras_audio_client_stub_impl.h b/chromeos/dbus/cras_audio_client_stub_impl.h
index 032305f..0f57f02 100644
--- a/chromeos/dbus/cras_audio_client_stub_impl.h
+++ b/chromeos/dbus/cras_audio_client_stub_impl.h
@@ -17,7 +17,8 @@
   CrasAudioClientStubImpl();
   virtual ~CrasAudioClientStubImpl();
 
-  // CrasAudioClient overrides:
+  // CrasAudioClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void AddObserver(Observer* observer) OVERRIDE;
   virtual void RemoveObserver(Observer* observer) OVERRIDE;
   virtual bool HasObserver(Observer* observer) OVERRIDE;
diff --git a/chromeos/dbus/cros_disks_client.cc b/chromeos/dbus/cros_disks_client.cc
index 84ae312..7233786 100644
--- a/chromeos/dbus/cros_disks_client.cc
+++ b/chromeos/dbus/cros_disks_client.cc
@@ -79,12 +79,7 @@
 // The CrosDisksClient implementation.
 class CrosDisksClientImpl : public CrosDisksClient {
  public:
-  explicit CrosDisksClientImpl(dbus::Bus* bus)
-      : proxy_(bus->GetObjectProxy(
-          cros_disks::kCrosDisksServiceName,
-          dbus::ObjectPath(cros_disks::kCrosDisksServicePath))),
-        weak_ptr_factory_(this) {
-  }
+  CrosDisksClientImpl() : proxy_(NULL), weak_ptr_factory_(this) {}
 
   // CrosDisksClient override.
   virtual void Mount(const std::string& source_path,
@@ -223,6 +218,13 @@
                    weak_ptr_factory_.GetWeakPtr()));
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    proxy_ = bus->GetObjectProxy(
+        cros_disks::kCrosDisksServiceName,
+        dbus::ObjectPath(cros_disks::kCrosDisksServicePath));
+  }
+
  private:
   // A struct to contain a pair of signal name and mount event type.
   // Used by SetUpConnections.
@@ -363,6 +365,7 @@
   virtual ~CrosDisksClientStubImpl() {}
 
   // CrosDisksClient overrides:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {}
   virtual void Mount(const std::string& source_path,
                      const std::string& source_format,
                      const std::string& mount_label,
@@ -693,10 +696,9 @@
 CrosDisksClient::~CrosDisksClient() {}
 
 // static
-CrosDisksClient* CrosDisksClient::Create(DBusClientImplementationType type,
-                                         dbus::Bus* bus) {
+CrosDisksClient* CrosDisksClient::Create(DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new CrosDisksClientImpl(bus);
+    return new CrosDisksClientImpl();
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new CrosDisksClientStubImpl();
 }
diff --git a/chromeos/dbus/cros_disks_client.h b/chromeos/dbus/cros_disks_client.h
index 4d65b2c..c477da1 100644
--- a/chromeos/dbus/cros_disks_client.h
+++ b/chromeos/dbus/cros_disks_client.h
@@ -11,6 +11,7 @@
 #include "base/basictypes.h"
 #include "base/callback_forward.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 
 namespace base {
@@ -18,7 +19,6 @@
 }
 
 namespace dbus {
-class Bus;
 class Response;
 }
 
@@ -190,7 +190,7 @@
 // A class to make the actual DBus calls for cros-disks service.
 // This class only makes calls, result/error handling should be done
 // by callbacks.
-class CHROMEOS_EXPORT CrosDisksClient {
+class CHROMEOS_EXPORT CrosDisksClient : public DBusClient {
  public:
   // A callback to handle the result of EnumerateAutoMountableDevices.
   // The argument is the enumerated device paths.
@@ -276,8 +276,7 @@
 
   // Factory function, creates a new instance and returns ownership.
   // For normal usage, access the singleton via DBusThreadManager::Get().
-  static CrosDisksClient* Create(DBusClientImplementationType type,
-                                 dbus::Bus* bus);
+  static CrosDisksClient* Create(DBusClientImplementationType type);
 
   // Returns the path of the mount point for archive files.
   static base::FilePath GetArchiveMountPoint();
diff --git a/chromeos/dbus/cryptohome_client.cc b/chromeos/dbus/cryptohome_client.cc
index 965ef92..c117f58 100644
--- a/chromeos/dbus/cryptohome_client.cc
+++ b/chromeos/dbus/cryptohome_client.cc
@@ -28,27 +28,7 @@
 // The CryptohomeClient implementation.
 class CryptohomeClientImpl : public CryptohomeClient {
  public:
-  explicit CryptohomeClientImpl(dbus::Bus* bus)
-      : proxy_(bus->GetObjectProxy(
-            cryptohome::kCryptohomeServiceName,
-            dbus::ObjectPath(cryptohome::kCryptohomeServicePath))),
-        blocking_method_caller_(bus, proxy_),
-        weak_ptr_factory_(this) {
-    proxy_->ConnectToSignal(
-        cryptohome::kCryptohomeInterface,
-        cryptohome::kSignalAsyncCallStatus,
-        base::Bind(&CryptohomeClientImpl::OnAsyncCallStatus,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&CryptohomeClientImpl::OnSignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-    proxy_->ConnectToSignal(
-        cryptohome::kCryptohomeInterface,
-        cryptohome::kSignalAsyncCallStatusWithData,
-        base::Bind(&CryptohomeClientImpl::OnAsyncCallStatusWithData,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&CryptohomeClientImpl::OnSignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-  }
+  CryptohomeClientImpl() : proxy_(NULL), weak_ptr_factory_(this) {}
 
   // CryptohomeClient override.
   virtual void SetAsyncCallStatusHandlers(
@@ -128,7 +108,7 @@
     dbus::MethodCall method_call(cryptohome::kCryptohomeInterface,
                                  cryptohome::kCryptohomeGetSystemSalt);
     scoped_ptr<dbus::Response> response(
-        blocking_method_caller_.CallMethodAndBlock(&method_call));
+        blocking_method_caller_->CallMethodAndBlock(&method_call));
     if (!response.get())
       return false;
     dbus::MessageReader reader(response.get());
@@ -163,7 +143,7 @@
     writer.AppendString(username);
 
     scoped_ptr<dbus::Response> response =
-        blocking_method_caller_.CallMethodAndBlock(&method_call);
+        blocking_method_caller_->CallMethodAndBlock(&method_call);
 
     std::string sanitized_username;
     if (response) {
@@ -324,7 +304,7 @@
     dbus::MethodCall method_call(cryptohome::kCryptohomeInterface,
                                  cryptohome::kCryptohomeTpmClearStoredPassword);
     scoped_ptr<dbus::Response> response(
-        blocking_method_caller_.CallMethodAndBlock(&method_call));
+        blocking_method_caller_->CallMethodAndBlock(&method_call));
     return response.get() != NULL;
   }
 
@@ -358,7 +338,7 @@
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(name);
     scoped_ptr<dbus::Response> response(
-        blocking_method_caller_.CallMethodAndBlock(&method_call));
+        blocking_method_caller_->CallMethodAndBlock(&method_call));
     if (!response.get())
       return false;
     dbus::MessageReader reader(response.get());
@@ -661,6 +641,29 @@
     CallBoolMethod(&method_call, callback);
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    proxy_ = bus->GetObjectProxy(
+        cryptohome::kCryptohomeServiceName,
+        dbus::ObjectPath(cryptohome::kCryptohomeServicePath));
+
+    blocking_method_caller_.reset(new BlockingMethodCaller(bus, proxy_));
+
+    proxy_->ConnectToSignal(cryptohome::kCryptohomeInterface,
+                            cryptohome::kSignalAsyncCallStatus,
+                            base::Bind(&CryptohomeClientImpl::OnAsyncCallStatus,
+                                       weak_ptr_factory_.GetWeakPtr()),
+                            base::Bind(&CryptohomeClientImpl::OnSignalConnected,
+                                       weak_ptr_factory_.GetWeakPtr()));
+    proxy_->ConnectToSignal(
+        cryptohome::kCryptohomeInterface,
+        cryptohome::kSignalAsyncCallStatusWithData,
+        base::Bind(&CryptohomeClientImpl::OnAsyncCallStatusWithData,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&CryptohomeClientImpl::OnSignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+  }
+
  private:
   // Handles the result of AsyncXXX methods.
   void OnAsyncMethodCall(const AsyncMethodCallback& callback,
@@ -698,7 +701,7 @@
   bool CallBoolMethodAndBlock(dbus::MethodCall* method_call,
                               bool* result) {
     scoped_ptr<dbus::Response> response(
-        blocking_method_caller_.CallMethodAndBlock(method_call));
+        blocking_method_caller_->CallMethodAndBlock(method_call));
     if (!response.get())
       return false;
     dbus::MessageReader reader(response.get());
@@ -829,7 +832,7 @@
   }
 
   dbus::ObjectProxy* proxy_;
-  BlockingMethodCaller blocking_method_caller_;
+  scoped_ptr<BlockingMethodCaller> blocking_method_caller_;
   AsyncCallStatusHandler async_call_status_handler_;
   AsyncCallStatusWithDataHandler async_call_status_data_handler_;
 
@@ -850,10 +853,9 @@
 CryptohomeClient::~CryptohomeClient() {}
 
 // static
-CryptohomeClient* CryptohomeClient::Create(DBusClientImplementationType type,
-                                           dbus::Bus* bus) {
+CryptohomeClient* CryptohomeClient::Create(DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new CryptohomeClientImpl(bus);
+    return new CryptohomeClientImpl();
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new CryptohomeClientStubImpl();
 }
diff --git a/chromeos/dbus/cryptohome_client.h b/chromeos/dbus/cryptohome_client.h
index 38d4880..b1019c5 100644
--- a/chromeos/dbus/cryptohome_client.h
+++ b/chromeos/dbus/cryptohome_client.h
@@ -12,19 +12,16 @@
 #include "base/callback.h"
 #include "chromeos/attestation/attestation_constants.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 #include "chromeos/dbus/dbus_method_call_status.h"
 
-namespace dbus {
-class Bus;
-}
-
 namespace chromeos {
 
 // CryptohomeClient is used to communicate with the Cryptohome service.
 // All method should be called from the origin thread (UI thread) which
 // initializes the DBusThreadManager instance.
-class CHROMEOS_EXPORT CryptohomeClient {
+class CHROMEOS_EXPORT CryptohomeClient : public DBusClient {
  public:
   // A callback to handle AsyncCallStatus signals.
   typedef base::Callback<void(int async_id,
@@ -52,8 +49,7 @@
 
   // Factory function, creates a new instance and returns ownership.
   // For normal usage, access the singleton via DBusThreadManager::Get().
-  static CryptohomeClient* Create(DBusClientImplementationType type,
-                                  dbus::Bus* bus);
+  static CryptohomeClient* Create(DBusClientImplementationType type);
 
   // Returns the sanitized |username| that the stub implementation would return.
   static std::string GetStubSanitizedUsername(const std::string& username);
diff --git a/chromeos/dbus/cryptohome_client_stub.cc b/chromeos/dbus/cryptohome_client_stub.cc
index 9942538..eee96e4 100644
--- a/chromeos/dbus/cryptohome_client_stub.cc
+++ b/chromeos/dbus/cryptohome_client_stub.cc
@@ -20,6 +20,9 @@
 
 CryptohomeClientStubImpl::~CryptohomeClientStubImpl() {}
 
+void CryptohomeClientStubImpl::Init(dbus::Bus* bus) {
+}
+
 void CryptohomeClientStubImpl::SetAsyncCallStatusHandlers(
     const AsyncCallStatusHandler& handler,
     const AsyncCallStatusWithDataHandler& data_handler) {
diff --git a/chromeos/dbus/cryptohome_client_stub.h b/chromeos/dbus/cryptohome_client_stub.h
index 8192600..0e90f88 100644
--- a/chromeos/dbus/cryptohome_client_stub.h
+++ b/chromeos/dbus/cryptohome_client_stub.h
@@ -13,11 +13,12 @@
 
 namespace chromeos {
 
-class CryptohomeClientStubImpl : public CryptohomeClient {
+class CHROMEOS_EXPORT CryptohomeClientStubImpl : public CryptohomeClient {
  public:
   CryptohomeClientStubImpl();
   virtual ~CryptohomeClientStubImpl();
 
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void SetAsyncCallStatusHandlers(
       const AsyncCallStatusHandler& handler,
       const AsyncCallStatusWithDataHandler& data_handler) OVERRIDE;
diff --git a/chromeos/dbus/dbus_client.h b/chromeos/dbus/dbus_client.h
new file mode 100644
index 0000000..902d533
--- /dev/null
+++ b/chromeos/dbus/dbus_client.h
@@ -0,0 +1,34 @@
+// 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 CHROMEOS_DBUS_DBUS_CLIENT_H_
+#define CHROMEOS_DBUS_DBUS_CLIENT_H_
+
+namespace dbus {
+class Bus;
+};
+
+namespace chromeos {
+
+// Interface for all DBus clients handled by DBusThreadManager. It restricts
+// access to the Init function to DBusThreadManagerImpl only to prevent
+// incorrect calls. Stub clients may lift that restriction however.
+class DBusClient {
+ protected:
+  friend class DBusThreadManagerImpl;
+
+  virtual ~DBusClient() {}
+
+  // This function is called by DBusThreadManager. Only in unit tests, which
+  // don't use DBusThreadManager, this function can be called through Stub
+  // implementations (they change Init's member visibility to public).
+  virtual void Init(dbus::Bus* bus) = 0;
+
+ private:
+  DISALLOW_ASSIGN(DBusClient);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_DBUS_DBUS_CLIENT_H_
diff --git a/chromeos/dbus/dbus_thread_manager.cc b/chromeos/dbus/dbus_thread_manager.cc
index 70ed816..b3159bc 100644
--- a/chromeos/dbus/dbus_thread_manager.cc
+++ b/chromeos/dbus/dbus_thread_manager.cc
@@ -33,7 +33,6 @@
 #include "chromeos/dbus/ibus/ibus_engine_factory_service.h"
 #include "chromeos/dbus/ibus/ibus_engine_service.h"
 #include "chromeos/dbus/ibus/ibus_input_context_client.h"
-#include "chromeos/dbus/ibus/ibus_panel_service.h"
 #include "chromeos/dbus/image_burner_client.h"
 #include "chromeos/dbus/introspectable_client.h"
 #include "chromeos/dbus/modem_messaging_client.h"
@@ -54,14 +53,13 @@
 // The DBusThreadManager implementation used in production.
 class DBusThreadManagerImpl : public DBusThreadManager {
  public:
-  explicit DBusThreadManagerImpl(DBusClientImplementationType client_type)
-      : client_type_(client_type),
-        client_type_override_(client_type) {
+  explicit DBusThreadManagerImpl(DBusClientImplementationType client_type) {
+    DBusClientImplementationType client_type_override = client_type;
     // If --dbus-stub was requested, pass STUB to specific components;
     // Many components like login are not useful with a stub implementation.
     if (CommandLine::ForCurrentProcess()->HasSwitch(
             chromeos::switches::kDbusStub)) {
-      client_type_override_ = STUB_DBUS_CLIENT_IMPLEMENTATION;
+      client_type_override = STUB_DBUS_CLIENT_IMPLEMENTATION;
     }
 
     // Create the D-Bus thread.
@@ -76,46 +74,34 @@
     system_bus_options.connection_type = dbus::Bus::PRIVATE;
     system_bus_options.dbus_task_runner = dbus_thread_->message_loop_proxy();
     system_bus_ = new dbus::Bus(system_bus_options);
+
+    CreateDefaultClients(client_type, client_type_override);
   }
 
   // InitializeClients gets called after g_dbus_thread_manager is set.
-  // NOTE: Clients that access other clients in their constructor must be
-  // construced in the correct order.
+  // NOTE: Clients that access other clients in their Init() must be
+  // initialized in the correct order. This is the only place where Clients'
+  // Init() should be called if DBusThreadManager is being used.
   void InitializeClients() {
-    bluetooth_adapter_client_.reset(
-        BluetoothAdapterClient::Create(client_type_, system_bus_.get()));
-    bluetooth_agent_manager_client_.reset(
-        BluetoothAgentManagerClient::Create(client_type_, system_bus_.get()));
-    bluetooth_device_client_.reset(
-        BluetoothDeviceClient::Create(client_type_, system_bus_.get()));
-    bluetooth_input_client_.reset(
-        BluetoothInputClient::Create(client_type_, system_bus_.get()));
-    bluetooth_profile_manager_client_.reset(
-        BluetoothProfileManagerClient::Create(client_type_, system_bus_.get()));
-    cras_audio_client_.reset(CrasAudioClient::Create(
-        client_type_, system_bus_.get()));
-    cros_disks_client_.reset(
-        CrosDisksClient::Create(client_type_, system_bus_.get()));
-    cryptohome_client_.reset(
-        CryptohomeClient::Create(client_type_, system_bus_.get()));
-    debug_daemon_client_.reset(
-        DebugDaemonClient::Create(client_type_, system_bus_.get()));
+    InitClient(bluetooth_adapter_client_.get());
+    InitClient(bluetooth_agent_manager_client_.get());
+    InitClient(bluetooth_device_client_.get());
+    InitClient(bluetooth_input_client_.get());
+    InitClient(bluetooth_profile_manager_client_.get());
+    InitClient(cras_audio_client_.get());
+    InitClient(cros_disks_client_.get());
+    InitClient(cryptohome_client_.get());
+    InitClient(debug_daemon_client_.get());
 
-    // Construction order of the Stub implementations of the Shill clients
-    // matters; stub clients may only have construction dependencies on clients
-    // previously constructed.
-    shill_manager_client_.reset(
-        ShillManagerClient::Create(client_type_override_, system_bus_.get()));
-    shill_device_client_.reset(
-        ShillDeviceClient::Create(client_type_override_, system_bus_.get()));
-    shill_ipconfig_client_.reset(
-        ShillIPConfigClient::Create(client_type_override_, system_bus_.get()));
-    shill_service_client_.reset(
-        ShillServiceClient::Create(client_type_override_, system_bus_.get()));
-    shill_profile_client_.reset(
-        ShillProfileClient::Create(client_type_override_, system_bus_.get()));
-    gsm_sms_client_.reset(
-        GsmSMSClient::Create(client_type_override_, system_bus_.get()));
+    // Initialization order of the Stub implementations of the Shill clients
+    // matters; stub clients may only have initialization dependencies on
+    // clients previously initialized.
+    InitClient(shill_manager_client_.get());
+    InitClient(shill_device_client_.get());
+    InitClient(shill_ipconfig_client_.get());
+    InitClient(shill_service_client_.get());
+    InitClient(shill_profile_client_.get());
+    InitClient(gsm_sms_client_.get());
 
     // If the Service client has a TestInterface, add the default services.
     ShillServiceClient::TestInterface* service_client_test =
@@ -123,24 +109,15 @@
     if (service_client_test)
       service_client_test->AddDefaultServices();
 
-    image_burner_client_.reset(ImageBurnerClient::Create(client_type_,
-                                                         system_bus_.get()));
-    introspectable_client_.reset(
-        IntrospectableClient::Create(client_type_, system_bus_.get()));
-    modem_messaging_client_.reset(
-        ModemMessagingClient::Create(client_type_, system_bus_.get()));
-    permission_broker_client_.reset(
-        PermissionBrokerClient::Create(client_type_, system_bus_.get()));
-    power_manager_client_.reset(
-        PowerManagerClient::Create(client_type_override_, system_bus_.get()));
-    session_manager_client_.reset(
-        SessionManagerClient::Create(client_type_, system_bus_.get()));
-    sms_client_.reset(
-        SMSClient::Create(client_type_, system_bus_.get()));
-    system_clock_client_.reset(
-        SystemClockClient::Create(client_type_, system_bus_.get()));
-    update_engine_client_.reset(
-        UpdateEngineClient::Create(client_type_, system_bus_.get()));
+    InitClient(image_burner_client_.get());
+    InitClient(introspectable_client_.get());
+    InitClient(modem_messaging_client_.get());
+    InitClient(permission_broker_client_.get());
+    InitClient(power_manager_client_.get());
+    InitClient(session_manager_client_.get());
+    InitClient(sms_client_.get());
+    InitClient(system_clock_client_.get());
+    InitClient(update_engine_client_.get());
 
     // PowerPolicyController is dependent on PowerManagerClient, so
     // initialize it after the main list of clients.
@@ -208,10 +185,6 @@
         IBusInputContextClient::Create(client_type));
     ibus_engine_factory_service_.reset(
         IBusEngineFactoryService::Create(ibus_bus_.get(), client_type));
-    ibus_panel_service_.reset(
-        IBusPanelService::Create(client_type,
-                                 ibus_bus_.get(),
-                                 ibus_input_context_client_.get()));
 
     ibus_engine_services_.clear();
   }
@@ -361,12 +334,52 @@
     ibus_engine_services_.erase(object_path);
   }
 
-  virtual IBusPanelService* GetIBusPanelService() OVERRIDE {
-    return ibus_panel_service_.get();
+ private:
+  // Initializes |client| with the |system_bus_|.
+  void InitClient(DBusClient* client) {
+    client->Init(system_bus_.get());
   }
 
-  DBusClientImplementationType client_type_;
-  DBusClientImplementationType client_type_override_;
+  // Constructs all clients -- stub or real implementation according to
+  // |client_type| and |client_type_override| -- and stores them in the
+  // respective *_client_ member variable.
+  void CreateDefaultClients(DBusClientImplementationType client_type,
+                            DBusClientImplementationType client_type_override) {
+    bluetooth_adapter_client_.reset(
+        BluetoothAdapterClient::Create(client_type));
+    bluetooth_agent_manager_client_.reset(
+        BluetoothAgentManagerClient::Create(client_type));
+    bluetooth_device_client_.reset(BluetoothDeviceClient::Create(client_type));
+    bluetooth_input_client_.reset(BluetoothInputClient::Create(client_type));
+    bluetooth_profile_manager_client_.reset(
+        BluetoothProfileManagerClient::Create(client_type));
+    cras_audio_client_.reset(CrasAudioClient::Create(client_type));
+    cros_disks_client_.reset(CrosDisksClient::Create(client_type));
+    cryptohome_client_.reset(CryptohomeClient::Create(client_type));
+    debug_daemon_client_.reset(DebugDaemonClient::Create(client_type));
+    shill_manager_client_.reset(
+        ShillManagerClient::Create(client_type_override));
+    shill_device_client_.reset(
+        ShillDeviceClient::Create(client_type_override));
+    shill_ipconfig_client_.reset(
+        ShillIPConfigClient::Create(client_type_override));
+    shill_service_client_.reset(
+        ShillServiceClient::Create(client_type_override));
+    shill_profile_client_.reset(
+        ShillProfileClient::Create(client_type_override));
+    gsm_sms_client_.reset(GsmSMSClient::Create(client_type_override));
+    image_burner_client_.reset(ImageBurnerClient::Create(client_type));
+    introspectable_client_.reset(IntrospectableClient::Create(client_type));
+    modem_messaging_client_.reset(ModemMessagingClient::Create(client_type));
+    permission_broker_client_.reset(
+        PermissionBrokerClient::Create(client_type));
+    power_manager_client_.reset(
+        PowerManagerClient::Create(client_type_override));
+    session_manager_client_.reset(SessionManagerClient::Create(client_type));
+    sms_client_.reset(SMSClient::Create(client_type));
+    system_clock_client_.reset(SystemClockClient::Create(client_type));
+    update_engine_client_.reset(UpdateEngineClient::Create(client_type));
+  }
 
   // Note: Keep this before other members so they can call AddObserver() in
   // their c'tors.
@@ -403,7 +416,6 @@
   scoped_ptr<IBusInputContextClient> ibus_input_context_client_;
   scoped_ptr<IBusEngineFactoryService> ibus_engine_factory_service_;
   std::map<dbus::ObjectPath, IBusEngineService*> ibus_engine_services_;
-  scoped_ptr<IBusPanelService> ibus_panel_service_;
   scoped_ptr<PowerPolicyController> power_policy_controller_;
 
   std::string ibus_address_;
diff --git a/chromeos/dbus/dbus_thread_manager.h b/chromeos/dbus/dbus_thread_manager.h
index 6d918af..2fe94a2 100644
--- a/chromeos/dbus/dbus_thread_manager.h
+++ b/chromeos/dbus/dbus_thread_manager.h
@@ -40,7 +40,6 @@
 class IBusEngineFactoryService;
 class IBusEngineService;
 class IBusInputContextClient;
-class IBusPanelService;
 class ImageBurnerClient;
 class IntrospectableClient;
 class ModemMessagingClient;
@@ -134,7 +133,6 @@
   virtual IBusEngineService* GetIBusEngineService(
       const dbus::ObjectPath& object_path) = 0;
   virtual IBusInputContextClient* GetIBusInputContextClient() = 0;
-  virtual IBusPanelService* GetIBusPanelService() = 0;
   virtual ImageBurnerClient* GetImageBurnerClient() = 0;
   virtual IntrospectableClient* GetIntrospectableClient() = 0;
   virtual ModemMessagingClient* GetModemMessagingClient() = 0;
diff --git a/chromeos/dbus/debug_daemon_client.cc b/chromeos/dbus/debug_daemon_client.cc
index cfb36c2..c9b760f 100644
--- a/chromeos/dbus/debug_daemon_client.cc
+++ b/chromeos/dbus/debug_daemon_client.cc
@@ -139,13 +139,7 @@
 // The DebugDaemonClient implementation used in production.
 class DebugDaemonClientImpl : public DebugDaemonClient {
  public:
-  explicit DebugDaemonClientImpl(dbus::Bus* bus)
-      : debugdaemon_proxy_(NULL),
-        weak_ptr_factory_(this) {
-    debugdaemon_proxy_ = bus->GetObjectProxy(
-        debugd::kDebugdServiceName,
-        dbus::ObjectPath(debugd::kDebugdServicePath));
-  }
+  DebugDaemonClientImpl() : debugdaemon_proxy_(NULL), weak_ptr_factory_(this) {}
 
   virtual ~DebugDaemonClientImpl() {}
 
@@ -402,6 +396,13 @@
                    callback));
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    debugdaemon_proxy_ =
+        bus->GetObjectProxy(debugd::kDebugdServiceName,
+                            dbus::ObjectPath(debugd::kDebugdServicePath));
+  }
+
  private:
   // Called to check descriptor validity on a thread where i/o is permitted.
   static void CheckValidity(dbus::FileDescriptor* file_descriptor) {
@@ -619,6 +620,7 @@
 // which does nothing.
 class DebugDaemonClientStubImpl : public DebugDaemonClient {
   // DebugDaemonClient overrides.
+  virtual void Init(dbus::Bus* bus) OVERRIDE {}
   virtual void GetDebugLogs(base::PlatformFile file,
                             const GetDebugLogsCallback& callback) OVERRIDE {
     callback.Run(false);
@@ -715,10 +717,10 @@
 }
 
 // static
-DebugDaemonClient* DebugDaemonClient::Create(DBusClientImplementationType type,
-                                   dbus::Bus* bus) {
+DebugDaemonClient* DebugDaemonClient::Create(
+    DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new DebugDaemonClientImpl(bus);
+    return new DebugDaemonClientImpl();
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new DebugDaemonClientStubImpl();
 }
diff --git a/chromeos/dbus/debug_daemon_client.h b/chromeos/dbus/debug_daemon_client.h
index 7c09a78..711c2cb 100644
--- a/chromeos/dbus/debug_daemon_client.h
+++ b/chromeos/dbus/debug_daemon_client.h
@@ -9,14 +9,11 @@
 #include "base/platform_file.h"
 #include "base/memory/ref_counted_memory.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 
 #include <map>
 
-namespace dbus {
-class Bus;
-}  // namespace dbus
-
 namespace metrics {
 class PerfDataProto;
 }
@@ -24,7 +21,7 @@
 namespace chromeos {
 
 // DebugDaemonClient is used to communicate with the debug daemon.
-class CHROMEOS_EXPORT DebugDaemonClient {
+class CHROMEOS_EXPORT DebugDaemonClient : public DBusClient {
  public:
   virtual ~DebugDaemonClient();
 
@@ -146,8 +143,8 @@
 
   // Factory function, creates a new instance and returns ownership.
   // For normal usage, access the singleton via DBusThreadManager::Get().
-  static DebugDaemonClient* Create(DBusClientImplementationType type,
-                                   dbus::Bus* bus);
+  static DebugDaemonClient* Create(DBusClientImplementationType type);
+
  protected:
   // Create() should be used instead.
   DebugDaemonClient();
diff --git a/chromeos/dbus/fake_bluetooth_adapter_client.cc b/chromeos/dbus/fake_bluetooth_adapter_client.cc
index f993d20..93c0e06 100644
--- a/chromeos/dbus/fake_bluetooth_adapter_client.cc
+++ b/chromeos/dbus/fake_bluetooth_adapter_client.cc
@@ -70,14 +70,12 @@
   }
 }
 
-
 FakeBluetoothAdapterClient::FakeBluetoothAdapterClient()
     : visible_(true),
       second_visible_(false),
       discovering_count_(0) {
   properties_.reset(new Properties(base::Bind(
-      &FakeBluetoothAdapterClient::OnPropertyChanged,
-      base::Unretained(this))));
+      &FakeBluetoothAdapterClient::OnPropertyChanged, base::Unretained(this))));
 
   properties_->address.ReplaceValue(kAdapterAddress);
   properties_->name.ReplaceValue("Fake Adapter (Name)");
@@ -85,8 +83,7 @@
   properties_->pairable.ReplaceValue(true);
 
   second_properties_.reset(new Properties(base::Bind(
-      &FakeBluetoothAdapterClient::OnPropertyChanged,
-      base::Unretained(this))));
+      &FakeBluetoothAdapterClient::OnPropertyChanged, base::Unretained(this))));
 
   second_properties_->address.ReplaceValue(kSecondAdapterAddress);
   second_properties_->name.ReplaceValue("Second Fake Adapter (Name)");
@@ -97,6 +94,9 @@
 FakeBluetoothAdapterClient::~FakeBluetoothAdapterClient() {
 }
 
+void FakeBluetoothAdapterClient::Init(dbus::Bus* bus) {
+}
+
 void FakeBluetoothAdapterClient::AddObserver(Observer* observer) {
   observers_.AddObserver(observer);
 }
diff --git a/chromeos/dbus/fake_bluetooth_adapter_client.h b/chromeos/dbus/fake_bluetooth_adapter_client.h
index 6eb994a..051368f 100644
--- a/chromeos/dbus/fake_bluetooth_adapter_client.h
+++ b/chromeos/dbus/fake_bluetooth_adapter_client.h
@@ -39,7 +39,8 @@
   FakeBluetoothAdapterClient();
   virtual ~FakeBluetoothAdapterClient();
 
-  // BluetoothAdapterClient override
+  // BluetoothAdapterClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void AddObserver(Observer* observer) OVERRIDE;
   virtual void RemoveObserver(Observer* observer) OVERRIDE;
   virtual std::vector<dbus::ObjectPath> GetAdapters() OVERRIDE;
diff --git a/chromeos/dbus/fake_bluetooth_agent_manager_client.cc b/chromeos/dbus/fake_bluetooth_agent_manager_client.cc
index 57759ff..2672ad5 100644
--- a/chromeos/dbus/fake_bluetooth_agent_manager_client.cc
+++ b/chromeos/dbus/fake_bluetooth_agent_manager_client.cc
@@ -19,6 +19,9 @@
 FakeBluetoothAgentManagerClient::~FakeBluetoothAgentManagerClient() {
 }
 
+void FakeBluetoothAgentManagerClient::Init(dbus::Bus* bus) {
+}
+
 void FakeBluetoothAgentManagerClient::RegisterAgent(
     const dbus::ObjectPath& agent_path,
     const std::string& capability,
diff --git a/chromeos/dbus/fake_bluetooth_agent_manager_client.h b/chromeos/dbus/fake_bluetooth_agent_manager_client.h
index 315fd5e..98ecdb2 100644
--- a/chromeos/dbus/fake_bluetooth_agent_manager_client.h
+++ b/chromeos/dbus/fake_bluetooth_agent_manager_client.h
@@ -27,7 +27,8 @@
   FakeBluetoothAgentManagerClient();
   virtual ~FakeBluetoothAgentManagerClient();
 
-  // BluetoothAgentManagerClient override
+  // BluetoothAgentManagerClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void RegisterAgent(const dbus::ObjectPath& agent_path,
                              const std::string& capability,
                              const base::Closure& callback,
diff --git a/chromeos/dbus/fake_bluetooth_device_client.cc b/chromeos/dbus/fake_bluetooth_device_client.cc
index 4146163..08f748a 100644
--- a/chromeos/dbus/fake_bluetooth_device_client.cc
+++ b/chromeos/dbus/fake_bluetooth_device_client.cc
@@ -62,7 +62,7 @@
   close(fd);
 }
 
-}
+}  // namespace
 
 namespace chromeos {
 
@@ -188,7 +188,6 @@
   }
 }
 
-
 FakeBluetoothDeviceClient::FakeBluetoothDeviceClient()
     : simulation_interval_ms_(kSimulationIntervalMs),
       discovery_simulation_step_(0),
@@ -222,6 +221,9 @@
   STLDeleteValues(&properties_map_);
 }
 
+void FakeBluetoothDeviceClient::Init(dbus::Bus* bus) {
+}
+
 void FakeBluetoothDeviceClient::AddObserver(Observer* observer) {
   observers_.AddObserver(observer);
 }
diff --git a/chromeos/dbus/fake_bluetooth_device_client.h b/chromeos/dbus/fake_bluetooth_device_client.h
index e5cfc51..ccb68b7 100644
--- a/chromeos/dbus/fake_bluetooth_device_client.h
+++ b/chromeos/dbus/fake_bluetooth_device_client.h
@@ -42,7 +42,8 @@
   FakeBluetoothDeviceClient();
   virtual ~FakeBluetoothDeviceClient();
 
-  // BluetoothDeviceClient override
+  // BluetoothDeviceClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void AddObserver(Observer* observer) OVERRIDE;
   virtual void RemoveObserver(Observer* observer) OVERRIDE;
   virtual std::vector<dbus::ObjectPath> GetDevicesForAdapter(
diff --git a/chromeos/dbus/fake_bluetooth_input_client.cc b/chromeos/dbus/fake_bluetooth_input_client.cc
index 717df07..9e40f6e 100644
--- a/chromeos/dbus/fake_bluetooth_input_client.cc
+++ b/chromeos/dbus/fake_bluetooth_input_client.cc
@@ -56,6 +56,9 @@
   STLDeleteValues(&properties_map_);
 }
 
+void FakeBluetoothInputClient::Init(dbus::Bus* bus) {
+}
+
 void FakeBluetoothInputClient::AddObserver(Observer* observer) {
   observers_.AddObserver(observer);
 }
diff --git a/chromeos/dbus/fake_bluetooth_input_client.h b/chromeos/dbus/fake_bluetooth_input_client.h
index 8152668..3a284ae 100644
--- a/chromeos/dbus/fake_bluetooth_input_client.h
+++ b/chromeos/dbus/fake_bluetooth_input_client.h
@@ -36,7 +36,8 @@
   FakeBluetoothInputClient();
   virtual ~FakeBluetoothInputClient();
 
-  // BluetoothInputClient override
+  // BluetoothInputClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void AddObserver(Observer* observer) OVERRIDE;
   virtual void RemoveObserver(Observer* observer) OVERRIDE;
   virtual Properties* GetProperties(const dbus::ObjectPath& object_path)
diff --git a/chromeos/dbus/fake_bluetooth_profile_manager_client.cc b/chromeos/dbus/fake_bluetooth_profile_manager_client.cc
index 4c7e0a4..784a84f 100644
--- a/chromeos/dbus/fake_bluetooth_profile_manager_client.cc
+++ b/chromeos/dbus/fake_bluetooth_profile_manager_client.cc
@@ -29,6 +29,9 @@
 FakeBluetoothProfileManagerClient::~FakeBluetoothProfileManagerClient() {
 }
 
+void FakeBluetoothProfileManagerClient::Init(dbus::Bus* bus) {
+}
+
 void FakeBluetoothProfileManagerClient::RegisterProfile(
     const dbus::ObjectPath& profile_path,
     const std::string& uuid,
diff --git a/chromeos/dbus/fake_bluetooth_profile_manager_client.h b/chromeos/dbus/fake_bluetooth_profile_manager_client.h
index 8bfeaaf..c7fb935 100644
--- a/chromeos/dbus/fake_bluetooth_profile_manager_client.h
+++ b/chromeos/dbus/fake_bluetooth_profile_manager_client.h
@@ -30,7 +30,8 @@
   FakeBluetoothProfileManagerClient();
   virtual ~FakeBluetoothProfileManagerClient();
 
-  // BluetoothProfileManagerClient override
+  // BluetoothProfileManagerClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void RegisterProfile(const dbus::ObjectPath& profile_path,
                                const std::string& uuid,
                                const Options& options,
diff --git a/chromeos/dbus/fake_cros_disks_client.cc b/chromeos/dbus/fake_cros_disks_client.cc
index 30c89b0..b152f72 100644
--- a/chromeos/dbus/fake_cros_disks_client.cc
+++ b/chromeos/dbus/fake_cros_disks_client.cc
@@ -10,13 +10,17 @@
 namespace chromeos {
 
 FakeCrosDisksClient::FakeCrosDisksClient()
-  : unmount_call_count_(0),
-    unmount_success_(true),
-    format_device_call_count_(0),
-    format_device_success_(true) {
+    : unmount_call_count_(0),
+      unmount_success_(true),
+      format_device_call_count_(0),
+      format_device_success_(true) {
 }
 
-FakeCrosDisksClient::~FakeCrosDisksClient() {}
+FakeCrosDisksClient::~FakeCrosDisksClient() {
+}
+
+void FakeCrosDisksClient::Init(dbus::Bus* bus) {
+}
 
 void FakeCrosDisksClient::Mount(const std::string& source_path,
                                 const std::string& source_format,
@@ -37,7 +41,7 @@
   last_unmount_options_ = options;
   base::MessageLoopProxy::current()->PostTask(
       FROM_HERE, unmount_success_ ? callback : error_callback);
-  if(!unmount_listener_.is_null())
+  if (!unmount_listener_.is_null())
     unmount_listener_.Run();
 }
 
diff --git a/chromeos/dbus/fake_cros_disks_client.h b/chromeos/dbus/fake_cros_disks_client.h
index 49e8fba..855a3c1 100644
--- a/chromeos/dbus/fake_cros_disks_client.h
+++ b/chromeos/dbus/fake_cros_disks_client.h
@@ -19,7 +19,8 @@
   FakeCrosDisksClient();
   virtual ~FakeCrosDisksClient();
 
-  // CrosDisksClient overrides.
+  // CrosDisksClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void Mount(const std::string& source_path,
                      const std::string& source_format,
                      const std::string& mount_label,
diff --git a/chromeos/dbus/fake_cryptohome_client.cc b/chromeos/dbus/fake_cryptohome_client.cc
index c5df786..1d224d9 100644
--- a/chromeos/dbus/fake_cryptohome_client.cc
+++ b/chromeos/dbus/fake_cryptohome_client.cc
@@ -23,6 +23,9 @@
 FakeCryptohomeClient::~FakeCryptohomeClient() {
 }
 
+void FakeCryptohomeClient::Init(dbus::Bus* bus) {
+}
+
 void FakeCryptohomeClient::TpmIsBeingOwned(
     const BoolDBusMethodCallback& callback) {
 }
diff --git a/chromeos/dbus/fake_cryptohome_client.h b/chromeos/dbus/fake_cryptohome_client.h
index e254648..74d9e81 100644
--- a/chromeos/dbus/fake_cryptohome_client.h
+++ b/chromeos/dbus/fake_cryptohome_client.h
@@ -18,7 +18,8 @@
   FakeCryptohomeClient();
   virtual ~FakeCryptohomeClient();
 
-  // CryptohomeClient overrides.
+  // CryptohomeClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void SetAsyncCallStatusHandlers(
       const AsyncCallStatusHandler& handler,
       const AsyncCallStatusWithDataHandler& data_handler) OVERRIDE;
diff --git a/chromeos/dbus/fake_gsm_sms_client.cc b/chromeos/dbus/fake_gsm_sms_client.cc
index d1904d7..4d4452f 100644
--- a/chromeos/dbus/fake_gsm_sms_client.cc
+++ b/chromeos/dbus/fake_gsm_sms_client.cc
@@ -9,9 +9,9 @@
 namespace chromeos {
 
 FakeGsmSMSClient::FakeGsmSMSClient()
-  : test_index_(-1),
-    sms_test_message_switch_present_(false),
-    weak_ptr_factory_(this)  {
+    : test_index_(-1),
+      sms_test_message_switch_present_(false),
+      weak_ptr_factory_(this) {
   test_messages_.push_back("Test Message 0");
   test_messages_.push_back("Test Message 1");
   test_messages_.push_back("Test a relatively long message 2");
@@ -22,7 +22,11 @@
   test_messages_.push_back("Test Message 6");
 }
 
-FakeGsmSMSClient::~FakeGsmSMSClient() {}
+FakeGsmSMSClient::~FakeGsmSMSClient() {
+}
+
+void FakeGsmSMSClient::Init(dbus::Bus* bus) {
+}
 
 void FakeGsmSMSClient::SetSmsReceivedHandler(
     const std::string& service_name,
diff --git a/chromeos/dbus/fake_gsm_sms_client.h b/chromeos/dbus/fake_gsm_sms_client.h
index 1a846ee..321c9f6 100644
--- a/chromeos/dbus/fake_gsm_sms_client.h
+++ b/chromeos/dbus/fake_gsm_sms_client.h
@@ -20,7 +20,8 @@
   FakeGsmSMSClient();
   virtual ~FakeGsmSMSClient();
 
-  // GsmSMSClient overrides.
+  // GsmSMSClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void SetSmsReceivedHandler(const std::string& service_name,
                                      const dbus::ObjectPath& object_path,
                                      const SmsReceivedHandler& handler)
diff --git a/chromeos/dbus/fake_image_burner_client.cc b/chromeos/dbus/fake_image_burner_client.cc
index 978687e..01683b5 100644
--- a/chromeos/dbus/fake_image_burner_client.cc
+++ b/chromeos/dbus/fake_image_burner_client.cc
@@ -12,6 +12,9 @@
 FakeImageBurnerClient::~FakeImageBurnerClient() {
 }
 
+void FakeImageBurnerClient::Init(dbus::Bus* bus) {
+}
+
 void FakeImageBurnerClient::ResetEventHandlers() {
 }
 
diff --git a/chromeos/dbus/fake_image_burner_client.h b/chromeos/dbus/fake_image_burner_client.h
index cb18bcf..b730ad2 100644
--- a/chromeos/dbus/fake_image_burner_client.h
+++ b/chromeos/dbus/fake_image_burner_client.h
@@ -17,7 +17,8 @@
   FakeImageBurnerClient();
   virtual ~FakeImageBurnerClient();
 
-  // ImageBurnerClient overrides.
+  // ImageBurnerClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void BurnImage(const std::string& from_path,
                          const std::string& to_path,
                          const ErrorCallback& error_callback) OVERRIDE;
diff --git a/chromeos/dbus/fake_power_manager_client.cc b/chromeos/dbus/fake_power_manager_client.cc
index 3fda910..a0762ef 100644
--- a/chromeos/dbus/fake_power_manager_client.cc
+++ b/chromeos/dbus/fake_power_manager_client.cc
@@ -8,12 +8,15 @@
 namespace chromeos {
 
 FakePowerManagerClient::FakePowerManagerClient()
-  : request_restart_call_count_(0) {
+    : request_restart_call_count_(0) {
 }
 
 FakePowerManagerClient::~FakePowerManagerClient() {
 }
 
+void FakePowerManagerClient::Init(dbus::Bus* bus) {
+}
+
 void FakePowerManagerClient::AddObserver(Observer* observer) {
 }
 
diff --git a/chromeos/dbus/fake_power_manager_client.h b/chromeos/dbus/fake_power_manager_client.h
index 31dd227..001d783 100644
--- a/chromeos/dbus/fake_power_manager_client.h
+++ b/chromeos/dbus/fake_power_manager_client.h
@@ -20,7 +20,8 @@
   FakePowerManagerClient();
   virtual ~FakePowerManagerClient();
 
-  // PowerManagerClient overrides.
+  // PowerManagerClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void AddObserver(Observer* observer) OVERRIDE;
   virtual void RemoveObserver(Observer* observer) OVERRIDE;
   virtual bool HasObserver(Observer* observer) OVERRIDE;
diff --git a/chromeos/dbus/fake_session_manager_client.cc b/chromeos/dbus/fake_session_manager_client.cc
index 37d90da..b384a03 100644
--- a/chromeos/dbus/fake_session_manager_client.cc
+++ b/chromeos/dbus/fake_session_manager_client.cc
@@ -13,14 +13,17 @@
 namespace chromeos {
 
 FakeSessionManagerClient::FakeSessionManagerClient()
-  : emit_login_prompt_ready_call_count_(0) ,
-    notify_lock_screen_shown_call_count_(0),
-    notify_lock_screen_dismissed_call_count_(0){
+    : emit_login_prompt_ready_call_count_(0),
+      notify_lock_screen_shown_call_count_(0),
+      notify_lock_screen_dismissed_call_count_(0) {
 }
 
 FakeSessionManagerClient::~FakeSessionManagerClient() {
 }
 
+void FakeSessionManagerClient::Init(dbus::Bus* bus) {
+}
+
 void FakeSessionManagerClient::AddObserver(Observer* observer) {
   observers_.AddObserver(observer);
 }
diff --git a/chromeos/dbus/fake_session_manager_client.h b/chromeos/dbus/fake_session_manager_client.h
index 5c8941b..993a071 100644
--- a/chromeos/dbus/fake_session_manager_client.h
+++ b/chromeos/dbus/fake_session_manager_client.h
@@ -17,12 +17,13 @@
 
 // A fake implementation of session_manager. Accepts policy blobs to be set and
 // returns them unmodified.
-class FakeSessionManagerClient : public chromeos::SessionManagerClient {
+class FakeSessionManagerClient : public SessionManagerClient {
  public:
   FakeSessionManagerClient();
   virtual ~FakeSessionManagerClient();
 
-  // SessionManagerClient:
+  // SessionManagerClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void AddObserver(Observer* observer) OVERRIDE;
   virtual void RemoveObserver(Observer* observer) OVERRIDE;
   virtual bool HasObserver(Observer* observer) OVERRIDE;
diff --git a/chromeos/dbus/fake_shill_device_client.cc b/chromeos/dbus/fake_shill_device_client.cc
index 4d8c7f3..2bf0b65 100644
--- a/chromeos/dbus/fake_shill_device_client.cc
+++ b/chromeos/dbus/fake_shill_device_client.cc
@@ -12,6 +12,9 @@
 FakeShillDeviceClient::~FakeShillDeviceClient() {
 }
 
+void FakeShillDeviceClient::Init(dbus::Bus* bus) {
+}
+
 void FakeShillDeviceClient::AddPropertyChangedObserver(
     const dbus::ObjectPath& device_path,
     ShillPropertyChangedObserver* observer) {
diff --git a/chromeos/dbus/fake_shill_device_client.h b/chromeos/dbus/fake_shill_device_client.h
index b887bb7..c97bae1 100644
--- a/chromeos/dbus/fake_shill_device_client.h
+++ b/chromeos/dbus/fake_shill_device_client.h
@@ -18,7 +18,8 @@
   FakeShillDeviceClient();
   virtual ~FakeShillDeviceClient();
 
-  // ShillDeviceClient overrides.
+  // ShillDeviceClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void AddPropertyChangedObserver(
       const dbus::ObjectPath& device_path,
       ShillPropertyChangedObserver* observer) OVERRIDE;
diff --git a/chromeos/dbus/fake_shill_manager_client.cc b/chromeos/dbus/fake_shill_manager_client.cc
index 35b256a..faab3d5 100644
--- a/chromeos/dbus/fake_shill_manager_client.cc
+++ b/chromeos/dbus/fake_shill_manager_client.cc
@@ -12,6 +12,9 @@
 FakeShillManagerClient::~FakeShillManagerClient() {
 }
 
+void FakeShillManagerClient::Init(dbus::Bus* bus) {
+}
+
 void FakeShillManagerClient::RequestScan(const std::string& type,
                                          const base::Closure& callback,
                                          const ErrorCallback& error_callback) {
diff --git a/chromeos/dbus/fake_shill_manager_client.h b/chromeos/dbus/fake_shill_manager_client.h
index 2a1c0e7..1d1ba1a 100644
--- a/chromeos/dbus/fake_shill_manager_client.h
+++ b/chromeos/dbus/fake_shill_manager_client.h
@@ -17,7 +17,8 @@
   FakeShillManagerClient();
   virtual ~FakeShillManagerClient();
 
-  // ShillManagerClient overrides.
+  // ShillManagerClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void AddPropertyChangedObserver(
       ShillPropertyChangedObserver* observer) OVERRIDE;
   virtual void RemovePropertyChangedObserver(
diff --git a/chromeos/dbus/fake_system_clock_client.cc b/chromeos/dbus/fake_system_clock_client.cc
index ea1b737..92a8dd3 100644
--- a/chromeos/dbus/fake_system_clock_client.cc
+++ b/chromeos/dbus/fake_system_clock_client.cc
@@ -12,6 +12,9 @@
 FakeSystemClockClient::~FakeSystemClockClient() {
 }
 
+void FakeSystemClockClient::Init(dbus::Bus* bus) {
+}
+
 void FakeSystemClockClient::AddObserver(Observer* observer) {
 }
 
diff --git a/chromeos/dbus/fake_system_clock_client.h b/chromeos/dbus/fake_system_clock_client.h
index cea91a6..31a3ebc 100644
--- a/chromeos/dbus/fake_system_clock_client.h
+++ b/chromeos/dbus/fake_system_clock_client.h
@@ -15,7 +15,8 @@
   FakeSystemClockClient();
   virtual ~FakeSystemClockClient();
 
-  // SystemClockClient overrides.
+  // SystemClockClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void AddObserver(Observer* observer) OVERRIDE;
   virtual void RemoveObserver(Observer* observer) OVERRIDE;
   virtual bool HasObserver(Observer* observer) OVERRIDE;
diff --git a/chromeos/dbus/fake_update_engine_client.cc b/chromeos/dbus/fake_update_engine_client.cc
index b9f40a2..a246c41 100644
--- a/chromeos/dbus/fake_update_engine_client.cc
+++ b/chromeos/dbus/fake_update_engine_client.cc
@@ -7,13 +7,16 @@
 namespace chromeos {
 
 FakeUpdateEngineClient::FakeUpdateEngineClient()
-  : update_check_result_(UpdateEngineClient::UPDATE_RESULT_SUCCESS),
-    reboot_after_update_call_count_(0) {
+    : update_check_result_(UpdateEngineClient::UPDATE_RESULT_SUCCESS),
+      reboot_after_update_call_count_(0) {
 }
 
 FakeUpdateEngineClient::~FakeUpdateEngineClient() {
 }
 
+void FakeUpdateEngineClient::Init(dbus::Bus* bus) {
+}
+
 void FakeUpdateEngineClient::AddObserver(Observer* observer) {
 }
 
diff --git a/chromeos/dbus/fake_update_engine_client.h b/chromeos/dbus/fake_update_engine_client.h
index 605d161..c5afe18 100644
--- a/chromeos/dbus/fake_update_engine_client.h
+++ b/chromeos/dbus/fake_update_engine_client.h
@@ -21,7 +21,8 @@
   FakeUpdateEngineClient();
   virtual ~FakeUpdateEngineClient();
 
-  // Overrides
+  // UpdateEngineClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void AddObserver(Observer* observer) OVERRIDE;
   virtual void RemoveObserver(Observer* observer) OVERRIDE;
   virtual bool HasObserver(Observer* observer) OVERRIDE;
diff --git a/chromeos/dbus/gsm_sms_client.cc b/chromeos/dbus/gsm_sms_client.cc
index ae9b900..4fead8d 100644
--- a/chromeos/dbus/gsm_sms_client.cc
+++ b/chromeos/dbus/gsm_sms_client.cc
@@ -165,10 +165,7 @@
 // The GsmSMSClient implementation.
 class GsmSMSClientImpl : public GsmSMSClient {
  public:
-  explicit GsmSMSClientImpl(dbus::Bus* bus)
-      : bus_(bus),
-        proxies_deleter_(&proxies_) {
-  }
+  GsmSMSClientImpl() : bus_(NULL), proxies_deleter_(&proxies_) {}
 
   // GsmSMSClient override.
   virtual void SetSmsReceivedHandler(
@@ -213,6 +210,9 @@
                              const dbus::ObjectPath& object_path) OVERRIDE {
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE { bus_ = bus; }
+
  private:
   typedef std::map<std::pair<std::string, std::string>, SMSProxy*> ProxyMap;
 
@@ -247,10 +247,9 @@
 GsmSMSClient::~GsmSMSClient() {}
 
 // static
-GsmSMSClient* GsmSMSClient::Create(DBusClientImplementationType type,
-                                   dbus::Bus* bus) {
+GsmSMSClient* GsmSMSClient::Create(DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new GsmSMSClientImpl(bus);
+    return new GsmSMSClientImpl();
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
 
   FakeGsmSMSClient* fake = new FakeGsmSMSClient();
diff --git a/chromeos/dbus/gsm_sms_client.h b/chromeos/dbus/gsm_sms_client.h
index 4effeff..af30753 100644
--- a/chromeos/dbus/gsm_sms_client.h
+++ b/chromeos/dbus/gsm_sms_client.h
@@ -10,6 +10,7 @@
 #include "base/basictypes.h"
 #include "base/callback.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 
 namespace base {
@@ -18,7 +19,6 @@
 }
 
 namespace dbus {
-class Bus;
 class ObjectPath;
 }
 
@@ -28,7 +28,7 @@
 // org.freedesktop.ModemManager.Modem.Gsm.SMS service.
 // All methods should be called from the origin thread (UI thread) which
 // initializes the DBusThreadManager instance.
-class CHROMEOS_EXPORT GsmSMSClient {
+class CHROMEOS_EXPORT GsmSMSClient : public DBusClient {
  public:
   typedef base::Callback<void(uint32 index, bool complete)> SmsReceivedHandler;
   typedef base::Callback<void()> DeleteCallback;
@@ -39,8 +39,7 @@
 
   // Factory function, creates a new instance and returns ownership.
   // For normal usage, access the singleton via DBusThreadManager::Get().
-  static GsmSMSClient* Create(DBusClientImplementationType type,
-                              dbus::Bus* bus);
+  static GsmSMSClient* Create(DBusClientImplementationType type);
 
   // Sets SmsReceived signal handler.
   virtual void SetSmsReceivedHandler(const std::string& service_name,
@@ -74,6 +73,8 @@
                              const dbus::ObjectPath& object_path) = 0;
 
  protected:
+  friend class GsmSMSClientTest;
+
   // Create() should be used instead.
   GsmSMSClient();
 
diff --git a/chromeos/dbus/gsm_sms_client_unittest.cc b/chromeos/dbus/gsm_sms_client_unittest.cc
index baed2f7..8a956c4 100644
--- a/chromeos/dbus/gsm_sms_client_unittest.cc
+++ b/chromeos/dbus/gsm_sms_client_unittest.cc
@@ -99,8 +99,8 @@
     EXPECT_CALL(*mock_bus_.get(), ShutdownAndBlock()).WillOnce(Return());
 
     // Create a client with the mock bus.
-    client_.reset(
-        GsmSMSClient::Create(REAL_DBUS_CLIENT_IMPLEMENTATION, mock_bus_.get()));
+    client_.reset(GsmSMSClient::Create(REAL_DBUS_CLIENT_IMPLEMENTATION));
+    client_->Init(mock_bus_.get());
   }
 
   virtual void TearDown() OVERRIDE {
diff --git a/chromeos/dbus/image_burner_client.cc b/chromeos/dbus/image_burner_client.cc
index 86c4249..a807f07 100644
--- a/chromeos/dbus/image_burner_client.cc
+++ b/chromeos/dbus/image_burner_client.cc
@@ -20,27 +20,8 @@
 // The ImageBurnerClient implementation.
 class ImageBurnerClientImpl : public ImageBurnerClient {
  public:
-  explicit ImageBurnerClientImpl(dbus::Bus* bus)
-      : proxy_(NULL),
-        weak_ptr_factory_(this) {
-    proxy_ = bus->GetObjectProxy(
-        imageburn::kImageBurnServiceName,
-        dbus::ObjectPath(imageburn::kImageBurnServicePath));
-    proxy_->ConnectToSignal(
-        imageburn::kImageBurnServiceInterface,
-        imageburn::kSignalBurnFinishedName,
-        base::Bind(&ImageBurnerClientImpl::OnBurnFinished,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&ImageBurnerClientImpl::OnSignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-    proxy_->ConnectToSignal(
-        imageburn::kImageBurnServiceInterface,
-        imageburn::kSignalBurnUpdateName,
-        base::Bind(&ImageBurnerClientImpl::OnBurnProgressUpdate,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&ImageBurnerClientImpl::OnSignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-  }
+  ImageBurnerClientImpl() : proxy_(NULL), weak_ptr_factory_(this) {}
+
   virtual ~ImageBurnerClientImpl() {}
 
   // ImageBurnerClient override.
@@ -72,6 +53,27 @@
     burn_progress_update_handler_.Reset();
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    proxy_ =
+        bus->GetObjectProxy(imageburn::kImageBurnServiceName,
+                            dbus::ObjectPath(imageburn::kImageBurnServicePath));
+    proxy_->ConnectToSignal(
+        imageburn::kImageBurnServiceInterface,
+        imageburn::kSignalBurnFinishedName,
+        base::Bind(&ImageBurnerClientImpl::OnBurnFinished,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&ImageBurnerClientImpl::OnSignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+    proxy_->ConnectToSignal(
+        imageburn::kImageBurnServiceInterface,
+        imageburn::kSignalBurnUpdateName,
+        base::Bind(&ImageBurnerClientImpl::OnBurnProgressUpdate,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&ImageBurnerClientImpl::OnSignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+  }
+
  private:
   // Called when a response for BurnImage is received
   void OnBurnImage(ErrorCallback error_callback, dbus::Response* response) {
@@ -142,10 +144,10 @@
 }
 
 // static
-ImageBurnerClient* ImageBurnerClient::Create(DBusClientImplementationType type,
-                                             dbus::Bus* bus) {
+ImageBurnerClient* ImageBurnerClient::Create(
+    DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new ImageBurnerClientImpl(bus);
+    return new ImageBurnerClientImpl();
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new FakeImageBurnerClient();
 }
diff --git a/chromeos/dbus/image_burner_client.h b/chromeos/dbus/image_burner_client.h
index 053b430..c6f65ef 100644
--- a/chromeos/dbus/image_burner_client.h
+++ b/chromeos/dbus/image_burner_client.h
@@ -10,18 +10,15 @@
 #include "base/basictypes.h"
 #include "base/callback.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 
-namespace dbus {
-class Bus;
-}
-
 namespace chromeos {
 
 // ImageBurnerClient is used to communicate with the image burner.
 // All method should be called from the origin thread (UI thread) which
 // initializes the DBusThreadManager instance.
-class CHROMEOS_EXPORT ImageBurnerClient {
+class CHROMEOS_EXPORT ImageBurnerClient : public DBusClient {
  public:
   virtual ~ImageBurnerClient();
 
@@ -57,8 +54,7 @@
 
   // Factory function, creates a new instance and returns ownership.
   // For normal usage, access the singleton via DBusThreadManager::Get().
-  static ImageBurnerClient* Create(DBusClientImplementationType type,
-                                   dbus::Bus* bus);
+  static ImageBurnerClient* Create(DBusClientImplementationType type);
 
  protected:
   // Create() should be used instead.
diff --git a/chromeos/dbus/introspectable_client.cc b/chromeos/dbus/introspectable_client.cc
index 83b09a3..81cc8a1 100644
--- a/chromeos/dbus/introspectable_client.cc
+++ b/chromeos/dbus/introspectable_client.cc
@@ -32,10 +32,7 @@
 // The IntrospectableClient implementation used in production.
 class IntrospectableClientImpl : public IntrospectableClient {
  public:
-  explicit IntrospectableClientImpl(dbus::Bus* bus)
-      : bus_(bus),
-        weak_ptr_factory_(this) {
-  }
+  IntrospectableClientImpl() : bus_(NULL), weak_ptr_factory_(this) {}
 
   virtual ~IntrospectableClientImpl() {
   }
@@ -57,6 +54,9 @@
                    service_name, object_path, callback));
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE { bus_ = bus; }
+
  private:
   // Called by dbus:: when a response for Introspect() is recieved.
   void OnIntrospect(const std::string& service_name,
@@ -96,6 +96,7 @@
 class IntrospectableClientStubImpl : public IntrospectableClient {
  public:
   // IntrospectableClient override.
+  virtual void Init(dbus::Bus* bus) OVERRIDE {}
   virtual void Introspect(const std::string& service_name,
                           const dbus::ObjectPath& object_path,
                           const IntrospectCallback& callback) OVERRIDE {
@@ -145,10 +146,9 @@
 
 // static
 IntrospectableClient* IntrospectableClient::Create(
-    DBusClientImplementationType type,
-    dbus::Bus* bus) {
+    DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new IntrospectableClientImpl(bus);
+    return new IntrospectableClientImpl();
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new IntrospectableClientStubImpl();
 }
diff --git a/chromeos/dbus/introspectable_client.h b/chromeos/dbus/introspectable_client.h
index 1ee15d6..573b9f9 100644
--- a/chromeos/dbus/introspectable_client.h
+++ b/chromeos/dbus/introspectable_client.h
@@ -10,18 +10,15 @@
 
 #include "base/callback.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 #include "dbus/object_path.h"
 
-namespace dbus {
-class Bus;
-}  // namespace dbus
-
 namespace chromeos {
 
 // IntrospectableClient is used to retrieve the D-Bus introspection data
 // from a remote object.
-class CHROMEOS_EXPORT IntrospectableClient {
+class CHROMEOS_EXPORT IntrospectableClient : public DBusClient {
  public:
   virtual ~IntrospectableClient();
 
@@ -48,8 +45,7 @@
       const std::string& xml_data);
 
   // Creates the instance
-  static IntrospectableClient* Create(DBusClientImplementationType type,
-                                      dbus::Bus* bus);
+  static IntrospectableClient* Create(DBusClientImplementationType type);
 
  protected:
   IntrospectableClient();
diff --git a/chromeos/dbus/mock_cryptohome_client.h b/chromeos/dbus/mock_cryptohome_client.h
index 86986e6..f057709 100644
--- a/chromeos/dbus/mock_cryptohome_client.h
+++ b/chromeos/dbus/mock_cryptohome_client.h
@@ -17,6 +17,7 @@
   MockCryptohomeClient();
   virtual ~MockCryptohomeClient();
 
+  MOCK_METHOD1(Init, void(dbus::Bus* bus));
   MOCK_METHOD2(SetAsyncCallStatusHandlers,
                void(const AsyncCallStatusHandler& handler,
                     const AsyncCallStatusWithDataHandler& data_handler));
diff --git a/chromeos/dbus/mock_dbus_thread_manager.cc b/chromeos/dbus/mock_dbus_thread_manager.cc
index 9cf664c..675f592 100644
--- a/chromeos/dbus/mock_dbus_thread_manager.cc
+++ b/chromeos/dbus/mock_dbus_thread_manager.cc
@@ -14,7 +14,6 @@
 #include "chromeos/dbus/ibus/mock_ibus_engine_factory_service.h"
 #include "chromeos/dbus/ibus/mock_ibus_engine_service.h"
 #include "chromeos/dbus/ibus/mock_ibus_input_context_client.h"
-#include "chromeos/dbus/ibus/mock_ibus_panel_service.h"
 #include "chromeos/dbus/mock_cryptohome_client.h"
 #include "chromeos/dbus/mock_shill_device_client.h"
 #include "chromeos/dbus/mock_shill_ipconfig_client.h"
diff --git a/chromeos/dbus/mock_dbus_thread_manager.h b/chromeos/dbus/mock_dbus_thread_manager.h
index 16c4be1..05c590d 100644
--- a/chromeos/dbus/mock_dbus_thread_manager.h
+++ b/chromeos/dbus/mock_dbus_thread_manager.h
@@ -85,7 +85,6 @@
                IBusEngineService*(const dbus::ObjectPath& object_path));
   MOCK_METHOD1(RemoveIBusEngineService,
                void(const dbus::ObjectPath& object_path));
-  MOCK_METHOD0(GetIBusPanelService, IBusPanelService*(void));
 
   FakeBluetoothAdapterClient* fake_bluetooth_adapter_client() {
     return fake_bluetooth_adapter_client_.get();
diff --git a/chromeos/dbus/mock_dbus_thread_manager_without_gmock.cc b/chromeos/dbus/mock_dbus_thread_manager_without_gmock.cc
index 8bd95a1..34a3b82 100644
--- a/chromeos/dbus/mock_dbus_thread_manager_without_gmock.cc
+++ b/chromeos/dbus/mock_dbus_thread_manager_without_gmock.cc
@@ -24,7 +24,6 @@
 #include "chromeos/dbus/ibus/mock_ibus_engine_factory_service.h"
 #include "chromeos/dbus/ibus/mock_ibus_engine_service.h"
 #include "chromeos/dbus/ibus/mock_ibus_input_context_client.h"
-#include "chromeos/dbus/ibus/mock_ibus_panel_service.h"
 #include "chromeos/dbus/power_policy_controller.h"
 
 namespace chromeos {
@@ -77,7 +76,6 @@
   mock_ibus_input_context_client_.reset(new MockIBusInputContextClient);
   mock_ibus_engine_service_.reset(new MockIBusEngineService);
   mock_ibus_engine_factory_service_.reset(new MockIBusEngineFactoryService);
-  mock_ibus_panel_service_.reset(new MockIBusPanelService);
 }
 
 dbus::Bus* MockDBusThreadManagerWithoutGMock::GetSystemBus() {
@@ -234,8 +232,4 @@
     const dbus::ObjectPath& object_path) {
 }
 
-IBusPanelService* MockDBusThreadManagerWithoutGMock::GetIBusPanelService() {
-  return mock_ibus_panel_service_.get();
-}
-
 }  // namespace chromeos
diff --git a/chromeos/dbus/mock_dbus_thread_manager_without_gmock.h b/chromeos/dbus/mock_dbus_thread_manager_without_gmock.h
index f5c5841..a68ae04 100644
--- a/chromeos/dbus/mock_dbus_thread_manager_without_gmock.h
+++ b/chromeos/dbus/mock_dbus_thread_manager_without_gmock.h
@@ -38,7 +38,6 @@
 class MockIBusEngineFactoryService;
 class MockIBusEngineService;
 class MockIBusInputContextClient;
-class MockIBusPanelService;
 
 // This class provides an another mock DBusThreadManager without gmock
 // dependency. This class is used for places where GMock is not allowed
@@ -91,7 +90,6 @@
       const dbus::ObjectPath& object_path) OVERRIDE;
   virtual void RemoveIBusEngineService(
       const dbus::ObjectPath& object_path) OVERRIDE;
-  virtual IBusPanelService* GetIBusPanelService() OVERRIDE;
 
   FakeBluetoothAdapterClient* fake_bluetooth_adapter_client() {
     return fake_bluetooth_adapter_client_.get();
@@ -169,10 +167,6 @@
     return mock_ibus_engine_factory_service_.get();
   }
 
-  MockIBusPanelService* mock_ibus_panel_service() {
-    return mock_ibus_panel_service_.get();
-  }
-
   void set_ibus_bus(dbus::Bus* ibus_bus) {
     ibus_bus_ = ibus_bus;
   }
@@ -204,7 +198,6 @@
   scoped_ptr<MockIBusInputContextClient> mock_ibus_input_context_client_;
   scoped_ptr<MockIBusEngineService> mock_ibus_engine_service_;
   scoped_ptr<MockIBusEngineFactoryService> mock_ibus_engine_factory_service_;
-  scoped_ptr<MockIBusPanelService> mock_ibus_panel_service_;
 
   scoped_ptr<PowerPolicyController> power_policy_controller_;
   dbus::Bus* ibus_bus_;
diff --git a/chromeos/dbus/mock_gsm_sms_client.h b/chromeos/dbus/mock_gsm_sms_client.h
index bc0bee9..aa522c5 100644
--- a/chromeos/dbus/mock_gsm_sms_client.h
+++ b/chromeos/dbus/mock_gsm_sms_client.h
@@ -19,6 +19,7 @@
   MockGsmSMSClient();
   virtual ~MockGsmSMSClient();
 
+  MOCK_METHOD1(Init, void(dbus::Bus* bus));
   MOCK_METHOD3(SetSmsReceivedHandler, void(const std::string& service_name,
                                            const dbus::ObjectPath& object_path,
                                            const SmsReceivedHandler& handler));
diff --git a/chromeos/dbus/mock_session_manager_client.h b/chromeos/dbus/mock_session_manager_client.h
index afe8e37..14e879b 100644
--- a/chromeos/dbus/mock_session_manager_client.h
+++ b/chromeos/dbus/mock_session_manager_client.h
@@ -17,6 +17,7 @@
   MockSessionManagerClient();
   virtual ~MockSessionManagerClient();
 
+  MOCK_METHOD1(Init, void(dbus::Bus* bus));
   MOCK_METHOD1(AddObserver, void(Observer*));
   MOCK_METHOD1(RemoveObserver, void(Observer*));
   MOCK_METHOD1(HasObserver, bool(Observer*));
diff --git a/chromeos/dbus/mock_shill_device_client.h b/chromeos/dbus/mock_shill_device_client.h
index eef766f..d473ed7 100644
--- a/chromeos/dbus/mock_shill_device_client.h
+++ b/chromeos/dbus/mock_shill_device_client.h
@@ -18,6 +18,7 @@
   MockShillDeviceClient();
   virtual ~MockShillDeviceClient();
 
+  MOCK_METHOD1(Init, void(dbus::Bus* bus));
   MOCK_METHOD2(AddPropertyChangedObserver,
                void(const dbus::ObjectPath& device_path,
                     ShillPropertyChangedObserver* observer));
diff --git a/chromeos/dbus/mock_shill_ipconfig_client.h b/chromeos/dbus/mock_shill_ipconfig_client.h
index 4a4bea3..4a35b37 100644
--- a/chromeos/dbus/mock_shill_ipconfig_client.h
+++ b/chromeos/dbus/mock_shill_ipconfig_client.h
@@ -18,6 +18,7 @@
   MockShillIPConfigClient();
   virtual ~MockShillIPConfigClient();
 
+  MOCK_METHOD1(Init, void(dbus::Bus* bus));
   MOCK_METHOD2(AddPropertyChangedObserver,
                void(const dbus::ObjectPath& ipconfig_path,
                     ShillPropertyChangedObserver* observer));
diff --git a/chromeos/dbus/mock_shill_manager_client.h b/chromeos/dbus/mock_shill_manager_client.h
index b62aebd..bf80b64 100644
--- a/chromeos/dbus/mock_shill_manager_client.h
+++ b/chromeos/dbus/mock_shill_manager_client.h
@@ -17,6 +17,7 @@
   MockShillManagerClient();
   virtual ~MockShillManagerClient();
 
+  MOCK_METHOD1(Init, void(dbus::Bus* bus));
   MOCK_METHOD1(AddPropertyChangedObserver,
                void(ShillPropertyChangedObserver* observer));
   MOCK_METHOD1(RemovePropertyChangedObserver,
diff --git a/chromeos/dbus/mock_shill_profile_client.h b/chromeos/dbus/mock_shill_profile_client.h
index b18a843..9bf2012 100644
--- a/chromeos/dbus/mock_shill_profile_client.h
+++ b/chromeos/dbus/mock_shill_profile_client.h
@@ -20,6 +20,7 @@
   MockShillProfileClient();
   virtual ~MockShillProfileClient();
 
+  MOCK_METHOD1(Init, void(dbus::Bus* bus));
   MOCK_METHOD2(AddPropertyChangedObserver,
                void(const dbus::ObjectPath& profile_path,
                     ShillPropertyChangedObserver* observer));
diff --git a/chromeos/dbus/mock_shill_service_client.h b/chromeos/dbus/mock_shill_service_client.h
index 6f4ab29..a890b51 100644
--- a/chromeos/dbus/mock_shill_service_client.h
+++ b/chromeos/dbus/mock_shill_service_client.h
@@ -18,6 +18,7 @@
   MockShillServiceClient();
   virtual ~MockShillServiceClient();
 
+  MOCK_METHOD1(Init, void(dbus::Bus* dbus));
   MOCK_METHOD2(AddPropertyChangedObserver,
                void(const dbus::ObjectPath& service_path,
                     ShillPropertyChangedObserver* observer));
diff --git a/chromeos/dbus/modem_messaging_client.cc b/chromeos/dbus/modem_messaging_client.cc
index 2aa12de..b421b7c 100644
--- a/chromeos/dbus/modem_messaging_client.cc
+++ b/chromeos/dbus/modem_messaging_client.cc
@@ -135,12 +135,11 @@
 
 class CHROMEOS_EXPORT ModemMessagingClientImpl : public ModemMessagingClient {
  public:
-  explicit ModemMessagingClientImpl(dbus::Bus *bus)
-      : bus_(bus),
+  ModemMessagingClientImpl()
+      : bus_(NULL),
         proxies_deleter_(&proxies_) {
   }
 
-  // ModemMessagingClient override.
   virtual void SetSmsReceivedHandler(
       const std::string& service_name,
       const dbus::ObjectPath& object_path,
@@ -148,14 +147,12 @@
     GetProxy(service_name, object_path)->SetSmsReceivedHandler(handler);
   }
 
-  // ModemMessagingClient override.
   virtual void ResetSmsReceivedHandler(
       const std::string& service_name,
       const dbus::ObjectPath& object_path) OVERRIDE {
     GetProxy(service_name, object_path)->ResetSmsReceivedHandler();
   }
 
-  // ModemMessagingClient override.
   virtual void Delete(const std::string& service_name,
                       const dbus::ObjectPath& object_path,
                       const dbus::ObjectPath& sms_path,
@@ -163,13 +160,17 @@
     GetProxy(service_name, object_path)->Delete(sms_path, callback);
   }
 
-  // ModemMessagingClient override.
   virtual void List(const std::string& service_name,
                     const dbus::ObjectPath& object_path,
                     const ListCallback& callback) OVERRIDE {
     GetProxy(service_name, object_path)->List(callback);
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    bus_ = bus;
+  };
+
  private:
   typedef std::map<std::pair<std::string, std::string>, ModemMessagingProxy*>
       ProxyMap;
@@ -203,6 +204,7 @@
   virtual ~ModemMessagingClientStubImpl() {}
 
   // ModemMessagingClient override.
+  virtual void Init(dbus::Bus* bus) OVERRIDE {}
   virtual void SetSmsReceivedHandler(
       const std::string& service_name,
       const dbus::ObjectPath& object_path,
@@ -268,10 +270,9 @@
 
 // static
 ModemMessagingClient* ModemMessagingClient::Create(
-    DBusClientImplementationType type,
-    dbus::Bus* bus) {
+    DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION) {
-    return new ModemMessagingClientImpl(bus);
+    return new ModemMessagingClientImpl();
   }
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new ModemMessagingClientStubImpl();
diff --git a/chromeos/dbus/modem_messaging_client.h b/chromeos/dbus/modem_messaging_client.h
index 8a5eb3d..d3ebc33 100644
--- a/chromeos/dbus/modem_messaging_client.h
+++ b/chromeos/dbus/modem_messaging_client.h
@@ -11,10 +11,10 @@
 #include "base/basictypes.h"
 #include "base/callback.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 
 namespace dbus {
-class Bus;
 class ObjectPath;
 }
 
@@ -24,7 +24,7 @@
 // org.freedesktop.ModemManager1.Modem.Messaging service.  All methods
 // should be called from the origin thread (UI thread) which
 // initializes the DBusThreadManager instance.
-class CHROMEOS_EXPORT ModemMessagingClient {
+class CHROMEOS_EXPORT ModemMessagingClient : public DBusClient {
  public:
   typedef base::Callback<void()> DeleteCallback;
   typedef base::Callback<void(const dbus::ObjectPath& message_path,
@@ -36,8 +36,7 @@
 
   // Factory function, creates a new instance and returns ownership.
   // For normal usage, access the singleton via DBusThreadManager::Get().
-  static ModemMessagingClient* Create(DBusClientImplementationType type,
-                                      dbus::Bus* bus);
+  static ModemMessagingClient* Create(DBusClientImplementationType type);
 
   // Sets SmsReceived signal handler.
   virtual void SetSmsReceivedHandler(const std::string& service_name,
@@ -60,6 +59,8 @@
                     const ListCallback& callback) = 0;
 
  protected:
+  friend class ModemMessagingClientTest;
+
   // Create() should be used instead.
   ModemMessagingClient();
 
diff --git a/chromeos/dbus/modem_messaging_client_unittest.cc b/chromeos/dbus/modem_messaging_client_unittest.cc
index f51e9f4..506ef9b 100644
--- a/chromeos/dbus/modem_messaging_client_unittest.cc
+++ b/chromeos/dbus/modem_messaging_client_unittest.cc
@@ -93,8 +93,9 @@
     EXPECT_CALL(*mock_bus_.get(), ShutdownAndBlock()).WillOnce(Return());
 
     // Create a client with the mock bus.
-    client_.reset(ModemMessagingClient::Create(REAL_DBUS_CLIENT_IMPLEMENTATION,
-                                               mock_bus_.get()));
+    client_.reset(
+        ModemMessagingClient::Create(REAL_DBUS_CLIENT_IMPLEMENTATION));
+    client_->Init(mock_bus_.get());
   }
 
   virtual void TearDown() OVERRIDE {
diff --git a/chromeos/dbus/permission_broker_client.cc b/chromeos/dbus/permission_broker_client.cc
index cd86eba..4bf2699 100644
--- a/chromeos/dbus/permission_broker_client.cc
+++ b/chromeos/dbus/permission_broker_client.cc
@@ -22,10 +22,7 @@
 
 class PermissionBrokerClientImpl : public PermissionBrokerClient {
  public:
-  explicit PermissionBrokerClientImpl(dbus::Bus* bus)
-      : proxy_(bus->GetObjectProxy(kPermissionBrokerServiceName,
-            dbus::ObjectPath(kPermissionBrokerServicePath))),
-        weak_ptr_factory_(this) {}
+  PermissionBrokerClientImpl() : proxy_(NULL), weak_ptr_factory_(this) {}
 
   virtual void RequestPathAccess(const std::string& path,
                                  const int interface_id,
@@ -56,6 +53,13 @@
                                   weak_ptr_factory_.GetWeakPtr(), callback));
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    proxy_ =
+        bus->GetObjectProxy(kPermissionBrokerServiceName,
+                            dbus::ObjectPath(kPermissionBrokerServicePath));
+  }
+
  private:
   // Handle a DBus response from the permission broker, invoking the callback
   // that the method was originally called with with the success response.
@@ -88,6 +92,7 @@
   PermissionBrokerClientStubImpl() {}
   virtual ~PermissionBrokerClientStubImpl() {}
 
+  virtual void Init(dbus::Bus* bus) OVERRIDE {}
   virtual void RequestPathAccess(const std::string& path,
                                  int interface_id,
                                  const ResultCallback& callback) OVERRIDE {
@@ -110,9 +115,9 @@
 PermissionBrokerClient::~PermissionBrokerClient() {}
 
 PermissionBrokerClient* PermissionBrokerClient::Create(
-    DBusClientImplementationType type, dbus::Bus* bus) {
+    DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new PermissionBrokerClientImpl(bus);
+    return new PermissionBrokerClientImpl();
   return new PermissionBrokerClientStubImpl();
 }
 
diff --git a/chromeos/dbus/permission_broker_client.h b/chromeos/dbus/permission_broker_client.h
index 645bcca..5780d8c 100644
--- a/chromeos/dbus/permission_broker_client.h
+++ b/chromeos/dbus/permission_broker_client.h
@@ -10,12 +10,9 @@
 #include "base/basictypes.h"
 #include "base/callback.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 
-namespace dbus {
-class Bus;
-}  // namespace dbus
-
 namespace chromeos {
 
 // PermissionBrokerClient is used to communicate with the permission broker, a
@@ -25,7 +22,7 @@
 // which the user the browser runs under normally wouldn't have access to. For
 // more details on the permission broker see:
 // http://git.chromium.org/gitweb/?p=chromiumos/platform/permission_broker.git
-class CHROMEOS_EXPORT PermissionBrokerClient {
+class CHROMEOS_EXPORT PermissionBrokerClient : public DBusClient {
  public:
   // The ResultCallback is used for both the RequestPathAccess and
   // RequestUsbAcess methods. Its boolean parameter represents the result of the
@@ -34,8 +31,7 @@
 
   virtual ~PermissionBrokerClient();
 
-  static PermissionBrokerClient* Create(DBusClientImplementationType type,
-                                        dbus::Bus* bus);
+  static PermissionBrokerClient* Create(DBusClientImplementationType type);
 
   // RequestPathAccess requests access to a single device node identified by
   // |path|. If |interface_id| value is passed (different than
diff --git a/chromeos/dbus/power_manager_client.cc b/chromeos/dbus/power_manager_client.cc
index 0669b44..548b053 100644
--- a/chromeos/dbus/power_manager_client.cc
+++ b/chromeos/dbus/power_manager_client.cc
@@ -41,7 +41,7 @@
 // The PowerManagerClient implementation used in production.
 class PowerManagerClientImpl : public PowerManagerClient {
  public:
-  explicit PowerManagerClientImpl(dbus::Bus* bus)
+  PowerManagerClientImpl()
       : origin_thread_id_(base::PlatformThread::CurrentId()),
         power_manager_proxy_(NULL),
         suspend_delay_id_(-1),
@@ -50,95 +50,7 @@
         suspend_is_pending_(false),
         num_pending_suspend_readiness_callbacks_(0),
         last_is_projecting_(false),
-        weak_ptr_factory_(this) {
-    power_manager_proxy_ = bus->GetObjectProxy(
-        power_manager::kPowerManagerServiceName,
-        dbus::ObjectPath(power_manager::kPowerManagerServicePath));
-
-    power_manager_proxy_->SetNameOwnerChangedCallback(
-        base::Bind(&PowerManagerClientImpl::NameOwnerChangedReceived,
-                   weak_ptr_factory_.GetWeakPtr()));
-
-    // Monitor the D-Bus signal for brightness changes. Only the power
-    // manager knows the actual brightness level. We don't cache the
-    // brightness level in Chrome as it'll make things less reliable.
-    power_manager_proxy_->ConnectToSignal(
-        power_manager::kPowerManagerInterface,
-        power_manager::kBrightnessChangedSignal,
-        base::Bind(&PowerManagerClientImpl::BrightnessChangedReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&PowerManagerClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-
-    power_manager_proxy_->ConnectToSignal(
-        power_manager::kPowerManagerInterface,
-        power_manager::kPeripheralBatteryStatusSignal,
-        base::Bind(&PowerManagerClientImpl::PeripheralBatteryStatusReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&PowerManagerClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-
-    power_manager_proxy_->ConnectToSignal(
-        power_manager::kPowerManagerInterface,
-        power_manager::kPowerSupplyPollSignal,
-        base::Bind(&PowerManagerClientImpl::PowerSupplyPollReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&PowerManagerClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-
-    power_manager_proxy_->ConnectToSignal(
-        power_manager::kPowerManagerInterface,
-        power_manager::kIdleNotifySignal,
-        base::Bind(&PowerManagerClientImpl::IdleNotifySignalReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&PowerManagerClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-
-    power_manager_proxy_->ConnectToSignal(
-        power_manager::kPowerManagerInterface,
-        power_manager::kInputEventSignal,
-        base::Bind(&PowerManagerClientImpl::InputEventReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&PowerManagerClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-
-    power_manager_proxy_->ConnectToSignal(
-        power_manager::kPowerManagerInterface,
-        power_manager::kSuspendStateChangedSignal,
-        base::Bind(&PowerManagerClientImpl::SuspendStateChangedReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&PowerManagerClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-
-    power_manager_proxy_->ConnectToSignal(
-        power_manager::kPowerManagerInterface,
-        power_manager::kSuspendImminentSignal,
-        base::Bind(
-            &PowerManagerClientImpl::SuspendImminentReceived,
-            weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&PowerManagerClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-
-    power_manager_proxy_->ConnectToSignal(
-        power_manager::kPowerManagerInterface,
-        power_manager::kIdleActionImminentSignal,
-        base::Bind(
-            &PowerManagerClientImpl::IdleActionImminentReceived,
-            weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&PowerManagerClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-
-    power_manager_proxy_->ConnectToSignal(
-        power_manager::kPowerManagerInterface,
-        power_manager::kIdleActionDeferredSignal,
-        base::Bind(
-            &PowerManagerClientImpl::IdleActionDeferredReceived,
-            weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&PowerManagerClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-
-    RegisterSuspendDelay();
-  }
+        weak_ptr_factory_(this) {}
 
   virtual ~PowerManagerClientImpl() {
     // Here we should unregister suspend notifications from powerd,
@@ -311,6 +223,97 @@
                       weak_ptr_factory_.GetWeakPtr(), pending_suspend_id_);
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    power_manager_proxy_ = bus->GetObjectProxy(
+        power_manager::kPowerManagerServiceName,
+        dbus::ObjectPath(power_manager::kPowerManagerServicePath));
+
+    power_manager_proxy_->SetNameOwnerChangedCallback(
+        base::Bind(&PowerManagerClientImpl::NameOwnerChangedReceived,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    // Monitor the D-Bus signal for brightness changes. Only the power
+    // manager knows the actual brightness level. We don't cache the
+    // brightness level in Chrome as it'll make things less reliable.
+    power_manager_proxy_->ConnectToSignal(
+        power_manager::kPowerManagerInterface,
+        power_manager::kBrightnessChangedSignal,
+        base::Bind(&PowerManagerClientImpl::BrightnessChangedReceived,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&PowerManagerClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    power_manager_proxy_->ConnectToSignal(
+        power_manager::kPowerManagerInterface,
+        power_manager::kPeripheralBatteryStatusSignal,
+        base::Bind(&PowerManagerClientImpl::PeripheralBatteryStatusReceived,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&PowerManagerClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    power_manager_proxy_->ConnectToSignal(
+        power_manager::kPowerManagerInterface,
+        power_manager::kPowerSupplyPollSignal,
+        base::Bind(&PowerManagerClientImpl::PowerSupplyPollReceived,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&PowerManagerClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    power_manager_proxy_->ConnectToSignal(
+        power_manager::kPowerManagerInterface,
+        power_manager::kIdleNotifySignal,
+        base::Bind(&PowerManagerClientImpl::IdleNotifySignalReceived,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&PowerManagerClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    power_manager_proxy_->ConnectToSignal(
+        power_manager::kPowerManagerInterface,
+        power_manager::kInputEventSignal,
+        base::Bind(&PowerManagerClientImpl::InputEventReceived,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&PowerManagerClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    power_manager_proxy_->ConnectToSignal(
+        power_manager::kPowerManagerInterface,
+        power_manager::kSuspendStateChangedSignal,
+        base::Bind(&PowerManagerClientImpl::SuspendStateChangedReceived,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&PowerManagerClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    power_manager_proxy_->ConnectToSignal(
+        power_manager::kPowerManagerInterface,
+        power_manager::kSuspendImminentSignal,
+        base::Bind(
+            &PowerManagerClientImpl::SuspendImminentReceived,
+            weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&PowerManagerClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    power_manager_proxy_->ConnectToSignal(
+        power_manager::kPowerManagerInterface,
+        power_manager::kIdleActionImminentSignal,
+        base::Bind(
+            &PowerManagerClientImpl::IdleActionImminentReceived,
+            weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&PowerManagerClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    power_manager_proxy_->ConnectToSignal(
+        power_manager::kPowerManagerInterface,
+        power_manager::kIdleActionDeferredSignal,
+        base::Bind(
+            &PowerManagerClientImpl::IdleActionDeferredReceived,
+            weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&PowerManagerClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    RegisterSuspendDelay();
+  }
+
  private:
   // Returns true if the current thread is the origin thread.
   bool OnOriginThread() {
@@ -671,7 +674,12 @@
         brightness_(50.0),
         pause_count_(2),
         cycle_count_(0),
-        weak_ptr_factory_(this) {
+        weak_ptr_factory_(this) {}
+
+  virtual ~PowerManagerClientStubImpl() {}
+
+  // PowerManagerClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
     if (CommandLine::ForCurrentProcess()->HasSwitch(
         chromeos::switches::kEnableStubInteractive)) {
       const int kStatusUpdateMs = 1000;
@@ -681,10 +689,6 @@
     }
   }
 
-  virtual ~PowerManagerClientStubImpl() {}
-
-  // PowerManagerClient overrides:
-
   virtual void AddObserver(Observer* observer) OVERRIDE {
     observers_.AddObserver(observer);
   }
@@ -856,10 +860,9 @@
 
 // static
 PowerManagerClient* PowerManagerClient::Create(
-    DBusClientImplementationType type,
-    dbus::Bus* bus) {
+    DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new PowerManagerClientImpl(bus);
+    return new PowerManagerClientImpl();
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new PowerManagerClientStubImpl();
 }
diff --git a/chromeos/dbus/power_manager_client.h b/chromeos/dbus/power_manager_client.h
index 5ddd711..e728f10 100644
--- a/chromeos/dbus/power_manager_client.h
+++ b/chromeos/dbus/power_manager_client.h
@@ -11,13 +11,10 @@
 #include "base/callback.h"
 #include "base/time/time.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
-namespace dbus {
-class Bus;
-}
-
 namespace power_manager {
 class PowerManagementPolicy;
 class PowerSupplyProperties;
@@ -32,7 +29,7 @@
 typedef base::Callback<void(double)> GetScreenBrightnessPercentCallback;
 
 // PowerManagerClient is used to communicate with the power manager.
-class CHROMEOS_EXPORT PowerManagerClient {
+class CHROMEOS_EXPORT PowerManagerClient : public DBusClient {
  public:
   // Interface for observing changes from the power manager.
   class Observer {
@@ -161,8 +158,7 @@
   virtual base::Closure GetSuspendReadinessCallback() = 0;
 
   // Creates the instance.
-  static PowerManagerClient* Create(DBusClientImplementationType type,
-                                    dbus::Bus* bus);
+  static PowerManagerClient* Create(DBusClientImplementationType type);
 
   virtual ~PowerManagerClient();
 
diff --git a/chromeos/dbus/session_manager_client.cc b/chromeos/dbus/session_manager_client.cc
index e842f88..f75a9d4 100644
--- a/chromeos/dbus/session_manager_client.cc
+++ b/chromeos/dbus/session_manager_client.cc
@@ -28,66 +28,9 @@
 // The SessionManagerClient implementation used in production.
 class SessionManagerClientImpl : public SessionManagerClient {
  public:
-  explicit SessionManagerClientImpl(dbus::Bus* bus)
-      : session_manager_proxy_(bus->GetObjectProxy(
-            login_manager::kSessionManagerServiceName,
-            dbus::ObjectPath(login_manager::kSessionManagerServicePath))),
-        blocking_method_caller_(bus, session_manager_proxy_),
-        weak_ptr_factory_(this) {
-    // Signals emitted on Chromium's interface.  Many of these ought to be
-    // method calls instead.
-    session_manager_proxy_->ConnectToSignal(
-        chromium::kChromiumInterface,
-        chromium::kOwnerKeySetSignal,
-        base::Bind(&SessionManagerClientImpl::OwnerKeySetReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&SessionManagerClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-    session_manager_proxy_->ConnectToSignal(
-        chromium::kChromiumInterface,
-        chromium::kPropertyChangeCompleteSignal,
-        base::Bind(&SessionManagerClientImpl::PropertyChangeCompleteReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&SessionManagerClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-    session_manager_proxy_->ConnectToSignal(
-        chromium::kChromiumInterface,
-        chromium::kLockScreenSignal,
-        base::Bind(&SessionManagerClientImpl::ScreenLockReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&SessionManagerClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-    session_manager_proxy_->ConnectToSignal(
-        chromium::kChromiumInterface,
-        chromium::kUnlockScreenSignal,
-        base::Bind(&SessionManagerClientImpl::ScreenUnlockReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&SessionManagerClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-    session_manager_proxy_->ConnectToSignal(
-        chromium::kChromiumInterface,
-        chromium::kLivenessRequestedSignal,
-        base::Bind(&SessionManagerClientImpl::LivenessRequestedReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&SessionManagerClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-
-    // Signals emitted on the session manager's interface.
-    session_manager_proxy_->ConnectToSignal(
-        login_manager::kSessionManagerInterface,
-        login_manager::kScreenIsLockedSignal,
-        base::Bind(&SessionManagerClientImpl::ScreenIsLockedReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&SessionManagerClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-    session_manager_proxy_->ConnectToSignal(
-        login_manager::kSessionManagerInterface,
-        login_manager::kScreenIsUnlockedSignal,
-        base::Bind(&SessionManagerClientImpl::ScreenIsUnlockedReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&SessionManagerClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-  }
+  SessionManagerClientImpl()
+      : session_manager_proxy_(NULL),
+        weak_ptr_factory_(this) {}
 
   virtual ~SessionManagerClientImpl() {
   }
@@ -231,7 +174,7 @@
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(username);
     scoped_ptr<dbus::Response> response =
-        blocking_method_caller_.CallMethodAndBlock(&method_call);
+        blocking_method_caller_->CallMethodAndBlock(&method_call);
     std::string policy;
     ExtractString(login_manager::kSessionManagerRetrievePolicyForUser,
                   response.get(),
@@ -300,6 +243,69 @@
         dbus::ObjectProxy::EmptyResponseCallback());
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    session_manager_proxy_ = bus->GetObjectProxy(
+        login_manager::kSessionManagerServiceName,
+        dbus::ObjectPath(login_manager::kSessionManagerServicePath));
+    blocking_method_caller_.reset(
+        new BlockingMethodCaller(bus, session_manager_proxy_));
+
+    // Signals emitted on Chromium's interface.  Many of these ought to be
+    // method calls instead.
+    session_manager_proxy_->ConnectToSignal(
+        chromium::kChromiumInterface,
+        chromium::kOwnerKeySetSignal,
+        base::Bind(&SessionManagerClientImpl::OwnerKeySetReceived,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&SessionManagerClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+    session_manager_proxy_->ConnectToSignal(
+        chromium::kChromiumInterface,
+        chromium::kPropertyChangeCompleteSignal,
+        base::Bind(&SessionManagerClientImpl::PropertyChangeCompleteReceived,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&SessionManagerClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+    session_manager_proxy_->ConnectToSignal(
+        chromium::kChromiumInterface,
+        chromium::kLockScreenSignal,
+        base::Bind(&SessionManagerClientImpl::ScreenLockReceived,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&SessionManagerClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+    session_manager_proxy_->ConnectToSignal(
+        chromium::kChromiumInterface,
+        chromium::kUnlockScreenSignal,
+        base::Bind(&SessionManagerClientImpl::ScreenUnlockReceived,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&SessionManagerClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+    session_manager_proxy_->ConnectToSignal(
+        chromium::kChromiumInterface,
+        chromium::kLivenessRequestedSignal,
+        base::Bind(&SessionManagerClientImpl::LivenessRequestedReceived,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&SessionManagerClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    // Signals emitted on the session manager's interface.
+    session_manager_proxy_->ConnectToSignal(
+        login_manager::kSessionManagerInterface,
+        login_manager::kScreenIsLockedSignal,
+        base::Bind(&SessionManagerClientImpl::ScreenIsLockedReceived,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&SessionManagerClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+    session_manager_proxy_->ConnectToSignal(
+        login_manager::kSessionManagerInterface,
+        login_manager::kScreenIsUnlockedSignal,
+        base::Bind(&SessionManagerClientImpl::ScreenIsUnlockedReceived,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&SessionManagerClientImpl::SignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+  }
+
  private:
   // Makes a method call to the session manager with no arguments and no
   // response.
@@ -513,7 +519,7 @@
   }
 
   dbus::ObjectProxy* session_manager_proxy_;
-  BlockingMethodCaller blocking_method_caller_;
+  scoped_ptr<BlockingMethodCaller> blocking_method_caller_;
   ObserverList<Observer> observers_;
 
   // Note: This should remain the last member so it'll be destroyed and
@@ -527,7 +533,11 @@
 // which does nothing.
 class SessionManagerClientStubImpl : public SessionManagerClient {
  public:
-  SessionManagerClientStubImpl() {
+  SessionManagerClientStubImpl() {}
+  virtual ~SessionManagerClientStubImpl() {}
+
+  // SessionManagerClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
     // Make sure that there are no keys left over from a previous browser run.
     base::FilePath user_policy_key_dir;
     if (PathService::Get(chromeos::DIR_USER_POLICY_KEYS,
@@ -539,9 +549,7 @@
           false);
     }
   }
-  virtual ~SessionManagerClientStubImpl() {}
 
-  // SessionManagerClient overrides.
   virtual void AddObserver(Observer* observer) OVERRIDE {
     observers_.AddObserver(observer);
   }
@@ -664,10 +672,9 @@
 }
 
 SessionManagerClient* SessionManagerClient::Create(
-    DBusClientImplementationType type,
-    dbus::Bus* bus) {
+    DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new SessionManagerClientImpl(bus);
+    return new SessionManagerClientImpl();
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new SessionManagerClientStubImpl();
 }
diff --git a/chromeos/dbus/session_manager_client.h b/chromeos/dbus/session_manager_client.h
index 45b9b14..f09a041 100644
--- a/chromeos/dbus/session_manager_client.h
+++ b/chromeos/dbus/session_manager_client.h
@@ -11,16 +11,13 @@
 #include "base/callback.h"
 #include "base/observer_list.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 
-namespace dbus {
-class Bus;
-}  // namespace dbus
-
 namespace chromeos {
 
 // SessionManagerClient is used to communicate with the session manager.
-class CHROMEOS_EXPORT SessionManagerClient {
+class CHROMEOS_EXPORT SessionManagerClient : public DBusClient {
  public:
   // Interface for observing changes from the session manager.
   class Observer {
@@ -173,8 +170,7 @@
                                const std::vector<std::string>& flags) = 0;
 
   // Creates the instance.
-  static SessionManagerClient* Create(DBusClientImplementationType type,
-                                      dbus::Bus* bus);
+  static SessionManagerClient* Create(DBusClientImplementationType type);
 
   virtual ~SessionManagerClient();
 
diff --git a/chromeos/dbus/shill_client_helper.cc b/chromeos/dbus/shill_client_helper.cc
index 61dcb4d..a0af3b8 100644
--- a/chromeos/dbus/shill_client_helper.cc
+++ b/chromeos/dbus/shill_client_helper.cc
@@ -180,9 +180,8 @@
 }
 
 ShillClientHelper::~ShillClientHelper() {
-  LOG_IF(ERROR, observer_list_.size() != 0u)
-      << "ShillClientHelper destroyed with active observers: "
-      << observer_list_.size();
+  LOG_IF(ERROR, observer_list_.might_have_observers())
+      << "ShillClientHelper destroyed with active observers";
 }
 
 void ShillClientHelper::AddPropertyChangedObserver(
@@ -203,7 +202,7 @@
 
 void ShillClientHelper::MonitorPropertyChanged(
     const std::string& interface_name) {
-  if (observer_list_.size() > 0) {
+  if (observer_list_.might_have_observers()) {
     // Effectively monitor the PropertyChanged now.
     MonitorPropertyChangedInternal(interface_name);
   } else {
diff --git a/chromeos/dbus/shill_device_client.cc b/chromeos/dbus/shill_device_client.cc
index ccab2b9..420f586 100644
--- a/chromeos/dbus/shill_device_client.cc
+++ b/chromeos/dbus/shill_device_client.cc
@@ -24,8 +24,8 @@
 // The ShillDeviceClient implementation.
 class ShillDeviceClientImpl : public ShillDeviceClient {
  public:
-  explicit ShillDeviceClientImpl(dbus::Bus* bus)
-      : bus_(bus) {
+  explicit ShillDeviceClientImpl()
+      : bus_(NULL) {
   }
 
   virtual ~ShillDeviceClientImpl() {
@@ -196,6 +196,11 @@
     return NULL;
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    bus_ = bus;
+  }
+
  private:
   typedef std::map<std::string, ShillClientHelper*> HelperMap;
 
@@ -231,10 +236,9 @@
 
 // static
 ShillDeviceClient* ShillDeviceClient::Create(
-    DBusClientImplementationType type,
-    dbus::Bus* bus) {
+    DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new ShillDeviceClientImpl(bus);
+    return new ShillDeviceClientImpl();
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new ShillDeviceClientStub();
 }
diff --git a/chromeos/dbus/shill_device_client.h b/chromeos/dbus/shill_device_client.h
index 2047059..4600e37 100644
--- a/chromeos/dbus/shill_device_client.h
+++ b/chromeos/dbus/shill_device_client.h
@@ -10,6 +10,7 @@
 #include "base/basictypes.h"
 #include "base/callback.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 #include "chromeos/dbus/shill_client_helper.h"
 
@@ -22,7 +23,6 @@
 
 namespace dbus {
 
-class Bus;
 class ObjectPath;
 
 }  // namespace dbus
@@ -34,7 +34,7 @@
 // ShillDeviceClient is used to communicate with the Shill Device service.
 // All methods should be called from the origin thread which initializes the
 // DBusThreadManager instance.
-class CHROMEOS_EXPORT ShillDeviceClient {
+class CHROMEOS_EXPORT ShillDeviceClient : public DBusClient {
  public:
   typedef ShillClientHelper::PropertyChangedHandler PropertyChangedHandler;
   typedef ShillClientHelper::DictionaryValueCallback DictionaryValueCallback;
@@ -62,8 +62,7 @@
 
   // Factory function, creates a new instance which is owned by the caller.
   // For normal usage, access the singleton via DBusThreadManager::Get().
-  static ShillDeviceClient* Create(DBusClientImplementationType type,
-                                      dbus::Bus* bus);
+  static ShillDeviceClient* Create(DBusClientImplementationType type);
 
   // Adds a property changed |observer| for the device at |device_path|.
   virtual void AddPropertyChangedObserver(
@@ -160,6 +159,8 @@
   virtual TestInterface* GetTestInterface() = 0;
 
  protected:
+  friend class ShillDeviceClientTest;
+
   // Create() should be used instead.
   ShillDeviceClient();
 
diff --git a/chromeos/dbus/shill_device_client_stub.cc b/chromeos/dbus/shill_device_client_stub.cc
index f7f39da..a32febd 100644
--- a/chromeos/dbus/shill_device_client_stub.cc
+++ b/chromeos/dbus/shill_device_client_stub.cc
@@ -30,7 +30,6 @@
 }  // namespace
 
 ShillDeviceClientStub::ShillDeviceClientStub() : weak_ptr_factory_(this) {
-  SetDefaultProperties();
 }
 
 ShillDeviceClientStub::~ShillDeviceClientStub() {
@@ -40,6 +39,10 @@
 
 // ShillDeviceClient overrides.
 
+void ShillDeviceClientStub::Init(dbus::Bus* bus) {
+  SetDefaultProperties();
+}
+
 void ShillDeviceClientStub::AddPropertyChangedObserver(
     const dbus::ObjectPath& device_path,
     ShillPropertyChangedObserver* observer){
diff --git a/chromeos/dbus/shill_device_client_stub.h b/chromeos/dbus/shill_device_client_stub.h
index 8038cc7..fe2ca8c 100644
--- a/chromeos/dbus/shill_device_client_stub.h
+++ b/chromeos/dbus/shill_device_client_stub.h
@@ -20,7 +20,8 @@
   ShillDeviceClientStub();
   virtual ~ShillDeviceClientStub();
 
-  // ShillDeviceClient overrides.
+  // ShillDeviceClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void AddPropertyChangedObserver(
       const dbus::ObjectPath& device_path,
       ShillPropertyChangedObserver* observer) OVERRIDE;
diff --git a/chromeos/dbus/shill_device_client_unittest.cc b/chromeos/dbus/shill_device_client_unittest.cc
index 4149b17..52e50a9 100644
--- a/chromeos/dbus/shill_device_client_unittest.cc
+++ b/chromeos/dbus/shill_device_client_unittest.cc
@@ -59,8 +59,8 @@
   virtual void SetUp() {
     ShillClientUnittestBase::SetUp();
     // Create a client with the mock bus.
-    client_.reset(ShillDeviceClient::Create(REAL_DBUS_CLIENT_IMPLEMENTATION,
-                                            mock_bus_.get()));
+    client_.reset(ShillDeviceClient::Create(REAL_DBUS_CLIENT_IMPLEMENTATION));
+    client_->Init(mock_bus_.get());
     // Run the message loop to run the signal connection result callback.
     message_loop_.RunUntilIdle();
   }
diff --git a/chromeos/dbus/shill_ipconfig_client.cc b/chromeos/dbus/shill_ipconfig_client.cc
index 2171590..6c6c750 100644
--- a/chromeos/dbus/shill_ipconfig_client.cc
+++ b/chromeos/dbus/shill_ipconfig_client.cc
@@ -24,7 +24,7 @@
 // The ShillIPConfigClient implementation.
 class ShillIPConfigClientImpl : public ShillIPConfigClient {
  public:
-  explicit ShillIPConfigClientImpl(dbus::Bus* bus);
+  ShillIPConfigClientImpl();
 
   ////////////////////////////////////
   // ShillIPConfigClient overrides.
@@ -55,6 +55,11 @@
   virtual void Remove(const dbus::ObjectPath& ipconfig_path,
                       const VoidDBusMethodCallback& callback) OVERRIDE;
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    bus_ = bus;
+  }
+
  private:
   typedef std::map<std::string, ShillClientHelper*> HelperMap;
 
@@ -80,8 +85,8 @@
   DISALLOW_COPY_AND_ASSIGN(ShillIPConfigClientImpl);
 };
 
-ShillIPConfigClientImpl::ShillIPConfigClientImpl(dbus::Bus* bus)
-    : bus_(bus),
+ShillIPConfigClientImpl::ShillIPConfigClientImpl()
+    : bus_(NULL),
       helpers_deleter_(&helpers_) {
 }
 
@@ -178,10 +183,9 @@
 
 // static
 ShillIPConfigClient* ShillIPConfigClient::Create(
-    DBusClientImplementationType type,
-    dbus::Bus* bus) {
+    DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new ShillIPConfigClientImpl(bus);
+    return new ShillIPConfigClientImpl();
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new ShillIPConfigClientStub();
 }
diff --git a/chromeos/dbus/shill_ipconfig_client.h b/chromeos/dbus/shill_ipconfig_client.h
index b311c56..df6db23 100644
--- a/chromeos/dbus/shill_ipconfig_client.h
+++ b/chromeos/dbus/shill_ipconfig_client.h
@@ -10,6 +10,7 @@
 #include "base/basictypes.h"
 #include "base/callback.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 #include "chromeos/dbus/shill_client_helper.h"
 
@@ -22,7 +23,6 @@
 
 namespace dbus {
 
-class Bus;
 class ObjectPath;
 
 }  // namespace dbus
@@ -34,7 +34,7 @@
 // ShillIPConfigClient is used to communicate with the Shill IPConfig
 // service.  All methods should be called from the origin thread which
 // initializes the DBusThreadManager instance.
-class CHROMEOS_EXPORT ShillIPConfigClient {
+class CHROMEOS_EXPORT ShillIPConfigClient : public DBusClient {
  public:
   typedef ShillClientHelper::PropertyChangedHandler PropertyChangedHandler;
   typedef ShillClientHelper::DictionaryValueCallback DictionaryValueCallback;
@@ -42,8 +42,7 @@
 
   // Factory function, creates a new instance which is owned by the caller.
   // For normal usage, access the singleton via DBusThreadManager::Get().
-  static ShillIPConfigClient* Create(DBusClientImplementationType type,
-                                        dbus::Bus* bus);
+  static ShillIPConfigClient* Create(DBusClientImplementationType type);
 
   // Adds a property changed |observer| for the ipconfig at |ipconfig_path|.
   virtual void AddPropertyChangedObserver(
@@ -93,6 +92,8 @@
                       const VoidDBusMethodCallback& callback) = 0;
 
  protected:
+  friend class ShillIPConfigClientTest;
+
   // Create() should be used instead.
   ShillIPConfigClient();
 
diff --git a/chromeos/dbus/shill_ipconfig_client_stub.cc b/chromeos/dbus/shill_ipconfig_client_stub.cc
index ccccfd4..a29d479 100644
--- a/chromeos/dbus/shill_ipconfig_client_stub.cc
+++ b/chromeos/dbus/shill_ipconfig_client_stub.cc
@@ -24,6 +24,9 @@
 ShillIPConfigClientStub::~ShillIPConfigClientStub() {
 }
 
+void ShillIPConfigClientStub::Init(dbus::Bus* bus) {
+}
+
 void ShillIPConfigClientStub::AddPropertyChangedObserver(
     const dbus::ObjectPath& ipconfig_path,
     ShillPropertyChangedObserver* observer) {
diff --git a/chromeos/dbus/shill_ipconfig_client_stub.h b/chromeos/dbus/shill_ipconfig_client_stub.h
index bc77486..1122d77 100644
--- a/chromeos/dbus/shill_ipconfig_client_stub.h
+++ b/chromeos/dbus/shill_ipconfig_client_stub.h
@@ -18,7 +18,8 @@
   ShillIPConfigClientStub();
   virtual ~ShillIPConfigClientStub();
 
-  // ShillIPConfigClient overrides:
+  // ShillIPConfigClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void AddPropertyChangedObserver(
       const dbus::ObjectPath& ipconfig_path,
       ShillPropertyChangedObserver* observer) OVERRIDE;
diff --git a/chromeos/dbus/shill_ipconfig_client_unittest.cc b/chromeos/dbus/shill_ipconfig_client_unittest.cc
index 424935f..8ea537e 100644
--- a/chromeos/dbus/shill_ipconfig_client_unittest.cc
+++ b/chromeos/dbus/shill_ipconfig_client_unittest.cc
@@ -33,8 +33,8 @@
   virtual void SetUp() {
     ShillClientUnittestBase::SetUp();
     // Create a client with the mock bus.
-    client_.reset(ShillIPConfigClient::Create(REAL_DBUS_CLIENT_IMPLEMENTATION,
-                                              mock_bus_.get()));
+    client_.reset(ShillIPConfigClient::Create(REAL_DBUS_CLIENT_IMPLEMENTATION));
+    client_->Init(mock_bus_.get());
     // Run the message loop to run the signal connection result callback.
     message_loop_.RunUntilIdle();
   }
diff --git a/chromeos/dbus/shill_manager_client.cc b/chromeos/dbus/shill_manager_client.cc
index fca8ba0..70f2095 100644
--- a/chromeos/dbus/shill_manager_client.cc
+++ b/chromeos/dbus/shill_manager_client.cc
@@ -24,37 +24,31 @@
 // The ShillManagerClient implementation.
 class ShillManagerClientImpl : public ShillManagerClient {
  public:
-  explicit ShillManagerClientImpl(dbus::Bus* bus)
-      : proxy_(bus->GetObjectProxy(
-          flimflam::kFlimflamServiceName,
-          dbus::ObjectPath(flimflam::kFlimflamServicePath))),
-        helper_(bus, proxy_) {
-    helper_.MonitorPropertyChanged(flimflam::kFlimflamManagerInterface);
-  }
+  ShillManagerClientImpl() : proxy_(NULL) {}
 
   ////////////////////////////////////
   // ShillManagerClient overrides.
   virtual void AddPropertyChangedObserver(
       ShillPropertyChangedObserver* observer) OVERRIDE {
-    helper_.AddPropertyChangedObserver(observer);
+    helper_->AddPropertyChangedObserver(observer);
   }
 
   virtual void RemovePropertyChangedObserver(
       ShillPropertyChangedObserver* observer) OVERRIDE {
-    helper_.RemovePropertyChangedObserver(observer);
+    helper_->RemovePropertyChangedObserver(observer);
   }
 
   virtual void GetProperties(const DictionaryValueCallback& callback) OVERRIDE {
     dbus::MethodCall method_call(flimflam::kFlimflamManagerInterface,
                                  flimflam::kGetPropertiesFunction);
-    helper_.CallDictionaryValueMethod(&method_call, callback);
+    helper_->CallDictionaryValueMethod(&method_call, callback);
   }
 
   virtual void GetNetworksForGeolocation(
       const DictionaryValueCallback& callback) OVERRIDE {
     dbus::MethodCall method_call(flimflam::kFlimflamManagerInterface,
                                  shill::kGetNetworksForGeolocation);
-    helper_.CallDictionaryValueMethod(&method_call, callback);
+    helper_->CallDictionaryValueMethod(&method_call, callback);
   }
 
   virtual void SetProperty(const std::string& name,
@@ -66,7 +60,7 @@
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(name);
     ShillClientHelper::AppendValueDataAsVariant(&writer, value);
-    helper_.CallVoidMethodWithErrorCallback(&method_call,
+    helper_->CallVoidMethodWithErrorCallback(&method_call,
                                             callback,
                                             error_callback);
   }
@@ -78,7 +72,7 @@
                                  flimflam::kRequestScanFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(type);
-    helper_.CallVoidMethodWithErrorCallback(&method_call,
+    helper_->CallVoidMethodWithErrorCallback(&method_call,
                                             callback,
                                             error_callback);
   }
@@ -91,7 +85,7 @@
                                  flimflam::kEnableTechnologyFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(type);
-    helper_.CallVoidMethodWithErrorCallback(&method_call,
+    helper_->CallVoidMethodWithErrorCallback(&method_call,
                                             callback,
                                             error_callback);
   }
@@ -104,7 +98,7 @@
                                  flimflam::kDisableTechnologyFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(type);
-    helper_.CallVoidMethodWithErrorCallback(&method_call,
+    helper_->CallVoidMethodWithErrorCallback(&method_call,
                                             callback,
                                             error_callback);
   }
@@ -117,7 +111,7 @@
                                  flimflam::kConfigureServiceFunction);
     dbus::MessageWriter writer(&method_call);
     ShillClientHelper::AppendServicePropertiesDictionary(&writer, properties);
-    helper_.CallObjectPathMethodWithErrorCallback(&method_call,
+    helper_->CallObjectPathMethodWithErrorCallback(&method_call,
                                                   callback,
                                                   error_callback);
   }
@@ -132,7 +126,7 @@
     dbus::MessageWriter writer(&method_call);
     writer.AppendObjectPath(dbus::ObjectPath(profile_path));
     ShillClientHelper::AppendServicePropertiesDictionary(&writer, properties);
-    helper_.CallObjectPathMethodWithErrorCallback(&method_call,
+    helper_->CallObjectPathMethodWithErrorCallback(&method_call,
                                                   callback,
                                                   error_callback);
   }
@@ -145,7 +139,7 @@
                                  flimflam::kGetServiceFunction);
     dbus::MessageWriter writer(&method_call);
     ShillClientHelper::AppendServicePropertiesDictionary(&writer, properties);
-    helper_.CallObjectPathMethodWithErrorCallback(&method_call,
+    helper_->CallObjectPathMethodWithErrorCallback(&method_call,
                                                   callback,
                                                   error_callback);
   }
@@ -163,7 +157,7 @@
     writer.AppendString(properties.device_serial);
     writer.AppendString(properties.device_ssid);
     writer.AppendString(properties.device_bssid);
-    helper_.CallBooleanMethodWithErrorCallback(
+    helper_->CallBooleanMethodWithErrorCallback(
         &method_call, callback, error_callback);
   }
 
@@ -183,7 +177,7 @@
     writer.AppendString(properties.device_ssid);
     writer.AppendString(properties.device_bssid);
     writer.AppendObjectPath(dbus::ObjectPath(service_path));
-    helper_.CallStringMethodWithErrorCallback(
+    helper_->CallStringMethodWithErrorCallback(
         &method_call, callback, error_callback);
   }
 
@@ -203,7 +197,7 @@
     writer.AppendString(properties.device_ssid);
     writer.AppendString(properties.device_bssid);
     writer.AppendString(data);
-    helper_.CallStringMethodWithErrorCallback(
+    helper_->CallStringMethodWithErrorCallback(
         &method_call, callback, error_callback);
   }
 
@@ -212,7 +206,7 @@
       const ErrorCallback& error_callback) OVERRIDE {
     dbus::MethodCall method_call(flimflam::kFlimflamManagerInterface,
                                  shill::kConnectToBestServicesFunction);
-    helper_.CallVoidMethodWithErrorCallback(&method_call,
+    helper_->CallVoidMethodWithErrorCallback(&method_call,
                                             callback,
                                             error_callback);
   }
@@ -221,9 +215,18 @@
     return NULL;
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    proxy_ =
+        bus->GetObjectProxy(flimflam::kFlimflamServiceName,
+                            dbus::ObjectPath(flimflam::kFlimflamServicePath));
+    helper_.reset(new ShillClientHelper(bus, proxy_));
+    helper_->MonitorPropertyChanged(flimflam::kFlimflamManagerInterface);
+  }
+
  private:
   dbus::ObjectProxy* proxy_;
-  ShillClientHelper helper_;
+  scoped_ptr<ShillClientHelper> helper_;
 
   DISALLOW_COPY_AND_ASSIGN(ShillManagerClientImpl);
 };
@@ -236,10 +239,9 @@
 
 // static
 ShillManagerClient* ShillManagerClient::Create(
-    DBusClientImplementationType type,
-    dbus::Bus* bus) {
+    DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new ShillManagerClientImpl(bus);
+    return new ShillManagerClientImpl();
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new ShillManagerClientStub();
 }
diff --git a/chromeos/dbus/shill_manager_client.h b/chromeos/dbus/shill_manager_client.h
index ce06815..c9307ca 100644
--- a/chromeos/dbus/shill_manager_client.h
+++ b/chromeos/dbus/shill_manager_client.h
@@ -9,13 +9,13 @@
 
 #include "base/basictypes.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 #include "chromeos/dbus/dbus_method_call_status.h"
 #include "chromeos/dbus/shill_client_helper.h"
 
 namespace dbus {
 
-class Bus;
 class ObjectPath;
 
 }  // namespace dbus
@@ -27,7 +27,7 @@
 // ShillManagerClient is used to communicate with the Shill Manager
 // service.  All methods should be called from the origin thread which
 // initializes the DBusThreadManager instance.
-class CHROMEOS_EXPORT ShillManagerClient {
+class CHROMEOS_EXPORT ShillManagerClient : public DBusClient {
  public:
   typedef ShillClientHelper::PropertyChangedHandler PropertyChangedHandler;
   typedef ShillClientHelper::DictionaryValueCallback DictionaryValueCallback;
@@ -109,8 +109,7 @@
 
   // Factory function, creates a new instance which is owned by the caller.
   // For normal usage, access the singleton via DBusThreadManager::Get().
-  static ShillManagerClient* Create(DBusClientImplementationType type,
-                                    dbus::Bus* bus);
+  static ShillManagerClient* Create(DBusClientImplementationType type);
 
   // Adds a property changed |observer|.
   virtual void AddPropertyChangedObserver(
@@ -207,6 +206,8 @@
   virtual TestInterface* GetTestInterface() = 0;
 
  protected:
+  friend class ShillManagerClientTest;
+
   // Create() should be used instead.
   ShillManagerClient();
 
diff --git a/chromeos/dbus/shill_manager_client_stub.cc b/chromeos/dbus/shill_manager_client_stub.cc
index 640949a..0614874 100644
--- a/chromeos/dbus/shill_manager_client_stub.cc
+++ b/chromeos/dbus/shill_manager_client_stub.cc
@@ -78,13 +78,16 @@
 
 ShillManagerClientStub::ShillManagerClientStub()
     : weak_ptr_factory_(this) {
-  SetDefaultProperties();
 }
 
 ShillManagerClientStub::~ShillManagerClientStub() {}
 
 // ShillManagerClient overrides.
 
+void ShillManagerClientStub::Init(dbus::Bus* bus) {
+  SetDefaultProperties();
+}
+
 void ShillManagerClientStub::AddPropertyChangedObserver(
     ShillPropertyChangedObserver* observer) {
   observer_list_.AddObserver(observer);
@@ -523,7 +526,7 @@
     int delay_ms) {
   // Avoid unnecessary delayed task if we have no observers (e.g. during
   // initial setup).
-  if (observer_list_.size() == 0)
+  if (!observer_list_.might_have_observers())
     return;
   if (!CommandLine::ForCurrentProcess()->HasSwitch(
           chromeos::switches::kEnableStubInteractive)) {
diff --git a/chromeos/dbus/shill_manager_client_stub.h b/chromeos/dbus/shill_manager_client_stub.h
index 678922f..f6edc15 100644
--- a/chromeos/dbus/shill_manager_client_stub.h
+++ b/chromeos/dbus/shill_manager_client_stub.h
@@ -22,7 +22,8 @@
   ShillManagerClientStub();
   virtual ~ShillManagerClientStub();
 
-  // ShillManagerClient overrides.
+  // ShillManagerClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void AddPropertyChangedObserver(
       ShillPropertyChangedObserver* observer) OVERRIDE;
   virtual void RemovePropertyChangedObserver(
diff --git a/chromeos/dbus/shill_manager_client_unittest.cc b/chromeos/dbus/shill_manager_client_unittest.cc
index e2feccf..107458d 100644
--- a/chromeos/dbus/shill_manager_client_unittest.cc
+++ b/chromeos/dbus/shill_manager_client_unittest.cc
@@ -60,8 +60,8 @@
   virtual void SetUp() {
     ShillClientUnittestBase::SetUp();
     // Create a client with the mock bus.
-    client_.reset(ShillManagerClient::Create(REAL_DBUS_CLIENT_IMPLEMENTATION,
-                                             mock_bus_.get()));
+    client_.reset(ShillManagerClient::Create(REAL_DBUS_CLIENT_IMPLEMENTATION));
+    client_->Init(mock_bus_.get());
     // Run the message loop to run the signal connection result callback.
     message_loop_.RunUntilIdle();
   }
diff --git a/chromeos/dbus/shill_profile_client.cc b/chromeos/dbus/shill_profile_client.cc
index 696be7f..c0f675d 100644
--- a/chromeos/dbus/shill_profile_client.cc
+++ b/chromeos/dbus/shill_profile_client.cc
@@ -23,9 +23,8 @@
 
 class ShillProfileClientImpl : public ShillProfileClient {
  public:
-  explicit ShillProfileClientImpl(dbus::Bus* bus);
+  ShillProfileClientImpl();
 
-  // ShillProfileClient overrides.
   virtual void AddPropertyChangedObserver(
       const dbus::ObjectPath& profile_path,
       ShillPropertyChangedObserver* observer) OVERRIDE {
@@ -37,6 +36,7 @@
       ShillPropertyChangedObserver* observer) OVERRIDE {
     GetHelper(profile_path)->RemovePropertyChangedObserver(observer);
   }
+
   virtual void GetProperties(
       const dbus::ObjectPath& profile_path,
       const DictionaryValueCallbackWithoutStatus& callback,
@@ -54,6 +54,11 @@
     return NULL;
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    bus_ = bus;
+  }
+
  private:
   typedef std::map<std::string, ShillClientHelper*> HelperMap;
 
@@ -67,8 +72,8 @@
   DISALLOW_COPY_AND_ASSIGN(ShillProfileClientImpl);
 };
 
-ShillProfileClientImpl::ShillProfileClientImpl(dbus::Bus* bus)
-    : bus_(bus),
+ShillProfileClientImpl::ShillProfileClientImpl()
+    : bus_(NULL),
       helpers_deleter_(&helpers_) {
 }
 
@@ -131,10 +136,9 @@
 
 // static
 ShillProfileClient* ShillProfileClient::Create(
-    DBusClientImplementationType type,
-    dbus::Bus* bus) {
+    DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new ShillProfileClientImpl(bus);
+    return new ShillProfileClientImpl();
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new ShillProfileClientStub();
 }
diff --git a/chromeos/dbus/shill_profile_client.h b/chromeos/dbus/shill_profile_client.h
index bf81afb..1529120 100644
--- a/chromeos/dbus/shill_profile_client.h
+++ b/chromeos/dbus/shill_profile_client.h
@@ -10,6 +10,7 @@
 #include "base/basictypes.h"
 #include "base/callback.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 #include "chromeos/dbus/shill_client_helper.h"
 
@@ -22,7 +23,6 @@
 
 namespace dbus {
 
-class Bus;
 class ObjectPath;
 
 }  // namespace dbus
@@ -34,7 +34,7 @@
 // ShillProfileClient is used to communicate with the Shill Profile
 // service.  All methods should be called from the origin thread which
 // initializes the DBusThreadManager instance.
-class CHROMEOS_EXPORT ShillProfileClient {
+class CHROMEOS_EXPORT ShillProfileClient : public DBusClient {
  public:
   typedef ShillClientHelper::PropertyChangedHandler PropertyChangedHandler;
   typedef ShillClientHelper::DictionaryValueCallbackWithoutStatus
@@ -75,8 +75,7 @@
 
   // Factory function, creates a new instance which is owned by the caller.
   // For normal usage, access the singleton via DBusThreadManager::Get().
-  static ShillProfileClient* Create(DBusClientImplementationType type,
-                                    dbus::Bus* bus);
+  static ShillProfileClient* Create(DBusClientImplementationType type);
 
   // Adds a property changed |observer| for the profile at |profile_path|.
   virtual void AddPropertyChangedObserver(
@@ -113,6 +112,8 @@
   virtual TestInterface* GetTestInterface() = 0;
 
  protected:
+  friend class ShillProfileClientTest;
+
   // Create() should be used instead.
   ShillProfileClient();
 
diff --git a/chromeos/dbus/shill_profile_client_stub.cc b/chromeos/dbus/shill_profile_client_stub.cc
index ef3e74c..cfa97b7 100644
--- a/chromeos/dbus/shill_profile_client_stub.cc
+++ b/chromeos/dbus/shill_profile_client_stub.cc
@@ -40,13 +40,16 @@
 const char ShillProfileClientStub::kSharedProfilePath[] = "/profile/default";
 
 ShillProfileClientStub::ShillProfileClientStub() {
-  AddProfile(kSharedProfilePath, std::string());
 }
 
 ShillProfileClientStub::~ShillProfileClientStub() {
   STLDeleteValues(&profiles_);
 }
 
+void ShillProfileClientStub::Init(dbus::Bus* bus) {
+  AddProfile(kSharedProfilePath, std::string());
+}
+
 void ShillProfileClientStub::AddPropertyChangedObserver(
     const dbus::ObjectPath& profile_path,
     ShillPropertyChangedObserver* observer) {
diff --git a/chromeos/dbus/shill_profile_client_stub.h b/chromeos/dbus/shill_profile_client_stub.h
index 98d8347..63a8912 100644
--- a/chromeos/dbus/shill_profile_client_stub.h
+++ b/chromeos/dbus/shill_profile_client_stub.h
@@ -21,7 +21,8 @@
   ShillProfileClientStub();
   virtual ~ShillProfileClientStub();
 
-  // ShillProfileClient overrides.
+  // ShillProfileClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void AddPropertyChangedObserver(
       const dbus::ObjectPath& profile_path,
       ShillPropertyChangedObserver* observer) OVERRIDE;
diff --git a/chromeos/dbus/shill_profile_client_unittest.cc b/chromeos/dbus/shill_profile_client_unittest.cc
index d95b62b..3352e2d 100644
--- a/chromeos/dbus/shill_profile_client_unittest.cc
+++ b/chromeos/dbus/shill_profile_client_unittest.cc
@@ -41,8 +41,8 @@
   virtual void SetUp() {
     ShillClientUnittestBase::SetUp();
     // Create a client with the mock bus.
-    client_.reset(ShillProfileClient::Create(REAL_DBUS_CLIENT_IMPLEMENTATION,
-                                             mock_bus_.get()));
+    client_.reset(ShillProfileClient::Create(REAL_DBUS_CLIENT_IMPLEMENTATION));
+    client_->Init(mock_bus_.get());
     // Run the message loop to run the signal connection result callback.
     message_loop_.RunUntilIdle();
   }
diff --git a/chromeos/dbus/shill_service_client.cc b/chromeos/dbus/shill_service_client.cc
index 1c42142..f058117 100644
--- a/chromeos/dbus/shill_service_client.cc
+++ b/chromeos/dbus/shill_service_client.cc
@@ -52,13 +52,11 @@
 // The ShillServiceClient implementation.
 class ShillServiceClientImpl : public ShillServiceClient {
  public:
-  explicit ShillServiceClientImpl(dbus::Bus* bus)
-      : bus_(bus),
+  explicit ShillServiceClientImpl()
+      : bus_(NULL),
         helpers_deleter_(&helpers_) {
   }
 
-  /////////////////////////////////////
-  // ShillServiceClient overrides.
   virtual void AddPropertyChangedObserver(
       const dbus::ObjectPath& service_path,
       ShillPropertyChangedObserver* observer) OVERRIDE {
@@ -219,6 +217,11 @@
     return NULL;
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    bus_ = bus;
+  }
+
  private:
   typedef std::map<std::string, ShillClientHelper*> HelperMap;
 
@@ -252,10 +255,9 @@
 
 // static
 ShillServiceClient* ShillServiceClient::Create(
-    DBusClientImplementationType type,
-    dbus::Bus* bus) {
+    DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new ShillServiceClientImpl(bus);
+    return new ShillServiceClientImpl();
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new ShillServiceClientStub();
 }
diff --git a/chromeos/dbus/shill_service_client.h b/chromeos/dbus/shill_service_client.h
index 0ee87a0..6280f2c 100644
--- a/chromeos/dbus/shill_service_client.h
+++ b/chromeos/dbus/shill_service_client.h
@@ -10,6 +10,7 @@
 #include "base/basictypes.h"
 #include "base/callback.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 #include "chromeos/dbus/shill_client_helper.h"
 
@@ -22,7 +23,6 @@
 
 namespace dbus {
 
-class Bus;
 class ObjectPath;
 
 }  // namespace dbus
@@ -33,7 +33,7 @@
 // service.
 // All methods should be called from the origin thread which initializes the
 // DBusThreadManager instance.
-class CHROMEOS_EXPORT ShillServiceClient {
+class CHROMEOS_EXPORT ShillServiceClient : public DBusClient {
  public:
   typedef ShillClientHelper::PropertyChangedHandler PropertyChangedHandler;
   typedef ShillClientHelper::DictionaryValueCallback DictionaryValueCallback;
@@ -85,8 +85,7 @@
 
   // Factory function, creates a new instance which is owned by the caller.
   // For normal usage, access the singleton via DBusThreadManager::Get().
-  static ShillServiceClient* Create(DBusClientImplementationType type,
-                                    dbus::Bus* bus);
+  static ShillServiceClient* Create(DBusClientImplementationType type);
 
   // Adds a property changed |observer| to the service at |service_path|.
   virtual void AddPropertyChangedObserver(
@@ -184,6 +183,8 @@
   virtual TestInterface* GetTestInterface() = 0;
 
  protected:
+  friend class ShillServiceClientTest;
+
   // Create() should be used instead.
   ShillServiceClient();
 
diff --git a/chromeos/dbus/shill_service_client_stub.cc b/chromeos/dbus/shill_service_client_stub.cc
index 19dc65e..d7c28da 100644
--- a/chromeos/dbus/shill_service_client_stub.cc
+++ b/chromeos/dbus/shill_service_client_stub.cc
@@ -66,6 +66,9 @@
 
 // ShillServiceClient overrides.
 
+void ShillServiceClientStub::Init(dbus::Bus* bus) {
+}
+
 void ShillServiceClientStub::AddPropertyChangedObserver(
     const dbus::ObjectPath& service_path,
     ShillPropertyChangedObserver* observer) {
diff --git a/chromeos/dbus/shill_service_client_stub.h b/chromeos/dbus/shill_service_client_stub.h
index 824aa2a..4cd2964 100644
--- a/chromeos/dbus/shill_service_client_stub.h
+++ b/chromeos/dbus/shill_service_client_stub.h
@@ -29,7 +29,8 @@
   CHROMEOS_EXPORT static bool IsStubPortalledWifiEnabled(
       const std::string& path);
 
-  // ShillServiceClient overrides.
+  // ShillServiceClient overrides
+  virtual void Init(dbus::Bus* bus) OVERRIDE;
   virtual void AddPropertyChangedObserver(
       const dbus::ObjectPath& service_path,
       ShillPropertyChangedObserver* observer) OVERRIDE;
diff --git a/chromeos/dbus/shill_service_client_unittest.cc b/chromeos/dbus/shill_service_client_unittest.cc
index 3d5649f..4267a45 100644
--- a/chromeos/dbus/shill_service_client_unittest.cc
+++ b/chromeos/dbus/shill_service_client_unittest.cc
@@ -33,8 +33,8 @@
   virtual void SetUp() {
     ShillClientUnittestBase::SetUp();
     // Create a client with the mock bus.
-    client_.reset(ShillServiceClient::Create(REAL_DBUS_CLIENT_IMPLEMENTATION,
-                                             mock_bus_.get()));
+    client_.reset(ShillServiceClient::Create(REAL_DBUS_CLIENT_IMPLEMENTATION));
+    client_->Init(mock_bus_.get());
     // Run the message loop to run the signal connection result callback.
     message_loop_.RunUntilIdle();
   }
diff --git a/chromeos/dbus/sms_client.cc b/chromeos/dbus/sms_client.cc
index ee6fb04..cd867a5 100644
--- a/chromeos/dbus/sms_client.cc
+++ b/chromeos/dbus/sms_client.cc
@@ -31,7 +31,8 @@
 // DBusThreadManager instance.
 class SMSClientImpl : public SMSClient {
  public:
-  explicit SMSClientImpl(dbus::Bus* bus) : bus_(bus), weak_ptr_factory_(this) {}
+  SMSClientImpl() : bus_(NULL), weak_ptr_factory_(this) {}
+
   virtual ~SMSClientImpl() {}
 
   // Calls GetAll method.  |callback| is called after the method call succeeds.
@@ -49,6 +50,11 @@
                                   callback));
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    bus_ = bus;
+  }
+
  private:
   // Handles responses of GetAll method calls.
   void OnGetAll(const GetAllCallback& callback, dbus::Response* response) {
@@ -84,6 +90,8 @@
   SMSClientStubImpl() : weak_ptr_factory_(this) {}
   virtual ~SMSClientStubImpl() {}
 
+  virtual void Init(dbus::Bus* bus) OVERRIDE {}
+
   virtual void GetAll(const std::string& service_name,
                       const dbus::ObjectPath& object_path,
                       const GetAllCallback& callback) OVERRIDE {
@@ -131,10 +139,9 @@
 
 
 // static
-SMSClient* SMSClient::Create(DBusClientImplementationType type,
-                             dbus::Bus* bus) {
+SMSClient* SMSClient::Create(DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION) {
-    return new SMSClientImpl(bus);
+    return new SMSClientImpl();
   }
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new SMSClientStubImpl();
diff --git a/chromeos/dbus/sms_client.h b/chromeos/dbus/sms_client.h
index f405bad..44cfa89 100644
--- a/chromeos/dbus/sms_client.h
+++ b/chromeos/dbus/sms_client.h
@@ -10,6 +10,7 @@
 #include "base/basictypes.h"
 #include "base/callback.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 
 namespace base {
@@ -17,7 +18,6 @@
 }
 
 namespace dbus {
-class Bus;
 class ObjectPath;
 }
 
@@ -27,7 +27,7 @@
 // org.freedesktop.ModemManager1.SMS service.  All methods should be
 // called from the origin thread (UI thread) which initializes the
 // DBusThreadManager instance.
-class CHROMEOS_EXPORT SMSClient {
+class CHROMEOS_EXPORT SMSClient : public DBusClient {
  public:
   typedef base::Callback<void(const base::DictionaryValue& sms)> GetAllCallback;
 
@@ -35,8 +35,7 @@
 
   // Factory function, creates a new instance and returns ownership.
   // For normal usage, access the singleton via DBusThreadManager::Get().
-  static SMSClient* Create(DBusClientImplementationType type,
-                           dbus::Bus* bus);
+  static SMSClient* Create(DBusClientImplementationType type);
 
   // Calls GetAll method.  |callback| is called after the method call succeeds.
   virtual void GetAll(const std::string& service_name,
diff --git a/chromeos/dbus/system_clock_client.cc b/chromeos/dbus/system_clock_client.cc
index b9a094e..6775c64 100644
--- a/chromeos/dbus/system_clock_client.cc
+++ b/chromeos/dbus/system_clock_client.cc
@@ -17,9 +17,26 @@
 // The SystemClockClient implementation used in production.
 class SystemClockClientImpl : public SystemClockClient {
  public:
-  explicit SystemClockClientImpl(dbus::Bus* bus)
-      : system_clock_proxy_(NULL),
-        weak_ptr_factory_(this) {
+  SystemClockClientImpl()
+      : system_clock_proxy_(NULL), weak_ptr_factory_(this) {}
+
+  virtual ~SystemClockClientImpl() {
+  }
+
+  virtual void AddObserver(Observer* observer) OVERRIDE {
+    observers_.AddObserver(observer);
+  }
+
+  virtual void RemoveObserver(Observer* observer) OVERRIDE {
+    observers_.RemoveObserver(observer);
+  }
+
+  virtual bool HasObserver(Observer* observer) OVERRIDE {
+    return observers_.HasObserver(observer);
+  }
+
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
     system_clock_proxy_ = bus->GetObjectProxy(
         system_clock::kSystemClockServiceName,
         dbus::ObjectPath(system_clock::kSystemClockServicePath));
@@ -34,22 +51,6 @@
                    weak_ptr_factory_.GetWeakPtr()));
   }
 
-  virtual ~SystemClockClientImpl() {
-  }
-
-  // SystemClockClient overrides:
-  virtual void AddObserver(Observer* observer) OVERRIDE {
-    observers_.AddObserver(observer);
-  }
-
-  virtual void RemoveObserver(Observer* observer) OVERRIDE {
-    observers_.RemoveObserver(observer);
-  }
-
-  virtual bool HasObserver(Observer* observer) OVERRIDE {
-    return observers_.HasObserver(observer);
-  }
-
  private:
   // Called when a TimeUpdated signal is received.
   void TimeUpdatedReceived(dbus::Signal* signal) {
@@ -84,10 +85,9 @@
 
 // static
 SystemClockClient* SystemClockClient::Create(
-    DBusClientImplementationType type,
-    dbus::Bus* bus) {
+    DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION) {
-    return new SystemClockClientImpl(bus);
+    return new SystemClockClientImpl();
   }
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new FakeSystemClockClient();
diff --git a/chromeos/dbus/system_clock_client.h b/chromeos/dbus/system_clock_client.h
index 38158da..ece1e2d 100644
--- a/chromeos/dbus/system_clock_client.h
+++ b/chromeos/dbus/system_clock_client.h
@@ -7,16 +7,13 @@
 
 #include "base/observer_list.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 
-namespace dbus {
-class Bus;
-}  // namespace
-
 namespace chromeos {
 
 // SystemClockClient is used to communicate with the system clock.
-class CHROMEOS_EXPORT SystemClockClient {
+class CHROMEOS_EXPORT SystemClockClient : public DBusClient {
  public:
   // Interface for observing changes from the system clock.
   class Observer {
@@ -36,8 +33,7 @@
   virtual bool HasObserver(Observer* observer) = 0;
 
   // Creates the instance.
-  static SystemClockClient* Create(DBusClientImplementationType type,
-                                   dbus::Bus* bus);
+  static SystemClockClient* Create(DBusClientImplementationType type);
 
  protected:
   // Create() should be used instead.
diff --git a/chromeos/dbus/update_engine_client.cc b/chromeos/dbus/update_engine_client.cc
index fca89aa..659d7a4 100644
--- a/chromeos/dbus/update_engine_client.cc
+++ b/chromeos/dbus/update_engine_client.cc
@@ -59,31 +59,8 @@
 // The UpdateEngineClient implementation used in production.
 class UpdateEngineClientImpl : public UpdateEngineClient {
  public:
-  explicit UpdateEngineClientImpl(dbus::Bus* bus)
-      : update_engine_proxy_(NULL),
-        last_status_(),
-        weak_ptr_factory_(this) {
-    update_engine_proxy_ = bus->GetObjectProxy(
-        update_engine::kUpdateEngineServiceName,
-        dbus::ObjectPath(update_engine::kUpdateEngineServicePath));
-
-    // Monitor the D-Bus signal for brightness changes. Only the power
-    // manager knows the actual brightness level. We don't cache the
-    // brightness level in Chrome as it will make things less reliable.
-    update_engine_proxy_->ConnectToSignal(
-        update_engine::kUpdateEngineInterface,
-        update_engine::kStatusUpdate,
-        base::Bind(&UpdateEngineClientImpl::StatusUpdateReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&UpdateEngineClientImpl::StatusUpdateConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
-
-    // Get update engine status for the initial status. Update engine won't
-    // send StatusUpdate signal unless there is a status change. If chrome
-    // crashes after UPDATE_STATUS_UPDATED_NEED_REBOOT status is set,
-    // restarted chrome would not get this status. See crbug.com/154104.
-    GetUpdateEngineStatus();
-  }
+  UpdateEngineClientImpl()
+      : update_engine_proxy_(NULL), last_status_(), weak_ptr_factory_(this) {}
 
   virtual ~UpdateEngineClientImpl() {
   }
@@ -178,6 +155,30 @@
                    callback));
   }
 
+ protected:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {
+    update_engine_proxy_ = bus->GetObjectProxy(
+        update_engine::kUpdateEngineServiceName,
+        dbus::ObjectPath(update_engine::kUpdateEngineServicePath));
+
+    // Monitor the D-Bus signal for brightness changes. Only the power
+    // manager knows the actual brightness level. We don't cache the
+    // brightness level in Chrome as it will make things less reliable.
+    update_engine_proxy_->ConnectToSignal(
+        update_engine::kUpdateEngineInterface,
+        update_engine::kStatusUpdate,
+        base::Bind(&UpdateEngineClientImpl::StatusUpdateReceived,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&UpdateEngineClientImpl::StatusUpdateConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    // Get update engine status for the initial status. Update engine won't
+    // send StatusUpdate signal unless there is a status change. If chrome
+    // crashes after UPDATE_STATUS_UPDATED_NEED_REBOOT status is set,
+    // restarted chrome would not get this status. See crbug.com/154104.
+    GetUpdateEngineStatus();
+  }
+
  private:
   void GetUpdateEngineStatus() {
     dbus::MethodCall method_call(
@@ -320,6 +321,7 @@
 // which does nothing.
 class UpdateEngineClientStubImpl : public UpdateEngineClient {
   // UpdateEngineClient implementation:
+  virtual void Init(dbus::Bus* bus) OVERRIDE {}
   virtual void AddObserver(Observer* observer) OVERRIDE {}
   virtual void RemoveObserver(Observer* observer) OVERRIDE {}
   virtual bool HasObserver(Observer* observer) OVERRIDE { return false; }
@@ -358,10 +360,9 @@
 
 // static
 UpdateEngineClient* UpdateEngineClient::Create(
-    DBusClientImplementationType type,
-    dbus::Bus* bus) {
+    DBusClientImplementationType type) {
   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
-    return new UpdateEngineClientImpl(bus);
+    return new UpdateEngineClientImpl();
   DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
   return new UpdateEngineClientStubImpl();
 }
diff --git a/chromeos/dbus/update_engine_client.h b/chromeos/dbus/update_engine_client.h
index 00120cb..2923c6b 100644
--- a/chromeos/dbus/update_engine_client.h
+++ b/chromeos/dbus/update_engine_client.h
@@ -8,18 +8,15 @@
 #include "base/callback.h"
 #include "base/observer_list.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
 
 #include <string>
 
-namespace dbus {
-class Bus;
-}
-
 namespace chromeos {
 
 // UpdateEngineClient is used to communicate with the update engine.
-class CHROMEOS_EXPORT UpdateEngineClient {
+class CHROMEOS_EXPORT UpdateEngineClient : public DBusClient {
  public:
   // Edges for state machine
   //    IDLE->CHECKING_FOR_UPDATE
@@ -123,8 +120,7 @@
   static UpdateCheckCallback EmptyUpdateCheckCallback();
 
   // Creates the instance.
-  static UpdateEngineClient* Create(DBusClientImplementationType type,
-                                    dbus::Bus* bus);
+  static UpdateEngineClient* Create(DBusClientImplementationType type);
 
  protected:
   // Create() should be used instead.
diff --git a/chromeos/ime/component_extension_ime_manager.cc b/chromeos/ime/component_extension_ime_manager.cc
index c65bb5d..7331328 100644
--- a/chromeos/ime/component_extension_ime_manager.cc
+++ b/chromeos/ime/component_extension_ime_manager.cc
@@ -153,6 +153,7 @@
               component_extension_imes_[i].engines[j].display_name,
               component_extension_imes_[i].engines[j].layouts,
               component_extension_imes_[i].engines[j].language_codes,
+              false,  // Do not use IME on login screen.
               component_extension_imes_[i].options_page_url));
     }
   }
diff --git a/chromeos/ime/gen_input_methods.py b/chromeos/ime/gen_input_methods.py
index a9f90bf..7e3993d 100755
--- a/chromeos/ime/gen_input_methods.py
+++ b/chromeos/ime/gen_input_methods.py
@@ -26,13 +26,14 @@
   const char* input_method_id;
   const char* language_code;
   const char* xkb_keyboard_id;
+  bool is_login_keyboard;
 };
 const InputMethodsInfo kInputMethods[] = {
-  {"mozc-chewing", "zh-TW", "us"},
-  {"xkb:us::eng", "en-US", "us"},
-  {"xkb:us:dvorak:eng", "en-US", "us(dvorak)"},
-  {"xkb:be::fra", "fr", "be"},
-  {"xkb:br::por", "pt-BR", "br"},
+  {"xkb:us::eng", "en-US", "us", true},
+  {"xkb:us:dvorak:eng", "en-US", "us(dvorak)", true},
+  {"xkb:be::fra", "fr", "be", true},
+  {"xkb:br::por", "pt-BR", "br", true},
+  {"xkb:ru::rus", "ru", "ru", false},
 };
 
 }  // namespace input_method
@@ -57,13 +58,14 @@
   const char* input_method_id;
   const char* language_code;
   const char* xkb_layout_id;
+  bool is_login_keyboard;
 };
 const InputMethodsInfo kInputMethods[] = {
 """
 
 CPP_FORMAT = '#if %s\n'
 ENGINE_FORMAT = ('  {"%(input_method_id)s", "%(language_code)s", ' +
-                 '"%(xkb_layout_id)s"},\n')
+                 '"%(xkb_layout_id)s", %(is_login_keyboard)s},\n')
 
 OUTPUT_FOOTER = """
 };
@@ -110,8 +112,11 @@
     engine['input_method_id'] = columns[0]
     engine['xkb_layout_id'] = columns[1]
     engine['language_code'] = columns[2]
+    is_login_keyboard = "false"
     if len(columns) == 4:
-      engine['if'] = columns[3]
+      assert columns[3] == "login", "Invalid attribute: " + columns[3]
+      is_login_keyboard = "true"
+    engine['is_login_keyboard'] = is_login_keyboard
     engines.append(engine)
 
   output = CreateEngineHeader(engines)
diff --git a/chromeos/ime/input_method_descriptor.cc b/chromeos/ime/input_method_descriptor.cc
index 5e0a3e7..e68b3bb 100644
--- a/chromeos/ime/input_method_descriptor.cc
+++ b/chromeos/ime/input_method_descriptor.cc
@@ -18,11 +18,13 @@
     const std::string& name,
     const std::vector<std::string>& keyboard_layouts,
     const std::vector<std::string>& language_codes,
+    bool is_login_keyboard,
     const GURL& options_page_url)
     : id_(id),
       name_(name),
       keyboard_layouts_(keyboard_layouts),
       language_codes_(language_codes),
+      is_login_keyboard_(is_login_keyboard),
       options_page_url_(options_page_url) {
 }
 
diff --git a/chromeos/ime/input_method_descriptor.h b/chromeos/ime/input_method_descriptor.h
index c410719..32f3de9 100644
--- a/chromeos/ime/input_method_descriptor.h
+++ b/chromeos/ime/input_method_descriptor.h
@@ -23,6 +23,7 @@
                         const std::string& name,
                         const std::vector<std::string>& keyboard_layouts,
                         const std::vector<std::string>& language_codes,
+                        bool is_login_keyboard,
                         const GURL& options_page_url);
   ~InputMethodDescriptor();
 
@@ -37,6 +38,8 @@
     return keyboard_layouts_;
   }
 
+  bool is_login_keyboard() const { return is_login_keyboard_; }
+
   // Returns preferred keyboard layout.
   std::string GetPreferredKeyboardLayout() const;
 
@@ -56,6 +59,9 @@
   // Language code like "ko", "ja", "en-US", and "zh-CN".
   std::vector<std::string> language_codes_;
 
+  // True if this input method can be used on login screen.
+  bool is_login_keyboard_;
+
   // Options page URL e.g.
   // "chrome-extension://ceaajjmckiakobniehbjpdcidfpohlin/options.html".
   // We can't use GURL here due to dependency policy. This field is valid only
diff --git a/chromeos/ime/input_method_manager.h b/chromeos/ime/input_method_manager.h
index 7b18124..81d4568 100644
--- a/chromeos/ime/input_method_manager.h
+++ b/chromeos/ime/input_method_manager.h
@@ -164,6 +164,9 @@
   // Sets the list of extension IME ids which should be enabled.
   virtual void SetEnabledExtensionImes(std::vector<std::string>* ids) = 0;
 
+  // Sets current input method to default (first owners, then hardware).
+  virtual void SetInputMethodDefault() = 0;
+
   // Gets the descriptor of the input method which is currently selected.
   virtual InputMethodDescriptor GetCurrentInputMethod() const = 0;
 
@@ -192,7 +195,7 @@
   virtual bool SwitchInputMethod(const ui::Accelerator& accelerator) = 0;
 
   // If keyboard layout can be uset at login screen
-  virtual bool IsFullLatinKeyboard(const std::string& layout) const = 0;
+  virtual bool IsLoginKeyboard(const std::string& layout) const = 0;
 };
 
 }  // namespace input_method
diff --git a/chromeos/ime/input_method_whitelist.cc b/chromeos/ime/input_method_whitelist.cc
index d798e09..495eb1c 100644
--- a/chromeos/ime/input_method_whitelist.cc
+++ b/chromeos/ime/input_method_whitelist.cc
@@ -46,6 +46,7 @@
         "",
         layouts,
         languages,
+        kInputMethods[i].is_login_keyboard,
         GURL()));  // options page url, not available for non-extension input
                    // method.
   }
diff --git a/chromeos/ime/input_methods.txt b/chromeos/ime/input_methods.txt
index d1e33cf..4aaddeb 100644
--- a/chromeos/ime/input_methods.txt
+++ b/chromeos/ime/input_methods.txt
@@ -16,6 +16,9 @@
 #    two-letter upper-case country code should be added (ex. "en-US", "zh-TW").
 #    See http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes for details.
 #    We can specify multiple language code with comma separator.
+# 4) The additional attibution.
+#    no-login: Spcified keyboard layout will not be used on login screen or lock
+#              screen.
 #
 # Notes:
 #   When adding a line to this list, please also add a mapping from the input
@@ -34,83 +37,75 @@
 #   in chrome/browser/ui/ash/event_rewriter.cc. Otherwise, Mod3Mask might be
 #   removed unexpectedly by the rewriter.
 #
-#   If you add a new language such that some of its layouts can be used at
-#   signin screen (e.g. it is "Full Latin Keyboard Layout" therefore allowing
-#   input of gmail password), you should also update
-#   kHasLatinKeyboardLanguageList[] in
-#   chrome/browser/chromeos/input_method/input_method_manager_impl_ll.cc
-#
-#   If you add a new keyboard layout for existing language, please ensure
-#   that information in kHasLatinKeyboardLanguageList[] is still correct.
 
 # U.S. English
-xkb:us::eng	us	en-US,en-AU,id,fil,ms
-xkb:us:intl:eng	us(intl)	en-US,nl,pt-BR
-xkb:us:altgr-intl:eng	us(altgr-intl)	en-US
-xkb:us:dvorak:eng	us(dvorak)	en-US
-xkb:us:colemak:eng	us(colemak)	en-US
+xkb:us::eng	us	en-US,en-AU,id,fil,ms login
+xkb:us:intl:eng	us(intl)	en-US,nl,pt-BR login
+xkb:us:altgr-intl:eng	us(altgr-intl)	en-US login
+xkb:us:dvorak:eng	us(dvorak)	en-US login
+xkb:us:colemak:eng	us(colemak)	en-US login
 # U.S. English entiries have to be above the Dutch entry so that xkb:us:intl:eng
 # will be selected as the default keyboard when the UI language is set to Dutch.
 
 # Dutch
-xkb:be::nld	be	nl
+xkb:be::nld	be	nl login
 # We don't support xkb:nl::nld. See b/4430951.
 
 # French
-xkb:fr::fra	fr	fr
-xkb:be::fra	be	fr
-xkb:ca::fra	ca	fr
-xkb:ch:fr:fra	ch(fr)	fr
-xkb:ca:multix:fra ca(multix) fr
+xkb:fr::fra	fr	fr login
+xkb:be::fra	be	fr login
+xkb:ca::fra	ca	fr login
+xkb:ch:fr:fra	ch(fr)	fr login
+xkb:ca:multix:fra ca(multix) fr login
 
 # German
-xkb:de::ger	de	de
-xkb:de:neo:ger	de(neo)	de
-xkb:be::ger	be	de
-xkb:ch::ger	ch	de
+xkb:de::ger	de	de login
+xkb:de:neo:ger	de(neo)	de login
+xkb:be::ger	be	de login
+xkb:ch::ger	ch	de login
 
 # Japanese
 # |kMozcJaInputMethodIds| in ibus_ui_controller.cc should also be updated when
 # a new Mozc Japanese IME for another keyboard layout is added.
-xkb:jp::jpn	jp	ja
+xkb:jp::jpn	jp	ja login
 
 # Russian
 xkb:ru::rus	ru	ru
 xkb:ru:phonetic:rus	ru(phonetic)	ru
 
 # Keyboard layouts.
-xkb:br::por	br	pt-BR
+xkb:br::por	br	pt-BR login
 xkb:bg::bul	bg	bg
 xkb:bg:phonetic:bul	bg(phonetic)	bg
-xkb:ca:eng:eng	ca(eng)	en-CA
-xkb:cz::cze	cz	cs
-xkb:cz:qwerty:cze	cz(qwerty)	cs
-xkb:ee::est	ee	et
-xkb:es::spa	es	es
-xkb:es:cat:cat	es(cat)	ca
-xkb:dk::dan	dk	da
+xkb:ca:eng:eng	ca(eng)	en-CA login
+xkb:cz::cze	cz	cs login
+xkb:cz:qwerty:cze	cz(qwerty)	cs login
+xkb:ee::est	ee	et login
+xkb:es::spa	es	es login
+xkb:es:cat:cat	es(cat)	ca login
+xkb:dk::dan	dk	da login
 xkb:gr::gre	gr	el
 xkb:il::heb	il	he
-xkb:latam::spa	latam	es,es-419
-xkb:lt::lit	lt	lt
-xkb:lv:apostrophe:lav	lv(apostrophe)	lv
-xkb:hr::scr	hr	hr
-xkb:gb:extd:eng	gb(extd)	en-GB
-xkb:gb:dvorak:eng	gb(dvorak)	en-GB
-xkb:fi::fin	fi	fi
-xkb:hu::hun	hu	hu
-xkb:it::ita	it	it
-xkb:is::ice	is	is
-xkb:no::nob	no	nb
-xkb:pl::pol	pl	pl
-xkb:pt::por	pt	pt-PT
-xkb:ro::rum	ro	ro
-xkb:se::swe	se	sv
+xkb:latam::spa	latam	es,es-419 login
+xkb:lt::lit	lt	lt login
+xkb:lv:apostrophe:lav	lv(apostrophe)	lv login
+xkb:hr::scr	hr	hr login
+xkb:gb:extd:eng	gb(extd)	en-GB login
+xkb:gb:dvorak:eng	gb(dvorak)	en-GB login
+xkb:fi::fin	fi	fi login
+xkb:hu::hun	hu	hu login
+xkb:it::ita	it	it login
+xkb:is::ice	is	is login
+xkb:no::nob	no	nb login
+xkb:pl::pol	pl	pl login
+xkb:pt::por	pt	pt-PT login
+xkb:ro::rum	ro	ro login
+xkb:se::swe	se	sv login
 xkb:sk::slo	sk	sk
-xkb:si::slv	si	sl
+xkb:si::slv	si	sl login
 xkb:rs::srp	rs	sr
-xkb:tr::tur	tr	tr
-xkb:ua::ukr	ua	uk
+xkb:tr::tur	tr	tr login
+xkb:ua::ukr	ua	uk login
 xkb:by::bel	by	be
 xkb:am:phonetic:arm	am	hy
 xkb:ge::geo	ge	ka
diff --git a/chromeos/network/OWNERS b/chromeos/network/OWNERS
index 720527f..a8f7040 100644
--- a/chromeos/network/OWNERS
+++ b/chromeos/network/OWNERS
@@ -1,2 +1,3 @@
 gspencer@chromium.org
 gauravsh@chromium.org
+pneubeck@chromium.org
diff --git a/chromeos/network/device_state.cc b/chromeos/network/device_state.cc
index e219571..f71b978 100644
--- a/chromeos/network/device_state.cc
+++ b/chromeos/network/device_state.cc
@@ -5,6 +5,7 @@
 #include "chromeos/network/device_state.h"
 
 #include "base/logging.h"
+#include "base/metrics/histogram.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
@@ -118,6 +119,16 @@
   return false;
 }
 
+bool DeviceState::InitialPropertiesReceived(
+    const base::DictionaryValue& properties) {
+  // Update UMA stats.
+  if (sim_present_) {
+    bool locked = !sim_lock_type_.empty();
+    UMA_HISTOGRAM_BOOLEAN("Cellular.SIMLocked", locked);
+  }
+  return false;
+}
+
 bool DeviceState::IsSimAbsent() const {
   return technology_family_ == flimflam::kTechnologyFamilyGsm && !sim_present_;
 }
diff --git a/chromeos/network/device_state.h b/chromeos/network/device_state.h
index 00a6cdc..bfad927 100644
--- a/chromeos/network/device_state.h
+++ b/chromeos/network/device_state.h
@@ -23,6 +23,8 @@
   // ManagedState overrides
   virtual bool PropertyChanged(const std::string& key,
                                const base::Value& value) OVERRIDE;
+  virtual bool InitialPropertiesReceived(
+      const base::DictionaryValue& properties) OVERRIDE;
 
   // Accessors
   const std::string& mac_address() const { return mac_address_; }
diff --git a/chromeos/network/managed_network_configuration_handler_impl.cc b/chromeos/network/managed_network_configuration_handler_impl.cc
index f2133c3..58c0c3a 100644
--- a/chromeos/network/managed_network_configuration_handler_impl.cc
+++ b/chromeos/network/managed_network_configuration_handler_impl.cc
@@ -152,7 +152,8 @@
 // Creates a Shill property dictionary from the given arguments. The resulting
 // dictionary will be sent to Shill by the caller. Depending on the profile
 // type, |policy| is interpreted as the user or device policy and |settings| as
-// the user or shared settings.
+// the user or shared settings. |policy| or |settings| can be NULL, but not
+// both.
 scoped_ptr<base::DictionaryValue> CreateShillConfiguration(
     const NetworkProfile& profile,
     const std::string& guid,
@@ -341,6 +342,13 @@
   // Sends Shill the command to delete profile entry |entry| from |profile_|.
   void DeleteEntry(const std::string& entry);
 
+  // Creates a Shill configuration from the given parameters and sends them to
+  // Shill. |user_settings| can be NULL if none exist.
+  void CreateAndWriteNewShillConfiguration(
+      const std::string& guid,
+      const base::DictionaryValue& policy,
+      const base::DictionaryValue* user_settings);
+
   // Creates new entries for all remaining policies, i.e. for which not matching
   // entry was found.
   virtual ~PolicyApplicator();
@@ -392,7 +400,6 @@
   std::string profile_path;
   shill_properties.GetStringWithoutPathExpansion(flimflam::kProfileProperty,
                                                  &profile_path);
-  LOG(ERROR) << "Profile: " << profile_path;
   const NetworkProfile* profile =
       network_profile_handler_->GetProfileForPath(profile_path);
   if (!profile) {
@@ -934,14 +941,7 @@
           ui_data ? ui_data->user_settings() : NULL;
 
       // Write the new configuration.
-      scoped_ptr<base::DictionaryValue> shill_dictionary =
-          CreateShillConfiguration(
-              profile_, new_guid, new_policy, user_settings);
-      handler_->network_configuration_handler()->CreateConfiguration(
-          *shill_dictionary,
-          base::Bind(&ManagedNetworkConfigurationHandlerImpl::OnPolicyApplied,
-                     handler_),
-          base::Bind(&LogErrorWithDict, FROM_HERE));
+      CreateAndWriteNewShillConfiguration(new_guid, *new_policy, user_settings);
       remaining_policies_.erase(new_guid);
     }
   } else if (was_managed) {
@@ -962,11 +962,25 @@
 
 void ManagedNetworkConfigurationHandlerImpl::PolicyApplicator::DeleteEntry(
     const std::string& entry) {
-  DBusThreadManager::Get()->GetShillProfileClient()
-      ->DeleteEntry(dbus::ObjectPath(profile_.path),
-                    entry,
-                    base::Bind(&base::DoNothing),
-                    base::Bind(&LogErrorMessage, FROM_HERE));
+  DBusThreadManager::Get()->GetShillProfileClient()->DeleteEntry(
+      dbus::ObjectPath(profile_.path),
+      entry,
+      base::Bind(&base::DoNothing),
+      base::Bind(&LogErrorMessage, FROM_HERE));
+}
+
+void ManagedNetworkConfigurationHandlerImpl::PolicyApplicator::
+    CreateAndWriteNewShillConfiguration(
+        const std::string& guid,
+        const base::DictionaryValue& policy,
+        const base::DictionaryValue* user_settings) {
+  scoped_ptr<base::DictionaryValue> shill_dictionary =
+      CreateShillConfiguration(profile_, guid, &policy, user_settings);
+  handler_->network_configuration_handler()->CreateConfiguration(
+      *shill_dictionary,
+      base::Bind(&ManagedNetworkConfigurationHandlerImpl::OnPolicyApplied,
+                 handler_),
+      base::Bind(&LogErrorWithDict, FROM_HERE));
 }
 
 ManagedNetworkConfigurationHandlerImpl::PolicyApplicator::~PolicyApplicator() {
@@ -1001,14 +1015,8 @@
     VLOG(1) << "Creating new configuration managed by policy " << *it
             << " in profile " << profile_.ToDebugString() << ".";
 
-    scoped_ptr<base::DictionaryValue> shill_dictionary =
-        CreateShillConfiguration(
-            profile_, *it, policy, NULL /* no user settings */);
-    handler_->network_configuration_handler()->CreateConfiguration(
-        *shill_dictionary,
-        base::Bind(&ManagedNetworkConfigurationHandlerImpl::OnPolicyApplied,
-                   handler_),
-        base::Bind(&LogErrorWithDict, FROM_HERE));
+    CreateAndWriteNewShillConfiguration(
+        *it, *policy, NULL /* no user settings */);
   }
 }
 
diff --git a/chromeos/network/network_activation_handler.cc b/chromeos/network/network_activation_handler.cc
new file mode 100644
index 0000000..90ed374
--- /dev/null
+++ b/chromeos/network/network_activation_handler.cc
@@ -0,0 +1,75 @@
+// 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 "chromeos/network/network_activation_handler.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/shill_service_client.h"
+#include "chromeos/network/network_event_log.h"
+#include "chromeos/network/network_handler.h"
+#include "dbus/object_proxy.h"
+
+namespace chromeos {
+
+// static
+const char NetworkActivationHandler::kErrorShillError[] = "shill-error";
+
+NetworkActivationHandler::NetworkActivationHandler() {}
+NetworkActivationHandler::~NetworkActivationHandler() {}
+
+void NetworkActivationHandler::Activate(
+    const std::string& service_path,
+    const std::string& carrier,
+    const base::Closure& success_callback,
+    const network_handler::ErrorCallback& error_callback) {
+  NET_LOG_USER("ActivateNetwork", service_path);
+  CallShillActivate(service_path, carrier, success_callback, error_callback);
+}
+
+void NetworkActivationHandler::CompleteActivation(
+    const std::string& service_path,
+    const base::Closure& success_callback,
+    const network_handler::ErrorCallback& error_callback) {
+  NET_LOG_USER("CompleteActivation", service_path);
+  CallShillCompleteActivation(service_path, success_callback, error_callback);
+}
+
+void NetworkActivationHandler::CallShillActivate(
+    const std::string& service_path,
+    const std::string& carrier,
+    const base::Closure& success_callback,
+    const network_handler::ErrorCallback& error_callback) {
+  NET_LOG_USER("Activation Request", service_path + ": '" + carrier + "'");
+  DBusThreadManager::Get()->GetShillServiceClient()->ActivateCellularModem(
+      dbus::ObjectPath(service_path),
+      carrier,
+      base::Bind(&NetworkActivationHandler::HandleShillSuccess,
+                 AsWeakPtr(), service_path, success_callback),
+      base::Bind(&network_handler::ShillErrorCallbackFunction,
+                 kErrorShillError, service_path, error_callback));
+}
+
+void NetworkActivationHandler::CallShillCompleteActivation(
+    const std::string& service_path,
+    const base::Closure& success_callback,
+    const network_handler::ErrorCallback& error_callback) {
+  NET_LOG_USER("CompleteActivation Request", service_path);
+  DBusThreadManager::Get()->GetShillServiceClient()->CompleteCellularActivation(
+      dbus::ObjectPath(service_path),
+      base::Bind(&NetworkActivationHandler::HandleShillSuccess,
+                 AsWeakPtr(), service_path, success_callback),
+      base::Bind(&network_handler::ShillErrorCallbackFunction,
+                 kErrorShillError, service_path, error_callback));
+}
+
+void NetworkActivationHandler::HandleShillSuccess(
+    const std::string& service_path,
+    const base::Closure& success_callback) {
+  if (!success_callback.is_null())
+    success_callback.Run();
+}
+
+}  // namespace chromeos
diff --git a/chromeos/network/network_activation_handler.h b/chromeos/network/network_activation_handler.h
new file mode 100644
index 0000000..f15c73d
--- /dev/null
+++ b/chromeos/network/network_activation_handler.h
@@ -0,0 +1,78 @@
+// 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 CHROMEOS_NETWORK_NETWORK_ACTIVATION_HANDLER_H_
+#define CHROMEOS_NETWORK_NETWORK_ACTIVATION_HANDLER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/weak_ptr.h"
+#include "chromeos/chromeos_export.h"
+#include "chromeos/network/network_handler_callbacks.h"
+
+namespace chromeos {
+
+// The NetworkActivationHandler class allows making service specific
+// calls required for activation on mobile networks.
+class CHROMEOS_EXPORT NetworkActivationHandler
+    : public base::SupportsWeakPtr<NetworkActivationHandler> {
+ public:
+  // Constants for |error_name| from |error_callback|.
+  // TODO(gauravsh): Merge various error constants from Network*Handlers into
+  // a single place. crbug.com/272554
+  static const char kErrorNotFound[];
+  static const char kErrorShillError[];
+
+  virtual ~NetworkActivationHandler();
+
+  // ActivateNetwork() will start an asynchronous activation attempt.
+  // |carrier| may be empty or may specify a carrier to activate.
+  // On success, |success_callback| will be called.
+  // On failure, |error_callback| will be called with |error_name| one of:
+  //  kErrorNotFound if no network matching |service_path| is found.
+  //  kErrorShillError if a DBus or Shill error occurred.
+  void Activate(const std::string& service_path,
+                const std::string& carrier,
+                const base::Closure& success_callback,
+                const network_handler::ErrorCallback& error_callback);
+
+  // CompleteActivation() will start an asynchronous activation completion
+  // attempt.
+  // On success, |success_callback| will be called.
+  // On failure, |error_callback| will be called with |error_name| one of:
+  //  kErrorNotFound if no network matching |service_path| is found.
+  //  kErrorShillError if a DBus or Shill error occurred.
+  void CompleteActivation(const std::string& service_path,
+                          const base::Closure& success_callback,
+                          const network_handler::ErrorCallback& error_callback);
+
+ private:
+  friend class NetworkHandler;
+
+  NetworkActivationHandler();
+
+  // Calls Shill.Service.ActivateCellularModem asynchronously.
+  void CallShillActivate(const std::string& service_path,
+                         const std::string& carrier,
+                         const base::Closure& success_callback,
+                         const network_handler::ErrorCallback& error_callback);
+
+  // Calls Shill.Service.CompleteCellularActivation asynchronously.
+  void CallShillCompleteActivation(
+      const std::string& service_path,
+      const base::Closure& success_callback,
+      const network_handler::ErrorCallback& error_callback);
+
+  // Handle success from Shill.Service.ActivateCellularModem or
+  // Shill.Service.CompleteCellularActivation.
+  void HandleShillSuccess(const std::string& service_path,
+                          const base::Closure& success_callback);
+
+  DISALLOW_COPY_AND_ASSIGN(NetworkActivationHandler);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_NETWORK_NETWORK_ACTIVATION_HANDLER_H_
diff --git a/chromeos/network/network_connection_handler.cc b/chromeos/network/network_connection_handler.cc
index f27ff4f..151d8f6 100644
--- a/chromeos/network/network_connection_handler.cc
+++ b/chromeos/network/network_connection_handler.cc
@@ -314,21 +314,6 @@
   CallShillDisconnect(service_path, success_callback, error_callback);
 }
 
-void NetworkConnectionHandler::ActivateNetwork(
-    const std::string& service_path,
-    const std::string& carrier,
-    const base::Closure& success_callback,
-    const network_handler::ErrorCallback& error_callback) {
-  NET_LOG_USER("DisconnectNetwork", service_path);
-  const NetworkState* network =
-      network_state_handler_->GetNetworkState(service_path);
-  if (!network) {
-    InvokeErrorCallback(service_path, error_callback, kErrorNotFound);
-    return;
-  }
-  CallShillActivate(service_path, carrier, success_callback, error_callback);
-}
-
 bool NetworkConnectionHandler::HasConnectingNetwork(
     const std::string& service_path) {
   return pending_requests_.count(service_path) != 0;
@@ -696,29 +681,4 @@
     success_callback.Run();
 }
 
-// Activate
-
-void NetworkConnectionHandler::CallShillActivate(
-    const std::string& service_path,
-    const std::string& carrier,
-    const base::Closure& success_callback,
-    const network_handler::ErrorCallback& error_callback) {
-  NET_LOG_USER("Activate Request", service_path + ": '" + carrier + "'");
-  DBusThreadManager::Get()->GetShillServiceClient()->ActivateCellularModem(
-      dbus::ObjectPath(service_path),
-      carrier,
-      base::Bind(&NetworkConnectionHandler::HandleShillActivateSuccess,
-                 AsWeakPtr(), service_path, success_callback),
-      base::Bind(&network_handler::ShillErrorCallbackFunction,
-                 kErrorShillError, service_path, error_callback));
-}
-
-void NetworkConnectionHandler::HandleShillActivateSuccess(
-    const std::string& service_path,
-    const base::Closure& success_callback) {
-  NET_LOG_EVENT("Activate Request Sent", service_path);
-  if (!success_callback.is_null())
-    success_callback.Run();
-}
-
 }  // namespace chromeos
diff --git a/chromeos/network/network_connection_handler.h b/chromeos/network/network_connection_handler.h
index 4b363ef..17abf01 100644
--- a/chromeos/network/network_connection_handler.h
+++ b/chromeos/network/network_connection_handler.h
@@ -101,18 +101,6 @@
                          const base::Closure& success_callback,
                          const network_handler::ErrorCallback& error_callback);
 
-  // ActivateNetwork() will start an asynchronous activation attempt.
-  // |carrier| may be empty or may specify a carrier to activate.
-  // On success, |success_callback| will be called.
-  // On failure, |error_callback| will be called with |error_name| one of:
-  //  kErrorNotFound if no network matching |service_path| is found.
-  //  kErrorShillError if a DBus or Shill error occurred.
-  // TODO(stevenjb/armansito): Move this to a separate NetworkActivationHandler.
-  void ActivateNetwork(const std::string& service_path,
-                       const std::string& carrier,
-                       const base::Closure& success_callback,
-                       const network_handler::ErrorCallback& error_callback);
-
   // Returns true if ConnectToNetwork has been called with |service_path| and
   // has not completed (i.e. success or error callback has been called).
   bool HasConnectingNetwork(const std::string& service_path);
@@ -187,16 +175,6 @@
   void HandleShillDisconnectSuccess(const std::string& service_path,
                                     const base::Closure& success_callback);
 
-  // Calls Shill.Manager.Activate asynchronously.
-  void CallShillActivate(
-      const std::string& service_path,
-      const std::string& carrier,
-      const base::Closure& success_callback,
-      const network_handler::ErrorCallback& error_callback);
-
-  // Handle success from Shill.Service.ActivateCellularModem.
-  void HandleShillActivateSuccess(const std::string& service_path,
-                                  const base::Closure& success_callback);
 
   // Local references to the associated handler instances.
   CertLoader* cert_loader_;
diff --git a/chromeos/network/network_handler.cc b/chromeos/network/network_handler.cc
index 47606ae..15ede66 100644
--- a/chromeos/network/network_handler.cc
+++ b/chromeos/network/network_handler.cc
@@ -9,6 +9,7 @@
 #include "chromeos/network/client_cert_resolver.h"
 #include "chromeos/network/geolocation_handler.h"
 #include "chromeos/network/managed_network_configuration_handler_impl.h"
+#include "chromeos/network/network_activation_handler.h"
 #include "chromeos/network/network_cert_migrator.h"
 #include "chromeos/network/network_configuration_handler.h"
 #include "chromeos/network/network_connection_handler.h"
@@ -40,6 +41,7 @@
     network_cert_migrator_.reset(new NetworkCertMigrator());
     client_cert_resolver_.reset(new ClientCertResolver());
   }
+  network_activation_handler_.reset(new NetworkActivationHandler());
   network_connection_handler_.reset(new NetworkConnectionHandler());
   network_sms_handler_.reset(new NetworkSmsHandler());
   geolocation_handler_.reset(new GeolocationHandler());
@@ -116,6 +118,10 @@
   return managed_network_configuration_handler_.get();
 }
 
+NetworkActivationHandler* NetworkHandler::network_activation_handler() {
+  return network_activation_handler_.get();
+}
+
 NetworkConnectionHandler* NetworkHandler::network_connection_handler() {
   return network_connection_handler_.get();
 }
diff --git a/chromeos/network/network_handler.h b/chromeos/network/network_handler.h
index 2197529..bbb9e9c 100644
--- a/chromeos/network/network_handler.h
+++ b/chromeos/network/network_handler.h
@@ -17,6 +17,7 @@
 class GeolocationHandler;
 class ManagedNetworkConfigurationHandler;
 class ManagedNetworkConfigurationHandlerImpl;
+class NetworkActivationHandler;
 class NetworkCertMigrator;
 class NetworkConfigurationHandler;
 class NetworkConnectionHandler;
@@ -54,6 +55,7 @@
   NetworkProfileHandler* network_profile_handler();
   NetworkConfigurationHandler* network_configuration_handler();
   ManagedNetworkConfigurationHandler* managed_network_configuration_handler();
+  NetworkActivationHandler* network_activation_handler();
   NetworkConnectionHandler* network_connection_handler();
   NetworkSmsHandler* network_sms_handler();
   GeolocationHandler* geolocation_handler();
@@ -74,6 +76,7 @@
       managed_network_configuration_handler_;
   scoped_ptr<NetworkCertMigrator> network_cert_migrator_;
   scoped_ptr<ClientCertResolver> client_cert_resolver_;
+  scoped_ptr<NetworkActivationHandler> network_activation_handler_;
   scoped_ptr<NetworkConnectionHandler> network_connection_handler_;
   scoped_ptr<NetworkSmsHandler> network_sms_handler_;
   scoped_ptr<GeolocationHandler> geolocation_handler_;
diff --git a/chromeos/network/network_state.h b/chromeos/network/network_state.h
index 5752057..62edc03 100644
--- a/chromeos/network/network_state.h
+++ b/chromeos/network/network_state.h
@@ -116,6 +116,7 @@
       const base::DictionaryValue& properties);
 
  private:
+  friend class MobileActivatorTest;
   friend class NetworkStateHandler;
   friend class NetworkChangeNotifierChromeosUpdateTest;
 
diff --git a/chromeos/network/network_state_handler.cc b/chromeos/network/network_state_handler.cc
index 106537f..eadc2ca 100644
--- a/chromeos/network/network_state_handler.cc
+++ b/chromeos/network/network_state_handler.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/format_macros.h"
 #include "base/location.h"
+#include "base/metrics/histogram.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -277,6 +278,11 @@
 }
 
 void NetworkStateHandler::GetNetworkList(NetworkStateList* list) const {
+  GetNetworkListByType(kMatchTypeDefault, list);
+}
+
+void NetworkStateHandler::GetNetworkListByType(const std::string& type,
+                                               NetworkStateList* list) const {
   DCHECK(list);
   list->clear();
   for (ManagedStateList::const_iterator iter = network_list_.begin();
@@ -285,7 +291,8 @@
       continue;
     const NetworkState* network = (*iter)->AsNetworkState();
     DCHECK(network);
-    list->push_back(network);
+    if (ManagedStateMatchesType(network, type))
+      list->push_back(network);
   }
 }
 
@@ -595,11 +602,28 @@
     // The list order may have changed, so check if the default network changed.
     if (CheckDefaultNetworkChanged())
       OnDefaultNetworkChanged();
+    // Update UMA stats.
+    UMA_HISTOGRAM_COUNTS_100("Networks.Visible", network_list_.size());
   } else if (type == ManagedState::MANAGED_TYPE_FAVORITE) {
     NET_LOG_DEBUG("FavoriteListChanged",
                   base::StringPrintf("Size:%" PRIuS, favorite_list_.size()));
     // The FavoriteState list only changes when the NetworkState list changes,
     // so no need to signal observers here again.
+
+    // Update UMA stats.
+    size_t shared = 0, unshared = 0;
+    for (ManagedStateList::iterator iter = favorite_list_.begin();
+         iter != favorite_list_.end(); ++iter) {
+      FavoriteState* favorite = (*iter)->AsFavoriteState();
+      if (!favorite->is_favorite())
+        continue;
+      if (favorite->IsPrivate())
+        ++unshared;
+      else
+        ++shared;
+    }
+    UMA_HISTOGRAM_COUNTS_100("Networks.RememberedShared", shared);
+    UMA_HISTOGRAM_COUNTS_100("Networks.RememberedUnshared", unshared);
   } else if (type == ManagedState::MANAGED_TYPE_DEVICE) {
     NET_LOG_DEBUG("DeviceListChanged",
                   base::StringPrintf("Size:%" PRIuS, device_list_.size()));
diff --git a/chromeos/network/network_state_handler.h b/chromeos/network/network_state_handler.h
index 3ae7126..9ad3ac8 100644
--- a/chromeos/network/network_state_handler.h
+++ b/chromeos/network/network_state_handler.h
@@ -150,6 +150,10 @@
   // only on the UI thread).
   void GetNetworkList(NetworkStateList* list) const;
 
+  // Like GetNetworkList() but only returns networks with matching |type|.
+  void GetNetworkListByType(const std::string& type,
+                            NetworkStateList* list) const;
+
   // Sets |list| to contain the list of devices.  The returned list contains
   // a copy of DeviceState pointers which should not be stored or used beyond
   // the scope of the calling function (i.e. they may later become invalid, but
diff --git a/chromeos/network/onc/OWNERS b/chromeos/network/onc/OWNERS
deleted file mode 100644
index 713045b..0000000
--- a/chromeos/network/onc/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-pneubeck@chromium.org
diff --git a/cloud_print/service/win/cloud_print_service.cc b/cloud_print/service/win/cloud_print_service.cc
index e93ad05..c6254c0 100644
--- a/cloud_print/service/win/cloud_print_service.cc
+++ b/cloud_print/service/win/cloud_print_service.cc
@@ -10,6 +10,7 @@
 
 #include "base/at_exit.h"
 #include "base/bind.h"
+#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/file_util.h"
 #include "base/guid.h"
diff --git a/cloud_print/service/win/cloud_print_service_config.cc b/cloud_print/service/win/cloud_print_service_config.cc
index 14694cc..bad7111 100644
--- a/cloud_print/service/win/cloud_print_service_config.cc
+++ b/cloud_print/service/win/cloud_print_service_config.cc
@@ -7,6 +7,7 @@
 
 #include "base/at_exit.h"
 #include "base/bind.h"
+#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/file_util.h"
 #include "base/message_loop/message_loop.h"
diff --git a/components/DEPS b/components/DEPS
index d6fbc7c..0ea897e 100644
--- a/components/DEPS
+++ b/components/DEPS
@@ -13,4 +13,7 @@
   # "+content/public/browser" rule.
   "-content",
   "+content/public/common",
+
+  # Dependencies of variations component.
+  "+third_party/mt19937ar",
 ]
diff --git a/components/OWNERS b/components/OWNERS
index ff02f9e..52618de 100644
--- a/components/OWNERS
+++ b/components/OWNERS
@@ -42,5 +42,9 @@
 per-file user_prefs.gypi=mnissler@chromium.org
 per-file user_prefs.gypi=pam@chromium.org
 
+per-file variations.gypi=asvitkine@chromium.org
+per-file variations.gypi=jwd@chromium.org
+per-file variations.gypi=stevet@chromium.org
+
 per-file *.isolate=csharp@chromium.org
 per-file *.isolate=maruel@chromium.org
diff --git a/components/autofill.gypi b/components/autofill.gypi
index 92eccd3..789c0c0 100644
--- a/components/autofill.gypi
+++ b/components/autofill.gypi
@@ -220,6 +220,7 @@
           'dependencies': [
             'autofill_core_common',
             'autofill_core_browser',
+            '../skia/skia.gyp:skia',
             '../testing/gtest.gyp:gtest',
           ],
           'sources': [
diff --git a/components/autofill/content/browser/autocheckout_manager.cc b/components/autofill/content/browser/autocheckout_manager.cc
index be81923..ec3007c 100644
--- a/components/autofill/content/browser/autocheckout_manager.cc
+++ b/components/autofill/content/browser/autocheckout_manager.cc
@@ -367,7 +367,7 @@
       // TODO(dgwallinga): Find a way of cleanly deprecating CREDIT_CARD_NAME.
       // code.google.com/p/chromium/issues/detail?id=263498
       if (server_type == CREDIT_CARD_NAME)
-        billing_address_->SetRawInfo(NAME_BILLING_FULL, value);
+        billing_address_->SetRawInfo(NAME_FULL, value);
     } else if (server_type == ADDRESS_HOME_COUNTRY) {
       if (IsBillingGroup(group))
         billing_address_->SetInfo(type, value, autofill_manager_->app_locale());
diff --git a/components/autofill/content/browser/wallet/wallet_client.cc b/components/autofill/content/browser/wallet/wallet_client.cc
index 6a02596..3607065 100644
--- a/components/autofill/content/browser/wallet/wallet_client.cc
+++ b/components/autofill/content/browser/wallet/wallet_client.cc
@@ -122,9 +122,9 @@
 // Get the more specific WalletClient::ErrorType when the error is
 // |BUYER_ACCOUNT_ERROR|.
 WalletClient::ErrorType BuyerErrorStringToErrorType(
-    const std::string& buyer_error_type) {
+    const std::string& message_type_for_buyer) {
   std::string trimmed;
-  TrimWhitespaceASCII(buyer_error_type,
+  TrimWhitespaceASCII(message_type_for_buyer,
                       TRIM_ALL,
                       &trimmed);
   if (LowerCaseEqualsASCII(trimmed, "bla_country_not_supported"))
@@ -245,7 +245,6 @@
 const char kAcceptedLegalDocumentKey[] = "accepted_legal_document";
 const char kApiKeyKey[] = "api_key";
 const char kAuthResultKey[] = "auth_result";
-const char kBuyerErrorTypeKey[] = "wallet_error.buyer_error_type";
 const char kEncryptedOtpKey[] = "encrypted_otp";
 const char kErrorTypeKey[] = "wallet_error.error_type";
 const char kFeatureKey[] = "feature";
@@ -258,6 +257,7 @@
 const char kInstrumentType[] = "instrument.type";
 const char kInstrumentPhoneNumberKey[] = "instrument_phone_number";
 const char kMerchantDomainKey[] = "merchant_domain";
+const char kMessageTypeForBuyerKey[] = "wallet_error.message_type_for_buyer";
 const char kNewWalletUser[] = "new_wallet_user";
 const char kPhoneNumberRequired[] = "phone_number_required";
 const char kReasonKey[] = "reason";
@@ -707,12 +707,14 @@
         WalletClient::ErrorType error_type =
             StringToErrorType(error_type_string);
         if (error_type == BUYER_ACCOUNT_ERROR) {
-          // If the error_type is |BUYER_ACCOUNT_ERROR|, then buyer_error_type
-          // field contains more specific information about the error.
-          std::string buyer_error_type_string;
-          if (response_dict->GetString(kBuyerErrorTypeKey,
-                                       &buyer_error_type_string)) {
-            error_type = BuyerErrorStringToErrorType(buyer_error_type_string);
+          // If the error_type is |BUYER_ACCOUNT_ERROR|, then
+          // message_type_for_buyer field contains more specific information
+          // about the error.
+          std::string message_type_for_buyer_string;
+          if (response_dict->GetString(kMessageTypeForBuyerKey,
+                                       &message_type_for_buyer_string)) {
+            error_type = BuyerErrorStringToErrorType(
+                message_type_for_buyer_string);
           }
         }
 
diff --git a/components/autofill/content/browser/wallet/wallet_client_unittest.cc b/components/autofill/content/browser/wallet/wallet_client_unittest.cc
index 5e230c9..c3da942 100644
--- a/components/autofill/content/browser/wallet/wallet_client_unittest.cc
+++ b/components/autofill/content/browser/wallet/wallet_client_unittest.cc
@@ -815,7 +815,7 @@
 
   void TestWalletErrorCode(
       const std::string& error_type_string,
-      const std::string& buyer_error_type_string,
+      const std::string& message_type_for_buyer_string,
       WalletClient::ErrorType expected_error_type,
       AutofillMetrics::WalletErrorMetric expected_autofill_metric) {
     static const char kResponseTemplate[] =
@@ -853,9 +853,9 @@
                                            statistics,
                                            "google_transaction_id");
     std::string buyer_error;
-    if (!buyer_error_type_string.empty()) {
-      buyer_error = base::StringPrintf("\"buyer_error_type\":\"%s\",",
-                                       buyer_error_type_string.c_str());
+    if (!message_type_for_buyer_string.empty()) {
+      buyer_error = base::StringPrintf("\"message_type_for_buyer\":\"%s\",",
+                                       message_type_for_buyer_string.c_str());
     }
     std::string response = base::StringPrintf(kResponseTemplate,
                                               error_type_string.c_str(),
@@ -893,11 +893,11 @@
 TEST_F(WalletClientTest, WalletErrorCodes) {
   struct {
     std::string error_type_string;
-    std::string buyer_error_type_string;
+    std::string message_type_for_buyer_string;
     WalletClient::ErrorType expected_error_type;
     AutofillMetrics::WalletErrorMetric expected_autofill_metric;
   } test_cases[] = {
-      // General |BUYER_ACCOUNT_ERROR| with no |buyer_error_type_string|.
+      // General |BUYER_ACCOUNT_ERROR| with no |message_type_for_buyer_string|.
       {
           "buyer_account_error",
           "",
@@ -905,21 +905,22 @@
           AutofillMetrics::WALLET_BUYER_ACCOUNT_ERROR
       },
       // |BUYER_ACCOUNT_ERROR| with "buyer_legal_address_not_supported" in
-      // buyer_error_type field.
+      // message_type_for_buyer field.
       {
           "buyer_account_error",
           "bla_country_not_supported",
           WalletClient::BUYER_LEGAL_ADDRESS_NOT_SUPPORTED,
           AutofillMetrics::WALLET_BUYER_LEGAL_ADDRESS_NOT_SUPPORTED
       },
-      // |BUYER_ACCOUNT_ERROR| with KYC error code in buyer_error_type field.
+      // |BUYER_ACCOUNT_ERROR| with KYC error code in message_type_for_buyer
+      // field.
       {
           "buyer_account_error",
           "buyer_kyc_error",
           WalletClient::UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS,
           AutofillMetrics::WALLET_UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS
       },
-      // |BUYER_ACCOUNT_ERROR| with un-recognizable |buyer_error_type|.
+      // |BUYER_ACCOUNT_ERROR| with un-recognizable |message_type_for_buyer|.
       {
           "buyer_account_error",
           "random_string",
@@ -974,11 +975,12 @@
 
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
     SCOPED_TRACE(
-        base::StringPrintf("%s - %s",
-                           test_cases[i].error_type_string.c_str(),
-                           test_cases[i].buyer_error_type_string.c_str()));
+        base::StringPrintf(
+            "%s - %s",
+            test_cases[i].error_type_string.c_str(),
+            test_cases[i].message_type_for_buyer_string.c_str()));
     TestWalletErrorCode(test_cases[i].error_type_string,
-                        test_cases[i].buyer_error_type_string,
+                        test_cases[i].message_type_for_buyer_string,
                         test_cases[i].expected_error_type,
                         test_cases[i].expected_autofill_metric);
   }
diff --git a/components/autofill/content/renderer/autofill_agent.cc b/components/autofill/content/renderer/autofill_agent.cc
index ca4c46b..615390e 100644
--- a/components/autofill/content/renderer/autofill_agent.cc
+++ b/components/autofill/content/renderer/autofill_agent.cc
@@ -322,7 +322,7 @@
   content::SSLStatus ssl_status = render_view()->GetSSLStatusOfFrame(frame);
   FormData form_data;
   if (!in_flight_request_form_.isNull() ||
-      (url.SchemeIs(chrome::kHttpsScheme) &&
+      (url.SchemeIs(content::kHttpsScheme) &&
        (net::IsCertStatusError(ssl_status.cert_status) ||
         net::IsCertStatusMinorError(ssl_status.cert_status))) ||
       !WebFormElementToFormData(form,
diff --git a/components/autofill/core/browser/address.cc b/components/autofill/core/browser/address.cc
index 6f7f2a9..e96183c 100644
--- a/components/autofill/core/browser/address.cc
+++ b/components/autofill/core/browser/address.cc
@@ -44,8 +44,8 @@
 }
 
 base::string16 Address::GetRawInfo(ServerFieldType type) const {
-  // TODO(isherman): Is GetStorableType even necessary?
-  switch (AutofillType(type).GetStorableType()) {
+  DCHECK_EQ(ADDRESS_HOME, AutofillType(type).group());
+  switch (type) {
     case ADDRESS_HOME_LINE1:
       return line1_;
 
@@ -70,8 +70,8 @@
 }
 
 void Address::SetRawInfo(ServerFieldType type, const base::string16& value) {
-  // TODO(isherman): Is GetStorableType even necessary?
-  switch (AutofillType(type).GetStorableType()) {
+  DCHECK_EQ(ADDRESS_HOME, AutofillType(type).group());
+  switch (type) {
     case ADDRESS_HOME_LINE1:
       line1_ = value;
       break;
diff --git a/components/autofill/core/browser/autofill_external_delegate.cc b/components/autofill/core/browser/autofill_external_delegate.cc
index 95119bf..ca6696e 100644
--- a/components/autofill/core/browser/autofill_external_delegate.cc
+++ b/components/autofill/core/browser/autofill_external_delegate.cc
@@ -36,7 +36,7 @@
       display_warning_if_disabled_(false),
       has_autofill_suggestion_(false),
       has_shown_autofill_popup_for_current_edit_(false),
-      registered_keyboard_listener_with_(NULL),
+      registered_key_press_event_callback_with_(NULL),
       weak_ptr_factory_(this) {
   DCHECK(autofill_manager);
 }
@@ -162,10 +162,12 @@
 }
 
 void AutofillExternalDelegate::OnPopupShown(
-    content::KeyboardListener* listener) {
-  if (!registered_keyboard_listener_with_) {
-    registered_keyboard_listener_with_ = web_contents_->GetRenderViewHost();
-    registered_keyboard_listener_with_->AddKeyboardListener(listener);
+    content::RenderWidgetHost::KeyPressEventCallback* callback) {
+  if (callback && !registered_key_press_event_callback_with_) {
+    registered_key_press_event_callback_with_ =
+        web_contents_->GetRenderViewHost();
+    registered_key_press_event_callback_with_->AddKeyPressEventCallback(
+        *callback);
   }
 
   autofill_manager_->OnDidShowAutofillSuggestions(
@@ -174,14 +176,19 @@
 }
 
 void AutofillExternalDelegate::OnPopupHidden(
-    content::KeyboardListener* listener) {
-  if ((!web_contents_->IsBeingDestroyed()) &&
-      (registered_keyboard_listener_with_ ==
+    content::RenderWidgetHost::KeyPressEventCallback* callback) {
+  if (callback && (!web_contents_->IsBeingDestroyed()) &&
+      (registered_key_press_event_callback_with_ ==
           web_contents_->GetRenderViewHost())) {
-    web_contents_->GetRenderViewHost()->RemoveKeyboardListener(listener);
+    web_contents_->GetRenderViewHost()->RemoveKeyPressEventCallback(*callback);
   }
 
-  registered_keyboard_listener_with_ = NULL;
+  registered_key_press_event_callback_with_ = NULL;
+}
+
+bool AutofillExternalDelegate::ShouldRepostEvent(const ui::MouseEvent& event) {
+  NOTREACHED();
+  return true;
 }
 
 void AutofillExternalDelegate::DidSelectSuggestion(int identifier) {
diff --git a/components/autofill/core/browser/autofill_external_delegate.h b/components/autofill/core/browser/autofill_external_delegate.h
index 3362ff8..0a0093e 100644
--- a/components/autofill/core/browser/autofill_external_delegate.h
+++ b/components/autofill/core/browser/autofill_external_delegate.h
@@ -47,8 +47,11 @@
   virtual ~AutofillExternalDelegate();
 
   // AutofillPopupDelegate implementation.
-  virtual void OnPopupShown(content::KeyboardListener* listener) OVERRIDE;
-  virtual void OnPopupHidden(content::KeyboardListener* listener) OVERRIDE;
+  virtual void OnPopupShown(
+      content::RenderWidgetHost::KeyPressEventCallback* callback) OVERRIDE;
+  virtual void OnPopupHidden(
+      content::RenderWidgetHost::KeyPressEventCallback* callback) OVERRIDE;
+  virtual bool ShouldRepostEvent(const ui::MouseEvent& event) OVERRIDE;
   virtual void DidSelectSuggestion(int identifier) OVERRIDE;
   virtual void DidAcceptSuggestion(const base::string16& value,
                                    int identifier) OVERRIDE;
@@ -167,9 +170,9 @@
   // currently editing?  Used to keep track of state for metrics logging.
   bool has_shown_autofill_popup_for_current_edit_;
 
-  // The RenderViewHost that this object has been registered with as a
-  // keyboard listener.
-  content::RenderViewHost* registered_keyboard_listener_with_;
+  // The RenderViewHost that this object has been registered with as a key press
+  // event callback.
+  content::RenderViewHost* registered_key_press_event_callback_with_;
 
   // The current data list values.
   std::vector<base::string16> data_list_values_;
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index 4c66699..2d2c677 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -129,7 +129,7 @@
 }
 
 bool FormIsHTTPS(const FormStructure& form) {
-  return form.source_url().SchemeIs(chrome::kHttpsScheme);
+  return form.source_url().SchemeIs(content::kHttpsScheme);
 }
 
 // Uses the existing personal data in |profiles| and |credit_cards| to determine
diff --git a/components/autofill/core/browser/autofill_metrics.cc b/components/autofill/core/browser/autofill_metrics.cc
index 57743c3..a3dac3d 100644
--- a/components/autofill/core/browser/autofill_metrics.cc
+++ b/components/autofill/core/browser/autofill_metrics.cc
@@ -142,6 +142,7 @@
           break;
         case ::autofill::CREDIT_CARD_TYPE:
           group = CREDIT_CARD_TYPE;
+          break;
         default:
           group = CREDIT_CARD_DATE;
       }
diff --git a/components/autofill/core/browser/autofill_popup_delegate.h b/components/autofill/core/browser/autofill_popup_delegate.h
index ec9a220..86b5eff 100644
--- a/components/autofill/core/browser/autofill_popup_delegate.h
+++ b/components/autofill/core/browser/autofill_popup_delegate.h
@@ -6,9 +6,10 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_POPUP_DELEGATE_H_
 
 #include "base/strings/string16.h"
+#include "content/public/browser/render_view_host.h"
 
-namespace content {
-class KeyboardListener;
+namespace ui {
+class MouseEvent;
 }
 
 namespace autofill {
@@ -17,13 +18,19 @@
 // of events by the controller.
 class AutofillPopupDelegate {
  public:
-  // Called when the Autofill popup is shown. |listener| may be used to pass
+  // Called when the Autofill popup is shown. |callback| may be used to pass
   // keyboard events to the popup.
-  virtual void OnPopupShown(content::KeyboardListener* listener) = 0;
+  virtual void OnPopupShown(
+      content::RenderWidgetHost::KeyPressEventCallback* callback) = 0;
 
-  // Called when the Autofill popup is hidden. |listener| must be unregistered
+  // Called when the Autofill popup is hidden. |callback| must be unregistered
   // if it was registered in OnPopupShown.
-  virtual void OnPopupHidden(content::KeyboardListener* listener) = 0;
+  virtual void OnPopupHidden(
+      content::RenderWidgetHost::KeyPressEventCallback* callback) = 0;
+
+  // Called when the Autofill popup recieves a click outside of the popup view
+  // to determine if the event should be reposted to the native window manager.
+  virtual bool ShouldRepostEvent(const ui::MouseEvent& event) = 0;
 
   // Called when the autofill suggestion indicated by |identifier| has been
   // temporarily selected (e.g., hovered).
diff --git a/components/autofill/core/browser/contact_info.cc b/components/autofill/core/browser/contact_info.cc
index 603c4fa..b3ef985 100644
--- a/components/autofill/core/browser/contact_info.cc
+++ b/components/autofill/core/browser/contact_info.cc
@@ -52,8 +52,8 @@
 }
 
 base::string16 NameInfo::GetRawInfo(ServerFieldType type) const {
-  // TODO(isherman): Is GetStorableType even necessary?
-  switch (AutofillType(type).GetStorableType()) {
+  DCHECK_EQ(NAME, AutofillType(type).group());
+  switch (type) {
     case NAME_FIRST:
       return first();
 
@@ -75,10 +75,8 @@
 }
 
 void NameInfo::SetRawInfo(ServerFieldType type, const base::string16& value) {
-  // TODO(isherman): Is GetStorableType even necessary?
-  ServerFieldType storable_type = AutofillType(type).GetStorableType();
-  DCHECK_EQ(NAME, AutofillType(storable_type).group());
-  switch (storable_type) {
+  DCHECK_EQ(NAME, AutofillType(type).group());
+  switch (type) {
     case NAME_FIRST:
       first_ = value;
       break;
diff --git a/components/autofill/core/browser/credit_card.cc b/components/autofill/core/browser/credit_card.cc
index f3ac6b1..103bee9 100644
--- a/components/autofill/core/browser/credit_card.cc
+++ b/components/autofill/core/browser/credit_card.cc
@@ -276,6 +276,7 @@
 }
 
 base::string16 CreditCard::GetRawInfo(ServerFieldType type) const {
+  DCHECK_EQ(CREDIT_CARD, AutofillType(type).group());
   switch (type) {
     case CREDIT_CARD_NAME:
       return name_on_card_;
@@ -323,6 +324,7 @@
 
 void CreditCard::SetRawInfo(ServerFieldType type,
                             const base::string16& value) {
+  DCHECK_EQ(CREDIT_CARD, AutofillType(type).group());
   switch (type) {
     case CREDIT_CARD_NAME:
       name_on_card_ = value;
diff --git a/components/autofill/core/browser/phone_number.cc b/components/autofill/core/browser/phone_number.cc
index 2ee67f9..b068b03 100644
--- a/components/autofill/core/browser/phone_number.cc
+++ b/components/autofill/core/browser/phone_number.cc
@@ -74,8 +74,8 @@
 }
 
 base::string16 PhoneNumber::GetRawInfo(ServerFieldType type) const {
-  // TODO(isherman): Is GetStorableType even necessary?
-  if (AutofillType(type).GetStorableType() == PHONE_HOME_WHOLE_NUMBER)
+  DCHECK_EQ(PHONE_HOME, AutofillType(type).group());
+  if (type == PHONE_HOME_WHOLE_NUMBER)
     return number_;
 
   // Only the whole number is available as raw data.  All of the other types are
@@ -86,8 +86,7 @@
 
 void PhoneNumber::SetRawInfo(ServerFieldType type,
                              const base::string16& value) {
-  // TODO(isherman): Is GetStorableType even necessary?
-  type = AutofillType(type).GetStorableType();
+  DCHECK_EQ(PHONE_HOME, AutofillType(type).group());
   if (type != PHONE_HOME_CITY_AND_NUMBER && type != PHONE_HOME_WHOLE_NUMBER) {
     // Only full phone numbers should be set directly.  The remaining field
     // field types are read-only.
diff --git a/components/autofill/core/common/form_data.cc b/components/autofill/core/common/form_data.cc
index e0c3c1c..078a58d 100644
--- a/components/autofill/core/common/form_data.cc
+++ b/components/autofill/core/common/form_data.cc
@@ -4,10 +4,51 @@
 
 #include "components/autofill/core/common/form_data.h"
 
+#include "base/pickle.h"
 #include "base/strings/string_util.h"
+#include "components/autofill/core/common/form_field_data.h"
 
 namespace autofill {
 
+namespace {
+
+const int kPickleVersion = 1;
+
+bool ReadGURL(PickleIterator* iter, GURL* url) {
+  std::string spec;
+  if (!iter->ReadString(&spec))
+    return false;
+
+  *url = GURL(spec);
+  return true;
+}
+
+void SerializeFormFieldDataVector(const std::vector<FormFieldData> fields,
+                                  Pickle* pickle) {
+  pickle->WriteInt(static_cast<int>(fields.size()));
+  for (size_t i = 0; i < fields.size(); ++i) {
+    SerializeFormFieldData(fields[i], pickle);
+  }
+}
+
+bool DeserializeFormFieldDataVector(PickleIterator* iter,
+                                    std::vector<FormFieldData>* fields) {
+  int size;
+  if (!iter->ReadInt(&size))
+    return false;
+
+  FormFieldData temp;
+  for (int i = 0; i < size; ++i) {
+    if (!DeserializeFormFieldData(iter, &temp))
+      return false;
+
+    fields->push_back(temp);
+  }
+  return true;
+}
+
+}  // namespace
+
 FormData::FormData()
     : user_submitted(false) {
 }
@@ -37,4 +78,42 @@
   return !operator==(form);
 }
 
+void SerializeFormData(const FormData& form_data, Pickle* pickle) {
+  pickle->WriteInt(kPickleVersion);
+  pickle->WriteString16(form_data.name);
+  pickle->WriteString16(form_data.method);
+  pickle->WriteString(form_data.origin.spec());
+  pickle->WriteString(form_data.action.spec());
+  pickle->WriteBool(form_data.user_submitted);
+  SerializeFormFieldDataVector(form_data.fields, pickle);
+}
+
+bool DeserializeFormData(PickleIterator* iter, FormData* form_data) {
+  int version;
+  if (!iter->ReadInt(&version)) {
+    LOG(ERROR) << "Bad pickle of FormData, no version present";
+    return false;
+  }
+
+  switch (version) {
+    case 1: {
+      if (!iter->ReadString16(&form_data->name) ||
+          !iter->ReadString16(&form_data->method) ||
+          !ReadGURL(iter, &form_data->origin) ||
+          !ReadGURL(iter, &form_data->action) ||
+          !iter->ReadBool(&form_data->user_submitted) ||
+          !DeserializeFormFieldDataVector(iter, &form_data->fields)) {
+        LOG(ERROR) << "Could not deserialize FormData from pickle";
+        return false;
+      }
+      break;
+    }
+    default: {
+      LOG(ERROR) << "Unknown FormData pickle version " << version;
+      return false;
+    }
+  }
+  return true;
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/common/form_data.h b/components/autofill/core/common/form_data.h
index 202fb8d..1d6dffa 100644
--- a/components/autofill/core/common/form_data.h
+++ b/components/autofill/core/common/form_data.h
@@ -37,6 +37,13 @@
   std::vector<FormFieldData> fields;
 };
 
+// Serialize FormData. Used by the PasswordManager to persist FormData
+// pertaining to password forms. Serialized data is appended to |pickle|
+void SerializeFormData(const FormData& form_data, Pickle* pickle);
+// Deserialize FormData. This assumes that |iter| is currently pointing to
+// the part of a pickle created by SerializeFormData. Returns true on success.
+bool DeserializeFormData(PickleIterator* iter, FormData* form_data);
+
 }  // namespace autofill
 
 #endif  // COMPONENTS_AUTOFILL_CORE_COMMON_FORM_DATA_H__
diff --git a/components/autofill/core/common/form_data_unittest.cc b/components/autofill/core/common/form_data_unittest.cc
new file mode 100644
index 0000000..2d4b7aa
--- /dev/null
+++ b/components/autofill/core/common/form_data_unittest.cc
@@ -0,0 +1,57 @@
+// 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 "components/autofill/core/common/form_data.h"
+
+#include "base/pickle.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+TEST(FormDataTest, SerializeAndDeserialize) {
+  FormData data;
+  data.name = ASCIIToUTF16("name");
+  data.method = ASCIIToUTF16("POST");
+  data.origin = GURL("origin");
+  data.action = GURL("action");
+  data.user_submitted = true;
+
+  FormFieldData field_data;
+  field_data.label = ASCIIToUTF16("label");
+  field_data.name = ASCIIToUTF16("name");
+  field_data.value = ASCIIToUTF16("value");
+  field_data.form_control_type = "password";
+  field_data.autocomplete_attribute = "off";
+  field_data.max_length = 200;
+  field_data.is_autofilled = true;
+  field_data.is_checked = true;
+  field_data.is_checkable = true;
+  field_data.is_focusable = true;
+  field_data.should_autocomplete = false;
+  field_data.text_direction = base::i18n::RIGHT_TO_LEFT;
+  field_data.option_values.push_back(ASCIIToUTF16("First"));
+  field_data.option_values.push_back(ASCIIToUTF16("Second"));
+  field_data.option_contents.push_back(ASCIIToUTF16("First"));
+  field_data.option_contents.push_back(ASCIIToUTF16("Second"));
+
+  data.fields.push_back(field_data);
+
+  // Change a few fields.
+  field_data.max_length = 150;
+  field_data.option_values.push_back(ASCIIToUTF16("Third"));
+  field_data.option_contents.push_back(ASCIIToUTF16("Third"));
+  data.fields.push_back(field_data);
+
+  Pickle pickle;
+  SerializeFormData(data, &pickle);
+
+  PickleIterator iter(pickle);
+  FormData actual;
+  EXPECT_TRUE(DeserializeFormData(&iter, &actual));
+
+  EXPECT_EQ(actual, data);
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/common/form_field_data.cc b/components/autofill/core/common/form_field_data.cc
index 1de786d..afa9f53 100644
--- a/components/autofill/core/common/form_field_data.cc
+++ b/components/autofill/core/common/form_field_data.cc
@@ -4,9 +4,59 @@
 
 #include "components/autofill/core/common/form_field_data.h"
 
+#include "base/pickle.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 
+namespace {
+
+const int kPickleVersion = 1;
+
+void AddVectorToPickle(std::vector<base::string16> strings,
+                       Pickle* pickle) {
+  pickle->WriteInt(static_cast<int>(strings.size()));
+  for (size_t i = 0; i < strings.size(); ++i) {
+    pickle->WriteString16(strings[i]);
+  }
+}
+
+bool ReadStringVector(PickleIterator* iter,
+                      std::vector<base::string16>* strings) {
+  int size;
+  if (!iter->ReadInt(&size))
+    return false;
+
+  string16 pickle_data;
+  for (int i = 0; i < size; i++) {
+    if (!iter->ReadString16(&pickle_data))
+      return false;
+
+    strings->push_back(pickle_data);
+  }
+  return true;
+}
+
+bool ReadTextDirection(PickleIterator* iter,
+                       base::i18n::TextDirection* direction) {
+  int pickle_data;
+  if (!iter->ReadInt(&pickle_data))
+    return false;
+
+  *direction = static_cast<base::i18n::TextDirection>(pickle_data);
+  return true;
+}
+
+bool ReadSize(PickleIterator* iter, size_t* size) {
+  uint64 pickle_data;
+  if (!iter->ReadUInt64(&pickle_data))
+    return false;
+
+  *size = static_cast<size_t>(pickle_data);
+  return true;
+}
+
+}  // namespace
+
 namespace autofill {
 
 FormFieldData::FormFieldData()
@@ -43,6 +93,62 @@
   return label < field.label;
 }
 
+void SerializeFormFieldData(const FormFieldData& field_data,
+                            Pickle* pickle) {
+  pickle->WriteInt(kPickleVersion);
+  pickle->WriteString16(field_data.label);
+  pickle->WriteString16(field_data.name);
+  pickle->WriteString16(field_data.value);
+  pickle->WriteString(field_data.form_control_type);
+  pickle->WriteString(field_data.autocomplete_attribute);
+  pickle->WriteUInt64(static_cast<uint64>(field_data.max_length));
+  pickle->WriteBool(field_data.is_autofilled);
+  pickle->WriteBool(field_data.is_checked);
+  pickle->WriteBool(field_data.is_checkable);
+  pickle->WriteBool(field_data.is_focusable);
+  pickle->WriteBool(field_data.should_autocomplete);
+  pickle->WriteInt(field_data.text_direction);
+  AddVectorToPickle(field_data.option_values, pickle);
+  AddVectorToPickle(field_data.option_contents, pickle);
+}
+
+bool DeserializeFormFieldData(PickleIterator* iter,
+                              FormFieldData* field_data) {
+  int version;
+  if (!iter->ReadInt(&version)) {
+    LOG(ERROR) << "Bad pickle of FormFieldData, no version present";
+    return false;
+  }
+
+  switch (version) {
+    case 1: {
+      if (!iter->ReadString16(&field_data->label) ||
+          !iter->ReadString16(&field_data->name) ||
+          !iter->ReadString16(&field_data->value) ||
+          !iter->ReadString(&field_data->form_control_type) ||
+          !iter->ReadString(&field_data->autocomplete_attribute) ||
+          !ReadSize(iter, &field_data->max_length) ||
+          !iter->ReadBool(&field_data->is_autofilled) ||
+          !iter->ReadBool(&field_data->is_checked) ||
+          !iter->ReadBool(&field_data->is_checkable) ||
+          !iter->ReadBool(&field_data->is_focusable) ||
+          !iter->ReadBool(&field_data->should_autocomplete) ||
+          !ReadTextDirection(iter, &field_data->text_direction) ||
+          !ReadStringVector(iter, &field_data->option_values) ||
+          !ReadStringVector(iter, &field_data->option_contents)) {
+        LOG(ERROR) << "Could not deserialize FormFieldData from pickle";
+        return false;
+      }
+      break;
+    }
+    default: {
+      LOG(ERROR) << "Unknown FormFieldData pickle version " << version;
+      return false;
+    }
+  }
+  return true;
+}
+
 std::ostream& operator<<(std::ostream& os, const FormFieldData& field) {
   return os
       << UTF16ToUTF8(field.label)
diff --git a/components/autofill/core/common/form_field_data.h b/components/autofill/core/common/form_field_data.h
index f0e7579..1bf163e 100644
--- a/components/autofill/core/common/form_field_data.h
+++ b/components/autofill/core/common/form_field_data.h
@@ -10,6 +10,9 @@
 #include "base/i18n/rtl.h"
 #include "base/strings/string16.h"
 
+class Pickle;
+class PickleIterator;
+
 namespace autofill {
 
 // Stores information about a field in a form.
@@ -45,6 +48,13 @@
   std::vector<base::string16> option_contents;
 };
 
+// Serialize and deserialize FormFieldData. These are used when FormData objects
+// are serialized and deserialized.
+void SerializeFormFieldData(const FormFieldData& form_field_data,
+                            Pickle* serialized);
+bool DeserializeFormFieldData(PickleIterator* pickle_iterator,
+                              FormFieldData* form_field_data);
+
 // So we can compare FormFieldDatas with EXPECT_EQ().
 std::ostream& operator<<(std::ostream& os, const FormFieldData& field);
 
@@ -66,4 +76,3 @@
 }  // namespace autofill
 
 #endif  // COMPONENTS_AUTOFILL_CORE_COMMON_FORM_FIELD_DATA_H_
-
diff --git a/components/autofill/core/common/form_field_data_unittest.cc b/components/autofill/core/common/form_field_data_unittest.cc
new file mode 100644
index 0000000..7f4ef09
--- /dev/null
+++ b/components/autofill/core/common/form_field_data_unittest.cc
@@ -0,0 +1,43 @@
+// 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 "components/autofill/core/common/form_field_data.h"
+
+#include "base/i18n/rtl.h"
+#include "base/pickle.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+TEST(FormFieldDataTest, SerializeAndDeserialize) {
+  FormFieldData data;
+  data.label = ASCIIToUTF16("label");
+  data.name = ASCIIToUTF16("name");
+  data.value = ASCIIToUTF16("value");
+  data.form_control_type = "password";
+  data.autocomplete_attribute = "off";
+  data.max_length = 200;
+  data.is_autofilled = true;
+  data.is_checked = true;
+  data.is_checkable = true;
+  data.is_focusable = true;
+  data.should_autocomplete = false;
+  data.text_direction = base::i18n::RIGHT_TO_LEFT;
+  data.option_values.push_back(ASCIIToUTF16("First"));
+  data.option_values.push_back(ASCIIToUTF16("Second"));
+  data.option_contents.push_back(ASCIIToUTF16("First"));
+  data.option_contents.push_back(ASCIIToUTF16("Second"));
+
+  Pickle pickle;
+  SerializeFormFieldData(data, &pickle);
+
+  PickleIterator iter(pickle);
+  FormFieldData actual;
+  EXPECT_TRUE(DeserializeFormFieldData(&iter, &actual));
+
+  EXPECT_EQ(actual, data);
+}
+
+}  // namespace autofill
diff --git a/components/components.gyp b/components/components.gyp
index 602650c..f3be9de 100644
--- a/components/components.gyp
+++ b/components/components.gyp
@@ -20,6 +20,7 @@
     'policy.gypi',
     'sessions.gypi',
     'user_prefs.gypi',
+    'variations.gypi',
     'visitedlink.gypi',
     'webdata.gypi',
     'web_contents_delegate_android.gypi',
diff --git a/components/components_tests.gypi b/components/components_tests.gypi
index 61346d0..910d2e6 100644
--- a/components/components_tests.gypi
+++ b/components/components_tests.gypi
@@ -10,6 +10,8 @@
           'target_name': 'components_unittests',
           'type': '<(gtest_target_type)',
           'sources': [
+            'autofill/core/common/form_data_unittest.cc',
+            'autofill/core/common/form_field_data_unittest.cc',
             'auto_login_parser/auto_login_parser_unittest.cc',
             'browser_context_keyed_service/browser_context_dependency_manager_unittest.cc',
             'browser_context_keyed_service/dependency_graph_unittest.cc',
@@ -19,6 +21,11 @@
             'navigation_interception/intercept_navigation_resource_throttle_unittest.cc',
             'sessions/serialized_navigation_entry_unittest.cc',
             'test/run_all_unittests.cc',
+            # TODO(asvitkine): These should be tested on iOS too.
+            'variations/entropy_provider_unittest.cc',
+            'variations/metrics_util_unittest.cc',
+            'variations/variations_associated_data_unittest.cc',
+            'variations/variations_seed_processor_unittest.cc',
             'visitedlink/test/visitedlink_unittest.cc',
             'webdata/encryptor/encryptor_password_mac_unittest.cc',
             'webdata/encryptor/encryptor_unittest.cc',
@@ -32,6 +39,9 @@
             '../testing/gmock.gyp:gmock',
             '../testing/gtest.gyp:gtest',
 
+            # Dependencies of autofill
+            'autofill_core_common',
+
             # Dependencies of auto_login_parser
             'auto_login_parser',
 
@@ -57,6 +67,9 @@
             'sessions',
             'sessions_test_support',
 
+            # Dependencies of variations
+            'variations',
+
             # Dependencies of visitedlink
             'visitedlink_browser',
             'visitedlink_renderer',
diff --git a/components/nacl/broker/nacl_broker_listener.cc b/components/nacl/broker/nacl_broker_listener.cc
index cc365d6..c4f268d 100644
--- a/components/nacl/broker/nacl_broker_listener.cc
+++ b/components/nacl/broker/nacl_broker_listener.cc
@@ -105,9 +105,18 @@
 
     loader_process = content::StartSandboxedProcess(this, cmd_line);
     if (loader_process) {
+      // Note: PROCESS_DUP_HANDLE is necessary here, because:
+      // 1) The current process is the broker, which is the loader's parent.
+      // 2) The browser is not the loader's parent, and so only gets the
+      //    access rights we confer here.
+      // 3) The browser calls DuplicateHandle to set up communications with
+      //    the loader.
+      // 4) The target process handle to DuplicateHandle needs to have
+      //    PROCESS_DUP_HANDLE access rights.
       DuplicateHandle(::GetCurrentProcess(), loader_process,
           browser_handle_, &loader_handle_in_browser,
-          PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION , FALSE, 0);
+          PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE,
+          FALSE, 0);
       base::CloseProcessHandle(loader_process);
     }
   }
diff --git a/components/sessions/serialized_navigation_entry.cc b/components/sessions/serialized_navigation_entry.cc
index 5e4183c..0fc6ff1 100644
--- a/components/sessions/serialized_navigation_entry.cc
+++ b/components/sessions/serialized_navigation_entry.cc
@@ -26,6 +26,7 @@
       has_post_data_(false),
       post_id_(-1),
       is_overriding_user_agent_(false),
+      http_status_code_(0),
       blocked_state_(STATE_INVALID) {}
 
 SerializedNavigationEntry::~SerializedNavigationEntry() {}
@@ -53,6 +54,7 @@
   entry.GetExtraData(kSearchTermsKey, &navigation.search_terms_);
   if (entry.GetFavicon().valid)
     navigation.favicon_url_ = entry.GetFavicon().url;
+  navigation.http_status_code_ = entry.GetHttpStatusCode();
 
   return navigation;
 }
@@ -143,6 +145,8 @@
   if (sync_data.has_favicon_url())
     navigation.favicon_url_ = GURL(sync_data.favicon_url());
 
+  navigation.http_status_code_ = sync_data.http_status_code();
+
   // We shouldn't sync session data for managed users down at the moment.
   DCHECK(!sync_data.has_blocked_state());
   DCHECK_EQ(0, sync_data.content_pack_categories_size());
@@ -216,6 +220,7 @@
 // is_overriding_user_agent_
 // timestamp_
 // search_terms_
+// http_status_code_
 
 void SerializedNavigationEntry::WriteToPickle(int max_size,
                                               Pickle* pickle) const {
@@ -255,6 +260,8 @@
   pickle->WriteInt64(timestamp_.ToInternalValue());
 
   WriteString16ToPickle(pickle, &bytes_written, max_size, search_terms_);
+
+  pickle->WriteInt(http_status_code_);
 }
 
 bool SerializedNavigationEntry::ReadFromPickle(PickleIterator* iterator) {
@@ -313,6 +320,9 @@
     // If the search terms field can't be found, leave it empty.
     if (!iterator->ReadString16(&search_terms_))
       search_terms_.clear();
+
+    if (!iterator->ReadInt(&http_status_code_))
+      http_status_code_ = 0;
   }
 
   return true;
@@ -342,6 +352,7 @@
   entry->SetIsOverridingUserAgent(is_overriding_user_agent_);
   entry->SetTimestamp(timestamp_);
   entry->SetExtraData(kSearchTermsKey, search_terms_);
+  entry->SetHttpStatusCode(http_status_code_);
 
   // These fields should have default values.
   DCHECK_EQ(STATE_INVALID, blocked_state_);
@@ -440,6 +451,8 @@
 
   sync_data.set_search_terms(UTF16ToUTF8(search_terms_));
 
+  sync_data.set_http_status_code(http_status_code_);
+
   if (favicon_url_.is_valid())
     sync_data.set_favicon_url(favicon_url_.spec());
 
diff --git a/components/sessions/serialized_navigation_entry.h b/components/sessions/serialized_navigation_entry.h
index e5813dc..80581cd 100644
--- a/components/sessions/serialized_navigation_entry.h
+++ b/components/sessions/serialized_navigation_entry.h
@@ -100,6 +100,7 @@
   const content::PageState& page_state() const { return page_state_; }
   const string16& search_terms() const { return search_terms_; }
   const GURL& favicon_url() const { return favicon_url_; }
+  int http_status_code() const { return http_status_code_; }
   const content::Referrer& referrer() const { return referrer_; }
   content::PageTransition transition_type() const {
     return transition_type_;
@@ -149,6 +150,7 @@
   base::Time timestamp_;
   string16 search_terms_;
   GURL favicon_url_;
+  int http_status_code_;
 
   // Additional information.
   BlockedState blocked_state_;
diff --git a/components/sessions/serialized_navigation_entry_test_helper.cc b/components/sessions/serialized_navigation_entry_test_helper.cc
index f4a7e2e..64d3be2 100644
--- a/components/sessions/serialized_navigation_entry_test_helper.cc
+++ b/components/sessions/serialized_navigation_entry_test_helper.cc
@@ -43,6 +43,7 @@
   navigation.page_state_ =
       content::PageState::CreateFromEncodedData("fake_state");
   navigation.timestamp_ = base::Time::Now();
+  navigation.http_status_code_ = 200;
   return navigation;
 }
 
diff --git a/components/sessions/serialized_navigation_entry_unittest.cc b/components/sessions/serialized_navigation_entry_unittest.cc
index 5f052fe..6aa35a1 100644
--- a/components/sessions/serialized_navigation_entry_unittest.cc
+++ b/components/sessions/serialized_navigation_entry_unittest.cc
@@ -48,6 +48,7 @@
 const base::Time kTimestamp = syncer::ProtoTimeToTime(100);
 const string16 kSearchTerms = ASCIIToUTF16("my search terms");
 const GURL kFaviconURL("http://virtual-url.com/favicon.ico");
+const int kHttpStatusCode = 404;
 
 const int kPageID = 10;
 
@@ -68,6 +69,7 @@
   navigation_entry->SetExtraData(kSearchTermsKey, kSearchTerms);
   navigation_entry->GetFavicon().valid = true;
   navigation_entry->GetFavicon().url = kFaviconURL;
+  navigation_entry->SetHttpStatusCode(kHttpStatusCode);
   return navigation_entry.Pass();
 }
 
@@ -86,6 +88,7 @@
   sync_data.set_navigation_home_page(true);
   sync_data.set_search_terms(UTF16ToUTF8(kSearchTerms));
   sync_data.set_favicon_url(kFaviconURL.spec());
+  sync_data.set_http_status_code(kHttpStatusCode);
   return sync_data;
 }
 
@@ -108,6 +111,7 @@
   EXPECT_TRUE(navigation.timestamp().is_null());
   EXPECT_TRUE(navigation.search_terms().empty());
   EXPECT_FALSE(navigation.favicon_url().is_valid());
+  EXPECT_EQ(0, navigation.http_status_code());
 }
 
 // Create a SerializedNavigationEntry from a NavigationEntry.  All its fields
@@ -134,6 +138,7 @@
   EXPECT_EQ(kIsOverridingUserAgent, navigation.is_overriding_user_agent());
   EXPECT_EQ(kTimestamp, navigation.timestamp());
   EXPECT_EQ(kFaviconURL, navigation.favicon_url());
+  EXPECT_EQ(kHttpStatusCode, navigation.http_status_code());
 }
 
 // Create a SerializedNavigationEntry from a sync_pb::TabNavigation.  All its
@@ -160,6 +165,7 @@
   EXPECT_TRUE(navigation.timestamp().is_null());
   EXPECT_EQ(kSearchTerms, navigation.search_terms());
   EXPECT_EQ(kFaviconURL, navigation.favicon_url());
+  EXPECT_EQ(kHttpStatusCode, navigation.http_status_code());
 }
 
 // Create a SerializedNavigationEntry, pickle it, then create another one by
@@ -192,6 +198,7 @@
   EXPECT_EQ(kIsOverridingUserAgent, new_navigation.is_overriding_user_agent());
   EXPECT_EQ(kTimestamp, new_navigation.timestamp());
   EXPECT_EQ(kSearchTerms, new_navigation.search_terms());
+  EXPECT_EQ(kHttpStatusCode, new_navigation.http_status_code());
 }
 
 // Create a NavigationEntry, then create another one by converting to
@@ -226,6 +233,7 @@
   string16 search_terms;
   new_navigation_entry->GetExtraData(kSearchTermsKey, &search_terms);
   EXPECT_EQ(kSearchTerms, search_terms);
+  EXPECT_EQ(kHttpStatusCode, new_navigation_entry->GetHttpStatusCode());
 }
 
 // Create a NavigationEntry, convert it to a SerializedNavigationEntry, then
@@ -251,6 +259,7 @@
   EXPECT_EQ(syncer::TimeToProtoTime(kTimestamp), sync_data.timestamp_msec());
   EXPECT_EQ(kTimestamp.ToInternalValue(), sync_data.global_id());
   EXPECT_EQ(kFaviconURL.spec(), sync_data.favicon_url());
+  EXPECT_EQ(kHttpStatusCode, sync_data.http_status_code());
 }
 
 // Ensure all transition types and qualifiers are converted to/from the sync
diff --git a/components/variations.gypi b/components/variations.gypi
new file mode 100644
index 0000000..5bf6b0e
--- /dev/null
+++ b/components/variations.gypi
@@ -0,0 +1,47 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+  'targets': [
+    {
+      'target_name': 'variations',
+      'type': 'static_library',
+      'include_dirs': [
+        '..',
+      ],
+      'dependencies': [
+        '../base/base.gyp:base',
+        '../third_party/mt19937ar/mt19937ar.gyp:mt19937ar',
+        'variations_seed_proto',
+      ],
+      'sources': [
+        'variations/entropy_provider.cc',
+        'variations/entropy_provider.h',
+        'variations/metrics_util.cc',
+        'variations/metrics_util.h',
+        'variations/variations_associated_data.cc',
+        'variations/variations_associated_data.h',
+        'variations/variations_seed_processor.cc',
+        'variations/variations_seed_processor.h',
+      ],
+    },
+    {
+      # Protobuf compiler / generator for Chrome Variations seed.
+      'target_name': 'variations_seed_proto',
+      'type': 'static_library',
+      'include_dirs': [
+        '..',
+      ],
+      'sources': [
+        'variations/proto/trials_seed.proto',
+        'variations/proto/study.proto',
+      ],
+      'variables': {
+        'proto_in_dir': 'variations/proto',
+        'proto_out_dir': 'components/variations/proto',
+      },
+      'includes': [ '../build/protoc.gypi' ]
+    },
+  ],
+}
diff --git a/components/variations/OWNERS b/components/variations/OWNERS
new file mode 100644
index 0000000..aadd23c
--- /dev/null
+++ b/components/variations/OWNERS
@@ -0,0 +1,3 @@
+asvitkine@chromium.org
+jwd@chromium.org
+stevet@chromium.org
diff --git a/components/variations/entropy_provider.cc b/components/variations/entropy_provider.cc
new file mode 100644
index 0000000..a547cb1
--- /dev/null
+++ b/components/variations/entropy_provider.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/variations/entropy_provider.h"
+
+#include <algorithm>
+#include <limits>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/sha1.h"
+#include "base/sys_byteorder.h"
+#include "components/variations/metrics_util.h"
+
+namespace metrics {
+
+namespace internal {
+
+SeededRandGenerator::SeededRandGenerator(uint32 seed) {
+  mersenne_twister_.init_genrand(seed);
+}
+
+SeededRandGenerator::~SeededRandGenerator() {
+}
+
+uint32 SeededRandGenerator::operator()(uint32 range) {
+  // Based on base::RandGenerator().
+  DCHECK_GT(range, 0u);
+
+  // We must discard random results above this number, as they would
+  // make the random generator non-uniform (consider e.g. if
+  // MAX_UINT64 was 7 and |range| was 5, then a result of 1 would be twice
+  // as likely as a result of 3 or 4).
+  uint32 max_acceptable_value =
+      (std::numeric_limits<uint32>::max() / range) * range - 1;
+
+  uint32 value;
+  do {
+    value = mersenne_twister_.genrand_int32();
+  } while (value > max_acceptable_value);
+
+  return value % range;
+}
+
+void PermuteMappingUsingRandomizationSeed(uint32 randomization_seed,
+                                          std::vector<uint16>* mapping) {
+  for (size_t i = 0; i < mapping->size(); ++i)
+    (*mapping)[i] = static_cast<uint16>(i);
+
+  SeededRandGenerator generator(randomization_seed);
+  std::random_shuffle(mapping->begin(), mapping->end(), generator);
+}
+
+}  // namespace internal
+
+SHA1EntropyProvider::SHA1EntropyProvider(const std::string& entropy_source)
+    : entropy_source_(entropy_source) {
+}
+
+SHA1EntropyProvider::~SHA1EntropyProvider() {
+}
+
+double SHA1EntropyProvider::GetEntropyForTrial(
+    const std::string& trial_name,
+    uint32 randomization_seed) const {
+  // Given enough input entropy, SHA-1 will produce a uniformly random spread
+  // in its output space. In this case, the input entropy that is used is the
+  // combination of the original |entropy_source_| and the |trial_name|.
+  //
+  // Note: If |entropy_source_| has very low entropy, such as 13 bits or less,
+  // it has been observed that this method does not result in a uniform
+  // distribution given the same |trial_name|. When using such a low entropy
+  // source, PermutedEntropyProvider should be used instead.
+  std::string input(entropy_source_ + trial_name);
+  unsigned char sha1_hash[base::kSHA1Length];
+  base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()),
+                      input.size(),
+                      sha1_hash);
+
+  uint64 bits;
+  COMPILE_ASSERT(sizeof(bits) < sizeof(sha1_hash), need_more_data);
+  memcpy(&bits, sha1_hash, sizeof(bits));
+  bits = base::ByteSwapToLE64(bits);
+
+  return base::BitsToOpenEndedUnitInterval(bits);
+}
+
+PermutedEntropyProvider::PermutedEntropyProvider(
+    uint16 low_entropy_source,
+    size_t low_entropy_source_max)
+    : low_entropy_source_(low_entropy_source),
+      low_entropy_source_max_(low_entropy_source_max) {
+  DCHECK_LT(low_entropy_source, low_entropy_source_max);
+  DCHECK_LE(low_entropy_source_max, std::numeric_limits<uint16>::max());
+}
+
+PermutedEntropyProvider::~PermutedEntropyProvider() {
+}
+
+double PermutedEntropyProvider::GetEntropyForTrial(
+    const std::string& trial_name,
+    uint32 randomization_seed) const {
+  if (randomization_seed == 0)
+    randomization_seed = HashName(trial_name);
+
+  return GetPermutedValue(randomization_seed) /
+         static_cast<double>(low_entropy_source_max_);
+}
+
+uint16 PermutedEntropyProvider::GetPermutedValue(
+    uint32 randomization_seed) const {
+  std::vector<uint16> mapping(low_entropy_source_max_);
+  internal::PermuteMappingUsingRandomizationSeed(randomization_seed, &mapping);
+  return mapping[low_entropy_source_];
+}
+
+}  // namespace metrics
diff --git a/components/variations/entropy_provider.h b/components/variations/entropy_provider.h
new file mode 100644
index 0000000..786ae28
--- /dev/null
+++ b/components/variations/entropy_provider.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VARIATIONS_ENTROPY_PROVIDER_H_
+#define COMPONENTS_VARIATIONS_ENTROPY_PROVIDER_H_
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/metrics/field_trial.h"
+#include "third_party/mt19937ar/mt19937ar.h"
+
+namespace metrics {
+
+// Internals of entropy_provider.cc exposed for testing.
+namespace internal {
+
+// A functor that generates random numbers based on a seed, using the Mersenne
+// Twister algorithm. Suitable for use with std::random_shuffle().
+struct SeededRandGenerator : std::unary_function<uint32, uint32> {
+  explicit SeededRandGenerator(uint32 seed);
+  ~SeededRandGenerator();
+
+  // Returns a random number in range [0, range).
+  uint32 operator()(uint32 range);
+
+  MersenneTwister mersenne_twister_;
+};
+
+// Fills |mapping| to create a bijection of values in the range of
+// [0, |mapping.size()|), permuted based on |randomization_seed|.
+void PermuteMappingUsingRandomizationSeed(uint32 randomization_seed,
+                                          std::vector<uint16>* mapping);
+
+}  // namespace internal
+
+// SHA1EntropyProvider is an entropy provider suitable for high entropy
+// sources. It works by taking the first 64 bits of the SHA1 hash of the
+// entropy source concatenated with the trial name and using that for the
+// final entropy value.
+class SHA1EntropyProvider : public base::FieldTrial::EntropyProvider {
+ public:
+  // Creates a SHA1EntropyProvider with the given |entropy_source|, which
+  // should contain a large amount of entropy - for example, a textual
+  // representation of a persistent randomly-generated 128-bit value.
+  explicit SHA1EntropyProvider(const std::string& entropy_source);
+  virtual ~SHA1EntropyProvider();
+
+  // base::FieldTrial::EntropyProvider implementation:
+  virtual double GetEntropyForTrial(const std::string& trial_name,
+                                    uint32 randomization_seed) const OVERRIDE;
+
+ private:
+  std::string entropy_source_;
+
+  DISALLOW_COPY_AND_ASSIGN(SHA1EntropyProvider);
+};
+
+// PermutedEntropyProvider is an entropy provider suitable for low entropy
+// sources (below 16 bits). It uses the field trial name to generate a
+// permutation of a mapping array from an initial entropy value to a new value.
+// Note: This provider's performance is O(2^n), where n is the number of bits
+// in the entropy source.
+class PermutedEntropyProvider : public base::FieldTrial::EntropyProvider {
+ public:
+  // Creates a PermutedEntropyProvider with the given |low_entropy_source|,
+  // which should have a value in the range of [0, low_entropy_source_max).
+  PermutedEntropyProvider(uint16 low_entropy_source,
+                          size_t low_entropy_source_max);
+  virtual ~PermutedEntropyProvider();
+
+  // base::FieldTrial::EntropyProvider implementation:
+  virtual double GetEntropyForTrial(const std::string& trial_name,
+                                    uint32 randomization_seed) const OVERRIDE;
+
+ protected:
+  // Performs the permutation algorithm and returns the permuted value that
+  // corresponds to |low_entropy_source_|.
+  virtual uint16 GetPermutedValue(uint32 randomization_seed) const;
+
+ private:
+  uint16 low_entropy_source_;
+  size_t low_entropy_source_max_;
+
+  DISALLOW_COPY_AND_ASSIGN(PermutedEntropyProvider);
+};
+
+}  // namespace metrics
+
+#endif  // COMPONENTS_VARIATIONS_ENTROPY_PROVIDER_H_
diff --git a/components/variations/entropy_provider_unittest.cc b/components/variations/entropy_provider_unittest.cc
new file mode 100644
index 0000000..4e9a637
--- /dev/null
+++ b/components/variations/entropy_provider_unittest.cc
@@ -0,0 +1,369 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/variations/entropy_provider.h"
+
+#include <cmath>
+#include <limits>
+#include <numeric>
+
+#include "base/basictypes.h"
+#include "base/guid.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/rand_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/variations/metrics_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace metrics {
+
+namespace {
+
+// Size of the low entropy source to use for the permuted entropy provider
+// in tests.
+const size_t kMaxLowEntropySize = 8000;
+
+// Field trial names used in unit tests.
+const char* const kTestTrialNames[] = { "TestTrial", "AnotherTestTrial",
+                                        "NewTabButton" };
+
+// Computes the Chi-Square statistic for |values| assuming they follow a uniform
+// distribution, where each entry has expected value |expected_value|.
+//
+// The Chi-Square statistic is defined as Sum((O-E)^2/E) where O is the observed
+// value and E is the expected value.
+double ComputeChiSquare(const std::vector<int>& values,
+                        double expected_value) {
+  double sum = 0;
+  for (size_t i = 0; i < values.size(); ++i) {
+    const double delta = values[i] - expected_value;
+    sum += (delta * delta) / expected_value;
+  }
+  return sum;
+}
+
+// Computes SHA1-based entropy for the given |trial_name| based on
+// |entropy_source|
+double GenerateSHA1Entropy(const std::string& entropy_source,
+                           const std::string& trial_name) {
+  SHA1EntropyProvider sha1_provider(entropy_source);
+  return sha1_provider.GetEntropyForTrial(trial_name, 0);
+}
+
+// Generates permutation-based entropy for the given |trial_name| based on
+// |entropy_source| which must be in the range [0, entropy_max).
+double GeneratePermutedEntropy(uint16 entropy_source,
+                               size_t entropy_max,
+                               const std::string& trial_name) {
+  PermutedEntropyProvider permuted_provider(entropy_source, entropy_max);
+  return permuted_provider.GetEntropyForTrial(trial_name, 0);
+}
+
+// Helper interface for testing used to generate entropy values for a given
+// field trial. Unlike EntropyProvider, which keeps the low/high entropy source
+// value constant and generates entropy for different trial names, instances
+// of TrialEntropyGenerator keep the trial name constant and generate low/high
+// entropy source values internally to produce each output entropy value.
+class TrialEntropyGenerator {
+ public:
+  virtual ~TrialEntropyGenerator() {}
+  virtual double GenerateEntropyValue() const = 0;
+};
+
+// An TrialEntropyGenerator that uses the SHA1EntropyProvider with the high
+// entropy source (random GUID with 128 bits of entropy + 13 additional bits of
+// entropy corresponding to a low entropy source).
+class SHA1EntropyGenerator : public TrialEntropyGenerator {
+ public:
+  explicit SHA1EntropyGenerator(const std::string& trial_name)
+      : trial_name_(trial_name) {
+  }
+
+  virtual ~SHA1EntropyGenerator() {
+  }
+
+  virtual double GenerateEntropyValue() const OVERRIDE {
+    // Use a random GUID + 13 additional bits of entropy to match how the
+    // SHA1EntropyProvider is used in metrics_service.cc.
+    const int low_entropy_source =
+        static_cast<uint16>(base::RandInt(0, kMaxLowEntropySize - 1));
+    const std::string high_entropy_source =
+        base::GenerateGUID() + base::IntToString(low_entropy_source);
+    return GenerateSHA1Entropy(high_entropy_source, trial_name_);
+  }
+
+ private:
+  std::string trial_name_;
+
+  DISALLOW_COPY_AND_ASSIGN(SHA1EntropyGenerator);
+};
+
+// An TrialEntropyGenerator that uses the permuted entropy provider algorithm,
+// using 13-bit low entropy source values.
+class PermutedEntropyGenerator : public TrialEntropyGenerator {
+ public:
+  explicit PermutedEntropyGenerator(const std::string& trial_name)
+      : mapping_(kMaxLowEntropySize) {
+    // Note: Given a trial name, the computed mapping will be the same.
+    // As a performance optimization, pre-compute the mapping once per trial
+    // name and index into it for each entropy value.
+    const uint32 randomization_seed = HashName(trial_name);
+    internal::PermuteMappingUsingRandomizationSeed(randomization_seed,
+                                                   &mapping_);
+  }
+
+  virtual ~PermutedEntropyGenerator() {
+  }
+
+  virtual double GenerateEntropyValue() const OVERRIDE {
+    const int low_entropy_source =
+        static_cast<uint16>(base::RandInt(0, kMaxLowEntropySize - 1));
+    return mapping_[low_entropy_source] /
+           static_cast<double>(kMaxLowEntropySize);
+  }
+
+ private:
+  std::vector<uint16> mapping_;
+
+  DISALLOW_COPY_AND_ASSIGN(PermutedEntropyGenerator);
+};
+
+// Tests uniformity of a given |entropy_generator| using the Chi-Square Goodness
+// of Fit Test.
+void PerformEntropyUniformityTest(
+    const std::string& trial_name,
+    const TrialEntropyGenerator& entropy_generator) {
+  // Number of buckets in the simulated field trials.
+  const size_t kBucketCount = 20;
+  // Max number of iterations to perform before giving up and failing.
+  const size_t kMaxIterationCount = 100000;
+  // The number of iterations to perform before each time the statistical
+  // significance of the results is checked.
+  const size_t kCheckIterationCount = 10000;
+  // This is the Chi-Square threshold from the Chi-Square statistic table for
+  // 19 degrees of freedom (based on |kBucketCount|) with a 99.9% confidence
+  // level. See: http://www.medcalc.org/manual/chi-square-table.php
+  const double kChiSquareThreshold = 43.82;
+
+  std::vector<int> distribution(kBucketCount);
+
+  for (size_t i = 1; i <= kMaxIterationCount; ++i) {
+    const double entropy_value = entropy_generator.GenerateEntropyValue();
+    const size_t bucket = static_cast<size_t>(kBucketCount * entropy_value);
+    ASSERT_LT(bucket, kBucketCount);
+    distribution[bucket] += 1;
+
+    // After |kCheckIterationCount| iterations, compute the Chi-Square
+    // statistic of the distribution. If the resulting statistic is greater
+    // than |kChiSquareThreshold|, we can conclude with 99.9% confidence
+    // that the observed samples do not follow a uniform distribution.
+    //
+    // However, since 99.9% would still result in a false negative every
+    // 1000 runs of the test, do not treat it as a failure (else the test
+    // will be flaky). Instead, perform additional iterations to determine
+    // if the distribution will converge, up to |kMaxIterationCount|.
+    if ((i % kCheckIterationCount) == 0) {
+      const double expected_value_per_bucket =
+          static_cast<double>(i) / kBucketCount;
+      const double chi_square =
+          ComputeChiSquare(distribution, expected_value_per_bucket);
+      if (chi_square < kChiSquareThreshold)
+        break;
+
+      // If |i == kMaxIterationCount|, the Chi-Square statistic did not
+      // converge after |kMaxIterationCount|.
+      EXPECT_NE(i, kMaxIterationCount) << "Failed for trial " <<
+          trial_name << " with chi_square = " << chi_square <<
+          " after " << kMaxIterationCount << " iterations.";
+    }
+  }
+}
+
+}  // namespace
+
+TEST(EntropyProviderTest, UseOneTimeRandomizationSHA1) {
+  // Simply asserts that two trials using one-time randomization
+  // that have different names, normally generate different results.
+  //
+  // Note that depending on the one-time random initialization, they
+  // _might_ actually give the same result, but we know that given
+  // the particular client_id we use for unit tests they won't.
+  base::FieldTrialList field_trial_list(new SHA1EntropyProvider("client_id"));
+  const int kNoExpirationYear = base::FieldTrialList::kNoExpirationYear;
+  scoped_refptr<base::FieldTrial> trials[] = {
+      base::FieldTrialList::FactoryGetFieldTrial(
+          "one", 100, "default", kNoExpirationYear, 1, 1,
+          base::FieldTrial::ONE_TIME_RANDOMIZED, NULL),
+      base::FieldTrialList::FactoryGetFieldTrial(
+          "two", 100, "default", kNoExpirationYear, 1, 1,
+          base::FieldTrial::ONE_TIME_RANDOMIZED, NULL),
+  };
+
+  for (size_t i = 0; i < arraysize(trials); ++i) {
+    for (int j = 0; j < 100; ++j)
+      trials[i]->AppendGroup(std::string(), 1);
+  }
+
+  // The trials are most likely to give different results since they have
+  // different names.
+  EXPECT_NE(trials[0]->group(), trials[1]->group());
+  EXPECT_NE(trials[0]->group_name(), trials[1]->group_name());
+}
+
+TEST(EntropyProviderTest, UseOneTimeRandomizationPermuted) {
+  // Simply asserts that two trials using one-time randomization
+  // that have different names, normally generate different results.
+  //
+  // Note that depending on the one-time random initialization, they
+  // _might_ actually give the same result, but we know that given
+  // the particular client_id we use for unit tests they won't.
+  base::FieldTrialList field_trial_list(
+      new PermutedEntropyProvider(1234, kMaxLowEntropySize));
+  const int kNoExpirationYear = base::FieldTrialList::kNoExpirationYear;
+  scoped_refptr<base::FieldTrial> trials[] = {
+      base::FieldTrialList::FactoryGetFieldTrial(
+          "one", 100, "default", kNoExpirationYear, 1, 1,
+          base::FieldTrial::ONE_TIME_RANDOMIZED, NULL),
+      base::FieldTrialList::FactoryGetFieldTrial(
+          "two", 100, "default", kNoExpirationYear, 1, 1,
+          base::FieldTrial::ONE_TIME_RANDOMIZED, NULL),
+  };
+
+  for (size_t i = 0; i < arraysize(trials); ++i) {
+    for (int j = 0; j < 100; ++j)
+      trials[i]->AppendGroup(std::string(), 1);
+  }
+
+  // The trials are most likely to give different results since they have
+  // different names.
+  EXPECT_NE(trials[0]->group(), trials[1]->group());
+  EXPECT_NE(trials[0]->group_name(), trials[1]->group_name());
+}
+
+TEST(EntropyProviderTest, UseOneTimeRandomizationWithCustomSeedPermuted) {
+  // Ensures that two trials with different names but the same custom seed used
+  // for one time randomization produce the same group assignments.
+  base::FieldTrialList field_trial_list(
+      new PermutedEntropyProvider(1234, kMaxLowEntropySize));
+  const int kNoExpirationYear = base::FieldTrialList::kNoExpirationYear;
+  const uint32 kCustomSeed = 9001;
+  scoped_refptr<base::FieldTrial> trials[] = {
+      base::FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
+          "one", 100, "default", kNoExpirationYear, 1, 1,
+          base::FieldTrial::ONE_TIME_RANDOMIZED, kCustomSeed, NULL),
+      base::FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
+          "two", 100, "default", kNoExpirationYear, 1, 1,
+          base::FieldTrial::ONE_TIME_RANDOMIZED, kCustomSeed, NULL),
+  };
+
+  for (size_t i = 0; i < arraysize(trials); ++i) {
+    for (int j = 0; j < 100; ++j)
+      trials[i]->AppendGroup(std::string(), 1);
+  }
+
+  // Normally, these trials should produce different groups, but if the same
+  // custom seed is used, they should produce the same group assignment.
+  EXPECT_EQ(trials[0]->group(), trials[1]->group());
+  EXPECT_EQ(trials[0]->group_name(), trials[1]->group_name());
+}
+
+TEST(EntropyProviderTest, SHA1Entropy) {
+  const double results[] = { GenerateSHA1Entropy("hi", "1"),
+                             GenerateSHA1Entropy("there", "1") };
+
+  EXPECT_NE(results[0], results[1]);
+  for (size_t i = 0; i < arraysize(results); ++i) {
+    EXPECT_LE(0.0, results[i]);
+    EXPECT_GT(1.0, results[i]);
+  }
+
+  EXPECT_EQ(GenerateSHA1Entropy("yo", "1"),
+            GenerateSHA1Entropy("yo", "1"));
+  EXPECT_NE(GenerateSHA1Entropy("yo", "something"),
+            GenerateSHA1Entropy("yo", "else"));
+}
+
+TEST(EntropyProviderTest, PermutedEntropy) {
+  const double results[] = {
+      GeneratePermutedEntropy(1234, kMaxLowEntropySize, "1"),
+      GeneratePermutedEntropy(4321, kMaxLowEntropySize, "1") };
+
+  EXPECT_NE(results[0], results[1]);
+  for (size_t i = 0; i < arraysize(results); ++i) {
+    EXPECT_LE(0.0, results[i]);
+    EXPECT_GT(1.0, results[i]);
+  }
+
+  EXPECT_EQ(GeneratePermutedEntropy(1234, kMaxLowEntropySize, "1"),
+            GeneratePermutedEntropy(1234, kMaxLowEntropySize, "1"));
+  EXPECT_NE(GeneratePermutedEntropy(1234, kMaxLowEntropySize, "something"),
+            GeneratePermutedEntropy(1234, kMaxLowEntropySize, "else"));
+}
+
+TEST(EntropyProviderTest, PermutedEntropyProviderResults) {
+  // Verifies that PermutedEntropyProvider produces expected results. This
+  // ensures that the results are the same between platforms and ensures that
+  // changes to the implementation do not regress this accidentally.
+
+  EXPECT_DOUBLE_EQ(2194 / static_cast<double>(kMaxLowEntropySize),
+                   GeneratePermutedEntropy(1234, kMaxLowEntropySize, "XYZ"));
+  EXPECT_DOUBLE_EQ(5676 / static_cast<double>(kMaxLowEntropySize),
+                   GeneratePermutedEntropy(1, kMaxLowEntropySize, "Test"));
+  EXPECT_DOUBLE_EQ(1151 / static_cast<double>(kMaxLowEntropySize),
+                   GeneratePermutedEntropy(5000, kMaxLowEntropySize, "Foo"));
+}
+
+TEST(EntropyProviderTest, SHA1EntropyIsUniform) {
+  for (size_t i = 0; i < arraysize(kTestTrialNames); ++i) {
+    SHA1EntropyGenerator entropy_generator(kTestTrialNames[i]);
+    PerformEntropyUniformityTest(kTestTrialNames[i], entropy_generator);
+  }
+}
+
+TEST(EntropyProviderTest, PermutedEntropyIsUniform) {
+  for (size_t i = 0; i < arraysize(kTestTrialNames); ++i) {
+    PermutedEntropyGenerator entropy_generator(kTestTrialNames[i]);
+    PerformEntropyUniformityTest(kTestTrialNames[i], entropy_generator);
+  }
+}
+
+TEST(EntropyProviderTest, SeededRandGeneratorIsUniform) {
+  // Verifies that SeededRandGenerator has a uniform distribution.
+  //
+  // Mirrors RandUtilTest.RandGeneratorIsUniform in base/rand_util_unittest.cc.
+
+  const uint32 kTopOfRange = (std::numeric_limits<uint32>::max() / 4ULL) * 3ULL;
+  const uint32 kExpectedAverage = kTopOfRange / 2ULL;
+  const uint32 kAllowedVariance = kExpectedAverage / 50ULL;  // +/- 2%
+  const int kMinAttempts = 1000;
+  const int kMaxAttempts = 1000000;
+
+  for (size_t i = 0; i < arraysize(kTestTrialNames); ++i) {
+    const uint32 seed = HashName(kTestTrialNames[i]);
+    internal::SeededRandGenerator rand_generator(seed);
+
+    double cumulative_average = 0.0;
+    int count = 0;
+    while (count < kMaxAttempts) {
+      uint32 value = rand_generator(kTopOfRange);
+      cumulative_average = (count * cumulative_average + value) / (count + 1);
+
+      // Don't quit too quickly for things to start converging, or we may have
+      // a false positive.
+      if (count > kMinAttempts &&
+          kExpectedAverage - kAllowedVariance < cumulative_average &&
+          cumulative_average < kExpectedAverage + kAllowedVariance) {
+        break;
+      }
+
+      ++count;
+    }
+
+    ASSERT_LT(count, kMaxAttempts) << "Expected average was " <<
+        kExpectedAverage << ", average ended at " << cumulative_average <<
+        ", for trial " << kTestTrialNames[i];
+  }
+}
+
+}  // namespace metrics
diff --git a/components/variations/metrics_util.cc b/components/variations/metrics_util.cc
new file mode 100644
index 0000000..031c3d3
--- /dev/null
+++ b/components/variations/metrics_util.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/variations/metrics_util.h"
+
+#include "base/sha1.h"
+#include "base/sys_byteorder.h"
+
+namespace metrics {
+
+uint32 HashName(const std::string& name) {
+  // SHA-1 is designed to produce a uniformly random spread in its output space,
+  // even for nearly-identical inputs.
+  unsigned char sha1_hash[base::kSHA1Length];
+  base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(name.c_str()),
+                      name.size(),
+                      sha1_hash);
+
+  uint32 bits;
+  COMPILE_ASSERT(sizeof(bits) < sizeof(sha1_hash), need_more_data);
+  memcpy(&bits, sha1_hash, sizeof(bits));
+
+  return base::ByteSwapToLE32(bits);
+}
+
+}  // namespace metrics
diff --git a/components/variations/metrics_util.h b/components/variations/metrics_util.h
new file mode 100644
index 0000000..b331d4e
--- /dev/null
+++ b/components/variations/metrics_util.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VARIATIONS_METRICS_UTIL_H_
+#define COMPONENTS_VARIATIONS_METRICS_UTIL_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace metrics {
+
+// Computes a uint32 hash of a given string based on its SHA1 hash. Suitable for
+// uniquely identifying field trial names and group names.
+uint32 HashName(const std::string& name);
+
+}  // namespace metrics
+
+#endif  // COMPONENTS_VARIATIONS_METRICS_UTIL_H_
diff --git a/components/variations/metrics_util_unittest.cc b/components/variations/metrics_util_unittest.cc
new file mode 100644
index 0000000..4f32494
--- /dev/null
+++ b/components/variations/metrics_util_unittest.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/variations/metrics_util.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace metrics {
+
+TEST(MetricsUtilTest, HashName) {
+  // Checks that hashing is stable on all platforms.
+  struct {
+    const char* name;
+    uint32 hash_value;
+  } known_hashes[] = {
+    {"a", 937752454u},
+    {"1", 723085877u},
+    {"Trial Name", 2713117220u},
+    {"Group Name", 3201815843u},
+    {"My Favorite Experiment", 3722155194u},
+    {"My Awesome Group Name", 4109503236u},
+    {"abcdefghijklmonpqrstuvwxyz", 787728696u},
+    {"0123456789ABCDEF", 348858318U}
+  };
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(known_hashes); ++i)
+    EXPECT_EQ(known_hashes[i].hash_value, HashName(known_hashes[i].name));
+}
+
+}  // namespace metrics
diff --git a/chrome/browser/metrics/proto/study.proto b/components/variations/proto/study.proto
similarity index 100%
rename from chrome/browser/metrics/proto/study.proto
rename to components/variations/proto/study.proto
diff --git a/chrome/browser/metrics/proto/trials_seed.proto b/components/variations/proto/trials_seed.proto
similarity index 100%
rename from chrome/browser/metrics/proto/trials_seed.proto
rename to components/variations/proto/trials_seed.proto
diff --git a/components/variations/variations_associated_data.cc b/components/variations/variations_associated_data.cc
new file mode 100644
index 0000000..64a6d6e
--- /dev/null
+++ b/components/variations/variations_associated_data.cc
@@ -0,0 +1,236 @@
+// 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 "components/variations/variations_associated_data.h"
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "base/memory/singleton.h"
+#include "components/variations/metrics_util.h"
+
+namespace chrome_variations {
+
+namespace {
+
+// The internal singleton accessor for the map, used to keep it thread-safe.
+class GroupMapAccessor {
+ public:
+  typedef std::map<ActiveGroupId, VariationID, ActiveGroupIdCompare>
+      GroupToIDMap;
+
+  // Retrieve the singleton.
+  static GroupMapAccessor* GetInstance() {
+    return Singleton<GroupMapAccessor>::get();
+  }
+
+  // Note that this normally only sets the ID for a group the first time, unless
+  // |force| is set to true, in which case it will always override it.
+  void AssociateID(IDCollectionKey key,
+                   const ActiveGroupId& group_identifier,
+                   const VariationID id,
+                   const bool force) {
+#if !defined(NDEBUG)
+    // Validate that all collections with this |group_identifier| have the same
+    // associated ID.
+    DCHECK_EQ(2, ID_COLLECTION_COUNT);
+    IDCollectionKey other_key = GOOGLE_WEB_PROPERTIES;
+    if (key == GOOGLE_WEB_PROPERTIES)
+      other_key = GOOGLE_UPDATE_SERVICE;
+    VariationID other_id = GetID(other_key, group_identifier);
+    DCHECK(other_id == EMPTY_ID || other_id == id);
+#endif
+
+    base::AutoLock scoped_lock(lock_);
+
+    GroupToIDMap* group_to_id_map = GetGroupToIDMap(key);
+    if (force ||
+        group_to_id_map->find(group_identifier) == group_to_id_map->end())
+      (*group_to_id_map)[group_identifier] = id;
+  }
+
+  VariationID GetID(IDCollectionKey key,
+                    const ActiveGroupId& group_identifier) {
+    base::AutoLock scoped_lock(lock_);
+    GroupToIDMap* group_to_id_map = GetGroupToIDMap(key);
+    GroupToIDMap::const_iterator it = group_to_id_map->find(group_identifier);
+    if (it == group_to_id_map->end())
+      return EMPTY_ID;
+    return it->second;
+  }
+
+  void ClearAllMapsForTesting() {
+    base::AutoLock scoped_lock(lock_);
+
+    for (int i = 0; i < ID_COLLECTION_COUNT; ++i) {
+      GroupToIDMap* map = GetGroupToIDMap(static_cast<IDCollectionKey>(i));
+      DCHECK(map);
+      map->clear();
+    }
+  }
+
+ private:
+  friend struct DefaultSingletonTraits<GroupMapAccessor>;
+
+  // Retrieves the GroupToIDMap for |key|.
+  GroupToIDMap* GetGroupToIDMap(IDCollectionKey key) {
+    return &group_to_id_maps_[key];
+  }
+
+  GroupMapAccessor() {
+    group_to_id_maps_.resize(ID_COLLECTION_COUNT);
+  }
+  ~GroupMapAccessor() {}
+
+  base::Lock lock_;
+  std::vector<GroupToIDMap> group_to_id_maps_;
+
+  DISALLOW_COPY_AND_ASSIGN(GroupMapAccessor);
+};
+
+// Singleton helper class that keeps track of the parameters of all variations
+// and ensures access to these is thread-safe.
+class VariationsParamAssociator {
+ public:
+  typedef std::pair<std::string, std::string> VariationKey;
+  typedef std::map<std::string, std::string> VariationParams;
+
+  // Retrieve the singleton.
+  static VariationsParamAssociator* GetInstance() {
+    return Singleton<VariationsParamAssociator>::get();
+  }
+
+  bool AssociateVariationParams(const std::string& trial_name,
+                                const std::string& group_name,
+                                const VariationParams& params) {
+    base::AutoLock scoped_lock(lock_);
+
+    if (IsFieldTrialActive(trial_name))
+      return false;
+
+    const VariationKey key(trial_name, group_name);
+    if (ContainsKey(variation_params_, key))
+      return false;
+
+    variation_params_[key] = params;
+    return true;
+  }
+
+  bool GetVariationParams(const std::string& trial_name,
+                          VariationParams* params) {
+    base::AutoLock scoped_lock(lock_);
+
+    const std::string group_name =
+        base::FieldTrialList::FindFullName(trial_name);
+    const VariationKey key(trial_name, group_name);
+    if (!ContainsKey(variation_params_, key))
+      return false;
+
+    *params = variation_params_[key];
+    return true;
+  }
+
+  void ClearAllParamsForTesting() {
+    base::AutoLock scoped_lock(lock_);
+    variation_params_.clear();
+  }
+
+ private:
+  friend struct DefaultSingletonTraits<VariationsParamAssociator>;
+
+  VariationsParamAssociator() {}
+  ~VariationsParamAssociator() {}
+
+  // Tests whether a field trial is active (i.e. group() has been called on it).
+  // TODO(asvitkine): Expose this as an API on base::FieldTrial.
+  bool IsFieldTrialActive(const std::string& trial_name) {
+    base::FieldTrial::ActiveGroups active_groups;
+    base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
+    for (size_t i = 0; i < active_groups.size(); ++i) {
+      if (active_groups[i].trial_name == trial_name)
+        return true;
+    }
+    return false;
+  }
+
+  base::Lock lock_;
+  std::map<VariationKey, VariationParams> variation_params_;
+
+  DISALLOW_COPY_AND_ASSIGN(VariationsParamAssociator);
+};
+
+}  // namespace
+
+ActiveGroupId MakeActiveGroupId(const std::string& trial_name,
+                                const std::string& group_name) {
+  ActiveGroupId id;
+  id.name = metrics::HashName(trial_name);
+  id.group = metrics::HashName(group_name);
+  return id;
+}
+
+void AssociateGoogleVariationID(IDCollectionKey key,
+                                const std::string& trial_name,
+                                const std::string& group_name,
+                                VariationID id) {
+  GroupMapAccessor::GetInstance()->AssociateID(
+      key, MakeActiveGroupId(trial_name, group_name), id, false);
+}
+
+void AssociateGoogleVariationIDForce(IDCollectionKey key,
+                                     const std::string& trial_name,
+                                     const std::string& group_name,
+                                     VariationID id) {
+  GroupMapAccessor::GetInstance()->AssociateID(
+      key, MakeActiveGroupId(trial_name, group_name), id, true);
+}
+
+VariationID GetGoogleVariationID(IDCollectionKey key,
+                                 const std::string& trial_name,
+                                 const std::string& group_name) {
+  return GroupMapAccessor::GetInstance()->GetID(
+      key, MakeActiveGroupId(trial_name, group_name));
+}
+
+bool AssociateVariationParams(
+    const std::string& trial_name,
+    const std::string& group_name,
+    const std::map<std::string, std::string>& params) {
+  return VariationsParamAssociator::GetInstance()->AssociateVariationParams(
+      trial_name, group_name, params);
+}
+
+bool GetVariationParams(const std::string& trial_name,
+                        std::map<std::string, std::string>* params) {
+  return VariationsParamAssociator::GetInstance()->GetVariationParams(
+      trial_name, params);
+}
+
+std::string GetVariationParamValue(const std::string& trial_name,
+                                   const std::string& param_name) {
+  std::map<std::string, std::string> params;
+  if (GetVariationParams(trial_name, &params)) {
+    std::map<std::string, std::string>::iterator it = params.find(param_name);
+    if (it != params.end())
+      return it->second;
+  }
+  return std::string();
+}
+
+// Functions below are exposed for testing explicitly behind this namespace.
+// They simply wrap existing functions in this file.
+namespace testing {
+
+void ClearAllVariationIDs() {
+  GroupMapAccessor::GetInstance()->ClearAllMapsForTesting();
+}
+
+void ClearAllVariationParams() {
+  VariationsParamAssociator::GetInstance()->ClearAllParamsForTesting();
+}
+
+}  // namespace testing
+
+}  // namespace chrome_variations
diff --git a/components/variations/variations_associated_data.h b/components/variations/variations_associated_data.h
new file mode 100644
index 0000000..bfc4a8f
--- /dev/null
+++ b/components/variations/variations_associated_data.h
@@ -0,0 +1,155 @@
+// 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 COMPONENTS_VARIATIONS_VARIATIONS_ASSOCIATED_DATA_H_
+#define COMPONENTS_VARIATIONS_VARIATIONS_ASSOCIATED_DATA_H_
+
+#include <map>
+#include <string>
+
+#include "base/metrics/field_trial.h"
+
+// This file provides various helpers that extend the functionality around
+// base::FieldTrial.
+//
+// This includes several simple APIs to handle getting and setting additional
+// data related to Chrome variations, such as parameters and Google variation
+// IDs. These APIs are meant to extend the base::FieldTrial APIs to offer extra
+// functionality that is not offered by the simpler base::FieldTrial APIs.
+//
+// The AssociateGoogleVariationID and AssociateVariationParams functions are
+// generally meant to be called by the VariationsService based on server-side
+// variation configs, but may also be used for client-only field trials by
+// invoking them directly after appending all the groups to a FieldTrial.
+//
+// Experiment code can then use the getter APIs to retrieve variation parameters
+// or IDs:
+//
+//  std::map<std::string, std::string> params;
+//  if (GetVariationParams("trial", &params)) {
+//    // use |params|
+//  }
+//
+//  std::string value = GetVariationParamValue("trial", "param_x");
+//  // use |value|, which will be "" if it does not exist
+//
+// VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial",
+//                                       "group1");
+// if (id != chrome_variations::kEmptyID) {
+//   // use |id|
+// }
+
+namespace chrome_variations {
+
+typedef int VariationID;
+
+const VariationID EMPTY_ID = 0;
+
+// The Unique ID of a trial and its active group, where the name and group
+// identifiers are hashes of the trial and group name strings.
+struct ActiveGroupId {
+  uint32 name;
+  uint32 group;
+};
+
+// Returns an ActiveGroupId struct for the given trial and group names.
+ActiveGroupId MakeActiveGroupId(const std::string& trial_name,
+                                const std::string& group_name);
+
+// We need to supply a Compare class for templates since ActiveGroupId is a
+// user-defined type.
+struct ActiveGroupIdCompare {
+  bool operator() (const ActiveGroupId& lhs, const ActiveGroupId& rhs) const {
+    // The group and name fields are just SHA-1 Hashes, so we just need to treat
+    // them as IDs and do a less-than comparison. We test group first, since
+    // name is more likely to collide.
+    if (lhs.group != rhs.group)
+      return lhs.group < rhs.group;
+    return lhs.name < rhs.name;
+  }
+};
+
+// A key into the Associate/Get methods for VariationIDs. This is used to create
+// separate ID associations for separate parties interested in VariationIDs.
+enum IDCollectionKey {
+  // This collection is used by Google web properties, transmitted through the
+  // X-Chrome-Variations header.
+  GOOGLE_WEB_PROPERTIES,
+  // This collection is used by Google update services, transmitted through the
+  // Google Update experiment labels.
+  GOOGLE_UPDATE_SERVICE,
+  // The total count of collections.
+  ID_COLLECTION_COUNT,
+};
+
+// Associate a chrome_variations::VariationID value with a FieldTrial group for
+// collection |key|. If an id was previously set for |trial_name| and
+// |group_name|, this does nothing. The group is denoted by |trial_name| and
+// |group_name|. This must be called whenever a FieldTrial is prepared (create
+// the trial and append groups) and needs to have a
+// chrome_variations::VariationID associated with it so Google servers can
+// recognize the FieldTrial. Thread safe.
+void AssociateGoogleVariationID(IDCollectionKey key,
+                                const std::string& trial_name,
+                                const std::string& group_name,
+                                VariationID id);
+
+// As above, but overwrites any previously set id. Thread safe.
+void AssociateGoogleVariationIDForce(IDCollectionKey key,
+                                     const std::string& trial_name,
+                                     const std::string& group_name,
+                                     VariationID id);
+
+// Retrieve the chrome_variations::VariationID associated with a FieldTrial
+// group for collection |key|. The group is denoted by |trial_name| and
+// |group_name|. This will return chrome_variations::kEmptyID if there is
+// currently no associated ID for the named group. This API can be nicely
+// combined with FieldTrial::GetActiveFieldTrialGroups() to enumerate the
+// variation IDs for all active FieldTrial groups. Thread safe.
+VariationID GetGoogleVariationID(IDCollectionKey key,
+                                 const std::string& trial_name,
+                                 const std::string& group_name);
+
+// Associates the specified set of key-value |params| with the variation
+// specified by |trial_name| and |group_name|. Fails and returns false if the
+// specified variation already has params associated with it or the field trial
+// is already active (group() has been called on it). Thread safe.
+bool AssociateVariationParams(const std::string& trial_name,
+                              const std::string& group_name,
+                              const std::map<std::string, std::string>& params);
+
+// Retrieves the set of key-value |params| for the variation associated with
+// the specified field trial, based on its selected group. If the field trial
+// does not exist or its selected group does not have any parameters associated
+// with it, returns false and does not modify |params|. Calling this function
+// will result in the field trial being marked as active if found (i.e. group()
+// will be called on it), if it wasn't already. Currently, this information is
+// only available from the browser process. Thread safe.
+bool GetVariationParams(const std::string& trial_name,
+                        std::map<std::string, std::string>* params);
+
+// Retrieves a specific parameter value corresponding to |param_name| for the
+// variation associated with the specified field trial, based on its selected
+// group. If the field trial does not exist or the specified parameter does not
+// exist, returns an empty string. Calling this function will result in the
+// field trial being marked as active if found (i.e. group() will be called on
+// it), if it wasn't already. Currently, this information is only available from
+// the browser process. Thread safe.
+std::string GetVariationParamValue(const std::string& trial_name,
+                                   const std::string& param_name);
+
+// Expose some functions for testing.
+namespace testing {
+
+// Clears all of the mapped associations.
+void ClearAllVariationIDs();
+
+// Clears all of the associated params.
+void ClearAllVariationParams();
+
+}  // namespace testing
+
+}  // namespace chrome_variations
+
+#endif  // COMPONENTS_VARIATIONS_VARIATIONS_ASSOCIATED_DATA_H_
diff --git a/components/variations/variations_associated_data_unittest.cc b/components/variations/variations_associated_data_unittest.cc
new file mode 100644
index 0000000..d9d4b5d
--- /dev/null
+++ b/components/variations/variations_associated_data_unittest.cc
@@ -0,0 +1,310 @@
+// 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 "components/variations/variations_associated_data.h"
+
+#include "base/metrics/field_trial.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chrome_variations {
+
+namespace {
+
+const VariationID TEST_VALUE_A = 3300200;
+const VariationID TEST_VALUE_B = 3300201;
+
+// Convenience helper to retrieve the chrome_variations::VariationID for a
+// FieldTrial. Note that this will do the group assignment in |trial| if not
+// already done.
+VariationID GetIDForTrial(IDCollectionKey key, base::FieldTrial* trial) {
+  return GetGoogleVariationID(key, trial->trial_name(), trial->group_name());
+}
+
+// Tests whether a field trial is active (i.e. group() has been called on it).
+bool IsFieldTrialActive(const std::string& trial_name) {
+  base::FieldTrial::ActiveGroups active_groups;
+  base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
+  for (size_t i = 0; i < active_groups.size(); ++i) {
+    if (active_groups[i].trial_name == trial_name)
+      return true;
+  }
+  return false;
+}
+
+// Call FieldTrialList::FactoryGetFieldTrial() with a future expiry date.
+scoped_refptr<base::FieldTrial> CreateFieldTrial(
+    const std::string& trial_name,
+    int total_probability,
+    const std::string& default_group_name,
+    int* default_group_number) {
+  return base::FieldTrialList::FactoryGetFieldTrial(
+      trial_name, total_probability, default_group_name,
+      base::FieldTrialList::kNoExpirationYear, 1, 1,
+      base::FieldTrial::SESSION_RANDOMIZED, default_group_number);
+}
+
+}  // namespace
+
+class VariationsAssociatedDataTest : public ::testing::Test {
+ public:
+  VariationsAssociatedDataTest() : field_trial_list_(NULL) {
+  }
+
+  virtual ~VariationsAssociatedDataTest() {
+    // Ensure that the maps are cleared between tests, since they are stored as
+    // process singletons.
+    testing::ClearAllVariationIDs();
+  }
+
+ private:
+  base::FieldTrialList field_trial_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(VariationsAssociatedDataTest);
+};
+
+// Test that if the trial is immediately disabled, GetGoogleVariationID just
+// returns the empty ID.
+TEST_F(VariationsAssociatedDataTest, DisableImmediately) {
+  int default_group_number = -1;
+  scoped_refptr<base::FieldTrial> trial(
+      CreateFieldTrial("trial", 100, "default", &default_group_number));
+
+  ASSERT_EQ(default_group_number, trial->group());
+  ASSERT_EQ(EMPTY_ID, GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial.get()));
+}
+
+// Test that successfully associating the FieldTrial with some ID, and then
+// disabling the FieldTrial actually makes GetGoogleVariationID correctly
+// return the empty ID.
+TEST_F(VariationsAssociatedDataTest, DisableAfterInitialization) {
+  const std::string default_name = "default";
+  const std::string non_default_name = "non_default";
+
+  scoped_refptr<base::FieldTrial> trial(
+      CreateFieldTrial("trial", 100, default_name, NULL));
+
+  trial->AppendGroup(non_default_name, 100);
+  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial->trial_name(),
+      default_name, TEST_VALUE_A);
+  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial->trial_name(),
+      non_default_name, TEST_VALUE_B);
+  trial->Disable();
+  ASSERT_EQ(default_name, trial->group_name());
+  ASSERT_EQ(TEST_VALUE_A, GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial.get()));
+}
+
+// Test various successful association cases.
+TEST_F(VariationsAssociatedDataTest, AssociateGoogleVariationID) {
+  const std::string default_name1 = "default";
+  scoped_refptr<base::FieldTrial> trial_true(
+      CreateFieldTrial("d1", 10, default_name1, NULL));
+  const std::string winner = "TheWinner";
+  int winner_group = trial_true->AppendGroup(winner, 10);
+
+  // Set GoogleVariationIDs so we can verify that they were chosen correctly.
+  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_true->trial_name(),
+      default_name1, TEST_VALUE_A);
+  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_true->trial_name(),
+      winner, TEST_VALUE_B);
+
+  EXPECT_EQ(winner_group, trial_true->group());
+  EXPECT_EQ(winner, trial_true->group_name());
+  EXPECT_EQ(TEST_VALUE_B,
+            GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get()));
+
+  const std::string default_name2 = "default2";
+  scoped_refptr<base::FieldTrial> trial_false(
+      CreateFieldTrial("d2", 10, default_name2, NULL));
+  const std::string loser = "ALoser";
+  const int loser_group = trial_false->AppendGroup(loser, 0);
+
+  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_false->trial_name(),
+      default_name2, TEST_VALUE_A);
+  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_false->trial_name(),
+      loser, TEST_VALUE_B);
+
+  EXPECT_NE(loser_group, trial_false->group());
+  EXPECT_EQ(TEST_VALUE_A,
+            GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_false.get()));
+}
+
+// Test that not associating a FieldTrial with any IDs ensure that the empty ID
+// will be returned.
+TEST_F(VariationsAssociatedDataTest, NoAssociation) {
+  const std::string default_name = "default";
+  scoped_refptr<base::FieldTrial> no_id_trial(
+      CreateFieldTrial("d3", 10, default_name, NULL));
+
+  const std::string winner = "TheWinner";
+  const int winner_group = no_id_trial->AppendGroup(winner, 10);
+
+  // Ensure that despite the fact that a normal winner is elected, it does not
+  // have a valid VariationID associated with it.
+  EXPECT_EQ(winner_group, no_id_trial->group());
+  EXPECT_EQ(winner, no_id_trial->group_name());
+  EXPECT_EQ(EMPTY_ID, GetIDForTrial(GOOGLE_WEB_PROPERTIES, no_id_trial.get()));
+}
+
+// Ensure that the AssociateGoogleVariationIDForce works as expected.
+TEST_F(VariationsAssociatedDataTest, ForceAssociation) {
+  EXPECT_EQ(EMPTY_ID,
+            GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group"));
+  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group",
+                             TEST_VALUE_A);
+  EXPECT_EQ(TEST_VALUE_A,
+            GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group"));
+  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group",
+                             TEST_VALUE_B);
+  EXPECT_EQ(TEST_VALUE_A,
+            GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group"));
+  AssociateGoogleVariationIDForce(GOOGLE_WEB_PROPERTIES, "trial", "group",
+                                  TEST_VALUE_B);
+  EXPECT_EQ(TEST_VALUE_B,
+            GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group"));
+}
+
+// Ensure that two collections can coexist without affecting each other.
+TEST_F(VariationsAssociatedDataTest, CollectionsCoexist) {
+  const std::string default_name = "default";
+  int default_group_number = -1;
+  scoped_refptr<base::FieldTrial> trial_true(
+      CreateFieldTrial("d1", 10, default_name, &default_group_number));
+  ASSERT_EQ(default_group_number, trial_true->group());
+  ASSERT_EQ(default_name, trial_true->group_name());
+
+  EXPECT_EQ(EMPTY_ID,
+            GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get()));
+  EXPECT_EQ(EMPTY_ID,
+            GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get()));
+
+  AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_true->trial_name(),
+      default_name, TEST_VALUE_A);
+  EXPECT_EQ(TEST_VALUE_A,
+            GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get()));
+  EXPECT_EQ(EMPTY_ID,
+            GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get()));
+
+  AssociateGoogleVariationID(GOOGLE_UPDATE_SERVICE, trial_true->trial_name(),
+      default_name, TEST_VALUE_A);
+  EXPECT_EQ(TEST_VALUE_A,
+            GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get()));
+  EXPECT_EQ(TEST_VALUE_A,
+            GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get()));
+}
+
+TEST_F(VariationsAssociatedDataTest, AssociateVariationParams) {
+  const std::string kTrialName = "AssociateVariationParams";
+
+  {
+    std::map<std::string, std::string> params;
+    params["a"] = "10";
+    params["b"] = "test";
+    ASSERT_TRUE(AssociateVariationParams(kTrialName, "A", params));
+  }
+  {
+    std::map<std::string, std::string> params;
+    params["a"] = "5";
+    ASSERT_TRUE(AssociateVariationParams(kTrialName, "B", params));
+  }
+
+  base::FieldTrialList::CreateFieldTrial(kTrialName, "B");
+  EXPECT_EQ("5", GetVariationParamValue(kTrialName, "a"));
+  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "b"));
+  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x"));
+
+  std::map<std::string, std::string> params;
+  EXPECT_TRUE(GetVariationParams(kTrialName, &params));
+  EXPECT_EQ(1U, params.size());
+  EXPECT_EQ("5", params["a"]);
+}
+
+TEST_F(VariationsAssociatedDataTest, AssociateVariationParams_Fail) {
+  const std::string kTrialName = "AssociateVariationParams_Fail";
+  const std::string kGroupName = "A";
+
+  std::map<std::string, std::string> params;
+  params["a"] = "10";
+  ASSERT_TRUE(AssociateVariationParams(kTrialName, kGroupName, params));
+  params["a"] = "1";
+  params["b"] = "2";
+  ASSERT_FALSE(AssociateVariationParams(kTrialName, kGroupName, params));
+
+  base::FieldTrialList::CreateFieldTrial(kTrialName, kGroupName);
+  EXPECT_EQ("10", GetVariationParamValue(kTrialName, "a"));
+  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "b"));
+}
+
+TEST_F(VariationsAssociatedDataTest, AssociateVariationParams_TrialActiveFail) {
+  const std::string kTrialName = "AssociateVariationParams_TrialActiveFail";
+  base::FieldTrialList::CreateFieldTrial(kTrialName, "A");
+  ASSERT_EQ("A", base::FieldTrialList::FindFullName(kTrialName));
+
+  std::map<std::string, std::string> params;
+  params["a"] = "10";
+  EXPECT_FALSE(AssociateVariationParams(kTrialName, "B", params));
+  EXPECT_FALSE(AssociateVariationParams(kTrialName, "A", params));
+}
+
+TEST_F(VariationsAssociatedDataTest,
+       AssociateVariationParams_DoesntActivateTrial) {
+  const std::string kTrialName = "AssociateVariationParams_DoesntActivateTrial";
+
+  ASSERT_FALSE(IsFieldTrialActive(kTrialName));
+  scoped_refptr<base::FieldTrial> trial(
+      CreateFieldTrial(kTrialName, 100, "A", NULL));
+  ASSERT_FALSE(IsFieldTrialActive(kTrialName));
+
+  std::map<std::string, std::string> params;
+  params["a"] = "10";
+  EXPECT_TRUE(AssociateVariationParams(kTrialName, "A", params));
+  ASSERT_FALSE(IsFieldTrialActive(kTrialName));
+}
+
+TEST_F(VariationsAssociatedDataTest, GetVariationParams_NoTrial) {
+  const std::string kTrialName = "GetVariationParams_NoParams";
+
+  std::map<std::string, std::string> params;
+  EXPECT_FALSE(GetVariationParams(kTrialName, &params));
+  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x"));
+  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "y"));
+}
+
+TEST_F(VariationsAssociatedDataTest, GetVariationParams_NoParams) {
+  const std::string kTrialName = "GetVariationParams_NoParams";
+
+  base::FieldTrialList::CreateFieldTrial(kTrialName, "A");
+
+  std::map<std::string, std::string> params;
+  EXPECT_FALSE(GetVariationParams(kTrialName, &params));
+  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x"));
+  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "y"));
+}
+
+TEST_F(VariationsAssociatedDataTest, GetVariationParams_ActivatesTrial) {
+  const std::string kTrialName = "GetVariationParams_ActivatesTrial";
+
+  ASSERT_FALSE(IsFieldTrialActive(kTrialName));
+  scoped_refptr<base::FieldTrial> trial(
+      CreateFieldTrial(kTrialName, 100, "A", NULL));
+  ASSERT_FALSE(IsFieldTrialActive(kTrialName));
+
+  std::map<std::string, std::string> params;
+  EXPECT_FALSE(GetVariationParams(kTrialName, &params));
+  ASSERT_TRUE(IsFieldTrialActive(kTrialName));
+}
+
+TEST_F(VariationsAssociatedDataTest, GetVariationParamValue_ActivatesTrial) {
+  const std::string kTrialName = "GetVariationParamValue_ActivatesTrial";
+
+  ASSERT_FALSE(IsFieldTrialActive(kTrialName));
+  scoped_refptr<base::FieldTrial> trial(
+      CreateFieldTrial(kTrialName, 100, "A", NULL));
+  ASSERT_FALSE(IsFieldTrialActive(kTrialName));
+
+  std::map<std::string, std::string> params;
+  EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x"));
+  ASSERT_TRUE(IsFieldTrialActive(kTrialName));
+}
+
+}  // namespace chrome_variations
diff --git a/components/variations/variations_seed_processor.cc b/components/variations/variations_seed_processor.cc
new file mode 100644
index 0000000..c28d351
--- /dev/null
+++ b/components/variations/variations_seed_processor.cc
@@ -0,0 +1,342 @@
+// 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 "components/variations/variations_seed_processor.h"
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/metrics/field_trial.h"
+#include "base/stl_util.h"
+#include "base/version.h"
+#include "components/variations/variations_associated_data.h"
+
+namespace chrome_variations {
+
+namespace {
+
+Study_Platform GetCurrentPlatform() {
+#if defined(OS_WIN)
+  return Study_Platform_PLATFORM_WINDOWS;
+#elif defined(OS_IOS)
+  return Study_Platform_PLATFORM_IOS;
+#elif defined(OS_MACOSX)
+  return Study_Platform_PLATFORM_MAC;
+#elif defined(OS_CHROMEOS)
+  return Study_Platform_PLATFORM_CHROMEOS;
+#elif defined(OS_ANDROID)
+  return Study_Platform_PLATFORM_ANDROID;
+#elif defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS)
+  // Default BSD and SOLARIS to Linux to not break those builds, although these
+  // platforms are not officially supported by Chrome.
+  return Study_Platform_PLATFORM_LINUX;
+#else
+#error Unknown platform
+#endif
+}
+
+// Converts |date_time| in Study date format to base::Time.
+base::Time ConvertStudyDateToBaseTime(int64 date_time) {
+  return base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(date_time);
+}
+
+}  // namespace
+
+VariationsSeedProcessor::VariationsSeedProcessor() {
+}
+
+VariationsSeedProcessor::~VariationsSeedProcessor() {
+}
+
+void VariationsSeedProcessor::CreateTrialsFromSeed(
+    const TrialsSeed& seed,
+    const std::string& locale,
+    const base::Time& reference_date,
+    const base::Version& version,
+    Study_Channel channel) {
+  DCHECK(version.IsValid());
+
+  // Add expired studies (in a disabled state) only after all the non-expired
+  // studies have been added (and do not add an expired study if a corresponding
+  // non-expired study got added). This way, if there's both an expired and a
+  // non-expired study that applies, the non-expired study takes priority.
+  std::set<std::string> created_studies;
+  std::vector<const Study*> expired_studies;
+
+  for (int i = 0; i < seed.study_size(); ++i) {
+    const Study& study = seed.study(i);
+    if (!ShouldAddStudy(study, locale, reference_date, version, channel))
+      continue;
+
+    if (IsStudyExpired(study, reference_date)) {
+      expired_studies.push_back(&study);
+    } else {
+      CreateTrialFromStudy(study, false);
+      created_studies.insert(study.name());
+    }
+  }
+
+  for (size_t i = 0; i < expired_studies.size(); ++i) {
+    if (!ContainsKey(created_studies, expired_studies[i]->name()))
+      CreateTrialFromStudy(*expired_studies[i], true);
+  }
+}
+
+bool VariationsSeedProcessor::CheckStudyChannel(const Study_Filter& filter,
+                                                Study_Channel channel) {
+  // An empty channel list matches all channels.
+  if (filter.channel_size() == 0)
+    return true;
+
+  for (int i = 0; i < filter.channel_size(); ++i) {
+    if (filter.channel(i) == channel)
+      return true;
+  }
+  return false;
+}
+
+bool VariationsSeedProcessor::CheckStudyLocale(
+    const Study_Filter& filter,
+    const std::string& locale) {
+  // An empty locale list matches all locales.
+  if (filter.locale_size() == 0)
+    return true;
+
+  for (int i = 0; i < filter.locale_size(); ++i) {
+    if (filter.locale(i) == locale)
+      return true;
+  }
+  return false;
+}
+
+bool VariationsSeedProcessor::CheckStudyPlatform(
+    const Study_Filter& filter,
+    Study_Platform platform) {
+  // An empty platform list matches all platforms.
+  if (filter.platform_size() == 0)
+    return true;
+
+  for (int i = 0; i < filter.platform_size(); ++i) {
+    if (filter.platform(i) == platform)
+      return true;
+  }
+  return false;
+}
+
+bool VariationsSeedProcessor::CheckStudyStartDate(
+    const Study_Filter& filter,
+    const base::Time& date_time) {
+  if (filter.has_start_date()) {
+    const base::Time start_date =
+        ConvertStudyDateToBaseTime(filter.start_date());
+    return date_time >= start_date;
+  }
+
+  return true;
+}
+
+bool VariationsSeedProcessor::CheckStudyVersion(
+    const Study_Filter& filter,
+    const base::Version& version) {
+  if (filter.has_min_version()) {
+    if (version.CompareToWildcardString(filter.min_version()) < 0)
+      return false;
+  }
+
+  if (filter.has_max_version()) {
+    if (version.CompareToWildcardString(filter.max_version()) > 0)
+      return false;
+  }
+
+  return true;
+}
+
+void VariationsSeedProcessor::CreateTrialFromStudy(const Study& study,
+                                                   bool is_expired) {
+  base::FieldTrial::Probability total_probability = 0;
+  if (!ValidateStudyAndComputeTotalProbability(study, &total_probability))
+    return;
+
+  // Check if any experiments need to be forced due to a command line
+  // flag. Force the first experiment with an existing flag.
+  CommandLine* command_line = CommandLine::ForCurrentProcess();
+  for (int i = 0; i < study.experiment_size(); ++i) {
+    const Study_Experiment& experiment = study.experiment(i);
+    if (experiment.has_forcing_flag() &&
+        command_line->HasSwitch(experiment.forcing_flag())) {
+      base::FieldTrialList::CreateFieldTrial(study.name(), experiment.name());
+      DVLOG(1) << "Trial " << study.name() << " forced by flag: "
+               << experiment.forcing_flag();
+      return;
+    }
+  }
+
+  uint32 randomization_seed = 0;
+  base::FieldTrial::RandomizationType randomization_type =
+      base::FieldTrial::SESSION_RANDOMIZED;
+  if (study.has_consistency() &&
+      study.consistency() == Study_Consistency_PERMANENT) {
+    randomization_type = base::FieldTrial::ONE_TIME_RANDOMIZED;
+    if (study.has_randomization_seed())
+      randomization_seed = study.randomization_seed();
+  }
+
+  // The trial is created without specifying an expiration date because the
+  // expiration check in field_trial.cc is based on the build date. Instead,
+  // the expiration check using |reference_date| is done explicitly below.
+  scoped_refptr<base::FieldTrial> trial(
+      base::FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
+          study.name(), total_probability, study.default_experiment_name(),
+          base::FieldTrialList::kNoExpirationYear, 1, 1, randomization_type,
+          randomization_seed, NULL));
+
+  for (int i = 0; i < study.experiment_size(); ++i) {
+    const Study_Experiment& experiment = study.experiment(i);
+
+    std::map<std::string, std::string> params;
+    for (int j = 0; j < experiment.param_size(); j++) {
+      if (experiment.param(j).has_name() && experiment.param(j).has_value())
+        params[experiment.param(j).name()] = experiment.param(j).value();
+    }
+    if (!params.empty())
+      AssociateVariationParams(study.name(), experiment.name(), params);
+
+    // Groups with flags can't be selected randomly, so we don't add them to
+    // the field trial.
+    if (experiment.has_forcing_flag())
+      continue;
+
+    if (experiment.name() != study.default_experiment_name())
+      trial->AppendGroup(experiment.name(), experiment.probability_weight());
+
+    if (experiment.has_google_web_experiment_id()) {
+      const VariationID variation_id =
+          static_cast<VariationID>(experiment.google_web_experiment_id());
+      AssociateGoogleVariationIDForce(GOOGLE_WEB_PROPERTIES,
+                                      study.name(),
+                                      experiment.name(),
+                                      variation_id);
+    }
+    if (experiment.has_google_update_experiment_id()) {
+      const VariationID variation_id =
+          static_cast<VariationID>(experiment.google_update_experiment_id());
+      AssociateGoogleVariationIDForce(GOOGLE_UPDATE_SERVICE,
+                                      study.name(),
+                                      experiment.name(),
+                                      variation_id);
+    }
+  }
+
+  trial->SetForced();
+  if (is_expired)
+    trial->Disable();
+}
+
+bool VariationsSeedProcessor::IsStudyExpired(const Study& study,
+                                       const base::Time& date_time) {
+  if (study.has_expiry_date()) {
+    const base::Time expiry_date =
+        ConvertStudyDateToBaseTime(study.expiry_date());
+    return date_time >= expiry_date;
+  }
+
+  return false;
+}
+
+bool VariationsSeedProcessor::ShouldAddStudy(
+    const Study& study,
+    const std::string& locale,
+    const base::Time& reference_date,
+    const base::Version& version,
+    Study_Channel channel) {
+  if (study.has_filter()) {
+    if (!CheckStudyChannel(study.filter(), channel)) {
+      DVLOG(1) << "Filtered out study " << study.name() << " due to channel.";
+      return false;
+    }
+
+    if (!CheckStudyLocale(study.filter(), locale)) {
+      DVLOG(1) << "Filtered out study " << study.name() << " due to locale.";
+      return false;
+    }
+
+    if (!CheckStudyPlatform(study.filter(), GetCurrentPlatform())) {
+      DVLOG(1) << "Filtered out study " << study.name() << " due to platform.";
+      return false;
+    }
+
+    if (!CheckStudyVersion(study.filter(), version)) {
+      DVLOG(1) << "Filtered out study " << study.name() << " due to version.";
+      return false;
+    }
+
+    if (!CheckStudyStartDate(study.filter(), reference_date)) {
+      DVLOG(1) << "Filtered out study " << study.name() <<
+                  " due to start date.";
+      return false;
+    }
+  }
+
+  DVLOG(1) << "Kept study " << study.name() << ".";
+  return true;
+}
+
+bool VariationsSeedProcessor::ValidateStudyAndComputeTotalProbability(
+    const Study& study,
+    base::FieldTrial::Probability* total_probability) {
+  // At the moment, a missing default_experiment_name makes the study invalid.
+  if (study.default_experiment_name().empty()) {
+    DVLOG(1) << study.name() << " has no default experiment defined.";
+    return false;
+  }
+  if (study.filter().has_min_version() &&
+      !Version::IsValidWildcardString(study.filter().min_version())) {
+    DVLOG(1) << study.name() << " has invalid min version: "
+             << study.filter().min_version();
+    return false;
+  }
+  if (study.filter().has_max_version() &&
+      !Version::IsValidWildcardString(study.filter().max_version())) {
+    DVLOG(1) << study.name() << " has invalid max version: "
+             << study.filter().max_version();
+    return false;
+  }
+
+  const std::string& default_group_name = study.default_experiment_name();
+  base::FieldTrial::Probability divisor = 0;
+
+  bool found_default_group = false;
+  std::set<std::string> experiment_names;
+  for (int i = 0; i < study.experiment_size(); ++i) {
+    if (study.experiment(i).name().empty()) {
+      DVLOG(1) << study.name() << " is missing experiment " << i << " name";
+      return false;
+    }
+    if (!experiment_names.insert(study.experiment(i).name()).second) {
+      DVLOG(1) << study.name() << " has a repeated experiment name "
+               << study.experiment(i).name();
+      return false;
+    }
+
+    if (!study.experiment(i).has_forcing_flag())
+      divisor += study.experiment(i).probability_weight();
+    if (study.experiment(i).name() == default_group_name)
+      found_default_group = true;
+  }
+
+  if (!found_default_group) {
+    DVLOG(1) << study.name() << " is missing default experiment in its "
+             << "experiment list";
+    // The default group was not found in the list of groups. This study is not
+    // valid.
+    return false;
+  }
+
+  *total_probability = divisor;
+  return true;
+}
+
+}  // namespace chrome_variations
diff --git a/components/variations/variations_seed_processor.h b/components/variations/variations_seed_processor.h
new file mode 100644
index 0000000..46dccfa
--- /dev/null
+++ b/components/variations/variations_seed_processor.h
@@ -0,0 +1,96 @@
+// 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 COMPONENTS_VARIATIONS_VARIATIONS_SEED_PROCESSOR_H_
+#define COMPONENTS_VARIATIONS_VARIATIONS_SEED_PROCESSOR_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/metrics/field_trial.h"
+#include "base/time/time.h"
+#include "base/version.h"
+#include "components/variations/proto/study.pb.h"
+#include "components/variations/proto/trials_seed.pb.h"
+
+namespace chrome_variations {
+
+// Helper class to instantiate field trials from a variations seed.
+class VariationsSeedProcessor {
+ public:
+  VariationsSeedProcessor();
+  virtual ~VariationsSeedProcessor();
+
+  // Creates field trials from the specified variations |seed|, based on the
+  // specified configuration (locale, current date, version and channel).
+  void CreateTrialsFromSeed(const TrialsSeed& seed,
+                            const std::string& locale,
+                            const base::Time& reference_date,
+                            const base::Version& version,
+                            Study_Channel channel);
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, CheckStudyChannel);
+  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, CheckStudyLocale);
+  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, CheckStudyPlatform);
+  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, CheckStudyStartDate);
+  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, CheckStudyVersion);
+  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest,
+                           CheckStudyVersionWildcards);
+  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, ForceGroupWithFlag1);
+  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, ForceGroupWithFlag2);
+  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest,
+                           ForceGroup_ChooseFirstGroupWithFlag);
+  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest,
+                           ForceGroup_DontChooseGroupWithFlag);
+  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, IsStudyExpired);
+  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, ValidateStudy);
+  FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, VariationParams);
+
+  // Checks whether a study is applicable for the given |channel| per |filter|.
+  bool CheckStudyChannel(const Study_Filter& filter, Study_Channel channel);
+
+  // Checks whether a study is applicable for the given |locale| per |filter|.
+  bool CheckStudyLocale(const Study_Filter& filter, const std::string& locale);
+
+  // Checks whether a study is applicable for the given |platform| per |filter|.
+  bool CheckStudyPlatform(const Study_Filter& filter, Study_Platform platform);
+
+  // Checks whether a study is applicable for the given date/time per |filter|.
+  bool CheckStudyStartDate(const Study_Filter& filter,
+                           const base::Time& date_time);
+
+  // Checks whether a study is applicable for the given version per |filter|.
+  bool CheckStudyVersion(const Study_Filter& filter,
+                         const base::Version& version);
+
+  // Creates and registers a field trial from the |study| data. Disables the
+  // trial if |is_expired| is true.
+  void CreateTrialFromStudy(const Study& study, bool is_expired);
+
+  // Checks whether |study| is expired using the given date/time.
+  bool IsStudyExpired(const Study& study, const base::Time& date_time);
+
+  // Returns whether |study| should be disabled according to its restriction
+  // parameters. Uses |version_info| for min / max version checks,
+  // |reference_date| for the start date check and |channel| for channel
+  // checks.
+  bool ShouldAddStudy(const Study& study,
+                      const std::string& locale,
+                      const base::Time& reference_date,
+                      const base::Version& version,
+                      Study_Channel channel);
+
+  // Validates the sanity of |study| and computes the total probability.
+  bool ValidateStudyAndComputeTotalProbability(
+      const Study& study,
+      base::FieldTrial::Probability* total_probability);
+
+  DISALLOW_COPY_AND_ASSIGN(VariationsSeedProcessor);
+};
+
+}  // namespace chrome_variations
+
+#endif  // COMPONENTS_VARIATIONS_VARIATIONS_SEED_PROCESSOR_H_
diff --git a/components/variations/variations_seed_processor_unittest.cc b/components/variations/variations_seed_processor_unittest.cc
new file mode 100644
index 0000000..6a41ce2
--- /dev/null
+++ b/components/variations/variations_seed_processor_unittest.cc
@@ -0,0 +1,531 @@
+// 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 "components/variations/variations_seed_processor.h"
+
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/strings/string_split.h"
+#include "components/variations/variations_associated_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chrome_variations {
+
+namespace {
+
+// Converts |time| to Study proto format.
+int64 TimeToProtoTime(const base::Time& time) {
+  return (time - base::Time::UnixEpoch()).InSeconds();
+}
+
+// Constants for testing associating command line flags with trial groups.
+const char kFlagStudyName[] = "flag_test_trial";
+const char kFlagGroup1Name[] = "flag_group1";
+const char kFlagGroup2Name[] = "flag_group2";
+const char kNonFlagGroupName[] = "non_flag_group";
+const char kForcingFlag1[] = "flag_test1";
+const char kForcingFlag2[] = "flag_test2";
+
+// Adds an experiment to |study| with the specified |name| and |probability|.
+Study_Experiment* AddExperiment(const std::string& name, int probability,
+                                Study* study) {
+  Study_Experiment* experiment = study->add_experiment();
+  experiment->set_name(name);
+  experiment->set_probability_weight(probability);
+  return experiment;
+}
+
+// Populates |study| with test data used for testing associating command line
+// flags with trials groups. The study will contain three groups, a default
+// group that isn't associated with a flag, and two other groups, both
+// associated with different flags.
+Study CreateStudyWithFlagGroups(int default_group_probability,
+                                int flag_group1_probability,
+                                int flag_group2_probability) {
+  DCHECK_GE(default_group_probability, 0);
+  DCHECK_GE(flag_group1_probability, 0);
+  DCHECK_GE(flag_group2_probability, 0);
+  Study study;
+  study.set_name(kFlagStudyName);
+  study.set_default_experiment_name(kNonFlagGroupName);
+
+  AddExperiment(kNonFlagGroupName, default_group_probability, &study);
+  AddExperiment(kFlagGroup1Name, flag_group1_probability, &study)
+      ->set_forcing_flag(kForcingFlag1);
+  AddExperiment(kFlagGroup2Name, flag_group2_probability, &study)
+      ->set_forcing_flag(kForcingFlag2);
+
+  return study;
+}
+
+}  // namespace
+
+TEST(VariationsSeedProcessorTest, CheckStudyChannel) {
+  VariationsSeedProcessor seed_processor;
+
+  const Study_Channel channels[] = {
+    Study_Channel_CANARY,
+    Study_Channel_DEV,
+    Study_Channel_BETA,
+    Study_Channel_STABLE,
+  };
+  bool channel_added[arraysize(channels)] = { 0 };
+
+  Study_Filter filter;
+
+  // Check in the forwarded order. The loop cond is <= arraysize(channels)
+  // instead of < so that the result of adding the last channel gets checked.
+  for (size_t i = 0; i <= arraysize(channels); ++i) {
+    for (size_t j = 0; j < arraysize(channels); ++j) {
+      const bool expected = channel_added[j] || filter.channel_size() == 0;
+      const bool result = seed_processor.CheckStudyChannel(filter, channels[j]);
+      EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!";
+    }
+
+    if (i < arraysize(channels)) {
+      filter.add_channel(channels[i]);
+      channel_added[i] = true;
+    }
+  }
+
+  // Do the same check in the reverse order.
+  filter.clear_channel();
+  memset(&channel_added, 0, sizeof(channel_added));
+  for (size_t i = 0; i <= arraysize(channels); ++i) {
+    for (size_t j = 0; j < arraysize(channels); ++j) {
+      const bool expected = channel_added[j] || filter.channel_size() == 0;
+      const bool result = seed_processor.CheckStudyChannel(filter, channels[j]);
+      EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!";
+    }
+
+    if (i < arraysize(channels)) {
+      const int index = arraysize(channels) - i - 1;
+      filter.add_channel(channels[index]);
+      channel_added[index] = true;
+    }
+  }
+}
+
+TEST(VariationsSeedProcessorTest, CheckStudyLocale) {
+  VariationsSeedProcessor seed_processor;
+
+  struct {
+    const char* filter_locales;
+    bool en_us_result;
+    bool en_ca_result;
+    bool fr_result;
+  } test_cases[] = {
+    {"en-US", true, false, false},
+    {"en-US,en-CA,fr", true, true, true},
+    {"en-US,en-CA,en-GB", true, true, false},
+    {"en-GB,en-CA,en-US", true, true, false},
+    {"ja,kr,vi", false, false, false},
+    {"fr-CA", false, false, false},
+    {"", true, true, true},
+  };
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
+    std::vector<std::string> filter_locales;
+    Study_Filter filter;
+    base::SplitString(test_cases[i].filter_locales, ',', &filter_locales);
+    for (size_t j = 0; j < filter_locales.size(); ++j)
+      filter.add_locale(filter_locales[j]);
+    EXPECT_EQ(test_cases[i].en_us_result,
+              seed_processor.CheckStudyLocale(filter, "en-US"));
+    EXPECT_EQ(test_cases[i].en_ca_result,
+              seed_processor.CheckStudyLocale(filter, "en-CA"));
+    EXPECT_EQ(test_cases[i].fr_result,
+              seed_processor.CheckStudyLocale(filter, "fr"));
+  }
+}
+
+TEST(VariationsSeedProcessorTest, CheckStudyPlatform) {
+  VariationsSeedProcessor seed_processor;
+
+  const Study_Platform platforms[] = {
+    Study_Platform_PLATFORM_WINDOWS,
+    Study_Platform_PLATFORM_MAC,
+    Study_Platform_PLATFORM_LINUX,
+    Study_Platform_PLATFORM_CHROMEOS,
+    Study_Platform_PLATFORM_ANDROID,
+    Study_Platform_PLATFORM_IOS,
+  };
+  ASSERT_EQ(Study_Platform_Platform_ARRAYSIZE,
+            static_cast<int>(arraysize(platforms)));
+  bool platform_added[arraysize(platforms)] = { 0 };
+
+  Study_Filter filter;
+
+  // Check in the forwarded order. The loop cond is <= arraysize(platforms)
+  // instead of < so that the result of adding the last channel gets checked.
+  for (size_t i = 0; i <= arraysize(platforms); ++i) {
+    for (size_t j = 0; j < arraysize(platforms); ++j) {
+      const bool expected = platform_added[j] || filter.platform_size() == 0;
+      const bool result = seed_processor.CheckStudyPlatform(filter,
+                                                            platforms[j]);
+      EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!";
+    }
+
+    if (i < arraysize(platforms)) {
+      filter.add_platform(platforms[i]);
+      platform_added[i] = true;
+    }
+  }
+
+  // Do the same check in the reverse order.
+  filter.clear_platform();
+  memset(&platform_added, 0, sizeof(platform_added));
+  for (size_t i = 0; i <= arraysize(platforms); ++i) {
+    for (size_t j = 0; j < arraysize(platforms); ++j) {
+      const bool expected = platform_added[j] || filter.platform_size() == 0;
+      const bool result = seed_processor.CheckStudyPlatform(filter,
+                                                            platforms[j]);
+      EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!";
+    }
+
+    if (i < arraysize(platforms)) {
+      const int index = arraysize(platforms) - i - 1;
+      filter.add_platform(platforms[index]);
+      platform_added[index] = true;
+    }
+  }
+}
+
+TEST(VariationsSeedProcessorTest, CheckStudyStartDate) {
+  VariationsSeedProcessor seed_processor;
+
+  const base::Time now = base::Time::Now();
+  const base::TimeDelta delta = base::TimeDelta::FromHours(1);
+  const struct {
+    const base::Time start_date;
+    bool expected_result;
+  } start_test_cases[] = {
+    { now - delta, true },
+    { now, true },
+    { now + delta, false },
+  };
+
+  Study_Filter filter;
+
+  // Start date not set should result in true.
+  EXPECT_TRUE(seed_processor.CheckStudyStartDate(filter, now));
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(start_test_cases); ++i) {
+    filter.set_start_date(TimeToProtoTime(start_test_cases[i].start_date));
+    const bool result = seed_processor.CheckStudyStartDate(filter, now);
+    EXPECT_EQ(start_test_cases[i].expected_result, result)
+        << "Case " << i << " failed!";
+  }
+}
+
+TEST(VariationsSeedProcessorTest, CheckStudyVersion) {
+  VariationsSeedProcessor seed_processor;
+
+  const struct {
+    const char* min_version;
+    const char* version;
+    bool expected_result;
+  } min_test_cases[] = {
+    { "1.2.2", "1.2.3", true },
+    { "1.2.3", "1.2.3", true },
+    { "1.2.4", "1.2.3", false },
+    { "1.3.2", "1.2.3", false },
+    { "2.1.2", "1.2.3", false },
+    { "0.3.4", "1.2.3", true },
+    // Wildcards.
+    { "1.*", "1.2.3", true },
+    { "1.2.*", "1.2.3", true },
+    { "1.2.3.*", "1.2.3", true },
+    { "1.2.4.*", "1.2.3", false },
+    { "2.*", "1.2.3", false },
+    { "0.3.*", "1.2.3", true },
+  };
+
+  const struct {
+    const char* max_version;
+    const char* version;
+    bool expected_result;
+  } max_test_cases[] = {
+    { "1.2.2", "1.2.3", false },
+    { "1.2.3", "1.2.3", true },
+    { "1.2.4", "1.2.3", true },
+    { "2.1.1", "1.2.3", true },
+    { "2.1.1", "2.3.4", false },
+    // Wildcards
+    { "2.1.*", "2.3.4", false },
+    { "2.*", "2.3.4", true },
+    { "2.3.*", "2.3.4", true },
+    { "2.3.4.*", "2.3.4", true },
+    { "2.3.4.0.*", "2.3.4", true },
+    { "2.4.*", "2.3.4", true },
+    { "1.3.*", "2.3.4", false },
+    { "1.*", "2.3.4", false },
+  };
+
+  Study_Filter filter;
+
+  // Min/max version not set should result in true.
+  EXPECT_TRUE(seed_processor.CheckStudyVersion(filter, base::Version("1.2.3")));
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(min_test_cases); ++i) {
+    filter.set_min_version(min_test_cases[i].min_version);
+    const bool result =
+        seed_processor.CheckStudyVersion(filter,
+                                         Version(min_test_cases[i].version));
+    EXPECT_EQ(min_test_cases[i].expected_result, result) <<
+        "Min. version case " << i << " failed!";
+  }
+  filter.clear_min_version();
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(max_test_cases); ++i) {
+    filter.set_max_version(max_test_cases[i].max_version);
+    const bool result =
+        seed_processor.CheckStudyVersion(filter,
+                                         Version(max_test_cases[i].version));
+    EXPECT_EQ(max_test_cases[i].expected_result, result) <<
+        "Max version case " << i << " failed!";
+  }
+
+  // Check intersection semantics.
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(min_test_cases); ++i) {
+    for (size_t j = 0; j < ARRAYSIZE_UNSAFE(max_test_cases); ++j) {
+      filter.set_min_version(min_test_cases[i].min_version);
+      filter.set_max_version(max_test_cases[j].max_version);
+
+      if (!min_test_cases[i].expected_result) {
+        const bool result =
+            seed_processor.CheckStudyVersion(
+                filter, Version(min_test_cases[i].version));
+        EXPECT_FALSE(result) << "Case " << i << "," << j << " failed!";
+      }
+
+      if (!max_test_cases[j].expected_result) {
+        const bool result =
+            seed_processor.CheckStudyVersion(
+                filter, Version(max_test_cases[j].version));
+        EXPECT_FALSE(result) << "Case " << i << "," << j << " failed!";
+      }
+    }
+  }
+}
+
+// Test that the group for kForcingFlag1 is forced.
+TEST(VariationsSeedProcessorTest, ForceGroupWithFlag1) {
+  CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
+
+  base::FieldTrialList field_trial_list(NULL);
+
+  Study study = CreateStudyWithFlagGroups(100, 0, 0);
+  VariationsSeedProcessor().CreateTrialFromStudy(study, false);
+
+  EXPECT_EQ(kFlagGroup1Name,
+            base::FieldTrialList::FindFullName(kFlagStudyName));
+}
+
+// Test that the group for kForcingFlag2 is forced.
+TEST(VariationsSeedProcessorTest, ForceGroupWithFlag2) {
+  CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag2);
+
+  base::FieldTrialList field_trial_list(NULL);
+
+  Study study = CreateStudyWithFlagGroups(100, 0, 0);
+  VariationsSeedProcessor().CreateTrialFromStudy(study, false);
+
+  EXPECT_EQ(kFlagGroup2Name,
+            base::FieldTrialList::FindFullName(kFlagStudyName));
+}
+
+TEST(VariationsSeedProcessorTest, ForceGroup_ChooseFirstGroupWithFlag) {
+  // Add the flag to the command line arguments so the flag group is forced.
+  CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
+  CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag2);
+
+  base::FieldTrialList field_trial_list(NULL);
+
+  Study study = CreateStudyWithFlagGroups(100, 0, 0);
+  VariationsSeedProcessor().CreateTrialFromStudy(study, false);
+
+  EXPECT_EQ(kFlagGroup1Name,
+            base::FieldTrialList::FindFullName(kFlagStudyName));
+}
+
+TEST(VariationsSeedProcessorTest, ForceGroup_DontChooseGroupWithFlag) {
+  base::FieldTrialList field_trial_list(NULL);
+
+  // The two flag groups are given high probability, which would normally make
+  // them very likely to be chosen. They won't be chosen since flag groups are
+  // never chosen when their flag isn't present.
+  Study study = CreateStudyWithFlagGroups(1, 999, 999);
+  VariationsSeedProcessor().CreateTrialFromStudy(study, false);
+  EXPECT_EQ(kNonFlagGroupName,
+            base::FieldTrialList::FindFullName(kFlagStudyName));
+}
+
+TEST(VariationsSeedProcessorTest, IsStudyExpired) {
+  VariationsSeedProcessor seed_processor;
+
+  const base::Time now = base::Time::Now();
+  const base::TimeDelta delta = base::TimeDelta::FromHours(1);
+  const struct {
+    const base::Time expiry_date;
+    bool expected_result;
+  } expiry_test_cases[] = {
+    { now - delta, true },
+    { now, true },
+    { now + delta, false },
+  };
+
+  Study study;
+
+  // Expiry date not set should result in false.
+  EXPECT_FALSE(seed_processor.IsStudyExpired(study, now));
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(expiry_test_cases); ++i) {
+    study.set_expiry_date(TimeToProtoTime(expiry_test_cases[i].expiry_date));
+    const bool result = seed_processor.IsStudyExpired(study, now);
+    EXPECT_EQ(expiry_test_cases[i].expected_result, result)
+        << "Case " << i << " failed!";
+  }
+}
+
+TEST(VariationsSeedProcessorTest, NonExpiredStudyPrioritizedOverExpiredStudy) {
+  VariationsSeedProcessor seed_processor;
+
+  const std::string kTrialName = "A";
+  const std::string kGroup1Name = "Group1";
+
+  TrialsSeed seed;
+  Study* study1 = seed.add_study();
+  study1->set_name(kTrialName);
+  study1->set_default_experiment_name("Default");
+  AddExperiment(kGroup1Name, 100, study1);
+  AddExperiment("Default", 0, study1);
+  Study* study2 = seed.add_study();
+  *study2 = *study1;
+  ASSERT_EQ(seed.study(0).name(), seed.study(1).name());
+
+  const base::Time year_ago =
+      base::Time::Now() - base::TimeDelta::FromDays(365);
+
+  const base::Version version("20.0.0.0");
+
+  // Check that adding [expired, non-expired] activates the non-expired one.
+  ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName));
+  {
+    base::FieldTrialList field_trial_list(NULL);
+    study1->set_expiry_date(TimeToProtoTime(year_ago));
+    seed_processor.CreateTrialsFromSeed(seed, "en-CA", base::Time::Now(),
+                                        version, Study_Channel_STABLE);
+    EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
+  }
+
+  // Check that adding [non-expired, expired] activates the non-expired one.
+  ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName));
+  {
+    base::FieldTrialList field_trial_list(NULL);
+    study1->clear_expiry_date();
+    study2->set_expiry_date(TimeToProtoTime(year_ago));
+    seed_processor.CreateTrialsFromSeed(seed, "en-CA", base::Time::Now(),
+                                        version, Study_Channel_STABLE);
+    EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
+  }
+}
+
+TEST(VariationsSeedProcessorTest, ValidateStudy) {
+  VariationsSeedProcessor seed_processor;
+
+  Study study;
+  study.set_default_experiment_name("def");
+  AddExperiment("abc", 100, &study);
+  Study_Experiment* default_group = AddExperiment("def", 200, &study);
+
+  base::FieldTrial::Probability total_probability = 0;
+  bool valid = seed_processor.ValidateStudyAndComputeTotalProbability(
+      study, &total_probability);
+  EXPECT_TRUE(valid);
+  EXPECT_EQ(300, total_probability);
+
+  // Min version checks.
+  study.mutable_filter()->set_min_version("1.2.3.*");
+  valid = seed_processor.ValidateStudyAndComputeTotalProbability(
+      study, &total_probability);
+  EXPECT_TRUE(valid);
+  study.mutable_filter()->set_min_version("1.*.3");
+  valid = seed_processor.ValidateStudyAndComputeTotalProbability(
+      study, &total_probability);
+  EXPECT_FALSE(valid);
+  study.mutable_filter()->set_min_version("1.2.3");
+  valid = seed_processor.ValidateStudyAndComputeTotalProbability(
+      study, &total_probability);
+  EXPECT_TRUE(valid);
+
+  // Max version checks.
+  study.mutable_filter()->set_max_version("2.3.4.*");
+  valid = seed_processor.ValidateStudyAndComputeTotalProbability(
+      study, &total_probability);
+  EXPECT_TRUE(valid);
+  study.mutable_filter()->set_max_version("*.3");
+  valid = seed_processor.ValidateStudyAndComputeTotalProbability(
+      study, &total_probability);
+  EXPECT_FALSE(valid);
+  study.mutable_filter()->set_max_version("2.3.4");
+  valid = seed_processor.ValidateStudyAndComputeTotalProbability(
+      study, &total_probability);
+  EXPECT_TRUE(valid);
+
+  study.clear_default_experiment_name();
+  valid = seed_processor.ValidateStudyAndComputeTotalProbability(study,
+      &total_probability);
+  EXPECT_FALSE(valid);
+
+  study.set_default_experiment_name("xyz");
+  valid = seed_processor.ValidateStudyAndComputeTotalProbability(study,
+      &total_probability);
+  EXPECT_FALSE(valid);
+
+  study.set_default_experiment_name("def");
+  default_group->clear_name();
+  valid = seed_processor.ValidateStudyAndComputeTotalProbability(study,
+      &total_probability);
+  EXPECT_FALSE(valid);
+
+  default_group->set_name("def");
+  valid = seed_processor.ValidateStudyAndComputeTotalProbability(study,
+      &total_probability);
+  ASSERT_TRUE(valid);
+  Study_Experiment* repeated_group = study.add_experiment();
+  repeated_group->set_name("abc");
+  repeated_group->set_probability_weight(1);
+  valid = seed_processor.ValidateStudyAndComputeTotalProbability(study,
+      &total_probability);
+  EXPECT_FALSE(valid);
+}
+
+TEST(VariationsSeedProcessorTest, VariationParams) {
+  base::FieldTrialList field_trial_list(NULL);
+  VariationsSeedProcessor seed_processor;
+
+  Study study;
+  study.set_name("Study1");
+  study.set_default_experiment_name("B");
+
+  Study_Experiment* experiment1 = AddExperiment("A", 1, &study);
+  Study_Experiment_Param* param = experiment1->add_param();
+  param->set_name("x");
+  param->set_value("y");
+
+  Study_Experiment* experiment2 = AddExperiment("B", 0, &study);
+
+  seed_processor.CreateTrialFromStudy(study, false);
+  EXPECT_EQ("y", GetVariationParamValue("Study1", "x"));
+
+  study.set_name("Study2");
+  experiment1->set_probability_weight(0);
+  experiment2->set_probability_weight(1);
+  seed_processor.CreateTrialFromStudy(study, false);
+  EXPECT_EQ(std::string(), GetVariationParamValue("Study2", "x"));
+}
+
+}  // namespace chrome_variations
diff --git a/components/visitedlink/test/visitedlink_perftest.cc b/components/visitedlink/test/visitedlink_perftest.cc
index 9ced41c..dfc254f 100644
--- a/components/visitedlink/test/visitedlink_perftest.cc
+++ b/components/visitedlink/test/visitedlink_perftest.cc
@@ -9,8 +9,8 @@
 #include "base/file_util.h"
 #include "base/files/file_path.h"
 #include "base/memory/shared_memory.h"
-#include "base/perftimer.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/perftimer.h"
 #include "base/test/test_file_util.h"
 #include "components/visitedlink/browser/visitedlink_master.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/components/visitedlink_browser.target.darwin-arm.mk b/components/visitedlink_browser.target.darwin-arm.mk
index ccd5788..3b1715e 100644
--- a/components/visitedlink_browser.target.darwin-arm.mk
+++ b/components/visitedlink_browser.target.darwin-arm.mk
@@ -92,9 +92,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/skia/config \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -178,9 +178,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/skia/config \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/components/visitedlink_browser.target.darwin-mips.mk b/components/visitedlink_browser.target.darwin-mips.mk
index 650a20e..1125407 100644
--- a/components/visitedlink_browser.target.darwin-mips.mk
+++ b/components/visitedlink_browser.target.darwin-mips.mk
@@ -91,9 +91,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/skia/config \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -176,9 +176,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/skia/config \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/components/visitedlink_browser.target.darwin-x86.mk b/components/visitedlink_browser.target.darwin-x86.mk
index 5aae55d..ca8c5c2 100644
--- a/components/visitedlink_browser.target.darwin-x86.mk
+++ b/components/visitedlink_browser.target.darwin-x86.mk
@@ -94,9 +94,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/skia/config \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -183,9 +183,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/skia/config \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/components/visitedlink_browser.target.linux-arm.mk b/components/visitedlink_browser.target.linux-arm.mk
index ccd5788..3b1715e 100644
--- a/components/visitedlink_browser.target.linux-arm.mk
+++ b/components/visitedlink_browser.target.linux-arm.mk
@@ -92,9 +92,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/skia/config \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -178,9 +178,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/skia/config \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/components/visitedlink_browser.target.linux-mips.mk b/components/visitedlink_browser.target.linux-mips.mk
index 650a20e..1125407 100644
--- a/components/visitedlink_browser.target.linux-mips.mk
+++ b/components/visitedlink_browser.target.linux-mips.mk
@@ -91,9 +91,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/skia/config \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -176,9 +176,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/skia/config \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/components/visitedlink_browser.target.linux-x86.mk b/components/visitedlink_browser.target.linux-x86.mk
index 5aae55d..ca8c5c2 100644
--- a/components/visitedlink_browser.target.linux-x86.mk
+++ b/components/visitedlink_browser.target.linux-x86.mk
@@ -94,9 +94,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/skia/config \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -183,9 +183,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
 	$(LOCAL_PATH)/skia/config \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/components/visitedlink_common.target.darwin-arm.mk b/components/visitedlink_common.target.darwin-arm.mk
index f7f37a9..2c8135b 100644
--- a/components/visitedlink_common.target.darwin-arm.mk
+++ b/components/visitedlink_common.target.darwin-arm.mk
@@ -91,9 +91,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -176,9 +176,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/components/visitedlink_common.target.darwin-mips.mk b/components/visitedlink_common.target.darwin-mips.mk
index 984f0bc..acfe53b 100644
--- a/components/visitedlink_common.target.darwin-mips.mk
+++ b/components/visitedlink_common.target.darwin-mips.mk
@@ -90,9 +90,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -174,9 +174,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/components/visitedlink_common.target.darwin-x86.mk b/components/visitedlink_common.target.darwin-x86.mk
index 7a4129e..00ec457 100644
--- a/components/visitedlink_common.target.darwin-x86.mk
+++ b/components/visitedlink_common.target.darwin-x86.mk
@@ -93,9 +93,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -181,9 +181,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/components/visitedlink_common.target.linux-arm.mk b/components/visitedlink_common.target.linux-arm.mk
index f7f37a9..2c8135b 100644
--- a/components/visitedlink_common.target.linux-arm.mk
+++ b/components/visitedlink_common.target.linux-arm.mk
@@ -91,9 +91,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -176,9 +176,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/components/visitedlink_common.target.linux-mips.mk b/components/visitedlink_common.target.linux-mips.mk
index 984f0bc..acfe53b 100644
--- a/components/visitedlink_common.target.linux-mips.mk
+++ b/components/visitedlink_common.target.linux-mips.mk
@@ -90,9 +90,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -174,9 +174,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/components/visitedlink_common.target.linux-x86.mk b/components/visitedlink_common.target.linux-x86.mk
index 7a4129e..00ec457 100644
--- a/components/visitedlink_common.target.linux-x86.mk
+++ b/components/visitedlink_common.target.linux-x86.mk
@@ -93,9 +93,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -181,9 +181,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/components/visitedlink_renderer.target.darwin-arm.mk b/components/visitedlink_renderer.target.darwin-arm.mk
index 6efae8e..4d47ad5 100644
--- a/components/visitedlink_renderer.target.darwin-arm.mk
+++ b/components/visitedlink_renderer.target.darwin-arm.mk
@@ -100,9 +100,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
@@ -212,9 +212,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
diff --git a/components/visitedlink_renderer.target.darwin-mips.mk b/components/visitedlink_renderer.target.darwin-mips.mk
index fc79b50..ff790cf 100644
--- a/components/visitedlink_renderer.target.darwin-mips.mk
+++ b/components/visitedlink_renderer.target.darwin-mips.mk
@@ -99,9 +99,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
@@ -210,9 +210,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
diff --git a/components/visitedlink_renderer.target.darwin-x86.mk b/components/visitedlink_renderer.target.darwin-x86.mk
index de9eacf..dea4066 100644
--- a/components/visitedlink_renderer.target.darwin-x86.mk
+++ b/components/visitedlink_renderer.target.darwin-x86.mk
@@ -102,9 +102,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
@@ -217,9 +217,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
diff --git a/components/visitedlink_renderer.target.linux-arm.mk b/components/visitedlink_renderer.target.linux-arm.mk
index 6efae8e..4d47ad5 100644
--- a/components/visitedlink_renderer.target.linux-arm.mk
+++ b/components/visitedlink_renderer.target.linux-arm.mk
@@ -100,9 +100,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
@@ -212,9 +212,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
diff --git a/components/visitedlink_renderer.target.linux-mips.mk b/components/visitedlink_renderer.target.linux-mips.mk
index fc79b50..ff790cf 100644
--- a/components/visitedlink_renderer.target.linux-mips.mk
+++ b/components/visitedlink_renderer.target.linux-mips.mk
@@ -99,9 +99,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
@@ -210,9 +210,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
diff --git a/components/visitedlink_renderer.target.linux-x86.mk b/components/visitedlink_renderer.target.linux-x86.mk
index de9eacf..dea4066 100644
--- a/components/visitedlink_renderer.target.linux-x86.mk
+++ b/components/visitedlink_renderer.target.linux-x86.mk
@@ -102,9 +102,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
@@ -217,9 +217,9 @@
 	$(gyp_shared_intermediate_dir)/shim_headers/icuuc/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/icui18n/target \
 	$(gyp_shared_intermediate_dir)/shim_headers/ashmem/target \
+	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
-	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
diff --git a/components/web_contents_delegate_android/android/java/src/org/chromium/components/web_contents_delegate_android/WebContentsDelegateAndroid.java b/components/web_contents_delegate_android/android/java/src/org/chromium/components/web_contents_delegate_android/WebContentsDelegateAndroid.java
index c09cb89..7db0705 100644
--- a/components/web_contents_delegate_android/android/java/src/org/chromium/components/web_contents_delegate_android/WebContentsDelegateAndroid.java
+++ b/components/web_contents_delegate_android/android/java/src/org/chromium/components/web_contents_delegate_android/WebContentsDelegateAndroid.java
@@ -57,12 +57,6 @@
     }
 
     @CalledByNative
-    public boolean addNewContents(int nativeSourceWebContents, int nativeWebContents,
-            int disposition, Rect initialPosition, boolean userGesture) {
-        return false;
-    }
-
-    @CalledByNative
     public void activateContents() {
     }
 
diff --git a/components/web_contents_delegate_android/web_contents_delegate_android.cc b/components/web_contents_delegate_android/web_contents_delegate_android.cc
index 8f599a1..c908f6a 100644
--- a/components/web_contents_delegate_android/web_contents_delegate_android.cc
+++ b/components/web_contents_delegate_android/web_contents_delegate_android.cc
@@ -124,30 +124,6 @@
       changed_flags);
 }
 
-void WebContentsDelegateAndroid::AddNewContents(
-    WebContents* source,
-    WebContents* new_contents,
-    WindowOpenDisposition disposition,
-    const gfx::Rect& initial_pos,
-    bool user_gesture,
-    bool* was_blocked) {
-  JNIEnv* env = AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
-  bool handled = false;
-  if (!obj.is_null()) {
-    handled = Java_WebContentsDelegateAndroid_addNewContents(
-        env,
-        obj.obj(),
-        reinterpret_cast<jint>(source),
-        reinterpret_cast<jint>(new_contents),
-        static_cast<jint>(disposition),
-        NULL,
-        user_gesture);
-  }
-  if (!handled)
-    delete new_contents;
-}
-
 void WebContentsDelegateAndroid::ActivateContents(WebContents* contents) {
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
diff --git a/components/web_contents_delegate_android/web_contents_delegate_android.h b/components/web_contents_delegate_android/web_contents_delegate_android.h
index 68bca15..d375be9 100644
--- a/components/web_contents_delegate_android/web_contents_delegate_android.h
+++ b/components/web_contents_delegate_android/web_contents_delegate_android.h
@@ -57,12 +57,6 @@
                                                   SkColor color) OVERRIDE;
   virtual void NavigationStateChanged(const content::WebContents* source,
                                       unsigned changed_flags) OVERRIDE;
-  virtual void AddNewContents(content::WebContents* source,
-                              content::WebContents* new_contents,
-                              WindowOpenDisposition disposition,
-                              const gfx::Rect& initial_pos,
-                              bool user_gesture,
-                              bool* was_blocked) OVERRIDE;
   virtual void ActivateContents(content::WebContents* contents) OVERRIDE;
   virtual void DeactivateContents(content::WebContents* contents) OVERRIDE;
   virtual void LoadingStateChanged(content::WebContents* source) OVERRIDE;
diff --git a/content/DEPS b/content/DEPS
index 389f18f..356e747 100644
--- a/content/DEPS
+++ b/content/DEPS
@@ -17,13 +17,10 @@
   "-base/prefs",
 
   "+cc",
-  "-cc/base/thread.h",  # http://crbug.com/249172
-  "!cc/base/thread_impl.h",  # http://crbug.com/249172
   # If you want to use any of these files, move them to src/base first.
   "-cc/base/hash_pair.h",
   "-cc/base/scoped_ptr_algorithm.h",
   "-cc/base/scoped_ptr_deque.h",
-  "-cc/base/scoped_ptr_hash_map.h",
   "-cc/base/scoped_ptr_vector.h",
 
   "+crypto",
diff --git a/content/app/android/child_process_service.cc b/content/app/android/child_process_service.cc
index 88de2ad..4d5de1d 100644
--- a/content/app/android/child_process_service.cc
+++ b/content/app/android/child_process_service.cc
@@ -45,13 +45,13 @@
 
   virtual void EstablishSurfaceTexturePeer(
       base::ProcessHandle pid,
-      scoped_refptr<gfx::SurfaceTextureBridge> surface_texture_bridge,
+      scoped_refptr<gfx::SurfaceTexture> surface_texture,
       int primary_id,
       int secondary_id) OVERRIDE {
     JNIEnv* env = base::android::AttachCurrentThread();
     content::Java_ChildProcessService_establishSurfaceTexturePeer(
         env, service_.obj(), pid,
-        surface_texture_bridge->j_surface_texture().obj(), primary_id,
+        surface_texture->j_surface_texture().obj(), primary_id,
         secondary_id);
     CheckException(env);
   }
diff --git a/content/app/android/content_main.cc b/content/app/android/content_main.cc
index d89d4f2..bfc372f 100644
--- a/content/app/android/content_main.cc
+++ b/content/app/android/content_main.cc
@@ -38,10 +38,15 @@
 static jint Start(JNIEnv* env, jclass clazz) {
   TRACE_EVENT0("startup", "content::Start");
 
-  DCHECK(!g_content_runner.Get().get());
-  g_content_runner.Get().reset(ContentMainRunner::Create());
-  g_content_runner.Get()->Initialize(0, NULL,
-                                     g_content_main_delegate.Get().get());
+  // On Android we can have multiple requests to start the browser in process
+  // simultaneously. If we get an asynchonous request followed by a synchronous
+  // request then we have to call this a second time to finish starting the
+  // browser synchronously.
+  if (!g_content_runner.Get().get()) {
+    g_content_runner.Get().reset(ContentMainRunner::Create());
+    g_content_runner.Get()->Initialize(
+        0, NULL, g_content_main_delegate.Get().get());
+  }
   return g_content_runner.Get()->Run();
 }
 
diff --git a/content/browser/android/android_browser_process.cc b/content/browser/android/android_browser_process.cc
deleted file mode 100644
index edc48e6..0000000
--- a/content/browser/android/android_browser_process.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/android/android_browser_process.h"
-
-#include "base/android/jni_string.h"
-#include "base/debug/debugger.h"
-#include "base/logging.h"
-#include "content/browser/android/content_startup_flags.h"
-#include "content/public/common/content_constants.h"
-#include "jni/AndroidBrowserProcess_jni.h"
-
-using base::android::ConvertJavaStringToUTF8;
-
-namespace content {
-
-static void SetCommandLineFlags(JNIEnv*env,
-                                jclass clazz,
-                                jint max_render_process_count,
-                                jstring plugin_descriptor) {
-  std::string plugin_str = (plugin_descriptor == NULL ?
-      std::string() : ConvertJavaStringToUTF8(env, plugin_descriptor));
-  SetContentCommandLineFlags(max_render_process_count, plugin_str);
-}
-
-static jboolean IsOfficialBuild(JNIEnv* env, jclass clazz) {
-#if defined(OFFICIAL_BUILD)
-  return true;
-#else
-  return false;
-#endif
-}
-
-static jboolean IsPluginEnabled(JNIEnv* env, jclass clazz) {
-#if defined(ENABLE_PLUGINS)
-  return true;
-#else
-  return false;
-#endif
-}
-
-bool RegisterAndroidBrowserProcess(JNIEnv* env) {
-  return RegisterNativesImpl(env);
-}
-
-}  // namespace
diff --git a/content/browser/android/android_browser_process.h b/content/browser/android/android_browser_process.h
deleted file mode 100644
index fbd06e8..0000000
--- a/content/browser/android/android_browser_process.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_APP_ANDROID_ANDROID_BROWSER_PROCESS_H_
-#define CONTENT_APP_ANDROID_ANDROID_BROWSER_PROCESS_H_
-
-#include <jni.h>
-
-namespace content {
-
-bool RegisterAndroidBrowserProcess(JNIEnv* env);
-
-}  // namespace content
-
-#endif  // CONTENT_APP_ANDROID_ANDROID_BROWSER_PROCESS_H_
diff --git a/content/browser/android/browser_jni_registrar.cc b/content/browser/android/browser_jni_registrar.cc
index 5e7940d..4e331c9 100644
--- a/content/browser/android/browser_jni_registrar.cc
+++ b/content/browser/android/browser_jni_registrar.cc
@@ -8,7 +8,6 @@
 #include "base/android/jni_registrar.h"
 #include "content/browser/accessibility/browser_accessibility_android.h"
 #include "content/browser/accessibility/browser_accessibility_manager_android.h"
-#include "content/browser/android/android_browser_process.h"
 #include "content/browser/android/browser_startup_controller.h"
 #include "content/browser/android/child_process_launcher_android.h"
 #include "content/browser/android/content_settings.h"
@@ -39,7 +38,6 @@
 base::android::RegistrationMethod kContentRegisteredMethods[] = {
     {"AndroidLocationApiAdapter",
      content::AndroidLocationApiAdapter::RegisterGeolocationService},
-    {"AndroidBrowserProcess", content::RegisterAndroidBrowserProcess},
     {"BrowserAccessibilityManager",
      content::RegisterBrowserAccessibilityManager},
     {"BrowserStartupController", content::RegisterBrowserStartupController},
diff --git a/content/browser/android/browser_media_player_manager.cc b/content/browser/android/browser_media_player_manager.cc
index dbf0c8f..31457f0 100644
--- a/content/browser/android/browser_media_player_manager.cc
+++ b/content/browser/android/browser_media_player_manager.cc
@@ -22,28 +22,23 @@
 // attempting to release inactive media players.
 static const int kMediaPlayerThreshold = 1;
 
-namespace media {
-
-static MediaPlayerManager::FactoryFunction g_factory_function = NULL;
-
-// static
-CONTENT_EXPORT void MediaPlayerManager::RegisterFactoryFunction(
-    FactoryFunction factory_function) {
-  g_factory_function = factory_function;
-}
-
-// static
-media::MediaPlayerManager* MediaPlayerManager::Create(
-    content::RenderViewHost* render_view_host) {
-  if (g_factory_function)
-    return g_factory_function(render_view_host);
-  return new content::BrowserMediaPlayerManager(render_view_host);
-}
-
-}  // namespace media
-
 namespace content {
 
+static BrowserMediaPlayerManager::Factory g_factory = NULL;
+
+// static
+void BrowserMediaPlayerManager::RegisterFactory(Factory factory) {
+  g_factory = factory;
+}
+
+// static
+BrowserMediaPlayerManager* BrowserMediaPlayerManager::Create(
+    RenderViewHost* rvh) {
+  if (g_factory)
+    return g_factory(rvh);
+  return new BrowserMediaPlayerManager(rvh);
+}
+
 BrowserMediaPlayerManager::BrowserMediaPlayerManager(
     RenderViewHost* render_view_host)
     : RenderViewHostObserver(render_view_host),
@@ -67,8 +62,7 @@
     IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_DestroyMediaPlayer, OnDestroyPlayer)
     IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_DestroyAllMediaPlayers,
                         DestroyAllMediaPlayers)
-    IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_DemuxerReady,
-                        OnDemuxerReady)
+    IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_DemuxerReady, OnDemuxerReady)
     IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_ReadFromDemuxerAck,
                         OnReadFromDemuxerAck)
     IPC_MESSAGE_HANDLER(MediaPlayerHostMsg_DurationChanged,
@@ -436,18 +430,18 @@
 
 void BrowserMediaPlayerManager::OnDemuxerReady(
     int player_id,
-    const media::MediaPlayerHostMsg_DemuxerReady_Params& params) {
+    const media::DemuxerConfigs& configs) {
   MediaPlayerAndroid* player = GetPlayer(player_id);
   if (player)
-    player->DemuxerReady(params);
+    player->DemuxerReady(configs);
 }
 
 void BrowserMediaPlayerManager::OnReadFromDemuxerAck(
     int player_id,
-    const media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params) {
+    const media::DemuxerData& data) {
   MediaPlayerAndroid* player = GetPlayer(player_id);
   if (player)
-    player->ReadFromDemuxerAck(params);
+    player->ReadFromDemuxerAck(data);
 }
 
 void BrowserMediaPlayerManager::OnMediaSeekRequestAck(
diff --git a/content/browser/android/browser_media_player_manager.h b/content/browser/android/browser_media_player_manager.h
index 7f193e9..e7c227a 100644
--- a/content/browser/android/browser_media_player_manager.h
+++ b/content/browser/android/browser_media_player_manager.h
@@ -39,6 +39,13 @@
     : public RenderViewHostObserver,
       public media::MediaPlayerManager {
  public:
+  // Permits embedders to provide an extended version of the class.
+  typedef BrowserMediaPlayerManager* (*Factory)(RenderViewHost*);
+  static void RegisterFactory(Factory factory);
+
+  // Returns a new instance using the registered factory if available.
+  static BrowserMediaPlayerManager* Create(RenderViewHost* rvh);
+
   virtual ~BrowserMediaPlayerManager();
 
   // RenderViewHostObserver overrides.
@@ -98,11 +105,7 @@
 #endif
 
  protected:
-  friend MediaPlayerManager* MediaPlayerManager::Create(
-      content::RenderViewHost*);
-
-  // The instance of this class is supposed to be created by either Create()
-  // method of MediaPlayerManager or the derived classes constructors.
+  // Clients must use Create() or subclass constructor.
   explicit BrowserMediaPlayerManager(RenderViewHost* render_view_host);
 
   // Message handlers.
@@ -119,12 +122,10 @@
   virtual void OnSetVolume(int player_id, double volume);
   virtual void OnReleaseResources(int player_id);
   virtual void OnDestroyPlayer(int player_id);
-  virtual void OnDemuxerReady(
-      int player_id,
-      const media::MediaPlayerHostMsg_DemuxerReady_Params& params);
-  virtual void OnReadFromDemuxerAck(
-      int player_id,
-      const media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params);
+  virtual void OnDemuxerReady(int player_id,
+                              const media::DemuxerConfigs& configs);
+  virtual void OnReadFromDemuxerAck(int player_id,
+                                    const media::DemuxerData& data);
   void OnMediaSeekRequestAck(int player_id, unsigned seek_request_id);
   void OnInitializeCDM(int media_keys_id, const std::vector<uint8>& uuid);
   void OnGenerateKeyRequest(int media_keys_id,
diff --git a/content/browser/android/browser_startup_controller.cc b/content/browser/android/browser_startup_controller.cc
index 6b90ce2..502f198 100644
--- a/content/browser/android/browser_startup_controller.cc
+++ b/content/browser/android/browser_startup_controller.cc
@@ -5,6 +5,9 @@
 #include "content/browser/android/browser_startup_controller.h"
 
 #include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "content/browser/android/content_startup_flags.h"
+
 #include "jni/BrowserStartupController_jni.h"
 
 namespace content {
@@ -22,4 +25,32 @@
 bool RegisterBrowserStartupController(JNIEnv* env) {
   return RegisterNativesImpl(env);
 }
+
+static void SetCommandLineFlags(JNIEnv* env,
+                                jclass clazz,
+                                jint max_render_process_count,
+                                jstring plugin_descriptor) {
+  std::string plugin_str =
+      (plugin_descriptor == NULL
+           ? std::string()
+           : base::android::ConvertJavaStringToUTF8(env, plugin_descriptor));
+  SetContentCommandLineFlags(max_render_process_count, plugin_str);
+}
+
+static jboolean IsOfficialBuild(JNIEnv* env, jclass clazz) {
+#if defined(OFFICIAL_BUILD)
+  return true;
+#else
+  return false;
+#endif
+}
+
+static jboolean IsPluginEnabled(JNIEnv* env, jclass clazz) {
+#if defined(ENABLE_PLUGINS)
+  return true;
+#else
+  return false;
+#endif
+}
+
 }  // namespace content
diff --git a/content/browser/android/child_process_launcher_android.cc b/content/browser/android/child_process_launcher_android.cc
index d73e299..ad1a018 100644
--- a/content/browser/android/child_process_launcher_android.cc
+++ b/content/browser/android/child_process_launcher_android.cc
@@ -8,6 +8,7 @@
 #include "base/android/jni_array.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
+#include "content/browser/android/browser_media_player_manager.h"
 #include "content/browser/renderer_host/compositor_impl_android.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/public/browser/browser_thread.h"
@@ -15,7 +16,6 @@
 #include "content/public/common/content_switches.h"
 #include "jni/ChildProcessLauncher_jni.h"
 #include "media/base/android/media_player_android.h"
-#include "media/base/android/media_player_manager.h"
 #include "ui/gl/android/scoped_java_surface.h"
 
 using base::android::AttachCurrentThread;
diff --git a/content/browser/android/content_view_core_impl.cc b/content/browser/android/content_view_core_impl.cc
index 7f13d6c..24c3cbf 100644
--- a/content/browser/android/content_view_core_impl.cc
+++ b/content/browser/android/content_view_core_impl.cc
@@ -577,15 +577,15 @@
                                                java_bitmap.obj());
 }
 
-ScopedJavaLocalRef<jobject> ContentViewCoreImpl::CreateSmoothScroller(
-    bool scroll_down, int mouse_event_x, int mouse_event_y) {
+ScopedJavaLocalRef<jobject> ContentViewCoreImpl::CreateGenericTouchGesture(
+    int start_x, int start_y, int delta_x, int delta_y) {
   JNIEnv* env = AttachCurrentThread();
 
   ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
   if (obj.is_null())
     return ScopedJavaLocalRef<jobject>();
-  return Java_ContentViewCore_createSmoothScroller(
-      env, obj.obj(), scroll_down, mouse_event_x, mouse_event_y);
+  return Java_ContentViewCore_createGenericTouchGesture(
+      env, obj.obj(), start_x, start_y, delta_x, delta_y);
 }
 
 void ContentViewCoreImpl::NotifyExternalSurface(
@@ -1301,12 +1301,8 @@
 #if defined(GOOGLE_TV)
   RenderViewHostImpl* rvhi = static_cast<RenderViewHostImpl*>(
       web_contents_->GetRenderViewHost());
-  BrowserMediaPlayerManager* browser_media_player_manager =
-      rvhi ? static_cast<BrowserMediaPlayerManager*>(
-                 rvhi->media_player_manager())
-           : NULL;
-  if (browser_media_player_manager) {
-    browser_media_player_manager->AttachExternalVideoSurface(
+  if (rvhi && rvhi->media_player_manager()) {
+    rvhi->media_player_manager()->AttachExternalVideoSurface(
         static_cast<int>(player_id), jsurface);
   }
 #endif
@@ -1318,12 +1314,8 @@
 #if defined(GOOGLE_TV)
   RenderViewHostImpl* rvhi = static_cast<RenderViewHostImpl*>(
       web_contents_->GetRenderViewHost());
-  BrowserMediaPlayerManager* browser_media_player_manager =
-      rvhi ? static_cast<BrowserMediaPlayerManager*>(
-                 rvhi->media_player_manager())
-           : NULL;
-  if (browser_media_player_manager) {
-    browser_media_player_manager->DetachExternalVideoSurface(
+  if (rvhi && rvhi->media_player_manager()) {
+    rvhi->media_player_manager()->DetachExternalVideoSurface(
         static_cast<int>(player_id));
   }
 #endif
diff --git a/content/browser/android/content_view_core_impl.h b/content/browser/android/content_view_core_impl.h
index f3cf755..ddcf0b6 100644
--- a/content/browser/android/content_view_core_impl.h
+++ b/content/browser/android/content_view_core_impl.h
@@ -268,10 +268,10 @@
   void ShowDisambiguationPopup(
       const gfx::Rect& target_rect, const SkBitmap& zoomed_bitmap);
 
-  // Creates a java-side smooth scroller. Used by
+  // Creates a java-side touch gesture, e.g. used by
   // chrome.gpuBenchmarking.smoothScrollBy.
-  base::android::ScopedJavaLocalRef<jobject> CreateSmoothScroller(
-      bool scroll_down, int mouse_event_x, int mouse_event_y);
+  base::android::ScopedJavaLocalRef<jobject> CreateGenericTouchGesture(
+      int start_x, int start_y, int delta_x, int delta_y);
 
   // Notifies the java object about the external surface, requesting for one if
   // necessary.
diff --git a/content/browser/android/in_process/synchronous_compositor_impl.cc b/content/browser/android/in_process/synchronous_compositor_impl.cc
index 8fcebf4..ee21e69 100644
--- a/content/browser/android/in_process/synchronous_compositor_impl.cc
+++ b/content/browser/android/in_process/synchronous_compositor_impl.cc
@@ -19,7 +19,7 @@
 #include "content/renderer/media/android/stream_texture_factory_android_synchronous_impl.h"
 #include "gpu/command_buffer/client/gl_in_process_context.h"
 #include "gpu/command_buffer/service/stream_texture_manager_in_process_android.h"
-#include "ui/gl/android/surface_texture_bridge.h"
+#include "ui/gl/android/surface_texture.h"
 #include "ui/gl/gl_surface.h"
 #include "webkit/common/gpu/context_provider_in_process.h"
 #include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h"
@@ -54,7 +54,7 @@
       : context_provider_(context_provider),
         gl_in_process_context_(gl_in_process_context) {}
 
-  virtual scoped_refptr<gfx::SurfaceTextureBridge> GetSurfaceTexture(
+  virtual scoped_refptr<gfx::SurfaceTexture> GetSurfaceTexture(
       uint32 stream_id) OVERRIDE {
     return gl_in_process_context_->GetSurfaceTexture(stream_id);
   }
diff --git a/content/browser/android/in_process/synchronous_compositor_output_surface.cc b/content/browser/android/in_process/synchronous_compositor_output_surface.cc
index 1faed9e..1919d73 100644
--- a/content/browser/android/in_process/synchronous_compositor_output_surface.cc
+++ b/content/browser/android/in_process/synchronous_compositor_output_surface.cc
@@ -15,8 +15,8 @@
 #include "content/browser/android/in_process/synchronous_compositor_impl.h"
 #include "content/public/browser/browser_thread.h"
 #include "gpu/command_buffer/client/gl_in_process_context.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
 #include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkDevice.h"
 #include "ui/gfx/rect_conversions.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/gfx/transform.h"
@@ -96,7 +96,7 @@
 
  private:
   SynchronousCompositorOutputSurface* surface_;
-  SkDevice null_device_;
+  SkBitmapDevice null_device_;
   SkCanvas null_canvas_;
 
   DISALLOW_COPY_AND_ASSIGN(SoftwareDevice);
diff --git a/content/browser/android/surface_texture_peer_browser_impl.cc b/content/browser/android/surface_texture_peer_browser_impl.cc
index faaf4a2..622c2d0 100644
--- a/content/browser/android/surface_texture_peer_browser_impl.cc
+++ b/content/browser/android/surface_texture_peer_browser_impl.cc
@@ -4,11 +4,11 @@
 
 #include "content/browser/android/surface_texture_peer_browser_impl.h"
 
+#include "content/browser/android/browser_media_player_manager.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_process_host.h"
 #include "media/base/android/media_player_android.h"
-#include "media/base/android/media_player_manager.h"
 #include "ui/gl/android/scoped_java_surface.h"
 
 namespace content {
@@ -18,7 +18,7 @@
 // Pass a java surface object to the MediaPlayerAndroid object
 // identified by render process handle, render view ID and player ID.
 static void SetSurfacePeer(
-    scoped_refptr<gfx::SurfaceTextureBridge> surface_texture_bridge,
+    scoped_refptr<gfx::SurfaceTexture> surface_texture,
     base::ProcessHandle render_process_handle,
     int render_view_id,
     int player_id) {
@@ -40,7 +40,7 @@
           host->media_player_manager()->GetPlayer(player_id);
       if (player &&
           player != host->media_player_manager()->GetFullscreenPlayer()) {
-        gfx::ScopedJavaSurface surface(surface_texture_bridge.get());
+        gfx::ScopedJavaSurface surface(surface_texture.get());
         player->SetVideoSurface(surface.Pass());
       }
     }
@@ -57,14 +57,14 @@
 
 void SurfaceTexturePeerBrowserImpl::EstablishSurfaceTexturePeer(
     base::ProcessHandle render_process_handle,
-    scoped_refptr<gfx::SurfaceTextureBridge> surface_texture_bridge,
+    scoped_refptr<gfx::SurfaceTexture> surface_texture,
     int render_view_id,
     int player_id) {
-  if (!surface_texture_bridge.get())
+  if (!surface_texture.get())
     return;
 
   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
-      &SetSurfacePeer, surface_texture_bridge, render_process_handle,
+      &SetSurfacePeer, surface_texture, render_process_handle,
       render_view_id, player_id));
 }
 
diff --git a/content/browser/android/surface_texture_peer_browser_impl.h b/content/browser/android/surface_texture_peer_browser_impl.h
index 644bf2c..4aa8563 100644
--- a/content/browser/android/surface_texture_peer_browser_impl.h
+++ b/content/browser/android/surface_texture_peer_browser_impl.h
@@ -24,7 +24,7 @@
   // SurfaceTexturePeer implementation.
   virtual void EstablishSurfaceTexturePeer(
       base::ProcessHandle render_process_handle,
-      scoped_refptr<gfx::SurfaceTextureBridge> surface_texture_bridge,
+      scoped_refptr<gfx::SurfaceTexture> surface_texture,
       int render_view_id,
       int player_id) OVERRIDE;
 
diff --git a/content/browser/android/web_contents_observer_android.cc b/content/browser/android/web_contents_observer_android.cc
index d9a1507..83821c4 100644
--- a/content/browser/android/web_contents_observer_android.cc
+++ b/content/browser/android/web_contents_observer_android.cc
@@ -15,6 +15,7 @@
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/navigation_details.h"
+#include "content/public/browser/navigation_entry.h"
 #include "jni/WebContentsObserverAndroid_jni.h"
 
 using base::android::AttachCurrentThread;
@@ -78,8 +79,19 @@
   ScopedJavaLocalRef<jobject> obj(weak_java_observer_.get(env));
   if (obj.is_null())
     return;
-  ScopedJavaLocalRef<jstring> jstring_url(ConvertUTF8ToJavaString(
-      env, web_contents()->GetLastCommittedURL().spec()));
+
+  std::string url_string;
+  NavigationEntry* entry =
+    web_contents()->GetController().GetLastCommittedEntry();
+  // Not that GetBaseURLForDataURL is only used by the Android WebView
+  if (entry && !entry->GetBaseURLForDataURL().is_empty()) {
+    url_string = entry->GetBaseURLForDataURL().possibly_invalid_spec();
+  } else {
+    url_string = web_contents()->GetLastCommittedURL().spec();
+  }
+
+  ScopedJavaLocalRef<jstring> jstring_url(
+      ConvertUTF8ToJavaString(env, url_string));
   Java_WebContentsObserverAndroid_didStopLoading(
       env, obj.obj(), jstring_url.obj());
 }
diff --git a/content/browser/aura/software_output_device_win.cc b/content/browser/aura/software_output_device_win.cc
index 1a5af46..7937810 100644
--- a/content/browser/aura/software_output_device_win.cc
+++ b/content/browser/aura/software_output_device_win.cc
@@ -36,7 +36,7 @@
     return;
 
   viewport_size_ = viewport_size;
-  contents_.reset(new gfx::Canvas(viewport_size, ui::SCALE_FACTOR_100P, false));
+  contents_.reset(new gfx::Canvas(viewport_size, ui::SCALE_FACTOR_100P, true));
   memset(&bitmap_info_, 0, sizeof(bitmap_info_));
   gfx::CreateBitmapHeader(viewport_size_.width(), viewport_size_.height(),
                           &bitmap_info_.bmiHeader);
@@ -85,16 +85,9 @@
                           RGB(0xFF, 0xFF, 0xFF), &blend, ULW_ALPHA);
     skia::EndPlatformPaint(canvas);
   } else {
-    SkDevice* device = canvas->getDevice();
-    const SkBitmap& bitmap = device->accessBitmap(false);
     HDC hdc = ::GetDC(hwnd_);
-    gfx::StretchDIBits(hdc,
-                       rect.x(), rect.y(),
-                       rect.width(), rect.height(),
-                       rect.x(), rect.y(),
-                       rect.width(), rect.height(),
-                       bitmap.getPixels(),
-                       &bitmap_info_);
+    RECT src_rect = rect.ToRECT();
+    skia::DrawToNativeContext(canvas, hdc, rect.x(), rect.y(), &src_rect);
     ::ReleaseDC(hwnd_, hdc);
   }
 }
@@ -102,7 +95,7 @@
 void SoftwareOutputDeviceWin::CopyToBitmap(
     gfx::Rect rect, SkBitmap* output) {
   DCHECK(contents_);
-  SkDevice* device = contents_->sk_canvas()->getDevice();
+  SkBaseDevice* device = contents_->sk_canvas()->getDevice();
   const SkBitmap& bitmap = device->accessBitmap(false);
   bitmap.extractSubset(output, gfx::RectToSkIRect(rect));
 }
diff --git a/content/browser/browser_child_process_host_impl.cc b/content/browser/browser_child_process_host_impl.cc
index 780dc6a..172674e 100644
--- a/content/browser/browser_child_process_host_impl.cc
+++ b/content/browser/browser_child_process_host_impl.cc
@@ -223,11 +223,11 @@
 }
 
 base::TerminationStatus BrowserChildProcessHostImpl::GetTerminationStatus(
-    int* exit_code) {
+    bool known_dead, int* exit_code) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   if (!child_process_)  // If the delegate doesn't use Launch() helper.
     return base::GetTerminationStatus(data_.handle, exit_code);
-  return child_process_->GetChildTerminationStatus(false /* known_dead */,
+  return child_process_->GetChildTerminationStatus(known_dead,
                                                    exit_code);
 }
 
@@ -264,7 +264,8 @@
   if (child_process_.get() || data_.handle) {
     DCHECK(data_.handle != base::kNullProcessHandle);
     int exit_code;
-    base::TerminationStatus status = GetTerminationStatus(&exit_code);
+    base::TerminationStatus status = GetTerminationStatus(
+        true /* known_dead */, &exit_code);
     switch (status) {
       case base::TERMINATION_STATUS_PROCESS_CRASHED:
       case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: {
diff --git a/content/browser/browser_child_process_host_impl.h b/content/browser/browser_child_process_host_impl.h
index 61971d6..866808f 100644
--- a/content/browser/browser_child_process_host_impl.h
+++ b/content/browser/browser_child_process_host_impl.h
@@ -51,7 +51,8 @@
       CommandLine* cmd_line) OVERRIDE;
   virtual const ChildProcessData& GetData() const OVERRIDE;
   virtual ChildProcessHost* GetHost() const OVERRIDE;
-  virtual base::TerminationStatus GetTerminationStatus(int* exit_code) OVERRIDE;
+  virtual base::TerminationStatus GetTerminationStatus(
+      bool known_dead, int* exit_code) OVERRIDE;
   virtual void SetName(const string16& name) OVERRIDE;
   virtual void SetHandle(base::ProcessHandle handle) OVERRIDE;
 
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 8bf6503..ee84e7c 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -420,6 +420,7 @@
     network_change_notifier_.reset(net::NetworkChangeNotifier::Create());
   }
 
+#if !defined(OS_IOS)
   {
     TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:MediaFeatures")
     media::InitializeCPUSpecificMediaFeatures();
@@ -432,8 +433,6 @@
     TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:MIDIManager")
     midi_manager_.reset(media::MIDIManager::Create());
   }
-
-#if !defined(OS_IOS)
   {
     TRACE_EVENT0("startup", "BrowserMainLoop::Subsystem:ContentWebUIController")
     WebUIControllerFactory::RegisterFactory(
@@ -527,36 +526,53 @@
 }
 
 void BrowserMainLoop::CreateStartupTasks() {
-  TRACE_EVENT0("startup", "BrowserMainLoop::CreateStartupTasks")
+  TRACE_EVENT0("startup", "BrowserMainLoop::CreateStartupTasks");
+
+  // First time through, we really want to create all the tasks
+  if (!startup_task_runner_.get()) {
+#if defined(OS_ANDROID)
+    startup_task_runner_ = make_scoped_ptr(new StartupTaskRunner(
+        base::Bind(&BrowserStartupComplete),
+        base::MessageLoop::current()->message_loop_proxy()));
+#else
+    startup_task_runner_ = make_scoped_ptr(new StartupTaskRunner(
+        base::Callback<void(int)>(),
+        base::MessageLoop::current()->message_loop_proxy()));
+#endif
+    StartupTask pre_create_threads =
+        base::Bind(&BrowserMainLoop::PreCreateThreads, base::Unretained(this));
+    startup_task_runner_->AddTask(pre_create_threads);
+
+    StartupTask create_threads =
+        base::Bind(&BrowserMainLoop::CreateThreads, base::Unretained(this));
+    startup_task_runner_->AddTask(create_threads);
+
+    StartupTask browser_thread_started = base::Bind(
+        &BrowserMainLoop::BrowserThreadsStarted, base::Unretained(this));
+    startup_task_runner_->AddTask(browser_thread_started);
+
+    StartupTask pre_main_message_loop_run = base::Bind(
+        &BrowserMainLoop::PreMainMessageLoopRun, base::Unretained(this));
+    startup_task_runner_->AddTask(pre_main_message_loop_run);
 
 #if defined(OS_ANDROID)
-  scoped_refptr<StartupTaskRunner> task_runner =
-      new StartupTaskRunner(BrowserMayStartAsynchronously(),
-                            base::Bind(&BrowserStartupComplete),
-                            base::MessageLoop::current()->message_loop_proxy());
-#else
-  scoped_refptr<StartupTaskRunner> task_runner =
-      new StartupTaskRunner(false,
-                            base::Callback<void(int)>(),
-                            base::MessageLoop::current()->message_loop_proxy());
+    if (BrowserMayStartAsynchronously()) {
+      startup_task_runner_->StartRunningTasksAsync();
+    }
 #endif
-  StartupTask pre_create_threads =
-      base::Bind(&BrowserMainLoop::PreCreateThreads, base::Unretained(this));
-  task_runner->AddTask(pre_create_threads);
-
-  StartupTask create_threads =
-      base::Bind(&BrowserMainLoop::CreateThreads, base::Unretained(this));
-  task_runner->AddTask(create_threads);
-
-  StartupTask browser_thread_started = base::Bind(
-      &BrowserMainLoop::BrowserThreadsStarted, base::Unretained(this));
-  task_runner->AddTask(browser_thread_started);
-
-  StartupTask pre_main_message_loop_run = base::Bind(
-      &BrowserMainLoop::PreMainMessageLoopRun, base::Unretained(this));
-  task_runner->AddTask(pre_main_message_loop_run);
-
-  task_runner->StartRunningTasks();
+  }
+#if defined(OS_ANDROID)
+  if (!BrowserMayStartAsynchronously()) {
+    // A second request for asynchronous startup can be ignored, so
+    // StartupRunningTasksAsync is only called first time through. If, however,
+    // this is a request for synchronous startup then it must override any
+    // previous call for async startup, so we call RunAllTasksNow()
+    // unconditionally.
+    startup_task_runner_->RunAllTasksNow();
+  }
+#else
+  startup_task_runner_->RunAllTasksNow();
+#endif
 }
 
 int BrowserMainLoop::CreateThreads() {
diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h
index 83dbafd..b3ec73c 100644
--- a/content/browser/browser_main_loop.h
+++ b/content/browser/browser_main_loop.h
@@ -42,6 +42,7 @@
 class MediaStreamManager;
 class ResourceDispatcherHostImpl;
 class SpeechRecognitionManagerImpl;
+class StartupTaskRunner;
 class SystemMessageWindowWin;
 struct MainFunctionParams;
 
@@ -68,7 +69,10 @@
   void InitializeToolkit();
   void MainMessageLoopStart();
 
-  // Create the tasks we need to complete startup.
+  // Create and start running the tasks we need to complete startup. Note that
+  // this can be called more than once (currently only on Android) if we get a
+  // request for synchronous startup while the tasks created by asynchronous
+  // startup are still running.
   void CreateStartupTasks();
 
   // Perform the default message loop run logic.
@@ -141,6 +145,8 @@
 #elif defined(OS_MACOSX) && !defined(OS_IOS)
   scoped_ptr<DeviceMonitorMac> device_monitor_mac_;
 #endif
+  // The startup task runner is created by CreateStartupTasks()
+  scoped_ptr<StartupTaskRunner> startup_task_runner_;
 
   // Destroy parts_ before main_message_loop_ (required) and before other
   // classes constructed in content (but after main_thread_).
diff --git a/content/browser/browser_main_runner.cc b/content/browser/browser_main_runner.cc
index cca1466..f97318a 100644
--- a/content/browser/browser_main_runner.cc
+++ b/content/browser/browser_main_runner.cc
@@ -29,75 +29,82 @@
 
 class BrowserMainRunnerImpl : public BrowserMainRunner {
  public:
-  BrowserMainRunnerImpl() : is_initialized_(false), is_shutdown_(false) {}
+  BrowserMainRunnerImpl()
+      : initialization_started_(false), is_shutdown_(false) {}
 
   virtual ~BrowserMainRunnerImpl() {
-    if (is_initialized_ && !is_shutdown_)
+    if (initialization_started_ && !is_shutdown_)
       Shutdown();
   }
 
-  virtual int Initialize(const MainFunctionParams& parameters)
-      OVERRIDE {
-    TRACE_EVENT0("startup", "BrowserMainRunnerImpl::Initialize")
-    is_initialized_ = true;
+  virtual int Initialize(const MainFunctionParams& parameters) OVERRIDE {
+    TRACE_EVENT0("startup", "BrowserMainRunnerImpl::Initialize");
+    // On Android we normally initialize the browser in a series of UI thread
+    // tasks. While this is happening a second request can come from the OS or
+    // another application to start the browser. If this happens then we must
+    // not run these parts of initialization twice.
+    if (!initialization_started_) {
+      initialization_started_ = true;
 
 #if !defined(OS_IOS)
-  if (parameters.command_line.HasSwitch(switches::kWaitForDebugger))
-    base::debug::WaitForDebugger(60, true);
+      if (parameters.command_line.HasSwitch(switches::kWaitForDebugger))
+        base::debug::WaitForDebugger(60, true);
 #endif
 
 #if defined(OS_WIN)
-    if (parameters.command_line.HasSwitch(
-            switches::kEnableTextServicesFramework)) {
-      base::win::SetForceToUseTSF();
-    } else if (base::win::GetVersion() < base::win::VERSION_VISTA) {
-      // When "Extend support of advanced text services to all programs"
-      // (a.k.a. Cicero Unaware Application Support; CUAS) is enabled on
-      // Windows XP and handwriting modules shipped with Office 2003 are
-      // installed, "penjpn.dll" and "skchui.dll" will be loaded and then crash
-      // unless a user installs Office 2003 SP3. To prevent these modules from
-      // being loaded, disable TSF entirely. crbug/160914.
-      // TODO(yukawa): Add a high-level wrapper for this instead of calling
-      // Win32 API here directly.
-      ImmDisableTextFrameService(static_cast<DWORD>(-1));
-    }
+      if (parameters.command_line.HasSwitch(
+              switches::kEnableTextServicesFramework)) {
+        base::win::SetForceToUseTSF();
+      } else if (base::win::GetVersion() < base::win::VERSION_VISTA) {
+        // When "Extend support of advanced text services to all programs"
+        // (a.k.a. Cicero Unaware Application Support; CUAS) is enabled on
+        // Windows XP and handwriting modules shipped with Office 2003 are
+        // installed, "penjpn.dll" and "skchui.dll" will be loaded and then
+        // crash
+        // unless a user installs Office 2003 SP3. To prevent these modules from
+        // being loaded, disable TSF entirely. crbug/160914.
+        // TODO(yukawa): Add a high-level wrapper for this instead of calling
+        // Win32 API here directly.
+        ImmDisableTextFrameService(static_cast<DWORD>(-1));
+      }
 #endif  // OS_WIN
 
-    base::StatisticsRecorder::Initialize();
+      base::StatisticsRecorder::Initialize();
 
-    notification_service_.reset(new NotificationServiceImpl);
+      notification_service_.reset(new NotificationServiceImpl);
 
 #if defined(OS_WIN)
-    // Ole must be initialized before starting message pump, so that TSF
-    // (Text Services Framework) module can interact with the message pump
-    // on Windows 8 Metro mode.
-    ole_initializer_.reset(new ui::ScopedOleInitializer);
+      // Ole must be initialized before starting message pump, so that TSF
+      // (Text Services Framework) module can interact with the message pump
+      // on Windows 8 Metro mode.
+      ole_initializer_.reset(new ui::ScopedOleInitializer);
 #endif  // OS_WIN
 
-    main_loop_.reset(new BrowserMainLoop(parameters));
+      main_loop_.reset(new BrowserMainLoop(parameters));
 
-    main_loop_->Init();
+      main_loop_->Init();
 
-    main_loop_->EarlyInitialization();
+      main_loop_->EarlyInitialization();
 
-    // Must happen before we try to use a message loop or display any UI.
-    main_loop_->InitializeToolkit();
+      // Must happen before we try to use a message loop or display any UI.
+      main_loop_->InitializeToolkit();
 
-    main_loop_->MainMessageLoopStart();
+      main_loop_->MainMessageLoopStart();
 
-    // WARNING: If we get a WM_ENDSESSION, objects created on the stack here
-    // are NOT deleted. If you need something to run during WM_ENDSESSION add it
-    // to browser_shutdown::Shutdown or BrowserProcess::EndSession.
+// WARNING: If we get a WM_ENDSESSION, objects created on the stack here
+// are NOT deleted. If you need something to run during WM_ENDSESSION add it
+// to browser_shutdown::Shutdown or BrowserProcess::EndSession.
 
 #if defined(OS_WIN) && !defined(NO_TCMALLOC)
-    // When linking shared libraries, NO_TCMALLOC is defined, and dynamic
-    // allocator selection is not supported.
+      // When linking shared libraries, NO_TCMALLOC is defined, and dynamic
+      // allocator selection is not supported.
 
-    // Make this call before going multithreaded, or spawning any subprocesses.
-    base::allocator::SetupSubprocessAllocator();
+      // Make this call before going multithreaded, or spawning any
+      // subprocesses.
+      base::allocator::SetupSubprocessAllocator();
 #endif
-    ui::InitializeInputMethod();
-
+      ui::InitializeInputMethod();
+    }
     main_loop_->CreateStartupTasks();
     int result_code = main_loop_->GetResultCode();
     if (result_code > 0)
@@ -108,14 +115,14 @@
   }
 
   virtual int Run() OVERRIDE {
-    DCHECK(is_initialized_);
+    DCHECK(initialization_started_);
     DCHECK(!is_shutdown_);
     main_loop_->RunMainMessageLoopParts();
     return main_loop_->GetResultCode();
   }
 
   virtual void Shutdown() OVERRIDE {
-    DCHECK(is_initialized_);
+    DCHECK(initialization_started_);
     DCHECK(!is_shutdown_);
     g_exited_main_message_loop = true;
 
@@ -134,8 +141,8 @@
   }
 
  protected:
-  // True if the runner has been initialized.
-  bool is_initialized_;
+  // True if we have started to initialize the runner.
+  bool initialization_started_;
 
   // True if the runner has been shut down.
   bool is_shutdown_;
diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc
index 96e60e1..83f2b3c 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.cc
+++ b/content/browser/browser_plugin/browser_plugin_guest.cc
@@ -1295,10 +1295,12 @@
     // Do not allow navigating a guest to schemes other than known safe schemes.
     // This will block the embedder trying to load unwanted schemes, e.g.
     // chrome://settings.
-    if (!ChildProcessSecurityPolicyImpl::GetInstance()->IsWebSafeScheme(
+    bool scheme_is_blocked =
+        !ChildProcessSecurityPolicyImpl::GetInstance()->IsWebSafeScheme(
             url.scheme()) &&
         !ChildProcessSecurityPolicyImpl::GetInstance()->IsPseudoScheme(
-            url.scheme())) {
+            url.scheme());
+    if (scheme_is_blocked || !url.is_valid()) {
       if (delegate_) {
         std::string error_type;
         RemoveChars(net::ErrorToString(net::ERR_ABORTED), "net::", &error_type);
@@ -1312,8 +1314,8 @@
     // other schemes (e.g., WebUI or extensions), and no permissions or bindings
     // can be granted to the guest process.
     GetWebContents()->GetController().LoadURL(url, Referrer(),
-                                            PAGE_TRANSITION_AUTO_TOPLEVEL,
-                                            std::string());
+                                              PAGE_TRANSITION_AUTO_TOPLEVEL,
+                                              std::string());
   }
 }
 
@@ -1391,6 +1393,7 @@
   if (auto_size_enabled_ && (!old_auto_size_enabled ||
                              (old_max_size != max_auto_size_) ||
                              (old_min_size != min_auto_size_))) {
+    RecordAction(UserMetricsAction("BrowserPlugin.Guest.EnableAutoResize"));
     GetWebContents()->GetRenderViewHost()->EnableAutoResize(
         min_auto_size_, max_auto_size_);
     // TODO(fsamuel): If we're changing autosize parameters, then we force
@@ -1549,6 +1552,7 @@
     JavaScriptMessageType javascript_message_type,
     const string16& message_text,
     const string16& default_prompt_text,
+    bool user_gesture,
     const DialogClosedCallback& callback,
     bool* did_suppress_message) {
   if (permission_request_map_.size() >= kNumMaxOutstandingPermissionRequests) {
diff --git a/content/browser/browser_plugin/browser_plugin_guest.h b/content/browser/browser_plugin/browser_plugin_guest.h
index 74624ef..15d28d2 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.h
+++ b/content/browser/browser_plugin/browser_plugin_guest.h
@@ -213,6 +213,7 @@
       JavaScriptMessageType javascript_message_type,
       const string16& message_text,
       const string16& default_prompt_text,
+      bool user_gesture,
       const DialogClosedCallback& callback,
       bool* did_suppress_message) OVERRIDE;
   virtual void RunBeforeUnloadDialog(
diff --git a/content/browser/browser_plugin/browser_plugin_host_browsertest.cc b/content/browser/browser_plugin/browser_plugin_host_browsertest.cc
index 8112296..7950cb8 100644
--- a/content/browser/browser_plugin/browser_plugin_host_browsertest.cc
+++ b/content/browser/browser_plugin/browser_plugin_host_browsertest.cc
@@ -13,6 +13,7 @@
 #include "content/browser/browser_plugin/browser_plugin_host_factory.h"
 #include "content/browser/browser_plugin/test_browser_plugin_embedder.h"
 #include "content/browser/browser_plugin/test_browser_plugin_guest.h"
+#include "content/browser/browser_plugin/test_browser_plugin_guest_delegate.h"
 #include "content/browser/browser_plugin/test_browser_plugin_guest_manager.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
@@ -24,6 +25,7 @@
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/drop_data.h"
+#include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
 #include "content/shell/browser/shell.h"
@@ -811,4 +813,34 @@
   EXPECT_TRUE(rvh->input_method_active());
 }
 
+// Verify that navigating to an invalid URL (e.g. 'http:') doesn't cause
+// a crash.
+IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, DoNotCrashOnInvalidNavigation) {
+  const char kEmbedderURL[] = "/browser_plugin_embedder.html";
+  StartBrowserPluginTest(kEmbedderURL, kHTMLForGuest, true, std::string());
+  TestBrowserPluginGuestDelegate* delegate =
+      new TestBrowserPluginGuestDelegate();
+  test_guest()->SetDelegate(delegate);
+
+  const char kValidSchemeWithEmptyURL[] = "http:";
+  ExecuteSyncJSFunction(
+      test_embedder()->web_contents()->GetRenderViewHost(),
+      base::StringPrintf("SetSrc('%s');", kValidSchemeWithEmptyURL));
+  EXPECT_TRUE(delegate->load_aborted());
+  EXPECT_FALSE(delegate->load_aborted_url().is_valid());
+  EXPECT_EQ(kValidSchemeWithEmptyURL,
+            delegate->load_aborted_url().possibly_invalid_spec());
+
+  delegate->ResetStates();
+
+  // Attempt a navigation to chrome-guest://abc123, which is a valid URL. But it
+  // should be blocked because the scheme isn't web-safe or a pseudo-scheme.
+  ExecuteSyncJSFunction(
+      test_embedder()->web_contents()->GetRenderViewHost(),
+      base::StringPrintf("SetSrc('%s://abc123');",
+                         chrome::kGuestScheme));
+  EXPECT_TRUE(delegate->load_aborted());
+  EXPECT_TRUE(delegate->load_aborted_url().is_valid());
+}
+
 }  // namespace content
diff --git a/content/browser/browser_plugin/test_browser_plugin_guest_delegate.cc b/content/browser/browser_plugin/test_browser_plugin_guest_delegate.cc
new file mode 100644
index 0000000..28c27b7
--- /dev/null
+++ b/content/browser/browser_plugin/test_browser_plugin_guest_delegate.cc
@@ -0,0 +1,66 @@
+// 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 "content/browser/browser_plugin/test_browser_plugin_guest_delegate.h"
+
+namespace content {
+
+TestBrowserPluginGuestDelegate::TestBrowserPluginGuestDelegate()
+    : load_aborted_(false) {
+}
+
+TestBrowserPluginGuestDelegate::~TestBrowserPluginGuestDelegate() {
+}
+
+void TestBrowserPluginGuestDelegate::ResetStates() {
+  load_aborted_ = false;
+  load_aborted_url_ = GURL();
+}
+
+void TestBrowserPluginGuestDelegate::AddMessageToConsole(
+    int32 level,
+    const string16& message,
+    int32 line_no,
+    const string16& source_id) {
+}
+
+void TestBrowserPluginGuestDelegate::Close() {
+}
+
+void TestBrowserPluginGuestDelegate::GuestProcessGone(
+    base::TerminationStatus status) {
+}
+
+bool TestBrowserPluginGuestDelegate::HandleKeyboardEvent(
+    const NativeWebKeyboardEvent& event) {
+  return BrowserPluginGuestDelegate::HandleKeyboardEvent(event);
+}
+
+void TestBrowserPluginGuestDelegate::LoadAbort(bool is_top_level,
+                                               const GURL& url,
+                                               const std::string& error_type) {
+  load_aborted_ = true;
+  load_aborted_url_ = url;
+}
+
+void TestBrowserPluginGuestDelegate::RendererResponsive() {
+}
+
+void TestBrowserPluginGuestDelegate::RendererUnresponsive() {
+}
+
+bool TestBrowserPluginGuestDelegate::RequestPermission(
+    BrowserPluginPermissionType permission_type,
+    const base::DictionaryValue& request_info,
+    const PermissionResponseCallback& callback) {
+  return BrowserPluginGuestDelegate::RequestPermission(permission_type,
+                                                       request_info,
+                                                       callback);
+}
+
+void TestBrowserPluginGuestDelegate::SizeChanged(const gfx::Size& old_size,
+                                                 const gfx::Size& new_size) {
+}
+
+}  // namespace content
diff --git a/content/browser/browser_plugin/test_browser_plugin_guest_delegate.h b/content/browser/browser_plugin/test_browser_plugin_guest_delegate.h
new file mode 100644
index 0000000..b6e7e90
--- /dev/null
+++ b/content/browser/browser_plugin/test_browser_plugin_guest_delegate.h
@@ -0,0 +1,51 @@
+// 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 CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_DELEGATE_H_
+#define CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_DELEGATE_H_
+
+#include "content/public/browser/browser_plugin_guest_delegate.h"
+
+namespace content {
+
+class TestBrowserPluginGuestDelegate : public BrowserPluginGuestDelegate {
+ public:
+  TestBrowserPluginGuestDelegate();
+  virtual ~TestBrowserPluginGuestDelegate();
+
+  void ResetStates();
+
+  bool load_aborted() const { return load_aborted_; }
+  const GURL& load_aborted_url() const { return load_aborted_url_; }
+
+ private:
+  // Overridden from BrowserPluginGuestDelegate:
+  virtual void AddMessageToConsole(int32 level,
+                                   const string16& message,
+                                   int32 line_no,
+                                   const string16& source_id) OVERRIDE;
+  virtual void Close() OVERRIDE;
+  virtual void GuestProcessGone(base::TerminationStatus status) OVERRIDE;
+  virtual bool HandleKeyboardEvent(
+      const NativeWebKeyboardEvent& event) OVERRIDE;
+  virtual void LoadAbort(bool is_top_level,
+                         const GURL& url,
+                         const std::string& error_type) OVERRIDE;
+  virtual void RendererResponsive() OVERRIDE;
+  virtual void RendererUnresponsive() OVERRIDE;
+  virtual bool RequestPermission(
+      BrowserPluginPermissionType permission_type,
+      const base::DictionaryValue& request_info,
+      const PermissionResponseCallback& callback) OVERRIDE;
+  virtual void SizeChanged(const gfx::Size& old_size,
+                           const gfx::Size& new_size) OVERRIDE;
+
+  bool load_aborted_;
+  GURL load_aborted_url_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginGuestDelegate);
+};
+
+}  // namespace content
+#endif  // CONTENT_BROWSER_BROWSER_PLUGIN_TEST_BROWSER_PLUGIN_GUEST_DELEGATE_H_
diff --git a/content/browser/browser_url_handler_impl.cc b/content/browser/browser_url_handler_impl.cc
index 56efe6b..1c06fdd 100644
--- a/content/browser/browser_url_handler_impl.cc
+++ b/content/browser/browser_url_handler_impl.cc
@@ -13,8 +13,7 @@
 namespace content {
 
 // Handles rewriting view-source URLs for what we'll actually load.
-static bool HandleViewSource(GURL* url,
-                             BrowserContext* browser_context) {
+static bool HandleViewSource(GURL* url, BrowserContext* browser_context) {
   if (url->SchemeIs(kViewSourceScheme)) {
     // Load the inner URL instead.
     *url = GURL(url->path());
@@ -22,7 +21,7 @@
     // Bug 26129: limit view-source to view the content and not any
     // other kind of 'active' url scheme like 'javascript' or 'data'.
     static const char* const allowed_sub_schemes[] = {
-      chrome::kHttpScheme, chrome::kHttpsScheme, chrome::kFtpScheme,
+      chrome::kHttpScheme, kHttpsScheme, chrome::kFtpScheme,
       chrome::kChromeDevToolsScheme, chrome::kChromeUIScheme,
       chrome::kFileScheme, chrome::kFileSystemScheme
     };
diff --git a/content/browser/child_process_launcher.h b/content/browser/child_process_launcher.h
index 5a6e1f9..fdbb58c 100644
--- a/content/browser/child_process_launcher.h
+++ b/content/browser/child_process_launcher.h
@@ -58,6 +58,11 @@
   // Call this when the child process exits to know what happened to it.
   // |known_dead| can be true if we already know the process is dead as it can
   // help the implemention figure the proper TerminationStatus.
+  // On Linux, the use of |known_dead| is subtle and can be crucial if an
+  // accurate status is important. With |known_dead| set to false, a dead
+  // process could be seen as running. With |known_dead| set to true, the
+  // process will be killed if it was still running. See ZygoteHostImpl for
+  // more discussion of Linux implementation details.
   // |exit_code| is the exit code of the process if it exited (e.g. status from
   // waitpid if on posix, from GetExitCodeProcess on Windows). |exit_code| may
   // be NULL.
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc
index b47cbd2..91a7493 100644
--- a/content/browser/child_process_security_policy_impl.cc
+++ b/content/browser/child_process_security_policy_impl.cc
@@ -74,7 +74,8 @@
  public:
   SecurityState()
     : enabled_bindings_(0),
-      can_read_raw_cookies_(false) { }
+      can_read_raw_cookies_(false),
+      can_send_midi_sysex_(false) { }
 
   ~SecurityState() {
     scheme_policy_.clear();
@@ -149,6 +150,10 @@
     can_read_raw_cookies_ = false;
   }
 
+  void GrantPermissionForMIDISysEx() {
+    can_send_midi_sysex_ = true;
+  }
+
   // Determine whether permission has been granted to request |url|.
   bool CanRequestURL(const GURL& url) {
     // Having permission to a scheme implies permssion to all of its URLs.
@@ -244,6 +249,10 @@
     return can_read_raw_cookies_;
   }
 
+  bool can_send_midi_sysex() const {
+    return can_send_midi_sysex_;
+  }
+
  private:
   typedef std::map<std::string, bool> SchemeMap;
 
@@ -269,6 +278,8 @@
 
   bool can_read_raw_cookies_;
 
+  bool can_send_midi_sysex_;
+
   GURL origin_lock_;
 
   // The set of isolated filesystems the child process is permitted to access.
@@ -280,7 +291,7 @@
 ChildProcessSecurityPolicyImpl::ChildProcessSecurityPolicyImpl() {
   // We know about these schemes and believe them to be safe.
   RegisterWebSafeScheme(chrome::kHttpScheme);
-  RegisterWebSafeScheme(chrome::kHttpsScheme);
+  RegisterWebSafeScheme(kHttpsScheme);
   RegisterWebSafeScheme(chrome::kFtpScheme);
   RegisterWebSafeScheme(chrome::kDataScheme);
   RegisterWebSafeScheme("feed");
@@ -486,6 +497,16 @@
                                 kCreateFilePermissions);
 }
 
+void ChildProcessSecurityPolicyImpl::GrantSendMIDISysExMessage(int child_id) {
+  base::AutoLock lock(lock_);
+
+  SecurityStateMap::iterator state = security_state_.find(child_id);
+  if (state == security_state_.end())
+    return;
+
+  state->second->GrantPermissionForMIDISysEx();
+}
+
 void ChildProcessSecurityPolicyImpl::GrantScheme(int child_id,
                                                  const std::string& scheme) {
   base::AutoLock lock(lock_);
@@ -828,4 +849,14 @@
   file_system_policy_map_[type] = policy;
 }
 
+bool ChildProcessSecurityPolicyImpl::CanSendMIDISysExMessage(int child_id) {
+  base::AutoLock lock(lock_);
+
+  SecurityStateMap::iterator state = security_state_.find(child_id);
+  if (state == security_state_.end())
+    return false;
+
+  return state->second->can_send_midi_sysex();
+}
+
 }  // namespace content
diff --git a/content/browser/child_process_security_policy_impl.h b/content/browser/child_process_security_policy_impl.h
index 3477f1e..580552a 100644
--- a/content/browser/child_process_security_policy_impl.h
+++ b/content/browser/child_process_security_policy_impl.h
@@ -122,6 +122,9 @@
   // Revoke read raw cookies permission.
   void RevokeReadRawCookies(int child_id);
 
+  // Grants permission to send system exclusive message to any MIDI devices.
+  void GrantSendMIDISysExMessage(int child_id);
+
   // Before servicing a child process's request for a URL, the browser should
   // call this method to determine whether the process has the capability to
   // request the URL.
@@ -200,6 +203,9 @@
       fileapi::FileSystemType type,
       int policy);
 
+  // Returns true if sending system exclusive messages is allowed.
+  bool CanSendMIDISysExMessage(int child_id);
+
  private:
   friend class ChildProcessSecurityPolicyInProcessBrowserTest;
   friend class ChildProcessSecurityPolicyTest;
diff --git a/content/browser/child_process_security_policy_unittest.cc b/content/browser/child_process_security_policy_unittest.cc
index 2469648..d945fdd 100644
--- a/content/browser/child_process_security_policy_unittest.cc
+++ b/content/browser/child_process_security_policy_unittest.cc
@@ -100,7 +100,7 @@
       ChildProcessSecurityPolicyImpl::GetInstance();
 
   EXPECT_TRUE(p->IsWebSafeScheme(chrome::kHttpScheme));
-  EXPECT_TRUE(p->IsWebSafeScheme(chrome::kHttpsScheme));
+  EXPECT_TRUE(p->IsWebSafeScheme(kHttpsScheme));
   EXPECT_TRUE(p->IsWebSafeScheme(chrome::kFtpScheme));
   EXPECT_TRUE(p->IsWebSafeScheme(chrome::kDataScheme));
   EXPECT_TRUE(p->IsWebSafeScheme("feed"));
diff --git a/content/browser/device_orientation/data_fetcher_impl_android.cc b/content/browser/device_orientation/data_fetcher_impl_android.cc
index 6748d74..d2f6016 100644
--- a/content/browser/device_orientation/data_fetcher_impl_android.cc
+++ b/content/browser/device_orientation/data_fetcher_impl_android.cc
@@ -26,7 +26,9 @@
 DataFetcherImplAndroid::DataFetcherImplAndroid()
     : number_active_device_motion_sensors_(0),
       device_motion_buffer_(NULL),
-      is_buffer_ready_(false) {
+      device_orientation_buffer_(NULL),
+      is_motion_buffer_ready_(false),
+      is_orientation_buffer_ready_(false) {
   memset(received_motion_data_, 0, sizeof(received_motion_data_));
   device_orientation_.Reset(
       Java_DeviceMotionAndOrientation_getInstance(AttachCurrentThread()));
@@ -66,14 +68,32 @@
 
 void DataFetcherImplAndroid::GotOrientation(
     JNIEnv*, jobject, double alpha, double beta, double gamma) {
-  base::AutoLock autolock(next_orientation_lock_);
+  {
+    // TODO(timvolodine): remove this part once Device Orientation is
+    // completely implemented using shared memory.
+    base::AutoLock autolock(next_orientation_lock_);
 
-  Orientation* orientation = new Orientation();
-  orientation->set_alpha(alpha);
-  orientation->set_beta(beta);
-  orientation->set_gamma(gamma);
-  orientation->set_absolute(true);
-  next_orientation_ = orientation;
+    Orientation* orientation = new Orientation();
+    orientation->set_alpha(alpha);
+    orientation->set_beta(beta);
+    orientation->set_gamma(gamma);
+    orientation->set_absolute(true);
+    next_orientation_ = orientation;
+  }
+
+  if (device_orientation_buffer_) {
+    device_orientation_buffer_->seqlock.WriteBegin();
+    device_orientation_buffer_->data.alpha = alpha;
+    device_orientation_buffer_->data.hasAlpha = true;
+    device_orientation_buffer_->data.beta = beta;
+    device_orientation_buffer_->data.hasBeta = true;
+    device_orientation_buffer_->data.gamma = gamma;
+    device_orientation_buffer_->data.hasGamma = true;
+    device_orientation_buffer_->seqlock.WriteEnd();
+
+    if (!is_orientation_buffer_ready_)
+      SetOrientationBufferReadyStatus(true);
+  }
 }
 
 void DataFetcherImplAndroid::GotAcceleration(
@@ -87,9 +107,9 @@
   device_motion_buffer_->data.hasAccelerationZ = true;
   device_motion_buffer_->seqlock.WriteEnd();
 
-  if (!is_buffer_ready_) {
+  if (!is_motion_buffer_ready_) {
     received_motion_data_[RECEIVED_MOTION_DATA_ACCELERATION] = 1;
-    CheckBufferReadyToRead();
+    CheckMotionBufferReadyToRead();
   }
 }
 
@@ -104,9 +124,9 @@
   device_motion_buffer_->data.hasAccelerationIncludingGravityZ = true;
   device_motion_buffer_->seqlock.WriteEnd();
 
-  if (!is_buffer_ready_) {
+  if (!is_motion_buffer_ready_) {
     received_motion_data_[RECEIVED_MOTION_DATA_ACCELERATION_INCL_GRAVITY] = 1;
-    CheckBufferReadyToRead();
+    CheckMotionBufferReadyToRead();
   }
 }
 
@@ -121,9 +141,9 @@
   device_motion_buffer_->data.hasRotationRateGamma = true;
   device_motion_buffer_->seqlock.WriteEnd();
 
-  if (!is_buffer_ready_) {
+  if (!is_motion_buffer_ready_) {
     received_motion_data_[RECEIVED_MOTION_DATA_ROTATION_RATE] = 1;
-    CheckBufferReadyToRead();
+    CheckMotionBufferReadyToRead();
   }
 }
 
@@ -151,26 +171,32 @@
 
 // ----- Shared memory API methods
 
+// --- Device Motion
+
 bool DataFetcherImplAndroid::StartFetchingDeviceMotionData(
     DeviceMotionHardwareBuffer* buffer) {
+  DCHECK(buffer);
   device_motion_buffer_ = buffer;
-  ClearInternalBuffers();
+  ClearInternalMotionBuffers();
   bool success = Start(DeviceData::kTypeMotion);
 
   // If no motion data can ever be provided, the number of active device motion
   // sensors will be zero. In that case flag the shared memory buffer
   // as ready to read, as it will not change anyway.
   number_active_device_motion_sensors_ = GetNumberActiveDeviceMotionSensors();
-  CheckBufferReadyToRead();
+  CheckMotionBufferReadyToRead();
   return success;
 }
 
 void DataFetcherImplAndroid::StopFetchingDeviceMotionData() {
   Stop(DeviceData::kTypeMotion);
-  ClearInternalBuffers();
+  if (device_motion_buffer_) {
+    ClearInternalMotionBuffers();
+    device_motion_buffer_ = NULL;
+  }
 }
 
-void DataFetcherImplAndroid::CheckBufferReadyToRead() {
+void DataFetcherImplAndroid::CheckMotionBufferReadyToRead() {
   if (received_motion_data_[RECEIVED_MOTION_DATA_ACCELERATION] +
       received_motion_data_[RECEIVED_MOTION_DATA_ACCELERATION_INCL_GRAVITY] +
       received_motion_data_[RECEIVED_MOTION_DATA_ROTATION_RATE] ==
@@ -178,21 +204,50 @@
     device_motion_buffer_->seqlock.WriteBegin();
     device_motion_buffer_->data.interval = kPeriodInMilliseconds;
     device_motion_buffer_->seqlock.WriteEnd();
-    SetBufferReadyStatus(true);
+    SetMotionBufferReadyStatus(true);
   }
 }
 
-void DataFetcherImplAndroid::SetBufferReadyStatus(bool ready) {
+void DataFetcherImplAndroid::SetMotionBufferReadyStatus(bool ready) {
   device_motion_buffer_->seqlock.WriteBegin();
   device_motion_buffer_->data.allAvailableSensorsAreActive = ready;
   device_motion_buffer_->seqlock.WriteEnd();
-  is_buffer_ready_ = ready;
+  is_motion_buffer_ready_ = ready;
 }
 
-void DataFetcherImplAndroid::ClearInternalBuffers() {
+void DataFetcherImplAndroid::ClearInternalMotionBuffers() {
   memset(received_motion_data_, 0, sizeof(received_motion_data_));
   number_active_device_motion_sensors_ = 0;
-  SetBufferReadyStatus(false);
+  SetMotionBufferReadyStatus(false);
+}
+
+// --- Device Orientation
+
+void DataFetcherImplAndroid::SetOrientationBufferReadyStatus(bool ready) {
+  device_orientation_buffer_->seqlock.WriteBegin();
+  device_orientation_buffer_->data.allAvailableSensorsAreActive = ready;
+  device_orientation_buffer_->seqlock.WriteEnd();
+  is_orientation_buffer_ready_ = ready;
+}
+
+bool DataFetcherImplAndroid::StartFetchingDeviceOrientationData(
+    DeviceOrientationHardwareBuffer* buffer) {
+  DCHECK(buffer);
+  device_orientation_buffer_ = buffer;
+  bool success = Start(DeviceData::kTypeOrientation);
+
+  // If Start() was unsuccessful then set the buffer ready flag to true
+  // to start firing all-null events.
+  SetOrientationBufferReadyStatus(!success);
+  return success;
+}
+
+void DataFetcherImplAndroid::StopFetchingDeviceOrientationData() {
+  Stop(DeviceData::kTypeOrientation);
+  if (device_orientation_buffer_) {
+    SetOrientationBufferReadyStatus(false);
+    device_orientation_buffer_ = NULL;
+  }
 }
 
 }  // namespace content
diff --git a/content/browser/device_orientation/data_fetcher_impl_android.h b/content/browser/device_orientation/data_fetcher_impl_android.h
index 9926e7d..35ed1c7 100644
--- a/content/browser/device_orientation/data_fetcher_impl_android.h
+++ b/content/browser/device_orientation/data_fetcher_impl_android.h
@@ -11,6 +11,7 @@
 #include "content/browser/device_orientation/device_data.h"
 #include "content/common/content_export.h"
 #include "content/common/device_motion_hardware_buffer.h"
+#include "content/common/device_orientation/device_orientation_hardware_buffer.h"
 
 template<typename T> struct DefaultSingletonTraits;
 
@@ -55,6 +56,10 @@
   bool StartFetchingDeviceMotionData(DeviceMotionHardwareBuffer* buffer);
   void StopFetchingDeviceMotionData();
 
+  bool StartFetchingDeviceOrientationData(
+      DeviceOrientationHardwareBuffer* buffer);
+  void StopFetchingDeviceOrientationData();
+
  protected:
   DataFetcherImplAndroid();
   virtual ~DataFetcherImplAndroid();
@@ -66,9 +71,11 @@
 
   const Orientation* GetOrientation();
 
-  void CheckBufferReadyToRead();
-  void SetBufferReadyStatus(bool ready);
-  void ClearInternalBuffers();
+  void CheckMotionBufferReadyToRead();
+  void SetMotionBufferReadyStatus(bool ready);
+  void ClearInternalMotionBuffers();
+
+  void SetOrientationBufferReadyStatus(bool ready);
 
   enum {
     RECEIVED_MOTION_DATA_ACCELERATION = 0,
@@ -88,7 +95,9 @@
   int number_active_device_motion_sensors_;
   int received_motion_data_[RECEIVED_MOTION_DATA_MAX];
   DeviceMotionHardwareBuffer* device_motion_buffer_;
-  bool is_buffer_ready_;
+  DeviceOrientationHardwareBuffer* device_orientation_buffer_;
+  bool is_motion_buffer_ready_;
+  bool is_orientation_buffer_ready_;
 
   DISALLOW_COPY_AND_ASSIGN(DataFetcherImplAndroid);
 };
diff --git a/content/browser/device_orientation/data_fetcher_impl_android_unittest.cc b/content/browser/device_orientation/data_fetcher_impl_android_unittest.cc
index 85d5184..d12c8c6 100644
--- a/content/browser/device_orientation/data_fetcher_impl_android_unittest.cc
+++ b/content/browser/device_orientation/data_fetcher_impl_android_unittest.cc
@@ -42,10 +42,12 @@
 class AndroidDataFetcherTest : public testing::Test {
  protected:
   AndroidDataFetcherTest() {
-    buffer_.reset(new DeviceMotionHardwareBuffer);
+    motion_buffer_.reset(new DeviceMotionHardwareBuffer);
+    orientation_buffer_.reset(new DeviceOrientationHardwareBuffer);
   }
 
-  scoped_ptr<DeviceMotionHardwareBuffer> buffer_;
+  scoped_ptr<DeviceMotionHardwareBuffer> motion_buffer_;
+  scoped_ptr<DeviceOrientationHardwareBuffer> orientation_buffer_;
 };
 
 TEST_F(AndroidDataFetcherTest, ThreeDeviceMotionSensorsActive) {
@@ -53,21 +55,39 @@
   FakeDataFetcherImplAndroid fetcher;
   fetcher.SetNumberActiveDeviceMotionSensors(3);
 
-  fetcher.StartFetchingDeviceMotionData(buffer_.get());
-  ASSERT_FALSE(buffer_->data.allAvailableSensorsAreActive);
+  fetcher.StartFetchingDeviceMotionData(motion_buffer_.get());
+  ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive);
 
   fetcher.GotAcceleration(0, 0, 1, 2, 3);
-  ASSERT_FALSE(buffer_->data.allAvailableSensorsAreActive);
+  ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive);
+  ASSERT_EQ(1, motion_buffer_->data.accelerationX);
+  ASSERT_TRUE(motion_buffer_->data.hasAccelerationX);
+  ASSERT_EQ(2, motion_buffer_->data.accelerationY);
+  ASSERT_TRUE(motion_buffer_->data.hasAccelerationY);
+  ASSERT_EQ(3, motion_buffer_->data.accelerationZ);
+  ASSERT_TRUE(motion_buffer_->data.hasAccelerationZ);
 
-  fetcher.GotAccelerationIncludingGravity(0, 0, 1, 2, 3);
-  ASSERT_FALSE(buffer_->data.allAvailableSensorsAreActive);
+  fetcher.GotAccelerationIncludingGravity(0, 0, 4, 5, 6);
+  ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive);
+  ASSERT_EQ(4, motion_buffer_->data.accelerationIncludingGravityX);
+  ASSERT_TRUE(motion_buffer_->data.hasAccelerationIncludingGravityX);
+  ASSERT_EQ(5, motion_buffer_->data.accelerationIncludingGravityY);
+  ASSERT_TRUE(motion_buffer_->data.hasAccelerationIncludingGravityY);
+  ASSERT_EQ(6, motion_buffer_->data.accelerationIncludingGravityZ);
+  ASSERT_TRUE(motion_buffer_->data.hasAccelerationIncludingGravityZ);
 
-  fetcher.GotRotationRate(0, 0, 1, 2, 3);
-  ASSERT_TRUE(buffer_->data.allAvailableSensorsAreActive);
-  ASSERT_EQ(kPeriodInMilliseconds, buffer_->data.interval);
+  fetcher.GotRotationRate(0, 0, 7, 8, 9);
+  ASSERT_TRUE(motion_buffer_->data.allAvailableSensorsAreActive);
+  ASSERT_EQ(7, motion_buffer_->data.rotationRateAlpha);
+  ASSERT_TRUE(motion_buffer_->data.hasRotationRateAlpha);
+  ASSERT_EQ(8, motion_buffer_->data.rotationRateBeta);
+  ASSERT_TRUE(motion_buffer_->data.hasRotationRateBeta);
+  ASSERT_EQ(9, motion_buffer_->data.rotationRateGamma);
+  ASSERT_TRUE(motion_buffer_->data.hasRotationRateGamma);
+  ASSERT_EQ(kPeriodInMilliseconds, motion_buffer_->data.interval);
 
   fetcher.StopFetchingDeviceMotionData();
-  ASSERT_FALSE(buffer_->data.allAvailableSensorsAreActive);
+  ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive);
 }
 
 TEST_F(AndroidDataFetcherTest, TwoDeviceMotionSensorsActive) {
@@ -75,18 +95,18 @@
   FakeDataFetcherImplAndroid fetcher;
   fetcher.SetNumberActiveDeviceMotionSensors(2);
 
-  fetcher.StartFetchingDeviceMotionData(buffer_.get());
-  ASSERT_FALSE(buffer_->data.allAvailableSensorsAreActive);
+  fetcher.StartFetchingDeviceMotionData(motion_buffer_.get());
+  ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive);
 
   fetcher.GotAcceleration(0, 0, 1, 2, 3);
-  ASSERT_FALSE(buffer_->data.allAvailableSensorsAreActive);
+  ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive);
 
   fetcher.GotAccelerationIncludingGravity(0, 0, 1, 2, 3);
-  ASSERT_TRUE(buffer_->data.allAvailableSensorsAreActive);
-  ASSERT_EQ(kPeriodInMilliseconds, buffer_->data.interval);
+  ASSERT_TRUE(motion_buffer_->data.allAvailableSensorsAreActive);
+  ASSERT_EQ(kPeriodInMilliseconds, motion_buffer_->data.interval);
 
   fetcher.StopFetchingDeviceMotionData();
-  ASSERT_FALSE(buffer_->data.allAvailableSensorsAreActive);
+  ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive);
 }
 
 TEST_F(AndroidDataFetcherTest, ZeroDeviceMotionSensorsActive) {
@@ -94,14 +114,35 @@
   FakeDataFetcherImplAndroid fetcher;
   fetcher.SetNumberActiveDeviceMotionSensors(0);
 
-  fetcher.StartFetchingDeviceMotionData(buffer_.get());
-  ASSERT_TRUE(buffer_->data.allAvailableSensorsAreActive);
-  ASSERT_EQ(kPeriodInMilliseconds, buffer_->data.interval);
+  fetcher.StartFetchingDeviceMotionData(motion_buffer_.get());
+  ASSERT_TRUE(motion_buffer_->data.allAvailableSensorsAreActive);
+  ASSERT_EQ(kPeriodInMilliseconds, motion_buffer_->data.interval);
 
   fetcher.StopFetchingDeviceMotionData();
-  ASSERT_FALSE(buffer_->data.allAvailableSensorsAreActive);
+  ASSERT_FALSE(motion_buffer_->data.allAvailableSensorsAreActive);
 }
 
+TEST_F(AndroidDataFetcherTest, DeviceOrientationSensorsActive) {
+  FakeDataFetcherImplAndroid::Register(base::android::AttachCurrentThread());
+  FakeDataFetcherImplAndroid fetcher;
+
+  fetcher.StartFetchingDeviceOrientationData(orientation_buffer_.get());
+  ASSERT_FALSE(orientation_buffer_->data.allAvailableSensorsAreActive);
+
+  fetcher.GotOrientation(0, 0, 1, 2, 3);
+  ASSERT_TRUE(orientation_buffer_->data.allAvailableSensorsAreActive);
+  ASSERT_EQ(1, orientation_buffer_->data.alpha);
+  ASSERT_TRUE(orientation_buffer_->data.hasAlpha);
+  ASSERT_EQ(2, orientation_buffer_->data.beta);
+  ASSERT_TRUE(orientation_buffer_->data.hasBeta);
+  ASSERT_EQ(3, orientation_buffer_->data.gamma);
+  ASSERT_TRUE(orientation_buffer_->data.hasGamma);
+
+  fetcher.StopFetchingDeviceOrientationData();
+  ASSERT_FALSE(orientation_buffer_->data.allAvailableSensorsAreActive);
+}
+
+
 }  // namespace
 
 }  // namespace content
diff --git a/content/browser/device_orientation/data_fetcher_shared_memory.h b/content/browser/device_orientation/data_fetcher_shared_memory.h
index 9d378eb..cbc274a 100644
--- a/content/browser/device_orientation/data_fetcher_shared_memory.h
+++ b/content/browser/device_orientation/data_fetcher_shared_memory.h
@@ -7,6 +7,13 @@
 
 #include "content/browser/device_orientation/data_fetcher_shared_memory_base.h"
 
+#if defined(OS_MACOSX)
+#include "content/common/device_motion_hardware_buffer.h"
+#include "content/common/device_orientation/device_orientation_hardware_buffer.h"
+
+class SuddenMotionSensor;
+#endif
+
 namespace content {
 
 class CONTENT_EXPORT DataFetcherSharedMemory
@@ -21,6 +28,15 @@
   virtual bool Start(ConsumerType consumer_type) OVERRIDE;
   virtual bool Stop(ConsumerType consumer_type) OVERRIDE;
 
+#if defined(OS_MACOSX)
+  virtual void Fetch(unsigned consumer_bitmask) OVERRIDE;
+  virtual bool IsPolling() const OVERRIDE;
+
+  DeviceMotionHardwareBuffer* motion_buffer_;
+  DeviceOrientationHardwareBuffer* orientation_buffer_;
+  scoped_ptr<SuddenMotionSensor> sudden_motion_sensor_;
+#endif
+
   DISALLOW_COPY_AND_ASSIGN(DataFetcherSharedMemory);
 };
 
diff --git a/content/browser/device_orientation/data_fetcher_shared_memory_android.cc b/content/browser/device_orientation/data_fetcher_shared_memory_android.cc
index 8d4a9d0..27b6ec9 100644
--- a/content/browser/device_orientation/data_fetcher_shared_memory_android.cc
+++ b/content/browser/device_orientation/data_fetcher_shared_memory_android.cc
@@ -28,7 +28,12 @@
       }
       break;
     case CONSUMER_TYPE_ORIENTATION:
-      NOTIMPLEMENTED();
+      if (void* buffer = InitSharedMemoryBuffer(consumer_type,
+          sizeof(DeviceOrientationHardwareBuffer))) {
+        return DataFetcherImplAndroid::GetInstance()->
+            StartFetchingDeviceOrientationData(
+                static_cast<DeviceOrientationHardwareBuffer*>(buffer));
+      }
       break;
     default:
       NOTREACHED();
@@ -42,8 +47,9 @@
       DataFetcherImplAndroid::GetInstance()->StopFetchingDeviceMotionData();
       return true;
     case CONSUMER_TYPE_ORIENTATION:
-      NOTIMPLEMENTED();
-      break;
+      DataFetcherImplAndroid::GetInstance()->
+          StopFetchingDeviceOrientationData();
+      return true;
     default:
       NOTREACHED();
   }
diff --git a/content/browser/device_orientation/data_fetcher_shared_memory_base.cc b/content/browser/device_orientation/data_fetcher_shared_memory_base.cc
index 167b787..5b60f1f 100644
--- a/content/browser/device_orientation/data_fetcher_shared_memory_base.cc
+++ b/content/browser/device_orientation/data_fetcher_shared_memory_base.cc
@@ -21,7 +21,9 @@
   PollingThread(const char* name, DataFetcherSharedMemoryBase* fetcher);
   virtual ~PollingThread();
 
-  void SetConsumers(int consumers_bitmask);
+  void AddConsumer(ConsumerType consumer_type);
+  void RemoveConsumer(ConsumerType consumer_type);
+
   unsigned GetConsumersBitmask() const { return consumers_bitmask_; }
 
  private:
@@ -45,13 +47,13 @@
 DataFetcherSharedMemoryBase::PollingThread::~PollingThread() {
 }
 
-void DataFetcherSharedMemoryBase::PollingThread::SetConsumers(
-    int consumers_bitmask) {
-  consumers_bitmask_ = consumers_bitmask;
-  if (!consumers_bitmask_) {
-    timer_.reset(); // will also stop the timer.
+void DataFetcherSharedMemoryBase::PollingThread::AddConsumer(
+    ConsumerType consumer_type) {
+  DCHECK(fetcher_);
+  if (!fetcher_->Start(consumer_type))
     return;
-  }
+
+  consumers_bitmask_ |= consumer_type;
 
   if (!timer_)
     timer_.reset(new base::RepeatingTimer<PollingThread>());
@@ -61,6 +63,18 @@
                 this, &PollingThread::DoPoll);
 }
 
+void DataFetcherSharedMemoryBase::PollingThread::RemoveConsumer(
+    ConsumerType consumer_type) {
+  DCHECK(fetcher_);
+  if (!fetcher_->Stop(consumer_type))
+    return;
+
+  consumers_bitmask_ ^= consumer_type;
+
+  if (!consumers_bitmask_)
+    timer_.reset(); // will also stop the timer.
+}
+
 void DataFetcherSharedMemoryBase::PollingThread::DoPoll() {
   DCHECK(fetcher_);
   DCHECK(consumers_bitmask_);
@@ -90,23 +104,21 @@
   if (started_consumers_ & consumer_type)
     return true;
 
-  if (!Start(consumer_type))
-    return false;
+  if (IsPolling()) {
+    if (!InitAndStartPollingThreadIfNecessary())
+      return false;
+    polling_thread_->message_loop()->PostTask(
+        FROM_HERE,
+        base::Bind(&PollingThread::AddConsumer,
+                   base::Unretained(polling_thread_.get()),
+                   consumer_type));
+  } else {
+    if (!Start(consumer_type))
+      return false;
+  }
 
   started_consumers_ |= consumer_type;
 
-  if (IsPolling()) {
-    if (!InitAndStartPollingThreadIfNecessary()) {
-      Stop(consumer_type);
-      started_consumers_ ^= consumer_type;
-      return false;
-    }
-    polling_thread_->message_loop()->PostTask(
-        FROM_HERE,
-        base::Bind(&PollingThread::SetConsumers,
-                   base::Unretained(polling_thread_.get()),
-                   started_consumers_));
-  }
   return true;
 }
 
@@ -115,18 +127,19 @@
   if (!(started_consumers_ & consumer_type))
     return true;
 
-  if (!Stop(consumer_type))
-    return false;
-
-  started_consumers_ ^= consumer_type;
-
   if (IsPolling()) {
     polling_thread_->message_loop()->PostTask(
         FROM_HERE,
-        base::Bind(&PollingThread::SetConsumers,
+        base::Bind(&PollingThread::RemoveConsumer,
                    base::Unretained(polling_thread_.get()),
-                   started_consumers_));
+                   consumer_type));
+  } else {
+    if (!Stop(consumer_type))
+      return false;
   }
+
+  started_consumers_ ^= consumer_type;
+
   return true;
 }
 
@@ -170,12 +183,13 @@
   if (it != shared_memory_map_.end())
     return it->second;
 
-  base::SharedMemory* new_shared_mem = new base::SharedMemory;
+  scoped_ptr<base::SharedMemory> new_shared_mem(new base::SharedMemory);
   if (new_shared_mem->CreateAndMapAnonymous(buffer_size)) {
     if (void* mem = new_shared_mem->memory()) {
       memset(mem, 0, buffer_size);
-      shared_memory_map_[consumer_type] = new_shared_mem;
-      return new_shared_mem;
+      base::SharedMemory* shared_mem = new_shared_mem.release();
+      shared_memory_map_[consumer_type] = shared_mem;
+      return shared_mem;
     }
   }
   LOG(ERROR) << "Failed to initialize shared memory";
diff --git a/content/browser/device_orientation/data_fetcher_shared_memory_base.h b/content/browser/device_orientation/data_fetcher_shared_memory_base.h
index a449596..3bd4103 100644
--- a/content/browser/device_orientation/data_fetcher_shared_memory_base.h
+++ b/content/browser/device_orientation/data_fetcher_shared_memory_base.h
@@ -59,8 +59,9 @@
   // fetch the sensor data.
   virtual bool IsPolling() const;
 
-  // Start() method should call InitSharedMemoryBuffer() and cache the obtained
-  // pointer for efficienty and thread-safety.
+  // Start() method should call InitSharedMemoryBuffer() to get the shared
+  // memory pointer. If IsPolling() is true both Start() and Stop() methods
+  // are called from the |polling_thread_|.
   virtual bool Start(ConsumerType consumer_type) = 0;
   virtual bool Stop(ConsumerType consumer_type) = 0;
 
diff --git a/content/browser/device_orientation/data_fetcher_shared_memory_base_unittest.cc b/content/browser/device_orientation/data_fetcher_shared_memory_base_unittest.cc
index b580872..d9b8835 100644
--- a/content/browser/device_orientation/data_fetcher_shared_memory_base_unittest.cc
+++ b/content/browser/device_orientation/data_fetcher_shared_memory_base_unittest.cc
@@ -147,18 +147,20 @@
   virtual ~FakePollingDataFetcher() { }
 
   virtual bool Start(ConsumerType consumer_type) OVERRIDE {
+    EXPECT_TRUE(base::MessageLoop::current() == GetPollingMessageLoop());
     Init(consumer_type);
     start_.Signal();
     return true;
   }
 
   virtual bool Stop(ConsumerType consumer_type) OVERRIDE {
+    EXPECT_TRUE(base::MessageLoop::current() == GetPollingMessageLoop());
     stop_.Signal();
     return true;
   }
 
   virtual void Fetch(unsigned consumer_bitmask) OVERRIDE {
-    DCHECK(base::MessageLoop::current() == GetPollingMessageLoop());
+    EXPECT_TRUE(base::MessageLoop::current() == GetPollingMessageLoop());
 
     if (consumer_bitmask & CONSUMER_TYPE_ORIENTATION)
       UpdateOrientation();
@@ -179,65 +181,61 @@
 
 
 TEST(DataFetcherSharedMemoryBaseTest, DoesStartMotion) {
-    FakeNonPollingDataFetcher* mock_data_fetcher =
-      new FakeNonPollingDataFetcher();
-  EXPECT_FALSE(mock_data_fetcher->IsPolling());
+  FakeNonPollingDataFetcher mock_data_fetcher;
+  EXPECT_FALSE(mock_data_fetcher.IsPolling());
 
-  EXPECT_TRUE(mock_data_fetcher->StartFetchingDeviceData(CONSUMER_TYPE_MOTION));
-  mock_data_fetcher->WaitForStart();
+  EXPECT_TRUE(mock_data_fetcher.StartFetchingDeviceData(CONSUMER_TYPE_MOTION));
+  mock_data_fetcher.WaitForStart();
 
   EXPECT_EQ(kPeriodInMilliseconds,
-      mock_data_fetcher->GetMotionBuffer()->data.interval);
+      mock_data_fetcher.GetMotionBuffer()->data.interval);
 
-  mock_data_fetcher->StopFetchingDeviceData(CONSUMER_TYPE_MOTION);
-  mock_data_fetcher->WaitForStop();
+  mock_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_MOTION);
+  mock_data_fetcher.WaitForStop();
 }
 
 TEST(DataFetcherSharedMemoryBaseTest, DoesStartOrientation) {
-  FakeNonPollingDataFetcher* mock_data_fetcher =
-      new FakeNonPollingDataFetcher();
-  EXPECT_FALSE(mock_data_fetcher->IsPolling());
+  FakeNonPollingDataFetcher mock_data_fetcher;
+  EXPECT_FALSE(mock_data_fetcher.IsPolling());
 
-  EXPECT_TRUE(mock_data_fetcher->StartFetchingDeviceData(
+  EXPECT_TRUE(mock_data_fetcher.StartFetchingDeviceData(
       CONSUMER_TYPE_ORIENTATION));
-  mock_data_fetcher->WaitForStart();
+  mock_data_fetcher.WaitForStart();
 
-  EXPECT_EQ(1, mock_data_fetcher->GetOrientationBuffer()->data.alpha);
+  EXPECT_EQ(1, mock_data_fetcher.GetOrientationBuffer()->data.alpha);
 
-  mock_data_fetcher->StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION);
-  mock_data_fetcher->WaitForStop();
+  mock_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION);
+  mock_data_fetcher.WaitForStop();
 }
 
 TEST(DataFetcherSharedMemoryBaseTest, DoesPollMotion) {
-    FakePollingDataFetcher* mock_data_fetcher =
-      new FakePollingDataFetcher();
-  EXPECT_TRUE(mock_data_fetcher->IsPolling());
+  FakePollingDataFetcher mock_data_fetcher;
+  EXPECT_TRUE(mock_data_fetcher.IsPolling());
 
-  EXPECT_TRUE(mock_data_fetcher->StartFetchingDeviceData(CONSUMER_TYPE_MOTION));
-  mock_data_fetcher->WaitForStart();
-  mock_data_fetcher->WaitForUpdateMotion();
+  EXPECT_TRUE(mock_data_fetcher.StartFetchingDeviceData(CONSUMER_TYPE_MOTION));
+  mock_data_fetcher.WaitForStart();
+  mock_data_fetcher.WaitForUpdateMotion();
 
   EXPECT_EQ(kPeriodInMilliseconds,
-      mock_data_fetcher->GetMotionBuffer()->data.interval);
+      mock_data_fetcher.GetMotionBuffer()->data.interval);
 
-  mock_data_fetcher->StopFetchingDeviceData(CONSUMER_TYPE_MOTION);
-  mock_data_fetcher->WaitForStop();
+  mock_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_MOTION);
+  mock_data_fetcher.WaitForStop();
 }
 
 TEST(DataFetcherSharedMemoryBaseTest, DoesPollOrientation) {
-  FakePollingDataFetcher* mock_data_fetcher =
-      new FakePollingDataFetcher();
-  EXPECT_TRUE(mock_data_fetcher->IsPolling());
+  FakePollingDataFetcher mock_data_fetcher;
+  EXPECT_TRUE(mock_data_fetcher.IsPolling());
 
-  EXPECT_TRUE(mock_data_fetcher->StartFetchingDeviceData(
+  EXPECT_TRUE(mock_data_fetcher.StartFetchingDeviceData(
       CONSUMER_TYPE_ORIENTATION));
-  mock_data_fetcher->WaitForStart();
-  mock_data_fetcher->WaitForUpdateOrientation();
+  mock_data_fetcher.WaitForStart();
+  mock_data_fetcher.WaitForUpdateOrientation();
 
-  EXPECT_EQ(1, mock_data_fetcher->GetOrientationBuffer()->data.alpha);
+  EXPECT_EQ(1, mock_data_fetcher.GetOrientationBuffer()->data.alpha);
 
-  mock_data_fetcher->StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION);
-  mock_data_fetcher->WaitForStop();
+  mock_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION);
+  mock_data_fetcher.WaitForStop();
 }
 
 }  // namespace
diff --git a/content/browser/device_orientation/data_fetcher_shared_memory_mac.cc b/content/browser/device_orientation/data_fetcher_shared_memory_mac.cc
new file mode 100644
index 0000000..1d658f0
--- /dev/null
+++ b/content/browser/device_orientation/data_fetcher_shared_memory_mac.cc
@@ -0,0 +1,163 @@
+// 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 "data_fetcher_shared_memory.h"
+
+#include "base/logging.h"
+#include "third_party/sudden_motion_sensor/sudden_motion_sensor_mac.h"
+
+namespace {
+
+void FetchMotion(SuddenMotionSensor* sensor,
+    content::DeviceMotionHardwareBuffer* buffer) {
+  DCHECK(buffer);
+
+  float axis_value[3];
+  if (!sensor->ReadSensorValues(axis_value))
+    return;
+
+  buffer->seqlock.WriteBegin();
+  buffer->data.accelerationIncludingGravityX = axis_value[0];
+  buffer->data.hasAccelerationIncludingGravityX = true;
+  buffer->data.accelerationIncludingGravityY = axis_value[1];
+  buffer->data.hasAccelerationIncludingGravityY = true;
+  buffer->data.accelerationIncludingGravityZ = axis_value[2];
+  buffer->data.hasAccelerationIncludingGravityZ = true;
+  buffer->data.allAvailableSensorsAreActive = true;
+  buffer->seqlock.WriteEnd();
+}
+
+void FetchOrientation(SuddenMotionSensor* sensor,
+    content::DeviceOrientationHardwareBuffer* buffer) {
+  DCHECK(buffer);
+
+  // Retrieve per-axis calibrated values.
+  float axis_value[3];
+  if (!sensor->ReadSensorValues(axis_value))
+    return;
+
+  // Transform the accelerometer values to W3C draft angles.
+  //
+  // Accelerometer values are just dot products of the sensor axes
+  // by the gravity vector 'g' with the result for the z axis inverted.
+  //
+  // To understand this transformation calculate the 3rd row of the z-x-y
+  // Euler angles rotation matrix (because of the 'g' vector, only 3rd row
+  // affects to the result). Note that z-x-y matrix means R = Ry * Rx * Rz.
+  // Then, assume alpha = 0 and you get this:
+  //
+  // x_acc = sin(gamma)
+  // y_acc = - cos(gamma) * sin(beta)
+  // z_acc = cos(beta) * cos(gamma)
+  //
+  // After that the rest is just a bit of trigonometry.
+  //
+  // Also note that alpha can't be provided but it's assumed to be always zero.
+  // This is necessary in order to provide enough information to solve
+  // the equations.
+  //
+  const double kRad2deg = 180.0 / M_PI;
+  double beta = kRad2deg * atan2(-axis_value[1], axis_value[2]);
+  double gamma = kRad2deg * asin(axis_value[0]);
+
+  // TODO(aousterh): should absolute_ be set to false here?
+  // See crbug.com/136010.
+
+  // Make sure that the interval boundaries comply with the specification. At
+  // this point, beta is [-180, 180] and gamma is [-90, 90], but the spec has
+  // the upper bound open on both.
+  if (beta == 180.0)
+    beta = -180;  // -180 == 180 (upside-down)
+  if (gamma == 90.0)
+    gamma = nextafter(90, 0);
+
+  // At this point, DCHECKing is paranoia. Never hurts.
+  DCHECK_GE(beta, -180.0);
+  DCHECK_LT(beta,  180.0);
+  DCHECK_GE(gamma, -90.0);
+  DCHECK_LT(gamma,  90.0);
+
+  buffer->seqlock.WriteBegin();
+  buffer->data.beta = beta;
+  buffer->data.hasBeta = true;
+  buffer->data.gamma = gamma;
+  buffer->data.hasGamma = true;
+  buffer->data.allAvailableSensorsAreActive = true;
+  buffer->seqlock.WriteEnd();
+}
+
+}  // namespace
+
+namespace content {
+
+DataFetcherSharedMemory::DataFetcherSharedMemory() {
+}
+
+DataFetcherSharedMemory::~DataFetcherSharedMemory() {
+}
+
+void DataFetcherSharedMemory::Fetch(unsigned consumer_bitmask) {
+  DCHECK(base::MessageLoop::current() == GetPollingMessageLoop());
+  DCHECK(sudden_motion_sensor_);
+
+  if (consumer_bitmask & CONSUMER_TYPE_ORIENTATION)
+    FetchOrientation(sudden_motion_sensor_.get(), orientation_buffer_);
+  else if (consumer_bitmask & CONSUMER_TYPE_MOTION)
+    FetchMotion(sudden_motion_sensor_.get(), motion_buffer_);
+
+  NOTREACHED();
+}
+
+bool DataFetcherSharedMemory::IsPolling() const {
+  return true;
+}
+
+bool DataFetcherSharedMemory::Start(ConsumerType consumer_type) {
+  DCHECK(base::MessageLoop::current() == GetPollingMessageLoop());
+  switch (consumer_type) {
+    case CONSUMER_TYPE_MOTION:
+      if (void* buffer = InitSharedMemoryBuffer(consumer_type,
+          sizeof(DeviceMotionHardwareBuffer))) {
+        motion_buffer_ = static_cast<DeviceMotionHardwareBuffer*>(buffer);
+        if (!sudden_motion_sensor_)
+          sudden_motion_sensor_.reset(SuddenMotionSensor::Create());
+        return true;
+      }
+    case CONSUMER_TYPE_ORIENTATION:
+      if (void* buffer = InitSharedMemoryBuffer(consumer_type,
+          sizeof(DeviceOrientationHardwareBuffer))) {
+        orientation_buffer_ =
+            static_cast<DeviceOrientationHardwareBuffer*>(buffer);
+        if (!sudden_motion_sensor_)
+          sudden_motion_sensor_.reset(SuddenMotionSensor::Create());
+        return true;
+      }
+    default:
+      NOTREACHED();
+  }
+  return false;
+}
+
+bool DataFetcherSharedMemory::Stop(ConsumerType consumer_type) {
+  DCHECK(base::MessageLoop::current() == GetPollingMessageLoop());
+  switch (consumer_type) {
+    case CONSUMER_TYPE_MOTION:
+      motion_buffer_->seqlock.WriteBegin();
+      motion_buffer_->data.allAvailableSensorsAreActive = false;
+      motion_buffer_->seqlock.WriteEnd();
+      motion_buffer_ = NULL;
+      return true;
+    case CONSUMER_TYPE_ORIENTATION:
+      orientation_buffer_->seqlock.WriteBegin();
+      orientation_buffer_->data.allAvailableSensorsAreActive = false;
+      orientation_buffer_->seqlock.WriteEnd();
+      orientation_buffer_ = NULL;
+      return true;
+    default:
+      NOTREACHED();
+  }
+  return false;
+}
+
+}  // namespace content
diff --git a/content/browser/devtools/devtools_protocol_constants.cc b/content/browser/devtools/devtools_protocol_constants.cc
index b25c5c8..319777f 100644
--- a/content/browser/devtools/devtools_protocol_constants.cc
+++ b/content/browser/devtools/devtools_protocol_constants.cc
@@ -7,6 +7,11 @@
 namespace content {
 namespace devtools {
 
+const char kParamX[] = "x";
+const char kParamY[] = "y";
+const char kParamWidth[] = "width";
+const char kParamHeight[] = "height";
+
 namespace DOM {
 
 namespace setFileInputFiles {
@@ -22,8 +27,6 @@
 const char kParamModifiers[] = "modifiers";
 const char kParamTimestamp[] = "timestamp";
 const char kParamDeviceSpace[] = "deviceSpace";
-const char kParamX[] = "x";
-const char kParamY[] = "y";
 
 namespace dispatchMouseEvent {
   const char kName[] = "Input.dispatchMouseEvent";
@@ -55,6 +58,11 @@
 
 namespace Page {
 
+const char kData[] = "data";
+const char kParamDeviceScaleFactor[] = "deviceScaleFactor";
+const char kParamPageScaleFactor[] = "pageScaleFactor";
+const char kParamViewport[] = "viewport";
+
 namespace disable {
   const char kName[] = "Page.disable";
 }  // disable
@@ -75,7 +83,6 @@
   const char kParamFormat[] = "format";
   const char kParamQuality[] = "quality";
   const char kParamScale[] = "scale";
-  const char kResponseData[] = "data";
 }  // captureScreenshot
 
 namespace startScreencast {
@@ -91,7 +98,6 @@
 
 namespace screencastFrame {
   const char kName[] = "Page.screencastFrame";
-  const char kResponseData[] = "data";
 }  // screencastFrame
 
 }  // Page
diff --git a/content/browser/devtools/devtools_protocol_constants.h b/content/browser/devtools/devtools_protocol_constants.h
index 7d06add..41913c4 100644
--- a/content/browser/devtools/devtools_protocol_constants.h
+++ b/content/browser/devtools/devtools_protocol_constants.h
@@ -14,6 +14,11 @@
 namespace content {
 namespace devtools {
 
+extern const char kParamX[];
+extern const char kParamY[];
+extern const char kParamWidth[];
+extern const char kParamHeight[];
+
 namespace DOM {
 
   namespace setFileInputFiles {
@@ -29,8 +34,6 @@
   extern const char kParamModifiers[];
   extern const char kParamTimestamp[];
   extern const char kParamDeviceSpace[];
-  extern const char kParamX[];
-  extern const char kParamY[];
 
   namespace dispatchMouseEvent {
     extern const char kName[];
@@ -64,6 +67,11 @@
 
 namespace Page {
 
+  extern const char kData[];
+  extern const char kParamDeviceScaleFactor[];
+  extern const char kParamPageScaleFactor[];
+  extern const char kParamViewport[];
+
   namespace disable {
     extern const char kName[];
   }  // disable
@@ -84,7 +92,6 @@
     extern const char kParamFormat[];
     extern const char kParamQuality[];
     extern const char kParamScale[];
-    extern const char kResponseData[];
   }  // captureScreenshot
 
   namespace startScreencast {
@@ -100,7 +107,6 @@
 
   namespace screencastFrame {
     extern const char kName[];
-    extern const char kResponseData[];
   }  // screencastFrame
 
 }  // Page
diff --git a/content/browser/devtools/render_view_devtools_agent_host.cc b/content/browser/devtools/render_view_devtools_agent_host.cc
index bcde4e5..461cc36 100644
--- a/content/browser/devtools/render_view_devtools_agent_host.cc
+++ b/content/browser/devtools/render_view_devtools_agent_host.cc
@@ -307,9 +307,9 @@
 }
 
 bool RenderViewDevToolsAgentHost::OnRvhMessageReceived(
-    const IPC::Message& message) {
+    const IPC::Message& msg) {
   bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(RenderViewDevToolsAgentHost, message)
+  IPC_BEGIN_MESSAGE_MAP(RenderViewDevToolsAgentHost, msg)
     IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
                         OnDispatchOnInspectorFrontend)
     IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState,
@@ -318,14 +318,15 @@
     IPC_MESSAGE_HANDLER(DevToolsHostMsg_ClearBrowserCookies,
                         OnClearBrowserCookies)
     IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_SwapCompositorFrame,
-                                handled = false; OnSwapCompositorFrame())
+                                handled = false; OnSwapCompositorFrame(msg))
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
   return handled;
 }
 
-void RenderViewDevToolsAgentHost::OnSwapCompositorFrame() {
-  overrides_handler_->OnSwapCompositorFrame();
+void RenderViewDevToolsAgentHost::OnSwapCompositorFrame(
+    const IPC::Message& message) {
+  overrides_handler_->OnSwapCompositorFrame(message);
 }
 
 void RenderViewDevToolsAgentHost::OnSaveAgentRuntimeState(
diff --git a/content/browser/devtools/render_view_devtools_agent_host.h b/content/browser/devtools/render_view_devtools_agent_host.h
index 4732ca4..09c8424 100644
--- a/content/browser/devtools/render_view_devtools_agent_host.h
+++ b/content/browser/devtools/render_view_devtools_agent_host.h
@@ -59,7 +59,7 @@
   void RenderViewHostDestroyed(RenderViewHost* rvh);
   void RenderViewCrashed();
   bool OnRvhMessageReceived(const IPC::Message& message);
-  void OnSwapCompositorFrame();
+  void OnSwapCompositorFrame(const IPC::Message& message);
 
   void OnDispatchOnInspectorFrontend(const std::string& message);
   void OnSaveAgentRuntimeState(const std::string& state);
diff --git a/content/browser/devtools/renderer_overrides_handler.cc b/content/browser/devtools/renderer_overrides_handler.cc
index 3e9ffc3..6bebadd 100644
--- a/content/browser/devtools/renderer_overrides_handler.cc
+++ b/content/browser/devtools/renderer_overrides_handler.cc
@@ -18,6 +18,7 @@
 #include "content/browser/renderer_host/dip_util.h"
 #include "content/browser/renderer_host/render_view_host_delegate.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/common/view_messages.h"
 #include "content/port/browser/render_widget_host_view_port.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/devtools_agent_host.h"
@@ -30,6 +31,7 @@
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/common/page_transition_types.h"
 #include "content/public/common/referrer.h"
+#include "ipc/ipc_sender.h"
 #include "third_party/WebKit/public/web/WebInputEvent.h"
 #include "ui/gfx/codec/jpeg_codec.h"
 #include "ui/gfx/codec/png_codec.h"
@@ -148,10 +150,18 @@
   screencast_command_ = NULL;
 }
 
-void RendererOverridesHandler::OnSwapCompositorFrame() {
-  if (!screencast_command_)
+void RendererOverridesHandler::OnSwapCompositorFrame(
+    const IPC::Message& message) {
+  ViewHostMsg_SwapCompositorFrame::Param param;
+  if (!ViewHostMsg_SwapCompositorFrame::Read(&message, &param))
     return;
+  last_compositor_frame_metadata_ = param.b.metadata;
 
+  if (screencast_command_)
+    InnerSwapCompositorFrame();
+}
+
+void RendererOverridesHandler::InnerSwapCompositorFrame() {
   std::string format;
   int quality = kDefaultScreenshotQuality;
   double scale = 1;
@@ -170,7 +180,7 @@
       base::Bind(&RendererOverridesHandler::ScreenshotCaptured,
                  weak_factory_.GetWeakPtr(),
                  scoped_refptr<DevToolsProtocol::Command>(), format, quality,
-                 scale));
+                 last_compositor_frame_metadata_));
 }
 
 
@@ -289,7 +299,7 @@
     if (success) {
       base::DictionaryValue* result = new base::DictionaryValue();
       result->SetString(
-          devtools::Page::captureScreenshot::kResponseData, base64_data);
+          devtools::Page::kData, base64_data);
       return command->SuccessResponse(result);
     }
     return command->InternalErrorResponse("Unable to base64encode screenshot");
@@ -304,7 +314,8 @@
   view_port->CopyFromCompositingSurface(
       view_bounds, snapshot_size,
       base::Bind(&RendererOverridesHandler::ScreenshotCaptured,
-                 weak_factory_.GetWeakPtr(), command, format, quality, scale));
+                 weak_factory_.GetWeakPtr(), command, format, quality,
+                 last_compositor_frame_metadata_));
   return command->AsyncResponsePromise();
 }
 
@@ -312,7 +323,7 @@
 RendererOverridesHandler::PageStartScreencast(
     scoped_refptr<DevToolsProtocol::Command> command) {
   screencast_command_ = command;
-  OnSwapCompositorFrame();
+  InnerSwapCompositorFrame();
   return command->SuccessResponse(NULL);
 }
 
@@ -327,7 +338,7 @@
     scoped_refptr<DevToolsProtocol::Command> command,
     const std::string& format,
     int quality,
-    double scale,
+    const cc::CompositorFrameMetadata& metadata,
     bool success,
     const SkBitmap& bitmap) {
   if (!success) {
@@ -381,13 +392,27 @@
   }
 
   base::DictionaryValue* response = new base::DictionaryValue();
+  response->SetString(devtools::Page::kData, base_64_data);
+
+  // Consider metadata empty in case it has no device scale factor.
+  if (metadata.device_scale_factor != 0) {
+    response->SetDouble(devtools::Page::kParamDeviceScaleFactor,
+                        metadata.device_scale_factor);
+    response->SetDouble(devtools::Page::kParamPageScaleFactor,
+                        metadata.page_scale_factor);
+
+    base::DictionaryValue* viewport = new base::DictionaryValue();
+    viewport->SetDouble(devtools::kParamX, metadata.root_scroll_offset.x());
+    viewport->SetDouble(devtools::kParamY, metadata.root_scroll_offset.y());
+    viewport->SetDouble(devtools::kParamWidth, metadata.viewport_size.width());
+    viewport->SetDouble(devtools::kParamHeight,
+                        metadata.viewport_size.height());
+    response->Set(devtools::Page::kParamViewport, viewport);
+  }
+
   if (command) {
-    response->SetString(
-        devtools::Page::captureScreenshot::kResponseData, base_64_data);
     SendAsyncResponse(command->SuccessResponse(response));
   } else {
-    response->SetString(
-        devtools::Page::screencastFrame::kResponseData, base_64_data);
     SendNotification(devtools::Page::screencastFrame::kName, response);
   }
 }
@@ -430,8 +455,8 @@
 
   int x;
   int y;
-  if (!params->GetInteger(devtools::Input::kParamX, &x) ||
-      !params->GetInteger(devtools::Input::kParamY, &y)) {
+  if (!params->GetInteger(devtools::kParamX, &x) ||
+      !params->GetInteger(devtools::kParamY, &y)) {
     return NULL;
   }
 
@@ -514,8 +539,8 @@
 
   int x;
   int y;
-  if (!params->GetInteger(devtools::Input::kParamX, &x) ||
-      !params->GetInteger(devtools::Input::kParamY, &y)) {
+  if (!params->GetInteger(devtools::kParamX, &x) ||
+      !params->GetInteger(devtools::kParamY, &y)) {
     return NULL;
   }
   event.x = floor(x / device_scale_factor);
diff --git a/content/browser/devtools/renderer_overrides_handler.h b/content/browser/devtools/renderer_overrides_handler.h
index d369ccd..92a11ef 100644
--- a/content/browser/devtools/renderer_overrides_handler.h
+++ b/content/browser/devtools/renderer_overrides_handler.h
@@ -9,10 +9,15 @@
 #include "base/compiler_specific.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "cc/output/compositor_frame_metadata.h"
 #include "content/browser/devtools/devtools_protocol.h"
 
 class SkBitmap;
 
+namespace IPC {
+class Message;
+}
+
 namespace content {
 
 class DevToolsAgentHost;
@@ -27,9 +32,10 @@
   virtual ~RendererOverridesHandler();
 
   void OnClientDetached();
-  void OnSwapCompositorFrame();
+  void OnSwapCompositorFrame(const IPC::Message& message);
 
  private:
+  void InnerSwapCompositorFrame();
 
   // DOM domain.
   scoped_refptr<DevToolsProtocol::Response>
@@ -54,7 +60,7 @@
       scoped_refptr<DevToolsProtocol::Command> command,
       const std::string& format,
       int quality,
-      double scale,
+      const cc::CompositorFrameMetadata& metadata,
       bool success,
       const SkBitmap& bitmap);
 
@@ -67,6 +73,7 @@
   DevToolsAgentHost* agent_;
   base::WeakPtrFactory<RendererOverridesHandler> weak_factory_;
   scoped_refptr<DevToolsProtocol::Command> screencast_command_;
+  cc::CompositorFrameMetadata last_compositor_frame_metadata_;
   DISALLOW_COPY_AND_ASSIGN(RendererOverridesHandler);
 };
 
diff --git a/content/browser/devtools/worker_devtools_manager.cc b/content/browser/devtools/worker_devtools_manager.cc
index dd54f80..f08523b 100644
--- a/content/browser/devtools/worker_devtools_manager.cc
+++ b/content/browser/devtools/worker_devtools_manager.cc
@@ -32,6 +32,16 @@
       worker_route_id);
 }
 
+// Called on the UI thread.
+// static
+bool DevToolsAgentHost::HasForWorker(
+    int worker_process_id,
+    int worker_route_id) {
+  return WorkerDevToolsManager::HasDevToolsAgentHostForWorker(
+      worker_process_id,
+      worker_route_id);
+}
+
 namespace {
 
 typedef std::map<WorkerDevToolsManager::WorkerId,
@@ -220,6 +230,14 @@
   return it->second;
 }
 
+// static
+bool WorkerDevToolsManager::HasDevToolsAgentHostForWorker(
+    int worker_process_id,
+    int worker_route_id) {
+  WorkerId id(worker_process_id, worker_route_id);
+  return g_agent_map.Get().find(id) != g_agent_map.Get().end();
+}
+
 WorkerDevToolsManager::WorkerDevToolsManager() {
 }
 
diff --git a/content/browser/devtools/worker_devtools_manager.h b/content/browser/devtools/worker_devtools_manager.h
index 7ea8840..8a9a708 100644
--- a/content/browser/devtools/worker_devtools_manager.h
+++ b/content/browser/devtools/worker_devtools_manager.h
@@ -32,6 +32,11 @@
       int worker_process_id,
       int worker_route_id);
 
+  // Called on the UI thread.
+  static bool HasDevToolsAgentHostForWorker(
+      int worker_process_id,
+      int worker_route_id);
+
   void ForwardToDevToolsClient(int worker_process_id,
                                int worker_route_id,
                                const std::string& message);
diff --git a/content/browser/dom_storage/dom_storage_namespace.h b/content/browser/dom_storage/dom_storage_namespace.h
index f33042f..5860685 100644
--- a/content/browser/dom_storage/dom_storage_namespace.h
+++ b/content/browser/dom_storage/dom_storage_namespace.h
@@ -11,8 +11,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
 #include "content/common/content_export.h"
-
-class GURL;
+#include "url/gurl.h"
 
 namespace content {
 
diff --git a/content/browser/download/mhtml_generation_browsertest.cc b/content/browser/download/mhtml_generation_browsertest.cc
index 7e99fb2..6e79013 100644
--- a/content/browser/download/mhtml_generation_browsertest.cc
+++ b/content/browser/download/mhtml_generation_browsertest.cc
@@ -21,7 +21,7 @@
  public:
   MHTMLGenerationTest() : mhtml_generated_(false), file_size_(0) {}
 
-  void MHTMLGenerated(const base::FilePath& path, int64 size) {
+  void MHTMLGenerated(int64 size) {
     mhtml_generated_ = true;
     file_size_ = size;
     base::MessageLoopForUI::current()->Quit();
diff --git a/content/browser/download/mhtml_generation_manager.cc b/content/browser/download/mhtml_generation_manager.cc
index c4bc378..7d7d4bb 100644
--- a/content/browser/download/mhtml_generation_manager.cc
+++ b/content/browser/download/mhtml_generation_manager.cc
@@ -36,29 +36,12 @@
 MHTMLGenerationManager::~MHTMLGenerationManager() {
 }
 
-void MHTMLGenerationManager::GenerateMHTML(
-    WebContents* web_contents,
-    const base::FilePath& file,
-    const GenerateMHTMLCallback& callback) {
+void MHTMLGenerationManager::SaveMHTML(WebContents* web_contents,
+                                       const base::FilePath& file,
+                                       const GenerateMHTMLCallback& callback) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  static int id_counter = 0;
 
-  int job_id = id_counter++;
-  Job job;
-  job.file_path = file;
-  job.process_id = web_contents->GetRenderProcessHost()->GetID();
-  job.routing_id = web_contents->GetRenderViewHost()->GetRoutingID();
-  job.callback = callback;
-  id_to_job_[job_id] = job;
-  if (!registrar_.IsRegistered(
-          this,
-          NOTIFICATION_RENDERER_PROCESS_TERMINATED,
-          Source<RenderProcessHost>(web_contents->GetRenderProcessHost()))) {
-    registrar_.Add(
-        this,
-        NOTIFICATION_RENDERER_PROCESS_TERMINATED,
-        Source<RenderProcessHost>(web_contents->GetRenderProcessHost()));
-  }
+  int job_id = NewJob(web_contents, callback);
 
   base::ProcessHandle renderer_process =
       web_contents->GetRenderProcessHost()->GetHandle();
@@ -67,6 +50,23 @@
                  job_id, file, renderer_process));
 }
 
+void MHTMLGenerationManager::StreamMHTML(
+    WebContents* web_contents,
+    const base::PlatformFile browser_file,
+    const GenerateMHTMLCallback& callback) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  int job_id = NewJob(web_contents, callback);
+
+  base::ProcessHandle renderer_process =
+      web_contents->GetRenderProcessHost()->GetHandle();
+  IPC::PlatformFileForTransit renderer_file =
+      IPC::GetFileHandleForProcess(browser_file, renderer_process, false);
+
+  FileHandleAvailable(job_id, browser_file, renderer_file);
+}
+
+
 void MHTMLGenerationManager::MHTMLGenerated(int job_id, int64 mhtml_data_size) {
   JobFinished(job_id, mhtml_data_size);
 }
@@ -86,12 +86,17 @@
   IPC::PlatformFileForTransit renderer_file =
       IPC::GetFileHandleForProcess(browser_file, renderer_process, false);
 
-  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-      base::Bind(&MHTMLGenerationManager::FileCreated, base::Unretained(this),
-                 job_id, browser_file, renderer_file));
+  BrowserThread::PostTask(
+      BrowserThread::UI,
+      FROM_HERE,
+      base::Bind(&MHTMLGenerationManager::FileHandleAvailable,
+                 base::Unretained(this),
+                 job_id,
+                 browser_file,
+                 renderer_file));
 }
 
-void MHTMLGenerationManager::FileCreated(int job_id,
+void MHTMLGenerationManager::FileHandleAvailable(int job_id,
     base::PlatformFile browser_file,
     IPC::PlatformFileForTransit renderer_file) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -131,7 +136,7 @@
   }
 
   Job& job = iter->second;
-  job.callback.Run(job.file_path, file_size);
+  job.callback.Run(file_size);
 
   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
       base::Bind(&MHTMLGenerationManager::CloseFile, base::Unretained(this),
@@ -145,6 +150,26 @@
   base::ClosePlatformFile(file);
 }
 
+int MHTMLGenerationManager::NewJob(WebContents* web_contents,
+                                   const GenerateMHTMLCallback& callback) {
+  static int id_counter = 0;
+  int job_id = id_counter++;
+  Job& job = id_to_job_[job_id];
+  job.process_id = web_contents->GetRenderProcessHost()->GetID();
+  job.routing_id = web_contents->GetRenderViewHost()->GetRoutingID();
+  job.callback = callback;
+  if (!registrar_.IsRegistered(
+          this,
+          NOTIFICATION_RENDERER_PROCESS_TERMINATED,
+          Source<RenderProcessHost>(web_contents->GetRenderProcessHost()))) {
+    registrar_.Add(
+        this,
+        NOTIFICATION_RENDERER_PROCESS_TERMINATED,
+        Source<RenderProcessHost>(web_contents->GetRenderProcessHost()));
+  }
+  return job_id;
+}
+
 void MHTMLGenerationManager::Observe(int type,
                                      const NotificationSource& source,
                                      const NotificationDetails& details) {
diff --git a/content/browser/download/mhtml_generation_manager.h b/content/browser/download/mhtml_generation_manager.h
index 0c9ec61..5525d99 100644
--- a/content/browser/download/mhtml_generation_manager.h
+++ b/content/browser/download/mhtml_generation_manager.h
@@ -25,14 +25,20 @@
  public:
   static MHTMLGenerationManager* GetInstance();
 
-  typedef base::Callback<void(const base::FilePath& /* path to the MHTML file */,
-      int64 /* size of the file */)> GenerateMHTMLCallback;
+  typedef base::Callback<void(int64 /* size of the file */)>
+      GenerateMHTMLCallback;
 
   // Instructs the render view to generate a MHTML representation of the current
   // page for |web_contents|.
-  void GenerateMHTML(WebContents* web_contents,
-                     const base::FilePath& file,
-                     const GenerateMHTMLCallback& callback);
+  void SaveMHTML(WebContents* web_contents,
+                 const base::FilePath& file,
+                 const GenerateMHTMLCallback& callback);
+
+  // Instructs the render view to generate a MHTML representation of the current
+  // page for |web_contents|.
+  void StreamMHTML(WebContents* web_contents,
+                   const base::PlatformFile file,
+                   const GenerateMHTMLCallback& callback);
 
   // Notification from the renderer that the MHTML generation finished.
   // |mhtml_data_size| contains the size in bytes of the generated MHTML data,
@@ -46,8 +52,6 @@
     Job();
     ~Job();
 
-    base::FilePath file_path;
-
     // The handles to file the MHTML is saved to, for the browser and renderer
     // processes.
     base::PlatformFile browser_file;
@@ -73,9 +77,9 @@
   // been created.  This returns a handle to that file for the browser process
   // and one for the renderer process. These handles are
   // kInvalidPlatformFileValue if the file could not be opened.
-  void FileCreated(int job_id,
-                   base::PlatformFile browser_file,
-                   IPC::PlatformFileForTransit renderer_file);
+  void FileHandleAvailable(int job_id,
+                           base::PlatformFile browser_file,
+                           IPC::PlatformFileForTransit renderer_file);
 
   // Called on the file thread to close the file the MHTML was saved to.
   void CloseFile(base::PlatformFile file);
@@ -85,6 +89,9 @@
   // |mhtml_data_size| is -1 if the MHTML generation failed.
   void JobFinished(int job_id, int64 mhtml_data_size);
 
+  // Creates an register a new job.
+  int NewJob(WebContents* web_contents, const GenerateMHTMLCallback& callback);
+
   // Implementation of NotificationObserver.
   virtual void Observe(int type,
                        const NotificationSource& source,
diff --git a/content/browser/download/save_package.cc b/content/browser/download/save_package.cc
index 8a9781d..f38f9cd 100644
--- a/content/browser/download/save_package.cc
+++ b/content/browser/download/save_package.cc
@@ -363,7 +363,7 @@
   }
 }
 
-void SavePackage::OnMHTMLGenerated(const base::FilePath& path, int64 size) {
+void SavePackage::OnMHTMLGenerated(int64 size) {
   if (size <= 0) {
     Cancel(false);
     return;
@@ -1209,8 +1209,7 @@
     bool can_save_as_complete,
     const std::string& contents_mime_type,
     const std::string& accept_langs) {
-  base::FilePath name_with_proper_ext =
-      base::FilePath::FromWStringHack(UTF16ToWideHack(title_));
+  base::FilePath name_with_proper_ext = base::FilePath::FromUTF16Unsafe(title_);
 
   // If the page's title matches its URL, use the URL. Try to use the last path
   // component or if there is none, the domain as the file name.
@@ -1238,8 +1237,7 @@
     } else {
       url_path = "dataurl";
     }
-    name_with_proper_ext =
-        base::FilePath::FromWStringHack(UTF8ToWide(url_path));
+    name_with_proper_ext = base::FilePath::FromUTF8Unsafe(url_path);
   }
 
   // Ask user for getting final saving name.
diff --git a/content/browser/download/save_package.h b/content/browser/download/save_package.h
index b53280d..939002e 100644
--- a/content/browser/download/save_package.h
+++ b/content/browser/download/save_package.h
@@ -129,7 +129,7 @@
       DownloadItemImpl* item);
 
   // Callback for WebContents::GenerateMHTML().
-  void OnMHTMLGenerated(const base::FilePath& path, int64 size);
+  void OnMHTMLGenerated(int64 size);
 
   // For testing only.
   SavePackage(WebContents* web_contents,
diff --git a/content/browser/fileapi/fileapi_message_filter.cc b/content/browser/fileapi/fileapi_message_filter.cc
index 66db0d7..b4b5aef 100644
--- a/content/browser/fileapi/fileapi_message_filter.cc
+++ b/content/browser/fileapi/fileapi_message_filter.cc
@@ -195,6 +195,7 @@
     IPC_MESSAGE_HANDLER(StreamHostMsg_SyncAppendSharedMemory,
                         OnAppendSharedMemoryToStream)
     IPC_MESSAGE_HANDLER(StreamHostMsg_FinishBuilding, OnFinishBuildingStream)
+    IPC_MESSAGE_HANDLER(StreamHostMsg_AbortBuilding, OnAbortBuildingStream)
     IPC_MESSAGE_HANDLER(StreamHostMsg_Clone, OnCloneStream)
     IPC_MESSAGE_HANDLER(StreamHostMsg_Remove, OnRemoveStream)
     IPC_MESSAGE_UNHANDLED(handled = false)
@@ -676,6 +677,13 @@
     stream->Finalize();
 }
 
+void FileAPIMessageFilter::OnAbortBuildingStream(const GURL& url) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  scoped_refptr<Stream> stream(GetStreamForURL(url));
+  if (stream.get())
+    stream->Abort();
+}
+
 void FileAPIMessageFilter::OnCloneStream(
     const GURL& url, const GURL& src_url) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
diff --git a/content/browser/fileapi/fileapi_message_filter.h b/content/browser/fileapi/fileapi_message_filter.h
index e3b64a1..873d010 100644
--- a/content/browser/fileapi/fileapi_message_filter.h
+++ b/content/browser/fileapi/fileapi_message_filter.h
@@ -154,6 +154,7 @@
   void OnAppendSharedMemoryToStream(
       const GURL& url, base::SharedMemoryHandle handle, size_t buffer_size);
   void OnFinishBuildingStream(const GURL& url);
+  void OnAbortBuildingStream(const GURL& url);
   void OnCloneStream(const GURL& url, const GURL& src_url);
   void OnRemoveStream(const GURL& url);
 
diff --git a/content/browser/geolocation/device_data_provider.cc b/content/browser/geolocation/device_data_provider.cc
deleted file mode 100644
index 00c585b..0000000
--- a/content/browser/geolocation/device_data_provider.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/geolocation/device_data_provider.h"
-
-namespace content {
-
-// statics
-template<> DeviceDataProvider<WifiData>*
-    DeviceDataProvider<WifiData>::instance_ = NULL;
-template<> DeviceDataProvider<WifiData>::ImplFactoryFunction
-    DeviceDataProvider<WifiData>::factory_function_ = DefaultFactoryFunction;
-
-AccessPointData::AccessPointData()
-    : radio_signal_strength(kint32min),
-      channel(kint32min),
-      signal_to_noise(kint32min) {
-}
-
-AccessPointData::~AccessPointData() {}
-
-WifiData::WifiData() {}
-
-WifiData::~WifiData() {}
-
-bool WifiData::DiffersSignificantly(const WifiData& other) const {
-  // More than 4 or 50% of access points added or removed is significant.
-  static const size_t kMinChangedAccessPoints = 4;
-  const size_t min_ap_count =
-      std::min(access_point_data.size(), other.access_point_data.size());
-  const size_t max_ap_count =
-      std::max(access_point_data.size(), other.access_point_data.size());
-  const size_t difference_threadhold = std::min(kMinChangedAccessPoints,
-                                                min_ap_count / 2);
-  if (max_ap_count > min_ap_count + difference_threadhold)
-    return true;
-  // Compute size of interesction of old and new sets.
-  size_t num_common = 0;
-  for (AccessPointDataSet::const_iterator iter = access_point_data.begin();
-       iter != access_point_data.end();
-       iter++) {
-    if (other.access_point_data.find(*iter) !=
-        other.access_point_data.end()) {
-      ++num_common;
-    }
-  }
-  DCHECK(num_common <= min_ap_count);
-
-  // Test how many have changed.
-  return max_ap_count > num_common + difference_threadhold;
-}
-
-}  // namespace content
diff --git a/content/browser/geolocation/device_data_provider.h b/content/browser/geolocation/device_data_provider.h
deleted file mode 100644
index 3c69fe8..0000000
--- a/content/browser/geolocation/device_data_provider.h
+++ /dev/null
@@ -1,297 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// A device data provider provides data from the device that is used by a
-// NetworkLocationProvider to obtain a position fix. This data may be either
-// wifi data or (currently not used) cell radio data. For a given type of data,
-// we use a singleton instance of the device data provider, which is used by
-// multiple NetworkLocationProvider objects.
-//
-// This file provides DeviceDataProvider, which provides static methods to
-// access the singleton instance. The singleton instance uses a private
-// implementation to abstract across platforms and also to allow mock providers
-// to be used for testing.
-//
-// This file also provides DeviceDataProviderImplBase, a base class which
-// provides common functionality for the private implementations.
-//
-// This file also declares the data structure used to represent wifi data.
-
-#ifndef CONTENT_BROWSER_GEOLOCATION_DEVICE_DATA_PROVIDER_H_
-#define CONTENT_BROWSER_GEOLOCATION_DEVICE_DATA_PROVIDER_H_
-
-#include <set>
-
-#include "base/basictypes.h"
-#include "base/bind.h"
-#include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop.h"
-#include "base/strings/string16.h"
-#include "base/strings/string_util.h"
-#include "content/common/content_export.h"
-
-namespace content {
-
-// Wifi data relating to a single access point.
-struct CONTENT_EXPORT AccessPointData {
-  AccessPointData();
-  ~AccessPointData();
-
-  // MAC address, formatted as per MacAddressAsString16.
-  string16 mac_address;
-  int radio_signal_strength;  // Measured in dBm
-  int channel;
-  int signal_to_noise;  // Ratio in dB
-  string16 ssid;   // Network identifier
-};
-
-// This is to allow AccessPointData to be used in std::set. We order
-// lexicographically by MAC address.
-struct AccessPointDataLess {
-  bool operator()(const AccessPointData& data1,
-                  const AccessPointData& data2) const {
-    return data1.mac_address < data2.mac_address;
-  }
-};
-
-// All data for wifi.
-struct CONTENT_EXPORT WifiData {
-  WifiData();
-  ~WifiData();
-
-  // Determines whether a new set of WiFi data differs significantly from this.
-  bool DiffersSignificantly(const WifiData& other) const;
-
-  // Store access points as a set, sorted by MAC address. This allows quick
-  // comparison of sets for detecting changes and for caching.
-  typedef std::set<AccessPointData, AccessPointDataLess> AccessPointDataSet;
-  AccessPointDataSet access_point_data;
-};
-
-template<typename DataType>
-class DeviceDataProvider;
-
-// This class just exists to work-around MSVC2005 not being able to have a
-// template class implement RefCountedThreadSafe
-class CONTENT_EXPORT DeviceDataProviderImplBaseHack
-    : public base::RefCountedThreadSafe<DeviceDataProviderImplBaseHack> {
- protected:
-  friend class base::RefCountedThreadSafe<DeviceDataProviderImplBaseHack>;
-  virtual ~DeviceDataProviderImplBaseHack() {}
-};
-
-// See class DeviceDataProvider for the public client API.
-// DeviceDataProvider uses containment to hide platform-specific implementation
-// details from common code. This class provides common functionality for these
-// contained implementation classes. This is a modified pimpl pattern: this
-// class needs to be in the public header due to use of templating.
-template<typename DataType>
-class DeviceDataProviderImplBase : public DeviceDataProviderImplBaseHack {
- public:
-  DeviceDataProviderImplBase()
-      : container_(NULL), client_loop_(base::MessageLoop::current()) {
-    DCHECK(client_loop_);
-  }
-
-  // Tells the provider to start looking for data. Listeners will start
-  // receiving notifications after this call.
-  virtual void StartDataProvider() = 0;
-  // Tells the provider to stop looking for data. Listeners will stop
-  // receiving notifications after this call.
-  virtual void StopDataProvider() = 0;
-  virtual bool GetData(DataType* data) = 0;
-
-  // Sets the container of this class, which is of type DeviceDataProvider.
-  // This is required to pass as a parameter when making the callback to
-  // listeners.
-  void SetContainer(DeviceDataProvider<DataType>* container) {
-    container_ = container;
-  }
-
-  typedef typename DeviceDataProvider<DataType>::ListenerInterface
-          ListenerInterface;
-  void AddListener(ListenerInterface* listener) {
-    listeners_.insert(listener);
-  }
-  bool RemoveListener(ListenerInterface* listener) {
-    return listeners_.erase(listener) == 1;
-  }
-
-  bool has_listeners() const {
-    return !listeners_.empty();
-  }
-
- protected:
-  virtual ~DeviceDataProviderImplBase() {}
-
-  // Calls DeviceDataUpdateAvailable() on all registered listeners.
-  typedef std::set<ListenerInterface*> ListenersSet;
-  void NotifyListeners() {
-    // Always make the notify callback via a posted task, so we can unwind
-    // callstack here and make callback without causing client re-entrancy.
-    client_loop_->PostTask(FROM_HERE, base::Bind(
-        &DeviceDataProviderImplBase<DataType>::DoNotifyListeners,
-        this));
-  }
-
-  bool CalledOnClientThread() const {
-    return base::MessageLoop::current() == this->client_loop_;
-  }
-
-  base::MessageLoop* client_loop() const { return client_loop_; }
-
- private:
-  void DoNotifyListeners() {
-    // It's possible that all the listeners (and the container) went away
-    // whilst this task was pending. This is fine; the loop will be a no-op.
-    typename ListenersSet::const_iterator iter = listeners_.begin();
-    while (iter != listeners_.end()) {
-      ListenerInterface* listener = *iter;
-      ++iter;  // Advance iter before callback, in case listener unregisters.
-      listener->DeviceDataUpdateAvailable(container_);
-    }
-  }
-
-  DeviceDataProvider<DataType>* container_;
-
-  // Reference to the client's message loop, all callbacks and access to
-  // the listeners_ member should happen in this context.
-  base::MessageLoop* client_loop_;
-
-  ListenersSet listeners_;
-
-  DISALLOW_COPY_AND_ASSIGN(DeviceDataProviderImplBase);
-};
-
-typedef DeviceDataProviderImplBase<WifiData> WifiDataProviderImplBase;
-
-// A device data provider
-//
-// We use a singleton instance of this class which is shared by multiple network
-// location providers. These location providers access the instance through the
-// Register and Unregister methods.
-template<typename DataType>
-class DeviceDataProvider {
- public:
-  // Interface to be implemented by listeners to a device data provider.
-  class ListenerInterface {
-   public:
-    // Will be called in the context of the thread that called Register().
-    virtual void DeviceDataUpdateAvailable(
-        DeviceDataProvider<DataType>* provider) = 0;
-    virtual ~ListenerInterface() {}
-  };
-
-  // Sets the factory function which will be used by Register to create the
-  // implementation used by the singleton instance. This factory approach is
-  // used both to abstract accross platform-specific implementations and to
-  // inject mock implementations for testing.
-  typedef DeviceDataProviderImplBase<DataType>* (*ImplFactoryFunction)(void);
-  static void SetFactory(ImplFactoryFunction factory_function_in) {
-    factory_function_ = factory_function_in;
-  }
-
-  static void ResetFactory() {
-    factory_function_ = DefaultFactoryFunction;
-  }
-
-  // Adds a listener, which will be called back with DeviceDataUpdateAvailable
-  // whenever new data is available. Returns the singleton instance.
-  static DeviceDataProvider* Register(ListenerInterface* listener) {
-    bool need_to_start_provider = false;
-    if (!instance_) {
-      instance_ = new DeviceDataProvider();
-      need_to_start_provider = true;
-    }
-    DCHECK(instance_);
-    instance_->AddListener(listener);
-    // Start the provider after adding the listener, to avoid any race in
-    // it receiving an early callback.
-    if (need_to_start_provider)
-      instance_->StartDataProvider();
-    return instance_;
-  }
-
-  // Removes a listener. If this is the last listener, deletes the singleton
-  // instance. Return value indicates success.
-  static bool Unregister(ListenerInterface* listener) {
-    DCHECK(instance_);
-    DCHECK(instance_->has_listeners());
-    if (!instance_->RemoveListener(listener)) {
-      return false;
-    }
-    if (!instance_->has_listeners()) {
-      // Must stop the provider (and any implementation threads) before
-      // destroying to avoid any race conditions in access to the provider in
-      // the destructor chain.
-      instance_->StopDataProvider();
-      delete instance_;
-      instance_ = NULL;
-    }
-    return true;
-  }
-
-  // Provides whatever data the provider has, which may be nothing. Return
-  // value indicates whether this is all the data the provider could ever
-  // obtain.
-  bool GetData(DataType* data) {
-    return impl_->GetData(data);
-  }
-
- private:
-  // Private constructor and destructor, callers access singleton through
-  // Register and Unregister.
-  DeviceDataProvider() {
-    DCHECK(factory_function_);
-    impl_ = (*factory_function_)();
-    DCHECK(impl_.get());
-    impl_->SetContainer(this);
-  }
-  virtual ~DeviceDataProvider() {
-    DCHECK(impl_.get());
-    impl_->SetContainer(NULL);
-  }
-
-  void AddListener(ListenerInterface* listener) {
-    impl_->AddListener(listener);
-  }
-
-  bool RemoveListener(ListenerInterface* listener) {
-    return impl_->RemoveListener(listener);
-  }
-
-  bool has_listeners() const {
-    return impl_->has_listeners();
-  }
-
-  void StartDataProvider() {
-    impl_->StartDataProvider();
-  }
-
-  void StopDataProvider() {
-    impl_->StopDataProvider();
-  }
-
-  CONTENT_EXPORT static DeviceDataProviderImplBase<DataType>*
-      DefaultFactoryFunction();
-
-  // The singleton-like instance of this class. (Not 'true' singleton, as it
-  // may go through multiple create/destroy/create cycles per process instance,
-  // e.g. when under test).
-  CONTENT_EXPORT static DeviceDataProvider* instance_;
-
-  // The factory function used to create the singleton instance.
-  CONTENT_EXPORT static ImplFactoryFunction factory_function_;
-
-  // The internal implementation.
-  scoped_refptr<DeviceDataProviderImplBase<DataType> > impl_;
-
-  DISALLOW_COPY_AND_ASSIGN(DeviceDataProvider);
-};
-
-typedef DeviceDataProvider<WifiData> WifiDataProvider;
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_GEOLOCATION_DEVICE_DATA_PROVIDER_H_
diff --git a/content/browser/geolocation/device_data_provider_unittest.cc b/content/browser/geolocation/device_data_provider_unittest.cc
deleted file mode 100644
index 42a678c..0000000
--- a/content/browser/geolocation/device_data_provider_unittest.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/threading/platform_thread.h"
-#include "content/browser/geolocation/device_data_provider.h"
-#include "content/browser/geolocation/wifi_data_provider_common.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace content {
-
-class NullWifiDataListenerInterface
-    : public WifiDataProviderCommon::ListenerInterface {
- public:
-  // ListenerInterface
-  virtual void DeviceDataUpdateAvailable(
-      DeviceDataProvider<WifiData>* provider) OVERRIDE {}
-};
-
-TEST(GeolocationDeviceDataProviderWifiData, CreateDestroy) {
-  // See http://crbug.com/59913 .  The main_message_loop is not required to be
-  // run for correct behaviour, but we run it in this test to help smoke out
-  // any race conditions between processing in the main loop and the setup /
-  // tear down of the DeviceDataProvider thread.
-  base::MessageLoopForUI main_message_loop;
-  NullWifiDataListenerInterface listener;
-  for (int i = 0; i < 10; i++) {
-    DeviceDataProvider<WifiData>::Register(&listener);
-    for (int j = 0; j < 10; j++) {
-      base::PlatformThread::Sleep(base::TimeDelta());
-      main_message_loop.RunUntilIdle();  // See comment above
-    }
-    DeviceDataProvider<WifiData>::Unregister(&listener);
-    for (int j = 0; j < 10; j++) {
-      base::PlatformThread::Sleep(base::TimeDelta());
-      main_message_loop.RunUntilIdle();  // See comment above
-    }
-  }
-}
-
-}  // namespace content
diff --git a/content/browser/geolocation/empty_device_data_provider.cc b/content/browser/geolocation/empty_device_data_provider.cc
deleted file mode 100644
index 57e25b0..0000000
--- a/content/browser/geolocation/empty_device_data_provider.cc
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/geolocation/empty_device_data_provider.h"
-
-namespace content {
-
-// Only define for platforms that lack a real wifi data provider.
-#if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_LINUX)
-// static
-template<>
-WifiDataProviderImplBase* WifiDataProvider::DefaultFactoryFunction() {
-  return new EmptyDeviceDataProvider<WifiData>();
-}
-#endif
-
-}  // namespace content
diff --git a/content/browser/geolocation/empty_device_data_provider.h b/content/browser/geolocation/empty_device_data_provider.h
deleted file mode 100644
index eb526d9..0000000
--- a/content/browser/geolocation/empty_device_data_provider.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_GEOLOCATION_EMPTY_DEVICE_DATA_PROVIDER_H_
-#define CONTENT_BROWSER_GEOLOCATION_EMPTY_DEVICE_DATA_PROVIDER_H_
-
-#include "content/browser/geolocation/device_data_provider.h"
-
-namespace content {
-
-// An implementation of DeviceDataProviderImplBase that does not provide any
-// data. Used on platforms where a given data type is not available.
-
-template<typename DataType>
-class EmptyDeviceDataProvider : public DeviceDataProviderImplBase<DataType> {
- public:
-  EmptyDeviceDataProvider() {}
-  virtual ~EmptyDeviceDataProvider() {}
-
-  // DeviceDataProviderImplBase implementation
-  virtual void StartDataProvider() { }
-  virtual void StopDataProvider() { }
-  virtual bool GetData(DataType *data) {
-    DCHECK(data);
-    // This is all the data we can get - nothing.
-    return true;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(EmptyDeviceDataProvider);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_GEOLOCATION_EMPTY_DEVICE_DATA_PROVIDER_H_
diff --git a/content/browser/geolocation/empty_wifi_data_provider.cc b/content/browser/geolocation/empty_wifi_data_provider.cc
new file mode 100644
index 0000000..7255f46
--- /dev/null
+++ b/content/browser/geolocation/empty_wifi_data_provider.cc
@@ -0,0 +1,29 @@
+// 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 "content/browser/geolocation/empty_wifi_data_provider.h"
+
+namespace content {
+
+EmptyWifiDataProvider::EmptyWifiDataProvider() {
+}
+
+EmptyWifiDataProvider::~EmptyWifiDataProvider() {
+}
+
+bool EmptyWifiDataProvider::GetData(WifiData* data) {
+  DCHECK(data);
+  // This is all the data we can get - nothing.
+  return true;
+}
+
+// Only define for platforms that lack a real wifi data provider.
+#if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_LINUX)
+// static
+WifiDataProviderImplBase* WifiDataProvider::DefaultFactoryFunction() {
+  return new EmptyWifiDataProvider();
+}
+#endif
+
+}  // namespace content
diff --git a/content/browser/geolocation/empty_wifi_data_provider.h b/content/browser/geolocation/empty_wifi_data_provider.h
new file mode 100644
index 0000000..a02ed2b
--- /dev/null
+++ b/content/browser/geolocation/empty_wifi_data_provider.h
@@ -0,0 +1,31 @@
+// 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 CONTENT_BROWSER_GEOLOCATION_EMPTY_WIFI_DATA_PROVIDER_H_
+#define CONTENT_BROWSER_GEOLOCATION_EMPTY_WIFI_DATA_PROVIDER_H_
+
+#include "content/browser/geolocation/wifi_data_provider.h"
+
+namespace content {
+
+// An implementation of WifiDataProviderImplBase that does not provide any
+// data. Used on platforms where a real implementation is not available.
+class EmptyWifiDataProvider : public WifiDataProviderImplBase {
+ public:
+  EmptyWifiDataProvider();
+
+  // WifiDataProviderImplBase implementation
+  virtual void StartDataProvider() OVERRIDE { }
+  virtual void StopDataProvider() OVERRIDE { }
+  virtual bool GetData(WifiData* data) OVERRIDE;
+
+ private:
+  virtual ~EmptyWifiDataProvider();
+
+  DISALLOW_COPY_AND_ASSIGN(EmptyWifiDataProvider);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_GEOLOCATION_EMPTY_WIFI_DATA_PROVIDER_H_
diff --git a/content/browser/geolocation/network_location_provider.cc b/content/browser/geolocation/network_location_provider.cc
index 0ea890c..678edb2 100644
--- a/content/browser/geolocation/network_location_provider.cc
+++ b/content/browser/geolocation/network_location_provider.cc
@@ -11,7 +11,7 @@
 
 namespace content {
 namespace {
-// The maximum period of time we'll wait for a complete set of device data
+// The maximum period of time we'll wait for a complete set of wifi data
 // before sending the request.
 const int kDataCompleteWaitSeconds = 2;
 }  // namespace
@@ -26,7 +26,7 @@
 bool NetworkLocationProvider::PositionCache::CachePosition(
     const WifiData& wifi_data,
     const Geoposition& position) {
-  // Check that we can generate a valid key for the device data.
+  // Check that we can generate a valid key for the wifi data.
   string16 key;
   if (!MakeKey(wifi_data, &key)) {
     return false;
@@ -53,8 +53,8 @@
   return true;
 }
 
-// Searches for a cached position response for the current set of cell ID and
-// WiFi data. Returns the cached position if available, NULL otherwise.
+// Searches for a cached position response for the current WiFi data. Returns
+// the cached position if available, NULL otherwise.
 const Geoposition* NetworkLocationProvider::PositionCache::FindPosition(
     const WifiData& wifi_data) {
   string16 key;
@@ -65,15 +65,14 @@
   return iter == cache_.end() ? NULL : &iter->second;
 }
 
-// Makes the key for the map of cached positions, using a set of
-// device data. Returns true if a good key was generated, false otherwise.
+// Makes the key for the map of cached positions, using the available data.
+// Returns true if a good key was generated, false otherwise.
 //
 // static
 bool NetworkLocationProvider::PositionCache::MakeKey(
     const WifiData& wifi_data,
     string16* key) {
-  // Currently we use only the WiFi data, and base the key only on
-  // the MAC addresses.
+  // Currently we use only WiFi data and base the key only on the MAC addresses.
   DCHECK(key);
   key->clear();
   const size_t kCharsPerMacAddress = 6 * 3 + 1;  // e.g. "11:22:33:44:55:66|"
@@ -88,7 +87,7 @@
     *key += separator;
   }
   // If the key is the empty string, return false, as we don't want to cache a
-  // position for such a set of device data.
+  // position for such data.
   return !key->empty();
 }
 
@@ -110,6 +109,9 @@
     const string16& access_token)
     : access_token_store_(access_token_store),
       wifi_data_provider_(NULL),
+      wifi_data_update_callback_(
+          base::Bind(&NetworkLocationProvider::WifiDataUpdateAvailable,
+                     base::Unretained(this))),
       is_wifi_data_complete_(false),
       access_token_(access_token),
       is_permission_granted_(false),
@@ -129,7 +131,7 @@
 }
 
 // LocationProvider implementation
-void NetworkLocationProvider::GetPosition(Geoposition *position) {
+void NetworkLocationProvider::GetPosition(Geoposition* position) {
   DCHECK(position);
   *position = position_;
 }
@@ -151,12 +153,11 @@
   }
 }
 
-// DeviceDataProviderInterface::ListenerInterface implementation.
-void NetworkLocationProvider::DeviceDataUpdateAvailable(
+void NetworkLocationProvider::WifiDataUpdateAvailable(
     WifiDataProvider* provider) {
   DCHECK(provider == wifi_data_provider_);
   is_wifi_data_complete_ = wifi_data_provider_->GetData(&wifi_data_);
-  OnDeviceDataUpdated();
+  OnWifiDataUpdated();
 }
 
 void NetworkLocationProvider::LocationResponseAvailable(
@@ -192,26 +193,35 @@
     return false;
   }
 
-  // Get the device data providers. The first call to Register will create the
-  // provider and it will be deleted by ref counting.
-  wifi_data_provider_ = WifiDataProvider::Register(this);
+  // Registers a callback with the data provider. The first call to Register
+  // will create a singleton data provider and it will be deleted when the last
+  // callback is removed with Unregister.
+  wifi_data_provider_ = WifiDataProvider::Register(&wifi_data_update_callback_);
 
   base::MessageLoop::current()->PostDelayedTask(
       FROM_HERE,
       base::Bind(&NetworkLocationProvider::RequestPosition,
                  weak_factory_.GetWeakPtr()),
       base::TimeDelta::FromSeconds(kDataCompleteWaitSeconds));
-  // Get the device data.
+  // Get the wifi data.
   is_wifi_data_complete_ = wifi_data_provider_->GetData(&wifi_data_);
   if (is_wifi_data_complete_)
-    OnDeviceDataUpdated();
+    OnWifiDataUpdated();
   return true;
 }
 
+void NetworkLocationProvider::OnWifiDataUpdated() {
+  DCHECK(CalledOnValidThread());
+  wifi_data_updated_timestamp_ = base::Time::Now();
+
+  is_new_data_available_ = is_wifi_data_complete_;
+  RequestRefresh();
+}
+
 void NetworkLocationProvider::StopProvider() {
   DCHECK(CalledOnValidThread());
   if (IsStarted()) {
-    wifi_data_provider_->Unregister(this);
+    wifi_data_provider_->Unregister(&wifi_data_update_callback_);
   }
   wifi_data_provider_ = NULL;
   weak_factory_.InvalidateWeakPtrs();
@@ -225,7 +235,7 @@
 
   const Geoposition* cached_position =
       position_cache_->FindPosition(wifi_data_);
-  DCHECK(!device_data_updated_timestamp_.is_null()) <<
+  DCHECK(!wifi_data_updated_timestamp_.is_null()) <<
       "Timestamp must be set before looking up position";
   if (cached_position) {
     DCHECK(cached_position->Validate());
@@ -234,7 +244,7 @@
     // The timestamp of a position fix is determined by the timestamp
     // of the source data update. (The value of position_.timestamp from
     // the cache could be from weeks ago!)
-    position_.timestamp = device_data_updated_timestamp_;
+    position_.timestamp = wifi_data_updated_timestamp_;
     is_new_data_available_ = false;
     // Let listeners know that we now have a position available.
     NotifyCallback(position_);
@@ -255,15 +265,7 @@
              << wifi_data_.access_point_data.size();
   }
   request_->MakeRequest(access_token_, wifi_data_,
-                        device_data_updated_timestamp_);
-}
-
-void NetworkLocationProvider::OnDeviceDataUpdated() {
-  DCHECK(CalledOnValidThread());
-  device_data_updated_timestamp_ = base::Time::Now();
-
-  is_new_data_available_ = is_wifi_data_complete_;
-  RequestRefresh();
+                        wifi_data_updated_timestamp_);
 }
 
 bool NetworkLocationProvider::IsStarted() const {
diff --git a/content/browser/geolocation/network_location_provider.h b/content/browser/geolocation/network_location_provider.h
index 84ecfd9..d5f3e99 100644
--- a/content/browser/geolocation/network_location_provider.h
+++ b/content/browser/geolocation/network_location_provider.h
@@ -15,9 +15,9 @@
 #include "base/strings/string16.h"
 #include "base/threading/non_thread_safe.h"
 #include "base/threading/thread.h"
-#include "content/browser/geolocation/device_data_provider.h"
 #include "content/browser/geolocation/location_provider_base.h"
 #include "content/browser/geolocation/network_location_request.h"
+#include "content/browser/geolocation/wifi_data_provider.h"
 #include "content/common/content_export.h"
 #include "content/public/common/geoposition.h"
 
@@ -27,14 +27,12 @@
 
 class NetworkLocationProvider
     : public base::NonThreadSafe,
-      public LocationProviderBase,
-      public WifiDataProvider::ListenerInterface {
+      public LocationProviderBase {
  public:
   // Cache of recently resolved locations. Public for tests.
   class CONTENT_EXPORT PositionCache {
    public:
-    // The maximum size of the cache of positions for previously requested
-    // device data.
+    // The maximum size of the cache of positions.
     static const size_t kMaximumSize;
 
     PositionCache();
@@ -47,19 +45,19 @@
     bool CachePosition(const WifiData& wifi_data,
                        const Geoposition& position);
 
-    // Searches for a cached position response for the current set of device
-    // data. Returns NULL if the position is not in the cache, or the cached
+    // Searches for a cached position response for the current set of data.
+    // Returns NULL if the position is not in the cache, or the cached
     // position if available. Ownership remains with the cache.
     const Geoposition* FindPosition(const WifiData& wifi_data);
 
    private:
     // Makes the key for the map of cached positions, using a set of
-    // device data. Returns true if a good key was generated, false otherwise.
+    // data. Returns true if a good key was generated, false otherwise.
     static bool MakeKey(const WifiData& wifi_data,
                         string16* key);
 
     // The cache of positions. This is stored as a map keyed on a string that
-    // represents a set of device data, and a list to provide
+    // represents a set of data, and a list to provide
     // least-recently-added eviction.
     typedef std::map<string16, Geoposition> CacheMap;
     CacheMap cache_;
@@ -84,14 +82,14 @@
   // Satisfies a position request from cache or network.
   void RequestPosition();
 
-  // Internal helper used by DeviceDataUpdateAvailable
-  void OnDeviceDataUpdated();
+  // Called from a callback when new wifi data is available.
+  void WifiDataUpdateAvailable(WifiDataProvider* provider);
+
+  // Internal helper used by WifiDataUpdateAvailable.
+  void OnWifiDataUpdated();
 
   bool IsStarted() const;
 
-  // DeviceDataProvider::ListenerInterface implementation.
-  virtual void DeviceDataUpdateAvailable(WifiDataProvider* provider) OVERRIDE;
-
   void LocationResponseAvailable(const Geoposition& position,
                                  bool server_error,
                                  const string16& access_token,
@@ -102,12 +100,14 @@
   // The wifi data provider, acquired via global factories.
   WifiDataProvider* wifi_data_provider_;
 
-  // The  wifi data, flags to indicate if the data set is complete.
+  WifiDataProvider::WifiDataUpdateCallback wifi_data_update_callback_;
+
+  // The  wifi data and a flag to indicate if the data set is complete.
   WifiData wifi_data_;
   bool is_wifi_data_complete_;
 
-  // The timestamp for the latest device data update.
-  base::Time device_data_updated_timestamp_;
+  // The timestamp for the latest wifi data update.
+  base::Time wifi_data_updated_timestamp_;
 
   // Cached value loaded from the token store or set by a previous server
   // response, and sent in each subsequent network request.
@@ -124,10 +124,11 @@
   // The network location request object, and the url it uses.
   scoped_ptr<NetworkLocationRequest> request_;
 
-  base::WeakPtrFactory<NetworkLocationProvider> weak_factory_;
   // The cache of positions.
   scoped_ptr<PositionCache> position_cache_;
 
+  base::WeakPtrFactory<NetworkLocationProvider> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(NetworkLocationProvider);
 };
 
diff --git a/content/browser/geolocation/network_location_provider_unittest.cc b/content/browser/geolocation/network_location_provider_unittest.cc
index b0d9c25..86ebad0 100644
--- a/content/browser/geolocation/network_location_provider_unittest.cc
+++ b/content/browser/geolocation/network_location_provider_unittest.cc
@@ -46,54 +46,49 @@
   const LocationProvider* updated_provider_;
 };
 
-// A mock implementation of DeviceDataProviderImplBase for testing. Adapted from
+// A mock implementation of WifiDataProviderImplBase for testing. Adapted from
 // http://gears.googlecode.com/svn/trunk/gears/geolocation/geolocation_test.cc
-template<typename DataType>
-class MockDeviceDataProviderImpl
-    : public DeviceDataProviderImplBase<DataType> {
+class MockWifiDataProviderImpl : public WifiDataProviderImplBase {
  public:
-  // Factory method for use with DeviceDataProvider::SetFactory.
-  static DeviceDataProviderImplBase<DataType>* GetInstance() {
+  // Factory method for use with WifiDataProvider::SetFactory.
+  static WifiDataProviderImplBase* GetInstance() {
     CHECK(instance_);
     return instance_;
   }
 
-  static MockDeviceDataProviderImpl<DataType>* CreateInstance() {
+  static MockWifiDataProviderImpl* CreateInstance() {
     CHECK(!instance_);
-    instance_ = new MockDeviceDataProviderImpl<DataType>;
+    instance_ = new MockWifiDataProviderImpl;
     return instance_;
   }
 
-  MockDeviceDataProviderImpl()
+  MockWifiDataProviderImpl()
       : start_calls_(0),
         stop_calls_(0),
         got_data_(true) {
   }
 
-  virtual ~MockDeviceDataProviderImpl() {
-    CHECK(this == instance_);
-    instance_ = NULL;
-  }
-
-  // DeviceDataProviderImplBase implementation.
-  virtual void StartDataProvider() {
+  // WifiDataProviderImplBase implementation.
+  virtual void StartDataProvider() OVERRIDE {
     ++start_calls_;
   }
-  virtual void StopDataProvider() {
+
+  virtual void StopDataProvider() OVERRIDE {
     ++stop_calls_;
   }
-  virtual bool GetData(DataType* data_out) {
+
+  virtual bool GetData(WifiData* data_out) OVERRIDE {
     CHECK(data_out);
     *data_out = data_;
     return got_data_;
   }
 
-  void SetData(const DataType& new_data) {
+  void SetData(const WifiData& new_data) {
     got_data_ = true;
     const bool differs = data_.DiffersSignificantly(new_data);
     data_ = new_data;
     if (differs)
-      this->NotifyListeners();
+      this->RunCallbacks();
   }
 
   void set_got_data(bool got_data) { got_data_ = got_data; }
@@ -101,17 +96,20 @@
   int stop_calls_;
 
  private:
-  static MockDeviceDataProviderImpl<DataType>* instance_;
+  virtual ~MockWifiDataProviderImpl() {
+    CHECK(this == instance_);
+    instance_ = NULL;
+  }
 
-  DataType data_;
+  static MockWifiDataProviderImpl* instance_;
+
+  WifiData data_;
   bool got_data_;
 
-  DISALLOW_COPY_AND_ASSIGN(MockDeviceDataProviderImpl);
+  DISALLOW_COPY_AND_ASSIGN(MockWifiDataProviderImpl);
 };
 
-template<typename DataType>
-MockDeviceDataProviderImpl<DataType>*
-MockDeviceDataProviderImpl<DataType>::instance_ = NULL;
+MockWifiDataProviderImpl* MockWifiDataProviderImpl::instance_ = NULL;
 
 // Main test fixture
 class GeolocationNetworkProviderTest : public testing::Test {
@@ -120,7 +118,7 @@
     test_server_url_ = GURL(kTestServerUrl);
     access_token_store_ = new FakeAccessTokenStore;
     wifi_data_provider_ =
-        MockDeviceDataProviderImpl<WifiData>::CreateInstance();
+        MockWifiDataProviderImpl::CreateInstance();
   }
 
   virtual void TearDown() {
@@ -142,8 +140,7 @@
   GeolocationNetworkProviderTest() {
     // TODO(joth): Really these should be in SetUp, not here, but they take no
     // effect on Mac OS Release builds if done there. I kid not. Figure out why.
-    WifiDataProvider::SetFactory(
-        MockDeviceDataProviderImpl<WifiData>::GetInstance);
+    WifiDataProvider::SetFactory(MockWifiDataProviderImpl::GetInstance);
   }
 
   // Returns the current url fetcher (if any) and advances the id ready for the
@@ -324,7 +321,7 @@
   base::MessageLoop main_message_loop_;
   scoped_refptr<FakeAccessTokenStore> access_token_store_;
   net::TestURLFetcherFactory url_fetcher_factory_;
-  scoped_refptr<MockDeviceDataProviderImpl<WifiData> > wifi_data_provider_;
+  scoped_refptr<MockWifiDataProviderImpl> wifi_data_provider_;
 };
 
 TEST_F(GeolocationNetworkProviderTest, CreateDestroy) {
@@ -491,7 +488,7 @@
 }
 
 TEST_F(GeolocationNetworkProviderTest, NewDataReplacesExistingNetworkRequest) {
-  // Send initial request with empty device data
+  // Send initial request with empty data
   scoped_ptr<LocationProvider> provider(CreateProvider(true));
   EXPECT_TRUE(provider->StartProvider(false));
   net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
diff --git a/content/browser/geolocation/network_location_request.cc b/content/browser/geolocation/network_location_request.cc
index d11a474..ccc7526 100644
--- a/content/browser/geolocation/network_location_request.cc
+++ b/content/browser/geolocation/network_location_request.cc
@@ -271,7 +271,7 @@
     FormatPositionError(server_url, message, position);
     return;
   }
-  // We use the timestamp from the device data that was used to generate
+  // We use the timestamp from the wifi data that was used to generate
   // this position fix.
   if (!ParseServerResponse(response_body, timestamp, position, access_token)) {
     // We failed to parse the repsonse.
diff --git a/content/browser/geolocation/network_location_request.h b/content/browser/geolocation/network_location_request.h
index 56a6004..38aaf3e 100644
--- a/content/browser/geolocation/network_location_request.h
+++ b/content/browser/geolocation/network_location_request.h
@@ -8,7 +8,7 @@
 #include "base/basictypes.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
-#include "content/browser/geolocation/device_data_provider.h"
+#include "content/browser/geolocation/wifi_data_provider.h"
 #include "content/common/content_export.h"
 #include "net/url_request/url_fetcher_delegate.h"
 #include "url/gurl.h"
@@ -21,7 +21,7 @@
 namespace content {
 struct Geoposition;
 
-// Takes a set of device data and sends it to a server to get a position fix.
+// Takes wifi data and sends it to a server to get a position fix.
 // It performs formatting of the request and interpretation of the response.
 class NetworkLocationRequest : private net::URLFetcherDelegate {
  public:
diff --git a/content/browser/geolocation/wifi_data.cc b/content/browser/geolocation/wifi_data.cc
new file mode 100644
index 0000000..f4ea267
--- /dev/null
+++ b/content/browser/geolocation/wifi_data.cc
@@ -0,0 +1,50 @@
+// 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 "content/browser/geolocation/wifi_data.h"
+
+#include "base/logging.h"
+
+namespace content {
+
+AccessPointData::AccessPointData()
+    : radio_signal_strength(kint32min),
+      channel(kint32min),
+      signal_to_noise(kint32min) {
+}
+
+AccessPointData::~AccessPointData() {}
+
+WifiData::WifiData() {}
+
+WifiData::~WifiData() {}
+
+bool WifiData::DiffersSignificantly(const WifiData& other) const {
+  // More than 4 or 50% of access points added or removed is significant.
+  static const size_t kMinChangedAccessPoints = 4;
+  const size_t min_ap_count =
+      std::min(access_point_data.size(), other.access_point_data.size());
+  const size_t max_ap_count =
+      std::max(access_point_data.size(), other.access_point_data.size());
+  const size_t difference_threadhold = std::min(kMinChangedAccessPoints,
+                                                min_ap_count / 2);
+  if (max_ap_count > min_ap_count + difference_threadhold)
+    return true;
+  // Compute size of intersection of old and new sets.
+  size_t num_common = 0;
+  for (AccessPointDataSet::const_iterator iter = access_point_data.begin();
+       iter != access_point_data.end();
+       iter++) {
+    if (other.access_point_data.find(*iter) !=
+        other.access_point_data.end()) {
+      ++num_common;
+    }
+  }
+  DCHECK(num_common <= min_ap_count);
+
+  // Test how many have changed.
+  return max_ap_count > num_common + difference_threadhold;
+}
+
+}  // namespace content
diff --git a/content/browser/geolocation/wifi_data.h b/content/browser/geolocation/wifi_data.h
new file mode 100644
index 0000000..a24e42e
--- /dev/null
+++ b/content/browser/geolocation/wifi_data.h
@@ -0,0 +1,54 @@
+// 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 CONTENT_BROWSER_GEOLOCATION_WIFI_DATA_H_
+#define CONTENT_BROWSER_GEOLOCATION_WIFI_DATA_H_
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+// Wifi data relating to a single access point.
+struct CONTENT_EXPORT AccessPointData {
+  AccessPointData();
+  ~AccessPointData();
+
+  // MAC address, formatted as per MacAddressAsString16.
+  string16 mac_address;
+  int radio_signal_strength;  // Measured in dBm
+  int channel;
+  int signal_to_noise;  // Ratio in dB
+  string16 ssid;   // Network identifier
+};
+
+// This is to allow AccessPointData to be used in std::set. We order
+// lexicographically by MAC address.
+struct AccessPointDataLess {
+  bool operator()(const AccessPointData& data1,
+                  const AccessPointData& data2) const {
+    return data1.mac_address < data2.mac_address;
+  }
+};
+
+// All data for wifi.
+struct CONTENT_EXPORT WifiData {
+  WifiData();
+  ~WifiData();
+
+  // Determines whether a new set of WiFi data differs significantly from this.
+  bool DiffersSignificantly(const WifiData& other) const;
+
+  // Store access points as a set, sorted by MAC address. This allows quick
+  // comparison of sets for detecting changes and for caching.
+  typedef std::set<AccessPointData, AccessPointDataLess> AccessPointDataSet;
+  AccessPointDataSet access_point_data;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_GEOLOCATION_WIFI_DATA_H_
diff --git a/content/browser/geolocation/wifi_data_provider.cc b/content/browser/geolocation/wifi_data_provider.cc
new file mode 100644
index 0000000..416ecb8
--- /dev/null
+++ b/content/browser/geolocation/wifi_data_provider.cc
@@ -0,0 +1,95 @@
+// 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 "content/browser/geolocation/wifi_data_provider.h"
+
+namespace content {
+
+// static
+WifiDataProvider* WifiDataProvider::instance_ = NULL;
+
+// static
+WifiDataProvider::ImplFactoryFunction WifiDataProvider::factory_function_ =
+    DefaultFactoryFunction;
+
+// static
+WifiDataProvider* WifiDataProvider::Register(WifiDataUpdateCallback* callback) {
+  bool need_to_start_data_provider = false;
+  if (!instance_) {
+    instance_ = new WifiDataProvider();
+    need_to_start_data_provider = true;
+  }
+  DCHECK(instance_);
+  instance_->AddCallback(callback);
+  // Start the provider after adding the callback, to avoid any race in
+  // it running early.
+  if (need_to_start_data_provider)
+    instance_->StartDataProvider();
+  return instance_;
+}
+
+WifiDataProviderImplBase::WifiDataProviderImplBase()
+    : container_(NULL),
+      client_loop_(base::MessageLoop::current()) {
+  DCHECK(client_loop_);
+}
+
+WifiDataProviderImplBase::~WifiDataProviderImplBase() {
+}
+
+void WifiDataProviderImplBase::SetContainer(WifiDataProvider* container) {
+  container_ = container;
+}
+
+void WifiDataProviderImplBase::AddCallback(WifiDataUpdateCallback* callback) {
+  callbacks_.insert(callback);
+}
+
+bool WifiDataProviderImplBase::RemoveCallback(
+    WifiDataUpdateCallback* callback) {
+  return callbacks_.erase(callback) == 1;
+}
+
+bool WifiDataProviderImplBase::has_callbacks() const {
+  return !callbacks_.empty();
+}
+
+void WifiDataProviderImplBase::RunCallbacks() {
+  client_loop_->PostTask(FROM_HERE, base::Bind(
+      &WifiDataProviderImplBase::DoRunCallbacks,
+      this));
+}
+
+bool WifiDataProviderImplBase::CalledOnClientThread() const {
+  return base::MessageLoop::current() == this->client_loop_;
+}
+
+base::MessageLoop* WifiDataProviderImplBase::client_loop() const {
+  return client_loop_;
+}
+
+void WifiDataProviderImplBase::DoRunCallbacks() {
+  // It's possible that all the callbacks (and the container) went away
+  // whilst this task was pending. This is fine; the loop will be a no-op.
+  CallbackSet::const_iterator iter = callbacks_.begin();
+  while (iter != callbacks_.end()) {
+    WifiDataUpdateCallback* callback = *iter;
+    ++iter;  // Advance iter before running, in case callback unregisters.
+    callback->Run(container_);
+  }
+}
+
+WifiDataProvider::WifiDataProvider() {
+  DCHECK(factory_function_);
+  impl_ = (*factory_function_)();
+  DCHECK(impl_.get());
+  impl_->SetContainer(this);
+}
+
+WifiDataProvider::~WifiDataProvider() {
+  DCHECK(impl_.get());
+  impl_->SetContainer(NULL);
+}
+
+}  // namespace content
diff --git a/content/browser/geolocation/wifi_data_provider.h b/content/browser/geolocation/wifi_data_provider.h
new file mode 100644
index 0000000..7937f28
--- /dev/null
+++ b/content/browser/geolocation/wifi_data_provider.h
@@ -0,0 +1,195 @@
+// 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.
+
+// A wifi data provider provides wifi data from the device that is used by a
+// NetworkLocationProvider to obtain a position fix. We use a singleton
+// instance of the wifi data provider, which is used by multiple
+// NetworkLocationProvider objects.
+//
+// This file provides WifiDataProvider, which provides static methods to
+// access the singleton instance. The singleton instance uses a private
+// implementation to abstract across platforms and also to allow mock providers
+// to be used for testing.
+//
+// This file also provides WifiDataProviderImplBase, a base class which
+// provides common functionality for the private implementations.
+
+#ifndef CONTENT_BROWSER_GEOLOCATION_WIFI_DATA_PROVIDER_H_
+#define CONTENT_BROWSER_GEOLOCATION_WIFI_DATA_PROVIDER_H_
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "content/browser/geolocation/wifi_data.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+class WifiDataProvider;
+
+// See class WifiDataProvider for the public client API.
+// WifiDataProvider uses containment to hide platform-specific implementation
+// details from common code. This class provides common functionality for these
+// contained implementation classes. This is a modified pimpl pattern.
+class CONTENT_EXPORT WifiDataProviderImplBase
+    : public base::RefCountedThreadSafe<WifiDataProviderImplBase> {
+ public:
+  WifiDataProviderImplBase();
+
+  // Tells the provider to start looking for data. Callbacks will start
+  // receiving notifications after this call.
+  virtual void StartDataProvider() = 0;
+
+  // Tells the provider to stop looking for data. Callbacks will stop
+  // receiving notifications after this call.
+  virtual void StopDataProvider() = 0;
+
+  // Provides whatever data the provider has, which may be nothing. Return
+  // value indicates whether this is all the data the provider could ever
+  // obtain.
+  virtual bool GetData(WifiData* data) = 0;
+
+  // Sets the container of this class, which is of type WifiDataProvider.
+  // This is required to pass as a parameter when calling a callback.
+  void SetContainer(WifiDataProvider* container);
+
+  typedef base::Callback<void(WifiDataProvider*)> WifiDataUpdateCallback;
+
+  void AddCallback(WifiDataUpdateCallback* callback);
+
+  bool RemoveCallback(WifiDataUpdateCallback* callback);
+
+  bool has_callbacks() const;
+
+ protected:
+  friend class base::RefCountedThreadSafe<WifiDataProviderImplBase>;
+  virtual ~WifiDataProviderImplBase();
+
+  typedef std::set<WifiDataUpdateCallback*> CallbackSet;
+
+  // Runs all callbacks via a posted task, so we can unwind callstack here and
+  // avoid client reentrancy.
+  void RunCallbacks();
+
+  bool CalledOnClientThread() const;
+
+  base::MessageLoop* client_loop() const;
+
+ private:
+  void DoRunCallbacks();
+
+  WifiDataProvider* container_;
+
+  // Reference to the client's message loop. All callbacks should happen in this
+  // context.
+  base::MessageLoop* client_loop_;
+
+  CallbackSet callbacks_;
+
+  DISALLOW_COPY_AND_ASSIGN(WifiDataProviderImplBase);
+};
+
+// A wifi data provider
+//
+// We use a singleton instance of this class which is shared by multiple network
+// location providers. These location providers access the instance through the
+// Register and Unregister methods.
+class CONTENT_EXPORT WifiDataProvider {
+ public:
+  // Sets the factory function which will be used by Register to create the
+  // implementation used by the singleton instance. This factory approach is
+  // used both to abstract accross platform-specific implementations and to
+  // inject mock implementations for testing.
+  typedef WifiDataProviderImplBase* (*ImplFactoryFunction)(void);
+  static void SetFactory(ImplFactoryFunction factory_function_in) {
+    factory_function_ = factory_function_in;
+  }
+
+  static void ResetFactory() {
+    factory_function_ = DefaultFactoryFunction;
+  }
+
+  typedef base::Callback<void(WifiDataProvider*)> WifiDataUpdateCallback;
+
+  // Registers a callback, which will be run whenever new data is available.
+  // Instantiates the singleton if necessary, and always returns it.
+  static WifiDataProvider* Register(WifiDataUpdateCallback* callback);
+
+  // Removes a callback. If this is the last callback, deletes the singleton
+  // instance. Return value indicates success.
+  static bool Unregister(WifiDataUpdateCallback* callback) {
+    DCHECK(instance_);
+    DCHECK(instance_->has_callbacks());
+    if (!instance_->RemoveCallback(callback)) {
+      return false;
+    }
+    if (!instance_->has_callbacks()) {
+      // Must stop the data provider (and any implementation threads) before
+      // destroying to avoid any race conditions in access to the provider in
+      // the destructor chain.
+      instance_->StopDataProvider();
+      delete instance_;
+      instance_ = NULL;
+    }
+    return true;
+  }
+
+  // Provides whatever data the provider has, which may be nothing. Return
+  // value indicates whether this is all the data the provider could ever
+  // obtain.
+  bool GetData(WifiData* data) {
+    return impl_->GetData(data);
+  }
+
+ private:
+  // Private constructor and destructor, callers access singleton through
+  // Register and Unregister.
+  WifiDataProvider();
+  virtual ~WifiDataProvider();
+
+  void AddCallback(WifiDataUpdateCallback* callback) {
+    impl_->AddCallback(callback);
+  }
+
+  bool RemoveCallback(WifiDataUpdateCallback* callback) {
+    return impl_->RemoveCallback(callback);
+  }
+
+  bool has_callbacks() const {
+    return impl_->has_callbacks();
+  }
+
+  void StartDataProvider() {
+    impl_->StartDataProvider();
+  }
+
+  void StopDataProvider() {
+    impl_->StopDataProvider();
+  }
+
+  static WifiDataProviderImplBase* DefaultFactoryFunction();
+
+  // The singleton-like instance of this class. (Not 'true' singleton, as it
+  // may go through multiple create/destroy/create cycles per process instance,
+  // e.g. when under test).
+  static WifiDataProvider* instance_;
+
+  // The factory function used to create the singleton instance.
+  static ImplFactoryFunction factory_function_;
+
+  // The internal implementation.
+  scoped_refptr<WifiDataProviderImplBase> impl_;
+
+  DISALLOW_COPY_AND_ASSIGN(WifiDataProvider);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_GEOLOCATION_WIFI_DATA_PROVIDER_H_
diff --git a/content/browser/geolocation/wifi_data_provider_chromeos.cc b/content/browser/geolocation/wifi_data_provider_chromeos.cc
index 951d2d3..7f29276 100644
--- a/content/browser/geolocation/wifi_data_provider_chromeos.cc
+++ b/content/browser/geolocation/wifi_data_provider_chromeos.cc
@@ -87,7 +87,7 @@
   // in between DoWifiScanTaskOnUIThread and this method).
   if (started_)
     ScheduleNextScan(polling_policy_->NoWifiInterval());
-  MaybeNotifyListeners(false);
+  MaybeRunCallbacks(false);
 }
 
 void WifiDataProviderChromeOs::DidWifiScanTask(const WifiData& new_data) {
@@ -100,13 +100,13 @@
     polling_policy_->UpdatePollingInterval(update_available);
     ScheduleNextScan(polling_policy_->PollingInterval());
   }
-  MaybeNotifyListeners(update_available);
+  MaybeRunCallbacks(update_available);
 }
 
-void WifiDataProviderChromeOs::MaybeNotifyListeners(bool update_available) {
+void WifiDataProviderChromeOs::MaybeRunCallbacks(bool update_available) {
   if (update_available || !is_first_scan_complete_) {
     is_first_scan_complete_ = true;
-    NotifyListeners();
+    RunCallbacks();
   }
 }
 
@@ -167,7 +167,6 @@
 }
 
 // static
-template<>
 WifiDataProviderImplBase* WifiDataProvider::DefaultFactoryFunction() {
   return new WifiDataProviderChromeOs();
 }
diff --git a/content/browser/geolocation/wifi_data_provider_chromeos.h b/content/browser/geolocation/wifi_data_provider_chromeos.h
index 4c8a80c..0118298 100644
--- a/content/browser/geolocation/wifi_data_provider_chromeos.h
+++ b/content/browser/geolocation/wifi_data_provider_chromeos.h
@@ -31,7 +31,7 @@
   // Client thread
   void DidWifiScanTaskNoResults();
   void DidWifiScanTask(const WifiData& new_data);
-  void MaybeNotifyListeners(bool update_available);
+  void MaybeRunCallbacks(bool update_available);
 
   // Will schedule a scan; i.e. enqueue DoWifiScanTask deferred task.
   void ScheduleNextScan(int interval);
diff --git a/content/browser/geolocation/wifi_data_provider_common.cc b/content/browser/geolocation/wifi_data_provider_common.cc
index 9132752..31f969c 100644
--- a/content/browser/geolocation/wifi_data_provider_common.cc
+++ b/content/browser/geolocation/wifi_data_provider_common.cc
@@ -75,7 +75,7 @@
   }
   if (update_available || !is_first_scan_complete_) {
     is_first_scan_complete_ = true;
-    NotifyListeners();
+    RunCallbacks();
   }
 }
 
diff --git a/content/browser/geolocation/wifi_data_provider_common.h b/content/browser/geolocation/wifi_data_provider_common.h
index 205eae3..5f15e09 100644
--- a/content/browser/geolocation/wifi_data_provider_common.h
+++ b/content/browser/geolocation/wifi_data_provider_common.h
@@ -11,7 +11,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
-#include "content/browser/geolocation/device_data_provider.h"
+#include "content/browser/geolocation/wifi_data_provider.h"
 #include "content/common/content_export.h"
 
 namespace content {
@@ -62,7 +62,7 @@
 // providers. It's optional for specific platforms to derive this, but if they
 // do polling behavior is taken care of by this base class, and all the platform
 // need do is provide the underlying WLAN access API and polling policy.
-// Also designed this way to for ease of testing the cross-platform behavior.
+// Also designed this way for ease of testing the cross-platform behavior.
 class CONTENT_EXPORT WifiDataProviderCommon : public WifiDataProviderImplBase {
  public:
   // Interface to abstract the low level data OS library call, and to allow
@@ -84,14 +84,14 @@
  protected:
   virtual ~WifiDataProviderCommon();
 
-  // Returns ownership. Will be called from the worker thread.
+  // Returns ownership.
   virtual WlanApiInterface* NewWlanApi() = 0;
 
-  // Returns ownership. Will be called from the worker thread.
+  // Returns ownership.
   virtual PollingPolicyInterface* NewPollingPolicy() = 0;
 
  private:
-  // Runs a scan. Notifies the listeners if new data is found.
+  // Runs a scan. Calls the callbacks if new data is found.
   void DoWifiScanTask();
 
   // Will schedule a scan; i.e. enqueue DoWifiScanTask deferred task.
diff --git a/content/browser/geolocation/wifi_data_provider_common_unittest.cc b/content/browser/geolocation/wifi_data_provider_common_unittest.cc
index 2873720..c37200f 100644
--- a/content/browser/geolocation/wifi_data_provider_common_unittest.cc
+++ b/content/browser/geolocation/wifi_data_provider_common_unittest.cc
@@ -42,7 +42,7 @@
   }
 };
 
-class MockPollingPolicy :public PollingPolicyInterface {
+class MockPollingPolicy : public PollingPolicyInterface {
  public:
   MockPollingPolicy() {
     ON_CALL(*this,PollingInterval())
@@ -57,27 +57,25 @@
   virtual void UpdatePollingInterval(bool) {}
 };
 
-// Stops the specified (nested) message loop when the listener is called back.
-class MessageLoopQuitListener
-    : public WifiDataProviderCommon::ListenerInterface {
+// Stops the specified (nested) message loop when the callback is called.
+class MessageLoopQuitter {
  public:
-  explicit MessageLoopQuitListener(base::MessageLoop* message_loop)
-      : message_loop_to_quit_(message_loop) {
+  explicit MessageLoopQuitter(base::MessageLoop* message_loop)
+      : message_loop_to_quit_(message_loop),
+        callback_(base::Bind(&MessageLoopQuitter::WifiDataUpdateAvailable,
+                             base::Unretained(this))) {
     CHECK(message_loop_to_quit_ != NULL);
   }
-  // ListenerInterface
-  virtual void DeviceDataUpdateAvailable(
-      DeviceDataProvider<WifiData>* provider) OVERRIDE {
+
+  void WifiDataUpdateAvailable(WifiDataProvider* provider) {
     // Provider should call back on client's thread.
     EXPECT_EQ(base::MessageLoop::current(), message_loop_to_quit_);
-    provider_ = provider;
     message_loop_to_quit_->QuitNow();
   }
   base::MessageLoop* message_loop_to_quit_;
-  DeviceDataProvider<WifiData>* provider_;
+  WifiDataProvider::WifiDataUpdateCallback callback_;
 };
 
-
 class WifiDataProviderCommonWithMock : public WifiDataProviderCommon {
  public:
   WifiDataProviderCommonWithMock()
@@ -111,24 +109,24 @@
 class GeolocationWifiDataProviderCommonTest : public testing::Test {
  public:
   GeolocationWifiDataProviderCommonTest()
-      : quit_listener_(&main_message_loop_) {
+      : loop_quitter_(&main_message_loop_) {
   }
 
   virtual void SetUp() {
     provider_ = new WifiDataProviderCommonWithMock;
     wlan_api_ = provider_->new_wlan_api_.get();
     polling_policy_ = provider_->new_polling_policy_.get();
-    provider_->AddListener(&quit_listener_);
+    provider_->AddCallback(&loop_quitter_.callback_);
   }
   virtual void TearDown() {
-    provider_->RemoveListener(&quit_listener_);
+    provider_->RemoveCallback(&loop_quitter_.callback_);
     provider_->StopDataProvider();
     provider_ = NULL;
   }
 
  protected:
   base::MessageLoop main_message_loop_;
-  MessageLoopQuitListener quit_listener_;
+  MessageLoopQuitter loop_quitter_;
   scoped_refptr<WifiDataProviderCommonWithMock> provider_;
   MockWlanApi* wlan_api_;
   MockPollingPolicy* polling_policy_;
@@ -141,7 +139,7 @@
   EXPECT_TRUE(NULL != wlan_api_);
 }
 
-TEST_F(GeolocationWifiDataProviderCommonTest, StartThread) {
+TEST_F(GeolocationWifiDataProviderCommonTest, RunNormal) {
   EXPECT_CALL(*wlan_api_, GetAccessPointData(_))
       .Times(AtLeast(1));
   EXPECT_CALL(*polling_policy_, PollingInterval())
@@ -190,9 +188,7 @@
       .Times(AtLeast(1));
   provider_->StartDataProvider();
   main_message_loop_.Run();
-  // Check we had at least one call. The worker thread may have raced ahead
-  // and made multiple calls.
-  EXPECT_GT(wlan_api_->calls_, 0);
+  EXPECT_EQ(wlan_api_->calls_, 1);
   WifiData data;
   EXPECT_TRUE(provider_->GetData(&data));
   EXPECT_EQ(0, static_cast<int>(data.access_point_data.size()));
@@ -213,21 +209,20 @@
 
   provider_->StartDataProvider();
   main_message_loop_.Run();
-  EXPECT_GT(wlan_api_->calls_, 0);
+  EXPECT_EQ(wlan_api_->calls_, 1);
   WifiData data;
   EXPECT_TRUE(provider_->GetData(&data));
   EXPECT_EQ(1, static_cast<int>(data.access_point_data.size()));
   EXPECT_EQ(single_access_point.ssid, data.access_point_data.begin()->ssid);
 }
 
-TEST_F(GeolocationWifiDataProviderCommonTest,
-       StartThreadViaDeviceDataProvider) {
-  MessageLoopQuitListener quit_listener(&main_message_loop_);
+TEST_F(GeolocationWifiDataProviderCommonTest, RegisterUnregister) {
+  MessageLoopQuitter loop_quitter(&main_message_loop_);
   WifiDataProvider::SetFactory(CreateWifiDataProviderCommonWithMock);
-  DeviceDataProvider<WifiData>::Register(&quit_listener);
+  WifiDataProvider::Register(&loop_quitter.callback_);
   main_message_loop_.Run();
-  DeviceDataProvider<WifiData>::Unregister(&quit_listener);
-  DeviceDataProvider<WifiData>::ResetFactory();
+  WifiDataProvider::Unregister(&loop_quitter.callback_);
+  WifiDataProvider::ResetFactory();
 }
 
 }  // namespace content
diff --git a/content/browser/geolocation/wifi_data_provider_common_win.cc b/content/browser/geolocation/wifi_data_provider_common_win.cc
index 9ac7848..a0d334f 100644
--- a/content/browser/geolocation/wifi_data_provider_common_win.cc
+++ b/content/browser/geolocation/wifi_data_provider_common_win.cc
@@ -7,7 +7,6 @@
 #include <assert.h>
 
 #include "base/strings/utf_string_conversions.h"
-#include "content/browser/geolocation/device_data_provider.h"
 #include "content/browser/geolocation/wifi_data_provider_common.h"
 
 namespace content {
diff --git a/content/browser/geolocation/wifi_data_provider_common_win.h b/content/browser/geolocation/wifi_data_provider_common_win.h
index f58e0c6..24973a5 100644
--- a/content/browser/geolocation/wifi_data_provider_common_win.h
+++ b/content/browser/geolocation/wifi_data_provider_common_win.h
@@ -8,7 +8,7 @@
 #include <windows.h>
 #include <ntddndis.h>
 
-#include "content/browser/geolocation/device_data_provider.h"
+#include "content/browser/geolocation/wifi_data_provider.h"
 
 namespace content {
 
diff --git a/content/browser/geolocation/wifi_data_provider_linux.cc b/content/browser/geolocation/wifi_data_provider_linux.cc
index edece1d..34bc440 100644
--- a/content/browser/geolocation/wifi_data_provider_linux.cc
+++ b/content/browser/geolocation/wifi_data_provider_linux.cc
@@ -344,7 +344,6 @@
 }  // namespace
 
 // static
-template<>
 WifiDataProviderImplBase* WifiDataProvider::DefaultFactoryFunction() {
   return new WifiDataProviderLinux();
 }
diff --git a/content/browser/geolocation/wifi_data_provider_mac.cc b/content/browser/geolocation/wifi_data_provider_mac.cc
index 8f81305..f1f3755 100644
--- a/content/browser/geolocation/wifi_data_provider_mac.cc
+++ b/content/browser/geolocation/wifi_data_provider_mac.cc
@@ -158,7 +158,6 @@
 }  // namespace
 
 // static
-template<>
 WifiDataProviderImplBase* WifiDataProvider::DefaultFactoryFunction() {
   return new MacWifiDataProvider();
 }
diff --git a/content/browser/geolocation/wifi_data_provider_win.cc b/content/browser/geolocation/wifi_data_provider_win.cc
index a35e6e9..2cb15dc 100644
--- a/content/browser/geolocation/wifi_data_provider_win.cc
+++ b/content/browser/geolocation/wifi_data_provider_win.cc
@@ -158,7 +158,6 @@
 bool GetSystemDirectory(string16* path);
 }  // namespace
 
-template<>
 WifiDataProviderImplBase* WifiDataProvider::DefaultFactoryFunction() {
   return new Win32WifiDataProvider();
 }
diff --git a/content/browser/gpu/compositor_util.cc b/content/browser/gpu/compositor_util.cc
index 77dda3f..9270300 100644
--- a/content/browser/gpu/compositor_util.cc
+++ b/content/browser/gpu/compositor_util.cc
@@ -44,8 +44,8 @@
 }  // namespace
 
 bool IsThreadedCompositingEnabled() {
-#if defined(OS_WIN) && defined(USE_AURA)
-  // We always want compositing on Aura Windows.
+#if defined(USE_AURA)
+  // We always want threaded compositing on Aura.
   return true;
 #endif
 
@@ -59,12 +59,6 @@
       command_line.HasSwitch(switches::kDisableThreadedCompositing))
     return false;
 
-#if defined(OS_CHROMEOS)
-  // We always want threaded compositing on  ChromeOS unless it's explicitly
-  // disabled above.
-  return true;
-#endif
-
   if (command_line.HasSwitch(switches::kEnableThreadedCompositing))
     return true;
 
@@ -78,8 +72,8 @@
 }
 
 bool IsForceCompositingModeEnabled() {
-#if defined(OS_WIN) && defined(USE_AURA)
-  // We always want compositing on Aura Windows.
+#if defined(USE_AURA)
+  // We always want compositing on Aura.
   return true;
 #endif
 
@@ -92,11 +86,6 @@
   if (command_line.HasSwitch(switches::kDisableForceCompositingMode))
     return false;
 
-#if defined(OS_CHROMEOS)
-  // We always want compositing ChromeOS unless it's explicitly disabled above.
-  return true;
-#endif
-
   if (command_line.HasSwitch(switches::kForceCompositingMode))
     return true;
 
diff --git a/content/browser/gpu/gpu_data_manager_impl.cc b/content/browser/gpu/gpu_data_manager_impl.cc
index 747503c..dbdcbe2 100644
--- a/content/browser/gpu/gpu_data_manager_impl.cc
+++ b/content/browser/gpu/gpu_data_manager_impl.cc
@@ -29,6 +29,11 @@
   return private_->IsFeatureBlacklisted(feature);
 }
 
+bool GpuDataManagerImpl::IsDriverBugWorkaroundActive(int feature) const {
+  base::AutoLock auto_lock(lock_);
+  return private_->IsDriverBugWorkaroundActive(feature);
+}
+
 gpu::GPUInfo GpuDataManagerImpl::GetGPUInfo() const {
   base::AutoLock auto_lock(lock_);
   return private_->GetGPUInfo();
diff --git a/content/browser/gpu/gpu_data_manager_impl.h b/content/browser/gpu/gpu_data_manager_impl.h
index 497d3bd..1e15313 100644
--- a/content/browser/gpu/gpu_data_manager_impl.h
+++ b/content/browser/gpu/gpu_data_manager_impl.h
@@ -177,6 +177,8 @@
   // Called when GPU process initialization failed.
   void OnGpuProcessInitFailure();
 
+  bool IsDriverBugWorkaroundActive(int feature) const;
+
  private:
   friend class GpuDataManagerImplPrivate;
   friend class GpuDataManagerImplPrivateTest;
diff --git a/content/browser/gpu/gpu_data_manager_impl_private.cc b/content/browser/gpu/gpu_data_manager_impl_private.cc
index 3ff628a..ee5463d 100644
--- a/content/browser/gpu/gpu_data_manager_impl_private.cc
+++ b/content/browser/gpu/gpu_data_manager_impl_private.cc
@@ -42,6 +42,7 @@
 #include "base/win/windows_version.h"
 #endif  // OS_WIN
 #if defined(OS_ANDROID)
+#include "base/android/build_info.h"
 #include "ui/gfx/android/device_display_info.h"
 #endif  // OS_ANDROID
 
@@ -229,7 +230,7 @@
 void DisplayReconfigCallback(CGDirectDisplayID display,
                              CGDisplayChangeSummaryFlags flags,
                              void* gpu_data_manager) {
-  if(flags == kCGDisplayBeginConfigurationFlag)
+  if (flags == kCGDisplayBeginConfigurationFlag)
     return; // This call contains no information about the display change
 
   GpuDataManagerImpl* manager =
@@ -278,12 +279,15 @@
   bool is_nexus10 =
       gpu_info.machine_model.find("Nexus 10") != std::string::npos;
 
+  int sdk_int = base::android::BuildInfo::GetInstance()->sdk_int();
+
   // IMG: avoid context switching perf problems, crashes with share groups
   // Mali-T604: http://crbug.com/154715
   // QualComm, NVIDIA: Crashes with share groups
-  if (is_vivante || is_img || is_mali_t604 || is_nvidia || is_qualcomm ||
-      is_broadcom)
+  if (is_vivante || is_img || is_mali_t604 ||
+      ((is_nvidia || is_qualcomm) && sdk_int < 18) || is_broadcom) {
     command_line->AppendSwitch(switches::kEnableVirtualGLContexts);
+  }
 
   gfx::DeviceDisplayInfo info;
   int default_tile_size = 256;
@@ -307,7 +311,7 @@
 
   // If we are using the MapImage API double the tile size to reduce
   // the number of zero-copy buffers being used.
-  if (command_line->HasSwitch(cc::switches::kUseMapImage))
+  if (command_line->HasSwitch(cc::switches::kEnableMapImage))
     default_tile_size *= 2;
 
   // Set the command line if it isn't already set and we changed
@@ -376,6 +380,10 @@
   return (blacklisted_features_.count(feature) == 1);
 }
 
+bool GpuDataManagerImplPrivate::IsDriverBugWorkaroundActive(int feature) const {
+  return (gpu_driver_bugs_.count(feature) == 1);
+}
+
 size_t GpuDataManagerImplPrivate::GetBlacklistedFeatureCount() const {
   if (use_swiftshader_)
     return 1;
@@ -817,8 +825,9 @@
     prefs->flash_stage3d_baseline_enabled = false;
   if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_2D_CANVAS))
     prefs->accelerated_2d_canvas_enabled = false;
-  if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_MULTISAMPLING)
-      || display_count_ > 1)
+  if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_MULTISAMPLING) ||
+      (IsDriverBugWorkaroundActive(gpu::DISABLE_MULTIMONITOR_MULTISAMPLING) &&
+          display_count_ > 1))
     prefs->gl_multisampling_enabled = false;
   if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_3D_CSS)) {
     prefs->accelerated_compositing_for_3d_transforms_enabled = false;
diff --git a/content/browser/gpu/gpu_data_manager_impl_private.h b/content/browser/gpu/gpu_data_manager_impl_private.h
index eb226e8..8d15296 100644
--- a/content/browser/gpu/gpu_data_manager_impl_private.h
+++ b/content/browser/gpu/gpu_data_manager_impl_private.h
@@ -28,6 +28,7 @@
       const std::string& gpu_blacklist_json,
       const gpu::GPUInfo& gpu_info);
   bool IsFeatureBlacklisted(int feature) const;
+  bool IsDriverBugWorkaroundActive(int feature) const;
   gpu::GPUInfo GetGPUInfo() const;
   void GetGpuProcessHandles(
       const GpuDataManager::GetGpuProcessHandlesCallback& callback) const;
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index 7839ea7..c694cdc 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -6,8 +6,9 @@
 
 #include "base/base64.h"
 #include "base/base_switches.h"
+#include "base/basictypes.h"
 #include "base/bind.h"
-#include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/debug/trace_event.h"
 #include "base/logging.h"
@@ -520,7 +521,8 @@
   std::string message;
   if (!in_process_) {
     int exit_code;
-    base::TerminationStatus status = process_->GetTerminationStatus(&exit_code);
+    base::TerminationStatus status = process_->GetTerminationStatus(
+        false /* known_dead */, &exit_code);
     UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessTerminationStatus",
                               status,
                               base::TERMINATION_STATUS_MAX_ENUM);
@@ -899,7 +901,7 @@
   // if the browser is waiting for a new frame. Otherwise the RenderWidgetHelper
   // will forward to the RenderWidgetHostView via RenderProcessHostImpl and
   // RenderWidgetHostImpl.
-  scoped_completion_runner.Release();
+  ignore_result(scoped_completion_runner.Release());
 
   ViewHostMsg_CompositorSurfaceBuffersSwapped_Params view_params;
   view_params.surface_id = params.surface_id;
@@ -935,7 +937,7 @@
     TRACE_EVENT1("gpu", "SurfaceIDNotFound_RoutingToUI",
                  "surface_id", params.surface_id);
     // This is a content area swap, send it on to the UI thread.
-    scoped_completion_runner.Release();
+    ignore_result(scoped_completion_runner.Release());
     RouteOnUIThread(GpuHostMsg_AcceleratedSurfaceBuffersSwapped(params));
     return;
   }
@@ -949,7 +951,7 @@
                  "EarlyOut_NativeWindowNotFound",
                  "handle",
                  handle.handle);
-    scoped_completion_runner.Release();
+    ignore_result(scoped_completion_runner.Release());
     AcceleratedSurfaceBuffersSwappedCompleted(host_id_,
                                               params.route_id,
                                               params.surface_id,
@@ -960,7 +962,7 @@
     return;
   }
 
-  scoped_completion_runner.Release();
+  ignore_result(scoped_completion_runner.Release());
   presenter->AsyncPresentAndAcknowledge(
       params.size,
       params.surface_handle,
@@ -1045,7 +1047,7 @@
 void GpuProcessHost::OnProcessCrashed(int exit_code) {
   SendOutstandingReplies();
   GpuDataManagerImpl::GetInstance()->ProcessCrashed(
-      process_->GetTerminationStatus(NULL));
+      process_->GetTerminationStatus(true /* known_dead */, NULL));
 }
 
 GpuProcessHost::GpuProcessKind GpuProcessHost::kind() {
diff --git a/content/browser/indexed_db/indexed_db_backing_store.cc b/content/browser/indexed_db/indexed_db_backing_store.cc
index 5836911..9b66518 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store.cc
@@ -348,11 +348,10 @@
 
 class DefaultLevelDBFactory : public LevelDBFactory {
  public:
-  virtual leveldb::Status OpenLevelDB(
-      const base::FilePath& file_name,
-      const LevelDBComparator* comparator,
-      scoped_ptr<LevelDBDatabase>* db,
-      bool* is_disk_full) OVERRIDE {
+  virtual leveldb::Status OpenLevelDB(const base::FilePath& file_name,
+                                      const LevelDBComparator* comparator,
+                                      scoped_ptr<LevelDBDatabase>* db,
+                                      bool* is_disk_full) OVERRIDE {
     return LevelDBDatabase::Open(file_name, comparator, db, is_disk_full);
   }
   virtual bool DestroyLevelDB(const base::FilePath& file_name) OVERRIDE {
@@ -968,10 +967,10 @@
 
     it->Next();
     if (!CheckObjectStoreAndMetaDataType(
-            it.get(),
-            stop_key,
-            object_store_id,
-            ObjectStoreMetaDataKey::AUTO_INCREMENT)) {
+             it.get(),
+             stop_key,
+             object_store_id,
+             ObjectStoreMetaDataKey::AUTO_INCREMENT)) {
       INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
       break;
     }
@@ -993,20 +992,20 @@
 
     it->Next();  // Last version.
     if (!CheckObjectStoreAndMetaDataType(
-            it.get(),
-            stop_key,
-            object_store_id,
-            ObjectStoreMetaDataKey::LAST_VERSION)) {
+             it.get(),
+             stop_key,
+             object_store_id,
+             ObjectStoreMetaDataKey::LAST_VERSION)) {
       INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
       break;
     }
 
     it->Next();  // Maximum index id allocated.
     if (!CheckObjectStoreAndMetaDataType(
-            it.get(),
-            stop_key,
-            object_store_id,
-            ObjectStoreMetaDataKey::MAX_INDEX_ID)) {
+             it.get(),
+             stop_key,
+             object_store_id,
+             ObjectStoreMetaDataKey::MAX_INDEX_ID)) {
       INTERNAL_CONSISTENCY_ERROR(GET_OBJECT_STORES);
       break;
     }
@@ -1542,7 +1541,7 @@
 
     it->Next();  // unique flag
     if (!CheckIndexAndMetaDataKey(
-            it.get(), stop_key, index_id, IndexMetaDataKey::UNIQUE)) {
+             it.get(), stop_key, index_id, IndexMetaDataKey::UNIQUE)) {
       INTERNAL_CONSISTENCY_ERROR(GET_INDEXES);
       break;
     }
@@ -1555,7 +1554,7 @@
 
     it->Next();  // key_path
     if (!CheckIndexAndMetaDataKey(
-            it.get(), stop_key, index_id, IndexMetaDataKey::KEY_PATH)) {
+             it.get(), stop_key, index_id, IndexMetaDataKey::KEY_PATH)) {
       INTERNAL_CONSISTENCY_ERROR(GET_INDEXES);
       break;
     }
@@ -1623,7 +1622,7 @@
   LevelDBTransaction* leveldb_transaction =
       IndexedDBBackingStore::Transaction::LevelDBTransactionFrom(transaction);
   if (!SetMaxIndexId(
-          leveldb_transaction, database_id, object_store_id, index_id))
+           leveldb_transaction, database_id, object_store_id, index_id))
     return false;
 
   const std::string name_key = IndexMetaDataKey::Encode(
@@ -2402,8 +2401,9 @@
       cursor_options->high_open = true;  // Not included.
     } else {
       // We need a key that exists.
-      if (!FindGreatestKeyLessThanOrEqual(
-              transaction, cursor_options->high_key, &cursor_options->high_key))
+      if (!FindGreatestKeyLessThanOrEqual(transaction,
+                                          cursor_options->high_key,
+                                          &cursor_options->high_key))
         return false;
       cursor_options->high_open = false;
     }
@@ -2416,7 +2416,7 @@
       // For reverse cursors, we need a key that exists.
       std::string found_high_key;
       if (!FindGreatestKeyLessThanOrEqual(
-              transaction, cursor_options->high_key, &found_high_key))
+               transaction, cursor_options->high_key, &found_high_key))
         return false;
 
       // If the target key should not be included, but we end up with a smaller
@@ -2472,8 +2472,9 @@
     cursor_options->high_open = false;  // Included.
 
     if (!cursor_options->forward) {  // We need a key that exists.
-      if (!FindGreatestKeyLessThanOrEqual(
-              transaction, cursor_options->high_key, &cursor_options->high_key))
+      if (!FindGreatestKeyLessThanOrEqual(transaction,
+                                          cursor_options->high_key,
+                                          &cursor_options->high_key))
         return false;
       cursor_options->high_open = false;
     }
@@ -2485,7 +2486,7 @@
     std::string found_high_key;
     // Seek to the *last* key in the set of non-unique keys
     if (!FindGreatestKeyLessThanOrEqual(
-            transaction, cursor_options->high_key, &found_high_key))
+             transaction, cursor_options->high_key, &found_high_key))
       return false;
 
     // If the target key should not be included, but we end up with a smaller
diff --git a/content/browser/indexed_db/indexed_db_backing_store.h b/content/browser/indexed_db/indexed_db_backing_store.h
index 4498b08..7169d21 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.h
+++ b/content/browser/indexed_db/indexed_db_backing_store.h
@@ -22,6 +22,7 @@
 #include "content/common/indexed_db/indexed_db_key_path.h"
 #include "content/common/indexed_db/indexed_db_key_range.h"
 #include "third_party/WebKit/public/platform/WebIDBCallbacks.h"
+#include "third_party/leveldatabase/src/include/leveldb/status.h"
 
 namespace content {
 
diff --git a/content/browser/indexed_db/indexed_db_database.cc b/content/browser/indexed_db/indexed_db_database.cc
index 78d5b6a..689b2f1 100644
--- a/content/browser/indexed_db/indexed_db_database.cc
+++ b/content/browser/indexed_db/indexed_db_database.cc
@@ -116,14 +116,14 @@
 
 scoped_refptr<IndexedDBDatabase> IndexedDBDatabase::Create(
     const string16& name,
-    IndexedDBBackingStore* database,
+    IndexedDBBackingStore* backing_store,
     IndexedDBFactory* factory,
     const Identifier& unique_identifier) {
-  scoped_refptr<IndexedDBDatabase> backend =
-      new IndexedDBDatabase(name, database, factory, unique_identifier);
-  if (!backend->OpenInternal())
+  scoped_refptr<IndexedDBDatabase> database =
+      new IndexedDBDatabase(name, backing_store, factory, unique_identifier);
+  if (!database->OpenInternal())
     return 0;
-  return backend;
+  return database;
 }
 
 namespace {
@@ -1030,13 +1030,22 @@
 
   scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
   if (params->index_id == IndexedDBIndexMetadata::kInvalidId) {
-    DCHECK_NE(params->cursor_type, indexed_db::CURSOR_KEY_ONLY);
-    backing_store_cursor = backing_store_->OpenObjectStoreCursor(
-        transaction->BackingStoreTransaction(),
-        id(),
-        params->object_store_id,
-        *params->key_range,
+    if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
+      DCHECK_EQ(params->task_type, IndexedDBDatabase::NORMAL_TASK);
+      backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
+          transaction->BackingStoreTransaction(),
+          id(),
+          params->object_store_id,
+          *params->key_range,
+          params->direction);
+    } else {
+      backing_store_cursor = backing_store_->OpenObjectStoreCursor(
+          transaction->BackingStoreTransaction(),
+          id(),
+          params->object_store_id,
+          *params->key_range,
         params->direction);
+    }
   } else {
     DCHECK_EQ(params->task_type, IndexedDBDatabase::NORMAL_TASK);
     if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
diff --git a/content/browser/indexed_db/indexed_db_database.h b/content/browser/indexed_db/indexed_db_database.h
index 7bfa61c..da7039c 100644
--- a/content/browser/indexed_db/indexed_db_database.h
+++ b/content/browser/indexed_db/indexed_db_database.h
@@ -52,7 +52,7 @@
 
   static scoped_refptr<IndexedDBDatabase> Create(
       const string16& name,
-      IndexedDBBackingStore* database,
+      IndexedDBBackingStore* backing_store,
       IndexedDBFactory* factory,
       const Identifier& unique_identifier);
   scoped_refptr<IndexedDBBackingStore> BackingStore() const;
@@ -236,7 +236,7 @@
   friend class base::RefCounted<IndexedDBDatabase>;
 
   IndexedDBDatabase(const string16& name,
-                    IndexedDBBackingStore* database,
+                    IndexedDBBackingStore* backing_store,
                     IndexedDBFactory* factory,
                     const Identifier& unique_identifier);
   ~IndexedDBDatabase();
diff --git a/content/browser/indexed_db/indexed_db_dispatcher_host.h b/content/browser/indexed_db/indexed_db_dispatcher_host.h
index 0731113..e254cad 100644
--- a/content/browser/indexed_db/indexed_db_dispatcher_host.h
+++ b/content/browser/indexed_db/indexed_db_dispatcher_host.h
@@ -12,8 +12,8 @@
 #include "base/id_map.h"
 #include "base/memory/ref_counted.h"
 #include "content/public/browser/browser_message_filter.h"
+#include "url/gurl.h"
 
-class GURL;
 struct IndexedDBDatabaseMetadata;
 struct IndexedDBHostMsg_DatabaseCount_Params;
 struct IndexedDBHostMsg_DatabaseCreateIndex_Params;
diff --git a/content/browser/indexed_db/indexed_db_factory.cc b/content/browser/indexed_db/indexed_db_factory.cc
index 6b8d0c8..7876a88 100644
--- a/content/browser/indexed_db/indexed_db_factory.cc
+++ b/content/browser/indexed_db/indexed_db_factory.cc
@@ -36,9 +36,8 @@
 
 void IndexedDBFactory::RemoveIDBDatabaseBackend(
     const IndexedDBDatabase::Identifier& unique_identifier) {
-  DCHECK(database_backend_map_.find(unique_identifier) !=
-         database_backend_map_.end());
-  database_backend_map_.erase(unique_identifier);
+  DCHECK(database_map_.find(unique_identifier) != database_map_.end());
+  database_map_.erase(unique_identifier);
 }
 
 void IndexedDBFactory::GetDatabaseNames(
@@ -68,9 +67,8 @@
     const base::FilePath& data_directory) {
   IDB_TRACE("IndexedDBFactory::DeleteDatabase");
   IndexedDBDatabase::Identifier unique_identifier(origin_identifier, name);
-  IndexedDBDatabaseMap::iterator it =
-      database_backend_map_.find(unique_identifier);
-  if (it != database_backend_map_.end()) {
+  IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier);
+  if (it != database_map_.end()) {
     // If there are any connections to the database, directly delete the
     // database.
     it->second->DeleteDatabase(callbacks);
@@ -82,26 +80,28 @@
   scoped_refptr<IndexedDBBackingStore> backing_store =
       OpenBackingStore(origin_identifier, data_directory, &data_loss);
   if (!backing_store) {
-    callbacks->OnError(IndexedDBDatabaseError(
-        WebKit::WebIDBDatabaseExceptionUnknownError,
-        ASCIIToUTF16("Internal error opening backing store "
-                     "for indexedDB.deleteDatabase.")));
+    callbacks->OnError(
+        IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
+                               ASCIIToUTF16(
+                                   "Internal error opening backing store "
+                                   "for indexedDB.deleteDatabase.")));
     return;
   }
 
-  scoped_refptr<IndexedDBDatabase> database_backend =
+  scoped_refptr<IndexedDBDatabase> database =
       IndexedDBDatabase::Create(name, backing_store, this, unique_identifier);
-  if (!database_backend) {
+  if (!database) {
     callbacks->OnError(IndexedDBDatabaseError(
         WebKit::WebIDBDatabaseExceptionUnknownError,
-        ASCIIToUTF16("Internal error creating database backend for "
-                     "indexedDB.deleteDatabase.")));
+        ASCIIToUTF16(
+            "Internal error creating database backend for "
+            "indexedDB.deleteDatabase.")));
     return;
   }
 
-  database_backend_map_[unique_identifier] = database_backend;
-  database_backend->DeleteDatabase(callbacks);
-  database_backend_map_.erase(unique_identifier);
+  database_map_[unique_identifier] = database;
+  database->DeleteDatabase(callbacks);
+  database_map_.erase(unique_identifier);
 }
 
 scoped_refptr<IndexedDBBackingStore> IndexedDBFactory::OpenBackingStore(
@@ -150,13 +150,12 @@
     const std::string& origin_identifier,
     const base::FilePath& data_directory) {
   IDB_TRACE("IndexedDBFactory::Open");
-  scoped_refptr<IndexedDBDatabase> database_backend;
+  scoped_refptr<IndexedDBDatabase> database;
   IndexedDBDatabase::Identifier unique_identifier(origin_identifier, name);
-  IndexedDBDatabaseMap::iterator it =
-      database_backend_map_.find(unique_identifier);
+  IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier);
   WebKit::WebIDBCallbacks::DataLoss data_loss =
       WebKit::WebIDBCallbacks::DataLossNone;
-  if (it == database_backend_map_.end()) {
+  if (it == database_map_.end()) {
     scoped_refptr<IndexedDBBackingStore> backing_store =
         OpenBackingStore(origin_identifier, data_directory, &data_loss);
     if (!backing_store) {
@@ -167,9 +166,9 @@
       return;
     }
 
-    database_backend =
+    database =
         IndexedDBDatabase::Create(name, backing_store, this, unique_identifier);
-    if (!database_backend) {
+    if (!database) {
       callbacks->OnError(IndexedDBDatabaseError(
           WebKit::WebIDBDatabaseExceptionUnknownError,
           ASCIIToUTF16(
@@ -177,20 +176,21 @@
       return;
     }
 
-    database_backend_map_[unique_identifier] = database_backend;
+    database_map_[unique_identifier] = database;
   } else {
-    database_backend = it->second;
+    database = it->second;
   }
 
-  database_backend->OpenConnection(
+  database->OpenConnection(
       callbacks, database_callbacks, transaction_id, version, data_loss);
 }
 
 std::vector<IndexedDBDatabase*> IndexedDBFactory::GetOpenDatabasesForOrigin(
     const std::string& origin_identifier) const {
   std::vector<IndexedDBDatabase*> result;
-  for (IndexedDBDatabaseMap::const_iterator it = database_backend_map_.begin();
-       it != database_backend_map_.end(); ++it) {
+  for (IndexedDBDatabaseMap::const_iterator it = database_map_.begin();
+       it != database_map_.end();
+       ++it) {
     if (it->first.first == origin_identifier)
       result.push_back(it->second.get());
   }
diff --git a/content/browser/indexed_db/indexed_db_factory.h b/content/browser/indexed_db/indexed_db_factory.h
index 1666af5..0d02964 100644
--- a/content/browser/indexed_db/indexed_db_factory.h
+++ b/content/browser/indexed_db/indexed_db_factory.h
@@ -65,7 +65,7 @@
  private:
   typedef std::map<IndexedDBDatabase::Identifier,
                    scoped_refptr<IndexedDBDatabase> > IndexedDBDatabaseMap;
-  IndexedDBDatabaseMap database_backend_map_;
+  IndexedDBDatabaseMap database_map_;
 
   typedef std::map<std::string, base::WeakPtr<IndexedDBBackingStore> >
       IndexedDBBackingStoreMap;
diff --git a/content/browser/indexed_db/indexed_db_transaction_coordinator.cc b/content/browser/indexed_db/indexed_db_transaction_coordinator.cc
index 1f5100d..e7ceb3f 100644
--- a/content/browser/indexed_db/indexed_db_transaction_coordinator.cc
+++ b/content/browser/indexed_db/indexed_db_transaction_coordinator.cc
@@ -105,11 +105,17 @@
   }
 }
 
-static bool DoScopesOverlap(const std::set<int64>& scope1,
-                            const std::set<int64>& scope2) {
-  for (std::set<int64>::const_iterator it = scope1.begin(); it != scope1.end();
-       ++it) {
-    if (scope2.find(*it) != scope2.end())
+template<typename T>
+static bool DoSetsIntersect(const std::set<T>& set1,
+                            const std::set<T>& set2) {
+  typename std::set<T>::const_iterator it1 = set1.begin();
+  typename std::set<T>::const_iterator it2 = set2.begin();
+  while (it1 != set1.end() && it2 != set2.end()) {
+    if (*it1 < *it2)
+      ++it1;
+    else if (*it2 < *it1)
+      ++it2;
+    else
       return true;
   }
   return false;
@@ -134,7 +140,7 @@
            ++it) {
         IndexedDBTransaction* other = *it;
         if (other->mode() == indexed_db::TRANSACTION_READ_WRITE &&
-            DoScopesOverlap(transaction->scope(), other->scope()))
+            DoSetsIntersect(transaction->scope(), other->scope()))
           return false;
       }
       for (list_set<IndexedDBTransaction*>::const_iterator it =
@@ -144,7 +150,7 @@
         DCHECK(it != queued_transactions_.end());
         IndexedDBTransaction* other = *it;
         if (other->mode() == indexed_db::TRANSACTION_READ_WRITE &&
-            DoScopesOverlap(transaction->scope(), other->scope()))
+            DoSetsIntersect(transaction->scope(), other->scope()))
           return false;
       }
       return true;
diff --git a/content/browser/indexed_db/leveldb/leveldb_database.cc b/content/browser/indexed_db/leveldb/leveldb_database.cc
index 03d8ca6..cab8473 100644
--- a/content/browser/indexed_db/leveldb/leveldb_database.cc
+++ b/content/browser/indexed_db/leveldb/leveldb_database.cc
@@ -121,9 +121,9 @@
         base::HistogramBase::kUmaTargetedHistogramFlag)->Add(1 /*sample*/);
     return -1;
   }
-  int clamped_disk_space_k_bytes =
-      free_disk_space_in_k_bytes > INT_MAX ? INT_MAX
-                                           : free_disk_space_in_k_bytes;
+  int clamped_disk_space_k_bytes = free_disk_space_in_k_bytes > INT_MAX
+                                       ? INT_MAX
+                                       : free_disk_space_in_k_bytes;
   const uint64 histogram_max = static_cast<uint64>(1e9);
   COMPILE_ASSERT(histogram_max <= INT_MAX, histogram_max_too_big);
   base::Histogram::FactoryGet(name,
@@ -268,11 +268,10 @@
     ParseAndHistogramCorruptionDetails(histogram_name, s);
 }
 
-leveldb::Status LevelDBDatabase::Open(
-    const base::FilePath& file_name,
-    const LevelDBComparator* comparator,
-    scoped_ptr<LevelDBDatabase>* result,
-    bool* is_disk_full) {
+leveldb::Status LevelDBDatabase::Open(const base::FilePath& file_name,
+                                      const LevelDBComparator* comparator,
+                                      scoped_ptr<LevelDBDatabase>* result,
+                                      bool* is_disk_full) {
   scoped_ptr<ComparatorAdapter> comparator_adapter(
       new ComparatorAdapter(comparator));
 
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index d4f6e53..2a4ad9a 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -1813,7 +1813,7 @@
   DelegateMap::iterator it = delegate_map_.find(id);
   DCHECK(it->second->HasObserver(delegate));
   it->second->RemoveObserver(delegate);
-  if (it->second->size() == 0) {
+  if (!it->second->might_have_observers()) {
     delete it->second;
     delegate_map_.erase(it);
   }
diff --git a/content/browser/media/encrypted_media_browsertest.cc b/content/browser/media/encrypted_media_browsertest.cc
index 90d07b2..7a5a24d 100644
--- a/content/browser/media/encrypted_media_browsertest.cc
+++ b/content/browser/media/encrypted_media_browsertest.cc
@@ -267,8 +267,21 @@
 }
 #endif  // defined(USE_PROPRIETARY_CODECS)
 
+IN_PROC_BROWSER_TEST_F(EncryptedMediaTest, UnknownKeySystemThrowsException) {
+  RunEncryptedMediaTest("encrypted_media_player.html", "bear-a-enc_a.webm",
+                        kWebMAudioOnly, "com.example.foo", SRC,
+                        "GENERATE_KEY_REQUEST_EXCEPTION");
+}
+
 // Run only when WV CDM is available.
 #if defined(WIDEVINE_CDM_AVAILABLE)
+// The parent key system cannot be used in generateKeyRequest.
+IN_PROC_BROWSER_TEST_F(EncryptedMediaTest, WVParentThrowsException) {
+  RunEncryptedMediaTest("encrypted_media_player.html", "bear-a-enc_a.webm",
+                        kWebMAudioOnly, "com.widevine", SRC,
+                        "GENERATE_KEY_REQUEST_EXCEPTION");
+}
+
 IN_PROC_BROWSER_TEST_F(WVEncryptedMediaTest, Playback_AudioOnly_WebM) {
   TestMSESimplePlayback("bear-a-enc_a.webm", kWebMAudioOnly,
                         kWidevineKeySystem);
diff --git a/content/browser/media/media_internals_handler.cc b/content/browser/media/media_internals_handler.cc
index 1ea6b04..1715330 100644
--- a/content/browser/media/media_internals_handler.cc
+++ b/content/browser/media/media_internals_handler.cc
@@ -7,7 +7,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/values.h"
-#include "content/browser//media/media_internals_proxy.h"
+#include "content/browser/media/media_internals_proxy.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
diff --git a/content/browser/media/webrtc_browsertest.cc b/content/browser/media/webrtc_browsertest.cc
index 31deb44..db7d86e 100644
--- a/content/browser/media/webrtc_browsertest.cc
+++ b/content/browser/media/webrtc_browsertest.cc
@@ -45,10 +45,17 @@
 
 namespace content {
 
-class WebrtcBrowserTest: public ContentBrowserTest {
+#if defined(OS_LINUX)
+// All tests are flaky on Linux: crbug.com/281492.
+#define MAYBE_WebrtcBrowserTest DISABLED_WebrtcBrowserTest
+#else
+#define MAYBE_WebrtcBrowserTest WebrtcBrowserTest
+#endif
+
+class MAYBE_WebrtcBrowserTest: public ContentBrowserTest {
  public:
-  WebrtcBrowserTest() {}
-  virtual ~WebrtcBrowserTest() {}
+  MAYBE_WebrtcBrowserTest() {}
+  virtual ~MAYBE_WebrtcBrowserTest() {}
 
   virtual void SetUpOnMainThread() OVERRIDE {
     ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
@@ -82,7 +89,7 @@
 // These tests will all make a getUserMedia call with different constraints and
 // see that the success callback is called. If the error callback is called or
 // none of the callbacks are called the tests will simply time out and fail.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, GetVideoStreamAndStop) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebrtcBrowserTest, GetVideoStreamAndStop) {
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
   NavigateToURL(shell(), url);
 
@@ -91,7 +98,8 @@
   ExpectTitle("OK");
 }
 
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, GetAudioAndVideoStreamAndStop) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebrtcBrowserTest,
+                       GetAudioAndVideoStreamAndStop) {
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
   NavigateToURL(shell(), url);
 
@@ -100,7 +108,8 @@
   ExpectTitle("OK");
 }
 
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, GetAudioAndVideoStreamAndClone) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebrtcBrowserTest,
+                       GetAudioAndVideoStreamAndClone) {
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
   NavigateToURL(shell(), url);
 
@@ -119,7 +128,7 @@
 
 // These tests will make a complete PeerConnection-based call and verify that
 // video is playing for the call.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CanSetupVideoCall) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebrtcBrowserTest, MAYBE_CanSetupVideoCall) {
   GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
   NavigateToURL(shell(), url);
 
@@ -134,7 +143,8 @@
 #define MAYBE_CanSetupAudioAndVideoCall CanSetupAudioAndVideoCall
 #endif
 
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CanSetupAudioAndVideoCall) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebrtcBrowserTest,
+                       MAYBE_CanSetupAudioAndVideoCall) {
   GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
   NavigateToURL(shell(), url);
 
@@ -142,7 +152,8 @@
   ExpectTitle("OK");
 }
 
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MANUAL_CanSetupCallAndSendDtmf) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebrtcBrowserTest,
+                       MANUAL_CanSetupCallAndSendDtmf) {
   GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
   NavigateToURL(shell(), url);
 
@@ -150,7 +161,7 @@
       ExecuteJavascript("callAndSendDtmf('123,abc');"));
 }
 
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
+IN_PROC_BROWSER_TEST_F(MAYBE_WebrtcBrowserTest,
                        CanMakeEmptyCallThenAddStreamsAndRenegotiate) {
   GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
   NavigateToURL(shell(), url);
@@ -177,7 +188,7 @@
 #define MAYBE_CanSetupAudioAndVideoCallWithoutMsidAndBundle\
         CanSetupAudioAndVideoCallWithoutMsidAndBundle
 #endif
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest,
+IN_PROC_BROWSER_TEST_F(MAYBE_WebrtcBrowserTest,
                        MAYBE_CanSetupAudioAndVideoCallWithoutMsidAndBundle) {
   GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
   NavigateToURL(shell(), url);
@@ -188,7 +199,7 @@
 
 // This test will make a PeerConnection-based call and test an unreliable text
 // dataChannel.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, CallWithDataOnly) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebrtcBrowserTest, CallWithDataOnly) {
   GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
   NavigateToURL(shell(), url);
 
@@ -205,7 +216,7 @@
 
 // This test will make a PeerConnection-based call and test an unreliable text
 // dataChannel and audio and video tracks.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CallWithDataAndMedia) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebrtcBrowserTest, MAYBE_CallWithDataAndMedia) {
   GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
   NavigateToURL(shell(), url);
 
@@ -222,7 +233,8 @@
 
 // This test will make a PeerConnection-based call and test an unreliable text
 // dataChannel and later add an audio and video track.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CallWithDataAndLaterAddMedia) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebrtcBrowserTest,
+                       MAYBE_CallWithDataAndLaterAddMedia) {
   GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
   NavigateToURL(shell(), url);
 
@@ -240,7 +252,8 @@
 // This test will make a PeerConnection-based call and send a new Video
 // MediaStream that has been created based on a MediaStream created with
 // getUserMedia.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MAYBE_CallWithNewVideoMediaStream) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebrtcBrowserTest,
+                       MAYBE_CallWithNewVideoMediaStream) {
   GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
   NavigateToURL(shell(), url);
 
@@ -254,7 +267,7 @@
 // AudioTrack is added instead.
 // TODO(phoglund): This test is manual since not all buildbots has an audio
 // input.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, MANUAL_CallAndModifyStream) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebrtcBrowserTest, MANUAL_CallAndModifyStream) {
   GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
   NavigateToURL(shell(), url);
 
@@ -264,7 +277,7 @@
 }
 
 // This test calls getUserMedia in sequence with different constraints.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, TestGetUserMediaConstraints) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebrtcBrowserTest, TestGetUserMediaConstraints) {
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
 
   std::vector<std::string> list_of_get_user_media_calls;
@@ -295,7 +308,7 @@
 }
 
 // This test calls getUserMedia and checks for aspect ratio behavior.
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, TestGetUserMediaAspectRatio) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebrtcBrowserTest, TestGetUserMediaAspectRatio) {
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
 
   std::string constraints_4_3 = GenerateGetUserMediaCall(
@@ -314,7 +327,7 @@
   ExpectTitle("16:9 letterbox");
 }
 
-IN_PROC_BROWSER_TEST_F(WebrtcBrowserTest, AddTwoMediaStreamsToOnePC) {
+IN_PROC_BROWSER_TEST_F(MAYBE_WebrtcBrowserTest, AddTwoMediaStreamsToOnePC) {
   GURL url(embedded_test_server()->GetURL("/media/peerconnection-call.html"));
   NavigateToURL(shell(), url);
 
diff --git a/content/browser/media/webrtc_internals.cc b/content/browser/media/webrtc_internals.cc
index f8bcdc3..e94447a 100644
--- a/content/browser/media/webrtc_internals.cc
+++ b/content/browser/media/webrtc_internals.cc
@@ -65,7 +65,7 @@
   dict->SetString("url", url);
   peer_connection_data_.Append(dict);
 
-  if (observers_.size() > 0)
+  if (observers_.might_have_observers())
     SendUpdate("addPeerConnection", dict);
 }
 
@@ -85,7 +85,7 @@
 
     peer_connection_data_.Remove(i, NULL);
 
-    if (observers_.size() > 0) {
+    if (observers_.might_have_observers()) {
       base::DictionaryValue id;
       id.SetInteger("pid", static_cast<int>(pid));
       id.SetInteger("lid", lid);
@@ -123,7 +123,7 @@
     log_entry->SetString("value", value);
     log->Append(log_entry);
 
-    if (observers_.size() > 0) {
+    if (observers_.might_have_observers()) {
       base::DictionaryValue update;
       update.SetInteger("pid", static_cast<int>(pid));
       update.SetInteger("lid", lid);
@@ -138,7 +138,7 @@
 
 void WebRTCInternals::OnAddStats(base::ProcessId pid, int lid,
                                  const base::ListValue& value) {
-  if (observers_.size() == 0)
+  if (!observers_.might_have_observers())
     return;
 
   base::DictionaryValue dict;
@@ -165,7 +165,7 @@
 }
 
 void WebRTCInternals::SendAllUpdates() {
-  if (observers_.size() > 0)
+  if (observers_.might_have_observers())
     SendUpdate("updateAllPeerConnections", &peer_connection_data_);
 }
 
@@ -184,7 +184,7 @@
 }
 
 void WebRTCInternals::SendUpdate(const string& command, base::Value* value) {
-  DCHECK_GT(observers_.size(), (size_t)0);
+  DCHECK(observers_.might_have_observers());
 
   FOR_EACH_OBSERVER(WebRTCInternalsUIObserver,
                     observers_,
@@ -216,7 +216,7 @@
     record->GetInteger("rid", &this_rid);
 
     if (this_rid == render_process_id) {
-      if (observers_.size() > 0) {
+      if (observers_.might_have_observers()) {
         int lid = 0, pid = 0;
         record->GetInteger("lid", &lid);
         record->GetInteger("pid", &pid);
diff --git a/content/browser/media/webrtc_internals_browsertest.cc b/content/browser/media/webrtc_internals_browsertest.cc
index b458276..6a17ea0 100644
--- a/content/browser/media/webrtc_internals_browsertest.cc
+++ b/content/browser/media/webrtc_internals_browsertest.cc
@@ -118,8 +118,9 @@
 
 static const int64 FAKE_TIME_STAMP = 3600000;
 
-#if defined(OS_WIN)
+#if defined(OS_WIN) || defined(OS_LINUX)
 // All tests are flaky on Windows: crbug.com/277322.
+// All tests are flaky on Linux: crbug.com/281492.
 #define MAYBE_WebRTCInternalsBrowserTest DISABLED_WebRTCInternalsBrowserTest
 #else
 #define MAYBE_WebRTCInternalsBrowserTest WebRTCInternalsBrowserTest
diff --git a/content/browser/net/sqlite_persistent_cookie_store_perftest.cc b/content/browser/net/sqlite_persistent_cookie_store_perftest.cc
index 4f62706..8c4cb8e 100644
--- a/content/browser/net/sqlite_persistent_cookie_store_perftest.cc
+++ b/content/browser/net/sqlite_persistent_cookie_store_perftest.cc
@@ -7,10 +7,10 @@
 #include "base/bind.h"
 #include "base/compiler_specific.h"
 #include "base/files/scoped_temp_dir.h"
-#include "base/perftimer.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/test/perftimer.h"
 #include "base/test/sequenced_worker_pool_owner.h"
 #include "base/threading/sequenced_worker_pool.h"
 #include "net/cookies/canonical_cookie.h"
diff --git a/content/browser/notification_service_impl.cc b/content/browser/notification_service_impl.cc
index 4913288..27de857 100644
--- a/content/browser/notification_service_impl.cc
+++ b/content/browser/notification_service_impl.cc
@@ -83,7 +83,7 @@
       observers_[type][source.map_key()];
   if (observer_list) {
     observer_list->RemoveObserver(observer);
-    if (!observer_list->size()) {
+    if (!observer_list->might_have_observers()) {
       observers_[type].erase(source.map_key());
       delete observer_list;
     }
diff --git a/content/browser/plugin_service_impl.cc b/content/browser/plugin_service_impl.cc
index 6682a4d..3837223 100644
--- a/content/browser/plugin_service_impl.cc
+++ b/content/browser/plugin_service_impl.cc
@@ -328,8 +328,7 @@
 PpapiPluginProcessHost* PluginServiceImpl::FindOrStartPpapiPluginProcess(
     int render_process_id,
     const base::FilePath& plugin_path,
-    const base::FilePath& profile_data_directory,
-    PpapiPluginProcessHost::PluginClient* client) {
+    const base::FilePath& profile_data_directory) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
 
   if (filter_ && !filter_->CanLoadPlugin(render_process_id, plugin_path))
@@ -356,8 +355,7 @@
 
   // This plugin isn't loaded by any plugin process, so create a new process.
   return PpapiPluginProcessHost::CreatePluginHost(
-      *info, profile_data_directory,
-      client->GetResourceContext()->GetHostResolver());
+      *info, profile_data_directory);
 }
 
 PpapiPluginProcessHost* PluginServiceImpl::FindOrStartPpapiBrokerProcess(
@@ -413,7 +411,7 @@
     const base::FilePath& profile_data_directory,
     PpapiPluginProcessHost::PluginClient* client) {
   PpapiPluginProcessHost* plugin_host = FindOrStartPpapiPluginProcess(
-      render_process_id, plugin_path, profile_data_directory, client);
+      render_process_id, plugin_path, profile_data_directory);
   if (plugin_host) {
     plugin_host->OpenChannelToPlugin(client);
   } else {
diff --git a/content/browser/plugin_service_impl.h b/content/browser/plugin_service_impl.h
index 6d358fd..11fb257 100644
--- a/content/browser/plugin_service_impl.h
+++ b/content/browser/plugin_service_impl.h
@@ -131,8 +131,7 @@
   PpapiPluginProcessHost* FindOrStartPpapiPluginProcess(
       int render_process_id,
       const base::FilePath& plugin_path,
-      const base::FilePath& profile_data_directory,
-      PpapiPluginProcessHost::PluginClient* client);
+      const base::FilePath& profile_data_directory);
   PpapiPluginProcessHost* FindOrStartPpapiBrokerProcess(
       int render_process_id, const base::FilePath& plugin_path);
 
diff --git a/content/browser/ppapi_plugin_process_host.cc b/content/browser/ppapi_plugin_process_host.cc
index c9aa699..30dd182 100644
--- a/content/browser/ppapi_plugin_process_host.cc
+++ b/content/browser/ppapi_plugin_process_host.cc
@@ -114,10 +114,9 @@
 // static
 PpapiPluginProcessHost* PpapiPluginProcessHost::CreatePluginHost(
     const PepperPluginInfo& info,
-    const base::FilePath& profile_data_directory,
-    net::HostResolver* host_resolver) {
+    const base::FilePath& profile_data_directory) {
   PpapiPluginProcessHost* plugin_host = new PpapiPluginProcessHost(
-      info, profile_data_directory, host_resolver);
+      info, profile_data_directory);
   if (plugin_host->Init(info))
     return plugin_host;
 
@@ -206,8 +205,7 @@
 
 PpapiPluginProcessHost::PpapiPluginProcessHost(
     const PepperPluginInfo& info,
-    const base::FilePath& profile_data_directory,
-    net::HostResolver* host_resolver)
+    const base::FilePath& profile_data_directory)
     : permissions_(
           ppapi::PpapiPermissions::GetForCommandLine(info.permissions)),
       profile_data_directory_(profile_data_directory),
@@ -215,13 +213,11 @@
   process_.reset(new BrowserChildProcessHostImpl(
       PROCESS_TYPE_PPAPI_PLUGIN, this));
 
-  filter_ = new PepperMessageFilter(permissions_, host_resolver);
-
   host_impl_.reset(new BrowserPpapiHostImpl(this, permissions_, info.name,
                                             info.path, profile_data_directory,
-                                            false,
-                                            filter_));
+                                            false));
 
+  filter_ = PepperMessageFilter::CreateForPpapiPluginProcess(permissions_);
   process_->GetHost()->AddFilter(filter_.get());
   process_->GetHost()->AddFilter(host_impl_->message_filter().get());
 
@@ -243,8 +239,7 @@
   host_impl_.reset(new BrowserPpapiHostImpl(this, permissions,
                                             std::string(), base::FilePath(),
                                             base::FilePath(),
-                                            false,
-                                            NULL));
+                                            false));
 }
 
 bool PpapiPluginProcessHost::Init(const PepperPluginInfo& info) {
diff --git a/content/browser/ppapi_plugin_process_host.h b/content/browser/ppapi_plugin_process_host.h
index 78d6bd6..87c79c3 100644
--- a/content/browser/ppapi_plugin_process_host.h
+++ b/content/browser/ppapi_plugin_process_host.h
@@ -21,10 +21,6 @@
 #include "ipc/ipc_sender.h"
 #include "ppapi/shared_impl/ppapi_permissions.h"
 
-namespace net {
-class HostResolver;
-}
-
 namespace content {
 class BrowserChildProcessHostImpl;
 class ResourceContext;
@@ -76,8 +72,7 @@
 
   static PpapiPluginProcessHost* CreatePluginHost(
       const PepperPluginInfo& info,
-      const base::FilePath& profile_data_directory,
-      net::HostResolver* host_resolver);
+      const base::FilePath& profile_data_directory);
   static PpapiPluginProcessHost* CreateBrokerHost(
       const PepperPluginInfo& info);
 
@@ -122,8 +117,7 @@
   // Constructors for plugin and broker process hosts, respectively.
   // You must call Init before doing anything else.
   PpapiPluginProcessHost(const PepperPluginInfo& info,
-                         const base::FilePath& profile_data_directory,
-                         net::HostResolver* host_resolver);
+                         const base::FilePath& profile_data_directory);
   PpapiPluginProcessHost();
 
   // Actually launches the process with the given plugin info. Returns true
diff --git a/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.cc b/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.cc
index 1bcc822..e456337 100644
--- a/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.cc
+++ b/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.cc
@@ -26,9 +26,9 @@
   if (pixels_scrolled_ >= pixels_to_scroll_)
     return false;
 
-  double position_delta = smooth_scroll_calculator_.GetScrollDelta(
+  float position_delta = synthetic_gesture_calculator_.GetDelta(
       now,
-      RenderWidgetHostImpl::From(host)->GetSyntheticScrollMessageInterval());
+      RenderWidgetHostImpl::From(host)->GetSyntheticGestureMessageInterval());
 
 
   WebKit::WebMouseWheelEvent event;
diff --git a/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.h b/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.h
index adea7a3..ba127da 100644
--- a/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.h
+++ b/content/browser/renderer_host/basic_mouse_wheel_smooth_scroll_gesture.h
@@ -6,14 +6,14 @@
 #define CONTENT_BROWSER_RENDERER_HOST_BASIC_MOUSE_WHEEL_SMOOTH_SCROLL_GESTURE_
 
 #include "base/time/time.h"
-#include "content/browser/renderer_host/smooth_scroll_calculator.h"
-#include "content/port/browser/smooth_scroll_gesture.h"
+#include "content/browser/renderer_host/synthetic_gesture_calculator.h"
+#include "content/port/browser/synthetic_gesture.h"
 
 namespace content {
 
 class RenderWidgetHost;
 
-class BasicMouseWheelSmoothScrollGesture : public SmoothScrollGesture {
+class BasicMouseWheelSmoothScrollGesture : public SyntheticGesture {
  public:
   BasicMouseWheelSmoothScrollGesture(bool scroll_down, int pixels_to_scroll,
                                      int mouse_event_x, int mouse_event_y);
@@ -23,7 +23,7 @@
  private:
   virtual ~BasicMouseWheelSmoothScrollGesture();
 
-  SmoothScrollCalculator smooth_scroll_calculator_;
+  SyntheticGestureCalculator synthetic_gesture_calculator_;
 
   bool scroll_down_;
   int pixels_scrolled_;
diff --git a/content/browser/renderer_host/clipboard_message_filter.cc b/content/browser/renderer_host/clipboard_message_filter.cc
index 2ad3546..ea50db1 100644
--- a/content/browser/renderer_host/clipboard_message_filter.cc
+++ b/content/browser/renderer_host/clipboard_message_filter.cc
@@ -11,7 +11,6 @@
 #include "content/public/browser/browser_context.h"
 #include "ipc/ipc_message_macros.h"
 #include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/zlib/zlib.h"
 #include "ui/gfx/codec/png_codec.h"
 #include "ui/gfx/size.h"
 #include "url/gurl.h"
@@ -200,16 +199,7 @@
   uint32 image_size = 0;
   if (!bitmap.isNull()) {
     std::vector<unsigned char> png_data;
-    SkAutoLockPixels lock(bitmap);
-    if (gfx::PNGCodec::EncodeWithCompressionLevel(
-            static_cast<const unsigned char*>(bitmap.getPixels()),
-            gfx::PNGCodec::FORMAT_BGRA,
-            gfx::Size(bitmap.width(), bitmap.height()),
-            bitmap.rowBytes(),
-            false,
-            std::vector<gfx::PNGCodec::Comment>(),
-            Z_BEST_SPEED,
-            &png_data)) {
+    if (gfx::PNGCodec::FastEncodeBGRASkBitmap(bitmap, false, &png_data)) {
       base::SharedMemory buffer;
       if (buffer.CreateAndMapAnonymous(png_data.size())) {
         memcpy(buffer.memory(), vector_as_array(&png_data), png_data.size());
diff --git a/content/browser/renderer_host/generic_touch_gesture_android.cc b/content/browser/renderer_host/generic_touch_gesture_android.cc
new file mode 100644
index 0000000..74080cd
--- /dev/null
+++ b/content/browser/renderer_host/generic_touch_gesture_android.cc
@@ -0,0 +1,62 @@
+// 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 "content/browser/renderer_host/generic_touch_gesture_android.h"
+
+#include "base/debug/trace_event.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "jni/GenericTouchGesture_jni.h"
+
+namespace {
+bool g_jni_initialized = false;
+
+void RegisterNativesIfNeeded(JNIEnv* env) {
+  if (!g_jni_initialized) {
+    content::RegisterNativesImpl(env);
+    g_jni_initialized = true;
+  }
+}
+}  // namespace
+
+namespace content {
+
+GenericTouchGestureAndroid::GenericTouchGestureAndroid(
+    RenderWidgetHost* rwh,
+    base::android::ScopedJavaLocalRef<jobject> java_touch_gesture)
+    : has_started_(false),
+      has_finished_(false),
+      rwh_(rwh),
+      java_touch_gesture_(java_touch_gesture) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  RegisterNativesIfNeeded(env);
+}
+
+GenericTouchGestureAndroid::~GenericTouchGestureAndroid() {
+}
+
+bool GenericTouchGestureAndroid::ForwardInputEvents(
+    base::TimeTicks now, RenderWidgetHost* host) {
+  if (!has_started_) {
+    has_started_ = true;
+    JNIEnv* env = base::android::AttachCurrentThread();
+    Java_GenericTouchGesture_start(
+        env, java_touch_gesture_.obj(), reinterpret_cast<intptr_t>(this));
+  }
+
+  return !has_finished_;
+}
+
+float GenericTouchGestureAndroid::GetDelta(
+    JNIEnv* env, jobject obj, float scale) {
+  return synthetic_gesture_calculator_.GetDelta(
+      base::TimeTicks::Now(),
+      RenderWidgetHostImpl::From(rwh_)->GetSyntheticGestureMessageInterval()) *
+      scale;
+}
+
+void GenericTouchGestureAndroid::SetHasFinished(JNIEnv* env, jobject obj) {
+  has_finished_ = true;
+}
+
+}  // namespace content
diff --git a/content/browser/renderer_host/generic_touch_gesture_android.h b/content/browser/renderer_host/generic_touch_gesture_android.h
new file mode 100644
index 0000000..030239b
--- /dev/null
+++ b/content/browser/renderer_host/generic_touch_gesture_android.h
@@ -0,0 +1,48 @@
+// 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 CONTENT_BROWSER_RENDERER_HOST_GENERIC_TOUCH_GESTURE_ANDROID_H_
+#define CONTENT_BROWSER_RENDERER_HOST_GENERIC_TOUCH_GESTURE_ANDROID_H_
+
+#include "base/android/jni_android.h"
+#include "base/time/time.h"
+#include "content/browser/renderer_host/synthetic_gesture_calculator.h"
+#include "content/port/browser/synthetic_gesture.h"
+
+namespace content {
+
+class ContentViewCore;
+class RenderWidgetHost;
+
+class GenericTouchGestureAndroid : public SyntheticGesture {
+ public:
+  GenericTouchGestureAndroid(
+      RenderWidgetHost* rwh,
+      base::android::ScopedJavaLocalRef<jobject> java_scroller);
+
+  // Called by the java side once the TimeAnimator ticks.
+  float GetDelta(JNIEnv* env, jobject obj, float scale);
+  void SetHasFinished(JNIEnv* env, jobject obj);
+
+  // SmoothScrollGesture
+  virtual bool ForwardInputEvents(base::TimeTicks now,
+                                  RenderWidgetHost* host) OVERRIDE;
+
+ private:
+  virtual ~GenericTouchGestureAndroid();
+
+  SyntheticGestureCalculator synthetic_gesture_calculator_;
+
+  bool has_started_;
+  bool has_finished_;
+
+  RenderWidgetHost* rwh_;
+  base::android::ScopedJavaGlobalRef<jobject> java_touch_gesture_;
+
+  DISALLOW_COPY_AND_ASSIGN(GenericTouchGestureAndroid);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_RENDERER_HOST_GENERIC_TOUCH_GESTURE_ANDROID_H_
diff --git a/content/browser/renderer_host/input/web_input_event_builders_win.cc b/content/browser/renderer_host/input/web_input_event_builders_win.cc
index a5a9b20..ba081fd 100644
--- a/content/browser/renderer_host/input/web_input_event_builders_win.cc
+++ b/content/browser/renderer_host/input/web_input_event_builders_win.cc
@@ -428,20 +428,22 @@
   // reading articles.
   static const float kScrollbarPixelsPerLine = 100.0f / 3.0f;
   wheel_delta /= WHEEL_DELTA;
-  float scroll_delta = wheel_delta * kScrollbarPixelsPerLine;
+  float scroll_delta = wheel_delta;
   if (horizontal_scroll) {
     unsigned long scroll_chars = kDefaultScrollCharsPerWheelDelta;
     SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &scroll_chars, 0);
     // TODO(pkasting): Should probably have a different multiplier
     // scrollbarPixelsPerChar here.
-    scroll_delta *= static_cast<float>(scroll_chars);
+    scroll_delta *= static_cast<float>(scroll_chars) * kScrollbarPixelsPerLine;
   } else {
     unsigned long scroll_lines = kDefaultScrollLinesPerWheelDelta;
     SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scroll_lines, 0);
     if (scroll_lines == WHEEL_PAGESCROLL)
       result.scrollByPage = true;
-    if (!result.scrollByPage)
-      scroll_delta *= static_cast<float>(scroll_lines);
+    if (!result.scrollByPage) {
+      scroll_delta *=
+          static_cast<float>(scroll_lines) * kScrollbarPixelsPerLine;
+    }
   }
 
   // Set scroll amount based on above calculations.  WebKit expects positive
diff --git a/content/browser/renderer_host/media/midi_dispatcher_host.cc b/content/browser/renderer_host/media/midi_dispatcher_host.cc
index 8ecbabc..53f5a76 100644
--- a/content/browser/renderer_host/media/midi_dispatcher_host.cc
+++ b/content/browser/renderer_host/media/midi_dispatcher_host.cc
@@ -5,6 +5,7 @@
 #include "content/browser/renderer_host/media/midi_dispatcher_host.h"
 
 #include "base/bind.h"
+#include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/common/media/midi_messages.h"
 #include "content/public/browser/browser_context.h"
@@ -57,6 +58,8 @@
 void MIDIDispatcherHost::WasSysExPermissionGranted(int render_view_id,
                                                    int client_id,
                                                    bool success) {
+  ChildProcessSecurityPolicyImpl::GetInstance()->GrantSendMIDISysExMessage(
+      render_process_id_);
   Send(new MIDIMsg_SysExPermissionApproved(render_view_id, client_id, success));
 }
 
diff --git a/content/browser/renderer_host/media/midi_host.cc b/content/browser/renderer_host/media/midi_host.cc
index b0b0e76..e03b3df 100644
--- a/content/browser/renderer_host/media/midi_host.cc
+++ b/content/browser/renderer_host/media/midi_host.cc
@@ -9,10 +9,12 @@
 #include "base/debug/trace_event.h"
 #include "base/process/process.h"
 #include "content/browser/browser_main_loop.h"
+#include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/media/media_internals.h"
 #include "content/common/media/midi_messages.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/media_observer.h"
+#include "content/public/browser/user_metrics.h"
 #include "media/midi/midi_manager.h"
 
 using media::MIDIManager;
@@ -32,8 +34,9 @@
 
 namespace content {
 
-MIDIHost::MIDIHost(media::MIDIManager* midi_manager)
-    : midi_manager_(midi_manager),
+MIDIHost::MIDIHost(int renderer_process_id, media::MIDIManager* midi_manager)
+    : renderer_process_id_(renderer_process_id),
+      midi_manager_(midi_manager),
       sent_bytes_in_flight_(0),
       bytes_sent_since_last_acknowledgement_(0) {
 }
@@ -88,7 +91,7 @@
        output_ports));
 }
 
-void MIDIHost::OnSendData(int port,
+void MIDIHost::OnSendData(uint32 port,
                           const std::vector<uint8>& data,
                           double timestamp) {
   if (!midi_manager_)
@@ -105,26 +108,29 @@
       data.size() + sent_bytes_in_flight_ > kMaxInFlightBytes)
     return;
 
-  // For now disallow all System Exclusive messages even if we
-  // have permission.
-  // TODO(toyoshim): allow System Exclusive if browser has granted
-  // this client access.  We'll likely need to pass a GURL
-  // here to compare against our permissions.
-  if (data[0] >= kSysExMessage)
+  if (data[0] >= kSysExMessage) {
+    // Blink running in a renderer checks permission to raise a SecurityError in
+    // JavaScript. The actual permission check for security perposes happens
+    // here in the browser process.
+    if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanSendMIDISysExMessage(
+        renderer_process_id_)) {
+      RecordAction(UserMetricsAction("BadMessageTerminate_MIDI"));
+      BadMessageReceived();
       return;
+    }
+  }
 
   midi_manager_->DispatchSendMIDIData(
       this,
       port,
-      &data[0],
-      data.size(),
+      data,
       timestamp);
 
   sent_bytes_in_flight_ += data.size();
 }
 
 void MIDIHost::ReceiveMIDIData(
-    int port_index,
+    uint32 port,
     const uint8* data,
     size_t length,
     double timestamp) {
@@ -140,7 +146,7 @@
 
   // Send to the renderer.
   std::vector<uint8> v(data, data + length);
-  Send(new MIDIMsg_DataReceived(port_index, v, timestamp));
+  Send(new MIDIMsg_DataReceived(port, v, timestamp));
 }
 
 void MIDIHost::AccumulateMIDIBytesSent(size_t n) {
diff --git a/content/browser/renderer_host/media/midi_host.h b/content/browser/renderer_host/media/midi_host.h
index f6b2813..944325d 100644
--- a/content/browser/renderer_host/media/midi_host.h
+++ b/content/browser/renderer_host/media/midi_host.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_MIDI_HOST_H_
 #define CONTENT_BROWSER_RENDERER_HOST_MEDIA_MIDI_HOST_H_
 
+#include <vector>
+
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
 #include "content/common/content_export.h"
@@ -23,7 +25,7 @@
       public media::MIDIManagerClient {
  public:
   // Called from UI thread from the owner of this object.
-  MIDIHost(media::MIDIManager* midi_manager);
+  MIDIHost(int renderer_process_id, media::MIDIManager* midi_manager);
 
   // BrowserMessageFilter implementation.
   virtual void OnChannelClosing() OVERRIDE;
@@ -33,7 +35,7 @@
 
   // MIDIManagerClient implementation.
   virtual void ReceiveMIDIData(
-      int port_index,
+      uint32 port,
       const uint8* data,
       size_t length,
       double timestamp) OVERRIDE;
@@ -43,7 +45,7 @@
   void OnStartSession(int client_id);
 
   // Data to be sent to a MIDI output port.
-  void OnSendData(int port,
+  void OnSendData(uint32 port,
                   const std::vector<uint8>& data,
                   double timestamp);
 
@@ -53,6 +55,8 @@
 
   virtual ~MIDIHost();
 
+  int renderer_process_id_;
+
   // |midi_manager_| talks to the platform-specific MIDI APIs.
   // It can be NULL if the platform (or our current implementation)
   // does not support MIDI.  If not supported then a call to
diff --git a/content/browser/renderer_host/media/video_capture_buffer_pool.cc b/content/browser/renderer_host/media/video_capture_buffer_pool.cc
index 207f86a..3e30834 100644
--- a/content/browser/renderer_host/media/video_capture_buffer_pool.cc
+++ b/content/browser/renderer_host/media/video_capture_buffer_pool.cc
@@ -128,8 +128,12 @@
 scoped_refptr<media::VideoFrame> VideoCaptureBufferPool::ReserveI420VideoFrame(
     const gfx::Size& size,
     int rotation) {
-  if (static_cast<size_t>(size.GetArea() * 3 / 2) != GetMemorySize())
+  if (GetMemorySize() !=
+      media::VideoFrame::AllocationSize(media::VideoFrame::I420, size)) {
+    DCHECK_EQ(GetMemorySize(),
+              media::VideoFrame::AllocationSize(media::VideoFrame::I420, size));
     return NULL;
+  }
 
   base::AutoLock lock(lock_);
 
@@ -151,6 +155,7 @@
           gfx::Rect(size),
           size,
           static_cast<uint8*>(buffer->shared_memory.memory()),
+          GetMemorySize(),
           buffer->shared_memory.handle(),
           base::TimeDelta(),
           disposal_handler);
diff --git a/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc b/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc
index 67a8be4..30509a3 100644
--- a/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc
@@ -21,8 +21,8 @@
   scoped_refptr<media::VideoFrame> non_pool_frame =
       media::VideoFrame::CreateFrame(media::VideoFrame::YV12, size,
                                      gfx::Rect(size), size, base::TimeDelta());
-  scoped_refptr<VideoCaptureBufferPool> pool =
-      new VideoCaptureBufferPool(size.GetArea() * 3 / 2, 3);
+  scoped_refptr<VideoCaptureBufferPool> pool = new VideoCaptureBufferPool(
+      media::VideoFrame::AllocationSize(media::VideoFrame::I420, size), 3);
 
   ASSERT_EQ(460800u, pool->GetMemorySize());
   ASSERT_TRUE(pool->Allocate());
diff --git a/content/browser/renderer_host/media/video_capture_controller.cc b/content/browser/renderer_host/media/video_capture_controller.cc
index bac4328..3efc4ce 100644
--- a/content/browser/renderer_host/media/video_capture_controller.cc
+++ b/content/browser/renderer_host/media/video_capture_controller.cc
@@ -573,8 +573,11 @@
     return;
 
   scoped_refptr<VideoCaptureBufferPool> buffer_pool =
-      new VideoCaptureBufferPool(frame_info_.width * frame_info_.height * 3 / 2,
-                                 kNoOfBuffers);
+      new VideoCaptureBufferPool(
+          media::VideoFrame::AllocationSize(
+              media::VideoFrame::I420,
+              gfx::Size(frame_info_.width, frame_info_.height)),
+          kNoOfBuffers);
 
   // Check whether all buffers were created successfully.
   if (!buffer_pool->Allocate()) {
diff --git a/content/browser/renderer_host/media/video_capture_host_unittest.cc b/content/browser/renderer_host/media/video_capture_host_unittest.cc
index 762148c..499423e 100644
--- a/content/browser/renderer_host/media/video_capture_host_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_host_unittest.cc
@@ -20,6 +20,7 @@
 #include "content/public/test/mock_resource_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "media/audio/audio_manager.h"
+#include "media/base/video_frame.h"
 #include "media/video/capture/video_capture_types.h"
 #include "net/url_request/url_request_context.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -57,7 +58,8 @@
     base::FilePath file_name = base::FilePath(base::StringPrintf(
         FILE_PATH_LITERAL("dump_w%d_h%d.yuv"), width, height));
     file_.reset(file_util::OpenFile(file_name, "wb"));
-    expected_size_ = width * height * 3 / 2;
+    expected_size_ = media::VideoFrame::AllocationSize(
+        media::VideoFrame::I420, gfx::Size(width, height));
   }
   void NewVideoFrame(const void* buffer) {
     if (file_.get() != NULL) {
@@ -146,7 +148,8 @@
   // These handler methods do minimal things and delegate to the mock methods.
   void OnNewBufferCreatedDispatch(int device_id,
                                   base::SharedMemoryHandle handle,
-                                  int length, int buffer_id) {
+                                  uint32 length,
+                                  int buffer_id) {
     OnNewBufferCreated(device_id, handle, length, buffer_id);
     base::SharedMemory* dib = new base::SharedMemory(handle, false);
     dib->Map(length);
diff --git a/content/browser/renderer_host/media/web_contents_video_capture_device.cc b/content/browser/renderer_host/media/web_contents_video_capture_device.cc
index e27b703..2788bb2 100644
--- a/content/browser/renderer_host/media/web_contents_video_capture_device.cc
+++ b/content/browser/renderer_host/media/web_contents_video_capture_device.cc
@@ -56,8 +56,8 @@
 
 #include "base/basictypes.h"
 #include "base/bind.h"
-#include "base/bind_helpers.h"
 #include "base/callback_forward.h"
+#include "base/callback_helpers.h"
 #include "base/debug/trace_event.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
@@ -693,7 +693,7 @@
   }
 
   // The result is now ready.
-  failure_handler.Release();
+  ignore_result(failure_handler.Release());
   done_cb.Run(true);
 }
 
diff --git a/content/browser/renderer_host/media/web_contents_video_capture_device_unittest.cc b/content/browser/renderer_host/media/web_contents_video_capture_device_unittest.cc
index d88d553..bc5645d 100644
--- a/content/browser/renderer_host/media/web_contents_video_capture_device_unittest.cc
+++ b/content/browser/renderer_host/media/web_contents_video_capture_device_unittest.cc
@@ -310,8 +310,10 @@
   StubConsumer()
       : error_encountered_(false),
         wait_color_yuv_(0xcafe1950) {
-    buffer_pool_ =
-        new VideoCaptureBufferPool(kTestWidth * kTestHeight * 3 / 2, 2);
+    buffer_pool_ = new VideoCaptureBufferPool(
+        media::VideoFrame::AllocationSize(media::VideoFrame::I420,
+                                          gfx::Size(kTestWidth, kTestHeight)),
+        2);
     EXPECT_TRUE(buffer_pool_->Allocate());
   }
   virtual ~StubConsumer() {}
diff --git a/content/browser/renderer_host/native_web_keyboard_event_aura.cc b/content/browser/renderer_host/native_web_keyboard_event_aura.cc
index 1658d8d..f3113cc 100644
--- a/content/browser/renderer_host/native_web_keyboard_event_aura.cc
+++ b/content/browser/renderer_host/native_web_keyboard_event_aura.cc
@@ -77,7 +77,8 @@
   nativeKeyCode = character;
   text[0] = character;
   unmodifiedText[0] = character;
-  isSystemKey = (state & ui::EF_ALT_DOWN) != 0;
+  isSystemKey =
+      (state & ui::EF_ALT_DOWN) != 0 && (state & ui::EF_ALTGR_DOWN) == 0;
   setKeyIdentifierFromWindowsKeyCode();
 }
 
diff --git a/content/browser/renderer_host/overscroll_configuration.cc b/content/browser/renderer_host/overscroll_configuration.cc
index 77585e5..f7c5579 100644
--- a/content/browser/renderer_host/overscroll_configuration.cc
+++ b/content/browser/renderer_host/overscroll_configuration.cc
@@ -11,7 +11,8 @@
 float g_horiz_threshold_complete = 0.25f;
 float g_vert_threshold_complete = 0.20f;
 
-float g_horiz_threshold_start = 50.f;
+float g_horiz_threshold_start_touchscreen = 50.f;
+float g_horiz_threshold_start_touchpad = 50.f;
 float g_vert_threshold_start = 0.f;
 
 float g_horiz_resist_after = 30.f;
@@ -31,8 +32,12 @@
       g_vert_threshold_complete = value;
       break;
 
-    case OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START:
-      g_horiz_threshold_start = value;
+    case OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN:
+      g_horiz_threshold_start_touchscreen = value;
+      break;
+
+    case OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD:
+      g_horiz_threshold_start_touchpad = value;
       break;
 
     case OVERSCROLL_CONFIG_VERT_THRESHOLD_START:
@@ -61,8 +66,11 @@
     case OVERSCROLL_CONFIG_VERT_THRESHOLD_COMPLETE:
       return g_vert_threshold_complete;
 
-    case OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START:
-      return g_horiz_threshold_start;
+    case OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN:
+      return g_horiz_threshold_start_touchscreen;
+
+    case OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD:
+      return g_horiz_threshold_start_touchpad;
 
     case OVERSCROLL_CONFIG_VERT_THRESHOLD_START:
       return g_vert_threshold_start;
diff --git a/content/browser/renderer_host/overscroll_controller.cc b/content/browser/renderer_host/overscroll_controller.cc
index 3fed4c3..51ff7df 100644
--- a/content/browser/renderer_host/overscroll_controller.cc
+++ b/content/browser/renderer_host/overscroll_controller.cc
@@ -9,6 +9,8 @@
 #include "content/public/browser/overscroll_configuration.h"
 #include "content/public/browser/render_widget_host_view.h"
 
+using WebKit::WebInputEvent;
+
 namespace content {
 
 OverscrollController::OverscrollController(
@@ -234,14 +236,16 @@
         return;
 
       ProcessOverscroll(wheel.deltaX * wheel.accelerationRatioX,
-                        wheel.deltaY * wheel.accelerationRatioY);
+                        wheel.deltaY * wheel.accelerationRatioY,
+                        wheel.type);
       break;
     }
     case WebKit::WebInputEvent::GestureScrollUpdate: {
       const WebKit::WebGestureEvent& gesture =
           static_cast<const WebKit::WebGestureEvent&>(event);
       ProcessOverscroll(gesture.data.scrollUpdate.deltaX,
-                        gesture.data.scrollUpdate.deltaY);
+                        gesture.data.scrollUpdate.deltaY,
+                        gesture.type);
       break;
     }
     case WebKit::WebInputEvent::GestureFlingStart: {
@@ -276,13 +280,17 @@
   }
 }
 
-void OverscrollController::ProcessOverscroll(float delta_x, float delta_y) {
+void OverscrollController::ProcessOverscroll(float delta_x,
+                                             float delta_y,
+                                             WebKit::WebInputEvent::Type type) {
   if (scroll_state_ != STATE_CONTENT_SCROLLING)
     overscroll_delta_x_ += delta_x;
   overscroll_delta_y_ += delta_y;
 
   float horiz_threshold = GetOverscrollConfig(
-      OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START);
+      WebInputEvent::isGestureEventType(type) ?
+          OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN :
+          OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD);
   float vert_threshold = GetOverscrollConfig(
       OVERSCROLL_CONFIG_VERT_THRESHOLD_START);
   if (fabs(overscroll_delta_x_) <= horiz_threshold &&
diff --git a/content/browser/renderer_host/overscroll_controller.h b/content/browser/renderer_host/overscroll_controller.h
index fc1fc5e..9c8282e 100644
--- a/content/browser/renderer_host/overscroll_controller.h
+++ b/content/browser/renderer_host/overscroll_controller.h
@@ -95,7 +95,9 @@
   // Processes horizontal overscroll. This can update both the overscroll mode
   // and the over scroll amount (i.e. |overscroll_mode_|, |overscroll_delta_x_|
   // and |overscroll_delta_y_|).
-  void ProcessOverscroll(float delta_x, float delta_y);
+  void ProcessOverscroll(float delta_x,
+                         float delta_y,
+                         WebKit::WebInputEvent::Type event_type);
 
   // Completes the desired action from the current gesture.
   void CompleteAction();
diff --git a/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc b/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc
index 10eef8b..f23965f 100644
--- a/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc
+++ b/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc
@@ -4,6 +4,7 @@
 
 #include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h"
 
+#include "content/browser/renderer_host/pepper/pepper_message_filter.h"
 #include "content/browser/tracing/trace_message_filter.h"
 #include "content/common/pepper_renderer_instance_data.h"
 #include "content/public/browser/render_view_host.h"
@@ -18,23 +19,17 @@
     ppapi::PpapiPermissions permissions,
     base::ProcessHandle plugin_child_process,
     IPC::ChannelProxy* channel,
-    net::HostResolver* host_resolver,
     int render_process_id,
     int render_view_id,
     const base::FilePath& profile_directory) {
-  scoped_refptr<PepperMessageFilter> pepper_message_filter(
-      new PepperMessageFilter(permissions,
-                              host_resolver,
-                              render_process_id,
-                              render_view_id));
-
   // The plugin name and path shouldn't be needed for external plugins.
   BrowserPpapiHostImpl* browser_ppapi_host =
       new BrowserPpapiHostImpl(sender, permissions, std::string(),
-                               base::FilePath(), profile_directory, true,
-                               pepper_message_filter);
+                               base::FilePath(), profile_directory, true);
   browser_ppapi_host->set_plugin_process_handle(plugin_child_process);
 
+  scoped_refptr<PepperMessageFilter> pepper_message_filter(
+      PepperMessageFilter::CreateForExternalPluginProcess(permissions));
   channel->AddFilter(pepper_message_filter);
   channel->AddFilter(browser_ppapi_host->message_filter().get());
   channel->AddFilter(new TraceMessageFilter());
@@ -48,17 +43,17 @@
     const std::string& plugin_name,
     const base::FilePath& plugin_path,
     const base::FilePath& profile_data_directory,
-    bool external_plugin,
-    const scoped_refptr<PepperMessageFilter>& pepper_message_filter)
+    bool external_plugin)
     : ppapi_host_(new ppapi::host::PpapiHost(sender, permissions)),
       plugin_process_handle_(base::kNullProcessHandle),
       plugin_name_(plugin_name),
       plugin_path_(plugin_path),
       profile_data_directory_(profile_data_directory),
-      external_plugin_(external_plugin) {
+      external_plugin_(external_plugin),
+      ssl_context_helper_(new SSLContextHelper()) {
   message_filter_ = new HostMessageFilter(ppapi_host_.get());
   ppapi_host_->AddHostFactoryFilter(scoped_ptr<ppapi::host::HostFactory>(
-      new ContentBrowserPepperHostFactory(this, pepper_message_filter)));
+      new ContentBrowserPepperHostFactory(this)));
 }
 
 BrowserPpapiHostImpl::~BrowserPpapiHostImpl() {
diff --git a/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h b/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h
index 9723634..f16d1d8 100644
--- a/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h
+++ b/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h
@@ -13,8 +13,9 @@
 #include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
 #include "content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h"
-#include "content/browser/renderer_host/pepper/pepper_message_filter.h"
+#include "content/browser/renderer_host/pepper/ssl_context_helper.h"
 #include "content/common/content_export.h"
+#include "content/common/pepper_renderer_instance_data.h"
 #include "content/public/browser/browser_ppapi_host.h"
 #include "content/public/common/process_type.h"
 #include "ipc/ipc_channel_proxy.h"
@@ -22,8 +23,6 @@
 
 namespace content {
 
-struct PepperRendererInstanceData;
-
 class CONTENT_EXPORT BrowserPpapiHostImpl : public BrowserPpapiHost {
  public:
   // The creator is responsible for calling set_plugin_process_handle as soon
@@ -37,10 +36,7 @@
       const std::string& plugin_name,
       const base::FilePath& plugin_path,
       const base::FilePath& profile_data_directory,
-      bool external_plugin,
-      // TODO (ygorshenin@): remove this once TCP sockets are
-      // converted to the new design.
-      const scoped_refptr<PepperMessageFilter>& pepper_message_filter);
+      bool external_plugin);
   virtual ~BrowserPpapiHostImpl();
 
   // BrowserPpapiHost.
@@ -73,6 +69,10 @@
     return message_filter_;
   }
 
+  const scoped_refptr<SSLContextHelper>& ssl_context_helper() const {
+    return ssl_context_helper_;
+  }
+
  private:
   friend class BrowserPpapiHostTest;
 
@@ -104,6 +104,8 @@
   // BrowserPpapiHost::CreateExternalPluginProcess.
   bool external_plugin_;
 
+  scoped_refptr<SSLContextHelper> ssl_context_helper_;
+
   // Tracks all PP_Instances in this plugin and associated renderer-related
   // data.
   typedef std::map<PP_Instance, PepperRendererInstanceData> InstanceMap;
diff --git a/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc b/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc
index a8c257a..c338a99 100644
--- a/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc
+++ b/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc
@@ -16,8 +16,7 @@
                         std::string(),
                         base::FilePath(),
                         base::FilePath(),
-                        false,
-                        NULL));
+                        false));
   ppapi_host_->set_plugin_process_handle(base::GetCurrentProcessHandle());
 }
 
diff --git a/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc b/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc
index f0cefc4..97e3173 100644
--- a/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc
+++ b/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.cc
@@ -15,8 +15,10 @@
 #include "content/browser/renderer_host/pepper/pepper_print_settings_manager.h"
 #include "content/browser/renderer_host/pepper/pepper_printing_host.h"
 #include "content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h"
+#include "content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h"
 #include "content/browser/renderer_host/pepper/pepper_truetype_font_list_host.h"
 #include "content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.h"
+#include "net/socket/stream_socket.h"
 #include "ppapi/host/message_filter_host.h"
 #include "ppapi/host/ppapi_host.h"
 #include "ppapi/host/resource_host.h"
@@ -36,18 +38,17 @@
 
 bool CanCreateSocket() {
   return
-      PepperUDPSocketMessageFilter::GetNumInstances() +
-      PepperTCPServerSocketMessageFilter::GetNumInstances() <
+      PepperTCPServerSocketMessageFilter::GetNumInstances() +
+      PepperTCPSocketMessageFilter::GetNumInstances() +
+      PepperUDPSocketMessageFilter::GetNumInstances() <
       kMaxSocketsAllowed;
 }
 
 }  // namespace
 
 ContentBrowserPepperHostFactory::ContentBrowserPepperHostFactory(
-    BrowserPpapiHostImpl* host,
-    const scoped_refptr<PepperMessageFilter>& pepper_message_filter)
-    : host_(host),
-      pepper_message_filter_(pepper_message_filter) {
+    BrowserPpapiHostImpl* host)
+    : host_(host) {
 }
 
 ContentBrowserPepperHostFactory::~ContentBrowserPepperHostFactory() {
@@ -102,6 +103,18 @@
       return scoped_ptr<ResourceHost>(new PepperFileRefHost(
           host_, instance, params.pp_resource(), file_system, internal_path));
     }
+    case PpapiHostMsg_TCPSocket_Create::ID: {
+      if (CanCreateSocket()) {
+        scoped_refptr<ResourceMessageFilter> tcp_socket(
+            new PepperTCPSocketMessageFilter(host_,
+                                             instance,
+                                             false));
+        return scoped_ptr<ResourceHost>(new MessageFilterHost(
+            host_->GetPpapiHost(), instance, params.pp_resource(), tcp_socket));
+      } else {
+        return scoped_ptr<ResourceHost>();
+      }
+    }
     case PpapiHostMsg_UDPSocket_Create::ID: {
       if (CanCreateSocket()) {
         scoped_refptr<ResourceMessageFilter> udp_socket(
@@ -155,8 +168,7 @@
   if (message.type() == PpapiHostMsg_TCPServerSocket_CreatePrivate::ID) {
     if (CanCreateSocket()) {
       scoped_refptr<ResourceMessageFilter> tcp_server_socket(
-          new PepperTCPServerSocketMessageFilter(host_, instance, true,
-                                                 pepper_message_filter_));
+          new PepperTCPServerSocketMessageFilter(this, host_, instance, true));
       return scoped_ptr<ResourceHost>(new MessageFilterHost(
           host_->GetPpapiHost(), instance, params.pp_resource(),
           tcp_server_socket));
@@ -164,6 +176,18 @@
       return scoped_ptr<ResourceHost>();
     }
   }
+  if (message.type() == PpapiHostMsg_TCPSocket_CreatePrivate::ID) {
+    if (CanCreateSocket()) {
+      scoped_refptr<ResourceMessageFilter> tcp_socket(
+          new PepperTCPSocketMessageFilter(host_,
+                                           instance,
+                                           true));
+      return scoped_ptr<ResourceHost>(new MessageFilterHost(
+          host_->GetPpapiHost(), instance, params.pp_resource(), tcp_socket));
+    } else {
+      return scoped_ptr<ResourceHost>();
+    }
+  }
   if (message.type() == PpapiHostMsg_UDPSocket_CreatePrivate::ID) {
     if (CanCreateSocket()) {
       scoped_refptr<ResourceMessageFilter> udp_socket(
@@ -191,6 +215,24 @@
   return scoped_ptr<ResourceHost>();
 }
 
+scoped_ptr<ppapi::host::ResourceHost>
+ContentBrowserPepperHostFactory::CreateAcceptedTCPSocket(
+    PP_Instance instance,
+    bool private_api,
+    net::StreamSocket* socket) {
+  scoped_ptr<net::StreamSocket> s(socket);
+
+  if (!CanCreateSocket())
+    return scoped_ptr<ResourceHost>();
+  scoped_refptr<ResourceMessageFilter> tcp_socket(
+      new PepperTCPSocketMessageFilter(host_,
+                                       instance,
+                                       private_api,
+                                       s.release()));
+  return scoped_ptr<ResourceHost>(new MessageFilterHost(
+      host_->GetPpapiHost(), instance, 0, tcp_socket));
+}
+
 const ppapi::PpapiPermissions&
 ContentBrowserPepperHostFactory::GetPermissions() const {
   return host_->GetPpapiHost()->permissions();
diff --git a/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h b/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h
index e39ec35..64c8b42 100644
--- a/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h
+++ b/content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h
@@ -7,9 +7,13 @@
 
 #include "base/compiler_specific.h"
 #include "base/memory/ref_counted.h"
-#include "content/browser/renderer_host/pepper/pepper_message_filter.h"
+#include "base/memory/scoped_ptr.h"
 #include "ppapi/host/host_factory.h"
 
+namespace net {
+class StreamSocket;
+}
+
 namespace ppapi {
 class PpapiPermissions;
 }
@@ -21,11 +25,8 @@
 class ContentBrowserPepperHostFactory : public ppapi::host::HostFactory {
  public:
   // Non-owning pointer to the filter must outlive this class.
-  ContentBrowserPepperHostFactory(
-      BrowserPpapiHostImpl* host,
-      // TODO (ygorshenin@): remove this once TCP sockets are
-      // converted to the new design.
-      const scoped_refptr<PepperMessageFilter>& pepper_message_filter);
+  explicit ContentBrowserPepperHostFactory(BrowserPpapiHostImpl* host);
+
   virtual ~ContentBrowserPepperHostFactory();
 
   virtual scoped_ptr<ppapi::host::ResourceHost> CreateResourceHost(
@@ -34,14 +35,20 @@
       PP_Instance instance,
       const IPC::Message& message) OVERRIDE;
 
+  // Creates ResourceHost for already accepted TCP |socket|.  Takes
+  // ownership of the |socket|. In the case of failure returns wrapped
+  // NULL.
+  scoped_ptr<ppapi::host::ResourceHost> CreateAcceptedTCPSocket(
+      PP_Instance instance,
+      bool private_api,
+      net::StreamSocket* socket);
+
  private:
   const ppapi::PpapiPermissions& GetPermissions() const;
 
   // Non-owning pointer.
   BrowserPpapiHostImpl* host_;
 
-  scoped_refptr<PepperMessageFilter> pepper_message_filter_;
-
   DISALLOW_COPY_AND_ASSIGN(ContentBrowserPepperHostFactory);
 };
 
diff --git a/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc b/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc
index 42344be..6afddb1 100644
--- a/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc
+++ b/content/browser/renderer_host/pepper/pepper_file_system_browser_host.cc
@@ -151,6 +151,18 @@
   fs_context_ = fs_context;
 }
 
+void PepperFileSystemBrowserHost::GotIsolatedFileSystemContext(
+    ppapi::host::ReplyMessageContext reply_context,
+    scoped_refptr<fileapi::FileSystemContext> fs_context) {
+  fs_context_ = fs_context;
+  if (fs_context.get())
+    reply_context.params.set_result(PP_OK);
+  else
+    reply_context.params.set_result(PP_ERROR_FAILED);
+  host()->SendReply(reply_context,
+                    PpapiPluginMsg_FileSystem_InitIsolatedFileSystemReply());
+}
+
 void PepperFileSystemBrowserHost::OpenFileSystemComplete(
     ppapi::host::ReplyMessageContext reply_context,
     base::PlatformFileError error,
@@ -177,7 +189,22 @@
   root_url_ = GURL(fileapi::GetIsolatedFileSystemRootURIString(
       url.GetOrigin(), fsid, "crxfs"));
   opened_ = true;
-  return PP_OK;
+
+  int render_process_id = 0;
+  int unused;
+  if (!browser_ppapi_host_->GetRenderViewIDsForInstance(pp_instance(),
+                                                        &render_process_id,
+                                                        &unused)) {
+    return PP_ERROR_FAILED;
+  }
+  BrowserThread::PostTaskAndReplyWithResult(
+      BrowserThread::UI,
+      FROM_HERE,
+      base::Bind(&GetFileSystemContextFromRenderId, render_process_id),
+      base::Bind(&PepperFileSystemBrowserHost::GotIsolatedFileSystemContext,
+                 weak_factory_.GetWeakPtr(),
+                 context->MakeReplyMessageContext()));
+  return PP_OK_COMPLETIONPENDING;
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h b/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h
index 525dd06..ed648fb 100644
--- a/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h
+++ b/content/browser/renderer_host/pepper/pepper_file_system_browser_host.h
@@ -47,6 +47,9 @@
       ppapi::host::ReplyMessageContext reply_context,
       fileapi::FileSystemType file_system_type,
       scoped_refptr<fileapi::FileSystemContext> fs_context);
+  void GotIsolatedFileSystemContext(
+      ppapi::host::ReplyMessageContext reply_context,
+      scoped_refptr<fileapi::FileSystemContext> fs_context);
   void OpenFileSystemComplete(
       ppapi::host::ReplyMessageContext reply_context,
       base::PlatformFileError error,
diff --git a/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.cc b/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.cc
index 337807d..0c3bd89 100644
--- a/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.cc
+++ b/content/browser/renderer_host/pepper/pepper_internal_file_ref_backend.cc
@@ -63,7 +63,10 @@
   if (!fs_url_.is_valid() && fs_host_.get() && fs_host_->IsOpened()) {
     GURL fs_path = fs_host_->GetRootUrl().Resolve(
         net::EscapePath(path_.substr(1)));
-    fs_url_ = GetFileSystemContext()->CrackURL(fs_path);
+    scoped_refptr<fileapi::FileSystemContext> fs_context =
+        GetFileSystemContext();
+    if (fs_context.get())
+      fs_url_ = fs_context->CrackURL(fs_path);
   }
   return fs_url_;
 }
@@ -83,7 +86,6 @@
 
 scoped_refptr<fileapi::FileSystemContext>
 PepperInternalFileRefBackend::GetFileSystemContext() const {
-  // TODO(teravest): Make this work for CRX file systems.
   if (!fs_host_.get())
     return NULL;
   return fs_host_->GetFileSystemContext();
diff --git a/content/browser/renderer_host/pepper/pepper_message_filter.cc b/content/browser/renderer_host/pepper/pepper_message_filter.cc
index 717de35..698decf 100644
--- a/content/browser/renderer_host/pepper/pepper_message_filter.cc
+++ b/content/browser/renderer_host/pepper/pepper_message_filter.cc
@@ -6,110 +6,44 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/callback.h"
-#include "base/compiler_specific.h"
-#include "base/files/file_path.h"
 #include "base/logging.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/threading/sequenced_worker_pool.h"
-#include "base/threading/worker_pool.h"
-#include "build/build_config.h"
 #include "content/browser/renderer_host/pepper/pepper_socket_utils.h"
-#include "content/browser/renderer_host/pepper/pepper_tcp_socket.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/common/pepper_messages.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/common/content_client.h"
-#include "net/base/address_family.h"
-#include "net/base/address_list.h"
-#include "net/base/host_port_pair.h"
-#include "net/base/sys_addrinfo.h"
-#include "net/cert/cert_verifier.h"
-#include "ppapi/c/pp_errors.h"
-#include "ppapi/c/private/ppb_net_address_private.h"
+#include "ppapi/c/private/ppb_network_list_private.h"
 #include "ppapi/proxy/ppapi_messages.h"
 #include "ppapi/shared_impl/api_id.h"
 #include "ppapi/shared_impl/private/net_address_private_impl.h"
-#include "ppapi/shared_impl/socket_option_data.h"
 
 using ppapi::NetAddressPrivateImpl;
 
 namespace content {
-namespace {
 
-const size_t kMaxSocketsAllowed = 1024;
-const uint32 kInvalidSocketID = 0;
-
-}  // namespace
-
-PepperMessageFilter::PepperMessageFilter(int process_id,
-                                         BrowserContext* browser_context)
-    : plugin_type_(PLUGIN_TYPE_IN_PROCESS),
-      permissions_(),
-      process_id_(process_id),
-      external_plugin_render_view_id_(0),
-      resource_context_(browser_context->GetResourceContext()),
-      host_resolver_(NULL),
-      next_socket_id_(1) {
-  DCHECK(browser_context);
-  // Keep BrowserContext data in FILE-thread friendly storage.
-  browser_path_ = browser_context->GetPath();
-  incognito_ = browser_context->IsOffTheRecord();
-  DCHECK(resource_context_);
+// static
+PepperMessageFilter* PepperMessageFilter::CreateForRendererProcess() {
+  return new PepperMessageFilter(ppapi::PpapiPermissions(),
+                                 PLUGIN_TYPE_IN_PROCESS);
 }
 
-PepperMessageFilter::PepperMessageFilter(
-    const ppapi::PpapiPermissions& permissions,
-    net::HostResolver* host_resolver)
-    : plugin_type_(PLUGIN_TYPE_OUT_OF_PROCESS),
-      permissions_(permissions),
-      process_id_(0),
-      external_plugin_render_view_id_(0),
-      resource_context_(NULL),
-      host_resolver_(host_resolver),
-      next_socket_id_(1),
-      incognito_(false) {
-  DCHECK(host_resolver);
+// static
+PepperMessageFilter* PepperMessageFilter::CreateForPpapiPluginProcess(
+    const ppapi::PpapiPermissions& permissions) {
+  return new PepperMessageFilter(permissions,
+                                 PLUGIN_TYPE_OUT_OF_PROCESS);
 }
 
-PepperMessageFilter::PepperMessageFilter(
-    const ppapi::PpapiPermissions& permissions,
-    net::HostResolver* host_resolver,
-    int process_id,
-    int render_view_id)
-    : plugin_type_(PLUGIN_TYPE_EXTERNAL_PLUGIN),
-      permissions_(permissions),
-      process_id_(process_id),
-      external_plugin_render_view_id_(render_view_id),
-      resource_context_(NULL),
-      host_resolver_(host_resolver),
-      next_socket_id_(1) {
-  DCHECK(host_resolver);
+// static
+PepperMessageFilter* PepperMessageFilter::CreateForExternalPluginProcess(
+    const ppapi::PpapiPermissions& permissions) {
+  return new PepperMessageFilter(permissions,
+                                 PLUGIN_TYPE_EXTERNAL_PLUGIN);
 }
 
 bool PepperMessageFilter::OnMessageReceived(const IPC::Message& msg,
                                             bool* message_was_ok) {
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP_EX(PepperMessageFilter, msg, *message_was_ok)
-    // TCP messages.
-    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_Create, OnTCPCreate)
-    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_CreatePrivate,
-                        OnTCPCreatePrivate)
-    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_Connect, OnTCPConnect)
-    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_ConnectWithNetAddress,
-                        OnTCPConnectWithNetAddress)
-    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_SSLHandshake,
-                        OnTCPSSLHandshake)
-    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_Read, OnTCPRead)
-    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_Write, OnTCPWrite)
-    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_Disconnect, OnTCPDisconnect)
-    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBTCPSocket_SetOption, OnTCPSetOption)
-
     // NetworkMonitor messages.
     IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBNetworkMonitor_Start,
                         OnNetworkMonitorStart)
@@ -129,44 +63,11 @@
   GetAndSendNetworkList();
 }
 
-net::HostResolver* PepperMessageFilter::GetHostResolver() {
-  return resource_context_ ?
-      resource_context_->GetHostResolver() : host_resolver_;
-}
-
-net::CertVerifier* PepperMessageFilter::GetCertVerifier() {
-  if (!cert_verifier_)
-    cert_verifier_.reset(net::CertVerifier::CreateDefault());
-
-  return cert_verifier_.get();
-}
-
-net::TransportSecurityState* PepperMessageFilter::GetTransportSecurityState() {
-  if (!transport_security_state_)
-    transport_security_state_.reset(new net::TransportSecurityState);
-
-  return transport_security_state_.get();
-}
-
-uint32 PepperMessageFilter::AddAcceptedTCPSocket(
-    int32 routing_id,
-    uint32 plugin_dispatcher_id,
-    net::StreamSocket* socket) {
-  scoped_ptr<net::StreamSocket> s(socket);
-
-  uint32 tcp_socket_id = GenerateSocketID();
-  if (tcp_socket_id != kInvalidSocketID) {
-    // Currently all TCP sockets created this way correspond to
-    // PPB_TCPSocket_Private.
-    tcp_sockets_[tcp_socket_id] = linked_ptr<PepperTCPSocket>(
-        new PepperTCPSocket(this,
-                            routing_id,
-                            plugin_dispatcher_id,
-                            tcp_socket_id,
-                            s.release(),
-                            true /* private_api */));
-  }
-  return tcp_socket_id;
+PepperMessageFilter::PepperMessageFilter(
+    const ppapi::PpapiPermissions& permissions,
+    PluginType plugin_type)
+    : plugin_type_(plugin_type),
+      permissions_(permissions) {
 }
 
 PepperMessageFilter::~PepperMessageFilter() {
@@ -174,176 +75,6 @@
     net::NetworkChangeNotifier::RemoveIPAddressObserver(this);
 }
 
-void PepperMessageFilter::OnTCPCreate(int32 routing_id,
-                                      uint32 plugin_dispatcher_id,
-                                      uint32* socket_id) {
-  CreateTCPSocket(routing_id, plugin_dispatcher_id, false, socket_id);
-}
-
-void PepperMessageFilter::OnTCPCreatePrivate(int32 routing_id,
-                                             uint32 plugin_dispatcher_id,
-                                             uint32* socket_id) {
-  CreateTCPSocket(routing_id, plugin_dispatcher_id, true, socket_id);
-}
-
-void PepperMessageFilter::OnTCPConnect(int32 routing_id,
-                                       uint32 socket_id,
-                                       const std::string& host,
-                                       uint16_t port) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id);
-  if (iter == tcp_sockets_.end()) {
-    NOTREACHED();
-    return;
-  }
-
-  // This is only supported by PPB_TCPSocket_Private.
-  if (!iter->second->private_api()) {
-    NOTREACHED();
-    return;
-  }
-
-  content::SocketPermissionRequest params(
-      content::SocketPermissionRequest::TCP_CONNECT, host, port);
-  BrowserThread::PostTaskAndReplyWithResult(
-      BrowserThread::UI, FROM_HERE,
-      base::Bind(&PepperMessageFilter::CanUseSocketAPIs, this,
-                 routing_id, params, true /* private_api */),
-      base::Bind(&PepperMessageFilter::DoTCPConnect, this,
-                 routing_id, socket_id, host, port));
-}
-
-void PepperMessageFilter::DoTCPConnect(int32 routing_id,
-                                       uint32 socket_id,
-                                       const std::string& host,
-                                       uint16_t port,
-                                       bool allowed) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id);
-  if (iter == tcp_sockets_.end()) {
-    // Due to current permission check process (IO -> UI -> IO) some
-    // calls to the TCP socket interface can be intermixed (like
-    // Connect and Close). So, NOTREACHED() is not appropriate here.
-    return;
-  }
-
-  if (routing_id == iter->second->routing_id() && allowed)
-    iter->second->Connect(host, port);
-  else
-    iter->second->SendConnectACKError(PP_ERROR_NOACCESS);
-}
-
-void PepperMessageFilter::OnTCPConnectWithNetAddress(
-    int32 routing_id,
-    uint32 socket_id,
-    const PP_NetAddress_Private& net_addr) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id);
-  if (iter == tcp_sockets_.end()) {
-    NOTREACHED();
-    return;
-  }
-
-  content::SocketPermissionRequest params =
-      pepper_socket_utils::CreateSocketPermissionRequest(
-          content::SocketPermissionRequest::TCP_CONNECT, net_addr);
-  BrowserThread::PostTaskAndReplyWithResult(
-      BrowserThread::UI, FROM_HERE,
-      base::Bind(&PepperMessageFilter::CanUseSocketAPIs, this,
-                 routing_id, params, iter->second->private_api()),
-      base::Bind(&PepperMessageFilter::DoTCPConnectWithNetAddress, this,
-                 routing_id, socket_id, net_addr));
-}
-
-void PepperMessageFilter::DoTCPConnectWithNetAddress(
-    int32 routing_id,
-    uint32 socket_id,
-    const PP_NetAddress_Private& net_addr,
-    bool allowed) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
-  TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id);
-  if (iter == tcp_sockets_.end()) {
-    // Due to current permission check process (IO -> UI -> IO) some
-    // calls to the TCP socket interface can be intermixed (like
-    // ConnectWithNetAddress and Close). So, NOTREACHED() is not
-    // appropriate here.
-    return;
-  }
-
-  if (routing_id == iter->second->routing_id() && allowed)
-    iter->second->ConnectWithNetAddress(net_addr);
-  else
-    iter->second->SendConnectACKError(PP_ERROR_NOACCESS);
-}
-
-void PepperMessageFilter::OnTCPSSLHandshake(
-    uint32 socket_id,
-    const std::string& server_name,
-    uint16_t server_port,
-    const std::vector<std::vector<char> >& trusted_certs,
-    const std::vector<std::vector<char> >& untrusted_certs) {
-  TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id);
-  if (iter == tcp_sockets_.end()) {
-    NOTREACHED();
-    return;
-  }
-
-  // This is only supported by PPB_TCPSocket_Private.
-  if (!iter->second->private_api()) {
-    NOTREACHED();
-    return;
-  }
-
-  iter->second->SSLHandshake(server_name, server_port, trusted_certs,
-                             untrusted_certs);
-}
-
-void PepperMessageFilter::OnTCPRead(uint32 socket_id, int32_t bytes_to_read) {
-  TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id);
-  if (iter == tcp_sockets_.end()) {
-    NOTREACHED();
-    return;
-  }
-
-  iter->second->Read(bytes_to_read);
-}
-
-void PepperMessageFilter::OnTCPWrite(uint32 socket_id,
-                                     const std::string& data) {
-  TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id);
-  if (iter == tcp_sockets_.end()) {
-    NOTREACHED();
-    return;
-  }
-
-  iter->second->Write(data);
-}
-
-void PepperMessageFilter::OnTCPDisconnect(uint32 socket_id) {
-  TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id);
-  if (iter == tcp_sockets_.end()) {
-    NOTREACHED();
-    return;
-  }
-
-  // Destroying the TCPSocket instance will cancel any pending completion
-  // callback. From this point on, there won't be any messages associated with
-  // this socket sent to the plugin side.
-  tcp_sockets_.erase(iter);
-}
-
-void PepperMessageFilter::OnTCPSetOption(uint32 socket_id,
-                                         PP_TCPSocket_Option name,
-                                         const ppapi::SocketOptionData& value) {
-  TCPSocketMap::iterator iter = tcp_sockets_.find(socket_id);
-  if (iter == tcp_sockets_.end()) {
-    NOTREACHED();
-    return;
-  }
-
-  iter->second->SetOption(name, value);
-}
-
 void PepperMessageFilter::OnNetworkMonitorStart(uint32 plugin_dispatcher_id) {
   // Support all in-process plugins, and ones with "private" permissions.
   if (plugin_type_ != PLUGIN_TYPE_IN_PROCESS &&
@@ -376,56 +107,8 @@
     ppapi::PPB_X509Certificate_Fields* result) {
   if (der.size() == 0)
     *succeeded = false;
-  *succeeded = PepperTCPSocket::GetCertificateFields(&der[0], der.size(),
-                                                     result);
-}
-
-uint32 PepperMessageFilter::GenerateSocketID() {
-  // TODO(yzshen): Change to use Pepper resource ID as socket ID.
-  //
-  // Generate a socket ID. For each process which sends us socket requests, IDs
-  // of living sockets must be unique, to each socket type.
-  //
-  // However, it is safe to generate IDs based on the internal state of a single
-  // PepperSocketMessageHandler object, because for each plugin or renderer
-  // process, there is at most one PepperMessageFilter (in other words, at most
-  // one PepperSocketMessageHandler) talking to it.
-  if (tcp_sockets_.size() >= kMaxSocketsAllowed)
-    return kInvalidSocketID;
-
-  uint32 socket_id = kInvalidSocketID;
-  do {
-    // Although it is unlikely, make sure that we won't cause any trouble when
-    // the counter overflows.
-    socket_id = next_socket_id_++;
-  } while (socket_id == kInvalidSocketID ||
-           tcp_sockets_.find(socket_id) != tcp_sockets_.end());
-
-  return socket_id;
-}
-
-bool PepperMessageFilter::CanUseSocketAPIs(
-    int32 render_id,
-    const content::SocketPermissionRequest& params,
-    bool private_api) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  // External plugins always get their own PepperMessageFilter, initialized with
-  // a render view id. Use this instead of the one that came with the message,
-  // which is actually an API ID.
-  bool external_plugin = false;
-  if (plugin_type_ == PLUGIN_TYPE_EXTERNAL_PLUGIN) {
-    external_plugin = true;
-    render_id = external_plugin_render_view_id_;
-  }
-
-  RenderViewHostImpl* render_view_host =
-      RenderViewHostImpl::FromID(process_id_, render_id);
-
-  return pepper_socket_utils::CanUseSocketAPIs(external_plugin,
-                                               private_api,
-                                               params,
-                                               render_view_host);
+  *succeeded =
+      pepper_socket_utils::GetCertificateFields(&der[0], der.size(), result);
 }
 
 void PepperMessageFilter::GetAndSendNetworkList() {
@@ -475,17 +158,4 @@
   }
 }
 
-void PepperMessageFilter::CreateTCPSocket(int32 routing_id,
-                                          uint32 plugin_dispatcher_id,
-                                          bool private_api,
-                                          uint32* socket_id) {
-  *socket_id = GenerateSocketID();
-  if (*socket_id == kInvalidSocketID)
-    return;
-
-  tcp_sockets_[*socket_id] = linked_ptr<PepperTCPSocket>(
-      new PepperTCPSocket(this, routing_id, plugin_dispatcher_id, *socket_id,
-                          private_api));
-}
-
 }  // namespace content
diff --git a/content/browser/renderer_host/pepper/pepper_message_filter.h b/content/browser/renderer_host/pepper/pepper_message_filter.h
index 11c8fbd..3d17193 100644
--- a/content/browser/renderer_host/pepper/pepper_message_filter.h
+++ b/content/browser/renderer_host/pepper/pepper_message_filter.h
@@ -5,51 +5,23 @@
 #ifndef CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_MESSAGE_FILTER_H_
 #define CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_MESSAGE_FILTER_H_
 
-#include <map>
-#include <string>
+#include <set>
 #include <vector>
 
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
 #include "base/files/file_path.h"
-#include "base/memory/linked_ptr.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/process/process.h"
 #include "content/public/browser/browser_message_filter.h"
-#include "content/public/browser/content_browser_client.h"
-#include "content/public/common/process_type.h"
 #include "net/base/net_util.h"
 #include "net/base/network_change_notifier.h"
-#include "net/http/transport_security_state.h"
-#include "net/socket/stream_socket.h"
-#include "net/ssl/ssl_config_service.h"
-#include "ppapi/c/pp_resource.h"
-#include "ppapi/c/pp_stdint.h"
-#include "ppapi/c/ppb_tcp_socket.h"
-#include "ppapi/c/private/ppb_flash.h"
-#include "ppapi/host/ppapi_host.h"
 #include "ppapi/shared_impl/ppapi_permissions.h"
 
-struct PP_NetAddress_Private;
-
-namespace base {
-class ListValue;
-}
-
-namespace net {
-class CertVerifier;
-class HostResolver;
-}
-
 namespace ppapi {
 class PPB_X509Certificate_Fields;
-class SocketOptionData;
 }
 
 namespace content {
-class BrowserContext;
-class PepperTCPSocket;
-class ResourceContext;
 
 // This class is used in two contexts, both supporting PPAPI plugins. The first
 // is on the renderer->browser channel, to handle requests from in-process
@@ -60,20 +32,16 @@
     : public BrowserMessageFilter,
       public net::NetworkChangeNotifier::IPAddressObserver {
  public:
-  // Constructor when used in the context of a render process.
-  PepperMessageFilter(int process_id,
-                      BrowserContext* browser_context);
+  // Factory method used in the context of a renderer process.
+  static PepperMessageFilter* CreateForRendererProcess();
 
-  // Constructor when used in the context of a PPAPI process..
-  PepperMessageFilter(const ppapi::PpapiPermissions& permissions,
-                      net::HostResolver* host_resolver);
+  // Factory method used in the context of a PPAPI process.
+  static PepperMessageFilter* CreateForPpapiPluginProcess(
+      const ppapi::PpapiPermissions& permissions);
 
-  // Constructor when used in the context of an external plugin, i.e. created by
-  // the embedder using BrowserPpapiHost::CreateExternalPluginProcess.
-  PepperMessageFilter(const ppapi::PpapiPermissions& permissions,
-                      net::HostResolver* host_resolver,
-                      int process_id,
-                      int render_view_id);
+  // Factory method used in the context of an external plugin,
+  static PepperMessageFilter* CreateForExternalPluginProcess(
+      const ppapi::PpapiPermissions& permissions);
 
   // BrowserMessageFilter methods.
   virtual bool OnMessageReceived(const IPC::Message& message,
@@ -82,98 +50,14 @@
   // net::NetworkChangeNotifier::IPAddressObserver interface.
   virtual void OnIPAddressChanged() OVERRIDE;
 
-  // Returns the host resolver (it may come from the resource context or the
-  // host_resolver_ member).
-  net::HostResolver* GetHostResolver();
-
-  net::CertVerifier* GetCertVerifier();
-  net::TransportSecurityState* GetTransportSecurityState();
-
-  // Adds already accepted socket to the internal TCP sockets table. Takes
-  // ownership over |socket|. In the case of failure (full socket table)
-  // returns 0 and deletes |socket|. Otherwise, returns generated ID for
-  // |socket|.
-  uint32 AddAcceptedTCPSocket(int32 routing_id,
-                              uint32 plugin_dispatcher_id,
-                              net::StreamSocket* socket);
-
-  const net::SSLConfig& ssl_config() { return ssl_config_; }
-
  protected:
   virtual ~PepperMessageFilter();
 
  private:
-  struct OnConnectTcpBoundInfo {
-    int routing_id;
-    int request_id;
-  };
-
-  // Containers for sockets keyed by socked_id.
-  typedef std::map<uint32, linked_ptr<PepperTCPSocket> > TCPSocketMap;
-
   // Set of disptachers ID's that have subscribed for NetworkMonitor
   // notifications.
   typedef std::set<uint32> NetworkMonitorIdSet;
 
-  void OnGetLocalTimeZoneOffset(base::Time t, double* result);
-
-  void OnTCPCreate(int32 routing_id,
-                   uint32 plugin_dispatcher_id,
-                   uint32* socket_id);
-  void OnTCPCreatePrivate(int32 routing_id,
-                          uint32 plugin_dispatcher_id,
-                          uint32* socket_id);
-  void OnTCPConnect(int32 routing_id,
-                    uint32 socket_id,
-                    const std::string& host,
-                    uint16_t port);
-  void OnTCPConnectWithNetAddress(int32 routing_id,
-                                  uint32 socket_id,
-                                  const PP_NetAddress_Private& net_addr);
-  void OnTCPSSLHandshake(
-      uint32 socket_id,
-      const std::string& server_name,
-      uint16_t server_port,
-      const std::vector<std::vector<char> >& trusted_certs,
-      const std::vector<std::vector<char> >& untrusted_certs);
-  void OnTCPRead(uint32 socket_id, int32_t bytes_to_read);
-  void OnTCPWrite(uint32 socket_id, const std::string& data);
-  void OnTCPDisconnect(uint32 socket_id);
-  void OnTCPSetOption(uint32 socket_id,
-                      PP_TCPSocket_Option name,
-                      const ppapi::SocketOptionData& value);
-
-  void OnNetworkMonitorStart(uint32 plugin_dispatcher_id);
-  void OnNetworkMonitorStop(uint32 plugin_dispatcher_id);
-
-  void DoTCPConnect(int32 routing_id,
-                    uint32 socket_id,
-                    const std::string& host,
-                    uint16_t port,
-                    bool allowed);
-  void DoTCPConnectWithNetAddress(int32 routing_id,
-                                  uint32 socket_id,
-                                  const PP_NetAddress_Private& net_addr,
-                                  bool allowed);
-  void OnX509CertificateParseDER(const std::vector<char>& der,
-                                 bool* succeeded,
-                                 ppapi::PPB_X509Certificate_Fields* result);
-  void OnUpdateActivity();
-
-  uint32 GenerateSocketID();
-
-  // Return true if render with given ID can use socket APIs.
-  bool CanUseSocketAPIs(int32 render_id,
-                        const content::SocketPermissionRequest& params,
-                        bool private_api);
-
-  void GetAndSendNetworkList();
-  void DoGetNetworkList();
-  void SendNetworkList(scoped_ptr<net::NetworkInterfaceList> list);
-  void CreateTCPSocket(int32 routing_id,
-                       uint32 plugin_dispatcher_id,
-                       bool private_api,
-                       uint32* socket_id);
   enum PluginType {
     PLUGIN_TYPE_IN_PROCESS,
     PLUGIN_TYPE_OUT_OF_PROCESS,
@@ -182,6 +66,20 @@
     PLUGIN_TYPE_EXTERNAL_PLUGIN,
   };
 
+  PepperMessageFilter(const ppapi::PpapiPermissions& permissions,
+                      PluginType plugin_type);
+
+  void OnNetworkMonitorStart(uint32 plugin_dispatcher_id);
+  void OnNetworkMonitorStop(uint32 plugin_dispatcher_id);
+
+  void OnX509CertificateParseDER(const std::vector<char>& der,
+                                 bool* succeeded,
+                                 ppapi::PPB_X509Certificate_Fields* result);
+
+  void GetAndSendNetworkList();
+  void DoGetNetworkList();
+  void SendNetworkList(scoped_ptr<net::NetworkInterfaceList> list);
+
   PluginType plugin_type_;
 
   // When attached to an out-of-process plugin (be it native or NaCl) this
@@ -190,39 +88,8 @@
   // be many plugins sharing this channel).
   ppapi::PpapiPermissions permissions_;
 
-  // Render process ID.
-  int process_id_;
-
-  // External plugin RenderView id to determine private API access. Normally, we
-  // handle messages coming from multiple RenderViews, but external plugins
-  // always creates a new PepperMessageFilter for each RenderView.
-  int external_plugin_render_view_id_;
-
-  // When non-NULL, this should be used instead of the host_resolver_.
-  ResourceContext* const resource_context_;
-
-  // When non-NULL, this should be used instead of the resource_context_. Use
-  // GetHostResolver instead of accessing directly.
-  net::HostResolver* host_resolver_;
-
-  // The default SSL configuration settings are used, as opposed to Chrome's SSL
-  // settings.
-  net::SSLConfig ssl_config_;
-  // This is lazily created. Users should use GetCertVerifier to retrieve it.
-  scoped_ptr<net::CertVerifier> cert_verifier_;
-  // This is lazily created. Users should use GetTransportSecurityState to
-  // retrieve it.
-  scoped_ptr<net::TransportSecurityState> transport_security_state_;
-
-  uint32 next_socket_id_;
-
-  TCPSocketMap tcp_sockets_;
-
   NetworkMonitorIdSet network_monitor_ids_;
 
-  base::FilePath browser_path_;
-  bool incognito_;
-
   DISALLOW_COPY_AND_ASSIGN(PepperMessageFilter);
 };
 
diff --git a/content/browser/renderer_host/pepper/pepper_renderer_connection.cc b/content/browser/renderer_host/pepper/pepper_renderer_connection.cc
index 630dda0..b2232fd 100644
--- a/content/browser/renderer_host/pepper/pepper_renderer_connection.cc
+++ b/content/browser/renderer_host/pepper/pepper_renderer_connection.cc
@@ -28,8 +28,7 @@
                                                   "",
                                                   base::FilePath(),
                                                   base::FilePath(),
-                                                  false,
-                                                  NULL));
+                                                  false));
 }
 
 PepperRendererConnection::~PepperRendererConnection() {
diff --git a/content/browser/renderer_host/pepper/pepper_socket_utils.cc b/content/browser/renderer_host/pepper/pepper_socket_utils.cc
index 9dc585e..32f2ef4 100644
--- a/content/browser/renderer_host/pepper/pepper_socket_utils.cc
+++ b/content/browser/renderer_host/pepper/pepper_socket_utils.cc
@@ -8,13 +8,16 @@
 #include <vector>
 
 #include "base/logging.h"
+#include "base/memory/ref_counted.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/common/content_client.h"
+#include "net/cert/x509_certificate.h"
 #include "ppapi/c/private/ppb_net_address_private.h"
 #include "ppapi/shared_impl/private/net_address_private_impl.h"
+#include "ppapi/shared_impl/private/ppb_x509_certificate_private_shared.h"
 
 namespace content {
 namespace pepper_socket_utils {
@@ -77,5 +80,60 @@
   return true;
 }
 
+bool GetCertificateFields(const net::X509Certificate& cert,
+                          ppapi::PPB_X509Certificate_Fields* fields) {
+  const net::CertPrincipal& issuer = cert.issuer();
+  fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_COMMON_NAME,
+      new base::StringValue(issuer.common_name));
+  fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_LOCALITY_NAME,
+      new base::StringValue(issuer.locality_name));
+  fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_STATE_OR_PROVINCE_NAME,
+      new base::StringValue(issuer.state_or_province_name));
+  fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_COUNTRY_NAME,
+      new base::StringValue(issuer.country_name));
+  fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_ORGANIZATION_NAME,
+      new base::StringValue(JoinString(issuer.organization_names, '\n')));
+  fields->SetField(PP_X509CERTIFICATE_PRIVATE_ISSUER_ORGANIZATION_UNIT_NAME,
+      new base::StringValue(JoinString(issuer.organization_unit_names, '\n')));
+
+  const net::CertPrincipal& subject = cert.subject();
+  fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_COMMON_NAME,
+      new base::StringValue(subject.common_name));
+  fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_LOCALITY_NAME,
+      new base::StringValue(subject.locality_name));
+  fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_STATE_OR_PROVINCE_NAME,
+      new base::StringValue(subject.state_or_province_name));
+  fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_COUNTRY_NAME,
+      new base::StringValue(subject.country_name));
+  fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_ORGANIZATION_NAME,
+      new base::StringValue(JoinString(subject.organization_names, '\n')));
+  fields->SetField(PP_X509CERTIFICATE_PRIVATE_SUBJECT_ORGANIZATION_UNIT_NAME,
+      new base::StringValue(JoinString(subject.organization_unit_names, '\n')));
+
+  const std::string& serial_number = cert.serial_number();
+  fields->SetField(PP_X509CERTIFICATE_PRIVATE_SERIAL_NUMBER,
+      base::BinaryValue::CreateWithCopiedBuffer(serial_number.data(),
+                                                serial_number.length()));
+  fields->SetField(PP_X509CERTIFICATE_PRIVATE_VALIDITY_NOT_BEFORE,
+      new base::FundamentalValue(cert.valid_start().ToDoubleT()));
+  fields->SetField(PP_X509CERTIFICATE_PRIVATE_VALIDITY_NOT_AFTER,
+      new base::FundamentalValue(cert.valid_expiry().ToDoubleT()));
+  std::string der;
+  net::X509Certificate::GetDEREncoded(cert.os_cert_handle(), &der);
+  fields->SetField(PP_X509CERTIFICATE_PRIVATE_RAW,
+      base::BinaryValue::CreateWithCopiedBuffer(der.data(), der.length()));
+  return true;
+}
+
+bool GetCertificateFields(const char* der,
+                          uint32_t length,
+                          ppapi::PPB_X509Certificate_Fields* fields) {
+  scoped_refptr<net::X509Certificate> cert =
+      net::X509Certificate::CreateFromBytes(der, length);
+  if (!cert.get())
+    return false;
+  return GetCertificateFields(*cert.get(), fields);
+}
+
 }  // namespace pepper_socket_utils
 }  // namespace content
diff --git a/content/browser/renderer_host/pepper/pepper_socket_utils.h b/content/browser/renderer_host/pepper/pepper_socket_utils.h
index 7a0cef5..7a1e16b 100644
--- a/content/browser/renderer_host/pepper/pepper_socket_utils.h
+++ b/content/browser/renderer_host/pepper/pepper_socket_utils.h
@@ -6,9 +6,18 @@
 #define CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_SOCKET_UTILS_H_
 
 #include "content/public/common/socket_permission_request.h"
+#include "ppapi/c/pp_stdint.h"
 
 struct PP_NetAddress_Private;
 
+namespace net {
+class X509Certificate;
+}
+
+namespace ppapi {
+class PPB_X509Certificate_Fields;
+}
+
 namespace content {
 
 class RenderViewHost;
@@ -31,6 +40,17 @@
                       const SocketPermissionRequest& params,
                       RenderViewHost* render_view_host);
 
+// Extracts the certificate field data from a net::X509Certificate into
+// PPB_X509Certificate_Fields.
+bool GetCertificateFields(const net::X509Certificate& cert,
+                          ppapi::PPB_X509Certificate_Fields* fields);
+
+// Extracts the certificate field data from the DER representation of a
+// certificate into PPB_X509Certificate_Fields.
+bool GetCertificateFields(const char* der,
+                          uint32_t length,
+                          ppapi::PPB_X509Certificate_Fields* fields);
+
 }  // namespace pepper_socket_utils
 
 }  // namespace content
diff --git a/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc b/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc
index 48c8291..af2234d 100644
--- a/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc
+++ b/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc
@@ -8,6 +8,7 @@
 #include "base/bind_helpers.h"
 #include "base/logging.h"
 #include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h"
+#include "content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h"
 #include "content/browser/renderer_host/pepper/pepper_socket_utils.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/socket_permission_request.h"
@@ -20,6 +21,8 @@
 #include "ppapi/c/private/ppb_net_address_private.h"
 #include "ppapi/host/dispatch_host_message.h"
 #include "ppapi/host/error_conversion.h"
+#include "ppapi/host/ppapi_host.h"
+#include "ppapi/host/resource_host.h"
 #include "ppapi/proxy/ppapi_messages.h"
 #include "ppapi/shared_impl/api_id.h"
 #include "ppapi/shared_impl/private/net_address_private_impl.h"
@@ -36,18 +39,21 @@
 namespace content {
 
 PepperTCPServerSocketMessageFilter::PepperTCPServerSocketMessageFilter(
+    ContentBrowserPepperHostFactory* factory,
     BrowserPpapiHostImpl* host,
     PP_Instance instance,
-    bool private_api,
-    const scoped_refptr<PepperMessageFilter>& pepper_message_filter)
-    : state_(STATE_BEFORE_LISTENING),
-      pepper_message_filter_(pepper_message_filter),
+    bool private_api)
+    : ppapi_host_(host->GetPpapiHost()),
+      factory_(factory),
+      instance_(instance),
+      state_(STATE_BEFORE_LISTENING),
       external_plugin_(host->external_plugin()),
       private_api_(private_api),
       render_process_id_(0),
       render_view_id_(0) {
   ++g_num_instances;
-  DCHECK(host);
+  DCHECK(factory_);
+  DCHECK(ppapi_host_);
   if (!host->GetRenderViewIDsForInstance(instance,
                                          &render_process_id_,
                                          &render_view_id_)) {
@@ -83,7 +89,7 @@
   IPC_BEGIN_MESSAGE_MAP(PepperTCPServerSocketMessageFilter, msg)
     PPAPI_DISPATCH_HOST_RESOURCE_CALL(
         PpapiHostMsg_TCPServerSocket_Listen, OnMsgListen)
-    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
+    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
         PpapiHostMsg_TCPServerSocket_Accept, OnMsgAccept)
     PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
         PpapiHostMsg_TCPServerSocket_StopListening, OnMsgStopListening)
@@ -117,8 +123,7 @@
 }
 
 int32_t PepperTCPServerSocketMessageFilter::OnMsgAccept(
-    const ppapi::host::HostMessageContext* context,
-    uint32 plugin_dispatcher_id) {
+    const ppapi::host::HostMessageContext* context) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   DCHECK(context);
 
@@ -131,10 +136,9 @@
   int net_result = socket_->Accept(
       &socket_buffer_,
       base::Bind(&PepperTCPServerSocketMessageFilter::OnAcceptCompleted,
-                 base::Unretained(this),
-                 reply_context, plugin_dispatcher_id));
+                 base::Unretained(this), reply_context));
   if (net_result != net::ERR_IO_PENDING)
-    OnAcceptCompleted(reply_context, plugin_dispatcher_id, net_result);
+    OnAcceptCompleted(reply_context, net_result);
   return PP_OK_COMPLETIONPENDING;
 }
 
@@ -211,7 +215,6 @@
 
 void PepperTCPServerSocketMessageFilter::OnAcceptCompleted(
     const ppapi::host::ReplyMessageContext& context,
-    uint32 plugin_dispatcher_id,
     int net_result) {
   if (state_ != STATE_ACCEPT_IN_PROGRESS) {
     SendAcceptError(context, PP_ERROR_FAILED);
@@ -260,16 +263,18 @@
     SendAcceptError(context, PP_ERROR_FAILED);
     return;
   }
-  if (!pepper_message_filter_.get() || plugin_dispatcher_id == 0) {
-    SendAcceptError(context, PP_ERROR_FAILED);
+
+  scoped_ptr<ppapi::host::ResourceHost> host =
+      factory_->CreateAcceptedTCPSocket(
+          instance_, true /* private_api */, socket.release());
+  if (!host) {
+    SendAcceptError(context, PP_ERROR_NOSPACE);
     return;
   }
-  uint32 accepted_socket_id = pepper_message_filter_->AddAcceptedTCPSocket(
-      ppapi::API_ID_PPB_TCPSOCKET_PRIVATE,
-      plugin_dispatcher_id,
-      socket.release());
-  if (accepted_socket_id != 0) {
-    SendAcceptReply(context, PP_OK, accepted_socket_id, local_addr,
+  int pending_resource_id = ppapi_host_->AddPendingResourceHost(host.Pass());
+  if (pending_resource_id) {
+    SendAcceptReply(context, PP_OK, pending_resource_id,
+                    local_addr,
                     remote_addr);
   } else {
     SendAcceptError(context, PP_ERROR_NOSPACE);
@@ -296,13 +301,13 @@
 void PepperTCPServerSocketMessageFilter::SendAcceptReply(
     const ppapi::host::ReplyMessageContext& context,
     int32_t pp_result,
-    uint32 accepted_socket_id,
+    int pending_resource_id,
     const PP_NetAddress_Private& local_addr,
     const PP_NetAddress_Private& remote_addr) {
   ppapi::host::ReplyMessageContext reply_context(context);
   reply_context.params.set_result(pp_result);
   SendReply(reply_context, PpapiPluginMsg_TCPServerSocket_AcceptReply(
-      accepted_socket_id, local_addr, remote_addr));
+      pending_resource_id, local_addr, remote_addr));
 }
 
 void PepperTCPServerSocketMessageFilter::SendAcceptError(
diff --git a/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h b/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h
index ad8cf19..d36b787 100644
--- a/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h
+++ b/content/browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h
@@ -9,7 +9,6 @@
 #include "base/compiler_specific.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
-#include "content/browser/renderer_host/pepper/pepper_message_filter.h"
 #include "content/common/content_export.h"
 #include "ppapi/c/pp_instance.h"
 #include "ppapi/host/resource_message_filter.h"
@@ -21,18 +20,25 @@
 class StreamSocket;
 }
 
+namespace ppapi {
+namespace host {
+class PpapiHost;
+}
+}
+
 namespace content {
 
 class BrowserPpapiHostImpl;
+class ContentBrowserPepperHostFactory;
 
 class CONTENT_EXPORT PepperTCPServerSocketMessageFilter
     : public ppapi::host::ResourceMessageFilter {
  public:
   PepperTCPServerSocketMessageFilter(
+      ContentBrowserPepperHostFactory* factory,
       BrowserPpapiHostImpl* host,
       PP_Instance instance,
-      bool private_api,
-      const scoped_refptr<PepperMessageFilter>& pepper_message_filter);
+      bool private_api);
 
   static size_t GetNumInstances();
 
@@ -58,8 +64,7 @@
   int32_t OnMsgListen(const ppapi::host::HostMessageContext* context,
                       const PP_NetAddress_Private& addr,
                       int32_t backlog);
-  int32_t OnMsgAccept(const ppapi::host::HostMessageContext* context,
-                      uint32 plugin_dispatcher_id);
+  int32_t OnMsgAccept(const ppapi::host::HostMessageContext* context);
   int32_t OnMsgStopListening(const ppapi::host::HostMessageContext* context);
 
   void DoListen(const ppapi::host::ReplyMessageContext& context,
@@ -69,7 +74,6 @@
   void OnListenCompleted(const ppapi::host::ReplyMessageContext& context,
                          int net_result);
   void OnAcceptCompleted(const ppapi::host::ReplyMessageContext& context,
-                         uint32 plugin_dispatcher_id,
                          int net_result);
 
   void SendListenReply(const ppapi::host::ReplyMessageContext& context,
@@ -79,17 +83,22 @@
                        int32_t pp_result);
   void SendAcceptReply(const ppapi::host::ReplyMessageContext& context,
                        int32_t pp_result,
-                       uint32 accepted_socket_id,
+                       int pending_resource_id,
                        const PP_NetAddress_Private& local_addr,
                        const PP_NetAddress_Private& remote_addr);
   void SendAcceptError(const ppapi::host::ReplyMessageContext& context,
                        int32_t pp_result);
 
   // Following fields are initialized and used only on the IO thread.
+  // Non-owning ptr.
+  ppapi::host::PpapiHost* ppapi_host_;
+  // Non-owning ptr.
+  ContentBrowserPepperHostFactory* factory_;
+  PP_Instance instance_;
+
   State state_;
   scoped_ptr<net::ServerSocket> socket_;
   scoped_ptr<net::StreamSocket> socket_buffer_;
-  scoped_refptr<PepperMessageFilter> pepper_message_filter_;
 
   // Following fields are initialized on the IO thread but used only
   // on the UI thread.
diff --git a/content/browser/renderer_host/pepper/pepper_tcp_socket.h b/content/browser/renderer_host/pepper/pepper_tcp_socket.h
deleted file mode 100644
index 986afb0..0000000
--- a/content/browser/renderer_host/pepper/pepper_tcp_socket.h
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_TCP_SOCKET_H_
-#define CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_TCP_SOCKET_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "net/base/address_list.h"
-#include "net/base/completion_callback.h"
-#include "ppapi/c/pp_stdint.h"
-#include "ppapi/c/ppb_tcp_socket.h"
-
-struct PP_NetAddress_Private;
-
-namespace ppapi {
-class PPB_X509Certificate_Fields;
-class SocketOptionData;
-}
-
-namespace net {
-class DrainableIOBuffer;
-class IOBuffer;
-class SingleRequestHostResolver;
-class StreamSocket;
-class X509Certificate;
-}
-
-namespace content {
-class PepperMessageFilter;
-
-// PepperTCPSocket is used by PepperMessageFilter to handle requests from
-// the Pepper TCP socket API (PPB_TCPSocket and PPB_TCPSocket_Private).
-class PepperTCPSocket {
- public:
-  PepperTCPSocket(PepperMessageFilter* manager,
-                  int32 routing_id,
-                  uint32 plugin_dispatcher_id,
-                  uint32 socket_id,
-                  bool private_api);
-
-  // Used for creation already connected sockets.  Takes ownership of
-  // |socket|.
-  PepperTCPSocket(PepperMessageFilter* manager,
-                  int32 routing_id,
-                  uint32 plugin_dispatcher_id,
-                  uint32 socket_id,
-                  net::StreamSocket* socket,
-                  bool private_api);
-  ~PepperTCPSocket();
-
-  int routing_id() { return routing_id_; }
-  bool private_api() const { return private_api_; }
-
-  void Connect(const std::string& host, uint16_t port);
-  void ConnectWithNetAddress(const PP_NetAddress_Private& net_addr);
-  void SSLHandshake(
-      const std::string& server_name,
-      uint16_t server_port,
-      const std::vector<std::vector<char> >& trusted_certs,
-      const std::vector<std::vector<char> >& untrusted_certs);
-  void Read(int32 bytes_to_read);
-  void Write(const std::string& data);
-  void SetOption(PP_TCPSocket_Option name,
-                 const ppapi::SocketOptionData& value);
-
-  void SendConnectACKError(int32_t error);
-
-  // Extracts the certificate field data from a |net::X509Certificate| into
-  // |PPB_X509Certificate_Fields|.
-  static bool GetCertificateFields(const net::X509Certificate& cert,
-                                   ppapi::PPB_X509Certificate_Fields* fields);
-  // Extracts the certificate field data from the DER representation of a
-  // certificate into |PPB_X509Certificate_Fields|.
-  static bool GetCertificateFields(const char* der,
-                                   uint32_t length,
-                                   ppapi::PPB_X509Certificate_Fields* fields);
-
- private:
-  enum ConnectionState {
-    // Before a connection is successfully established (including a previous
-    // connect request failed).
-    BEFORE_CONNECT,
-    // There is a connect request that is pending.
-    CONNECT_IN_PROGRESS,
-    // A connection has been successfully established.
-    CONNECTED,
-    // There is an SSL handshake request that is pending.
-    SSL_HANDSHAKE_IN_PROGRESS,
-    // An SSL connection has been successfully established.
-    SSL_CONNECTED,
-    // An SSL handshake has failed.
-    SSL_HANDSHAKE_FAILED
-  };
-
-  void StartConnect(const net::AddressList& addresses);
-
-  void SendReadACKError(int32_t error);
-  void SendWriteACKError(int32_t error);
-  void SendSSLHandshakeACK(bool succeeded);
-  void SendSetOptionACK(int32_t result);
-
-  void OnResolveCompleted(int net_result);
-  void OnConnectCompleted(int net_result);
-  void OnSSLHandshakeCompleted(int net_result);
-  void OnReadCompleted(int net_result);
-  void OnWriteCompleted(int net_result);
-
-  bool IsConnected() const;
-  bool IsSsl() const;
-
-  // Actually does a write from |write_buffer_|; possibly called many times for
-  // each |Write()|.
-  void DoWrite();
-
-  PepperMessageFilter* manager_;
-  int32 routing_id_;
-  uint32 plugin_dispatcher_id_;
-  uint32 socket_id_;
-  bool private_api_;
-
-  ConnectionState connection_state_;
-  bool end_of_file_reached_;
-
-  scoped_ptr<net::SingleRequestHostResolver> resolver_;
-  net::AddressList address_list_;
-
-  scoped_ptr<net::StreamSocket> socket_;
-
-  scoped_refptr<net::IOBuffer> read_buffer_;
-
-  // |StreamSocket::Write()| may not always write the full buffer, but we would
-  // rather have our |Write()| do so whenever possible. To do this, we may have
-  // to call the former multiple times for each of the latter. This entails
-  // using a |DrainableIOBuffer|, which requires an underlying base |IOBuffer|.
-  scoped_refptr<net::IOBuffer> write_buffer_base_;
-  scoped_refptr<net::DrainableIOBuffer> write_buffer_;
-
-  DISALLOW_COPY_AND_ASSIGN(PepperTCPSocket);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_TCP_SOCKET_H_
diff --git a/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc b/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc
new file mode 100644
index 0000000..2ec8c54
--- /dev/null
+++ b/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc
@@ -0,0 +1,648 @@
+// 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 "content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h"
+#include "content/browser/renderer_host/pepper/pepper_socket_utils.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/resource_context.h"
+#include "content/public/common/socket_permission_request.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/dns/single_request_host_resolver.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/socket/ssl_client_socket.h"
+#include "net/socket/tcp_client_socket.h"
+#include "ppapi/c/private/ppb_net_address_private.h"
+#include "ppapi/host/dispatch_host_message.h"
+#include "ppapi/host/error_conversion.h"
+#include "ppapi/proxy/ppapi_messages.h"
+#include "ppapi/proxy/tcp_socket_resource_base.h"
+#include "ppapi/shared_impl/private/net_address_private_impl.h"
+
+using ppapi::NetAddressPrivateImpl;
+using ppapi::host::NetErrorToPepperError;
+using ppapi::proxy::TCPSocketResourceBase;
+
+namespace {
+
+size_t g_num_instances = 0;
+
+}  // namespace
+
+namespace content {
+
+PepperTCPSocketMessageFilter::PepperTCPSocketMessageFilter(
+    BrowserPpapiHostImpl* host,
+    PP_Instance instance,
+    bool private_api)
+    : external_plugin_(host->external_plugin()),
+      private_api_(private_api),
+      render_process_id_(0),
+      render_view_id_(0),
+      state_(STATE_BEFORE_CONNECT),
+      end_of_file_reached_(false),
+      ssl_context_helper_(host->ssl_context_helper()) {
+  DCHECK(host);
+  ++g_num_instances;
+  if (!host->GetRenderViewIDsForInstance(instance,
+                                         &render_process_id_,
+                                         &render_view_id_)) {
+    NOTREACHED();
+  }
+}
+
+PepperTCPSocketMessageFilter::PepperTCPSocketMessageFilter(
+    BrowserPpapiHostImpl* host,
+    PP_Instance instance,
+    bool private_api,
+    net::StreamSocket* socket)
+    : external_plugin_(host->external_plugin()),
+      private_api_(private_api),
+      render_process_id_(0),
+      render_view_id_(0),
+      state_(STATE_CONNECTED),
+      end_of_file_reached_(false),
+      socket_(socket),
+      ssl_context_helper_(host->ssl_context_helper()) {
+  DCHECK(host);
+  ++g_num_instances;
+  if (!host->GetRenderViewIDsForInstance(instance,
+                                         &render_process_id_,
+                                         &render_view_id_)) {
+    NOTREACHED();
+  }
+}
+
+PepperTCPSocketMessageFilter::~PepperTCPSocketMessageFilter() {
+  if (socket_)
+    socket_->Disconnect();
+  --g_num_instances;
+}
+
+// static
+size_t PepperTCPSocketMessageFilter::GetNumInstances() {
+  return g_num_instances;
+}
+
+scoped_refptr<base::TaskRunner>
+PepperTCPSocketMessageFilter::OverrideTaskRunnerForMessage(
+    const IPC::Message& message) {
+  switch (message.type()) {
+    case PpapiHostMsg_TCPSocket_Connect::ID:
+    case PpapiHostMsg_TCPSocket_ConnectWithNetAddress::ID:
+      return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
+    case PpapiHostMsg_TCPSocket_SSLHandshake::ID:
+    case PpapiHostMsg_TCPSocket_Read::ID:
+    case PpapiHostMsg_TCPSocket_Write::ID:
+    case PpapiHostMsg_TCPSocket_Disconnect::ID:
+    case PpapiHostMsg_TCPSocket_SetOption::ID:
+      return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
+  }
+  return NULL;
+}
+
+int32_t PepperTCPSocketMessageFilter::OnResourceMessageReceived(
+    const IPC::Message& msg,
+    ppapi::host::HostMessageContext* context) {
+  IPC_BEGIN_MESSAGE_MAP(PepperTCPSocketMessageFilter, msg)
+    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
+        PpapiHostMsg_TCPSocket_Connect, OnMsgConnect)
+    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
+        PpapiHostMsg_TCPSocket_ConnectWithNetAddress,
+        OnMsgConnectWithNetAddress)
+    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
+        PpapiHostMsg_TCPSocket_SSLHandshake, OnMsgSSLHandshake)
+    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
+        PpapiHostMsg_TCPSocket_Read, OnMsgRead)
+    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
+        PpapiHostMsg_TCPSocket_Write, OnMsgWrite)
+    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
+        PpapiHostMsg_TCPSocket_Disconnect, OnMsgDisconnect)
+    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
+        PpapiHostMsg_TCPSocket_SetOption, OnMsgSetOption)
+  IPC_END_MESSAGE_MAP()
+  return PP_ERROR_FAILED;
+}
+
+int32_t PepperTCPSocketMessageFilter::OnMsgConnect(
+    const ppapi::host::HostMessageContext* context,
+    const std::string& host,
+    uint16_t port) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  // This is only supported by PPB_TCPSocket_Private.
+  if (!private_api_) {
+    NOTREACHED();
+    return PP_ERROR_NOACCESS;
+  }
+
+  SocketPermissionRequest request(SocketPermissionRequest::TCP_CONNECT,
+                                  host,
+                                  port);
+  if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, private_api_,
+                                             request, render_process_id_,
+                                             render_view_id_)) {
+    return PP_ERROR_NOACCESS;
+  }
+
+  RenderProcessHost* render_process_host =
+      RenderProcessHost::FromID(render_process_id_);
+  if (!render_process_host)
+    return PP_ERROR_FAILED;
+  BrowserContext* browser_context = render_process_host->GetBrowserContext();
+  if (!browser_context || !browser_context->GetResourceContext())
+    return PP_ERROR_FAILED;
+
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(&PepperTCPSocketMessageFilter::DoConnect, this,
+                 context->MakeReplyMessageContext(),
+                 host, port, browser_context->GetResourceContext()));
+  return PP_OK_COMPLETIONPENDING;
+}
+
+int32_t PepperTCPSocketMessageFilter::OnMsgConnectWithNetAddress(
+    const ppapi::host::HostMessageContext* context,
+    const PP_NetAddress_Private& net_addr) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  content::SocketPermissionRequest request =
+      pepper_socket_utils::CreateSocketPermissionRequest(
+          content::SocketPermissionRequest::TCP_CONNECT, net_addr);
+  if (!pepper_socket_utils::CanUseSocketAPIs(external_plugin_, private_api_,
+                                             request, render_process_id_,
+                                             render_view_id_)) {
+    return PP_ERROR_NOACCESS;
+  }
+
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::Bind(&PepperTCPSocketMessageFilter::DoConnectWithNetAddress, this,
+                 context->MakeReplyMessageContext(), net_addr));
+  return PP_OK_COMPLETIONPENDING;
+}
+
+int32_t PepperTCPSocketMessageFilter::OnMsgSSLHandshake(
+    const ppapi::host::HostMessageContext* context,
+    const std::string& server_name,
+    uint16_t server_port,
+    const std::vector<std::vector<char> >& trusted_certs,
+    const std::vector<std::vector<char> >& untrusted_certs) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  // Allow to do SSL handshake only if currently the socket has been connected
+  // and there isn't pending read or write.
+  // IsConnected() includes the state that SSL handshake has been finished and
+  // therefore isn't suitable here.
+  if (state_ != STATE_CONNECTED || read_buffer_.get() ||
+      write_buffer_base_.get() || write_buffer_.get()) {
+    return PP_ERROR_FAILED;
+  }
+
+  SetState(STATE_SSL_HANDSHAKE_IN_PROGRESS);
+  // TODO(raymes,rsleevi): Use trusted/untrusted certificates when connecting.
+
+  scoped_ptr<net::ClientSocketHandle> handle(new net::ClientSocketHandle());
+  handle->SetSocket(socket_.Pass());
+  net::ClientSocketFactory* factory =
+      net::ClientSocketFactory::GetDefaultFactory();
+  net::HostPortPair host_port_pair(server_name, server_port);
+  net::SSLClientSocketContext ssl_context;
+  ssl_context.cert_verifier = ssl_context_helper_->GetCertVerifier();
+  ssl_context.transport_security_state =
+      ssl_context_helper_->GetTransportSecurityState();
+  socket_ = factory->CreateSSLClientSocket(
+      handle.Pass(), host_port_pair, ssl_context_helper_->ssl_config(),
+      ssl_context);
+  if (!socket_) {
+    LOG(WARNING) << "Failed to create an SSL client socket.";
+    return PP_ERROR_FAILED;
+  }
+
+  const ppapi::host::ReplyMessageContext reply_context(
+      context->MakeReplyMessageContext());
+  int net_result = socket_->Connect(
+      base::Bind(&PepperTCPSocketMessageFilter::OnSSLHandshakeCompleted,
+                 base::Unretained(this), reply_context));
+  if (net_result != net::ERR_IO_PENDING)
+    OnSSLHandshakeCompleted(reply_context, net_result);
+  return PP_OK_COMPLETIONPENDING;
+}
+
+int32_t PepperTCPSocketMessageFilter::OnMsgRead(
+    const ppapi::host::HostMessageContext* context,
+    int32_t bytes_to_read) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  if (!IsConnected() || end_of_file_reached_)
+    return PP_ERROR_FAILED;
+  if (read_buffer_.get())
+    return PP_ERROR_INPROGRESS;
+  if (bytes_to_read <= 0 ||
+      bytes_to_read > TCPSocketResourceBase::kMaxReadSize) {
+    return PP_ERROR_BADARGUMENT;
+  }
+
+  ppapi::host::ReplyMessageContext reply_context(
+      context->MakeReplyMessageContext());
+  read_buffer_ = new net::IOBuffer(bytes_to_read);
+  int net_result = socket_->Read(
+      read_buffer_.get(),
+      bytes_to_read,
+      base::Bind(&PepperTCPSocketMessageFilter::OnReadCompleted,
+                 base::Unretained(this), reply_context));
+  if (net_result != net::ERR_IO_PENDING)
+    OnReadCompleted(reply_context, net_result);
+  return PP_OK_COMPLETIONPENDING;
+}
+
+int32_t PepperTCPSocketMessageFilter::OnMsgWrite(
+    const ppapi::host::HostMessageContext* context,
+    const std::string& data) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  if (!IsConnected())
+    return PP_ERROR_FAILED;
+  if (write_buffer_base_.get() || write_buffer_.get())
+    return PP_ERROR_INPROGRESS;
+
+  size_t data_size = data.size();
+  if (data_size == 0 ||
+      data_size > static_cast<size_t>(TCPSocketResourceBase::kMaxWriteSize)) {
+    return PP_ERROR_BADARGUMENT;
+  }
+
+  write_buffer_base_ = new net::IOBuffer(data_size);
+  memcpy(write_buffer_base_->data(), data.data(), data_size);
+  write_buffer_ =
+      new net::DrainableIOBuffer(write_buffer_base_.get(), data_size);
+  DoWrite(context->MakeReplyMessageContext());
+  return PP_OK_COMPLETIONPENDING;
+}
+
+int32_t PepperTCPSocketMessageFilter::OnMsgDisconnect(
+    const ppapi::host::HostMessageContext* context) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  SetState(STATE_CLOSED);
+  return PP_OK;
+}
+
+int32_t PepperTCPSocketMessageFilter::OnMsgSetOption(
+    const ppapi::host::HostMessageContext* context,
+    PP_TCPSocket_Option name,
+    const ppapi::SocketOptionData& value) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  if (!IsConnected() || IsSsl())
+    return PP_ERROR_FAILED;
+
+  net::TCPClientSocket* tcp_socket =
+      static_cast<net::TCPClientSocket*>(socket_.get());
+  DCHECK(tcp_socket);
+
+  switch (name) {
+    case PP_TCPSOCKET_OPTION_NO_DELAY: {
+      bool boolean_value = false;
+      if (!value.GetBool(&boolean_value))
+        return PP_ERROR_BADARGUMENT;
+      return tcp_socket->SetNoDelay(boolean_value) ? PP_OK : PP_ERROR_FAILED;
+    }
+    case PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE:
+    case PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE: {
+      int32_t integer_value = 0;
+      if (!value.GetInt32(&integer_value) || integer_value <= 0)
+        return PP_ERROR_BADARGUMENT;
+
+      bool result = false;
+      if (name == PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE) {
+        if (integer_value > TCPSocketResourceBase::kMaxSendBufferSize)
+          return PP_ERROR_BADARGUMENT;
+        result = tcp_socket->SetSendBufferSize(integer_value);
+      } else {
+        if (integer_value > TCPSocketResourceBase::kMaxReceiveBufferSize)
+          return PP_ERROR_BADARGUMENT;
+        result = tcp_socket->SetReceiveBufferSize(integer_value);
+      }
+      return result ? PP_OK : PP_ERROR_FAILED;
+    }
+    default: {
+      NOTREACHED();
+      return PP_ERROR_BADARGUMENT;
+    }
+  }
+  return PP_ERROR_FAILED;
+}
+
+void PepperTCPSocketMessageFilter::DoConnect(
+    const ppapi::host::ReplyMessageContext& context,
+    const std::string& host,
+    uint16_t port,
+    ResourceContext* resource_context) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  if (state_ != STATE_BEFORE_CONNECT) {
+    SendConnectError(context, PP_ERROR_FAILED);
+    return;
+  }
+
+  SetState(STATE_CONNECT_IN_PROGRESS);
+  net::HostResolver::RequestInfo request_info(net::HostPortPair(host, port));
+  resolver_.reset(new net::SingleRequestHostResolver(
+      resource_context->GetHostResolver()));
+  int net_result = resolver_->Resolve(
+      request_info,
+      net::DEFAULT_PRIORITY,
+      &address_list_,
+      base::Bind(&PepperTCPSocketMessageFilter::OnResolveCompleted,
+                 base::Unretained(this), context),
+      net::BoundNetLog());
+  if (net_result != net::ERR_IO_PENDING)
+    OnResolveCompleted(context, net_result);
+}
+
+void PepperTCPSocketMessageFilter::DoConnectWithNetAddress(
+    const ppapi::host::ReplyMessageContext& context,
+    const PP_NetAddress_Private& net_addr) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  if (state_ != STATE_BEFORE_CONNECT) {
+    SendConnectError(context, PP_ERROR_FAILED);
+    return;
+  }
+
+  net::IPAddressNumber address;
+  int port;
+  if (!NetAddressPrivateImpl::NetAddressToIPEndPoint(net_addr, &address,
+                                                     &port)) {
+    SendConnectError(context, PP_ERROR_ADDRESS_INVALID);
+    return;
+  }
+
+  // Copy the single IPEndPoint to address_list_.
+  address_list_.clear();
+  address_list_.push_back(net::IPEndPoint(address, port));
+  SetState(STATE_CONNECT_IN_PROGRESS);
+  StartConnect(context);
+}
+
+void PepperTCPSocketMessageFilter::DoWrite(
+    const ppapi::host::ReplyMessageContext& context) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK(write_buffer_base_.get());
+  DCHECK(write_buffer_.get());
+  DCHECK_GT(write_buffer_->BytesRemaining(), 0);
+
+  int net_result = socket_->Write(
+      write_buffer_.get(),
+      write_buffer_->BytesRemaining(),
+      base::Bind(&PepperTCPSocketMessageFilter::OnWriteCompleted,
+                 base::Unretained(this), context));
+  if (net_result != net::ERR_IO_PENDING)
+    OnWriteCompleted(context, net_result);
+}
+
+void PepperTCPSocketMessageFilter::OnResolveCompleted(
+    const ppapi::host::ReplyMessageContext& context,
+    int net_result) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  if (state_ != STATE_CONNECT_IN_PROGRESS) {
+    SendConnectError(context, PP_ERROR_FAILED);
+    SetState(STATE_CLOSED);
+    return;
+  }
+
+  if (net_result != net::OK) {
+    SendConnectError(context, NetErrorToPepperError(net_result));
+    SetState(STATE_BEFORE_CONNECT);
+    return;
+  }
+
+  StartConnect(context);
+}
+
+void PepperTCPSocketMessageFilter::StartConnect(
+    const ppapi::host::ReplyMessageContext& context) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  if (state_ != STATE_CONNECT_IN_PROGRESS) {
+    SendConnectError(context, PP_ERROR_FAILED);
+    SetState(STATE_CLOSED);
+    return;
+  }
+
+  socket_.reset(new net::TCPClientSocket(address_list_, NULL,
+                                         net::NetLog::Source()));
+  int net_result = socket_->Connect(
+      base::Bind(&PepperTCPSocketMessageFilter::OnConnectCompleted,
+                 base::Unretained(this), context));
+  if (net_result != net::ERR_IO_PENDING)
+    OnConnectCompleted(context, net_result);
+}
+
+void PepperTCPSocketMessageFilter::OnConnectCompleted(
+    const ppapi::host::ReplyMessageContext& context,
+    int net_result) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK(socket_.get());
+
+  if (state_ != STATE_CONNECT_IN_PROGRESS) {
+    SendConnectError(context, PP_ERROR_FAILED);
+    SetState(STATE_CLOSED);
+    return;
+  }
+
+  int32_t pp_result = NetErrorToPepperError(net_result);
+  do {
+    if (pp_result != PP_OK)
+      break;
+
+    net::IPEndPoint ip_end_point_local;
+    net::IPEndPoint ip_end_point_remote;
+    pp_result = NetErrorToPepperError(
+        socket_->GetLocalAddress(&ip_end_point_local));
+    if (pp_result != PP_OK)
+      break;
+    pp_result = NetErrorToPepperError(
+        socket_->GetPeerAddress(&ip_end_point_remote));
+    if (pp_result != PP_OK)
+      break;
+
+    PP_NetAddress_Private local_addr =
+        NetAddressPrivateImpl::kInvalidNetAddress;
+    PP_NetAddress_Private remote_addr =
+        NetAddressPrivateImpl::kInvalidNetAddress;
+    if (!NetAddressPrivateImpl::IPEndPointToNetAddress(
+            ip_end_point_local.address(),
+            ip_end_point_local.port(),
+            &local_addr) ||
+        !NetAddressPrivateImpl::IPEndPointToNetAddress(
+            ip_end_point_remote.address(),
+            ip_end_point_remote.port(),
+            &remote_addr)) {
+      pp_result = PP_ERROR_ADDRESS_INVALID;
+      break;
+    }
+
+    SendConnectReply(context, PP_OK, local_addr, remote_addr);
+    SetState(STATE_CONNECTED);
+    return;
+  } while (false);
+
+  SendConnectError(context, pp_result);
+  SetState(STATE_BEFORE_CONNECT);
+}
+
+void PepperTCPSocketMessageFilter::OnSSLHandshakeCompleted(
+    const ppapi::host::ReplyMessageContext& context,
+    int net_result) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+  if (state_ != STATE_SSL_HANDSHAKE_IN_PROGRESS) {
+    SendSSLHandshakeReply(context, PP_ERROR_FAILED);
+    SetState(STATE_CLOSED);
+    return;
+  }
+  SendSSLHandshakeReply(context, NetErrorToPepperError(net_result));
+  SetState(net_result == net::OK ?
+           STATE_SSL_CONNECTED :
+           STATE_SSL_HANDSHAKE_FAILED);
+}
+
+void PepperTCPSocketMessageFilter::OnReadCompleted(
+    const ppapi::host::ReplyMessageContext& context,
+    int net_result) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK(read_buffer_.get());
+
+  if (net_result > 0) {
+    SendReadReply(context,
+                  PP_OK,
+                  std::string(read_buffer_->data(), net_result));
+  } else if (net_result == 0) {
+    end_of_file_reached_ = true;
+    SendReadReply(context, PP_OK, std::string());
+  } else {
+    SendReadError(context, NetErrorToPepperError(net_result));
+  }
+  read_buffer_ = NULL;
+}
+
+void PepperTCPSocketMessageFilter::OnWriteCompleted(
+    const ppapi::host::ReplyMessageContext& context,
+    int net_result) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+  DCHECK(write_buffer_base_.get());
+  DCHECK(write_buffer_.get());
+
+  // Note: For partial writes of 0 bytes, don't continue writing to avoid a
+  // likely infinite loop.
+  if (net_result > 0) {
+    write_buffer_->DidConsume(net_result);
+    if (write_buffer_->BytesRemaining() > 0) {
+      DoWrite(context);
+      return;
+    }
+  }
+
+  if (net_result >= 0)
+    SendWriteReply(context, write_buffer_->BytesConsumed());
+  else
+    SendWriteReply(context, NetErrorToPepperError(net_result));
+
+  write_buffer_ = NULL;
+  write_buffer_base_ = NULL;
+}
+
+void PepperTCPSocketMessageFilter::SendConnectReply(
+    const ppapi::host::ReplyMessageContext& context,
+    int32_t pp_result,
+    const PP_NetAddress_Private& local_addr,
+    const PP_NetAddress_Private& remote_addr) {
+  ppapi::host::ReplyMessageContext reply_context(context);
+  reply_context.params.set_result(pp_result);
+  SendReply(reply_context,
+            PpapiPluginMsg_TCPSocket_ConnectReply(local_addr, remote_addr));
+}
+
+void PepperTCPSocketMessageFilter::SendConnectError(
+    const ppapi::host::ReplyMessageContext& context,
+    int32_t pp_error) {
+  SendConnectReply(context,
+                   pp_error,
+                   NetAddressPrivateImpl::kInvalidNetAddress,
+                   NetAddressPrivateImpl::kInvalidNetAddress);
+}
+
+void PepperTCPSocketMessageFilter::SendSSLHandshakeReply(
+    const ppapi::host::ReplyMessageContext& context,
+    int32_t pp_result) {
+  ppapi::host::ReplyMessageContext reply_context(context);
+  reply_context.params.set_result(pp_result);
+  ppapi::PPB_X509Certificate_Fields certificate_fields;
+  if (pp_result == PP_OK) {
+    // Our socket is guaranteed to be an SSL socket if we get here.
+    net::SSLClientSocket* ssl_socket =
+        static_cast<net::SSLClientSocket*>(socket_.get());
+    net::SSLInfo ssl_info;
+    ssl_socket->GetSSLInfo(&ssl_info);
+    if (ssl_info.cert.get()) {
+      pepper_socket_utils::GetCertificateFields(*ssl_info.cert.get(),
+                                                &certificate_fields);
+    }
+  }
+  SendReply(reply_context,
+            PpapiPluginMsg_TCPSocket_SSLHandshakeReply(certificate_fields));
+}
+
+void PepperTCPSocketMessageFilter::SendReadReply(
+    const ppapi::host::ReplyMessageContext& context,
+    int32_t pp_result,
+    const std::string& data) {
+  ppapi::host::ReplyMessageContext reply_context(context);
+  reply_context.params.set_result(pp_result);
+  SendReply(reply_context, PpapiPluginMsg_TCPSocket_ReadReply(data));
+}
+
+void PepperTCPSocketMessageFilter::SendReadError(
+    const ppapi::host::ReplyMessageContext& context,
+    int32_t pp_error) {
+  SendReadReply(context, pp_error, std::string());
+}
+
+void PepperTCPSocketMessageFilter::SendWriteReply(
+    const ppapi::host::ReplyMessageContext& context,
+    int32_t pp_result) {
+  ppapi::host::ReplyMessageContext reply_context(context);
+  reply_context.params.set_result(pp_result);
+  SendReply(reply_context, PpapiPluginMsg_TCPSocket_WriteReply());
+}
+
+bool PepperTCPSocketMessageFilter::IsConnected() const {
+  return state_ == STATE_CONNECTED || state_ == STATE_SSL_CONNECTED;
+}
+
+bool PepperTCPSocketMessageFilter::IsSsl() const {
+  return state_ == STATE_SSL_HANDSHAKE_IN_PROGRESS ||
+      state_ == STATE_SSL_CONNECTED ||
+      state_ == STATE_SSL_HANDSHAKE_FAILED;
+}
+
+void PepperTCPSocketMessageFilter::SetState(State state) {
+  state_ = state;
+  if (state_ == STATE_CLOSED && socket_) {
+    // Make sure no further callbacks from socket_.
+    socket_->Disconnect();
+    socket_.reset();
+  }
+}
+
+}  // namespace content
diff --git a/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h b/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h
new file mode 100644
index 0000000..84e8a75
--- /dev/null
+++ b/content/browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h
@@ -0,0 +1,183 @@
+// 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 CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_TCP_SOCKET_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_TCP_SOCKET_MESSAGE_FILTER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/renderer_host/pepper/ssl_context_helper.h"
+#include "content/common/content_export.h"
+#include "net/base/address_list.h"
+#include "ppapi/c/pp_instance.h"
+#include "ppapi/c/ppb_tcp_socket.h"
+#include "ppapi/host/resource_message_filter.h"
+
+struct PP_NetAddress_Private;
+
+namespace net {
+class DrainableIOBuffer;
+class IOBuffer;
+class SingleRequestHostResolver;
+class StreamSocket;
+}
+
+namespace ppapi {
+class SocketOptionData;
+
+namespace host {
+struct ReplyMessageContext;
+}
+}
+
+namespace content {
+
+class BrowserPpapiHostImpl;
+class ResourceContext;
+
+class CONTENT_EXPORT PepperTCPSocketMessageFilter
+    : public ppapi::host::ResourceMessageFilter {
+ public:
+  PepperTCPSocketMessageFilter(
+      BrowserPpapiHostImpl* host,
+      PP_Instance instance,
+      bool private_api);
+
+  // Used for creating already connected sockets.  Takes ownership of
+  // |socket|.
+  PepperTCPSocketMessageFilter(
+      BrowserPpapiHostImpl* host,
+      PP_Instance instance,
+      bool private_api,
+      net::StreamSocket* socket);
+
+  static size_t GetNumInstances();
+
+ protected:
+  virtual ~PepperTCPSocketMessageFilter();
+
+ private:
+  enum State {
+    // Before a connection is successfully established (including a previous
+    // connect request failed).
+    STATE_BEFORE_CONNECT,
+    // There is a connect request that is pending.
+    STATE_CONNECT_IN_PROGRESS,
+    // A connection has been successfully established.
+    STATE_CONNECTED,
+    // There is an SSL handshake request that is pending.
+    STATE_SSL_HANDSHAKE_IN_PROGRESS,
+    // An SSL connection has been successfully established.
+    STATE_SSL_CONNECTED,
+    // An SSL handshake has failed.
+    STATE_SSL_HANDSHAKE_FAILED,
+    // Socket is closed.
+    STATE_CLOSED
+  };
+
+  // ppapi::host::ResourceMessageFilter overrides.
+  virtual scoped_refptr<base::TaskRunner> OverrideTaskRunnerForMessage(
+      const IPC::Message& message) OVERRIDE;
+  virtual int32_t OnResourceMessageReceived(
+      const IPC::Message& msg,
+      ppapi::host::HostMessageContext* context) OVERRIDE;
+
+  int32_t OnMsgConnect(const ppapi::host::HostMessageContext* context,
+                       const std::string& host,
+                       uint16_t port);
+  int32_t OnMsgConnectWithNetAddress(
+      const ppapi::host::HostMessageContext* context,
+      const PP_NetAddress_Private& net_addr);
+  int32_t OnMsgSSLHandshake(
+      const ppapi::host::HostMessageContext* context,
+      const std::string& server_name,
+      uint16_t server_port,
+      const std::vector<std::vector<char> >& trusted_certs,
+      const std::vector<std::vector<char> >& untrusted_certs);
+  int32_t OnMsgRead(const ppapi::host::HostMessageContext* context,
+                    int32_t bytes_to_read);
+  int32_t OnMsgWrite(const ppapi::host::HostMessageContext* context,
+                     const std::string& data);
+  int32_t OnMsgDisconnect(const ppapi::host::HostMessageContext* context);
+  int32_t OnMsgSetOption(const ppapi::host::HostMessageContext* context,
+                         PP_TCPSocket_Option name,
+                         const ppapi::SocketOptionData& value);
+
+  void DoConnect(const ppapi::host::ReplyMessageContext& context,
+                 const std::string& host,
+                 uint16_t port,
+                 ResourceContext* resource_context);
+  void DoConnectWithNetAddress(
+      const ppapi::host::ReplyMessageContext& context,
+      const PP_NetAddress_Private& net_addr);
+  void DoWrite(const ppapi::host::ReplyMessageContext& context);
+
+  void OnResolveCompleted(const ppapi::host::ReplyMessageContext& context,
+                          int net_result);
+  void StartConnect(const ppapi::host::ReplyMessageContext& context);
+
+  void OnConnectCompleted(const ppapi::host::ReplyMessageContext& context,
+                          int net_result);
+  void OnSSLHandshakeCompleted(const ppapi::host::ReplyMessageContext& context,
+                               int net_result);
+  void OnReadCompleted(const ppapi::host::ReplyMessageContext& context,
+                       int net_result);
+  void OnWriteCompleted(const ppapi::host::ReplyMessageContext& context,
+                        int net_result);
+
+  void SendConnectReply(const ppapi::host::ReplyMessageContext& context,
+                        int32_t pp_result,
+                        const PP_NetAddress_Private& local_addr,
+                        const PP_NetAddress_Private& remote_addr);
+  void SendConnectError(const ppapi::host::ReplyMessageContext& context,
+                        int32_t pp_error);
+  void SendSSLHandshakeReply(const ppapi::host::ReplyMessageContext& context,
+                             int32_t pp_result);
+  void SendReadReply(const ppapi::host::ReplyMessageContext& context,
+                     int32_t pp_result,
+                     const std::string& data);
+  void SendReadError(const ppapi::host::ReplyMessageContext& context,
+                     int32_t pp_error);
+  void SendWriteReply(const ppapi::host::ReplyMessageContext& context,
+                      int32_t pp_result);
+
+  bool IsConnected() const;
+  bool IsSsl() const;
+  void SetState(State state);
+
+  bool external_plugin_;
+  bool private_api_;
+
+  int render_process_id_;
+  int render_view_id_;
+
+  State state_;
+  bool end_of_file_reached_;
+
+  scoped_ptr<net::SingleRequestHostResolver> resolver_;
+  net::AddressList address_list_;
+
+  scoped_ptr<net::StreamSocket> socket_;
+
+  scoped_refptr<net::IOBuffer> read_buffer_;
+
+  // StreamSocket::Write() may not always write the full buffer, but we would
+  // rather have our DoWrite() do so whenever possible. To do this, we may have
+  // to call the former multiple times for each of the latter. This entails
+  // using a DrainableIOBuffer, which requires an underlying base IOBuffer.
+  scoped_refptr<net::IOBuffer> write_buffer_base_;
+  scoped_refptr<net::DrainableIOBuffer> write_buffer_;
+  scoped_refptr<SSLContextHelper> ssl_context_helper_;
+
+  DISALLOW_COPY_AND_ASSIGN(PepperTCPSocketMessageFilter);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_TCP_SOCKET_MESSAGE_FILTER_H_
diff --git a/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc b/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc
index 7453fd2..4a36c3d 100644
--- a/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc
+++ b/content/browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc
@@ -28,8 +28,8 @@
 #include "ppapi/shared_impl/private/net_address_private_impl.h"
 #include "ppapi/shared_impl/socket_option_data.h"
 
-using ppapi::host::NetErrorToPepperError;
 using ppapi::NetAddressPrivateImpl;
+using ppapi::host::NetErrorToPepperError;
 
 namespace {
 
diff --git a/content/browser/renderer_host/pepper/ssl_context_helper.cc b/content/browser/renderer_host/pepper/ssl_context_helper.cc
new file mode 100644
index 0000000..3b92e6d
--- /dev/null
+++ b/content/browser/renderer_host/pepper/ssl_context_helper.cc
@@ -0,0 +1,30 @@
+// 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 "content/browser/renderer_host/pepper/ssl_context_helper.h"
+
+#include "net/cert/cert_verifier.h"
+#include "net/http/transport_security_state.h"
+
+namespace content {
+
+SSLContextHelper::SSLContextHelper() {
+}
+
+SSLContextHelper::~SSLContextHelper() {
+}
+
+net::CertVerifier* SSLContextHelper::GetCertVerifier() {
+  if (!cert_verifier_)
+    cert_verifier_.reset(net::CertVerifier::CreateDefault());
+  return cert_verifier_.get();
+}
+
+net::TransportSecurityState* SSLContextHelper::GetTransportSecurityState() {
+  if (!transport_security_state_)
+    transport_security_state_.reset(new net::TransportSecurityState());
+  return transport_security_state_.get();
+}
+
+}  // namespace content
diff --git a/content/browser/renderer_host/pepper/ssl_context_helper.h b/content/browser/renderer_host/pepper/ssl_context_helper.h
new file mode 100644
index 0000000..f1da2a6
--- /dev/null
+++ b/content/browser/renderer_host/pepper/ssl_context_helper.h
@@ -0,0 +1,48 @@
+// 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 CONTENT_BROWSER_RENDERER_HOST_PEPPER_SSL_CONTEXT_HELPER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_PEPPER_SSL_CONTEXT_HELPER_H_
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/ssl/ssl_config_service.h"
+
+namespace net {
+class CertVerifier;
+class TransportSecurityState;
+}
+
+namespace content {
+
+class SSLContextHelper : public base::RefCounted<SSLContextHelper> {
+ public:
+  SSLContextHelper();
+
+  net::CertVerifier* GetCertVerifier();
+  net::TransportSecurityState* GetTransportSecurityState();
+  const net::SSLConfig& ssl_config() { return ssl_config_; }
+
+ private:
+  friend class base::RefCounted<SSLContextHelper>;
+
+  ~SSLContextHelper();
+
+  // This is lazily created. Users should use GetCertVerifier to retrieve it.
+  scoped_ptr<net::CertVerifier> cert_verifier_;
+  // This is lazily created. Users should use GetTransportSecurityState to
+  // retrieve it.
+  scoped_ptr<net::TransportSecurityState> transport_security_state_;
+
+  // The default SSL configuration settings are used, as opposed to Chrome's SSL
+  // settings.
+  net::SSLConfig ssl_config_;
+
+  DISALLOW_COPY_AND_ASSIGN(SSLContextHelper);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_RENDERER_HOST_PEPPER_SSL_CONTEXT_HELPER_H_
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index ea511d0..ec621c1 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -4,31 +4,64 @@
 
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 
+#include "base/containers/hash_tables.h"
+#include "base/lazy_instance.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 
 namespace content {
 
+// The (process id, routing id) pair that identifies one RenderFrame.
+typedef std::pair<int32, int32> RenderFrameHostID;
+typedef base::hash_map<RenderFrameHostID, RenderFrameHostImpl*>
+    RoutingIDFrameMap;
+static base::LazyInstance<RoutingIDFrameMap> g_routing_id_frame_map =
+    LAZY_INSTANCE_INITIALIZER;
+
+// static
+RenderFrameHostImpl* RenderFrameHostImpl::FromID(
+    int process_id, int routing_id) {
+  RoutingIDFrameMap* frames = g_routing_id_frame_map.Pointer();
+  RoutingIDFrameMap::iterator it = frames->find(
+      RenderFrameHostID(process_id, routing_id));
+  return it == frames->end() ? NULL : it->second;
+}
+
 RenderFrameHostImpl::RenderFrameHostImpl(
     RenderViewHostImpl* render_view_host,
     int routing_id,
-    bool swapped_out)
+    bool is_swapped_out)
     : render_view_host_(render_view_host),
-      routing_id_(routing_id) {
+      routing_id_(routing_id),
+      is_swapped_out_(is_swapped_out) {
+  GetProcess()->AddRoute(routing_id_, this);
+  g_routing_id_frame_map.Get().insert(std::make_pair(
+      RenderFrameHostID(GetProcess()->GetID(), routing_id_),
+      this));
 }
 
 RenderFrameHostImpl::~RenderFrameHostImpl() {
+  GetProcess()->RemoveRoute(routing_id_);
+  g_routing_id_frame_map.Get().erase(
+      RenderFrameHostID(GetProcess()->GetID(), routing_id_));
+
 }
 
 bool RenderFrameHostImpl::Send(IPC::Message* message) {
-  // Use the RenderViewHost object to send the message. It inherits it from
-  // RenderWidgetHost, which ultimately uses the current process's |Send|.
-  return render_view_host_->Send(message);
+  return GetProcess()->Send(message);
 }
 
 bool RenderFrameHostImpl::OnMessageReceived(const IPC::Message &msg) {
-  // Pass the message up to the RenderViewHost, until we have enough
-  // infrastructure to start processing messages in this object.
-  return render_view_host_->OnMessageReceived(msg);
+  return false;
+}
+
+void RenderFrameHostImpl::Init() {
+  GetProcess()->ResumeRequestsForView(routing_id());
+}
+
+RenderProcessHost* RenderFrameHostImpl::GetProcess() const {
+  // TODO(ajwong): This should return its own process once cross-process
+  // subframe navigations are supported.
+  return render_view_host_->GetProcess();
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index bb94c8e..78df333 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -10,14 +10,16 @@
 
 namespace content {
 
+class RenderProcessHost;
 class RenderViewHostImpl;
 
 class CONTENT_EXPORT RenderFrameHostImpl : public RenderFrameHost {
  public:
-  RenderFrameHostImpl(
-      RenderViewHostImpl* render_view_host,
-      int routing_id,
-      bool swapped_out);
+  static RenderFrameHostImpl* FromID(int process_id, int routing_id);
+
+  RenderFrameHostImpl(RenderViewHostImpl* render_view_host,
+                      int routing_id,
+                      bool is_swapped_out);
   virtual ~RenderFrameHostImpl();
 
   // IPC::Sender
@@ -26,12 +28,16 @@
   // IPC::Listener
   virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE;
 
-  int routing_id() { return routing_id_; }
+  void Init();
+  RenderProcessHost* GetProcess() const;
+  int routing_id() const { return routing_id_; }
 
  private:
-  RenderViewHostImpl* render_view_host_;
+  bool is_swapped_out() { return is_swapped_out_; }
 
+  RenderViewHostImpl* render_view_host_;  // Not owned. Outlives this object.
   int routing_id_;
+  bool is_swapped_out_;
 
   DISALLOW_COPY_AND_ASSIGN(RenderFrameHostImpl);
 };
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 39b3eda..53b2e91 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -585,7 +585,7 @@
       media_internals,
       media_stream_manager));
   channel_->AddFilter(
-      new MIDIHost(BrowserMainLoop::GetInstance()->midi_manager()));
+      new MIDIHost(GetID(), BrowserMainLoop::GetInstance()->midi_manager()));
   channel_->AddFilter(new MIDIDispatcherHost(GetID(), browser_context));
   channel_->AddFilter(new VideoCaptureHost(media_stream_manager));
   channel_->AddFilter(new AppCacheDispatcherHost(
@@ -623,7 +623,7 @@
 #endif
 #if defined(ENABLE_PLUGINS)
   // TODO(raymes): PepperMessageFilter should be removed from here.
-  channel_->AddFilter(new PepperMessageFilter(GetID(), browser_context));
+  channel_->AddFilter(PepperMessageFilter::CreateForRendererProcess());
   channel_->AddFilter(new PepperRendererConnection(GetID()));
 #endif
 #if defined(ENABLE_INPUT_SPEECH)
@@ -840,6 +840,7 @@
     switches::kAudioBufferSize,
     switches::kAuditAllHandles,
     switches::kAuditHandles,
+    switches::kBlockCrossSiteDocuments,
     switches::kDisable3DAPIs,
     switches::kDisableAcceleratedCompositing,
     switches::kDisableAcceleratedVideoDecode,
@@ -996,6 +997,7 @@
     switches::kWebCoreLogChannels,
     switches::kEnableWebGLDraftExtensions,
     switches::kEnableHTMLImports,
+    switches::kEnableInputModeAttribute,
     switches::kTraceToConsole,
     switches::kEnableDeviceMotion,
 #if defined(OS_ANDROID)
@@ -1007,8 +1009,10 @@
     cc::switches::kCompositeToMailbox,
     cc::switches::kDisableCompositedAntialiasing,
     cc::switches::kDisableImplSidePainting,
+    cc::switches::kDisableMapImage,
     cc::switches::kDisableThreadedAnimation,
     cc::switches::kEnableImplSidePainting,
+    cc::switches::kEnableMapImage,
     cc::switches::kEnablePartialSwap,
     cc::switches::kEnablePerTilePainting,
     cc::switches::kEnablePinchVirtualViewport,
@@ -1032,7 +1036,6 @@
     cc::switches::kTopControlsHideThreshold,
     cc::switches::kTopControlsShowThreshold,
     cc::switches::kTraceOverdraw,
-    cc::switches::kUseMapImage,
   };
   renderer_cmd->CopySwitchesFrom(browser_cmd, kSwitchNames,
                                  arraysize(kSwitchNames));
diff --git a/content/browser/renderer_host/render_view_host_delegate.h b/content/browser/renderer_host/render_view_host_delegate.h
index 21b22b3..5cb5172 100644
--- a/content/browser/renderer_host/render_view_host_delegate.h
+++ b/content/browser/renderer_host/render_view_host_delegate.h
@@ -262,6 +262,7 @@
                                     const string16& default_prompt,
                                     const GURL& frame_url,
                                     JavaScriptMessageType type,
+                                    bool user_gesture,
                                     IPC::Message* reply_msg,
                                     bool* did_suppress_message) {}
 
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index c841f28..de33517 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -72,7 +72,7 @@
 #if defined(OS_MACOSX)
 #include "content/browser/renderer_host/popup_menu_helper_mac.h"
 #elif defined(OS_ANDROID)
-#include "media/base/android/media_player_manager.h"
+#include "content/browser/android/browser_media_player_manager.h"
 #endif
 
 using base::TimeDelta;
@@ -116,11 +116,7 @@
 // static
 RenderViewHost* RenderViewHost::FromID(int render_process_id,
                                        int render_view_id) {
-  RenderWidgetHost* widget =
-      RenderWidgetHost::FromID(render_process_id, render_view_id);
-  if (!widget || !widget->IsRenderView())
-    return NULL;
-  return static_cast<RenderViewHostImpl*>(RenderWidgetHostImpl::From(widget));
+  return RenderViewHostImpl::FromID(render_process_id, render_view_id);
 }
 
 // static
@@ -143,8 +139,11 @@
 // static
 RenderViewHostImpl* RenderViewHostImpl::FromID(int render_process_id,
                                                int render_view_id) {
-  return static_cast<RenderViewHostImpl*>(
-      RenderViewHost::FromID(render_process_id, render_view_id));
+  RenderWidgetHost* widget =
+      RenderWidgetHost::FromID(render_process_id, render_view_id);
+  if (!widget || !widget->IsRenderView())
+    return NULL;
+  return static_cast<RenderViewHostImpl*>(RenderWidgetHostImpl::From(widget));
 }
 
 RenderViewHostImpl::RenderViewHostImpl(
@@ -195,7 +194,7 @@
     instance_->increment_active_view_count();
 
 #if defined(OS_ANDROID)
-  media_player_manager_ = media::MediaPlayerManager::Create(this);
+  media_player_manager_ = BrowserMediaPlayerManager::Create(this);
 #endif
 }
 
@@ -1022,6 +1021,11 @@
   return handled;
 }
 
+void RenderViewHostImpl::Init() {
+  RenderWidgetHostImpl::Init();
+  main_render_frame_host()->Init();
+}
+
 void RenderViewHostImpl::Shutdown() {
   // If we are being run modally (see RunModal), then we need to cleanup.
   if (run_modal_reply_msg_) {
@@ -1431,13 +1435,14 @@
     const string16& default_prompt,
     const GURL& frame_url,
     JavaScriptMessageType type,
+    bool user_gesture,
     IPC::Message* reply_msg) {
   // While a JS message dialog is showing, tabs in the same process shouldn't
   // process input events.
   GetProcess()->SetIgnoreInputEvents(true);
   StopHangMonitorTimeout();
   delegate_->RunJavaScriptMessage(this, message, default_prompt, frame_url,
-                                  type, reply_msg,
+                                  type, user_gesture, reply_msg,
                                   &are_javascript_messages_suppressed_);
 }
 
@@ -2031,6 +2036,11 @@
 }
 #endif
 
+RenderFrameHostImpl* RenderViewHostImpl::main_render_frame_host() const {
+  DCHECK_EQ(GetProcess(), main_render_frame_host_->GetProcess());
+  return main_render_frame_host_.get();
+}
+
 void RenderViewHostImpl::SetSwappedOut(bool is_swapped_out) {
   // We update the number of RenderViews in a SiteInstance when the
   // swapped out status of this RenderView gets flipped.
diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h
index 91353d5..880a339 100644
--- a/content/browser/renderer_host/render_view_host_impl.h
+++ b/content/browser/renderer_host/render_view_host_impl.h
@@ -53,14 +53,9 @@
 struct SelectedFileInfo;
 }
 
-#if defined(OS_ANDROID)
-namespace media {
-class MediaPlayerManager;
-}
-#endif
-
 namespace content {
 
+class BrowserMediaPlayerManager;
 class ChildProcessSecurityPolicyImpl;
 class PageState;
 class RenderFrameHostImpl;
@@ -356,6 +351,7 @@
   }
 
   // RenderWidgetHost public overrides.
+  virtual void Init() OVERRIDE;
   virtual void Shutdown() OVERRIDE;
   virtual bool IsRenderView() const OVERRIDE;
   virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE;
@@ -390,7 +386,7 @@
 #endif
 
 #if defined(OS_ANDROID)
-  media::MediaPlayerManager* media_player_manager() {
+  BrowserMediaPlayerManager* media_player_manager() {
     return media_player_manager_;
   }
 
@@ -538,6 +534,7 @@
                               const string16& default_prompt,
                               const GURL& frame_url,
                               JavaScriptMessageType type,
+                              bool user_gesture,
                               IPC::Message* reply_msg);
   void OnRunBeforeUnloadConfirm(const GURL& frame_url,
                                 const string16& message,
@@ -587,6 +584,11 @@
  private:
   friend class TestRenderViewHost;
   FRIEND_TEST_ALL_PREFIXES(RenderViewHostTest, BasicRenderFrameHost);
+  FRIEND_TEST_ALL_PREFIXES(RenderViewHostTest, RoutingIdSane);
+
+  // TODO(nasko): Remove this accessor once RenderFrameHost moves into the frame
+  // tree.
+  RenderFrameHostImpl* main_render_frame_host() const;
 
   // Sets whether this RenderViewHost is swapped out in favor of another,
   // and clears any waiting state that is no longer relevant.
@@ -702,7 +704,7 @@
 #if defined(OS_ANDROID)
   // Manages all the android mediaplayer objects and handling IPCs for video.
   // This class inherits from RenderViewHostObserver.
-  media::MediaPlayerManager* media_player_manager_;
+  BrowserMediaPlayerManager* media_player_manager_;
 #endif
 
   DISALLOW_COPY_AND_ASSIGN(RenderViewHostImpl);
diff --git a/content/browser/renderer_host/render_view_host_unittest.cc b/content/browser/renderer_host/render_view_host_unittest.cc
index a38e044..f4d694f 100644
--- a/content/browser/renderer_host/render_view_host_unittest.cc
+++ b/content/browser/renderer_host/render_view_host_unittest.cc
@@ -288,4 +288,11 @@
   EXPECT_EQ(1, process()->bad_msg_count());
 }
 
+TEST_F(RenderViewHostTest, RoutingIdSane) {
+  EXPECT_EQ(test_rvh()->GetProcess(),
+            test_rvh()->main_render_frame_host()->GetProcess());
+  EXPECT_NE(test_rvh()->GetRoutingID(),
+            test_rvh()->main_render_frame_host()->routing_id());
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 16892a1..d7e5479 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -362,8 +362,8 @@
 }
 
 base::TimeDelta
-    RenderWidgetHostImpl::GetSyntheticScrollMessageInterval() const {
-  return smooth_scroll_gesture_controller_.GetSyntheticScrollMessageInterval();
+    RenderWidgetHostImpl::GetSyntheticGestureMessageInterval() const {
+  return synthetic_gesture_controller_.GetSyntheticGestureMessageInterval();
 }
 
 void RenderWidgetHostImpl::SetOverscrollControllerEnabled(bool enabled) {
@@ -1016,6 +1016,12 @@
     const MouseEventWithLatencyInfo& mouse_event) {
   TRACE_EVENT2("input", "RenderWidgetHostImpl::ForwardMouseEvent",
                "x", mouse_event.event.x, "y", mouse_event.event.y);
+
+  for (size_t i = 0; i < mouse_event_callbacks_.size(); ++i) {
+    if (mouse_event_callbacks_[i].Run(mouse_event.event))
+      return;
+  }
+
   input_router_->SendMouseEvent(mouse_event);
 }
 
@@ -1123,15 +1129,35 @@
 }
 
 
-void RenderWidgetHostImpl::AddKeyboardListener(KeyboardListener* listener) {
-  keyboard_listeners_.AddObserver(listener);
+void RenderWidgetHostImpl::AddKeyPressEventCallback(
+    const KeyPressEventCallback& callback) {
+  key_press_event_callbacks_.push_back(callback);
 }
 
-void RenderWidgetHostImpl::RemoveKeyboardListener(
-    KeyboardListener* listener) {
-  // Ensure that the element is actually an observer.
-  DCHECK(keyboard_listeners_.HasObserver(listener));
-  keyboard_listeners_.RemoveObserver(listener);
+void RenderWidgetHostImpl::RemoveKeyPressEventCallback(
+    const KeyPressEventCallback& callback) {
+  for (size_t i = 0; i < key_press_event_callbacks_.size(); ++i) {
+    if (key_press_event_callbacks_[i].Equals(callback)) {
+      key_press_event_callbacks_.erase(
+          key_press_event_callbacks_.begin() + i);
+      return;
+    }
+  }
+}
+
+void RenderWidgetHostImpl::AddMouseEventCallback(
+    const MouseEventCallback& callback) {
+  mouse_event_callbacks_.push_back(callback);
+}
+
+void RenderWidgetHostImpl::RemoveMouseEventCallback(
+    const MouseEventCallback& callback) {
+  for (size_t i = 0; i < mouse_event_callbacks_.size(); ++i) {
+    if (mouse_event_callbacks_[i].Equals(callback)) {
+      mouse_event_callbacks_.erase(mouse_event_callbacks_.begin() + i);
+      return;
+    }
+  }
 }
 
 void RenderWidgetHostImpl::GetWebScreenInfo(WebKit::WebScreenInfo* result) {
@@ -1675,7 +1701,7 @@
     const ViewHostMsg_BeginSmoothScroll_Params& params) {
   if (!view_)
     return;
-  smooth_scroll_gesture_controller_.BeginSmoothScroll(view_, params);
+  synthetic_gesture_controller_.BeginSmoothScroll(view_, params);
 }
 
 void RenderWidgetHostImpl::OnFocus() {
@@ -1889,10 +1915,8 @@
   if (event.skip_in_browser || event.type != WebKeyboardEvent::RawKeyDown)
     return false;
 
-  ObserverList<KeyboardListener>::Iterator it(keyboard_listeners_);
-  KeyboardListener* listener;
-  while ((listener = it.GetNext()) != NULL) {
-    if (listener->HandleKeyPressEvent(event))
+  for (size_t i = 0; i < key_press_event_callbacks_.size(); i++) {
+    if (key_press_event_callbacks_[i].Run(event))
       return true;
   }
 
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 2004c0b..8dde60a 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -24,7 +24,7 @@
 #include "base/timer/timer.h"
 #include "build/build_config.h"
 #include "content/browser/renderer_host/input/input_router_client.h"
-#include "content/browser/renderer_host/smooth_scroll_gesture_controller.h"
+#include "content/browser/renderer_host/synthetic_gesture_controller.h"
 #include "content/common/browser_rendering_stats.h"
 #include "content/common/view_message_enums.h"
 #include "content/port/browser/event_with_latency_info.h"
@@ -78,7 +78,7 @@
 class OverscrollController;
 class RenderWidgetHostDelegate;
 class RenderWidgetHostViewPort;
-class SmoothScrollGestureController;
+class SyntheticGestureController;
 struct EditCommand;
 
 // This implements the RenderWidgetHost interface that is exposed to
@@ -173,8 +173,14 @@
   virtual void SetIgnoreInputEvents(bool ignore_input_events) OVERRIDE;
   virtual void Stop() OVERRIDE;
   virtual void WasResized() OVERRIDE;
-  virtual void AddKeyboardListener(KeyboardListener* listener) OVERRIDE;
-  virtual void RemoveKeyboardListener(KeyboardListener* listener) OVERRIDE;
+  virtual void AddKeyPressEventCallback(
+      const KeyPressEventCallback& callback) OVERRIDE;
+  virtual void RemoveKeyPressEventCallback(
+      const KeyPressEventCallback& callback) OVERRIDE;
+  virtual void AddMouseEventCallback(
+      const MouseEventCallback& callback) OVERRIDE;
+  virtual void RemoveMouseEventCallback(
+      const MouseEventCallback& callback) OVERRIDE;
   virtual void GetWebScreenInfo(WebKit::WebScreenInfo* result) OVERRIDE;
   virtual void GetSnapshotFromRenderer(
       const gfx::Rect& src_subrect,
@@ -200,7 +206,7 @@
   // Called when a renderer object already been created for this host, and we
   // just need to be attached to it. Used for window.open, <select> dropdown
   // menus, and other times when the renderer initiates creating an object.
-  void Init();
+  virtual void Init();
 
   // Tells the renderer to die and then calls Destroy().
   virtual void Shutdown();
@@ -504,7 +510,7 @@
     return overscroll_controller_.get();
   }
 
-  base::TimeDelta GetSyntheticScrollMessageInterval() const;
+  base::TimeDelta GetSyntheticGestureMessageInterval() const;
 
   // Sets whether the overscroll controller should be enabled for this page.
   void SetOverscrollControllerEnabled(bool enabled);
@@ -822,7 +828,10 @@
   AccessibilityMode accessibility_mode_;
 
   // Keyboard event listeners.
-  ObserverList<KeyboardListener> keyboard_listeners_;
+  std::vector<KeyPressEventCallback> key_press_event_callbacks_;
+
+  // Mouse event callbacks.
+  std::vector<MouseEventCallback> mouse_event_callbacks_;
 
   // If true, then we should repaint when restoring even if we have a
   // backingstore.  This flag is set to true if we receive a paint message
@@ -900,7 +909,7 @@
 
   base::WeakPtrFactory<RenderWidgetHostImpl> weak_factory_;
 
-  SmoothScrollGestureController smooth_scroll_gesture_controller_;
+  SyntheticGestureController synthetic_gesture_controller_;
 
   // Receives and handles all input events.
   scoped_ptr<InputRouter> input_router_;
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc
index 980fb66..54138de 100644
--- a/content/browser/renderer_host/render_widget_host_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/basictypes.h"
+#include "base/bind.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/shared_memory.h"
 #include "base/timer/timer.h"
@@ -104,30 +105,6 @@
   DISALLOW_COPY_AND_ASSIGN(TestOverscrollDelegate);
 };
 
-// MockKeyboardListener --------------------------------------------------------
-class MockKeyboardListener : public KeyboardListener {
- public:
-  MockKeyboardListener()
-      : handle_key_press_event_(false) {
-  }
-  virtual ~MockKeyboardListener() {}
-
-  // KeyboardListener:
-  virtual bool HandleKeyPressEvent(
-      const NativeWebKeyboardEvent& event) OVERRIDE {
-    return handle_key_press_event_;
-  }
-
-  void set_handle_key_press_event(bool handle) {
-    handle_key_press_event_ = handle;
-  }
-
- private:
-  bool handle_key_press_event_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockKeyboardListener);
-};
-
 // MockInputRouter -------------------------------------------------------------
 
 class MockInputRouter : public InputRouter {
@@ -628,11 +605,21 @@
 
 class RenderWidgetHostTest : public testing::Test {
  public:
-  RenderWidgetHostTest() : process_(NULL) {
+  RenderWidgetHostTest()
+      : process_(NULL),
+        handle_key_press_event_(false),
+        handle_mouse_event_(false) {
   }
   virtual ~RenderWidgetHostTest() {
   }
 
+  bool KeyPressEventCallback(const NativeWebKeyboardEvent& /* event */) {
+    return handle_key_press_event_;
+  }
+  bool MouseEventCallback(const WebKit::WebMouseEvent& /* event */) {
+    return handle_mouse_event_;
+  }
+
  protected:
   // testing::Test
   virtual void SetUp() {
@@ -835,6 +822,8 @@
   scoped_ptr<MockRenderWidgetHost> host_;
   scoped_ptr<TestView> view_;
   scoped_ptr<gfx::Screen> screen_;
+  bool handle_key_press_event_;
+  bool handle_mouse_event_;
 
  private:
   WebTouchEvent touch_event_;
@@ -2498,32 +2487,30 @@
 
 TEST_F(RenderWidgetHostTest, KeyboardListenerIgnoresEvent) {
   host_->SetupForInputRouterTest();
-
-  scoped_ptr<MockKeyboardListener> keyboard_listener_(new MockKeyboardListener);
-  host_->AddKeyboardListener(keyboard_listener_.get());
-
-  keyboard_listener_->set_handle_key_press_event(false);
+  host_->AddKeyPressEventCallback(
+      base::Bind(&RenderWidgetHostTest::KeyPressEventCallback,
+                 base::Unretained(this)));
+  handle_key_press_event_ = false;
   SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
 
   EXPECT_TRUE(host_->mock_input_router()->sent_keyboard_event_);
-
-  host_->RemoveKeyboardListener(keyboard_listener_.get());
 }
 
 TEST_F(RenderWidgetHostTest, KeyboardListenerSuppressFollowingEvents) {
   host_->SetupForInputRouterTest();
 
-  scoped_ptr<MockKeyboardListener> keyboard_listener_(new MockKeyboardListener);
-  host_->AddKeyboardListener(keyboard_listener_.get());
+  host_->AddKeyPressEventCallback(
+      base::Bind(&RenderWidgetHostTest::KeyPressEventCallback,
+                 base::Unretained(this)));
 
-  // KeyboardListener handles the first event
-  keyboard_listener_->set_handle_key_press_event(true);
+  // The callback handles the first event
+  handle_key_press_event_ = true;
   SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
 
   EXPECT_FALSE(host_->mock_input_router()->sent_keyboard_event_);
 
   // Following Char events should be suppressed
-  keyboard_listener_->set_handle_key_press_event(false);
+  handle_key_press_event_ = false;
   SimulateKeyboardEvent(WebInputEvent::Char);
   EXPECT_FALSE(host_->mock_input_router()->sent_keyboard_event_);
   SimulateKeyboardEvent(WebInputEvent::Char);
@@ -2536,8 +2523,24 @@
   host_->mock_input_router()->sent_keyboard_event_ = false;
   SimulateKeyboardEvent(WebInputEvent::Char);
   EXPECT_TRUE(host_->mock_input_router()->sent_keyboard_event_);
+}
 
-  host_->RemoveKeyboardListener(keyboard_listener_.get());
+TEST_F(RenderWidgetHostTest, MouseEventCallbackCanHandleEvent) {
+  host_->SetupForInputRouterTest();
+
+  host_->AddMouseEventCallback(
+      base::Bind(&RenderWidgetHostTest::MouseEventCallback,
+                 base::Unretained(this)));
+
+  handle_mouse_event_ = true;
+  SimulateMouseEvent(WebInputEvent::MouseDown);
+
+  EXPECT_FALSE(host_->mock_input_router()->sent_mouse_event_);
+
+  handle_mouse_event_ = false;
+  SimulateMouseEvent(WebInputEvent::MouseDown);
+
+  EXPECT_TRUE(host_->mock_input_router()->sent_mouse_event_);
 }
 
 TEST_F(RenderWidgetHostTest, InputRouterReceivesHandleInputEvent_ACK) {
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 8e88540..7283973 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -6,8 +6,9 @@
 
 #include <android/bitmap.h>
 
+#include "base/basictypes.h"
 #include "base/bind.h"
-#include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/message_loop/message_loop.h"
@@ -28,10 +29,10 @@
 #include "content/browser/gpu/gpu_surface_tracker.h"
 #include "content/browser/renderer_host/compositor_impl_android.h"
 #include "content/browser/renderer_host/dip_util.h"
+#include "content/browser/renderer_host/generic_touch_gesture_android.h"
 #include "content/browser/renderer_host/image_transport_factory_android.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/browser/renderer_host/surface_texture_transport_client_android.h"
-#include "content/browser/renderer_host/touch_smooth_scroll_gesture_android.h"
 #include "content/common/gpu/client/gl_helper.h"
 #include "content/common/gpu/gpu_messages.h"
 #include "content/common/input_messages.h"
@@ -420,9 +421,9 @@
   // If an acknowledgement is required for this event, regardless of how we exit
   // from this method, we must acknowledge that we processed the input state
   // change.
-  base::ScopedClosureRunner ack_caller(base::Bind(&SendImeEventAck, host_));
-  if (!params.require_ack)
-    ack_caller.Release();
+  base::ScopedClosureRunner ack_caller;
+  if (params.require_ack)
+    ack_caller.Reset(base::Bind(&SendImeEventAck, host_));
 
   if (!IsShowing())
     return;
@@ -606,14 +607,14 @@
   content_view_core_->ShowDisambiguationPopup(target_rect, zoomed_bitmap);
 }
 
-SmoothScrollGesture* RenderWidgetHostViewAndroid::CreateSmoothScrollGesture(
+SyntheticGesture* RenderWidgetHostViewAndroid::CreateSmoothScrollGesture(
     bool scroll_down, int pixels_to_scroll, int mouse_event_x,
     int mouse_event_y) {
-  return new TouchSmoothScrollGestureAndroid(
-      pixels_to_scroll,
+  return new GenericTouchGestureAndroid(
       GetRenderWidgetHost(),
-      content_view_core_->CreateSmoothScroller(
-          scroll_down, mouse_event_x, mouse_event_y));
+      content_view_core_->CreateGenericTouchGesture(
+          mouse_event_x, mouse_event_y,
+          0, scroll_down ? -pixels_to_scroll : pixels_to_scroll));
 }
 
 void RenderWidgetHostViewAndroid::OnAcceleratedCompositingStateChange() {
@@ -1228,7 +1229,7 @@
   if (!texture_mailbox->IsTexture())
     return;
 
-  scoped_callback_runner.Release();
+  ignore_result(scoped_callback_runner.Release());
 
   gl_helper->CropScaleReadbackAndCleanMailbox(
       texture_mailbox->name(),
@@ -1264,7 +1265,7 @@
   DCHECK_EQ(source->width(), dst_size_in_pixel.width());
   DCHECK_EQ(source->height(), dst_size_in_pixel.height());
 
-  scoped_callback_runner.Release();
+  ignore_result(scoped_callback_runner.Release());
   callback.Run(true, *source);
 }
 
diff --git a/content/browser/renderer_host/render_widget_host_view_android.h b/content/browser/renderer_host/render_widget_host_view_android.h
index d8ea83d..2282dba 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.h
+++ b/content/browser/renderer_host/render_widget_host_view_android.h
@@ -165,7 +165,7 @@
                               gfx::Vector2dF current_fling_velocity) OVERRIDE;
   virtual void ShowDisambiguationPopup(const gfx::Rect& target_rect,
                                        const SkBitmap& zoomed_bitmap) OVERRIDE;
-  virtual SmoothScrollGesture* CreateSmoothScrollGesture(
+  virtual SyntheticGesture* CreateSmoothScrollGesture(
       bool scroll_down, int pixels_to_scroll, int mouse_event_x,
       int mouse_event_y) OVERRIDE;
 
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 2369044..261278f 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -4,8 +4,9 @@
 
 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
 
+#include "base/basictypes.h"
 #include "base/bind.h"
-#include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/debug/trace_event.h"
 #include "base/logging.h"
@@ -1815,7 +1816,7 @@
   if (!texture_mailbox->IsTexture())
     return;
 
-  scoped_callback_runner.Release();
+  ignore_result(scoped_callback_runner.Release());
 
   gl_helper->CropScaleReadbackAndCleanMailbox(
       texture_mailbox->name(),
@@ -1848,7 +1849,7 @@
   if (!source)
     return;
 
-  scoped_callback_runner.Release();
+  ignore_result(scoped_callback_runner.Release());
 
   SkBitmap bitmap = skia::ImageOperations::Resize(
       *source,
@@ -1925,7 +1926,7 @@
           region_in_frame,
           video_frame.get());
     }
-    scoped_callback_runner.Release();
+    ignore_result(scoped_callback_runner.Release());
     callback.Run(true);
     return;
   }
@@ -1975,7 +1976,7 @@
     yuv_readback_pipeline = rwhva->yuv_readback_pipeline_.get();
   }
 
-  scoped_callback_runner.Release();
+  ignore_result(scoped_callback_runner.Release());
   base::Callback<void(bool result)> finished_callback = base::Bind(
       &CopyFromCompositingSurfaceFinishedForVideo,
       callback,
@@ -2021,7 +2022,7 @@
   }
 }
 
-SmoothScrollGesture* RenderWidgetHostViewAura::CreateSmoothScrollGesture(
+SyntheticGesture* RenderWidgetHostViewAura::CreateSmoothScrollGesture(
     bool scroll_down,
     int pixels_to_scroll,
     int mouse_event_x,
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h
index c4a1627..0f72120 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.h
+++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -227,7 +227,7 @@
   virtual void ProcessAckedTouchEvent(
       const TouchEventWithLatencyInfo& touch,
       InputEventAckState ack_result) OVERRIDE;
-  virtual SmoothScrollGesture* CreateSmoothScrollGesture(
+  virtual SyntheticGesture* CreateSmoothScrollGesture(
       bool scroll_down,
       int pixels_to_scroll,
       int mouse_event_x,
diff --git a/content/browser/renderer_host/render_widget_host_view_base.cc b/content/browser/renderer_host/render_widget_host_view_base.cc
index d6b1e78..9de8a85 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.cc
+++ b/content/browser/renderer_host/render_widget_host_view_base.cc
@@ -11,7 +11,7 @@
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
 #include "content/port/browser/render_widget_host_view_frame_subscriber.h"
-#include "content/port/browser/smooth_scroll_gesture.h"
+#include "content/port/browser/synthetic_gesture.h"
 #include "third_party/WebKit/public/web/WebScreenInfo.h"
 #include "ui/gfx/display.h"
 #include "ui/gfx/screen.h"
@@ -501,7 +501,7 @@
   return true;
 }
 
-SmoothScrollGesture* RenderWidgetHostViewBase::CreateSmoothScrollGesture(
+SyntheticGesture* RenderWidgetHostViewBase::CreateSmoothScrollGesture(
     bool scroll_down, int pixels_to_scroll, int mouse_event_x,
     int mouse_event_y) {
   return new BasicMouseWheelSmoothScrollGesture(scroll_down, pixels_to_scroll,
diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h
index 68fcaff..ab8f8cb 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.h
+++ b/content/browser/renderer_host/render_widget_host_view_base.h
@@ -69,7 +69,7 @@
       GetBrowserAccessibilityManager() const OVERRIDE;
   virtual void ProcessAckedTouchEvent(const TouchEventWithLatencyInfo& touch,
                                       InputEventAckState ack_result) OVERRIDE;
-  virtual SmoothScrollGesture* CreateSmoothScrollGesture(
+  virtual SyntheticGesture* CreateSmoothScrollGesture(
       bool scroll_down, int pixels_to_scroll, int mouse_event_x,
       int mouse_event_y) OVERRIDE;
   virtual bool CanSubscribeFrame() const OVERRIDE;
diff --git a/content/browser/renderer_host/render_widget_host_view_browsertest.cc b/content/browser/renderer_host/render_widget_host_view_browsertest.cc
index 05ce07d..9adb554 100644
--- a/content/browser/renderer_host/render_widget_host_view_browsertest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_browsertest.cc
@@ -25,8 +25,8 @@
 #include "media/filters/skcanvas_video_renderer.h"
 #include "net/base/net_util.h"
 #include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
 #include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkDevice.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/gfx/size_conversions.h"
 #include "ui/gl/gl_switches.h"
@@ -568,7 +568,7 @@
     bitmap.allocPixels();
     bitmap.setIsOpaque(true);
 
-    SkDevice device(bitmap);
+    SkBitmapDevice device(bitmap);
     SkCanvas canvas(&device);
 
     video_renderer.Paint(video_frame.get(),
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index 0744888..b74097a 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -7,8 +7,9 @@
 #import <objc/runtime.h>
 #include <QuartzCore/QuartzCore.h>
 
+#include "base/basictypes.h"
 #include "base/bind.h"
-#include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/debug/crash_logging.h"
 #include "base/debug/trace_event.h"
@@ -1161,7 +1162,7 @@
   gfx::Size dst_pixel_size = gfx::ToFlooredSize(
       gfx::ScaleSize(dst_size, scale));
 
-  scoped_callback_runner.Release();
+  ignore_result(scoped_callback_runner.Release());
 
   compositing_iosurface_->CopyTo(GetScaledOpenGLPixelRect(src_subrect),
                                  dst_pixel_size,
@@ -1192,7 +1193,7 @@
   if (src_subrect.IsEmpty())
     return;
 
-  scoped_callback_runner.Release();
+  ignore_result(scoped_callback_runner.Release());
   compositing_iosurface_->CopyToVideoFrame(
       GetScaledOpenGLPixelRect(src_subrect),
       target,
@@ -1787,6 +1788,10 @@
     // Also note that it is necessary that clearDrawable be called if
     // overlapping views are not allowed, e.g, for content shell.
     // http://crbug.com/178408
+    // Disable screen updates so that the changes of flashes is minimized.
+    // http://crbug.com/279472
+    if (!use_core_animation_)
+      [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
     if (allow_overlapping_views_)
       DestroyCompositedIOSurfaceAndLayer(kLeaveContextBoundToView);
     else
diff --git a/content/browser/renderer_host/render_widget_host_view_win.cc b/content/browser/renderer_host/render_widget_host_view_win.cc
index b36b5e0..f3b8ae2 100644
--- a/content/browser/renderer_host/render_widget_host_view_win.cc
+++ b/content/browser/renderer_host/render_widget_host_view_win.cc
@@ -12,8 +12,9 @@
 #include <map>
 #include <stack>
 
+#include "base/basictypes.h"
 #include "base/bind.h"
-#include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/debug/trace_event.h"
 #include "base/i18n/rtl.h"
@@ -885,7 +886,7 @@
   if (dst_size.IsEmpty() || src_subrect.IsEmpty())
     return;
 
-  scoped_callback_runner.Release();
+  ignore_result(scoped_callback_runner.Release());
   accelerated_surface_->AsyncCopyTo(src_subrect, dst_size, callback);
 }
 
@@ -904,7 +905,7 @@
   if (src_subrect.IsEmpty())
     return;
 
-  scoped_callback_runner.Release();
+  ignore_result(scoped_callback_runner.Release());
   accelerated_surface_->AsyncCopyToVideoFrame(src_subrect, target, callback);
 }
 
@@ -1050,7 +1051,7 @@
     NOTREACHED();
     return ui::TEXT_INPUT_MODE_DEFAULT;
   }
-  return ui::TEXT_INPUT_MODE_DEFAULT;
+  return text_input_mode_;
 }
 
 bool RenderWidgetHostViewWin::CanComposeInline() const {
@@ -2169,18 +2170,20 @@
     WebKit::WebTouchPoint* touch_point,
     TOUCHINPUT* touch_input) {
   CPoint coordinates(
-    TOUCH_COORD_TO_PIXEL(touch_input->x) / ui::win::GetUndocumentedDPIScale(),
-    TOUCH_COORD_TO_PIXEL(touch_input->y) / ui::win::GetUndocumentedDPIScale());
+      TOUCH_COORD_TO_PIXEL(touch_input->x) /
+      ui::win::GetUndocumentedDPITouchScale(),
+      TOUCH_COORD_TO_PIXEL(touch_input->y) /
+      ui::win::GetUndocumentedDPITouchScale());
   int radius_x = 1;
   int radius_y = 1;
   if (touch_input->dwMask & TOUCHINPUTMASKF_CONTACTAREA) {
     // Some touch drivers send a contact area of "-1", yet flag it as valid.
     radius_x = std::max(1,
         static_cast<int>(TOUCH_COORD_TO_PIXEL(touch_input->cxContact) /
-                         ui::win::GetUndocumentedDPIScale()));
+                         ui::win::GetUndocumentedDPITouchScale()));
     radius_y = std::max(1,
         static_cast<int>(TOUCH_COORD_TO_PIXEL(touch_input->cyContact) /
-                         ui::win::GetUndocumentedDPIScale()));
+                         ui::win::GetUndocumentedDPITouchScale()));
   }
 
   // Detect and exclude stationary moves.
@@ -2243,8 +2246,10 @@
   if (total == 1 && (points[0].dwFlags & TOUCHEVENTF_DOWN)) {
     pointer_down_context_ = true;
     last_touch_location_ = gfx::Point(
-        TOUCH_COORD_TO_PIXEL(points[0].x) / ui::win::GetUndocumentedDPIScale(),
-        TOUCH_COORD_TO_PIXEL(points[0].y) / ui::win::GetUndocumentedDPIScale());
+        TOUCH_COORD_TO_PIXEL(points[0].x) /
+        ui::win::GetUndocumentedDPITouchScale(),
+        TOUCH_COORD_TO_PIXEL(points[0].y) /
+        ui::win::GetUndocumentedDPITouchScale());
   }
 
   bool should_forward = render_widget_host_->ShouldForwardTouchEvent() &&
@@ -3182,7 +3187,7 @@
     return;
 
   ui::tsf_inputscope::SetInputScopeForTsfUnawareWindow(
-      m_hWnd, text_input_type, ui::TEXT_INPUT_MODE_DEFAULT);
+      m_hWnd, text_input_type, text_input_mode_);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/content/browser/renderer_host/smooth_scroll_calculator.cc b/content/browser/renderer_host/smooth_scroll_calculator.cc
deleted file mode 100644
index 2538a18..0000000
--- a/content/browser/renderer_host/smooth_scroll_calculator.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 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 "content/browser/renderer_host/smooth_scroll_calculator.h"
-
-namespace content {
-
-SmoothScrollCalculator::SmoothScrollCalculator() {
-}
-
-SmoothScrollCalculator::~SmoothScrollCalculator() {
-}
-
-double SmoothScrollCalculator::GetScrollDelta(
-    base::TimeTicks now, base::TimeDelta desired_interval) {
-  double position_delta = 10;
-  if (!last_tick_time_.is_null()) {
-    double velocity = 10 / desired_interval.InMillisecondsF();
-    double time_delta = (now - last_tick_time_).InMillisecondsF();
-    position_delta = velocity * time_delta;
-  }
-
-  last_tick_time_ = now;
-  return position_delta;
-}
-
-}  // namespace content
diff --git a/content/browser/renderer_host/smooth_scroll_calculator.h b/content/browser/renderer_host/smooth_scroll_calculator.h
deleted file mode 100644
index 1427416..0000000
--- a/content/browser/renderer_host/smooth_scroll_calculator.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 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 CONTENT_BROWSER_RENDERER_HOST_SMOOTH_SCROLL_CALCULATOR_H_
-#define CONTENT_BROWSER_RENDERER_HOST_SMOOTH_SCROLL_CALCULATOR_H_
-
-#include "base/time/time.h"
-
-namespace content {
-
-// An utility class to calculate the delta for smooth scroll gesture
-// events.
-class SmoothScrollCalculator {
- public:
-  SmoothScrollCalculator();
-  ~SmoothScrollCalculator();
-
-  double GetScrollDelta(base::TimeTicks now, base::TimeDelta desired_interval);
-
- private:
-  base::TimeTicks last_tick_time_;
-
-  DISALLOW_COPY_AND_ASSIGN(SmoothScrollCalculator);
-};
-
-} // namespace content
-
-#endif  // CONTENT_BROWSER_RENDERER_HOST_SMOOTH_SCROLL_CALCULATOR_H_
diff --git a/content/browser/renderer_host/smooth_scroll_gesture_controller.cc b/content/browser/renderer_host/smooth_scroll_gesture_controller.cc
deleted file mode 100644
index ca9dfae..0000000
--- a/content/browser/renderer_host/smooth_scroll_gesture_controller.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (c) 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 "content/browser/renderer_host/smooth_scroll_gesture_controller.h"
-
-#include "base/debug/trace_event.h"
-#include "base/message_loop/message_loop.h"
-#include "content/common/view_messages.h"
-#include "content/port/browser/render_widget_host_view_port.h"
-#include "content/port/browser/smooth_scroll_gesture.h"
-#include "content/public/browser/render_widget_host.h"
-
-namespace content {
-
-namespace {
-
-// How many milliseconds apart synthetic scroll messages should be sent.
-const int kSyntheticScrollMessageIntervalMs = 7;
-
-}  // namespace
-
-SmoothScrollGestureController::SmoothScrollGestureController()
-    : rwh_(NULL) {
-}
-
-SmoothScrollGestureController::~SmoothScrollGestureController() {
-}
-
-void SmoothScrollGestureController::BeginSmoothScroll(
-    RenderWidgetHostViewPort* view,
-    const ViewHostMsg_BeginSmoothScroll_Params& params) {
-  if (pending_smooth_scroll_gesture_.get())
-    return;
-
-  rwh_ = view->GetRenderWidgetHost();
-  pending_smooth_scroll_gesture_ = view->CreateSmoothScrollGesture(
-      params.scroll_down,
-      params.pixels_to_scroll,
-      params.mouse_event_x,
-      params.mouse_event_y);
-
-  timer_.Start(FROM_HERE, GetSyntheticScrollMessageInterval(), this,
-               &SmoothScrollGestureController::OnTimer);
-}
-
-base::TimeDelta
-    SmoothScrollGestureController::GetSyntheticScrollMessageInterval() const {
-  return base::TimeDelta::FromMilliseconds(kSyntheticScrollMessageIntervalMs);
-}
-
-void SmoothScrollGestureController::OnTimer() {
-  base::TimeTicks now = base::TimeTicks::Now();
-  if (!pending_smooth_scroll_gesture_->ForwardInputEvents(now, rwh_)) {
-    timer_.Stop();
-    pending_smooth_scroll_gesture_ = NULL;
-    rwh_->Send(new ViewMsg_SmoothScrollCompleted(rwh_->GetRoutingID()));
-  }
-}
-
-}  // namespace content
diff --git a/content/browser/renderer_host/smooth_scroll_gesture_controller.h b/content/browser/renderer_host/smooth_scroll_gesture_controller.h
deleted file mode 100644
index 1477a17..0000000
--- a/content/browser/renderer_host/smooth_scroll_gesture_controller.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 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 CONTENT_BROWSER_RENDERER_HOST_SMOOTH_SCROLL_GESTURE_CONTROLLER_H_
-#define CONTENT_BROWSER_RENDERER_HOST_SMOOTH_SCROLL_GESTURE_CONTROLLER_H_
-
-#include <map>
-
-#include "base/memory/weak_ptr.h"
-#include "base/time/time.h"
-#include "base/timer/timer.h"
-#include "content/common/content_export.h"
-
-struct ViewHostMsg_BeginSmoothScroll_Params;
-
-namespace content {
-
-class RenderWidgetHost;
-class RenderWidgetHostViewPort;
-class SmoothScrollGesture;
-
-// Controls SmoothScrollGestures, used to inject synthetic events
-// for performance test harness.
-class CONTENT_EXPORT SmoothScrollGestureController {
- public:
-  SmoothScrollGestureController();
-  ~SmoothScrollGestureController();
-
-  // Initiates a synthetic event stream.
-  void BeginSmoothScroll(RenderWidgetHostViewPort* view,
-                         const ViewHostMsg_BeginSmoothScroll_Params& params);
-
-  base::TimeDelta GetSyntheticScrollMessageInterval() const;
-
- private:
-  // Called periodically to advance the active scroll gesture after being
-  // initiated by OnBeginSmoothScroll.
-  void OnTimer();
-
-  base::RepeatingTimer<SmoothScrollGestureController> timer_;
-
-  RenderWidgetHost* rwh_;
-
-  scoped_refptr<SmoothScrollGesture> pending_smooth_scroll_gesture_;
-
-  DISALLOW_COPY_AND_ASSIGN(SmoothScrollGestureController);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_RENDERER_HOST_SMOOTH_SCROLL_GESTURE_CONTROLLER_H_
diff --git a/content/browser/renderer_host/smooth_scroll_gesture_controller_unittest.cc b/content/browser/renderer_host/smooth_scroll_gesture_controller_unittest.cc
deleted file mode 100644
index 9e70aca..0000000
--- a/content/browser/renderer_host/smooth_scroll_gesture_controller_unittest.cc
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright (c) 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 "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/time/time.h"
-#include "content/browser/renderer_host/render_widget_host_delegate.h"
-#include "content/browser/renderer_host/smooth_scroll_gesture_controller.h"
-#include "content/browser/renderer_host/test_render_view_host.h"
-#include "content/common/view_messages.h"
-#include "content/port/browser/render_widget_host_view_port.h"
-#include "content/port/browser/smooth_scroll_gesture.h"
-#include "content/public/test/mock_render_process_host.h"
-#include "content/public/test/test_browser_context.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#if defined(USE_AURA)
-#include "ui/aura/env.h"
-#include "ui/aura/test/test_screen.h"
-#endif
-
-using base::TimeDelta;
-
-namespace content {
-
-namespace {
-
-class MockSmoothScrollGesture : public SmoothScrollGesture {
- public:
-  MockSmoothScrollGesture() :
-      called_(0) {
-  }
-
-  // SmoothScrollGesture implementation:
-  virtual bool ForwardInputEvents(base::TimeTicks now,
-                                  RenderWidgetHost* host) OVERRIDE {
-    ++called_;
-    return true;
-  }
-
-  int called_;
-
- protected:
-  virtual ~MockSmoothScrollGesture() {
-  }
-};
-
-class MockRenderWidgetHostDelegate : public RenderWidgetHostDelegate {
- public:
-  MockRenderWidgetHostDelegate() {
-  }
-  virtual ~MockRenderWidgetHostDelegate() {}
-};
-
-class MockRenderWidgetHost : public RenderWidgetHostImpl {
- public:
-  MockRenderWidgetHost(
-      RenderWidgetHostDelegate* delegate,
-      RenderProcessHost* process,
-      int routing_id)
-      : RenderWidgetHostImpl(delegate, process, routing_id, false) {
-  }
-  virtual ~MockRenderWidgetHost() {}
-};
-
-class TestView : public TestRenderWidgetHostView {
- public:
-  explicit TestView(RenderWidgetHostImpl* rwh)
-      : TestRenderWidgetHostView(rwh),
-        mock_gesture_(NULL) {
-  }
-  virtual ~TestView() {}
-
-  // TestRenderWidgetHostView implementation:
-  virtual SmoothScrollGesture* CreateSmoothScrollGesture(
-      bool scroll_down, int pixels_to_scroll, int mouse_event_x,
-      int mouse_event_y) OVERRIDE {
-    mock_gesture_ = new MockSmoothScrollGesture();
-    return mock_gesture_;
-  }
-
-  virtual RenderWidgetHost* GetRenderWidgetHost() const OVERRIDE {
-    return rwh_;
-  }
-
-  MockSmoothScrollGesture* mock_gesture_;
-};
-
-class SmoothScrollGestureControllerTest : public testing::Test {
- public:
-  SmoothScrollGestureControllerTest() : process_(NULL) {
-  }
-  virtual ~SmoothScrollGestureControllerTest() {}
-
- protected:
-  // testing::Test implementation:
-  virtual void SetUp() OVERRIDE {
-    browser_context_.reset(new TestBrowserContext());
-    delegate_.reset(new MockRenderWidgetHostDelegate());
-    process_ = new MockRenderProcessHost(browser_context_.get());
-#if defined(USE_AURA)
-    screen_.reset(aura::TestScreen::Create());
-    gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, screen_.get());
-#endif
-    host_.reset(
-        new MockRenderWidgetHost(delegate_.get(), process_, MSG_ROUTING_NONE));
-    view_.reset(new TestView(host_.get()));
-    host_->SetView(view_.get());
-    host_->Init();
-  }
-
-  virtual void TearDown() OVERRIDE {
-    view_.reset();
-    host_.reset();
-    delegate_.reset();
-    process_ = NULL;
-    browser_context_.reset();
-
-#if defined(USE_AURA)
-    aura::Env::DeleteInstance();
-    screen_.reset();
-#endif
-
-    // Process all pending tasks to avoid leaks.
-    base::MessageLoop::current()->RunUntilIdle();
-  }
-
-  void PostQuitMessageAndRun() {
-    // Allow the message loop to process pending synthetic scrolls, then quit.
-    base::MessageLoop::current()->PostDelayedTask(
-        FROM_HERE, base::MessageLoop::QuitClosure(),
-        TimeDelta::FromMilliseconds(
-            controller_.GetSyntheticScrollMessageInterval().InMilliseconds() *
-            3));
-    base::MessageLoop::current()->Run();
-  }
-
-  base::MessageLoopForUI message_loop_;
-
-  scoped_ptr<TestBrowserContext> browser_context_;
-  MockRenderProcessHost* process_;  // Deleted automatically by the widget.
-  scoped_ptr<MockRenderWidgetHostDelegate> delegate_;
-  scoped_ptr<MockRenderWidgetHost> host_;
-  scoped_ptr<TestView> view_;
-#if defined(USE_AURA)
-  scoped_ptr<gfx::Screen> screen_;
-#endif
-
-  SmoothScrollGestureController controller_;
-};
-
-TEST_F(SmoothScrollGestureControllerTest, Tick) {
-  ViewHostMsg_BeginSmoothScroll_Params params;
-  params.scroll_down = true;
-  params.pixels_to_scroll = 10;
-  params.mouse_event_x = 20;
-  params.mouse_event_y = 30;
-
-  // Begin a smooth scroll, |mock_gesture_| won't be |called| until we post
-  // message.
-  controller_.BeginSmoothScroll(view_.get(), params);
-  EXPECT_TRUE(view_->mock_gesture_ != NULL);
-  EXPECT_EQ(0, view_->mock_gesture_->called_);
-
-  PostQuitMessageAndRun();
-  const int current_ticks = view_->mock_gesture_->called_;
-  EXPECT_LT(0, current_ticks);
-
-  // Ensure it won't start another smooth scroll.
-  MockSmoothScrollGesture* original_gesture = view_->mock_gesture_;
-  controller_.BeginSmoothScroll(view_.get(), params);
-  PostQuitMessageAndRun();
-  EXPECT_EQ(original_gesture, view_->mock_gesture_);
-
-  // Ensure the smooth scroll is ticked.
-  PostQuitMessageAndRun();
-  EXPECT_LT(current_ticks, view_->mock_gesture_->called_);
-}
-
-}  // namespace
-
-}  // namespace content
diff --git a/content/browser/renderer_host/surface_texture_transport_client_android.cc b/content/browser/renderer_host/surface_texture_transport_client_android.cc
index b563480..d382cd5 100644
--- a/content/browser/renderer_host/surface_texture_transport_client_android.cc
+++ b/content/browser/renderer_host/surface_texture_transport_client_android.cc
@@ -15,7 +15,7 @@
 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
 #include "third_party/khronos/GLES2/gl2.h"
 #include "third_party/khronos/GLES2/gl2ext.h"
-#include "ui/gl/android/surface_texture_bridge.h"
+#include "ui/gl/android/surface_texture.h"
 
 namespace content {
 
@@ -26,7 +26,7 @@
 class SurfaceRefAndroid : public GpuSurfaceTracker::SurfaceRef {
  public:
   SurfaceRefAndroid(
-      const scoped_refptr<gfx::SurfaceTextureBridge>& surface,
+      const scoped_refptr<gfx::SurfaceTexture>& surface,
       ANativeWindow* window)
       : surface_(surface),
         window_(window) {
@@ -39,7 +39,7 @@
     ANativeWindow_release(window_);
   }
 
-  scoped_refptr<gfx::SurfaceTextureBridge> surface_;
+  scoped_refptr<gfx::SurfaceTexture> surface_;
   ANativeWindow* window_;
 };
 
@@ -61,7 +61,7 @@
   // Use a SurfaceTexture to stream frames to the UI thread.
   video_layer_ = cc::VideoLayer::Create(this);
 
-  surface_texture_ = new gfx::SurfaceTextureBridge(0);
+  surface_texture_ = new gfx::SurfaceTexture(0);
   surface_texture_->SetFrameAvailableCallback(
     base::Bind(
         &SurfaceTextureTransportClient::OnSurfaceTextureFrameAvailable,
diff --git a/content/browser/renderer_host/surface_texture_transport_client_android.h b/content/browser/renderer_host/surface_texture_transport_client_android.h
index 83d9175..5a000bb 100644
--- a/content/browser/renderer_host/surface_texture_transport_client_android.h
+++ b/content/browser/renderer_host/surface_texture_transport_client_android.h
@@ -21,7 +21,7 @@
 }
 
 namespace gfx {
-class SurfaceTextureBridge;
+class SurfaceTexture;
 }
 
 namespace content {
@@ -45,7 +45,7 @@
   void OnSurfaceTextureFrameAvailable();
 
   scoped_refptr<cc::VideoLayer> video_layer_;
-  scoped_refptr<gfx::SurfaceTextureBridge> surface_texture_;
+  scoped_refptr<gfx::SurfaceTexture> surface_texture_;
   ANativeWindow* window_;
   scoped_refptr<media::VideoFrame> video_frame_;
   uint32 texture_id_;
diff --git a/content/browser/renderer_host/synthetic_gesture_calculator.cc b/content/browser/renderer_host/synthetic_gesture_calculator.cc
new file mode 100644
index 0000000..9816df9
--- /dev/null
+++ b/content/browser/renderer_host/synthetic_gesture_calculator.cc
@@ -0,0 +1,37 @@
+// 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 "content/browser/renderer_host/synthetic_gesture_calculator.h"
+
+
+namespace {
+
+const float kDefaultPositionDelta = 10.0f;
+
+}
+
+
+namespace content {
+
+SyntheticGestureCalculator::SyntheticGestureCalculator() {
+}
+
+SyntheticGestureCalculator::~SyntheticGestureCalculator() {
+}
+
+float SyntheticGestureCalculator::GetDelta(
+    base::TimeTicks now, base::TimeDelta desired_interval) {
+  float position_delta = kDefaultPositionDelta;
+  if (!last_tick_time_.is_null()) {
+    float velocity = kDefaultPositionDelta /
+        (float)desired_interval.InMillisecondsF();
+    float time_delta = (now - last_tick_time_).InMillisecondsF();
+    position_delta = velocity * time_delta;
+  }
+
+  last_tick_time_ = now;
+  return position_delta;
+}
+
+}  // namespace content
diff --git a/content/browser/renderer_host/synthetic_gesture_calculator.h b/content/browser/renderer_host/synthetic_gesture_calculator.h
new file mode 100644
index 0000000..c2af497
--- /dev/null
+++ b/content/browser/renderer_host/synthetic_gesture_calculator.h
@@ -0,0 +1,28 @@
+// 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 CONTENT_BROWSER_RENDERER_HOST_SYNTHETIC_GESTURE_CALCULATOR_H_
+#define CONTENT_BROWSER_RENDERER_HOST_SYNTHETIC_GESTURE_CALCULATOR_H_
+
+#include "base/time/time.h"
+
+namespace content {
+
+// A utility class to calculate the delta for synthetic gesture events.
+class SyntheticGestureCalculator {
+ public:
+  SyntheticGestureCalculator();
+  ~SyntheticGestureCalculator();
+
+  float GetDelta(base::TimeTicks now, base::TimeDelta desired_interval);
+
+ private:
+  base::TimeTicks last_tick_time_;
+
+  DISALLOW_COPY_AND_ASSIGN(SyntheticGestureCalculator);
+};
+
+} // namespace content
+
+#endif  // CONTENT_BROWSER_RENDERER_HOST_SYNTHETIC_GESTURE_CALCULATOR_H_
diff --git a/content/browser/renderer_host/synthetic_gesture_controller.cc b/content/browser/renderer_host/synthetic_gesture_controller.cc
new file mode 100644
index 0000000..bc35a8f
--- /dev/null
+++ b/content/browser/renderer_host/synthetic_gesture_controller.cc
@@ -0,0 +1,61 @@
+// 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 "content/browser/renderer_host/synthetic_gesture_controller.h"
+
+#include "base/debug/trace_event.h"
+#include "base/message_loop/message_loop.h"
+#include "content/common/view_messages.h"
+#include "content/port/browser/render_widget_host_view_port.h"
+#include "content/port/browser/synthetic_gesture.h"
+#include "content/public/browser/render_widget_host.h"
+
+namespace content {
+
+namespace {
+
+// How many milliseconds apart synthetic scroll messages should be sent.
+const int kSyntheticGestureMessageIntervalMs = 7;
+
+}  // namespace
+
+SyntheticGestureController::SyntheticGestureController()
+    : rwh_(NULL) {
+}
+
+SyntheticGestureController::~SyntheticGestureController() {
+}
+
+void SyntheticGestureController::BeginSmoothScroll(
+    RenderWidgetHostViewPort* view,
+    const ViewHostMsg_BeginSmoothScroll_Params& params) {
+  if (pending_synthetic_gesture_.get())
+    return;
+
+  rwh_ = view->GetRenderWidgetHost();
+  pending_synthetic_gesture_ = view->CreateSmoothScrollGesture(
+      params.scroll_down,
+      params.pixels_to_scroll,
+      params.mouse_event_x,
+      params.mouse_event_y);
+
+  timer_.Start(FROM_HERE, GetSyntheticGestureMessageInterval(), this,
+               &SyntheticGestureController::OnTimer);
+}
+
+base::TimeDelta
+    SyntheticGestureController::GetSyntheticGestureMessageInterval() const {
+  return base::TimeDelta::FromMilliseconds(kSyntheticGestureMessageIntervalMs);
+}
+
+void SyntheticGestureController::OnTimer() {
+  base::TimeTicks now = base::TimeTicks::Now();
+  if (!pending_synthetic_gesture_->ForwardInputEvents(now, rwh_)) {
+    timer_.Stop();
+    pending_synthetic_gesture_ = NULL;
+    rwh_->Send(new ViewMsg_SyntheticGestureCompleted(rwh_->GetRoutingID()));
+  }
+}
+
+}  // namespace content
diff --git a/content/browser/renderer_host/synthetic_gesture_controller.h b/content/browser/renderer_host/synthetic_gesture_controller.h
new file mode 100644
index 0000000..90a271e
--- /dev/null
+++ b/content/browser/renderer_host/synthetic_gesture_controller.h
@@ -0,0 +1,52 @@
+// 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 CONTENT_BROWSER_RENDERER_HOST_SYNTHETIC_GESTURE_CONTROLLER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_SYNTHETIC_GESTURE_CONTROLLER_H_
+
+#include <map>
+
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "content/common/content_export.h"
+
+struct ViewHostMsg_BeginSmoothScroll_Params;
+
+namespace content {
+
+class RenderWidgetHost;
+class RenderWidgetHostViewPort;
+class SyntheticGesture;
+
+// Controls SyntheticGestures, used to inject synthetic events
+// for performance test harness.
+class CONTENT_EXPORT SyntheticGestureController {
+ public:
+  SyntheticGestureController();
+  ~SyntheticGestureController();
+
+  // Initiates a synthetic event stream.
+  void BeginSmoothScroll(RenderWidgetHostViewPort* view,
+                         const ViewHostMsg_BeginSmoothScroll_Params& params);
+
+  base::TimeDelta GetSyntheticGestureMessageInterval() const;
+
+ private:
+  // Called periodically to advance the active scroll gesture after being
+  // initiated by OnBeginSmoothScroll.
+  void OnTimer();
+
+  base::RepeatingTimer<SyntheticGestureController> timer_;
+
+  RenderWidgetHost* rwh_;
+
+  scoped_refptr<SyntheticGesture> pending_synthetic_gesture_;
+
+  DISALLOW_COPY_AND_ASSIGN(SyntheticGestureController);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_RENDERER_HOST_SYNTHETIC_GESTURE_CONTROLLER_H_
diff --git a/content/browser/renderer_host/synthetic_gesture_controller_unittest.cc b/content/browser/renderer_host/synthetic_gesture_controller_unittest.cc
new file mode 100644
index 0000000..7fb9ba9
--- /dev/null
+++ b/content/browser/renderer_host/synthetic_gesture_controller_unittest.cc
@@ -0,0 +1,183 @@
+// 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 "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+#include "content/browser/renderer_host/render_widget_host_delegate.h"
+#include "content/browser/renderer_host/synthetic_gesture_controller.h"
+#include "content/browser/renderer_host/test_render_view_host.h"
+#include "content/common/view_messages.h"
+#include "content/port/browser/render_widget_host_view_port.h"
+#include "content/port/browser/synthetic_gesture.h"
+#include "content/public/test/mock_render_process_host.h"
+#include "content/public/test/test_browser_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(USE_AURA)
+#include "ui/aura/env.h"
+#include "ui/aura/test/test_screen.h"
+#endif
+
+using base::TimeDelta;
+
+namespace content {
+
+namespace {
+
+class MockSyntheticGesture : public SyntheticGesture {
+ public:
+  MockSyntheticGesture() :
+      called_(0) {
+  }
+
+  // SmoothScrollGesture implementation:
+  virtual bool ForwardInputEvents(base::TimeTicks now,
+                                  RenderWidgetHost* host) OVERRIDE {
+    ++called_;
+    return true;
+  }
+
+  int called_;
+
+ protected:
+  virtual ~MockSyntheticGesture() {
+  }
+};
+
+class MockRenderWidgetHostDelegate : public RenderWidgetHostDelegate {
+ public:
+  MockRenderWidgetHostDelegate() {
+  }
+  virtual ~MockRenderWidgetHostDelegate() {}
+};
+
+class MockRenderWidgetHost : public RenderWidgetHostImpl {
+ public:
+  MockRenderWidgetHost(
+      RenderWidgetHostDelegate* delegate,
+      RenderProcessHost* process,
+      int routing_id)
+      : RenderWidgetHostImpl(delegate, process, routing_id, false) {
+  }
+  virtual ~MockRenderWidgetHost() {}
+};
+
+class TestView : public TestRenderWidgetHostView {
+ public:
+  explicit TestView(RenderWidgetHostImpl* rwh)
+      : TestRenderWidgetHostView(rwh),
+        mock_gesture_(NULL) {
+  }
+  virtual ~TestView() {}
+
+  // TestRenderWidgetHostView implementation:
+  virtual SyntheticGesture* CreateSmoothScrollGesture(
+      bool scroll_down, int pixels_to_scroll, int mouse_event_x,
+      int mouse_event_y) OVERRIDE {
+    mock_gesture_ = new MockSyntheticGesture();
+    return mock_gesture_;
+  }
+
+  virtual RenderWidgetHost* GetRenderWidgetHost() const OVERRIDE {
+    return rwh_;
+  }
+
+  MockSyntheticGesture* mock_gesture_;
+};
+
+class SyntheticGestureControllerTest : public testing::Test {
+ public:
+  SyntheticGestureControllerTest() : process_(NULL) {
+  }
+  virtual ~SyntheticGestureControllerTest() {}
+
+ protected:
+  // testing::Test implementation:
+  virtual void SetUp() OVERRIDE {
+    browser_context_.reset(new TestBrowserContext());
+    delegate_.reset(new MockRenderWidgetHostDelegate());
+    process_ = new MockRenderProcessHost(browser_context_.get());
+#if defined(USE_AURA)
+    screen_.reset(aura::TestScreen::Create());
+    gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, screen_.get());
+#endif
+    host_.reset(
+        new MockRenderWidgetHost(delegate_.get(), process_, MSG_ROUTING_NONE));
+    view_.reset(new TestView(host_.get()));
+    host_->SetView(view_.get());
+    host_->Init();
+  }
+
+  virtual void TearDown() OVERRIDE {
+    view_.reset();
+    host_.reset();
+    delegate_.reset();
+    process_ = NULL;
+    browser_context_.reset();
+
+#if defined(USE_AURA)
+    aura::Env::DeleteInstance();
+    screen_.reset();
+#endif
+
+    // Process all pending tasks to avoid leaks.
+    base::MessageLoop::current()->RunUntilIdle();
+  }
+
+  void PostQuitMessageAndRun() {
+    // Allow the message loop to process pending synthetic scrolls, then quit.
+    base::MessageLoop::current()->PostDelayedTask(
+        FROM_HERE, base::MessageLoop::QuitClosure(),
+        TimeDelta::FromMilliseconds(
+            controller_.GetSyntheticGestureMessageInterval().InMilliseconds() *
+            3));
+    base::MessageLoop::current()->Run();
+  }
+
+  base::MessageLoopForUI message_loop_;
+
+  scoped_ptr<TestBrowserContext> browser_context_;
+  MockRenderProcessHost* process_;  // Deleted automatically by the widget.
+  scoped_ptr<MockRenderWidgetHostDelegate> delegate_;
+  scoped_ptr<MockRenderWidgetHost> host_;
+  scoped_ptr<TestView> view_;
+#if defined(USE_AURA)
+  scoped_ptr<gfx::Screen> screen_;
+#endif
+
+  SyntheticGestureController controller_;
+};
+
+TEST_F(SyntheticGestureControllerTest, Tick) {
+  ViewHostMsg_BeginSmoothScroll_Params params;
+  params.scroll_down = true;
+  params.pixels_to_scroll = 10;
+  params.mouse_event_x = 20;
+  params.mouse_event_y = 30;
+
+  // Begin a smooth scroll, |mock_gesture_| won't be |called| until we post
+  // message.
+  controller_.BeginSmoothScroll(view_.get(), params);
+  EXPECT_TRUE(view_->mock_gesture_ != NULL);
+  EXPECT_EQ(0, view_->mock_gesture_->called_);
+
+  PostQuitMessageAndRun();
+  const int current_ticks = view_->mock_gesture_->called_;
+  EXPECT_LT(0, current_ticks);
+
+  // Ensure it won't start another smooth scroll.
+  MockSyntheticGesture* original_gesture = view_->mock_gesture_;
+  controller_.BeginSmoothScroll(view_.get(), params);
+  PostQuitMessageAndRun();
+  EXPECT_EQ(original_gesture, view_->mock_gesture_);
+
+  // Ensure the smooth scroll is ticked.
+  PostQuitMessageAndRun();
+  EXPECT_LT(current_ticks, view_->mock_gesture_->called_);
+}
+
+}  // namespace
+
+}  // namespace content
diff --git a/content/browser/renderer_host/touch_smooth_scroll_gesture_android.cc b/content/browser/renderer_host/touch_smooth_scroll_gesture_android.cc
deleted file mode 100644
index c504f6c..0000000
--- a/content/browser/renderer_host/touch_smooth_scroll_gesture_android.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (c) 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 "content/browser/renderer_host/touch_smooth_scroll_gesture_android.h"
-
-#include "base/debug/trace_event.h"
-#include "content/browser/renderer_host/render_widget_host_impl.h"
-#include "jni/SmoothScroller_jni.h"
-
-namespace {
-bool g_jni_initialized = false;
-
-void RegisterNativesIfNeeded(JNIEnv* env) {
-  if (!g_jni_initialized) {
-    content::RegisterNativesImpl(env);
-    g_jni_initialized = true;
-  }
-}
-}  // namespace
-
-namespace content {
-
-TouchSmoothScrollGestureAndroid::TouchSmoothScrollGestureAndroid(
-    int pixels_to_scroll,
-    RenderWidgetHost* rwh,
-    base::android::ScopedJavaLocalRef<jobject> java_scroller)
-    : pixels_scrolled_(0),
-      has_started_(false),
-      has_sent_motion_up_(false),
-      pixels_to_scroll_(pixels_to_scroll),
-      rwh_(rwh),
-      java_scroller_(java_scroller) {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  RegisterNativesIfNeeded(env);
-}
-
-TouchSmoothScrollGestureAndroid::~TouchSmoothScrollGestureAndroid() {
-}
-
-bool TouchSmoothScrollGestureAndroid::ForwardInputEvents(
-    base::TimeTicks now, RenderWidgetHost* host) {
-  if (!has_started_) {
-    has_started_ = true;
-    JNIEnv* env = base::android::AttachCurrentThread();
-    Java_SmoothScroller_start(
-        env, java_scroller_.obj(), reinterpret_cast<int>(this));
-  }
-
-  TRACE_COUNTER_ID1(
-      "gpu", "smooth_scroll_by_pixels_scrolled", this, pixels_scrolled_);
-
-  return !has_sent_motion_up_;
-}
-
-double TouchSmoothScrollGestureAndroid::GetScrollDelta(
-    JNIEnv* env, jobject obj, double scale) {
-  double delta = smooth_scroll_calculator_.GetScrollDelta(
-      base::TimeTicks::Now(),
-      RenderWidgetHostImpl::From(rwh_)->GetSyntheticScrollMessageInterval())
-      * scale;
-  pixels_scrolled_ += delta;
-  return delta;
-}
-
-bool TouchSmoothScrollGestureAndroid::HasFinished(JNIEnv* env, jobject obj) {
-  return pixels_scrolled_ >= pixels_to_scroll_;
-}
-
-void TouchSmoothScrollGestureAndroid::SetHasSentMotionUp(
-    JNIEnv* env, jobject obj) {
-  has_sent_motion_up_ = true;
-}
-
-}  // namespace content
diff --git a/content/browser/renderer_host/touch_smooth_scroll_gesture_android.h b/content/browser/renderer_host/touch_smooth_scroll_gesture_android.h
deleted file mode 100644
index 0b89267..0000000
--- a/content/browser/renderer_host/touch_smooth_scroll_gesture_android.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 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 CONTENT_BROWSER_RENDERER_HOST_TOUCH_SMOOTH_GESTURE_ANDROID_H_
-#define CONTENT_BROWSER_RENDERER_HOST_TOUCH_SMOOTH_GESTURE_ANDROID_H_
-
-#include "base/android/jni_android.h"
-#include "base/time/time.h"
-#include "content/browser/renderer_host/smooth_scroll_calculator.h"
-#include "content/port/browser/smooth_scroll_gesture.h"
-
-namespace content {
-
-class ContentViewCore;
-class RenderWidgetHost;
-
-class TouchSmoothScrollGestureAndroid : public SmoothScrollGesture {
- public:
-  TouchSmoothScrollGestureAndroid(
-      int pixels_to_scroll,
-      RenderWidgetHost* rwh,
-      base::android::ScopedJavaLocalRef<jobject> java_scroller);
-
-  // Called by the java side once the TimeAnimator ticks.
-  double GetScrollDelta(JNIEnv* env, jobject obj, double scale);
-  bool HasFinished(JNIEnv* env, jobject obj);
-  void SetHasSentMotionUp(JNIEnv* env, jobject obj);
-
-  // SmoothScrollGesture
-  virtual bool ForwardInputEvents(base::TimeTicks now,
-                                  RenderWidgetHost* host) OVERRIDE;
-
- private:
-  virtual ~TouchSmoothScrollGestureAndroid();
-
-  SmoothScrollCalculator smooth_scroll_calculator_;
-
-  int pixels_scrolled_;
-  bool has_started_;
-  bool has_sent_motion_up_;
-
-  int pixels_to_scroll_;
-  RenderWidgetHost* rwh_;
-  base::android::ScopedJavaGlobalRef<jobject> java_scroller_;
-
-  DISALLOW_COPY_AND_ASSIGN(TouchSmoothScrollGestureAndroid);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_RENDERER_HOST_TOUCH_SMOOTH_GESTURE_ANDROID_H_
diff --git a/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.cc b/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.cc
index ffb1564..dd5b401 100644
--- a/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.cc
+++ b/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.cc
@@ -53,8 +53,8 @@
     return false;
 
   RenderWidgetHostImpl* host_impl = RenderWidgetHostImpl::From(host);
-  double position_delta = smooth_scroll_calculator_.GetScrollDelta(now,
-      host_impl->GetSyntheticScrollMessageInterval());
+  float position_delta = synthetic_gesture_calculator_.GetDelta(now,
+      host_impl->GetSyntheticGestureMessageInterval());
 
   if (pixels_scrolled_ == 0) {
     InjectTouchEvent(location_, ui::ET_TOUCH_PRESSED, window_);
diff --git a/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.h b/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.h
index 779e02d..890b146 100644
--- a/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.h
+++ b/content/browser/renderer_host/touch_smooth_scroll_gesture_aura.h
@@ -6,8 +6,8 @@
 #define CONTENT_BROWSER_RENDERER_HOST_TOUCH_SMOOTH_SCROLL_GESTURE_
 
 #include "base/time/time.h"
-#include "content/browser/renderer_host/smooth_scroll_calculator.h"
-#include "content/port/browser/smooth_scroll_gesture.h"
+#include "content/browser/renderer_host/synthetic_gesture_calculator.h"
+#include "content/port/browser/synthetic_gesture.h"
 #include "ui/gfx/point.h"
 
 namespace aura {
@@ -16,7 +16,7 @@
 
 namespace content {
 
-class TouchSmoothScrollGestureAura : public SmoothScrollGesture {
+class TouchSmoothScrollGestureAura : public SyntheticGesture {
  public:
   TouchSmoothScrollGestureAura(bool scroll_down,
                                int pixels_to_scroll,
@@ -35,7 +35,7 @@
   int pixels_scrolled_;
   gfx::Point location_;
   aura::Window* window_;
-  SmoothScrollCalculator smooth_scroll_calculator_;
+  SyntheticGestureCalculator synthetic_gesture_calculator_;
 
   DISALLOW_COPY_AND_ASSIGN(TouchSmoothScrollGestureAura);
 };
diff --git a/content/browser/resources/media/new/client_renderer.js b/content/browser/resources/media/new/client_renderer.js
index 6acb302..02f47e1 100644
--- a/content/browser/resources/media/new/client_renderer.js
+++ b/content/browser/resources/media/new/client_renderer.js
@@ -5,8 +5,14 @@
 var ClientRenderer = (function() {
   var ClientRenderer = function() {
     this.playerListElement = document.getElementById('player-list');
+    this.audioStreamListElement = document.getElementById('audio-stream-list');
     this.propertiesTable = document.getElementById('property-table');
+    this.logTable = document.getElementById('log');
+
     this.selectedPlayer = null;
+    this.selectedStream = null;
+
+    this.selectedPlayerLogIndex = 0;
   };
 
   function removeChildren(element) {
@@ -15,14 +21,39 @@
     }
   };
 
+  function createButton(text, select_cb) {
+    var button = document.createElement('button');
+
+    button.appendChild(document.createTextNode(text));
+    button.onclick = function() {
+      select_cb();
+    };
+
+    return button;
+  };
+
   ClientRenderer.prototype = {
+    audioStreamAdded: function(audioStreams, audioStreamAdded) {
+      this.redrawAudioStreamList_(audioStreams);
+    },
+
+    audioStreamUpdated: function(audioStreams, stream, key, value) {
+      if (stream === this.selectedStream) {
+        this.drawProperties_(stream);
+      }
+    },
+
+    audioStreamRemoved: function(audioStreams, audioStreamRemoved) {
+      this.redrawAudioStreamList_(audioStreams);
+    },
+
     /**
      * Called when a player is added to the collection.
      * @param players The entire map of id -> player.
      * @param player_added The player that is added.
      */
-    playerAdded: function(players, player_added) {
-      this.redrawList_(players);
+    playerAdded: function(players, playerAdded) {
+      this.redrawPlayerList_(players);
     },
 
     /**
@@ -30,8 +61,8 @@
      * @param players The entire map of id -> player.
      * @param player_added The player that was removed.
      */
-    playerRemoved: function(players, player_removed) {
-      this.redrawList_(players);
+    playerRemoved: function(players, playerRemoved) {
+      this.redrawPlayerList_(players);
     },
 
     /**
@@ -43,47 +74,63 @@
      */
     playerUpdated: function(players, player, key, value) {
       if (player === this.selectedPlayer) {
-        this.drawSelectedPlayer_();
+        this.drawProperties_(player.properties);
+        this.drawLog_();
       }
       if (key === 'name' || key === 'url') {
-        this.redrawList_(players);
+        this.redrawPlayerList_(players);
       }
     },
 
-    redrawList_: function(players) {
+    redrawAudioStreamList_: function(streams) {
+      removeChildren(this.audioStreamListElement);
+
+      for (id in streams) {
+        var li = document.createElement('li');
+        li.appendChild(createButton(
+            id, this.selectAudioStream_.bind(this, streams[id])));
+        this.audioStreamListElement.appendChild(li);
+      }
+    },
+
+    selectAudioStream_: function(audioStream) {
+      this.selectedStream = audioStream;
+      this.selectedPlayer = null;
+      this.drawProperties_(audioStream);
+      removeChildren(this.logTable.querySelector('tbody'));
+    },
+
+    redrawPlayerList_: function(players) {
       removeChildren(this.playerListElement);
 
-      function createButton(player, select_cb) {
-        var button = document.createElement('button');
+      for (id in players) {
+        var li = document.createElement('li');
+        var player = players[id];
         var usableName = player.properties.name ||
             player.properties.url ||
             'player ' + player.id;
 
-        button.appendChild(document.createTextNode(usableName));
-        button.onclick = function() {
-          select_cb(player);
-        };
-
-        return button;
-      };
-
-      for (id in players) {
-        var li = document.createElement('li');
-        li.appendChild(createButton(players[id], this.select_.bind(this)));
+        li.appendChild(createButton(
+            usableName, this.selectPlayer_.bind(this, player)));
         this.playerListElement.appendChild(li);
       }
     },
 
-    select_: function(player) {
+    selectPlayer_: function(player) {
       this.selectedPlayer = player;
-      this.drawSelectedPlayer_();
+      this.selectedPlayerLogIndex = 0;
+      this.selectedStream = null;
+      this.drawProperties_(player.properties);
+
+      removeChildren(this.logTable.querySelector('tbody'));
+      this.drawLog_();
     },
 
-    drawSelectedPlayer_: function() {
+    drawProperties_: function(propertyMap) {
       removeChildren(this.propertiesTable);
 
-      for (key in this.selectedPlayer.properties) {
-        var value = this.selectedPlayer.properties[key];
+      for (key in propertyMap) {
+        var value = propertyMap[key];
         var row = this.propertiesTable.insertRow(-1);
         var keyCell = row.insertCell(-1);
         var valueCell = row.insertCell(-1);
@@ -91,6 +138,22 @@
         keyCell.appendChild(document.createTextNode(key));
         valueCell.appendChild(document.createTextNode(value));
       }
+    },
+
+    appendEventToLog_: function(event) {
+      var row = this.logTable.querySelector('tbody').insertRow(-1);
+
+      row.insertCell(-1).appendChild(document.createTextNode(
+          util.millisecondsToString(event.time)));
+      row.insertCell(-1).appendChild(document.createTextNode(event.key));
+      row.insertCell(-1).appendChild(document.createTextNode(event.value));
+    },
+
+    drawLog_: function() {
+      var toDraw = this.selectedPlayer.allEvents.slice(
+          this.selectedPlayerLogIndex);
+      toDraw.forEach(this.appendEventToLog_.bind(this));
+      this.selectedPlayerLogIndex = this.selectedPlayer.allEvents.length;
     }
   };
 
diff --git a/content/browser/resources/media/new/main.js b/content/browser/resources/media/new/main.js
index 61f6407..85cdd3a 100644
--- a/content/browser/resources/media/new/main.js
+++ b/content/browser/resources/media/new/main.js
@@ -8,68 +8,29 @@
 var media = (function() {
   'use strict';
 
-  var manager = null;
+  var manager_ = null;
 
   /**
    * Users of |media| must call initialize prior to calling other methods.
    */
-  function initialize(playerManager) {
-    manager = playerManager;
-  }
-
-  /**
-   * Call to modify or add a system property.
-   */
-  function onSystemProperty(timestamp, key, value) {
-    console.log('System properties not yet implemented');
-  }
-
-  /**
-   * Call to modify or add a property on a player.
-   */
-  function onPlayerProperty(id, timestamp, key, value) {
-    manager.updatePlayerInfo(id, timestamp, key, value);
-  }
-
-  function onPlayerPropertyNoRecord(id, timestamp, key, value) {
-    manager.updatePlayerInfoNoRecord(id, timestamp, key, value);
-  }
-
-  /**
-   * Call to add a player.
-   */
-  function onPlayerOpen(id, timestamp) {
-    manager.addPlayer(id, timestamp);
-  }
-
-  /**
-   * Call to remove a player.
-   */
-  function onPlayerClose(id) {
-    manager.removePlayer(id);
-  }
-
-  var media = {
-    onSystemProperty: onSystemProperty,
-    onPlayerProperty: onPlayerProperty,
-    onPlayerPropertyNoRecord: onPlayerPropertyNoRecord,
-    onPlayerOpen: onPlayerOpen,
-    onPlayerClose: onPlayerClose,
-
-    initialize: initialize
+  media.initialize = function(manager) {
+    manager_ = manager;
   };
 
-  // Everything beyond this point is for backwards compatibility reasons.
-  // It will go away when the backend is updated.
+  media.onReceiveEverything = function(everything) {
+    for (var key in everything.audio_streams) {
+      media.updateAudioStream(everything.audio_streams[key]);
+    }
+  };
 
   media.onNetUpdate = function(update) {
     // TODO(tyoverby): Implement
   };
 
   media.onRendererTerminated = function(renderId) {
-    util.object.forEach(manager.players_, function(playerInfo, id) {
+    util.object.forEach(manager_.players_, function(playerInfo, id) {
       if (playerInfo.properties['render_id'] == renderId) {
-        media.onPlayerClose(id);
+        manager_.removePlayer(id);
       }
     });
   };
@@ -79,31 +40,39 @@
   media.addAudioStream = function(event) {
     switch (event.status) {
       case 'created':
-        media.onPlayerOpen(event.id);
-        // We have to simulate the timestamp since it isn't provided to us.
-        media.onPlayerProperty(
-            event.id, (new Date()).getTime(), 'playing', event.playing);
+        manager_.addAudioStream(event.id);
+        manager_.updateAudioStream(event.id, { 'playing': event.playing });
         break;
       case 'closed':
-        media.onPlayerClose(event.id);
+        manager_.removeAudioStream(event.id);
         break;
     }
   };
+
+  media.updateAudioStream = function(stream) {
+    manager_.addAudioStream(stream.id);
+    manager_.updateAudioStream(stream.id, stream);
+  };
+
   media.onItemDeleted = function() {
     // This only gets called when an audio stream is removed, which
     // for whatever reason is also handled by addAudioStream...
     // Because it is already handled, we can safely ignore it.
   };
 
+  media.onPlayerOpen = function(id, timestamp) {
+    manager_.addPlayer(id, timestamp);
+  };
+
   media.onMediaEvent = function(event) {
     var source = event.renderer + ':' + event.player;
 
     // Although this gets called on every event, there is nothing we can do
-    // about this because there is no onOpen event.
+    // because there is no onOpen event.
     media.onPlayerOpen(source);
-    media.onPlayerPropertyNoRecord(
+    manager_.updatePlayerInfoNoRecord(
         source, event.ticksMillis, 'render_id', event.renderer);
-    media.onPlayerPropertyNoRecord(
+    manager_.updatePlayerInfoNoRecord(
         source, event.ticksMillis, 'player_id', event.player);
 
     var propertyCount = 0;
@@ -116,16 +85,16 @@
           key === 'buffer_end' ||
           key === 'buffer_current' ||
           key === 'is_downloading_data') {
-        media.onPlayerPropertyNoRecord(
+        manager_.updatePlayerInfoNoRecord(
             source, event.ticksMillis, key, value);
       } else {
-        media.onPlayerProperty(source, event.ticksMillis, key, value);
+        manager_.updatePlayerInfo(source, event.ticksMillis, key, value);
       }
       propertyCount += 1;
     });
 
     if (propertyCount === 0) {
-      media.onPlayerProperty(
+      manager_.updatePlayerInfo(
           source, event.ticksMillis, 'EVENT', event.type);
     }
   };
diff --git a/content/browser/resources/media/new/media_internals.css b/content/browser/resources/media/new/media_internals.css
new file mode 100644
index 0000000..88a792b
--- /dev/null
+++ b/content/browser/resources/media/new/media_internals.css
@@ -0,0 +1,91 @@
+/* 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. */
+
+html,
+body,
+#container {
+  margin: 0;
+  padding: 0;
+  width: 100%;
+  height: 100%;
+
+  font-family: 'Lucida Grande', sans-serif;
+}
+
+table {
+  border-collapse: collapse;
+}
+td {
+  border: 1px solid black;
+}
+thead {
+  color: rgb(50,50,50);
+  font-size: 1.1em;
+}
+
+h1,
+h2,
+h3 {
+  color: rgb(50,50,50);
+}
+
+#container {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  justify-content: space-between;
+  align-items: flex-start;
+  align-content: stretch;
+}
+
+#container > * {
+  padding: 0;
+  padding-left: 25px;
+  margin: 0;
+  max-height: 100%;
+}
+
+#list-wrapper {
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+  align-items: flex-start;
+  align-content: stretch;
+  height: 100%;
+}
+
+#player-list-wrapper,
+#audio-stream-list-wrapper {
+  flex-grow: 1;
+  align-self: stretch;
+  min-width: 200px;
+  max-width: 200px;
+  overflow: auto;
+}
+
+#player-list-wrapper ul,
+#player-list-wrapper li,
+#audio-stream-list-wrapper ul,
+#audio-stream-list-wrapper li {
+  margin: 0;
+  padding: 0;
+  list-style-type: none;
+}
+#list-wrapper button {
+  margin: 0;
+  padding: 0;
+  width: 170px;
+}
+
+#property-wrapper,
+#log-wrapper {
+  display:block;
+  flex-grow: 0.5;
+  align-self: stretch;
+  overflow: auto;
+}
+
+#log-wrapper > thead {
+  position: fixed;
+}
diff --git a/content/browser/resources/media/new/media_internals.html b/content/browser/resources/media/new/media_internals.html
index bc2a4de..8a41e7f 100644
--- a/content/browser/resources/media/new/media_internals.html
+++ b/content/browser/resources/media/new/media_internals.html
@@ -8,11 +8,48 @@
 <head>
   <meta charset="utf-8">
   <title i18n-content="Media Internals"></title>
+  <link rel="stylesheet" href="media_internals.css">
 </head>
 
 <body>
-  <ul id="player-list"></ul>
-  <table id="property-table"></table>
+  <div id="container">
+    <div id="list-wrapper">
+      <div id="player-list-wrapper">
+        <h2>Players</h2>
+        <ul id="player-list"></ul>
+      </div>
+      <div id="audio-stream-list-wrapper">
+        <h2>Audio Streams</h2>
+        <ul id="audio-stream-list"></ul>
+      </div>
+    </div>
+    <div id="property-wrapper">
+      <h2>Properties</h2>
+      <table id="property-table">
+        <thead>
+          <tr>
+            <td>Property Name</td> <td>Value</td>
+          </tr>
+        </thead>
+      </table>
+    </div>
+    <div id="log-wrapper">
+      <h2>
+        Log
+      </h2>
+      <table id="log">
+        <thead>
+          <tr>
+            <td>Timestamp</td>
+            <td>Property</td>
+            <td>Value</td>
+          </tr>
+        </thead>
+        <tbody>
+        </tbody>
+      </table>
+    </div>
+  </div>
   <script src="chrome://media-internals/media_internals.js"></script>
 </body>
 </html>
diff --git a/content/browser/resources/media/new/media_internals.js b/content/browser/resources/media/new/media_internals.js
index a795273..67c2944 100644
--- a/content/browser/resources/media/new/media_internals.js
+++ b/content/browser/resources/media/new/media_internals.js
@@ -10,4 +10,4 @@
 <include src="client_renderer.js"/>
 <include src="main.js"/>
 
-media.initialize(new PlayerManager(new ClientRenderer()));
+media.initialize(new Manager(new ClientRenderer()));
diff --git a/content/browser/resources/media/new/player_manager.js b/content/browser/resources/media/new/player_manager.js
index 4ef1a93..1ba1301 100644
--- a/content/browser/resources/media/new/player_manager.js
+++ b/content/browser/resources/media/new/player_manager.js
@@ -3,21 +3,56 @@
 // found in the LICENSE file.
 
 /**
- * @fileoverview Keeps track of all the existing
- * PlayerInfo objects and is the entry-point for messages from the backend.
+ * @fileoverview Keeps track of all the existing PlayerInfo and
+ * audio stream objects and is the entry-point for messages from the backend.
  *
- * The events captured by PlayerManager (add, remove, update) are relayed
+ * The events captured by Manager (add, remove, update) are relayed
  * to the clientRenderer which it can choose to use to modify the UI.
  */
-var PlayerManager = (function() {
+var Manager = (function() {
   'use strict';
 
-  function PlayerManager(clientRenderer) {
+  function Manager(clientRenderer) {
     this.players_ = {};
+    this.audioStreams_ = {};
     this.clientRenderer_ = clientRenderer;
   }
 
-  PlayerManager.prototype = {
+  Manager.prototype = {
+    /**
+     * Adds an audio-stream to the dictionary of audio-streams to manage.
+     * @param id The unique-id of the audio-stream.
+     */
+    addAudioStream: function(id) {
+      this.audioStreams_[id] = this.audioStreams_[id] || {};
+      this.clientRenderer_.audioStreamAdded(this.audioStreams_,
+                                            this.audioStreams_[id]);
+    },
+
+    /**
+     * Sets properties of an audiostream.
+     * @param id The unique-id of the audio-stream.
+     * @param properties A dictionary of properties to be added to the
+     * audio-stream.
+     */
+    updateAudioStream: function(id, properties) {
+      for (var key in properties) {
+        this.audioStreams_[id][key] = properties[key];
+      }
+      this.clientRenderer_.audioStreamAdded(
+          this.audioStreams_, this.audioStreams_[id]);
+    },
+
+    /**
+     * Removes an audio-stream from the manager.
+     * @param id The unique-id of the audio-stream.
+     */
+    removeAudioStream: function(id) {
+      this.clientRenderer_.audioStreamRemoved(
+          this.audioStreams_, this.audioStreams_[id]);
+      delete this.audioStreams_[id];
+    },
+
 
     /**
      * Adds a player to the list of players to manage.
@@ -75,5 +110,5 @@
     }
   };
 
-  return PlayerManager;
+  return Manager;
 }());
diff --git a/content/browser/resources/media/new/util.js b/content/browser/resources/media/new/util.js
index 5909e9e..29cc1b1 100644
--- a/content/browser/resources/media/new/util.js
+++ b/content/browser/resources/media/new/util.js
@@ -29,6 +29,19 @@
       }
     }
   };
+  util.millisecondsToString = function(timeMillis) {
+    function pad(num) {
+      num = num.toString();
+      if (num.length < 2) {
+        return '0' + num;
+      }
+      return num;
+    }
+
+    var date = new Date(timeMillis);
+    return pad(date.getUTCHours()) + ':' + pad(date.getUTCMinutes()) + ':' +
+        pad(date.getUTCSeconds()) + ' ' + pad((date.getMilliseconds()) % 1000);
+  };
 
   return util;
 }());
diff --git a/content/browser/startup_task_runner.cc b/content/browser/startup_task_runner.cc
index a7e730e..d1fc904 100644
--- a/content/browser/startup_task_runner.cc
+++ b/content/browser/startup_task_runner.cc
@@ -11,40 +11,50 @@
 namespace content {
 
 StartupTaskRunner::StartupTaskRunner(
-    bool browser_may_start_asynchronously,
     base::Callback<void(int)> const startup_complete_callback,
     scoped_refptr<base::SingleThreadTaskRunner> proxy)
-    : asynchronous_startup_(browser_may_start_asynchronously),
-      startup_complete_callback_(startup_complete_callback),
-      proxy_(proxy) {}
+    : startup_complete_callback_(startup_complete_callback), proxy_(proxy) {}
+
+StartupTaskRunner::~StartupTaskRunner() {}
 
 void StartupTaskRunner::AddTask(StartupTask& callback) {
   task_list_.push_back(callback);
 }
 
-void StartupTaskRunner::StartRunningTasks() {
+void StartupTaskRunner::StartRunningTasksAsync() {
   DCHECK(proxy_);
   int result = 0;
-  if (asynchronous_startup_ && !task_list_.empty()) {
-    const base::Closure next_task =
-        base::Bind(&StartupTaskRunner::WrappedTask, this);
-    proxy_->PostNonNestableTask(FROM_HERE, next_task);
-  } else {
-    for (std::list<StartupTask>::iterator it = task_list_.begin();
-         it != task_list_.end();
-         it++) {
-      result = it->Run();
-      if (result > 0) {
-        break;
-      }
-    }
+  if (task_list_.empty()) {
     if (!startup_complete_callback_.is_null()) {
       startup_complete_callback_.Run(result);
     }
+  } else {
+    const base::Closure next_task =
+        base::Bind(&StartupTaskRunner::WrappedTask, base::Unretained(this));
+    proxy_->PostNonNestableTask(FROM_HERE, next_task);
+  }
+}
+
+void StartupTaskRunner::RunAllTasksNow() {
+  int result = 0;
+  for (std::list<StartupTask>::iterator it = task_list_.begin();
+       it != task_list_.end();
+       it++) {
+    result = it->Run();
+    if (result > 0) break;
+  }
+  if (!startup_complete_callback_.is_null()) {
+    startup_complete_callback_.Run(result);
   }
 }
 
 void StartupTaskRunner::WrappedTask() {
+  if (task_list_.empty()) {
+    // This will happen if the remaining tasks have been run synchronously since
+    // the WrappedTask was created. Any callback will already have been called,
+    // so there is nothing to do
+    return;
+  }
   int result = task_list_.front().Run();
   task_list_.pop_front();
   if (result > 0 || task_list_.empty()) {
@@ -53,11 +63,9 @@
     }
   } else {
     const base::Closure next_task =
-        base::Bind(&StartupTaskRunner::WrappedTask, this);
+        base::Bind(&StartupTaskRunner::WrappedTask, base::Unretained(this));
     proxy_->PostNonNestableTask(FROM_HERE, next_task);
   }
 }
 
-StartupTaskRunner::~StartupTaskRunner() {}
-
 }  // namespace content
diff --git a/content/browser/startup_task_runner.h b/content/browser/startup_task_runner.h
index 5f954ed..80e5627 100644
--- a/content/browser/startup_task_runner.h
+++ b/content/browser/startup_task_runner.h
@@ -8,7 +8,6 @@
 #include <list>
 
 #include "base/callback.h"
-#include "base/memory/ref_counted.h"
 #include "base/single_thread_task_runner.h"
 
 #include "build/build_config.h"
@@ -32,30 +31,31 @@
 // no opportunity to handle UI events between the tasks of a
 // SingleThreadedTaskRunner.
 
-class CONTENT_EXPORT StartupTaskRunner
-    : public base::RefCounted<StartupTaskRunner> {
+class CONTENT_EXPORT StartupTaskRunner {
 
  public:
   // Constructor: Note that |startup_complete_callback| is optional. If it is
   // not null it will be called once all the startup tasks have run.
-  StartupTaskRunner(bool browser_may_start_asynchronously,
-                    base::Callback<void(int)> startup_complete_callback,
+  StartupTaskRunner(base::Callback<void(int)> startup_complete_callback,
                     scoped_refptr<base::SingleThreadTaskRunner> proxy);
 
-  // Add a task to the queue of startup tasks to be run.
-  virtual void AddTask(StartupTask& callback);
+  ~StartupTaskRunner();
 
-  // Start running the tasks.
-  virtual void StartRunningTasks();
+  // Add a task to the queue of startup tasks to be run.
+  void AddTask(StartupTask& callback);
+
+  // Start running the tasks asynchronously.
+  void StartRunningTasksAsync();
+
+  // Run all tasks, or all remaining tasks, synchronously
+  void RunAllTasksNow();
 
  private:
   friend class base::RefCounted<StartupTaskRunner>;
-  virtual ~StartupTaskRunner();
 
   std::list<StartupTask> task_list_;
   void WrappedTask();
 
-  const bool asynchronous_startup_;
   base::Callback<void(int)> startup_complete_callback_;
   scoped_refptr<base::SingleThreadTaskRunner> proxy_;
 
diff --git a/content/browser/startup_task_runner_unittest.cc b/content/browser/startup_task_runner_unittest.cc
index 2efa79f..b63f555 100644
--- a/content/browser/startup_task_runner_unittest.cc
+++ b/content/browser/startup_task_runner_unittest.cc
@@ -116,20 +116,19 @@
   EXPECT_CALL(mock_runner, PostDelayedTask(_, _, _)).Times(0);
   EXPECT_CALL(mock_runner, PostNonNestableDelayedTask(_, _, _)).Times(0);
 
-  scoped_refptr<StartupTaskRunner> runner =
-      new StartupTaskRunner(false, base::Bind(&Observer), proxy);
+  StartupTaskRunner runner(base::Bind(&Observer), proxy);
 
   StartupTask task1 =
       base::Bind(&StartupTaskRunnerTest::Task1, base::Unretained(this));
-  runner->AddTask(task1);
+  runner.AddTask(task1);
   EXPECT_EQ(GetLastTask(), 0);
   StartupTask task2 =
       base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
-  runner->AddTask(task2);
+  runner.AddTask(task2);
 
   // Nothing should run until we tell them to.
   EXPECT_EQ(GetLastTask(), 0);
-  runner->StartRunningTasks();
+  runner.RunAllTasksNow();
 
   // On an immediate StartupTaskRunner the tasks should now all have run.
   EXPECT_EQ(GetLastTask(), 2);
@@ -145,20 +144,19 @@
   EXPECT_CALL(mock_runner, PostDelayedTask(_, _, _)).Times(0);
   EXPECT_CALL(mock_runner, PostNonNestableDelayedTask(_, _, _)).Times(0);
 
-  scoped_refptr<StartupTaskRunner> runner =
-      new StartupTaskRunner(false, base::Callback<void(int)>(), proxy);
+  StartupTaskRunner runner(base::Callback<void(int)>(), proxy);
 
   StartupTask task1 =
       base::Bind(&StartupTaskRunnerTest::Task1, base::Unretained(this));
-  runner->AddTask(task1);
+  runner.AddTask(task1);
   EXPECT_EQ(GetLastTask(), 0);
   StartupTask task2 =
       base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
-  runner->AddTask(task2);
+  runner.AddTask(task2);
 
   // Nothing should run until we tell them to.
   EXPECT_EQ(GetLastTask(), 0);
-  runner->StartRunningTasks();
+  runner.RunAllTasksNow();
 
   // On an immediate StartupTaskRunner the tasks should now all have run.
   EXPECT_EQ(GetLastTask(), 2);
@@ -173,20 +171,19 @@
   EXPECT_CALL(mock_runner, PostDelayedTask(_, _, _)).Times(0);
   EXPECT_CALL(mock_runner, PostNonNestableDelayedTask(_, _, _)).Times(0);
 
-  scoped_refptr<StartupTaskRunner> runner =
-      new StartupTaskRunner(false, base::Bind(&Observer), proxy);
+  StartupTaskRunner runner(base::Bind(&Observer), proxy);
 
   StartupTask task3 =
       base::Bind(&StartupTaskRunnerTest::FailingTask, base::Unretained(this));
-  runner->AddTask(task3);
+  runner.AddTask(task3);
   EXPECT_EQ(GetLastTask(), 0);
   StartupTask task2 =
       base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
-  runner->AddTask(task2);
+  runner.AddTask(task2);
 
   // Nothing should run until we tell them to.
   EXPECT_EQ(GetLastTask(), 0);
-  runner->StartRunningTasks();
+  runner.RunAllTasksNow();
 
   // Only the first task should have run, since it failed
   EXPECT_EQ(GetLastTask(), 3);
@@ -207,19 +204,18 @@
       .Times(testing::Between(2, 3))
       .WillRepeatedly(WithArg<1>(Invoke(SaveTaskArg)));
 
-  scoped_refptr<StartupTaskRunner> runner =
-      new StartupTaskRunner(true, base::Bind(&Observer), proxy);
+  StartupTaskRunner runner(base::Bind(&Observer), proxy);
 
   StartupTask task1 =
       base::Bind(&StartupTaskRunnerTest::Task1, base::Unretained(this));
-  runner->AddTask(task1);
+  runner.AddTask(task1);
   StartupTask task2 =
       base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
-  runner->AddTask(task2);
+  runner.AddTask(task2);
 
   // Nothing should run until we tell them to.
   EXPECT_EQ(GetLastTask(), 0);
-  runner->StartRunningTasks();
+  runner.StartRunningTasksAsync();
 
   // No tasks should have run yet, since we the message loop hasn't run.
   EXPECT_EQ(GetLastTask(), 0);
@@ -248,19 +244,18 @@
       .Times(testing::Between(1, 2))
       .WillRepeatedly(WithArg<1>(Invoke(SaveTaskArg)));
 
-  scoped_refptr<StartupTaskRunner> runner =
-      new StartupTaskRunner(true, base::Bind(&Observer), proxy);
+  StartupTaskRunner runner(base::Bind(&Observer), proxy);
 
   StartupTask task3 =
       base::Bind(&StartupTaskRunnerTest::FailingTask, base::Unretained(this));
-  runner->AddTask(task3);
+  runner.AddTask(task3);
   StartupTask task2 =
       base::Bind(&StartupTaskRunnerTest::Task2, base::Unretained(this));
-  runner->AddTask(task2);
+  runner.AddTask(task2);
 
   // Nothing should run until we tell them to.
   EXPECT_EQ(GetLastTask(), 0);
-  runner->StartRunningTasks();
+  runner.StartRunningTasksAsync();
 
   // No tasks should have run yet, since we the message loop hasn't run.
   EXPECT_EQ(GetLastTask(), 0);
diff --git a/content/browser/streams/stream.h b/content/browser/streams/stream.h
index a0599de..3937f5b 100644
--- a/content/browser/streams/stream.h
+++ b/content/browser/streams/stream.h
@@ -58,6 +58,10 @@
   // Removes the write observer.  |observer| must be the current observer.
   void RemoveWriteObserver(StreamWriteObserver* observer);
 
+  // Stops accepting new data, clears all buffer, unregisters this stream from
+  // |registry_| and make coming ReadRawData() calls return STREAM_ABORTED.
+  void Abort();
+
   // Adds the data in |buffer| to the stream.  Takes ownership of |buffer|.
   void AddData(scoped_refptr<net::IOBuffer> buffer, size_t size);
   // Adds data of |size| at |data| to the stream. This method creates a copy
@@ -87,10 +91,6 @@
     return last_total_buffered_bytes_;
   }
 
- protected:
-  // Stops accepting new data and make ReadRawData() return STREAM_ABORTED.
-  void Abort();
-
  private:
   friend class base::RefCountedThreadSafe<Stream>;
 
diff --git a/content/browser/streams/stream_url_request_job_unittest.cc b/content/browser/streams/stream_url_request_job_unittest.cc
index 4bb1798..8914fe5 100644
--- a/content/browser/streams/stream_url_request_job_unittest.cc
+++ b/content/browser/streams/stream_url_request_job_unittest.cc
@@ -83,6 +83,7 @@
 
     // Verify response.
     EXPECT_TRUE(request_->status().is_success());
+    ASSERT_TRUE(request_->response_headers());
     EXPECT_EQ(expected_status_code,
               request_->response_headers()->response_code());
     EXPECT_EQ(expected_response, delegate.data_received());
diff --git a/content/browser/web_contents/aura/window_slider.cc b/content/browser/web_contents/aura/window_slider.cc
index 7afd32d..a4bd552 100644
--- a/content/browser/web_contents/aura/window_slider.cc
+++ b/content/browser/web_contents/aura/window_slider.cc
@@ -58,8 +58,11 @@
       owner_(owner),
       delta_x_(0.f),
       weak_factory_(this),
-      horiz_start_threshold_(content::GetOverscrollConfig(
-          content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START)),
+      active_start_threshold_(0.f),
+      start_threshold_touchscreen_(content::GetOverscrollConfig(
+          content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN)),
+      start_threshold_touchpad_(content::GetOverscrollConfig(
+          content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD)),
       complete_threshold_(content::GetOverscrollConfig(
           content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE)) {
   event_window_->AddPreTargetHandler(this);
@@ -89,7 +92,7 @@
 }
 
 bool WindowSlider::IsSlideInProgress() const {
-  return fabs(delta_x_) >= horiz_start_threshold_ || slider_.get() ||
+  return fabs(delta_x_) >= active_start_threshold_ || slider_.get() ||
       weak_factory_.HasWeakPtrs();
 }
 
@@ -107,7 +110,7 @@
 void WindowSlider::UpdateForScroll(float x_offset, float y_offset) {
   float old_delta = delta_x_;
   delta_x_ += x_offset;
-  if (fabs(delta_x_) < horiz_start_threshold_ && !slider_.get())
+  if (fabs(delta_x_) < active_start_threshold_ && !slider_.get())
     return;
 
   if ((old_delta < 0 && delta_x_ > 0) ||
@@ -127,13 +130,13 @@
     SetupSliderLayer();
   }
 
-  if (delta_x_ <= -horiz_start_threshold_) {
+  if (delta_x_ <= -active_start_threshold_) {
     translate = owner_->bounds().width() +
-        std::max(delta_x_ + horiz_start_threshold_,
+        std::max(delta_x_ + active_start_threshold_,
                  static_cast<float>(-owner_->bounds().width()));
     translate_layer = slider_.get();
-  } else if (delta_x_ >= horiz_start_threshold_) {
-    translate = std::min(delta_x_ - horiz_start_threshold_,
+  } else if (delta_x_ >= active_start_threshold_) {
+    translate = std::min(delta_x_ - active_start_threshold_,
                          static_cast<float>(owner_->bounds().width()));
     translate_layer = owner_->layer();
   } else {
@@ -153,7 +156,7 @@
     return;
 
   int width = owner_->bounds().width();
-  float ratio = (fabs(delta_x_) - horiz_start_threshold_) / width;
+  float ratio = (fabs(delta_x_) - active_start_threshold_) / width;
   if (ratio < complete_threshold_) {
     ResetScroll();
     return;
@@ -245,6 +248,7 @@
 }
 
 void WindowSlider::OnScrollEvent(ui::ScrollEvent* event) {
+  active_start_threshold_ = start_threshold_touchpad_;
   if (event->type() == ui::ET_SCROLL)
     UpdateForScroll(event->x_offset_ordinal(), event->y_offset_ordinal());
   else if (event->type() == ui::ET_SCROLL_FLING_START)
@@ -255,6 +259,7 @@
 }
 
 void WindowSlider::OnGestureEvent(ui::GestureEvent* event) {
+  active_start_threshold_ = start_threshold_touchscreen_;
   const ui::GestureEventDetails& details = event->details();
   switch (event->type()) {
     case ui::ET_GESTURE_SCROLL_BEGIN:
diff --git a/content/browser/web_contents/aura/window_slider.h b/content/browser/web_contents/aura/window_slider.h
index 227ad1b..cd140ff 100644
--- a/content/browser/web_contents/aura/window_slider.h
+++ b/content/browser/web_contents/aura/window_slider.h
@@ -120,7 +120,10 @@
 
   base::WeakPtrFactory<WindowSlider> weak_factory_;
 
-  const float horiz_start_threshold_;
+  float active_start_threshold_;
+
+  const float start_threshold_touchscreen_;
+  const float start_threshold_touchpad_;
   const float complete_threshold_;
 
   DISALLOW_COPY_AND_ASSIGN(WindowSlider);
diff --git a/content/browser/web_contents/navigation_controller_impl.cc b/content/browser/web_contents/navigation_controller_impl.cc
index c3e9140..c4a0c24 100644
--- a/content/browser/web_contents/navigation_controller_impl.cc
+++ b/content/browser/web_contents/navigation_controller_impl.cc
@@ -638,7 +638,7 @@
       break;
     case LOAD_TYPE_BROWSER_INITIATED_HTTP_POST:
       if (!params.url.SchemeIs(chrome::kHttpScheme) &&
-          !params.url.SchemeIs(chrome::kHttpsScheme)) {
+          !params.url.SchemeIs(kHttpsScheme)) {
         NOTREACHED() << "Http post load must use http(s) scheme.";
         return;
       }
@@ -798,6 +798,7 @@
   NavigationEntryImpl* active_entry =
       NavigationEntryImpl::FromNavigationEntry(GetLastCommittedEntry());
   active_entry->SetTimestamp(timestamp);
+  active_entry->SetHttpStatusCode(params.http_status_code);
   active_entry->SetPageState(params.page_state);
   // No longer needed since content state will hold the post data if any.
   active_entry->SetBrowserInitiatedPostData(NULL);
diff --git a/content/browser/web_contents/navigation_controller_impl_unittest.cc b/content/browser/web_contents/navigation_controller_impl_unittest.cc
index 7a0b03b..a14f6e2 100644
--- a/content/browser/web_contents/navigation_controller_impl_unittest.cc
+++ b/content/browser/web_contents/navigation_controller_impl_unittest.cc
@@ -355,8 +355,9 @@
   EXPECT_FALSE(controller.CanGoForward());
   EXPECT_EQ(contents()->GetMaxPageID(), -1);
 
-  // The timestamp should not have been set yet.
+  // Neither the timestamp nor the status code should have been set yet.
   EXPECT_TRUE(controller.GetPendingEntry()->GetTimestamp().is_null());
+  EXPECT_EQ(0, controller.GetPendingEntry()->GetHttpStatusCode());
 
   // We should have gotten no notifications from the preceeding checks.
   EXPECT_EQ(0U, notifications.size());
diff --git a/content/browser/web_contents/navigation_entry_impl.cc b/content/browser/web_contents/navigation_entry_impl.cc
index a23b7b6..0a86644 100644
--- a/content/browser/web_contents/navigation_entry_impl.cc
+++ b/content/browser/web_contents/navigation_entry_impl.cc
@@ -48,6 +48,7 @@
       post_id_(-1),
       restore_type_(RESTORE_NONE),
       is_overriding_user_agent_(false),
+      http_status_code_(0),
       is_renderer_initiated_(false),
       should_replace_entry_(false),
       should_clear_history_list_(false),
@@ -75,6 +76,7 @@
       post_id_(-1),
       restore_type_(RESTORE_NONE),
       is_overriding_user_agent_(false),
+      http_status_code_(0),
       is_renderer_initiated_(is_renderer_initiated),
       should_replace_entry_(false),
       should_clear_history_list_(false),
@@ -277,6 +279,14 @@
   return timestamp_;
 }
 
+void NavigationEntryImpl::SetHttpStatusCode(int http_status_code) {
+  http_status_code_ = http_status_code;
+}
+
+int NavigationEntryImpl::GetHttpStatusCode() const {
+  return http_status_code_;
+}
+
 void NavigationEntryImpl::SetCanLoadLocalResources(bool allow) {
   can_load_local_resources_ = allow;
 }
diff --git a/content/browser/web_contents/navigation_entry_impl.h b/content/browser/web_contents/navigation_entry_impl.h
index 96d7ac9..63924b8 100644
--- a/content/browser/web_contents/navigation_entry_impl.h
+++ b/content/browser/web_contents/navigation_entry_impl.h
@@ -84,6 +84,8 @@
   virtual bool GetExtraData(const std::string& key,
                             string16* data) const OVERRIDE;
   virtual void ClearExtraData(const std::string& key) OVERRIDE;
+  virtual void SetHttpStatusCode(int http_status_code) OVERRIDE;
+  virtual int GetHttpStatusCode() const OVERRIDE;
 
   void set_unique_id(int unique_id) {
     unique_id_ = unique_id;
@@ -231,6 +233,7 @@
   GURL original_request_url_;
   bool is_overriding_user_agent_;
   base::Time timestamp_;
+  int http_status_code_;
 
   // This member is not persisted with session restore because it is transient.
   // If the post request succeeds, this field is cleared since the same
diff --git a/content/browser/web_contents/render_view_host_manager.cc b/content/browser/web_contents/render_view_host_manager.cc
index 2c92430..2f62591 100644
--- a/content/browser/web_contents/render_view_host_manager.cc
+++ b/content/browser/web_contents/render_view_host_manager.cc
@@ -200,6 +200,10 @@
   if (!cross_navigation_pending_)
     return true;
 
+  // We should always have a pending RVH when there's a cross-process navigation
+  // in progress.  Sanity check this for http://crbug.com/276333.
+  CHECK(pending_render_view_host_);
+
   // If the tab becomes unresponsive during {before}unload while doing a
   // cross-site navigation, proceed with the navigation.  (This assumes that
   // the pending RenderViewHost is still responsive.)
diff --git a/content/browser/web_contents/render_view_host_manager_unittest.cc b/content/browser/web_contents/render_view_host_manager_unittest.cc
index ea35e6b..77d6533 100644
--- a/content/browser/web_contents/render_view_host_manager_unittest.cc
+++ b/content/browser/web_contents/render_view_host_manager_unittest.cc
@@ -304,7 +304,7 @@
   ntp_process_host->sink().ClearMessages();
   ViewHostMsg_RunJavaScriptMessage js_msg(
       rvh()->GetRoutingID(), msg, msg, kChromeURL,
-      JAVASCRIPT_MESSAGE_TYPE_CONFIRM, &result, &unused);
+      JAVASCRIPT_MESSAGE_TYPE_CONFIRM, false, &result, &unused);
   js_msg.EnableMessagePumping();
   EXPECT_TRUE(ntp_rvh->OnMessageReceived(js_msg));
   EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 7d999ec..578f900 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -1929,8 +1929,8 @@
 
 void WebContentsImpl::GenerateMHTML(
     const base::FilePath& file,
-    const base::Callback<void(const base::FilePath&, int64)>& callback) {
-  MHTMLGenerationManager::GetInstance()->GenerateMHTML(this, file, callback);
+    const base::Callback<void(int64)>& callback) {
+  MHTMLGenerationManager::GetInstance()->SaveMHTML(this, file, callback);
 }
 
 bool WebContentsImpl::IsActiveEntry(int32 page_id) {
@@ -2873,16 +2873,14 @@
       NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
       Source<WebContents>(this),
       Details<RenderViewHost>(render_view_host));
-  NavigationEntry* entry = controller_.GetActiveEntry();
-  if (!entry)
-    return;
 
   // When we're creating views, we're still doing initial setup, so we always
   // use the pending Web UI rather than any possibly existing committed one.
   if (render_manager_.pending_web_ui())
     render_manager_.pending_web_ui()->RenderViewCreated(render_view_host);
 
-  if (entry->IsViewSourceMode()) {
+  NavigationEntry* entry = controller_.GetActiveEntry();
+  if (entry && entry->IsViewSourceMode()) {
     // Put the renderer in view source mode.
     render_view_host->Send(
         new ViewMsg_EnableViewSourceMode(render_view_host->GetRoutingID()));
@@ -3356,6 +3354,7 @@
     const string16& default_prompt,
     const GURL& frame_url,
     JavaScriptMessageType javascript_message_type,
+    bool user_gesture,
     IPC::Message* reply_msg,
     bool* did_suppress_message) {
   // Suppress JavaScript dialogs when requested. Also suppress messages when
@@ -3379,6 +3378,7 @@
         javascript_message_type,
         message,
         default_prompt,
+        user_gesture,
         base::Bind(&WebContentsImpl::OnDialogClosed,
                    base::Unretained(this),
                    rvh,
@@ -3552,7 +3552,7 @@
 
 void WebContentsImpl::UpdateRenderViewSizeForRenderManager() {
   // TODO(brettw) this is a hack. See WebContentsView::SizeContents.
-  gfx::Size size = view_->GetContainerSize();
+  gfx::Size size = GetSizeForNewRenderView();
   // 0x0 isn't a valid window size (minimal window size is 1x1) but it may be
   // here during container initialization and normal window size will be set
   // later. In case of tab duplication this resizing to 0x0 prevents setting
@@ -3647,7 +3647,7 @@
 
   // Now that the RenderView has been created, we need to tell it its size.
   if (rwh_view)
-    rwh_view->SetSize(view_->GetContainerSize());
+    rwh_view->SetSize(GetSizeForNewRenderView());
 
   // Make sure we use the correct starting page_id in the new RenderView.
   UpdateMaxPageIDIfNecessary(render_view_host);
@@ -3751,4 +3751,13 @@
   power_save_blockers_.clear();
 }
 
+gfx::Size WebContentsImpl::GetSizeForNewRenderView() const {
+  gfx::Size size;
+  if (delegate_)
+    size = delegate_->GetSizeForNewRenderView(this);
+  if (size.IsEmpty())
+    size = view_->GetContainerSize();
+  return size;
+}
+
 }  // namespace content
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index bb56ddb..bd896fe 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -275,7 +275,7 @@
                          const Referrer& referrer) OVERRIDE;
   virtual void GenerateMHTML(
       const base::FilePath& file,
-      const base::Callback<void(const base::FilePath&, int64)>& callback)
+      const base::Callback<void(int64)>& callback)
           OVERRIDE;
   virtual bool IsActiveEntry(int32 page_id) OVERRIDE;
 
@@ -398,6 +398,7 @@
                                     const string16& default_prompt,
                                     const GURL& frame_url,
                                     JavaScriptMessageType type,
+                                    bool user_gesture,
                                     IPC::Message* reply_msg,
                                     bool* did_suppress_message) OVERRIDE;
   virtual void RunBeforeUnloadConfirm(RenderViewHost* rvh,
@@ -760,6 +761,9 @@
   // Clear all PowerSaveBlockers, leave power_save_blocker_ empty.
   void ClearAllPowerSaveBlockers();
 
+  // Helper function to invoke WebContentsDelegate::GetSizeForNewRenderView().
+  gfx::Size GetSizeForNewRenderView() const;
+
   // Data for core operation ---------------------------------------------------
 
   // Delegate for notifying our owner about stuff. Not owned by us.
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index 70ab523..05e3ebe 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -10,7 +10,9 @@
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_view.h"
 #include "content/public/common/content_paths.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
@@ -21,6 +23,25 @@
 
 namespace content {
 
+void ResizeWebContentsView(Shell* shell, const gfx::Size& size,
+                           bool set_start_page) {
+  // Shell::SizeTo is not implemented on Aura; WebContentsView::SizeContents
+  // works on Win and ChromeOS but not Linux - we need to resize the shell
+  // window on Linux because if we don't, the next layout of the unchanged shell
+  // window will resize WebContentsView back to the previous size.
+  // The cleaner and shorter SizeContents is preferred as more platforms convert
+  // to Aura.
+#if defined(TOOLKIT_GTK) || defined(OS_MACOSX)
+  shell->SizeTo(size.width(), size.height());
+  // If |set_start_page| is true, start with blank page to make sure resize
+  // takes effect.
+  if (set_start_page)
+    NavigateToURL(shell, GURL("about://blank"));
+#else
+  shell->web_contents()->GetView()->SizeContents(size);
+#endif  // defined(TOOLKIT_GTK) || defined(OS_MACOSX)
+}
+
 class WebContentsImplBrowserTest : public ContentBrowserTest {
  public:
   WebContentsImplBrowserTest() {}
@@ -82,6 +103,51 @@
   bool done_;
 };
 
+class RenderViewSizeDelegate : public WebContentsDelegate {
+ public:
+  void set_size_insets(const gfx::Size& size_insets) {
+    size_insets_ = size_insets;
+  }
+
+  // WebContentsDelegate:
+  virtual gfx::Size GetSizeForNewRenderView(
+      const WebContents* web_contents) const OVERRIDE {
+    gfx::Size size(web_contents->GetView()->GetContainerSize());
+    size.Enlarge(size_insets_.width(), size_insets_.height());
+    return size;
+  }
+
+ private:
+  gfx::Size size_insets_;
+};
+
+class RenderViewSizeObserver : public WebContentsObserver {
+ public:
+  RenderViewSizeObserver(Shell* shell, const gfx::Size& wcv_new_size)
+      : WebContentsObserver(shell->web_contents()),
+        shell_(shell),
+        wcv_new_size_(wcv_new_size) {
+  }
+
+  // WebContentsObserver:
+  virtual void RenderViewCreated(RenderViewHost* rvh) OVERRIDE {
+    rwhv_create_size_ = rvh->GetView()->GetViewBounds().size();
+  }
+
+  virtual void NavigateToPendingEntry(
+      const GURL& url,
+      NavigationController::ReloadType reload_type) OVERRIDE {
+    ResizeWebContentsView(shell_, wcv_new_size_, false);
+  }
+
+  gfx::Size rwhv_create_size() const { return rwhv_create_size_; }
+
+ private:
+  Shell* shell_;  // Weak ptr.
+  gfx::Size wcv_new_size_;
+  gfx::Size rwhv_create_size_;
+};
+
 // Test that DidStopLoading includes the correct URL in the details.
 IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, DidStopLoadingDetails) {
   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
@@ -159,4 +225,61 @@
   EXPECT_EQ(rvh->main_frame_id(), root->frame_id());
 }
 
+// TODO(sail): enable this for MAC when auto resizing of WebContentsViewCocoa is
+// fixed.
+#if defined(OS_MACOSX) || defined(OS_ANDROID)
+#define MAYBE_GetSizeForNewRenderView DISABLED_GetSizeForNewRenderView
+#else
+#define MAYBE_GetSizeForNewRenderView GetSizeForNewRenderView
+#endif
+// Test that RenderViewHost is created and updated at the size specified by
+// WebContentsDelegate::GetSizeForNewRenderView().
+IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
+                       MAYBE_GetSizeForNewRenderView) {
+  scoped_ptr<RenderViewSizeDelegate> delegate(new RenderViewSizeDelegate());
+  shell()->web_contents()->SetDelegate(delegate.get());
+  ASSERT_TRUE(shell()->web_contents()->GetDelegate() == delegate.get());
+
+  // When no size is set, RenderWidgetHostView adopts the size of
+  // WebContenntsView.
+  NavigateToURL(shell(), GURL("http://foo0.com"));
+  EXPECT_EQ(shell()->web_contents()->GetView()->GetContainerSize(),
+            shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds().
+                size());
+
+  // When a size is set, RenderWidgetHostView and WebContentsView honor this
+  // size.
+  gfx::Size size(300, 300);
+  gfx::Size size_insets(-10, -15);
+  ResizeWebContentsView(shell(), size, true);
+  delegate->set_size_insets(size_insets);
+  NavigateToURL(shell(), GURL("http://foo1.com"));
+  size.Enlarge(size_insets.width(), size_insets.height());
+  EXPECT_EQ(size,
+            shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds().
+                size());
+  EXPECT_EQ(size, shell()->web_contents()->GetView()->GetContainerSize());
+
+  // If WebContentsView is resized after RenderWidgetHostView is created but
+  // before pending navigation entry is committed, both RenderWidgetHostView and
+  // WebContentsView use the new size of WebContentsView.
+  gfx::Size init_size(200, 200);
+  gfx::Size new_size(100, 100);
+  size_insets = gfx::Size(-20, -30);
+  ResizeWebContentsView(shell(), init_size, true);
+  delegate->set_size_insets(size_insets);
+  RenderViewSizeObserver observer(shell(), new_size);
+  NavigateToURL(shell(), GURL("http://foo2.com"));
+  // RenderWidgetHostView is created at specified size.
+  init_size.Enlarge(size_insets.width(), size_insets.height());
+  EXPECT_EQ(init_size, observer.rwhv_create_size());
+  // RenderViewSizeObserver resizes WebContentsView in NavigateToPendingEntry,
+  // so both WebContentsView and RenderWidgetHostView adopt this new size.
+  new_size.Enlarge(size_insets.width(), size_insets.height());
+  EXPECT_EQ(new_size,
+            shell()->web_contents()->GetRenderWidgetHostView()->GetViewBounds().
+                size());
+  EXPECT_EQ(new_size, shell()->web_contents()->GetView()->GetContainerSize());
+}
+
 }  // namespace content
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index aded7ae..b20cf66 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -2014,7 +2014,7 @@
   bool did_suppress_message = false;
   contents()->RunJavaScriptMessage(contents()->GetRenderViewHost(),
       ASCIIToUTF16("This is an informative message"), ASCIIToUTF16("OK"),
-      kGURL, JAVASCRIPT_MESSAGE_TYPE_ALERT, dummy_message,
+      kGURL, JAVASCRIPT_MESSAGE_TYPE_ALERT, false, dummy_message,
       &did_suppress_message);
   EXPECT_TRUE(did_suppress_message);
 }
diff --git a/content/browser/web_contents/web_contents_view_android.cc b/content/browser/web_contents/web_contents_view_android.cc
index 1f7b247..ebdfd47 100644
--- a/content/browser/web_contents/web_contents_view_android.cc
+++ b/content/browser/web_contents/web_contents_view_android.cc
@@ -5,6 +5,7 @@
 #include "content/browser/web_contents/web_contents_view_android.h"
 
 #include "base/logging.h"
+#include "content/browser/android/browser_media_player_manager.h"
 #include "content/browser/android/content_view_core_impl.h"
 #include "content/browser/renderer_host/render_widget_host_view_android.h"
 #include "content/browser/renderer_host/render_view_host_factory.h"
@@ -12,7 +13,6 @@
 #include "content/browser/web_contents/interstitial_page_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/web_contents_delegate.h"
-#include "media/base/android/media_player_manager.h"
 
 namespace content {
 WebContentsViewPort* CreateWebContentsView(
diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc
index 043efca..1349954 100644
--- a/content/browser/web_contents/web_contents_view_aura.cc
+++ b/content/browser/web_contents/web_contents_view_aura.cc
@@ -1227,11 +1227,12 @@
 // WebContentsViewAura, RenderViewHostDelegateView implementation:
 
 void WebContentsViewAura::ShowContextMenu(const ContextMenuParams& params) {
-  if (delegate_)
-    delegate_->ShowContextMenu(params);
   if (touch_editable_)
     touch_editable_->EndTouchEditing();
-
+  if (delegate_) {
+    delegate_->ShowContextMenu(params);
+    // WARNING: we may have been deleted during the call to ShowContextMenu().
+  }
 }
 
 void WebContentsViewAura::ShowPopupMenu(const gfx::Rect& bounds,
diff --git a/content/browser/web_contents/web_contents_view_win.cc b/content/browser/web_contents/web_contents_view_win.cc
index 6a75e71..0c65b1d 100644
--- a/content/browser/web_contents/web_contents_view_win.cc
+++ b/content/browser/web_contents/web_contents_view_win.cc
@@ -246,6 +246,7 @@
 void WebContentsViewWin::ShowContextMenu(const ContextMenuParams& params) {
   if (delegate_)
     delegate_->ShowContextMenu(params);
+  // WARNING: this may have been deleted.
 }
 
 void WebContentsViewWin::ShowPopupMenu(const gfx::Rect& bounds,
diff --git a/content/browser/web_contents/web_drag_source_mac.mm b/content/browser/web_contents/web_drag_source_mac.mm
index ee365d6..44ac369 100644
--- a/content/browser/web_contents/web_drag_source_mac.mm
+++ b/content/browser/web_contents/web_drag_source_mac.mm
@@ -10,6 +10,7 @@
 #include "base/files/file_path.h"
 #include "base/mac/mac_logging.h"
 #include "base/mac/mac_util.h"
+#include "base/mac/scoped_aedesc.h"
 #include "base/pickle.h"
 #include "base/strings/string_util.h"
 #include "base/strings/sys_string_conversions.h"
@@ -120,6 +121,87 @@
   return drop_path;
 }
 
+void SelectFileInFinder(const base::FilePath& file_path) {
+  DCHECK([NSThread isMainThread]);
+
+  // Create the target of this AppleEvent, the Finder.
+  base::mac::ScopedAEDesc<AEAddressDesc> address;
+  const OSType finder_creator_code = 'MACS';
+  OSErr status = AECreateDesc(typeApplSignature,  // type
+                              &finder_creator_code,  // data
+                              sizeof(finder_creator_code),  // dataSize
+                              address.OutPointer());  // result
+  if (status != noErr) {
+    OSSTATUS_LOG(WARNING, status) << "Could not create SelectFile() AE target";
+    return;
+  }
+
+  // Build the AppleEvent data structure that instructs Finder to select files.
+  base::mac::ScopedAEDesc<AppleEvent> the_event;
+  status = AECreateAppleEvent(kAEMiscStandards,  // theAEEventClass
+                              kAESelect,  // theAEEventID
+                              address,  // target
+                              kAutoGenerateReturnID,  // returnID
+                              kAnyTransactionID,  // transactionID
+                              the_event.OutPointer());  // result
+  if (status != noErr) {
+    OSSTATUS_LOG(WARNING, status) << "Could not create SelectFile() AE event";
+    return;
+  }
+
+  // Create the list of files (only ever one) to select.
+  base::mac::ScopedAEDesc<AEDescList> file_list;
+  status = AECreateList(NULL,  // factoringPtr
+                        0,  // factoredSize
+                        false,  // isRecord
+                        file_list.OutPointer());  // resultList
+  if (status != noErr) {
+    OSSTATUS_LOG(WARNING, status)
+        << "Could not create SelectFile() AE file list";
+    return;
+  }
+
+  // Add the single path to the file list.
+  NSURL* url = [NSURL fileURLWithPath:SysUTF8ToNSString(file_path.value())];
+  base::ScopedCFTypeRef<CFDataRef> url_data(
+      CFURLCreateData(kCFAllocatorDefault, base::mac::NSToCFCast(url),
+                      kCFStringEncodingUTF8, true));
+  status = AEPutPtr(file_list.OutPointer(),  // theAEDescList
+                    0,  // index
+                    typeFileURL,  // typeCode
+                    CFDataGetBytePtr(url_data),  // dataPtr
+                    CFDataGetLength(url_data));  // dataSize
+  if (status != noErr) {
+    OSSTATUS_LOG(WARNING, status)
+        << "Could not add file path to AE list in SelectFile()";
+    return;
+  }
+
+  // Attach the file list to the AppleEvent.
+  status = AEPutParamDesc(the_event.OutPointer(),  // theAppleEvent
+                          keyDirectObject,  // theAEKeyword
+                          file_list);  // theAEDesc
+  if (status != noErr) {
+    OSSTATUS_LOG(WARNING, status)
+        << "Could not put the AE file list the path in SelectFile()";
+    return;
+  }
+
+  // Send the actual event.  Do not care about the reply.
+  base::mac::ScopedAEDesc<AppleEvent> reply;
+  status = AESend(the_event,  // theAppleEvent
+                  reply.OutPointer(),  // reply
+                  kAENoReply + kAEAlwaysInteract,  // sendMode
+                  kAENormalPriority,  // sendPriority
+                  kAEDefaultTimeout,  // timeOutInTicks
+                  NULL, // idleProc
+                  NULL);  // filterProc
+  if (status != noErr) {
+    OSSTATUS_LOG(WARNING, status)
+        << "Could not send AE to Finder in SelectFile()";
+  }
+}
+
 }  // namespace
 
 
@@ -399,6 +481,7 @@
                                        *dropData_,
                                        base::Passed(&fileStream)));
   }
+  SelectFileInFinder(filePath);
 }
 
 - (void)fillPasteboard {
diff --git a/content/browser/webui/web_ui_data_source_impl.cc b/content/browser/webui/web_ui_data_source_impl.cc
index 63e08bc..9d0d171 100644
--- a/content/browser/webui/web_ui_data_source_impl.cc
+++ b/content/browser/webui/web_ui_data_source_impl.cc
@@ -176,6 +176,9 @@
   if (EndsWith(path, ".pdf", false))
     return "application/pdf";
 
+  if (EndsWith(path, ".svg", false))
+    return "image/svg+xml";
+
   return "text/html";
 }
 
diff --git a/content/browser/zygote_host/zygote_host_impl_linux.h b/content/browser/zygote_host/zygote_host_impl_linux.h
index 9027fe7..6a1aca5 100644
--- a/content/browser/zygote_host/zygote_host_impl_linux.h
+++ b/content/browser/zygote_host/zygote_host_impl_linux.h
@@ -40,7 +40,11 @@
   // Unfortunately the Zygote can not accurately figure out if a process
   // is already dead without waiting synchronously for it.
   // |known_dead| should be set to true when we already know that the process
-  // is dead.
+  // is dead. When |known_dead| is false, processes could be seen as
+  // still running, even when they're not. When |known_dead| is true, the
+  // process will be SIGKILL-ed first (which should have no effect if it was
+  // really dead). This is to prevent a waiting waitpid() from blocking in
+  // a single-threaded Zygote. See crbug.com/157458.
   base::TerminationStatus GetTerminationStatus(base::ProcessHandle handle,
                                                bool known_dead,
                                                int* exit_code);
diff --git a/content/child/npapi/plugin_host.cc b/content/child/npapi/plugin_host.cc
index 6094cc9..9b72be4 100644
--- a/content/child/npapi/plugin_host.cc
+++ b/content/child/npapi/plugin_host.cc
@@ -466,8 +466,7 @@
       DCHECK(file_url.SchemeIsFile());
       net::FileURLToFilePath(file_url, &file_path);
     } else {
-      file_path = base::FilePath::FromWStringHack(
-          base::SysNativeMBToWide(file_path_ascii));
+      file_path = base::FilePath::FromUTF8Unsafe(file_path_ascii);
     }
 
     base::PlatformFileInfo post_file_info;
diff --git a/content/child/npapi/plugin_instance.cc b/content/child/npapi/plugin_instance.cc
index c279b9b..8117c75 100644
--- a/content/child/npapi/plugin_instance.cc
+++ b/content/child/npapi/plugin_instance.cc
@@ -189,8 +189,9 @@
 }
 
 // WebPluginLoadDelegate methods
-void PluginInstance::DidFinishLoadWithReason(
-    const GURL& url, NPReason reason, int notify_id) {
+void PluginInstance::DidFinishLoadWithReason(const GURL& url,
+                                             NPReason reason,
+                                             int notify_id) {
   bool notify;
   void* notify_data;
   GetNotifyData(notify_id, &notify, &notify_data);
@@ -210,8 +211,8 @@
 // NPAPI methods
 NPError PluginInstance::NPP_New(unsigned short mode,
                                 short argc,
-                                char *argn[],
-                                char *argv[]) {
+                                char* argn[],
+                                char* argv[]) {
   DCHECK(npp_functions_ != 0);
   DCHECK(npp_functions_->newp != 0);
   DCHECK(argc >= 0);
@@ -249,7 +250,7 @@
   timers_.clear();
 }
 
-NPError PluginInstance::NPP_SetWindow(NPWindow *window) {
+NPError PluginInstance::NPP_SetWindow(NPWindow* window) {
   DCHECK(npp_functions_ != 0);
   DCHECK(npp_functions_->setwindow != 0);
 
@@ -260,9 +261,9 @@
 }
 
 NPError PluginInstance::NPP_NewStream(NPMIMEType type,
-                                      NPStream *stream,
+                                      NPStream* stream,
                                       NPBool seekable,
-                                      unsigned short *stype) {
+                                      unsigned short* stype) {
   DCHECK(npp_functions_ != 0);
   DCHECK(npp_functions_->newstream != 0);
   if (npp_functions_->newstream != 0) {
@@ -271,7 +272,7 @@
   return NPERR_INVALID_FUNCTABLE_ERROR;
 }
 
-NPError PluginInstance::NPP_DestroyStream(NPStream *stream, NPReason reason) {
+NPError PluginInstance::NPP_DestroyStream(NPStream* stream, NPReason reason) {
   DCHECK(npp_functions_ != 0);
   DCHECK(npp_functions_->destroystream != 0);
 
@@ -286,7 +287,7 @@
   return NPERR_INVALID_FUNCTABLE_ERROR;
 }
 
-int PluginInstance::NPP_WriteReady(NPStream *stream) {
+int PluginInstance::NPP_WriteReady(NPStream* stream) {
   DCHECK(npp_functions_ != 0);
   DCHECK(npp_functions_->writeready != 0);
   if (npp_functions_->writeready != 0) {
@@ -295,10 +296,10 @@
   return 0;
 }
 
-int PluginInstance::NPP_Write(NPStream *stream,
+int PluginInstance::NPP_Write(NPStream* stream,
                               int offset,
                               int len,
-                              void *buffer) {
+                              void* buffer) {
   DCHECK(npp_functions_ != 0);
   DCHECK(npp_functions_->write != 0);
   if (npp_functions_->write != 0) {
@@ -307,7 +308,7 @@
   return 0;
 }
 
-void PluginInstance::NPP_StreamAsFile(NPStream *stream, const char *fname) {
+void PluginInstance::NPP_StreamAsFile(NPStream* stream, const char* fname) {
   DCHECK(npp_functions_ != 0);
   DCHECK(npp_functions_->asfile != 0);
   if (npp_functions_->asfile != 0) {
@@ -317,13 +318,13 @@
   // Creating a temporary FilePath instance on the stack as the explicit
   // FilePath constructor with StringType as an argument causes a compiler
   // error when invoked via vector push back.
-  base::FilePath file_name = base::FilePath::FromWStringHack(UTF8ToWide(fname));
+  base::FilePath file_name = base::FilePath::FromUTF8Unsafe(fname);
   files_created_.push_back(file_name);
 }
 
-void PluginInstance::NPP_URLNotify(const char *url,
+void PluginInstance::NPP_URLNotify(const char* url,
                                    NPReason reason,
-                                   void *notifyData) {
+                                   void* notifyData) {
   DCHECK(npp_functions_ != 0);
   DCHECK(npp_functions_->urlnotify != 0);
   if (npp_functions_->urlnotify != 0) {
@@ -331,7 +332,7 @@
   }
 }
 
-NPError PluginInstance::NPP_GetValue(NPPVariable variable, void *value) {
+NPError PluginInstance::NPP_GetValue(NPPVariable variable, void* value) {
   DCHECK(npp_functions_ != 0);
   // getvalue is NULL for Shockwave
   if (npp_functions_->getvalue != 0) {
@@ -340,7 +341,7 @@
   return NPERR_INVALID_FUNCTABLE_ERROR;
 }
 
-NPError PluginInstance::NPP_SetValue(NPNVariable variable, void *value) {
+NPError PluginInstance::NPP_SetValue(NPNVariable variable, void* value) {
   DCHECK(npp_functions_ != 0);
   if (npp_functions_->setvalue != 0) {
     return npp_functions_->setvalue(npp_, variable, value);
@@ -431,15 +432,15 @@
   }
 }
 
-void PluginInstance::PluginThreadAsyncCall(void (*func)(void *),
-                                           void *user_data) {
+void PluginInstance::PluginThreadAsyncCall(void (*func)(void*),
+                                           void* user_data) {
   message_loop_->PostTask(
       FROM_HERE, base::Bind(&PluginInstance::OnPluginThreadAsyncCall, this,
                             func, user_data));
 }
 
-void PluginInstance::OnPluginThreadAsyncCall(void (*func)(void *),
-                                             void *user_data) {
+void PluginInstance::OnPluginThreadAsyncCall(void (*func)(void*),
+                                             void* user_data) {
   // Do not invoke the callback if NPP_Destroy has already been invoked.
   if (webplugin_)
     func(user_data);
@@ -652,8 +653,9 @@
 #endif
 }
 
-void PluginInstance::GetNotifyData(
-    int notify_id, bool* notify, void** notify_data) {
+void PluginInstance::GetNotifyData(int notify_id,
+                                   bool* notify,
+                                   void** notify_data) {
   PendingRequestMap::iterator iter = pending_requests_.find(notify_id);
   if (iter != pending_requests_.end()) {
     *notify = true;
diff --git a/content/child/npapi/webplugin_delegate_impl_win.cc b/content/child/npapi/webplugin_delegate_impl_win.cc
index 01f2100..6193055 100644
--- a/content/child/npapi/webplugin_delegate_impl_win.cc
+++ b/content/child/npapi/webplugin_delegate_impl_win.cc
@@ -321,7 +321,7 @@
     if (current_wnd_proc == DummyWindowProc) {
       SetWindowLongPtr(dummy_window_for_activation_,
                        GWLP_WNDPROC,
-                       reinterpret_cast<LONG>(old_dummy_window_proc_));
+                       reinterpret_cast<LONG_PTR>(old_dummy_window_proc_));
     }
     ::DestroyWindow(dummy_window_for_activation_);
   }
@@ -516,8 +516,9 @@
 
   // Calling SetWindowLongPtrA here makes the window proc ASCII, which is
   // required by at least the Shockwave Director plug-in.
-  SetWindowLongPtrA(
-      windowed_handle_, GWLP_WNDPROC, reinterpret_cast<LONG>(DefWindowProcA));
+  SetWindowLongPtrA(windowed_handle_,
+                    GWLP_WNDPROC,
+                    reinterpret_cast<LONG_PTR>(DefWindowProcA));
 
   return true;
 }
@@ -530,7 +531,7 @@
     if (current_wnd_proc == NativeWndProc) {
       SetWindowLongPtr(windowed_handle_,
                        GWLP_WNDPROC,
-                       reinterpret_cast<LONG>(plugin_wnd_proc_));
+                       reinterpret_cast<LONG_PTR>(plugin_wnd_proc_));
     }
 
     plugin_->WillDestroyWindow(windowed_handle_);
@@ -716,7 +717,7 @@
   if (current_wnd_proc != wnd_proc) {
     WNDPROC old_flash_proc = reinterpret_cast<WNDPROC>(SetWindowLongPtr(
         window, GWLP_WNDPROC,
-        reinterpret_cast<LONG>(wnd_proc)));
+        reinterpret_cast<LONG_PTR>(wnd_proc)));
     DCHECK(old_flash_proc);
     g_window_handle_proc_map.Get()[window] = old_flash_proc;
   }
@@ -751,7 +752,7 @@
   DCHECK(result == TRUE) << "SetProp failed, last error = " << GetLastError();
   old_dummy_window_proc_ = reinterpret_cast<WNDPROC>(SetWindowLongPtr(
       dummy_window_for_activation_, GWLP_WNDPROC,
-      reinterpret_cast<LONG>(DummyWindowProc)));
+      reinterpret_cast<LONG_PTR>(DummyWindowProc)));
 
   // Flash creates background windows which use excessive CPU in our
   // environment; we wrap these windows and throttle them so that they don't
@@ -843,8 +844,10 @@
   WNDPROC current_wnd_proc = reinterpret_cast<WNDPROC>(
         GetWindowLongPtr(windowed_handle_, GWLP_WNDPROC));
   if (current_wnd_proc != NativeWndProc) {
-    plugin_wnd_proc_ = reinterpret_cast<WNDPROC>(SetWindowLongPtr(
-        windowed_handle_, GWLP_WNDPROC, reinterpret_cast<LONG>(NativeWndProc)));
+    plugin_wnd_proc_ = reinterpret_cast<WNDPROC>(
+        SetWindowLongPtr(windowed_handle_,
+                         GWLP_WNDPROC,
+                         reinterpret_cast<LONG_PTR>(NativeWndProc)));
   }
 }
 
diff --git a/content/child/plugin_param_traits.cc b/content/child/plugin_param_traits.cc
index b444c17..7a3908d 100644
--- a/content/child/plugin_param_traits.cc
+++ b/content/child/plugin_param_traits.cc
@@ -80,8 +80,8 @@
     result = ReadParam(m, iter, &r->string_value);
   } else if (r->type == content::NPVARIANT_PARAM_SENDER_OBJECT_ROUTING_ID ||
              r->type == content::NPVARIANT_PARAM_RECEIVER_OBJECT_ROUTING_ID) {
-    result = ReadParam(m, iter, &r->npobject_routing_id);
-    result = ReadParam(m, iter, &r->npobject_owner_id);
+    result = ReadParam(m, iter, &r->npobject_routing_id) &&
+        ReadParam(m, iter, &r->npobject_owner_id);
   } else if ((r->type == content::NPVARIANT_PARAM_VOID) ||
              (r->type == content::NPVARIANT_PARAM_NULL)) {
     result = true;
diff --git a/content/child/request_extra_data.cc b/content/child/request_extra_data.cc
index 419fbb4..2c935ce 100644
--- a/content/child/request_extra_data.cc
+++ b/content/child/request_extra_data.cc
@@ -14,6 +14,7 @@
                                    bool was_after_preconnect_request,
                                    bool is_main_frame,
                                    int64 frame_id,
+                                   const GURL& frame_origin,
                                    bool parent_is_main_frame,
                                    int64 parent_frame_id,
                                    bool allow_download,
@@ -25,6 +26,7 @@
                                               was_after_preconnect_request),
       is_main_frame_(is_main_frame),
       frame_id_(frame_id),
+      frame_origin_(frame_origin),
       parent_is_main_frame_(parent_is_main_frame),
       parent_frame_id_(parent_frame_id),
       allow_download_(allow_download),
diff --git a/content/child/request_extra_data.h b/content/child/request_extra_data.h
index 83adf28..0b3d30a 100644
--- a/content/child/request_extra_data.h
+++ b/content/child/request_extra_data.h
@@ -22,6 +22,7 @@
                    bool was_after_preconnect_request,
                    bool is_main_frame,
                    int64 frame_id,
+                   const GURL& frame_origin,
                    bool parent_is_main_frame,
                    int64 parent_frame_id,
                    bool allow_download,
@@ -32,6 +33,7 @@
 
   bool is_main_frame() const { return is_main_frame_; }
   int64 frame_id() const { return frame_id_; }
+  GURL frame_origin() const { return frame_origin_; }
   bool parent_is_main_frame() const { return parent_is_main_frame_; }
   int64 parent_frame_id() const { return parent_frame_id_; }
   bool allow_download() const { return allow_download_; }
@@ -46,6 +48,7 @@
  private:
   bool is_main_frame_;
   int64 frame_id_;
+  GURL frame_origin_;
   bool parent_is_main_frame_;
   int64 parent_frame_id_;
   bool allow_download_;
diff --git a/content/child/resource_dispatcher.cc b/content/child/resource_dispatcher.cc
index 424902f..31e6f6a 100644
--- a/content/child/resource_dispatcher.cc
+++ b/content/child/resource_dispatcher.cc
@@ -16,6 +16,7 @@
 #include "base/metrics/histogram.h"
 #include "base/strings/string_util.h"
 #include "content/child/request_extra_data.h"
+#include "content/child/site_isolation_policy.h"
 #include "content/common/inter_process_time_ticks_converter.h"
 #include "content/common/resource_messages.h"
 #include "content/public/child/resource_dispatcher_delegate.h"
@@ -96,6 +97,9 @@
   // The routing id used when sending IPC messages.
   int routing_id_;
 
+  // The security origin of the frame that initiates this request.
+  GURL frame_origin_;
+
   bool is_synchronous_request_;
 };
 
@@ -135,6 +139,7 @@
         extra_data->transferred_request_child_id();
     request_.transferred_request_request_id =
         extra_data->transferred_request_request_id();
+    frame_origin_ = extra_data->frame_origin();
   } else {
     request_.is_main_frame = false;
     request_.frame_id = -1;
@@ -179,7 +184,7 @@
 
   // generate the request ID, and append it to the message
   request_id_ = dispatcher_->AddPendingRequest(
-      peer_, request_.resource_type, request_.url);
+      peer_, request_.resource_type, frame_origin_, request_.url);
 
   return dispatcher_->message_sender()->Send(
       new ResourceHostMsg_RequestResource(routing_id_, request_id_, request_));
@@ -346,6 +351,11 @@
 
   ResourceResponseInfo renderer_response_info;
   ToResourceResponseInfo(*request_info, response_head, &renderer_response_info);
+  SiteIsolationPolicy::OnReceivedResponse(request_id,
+                                          request_info->frame_origin,
+                                          request_info->response_url,
+                                          request_info->resource_type,
+                                          renderer_response_info);
   request_info->peer->OnReceivedResponse(renderer_response_info);
 }
 
@@ -411,10 +421,24 @@
     CHECK(data_ptr);
     CHECK(data_ptr + data_offset);
 
-    request_info->peer->OnReceivedData(
-        data_ptr + data_offset,
-        data_length,
-        encoded_data_length);
+    // Check whether this response data is compliant with our cross-site
+    // document blocking policy.
+    std::string alternative_data;
+    bool blocked_response = SiteIsolationPolicy::ShouldBlockResponse(
+        request_id, data_ptr + data_offset, data_length, &alternative_data);
+
+    // When the response is not blocked.
+    if (!blocked_response) {
+      request_info->peer->OnReceivedData(
+          data_ptr + data_offset, data_length, encoded_data_length);
+    } else if (alternative_data.size() > 0) {
+      // When the response is blocked, and when we have any alternative data to
+      // send to the renderer. When |alternative_data| is zero-sized, we do not
+      // call peer's callback.
+      request_info->peer->OnReceivedData(alternative_data.data(),
+                                         alternative_data.size(),
+                                         alternative_data.size());
+    }
 
     UMA_HISTOGRAM_TIMES("ResourceDispatcher.OnReceivedDataTime",
                         base::TimeTicks::Now() - time_start);
@@ -462,6 +486,9 @@
     request_info = GetPendingRequestInfo(request_id);
     if (!request_info)
       return;
+    // We update the response_url here so that we can send it to
+    // SiteIsolationPolicy later when OnReceivedResponse is called.
+    request_info->response_url = new_url;
     request_info->pending_redirect_message.reset(
         new ResourceHostMsg_FollowRedirect(routing_id, request_id,
                                            has_new_first_party_for_cookies,
@@ -488,6 +515,8 @@
     bool was_ignored_by_handler,
     const std::string& security_info,
     const base::TimeTicks& browser_completion_time) {
+  SiteIsolationPolicy::OnRequestComplete(request_id);
+
   PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
   if (!request_info)
     return;
@@ -517,11 +546,12 @@
 int ResourceDispatcher::AddPendingRequest(
     ResourceLoaderBridge::Peer* callback,
     ResourceType::Type resource_type,
+    const GURL& frame_origin,
     const GURL& request_url) {
   // Compute a unique request_id for this renderer process.
   int id = MakeRequestID();
   pending_requests_[id] =
-      PendingRequestInfo(callback, resource_type, request_url);
+      PendingRequestInfo(callback, resource_type, frame_origin, request_url);
   return id;
 }
 
@@ -530,6 +560,7 @@
   if (it == pending_requests_.end())
     return false;
 
+  SiteIsolationPolicy::OnRequestComplete(request_id);
   PendingRequestInfo& request_info = it->second;
   ReleaseResourcesInMessageQueue(&request_info.deferred_message_queue);
   pending_requests_.erase(it);
@@ -545,6 +576,7 @@
     return;
   }
 
+  SiteIsolationPolicy::OnRequestComplete(request_id);
   PendingRequestInfo& request_info = it->second;
   ReleaseResourcesInMessageQueue(&request_info.deferred_message_queue);
   pending_requests_.erase(it);
@@ -592,11 +624,14 @@
 ResourceDispatcher::PendingRequestInfo::PendingRequestInfo(
     webkit_glue::ResourceLoaderBridge::Peer* peer,
     ResourceType::Type resource_type,
+    const GURL& frame_origin,
     const GURL& request_url)
     : peer(peer),
       resource_type(resource_type),
       is_deferred(false),
       url(request_url),
+      frame_origin(frame_origin),
+      response_url(request_url),
       request_start(base::TimeTicks::Now()) {
 }
 
diff --git a/content/child/resource_dispatcher.h b/content/child/resource_dispatcher.h
index 66416f4..465a0a3 100644
--- a/content/child/resource_dispatcher.h
+++ b/content/child/resource_dispatcher.h
@@ -45,6 +45,7 @@
   // requests' ID
   int AddPendingRequest(webkit_glue::ResourceLoaderBridge::Peer* callback,
                         ResourceType::Type resource_type,
+                        const GURL& frame_origin,
                         const GURL& request_url);
 
   // Removes a request from the pending_requests_ list, returning true if the
@@ -85,6 +86,7 @@
 
     PendingRequestInfo(webkit_glue::ResourceLoaderBridge::Peer* peer,
                        ResourceType::Type resource_type,
+                       const GURL& frame_origin,
                        const GURL& request_url);
 
     ~PendingRequestInfo();
@@ -93,7 +95,12 @@
     ResourceType::Type resource_type;
     MessageQueue deferred_message_queue;
     bool is_deferred;
+    // Original requested url.
     GURL url;
+    // The security origin of the frame that initiates this request.
+    GURL frame_origin;
+    // The url of the latest response even in case of redirection.
+    GURL response_url;
     linked_ptr<IPC::Message> pending_redirect_message;
     base::TimeTicks request_start;
     base::TimeTicks response_start;
diff --git a/content/child/resource_dispatcher_unittest.cc b/content/child/resource_dispatcher_unittest.cc
index f537496..d3a33c2 100644
--- a/content/child/resource_dispatcher_unittest.cc
+++ b/content/child/resource_dispatcher_unittest.cc
@@ -176,7 +176,8 @@
     request_info.routing_id = 0;
     RequestExtraData extra_data(WebKit::WebReferrerPolicyDefault,
                                 WebKit::WebString(),
-                                false, true, 0, false, -1, true,
+                                false, true, 0, GURL(),
+                                false, -1, true,
                                 PAGE_TRANSITION_LINK, -1, -1);
     request_info.extra_data = &extra_data;
 
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 25804ae..d1c41c9 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -29,7 +29,10 @@
 #endif  // !defined(GOOGLE_TV)
   bool enable_webaudio = false;
 #if defined(ARCH_CPU_ARMEL)
+  // WebAudio needs Android MediaCodec API that was introduced in
+  // JellyBean, and also currently needs NEON support for the FFT.
   enable_webaudio =
+      (base::android::BuildInfo::GetInstance()->sdk_int() >= 16) &&
       ((android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0);
 #endif  // defined(ARCH_CPU_ARMEL)
   WebRuntimeFeatures::enableWebAudio(enable_webaudio);
@@ -138,6 +141,9 @@
 
   if (command_line.HasSwitch(switches::kEnableOverlayScrollbars))
     WebRuntimeFeatures::enableOverlayScrollbars(true);
+
+  if (command_line.HasSwitch(switches::kEnableInputModeAttribute))
+    WebRuntimeFeatures::enableInputModeAttribute(true);
 }
 
 }  // namespace content
diff --git a/content/child/site_isolation_policy.cc b/content/child/site_isolation_policy.cc
new file mode 100644
index 0000000..91eb158
--- /dev/null
+++ b/content/child/site_isolation_policy.cc
@@ -0,0 +1,561 @@
+// 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 "content/child/site_isolation_policy.h"
+
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "content/public/common/content_switches.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "net/http/http_response_headers.h"
+#include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/platform/WebURL.h"
+#include "third_party/WebKit/public/platform/WebURLRequest.h"
+#include "third_party/WebKit/public/platform/WebURLResponse.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebFrameClient.h"
+#include "third_party/WebKit/public/web/WebSecurityOrigin.h"
+
+using WebKit::WebDocument;
+using WebKit::WebString;
+using WebKit::WebURL;
+using WebKit::WebURLResponse;
+using WebKit::WebURLRequest;
+
+namespace content {
+
+namespace {
+
+// MIME types
+const char kTextHtml[] = "text/html";
+const char kTextXml[] = "text/xml";
+const char xAppRssXml[] = "application/rss+xml";
+const char kAppXml[] = "application/xml";
+const char kAppJson[] = "application/json";
+const char kTextJson[] = "text/json";
+const char kTextXjson[] = "text/x-json";
+const char kTextPlain[] = "text/plain";
+
+}  // anonymous namespace
+
+SiteIsolationPolicy::ResponseMetaData::ResponseMetaData() {}
+
+void SiteIsolationPolicy::OnReceivedResponse(
+    int request_id,
+    GURL& frame_origin,
+    GURL& response_url,
+    ResourceType::Type resource_type,
+    const webkit_glue::ResourceResponseInfo& info) {
+  UMA_HISTOGRAM_COUNTS("SiteIsolation.AllResponses", 1);
+
+  // See if this is for navigation. If it is, don't block it, under the
+  // assumption that we will put it in an appropriate process.
+  if (ResourceType::IsFrame(resource_type))
+    return;
+
+  if (!IsBlockableScheme(response_url))
+    return;
+
+  if (IsSameSite(frame_origin, response_url))
+    return;
+
+  SiteIsolationPolicy::ResponseMetaData::CanonicalMimeType canonical_mime_type =
+      GetCanonicalMimeType(info.mime_type);
+
+  if (canonical_mime_type == SiteIsolationPolicy::ResponseMetaData::Others)
+    return;
+
+  // Every CORS request should have the Access-Control-Allow-Origin header even
+  // if it is preceded by a pre-flight request. Therefore, if this is a CORS
+  // request, it has this header.  response.httpHeaderField() internally uses
+  // case-insensitive matching for the header name.
+  std::string access_control_origin;
+
+  // We can use a case-insensitive header name for EnumerateHeader().
+  info.headers->EnumerateHeader(
+      NULL, "access-control-allow-origin", &access_control_origin);
+  if (IsValidCorsHeaderSet(frame_origin, response_url, access_control_origin))
+    return;
+
+  // Real XSD data collection starts from here.
+  std::string no_sniff;
+  info.headers->EnumerateHeader(NULL, "x-content-type-options", &no_sniff);
+
+  ResponseMetaData resp_data;
+  resp_data.frame_origin = frame_origin.spec();
+  resp_data.response_url = response_url;
+  resp_data.resource_type = resource_type;
+  resp_data.canonical_mime_type = canonical_mime_type;
+  resp_data.http_status_code = info.headers->response_code();
+  resp_data.no_sniff = LowerCaseEqualsASCII(no_sniff, "nosniff");
+
+  RequestIdToMetaDataMap* metadata_map = GetRequestIdToMetaDataMap();
+  (*metadata_map)[request_id] = resp_data;
+}
+
+// These macros are defined here so that we prevent code size bloat-up due to
+// the UMA_HISTOGRAM_* macros. Similar logic is used for recording UMA stats for
+// different MIME types, but we cannot create a helper function for this since
+// UMA_HISTOGRAM_* macros do not accept variables as their bucket names. As a
+// solution, macros are used instead to capture the repeated pattern for
+// recording UMA stats.  TODO(dsjang): this is only needed for collecting UMA
+// stat. Will be deleted when this class is used for actual blocking.
+
+#define SITE_ISOLATION_POLICY_COUNT_BLOCK(BUCKET_PREFIX) \
+    UMA_HISTOGRAM_COUNTS( BUCKET_PREFIX ".Blocked", 1); \
+    result = true;                                      \
+    if (renderable_status_code) { \
+      UMA_HISTOGRAM_ENUMERATION( \
+          BUCKET_PREFIX ".Blocked.RenderableStatusCode", \
+        resp_data.resource_type, \
+        WebURLRequest::TargetIsUnspecified + 1); \
+    } else { \
+      UMA_HISTOGRAM_COUNTS(BUCKET_PREFIX ".Blocked.NonRenderableStatusCode",1);\
+    }
+
+#define SITE_ISOLATION_POLICY_COUNT_NO_SNIFF_BLOCK(BUCKET_PREFIX) \
+    UMA_HISTOGRAM_COUNTS( BUCKET_PREFIX ".NoSniffBlocked", 1); \
+    result = true;  \
+    if (renderable_status_code) { \
+      UMA_HISTOGRAM_ENUMERATION( \
+          BUCKET_PREFIX ".NoSniffBlocked.RenderableStatusCode", \
+        resp_data.resource_type, \
+        WebURLRequest::TargetIsUnspecified + 1); \
+    } else { \
+      UMA_HISTOGRAM_ENUMERATION( \
+          BUCKET_PREFIX ".NoSniffBlocked.NonRenderableStatusCode", \
+        resp_data.resource_type, \
+        WebURLRequest::TargetIsUnspecified + 1); \
+    }
+
+#define SITE_ISOLATION_POLICY_COUNT_NOTBLOCK(BUCKET_PREFIX) \
+    UMA_HISTOGRAM_COUNTS(BUCKET_PREFIX ".NotBlocked", 1); \
+    if (is_sniffed_for_js) \
+      UMA_HISTOGRAM_COUNTS(BUCKET_PREFIX ".NotBlocked.MaybeJS", 1); \
+
+#define SITE_ISOLATION_POLICY_SNIFF_AND_COUNT(SNIFF_EXPR,BUCKET_PREFIX) \
+  if (SNIFF_EXPR) { \
+    SITE_ISOLATION_POLICY_COUNT_BLOCK(BUCKET_PREFIX) \
+  } else { \
+    if (resp_data.no_sniff) { \
+      SITE_ISOLATION_POLICY_COUNT_NO_SNIFF_BLOCK(BUCKET_PREFIX) \
+    } else { \
+      SITE_ISOLATION_POLICY_COUNT_NOTBLOCK(BUCKET_PREFIX) \
+    } \
+  }
+
+bool SiteIsolationPolicy::ShouldBlockResponse(
+    int request_id,
+    const char* data,
+    int length,
+    std::string* alternative_data) {
+  RequestIdToMetaDataMap* metadata_map = GetRequestIdToMetaDataMap();
+  RequestIdToResultMap* result_map = GetRequestIdToResultMap();
+
+  // If there's an entry for |request_id| in blocked_map, this request's first
+  // data packet has already been examined. We can return the result here.
+  if (result_map->count(request_id) != 0) {
+    if ((*result_map)[request_id]) {
+      // Here, the blocking result has been set for the previous run of
+      // ShouldBlockResponse(), so we set alternative data to an empty string so
+      // that ResourceDispatcher doesn't call its peer's onReceivedData() with
+      // the alternative data.
+      alternative_data->erase();
+      return true;
+    }
+    return false;
+  }
+
+  // If result_map doesn't have an entry for |request_id|, we're receiving the
+  // first data packet for request_id. If request_id is not registered, this
+  // request is identified as a non-target of our policy. So we return true.
+  if (metadata_map->count(request_id) == 0) {
+    // We set request_id to true so that we always return true for this request.
+    (*result_map)[request_id] = false;
+    return false;
+  }
+
+  // We now look at the first data packet received for request_id.
+  ResponseMetaData resp_data = (*metadata_map)[request_id];
+  metadata_map->erase(request_id);
+
+  // Record the length of the first received network packet to see if it's
+  // enough for sniffing.
+  UMA_HISTOGRAM_COUNTS("SiteIsolation.XSD.DataLength", length);
+
+  // Record the number of cross-site document responses with a specific mime
+  // type (text/html, text/xml, etc).
+  UMA_HISTOGRAM_ENUMERATION(
+      "SiteIsolation.XSD.MimeType",
+      resp_data.canonical_mime_type,
+      SiteIsolationPolicy::ResponseMetaData::MaxCanonicalMimeType);
+
+  // Store the result of cross-site document blocking analysis. True means we
+  // can return this document to the renderer, false means that we have to block
+  // the response data.
+  bool result = false;
+
+  // The content is blocked if it is sniffed for HTML/JSON/XML. When the blocked
+  // response is with an error status code, it is not disruptive by the
+  // following reasons : 1) the blocked content is not a binary object (such as
+  // an image) since it is sniffed for text; 2) then, this blocking only breaks
+  // the renderer behavior only if it is either JavaScript or CSS. However, the
+  // renderer doesn't use the contents of JS/CSS with unaffected status code
+  // (e.g, 404). 3) the renderer is expected not to use the cross-site document
+  // content for purposes other than JS/CSS (e.g, XHR).
+  bool renderable_status_code = IsRenderableStatusCodeForDocument(
+      resp_data.http_status_code);
+
+  // This is only used for false-negative analysis for non-blocked resources.
+  bool is_sniffed_for_js = SniffForJS(data, length);
+
+  // Record the number of responses whose content is sniffed for what its mime
+  // type claims it to be. For example, we apply a HTML sniffer for a document
+  // tagged with text/html here. Whenever this check becomes true, we'll block
+  // the response.
+  switch (resp_data.canonical_mime_type) {
+    case SiteIsolationPolicy::ResponseMetaData::HTML:
+      SITE_ISOLATION_POLICY_SNIFF_AND_COUNT(SniffForHTML(data, length),
+                                            "SiteIsolation.XSD.HTML");
+      break;
+    case SiteIsolationPolicy::ResponseMetaData::XML:
+      SITE_ISOLATION_POLICY_SNIFF_AND_COUNT(SniffForXML(data, length),
+                                            "SiteIsolation.XSD.XML");
+      break;
+    case SiteIsolationPolicy::ResponseMetaData::JSON:
+      SITE_ISOLATION_POLICY_SNIFF_AND_COUNT(SniffForJSON(data, length),
+                                            "SiteIsolation.XSD.JSON");
+      break;
+    case SiteIsolationPolicy::ResponseMetaData::Plain:
+      if (SniffForHTML(data, length)) {
+        SITE_ISOLATION_POLICY_COUNT_BLOCK(
+            "SiteIsolation.XSD.Plain.HTML");
+      } else if (SniffForXML(data, length)) {
+        SITE_ISOLATION_POLICY_COUNT_BLOCK(
+            "SiteIsolation.XSD.Plain.XML");
+      } else if (SniffForJSON(data, length)) {
+        SITE_ISOLATION_POLICY_COUNT_BLOCK(
+            "SiteIsolation.XSD.Plain.JSON");
+      } else if (is_sniffed_for_js) {
+        if (resp_data.no_sniff) {
+          SITE_ISOLATION_POLICY_COUNT_NO_SNIFF_BLOCK(
+              "SiteIsolation.XSD.Plain");
+        } else {
+          SITE_ISOLATION_POLICY_COUNT_NOTBLOCK(
+              "SiteIsolation.XSD.Plain");
+        }
+      }
+      break;
+    default :
+      NOTREACHED() <<
+          "Not a blockable mime type. This mime type shouldn't reach here.";
+      break;
+  }
+
+  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+  if (!command_line.HasSwitch(switches::kBlockCrossSiteDocuments))
+    result = false;
+  (*result_map)[request_id] = result;
+
+  if (result) {
+    alternative_data->erase();
+    alternative_data->insert(0, " ");
+    LOG(ERROR) << resp_data.response_url
+               << " is blocked as an illegal cross-site document from "
+               << resp_data.frame_origin;
+
+  }
+  return result;
+}
+
+#undef SITE_ISOLATION_POLICY_COUNT_NOTBLOCK
+#undef SITE_ISOLATION_POLICY_SNIFF_AND_COUNT
+#undef SITE_ISOLATION_POLICY_COUNT_BLOCK
+
+void SiteIsolationPolicy::OnRequestComplete(int request_id) {
+  RequestIdToMetaDataMap* metadata_map = GetRequestIdToMetaDataMap();
+  RequestIdToResultMap* result_map = GetRequestIdToResultMap();
+  metadata_map->erase(request_id);
+  result_map->erase(request_id);
+}
+
+SiteIsolationPolicy::ResponseMetaData::CanonicalMimeType
+SiteIsolationPolicy::GetCanonicalMimeType(const std::string& mime_type) {
+  if (LowerCaseEqualsASCII(mime_type, kTextHtml)) {
+    return SiteIsolationPolicy::ResponseMetaData::HTML;
+  }
+
+  if (LowerCaseEqualsASCII(mime_type, kTextPlain)) {
+    return SiteIsolationPolicy::ResponseMetaData::Plain;
+  }
+
+  if (LowerCaseEqualsASCII(mime_type, kAppJson) ||
+      LowerCaseEqualsASCII(mime_type, kTextJson) ||
+      LowerCaseEqualsASCII(mime_type, kTextXjson)) {
+    return SiteIsolationPolicy::ResponseMetaData::JSON;
+  }
+
+  if (LowerCaseEqualsASCII(mime_type, kTextXml) ||
+      LowerCaseEqualsASCII(mime_type, xAppRssXml) ||
+      LowerCaseEqualsASCII(mime_type, kAppXml)) {
+    return SiteIsolationPolicy::ResponseMetaData::XML;
+  }
+
+ return SiteIsolationPolicy::ResponseMetaData::Others;
+
+}
+
+bool SiteIsolationPolicy::IsBlockableScheme(const GURL& url) {
+  // We exclude ftp:// from here. FTP doesn't provide a Content-Type
+  // header which our policy depends on, so we cannot protect any
+  // document from FTP servers.
+  return url.SchemeIs("http") || url.SchemeIs("https");
+}
+
+bool SiteIsolationPolicy::IsSameSite(const GURL& frame_origin,
+                                     const GURL& response_url) {
+
+  if (!frame_origin.is_valid() || !response_url.is_valid())
+    return false;
+
+  if (frame_origin.scheme() != response_url.scheme())
+    return false;
+
+  // SameDomainOrHost() extracts the effective domains (public suffix plus one)
+  // from the two URLs and compare them.
+  // TODO(dsjang): use INCLUDE_PRIVATE_REGISTRIES when http://crbug.com/7988 is
+  // fixed.
+  return net::registry_controlled_domains::SameDomainOrHost(
+      frame_origin,
+      response_url,
+      net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
+}
+
+bool SiteIsolationPolicy::IsFrameNavigating(WebKit::WebFrame* frame) {
+  // When a navigation starts, frame->provisionalDataSource() is set
+  // to a not-null value which stands for the request made for the
+  // navigation. As soon as the network request is committed to the
+  // frame, frame->provisionalDataSource() is converted to null, and
+  // the committed data source is moved to frame->dataSource(). This
+  // is the most reliable way to detect whether the frame is in
+  // navigation or not.
+  return frame->provisionalDataSource() != NULL;
+}
+
+// We don't use Webkit's existing CORS policy implementation since
+// their policy works in terms of origins, not sites. For example,
+// when frame is sub.a.com and it is not allowed to access a document
+// with sub1.a.com. But under Site Isolation, it's allowed.
+bool SiteIsolationPolicy::IsValidCorsHeaderSet(
+    GURL& frame_origin,
+    GURL& website_origin,
+    std::string access_control_origin) {
+  // Many websites are sending back "\"*\"" instead of "*". This is
+  // non-standard practice, and not supported by Chrome. Refer to
+  // CrossOriginAccessControl::passesAccessControlCheck().
+
+  // TODO(dsjang): * is not allowed for the response from a request
+  // with cookies. This allows for more than what the renderer will
+  // eventually be able to receive, so we won't see illegal cross-site
+  // documents allowed by this. We have to find a way to see if this
+  // response is from a cookie-tagged request or not in the future.
+  if (access_control_origin == "*")
+    return true;
+
+  // TODO(dsjang): The CORS spec only treats a fully specified URL, except for
+  // "*", but many websites are using just a domain for access_control_origin,
+  // and this is blocked by Webkit's CORS logic here :
+  // CrossOriginAccessControl::passesAccessControlCheck(). GURL is set
+  // is_valid() to false when it is created from a URL containing * in the
+  // domain part.
+
+  GURL cors_origin(access_control_origin);
+  return IsSameSite(frame_origin, cors_origin);
+}
+
+// This function is a slight modification of |net::SniffForHTML|.
+bool SiteIsolationPolicy::SniffForHTML(const char* data, size_t length) {
+  // The content sniffer used by Chrome and Firefox are using "<!--"
+  // as one of the HTML signatures, but it also appears in valid
+  // JavaScript, considered as well-formed JS by the browser.  Since
+  // we do not want to block any JS, we exclude it from our HTML
+  // signatures. This can weaken our document block policy, but we can
+  // break less websites.
+  // TODO(dsjang): parameterize |net::SniffForHTML| with an option
+  // that decides whether to include <!-- or not, so that we can
+  // remove this function.
+  const char* html_signatures[] = {"<!DOCTYPE html",  // HTML5 spec
+                                   "<script",         // HTML5 spec, Mozilla
+                                   "<html",           // HTML5 spec, Mozilla
+                                   "<head",           // HTML5 spec, Mozilla
+                                   "<iframe",         // Mozilla
+                                   "<h1",             // Mozilla
+                                   "<div",            // Mozilla
+                                   "<font",           // Mozilla
+                                   "<table",          // Mozilla
+                                   "<a",              // Mozilla
+                                   "<style",          // Mozilla
+                                   "<title",          // Mozilla
+                                   "<b",              // Mozilla
+                                   "<body",           // Mozilla
+                                   "<br", "<p",       // Mozilla
+                                   "<?xml"            // Mozilla
+  };
+
+  if (MatchesSignature(
+          data, length, html_signatures, arraysize(html_signatures)))
+    return true;
+
+  // "<!--" is specially treated since web JS can use "<!--" "-->" pair for
+  // comments.
+  static const char* comment_begins[] = {"<!--"};
+
+  if (MatchesSignature(
+          data, length, comment_begins, arraysize(comment_begins))) {
+    // Search for --> and do SniffForHTML after that. If we can find the
+    // comment's end, we start HTML sniffing from there again.
+    static const char end_comment[] = "-->";
+    base::StringPiece data_as_stringpiece(data, length);
+
+    size_t offset = data_as_stringpiece.find(end_comment);
+    if (offset != base::StringPiece::npos) {
+      size_t new_start_offset = offset + strlen(end_comment);
+      if (new_start_offset < length) {
+        return SniffForHTML(data + new_start_offset,
+                            length - new_start_offset);
+      }
+    }
+  }
+
+  return false;
+}
+
+bool SiteIsolationPolicy::SniffForXML(const char* data, size_t length) {
+  // TODO(dsjang): Chrome's mime_sniffer is using strncasecmp() for
+  // this signature. However, XML is case-sensitive. Don't we have to
+  // be more lenient only to block documents starting with the exact
+  // string <?xml rather than <?XML ?
+  const char* xml_signatures[] = {"<?xml"  // Mozilla
+  };
+  return MatchesSignature(
+      data, length, xml_signatures, arraysize(xml_signatures));
+}
+
+bool SiteIsolationPolicy::SniffForJSON(const char* data, size_t length) {
+  // TODO(dsjang): We have to come up with a better way to sniff
+  // JSON. However, even RE cannot help us that much due to the fact
+  // that we don't do full parsing.  This DFA starts with state 0, and
+  // finds {, "/' and : in that order. We're avoiding adding a
+  // dependency on a regular expression library.
+  const int kInitState = 0;
+  const int kLeftBraceState = 1;
+  const int kLeftQuoteState = 2;
+  const int kColonState = 3;
+  const int kDeadState = 4;
+
+  int state = kInitState;
+  for (size_t i = 0; i < length && state < kColonState; ++i) {
+    const char c = data[i];
+    if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
+      continue;
+
+    switch (state) {
+      case kInitState:
+        if (c == '{')
+          state = kLeftBraceState;
+        else
+          state = kDeadState;
+        break;
+      case kLeftBraceState:
+        if (c == '\"' || c == '\'')
+          state = kLeftQuoteState;
+        else
+          state = kDeadState;
+        break;
+      case kLeftQuoteState:
+        if (c == ':')
+          state = kColonState;
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+  }
+  return state == kColonState;
+}
+
+bool SiteIsolationPolicy::MatchesSignature(const char* raw_data,
+                                           size_t raw_length,
+                                           const char* signatures[],
+                                           size_t arr_size) {
+  size_t start = 0;
+  // Skip white characters at the beginning of the document.
+  for (start = 0; start < raw_length; ++start) {
+    char c = raw_data[start];
+    if (!(c == ' ' || c == '\t' || c == '\r' || c == '\n'))
+      break;
+  }
+
+  // There is no not-whitespace character in this document.
+  if (!(start < raw_length))
+    return false;
+
+  const char* data = raw_data + start;
+  size_t length = raw_length - start;
+
+  for (size_t sig_index = 0; sig_index < arr_size; ++sig_index) {
+    const char* signature = signatures[sig_index];
+    size_t signature_length = strlen(signature);
+
+    if (length < signature_length)
+      continue;
+
+    if (!base::strncasecmp(signature, data, signature_length))
+      return true;
+  }
+  return false;
+}
+
+bool SiteIsolationPolicy::IsRenderableStatusCodeForDocument(int status_code) {
+  // Chrome only uses the content of a response with one of these status codes
+  // for CSS/JavaScript. For images, Chrome just ignores status code.
+  const int renderable_status_code[] = {200, 201, 202, 203, 206, 300, 301, 302,
+                                        303, 305, 306, 307};
+  for (size_t i = 0; i < arraysize(renderable_status_code); ++i) {
+    if (renderable_status_code[i] == status_code)
+      return true;
+  }
+  return false;
+}
+
+bool SiteIsolationPolicy::SniffForJS(const char* data, size_t length) {
+  // TODO(dsjang): This is a real hack. The only purpose of this function is to
+  // try to see if there's any possibility that this data can be JavaScript
+  // (superset of JS). This function will be removed once UMA stats are
+  // gathered.
+
+  // Search for "var " for JS detection.
+  return base::StringPiece(data, length).find("var ") !=
+    base::StringPiece::npos;
+}
+
+SiteIsolationPolicy::RequestIdToMetaDataMap*
+SiteIsolationPolicy::GetRequestIdToMetaDataMap() {
+  CR_DEFINE_STATIC_LOCAL(RequestIdToMetaDataMap, metadata_map_, ());
+  return &metadata_map_;
+}
+
+SiteIsolationPolicy::RequestIdToResultMap*
+SiteIsolationPolicy::GetRequestIdToResultMap() {
+  CR_DEFINE_STATIC_LOCAL(RequestIdToResultMap, result_map_, ());
+  return &result_map_;
+}
+
+}  // namespace content
diff --git a/content/child/site_isolation_policy.h b/content/child/site_isolation_policy.h
new file mode 100644
index 0000000..bf816a9
--- /dev/null
+++ b/content/child/site_isolation_policy.h
@@ -0,0 +1,179 @@
+// 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 CONTENT_CHILD_SITE_ISOLATION_POLICY_H_
+#define CONTENT_CHILD_SITE_ISOLATION_POLICY_H_
+
+#include <map>
+#include <utility>
+
+#include "base/gtest_prod_util.h"
+#include "content/common/content_export.h"
+#include "third_party/WebKit/public/platform/WebURLRequest.h"
+#include "third_party/WebKit/public/platform/WebURLResponse.h"
+#include "third_party/WebKit/public/web/WebFrame.h"
+#include "webkit/common/resource_response_info.h"
+#include "webkit/common/resource_type.h"
+
+namespace content {
+
+// SiteIsolationPolicy implements the cross-site document blocking policy (XSDP)
+// for Site Isolation. XSDP will monitor network responses to a renderer and
+// block illegal responses so that a compromised renderer cannot steal private
+// information from other sites. For now SiteIsolationPolicy monitors responses
+// to gather various UMA stats to see the compatibility impact of actual
+// deployment of the policy. The UMA stat categories SiteIsolationPolicy gathers
+// are as follows:
+//
+// SiteIsolation.AllResponses : # of all network responses.
+// SiteIsolation.XSD.DataLength : the length of the first packet of a response.
+// SiteIsolation.XSD.MimeType (enum):
+//   # of responses from other sites, tagged with a document mime type.
+//   0:HTML, 1:XML, 2:JSON, 3:Plain, 4:Others
+// SiteIsolation.XSD.[%MIMETYPE].Blocked :
+//   blocked # of cross-site document responses grouped by sniffed MIME type.
+// SiteIsolation.XSD.[%MIMETYPE].Blocked.RenderableStatusCode :
+//   # of responses with renderable status code,
+//   out of SiteIsolation.XSD.[%MIMETYPE].Blocked.
+// SiteIsolation.XSD.[%MIMETYPE].Blocked.NonRenderableStatusCode :
+//   # of responses with non-renderable status code,
+//   out of SiteIsolation.XSD.[%MIMETYPE].Blocked.
+// SiteIsolation.XSD.[%MIMETYPE].NoSniffBlocked.RenderableStatusCode :
+//   # of responses failed to be sniffed for its MIME type, but blocked by
+//   "X-Content-Type-Options: nosniff" header, and with renderable status code
+//   out of SiteIsolation.XSD.[%MIMETYPE].Blocked.
+// SiteIsolation.XSD.[%MIMETYPE].NoSniffBlocked.NonRenderableStatusCode :
+//   # of responses failed to be sniffed for its MIME type, but blocked by
+//   "X-Content-Type-Options: nosniff" header, and with non-renderable status
+//   code out of SiteIsolation.XSD.[%MIMETYPE].Blocked.
+// SiteIsolation.XSD.[%MIMETYPE].NotBlocked :
+//   # of responses, but not blocked due to failure of mime sniffing.
+// SiteIsolation.XSD.[%MIMETYPE].NotBlocked.MaybeJS :
+//   # of responses that are plausibly sniffed to be JavaScript.
+
+class CONTENT_EXPORT SiteIsolationPolicy {
+ public:
+
+  // Records the bookkeeping data about the HTTP header information for the
+  // request identified by |request_id|. The bookkeeping data is used by
+  // ShouldBlockResponse. We have to make sure to call OnRequestComplete to free
+  // the bookkeeping data.
+  static void OnReceivedResponse(int request_id,
+                                 GURL& frame_origin,
+                                 GURL& response_url,
+                                 ResourceType::Type resource_type,
+                                 const webkit_glue::ResourceResponseInfo& info);
+
+  // Examines the first network packet in case response_url is registered as a
+  // cross-site document by DidReceiveResponse().  In case that this response is
+  // blocked, it returns an alternative data to be sent to the renderer in
+  // |alternative_data|. This records various kinds of UMA data stats. This
+  // function is called only if the length of received data is non-zero.
+  static bool ShouldBlockResponse(int request_id,
+                                  const char* payload,
+                                  int length,
+                                  std::string* alternative_data);
+
+  // Clean up booking data registered by OnReceiveResponse and OnReceivedData.
+  static void OnRequestComplete(int request_id);
+
+  struct ResponseMetaData {
+
+    enum CanonicalMimeType {
+      HTML = 0,
+      XML = 1,
+      JSON = 2,
+      Plain = 3,
+      Others = 4,
+      MaxCanonicalMimeType,
+    };
+
+    ResponseMetaData();
+
+    std::string frame_origin;
+    GURL response_url;
+    ResourceType::Type resource_type;
+    CanonicalMimeType canonical_mime_type;
+    int http_status_code;
+    bool no_sniff;
+  };
+
+  typedef std::map<int, ResponseMetaData> RequestIdToMetaDataMap;
+  typedef std::map<int, bool> RequestIdToResultMap;
+
+private:
+  FRIEND_TEST_ALL_PREFIXES(SiteIsolationPolicyTest, IsBlockableScheme);
+  FRIEND_TEST_ALL_PREFIXES(SiteIsolationPolicyTest, IsSameSite);
+  FRIEND_TEST_ALL_PREFIXES(SiteIsolationPolicyTest, IsValidCorsHeaderSet);
+  FRIEND_TEST_ALL_PREFIXES(SiteIsolationPolicyTest, SniffForHTML);
+  FRIEND_TEST_ALL_PREFIXES(SiteIsolationPolicyTest, SniffForXML);
+  FRIEND_TEST_ALL_PREFIXES(SiteIsolationPolicyTest, SniffForJSON);
+  FRIEND_TEST_ALL_PREFIXES(SiteIsolationPolicyTest, SniffForJS);
+
+  // Returns the representative mime type enum value of the mime type of
+  // response. For example, this returns the same value for all text/xml mime
+  // type families such as application/xml, application/rss+xml.
+  static ResponseMetaData::CanonicalMimeType GetCanonicalMimeType(
+      const std::string& mime_type);
+
+  // Returns whether this scheme is a target of cross-site document
+  // policy(XSDP). This returns true only for http://* and https://* urls.
+  static bool IsBlockableScheme(const GURL& frame_origin);
+
+  // Returns whether the two urls belong to the same sites.
+  static bool IsSameSite(const GURL& frame_origin, const GURL& response_url);
+
+  // Returns whether there's a valid CORS header for frame_origin.  This is
+  // simliar to CrossOriginAccessControl::passesAccessControlCheck(), but we use
+  // sites as our security domain, not origins.
+  // TODO(dsjang): this must be improved to be more accurate to the actual CORS
+  // specification. For now, this works conservatively, allowing XSDs that are
+  // not allowed by actual CORS rules by ignoring 1) credentials and 2)
+  // methods. Preflight requests don't matter here since they are not used to
+  // decide whether to block a document or not on the client side.
+  static bool IsValidCorsHeaderSet(GURL& frame_origin,
+                                   GURL& website_origin,
+                                   std::string access_control_origin);
+
+  // Returns whether the given frame is navigating. When this is true, the frame
+  // is requesting is a web page to be loaded.
+  static bool IsFrameNavigating(WebKit::WebFrame* frame);
+
+  static bool SniffForHTML(const char* data, size_t length);
+  static bool SniffForXML(const char* data, size_t length);
+  static bool SniffForJSON(const char* data, size_t length);
+
+  static bool MatchesSignature(const char* data,
+                               size_t length,
+                               const char* signatures[],
+                               size_t arr_size);
+
+  // TODO(dsjang): this is only needed for collecting UMA stat. Will be deleted
+  // when this class is used for actual blocking.
+  static bool SniffForJS(const char* data, size_t length);
+
+  // TODO(dsjang): this is only needed for collecting UMA stat. Will be deleted
+  // when this class is used for actual blocking.
+  static bool IsRenderableStatusCodeForDocument(int status_code);
+
+  // Maintain the bookkeeping data between OnReceivedResponse and
+  // OnReceivedData. The key is a request id maintained by ResourceDispatcher.
+  static RequestIdToMetaDataMap* GetRequestIdToMetaDataMap();
+
+  // Maintain the bookkeeping data for OnReceivedData. Blocking decision is made
+  // when OnReceivedData is called for the first time for a request, and the
+  // decision will remain the same for following data. This map maintains the
+  // decision. The key is a request id maintained by ResourceDispatcher.
+  static RequestIdToResultMap* GetRequestIdToResultMap();
+
+  // Never needs to be constructed/destructed.
+  SiteIsolationPolicy() {}
+  ~SiteIsolationPolicy() {}
+
+  DISALLOW_COPY_AND_ASSIGN(SiteIsolationPolicy);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_CHILD_SITE_ISOLATION_POLICY_H_
diff --git a/content/child/site_isolation_policy_browsertest.cc b/content/child/site_isolation_policy_browsertest.cc
new file mode 100644
index 0000000..15cd06e
--- /dev/null
+++ b/content/child/site_isolation_policy_browsertest.cc
@@ -0,0 +1,106 @@
+// 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 "base/command_line.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/test/content_browser_test.h"
+#include "content/test/content_browser_test_utils.h"
+
+namespace content {
+
+// These tests simulate exploited renderer processes, which can fetch arbitrary
+// resources from other websites, not constrained by the Same Origin Policy.  We
+// are trying to verify that the renderer cannot fetch any cross-site document
+// responses even when the Same Origin Policy is turned off inside the renderer.
+class SiteIsolationPolicyBrowserTest : public ContentBrowserTest {
+ public:
+  SiteIsolationPolicyBrowserTest() {}
+  virtual ~SiteIsolationPolicyBrowserTest() {}
+
+  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+    ASSERT_TRUE(test_server()->Start());
+    net::SpawnedTestServer https_server(
+        net::SpawnedTestServer::TYPE_HTTPS,
+        net::SpawnedTestServer::kLocalhost,
+        base::FilePath(FILE_PATH_LITERAL("content/test/data")));
+    ASSERT_TRUE(https_server.Start());
+
+    // Add a host resolver rule to map all outgoing requests to the test server.
+    // This allows us to use "real" hostnames in URLs, which we can use to
+    // create arbitrary SiteInstances.
+    command_line->AppendSwitchASCII(
+        switches::kHostResolverRules,
+        "MAP * " + test_server()->host_port_pair().ToString() +
+            ",EXCLUDE localhost");
+
+    // Since we assume exploited renderer process, it can bypass the same origin
+    // policy at will. Simulate that by passing the disable-web-security flag.
+    command_line->AppendSwitch(switches::kDisableWebSecurity);
+
+    // We assume that we're using our cross-site document blocking logic which
+    // is turned on even when the Same Origin Policy is turned off.
+    command_line->AppendSwitch(switches::kBlockCrossSiteDocuments);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SiteIsolationPolicyBrowserTest);
+};
+
+// TODO(dsjang): we cannot run these tests on Android since SetUpCommandLine()
+// is executed before the I/O thread is created on Android. After this bug
+// (crbug.com/278425) is resolved, we can enable this test case on Android.
+#if defined(OS_ANDROID)
+#define MAYBE_CrossSiteDocumentBlockingForMimeType \
+  DISABLED_CrossSiteDocumentBlockingForMimeType
+#else
+#define MAYBE_CrossSiteDocumentBlockingForMimeType \
+  CrossSiteDocumentBlockingForMimeType
+#endif
+
+IN_PROC_BROWSER_TEST_F(SiteIsolationPolicyBrowserTest,
+                       MAYBE_CrossSiteDocumentBlockingForMimeType) {
+  // Load a page that issues illegal cross-site document requests to bar.com.
+  // The page uses XHR to request HTML/XML/JSON documents from bar.com, and
+  // inspects if any of them were successfully received. The XHR requests will
+  // get a one character string ' ' for a blocked response. This test is only
+  // possible since we run the browser without the same origin policy.
+  GURL foo("http://foo.com/files/cross_site_document_request.html");
+
+  content::DOMMessageQueue msg_queue;
+
+  NavigateToURL(shell(), foo);
+
+  std::string status;
+  // The page will return 1 from the DOMAutomationController if it succeeds,
+  // otherwise it will return 0.
+  std::string expected_status("1");
+  EXPECT_TRUE(msg_queue.WaitForMessage(&status));
+  EXPECT_STREQ(status.c_str(), expected_status.c_str());
+}
+
+// TODO(dsjang): we cannot run these tests on Android since SetUpCommandLine()
+// is executed before the I/O thread is created on Android. After this bug
+// (crbug.com/278425) is resolved, we can enable this test case on Android.
+#if defined(OS_ANDROID)
+#define MAYBE_CrossSiteDocumentBlockingForDifferentTargets \
+  DISABLED_CrossSiteDocumentBlockingForDifferentTargets
+#else
+#define MAYBE_CrossSiteDocumentBlockingForDifferentTargets \
+  CrossSiteDocumentBlockingForDifferentTargets
+#endif
+
+IN_PROC_BROWSER_TEST_F(SiteIsolationPolicyBrowserTest,
+                       MAYBE_CrossSiteDocumentBlockingForDifferentTargets) {
+  // This webpage loads a cross-site HTML page in different targets such as
+  // <img>,<link>,<embed>, etc. Since the requested document is blocked, and one
+  // character string (' ') is returned instead, this tests that the renderer
+  // does not crash even when it receives a response body which is " ", whose
+  // length is different from what's described in "content-length" for such
+  // different targets.
+  GURL foo("http://foo.com/files/cross_site_document_request_target.html");
+  NavigateToURL(shell(), foo);
+}
+
+}
diff --git a/content/child/site_isolation_policy_unittest.cc b/content/child/site_isolation_policy_unittest.cc
new file mode 100644
index 0000000..2a92ba4
--- /dev/null
+++ b/content/child/site_isolation_policy_unittest.cc
@@ -0,0 +1,137 @@
+// 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 "base/strings/utf_string_conversions.h"
+#include "content/child/site_isolation_policy.h"
+#include "content/public/common/context_menu_params.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebURLResponse.h"
+#include "ui/base/range/range.h"
+
+namespace content {
+
+TEST(SiteIsolationPolicyTest, IsBlockableScheme) {
+  GURL data_url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA==");
+  GURL ftp_url("ftp://google.com");
+  GURL mailto_url("mailto:google@google.com");
+  GURL about_url("about:chrome");
+  GURL http_url("http://google.com");
+  GURL https_url("https://google.com");
+
+  EXPECT_FALSE(SiteIsolationPolicy::IsBlockableScheme(data_url));
+  EXPECT_FALSE(SiteIsolationPolicy::IsBlockableScheme(ftp_url));
+  EXPECT_FALSE(SiteIsolationPolicy::IsBlockableScheme(mailto_url));
+  EXPECT_FALSE(SiteIsolationPolicy::IsBlockableScheme(about_url));
+  EXPECT_TRUE(SiteIsolationPolicy::IsBlockableScheme(http_url));
+  EXPECT_TRUE(SiteIsolationPolicy::IsBlockableScheme(https_url));
+}
+
+TEST(SiteIsolationPolicyTest, IsSameSite) {
+  GURL a_com_url0("https://mock1.a.com:8080/page1.html");
+  GURL a_com_url1("https://mock2.a.com:9090/page2.html");
+  GURL a_com_url2("https://a.com/page3.html");
+  EXPECT_TRUE(SiteIsolationPolicy::IsSameSite(a_com_url0, a_com_url1));
+  EXPECT_TRUE(SiteIsolationPolicy::IsSameSite(a_com_url1, a_com_url2));
+  EXPECT_TRUE(SiteIsolationPolicy::IsSameSite(a_com_url2, a_com_url0));
+
+  GURL b_com_url0("https://mock1.b.com/index.html");
+  EXPECT_FALSE(SiteIsolationPolicy::IsSameSite(a_com_url0, b_com_url0));
+
+  GURL about_blank_url("about:blank");
+  EXPECT_FALSE(SiteIsolationPolicy::IsSameSite(a_com_url0, about_blank_url));
+
+  GURL chrome_url("chrome://extension");
+  EXPECT_FALSE(SiteIsolationPolicy::IsSameSite(a_com_url0, chrome_url));
+
+  GURL empty_url("");
+  EXPECT_FALSE(SiteIsolationPolicy::IsSameSite(a_com_url0, empty_url));
+}
+
+TEST(SiteIsolationPolicyTest, IsValidCorsHeaderSet) {
+  GURL frame_origin("http://www.google.com");
+  GURL site_origin("http://www.yahoo.com");
+
+  EXPECT_TRUE(SiteIsolationPolicy::IsValidCorsHeaderSet(
+      frame_origin, site_origin, "*"));
+  EXPECT_FALSE(SiteIsolationPolicy::IsValidCorsHeaderSet(
+      frame_origin, site_origin, "\"*\""));
+  EXPECT_TRUE(SiteIsolationPolicy::IsValidCorsHeaderSet(
+      frame_origin, site_origin, "http://mail.google.com"));
+  EXPECT_FALSE(SiteIsolationPolicy::IsValidCorsHeaderSet(
+      frame_origin, site_origin, "https://mail.google.com"));
+  EXPECT_FALSE(SiteIsolationPolicy::IsValidCorsHeaderSet(
+      frame_origin, site_origin, "http://yahoo.com"));
+  EXPECT_FALSE(SiteIsolationPolicy::IsValidCorsHeaderSet(
+      frame_origin, site_origin, "www.google.com"));
+}
+
+TEST(SiteIsolationPolicyTest, SniffForHTML) {
+  const char html_data[] = "  \t\r\n    <HtMladfokadfkado";
+  const char comment_html_data[] = " <!-- this is comment --> <html><body>";
+  const char two_comments_html_data[] =
+      "<!-- this is comment -->\n<!-- this is comment --><html><body>";
+  const char mixed_comments_html_data[] =
+      "<!-- this is comment <!-- --> <script></script>";
+  const char non_html_data[] = "        var name=window.location;\nadfadf";
+  const char comment_js_data[] = " <!-- this is comment -> document.write(1); ";
+
+  EXPECT_TRUE(
+      SiteIsolationPolicy::SniffForHTML(html_data, arraysize(html_data)));
+  EXPECT_TRUE(SiteIsolationPolicy::SniffForHTML(comment_html_data,
+                                                arraysize(comment_html_data)));
+  EXPECT_TRUE(SiteIsolationPolicy::SniffForHTML(
+      two_comments_html_data, arraysize(two_comments_html_data)));
+  EXPECT_TRUE(SiteIsolationPolicy::SniffForHTML(
+      mixed_comments_html_data, arraysize(mixed_comments_html_data)));
+  EXPECT_FALSE(SiteIsolationPolicy::SniffForHTML(non_html_data,
+                                                 arraysize(non_html_data)));
+  EXPECT_FALSE(SiteIsolationPolicy::SniffForHTML(comment_js_data,
+                                                 arraysize(comment_js_data)));
+
+  // Basic bounds check.
+  EXPECT_FALSE(SiteIsolationPolicy::SniffForHTML(html_data, 0));
+}
+
+TEST(SiteIsolationPolicyTest, SniffForXML) {
+  const char xml_data[] = "   \t \r \n     <?xml version=\"1.0\"?>\n <catalog";
+  const char non_xml_data[] = "        var name=window.location;\nadfadf";
+
+  EXPECT_TRUE(SiteIsolationPolicy::SniffForXML(xml_data, arraysize(xml_data)));
+  EXPECT_FALSE(
+      SiteIsolationPolicy::SniffForXML(non_xml_data, arraysize(non_xml_data)));
+
+  // Basic bounds check.
+  EXPECT_FALSE(SiteIsolationPolicy::SniffForXML(xml_data, 0));
+}
+
+TEST(SiteIsolationPolicyTest, SniffForJSON) {
+  const char json_data[] = "\t\t\r\n   { \"name\" : \"chrome\", ";
+  const char non_json_data0[] = "\t\t\r\n   { name : \"chrome\", ";
+  const char non_json_data1[] = "\t\t\r\n   foo({ \"name\" : \"chrome\", ";
+
+  EXPECT_TRUE(
+      SiteIsolationPolicy::SniffForJSON(json_data, arraysize(json_data)));
+  EXPECT_FALSE(SiteIsolationPolicy::SniffForJSON(non_json_data0,
+                                                 arraysize(non_json_data0)));
+  EXPECT_FALSE(SiteIsolationPolicy::SniffForJSON(non_json_data1,
+                                                 arraysize(non_json_data1)));
+
+  // Basic bounds check.
+  EXPECT_FALSE(SiteIsolationPolicy::SniffForJSON(json_data, 0));
+}
+
+TEST(SiteIsolationPolicyTest, SniffForJS) {
+  const char basic_js_data[] = "var a = 4";
+  const char js_data[] = "\t\t\r\n var a = 4";
+  const char json_data[] = "\t\t\r\n   { \"name\" : \"chrome\", ";
+
+  EXPECT_TRUE(SiteIsolationPolicy::SniffForJS(js_data, arraysize(js_data)));
+  EXPECT_FALSE(
+      SiteIsolationPolicy::SniffForJS(json_data, arraysize(json_data)));
+
+  // Basic bounds check.
+  EXPECT_FALSE(SiteIsolationPolicy::SniffForJS(basic_js_data, 0));
+}
+
+}  // namespace content
diff --git a/content/child/webblobregistry_impl.cc b/content/child/webblobregistry_impl.cc
index a51f4ec..421de95 100644
--- a/content/child/webblobregistry_impl.cc
+++ b/content/child/webblobregistry_impl.cc
@@ -189,6 +189,11 @@
   sender_->Send(new StreamHostMsg_FinishBuilding(url));
 }
 
+void WebBlobRegistryImpl::abortStream(const WebURL& url) {
+  DCHECK(ChildThread::current());
+  sender_->Send(new StreamHostMsg_AbortBuilding(url));
+}
+
 void WebBlobRegistryImpl::unregisterStreamURL(const WebURL& url) {
   DCHECK(ChildThread::current());
   sender_->Send(new StreamHostMsg_Remove(url));
diff --git a/content/child/webblobregistry_impl.h b/content/child/webblobregistry_impl.h
index ddc7a02..87afcdb 100644
--- a/content/child/webblobregistry_impl.h
+++ b/content/child/webblobregistry_impl.h
@@ -37,6 +37,7 @@
   virtual void addDataToStream(const WebKit::WebURL& url,
                                WebKit::WebThreadSafeData& data);
   virtual void finalizeStream(const WebKit::WebURL& url);
+  virtual void abortStream(const WebKit::WebURL& url);
   virtual void unregisterStreamURL(const WebKit::WebURL& url);
 
  private:
diff --git a/content/common/android/surface_texture_peer.h b/content/common/android/surface_texture_peer.h
index 9be032d..03113b8 100644
--- a/content/common/android/surface_texture_peer.h
+++ b/content/common/android/surface_texture_peer.h
@@ -6,7 +6,7 @@
 #define CONTENT_COMMON_ANDROID_SURFACE_TEXTURE_PEER_H_
 
 #include "base/process/process.h"
-#include "ui/gl/android/surface_texture_bridge.h"
+#include "ui/gl/android/surface_texture.h"
 
 namespace content {
 
@@ -20,7 +20,7 @@
   // process.
   virtual void EstablishSurfaceTexturePeer(
       base::ProcessHandle pid,
-      scoped_refptr<gfx::SurfaceTextureBridge> surface_texture_bridge,
+      scoped_refptr<gfx::SurfaceTexture> surface_texture,
       int primary_id,
       int secondary_id) = 0;
 
diff --git a/content/common/fileapi/webblob_messages.h b/content/common/fileapi/webblob_messages.h
index 9a54c55..51c44cb 100644
--- a/content/common/fileapi/webblob_messages.h
+++ b/content/common/fileapi/webblob_messages.h
@@ -68,6 +68,10 @@
 IPC_MESSAGE_CONTROL1(StreamHostMsg_FinishBuilding,
                      GURL /* url */)
 
+// Aborts building a stream.
+IPC_MESSAGE_CONTROL1(StreamHostMsg_AbortBuilding,
+                     GURL /* url */)
+
 // Creates a new stream that's a clone of an existing src stream.
 IPC_MESSAGE_CONTROL2(StreamHostMsg_Clone,
                      GURL /* url */,
diff --git a/content/common/gpu/client/command_buffer_proxy_impl.cc b/content/common/gpu/client/command_buffer_proxy_impl.cc
index 1056939..9027164 100644
--- a/content/common/gpu/client/command_buffer_proxy_impl.cc
+++ b/content/common/gpu/client/command_buffer_proxy_impl.cc
@@ -10,6 +10,7 @@
 #include "base/memory/shared_memory.h"
 #include "base/stl_util.h"
 #include "content/common/child_process_messages.h"
+#include "content/common/gpu/client/gl_surface_capturer_host.h"
 #include "content/common/gpu/client/gpu_channel_host.h"
 #include "content/common/gpu/client/gpu_video_decode_accelerator_host.h"
 #include "content/common/gpu/gpu_memory_allocation.h"
@@ -500,6 +501,25 @@
   return vda.Pass();
 }
 
+scoped_ptr<SurfaceCapturer> CommandBufferProxyImpl::CreateSurfaceCapturer(
+    SurfaceCapturer::Client* client) {
+  int capturer_route_id;
+  scoped_ptr<SurfaceCapturer> capturer;
+  if (!Send(new GpuCommandBufferMsg_CreateSurfaceCapturer(
+           route_id_, &capturer_route_id))) {
+    LOG(ERROR) << "Send(GpuCommandBufferMsg_CreateSurfaceCapturer) failed";
+    return capturer.Pass();
+  }
+
+  if (capturer_route_id < 0) {
+    DLOG(ERROR) << "Failed create surface capturer";
+    return capturer.Pass();
+  }
+
+  capturer.reset(new GLSurfaceCapturerHost(capturer_route_id, client, this));
+  return capturer.Pass();
+}
+
 gpu::error::Error CommandBufferProxyImpl::GetLastError() {
   return last_state_.error;
 }
diff --git a/content/common/gpu/client/command_buffer_proxy_impl.h b/content/common/gpu/client/command_buffer_proxy_impl.h
index cd1232a..04f826d 100644
--- a/content/common/gpu/client/command_buffer_proxy_impl.h
+++ b/content/common/gpu/client/command_buffer_proxy_impl.h
@@ -19,6 +19,7 @@
 #include "base/observer_list.h"
 #include "content/common/gpu/gpu_memory_allocation.h"
 #include "content/common/gpu/gpu_memory_allocation.h"
+#include "content/common/gpu/surface_capturer.h"
 #include "gpu/command_buffer/common/command_buffer.h"
 #include "gpu/command_buffer/common/command_buffer_shared.h"
 #include "ipc/ipc_listener.h"
@@ -70,6 +71,11 @@
       media::VideoCodecProfile profile,
       media::VideoDecodeAccelerator::Client* client);
 
+  // Send an IPC message to create a SurfaceCapturer.  Returns NULL on failure
+  // to create the SurfaceCapturer.
+  scoped_ptr<SurfaceCapturer> CreateSurfaceCapturer(
+      SurfaceCapturer::Client* client);
+
   // IPC::Listener implementation:
   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
   virtual void OnChannelError() OVERRIDE;
diff --git a/content/common/gpu/client/context_provider_command_buffer.cc b/content/common/gpu/client/context_provider_command_buffer.cc
index 65c1c75..2c5b4da 100644
--- a/content/common/gpu/client/context_provider_command_buffer.cc
+++ b/content/common/gpu/client/context_provider_command_buffer.cc
@@ -4,8 +4,13 @@
 
 #include "content/common/gpu/client/context_provider_command_buffer.h"
 
+#include <set>
+#include <vector>
+
 #include "base/callback_helpers.h"
+#include "base/strings/string_split.h"
 #include "cc/output/managed_memory_policy.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
 #include "webkit/common/gpu/grcontext_for_webgraphicscontext3d.h"
 #include "webkit/common/gpu/managed_memory_policy_convert.h"
 
@@ -119,6 +124,8 @@
   if (!context3d_->makeContextCurrent())
     return false;
 
+  InitializeCapabilities();
+
   lost_context_callback_proxy_.reset(new LostContextCallbackProxy(this));
   swap_buffers_complete_callback_proxy_.reset(
       new SwapBuffersCompleteCallbackProxy(this));
@@ -149,6 +156,15 @@
   return gr_context_->get();
 }
 
+cc::ContextProvider::Capabilities
+ContextProviderCommandBuffer::ContextCapabilities() {
+  DCHECK(context3d_);
+  DCHECK(lost_context_callback_proxy_);  // Is bound to thread.
+  DCHECK(context_thread_checker_.CalledOnValidThread());
+
+  return capabilities_;
+}
+
 void ContextProviderCommandBuffer::VerifyContexts() {
   DCHECK(context3d_);
   DCHECK(lost_context_callback_proxy_);  // Is bound to thread.
@@ -198,6 +214,60 @@
       policy, discard_backbuffer_when_not_visible);
 }
 
+void ContextProviderCommandBuffer::InitializeCapabilities() {
+  // The command buffer provides the following capabilities always.
+  // TODO(jamesr): This information is duplicated with
+  // gpu::gles2::FeatureInfo::AddFeatures().
+  Capabilities caps;
+  caps.bind_uniform_location = true;
+  caps.discard_backbuffer = true;
+  caps.set_visibility = true;
+
+  // TODO(jamesr): These are also added in
+  // gpu::gles2::GLES2Implementation::GetStringHelper() on the client side.
+  caps.map_sub = true;
+  caps.shallow_flush = true;
+
+  // The swapbuffers complete callback is always supported by multi-process
+  // command buffer implementations.
+  caps.swapbuffers_complete_callback = true;
+
+  std::string extensions = reinterpret_cast<const char*>(
+      context3d_->GetImplementation()->GetString(0x1F03 /* GL_EXTENSIONS */));
+  std::vector<std::string> extension_list;
+  base::SplitString(extensions, ' ', &extension_list);
+  std::set<std::string> extension_set(extension_list.begin(),
+                                      extension_list.end());
+
+
+  // caps.map_image depends on GL_CHROMIUM_map_image, which is set client-side
+  // based on the presence of GpuControl.
+  caps.map_image = extension_set.count("GL_CHROMIUM_map_image") > 0;
+
+  // caps.fast_npot_mo8_textures depends on
+  //    workarounds_.enable_chromium_fast_npot_mo8_textures which controls
+  //    GL_CHROMIUM_fast_NPOT_MO8_textures
+  caps.fast_npot_mo8_textures =
+      extension_set.count("GL_CHROMIUM_fast_NPOT_MO8_textures") > 0;
+
+  caps.egl_image_external =
+      extension_set.count("GL_OES_EGL_image_external") > 0;
+
+  caps.texture_format_bgra8888 =
+      extension_set.count("GL_EXT_texture_format_BGRA8888") > 0;
+  caps.texture_rectangle = extension_set.count("GL_ARB_texture_rectangle") > 0;
+
+  // TODO(jamesr): This is unconditionally true on mac, no need to test for it
+  // at runtime.
+  caps.iosurface = extension_set.count("GL_CHROMIUM_iosurface") > 0;
+
+  caps.texture_usage = extension_set.count("GL_ANGLE_texture_usage") > 0;
+  caps.texture_storage = extension_set.count("GL_EXT_texture_storage") > 0;
+
+  capabilities_ = caps;
+}
+
+
 bool ContextProviderCommandBuffer::DestroyedOnMainThread() {
   DCHECK(main_thread_checker_.CalledOnValidThread());
 
diff --git a/content/common/gpu/client/context_provider_command_buffer.h b/content/common/gpu/client/context_provider_command_buffer.h
index efaf87a..f5a8ae4 100644
--- a/content/common/gpu/client/context_provider_command_buffer.h
+++ b/content/common/gpu/client/context_provider_command_buffer.h
@@ -29,6 +29,7 @@
   virtual bool BindToCurrentThread() OVERRIDE;
   virtual WebGraphicsContext3DCommandBufferImpl* Context3d() OVERRIDE;
   virtual class GrContext* GrContext() OVERRIDE;
+  virtual Capabilities ContextCapabilities() OVERRIDE;
   virtual void VerifyContexts() OVERRIDE;
   virtual bool DestroyedOnMainThread() OVERRIDE;
   virtual void SetLostContextCallback(
@@ -56,12 +57,16 @@
       const WebKit::WebGraphicsMemoryAllocation& allocation);
 
  private:
+  void InitializeCapabilities();
+
   base::ThreadChecker main_thread_checker_;
   base::ThreadChecker context_thread_checker_;
 
   scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context3d_;
   scoped_ptr<webkit::gpu::GrContextForWebGraphicsContext3D> gr_context_;
 
+  cc::ContextProvider::Capabilities capabilities_;
+
   LostContextCallback lost_context_callback_;
   SwapBuffersCompleteCallback swap_buffers_complete_callback_;
   MemoryPolicyChangedCallback memory_policy_changed_callback_;
diff --git a/content/common/gpu/client/gl_helper.cc b/content/common/gpu/client/gl_helper.cc
index 0a10d4a..bd5ad49 100644
--- a/content/common/gpu/client/gl_helper.cc
+++ b/content/common/gpu/client/gl_helper.cc
@@ -394,11 +394,11 @@
                        GL_STREAM_READ);
 
   request->query = context_->createQueryEXT();
-  context_->beginQueryEXT(GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM,
+  context_->beginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM,
                           request->query);
   context_->readPixels(0, 0, dst_size.width(), dst_size.height(),
                        GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-  context_->endQueryEXT(GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM);
+  context_->endQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM);
   context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
   cc::SyncPointHelper::SignalQuery(
       context_,
diff --git a/content/common/gpu/client/gl_surface_capturer_host.cc b/content/common/gpu/client/gl_surface_capturer_host.cc
new file mode 100644
index 0000000..c34fcfb
--- /dev/null
+++ b/content/common/gpu/client/gl_surface_capturer_host.cc
@@ -0,0 +1,196 @@
+// 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 "content/common/gpu/client/gl_surface_capturer_host.h"
+
+#include "content/common/gpu/client/gpu_channel_host.h"
+#include "content/common/gpu/gpu_messages.h"
+#include "ipc/ipc_message_macros.h"
+
+namespace content {
+
+namespace {
+
+enum {
+  kNumCaptureBuffers = 2,
+  kMaxCaptureSize = 4096,
+};
+
+}  // anonymous namespace
+
+GLSurfaceCapturerHost::GLSurfaceCapturerHost(int32 capturer_route_id,
+                                             SurfaceCapturer::Client* client,
+                                             CommandBufferProxyImpl* impl)
+    : thread_checker_(),
+      capturer_route_id_(capturer_route_id),
+      weak_this_factory_(this),
+      client_(client),
+      impl_(impl),
+      channel_(impl_->channel()),
+      next_frame_id_(0) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  channel_->AddRoute(capturer_route_id_, weak_this_factory_.GetWeakPtr());
+  impl_->AddDeletionObserver(this);
+}
+
+void GLSurfaceCapturerHost::OnChannelError() {
+  DLOG(ERROR) << "OnChannelError()";
+  DCHECK(thread_checker_.CalledOnValidThread());
+  OnNotifyError(kPlatformFailureError);
+  if (channel_) {
+    weak_this_factory_.InvalidateWeakPtrs();
+    channel_->RemoveRoute(capturer_route_id_);
+    channel_ = NULL;
+  }
+}
+
+bool GLSurfaceCapturerHost::OnMessageReceived(const IPC::Message& message) {
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP(GLSurfaceCapturerHost, message)
+    IPC_MESSAGE_HANDLER(SurfaceCapturerHostMsg_NotifyCaptureParameters,
+                        OnNotifyCaptureParameters)
+    IPC_MESSAGE_HANDLER(SurfaceCapturerHostMsg_NotifyCopyCaptureDone,
+                        OnNotifyCopyCaptureDone)
+    IPC_MESSAGE_HANDLER(SurfaceCapturerHostMsg_NotifyError, OnNotifyError)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+  return handled;
+}
+
+void GLSurfaceCapturerHost::Initialize(media::VideoFrame::Format format) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  Send(new SurfaceCapturerMsg_Initialize(capturer_route_id_, format));
+}
+
+void GLSurfaceCapturerHost::TryCapture() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  Send(new SurfaceCapturerMsg_TryCapture(capturer_route_id_));
+}
+
+void GLSurfaceCapturerHost::CopyCaptureToVideoFrame(
+    const scoped_refptr<media::VideoFrame>& frame) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!channel_)
+    return;
+  if (!base::SharedMemory::IsHandleValid(frame->shared_memory_handle())) {
+    DLOG(ERROR) << "CopyCaptureToVideoFrame(): cannot capture to frame not "
+                   "backed by shared memory";
+    OnNotifyError(kPlatformFailureError);
+    return;
+  }
+  base::SharedMemoryHandle handle =
+      channel_->ShareToGpuProcess(frame->shared_memory_handle());
+  if (!base::SharedMemory::IsHandleValid(frame->shared_memory_handle())) {
+    DLOG(ERROR) << "CopyCaptureToVideoFrame(): failed to duplicate buffer "
+                   "handle for GPU process";
+    OnNotifyError(kPlatformFailureError);
+    return;
+  }
+
+  const size_t plane_count = media::VideoFrame::NumPlanes(frame->format());
+  size_t frame_size = 0;
+  for (size_t i = 0; i < plane_count; ++i) {
+    DCHECK_EQ(reinterpret_cast<void*>(frame->data(i)),
+              reinterpret_cast<void*>((frame->data(0) + frame_size)))
+        << "plane=" << i;
+    frame_size += frame->stride(i) * frame->rows(i);
+  }
+
+  Send(new SurfaceCapturerMsg_CopyCaptureToVideoFrame(
+      capturer_route_id_, next_frame_id_, handle, frame_size));
+  frame_map_[next_frame_id_] = frame;
+  next_frame_id_ = (next_frame_id_ + 1) & 0x3FFFFFFF;
+}
+
+void GLSurfaceCapturerHost::Destroy() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  client_ = NULL;
+  Send(new SurfaceCapturerMsg_Destroy(capturer_route_id_));
+  delete this;
+}
+
+void GLSurfaceCapturerHost::OnWillDeleteImpl() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  impl_ = NULL;
+  OnChannelError();
+}
+
+GLSurfaceCapturerHost::~GLSurfaceCapturerHost() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  weak_this_factory_.InvalidateWeakPtrs();
+  if (channel_)
+    channel_->RemoveRoute(capturer_route_id_);
+  if (impl_)
+    impl_->RemoveDeletionObserver(this);
+}
+
+void GLSurfaceCapturerHost::NotifyError(Error error) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  base::MessageLoopProxy::current()->PostTask(
+      FROM_HERE,
+      base::Bind(&GLSurfaceCapturerHost::OnNotifyError,
+                 weak_this_factory_.GetWeakPtr(),
+                 error));
+}
+
+void GLSurfaceCapturerHost::OnNotifyCaptureParameters(
+    const gfx::Size& buffer_size,
+    const gfx::Rect& visible_rect) {
+  DVLOG(2) << "OnNotifyCaptureParameters(): "
+              "buffer_size=" << buffer_size.ToString()
+           << ", visible_rect=" << visible_rect.ToString();
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (buffer_size.width() < 1 || buffer_size.width() > kMaxCaptureSize ||
+      buffer_size.height() < 1 || buffer_size.height() > kMaxCaptureSize ||
+      visible_rect.x() < 0 || visible_rect.x() > kMaxCaptureSize - 1 ||
+      visible_rect.y() < 0 || visible_rect.y() > kMaxCaptureSize - 1 ||
+      visible_rect.width() < 1 || visible_rect.width() > kMaxCaptureSize ||
+      visible_rect.height() < 1 || visible_rect.height() > kMaxCaptureSize) {
+    DLOG(ERROR) << "OnNotifyCaptureParameters(): parameters out of bounds: "
+                   "buffer_size=" << buffer_size.ToString()
+                << ", visible_rect=" << visible_rect.ToString();
+    OnNotifyError(kPlatformFailureError);
+    return;
+  }
+  if (client_)
+    client_->NotifyCaptureParameters(buffer_size, visible_rect);
+}
+
+void GLSurfaceCapturerHost::OnNotifyCopyCaptureDone(int32 frame_id) {
+  DVLOG(3) << "OnNotifyCopyCaptureDone(): frame_id=" << frame_id;
+  DCHECK(thread_checker_.CalledOnValidThread());
+  FrameMap::iterator iter = frame_map_.find(frame_id);
+  if (iter == frame_map_.end()) {
+    DLOG(ERROR) << "OnNotifyCopyCaptureDone(): invalid frame_id=" << frame_id;
+    OnNotifyError(kPlatformFailureError);
+    return;
+  }
+  if (client_)
+    client_->NotifyCopyCaptureDone(iter->second);
+  frame_map_.erase(iter);
+}
+
+void GLSurfaceCapturerHost::OnNotifyError(Error error) {
+  DVLOG(2) << "OnNotifyError(): error=" << error;
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (client_) {
+    client_->NotifyError(error);
+    client_ = NULL;
+  }
+}
+
+void GLSurfaceCapturerHost::Send(IPC::Message* message) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  uint32 type = message->type();
+  if (!channel_) {
+    DLOG(ERROR) << "Send(): no channel";
+    delete message;
+    NotifyError(kPlatformFailureError);
+  } else if (!channel_->Send(message)) {
+    DLOG(ERROR) << "Send(): failed: message->type()=" << type;
+    NotifyError(kPlatformFailureError);
+  }
+}
+
+}  // namespace content
diff --git a/content/common/gpu/client/gl_surface_capturer_host.h b/content/common/gpu/client/gl_surface_capturer_host.h
new file mode 100644
index 0000000..d90afcd
--- /dev/null
+++ b/content/common/gpu/client/gl_surface_capturer_host.h
@@ -0,0 +1,107 @@
+// 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 CONTENT_COMMON_GPU_CLIENT_GL_SURFACE_CAPTURER_HOST_H_
+#define CONTENT_COMMON_GPU_CLIENT_GL_SURFACE_CAPTURER_HOST_H_
+
+#include "base/containers/small_map.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "content/common/gpu/client/command_buffer_proxy_impl.h"
+#include "content/common/gpu/surface_capturer.h"
+#include "ipc/ipc_listener.h"
+
+namespace gfx {
+
+class Rect;
+class Size;
+
+}  // namespace gfx
+
+namespace media {
+
+class VideoFrame;
+
+}  // namespace media
+
+namespace content {
+
+class GpuChannelHost;
+
+// This class is the browser-side host for the SurfaceCapturer in the GPU
+// process, coordinated over IPC.
+class GLSurfaceCapturerHost : public IPC::Listener,
+                              public SurfaceCapturer,
+                              public CommandBufferProxyImpl::DeletionObserver {
+ public:
+  // |client| is expected to outlive this object.  We are registered as a
+  // DeletionObserver with |impl|, so it will notify us when it is about to
+  // be destroyed.
+  GLSurfaceCapturerHost(int32 capturer_route_id,
+                        SurfaceCapturer::Client* client,
+                        CommandBufferProxyImpl* impl);
+
+  // IPC::Listener implementation.
+  virtual void OnChannelError() OVERRIDE;
+  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+  // SurfaceCapturer implementation.
+  virtual void Initialize(media::VideoFrame::Format format) OVERRIDE;
+  virtual void TryCapture() OVERRIDE;
+  virtual void CopyCaptureToVideoFrame(
+      const scoped_refptr<media::VideoFrame>& frame) OVERRIDE;
+  virtual void Destroy() OVERRIDE;
+
+  // CommandBufferProxyImpl::DeletionObserver implementation.
+  virtual void OnWillDeleteImpl() OVERRIDE;
+
+ private:
+  virtual ~GLSurfaceCapturerHost();
+
+  // Notify |client_| when an error has occured.  Used when notifying from
+  // within a media::VideoEncodeAccelerator entry point, to avoid re-entrancy.
+  void NotifyError(Error error);
+
+  // IPC handlers, proxying SurfaceCapturer::Client for the GPU process.
+  void OnNotifyCaptureParameters(const gfx::Size& buffer_size,
+                                 const gfx::Rect& visible_rect);
+  void OnNotifyCopyCaptureDone(int32 frame_id);
+  void OnNotifyError(Error error);
+
+  void Send(IPC::Message* message);
+
+  const base::ThreadChecker thread_checker_;
+
+  // Route ID for corresponding GLSurfaceCapturer in the GPU process.
+  const int32 capturer_route_id_;
+
+  // Weak pointer for registering as a listener for |channel_|.
+  base::WeakPtrFactory<GLSurfaceCapturerHost> weak_this_factory_;
+
+  // SurfaceEncoder::Client callbacks received over IPC are forwarded to
+  // |client_|.
+  SurfaceCapturer::Client* client_;
+
+  // Unowned reference to the CommandBufferProxyImpl that created us.
+  CommandBufferProxyImpl* impl_;
+
+  // GPU process channel to send messages on.
+  GpuChannelHost* channel_;
+
+  // media::VideoFrames sent to the capturer.
+  // base::IDMap not used here, since that takes pointers, not scoped_refptr.
+  typedef base::SmallMap<std::map<int32, scoped_refptr<media::VideoFrame> > >
+      FrameMap;
+  FrameMap frame_map_;
+
+  // ID serial number for next frame to send to the GPU process.
+  int32 next_frame_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(GLSurfaceCapturerHost);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_COMMON_GPU_CLIENT_GL_SURFACE_CAPTURER_HOST_H_
diff --git a/content/common/gpu/gpu_channel.cc b/content/common/gpu/gpu_channel.cc
index 2b920d3..3078887 100644
--- a/content/common/gpu/gpu_channel.cc
+++ b/content/common/gpu/gpu_channel.cc
@@ -985,4 +985,12 @@
       new GpuHostMsg_CacheShader(client_id_, key, shader));
 }
 
+void GpuChannel::AddFilter(IPC::ChannelProxy::MessageFilter* filter) {
+  channel_->AddFilter(filter);
+}
+
+void GpuChannel::RemoveFilter(IPC::ChannelProxy::MessageFilter* filter) {
+  channel_->RemoveFilter(filter);
+}
+
 }  // namespace content
diff --git a/content/common/gpu/gpu_channel.h b/content/common/gpu/gpu_channel.h
index 0692b90..23c27e0 100644
--- a/content/common/gpu/gpu_channel.h
+++ b/content/common/gpu/gpu_channel.h
@@ -86,6 +86,10 @@
 
   base::ProcessId renderer_pid() const { return channel_->peer_pid(); }
 
+  scoped_refptr<base::MessageLoopProxy> io_message_loop() const {
+    return io_message_loop_;
+  }
+
   // IPC::Listener implementation:
   virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE;
   virtual void OnChannelError() OVERRIDE;
@@ -156,6 +160,9 @@
 
   void CacheShader(const std::string& key, const std::string& shader);
 
+  void AddFilter(IPC::ChannelProxy::MessageFilter* filter);
+  void RemoveFilter(IPC::ChannelProxy::MessageFilter* filter);
+
  protected:
   virtual ~GpuChannel();
 
diff --git a/content/common/gpu/gpu_command_buffer_stub.cc b/content/common/gpu/gpu_command_buffer_stub.cc
index 78c0a49..b5ee308 100644
--- a/content/common/gpu/gpu_command_buffer_stub.cc
+++ b/content/common/gpu/gpu_command_buffer_stub.cc
@@ -18,6 +18,7 @@
 #include "content/common/gpu/gpu_messages.h"
 #include "content/common/gpu/gpu_watchdog.h"
 #include "content/common/gpu/image_transport_surface.h"
+#include "content/common/gpu/media/gl_surface_capturer.h"
 #include "content/common/gpu/media/gpu_video_decode_accelerator.h"
 #include "content/common/gpu/sync_point_manager.h"
 #include "content/public/common/content_client.h"
@@ -206,6 +207,8 @@
                                     OnGetTransferBuffer);
     IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuCommandBufferMsg_CreateVideoDecoder,
                                     OnCreateVideoDecoder)
+    IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuCommandBufferMsg_CreateSurfaceCapturer,
+                                    OnCreateSurfaceCapturer)
     IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_SetSurfaceVisible,
                         OnSetSurfaceVisible)
     IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_DiscardBackbuffer,
@@ -722,11 +725,23 @@
   int decoder_route_id = channel_->GenerateRouteID();
   GpuVideoDecodeAccelerator* decoder =
       new GpuVideoDecodeAccelerator(decoder_route_id, this);
-  decoder->Initialize(profile, reply_message);
+  decoder->Initialize(profile, reply_message, channel_->io_message_loop());
   // decoder is registered as a DestructionObserver of this stub and will
   // self-delete during destruction of this stub.
 }
 
+void GpuCommandBufferStub::OnCreateSurfaceCapturer(
+    IPC::Message* reply_message) {
+  TRACE_EVENT0("gpu", "GpuCommandBufferStub::OnCreateSurfaceCapturer");
+  int capturer_route_id = channel_->GenerateRouteID();
+  new GLSurfaceCapturer(capturer_route_id, this);
+  // The capturer is registered as a DestructionObserver of this stub and will
+  // self-delete during destruction of this stub.
+  GpuCommandBufferMsg_CreateSurfaceCapturer::WriteReplyParams(
+      reply_message, capturer_route_id);
+  Send(reply_message);
+}
+
 void GpuCommandBufferStub::OnSetSurfaceVisible(bool visible) {
   TRACE_EVENT0("gpu", "GpuCommandBufferStub::OnSetSurfaceVisible");
   if (memory_manager_client_state_)
diff --git a/content/common/gpu/gpu_command_buffer_stub.h b/content/common/gpu/gpu_command_buffer_stub.h
index 5811fe1..505b026 100644
--- a/content/common/gpu/gpu_command_buffer_stub.h
+++ b/content/common/gpu/gpu_command_buffer_stub.h
@@ -167,6 +167,8 @@
       media::VideoCodecProfile profile,
       IPC::Message* reply_message);
 
+  void OnCreateSurfaceCapturer(IPC::Message* reply_message);
+
   void OnSetSurfaceVisible(bool visible);
 
   void OnDiscardBackbuffer();
diff --git a/content/common/gpu/gpu_messages.h b/content/common/gpu/gpu_messages.h
index 49493d9..033d00d 100644
--- a/content/common/gpu/gpu_messages.h
+++ b/content/common/gpu/gpu_messages.h
@@ -14,6 +14,7 @@
 #include "content/common/gpu/gpu_memory_uma_stats.h"
 #include "content/common/gpu/gpu_process_launch_causes.h"
 #include "content/common/gpu/gpu_rendering_stats.h"
+#include "content/common/gpu/surface_capturer.h"
 #include "content/public/common/common_param_traits.h"
 #include "content/public/common/gpu_memory_stats.h"
 #include "gpu/command_buffer/common/command_buffer.h"
@@ -223,6 +224,8 @@
 
 IPC_ENUM_TRAITS(media::VideoEncodeAccelerator::Error)
 
+IPC_ENUM_TRAITS(content::SurfaceCapturer::Error)
+
 //------------------------------------------------------------------------------
 // GPU Messages
 // These are messages from the browser to the GPU process.
@@ -571,6 +574,12 @@
                            media::VideoCodecProfile /* profile */,
                            int /* route_id */)
 
+// Create and initialize a surface capturer, returning its new route_id.
+// Created capturers should be freed with SurfaceCapturerMsg_Destroy when no
+// longer needed.
+IPC_SYNC_MESSAGE_ROUTED0_1(GpuCommandBufferMsg_CreateSurfaceCapturer,
+                           int /* route_id */)
+
 // Tells the proxy that there was an error and the command buffer had to be
 // destroyed for some reason.
 IPC_MESSAGE_ROUTED1(GpuCommandBufferMsg_Destroyed,
@@ -763,3 +772,40 @@
 // Report error condition.
 IPC_MESSAGE_ROUTED1(AcceleratedVideoEncoderHostMsg_NotifyError,
                     media::VideoEncodeAccelerator::Error /* error */)
+
+//------------------------------------------------------------------------------
+// Gpu Surface Capturer Messages
+// These messages are sent from the Browser process to the GPU process.
+
+// Initialize the capturer.
+IPC_MESSAGE_ROUTED1(SurfaceCapturerMsg_Initialize,
+                    media::VideoFrame::Format /* format */)
+
+// Attempt to start a capture.
+IPC_MESSAGE_ROUTED0(SurfaceCapturerMsg_TryCapture)
+
+// Copy captured contents to a video frame.
+IPC_MESSAGE_ROUTED3(SurfaceCapturerMsg_CopyCaptureToVideoFrame,
+                    int32 /* buffer_id */,
+                    base::SharedMemoryHandle /* buffer_shm */,
+                    uint32 /* buffer_size */)
+
+// Destroy the capturer.
+IPC_MESSAGE_ROUTED0(SurfaceCapturerMsg_Destroy)
+
+//------------------------------------------------------------------------------
+// Gpu Surface Capturer Host Messages
+// These messages are sent from GPU process to Browser process.
+
+// Report the capture output parameters to the Browser process.
+IPC_MESSAGE_ROUTED2(SurfaceCapturerHostMsg_NotifyCaptureParameters,
+                    gfx::Size /* buffer_size */,
+                    gfx::Rect /* visible_rect */)
+
+// Report successful copy of a capture of a surface.
+IPC_MESSAGE_ROUTED1(SurfaceCapturerHostMsg_NotifyCopyCaptureDone,
+                    int32 /* frame_id */)
+
+// Report error.
+IPC_MESSAGE_ROUTED1(SurfaceCapturerHostMsg_NotifyError,
+                    content::SurfaceCapturer::Error /* error */)
diff --git a/content/common/gpu/media/android_video_decode_accelerator.cc b/content/common/gpu/media/android_video_decode_accelerator.cc
index ac48abc..403d4ad 100644
--- a/content/common/gpu/media/android_video_decode_accelerator.cc
+++ b/content/common/gpu/media/android_video_decode_accelerator.cc
@@ -102,7 +102,7 @@
   gl_decoder_->RestoreTextureUnitBindings(0);
   gl_decoder_->RestoreActiveTexture();
 
-  surface_texture_ = new gfx::SurfaceTextureBridge(surface_texture_id_);
+  surface_texture_ = new gfx::SurfaceTexture(surface_texture_id_);
 
   if (!ConfigureMediaCodec()) {
     LOG(ERROR) << "Failed to create MediaCodec instance.";
diff --git a/content/common/gpu/media/android_video_decode_accelerator.h b/content/common/gpu/media/android_video_decode_accelerator.h
index b846831..abcb1db 100644
--- a/content/common/gpu/media/android_video_decode_accelerator.h
+++ b/content/common/gpu/media/android_video_decode_accelerator.h
@@ -15,21 +15,22 @@
 #include "base/compiler_specific.h"
 #include "base/threading/thread_checker.h"
 #include "content/common/content_export.h"
+#include "content/common/gpu/media/video_decode_accelerator_impl.h"
 #include "gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h"
 #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
 #include "media/base/android/media_codec_bridge.h"
 #include "media/video/video_decode_accelerator.h"
 
 namespace gfx {
-class SurfaceTextureBridge;
+class SurfaceTexture;
 }
 
 namespace content {
 // A VideoDecodeAccelerator implementation for Android.
 // This class decodes the input encoded stream by using Android's MediaCodec
 // class. http://developer.android.com/reference/android/media/MediaCodec.html
-class CONTENT_EXPORT AndroidVideoDecodeAccelerator :
-    public media::VideoDecodeAccelerator {
+class CONTENT_EXPORT AndroidVideoDecodeAccelerator
+    : public VideoDecodeAcceleratorImpl {
  public:
   // Does not take ownership of |client| which must outlive |*this|.
   AndroidVideoDecodeAccelerator(
@@ -127,7 +128,7 @@
   scoped_ptr<media::VideoCodecBridge> media_codec_;
 
   // A container of texture. Used to set a texture to |media_codec_|.
-  scoped_refptr<gfx::SurfaceTextureBridge> surface_texture_;
+  scoped_refptr<gfx::SurfaceTexture> surface_texture_;
 
   // The texture id which is set to |surface_texture_|.
   uint32 surface_texture_id_;
diff --git a/content/common/gpu/media/android_video_decode_accelerator_unittest.cc b/content/common/gpu/media/android_video_decode_accelerator_unittest.cc
index baf516d..4e25e68 100644
--- a/content/common/gpu/media/android_video_decode_accelerator_unittest.cc
+++ b/content/common/gpu/media/android_video_decode_accelerator_unittest.cc
@@ -15,7 +15,7 @@
 #include "media/video/picture.h"
 #include "media/video/video_decode_accelerator.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gl/android/surface_texture_bridge.h"
+#include "ui/gl/android/surface_texture.h"
 
 namespace {
 
@@ -73,7 +73,7 @@
   bool Configure(media::VideoCodec codec) {
     AndroidVideoDecodeAccelerator* accelerator =
         static_cast<AndroidVideoDecodeAccelerator*>(accelerator_.get());
-    accelerator->surface_texture_ = new gfx::SurfaceTextureBridge(0);
+    accelerator->surface_texture_ = new gfx::SurfaceTexture(0);
     accelerator->codec_ = codec;
     return accelerator->ConfigureMediaCodec();
   }
diff --git a/content/common/gpu/media/dxva_video_decode_accelerator.cc b/content/common/gpu/media/dxva_video_decode_accelerator.cc
index fa4dc5c..97c883e 100644
--- a/content/common/gpu/media/dxva_video_decode_accelerator.cc
+++ b/content/common/gpu/media/dxva_video_decode_accelerator.cc
@@ -23,6 +23,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/shared_memory.h"
 #include "base/message_loop/message_loop.h"
+#include "base/win/windows_version.h"
 #include "media/video/video_decode_accelerator.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_surface_egl.h"
@@ -395,6 +396,22 @@
 
   RETURN_ON_FAILURE(CreateD3DDevManager(),
                     "Failed to initialize D3D device and manager",);
+
+  if (base::win::GetVersion() == base::win::VERSION_WIN8) {
+    // On Windows 8+ mf.dll forwards to mfcore.dll. It does not exist in
+    // Windows 7. Loading mfcore.dll fails on Windows 8.1 in the
+    // sandbox.
+    if (!LoadLibrary(L"mfcore.dll")) {
+      DLOG(ERROR) << "Failed to load mfcore.dll, Error: " << ::GetLastError();
+      return;
+    }
+    // MFStartup needs to be called once outside the sandbox. It fails on
+    // Windows 8.1 with E_NOTIMPL if it is called the first time in the
+    // sandbox.
+    RETURN_ON_HR_FAILURE(MFStartup(MF_VERSION, MFSTARTUP_FULL),
+                         "MFStartup failed.",);
+  }
+
   pre_sandbox_init_done_ = true;
 }
 
diff --git a/content/common/gpu/media/dxva_video_decode_accelerator.h b/content/common/gpu/media/dxva_video_decode_accelerator.h
index 26b1ba7..206099c 100644
--- a/content/common/gpu/media/dxva_video_decode_accelerator.h
+++ b/content/common/gpu/media/dxva_video_decode_accelerator.h
@@ -17,6 +17,7 @@
 #include "base/threading/non_thread_safe.h"
 #include "base/win/scoped_comptr.h"
 #include "content/common/content_export.h"
+#include "content/common/gpu/media/video_decode_accelerator_impl.h"
 #include "media/video/video_decode_accelerator.h"
 
 interface IMFSample;
@@ -29,7 +30,7 @@
 // This class lives on a single thread and DCHECKs that it is never accessed
 // from any other.
 class CONTENT_EXPORT DXVAVideoDecodeAccelerator
-    : public media::VideoDecodeAccelerator,
+    : public VideoDecodeAcceleratorImpl,
       NON_EXPORTED_BASE(public base::NonThreadSafe) {
  public:
   enum State {
diff --git a/content/common/gpu/media/exynos_video_decode_accelerator.cc b/content/common/gpu/media/exynos_video_decode_accelerator.cc
index 677df9c..e643969 100644
--- a/content/common/gpu/media/exynos_video_decode_accelerator.cc
+++ b/content/common/gpu/media/exynos_video_decode_accelerator.cc
@@ -205,8 +205,10 @@
     EGLDisplay egl_display,
     EGLContext egl_context,
     Client* client,
-    const base::Callback<bool(void)>& make_context_current)
+    const base::Callback<bool(void)>& make_context_current,
+    const scoped_refptr<base::MessageLoopProxy>& io_message_loop_proxy)
     : child_message_loop_proxy_(base::MessageLoopProxy::current()),
+      io_message_loop_proxy_(io_message_loop_proxy),
       weak_this_(base::AsWeakPtr(this)),
       client_ptr_factory_(client),
       client_(client_ptr_factory_.GetWeakPtr()),
@@ -415,23 +417,12 @@
     const media::BitstreamBuffer& bitstream_buffer) {
   DVLOG(1) << "Decode(): input_id=" << bitstream_buffer.id()
            << ", size=" << bitstream_buffer.size();
-  DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
-
-  scoped_ptr<BitstreamBufferRef> bitstream_record(new BitstreamBufferRef(
-      client_, child_message_loop_proxy_,
-      new base::SharedMemory(bitstream_buffer.handle(), true),
-      bitstream_buffer.size(), bitstream_buffer.id()));
-  if (!bitstream_record->shm->Map(bitstream_buffer.size())) {
-    DLOG(ERROR) << "Decode(): could not map bitstream_buffer";
-    NOTIFY_ERROR(UNREADABLE_INPUT);
-    return;
-  }
-  DVLOG(3) << "Decode(): mapped to addr=" << bitstream_record->shm->memory();
+  DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
 
   // DecodeTask() will take care of running a DecodeBufferTask().
   decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
       &ExynosVideoDecodeAccelerator::DecodeTask, base::Unretained(this),
-      base::Passed(&bitstream_record)));
+      bitstream_buffer));
 }
 
 void ExynosVideoDecodeAccelerator::AssignPictureBuffers(
@@ -576,6 +567,8 @@
   delete this;
 }
 
+bool ExynosVideoDecodeAccelerator::CanDecodeOnIOThread() { return true; }
+
 // static
 void ExynosVideoDecodeAccelerator::PreSandboxInitialization() {
   DVLOG(3) << "PreSandboxInitialization()";
@@ -610,12 +603,23 @@
 }
 
 void ExynosVideoDecodeAccelerator::DecodeTask(
-    scoped_ptr<BitstreamBufferRef> bitstream_record) {
-  DVLOG(3) << "DecodeTask(): input_id=" << bitstream_record->input_id;
+    const media::BitstreamBuffer& bitstream_buffer) {
+  DVLOG(3) << "DecodeTask(): input_id=" << bitstream_buffer.id();
   DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current());
   DCHECK_NE(decoder_state_, kUninitialized);
   TRACE_EVENT1("Video Decoder", "EVDA::DecodeTask", "input_id",
-               bitstream_record->input_id);
+               bitstream_buffer.id());
+
+  scoped_ptr<BitstreamBufferRef> bitstream_record(new BitstreamBufferRef(
+      client_, child_message_loop_proxy_,
+      new base::SharedMemory(bitstream_buffer.handle(), true),
+      bitstream_buffer.size(), bitstream_buffer.id()));
+  if (!bitstream_record->shm->Map(bitstream_buffer.size())) {
+    DLOG(ERROR) << "Decode(): could not map bitstream_buffer";
+    NOTIFY_ERROR(UNREADABLE_INPUT);
+    return;
+  }
+  DVLOG(3) << "Decode(): mapped to addr=" << bitstream_record->shm->memory();
 
   if (decoder_state_ == kResetting || decoder_flushing_) {
     // In the case that we're resetting or flushing, we need to delay decoding
diff --git a/content/common/gpu/media/exynos_video_decode_accelerator.h b/content/common/gpu/media/exynos_video_decode_accelerator.h
index 77471402..09264b9 100644
--- a/content/common/gpu/media/exynos_video_decode_accelerator.h
+++ b/content/common/gpu/media/exynos_video_decode_accelerator.h
@@ -16,8 +16,8 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/threading/thread.h"
 #include "content/common/content_export.h"
+#include "content/common/gpu/media/video_decode_accelerator_impl.h"
 #include "media/base/video_decoder_config.h"
-#include "media/video/video_decode_accelerator.h"
 #include "ui/gfx/size.h"
 #include "ui/gl/gl_bindings.h"
 
@@ -54,14 +54,15 @@
 // decoder_thread_, so there are no synchronization issues.
 // ... well, there are, but it's a matter of getting messages posted in the
 // right order, not fiddling with locks.
-class CONTENT_EXPORT ExynosVideoDecodeAccelerator :
-    public media::VideoDecodeAccelerator {
+class CONTENT_EXPORT ExynosVideoDecodeAccelerator
+    : public VideoDecodeAcceleratorImpl {
  public:
   ExynosVideoDecodeAccelerator(
       EGLDisplay egl_display,
       EGLContext egl_context,
       Client* client,
-      const base::Callback<bool(void)>& make_context_current);
+      const base::Callback<bool(void)>& make_context_current,
+      const scoped_refptr<base::MessageLoopProxy>& io_message_loop_proxy);
   virtual ~ExynosVideoDecodeAccelerator();
 
   // media::VideoDecodeAccelerator implementation.
@@ -75,6 +76,9 @@
   virtual void Reset() OVERRIDE;
   virtual void Destroy() OVERRIDE;
 
+  // VideoDecodeAcceleratorImpl implementation.
+  virtual bool CanDecodeOnIOThread() OVERRIDE;
+
   // Do any necessary initialization before the sandbox is enabled.
   static void PreSandboxInitialization();
 
@@ -178,7 +182,7 @@
   // Enqueue a BitstreamBuffer to decode.  This will enqueue a buffer to the
   // decoder_input_queue_, then queue a DecodeBufferTask() to actually decode
   // the buffer.
-  void DecodeTask(scoped_ptr<BitstreamBufferRef> bitstream_record);
+  void DecodeTask(const media::BitstreamBuffer& bitstream_buffer);
 
   // Decode from the buffers queued in decoder_input_queue_.  Calls
   // DecodeBufferInitial() or DecodeBufferContinue() as appropriate.
@@ -311,6 +315,9 @@
   // Our original calling message loop for the child thread.
   scoped_refptr<base::MessageLoopProxy> child_message_loop_proxy_;
 
+  // Message loop of the IO thread.
+  scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
+
   // WeakPtr<> pointing to |this| for use in posting tasks from the decoder or
   // device worker threads back to the child thread.  Because the worker threads
   // are members of this class, any task running on those threads is guaranteed
diff --git a/content/common/gpu/media/exynos_video_encode_accelerator.cc b/content/common/gpu/media/exynos_video_encode_accelerator.cc
index 4b9d38f..065e629 100644
--- a/content/common/gpu/media/exynos_video_encode_accelerator.cc
+++ b/content/common/gpu/media/exynos_video_encode_accelerator.cc
@@ -707,7 +707,7 @@
       return;
     }
     const bool key_frame = ((dqbuf.flags & V4L2_BUF_FLAG_KEYFRAME) != 0);
-    const size_t output_size = dqbuf.m.planes[0].bytesused;
+    size_t output_size = dqbuf.m.planes[0].bytesused;
     MfcOutputRecord& output_record = mfc_output_buffer_map_[dqbuf.index];
     DCHECK(output_record.at_device);
     DCHECK(output_record.buffer_ref.get());
@@ -724,6 +724,7 @@
       // Insert stream header before every keyframe.
       memmove(data + stream_header_size_, data, output_size);
       memcpy(data, stream_header_.get(), stream_header_size_);
+      output_size += stream_header_size_;
     }
     DVLOG(3) << "DequeueMfc(): returning "
                 "bitstream_buffer_id=" << output_record.buffer_ref->id
@@ -733,7 +734,7 @@
         base::Bind(&Client::BitstreamBufferReady,
                    client_,
                    output_record.buffer_ref->id,
-                   dqbuf.m.planes[0].bytesused,
+                   output_size,
                    key_frame));
     output_record.at_device = false;
     output_record.buffer_ref.reset();
diff --git a/content/common/gpu/media/gl_surface_capturer.cc b/content/common/gpu/media/gl_surface_capturer.cc
new file mode 100644
index 0000000..5f76eda
--- /dev/null
+++ b/content/common/gpu/media/gl_surface_capturer.cc
@@ -0,0 +1,186 @@
+// 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 "content/common/gpu/media/gl_surface_capturer.h"
+
+#include "base/message_loop/message_loop_proxy.h"
+#include "content/common/gpu/gpu_channel.h"
+#include "content/common/gpu/gpu_messages.h"
+#include "ipc/ipc_message_macros.h"
+
+namespace content {
+
+namespace {
+
+enum {
+  kMaxCaptureSize = 4096,
+};
+
+}  // anonymous namespace
+
+GLSurfaceCapturer::GLSurfaceCapturer(int32 route_id, GpuCommandBufferStub* stub)
+    : thread_checker_(),
+      route_id_(route_id),
+      stub_(stub),
+      output_format_(media::VideoFrame::INVALID) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  stub_->AddDestructionObserver(this);
+  stub_->channel()->AddRoute(route_id_, this);
+}
+
+GLSurfaceCapturer::~GLSurfaceCapturer() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (surface_capturer_)
+    surface_capturer_.release()->Destroy();
+
+  stub_->channel()->RemoveRoute(route_id_);
+  stub_->RemoveDestructionObserver(this);
+}
+
+bool GLSurfaceCapturer::OnMessageReceived(const IPC::Message& message) {
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP(GLSurfaceCapturer, message)
+    IPC_MESSAGE_HANDLER(SurfaceCapturerMsg_Initialize, OnInitialize)
+    IPC_MESSAGE_HANDLER(SurfaceCapturerMsg_TryCapture, OnTryCapture)
+    IPC_MESSAGE_HANDLER(SurfaceCapturerMsg_CopyCaptureToVideoFrame,
+                        OnCopyCaptureToVideoFrame)
+    IPC_MESSAGE_HANDLER(SurfaceCapturerMsg_Destroy, OnDestroy)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+  return handled;
+}
+
+void GLSurfaceCapturer::NotifyCaptureParameters(const gfx::Size& buffer_size,
+                                                const gfx::Rect& visible_rect) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (buffer_size.width() < 1 || buffer_size.width() > kMaxCaptureSize ||
+      buffer_size.height() < 1 || buffer_size.height() > kMaxCaptureSize ||
+      visible_rect.x() < 0 || visible_rect.x() > kMaxCaptureSize - 1 ||
+      visible_rect.y() < 0 || visible_rect.y() > kMaxCaptureSize - 1 ||
+      visible_rect.width() < 1 || visible_rect.width() > kMaxCaptureSize ||
+      visible_rect.height() < 1 || visible_rect.height() > kMaxCaptureSize) {
+    DLOG(ERROR) << "NotifyCaptureParameters(): parameters out of bounds: "
+                   "buffer_size=" << buffer_size.ToString()
+                << ", visible_rect=" << visible_rect.ToString();
+    NotifyError(SurfaceCapturer::kInvalidArgumentError);
+    return;
+  }
+  output_buffer_size_ = buffer_size;
+  output_visible_rect_ = visible_rect;
+  Send(new SurfaceCapturerHostMsg_NotifyCaptureParameters(
+      route_id_, buffer_size, visible_rect));
+}
+
+void GLSurfaceCapturer::NotifyCopyCaptureDone(
+    const scoped_refptr<media::VideoFrame>& frame) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  FrameIdMap::iterator iter = frame_id_map_.find(frame);
+  if (iter == frame_id_map_.end()) {
+    DLOG(ERROR) << "NotifyCopyCaptureDone(): invalid frame";
+    NotifyError(SurfaceCapturer::kInvalidArgumentError);
+    return;
+  }
+  Send(new SurfaceCapturerHostMsg_NotifyCopyCaptureDone(route_id_,
+                                                        iter->second));
+  frame_id_map_.erase(iter);
+}
+
+void GLSurfaceCapturer::NotifyError(SurfaceCapturer::Error error) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  Send(new SurfaceCapturerHostMsg_NotifyError(route_id_, error));
+}
+
+void GLSurfaceCapturer::OnWillDestroyStub() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  delete this;
+}
+
+void GLSurfaceCapturer::OnInitialize(media::VideoFrame::Format format) {
+  DVLOG(2) << "OnInitialize(): format=" << format;
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!surface_capturer_);
+
+  // TODO(sheu): actually create a surface capturer.
+
+  if (!surface_capturer_) {
+    NOTIMPLEMENTED() << "OnInitialize(): GL surface capturing not available";
+    NotifyError(SurfaceCapturer::kPlatformFailureError);
+    return;
+  }
+
+  output_format_ = format;
+  surface_capturer_->Initialize(output_format_);
+}
+
+void GLSurfaceCapturer::OnTryCapture() {
+  DVLOG(3) << "OnTryCapture()";
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!surface_capturer_)
+    return;
+  surface_capturer_->TryCapture();
+}
+
+static void DeleteShm(scoped_ptr<base::SharedMemory> shm) {
+  // Just let shm fall out of scope.
+}
+
+void GLSurfaceCapturer::OnCopyCaptureToVideoFrame(
+    int32 frame_id,
+    base::SharedMemoryHandle buffer_shm,
+    uint32_t buffer_size) {
+  DVLOG(3) << "OnCopyCaptureToVideoFrame(): frame_id=" << frame_id
+           << ", buffer_size=" << buffer_size;
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!surface_capturer_)
+    return;
+  if (frame_id < 0) {
+    DLOG(ERROR) << "OnCopyCaptureToVideoFrame(): invalid frame_id=" << frame_id;
+    NotifyError(SurfaceCapturer::kPlatformFailureError);
+    return;
+  }
+
+  scoped_ptr<base::SharedMemory> shm(new base::SharedMemory(buffer_shm, false));
+  if (!shm->Map(buffer_size)) {
+    DLOG(ERROR) << "OnCopyCaptureToVideoFrame(): could not map "
+                   "frame_id=" << frame_id;
+    NotifyError(SurfaceCapturer::kPlatformFailureError);
+    return;
+  }
+  scoped_refptr<media::VideoFrame> frame =
+      media::VideoFrame::WrapExternalSharedMemory(
+          output_format_,
+          output_buffer_size_,
+          output_visible_rect_,
+          output_visible_rect_.size(),
+          reinterpret_cast<uint8*>(shm->memory()),
+          buffer_size,
+          buffer_shm,
+          base::TimeDelta(),
+          base::Bind(&DeleteShm, base::Passed(&shm)));
+  if (!frame) {
+    DLOG(ERROR) << "OnCopyCaptureToVideoFrame(): could not create frame for"
+                   "frame_id=" << frame_id;
+    NotifyError(SurfaceCapturer::kPlatformFailureError);
+    return;
+  }
+  frame_id_map_[frame] = frame_id;
+  surface_capturer_->CopyCaptureToVideoFrame(frame);
+}
+
+void GLSurfaceCapturer::OnDestroy() {
+  DVLOG(2) << "OnDestroy()";
+  DCHECK(thread_checker_.CalledOnValidThread());
+  delete this;
+}
+
+void GLSurfaceCapturer::Send(IPC::Message* message) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  uint32 type = message->type();
+  if (!stub_->channel()->Send(message)) {
+    DLOG(ERROR) << "Send(): send failed: message->type()=" << type;
+    NotifyError(SurfaceCapturer::kPlatformFailureError);
+  }
+}
+
+}  // namespace content
diff --git a/content/common/gpu/media/gl_surface_capturer.h b/content/common/gpu/media/gl_surface_capturer.h
new file mode 100644
index 0000000..d40ae99
--- /dev/null
+++ b/content/common/gpu/media/gl_surface_capturer.h
@@ -0,0 +1,88 @@
+// 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 CONTENT_COMMON_GPU_MEDIA_GL_SURFACE_CAPTURER_H_
+#define CONTENT_COMMON_GPU_MEDIA_GL_SURFACE_CAPTURER_H_
+
+#include "base/callback.h"
+#include "base/containers/small_map.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "content/common/gpu/gpu_command_buffer_stub.h"
+#include "content/common/gpu/surface_capturer.h"
+#include "ipc/ipc_listener.h"
+#include "ipc/ipc_sender.h"
+
+namespace gfx {
+
+class Size;
+class Rect;
+
+}  // namespace gfx
+
+namespace content {
+
+class GpuChannelHost;
+
+// This class implements the GPU process side of a SurfaceCapturer,
+// communicating over IPC with a GLSurfaceCapturerHost on the browser process.
+class GLSurfaceCapturer : public IPC::Listener,
+                          public SurfaceCapturer::Client,
+                          public GpuCommandBufferStub::DestructionObserver {
+ public:
+  GLSurfaceCapturer(int32 route_id, GpuCommandBufferStub* stub);
+  virtual ~GLSurfaceCapturer();
+
+  // IPC::Listener implementation.
+  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+  // SurfaceCapturer::Client implementation.
+  virtual void NotifyCaptureParameters(const gfx::Size& buffer_size,
+                                       const gfx::Rect& visible_rect) OVERRIDE;
+  virtual void NotifyCopyCaptureDone(
+      const scoped_refptr<media::VideoFrame>& frame) OVERRIDE;
+  virtual void NotifyError(SurfaceCapturer::Error error) OVERRIDE;
+
+  // GpuCommandBufferStub::DestructionObserver implementation.
+  virtual void OnWillDestroyStub() OVERRIDE;
+
+ private:
+  // Handlers for IPC messages.
+  void OnInitialize(media::VideoFrame::Format format);
+  void OnCopyCaptureToVideoFrame(int32 frame_id,
+                                 base::SharedMemoryHandle buffer_shm,
+                                 uint32 buffer_size);
+  void OnTryCapture();
+  void OnDestroy();
+
+  void Send(IPC::Message* message);
+
+  const base::ThreadChecker thread_checker_;
+
+  // Route ID assigned by the GpuCommandBufferStub.
+  const int32 route_id_;
+
+  // The GpuCommandBufferStub this instance belongs to, as an unowned pointer
+  // since |stub_| will own (and outlive) this.
+  GpuCommandBufferStub* const stub_;
+
+  // media::VideoFrames received from the host side.
+  typedef base::SmallMap<std::map<scoped_refptr<media::VideoFrame>, int32> >
+      FrameIdMap;
+  FrameIdMap frame_id_map_;
+
+  // The underlying capturer we delegate to.
+  scoped_ptr<SurfaceCapturer> surface_capturer_;
+
+  // The capture output parameters that the capturer informs us of.
+  media::VideoFrame::Format output_format_;
+  gfx::Size output_buffer_size_;
+  gfx::Rect output_visible_rect_;
+
+  DISALLOW_COPY_AND_ASSIGN(GLSurfaceCapturer);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_COMMON_GPU_MEDIA_GL_SURFACE_CAPTURER_H_
diff --git a/content/common/gpu/media/gpu_video_decode_accelerator.cc b/content/common/gpu/media/gpu_video_decode_accelerator.cc
index 2b0bb84..a1062e3 100644
--- a/content/common/gpu/media/gpu_video_decode_accelerator.cc
+++ b/content/common/gpu/media/gpu_video_decode_accelerator.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/logging.h"
+#include "base/message_loop/message_loop_proxy.h"
 #include "base/stl_util.h"
 
 #include "content/common/gpu/gpu_channel.h"
@@ -54,6 +55,36 @@
   return true;
 }
 
+class GpuVideoDecodeAccelerator::MessageFilter
+    : public IPC::ChannelProxy::MessageFilter {
+ public:
+  MessageFilter(GpuVideoDecodeAccelerator* owner, int32 host_route_id)
+      : owner_(owner), host_route_id_(host_route_id) {}
+
+  virtual void OnFilterRemoved() OVERRIDE {
+    // This will delete |owner_| and |this|.
+    owner_->OnFilterRemoved();
+  }
+  virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE {
+    if (msg.routing_id() != host_route_id_)
+      return false;
+
+    IPC_BEGIN_MESSAGE_MAP(MessageFilter, msg)
+      IPC_MESSAGE_FORWARD(AcceleratedVideoDecoderMsg_Decode, owner_,
+                          GpuVideoDecodeAccelerator::OnDecode)
+      IPC_MESSAGE_UNHANDLED(return false;)
+    IPC_END_MESSAGE_MAP()
+    return true;
+  }
+
+ protected:
+  virtual ~MessageFilter() {}
+
+ private:
+  GpuVideoDecodeAccelerator* owner_;
+  int32 host_route_id_;
+};
+
 GpuVideoDecodeAccelerator::GpuVideoDecodeAccelerator(int32 host_route_id,
                                                      GpuCommandBufferStub* stub)
     : init_done_msg_(NULL),
@@ -63,17 +94,14 @@
   DCHECK(stub_);
   stub_->AddDestructionObserver(this);
   stub_->channel()->AddRoute(host_route_id_, this);
+  child_message_loop_ = base::MessageLoopProxy::current();
   make_context_current_ =
       base::Bind(&MakeDecoderContextCurrent, stub_->AsWeakPtr());
 }
 
 GpuVideoDecodeAccelerator::~GpuVideoDecodeAccelerator() {
-  DCHECK(stub_);
   if (video_decode_accelerator_)
     video_decode_accelerator_.release()->Destroy();
-
-  stub_->channel()->RemoveRoute(host_route_id_);
-  stub_->RemoveDestructionObserver(this);
 }
 
 bool GpuVideoDecodeAccelerator::OnMessageReceived(const IPC::Message& msg) {
@@ -149,7 +177,8 @@
 
 void GpuVideoDecodeAccelerator::Initialize(
     const media::VideoCodecProfile profile,
-    IPC::Message* init_done_msg) {
+    IPC::Message* init_done_msg,
+    const scoped_refptr<base::MessageLoopProxy>& io_message_loop) {
   DCHECK(stub_);
   DCHECK(!video_decode_accelerator_.get());
   DCHECK(!init_done_msg_);
@@ -179,7 +208,8 @@
       gfx::GLSurfaceEGL::GetHardwareDisplay(),
       stub_->decoder()->GetGLContext()->GetHandle(),
       this,
-      make_context_current_));
+      make_context_current_,
+      io_message_loop));
 #elif defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY) && defined(USE_X11)
   gfx::GLContextGLX* glx_context =
       static_cast<gfx::GLContextGLX*>(stub_->decoder()->GetGLContext());
@@ -199,16 +229,31 @@
   return;
 #endif
 
+  if (video_decode_accelerator_->CanDecodeOnIOThread()) {
+    filter_ = new MessageFilter(this, host_route_id_);
+    stub_->channel()->AddFilter(filter_.get());
+  }
+
   if (!video_decode_accelerator_->Initialize(profile))
     NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE);
 }
 
+// Runs on IO thread if video_decode_accelerator_->CanDecodeOnIOThread() is
+// true, otherwise on the main thread.
 void GpuVideoDecodeAccelerator::OnDecode(
     base::SharedMemoryHandle handle, int32 id, uint32 size) {
   DCHECK(video_decode_accelerator_.get());
   if (id < 0) {
     DLOG(FATAL) << "BitstreamBuffer id " << id << " out of range";
-    NotifyError(media::VideoDecodeAccelerator::INVALID_ARGUMENT);
+    if (child_message_loop_->BelongsToCurrentThread()) {
+      NotifyError(media::VideoDecodeAccelerator::INVALID_ARGUMENT);
+    } else {
+      child_message_loop_->PostTask(
+          FROM_HERE,
+          base::Bind(&GpuVideoDecodeAccelerator::NotifyError,
+                     base::Unretained(this),
+                     media::VideoDecodeAccelerator::INVALID_ARGUMENT));
+    }
     return;
   }
   video_decode_accelerator_->Decode(media::BitstreamBuffer(id, handle, size));
@@ -297,7 +342,20 @@
 
 void GpuVideoDecodeAccelerator::OnDestroy() {
   DCHECK(video_decode_accelerator_.get());
-  delete this;
+  DCHECK(stub_);
+  stub_->channel()->RemoveRoute(host_route_id_);
+  stub_->RemoveDestructionObserver(this);
+  if (filter_.get()) {
+    // Remove the filter first because the member variables can be accessed on
+    // IO thread. When filter is removed, OnFilterRemoved will delete |this|.
+    stub_->channel()->RemoveFilter(filter_.get());
+  } else {
+    delete this;
+  }
+}
+
+void GpuVideoDecodeAccelerator::OnFilterRemoved() {
+  child_message_loop_->DeleteSoon(FROM_HERE, this);
 }
 
 void GpuVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer(
@@ -328,9 +386,7 @@
     DLOG(ERROR) << "Send(AcceleratedVideoDecoderHostMsg_ResetDone) failed";
 }
 
-void GpuVideoDecodeAccelerator::OnWillDestroyStub() {
-  delete this;
-}
+void GpuVideoDecodeAccelerator::OnWillDestroyStub() { OnDestroy(); }
 
 bool GpuVideoDecodeAccelerator::Send(IPC::Message* message) {
   DCHECK(stub_);
diff --git a/content/common/gpu/media/gpu_video_decode_accelerator.h b/content/common/gpu/media/gpu_video_decode_accelerator.h
index be09302..70d9a44 100644
--- a/content/common/gpu/media/gpu_video_decode_accelerator.h
+++ b/content/common/gpu/media/gpu_video_decode_accelerator.h
@@ -11,10 +11,15 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/shared_memory.h"
 #include "content/common/gpu/gpu_command_buffer_stub.h"
+#include "content/common/gpu/media/video_decode_accelerator_impl.h"
 #include "ipc/ipc_listener.h"
 #include "ipc/ipc_sender.h"
 #include "media/video/video_decode_accelerator.h"
 
+namespace base {
+class MessageLoopProxy;
+}
+
 namespace content {
 
 class GpuVideoDecodeAccelerator
@@ -55,9 +60,12 @@
   // The renderer process handle is valid as long as we have a channel between
   // GPU process and the renderer.
   void Initialize(const media::VideoCodecProfile profile,
-                  IPC::Message* init_done_msg);
+                  IPC::Message* init_done_msg,
+                  const scoped_refptr<base::MessageLoopProxy>& io_message_loop);
 
  private:
+  class MessageFilter;
+
   // Handlers for IPC messages.
   void OnDecode(base::SharedMemoryHandle handle, int32 id, uint32 size);
   void OnAssignPictureBuffers(
@@ -70,6 +78,9 @@
   void OnReset();
   void OnDestroy();
 
+  // Called on IO thread when |filter_| has been removed.
+  void OnFilterRemoved();
+
   // Message to Send() when initialization is done.  Is only non-NULL during
   // initialization and is owned by the IPC channel underlying the
   // GpuCommandBufferStub.
@@ -82,7 +93,7 @@
   GpuCommandBufferStub* stub_;
 
   // The underlying VideoDecodeAccelerator.
-  scoped_ptr<media::VideoDecodeAccelerator> video_decode_accelerator_;
+  scoped_ptr<VideoDecodeAcceleratorImpl> video_decode_accelerator_;
 
   // Callback for making the relevant context current for GL calls.
   // Returns false if failed.
@@ -91,6 +102,12 @@
   // The texture target as requested by ProvidePictureBuffers().
   uint32 texture_target_;
 
+  // The message filter to run VDA::Decode on IO thread if VDA supports it.
+  scoped_refptr<MessageFilter> filter_;
+
+  // GPU child message loop.
+  scoped_refptr<base::MessageLoopProxy> child_message_loop_;
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(GpuVideoDecodeAccelerator);
 };
 
diff --git a/content/common/gpu/media/gpu_video_encode_accelerator.cc b/content/common/gpu/media/gpu_video_encode_accelerator.cc
index 30f63b3..badd512 100644
--- a/content/common/gpu/media/gpu_video_encode_accelerator.cc
+++ b/content/common/gpu/media/gpu_video_encode_accelerator.cc
@@ -163,6 +163,7 @@
           gfx::Rect(input_visible_size_),
           input_visible_size_,
           reinterpret_cast<uint8*>(shm->memory()),
+          buffer_size,
           buffer_handle,
           base::TimeDelta(),
           // It's turtles all the way down...
diff --git a/content/common/gpu/media/vaapi_video_decode_accelerator.h b/content/common/gpu/media/vaapi_video_decode_accelerator.h
index 9ac73da..1755b98 100644
--- a/content/common/gpu/media/vaapi_video_decode_accelerator.h
+++ b/content/common/gpu/media/vaapi_video_decode_accelerator.h
@@ -25,6 +25,7 @@
 #include "content/common/content_export.h"
 #include "content/common/gpu/media/vaapi_h264_decoder.h"
 #include "content/common/gpu/media/vaapi_wrapper.h"
+#include "content/common/gpu/media/video_decode_accelerator_impl.h"
 #include "media/base/bitstream_buffer.h"
 #include "media/video/picture.h"
 #include "media/video/video_decode_accelerator.h"
@@ -40,8 +41,8 @@
 // ChildThread.  A few methods on it are called on the decoder thread which is
 // stopped during |this->Destroy()|, so any tasks posted to the decoder thread
 // can assume |*this| is still alive.  See |weak_this_| below for more details.
-class CONTENT_EXPORT VaapiVideoDecodeAccelerator :
-    public media::VideoDecodeAccelerator {
+class CONTENT_EXPORT VaapiVideoDecodeAccelerator
+    : public VideoDecodeAcceleratorImpl {
  public:
   VaapiVideoDecodeAccelerator(
       Display* x_display, GLXContext glx_context,
diff --git a/content/common/gpu/media/video_decode_accelerator_impl.cc b/content/common/gpu/media/video_decode_accelerator_impl.cc
new file mode 100644
index 0000000..34f40ef
--- /dev/null
+++ b/content/common/gpu/media/video_decode_accelerator_impl.cc
@@ -0,0 +1,15 @@
+// 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 "content/common/gpu/media/video_decode_accelerator_impl.h"
+
+namespace content {
+
+VideoDecodeAcceleratorImpl::VideoDecodeAcceleratorImpl() {}
+
+VideoDecodeAcceleratorImpl::~VideoDecodeAcceleratorImpl() {}
+
+bool VideoDecodeAcceleratorImpl::CanDecodeOnIOThread() { return false; }
+
+}  // namespace content
diff --git a/content/common/gpu/media/video_decode_accelerator_impl.h b/content/common/gpu/media/video_decode_accelerator_impl.h
new file mode 100644
index 0000000..146a8ef
--- /dev/null
+++ b/content/common/gpu/media/video_decode_accelerator_impl.h
@@ -0,0 +1,28 @@
+// 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 CONTENT_COMMON_GPU_MEDIA_VIDEO_DECODE_ACCELERATOR_IMPL_H_
+#define CONTENT_COMMON_GPU_MEDIA_VIDEO_DECODE_ACCELERATOR_IMPL_H_
+
+#include "content/common/content_export.h"
+#include "media/video/video_decode_accelerator.h"
+
+namespace content {
+
+class CONTENT_EXPORT VideoDecodeAcceleratorImpl
+    : public media::VideoDecodeAccelerator {
+ public:
+  VideoDecodeAcceleratorImpl();
+  virtual ~VideoDecodeAcceleratorImpl();
+
+  // Returns true if media::VideoDecodeAccelerator::Decode can run on the IO
+  // thread. Otherwise Decode will run on the GPU child thread. The purpose of
+  // running Decode on the IO thread is to reduce decode latency. Note Decode
+  // should return as soon as possible and not block on the IO thread.
+  virtual bool CanDecodeOnIOThread();
+};
+
+}  // namespace content
+
+#endif  // CONTENT_COMMON_GPU_MEDIA_VIDEO_DECODE_ACCELERATOR_IMPL_H_
diff --git a/content/common/gpu/media/video_decode_accelerator_unittest.cc b/content/common/gpu/media/video_decode_accelerator_unittest.cc
index a180ff6..5b3e114 100644
--- a/content/common/gpu/media/video_decode_accelerator_unittest.cc
+++ b/content/common/gpu/media/video_decode_accelerator_unittest.cc
@@ -32,6 +32,7 @@
 #include "base/file_util.h"
 #include "base/format_macros.h"
 #include "base/md5.h"
+#include "base/message_loop/message_loop_proxy.h"
 #include "base/platform_file.h"
 #include "base/process/process.h"
 #include "base/stl_util.h"
@@ -616,7 +617,8 @@
       static_cast<EGLDisplay>(rendering_helper_->GetGLDisplay()),
       static_cast<EGLContext>(rendering_helper_->GetGLContext()),
       client,
-      base::Bind(&DoNothingReturnTrue)));
+      base::Bind(&DoNothingReturnTrue),
+      base::MessageLoopProxy::current()));
 #elif defined(ARCH_CPU_X86_FAMILY)
   decoder_.reset(new VaapiVideoDecodeAccelerator(
       static_cast<Display*>(rendering_helper_->GetGLDisplay()),
diff --git a/content/common/gpu/stream_texture_manager_android.cc b/content/common/gpu/stream_texture_manager_android.cc
index 3462fe2..0f173e1 100644
--- a/content/common/gpu/stream_texture_manager_android.cc
+++ b/content/common/gpu/stream_texture_manager_android.cc
@@ -9,14 +9,14 @@
 #include "content/common/gpu/gpu_messages.h"
 #include "gpu/command_buffer/service/stream_texture.h"
 #include "ui/gfx/size.h"
-#include "ui/gl/android/surface_texture_bridge.h"
+#include "ui/gl/android/surface_texture.h"
 #include "ui/gl/gl_bindings.h"
 
 namespace content {
 
 StreamTextureManagerAndroid::StreamTextureAndroid::StreamTextureAndroid(
     GpuChannel* channel, int service_id)
-    : surface_texture_bridge_(new gfx::SurfaceTextureBridge(service_id)),
+    : surface_texture_(new gfx::SurfaceTexture(service_id)),
       size_(0, 0),
       has_updated_(false),
       channel_(channel) {
@@ -29,13 +29,13 @@
 void StreamTextureManagerAndroid::StreamTextureAndroid::Update() {
   GLint texture_id = 0;
   glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texture_id);
-  surface_texture_bridge_->UpdateTexImage();
+  surface_texture_->UpdateTexImage();
   glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id);
   if (matrix_callback_.is_null())
     return;
 
   float mtx[16];
-  surface_texture_bridge_->GetTransformMatrix(mtx);
+  surface_texture_->GetTransformMatrix(mtx);
 
   // Only query the matrix once we have bound a valid frame.
   if (has_updated_ && memcmp(current_matrix_, mtx, sizeof(mtx)) != 0) {
@@ -120,7 +120,7 @@
           base::Unretained(this),
           route_id);
     stream_texture->set_matrix_changed_callback(matrix_cb);
-    stream_texture->surface_texture_bridge()->SetFrameAvailableCallback(
+    stream_texture->surface_texture()->SetFrameAvailableCallback(
         frame_cb);
   }
 }
@@ -133,7 +133,7 @@
   if (stream_texture) {
     SurfaceTexturePeer::GetInstance()->EstablishSurfaceTexturePeer(
         process,
-        stream_texture->surface_texture_bridge(),
+        stream_texture->surface_texture(),
         primary_id,
         secondary_id);
   }
diff --git a/content/common/gpu/stream_texture_manager_android.h b/content/common/gpu/stream_texture_manager_android.h
index 9da84d9..e1bfda4 100644
--- a/content/common/gpu/stream_texture_manager_android.h
+++ b/content/common/gpu/stream_texture_manager_android.h
@@ -17,7 +17,7 @@
 
 namespace gfx {
 class Size;
-class SurfaceTextureBridge;
+class SurfaceTexture;
 }
 
 namespace content {
@@ -57,8 +57,8 @@
     virtual void Update() OVERRIDE;
     virtual gfx::Size GetSize() OVERRIDE;
 
-    scoped_refptr<gfx::SurfaceTextureBridge> surface_texture_bridge() {
-        return surface_texture_bridge_;
+    scoped_refptr<gfx::SurfaceTexture> surface_texture() {
+        return surface_texture_;
     }
 
     // Called when a new frame is available.
@@ -78,7 +78,7 @@
     }
 
    private:
-    scoped_refptr<gfx::SurfaceTextureBridge> surface_texture_bridge_;
+    scoped_refptr<gfx::SurfaceTexture> surface_texture_;
 
     // Current transform matrix of the surface texture.
     float current_matrix_[16];
diff --git a/content/common/gpu/surface_capturer.cc b/content/common/gpu/surface_capturer.cc
new file mode 100644
index 0000000..ce02b4d
--- /dev/null
+++ b/content/common/gpu/surface_capturer.cc
@@ -0,0 +1,11 @@
+// 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 "content/common/gpu/surface_capturer.h"
+
+namespace content {
+
+SurfaceCapturer::~SurfaceCapturer() {}
+
+}  // namespace content
diff --git a/content/common/gpu/surface_capturer.h b/content/common/gpu/surface_capturer.h
new file mode 100644
index 0000000..77bfe5a
--- /dev/null
+++ b/content/common/gpu/surface_capturer.h
@@ -0,0 +1,100 @@
+// 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 CONTENT_COMMON_GPU_SURFACE_CAPTURER_H_
+#define CONTENT_COMMON_GPU_SURFACE_CAPTURER_H_
+
+#include "content/common/content_export.h"
+#include "media/base/video_frame.h"
+
+namespace gfx {
+
+class Size;
+class Rect;
+
+}  // namespace ui
+
+namespace content {
+
+// Surface capturer interface.  This interface is implemented by classes
+// that perform image capturing from the backbuffer.
+class CONTENT_EXPORT SurfaceCapturer {
+ public:
+  enum Error {
+    // Invalid argument was passed to an API method.
+    kInvalidArgumentError,
+    // A failure occurred at the GPU process or one of its dependencies.
+    // Examples of such failures include GPU hardware failures, GPU driver
+    // failures, GPU library failures, GPU process programming errors, and so
+    // on.
+    kPlatformFailureError,
+  };
+
+  class CONTENT_EXPORT Client {
+   public:
+    // Callback to notify client of parameters of the backbuffer capture.  Every
+    // time the Client receives this callback, subsequent media::VideoFrames
+    // passed to CopyCaptureToVideoFrame() should mind the new parameters.
+    // Parameters:
+    //  |buffer_size| is the required logical size (in pixels) of the buffer
+    //  to capture to (corresponds to |frame->coded_size()| in
+    //  CopyCaptureToVideoFrame().
+    //  |visible_rect| is the visible subrect of the actual screen capture
+    //  contents in the buffer to capture to (corresponds to
+    //  |frame->visible_rect()| in CopyCaptureToVideoFrame().
+    virtual void NotifyCaptureParameters(const gfx::Size& buffer_size,
+                                         const gfx::Rect& visible_rect) = 0;
+
+    // Callback to notify client that CopyCaptureToVideoFrame() has been
+    // completed for |frame|.  After this call, the capturer will drop all its
+    // outstanding references to |frame|.
+    // Parameters:
+    //  |frame| is the completed copied captured frame.
+    virtual void NotifyCopyCaptureDone(
+        const scoped_refptr<media::VideoFrame>& frame) = 0;
+
+    // Error notification callback.
+    // Parameters:
+    //  |error| is the error to report.
+    virtual void NotifyError(Error error) = 0;
+
+   protected:
+    // Clients are not owned by Capturer instances and should not be deleted
+    // through these pointers.
+    virtual ~Client() {}
+  };
+
+  // Initialize the capturer to a specific configuration.
+  // Parameters:
+  //  |format| is the format to capture to (corresponds to |frame->format()| in
+  //  CopyCaptureToVideoFrame()).  The NotifyCaptureParameters() callback is
+  //  made to the Client on success; on failure, a NotifyError() callback is
+  //  performed.
+  virtual void Initialize(media::VideoFrame::Format format) = 0;
+
+  // Attempt to capture a single frame.  This call is advisory to note to the
+  // SurfaceCapturer that capture should be attempted at this time; success is
+  // not guaranteed.  The most recent captured frame is cached internally, and
+  // its contents returned every time CopyCaptureToVideoFrame() is called.
+  virtual void TryCapture() = 0;
+
+  // Copy the most recent captured contents to |frame|.
+  // Parameters:
+  //  |frame| is the media::VideoFrame to fill with captured contents.
+  virtual void CopyCaptureToVideoFrame(
+      const scoped_refptr<media::VideoFrame>& frame) = 0;
+
+  // Destroys the capturer; all pending inputs and outputs are dropped
+  // immediately and the component is freed.  This call may asynchronously free
+  // system resources, but its client-visible effects are synchronous.  After
+  // this method returns no more callbacks will be made on the client.  Deletes
+  // |this| unconditionally, so make sure to drop all pointers to it!
+  virtual void Destroy() = 0;
+
+  virtual ~SurfaceCapturer();
+};
+
+}  // namespace content
+
+#endif  // CONTENT_COMMON_GPU_SURFACE_CAPTURER_H_
diff --git a/content/common/gpu/testdata/test-25fps.README b/content/common/gpu/testdata/test-25fps.README
index 98aaf4e..1a9ecd3 100644
--- a/content/common/gpu/testdata/test-25fps.README
+++ b/content/common/gpu/testdata/test-25fps.README
@@ -8,7 +8,7 @@
 
 test-25fps.h264.md5:
   MD5s of RGB thumbnail rendered version of test-25fps.h264 decoded with Intel
-  VAAPI on Ivy Bridge and Sandy Bridge and Exynos EVDA.
+  VAAPI on Haswell, Ivy Bridge, Sandy Bridge and Exynos EVDA.
   Written out by video_decode_accelerator_unittest.
   These differ between implementations because color space-converted frames are
   not specified to the last bit and GLES shader/texture filtering
diff --git a/content/common/gpu/testdata/test-25fps.h264.md5 b/content/common/gpu/testdata/test-25fps.h264.md5
index 787bd09..c44facc 100644
--- a/content/common/gpu/testdata/test-25fps.h264.md5
+++ b/content/common/gpu/testdata/test-25fps.h264.md5
@@ -1,3 +1,4 @@
+8cea9604324f350129d98cf8d2961797
 cebd4bb73a92953982a9d62db1d9452f
 3f3b6d9f221710def66101bc1c55fd43
 fb20506aa3c882de66ec17954ba18b09
diff --git a/content/common/media/media_player_messages_android.h b/content/common/media/media_player_messages_android.h
index 0489625..5e076f6 100644
--- a/content/common/media/media_player_messages_android.h
+++ b/content/common/media/media_player_messages_android.h
@@ -29,7 +29,7 @@
 IPC_ENUM_TRAITS(media::MediaKeys::KeyError)
 IPC_ENUM_TRAITS(media::VideoCodec)
 
-IPC_STRUCT_TRAITS_BEGIN(media::MediaPlayerHostMsg_DemuxerReady_Params)
+IPC_STRUCT_TRAITS_BEGIN(media::DemuxerConfigs)
   IPC_STRUCT_TRAITS_MEMBER(audio_codec)
   IPC_STRUCT_TRAITS_MEMBER(audio_channels)
   IPC_STRUCT_TRAITS_MEMBER(audio_sampling_rate)
@@ -45,7 +45,7 @@
   IPC_STRUCT_TRAITS_MEMBER(key_system)
 IPC_STRUCT_TRAITS_END()
 
-IPC_STRUCT_TRAITS_BEGIN(media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params)
+IPC_STRUCT_TRAITS_BEGIN(media::DemuxerData)
   IPC_STRUCT_TRAITS_MEMBER(type)
   IPC_STRUCT_TRAITS_MEMBER(access_units)
 IPC_STRUCT_TRAITS_END()
@@ -192,12 +192,12 @@
 // Inform the media source player that the demuxer is ready.
 IPC_MESSAGE_ROUTED2(MediaPlayerHostMsg_DemuxerReady,
                     int /* player_id */,
-                    media::MediaPlayerHostMsg_DemuxerReady_Params)
+                    media::DemuxerConfigs)
 
 // Sent when the data was read from the ChunkDemuxer.
 IPC_MESSAGE_ROUTED2(MediaPlayerHostMsg_ReadFromDemuxerAck,
                     int /* player_id */,
-                    media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params)
+                    media::DemuxerData)
 
 // Inform the media source player of changed media duration from demuxer.
 IPC_MESSAGE_ROUTED2(MediaPlayerHostMsg_DurationChanged,
diff --git a/content/common/media/midi_messages.h b/content/common/media/midi_messages.h
index 86e5c16..bff0241 100644
--- a/content/common/media/midi_messages.h
+++ b/content/common/media/midi_messages.h
@@ -43,7 +43,7 @@
                      int /* client id */)
 
 IPC_MESSAGE_CONTROL3(MIDIHostMsg_SendData,
-                     int /* port */,
+                     uint32 /* port */,
                      std::vector<uint8> /* data */,
                      double /* timestamp */)
 
@@ -56,9 +56,9 @@
                      media::MIDIPortInfoList /* output ports */)
 
 IPC_MESSAGE_CONTROL3(MIDIMsg_DataReceived,
-                     int /* port */,
+                     uint32 /* port */,
                      std::vector<uint8> /* data */,
                      double /* timestamp */)
 
 IPC_MESSAGE_CONTROL1(MIDIMsg_AcknowledgeSentData,
-                     size_t /* bytes sent */)
+                     uint32 /* bytes sent */)
diff --git a/content/common/sandbox_linux.cc b/content/common/sandbox_linux.cc
index 1746390..c2b3aad 100644
--- a/content/common/sandbox_linux.cc
+++ b/content/common/sandbox_linux.cc
@@ -11,7 +11,7 @@
 #include <limits>
 
 #include "base/bind.h"
-#include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/memory/singleton.h"
diff --git a/content/common/sandbox_seccomp_bpf_linux.cc b/content/common/sandbox_seccomp_bpf_linux.cc
index d6ebdad..1ff54e7 100644
--- a/content/common/sandbox_seccomp_bpf_linux.cc
+++ b/content/common/sandbox_seccomp_bpf_linux.cc
@@ -6,8 +6,6 @@
 #include <dlfcn.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <linux/audit.h>
-#include <linux/filter.h>
 #include <linux/net.h>
 #include <signal.h>
 #include <string.h>
diff --git a/content/common/savable_url_schemes.cc b/content/common/savable_url_schemes.cc
index 0e2c37e..c37b78c 100644
--- a/content/common/savable_url_schemes.cc
+++ b/content/common/savable_url_schemes.cc
@@ -8,11 +8,13 @@
 
 #include "content/public/common/url_constants.h"
 
+namespace content {
+
 namespace {
 
 const char* const kDefaultSavableSchemes[] = {
   chrome::kHttpScheme,
-  chrome::kHttpsScheme,
+  kHttpsScheme,
   chrome::kFileScheme,
   chrome::kFileSystemScheme,
   chrome::kFtpScheme,
@@ -26,8 +28,6 @@
 
 }  // namespace
 
-namespace content {
-
 const char* const* GetSavableSchemesInternal() {
   return g_savable_schemes;
 }
diff --git a/content/common/view_messages.h b/content/common/view_messages.h
index a90e645..6273f84 100644
--- a/content/common/view_messages.h
+++ b/content/common/view_messages.h
@@ -167,7 +167,7 @@
   IPC_STRUCT_TRAITS_MEMBER(link_text)
   IPC_STRUCT_TRAITS_MEMBER(unfiltered_link_url)
   IPC_STRUCT_TRAITS_MEMBER(src_url)
-  IPC_STRUCT_TRAITS_MEMBER(is_image_blocked)
+  IPC_STRUCT_TRAITS_MEMBER(has_image_contents)
   IPC_STRUCT_TRAITS_MEMBER(page_url)
   IPC_STRUCT_TRAITS_MEMBER(keyword_url)
   IPC_STRUCT_TRAITS_MEMBER(frame_url)
@@ -891,7 +891,7 @@
 IPC_MESSAGE_ROUTED0(ViewMsg_SwapBuffers_ACK)
 
 // Tells the render widget that a smooth scroll completed.
-IPC_MESSAGE_ROUTED0(ViewMsg_SmoothScrollCompleted)
+IPC_MESSAGE_ROUTED0(ViewMsg_SyntheticGestureCompleted)
 
 // Tells the renderer to focus the first (last if reverse is true) focusable
 // node.
@@ -1800,11 +1800,12 @@
 IPC_MESSAGE_ROUTED1(ViewHostMsg_RouteMessageEvent,
                     ViewMsg_PostMessage_Params)
 
-IPC_SYNC_MESSAGE_ROUTED4_2(ViewHostMsg_RunJavaScriptMessage,
+IPC_SYNC_MESSAGE_ROUTED5_2(ViewHostMsg_RunJavaScriptMessage,
                            string16     /* in - alert message */,
                            string16     /* in - default prompt */,
                            GURL         /* in - originating page URL */,
                            content::JavaScriptMessageType /* in - type */,
+                           bool         /* in - user_gesture */,
                            bool         /* out - success */,
                            string16     /* out - user_input field */)
 
diff --git a/content/content.gyp b/content/content.gyp
index c68fd3a..9c51df7 100644
--- a/content/content.gyp
+++ b/content/content.gyp
@@ -47,12 +47,12 @@
           'type': 'none',
           'dependencies': [
             'content_browser',
-            'content_child',
             'content_common',
           ],
           'conditions': [
             ['OS != "ios"', {
               'dependencies': [
+                'content_child',
                 'content_gpu',
                 'content_plugin',
                 'content_ppapi_plugin',
@@ -152,29 +152,24 @@
           # Disable c4267 warnings until we fix size_t to int truncations.
           'msvs_disabled_warnings': [ 4267, ],
         },
-        {
-          'target_name': 'content_child',
-          'type': 'static_library',
-          'variables': { 'enable_wexit_time_destructors': 1, },
-          'includes': [
-            'content_child.gypi',
-          ],
-          'conditions': [
-            ['OS != "ios"', {
-              'dependencies': [
-                'content_resources.gyp:content_resources',
-              ],
-            }],
-          ],
-          # Disable c4267 warnings until we fix size_t to int truncations.
-          'msvs_disabled_warnings': [ 4267, ],
-        },
-
       ],
       'conditions': [
         ['OS != "ios"', {
           'targets': [
             {
+              'target_name': 'content_child',
+              'type': 'static_library',
+              'variables': { 'enable_wexit_time_destructors': 1, },
+              'includes': [
+                'content_child.gypi',
+              ],
+              'dependencies': [
+                'content_resources.gyp:content_resources',
+              ],
+              # Disable c4267 warnings until we fix size_t to int truncations.
+              'msvs_disabled_warnings': [ 4267, ],
+            },
+            {
               'target_name': 'content_gpu',
               'type': 'static_library',
               'variables': { 'enable_wexit_time_destructors': 1, },
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index 49e7987..01b33f1 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -27,7 +27,7 @@
     'port/browser/location_provider.h',
     'port/browser/render_view_host_delegate_view.h',
     'port/browser/render_widget_host_view_port.h',
-    'port/browser/smooth_scroll_gesture.h',
+    'port/browser/synthetic_gesture.h',
     'port/browser/web_contents_view_port.h',
     'public/browser/access_token_store.h',
     'public/browser/android/compositor.h',
@@ -108,7 +108,6 @@
     'public/browser/invalidate_type.h',
     'public/browser/javascript_dialog_manager.cc',
     'public/browser/javascript_dialog_manager.h',
-    'public/browser/keyboard_listener.h',
     'public/browser/load_from_memory_cache_details.cc',
     'public/browser/load_from_memory_cache_details.h',
     'public/browser/load_notification_details.h',
@@ -233,8 +232,6 @@
     'browser/accessibility/browser_accessibility_state_impl.h',
     'browser/accessibility/browser_accessibility_win.cc',
     'browser/accessibility/browser_accessibility_win.h',
-    'browser/android/android_browser_process.cc',
-    'browser/android/android_browser_process.h',
     'browser/android/browser_jni_registrar.cc',
     'browser/android/browser_jni_registrar.h',
     'browser/android/browser_media_player_manager.cc',
@@ -399,11 +396,12 @@
     'browser/device_orientation/data_fetcher_impl_win.h',
     'browser/device_orientation/data_fetcher_orientation_android.cc',
     'browser/device_orientation/data_fetcher_orientation_android.h',
+    'browser/device_orientation/data_fetcher_shared_memory.h',
     'browser/device_orientation/data_fetcher_shared_memory_android.cc',
     'browser/device_orientation/data_fetcher_shared_memory_base.cc',
     'browser/device_orientation/data_fetcher_shared_memory_base.h',
     'browser/device_orientation/data_fetcher_shared_memory_default.cc',
-    'browser/device_orientation/data_fetcher_shared_memory.h',
+    'browser/device_orientation/data_fetcher_shared_memory_mac.cc',
     'browser/device_orientation/device_data.h',
     'browser/device_orientation/device_inertial_sensor_service.cc',
     'browser/device_orientation/device_inertial_sensor_service.h',
@@ -530,10 +528,8 @@
     'browser/gamepad/gamepad_standard_mappings_win.cc',
     'browser/gamepad/xbox_data_fetcher_mac.cc',
     'browser/gamepad/xbox_data_fetcher_mac.h',
-    'browser/geolocation/device_data_provider.cc',
-    'browser/geolocation/device_data_provider.h',
-    'browser/geolocation/empty_device_data_provider.cc',
-    'browser/geolocation/empty_device_data_provider.h',
+    'browser/geolocation/empty_wifi_data_provider.cc',
+    'browser/geolocation/empty_wifi_data_provider.h',
     'browser/geolocation/geolocation_dispatcher_host.cc',
     'browser/geolocation/geolocation_dispatcher_host.h',
     'browser/geolocation/geolocation_provider_impl.cc',
@@ -554,6 +550,10 @@
     'browser/geolocation/network_location_request.cc',
     'browser/geolocation/network_location_request.h',
     'browser/geolocation/osx_wifi.h',
+    'browser/geolocation/wifi_data.cc',
+    'browser/geolocation/wifi_data.h',
+    'browser/geolocation/wifi_data_provider.cc',
+    'browser/geolocation/wifi_data_provider.h',
     'browser/geolocation/wifi_data_provider_chromeos.cc',
     'browser/geolocation/wifi_data_provider_chromeos.h',
     'browser/geolocation/wifi_data_provider_common.cc',
@@ -939,8 +939,8 @@
     'browser/renderer_host/pepper/pepper_socket_utils.h',
     'browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.cc',
     'browser/renderer_host/pepper/pepper_tcp_server_socket_message_filter.h',
-    'browser/renderer_host/pepper/pepper_tcp_socket.cc',
-    'browser/renderer_host/pepper/pepper_tcp_socket.h',
+    'browser/renderer_host/pepper/pepper_tcp_socket_message_filter.cc',
+    'browser/renderer_host/pepper/pepper_tcp_socket_message_filter.h',
     'browser/renderer_host/pepper/pepper_truetype_font_list.h',
     'browser/renderer_host/pepper/pepper_truetype_font_list_android.cc',
     'browser/renderer_host/pepper/pepper_truetype_font_list_host.cc',
@@ -950,6 +950,8 @@
     'browser/renderer_host/pepper/pepper_truetype_font_list_win.cc',
     'browser/renderer_host/pepper/pepper_udp_socket_message_filter.cc',
     'browser/renderer_host/pepper/pepper_udp_socket_message_filter.h',
+    'browser/renderer_host/pepper/ssl_context_helper.cc',
+    'browser/renderer_host/pepper/ssl_context_helper.h',
     'browser/renderer_host/popup_menu_helper_mac.h',
     'browser/renderer_host/popup_menu_helper_mac.mm',
     'browser/renderer_host/render_frame_host_impl.cc',
@@ -990,10 +992,10 @@
     'browser/renderer_host/render_widget_host_view_mac.mm',
     'browser/renderer_host/render_widget_host_view_win.cc',
     'browser/renderer_host/render_widget_host_view_win.h',
-    'browser/renderer_host/smooth_scroll_calculator.cc',
-    'browser/renderer_host/smooth_scroll_calculator.h',
-    'browser/renderer_host/smooth_scroll_gesture_controller.cc',
-    'browser/renderer_host/smooth_scroll_gesture_controller.h',
+    'browser/renderer_host/synthetic_gesture_calculator.cc',
+    'browser/renderer_host/synthetic_gesture_calculator.h',
+    'browser/renderer_host/synthetic_gesture_controller.cc',
+    'browser/renderer_host/synthetic_gesture_controller.h',
     'browser/renderer_host/socket_stream_dispatcher_host.cc',
     'browser/renderer_host/socket_stream_dispatcher_host.h',
     'browser/renderer_host/socket_stream_host.cc',
@@ -1004,8 +1006,8 @@
     'browser/renderer_host/text_input_client_mac.mm',
     'browser/renderer_host/text_input_client_message_filter.h',
     'browser/renderer_host/text_input_client_message_filter.mm',
-    'browser/renderer_host/touch_smooth_scroll_gesture_android.cc',
-    'browser/renderer_host/touch_smooth_scroll_gesture_android.h',
+    'browser/renderer_host/generic_touch_gesture_android.cc',
+    'browser/renderer_host/generic_touch_gesture_android.h',
     'browser/renderer_host/touch_smooth_scroll_gesture_aura.cc',
     'browser/renderer_host/touch_smooth_scroll_gesture_aura.h',
     'browser/renderer_host/ui_events_helper.cc',
@@ -1402,6 +1404,9 @@
         '../third_party/mozilla/NSURL+Utils.h',
         '../third_party/mozilla/NSURL+Utils.m',
       ],
+      'sources/': [
+        ['exclude', '^browser/device_orientation/data_fetcher_shared_memory_default.cc$'],
+      ],
       'dependencies': [
         '../third_party/sudden_motion_sensor/sudden_motion_sensor.gyp:sudden_motion_sensor',
       ],
diff --git a/content/content_browser.target.darwin-arm.mk b/content/content_browser.target.darwin-arm.mk
index 59a86ce..26cac6b 100644
--- a/content/content_browser.target.darwin-arm.mk
+++ b/content/content_browser.target.darwin-arm.mk
@@ -80,7 +80,6 @@
 	content/browser/accessibility/browser_accessibility_manager.cc \
 	content/browser/accessibility/browser_accessibility_manager_android.cc \
 	content/browser/accessibility/browser_accessibility_state_impl.cc \
-	content/browser/android/android_browser_process.cc \
 	content/browser/android/browser_jni_registrar.cc \
 	content/browser/android/browser_media_player_manager.cc \
 	content/browser/android/browser_startup_controller.cc \
@@ -203,14 +202,15 @@
 	content/browser/font_list_async.cc \
 	content/browser/gamepad/gamepad_provider.cc \
 	content/browser/gamepad/gamepad_service.cc \
-	content/browser/geolocation/device_data_provider.cc \
-	content/browser/geolocation/empty_device_data_provider.cc \
+	content/browser/geolocation/empty_wifi_data_provider.cc \
 	content/browser/geolocation/geolocation_dispatcher_host.cc \
 	content/browser/geolocation/geolocation_provider_impl.cc \
 	content/browser/geolocation/location_api_adapter_android.cc \
 	content/browser/geolocation/location_arbitrator_impl.cc \
 	content/browser/geolocation/location_provider_android.cc \
 	content/browser/geolocation/location_provider_base.cc \
+	content/browser/geolocation/wifi_data.cc \
+	content/browser/geolocation/wifi_data_provider.cc \
 	content/browser/geolocation/wifi_data_provider_common.cc \
 	content/browser/gpu/browser_gpu_channel_host_factory.cc \
 	content/browser/gpu/compositor_util.cc \
@@ -357,12 +357,12 @@
 	content/browser/renderer_host/render_widget_host_view_android.cc \
 	content/browser/renderer_host/render_widget_host_view_base.cc \
 	content/browser/renderer_host/render_widget_host_view_guest.cc \
-	content/browser/renderer_host/smooth_scroll_calculator.cc \
-	content/browser/renderer_host/smooth_scroll_gesture_controller.cc \
+	content/browser/renderer_host/synthetic_gesture_calculator.cc \
+	content/browser/renderer_host/synthetic_gesture_controller.cc \
 	content/browser/renderer_host/socket_stream_dispatcher_host.cc \
 	content/browser/renderer_host/socket_stream_host.cc \
 	content/browser/renderer_host/surface_texture_transport_client_android.cc \
-	content/browser/renderer_host/touch_smooth_scroll_gesture_android.cc \
+	content/browser/renderer_host/generic_touch_gesture_android.cc \
 	content/browser/resolve_proxy_msg_helper.cc \
 	content/browser/resource_context_impl.cc \
 	content/browser/site_instance_impl.cc \
diff --git a/content/content_browser.target.darwin-mips.mk b/content/content_browser.target.darwin-mips.mk
index ceb4a39..c938859 100644
--- a/content/content_browser.target.darwin-mips.mk
+++ b/content/content_browser.target.darwin-mips.mk
@@ -80,7 +80,6 @@
 	content/browser/accessibility/browser_accessibility_manager.cc \
 	content/browser/accessibility/browser_accessibility_manager_android.cc \
 	content/browser/accessibility/browser_accessibility_state_impl.cc \
-	content/browser/android/android_browser_process.cc \
 	content/browser/android/browser_jni_registrar.cc \
 	content/browser/android/browser_media_player_manager.cc \
 	content/browser/android/browser_startup_controller.cc \
@@ -203,14 +202,15 @@
 	content/browser/font_list_async.cc \
 	content/browser/gamepad/gamepad_provider.cc \
 	content/browser/gamepad/gamepad_service.cc \
-	content/browser/geolocation/device_data_provider.cc \
-	content/browser/geolocation/empty_device_data_provider.cc \
+	content/browser/geolocation/empty_wifi_data_provider.cc \
 	content/browser/geolocation/geolocation_dispatcher_host.cc \
 	content/browser/geolocation/geolocation_provider_impl.cc \
 	content/browser/geolocation/location_api_adapter_android.cc \
 	content/browser/geolocation/location_arbitrator_impl.cc \
 	content/browser/geolocation/location_provider_android.cc \
 	content/browser/geolocation/location_provider_base.cc \
+	content/browser/geolocation/wifi_data.cc \
+	content/browser/geolocation/wifi_data_provider.cc \
 	content/browser/geolocation/wifi_data_provider_common.cc \
 	content/browser/gpu/browser_gpu_channel_host_factory.cc \
 	content/browser/gpu/compositor_util.cc \
@@ -357,12 +357,12 @@
 	content/browser/renderer_host/render_widget_host_view_android.cc \
 	content/browser/renderer_host/render_widget_host_view_base.cc \
 	content/browser/renderer_host/render_widget_host_view_guest.cc \
-	content/browser/renderer_host/smooth_scroll_calculator.cc \
-	content/browser/renderer_host/smooth_scroll_gesture_controller.cc \
+	content/browser/renderer_host/synthetic_gesture_calculator.cc \
+	content/browser/renderer_host/synthetic_gesture_controller.cc \
 	content/browser/renderer_host/socket_stream_dispatcher_host.cc \
 	content/browser/renderer_host/socket_stream_host.cc \
 	content/browser/renderer_host/surface_texture_transport_client_android.cc \
-	content/browser/renderer_host/touch_smooth_scroll_gesture_android.cc \
+	content/browser/renderer_host/generic_touch_gesture_android.cc \
 	content/browser/resolve_proxy_msg_helper.cc \
 	content/browser/resource_context_impl.cc \
 	content/browser/site_instance_impl.cc \
diff --git a/content/content_browser.target.darwin-x86.mk b/content/content_browser.target.darwin-x86.mk
index 9dd9f43..006428e 100644
--- a/content/content_browser.target.darwin-x86.mk
+++ b/content/content_browser.target.darwin-x86.mk
@@ -80,7 +80,6 @@
 	content/browser/accessibility/browser_accessibility_manager.cc \
 	content/browser/accessibility/browser_accessibility_manager_android.cc \
 	content/browser/accessibility/browser_accessibility_state_impl.cc \
-	content/browser/android/android_browser_process.cc \
 	content/browser/android/browser_jni_registrar.cc \
 	content/browser/android/browser_media_player_manager.cc \
 	content/browser/android/browser_startup_controller.cc \
@@ -203,14 +202,15 @@
 	content/browser/font_list_async.cc \
 	content/browser/gamepad/gamepad_provider.cc \
 	content/browser/gamepad/gamepad_service.cc \
-	content/browser/geolocation/device_data_provider.cc \
-	content/browser/geolocation/empty_device_data_provider.cc \
+	content/browser/geolocation/empty_wifi_data_provider.cc \
 	content/browser/geolocation/geolocation_dispatcher_host.cc \
 	content/browser/geolocation/geolocation_provider_impl.cc \
 	content/browser/geolocation/location_api_adapter_android.cc \
 	content/browser/geolocation/location_arbitrator_impl.cc \
 	content/browser/geolocation/location_provider_android.cc \
 	content/browser/geolocation/location_provider_base.cc \
+	content/browser/geolocation/wifi_data.cc \
+	content/browser/geolocation/wifi_data_provider.cc \
 	content/browser/geolocation/wifi_data_provider_common.cc \
 	content/browser/gpu/browser_gpu_channel_host_factory.cc \
 	content/browser/gpu/compositor_util.cc \
@@ -357,12 +357,12 @@
 	content/browser/renderer_host/render_widget_host_view_android.cc \
 	content/browser/renderer_host/render_widget_host_view_base.cc \
 	content/browser/renderer_host/render_widget_host_view_guest.cc \
-	content/browser/renderer_host/smooth_scroll_calculator.cc \
-	content/browser/renderer_host/smooth_scroll_gesture_controller.cc \
+	content/browser/renderer_host/synthetic_gesture_calculator.cc \
+	content/browser/renderer_host/synthetic_gesture_controller.cc \
 	content/browser/renderer_host/socket_stream_dispatcher_host.cc \
 	content/browser/renderer_host/socket_stream_host.cc \
 	content/browser/renderer_host/surface_texture_transport_client_android.cc \
-	content/browser/renderer_host/touch_smooth_scroll_gesture_android.cc \
+	content/browser/renderer_host/generic_touch_gesture_android.cc \
 	content/browser/resolve_proxy_msg_helper.cc \
 	content/browser/resource_context_impl.cc \
 	content/browser/site_instance_impl.cc \
diff --git a/content/content_browser.target.linux-arm.mk b/content/content_browser.target.linux-arm.mk
index 59a86ce..26cac6b 100644
--- a/content/content_browser.target.linux-arm.mk
+++ b/content/content_browser.target.linux-arm.mk
@@ -80,7 +80,6 @@
 	content/browser/accessibility/browser_accessibility_manager.cc \
 	content/browser/accessibility/browser_accessibility_manager_android.cc \
 	content/browser/accessibility/browser_accessibility_state_impl.cc \
-	content/browser/android/android_browser_process.cc \
 	content/browser/android/browser_jni_registrar.cc \
 	content/browser/android/browser_media_player_manager.cc \
 	content/browser/android/browser_startup_controller.cc \
@@ -203,14 +202,15 @@
 	content/browser/font_list_async.cc \
 	content/browser/gamepad/gamepad_provider.cc \
 	content/browser/gamepad/gamepad_service.cc \
-	content/browser/geolocation/device_data_provider.cc \
-	content/browser/geolocation/empty_device_data_provider.cc \
+	content/browser/geolocation/empty_wifi_data_provider.cc \
 	content/browser/geolocation/geolocation_dispatcher_host.cc \
 	content/browser/geolocation/geolocation_provider_impl.cc \
 	content/browser/geolocation/location_api_adapter_android.cc \
 	content/browser/geolocation/location_arbitrator_impl.cc \
 	content/browser/geolocation/location_provider_android.cc \
 	content/browser/geolocation/location_provider_base.cc \
+	content/browser/geolocation/wifi_data.cc \
+	content/browser/geolocation/wifi_data_provider.cc \
 	content/browser/geolocation/wifi_data_provider_common.cc \
 	content/browser/gpu/browser_gpu_channel_host_factory.cc \
 	content/browser/gpu/compositor_util.cc \
@@ -357,12 +357,12 @@
 	content/browser/renderer_host/render_widget_host_view_android.cc \
 	content/browser/renderer_host/render_widget_host_view_base.cc \
 	content/browser/renderer_host/render_widget_host_view_guest.cc \
-	content/browser/renderer_host/smooth_scroll_calculator.cc \
-	content/browser/renderer_host/smooth_scroll_gesture_controller.cc \
+	content/browser/renderer_host/synthetic_gesture_calculator.cc \
+	content/browser/renderer_host/synthetic_gesture_controller.cc \
 	content/browser/renderer_host/socket_stream_dispatcher_host.cc \
 	content/browser/renderer_host/socket_stream_host.cc \
 	content/browser/renderer_host/surface_texture_transport_client_android.cc \
-	content/browser/renderer_host/touch_smooth_scroll_gesture_android.cc \
+	content/browser/renderer_host/generic_touch_gesture_android.cc \
 	content/browser/resolve_proxy_msg_helper.cc \
 	content/browser/resource_context_impl.cc \
 	content/browser/site_instance_impl.cc \
diff --git a/content/content_browser.target.linux-mips.mk b/content/content_browser.target.linux-mips.mk
index ceb4a39..c938859 100644
--- a/content/content_browser.target.linux-mips.mk
+++ b/content/content_browser.target.linux-mips.mk
@@ -80,7 +80,6 @@
 	content/browser/accessibility/browser_accessibility_manager.cc \
 	content/browser/accessibility/browser_accessibility_manager_android.cc \
 	content/browser/accessibility/browser_accessibility_state_impl.cc \
-	content/browser/android/android_browser_process.cc \
 	content/browser/android/browser_jni_registrar.cc \
 	content/browser/android/browser_media_player_manager.cc \
 	content/browser/android/browser_startup_controller.cc \
@@ -203,14 +202,15 @@
 	content/browser/font_list_async.cc \
 	content/browser/gamepad/gamepad_provider.cc \
 	content/browser/gamepad/gamepad_service.cc \
-	content/browser/geolocation/device_data_provider.cc \
-	content/browser/geolocation/empty_device_data_provider.cc \
+	content/browser/geolocation/empty_wifi_data_provider.cc \
 	content/browser/geolocation/geolocation_dispatcher_host.cc \
 	content/browser/geolocation/geolocation_provider_impl.cc \
 	content/browser/geolocation/location_api_adapter_android.cc \
 	content/browser/geolocation/location_arbitrator_impl.cc \
 	content/browser/geolocation/location_provider_android.cc \
 	content/browser/geolocation/location_provider_base.cc \
+	content/browser/geolocation/wifi_data.cc \
+	content/browser/geolocation/wifi_data_provider.cc \
 	content/browser/geolocation/wifi_data_provider_common.cc \
 	content/browser/gpu/browser_gpu_channel_host_factory.cc \
 	content/browser/gpu/compositor_util.cc \
@@ -357,12 +357,12 @@
 	content/browser/renderer_host/render_widget_host_view_android.cc \
 	content/browser/renderer_host/render_widget_host_view_base.cc \
 	content/browser/renderer_host/render_widget_host_view_guest.cc \
-	content/browser/renderer_host/smooth_scroll_calculator.cc \
-	content/browser/renderer_host/smooth_scroll_gesture_controller.cc \
+	content/browser/renderer_host/synthetic_gesture_calculator.cc \
+	content/browser/renderer_host/synthetic_gesture_controller.cc \
 	content/browser/renderer_host/socket_stream_dispatcher_host.cc \
 	content/browser/renderer_host/socket_stream_host.cc \
 	content/browser/renderer_host/surface_texture_transport_client_android.cc \
-	content/browser/renderer_host/touch_smooth_scroll_gesture_android.cc \
+	content/browser/renderer_host/generic_touch_gesture_android.cc \
 	content/browser/resolve_proxy_msg_helper.cc \
 	content/browser/resource_context_impl.cc \
 	content/browser/site_instance_impl.cc \
diff --git a/content/content_browser.target.linux-x86.mk b/content/content_browser.target.linux-x86.mk
index 9dd9f43..006428e 100644
--- a/content/content_browser.target.linux-x86.mk
+++ b/content/content_browser.target.linux-x86.mk
@@ -80,7 +80,6 @@
 	content/browser/accessibility/browser_accessibility_manager.cc \
 	content/browser/accessibility/browser_accessibility_manager_android.cc \
 	content/browser/accessibility/browser_accessibility_state_impl.cc \
-	content/browser/android/android_browser_process.cc \
 	content/browser/android/browser_jni_registrar.cc \
 	content/browser/android/browser_media_player_manager.cc \
 	content/browser/android/browser_startup_controller.cc \
@@ -203,14 +202,15 @@
 	content/browser/font_list_async.cc \
 	content/browser/gamepad/gamepad_provider.cc \
 	content/browser/gamepad/gamepad_service.cc \
-	content/browser/geolocation/device_data_provider.cc \
-	content/browser/geolocation/empty_device_data_provider.cc \
+	content/browser/geolocation/empty_wifi_data_provider.cc \
 	content/browser/geolocation/geolocation_dispatcher_host.cc \
 	content/browser/geolocation/geolocation_provider_impl.cc \
 	content/browser/geolocation/location_api_adapter_android.cc \
 	content/browser/geolocation/location_arbitrator_impl.cc \
 	content/browser/geolocation/location_provider_android.cc \
 	content/browser/geolocation/location_provider_base.cc \
+	content/browser/geolocation/wifi_data.cc \
+	content/browser/geolocation/wifi_data_provider.cc \
 	content/browser/geolocation/wifi_data_provider_common.cc \
 	content/browser/gpu/browser_gpu_channel_host_factory.cc \
 	content/browser/gpu/compositor_util.cc \
@@ -357,12 +357,12 @@
 	content/browser/renderer_host/render_widget_host_view_android.cc \
 	content/browser/renderer_host/render_widget_host_view_base.cc \
 	content/browser/renderer_host/render_widget_host_view_guest.cc \
-	content/browser/renderer_host/smooth_scroll_calculator.cc \
-	content/browser/renderer_host/smooth_scroll_gesture_controller.cc \
+	content/browser/renderer_host/synthetic_gesture_calculator.cc \
+	content/browser/renderer_host/synthetic_gesture_controller.cc \
 	content/browser/renderer_host/socket_stream_dispatcher_host.cc \
 	content/browser/renderer_host/socket_stream_host.cc \
 	content/browser/renderer_host/surface_texture_transport_client_android.cc \
-	content/browser/renderer_host/touch_smooth_scroll_gesture_android.cc \
+	content/browser/renderer_host/generic_touch_gesture_android.cc \
 	content/browser/resolve_proxy_msg_helper.cc \
 	content/browser/resource_context_impl.cc \
 	content/browser/site_instance_impl.cc \
diff --git a/content/content_child.gypi b/content/content_child.gypi
index 3fe2250..aac52fa 100644
--- a/content/content_child.gypi
+++ b/content/content_child.gypi
@@ -120,6 +120,8 @@
     'child/resource_dispatcher.h',
     'child/runtime_features.cc',
     'child/runtime_features.h',
+    'child/site_isolation_policy.cc',
+    'child/site_isolation_policy.h',
     'child/socket_stream_dispatcher.cc',
     'child/socket_stream_dispatcher.h',
     'child/thread_safe_sender.cc',
diff --git a/content/content_child.target.darwin-arm.mk b/content/content_child.target.darwin-arm.mk
index 9c49ca4..177d6f0 100644
--- a/content/content_child.target.darwin-arm.mk
+++ b/content/content_child.target.darwin-arm.mk
@@ -11,10 +11,10 @@
 
 # Make sure our deps are built first.
 GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
 	$(call intermediates-dir-for,GYP,skia_skia_gyp)/skia.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_library_gyp)/skia_skia_library_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
-	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
 	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_gyp)/blink.stamp \
 	$(call intermediates-dir-for,GYP,third_party_npapi_npapi_gyp)/npapi.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,webkit_support_glue_child_gyp)/webkit_support_glue_child_gyp.a
@@ -65,6 +65,7 @@
 	content/child/request_extra_data.cc \
 	content/child/resource_dispatcher.cc \
 	content/child/runtime_features.cc \
+	content/child/site_isolation_policy.cc \
 	content/child/socket_stream_dispatcher.cc \
 	content/child/thread_safe_sender.cc \
 	content/child/web_database_observer_impl.cc \
@@ -150,6 +151,7 @@
 	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
+	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
 	$(LOCAL_PATH)/third_party/skia/include/config \
@@ -165,7 +167,6 @@
 	$(LOCAL_PATH)/skia/ext \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/v8/include \
 	$(LOCAL_PATH)/third_party/npapi \
@@ -265,6 +266,7 @@
 	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
+	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
 	$(LOCAL_PATH)/third_party/skia/include/config \
@@ -280,7 +282,6 @@
 	$(LOCAL_PATH)/skia/ext \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/v8/include \
 	$(LOCAL_PATH)/third_party/npapi \
diff --git a/content/content_child.target.darwin-mips.mk b/content/content_child.target.darwin-mips.mk
index 821477e..f84ef11 100644
--- a/content/content_child.target.darwin-mips.mk
+++ b/content/content_child.target.darwin-mips.mk
@@ -11,10 +11,10 @@
 
 # Make sure our deps are built first.
 GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
 	$(call intermediates-dir-for,GYP,skia_skia_gyp)/skia.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_library_gyp)/skia_skia_library_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
-	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
 	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_gyp)/blink.stamp \
 	$(call intermediates-dir-for,GYP,third_party_npapi_npapi_gyp)/npapi.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,webkit_support_glue_child_gyp)/webkit_support_glue_child_gyp.a
@@ -65,6 +65,7 @@
 	content/child/request_extra_data.cc \
 	content/child/resource_dispatcher.cc \
 	content/child/runtime_features.cc \
+	content/child/site_isolation_policy.cc \
 	content/child/socket_stream_dispatcher.cc \
 	content/child/thread_safe_sender.cc \
 	content/child/web_database_observer_impl.cc \
@@ -149,6 +150,7 @@
 	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
+	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
 	$(LOCAL_PATH)/third_party/skia/include/config \
@@ -164,7 +166,6 @@
 	$(LOCAL_PATH)/skia/ext \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/v8/include \
 	$(LOCAL_PATH)/third_party/npapi \
@@ -263,6 +264,7 @@
 	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
+	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
 	$(LOCAL_PATH)/third_party/skia/include/config \
@@ -278,7 +280,6 @@
 	$(LOCAL_PATH)/skia/ext \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/v8/include \
 	$(LOCAL_PATH)/third_party/npapi \
diff --git a/content/content_child.target.darwin-x86.mk b/content/content_child.target.darwin-x86.mk
index ad31966..465cb07 100644
--- a/content/content_child.target.darwin-x86.mk
+++ b/content/content_child.target.darwin-x86.mk
@@ -11,10 +11,10 @@
 
 # Make sure our deps are built first.
 GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
 	$(call intermediates-dir-for,GYP,skia_skia_gyp)/skia.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_library_gyp)/skia_skia_library_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
-	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
 	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_gyp)/blink.stamp \
 	$(call intermediates-dir-for,GYP,third_party_npapi_npapi_gyp)/npapi.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,webkit_support_glue_child_gyp)/webkit_support_glue_child_gyp.a
@@ -65,6 +65,7 @@
 	content/child/request_extra_data.cc \
 	content/child/resource_dispatcher.cc \
 	content/child/runtime_features.cc \
+	content/child/site_isolation_policy.cc \
 	content/child/socket_stream_dispatcher.cc \
 	content/child/thread_safe_sender.cc \
 	content/child/web_database_observer_impl.cc \
@@ -151,6 +152,7 @@
 	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
+	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
 	$(LOCAL_PATH)/third_party/skia/include/config \
@@ -166,7 +168,6 @@
 	$(LOCAL_PATH)/skia/ext \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/v8/include \
 	$(LOCAL_PATH)/third_party/npapi \
@@ -268,6 +269,7 @@
 	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
+	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
 	$(LOCAL_PATH)/third_party/skia/include/config \
@@ -283,7 +285,6 @@
 	$(LOCAL_PATH)/skia/ext \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/v8/include \
 	$(LOCAL_PATH)/third_party/npapi \
diff --git a/content/content_child.target.linux-arm.mk b/content/content_child.target.linux-arm.mk
index 9c49ca4..177d6f0 100644
--- a/content/content_child.target.linux-arm.mk
+++ b/content/content_child.target.linux-arm.mk
@@ -11,10 +11,10 @@
 
 # Make sure our deps are built first.
 GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
 	$(call intermediates-dir-for,GYP,skia_skia_gyp)/skia.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_library_gyp)/skia_skia_library_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
-	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
 	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_gyp)/blink.stamp \
 	$(call intermediates-dir-for,GYP,third_party_npapi_npapi_gyp)/npapi.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,webkit_support_glue_child_gyp)/webkit_support_glue_child_gyp.a
@@ -65,6 +65,7 @@
 	content/child/request_extra_data.cc \
 	content/child/resource_dispatcher.cc \
 	content/child/runtime_features.cc \
+	content/child/site_isolation_policy.cc \
 	content/child/socket_stream_dispatcher.cc \
 	content/child/thread_safe_sender.cc \
 	content/child/web_database_observer_impl.cc \
@@ -150,6 +151,7 @@
 	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
+	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
 	$(LOCAL_PATH)/third_party/skia/include/config \
@@ -165,7 +167,6 @@
 	$(LOCAL_PATH)/skia/ext \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/v8/include \
 	$(LOCAL_PATH)/third_party/npapi \
@@ -265,6 +266,7 @@
 	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
+	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
 	$(LOCAL_PATH)/third_party/skia/include/config \
@@ -280,7 +282,6 @@
 	$(LOCAL_PATH)/skia/ext \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/v8/include \
 	$(LOCAL_PATH)/third_party/npapi \
diff --git a/content/content_child.target.linux-mips.mk b/content/content_child.target.linux-mips.mk
index 821477e..f84ef11 100644
--- a/content/content_child.target.linux-mips.mk
+++ b/content/content_child.target.linux-mips.mk
@@ -11,10 +11,10 @@
 
 # Make sure our deps are built first.
 GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
 	$(call intermediates-dir-for,GYP,skia_skia_gyp)/skia.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_library_gyp)/skia_skia_library_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
-	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
 	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_gyp)/blink.stamp \
 	$(call intermediates-dir-for,GYP,third_party_npapi_npapi_gyp)/npapi.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,webkit_support_glue_child_gyp)/webkit_support_glue_child_gyp.a
@@ -65,6 +65,7 @@
 	content/child/request_extra_data.cc \
 	content/child/resource_dispatcher.cc \
 	content/child/runtime_features.cc \
+	content/child/site_isolation_policy.cc \
 	content/child/socket_stream_dispatcher.cc \
 	content/child/thread_safe_sender.cc \
 	content/child/web_database_observer_impl.cc \
@@ -149,6 +150,7 @@
 	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
+	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
 	$(LOCAL_PATH)/third_party/skia/include/config \
@@ -164,7 +166,6 @@
 	$(LOCAL_PATH)/skia/ext \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/v8/include \
 	$(LOCAL_PATH)/third_party/npapi \
@@ -263,6 +264,7 @@
 	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
+	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
 	$(LOCAL_PATH)/third_party/skia/include/config \
@@ -278,7 +280,6 @@
 	$(LOCAL_PATH)/skia/ext \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/v8/include \
 	$(LOCAL_PATH)/third_party/npapi \
diff --git a/content/content_child.target.linux-x86.mk b/content/content_child.target.linux-x86.mk
index ad31966..465cb07 100644
--- a/content/content_child.target.linux-x86.mk
+++ b/content/content_child.target.linux-x86.mk
@@ -11,10 +11,10 @@
 
 # Make sure our deps are built first.
 GYP_TARGET_DEPENDENCIES := \
+	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
 	$(call intermediates-dir-for,GYP,skia_skia_gyp)/skia.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,skia_skia_library_gyp)/skia_skia_library_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
-	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
 	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_gyp)/blink.stamp \
 	$(call intermediates-dir-for,GYP,third_party_npapi_npapi_gyp)/npapi.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,webkit_support_glue_child_gyp)/webkit_support_glue_child_gyp.a
@@ -65,6 +65,7 @@
 	content/child/request_extra_data.cc \
 	content/child/resource_dispatcher.cc \
 	content/child/runtime_features.cc \
+	content/child/site_isolation_policy.cc \
 	content/child/socket_stream_dispatcher.cc \
 	content/child/thread_safe_sender.cc \
 	content/child/web_database_observer_impl.cc \
@@ -151,6 +152,7 @@
 	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
+	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
 	$(LOCAL_PATH)/third_party/skia/include/config \
@@ -166,7 +168,6 @@
 	$(LOCAL_PATH)/skia/ext \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/v8/include \
 	$(LOCAL_PATH)/third_party/npapi \
@@ -268,6 +269,7 @@
 	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/third_party/khronos \
 	$(LOCAL_PATH)/gpu \
+	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/skia/src/core \
 	$(LOCAL_PATH)/skia/config \
 	$(LOCAL_PATH)/third_party/skia/include/config \
@@ -283,7 +285,6 @@
 	$(LOCAL_PATH)/skia/ext \
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
-	$(gyp_shared_intermediate_dir)/content \
 	$(LOCAL_PATH)/third_party/WebKit \
 	$(LOCAL_PATH)/v8/include \
 	$(LOCAL_PATH)/third_party/npapi \
diff --git a/content/content_common.gypi b/content/content_common.gypi
index 18ecdea..409a9d8 100644
--- a/content/content_common.gypi
+++ b/content/content_common.gypi
@@ -6,7 +6,6 @@
   'dependencies': [
     '../base/base.gyp:base',
     '../components/tracing.gyp:tracing',
-    '../media/media.gyp:media',
     '../net/net.gyp:net',
     '../skia/skia.gyp:skia',
     '../third_party/icu/icu.gyp:icuuc',
@@ -193,6 +192,8 @@
     'common/gpu/client/gl_helper.h',
     'common/gpu/client/gl_helper_scaling.cc',
     'common/gpu/client/gl_helper_scaling.h',
+    'common/gpu/client/gl_surface_capturer_host.cc',
+    'common/gpu/client/gl_surface_capturer_host.h',
     'common/gpu/client/gpu_channel_host.cc',
     'common/gpu/client/gpu_channel_host.h',
     'common/gpu/client/gpu_video_decode_accelerator_host.cc',
@@ -220,29 +221,35 @@
     'common/gpu/gpu_process_launch_causes.h',
     'common/gpu/gpu_rendering_stats.cc',
     'common/gpu/gpu_rendering_stats.h',
-    'common/gpu/gpu_surface_lookup.h',
     'common/gpu/gpu_surface_lookup.cc',
-    'common/gpu/stream_texture_manager_android.cc',
-    'common/gpu/stream_texture_manager_android.h',
+    'common/gpu/gpu_surface_lookup.h',
     'common/gpu/gpu_watchdog.h',
-    'common/gpu/image_transport_surface.h',
     'common/gpu/image_transport_surface.cc',
+    'common/gpu/image_transport_surface.h',
     'common/gpu/image_transport_surface_android.cc',
     'common/gpu/image_transport_surface_linux.cc',
     'common/gpu/image_transport_surface_mac.cc',
     'common/gpu/image_transport_surface_win.cc',
-    'common/gpu/media/h264_bit_reader.cc',
-    'common/gpu/media/h264_bit_reader.h',
-    'common/gpu/media/h264_parser.cc',
-    'common/gpu/media/h264_parser.h',
+    'common/gpu/media/gl_surface_capturer.cc',
+    'common/gpu/media/gl_surface_capturer.h',
     'common/gpu/media/gpu_video_decode_accelerator.cc',
     'common/gpu/media/gpu_video_decode_accelerator.h',
     'common/gpu/media/gpu_video_encode_accelerator.cc',
     'common/gpu/media/gpu_video_encode_accelerator.h',
-    'common/gpu/sync_point_manager.h',
+    'common/gpu/media/video_decode_accelerator_impl.cc',
+    'common/gpu/media/video_decode_accelerator_impl.h',
+    'common/gpu/media/h264_bit_reader.cc',
+    'common/gpu/media/h264_bit_reader.h',
+    'common/gpu/media/h264_parser.cc',
+    'common/gpu/media/h264_parser.h',
+    'common/gpu/stream_texture_manager_android.cc',
+    'common/gpu/stream_texture_manager_android.h',
+    'common/gpu/surface_capturer.cc',
+    'common/gpu/surface_capturer.h',
     'common/gpu/sync_point_manager.cc',
-    'common/gpu/texture_image_transport_surface.h',
+    'common/gpu/sync_point_manager.h',
     'common/gpu/texture_image_transport_surface.cc',
+    'common/gpu/texture_image_transport_surface.h',
     'common/handle_enumerator_win.cc',
     'common/handle_enumerator_win.h',
     'common/image_messages.h',
@@ -395,8 +402,8 @@
         '../gpu/gpu.gyp:gles2_implementation',
         '../gpu/gpu.gyp:gpu_ipc',
         '../ipc/ipc.gyp:ipc',
+        '../media/media.gyp:media',
         '../media/media.gyp:shared_memory_support',
-        '../third_party/WebKit/public/blink.gyp:blink_minimal',
         '../ui/gl/gl.gyp:gl',
         '../webkit/common/webkit_common.gyp:webkit_common',
         '../webkit/storage_browser.gyp:webkit_storage_browser',
diff --git a/content/content_common.target.darwin-arm.mk b/content/content_common.target.darwin-arm.mk
index 0aced76..88ef0eb 100644
--- a/content/content_common.target.darwin-arm.mk
+++ b/content/content_common.target.darwin-arm.mk
@@ -16,7 +16,6 @@
 	$(call intermediates-dir-for,GYP,third_party_icu_icuuc_gyp)/icuuc.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
 	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
-	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,webkit_support_glue_gyp)/webkit_support_glue_gyp.a \
 	$(call intermediates-dir-for,GYP,content_content_jni_headers_gyp)/content_jni_headers.stamp \
@@ -86,6 +85,7 @@
 	content/common/gpu/client/context_provider_command_buffer.cc \
 	content/common/gpu/client/gl_helper.cc \
 	content/common/gpu/client/gl_helper_scaling.cc \
+	content/common/gpu/client/gl_surface_capturer_host.cc \
 	content/common/gpu/client/gpu_channel_host.cc \
 	content/common/gpu/client/gpu_video_decode_accelerator_host.cc \
 	content/common/gpu/client/gpu_video_encode_accelerator_host.cc \
@@ -98,13 +98,16 @@
 	content/common/gpu/gpu_memory_tracking.cc \
 	content/common/gpu/gpu_rendering_stats.cc \
 	content/common/gpu/gpu_surface_lookup.cc \
-	content/common/gpu/stream_texture_manager_android.cc \
 	content/common/gpu/image_transport_surface.cc \
 	content/common/gpu/image_transport_surface_android.cc \
-	content/common/gpu/media/h264_bit_reader.cc \
-	content/common/gpu/media/h264_parser.cc \
+	content/common/gpu/media/gl_surface_capturer.cc \
 	content/common/gpu/media/gpu_video_decode_accelerator.cc \
 	content/common/gpu/media/gpu_video_encode_accelerator.cc \
+	content/common/gpu/media/video_decode_accelerator_impl.cc \
+	content/common/gpu/media/h264_bit_reader.cc \
+	content/common/gpu/media/h264_parser.cc \
+	content/common/gpu/stream_texture_manager_android.cc \
+	content/common/gpu/surface_capturer.cc \
 	content/common/gpu/sync_point_manager.cc \
 	content/common/gpu/texture_image_transport_surface.cc \
 	content/common/indexed_db/indexed_db_key.cc \
@@ -188,7 +191,6 @@
 	'-DENABLE_GPU=1' \
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DPOSIX_AVOID_MMAP' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
@@ -198,6 +200,7 @@
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
 	'-DU_USING_ICU_NAMESPACE=0' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
@@ -235,7 +238,6 @@
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
 	$(gyp_shared_intermediate_dir)/content \
-	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/frameworks/wilhelm/include \
@@ -304,7 +306,6 @@
 	'-DENABLE_GPU=1' \
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DPOSIX_AVOID_MMAP' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
@@ -314,6 +315,7 @@
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
 	'-DU_USING_ICU_NAMESPACE=0' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
@@ -352,7 +354,6 @@
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
 	$(gyp_shared_intermediate_dir)/content \
-	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/frameworks/wilhelm/include \
diff --git a/content/content_common.target.darwin-mips.mk b/content/content_common.target.darwin-mips.mk
index 6fdb22c..e56a0ab 100644
--- a/content/content_common.target.darwin-mips.mk
+++ b/content/content_common.target.darwin-mips.mk
@@ -16,7 +16,6 @@
 	$(call intermediates-dir-for,GYP,third_party_icu_icuuc_gyp)/icuuc.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
 	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
-	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,webkit_support_glue_gyp)/webkit_support_glue_gyp.a \
 	$(call intermediates-dir-for,GYP,content_content_jni_headers_gyp)/content_jni_headers.stamp \
@@ -86,6 +85,7 @@
 	content/common/gpu/client/context_provider_command_buffer.cc \
 	content/common/gpu/client/gl_helper.cc \
 	content/common/gpu/client/gl_helper_scaling.cc \
+	content/common/gpu/client/gl_surface_capturer_host.cc \
 	content/common/gpu/client/gpu_channel_host.cc \
 	content/common/gpu/client/gpu_video_decode_accelerator_host.cc \
 	content/common/gpu/client/gpu_video_encode_accelerator_host.cc \
@@ -98,13 +98,16 @@
 	content/common/gpu/gpu_memory_tracking.cc \
 	content/common/gpu/gpu_rendering_stats.cc \
 	content/common/gpu/gpu_surface_lookup.cc \
-	content/common/gpu/stream_texture_manager_android.cc \
 	content/common/gpu/image_transport_surface.cc \
 	content/common/gpu/image_transport_surface_android.cc \
-	content/common/gpu/media/h264_bit_reader.cc \
-	content/common/gpu/media/h264_parser.cc \
+	content/common/gpu/media/gl_surface_capturer.cc \
 	content/common/gpu/media/gpu_video_decode_accelerator.cc \
 	content/common/gpu/media/gpu_video_encode_accelerator.cc \
+	content/common/gpu/media/video_decode_accelerator_impl.cc \
+	content/common/gpu/media/h264_bit_reader.cc \
+	content/common/gpu/media/h264_parser.cc \
+	content/common/gpu/stream_texture_manager_android.cc \
+	content/common/gpu/surface_capturer.cc \
 	content/common/gpu/sync_point_manager.cc \
 	content/common/gpu/texture_image_transport_surface.cc \
 	content/common/indexed_db/indexed_db_key.cc \
@@ -187,7 +190,6 @@
 	'-DENABLE_GPU=1' \
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DPOSIX_AVOID_MMAP' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
@@ -197,6 +199,7 @@
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
 	'-DU_USING_ICU_NAMESPACE=0' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
@@ -234,7 +237,6 @@
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
 	$(gyp_shared_intermediate_dir)/content \
-	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/frameworks/wilhelm/include \
@@ -302,7 +304,6 @@
 	'-DENABLE_GPU=1' \
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DPOSIX_AVOID_MMAP' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
@@ -312,6 +313,7 @@
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
 	'-DU_USING_ICU_NAMESPACE=0' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
@@ -350,7 +352,6 @@
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
 	$(gyp_shared_intermediate_dir)/content \
-	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/frameworks/wilhelm/include \
diff --git a/content/content_common.target.darwin-x86.mk b/content/content_common.target.darwin-x86.mk
index 8adec50..e408b99 100644
--- a/content/content_common.target.darwin-x86.mk
+++ b/content/content_common.target.darwin-x86.mk
@@ -16,7 +16,6 @@
 	$(call intermediates-dir-for,GYP,third_party_icu_icuuc_gyp)/icuuc.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
 	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
-	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,webkit_support_glue_gyp)/webkit_support_glue_gyp.a \
 	$(call intermediates-dir-for,GYP,content_content_jni_headers_gyp)/content_jni_headers.stamp \
@@ -86,6 +85,7 @@
 	content/common/gpu/client/context_provider_command_buffer.cc \
 	content/common/gpu/client/gl_helper.cc \
 	content/common/gpu/client/gl_helper_scaling.cc \
+	content/common/gpu/client/gl_surface_capturer_host.cc \
 	content/common/gpu/client/gpu_channel_host.cc \
 	content/common/gpu/client/gpu_video_decode_accelerator_host.cc \
 	content/common/gpu/client/gpu_video_encode_accelerator_host.cc \
@@ -98,13 +98,16 @@
 	content/common/gpu/gpu_memory_tracking.cc \
 	content/common/gpu/gpu_rendering_stats.cc \
 	content/common/gpu/gpu_surface_lookup.cc \
-	content/common/gpu/stream_texture_manager_android.cc \
 	content/common/gpu/image_transport_surface.cc \
 	content/common/gpu/image_transport_surface_android.cc \
-	content/common/gpu/media/h264_bit_reader.cc \
-	content/common/gpu/media/h264_parser.cc \
+	content/common/gpu/media/gl_surface_capturer.cc \
 	content/common/gpu/media/gpu_video_decode_accelerator.cc \
 	content/common/gpu/media/gpu_video_encode_accelerator.cc \
+	content/common/gpu/media/video_decode_accelerator_impl.cc \
+	content/common/gpu/media/h264_bit_reader.cc \
+	content/common/gpu/media/h264_parser.cc \
+	content/common/gpu/stream_texture_manager_android.cc \
+	content/common/gpu/surface_capturer.cc \
 	content/common/gpu/sync_point_manager.cc \
 	content/common/gpu/texture_image_transport_surface.cc \
 	content/common/indexed_db/indexed_db_key.cc \
@@ -190,7 +193,6 @@
 	'-DENABLE_GPU=1' \
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
 	'-DGR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"' \
@@ -199,6 +201,7 @@
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
 	'-DU_USING_ICU_NAMESPACE=0' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
@@ -236,7 +239,6 @@
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
 	$(gyp_shared_intermediate_dir)/content \
-	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/frameworks/wilhelm/include \
@@ -308,7 +310,6 @@
 	'-DENABLE_GPU=1' \
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
 	'-DGR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"' \
@@ -317,6 +318,7 @@
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
 	'-DU_USING_ICU_NAMESPACE=0' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
@@ -355,7 +357,6 @@
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
 	$(gyp_shared_intermediate_dir)/content \
-	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/frameworks/wilhelm/include \
diff --git a/content/content_common.target.linux-arm.mk b/content/content_common.target.linux-arm.mk
index 0aced76..88ef0eb 100644
--- a/content/content_common.target.linux-arm.mk
+++ b/content/content_common.target.linux-arm.mk
@@ -16,7 +16,6 @@
 	$(call intermediates-dir-for,GYP,third_party_icu_icuuc_gyp)/icuuc.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
 	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
-	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,webkit_support_glue_gyp)/webkit_support_glue_gyp.a \
 	$(call intermediates-dir-for,GYP,content_content_jni_headers_gyp)/content_jni_headers.stamp \
@@ -86,6 +85,7 @@
 	content/common/gpu/client/context_provider_command_buffer.cc \
 	content/common/gpu/client/gl_helper.cc \
 	content/common/gpu/client/gl_helper_scaling.cc \
+	content/common/gpu/client/gl_surface_capturer_host.cc \
 	content/common/gpu/client/gpu_channel_host.cc \
 	content/common/gpu/client/gpu_video_decode_accelerator_host.cc \
 	content/common/gpu/client/gpu_video_encode_accelerator_host.cc \
@@ -98,13 +98,16 @@
 	content/common/gpu/gpu_memory_tracking.cc \
 	content/common/gpu/gpu_rendering_stats.cc \
 	content/common/gpu/gpu_surface_lookup.cc \
-	content/common/gpu/stream_texture_manager_android.cc \
 	content/common/gpu/image_transport_surface.cc \
 	content/common/gpu/image_transport_surface_android.cc \
-	content/common/gpu/media/h264_bit_reader.cc \
-	content/common/gpu/media/h264_parser.cc \
+	content/common/gpu/media/gl_surface_capturer.cc \
 	content/common/gpu/media/gpu_video_decode_accelerator.cc \
 	content/common/gpu/media/gpu_video_encode_accelerator.cc \
+	content/common/gpu/media/video_decode_accelerator_impl.cc \
+	content/common/gpu/media/h264_bit_reader.cc \
+	content/common/gpu/media/h264_parser.cc \
+	content/common/gpu/stream_texture_manager_android.cc \
+	content/common/gpu/surface_capturer.cc \
 	content/common/gpu/sync_point_manager.cc \
 	content/common/gpu/texture_image_transport_surface.cc \
 	content/common/indexed_db/indexed_db_key.cc \
@@ -188,7 +191,6 @@
 	'-DENABLE_GPU=1' \
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DPOSIX_AVOID_MMAP' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
@@ -198,6 +200,7 @@
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
 	'-DU_USING_ICU_NAMESPACE=0' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
@@ -235,7 +238,6 @@
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
 	$(gyp_shared_intermediate_dir)/content \
-	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/frameworks/wilhelm/include \
@@ -304,7 +306,6 @@
 	'-DENABLE_GPU=1' \
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DPOSIX_AVOID_MMAP' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
@@ -314,6 +315,7 @@
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
 	'-DU_USING_ICU_NAMESPACE=0' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
@@ -352,7 +354,6 @@
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
 	$(gyp_shared_intermediate_dir)/content \
-	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/frameworks/wilhelm/include \
diff --git a/content/content_common.target.linux-mips.mk b/content/content_common.target.linux-mips.mk
index 6fdb22c..e56a0ab 100644
--- a/content/content_common.target.linux-mips.mk
+++ b/content/content_common.target.linux-mips.mk
@@ -16,7 +16,6 @@
 	$(call intermediates-dir-for,GYP,third_party_icu_icuuc_gyp)/icuuc.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
 	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
-	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,webkit_support_glue_gyp)/webkit_support_glue_gyp.a \
 	$(call intermediates-dir-for,GYP,content_content_jni_headers_gyp)/content_jni_headers.stamp \
@@ -86,6 +85,7 @@
 	content/common/gpu/client/context_provider_command_buffer.cc \
 	content/common/gpu/client/gl_helper.cc \
 	content/common/gpu/client/gl_helper_scaling.cc \
+	content/common/gpu/client/gl_surface_capturer_host.cc \
 	content/common/gpu/client/gpu_channel_host.cc \
 	content/common/gpu/client/gpu_video_decode_accelerator_host.cc \
 	content/common/gpu/client/gpu_video_encode_accelerator_host.cc \
@@ -98,13 +98,16 @@
 	content/common/gpu/gpu_memory_tracking.cc \
 	content/common/gpu/gpu_rendering_stats.cc \
 	content/common/gpu/gpu_surface_lookup.cc \
-	content/common/gpu/stream_texture_manager_android.cc \
 	content/common/gpu/image_transport_surface.cc \
 	content/common/gpu/image_transport_surface_android.cc \
-	content/common/gpu/media/h264_bit_reader.cc \
-	content/common/gpu/media/h264_parser.cc \
+	content/common/gpu/media/gl_surface_capturer.cc \
 	content/common/gpu/media/gpu_video_decode_accelerator.cc \
 	content/common/gpu/media/gpu_video_encode_accelerator.cc \
+	content/common/gpu/media/video_decode_accelerator_impl.cc \
+	content/common/gpu/media/h264_bit_reader.cc \
+	content/common/gpu/media/h264_parser.cc \
+	content/common/gpu/stream_texture_manager_android.cc \
+	content/common/gpu/surface_capturer.cc \
 	content/common/gpu/sync_point_manager.cc \
 	content/common/gpu/texture_image_transport_surface.cc \
 	content/common/indexed_db/indexed_db_key.cc \
@@ -187,7 +190,6 @@
 	'-DENABLE_GPU=1' \
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DPOSIX_AVOID_MMAP' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
@@ -197,6 +199,7 @@
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
 	'-DU_USING_ICU_NAMESPACE=0' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
@@ -234,7 +237,6 @@
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
 	$(gyp_shared_intermediate_dir)/content \
-	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/frameworks/wilhelm/include \
@@ -302,7 +304,6 @@
 	'-DENABLE_GPU=1' \
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DPOSIX_AVOID_MMAP' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
@@ -312,6 +313,7 @@
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
 	'-DU_USING_ICU_NAMESPACE=0' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
@@ -350,7 +352,6 @@
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
 	$(gyp_shared_intermediate_dir)/content \
-	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/frameworks/wilhelm/include \
diff --git a/content/content_common.target.linux-x86.mk b/content/content_common.target.linux-x86.mk
index 8adec50..e408b99 100644
--- a/content/content_common.target.linux-x86.mk
+++ b/content/content_common.target.linux-x86.mk
@@ -16,7 +16,6 @@
 	$(call intermediates-dir-for,GYP,third_party_icu_icuuc_gyp)/icuuc.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_ui_gyp)/ui_ui_gyp.a \
 	$(call intermediates-dir-for,GYP,content_content_resources_gyp)/content_resources.stamp \
-	$(call intermediates-dir-for,GYP,third_party_WebKit_public_blink_minimal_gyp)/blink_minimal.stamp \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,ui_gl_gl_gyp)/ui_gl_gl_gyp.a \
 	$(call intermediates-dir-for,STATIC_LIBRARIES,webkit_support_glue_gyp)/webkit_support_glue_gyp.a \
 	$(call intermediates-dir-for,GYP,content_content_jni_headers_gyp)/content_jni_headers.stamp \
@@ -86,6 +85,7 @@
 	content/common/gpu/client/context_provider_command_buffer.cc \
 	content/common/gpu/client/gl_helper.cc \
 	content/common/gpu/client/gl_helper_scaling.cc \
+	content/common/gpu/client/gl_surface_capturer_host.cc \
 	content/common/gpu/client/gpu_channel_host.cc \
 	content/common/gpu/client/gpu_video_decode_accelerator_host.cc \
 	content/common/gpu/client/gpu_video_encode_accelerator_host.cc \
@@ -98,13 +98,16 @@
 	content/common/gpu/gpu_memory_tracking.cc \
 	content/common/gpu/gpu_rendering_stats.cc \
 	content/common/gpu/gpu_surface_lookup.cc \
-	content/common/gpu/stream_texture_manager_android.cc \
 	content/common/gpu/image_transport_surface.cc \
 	content/common/gpu/image_transport_surface_android.cc \
-	content/common/gpu/media/h264_bit_reader.cc \
-	content/common/gpu/media/h264_parser.cc \
+	content/common/gpu/media/gl_surface_capturer.cc \
 	content/common/gpu/media/gpu_video_decode_accelerator.cc \
 	content/common/gpu/media/gpu_video_encode_accelerator.cc \
+	content/common/gpu/media/video_decode_accelerator_impl.cc \
+	content/common/gpu/media/h264_bit_reader.cc \
+	content/common/gpu/media/h264_parser.cc \
+	content/common/gpu/stream_texture_manager_android.cc \
+	content/common/gpu/surface_capturer.cc \
 	content/common/gpu/sync_point_manager.cc \
 	content/common/gpu/texture_image_transport_surface.cc \
 	content/common/indexed_db/indexed_db_key.cc \
@@ -190,7 +193,6 @@
 	'-DENABLE_GPU=1' \
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
 	'-DGR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"' \
@@ -199,6 +201,7 @@
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
 	'-DU_USING_ICU_NAMESPACE=0' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
@@ -236,7 +239,6 @@
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
 	$(gyp_shared_intermediate_dir)/content \
-	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/frameworks/wilhelm/include \
@@ -308,7 +310,6 @@
 	'-DENABLE_GPU=1' \
 	'-DUSE_OPENSSL=1' \
 	'-DENABLE_EGLIMAGE=1' \
-	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DSK_ENABLE_INST_COUNT=0' \
 	'-DSK_SUPPORT_GPU=1' \
 	'-DGR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"' \
@@ -317,6 +318,7 @@
 	'-DSK_USE_POSIX_THREADS' \
 	'-DSK_DEFERRED_CANVAS_USES_FACTORIES=1' \
 	'-DU_USING_ICU_NAMESPACE=0' \
+	'-DMEDIA_DISABLE_LIBVPX' \
 	'-DMESA_EGL_NO_X11_HEADERS' \
 	'-D__STDC_CONSTANT_MACROS' \
 	'-D__STDC_FORMAT_MACROS' \
@@ -355,7 +357,6 @@
 	$(PWD)/external/icu4c/common \
 	$(PWD)/external/icu4c/i18n \
 	$(gyp_shared_intermediate_dir)/content \
-	$(LOCAL_PATH)/v8/include \
 	$(gyp_shared_intermediate_dir)/ui/gl \
 	$(LOCAL_PATH)/third_party/mesa/src/include \
 	$(PWD)/frameworks/wilhelm/include \
diff --git a/content/content_jni.gypi b/content/content_jni.gypi
index cfba343..cd83aa6 100644
--- a/content/content_jni.gypi
+++ b/content/content_jni.gypi
@@ -12,7 +12,6 @@
     'public/android/java/src/org/chromium/content/app/ContentMain.java',
     'public/android/java/src/org/chromium/content/app/LibraryLoader.java',
     'public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java',
-    'public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java',
     'public/android/java/src/org/chromium/content/browser/BrowserStartupController.java',
     'public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java',
     'public/android/java/src/org/chromium/content/browser/ContentSettings.java',
@@ -29,7 +28,7 @@
     'public/android/java/src/org/chromium/content/browser/LocationProvider.java',
     'public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java',
     'public/android/java/src/org/chromium/content/browser/PowerSaveBlocker.java',
-    'public/android/java/src/org/chromium/content/browser/SmoothScroller.java',
+    'public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java',
     'public/android/java/src/org/chromium/content/browser/SpeechRecognition.java',
     'public/android/java/src/org/chromium/content/browser/TouchPoint.java',
     'public/android/java/src/org/chromium/content/browser/TracingIntentHandler.java',
diff --git a/content/content_jni_headers.target.darwin-arm.mk b/content/content_jni_headers.target.darwin-arm.mk
index d6e91e1..affcc3f 100644
--- a/content/content_jni_headers.target.darwin-arm.mk
+++ b/content/content_jni_headers.target.darwin-arm.mk
@@ -16,7 +16,7 @@
 
 
 ### Generated for rule "content_content_gyp_content_jni_headers_target_generate_jni_headers":
-# "{'inputs': ['../base/android/jni_generator/jni_generator.py', '../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/content/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['public/android/java/src/org/chromium/content/app/ChildProcessService.java', 'public/android/java/src/org/chromium/content/app/ContentMain.java', 'public/android/java/src/org/chromium/content/app/LibraryLoader.java', 'public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java', 'public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java', 'public/android/java/src/org/chromium/content/browser/BrowserStartupController.java', 'public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java', 'public/android/java/src/org/chromium/content/browser/ContentSettings.java', 'public/android/java/src/org/chromium/content/browser/ContentVideoView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewCore.java', 'public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewStatics.java', 'public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java', 'public/android/java/src/org/chromium/content/browser/DownloadController.java', 'public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java', 'public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java', 'public/android/java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java', 'public/android/java/src/org/chromium/content/browser/LoadUrlParams.java', 'public/android/java/src/org/chromium/content/browser/LocationProvider.java', 'public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java', 'public/android/java/src/org/chromium/content/browser/PowerSaveBlocker.java', 'public/android/java/src/org/chromium/content/browser/SmoothScroller.java', 'public/android/java/src/org/chromium/content/browser/SpeechRecognition.java', 'public/android/java/src/org/chromium/content/browser/TouchPoint.java', 'public/android/java/src/org/chromium/content/browser/TracingIntentHandler.java', 'public/android/java/src/org/chromium/content/browser/VibrationMessageFilter.java', 'public/android/java/src/org/chromium/content/browser/WebContentsObserverAndroid.java', 'public/android/java/src/org/chromium/content/common/CommandLine.java', 'public/android/java/src/org/chromium/content/common/DeviceTelephonyInfo.java', 'public/android/java/src/org/chromium/content/common/TraceEvent.java'], 'action': ['../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/content/jni', '--optimize_generation', '0', '--jarjar', '../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
+# "{'inputs': ['../base/android/jni_generator/jni_generator.py', '../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/content/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['public/android/java/src/org/chromium/content/app/ChildProcessService.java', 'public/android/java/src/org/chromium/content/app/ContentMain.java', 'public/android/java/src/org/chromium/content/app/LibraryLoader.java', 'public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java', 'public/android/java/src/org/chromium/content/browser/BrowserStartupController.java', 'public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java', 'public/android/java/src/org/chromium/content/browser/ContentSettings.java', 'public/android/java/src/org/chromium/content/browser/ContentVideoView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewCore.java', 'public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewStatics.java', 'public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java', 'public/android/java/src/org/chromium/content/browser/DownloadController.java', 'public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java', 'public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java', 'public/android/java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java', 'public/android/java/src/org/chromium/content/browser/LoadUrlParams.java', 'public/android/java/src/org/chromium/content/browser/LocationProvider.java', 'public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java', 'public/android/java/src/org/chromium/content/browser/PowerSaveBlocker.java', 'public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java', 'public/android/java/src/org/chromium/content/browser/SpeechRecognition.java', 'public/android/java/src/org/chromium/content/browser/TouchPoint.java', 'public/android/java/src/org/chromium/content/browser/TracingIntentHandler.java', 'public/android/java/src/org/chromium/content/browser/VibrationMessageFilter.java', 'public/android/java/src/org/chromium/content/browser/WebContentsObserverAndroid.java', 'public/android/java/src/org/chromium/content/common/CommandLine.java', 'public/android/java/src/org/chromium/content/common/DeviceTelephonyInfo.java', 'public/android/java/src/org/chromium/content/common/TraceEvent.java'], 'action': ['../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/content/jni', '--optimize_generation', '0', '--jarjar', '../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
 $(gyp_shared_intermediate_dir)/content/jni/ChildProcessService_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/content/jni/ChildProcessService_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/jni/ChildProcessService_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
@@ -57,16 +57,6 @@
 .PHONY: content_content_jni_headers_gyp_rule_trigger
 content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/BrowserAccessibilityManager_jni.h
 
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: $(LOCAL_PATH)/content/public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
-	mkdir -p $(gyp_shared_intermediate_dir)/content/jni; cd $(gyp_local_path)/content; ../base/android/jni_generator/jni_generator.py --input_file public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java --output_dir "$(gyp_shared_intermediate_dir)/content/jni" --optimize_generation 0 --jarjar ../android_webview/build/jarjar-rules.txt
-
-.PHONY: content_content_jni_headers_gyp_rule_trigger
-content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h
-
 $(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
@@ -227,15 +217,15 @@
 .PHONY: content_content_jni_headers_gyp_rule_trigger
 content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/PowerSaveBlocker_jni.h
 
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: $(LOCAL_PATH)/content/public/android/java/src/org/chromium/content/browser/SmoothScroller.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
-	mkdir -p $(gyp_shared_intermediate_dir)/content/jni; cd $(gyp_local_path)/content; ../base/android/jni_generator/jni_generator.py --input_file public/android/java/src/org/chromium/content/browser/SmoothScroller.java --output_dir "$(gyp_shared_intermediate_dir)/content/jni" --optimize_generation 0 --jarjar ../android_webview/build/jarjar-rules.txt
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: $(LOCAL_PATH)/content/public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
+	mkdir -p $(gyp_shared_intermediate_dir)/content/jni; cd $(gyp_local_path)/content; ../base/android/jni_generator/jni_generator.py --input_file public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java --output_dir "$(gyp_shared_intermediate_dir)/content/jni" --optimize_generation 0 --jarjar ../android_webview/build/jarjar-rules.txt
 
 .PHONY: content_content_jni_headers_gyp_rule_trigger
-content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h
+content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h
 
 $(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
@@ -324,7 +314,6 @@
 	$(gyp_shared_intermediate_dir)/content/jni/ContentMain_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/LibraryLoader_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserAccessibilityManager_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ChildProcessLauncher_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ContentSettings_jni.h \
@@ -341,7 +330,7 @@
 	$(gyp_shared_intermediate_dir)/content/jni/LocationProvider_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/MediaResourceGetter_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/PowerSaveBlocker_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h \
+	$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TouchPoint_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TracingIntentHandler_jni.h \
@@ -359,7 +348,6 @@
 	$(gyp_shared_intermediate_dir)/content/jni/ContentMain_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/LibraryLoader_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserAccessibilityManager_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ChildProcessLauncher_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ContentSettings_jni.h \
@@ -376,7 +364,7 @@
 	$(gyp_shared_intermediate_dir)/content/jni/LocationProvider_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/MediaResourceGetter_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/PowerSaveBlocker_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h \
+	$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TouchPoint_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TracingIntentHandler_jni.h \
diff --git a/content/content_jni_headers.target.darwin-mips.mk b/content/content_jni_headers.target.darwin-mips.mk
index 2de4453..b4b038b 100644
--- a/content/content_jni_headers.target.darwin-mips.mk
+++ b/content/content_jni_headers.target.darwin-mips.mk
@@ -16,7 +16,7 @@
 
 
 ### Generated for rule "content_content_gyp_content_jni_headers_target_generate_jni_headers":
-# "{'inputs': ['../base/android/jni_generator/jni_generator.py', '../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/content/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['public/android/java/src/org/chromium/content/app/ChildProcessService.java', 'public/android/java/src/org/chromium/content/app/ContentMain.java', 'public/android/java/src/org/chromium/content/app/LibraryLoader.java', 'public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java', 'public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java', 'public/android/java/src/org/chromium/content/browser/BrowserStartupController.java', 'public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java', 'public/android/java/src/org/chromium/content/browser/ContentSettings.java', 'public/android/java/src/org/chromium/content/browser/ContentVideoView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewCore.java', 'public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewStatics.java', 'public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java', 'public/android/java/src/org/chromium/content/browser/DownloadController.java', 'public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java', 'public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java', 'public/android/java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java', 'public/android/java/src/org/chromium/content/browser/LoadUrlParams.java', 'public/android/java/src/org/chromium/content/browser/LocationProvider.java', 'public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java', 'public/android/java/src/org/chromium/content/browser/PowerSaveBlocker.java', 'public/android/java/src/org/chromium/content/browser/SmoothScroller.java', 'public/android/java/src/org/chromium/content/browser/SpeechRecognition.java', 'public/android/java/src/org/chromium/content/browser/TouchPoint.java', 'public/android/java/src/org/chromium/content/browser/TracingIntentHandler.java', 'public/android/java/src/org/chromium/content/browser/VibrationMessageFilter.java', 'public/android/java/src/org/chromium/content/browser/WebContentsObserverAndroid.java', 'public/android/java/src/org/chromium/content/common/CommandLine.java', 'public/android/java/src/org/chromium/content/common/DeviceTelephonyInfo.java', 'public/android/java/src/org/chromium/content/common/TraceEvent.java'], 'action': ['../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/content/jni', '--optimize_generation', '0', '--jarjar', '../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
+# "{'inputs': ['../base/android/jni_generator/jni_generator.py', '../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/content/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['public/android/java/src/org/chromium/content/app/ChildProcessService.java', 'public/android/java/src/org/chromium/content/app/ContentMain.java', 'public/android/java/src/org/chromium/content/app/LibraryLoader.java', 'public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java', 'public/android/java/src/org/chromium/content/browser/BrowserStartupController.java', 'public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java', 'public/android/java/src/org/chromium/content/browser/ContentSettings.java', 'public/android/java/src/org/chromium/content/browser/ContentVideoView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewCore.java', 'public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewStatics.java', 'public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java', 'public/android/java/src/org/chromium/content/browser/DownloadController.java', 'public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java', 'public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java', 'public/android/java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java', 'public/android/java/src/org/chromium/content/browser/LoadUrlParams.java', 'public/android/java/src/org/chromium/content/browser/LocationProvider.java', 'public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java', 'public/android/java/src/org/chromium/content/browser/PowerSaveBlocker.java', 'public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java', 'public/android/java/src/org/chromium/content/browser/SpeechRecognition.java', 'public/android/java/src/org/chromium/content/browser/TouchPoint.java', 'public/android/java/src/org/chromium/content/browser/TracingIntentHandler.java', 'public/android/java/src/org/chromium/content/browser/VibrationMessageFilter.java', 'public/android/java/src/org/chromium/content/browser/WebContentsObserverAndroid.java', 'public/android/java/src/org/chromium/content/common/CommandLine.java', 'public/android/java/src/org/chromium/content/common/DeviceTelephonyInfo.java', 'public/android/java/src/org/chromium/content/common/TraceEvent.java'], 'action': ['../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/content/jni', '--optimize_generation', '0', '--jarjar', '../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
 $(gyp_shared_intermediate_dir)/content/jni/ChildProcessService_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/content/jni/ChildProcessService_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/jni/ChildProcessService_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
@@ -57,16 +57,6 @@
 .PHONY: content_content_jni_headers_gyp_rule_trigger
 content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/BrowserAccessibilityManager_jni.h
 
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: $(LOCAL_PATH)/content/public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
-	mkdir -p $(gyp_shared_intermediate_dir)/content/jni; cd $(gyp_local_path)/content; ../base/android/jni_generator/jni_generator.py --input_file public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java --output_dir "$(gyp_shared_intermediate_dir)/content/jni" --optimize_generation 0 --jarjar ../android_webview/build/jarjar-rules.txt
-
-.PHONY: content_content_jni_headers_gyp_rule_trigger
-content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h
-
 $(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
@@ -227,15 +217,15 @@
 .PHONY: content_content_jni_headers_gyp_rule_trigger
 content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/PowerSaveBlocker_jni.h
 
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: $(LOCAL_PATH)/content/public/android/java/src/org/chromium/content/browser/SmoothScroller.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
-	mkdir -p $(gyp_shared_intermediate_dir)/content/jni; cd $(gyp_local_path)/content; ../base/android/jni_generator/jni_generator.py --input_file public/android/java/src/org/chromium/content/browser/SmoothScroller.java --output_dir "$(gyp_shared_intermediate_dir)/content/jni" --optimize_generation 0 --jarjar ../android_webview/build/jarjar-rules.txt
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: $(LOCAL_PATH)/content/public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
+	mkdir -p $(gyp_shared_intermediate_dir)/content/jni; cd $(gyp_local_path)/content; ../base/android/jni_generator/jni_generator.py --input_file public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java --output_dir "$(gyp_shared_intermediate_dir)/content/jni" --optimize_generation 0 --jarjar ../android_webview/build/jarjar-rules.txt
 
 .PHONY: content_content_jni_headers_gyp_rule_trigger
-content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h
+content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h
 
 $(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
@@ -324,7 +314,6 @@
 	$(gyp_shared_intermediate_dir)/content/jni/ContentMain_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/LibraryLoader_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserAccessibilityManager_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ChildProcessLauncher_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ContentSettings_jni.h \
@@ -341,7 +330,7 @@
 	$(gyp_shared_intermediate_dir)/content/jni/LocationProvider_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/MediaResourceGetter_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/PowerSaveBlocker_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h \
+	$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TouchPoint_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TracingIntentHandler_jni.h \
@@ -359,7 +348,6 @@
 	$(gyp_shared_intermediate_dir)/content/jni/ContentMain_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/LibraryLoader_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserAccessibilityManager_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ChildProcessLauncher_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ContentSettings_jni.h \
@@ -376,7 +364,7 @@
 	$(gyp_shared_intermediate_dir)/content/jni/LocationProvider_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/MediaResourceGetter_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/PowerSaveBlocker_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h \
+	$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TouchPoint_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TracingIntentHandler_jni.h \
diff --git a/content/content_jni_headers.target.darwin-x86.mk b/content/content_jni_headers.target.darwin-x86.mk
index 879717a..f0bd3fd 100644
--- a/content/content_jni_headers.target.darwin-x86.mk
+++ b/content/content_jni_headers.target.darwin-x86.mk
@@ -16,7 +16,7 @@
 
 
 ### Generated for rule "content_content_gyp_content_jni_headers_target_generate_jni_headers":
-# "{'inputs': ['../base/android/jni_generator/jni_generator.py', '../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/content/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['public/android/java/src/org/chromium/content/app/ChildProcessService.java', 'public/android/java/src/org/chromium/content/app/ContentMain.java', 'public/android/java/src/org/chromium/content/app/LibraryLoader.java', 'public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java', 'public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java', 'public/android/java/src/org/chromium/content/browser/BrowserStartupController.java', 'public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java', 'public/android/java/src/org/chromium/content/browser/ContentSettings.java', 'public/android/java/src/org/chromium/content/browser/ContentVideoView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewCore.java', 'public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewStatics.java', 'public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java', 'public/android/java/src/org/chromium/content/browser/DownloadController.java', 'public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java', 'public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java', 'public/android/java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java', 'public/android/java/src/org/chromium/content/browser/LoadUrlParams.java', 'public/android/java/src/org/chromium/content/browser/LocationProvider.java', 'public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java', 'public/android/java/src/org/chromium/content/browser/PowerSaveBlocker.java', 'public/android/java/src/org/chromium/content/browser/SmoothScroller.java', 'public/android/java/src/org/chromium/content/browser/SpeechRecognition.java', 'public/android/java/src/org/chromium/content/browser/TouchPoint.java', 'public/android/java/src/org/chromium/content/browser/TracingIntentHandler.java', 'public/android/java/src/org/chromium/content/browser/VibrationMessageFilter.java', 'public/android/java/src/org/chromium/content/browser/WebContentsObserverAndroid.java', 'public/android/java/src/org/chromium/content/common/CommandLine.java', 'public/android/java/src/org/chromium/content/common/DeviceTelephonyInfo.java', 'public/android/java/src/org/chromium/content/common/TraceEvent.java'], 'action': ['../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/content/jni', '--optimize_generation', '0', '--jarjar', '../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
+# "{'inputs': ['../base/android/jni_generator/jni_generator.py', '../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/content/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['public/android/java/src/org/chromium/content/app/ChildProcessService.java', 'public/android/java/src/org/chromium/content/app/ContentMain.java', 'public/android/java/src/org/chromium/content/app/LibraryLoader.java', 'public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java', 'public/android/java/src/org/chromium/content/browser/BrowserStartupController.java', 'public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java', 'public/android/java/src/org/chromium/content/browser/ContentSettings.java', 'public/android/java/src/org/chromium/content/browser/ContentVideoView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewCore.java', 'public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewStatics.java', 'public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java', 'public/android/java/src/org/chromium/content/browser/DownloadController.java', 'public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java', 'public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java', 'public/android/java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java', 'public/android/java/src/org/chromium/content/browser/LoadUrlParams.java', 'public/android/java/src/org/chromium/content/browser/LocationProvider.java', 'public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java', 'public/android/java/src/org/chromium/content/browser/PowerSaveBlocker.java', 'public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java', 'public/android/java/src/org/chromium/content/browser/SpeechRecognition.java', 'public/android/java/src/org/chromium/content/browser/TouchPoint.java', 'public/android/java/src/org/chromium/content/browser/TracingIntentHandler.java', 'public/android/java/src/org/chromium/content/browser/VibrationMessageFilter.java', 'public/android/java/src/org/chromium/content/browser/WebContentsObserverAndroid.java', 'public/android/java/src/org/chromium/content/common/CommandLine.java', 'public/android/java/src/org/chromium/content/common/DeviceTelephonyInfo.java', 'public/android/java/src/org/chromium/content/common/TraceEvent.java'], 'action': ['../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/content/jni', '--optimize_generation', '0', '--jarjar', '../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
 $(gyp_shared_intermediate_dir)/content/jni/ChildProcessService_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/content/jni/ChildProcessService_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/jni/ChildProcessService_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
@@ -57,16 +57,6 @@
 .PHONY: content_content_jni_headers_gyp_rule_trigger
 content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/BrowserAccessibilityManager_jni.h
 
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: $(LOCAL_PATH)/content/public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
-	mkdir -p $(gyp_shared_intermediate_dir)/content/jni; cd $(gyp_local_path)/content; ../base/android/jni_generator/jni_generator.py --input_file public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java --output_dir "$(gyp_shared_intermediate_dir)/content/jni" --optimize_generation 0 --jarjar ../android_webview/build/jarjar-rules.txt
-
-.PHONY: content_content_jni_headers_gyp_rule_trigger
-content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h
-
 $(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
@@ -227,15 +217,15 @@
 .PHONY: content_content_jni_headers_gyp_rule_trigger
 content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/PowerSaveBlocker_jni.h
 
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: $(LOCAL_PATH)/content/public/android/java/src/org/chromium/content/browser/SmoothScroller.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
-	mkdir -p $(gyp_shared_intermediate_dir)/content/jni; cd $(gyp_local_path)/content; ../base/android/jni_generator/jni_generator.py --input_file public/android/java/src/org/chromium/content/browser/SmoothScroller.java --output_dir "$(gyp_shared_intermediate_dir)/content/jni" --optimize_generation 0 --jarjar ../android_webview/build/jarjar-rules.txt
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: $(LOCAL_PATH)/content/public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
+	mkdir -p $(gyp_shared_intermediate_dir)/content/jni; cd $(gyp_local_path)/content; ../base/android/jni_generator/jni_generator.py --input_file public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java --output_dir "$(gyp_shared_intermediate_dir)/content/jni" --optimize_generation 0 --jarjar ../android_webview/build/jarjar-rules.txt
 
 .PHONY: content_content_jni_headers_gyp_rule_trigger
-content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h
+content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h
 
 $(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
@@ -324,7 +314,6 @@
 	$(gyp_shared_intermediate_dir)/content/jni/ContentMain_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/LibraryLoader_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserAccessibilityManager_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ChildProcessLauncher_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ContentSettings_jni.h \
@@ -341,7 +330,7 @@
 	$(gyp_shared_intermediate_dir)/content/jni/LocationProvider_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/MediaResourceGetter_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/PowerSaveBlocker_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h \
+	$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TouchPoint_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TracingIntentHandler_jni.h \
@@ -359,7 +348,6 @@
 	$(gyp_shared_intermediate_dir)/content/jni/ContentMain_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/LibraryLoader_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserAccessibilityManager_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ChildProcessLauncher_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ContentSettings_jni.h \
@@ -376,7 +364,7 @@
 	$(gyp_shared_intermediate_dir)/content/jni/LocationProvider_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/MediaResourceGetter_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/PowerSaveBlocker_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h \
+	$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TouchPoint_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TracingIntentHandler_jni.h \
diff --git a/content/content_jni_headers.target.linux-arm.mk b/content/content_jni_headers.target.linux-arm.mk
index d6e91e1..affcc3f 100644
--- a/content/content_jni_headers.target.linux-arm.mk
+++ b/content/content_jni_headers.target.linux-arm.mk
@@ -16,7 +16,7 @@
 
 
 ### Generated for rule "content_content_gyp_content_jni_headers_target_generate_jni_headers":
-# "{'inputs': ['../base/android/jni_generator/jni_generator.py', '../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/content/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['public/android/java/src/org/chromium/content/app/ChildProcessService.java', 'public/android/java/src/org/chromium/content/app/ContentMain.java', 'public/android/java/src/org/chromium/content/app/LibraryLoader.java', 'public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java', 'public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java', 'public/android/java/src/org/chromium/content/browser/BrowserStartupController.java', 'public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java', 'public/android/java/src/org/chromium/content/browser/ContentSettings.java', 'public/android/java/src/org/chromium/content/browser/ContentVideoView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewCore.java', 'public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewStatics.java', 'public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java', 'public/android/java/src/org/chromium/content/browser/DownloadController.java', 'public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java', 'public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java', 'public/android/java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java', 'public/android/java/src/org/chromium/content/browser/LoadUrlParams.java', 'public/android/java/src/org/chromium/content/browser/LocationProvider.java', 'public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java', 'public/android/java/src/org/chromium/content/browser/PowerSaveBlocker.java', 'public/android/java/src/org/chromium/content/browser/SmoothScroller.java', 'public/android/java/src/org/chromium/content/browser/SpeechRecognition.java', 'public/android/java/src/org/chromium/content/browser/TouchPoint.java', 'public/android/java/src/org/chromium/content/browser/TracingIntentHandler.java', 'public/android/java/src/org/chromium/content/browser/VibrationMessageFilter.java', 'public/android/java/src/org/chromium/content/browser/WebContentsObserverAndroid.java', 'public/android/java/src/org/chromium/content/common/CommandLine.java', 'public/android/java/src/org/chromium/content/common/DeviceTelephonyInfo.java', 'public/android/java/src/org/chromium/content/common/TraceEvent.java'], 'action': ['../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/content/jni', '--optimize_generation', '0', '--jarjar', '../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
+# "{'inputs': ['../base/android/jni_generator/jni_generator.py', '../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/content/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['public/android/java/src/org/chromium/content/app/ChildProcessService.java', 'public/android/java/src/org/chromium/content/app/ContentMain.java', 'public/android/java/src/org/chromium/content/app/LibraryLoader.java', 'public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java', 'public/android/java/src/org/chromium/content/browser/BrowserStartupController.java', 'public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java', 'public/android/java/src/org/chromium/content/browser/ContentSettings.java', 'public/android/java/src/org/chromium/content/browser/ContentVideoView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewCore.java', 'public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewStatics.java', 'public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java', 'public/android/java/src/org/chromium/content/browser/DownloadController.java', 'public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java', 'public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java', 'public/android/java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java', 'public/android/java/src/org/chromium/content/browser/LoadUrlParams.java', 'public/android/java/src/org/chromium/content/browser/LocationProvider.java', 'public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java', 'public/android/java/src/org/chromium/content/browser/PowerSaveBlocker.java', 'public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java', 'public/android/java/src/org/chromium/content/browser/SpeechRecognition.java', 'public/android/java/src/org/chromium/content/browser/TouchPoint.java', 'public/android/java/src/org/chromium/content/browser/TracingIntentHandler.java', 'public/android/java/src/org/chromium/content/browser/VibrationMessageFilter.java', 'public/android/java/src/org/chromium/content/browser/WebContentsObserverAndroid.java', 'public/android/java/src/org/chromium/content/common/CommandLine.java', 'public/android/java/src/org/chromium/content/common/DeviceTelephonyInfo.java', 'public/android/java/src/org/chromium/content/common/TraceEvent.java'], 'action': ['../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/content/jni', '--optimize_generation', '0', '--jarjar', '../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
 $(gyp_shared_intermediate_dir)/content/jni/ChildProcessService_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/content/jni/ChildProcessService_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/jni/ChildProcessService_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
@@ -57,16 +57,6 @@
 .PHONY: content_content_jni_headers_gyp_rule_trigger
 content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/BrowserAccessibilityManager_jni.h
 
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: $(LOCAL_PATH)/content/public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
-	mkdir -p $(gyp_shared_intermediate_dir)/content/jni; cd $(gyp_local_path)/content; ../base/android/jni_generator/jni_generator.py --input_file public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java --output_dir "$(gyp_shared_intermediate_dir)/content/jni" --optimize_generation 0 --jarjar ../android_webview/build/jarjar-rules.txt
-
-.PHONY: content_content_jni_headers_gyp_rule_trigger
-content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h
-
 $(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
@@ -227,15 +217,15 @@
 .PHONY: content_content_jni_headers_gyp_rule_trigger
 content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/PowerSaveBlocker_jni.h
 
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: $(LOCAL_PATH)/content/public/android/java/src/org/chromium/content/browser/SmoothScroller.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
-	mkdir -p $(gyp_shared_intermediate_dir)/content/jni; cd $(gyp_local_path)/content; ../base/android/jni_generator/jni_generator.py --input_file public/android/java/src/org/chromium/content/browser/SmoothScroller.java --output_dir "$(gyp_shared_intermediate_dir)/content/jni" --optimize_generation 0 --jarjar ../android_webview/build/jarjar-rules.txt
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: $(LOCAL_PATH)/content/public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
+	mkdir -p $(gyp_shared_intermediate_dir)/content/jni; cd $(gyp_local_path)/content; ../base/android/jni_generator/jni_generator.py --input_file public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java --output_dir "$(gyp_shared_intermediate_dir)/content/jni" --optimize_generation 0 --jarjar ../android_webview/build/jarjar-rules.txt
 
 .PHONY: content_content_jni_headers_gyp_rule_trigger
-content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h
+content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h
 
 $(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
@@ -324,7 +314,6 @@
 	$(gyp_shared_intermediate_dir)/content/jni/ContentMain_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/LibraryLoader_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserAccessibilityManager_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ChildProcessLauncher_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ContentSettings_jni.h \
@@ -341,7 +330,7 @@
 	$(gyp_shared_intermediate_dir)/content/jni/LocationProvider_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/MediaResourceGetter_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/PowerSaveBlocker_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h \
+	$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TouchPoint_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TracingIntentHandler_jni.h \
@@ -359,7 +348,6 @@
 	$(gyp_shared_intermediate_dir)/content/jni/ContentMain_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/LibraryLoader_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserAccessibilityManager_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ChildProcessLauncher_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ContentSettings_jni.h \
@@ -376,7 +364,7 @@
 	$(gyp_shared_intermediate_dir)/content/jni/LocationProvider_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/MediaResourceGetter_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/PowerSaveBlocker_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h \
+	$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TouchPoint_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TracingIntentHandler_jni.h \
diff --git a/content/content_jni_headers.target.linux-mips.mk b/content/content_jni_headers.target.linux-mips.mk
index 2de4453..b4b038b 100644
--- a/content/content_jni_headers.target.linux-mips.mk
+++ b/content/content_jni_headers.target.linux-mips.mk
@@ -16,7 +16,7 @@
 
 
 ### Generated for rule "content_content_gyp_content_jni_headers_target_generate_jni_headers":
-# "{'inputs': ['../base/android/jni_generator/jni_generator.py', '../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/content/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['public/android/java/src/org/chromium/content/app/ChildProcessService.java', 'public/android/java/src/org/chromium/content/app/ContentMain.java', 'public/android/java/src/org/chromium/content/app/LibraryLoader.java', 'public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java', 'public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java', 'public/android/java/src/org/chromium/content/browser/BrowserStartupController.java', 'public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java', 'public/android/java/src/org/chromium/content/browser/ContentSettings.java', 'public/android/java/src/org/chromium/content/browser/ContentVideoView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewCore.java', 'public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewStatics.java', 'public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java', 'public/android/java/src/org/chromium/content/browser/DownloadController.java', 'public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java', 'public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java', 'public/android/java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java', 'public/android/java/src/org/chromium/content/browser/LoadUrlParams.java', 'public/android/java/src/org/chromium/content/browser/LocationProvider.java', 'public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java', 'public/android/java/src/org/chromium/content/browser/PowerSaveBlocker.java', 'public/android/java/src/org/chromium/content/browser/SmoothScroller.java', 'public/android/java/src/org/chromium/content/browser/SpeechRecognition.java', 'public/android/java/src/org/chromium/content/browser/TouchPoint.java', 'public/android/java/src/org/chromium/content/browser/TracingIntentHandler.java', 'public/android/java/src/org/chromium/content/browser/VibrationMessageFilter.java', 'public/android/java/src/org/chromium/content/browser/WebContentsObserverAndroid.java', 'public/android/java/src/org/chromium/content/common/CommandLine.java', 'public/android/java/src/org/chromium/content/common/DeviceTelephonyInfo.java', 'public/android/java/src/org/chromium/content/common/TraceEvent.java'], 'action': ['../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/content/jni', '--optimize_generation', '0', '--jarjar', '../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
+# "{'inputs': ['../base/android/jni_generator/jni_generator.py', '../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/content/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['public/android/java/src/org/chromium/content/app/ChildProcessService.java', 'public/android/java/src/org/chromium/content/app/ContentMain.java', 'public/android/java/src/org/chromium/content/app/LibraryLoader.java', 'public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java', 'public/android/java/src/org/chromium/content/browser/BrowserStartupController.java', 'public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java', 'public/android/java/src/org/chromium/content/browser/ContentSettings.java', 'public/android/java/src/org/chromium/content/browser/ContentVideoView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewCore.java', 'public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewStatics.java', 'public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java', 'public/android/java/src/org/chromium/content/browser/DownloadController.java', 'public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java', 'public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java', 'public/android/java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java', 'public/android/java/src/org/chromium/content/browser/LoadUrlParams.java', 'public/android/java/src/org/chromium/content/browser/LocationProvider.java', 'public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java', 'public/android/java/src/org/chromium/content/browser/PowerSaveBlocker.java', 'public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java', 'public/android/java/src/org/chromium/content/browser/SpeechRecognition.java', 'public/android/java/src/org/chromium/content/browser/TouchPoint.java', 'public/android/java/src/org/chromium/content/browser/TracingIntentHandler.java', 'public/android/java/src/org/chromium/content/browser/VibrationMessageFilter.java', 'public/android/java/src/org/chromium/content/browser/WebContentsObserverAndroid.java', 'public/android/java/src/org/chromium/content/common/CommandLine.java', 'public/android/java/src/org/chromium/content/common/DeviceTelephonyInfo.java', 'public/android/java/src/org/chromium/content/common/TraceEvent.java'], 'action': ['../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/content/jni', '--optimize_generation', '0', '--jarjar', '../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
 $(gyp_shared_intermediate_dir)/content/jni/ChildProcessService_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/content/jni/ChildProcessService_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/jni/ChildProcessService_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
@@ -57,16 +57,6 @@
 .PHONY: content_content_jni_headers_gyp_rule_trigger
 content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/BrowserAccessibilityManager_jni.h
 
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: $(LOCAL_PATH)/content/public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
-	mkdir -p $(gyp_shared_intermediate_dir)/content/jni; cd $(gyp_local_path)/content; ../base/android/jni_generator/jni_generator.py --input_file public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java --output_dir "$(gyp_shared_intermediate_dir)/content/jni" --optimize_generation 0 --jarjar ../android_webview/build/jarjar-rules.txt
-
-.PHONY: content_content_jni_headers_gyp_rule_trigger
-content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h
-
 $(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
@@ -227,15 +217,15 @@
 .PHONY: content_content_jni_headers_gyp_rule_trigger
 content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/PowerSaveBlocker_jni.h
 
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: $(LOCAL_PATH)/content/public/android/java/src/org/chromium/content/browser/SmoothScroller.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
-	mkdir -p $(gyp_shared_intermediate_dir)/content/jni; cd $(gyp_local_path)/content; ../base/android/jni_generator/jni_generator.py --input_file public/android/java/src/org/chromium/content/browser/SmoothScroller.java --output_dir "$(gyp_shared_intermediate_dir)/content/jni" --optimize_generation 0 --jarjar ../android_webview/build/jarjar-rules.txt
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: $(LOCAL_PATH)/content/public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
+	mkdir -p $(gyp_shared_intermediate_dir)/content/jni; cd $(gyp_local_path)/content; ../base/android/jni_generator/jni_generator.py --input_file public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java --output_dir "$(gyp_shared_intermediate_dir)/content/jni" --optimize_generation 0 --jarjar ../android_webview/build/jarjar-rules.txt
 
 .PHONY: content_content_jni_headers_gyp_rule_trigger
-content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h
+content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h
 
 $(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
@@ -324,7 +314,6 @@
 	$(gyp_shared_intermediate_dir)/content/jni/ContentMain_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/LibraryLoader_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserAccessibilityManager_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ChildProcessLauncher_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ContentSettings_jni.h \
@@ -341,7 +330,7 @@
 	$(gyp_shared_intermediate_dir)/content/jni/LocationProvider_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/MediaResourceGetter_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/PowerSaveBlocker_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h \
+	$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TouchPoint_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TracingIntentHandler_jni.h \
@@ -359,7 +348,6 @@
 	$(gyp_shared_intermediate_dir)/content/jni/ContentMain_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/LibraryLoader_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserAccessibilityManager_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ChildProcessLauncher_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ContentSettings_jni.h \
@@ -376,7 +364,7 @@
 	$(gyp_shared_intermediate_dir)/content/jni/LocationProvider_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/MediaResourceGetter_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/PowerSaveBlocker_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h \
+	$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TouchPoint_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TracingIntentHandler_jni.h \
diff --git a/content/content_jni_headers.target.linux-x86.mk b/content/content_jni_headers.target.linux-x86.mk
index 879717a..f0bd3fd 100644
--- a/content/content_jni_headers.target.linux-x86.mk
+++ b/content/content_jni_headers.target.linux-x86.mk
@@ -16,7 +16,7 @@
 
 
 ### Generated for rule "content_content_gyp_content_jni_headers_target_generate_jni_headers":
-# "{'inputs': ['../base/android/jni_generator/jni_generator.py', '../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/content/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['public/android/java/src/org/chromium/content/app/ChildProcessService.java', 'public/android/java/src/org/chromium/content/app/ContentMain.java', 'public/android/java/src/org/chromium/content/app/LibraryLoader.java', 'public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java', 'public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java', 'public/android/java/src/org/chromium/content/browser/BrowserStartupController.java', 'public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java', 'public/android/java/src/org/chromium/content/browser/ContentSettings.java', 'public/android/java/src/org/chromium/content/browser/ContentVideoView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewCore.java', 'public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewStatics.java', 'public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java', 'public/android/java/src/org/chromium/content/browser/DownloadController.java', 'public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java', 'public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java', 'public/android/java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java', 'public/android/java/src/org/chromium/content/browser/LoadUrlParams.java', 'public/android/java/src/org/chromium/content/browser/LocationProvider.java', 'public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java', 'public/android/java/src/org/chromium/content/browser/PowerSaveBlocker.java', 'public/android/java/src/org/chromium/content/browser/SmoothScroller.java', 'public/android/java/src/org/chromium/content/browser/SpeechRecognition.java', 'public/android/java/src/org/chromium/content/browser/TouchPoint.java', 'public/android/java/src/org/chromium/content/browser/TracingIntentHandler.java', 'public/android/java/src/org/chromium/content/browser/VibrationMessageFilter.java', 'public/android/java/src/org/chromium/content/browser/WebContentsObserverAndroid.java', 'public/android/java/src/org/chromium/content/common/CommandLine.java', 'public/android/java/src/org/chromium/content/common/DeviceTelephonyInfo.java', 'public/android/java/src/org/chromium/content/common/TraceEvent.java'], 'action': ['../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/content/jni', '--optimize_generation', '0', '--jarjar', '../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
+# "{'inputs': ['../base/android/jni_generator/jni_generator.py', '../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/content/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['public/android/java/src/org/chromium/content/app/ChildProcessService.java', 'public/android/java/src/org/chromium/content/app/ContentMain.java', 'public/android/java/src/org/chromium/content/app/LibraryLoader.java', 'public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java', 'public/android/java/src/org/chromium/content/browser/BrowserStartupController.java', 'public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java', 'public/android/java/src/org/chromium/content/browser/ContentSettings.java', 'public/android/java/src/org/chromium/content/browser/ContentVideoView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewCore.java', 'public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java', 'public/android/java/src/org/chromium/content/browser/ContentViewStatics.java', 'public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java', 'public/android/java/src/org/chromium/content/browser/DownloadController.java', 'public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java', 'public/android/java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java', 'public/android/java/src/org/chromium/content/browser/InterstitialPageDelegateAndroid.java', 'public/android/java/src/org/chromium/content/browser/LoadUrlParams.java', 'public/android/java/src/org/chromium/content/browser/LocationProvider.java', 'public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java', 'public/android/java/src/org/chromium/content/browser/PowerSaveBlocker.java', 'public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java', 'public/android/java/src/org/chromium/content/browser/SpeechRecognition.java', 'public/android/java/src/org/chromium/content/browser/TouchPoint.java', 'public/android/java/src/org/chromium/content/browser/TracingIntentHandler.java', 'public/android/java/src/org/chromium/content/browser/VibrationMessageFilter.java', 'public/android/java/src/org/chromium/content/browser/WebContentsObserverAndroid.java', 'public/android/java/src/org/chromium/content/common/CommandLine.java', 'public/android/java/src/org/chromium/content/common/DeviceTelephonyInfo.java', 'public/android/java/src/org/chromium/content/common/TraceEvent.java'], 'action': ['../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/content/jni', '--optimize_generation', '0', '--jarjar', '../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
 $(gyp_shared_intermediate_dir)/content/jni/ChildProcessService_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/content/jni/ChildProcessService_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/jni/ChildProcessService_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
@@ -57,16 +57,6 @@
 .PHONY: content_content_jni_headers_gyp_rule_trigger
 content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/BrowserAccessibilityManager_jni.h
 
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h: $(LOCAL_PATH)/content/public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
-	mkdir -p $(gyp_shared_intermediate_dir)/content/jni; cd $(gyp_local_path)/content; ../base/android/jni_generator/jni_generator.py --input_file public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java --output_dir "$(gyp_shared_intermediate_dir)/content/jni" --optimize_generation 0 --jarjar ../android_webview/build/jarjar-rules.txt
-
-.PHONY: content_content_jni_headers_gyp_rule_trigger
-content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h
-
 $(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
@@ -227,15 +217,15 @@
 .PHONY: content_content_jni_headers_gyp_rule_trigger
 content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/PowerSaveBlocker_jni.h
 
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h: $(LOCAL_PATH)/content/public/android/java/src/org/chromium/content/browser/SmoothScroller.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
-	mkdir -p $(gyp_shared_intermediate_dir)/content/jni; cd $(gyp_local_path)/content; ../base/android/jni_generator/jni_generator.py --input_file public/android/java/src/org/chromium/content/browser/SmoothScroller.java --output_dir "$(gyp_shared_intermediate_dir)/content/jni" --optimize_generation 0 --jarjar ../android_webview/build/jarjar-rules.txt
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h: $(LOCAL_PATH)/content/public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
+	mkdir -p $(gyp_shared_intermediate_dir)/content/jni; cd $(gyp_local_path)/content; ../base/android/jni_generator/jni_generator.py --input_file public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java --output_dir "$(gyp_shared_intermediate_dir)/content/jni" --optimize_generation 0 --jarjar ../android_webview/build/jarjar-rules.txt
 
 .PHONY: content_content_jni_headers_gyp_rule_trigger
-content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h
+content_content_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h
 
 $(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
@@ -324,7 +314,6 @@
 	$(gyp_shared_intermediate_dir)/content/jni/ContentMain_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/LibraryLoader_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserAccessibilityManager_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ChildProcessLauncher_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ContentSettings_jni.h \
@@ -341,7 +330,7 @@
 	$(gyp_shared_intermediate_dir)/content/jni/LocationProvider_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/MediaResourceGetter_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/PowerSaveBlocker_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h \
+	$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TouchPoint_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TracingIntentHandler_jni.h \
@@ -359,7 +348,6 @@
 	$(gyp_shared_intermediate_dir)/content/jni/ContentMain_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/LibraryLoader_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserAccessibilityManager_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/AndroidBrowserProcess_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/BrowserStartupController_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ChildProcessLauncher_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/ContentSettings_jni.h \
@@ -376,7 +364,7 @@
 	$(gyp_shared_intermediate_dir)/content/jni/LocationProvider_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/MediaResourceGetter_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/PowerSaveBlocker_jni.h \
-	$(gyp_shared_intermediate_dir)/content/jni/SmoothScroller_jni.h \
+	$(gyp_shared_intermediate_dir)/content/jni/GenericTouchGesture_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/SpeechRecognition_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TouchPoint_jni.h \
 	$(gyp_shared_intermediate_dir)/content/jni/TracingIntentHandler_jni.h \
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi
index 53f1219..b0ff0c9 100644
--- a/content/content_renderer.gypi
+++ b/content/content_renderer.gypi
@@ -221,8 +221,6 @@
     'renderer/media/renderer_webaudiodevice_impl.h',
     'renderer/media/renderer_webmidiaccessor_impl.cc',
     'renderer/media/renderer_webmidiaccessor_impl.h',
-    'renderer/media/rtc_video_renderer.cc',
-    'renderer/media/rtc_video_renderer.h',
     'renderer/media/texttrack_impl.cc',
     'renderer/media/texttrack_impl.h',
     'renderer/media/video_capture_impl.cc',
@@ -369,8 +367,6 @@
     'renderer/pepper/ppb_proxy_impl.h',
     'renderer/pepper/ppb_scrollbar_impl.cc',
     'renderer/pepper/ppb_scrollbar_impl.h',
-    'renderer/pepper/ppb_tcp_socket_private_impl.cc',
-    'renderer/pepper/ppb_tcp_socket_private_impl.h',
     'renderer/pepper/ppb_uma_private_impl.cc',
     'renderer/pepper/ppb_uma_private_impl.h',
     'renderer/pepper/ppb_var_deprecated_impl.cc',
@@ -504,6 +500,8 @@
     'renderer/web_ui_extension_data.h',
     'renderer/webcrypto_impl.cc',
     'renderer/webcrypto_impl.h',
+    'renderer/webcrypto_impl_nss.cc',
+    'renderer/webcrypto_impl_openssl.cc',
     'renderer/websharedworker_proxy.cc',
     'renderer/websharedworker_proxy.h',
     'renderer/websharedworkerrepository_impl.cc',
@@ -587,6 +585,8 @@
         'renderer/media/media_stream_registry_interface.h',
         'renderer/media/media_stream_source_observer.cc',
         'renderer/media/media_stream_source_observer.h',
+        'renderer/media/native_handle_impl.cc',
+        'renderer/media/native_handle_impl.h',
         'renderer/media/peer_connection_handler_base.cc',
         'renderer/media/peer_connection_handler_base.h',
         'renderer/media/peer_connection_identity_service.cc',
@@ -615,6 +615,8 @@
         'renderer/media/rtc_video_encoder.h',
         'renderer/media/rtc_video_encoder_factory.cc',
         'renderer/media/rtc_video_encoder_factory.h',
+        'renderer/media/rtc_video_renderer.cc',
+        'renderer/media/rtc_video_renderer.h',
         'renderer/media/video_destination_handler.cc',
         'renderer/media/video_destination_handler.h',
         'renderer/media/video_source_handler.cc',
@@ -697,6 +699,30 @@
         'renderer/pepper/ppb_open_gl_es_impl.cc',
       ],
     }],
+    ['use_openssl==1', {
+      'sources!': [
+        'renderer/webcrypto_impl_nss.cc',
+      ],
+      'dependencies': [
+        '../third_party/openssl/openssl.gyp:openssl',
+      ],
+    }, {
+      'sources!': [
+        'renderer/webcrypto_impl_openssl.cc',
+      ],
+      'conditions': [
+        ['os_posix == 1 and OS != "mac" and OS != "ios" and OS != "android"', {
+          'dependencies': [
+            '../build/linux/system.gyp:ssl',
+          ],
+        }, {
+          'dependencies': [
+            '../third_party/nss/nss.gyp:nspr',
+            '../third_party/nss/nss.gyp:nss',
+          ],
+        }],
+      ],
+    }],
   ],
   'target_conditions': [
     ['OS=="android"', {
diff --git a/content/content_renderer.target.darwin-arm.mk b/content/content_renderer.target.darwin-arm.mk
index 2ee5958..b5f64bf 100644
--- a/content/content_renderer.target.darwin-arm.mk
+++ b/content/content_renderer.target.darwin-arm.mk
@@ -117,7 +117,6 @@
 	content/renderer/media/renderer_gpu_video_accelerator_factories.cc \
 	content/renderer/media/renderer_webaudiodevice_impl.cc \
 	content/renderer/media/renderer_webmidiaccessor_impl.cc \
-	content/renderer/media/rtc_video_renderer.cc \
 	content/renderer/media/texttrack_impl.cc \
 	content/renderer/media/video_capture_impl.cc \
 	content/renderer/media/video_capture_impl_manager.cc \
@@ -185,6 +184,7 @@
 	content/renderer/web_ui_extension.cc \
 	content/renderer/web_ui_extension_data.cc \
 	content/renderer/webcrypto_impl.cc \
+	content/renderer/webcrypto_impl_openssl.cc \
 	content/renderer/websharedworker_proxy.cc \
 	content/renderer/websharedworkerrepository_impl.cc
 
@@ -313,6 +313,7 @@
 	$(gyp_shared_intermediate_dir)/protoc_out \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
+	$(LOCAL_PATH)/third_party/openssl/openssl/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -455,6 +456,7 @@
 	$(gyp_shared_intermediate_dir)/protoc_out \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
+	$(LOCAL_PATH)/third_party/openssl/openssl/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/content/content_renderer.target.darwin-mips.mk b/content/content_renderer.target.darwin-mips.mk
index 4b2aa86..f8e5d67 100644
--- a/content/content_renderer.target.darwin-mips.mk
+++ b/content/content_renderer.target.darwin-mips.mk
@@ -117,7 +117,6 @@
 	content/renderer/media/renderer_gpu_video_accelerator_factories.cc \
 	content/renderer/media/renderer_webaudiodevice_impl.cc \
 	content/renderer/media/renderer_webmidiaccessor_impl.cc \
-	content/renderer/media/rtc_video_renderer.cc \
 	content/renderer/media/texttrack_impl.cc \
 	content/renderer/media/video_capture_impl.cc \
 	content/renderer/media/video_capture_impl_manager.cc \
@@ -185,6 +184,7 @@
 	content/renderer/web_ui_extension.cc \
 	content/renderer/web_ui_extension_data.cc \
 	content/renderer/webcrypto_impl.cc \
+	content/renderer/webcrypto_impl_openssl.cc \
 	content/renderer/websharedworker_proxy.cc \
 	content/renderer/websharedworkerrepository_impl.cc
 
@@ -312,6 +312,7 @@
 	$(gyp_shared_intermediate_dir)/protoc_out \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
+	$(LOCAL_PATH)/third_party/openssl/openssl/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -453,6 +454,7 @@
 	$(gyp_shared_intermediate_dir)/protoc_out \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
+	$(LOCAL_PATH)/third_party/openssl/openssl/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/content/content_renderer.target.darwin-x86.mk b/content/content_renderer.target.darwin-x86.mk
index dafdccf..8f93613 100644
--- a/content/content_renderer.target.darwin-x86.mk
+++ b/content/content_renderer.target.darwin-x86.mk
@@ -117,7 +117,6 @@
 	content/renderer/media/renderer_gpu_video_accelerator_factories.cc \
 	content/renderer/media/renderer_webaudiodevice_impl.cc \
 	content/renderer/media/renderer_webmidiaccessor_impl.cc \
-	content/renderer/media/rtc_video_renderer.cc \
 	content/renderer/media/texttrack_impl.cc \
 	content/renderer/media/video_capture_impl.cc \
 	content/renderer/media/video_capture_impl_manager.cc \
@@ -185,6 +184,7 @@
 	content/renderer/web_ui_extension.cc \
 	content/renderer/web_ui_extension_data.cc \
 	content/renderer/webcrypto_impl.cc \
+	content/renderer/webcrypto_impl_openssl.cc \
 	content/renderer/websharedworker_proxy.cc \
 	content/renderer/websharedworkerrepository_impl.cc
 
@@ -314,6 +314,7 @@
 	$(gyp_shared_intermediate_dir)/protoc_out \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
+	$(LOCAL_PATH)/third_party/openssl/openssl/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -458,6 +459,7 @@
 	$(gyp_shared_intermediate_dir)/protoc_out \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
+	$(LOCAL_PATH)/third_party/openssl/openssl/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/content/content_renderer.target.linux-arm.mk b/content/content_renderer.target.linux-arm.mk
index 2ee5958..b5f64bf 100644
--- a/content/content_renderer.target.linux-arm.mk
+++ b/content/content_renderer.target.linux-arm.mk
@@ -117,7 +117,6 @@
 	content/renderer/media/renderer_gpu_video_accelerator_factories.cc \
 	content/renderer/media/renderer_webaudiodevice_impl.cc \
 	content/renderer/media/renderer_webmidiaccessor_impl.cc \
-	content/renderer/media/rtc_video_renderer.cc \
 	content/renderer/media/texttrack_impl.cc \
 	content/renderer/media/video_capture_impl.cc \
 	content/renderer/media/video_capture_impl_manager.cc \
@@ -185,6 +184,7 @@
 	content/renderer/web_ui_extension.cc \
 	content/renderer/web_ui_extension_data.cc \
 	content/renderer/webcrypto_impl.cc \
+	content/renderer/webcrypto_impl_openssl.cc \
 	content/renderer/websharedworker_proxy.cc \
 	content/renderer/websharedworkerrepository_impl.cc
 
@@ -313,6 +313,7 @@
 	$(gyp_shared_intermediate_dir)/protoc_out \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
+	$(LOCAL_PATH)/third_party/openssl/openssl/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -455,6 +456,7 @@
 	$(gyp_shared_intermediate_dir)/protoc_out \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
+	$(LOCAL_PATH)/third_party/openssl/openssl/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/content/content_renderer.target.linux-mips.mk b/content/content_renderer.target.linux-mips.mk
index 4b2aa86..f8e5d67 100644
--- a/content/content_renderer.target.linux-mips.mk
+++ b/content/content_renderer.target.linux-mips.mk
@@ -117,7 +117,6 @@
 	content/renderer/media/renderer_gpu_video_accelerator_factories.cc \
 	content/renderer/media/renderer_webaudiodevice_impl.cc \
 	content/renderer/media/renderer_webmidiaccessor_impl.cc \
-	content/renderer/media/rtc_video_renderer.cc \
 	content/renderer/media/texttrack_impl.cc \
 	content/renderer/media/video_capture_impl.cc \
 	content/renderer/media/video_capture_impl_manager.cc \
@@ -185,6 +184,7 @@
 	content/renderer/web_ui_extension.cc \
 	content/renderer/web_ui_extension_data.cc \
 	content/renderer/webcrypto_impl.cc \
+	content/renderer/webcrypto_impl_openssl.cc \
 	content/renderer/websharedworker_proxy.cc \
 	content/renderer/websharedworkerrepository_impl.cc
 
@@ -312,6 +312,7 @@
 	$(gyp_shared_intermediate_dir)/protoc_out \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
+	$(LOCAL_PATH)/third_party/openssl/openssl/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -453,6 +454,7 @@
 	$(gyp_shared_intermediate_dir)/protoc_out \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
+	$(LOCAL_PATH)/third_party/openssl/openssl/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/content/content_renderer.target.linux-x86.mk b/content/content_renderer.target.linux-x86.mk
index dafdccf..8f93613 100644
--- a/content/content_renderer.target.linux-x86.mk
+++ b/content/content_renderer.target.linux-x86.mk
@@ -117,7 +117,6 @@
 	content/renderer/media/renderer_gpu_video_accelerator_factories.cc \
 	content/renderer/media/renderer_webaudiodevice_impl.cc \
 	content/renderer/media/renderer_webmidiaccessor_impl.cc \
-	content/renderer/media/rtc_video_renderer.cc \
 	content/renderer/media/texttrack_impl.cc \
 	content/renderer/media/video_capture_impl.cc \
 	content/renderer/media/video_capture_impl_manager.cc \
@@ -185,6 +184,7 @@
 	content/renderer/web_ui_extension.cc \
 	content/renderer/web_ui_extension_data.cc \
 	content/renderer/webcrypto_impl.cc \
+	content/renderer/webcrypto_impl_openssl.cc \
 	content/renderer/websharedworker_proxy.cc \
 	content/renderer/websharedworkerrepository_impl.cc
 
@@ -314,6 +314,7 @@
 	$(gyp_shared_intermediate_dir)/protoc_out \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
+	$(LOCAL_PATH)/third_party/openssl/openssl/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
@@ -458,6 +459,7 @@
 	$(gyp_shared_intermediate_dir)/protoc_out \
 	$(LOCAL_PATH)/third_party/protobuf \
 	$(LOCAL_PATH)/third_party/protobuf/src \
+	$(LOCAL_PATH)/third_party/openssl/openssl/include \
 	$(PWD)/frameworks/wilhelm/include \
 	$(PWD)/bionic \
 	$(PWD)/external/stlport/stlport
diff --git a/content/content_resources.target.darwin-arm.mk b/content/content_resources.target.darwin-arm.mk
index 87db013..bd6d492 100644
--- a/content/content_resources.target.darwin-arm.mk
+++ b/content/content_resources.target.darwin-arm.mk
@@ -18,7 +18,7 @@
 $(gyp_shared_intermediate_dir)/content/grit/content_resources.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/grit/content_resources.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/grit/content_resources.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/content/grit/content_resources.h: $(LOCAL_PATH)/tools/gritsettings/resource_ids $(LOCAL_PATH)/content/content_resources.grd $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.css $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.html $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.js $(LOCAL_PATH)/content/browser/resources/gpu/browser_bridge.js $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.html $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.js $(LOCAL_PATH)/content/browser/resources/gpu/info_view.css $(LOCAL_PATH)/content/browser/resources/gpu/info_view.html $(LOCAL_PATH)/content/browser/resources/gpu/info_view.js $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.css $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.html $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.js $(LOCAL_PATH)/content/browser/resources/media/cache_entry.js $(LOCAL_PATH)/content/browser/resources/media/data_series.js $(LOCAL_PATH)/content/browser/resources/media/disjoint_range_set.js $(LOCAL_PATH)/content/browser/resources/media/dump_creator.js $(LOCAL_PATH)/content/browser/resources/media/event_list.js $(LOCAL_PATH)/content/browser/resources/media/item_store.js $(LOCAL_PATH)/content/browser/resources/media/media_internals.css $(LOCAL_PATH)/content/browser/resources/media/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/media_player.js $(LOCAL_PATH)/content/browser/resources/media/metrics.js $(LOCAL_PATH)/content/browser/resources/media/new/client_renderer.js $(LOCAL_PATH)/content/browser/resources/media/new/main.js $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/new/player_info.js $(LOCAL_PATH)/content/browser/resources/media/new/player_manager.js $(LOCAL_PATH)/content/browser/resources/media/new/util.js $(LOCAL_PATH)/content/browser/resources/media/peer_connection_update_table.js $(LOCAL_PATH)/content/browser/resources/media/ssrc_info_manager.js $(LOCAL_PATH)/content/browser/resources/media/stats_graph_helper.js $(LOCAL_PATH)/content/browser/resources/media/stats_table.js $(LOCAL_PATH)/content/browser/resources/media/timeline_graph_view.js $(LOCAL_PATH)/content/browser/resources/media/util.js $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.css $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.html $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.js $(LOCAL_PATH)/tools/grit/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit.py $(LOCAL_PATH)/tools/grit/grit/__init__.py $(LOCAL_PATH)/tools/grit/grit/clique.py $(LOCAL_PATH)/tools/grit/grit/clique_unittest.py $(LOCAL_PATH)/tools/grit/grit/constants.py $(LOCAL_PATH)/tools/grit/grit/exception.py $(LOCAL_PATH)/tools/grit/grit/extern/BogoFP.py $(LOCAL_PATH)/tools/grit/grit/extern/FP.py $(LOCAL_PATH)/tools/grit/grit/extern/__init__.py $(LOCAL_PATH)/tools/grit/grit/extern/tclib.py $(LOCAL_PATH)/tools/grit/grit/format/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/c_format.py $(LOCAL_PATH)/tools/grit/grit/format/c_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/template_formatter.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writer_configuration.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/mock_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_helper.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/writer_unittest_common.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_formatted_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_writer_base_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/repack.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/__init__.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/interface.py $(LOCAL_PATH)/tools/grit/grit/gather/json_loader.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/rc.py $(LOCAL_PATH)/tools/grit/grit/gather/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/regexp.py $(LOCAL_PATH)/tools/grit/grit/gather/skeleton_gatherer.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/txt.py $(LOCAL_PATH)/tools/grit/grit/gather/txt_unittest.py $(LOCAL_PATH)/tools/grit/grit/grd_reader.py $(LOCAL_PATH)/tools/grit/grit/grd_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit/grit_runner.py $(LOCAL_PATH)/tools/grit/grit/grit_runner_unittest.py $(LOCAL_PATH)/tools/grit/grit/lazy_re.py $(LOCAL_PATH)/tools/grit/grit/lazy_re_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/base.py $(LOCAL_PATH)/tools/grit/grit/node/base_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/custom/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/empty.py $(LOCAL_PATH)/tools/grit/grit/node/include.py $(LOCAL_PATH)/tools/grit/grit/node/include_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/io.py $(LOCAL_PATH)/tools/grit/grit/node/io_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/mapping.py $(LOCAL_PATH)/tools/grit/grit/node/message.py $(LOCAL_PATH)/tools/grit/grit/node/message_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/misc.py $(LOCAL_PATH)/tools/grit/grit/node/misc_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/structure.py $(LOCAL_PATH)/tools/grit/grit/node/structure_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/variant.py $(LOCAL_PATH)/tools/grit/grit/pseudo.py $(LOCAL_PATH)/tools/grit/grit/pseudo_rtl.py $(LOCAL_PATH)/tools/grit/grit/pseudo_unittest.py $(LOCAL_PATH)/tools/grit/grit/scons.py $(LOCAL_PATH)/tools/grit/grit/shortcuts.py $(LOCAL_PATH)/tools/grit/grit/shortcuts_unittests.py $(LOCAL_PATH)/tools/grit/grit/tclib.py $(LOCAL_PATH)/tools/grit/grit/tclib_unittest.py $(LOCAL_PATH)/tools/grit/grit/test_suite_all.py $(LOCAL_PATH)/tools/grit/grit/tool/__init__.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/build.py $(LOCAL_PATH)/tools/grit/grit/tool/build_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/count.py $(LOCAL_PATH)/tools/grit/grit/tool/diff_structures.py $(LOCAL_PATH)/tools/grit/grit/tool/interface.py $(LOCAL_PATH)/tools/grit/grit/tool/menu_from_parts.py $(LOCAL_PATH)/tools/grit/grit/tool/newgrd.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/resize.py $(LOCAL_PATH)/tools/grit/grit/tool/test.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_postprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_preprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/unit.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb_unittest.py $(LOCAL_PATH)/tools/grit/grit/util.py $(LOCAL_PATH)/tools/grit/grit/util_unittest.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit_info.py $(GYP_TARGET_DEPENDENCIES)
+$(gyp_shared_intermediate_dir)/content/grit/content_resources.h: $(LOCAL_PATH)/tools/gritsettings/resource_ids $(LOCAL_PATH)/content/content_resources.grd $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.css $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.html $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.js $(LOCAL_PATH)/content/browser/resources/gpu/browser_bridge.js $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.html $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.js $(LOCAL_PATH)/content/browser/resources/gpu/info_view.css $(LOCAL_PATH)/content/browser/resources/gpu/info_view.html $(LOCAL_PATH)/content/browser/resources/gpu/info_view.js $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.css $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.html $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.js $(LOCAL_PATH)/content/browser/resources/media/cache_entry.js $(LOCAL_PATH)/content/browser/resources/media/data_series.js $(LOCAL_PATH)/content/browser/resources/media/disjoint_range_set.js $(LOCAL_PATH)/content/browser/resources/media/dump_creator.js $(LOCAL_PATH)/content/browser/resources/media/event_list.js $(LOCAL_PATH)/content/browser/resources/media/item_store.js $(LOCAL_PATH)/content/browser/resources/media/media_internals.css $(LOCAL_PATH)/content/browser/resources/media/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/media_player.js $(LOCAL_PATH)/content/browser/resources/media/metrics.js $(LOCAL_PATH)/content/browser/resources/media/new/client_renderer.js $(LOCAL_PATH)/content/browser/resources/media/new/main.js $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.css $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/new/player_info.js $(LOCAL_PATH)/content/browser/resources/media/new/player_manager.js $(LOCAL_PATH)/content/browser/resources/media/new/util.js $(LOCAL_PATH)/content/browser/resources/media/peer_connection_update_table.js $(LOCAL_PATH)/content/browser/resources/media/ssrc_info_manager.js $(LOCAL_PATH)/content/browser/resources/media/stats_graph_helper.js $(LOCAL_PATH)/content/browser/resources/media/stats_table.js $(LOCAL_PATH)/content/browser/resources/media/timeline_graph_view.js $(LOCAL_PATH)/content/browser/resources/media/util.js $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.css $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.html $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.js $(LOCAL_PATH)/tools/grit/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit.py $(LOCAL_PATH)/tools/grit/grit/__init__.py $(LOCAL_PATH)/tools/grit/grit/clique.py $(LOCAL_PATH)/tools/grit/grit/clique_unittest.py $(LOCAL_PATH)/tools/grit/grit/constants.py $(LOCAL_PATH)/tools/grit/grit/exception.py $(LOCAL_PATH)/tools/grit/grit/extern/BogoFP.py $(LOCAL_PATH)/tools/grit/grit/extern/FP.py $(LOCAL_PATH)/tools/grit/grit/extern/__init__.py $(LOCAL_PATH)/tools/grit/grit/extern/tclib.py $(LOCAL_PATH)/tools/grit/grit/format/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/c_format.py $(LOCAL_PATH)/tools/grit/grit/format/c_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/template_formatter.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writer_configuration.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/mock_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_helper.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/writer_unittest_common.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_formatted_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_writer_base_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/repack.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/__init__.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/interface.py $(LOCAL_PATH)/tools/grit/grit/gather/json_loader.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/rc.py $(LOCAL_PATH)/tools/grit/grit/gather/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/regexp.py $(LOCAL_PATH)/tools/grit/grit/gather/skeleton_gatherer.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/txt.py $(LOCAL_PATH)/tools/grit/grit/gather/txt_unittest.py $(LOCAL_PATH)/tools/grit/grit/grd_reader.py $(LOCAL_PATH)/tools/grit/grit/grd_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit/grit_runner.py $(LOCAL_PATH)/tools/grit/grit/grit_runner_unittest.py $(LOCAL_PATH)/tools/grit/grit/lazy_re.py $(LOCAL_PATH)/tools/grit/grit/lazy_re_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/base.py $(LOCAL_PATH)/tools/grit/grit/node/base_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/custom/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/empty.py $(LOCAL_PATH)/tools/grit/grit/node/include.py $(LOCAL_PATH)/tools/grit/grit/node/include_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/io.py $(LOCAL_PATH)/tools/grit/grit/node/io_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/mapping.py $(LOCAL_PATH)/tools/grit/grit/node/message.py $(LOCAL_PATH)/tools/grit/grit/node/message_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/misc.py $(LOCAL_PATH)/tools/grit/grit/node/misc_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/structure.py $(LOCAL_PATH)/tools/grit/grit/node/structure_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/variant.py $(LOCAL_PATH)/tools/grit/grit/pseudo.py $(LOCAL_PATH)/tools/grit/grit/pseudo_rtl.py $(LOCAL_PATH)/tools/grit/grit/pseudo_unittest.py $(LOCAL_PATH)/tools/grit/grit/scons.py $(LOCAL_PATH)/tools/grit/grit/shortcuts.py $(LOCAL_PATH)/tools/grit/grit/shortcuts_unittests.py $(LOCAL_PATH)/tools/grit/grit/tclib.py $(LOCAL_PATH)/tools/grit/grit/tclib_unittest.py $(LOCAL_PATH)/tools/grit/grit/test_suite_all.py $(LOCAL_PATH)/tools/grit/grit/tool/__init__.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/build.py $(LOCAL_PATH)/tools/grit/grit/tool/build_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/count.py $(LOCAL_PATH)/tools/grit/grit/tool/diff_structures.py $(LOCAL_PATH)/tools/grit/grit/tool/interface.py $(LOCAL_PATH)/tools/grit/grit/tool/menu_from_parts.py $(LOCAL_PATH)/tools/grit/grit/tool/newgrd.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/resize.py $(LOCAL_PATH)/tools/grit/grit/tool/test.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_postprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_preprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/unit.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb_unittest.py $(LOCAL_PATH)/tools/grit/grit/util.py $(LOCAL_PATH)/tools/grit/grit/util_unittest.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit_info.py $(GYP_TARGET_DEPENDENCIES)
 	@echo "Gyp action: Generating resources from content_resources.grd ($@)"
 	$(hide)cd $(gyp_local_path)/content; mkdir -p $(gyp_shared_intermediate_dir)/content/grit $(gyp_shared_intermediate_dir)/content; python ../tools/grit/grit.py -i content_resources.grd build -f ../tools/gritsettings/resource_ids -o "$(gyp_shared_intermediate_dir)/content" -D _chromium -E "CHROMIUM_BUILD=chromium" -t android -E "ANDROID_JAVA_TAGGED_ONLY=true" -D use_concatenated_impulse_responses
 
diff --git a/content/content_resources.target.darwin-mips.mk b/content/content_resources.target.darwin-mips.mk
index 87db013..bd6d492 100644
--- a/content/content_resources.target.darwin-mips.mk
+++ b/content/content_resources.target.darwin-mips.mk
@@ -18,7 +18,7 @@
 $(gyp_shared_intermediate_dir)/content/grit/content_resources.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/grit/content_resources.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/grit/content_resources.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/content/grit/content_resources.h: $(LOCAL_PATH)/tools/gritsettings/resource_ids $(LOCAL_PATH)/content/content_resources.grd $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.css $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.html $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.js $(LOCAL_PATH)/content/browser/resources/gpu/browser_bridge.js $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.html $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.js $(LOCAL_PATH)/content/browser/resources/gpu/info_view.css $(LOCAL_PATH)/content/browser/resources/gpu/info_view.html $(LOCAL_PATH)/content/browser/resources/gpu/info_view.js $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.css $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.html $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.js $(LOCAL_PATH)/content/browser/resources/media/cache_entry.js $(LOCAL_PATH)/content/browser/resources/media/data_series.js $(LOCAL_PATH)/content/browser/resources/media/disjoint_range_set.js $(LOCAL_PATH)/content/browser/resources/media/dump_creator.js $(LOCAL_PATH)/content/browser/resources/media/event_list.js $(LOCAL_PATH)/content/browser/resources/media/item_store.js $(LOCAL_PATH)/content/browser/resources/media/media_internals.css $(LOCAL_PATH)/content/browser/resources/media/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/media_player.js $(LOCAL_PATH)/content/browser/resources/media/metrics.js $(LOCAL_PATH)/content/browser/resources/media/new/client_renderer.js $(LOCAL_PATH)/content/browser/resources/media/new/main.js $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/new/player_info.js $(LOCAL_PATH)/content/browser/resources/media/new/player_manager.js $(LOCAL_PATH)/content/browser/resources/media/new/util.js $(LOCAL_PATH)/content/browser/resources/media/peer_connection_update_table.js $(LOCAL_PATH)/content/browser/resources/media/ssrc_info_manager.js $(LOCAL_PATH)/content/browser/resources/media/stats_graph_helper.js $(LOCAL_PATH)/content/browser/resources/media/stats_table.js $(LOCAL_PATH)/content/browser/resources/media/timeline_graph_view.js $(LOCAL_PATH)/content/browser/resources/media/util.js $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.css $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.html $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.js $(LOCAL_PATH)/tools/grit/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit.py $(LOCAL_PATH)/tools/grit/grit/__init__.py $(LOCAL_PATH)/tools/grit/grit/clique.py $(LOCAL_PATH)/tools/grit/grit/clique_unittest.py $(LOCAL_PATH)/tools/grit/grit/constants.py $(LOCAL_PATH)/tools/grit/grit/exception.py $(LOCAL_PATH)/tools/grit/grit/extern/BogoFP.py $(LOCAL_PATH)/tools/grit/grit/extern/FP.py $(LOCAL_PATH)/tools/grit/grit/extern/__init__.py $(LOCAL_PATH)/tools/grit/grit/extern/tclib.py $(LOCAL_PATH)/tools/grit/grit/format/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/c_format.py $(LOCAL_PATH)/tools/grit/grit/format/c_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/template_formatter.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writer_configuration.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/mock_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_helper.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/writer_unittest_common.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_formatted_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_writer_base_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/repack.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/__init__.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/interface.py $(LOCAL_PATH)/tools/grit/grit/gather/json_loader.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/rc.py $(LOCAL_PATH)/tools/grit/grit/gather/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/regexp.py $(LOCAL_PATH)/tools/grit/grit/gather/skeleton_gatherer.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/txt.py $(LOCAL_PATH)/tools/grit/grit/gather/txt_unittest.py $(LOCAL_PATH)/tools/grit/grit/grd_reader.py $(LOCAL_PATH)/tools/grit/grit/grd_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit/grit_runner.py $(LOCAL_PATH)/tools/grit/grit/grit_runner_unittest.py $(LOCAL_PATH)/tools/grit/grit/lazy_re.py $(LOCAL_PATH)/tools/grit/grit/lazy_re_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/base.py $(LOCAL_PATH)/tools/grit/grit/node/base_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/custom/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/empty.py $(LOCAL_PATH)/tools/grit/grit/node/include.py $(LOCAL_PATH)/tools/grit/grit/node/include_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/io.py $(LOCAL_PATH)/tools/grit/grit/node/io_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/mapping.py $(LOCAL_PATH)/tools/grit/grit/node/message.py $(LOCAL_PATH)/tools/grit/grit/node/message_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/misc.py $(LOCAL_PATH)/tools/grit/grit/node/misc_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/structure.py $(LOCAL_PATH)/tools/grit/grit/node/structure_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/variant.py $(LOCAL_PATH)/tools/grit/grit/pseudo.py $(LOCAL_PATH)/tools/grit/grit/pseudo_rtl.py $(LOCAL_PATH)/tools/grit/grit/pseudo_unittest.py $(LOCAL_PATH)/tools/grit/grit/scons.py $(LOCAL_PATH)/tools/grit/grit/shortcuts.py $(LOCAL_PATH)/tools/grit/grit/shortcuts_unittests.py $(LOCAL_PATH)/tools/grit/grit/tclib.py $(LOCAL_PATH)/tools/grit/grit/tclib_unittest.py $(LOCAL_PATH)/tools/grit/grit/test_suite_all.py $(LOCAL_PATH)/tools/grit/grit/tool/__init__.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/build.py $(LOCAL_PATH)/tools/grit/grit/tool/build_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/count.py $(LOCAL_PATH)/tools/grit/grit/tool/diff_structures.py $(LOCAL_PATH)/tools/grit/grit/tool/interface.py $(LOCAL_PATH)/tools/grit/grit/tool/menu_from_parts.py $(LOCAL_PATH)/tools/grit/grit/tool/newgrd.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/resize.py $(LOCAL_PATH)/tools/grit/grit/tool/test.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_postprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_preprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/unit.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb_unittest.py $(LOCAL_PATH)/tools/grit/grit/util.py $(LOCAL_PATH)/tools/grit/grit/util_unittest.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit_info.py $(GYP_TARGET_DEPENDENCIES)
+$(gyp_shared_intermediate_dir)/content/grit/content_resources.h: $(LOCAL_PATH)/tools/gritsettings/resource_ids $(LOCAL_PATH)/content/content_resources.grd $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.css $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.html $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.js $(LOCAL_PATH)/content/browser/resources/gpu/browser_bridge.js $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.html $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.js $(LOCAL_PATH)/content/browser/resources/gpu/info_view.css $(LOCAL_PATH)/content/browser/resources/gpu/info_view.html $(LOCAL_PATH)/content/browser/resources/gpu/info_view.js $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.css $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.html $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.js $(LOCAL_PATH)/content/browser/resources/media/cache_entry.js $(LOCAL_PATH)/content/browser/resources/media/data_series.js $(LOCAL_PATH)/content/browser/resources/media/disjoint_range_set.js $(LOCAL_PATH)/content/browser/resources/media/dump_creator.js $(LOCAL_PATH)/content/browser/resources/media/event_list.js $(LOCAL_PATH)/content/browser/resources/media/item_store.js $(LOCAL_PATH)/content/browser/resources/media/media_internals.css $(LOCAL_PATH)/content/browser/resources/media/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/media_player.js $(LOCAL_PATH)/content/browser/resources/media/metrics.js $(LOCAL_PATH)/content/browser/resources/media/new/client_renderer.js $(LOCAL_PATH)/content/browser/resources/media/new/main.js $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.css $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/new/player_info.js $(LOCAL_PATH)/content/browser/resources/media/new/player_manager.js $(LOCAL_PATH)/content/browser/resources/media/new/util.js $(LOCAL_PATH)/content/browser/resources/media/peer_connection_update_table.js $(LOCAL_PATH)/content/browser/resources/media/ssrc_info_manager.js $(LOCAL_PATH)/content/browser/resources/media/stats_graph_helper.js $(LOCAL_PATH)/content/browser/resources/media/stats_table.js $(LOCAL_PATH)/content/browser/resources/media/timeline_graph_view.js $(LOCAL_PATH)/content/browser/resources/media/util.js $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.css $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.html $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.js $(LOCAL_PATH)/tools/grit/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit.py $(LOCAL_PATH)/tools/grit/grit/__init__.py $(LOCAL_PATH)/tools/grit/grit/clique.py $(LOCAL_PATH)/tools/grit/grit/clique_unittest.py $(LOCAL_PATH)/tools/grit/grit/constants.py $(LOCAL_PATH)/tools/grit/grit/exception.py $(LOCAL_PATH)/tools/grit/grit/extern/BogoFP.py $(LOCAL_PATH)/tools/grit/grit/extern/FP.py $(LOCAL_PATH)/tools/grit/grit/extern/__init__.py $(LOCAL_PATH)/tools/grit/grit/extern/tclib.py $(LOCAL_PATH)/tools/grit/grit/format/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/c_format.py $(LOCAL_PATH)/tools/grit/grit/format/c_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/template_formatter.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writer_configuration.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/mock_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_helper.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/writer_unittest_common.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_formatted_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_writer_base_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/repack.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/__init__.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/interface.py $(LOCAL_PATH)/tools/grit/grit/gather/json_loader.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/rc.py $(LOCAL_PATH)/tools/grit/grit/gather/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/regexp.py $(LOCAL_PATH)/tools/grit/grit/gather/skeleton_gatherer.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/txt.py $(LOCAL_PATH)/tools/grit/grit/gather/txt_unittest.py $(LOCAL_PATH)/tools/grit/grit/grd_reader.py $(LOCAL_PATH)/tools/grit/grit/grd_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit/grit_runner.py $(LOCAL_PATH)/tools/grit/grit/grit_runner_unittest.py $(LOCAL_PATH)/tools/grit/grit/lazy_re.py $(LOCAL_PATH)/tools/grit/grit/lazy_re_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/base.py $(LOCAL_PATH)/tools/grit/grit/node/base_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/custom/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/empty.py $(LOCAL_PATH)/tools/grit/grit/node/include.py $(LOCAL_PATH)/tools/grit/grit/node/include_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/io.py $(LOCAL_PATH)/tools/grit/grit/node/io_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/mapping.py $(LOCAL_PATH)/tools/grit/grit/node/message.py $(LOCAL_PATH)/tools/grit/grit/node/message_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/misc.py $(LOCAL_PATH)/tools/grit/grit/node/misc_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/structure.py $(LOCAL_PATH)/tools/grit/grit/node/structure_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/variant.py $(LOCAL_PATH)/tools/grit/grit/pseudo.py $(LOCAL_PATH)/tools/grit/grit/pseudo_rtl.py $(LOCAL_PATH)/tools/grit/grit/pseudo_unittest.py $(LOCAL_PATH)/tools/grit/grit/scons.py $(LOCAL_PATH)/tools/grit/grit/shortcuts.py $(LOCAL_PATH)/tools/grit/grit/shortcuts_unittests.py $(LOCAL_PATH)/tools/grit/grit/tclib.py $(LOCAL_PATH)/tools/grit/grit/tclib_unittest.py $(LOCAL_PATH)/tools/grit/grit/test_suite_all.py $(LOCAL_PATH)/tools/grit/grit/tool/__init__.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/build.py $(LOCAL_PATH)/tools/grit/grit/tool/build_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/count.py $(LOCAL_PATH)/tools/grit/grit/tool/diff_structures.py $(LOCAL_PATH)/tools/grit/grit/tool/interface.py $(LOCAL_PATH)/tools/grit/grit/tool/menu_from_parts.py $(LOCAL_PATH)/tools/grit/grit/tool/newgrd.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/resize.py $(LOCAL_PATH)/tools/grit/grit/tool/test.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_postprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_preprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/unit.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb_unittest.py $(LOCAL_PATH)/tools/grit/grit/util.py $(LOCAL_PATH)/tools/grit/grit/util_unittest.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit_info.py $(GYP_TARGET_DEPENDENCIES)
 	@echo "Gyp action: Generating resources from content_resources.grd ($@)"
 	$(hide)cd $(gyp_local_path)/content; mkdir -p $(gyp_shared_intermediate_dir)/content/grit $(gyp_shared_intermediate_dir)/content; python ../tools/grit/grit.py -i content_resources.grd build -f ../tools/gritsettings/resource_ids -o "$(gyp_shared_intermediate_dir)/content" -D _chromium -E "CHROMIUM_BUILD=chromium" -t android -E "ANDROID_JAVA_TAGGED_ONLY=true" -D use_concatenated_impulse_responses
 
diff --git a/content/content_resources.target.darwin-x86.mk b/content/content_resources.target.darwin-x86.mk
index 87db013..bd6d492 100644
--- a/content/content_resources.target.darwin-x86.mk
+++ b/content/content_resources.target.darwin-x86.mk
@@ -18,7 +18,7 @@
 $(gyp_shared_intermediate_dir)/content/grit/content_resources.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/grit/content_resources.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/grit/content_resources.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/content/grit/content_resources.h: $(LOCAL_PATH)/tools/gritsettings/resource_ids $(LOCAL_PATH)/content/content_resources.grd $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.css $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.html $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.js $(LOCAL_PATH)/content/browser/resources/gpu/browser_bridge.js $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.html $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.js $(LOCAL_PATH)/content/browser/resources/gpu/info_view.css $(LOCAL_PATH)/content/browser/resources/gpu/info_view.html $(LOCAL_PATH)/content/browser/resources/gpu/info_view.js $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.css $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.html $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.js $(LOCAL_PATH)/content/browser/resources/media/cache_entry.js $(LOCAL_PATH)/content/browser/resources/media/data_series.js $(LOCAL_PATH)/content/browser/resources/media/disjoint_range_set.js $(LOCAL_PATH)/content/browser/resources/media/dump_creator.js $(LOCAL_PATH)/content/browser/resources/media/event_list.js $(LOCAL_PATH)/content/browser/resources/media/item_store.js $(LOCAL_PATH)/content/browser/resources/media/media_internals.css $(LOCAL_PATH)/content/browser/resources/media/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/media_player.js $(LOCAL_PATH)/content/browser/resources/media/metrics.js $(LOCAL_PATH)/content/browser/resources/media/new/client_renderer.js $(LOCAL_PATH)/content/browser/resources/media/new/main.js $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/new/player_info.js $(LOCAL_PATH)/content/browser/resources/media/new/player_manager.js $(LOCAL_PATH)/content/browser/resources/media/new/util.js $(LOCAL_PATH)/content/browser/resources/media/peer_connection_update_table.js $(LOCAL_PATH)/content/browser/resources/media/ssrc_info_manager.js $(LOCAL_PATH)/content/browser/resources/media/stats_graph_helper.js $(LOCAL_PATH)/content/browser/resources/media/stats_table.js $(LOCAL_PATH)/content/browser/resources/media/timeline_graph_view.js $(LOCAL_PATH)/content/browser/resources/media/util.js $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.css $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.html $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.js $(LOCAL_PATH)/tools/grit/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit.py $(LOCAL_PATH)/tools/grit/grit/__init__.py $(LOCAL_PATH)/tools/grit/grit/clique.py $(LOCAL_PATH)/tools/grit/grit/clique_unittest.py $(LOCAL_PATH)/tools/grit/grit/constants.py $(LOCAL_PATH)/tools/grit/grit/exception.py $(LOCAL_PATH)/tools/grit/grit/extern/BogoFP.py $(LOCAL_PATH)/tools/grit/grit/extern/FP.py $(LOCAL_PATH)/tools/grit/grit/extern/__init__.py $(LOCAL_PATH)/tools/grit/grit/extern/tclib.py $(LOCAL_PATH)/tools/grit/grit/format/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/c_format.py $(LOCAL_PATH)/tools/grit/grit/format/c_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/template_formatter.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writer_configuration.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/mock_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_helper.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/writer_unittest_common.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_formatted_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_writer_base_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/repack.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/__init__.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/interface.py $(LOCAL_PATH)/tools/grit/grit/gather/json_loader.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/rc.py $(LOCAL_PATH)/tools/grit/grit/gather/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/regexp.py $(LOCAL_PATH)/tools/grit/grit/gather/skeleton_gatherer.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/txt.py $(LOCAL_PATH)/tools/grit/grit/gather/txt_unittest.py $(LOCAL_PATH)/tools/grit/grit/grd_reader.py $(LOCAL_PATH)/tools/grit/grit/grd_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit/grit_runner.py $(LOCAL_PATH)/tools/grit/grit/grit_runner_unittest.py $(LOCAL_PATH)/tools/grit/grit/lazy_re.py $(LOCAL_PATH)/tools/grit/grit/lazy_re_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/base.py $(LOCAL_PATH)/tools/grit/grit/node/base_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/custom/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/empty.py $(LOCAL_PATH)/tools/grit/grit/node/include.py $(LOCAL_PATH)/tools/grit/grit/node/include_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/io.py $(LOCAL_PATH)/tools/grit/grit/node/io_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/mapping.py $(LOCAL_PATH)/tools/grit/grit/node/message.py $(LOCAL_PATH)/tools/grit/grit/node/message_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/misc.py $(LOCAL_PATH)/tools/grit/grit/node/misc_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/structure.py $(LOCAL_PATH)/tools/grit/grit/node/structure_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/variant.py $(LOCAL_PATH)/tools/grit/grit/pseudo.py $(LOCAL_PATH)/tools/grit/grit/pseudo_rtl.py $(LOCAL_PATH)/tools/grit/grit/pseudo_unittest.py $(LOCAL_PATH)/tools/grit/grit/scons.py $(LOCAL_PATH)/tools/grit/grit/shortcuts.py $(LOCAL_PATH)/tools/grit/grit/shortcuts_unittests.py $(LOCAL_PATH)/tools/grit/grit/tclib.py $(LOCAL_PATH)/tools/grit/grit/tclib_unittest.py $(LOCAL_PATH)/tools/grit/grit/test_suite_all.py $(LOCAL_PATH)/tools/grit/grit/tool/__init__.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/build.py $(LOCAL_PATH)/tools/grit/grit/tool/build_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/count.py $(LOCAL_PATH)/tools/grit/grit/tool/diff_structures.py $(LOCAL_PATH)/tools/grit/grit/tool/interface.py $(LOCAL_PATH)/tools/grit/grit/tool/menu_from_parts.py $(LOCAL_PATH)/tools/grit/grit/tool/newgrd.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/resize.py $(LOCAL_PATH)/tools/grit/grit/tool/test.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_postprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_preprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/unit.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb_unittest.py $(LOCAL_PATH)/tools/grit/grit/util.py $(LOCAL_PATH)/tools/grit/grit/util_unittest.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit_info.py $(GYP_TARGET_DEPENDENCIES)
+$(gyp_shared_intermediate_dir)/content/grit/content_resources.h: $(LOCAL_PATH)/tools/gritsettings/resource_ids $(LOCAL_PATH)/content/content_resources.grd $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.css $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.html $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.js $(LOCAL_PATH)/content/browser/resources/gpu/browser_bridge.js $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.html $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.js $(LOCAL_PATH)/content/browser/resources/gpu/info_view.css $(LOCAL_PATH)/content/browser/resources/gpu/info_view.html $(LOCAL_PATH)/content/browser/resources/gpu/info_view.js $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.css $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.html $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.js $(LOCAL_PATH)/content/browser/resources/media/cache_entry.js $(LOCAL_PATH)/content/browser/resources/media/data_series.js $(LOCAL_PATH)/content/browser/resources/media/disjoint_range_set.js $(LOCAL_PATH)/content/browser/resources/media/dump_creator.js $(LOCAL_PATH)/content/browser/resources/media/event_list.js $(LOCAL_PATH)/content/browser/resources/media/item_store.js $(LOCAL_PATH)/content/browser/resources/media/media_internals.css $(LOCAL_PATH)/content/browser/resources/media/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/media_player.js $(LOCAL_PATH)/content/browser/resources/media/metrics.js $(LOCAL_PATH)/content/browser/resources/media/new/client_renderer.js $(LOCAL_PATH)/content/browser/resources/media/new/main.js $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.css $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/new/player_info.js $(LOCAL_PATH)/content/browser/resources/media/new/player_manager.js $(LOCAL_PATH)/content/browser/resources/media/new/util.js $(LOCAL_PATH)/content/browser/resources/media/peer_connection_update_table.js $(LOCAL_PATH)/content/browser/resources/media/ssrc_info_manager.js $(LOCAL_PATH)/content/browser/resources/media/stats_graph_helper.js $(LOCAL_PATH)/content/browser/resources/media/stats_table.js $(LOCAL_PATH)/content/browser/resources/media/timeline_graph_view.js $(LOCAL_PATH)/content/browser/resources/media/util.js $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.css $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.html $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.js $(LOCAL_PATH)/tools/grit/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit.py $(LOCAL_PATH)/tools/grit/grit/__init__.py $(LOCAL_PATH)/tools/grit/grit/clique.py $(LOCAL_PATH)/tools/grit/grit/clique_unittest.py $(LOCAL_PATH)/tools/grit/grit/constants.py $(LOCAL_PATH)/tools/grit/grit/exception.py $(LOCAL_PATH)/tools/grit/grit/extern/BogoFP.py $(LOCAL_PATH)/tools/grit/grit/extern/FP.py $(LOCAL_PATH)/tools/grit/grit/extern/__init__.py $(LOCAL_PATH)/tools/grit/grit/extern/tclib.py $(LOCAL_PATH)/tools/grit/grit/format/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/c_format.py $(LOCAL_PATH)/tools/grit/grit/format/c_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/template_formatter.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writer_configuration.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/mock_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_helper.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/writer_unittest_common.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_formatted_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_writer_base_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/repack.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/__init__.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/interface.py $(LOCAL_PATH)/tools/grit/grit/gather/json_loader.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/rc.py $(LOCAL_PATH)/tools/grit/grit/gather/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/regexp.py $(LOCAL_PATH)/tools/grit/grit/gather/skeleton_gatherer.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/txt.py $(LOCAL_PATH)/tools/grit/grit/gather/txt_unittest.py $(LOCAL_PATH)/tools/grit/grit/grd_reader.py $(LOCAL_PATH)/tools/grit/grit/grd_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit/grit_runner.py $(LOCAL_PATH)/tools/grit/grit/grit_runner_unittest.py $(LOCAL_PATH)/tools/grit/grit/lazy_re.py $(LOCAL_PATH)/tools/grit/grit/lazy_re_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/base.py $(LOCAL_PATH)/tools/grit/grit/node/base_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/custom/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/empty.py $(LOCAL_PATH)/tools/grit/grit/node/include.py $(LOCAL_PATH)/tools/grit/grit/node/include_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/io.py $(LOCAL_PATH)/tools/grit/grit/node/io_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/mapping.py $(LOCAL_PATH)/tools/grit/grit/node/message.py $(LOCAL_PATH)/tools/grit/grit/node/message_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/misc.py $(LOCAL_PATH)/tools/grit/grit/node/misc_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/structure.py $(LOCAL_PATH)/tools/grit/grit/node/structure_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/variant.py $(LOCAL_PATH)/tools/grit/grit/pseudo.py $(LOCAL_PATH)/tools/grit/grit/pseudo_rtl.py $(LOCAL_PATH)/tools/grit/grit/pseudo_unittest.py $(LOCAL_PATH)/tools/grit/grit/scons.py $(LOCAL_PATH)/tools/grit/grit/shortcuts.py $(LOCAL_PATH)/tools/grit/grit/shortcuts_unittests.py $(LOCAL_PATH)/tools/grit/grit/tclib.py $(LOCAL_PATH)/tools/grit/grit/tclib_unittest.py $(LOCAL_PATH)/tools/grit/grit/test_suite_all.py $(LOCAL_PATH)/tools/grit/grit/tool/__init__.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/build.py $(LOCAL_PATH)/tools/grit/grit/tool/build_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/count.py $(LOCAL_PATH)/tools/grit/grit/tool/diff_structures.py $(LOCAL_PATH)/tools/grit/grit/tool/interface.py $(LOCAL_PATH)/tools/grit/grit/tool/menu_from_parts.py $(LOCAL_PATH)/tools/grit/grit/tool/newgrd.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/resize.py $(LOCAL_PATH)/tools/grit/grit/tool/test.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_postprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_preprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/unit.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb_unittest.py $(LOCAL_PATH)/tools/grit/grit/util.py $(LOCAL_PATH)/tools/grit/grit/util_unittest.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit_info.py $(GYP_TARGET_DEPENDENCIES)
 	@echo "Gyp action: Generating resources from content_resources.grd ($@)"
 	$(hide)cd $(gyp_local_path)/content; mkdir -p $(gyp_shared_intermediate_dir)/content/grit $(gyp_shared_intermediate_dir)/content; python ../tools/grit/grit.py -i content_resources.grd build -f ../tools/gritsettings/resource_ids -o "$(gyp_shared_intermediate_dir)/content" -D _chromium -E "CHROMIUM_BUILD=chromium" -t android -E "ANDROID_JAVA_TAGGED_ONLY=true" -D use_concatenated_impulse_responses
 
diff --git a/content/content_resources.target.linux-arm.mk b/content/content_resources.target.linux-arm.mk
index 87db013..bd6d492 100644
--- a/content/content_resources.target.linux-arm.mk
+++ b/content/content_resources.target.linux-arm.mk
@@ -18,7 +18,7 @@
 $(gyp_shared_intermediate_dir)/content/grit/content_resources.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/grit/content_resources.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/grit/content_resources.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/content/grit/content_resources.h: $(LOCAL_PATH)/tools/gritsettings/resource_ids $(LOCAL_PATH)/content/content_resources.grd $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.css $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.html $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.js $(LOCAL_PATH)/content/browser/resources/gpu/browser_bridge.js $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.html $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.js $(LOCAL_PATH)/content/browser/resources/gpu/info_view.css $(LOCAL_PATH)/content/browser/resources/gpu/info_view.html $(LOCAL_PATH)/content/browser/resources/gpu/info_view.js $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.css $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.html $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.js $(LOCAL_PATH)/content/browser/resources/media/cache_entry.js $(LOCAL_PATH)/content/browser/resources/media/data_series.js $(LOCAL_PATH)/content/browser/resources/media/disjoint_range_set.js $(LOCAL_PATH)/content/browser/resources/media/dump_creator.js $(LOCAL_PATH)/content/browser/resources/media/event_list.js $(LOCAL_PATH)/content/browser/resources/media/item_store.js $(LOCAL_PATH)/content/browser/resources/media/media_internals.css $(LOCAL_PATH)/content/browser/resources/media/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/media_player.js $(LOCAL_PATH)/content/browser/resources/media/metrics.js $(LOCAL_PATH)/content/browser/resources/media/new/client_renderer.js $(LOCAL_PATH)/content/browser/resources/media/new/main.js $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/new/player_info.js $(LOCAL_PATH)/content/browser/resources/media/new/player_manager.js $(LOCAL_PATH)/content/browser/resources/media/new/util.js $(LOCAL_PATH)/content/browser/resources/media/peer_connection_update_table.js $(LOCAL_PATH)/content/browser/resources/media/ssrc_info_manager.js $(LOCAL_PATH)/content/browser/resources/media/stats_graph_helper.js $(LOCAL_PATH)/content/browser/resources/media/stats_table.js $(LOCAL_PATH)/content/browser/resources/media/timeline_graph_view.js $(LOCAL_PATH)/content/browser/resources/media/util.js $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.css $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.html $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.js $(LOCAL_PATH)/tools/grit/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit.py $(LOCAL_PATH)/tools/grit/grit/__init__.py $(LOCAL_PATH)/tools/grit/grit/clique.py $(LOCAL_PATH)/tools/grit/grit/clique_unittest.py $(LOCAL_PATH)/tools/grit/grit/constants.py $(LOCAL_PATH)/tools/grit/grit/exception.py $(LOCAL_PATH)/tools/grit/grit/extern/BogoFP.py $(LOCAL_PATH)/tools/grit/grit/extern/FP.py $(LOCAL_PATH)/tools/grit/grit/extern/__init__.py $(LOCAL_PATH)/tools/grit/grit/extern/tclib.py $(LOCAL_PATH)/tools/grit/grit/format/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/c_format.py $(LOCAL_PATH)/tools/grit/grit/format/c_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/template_formatter.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writer_configuration.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/mock_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_helper.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/writer_unittest_common.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_formatted_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_writer_base_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/repack.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/__init__.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/interface.py $(LOCAL_PATH)/tools/grit/grit/gather/json_loader.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/rc.py $(LOCAL_PATH)/tools/grit/grit/gather/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/regexp.py $(LOCAL_PATH)/tools/grit/grit/gather/skeleton_gatherer.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/txt.py $(LOCAL_PATH)/tools/grit/grit/gather/txt_unittest.py $(LOCAL_PATH)/tools/grit/grit/grd_reader.py $(LOCAL_PATH)/tools/grit/grit/grd_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit/grit_runner.py $(LOCAL_PATH)/tools/grit/grit/grit_runner_unittest.py $(LOCAL_PATH)/tools/grit/grit/lazy_re.py $(LOCAL_PATH)/tools/grit/grit/lazy_re_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/base.py $(LOCAL_PATH)/tools/grit/grit/node/base_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/custom/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/empty.py $(LOCAL_PATH)/tools/grit/grit/node/include.py $(LOCAL_PATH)/tools/grit/grit/node/include_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/io.py $(LOCAL_PATH)/tools/grit/grit/node/io_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/mapping.py $(LOCAL_PATH)/tools/grit/grit/node/message.py $(LOCAL_PATH)/tools/grit/grit/node/message_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/misc.py $(LOCAL_PATH)/tools/grit/grit/node/misc_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/structure.py $(LOCAL_PATH)/tools/grit/grit/node/structure_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/variant.py $(LOCAL_PATH)/tools/grit/grit/pseudo.py $(LOCAL_PATH)/tools/grit/grit/pseudo_rtl.py $(LOCAL_PATH)/tools/grit/grit/pseudo_unittest.py $(LOCAL_PATH)/tools/grit/grit/scons.py $(LOCAL_PATH)/tools/grit/grit/shortcuts.py $(LOCAL_PATH)/tools/grit/grit/shortcuts_unittests.py $(LOCAL_PATH)/tools/grit/grit/tclib.py $(LOCAL_PATH)/tools/grit/grit/tclib_unittest.py $(LOCAL_PATH)/tools/grit/grit/test_suite_all.py $(LOCAL_PATH)/tools/grit/grit/tool/__init__.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/build.py $(LOCAL_PATH)/tools/grit/grit/tool/build_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/count.py $(LOCAL_PATH)/tools/grit/grit/tool/diff_structures.py $(LOCAL_PATH)/tools/grit/grit/tool/interface.py $(LOCAL_PATH)/tools/grit/grit/tool/menu_from_parts.py $(LOCAL_PATH)/tools/grit/grit/tool/newgrd.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/resize.py $(LOCAL_PATH)/tools/grit/grit/tool/test.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_postprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_preprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/unit.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb_unittest.py $(LOCAL_PATH)/tools/grit/grit/util.py $(LOCAL_PATH)/tools/grit/grit/util_unittest.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit_info.py $(GYP_TARGET_DEPENDENCIES)
+$(gyp_shared_intermediate_dir)/content/grit/content_resources.h: $(LOCAL_PATH)/tools/gritsettings/resource_ids $(LOCAL_PATH)/content/content_resources.grd $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.css $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.html $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.js $(LOCAL_PATH)/content/browser/resources/gpu/browser_bridge.js $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.html $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.js $(LOCAL_PATH)/content/browser/resources/gpu/info_view.css $(LOCAL_PATH)/content/browser/resources/gpu/info_view.html $(LOCAL_PATH)/content/browser/resources/gpu/info_view.js $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.css $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.html $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.js $(LOCAL_PATH)/content/browser/resources/media/cache_entry.js $(LOCAL_PATH)/content/browser/resources/media/data_series.js $(LOCAL_PATH)/content/browser/resources/media/disjoint_range_set.js $(LOCAL_PATH)/content/browser/resources/media/dump_creator.js $(LOCAL_PATH)/content/browser/resources/media/event_list.js $(LOCAL_PATH)/content/browser/resources/media/item_store.js $(LOCAL_PATH)/content/browser/resources/media/media_internals.css $(LOCAL_PATH)/content/browser/resources/media/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/media_player.js $(LOCAL_PATH)/content/browser/resources/media/metrics.js $(LOCAL_PATH)/content/browser/resources/media/new/client_renderer.js $(LOCAL_PATH)/content/browser/resources/media/new/main.js $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.css $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/new/player_info.js $(LOCAL_PATH)/content/browser/resources/media/new/player_manager.js $(LOCAL_PATH)/content/browser/resources/media/new/util.js $(LOCAL_PATH)/content/browser/resources/media/peer_connection_update_table.js $(LOCAL_PATH)/content/browser/resources/media/ssrc_info_manager.js $(LOCAL_PATH)/content/browser/resources/media/stats_graph_helper.js $(LOCAL_PATH)/content/browser/resources/media/stats_table.js $(LOCAL_PATH)/content/browser/resources/media/timeline_graph_view.js $(LOCAL_PATH)/content/browser/resources/media/util.js $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.css $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.html $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.js $(LOCAL_PATH)/tools/grit/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit.py $(LOCAL_PATH)/tools/grit/grit/__init__.py $(LOCAL_PATH)/tools/grit/grit/clique.py $(LOCAL_PATH)/tools/grit/grit/clique_unittest.py $(LOCAL_PATH)/tools/grit/grit/constants.py $(LOCAL_PATH)/tools/grit/grit/exception.py $(LOCAL_PATH)/tools/grit/grit/extern/BogoFP.py $(LOCAL_PATH)/tools/grit/grit/extern/FP.py $(LOCAL_PATH)/tools/grit/grit/extern/__init__.py $(LOCAL_PATH)/tools/grit/grit/extern/tclib.py $(LOCAL_PATH)/tools/grit/grit/format/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/c_format.py $(LOCAL_PATH)/tools/grit/grit/format/c_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/template_formatter.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writer_configuration.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/mock_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_helper.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/writer_unittest_common.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_formatted_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_writer_base_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/repack.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/__init__.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/interface.py $(LOCAL_PATH)/tools/grit/grit/gather/json_loader.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/rc.py $(LOCAL_PATH)/tools/grit/grit/gather/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/regexp.py $(LOCAL_PATH)/tools/grit/grit/gather/skeleton_gatherer.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/txt.py $(LOCAL_PATH)/tools/grit/grit/gather/txt_unittest.py $(LOCAL_PATH)/tools/grit/grit/grd_reader.py $(LOCAL_PATH)/tools/grit/grit/grd_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit/grit_runner.py $(LOCAL_PATH)/tools/grit/grit/grit_runner_unittest.py $(LOCAL_PATH)/tools/grit/grit/lazy_re.py $(LOCAL_PATH)/tools/grit/grit/lazy_re_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/base.py $(LOCAL_PATH)/tools/grit/grit/node/base_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/custom/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/empty.py $(LOCAL_PATH)/tools/grit/grit/node/include.py $(LOCAL_PATH)/tools/grit/grit/node/include_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/io.py $(LOCAL_PATH)/tools/grit/grit/node/io_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/mapping.py $(LOCAL_PATH)/tools/grit/grit/node/message.py $(LOCAL_PATH)/tools/grit/grit/node/message_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/misc.py $(LOCAL_PATH)/tools/grit/grit/node/misc_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/structure.py $(LOCAL_PATH)/tools/grit/grit/node/structure_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/variant.py $(LOCAL_PATH)/tools/grit/grit/pseudo.py $(LOCAL_PATH)/tools/grit/grit/pseudo_rtl.py $(LOCAL_PATH)/tools/grit/grit/pseudo_unittest.py $(LOCAL_PATH)/tools/grit/grit/scons.py $(LOCAL_PATH)/tools/grit/grit/shortcuts.py $(LOCAL_PATH)/tools/grit/grit/shortcuts_unittests.py $(LOCAL_PATH)/tools/grit/grit/tclib.py $(LOCAL_PATH)/tools/grit/grit/tclib_unittest.py $(LOCAL_PATH)/tools/grit/grit/test_suite_all.py $(LOCAL_PATH)/tools/grit/grit/tool/__init__.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/build.py $(LOCAL_PATH)/tools/grit/grit/tool/build_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/count.py $(LOCAL_PATH)/tools/grit/grit/tool/diff_structures.py $(LOCAL_PATH)/tools/grit/grit/tool/interface.py $(LOCAL_PATH)/tools/grit/grit/tool/menu_from_parts.py $(LOCAL_PATH)/tools/grit/grit/tool/newgrd.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/resize.py $(LOCAL_PATH)/tools/grit/grit/tool/test.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_postprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_preprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/unit.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb_unittest.py $(LOCAL_PATH)/tools/grit/grit/util.py $(LOCAL_PATH)/tools/grit/grit/util_unittest.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit_info.py $(GYP_TARGET_DEPENDENCIES)
 	@echo "Gyp action: Generating resources from content_resources.grd ($@)"
 	$(hide)cd $(gyp_local_path)/content; mkdir -p $(gyp_shared_intermediate_dir)/content/grit $(gyp_shared_intermediate_dir)/content; python ../tools/grit/grit.py -i content_resources.grd build -f ../tools/gritsettings/resource_ids -o "$(gyp_shared_intermediate_dir)/content" -D _chromium -E "CHROMIUM_BUILD=chromium" -t android -E "ANDROID_JAVA_TAGGED_ONLY=true" -D use_concatenated_impulse_responses
 
diff --git a/content/content_resources.target.linux-mips.mk b/content/content_resources.target.linux-mips.mk
index 87db013..bd6d492 100644
--- a/content/content_resources.target.linux-mips.mk
+++ b/content/content_resources.target.linux-mips.mk
@@ -18,7 +18,7 @@
 $(gyp_shared_intermediate_dir)/content/grit/content_resources.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/grit/content_resources.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/grit/content_resources.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/content/grit/content_resources.h: $(LOCAL_PATH)/tools/gritsettings/resource_ids $(LOCAL_PATH)/content/content_resources.grd $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.css $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.html $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.js $(LOCAL_PATH)/content/browser/resources/gpu/browser_bridge.js $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.html $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.js $(LOCAL_PATH)/content/browser/resources/gpu/info_view.css $(LOCAL_PATH)/content/browser/resources/gpu/info_view.html $(LOCAL_PATH)/content/browser/resources/gpu/info_view.js $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.css $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.html $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.js $(LOCAL_PATH)/content/browser/resources/media/cache_entry.js $(LOCAL_PATH)/content/browser/resources/media/data_series.js $(LOCAL_PATH)/content/browser/resources/media/disjoint_range_set.js $(LOCAL_PATH)/content/browser/resources/media/dump_creator.js $(LOCAL_PATH)/content/browser/resources/media/event_list.js $(LOCAL_PATH)/content/browser/resources/media/item_store.js $(LOCAL_PATH)/content/browser/resources/media/media_internals.css $(LOCAL_PATH)/content/browser/resources/media/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/media_player.js $(LOCAL_PATH)/content/browser/resources/media/metrics.js $(LOCAL_PATH)/content/browser/resources/media/new/client_renderer.js $(LOCAL_PATH)/content/browser/resources/media/new/main.js $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/new/player_info.js $(LOCAL_PATH)/content/browser/resources/media/new/player_manager.js $(LOCAL_PATH)/content/browser/resources/media/new/util.js $(LOCAL_PATH)/content/browser/resources/media/peer_connection_update_table.js $(LOCAL_PATH)/content/browser/resources/media/ssrc_info_manager.js $(LOCAL_PATH)/content/browser/resources/media/stats_graph_helper.js $(LOCAL_PATH)/content/browser/resources/media/stats_table.js $(LOCAL_PATH)/content/browser/resources/media/timeline_graph_view.js $(LOCAL_PATH)/content/browser/resources/media/util.js $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.css $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.html $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.js $(LOCAL_PATH)/tools/grit/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit.py $(LOCAL_PATH)/tools/grit/grit/__init__.py $(LOCAL_PATH)/tools/grit/grit/clique.py $(LOCAL_PATH)/tools/grit/grit/clique_unittest.py $(LOCAL_PATH)/tools/grit/grit/constants.py $(LOCAL_PATH)/tools/grit/grit/exception.py $(LOCAL_PATH)/tools/grit/grit/extern/BogoFP.py $(LOCAL_PATH)/tools/grit/grit/extern/FP.py $(LOCAL_PATH)/tools/grit/grit/extern/__init__.py $(LOCAL_PATH)/tools/grit/grit/extern/tclib.py $(LOCAL_PATH)/tools/grit/grit/format/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/c_format.py $(LOCAL_PATH)/tools/grit/grit/format/c_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/template_formatter.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writer_configuration.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/mock_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_helper.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/writer_unittest_common.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_formatted_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_writer_base_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/repack.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/__init__.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/interface.py $(LOCAL_PATH)/tools/grit/grit/gather/json_loader.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/rc.py $(LOCAL_PATH)/tools/grit/grit/gather/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/regexp.py $(LOCAL_PATH)/tools/grit/grit/gather/skeleton_gatherer.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/txt.py $(LOCAL_PATH)/tools/grit/grit/gather/txt_unittest.py $(LOCAL_PATH)/tools/grit/grit/grd_reader.py $(LOCAL_PATH)/tools/grit/grit/grd_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit/grit_runner.py $(LOCAL_PATH)/tools/grit/grit/grit_runner_unittest.py $(LOCAL_PATH)/tools/grit/grit/lazy_re.py $(LOCAL_PATH)/tools/grit/grit/lazy_re_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/base.py $(LOCAL_PATH)/tools/grit/grit/node/base_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/custom/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/empty.py $(LOCAL_PATH)/tools/grit/grit/node/include.py $(LOCAL_PATH)/tools/grit/grit/node/include_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/io.py $(LOCAL_PATH)/tools/grit/grit/node/io_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/mapping.py $(LOCAL_PATH)/tools/grit/grit/node/message.py $(LOCAL_PATH)/tools/grit/grit/node/message_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/misc.py $(LOCAL_PATH)/tools/grit/grit/node/misc_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/structure.py $(LOCAL_PATH)/tools/grit/grit/node/structure_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/variant.py $(LOCAL_PATH)/tools/grit/grit/pseudo.py $(LOCAL_PATH)/tools/grit/grit/pseudo_rtl.py $(LOCAL_PATH)/tools/grit/grit/pseudo_unittest.py $(LOCAL_PATH)/tools/grit/grit/scons.py $(LOCAL_PATH)/tools/grit/grit/shortcuts.py $(LOCAL_PATH)/tools/grit/grit/shortcuts_unittests.py $(LOCAL_PATH)/tools/grit/grit/tclib.py $(LOCAL_PATH)/tools/grit/grit/tclib_unittest.py $(LOCAL_PATH)/tools/grit/grit/test_suite_all.py $(LOCAL_PATH)/tools/grit/grit/tool/__init__.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/build.py $(LOCAL_PATH)/tools/grit/grit/tool/build_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/count.py $(LOCAL_PATH)/tools/grit/grit/tool/diff_structures.py $(LOCAL_PATH)/tools/grit/grit/tool/interface.py $(LOCAL_PATH)/tools/grit/grit/tool/menu_from_parts.py $(LOCAL_PATH)/tools/grit/grit/tool/newgrd.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/resize.py $(LOCAL_PATH)/tools/grit/grit/tool/test.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_postprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_preprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/unit.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb_unittest.py $(LOCAL_PATH)/tools/grit/grit/util.py $(LOCAL_PATH)/tools/grit/grit/util_unittest.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit_info.py $(GYP_TARGET_DEPENDENCIES)
+$(gyp_shared_intermediate_dir)/content/grit/content_resources.h: $(LOCAL_PATH)/tools/gritsettings/resource_ids $(LOCAL_PATH)/content/content_resources.grd $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.css $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.html $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.js $(LOCAL_PATH)/content/browser/resources/gpu/browser_bridge.js $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.html $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.js $(LOCAL_PATH)/content/browser/resources/gpu/info_view.css $(LOCAL_PATH)/content/browser/resources/gpu/info_view.html $(LOCAL_PATH)/content/browser/resources/gpu/info_view.js $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.css $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.html $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.js $(LOCAL_PATH)/content/browser/resources/media/cache_entry.js $(LOCAL_PATH)/content/browser/resources/media/data_series.js $(LOCAL_PATH)/content/browser/resources/media/disjoint_range_set.js $(LOCAL_PATH)/content/browser/resources/media/dump_creator.js $(LOCAL_PATH)/content/browser/resources/media/event_list.js $(LOCAL_PATH)/content/browser/resources/media/item_store.js $(LOCAL_PATH)/content/browser/resources/media/media_internals.css $(LOCAL_PATH)/content/browser/resources/media/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/media_player.js $(LOCAL_PATH)/content/browser/resources/media/metrics.js $(LOCAL_PATH)/content/browser/resources/media/new/client_renderer.js $(LOCAL_PATH)/content/browser/resources/media/new/main.js $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.css $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/new/player_info.js $(LOCAL_PATH)/content/browser/resources/media/new/player_manager.js $(LOCAL_PATH)/content/browser/resources/media/new/util.js $(LOCAL_PATH)/content/browser/resources/media/peer_connection_update_table.js $(LOCAL_PATH)/content/browser/resources/media/ssrc_info_manager.js $(LOCAL_PATH)/content/browser/resources/media/stats_graph_helper.js $(LOCAL_PATH)/content/browser/resources/media/stats_table.js $(LOCAL_PATH)/content/browser/resources/media/timeline_graph_view.js $(LOCAL_PATH)/content/browser/resources/media/util.js $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.css $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.html $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.js $(LOCAL_PATH)/tools/grit/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit.py $(LOCAL_PATH)/tools/grit/grit/__init__.py $(LOCAL_PATH)/tools/grit/grit/clique.py $(LOCAL_PATH)/tools/grit/grit/clique_unittest.py $(LOCAL_PATH)/tools/grit/grit/constants.py $(LOCAL_PATH)/tools/grit/grit/exception.py $(LOCAL_PATH)/tools/grit/grit/extern/BogoFP.py $(LOCAL_PATH)/tools/grit/grit/extern/FP.py $(LOCAL_PATH)/tools/grit/grit/extern/__init__.py $(LOCAL_PATH)/tools/grit/grit/extern/tclib.py $(LOCAL_PATH)/tools/grit/grit/format/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/c_format.py $(LOCAL_PATH)/tools/grit/grit/format/c_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/template_formatter.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writer_configuration.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/mock_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_helper.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/writer_unittest_common.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_formatted_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_writer_base_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/repack.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/__init__.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/interface.py $(LOCAL_PATH)/tools/grit/grit/gather/json_loader.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/rc.py $(LOCAL_PATH)/tools/grit/grit/gather/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/regexp.py $(LOCAL_PATH)/tools/grit/grit/gather/skeleton_gatherer.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/txt.py $(LOCAL_PATH)/tools/grit/grit/gather/txt_unittest.py $(LOCAL_PATH)/tools/grit/grit/grd_reader.py $(LOCAL_PATH)/tools/grit/grit/grd_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit/grit_runner.py $(LOCAL_PATH)/tools/grit/grit/grit_runner_unittest.py $(LOCAL_PATH)/tools/grit/grit/lazy_re.py $(LOCAL_PATH)/tools/grit/grit/lazy_re_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/base.py $(LOCAL_PATH)/tools/grit/grit/node/base_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/custom/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/empty.py $(LOCAL_PATH)/tools/grit/grit/node/include.py $(LOCAL_PATH)/tools/grit/grit/node/include_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/io.py $(LOCAL_PATH)/tools/grit/grit/node/io_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/mapping.py $(LOCAL_PATH)/tools/grit/grit/node/message.py $(LOCAL_PATH)/tools/grit/grit/node/message_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/misc.py $(LOCAL_PATH)/tools/grit/grit/node/misc_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/structure.py $(LOCAL_PATH)/tools/grit/grit/node/structure_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/variant.py $(LOCAL_PATH)/tools/grit/grit/pseudo.py $(LOCAL_PATH)/tools/grit/grit/pseudo_rtl.py $(LOCAL_PATH)/tools/grit/grit/pseudo_unittest.py $(LOCAL_PATH)/tools/grit/grit/scons.py $(LOCAL_PATH)/tools/grit/grit/shortcuts.py $(LOCAL_PATH)/tools/grit/grit/shortcuts_unittests.py $(LOCAL_PATH)/tools/grit/grit/tclib.py $(LOCAL_PATH)/tools/grit/grit/tclib_unittest.py $(LOCAL_PATH)/tools/grit/grit/test_suite_all.py $(LOCAL_PATH)/tools/grit/grit/tool/__init__.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/build.py $(LOCAL_PATH)/tools/grit/grit/tool/build_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/count.py $(LOCAL_PATH)/tools/grit/grit/tool/diff_structures.py $(LOCAL_PATH)/tools/grit/grit/tool/interface.py $(LOCAL_PATH)/tools/grit/grit/tool/menu_from_parts.py $(LOCAL_PATH)/tools/grit/grit/tool/newgrd.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/resize.py $(LOCAL_PATH)/tools/grit/grit/tool/test.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_postprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_preprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/unit.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb_unittest.py $(LOCAL_PATH)/tools/grit/grit/util.py $(LOCAL_PATH)/tools/grit/grit/util_unittest.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit_info.py $(GYP_TARGET_DEPENDENCIES)
 	@echo "Gyp action: Generating resources from content_resources.grd ($@)"
 	$(hide)cd $(gyp_local_path)/content; mkdir -p $(gyp_shared_intermediate_dir)/content/grit $(gyp_shared_intermediate_dir)/content; python ../tools/grit/grit.py -i content_resources.grd build -f ../tools/gritsettings/resource_ids -o "$(gyp_shared_intermediate_dir)/content" -D _chromium -E "CHROMIUM_BUILD=chromium" -t android -E "ANDROID_JAVA_TAGGED_ONLY=true" -D use_concatenated_impulse_responses
 
diff --git a/content/content_resources.target.linux-x86.mk b/content/content_resources.target.linux-x86.mk
index 87db013..bd6d492 100644
--- a/content/content_resources.target.linux-x86.mk
+++ b/content/content_resources.target.linux-x86.mk
@@ -18,7 +18,7 @@
 $(gyp_shared_intermediate_dir)/content/grit/content_resources.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/grit/content_resources.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
 $(gyp_shared_intermediate_dir)/content/grit/content_resources.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/content/grit/content_resources.h: $(LOCAL_PATH)/tools/gritsettings/resource_ids $(LOCAL_PATH)/content/content_resources.grd $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.css $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.html $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.js $(LOCAL_PATH)/content/browser/resources/gpu/browser_bridge.js $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.html $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.js $(LOCAL_PATH)/content/browser/resources/gpu/info_view.css $(LOCAL_PATH)/content/browser/resources/gpu/info_view.html $(LOCAL_PATH)/content/browser/resources/gpu/info_view.js $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.css $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.html $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.js $(LOCAL_PATH)/content/browser/resources/media/cache_entry.js $(LOCAL_PATH)/content/browser/resources/media/data_series.js $(LOCAL_PATH)/content/browser/resources/media/disjoint_range_set.js $(LOCAL_PATH)/content/browser/resources/media/dump_creator.js $(LOCAL_PATH)/content/browser/resources/media/event_list.js $(LOCAL_PATH)/content/browser/resources/media/item_store.js $(LOCAL_PATH)/content/browser/resources/media/media_internals.css $(LOCAL_PATH)/content/browser/resources/media/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/media_player.js $(LOCAL_PATH)/content/browser/resources/media/metrics.js $(LOCAL_PATH)/content/browser/resources/media/new/client_renderer.js $(LOCAL_PATH)/content/browser/resources/media/new/main.js $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/new/player_info.js $(LOCAL_PATH)/content/browser/resources/media/new/player_manager.js $(LOCAL_PATH)/content/browser/resources/media/new/util.js $(LOCAL_PATH)/content/browser/resources/media/peer_connection_update_table.js $(LOCAL_PATH)/content/browser/resources/media/ssrc_info_manager.js $(LOCAL_PATH)/content/browser/resources/media/stats_graph_helper.js $(LOCAL_PATH)/content/browser/resources/media/stats_table.js $(LOCAL_PATH)/content/browser/resources/media/timeline_graph_view.js $(LOCAL_PATH)/content/browser/resources/media/util.js $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.css $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.html $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.js $(LOCAL_PATH)/tools/grit/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit.py $(LOCAL_PATH)/tools/grit/grit/__init__.py $(LOCAL_PATH)/tools/grit/grit/clique.py $(LOCAL_PATH)/tools/grit/grit/clique_unittest.py $(LOCAL_PATH)/tools/grit/grit/constants.py $(LOCAL_PATH)/tools/grit/grit/exception.py $(LOCAL_PATH)/tools/grit/grit/extern/BogoFP.py $(LOCAL_PATH)/tools/grit/grit/extern/FP.py $(LOCAL_PATH)/tools/grit/grit/extern/__init__.py $(LOCAL_PATH)/tools/grit/grit/extern/tclib.py $(LOCAL_PATH)/tools/grit/grit/format/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/c_format.py $(LOCAL_PATH)/tools/grit/grit/format/c_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/template_formatter.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writer_configuration.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/mock_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_helper.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/writer_unittest_common.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_formatted_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_writer_base_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/repack.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/__init__.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/interface.py $(LOCAL_PATH)/tools/grit/grit/gather/json_loader.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/rc.py $(LOCAL_PATH)/tools/grit/grit/gather/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/regexp.py $(LOCAL_PATH)/tools/grit/grit/gather/skeleton_gatherer.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/txt.py $(LOCAL_PATH)/tools/grit/grit/gather/txt_unittest.py $(LOCAL_PATH)/tools/grit/grit/grd_reader.py $(LOCAL_PATH)/tools/grit/grit/grd_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit/grit_runner.py $(LOCAL_PATH)/tools/grit/grit/grit_runner_unittest.py $(LOCAL_PATH)/tools/grit/grit/lazy_re.py $(LOCAL_PATH)/tools/grit/grit/lazy_re_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/base.py $(LOCAL_PATH)/tools/grit/grit/node/base_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/custom/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/empty.py $(LOCAL_PATH)/tools/grit/grit/node/include.py $(LOCAL_PATH)/tools/grit/grit/node/include_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/io.py $(LOCAL_PATH)/tools/grit/grit/node/io_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/mapping.py $(LOCAL_PATH)/tools/grit/grit/node/message.py $(LOCAL_PATH)/tools/grit/grit/node/message_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/misc.py $(LOCAL_PATH)/tools/grit/grit/node/misc_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/structure.py $(LOCAL_PATH)/tools/grit/grit/node/structure_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/variant.py $(LOCAL_PATH)/tools/grit/grit/pseudo.py $(LOCAL_PATH)/tools/grit/grit/pseudo_rtl.py $(LOCAL_PATH)/tools/grit/grit/pseudo_unittest.py $(LOCAL_PATH)/tools/grit/grit/scons.py $(LOCAL_PATH)/tools/grit/grit/shortcuts.py $(LOCAL_PATH)/tools/grit/grit/shortcuts_unittests.py $(LOCAL_PATH)/tools/grit/grit/tclib.py $(LOCAL_PATH)/tools/grit/grit/tclib_unittest.py $(LOCAL_PATH)/tools/grit/grit/test_suite_all.py $(LOCAL_PATH)/tools/grit/grit/tool/__init__.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/build.py $(LOCAL_PATH)/tools/grit/grit/tool/build_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/count.py $(LOCAL_PATH)/tools/grit/grit/tool/diff_structures.py $(LOCAL_PATH)/tools/grit/grit/tool/interface.py $(LOCAL_PATH)/tools/grit/grit/tool/menu_from_parts.py $(LOCAL_PATH)/tools/grit/grit/tool/newgrd.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/resize.py $(LOCAL_PATH)/tools/grit/grit/tool/test.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_postprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_preprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/unit.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb_unittest.py $(LOCAL_PATH)/tools/grit/grit/util.py $(LOCAL_PATH)/tools/grit/grit/util_unittest.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit_info.py $(GYP_TARGET_DEPENDENCIES)
+$(gyp_shared_intermediate_dir)/content/grit/content_resources.h: $(LOCAL_PATH)/tools/gritsettings/resource_ids $(LOCAL_PATH)/content/content_resources.grd $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.css $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.html $(LOCAL_PATH)/content/browser/resources/accessibility/accessibility.js $(LOCAL_PATH)/content/browser/resources/gpu/browser_bridge.js $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.html $(LOCAL_PATH)/content/browser/resources/gpu/gpu_internals.js $(LOCAL_PATH)/content/browser/resources/gpu/info_view.css $(LOCAL_PATH)/content/browser/resources/gpu/info_view.html $(LOCAL_PATH)/content/browser/resources/gpu/info_view.js $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.css $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.html $(LOCAL_PATH)/content/browser/resources/indexed_db/indexeddb_internals.js $(LOCAL_PATH)/content/browser/resources/media/cache_entry.js $(LOCAL_PATH)/content/browser/resources/media/data_series.js $(LOCAL_PATH)/content/browser/resources/media/disjoint_range_set.js $(LOCAL_PATH)/content/browser/resources/media/dump_creator.js $(LOCAL_PATH)/content/browser/resources/media/event_list.js $(LOCAL_PATH)/content/browser/resources/media/item_store.js $(LOCAL_PATH)/content/browser/resources/media/media_internals.css $(LOCAL_PATH)/content/browser/resources/media/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/media_player.js $(LOCAL_PATH)/content/browser/resources/media/metrics.js $(LOCAL_PATH)/content/browser/resources/media/new/client_renderer.js $(LOCAL_PATH)/content/browser/resources/media/new/main.js $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.css $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.html $(LOCAL_PATH)/content/browser/resources/media/new/media_internals.js $(LOCAL_PATH)/content/browser/resources/media/new/player_info.js $(LOCAL_PATH)/content/browser/resources/media/new/player_manager.js $(LOCAL_PATH)/content/browser/resources/media/new/util.js $(LOCAL_PATH)/content/browser/resources/media/peer_connection_update_table.js $(LOCAL_PATH)/content/browser/resources/media/ssrc_info_manager.js $(LOCAL_PATH)/content/browser/resources/media/stats_graph_helper.js $(LOCAL_PATH)/content/browser/resources/media/stats_table.js $(LOCAL_PATH)/content/browser/resources/media/timeline_graph_view.js $(LOCAL_PATH)/content/browser/resources/media/util.js $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.css $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.html $(LOCAL_PATH)/content/browser/resources/media/webrtc_internals.js $(LOCAL_PATH)/tools/grit/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit.py $(LOCAL_PATH)/tools/grit/grit/__init__.py $(LOCAL_PATH)/tools/grit/grit/clique.py $(LOCAL_PATH)/tools/grit/grit/clique_unittest.py $(LOCAL_PATH)/tools/grit/grit/constants.py $(LOCAL_PATH)/tools/grit/grit/exception.py $(LOCAL_PATH)/tools/grit/grit/extern/BogoFP.py $(LOCAL_PATH)/tools/grit/grit/extern/FP.py $(LOCAL_PATH)/tools/grit/grit/extern/__init__.py $(LOCAL_PATH)/tools/grit/grit/extern/tclib.py $(LOCAL_PATH)/tools/grit/grit/format/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml.py $(LOCAL_PATH)/tools/grit/grit/format/android_xml_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/c_format.py $(LOCAL_PATH)/tools/grit/grit/format/c_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json.py $(LOCAL_PATH)/tools/grit/grit/format/chrome_messages_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack.py $(LOCAL_PATH)/tools/grit/grit/format/data_pack_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline.py $(LOCAL_PATH)/tools/grit/grit/format/html_inline_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format.py $(LOCAL_PATH)/tools/grit/grit/format/js_map_format_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/PRESUBMIT.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/policy_template_generator_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/template_formatter.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writer_configuration.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/__init__.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adm_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/adml_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/admx_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/doc_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/json_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/mock_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_helper.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_strings_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/plist_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/reg_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/template_writer_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/writer_unittest_common.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_formatted_writer.py $(LOCAL_PATH)/tools/grit/grit/format/policy_templates/writers/xml_writer_base_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header.py $(LOCAL_PATH)/tools/grit/grit/format/rc_header_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/format/repack.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map.py $(LOCAL_PATH)/tools/grit/grit/format/resource_map_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/__init__.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template.py $(LOCAL_PATH)/tools/grit/grit/gather/admin_template_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image.py $(LOCAL_PATH)/tools/grit/grit/gather/chrome_scaled_image_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/igoogle_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/interface.py $(LOCAL_PATH)/tools/grit/grit/gather/json_loader.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings.py $(LOCAL_PATH)/tools/grit/grit/gather/muppet_strings_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json.py $(LOCAL_PATH)/tools/grit/grit/gather/policy_json_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/rc.py $(LOCAL_PATH)/tools/grit/grit/gather/rc_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/regexp.py $(LOCAL_PATH)/tools/grit/grit/gather/skeleton_gatherer.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html.py $(LOCAL_PATH)/tools/grit/grit/gather/tr_html_unittest.py $(LOCAL_PATH)/tools/grit/grit/gather/txt.py $(LOCAL_PATH)/tools/grit/grit/gather/txt_unittest.py $(LOCAL_PATH)/tools/grit/grit/grd_reader.py $(LOCAL_PATH)/tools/grit/grit/grd_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit/grit_runner.py $(LOCAL_PATH)/tools/grit/grit/grit_runner_unittest.py $(LOCAL_PATH)/tools/grit/grit/lazy_re.py $(LOCAL_PATH)/tools/grit/grit/lazy_re_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/base.py $(LOCAL_PATH)/tools/grit/grit/node/base_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/custom/__init__.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename.py $(LOCAL_PATH)/tools/grit/grit/node/custom/filename_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/empty.py $(LOCAL_PATH)/tools/grit/grit/node/include.py $(LOCAL_PATH)/tools/grit/grit/node/include_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/io.py $(LOCAL_PATH)/tools/grit/grit/node/io_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/mapping.py $(LOCAL_PATH)/tools/grit/grit/node/message.py $(LOCAL_PATH)/tools/grit/grit/node/message_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/misc.py $(LOCAL_PATH)/tools/grit/grit/node/misc_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/structure.py $(LOCAL_PATH)/tools/grit/grit/node/structure_unittest.py $(LOCAL_PATH)/tools/grit/grit/node/variant.py $(LOCAL_PATH)/tools/grit/grit/pseudo.py $(LOCAL_PATH)/tools/grit/grit/pseudo_rtl.py $(LOCAL_PATH)/tools/grit/grit/pseudo_unittest.py $(LOCAL_PATH)/tools/grit/grit/scons.py $(LOCAL_PATH)/tools/grit/grit/shortcuts.py $(LOCAL_PATH)/tools/grit/grit/shortcuts_unittests.py $(LOCAL_PATH)/tools/grit/grit/tclib.py $(LOCAL_PATH)/tools/grit/grit/tclib_unittest.py $(LOCAL_PATH)/tools/grit/grit/test_suite_all.py $(LOCAL_PATH)/tools/grit/grit/tool/__init__.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/android2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/build.py $(LOCAL_PATH)/tools/grit/grit/tool/build_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo.py $(LOCAL_PATH)/tools/grit/grit/tool/buildinfo_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/count.py $(LOCAL_PATH)/tools/grit/grit/tool/diff_structures.py $(LOCAL_PATH)/tools/grit/grit/tool/interface.py $(LOCAL_PATH)/tools/grit/grit/tool/menu_from_parts.py $(LOCAL_PATH)/tools/grit/grit/tool/newgrd.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/postprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_interface.py $(LOCAL_PATH)/tools/grit/grit/tool/preprocess_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd.py $(LOCAL_PATH)/tools/grit/grit/tool/rc2grd_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/resize.py $(LOCAL_PATH)/tools/grit/grit/tool/test.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_postprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/toolbar_preprocess.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc.py $(LOCAL_PATH)/tools/grit/grit/tool/transl2tc_unittest.py $(LOCAL_PATH)/tools/grit/grit/tool/unit.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb.py $(LOCAL_PATH)/tools/grit/grit/tool/xmb_unittest.py $(LOCAL_PATH)/tools/grit/grit/util.py $(LOCAL_PATH)/tools/grit/grit/util_unittest.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader.py $(LOCAL_PATH)/tools/grit/grit/xtb_reader_unittest.py $(LOCAL_PATH)/tools/grit/grit_info.py $(GYP_TARGET_DEPENDENCIES)
 	@echo "Gyp action: Generating resources from content_resources.grd ($@)"
 	$(hide)cd $(gyp_local_path)/content; mkdir -p $(gyp_shared_intermediate_dir)/content/grit $(gyp_shared_intermediate_dir)/content; python ../tools/grit/grit.py -i content_resources.grd build -f ../tools/gritsettings/resource_ids -o "$(gyp_shared_intermediate_dir)/content" -D _chromium -E "CHROMIUM_BUILD=chromium" -t android -E "ANDROID_JAVA_TAGGED_ONLY=true" -D use_concatenated_impulse_responses
 
diff --git a/content/content_shell.gypi b/content/content_shell.gypi
index e6e5b6a..85a88c4 100644
--- a/content/content_shell.gypi
+++ b/content/content_shell.gypi
@@ -124,6 +124,8 @@
         'shell/browser/shell_quota_permission_context.h',
         'shell/browser/shell_resource_dispatcher_host_delegate.cc',
         'shell/browser/shell_resource_dispatcher_host_delegate.h',
+        'shell/browser/shell_speech_recognition_manager_delegate.cc',
+        'shell/browser/shell_speech_recognition_manager_delegate.h',
         'shell/browser/shell_url_request_context_getter.cc',
         'shell/browser/shell_url_request_context_getter.h',
         'shell/browser/shell_web_contents_view_delegate_android.cc',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 783106d..1074d32 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -9,7 +9,6 @@
       'type': 'static_library',
       'defines!': ['CONTENT_IMPLEMENTATION'],
       'dependencies': [
-        '../media/media.gyp:media',
         '../net/net.gyp:net_test_support',
         '../skia/skia.gyp:skia',
         '../testing/gmock.gyp:gmock',
@@ -174,6 +173,7 @@
             'content_renderer',
             'content_utility',
             'content_worker',
+            '../media/media.gyp:media',
             '../ppapi/ppapi_internal.gyp:ppapi_host',
             '../ppapi/ppapi_internal.gyp:ppapi_proxy',
             '../ppapi/ppapi_internal.gyp:ppapi_shared',
@@ -259,7 +259,6 @@
         'browser/speech/proto/speech_proto.gyp:speech_proto',
         '../base/base.gyp:test_support_base',
         '../crypto/crypto.gyp:crypto',
-        '../media/media.gyp:media_test_support',
         '../net/net.gyp:net_test_support',
         '../skia/skia.gyp:skia',
         '../sql/sql.gyp:sql',
@@ -302,7 +301,6 @@
         'browser/gamepad/gamepad_provider_unittest.cc',
         'browser/gamepad/gamepad_test_helpers.cc',
         'browser/gamepad/gamepad_test_helpers.h',
-        'browser/geolocation/device_data_provider_unittest.cc',
         'browser/geolocation/geolocation_provider_unittest.cc',
         'browser/geolocation/gps_location_provider_unittest_linux.cc',
         'browser/geolocation/location_arbitrator_impl_unittest.cc',
@@ -338,7 +336,7 @@
         'browser/power_monitor_message_broadcaster_unittest.cc',
         'browser/renderer_host/compositing_iosurface_transformer_mac_unittest.cc',
         'browser/renderer_host/gtk_key_bindings_handler_unittest.cc',
-	'browser/renderer_host/input/immediate_input_router_unittest.cc',
+        'browser/renderer_host/input/immediate_input_router_unittest.cc',
         'browser/renderer_host/media/audio_input_device_manager_unittest.cc',
         'browser/renderer_host/media/audio_mirroring_manager_unittest.cc',
         'browser/renderer_host/media/audio_renderer_host_unittest.cc',
@@ -363,7 +361,7 @@
         'browser/renderer_host/render_widget_host_view_guest_unittest.cc',
         'browser/renderer_host/render_widget_host_view_mac_editcommand_helper_unittest.mm',
         'browser/renderer_host/render_widget_host_view_mac_unittest.mm',
-        'browser/renderer_host/smooth_scroll_gesture_controller_unittest.cc',
+        'browser/renderer_host/synthetic_gesture_controller_unittest.cc',
         'browser/renderer_host/text_input_client_mac_unittest.mm',
         'browser/renderer_host/web_input_event_aura_unittest.cc',
         'browser/resolve_proxy_msg_helper_unittest.cc',
@@ -399,6 +397,7 @@
         'child/npapi/plugin_lib_unittest.cc',
         'child/power_monitor_broadcast_source_unittest.cc',
         'child/resource_dispatcher_unittest.cc',
+        'child/site_isolation_policy_unittest.cc',
         'common/android/address_parser_unittest.cc',
         'common/cc_messages_unittest.cc',
         'common/common_param_traits_unittest.cc',
@@ -453,6 +452,7 @@
         'renderer/render_view_impl_unittest.cc',
         'renderer/skia_benchmarking_extension_unittest.cc',
         'renderer/v8_value_converter_impl_unittest.cc',
+        'renderer/webcrypto_impl_unittest.cc',
         'test/image_decoder_test.cc',
         'test/image_decoder_test.h',
         'test/run_all_unittests.cc',
@@ -576,6 +576,7 @@
             '../gpu/gpu.gyp:gpu_unittest_utils',
             '../ipc/ipc.gyp:test_support_ipc',
             '../jingle/jingle.gyp:jingle_glue_test_util',
+            '../media/media.gyp:media_test_support',
             '../media/media.gyp:shared_memory_support',
             '../third_party/WebKit/public/blink.gyp:blink',
             '../third_party/icu/icu.gyp:icui18n',
@@ -709,7 +710,6 @@
             '../ui/ui.gyp:shell_dialogs',
           ],
           'sources!': [
-            'browser/geolocation/device_data_provider_unittest.cc',
             'browser/geolocation/gps_location_provider_unittest_linux.cc',
             'browser/geolocation/network_location_provider_unittest.cc',
             'browser/geolocation/wifi_data_provider_chromeos_unittest.cc',
@@ -732,6 +732,13 @@
             'renderer/media/rtc_video_decoder_factory_tv_unittest.cc',
           ],
         }],
+        # TODO(bryaneyler): Also enable these for OpenSSL once that
+        # implementation is ready.  Issue 267888.
+        ['use_nss!=1', {
+          'sources!': [
+            'renderer/webcrypto_impl_unittest.cc',
+          ],
+        }],
       ],
     },
   ],
@@ -763,6 +770,7 @@
             '../skia/skia.gyp:skia',
             '../testing/gmock.gyp:gmock',
             '../testing/gtest.gyp:gtest',
+            '../third_party/leveldatabase/leveldatabase.gyp:leveldatabase',
             '../third_party/mesa/mesa.gyp:osmesa',
             '../third_party/widevine/cdm/widevine_cdm.gyp:widevine_cdm_version_h',
             '../ui/gl/gl.gyp:gl',
@@ -793,6 +801,8 @@
             'browser/browser_plugin/test_browser_plugin_embedder.h',
             'browser/browser_plugin/test_browser_plugin_guest.cc',
             'browser/browser_plugin/test_browser_plugin_guest.h',
+            'browser/browser_plugin/test_browser_plugin_guest_delegate.cc',
+            'browser/browser_plugin/test_browser_plugin_guest_delegate.h',
             'browser/browser_plugin/test_browser_plugin_guest_manager.cc',
             'browser/browser_plugin/test_browser_plugin_guest_manager.h',
             'browser/child_process_security_policy_browsertest.cc',
@@ -837,6 +847,7 @@
             'browser/web_contents/web_contents_view_aura_browsertest.cc',
             'browser/webkit_browsertest.cc',
             'browser/worker_host/test/worker_browsertest.cc',
+            'child/site_isolation_policy_browsertest.cc',
             'renderer/accessibility/renderer_accessibility_browsertest.cc',
             'renderer/browser_plugin/browser_plugin_browsertest.cc',
             'renderer/browser_plugin/browser_plugin_browsertest.h',
diff --git a/content/plugin/webplugin_proxy.cc b/content/plugin/webplugin_proxy.cc
index f411f30..0988d68 100644
--- a/content/plugin/webplugin_proxy.cc
+++ b/content/plugin/webplugin_proxy.cc
@@ -321,7 +321,7 @@
         WebPluginDelegateImpl::PLUGIN_QUIRK_BLOCK_NONSTANDARD_GETURL_REQUESTS) {
       GURL request_url(url);
       if (!request_url.SchemeIs(chrome::kHttpScheme) &&
-          !request_url.SchemeIs(chrome::kHttpsScheme) &&
+          !request_url.SchemeIs(kHttpsScheme) &&
           !request_url.SchemeIs(chrome::kFtpScheme)) {
         return;
       }
diff --git a/content/port/browser/render_widget_host_view_port.h b/content/port/browser/render_widget_host_view_port.h
index 59d4653..162a7fb 100644
--- a/content/port/browser/render_widget_host_view_port.h
+++ b/content/port/browser/render_widget_host_view_port.h
@@ -41,7 +41,7 @@
 namespace content {
 class BackingStore;
 class RenderWidgetHostViewFrameSubscriber;
-class SmoothScrollGesture;
+class SyntheticGesture;
 struct WebPluginGeometry;
 struct NativeWebKeyboardEvent;
 
@@ -263,9 +263,9 @@
   virtual void ProcessAckedTouchEvent(const TouchEventWithLatencyInfo& touch,
                                       InputEventAckState ack_result) = 0;
 
-  // Asks the view to create a smooth scroll gesture that will be used to
+  // Asks the view to create a synthetic gesture that will be used to
   // simulate a user-initiated scroll.
-  virtual SmoothScrollGesture* CreateSmoothScrollGesture(
+  virtual SyntheticGesture* CreateSmoothScrollGesture(
       bool scroll_down, int pixels_to_scroll, int mouse_event_x,
       int mouse_event_y) = 0;
 
diff --git a/content/port/browser/smooth_scroll_gesture.h b/content/port/browser/smooth_scroll_gesture.h
deleted file mode 100644
index d4682cc..0000000
--- a/content/port/browser/smooth_scroll_gesture.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_PORT_BROWSER_SMOOTH_SCROLL_GESTURE_H_
-#define CONTENT_PORT_BROWSER_SMOOTH_SCROLL_GESTURE_H_
-
-#include "base/memory/ref_counted.h"
-#include "base/time/time.h"
-
-namespace content {
-
-class RenderWidgetHost;
-
-// This is a base class representing a single scroll gesture. These gestures are
-// paired with the rendering benchmarking system to (automatically) measure how
-// smoothnly chrome is responding to user input.
-class SmoothScrollGesture : public base::RefCounted<SmoothScrollGesture> {
- public:
-
-  // When called, the gesture should compute its state at the provided timestamp
-  // and send the right input events to the provided RenderWidgetHost to
-  // simulate the gesture having run up to that point in time.
-  //
-  // This function should return true until the gesture is over. A false return
-  // value will stop ticking the gesture and clean it up.
-  virtual bool ForwardInputEvents(base::TimeTicks now,
-                                  RenderWidgetHost* host) = 0;
- protected:
-  friend class base::RefCounted<SmoothScrollGesture>;
-  virtual ~SmoothScrollGesture() {}
-};
-
-} // namespace content
-
-#endif // CONTENT_PORT_BROWSER_SMOOTH_SCROLL_GESTURE_H_
diff --git a/content/port/browser/synthetic_gesture.h b/content/port/browser/synthetic_gesture.h
new file mode 100644
index 0000000..4330d76
--- /dev/null
+++ b/content/port/browser/synthetic_gesture.h
@@ -0,0 +1,36 @@
+// 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 CONTENT_PORT_BROWSER_SYNTHETIC_GESTURE_H_
+#define CONTENT_PORT_BROWSER_SYNTHETIC_GESTURE_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+
+namespace content {
+
+class RenderWidgetHost;
+
+// This is a base class representing a single scroll gesture. These gestures are
+// paired with the rendering benchmarking system to (automatically) measure how
+// smoothnly chrome is responding to user input.
+class SyntheticGesture : public base::RefCounted<SyntheticGesture> {
+ public:
+
+  // When called, the gesture should compute its state at the provided timestamp
+  // and send the right input events to the provided RenderWidgetHost to
+  // simulate the gesture having run up to that point in time.
+  //
+  // This function should return true until the gesture is over. A false return
+  // value will stop ticking the gesture and clean it up.
+  virtual bool ForwardInputEvents(base::TimeTicks now,
+                                  RenderWidgetHost* host) = 0;
+ protected:
+  friend class base::RefCounted<SyntheticGesture>;
+  virtual ~SyntheticGesture() {}
+};
+
+} // namespace content
+
+#endif // CONTENT_PORT_BROWSER_SYNTHETIC_GESTURE_H_
diff --git a/content/ppapi_plugin/ppapi_thread.cc b/content/ppapi_plugin/ppapi_thread.cc
index 73d51ad..c2b4e04 100644
--- a/content/ppapi_plugin/ppapi_thread.cc
+++ b/content/ppapi_plugin/ppapi_thread.cc
@@ -101,10 +101,6 @@
 
   // Register interfaces that expect messages from the browser process. Please
   // note that only those InterfaceProxy-based ones require registration.
-  AddRoute(ppapi::API_ID_PPB_TCPSOCKET,
-           &dispatcher_message_listener_);
-  AddRoute(ppapi::API_ID_PPB_TCPSOCKET_PRIVATE,
-           &dispatcher_message_listener_);
   AddRoute(ppapi::API_ID_PPB_HOSTRESOLVER_PRIVATE,
            &dispatcher_message_listener_);
   AddRoute(ppapi::API_ID_PPB_NETWORKMANAGER_PRIVATE,
diff --git a/content/public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java b/content/public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java
index b1b72ba..00a55db 100644
--- a/content/public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java
+++ b/content/public/android/java/src/org/chromium/content/browser/AndroidBrowserProcess.java
@@ -4,106 +4,24 @@
 
 package org.chromium.content.browser;
 
-import android.app.ActivityManager;
 import android.content.Context;
-import android.util.Log;
 
 import org.chromium.base.JNINamespace;
-import org.chromium.content.app.ContentMain;
-import org.chromium.content.app.LibraryLoader;
 import org.chromium.content.common.ProcessInitException;
 
-@JNINamespace("content")
-// TODO(nyquist) Remove this class, and move the functionality to BrowserStartupController.
+@JNINamespace("content") // TODO(nyquist) Remove this class, and move the functionality to
+                         // BrowserStartupController.
 public class AndroidBrowserProcess {
-    private static final String TAG = "BrowserProcessMain";
 
-    // Prevents initializing the process more than once.
-    private static boolean sInitialized = false;
+    public static final int MAX_RENDERERS_LIMIT = BrowserStartupController.MAX_RENDERERS_LIMIT;
 
-    // Use single-process mode that runs the renderer on a separate thread in the main application.
-    public static final int MAX_RENDERERS_SINGLE_PROCESS = 0;
-
-    // Cap on the maximum number of renderer processes that can be requested.
-    // This is currently set to account for:
-    //  13: The maximum number of sandboxed processes we have available
-    // - 1: The regular New Tab Page
-    // - 1: The incognito New Tab Page
-    // - 1: A regular incognito tab
-    // - 1: Safety buffer (http://crbug.com/251279)
-    public static final int MAX_RENDERERS_LIMIT =
-        ChildProcessLauncher.MAX_REGISTERED_SANDBOXED_SERVICES - 4;
-
-    /**
-     * Initialize the process as a ContentView host. This must be called from the main UI thread.
-     * This should be called by the ContentView constructor to prepare this process for ContentView
-     * use outside of the browser. In the case where ContentView is used in the browser then
-     * initBrowserProcess() should already have been called and this is a no-op.
-     *
-     * @param context Context used to obtain the application context.
-     * @param maxRendererProcesses Limit on the number of renderers to use. Each tab runs in its own
-     * process until the maximum number of processes is reached. The special value of
-     * MAX_RENDERERS_SINGLE_PROCESS requests single-process mode where the renderer will run in the
-     * application process in a separate thread. The maximum number of allowed renderers is capped
-     * by MAX_RENDERERS_LIMIT.
-     *
-     * @return Whether the process actually needed to be initialized (false if already running).
-     */
-     public static boolean init(Context context, int maxRendererProcesses)
-             throws ProcessInitException {
-        assert maxRendererProcesses >= 0;
-        assert maxRendererProcesses <= MAX_RENDERERS_LIMIT;
-        if (sInitialized) return false;
-        sInitialized = true;
-        Log.i(TAG, "Initializing chromium process, renderers=" + maxRendererProcesses);
-
-        // Normally Main.java will have kicked this off asynchronously for Chrome. But other
-        // ContentView apps like tests also need them so we make sure we've extracted resources
-        // here. We can still make it a little async (wait until the library is loaded).
-        ResourceExtractor resourceExtractor = ResourceExtractor.get(context);
-        resourceExtractor.startExtractingResources();
-
-        // Normally Main.java will have already loaded the library asynchronously, we only need to
-        // load it here if we arrived via another flow, e.g. bookmark access & sync setup.
-        LibraryLoader.ensureInitialized();
-        // TODO(yfriedman): Remove dependency on a command line flag for this.
-        DeviceUtils.addDeviceSpecificUserAgentSwitch(context);
-
-        Context appContext = context.getApplicationContext();
-        // Now we really need to have the resources ready.
-        resourceExtractor.waitForCompletion();
-
-        nativeSetCommandLineFlags(maxRendererProcesses,
-                nativeIsPluginEnabled() ? getPlugins(context) : null);
-        ContentMain.initApplicationContext(appContext);
-        int result = ContentMain.start();
-        if (result > 0) throw new ProcessInitException(result);
-        return true;
+    // Temporarily provide the old init, to simplify landing patches
+    // TODO(aberent) Remove
+    @Deprecated
+    public static void init(Context context, int maxRendererProcesses) throws ProcessInitException {
+        if (!BrowserStartupController.get(context).startBrowserProcessesSync(
+                maxRendererProcesses)) {
+            throw new ProcessInitException(1);
+        }
     }
-
-    /**
-     * Initialization needed for tests. Mainly used by content browsertests.
-     */
-    public static void initChromiumBrowserProcessForTests(Context context) {
-        ResourceExtractor resourceExtractor = ResourceExtractor.get(context);
-        resourceExtractor.startExtractingResources();
-        resourceExtractor.waitForCompletion();
-
-        // Having a single renderer should be sufficient for tests. We can't have more than
-        // MAX_RENDERERS_LIMIT.
-        nativeSetCommandLineFlags(1 /* maxRenderers */, null);
-    }
-
-    private static String getPlugins(final Context context) {
-        return PepperPluginManager.getPlugins(context);
-    }
-
-    private static native void nativeSetCommandLineFlags(int maxRenderProcesses,
-            String pluginDescriptor);
-
-    // Is this an official build of Chrome? Only native code knows for sure. Official build
-    // knowledge is needed very early in process startup.
-    private static native boolean nativeIsOfficialBuild();
-
-    private static native boolean nativeIsPluginEnabled();
 }
diff --git a/content/public/android/java/src/org/chromium/content/browser/BrowserStartupController.java b/content/public/android/java/src/org/chromium/content/browser/BrowserStartupController.java
index e6b3a67..b72d60d 100644
--- a/content/public/android/java/src/org/chromium/content/browser/BrowserStartupController.java
+++ b/content/public/android/java/src/org/chromium/content/browser/BrowserStartupController.java
@@ -13,6 +13,8 @@
 import org.chromium.base.CalledByNative;
 import org.chromium.base.JNINamespace;
 import org.chromium.base.ThreadUtils;
+import org.chromium.content.app.ContentMain;
+import org.chromium.content.app.LibraryLoader;
 import org.chromium.content.common.ProcessInitException;
 
 import java.util.ArrayList;
@@ -55,12 +57,13 @@
 
     private static boolean sBrowserMayStartAsynchronously = false;
 
-    private static void setAsynchronousStartupConfig() {
-        sBrowserMayStartAsynchronously = true;
+    private static void setAsynchronousStartup(boolean enable) {
+        sBrowserMayStartAsynchronously = enable;
     }
 
+    @VisibleForTesting
     @CalledByNative
-    private static boolean browserMayStartAsynchonously() {
+    static boolean browserMayStartAsynchonously() {
         return sBrowserMayStartAsynchronously;
     }
 
@@ -79,13 +82,27 @@
     // The context is set on creation, but the reference is cleared after the browser process
     // initialization has been started, since it is not needed anymore. This is to ensure the
     // context is not leaked.
-    private Context mContext;
+    private final Context mContext;
 
     // Whether the async startup of the browser process has started.
     private boolean mHasStartedInitializingBrowserProcess;
 
     // Whether the async startup of the browser process is complete.
-    private boolean mAsyncStartupDone;
+    private boolean mStartupDone;
+
+    // Use single-process mode that runs the renderer on a separate thread in
+    // the main application.
+    public static final int MAX_RENDERERS_SINGLE_PROCESS = 0;
+
+    // Cap on the maximum number of renderer processes that can be requested.
+    // This is currently set to account for:
+    //  13: The maximum number of sandboxed processes we have available
+    // - 1: The regular New Tab Page
+    // - 1: The incognito New Tab Page
+    // - 1: A regular incognito tab
+    // - 1: Safety buffer (http://crbug.com/251279)
+    public static final int MAX_RENDERERS_LIMIT =
+            ChildProcessLauncher.MAX_REGISTERED_SANDBOXED_SERVICES - 4;
 
     // This field is set after startup has been completed based on whether the startup was a success
     // or not. It is used when later requests to startup come in that happen after the initial set
@@ -124,7 +141,7 @@
      */
     public void startBrowserProcessesAsync(final StartupCallback callback) {
         assert ThreadUtils.runningOnUiThread() : "Tried to start the browser on the wrong thread.";
-        if (mAsyncStartupDone) {
+        if (mStartupDone) {
             // Browser process initialization has already been completed, so we can immediately post
             // the callback.
             postStartupCompleted(callback);
@@ -139,62 +156,93 @@
             // flag that indicates that we have kicked off starting the browser process.
             mHasStartedInitializingBrowserProcess = true;
 
-            enableAsynchronousStartup();
+            if (!tryPrepareToStartBrowserProcess(MAX_RENDERERS_LIMIT)) return;
 
-            // Try to initialize the Android browser process.
-            tryToInitializeBrowserProcess();
+            setAsynchronousStartup(true);
+            if (contentStart() > 0) {
+                // Failed. The callbacks may not have run, so run them.
+                enqueueCallbackExecution(STARTUP_FAILURE, NOT_ALREADY_STARTED);
+            }
         }
     }
 
-    private void tryToInitializeBrowserProcess() {
+    private boolean tryPrepareToStartBrowserProcess(int maxRenderers) {
+        // Make sure that everything is in place to initialize the Android browser process.
         try {
-            assert mContext != null;
-            boolean wasAlreadyInitialized = initializeAndroidBrowserProcess();
-            // The context is not needed anymore, so clear the member field to not leak.
-            mContext = null;
-            if (wasAlreadyInitialized) {
-                // Something has already initialized the browser process before we got to setup the
-                // async startup. This means that we will never get a callback, so manually call
-                // them now, and just assume that the startup was successful.
-                Log.w(TAG, "Browser process was initialized without BrowserStartupController");
-                enqueueCallbackExecution(STARTUP_SUCCESS, ALREADY_STARTED);
-            }
+            prepareToStartBrowserProcess(maxRenderers);
+            return true;
         } catch (ProcessInitException e) {
-            Log.e(TAG, "Unable to start browser process.", e);
-            // ProcessInitException could mean one of two things:
-            // 1) The LibraryLoader failed.
-            // 2) ContentMain failed to start.
-            // It is unclear whether the browser tasks have already been started, and in case they
-            // have not, post a message to execute all the callbacks. Whichever call to
-            // executeEnqueuedCallbacks comes first will trigger the callbacks, but since the list
-            // of callbacks is then cleared, they will only be called once.
+            Log.e(TAG, "Unable to load native library.", e);
+            enqueueCallbackExecution(STARTUP_FAILURE, NOT_ALREADY_STARTED);
+            return false;
+        }
+    }
+
+    /**
+     * Start the browser process synchronously. If the browser is already being started
+     * asynchronously then complete startup synchronously
+     *
+     * <p/>
+     * Note that this can only be called on the UI thread.
+     *
+     * @param max_renderers The maximum number of renderer processes the browser may
+     *                      create. Zero for single process mode.
+     * @return true if successfully started, false otherwise.
+     */
+    public boolean startBrowserProcessesSync(int maxRenderers) {
+        if (mStartupDone) {
+            // Nothing to do
+            return mStartupSuccess;
+        }
+        if (!mHasStartedInitializingBrowserProcess) {
+            if (!tryPrepareToStartBrowserProcess(maxRenderers)) return false;
+        }
+
+        setAsynchronousStartup(false);
+        if (contentStart() > 0) {
+            // Failed. The callbacks may not have run, so run them.
             enqueueCallbackExecution(STARTUP_FAILURE, NOT_ALREADY_STARTED);
         }
+
+        // Startup should now be complete
+        assert mStartupDone;
+        return mStartupSuccess;
+    }
+
+    /**
+     * Wrap ContentMain.start() for testing.
+     */
+    @VisibleForTesting
+    int contentStart() {
+        return ContentMain.start();
     }
 
     public void addStartupCompletedObserver(StartupCallback callback) {
         ThreadUtils.assertOnUiThread();
-        if (mAsyncStartupDone)
+        if (mStartupDone) {
             postStartupCompleted(callback);
-        else
+        } else {
             mAsyncStartupCallbacks.add(callback);
+        }
     }
 
     private void executeEnqueuedCallbacks(int startupResult, boolean alreadyStarted) {
         assert ThreadUtils.runningOnUiThread() : "Callback from browser startup from wrong thread.";
-        mAsyncStartupDone = true;
+        mStartupDone = true;
+        mStartupSuccess = (startupResult <= 0);
         for (StartupCallback asyncStartupCallback : mAsyncStartupCallbacks) {
-            if (startupResult > 0) {
-                asyncStartupCallback.onFailure();
-            } else {
-                mStartupSuccess = true;
+            if (mStartupSuccess) {
                 asyncStartupCallback.onSuccess(alreadyStarted);
+            } else {
+                asyncStartupCallback.onFailure();
             }
         }
         // We don't want to hold on to any objects after we do not need them anymore.
         mAsyncStartupCallbacks.clear();
     }
 
+    // Queue the callbacks to run. Since running the callbacks clears the list it is safe to call
+    // this more than once.
     private void enqueueCallbackExecution(final int startupFailure, final boolean alreadyStarted) {
         new Handler().post(new Runnable() {
             @Override
@@ -208,28 +256,64 @@
         new Handler().post(new Runnable() {
             @Override
             public void run() {
-                if (mStartupSuccess)
+                if (mStartupSuccess) {
                     callback.onSuccess(ALREADY_STARTED);
-                else
+                } else {
                     callback.onFailure();
+                }
             }
         });
     }
 
-    /**
-     * Ensure that the browser process will be asynchronously started up. This also ensures that we
-     * get a call to {@link #browserStartupComplete} when the browser startup is complete.
-     */
     @VisibleForTesting
-    void enableAsynchronousStartup() {
-        setAsynchronousStartupConfig();
+    void prepareToStartBrowserProcess(int maxRendererProcesses) throws ProcessInitException {
+        Log.i(TAG, "Initializing chromium process, renderers=" + maxRendererProcesses);
+
+        // Normally Main.java will have kicked this off asynchronously for Chrome. But other
+        // ContentView apps like tests also need them so we make sure we've extracted resources
+        // here. We can still make it a little async (wait until the library is loaded).
+        ResourceExtractor resourceExtractor = ResourceExtractor.get(mContext);
+        resourceExtractor.startExtractingResources();
+
+        // Normally Main.java will have already loaded the library asynchronously, we only need
+        // to load it here if we arrived via another flow, e.g. bookmark access & sync setup.
+        LibraryLoader.ensureInitialized();
+
+        // TODO(yfriedman): Remove dependency on a command line flag for this.
+        DeviceUtils.addDeviceSpecificUserAgentSwitch(mContext);
+
+        Context appContext = mContext.getApplicationContext();
+        // Now we really need to have the resources ready.
+        resourceExtractor.waitForCompletion();
+
+        nativeSetCommandLineFlags(maxRendererProcesses,
+                nativeIsPluginEnabled() ? getPlugins() : null);
+        ContentMain.initApplicationContext(appContext);
     }
 
     /**
-     * @return whether the process was already initialized, so native was not instructed to start.
+     * Initialization needed for tests. Mainly used by content browsertests.
      */
-    @VisibleForTesting
-    boolean initializeAndroidBrowserProcess() throws ProcessInitException {
-        return !AndroidBrowserProcess.init(mContext, AndroidBrowserProcess.MAX_RENDERERS_LIMIT);
+    public void initChromiumBrowserProcessForTests() {
+        ResourceExtractor resourceExtractor = ResourceExtractor.get(mContext);
+        resourceExtractor.startExtractingResources();
+        resourceExtractor.waitForCompletion();
+
+        // Having a single renderer should be sufficient for tests. We can't have more than
+        // MAX_RENDERERS_LIMIT.
+        nativeSetCommandLineFlags(1 /* maxRenderers */, null);
     }
+
+    private String getPlugins() {
+        return PepperPluginManager.getPlugins(mContext);
+    }
+
+    private static native void nativeSetCommandLineFlags(int maxRenderProcesses,
+            String pluginDescriptor);
+
+    // Is this an official build of Chrome? Only native code knows for sure. Official build
+    // knowledge is needed very early in process startup.
+    private static native boolean nativeIsOfficialBuild();
+
+    private static native boolean nativeIsPluginEnabled();
 }
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
index eac5605..5e25e50 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
@@ -403,6 +403,12 @@
     // The AccessibilityInjector that handles loading Accessibility scripts into the web page.
     private AccessibilityInjector mAccessibilityInjector;
 
+    // Whether native accessibility, i.e. without any script injection, is allowed.
+    private boolean mNativeAccessibilityAllowed;
+
+    // Whether native accessibility, i.e. without any script injection, has been enabled.
+    private boolean mNativeAccessibilityEnabled;
+
     // Handles native accessibility, i.e. without any script injection.
     private BrowserAccessibilityManager mBrowserAccessibilityManager;
 
@@ -2387,9 +2393,9 @@
 
     @SuppressWarnings("unused")
     @CalledByNative
-    private SmoothScroller createSmoothScroller(boolean scrollDown, int mouseEventX,
-            int mouseEventY) {
-        return new SmoothScroller(this, scrollDown, mouseEventX, mouseEventY);
+    private GenericTouchGesture createGenericTouchGesture(int startX,
+            int startY, int deltaX, int deltaY) {
+        return new GenericTouchGesture(this, startX, startY, deltaX, deltaY);
     }
 
     @SuppressWarnings("unused")
@@ -2769,14 +2775,23 @@
      * If native accessibility (not script injection) is enabled, and if this is
      * running on JellyBean or later, returns an AccessibilityNodeProvider that
      * implements native accessibility for this view. Returns null otherwise.
+     * Lazily initializes native accessibility here if it's allowed.
      * @return The AccessibilityNodeProvider, if available, or null otherwise.
      */
     public AccessibilityNodeProvider getAccessibilityNodeProvider() {
         if (mBrowserAccessibilityManager != null) {
             return mBrowserAccessibilityManager.getAccessibilityNodeProvider();
-        } else {
-            return null;
         }
+
+        if (mNativeAccessibilityAllowed &&
+                !mNativeAccessibilityEnabled &&
+                mNativeContentViewCore != 0 &&
+                Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            mNativeAccessibilityEnabled = true;
+            nativeSetAccessibilityEnabled(mNativeContentViewCore, true);
+        }
+
+        return null;
     }
 
     /**
@@ -2863,30 +2878,20 @@
      * Turns browser accessibility on or off.
      * If |state| is |false|, this turns off both native and injected accessibility.
      * Otherwise, if accessibility script injection is enabled, this will enable the injected
-     * accessibility scripts, and if it is disabled this will enable the native accessibility.
+     * accessibility scripts. Native accessibility is enabled on demand.
      */
     public void setAccessibilityState(boolean state) {
-        boolean injectedAccessibility = false;
-        boolean nativeAccessibility = false;
-        if (state) {
-            if (isDeviceAccessibilityScriptInjectionEnabled()) {
-                injectedAccessibility = true;
-            } else {
-                nativeAccessibility = true;
-            }
+        if (!state) {
+            setInjectedAccessibility(false);
+            return;
         }
-        setInjectedAccessibility(injectedAccessibility);
-        setNativeAccessibilityState(nativeAccessibility);
-    }
 
-    /**
-     * Enable or disable native accessibility features.
-     */
-    public void setNativeAccessibilityState(boolean enabled) {
-        if (mNativeContentViewCore == 0) return;
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
-            nativeSetAccessibilityEnabled(mNativeContentViewCore, enabled);
+        if (isDeviceAccessibilityScriptInjectionEnabled()) {
+            setInjectedAccessibility(true);
+            return;
         }
+
+        mNativeAccessibilityAllowed = true;
     }
 
     /**
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewGestureHandler.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewGestureHandler.java
index e82f01d..eb295b8 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewGestureHandler.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewGestureHandler.java
@@ -9,6 +9,7 @@
 import android.os.Handler;
 import android.os.SystemClock;
 import android.util.Log;
+import android.view.InputDevice;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
 
@@ -53,7 +54,11 @@
      */
     static final String DELTA = "Delta";
 
-    private final Bundle mExtraParamBundle;
+    private final Bundle mExtraParamBundleSingleTap;
+    private final Bundle mExtraParamBundleFling;
+    private final Bundle mExtraParamBundleScroll;
+    private final Bundle mExtraParamBundleDoubleTapDragZoom;
+    private final Bundle mExtraParamBundlePinchBy;
     private GestureDetector mGestureDetector;
     private final ZoomManager mZoomManager;
     private LongPressDetector mLongPressDetector;
@@ -290,6 +295,7 @@
          * @param lastInputEventForVSync Indicates that this gesture event is the last input
          * to be event sent during the current vsync interval.
          * @param extraParams A bundle that holds specific extra parameters for certain gestures.
+         *                    This is read-only and should not be modified in this function.
          * Refer to gesture type definition for more information.
          * @return Whether the gesture was sent successfully.
          */
@@ -319,7 +325,12 @@
     ContentViewGestureHandler(
             Context context, MotionEventDelegate delegate, ZoomManager zoomManager,
             int inputEventDeliveryMode) {
-        mExtraParamBundle = new Bundle();
+        mExtraParamBundleSingleTap = new Bundle();
+        mExtraParamBundleFling = new Bundle();
+        mExtraParamBundleScroll = new Bundle();
+        mExtraParamBundleDoubleTapDragZoom = new Bundle();
+        mExtraParamBundlePinchBy = new Bundle();
+
         mLongPressDetector = new LongPressDetector(context, this);
         mMotionEventDelegate = delegate;
         mZoomManager = zoomManager;
@@ -417,12 +428,14 @@
                         int dy = (int) (distanceY + mAccumulatedScrollErrorY);
                         mAccumulatedScrollErrorX = distanceX + mAccumulatedScrollErrorX - dx;
                         mAccumulatedScrollErrorY = distanceY + mAccumulatedScrollErrorY - dy;
-                        mExtraParamBundle.clear();
-                        mExtraParamBundle.putInt(DISTANCE_X, dx);
-                        mExtraParamBundle.putInt(DISTANCE_Y, dy);
+
+                        mExtraParamBundleScroll.putInt(DISTANCE_X, dx);
+                        mExtraParamBundleScroll.putInt(DISTANCE_Y, dy);
+                        assert mExtraParamBundleScroll.size() == 2;
+
                         if ((dx | dy) != 0) {
                             sendLastGestureForVSync(GESTURE_SCROLL_BY,
-                                    e2.getEventTime(), x, y, mExtraParamBundle);
+                                    e2.getEventTime(), x, y, mExtraParamBundleScroll);
                         }
 
                         mMotionEventDelegate.invokeZoomPicker();
@@ -478,10 +491,12 @@
                                 // for double tap timeout.
                                 float x = e.getX();
                                 float y = e.getY();
-                                mExtraParamBundle.clear();
-                                mExtraParamBundle.putBoolean(SHOW_PRESS, mShowPressIsCalled);
+                                mExtraParamBundleSingleTap.putBoolean(SHOW_PRESS,
+                                        mShowPressIsCalled);
+                                assert mExtraParamBundleSingleTap.size() == 1;
+
                                 if (sendMotionEventAsGesture(GESTURE_SINGLE_TAP_CONFIRMED, e,
-                                        mExtraParamBundle)) {
+                                        mExtraParamBundleSingleTap)) {
                                     mIgnoreSingleTap = true;
                                 }
                                 setClickXAndY((int) x, (int) y);
@@ -505,10 +520,11 @@
 
                         int x = (int) e.getX();
                         int y = (int) e.getY();
-                        mExtraParamBundle.clear();
-                        mExtraParamBundle.putBoolean(SHOW_PRESS, mShowPressIsCalled);
+                        mExtraParamBundleSingleTap.putBoolean(SHOW_PRESS, mShowPressIsCalled);
                         sendMotionEventAsGesture(GESTURE_SINGLE_TAP_CONFIRMED, e,
-                            mExtraParamBundle);
+                            mExtraParamBundleSingleTap);
+                        assert mExtraParamBundleSingleTap.size() == 1;
+
                         setClickXAndY(x, y);
                         return true;
                     }
@@ -541,9 +557,10 @@
                                         mDoubleTapDragMode = DOUBLE_TAP_DRAG_MODE_ZOOM;
                                     }
                                 } else if (mDoubleTapDragMode == DOUBLE_TAP_DRAG_MODE_ZOOM) {
-                                    mExtraParamBundle.clear();
+                                    assert mExtraParamBundleDoubleTapDragZoom.isEmpty();
                                     sendGesture(GESTURE_SCROLL_BY, e.getEventTime(),
-                                            (int) e.getX(), (int) e.getY(), mExtraParamBundle);
+                                            (int) e.getX(), (int) e.getY(),
+                                            mExtraParamBundleDoubleTapDragZoom);
 
                                     float dy = mDoubleTapY - e.getY();
                                     pinchBy(e.getEventTime(),
@@ -649,10 +666,10 @@
 
         mFlingMayBeActive = true;
 
-        mExtraParamBundle.clear();
-        mExtraParamBundle.putInt(VELOCITY_X, velocityX);
-        mExtraParamBundle.putInt(VELOCITY_Y, velocityY);
-        sendGesture(GESTURE_FLING_START, timeMs, x, y, mExtraParamBundle);
+        mExtraParamBundleFling.putInt(VELOCITY_X, velocityX);
+        mExtraParamBundleFling.putInt(VELOCITY_Y, velocityY);
+        assert mExtraParamBundleFling.size() == 2;
+        sendGesture(GESTURE_FLING_START, timeMs, x, y, mExtraParamBundleFling);
     }
 
     /**
@@ -729,9 +746,10 @@
      * @param delta The percentage to pinch by.
      */
     void pinchBy(long timeMs, int anchorX, int anchorY, float delta) {
-        mExtraParamBundle.clear();
-        mExtraParamBundle.putFloat(DELTA, delta);
-        sendLastGestureForVSync(GESTURE_PINCH_BY, timeMs, anchorX, anchorY, mExtraParamBundle);
+        mExtraParamBundlePinchBy.putFloat(DELTA, delta);
+        assert mExtraParamBundlePinchBy.size() == 1;
+        sendLastGestureForVSync(
+                GESTURE_PINCH_BY, timeMs, anchorX, anchorY, mExtraParamBundlePinchBy);
         mPinchInProgress = true;
     }
 
@@ -838,16 +856,11 @@
      * FrameLoader::transitionToCommitted iff the page ever had touch handlers.
      */
     void resetGestureHandlers() {
-        {
-            MotionEvent me = obtainActionCancelMotionEvent();
-            mGestureDetector.onTouchEvent(me);
-            me.recycle();
-        }
-        {
-            MotionEvent me = obtainActionCancelMotionEvent();
-            mZoomManager.processTouchEvent(me);
-            me.recycle();
-        }
+        MotionEvent me = obtainActionCancelMotionEvent();
+        me.setSource(InputDevice.SOURCE_CLASS_POINTER);
+        mGestureDetector.onTouchEvent(me);
+        mZoomManager.processTouchEvent(me);
+        me.recycle();
         mLongPressDetector.cancelLongPress();
     }
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java b/content/public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java
new file mode 100644
index 0000000..f71b023
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content/browser/GenericTouchGesture.java
@@ -0,0 +1,269 @@
+// 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.
+
+package org.chromium.content.browser;
+
+import android.animation.TimeAnimator;
+import android.animation.TimeAnimator.TimeListener;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerProperties;
+import android.view.MotionEvent.PointerCoords;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+
+/**
+ * Provides a Java-side implementation for simulating touch gestures,
+ * such as scroll or pinch.
+ */
+@JNINamespace("content")
+public class GenericTouchGesture {
+    private final ContentViewCore mContentViewCore;
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+    private TimeAnimator mTimeAnimator;
+
+    private int mNativePtr;
+    private long mDownTime;
+
+    private final byte STATE_INITIAL = 0;
+    private final byte STATE_MOVING = 1;
+    private final byte STATE_PENDING_UP = 2;
+    private final byte STATE_FINAL = 3;
+    private byte state = STATE_INITIAL;
+
+    private static class TouchPointer {
+        private final float mStartX;
+        private final float mStartY;
+        private final float mDeltaX;
+        private final float mDeltaY;
+        private final float mStepX;
+        private final float mStepY;
+
+        private PointerProperties mProperties;
+        private PointerCoords mCoords;
+
+
+        // Class representing a single pointer being moved over the screen.
+        TouchPointer(int startX, int startY, int deltaX, int deltaY,
+                int id, float scale) {
+            mStartX = startX * scale;
+            mStartY = startY * scale;
+
+            mDeltaX = deltaX * scale;
+            mDeltaY = deltaY * scale;
+
+            if (deltaX != 0 || deltaY != 0) {
+                mStepX = mDeltaX / Math.abs(mDeltaX + mDeltaY);
+                mStepY = mDeltaY / Math.abs(mDeltaX + mDeltaY);
+            } else {
+                mStepX = 0;
+                mStepY = 0;
+            }
+
+            mProperties = new PointerProperties();
+            mProperties.id = id;
+            mProperties.toolType = MotionEvent.TOOL_TYPE_FINGER;
+
+            mCoords = new PointerCoords();
+            mCoords.x = mStartX;
+            mCoords.y = mStartY;
+            mCoords.pressure = 1.0f;
+        }
+
+        PointerProperties getProperties() {
+            return mProperties;
+        }
+
+        PointerCoords getCoords() {
+            return mCoords;
+        }
+
+        float getCurrentX() {
+            return mCoords.x;
+        }
+
+        float getCurrentY() {
+            return mCoords.y;
+        }
+
+        void moveBy(float delta) {
+            mCoords.x += mStepX * delta;
+            mCoords.y += mStepY * delta;
+        }
+
+        boolean hasArrived() {
+            return Math.abs(mCoords.x - mStartX) >= Math.abs(mDeltaX) &&
+                   Math.abs(mCoords.y - mStartY) >= Math.abs(mDeltaY);
+        }
+    }
+
+    private TouchPointer[] mPointers;
+    private final PointerProperties[] mPointerProperties;
+    private final PointerCoords[] mPointerCoords;
+
+
+    GenericTouchGesture(ContentViewCore contentViewCore,
+            int startX, int startY, int deltaX, int deltaY) {
+        mContentViewCore = contentViewCore;
+
+        float scale = mContentViewCore.getRenderCoordinates().getDeviceScaleFactor();
+
+        mPointers = new TouchPointer[1];
+        mPointers[0] = new TouchPointer(startX, startY, deltaX, deltaY, 0, scale);
+
+        mPointerProperties = new PointerProperties[1];
+        mPointerProperties[0] = mPointers[0].getProperties();
+
+        mPointerCoords = new PointerCoords[1];
+        mPointerCoords[0] = mPointers[0].getCoords();
+    }
+
+    GenericTouchGesture(ContentViewCore contentViewCore,
+            int startX0, int startY0, int deltaX0, int deltaY0,
+            int startX1, int startY1, int deltaX1, int deltaY1) {
+        mContentViewCore = contentViewCore;
+
+        float scale = mContentViewCore.getRenderCoordinates().getDeviceScaleFactor();
+
+        mPointers = new TouchPointer[2];
+        mPointers[0] = new TouchPointer(startX0, startY0, deltaX0, deltaY0, 0, scale);
+        mPointers[1] = new TouchPointer(startX1, startY1, deltaX1, deltaY1, 1, scale);
+
+        mPointerProperties = new PointerProperties[2];
+        mPointerProperties[0] = mPointers[0].getProperties();
+        mPointerProperties[1] = mPointers[1].getProperties();
+
+        mPointerCoords = new PointerCoords[2];
+        mPointerCoords[0] = mPointers[0].getCoords();
+        mPointerCoords[1] = mPointers[1].getCoords();
+    }
+
+    @CalledByNative
+    void start(int nativePtr) {
+        assert mNativePtr == 0;
+        mNativePtr = nativePtr;
+
+        Runnable runnable = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) ?
+            createJBRunnable() : createPreJBRunnable();
+        mHandler.post(runnable);
+    }
+
+    boolean sendEvent(long time) {
+        switch (state) {
+            case STATE_INITIAL: {
+                mDownTime = SystemClock.uptimeMillis();
+
+                // Touch the first pointer down. This initiates the gesture.
+                MotionEvent event = MotionEvent.obtain(mDownTime, time,
+                        MotionEvent.ACTION_DOWN,
+                        mPointers[0].getCurrentX(), mPointers[0].getCurrentY(), 0);
+                mContentViewCore.onTouchEvent(event);
+                event.recycle();
+
+                // If there are more pointers, touch them down too.
+                if (mPointers.length > 1) {
+                    event = MotionEvent.obtain(mDownTime, time,
+                            MotionEvent.ACTION_POINTER_DOWN,
+                            mPointers.length, mPointerProperties, mPointerCoords,
+                            0, 0, 1, 1, 0, 0, 0, 0);
+                    mContentViewCore.onTouchEvent(event);
+                    event.recycle();
+                }
+                state = STATE_MOVING;
+                break;
+            }
+            case STATE_MOVING: {
+                float delta = nativeGetDelta(
+                    mNativePtr, mContentViewCore.getRenderCoordinates().getDeviceScaleFactor());
+                if (delta != 0) {
+                    for (TouchPointer pointer : mPointers) {
+                        pointer.moveBy((float)delta);
+                    }
+                    MotionEvent event = MotionEvent.obtain(mDownTime, time,
+                            MotionEvent.ACTION_MOVE,
+                            mPointers.length, mPointerProperties, mPointerCoords,
+                            0, 0, 1, 1, 0, 0, 0, 0);
+                    mContentViewCore.onTouchEvent(event);
+                    event.recycle();
+                }
+                if (havePointersArrived())
+                    state = STATE_PENDING_UP;
+                break;
+            }
+            case STATE_PENDING_UP: {
+                // If there are more than one pointers, lift them up first.
+                if (mPointers.length > 1) {
+                    MotionEvent event = MotionEvent.obtain(mDownTime, time,
+                            MotionEvent.ACTION_POINTER_UP,
+                            mPointers.length, mPointerProperties, mPointerCoords,
+                            0, 0, 1, 1, 0, 0, 0, 0);
+                    mContentViewCore.onTouchEvent(event);
+                    event.recycle();
+                }
+
+                // Finally, lift the first pointer up to finish the gesture.
+                MotionEvent event = MotionEvent.obtain(mDownTime, time,
+                        MotionEvent.ACTION_UP,
+                        mPointers[0].getCurrentX(), mPointers[0].getCurrentY(), 0);
+                mContentViewCore.onTouchEvent(event);
+                event.recycle();
+
+                nativeSetHasFinished(mNativePtr);
+                state = STATE_FINAL;
+                break;
+            }
+        }
+        return state != STATE_FINAL;
+    }
+
+    private boolean havePointersArrived() {
+        boolean arrived = true;
+        for (TouchPointer pointer : mPointers) {
+            arrived = arrived && pointer.hasArrived();
+        }
+        return arrived;
+    }
+
+    private Runnable createJBRunnable() {
+        // On JB, we rely on TimeAnimator to send events tied with vsync.
+        return new Runnable() {
+            @Override
+            public void run() {
+                mTimeAnimator = new TimeAnimator();
+                mTimeAnimator.setTimeListener(new TimeListener() {
+                    @Override
+                    public void onTimeUpdate(TimeAnimator animation, long totalTime,
+                            long deltaTime) {
+                        if (!sendEvent(mDownTime + totalTime)) {
+                            mTimeAnimator.end();
+                        }
+                    }
+                });
+                mTimeAnimator.start();
+            }
+        };
+    }
+
+    private Runnable createPreJBRunnable() {
+        // Pre-JB there's no TimeAnimator, so we keep posting messages.
+        return new Runnable() {
+            @Override
+            public void run() {
+                if (sendEvent(SystemClock.uptimeMillis())) {
+                    mHandler.post(this);
+                }
+            }
+        };
+    }
+
+    private native float nativeGetDelta(
+            int nativeGenericTouchGestureAndroid, float scale);
+    private native void nativeSetHasFinished(int nativeGenericTouchGestureAndroid);
+}
diff --git a/content/public/android/java/src/org/chromium/content/browser/SelectActionModeCallback.java b/content/public/android/java/src/org/chromium/content/browser/SelectActionModeCallback.java
index 4ff58bd..d44c574 100644
--- a/content/public/android/java/src/org/chromium/content/browser/SelectActionModeCallback.java
+++ b/content/public/android/java/src/org/chromium/content/browser/SelectActionModeCallback.java
@@ -4,6 +4,7 @@
 
 package org.chromium.content.browser;
 
+import android.app.Activity;
 import android.app.SearchManager;
 import android.content.ClipboardManager;
 import android.content.Context;
@@ -212,6 +213,9 @@
                     i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true);
                     i.putExtra(SearchManager.QUERY, selection);
                     i.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName());
+                    if (!(getContext() instanceof Activity)) {
+                        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    }
                     try {
                         getContext().startActivity(i);
                     } catch (android.content.ActivityNotFoundException ex) {
diff --git a/content/public/android/java/src/org/chromium/content/browser/SmoothScroller.java b/content/public/android/java/src/org/chromium/content/browser/SmoothScroller.java
deleted file mode 100644
index 5c6c339..0000000
--- a/content/public/android/java/src/org/chromium/content/browser/SmoothScroller.java
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.content.browser;
-
-import android.animation.TimeAnimator;
-import android.animation.TimeAnimator.TimeListener;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.MotionEvent;
-
-import org.chromium.base.CalledByNative;
-import org.chromium.base.JNINamespace;
-
-/**
- * Provides a Java-side implementation for chrome.gpuBenchmarking.smoothScrollBy.
- */
-@JNINamespace("content")
-public class SmoothScroller {
-    private final ContentViewCore mContentViewCore;
-    private final boolean mScrollDown;
-    private final float mMouseEventX;
-    private final float mMouseEventY;
-
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
-
-    private TimeAnimator mTimeAnimator;
-
-    private int mNativePtr;
-    private long mDownTime;
-    private float mCurrentY;
-
-    private final byte STATE_INITIAL = 0;
-    private final byte STATE_MOVING = 1;
-    private final byte STATE_PENDING_UP = 2;
-    private final byte STATE_FINAL = 3;
-    private byte state = STATE_INITIAL;
-
-    SmoothScroller(ContentViewCore contentViewCore, boolean scrollDown,
-            int mouseEventX, int mouseEventY) {
-        mContentViewCore = contentViewCore;
-        mScrollDown = scrollDown;
-        float scale = mContentViewCore.getRenderCoordinates().getDeviceScaleFactor();
-        mMouseEventX = mouseEventX * scale;
-        mMouseEventY = mouseEventY * scale;
-        mCurrentY = mMouseEventY;
-    }
-
-    @CalledByNative
-    void start(int nativePtr) {
-        assert mNativePtr == 0;
-        mNativePtr = nativePtr;
-
-        Runnable runnable = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) ?
-            createJBRunnable() : createPreJBRunnable();
-        mHandler.post(runnable);
-    }
-
-    boolean sendEvent(long time) {
-        switch (state) {
-            case STATE_INITIAL: {
-                mDownTime = SystemClock.uptimeMillis();
-                MotionEvent event = MotionEvent.obtain(mDownTime, mDownTime,
-                        MotionEvent.ACTION_DOWN, mMouseEventX, mCurrentY, 0);
-                mContentViewCore.onTouchEvent(event);
-                event.recycle();
-                state = STATE_MOVING;
-                break;
-            }
-            case STATE_MOVING: {
-                double delta = nativeGetScrollDelta(
-                    mNativePtr, mContentViewCore.getRenderCoordinates().getDeviceScaleFactor());
-                if (delta != 0) {
-                    mCurrentY += mScrollDown ? -delta : delta;
-
-                    MotionEvent event = MotionEvent.obtain(mDownTime, time,
-                            MotionEvent.ACTION_MOVE, mMouseEventX, mCurrentY, 0);
-                    mContentViewCore.onTouchEvent(event);
-                    event.recycle();
-                }
-                if (nativeHasFinished(mNativePtr))
-                    state = STATE_PENDING_UP;
-                break;
-            }
-            case STATE_PENDING_UP: {
-                MotionEvent event = MotionEvent.obtain(mDownTime, time,
-                        MotionEvent.ACTION_UP, mMouseEventX, mCurrentY, 0);
-                mContentViewCore.onTouchEvent(event);
-                event.recycle();
-                nativeSetHasSentMotionUp(mNativePtr);
-                state = STATE_FINAL;
-                break;
-            }
-        }
-        return state != STATE_FINAL;
-    }
-
-    private Runnable createJBRunnable() {
-        // On JB, we rely on TimeAnimator to send events tied with vsync.
-        return new Runnable() {
-            @Override
-            public void run() {
-                mTimeAnimator = new TimeAnimator();
-                mTimeAnimator.setTimeListener(new TimeListener() {
-                    @Override
-                    public void onTimeUpdate(TimeAnimator animation, long totalTime,
-                            long deltaTime) {
-                        if (!sendEvent(mDownTime + totalTime)) {
-                            mTimeAnimator.end();
-                        }
-                    }
-                });
-                mTimeAnimator.start();
-            }
-        };
-    }
-
-    private Runnable createPreJBRunnable() {
-        // Pre-JB there's no TimeAnimator, so we keep posting messages.
-        return new Runnable() {
-            @Override
-            public void run() {
-                if (sendEvent(SystemClock.uptimeMillis())) {
-                    mHandler.post(this);
-                }
-            }
-        };
-    }
-
-    private native double nativeGetScrollDelta(
-            int nativeTouchSmoothScrollGestureAndroid, double scale);
-    private native boolean nativeHasFinished(int nativeTouchSmoothScrollGestureAndroid);
-    private native void nativeSetHasSentMotionUp(int nativeTouchSmoothScrollGestureAndroid);
-}
diff --git a/content/public/android/java/src/org/chromium/content/browser/VSyncMonitor.java b/content/public/android/java/src/org/chromium/content/browser/VSyncMonitor.java
index ca08c43..9e3bfc0 100644
--- a/content/public/android/java/src/org/chromium/content/browser/VSyncMonitor.java
+++ b/content/public/android/java/src/org/chromium/content/browser/VSyncMonitor.java
@@ -23,6 +23,11 @@
 public class VSyncMonitor {
     private static final String TAG = "VSyncMonitor";
 
+    private static final long NANOSECONDS_PER_SECOND = 1000000000;
+    private static final long NANOSECONDS_PER_MILLISECOND = 1000000;
+    private static final long NANOSECONDS_PER_MICROSECOND = 1000;
+    public static final int MAX_AUTO_ONVSYNC_COUNT = 5;
+
     public interface Listener {
         /**
          * Called very soon after the start of the display's vertical sync period.
@@ -32,22 +37,13 @@
         public void onVSync(VSyncMonitor monitor, long vsyncTimeMicros);
     }
 
-    private static final long NANOSECONDS_PER_SECOND = 1000000000;
-    private static final long NANOSECONDS_PER_MILLISECOND = 1000000;
-    private static final long NANOSECONDS_PER_MICROSECOND = 1000;
-
     private Listener mListener;
 
     // Display refresh rate as reported by the system.
     private final long mRefreshPeriodNano;
 
-    // Last time requestUpdate() was called.
-    private long mLastUpdateRequestNano;
-
     private boolean mHaveRequestInFlight;
-
     private int mTriggerNextVSyncCount;
-    private static final int MAX_VSYNC_COUNT = 5;
 
     // Choreographer is used to detect vsync on >= JB.
     private final Choreographer mChoreographer;
@@ -134,11 +130,10 @@
     /**
      * Request to be notified of the closest display vsync events.
      * Listener.onVSync() will be called soon after the upcoming vsync pulses.
-     * It will be called at most MAX_VSYNC_COUNT times unless requestUpdate() is called again.
+     * It will be called at most MAX_AUTO_ONVSYNC_COUNT times unless requestUpdate() is called.
      */
     public void requestUpdate() {
-        mTriggerNextVSyncCount = MAX_VSYNC_COUNT;
-        mLastUpdateRequestNano = getCurrentNanoTime();
+        mTriggerNextVSyncCount = MAX_AUTO_ONVSYNC_COUNT;
         postCallback();
     }
 
@@ -157,7 +152,7 @@
     private void onVSyncCallback(long frameTimeNanos) {
         assert mHaveRequestInFlight;
         mHaveRequestInFlight = false;
-        if (mTriggerNextVSyncCount > 0) {
+        if (mTriggerNextVSyncCount >= 0) {
             mTriggerNextVSyncCount--;
             postCallback();
         }
@@ -178,7 +173,7 @@
 
     private void postRunnableCallback() {
         assert !isVSyncSignalAvailable();
-        final long currentTime = mLastUpdateRequestNano;
+        final long currentTime = getCurrentNanoTime();
         final long lastRefreshTime = mGoodStartingPointNano +
                 ((currentTime - mGoodStartingPointNano) / mRefreshPeriodNano) * mRefreshPeriodNano;
         long delay = (lastRefreshTime + mRefreshPeriodNano) - currentTime;
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java b/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java
index ab8a1fc..bedfed0 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java
@@ -335,6 +335,15 @@
                             Character.toString((char)unicodeChar));
                 }
             }
+        } else if (event.getAction() == KeyEvent.ACTION_DOWN) {
+            // TODO(aurimas): remove this workaround when crbug.com/278584 is fixed.
+            if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
+                beginBatchEdit();
+                finishComposingText();
+                mImeAdapter.translateAndSendNativeEvents(event);
+                endBatchEdit();
+                return true;
+            }
         }
         mImeAdapter.translateAndSendNativeEvents(event);
         return true;
diff --git a/content/public/android/java/strings/android_content_strings.grd b/content/public/android/java/strings/android_content_strings.grd
index 6589a0f..7ad7344 100644
--- a/content/public/android/java/strings/android_content_strings.grd
+++ b/content/public/android/java/strings/android_content_strings.grd
@@ -26,10 +26,10 @@
       <message desc="Title for the time picker dialog, which can be used to choose a time. [CHAR-LIMIT=32]" name="IDS_TIME_PICKER_DIALOG_TITLE">
         Set time
       </message>
-      <message desc="Value for AM in AM/PM in the time picker dialog. AM represents the morning (ante-meridiem). [CHAR-LIMIT=2]" name="IDS_TIME_PICKER_DIALOG_AM">
+      <message desc="Value for AM in AM/PM in the time picker dialog. AM represents the morning (ante-meridiem). [CHAR-LIMIT=4]" name="IDS_TIME_PICKER_DIALOG_AM">
         AM
       </message>
-      <message desc="Value for PM in AM/PM in the time picker dialog. PM represents the afternoon (post-meridiem). [CHAR-LIMIT=2]" name="IDS_TIME_PICKER_DIALOG_PM">
+      <message desc="Value for PM in AM/PM in the time picker dialog. PM represents the afternoon (post-meridiem). [CHAR-LIMIT=4]" name="IDS_TIME_PICKER_DIALOG_PM">
         PM
       </message>
       <message desc="Separator used between hours and minutes in the time pickerr dialog (hh:mm) [CHAR-LIMIT=1]" name="IDS_TIME_PICKER_DIALOG_HOUR_MINUTE_SEPARATOR">
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/AddressDetectionTest.java b/content/public/android/javatests/src/org/chromium/content/browser/AddressDetectionTest.java
index ce7cfd6..867b662 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/AddressDetectionTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/AddressDetectionTest.java
@@ -6,6 +6,7 @@
 
 import android.test.suitebuilder.annotation.MediumTest;
 
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 
 /**
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java
index 541c96e..d60b04d 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/BrowserStartupControllerTest.java
@@ -11,6 +11,7 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.AdvancedMockContext;
 import org.chromium.content.common.ProcessInitException;
+import org.chromium.content.common.ResultCodes;
 
 public class BrowserStartupControllerTest extends InstrumentationTestCase {
 
@@ -18,41 +19,40 @@
 
     private static class TestBrowserStartupController extends BrowserStartupController {
 
-        private boolean mThrowProcessInitException;
         private int mStartupResult;
-        private boolean mAlreadyInitialized = false;
+        private boolean mLibraryLoadSucceeds;
         private int mInitializedCounter = 0;
-        private boolean mCallbackWasSetup;
+
+        @Override
+        void prepareToStartBrowserProcess(int numRenderers) throws ProcessInitException {
+            if (!mLibraryLoadSucceeds) {
+                throw new ProcessInitException(ResultCodes.RESULT_CODE_NATIVE_LIBRARY_LOAD_FAILED);
+            }
+        }
 
         private TestBrowserStartupController(Context context) {
             super(context);
         }
 
         @Override
-        void enableAsynchronousStartup() {
-            mCallbackWasSetup = true;
-        }
-
-        @Override
-        boolean initializeAndroidBrowserProcess() throws ProcessInitException {
+        int contentStart() {
             mInitializedCounter++;
-            if (mThrowProcessInitException) {
-                throw new ProcessInitException(4);
-            }
-            if (!mAlreadyInitialized) {
+            if(BrowserStartupController.browserMayStartAsynchonously()) {
                 // Post to the UI thread to emulate what would happen in a real scenario.
-                ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+                ThreadUtils.postOnUiThread(new Runnable() {
                     @Override
                     public void run() {
                         BrowserStartupController.browserStartupComplete(mStartupResult);
                     }
                 });
+            } else {
+                BrowserStartupController.browserStartupComplete(mStartupResult);
             }
-            return mAlreadyInitialized;
+            return mStartupResult;
         }
 
-        private boolean hasBeenInitializedOneTime() {
-            return mInitializedCounter == 1;
+        private int initializedCounter() {
+            return mInitializedCounter;
         }
     }
 
@@ -92,6 +92,7 @@
     @SmallTest
     public void testSingleAsynchronousStartupRequest() {
         mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS;
+        mController.mLibraryLoadSucceeds = true;
         final TestStartupCallback callback = new TestStartupCallback();
 
         // Kick off the asynchronous startup request.
@@ -102,9 +103,10 @@
             }
         });
 
-        assertTrue("The callback should have been setup", mController.mCallbackWasSetup);
-        assertTrue("The browser process should have been initialized one time.",
-                mController.hasBeenInitializedOneTime());
+        assertTrue("Asynchronous mode should have been set.",
+                BrowserStartupController.browserMayStartAsynchonously());
+        assertEquals("The browser process should have been initialized one time.",
+                1, mController.initializedCounter());
 
         // Wait for callbacks to complete.
         getInstrumentation().waitForIdleSync();
@@ -118,6 +120,7 @@
     @SmallTest
     public void testMultipleAsynchronousStartupRequests() {
         mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS;
+        mController.mLibraryLoadSucceeds = true;
         final TestStartupCallback callback1 = new TestStartupCallback();
         final TestStartupCallback callback2 = new TestStartupCallback();
         final TestStartupCallback callback3 = new TestStartupCallback();
@@ -142,9 +145,10 @@
             }
         });
 
-        assertTrue("The callback should have been setup", mController.mCallbackWasSetup);
-        assertTrue("The browser process should have been initialized one time.",
-                mController.hasBeenInitializedOneTime());
+        assertTrue("Asynchronous mode should have been set.",
+                BrowserStartupController.browserMayStartAsynchonously());
+        assertEquals("The browser process should have been initialized one time.",
+                1, mController.initializedCounter());
 
         // Wait for callbacks to complete.
         getInstrumentation().waitForIdleSync();
@@ -164,6 +168,7 @@
     @SmallTest
     public void testConsecutiveAsynchronousStartupRequests() {
         mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS;
+        mController.mLibraryLoadSucceeds = true;
         final TestStartupCallback callback1 = new TestStartupCallback();
         final TestStartupCallback callback2 = new TestStartupCallback();
 
@@ -181,9 +186,10 @@
             }
         });
 
-        assertTrue("The callback should have been setup", mController.mCallbackWasSetup);
-        assertTrue("The browser process should have been initialized one time.",
-                mController.hasBeenInitializedOneTime());
+        assertTrue("Asynchronous mode should have been set.",
+                BrowserStartupController.browserMayStartAsynchonously());
+        assertEquals("The browser process should have been initialized one time.",
+                1, mController.initializedCounter());
 
         // Wait for callbacks to complete.
         getInstrumentation().waitForIdleSync();
@@ -226,6 +232,7 @@
     @SmallTest
     public void testSingleFailedAsynchronousStartupRequest() {
         mController.mStartupResult = BrowserStartupController.STARTUP_FAILURE;
+        mController.mLibraryLoadSucceeds = true;
         final TestStartupCallback callback = new TestStartupCallback();
 
         // Kick off the asynchronous startup request.
@@ -236,9 +243,10 @@
             }
         });
 
-        assertTrue("The callback should have been setup", mController.mCallbackWasSetup);
-        assertTrue("The browser process should have been initialized one time.",
-                mController.hasBeenInitializedOneTime());
+        assertTrue("Asynchronous mode should have been set.",
+                BrowserStartupController.browserMayStartAsynchonously());
+        assertEquals("The browser process should have been initialized one time.",
+                1, mController.initializedCounter());
 
         // Wait for callbacks to complete.
         getInstrumentation().waitForIdleSync();
@@ -250,6 +258,7 @@
     @SmallTest
     public void testConsecutiveFailedAsynchronousStartupRequests() {
         mController.mStartupResult = BrowserStartupController.STARTUP_FAILURE;
+        mController.mLibraryLoadSucceeds = true;
         final TestStartupCallback callback1 = new TestStartupCallback();
         final TestStartupCallback callback2 = new TestStartupCallback();
 
@@ -267,9 +276,10 @@
             }
         });
 
-        assertTrue("The callback should have been setup", mController.mCallbackWasSetup);
-        assertTrue("The browser process should have been initialized one time.",
-                mController.hasBeenInitializedOneTime());
+        assertTrue("Asynchronous mode should have been set.",
+                BrowserStartupController.browserMayStartAsynchonously());
+        assertEquals("The browser process should have been initialized one time.",
+                1, mController.initializedCounter());
 
         // Wait for callbacks to complete.
         getInstrumentation().waitForIdleSync();
@@ -306,35 +316,76 @@
     }
 
     @SmallTest
-    public void testAndroidBrowserStartupThrowsException() {
-        mController.mThrowProcessInitException = true;
-        final TestStartupCallback callback = new TestStartupCallback();
-
-        // Kick off the asynchronous startup request.
+    public void testSingleSynchronousRequest() {
+        mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS;
+        mController.mLibraryLoadSucceeds = true;
+        // Kick off the synchronous startup.
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                mController.startBrowserProcessesAsync(callback);
+                assertTrue("Browser should have started successfully",
+                        mController.startBrowserProcessesSync(1));
             }
         });
+        assertFalse("Synchronous mode should have been set",
+                BrowserStartupController.browserMayStartAsynchonously());
 
-        assertTrue("The callback should have been setup", mController.mCallbackWasSetup);
-        assertTrue("The browser process should have been initialized one time.",
-                mController.hasBeenInitializedOneTime());
-
-        // Wait for callbacks to complete.
-        getInstrumentation().waitForIdleSync();
-
-        assertTrue("Callback should have been executed.", callback.mHasStartupResult);
-        assertTrue("Callback should have been a failure.", callback.mWasFailure);
+        assertEquals("The browser process should have been initialized one time.",
+                1, mController.initializedCounter());
     }
 
     @SmallTest
-    public void testAndroidBrowserProcessAlreadyInitializedByOtherPartsOfCode() {
-        mController.mAlreadyInitialized = true;
+    public void testAsyncThenSyncRequests() {
+        mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS;
+        mController.mLibraryLoadSucceeds = true;
         final TestStartupCallback callback = new TestStartupCallback();
 
-        // Kick off the asynchronous startup request.
+        // Kick off the startups.
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                mController.startBrowserProcessesAsync(callback);
+                // To ensure that the async startup doesn't complete too soon we have
+                // to do both these in a since Runnable instance. This avoids the
+                // unpredictable race that happens in real situations.
+                assertTrue("Browser should have started successfully",
+                        mController.startBrowserProcessesSync(1));
+            }
+        });
+        assertFalse("Synchronous mode should have been set",
+                BrowserStartupController.browserMayStartAsynchonously());
+
+        assertEquals("The browser process should have been initialized twice.",
+                2, mController.initializedCounter());
+
+        assertTrue("Callback should have been executed.", callback.mHasStartupResult);
+        assertTrue("Callback should have been a success.", callback.mWasSuccess);
+        assertFalse("Callback should be told that the browser process was not already started.",
+                callback.mAlreadyStarted);
+    }
+
+    @SmallTest
+    public void testSyncThenAsyncRequests() {
+        mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS;
+        mController.mLibraryLoadSucceeds = true;
+        final TestStartupCallback callback = new TestStartupCallback();
+
+        // Do a synchronous startup first.
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                assertTrue("Browser should have started successfully",
+                        mController.startBrowserProcessesSync(1));
+            }
+        });
+
+        assertEquals("The browser process should have been initialized once.",
+                1, mController.initializedCounter());
+
+        assertFalse("Synchronous mode should have been set",
+                BrowserStartupController.browserMayStartAsynchonously());
+
+        // Kick off the asynchronous startup request. This should just queue the callback.
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
@@ -342,9 +393,8 @@
             }
         });
 
-        assertTrue("The callback should have been setup", mController.mCallbackWasSetup);
-        assertTrue("The browser process should have been initialized one time.",
-                mController.hasBeenInitializedOneTime());
+        assertEquals("The browser process should not have been initialized a second time.",
+                1, mController.initializedCounter());
 
         // Wait for callbacks to complete.
         getInstrumentation().waitForIdleSync();
@@ -354,4 +404,30 @@
         assertTrue("Callback should be told that the browser process was already started.",
                 callback.mAlreadyStarted);
     }
+
+    @SmallTest
+    public void testLibraryLoadFails() {
+        mController.mLibraryLoadSucceeds = false;
+        final TestStartupCallback callback = new TestStartupCallback();
+
+        // Kick off the asynchronous startup request.
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                mController.startBrowserProcessesAsync(callback);
+            }
+        });
+
+        assertEquals("The browser process should not have been initialized.",
+                0, mController.initializedCounter());
+
+        // Wait for callbacks to complete.
+        getInstrumentation().waitForIdleSync();
+
+        assertTrue("Callback should have been executed.", callback.mHasStartupResult);
+        assertFalse("Callback should have been a failure.", callback.mWasSuccess);
+        assertFalse("Callback should be told that the browser process was not already started.",
+                callback.mAlreadyStarted);
+    }
+
 }
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/VSyncMonitorTest.java b/content/public/android/javatests/src/org/chromium/content/browser/VSyncMonitorTest.java
index 935d1eb..4fd9876 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/VSyncMonitorTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/VSyncMonitorTest.java
@@ -17,12 +17,14 @@
         public long mFramePeriods[];
         public int mFrameCount;
 
+        private final boolean mActivelyRequestUpdate;
         private boolean mDone;
         private long mPreviousVSyncTimeMicros;
         private Object mSyncRoot = new Object();
 
-        VSyncDataCollector(int frames) {
+        VSyncDataCollector(int frames, boolean activelyRequestUpdate) {
             mFramePeriods = new long[frames];
+            mActivelyRequestUpdate = activelyRequestUpdate;
         }
 
         public boolean isDone() {
@@ -46,7 +48,7 @@
             }
             mFramePeriods[mFrameCount++] = vsyncTimeMicros - mPreviousVSyncTimeMicros;
             mPreviousVSyncTimeMicros = vsyncTimeMicros;
-            monitor.requestUpdate();
+            if (mActivelyRequestUpdate) monitor.requestUpdate();
         }
 
         public void waitTillDone() throws InterruptedException {
@@ -74,17 +76,20 @@
     // Check that the vsync period roughly matches the timestamps that the monitor generates.
     private void performVSyncPeriodTest(boolean enableJBVSync) throws InterruptedException {
         // Collect roughly one second of data on a 60 fps display.
-        final int framesPerSecond = 60;
-        final int secondsToRun = 1;
-        final int totalFrames = secondsToRun * framesPerSecond;
-        VSyncDataCollector collector = new VSyncDataCollector(totalFrames);
+        collectAndCheckVSync(enableJBVSync, 60, true);
+        collectAndCheckVSync(enableJBVSync, VSyncMonitor.MAX_AUTO_ONVSYNC_COUNT, false);
+    }
+
+    private void collectAndCheckVSync(
+            boolean enableJBVSync, final int totalFrames, final boolean activeFrames)
+            throws InterruptedException {
+        VSyncDataCollector collector = new VSyncDataCollector(totalFrames, activeFrames);
         VSyncMonitor monitor = createVSyncMonitor(collector, enableJBVSync);
 
         long reportedFramePeriod = monitor.getVSyncPeriodInMicroseconds();
         assertTrue(reportedFramePeriod > 0);
 
         assertFalse(collector.isDone());
-
         monitor.requestUpdate();
         collector.waitTillDone();
         assertTrue(collector.isDone());
@@ -95,9 +100,10 @@
         Arrays.sort(collector.mFramePeriods, 0, collector.mFramePeriods.length);
         long medianFramePeriod = collector.mFramePeriods[collector.mFramePeriods.length / 2];
         if (Math.abs(medianFramePeriod - reportedFramePeriod) > reportedFramePeriod * .1) {
-            fail("Measured median frame period " + medianFramePeriod +
-                 " differs by more than 10% from the reported frame period " +
-                 reportedFramePeriod);
+            fail("Measured median frame period " + medianFramePeriod
+                    + " differs by more than 10% from the reported frame period "
+                    + reportedFramePeriod + " for "
+                    + (activeFrames ? "requested" : "automatically sent") + " frames");
         }
     }
 
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
index e4c1a5f..94cb245 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
@@ -11,6 +11,7 @@
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
+import android.view.KeyEvent;
 import android.view.View;
 import android.view.inputmethod.EditorInfo;
 
@@ -265,6 +266,39 @@
         waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 6, "h\nllo ", 2, 2, -1, -1);
     }
 
+    @SmallTest
+    @Feature({"TextInput", "Main"})
+    public void testEnterKeyEventWhileComposingText() throws Throwable {
+        // Focus the textarea. We need to do the following steps because we are focusing using JS.
+        DOMUtils.focusNode(this, mContentView, mCallbackContainer, "input_radio");
+        assertWaitForKeyboardStatus(false);
+        DOMUtils.focusNode(this, mContentView, mCallbackContainer, "textarea");
+        assertWaitForKeyboardStatus(false);
+        performShowImeIfNeeded();
+        assertWaitForKeyboardStatus(true);
+
+        mConnection = (TestAdapterInputConnection) getAdapterInputConnection();
+        waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1);
+
+        mConnection.setComposingText("hello", 1);
+        waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 1, "hello", 5, 5, 0, 5);
+
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mConnection.sendKeyEvent(
+                        new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
+                mConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER));
+            }
+        });
+
+        // TODO(aurimas): remove this workaround when crbug.com/278584 is fixed.
+        waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 2, "hello", 5, 5, -1, -1);
+        // The second new line is not a user visible/editable one, it is a side-effect of Blink
+        // using <br> internally.
+        waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 3, "hello\n\n", 6, 6, -1, -1);
+    }
+
     private void performShowImeIfNeeded() {
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/SelectPopupTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/SelectPopupTest.java
index 7e65a25..923162b 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/input/SelectPopupTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/input/SelectPopupTest.java
@@ -6,7 +6,6 @@
 
 import android.test.suitebuilder.annotation.LargeTest;
 
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.content.browser.ContentView;
@@ -22,8 +21,9 @@
 public class SelectPopupTest extends ContentShellTestBase {
     private static final int WAIT_TIMEOUT_SECONDS = 2;
     private static final String SELECT_URL = UrlUtils.encodeHtmlDataUri(
-            "<html><body>" +
-            "Which animal is the strongest:<br/>" +
+            "<html><head><meta name=\"viewport\"" +
+            "content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0\" /></head>" +
+            "<body>Which animal is the strongest:<br/>" +
             "<select id=\"select\">" +
             "<option>Black bear</option>" +
             "<option>Polar bear</option>" +
@@ -49,22 +49,22 @@
         }
     }
 
-    public SelectPopupTest() {
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        launchContentShellWithUrl(SELECT_URL);
+        assertTrue("Page failed to load", waitForActiveShellToBeDoneLoading());
+        // TODO(aurimas) remove this wait once crbug.com/179511 is fixed.
+        assertWaitForPageScaleFactorMatch(1);
     }
 
     /**
      * Tests that showing a select popup and having the page reload while the popup is showing does
      * not assert.
-     * @LargeTest
-     * @Feature({"Browser"})
-     * BUG 172967
      */
-    @DisabledTest
+    @LargeTest
+    @Feature({"Browser"})
     public void testReloadWhilePopupShowing() throws InterruptedException, Exception, Throwable {
-        // Load the test page.
-        launchContentShellWithUrl(SELECT_URL);
-        assertTrue("Page failed to load", waitForActiveShellToBeDoneLoading());
-
         // The popup should be hidden before the click.
         assertTrue("The select popup is shown after load.",
                 CriteriaHelper.pollForCriteria(new PopupHiddenCriteria()));
diff --git a/content/public/browser/browser_child_process_host.h b/content/public/browser/browser_child_process_host.h
index 7553478..977b1b4 100644
--- a/content/public/browser/browser_child_process_host.h
+++ b/content/public/browser/browser_child_process_host.h
@@ -61,7 +61,11 @@
   // status returned when the process exited (for posix, as returned
   // from waitpid(), for Windows, as returned from
   // GetExitCodeProcess()).  |exit_code| may be NULL.
-  virtual base::TerminationStatus GetTerminationStatus(int* exit_code) = 0;
+  // |known_dead| indicates that the child is already dead. On Linux, this
+  // information is necessary to retrieve accurate information. See
+  // ChildProcessLauncher::GetChildTerminationStatus() for more details.
+  virtual base::TerminationStatus GetTerminationStatus(
+      bool known_dead, int* exit_code) = 0;
 
   // Sets the user-visible name of the process.
   virtual void SetName(const string16& name) = 0;
diff --git a/content/public/browser/browser_plugin_guest_delegate.h b/content/public/browser/browser_plugin_guest_delegate.h
index f52e9e3..52ebc30 100644
--- a/content/public/browser/browser_plugin_guest_delegate.h
+++ b/content/public/browser/browser_plugin_guest_delegate.h
@@ -41,7 +41,8 @@
 
   virtual bool HandleKeyboardEvent(const NativeWebKeyboardEvent& event);
 
-  // Notification that a load in the guest resulted in abort.
+  // Notification that a load in the guest resulted in abort. Note that |url|
+  // may be invalid.
   virtual void LoadAbort(bool is_top_level,
                          const GURL& url,
                          const std::string& error_type) {}
diff --git a/content/public/browser/browser_ppapi_host.h b/content/public/browser/browser_ppapi_host.h
index 1ad0d17..45f997e 100644
--- a/content/public/browser/browser_ppapi_host.h
+++ b/content/public/browser/browser_ppapi_host.h
@@ -18,10 +18,6 @@
 class Sender;
 }
 
-namespace net {
-class HostResolver;
-}
-
 namespace ppapi {
 class PpapiPermissions;
 namespace host {
@@ -45,7 +41,6 @@
       ppapi::PpapiPermissions permissions,
       base::ProcessHandle plugin_child_process,
       IPC::ChannelProxy* channel,
-      net::HostResolver* host_resolver,
       int render_process_id,
       int render_view_id,
       const base::FilePath& profile_directory);
diff --git a/content/public/browser/devtools_agent_host.h b/content/public/browser/devtools_agent_host.h
index b98363e..3b0881c 100644
--- a/content/public/browser/devtools_agent_host.h
+++ b/content/public/browser/devtools_agent_host.h
@@ -37,6 +37,11 @@
   static scoped_refptr<DevToolsAgentHost> GetForWorker(int worker_process_id,
                                                        int worker_route_id);
 
+  // Returns true iff an instance of DevToolsAgentHost exists for the shared
+  // worker with given process host id and routing id.
+  static bool HasForWorker(int worker_process_id,
+                           int worker_route_id);
+
   static bool IsDebuggerAttached(WebContents* web_contents);
 
   // Returns a list of all existing RenderViewHost's that can be debugged.
diff --git a/content/public/browser/javascript_dialog_manager.h b/content/public/browser/javascript_dialog_manager.h
index 778893d..c9e1792 100644
--- a/content/public/browser/javascript_dialog_manager.h
+++ b/content/public/browser/javascript_dialog_manager.h
@@ -35,6 +35,7 @@
       JavaScriptMessageType javascript_message_type,
       const string16& message_text,
       const string16& default_prompt_text,
+      bool user_gesture,
       const DialogClosedCallback& callback,
       bool* did_suppress_message) = 0;
 
diff --git a/content/public/browser/keyboard_listener.h b/content/public/browser/keyboard_listener.h
deleted file mode 100644
index e69ab49..0000000
--- a/content/public/browser/keyboard_listener.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_PUBLIC_BROWSER_KEYBOARD_LISTENER_H_
-#define CONTENT_PUBLIC_BROWSER_KEYBOARD_LISTENER_H_
-
-#include "content/common/content_export.h"
-
-namespace content {
-
-struct NativeWebKeyboardEvent;
-
-class CONTENT_EXPORT KeyboardListener {
- public:
-  virtual bool HandleKeyPressEvent(const NativeWebKeyboardEvent& event) = 0;
-
- protected:
-  virtual ~KeyboardListener() {}
-};
-
-}  // content
-
-#endif  // CONTENT_PUBLIC_BROWSER_KEYBOARD_LISTENER_H_
diff --git a/content/public/browser/navigation_entry.h b/content/public/browser/navigation_entry.h
index e7724eb..faeecc7 100644
--- a/content/public/browser/navigation_entry.h
+++ b/content/public/browser/navigation_entry.h
@@ -198,6 +198,16 @@
   virtual bool GetExtraData(const std::string& key, string16* data) const = 0;
   // Removes the data at the specified |key|.
   virtual void ClearExtraData(const std::string& key) = 0;
+
+  // The status code of the last known successful navigation.  If
+  // GetHttpStatusCode() returns 0 that means that either:
+  //
+  //   - this navigation hasn't completed yet;
+  //   - a response wasn't received;
+  //   - or this navigation was restored and for some reason the
+  //     status code wasn't available.
+  virtual void SetHttpStatusCode(int http_status_code) = 0;
+  virtual int GetHttpStatusCode() const = 0;
 };
 
 }  // namespace content
diff --git a/content/public/browser/overscroll_configuration.h b/content/public/browser/overscroll_configuration.h
index 5335733..2d4c6df 100644
--- a/content/public/browser/overscroll_configuration.h
+++ b/content/public/browser/overscroll_configuration.h
@@ -15,7 +15,8 @@
   OVERSCROLL_CONFIG_NONE,
   OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE,
   OVERSCROLL_CONFIG_VERT_THRESHOLD_COMPLETE,
-  OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START,
+  OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD,
+  OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN,
   OVERSCROLL_CONFIG_VERT_THRESHOLD_START,
   OVERSCROLL_CONFIG_HORIZ_RESIST_AFTER,
   OVERSCROLL_CONFIG_VERT_RESIST_AFTER,
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index a5a49f2..9b6344f 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -14,6 +14,9 @@
 // The interface provides a communication conduit with a frame in the renderer.
 class CONTENT_EXPORT RenderFrameHost : public IPC::Listener,
                                        public IPC::Sender {
+ public:
+  virtual ~RenderFrameHost() {}
+
  private:
   // This interface should only be implemented inside content.
   friend class RenderFrameHostImpl;
diff --git a/content/public/browser/render_widget_host.h b/content/public/browser/render_widget_host.h
index ca727b7..a700266 100644
--- a/content/public/browser/render_widget_host.h
+++ b/content/public/browser/render_widget_host.h
@@ -7,7 +7,6 @@
 
 #include "base/callback.h"
 #include "content/common/content_export.h"
-#include "content/public/browser/keyboard_listener.h"
 #include "content/public/browser/native_web_keyboard_event.h"
 #include "ipc/ipc_channel.h"
 #include "ipc/ipc_sender.h"
@@ -29,6 +28,7 @@
 }
 
 namespace WebKit {
+class WebMouseEvent;
 struct WebScreenInfo;
 }
 
@@ -289,12 +289,18 @@
   // Access to the implementation's IPC::Listener::OnMessageReceived. Intended
   // only for test code.
 
-  // Add a keyboard listener that can handle key presses without requiring
-  // focus.
-  virtual void AddKeyboardListener(KeyboardListener* listener) = 0;
+  // Add/remove a callback that can handle key presses without requiring focus.
+  typedef base::Callback<bool(const NativeWebKeyboardEvent&)>
+      KeyPressEventCallback;
+  virtual void AddKeyPressEventCallback(
+      const KeyPressEventCallback& callback) = 0;
+  virtual void RemoveKeyPressEventCallback(
+      const KeyPressEventCallback& callback) = 0;
 
-  // Remove a keyboard listener.
-  virtual void RemoveKeyboardListener(KeyboardListener* listener) = 0;
+  // Add/remove a callback that can handle all kinds of mouse events.
+  typedef base::Callback<bool(const WebKit::WebMouseEvent&)> MouseEventCallback;
+  virtual void AddMouseEventCallback(const MouseEventCallback& callback) = 0;
+  virtual void RemoveMouseEventCallback(const MouseEventCallback& callback) = 0;
 
   // Get the screen info corresponding to this render widget.
   virtual void GetWebScreenInfo(WebKit::WebScreenInfo* result) = 0;
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index ac30001..e1e6189 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -352,7 +352,6 @@
   virtual void GenerateMHTML(
       const base::FilePath& file,
       const base::Callback<void(
-          const base::FilePath& /* path to the MHTML file */,
           int64 /* size of the file */)>& callback) = 0;
 
   // Returns true if the active NavigationEntry's page_id equals page_id.
diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc
index 47e30b2..02ca86a 100644
--- a/content/public/browser/web_contents_delegate.cc
+++ b/content/public/browser/web_contents_delegate.cc
@@ -172,4 +172,9 @@
   attached_contents_.erase(web_contents);
 }
 
+gfx::Size WebContentsDelegate::GetSizeForNewRenderView(
+    const WebContents* web_contents) const {
+  return gfx::Size();
+}
+
 }  // namespace content
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index 69ac871..0e019a9 100644
--- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h
@@ -94,9 +94,10 @@
 
   // Creates a new tab with the already-created WebContents 'new_contents'.
   // The window for the added contents should be reparented correctly when this
-  // method returns.  If |disposition| is NEW_POPUP, |pos| should hold the
-  // initial position. If |was_blocked| is non-NULL, then |*was_blocked| will
-  // be set to true if the popup gets blocked, and left unchanged otherwise.
+  // method returns.  If |disposition| is NEW_POPUP, |initial_pos| should hold
+  // the initial position. If |was_blocked| is non-NULL, then |*was_blocked|
+  // will be set to true if the popup gets blocked, and left unchanged
+  // otherwise.
   virtual void AddNewContents(WebContents* source,
                               WebContents* new_contents,
                               WindowOpenDisposition disposition,
@@ -433,6 +434,14 @@
       const base::FilePath& plugin_path,
       const base::Callback<void(bool)>& callback);
 
+  // Returns the size for the new render view created for the pending entry in
+  // |web_contents|; if there's no size, returns an empty size.
+  // This is optional for implementations of WebContentsDelegate; if the
+  // delegate doesn't provide a size, the current WebContentsView's size will be
+  // used.
+  virtual gfx::Size GetSizeForNewRenderView(
+      const WebContents* web_contents) const;
+
  protected:
   virtual ~WebContentsDelegate();
 
diff --git a/content/public/browser/web_ui_data_source.h b/content/public/browser/web_ui_data_source.h
index f3692b3..8348de3 100644
--- a/content/public/browser/web_ui_data_source.h
+++ b/content/public/browser/web_ui_data_source.h
@@ -79,9 +79,7 @@
   // pages, see URLDataSource::ShouldAddContentSecurityPolicy and talk to
   // tsepez.
 
-  // Currently only used by embedders for WebUIs with multiple instances, could
-  // have been useful for NTP as well if it wasn't implementing URLDataSource
-  // itself.
+  // Currently only used by embedders for WebUIs with multiple instances.
   virtual void DisableReplaceExistingSource() = 0;
   virtual void DisableContentSecurityPolicy() = 0;
   virtual void OverrideContentSecurityPolicyObjectSrc(
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 36f707f..2521315 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -41,6 +41,12 @@
 // Blacklist the GPU for WebGL.
 const char kBlacklistWebGL[]                = "blacklist-webgl";
 
+// Block cross-site documents (i.e., HTML/XML/JSON) from being loaded in
+// subresources when a document is not supposed to read them.  This will later
+// allow us to block them from the entire renderer process when site isolation
+// is enabled.
+const char kBlockCrossSiteDocuments[]     = "block-cross-site-documents";
+
 // Causes the browser process to throw an assertion on startup.
 const char kBrowserAssertTest[]             = "assert-test";
 
@@ -408,6 +414,9 @@
 // Enables support for inband text tracks in media content.
 const char kEnableInbandTextTracks[]        = "enable-inband-text-tracks";
 
+// Enable inputmode attribute of HTML input or text element.
+extern const char kEnableInputModeAttribute[] = "enable-input-mode-attribute";
+
 // Force logging to be enabled.  Logging is disabled by default in release
 // builds.
 const char kEnableLogging[]                 = "enable-logging";
@@ -631,6 +640,10 @@
 // Disables the sandbox for all process types that are normally sandboxed.
 const char kNoSandbox[]                     = "no-sandbox";
 
+// Enables not sending touch events to renderer while scrolling.
+const char kNoTouchToRendererWhileScrolling[] =
+    "no-touch-to-renderer-while-scrolling";
+
 // Enables or disables history navigation in response to horizontal overscroll.
 // Set the value to '1' to enable the feature, and set to '0' to disable.
 // Defaults to enabled.
@@ -780,10 +793,6 @@
 // Runs the security test for the renderer sandbox.
 const char kTestSandbox[]                   = "test-sandbox";
 
-// Enables not sending touch events to renderer while scrolling.
-const char kNoTouchToRendererWhileScrolling[] =
-    "no-touch-to-renderer-while-scrolling";
-
 // Causes TRACE_EVENT flags to be recorded from startup. Optionally, can
 // specify the specific trace categories to include (e.g.
 // --trace-startup=base,net) otherwise, all events are recorded. Setting this
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index f2a0829..b83a288 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -23,6 +23,7 @@
 extern const char kAuditHandles[];
 CONTENT_EXPORT extern const char kBlacklistAcceleratedCompositing[];
 CONTENT_EXPORT extern const char kBlacklistWebGL[];
+CONTENT_EXPORT extern const char kBlockCrossSiteDocuments[];
 CONTENT_EXPORT extern const char kBrowserAssertTest[];
 CONTENT_EXPORT extern const char kBrowserCrashTest[];
 CONTENT_EXPORT extern const char kBrowserSubprocessPath[];
@@ -93,7 +94,7 @@
 CONTENT_EXPORT extern const char kDisableThreadedHTMLParser[];
 CONTENT_EXPORT extern const char kDisableWebAudio[];
 extern const char kDisableWebKitMediaSource[];
-extern const char kDisableWebSecurity[];
+CONTENT_EXPORT extern const char kDisableWebSecurity[];
 extern const char kDisableXSSAuditor[];
 CONTENT_EXPORT extern const char kDomAutomationController[];
 CONTENT_EXPORT extern const char kEnableAcceleratedFilters[];
@@ -126,6 +127,7 @@
 CONTENT_EXPORT extern const char kEnableHighDpiCompositingForFixedPosition[];
 extern const char kEnableHTMLImports[];
 CONTENT_EXPORT extern const char kEnableInbandTextTracks[];
+extern const char kEnableInputModeAttribute[];
 CONTENT_EXPORT extern const char kEnableLogging[];
 extern const char kEnableMemoryBenchmarking[];
 extern const char kEnableMonitorProfile[];
@@ -188,6 +190,7 @@
 CONTENT_EXPORT extern const char kMuteAudio[];
 CONTENT_EXPORT extern const char kNoReferrers[];
 CONTENT_EXPORT extern const char kNoSandbox[];
+CONTENT_EXPORT extern const char kNoTouchToRendererWhileScrolling[];
 CONTENT_EXPORT extern const char kOverscrollHistoryNavigation[];
 extern const char kPluginLauncher[];
 CONTENT_EXPORT extern const char kPluginPath[];
@@ -224,7 +227,6 @@
 CONTENT_EXPORT extern const char kTestingFixedHttpPort[];
 CONTENT_EXPORT extern const char kTestingFixedHttpsPort[];
 CONTENT_EXPORT extern const char kTestSandbox[];
-CONTENT_EXPORT extern const char kNoTouchToRendererWhileScrolling[];
 extern const char kTraceStartup[];
 extern const char kTraceStartupDuration[];
 extern const char kTraceStartupFile[];
diff --git a/content/public/common/context_menu_params.cc b/content/public/common/context_menu_params.cc
index 176a429..5ec5a1f 100644
--- a/content/public/common/context_menu_params.cc
+++ b/content/public/common/context_menu_params.cc
@@ -18,7 +18,7 @@
     : media_type(WebKit::WebContextMenuData::MediaTypeNone),
       x(0),
       y(0),
-      is_image_blocked(false),
+      has_image_contents(true),
       frame_id(0),
       media_flags(0),
       misspelling_hash(0),
diff --git a/content/public/common/context_menu_params.h b/content/public/common/context_menu_params.h
index f58e9de..053d5ad 100644
--- a/content/public/common/context_menu_params.h
+++ b/content/public/common/context_menu_params.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_PUBLIC_COMMON_CONTEXT_MENU_PARAMS_H_
 #define CONTENT_PUBLIC_COMMON_CONTEXT_MENU_PARAMS_H_
 
+#include <string>
 #include <vector>
 
 #include "base/basictypes.h"
@@ -72,8 +73,9 @@
   // video.
   GURL src_url;
 
-  // This is true if the context menu was invoked on a blocked image.
-  bool is_image_blocked;
+  // This is true if the context menu was invoked on an image which has
+  // non-empty contents.
+  bool has_image_contents;
 
   // This is the URL of the top level page that the context menu was invoked
   // on.
@@ -154,7 +156,6 @@
   gfx::Point selection_start;
   gfx::Point selection_end;
 #endif
-
 };
 
 }  // namespace content
diff --git a/content/public/common/renderer_preferences.h b/content/public/common/renderer_preferences.h
index bc5447a..0f141d2 100644
--- a/content/public/common/renderer_preferences.h
+++ b/content/public/common/renderer_preferences.h
@@ -40,8 +40,9 @@
 enum TapMultipleTargetsStrategy {
   TAP_MULTIPLE_TARGETS_STRATEGY_ZOOM = 0,
   TAP_MULTIPLE_TARGETS_STRATEGY_POPUP,
+  TAP_MULTIPLE_TARGETS_STRATEGY_NONE,
 
-  TAP_MULTIPLE_TARGETS_STRATEGY_MAX = TAP_MULTIPLE_TARGETS_STRATEGY_POPUP,
+  TAP_MULTIPLE_TARGETS_STRATEGY_MAX = TAP_MULTIPLE_TARGETS_STRATEGY_NONE,
 };
 
 struct CONTENT_EXPORT RendererPreferences {
diff --git a/content/public/common/url_constants.cc b/content/public/common/url_constants.cc
index e380faa..e1d9c01 100644
--- a/content/public/common/url_constants.cc
+++ b/content/public/common/url_constants.cc
@@ -20,11 +20,11 @@
 const char kFtpScheme[] = "ftp";
 const char kGuestScheme[] = "chrome-guest";
 const char kHttpScheme[] = "http";
-const char kHttpsScheme[] = "https";
 }  // namespace chrome
 
 namespace content {
 
+const char kHttpsScheme[] = "https";
 const char kJavaScriptScheme[] = "javascript";
 const char kMailToScheme[] = "mailto";
 const char kMetadataScheme[] = "metadata";
diff --git a/content/public/common/url_constants.h b/content/public/common/url_constants.h
index 68a7ac4..b6d4418 100644
--- a/content/public/common/url_constants.h
+++ b/content/public/common/url_constants.h
@@ -26,11 +26,11 @@
 CONTENT_EXPORT extern const char kFtpScheme[];
 CONTENT_EXPORT extern const char kGuestScheme[];
 CONTENT_EXPORT extern const char kHttpScheme[];
-CONTENT_EXPORT extern const char kHttpsScheme[];
 }  // namespace chrome
 
 namespace content {
 
+CONTENT_EXPORT extern const char kHttpsScheme[];
 CONTENT_EXPORT extern const char kJavaScriptScheme[];
 CONTENT_EXPORT extern const char kMailToScheme[];
 CONTENT_EXPORT extern const char kMetadataScheme[];
diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h
index f3ecf7d..4eea8a3 100644
--- a/content/public/renderer/content_renderer_client.h
+++ b/content/public/renderer/content_renderer_client.h
@@ -247,9 +247,7 @@
   // Returns true if we should report a detailed message (including a stack
   // trace) for console [logs|errors|exceptions]. |source| is the WebKit-
   // reported source for the error; this can point to a page or a script,
-  // and can be external (e.g., "http://www.google.com"), extension-related
-  // (e.g., "chrome-extension://<extension_id>/background.js"), or internal
-  // (e.g., "event_bindings" or "schemaUtils").
+  // and can be external or internal.
   virtual bool ShouldReportDetailedMessageForSource(
       const base::string16& source) const;
 };
diff --git a/content/public/renderer/renderer_ppapi_host.h b/content/public/renderer/renderer_ppapi_host.h
index 2922ff3..1275429 100644
--- a/content/public/renderer/renderer_ppapi_host.h
+++ b/content/public/renderer/renderer_ppapi_host.h
@@ -114,10 +114,10 @@
   // There are times when the renderer needs to create a ResourceHost in the
   // browser. This function does so asynchronously. |nested_msgs| is a list of
   // resource host creation messages and |instance| is the PP_Instance which
-  // the resource will belong to. |callback| will be called with the pending
-  // host IDs when the ResourceHosts have been created. This can be passed back
-  // to the plugin to attach to the ResourceHosts. Pending IDs of 0 will be
-  // passed to the callback if a ResourceHost fails to be created.
+  // the resource will belong to. |callback| will be called asynchronously with
+  // the pending host IDs when the ResourceHosts have been created. This can be
+  // passed back to the plugin to attach to the ResourceHosts. Pending IDs of 0
+  // will be passed to the callback if a ResourceHost fails to be created.
   virtual void CreateBrowserResourceHosts(
       PP_Instance instance,
       const std::vector<IPC::Message>& nested_msgs,
diff --git a/content/public/test/browser_test_base.cc b/content/public/test/browser_test_base.cc
index 9a0f317..f116656 100644
--- a/content/public/test/browser_test_base.cc
+++ b/content/public/test/browser_test_base.cc
@@ -52,7 +52,7 @@
     logging::RawLog(logging::LOG_ERROR,
                     "BrowserTestBase signal handler received SIGTERM. "
                     "Backtrace:\n");
-    base::debug::StackTrace().PrintBacktrace();
+    base::debug::StackTrace().Print();
   }
   _exit(128 + signal);
 }
diff --git a/content/public/test/content_test_suite_base.cc b/content/public/test/content_test_suite_base.cc
index f245683..ce6eac3 100644
--- a/content/public/test/content_test_suite_base.cc
+++ b/content/public/test/content_test_suite_base.cc
@@ -19,7 +19,6 @@
 #include "content/public/common/content_paths.h"
 #include "content/renderer/renderer_main_thread.h"
 #include "content/utility/utility_main_thread.h"
-#include "media/base/media.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/ui_base_paths.h"
 
@@ -34,6 +33,10 @@
 #include "ui/shell_dialogs/android/shell_dialogs_jni_registrar.h"
 #endif
 
+#if !defined(OS_IOS)
+#include "media/base/media.h"
+#endif
+
 namespace content {
 
 class ContentTestSuiteBaseListener : public testing::EmptyTestEventListener {
@@ -71,10 +74,9 @@
   RenderProcessHost::RegisterRendererMainThreadFactory(
       CreateRendererMainThread);
   GpuProcessHost::RegisterGpuMainThreadFactory(CreateGpuMainThread);
-#endif
-
   if (external_libraries_enabled_)
     media::InitializeMediaLibraryForTesting();
+#endif
 
   scoped_ptr<ContentClient> client_for_init(CreateClientForInitialization());
   SetContentClient(client_for_init.get());
diff --git a/content/public/test/test_file_error_injector.h b/content/public/test/test_file_error_injector.h
index c056780..306c7e2 100644
--- a/content/public/test/test_file_error_injector.h
+++ b/content/public/test/test_file_error_injector.h
@@ -13,8 +13,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "content/public/browser/download_interrupt_reasons.h"
-
-class GURL;
+#include "url/gurl.h"
 
 namespace content {
 
diff --git a/content/public/test/test_launcher.cc b/content/public/test/test_launcher.cc
index c29a239..fe18fc0 100644
--- a/content/public/test/test_launcher.cc
+++ b/content/public/test/test_launcher.cc
@@ -184,7 +184,8 @@
       const testing::TestCase* test_case,
       const testing::TestInfo* test_info,
       const base::TestLauncherDelegate::TestResultCallback& callback) OVERRIDE;
-  virtual void RunRemainingTests() OVERRIDE;
+  virtual void RunRemainingTests(
+      const RunRemainingTestsCallback& callback) OVERRIDE;
 
  private:
   content::TestLauncherDelegate* launcher_delegate_;
@@ -250,8 +251,10 @@
   callback.Run(result);
 }
 
-void WrapperTestLauncherDelegate::RunRemainingTests() {
-  // No need to do anything here, we launch tests synchronously.
+void WrapperTestLauncherDelegate::RunRemainingTests(
+    const RunRemainingTestsCallback& callback) {
+  // No need to do anything else here, we launch tests synchronously.
+  callback.Run();
 }
 
 }  // namespace
diff --git a/content/public/utility/utility_thread.cc b/content/public/utility/utility_thread.cc
index bc4bd30..87bd71e 100644
--- a/content/public/utility/utility_thread.cc
+++ b/content/public/utility/utility_thread.cc
@@ -11,8 +11,8 @@
 
 // Keep the global UtilityThread in a TLS slot so it is impossible to access
 // incorrectly from the wrong thread.
-static base::LazyInstance<base::ThreadLocalPointer<UtilityThread> > lazy_tls =
-    LAZY_INSTANCE_INITIALIZER;
+static base::LazyInstance<base::ThreadLocalPointer<UtilityThread> >::Leaky
+    lazy_tls = LAZY_INSTANCE_INITIALIZER;
 
 UtilityThread* UtilityThread::Get() {
   return lazy_tls.Pointer()->Get();
diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc
index 23537a5..a116f01 100644
--- a/content/renderer/browser_plugin/browser_plugin.cc
+++ b/content/renderer/browser_plugin/browser_plugin.cc
@@ -76,11 +76,11 @@
       render_view_routing_id_(render_view->GetRoutingID()),
       container_(NULL),
       damage_buffer_sequence_id_(0),
-      resize_ack_received_(true),
+      paint_ack_received_(true),
       last_device_scale_factor_(1.0f),
       sad_guest_(NULL),
       guest_crashed_(false),
-      auto_size_ack_pending_(false),
+      is_auto_size_state_dirty_(false),
       persist_storage_(false),
       valid_partition_id_(true),
       content_window_routing_id_(MSG_ROUTING_NONE),
@@ -295,35 +295,49 @@
 }
 
 void BrowserPlugin::ParseAutoSizeAttribute() {
-  auto_size_ack_pending_ = true;
   last_view_size_ = plugin_rect_.size();
   UpdateGuestAutoSizeState(GetAutoSizeAttribute());
 }
 
 void BrowserPlugin::PopulateAutoSizeParameters(
-    BrowserPluginHostMsg_AutoSize_Params* params, bool current_auto_size) {
-  params->enable = current_auto_size;
+    BrowserPluginHostMsg_AutoSize_Params* params, bool auto_size_enabled) {
+  params->enable = auto_size_enabled;
   // No need to populate the params if autosize is off.
-  if (current_auto_size) {
+  if (auto_size_enabled) {
     params->max_size = gfx::Size(GetAdjustedMaxWidth(), GetAdjustedMaxHeight());
     params->min_size = gfx::Size(GetAdjustedMinWidth(), GetAdjustedMinHeight());
+
+    if (max_auto_size_ != params->max_size)
+      is_auto_size_state_dirty_ = true;
+
+    max_auto_size_ = params->max_size;
+  } else {
+    max_auto_size_ = gfx::Size();
   }
 }
 
-void BrowserPlugin::UpdateGuestAutoSizeState(bool current_auto_size) {
+void BrowserPlugin::UpdateGuestAutoSizeState(bool auto_size_enabled) {
   // If we haven't yet heard back from the guest about the last resize request,
   // then we don't issue another request until we do in
   // BrowserPlugin::UpdateRect.
-  if (!HasGuestInstanceID() || !resize_ack_received_)
+  if (!HasGuestInstanceID())
     return;
+
+  is_auto_size_state_dirty_ = true;
+
+  if (!paint_ack_received_)
+    return;
+
   BrowserPluginHostMsg_AutoSize_Params auto_size_params;
   BrowserPluginHostMsg_ResizeGuest_Params resize_guest_params;
-  if (current_auto_size) {
-    GetDamageBufferWithSizeParams(&auto_size_params, &resize_guest_params);
+  if (auto_size_enabled) {
+    GetDamageBufferWithSizeParams(&auto_size_params,
+                                  &resize_guest_params,
+                                  true);
   } else {
-    GetDamageBufferWithSizeParams(NULL, &resize_guest_params);
+    GetDamageBufferWithSizeParams(NULL, &resize_guest_params, true);
   }
-  resize_ack_received_ = false;
+  paint_ack_received_ = false;
   browser_plugin_manager()->Send(
       new BrowserPluginHostMsg_SetAutoSize(render_view_routing_id_,
                                            guest_instance_id_,
@@ -365,7 +379,8 @@
   attach_params.persist_storage = persist_storage_;
   attach_params.src = GetSrcAttribute();
   GetDamageBufferWithSizeParams(&attach_params.auto_size_params,
-                                &attach_params.resize_guest_params);
+                                &attach_params.resize_guest_params,
+                                false);
 
   browser_plugin_manager()->Send(
       new BrowserPluginHostMsg_Attach(render_view_routing_id_,
@@ -498,19 +513,20 @@
 
   bool auto_size = GetAutoSizeAttribute();
   // We receive a resize ACK in regular mode, but not in autosize.
-  // In SW, |resize_ack_received_| is reset in SwapDamageBuffers().
+  // In SW, |paint_ack_received_| is reset in SwapDamageBuffers().
   // In HW mode, we need to do it here so we can continue sending
   // resize messages when needed.
   if (params.is_resize_ack ||
-      (!params.needs_ack && (auto_size || auto_size_ack_pending_))) {
-    resize_ack_received_ = true;
+      (!params.needs_ack && (auto_size || is_auto_size_state_dirty_))) {
+    paint_ack_received_ = true;
   }
 
-  auto_size_ack_pending_ = false;
+  bool was_auto_size_state_dirty = auto_size && is_auto_size_state_dirty_;
+  is_auto_size_state_dirty_ = false;
 
   if ((!auto_size && (width() != params.view_size.width() ||
                       height() != params.view_size.height())) ||
-      (auto_size && (!InAutoSizeBounds(params.view_size))) ||
+      (auto_size && was_auto_size_state_dirty) ||
       GetDeviceScaleFactor() != params.scale_factor) {
     // We are HW accelerated, render widget does not expect an ack,
     // but we still need to update the size.
@@ -519,7 +535,7 @@
       return;
     }
 
-    if (!resize_ack_received_) {
+    if (!paint_ack_received_) {
       // The guest has not yet responded to the last resize request, and
       // so we don't want to do anything at this point other than ACK the guest.
       if (auto_size)
@@ -530,9 +546,12 @@
       // container size.
       if (auto_size) {
         GetDamageBufferWithSizeParams(&auto_size_params,
-                                      &resize_guest_params);
+                                      &resize_guest_params,
+                                      was_auto_size_state_dirty);
       } else {
-        GetDamageBufferWithSizeParams(NULL, &resize_guest_params);
+        GetDamageBufferWithSizeParams(NULL,
+                                      &resize_guest_params,
+                                      was_auto_size_state_dirty);
       }
     }
     browser_plugin_manager()->Send(new BrowserPluginHostMsg_UpdateRect_ACK(
@@ -747,11 +766,11 @@
 }
 
 void BrowserPlugin::UpdateDeviceScaleFactor(float device_scale_factor) {
-  if (last_device_scale_factor_ == device_scale_factor || !resize_ack_received_)
+  if (last_device_scale_factor_ == device_scale_factor || !paint_ack_received_)
     return;
 
   BrowserPluginHostMsg_ResizeGuest_Params params;
-  PopulateResizeGuestParameters(&params, plugin_rect());
+  PopulateResizeGuestParameters(&params, plugin_rect(), false);
   browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
       render_view_routing_id_,
       guest_instance_id_,
@@ -860,11 +879,12 @@
     // We're switching back to the software path. We create a new damage
     // buffer that can accommodate the current size of the container.
     BrowserPluginHostMsg_ResizeGuest_Params params;
-    PopulateResizeGuestParameters(&params, plugin_rect());
     // Request a full repaint from the guest even if its size is not actually
     // changing.
-    params.repaint = true;
-    resize_ack_received_ = false;
+    PopulateResizeGuestParameters(&params,
+                                  plugin_rect(),
+                                  true /* needs_repaint */);
+    paint_ack_received_ = false;
     browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
         render_view_routing_id_,
         guest_instance_id_,
@@ -1008,12 +1028,12 @@
     return;
 
   // In AutoSize mode, guests don't care when the BrowserPlugin container is
-  // resized. If |!resize_ack_received_|, then we are still waiting on a
+  // resized. If |!paint_ack_received_|, then we are still waiting on a
   // previous resize to be ACK'ed and so we don't issue additional resizes
   // until the previous one is ACK'ed.
   // TODO(mthiesse): Assess the performance of calling GetAutoSizeAttribute() on
   // resize.
-  if (!resize_ack_received_ ||
+  if (!paint_ack_received_ ||
       (old_width == window_rect.width && old_height == window_rect.height) ||
       GetAutoSizeAttribute()) {
     // Let the browser know about the updated view rect.
@@ -1023,8 +1043,8 @@
   }
 
   BrowserPluginHostMsg_ResizeGuest_Params params;
-  PopulateResizeGuestParameters(&params, plugin_rect());
-  resize_ack_received_ = false;
+  PopulateResizeGuestParameters(&params, plugin_rect(), false);
+  paint_ack_received_ = false;
   browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
       render_view_routing_id_,
       guest_instance_id_,
@@ -1033,14 +1053,16 @@
 
 void BrowserPlugin::SwapDamageBuffers() {
   current_damage_buffer_.reset(pending_damage_buffer_.release());
-  resize_ack_received_ = true;
+  paint_ack_received_ = true;
 }
 
 void BrowserPlugin::PopulateResizeGuestParameters(
     BrowserPluginHostMsg_ResizeGuest_Params* params,
-    const gfx::Rect& view_rect) {
+    const gfx::Rect& view_rect,
+    bool needs_repaint) {
   params->size_changed = true;
   params->view_rect = view_rect;
+  params->repaint = needs_repaint;
   params->scale_factor = GetDeviceScaleFactor();
   if (last_device_scale_factor_ != params->scale_factor){
     params->repaint = true;
@@ -1071,16 +1093,20 @@
 
 void BrowserPlugin::GetDamageBufferWithSizeParams(
     BrowserPluginHostMsg_AutoSize_Params* auto_size_params,
-    BrowserPluginHostMsg_ResizeGuest_Params* resize_guest_params) {
-  if (auto_size_params)
+    BrowserPluginHostMsg_ResizeGuest_Params* resize_guest_params,
+    bool needs_repaint) {
+  if (auto_size_params) {
     PopulateAutoSizeParameters(auto_size_params, GetAutoSizeAttribute());
+  } else {
+    max_auto_size_ = gfx::Size();
+  }
   gfx::Size view_size = (auto_size_params && auto_size_params->enable) ?
       auto_size_params->max_size : gfx::Size(width(), height());
   if (view_size.IsEmpty())
     return;
-  resize_ack_received_ = false;
+  paint_ack_received_ = false;
   gfx::Rect view_rect = gfx::Rect(plugin_rect_.origin(), view_size);
-  PopulateResizeGuestParameters(resize_guest_params, view_rect);
+  PopulateResizeGuestParameters(resize_guest_params, view_rect, needs_repaint);
 }
 
 #if defined(OS_POSIX)
diff --git a/content/renderer/browser_plugin/browser_plugin.h b/content/renderer/browser_plugin/browser_plugin.h
index 001f2d7..ba30ac9 100644
--- a/content/renderer/browser_plugin/browser_plugin.h
+++ b/content/renderer/browser_plugin/browser_plugin.h
@@ -252,20 +252,22 @@
   // allocates a new |pending_damage_buffer_| if in software rendering mode.
   void PopulateResizeGuestParameters(
       BrowserPluginHostMsg_ResizeGuest_Params* params,
-      const gfx::Rect& view_size);
+      const gfx::Rect& view_size,
+      bool needs_repaint);
 
   // Populates BrowserPluginHostMsg_AutoSize_Params object with autosize state.
   void PopulateAutoSizeParameters(
-      BrowserPluginHostMsg_AutoSize_Params* params, bool current_auto_size);
+      BrowserPluginHostMsg_AutoSize_Params* params, bool auto_size_enabled);
 
   // Populates both AutoSize and ResizeGuest parameters based on the current
   // autosize state.
   void GetDamageBufferWithSizeParams(
       BrowserPluginHostMsg_AutoSize_Params* auto_size_params,
-      BrowserPluginHostMsg_ResizeGuest_Params* resize_guest_params);
+      BrowserPluginHostMsg_ResizeGuest_Params* resize_guest_params,
+      bool needs_repaint);
 
   // Informs the guest of an updated autosize state.
-  void UpdateGuestAutoSizeState(bool current_auto_size);
+  void UpdateGuestAutoSizeState(bool auto_size_enabled);
 
   // Indicates whether a damage buffer was used by the guest process for the
   // provided |params|.
@@ -312,14 +314,16 @@
   scoped_ptr<base::SharedMemory> current_damage_buffer_;
   scoped_ptr<base::SharedMemory> pending_damage_buffer_;
   uint32 damage_buffer_sequence_id_;
-  bool resize_ack_received_;
+  bool paint_ack_received_;
   gfx::Rect plugin_rect_;
   float last_device_scale_factor_;
   // Bitmap for crashed plugin. Lazily initialized, non-owning pointer.
   SkBitmap* sad_guest_;
   bool guest_crashed_;
   scoped_ptr<BrowserPluginHostMsg_ResizeGuest_Params> pending_resize_params_;
-  bool auto_size_ack_pending_;
+  bool is_auto_size_state_dirty_;
+  // Maximum size constraint for autosize.
+  gfx::Size max_auto_size_;
   std::string storage_partition_id_;
   bool persist_storage_;
   bool valid_partition_id_;
diff --git a/content/renderer/browser_plugin/browser_plugin_bindings.cc b/content/renderer/browser_plugin/browser_plugin_bindings.cc
index bc20ea4..5dd5231 100644
--- a/content/renderer/browser_plugin/browser_plugin_bindings.cc
+++ b/content/renderer/browser_plugin/browser_plugin_bindings.cc
@@ -588,39 +588,29 @@
     // src property to the empty string. Instead, we want to simply restore
     // the src attribute back to its old value.
     if (new_value.empty()) {
-      RemoveProperty(bindings, np_obj);
       return true;
     }
     std::string old_value = bindings->instance()->GetSrcAttribute();
-    bool guest_crashed = bindings->instance()->guest_crashed();
-    if ((old_value != new_value) || guest_crashed) {
-      // If the new value was empty then we're effectively resetting the
-      // attribute to the old value here. This will be picked up by <webview>'s
-      // mutation observer and will restore the src attribute after it has been
-      // removed.
-      UpdateDOMAttribute(bindings, new_value);
-      std::string error_message;
-      if (!bindings->instance()->ParseSrcAttribute(&error_message)) {
-        // Reset to old value on error.
-        UpdateDOMAttribute(bindings, old_value);
-        // Exceptions must be set as the last operation before returning to
-        // script.
-        WebBindings::setException(
-            np_obj, static_cast<const NPUTF8 *>(error_message.c_str()));
-        return false;
-      }
+    // If the new value was empty then we're effectively resetting the
+    // attribute to the old value here. This will be picked up by <webview>'s
+    // mutation observer and will restore the src attribute after it has been
+    // removed.
+    UpdateDOMAttribute(bindings, new_value);
+    std::string error_message;
+    if (!bindings->instance()->ParseSrcAttribute(&error_message)) {
+      // Reset to old value on error.
+      UpdateDOMAttribute(bindings, old_value);
+      // Exceptions must be set as the last operation before returning to
+      // script.
+      WebBindings::setException(
+          np_obj, static_cast<const NPUTF8 *>(error_message.c_str()));
+      return false;
     }
     return true;
   }
   virtual void RemoveProperty(BrowserPluginBindings* bindings,
                               NPObject* np_obj) OVERRIDE {
-    std::string old_value = bindings->instance()->GetSrcAttribute();
-    if (old_value.empty())
-      return;
-    // Remove the DOM attribute to trigger the mutation observer when it is
-    // restored to its original value again.
     bindings->instance()->RemoveDOMAttribute(name());
-    UpdateDOMAttribute(bindings, old_value);
   }
  private:
   DISALLOW_COPY_AND_ASSIGN(BrowserPluginPropertyBindingSrc);
diff --git a/content/renderer/context_menu_params_builder.cc b/content/renderer/context_menu_params_builder.cc
index 8459fa2..e8cd078 100644
--- a/content/renderer/context_menu_params_builder.cc
+++ b/content/renderer/context_menu_params_builder.cc
@@ -24,7 +24,7 @@
   params.link_url = data.linkURL;
   params.unfiltered_link_url = data.linkURL;
   params.src_url = data.srcURL;
-  params.is_image_blocked = data.isImageBlocked;
+  params.has_image_contents = data.hasImageContents;
   params.page_url = data.pageURL;
   params.keyword_url = data.keywordURL;
   params.frame_url = data.frameURL;
diff --git a/content/renderer/gpu/compositor_output_surface.cc b/content/renderer/gpu/compositor_output_surface.cc
index e44f832..f5b66e0 100644
--- a/content/renderer/gpu/compositor_output_surface.cc
+++ b/content/renderer/gpu/compositor_output_surface.cc
@@ -69,7 +69,7 @@
   DetachFromThread();
   message_sender_ = RenderThreadImpl::current()->sync_message_filter();
   DCHECK(message_sender_.get());
-  if (software_device)
+  if (OutputSurface::software_device())
     capabilities_.max_frames_pending = 1;
 }
 
diff --git a/content/renderer/gpu/compositor_software_output_device.cc b/content/renderer/gpu/compositor_software_output_device.cc
index 948e3e7..6132590 100644
--- a/content/renderer/gpu/compositor_software_output_device.cc
+++ b/content/renderer/gpu/compositor_software_output_device.cc
@@ -7,8 +7,8 @@
 #include "base/logging.h"
 #include "cc/output/software_frame_data.h"
 #include "content/renderer/render_process.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
 #include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkDevice.h"
 #include "third_party/skia/include/core/SkPixelRef.h"
 #include "third_party/skia/include/core/SkRegion.h"
 #include "ui/gfx/skia_util.h"
@@ -147,7 +147,7 @@
                     viewport_size_.width(),
                     viewport_size_.height());
   bitmap_.setPixels(current->memory());
-  device_ = skia::AdoptRef(new SkDevice(bitmap_));
+  device_ = skia::AdoptRef(new SkBitmapDevice(bitmap_));
   canvas_ = skia::AdoptRef(new SkCanvas(device_.get()));
 
   if (!previous) {
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc
index e7a7e3e..bb7fd1c 100644
--- a/content/renderer/gpu/render_widget_compositor.cc
+++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -260,13 +260,13 @@
   settings.strict_layer_property_change_checking =
       cmd->HasSwitch(cc::switches::kStrictLayerPropertyChangeChecking);
 
-  settings.use_map_image = cmd->HasSwitch(cc::switches::kUseMapImage);
+  settings.use_map_image = cc::switches::IsMapImageEnabled();
 
 #if defined(OS_ANDROID)
   // TODO(danakj): Move these to the android code.
   settings.can_use_lcd_text = false;
   settings.max_partial_texture_updates = 0;
-  settings.use_linear_fade_scrollbar_animator = true;
+  settings.scrollbar_animator = cc::LayerTreeSettings::LinearFade;
   settings.solid_color_scrollbars = true;
   settings.solid_color_scrollbar_color =
       cmd->HasSwitch(switches::kHideScrollbars)
@@ -279,10 +279,10 @@
       widget->UsingSynchronousRendererCompositor();
 #elif !defined(OS_MACOSX)
   if (cmd->HasSwitch(switches::kEnableOverlayScrollbars)) {
-    settings.use_linear_fade_scrollbar_animator = true;
+    settings.scrollbar_animator = cc::LayerTreeSettings::LinearFade;
     settings.solid_color_scrollbars = true;
     settings.solid_color_scrollbar_color = SkColorSetARGB(128, 128, 128, 128);
-    settings.solid_color_scrollbar_thickness_dip = 3;
+    settings.solid_color_scrollbar_thickness_dip = 7;
   }
 #endif
 
diff --git a/content/renderer/media/android/audio_decoder_android.cc b/content/renderer/media/android/audio_decoder_android.cc
index 4194431..afc6dec 100644
--- a/content/renderer/media/android/audio_decoder_android.cc
+++ b/content/renderer/media/android/audio_decoder_android.cc
@@ -422,6 +422,11 @@
       ++decoded_frames;
     }
   }
+
+  // number_of_frames is only an estimate.  Resize the buffer with the
+  // actual number of received frames.
+  if (decoded_frames < number_of_frames)
+    destination_bus->resizeSmaller(decoded_frames);
 }
 
 // The number of frames is unknown, so keep reading and buffering
@@ -466,6 +471,11 @@
     }
     ++decoded_frames;
   }
+
+  // number_of_frames is only an estimate.  Resize the buffer with the
+  // actual number of received frames.
+  if (decoded_frames < number_of_frames)
+    destination_bus->resizeSmaller(decoded_frames);
 }
 
 static bool TryWAVEFileDecoder(WebKit::WebAudioBus* destination_bus,
diff --git a/content/renderer/media/android/media_source_delegate.cc b/content/renderer/media/android/media_source_delegate.cc
index 5600285..6271b71 100644
--- a/content/renderer/media/android/media_source_delegate.cc
+++ b/content/renderer/media/android/media_source_delegate.cc
@@ -16,12 +16,11 @@
 #include "media/filters/chunk_demuxer.h"
 #include "media/filters/decrypting_demuxer_stream.h"
 #include "third_party/WebKit/public/platform/WebString.h"
-#include "third_party/WebKit/public/web/WebMediaSource.h"
 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
 
 using media::DemuxerStream;
-using media::MediaPlayerHostMsg_DemuxerReady_Params;
-using media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params;
+using media::DemuxerConfigs;
+using media::DemuxerData;
 using WebKit::WebMediaPlayer;
 using WebKit::WebString;
 
@@ -309,29 +308,27 @@
   DCHECK(type == DemuxerStream::AUDIO || type == DemuxerStream::VIDEO);
   // The access unit size should have been initialized properly at this stage.
   DCHECK_GT(access_unit_size_, 0u);
-  scoped_ptr<MediaPlayerHostMsg_ReadFromDemuxerAck_Params> params(
-      new MediaPlayerHostMsg_ReadFromDemuxerAck_Params());
-  params->type = type;
-  params->access_units.resize(access_unit_size_);
-  ReadFromDemuxerStream(type, params.Pass(), 0);
+  scoped_ptr<DemuxerData> data(new DemuxerData());
+  data->type = type;
+  data->access_units.resize(access_unit_size_);
+  ReadFromDemuxerStream(type, data.Pass(), 0);
 }
 
-void MediaSourceDelegate::ReadFromDemuxerStream(
-    media::DemuxerStream::Type type,
-    scoped_ptr<MediaPlayerHostMsg_ReadFromDemuxerAck_Params> params,
-    size_t index) {
+void MediaSourceDelegate::ReadFromDemuxerStream(media::DemuxerStream::Type type,
+                                                scoped_ptr<DemuxerData> data,
+                                                size_t index) {
   DCHECK_BELONG_TO_MEDIA_LOOP();
   // DemuxerStream::Read() always returns the read callback asynchronously.
   DemuxerStream* stream =
       (type == DemuxerStream::AUDIO) ? audio_stream_ : video_stream_;
   stream->Read(base::Bind(
       &MediaSourceDelegate::OnBufferReady,
-      media_weak_this_.GetWeakPtr(), type, base::Passed(&params), index));
+      media_weak_this_.GetWeakPtr(), type, base::Passed(&data), index));
 }
 
 void MediaSourceDelegate::OnBufferReady(
     media::DemuxerStream::Type type,
-    scoped_ptr<MediaPlayerHostMsg_ReadFromDemuxerAck_Params> params,
+    scoped_ptr<DemuxerData> data,
     size_t index,
     DemuxerStream::Status status,
     const scoped_refptr<media::DecoderBuffer>& buffer) {
@@ -351,10 +348,10 @@
 
   bool is_audio = (type == DemuxerStream::AUDIO);
   if (status != DemuxerStream::kAborted &&
-      index >= params->access_units.size()) {
+      index >= data->access_units.size()) {
     LOG(ERROR) << "The internal state inconsistency onBufferReady: "
                << (is_audio ? "Audio" : "Video") << ", index " << index
-               <<", size " << params->access_units.size()
+               << ", size " << data->access_units.size()
                << ", status " << static_cast<int>(status);
     NOTREACHED();
     return;
@@ -362,9 +359,10 @@
 
   switch (status) {
     case DemuxerStream::kAborted:
-      // Because the abort was caused by the seek, don't respond ack.
       DVLOG(1) << "OnBufferReady() : Aborted";
-      return;
+      data->access_units[index].status = status;
+      data->access_units.resize(index + 1);
+      break;
 
     case DemuxerStream::kConfigChanged:
       // In case of kConfigChanged, need to read decoder_config once
@@ -378,15 +376,15 @@
         DVLOG(1) << "Video config is changed: " << size.width() << "x"
                  << size.height();
       }
-      params->access_units[index].status = status;
-      params->access_units.resize(index + 1);
+      data->access_units[index].status = status;
+      data->access_units.resize(index + 1);
       break;
 
     case DemuxerStream::kOk:
-      params->access_units[index].status = status;
+      data->access_units[index].status = status;
       if (buffer->end_of_stream()) {
-        params->access_units[index].end_of_stream = true;
-        params->access_units.resize(index + 1);
+        data->access_units[index].end_of_stream = true;
+        data->access_units.resize(index + 1);
         break;
       }
       // TODO(ycheo): We assume that the inputed stream will be decoded
@@ -398,8 +396,8 @@
         statistics_.video_bytes_decoded += buffer->data_size();
         statistics_.video_frames_decoded++;
       }
-      params->access_units[index].timestamp = buffer->timestamp();
-      params->access_units[index].data = std::vector<uint8>(
+      data->access_units[index].timestamp = buffer->timestamp();
+      data->access_units[index].data = std::vector<uint8>(
           buffer->data(),
           buffer->data() + buffer->data_size());
 #if !defined(GOOGLE_TV)
@@ -407,23 +405,23 @@
       // NuMediaExtractor.cpp in Android source code.
       if (is_audio && media::kCodecVorbis ==
           audio_stream_->audio_decoder_config().codec()) {
-        params->access_units[index].data.insert(
-            params->access_units[index].data.end(), kVorbisPadding,
+        data->access_units[index].data.insert(
+            data->access_units[index].data.end(), kVorbisPadding,
             kVorbisPadding + 4);
       }
 #endif
       if (buffer->decrypt_config()) {
-        params->access_units[index].key_id = std::vector<char>(
+        data->access_units[index].key_id = std::vector<char>(
             buffer->decrypt_config()->key_id().begin(),
             buffer->decrypt_config()->key_id().end());
-        params->access_units[index].iv = std::vector<char>(
+        data->access_units[index].iv = std::vector<char>(
             buffer->decrypt_config()->iv().begin(),
             buffer->decrypt_config()->iv().end());
-        params->access_units[index].subsamples =
+        data->access_units[index].subsamples =
             buffer->decrypt_config()->subsamples();
       }
-      if (++index < params->access_units.size()) {
-        ReadFromDemuxerStream(type, params.Pass(), index);
+      if (++index < data->access_units.size()) {
+        ReadFromDemuxerStream(type, data.Pass(), index);
         return;
       }
       break;
@@ -433,17 +431,16 @@
   }
 
 #if defined(GOOGLE_TV)
-  send_read_from_demuxer_ack_cb_.Run(params.Pass());
+  send_read_from_demuxer_ack_cb_.Run(data.Pass());
 #else
-  SendReadFromDemuxerAck(params.Pass());
+  SendReadFromDemuxerAck(data.Pass());
 #endif
 }
 
-void MediaSourceDelegate::SendReadFromDemuxerAck(
-    scoped_ptr<MediaPlayerHostMsg_ReadFromDemuxerAck_Params> params) {
+void MediaSourceDelegate::SendReadFromDemuxerAck(scoped_ptr<DemuxerData> data) {
   DCHECK(main_loop_->BelongsToCurrentThread());
   if (!IsSeeking() && proxy_)
-    proxy_->ReadFromDemuxerAck(player_id_, *params);
+    proxy_->ReadFromDemuxerAck(player_id_, *data);
 }
 
 void MediaSourceDelegate::OnDemuxerError(media::PipelineStatus status) {
@@ -677,41 +674,39 @@
   DVLOG(1) << "NotifyDemuxerReady() : " << player_id_;
   DCHECK(CanNotifyDemuxerReady());
 
-  scoped_ptr<MediaPlayerHostMsg_DemuxerReady_Params> params(
-      new MediaPlayerHostMsg_DemuxerReady_Params());
+  scoped_ptr<DemuxerConfigs> configs(new DemuxerConfigs());
   if (audio_stream_) {
     media::AudioDecoderConfig config = audio_stream_->audio_decoder_config();
-    params->audio_codec = config.codec();
-    params->audio_channels =
+    configs->audio_codec = config.codec();
+    configs->audio_channels =
         media::ChannelLayoutToChannelCount(config.channel_layout());
-    params->audio_sampling_rate = config.samples_per_second();
-    params->is_audio_encrypted = config.is_encrypted();
-    params->audio_extra_data = std::vector<uint8>(
+    configs->audio_sampling_rate = config.samples_per_second();
+    configs->is_audio_encrypted = config.is_encrypted();
+    configs->audio_extra_data = std::vector<uint8>(
         config.extra_data(), config.extra_data() + config.extra_data_size());
   }
   if (video_stream_) {
     media::VideoDecoderConfig config = video_stream_->video_decoder_config();
-    params->video_codec = config.codec();
-    params->video_size = config.natural_size();
-    params->is_video_encrypted = config.is_encrypted();
-    params->video_extra_data = std::vector<uint8>(
+    configs->video_codec = config.codec();
+    configs->video_size = config.natural_size();
+    configs->is_video_encrypted = config.is_encrypted();
+    configs->video_extra_data = std::vector<uint8>(
         config.extra_data(), config.extra_data() + config.extra_data_size());
   }
-  params->duration_ms = GetDurationMs();
-  params->key_system = HasEncryptedStream() ? key_system_ : "";
+  configs->duration_ms = GetDurationMs();
+  configs->key_system = HasEncryptedStream() ? key_system_ : "";
 
 #if defined(GOOGLE_TV)
-  send_demuxer_ready_cb_.Run(params.Pass());
+  send_demuxer_ready_cb_.Run(configs.Pass());
 #else
-  SendDemuxerReady(params.Pass());
+  SendDemuxerReady(configs.Pass());
 #endif
 }
 
-void MediaSourceDelegate::SendDemuxerReady(
-    scoped_ptr<MediaPlayerHostMsg_DemuxerReady_Params> params) {
+void MediaSourceDelegate::SendDemuxerReady(scoped_ptr<DemuxerConfigs> configs) {
   DCHECK(main_loop_->BelongsToCurrentThread());
   if (proxy_)
-    proxy_->DemuxerReady(player_id_, *params);
+    proxy_->DemuxerReady(player_id_, *configs);
 }
 
 int MediaSourceDelegate::GetDurationMs() {
@@ -739,13 +734,12 @@
 
 void MediaSourceDelegate::OnNeedKey(const std::string& session_id,
                                     const std::string& type,
-                                    scoped_ptr<uint8[]> init_data,
-                                    int init_data_size) {
+                                    const std::vector<uint8>& init_data) {
   DCHECK(main_loop_->BelongsToCurrentThread());
   if (need_key_cb_.is_null())
     return;
 
-  need_key_cb_.Run(session_id, type, init_data.Pass(), init_data_size);
+  need_key_cb_.Run(session_id, type, init_data);
 }
 
 scoped_ptr<media::TextTrack> MediaSourceDelegate::OnAddTextTrack(
diff --git a/content/renderer/media/android/media_source_delegate.h b/content/renderer/media/android/media_source_delegate.h
index 2d5851c..5ddc021 100644
--- a/content/renderer/media/android/media_source_delegate.h
+++ b/content/renderer/media/android/media_source_delegate.h
@@ -25,8 +25,8 @@
 class DecryptingDemuxerStream;
 class DemuxerStream;
 class MediaLog;
-struct MediaPlayerHostMsg_DemuxerReady_Params;
-struct MediaPlayerHostMsg_ReadFromDemuxerAck_Params;
+struct DemuxerConfigs;
+struct DemuxerData;
 }
 
 namespace content {
@@ -35,7 +35,7 @@
 
 class MediaSourceDelegate : public media::DemuxerHost {
  public:
-  typedef base::Callback<void(WebKit::WebMediaSourceNew*)>
+  typedef base::Callback<void(WebKit::WebMediaSource*)>
       MediaSourceOpenedCB;
   typedef base::Callback<void(WebKit::WebMediaPlayer::NetworkState)>
       UpdateNetworkStateCB;
@@ -93,12 +93,10 @@
   void Destroy();
 
  private:
-  typedef base::Callback<void(
-      scoped_ptr<media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params> params)>
-          ReadFromDemuxerAckCB;
-  typedef base::Callback<void(
-      scoped_ptr<media::MediaPlayerHostMsg_DemuxerReady_Params> params)>
-          DemuxerReadyCB;
+  typedef base::Callback<void(scoped_ptr<media::DemuxerData> data)>
+      ReadFromDemuxerAckCB;
+  typedef base::Callback<void(scoped_ptr<media::DemuxerConfigs> configs)>
+      DemuxerReadyCB;
 
   // This is private to enforce use of the Destroyer.
   virtual ~MediaSourceDelegate();
@@ -142,15 +140,13 @@
   void OnDemuxerOpened();
   void OnNeedKey(const std::string& type,
                  const std::string& session_id,
-                 scoped_ptr<uint8[]> init_data,
-                 int init_data_size);
+                 const std::vector<uint8>& init_data);
   scoped_ptr<media::TextTrack> OnAddTextTrack(media::TextKind kind,
                                               const std::string& label,
                                               const std::string& language);
   void NotifyDemuxerReady();
   bool CanNotifyDemuxerReady();
-  void SendDemuxerReady(
-      scoped_ptr<media::MediaPlayerHostMsg_DemuxerReady_Params> params);
+  void SendDemuxerReady(scoped_ptr<media::DemuxerConfigs> configs);
 
   void StopDemuxer();
   void InitializeDemuxer();
@@ -158,19 +154,16 @@
   void OnReadFromDemuxerInternal(media::DemuxerStream::Type type);
   // Reads an access unit from the demuxer stream |stream| and stores it in
   // the |index|th access unit in |params|.
-  void ReadFromDemuxerStream(
-      media::DemuxerStream::Type type,
-      scoped_ptr<media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params> params,
-      size_t index);
-  void OnBufferReady(
-      media::DemuxerStream::Type type,
-      scoped_ptr<media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params> params,
-      size_t index,
-      media::DemuxerStream::Status status,
-      const scoped_refptr<media::DecoderBuffer>& buffer);
+  void ReadFromDemuxerStream(media::DemuxerStream::Type type,
+                             scoped_ptr<media::DemuxerData> data,
+                             size_t index);
+  void OnBufferReady(media::DemuxerStream::Type type,
+                     scoped_ptr<media::DemuxerData> data,
+                     size_t index,
+                     media::DemuxerStream::Status status,
+                     const scoped_refptr<media::DecoderBuffer>& buffer);
 
-  void SendReadFromDemuxerAck(
-      scoped_ptr<media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params> params);
+  void SendReadFromDemuxerAck(scoped_ptr<media::DemuxerData> data);
 
   // Helper function for calculating duration.
   int GetDurationMs();
diff --git a/content/renderer/media/android/stream_texture_factory_android_synchronous_impl.cc b/content/renderer/media/android/stream_texture_factory_android_synchronous_impl.cc
index de0ca92..1027923 100644
--- a/content/renderer/media/android/stream_texture_factory_android_synchronous_impl.cc
+++ b/content/renderer/media/android/stream_texture_factory_android_synchronous_impl.cc
@@ -16,7 +16,7 @@
 #include "cc/output/context_provider.h"
 #include "content/common/android/surface_texture_peer.h"
 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
-#include "ui/gl/android/surface_texture_bridge.h"
+#include "ui/gl/android/surface_texture.h"
 
 namespace content {
 
@@ -46,7 +46,7 @@
 
   scoped_refptr<StreamTextureFactorySynchronousImpl::ContextProvider>
       context_provider_;
-  scoped_refptr<gfx::SurfaceTextureBridge> surface_texture_;
+  scoped_refptr<gfx::SurfaceTexture> surface_texture_;
 
   float current_matrix_[16];
   bool has_updated_;
@@ -64,7 +64,7 @@
 
 void StreamTextureProxyImpl::Release() {
   SetClient(NULL);
-  if (!loop_->BelongsToCurrentThread())
+  if (loop_.get() && !loop_->BelongsToCurrentThread())
     loop_->DeleteSoon(FROM_HERE, this);
   else
     delete this;
@@ -129,7 +129,7 @@
 
 void StreamTextureFactorySynchronousImpl::EstablishPeer(int32 stream_id,
                                                         int player_id) {
-  scoped_refptr<gfx::SurfaceTextureBridge> surface_texture =
+  scoped_refptr<gfx::SurfaceTexture> surface_texture =
       context_provider_->GetSurfaceTexture(stream_id);
   if (surface_texture) {
     SurfaceTexturePeer::GetInstance()->EstablishSurfaceTexturePeer(
diff --git a/content/renderer/media/android/stream_texture_factory_android_synchronous_impl.h b/content/renderer/media/android/stream_texture_factory_android_synchronous_impl.h
index 50f9e80..8b6ab6f 100644
--- a/content/renderer/media/android/stream_texture_factory_android_synchronous_impl.h
+++ b/content/renderer/media/android/stream_texture_factory_android_synchronous_impl.h
@@ -9,7 +9,7 @@
 #include "content/renderer/media/android/stream_texture_factory_android.h"
 
 namespace gfx {
-class SurfaceTextureBridge;
+class SurfaceTexture;
 }
 
 namespace WebKit {
@@ -23,7 +23,7 @@
  public:
   class ContextProvider : public base::RefCountedThreadSafe<ContextProvider> {
    public:
-    virtual scoped_refptr<gfx::SurfaceTextureBridge> GetSurfaceTexture(
+    virtual scoped_refptr<gfx::SurfaceTexture> GetSurfaceTexture(
         uint32 stream_id) = 0;
 
     virtual WebKit::WebGraphicsContext3D* Context3d() = 0;
diff --git a/content/renderer/media/android/webmediaplayer_android.cc b/content/renderer/media/android/webmediaplayer_android.cc
index f8eea96..5adbee7 100644
--- a/content/renderer/media/android/webmediaplayer_android.cc
+++ b/content/renderer/media/android/webmediaplayer_android.cc
@@ -27,7 +27,6 @@
 #include "third_party/WebKit/public/web/WebDocument.h"
 #include "third_party/WebKit/public/web/WebFrame.h"
 #include "third_party/WebKit/public/web/WebMediaPlayerClient.h"
-#include "third_party/WebKit/public/web/WebMediaSource.h"
 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
 #include "third_party/WebKit/public/web/WebView.h"
 #include "webkit/renderer/compositor_bindings/web_layer_impl.h"
@@ -40,7 +39,6 @@
 static const uint32 kGLTextureExternalOES = 0x8D65;
 
 using WebKit::WebMediaPlayer;
-using WebKit::WebMediaSource;
 using WebKit::WebSize;
 using WebKit::WebString;
 using WebKit::WebTimeRanges;
@@ -213,7 +211,7 @@
       break;
     case LoadTypeMediaStream:
 #if defined(GOOGLE_TV)
-      source_type_ = MediaPlayerAndroid::SOURCE_TYPE_MSE;
+      source_type_ = MediaPlayerAndroid::SOURCE_TYPE_STREAM;
 #else
       source_type_ = MediaPlayerAndroid::SOURCE_TYPE_URL;
 #endif
@@ -1001,7 +999,7 @@
            << std::string(reinterpret_cast<const char*>(init_data),
                           static_cast<size_t>(init_data_length));
 
-  if (!IsSupportedKeySystem(key_system))
+  if (!IsConcreteSupportedKeySystem(key_system))
     return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
 
   // We do not support run-time switching between key systems for now.
@@ -1054,7 +1052,7 @@
                           static_cast<size_t>(init_data_length))
            << " [" << session_id.utf8().data() << "]";
 
-  if (!IsSupportedKeySystem(key_system))
+  if (!IsConcreteSupportedKeySystem(key_system))
     return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
 
   if (current_key_system_.isEmpty() || key_system != current_key_system_)
@@ -1078,7 +1076,7 @@
 WebMediaPlayerAndroid::CancelKeyRequestInternal(
     const WebString& key_system,
     const WebString& session_id) {
-  if (!IsSupportedKeySystem(key_system))
+  if (!IsConcreteSupportedKeySystem(key_system))
     return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
 
   if (current_key_system_.isEmpty() || key_system != current_key_system_)
@@ -1125,14 +1123,13 @@
 }
 
 void WebMediaPlayerAndroid::OnMediaSourceOpened(
-    WebKit::WebMediaSourceNew* web_media_source) {
+    WebKit::WebMediaSource* web_media_source) {
   client_->mediaSourceOpened(web_media_source);
 }
 
 void WebMediaPlayerAndroid::OnNeedKey(const std::string& session_id,
                                       const std::string& type,
-                                      scoped_ptr<uint8[]> init_data,
-                                      int init_data_size) {
+                                      const std::vector<uint8>& init_data) {
   // Do not fire NeedKey event if encrypted media is not enabled.
   if (!WebKit::WebRuntimeFeatures::isEncryptedMediaEnabled() &&
       !WebKit::WebRuntimeFeatures::isLegacyEncryptedMediaEnabled()) {
@@ -1145,10 +1142,11 @@
   if (init_data_type_.empty())
     init_data_type_ = type;
 
+  const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
   client_->keyNeeded(WebString(),
                      WebString::fromUTF8(session_id),
-                     init_data.get(),
-                     init_data_size);
+                     init_data_ptr,
+                     init_data.size());
 }
 
 #if defined(GOOGLE_TV)
diff --git a/content/renderer/media/android/webmediaplayer_android.h b/content/renderer/media/android/webmediaplayer_android.h
index 03f511c..71588be 100644
--- a/content/renderer/media/android/webmediaplayer_android.h
+++ b/content/renderer/media/android/webmediaplayer_android.h
@@ -220,12 +220,11 @@
                     const std::vector<uint8>& message,
                     const std::string& destination_url);
 
-  void OnMediaSourceOpened(WebKit::WebMediaSourceNew* web_media_source);
+  void OnMediaSourceOpened(WebKit::WebMediaSource* web_media_source);
 
   void OnNeedKey(const std::string& type,
                  const std::string& session_id,
-                 scoped_ptr<uint8[]> init_data,
-                 int init_data_size);
+                 const std::vector<uint8>& init_data);
 
 #if defined(GOOGLE_TV)
   bool InjectMediaStream(MediaStreamClient* media_stream_client,
diff --git a/content/renderer/media/android/webmediaplayer_proxy_android.cc b/content/renderer/media/android/webmediaplayer_proxy_android.cc
index ef8c3d7..171beb4 100644
--- a/content/renderer/media/android/webmediaplayer_proxy_android.cc
+++ b/content/renderer/media/android/webmediaplayer_proxy_android.cc
@@ -178,9 +178,9 @@
 
 void WebMediaPlayerProxyAndroid::ReadFromDemuxerAck(
     int player_id,
-    const media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params) {
+    const media::DemuxerData& data) {
   Send(new MediaPlayerHostMsg_ReadFromDemuxerAck(
-      routing_id(), player_id, params));
+      routing_id(), player_id, data));
 }
 
 void WebMediaPlayerProxyAndroid::SeekRequestAck(int player_id,
@@ -219,8 +219,8 @@
 
 void WebMediaPlayerProxyAndroid::DemuxerReady(
     int player_id,
-    const media::MediaPlayerHostMsg_DemuxerReady_Params& params) {
-  Send(new MediaPlayerHostMsg_DemuxerReady(routing_id(), player_id, params));
+    const media::DemuxerConfigs& configs) {
+  Send(new MediaPlayerHostMsg_DemuxerReady(routing_id(), player_id, configs));
 }
 
 void WebMediaPlayerProxyAndroid::DurationChanged(
diff --git a/content/renderer/media/android/webmediaplayer_proxy_android.h b/content/renderer/media/android/webmediaplayer_proxy_android.h
index a8dd806..6dc612c 100644
--- a/content/renderer/media/android/webmediaplayer_proxy_android.h
+++ b/content/renderer/media/android/webmediaplayer_proxy_android.h
@@ -74,11 +74,8 @@
 #endif
 
   // Media source related methods.
-  void DemuxerReady(int player_id,
-                    const media::MediaPlayerHostMsg_DemuxerReady_Params&);
-  void ReadFromDemuxerAck(
-      int player_id,
-      const media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params);
+  void DemuxerReady(int player_id, const media::DemuxerConfigs& configs);
+  void ReadFromDemuxerAck(int player_id, const media::DemuxerData& data);
   void SeekRequestAck(int player_id, unsigned seek_request_id);
   void DurationChanged(int player_id, const base::TimeDelta& duration);
 
diff --git a/content/renderer/media/crypto/key_systems.cc b/content/renderer/media/crypto/key_systems.cc
index 4f55bce..a1c6b5e 100644
--- a/content/renderer/media/crypto/key_systems.cc
+++ b/content/renderer/media/crypto/key_systems.cc
@@ -87,7 +87,7 @@
 
 bool KeySystems::IsSupportedKeySystem(const std::string& key_system) {
   bool is_supported = key_system_map_.find(key_system) != key_system_map_.end();
-  return is_supported && IsSystemCompatible(key_system);
+  return is_supported && !IsOSIncompatible(key_system);
 }
 
 bool KeySystems::IsSupportedKeySystemWithContainerAndCodec(
@@ -105,7 +105,7 @@
     return false;
 
   const CodecMappings& codecs = mime_iter->second;
-  return (codecs.find(codec) != codecs.end()) && IsSystemCompatible(key_system);
+  return (codecs.find(codec) != codecs.end()) && !IsOSIncompatible(key_system);
 }
 
 bool KeySystems::IsSupportedKeySystemWithMediaMimeType(
@@ -130,16 +130,24 @@
   return true;
 }
 
-bool IsSupportedKeySystem(const WebKit::WebString& key_system) {
-  return g_key_systems.Get().IsSupportedKeySystem(ToASCIIOrEmpty(key_system));
+static inline bool IsConcreteSupportedKeySystem(const std::string& key_system) {
+  bool result = g_key_systems.Get().IsSupportedKeySystem(key_system);
+  // Verify the two "Concrete" lists are in sync.
+  DCHECK_EQ(result, IsConcreteKeySystem(key_system));
+  return result;
+}
+
+bool IsConcreteSupportedKeySystem(const WebKit::WebString& key_system) {
+  return IsConcreteSupportedKeySystem(ToASCIIOrEmpty(key_system));
 }
 
 bool IsSupportedKeySystemWithMediaMimeType(
     const std::string& mime_type,
     const std::vector<std::string>& codecs,
     const std::string& key_system) {
+  std::string concrete_key_system = EnsureConcreteKeySystem(key_system);
   return g_key_systems.Get().IsSupportedKeySystemWithMediaMimeType(
-      mime_type, codecs, key_system);
+      mime_type, codecs, concrete_key_system);
 }
 
 std::string KeySystemNameForUMA(const WebKit::WebString& key_system) {
@@ -147,13 +155,15 @@
 }
 
 bool CanUseAesDecryptor(const std::string& key_system) {
-  return CanUseBuiltInAesDecryptor(key_system);
+  return CanUseAesDecryptorInternal(key_system);
 }
 
 #if defined(ENABLE_PEPPER_CDMS)
-std::string GetPepperType(const std::string& key_system) {
+std::string GetPepperType(const std::string& concrete_key_system) {
+  DCHECK(IsConcreteKeySystem(concrete_key_system))
+      << concrete_key_system << " is not a concrete system";
   for (int i = 0; i < kNumKeySystemToPepperTypeMapping; ++i) {
-    if (kKeySystemToPepperTypeMapping[i].key_system == key_system)
+    if (kKeySystemToPepperTypeMapping[i].key_system == concrete_key_system)
       return kKeySystemToPepperTypeMapping[i].type;
   }
 
@@ -162,9 +172,11 @@
 #endif  // defined(ENABLE_PEPPER_CDMS)
 
 #if defined(OS_ANDROID)
-std::vector<uint8> GetUUID(const std::string& key_system) {
+std::vector<uint8> GetUUID(const std::string& concrete_key_system) {
+  DCHECK(IsConcreteKeySystem(concrete_key_system))
+      << concrete_key_system << " is not a concrete system";
   for (int i = 0; i < kNumKeySystemToUUIDMapping; ++i) {
-    if (kKeySystemToUUIDMapping[i].key_system == key_system)
+    if (kKeySystemToUUIDMapping[i].key_system == concrete_key_system)
       return std::vector<uint8>(kKeySystemToUUIDMapping[i].uuid,
                                 kKeySystemToUUIDMapping[i].uuid + 16);
   }
diff --git a/content/renderer/media/crypto/key_systems.h b/content/renderer/media/crypto/key_systems.h
index ede27b2..1932e1e 100644
--- a/content/renderer/media/crypto/key_systems.h
+++ b/content/renderer/media/crypto/key_systems.h
@@ -15,12 +15,31 @@
 class WebString;
 }
 
+// Definitions:
+// * Key system
+//    https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html#key-system
+// * Concrete key system
+//    A key system string that can be instantiated, such as
+//    via the MediaKeys constructor. Examples include "org.w3.clearkey" and
+//    "com.widevine.alpha".
+// * Abstract key system
+//    A key system string that cannot be instantiated like a concrete key system
+//    but is otherwise useful, such as in discovery using isTypeSupported().
+// * Parent key system
+//    A key system string that is one level up from the child key system. It may
+//    be an abstract key system.
+//    As an example, "com.example" is the parent of "com.example.foo".
+
 namespace content {
 
-// Returns whether |key_sytem| is supported at all.
+// Returns whether |key_system| is a real supported key system that can be
+// instantiated.
+// Abstract parent |key_system| strings will return false.
 // Call IsSupportedKeySystemWithMediaMimeType() to determine whether a
-// |key_system| supports a specific type of media.
-CONTENT_EXPORT bool IsSupportedKeySystem(const WebKit::WebString& key_system);
+// |key_system| supports a specific type of media or to check parent key
+// systems.
+CONTENT_EXPORT bool IsConcreteSupportedKeySystem(
+    const WebKit::WebString& key_system);
 
 // Returns whether |key_sytem| supports the specified media type and codec(s).
 CONTENT_EXPORT bool IsSupportedKeySystemWithMediaMimeType(
@@ -36,14 +55,16 @@
 CONTENT_EXPORT bool CanUseAesDecryptor(const std::string& key_system);
 
 #if defined(ENABLE_PEPPER_CDMS)
-// Returns the Pepper MIME type for |key_system|.
-// Returns an empty string if |key_system| is unknown or not Pepper-based.
-CONTENT_EXPORT std::string GetPepperType(const std::string& key_system);
+// Returns the Pepper MIME type for |concrete_key_system|.
+// Returns empty string if |concrete_key_system| is unknown or not Pepper-based.
+CONTENT_EXPORT std::string GetPepperType(
+    const std::string& concrete_key_system);
 #endif
 
 #if defined(OS_ANDROID)
-// Convert |key_system| to 16-byte Android UUID.
-CONTENT_EXPORT std::vector<uint8> GetUUID(const std::string& key_system);
+// Convert |concrete_key_system| to 16-byte Android UUID.
+CONTENT_EXPORT std::vector<uint8> GetUUID(
+    const std::string& concrete_key_system);
 #endif
 
 }  // namespace content
diff --git a/content/renderer/media/crypto/key_systems_info.cc b/content/renderer/media/crypto/key_systems_info.cc
index fc9f8ba..006e133 100644
--- a/content/renderer/media/crypto/key_systems_info.cc
+++ b/content/renderer/media/crypto/key_systems_info.cc
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #include "content/renderer/media/crypto/key_systems_info.h"
+
+#include "base/logging.h"
 #include "third_party/WebKit/public/platform/WebString.h"
 
 #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR.
@@ -25,8 +27,10 @@
 
 static const char kClearKeyKeySystem[] = "webkit-org.w3.clearkey";
 
+#if defined(ENABLE_PEPPER_CDMS)
 static const char kExternalClearKeyKeySystem[] =
     "org.chromium.externalclearkey";
+#endif  // defined(ENABLE_PEPPER_CDMS)
 
 #if defined(WIDEVINE_CDM_AVAILABLE)
 // TODO(ddorwin): Automatically support parent systems: http://crbug.com/164303.
@@ -65,28 +69,26 @@
 #if defined(USE_PROPRIETARY_CODECS)
   { "video/mp4", "avc1,mp4a", kClearKeyKeySystem },
   { "audio/mp4", "mp4a", kClearKeyKeySystem },
-#endif
+#endif  // defined(USE_PROPRIETARY_CODECS)
 
+#if defined(ENABLE_PEPPER_CDMS)
   // External Clear Key (used for testing).
   { "video/webm", "vorbis,vp8,vp8.0", kExternalClearKeyKeySystem },
   { "audio/webm", "vorbis", kExternalClearKeyKeySystem },
 #if defined(USE_PROPRIETARY_CODECS)
   { "video/mp4", "avc1,mp4a", kExternalClearKeyKeySystem },
   { "audio/mp4", "mp4a", kExternalClearKeyKeySystem },
-#endif
+#endif  // defined(USE_PROPRIETARY_CODECS)
+#endif  // defined(ENABLE_PEPPER_CDMS)
 
 #if defined(WIDEVINE_CDM_AVAILABLE)
   // Widevine.
   { "video/webm", "vorbis,vp8,vp8.0", kWidevineKeySystem },
   { "audio/webm", "vorbis", kWidevineKeySystem },
-  { "video/webm", "vorbis,vp8,vp8.0", kWidevineBaseKeySystem },
-  { "audio/webm", "vorbis", kWidevineBaseKeySystem },
 #if defined(USE_PROPRIETARY_CODECS)
 #if defined(WIDEVINE_CDM_CENC_SUPPORT_AVAILABLE)
   { "video/mp4", kWidevineVideoMp4Codecs, kWidevineKeySystem },
-  { "video/mp4", kWidevineVideoMp4Codecs, kWidevineBaseKeySystem },
   { "audio/mp4", kWidevineAudioMp4Codecs, kWidevineKeySystem },
-  { "audio/mp4", kWidevineAudioMp4Codecs, kWidevineBaseKeySystem },
 #endif  // defined(WIDEVINE_CDM_CENC_SUPPORT_AVAILABLE)
 #endif  // defined(USE_PROPRIETARY_CODECS)
 #endif  // WIDEVINE_CDM_AVAILABLE
@@ -122,16 +124,34 @@
     ARRAYSIZE_UNSAFE(kKeySystemToUUIDMapping);
 #endif  // defined(OS_ANDROID)
 
-bool IsSystemCompatible(const std::string& key_system) {
+bool IsOSIncompatible(const std::string& concrete_key_system) {
+  DCHECK(IsConcreteKeySystem(concrete_key_system))
+      << concrete_key_system << " is not a concrete system";
 #if defined(WIDEVINE_CDM_AVAILABLE) && \
     defined(OS_LINUX) && !defined(OS_CHROMEOS)
-  if (IsWidevine(key_system)) {
+  if (IsWidevine(concrete_key_system)) {
     Version glibc_version(gnu_get_libc_version());
     DCHECK(glibc_version.IsValid());
-    return !glibc_version.IsOlderThan(WIDEVINE_CDM_MIN_GLIBC_VERSION);
+    return glibc_version.IsOlderThan(WIDEVINE_CDM_MIN_GLIBC_VERSION);
   }
 #endif
-  return true;
+  return false;
+}
+
+std::string EnsureConcreteKeySystem(const std::string& key_system) {
+#if defined(WIDEVINE_CDM_AVAILABLE)
+  if (key_system == kWidevineKeySystem || key_system == kWidevineBaseKeySystem)
+    return kWidevineKeySystem;
+#endif  // WIDEVINE_CDM_AVAILABLE
+  // No parent names for Clear Key.
+  if (key_system == kClearKeyKeySystem)
+    return kClearKeyKeySystem;
+#if defined(ENABLE_PEPPER_CDMS)
+  // No parent names for External Clear Key.
+  if (key_system == kExternalClearKeyKeySystem)
+    return kExternalClearKeyKeySystem;
+#endif  // defined(ENABLE_PEPPER_CDMS)
+  return std::string();
 }
 
 bool IsCanPlayTypeSuppressed(const std::string& key_system) {
@@ -155,7 +175,7 @@
   return "Unknown";
 }
 
-bool CanUseBuiltInAesDecryptor(const std::string& key_system) {
+bool CanUseAesDecryptorInternal(const std::string& key_system) {
   return  key_system == kClearKeyKeySystem;
 }
 
diff --git a/content/renderer/media/crypto/key_systems_info.h b/content/renderer/media/crypto/key_systems_info.h
index df4547d..b36fc13 100644
--- a/content/renderer/media/crypto/key_systems_info.h
+++ b/content/renderer/media/crypto/key_systems_info.h
@@ -56,8 +56,26 @@
 extern const int kNumKeySystemToUUIDMapping;
 #endif  // defined(OS_ANDROID)
 
-// Returns whether |key_system| is compatible with the user's system.
-bool IsSystemCompatible(const std::string& key_system);
+// Returns a concrete key system supported by the platform that most closely
+// corresponds to |key_system|. The result can be passed to other functions that
+// require a concrete key system.
+// Returns null if a conversion cannot be made or |key_system| is unrecognized.
+// The primary use case is to convert a parent key system to a concrete key
+// system to check properties.
+// If we ever have multiple children for a single parent, we may need a more
+// complex solution that checks all concrete children until it gets true.
+std::string EnsureConcreteKeySystem(const std::string& key_system);
+
+// Returns whether |key_system| is a concrete key system.
+// This is used for DCHECKs. Production code should use
+// EnsureConcreteKeySystem().
+inline bool IsConcreteKeySystem(const std::string& key_system) {
+  return !key_system.empty() &&
+      key_system == EnsureConcreteKeySystem(key_system);
+}
+
+// Returns true if there is a known incompatibility with the operating system.
+bool IsOSIncompatible(const std::string& actual_key_system);
 
 // Returns true if canPlayType should return an empty string for |key_system|.
 bool IsCanPlayTypeSuppressed(const std::string& key_system);
@@ -68,7 +86,7 @@
 std::string KeySystemNameForUMAInternal(const WebKit::WebString& key_system);
 
 // Returns whether built-in AesDecryptor can be used for the given |key_system|.
-bool CanUseBuiltInAesDecryptor(const std::string& key_system);
+bool CanUseAesDecryptorInternal(const std::string& key_system);
 
 }  // namespace content
 
diff --git a/content/renderer/media/crypto/key_systems_unittest.cc b/content/renderer/media/crypto/key_systems_unittest.cc
index 8199f15..f4c2f34 100644
--- a/content/renderer/media/crypto/key_systems_unittest.cc
+++ b/content/renderer/media/crypto/key_systems_unittest.cc
@@ -11,6 +11,30 @@
 
 #include "widevine_cdm_version.h"  // In SHARED_INTERMEDIATE_DIR.
 
+// Death tests are not always available. When they are, they use NDEBUG for
+// the DCHECK variant, which is not sufficient when defined(DCHECK_ALWAYS_ON).
+// EXPECT_DCHECK_DEATH handles all these cases. The test will execute correctly
+// except in the case that death tests are not available but DCHECKs are on.
+#if defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID)
+#if defined(DCHECK_ALWAYS_ON)
+#define EXPECT_DCHECK_DEATH(statement, regex) \
+  EXPECT_DEATH(statement, regex)
+#else
+#define EXPECT_DCHECK_DEATH(statement, regex) \
+  EXPECT_DEBUG_DEATH(statement, regex)
+#endif  // defined(DCHECK_ALWAYS_ON)
+#else  // defined(GTEST_HAS_DEATH_TEST)
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+#define EXPECT_DCHECK_DEATH(statement, regex) \
+  do { statement; } while (false)
+#else
+#include "base/logging.h"
+#define EXPECT_DCHECK_DEATH(statement, regex) \
+  LOG(WARNING) << "Death tests are not supported on this platform.\n" \
+               << "Statement '" #statement "' cannot be verified.";
+#endif  // defined(NDEBUG) && defined(DCHECK_ALWAYS_ON)
+#endif  // defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID)
+
 #if defined(WIDEVINE_CDM_AVAILABLE) && \
     defined(OS_LINUX) && !defined(OS_CHROMEOS)
 #include <gnu/libc-version.h>
@@ -24,6 +48,16 @@
 #define EXPECT_PROPRIETARY EXPECT_FALSE
 #endif
 
+// Expectations for External Clear Key.
+#if defined(ENABLE_PEPPER_CDMS)
+#define EXPECT_ECK EXPECT_TRUE
+#define EXPECT_ECKPROPRIETARY EXPECT_PROPRIETARY
+#else
+#define EXPECT_ECK EXPECT_FALSE
+#define EXPECT_ECKPROPRIETARY EXPECT_FALSE
+#endif  // defined(ENABLE_PEPPER_CDMS)
+
+// Expectations for Widevine.
 #if defined(WIDEVINE_CDM_AVAILABLE)
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
 // TODO(ddorwin): Remove after bots switch to Precise.
@@ -160,12 +194,12 @@
 };
 
 TEST_F(KeySystemsTest, ClearKey_Basic) {
-  EXPECT_TRUE(IsSupportedKeySystem(WebString::fromUTF8(kClearKey)));
+  EXPECT_TRUE(IsConcreteSupportedKeySystem(WebString::fromUTF8(kClearKey)));
   EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), kClearKey));
 
   // Not yet out from behind the vendor prefix.
-  EXPECT_FALSE(IsSupportedKeySystem("org.w3.clearkey"));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("org.w3.clearkey"));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "org.w3.clearkey"));
 
@@ -182,7 +216,8 @@
   const char* const kClearKeyParent = "webkit-org.w3";
 
   // The parent should be supported but is not. See http://crbug.com/164303.
-  EXPECT_FALSE(IsSupportedKeySystem(WebString::fromUTF8(kClearKeyParent)));
+  EXPECT_FALSE(
+      IsConcreteSupportedKeySystem(WebString::fromUTF8(kClearKeyParent)));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), kClearKeyParent));
 
@@ -191,47 +226,50 @@
       KeySystemNameForUMA(WebString::fromUTF8(kClearKeyParent)).c_str());
   EXPECT_FALSE(CanUseAesDecryptor(kClearKeyParent));
 #if defined(ENABLE_PEPPER_CDMS)
-  EXPECT_TRUE(GetPepperType(kClearKeyParent).empty());
+  std::string type;
+  EXPECT_DCHECK_DEATH(type = GetPepperType(kClearKeyParent),
+                      "webkit-org.w3 is not a concrete system");
+  EXPECT_TRUE(type.empty());
 #endif
 }
 
 TEST_F(KeySystemsTest, ClearKey_IsSupportedKeySystem_InvalidVariants) {
   // Case sensitive.
-  EXPECT_FALSE(IsSupportedKeySystem("webkit-org.w3.ClEaRkEy"));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("webkit-org.w3.ClEaRkEy"));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "webkit-org.w3.ClEaRkEy"));
 
   // TLDs are not allowed.
-  EXPECT_FALSE(IsSupportedKeySystem("webkit-org."));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("webkit-org."));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "webkit-org."));
-  EXPECT_FALSE(IsSupportedKeySystem("webkit-org"));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("webkit-org"));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "webkit-org"));
-  EXPECT_FALSE(IsSupportedKeySystem("org."));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("org."));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "org."));
-  EXPECT_FALSE(IsSupportedKeySystem("org"));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("org"));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "org"));
 
   // Extra period.
-  EXPECT_FALSE(IsSupportedKeySystem("webkit-org.w3."));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("webkit-org.w3."));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "webkit-org.w3."));
 
   // Incomplete.
-  EXPECT_FALSE(IsSupportedKeySystem("webkit-org.w3.clearke"));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("webkit-org.w3.clearke"));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "webkit-org.w3.clearke"));
 
   // Extra character.
-  EXPECT_FALSE(IsSupportedKeySystem("webkit-org.w3.clearkeyz"));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("webkit-org.w3.clearkeyz"));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "webkit-org.w3.clearkeyz"));
 
   // There are no child key systems for Clear Key.
-  EXPECT_FALSE(IsSupportedKeySystem("webkit-org.w3.clearkey.foo"));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("webkit-org.w3.clearkey.foo"));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "webkit-org.w3.clearkey.foo"));
 }
@@ -345,8 +383,9 @@
 //
 
 TEST_F(KeySystemsTest, ExternalClearKey_Basic) {
-  EXPECT_TRUE(IsSupportedKeySystem(WebString::fromUTF8(kExternalClearKey)));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_ECK(
+      IsConcreteSupportedKeySystem(WebString::fromUTF8(kExternalClearKey)));
+  EXPECT_ECK(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), kExternalClearKey));
 
   // External Clear Key does not have a UMA name because it is for testing.
@@ -365,8 +404,8 @@
   const char* const kExternalClearKeyParent = "org.chromium";
 
   // The parent should be supported but is not. See http://crbug.com/164303.
-  EXPECT_FALSE(
-      IsSupportedKeySystem(WebString::fromUTF8(kExternalClearKeyParent)));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem(
+      WebString::fromUTF8(kExternalClearKeyParent)));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), kExternalClearKeyParent));
 
@@ -376,44 +415,48 @@
                    WebString::fromUTF8(kExternalClearKeyParent)).c_str());
   EXPECT_FALSE(CanUseAesDecryptor(kExternalClearKeyParent));
 #if defined(ENABLE_PEPPER_CDMS)
-  EXPECT_TRUE(GetPepperType(kExternalClearKeyParent).empty());
+  std::string type;
+  EXPECT_DCHECK_DEATH(type = GetPepperType(kExternalClearKeyParent),
+                      "org.chromium is not a concrete system");
+  EXPECT_TRUE(type.empty());
 #endif
 }
 
 TEST_F(KeySystemsTest, ExternalClearKey_IsSupportedKeySystem_InvalidVariants) {
   // Case sensitive.
-  EXPECT_FALSE(IsSupportedKeySystem("org.chromium.ExTeRnAlClEaRkEy"));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("org.chromium.ExTeRnAlClEaRkEy"));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(),
       "org.chromium.ExTeRnAlClEaRkEy"));
 
   // TLDs are not allowed.
-  EXPECT_FALSE(IsSupportedKeySystem("org."));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("org."));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "org."));
-  EXPECT_FALSE(IsSupportedKeySystem("org"));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("org"));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "org"));
 
   // Extra period.
-  EXPECT_FALSE(IsSupportedKeySystem("org.chromium."));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("org.chromium."));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "org.chromium."));
 
   // Incomplete.
-  EXPECT_FALSE(IsSupportedKeySystem("org.chromium.externalclearke"));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("org.chromium.externalclearke"));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(),
       "org.chromium.externalclearke"));
 
   // Extra character.
-  EXPECT_FALSE(IsSupportedKeySystem("org.chromium.externalclearkeyz"));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("org.chromium.externalclearkeyz"));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(),
       "org.chromium.externalclearkeyz"));
 
   // There are no child key systems for Clear Key.
-  EXPECT_FALSE(IsSupportedKeySystem("org.chromium.externalclearkey.foo"));
+  EXPECT_FALSE(
+      IsConcreteSupportedKeySystem("org.chromium.externalclearkey.foo"));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(),
       "org.chromium.externalclearkey.foo"));
@@ -436,18 +479,18 @@
 TEST_F(KeySystemsTest,
        IsSupportedKeySystemWithMediaMimeType_ExternalClearKey_WebM) {
   // Valid video types.
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_ECK(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), kExternalClearKey));
   // The parent should be supported but is not. See http://crbug.com/164303.
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "org.chromium"));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_ECK(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", vp8_codec(), kExternalClearKey));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_ECK(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", vp80_codec(), kExternalClearKey));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_ECK(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", vp8_and_vorbis_codecs(), kExternalClearKey));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_ECK(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", vorbis_codec(), kExternalClearKey));
 
   // Non-Webm codecs.
@@ -459,9 +502,9 @@
       "video/webm", mixed_codecs(), kExternalClearKey));
 
   // Valid audio types.
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_ECK(IsSupportedKeySystemWithMediaMimeType(
       "audio/webm", no_codecs(), kExternalClearKey));
-  EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_ECK(IsSupportedKeySystemWithMediaMimeType(
       "audio/webm", vorbis_codec(), kExternalClearKey));
 
   // Non-audio codecs.
@@ -478,16 +521,16 @@
 TEST_F(KeySystemsTest,
        IsSupportedKeySystemWithMediaMimeType_ExternalClearKey_MP4) {
   // Valid video types.
-  EXPECT_PROPRIETARY(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_ECKPROPRIETARY(IsSupportedKeySystemWithMediaMimeType(
       "video/mp4", no_codecs(), kExternalClearKey));
   // The parent should be supported but is not. See http://crbug.com/164303.
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/mp4", no_codecs(), "org.chromium"));
-  EXPECT_PROPRIETARY(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_ECKPROPRIETARY(IsSupportedKeySystemWithMediaMimeType(
       "video/mp4", avc1_codec(), kExternalClearKey));
-  EXPECT_PROPRIETARY(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_ECKPROPRIETARY(IsSupportedKeySystemWithMediaMimeType(
       "video/mp4", avc1_and_aac_codecs(), kExternalClearKey));
-  EXPECT_PROPRIETARY(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_ECKPROPRIETARY(IsSupportedKeySystemWithMediaMimeType(
       "video/mp4", aac_codec(), kExternalClearKey));
 
   // Extended codecs fail because this is handled by SimpleWebMimeRegistryImpl.
@@ -510,9 +553,9 @@
       "video/mp4", mixed_codecs(), kExternalClearKey));
 
   // Valid audio types.
-  EXPECT_PROPRIETARY(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_ECKPROPRIETARY(IsSupportedKeySystemWithMediaMimeType(
       "audio/mp4", no_codecs(), kExternalClearKey));
-  EXPECT_PROPRIETARY(IsSupportedKeySystemWithMediaMimeType(
+  EXPECT_ECKPROPRIETARY(IsSupportedKeySystemWithMediaMimeType(
       "audio/mp4", aac_codec(), kExternalClearKey));
 
   // Non-audio codecs.
@@ -533,9 +576,10 @@
 TEST_F(KeySystemsTest, Widevine_Basic) {
 #if defined(WIDEVINE_CDM_AVAILABLE) && \
     defined(DISABLE_WIDEVINE_CDM_CANPLAYTYPE)
-  EXPECT_TRUE(IsSupportedKeySystem(WebString::fromUTF8(kWidevineAlpha)));
+  EXPECT_TRUE(
+      IsConcreteSupportedKeySystem(WebString::fromUTF8(kWidevineAlpha)));
 #else
-  EXPECT_WV(IsSupportedKeySystem(WebString::fromUTF8(kWidevineAlpha)));
+  EXPECT_WV(IsConcreteSupportedKeySystem(WebString::fromUTF8(kWidevineAlpha)));
 #endif
 
   EXPECT_WV(IsSupportedKeySystemWithMediaMimeType(
@@ -556,64 +600,64 @@
   EXPECT_STREQ("application/x-ppapi-widevine-cdm",
                GetPepperType(kWidevineAlpha).c_str());
 #else
-  EXPECT_TRUE(GetPepperType(kWidevineAlpha).empty());
+  std::string type;
+  EXPECT_DCHECK_DEATH(type = GetPepperType(kWidevineAlpha),
+                      "com.widevine.alpha is not a concrete system");
+  EXPECT_TRUE(type.empty());
 #endif  // defined(WIDEVINE_CDM_AVAILABLE)
 #endif  // defined(ENABLE_PEPPER_CDMS)
 
 }
 
 TEST_F(KeySystemsTest, Widevine_Parent) {
-  const char* const kWidevineParent = kWidevine;
-
-#if defined(WIDEVINE_CDM_AVAILABLE) && \
-    defined(DISABLE_WIDEVINE_CDM_CANPLAYTYPE)
-  EXPECT_TRUE(IsSupportedKeySystem(WebString::fromUTF8(kWidevineParent)));
-#else
-  EXPECT_WV(IsSupportedKeySystem(WebString::fromUTF8(kWidevineParent)));
-#endif
+  // The parent system is not a concrete system but is supported.
+  EXPECT_FALSE(IsConcreteSupportedKeySystem(WebString::fromUTF8(kWidevine)));
   EXPECT_WV(IsSupportedKeySystemWithMediaMimeType(
-      "video/webm", no_codecs(), kWidevineParent));
+      "video/webm", no_codecs(), kWidevine));
 
   // The parent is not supported for most things.
   EXPECT_STREQ("Unknown",
-      KeySystemNameForUMA(WebString::fromUTF8(kWidevineParent)).c_str());
-  EXPECT_FALSE(CanUseAesDecryptor(kWidevineParent));
+      KeySystemNameForUMA(WebString::fromUTF8(kWidevine)).c_str());
+  EXPECT_FALSE(CanUseAesDecryptor(kWidevine));
 #if defined(ENABLE_PEPPER_CDMS)
-  EXPECT_TRUE(GetPepperType(kWidevineParent).empty());
+  std::string type;
+  EXPECT_DCHECK_DEATH(type = GetPepperType(kWidevine),
+                      "com.widevine is not a concrete system");
+  EXPECT_TRUE(type.empty());
 #endif
 }
 
 TEST_F(KeySystemsTest, Widevine_IsSupportedKeySystem_InvalidVariants) {
   // Case sensitive.
-  EXPECT_FALSE(IsSupportedKeySystem("com.widevine.AlPhA"));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("com.widevine.AlPhA"));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "com.widevine.AlPhA"));
 
   // TLDs are not allowed.
-  EXPECT_FALSE(IsSupportedKeySystem("com."));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("com."));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "com."));
-  EXPECT_FALSE(IsSupportedKeySystem("com"));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("com"));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "com"));
 
   // Extra period.
-  EXPECT_FALSE(IsSupportedKeySystem("com.widevine."));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("com.widevine."));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "com.widevine."));
 
   // Incomplete.
-  EXPECT_FALSE(IsSupportedKeySystem("com.widevine.alph"));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("com.widevine.alph"));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "com.widevine.alph"));
 
   // Extra character.
-  EXPECT_FALSE(IsSupportedKeySystem("com.widevine.alphab"));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("com.widevine.alphab"));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "com.widevine.alphab"));
 
   // There are no child key systems for Widevine Alpha.
-  EXPECT_FALSE(IsSupportedKeySystem("com.widevine.alpha.foo"));
+  EXPECT_FALSE(IsConcreteSupportedKeySystem("com.widevine.alpha.foo"));
   EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
       "video/webm", no_codecs(), "com.widevine.alpha.foo"));
 }
@@ -752,19 +796,28 @@
 
 #if defined(OS_ANDROID)
 TEST_F(KeySystemsTest, GetUUID_Widevine) {
-  std::vector<uint8> uuid = GetUUID(kWidevineAlpha);
 #if defined(WIDEVINE_CDM_AVAILABLE)
+  std::vector<uint8> uuid = GetUUID(kWidevineAlpha);
   EXPECT_EQ(16u, uuid.size());
   EXPECT_EQ(0xED, uuid[15]);
 #else
+  std::vector<uint8> uuid;
+  EXPECT_DCHECK_DEATH(uuid = GetUUID(kWidevineAlpha),
+                      "com.widevine.alpha is not a concrete system");
   EXPECT_TRUE(uuid.empty());
 #endif
 }
 
 TEST_F(KeySystemsTest, GetUUID_Unrecognized) {
-  EXPECT_TRUE(GetUUID(kWidevine).empty());
+  std::vector<uint8> uuid;
+  EXPECT_DCHECK_DEATH(uuid = GetUUID(kWidevine),
+                      "com.widevine is not a concrete system");
+  EXPECT_TRUE(uuid.empty());
+
   EXPECT_TRUE(GetUUID(kClearKey).empty());
-  EXPECT_TRUE(GetUUID("").empty());
+
+  EXPECT_DCHECK_DEATH(uuid = GetUUID(""), " is not a concrete system");
+  EXPECT_TRUE(uuid.empty());
 }
 #endif  // defined(OS_ANDROID)
 
diff --git a/content/renderer/media/media_stream_center.h b/content/renderer/media/media_stream_center.h
index 642cda5..83a55f6 100644
--- a/content/renderer/media/media_stream_center.h
+++ b/content/renderer/media/media_stream_center.h
@@ -15,6 +15,7 @@
 #include "third_party/WebKit/public/platform/WebMediaStream.h"
 #include "third_party/WebKit/public/platform/WebMediaStreamCenter.h"
 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
+#include "third_party/WebKit/public/platform/WebMediaStreamTrackSourcesRequest.h"
 
 namespace WebKit {
 class WebMediaStreamCenterClient;
diff --git a/content/renderer/media/media_stream_impl.cc b/content/renderer/media/media_stream_impl.cc
index 6ce995e..9eab908 100644
--- a/content/renderer/media/media_stream_impl.cc
+++ b/content/renderer/media/media_stream_impl.cc
@@ -552,6 +552,19 @@
       ++request_it;
     }
   }
+
+  // Free the potentially-cached audio renderer.  If we don't do this, the
+  // audio device will be held open after the tab has been closed even though
+  // it's not being used.
+  WebRtcAudioDeviceImpl* audio_device =
+      dependency_factory_->GetWebRtcAudioDevice();
+  if (audio_device) {
+    scoped_refptr<WebRtcAudioRenderer> renderer(audio_device->renderer());
+    if (renderer.get()) {
+      renderer->Stop();  // This will clear the dependency factory's pointer.
+      DCHECK(!audio_device->renderer().get());
+    }
+  }
 }
 
 scoped_refptr<VideoFrameProvider>
diff --git a/content/renderer/media/midi_message_filter.cc b/content/renderer/media/midi_message_filter.cc
index 39f3914..32e8bdd 100644
--- a/content/renderer/media/midi_message_filter.cc
+++ b/content/renderer/media/midi_message_filter.cc
@@ -137,10 +137,7 @@
           UTF8ToUTF16(outputs[i].version));
     }
   }
-  // TODO(toyoshim): Reports device initialization failure to JavaScript as
-  // "NotSupportedError" or something when |success| is false.
-  // http://crbug.com/260315
-  client->didStartSession();
+  client->didStartSession(success);
 }
 
 WebKit::WebMIDIAccessorClient*
@@ -156,7 +153,7 @@
   return NULL;
 }
 
-void MIDIMessageFilter::OnDataReceived(int port,
+void MIDIMessageFilter::OnDataReceived(uint32 port,
                                        const std::vector<uint8>& data,
                                        double timestamp) {
   TRACE_EVENT0("midi", "MIDIMessageFilter::OnDataReceived");
@@ -173,21 +170,17 @@
     unacknowledged_bytes_sent_ -= bytes_sent;
 }
 
-void MIDIMessageFilter::HandleDataReceived(int port,
+void MIDIMessageFilter::HandleDataReceived(uint32 port,
                                            const std::vector<uint8>& data,
                                            double timestamp) {
+  DCHECK(!data.empty());
   TRACE_EVENT0("midi", "MIDIMessageFilter::HandleDataReceived");
 
-#if defined(OS_ANDROID)
-  // TODO(crogers): figure out why data() method does not compile on Android.
-  NOTIMPLEMENTED();
-#else
   for (ClientsMap::iterator i = clients_.begin(); i != clients_.end(); ++i)
-    (*i).first->didReceiveMIDIData(port, data.data(), data.size(), timestamp);
-#endif
+    (*i).first->didReceiveMIDIData(port, &data[0], data.size(), timestamp);
 }
 
-void MIDIMessageFilter::SendMIDIData(int port,
+void MIDIMessageFilter::SendMIDIData(uint32 port,
                                      const uint8* data,
                                      size_t length,
                                      double timestamp) {
@@ -203,7 +196,7 @@
                  port, v, timestamp));
 }
 
-void MIDIMessageFilter::SendMIDIDataOnIOThread(int port,
+void MIDIMessageFilter::SendMIDIDataOnIOThread(uint32 port,
                                                const std::vector<uint8>& data,
                                                double timestamp) {
   size_t n = data.size();
diff --git a/content/renderer/media/midi_message_filter.h b/content/renderer/media/midi_message_filter.h
index cf969e9..4b8481f 100644
--- a/content/renderer/media/midi_message_filter.h
+++ b/content/renderer/media/midi_message_filter.h
@@ -12,7 +12,6 @@
 #include "content/common/content_export.h"
 #include "ipc/ipc_channel_proxy.h"
 #include "media/midi/midi_port_info.h"
-#include "third_party/WebKit/public/platform/WebMIDIAccessor.h"
 #include "third_party/WebKit/public/platform/WebMIDIAccessorClient.h"
 
 namespace base {
@@ -37,7 +36,7 @@
 
   // A client will only be able to call this method if it has a suitable
   // output port (from addOutputPort()).
-  void SendMIDIData(int port,
+  void SendMIDIData(uint32 port,
                     const uint8* data,
                     size_t length,
                     double timestamp);
@@ -69,7 +68,7 @@
 
   // Called when the browser process has sent MIDI data containing one or
   // more messages.
-  void OnDataReceived(int port,
+  void OnDataReceived(uint32 port,
                       const std::vector<uint8>& data,
                       double timestamp);
 
@@ -83,13 +82,13 @@
                             media::MIDIPortInfoList inputs,
                             media::MIDIPortInfoList outputs);
 
-  void HandleDataReceived(int port,
+  void HandleDataReceived(uint32 port,
                           const std::vector<uint8>& data,
                           double timestamp);
 
   void StartSessionOnIOThread(int client_id);
 
-  void SendMIDIDataOnIOThread(int port,
+  void SendMIDIDataOnIOThread(uint32 port,
                               const std::vector<uint8>& data,
                               double timestamp);
 
diff --git a/content/renderer/media/mock_peer_connection_impl.cc b/content/renderer/media/mock_peer_connection_impl.cc
index 138b905..b944bd1 100644
--- a/content/renderer/media/mock_peer_connection_impl.cc
+++ b/content/renderer/media/mock_peer_connection_impl.cc
@@ -86,7 +86,8 @@
                   const webrtc::DataChannelInit* config)
       : label_(label),
         reliable_(config->reliable),
-        state_(webrtc::DataChannelInterface::kConnecting) {
+        state_(webrtc::DataChannelInterface::kConnecting),
+        config_(*config) {
   }
 
   virtual void RegisterObserver(
@@ -104,6 +105,26 @@
     return reliable_;
   }
 
+  virtual bool ordered() const OVERRIDE {
+    return config_.ordered;
+  }
+
+  virtual unsigned short maxRetransmitTime() const OVERRIDE {
+    return config_.maxRetransmitTime;
+  }
+
+  virtual unsigned short maxRetransmits() const OVERRIDE {
+    return config_.maxRetransmits;
+  }
+
+  virtual std::string protocol() const OVERRIDE {
+    return config_.protocol;
+  }
+
+  virtual bool negotiated() const OVERRIDE {
+    return config_.negotiated;
+  }
+
   virtual int id() const OVERRIDE {
     NOTIMPLEMENTED();
     return 0;
@@ -133,6 +154,7 @@
   std::string label_;
   bool reliable_;
   webrtc::DataChannelInterface::DataState state_;
+  webrtc::DataChannelInit config_;
 };
 
 class MockDtmfSender : public DtmfSenderInterface {
diff --git a/content/renderer/media/native_handle_impl.cc b/content/renderer/media/native_handle_impl.cc
new file mode 100644
index 0000000..3988e39
--- /dev/null
+++ b/content/renderer/media/native_handle_impl.cc
@@ -0,0 +1,16 @@
+// 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 "content/renderer/media/native_handle_impl.h"
+
+namespace content {
+
+NativeHandleImpl::NativeHandleImpl(scoped_refptr<media::VideoFrame> frame)
+    : frame_(frame) {}
+
+NativeHandleImpl::~NativeHandleImpl() {}
+
+void* NativeHandleImpl::GetHandle() { return frame_.get(); }
+
+}  // namespace content
diff --git a/content/renderer/media/native_handle_impl.h b/content/renderer/media/native_handle_impl.h
new file mode 100644
index 0000000..36c5dab
--- /dev/null
+++ b/content/renderer/media/native_handle_impl.h
@@ -0,0 +1,32 @@
+// 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 CONTENT_RENDERER_MEDIA_NATIVE_HANDLE_IMPL_H_
+#define CONTENT_RENDERER_MEDIA_NATIVE_HANDLE_IMPL_H_
+
+#include "base/memory/ref_counted.h"
+#include "media/base/video_frame.h"
+#include "third_party/webrtc/common_video/interface/native_handle.h"
+
+namespace content {
+
+class NativeHandleImpl : public webrtc::NativeHandle {
+ public:
+  // Wraps a video frame in the handle.
+  explicit NativeHandleImpl(scoped_refptr<media::VideoFrame> frame);
+  virtual ~NativeHandleImpl();
+
+  // Retrieves the video frame in the handle. The frame is still ref-counted by
+  // the handle. The ref count decreases when NativeHandleImpl is destroyed.
+  virtual void* GetHandle() OVERRIDE;
+
+ private:
+  scoped_refptr<media::VideoFrame> frame_;
+
+  DISALLOW_COPY_AND_ASSIGN(NativeHandleImpl);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_MEDIA_NATIVE_HANDLE_IMPL_H_
diff --git a/content/renderer/media/rtc_data_channel_handler.cc b/content/renderer/media/rtc_data_channel_handler.cc
index 866bdd9..713dc4c 100644
--- a/content/renderer/media/rtc_data_channel_handler.cc
+++ b/content/renderer/media/rtc_data_channel_handler.cc
@@ -37,6 +37,30 @@
   return channel_->reliable();
 }
 
+bool RtcDataChannelHandler::ordered() const {
+  return channel_->ordered();
+}
+
+unsigned short RtcDataChannelHandler::maxRetransmitTime() const {
+  return channel_->maxRetransmitTime();
+}
+
+unsigned short RtcDataChannelHandler::maxRetransmits() const {
+  return channel_->maxRetransmits();
+}
+
+WebKit::WebString RtcDataChannelHandler::protocol() const {
+  return UTF8ToUTF16(channel_->protocol());
+}
+
+bool RtcDataChannelHandler::negotiated() const {
+  return channel_->negotiated();
+}
+
+unsigned short RtcDataChannelHandler::id() const {
+  return channel_->id();
+}
+
 unsigned long RtcDataChannelHandler::bufferedAmount() {
   return channel_->buffered_amount();
 }
diff --git a/content/renderer/media/rtc_data_channel_handler.h b/content/renderer/media/rtc_data_channel_handler.h
index a58b100..8ec07e0 100644
--- a/content/renderer/media/rtc_data_channel_handler.h
+++ b/content/renderer/media/rtc_data_channel_handler.h
@@ -33,6 +33,12 @@
       WebKit::WebRTCDataChannelHandlerClient* client) OVERRIDE;
   virtual WebKit::WebString label() OVERRIDE;
   virtual bool isReliable() OVERRIDE;
+  virtual bool ordered() const OVERRIDE;
+  virtual unsigned short maxRetransmitTime() const OVERRIDE;
+  virtual unsigned short maxRetransmits() const OVERRIDE;
+  virtual WebKit::WebString protocol() const OVERRIDE;
+  virtual bool negotiated() const OVERRIDE;
+  virtual unsigned short id() const OVERRIDE;
   virtual unsigned long bufferedAmount() OVERRIDE;
   virtual bool sendStringData(const WebKit::WebString& data) OVERRIDE;
   virtual bool sendRawData(const char* data, size_t length) OVERRIDE;
diff --git a/content/renderer/media/rtc_video_decoder.cc b/content/renderer/media/rtc_video_decoder.cc
index c048b6b..adce1f6 100644
--- a/content/renderer/media/rtc_video_decoder.cc
+++ b/content/renderer/media/rtc_video_decoder.cc
@@ -12,8 +12,10 @@
 #include "base/stl_util.h"
 #include "base/task_runner_util.h"
 #include "content/child/child_thread.h"
+#include "content/renderer/media/native_handle_impl.h"
 #include "media/base/bind_to_loop.h"
 #include "media/filters/gpu_video_accelerator_factories.h"
+#include "third_party/webrtc/common_video/interface/texture_video_frame.h"
 #include "third_party/webrtc/system_wrappers/interface/ref_count.h"
 
 namespace content {
@@ -362,12 +364,9 @@
   DCHECK(inserted);
 
   // Create a WebRTC video frame.
-  // TODO(wuchengli): make media::VideoFrame an opaque native handle and put it
-  // into WebRTC frame.
-  webrtc::I420VideoFrame decoded_image;
-  decoded_image.CreateEmptyFrame(
-      width, height, width, (width + 1) / 2, (width + 1) / 2);
-  decoded_image.set_timestamp(timestamp);
+  webrtc::RefCountImpl<NativeHandleImpl>* handle =
+      new webrtc::RefCountImpl<NativeHandleImpl>(frame);
+  webrtc::TextureVideoFrame decoded_image(handle, width, height, timestamp, 0);
 
   // Invoke decode callback. WebRTC expects no callback after Reset or Release.
   {
diff --git a/content/renderer/media/rtc_video_encoder.cc b/content/renderer/media/rtc_video_encoder.cc
index 416317d..1a4efcd 100644
--- a/content/renderer/media/rtc_video_encoder.cc
+++ b/content/renderer/media/rtc_video_encoder.cc
@@ -13,6 +13,7 @@
 #include "content/renderer/media/renderer_gpu_video_accelerator_factories.h"
 #include "media/base/bitstream_buffer.h"
 #include "media/base/video_frame.h"
+#include "media/base/video_util.h"
 #include "media/filters/gpu_video_accelerator_factories.h"
 #include "media/video/video_encode_accelerator.h"
 
@@ -268,7 +269,8 @@
 
   for (unsigned int i = 0; i < input_count + kInputBufferExtraCount; ++i) {
     base::SharedMemory* shm =
-        gpu_factories_->CreateSharedMemory(input_coded_size.GetArea() * 3 / 2);
+        gpu_factories_->CreateSharedMemory(media::VideoFrame::AllocationSize(
+            media::VideoFrame::I420, input_coded_size));
     if (!shm) {
       DLOG(ERROR) << "Impl::RequireBitstreamBuffers(): "
                      "failed to create input buffer " << i;
@@ -396,47 +398,38 @@
 
   const int index = input_buffers_free_.back();
   base::SharedMemory* input_buffer = input_buffers_[index];
-
-  // 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
-  const uint8_t* src = next_frame->buffer(webrtc::kYPlane);
-  uint8* dst = reinterpret_cast<uint8*>(input_buffer->memory());
-  uint8* const y_dst = dst;
-  int width = input_frame_coded_size_.width();
-  int stride = next_frame->stride(webrtc::kYPlane);
-  for (int i = 0; i < next_frame->height(); ++i) {
-    memcpy(dst, src, width);
-    src += stride;
-    dst += width;
-  }
-  src = next_frame->buffer(webrtc::kUPlane);
-  width = input_frame_coded_size_.width() / 2;
-  stride = next_frame->stride(webrtc::kUPlane);
-  for (int i = 0; i < next_frame->height() / 2; ++i) {
-    memcpy(dst, src, width);
-    src += stride;
-    dst += width;
-  }
-  src = next_frame->buffer(webrtc::kVPlane);
-  width = input_frame_coded_size_.width() / 2;
-  stride = next_frame->stride(webrtc::kVPlane);
-  for (int i = 0; i < next_frame->height() / 2; ++i) {
-    memcpy(dst, src, width);
-    src += stride;
-    dst += width;
-  }
-
   scoped_refptr<media::VideoFrame> frame =
       media::VideoFrame::WrapExternalSharedMemory(
           media::VideoFrame::I420,
           input_frame_coded_size_,
           gfx::Rect(input_visible_size_),
           input_visible_size_,
-          y_dst,
+          reinterpret_cast<uint8*>(input_buffer->memory()),
+          input_buffer->mapped_size(),
           input_buffer->handle(),
           base::TimeDelta(),
           base::Bind(&RTCVideoEncoder::Impl::EncodeFrameFinished, this, index));
+  if (!frame) {
+    DLOG(ERROR) << "Impl::EncodeOneFrame(): failed to create frame";
+    NOTIFY_ERROR(media::VideoEncodeAccelerator::kPlatformFailureError);
+    return;
+  }
+
+  // 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());
 
   video_encoder_->Encode(frame, next_frame_keyframe);
   input_buffers_free_.pop_back();
@@ -485,6 +478,7 @@
     : video_codec_type_(type),
       video_codec_profile_(profile),
       gpu_factories_(gpu_factories),
+      weak_this_factory_(this),
       encoded_image_callback_(NULL),
       impl_status_(WEBRTC_VIDEO_CODEC_UNINITIALIZED) {
   DVLOG(1) << "RTCVideoEncoder(): profile=" << profile;
@@ -506,8 +500,8 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(!impl_);
 
-  weak_this_factory_.reset(new base::WeakPtrFactory<RTCVideoEncoder>(this));
-  impl_ = new Impl(weak_this_factory_->GetWeakPtr(), gpu_factories_);
+  weak_this_factory_.InvalidateWeakPtrs();
+  impl_ = new Impl(weak_this_factory_.GetWeakPtr(), gpu_factories_);
   base::WaitableEvent initialization_waiter(true, false);
   int32_t initialization_retval = WEBRTC_VIDEO_CODEC_UNINITIALIZED;
   gpu_factories_->GetMessageLoop()->PostTask(
@@ -578,7 +572,7 @@
     gpu_factories_->GetMessageLoop()->PostTask(
         FROM_HERE, base::Bind(&RTCVideoEncoder::Impl::Destroy, impl_));
     impl_ = NULL;
-    weak_this_factory_.reset();
+    weak_this_factory_.InvalidateWeakPtrs();
     impl_status_ = WEBRTC_VIDEO_CODEC_UNINITIALIZED;
   }
   return WEBRTC_VIDEO_CODEC_OK;
diff --git a/content/renderer/media/rtc_video_encoder.h b/content/renderer/media/rtc_video_encoder.h
index 22d4c50..3c15a55 100644
--- a/content/renderer/media/rtc_video_encoder.h
+++ b/content/renderer/media/rtc_video_encoder.h
@@ -80,9 +80,9 @@
   // Factory for creating VEAs, shared memory buffers, etc.
   scoped_refptr<RendererGpuVideoAcceleratorFactories> gpu_factories_;
 
-  // Weak pointer and factory for posting back VEA::Client notifications to
+  // Weak pointer factory for posting back VEA::Client notifications to
   // RTCVideoEncoder.
-  scoped_ptr<base::WeakPtrFactory<RTCVideoEncoder> > weak_this_factory_;
+  base::WeakPtrFactory<RTCVideoEncoder> weak_this_factory_;
 
   // webrtc::VideoEncoder encode complete callback.
   webrtc::EncodedImageCallback* encoded_image_callback_;
diff --git a/content/renderer/media/rtc_video_renderer.cc b/content/renderer/media/rtc_video_renderer.cc
index 725a778..f64ed02 100644
--- a/content/renderer/media/rtc_video_renderer.cc
+++ b/content/renderer/media/rtc_video_renderer.cc
@@ -9,6 +9,7 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/message_loop/message_loop_proxy.h"
+#include "content/renderer/media/native_handle_impl.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_util.h"
 #include "third_party/libjingle/source/talk/media/base/videoframe.h"
@@ -84,25 +85,30 @@
                        "timestamp_ms",
                        timestamp.InMilliseconds());
 
-  gfx::Size size(frame->GetWidth(), frame->GetHeight());
-  scoped_refptr<media::VideoFrame> video_frame =
-      media::VideoFrame::CreateFrame(media::VideoFrame::YV12,
-                                     size,
-                                     gfx::Rect(size),
-                                     size,
-                                     timestamp);
+  scoped_refptr<media::VideoFrame> video_frame;
+  if (frame->GetNativeHandle() != NULL) {
+    NativeHandleImpl* handle =
+        static_cast<NativeHandleImpl*>(frame->GetNativeHandle());
+    video_frame = static_cast<media::VideoFrame*>(handle->GetHandle());
+    video_frame->SetTimestamp(timestamp);
+  } else {
+    gfx::Size size(frame->GetWidth(), frame->GetHeight());
+    video_frame = media::VideoFrame::CreateFrame(
+        media::VideoFrame::YV12, size, gfx::Rect(size), size, timestamp);
 
-  // Aspect ratio unsupported; DCHECK when there are non-square pixels.
-  DCHECK_EQ(frame->GetPixelWidth(), 1u);
-  DCHECK_EQ(frame->GetPixelHeight(), 1u);
+    // Aspect ratio unsupported; DCHECK when there are non-square pixels.
+    DCHECK_EQ(frame->GetPixelWidth(), 1u);
+    DCHECK_EQ(frame->GetPixelHeight(), 1u);
 
-  int y_rows = frame->GetHeight();
-  int uv_rows = frame->GetHeight() / 2;  // YV12 format.
-  CopyYPlane(frame->GetYPlane(), frame->GetYPitch(), y_rows, video_frame.get());
-  CopyUPlane(
-      frame->GetUPlane(), frame->GetUPitch(), uv_rows, video_frame.get());
-  CopyVPlane(
-      frame->GetVPlane(), frame->GetVPitch(), uv_rows, video_frame.get());
+    int y_rows = frame->GetHeight();
+    int uv_rows = frame->GetHeight() / 2;  // YV12 format.
+    CopyYPlane(
+        frame->GetYPlane(), frame->GetYPitch(), y_rows, video_frame.get());
+    CopyUPlane(
+        frame->GetUPlane(), frame->GetUPitch(), uv_rows, video_frame.get());
+    CopyVPlane(
+        frame->GetVPlane(), frame->GetVPitch(), uv_rows, video_frame.get());
+  }
 
   message_loop_proxy_->PostTask(
       FROM_HERE, base::Bind(&RTCVideoRenderer::DoRenderFrameOnMainThread,
diff --git a/content/renderer/media/webmediaplayer_impl.cc b/content/renderer/media/webmediaplayer_impl.cc
index c651764..a99c3ae 100644
--- a/content/renderer/media/webmediaplayer_impl.cc
+++ b/content/renderer/media/webmediaplayer_impl.cc
@@ -723,7 +723,7 @@
            << std::string(reinterpret_cast<const char*>(init_data),
                           static_cast<size_t>(init_data_length));
 
-  if (!IsSupportedKeySystem(key_system))
+  if (!IsConcreteSupportedKeySystem(key_system))
     return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
 
   // We do not support run-time switching between key systems for now.
@@ -778,7 +778,7 @@
            << " [" << session_id.utf8().data() << "]";
 
 
-  if (!IsSupportedKeySystem(key_system))
+  if (!IsConcreteSupportedKeySystem(key_system))
     return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
 
   if (current_key_system_.isEmpty() || key_system != current_key_system_)
@@ -802,7 +802,7 @@
 WebMediaPlayerImpl::CancelKeyRequestInternal(
     const WebString& key_system,
     const WebString& session_id) {
-  if (!IsSupportedKeySystem(key_system))
+  if (!IsConcreteSupportedKeySystem(key_system))
     return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
 
   if (current_key_system_.isEmpty() || key_system != current_key_system_)
@@ -923,8 +923,7 @@
 
 void WebMediaPlayerImpl::OnNeedKey(const std::string& session_id,
                                    const std::string& type,
-                                   scoped_ptr<uint8[]> init_data,
-                                   int init_data_size) {
+                                   const std::vector<uint8>& init_data) {
   DCHECK(main_loop_->BelongsToCurrentThread());
 
   // Do not fire NeedKey event if encrypted media is not enabled.
@@ -937,10 +936,11 @@
   if (init_data_type_.empty())
     init_data_type_ = type;
 
+  const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
   GetClient()->keyNeeded(WebString(),
                          WebString::fromUTF8(session_id),
-                         init_data.get(),
-                         init_data_size);
+                         init_data_ptr,
+                         init_data.size());
 }
 
 scoped_ptr<media::TextTrack>
diff --git a/content/renderer/media/webmediaplayer_impl.h b/content/renderer/media/webmediaplayer_impl.h
index e9ad902..270186b 100644
--- a/content/renderer/media/webmediaplayer_impl.h
+++ b/content/renderer/media/webmediaplayer_impl.h
@@ -196,8 +196,7 @@
                     const std::string& default_url);
   void OnNeedKey(const std::string& type,
                  const std::string& session_id,
-                 scoped_ptr<uint8[]> init_data,
-                 int init_data_size);
+                 const std::vector<uint8>& init_data);
   scoped_ptr<media::TextTrack> OnTextTrack(media::TextKind kind,
                                            const std::string& label,
                                            const std::string& language);
diff --git a/content/renderer/media/webmediasource_impl.cc b/content/renderer/media/webmediasource_impl.cc
index 275b29a..a420f9d 100644
--- a/content/renderer/media/webmediasource_impl.cc
+++ b/content/renderer/media/webmediasource_impl.cc
@@ -11,12 +11,12 @@
 #include "third_party/WebKit/public/platform/WebString.h"
 
 using ::WebKit::WebString;
-using ::WebKit::WebMediaSourceNew;
+using ::WebKit::WebMediaSource;
 
 namespace content {
 
 #define COMPILE_ASSERT_MATCHING_STATUS_ENUM(webkit_name, chromium_name) \
-  COMPILE_ASSERT(static_cast<int>(WebMediaSourceNew::webkit_name) == \
+  COMPILE_ASSERT(static_cast<int>(WebMediaSource::webkit_name) == \
                  static_cast<int>(media::ChunkDemuxer::chromium_name),  \
                  mismatching_status_enums)
 COMPILE_ASSERT_MATCHING_STATUS_ENUM(AddStatusOk, kOk);
@@ -33,7 +33,7 @@
 
 WebMediaSourceImpl::~WebMediaSourceImpl() {}
 
-WebMediaSourceNew::AddStatus WebMediaSourceImpl::addSourceBuffer(
+WebMediaSource::AddStatus WebMediaSourceImpl::addSourceBuffer(
     const WebKit::WebString& type,
     const WebKit::WebVector<WebKit::WebString>& codecs,
     WebKit::WebSourceBuffer** source_buffer) {
@@ -41,11 +41,11 @@
   std::vector<std::string> new_codecs(codecs.size());
   for (size_t i = 0; i < codecs.size(); ++i)
     new_codecs[i] = codecs[i].utf8().data();
-  WebMediaSourceNew::AddStatus result =
-      static_cast<WebMediaSourceNew::AddStatus>(
+  WebMediaSource::AddStatus result =
+      static_cast<WebMediaSource::AddStatus>(
           demuxer_->AddId(id, type.utf8().data(), new_codecs));
 
-  if (result == WebMediaSourceNew::AddStatusOk)
+  if (result == WebMediaSource::AddStatusOk)
     *source_buffer = new WebSourceBufferImpl(id, demuxer_);
 
   return result;
@@ -61,16 +61,16 @@
 }
 
 void WebMediaSourceImpl::markEndOfStream(
-    WebMediaSourceNew::EndOfStreamStatus status) {
+    WebMediaSource::EndOfStreamStatus status) {
   media::PipelineStatus pipeline_status = media::PIPELINE_OK;
 
   switch (status) {
-    case WebMediaSourceNew::EndOfStreamStatusNoError:
+    case WebMediaSource::EndOfStreamStatusNoError:
       break;
-    case WebMediaSourceNew::EndOfStreamStatusNetworkError:
+    case WebMediaSource::EndOfStreamStatusNetworkError:
       pipeline_status = media::PIPELINE_ERROR_NETWORK;
       break;
-    case WebMediaSourceNew::EndOfStreamStatusDecodeError:
+    case WebMediaSource::EndOfStreamStatusDecodeError:
       pipeline_status = media::PIPELINE_ERROR_DECODE;
       break;
     default:
diff --git a/content/renderer/media/webmediasource_impl.h b/content/renderer/media/webmediasource_impl.h
index a09790b..8d67994 100644
--- a/content/renderer/media/webmediasource_impl.h
+++ b/content/renderer/media/webmediasource_impl.h
@@ -17,12 +17,12 @@
 
 namespace content {
 
-class WebMediaSourceImpl : public WebKit::WebMediaSourceNew {
+class WebMediaSourceImpl : public WebKit::WebMediaSource {
  public:
   WebMediaSourceImpl(media::ChunkDemuxer* demuxer, media::LogCB log_cb);
   virtual ~WebMediaSourceImpl();
 
-  // WebKit::WebMediaSourceNew implementation.
+  // WebKit::WebMediaSource implementation.
   virtual AddStatus addSourceBuffer(
       const WebKit::WebString& type,
       const WebKit::WebVector<WebKit::WebString>& codecs,
diff --git a/content/renderer/media/webrtc_audio_device_impl.cc b/content/renderer/media/webrtc_audio_device_impl.cc
index 93e024b..47e72c8 100644
--- a/content/renderer/media/webrtc_audio_device_impl.cc
+++ b/content/renderer/media/webrtc_audio_device_impl.cc
@@ -64,7 +64,9 @@
   int total_delay_ms = 0;
   {
     base::AutoLock auto_lock(lock_);
-    if (!recording_)
+    // Return immediately when not recording or |channels| is empty.
+    // See crbug.com/274017: renderer crash dereferencing invalid channels[0].
+    if (!recording_ || channels.empty())
       return 0;
 
     // Store the reported audio delay locally.
diff --git a/content/renderer/pepper/message_channel.cc b/content/renderer/pepper/message_channel.cc
index a468880..efcdfb3 100644
--- a/content/renderer/pepper/message_channel.cc
+++ b/content/renderer/pepper/message_channel.cc
@@ -354,9 +354,10 @@
       // Calling WebBindings::toV8Value creates a wrapper around NPVariant so it
       // shouldn't result in a deep copy.
       v8::Handle<v8::Value> v8_value = WebBindings::toV8Value(variant);
-      V8VarConverter().FromV8Value(v8_value, v8::Context::GetCurrent(),
+      V8VarConverter(instance_->pp_instance()).FromV8Value(
+          v8_value, v8::Context::GetCurrent(),
           base::Bind(&MessageChannel::NPVariantToPPVarComplete,
-              weak_ptr_factory_.GetWeakPtr(), result_iterator));
+                     weak_ptr_factory_.GetWeakPtr(), result_iterator));
       return;
     }
   }
@@ -383,7 +384,8 @@
   v8::Context::Scope context_scope(context);
 
   v8::Handle<v8::Value> v8_val;
-  if (!V8VarConverter().ToV8Value(message_data, context, &v8_val)) {
+  if (!V8VarConverter(instance_->pp_instance()).ToV8Value(
+          message_data, context, &v8_val)) {
     PpapiGlobals::Get()->LogWithSource(instance_->pp_instance(),
         PP_LOGLEVEL_ERROR, std::string(), kVarToV8ConversionError);
     return;
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc
index d88a10c..49daf81 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.cc
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -1380,13 +1380,19 @@
 }
 
 void PepperPluginInstanceImpl::SendFocusChangeNotification() {
-  // This call can happen during PepperPluginIn>stanceImpl destruction, because
-  // WebKit informs the plugin it's losing focus. See crbug.com/236574
-  if (!instance_interface_)
-    return;
+  // Keep a reference on the stack. RenderViewImpl::PepperFocusChanged may
+  // remove the <embed> from the DOM, which will make the PepperWebPluginImpl
+  // drop its reference, usually the last one. This is similar to possible
+  // plugin behavior described at the NOTE above Delete().
+  scoped_refptr<PepperPluginInstanceImpl> ref(this);
+
   bool has_focus = PluginHasFocus();
   render_view_->PepperFocusChanged(this, has_focus);
-  instance_interface_->DidChangeFocus(pp_instance(), PP_FromBool(has_focus));
+
+  // instance_interface_ may have been cleared in Delete() if the
+  // PepperWebPluginImpl is destroyed.
+  if (instance_interface_)
+    instance_interface_->DidChangeFocus(pp_instance(), PP_FromBool(has_focus));
 }
 
 void PepperPluginInstanceImpl::UpdateTouchEventRequest() {
diff --git a/content/renderer/pepper/ppb_tcp_socket_private_impl.cc b/content/renderer/pepper/ppb_tcp_socket_private_impl.cc
deleted file mode 100644
index 2b47e64..0000000
--- a/content/renderer/pepper/ppb_tcp_socket_private_impl.cc
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/renderer/pepper/ppb_tcp_socket_private_impl.h"
-
-#include "content/common/pepper_messages.h"
-#include "content/renderer/pepper/host_globals.h"
-#include "content/renderer/pepper/pepper_plugin_instance_impl.h"
-#include "content/renderer/render_thread_impl.h"
-#include "ppapi/proxy/ppapi_messages.h"
-#include "ppapi/shared_impl/socket_option_data.h"
-
-namespace content {
-
-PPB_TCPSocket_Private_Impl::PPB_TCPSocket_Private_Impl(
-    PP_Instance instance,
-    uint32 socket_id,
-    int routing_id)
-    : ppapi::TCPSocketPrivateImpl(instance, socket_id),
-      routing_id_(routing_id) {
-  ChildThread::current()->AddRoute(routing_id, this);
-}
-
-PPB_TCPSocket_Private_Impl::~PPB_TCPSocket_Private_Impl() {
-  ChildThread::current()->RemoveRoute(routing_id_);
-  Disconnect();
-}
-
-PP_Resource PPB_TCPSocket_Private_Impl::CreateResource(PP_Instance instance) {
-  int routing_id = RenderThreadImpl::current()->GenerateRoutingID();
-  uint32 socket_id = 0;
-  RenderThreadImpl::current()->Send(new PpapiHostMsg_PPBTCPSocket_CreatePrivate(
-      routing_id, 0, &socket_id));
-  if (!socket_id)
-    return 0;
-
-  return (new PPB_TCPSocket_Private_Impl(
-      instance, socket_id, routing_id))->GetReference();
-}
-
-void PPB_TCPSocket_Private_Impl::SendConnect(const std::string& host,
-                                             uint16_t port) {
-  RenderThreadImpl::current()->Send(new PpapiHostMsg_PPBTCPSocket_Connect(
-      routing_id_, socket_id_, host, port));
-}
-
-void PPB_TCPSocket_Private_Impl::SendConnectWithNetAddress(
-    const PP_NetAddress_Private& addr) {
-  RenderThreadImpl::current()->Send(
-      new PpapiHostMsg_PPBTCPSocket_ConnectWithNetAddress(
-          routing_id_, socket_id_, addr));
-}
-
-void PPB_TCPSocket_Private_Impl::SendSSLHandshake(
-    const std::string& server_name,
-    uint16_t server_port,
-    const std::vector<std::vector<char> >& trusted_certs,
-    const std::vector<std::vector<char> >& untrusted_certs) {
-  RenderThreadImpl::current()->Send(new PpapiHostMsg_PPBTCPSocket_SSLHandshake(
-      socket_id_, server_name, server_port, trusted_certs, untrusted_certs));
-}
-
-void PPB_TCPSocket_Private_Impl::SendRead(int32_t bytes_to_read) {
-  RenderThreadImpl::current()->Send(new PpapiHostMsg_PPBTCPSocket_Read(
-      socket_id_, bytes_to_read));
-}
-
-
-void PPB_TCPSocket_Private_Impl::SendWrite(const std::string& buffer) {
-  RenderThreadImpl::current()->Send(
-      new PpapiHostMsg_PPBTCPSocket_Write(socket_id_, buffer));
-}
-
-void PPB_TCPSocket_Private_Impl::SendDisconnect() {
-  RenderThreadImpl::current()->Send(
-      new PpapiHostMsg_PPBTCPSocket_Disconnect(socket_id_));
-}
-
-void PPB_TCPSocket_Private_Impl::SendSetOption(
-    PP_TCPSocket_Option name,
-    const ppapi::SocketOptionData& value) {
-  RenderThreadImpl::current()->Send(
-      new PpapiHostMsg_PPBTCPSocket_SetOption(socket_id_, name, value));
-}
-
-bool PPB_TCPSocket_Private_Impl::OnMessageReceived(
-    const IPC::Message& message) {
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(PPB_TCPSocket_Private_Impl, message)
-    IPC_MESSAGE_HANDLER(PpapiMsg_PPBTCPSocket_ConnectACK, OnTCPSocketConnectACK)
-    IPC_MESSAGE_HANDLER(PpapiMsg_PPBTCPSocket_SSLHandshakeACK,
-                        OnTCPSocketSSLHandshakeACK)
-    IPC_MESSAGE_HANDLER(PpapiMsg_PPBTCPSocket_ReadACK, OnTCPSocketReadACK)
-    IPC_MESSAGE_HANDLER(PpapiMsg_PPBTCPSocket_WriteACK, OnTCPSocketWriteACK)
-    IPC_MESSAGE_HANDLER(PpapiMsg_PPBTCPSocket_SetOptionACK,
-                        OnTCPSocketSetOptionACK)
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-  return handled;
-}
-
-void PPB_TCPSocket_Private_Impl::OnTCPSocketConnectACK(
-    uint32 plugin_dispatcher_id,
-    uint32 socket_id,
-    int32_t result,
-    const PP_NetAddress_Private& local_addr,
-    const PP_NetAddress_Private& remote_addr) {
-  OnConnectCompleted(result, local_addr, remote_addr);
-}
-
-void PPB_TCPSocket_Private_Impl::OnTCPSocketSSLHandshakeACK(
-    uint32 plugin_dispatcher_id,
-    uint32 socket_id,
-    bool succeeded,
-    const ppapi::PPB_X509Certificate_Fields& certificate_fields) {
-  OnSSLHandshakeCompleted(succeeded, certificate_fields);
-}
-
-void PPB_TCPSocket_Private_Impl::OnTCPSocketReadACK(uint32 plugin_dispatcher_id,
-                                                    uint32 socket_id,
-                                                    int32_t result,
-                                                    const std::string& data) {
-  OnReadCompleted(result, data);
-}
-
-void PPB_TCPSocket_Private_Impl::OnTCPSocketWriteACK(
-    uint32 plugin_dispatcher_id,
-    uint32 socket_id,
-    int32_t result) {
-  OnWriteCompleted(result);
-}
-
-void PPB_TCPSocket_Private_Impl::OnTCPSocketSetOptionACK(
-    uint32 plugin_dispatcher_id,
-    uint32 socket_id,
-    int32_t result) {
-  OnSetOptionCompleted(result);
-}
-
-}  // namespace content
diff --git a/content/renderer/pepper/ppb_tcp_socket_private_impl.h b/content/renderer/pepper/ppb_tcp_socket_private_impl.h
deleted file mode 100644
index 2ac6981..0000000
--- a/content/renderer/pepper/ppb_tcp_socket_private_impl.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_RENDERER_PEPPER_PPB_TCP_SOCKET_PRIVATE_IMPL_H_
-#define CONTENT_RENDERER_PEPPER_PPB_TCP_SOCKET_PRIVATE_IMPL_H_
-
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "ipc/ipc_listener.h"
-#include "ppapi/shared_impl/private/tcp_socket_private_impl.h"
-
-namespace content {
-
-class PPB_TCPSocket_Private_Impl : public ppapi::TCPSocketPrivateImpl,
-                                   public IPC::Listener {
- public:
-  static PP_Resource CreateResource(PP_Instance instance);
-
-  virtual void SendConnect(const std::string& host, uint16_t port) OVERRIDE;
-  virtual void SendConnectWithNetAddress(
-      const PP_NetAddress_Private& addr) OVERRIDE;
-  virtual void SendSSLHandshake(
-      const std::string& server_name,
-      uint16_t server_port,
-      const std::vector<std::vector<char> >& trusted_certs,
-      const std::vector<std::vector<char> >& untrusted_certs) OVERRIDE;
-  virtual void SendRead(int32_t bytes_to_read) OVERRIDE;
-  virtual void SendWrite(const std::string& buffer) OVERRIDE;
-  virtual void SendDisconnect() OVERRIDE;
-  virtual void SendSetOption(PP_TCPSocket_Option name,
-                             const ppapi::SocketOptionData& value) OVERRIDE;
-
- private:
-  PPB_TCPSocket_Private_Impl(PP_Instance instance,
-                             uint32 socket_id,
-                             int routing_id);
-  virtual ~PPB_TCPSocket_Private_Impl();
-
-  // IPC::Listener implementation.
-  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
-
-  void OnTCPSocketConnectACK(uint32 plugin_dispatcher_id,
-                             uint32 socket_id,
-                             int32_t result,
-                             const PP_NetAddress_Private& local_addr,
-                             const PP_NetAddress_Private& remote_addr);
-  void OnTCPSocketSSLHandshakeACK(
-      uint32 plugin_dispatcher_id,
-      uint32 socket_id,
-      bool succeeded,
-      const ppapi::PPB_X509Certificate_Fields& certificate_fields);
-  void OnTCPSocketReadACK(uint32 plugin_dispatcher_id,
-                          uint32 socket_id,
-                          int32_t result,
-                          const std::string& data);
-  void OnTCPSocketWriteACK(uint32 plugin_dispatcher_id,
-                           uint32 socket_id,
-                           int32_t result);
-  void OnTCPSocketSetOptionACK(uint32 plugin_dispatcher_id,
-                               uint32 socket_id,
-                               int32_t result);
-
-  int routing_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(PPB_TCPSocket_Private_Impl);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_PEPPER_PPB_TCP_SOCKET_PRIVATE_IMPL_H_
diff --git a/content/renderer/pepper/renderer_ppapi_host_impl.cc b/content/renderer/pepper/renderer_ppapi_host_impl.cc
index b94eac7..5c41003 100644
--- a/content/renderer/pepper/renderer_ppapi_host_impl.cc
+++ b/content/renderer/pepper/renderer_ppapi_host_impl.cc
@@ -4,8 +4,10 @@
 
 #include "content/renderer/pepper/renderer_ppapi_host_impl.h"
 
+#include "base/bind.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/message_loop/message_loop.h"
 #include "base/process/process_handle.h"
 #include "content/common/sandbox_util.h"
 #include "content/renderer/pepper/fullscreen_container.h"
@@ -233,7 +235,8 @@
   PepperBrowserConnection* browser_connection =
       PepperBrowserConnection::Get(render_view);
   if (!browser_connection) {
-    callback.Run(std::vector<int>(nested_msgs.size(), 0));
+    base::MessageLoop::current()->PostTask(FROM_HERE,
+        base::Bind(callback, std::vector<int>(nested_msgs.size(), 0)));
   } else {
     browser_connection->SendBrowserCreate(module_->GetPluginChildId(),
                                           instance,
diff --git a/content/renderer/pepper/resource_converter.cc b/content/renderer/pepper/resource_converter.cc
index 4df384a..4f9f5d9 100644
--- a/content/renderer/pepper/resource_converter.cc
+++ b/content/renderer/pepper/resource_converter.cc
@@ -6,22 +6,70 @@
 
 #include "base/bind.h"
 #include "base/message_loop/message_loop.h"
+#include "content/public/renderer/renderer_ppapi_host.h"
+#include "ipc/ipc_message.h"
+#include "ppapi/shared_impl/scoped_pp_var.h"
+
+namespace {
+
+void FlushComplete(
+    const base::Callback<void(bool)>& callback,
+    const std::vector<PP_Var>& browser_host_resource_vars,
+    const std::vector<int>& pending_host_ids) {
+  CHECK(browser_host_resource_vars.size() == pending_host_ids.size());
+  for (size_t i = 0; i < browser_host_resource_vars.size(); ++i) {
+    // TODO(raymes): Set the pending browser host ID in the resource var.
+  }
+  callback.Run(true);
+}
+
+}  // namespace
 
 namespace content {
 
-ResourceConverter::~ResourceConverter() {
-}
+ResourceConverter::~ResourceConverter() {}
 
-ResourceConverterImpl::ResourceConverterImpl() {
+ResourceConverterImpl::ResourceConverterImpl(PP_Instance instance,
+                                             RendererPpapiHost* host)
+    : instance_(instance),
+      host_(host) {
 }
 
 ResourceConverterImpl::~ResourceConverterImpl() {
+  // Verify Flush() was called.
+  DCHECK(browser_host_create_messages_.empty());
+  DCHECK(browser_host_resource_vars.empty());
 }
 
-void ResourceConverterImpl::ShutDown(
-    const base::Callback<void(bool)>& callback) {
-  // TODO(raymes): Implement the creation of a browser resource host here.
-  base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(callback, true));
+bool ResourceConverterImpl::FromV8Value(v8::Handle<v8::Value> val,
+                                        v8::Handle<v8::Context> context,
+                                        PP_Var* result) {
+
+  return false;
+}
+
+void ResourceConverterImpl::Flush(const base::Callback<void(bool)>& callback) {
+  host_->CreateBrowserResourceHosts(
+      instance_,
+      browser_host_create_messages_,
+      base::Bind(&FlushComplete, callback, browser_host_resource_vars));
+  browser_host_create_messages_.clear();
+  browser_host_resource_vars.clear();
+}
+
+PP_Var ResourceConverterImpl::CreateResourceVar(
+    const IPC::Message& create_message) {
+  // TODO(raymes): Create a ResourceVar here.
+  return PP_MakeUndefined();
+}
+
+PP_Var ResourceConverterImpl::CreateResourceVarWithBrowserHost(
+    const IPC::Message& create_message,
+    const IPC::Message& browser_host_create_message) {
+  PP_Var result = CreateResourceVar(create_message);
+  browser_host_create_messages_.push_back(browser_host_create_message);
+  browser_host_resource_vars.push_back(result);
+  return result;
 }
 
 }  // namespace content
diff --git a/content/renderer/pepper/resource_converter.h b/content/renderer/pepper/resource_converter.h
index 28d06ef..7d9fd1e 100644
--- a/content/renderer/pepper/resource_converter.h
+++ b/content/renderer/pepper/resource_converter.h
@@ -5,33 +5,71 @@
 #ifndef CONTENT_RENDERER_PEPPER_RESOURCE_CONVERTER_H
 #define CONTENT_RENDERER_PEPPER_RESOURCE_CONVERTER_H
 
+#include <vector>
+
 #include "base/basictypes.h"
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "content/common/content_export.h"
+#include "ppapi/c/pp_instance.h"
+#include "ppapi/c/pp_var.h"
+#include "v8/include/v8.h"
+
+namespace IPC {
+class Message;
+}
+
+namespace ppapi {
+class ScopedPPVar;
+}
 
 namespace content {
 
+class RendererPpapiHost;
+
 // This class is responsible for converting V8 vars to Pepper resources.
 class CONTENT_EXPORT ResourceConverter {
  public:
   virtual ~ResourceConverter();
 
- // ShutDown must be called before any vars created by the ResourceConverter
+  // Flush() must be called before any vars created by the ResourceConverter
   // are valid. It handles creating any resource hosts that need to be created.
-  virtual void ShutDown(const base::Callback<void(bool)>& callback) = 0;
+  virtual void Flush(const base::Callback<void(bool)>& callback) = 0;
 };
 
 class ResourceConverterImpl : public ResourceConverter {
  public:
-  ResourceConverterImpl();
+  ResourceConverterImpl(PP_Instance instance, RendererPpapiHost* host);
   virtual ~ResourceConverterImpl();
-  virtual void ShutDown(const base::Callback<void(bool)>& callback) OVERRIDE;
+  virtual void Flush(const base::Callback<void(bool)>& callback) OVERRIDE;
+
+  bool FromV8Value(v8::Handle<v8::Value> val,
+                   v8::Handle<v8::Context> context,
+                   PP_Var* result);
 
  private:
+  // Creates a resource var with the given message to be sent to the plugin.
+  PP_Var CreateResourceVar(const IPC::Message& create_message);
+  // Creates a resource var with the given message to send to the plugin and a
+  // message to create the browser host.
+  PP_Var CreateResourceVarWithBrowserHost(
+      const IPC::Message& create_message,
+      const IPC::Message& browser_host_create_message);
+
+  // The instance this ResourceConverter is associated with.
+  PP_Instance instance_;
+  // The RendererPpapiHost to use to create browser hosts.
+  RendererPpapiHost* host_;
+
+  // A list of the messages to create the browser hosts. This is a parallel
+  // array to |browser_host_resource_vars|. It is kept as a parallel array so
+  // that it can be conveniently passed to |CreateBrowserResourceHosts|.
+  std::vector<IPC::Message> browser_host_create_messages_;
+  // A list of the resource vars associated with browser hosts.
+  std::vector<PP_Var> browser_host_resource_vars;
+
   DISALLOW_COPY_AND_ASSIGN(ResourceConverterImpl);
 };
 
 }  // namespace content
-
 #endif  // CONTENT_RENDERER_PEPPER_RESOURCE_CONVERTER_H
diff --git a/content/renderer/pepper/resource_creation_impl.cc b/content/renderer/pepper/resource_creation_impl.cc
index 8cc4309..2ac29a8 100644
--- a/content/renderer/pepper/resource_creation_impl.cc
+++ b/content/renderer/pepper/resource_creation_impl.cc
@@ -14,7 +14,6 @@
 #include "content/renderer/pepper/ppb_image_data_impl.h"
 #include "content/renderer/pepper/ppb_network_monitor_private_impl.h"
 #include "content/renderer/pepper/ppb_scrollbar_impl.h"
-#include "content/renderer/pepper/ppb_tcp_socket_private_impl.h"
 #include "content/renderer/pepper/ppb_video_decoder_impl.h"
 #include "content/renderer/pepper/ppb_x509_certificate_private_impl.h"
 #include "ppapi/c/pp_size.h"
@@ -167,12 +166,6 @@
       segment_offsets, target_segment, selection_start, selection_end);
 }
 
-PP_Resource ResourceCreationImpl::CreateIsolatedFileSystem(PP_Instance instance,
-                                                           const char* fsid) {
-  NOTIMPLEMENTED();  // no need to support in-process
-  return 0;
-}
-
 PP_Resource ResourceCreationImpl::CreateKeyboardInputEvent(
     PP_Instance instance,
     PP_InputEvent_Type type,
@@ -261,7 +254,7 @@
 }
 
 PP_Resource ResourceCreationImpl::CreateTCPSocketPrivate(PP_Instance instance) {
-  return PPB_TCPSocket_Private_Impl::CreateResource(instance);
+  return 0;  // Not supported in-process.
 }
 
 PP_Resource ResourceCreationImpl::CreateUDPSocket(PP_Instance instance) {
diff --git a/content/renderer/pepper/resource_creation_impl.h b/content/renderer/pepper/resource_creation_impl.h
index dc05890..baaf1a0 100644
--- a/content/renderer/pepper/resource_creation_impl.h
+++ b/content/renderer/pepper/resource_creation_impl.h
@@ -73,8 +73,6 @@
                                           int32_t target_segment,
                                           uint32_t selection_start,
                                           uint32_t selection_end) OVERRIDE;
-  virtual PP_Resource CreateIsolatedFileSystem(PP_Instance instance,
-                                               const char* fsid) OVERRIDE;
   virtual PP_Resource CreateKeyboardInputEvent(
       PP_Instance instance,
       PP_InputEvent_Type type,
diff --git a/content/renderer/pepper/v8_var_converter.cc b/content/renderer/pepper/v8_var_converter.cc
index cf56da4..38447a8 100644
--- a/content/renderer/pepper/v8_var_converter.cc
+++ b/content/renderer/pepper/v8_var_converter.cc
@@ -13,6 +13,7 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
+#include "content/public/renderer/renderer_ppapi_host.h"
 #include "content/renderer/pepper/host_array_buffer_var.h"
 #include "content/renderer/pepper/resource_converter.h"
 #include "ppapi/shared_impl/array_var.h"
@@ -234,12 +235,14 @@
 
 }  // namespace
 
-V8VarConverter::V8VarConverter()
-    : message_loop_proxy_(base::MessageLoopProxy::current()),
-      resource_converter_(new ResourceConverterImpl()) {
+V8VarConverter::V8VarConverter(PP_Instance instance)
+    : message_loop_proxy_(base::MessageLoopProxy::current()) {
+  resource_converter_.reset(new ResourceConverterImpl(
+      instance, RendererPpapiHost::GetForPPInstance(instance)));
 }
 
 V8VarConverter::V8VarConverter(
+    PP_Instance instance,
     const scoped_refptr<base::MessageLoopProxy>& message_loop_proxy,
     scoped_ptr<ResourceConverter> resource_converter)
     : message_loop_proxy_(message_loop_proxy),
@@ -500,7 +503,7 @@
       }
     }
   }
-  resource_converter_->ShutDown(base::Bind(callback, root));
+  resource_converter_->Flush(base::Bind(callback, root));
 }
 
 }  // namespace content
diff --git a/content/renderer/pepper/v8_var_converter.h b/content/renderer/pepper/v8_var_converter.h
index a9865c3..2b879fa 100644
--- a/content/renderer/pepper/v8_var_converter.h
+++ b/content/renderer/pepper/v8_var_converter.h
@@ -10,6 +10,7 @@
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/message_loop/message_loop_proxy.h"
+#include "ppapi/c/pp_instance.h"
 #include "ppapi/c/pp_var.h"
 #include "v8/include/v8.h"
 #include "content/common/content_export.h"
@@ -24,9 +25,10 @@
 
 class CONTENT_EXPORT V8VarConverter {
  public:
-  V8VarConverter();
+  explicit V8VarConverter(PP_Instance instance);
   // Constructor for testing.
   V8VarConverter(
+      PP_Instance instance,
       const scoped_refptr<base::MessageLoopProxy>& message_loop_proxy,
       scoped_ptr<ResourceConverter> resource_converter);
   ~V8VarConverter();
diff --git a/content/renderer/pepper/v8_var_converter_unittest.cc b/content/renderer/pepper/v8_var_converter_unittest.cc
index c27a3b9..095d70c 100644
--- a/content/renderer/pepper/v8_var_converter_unittest.cc
+++ b/content/renderer/pepper/v8_var_converter_unittest.cc
@@ -45,7 +45,7 @@
 class MockResourceConverter : public content::ResourceConverter {
  public:
   virtual ~MockResourceConverter() {}
-  virtual void ShutDown(const base::Callback<void(bool)>& callback) OVERRIDE {
+  virtual void Flush(const base::Callback<void(bool)>& callback) OVERRIDE {
     callback.Run(true);
   }
 };
@@ -149,7 +149,9 @@
         conversion_event_(true, false),
         callback_thread_("callback_thread") {
     callback_thread_.Start();
+    PP_Instance dummy = 1234;
     converter_.reset(new V8VarConverter(
+        dummy,
         callback_thread_.message_loop_proxy(),
         scoped_ptr<ResourceConverter>(new MockResourceConverter).Pass()));
   }
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 07620e7..115c478 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -4,14 +4,17 @@
 
 #include "content/renderer/render_frame_impl.h"
 
+#include "base/command_line.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "content/child/appcache/appcache_dispatcher.h"
 #include "content/child/quota_dispatcher.h"
 #include "content/child/request_extra_data.h"
 #include "content/common/socket_stream_handle_data.h"
+#include "content/common/swapped_out_messages.h"
 #include "content/common/view_messages.h"
 #include "content/public/common/content_constants.h"
+#include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/renderer/content_renderer_client.h"
 #include "content/public/renderer/document_state.h"
@@ -94,29 +97,33 @@
 // RenderFrameImpl ----------------------------------------------------------
 RenderFrameImpl::RenderFrameImpl(RenderViewImpl* render_view, int routing_id)
     : render_view_(render_view),
-      routing_id_(routing_id) {
+      routing_id_(routing_id),
+      is_swapped_out_(false),
+      is_detaching_(false) {
 }
 
 RenderFrameImpl::~RenderFrameImpl() {
 }
 
 int RenderFrameImpl::GetRoutingID() const {
-  // TODO(nasko): Until we register RenderFrameHost in the browser process as
-  // a listener, we must route all messages to the RenderViewHost, so use the
-  // routing id of the RenderView for now.
-  return render_view_->GetRoutingID();
+  return routing_id_;
 }
 
 bool RenderFrameImpl::Send(IPC::Message* message) {
-  // TODO(nasko): Move away from using the RenderView's Send method once we
-  // have enough infrastructure and state to make the right checks here.
-  return render_view_->Send(message);
+  if (is_detaching_ ||
+      (is_swapped_out_ &&
+       !SwappedOutMessages::CanSendWhileSwappedOut(message))) {
+    delete message;
+    return false;
+  }
+
+  return RenderThread::Get()->Send(message);
 }
 
 bool RenderFrameImpl::OnMessageReceived(const IPC::Message& msg) {
-  // Pass the message up to the RenderView, until we have enough
-  // infrastructure to start processing messages in this object.
-  return render_view_->OnMessageReceived(msg);
+  // TODO(ajwong): Fill in with message handlers as various components
+  // are migrated over to understand frames.
+  return false;
 }
 
 // WebKit::WebFrameClient implementation -------------------------------------
@@ -164,10 +171,10 @@
   params.url = url;
   params.name = name;
   params.document_id = document_id;
-  params.render_view_route_id = GetRoutingID();
+  params.render_view_route_id = render_view_->GetRoutingID();
   params.route_id = MSG_ROUTING_NONE;
   params.script_resource_appcache_id = 0;
-  Send(new ViewHostMsg_LookupSharedWorker(
+  render_view_->Send(new ViewHostMsg_LookupSharedWorker(
       params, &exists, &route_id, &url_mismatch));
   if (url_mismatch) {
     return NULL;
@@ -176,7 +183,7 @@
                                     document_id,
                                     exists,
                                     route_id,
-                                    GetRoutingID());
+                                    render_view_->GetRoutingID());
   }
 }
 
@@ -210,8 +217,9 @@
 
 void RenderFrameImpl::didCreateFrame(WebKit::WebFrame* parent,
                                      WebKit::WebFrame* child) {
-  Send(new ViewHostMsg_FrameAttached(GetRoutingID(), parent->identifier(),
-      child->identifier(), UTF16ToUTF8(child->assignedName())));
+  render_view_->Send(new ViewHostMsg_FrameAttached(
+      render_view_->GetRoutingID(), parent->identifier(), child->identifier(),
+      UTF16ToUTF8(child->assignedName())));
 }
 
 void RenderFrameImpl::didDisownOpener(WebKit::WebFrame* frame) {
@@ -219,12 +227,25 @@
 }
 
 void RenderFrameImpl::frameDetached(WebKit::WebFrame* frame) {
+  // Currently multiple WebCore::Frames can send frameDetached to a single
+  // RenderFrameImpl. This is legacy behavior from when RenderViewImpl served
+  // as a shared WebFrameClient for multiple Webcore::Frame objects. It also
+  // prevents this class from entering the |is_detaching_| state because
+  // even though one WebCore::Frame may have detached itself, others will
+  // still need to use this object.
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess)) {
+    // TODO(ajwong): Add CHECK(!is_detaching_) once we guarantee each
+    // RenderFrameImpl is only used by one WebCore::Frame.
+    is_detaching_ = true;
+  }
+
   int64 parent_frame_id = -1;
   if (frame->parent())
     parent_frame_id = frame->parent()->identifier();
 
-  Send(new ViewHostMsg_FrameDetached(GetRoutingID(), parent_frame_id,
-      frame->identifier()));
+  render_view_->Send(new ViewHostMsg_FrameDetached(render_view_->GetRoutingID(),
+                                                   parent_frame_id,
+                                                   frame->identifier()));
 
   // Call back to RenderViewImpl for observers to be notified.
   // TODO(nasko): Remove once we have RenderFrameObserver.
@@ -242,10 +263,11 @@
   if (!render_view_->renderer_preferences_.report_frame_name_changes)
     return;
 
-  Send(new ViewHostMsg_UpdateFrameName(GetRoutingID(),
-                                       frame->identifier(),
-                                       !frame->parent(),
-                                       UTF16ToUTF8(name)));
+  render_view_->Send(
+      new ViewHostMsg_UpdateFrameName(render_view_->GetRoutingID(),
+                                      frame->identifier(),
+                                      !frame->parent(),
+                                      UTF16ToUTF8(name)));
 }
 
 void RenderFrameImpl::loadURLExternally(WebKit::WebFrame* frame,
@@ -261,8 +283,9 @@
     const WebKit::WebString& suggested_name) {
   Referrer referrer(RenderViewImpl::GetReferrerFromRequest(frame, request));
   if (policy == WebKit::WebNavigationPolicyDownload) {
-    Send(new ViewHostMsg_DownloadUrl(GetRoutingID(), request.url(), referrer,
-                                     suggested_name));
+    render_view_->Send(new ViewHostMsg_DownloadUrl(render_view_->GetRoutingID(),
+                                                   request.url(), referrer,
+                                                   suggested_name));
   } else {
     render_view_->OpenURL(frame, request.url(), referrer, policy);
   }
@@ -387,8 +410,10 @@
   if (url.is_valid() && url.spec() != kAboutBlankURL) {
     // TODO(nasko): Check if webview()->mainFrame() is the same as the
     // frame->tree()->top().
-    if (frame == render_view_->webview()->mainFrame())
-      Send(new ViewHostMsg_DocumentAvailableInMainFrame(GetRoutingID()));
+    if (frame == render_view_->webview()->mainFrame()) {
+      render_view_->Send(new ViewHostMsg_DocumentAvailableInMainFrame(
+          render_view_->GetRoutingID()));
+    }
   }
 
   // Call back to RenderViewImpl for observers to be notified.
@@ -544,6 +569,7 @@
                            was_after_preconnect_request,
                            (frame == top_frame),
                            frame->identifier(),
+                           GURL(frame->document().securityOrigin().toString()),
                            frame->parent() == top_frame,
                            frame->parent() ? frame->parent()->identifier() : -1,
                            navigation_state->allow_download(),
@@ -567,7 +593,7 @@
   // into the data portion of the message. This can cause problems if we
   // don't register this id on the browser side, since the download manager
   // expects to find a RenderViewHost based off the id.
-  request.setRequestorID(GetRoutingID());
+  request.setRequestorID(render_view_->GetRoutingID());
   request.setHasUserGesture(WebUserGestureIndicator::isProcessingUserGesture());
 
   if (!navigation_state->extra_headers().empty()) {
@@ -653,8 +679,8 @@
 
   // Let the browser know we loaded a resource from the memory cache.  This
   // message is needed to display the correct SSL indicators.
-  Send(new ViewHostMsg_DidLoadResourceFromMemoryCache(
-      GetRoutingID(),
+  render_view_->Send(new ViewHostMsg_DidLoadResourceFromMemoryCache(
+      render_view_->GetRoutingID(),
       url,
       response.securityInfo(),
       request.httpMethod().utf8(),
@@ -663,22 +689,24 @@
 }
 
 void RenderFrameImpl::didDisplayInsecureContent(WebKit::WebFrame* frame) {
-  Send(new ViewHostMsg_DidDisplayInsecureContent(GetRoutingID()));
+  render_view_->Send(new ViewHostMsg_DidDisplayInsecureContent(
+      render_view_->GetRoutingID()));
 }
 
 void RenderFrameImpl::didRunInsecureContent(
     WebKit::WebFrame* frame,
     const WebKit::WebSecurityOrigin& origin,
     const WebKit::WebURL& target) {
-  Send(new ViewHostMsg_DidRunInsecureContent(
-      GetRoutingID(),
+  render_view_->Send(new ViewHostMsg_DidRunInsecureContent(
+      render_view_->GetRoutingID(),
       origin.toString().utf8(),
       target));
 }
 
 void RenderFrameImpl::didExhaustMemoryAvailableForScript(
     WebKit::WebFrame* frame) {
-  Send(new ViewHostMsg_JSOutOfMemory(GetRoutingID()));
+  render_view_->Send(new ViewHostMsg_JSOutOfMemory(
+      render_view_->GetRoutingID()));
 }
 
 void RenderFrameImpl::didCreateScriptContext(WebKit::WebFrame* frame,
@@ -715,8 +743,10 @@
 }
 
 void RenderFrameImpl::willInsertBody(WebKit::WebFrame* frame) {
-  if (!frame->parent())
-    Send(new ViewHostMsg_WillInsertBody(GetRoutingID()));
+  if (!frame->parent()) {
+    render_view_->Send(new ViewHostMsg_WillInsertBody(
+        render_view_->GetRoutingID()));
+  }
 }
 
 void RenderFrameImpl::reportFindInPageMatchCount(int request_id,
@@ -726,24 +756,18 @@
   if (!count)
     active_match_ordinal = 0;
 
-  Send(new ViewHostMsg_Find_Reply(GetRoutingID(),
-                                  request_id,
-                                  count,
-                                  gfx::Rect(),
-                                  active_match_ordinal,
-                                  final_update));
+  render_view_->Send(new ViewHostMsg_Find_Reply(
+      render_view_->GetRoutingID(), request_id, count,
+      gfx::Rect(), active_match_ordinal, final_update));
 }
 
 void RenderFrameImpl::reportFindInPageSelection(
     int request_id,
     int active_match_ordinal,
     const WebKit::WebRect& selection_rect) {
-  Send(new ViewHostMsg_Find_Reply(GetRoutingID(),
-                                  request_id,
-                                  -1,
-                                  selection_rect,
-                                  active_match_ordinal,
-                                  false));
+  render_view_->Send(new ViewHostMsg_Find_Reply(
+      render_view_->GetRoutingID(), request_id, -1, selection_rect,
+      active_match_ordinal, false));
 }
 
 void RenderFrameImpl::requestStorageQuota(
@@ -759,14 +783,14 @@
     return;
   }
   ChildThread::current()->quota_dispatcher()->RequestStorageQuota(
-      GetRoutingID(), GURL(origin.toString()),
+      render_view_->GetRoutingID(), GURL(origin.toString()),
       static_cast<quota::StorageType>(type), requested_size,
       QuotaDispatcher::CreateWebStorageQuotaCallbacksWrapper(callbacks));
 }
 
 void RenderFrameImpl::willOpenSocketStream(
     WebKit::WebSocketStreamHandle* handle) {
-  SocketStreamHandleData::AddToHandle(handle, GetRoutingID());
+  SocketStreamHandleData::AddToHandle(handle, render_view_->GetRoutingID());
 }
 
 void RenderFrameImpl::willStartUsingPeerConnectionHandler(
@@ -824,8 +848,8 @@
     return false;
 
   bool blocked = true;
-  Send(new ViewHostMsg_Are3DAPIsBlocked(
-      GetRoutingID(),
+  render_view_->Send(new ViewHostMsg_Are3DAPIsBlocked(
+      render_view_->GetRoutingID(),
       GURL(frame->top()->document().securityOrigin().toString()),
       THREE_D_API_TYPE_WEBGL,
       &blocked));
@@ -834,7 +858,7 @@
 
 void RenderFrameImpl::didLoseWebGLContext(WebKit::WebFrame* frame,
                                           int arb_robustness_status_code) {
-  Send(new ViewHostMsg_DidLose3DContext(
+  render_view_->Send(new ViewHostMsg_DidLose3DContext(
       GURL(frame->top()->document().securityOrigin().toString()),
       THREE_D_API_TYPE_WEBGL,
       arb_robustness_status_code));
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index fe8ff87..b6940f9 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -176,15 +176,16 @@
   virtual void didLoseWebGLContext(WebKit::WebFrame* frame,
                                    int arb_robustness_status_code);
 
-  // RenderFrameImpl methods
-  int GetRoutingID() const;
-
  protected:
   RenderFrameImpl(RenderViewImpl* render_view, int32 routing_id);
 
  private:
+  int GetRoutingID() const;
+
   RenderViewImpl* render_view_;
   int routing_id_;
+  bool is_swapped_out_;
+  bool is_detaching_;
 
   DISALLOW_COPY_AND_ASSIGN(RenderFrameImpl);
 };
diff --git a/content/renderer/render_process_impl.cc b/content/renderer/render_process_impl.cc
index 79b5b07..2353701 100644
--- a/content/renderer/render_process_impl.cc
+++ b/content/renderer/render_process_impl.cc
@@ -65,10 +65,7 @@
 #endif
 
   // Out of process dev tools rely upon auto break behavior.
-  webkit_glue::SetJavaScriptFlags(
-      "--debugger-auto-break"
-      // Enable on-demand profiling.
-      " --prof --prof-lazy");
+  webkit_glue::SetJavaScriptFlags("--debugger-auto-break");
 
   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
   if (command_line.HasSwitch(switches::kJavaScriptFlags)) {
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 32f323f..4d450a2 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -15,6 +15,7 @@
 #include "base/debug/alias.h"
 #include "base/debug/trace_event.h"
 #include "base/files/file_path.h"
+#include "base/i18n/char_iterator.h"
 #include "base/json/json_writer.h"
 #include "base/lazy_instance.h"
 #include "base/memory/scoped_ptr.h"
@@ -518,7 +519,7 @@
   // 2. The origin of the url and the opener is the same in which case the
   //    opener relationship is maintained.
   // 3. Reloads/form submits/back forward navigations
-  if (!url.SchemeIs(chrome::kHttpScheme) && !url.SchemeIs(chrome::kHttpsScheme))
+  if (!url.SchemeIs(chrome::kHttpScheme) && !url.SchemeIs(kHttpsScheme))
     return false;
 
   if (type != WebKit::WebNavigationTypeReload &&
@@ -2226,13 +2227,16 @@
                                           const string16& default_value,
                                           const GURL& frame_url,
                                           string16* result) {
+  bool user_gesture = WebUserGestureIndicator::isProcessingUserGesture();
   bool success = false;
   string16 result_temp;
   if (!result)
     result = &result_temp;
 
   SendAndRunNestedMessageLoop(new ViewHostMsg_RunJavaScriptMessage(
-      routing_id_, message, default_value, frame_url, type, &success, result));
+      routing_id_, message, default_value, frame_url, type, user_gesture,
+      &success, result));
+  WebUserGestureIndicator::consumeUserGesture();
   return success;
 }
 
@@ -2391,12 +2395,6 @@
 
 void RenderViewImpl::didAddMessageToConsole(
     const WebConsoleMessage& message, const WebString& source_name,
-    unsigned source_line) {
-  didAddMessageToConsole(message, source_name, source_line, WebString());
-}
-
-void RenderViewImpl::didAddMessageToConsole(
-    const WebConsoleMessage& message, const WebString& source_name,
     unsigned source_line, const WebString& stack_trace) {
   logging::LogSeverity log_severity = logging::LOG_VERBOSE;
   switch (message.level) {
@@ -2980,19 +2978,6 @@
 
 // WebKit::WebFrameClient -----------------------------------------------------
 
-WebKit::WebPlugin* RenderViewImpl::createPlugin(WebFrame* frame,
-                                                const WebPluginParams& params) {
-  NOTREACHED();
-  return NULL;
-}
-
-WebSharedWorker* RenderViewImpl::createSharedWorker(
-    WebFrame* frame, const WebURL& url, const WebString& name,
-    unsigned long long document_id) {
-  NOTREACHED();
-  return NULL;
-}
-
 WebMediaPlayer* RenderViewImpl::createMediaPlayer(
     WebFrame* frame, const WebKit::WebURL& url, WebMediaPlayerClient* client) {
   FOR_EACH_OBSERVER(
@@ -3002,7 +2987,7 @@
 #if defined(ENABLE_WEBRTC)
   EnsureMediaStreamClient();
 #if !defined(GOOGLE_TV)
-  if (media_stream_client_->IsMediaStream(url)) {
+  if (media_stream_client_ && media_stream_client_->IsMediaStream(url)) {
 #if defined(OS_ANDROID) && defined(ARCH_CPU_ARMEL)
     bool found_neon =
         (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0;
@@ -3097,12 +3082,6 @@
   return new WebMediaPlayerImpl(frame, client, AsWeakPtr(), params);
 }
 
-WebApplicationCacheHost* RenderViewImpl::createApplicationCacheHost(
-    WebFrame* frame, WebApplicationCacheHostClient* client) {
-  NOTREACHED();
-  return NULL;
-}
-
 WebCookieJar* RenderViewImpl::cookieJar(WebFrame* frame) {
   return &cookie_jar_;
 }
@@ -3114,10 +3093,6 @@
     Send(new ViewHostMsg_DidAccessInitialDocument(routing_id_));
 }
 
-void RenderViewImpl::didCreateFrame(WebFrame* parent, WebFrame* child) {
-  NOTREACHED();
-}
-
 void RenderViewImpl::didDisownOpener(WebKit::WebFrame* frame) {
   // We only need to notify the browser if the active, top-level frame clears
   // its opener.  We can ignore cases where a swapped out frame clears its
@@ -3138,17 +3113,6 @@
   FOR_EACH_OBSERVER(RenderViewObserver, observers_, FrameWillClose(frame));
 }
 
-void RenderViewImpl::didChangeName(WebFrame* frame,
-                                   const WebString& name)  {
-  NOTREACHED();
-}
-
-void RenderViewImpl::loadURLExternally(
-    WebFrame* frame, const WebURLRequest& request,
-    WebNavigationPolicy policy) {
-  NOTREACHED();
-}
-
 void RenderViewImpl::Repaint(const gfx::Size& size) {
   OnRepaint(size);
 }
@@ -3178,13 +3142,6 @@
   return ssl_status;
 }
 
-void RenderViewImpl::loadURLExternally(
-    WebFrame* frame, const WebURLRequest& request,
-    WebNavigationPolicy policy,
-    const WebString& suggested_name) {
-  NOTREACHED();
-}
-
 WebNavigationPolicy RenderViewImpl::decidePolicyForNavigation(
     WebFrame* frame, WebDataSource::ExtraData* extraData,
     const WebURLRequest& request, WebNavigationType type,
@@ -4227,13 +4184,17 @@
 void RenderViewImpl::reportFindInPageMatchCount(int request_id,
                                                 int count,
                                                 bool final_update) {
-  NOTREACHED();
+  // TODO(jam): switch PepperPluginInstanceImpl to take a RenderFrame
+  main_render_frame_->reportFindInPageMatchCount(
+      request_id, count, final_update);
 }
 
 void RenderViewImpl::reportFindInPageSelection(int request_id,
                                                int active_match_ordinal,
                                                const WebRect& selection_rect) {
-  NOTREACHED();
+  // TODO(jam): switch PepperPluginInstanceImpl to take a RenderFrame
+  main_render_frame_->reportFindInPageSelection(
+      request_id, active_match_ordinal, selection_rect);
 }
 
 void RenderViewImpl::requestStorageQuota(
@@ -5936,15 +5897,22 @@
       return;
 
     if (!IsPepperAcceptingCompositionEvents()) {
-      for (size_t i = 0; i < text.size(); ++i) {
+      base::i18n::UTF16CharIterator iterator(&last_text);
+      int32 i = 0;
+      while (iterator.Advance()) {
         WebKit::WebKeyboardEvent char_event;
         char_event.type = WebKit::WebInputEvent::Char;
         char_event.timeStampSeconds = base::Time::Now().ToDoubleT();
         char_event.modifiers = 0;
         char_event.windowsKeyCode = last_text[i];
         char_event.nativeKeyCode = last_text[i];
-        char_event.text[0] = last_text[i];
-        char_event.unmodifiedText[0] = last_text[i];
+
+        const int32 char_start = i;
+        for (; i < iterator.array_pos(); ++i) {
+          char_event.text[i - char_start] = last_text[i];
+          char_event.unmodifiedText[i - char_start] = last_text[i];
+        }
+
         if (webwidget())
           webwidget()->handleInputEvent(char_event);
       }
@@ -6418,11 +6386,12 @@
   if (!new_total_scale)
     return false;
 
-  TapMultipleTargetsStrategy multitarget_strategy =
-    renderer_preferences_.tap_multiple_targets_strategy;
-  if (multitarget_strategy == TAP_MULTIPLE_TARGETS_STRATEGY_ZOOM) {
-      return webview()->zoomToMultipleTargetsRect(zoom_rect);
-  } else if (multitarget_strategy == TAP_MULTIPLE_TARGETS_STRATEGY_POPUP) {
+  bool handled = false;
+  switch (renderer_preferences_.tap_multiple_targets_strategy) {
+    case TAP_MULTIPLE_TARGETS_STRATEGY_ZOOM:
+      handled = webview()->zoomToMultipleTargetsRect(zoom_rect);
+      break;
+    case TAP_MULTIPLE_TARGETS_STRATEGY_POPUP: {
       gfx::Size canvas_size =
           gfx::ToCeiledSize(gfx::ScaleSize(zoom_rect.size(), new_total_scale));
       TransportDIB* transport_dib = NULL;
@@ -6430,8 +6399,10 @@
         scoped_ptr<skia::PlatformCanvas> canvas(
             RenderProcess::current()->GetDrawingCanvas(&transport_dib,
                                                        gfx::Rect(canvas_size)));
-        if (!canvas)
-          return false;
+        if (!canvas) {
+          handled = false;
+          break;
+        }
 
         // TODO(trchen): Cleanup the device scale factor mess.
         // device scale will be applied in WebKit
@@ -6454,11 +6425,15 @@
                                                    physical_window_zoom_rect,
                                                    canvas_size,
                                                    transport_dib->id()));
-  } else {
-    return false;
+      handled = true;
+      break;
+    }
+    case TAP_MULTIPLE_TARGETS_STRATEGY_NONE:
+      // No-op.
+      break;
   }
 
-  return true;
+  return handled;
 }
 #endif
 
@@ -6543,5 +6518,4 @@
   SendUpdateFaviconURL(urls);
 }
 
-
 }  // namespace content
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index 8571234..a221de5 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -448,16 +448,11 @@
   virtual WebKit::WebStorageNamespace* createSessionStorageNamespace();
   virtual bool shouldReportDetailedMessageForSource(
       const WebKit::WebString& source);
-  // TODO(rdevlin.cronin): Remove this version once
-  // https://codereview.chromium.org/18822004/ lands.
   virtual void didAddMessageToConsole(
       const WebKit::WebConsoleMessage& message,
       const WebKit::WebString& source_name,
-      unsigned source_line);
-  virtual void didAddMessageToConsole(
-      const WebKit::WebConsoleMessage& message,
-      const WebKit::WebString& source_name,
-      unsigned source_line, const WebKit::WebString& stack_trace);
+      unsigned source_line,
+      const WebKit::WebString& stack_trace);
   virtual void printPage(WebKit::WebFrame* frame);
   virtual WebKit::WebNotificationPresenter* notificationPresenter();
   virtual bool enumerateChosenDirectory(
@@ -554,35 +549,15 @@
 
   // WebKit::WebFrameClient implementation -------------------------------------
 
-  virtual WebKit::WebPlugin* createPlugin(
-      WebKit::WebFrame* frame,
-      const WebKit::WebPluginParams& params);
-  virtual WebKit::WebSharedWorker* createSharedWorker(
-      WebKit::WebFrame* frame, const WebKit::WebURL& url,
-      const WebKit::WebString& name, unsigned long long documentId);
   virtual WebKit::WebMediaPlayer* createMediaPlayer(
       WebKit::WebFrame* frame,
       const WebKit::WebURL& url,
       WebKit::WebMediaPlayerClient* client);
-  virtual WebKit::WebApplicationCacheHost* createApplicationCacheHost(
-      WebKit::WebFrame* frame,
-      WebKit::WebApplicationCacheHostClient* client);
   virtual WebKit::WebCookieJar* cookieJar(WebKit::WebFrame* frame);
   virtual void didAccessInitialDocument(WebKit::WebFrame* frame);
-  virtual void didCreateFrame(WebKit::WebFrame* parent,
-                              WebKit::WebFrame* child);
   virtual void didDisownOpener(WebKit::WebFrame* frame);
   virtual void frameDetached(WebKit::WebFrame* frame);
   virtual void willClose(WebKit::WebFrame* frame);
-  virtual void didChangeName(WebKit::WebFrame* frame,
-                             const WebKit::WebString& name);
-  virtual void loadURLExternally(WebKit::WebFrame* frame,
-                                 const WebKit::WebURLRequest& request,
-                                 WebKit::WebNavigationPolicy policy);
-  virtual void loadURLExternally(WebKit::WebFrame* frame,
-                                 const WebKit::WebURLRequest& request,
-                                 WebKit::WebNavigationPolicy policy,
-                                 const WebKit::WebString& suggested_name);
 
   // The WebDataSource::ExtraData* is assumed to be a DocumentState* subclass.
   virtual WebKit::WebNavigationPolicy decidePolicyForNavigation(
@@ -1092,6 +1067,9 @@
   void CheckPreferredSize();
 
   // Initializes |media_stream_client_| if needed.
+  // TODO(qinmin): rename this function as it does not guarantee
+  // |media_stream_client_| will be created.
+  // http://crbug.com/278490.
   void EnsureMediaStreamClient();
 
   // This callback is triggered when DownloadFavicon completes, either
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 53b8839..0f3d037 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -239,6 +239,11 @@
   is_threaded_compositing_enabled_ =
       CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnableThreadedCompositing);
+
+  legacy_software_mode_stats_ = cc::RenderingStatsInstrumentation::Create();
+  if (CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableGpuBenchmarking))
+    legacy_software_mode_stats_->set_record_rendering_stats(true);
 }
 
 RenderWidget::~RenderWidget() {
@@ -386,7 +391,8 @@
     IPC_MESSAGE_HANDLER(ViewMsg_ImeConfirmComposition, OnImeConfirmComposition)
     IPC_MESSAGE_HANDLER(ViewMsg_PaintAtSize, OnPaintAtSize)
     IPC_MESSAGE_HANDLER(ViewMsg_Repaint, OnRepaint)
-    IPC_MESSAGE_HANDLER(ViewMsg_SmoothScrollCompleted, OnSmoothScrollCompleted)
+    IPC_MESSAGE_HANDLER(ViewMsg_SyntheticGestureCompleted,
+                        OnSyntheticGestureCompleted)
     IPC_MESSAGE_HANDLER(ViewMsg_SetTextDirection, OnSetTextDirection)
     IPC_MESSAGE_HANDLER(ViewMsg_Move_ACK, OnRequestMoveAck)
     IPC_MESSAGE_HANDLER(ViewMsg_UpdateScreenRects, OnUpdateScreenRects)
@@ -965,9 +971,6 @@
   TRACE_EVENT2("renderer", "PaintRect",
                "width", rect.width(), "height", rect.height());
 
-  const bool kEnableGpuBenchmarking =
-      CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableGpuBenchmarking);
   canvas->save();
 
   // Bring the canvas into the coordinate system of the paint rect.
@@ -1026,34 +1029,24 @@
     // WebKit and filling the background (which can be slow) and just painting
     // the plugin. Unlike the DoDeferredUpdate case, an extra copy is still
     // required.
-    base::TimeTicks paint_begin_ticks;
-    if (kEnableGpuBenchmarking)
-      paint_begin_ticks = base::TimeTicks::HighResNow();
-
     SkAutoCanvasRestore auto_restore(canvas, true);
     canvas->scale(device_scale_factor_, device_scale_factor_);
     optimized_instance->Paint(canvas, optimized_copy_location, rect);
     canvas->restore();
-    if (kEnableGpuBenchmarking) {
-      base::TimeDelta paint_time =
-          base::TimeTicks::HighResNow() - paint_begin_ticks;
-      if (!is_accelerated_compositing_active_)
-        software_stats_.main_stats.paint_time += paint_time;
-    }
 #endif
   } else {
     // Normal painting case.
-    base::TimeTicks paint_begin_ticks;
-    if (kEnableGpuBenchmarking)
-      paint_begin_ticks = base::TimeTicks::HighResNow();
+    base::TimeTicks start_time;
+    if (!is_accelerated_compositing_active_)
+      start_time = legacy_software_mode_stats_->StartRecording();
 
     webwidget_->paint(canvas, rect);
 
-    if (kEnableGpuBenchmarking) {
+    if (!is_accelerated_compositing_active_) {
       base::TimeDelta paint_time =
-          base::TimeTicks::HighResNow() - paint_begin_ticks;
-      if (!is_accelerated_compositing_active_)
-        software_stats_.main_stats.paint_time += paint_time;
+          legacy_software_mode_stats_->EndRecording(start_time);
+      int64 painted_pixel_count = rect.width() * rect.height();
+      legacy_software_mode_stats_->AddPaint(paint_time, painted_pixel_count);
     }
 
     // Flush to underlying bitmap.  TODO(darin): is this needed?
@@ -1062,11 +1055,6 @@
 
   PaintDebugBorder(rect, canvas);
   canvas->restore();
-
-  if (kEnableGpuBenchmarking) {
-    int64 num_pixels_processed = rect.width() * rect.height();
-    software_stats_.main_stats.painted_pixel_count += num_pixels_processed;
-  }
 }
 
 void RenderWidget::PaintDebugBorder(const gfx::Rect& rect,
@@ -1272,8 +1260,10 @@
   last_do_deferred_update_time_ = frame_begin_ticks;
 
   if (!is_accelerated_compositing_active_) {
-    software_stats_.main_stats.animation_frame_count++;
-    software_stats_.main_stats.screen_frame_count++;
+    legacy_software_mode_stats_->IncrementAnimationFrameCount();
+    legacy_software_mode_stats_->IncrementScreenFrameCount(1, true);
+    legacy_software_mode_stats_->IssueTraceEventForMainThreadStats();
+    legacy_software_mode_stats_->AccumulateAndClearMainThreadStats();
   }
 
   // OK, save the pending update to a local since painting may cause more
@@ -2015,8 +2005,8 @@
   }
 }
 
-void RenderWidget::OnSmoothScrollCompleted() {
-  pending_smooth_scroll_gesture_.Run();
+void RenderWidget::OnSyntheticGestureCompleted() {
+  pending_synthetic_gesture_.Run();
 }
 
 void RenderWidget::OnSetTextDirection(WebTextDirection direction) {
@@ -2454,14 +2444,8 @@
   if (compositor_)
     compositor_->GetRenderingStats(&stats.rendering_stats);
 
-  stats.rendering_stats.main_stats.animation_frame_count +=
-      software_stats_.main_stats.animation_frame_count;
-  stats.rendering_stats.main_stats.screen_frame_count +=
-      software_stats_.main_stats.screen_frame_count;
-  stats.rendering_stats.main_stats.paint_time +=
-      software_stats_.main_stats.paint_time;
-  stats.rendering_stats.main_stats.painted_pixel_count +=
-      software_stats_.main_stats.painted_pixel_count;
+  stats.rendering_stats.Add(
+      legacy_software_mode_stats_->GetRenderingStats());
 }
 
 bool RenderWidget::GetGpuRenderingStats(GpuRenderingStats* stats) const {
@@ -2487,7 +2471,7 @@
 
 void RenderWidget::BeginSmoothScroll(
     bool down,
-    const SmoothScrollCompletionCallback& callback,
+    const SyntheticGestureCompletionCallback& callback,
     int pixels_to_scroll,
     int mouse_event_x,
     int mouse_event_y) {
@@ -2500,7 +2484,7 @@
   params.mouse_event_y = mouse_event_y;
 
   Send(new ViewHostMsg_BeginSmoothScroll(routing_id_, params));
-  pending_smooth_scroll_gesture_ = callback;
+  pending_synthetic_gesture_ = callback;
 }
 
 bool RenderWidget::WillHandleMouseEvent(const WebKit::WebMouseEvent& event) {
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index ec3609a..231d0c7 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -15,7 +15,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
-#include "cc/debug/rendering_stats.h"
+#include "cc/debug/rendering_stats_instrumentation.h"
 #include "content/common/browser_rendering_stats.h"
 #include "content/common/content_export.h"
 #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h"
@@ -176,15 +176,15 @@
 
   virtual scoped_ptr<cc::OutputSurface> CreateOutputSurface(bool fallback);
 
-  // Callback for use with BeginSmoothScroll.
-  typedef base::Callback<void()> SmoothScrollCompletionCallback;
+  // Callback for use with synthetic gestures (e.g. BeginSmoothScroll).
+  typedef base::Callback<void()> SyntheticGestureCompletionCallback;
 
   // Directs the host to begin a smooth scroll. This scroll should have the same
   // performance characteristics as a user-initiated scroll. Returns an ID of
   // the scroll gesture. |mouse_event_x| and |mouse_event_y| are expected to be
   // in local DIP coordinates.
   void BeginSmoothScroll(bool scroll_down,
-                         const SmoothScrollCompletionCallback& callback,
+                         const SyntheticGestureCompletionCallback& callback,
                          int pixels_to_scroll,
                          int mouse_event_x,
                          int mouse_event_y);
@@ -314,7 +314,7 @@
                      const gfx::Size& page_size,
                      const gfx::Size& desired_size);
   void OnRepaint(gfx::Size size_to_paint);
-  void OnSmoothScrollCompleted();
+  void OnSyntheticGestureCompleted();
   void OnSetTextDirection(WebKit::WebTextDirection direction);
   void OnGetFPS();
   void OnUpdateScreenRects(const gfx::Rect& view_screen_rect,
@@ -693,7 +693,8 @@
   bool has_disable_gpu_vsync_switch_;
   base::TimeTicks last_do_deferred_update_time_;
 
-  cc::RenderingStats software_stats_;
+  // Stats for legacy software mode
+  scoped_ptr<cc::RenderingStatsInstrumentation> legacy_software_mode_stats_;
 
   // UpdateRect parameters for the current compositing pass. This is used to
   // pass state between DoDeferredUpdate and OnSwapBuffersPosted.
@@ -713,8 +714,9 @@
   // |screen_info_| on some platforms, and defaults to 1 on other platforms.
   float device_scale_factor_;
 
-  // State associated with the BeginSmoothScroll synthetic scrolling function.
-  SmoothScrollCompletionCallback pending_smooth_scroll_gesture_;
+  // State associated with the synthetic gestures function
+  // (e.g. BeginSmoothScroll).
+  SyntheticGestureCompletionCallback pending_synthetic_gesture_;
 
   // Specified whether the compositor will run in its own thread.
   bool is_threaded_compositing_enabled_;
diff --git a/content/renderer/renderer_webkitplatformsupport_impl.cc b/content/renderer/renderer_webkitplatformsupport_impl.cc
index afa6bc0..25ca5d1 100644
--- a/content/renderer/renderer_webkitplatformsupport_impl.cc
+++ b/content/renderer/renderer_webkitplatformsupport_impl.cc
@@ -400,10 +400,6 @@
   if (!key_system.isEmpty()) {
     // Check whether the key system is supported with the mime_type and codecs.
 
-    // Not supporting the key system is a flat-out no.
-    if (!IsSupportedKeySystem(key_system))
-      return IsNotSupported;
-
     std::vector<std::string> strict_codecs;
     bool strip_suffix = !net::IsStrictMediaMimeType(mime_type_ascii);
     net::ParseCodecString(ToASCIIOrEmpty(codecs), &strict_codecs, strip_suffix);
@@ -448,7 +444,7 @@
   const std::string mime_type_ascii = ToASCIIOrEmpty(mime_type);
   std::vector<std::string> parsed_codec_ids;
   net::ParseCodecString(ToASCIIOrEmpty(codecs), &parsed_codec_ids, false);
-  if (mime_type_ascii.empty() || parsed_codec_ids.size() == 0)
+  if (mime_type_ascii.empty())
     return false;
   return media::StreamParserFactory::IsTypeSupported(
       mime_type_ascii, parsed_codec_ids);
@@ -621,12 +617,18 @@
 
 WebKit::WebSharedWorkerRepository*
 RendererWebKitPlatformSupportImpl::sharedWorkerRepository() {
+#if !defined(OS_ANDROID)
   if (!CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kDisableSharedWorkers)) {
     return shared_worker_repository_.get();
   } else {
     return NULL;
   }
+#else
+  // Shared workers are unsupported on Android. Returning NULL will prevent the
+  // window.SharedWorker constructor from being exposed. http://crbug.com/154571
+  return NULL;
+#endif
 }
 
 bool RendererWebKitPlatformSupportImpl::canAccelerate2dCanvas() {
diff --git a/content/renderer/skia_benchmarking_extension.cc b/content/renderer/skia_benchmarking_extension.cc
index 8ea59f1..95e2698 100644
--- a/content/renderer/skia_benchmarking_extension.cc
+++ b/content/renderer/skia_benchmarking_extension.cc
@@ -12,9 +12,9 @@
 #include "skia/ext/benchmarking_canvas.h"
 #include "third_party/WebKit/public/platform/WebArrayBuffer.h"
 #include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkColorPriv.h"
-#include "third_party/skia/include/core/SkDevice.h"
 #include "third_party/skia/include/core/SkGraphics.h"
 #include "third_party/skia/src/utils/debugger/SkDebugCanvas.h"
 #include "third_party/skia/src/utils/debugger/SkDrawCommand.h"
@@ -250,8 +250,9 @@
     gfx::Rect bounds = picture->LayerRect();
 
     // Measure the total time by drawing straight into a bitmap-backed canvas.
-    skia::RefPtr<SkDevice> device = skia::AdoptRef(SkNEW_ARGS(SkDevice,
-        (SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height())));
+    skia::RefPtr<SkBaseDevice> device = skia::AdoptRef(
+        SkNEW_ARGS(SkBitmapDevice,
+             (SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height())));
     SkCanvas bitmap_canvas(device.get());
     bitmap_canvas.clear(SK_ColorTRANSPARENT);
     base::TimeTicks t0 = base::TimeTicks::HighResNow();
diff --git a/content/renderer/v8_value_converter_impl.cc b/content/renderer/v8_value_converter_impl.cc
index efd3d44..cacd9de 100644
--- a/content/renderer/v8_value_converter_impl.cc
+++ b/content/renderer/v8_value_converter_impl.cc
@@ -382,6 +382,19 @@
       return out;
   }
 
+  // Don't consider DOM objects. This check matches isHostObject() in Blink's
+  // bindings/v8/V8Binding.h used in structured cloning. It reads:
+  //
+  // If the object has any internal fields, then we won't be able to serialize
+  // or deserialize them; conveniently, this is also a quick way to detect DOM
+  // wrapper objects, because the mechanism for these relies on data stored in
+  // these fields.
+  //
+  // NOTE: check this after |strategy_| so that callers have a chance to
+  // do something else, such as convert to the node's name rather than NULL.
+  if (val->InternalFieldCount())
+    return NULL;
+
   scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
   v8::Handle<v8::Array> property_names(val->GetOwnPropertyNames());
 
diff --git a/content/renderer/v8_value_converter_impl_unittest.cc b/content/renderer/v8_value_converter_impl_unittest.cc
index 934d8e3..f9b0ebc 100644
--- a/content/renderer/v8_value_converter_impl_unittest.cc
+++ b/content/renderer/v8_value_converter_impl_unittest.cc
@@ -44,16 +44,6 @@
   converter_->avoid_identity_hash_for_testing_ = false;
 }
 
-namespace {
-
-// A dumb getter for an object's named callback.
-v8::Handle<v8::Value> NamedCallbackGetter(v8::Local<v8::String> name,
-                                          const v8::AccessorInfo& info) {
-  return v8::String::New("bar");
-}
-
-}  // namespace
-
 class V8ValueConverterImplTest : public testing::Test {
  public:
   V8ValueConverterImplTest()
diff --git a/content/renderer/webcrypto_impl.cc b/content/renderer/webcrypto_impl.cc
index 80c5512..ad37457 100644
--- a/content/renderer/webcrypto_impl.cc
+++ b/content/renderer/webcrypto_impl.cc
@@ -4,23 +4,20 @@
 
 #include "content/renderer/webcrypto_impl.h"
 
-#include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h"
+#include "third_party/WebKit/public/platform/WebArrayBuffer.h"
 
 namespace content {
 
-WebKit::WebCryptoOperation* WebCryptoImpl::digest(
-    const WebKit::WebCryptoAlgorithm& algorithm) {
-  switch (algorithm.id()) {
-    case WebKit::WebCryptoAlgorithmIdSha1:
-    case WebKit::WebCryptoAlgorithmIdSha224:
-    case WebKit::WebCryptoAlgorithmIdSha256:
-    case WebKit::WebCryptoAlgorithmIdSha384:
-    case WebKit::WebCryptoAlgorithmIdSha512:
-      // TODO(eroman): Implement.
-      return NULL;
-    default:
-      // Not a digest algorithm.
-      return NULL;
+void WebCryptoImpl::digest(
+    const WebKit::WebCryptoAlgorithm& algorithm,
+    const unsigned char* data,
+    size_t data_size,
+    WebKit::WebCryptoResult result) {
+  WebKit::WebArrayBuffer buffer;
+  if (!digestInternal(algorithm, data, data_size, &buffer)) {
+    result.completeWithError();
+  } else {
+    result.completeWithBuffer(buffer);
   }
 }
 
diff --git a/content/renderer/webcrypto_impl.h b/content/renderer/webcrypto_impl.h
index 42081c4..6590202 100644
--- a/content/renderer/webcrypto_impl.h
+++ b/content/renderer/webcrypto_impl.h
@@ -7,14 +7,29 @@
 
 #include "base/compiler_specific.h"
 
+#include "base/gtest_prod_util.h"
+#include "content/common/content_export.h"
 #include "third_party/WebKit/public/platform/WebCrypto.h"
 
 namespace content {
 
-class WebCryptoImpl : public WebKit::WebCrypto {
+class CONTENT_EXPORT WebCryptoImpl
+    : NON_EXPORTED_BASE(public WebKit::WebCrypto) {
  public:
-  virtual WebKit::WebCryptoOperation* digest(
-      const WebKit::WebCryptoAlgorithm& algorithm) OVERRIDE;
+  virtual void digest(
+      const WebKit::WebCryptoAlgorithm& algorithm,
+      const unsigned char* data,
+      size_t data_size,
+      WebKit::WebCryptoResult result);
+
+ protected:
+  FRIEND_TEST_ALL_PREFIXES(WebCryptoImplTest, DigestSampleSets);
+
+  bool digestInternal(
+      const WebKit::WebCryptoAlgorithm& algorithm,
+      const unsigned char* data,
+      size_t data_size,
+      WebKit::WebArrayBuffer* buffer);
 };
 
 }  // namespace content
diff --git a/content/renderer/webcrypto_impl_nss.cc b/content/renderer/webcrypto_impl_nss.cc
new file mode 100644
index 0000000..61aa3a6
--- /dev/null
+++ b/content/renderer/webcrypto_impl_nss.cc
@@ -0,0 +1,70 @@
+// 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 "content/renderer/webcrypto_impl.h"
+
+#include <sechash.h>
+
+#include "base/logging.h"
+#include "crypto/nss_util.h"
+#include "third_party/WebKit/public/platform/WebArrayBuffer.h"
+#include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h"
+
+namespace content {
+
+bool WebCryptoImpl::digestInternal(
+    const WebKit::WebCryptoAlgorithm& algorithm,
+    const unsigned char* data,
+    size_t data_size,
+    WebKit::WebArrayBuffer* buffer) {
+  HASH_HashType hash_type = HASH_AlgNULL;
+
+  switch (algorithm.id()) {
+    case WebKit::WebCryptoAlgorithmIdSha1:
+      hash_type = HASH_AlgSHA1;
+      break;
+    case WebKit::WebCryptoAlgorithmIdSha224:
+      hash_type = HASH_AlgSHA224;
+      break;
+    case WebKit::WebCryptoAlgorithmIdSha256:
+      hash_type = HASH_AlgSHA256;
+      break;
+    case WebKit::WebCryptoAlgorithmIdSha384:
+      hash_type = HASH_AlgSHA384;
+      break;
+    case WebKit::WebCryptoAlgorithmIdSha512:
+      hash_type = HASH_AlgSHA512;
+      break;
+    default:
+      // Not a digest algorithm.
+      return false;
+  }
+
+  crypto::EnsureNSSInit();
+
+  HASHContext* context = HASH_Create(hash_type);
+  if (!context) {
+    return false;
+  }
+
+  HASH_Begin(context);
+
+  HASH_Update(context, data, data_size);
+
+  size_t hash_result_length = HASH_ResultLenContext(context);
+  DCHECK_LE(hash_result_length, static_cast<size_t>(HASH_LENGTH_MAX));
+
+  *buffer = WebKit::WebArrayBuffer::create(hash_result_length, 1);
+
+  unsigned char* digest = reinterpret_cast<unsigned char*>(buffer->data());
+
+  uint32 result_length = 0;
+  HASH_End(context, digest, &result_length, hash_result_length);
+
+  HASH_Destroy(context);
+
+  return result_length == hash_result_length;
+}
+
+}  // namespace content
diff --git a/content/renderer/webcrypto_impl_openssl.cc b/content/renderer/webcrypto_impl_openssl.cc
new file mode 100644
index 0000000..125aee5
--- /dev/null
+++ b/content/renderer/webcrypto_impl_openssl.cc
@@ -0,0 +1,19 @@
+// 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 "content/renderer/webcrypto_impl.h"
+
+namespace content {
+
+bool WebCryptoImpl::digestInternal(
+    const WebKit::WebCryptoAlgorithm& algorithm,
+    const unsigned char* data,
+    size_t data_size,
+    WebKit::WebArrayBuffer* buffer) {
+  // TODO(bryaneyler): Placeholder for OpenSSL implementation.
+  // Issue http://crbug.com/267888.
+  return false;
+}
+
+}  // namespace content
diff --git a/content/renderer/webcrypto_impl_unittest.cc b/content/renderer/webcrypto_impl_unittest.cc
new file mode 100644
index 0000000..2a569dd
--- /dev/null
+++ b/content/renderer/webcrypto_impl_unittest.cc
@@ -0,0 +1,120 @@
+// 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 "webcrypto_impl.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/string_number_conversions.h"
+#include "content/public/renderer/content_renderer_client.h"
+#include "content/renderer/renderer_webkitplatformsupport_impl.h"
+#include "content/renderer/webcrypto_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebArrayBuffer.h"
+#include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h"
+
+namespace content {
+
+const WebKit::WebCryptoAlgorithmId kAlgorithmIds[] = {
+    WebKit::WebCryptoAlgorithmIdSha1,
+    WebKit::WebCryptoAlgorithmIdSha224,
+    WebKit::WebCryptoAlgorithmIdSha256,
+    WebKit::WebCryptoAlgorithmIdSha384,
+    WebKit::WebCryptoAlgorithmIdSha512
+};
+
+class WebCryptoImplTest : public testing::Test, public WebCryptoImpl {
+};
+
+TEST_F(WebCryptoImplTest, DigestSampleSets) {
+  // The results are stored here in hex format for readability.
+  //
+  // TODO(bryaneyler): Eventually, all these sample test sets should be replaced
+  // with the sets here: http://csrc.nist.gov/groups/STM/cavp/index.html#03
+  struct {
+    const char* input;
+    size_t input_length;
+    const char* hex_result[arraysize(kAlgorithmIds)];
+  } input_set[] = {
+    {
+      "", 0,
+      {
+        // echo -n "" | sha1sum
+        "da39a3ee5e6b4b0d3255bfef95601890afd80709",
+        // echo -n "" | sha224sum
+        "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f",
+        // echo -n "" | sha256sum
+        "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+        // echo -n "" | sha384sum
+        "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274e"
+        "debfe76f65fbd51ad2f14898b95b",
+        // echo -n "" | sha512sum
+        "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0"
+        "d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
+      },
+    },
+    {
+      "\000", 1,
+      {
+        // echo -n -e "\000" | sha1sum
+        "5ba93c9db0cff93f52b521d7420e43f6eda2784f",
+        // echo -n -e "\000" | sha224sum
+        "fff9292b4201617bdc4d3053fce02734166a683d7d858a7f5f59b073",
+        // echo -n -e "\000" | sha256sum
+        "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d",
+        // echo -n -e "\000" | sha384sum
+        "bec021b4f368e3069134e012c2b4307083d3a9bdd206e24e5f0d86e13d6636655933"
+        "ec2b413465966817a9c208a11717",
+        // echo -n -e "\000" | sha512sum
+        "b8244d028981d693af7b456af8efa4cad63d282e19ff14942c246e50d9351d22704a"
+        "802a71c3580b6370de4ceb293c324a8423342557d4e5c38438f0e36910ee",
+      },
+    },
+    {
+      "\000\001\002\003\004\005", 6,
+      {
+        // echo -n -e "\000\001\002\003\004\005" | sha1sum
+        "868460d98d09d8bbb93d7b6cdd15cc7fbec676b9",
+        // echo -n -e "\000\001\002\003\004\005" | sha224sum
+        "7d92e7f1cad1818ed1d13ab41f04ebabfe1fef6bb4cbeebac34c29bc",
+        // echo -n -e "\000\001\002\003\004\005" | sha256sum
+        "17e88db187afd62c16e5debf3e6527cd006bc012bc90b51a810cd80c2d511f43",
+        // echo -n -e "\000\001\002\003\004\005" | sha384sum
+        "79f4738706fce9650ac60266675c3cd07298b09923850d525604d040e6e448adc7dc"
+        "22780d7e1b95bfeaa86a678e4552",
+        // echo -n -e "\000\001\002\003\004\005" | sha512sum
+        "2f3831bccc94cf061bcfa5f8c23c1429d26e3bc6b76edad93d9025cb91c903af6cf9"
+        "c935dc37193c04c2c66e7d9de17c358284418218afea2160147aaa912f4c",
+      },
+    },
+  };
+
+  for (size_t id_index = 0; id_index < arraysize(kAlgorithmIds); id_index++) {
+    WebKit::WebCryptoAlgorithm algorithm(
+        WebKit::WebCryptoAlgorithm::adoptParamsAndCreate(
+            kAlgorithmIds[id_index], "SHA", NULL));
+
+    for (size_t set_index = 0;
+         set_index < ARRAYSIZE_UNSAFE(input_set);
+         set_index++) {
+      WebKit::WebArrayBuffer array_buffer;
+
+      WebCryptoImpl crypto;
+      crypto.digestInternal(
+          algorithm,
+          reinterpret_cast<const unsigned char*>(input_set[set_index].input),
+          input_set[set_index].input_length,
+          &array_buffer);
+
+      // Ignore case, it's checking the hex value.
+      EXPECT_STRCASEEQ(
+          input_set[set_index].hex_result[id_index],
+          base::HexEncode(
+              array_buffer.data(), array_buffer.byteLength()).c_str());
+    }
+  }
+}
+
+}  // namespace content
diff --git a/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsActivity.java b/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsActivity.java
index 3f25e68..d7aeecf 100644
--- a/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsActivity.java
+++ b/content/shell/android/browsertests_apk/src/org/chromium/content_browsertests_apk/ContentBrowserTestsActivity.java
@@ -7,13 +7,13 @@
 import android.app.Activity;
 import android.content.Context;
 import android.os.Bundle;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.util.Log;
 
 import org.chromium.base.JNINamespace;
 import org.chromium.content.app.LibraryLoader;
-import org.chromium.content.browser.AndroidBrowserProcess;
+import org.chromium.content.browser.BrowserStartupController;
 import org.chromium.content.common.ProcessInitException;
 import org.chromium.content_shell.ShellManager;
 import org.chromium.ui.WindowAndroid;
@@ -34,7 +34,7 @@
         } catch (ProcessInitException e) {
             Log.i(TAG, "Cannot load content_browsertests:" +  e);
         }
-        AndroidBrowserProcess.initChromiumBrowserProcessForTests(getApplicationContext());
+        BrowserStartupController.get(getApplicationContext()).initChromiumBrowserProcessForTests();
 
         LayoutInflater inflater =
                 (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
diff --git a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java
index 2d819e9..c98ba12 100644
--- a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java
+++ b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java
@@ -18,7 +18,6 @@
 import org.chromium.base.MemoryPressureListener;
 import org.chromium.content.app.LibraryLoader;
 import org.chromium.content.browser.ActivityContentVideoViewClient;
-import org.chromium.content.browser.AndroidBrowserProcess;
 import org.chromium.content.browser.BrowserStartupController;
 import org.chromium.content.browser.ContentVideoViewClient;
 import org.chromium.content.browser.ContentView;
@@ -47,15 +46,15 @@
     public static final String COMMAND_LINE_ARGS_KEY = "commandLineArgs";
 
     /**
-     * Sending an intent with this action will simulate a memory pressure signal
-     * at a critical level.
+     * Sending an intent with this action will simulate a memory pressure signal at a critical
+     * level.
      */
     private static final String ACTION_LOW_MEMORY =
             "org.chromium.content_shell.action.ACTION_LOW_MEMORY";
 
     /**
-     * Sending an intent with this action will simulate a memory pressure signal
-     * at a moderate level.
+     * Sending an intent with this action will simulate a memory pressure signal at a moderate
+     * level.
      */
     private static final String ACTION_TRIM_MEMORY_MODERATE =
             "org.chromium.content_shell.action.ACTION_TRIM_MEMORY_MODERATE";
@@ -100,11 +99,10 @@
         }
 
         if (CommandLine.getInstance().hasSwitch(CommandLine.DUMP_RENDER_TREE)) {
-            try {
-                if (!AndroidBrowserProcess.init(this, AndroidBrowserProcess.MAX_RENDERERS_LIMIT)) {
-                    finishInitialization(savedInstanceState);
-                }
-            } catch (ProcessInitException e) {
+            if(BrowserStartupController.get(this).startBrowserProcessesSync(
+                   BrowserStartupController.MAX_RENDERERS_LIMIT)) {
+                finishInitialization(savedInstanceState);
+            } else {
                 initializationFailed();
             }
         } else {
diff --git a/content/shell/browser/shell.cc b/content/shell/browser/shell.cc
index c8616d3..df09655 100644
--- a/content/shell/browser/shell.cc
+++ b/content/shell/browser/shell.cc
@@ -230,8 +230,9 @@
 
 WebContents* Shell::OpenURLFromTab(WebContents* source,
                                    const OpenURLParams& params) {
-  // The only one we implement for now.
-  DCHECK(params.disposition == CURRENT_TAB);
+  // CURRENT_TAB is the only one we implement for now.
+  if (params.disposition != CURRENT_TAB)
+      return NULL;
   NavigationController::LoadURLParams load_url_params(params.url);
   load_url_params.referrer = params.referrer;
   load_url_params.transition_type = params.transition;
diff --git a/content/shell/browser/shell_aura.cc b/content/shell/browser/shell_aura.cc
index ba3366e..d2d3c38 100644
--- a/content/shell/browser/shell_aura.cc
+++ b/content/shell/browser/shell_aura.cc
@@ -304,8 +304,7 @@
   std::vector<Shell*> windows = windows_;
   for (std::vector<Shell*>::iterator it = windows.begin();
        it != windows.end(); ++it) {
-    if (!(*it)->headless_)
-      (*it)->window_widget_->Close();
+    (*it)->window_widget_->Close();
   }
 #if defined(OS_CHROMEOS)
   if (minimal_shell_)
@@ -323,8 +322,6 @@
 }
 
 void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) {
-  if (headless_)
-    return;
   ShellWindowDelegateView* delegate_view =
     static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
   if (control == BACK_BUTTON) {
@@ -340,8 +337,6 @@
 }
 
 void Shell::PlatformSetAddressBarURL(const GURL& url) {
-  if (headless_)
-    return;
   ShellWindowDelegateView* delegate_view =
     static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
   delegate_view->SetAddressBarURL(url);
@@ -351,8 +346,6 @@
 }
 
 void Shell::PlatformCreateWindow(int width, int height) {
-  if (headless_)
-    return;
 #if defined(OS_CHROMEOS)
   window_widget_ =
       views::Widget::CreateWindowWithContextAndBounds(
@@ -373,8 +366,6 @@
 }
 
 void Shell::PlatformSetContents() {
-  if (headless_)
-    return;
   ShellWindowDelegateView* delegate_view =
     static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
   delegate_view->SetWebContents(web_contents_.get());
@@ -384,14 +375,10 @@
 }
 
 void Shell::Close() {
-  if (headless_)
-    return;
   window_widget_->CloseNow();
 }
 
 void Shell::PlatformSetTitle(const string16& title) {
-  if (headless_)
-    return;
   ShellWindowDelegateView* delegate_view =
     static_cast<ShellWindowDelegateView*>(window_widget_->widget_delegate());
   delegate_view->SetWindowTitle(title);
diff --git a/content/shell/browser/shell_browser_main.cc b/content/shell/browser/shell_browser_main.cc
index ac520d6..44871db 100644
--- a/content/shell/browser/shell_browser_main.cc
+++ b/content/shell/browser/shell_browser_main.cc
@@ -194,6 +194,12 @@
 
       if (!content::WebKitTestController::Get()->ResetAfterLayoutTest())
         break;
+
+#if defined(OS_ANDROID)
+      // There will be left-over tasks in the queue for Android because the
+      // main window is being destroyed. Run them before starting the next test.
+      base::MessageLoop::current()->RunUntilIdle();
+#endif
     }
     if (!ran_at_least_once) {
       base::MessageLoop::current()->PostTask(FROM_HERE,
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index c837929..040fce8 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -188,6 +188,11 @@
   return new ShellQuotaPermissionContext();
 }
 
+SpeechRecognitionManagerDelegate*
+    ShellContentBrowserClient::GetSpeechRecognitionManagerDelegate() {
+  return new ShellSpeechRecognitionManagerDelegate();
+}
+
 net::NetLog* ShellContentBrowserClient::GetNetLog() {
   return shell_browser_main_parts_->net_log();
 }
diff --git a/content/shell/browser/shell_content_browser_client.h b/content/shell/browser/shell_content_browser_client.h
index 15285ca..b96b481 100644
--- a/content/shell/browser/shell_content_browser_client.h
+++ b/content/shell/browser/shell_content_browser_client.h
@@ -14,6 +14,7 @@
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
+#include "content/shell/browser/shell_speech_recognition_manager_delegate.h"
 
 namespace content {
 
@@ -58,6 +59,8 @@
   virtual WebContentsViewDelegate* GetWebContentsViewDelegate(
       WebContents* web_contents) OVERRIDE;
   virtual QuotaPermissionContext* CreateQuotaPermissionContext() OVERRIDE;
+  virtual SpeechRecognitionManagerDelegate*
+      GetSpeechRecognitionManagerDelegate() OVERRIDE;
   virtual net::NetLog* GetNetLog() OVERRIDE;
   virtual bool ShouldSwapProcessesForRedirect(ResourceContext* resource_context,
                                               const GURL& current_url,
diff --git a/content/shell/browser/shell_javascript_dialog_manager.cc b/content/shell/browser/shell_javascript_dialog_manager.cc
index 1476f7c..b250d18 100644
--- a/content/shell/browser/shell_javascript_dialog_manager.cc
+++ b/content/shell/browser/shell_javascript_dialog_manager.cc
@@ -29,6 +29,7 @@
     JavaScriptMessageType javascript_message_type,
     const string16& message_text,
     const string16& default_prompt_text,
+    bool user_gesture,
     const DialogClosedCallback& callback,
     bool* did_suppress_message) {
   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) {
diff --git a/content/shell/browser/shell_javascript_dialog_manager.h b/content/shell/browser/shell_javascript_dialog_manager.h
index 763736f..30d23d5 100644
--- a/content/shell/browser/shell_javascript_dialog_manager.h
+++ b/content/shell/browser/shell_javascript_dialog_manager.h
@@ -27,6 +27,7 @@
       JavaScriptMessageType javascript_message_type,
       const string16& message_text,
       const string16& default_prompt_text,
+      bool user_gesture,
       const DialogClosedCallback& callback,
       bool* did_suppress_message) OVERRIDE;
 
diff --git a/content/shell/browser/shell_speech_recognition_manager_delegate.cc b/content/shell/browser/shell_speech_recognition_manager_delegate.cc
new file mode 100644
index 0000000..0b0778d
--- /dev/null
+++ b/content/shell/browser/shell_speech_recognition_manager_delegate.cc
@@ -0,0 +1,33 @@
+// 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 "content/shell/browser/shell_speech_recognition_manager_delegate.h"
+
+#include "content/public/browser/browser_thread.h"
+
+using base::Callback;
+
+namespace content {
+
+void ShellSpeechRecognitionManagerDelegate::CheckRecognitionIsAllowed(
+    int session_id, Callback<void(bool ask_user, bool is_allowed)> callback) {
+  // In content_shell, we expect speech recognition to happen when requested.
+  // Therefore we simply authorize it by calling back with is_allowed=true. The
+  // first parameter, ask_user, is set to false because we don't want to prompt
+  // the user for permission with an infobar.
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE, base::Bind(callback, false, true));
+}
+
+SpeechRecognitionEventListener*
+    ShellSpeechRecognitionManagerDelegate::GetEventListener() {
+  return NULL;
+}
+
+bool ShellSpeechRecognitionManagerDelegate::FilterProfanities(
+    int render_process_id) {
+  return false;
+}
+
+}  // namespace content
diff --git a/content/shell/browser/shell_speech_recognition_manager_delegate.h b/content/shell/browser/shell_speech_recognition_manager_delegate.h
new file mode 100644
index 0000000..41ad2e5
--- /dev/null
+++ b/content/shell/browser/shell_speech_recognition_manager_delegate.h
@@ -0,0 +1,38 @@
+// 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 CONTENT_SHELL_BROWSER_SHELL_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_
+#define CONTENT_SHELL_BROWSER_SHELL_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "content/public/browser/speech_recognition_event_listener.h"
+#include "content/public/browser/speech_recognition_manager_delegate.h"
+
+namespace content {
+
+// This is content_shell's delegate used by the speech recognition manager to
+// check for permission to record audio. For content_shell, we always authorize
+// speech recognition (see crbug.com/237119).
+class ShellSpeechRecognitionManagerDelegate
+    : public SpeechRecognitionManagerDelegate {
+ public:
+  ShellSpeechRecognitionManagerDelegate() {}
+  virtual ~ShellSpeechRecognitionManagerDelegate() {}
+
+  // SpeechRecognitionManagerDelegate methods.
+  virtual void GetDiagnosticInformation(
+      bool* can_report_metrics, std::string* hardware_info) OVERRIDE {}
+  virtual void CheckRecognitionIsAllowed(int session_id,
+      base::Callback<void(bool ask_user, bool is_allowed)> callback) OVERRIDE;
+  virtual SpeechRecognitionEventListener* GetEventListener() OVERRIDE;
+  virtual bool FilterProfanities(int render_process_id) OVERRIDE;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ShellSpeechRecognitionManagerDelegate);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_SHELL_BROWSER_SHELL_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_
diff --git a/content/shell/browser/webkit_test_controller.cc b/content/shell/browser/webkit_test_controller.cc
index 40ed9c5..52f0611 100644
--- a/content/shell/browser/webkit_test_controller.cc
+++ b/content/shell/browser/webkit_test_controller.cc
@@ -287,6 +287,12 @@
   test_url_ = GURL();
   prefs_ = WebPreferences();
   should_override_prefs_ = false;
+
+#if defined(OS_ANDROID)
+  // Re-using the shell's main window on Android causes issues with networking
+  // requests never succeeding. See http://crbug.com/277652.
+  DiscardMainWindow();
+#endif
   return true;
 }
 
diff --git a/content/shell/common/webkit_test_helpers.cc b/content/shell/common/webkit_test_helpers.cc
index 320eb4b..b0d4fc0 100644
--- a/content/shell/common/webkit_test_helpers.cc
+++ b/content/shell/common/webkit_test_helpers.cc
@@ -8,6 +8,7 @@
 #include "base/file_util.h"
 #include "base/path_service.h"
 #include "base/strings/utf_string_conversions.h"
+#include "content/public/common/content_switches.h"
 #include "content/shell/common/shell_switches.h"
 #include "third_party/WebKit/public/testing/WebPreferences.h"
 #include "webkit/common/webpreferences.h"
@@ -55,7 +56,7 @@
 // of the defaults are controlled via command line flags which are
 // automatically set for layout tests.
 void ApplyLayoutTestDefaultPreferences(WebPreferences* prefs) {
-  CommandLine& command_line = *CommandLine::ForCurrentProcess();
+  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
   prefs->allow_universal_access_from_file_urls = true;
   prefs->dom_paste_enabled = true;
   prefs->javascript_can_access_clipboard = true;
@@ -100,6 +101,8 @@
   prefs->threaded_html_parser = true;
   prefs->accelerated_2d_canvas_enabled =
       command_line.HasSwitch(switches::kEnableAccelerated2DCanvas);
+  prefs->force_compositing_mode =
+      command_line.HasSwitch(switches::kForceCompositingMode);
   prefs->accelerated_compositing_for_video_enabled = false;
   prefs->mock_scrollbars_enabled = false;
   prefs->fixed_position_creates_stacking_context = false;
diff --git a/content/shell/renderer/webkit_test_runner.cc b/content/shell/renderer/webkit_test_runner.cc
index 7f11fb9..d93a967 100644
--- a/content/shell/renderer/webkit_test_runner.cc
+++ b/content/shell/renderer/webkit_test_runner.cc
@@ -110,7 +110,7 @@
 #endif
 
 void CopyCanvasToBitmap(SkCanvas* canvas,  SkBitmap* snapshot) {
-  SkDevice* device = skia::GetTopDevice(*canvas);
+  SkBaseDevice* device = skia::GetTopDevice(*canvas);
   const SkBitmap& bitmap = device->accessBitmap(false);
   const bool success = bitmap.copyTo(snapshot, SkBitmap::kARGB_8888_Config);
   DCHECK(success);
diff --git a/content/test/data/accessibility/a-onclick-expected-android.txt b/content/test/data/accessibility/a-onclick-expected-android.txt
index c8bf65d..4121ed9 100644
--- a/content/test/data/accessibility/a-onclick-expected-android.txt
+++ b/content/test/data/accessibility/a-onclick-expected-android.txt
@@ -1,3 +1,3 @@
 android.view.View focusable focused scrollable
-    android.view.View clickable name='link with no href but onclick'
-    android.view.View clickable name='link with no href and click handler added via script'
+    android.view.View clickable name='link with no href but onclick Link'
+    android.view.View clickable name='link with no href and click handler added via script Link'
diff --git a/content/test/data/cross_site_document_request.html b/content/test/data/cross_site_document_request.html
new file mode 100644
index 0000000..7bd949f
--- /dev/null
+++ b/content/test/data/cross_site_document_request.html
@@ -0,0 +1,81 @@
+<html>
+<head>
+</head>
+<body>
+This test shows that cross-site documents are blocked by SiteIsolationPolicy
+even if the Same Origin Policy is turned off in the renderer. The Same Origin
+Policy can be circumvented when the renderer is compromised, but we have
+SiteIsolationPolicy that blocks cross-site documents at the IPC layer. For now
+cross-site document blocking by SiteIsolationPolicy is done in the renderer, but
+our ultimate plan is to do that in the browser process.
+
+<script>
+var xhrStatus = -1;
+var pathPrefix = "http://bar.com/files/site_isolation/";
+
+// We only block cross-site documents with a blacklisted mime type(text/html,
+// text/xml, application/json), that are correctly sniffed as the content type
+// that they claim to be. We also block text/plain documents when their body
+// looks like one of the blacklisted content types.
+
+var blockedResourceUrls = ['valid.html', 'comment_valid.html', 'valid.xml',
+'valid.json', 'html.txt', 'xml.txt', 'json.txt'];
+
+var nonBlockedResourceUrls = ['js.html', 'comment_js.html', 'js.xml', 'js.json',
+'js.txt', 'img.html', 'img.xml', 'img.json', 'img.txt', 'comment_js.html'];
+
+var resourceUrls = blockedResourceUrls.concat(nonBlockedResourceUrls);
+
+var failed = false;
+function sendRequest(resourceUrl) {
+  var xhr = new XMLHttpRequest();
+  xhr.onreadystatechange = function() {
+    if (xhr.readyState == 4) {
+      var prefix = "";
+      if ((blockedResourceUrls.indexOf(resourceUrl) != -1 &&
+           xhr.responseText != " ") ||
+          (nonBlockedResourceUrls.indexOf(resourceUrl) != -1 &&
+           xhr.responseText == " ")) {
+        // Test failed. Either a resource that should have been blocked is not
+        // blocked, or a resource that should have not been blocked is blocked.
+        domAutomationController.setAutomationId(0);
+        domAutomationController.send(0);
+        if (blockedResourceUrls.indexOf(resourceUrl) != -1) {
+          prefix = "[ERROR:resource to be blocked wasn't blocked]";
+        } else {
+          prefix = "[ERROR:resource to be unblocked was blocked]";
+        }
+      }
+      document.getElementById("response_body").value +=
+          ("\n" + prefix + "response to " + resourceUrl + "(" +
+           xhr.getResponseHeader("content-type") + ") " +
+           (xhr.responseText == " " ? "blocked" : "not-blocked"));
+      drive();
+    }
+  }
+  xhr.open('GET', pathPrefix + resourceUrl);
+  xhr.send();
+}
+
+var cnt = 0;
+function drive() {
+  if (cnt < resourceUrls.length) {
+    sendRequest(resourceUrls[cnt]);
+    ++cnt;
+  } else {
+    // All the test cases are successfully passed.
+    domAutomationController.setAutomationId(0);
+    domAutomationController.send(1);
+  }
+}
+
+window.onload = function() {
+  // The call to pushState with another domain will succeed, since the
+  // test uses --disable-web-security.
+  history.pushState('', '', 'http://bar.com/files/main.html');
+  drive();
+}
+</script>
+<textarea rows=20 cols=50 id='response_body'></textarea>
+</body>
+</html>
diff --git a/content/test/data/cross_site_document_request_target.html b/content/test/data/cross_site_document_request_target.html
new file mode 100644
index 0000000..cd94fef
--- /dev/null
+++ b/content/test/data/cross_site_document_request_target.html
@@ -0,0 +1,39 @@
+<html>
+<head>
+<!-- favicon -->
+<link rel="icon"
+      type="image/png"
+      href="http://bar.com/files/site_isolation/valid.html">
+<!-- css -->
+<link rel="stylesheet" type="text/css" href="http://bar.com/files/site_isolation/valid.html">
+<!-- script -->
+<script language="javascript" src="http://bar.com/files/site_isolation/valid.html"></script>
+</head>
+<body>
+This page tests that the renderer process does not crash even the response that
+it requested is blocked by SiteIsolationPolicy for various targets. This page
+should be loaded on a domain other than bar.com to be different from the
+requested resource here.
+
+image:<img src="http://bar.com/files/site_isolation/valid.html"></img><br/>
+
+
+font:
+<style media="screen" type="text/css">
+@font-face {
+    font-family: "myfont";
+    src: url("http://bar.com/files/site_isolation/valid.html") format('woff');
+}
+</style><br/>
+
+<!-- The renderer creates an iframe for a file with .html extension, so this
+doesn't go through SiteIsolationPolicy -->
+embed:<embed src="http://bar.com/files/site_isolation/valid.html"></embed><br/>
+
+<!-- The renderer creates an iframe for a file with .html extension, so this
+doesn't go through SiteIsolationPolicy -->
+object:<object width="400" height="400"
+data="http://bar.com/files/site_isolation/valid.html"></object><br/>
+
+</body>
+</html>
diff --git a/content/test/data/media/webui/integration_test.html b/content/test/data/media/webui/integration_test.html
index 2d25ede..71d205c 100644
--- a/content/test/data/media/webui/integration_test.html
+++ b/content/test/data/media/webui/integration_test.html
@@ -12,13 +12,16 @@
         var mockClientRenderer = {
           playerUpdated: doNothing,
           playerRemoved: doNothing,
-          playerAdded: doNothing
+          playerAdded: doNothing,
+          audioStreamUpdated: doNothing,
+          audioStreamAdded: doNothing,
+          audioStreamRemoved: doNothing
         };
 
-        var manager = new PlayerManager(mockClientRenderer);
+        var manager = new Manager(mockClientRenderer);
         media.initialize(manager);
 
-        window.playerManager = manager;
+        window.manager= manager;
       };
 
       // The renderer and player ids are completely arbitrarily.
@@ -39,7 +42,7 @@
         };
 
         window.media.onMediaEvent(event);
-        var info = window.playerManager.players_[TEST_NAME];
+        var info = window.manager.players_[TEST_NAME];
 
         assertEquals(event.ticksMillis, info.firstTimestamp_);
         assertEquals(TEST_NAME, info.id);
@@ -51,7 +54,7 @@
         window.testOnMediaEvent();
 
         window.media.onRendererTerminated(TEST_RENDERER);
-        assertEquals(undefined, window.playerManager.players_[TEST_NAME]);
+        assertEquals(undefined, window.manager.players_[TEST_NAME]);
       };
 
       // Audio Streams are weird, they are handled separately
@@ -64,9 +67,9 @@
 
         window.media.addAudioStream(event);
 
-        var player = window.playerManager.players_[event.id];
+        var player = window.manager.audioStreams_[event.id];
         assertTrue(undefined !== player);
-        assertEquals(event.playing, player.properties['playing']);
+        assertEquals(event.playing, player['playing']);
       };
     </script>
   </body>
diff --git a/content/test/data/media/webui/player_manager_test.html b/content/test/data/media/webui/player_manager_test.html
index 290ee3f..73ea76c 100644
--- a/content/test/data/media/webui/player_manager_test.html
+++ b/content/test/data/media/webui/player_manager_test.html
@@ -17,7 +17,7 @@
       };
 
       window.setUp = function() {
-        window.pm = new PlayerManager(emptyClientRenderer);
+        window.pm = new Manager(emptyClientRenderer);
       };
 
       window.tearDown = function() {
diff --git a/content/test/data/site_isolation/comment_js.html b/content/test/data/site_isolation/comment_js.html
new file mode 100644
index 0000000..7f92594
--- /dev/null
+++ b/content/test/data/site_isolation/comment_js.html
@@ -0,0 +1,2 @@
+<!-- This is a valid javascript content despite having an HTML comment. -->
+var j = 0; document.write(j);
diff --git a/content/test/data/site_isolation/comment_valid.html b/content/test/data/site_isolation/comment_valid.html
new file mode 100644
index 0000000..6498271
--- /dev/null
+++ b/content/test/data/site_isolation/comment_valid.html
@@ -0,0 +1,2 @@
+<!-- This is an HTML document. -->
+<html></html>
diff --git a/content/test/data/site_isolation/html.txt b/content/test/data/site_isolation/html.txt
new file mode 100644
index 0000000..1bfe9eb
--- /dev/null
+++ b/content/test/data/site_isolation/html.txt
@@ -0,0 +1 @@
+<html>This has the text/plain mime type but the content is html.</html>
diff --git a/content/test/data/site_isolation/img.html b/content/test/data/site_isolation/img.html
new file mode 100644
index 0000000..22d5b52
--- /dev/null
+++ b/content/test/data/site_isolation/img.html
@@ -0,0 +1 @@
+This is an arbitrary string that doesn't match any signatures of HTML/XML/JSON.
diff --git a/content/test/data/site_isolation/img.json b/content/test/data/site_isolation/img.json
new file mode 100644
index 0000000..22d5b52
--- /dev/null
+++ b/content/test/data/site_isolation/img.json
@@ -0,0 +1 @@
+This is an arbitrary string that doesn't match any signatures of HTML/XML/JSON.
diff --git a/content/test/data/site_isolation/img.txt b/content/test/data/site_isolation/img.txt
new file mode 100644
index 0000000..22d5b52
--- /dev/null
+++ b/content/test/data/site_isolation/img.txt
@@ -0,0 +1 @@
+This is an arbitrary string that doesn't match any signatures of HTML/XML/JSON.
diff --git a/content/test/data/site_isolation/img.xml b/content/test/data/site_isolation/img.xml
new file mode 100644
index 0000000..22d5b52
--- /dev/null
+++ b/content/test/data/site_isolation/img.xml
@@ -0,0 +1 @@
+This is an arbitrary string that doesn't match any signatures of HTML/XML/JSON.
diff --git a/content/test/data/site_isolation/js.html b/content/test/data/site_isolation/js.html
new file mode 100644
index 0000000..0daa45e
--- /dev/null
+++ b/content/test/data/site_isolation/js.html
@@ -0,0 +1 @@
+var j = 0; document.write(j);
diff --git a/content/test/data/site_isolation/js.json b/content/test/data/site_isolation/js.json
new file mode 100644
index 0000000..0daa45e
--- /dev/null
+++ b/content/test/data/site_isolation/js.json
@@ -0,0 +1 @@
+var j = 0; document.write(j);
diff --git a/content/test/data/site_isolation/js.txt b/content/test/data/site_isolation/js.txt
new file mode 100644
index 0000000..0daa45e
--- /dev/null
+++ b/content/test/data/site_isolation/js.txt
@@ -0,0 +1 @@
+var j = 0; document.write(j);
diff --git a/content/test/data/site_isolation/js.xml b/content/test/data/site_isolation/js.xml
new file mode 100644
index 0000000..0daa45e
--- /dev/null
+++ b/content/test/data/site_isolation/js.xml
@@ -0,0 +1 @@
+var j = 0; document.write(j);
diff --git a/content/test/data/site_isolation/json.txt b/content/test/data/site_isolation/json.txt
new file mode 100644
index 0000000..d943abc
--- /dev/null
+++ b/content/test/data/site_isolation/json.txt
@@ -0,0 +1 @@
+{ "name" : "chromium" }
diff --git a/content/test/data/site_isolation/valid.html b/content/test/data/site_isolation/valid.html
new file mode 100644
index 0000000..ea2fdc4
--- /dev/null
+++ b/content/test/data/site_isolation/valid.html
@@ -0,0 +1 @@
+<html>This is valid html</html>
diff --git a/content/test/data/site_isolation/valid.json b/content/test/data/site_isolation/valid.json
new file mode 100644
index 0000000..d943abc
--- /dev/null
+++ b/content/test/data/site_isolation/valid.json
@@ -0,0 +1 @@
+{ "name" : "chromium" }
diff --git a/content/test/data/site_isolation/valid.xml b/content/test/data/site_isolation/valid.xml
new file mode 100644
index 0000000..575dbaa
--- /dev/null
+++ b/content/test/data/site_isolation/valid.xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" ?>
diff --git a/content/test/data/site_isolation/xml.txt b/content/test/data/site_isolation/xml.txt
new file mode 100644
index 0000000..575dbaa
--- /dev/null
+++ b/content/test/data/site_isolation/xml.txt
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" ?>
diff --git a/content/test/gpu/gpu_tests/webgl_conformance.py b/content/test/gpu/gpu_tests/webgl_conformance.py
index 5e27d21..6a58b79 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance.py
@@ -88,10 +88,9 @@
           {'action': 'navigate'},
           {
             'action': 'wait',
-            'condition': 'javascript',
             'javascript': 'webglTestHarness._finished',
             'timeout': 120
-          },
+          }
         ]
       })
 
diff --git a/content/test/gpu/gpu_tests/webgl_robustness.py b/content/test/gpu/gpu_tests/webgl_robustness.py
index 507d770..aefb8b9 100644
--- a/content/test/gpu/gpu_tests/webgl_robustness.py
+++ b/content/test/gpu/gpu_tests/webgl_robustness.py
@@ -57,7 +57,10 @@
         {
           'url': 'file:///extra/lots-of-polys-example.html',
           'script_to_evaluate_on_commit': robustness_harness_script,
-          'wait_for_javascript_expression': 'webglTestHarness._finished'
+          'navigate_steps': [
+            { 'action': 'navigate' },
+            { 'action': 'wait', 'javascript': 'webglTestHarness._finished' }
+          ]
         }
       ]
     }
diff --git a/content/test/gpu/page_sets/pixel_tests.json b/content/test/gpu/page_sets/pixel_tests.json
index 7581ff8..bd83281 100644
--- a/content/test/gpu/page_sets/pixel_tests.json
+++ b/content/test/gpu/page_sets/pixel_tests.json
@@ -4,7 +4,10 @@
   "pages": [
     {
       "url": "file:///../../data/gpu/pixel_webgl.html",
-      "wait_seconds": 4
+      "navigate_steps": [
+        { "action": "navigate"},
+        { "action": "wait", "seconds": 4 }
+      ]
     }
   ]
 }
diff --git a/courgette/analyze_mem_test b/courgette/analyze_mem_test
index 9470fa2..1e72939 100755
--- a/courgette/analyze_mem_test
+++ b/courgette/analyze_mem_test
@@ -6,9 +6,7 @@
 
 # Produce memory metrics for run_apply_test
 
-error() {
-  echo "error: ${@}" >&2
-}
+source "$(dirname ${0})/stress_test_common"
 
 compute_percentiles() {
   if [ -z "${1}" ]; then
diff --git a/courgette/analyze_stress_test b/courgette/analyze_stress_test
index 338eb6e..0c700eb 100755
--- a/courgette/analyze_stress_test
+++ b/courgette/analyze_stress_test
@@ -6,22 +6,10 @@
 
 # Produce metrics analyzing the output of a stress test
 
+source "$(dirname ${0})/stress_test_common"
+
 set -e
 
-error() {
-  echo "error: ${@}" >&2
-}
-
-# Given a token, search for and count the instances of lines from the
-# logfile that start with the token.
-count_result() {
-  if [ ! -z "${1}" ]; then
-    echo $(cat "${log}" | grep "^${1} " | wc -l)    
-  else
-    echo 0
-  fi
-}
-
 # Given a token, search for and compute the percentiles from logfile.
 compute_percentiles() {
   if [ ! -z "${1}" ]; then
diff --git a/courgette/courgette_application.png b/courgette/courgette_application.png
new file mode 100644
index 0000000..97fe870
--- /dev/null
+++ b/courgette/courgette_application.png
Binary files differ
diff --git a/courgette/courgette_generation.png b/courgette/courgette_generation.png
new file mode 100644
index 0000000..5f58a6d
--- /dev/null
+++ b/courgette/courgette_generation.png
Binary files differ
diff --git a/courgette/description.html b/courgette/description.html
new file mode 100644
index 0000000..8fe4538
--- /dev/null
+++ b/courgette/description.html
@@ -0,0 +1,147 @@
+<h1>Courgette Internals</h1>
+
+<h2>Patch Generation</h2>
+
+<p><img src="generation.png" alt="Patch Generation" title="" /></p>
+
+<ul>
+<li><p>courgette_tool.cc:GenerateEnsemblePatch kicks off the patch
+generation by calling ensemble_create.cc:GenerateEnsemblePatch</p></li>
+<li><p>The files are read in by in courgette:SourceStream objects</p></li>
+<li><p>ensemble_create.cc:GenerateEnsemblePatch uses FindGenerators, which
+uses MakeGenerator to create
+patch_generator_x86_32.h:PatchGeneratorX86_32 classes.</p></li>
+<li><p>PatchGeneratorX86_32's Transform method transforms the input file
+using Courgette's core techniques that make the bsdiff delta
+smaller.  The steps it takes are the following:</p>
+
+<ul>
+<li><p><em>disassemble</em> the old and new binaries into AssemblyProgram
+objects,</p></li>
+<li><p><em>adjust</em> the new AssemblyProgram object, and</p></li>
+<li><p><em>encode</em> the AssemblyProgram object back into raw bytes.</p></li>
+</ul></li>
+</ul>
+
+<h3>Disassemble</h3>
+
+<ul>
+<li><p>The input is a pointer to a buffer containing the raw bytes of the
+input file.</p></li>
+<li><p>Disassembly converts certain machine instructions that reference
+addresses to Courgette instructions.  It is not actually
+disassembly, but this is the term the code-base uses.  Specifically,
+it detects instructions that use absolute addresses given by the
+binary file's relocation table, and relative addresses used in
+relative branches.</p></li>
+<li><p>Done by disassemble:ParseDetectedExecutable, which selects the
+appropriate Disassembler subclass by looking at the binary file's
+headers.</p>
+
+<ul>
+<li><p>disassembler_win32_x86.h defines the PE/COFF x86 disassembler</p></li>
+<li><p>disassembler_elf_32_x86.h defines the ELF 32-bit x86 disassembler</p></li>
+<li><p>disassembler_elf_32_arm.h defines the ELF 32-bit arm disassembler</p></li>
+</ul></li>
+<li><p>The Disassembler replaces the relocation table with a Courgette
+instruction that can regenerate the relocation table.</p></li>
+<li><p>The Disassembler builds a list of addresses referenced by the
+machine code, numbering each one.</p></li>
+<li><p>The Disassembler replaces and address used in machine instructions
+with its index number.</p></li>
+<li><p>The output is an assembly_program.h:AssemblyProgram class, which
+contains a list of instructions, machine or Courgette, and a mapping
+of indices to actual addresses.</p></li>
+</ul>
+
+<h3>Adjust</h3>
+
+<ul>
+<li><p>This step takes the AssemblyProgram for the old file and reassigns
+the indices that map to actual addresses.  It is performed by
+adjustment_method.cc:Adjust().</p></li>
+<li><p>The goal is the match the indices from the old program to the new
+program as closely as possible.</p></li>
+<li><p>When matched correctly, machine instructions that jump to the
+function in both the new and old binary will look the same to
+bsdiff, even the function is located in a different part of the
+binary.</p></li>
+</ul>
+
+<h3>Encode</h3>
+
+<ul>
+<li><p>This step takes an AssemblyProgram object and encodes both the
+instructions and the mapping of indices to addresses as byte
+vectors.  This format can be written to a file directly, and is also
+more appropriate for bsdiffing.  It is done by
+AssemblyProgram.Encode().</p></li>
+<li><p>encoded_program.h:EncodedProgram defines the binary format and a
+WriteTo method that writes to a file.</p></li>
+</ul>
+
+<h3>bsdiff</h3>
+
+<ul>
+<li>simple_delta.c:GenerateSimpleDelta</li>
+</ul>
+
+<h2>Patch Application</h2>
+
+<p><img src="application.png" alt="Patch Application" title="" /></p>
+
+<ul>
+<li><p>courgette_tool.cc:ApplyEnsemblePatch kicks off the patch generation
+by calling ensemble_apply.cc:ApplyEnsemblePatch</p></li>
+<li><p>ensemble_create.cc:ApplyEnsemblePatch, reads and verifies the
+patch's header, then calls the overloaded version of
+ensemble_create.cc:ApplyEnsemblePatch.</p></li>
+<li><p>The patch is read into an ensemble<em>apply.cc:EnsemblePatchApplication
+object, which generates a set of patcher</em>x86<em>32.h:PatcherX86</em>32
+objects for the sections in the patch.</p></li>
+<li><p>The original file is disassembled and encoded via a call
+EnsemblePatchApplication.TransformUp, which in turn call
+patcher<em>x86</em>32.h:PatcherX86_32.Transform.</p></li>
+<li><p>The transformed file is then bspatched via
+EnsemblePatchApplication.SubpatchTransformedElements, which calls
+EnsemblePatchApplication.SubpatchStreamSets, which calls
+simple_delta.cc:ApplySimpleDelta, Courgette's built-in
+implementation of bspatch.</p></li>
+<li><p>Finally, EnsemblePatchApplication.TransformDown assembles, i.e.,
+reverses the encoding and disassembly, on the patched binary data.
+This is done by calling PatcherX86<em>32.Reform, which in turn calls
+the global function encoded</em>program.cc:Assemble, which calls
+EncodedProgram.AssembleTo.</p></li>
+</ul>
+
+<h2>Glossary</h2>
+
+<p><strong>Adjust</strong>: Reassign address indices in the new program to match more
+  closely those from the old.</p>
+
+<p><strong>Assembly program</strong>: The output of <em>disassembly</em>.  Contains a list of
+  <em>Courgette instructions</em> and an index of branch target addresses.</p>
+
+<p><strong>Assemble</strong>: Convert an <em>assembly program</em> back into an object file
+  by evaluating the <em>Courgette instructions</em> and leaving the machine
+  instructions in place.</p>
+
+<p><strong>Courgette instruction</strong>: Replaces machine instructions in the
+  program.  Courgette instructions replace branches with an index to
+  the target addresses and replace part of the relocation table.</p>
+
+<p><strong>Disassembler</strong>: Takes a binary file and produces an <em>assembly
+  program</em>.</p>
+
+<p><strong>Encode</strong>: Convert an <em>assembly program</em> into an <em>encoded program</em> by
+  serializing its data structures into byte vectors more appropriate
+  for storage in a file.</p>
+
+<p><strong>Encoded Program</strong>: The output of encoding.</p>
+
+<p><strong>Ensemble</strong>: A Courgette-style patch containing sections for the list
+  of branch addresses, the encoded program.  It supports patching
+  multiple object files at once.</p>
+
+<p><strong>Opcode</strong>: The number corresponding to either a machine or <em>Courgette
+  instruction</em>.</p>
diff --git a/courgette/description.md b/courgette/description.md
new file mode 100644
index 0000000..f79f99e
--- /dev/null
+++ b/courgette/description.md
@@ -0,0 +1,157 @@
+Courgette Internals
+===================
+
+Patch Generation
+----------------
+
+![Patch Generation](generation.png)
+
+- courgette\_tool.cc:GenerateEnsemblePatch kicks off the patch
+  generation by calling ensemble\_create.cc:GenerateEnsemblePatch
+
+- The files are read in by in courgette:SourceStream objects
+
+- ensemble\_create.cc:GenerateEnsemblePatch uses FindGenerators, which
+  uses MakeGenerator to create
+  patch\_generator\_x86\_32.h:PatchGeneratorX86\_32 classes.
+
+- PatchGeneratorX86\_32's Transform method transforms the input file
+  using Courgette's core techniques that make the bsdiff delta
+  smaller.  The steps it takes are the following:
+
+  - _disassemble_ the old and new binaries into AssemblyProgram
+    objects,
+
+  - _adjust_ the new AssemblyProgram object, and
+
+  - _encode_ the AssemblyProgram object back into raw bytes.
+
+### Disassemble
+
+- The input is a pointer to a buffer containing the raw bytes of the
+  input file.
+
+- Disassembly converts certain machine instructions that reference
+  addresses to Courgette instructions.  It is not actually
+  disassembly, but this is the term the code-base uses.  Specifically,
+  it detects instructions that use absolute addresses given by the
+  binary file's relocation table, and relative addresses used in
+  relative branches.
+
+- Done by disassemble:ParseDetectedExecutable, which selects the
+  appropriate Disassembler subclass by looking at the binary file's
+  headers.
+
+  - disassembler\_win32\_x86.h defines the PE/COFF x86 disassembler
+
+  - disassembler\_elf\_32\_x86.h defines the ELF 32-bit x86 disassembler
+
+  - disassembler\_elf\_32\_arm.h defines the ELF 32-bit arm disassembler
+
+- The Disassembler replaces the relocation table with a Courgette
+  instruction that can regenerate the relocation table.
+
+- The Disassembler builds a list of addresses referenced by the
+  machine code, numbering each one.
+
+- The Disassembler replaces and address used in machine instructions
+  with its index number.
+
+- The output is an assembly\_program.h:AssemblyProgram class, which
+  contains a list of instructions, machine or Courgette, and a mapping
+  of indices to actual addresses.
+
+### Adjust
+
+- This step takes the AssemblyProgram for the old file and reassigns
+  the indices that map to actual addresses.  It is performed by
+  adjustment_method.cc:Adjust().
+
+- The goal is the match the indices from the old program to the new
+  program as closely as possible.
+
+- When matched correctly, machine instructions that jump to the
+  function in both the new and old binary will look the same to
+  bsdiff, even the function is located in a different part of the
+  binary.
+
+### Encode
+
+- This step takes an AssemblyProgram object and encodes both the
+  instructions and the mapping of indices to addresses as byte
+  vectors.  This format can be written to a file directly, and is also
+  more appropriate for bsdiffing.  It is done by
+  AssemblyProgram.Encode().
+
+- encoded_program.h:EncodedProgram defines the binary format and a
+  WriteTo method that writes to a file.
+
+### bsdiff
+
+- simple_delta.c:GenerateSimpleDelta
+
+Patch Application
+-----------------
+
+![Patch Application](application.png)
+
+- courgette\_tool.cc:ApplyEnsemblePatch kicks off the patch generation
+  by calling ensemble\_apply.cc:ApplyEnsemblePatch
+
+- ensemble\_create.cc:ApplyEnsemblePatch, reads and verifies the
+  patch's header, then calls the overloaded version of
+  ensemble\_create.cc:ApplyEnsemblePatch.
+
+- The patch is read into an ensemble_apply.cc:EnsemblePatchApplication
+  object, which generates a set of patcher_x86_32.h:PatcherX86_32
+  objects for the sections in the patch.
+
+- The original file is disassembled and encoded via a call
+  EnsemblePatchApplication.TransformUp, which in turn call
+  patcher_x86_32.h:PatcherX86_32.Transform.
+
+- The transformed file is then bspatched via
+  EnsemblePatchApplication.SubpatchTransformedElements, which calls
+  EnsemblePatchApplication.SubpatchStreamSets, which calls
+  simple_delta.cc:ApplySimpleDelta, Courgette's built-in
+  implementation of bspatch.
+
+- Finally, EnsemblePatchApplication.TransformDown assembles, i.e.,
+  reverses the encoding and disassembly, on the patched binary data.
+  This is done by calling PatcherX86_32.Reform, which in turn calls
+  the global function encoded_program.cc:Assemble, which calls
+  EncodedProgram.AssembleTo.
+
+
+Glossary
+--------
+
+**Adjust**: Reassign address indices in the new program to match more
+  closely those from the old.
+
+**Assembly program**: The output of _disassembly_.  Contains a list of
+  _Courgette instructions_ and an index of branch target addresses.
+
+**Assemble**: Convert an _assembly program_ back into an object file
+  by evaluating the _Courgette instructions_ and leaving the machine
+  instructions in place.
+
+**Courgette instruction**: Replaces machine instructions in the
+  program.  Courgette instructions replace branches with an index to
+  the target addresses and replace part of the relocation table.
+
+**Disassembler**: Takes a binary file and produces an _assembly
+  program_.
+
+**Encode**: Convert an _assembly program_ into an _encoded program_ by
+  serializing its data structures into byte vectors more appropriate
+  for storage in a file.
+
+**Encoded Program**: The output of encoding.
+
+**Ensemble**: A Courgette-style patch containing sections for the list
+  of branch addresses, the encoded program.  It supports patching
+  multiple object files at once.
+
+**Opcode**: The number corresponding to either a machine or _Courgette
+  instruction_.
diff --git a/courgette/run_mem_test b/courgette/run_mem_test
index 07a8e5b..7f3640a 100755
--- a/courgette/run_mem_test
+++ b/courgette/run_mem_test
@@ -6,9 +6,7 @@
 
 # Collect memory usage on the patches from run_stress_test
 
-error() {
-  echo "error: ${@}" >&2
-}
+source "$(dirname ${0})/stress_test_common"
 
 main() {
   if [ $# -lt 1 ]; then
diff --git a/courgette/run_stress_test b/courgette/run_stress_test
index 442ad3d..dd1a78d 100755
--- a/courgette/run_stress_test
+++ b/courgette/run_stress_test
@@ -6,9 +6,7 @@
 
 # Stress test and size measurement for courgette patches.
 
-error() {
-  echo "error: ${@}" >&2
-}
+source "$(dirname ${0})/stress_test_common"
 
 outdir_prefix="stress_test_"
 time="/usr/bin/time"
@@ -163,14 +161,6 @@
 
 date >> "${log}"
 
-count_result() {
-  if [ ! -z "${1}" ]; then
-    echo $(cat "${log}" | grep "^${1} " | wc -l)    
-  else
-    echo 0
-  fi
-}
-
 cat <<EOF | tee -a "${log}"
 $(count_result "PASS_COURGETTE") successful courgette patches
 $(count_result "FAIL_COURGETTE") failed courgette patches (search log for \
diff --git a/extensions/browser/extension_error.cc b/extensions/browser/extension_error.cc
index 8b6196c..eaef221 100644
--- a/extensions/browser/extension_error.cc
+++ b/extensions/browser/extension_error.cc
@@ -40,13 +40,16 @@
 ExtensionError::ExtensionError(Type type,
                                const std::string& extension_id,
                                bool from_incognito,
+                               logging::LogSeverity level,
                                const string16& source,
                                const string16& message)
     : type_(type),
       extension_id_(extension_id),
       from_incognito_(from_incognito),
+      level_(level),
       source_(source),
-      message_(message) {
+      message_(message),
+      occurrences_(1u) {
 }
 
 ExtensionError::~ExtensionError() {
@@ -55,66 +58,89 @@
 std::string ExtensionError::PrintForTest() const {
   return std::string("Extension Error:") +
          "\n  OTR:     " + std::string(from_incognito_ ? "true" : "false") +
+         "\n  Level:   " + base::IntToString(static_cast<int>(level_));
          "\n  Source:  " + base::UTF16ToUTF8(source_) +
          "\n  Message: " + base::UTF16ToUTF8(message_) +
          "\n  ID:      " + extension_id_;
 }
 
-ManifestParsingError::ManifestParsingError(const std::string& extension_id,
-                                           const string16& message)
-    : ExtensionError(ExtensionError::MANIFEST_PARSING_ERROR,
+bool ExtensionError::IsEqual(const ExtensionError* rhs) const {
+  // We don't check |source_| or |level_| here, since they are constant for
+  // manifest errors. Check them in RuntimeError::IsEqualImpl() instead.
+  return type_ == rhs->type_ &&
+         extension_id_ == rhs->extension_id_ &&
+         message_ == rhs->message_ &&
+         IsEqualImpl(rhs);
+}
+
+ManifestError::ManifestError(const std::string& extension_id,
+                             const string16& message)
+    : ExtensionError(ExtensionError::MANIFEST_ERROR,
                      extension_id,
                      false,  // extensions can't be installed while incognito.
+                     logging::LOG_WARNING,  // All manifest errors are warnings.
                      base::FilePath(kManifestFilename).AsUTF16Unsafe(),
                      message) {
 }
 
-ManifestParsingError::~ManifestParsingError() {
+ManifestError::~ManifestError() {
 }
 
-std::string ManifestParsingError::PrintForTest() const {
+std::string ManifestError::PrintForTest() const {
   return ExtensionError::PrintForTest() +
-         "\n  Type:    ManifestParsingError";
+         "\n  Type:    ManifestError";
 }
 
-JavascriptRuntimeError::StackFrame::StackFrame() : line_number(-1),
-                                                   column_number(-1) {
+bool ManifestError::IsEqualImpl(const ExtensionError* rhs) const {
+  // If two manifest errors have the same extension id and message (which are
+  // both checked in ExtensionError::IsEqual), then they are equal.
+  return true;
 }
 
-JavascriptRuntimeError::StackFrame::StackFrame(size_t frame_line,
-                                               size_t frame_column,
-                                               const string16& frame_url,
-                                               const string16& frame_function)
+RuntimeError::StackFrame::StackFrame() : line_number(-1), column_number(-1) {
+}
+
+RuntimeError::StackFrame::StackFrame(size_t frame_line,
+                                     size_t frame_column,
+                                     const string16& frame_url,
+                                     const string16& frame_function)
     : line_number(frame_line),
       column_number(frame_column),
       url(frame_url),
       function(frame_function) {
 }
 
-JavascriptRuntimeError::StackFrame::~StackFrame() {
+RuntimeError::StackFrame::~StackFrame() {
 }
 
-JavascriptRuntimeError::JavascriptRuntimeError(bool from_incognito,
-                                               const string16& source,
-                                               const string16& message,
-                                               logging::LogSeverity level,
-                                               const string16& details)
-    : ExtensionError(ExtensionError::JAVASCRIPT_RUNTIME_ERROR,
+bool RuntimeError::StackFrame::operator==(
+    const RuntimeError::StackFrame& rhs) const {
+  return line_number == rhs.line_number &&
+         column_number == rhs.column_number &&
+         url == rhs.url &&
+         function == rhs.function;
+}
+RuntimeError::RuntimeError(bool from_incognito,
+                           const string16& source,
+                           const string16& message,
+                           logging::LogSeverity level,
+                           const string16& details)
+    : ExtensionError(ExtensionError::RUNTIME_ERROR,
                      std::string(),  // We don't know the id yet.
                      from_incognito,
+                     level,
                      source,
-                     message),
-      level_(level) {
+                     message) {
   ParseDetails(details);
   DetermineExtensionID();
 }
 
-JavascriptRuntimeError::~JavascriptRuntimeError() {
+RuntimeError::~RuntimeError() {
 }
 
-std::string JavascriptRuntimeError::PrintForTest() const {
+std::string RuntimeError::PrintForTest() const {
   std::string result = ExtensionError::PrintForTest() +
-         "\n  Type:    JavascriptRuntimeError"
+         "\n  Type:    RuntimeError"
          "\n  Context: " + base::UTF16ToUTF8(execution_context_url_) +
          "\n  Stack Trace: ";
   for (StackTrace::const_iterator iter = stack_trace_.begin();
@@ -129,7 +155,20 @@
   return result;
 }
 
-void JavascriptRuntimeError::ParseDetails(const string16& details) {
+bool RuntimeError::IsEqualImpl(const ExtensionError* rhs) const {
+  const RuntimeError* error = static_cast<const RuntimeError*>(rhs);
+
+  // Only look at the first frame of a stack trace to save time and group
+  // nearly-identical errors. The most recent error is kept, so there's no risk
+  // of displaying an old and inaccurate stack trace.
+  return level_ == level_ &&
+         source_ == source_ &&
+         execution_context_url_ == error->execution_context_url_ &&
+         stack_trace_.size() == error->stack_trace_.size() &&
+         (stack_trace_.empty() || stack_trace_[0] == error->stack_trace_[0]);
+}
+
+void RuntimeError::ParseDetails(const string16& details) {
   scoped_ptr<base::Value> value(
       base::JSONReader::Read(base::UTF16ToUTF8(details)));
   const base::DictionaryValue* details_value;
@@ -164,7 +203,7 @@
   }
 }
 
-void JavascriptRuntimeError::DetermineExtensionID() {
+void RuntimeError::DetermineExtensionID() {
   if (!GetExtensionIDFromGURL(GURL(source_), &extension_id_))
     GetExtensionIDFromGURL(GURL(execution_context_url_), &extension_id_);
 }
diff --git a/extensions/browser/extension_error.h b/extensions/browser/extension_error.h
index 1cd4a7b..c5be169 100644
--- a/extensions/browser/extension_error.h
+++ b/extensions/browser/extension_error.h
@@ -17,55 +17,71 @@
 class ExtensionError {
  public:
   enum Type {
-    MANIFEST_PARSING_ERROR,
-    JAVASCRIPT_RUNTIME_ERROR
+    MANIFEST_ERROR,
+    RUNTIME_ERROR
   };
 
   virtual ~ExtensionError();
 
   virtual std::string PrintForTest() const;
 
+  // Return true if this error and |rhs| are considered equal, and should be
+  // grouped together.
+  bool IsEqual(const ExtensionError* rhs) const;
+
   Type type() const { return type_; }
-  const base::string16& source() const { return source_; }
-  const base::string16& message() const { return message_; }
   const std::string& extension_id() const { return extension_id_; }
   bool from_incognito() const { return from_incognito_; }
+  logging::LogSeverity level() const { return level_; }
+  const base::string16& source() const { return source_; }
+  const base::string16& message() const { return message_; }
+  size_t occurrences() const { return occurrences_; }
+  void set_occurrences(size_t occurrences) { occurrences_ = occurrences; }
 
  protected:
   ExtensionError(Type type,
                  const std::string& extension_id,
                  bool from_incognito,
+                 logging::LogSeverity level,
                  const base::string16& source,
                  const base::string16& message);
 
+  virtual bool IsEqualImpl(const ExtensionError* rhs) const = 0;
+
   // Which type of error this is.
   Type type_;
   // The ID of the extension which caused the error.
   std::string extension_id_;
   // Whether or not the error was caused while incognito.
   bool from_incognito_;
+  // The severity level of the error.
+  logging::LogSeverity level_;
   // The source for the error; this can be a script, web page, or manifest file.
   // This is stored as a string (rather than a url) since it can be a Chrome
   // script file (e.g., event_bindings.js).
   base::string16 source_;
   // The error message itself.
   base::string16 message_;
+  // The number of times this error has occurred.
+  size_t occurrences_;
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionError);
 };
 
-class ManifestParsingError : public ExtensionError {
+class ManifestError : public ExtensionError {
  public:
-  ManifestParsingError(const std::string& extension_id,
-                       const base::string16& message);
-  virtual ~ManifestParsingError();
+  ManifestError(const std::string& extension_id,
+                const base::string16& message);
+  virtual ~ManifestError();
 
   virtual std::string PrintForTest() const OVERRIDE;
  private:
-  DISALLOW_COPY_AND_ASSIGN(ManifestParsingError);
+  virtual bool IsEqualImpl(const ExtensionError* rhs) const OVERRIDE;
+
+  DISALLOW_COPY_AND_ASSIGN(ManifestError);
 };
 
-class JavascriptRuntimeError : public ExtensionError {
+class RuntimeError : public ExtensionError {
  public:
   struct StackFrame {
     size_t line_number;
@@ -84,24 +100,27 @@
                const base::string16& frame_function  /* can be empty */);
 
     ~StackFrame();
+
+    bool operator==(const StackFrame& rhs) const;
   };
   typedef std::vector<StackFrame> StackTrace;
 
-  JavascriptRuntimeError(bool from_incognito,
-                         const base::string16& source,
-                         const base::string16& message,
-                         logging::LogSeverity level,
-                         const base::string16& details);
-  virtual ~JavascriptRuntimeError();
+  RuntimeError(bool from_incognito,
+               const base::string16& source,
+               const base::string16& message,
+               logging::LogSeverity level,
+               const base::string16& details);
+  virtual ~RuntimeError();
 
   virtual std::string PrintForTest() const OVERRIDE;
 
-  logging::LogSeverity level() const { return level_; }
   const base::string16& execution_context_url() const {
       return execution_context_url_;
   }
   const StackTrace& stack_trace() const { return stack_trace_; }
  private:
+  virtual bool IsEqualImpl(const ExtensionError* rhs) const OVERRIDE;
+
   // Parse the JSON |details| passed to the error. This includes a stack trace
   // and an execution context url.
   void ParseDetails(const base::string16& details);
@@ -109,11 +128,10 @@
   // reported source, or through the execution context url.
   void DetermineExtensionID();
 
-  logging::LogSeverity level_;
   base::string16 execution_context_url_;
   StackTrace stack_trace_;
 
-  DISALLOW_COPY_AND_ASSIGN(JavascriptRuntimeError);
+  DISALLOW_COPY_AND_ASSIGN(RuntimeError);
 };
 
 }  // namespace extensions
diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc
index fcdf6e9..3ba2a63 100644
--- a/extensions/common/manifest_constants.cc
+++ b/extensions/common/manifest_constants.cc
@@ -111,6 +111,7 @@
 const char kScriptBadge[] = "script_badge";
 const char kShiftKey[] = "shiftKey";
 const char kShortcutKey[] = "shortcutKey";
+const char kShortName[] = "short_name";
 const char kSignature[] = "signature";
 const char kSpellcheck[] = "spellcheck";
 const char kSpellcheckDictionaryFormat[] = "dictionary_format";
diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h
index ce31f7c..ff4ca85 100644
--- a/extensions/common/manifest_constants.h
+++ b/extensions/common/manifest_constants.h
@@ -117,6 +117,7 @@
   extern const char kScriptBadge[];
   extern const char kShiftKey[];
   extern const char kShortcutKey[];
+  extern const char kShortName[];
   extern const char kSignature[];
   extern const char kSpellcheck[];
   extern const char kSpellcheckDictionaryFormat[];
diff --git a/extensions/common/switches.cc b/extensions/common/switches.cc
index 228a557..3c8ab10 100644
--- a/extensions/common/switches.cc
+++ b/extensions/common/switches.cc
@@ -22,6 +22,10 @@
 const char kEnableExperimentalExtensionApis[] =
     "enable-experimental-extension-apis";
 
+// Allows the ErrorConsole to collect runtime and manifest errors, and display
+// them in the chrome:extensions page.
+const char kErrorConsole[] = "error-console";
+
 // Enables extensions running scripts on chrome:// URLs.
 // Extensions still need to explicitly request access to chrome:// URLs in the
 // manifest.
diff --git a/extensions/common/switches.h b/extensions/common/switches.h
index 7fceb54..43cc0f1 100644
--- a/extensions/common/switches.h
+++ b/extensions/common/switches.h
@@ -14,6 +14,7 @@
 extern const char kAllowLegacyExtensionManifests[];
 extern const char kAllowScriptingGallery[];
 extern const char kEnableExperimentalExtensionApis[];
+extern const char kErrorConsole[];
 extern const char kExtensionsOnChromeURLs[];
 extern const char kShowComponentExtensionOptions[];
 
diff --git a/extensions/common/url_pattern.cc b/extensions/common/url_pattern.cc
index 5400567..93b3270 100644
--- a/extensions/common/url_pattern.cc
+++ b/extensions/common/url_pattern.cc
@@ -21,7 +21,7 @@
 // Note: keep this array in sync with kValidSchemeMasks.
 const char* kValidSchemes[] = {
   chrome::kHttpScheme,
-  chrome::kHttpsScheme,
+  content::kHttpsScheme,
   chrome::kFileScheme,
   chrome::kFtpScheme,
   chrome::kChromeUIScheme,
diff --git a/google_apis/gaia/oauth2_token_service.cc b/google_apis/gaia/oauth2_token_service.cc
new file mode 100644
index 0000000..0834c60
--- /dev/null
+++ b/google_apis/gaia/oauth2_token_service.cc
@@ -0,0 +1,608 @@
+// 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 "google_apis/gaia/oauth2_token_service.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/rand_util.h"
+#include "base/stl_util.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_access_token_consumer.h"
+#include "google_apis/gaia/oauth2_access_token_fetcher.h"
+#include "net/url_request/url_request_context_getter.h"
+
+int OAuth2TokenService::max_fetch_retry_num_ = 5;
+
+OAuth2TokenService::RequestImpl::RequestImpl(
+    OAuth2TokenService::Consumer* consumer)
+    : consumer_(consumer) {
+}
+
+OAuth2TokenService::RequestImpl::~RequestImpl() {
+  DCHECK(CalledOnValidThread());
+}
+
+void OAuth2TokenService::RequestImpl::InformConsumer(
+    const GoogleServiceAuthError& error,
+    const std::string& access_token,
+    const base::Time& expiration_date) {
+  DCHECK(CalledOnValidThread());
+  if (error.state() == GoogleServiceAuthError::NONE)
+    consumer_->OnGetTokenSuccess(this, access_token, expiration_date);
+  else
+    consumer_->OnGetTokenFailure(this, error);
+}
+
+// Class that fetches OAuth2 access tokens for given scopes and refresh token.
+//
+// It aims to meet OAuth2TokenService's requirements on token fetching. Retry
+// mechanism is used to handle failures.
+//
+// To use this class, call CreateAndStart() to create and start a Fetcher.
+//
+// The Fetcher will call back the service by calling
+// OAuth2TokenService::OnFetchComplete() when it completes fetching, if it is
+// not destructed before it completes fetching; if the Fetcher is destructed
+// before it completes fetching, the service will never be called back. The
+// Fetcher destructs itself after calling back the service when finishes
+// fetching.
+//
+// Requests that are waiting for the fetching results of this Fetcher can be
+// added to the Fetcher by calling
+// OAuth2TokenService::Fetcher::AddWaitingRequest() before the Fetcher completes
+// fetching.
+//
+// The waiting requests are taken as weak pointers and they can be deleted. The
+// waiting requests will be called back with fetching results if they are not
+// deleted
+// - when the Fetcher completes fetching, if the Fetcher is not destructed
+//   before it completes fetching, or
+// - when the Fetcher is destructed if the Fetcher is destructed before it
+//   completes fetching (in this case, the waiting requests will be called back
+//   with error).
+class OAuth2TokenService::Fetcher : public OAuth2AccessTokenConsumer {
+ public:
+  // Creates a Fetcher and starts fetching an OAuth2 access token for
+  // |refresh_token| and |scopes| in the request context obtained by |getter|.
+  // The given |oauth2_token_service| will be informed when fetching is done.
+  static Fetcher* CreateAndStart(OAuth2TokenService* oauth2_token_service,
+                                 net::URLRequestContextGetter* getter,
+                                 const std::string& chrome_client_id,
+                                 const std::string& chrome_client_secret,
+                                 const std::string& refresh_token,
+                                 const OAuth2TokenService::ScopeSet& scopes,
+                                 base::WeakPtr<RequestImpl> waiting_request);
+  virtual ~Fetcher();
+
+  // Add a request that is waiting for the result of this Fetcher.
+  void AddWaitingRequest(base::WeakPtr<RequestImpl> waiting_request);
+
+  void Cancel();
+
+  const OAuth2TokenService::ScopeSet& GetScopeSet() const;
+  const std::string& GetRefreshToken() const;
+
+  // The error result from this fetcher.
+  const GoogleServiceAuthError& error() const { return error_; }
+
+ protected:
+   // OAuth2AccessTokenConsumer
+  virtual void OnGetTokenSuccess(const std::string& access_token,
+                                 const base::Time& expiration_date) OVERRIDE;
+  virtual void OnGetTokenFailure(const GoogleServiceAuthError& error) OVERRIDE;
+
+ private:
+  Fetcher(OAuth2TokenService* oauth2_token_service,
+          net::URLRequestContextGetter* getter,
+          const std::string& chrome_client_id,
+          const std::string& chrome_client_secret,
+          const std::string& refresh_token,
+          const OAuth2TokenService::ScopeSet& scopes,
+          base::WeakPtr<RequestImpl> waiting_request);
+  void Start();
+  void InformWaitingRequests();
+  void InformWaitingRequestsAndDelete();
+  static bool ShouldRetry(const GoogleServiceAuthError& error);
+  int64 ComputeExponentialBackOffMilliseconds(int retry_num);
+
+  // |oauth2_token_service_| remains valid for the life of this Fetcher, since
+  // this Fetcher is destructed in the dtor of the OAuth2TokenService or is
+  // scheduled for deletion at the end of OnGetTokenFailure/OnGetTokenSuccess
+  // (whichever comes first).
+  OAuth2TokenService* const oauth2_token_service_;
+  scoped_refptr<net::URLRequestContextGetter> getter_;
+  const std::string refresh_token_;
+  const OAuth2TokenService::ScopeSet scopes_;
+  std::vector<base::WeakPtr<RequestImpl> > waiting_requests_;
+
+  int retry_number_;
+  base::OneShotTimer<OAuth2TokenService::Fetcher> retry_timer_;
+  scoped_ptr<OAuth2AccessTokenFetcher> fetcher_;
+
+  // Variables that store fetch results.
+  // Initialized to be GoogleServiceAuthError::SERVICE_UNAVAILABLE to handle
+  // destruction.
+  GoogleServiceAuthError error_;
+  std::string access_token_;
+  base::Time expiration_date_;
+  // OAuth2 client id and secret.
+  std::string chrome_client_id_;
+  std::string chrome_client_secret_;
+
+  DISALLOW_COPY_AND_ASSIGN(Fetcher);
+};
+
+// static
+OAuth2TokenService::Fetcher* OAuth2TokenService::Fetcher::CreateAndStart(
+    OAuth2TokenService* oauth2_token_service,
+    net::URLRequestContextGetter* getter,
+    const std::string& chrome_client_id,
+    const std::string& chrome_client_secret,
+    const std::string& refresh_token,
+    const OAuth2TokenService::ScopeSet& scopes,
+    base::WeakPtr<RequestImpl> waiting_request) {
+  OAuth2TokenService::Fetcher* fetcher = new Fetcher(
+      oauth2_token_service,
+      getter,
+      chrome_client_id,
+      chrome_client_secret,
+      refresh_token,
+      scopes,
+      waiting_request);
+  fetcher->Start();
+  return fetcher;
+}
+
+OAuth2TokenService::Fetcher::Fetcher(
+    OAuth2TokenService* oauth2_token_service,
+    net::URLRequestContextGetter* getter,
+    const std::string& chrome_client_id,
+    const std::string& chrome_client_secret,
+    const std::string& refresh_token,
+    const OAuth2TokenService::ScopeSet& scopes,
+    base::WeakPtr<RequestImpl> waiting_request)
+    : oauth2_token_service_(oauth2_token_service),
+      getter_(getter),
+      refresh_token_(refresh_token),
+      scopes_(scopes),
+      retry_number_(0),
+      error_(GoogleServiceAuthError::SERVICE_UNAVAILABLE),
+      chrome_client_id_(chrome_client_id),
+      chrome_client_secret_(chrome_client_secret) {
+  DCHECK(oauth2_token_service_);
+  DCHECK(getter_.get());
+  DCHECK(refresh_token_.length());
+  waiting_requests_.push_back(waiting_request);
+}
+
+OAuth2TokenService::Fetcher::~Fetcher() {
+  // Inform the waiting requests if it has not done so.
+  if (waiting_requests_.size())
+    InformWaitingRequests();
+}
+
+void OAuth2TokenService::Fetcher::Start() {
+  fetcher_.reset(new OAuth2AccessTokenFetcher(this, getter_.get()));
+  fetcher_->Start(chrome_client_id_,
+                  chrome_client_secret_,
+                  refresh_token_,
+                  std::vector<std::string>(scopes_.begin(), scopes_.end()));
+  retry_timer_.Stop();
+}
+
+void OAuth2TokenService::Fetcher::OnGetTokenSuccess(
+    const std::string& access_token,
+    const base::Time& expiration_date) {
+  fetcher_.reset();
+
+  // Fetch completes.
+  error_ = GoogleServiceAuthError::AuthErrorNone();
+  access_token_ = access_token;
+  expiration_date_ = expiration_date;
+
+  // Subclasses may override this method to skip caching in some cases, but
+  // we still inform all waiting Consumers of a successful token fetch below.
+  // This is intentional -- some consumers may need the token for cleanup
+  // tasks. https://chromiumcodereview.appspot.com/11312124/
+  oauth2_token_service_->RegisterCacheEntry(refresh_token_,
+                                            scopes_,
+                                            access_token_,
+                                            expiration_date_);
+  InformWaitingRequestsAndDelete();
+}
+
+void OAuth2TokenService::Fetcher::OnGetTokenFailure(
+    const GoogleServiceAuthError& error) {
+  fetcher_.reset();
+
+  if (ShouldRetry(error) && retry_number_ < max_fetch_retry_num_) {
+    int64 backoff = ComputeExponentialBackOffMilliseconds(retry_number_);
+    ++retry_number_;
+    retry_timer_.Stop();
+    retry_timer_.Start(FROM_HERE,
+                       base::TimeDelta::FromMilliseconds(backoff),
+                       this,
+                       &OAuth2TokenService::Fetcher::Start);
+    return;
+  }
+
+  error_ = error;
+  InformWaitingRequestsAndDelete();
+}
+
+// Returns an exponential backoff in milliseconds including randomness less than
+// 1000 ms when retrying fetching an OAuth2 access token.
+int64 OAuth2TokenService::Fetcher::ComputeExponentialBackOffMilliseconds(
+    int retry_num) {
+  DCHECK(retry_num < max_fetch_retry_num_);
+  int64 exponential_backoff_in_seconds = 1 << retry_num;
+  // Returns a backoff with randomness < 1000ms
+  return (exponential_backoff_in_seconds + base::RandDouble()) * 1000;
+}
+
+// static
+bool OAuth2TokenService::Fetcher::ShouldRetry(
+    const GoogleServiceAuthError& error) {
+  GoogleServiceAuthError::State error_state = error.state();
+  return error_state == GoogleServiceAuthError::CONNECTION_FAILED ||
+         error_state == GoogleServiceAuthError::REQUEST_CANCELED ||
+         error_state == GoogleServiceAuthError::SERVICE_UNAVAILABLE;
+}
+
+void OAuth2TokenService::Fetcher::InformWaitingRequests() {
+  std::vector<base::WeakPtr<RequestImpl> >::const_iterator iter =
+      waiting_requests_.begin();
+  for (; iter != waiting_requests_.end(); ++iter) {
+    base::WeakPtr<RequestImpl> waiting_request = *iter;
+    if (waiting_request.get())
+      waiting_request->InformConsumer(error_, access_token_, expiration_date_);
+  }
+  waiting_requests_.clear();
+}
+
+void OAuth2TokenService::Fetcher::InformWaitingRequestsAndDelete() {
+  // Deregisters itself from the service to prevent more waiting requests to
+  // be added when it calls back the waiting requests.
+  oauth2_token_service_->OnFetchComplete(this);
+  InformWaitingRequests();
+  base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+}
+
+void OAuth2TokenService::Fetcher::AddWaitingRequest(
+    base::WeakPtr<OAuth2TokenService::RequestImpl> waiting_request) {
+  waiting_requests_.push_back(waiting_request);
+}
+
+void OAuth2TokenService::Fetcher::Cancel() {
+  fetcher_.reset();
+  retry_timer_.Stop();
+  error_ = GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED);
+  InformWaitingRequestsAndDelete();
+}
+
+const OAuth2TokenService::ScopeSet& OAuth2TokenService::Fetcher::GetScopeSet()
+    const {
+  return scopes_;
+}
+
+const std::string& OAuth2TokenService::Fetcher::GetRefreshToken() const {
+  return refresh_token_;
+}
+
+OAuth2TokenService::Request::Request() {
+}
+
+OAuth2TokenService::Request::~Request() {
+}
+
+OAuth2TokenService::Consumer::Consumer() {
+}
+
+OAuth2TokenService::Consumer::~Consumer() {
+}
+
+OAuth2TokenService::OAuth2TokenService() {
+}
+
+OAuth2TokenService::~OAuth2TokenService() {
+  // Release all the pending fetchers.
+  STLDeleteContainerPairSecondPointers(
+      pending_fetchers_.begin(), pending_fetchers_.end());
+}
+
+void OAuth2TokenService::AddObserver(Observer* observer) {
+  observer_list_.AddObserver(observer);
+}
+
+void OAuth2TokenService::RemoveObserver(Observer* observer) {
+  observer_list_.RemoveObserver(observer);
+}
+
+bool OAuth2TokenService::RefreshTokenIsAvailable() {
+  return !GetRefreshToken().empty();
+}
+
+scoped_ptr<OAuth2TokenService::Request> OAuth2TokenService::StartRequest(
+    const OAuth2TokenService::ScopeSet& scopes,
+    OAuth2TokenService::Consumer* consumer) {
+  return StartRequestForClientWithContext(
+      GetRequestContext(),
+      GaiaUrls::GetInstance()->oauth2_chrome_client_id(),
+      GaiaUrls::GetInstance()->oauth2_chrome_client_secret(),
+      scopes,
+      consumer);
+}
+
+scoped_ptr<OAuth2TokenService::Request>
+OAuth2TokenService::StartRequestForClient(
+    const std::string& client_id,
+    const std::string& client_secret,
+    const OAuth2TokenService::ScopeSet& scopes,
+    OAuth2TokenService::Consumer* consumer) {
+  return StartRequestForClientWithContext(
+      GetRequestContext(),
+      client_id,
+      client_secret,
+      scopes,
+      consumer);
+}
+
+scoped_ptr<OAuth2TokenService::Request>
+OAuth2TokenService::StartRequestWithContext(
+    net::URLRequestContextGetter* getter,
+    const ScopeSet& scopes,
+    Consumer* consumer) {
+  return StartRequestForClientWithContext(
+      getter,
+      GaiaUrls::GetInstance()->oauth2_chrome_client_id(),
+      GaiaUrls::GetInstance()->oauth2_chrome_client_secret(),
+      scopes,
+      consumer);
+}
+
+scoped_ptr<OAuth2TokenService::Request>
+OAuth2TokenService::StartRequestForClientWithContext(
+    net::URLRequestContextGetter* getter,
+    const std::string& client_id,
+    const std::string& client_secret,
+    const ScopeSet& scopes,
+    Consumer* consumer) {
+  DCHECK(CalledOnValidThread());
+
+  scoped_ptr<RequestImpl> request(new RequestImpl(consumer));
+
+  if (!RefreshTokenIsAvailable()) {
+    base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+        &RequestImpl::InformConsumer,
+        request->AsWeakPtr(),
+        GoogleServiceAuthError(GoogleServiceAuthError::USER_NOT_SIGNED_UP),
+        std::string(),
+        base::Time()));
+    return request.PassAs<Request>();
+  }
+
+  if (HasCacheEntry(scopes)) {
+    StartCacheLookupRequest(request.get(), scopes, consumer);
+  } else {
+    FetchOAuth2Token(request.get(),
+                     getter,
+                     client_id,
+                     client_secret,
+                     scopes);
+  }
+  return request.PassAs<Request>();
+}
+
+void OAuth2TokenService::FetchOAuth2Token(RequestImpl* request,
+                                          net::URLRequestContextGetter* getter,
+                                          const std::string& client_id,
+                                          const std::string& client_secret,
+                                          const ScopeSet& scopes) {
+  std::string refresh_token = GetRefreshToken();
+
+  // If there is already a pending fetcher for |scopes| and |refresh_token|,
+  // simply register this |request| for those results rather than starting
+  // a new fetcher.
+  FetchParameters fetch_parameters = std::make_pair(refresh_token, scopes);
+  std::map<FetchParameters, Fetcher*>::iterator iter =
+      pending_fetchers_.find(fetch_parameters);
+  if (iter != pending_fetchers_.end()) {
+    iter->second->AddWaitingRequest(request->AsWeakPtr());
+    return;
+  }
+
+  pending_fetchers_[fetch_parameters] =
+      Fetcher::CreateAndStart(this,
+                              getter,
+                              client_id,
+                              client_secret,
+                              refresh_token,
+                              scopes,
+                              request->AsWeakPtr());
+}
+
+void OAuth2TokenService::StartCacheLookupRequest(
+    RequestImpl* request,
+    const OAuth2TokenService::ScopeSet& scopes,
+    OAuth2TokenService::Consumer* consumer) {
+  CHECK(HasCacheEntry(scopes));
+  const CacheEntry* cache_entry = GetCacheEntry(scopes);
+  base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+      &RequestImpl::InformConsumer,
+      request->AsWeakPtr(),
+      GoogleServiceAuthError(GoogleServiceAuthError::NONE),
+      cache_entry->access_token,
+      cache_entry->expiration_date));
+}
+
+void OAuth2TokenService::InvalidateToken(const ScopeSet& scopes,
+                                         const std::string& invalid_token) {
+  DCHECK(CalledOnValidThread());
+  RemoveCacheEntry(scopes, invalid_token);
+}
+
+void OAuth2TokenService::OnFetchComplete(Fetcher* fetcher) {
+  DCHECK(CalledOnValidThread());
+
+  // Update the auth error state so auth errors are appropriately communicated
+  // to the user.
+  UpdateAuthError(fetcher->error());
+
+  // Note |fetcher| is recorded in |pending_fetcher_| mapped to its refresh
+  // token and scope set. This is guaranteed as follows; here a Fetcher is said
+  // to be uncompleted if it has not finished calling back
+  // OAuth2TokenService::OnFetchComplete().
+  //
+  // (1) All the live Fetchers are created by this service.
+  //     This is because (1) all the live Fetchers are created by a live
+  //     service, as all the fetchers created by a service are destructed in the
+  //     service's dtor.
+  //
+  // (2) All the uncompleted Fetchers created by this service are recorded in
+  //     |pending_fetchers_|.
+  //     This is because (1) all the created Fetchers are added to
+  //     |pending_fetchers_| (in method StartRequest()) and (2) method
+  //     OnFetchComplete() is the only place where a Fetcher is erased from
+  //     |pending_fetchers_|. Note no Fetcher is erased in method
+  //     StartRequest().
+  //
+  // (3) Each of the Fetchers recorded in |pending_fetchers_| is mapped to its
+  //     refresh token and ScopeSet. This is guaranteed by Fetcher creation in
+  //     method StartRequest().
+  //
+  // When this method is called, |fetcher| is alive and uncompleted.
+  // By (1), |fetcher| is created by this service.
+  // Then by (2), |fetcher| is recorded in |pending_fetchers_|.
+  // Then by (3), |fetcher_| is mapped to its refresh token and ScopeSet.
+  std::map<FetchParameters, Fetcher*>::iterator iter =
+    pending_fetchers_.find(std::make_pair(
+        fetcher->GetRefreshToken(), fetcher->GetScopeSet()));
+  DCHECK(iter != pending_fetchers_.end());
+  DCHECK_EQ(fetcher, iter->second);
+  pending_fetchers_.erase(iter);
+}
+
+bool OAuth2TokenService::HasCacheEntry(
+    const OAuth2TokenService::ScopeSet& scopes) {
+  const CacheEntry* cache_entry = GetCacheEntry(scopes);
+  return cache_entry && cache_entry->access_token.length();
+}
+
+const OAuth2TokenService::CacheEntry* OAuth2TokenService::GetCacheEntry(
+    const OAuth2TokenService::ScopeSet& scopes) {
+  DCHECK(CalledOnValidThread());
+  TokenCache::iterator token_iterator = token_cache_.find(scopes);
+  if (token_iterator == token_cache_.end())
+    return NULL;
+  if (token_iterator->second.expiration_date <= base::Time::Now()) {
+    token_cache_.erase(token_iterator);
+    return NULL;
+  }
+  return &token_iterator->second;
+}
+
+bool OAuth2TokenService::RemoveCacheEntry(
+    const OAuth2TokenService::ScopeSet& scopes,
+    const std::string& token_to_remove) {
+  DCHECK(CalledOnValidThread());
+  TokenCache::iterator token_iterator = token_cache_.find(scopes);
+  if (token_iterator != token_cache_.end() &&
+      token_iterator->second.access_token == token_to_remove) {
+    token_cache_.erase(token_iterator);
+    return true;
+  }
+  return false;
+}
+
+void OAuth2TokenService::RegisterCacheEntry(
+    const std::string& refresh_token,
+    const OAuth2TokenService::ScopeSet& scopes,
+    const std::string& access_token,
+    const base::Time& expiration_date) {
+  DCHECK(CalledOnValidThread());
+
+  CacheEntry& token = token_cache_[scopes];
+  token.access_token = access_token;
+  token.expiration_date = expiration_date;
+}
+
+void OAuth2TokenService::UpdateAuthError(const GoogleServiceAuthError& error) {
+  // Default implementation does nothing.
+}
+
+void OAuth2TokenService::ClearCache() {
+  DCHECK(CalledOnValidThread());
+  token_cache_.clear();
+}
+
+void OAuth2TokenService::CancelAllRequests() {
+  std::vector<Fetcher*> fetchers_to_cancel;
+  for (std::map<FetchParameters, Fetcher*>::iterator iter =
+           pending_fetchers_.begin();
+       iter != pending_fetchers_.end();
+       ++iter) {
+    fetchers_to_cancel.push_back(iter->second);
+  }
+  CancelFetchers(fetchers_to_cancel);
+}
+
+void OAuth2TokenService::CancelRequestsForToken(
+    const std::string& refresh_token) {
+  std::vector<Fetcher*> fetchers_to_cancel;
+  for (std::map<FetchParameters, Fetcher*>::iterator iter =
+           pending_fetchers_.begin();
+       iter != pending_fetchers_.end();
+       ++iter) {
+    if (iter->first.first == refresh_token)
+      fetchers_to_cancel.push_back(iter->second);
+  }
+  CancelFetchers(fetchers_to_cancel);
+}
+
+void OAuth2TokenService::CancelFetchers(
+    std::vector<Fetcher*> fetchers_to_cancel) {
+  for (std::vector<OAuth2TokenService::Fetcher*>::iterator iter =
+           fetchers_to_cancel.begin();
+       iter != fetchers_to_cancel.end();
+       ++iter) {
+    (*iter)->Cancel();
+  }
+}
+
+void OAuth2TokenService::FireRefreshTokenAvailable(
+    const std::string& account_id) {
+  FOR_EACH_OBSERVER(Observer, observer_list_,
+                    OnRefreshTokenAvailable(account_id));
+}
+
+void OAuth2TokenService::FireRefreshTokenRevoked(
+    const std::string& account_id) {
+  FOR_EACH_OBSERVER(Observer, observer_list_,
+                    OnRefreshTokenRevoked(account_id));
+}
+
+void OAuth2TokenService::FireRefreshTokensLoaded() {
+  FOR_EACH_OBSERVER(Observer, observer_list_, OnRefreshTokensLoaded());
+}
+
+void OAuth2TokenService::FireRefreshTokensCleared() {
+  FOR_EACH_OBSERVER(Observer, observer_list_, OnRefreshTokensCleared());
+}
+
+int OAuth2TokenService::cache_size_for_testing() const {
+  return token_cache_.size();
+}
+
+void OAuth2TokenService::set_max_authorization_token_fetch_retries_for_testing(
+    int max_retries) {
+  DCHECK(CalledOnValidThread());
+  max_fetch_retry_num_ = max_retries;
+}
diff --git a/google_apis/gaia/oauth2_token_service.h b/google_apis/gaia/oauth2_token_service.h
new file mode 100644
index 0000000..967dc93
--- /dev/null
+++ b/google_apis/gaia/oauth2_token_service.h
@@ -0,0 +1,285 @@
+// 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 GOOGLE_APIS_GAIA_OAUTH2_TOKEN_SERVICE_H_
+#define GOOGLE_APIS_GAIA_OAUTH2_TOKEN_SERVICE_H_
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/time/time.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+class GoogleServiceAuthError;
+
+// Abstract base class for a service that fetches and caches OAuth2 access
+// tokens. Concrete subclasses should implement GetRefreshToken to return
+// the appropriate refresh token.
+//
+// All calls are expected from the UI thread.
+//
+// To use this service, call StartRequest() with a given set of scopes and a
+// consumer of the request results. The consumer is required to outlive the
+// request. The request can be deleted. The consumer may be called back
+// asynchronously with the fetch results.
+//
+// - If the consumer is not called back before the request is deleted, it will
+//   never be called back.
+//   Note in this case, the actual network requests are not canceled and the
+//   cache will be populated with the fetched results; it is just the consumer
+//   callback that is aborted.
+//
+// - Otherwise the consumer will be called back with the request and the fetch
+//   results.
+//
+// The caller of StartRequest() owns the returned request and is responsible to
+// delete the request even once the callback has been invoked.
+class OAuth2TokenService : public base::NonThreadSafe {
+ public:
+  // Class representing a request that fetches an OAuth2 access token.
+  class Request {
+   public:
+    virtual ~Request();
+   protected:
+    Request();
+  };
+
+  // Class representing the consumer of a Request passed to |StartRequest|,
+  // which will be called back when the request completes.
+  class Consumer {
+   public:
+    Consumer();
+    virtual ~Consumer();
+    // |request| is a Request that is started by this consumer and has
+    // completed.
+    virtual void OnGetTokenSuccess(const Request* request,
+                                   const std::string& access_token,
+                                   const base::Time& expiration_time) = 0;
+    virtual void OnGetTokenFailure(const Request* request,
+                                   const GoogleServiceAuthError& error) = 0;
+  };
+
+  // Classes that want to listen for token availability should implement this
+  // interface and register with the AddObserver() call.
+  // TODO(rogerta): may get rid of |error| argument for OnRefreshTokenRevoked()
+  // once we stop supporting ClientLogin.  Need to evaluate if its still useful.
+  class Observer {
+   public:
+    // Called whenever a new login-scoped refresh token is available for
+    // account |account_id|. Once available, access tokens can be retrieved for
+    // this account.  This is called during initial startup for each token
+    // loaded.
+    virtual void OnRefreshTokenAvailable(const std::string& account_id) {}
+    // Called whenever the login-scoped refresh token becomes unavailable for
+    // account |account_id|.
+    virtual void OnRefreshTokenRevoked(const std::string& account_id) {}
+    // Called after all refresh tokens are loaded during OAuth2TokenService
+    // startup.
+    virtual void OnRefreshTokensLoaded() {}
+    // Called after all refresh tokens are removed from OAuth2TokenService.
+    virtual void OnRefreshTokensCleared() {}
+   protected:
+    virtual ~Observer() {}
+  };
+
+  // A set of scopes in OAuth2 authentication.
+  typedef std::set<std::string> ScopeSet;
+
+  OAuth2TokenService();
+  virtual ~OAuth2TokenService();
+
+  // Add or remove observers of this token service.
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+  // Checks in the cache for a valid access token, and if not found starts
+  // a request for an OAuth2 access token using the OAuth2 refresh token
+  // maintained by this instance. The caller owns the returned Request.
+  // |scopes| is the set of scopes to get an access token for, |consumer| is
+  // the object that will be called back with results if the returned request
+  // is not deleted.
+  // TODO(atwilson): Make this non-virtual when we change
+  // ProfileOAuth2TokenServiceRequestTest to use FakeProfileOAuth2TokenService.
+  virtual scoped_ptr<Request> StartRequest(const ScopeSet& scopes,
+                                           Consumer* consumer);
+
+  // This method does the same as |StartRequest| except it uses |client_id| and
+  // |client_secret| to identify OAuth client app instead of using
+  // Chrome's default values.
+  scoped_ptr<Request> StartRequestForClient(
+      const std::string& client_id,
+      const std::string& client_secret,
+      const ScopeSet& scopes,
+      Consumer* consumer);
+
+  // This method does the same as |StartRequest| except it uses the request
+  // context given by |getter| instead of using the one returned by
+  // |GetRequestContext| implemented by derived classes.
+  scoped_ptr<Request> StartRequestWithContext(
+      net::URLRequestContextGetter* getter,
+      const ScopeSet& scopes,
+      Consumer* consumer);
+
+  // Returns true if a refresh token exists. If false, calls to
+  // |StartRequest| will result in a Consumer::OnGetTokenFailure callback.
+  virtual bool RefreshTokenIsAvailable();
+
+  // Mark an OAuth2 access token as invalid. This should be done if the token
+  // was received from this class, but was not accepted by the server (e.g.,
+  // the server returned 401 Unauthorized). The token will be removed from the
+  // cache for the given scopes.
+  virtual void InvalidateToken(const ScopeSet& scopes,
+                               const std::string& invalid_token);
+
+  // Return the current number of entries in the cache.
+  int cache_size_for_testing() const;
+  void set_max_authorization_token_fetch_retries_for_testing(int max_retries);
+
+ protected:
+  // Implements a cancelable |OAuth2TokenService::Request|, which should be
+  // operated on the UI thread.
+  // TODO(davidroche): move this out of header file.
+  class RequestImpl : public base::SupportsWeakPtr<RequestImpl>,
+                      public base::NonThreadSafe,
+                      public Request {
+   public:
+    // |consumer| is required to outlive this.
+    explicit RequestImpl(Consumer* consumer);
+    virtual ~RequestImpl();
+
+    // Informs |consumer_| that this request is completed.
+    void InformConsumer(const GoogleServiceAuthError& error,
+                        const std::string& access_token,
+                        const base::Time& expiration_date);
+
+   private:
+    // |consumer_| to call back when this request completes.
+    Consumer* const consumer_;
+  };
+
+  // Subclasses should return the refresh token maintained.
+  // If no token is available, return an empty string.
+  virtual std::string GetRefreshToken() = 0;
+
+  // Subclasses can override if they want to report errors to the user.
+  virtual void UpdateAuthError(const GoogleServiceAuthError& error);
+
+  // Add a new entry to the cache.
+  // Subclasses can override if there are implementation-specific reasons
+  // that an access token should ever not be cached.
+  virtual void RegisterCacheEntry(const std::string& refresh_token,
+                                  const ScopeSet& scopes,
+                                  const std::string& access_token,
+                                  const base::Time& expiration_date);
+
+  // Returns true if GetCacheEntry would return a valid cache entry for the
+  // given scopes.
+  bool HasCacheEntry(const ScopeSet& scopes);
+
+  // Posts a task to fire the Consumer callback with the cached token.  Must
+  // Must only be called if HasCacheEntry() returns true.
+  void StartCacheLookupRequest(RequestImpl* request,
+                               const ScopeSet& scopes,
+                               Consumer* consumer);
+
+  // Clears the internal token cache.
+  void ClearCache();
+
+  // Cancels all requests that are currently in progress.
+  void CancelAllRequests();
+
+  // Cancels all requests related to a given refresh token.
+  void CancelRequestsForToken(const std::string& refresh_token);
+
+  // Called by subclasses to notify observers.
+  void FireRefreshTokenAvailable(const std::string& account_id);
+  void FireRefreshTokenRevoked(const std::string& account_id);
+  void FireRefreshTokensLoaded();
+  void FireRefreshTokensCleared();
+
+  // Derived classes must provide a request context used for fetching access
+  // tokens with the |StartRequest| method.
+  virtual net::URLRequestContextGetter* GetRequestContext() = 0;
+
+  // Fetches an OAuth token for the specified client/scopes. Virtual so it can
+  // be overridden for tests and for platform-specific behavior on Android.
+  virtual void FetchOAuth2Token(RequestImpl* request,
+                                net::URLRequestContextGetter* getter,
+                                const std::string& client_id,
+                                const std::string& client_secret,
+                                const ScopeSet& scopes);
+
+ private:
+  // Class that fetches an OAuth2 access token for a given set of scopes and
+  // OAuth2 refresh token.
+  class Fetcher;
+  friend class Fetcher;
+
+  // Struct that contains the information of an OAuth2 access token.
+  struct CacheEntry {
+    std::string access_token;
+    base::Time expiration_date;
+  };
+
+  // This method does the same as |StartRequestWithContext| except it
+  // uses |client_id| and |client_secret| to identify OAuth
+  // client app instead of using Chrome's default values.
+  scoped_ptr<Request> StartRequestForClientWithContext(
+      net::URLRequestContextGetter* getter,
+      const std::string& client_id,
+      const std::string& client_secret,
+      const ScopeSet& scopes,
+      Consumer* consumer);
+
+  // Returns a currently valid OAuth2 access token for the given set of scopes,
+  // or NULL if none have been cached. Note the user of this method should
+  // ensure no entry with the same |scopes| is added before the usage of the
+  // returned entry is done.
+  const CacheEntry* GetCacheEntry(const ScopeSet& scopes);
+
+
+  // Removes an access token for the given set of scopes from the cache.
+  // Returns true if the entry was removed, otherwise false.
+  bool RemoveCacheEntry(const OAuth2TokenService::ScopeSet& scopes,
+                        const std::string& token_to_remove);
+
+
+  // Called when |fetcher| finishes fetching.
+  void OnFetchComplete(Fetcher* fetcher);
+
+  // Called when a number of fetchers need to be canceled.
+  void CancelFetchers(std::vector<Fetcher*> fetchers_to_cancel);
+
+  // The cache of currently valid tokens.
+  typedef std::map<ScopeSet, CacheEntry> TokenCache;
+  TokenCache token_cache_;
+
+  // The parameters (refresh token and scope set) used to fetch an OAuth2 access
+  // token.
+  typedef std::pair<std::string, ScopeSet> FetchParameters;
+  // A map from fetch parameters to a fetcher that is fetching an OAuth2 access
+  // token using these parameters.
+  std::map<FetchParameters, Fetcher*> pending_fetchers_;
+
+  // List of observers to notify when token availability changes.
+  // Makes sure list is empty on destruction.
+  ObserverList<Observer, true> observer_list_;
+
+  // Maximum number of retries in fetching an OAuth2 access token.
+  static int max_fetch_retry_num_;
+
+  DISALLOW_COPY_AND_ASSIGN(OAuth2TokenService);
+};
+
+#endif  // GOOGLE_APIS_GAIA_OAUTH2_TOKEN_SERVICE_H_
diff --git a/google_apis/gaia/oauth2_token_service_test_util.cc b/google_apis/gaia/oauth2_token_service_test_util.cc
new file mode 100644
index 0000000..2aae59b
--- /dev/null
+++ b/google_apis/gaia/oauth2_token_service_test_util.cc
@@ -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.
+
+#include "google_apis/gaia/oauth2_token_service_test_util.h"
+
+#include "base/strings/stringprintf.h"
+
+namespace {
+const char kValidTokenResponse[] =
+    "{"
+    "  \"access_token\": \"%s\","
+    "  \"expires_in\": %d,"
+    "  \"token_type\": \"Bearer\""
+    "}";
+}
+
+std::string GetValidTokenResponse(std::string token, int expiration) {
+  return base::StringPrintf(kValidTokenResponse, token.c_str(), expiration);
+}
+
+TestingOAuth2TokenServiceConsumer::TestingOAuth2TokenServiceConsumer()
+    : number_of_successful_tokens_(0),
+      last_error_(GoogleServiceAuthError::AuthErrorNone()),
+      number_of_errors_(0) {
+}
+
+TestingOAuth2TokenServiceConsumer::~TestingOAuth2TokenServiceConsumer() {
+}
+
+void TestingOAuth2TokenServiceConsumer::OnGetTokenSuccess(
+    const OAuth2TokenService::Request* request,
+    const std::string& token,
+    const base::Time& expiration_date) {
+  last_token_ = token;
+  ++number_of_successful_tokens_;
+}
+
+void TestingOAuth2TokenServiceConsumer::OnGetTokenFailure(
+    const OAuth2TokenService::Request* request,
+    const GoogleServiceAuthError& error) {
+  last_error_ = error;
+  ++number_of_errors_;
+}
diff --git a/google_apis/gaia/oauth2_token_service_test_util.h b/google_apis/gaia/oauth2_token_service_test_util.h
new file mode 100644
index 0000000..53ccdf8
--- /dev/null
+++ b/google_apis/gaia/oauth2_token_service_test_util.h
@@ -0,0 +1,34 @@
+// 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 GOOGLE_APIS_GAIA_OAUTH2_TOKEN_SERVICE_TEST_UTIL_H_
+#define GOOGLE_APIS_GAIA_OAUTH2_TOKEN_SERVICE_TEST_UTIL_H_
+
+#include <string>
+
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_token_service.h"
+
+std::string GetValidTokenResponse(std::string token, int expiration);
+
+// A simple testing consumer.
+class TestingOAuth2TokenServiceConsumer : public OAuth2TokenService::Consumer {
+ public:
+  TestingOAuth2TokenServiceConsumer();
+  virtual ~TestingOAuth2TokenServiceConsumer();
+
+  // OAuth2TokenService::Consumer overrides.
+  virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
+                                 const std::string& token,
+                                 const base::Time& expiration_date) OVERRIDE;
+  virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request,
+                                 const GoogleServiceAuthError& error) OVERRIDE;
+
+  std::string last_token_;
+  int number_of_successful_tokens_;
+  GoogleServiceAuthError last_error_;
+  int number_of_errors_;
+};
+
+#endif  // GOOGLE_APIS_GAIA_OAUTH2_TOKEN_SERVICE_TEST_UTIL_H_
diff --git a/google_apis/gaia/oauth2_token_service_unittest.cc b/google_apis/gaia/oauth2_token_service_unittest.cc
new file mode 100644
index 0000000..9937268
--- /dev/null
+++ b/google_apis/gaia/oauth2_token_service_unittest.cc
@@ -0,0 +1,522 @@
+// 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 <string>
+
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "google_apis/gaia/gaia_constants.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_access_token_consumer.h"
+#include "google_apis/gaia/oauth2_token_service.h"
+#include "google_apis/gaia/oauth2_token_service_test_util.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// A testing consumer that retries on error.
+class RetryingTestingOAuth2TokenServiceConsumer
+    : public TestingOAuth2TokenServiceConsumer {
+ public:
+  RetryingTestingOAuth2TokenServiceConsumer(
+      OAuth2TokenService* oauth2_service)
+      : oauth2_service_(oauth2_service) {}
+  virtual ~RetryingTestingOAuth2TokenServiceConsumer() {}
+
+  virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request,
+                                 const GoogleServiceAuthError& error) OVERRIDE {
+    TestingOAuth2TokenServiceConsumer::OnGetTokenFailure(request, error);
+    request_.reset(oauth2_service_->StartRequest(
+        std::set<std::string>(), this).release());
+  }
+
+  OAuth2TokenService* oauth2_service_;
+  scoped_ptr<OAuth2TokenService::Request> request_;
+};
+
+class TestOAuth2TokenService : public OAuth2TokenService {
+ public:
+  explicit TestOAuth2TokenService(net::TestURLRequestContextGetter* getter)
+      : request_context_getter_(getter) {
+  }
+
+  void CancelAllRequestsForTest() { CancelAllRequests(); }
+
+  void CancelRequestsForTokenForTest(const std::string& refresh_token) {
+    CancelRequestsForToken(refresh_token);
+  }
+
+  // For testing: set the refresh token to be used.
+  void set_refresh_token(const std::string& refresh_token) {
+    refresh_token_ = refresh_token;
+  }
+
+ protected:
+  virtual std::string GetRefreshToken() OVERRIDE { return refresh_token_; }
+
+ private:
+  // OAuth2TokenService implementation.
+  virtual net::URLRequestContextGetter* GetRequestContext() OVERRIDE {
+    return request_context_getter_.get();
+  }
+
+  std::string refresh_token_;
+  scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
+};
+
+class OAuth2TokenServiceTest : public testing::Test {
+ public:
+  virtual void SetUp() OVERRIDE {
+    oauth2_service_.reset(
+        new TestOAuth2TokenService(new net::TestURLRequestContextGetter(
+            message_loop_.message_loop_proxy())));
+  }
+
+  virtual void TearDown() OVERRIDE {
+    // Makes sure that all the clean up tasks are run.
+    base::RunLoop().RunUntilIdle();
+  }
+
+ protected:
+  base::MessageLoopForIO message_loop_;  // net:: stuff needs IO message loop.
+  net::TestURLFetcherFactory factory_;
+  scoped_ptr<TestOAuth2TokenService> oauth2_service_;
+  TestingOAuth2TokenServiceConsumer consumer_;
+};
+
+TEST_F(OAuth2TokenServiceTest, NoOAuth2RefreshToken) {
+  scoped_ptr<OAuth2TokenService::Request> request(
+      oauth2_service_->StartRequest(std::set<std::string>(), &consumer_));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(1, consumer_.number_of_errors_);
+}
+
+TEST_F(OAuth2TokenServiceTest, FailureShouldNotRetry) {
+  oauth2_service_->set_refresh_token("refreshToken");
+  scoped_ptr<OAuth2TokenService::Request> request(
+      oauth2_service_->StartRequest(std::set<std::string>(), &consumer_));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+  EXPECT_TRUE(fetcher);
+  fetcher->set_response_code(net::HTTP_UNAUTHORIZED);
+  fetcher->SetResponseString(std::string());
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(1, consumer_.number_of_errors_);
+  EXPECT_EQ(fetcher, factory_.GetFetcherByID(0));
+}
+
+TEST_F(OAuth2TokenServiceTest, SuccessWithoutCaching) {
+  oauth2_service_->set_refresh_token("refreshToken");
+  scoped_ptr<OAuth2TokenService::Request> request(
+      oauth2_service_->StartRequest(std::set<std::string>(), &consumer_));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+  EXPECT_TRUE(fetcher);
+  fetcher->set_response_code(net::HTTP_OK);
+  fetcher->SetResponseString(GetValidTokenResponse("token", 3600));
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  EXPECT_EQ("token", consumer_.last_token_);
+}
+
+TEST_F(OAuth2TokenServiceTest, SuccessWithCaching) {
+  std::set<std::string> scopes1;
+  scopes1.insert("s1");
+  scopes1.insert("s2");
+  std::set<std::string> scopes1_same;
+  scopes1_same.insert("s2");
+  scopes1_same.insert("s1");
+  std::set<std::string> scopes2;
+  scopes2.insert("s3");
+
+  oauth2_service_->set_refresh_token("refreshToken");
+
+  // First request.
+  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
+      scopes1, &consumer_));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+  EXPECT_TRUE(fetcher);
+  fetcher->set_response_code(net::HTTP_OK);
+  fetcher->SetResponseString(GetValidTokenResponse("token", 3600));
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  EXPECT_EQ("token", consumer_.last_token_);
+
+  // Second request to the same set of scopes, should return the same token
+  // without needing a network request.
+  scoped_ptr<OAuth2TokenService::Request> request2(
+      oauth2_service_->StartRequest(scopes1_same, &consumer_));
+  base::RunLoop().RunUntilIdle();
+
+  // No new network fetcher.
+  EXPECT_EQ(fetcher, factory_.GetFetcherByID(0));
+  EXPECT_EQ(2, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  EXPECT_EQ("token", consumer_.last_token_);
+
+  // Third request to a new set of scopes, should return another token.
+  scoped_ptr<OAuth2TokenService::Request> request3(
+      oauth2_service_->StartRequest(scopes2, &consumer_));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(2, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  fetcher = factory_.GetFetcherByID(0);
+  EXPECT_TRUE(fetcher);
+  fetcher->set_response_code(net::HTTP_OK);
+  fetcher->SetResponseString(GetValidTokenResponse("token2", 3600));
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_EQ(3, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  EXPECT_EQ("token2", consumer_.last_token_);
+}
+
+TEST_F(OAuth2TokenServiceTest, SuccessAndExpirationAndFailure) {
+  oauth2_service_->set_refresh_token("refreshToken");
+
+  // First request.
+  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
+      std::set<std::string>(), &consumer_));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+  EXPECT_TRUE(fetcher);
+  fetcher->set_response_code(net::HTTP_OK);
+  fetcher->SetResponseString(GetValidTokenResponse("token", 0));
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  EXPECT_EQ("token", consumer_.last_token_);
+
+  // Second request must try to access the network as the token has expired.
+  scoped_ptr<OAuth2TokenService::Request> request2(
+      oauth2_service_->StartRequest(std::set<std::string>(), &consumer_));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+
+  // Network failure.
+  fetcher = factory_.GetFetcherByID(0);
+  EXPECT_TRUE(fetcher);
+  fetcher->set_response_code(net::HTTP_UNAUTHORIZED);
+  fetcher->SetResponseString(std::string());
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(1, consumer_.number_of_errors_);
+}
+
+TEST_F(OAuth2TokenServiceTest, SuccessAndExpirationAndSuccess) {
+  oauth2_service_->set_refresh_token("refreshToken");
+
+  // First request.
+  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
+      std::set<std::string>(), &consumer_));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+  EXPECT_TRUE(fetcher);
+  fetcher->set_response_code(net::HTTP_OK);
+  fetcher->SetResponseString(GetValidTokenResponse("token", 0));
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  EXPECT_EQ("token", consumer_.last_token_);
+
+  // Second request must try to access the network as the token has expired.
+  scoped_ptr<OAuth2TokenService::Request> request2(
+      oauth2_service_->StartRequest(std::set<std::string>(), &consumer_));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+
+  fetcher = factory_.GetFetcherByID(0);
+  EXPECT_TRUE(fetcher);
+  fetcher->set_response_code(net::HTTP_OK);
+  fetcher->SetResponseString(GetValidTokenResponse("another token", 0));
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_EQ(2, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  EXPECT_EQ("another token", consumer_.last_token_);
+}
+
+TEST_F(OAuth2TokenServiceTest, RequestDeletedBeforeCompletion) {
+  oauth2_service_->set_refresh_token("refreshToken");
+
+  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
+      std::set<std::string>(), &consumer_));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+  EXPECT_TRUE(fetcher);
+
+  request.reset();
+
+  fetcher->set_response_code(net::HTTP_OK);
+  fetcher->SetResponseString(GetValidTokenResponse("token", 3600));
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+}
+
+TEST_F(OAuth2TokenServiceTest, RequestDeletedAfterCompletion) {
+  oauth2_service_->set_refresh_token("refreshToken");
+
+  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
+      std::set<std::string>(), &consumer_));
+  base::RunLoop().RunUntilIdle();
+  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+  fetcher->set_response_code(net::HTTP_OK);
+  fetcher->SetResponseString(GetValidTokenResponse("token", 3600));
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  EXPECT_EQ("token", consumer_.last_token_);
+
+  request.reset();
+
+  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  EXPECT_EQ("token", consumer_.last_token_);
+}
+
+TEST_F(OAuth2TokenServiceTest, MultipleRequestsForTheSameScopesWithOneDeleted) {
+  oauth2_service_->set_refresh_token("refreshToken");
+
+  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
+      std::set<std::string>(), &consumer_));
+  base::RunLoop().RunUntilIdle();
+  scoped_ptr<OAuth2TokenService::Request> request2(
+      oauth2_service_->StartRequest(std::set<std::string>(), &consumer_));
+  base::RunLoop().RunUntilIdle();
+
+  request.reset();
+
+  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+  fetcher->set_response_code(net::HTTP_OK);
+  fetcher->SetResponseString(GetValidTokenResponse("token", 3600));
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+}
+
+TEST_F(OAuth2TokenServiceTest, ClearedRefreshTokenFailsSubsequentRequests) {
+  // We have a valid refresh token; the first request is successful.
+  oauth2_service_->set_refresh_token("refreshToken");
+  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
+      std::set<std::string>(), &consumer_));
+  base::RunLoop().RunUntilIdle();
+  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+  fetcher->set_response_code(net::HTTP_OK);
+  fetcher->SetResponseString(GetValidTokenResponse("token", 3600));
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  EXPECT_EQ("token", consumer_.last_token_);
+
+  // The refresh token is no longer available; subsequent requests fail.
+  oauth2_service_->set_refresh_token("");
+  request = oauth2_service_->StartRequest(std::set<std::string>(), &consumer_);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(fetcher, factory_.GetFetcherByID(0));
+  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(1, consumer_.number_of_errors_);
+}
+
+TEST_F(OAuth2TokenServiceTest,
+       ChangedRefreshTokenDoesNotAffectInFlightRequests) {
+  oauth2_service_->set_refresh_token("first refreshToken");
+  std::set<std::string> scopes;
+  scopes.insert("s1");
+  scopes.insert("s2");
+
+  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
+      scopes, &consumer_));
+  base::RunLoop().RunUntilIdle();
+  net::TestURLFetcher* fetcher1 = factory_.GetFetcherByID(0);
+
+  // Note |request| is still pending when the refresh token changes.
+  oauth2_service_->set_refresh_token("second refreshToken");
+
+  // A 2nd request (using the new refresh token) that occurs and completes
+  // while the 1st request is in flight is successful.
+  TestingOAuth2TokenServiceConsumer consumer2;
+  scoped_ptr<OAuth2TokenService::Request> request2(
+      oauth2_service_->StartRequest(scopes, &consumer2));
+  base::RunLoop().RunUntilIdle();
+
+  net::TestURLFetcher* fetcher2 = factory_.GetFetcherByID(0);
+  fetcher2->set_response_code(net::HTTP_OK);
+  fetcher2->SetResponseString(GetValidTokenResponse("second token", 3600));
+  fetcher2->delegate()->OnURLFetchComplete(fetcher2);
+  EXPECT_EQ(1, consumer2.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer2.number_of_errors_);
+  EXPECT_EQ("second token", consumer2.last_token_);
+
+  fetcher1->set_response_code(net::HTTP_OK);
+  fetcher1->SetResponseString(GetValidTokenResponse("first token", 3600));
+  fetcher1->delegate()->OnURLFetchComplete(fetcher1);
+  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  EXPECT_EQ("first token", consumer_.last_token_);
+}
+
+TEST_F(OAuth2TokenServiceTest, ServiceShutDownBeforeFetchComplete) {
+  oauth2_service_->set_refresh_token("refreshToken");
+  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
+      std::set<std::string>(), &consumer_));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+
+  // The destructor should cancel all in-flight fetchers.
+  oauth2_service_.reset(NULL);
+
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(1, consumer_.number_of_errors_);
+}
+
+TEST_F(OAuth2TokenServiceTest, RetryingConsumer) {
+  oauth2_service_->set_refresh_token("refreshToken");
+  RetryingTestingOAuth2TokenServiceConsumer consumer(oauth2_service_.get());
+  scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest(
+      std::set<std::string>(), &consumer));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, consumer.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer.number_of_errors_);
+
+  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+  ASSERT_TRUE(fetcher);
+  fetcher->set_response_code(net::HTTP_UNAUTHORIZED);
+  fetcher->SetResponseString(std::string());
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_EQ(0, consumer.number_of_successful_tokens_);
+  EXPECT_EQ(1, consumer.number_of_errors_);
+
+  fetcher = factory_.GetFetcherByID(0);
+  ASSERT_TRUE(fetcher);
+  fetcher->set_response_code(net::HTTP_UNAUTHORIZED);
+  fetcher->SetResponseString(std::string());
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_EQ(0, consumer.number_of_successful_tokens_);
+  EXPECT_EQ(2, consumer.number_of_errors_);
+}
+
+TEST_F(OAuth2TokenServiceTest, InvalidateToken) {
+  std::set<std::string> scopes;
+  oauth2_service_->set_refresh_token("refreshToken");
+
+  // First request.
+  scoped_ptr<OAuth2TokenService::Request> request(
+      oauth2_service_->StartRequest(scopes, &consumer_));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+  EXPECT_TRUE(fetcher);
+  fetcher->set_response_code(net::HTTP_OK);
+  fetcher->SetResponseString(GetValidTokenResponse("token", 3600));
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_EQ(1, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  EXPECT_EQ("token", consumer_.last_token_);
+
+  // Second request, should return the same token without needing a network
+  // request.
+  scoped_ptr<OAuth2TokenService::Request> request2(
+      oauth2_service_->StartRequest(scopes, &consumer_));
+  base::RunLoop().RunUntilIdle();
+
+  // No new network fetcher.
+  EXPECT_EQ(fetcher, factory_.GetFetcherByID(0));
+  EXPECT_EQ(2, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  EXPECT_EQ("token", consumer_.last_token_);
+
+  // Invalidating the token should return a new token on the next request.
+  oauth2_service_->InvalidateToken(scopes, consumer_.last_token_);
+  scoped_ptr<OAuth2TokenService::Request> request3(
+      oauth2_service_->StartRequest(scopes, &consumer_));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(2, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  fetcher = factory_.GetFetcherByID(0);
+  EXPECT_TRUE(fetcher);
+  fetcher->set_response_code(net::HTTP_OK);
+  fetcher->SetResponseString(GetValidTokenResponse("token2", 3600));
+  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  EXPECT_EQ(3, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+  EXPECT_EQ("token2", consumer_.last_token_);
+}
+
+TEST_F(OAuth2TokenServiceTest, CancelAllRequests) {
+  oauth2_service_->set_refresh_token("refreshToken");
+  scoped_ptr<OAuth2TokenService::Request> request(
+      oauth2_service_->StartRequest(std::set<std::string>(), &consumer_));
+
+  oauth2_service_->set_refresh_token("refreshToken2");
+  scoped_ptr<OAuth2TokenService::Request> request2(
+      oauth2_service_->StartRequest(std::set<std::string>(), &consumer_));
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+
+  oauth2_service_->CancelAllRequestsForTest();
+
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(2, consumer_.number_of_errors_);
+}
+
+TEST_F(OAuth2TokenServiceTest, CancelRequestsForToken) {
+  std::set<std::string> scope_set_1;
+  scope_set_1.insert("scope1");
+  scope_set_1.insert("scope2");
+  std::set<std::string> scope_set_2(scope_set_1.begin(), scope_set_1.end());
+  scope_set_2.insert("scope3");
+
+  oauth2_service_->set_refresh_token("refreshToken");
+  scoped_ptr<OAuth2TokenService::Request> request1(
+      oauth2_service_->StartRequest(scope_set_1, &consumer_));
+  scoped_ptr<OAuth2TokenService::Request> request2(
+      oauth2_service_->StartRequest(scope_set_2, &consumer_));
+
+  oauth2_service_->set_refresh_token("refreshToken2");
+  scoped_ptr<OAuth2TokenService::Request> request3(
+      oauth2_service_->StartRequest(scope_set_1, &consumer_));
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(0, consumer_.number_of_errors_);
+
+  oauth2_service_->CancelRequestsForTokenForTest("refreshToken");
+
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(2, consumer_.number_of_errors_);
+
+  oauth2_service_->CancelRequestsForTokenForTest("refreshToken2");
+
+  EXPECT_EQ(0, consumer_.number_of_successful_tokens_);
+  EXPECT_EQ(3, consumer_.number_of_errors_);
+}
diff --git a/google_apis/google_apis.gyp b/google_apis/google_apis.gyp
index c67232f..ef492ce 100644
--- a/google_apis/google_apis.gyp
+++ b/google_apis/google_apis.gyp
@@ -83,6 +83,8 @@
         'gaia/oauth2_api_call_flow.h',
         'gaia/oauth2_mint_token_flow.cc',
         'gaia/oauth2_mint_token_flow.h',
+        'gaia/oauth2_token_service.cc',
+        'gaia/oauth2_token_service.h',
         'google_api_keys.cc',
         'google_api_keys.h',
       ],
diff --git a/google_apis/google_apis.target.darwin-arm.mk b/google_apis/google_apis.target.darwin-arm.mk
index a63fafe..611757a 100644
--- a/google_apis/google_apis.target.darwin-arm.mk
+++ b/google_apis/google_apis.target.darwin-arm.mk
@@ -37,6 +37,7 @@
 	google_apis/gaia/oauth2_access_token_fetcher.cc \
 	google_apis/gaia/oauth2_api_call_flow.cc \
 	google_apis/gaia/oauth2_mint_token_flow.cc \
+	google_apis/gaia/oauth2_token_service.cc \
 	google_apis/google_api_keys.cc
 
 
diff --git a/google_apis/google_apis.target.darwin-mips.mk b/google_apis/google_apis.target.darwin-mips.mk
index 0ee2c1a..16e2eb4 100644
--- a/google_apis/google_apis.target.darwin-mips.mk
+++ b/google_apis/google_apis.target.darwin-mips.mk
@@ -37,6 +37,7 @@
 	google_apis/gaia/oauth2_access_token_fetcher.cc \
 	google_apis/gaia/oauth2_api_call_flow.cc \
 	google_apis/gaia/oauth2_mint_token_flow.cc \
+	google_apis/gaia/oauth2_token_service.cc \
 	google_apis/google_api_keys.cc
 
 
diff --git a/google_apis/google_apis.target.darwin-x86.mk b/google_apis/google_apis.target.darwin-x86.mk
index 31f388d..839f0c1 100644
--- a/google_apis/google_apis.target.darwin-x86.mk
+++ b/google_apis/google_apis.target.darwin-x86.mk
@@ -37,6 +37,7 @@
 	google_apis/gaia/oauth2_access_token_fetcher.cc \
 	google_apis/gaia/oauth2_api_call_flow.cc \
 	google_apis/gaia/oauth2_mint_token_flow.cc \
+	google_apis/gaia/oauth2_token_service.cc \
 	google_apis/google_api_keys.cc
 
 
diff --git a/google_apis/google_apis.target.linux-arm.mk b/google_apis/google_apis.target.linux-arm.mk
index a63fafe..611757a 100644
--- a/google_apis/google_apis.target.linux-arm.mk
+++ b/google_apis/google_apis.target.linux-arm.mk
@@ -37,6 +37,7 @@
 	google_apis/gaia/oauth2_access_token_fetcher.cc \
 	google_apis/gaia/oauth2_api_call_flow.cc \
 	google_apis/gaia/oauth2_mint_token_flow.cc \
+	google_apis/gaia/oauth2_token_service.cc \
 	google_apis/google_api_keys.cc
 
 
diff --git a/google_apis/google_apis.target.linux-mips.mk b/google_apis/google_apis.target.linux-mips.mk
index 0ee2c1a..16e2eb4 100644
--- a/google_apis/google_apis.target.linux-mips.mk
+++ b/google_apis/google_apis.target.linux-mips.mk
@@ -37,6 +37,7 @@
 	google_apis/gaia/oauth2_access_token_fetcher.cc \
 	google_apis/gaia/oauth2_api_call_flow.cc \
 	google_apis/gaia/oauth2_mint_token_flow.cc \
+	google_apis/gaia/oauth2_token_service.cc \
 	google_apis/google_api_keys.cc
 
 
diff --git a/google_apis/google_apis.target.linux-x86.mk b/google_apis/google_apis.target.linux-x86.mk
index 31f388d..839f0c1 100644
--- a/google_apis/google_apis.target.linux-x86.mk
+++ b/google_apis/google_apis.target.linux-x86.mk
@@ -37,6 +37,7 @@
 	google_apis/gaia/oauth2_access_token_fetcher.cc \
 	google_apis/gaia/oauth2_api_call_flow.cc \
 	google_apis/gaia/oauth2_mint_token_flow.cc \
+	google_apis/gaia/oauth2_token_service.cc \
 	google_apis/google_api_keys.cc
 
 
diff --git a/gpu/GLES2/gl2extchromium.h b/gpu/GLES2/gl2extchromium.h
index 433242c..bd4d432 100644
--- a/gpu/GLES2/gl2extchromium.h
+++ b/gpu/GLES2/gl2extchromium.h
@@ -336,11 +336,11 @@
 #ifndef GL_CHROMIUM_async_pixel_transfers
 #define GL_CHROMIUM_async_pixel_transfers 1
 
-#ifndef GL_ASYNC_PIXEL_TRANSFERS_COMPLETED_CHROMIUM
-#define GL_ASYNC_PIXEL_TRANSFERS_COMPLETED_CHROMIUM 0x84F5
+#ifndef GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM
+#define GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM 0x84F5
 #endif
-#ifndef GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM
-#define GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM 0x84F6
+#ifndef GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM
+#define GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM 0x84F6
 #endif
 #endif  /* GL_CHROMIUM_async_pixel_transfers */
 
diff --git a/gpu/command_buffer/OWNERS b/gpu/command_buffer/OWNERS
new file mode 100644
index 0000000..7ef33ed
--- /dev/null
+++ b/gpu/command_buffer/OWNERS
@@ -0,0 +1,5 @@
+piman@chromium.org
+apatrick@chromium.org
+jbauman@chromium.org
+bajones@chromium.org
+zmo@chromium.org
\ No newline at end of file
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py
index a6f13c1..f4c7e5b 100755
--- a/gpu/command_buffer/build_gles2_cmd_buffer.py
+++ b/gpu/command_buffer/build_gles2_cmd_buffer.py
@@ -827,8 +827,8 @@
       'GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT',
       'GL_COMMANDS_ISSUED_CHROMIUM',
       'GL_LATENCY_QUERY_CHROMIUM',
-      'GL_ASYNC_PIXEL_TRANSFERS_COMPLETED_CHROMIUM',
-      'GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM',
+      'GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM',
+      'GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM',
     ],
   },
   'RenderBufferParameter': {
diff --git a/gpu/command_buffer/client/gl_in_process_context.cc b/gpu/command_buffer/client/gl_in_process_context.cc
index 984214d..a8e33b6 100644
--- a/gpu/command_buffer/client/gl_in_process_context.cc
+++ b/gpu/command_buffer/client/gl_in_process_context.cc
@@ -31,7 +31,7 @@
 #include "ui/gl/gl_image.h"
 
 #if defined(OS_ANDROID)
-#include "ui/gl/android/surface_texture_bridge.h"
+#include "ui/gl/android/surface_texture.h"
 #endif
 
 namespace gpu {
@@ -70,7 +70,7 @@
   virtual gles2::GLES2Implementation* GetImplementation() OVERRIDE;
 
 #if defined(OS_ANDROID)
-  virtual scoped_refptr<gfx::SurfaceTextureBridge> GetSurfaceTexture(
+  virtual scoped_refptr<gfx::SurfaceTexture> GetSurfaceTexture(
       uint32 stream_id) OVERRIDE;
 #endif
 
@@ -343,7 +343,7 @@
 }
 
 #if defined(OS_ANDROID)
-scoped_refptr<gfx::SurfaceTextureBridge>
+scoped_refptr<gfx::SurfaceTexture>
 GLInProcessContextImpl::GetSurfaceTexture(uint32 stream_id) {
   return command_buffer_->GetSurfaceTexture(stream_id);
 }
diff --git a/gpu/command_buffer/client/gl_in_process_context.h b/gpu/command_buffer/client/gl_in_process_context.h
index ec7bdc5..3fe82aa 100644
--- a/gpu/command_buffer/client/gl_in_process_context.h
+++ b/gpu/command_buffer/client/gl_in_process_context.h
@@ -18,7 +18,7 @@
 
 #if defined(OS_ANDROID)
 namespace gfx {
-class SurfaceTextureBridge;
+class SurfaceTexture;
 }
 #endif
 
@@ -81,7 +81,7 @@
   virtual gles2::GLES2Implementation* GetImplementation() = 0;
 
 #if defined(OS_ANDROID)
-  virtual scoped_refptr<gfx::SurfaceTextureBridge> GetSurfaceTexture(
+  virtual scoped_refptr<gfx::SurfaceTexture> GetSurfaceTexture(
       uint32 stream_id) = 0;
 #endif
 };
diff --git a/gpu/command_buffer/client/query_tracker.cc b/gpu/command_buffer/client/query_tracker.cc
index 35732bf..307b615 100644
--- a/gpu/command_buffer/client/query_tracker.cc
+++ b/gpu/command_buffer/client/query_tracker.cc
@@ -112,8 +112,8 @@
       // tell service about id, shared memory and count
       gl->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset());
       break;
-    case GL_ASYNC_PIXEL_TRANSFERS_COMPLETED_CHROMIUM:
-    case GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM:
+    case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM:
+    case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM:
     default:
       // tell service about id, shared memory and count
       gl->helper()->BeginQueryEXT(target(), id(), shm_id(), shm_offset());
@@ -162,8 +162,8 @@
           result_ = std::min(info_.sync->result - client_begin_time_us_,
                              static_cast<uint64>(0xFFFFFFFFL));
           break;
-        case GL_ASYNC_PIXEL_TRANSFERS_COMPLETED_CHROMIUM:
-        case GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM:
+        case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM:
+        case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM:
         default:
           result_ = info_.sync->result;
           break;
diff --git a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
index 573aee9..aa24ccd 100644
--- a/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
+++ b/gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
@@ -225,10 +225,10 @@
   { 0x90F1, "GL_MULTIVIEW_EXT", },
   { 0x90F0, "GL_COLOR_ATTACHMENT_EXT", },
   { 0x803C, "GL_ALPHA8_OES", },
-  { 0x84F5, "GL_ASYNC_PIXEL_TRANSFERS_COMPLETED_CHROMIUM", },
+  { 0x84F5, "GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM", },
   { 0x882A, "GL_DRAW_BUFFER5_NV", },
   { 0x80AA, "GL_SAMPLE_COVERAGE_VALUE", },
-  { 0x84F6, "GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM", },
+  { 0x84F6, "GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM", },
   { 0x80AB, "GL_SAMPLE_COVERAGE_INVERT", },
   { 0x8FC4, "GL_SHADER_BINARY_VIV", },
   { 0x882B, "GL_DRAW_BUFFER6_NV", },
@@ -1078,10 +1078,10 @@
     "GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT" },
     { GL_COMMANDS_ISSUED_CHROMIUM, "GL_COMMANDS_ISSUED_CHROMIUM" },
     { GL_LATENCY_QUERY_CHROMIUM, "GL_LATENCY_QUERY_CHROMIUM" },
-    { GL_ASYNC_PIXEL_TRANSFERS_COMPLETED_CHROMIUM,
-    "GL_ASYNC_PIXEL_TRANSFERS_COMPLETED_CHROMIUM" },
-    { GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM,
-    "GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM" },
+    { GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM,
+    "GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM" },
+    { GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM,
+    "GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM" },
   };
   return GLES2Util::GetQualifiedEnumString(
       string_table, arraysize(string_table), value);
diff --git a/gpu/command_buffer/service/buffer_manager.cc b/gpu/command_buffer/service/buffer_manager.cc
index f9cec7a..7b1c90d 100644
--- a/gpu/command_buffer/service/buffer_manager.cc
+++ b/gpu/command_buffer/service/buffer_manager.cc
@@ -74,13 +74,13 @@
 
 Buffer::Buffer(BufferManager* manager, GLuint service_id)
     : manager_(manager),
+      size_(0),
       deleted_(false),
+      shadowed_(false),
+      is_client_side_array_(false),
       service_id_(service_id),
       target_(0),
-      size_(0),
-      usage_(GL_STATIC_DRAW),
-      shadowed_(false),
-      is_client_side_array_(false) {
+      usage_(GL_STATIC_DRAW) {
   manager_->StartTracking(this);
 }
 
diff --git a/gpu/command_buffer/service/buffer_manager.h b/gpu/command_buffer/service/buffer_manager.h
index 8ddf334..cc23f01 100644
--- a/gpu/command_buffer/service/buffer_manager.h
+++ b/gpu/command_buffer/service/buffer_manager.h
@@ -135,9 +135,23 @@
   // The manager that owns this Buffer.
   BufferManager* manager_;
 
+  // A copy of the data in the buffer. This data is only kept if the target
+  // is backed_ = true.
+  scoped_ptr<int8[]> shadow_;
+
+  // Size of buffer.
+  GLsizeiptr size_;
+
   // True if deleted.
   bool deleted_;
 
+  // Whether or not the data is shadowed.
+  bool shadowed_;
+
+  // Whether or not this Buffer is not uploaded to the GPU but just
+  // sitting in local memory.
+  bool is_client_side_array_;
+
   // Service side buffer id.
   GLuint service_id_;
 
@@ -146,23 +160,9 @@
   // Once set a buffer can not be used for something else.
   GLenum target_;
 
-  // Size of buffer.
-  GLsizeiptr size_;
-
   // Usage of buffer.
   GLenum usage_;
 
-  // Whether or not the data is shadowed.
-  bool shadowed_;
-
-  // Whether or not this Buffer is not uploaded to the GPU but just
-  // sitting in local memory.
-  bool is_client_side_array_;
-
-  // A copy of the data in the buffer. This data is only kept if the target
-  // is backed_ = true.
-  scoped_ptr<int8[]> shadow_;
-
   // A map of ranges to the highest value in that range of a certain type.
   typedef std::map<Range, GLuint, Range::Less> RangeToMaxValueMap;
   RangeToMaxValueMap range_set_;
diff --git a/gpu/command_buffer/service/disk_cache_proto.proto b/gpu/command_buffer/service/disk_cache_proto.proto
index 2c504aa..5a55943 100644
--- a/gpu/command_buffer/service/disk_cache_proto.proto
+++ b/gpu/command_buffer/service/disk_cache_proto.proto
@@ -6,6 +6,7 @@
   optional string name = 3;
   optional string key = 4;
   optional int32 precision = 5;
+  optional int32 static_use = 6;  
 }
 
 message ShaderProto {
diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc
index b0fb120..453b43d 100644
--- a/gpu/command_buffer/service/feature_info.cc
+++ b/gpu/command_buffer/service/feature_info.cc
@@ -120,7 +120,8 @@
       enable_samplers(false),
       ext_draw_buffers(false),
       ext_frag_depth(false),
-      use_async_readpixels(false) {
+      use_async_readpixels(false),
+      map_buffer_range(false) {
 }
 
 FeatureInfo::Workarounds::Workarounds() :
@@ -605,9 +606,10 @@
         !have_arb_occlusion_query2;
   }
 
-  if (extensions.Contains("GL_ANGLE_instanced_arrays") ||
-      (extensions.Contains("GL_ARB_instanced_arrays") &&
-       extensions.Contains("GL_ARB_draw_instanced"))) {
+  if (!workarounds_.disable_angle_instanced_arrays &&
+      (extensions.Contains("GL_ANGLE_instanced_arrays") ||
+       (extensions.Contains("GL_ARB_instanced_arrays") &&
+        extensions.Contains("GL_ARB_draw_instanced")))) {
     AddExtensionString("GL_ANGLE_instanced_arrays");
     feature_flags_.angle_instanced_arrays = true;
     validators_.vertex_attribute.AddValue(GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE);
@@ -643,16 +645,6 @@
     feature_flags_.ext_frag_depth = true;
   }
 
-  bool ui_gl_fence_works =
-      extensions.Contains("GL_NV_fence") ||
-      extensions.Contains("GL_ARB_sync");
-
-  if (ui_gl_fence_works &&
-      extensions.Contains("GL_ARB_pixel_buffer_object") &&
-      !workarounds_.disable_async_readpixels) {
-    feature_flags_.use_async_readpixels = true;
-  }
-
   if (!disallowed_features_.swap_buffer_complete_callback)
     AddExtensionString("GL_CHROMIUM_swapbuffers_complete_callback");
 
@@ -663,6 +655,24 @@
     is_es3 = (lstr.substr(0, 12) == "opengl es 3.");
   }
 
+  bool ui_gl_fence_works = extensions.Contains("GL_NV_fence") ||
+                           extensions.Contains("GL_ARB_sync") ||
+                           extensions.Contains("EGL_KHR_fence_sync");
+
+  feature_flags_.map_buffer_range =
+      is_es3 || extensions.Contains("GL_ARB_map_buffer_range");
+
+  // Really it's part of core OpenGL 2.1 and up, but let's assume the
+  // extension is still advertised.
+  bool has_pixel_buffers =
+      is_es3 || extensions.Contains("GL_ARB_pixel_buffer_object");
+
+  // We will use either glMapBuffer() or glMapBufferRange() for async readbacks.
+  if (has_pixel_buffers && ui_gl_fence_works &&
+      !workarounds_.disable_async_readpixels) {
+    feature_flags_.use_async_readpixels = true;
+  }
+
   if (is_es3 || extensions.Contains("GL_ARB_sampler_objects")) {
     feature_flags_.enable_samplers = true;
     // TODO(dsinclair): Add AddExtensionString("GL_CHROMIUM_sampler_objects")
diff --git a/gpu/command_buffer/service/feature_info.h b/gpu/command_buffer/service/feature_info.h
index a9ccf64..8b33bb1 100644
--- a/gpu/command_buffer/service/feature_info.h
+++ b/gpu/command_buffer/service/feature_info.h
@@ -49,6 +49,7 @@
     bool ext_draw_buffers;
     bool ext_frag_depth;
     bool use_async_readpixels;
+    bool map_buffer_range;
   };
 
   struct Workarounds {
diff --git a/gpu/command_buffer/service/feature_info_unittest.cc b/gpu/command_buffer/service/feature_info_unittest.cc
index 0dc1685..1d1cba4 100644
--- a/gpu/command_buffer/service/feature_info_unittest.cc
+++ b/gpu/command_buffer/service/feature_info_unittest.cc
@@ -96,6 +96,8 @@
   EXPECT_FALSE(info_->feature_flags(
       ).use_arb_occlusion_query_for_occlusion_query_boolean);
   EXPECT_FALSE(info_->feature_flags().native_vertex_array_object);
+  EXPECT_FALSE(info_->feature_flags().map_buffer_range);
+  EXPECT_FALSE(info_->feature_flags().use_async_readpixels);
 
 #define GPU_OP(type, name) EXPECT_FALSE(info_->workarounds().name);
   GPU_DRIVER_BUG_WORKAROUNDS(GPU_OP)
@@ -864,10 +866,12 @@
   EXPECT_TRUE(info_->feature_flags().enable_samplers);
 }
 
-TEST_F(FeatureInfoTest, InitializeSamplersWithES3) {
+TEST_F(FeatureInfoTest, InitializeWithES3) {
   SetupInitExpectationsWithGLVersion("", "OpenGL ES 3.0");
   info_->Initialize(NULL);
   EXPECT_TRUE(info_->feature_flags().enable_samplers);
+  EXPECT_TRUE(info_->feature_flags().map_buffer_range);
+  EXPECT_FALSE(info_->feature_flags().use_async_readpixels);
 }
 
 TEST_F(FeatureInfoTest, InitializeWithoutSamplers) {
@@ -876,6 +880,12 @@
   EXPECT_FALSE(info_->feature_flags().enable_samplers);
 }
 
+TEST_F(FeatureInfoTest, InitializeWithES3AndFences) {
+  SetupInitExpectationsWithGLVersion("EGL_KHR_fence_sync", "OpenGL ES 3.0");
+  info_->Initialize(NULL);
+  EXPECT_TRUE(info_->feature_flags().use_async_readpixels);
+}
+
 TEST_F(FeatureInfoTest, ParseDriverBugWorkaroundsSingle) {
   SetupInitExpectations("");
   CommandLine command_line(0, NULL);
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 77a6b8a..be644ce 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -68,6 +68,10 @@
 #include "ui/gl/io_surface_support_mac.h"
 #endif
 
+#if defined(OS_WIN)
+#include "base/win/win_util.h"
+#endif
+
 // TODO(zmo): we can't include "City.h" due to type def conflicts.
 extern uint64 CityHash64(const char*, size_t);
 
@@ -2720,6 +2724,9 @@
     if (workarounds().exit_on_context_lost) {
       LOG(ERROR) << "Exiting GPU process because some drivers cannot reset"
                  << " a D3D device in the Chrome GPU process sandbox.";
+#if defined(OS_WIN)
+      base::win::SetShouldCrashOnProcessDetach(false);
+#endif
       exit(0);
     }
 
@@ -6716,7 +6723,13 @@
 
   if (buffer != 0) {
     glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, buffer);
-    void* data = glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY);
+    void* data;
+    if (features().map_buffer_range) {
+      data = glMapBufferRange(
+          GL_PIXEL_PACK_BUFFER_ARB, 0, pixels_size, GL_MAP_READ_BIT);
+    } else {
+      data = glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY);
+    }
     memcpy(pixels, data, pixels_size);
     // GL_PIXEL_PACK_BUFFER_ARB is currently unused, so we don't
     // have to restore the state.
@@ -9010,8 +9023,8 @@
   switch (target) {
     case GL_COMMANDS_ISSUED_CHROMIUM:
     case GL_LATENCY_QUERY_CHROMIUM:
-    case GL_ASYNC_PIXEL_TRANSFERS_COMPLETED_CHROMIUM:
-    case GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM:
+    case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM:
+    case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM:
     case GL_GET_ERROR_QUERY_CHROMIUM:
       break;
     default:
@@ -9479,7 +9492,6 @@
   }
 
   if (source_texture->target() == GL_TEXTURE_EXTERNAL_OES) {
-    UpdateStreamTextureIfNeeded(source_texture);
     DCHECK(stream_texture_manager());
     StreamTexture* stream_tex =
         stream_texture_manager()->LookupStreamTexture(
diff --git a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
index b8194d9..8975d20 100644
--- a/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
+++ b/gpu/command_buffer/service/gles2_cmd_validation_implementation_autogen.h
@@ -299,8 +299,8 @@
   GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT,
   GL_COMMANDS_ISSUED_CHROMIUM,
   GL_LATENCY_QUERY_CHROMIUM,
-  GL_ASYNC_PIXEL_TRANSFERS_COMPLETED_CHROMIUM,
-  GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM,
+  GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM,
+  GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM,
 };
 
 static const GLenum valid_read_pixel_format_table[] = {
diff --git a/gpu/command_buffer/service/in_process_command_buffer.cc b/gpu/command_buffer/service/in_process_command_buffer.cc
index 6b9fe8a..3c1249a 100644
--- a/gpu/command_buffer/service/in_process_command_buffer.cc
+++ b/gpu/command_buffer/service/in_process_command_buffer.cc
@@ -36,7 +36,7 @@
 
 #if defined(OS_ANDROID)
 #include "gpu/command_buffer/service/stream_texture_manager_in_process_android.h"
-#include "ui/gl/android/surface_texture_bridge.h"
+#include "ui/gl/android/surface_texture.h"
 #endif
 
 namespace gpu {
@@ -728,7 +728,7 @@
 }
 
 #if defined(OS_ANDROID)
-scoped_refptr<gfx::SurfaceTextureBridge>
+scoped_refptr<gfx::SurfaceTexture>
 InProcessCommandBuffer::GetSurfaceTexture(uint32 stream_id) {
   DCHECK(stream_texture_manager_);
   return stream_texture_manager_->GetSurfaceTexture(stream_id);
diff --git a/gpu/command_buffer/service/in_process_command_buffer.h b/gpu/command_buffer/service/in_process_command_buffer.h
index 3caf4aa..64b466f 100644
--- a/gpu/command_buffer/service/in_process_command_buffer.h
+++ b/gpu/command_buffer/service/in_process_command_buffer.h
@@ -33,7 +33,7 @@
 
 #if defined(OS_ANDROID)
 namespace gfx {
-class SurfaceTextureBridge;
+class SurfaceTexture;
 }
 namespace gpu {
 class StreamTextureManagerInProcess;
@@ -126,7 +126,7 @@
   };
 
 #if defined(OS_ANDROID)
-  scoped_refptr<gfx::SurfaceTextureBridge> GetSurfaceTexture(
+  scoped_refptr<gfx::SurfaceTexture> GetSurfaceTexture(
       uint32 stream_id);
 #endif
 
diff --git a/gpu/command_buffer/service/memory_program_cache.cc b/gpu/command_buffer/service/memory_program_cache.cc
index 3bff3dc..94dc366 100644
--- a/gpu/command_buffer/service/memory_program_cache.cc
+++ b/gpu/command_buffer/service/memory_program_cache.cc
@@ -67,6 +67,7 @@
     info->set_type(iter->second.type);
     info->set_size(iter->second.size);
     info->set_precision(iter->second.precision);
+    info->set_static_use(iter->second.static_use);
     info->set_name(iter->second.name);
   }
 }
@@ -74,7 +75,8 @@
 void RetrieveShaderInfo(const ShaderInfoProto& proto,
                         ShaderTranslator::VariableMap* map) {
   ShaderTranslator::VariableInfo info(
-      proto.type(), proto.size(), proto.precision(), proto.name());
+      proto.type(), proto.size(), proto.precision(),
+      proto.static_use(), proto.name());
   (*map)[proto.key()] = info;
 }
 
diff --git a/gpu/command_buffer/service/memory_program_cache_unittest.cc b/gpu/command_buffer/service/memory_program_cache_unittest.cc
index 36291c4..6c6b099 100644
--- a/gpu/command_buffer/service/memory_program_cache_unittest.cc
+++ b/gpu/command_buffer/service/memory_program_cache_unittest.cc
@@ -113,15 +113,15 @@
     VariableMap fragment_uniform_map;
     VariableMap fragment_varying_map;
 
-    vertex_attrib_map["a"] = VariableInfo(1, 34, SH_PRECISION_LOWP, "a");
-    vertex_uniform_map["a"] = VariableInfo(0, 10, SH_PRECISION_MEDIUMP,  "a");
-    vertex_uniform_map["b"] = VariableInfo(2, 3114, SH_PRECISION_HIGHP, "b");
-    vertex_varying_map["c"] = VariableInfo(3, 2, SH_PRECISION_HIGHP, "c");
+    vertex_attrib_map["a"] = VariableInfo(1, 34, SH_PRECISION_LOWP, 0, "a");
+    vertex_uniform_map["a"] = VariableInfo(0, 10, SH_PRECISION_MEDIUMP, 1, "a");
+    vertex_uniform_map["b"] = VariableInfo(2, 3114, SH_PRECISION_HIGHP, 1, "b");
+    vertex_varying_map["c"] = VariableInfo(3, 2, SH_PRECISION_HIGHP, 1, "c");
     fragment_attrib_map["jjjbb"] =
-        VariableInfo(463, 1114, SH_PRECISION_MEDIUMP, "jjjbb");
+        VariableInfo(463, 1114, SH_PRECISION_MEDIUMP, 0, "jjjbb");
     fragment_uniform_map["k"] =
-        VariableInfo(10, 34413, SH_PRECISION_MEDIUMP, "k");
-    fragment_varying_map["c"] = VariableInfo(3, 2, SH_PRECISION_HIGHP, "c");
+        VariableInfo(10, 34413, SH_PRECISION_MEDIUMP, 1, "k");
+    fragment_varying_map["c"] = VariableInfo(3, 2, SH_PRECISION_HIGHP, 1, "c");
 
     vertex_shader_->set_attrib_map(vertex_attrib_map);
     vertex_shader_->set_uniform_map(vertex_uniform_map);
diff --git a/gpu/command_buffer/service/program_manager.cc b/gpu/command_buffer/service/program_manager.cc
index 19f05cc..fa42deb 100644
--- a/gpu/command_buffer/service/program_manager.cc
+++ b/gpu/command_buffer/service/program_manager.cc
@@ -538,6 +538,12 @@
     set_log_info("Uniforms with the same name but different type/precision");
     return false;
   }
+  if (DetectVaryingsMismatch()) {
+    set_log_info("Varyings with the same name but different type, "
+                 "or statically used varyings in fragment shader are not "
+                 "declared in vertex shader");
+    return false;
+  }
 
   TimeTicks before_time = TimeTicks::HighResNow();
   bool link = true;
@@ -1015,6 +1021,52 @@
   return false;
 }
 
+bool Program::DetectVaryingsMismatch() const {
+  DCHECK(attached_shaders_[0] &&
+         attached_shaders_[0]->shader_type() == GL_VERTEX_SHADER &&
+         attached_shaders_[1] &&
+         attached_shaders_[1]->shader_type() == GL_FRAGMENT_SHADER);
+  const ShaderTranslator::VariableMap* vertex_varyings =
+      &(attached_shaders_[0]->varying_map());
+  const ShaderTranslator::VariableMap* fragment_varyings =
+      &(attached_shaders_[1]->varying_map());
+
+  for (ShaderTranslator::VariableMap::const_iterator iter =
+           fragment_varyings->begin();
+       iter != fragment_varyings->end(); ++iter) {
+    // Built-in variables.
+    const char* kBuiltInVaryings[] = {
+        "gl_FragCoord",
+        "gl_FrontFacing",
+        "gl_PointCoord"
+    };
+    const std::string& name = iter->first;
+    bool is_built_in = false;
+    for (size_t ii = 0; ii < arraysize(kBuiltInVaryings); ++ii) {
+      if (name == kBuiltInVaryings[ii]) {
+        is_built_in = true;
+        break;
+      }
+    }
+    if (is_built_in)
+      continue;
+
+    ShaderTranslator::VariableMap::const_iterator hit =
+        vertex_varyings->find(name);
+    if (hit == vertex_varyings->end()) {
+      if (iter->second.static_use)
+        return true;
+      continue;
+    }
+
+    if (hit->second.type != iter->second.type ||
+        hit->second.size != iter->second.size)
+      return true;
+
+  }
+  return false;
+}
+
 static uint32 ComputeOffset(const void* start, const void* position) {
   return static_cast<const uint8*>(position) -
          static_cast<const uint8*>(start);
diff --git a/gpu/command_buffer/service/program_manager.h b/gpu/command_buffer/service/program_manager.h
index 05014ba..6858ced 100644
--- a/gpu/command_buffer/service/program_manager.h
+++ b/gpu/command_buffer/service/program_manager.h
@@ -191,6 +191,10 @@
   // Return true if such cases are detected.
   bool DetectUniformsMismatch() const;
 
+  // Return true if a varying is statically used in fragment shader, but it
+  // is not declared in vertex shader.
+  bool DetectVaryingsMismatch() const;
+
   // Visible for testing
   const LocationMap& bind_attrib_location_map() const {
     return bind_attrib_location_map_;
diff --git a/gpu/command_buffer/service/program_manager_unittest.cc b/gpu/command_buffer/service/program_manager_unittest.cc
index da16c85..c238458 100644
--- a/gpu/command_buffer/service/program_manager_unittest.cc
+++ b/gpu/command_buffer/service/program_manager_unittest.cc
@@ -165,6 +165,7 @@
   static const int kAttrib1Precision = SH_PRECISION_MEDIUMP;
   static const int kAttrib2Precision = SH_PRECISION_HIGHP;
   static const int kAttrib3Precision = SH_PRECISION_LOWP;
+  static const int kAttribStaticUse = 0;
   static const GLint kAttrib1Location = 0;
   static const GLint kAttrib2Location = 1;
   static const GLint kAttrib3Location = 2;
@@ -184,6 +185,9 @@
   static const int kUniform1Precision = SH_PRECISION_LOWP;
   static const int kUniform2Precision = SH_PRECISION_MEDIUMP;
   static const int kUniform3Precision = SH_PRECISION_HIGHP;
+  static const int kUniform1StaticUse = 1;
+  static const int kUniform2StaticUse = 1;
+  static const int kUniform3StaticUse = 1;
   static const GLint kUniform1FakeLocation = 0;  // These are hard coded
   static const GLint kUniform2FakeLocation = 1;  // to match
   static const GLint kUniform3FakeLocation = 2;  // ProgramManager.
@@ -268,6 +272,82 @@
     return (static_cast<bool>(link_status) == expected_link_status);
   }
 
+  Program* SetupVaryingsMatchingTest(bool vertex_varying_declare,
+                                     int vertex_varying_type,
+                                     int vertex_varying_size,
+                                     int vertex_varying_precision,
+                                     int vertex_varying_static_use,
+                                     bool frag_varying_declare,
+                                     int frag_varying_type,
+                                     int frag_varying_size,
+                                     int frag_varying_precision,
+                                     int frag_varying_static_use) {
+    // Set up shader
+    const GLuint kVShaderClientId = 1;
+    const GLuint kVShaderServiceId = 11;
+    const GLuint kFShaderClientId = 2;
+    const GLuint kFShaderServiceId = 12;
+
+    MockShaderTranslator vertex_shader_translator;
+    ShaderTranslator::VariableMap vertex_attrib_map;
+    ShaderTranslator::VariableMap vertex_uniform_map;
+    ShaderTranslator::VariableMap vertex_varying_map;
+    if (vertex_varying_declare) {
+      vertex_varying_map["a"] = ShaderTranslator::VariableInfo(
+          vertex_varying_type, vertex_varying_size,
+          vertex_varying_precision, vertex_varying_static_use, "a");
+    }
+    ShaderTranslator::NameMap vertex_name_map;
+    EXPECT_CALL(vertex_shader_translator, attrib_map())
+        .WillRepeatedly(ReturnRef(vertex_attrib_map));
+    EXPECT_CALL(vertex_shader_translator, uniform_map())
+        .WillRepeatedly(ReturnRef(vertex_uniform_map));
+    EXPECT_CALL(vertex_shader_translator, varying_map())
+        .WillRepeatedly(ReturnRef(vertex_varying_map));
+    EXPECT_CALL(vertex_shader_translator, name_map())
+      .WillRepeatedly(ReturnRef(vertex_name_map));
+
+    MockShaderTranslator frag_shader_translator;
+    ShaderTranslator::VariableMap frag_attrib_map;
+    ShaderTranslator::VariableMap frag_uniform_map;
+    ShaderTranslator::VariableMap frag_varying_map;
+    if (frag_varying_declare) {
+      frag_varying_map["a"] = ShaderTranslator::VariableInfo(
+          frag_varying_type, frag_varying_size,
+          frag_varying_precision, frag_varying_static_use, "a");
+    }
+    ShaderTranslator::NameMap frag_name_map;
+    EXPECT_CALL(frag_shader_translator, attrib_map())
+        .WillRepeatedly(ReturnRef(frag_attrib_map));
+    EXPECT_CALL(frag_shader_translator, uniform_map())
+        .WillRepeatedly(ReturnRef(frag_uniform_map));
+    EXPECT_CALL(frag_shader_translator, varying_map())
+        .WillRepeatedly(ReturnRef(frag_varying_map));
+    EXPECT_CALL(frag_shader_translator, name_map())
+      .WillRepeatedly(ReturnRef(frag_name_map));
+
+    // Check we can create shader.
+    Shader* vshader = shader_manager_.CreateShader(
+        kVShaderClientId, kVShaderServiceId, GL_VERTEX_SHADER);
+    Shader* fshader = shader_manager_.CreateShader(
+        kFShaderClientId, kFShaderServiceId, GL_FRAGMENT_SHADER);
+    // Check shader got created.
+    EXPECT_TRUE(vshader != NULL && fshader != NULL);
+    // Set Status
+    vshader->SetStatus(true, "", &vertex_shader_translator);
+    fshader->SetStatus(true, "", &frag_shader_translator);
+
+    // Set up program
+    const GLuint kClientProgramId = 6666;
+    const GLuint kServiceProgramId = 8888;
+    Program* program =
+        manager_.CreateProgram(kClientProgramId, kServiceProgramId);
+    EXPECT_TRUE(program != NULL);
+    EXPECT_TRUE(program->AttachShader(&shader_manager_, vshader));
+    EXPECT_TRUE(program->AttachShader(&shader_manager_, fshader));
+    return program;
+  }
+
   static AttribInfo kAttribs[];
   static UniformInfo kUniforms[];
 
@@ -678,17 +758,23 @@
   ShaderTranslator::VariableMap uniform_map;
   ShaderTranslator::VariableMap varying_map;
   attrib_map[kAttrib1Name] = ShaderTranslatorInterface::VariableInfo(
-      kAttrib1Type, kAttrib1Size, kAttrib1Precision, kAttrib1Name);
+      kAttrib1Type, kAttrib1Size, kAttrib1Precision,
+      kAttribStaticUse, kAttrib1Name);
   attrib_map[kAttrib2Name] = ShaderTranslatorInterface::VariableInfo(
-      kAttrib2GoodType, kAttrib2Size, kAttrib2Precision, kAttrib2Name);
+      kAttrib2GoodType, kAttrib2Size, kAttrib2Precision,
+      kAttribStaticUse, kAttrib2Name);
   attrib_map[kAttrib3Name] = ShaderTranslatorInterface::VariableInfo(
-      kAttrib3Type, kAttrib3Size, kAttrib3Precision, kAttrib3Name);
+      kAttrib3Type, kAttrib3Size, kAttrib3Precision,
+      kAttribStaticUse, kAttrib3Name);
   uniform_map[kUniform1Name] = ShaderTranslatorInterface::VariableInfo(
-      kUniform1Type, kUniform1Size, kUniform1Precision, kUniform1Name);
+      kUniform1Type, kUniform1Size, kUniform1Precision,
+      kUniform1StaticUse, kUniform1Name);
   uniform_map[kUniform2Name] = ShaderTranslatorInterface::VariableInfo(
-      kUniform2GoodType, kUniform2Size, kUniform2Precision, kUniform2Name);
+      kUniform2GoodType, kUniform2Size, kUniform2Precision,
+      kUniform2StaticUse, kUniform2Name);
   uniform_map[kUniform3GoodName] = ShaderTranslatorInterface::VariableInfo(
-      kUniform3Type, kUniform3Size, kUniform3Precision, kUniform3GoodName);
+      kUniform3Type, kUniform3Size, kUniform3Precision,
+      kUniform3StaticUse, kUniform3GoodName);
   EXPECT_CALL(shader_translator, attrib_map())
       .WillRepeatedly(ReturnRef(attrib_map));
   EXPECT_CALL(shader_translator, uniform_map())
@@ -949,6 +1035,7 @@
         kAttribs[ii].type,
         kAttribs[ii].size,
         SH_PRECISION_MEDIUMP,
+        kAttribStaticUse,
         kAttribs[ii].name);
   }
   ShaderTranslator::VariableMap uniform_map;
@@ -980,6 +1067,7 @@
     EXPECT_EQ(it->second.type, variable_info->type);
     EXPECT_EQ(it->second.size, variable_info->size);
     EXPECT_EQ(it->second.precision, variable_info->precision);
+    EXPECT_EQ(it->second.static_use, variable_info->static_use);
     EXPECT_EQ(it->second.name, variable_info->name);
   }
   fshader->SetStatus(true, "", NULL);
@@ -1024,7 +1112,7 @@
   ShaderTranslator::VariableMap vertex_attrib_map;
   ShaderTranslator::VariableMap vertex_uniform_map;
   vertex_uniform_map["a"] = ShaderTranslator::VariableInfo(
-      1, 3, SH_PRECISION_MEDIUMP, "a");
+      1, 3, SH_PRECISION_MEDIUMP, 1, "a");
   ShaderTranslator::VariableMap vertex_varying_map;
   ShaderTranslator::NameMap vertex_name_map;
   EXPECT_CALL(vertex_shader_translator, attrib_map())
@@ -1040,7 +1128,7 @@
   ShaderTranslator::VariableMap frag_attrib_map;
   ShaderTranslator::VariableMap frag_uniform_map;
   frag_uniform_map["a"] = ShaderTranslator::VariableInfo(
-      1, 3, SH_PRECISION_LOWP, "a");
+      1, 3, SH_PRECISION_LOWP, 1, "a");
   ShaderTranslator::VariableMap frag_varying_map;
   ShaderTranslator::NameMap frag_name_map;
   EXPECT_CALL(frag_shader_translator, attrib_map())
@@ -1076,6 +1164,62 @@
   EXPECT_TRUE(LinkAsExpected(program, false));
 }
 
+// If a varying has different type in the vertex and fragment
+// shader, linking should fail.
+TEST_F(ProgramManagerWithShaderTest, VaryingTypeMismatch) {
+  Program* program = SetupVaryingsMatchingTest(
+      true, SH_FLOAT_VEC3, 1, SH_PRECISION_MEDIUMP, 1,
+      true, SH_FLOAT_VEC4, 1, SH_PRECISION_MEDIUMP, 1);
+
+  EXPECT_TRUE(program->DetectVaryingsMismatch());
+  EXPECT_TRUE(LinkAsExpected(program, false));
+}
+
+// If a varying has different array size in the vertex and fragment
+// shader, linking should fail.
+TEST_F(ProgramManagerWithShaderTest, VaryingArraySizeMismatch) {
+  Program* program = SetupVaryingsMatchingTest(
+      true, SH_FLOAT, 2, SH_PRECISION_MEDIUMP, 1,
+      true, SH_FLOAT, 3, SH_PRECISION_MEDIUMP, 1);
+
+  EXPECT_TRUE(program->DetectVaryingsMismatch());
+  EXPECT_TRUE(LinkAsExpected(program, false));
+}
+
+// If a varying has different precision in the vertex and fragment
+// shader, linking should succeed.
+TEST_F(ProgramManagerWithShaderTest, VaryingPrecisionMismatch) {
+  Program* program = SetupVaryingsMatchingTest(
+      true, SH_FLOAT, 2, SH_PRECISION_HIGHP, 1,
+      true, SH_FLOAT, 2, SH_PRECISION_MEDIUMP, 1);
+
+  EXPECT_FALSE(program->DetectVaryingsMismatch());
+  EXPECT_TRUE(LinkAsExpected(program, true));
+}
+
+// If a varying is statically used in fragment shader but not
+// declared in vertex shader, link should fail.
+TEST_F(ProgramManagerWithShaderTest, VaryingMissing) {
+  Program* program = SetupVaryingsMatchingTest(
+      false, 0, 0, 0, 0,
+      true, SH_FLOAT, 3, SH_PRECISION_MEDIUMP, 1);
+
+  EXPECT_TRUE(program->DetectVaryingsMismatch());
+  EXPECT_TRUE(LinkAsExpected(program, false));
+}
+
+// If a varying is declared but not statically used in fragment
+// shader, even if it's not declared in vertex shader, link should
+// succeed.
+TEST_F(ProgramManagerWithShaderTest, InactiveVarying) {
+  Program* program = SetupVaryingsMatchingTest(
+      false, 0, 0, 0, 0,
+      true, SH_FLOAT, 3, SH_PRECISION_MEDIUMP, 0);
+
+  EXPECT_FALSE(program->DetectVaryingsMismatch());
+  EXPECT_TRUE(LinkAsExpected(program, true));
+}
+
 TEST_F(ProgramManagerWithShaderTest, ClearWithSamplerTypes) {
   const GLuint kVShaderClientId = 2001;
   const GLuint kFShaderClientId = 2002;
diff --git a/gpu/command_buffer/service/query_manager.cc b/gpu/command_buffer/service/query_manager.cc
index 7566099..85e273c 100644
--- a/gpu/command_buffer/service/query_manager.cc
+++ b/gpu/command_buffer/service/query_manager.cc
@@ -431,11 +431,12 @@
     case GL_LATENCY_QUERY_CHROMIUM:
       query = new CommandLatencyQuery(this, target, shm_id, shm_offset);
       break;
-    case GL_ASYNC_PIXEL_TRANSFERS_COMPLETED_CHROMIUM:
+    case GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM:
+      // Currently async pixel transfer delegates only support uploads.
       query = new AsyncPixelTransfersCompletedQuery(
           this, target, shm_id, shm_offset);
       break;
-    case GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM:
+    case GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM:
       query = new AsyncReadPixelsCompletedQuery(
           this, target, shm_id, shm_offset);
       break;
diff --git a/gpu/command_buffer/service/shader_manager_unittest.cc b/gpu/command_buffer/service/shader_manager_unittest.cc
index 642b121..d86aca4 100644
--- a/gpu/command_buffer/service/shader_manager_unittest.cc
+++ b/gpu/command_buffer/service/shader_manager_unittest.cc
@@ -140,26 +140,33 @@
   const GLsizei kAttrib2Size = 4;
   const int kAttrib2Precision = SH_PRECISION_HIGHP;
   const char* kAttrib2Name = "attr2";
+  const int kAttribStaticUse = 0;
   const GLenum kUniform1Type = GL_FLOAT_MAT2;
   const GLsizei kUniform1Size = 3;
   const int kUniform1Precision = SH_PRECISION_LOWP;
+  const int kUniform1StaticUse = 1;
   const char* kUniform1Name = "uni1";
   const GLenum kUniform2Type = GL_FLOAT_MAT3;
   const GLsizei kUniform2Size = 5;
   const int kUniform2Precision = SH_PRECISION_MEDIUMP;
+  const int kUniform2StaticUse = 0;
   const char* kUniform2Name = "uni2";
 
   MockShaderTranslator shader_translator;
   ShaderTranslator::VariableMap attrib_map;
   attrib_map[kAttrib1Name] = ShaderTranslatorInterface::VariableInfo(
-      kAttrib1Type, kAttrib1Size, kAttrib1Precision, kAttrib1Name);
+      kAttrib1Type, kAttrib1Size, kAttrib1Precision,
+      kAttribStaticUse, kAttrib1Name);
   attrib_map[kAttrib2Name] = ShaderTranslatorInterface::VariableInfo(
-      kAttrib2Type, kAttrib2Size, kAttrib2Precision, kAttrib2Name);
+      kAttrib2Type, kAttrib2Size, kAttrib2Precision,
+      kAttribStaticUse, kAttrib2Name);
   ShaderTranslator::VariableMap uniform_map;
   uniform_map[kUniform1Name] = ShaderTranslatorInterface::VariableInfo(
-      kUniform1Type, kUniform1Size, kUniform1Precision, kUniform1Name);
+      kUniform1Type, kUniform1Size, kUniform1Precision,
+      kUniform1StaticUse, kUniform1Name);
   uniform_map[kUniform2Name] = ShaderTranslatorInterface::VariableInfo(
-      kUniform2Type, kUniform2Size, kUniform2Precision, kUniform2Name);
+      kUniform2Type, kUniform2Size, kUniform2Precision,
+      kUniform2StaticUse, kUniform2Name);
   EXPECT_CALL(shader_translator, attrib_map())
       .WillRepeatedly(ReturnRef(attrib_map));
   EXPECT_CALL(shader_translator, uniform_map())
@@ -186,6 +193,7 @@
     EXPECT_EQ(it->second.type, variable_info->type);
     EXPECT_EQ(it->second.size, variable_info->size);
     EXPECT_EQ(it->second.precision, variable_info->precision);
+    EXPECT_EQ(it->second.static_use, variable_info->static_use);
     EXPECT_EQ(it->second.name, variable_info->name);
   }
   for (ShaderTranslator::VariableMap::const_iterator it = uniform_map.begin();
@@ -196,6 +204,7 @@
     EXPECT_EQ(it->second.type, variable_info->type);
     EXPECT_EQ(it->second.size, variable_info->size);
     EXPECT_EQ(it->second.precision, variable_info->precision);
+    EXPECT_EQ(it->second.static_use, variable_info->static_use);
     EXPECT_EQ(it->second.name, variable_info->name);
   }
   // Check attrib and uniform get cleared.
diff --git a/gpu/command_buffer/service/shader_translator.cc b/gpu/command_buffer/service/shader_translator.cc
index f05c450..e6378b8 100644
--- a/gpu/command_buffer/service/shader_translator.cc
+++ b/gpu/command_buffer/service/shader_translator.cc
@@ -64,10 +64,11 @@
     ANGLEGetInfoType len = 0;
     int size = 0;
     ShDataType type = SH_NONE;
-    ShPrecisionType precision = SH_PRECISION_MEDIUMP;
+    ShPrecisionType precision = SH_PRECISION_UNDEFINED;
+    int static_use = 0;
 
     ShGetVariableInfo(compiler, var_type, i,
-                      &len, &size, &type, &precision,
+                      &len, &size, &type, &precision, &static_use,
                       name.get(), mapped_name.get());
 
     // In theory we should CHECK(len <= name_len - 1) here, but ANGLE needs
@@ -77,7 +78,8 @@
     std::string name_string(name.get(), std::min(len, name_len - 1));
     mapped_name.get()[mapped_name_len - 1] = '\0';
 
-    ShaderTranslator::VariableInfo info(type, size, precision, name_string);
+    ShaderTranslator::VariableInfo info(
+        type, size, precision, static_use, name_string);
     (*var_map)[mapped_name.get()] = info;
   }
 }
diff --git a/gpu/command_buffer/service/shader_translator.h b/gpu/command_buffer/service/shader_translator.h
index d4524d0..268a155 100644
--- a/gpu/command_buffer/service/shader_translator.h
+++ b/gpu/command_buffer/service/shader_translator.h
@@ -36,13 +36,16 @@
     VariableInfo()
         : type(0),
           size(0),
-          precision(SH_PRECISION_MEDIUMP) {
+          precision(SH_PRECISION_UNDEFINED),
+          static_use(0) {
     }
 
-    VariableInfo(int _type, int _size, int _precision, std::string _name)
+    VariableInfo(int _type, int _size, int _precision,
+                 int _static_use, std::string _name)
         : type(_type),
           size(_size),
           precision(_precision),
+          static_use(_static_use),
           name(_name) {
     }
     bool operator==(
@@ -56,6 +59,7 @@
     int type;
     int size;
     int precision;
+    int static_use;
     std::string name;  // name in the original shader source.
   };
 
diff --git a/gpu/command_buffer/service/stream_texture_manager_in_process_android.cc b/gpu/command_buffer/service/stream_texture_manager_in_process_android.cc
index 0a6c4c9..2fd5535 100644
--- a/gpu/command_buffer/service/stream_texture_manager_in_process_android.cc
+++ b/gpu/command_buffer/service/stream_texture_manager_in_process_android.cc
@@ -6,7 +6,7 @@
 
 #include "base/bind.h"
 #include "ui/gfx/size.h"
-#include "ui/gl/android/surface_texture_bridge.h"
+#include "ui/gl/android/surface_texture.h"
 #include "ui/gl/gl_bindings.h"
 
 namespace gpu {
@@ -14,7 +14,7 @@
 StreamTextureManagerInProcess::StreamTextureImpl::StreamTextureImpl(
     uint32 service_id,
     uint32 stream_id)
-    : surface_texture_bridge_(new gfx::SurfaceTextureBridge(service_id)),
+    : surface_texture_(new gfx::SurfaceTexture(service_id)),
       stream_id_(stream_id) {}
 
 StreamTextureManagerInProcess::StreamTextureImpl::~StreamTextureImpl() {}
@@ -22,7 +22,7 @@
 void StreamTextureManagerInProcess::StreamTextureImpl::Update() {
   GLint texture_id = 0;
   glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texture_id);
-  surface_texture_bridge_->UpdateTexImage();
+  surface_texture_->UpdateTexImage();
   glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id);
 }
 
@@ -34,9 +34,9 @@
   size_ = size;
 }
 
-scoped_refptr<gfx::SurfaceTextureBridge>
+scoped_refptr<gfx::SurfaceTexture>
 StreamTextureManagerInProcess::StreamTextureImpl::GetSurfaceTexture() {
-  return surface_texture_bridge_;
+  return surface_texture_;
 }
 
 StreamTextureManagerInProcess::StreamTextureManagerInProcess() : next_id_(1) {}
@@ -77,7 +77,7 @@
   return NULL;
 }
 
-scoped_refptr<gfx::SurfaceTextureBridge>
+scoped_refptr<gfx::SurfaceTexture>
 StreamTextureManagerInProcess::GetSurfaceTexture(uint32 stream_id) {
   base::AutoLock lock(map_lock_);
   for (TextureMap::iterator it = textures_.begin(); it != textures_.end();
diff --git a/gpu/command_buffer/service/stream_texture_manager_in_process_android.h b/gpu/command_buffer/service/stream_texture_manager_in_process_android.h
index 70ec024..6753c13 100644
--- a/gpu/command_buffer/service/stream_texture_manager_in_process_android.h
+++ b/gpu/command_buffer/service/stream_texture_manager_in_process_android.h
@@ -15,7 +15,7 @@
 
 namespace gfx {
 class Size;
-class SurfaceTextureBridge;
+class SurfaceTexture;
 }
 
 namespace gpu {
@@ -32,7 +32,7 @@
   virtual void DestroyStreamTexture(uint32 service_id) OVERRIDE;
   virtual gpu::StreamTexture* LookupStreamTexture(uint32 service_id) OVERRIDE;
 
-  scoped_refptr<gfx::SurfaceTextureBridge> GetSurfaceTexture(uint32 stream_id);
+  scoped_refptr<gfx::SurfaceTexture> GetSurfaceTexture(uint32 stream_id);
 
  private:
   class StreamTextureImpl : public gpu::StreamTexture {
@@ -46,11 +46,11 @@
 
     void SetSize(gfx::Size size);
 
-    scoped_refptr<gfx::SurfaceTextureBridge> GetSurfaceTexture();
+    scoped_refptr<gfx::SurfaceTexture> GetSurfaceTexture();
     uint32 stream_id() { return stream_id_; }
 
    private:
-    scoped_refptr<gfx::SurfaceTextureBridge> surface_texture_bridge_;
+    scoped_refptr<gfx::SurfaceTexture> surface_texture_;
     uint32 stream_id_;
     gfx::Size size_;
 
diff --git a/gpu/command_buffer/tests/gl_readback_unittests.cc b/gpu/command_buffer/tests/gl_readback_unittests.cc
index 4ebb09a..d4b5d3d 100644
--- a/gpu/command_buffer/tests/gl_readback_unittests.cc
+++ b/gpu/command_buffer/tests/gl_readback_unittests.cc
@@ -64,9 +64,9 @@
                kWidth * kHeight * kBytesPerPixel,
                NULL,
                GL_STREAM_READ);
-  glBeginQueryEXT(GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM, q);
+  glBeginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, q);
   glReadPixels(0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, 0);
-  glEndQueryEXT(GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM);
+  glEndQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM);
   glFlush();
   WaitForQuery(q);
 
diff --git a/gpu/command_buffer/tests/gl_stream_draw_unittests.cc b/gpu/command_buffer/tests/gl_stream_draw_unittests.cc
index 3997016..8915209 100644
--- a/gpu/command_buffer/tests/gl_stream_draw_unittests.cc
+++ b/gpu/command_buffer/tests/gl_stream_draw_unittests.cc
@@ -82,6 +82,8 @@
   GLTestHelper::CheckGLError("no errors", __LINE__);
 }
 
+// http://crbug.com/281565
+#if !defined(OS_ANDROID)
 TEST_F(GLStreamDrawTest, DrawElements) {
   static GLfloat float_red[4] = { 1.0f, 0.0f, 0.0f, 1.0f, };
   static GLfloat float_green[4] = { 0.0f, 1.0f, 0.0f, 1.0f, };
@@ -111,6 +113,7 @@
 
   GLTestHelper::CheckGLError("no errors", __LINE__);
 }
+#endif
 
 TEST_F(GLStreamDrawTest, VertexArrayObjects) {
   if (!GLTestHelper::HasExtension("GL_OES_vertex_array_object")) {
diff --git a/gpu/command_buffer/tests/gl_tests_main.cc b/gpu/command_buffer/tests/gl_tests_main.cc
index e6044e7..4a689a2 100644
--- a/gpu/command_buffer/tests/gl_tests_main.cc
+++ b/gpu/command_buffer/tests/gl_tests_main.cc
@@ -38,8 +38,7 @@
   gfx::GLSurface::InitializeOneOff();
   ::gles2::Initialize();
   gpu::ApplyGpuDriverBugWorkarounds(CommandLine::ForCurrentProcess());
-  base::MessageLoop::Type message_loop_type = base::MessageLoop::TYPE_UI;
-  base::MessageLoop main_message_loop(message_loop_type);
+  base::MessageLoop main_message_loop;
   return GLTestHelper::RunTests(argc, argv);
 }
 
diff --git a/gpu/command_buffer/tests/gl_texture_mailbox_unittests.cc b/gpu/command_buffer/tests/gl_texture_mailbox_unittests.cc
index d960efc..d47f301 100644
--- a/gpu/command_buffer/tests/gl_texture_mailbox_unittests.cc
+++ b/gpu/command_buffer/tests/gl_texture_mailbox_unittests.cc
@@ -318,6 +318,8 @@
   glDeleteTextures(1, &tex1);
 }
 
+// http://crbug.com/281565
+#if !defined(OS_ANDROID)
 TEST_F(GLTextureMailboxTest, ProduceFrontBufferMultipleContexts) {
   gl1_.MakeCurrent();
   Mailbox mailbox[2];
@@ -366,6 +368,7 @@
   gl1_.MakeCurrent();
   glDeleteTextures(2, tex);
 }
+#endif
 
 }  // namespace gpu
 
diff --git a/gpu/command_buffer/tests/gl_unittests_android.cc b/gpu/command_buffer/tests/gl_unittests_android.cc
index 870bedb..671676a 100644
--- a/gpu/command_buffer/tests/gl_unittests_android.cc
+++ b/gpu/command_buffer/tests/gl_unittests_android.cc
@@ -15,7 +15,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/native_widget_types.h"
-#include "ui/gl/android/surface_texture_bridge.h"
+#include "ui/gl/android/surface_texture.h"
 #include "ui/gl/gl_surface.h"
 
 namespace gpu {
@@ -38,8 +38,8 @@
   // abstraction for the SurfaceTexture in this test.
   GLuint texture = 0xFEEDBEEF;
 
-  scoped_refptr<gfx::SurfaceTextureBridge> surface_texture(
-      new gfx::SurfaceTextureBridge(texture));
+  scoped_refptr<gfx::SurfaceTexture> surface_texture(
+      new gfx::SurfaceTexture(texture));
   gfx::AcceleratedWidget window = surface_texture->CreateSurface();
   EXPECT_TRUE(window != NULL);
 
diff --git a/gpu/command_buffer/tests/gl_virtual_contexts_unittests.cc b/gpu/command_buffer/tests/gl_virtual_contexts_unittests.cc
index 29c69e6..fa91220 100644
--- a/gpu/command_buffer/tests/gl_virtual_contexts_unittests.cc
+++ b/gpu/command_buffer/tests/gl_virtual_contexts_unittests.cc
@@ -92,6 +92,8 @@
 
 }  // anonymous namespace
 
+// http://crbug.com/281565
+#if !defined(OS_ANDROID)
 TEST_F(GLVirtualContextsTest, Basic) {
   struct TestInfo {
     int size;
@@ -134,6 +136,7 @@
     GLTestHelper::CheckGLError("no errors", __LINE__);
   }
 }
+#endif
 
 }  // namespace gpu
 
diff --git a/gpu/config/gpu_driver_bug_list_json.cc b/gpu/config/gpu_driver_bug_list_json.cc
index 423a723..e5654fa 100644
--- a/gpu/config/gpu_driver_bug_list_json.cc
+++ b/gpu/config/gpu_driver_bug_list_json.cc
@@ -85,7 +85,7 @@
 {
   "name": "gpu driver bug list",
   // Please update the version number whenever you change this file.
-  "version": "2.5",
+  "version": "2.8",
   "entries": [
     {
       "id": 1,
@@ -425,6 +425,43 @@
       "features": [
         "swizzle_rgba_for_async_readpixels"
       ]
+    },
+    {
+      "id": 28,
+      "cr_bugs": [277817],
+      "description": "Disable use of ANGLE_instanced_arrays on Windows",
+      "os": {
+        "type": "win"
+      },
+      "features": [
+        "disable_angle_instanced_arrays"
+      ]
+    },
+    {
+      "id": 29,
+      "cr_bugs": [278606],
+      "description": "Testing fences is broken on QualComm.",
+      "os": {
+        "type": "android"
+      },
+      "gl_vendor": {
+        "op": "beginwith",
+        "value": "Qualcomm"
+      },
+      "features": [
+        "disable_async_readpixels"
+      ]
+    },
+    {
+      "id": 30,
+      "cr_bugs": [237931],
+      "description": "Multisampling is buggy on OSX when multiple monitors are connected",
+      "os": {
+        "type": "macosx"
+      },
+      "features": [
+        "disable_multimonitor_multisampling"
+      ]
     }
   ]
 }
diff --git a/gpu/config/gpu_driver_bug_workaround_type.h b/gpu/config/gpu_driver_bug_workaround_type.h
index 20198da..b3e2581 100644
--- a/gpu/config/gpu_driver_bug_workaround_type.h
+++ b/gpu/config/gpu_driver_bug_workaround_type.h
@@ -62,6 +62,10 @@
          disable_d3d11)                                      \
   GPU_OP(SWIZZLE_RGBA_FOR_ASYNC_READPIXELS,                  \
          swizzle_rgba_for_async_readpixels)                  \
+  GPU_OP(DISABLE_ANGLE_INSTANCED_ARRAYS,                     \
+         disable_angle_instanced_arrays)                     \
+  GPU_OP(DISABLE_MULTIMONITOR_MULTISAMPLING,                 \
+         disable_multimonitor_multisampling)                 \
 
 namespace gpu {
 
diff --git a/gpu/config/software_rendering_list_json.cc b/gpu/config/software_rendering_list_json.cc
index b7c5c53..4a2f0bf 100644
--- a/gpu/config/software_rendering_list_json.cc
+++ b/gpu/config/software_rendering_list_json.cc
@@ -89,7 +89,7 @@
 {
   "name": "software rendering list",
   // Please update the version number whenever you change this file.
-  "version": "6.5",
+  "version": "6.6",
   "entries": [
     {
       "id": 1,
@@ -1160,6 +1160,23 @@
       "features": [
         "webgl"
       ]
+    },
+    {
+      "id": 77,
+      "description": "Multisampling is reportedly very slow on Quadro NVS 135M/GeForce 8400M GS",
+      "cr_bugs": [279446],
+      "os": {
+        "type": "win",
+        "version": {
+          "op": "=",
+          "number": "5"
+        }
+      },
+      "vendor_id": "0x10de",
+      "device_id": ["0x0429", "0x042b"],
+      "features": [
+        "multisampling"
+      ]
     }
   ]
 }
diff --git a/ipc/file_descriptor_set_posix.cc b/ipc/file_descriptor_set_posix.cc
index 3cb5880..584efec 100644
--- a/ipc/file_descriptor_set_posix.cc
+++ b/ipc/file_descriptor_set_posix.cc
@@ -37,8 +37,10 @@
 }
 
 bool FileDescriptorSet::Add(int fd) {
-  if (descriptors_.size() == kMaxDescriptorsPerMessage)
+  if (descriptors_.size() == kMaxDescriptorsPerMessage) {
+    DLOG(WARNING) << "Cannot add file descriptor. FileDescriptorSet full.";
     return false;
+  }
 
   struct base::FileDescriptor sd;
   sd.fd = fd;
@@ -48,8 +50,10 @@
 }
 
 bool FileDescriptorSet::AddAndAutoClose(int fd) {
-  if (descriptors_.size() == kMaxDescriptorsPerMessage)
+  if (descriptors_.size() == kMaxDescriptorsPerMessage) {
+    DLOG(WARNING) << "Cannot add file descriptor. FileDescriptorSet full.";
     return false;
+  }
 
   struct base::FileDescriptor sd;
   sd.fd = fd;
diff --git a/ipc/file_descriptor_set_posix.h b/ipc/file_descriptor_set_posix.h
index de4c5c6..f9c6033 100644
--- a/ipc/file_descriptor_set_posix.h
+++ b/ipc/file_descriptor_set_posix.h
@@ -30,7 +30,7 @@
   //
   // In debugging mode, it's a fatal error to try and add more than this number
   // of descriptors to a FileDescriptorSet.
-  static const size_t kMaxDescriptorsPerMessage = 5;
+  static const size_t kMaxDescriptorsPerMessage = 7;
 
   // ---------------------------------------------------------------------------
   // Interfaces for building during message serialisation...
diff --git a/ipc/ipc_perftests.cc b/ipc/ipc_perftests.cc
index 81adde8..c9891dc 100644
--- a/ipc/ipc_perftests.cc
+++ b/ipc/ipc_perftests.cc
@@ -10,9 +10,9 @@
 #include "base/basictypes.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/perftimer.h"
 #include "base/pickle.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/perftimer.h"
 #include "base/threading/thread.h"
 #include "base/time/time.h"
 #include "ipc/ipc_channel.h"
diff --git a/jingle/glue/channel_socket_adapter_unittest.cc b/jingle/glue/channel_socket_adapter_unittest.cc
index 65dce71..32aa7cc 100644
--- a/jingle/glue/channel_socket_adapter_unittest.cc
+++ b/jingle/glue/channel_socket_adapter_unittest.cc
@@ -38,6 +38,17 @@
   MOCK_METHOD2(SetOption, int(talk_base::Socket::Option opt, int value));
   MOCK_METHOD0(GetError, int());
   MOCK_CONST_METHOD0(GetIceRole, cricket::IceRole());
+  MOCK_METHOD1(GetStats, bool(cricket::ConnectionInfos* infos));
+  MOCK_CONST_METHOD0(IsDtlsActive, bool());
+  MOCK_CONST_METHOD1(GetSslRole, bool(talk_base::SSLRole* role));
+  MOCK_METHOD1(SetSrtpCiphers, bool(const std::vector<std::string>& ciphers));
+  MOCK_METHOD1(GetSrtpCipher, bool(std::string* cipher));
+  MOCK_METHOD6(ExportKeyingMaterial, bool(const std::string& label,
+                                          const uint8* context,
+                                          size_t context_len,
+                                          bool use_context,
+                                          uint8* result,
+                                          size_t result_len));
 };
 
 class TransportChannelSocketAdapterTest : public testing::Test {
diff --git a/jingle/glue/thread_wrapper.cc b/jingle/glue/thread_wrapper.cc
index f26bc3c..ab5120d 100644
--- a/jingle/glue/thread_wrapper.cc
+++ b/jingle/glue/thread_wrapper.cc
@@ -56,7 +56,7 @@
       weak_ptr_(weak_ptr_factory_.GetWeakPtr()) {
   DCHECK(task_runner->BelongsToCurrentThread());
   DCHECK(!talk_base::Thread::Current());
-  talk_base::MessageQueueManager::Instance()->Add(this);
+  talk_base::MessageQueueManager::Add(this);
   WrapCurrent();
 }
 
@@ -69,7 +69,7 @@
   UnwrapCurrent();
   g_jingle_thread_wrapper.Get().Set(NULL);
   talk_base::ThreadManager::Instance()->SetCurrentThread(NULL);
-  talk_base::MessageQueueManager::Instance()->Remove(this);
+  talk_base::MessageQueueManager::Remove(this);
   talk_base::SocketServer* ss = socketserver();
   delete this;
   delete ss;
diff --git a/media/audio/android/audio_manager_android.cc b/media/audio/android/audio_manager_android.cc
index 69bd2b8..164344a 100644
--- a/media/audio/android/audio_manager_android.cc
+++ b/media/audio/android/audio_manager_android.cc
@@ -59,11 +59,16 @@
 
 AudioParameters AudioManagerAndroid::GetInputStreamParameters(
     const std::string& device_id) {
+  // Use mono as preferred number of input channels on Android to save
+  // resources. Using mono also avoids a driver issue seen on Samsung
+  // Galaxy S3 and S4 devices. See http://crbug.com/256851 for details.
+  ChannelLayout channel_layout = CHANNEL_LAYOUT_MONO;
   int buffer_size = Java_AudioManagerAndroid_getMinInputFrameSize(
-      base::android::AttachCurrentThread(), GetNativeOutputSampleRate(), 2);
+      base::android::AttachCurrentThread(), GetNativeOutputSampleRate(),
+      ChannelLayoutToChannelCount(channel_layout));
 
   return AudioParameters(
-      AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
+      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
       GetNativeOutputSampleRate(), 16,
       buffer_size <= 0 ? kDefaultInputBufferSize : buffer_size);
 }
diff --git a/media/audio/audio_input_controller.cc b/media/audio/audio_input_controller.cc
index f7747b9..d701337 100644
--- a/media/audio/audio_input_controller.cc
+++ b/media/audio/audio_input_controller.cc
@@ -8,6 +8,7 @@
 #include "base/threading/thread_restrictions.h"
 #include "media/base/limits.h"
 #include "media/base/scoped_histogram_timer.h"
+#include "media/base/user_input_monitor.h"
 
 namespace {
 const int kMaxInputChannels = 2;
@@ -46,7 +47,7 @@
       sync_writer_(sync_writer),
       max_volume_(0.0),
       user_input_monitor_(user_input_monitor),
-      key_pressed_(false) {
+      prev_key_down_count_(0) {
   DCHECK(creator_loop_.get());
 }
 
@@ -218,6 +219,11 @@
 
   state_ = kCreated;
   handler_->OnCreated(this);
+
+  if (user_input_monitor_) {
+    user_input_monitor_->EnableKeyPressMonitoring();
+    prev_key_down_count_ = user_input_monitor_->GetKeyPressCount();
+  }
 }
 
 void AudioInputController::DoRecord() {
@@ -240,9 +246,6 @@
 
   stream_->Start(this);
   handler_->OnRecording(this);
-
-  if (user_input_monitor_)
-    user_input_monitor_->AddKeyStrokeListener(this);
 }
 
 void AudioInputController::DoClose() {
@@ -263,7 +266,7 @@
     state_ = kClosed;
 
     if (user_input_monitor_)
-      user_input_monitor_->RemoveKeyStrokeListener(this);
+      user_input_monitor_->DisableKeyPressMonitoring();
   }
 }
 
@@ -330,16 +333,23 @@
       base::Unretained(this)));
 }
 
-void AudioInputController::OnData(AudioInputStream* stream, const uint8* data,
-                                  uint32 size, uint32 hardware_delay_bytes,
+void AudioInputController::OnData(AudioInputStream* stream,
+                                  const uint8* data,
+                                  uint32 size,
+                                  uint32 hardware_delay_bytes,
                                   double volume) {
-  bool key_pressed = false;
   {
     base::AutoLock auto_lock(lock_);
     if (state_ != kRecording)
       return;
+  }
 
-    std::swap(key_pressed, key_pressed_);
+  bool key_pressed = false;
+  if (user_input_monitor_) {
+    size_t current_count = user_input_monitor_->GetKeyPressCount();
+    key_pressed = current_count != prev_key_down_count_;
+    prev_key_down_count_ = current_count;
+    DVLOG_IF(6, key_pressed) << "Detected keypress.";
   }
 
   // Mark data as active to ensure that the periodic calls to
@@ -369,11 +379,6 @@
       &AudioInputController::DoReportError, this));
 }
 
-void AudioInputController::OnKeyStroke() {
-  base::AutoLock auto_lock(lock_);
-  key_pressed_ = true;
-}
-
 void AudioInputController::DoStopCloseAndClearStream(
     base::WaitableEvent* done) {
   DCHECK(message_loop_->BelongsToCurrentThread());
diff --git a/media/audio/audio_input_controller.h b/media/audio/audio_input_controller.h
index 6be4821..6b40459 100644
--- a/media/audio/audio_input_controller.h
+++ b/media/audio/audio_input_controller.h
@@ -16,7 +16,6 @@
 #include "base/timer/timer.h"
 #include "media/audio/audio_io.h"
 #include "media/audio/audio_manager_base.h"
-#include "media/base/user_input_monitor.h"
 
 // An AudioInputController controls an AudioInputStream and records data
 // from this input stream. The two main methods are Record() and Close() and
@@ -73,10 +72,11 @@
 //
 namespace media {
 
+class UserInputMonitor;
+
 class MEDIA_EXPORT AudioInputController
     : public base::RefCountedThreadSafe<AudioInputController>,
-      public AudioInputStream::AudioInputCallback,
-      public UserInputMonitor::KeyStrokeListener {
+      public AudioInputStream::AudioInputCallback {
  public:
   // An event handler that receives events from the AudioInputController. The
   // following methods are all called on the audio thread.
@@ -203,9 +203,6 @@
 
   bool LowLatencyMode() const { return sync_writer_ != NULL; }
 
-  // Impl of KeyStrokeListener.
-  virtual void OnKeyStroke() OVERRIDE;
-
  protected:
   friend class base::RefCountedThreadSafe<AudioInputController>;
 
@@ -287,8 +284,7 @@
 
   UserInputMonitor* user_input_monitor_;
 
-  // True if any key has been pressed after the last OnData call.
-  bool key_pressed_;
+  size_t prev_key_down_count_;
 
   DISALLOW_COPY_AND_ASSIGN(AudioInputController);
 };
diff --git a/media/audio/cras/cras_unified.cc b/media/audio/cras/cras_unified.cc
index c1c3ee9..a774186 100644
--- a/media/audio/cras/cras_unified.cc
+++ b/media/audio/cras/cras_unified.cc
@@ -168,7 +168,6 @@
   if (is_playing_)
     return;
 
-  LOG(ERROR) << "Unified Start";
   // Prepare |audio_format| and |stream_params| for the stream we
   // will create.
   cras_audio_format* audio_format = cras_audio_format_create(
diff --git a/media/audio/cross_process_notification.cc b/media/audio/cross_process_notification.cc
deleted file mode 100644
index 1806f77..0000000
--- a/media/audio/cross_process_notification.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/audio/cross_process_notification.h"
-
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-
-CrossProcessNotification::CrossProcessNotification() {}
-
-CrossProcessNotification::WaitForMultiple::WaitForMultiple(
-    const Notifications* notifications) {
-  Reset(notifications);
-}
-
-int CrossProcessNotification::WaitForMultiple::Wait() {
-  DCHECK(CalledOnValidThread());
-  int ret = WaitMultiple(*notifications_, wait_offset_);
-  wait_offset_ = (ret + 1) % notifications_->size();
-  return ret;
-}
-
-void CrossProcessNotification::WaitForMultiple::Reset(
-    const Notifications* notifications) {
-  DCHECK(CalledOnValidThread());
-  wait_offset_ = 0;
-  notifications_ = notifications;
-  DCHECK(!notifications_->empty());
-}
diff --git a/media/audio/cross_process_notification.h b/media/audio/cross_process_notification.h
deleted file mode 100644
index 16f2fc0..0000000
--- a/media/audio/cross_process_notification.h
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_AUDIO_CROSS_PROCESS_NOTIFICATION_H_
-#define MEDIA_AUDIO_CROSS_PROCESS_NOTIFICATION_H_
-
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/process/process.h"
-#include "base/threading/non_thread_safe.h"
-#include "media/base/media_export.h"
-
-#if defined(OS_WIN)
-#include "base/win/scoped_handle.h"
-#else
-#include "base/file_descriptor_posix.h"
-#include "base/sync_socket.h"
-#endif
-
-// A mechanism to synchronize access to a shared resource between two parties
-// when the usage pattern resembles that of two players playing a game of chess.
-// Each end has an instance of CrossProcessNotification and calls Signal() when
-// it has finished using the shared resource.
-// Before accessing the resource, it must call Wait() in order to know when the
-// other end has called Signal().
-//
-// Here's some pseudo code for how this class can be used:
-//
-// This method is used by both processes as it's a general way to use the
-// shared resource and then grant the privilege to the other process:
-//
-// void WriteToSharedMemory(CrossProcessNotification* notification,
-//                          SharedMemory* mem,
-//                          const char my_char) {
-//   notification->Wait();  // Wait for the other process to yield access.
-//   reinterpret_cast<char*>(mem->memory())[0] = my_char;
-//   notification->Signal();  // Grant the other process access.
-// }
-//
-// Process A:
-//
-// class A {
-//  public:
-//   void Initialize(base::ProcessHandle process_b) {
-//     mem_.CreateNamed("foo", false, 1024);
-//
-//     CrossProcessNotification other;
-//     CHECK(CrossProcessNotification::InitializePair(&notification_, &other));
-//     CrossProcessNotification::IPCHandle handle_1, handle_2;
-//     CHECK(other.ShareToProcess(process_b, &handle_1, &handle_2));
-//     // This could be implemented by using some IPC mechanism
-//     // such as MessageLoop.
-//     SendToProcessB(mem_, handle_1, handle_2);
-//     // Allow process B the first chance to write to the memory:
-//     notification_.Signal();
-//     // Once B is done, we'll write 'A' to the shared memory.
-//     WriteToSharedMemory(&notification_, &mem_, 'A');
-//   }
-//
-//   CrossProcessNotification notification_;
-//   SharedMemory mem_;
-// };
-//
-// Process B:
-//
-// class B {
-//  public:
-//   // Called when we receive the IPC message from A.
-//   void Initialize(SharedMemoryHandle mem,
-//                   CrossProcessNotification::IPCHandle handle_1,
-//                   CrossProcessNotification::IPCHandle handle_2) {
-//     mem_.reset(new SharedMemory(mem, false));
-//     notification_.reset(new CrossProcessNotification(handle_1, handle_2));
-//     WriteToSharedMemory(&notification_, &mem_, 'B');
-//   }
-//
-//   CrossProcessNotification notification_;
-//   scoped_ptr<SharedMemory> mem_;
-// };
-//
-class MEDIA_EXPORT CrossProcessNotification {
- public:
-#if defined(OS_WIN)
-  typedef HANDLE IPCHandle;
-#else
-  typedef base::FileDescriptor IPCHandle;
-#endif
-
-  typedef std::vector<CrossProcessNotification*> Notifications;
-
-  // Default ctor.  Initializes a NULL notification.  User must call
-  // InitializePair() to initialize the instance along with a connected one.
-  CrossProcessNotification();
-
-  // Ctor for the user that does not call InitializePair but instead receives
-  // handles from the one that did.  These handles come from a call to
-  // ShareToProcess.
-  CrossProcessNotification(IPCHandle handle_1, IPCHandle handle_2);
-  ~CrossProcessNotification();
-
-  // Raises a signal that the shared resource now can be accessed by the other
-  // party.
-  // NOTE: Calling Signal() more than once without calling Wait() in between
-  // is not a supported scenario and will result in undefined behavior (and
-  // different depending on platform).
-  void Signal();
-
-  // Waits for the other party to finish using the shared resource.
-  // NOTE: As with Signal(), you must not call Wait() more than once without
-  // calling Signal() in between.
-  void Wait();
-
-  bool IsValid() const;
-
-  // Copies the internal handles to the output parameters, |handle_1| and
-  // |handle_2|.  The operation can fail, so the caller must be prepared to
-  // handle that case.
-  bool ShareToProcess(base::ProcessHandle process, IPCHandle* handle_1,
-                      IPCHandle* handle_2);
-
-  // Initializes a pair of CrossProcessNotification instances.  Note that this
-  // can fail (e.g. due to EMFILE on Linux).
-  static bool InitializePair(CrossProcessNotification* a,
-                             CrossProcessNotification* b);
-
-  // Use an instance of this class when you have to repeatedly wait for multiple
-  // notifications on the same thread.  The class will store information about
-  // which notification was last signaled and try to distribute the signals so
-  // that all notifications get a chance to be processed in times of high load
-  // and a busy one won't starve the others.
-  // TODO(tommi): Support a way to abort the wait.
-  class MEDIA_EXPORT WaitForMultiple :
-      public NON_EXPORTED_BASE(base::NonThreadSafe) {
-   public:
-    // Caller must make sure that the lifetime of the array is greater than
-    // that of the WaitForMultiple instance.
-    explicit WaitForMultiple(const Notifications* notifications);
-
-    // Waits for any of the notifications to be signaled.  Returns the 0 based
-    // index of a signaled notification.
-    int Wait();
-
-    // Call when the array changes.  This should be called on the same thread
-    // as Wait() is called on and the array must never change while a Wait()
-    // is in progress.
-    void Reset(const Notifications* notifications);
-
-   private:
-    const Notifications* notifications_;
-    size_t wait_offset_;
-  };
-
- private:
-  // Only called by the WaitForMultiple class.  See documentation
-  // for WaitForMultiple and comments inside WaitMultiple for details.
-  static int WaitMultiple(const Notifications& notifications,
-                          size_t wait_offset);
-
-#if defined(OS_WIN)
-  base::win::ScopedHandle mine_;
-  base::win::ScopedHandle other_;
-#else
-  typedef base::CancelableSyncSocket SocketClass;
-  SocketClass socket_;
-#endif
-
-  DISALLOW_COPY_AND_ASSIGN(CrossProcessNotification);
-};
-
-#endif  // MEDIA_AUDIO_CROSS_PROCESS_NOTIFICATION_H_
diff --git a/media/audio/cross_process_notification_posix.cc b/media/audio/cross_process_notification_posix.cc
deleted file mode 100644
index d568349..0000000
--- a/media/audio/cross_process_notification_posix.cc
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/audio/cross_process_notification.h"
-
-#include <errno.h>
-#include <sys/poll.h>
-
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/file_descriptor_posix.h"
-
-CrossProcessNotification::~CrossProcessNotification() {}
-
-CrossProcessNotification::CrossProcessNotification(IPCHandle handle_1,
-                                                   IPCHandle handle_2)
-    : socket_(handle_1.fd) {
-  DCHECK_NE(handle_1.fd, -1);
-  DCHECK_EQ(handle_2.fd, -1);
-  DCHECK(IsValid());
-}
-
-void CrossProcessNotification::Signal() {
-  DCHECK(IsValid());
-  char signal = 1;
-  size_t bytes = socket_.Send(&signal, sizeof(signal));
-  DCHECK_EQ(bytes, 1U) << "errno: " << errno;
-}
-
-void CrossProcessNotification::Wait() {
-  DCHECK(IsValid());
-  char signal = 0;
-  size_t bytes = socket_.Receive(&signal, sizeof(signal));
-  DCHECK_EQ(bytes, 1U) << "errno: " << errno;
-  DCHECK_EQ(signal, 1);
-}
-
-bool CrossProcessNotification::IsValid() const {
-  return socket_.handle() != SocketClass::kInvalidHandle;
-}
-
-bool CrossProcessNotification::ShareToProcess(base::ProcessHandle process,
-                                              IPCHandle* handle_1,
-                                              IPCHandle* handle_2) {
-  DCHECK(IsValid());
-  handle_1->fd = socket_.handle();
-  handle_1->auto_close = false;
-  handle_2->fd = -1;
-  return true;
-}
-
-// static
-bool CrossProcessNotification::InitializePair(CrossProcessNotification* a,
-                                              CrossProcessNotification* b) {
-  DCHECK(!a->IsValid());
-  DCHECK(!b->IsValid());
-
-  bool ok = SocketClass::CreatePair(&a->socket_, &b->socket_);
-
-  DLOG_IF(WARNING, !ok) << "failed to create socket: " << errno;
-  DCHECK(!ok || a->IsValid());
-  DCHECK(!ok || b->IsValid());
-  return ok;
-}
-
-// static
-int CrossProcessNotification::WaitMultiple(const Notifications& notifications,
-                                           size_t wait_offset) {
-  DCHECK_LT(wait_offset, notifications.size());
-
-  for (size_t i = 0; i < notifications.size(); ++i) {
-    DCHECK(notifications[i]->IsValid());
-  }
-
-  // Below, we always check the |revents| of the first socket in the array
-  // and return the index of that socket if set.  This can cause sockets
-  // that come later in the array to starve when the first sockets are
-  // very busy.  So to avoid the starving problem, we use the |wait_offset|
-  // variable to split up the array so that the last socket to be signaled
-  // becomes the last socket in the array and all the other sockets will have
-  // priority the next time WaitMultiple is called.
-  scoped_ptr<struct pollfd[]> sockets(new struct pollfd[notifications.size()]);
-  memset(&sockets[0], 0, notifications.size() * sizeof(sockets[0]));
-  size_t index = 0;
-  for (size_t i = wait_offset; i < notifications.size(); ++i) {
-    struct pollfd& fd = sockets[index++];
-    fd.events = POLLIN;
-    fd.fd = notifications[i]->socket_.handle();
-  }
-
-  for (size_t i = 0; i < wait_offset; ++i) {
-    struct pollfd& fd = sockets[index++];
-    fd.events = POLLIN;
-    fd.fd = notifications[i]->socket_.handle();
-  }
-  DCHECK_EQ(index, notifications.size());
-
-  int err = poll(&sockets[0], notifications.size(), -1);
-  if (err != -1) {
-    for (size_t i = 0; i < notifications.size(); ++i) {
-      if (sockets[i].revents) {
-        size_t ret = (i + wait_offset) % notifications.size();
-        DCHECK_EQ(sockets[i].fd, notifications[ret]->socket_.handle());
-        notifications[ret]->Wait();
-        return ret;
-      }
-    }
-  }
-  // Either poll() failed or we failed to find a single socket that was
-  // signaled.  Either way continuing will result in undefined behavior.
-  LOG(FATAL) << "poll() failed: " << errno;
-  return -1;
-}
diff --git a/media/audio/cross_process_notification_unittest.cc b/media/audio/cross_process_notification_unittest.cc
deleted file mode 100644
index a272194..0000000
--- a/media/audio/cross_process_notification_unittest.cc
+++ /dev/null
@@ -1,462 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/compiler_specific.h"
-#include "base/logging.h"
-#include "base/memory/shared_memory.h"
-#include "base/process/kill.h"
-#include "base/stl_util.h"
-#include "base/test/multiprocess_test.h"
-#include "base/threading/platform_thread.h"
-#include "media/audio/cross_process_notification.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/multiprocess_func_list.h"
-
-#include <utility>  // NOLINT
-
-namespace {
-
-// Initializes (ctor) and deletes (dtor) two vectors of pairs of
-// CrossProcessNotification instances.
-class NotificationsOwner {
- public:
-  // Attempts to create up to |number_of_pairs| number of pairs. Call size()
-  // after construction to find out how many pairs were actually created.
-  explicit NotificationsOwner(size_t number_of_pairs) {
-    CreateMultiplePairs(number_of_pairs);
-  }
-  ~NotificationsOwner() {
-    STLDeleteElements(&a_);
-    STLDeleteElements(&b_);
-  }
-
-  size_t size() const {
-    DCHECK_EQ(a_.size(), b_.size());
-    return a_.size();
-  }
-
-  const CrossProcessNotification::Notifications& a() { return a_; }
-  const CrossProcessNotification::Notifications& b() { return b_; }
-
- private:
-  void CreateMultiplePairs(size_t count) {
-    a_.resize(count);
-    b_.resize(count);
-    size_t i = 0;
-    for (; i < count; ++i) {
-      a_[i] = new CrossProcessNotification();
-      b_[i] = new CrossProcessNotification();
-      if (!CrossProcessNotification::InitializePair(a_[i], b_[i])) {
-        LOG(WARNING) << "InitializePair failed at " << i;
-        delete a_[i];
-        delete b_[i];
-        break;
-      }
-    }
-    a_.resize(i);
-    b_.resize(i);
-  }
-
-  CrossProcessNotification::Notifications a_;
-  CrossProcessNotification::Notifications b_;
-};
-
-// A simple thread that we'll run two instances of.  Both threads get a pointer
-// to the same |shared_data| and use a CrossProcessNotification to control when
-// each thread can read/write.
-class SingleNotifierWorker : public base::PlatformThread::Delegate {
- public:
-  SingleNotifierWorker(size_t* shared_data, size_t repeats,
-                       CrossProcessNotification* notifier)
-      : shared_data_(shared_data), repeats_(repeats),
-        notifier_(notifier) {
-  }
-  virtual ~SingleNotifierWorker() {}
-
-  // base::PlatformThread::Delegate:
-  virtual void ThreadMain() OVERRIDE {
-    for (size_t i = 0; i < repeats_; ++i) {
-      notifier_->Wait();
-      ++(*shared_data_);
-      notifier_->Signal();
-    }
-  }
-
- private:
-  size_t* shared_data_;
-  size_t repeats_;
-  CrossProcessNotification* notifier_;
-  DISALLOW_COPY_AND_ASSIGN(SingleNotifierWorker);
-};
-
-// Similar to SingleNotifierWorker, except each instance of this class will
-// have >1 instances of CrossProcessNotification to Wait/Signal and an equal
-// amount of |shared_data| that the notifiers control access to.
-class MultiNotifierWorker : public base::PlatformThread::Delegate {
- public:
-  MultiNotifierWorker(size_t* shared_data, size_t repeats,
-      const CrossProcessNotification::Notifications* notifiers)
-      : shared_data_(shared_data), repeats_(repeats),
-        notifiers_(notifiers) {
-  }
-  virtual ~MultiNotifierWorker() {}
-
-  // base::PlatformThread::Delegate:
-  virtual void ThreadMain() OVERRIDE {
-    CrossProcessNotification::WaitForMultiple waiter(notifiers_);
-    for (size_t i = 0; i < repeats_; ++i) {
-      int signaled = waiter.Wait();
-      ++shared_data_[signaled];
-      (*notifiers_)[signaled]->Signal();
-    }
-  }
-
- private:
-  size_t* shared_data_;
-  size_t repeats_;
-  const CrossProcessNotification::Notifications* notifiers_;
-  DISALLOW_COPY_AND_ASSIGN(MultiNotifierWorker);
-};
-
-// A fixed array of bool flags.  Each flag uses 1 bit.  Use sizeof(FlagArray)
-// to determine how much memory you need.  The number of flags will therefore
-// be sizeof(FlagArray) * 8.
-// We use 'struct' to signify that this structures represents compiler
-// independent structured data.  I.e. you must be able to map this class
-// to a piece of shared memory of size sizeof(FlagArray) and be able to
-// use the class.  No vtables etc.
-// TODO(tommi): Move this to its own header when we start using it for signaling
-// audio devices.  As is, it's just here for perf comparison against the
-// "multiple notifiers" approach.
-struct FlagArray {
- public:
-  FlagArray() : flags_() {}
-
-  bool is_set(size_t index) const {
-    return (flags_[index >> 5] & (1 << (index & 31)));
-  }
-
-  void set(size_t index) {
-    flags_[index >> 5] |= (1U << (static_cast<uint32>(index) & 31));
-  }
-
-  void clear(size_t index) {
-    flags_[index >> 5] &= ~(1U << (static_cast<uint32>(index) & 31));
-  }
-
-  // Returns the number of flags that can be set/checked.
-  size_t size() const { return sizeof(flags_) * 8; }
-
- private:
-  // 256 * 32 = 8192 flags in 1KB.
-  uint32 flags_[256];
-  DISALLOW_COPY_AND_ASSIGN(FlagArray);
-};
-
-class MultiNotifierWorkerFlagArray : public base::PlatformThread::Delegate {
- public:
-  MultiNotifierWorkerFlagArray(size_t count, FlagArray* signals,
-                               size_t* shared_data, size_t repeats,
-                               CrossProcessNotification* notifier)
-      : count_(count), signals_(signals), shared_data_(shared_data),
-        repeats_(repeats), notifier_(notifier) {
-  }
-  virtual ~MultiNotifierWorkerFlagArray() {}
-
-  // base::PlatformThread::Delegate:
-  virtual void ThreadMain() OVERRIDE {
-    for (size_t i = 0; i < repeats_; ++i) {
-      notifier_->Wait();
-      for (size_t s = 0; s < count_; ++s) {
-        if (signals_->is_set(s)) {
-          ++shared_data_[s];
-          // We don't clear the flag here but simply leave it signaled because
-          // we want the other thread to also increment this variable.
-        }
-      }
-      notifier_->Signal();
-    }
-  }
-
- private:
-  size_t count_;
-  FlagArray* signals_;
-  size_t* shared_data_;
-  size_t repeats_;
-  CrossProcessNotification* notifier_;
-  DISALLOW_COPY_AND_ASSIGN(MultiNotifierWorkerFlagArray);
-};
-
-}  // end namespace
-
-TEST(CrossProcessNotification, FlagArray) {
-  FlagArray flags;
-  EXPECT_GT(flags.size(), 1000U);
-  for (size_t i = 0; i < flags.size(); ++i) {
-    EXPECT_FALSE(flags.is_set(i));
-    flags.set(i);
-    EXPECT_TRUE(flags.is_set(i));
-    flags.clear(i);
-    EXPECT_FALSE(flags.is_set(i));
-  }
-}
-
-// Initializes two notifiers, signals the each one and make sure the others
-// wait is satisfied.
-TEST(CrossProcessNotification, Basic) {
-  CrossProcessNotification a, b;
-  ASSERT_TRUE(CrossProcessNotification::InitializePair(&a, &b));
-  EXPECT_TRUE(a.IsValid());
-  EXPECT_TRUE(b.IsValid());
-
-  a.Signal();
-  b.Wait();
-
-  b.Signal();
-  a.Wait();
-}
-
-// Spins two worker threads, each with their own CrossProcessNotification
-// that they use to read and write from a shared memory buffer.
-// Disabled as it trips of the TSAN bot (false positive since TSAN doesn't
-// recognize sockets as being a synchronization primitive).
-TEST(CrossProcessNotification, DISABLED_TwoThreads) {
-  CrossProcessNotification a, b;
-  ASSERT_TRUE(CrossProcessNotification::InitializePair(&a, &b));
-
-  size_t data = 0;
-  const size_t kRepeats = 10000;
-  SingleNotifierWorker worker1(&data, kRepeats, &a);
-  SingleNotifierWorker worker2(&data, kRepeats, &b);
-  base::PlatformThreadHandle thread1, thread2;
-  base::PlatformThread::Create(0, &worker1, &thread1);
-  base::PlatformThread::Create(0, &worker2, &thread2);
-
-  // Start the first thread.  They should ping pong a few times and take turns
-  // incrementing the shared variable and never step on each other's toes.
-  a.Signal();
-
-  base::PlatformThread::Join(thread1);
-  base::PlatformThread::Join(thread2);
-
-  EXPECT_EQ(kRepeats * 2, data);
-}
-
-// Uses a pair of threads to access up to 1000 pieces of synchronized shared
-// data. On regular dev machines, the number of notifiers should be 1000, but on
-// mac and linux bots, the number will be smaller due to the RLIMIT_NOFILE
-// limit. Specifically, linux will have this limit at 1024 which means for this
-// test that the max number of notifiers will be in the range 500-512. On Mac
-// the limit is 256, so |count| will be ~120.  Oh, and raising the limit via
-// setrlimit() won't work.
-// DISABLED since the distribution won't be accurate when run on valgrind.
-TEST(CrossProcessNotification, DISABLED_ThousandNotifiersTwoThreads) {
-  const size_t kCount = 1000;
-  NotificationsOwner pairs(kCount);
-  size_t data[kCount] = {0};
-  // We use a multiple of the count so that the division in the check below
-  // will be nice and round.
-  size_t repeats = pairs.size() * 1;
-
-  MultiNotifierWorker worker_1(&data[0], repeats, &pairs.a());
-  MultiNotifierWorker worker_2(&data[0], repeats, &pairs.b());
-  base::PlatformThreadHandle thread_1, thread_2;
-  base::PlatformThread::Create(0, &worker_1, &thread_1);
-  base::PlatformThread::Create(0, &worker_2, &thread_2);
-
-  for (size_t i = 0; i < pairs.size(); ++i)
-    pairs.a()[i]->Signal();
-
-  base::PlatformThread::Join(thread_1);
-  base::PlatformThread::Join(thread_2);
-
-  size_t expected_total = pairs.size() * 2;
-  size_t total = 0;
-  for (size_t i = 0; i < pairs.size(); ++i) {
-    // The CrossProcessNotification::WaitForMultiple class should have ensured
-    // that all notifiers had the same quality of service.
-    EXPECT_EQ(expected_total / pairs.size(), data[i]);
-    total += data[i];
-  }
-  EXPECT_EQ(expected_total, total);
-}
-
-// Functionally equivalent (as far as the shared data goes) to the
-// ThousandNotifiersTwoThreads test but uses a single pair of notifiers +
-// FlagArray for the 1000 signals. This approach is significantly faster.
-// Disabled as it trips of the TSAN bot - "Possible data race during write of
-// size 4" (the flag array).
-TEST(CrossProcessNotification, DISABLED_TwoNotifiersTwoThreads1000Signals) {
-  CrossProcessNotification a, b;
-  ASSERT_TRUE(CrossProcessNotification::InitializePair(&a, &b));
-
-  const size_t kCount = 1000;
-  FlagArray signals;
-  ASSERT_GE(signals.size(), kCount);
-  size_t data[kCount] = {0};
-
-  // Since this algorithm checks all events each time the notifier is
-  // signaled, |repeat| doesn't mean the same thing here as it does in
-  // ThousandNotifiersTwoThreads.  1 repeat here is the same as kCount
-  // repeats in ThousandNotifiersTwoThreads.
-  size_t repeats = 1;
-  MultiNotifierWorkerFlagArray worker1(kCount, &signals, &data[0], repeats, &a);
-  MultiNotifierWorkerFlagArray worker2(kCount, &signals, &data[0], repeats, &b);
-  base::PlatformThreadHandle thread1, thread2;
-  base::PlatformThread::Create(0, &worker1, &thread1);
-  base::PlatformThread::Create(0, &worker2, &thread2);
-
-  for (size_t i = 0; i < kCount; ++i)
-    signals.set(i);
-  a.Signal();
-
-  base::PlatformThread::Join(thread1);
-  base::PlatformThread::Join(thread2);
-
-  size_t expected_total = kCount * 2;
-  size_t total = 0;
-  for (size_t i = 0; i < kCount; ++i) {
-    // Since for each signal, we process all signaled events, the shared data
-    // variables should all be equal.
-    EXPECT_EQ(expected_total / kCount, data[i]);
-    total += data[i];
-  }
-  EXPECT_EQ(expected_total, total);
-}
-
-// Test the maximum number of notifiers without spinning further wait
-// threads on Windows. This test assumes we can always create 64 pairs and
-// bails if we can't.
-TEST(CrossProcessNotification, MultipleWaits64) {
-  const size_t kCount = 64;
-  NotificationsOwner pairs(kCount);
-  ASSERT_TRUE(pairs.size() == kCount);
-
-  CrossProcessNotification::WaitForMultiple waiter(&pairs.b());
-  for (size_t i = 0; i < kCount; ++i) {
-    pairs.a()[i]->Signal();
-    int index = waiter.Wait();
-    EXPECT_EQ(i, static_cast<size_t>(index));
-  }
-}
-
-// Tests waiting for more notifiers than the OS supports on one thread.
-// The test will create at most 1000 pairs, but on mac/linux bots the actual
-// number will be lower.  See comment about the RLIMIT_NOFILE limit above for
-// more details.
-// DISABLED since the distribution won't be accurate when run on valgrind.
-TEST(CrossProcessNotification, DISABLED_MultipleWaits1000) {
-  // A 1000 notifiers requires 16 threads on Windows, including the current
-  // one, to perform the wait operation.
-  const size_t kCount = 1000;
-  NotificationsOwner pairs(kCount);
-
-  for (size_t i = 0; i < pairs.size(); ++i) {
-    pairs.a()[i]->Signal();
-    // To disable the load distribution algorithm and force the extra worker
-    // thread(s) to catch the signaled event, we define the |waiter| inside
-    // the loop.
-    CrossProcessNotification::WaitForMultiple waiter(&pairs.b());
-    int index = waiter.Wait();
-    EXPECT_EQ(i, static_cast<size_t>(index));
-  }
-}
-
-class CrossProcessNotificationMultiProcessTest : public base::MultiProcessTest {
-};
-
-namespace {
-
-// A very crude IPC mechanism that we use to set up the spawned child process
-// and the parent process.
-struct CrudeIpc {
-  uint8 ready;
-  CrossProcessNotification::IPCHandle handle_1;
-  CrossProcessNotification::IPCHandle handle_2;
-};
-
-#if defined(OS_POSIX)
-const int kPosixChildSharedMem = 30;
-#else
-const char kSharedMemName[] = "CrossProcessNotificationMultiProcessTest";
-#endif
-
-const size_t kSharedMemSize = 1024;
-
-}  // namespace
-
-// The main routine of the child process.  Waits for the parent process
-// to copy handles over to the child and then uses a CrossProcessNotification to
-// wait and signal to the parent process.
-MULTIPROCESS_TEST_MAIN(CrossProcessNotificationChildMain) {
-#if defined(OS_POSIX)
-  base::SharedMemory mem(
-      base::SharedMemoryHandle(kPosixChildSharedMem, true /* auto close */),
-      false);
-#else
-  base::SharedMemory mem;
-  CHECK(mem.CreateNamed(kSharedMemName, true, kSharedMemSize));
-#endif
-
-  CHECK(mem.Map(kSharedMemSize));
-  CrudeIpc* ipc = reinterpret_cast<CrudeIpc*>(mem.memory());
-
-  while (!ipc->ready)
-    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
-
-  CrossProcessNotification notifier(ipc->handle_1, ipc->handle_2);
-  notifier.Wait();
-  notifier.Signal();
-
-  return 0;
-}
-
-// Spawns a new process and hands a CrossProcessNotification instance to the
-// new process.  Once that's done, it waits for the child process to signal
-// it's end and quits.
-TEST_F(CrossProcessNotificationMultiProcessTest, Basic) {
-  CrossProcessNotification a, b;
-  ASSERT_TRUE(CrossProcessNotification::InitializePair(&a, &b));
-  EXPECT_TRUE(a.IsValid());
-  EXPECT_TRUE(b.IsValid());
-
-  base::SharedMemory mem;
-
-#if defined(OS_POSIX)
-  ASSERT_TRUE(mem.CreateAndMapAnonymous(kSharedMemSize));
-#else
-  mem.Delete(kSharedMemName);  // In case a previous run was unsuccessful.
-  ASSERT_TRUE(mem.CreateNamed(kSharedMemName, false, kSharedMemSize));
-  ASSERT_TRUE(mem.Map(kSharedMemSize));
-#endif
-
-  CrudeIpc* ipc = reinterpret_cast<CrudeIpc*>(mem.memory());
-  ipc->ready = false;
-
-#if defined(OS_POSIX)
-  const int kPosixChildSocket = 20;
-  EXPECT_TRUE(b.ShareToProcess(
-        base::kNullProcessHandle, &ipc->handle_1, &ipc->handle_2));
-  base::FileHandleMappingVector fd_mapping_vec;
-  fd_mapping_vec.push_back(std::make_pair(ipc->handle_1.fd, kPosixChildSocket));
-  fd_mapping_vec.push_back(
-      std::make_pair(mem.handle().fd, kPosixChildSharedMem));
-  ipc->handle_1.fd = kPosixChildSocket;
-  base::ProcessHandle process = SpawnChild("CrossProcessNotificationChildMain",
-                                           fd_mapping_vec, false);
-#else
-  base::ProcessHandle process = SpawnChild("CrossProcessNotificationChildMain",
-                                           false);
-  EXPECT_TRUE(b.ShareToProcess(process, &ipc->handle_1, &ipc->handle_2));
-#endif
-
-  ipc->ready = true;
-
-  a.Signal();
-  a.Wait();
-
-  int exit_code = -1;
-  base::WaitForExitCode(process, &exit_code);
-  EXPECT_EQ(0, exit_code);
-}
diff --git a/media/audio/cross_process_notification_win.cc b/media/audio/cross_process_notification_win.cc
deleted file mode 100644
index 53bf0f4..0000000
--- a/media/audio/cross_process_notification_win.cc
+++ /dev/null
@@ -1,270 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/audio/cross_process_notification.h"
-
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/threading/platform_thread.h"
-#include "base/win/scoped_handle.h"
-
-CrossProcessNotification::~CrossProcessNotification() {}
-
-CrossProcessNotification::CrossProcessNotification(IPCHandle handle_1,
-                                                   IPCHandle handle_2)
-    : mine_(handle_1), other_(handle_2) {
-  DCHECK(IsValid());
-}
-
-void CrossProcessNotification::Signal() {
-  DCHECK(IsValid());
-  DCHECK_EQ(::WaitForSingleObject(mine_, 0), static_cast<DWORD>(WAIT_TIMEOUT))
-      << "Are you calling Signal() without calling Wait() first?";
-  BOOL ok = ::SetEvent(mine_);
-  CHECK(ok);
-}
-
-void CrossProcessNotification::Wait() {
-  DCHECK(IsValid());
-  DWORD wait = ::WaitForSingleObject(other_, INFINITE);
-  DCHECK_EQ(wait, WAIT_OBJECT_0);
-  BOOL ok = ::ResetEvent(other_);
-  CHECK(ok);
-}
-
-bool CrossProcessNotification::IsValid() const {
-  return mine_.IsValid() && other_.IsValid();
-}
-
-bool CrossProcessNotification::ShareToProcess(base::ProcessHandle process,
-                                              IPCHandle* handle_1,
-                                              IPCHandle* handle_2) {
-  DCHECK(IsValid());
-  HANDLE our_process = ::GetCurrentProcess();
-  if (!::DuplicateHandle(our_process, mine_, process, handle_1, 0, FALSE,
-                         DUPLICATE_SAME_ACCESS)) {
-    return false;
-  }
-
-  if (!::DuplicateHandle(our_process, other_, process, handle_2, 0, FALSE,
-                         DUPLICATE_SAME_ACCESS)) {
-    // In case we're sharing to ourselves, we can close the handle, but
-    // if the target process is a different process, we do nothing.
-    if (process == our_process)
-      ::CloseHandle(*handle_1);
-    *handle_1 = NULL;
-    return false;
-  }
-
-  return true;
-}
-
-// static
-bool CrossProcessNotification::InitializePair(CrossProcessNotification* a,
-                                              CrossProcessNotification* b) {
-  DCHECK(!a->IsValid());
-  DCHECK(!b->IsValid());
-
-  bool success = false;
-
-  // Create two manually resettable events and give each party a handle
-  // to both events.
-  HANDLE event_a = ::CreateEvent(NULL, TRUE, FALSE, NULL);
-  HANDLE event_b = ::CreateEvent(NULL, TRUE, FALSE, NULL);
-  if (event_a && event_b) {
-    a->mine_.Set(event_a);
-    a->other_.Set(event_b);
-    success = a->ShareToProcess(GetCurrentProcess(), &event_a, &event_b);
-    if (success) {
-      b->mine_.Set(event_b);
-      b->other_.Set(event_a);
-    } else {
-      a->mine_.Close();
-      a->other_.Close();
-    }
-  } else {
-    if (event_a)
-      ::CloseHandle(event_a);
-    if (event_b)
-      ::CloseHandle(event_b);
-  }
-
-  DCHECK(!success || a->IsValid());
-  DCHECK(!success || b->IsValid());
-
-  return success;
-}
-
-namespace {
-class ExtraWaitThread : public base::PlatformThread::Delegate {
- public:
-  ExtraWaitThread(HANDLE stop, HANDLE* events, size_t count,
-                  int* signaled_event)
-      : stop_(stop), events_(events), count_(count),
-        signaled_event_(signaled_event) {
-    *signaled_event_ = -1;
-  }
-  virtual ~ExtraWaitThread() {}
-
-  virtual void ThreadMain() OVERRIDE {
-    // Store the |stop_| event as the first event.
-    HANDLE events[MAXIMUM_WAIT_OBJECTS] = { stop_ };
-    HANDLE next_thread = NULL;
-    DWORD event_count = MAXIMUM_WAIT_OBJECTS;
-    int thread_signaled_event = -1;
-    scoped_ptr<ExtraWaitThread> extra_wait_thread;
-    if (count_ > (MAXIMUM_WAIT_OBJECTS - 1)) {
-      std::copy(&events_[0], &events_[MAXIMUM_WAIT_OBJECTS - 2], &events[1]);
-
-      extra_wait_thread.reset(new ExtraWaitThread(stop_,
-          &events_[MAXIMUM_WAIT_OBJECTS - 2],
-          count_ - (MAXIMUM_WAIT_OBJECTS - 2),
-          &thread_signaled_event));
-      base::PlatformThreadHandle handle;
-      base::PlatformThread::Create(0, extra_wait_thread.get(), &handle);
-      next_thread = handle.platform_handle();
-
-      event_count = MAXIMUM_WAIT_OBJECTS;
-      events[MAXIMUM_WAIT_OBJECTS - 1] = next_thread;
-    } else {
-      std::copy(&events_[0], &events_[count_], &events[1]);
-      event_count = count_ + 1;
-    }
-
-    DWORD wait = ::WaitForMultipleObjects(event_count, &events[0], FALSE,
-                                          INFINITE);
-    if (wait >= WAIT_OBJECT_0 && wait < (WAIT_OBJECT_0 + event_count)) {
-      wait -= WAIT_OBJECT_0;
-      if (wait == 0) {
-        // The stop event was signaled.  Check if it was signaled by a
-        // sub thread.  In case our sub thread had to spin another thread (and
-        // so on), we must wait for ours to exit before we can check the
-        // propagated event offset.
-        if (next_thread) {
-          base::PlatformThread::Join(base::PlatformThreadHandle(next_thread));
-          next_thread = NULL;
-        }
-        if (thread_signaled_event != -1)
-          *signaled_event_ = thread_signaled_event + (MAXIMUM_WAIT_OBJECTS - 2);
-      } else if (events[wait] == next_thread) {
-        NOTREACHED();
-      } else {
-        *signaled_event_ = static_cast<int>(wait);
-        SetEvent(stop_);
-      }
-    } else {
-      NOTREACHED();
-    }
-
-    if (next_thread)
-      base::PlatformThread::Join(base::PlatformThreadHandle(next_thread));
-  }
-
- private:
-  HANDLE stop_;
-  HANDLE* events_;
-  size_t count_;
-  int* signaled_event_;
-  DISALLOW_COPY_AND_ASSIGN(ExtraWaitThread);
-};
-}  // end namespace
-
-// static
-int CrossProcessNotification::WaitMultiple(const Notifications& notifications,
-                                           size_t wait_offset) {
-  DCHECK_LT(wait_offset, notifications.size());
-
-  for (size_t i = 0; i < notifications.size(); ++i) {
-    DCHECK(notifications[i]->IsValid());
-  }
-
-  // TODO(tommi): Should we wait in an alertable state so that we can be
-  // canceled via an APC?
-  scoped_ptr<HANDLE[]> handles(new HANDLE[notifications.size()]);
-
-  // Because of the way WaitForMultipleObjects works, we do a little trick here.
-  // When multiple events are signaled, WaitForMultipleObjects will return the
-  // index of the first signaled item (lowest).  This means that if we always
-  // pass the array the same way to WaitForMultipleObjects, the objects that
-  // come first, have higher priority.  In times of heavy load, this will cause
-  // elements at the back to become DOS-ed.
-  // So, we store the location of the item that was last signaled. Then we split
-  // up the array and move everything higher than the last signaled index to the
-  // front and the rest to the back (meaning that the last signaled item will
-  // become the last element in the list).
-  // Assuming equally busy events, this approach distributes the priority
-  // evenly.
-
-  size_t index = 0;
-  for (size_t i = wait_offset; i < notifications.size(); ++i)
-    handles[index++] = notifications[i]->other_;
-
-  for (size_t i = 0; i < wait_offset; ++i)
-    handles[index++] = notifications[i]->other_;
-  DCHECK_EQ(index, notifications.size());
-
-  DWORD wait = WAIT_FAILED;
-  bool wait_failed = false;
-  if (notifications.size() <= MAXIMUM_WAIT_OBJECTS) {
-    wait = ::WaitForMultipleObjects(notifications.size(), &handles[0], FALSE,
-                                    INFINITE);
-    wait_failed = wait < WAIT_OBJECT_0 ||
-                  wait >= (WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS);
-  } else {
-    // Used to stop the other wait threads when an event has been signaled.
-    base::win::ScopedHandle stop(::CreateEvent(NULL, TRUE, FALSE, NULL));
-
-    // Create the first thread and pass a pointer to all handles >63
-    // to the thread + 'stop'.  Then implement the thread so that it checks
-    // if the number of handles is > 63.  If so, spawns a new thread and
-    // passes >62 handles to that thread and waits for the 62 handles + stop +
-    // next thread.  etc etc.
-
-    // Create a list of threads so that each thread waits on at most 62 events
-    // including one event for when a child thread signals completion and one
-    // event for when all of the threads must be stopped (due to some event
-    // being signaled).
-
-    int thread_signaled_event = -1;
-    ExtraWaitThread wait_thread(stop, &handles[MAXIMUM_WAIT_OBJECTS - 1],
-        notifications.size() - (MAXIMUM_WAIT_OBJECTS - 1),
-        &thread_signaled_event);
-    base::PlatformThreadHandle thread;
-    base::PlatformThread::Create(0, &wait_thread, &thread);
-    HANDLE events[MAXIMUM_WAIT_OBJECTS];
-    std::copy(&handles[0], &handles[MAXIMUM_WAIT_OBJECTS - 1], &events[0]);
-    events[MAXIMUM_WAIT_OBJECTS - 1] = thread.platform_handle();
-    wait = ::WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS, &events[0], FALSE,
-                                    INFINITE);
-    wait_failed = wait < WAIT_OBJECT_0 ||
-                  wait >= (WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS);
-    if (wait == WAIT_OBJECT_0 + (MAXIMUM_WAIT_OBJECTS - 1)) {
-      if (thread_signaled_event < 0) {
-        wait_failed = true;
-        NOTREACHED();
-      } else {
-        wait = WAIT_OBJECT_0 + (MAXIMUM_WAIT_OBJECTS - 2) +
-               thread_signaled_event;
-      }
-    } else {
-      ::SetEvent(stop);
-    }
-    base::PlatformThread::Join(thread);
-  }
-
-  int ret = -1;
-  if (!wait_failed) {
-    // Subtract to be politically correct (WAIT_OBJECT_0 is actually 0).
-    wait -= WAIT_OBJECT_0;
-    BOOL ok = ::ResetEvent(handles[wait]);
-    CHECK(ok);
-    ret = (wait + wait_offset) % notifications.size();
-    DCHECK_EQ(handles[wait], notifications[ret]->other_.Get());
-  } else {
-    NOTREACHED();
-  }
-
-  CHECK_NE(ret, -1);
-  return ret;
-}
diff --git a/media/base/android/audio_decoder_job.cc b/media/base/android/audio_decoder_job.cc
index 5c6842a..59a1a63 100644
--- a/media/base/android/audio_decoder_job.cc
+++ b/media/base/android/audio_decoder_job.cc
@@ -30,19 +30,21 @@
     int channel_count,
     const uint8* extra_data,
     size_t extra_data_size,
-    jobject media_crypto) {
+    jobject media_crypto,
+    const base::Closure& request_data_cb) {
   scoped_ptr<AudioCodecBridge> codec(AudioCodecBridge::Create(audio_codec));
   if (codec && codec->Start(audio_codec, sample_rate, channel_count, extra_data,
                             extra_data_size, true, media_crypto)) {
-    return new AudioDecoderJob(codec.Pass());
+    return new AudioDecoderJob(codec.Pass(), request_data_cb);
   }
   return NULL;
 }
 
 AudioDecoderJob::AudioDecoderJob(
-    scoped_ptr<AudioCodecBridge> audio_codec_bridge)
+    scoped_ptr<AudioCodecBridge> audio_codec_bridge,
+    const base::Closure& request_data_cb)
     : MediaDecoderJob(g_audio_decoder_thread.Pointer()->message_loop_proxy(),
-                      audio_codec_bridge.get()),
+                      audio_codec_bridge.get(), request_data_cb),
       audio_codec_bridge_(audio_codec_bridge.Pass()) {
 }
 
diff --git a/media/base/android/audio_decoder_job.h b/media/base/android/audio_decoder_job.h
index 171dc4e..e74f5b8 100644
--- a/media/base/android/audio_decoder_job.h
+++ b/media/base/android/audio_decoder_job.h
@@ -26,14 +26,17 @@
   // the decoder.
   // |media_crypto| - Handle to a Java object that handles the encryption for
   // the audio data.
+  // |request_data_cb| - Callback used to request more data for the decoder.
   static AudioDecoderJob* Create(
       const AudioCodec audio_codec, int sample_rate, int channel_count,
-      const uint8* extra_data, size_t extra_data_size, jobject media_crypto);
+      const uint8* extra_data, size_t extra_data_size, jobject media_crypto,
+      const base::Closure& request_data_cb);
 
   void SetVolume(double volume);
 
  private:
-  AudioDecoderJob(scoped_ptr<AudioCodecBridge> audio_decoder_bridge);
+  AudioDecoderJob(scoped_ptr<AudioCodecBridge> audio_decoder_bridge,
+                  const base::Closure& request_data_cb);
 
   // MediaDecoderJob implementation.
   virtual void ReleaseOutputBuffer(
diff --git a/media/base/android/demuxer_stream_player_params.cc b/media/base/android/demuxer_stream_player_params.cc
index 827be11..d5021a9 100644
--- a/media/base/android/demuxer_stream_player_params.cc
+++ b/media/base/android/demuxer_stream_player_params.cc
@@ -6,8 +6,7 @@
 
 namespace media {
 
-MediaPlayerHostMsg_DemuxerReady_Params::
-    MediaPlayerHostMsg_DemuxerReady_Params()
+DemuxerConfigs::DemuxerConfigs()
     : audio_codec(kUnknownAudioCodec),
       audio_channels(0),
       audio_sampling_rate(0),
@@ -16,18 +15,14 @@
       is_video_encrypted(false),
       duration_ms(0) {}
 
-MediaPlayerHostMsg_DemuxerReady_Params::
-    ~MediaPlayerHostMsg_DemuxerReady_Params() {}
+DemuxerConfigs::~DemuxerConfigs() {}
 
 AccessUnit::AccessUnit() : end_of_stream(false) {}
 
 AccessUnit::~AccessUnit() {}
 
-MediaPlayerHostMsg_ReadFromDemuxerAck_Params::
-    MediaPlayerHostMsg_ReadFromDemuxerAck_Params()
-    : type(DemuxerStream::UNKNOWN) {}
+DemuxerData::DemuxerData() : type(DemuxerStream::UNKNOWN) {}
 
-MediaPlayerHostMsg_ReadFromDemuxerAck_Params::
-    ~MediaPlayerHostMsg_ReadFromDemuxerAck_Params() {}
+DemuxerData::~DemuxerData() {}
 
 }  // namespace media
diff --git a/media/base/android/demuxer_stream_player_params.h b/media/base/android/demuxer_stream_player_params.h
index a9fb052..92ef74f 100644
--- a/media/base/android/demuxer_stream_player_params.h
+++ b/media/base/android/demuxer_stream_player_params.h
@@ -17,9 +17,9 @@
 
 namespace media {
 
-struct MEDIA_EXPORT MediaPlayerHostMsg_DemuxerReady_Params {
-  MediaPlayerHostMsg_DemuxerReady_Params();
-  ~MediaPlayerHostMsg_DemuxerReady_Params();
+struct MEDIA_EXPORT DemuxerConfigs {
+  DemuxerConfigs();
+  ~DemuxerConfigs();
 
   AudioCodec audio_codec;
   int audio_channels;
@@ -50,9 +50,9 @@
   std::vector<media::SubsampleEntry> subsamples;
 };
 
-struct MEDIA_EXPORT MediaPlayerHostMsg_ReadFromDemuxerAck_Params {
-  MediaPlayerHostMsg_ReadFromDemuxerAck_Params();
-  ~MediaPlayerHostMsg_ReadFromDemuxerAck_Params();
+struct MEDIA_EXPORT DemuxerData {
+  DemuxerData();
+  ~DemuxerData();
 
   DemuxerStream::Type type;
   std::vector<AccessUnit> access_units;
diff --git a/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java b/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
index 5f824fc..d588b00 100644
--- a/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
@@ -39,7 +39,8 @@
     private String mSessionId;
     private MediaCrypto mMediaCrypto;
     private String mMimeType;
-    private Handler mhandler;
+    private Handler mHandler;
+    private byte[] mPendingInitData;
 
     private static UUID getUUIDFromBytes(byte[] data) {
         if (data.length != 16) {
@@ -61,30 +62,66 @@
             mSchemeUUID = schemeUUID;
             mMediaDrm = new MediaDrm(schemeUUID);
             mNativeMediaDrmBridge = nativeMediaDrmBridge;
+            mMediaDrm.setPropertyString("privacyMode", "enable");
             mMediaDrm.setOnEventListener(new MediaDrmListener());
-            mSessionId = openSession();
-            mhandler = new Handler();
+            mHandler = new Handler();
         } catch (android.media.UnsupportedSchemeException e) {
             Log.e(TAG, "Unsupported DRM scheme " + e.toString());
         }
     }
 
     /**
+     * Create a MediaCrypto object.
+     *
+     * @return if a MediaCrypto object is successfully created.
+     */
+    private boolean createMediaCrypto() {
+        assert(mSessionId != null);
+        assert(mMediaCrypto == null);
+        try {
+            final byte[] session = mSessionId.getBytes("UTF-8");
+            if (MediaCrypto.isCryptoSchemeSupported(mSchemeUUID)) {
+                mMediaCrypto = new MediaCrypto(mSchemeUUID, session);
+            }
+        } catch (android.media.MediaCryptoException e) {
+            Log.e(TAG, "Cannot create MediaCrypto " + e.toString());
+            return false;
+        } catch (java.io.UnsupportedEncodingException e) {
+            Log.e(TAG, "Cannot create MediaCrypto " + e.toString());
+            return false;
+        }
+
+        assert(mMediaCrypto != null);
+        nativeOnMediaCryptoReady(mNativeMediaDrmBridge);
+        return true;
+    }
+
+    /**
      * Open a new session and return the sessionId.
      *
-     * @return ID of the session.
+     * @return false if unexpected error happens. Return true if a new session
+     * is successfully opened, or if provisioning is required to open a session.
      */
-    private String openSession() {
-        String session = null;
+    private boolean openSession() {
+        assert(mSessionId == null);
+
+        if (mMediaDrm == null) {
+            return false;
+        }
+
         try {
             final byte[] sessionId = mMediaDrm.openSession();
-            session = new String(sessionId, "UTF-8");
+            mSessionId = new String(sessionId, "UTF-8");
         } catch (android.media.NotProvisionedException e) {
             Log.e(TAG, "Cannot open a new session " + e.toString());
-        } catch (java.io.UnsupportedEncodingException e) {
+            return true;
+        } catch (Exception e) {
             Log.e(TAG, "Cannot open a new session " + e.toString());
+            return false;
         }
-        return session;
+
+        assert(mSessionId != null);
+        return createMediaCrypto();
     }
 
     /**
@@ -103,25 +140,10 @@
     }
 
     /**
-     * Create a new MediaCrypto object from the session Id.
-     *
-     * @param sessionId Crypto session Id.
+     * Return the MediaCrypto object if available.
      */
     @CalledByNative
     private MediaCrypto getMediaCrypto() {
-        if (mMediaCrypto != null) {
-            return mMediaCrypto;
-        }
-        try {
-            final byte[] session = mSessionId.getBytes("UTF-8");
-            if (MediaCrypto.isCryptoSchemeSupported(mSchemeUUID)) {
-                mMediaCrypto = new MediaCrypto(mSchemeUUID, session);
-            }
-        } catch (android.media.MediaCryptoException e) {
-            Log.e(TAG, "Cannot create MediaCrypto " + e.toString());
-        } catch (java.io.UnsupportedEncodingException e) {
-            Log.e(TAG, "Cannot create MediaCrypto " + e.toString());
-        }
         return mMediaCrypto;
     }
 
@@ -142,6 +164,7 @@
             }
         }
         mMediaDrm.release();
+        mMediaDrm = null;
     }
 
     /**
@@ -153,16 +176,45 @@
      */
     @CalledByNative
     private void generateKeyRequest(byte[] initData, String mime) {
-        if (mSessionId == null) {
+        Log.d(TAG, "generateKeyRequest().");
+
+        if (mMimeType == null) {
+            mMimeType = mime;
+        } else if (!mMimeType.equals(mime)) {
+            onKeyError();
             return;
         }
+
+        if (mSessionId == null) {
+            if (!openSession()) {
+                onKeyError();
+                return;
+            }
+
+            // NotProvisionedException happened during openSession().
+            if (mSessionId == null) {
+                if (mPendingInitData != null) {
+                    Log.e(TAG, "generateKeyRequest is called when another call is pending.");
+                    onKeyError();
+                    return;
+                }
+
+                // We assume MediaDrm.EVENT_PROVISION_REQUIRED is always fired if
+                // NotProvisionedException is throwed in openSession().
+                // generateKeyRequest() will be resumed after provisioning is finished.
+                // TODO(xhwang): Double check if this assumption is true. Otherwise we need
+                // to handle the exception in openSession more carefully.
+                mPendingInitData = initData;
+                return;
+            }
+        }
+
         try {
             final byte[] session = mSessionId.getBytes("UTF-8");
-            mMimeType = mime;
             HashMap<String, String> optionalParameters = new HashMap<String, String>();
             final MediaDrm.KeyRequest request = mMediaDrm.getKeyRequest(
                     session, initData, mime, MediaDrm.KEY_TYPE_STREAMING, optionalParameters);
-            mhandler.post(new Runnable(){
+            mHandler.post(new Runnable(){
                 public void run() {
                     nativeOnKeyMessage(mNativeMediaDrmBridge, mSessionId,
                             request.getData(), request.getDefaultUrl());
@@ -170,7 +222,10 @@
             });
             return;
         } catch (android.media.NotProvisionedException e) {
+            // MediaDrm.EVENT_PROVISION_REQUIRED is also fired in this case.
+            // Provisioning is handled in the handler of that event.
             Log.e(TAG, "Cannot get key request " + e.toString());
+            return;
         } catch (java.io.UnsupportedEncodingException e) {
             Log.e(TAG, "Cannot get key request " + e.toString());
         }
@@ -208,8 +263,16 @@
         }
         try {
             final byte[] session = sessionId.getBytes("UTF-8");
-            mMediaDrm.provideKeyResponse(session, key);
-            mhandler.post(new Runnable() {
+            try {
+                mMediaDrm.provideKeyResponse(session, key);
+            } catch (java.lang.IllegalStateException e) {
+                // This is not really an exception. Some error code are incorrectly
+                // reported as an exception.
+                // TODO(qinmin): remove this exception catch when b/10495563 is fixed.
+                Log.e(TAG, "Exception intentionally caught when calling provideKeyResponse() "
+                        + e.toString());
+            }
+            mHandler.post(new Runnable() {
                 public void run() {
                     nativeOnKeyAdded(mNativeMediaDrmBridge, mSessionId);
                 }
@@ -226,21 +289,45 @@
     }
 
     /**
+     * Return the security level of this DRM object.
+     */
+    @CalledByNative
+    private String getSecurityLevel() {
+        return mMediaDrm.getPropertyString("securityLevel");
+    }
+
+    /**
      * Called when the provision response is received.
      *
      * @param response Response data from the provision server.
      */
     private void onProvisionResponse(byte[] response) {
+        Log.d(TAG, "provide key response.");
+
+        if (response == null || response.length == 0) {
+            Log.e(TAG, "Invalid provision response.");
+            onKeyError();
+            return;
+        }
+
         try {
             mMediaDrm.provideProvisionResponse(response);
         } catch (android.media.DeniedByServerException e) {
-            Log.e(TAG, "failed to provide key response " + e.toString());
+            Log.e(TAG, "failed to provide provision response " + e.toString());
+            onKeyError();
+            return;
+        }
+
+        if (mPendingInitData != null) {
+            byte[] initData = mPendingInitData;
+            mPendingInitData = null;
+            generateKeyRequest(initData, mMimeType);
         }
     }
 
     private void onKeyError() {
         // TODO(qinmin): pass the error code to native.
-        mhandler.post(new Runnable() {
+        mHandler.post(new Runnable() {
             public void run() {
                 nativeOnKeyError(mNativeMediaDrmBridge, mSessionId);
             }
@@ -253,6 +340,7 @@
                 byte[] data) {
             switch(event) {
                 case MediaDrm.EVENT_PROVISION_REQUIRED:
+                    Log.d(TAG, "MediaDrm.EVENT_PROVISION_REQUIRED.");
                     MediaDrm.ProvisionRequest request = mMediaDrm.getProvisionRequest();
                     PostRequestTask postTask = new PostRequestTask(request.getData());
                     postTask.execute(request.getDefaultUrl());
@@ -329,6 +417,8 @@
         }
     }
 
+    private native void nativeOnMediaCryptoReady(int nativeMediaDrmBridge);
+
     private native void nativeOnKeyMessage(int nativeMediaDrmBridge, String sessionId,
                                            byte[] message, String destinationUrl);
 
diff --git a/media/base/android/java/src/org/chromium/media/VideoCapture.java b/media/base/android/java/src/org/chromium/media/VideoCapture.java
index 5099c0a..df9eb4d 100644
--- a/media/base/android/java/src/org/chromium/media/VideoCapture.java
+++ b/media/base/android/java/src/org/chromium/media/VideoCapture.java
@@ -119,7 +119,7 @@
 
             // Calculate fps.
             List<int[]> listFpsRange = parameters.getSupportedPreviewFpsRange();
-            if (listFpsRange.size() == 0) {
+            if (listFpsRange == null || listFpsRange.size() == 0) {
                 Log.e(TAG, "allocate: no fps range found");
                 return false;
             }
diff --git a/media/base/android/media_decoder_job.cc b/media/base/android/media_decoder_job.cc
index c29eaf9..0a2e147 100644
--- a/media/base/android/media_decoder_job.cc
+++ b/media/base/android/media_decoder_job.cc
@@ -5,6 +5,7 @@
 #include "media/base/android/media_decoder_job.h"
 
 #include "base/bind.h"
+#include "base/callback_helpers.h"
 #include "base/message_loop/message_loop.h"
 #include "media/base/android/media_codec_bridge.h"
 #include "media/base/bind_to_loop.h"
@@ -18,31 +19,117 @@
 
 MediaDecoderJob::MediaDecoderJob(
     const scoped_refptr<base::MessageLoopProxy>& decoder_loop,
-    MediaCodecBridge* media_codec_bridge)
+    MediaCodecBridge* media_codec_bridge,
+    const base::Closure& request_data_cb)
     : ui_loop_(base::MessageLoopProxy::current()),
       decoder_loop_(decoder_loop),
       media_codec_bridge_(media_codec_bridge),
       needs_flush_(false),
       input_eos_encountered_(false),
       weak_this_(this),
-      is_decoding_(false) {
+      request_data_cb_(request_data_cb),
+      access_unit_index_(0),
+      stop_decode_pending_(false),
+      destroy_pending_(false) {
 }
 
 MediaDecoderJob::~MediaDecoderJob() {}
 
-void MediaDecoderJob::Decode(
-    const AccessUnit& unit,
+void MediaDecoderJob::OnDataReceived(const DemuxerData& data) {
+  DCHECK(ui_loop_->BelongsToCurrentThread());
+  DCHECK(!on_data_received_cb_.is_null());
+
+  base::Closure done_cb = base::ResetAndReturn(&on_data_received_cb_);
+
+  if (stop_decode_pending_) {
+    OnDecodeCompleted(DECODE_STOPPED, kNoTimestamp(), 0);
+    return;
+  }
+
+  access_unit_index_ = 0;
+  received_data_ = data;
+  done_cb.Run();
+}
+
+bool MediaDecoderJob::HasData() const {
+  DCHECK(ui_loop_->BelongsToCurrentThread());
+  return access_unit_index_ < received_data_.access_units.size();
+}
+
+void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) {
+  DCHECK(ui_loop_->BelongsToCurrentThread());
+  DCHECK(on_data_received_cb_.is_null());
+  DCHECK(decode_cb_.is_null());
+
+  if (HasData()) {
+    ui_loop_->PostTask(FROM_HERE, prefetch_cb);
+    return;
+  }
+
+  RequestData(prefetch_cb);
+}
+
+bool MediaDecoderJob::Decode(
     const base::TimeTicks& start_time_ticks,
     const base::TimeDelta& start_presentation_timestamp,
     const MediaDecoderJob::DecoderCallback& callback) {
-  DCHECK(!is_decoding_);
+  DCHECK(decode_cb_.is_null());
+  DCHECK(on_data_received_cb_.is_null());
   DCHECK(ui_loop_->BelongsToCurrentThread());
-  is_decoding_ = true;
-  decoder_loop_->PostTask(FROM_HERE, base::Bind(
-      &MediaDecoderJob::DecodeInternal, base::Unretained(this), unit,
-      start_time_ticks, start_presentation_timestamp, needs_flush_,
-      media::BindToLoop(ui_loop_, callback)));
-  needs_flush_ = false;
+
+  decode_cb_ = callback;
+
+  if (!HasData()) {
+    RequestData(base::Bind(&MediaDecoderJob::DecodeNextAccessUnit,
+                           base::Unretained(this),
+                           start_time_ticks,
+                           start_presentation_timestamp));
+    return true;
+  }
+
+  if (DemuxerStream::kConfigChanged ==
+      received_data_.access_units[access_unit_index_].status) {
+    // Clear received data because we need to handle a config change.
+    decode_cb_.Reset();
+    received_data_ = DemuxerData();
+    access_unit_index_ = 0;
+    return false;
+  }
+
+  DecodeNextAccessUnit(start_time_ticks, start_presentation_timestamp);
+  return true;
+}
+
+void MediaDecoderJob::StopDecode() {
+  DCHECK(ui_loop_->BelongsToCurrentThread());
+  DCHECK(is_decoding());
+  stop_decode_pending_ = true;
+}
+
+void MediaDecoderJob::Flush() {
+  DCHECK(decode_cb_.is_null());
+
+  // Do nothing, flush when the next Decode() happens.
+  needs_flush_ = true;
+
+  received_data_ = DemuxerData();
+  access_unit_index_ = 0;
+  on_data_received_cb_.Reset();
+}
+
+void MediaDecoderJob::Release() {
+  DCHECK(ui_loop_->BelongsToCurrentThread());
+
+  destroy_pending_ = is_decoding();
+
+  request_data_cb_.Reset();
+  on_data_received_cb_.Reset();
+  decode_cb_.Reset();
+
+  if (destroy_pending_)
+    return;
+
+  delete this;
 }
 
 MediaDecoderJob::DecodeStatus MediaDecoderJob::QueueInputBuffer(
@@ -80,6 +167,32 @@
   return DECODE_SUCCEEDED;
 }
 
+void MediaDecoderJob::RequestData(const base::Closure& done_cb) {
+  DCHECK(ui_loop_->BelongsToCurrentThread());
+  DCHECK(on_data_received_cb_.is_null());
+
+  received_data_ = DemuxerData();
+  access_unit_index_ = 0;
+  on_data_received_cb_ = done_cb;
+
+  request_data_cb_.Run();
+}
+
+void MediaDecoderJob::DecodeNextAccessUnit(
+    const base::TimeTicks& start_time_ticks,
+    const base::TimeDelta& start_presentation_timestamp) {
+  DCHECK(ui_loop_->BelongsToCurrentThread());
+  DCHECK(!decode_cb_.is_null());
+
+  decoder_loop_->PostTask(FROM_HERE, base::Bind(
+      &MediaDecoderJob::DecodeInternal, base::Unretained(this),
+      received_data_.access_units[access_unit_index_],
+      start_time_ticks, start_presentation_timestamp, needs_flush_,
+      media::BindToLoop(ui_loop_, base::Bind(
+          &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this)))));
+  needs_flush_ = false;
+}
+
 void MediaDecoderJob::DecodeInternal(
     const AccessUnit& unit,
     const base::TimeTicks& start_time_ticks,
@@ -166,36 +279,27 @@
   callback.Run(decode_status, start_presentation_timestamp, 0);
 }
 
-void MediaDecoderJob::OnDecodeCompleted() {
+void MediaDecoderJob::OnDecodeCompleted(
+    DecodeStatus status, const base::TimeDelta& presentation_timestamp,
+    size_t audio_output_bytes) {
   DCHECK(ui_loop_->BelongsToCurrentThread());
-  is_decoding_ = false;
-}
 
-void MediaDecoderJob::Flush() {
-  // Do nothing, flush when the next Decode() happens.
-  needs_flush_ = true;
-}
-
-void MediaDecoderJob::Release() {
-  // If |decoding_| is false, there is nothing running on the decoder thread.
-  // So it is safe to delete the MediaDecoderJob on the UI thread. However,
-  // if we post a task to the decoder thread to delete object, then we cannot
-  // immediately pass the surface to a new MediaDecoderJob instance because
-  // the java surface is still owned by the old object. New decoder creation
-  // will be blocked on the UI thread until the previous decoder gets deleted.
-  // This introduces extra latency during config changes, and makes the logic in
-  // MediaSourcePlayer more complicated.
-  //
-  // TODO(qinmin): Figure out the logic to passing the surface to a new
-  // MediaDecoderJob instance after the previous one gets deleted on the decoder
-  // thread.
-  if (is_decoding_ && !decoder_loop_->BelongsToCurrentThread()) {
-    DCHECK(ui_loop_->BelongsToCurrentThread());
-    decoder_loop_->DeleteSoon(FROM_HERE, this);
+  if (destroy_pending_) {
+    delete this;
     return;
   }
 
-  delete this;
+  DCHECK(!decode_cb_.is_null());
+
+  if (status != MediaDecoderJob::DECODE_FAILED &&
+      status != MediaDecoderJob::DECODE_TRY_ENQUEUE_INPUT_AGAIN_LATER &&
+      status != MediaDecoderJob::DECODE_INPUT_END_OF_STREAM) {
+    access_unit_index_++;
+  }
+
+  stop_decode_pending_ = false;
+  base::ResetAndReturn(&decode_cb_).Run(status, presentation_timestamp,
+                                        audio_output_bytes);
 }
 
 }  // namespace media
diff --git a/media/base/android/media_decoder_job.h b/media/base/android/media_decoder_job.h
index 4a3bd87..00e20cf 100644
--- a/media/base/android/media_decoder_job.h
+++ b/media/base/android/media_decoder_job.h
@@ -30,42 +30,66 @@
     DECODE_INPUT_END_OF_STREAM,
     DECODE_OUTPUT_END_OF_STREAM,
     DECODE_FAILED,
+    DECODE_STOPPED
   };
 
   struct Deleter {
     inline void operator()(MediaDecoderJob* ptr) const { ptr->Release(); }
   };
 
-  virtual ~MediaDecoderJob();
-
   // Callback when a decoder job finishes its work. Args: whether decode
   // finished successfully, presentation time, audio output bytes.
   typedef base::Callback<void(DecodeStatus, const base::TimeDelta&,
                               size_t)> DecoderCallback;
 
+  virtual ~MediaDecoderJob();
+
+  // Called by MediaSourcePlayer when more data for this object has arrived.
+  void OnDataReceived(const DemuxerData& data);
+
+  // Returns true if this object has data to decode.
+  bool HasData() const;
+
+  // Prefetch so we know the decoder job has data when we call Decode().
+  // |prefetch_cb| - Run when prefetching has completed.
+  void Prefetch(const base::Closure& prefetch_cb);
+
   // Called by MediaSourcePlayer to decode some data.
-  void Decode(const AccessUnit& unit,
-              const base::TimeTicks& start_time_ticks,
+  // |callback| - Run when decode operation has completed.
+  //
+  // Returns true if the next decode was started and |callback| will be
+  // called when the decode operation is complete.
+  // Returns false if a config change is needed. |callback| is ignored
+  // and will not be called.
+  bool Decode(const base::TimeTicks& start_time_ticks,
               const base::TimeDelta& start_presentation_timestamp,
-              const MediaDecoderJob::DecoderCallback& callback);
+              const DecoderCallback& callback);
+
+  // Called to stop the last Decode() early.
+  // If the decoder is in the process of decoding the next frame, then
+  // this method will just allow the decode to complete as normal. If
+  // this object is waiting for a data request to complete, then this method
+  // will wait for the data to arrive and then call the |callback|
+  // passed to Decode() with a status of DECODE_STOPPED. This ensures that
+  // the |callback| passed to Decode() is always called and the status
+  // reflects whether data was actually decoded or the decode terminated early.
+  void StopDecode();
 
   // Flush the decoder.
   void Flush();
 
-  // Called on the UI thread to indicate that one decode cycle has completed.
-  void OnDecodeCompleted();
-
-  bool is_decoding() const { return is_decoding_; }
+  bool is_decoding() const { return !decode_cb_.is_null(); }
 
  protected:
   MediaDecoderJob(const scoped_refptr<base::MessageLoopProxy>& decoder_loop,
-                  MediaCodecBridge* media_codec_bridge);
+                  MediaCodecBridge* media_codec_bridge,
+                  const base::Closure& request_data_cb);
 
   // Release the output buffer and render it.
   virtual void ReleaseOutputBuffer(
       int outputBufferIndex, size_t size,
       const base::TimeDelta& presentation_timestamp,
-      const MediaDecoderJob::DecoderCallback& callback,
+      const DecoderCallback& callback,
       DecodeStatus status) = 0;
 
   // Returns true if the "time to render" needs to be computed for frames in
@@ -78,6 +102,15 @@
 
   DecodeStatus QueueInputBuffer(const AccessUnit& unit);
 
+  // Initiates a request for more data.
+  // |done_cb| is called when more data is available in |received_data_|.
+  void RequestData(const base::Closure& done_cb);
+
+  // Posts a task to start decoding the next access unit in |received_data_|.
+  void DecodeNextAccessUnit(
+      const base::TimeTicks& start_time_ticks,
+      const base::TimeDelta& start_presentation_timestamp);
+
   // Helper function to decoder data on |thread_|. |unit| contains all the data
   // to be decoded. |start_time_ticks| and |start_presentation_timestamp|
   // represent the system time and the presentation timestamp when the first
@@ -88,7 +121,12 @@
                       const base::TimeTicks& start_time_ticks,
                       const base::TimeDelta& start_presentation_timestamp,
                       bool needs_flush,
-                      const MediaDecoderJob::DecoderCallback& callback);
+                      const DecoderCallback& callback);
+
+  // Called on the UI thread to indicate that one decode cycle has completed.
+  void OnDecodeCompleted(DecodeStatus status,
+                         const base::TimeDelta& presentation_timestamp,
+                         size_t audio_output_bytes);
 
   // The UI message loop where callbacks should be dispatched.
   scoped_refptr<base::MessageLoopProxy> ui_loop_;
@@ -110,8 +148,27 @@
   // the decoder thread.
   base::WeakPtrFactory<MediaDecoderJob> weak_this_;
 
-  // Whether the decoder is actively decoding data.
-  bool is_decoding_;
+  // Callback used to request more data.
+  base::Closure request_data_cb_;
+
+  // Callback to run when new data has been received.
+  base::Closure on_data_received_cb_;
+
+  // Callback to run when the current Decode() operation completes.
+  DecoderCallback decode_cb_;
+
+  // The current access unit being processed.
+  size_t access_unit_index_;
+
+  // Data received over IPC from last RequestData() operation.
+  DemuxerData received_data_;
+
+  bool stop_decode_pending_;
+
+  // Indicates that this object should be destroyed once the current
+  // Decode() has completed. This gets set when Release() gets called
+  // while there is a decode in progress.
+  bool destroy_pending_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(MediaDecoderJob);
 };
diff --git a/media/base/android/media_drm_bridge.cc b/media/base/android/media_drm_bridge.cc
index 9ac62ca..e0e4ec5 100644
--- a/media/base/android/media_drm_bridge.cc
+++ b/media/base/android/media_drm_bridge.cc
@@ -7,7 +7,10 @@
 #include "base/android/build_info.h"
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
+#include "base/callback_helpers.h"
+#include "base/location.h"
 #include "base/logging.h"
+#include "base/message_loop/message_loop_proxy.h"
 #include "jni/MediaDrmBridge_jni.h"
 #include "media/base/android/media_player_manager.h"
 
@@ -205,11 +208,6 @@
       env, j_media_drm_.obj(), j_session_id.obj(), j_key_data.obj());
 }
 
-ScopedJavaLocalRef<jobject> MediaDrmBridge::GetMediaCrypto() {
-  JNIEnv* env = AttachCurrentThread();
-  return Java_MediaDrmBridge_getMediaCrypto(env, j_media_drm_.obj());
-}
-
 void MediaDrmBridge::CancelKeyRequest(const std::string& session_id) {
   JNIEnv* env = AttachCurrentThread();
   ScopedJavaLocalRef<jstring> j_session_id =
@@ -218,6 +216,28 @@
       env, j_media_drm_.obj(), j_session_id.obj());
 }
 
+void MediaDrmBridge::SetMediaCryptoReadyCB(const base::Closure& closure) {
+  if (closure.is_null()) {
+    media_crypto_ready_cb_.Reset();
+    return;
+  }
+
+  DCHECK(media_crypto_ready_cb_.is_null());
+
+  if (!GetMediaCrypto().is_null()) {
+    base::MessageLoopProxy::current()->PostTask(FROM_HERE, closure);
+    return;
+  }
+
+  media_crypto_ready_cb_ = closure;
+}
+
+void MediaDrmBridge::OnMediaCryptoReady(JNIEnv* env, jobject) {
+  DCHECK(!GetMediaCrypto().is_null());
+  if (!media_crypto_ready_cb_.is_null())
+    base::ResetAndReturn(&media_crypto_ready_cb_).Run();
+}
+
 void MediaDrmBridge::OnKeyMessage(JNIEnv* env,
                                   jobject j_media_drm,
                                   jstring j_session_id,
@@ -237,8 +257,32 @@
 }
 
 void MediaDrmBridge::OnKeyError(JNIEnv* env, jobject, jstring j_session_id) {
+  // |j_session_id| can be NULL, in which case we'll return an empty string.
   std::string session_id = ConvertJavaStringToUTF8(env, j_session_id);
   manager_->OnKeyError(media_keys_id_, session_id, MediaKeys::kUnknownError, 0);
 }
 
+ScopedJavaLocalRef<jobject> MediaDrmBridge::GetMediaCrypto() {
+  JNIEnv* env = AttachCurrentThread();
+  return Java_MediaDrmBridge_getMediaCrypto(env, j_media_drm_.obj());
+}
+
+MediaDrmBridge::SecurityLevel MediaDrmBridge::GetSecurityLevel() {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> j_security_level =
+      Java_MediaDrmBridge_getSecurityLevel(env, j_media_drm_.obj());
+  std::string security_level =
+      ConvertJavaStringToUTF8(env, j_security_level.obj());
+  if (0 == security_level.compare("L1"))
+    return SECURITY_LEVEL_1;
+  if (0 == security_level.compare("L3"))
+    return SECURITY_LEVEL_3;
+  DCHECK(security_level.empty());
+  return SECURITY_LEVEL_NONE;
+}
+
+bool MediaDrmBridge::IsProtectedSurfaceRequired() {
+  return MediaDrmBridge::SECURITY_LEVEL_1 == GetSecurityLevel();
+}
+
 }  // namespace media
diff --git a/media/base/android/media_drm_bridge.h b/media/base/android/media_drm_bridge.h
index 26e6437..821e9e8 100644
--- a/media/base/android/media_drm_bridge.h
+++ b/media/base/android/media_drm_bridge.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/android/scoped_java_ref.h"
+#include "base/callback.h"
 #include "media/base/media_export.h"
 #include "media/base/media_keys.h"
 
@@ -21,6 +22,12 @@
 // TODO(qinmin): implement all the functions in this class.
 class MEDIA_EXPORT MediaDrmBridge : public MediaKeys {
  public:
+  enum SecurityLevel {
+    SECURITY_LEVEL_NONE = 0,
+    SECURITY_LEVEL_1 = 1,
+    SECURITY_LEVEL_3 = 3,
+  };
+
   virtual ~MediaDrmBridge();
 
   // Returns a MediaDrmBridge instance if |scheme_uuid| is supported, or a NULL
@@ -43,6 +50,17 @@
                       const std::string& session_id) OVERRIDE;
   virtual void CancelKeyRequest(const std::string& session_id) OVERRIDE;
 
+  // Returns a MediaCrypto object if it's already created. Returns a null object
+  // otherwise.
+  base::android::ScopedJavaLocalRef<jobject> GetMediaCrypto();
+
+  // Sets callback which will be called when MediaCrypto is ready.
+  // If |closure| is null, previously set callback will be cleared.
+  void SetMediaCryptoReadyCB(const base::Closure& closure);
+
+  // Called after a MediaCrypto object is created.
+  void OnMediaCryptoReady(JNIEnv* env, jobject);
+
   // Called after we got the response for GenerateKeyRequest().
   void OnKeyMessage(JNIEnv* env, jobject, jstring j_session_id,
                     jbyteArray message, jstring destination_url);
@@ -53,8 +71,9 @@
   // Called when error happens.
   void OnKeyError(JNIEnv* env, jobject, jstring j_session_id);
 
-  // Methods to create and release a MediaCrypto object.
-  base::android::ScopedJavaLocalRef<jobject> GetMediaCrypto();
+  // Helper function to determine whether a protected surface is needed for the
+  // video playback.
+  bool IsProtectedSurfaceRequired();
 
   int media_keys_id() const { return media_keys_id_; }
 
@@ -63,6 +82,9 @@
                  const std::vector<uint8>& scheme_uuid,
                  MediaPlayerManager* manager);
 
+  // Get the security level of the media.
+  SecurityLevel GetSecurityLevel();
+
   // ID of the MediaKeys object.
   int media_keys_id_;
 
@@ -75,6 +97,8 @@
   // Non-owned pointer.
   MediaPlayerManager* manager_;
 
+  base::Closure media_crypto_ready_cb_;
+
   DISALLOW_COPY_AND_ASSIGN(MediaDrmBridge);
 };
 
diff --git a/media/base/android/media_player_android.cc b/media/base/android/media_player_android.cc
index c005506..6b1626a 100644
--- a/media/base/android/media_player_android.cc
+++ b/media/base/android/media_player_android.cc
@@ -63,13 +63,11 @@
     manager_->ReleaseMediaResources(player_id_);
 }
 
-void MediaPlayerAndroid::DemuxerReady(
-    const MediaPlayerHostMsg_DemuxerReady_Params& params) {
+void MediaPlayerAndroid::DemuxerReady(const DemuxerConfigs& configs) {
   NOTREACHED() << "Unexpected ipc received";
 }
 
-void MediaPlayerAndroid::ReadFromDemuxerAck(
-    const MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params) {
+void MediaPlayerAndroid::ReadFromDemuxerAck(const DemuxerData& data) {
   NOTREACHED() << "Unexpected ipc received";
 }
 
diff --git a/media/base/android/media_player_android.h b/media/base/android/media_player_android.h
index f1c9c37..06cb573 100644
--- a/media/base/android/media_player_android.h
+++ b/media/base/android/media_player_android.h
@@ -88,11 +88,10 @@
 
   // Methods for DemuxerStreamPlayer.
   // Informs DemuxerStreamPlayer that the demuxer is ready.
-  virtual void DemuxerReady(
-      const MediaPlayerHostMsg_DemuxerReady_Params& params);
+  virtual void DemuxerReady(const DemuxerConfigs& configs);
+
   // Called when the requested data is received from the demuxer.
-  virtual void ReadFromDemuxerAck(
-      const MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params);
+  virtual void ReadFromDemuxerAck(const DemuxerData& data);
 
   // Called when a seek request is acked by the render process.
   virtual void OnSeekRequestAck(unsigned seek_request_id);
diff --git a/media/base/android/media_player_bridge.cc b/media/base/android/media_player_bridge.cc
index 342ceaa..49c3563 100644
--- a/media/base/android/media_player_bridge.cc
+++ b/media/base/android/media_player_bridge.cc
@@ -20,13 +20,6 @@
 // Time update happens every 250ms.
 static const int kTimeUpdateInterval = 250;
 
-// Android MediaMetadataRetriever may fail to extract the metadata from the
-// media under some circumstances. This makes the user unable to perform
-// seek. To solve this problem, we use a temporary duration of 100 seconds when
-// the duration is unknown. And we scale the seek position later when duration
-// is available.
-static const int kTemporaryDuration = 100;
-
 namespace media {
 
 #if !defined(GOOGLE_TV)
@@ -68,7 +61,6 @@
       url_(url),
       first_party_for_cookies_(first_party_for_cookies),
       hide_url_log_(hide_url_log),
-      duration_(base::TimeDelta::FromSeconds(kTemporaryDuration)),
       width_(0),
       height_(0),
       can_pause_(true),
@@ -331,16 +323,8 @@
     return;
 
   prepared_ = true;
-
-  base::TimeDelta dur = duration_;
   duration_ = GetDuration();
 
-  if (duration_ != dur && 0 != dur.InMilliseconds()) {
-    // Scale the |pending_seek_| according to the new duration.
-    pending_seek_ = base::TimeDelta::FromSeconds(
-        pending_seek_.InSecondsF() * duration_.InSecondsF() / dur.InSecondsF());
-  }
-
   // If media player was recovered from a saved state, consume all the pending
   // events.
   PendingSeekInternal(pending_seek_);
@@ -402,7 +386,6 @@
 
   JNIEnv* env = base::android::AttachCurrentThread();
   CHECK(env);
-
   int time_msec = static_cast<int>(time.InMilliseconds());
   Java_MediaPlayerBridge_seekTo(
       env, j_media_player_bridge_.obj(), time_msec);
diff --git a/media/base/android/media_player_manager.h b/media/base/android/media_player_manager.h
index a0f5017..46ea8ca 100644
--- a/media/base/android/media_player_manager.h
+++ b/media/base/android/media_player_manager.h
@@ -14,10 +14,6 @@
 #include "media/base/media_export.h"
 #include "media/base/media_keys.h"
 
-namespace content {
-class RenderViewHost;
-}
-
 namespace media {
 
 class MediaDrmBridge;
@@ -25,25 +21,8 @@
 class MediaResourceGetter;
 
 // This class is responsible for managing active MediaPlayerAndroid objects.
-// Objects implementing this interface a created via
-// MediaPlayerManager::Create(), allowing embedders to provide their
-// implementation.
 class MEDIA_EXPORT MediaPlayerManager {
  public:
-  // The type of the factory function that returns a new instance of the
-  // MediaPlayerManager implementation.
-  typedef MediaPlayerManager* (*FactoryFunction)(content::RenderViewHost*);
-
-  // Allows to override the default factory function in order to provide
-  // a custom implementation to the RenderViewHost instance.
-  // Must be called from the main thread.
-  static void RegisterFactoryFunction(FactoryFunction factory_function);
-
-  // Returns a new instance of MediaPlayerManager interface implementation.
-  // The returned object is owned by the caller. Must be called on the main
-  // thread.
-  static MediaPlayerManager* Create(content::RenderViewHost* render_view_host);
-
   virtual ~MediaPlayerManager() {}
 
   // Called by a MediaPlayerAndroid object when it is going to decode
diff --git a/media/base/android/media_source_player.cc b/media/base/android/media_source_player.cc
index e0fd9c7..ba87bf0 100644
--- a/media/base/android/media_source_player.cc
+++ b/media/base/android/media_source_player.cc
@@ -6,6 +6,7 @@
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
+#include "base/barrier_closure.h"
 #include "base/basictypes.h"
 #include "base/bind.h"
 #include "base/logging.h"
@@ -45,11 +46,6 @@
       clock_(&default_tick_clock_),
       reconfig_audio_decoder_(false),
       reconfig_video_decoder_(false),
-      audio_access_unit_index_(0),
-      video_access_unit_index_(0),
-      waiting_for_audio_data_(false),
-      waiting_for_video_data_(false),
-      sync_decoder_jobs_(true),
       weak_this_(this),
       drm_bridge_(NULL) {
 }
@@ -59,21 +55,37 @@
 }
 
 void MediaSourcePlayer::SetVideoSurface(gfx::ScopedJavaSurface surface) {
-  // Ignore non-empty surface that is unprotected if |is_video_encrypted_| is
-  // true.
-  if (is_video_encrypted_ && !surface.IsEmpty() && !surface.is_protected())
+  // For an empty surface, always pass it to the decoder job so that it
+  // can detach from the current one. Otherwise, don't pass an unprotected
+  // surface if the video content requires a protected one.
+  if (!surface.IsEmpty() &&
+      IsProtectedSurfaceRequired() && !surface.is_protected()) {
     return;
+  }
 
   surface_ =  surface.Pass();
-  pending_event_ |= SURFACE_CHANGE_EVENT_PENDING;
-  if (pending_event_ & SEEK_EVENT_PENDING) {
+  SetPendingEvent(SURFACE_CHANGE_EVENT_PENDING);
+  if (IsEventPending(SEEK_EVENT_PENDING)) {
     // Waiting for the seek to finish.
     return;
   }
+
   // Setting a new surface will require a new MediaCodec to be created.
   // Request a seek so that the new decoder will decode an I-frame first.
   // Or otherwise, the new MediaCodec might crash. See b/8950387.
-  pending_event_ |= SEEK_EVENT_PENDING;
+  ScheduleSeekEventAndStopDecoding();
+}
+
+void MediaSourcePlayer::ScheduleSeekEventAndStopDecoding() {
+  if (audio_decoder_job_ && audio_decoder_job_->is_decoding())
+    audio_decoder_job_->StopDecode();
+  if (video_decoder_job_ && video_decoder_job_->is_decoding())
+    video_decoder_job_->StopDecode();
+
+  if (IsEventPending(SEEK_EVENT_PENDING))
+    return;
+
+  SetPendingEvent(SEEK_EVENT_PENDING);
   ProcessPendingEvents();
 }
 
@@ -87,15 +99,19 @@
 }
 
 void MediaSourcePlayer::Start() {
+  DVLOG(1) << __FUNCTION__;
+
   playing_ = true;
 
-  if (is_video_encrypted_)
+  if (IsProtectedSurfaceRequired())
     manager()->OnProtectedSurfaceRequested(player_id());
 
   StartInternal();
 }
 
 void MediaSourcePlayer::Pause() {
+  DVLOG(1) << __FUNCTION__;
+
   // Since decoder jobs have their own thread, decoding is not fully paused
   // until all the decoder jobs call MediaDecoderCallback(). It is possible
   // that Start() is called while the player is waiting for
@@ -118,11 +134,12 @@
 }
 
 void MediaSourcePlayer::SeekTo(base::TimeDelta timestamp) {
+  DVLOG(1) << __FUNCTION__ << "(" << timestamp.InSecondsF() << ")";
+
   clock_.SetTime(timestamp, timestamp);
   if (audio_timestamp_helper_)
     audio_timestamp_helper_->SetBaseTimestamp(timestamp);
-  pending_event_ |= SEEK_EVENT_PENDING;
-  ProcessPendingEvents();
+  ScheduleSeekEventAndStopDecoding();
 }
 
 base::TimeDelta MediaSourcePlayer::GetCurrentTime() {
@@ -134,6 +151,7 @@
 }
 
 void MediaSourcePlayer::Release() {
+  DVLOG(1) << __FUNCTION__;
   ClearDecodingData();
   audio_decoder_job_.reset();
   video_decoder_job_.reset();
@@ -167,6 +185,7 @@
 }
 
 void MediaSourcePlayer::StartInternal() {
+  DVLOG(1) << __FUNCTION__;
   // If there are pending events, wait for them finish.
   if (pending_event_ != NO_EVENT_PENDING)
     return;
@@ -184,20 +203,20 @@
 
   audio_finished_ = false;
   video_finished_ = false;
-  sync_decoder_jobs_ = true;
-  SyncAndStartDecoderJobs();
+  SetPendingEvent(PREFETCH_REQUEST_EVENT_PENDING);
+  ProcessPendingEvents();
 }
 
-void MediaSourcePlayer::DemuxerReady(
-    const MediaPlayerHostMsg_DemuxerReady_Params& params) {
-  duration_ = base::TimeDelta::FromMilliseconds(params.duration_ms);
+void MediaSourcePlayer::DemuxerReady(const DemuxerConfigs& configs) {
+  DVLOG(1) << __FUNCTION__;
+  duration_ = base::TimeDelta::FromMilliseconds(configs.duration_ms);
   clock_.SetDuration(duration_);
 
-  audio_codec_ = params.audio_codec;
-  num_channels_ = params.audio_channels;
-  sampling_rate_ = params.audio_sampling_rate;
-  is_audio_encrypted_ = params.is_audio_encrypted;
-  audio_extra_data_ = params.audio_extra_data;
+  audio_codec_ = configs.audio_codec;
+  num_channels_ = configs.audio_channels;
+  sampling_rate_ = configs.audio_sampling_rate;
+  is_audio_encrypted_ = configs.is_audio_encrypted;
+  audio_extra_data_ = configs.audio_extra_data;
   if (HasAudio()) {
     DCHECK_GT(num_channels_, 0);
     audio_timestamp_helper_.reset(new AudioTimestampHelper(sampling_rate_));
@@ -206,62 +225,40 @@
     audio_timestamp_helper_.reset();
   }
 
-  video_codec_ = params.video_codec;
-  width_ = params.video_size.width();
-  height_ = params.video_size.height();
-  is_video_encrypted_ = params.is_video_encrypted;
+  video_codec_ = configs.video_codec;
+  width_ = configs.video_size.width();
+  height_ = configs.video_size.height();
+  is_video_encrypted_ = configs.is_video_encrypted;
 
   OnMediaMetadataChanged(duration_, width_, height_, true);
 
-  if (pending_event_ & CONFIG_CHANGE_EVENT_PENDING) {
+  if (IsEventPending(CONFIG_CHANGE_EVENT_PENDING)) {
     if (reconfig_audio_decoder_)
       ConfigureAudioDecoderJob();
 
     // If there is a pending surface change, we can merge it with the config
     // change.
     if (reconfig_video_decoder_) {
-      pending_event_ &= ~SURFACE_CHANGE_EVENT_PENDING;
+      if (IsEventPending(SURFACE_CHANGE_EVENT_PENDING))
+        ClearPendingEvent(SURFACE_CHANGE_EVENT_PENDING);
       ConfigureVideoDecoderJob();
     }
-    pending_event_ &= ~CONFIG_CHANGE_EVENT_PENDING;
+
+    ClearPendingEvent(CONFIG_CHANGE_EVENT_PENDING);
+
+    // Resume decoding after the config change if we are still playing.
     if (playing_)
       StartInternal();
   }
 }
 
-void MediaSourcePlayer::ReadFromDemuxerAck(
-    const MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params) {
-  DCHECK_LT(0u, params.access_units.size());
-  if (params.type == DemuxerStream::AUDIO)
-    waiting_for_audio_data_ = false;
+void MediaSourcePlayer::ReadFromDemuxerAck(const DemuxerData& data) {
+  DVLOG(1) << __FUNCTION__ << "(" << data.type << ")";
+  DCHECK_LT(0u, data.access_units.size());
+  if (data.type == DemuxerStream::AUDIO)
+    audio_decoder_job_->OnDataReceived(data);
   else
-    waiting_for_video_data_ = false;
-
-  // If there is a pending seek request, ignore the data from the chunk demuxer.
-  // The data will be requested later when OnSeekRequestAck() is called.
-  if (pending_event_ & SEEK_EVENT_PENDING)
-    return;
-
-  if (params.type == DemuxerStream::AUDIO) {
-    DCHECK_EQ(0u, audio_access_unit_index_);
-    received_audio_ = params;
-  } else {
-    DCHECK_EQ(0u, video_access_unit_index_);
-    received_video_ = params;
-  }
-
-  if (pending_event_ != NO_EVENT_PENDING || !playing_)
-    return;
-
-  if (sync_decoder_jobs_) {
-    SyncAndStartDecoderJobs();
-    return;
-  }
-
-  if (params.type == DemuxerStream::AUDIO)
-    DecodeMoreAudio();
-  else
-    DecodeMoreVideo();
+    video_decoder_job_->OnDataReceived(data);
 }
 
 void MediaSourcePlayer::DurationChanged(const base::TimeDelta& duration) {
@@ -269,28 +266,51 @@
   clock_.SetDuration(duration_);
 }
 
+base::android::ScopedJavaLocalRef<jobject> MediaSourcePlayer::GetMediaCrypto() {
+  base::android::ScopedJavaLocalRef<jobject> media_crypto;
+  if (drm_bridge_)
+    media_crypto = drm_bridge_->GetMediaCrypto();
+  return media_crypto;
+}
+
+void MediaSourcePlayer::OnMediaCryptoReady() {
+  DCHECK(!drm_bridge_->GetMediaCrypto().is_null());
+  drm_bridge_->SetMediaCryptoReadyCB(base::Closure());
+
+  if (playing_)
+    StartInternal();
+}
+
 void MediaSourcePlayer::SetDrmBridge(MediaDrmBridge* drm_bridge) {
   // Currently we don't support DRM change during the middle of playback, even
   // if the player is paused.
   // TODO(qinmin): support DRM change after playback has started.
   // http://crbug.com/253792.
   if (GetCurrentTime() > base::TimeDelta()) {
-    LOG(INFO) << "Setting DRM bridge after play back has started. "
+    LOG(INFO) << "Setting DRM bridge after playback has started. "
               << "This is not well supported!";
   }
 
   drm_bridge_ = drm_bridge;
 
+  if (drm_bridge_->GetMediaCrypto().is_null()) {
+    drm_bridge_->SetMediaCryptoReadyCB(base::Bind(
+        &MediaSourcePlayer::OnMediaCryptoReady, weak_this_.GetWeakPtr()));
+    return;
+  }
+
   if (playing_)
     StartInternal();
 }
 
 void MediaSourcePlayer::OnSeekRequestAck(unsigned seek_request_id) {
-  DVLOG(1) << "OnSeekRequestAck(" << seek_request_id << ")";
+  DVLOG(1) << __FUNCTION__ << "(" << seek_request_id << ")";
   // Do nothing until the most recent seek request is processed.
   if (seek_request_id_ != seek_request_id)
     return;
-  pending_event_ &= ~SEEK_EVENT_PENDING;
+
+  ClearPendingEvent(SEEK_EVENT_PENDING);
+
   OnSeekComplete();
   ProcessPendingEvents();
 }
@@ -309,13 +329,22 @@
 }
 
 void MediaSourcePlayer::ProcessPendingEvents() {
+  DVLOG(1) << __FUNCTION__ << " : 0x"
+           << std::hex << pending_event_;
   // Wait for all the decoding jobs to finish before processing pending tasks.
   if ((audio_decoder_job_ && audio_decoder_job_->is_decoding()) ||
       (video_decoder_job_ && video_decoder_job_->is_decoding())) {
+    DVLOG(1) << __FUNCTION__ << " : A job is still decoding.";
     return;
   }
 
-  if (pending_event_ & SEEK_EVENT_PENDING) {
+  if (IsEventPending(PREFETCH_DONE_EVENT_PENDING)) {
+    DVLOG(1) << __FUNCTION__ << " : PREFETCH_DONE still pending.";
+    return;
+  }
+
+  if (IsEventPending(SEEK_EVENT_PENDING)) {
+    DVLOG(1) << __FUNCTION__ << " : Handling SEEK_EVENT.";
     ClearDecodingData();
     manager()->OnMediaSeekRequest(
         player_id(), GetCurrentTime(), ++seek_request_id_);
@@ -323,18 +352,42 @@
   }
 
   start_time_ticks_ = base::TimeTicks();
-  if (pending_event_ & CONFIG_CHANGE_EVENT_PENDING) {
+  if (IsEventPending(CONFIG_CHANGE_EVENT_PENDING)) {
+    DVLOG(1) << __FUNCTION__ << " : Handling CONFIG_CHANGE_EVENT.";
     DCHECK(reconfig_audio_decoder_ || reconfig_video_decoder_);
     manager()->OnMediaConfigRequest(player_id());
     return;
   }
 
-  if (pending_event_ & SURFACE_CHANGE_EVENT_PENDING) {
+  if (IsEventPending(SURFACE_CHANGE_EVENT_PENDING)) {
+    DVLOG(1) << __FUNCTION__ << " : Handling SURFACE_CHANGE_EVENT.";
     video_decoder_job_.reset();
     ConfigureVideoDecoderJob();
-    pending_event_ &= ~SURFACE_CHANGE_EVENT_PENDING;
+    ClearPendingEvent(SURFACE_CHANGE_EVENT_PENDING);
   }
 
+  if (IsEventPending(PREFETCH_REQUEST_EVENT_PENDING)) {
+    DVLOG(1) << __FUNCTION__ << " : Handling PREFETCH_REQUEST_EVENT.";
+    int count = (audio_decoder_job_ ? 1 : 0) + (video_decoder_job_ ? 1 : 0);
+
+    base::Closure barrier = BarrierClosure(count, base::Bind(
+        &MediaSourcePlayer::OnPrefetchDone, weak_this_.GetWeakPtr()));
+
+    if (audio_decoder_job_)
+      audio_decoder_job_->Prefetch(barrier);
+
+    if (video_decoder_job_)
+      video_decoder_job_->Prefetch(barrier);
+
+    SetPendingEvent(PREFETCH_DONE_EVENT_PENDING);
+    ClearPendingEvent(PREFETCH_REQUEST_EVENT_PENDING);
+    return;
+  }
+
+  DCHECK_EQ(pending_event_, NO_EVENT_PENDING);
+
+  // Now that all pending events have been handled, resume decoding if we are
+  // still playing.
   if (playing_)
     StartInternal();
 }
@@ -342,11 +395,7 @@
 void MediaSourcePlayer::MediaDecoderCallback(
     bool is_audio, MediaDecoderJob::DecodeStatus decode_status,
     const base::TimeDelta& presentation_timestamp, size_t audio_output_bytes) {
-  if (is_audio && audio_decoder_job_)
-    audio_decoder_job_->OnDecodeCompleted();
-  if (!is_audio && video_decoder_job_)
-    video_decoder_job_->OnDecodeCompleted();
-
+  DVLOG(1) << __FUNCTION__;
   if (is_audio)
     decoder_starvation_callback_.Cancel();
 
@@ -356,15 +405,6 @@
     return;
   }
 
-  // If the input reaches input EOS, there is no need to request new data.
-  if (decode_status != MediaDecoderJob::DECODE_TRY_ENQUEUE_INPUT_AGAIN_LATER &&
-      decode_status != MediaDecoderJob::DECODE_INPUT_END_OF_STREAM) {
-    if (is_audio)
-      audio_access_unit_index_++;
-    else
-      video_access_unit_index_++;
-  }
-
   if (pending_event_ != NO_EVENT_PENDING) {
     ProcessPendingEvents();
     return;
@@ -386,11 +426,6 @@
     return;
   }
 
-  if (sync_decoder_jobs_) {
-    SyncAndStartDecoderJobs();
-    return;
-  }
-
   base::TimeDelta current_timestamp = GetCurrentTime();
   if (is_audio) {
     if (decode_status == MediaDecoderJob::DECODE_SUCCEEDED) {
@@ -398,9 +433,6 @@
           audio_timestamp_helper_->GetTimestamp() - current_timestamp;
       StartStarvationCallback(timeout);
     }
-    if (!HasAudioData())
-      RequestAudioData();
-    else
       DecodeMoreAudio();
     return;
   }
@@ -414,61 +446,48 @@
     // video frame timeout.
     StartStarvationCallback(2 * (presentation_timestamp - current_timestamp));
   }
-  if (!HasVideoData())
-    RequestVideoData();
-  else
-    DecodeMoreVideo();
+
+  DecodeMoreVideo();
 }
 
 void MediaSourcePlayer::DecodeMoreAudio() {
+  DVLOG(1) << __FUNCTION__;
   DCHECK(!audio_decoder_job_->is_decoding());
-  DCHECK(HasAudioData());
 
-  if (DemuxerStream::kConfigChanged ==
-      received_audio_.access_units[audio_access_unit_index_].status) {
-    // Wait for demuxer ready message.
-    reconfig_audio_decoder_ = true;
-    pending_event_ |= CONFIG_CHANGE_EVENT_PENDING;
-    received_audio_ = MediaPlayerHostMsg_ReadFromDemuxerAck_Params();
-    audio_access_unit_index_ = 0;
-    ProcessPendingEvents();
+  if (audio_decoder_job_->Decode(
+          start_time_ticks_, start_presentation_timestamp_, base::Bind(
+              &MediaSourcePlayer::MediaDecoderCallback,
+              weak_this_.GetWeakPtr(), true))) {
     return;
   }
 
-  audio_decoder_job_->Decode(
-      received_audio_.access_units[audio_access_unit_index_],
-      start_time_ticks_, start_presentation_timestamp_,
-      base::Bind(&MediaSourcePlayer::MediaDecoderCallback,
-                 weak_this_.GetWeakPtr(), true));
+  // Failed to start the next decode.
+  // Wait for demuxer ready message.
+  reconfig_audio_decoder_ = true;
+  SetPendingEvent(CONFIG_CHANGE_EVENT_PENDING);
+  ProcessPendingEvents();
 }
 
 void MediaSourcePlayer::DecodeMoreVideo() {
-  DVLOG(1) << "DecodeMoreVideo()";
+  DVLOG(1) << __FUNCTION__;
   DCHECK(!video_decoder_job_->is_decoding());
-  DCHECK(HasVideoData());
 
-  if (DemuxerStream::kConfigChanged ==
-      received_video_.access_units[video_access_unit_index_].status) {
-    // Wait for demuxer ready message.
-    reconfig_video_decoder_ = true;
-    pending_event_ |= CONFIG_CHANGE_EVENT_PENDING;
-    received_video_ = MediaPlayerHostMsg_ReadFromDemuxerAck_Params();
-    video_access_unit_index_ = 0;
-    ProcessPendingEvents();
+  if (video_decoder_job_->Decode(
+          start_time_ticks_, start_presentation_timestamp_, base::Bind(
+              &MediaSourcePlayer::MediaDecoderCallback,
+              weak_this_.GetWeakPtr(), false))) {
     return;
   }
 
-  DVLOG(3) << "VideoDecoderJob::Decode(" << video_access_unit_index_ << ", "
-           << start_time_ticks_.ToInternalValue() << ", "
-           << start_presentation_timestamp_.InMilliseconds() << ")";
-  video_decoder_job_->Decode(
-      received_video_.access_units[video_access_unit_index_],
-      start_time_ticks_, start_presentation_timestamp_,
-      base::Bind(&MediaSourcePlayer::MediaDecoderCallback,
-                 weak_this_.GetWeakPtr(), false));
+  // Failed to start the next decode.
+  // Wait for demuxer ready message.
+  reconfig_video_decoder_ = true;
+  SetPendingEvent(CONFIG_CHANGE_EVENT_PENDING);
+  ProcessPendingEvents();
 }
 
 void MediaSourcePlayer::PlaybackCompleted(bool is_audio) {
+  DVLOG(1) << __FUNCTION__ << "(" << is_audio << ")";
   if (is_audio)
     audio_finished_ = true;
   else
@@ -483,18 +502,12 @@
 }
 
 void MediaSourcePlayer::ClearDecodingData() {
-  DVLOG(1) << "ClearDecodingData()";
+  DVLOG(1) << __FUNCTION__;
   if (audio_decoder_job_)
     audio_decoder_job_->Flush();
   if (video_decoder_job_)
     video_decoder_job_->Flush();
   start_time_ticks_ = base::TimeTicks();
-  received_audio_ = MediaPlayerHostMsg_ReadFromDemuxerAck_Params();
-  received_video_ = MediaPlayerHostMsg_ReadFromDemuxerAck_Params();
-  audio_access_unit_index_ = 0;
-  video_access_unit_index_ = 0;
-  waiting_for_audio_data_ = false;
-  waiting_for_video_data_ = false;
 }
 
 bool MediaSourcePlayer::HasVideo() {
@@ -515,26 +528,18 @@
   if (audio_decoder_job_ && !reconfig_audio_decoder_)
     return;
 
-  base::android::ScopedJavaLocalRef<jobject> media_codec;
-  if (is_audio_encrypted_) {
-    if (drm_bridge_) {
-      media_codec = drm_bridge_->GetMediaCrypto();
-      // TODO(qinmin): currently we assume MediaCrypto is available whenever
-      // MediaDrmBridge is constructed. This will change if we want to support
-      // more general uses cases of EME.
-      DCHECK(!media_codec.is_null());
-    } else {
-      // Don't create the decoder job if |drm_bridge_| is not set,
-      // so StartInternal() will not proceed.
-      LOG(INFO) << "MediaDrmBridge is not available when creating decoder "
-                << "for encrypted audio stream.";
-      return;
-    }
-  }
+  base::android::ScopedJavaLocalRef<jobject> media_crypto = GetMediaCrypto();
+  if (is_audio_encrypted_ && media_crypto.is_null())
+    return;
+
+  DCHECK(!audio_decoder_job_ || !audio_decoder_job_->is_decoding());
 
   audio_decoder_job_.reset(AudioDecoderJob::Create(
       audio_codec_, sampling_rate_, num_channels_, &audio_extra_data_[0],
-      audio_extra_data_.size(), media_codec.obj()));
+      audio_extra_data_.size(), media_crypto.obj(),
+      base::Bind(&MediaPlayerManager::OnReadFromDemuxer,
+                 base::Unretained(manager()), player_id(),
+                 DemuxerStream::AUDIO)));
 
   if (audio_decoder_job_) {
     SetVolumeInternal();
@@ -552,17 +557,11 @@
   if (video_decoder_job_ && !reconfig_video_decoder_)
     return;
 
-  base::android::ScopedJavaLocalRef<jobject> media_crypto;
-  if (is_video_encrypted_) {
-    if (drm_bridge_) {
-      media_crypto = drm_bridge_->GetMediaCrypto();
-      DCHECK(!media_crypto.is_null());
-    } else {
-      LOG(INFO) << "MediaDrmBridge is not available when creating decoder "
-                << "for encrypted video stream.";
-      return;
-    }
-  }
+  base::android::ScopedJavaLocalRef<jobject> media_crypto = GetMediaCrypto();
+  if (is_video_encrypted_ && media_crypto.is_null())
+    return;
+
+  DCHECK(!video_decoder_job_ || !video_decoder_job_->is_decoding());
 
   // Release the old VideoDecoderJob first so the surface can get released.
   // Android does not allow 2 MediaCodec instances use the same surface.
@@ -570,7 +569,11 @@
   // Create the new VideoDecoderJob.
   video_decoder_job_.reset(VideoDecoderJob::Create(
       video_codec_, gfx::Size(width_, height_), surface_.j_surface().obj(),
-      media_crypto.obj()));
+      media_crypto.obj(),
+      base::Bind(&MediaPlayerManager::OnReadFromDemuxer,
+                 base::Unretained(manager()),
+                 player_id(),
+                 DemuxerStream::VIDEO)));
   if (video_decoder_job_)
     reconfig_video_decoder_ = false;
 
@@ -581,11 +584,15 @@
 }
 
 void MediaSourcePlayer::OnDecoderStarved() {
-  sync_decoder_jobs_ = true;
+  DVLOG(1) << __FUNCTION__;
+  SetPendingEvent(PREFETCH_REQUEST_EVENT_PENDING);
+  ProcessPendingEvents();
 }
 
 void MediaSourcePlayer::StartStarvationCallback(
     const base::TimeDelta& timeout) {
+  DVLOG(1) << __FUNCTION__ << "(" << timeout.InSecondsF() << ")";
+
   decoder_starvation_callback_.Reset(
       base::Bind(&MediaSourcePlayer::OnDecoderStarved,
                  weak_this_.GetWeakPtr()));
@@ -593,66 +600,76 @@
       FROM_HERE, decoder_starvation_callback_.callback(), timeout);
 }
 
-void MediaSourcePlayer::SyncAndStartDecoderJobs() {
-  // For streams with both audio and video, send the request for video too.
-  // However, don't wait for the response so that we won't have lots of
-  // noticeable pauses in the audio. Video will sync with audio by itself.
-  if (HasVideo() && !HasVideoData()) {
-    RequestVideoData();
-    if (!HasAudio())
-      return;
-  }
-  if (HasAudio() && !HasAudioData()) {
-    RequestAudioData();
+void MediaSourcePlayer::SetVolumeInternal() {
+  if (audio_decoder_job_ && volume_ >= 0)
+    audio_decoder_job_->SetVolume(volume_);
+}
+
+bool MediaSourcePlayer::IsProtectedSurfaceRequired() {
+  return is_video_encrypted_ &&
+      drm_bridge_ && drm_bridge_->IsProtectedSurfaceRequired();
+}
+
+void MediaSourcePlayer::OnPrefetchDone() {
+  DVLOG(1) << __FUNCTION__;
+  DCHECK(!audio_decoder_job_ || !audio_decoder_job_->is_decoding());
+  DCHECK(!video_decoder_job_ || !video_decoder_job_->is_decoding());
+  DCHECK(IsEventPending(PREFETCH_DONE_EVENT_PENDING));
+
+  ClearPendingEvent(PREFETCH_DONE_EVENT_PENDING);
+
+  if (pending_event_ != NO_EVENT_PENDING) {
+    ProcessPendingEvents();
     return;
   }
+
   start_time_ticks_ = base::TimeTicks::Now();
   start_presentation_timestamp_ = GetCurrentTime();
   if (!clock_.IsPlaying())
     clock_.Play();
-  if (HasAudioData() && !audio_decoder_job_->is_decoding())
+
+  if (audio_decoder_job_)
     DecodeMoreAudio();
-  if (HasVideoData() && !video_decoder_job_->is_decoding())
+  if (video_decoder_job_)
     DecodeMoreVideo();
-  sync_decoder_jobs_ = false;
 }
 
-void MediaSourcePlayer::RequestAudioData() {
-  DVLOG(2) << "RequestAudioData()";
-  DCHECK(HasAudio());
+const char* MediaSourcePlayer::GetEventName(PendingEventFlags event) {
+  static const char* kPendingEventNames[] = {
+    "SEEK",
+    "SURFACE_CHANGE",
+    "CONFIG_CHANGE",
+    "PREFETCH_REQUEST",
+    "PREFETCH_DONE",
+  };
 
-  if (waiting_for_audio_data_)
-    return;
+  int mask = 1;
+  for (size_t i = 0; i < arraysize(kPendingEventNames); ++i, mask <<= 1) {
+    if (event & mask)
+      return kPendingEventNames[i];
+  }
 
-  manager()->OnReadFromDemuxer(player_id(), DemuxerStream::AUDIO);
-  received_audio_ = MediaPlayerHostMsg_ReadFromDemuxerAck_Params();
-  audio_access_unit_index_ = 0;
-  waiting_for_audio_data_ = true;
+  return "UNKNOWN";
 }
 
-void MediaSourcePlayer::RequestVideoData() {
-  DVLOG(2) << "RequestVideoData()";
-  DCHECK(HasVideo());
-  if (waiting_for_video_data_)
-    return;
-
-  manager()->OnReadFromDemuxer(player_id(), DemuxerStream::VIDEO);
-  received_video_ = MediaPlayerHostMsg_ReadFromDemuxerAck_Params();
-  video_access_unit_index_ = 0;
-  waiting_for_video_data_ = true;
+bool MediaSourcePlayer::IsEventPending(PendingEventFlags event) const {
+  return pending_event_ & event;
 }
 
-bool MediaSourcePlayer::HasAudioData() const {
-  return audio_access_unit_index_ < received_audio_.access_units.size();
+void MediaSourcePlayer::SetPendingEvent(PendingEventFlags event) {
+  DVLOG(1) << __FUNCTION__ << "(" << GetEventName(event) << ")";
+  DCHECK_NE(event, NO_EVENT_PENDING);
+  DCHECK(!IsEventPending(event));
+
+  pending_event_ |= event;
 }
 
-bool MediaSourcePlayer::HasVideoData() const {
-  return video_access_unit_index_ < received_video_.access_units.size();
-}
+void MediaSourcePlayer::ClearPendingEvent(PendingEventFlags event) {
+  DVLOG(1) << __FUNCTION__ << "(" << GetEventName(event) << ")";
+  DCHECK_NE(event, NO_EVENT_PENDING);
+  DCHECK(IsEventPending(event));
 
-void MediaSourcePlayer::SetVolumeInternal() {
-  if (audio_decoder_job_ && volume_ >= 0)
-    audio_decoder_job_.get()->SetVolume(volume_);
+  pending_event_ &= ~event;
 }
 
 }  // namespace media
diff --git a/media/base/android/media_source_player.h b/media/base/android/media_source_player.h
index c475762..decc5cc 100644
--- a/media/base/android/media_source_player.h
+++ b/media/base/android/media_source_player.h
@@ -58,10 +58,8 @@
   virtual bool CanSeekBackward() OVERRIDE;
   virtual bool IsPlayerReady() OVERRIDE;
   virtual void OnSeekRequestAck(unsigned seek_request_id) OVERRIDE;
-  virtual void DemuxerReady(
-      const MediaPlayerHostMsg_DemuxerReady_Params& params) OVERRIDE;
-  virtual void ReadFromDemuxerAck(
-      const MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params) OVERRIDE;
+  virtual void DemuxerReady(const DemuxerConfigs& configs) OVERRIDE;
+  virtual void ReadFromDemuxerAck(const DemuxerData& data) OVERRIDE;
   virtual void DurationChanged(const base::TimeDelta& duration) OVERRIDE;
   virtual void SetDrmBridge(MediaDrmBridge* drm_bridge) OVERRIDE;
 
@@ -82,6 +80,12 @@
         const base::TimeDelta& presentation_timestamp,
         size_t audio_output_bytes);
 
+  // Gets MediaCrypto object from |drm_bridge_|.
+  base::android::ScopedJavaLocalRef<jobject> GetMediaCrypto();
+
+  // Callback to notify that MediaCrypto is ready in |drm_bridge_|.
+  void OnMediaCryptoReady();
+
   // Handle pending events when all the decoder jobs finished.
   void ProcessPendingEvents();
 
@@ -109,30 +113,37 @@
   // Starts the |decoder_starvation_callback_| task with the timeout value.
   void StartStarvationCallback(const base::TimeDelta& timeout);
 
-  // Called to sync decoder jobs. This call requests data from chunk demuxer
-  // first. Then it updates |start_time_ticks_| and
-  // |start_presentation_timestamp_| so that video can resync with audio.
-  void SyncAndStartDecoderJobs();
-
-  // Functions that send IPC requests to the renderer process for more
-  // audio/video data. Returns true if a request has been sent and the decoder
-  // needs to wait, or false otherwise.
-  void RequestAudioData();
-  void RequestVideoData();
-
-  // Check whether audio or video data is available for decoders to consume.
-  bool HasAudioData() const;
-  bool HasVideoData() const;
+  // Schedules a seek event in |pending_events_| and calls StopDecode() on all
+  // the MediaDecoderJobs.
+  void ScheduleSeekEventAndStopDecoding();
 
   // Helper function to set the volume.
   void SetVolumeInternal();
 
+  // Helper function to determine whether a protected surface is needed for
+  // video playback.
+  bool IsProtectedSurfaceRequired();
+
+  // Called when a MediaDecoderJob finishes prefetching data. Once all
+  // MediaDecoderJobs have prefetched data, then this method updates
+  // |start_time_ticks_| and |start_presentation_timestamp_| so that video can
+  // resync with audio and starts decoding.
+  void OnPrefetchDone();
+
   enum PendingEventFlags {
     NO_EVENT_PENDING = 0,
     SEEK_EVENT_PENDING = 1 << 0,
     SURFACE_CHANGE_EVENT_PENDING = 1 << 1,
     CONFIG_CHANGE_EVENT_PENDING = 1 << 2,
+    PREFETCH_REQUEST_EVENT_PENDING = 1 << 3,
+    PREFETCH_DONE_EVENT_PENDING = 1 << 4,
   };
+
+  static const char* GetEventName(PendingEventFlags event);
+  bool IsEventPending(PendingEventFlags event) const;
+  void SetPendingEvent(PendingEventFlags event);
+  void ClearPendingEvent(PendingEventFlags event);
+
   // Pending event that the player needs to do.
   unsigned pending_event_;
 
@@ -181,24 +192,11 @@
   bool reconfig_audio_decoder_;
   bool reconfig_video_decoder_;
 
-  // These variables keep track of the current decoding data.
-  // TODO(qinmin): remove these variables when we no longer relies on IPC for
-  // data passing.
-  size_t audio_access_unit_index_;
-  size_t video_access_unit_index_;
-  bool waiting_for_audio_data_;
-  bool waiting_for_video_data_;
-  MediaPlayerHostMsg_ReadFromDemuxerAck_Params received_audio_;
-  MediaPlayerHostMsg_ReadFromDemuxerAck_Params received_video_;
-
   // A cancelable task that is posted when the audio decoder starts requesting
   // new data. This callback runs if no data arrives before the timeout period
   // elapses.
   base::CancelableClosure decoder_starvation_callback_;
 
-  // Whether the audio and video decoder jobs should resync with each other.
-  bool sync_decoder_jobs_;
-
   // Object to calculate the current audio timestamp for A/V sync.
   scoped_ptr<AudioTimestampHelper> audio_timestamp_helper_;
 
diff --git a/media/base/android/media_source_player_unittest.cc b/media/base/android/media_source_player_unittest.cc
index 02ded2d..e3dbf20 100644
--- a/media/base/android/media_source_player_unittest.cc
+++ b/media/base/android/media_source_player_unittest.cc
@@ -13,7 +13,7 @@
 #include "media/base/decoder_buffer.h"
 #include "media/base/test_data_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
-#include "ui/gl/android/surface_texture_bridge.h"
+#include "ui/gl/android/surface_texture.h"
 
 namespace media {
 
@@ -108,72 +108,70 @@
 
   // Starts an audio decoder job.
   void StartAudioDecoderJob() {
-    MediaPlayerHostMsg_DemuxerReady_Params params;
-    params.audio_codec = kCodecVorbis;
-    params.audio_channels = 2;
-    params.audio_sampling_rate = 44100;
-    params.is_audio_encrypted = false;
-    params.duration_ms = kDefaultDurationInMs;
+    DemuxerConfigs configs;
+    configs.audio_codec = kCodecVorbis;
+    configs.audio_channels = 2;
+    configs.audio_sampling_rate = 44100;
+    configs.is_audio_encrypted = false;
+    configs.duration_ms = kDefaultDurationInMs;
     scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile("vorbis-extradata");
-    params.audio_extra_data = std::vector<uint8>(
+    configs.audio_extra_data = std::vector<uint8>(
         buffer->data(),
         buffer->data() + buffer->data_size());
-    Start(params);
+    Start(configs);
   }
 
   void StartVideoDecoderJob() {
-    MediaPlayerHostMsg_DemuxerReady_Params params;
-    params.video_codec = kCodecVP8;
-    params.video_size = gfx::Size(320, 240);
-    params.is_video_encrypted = false;
-    params.duration_ms = kDefaultDurationInMs;
-    Start(params);
+    DemuxerConfigs configs;
+    configs.video_codec = kCodecVP8;
+    configs.video_size = gfx::Size(320, 240);
+    configs.is_video_encrypted = false;
+    configs.duration_ms = kDefaultDurationInMs;
+    Start(configs);
   }
 
   // Starts decoding the data.
-  void Start(const MediaPlayerHostMsg_DemuxerReady_Params& params) {
-    player_->DemuxerReady(params);
+  void Start(const DemuxerConfigs& configs) {
+    player_->DemuxerReady(configs);
     player_->Start();
   }
 
-  MediaPlayerHostMsg_ReadFromDemuxerAck_Params
-      CreateReadFromDemuxerAckForAudio(int packet_id) {
-    MediaPlayerHostMsg_ReadFromDemuxerAck_Params ack_params;
-    ack_params.type = DemuxerStream::AUDIO;
-    ack_params.access_units.resize(1);
-    ack_params.access_units[0].status = DemuxerStream::kOk;
+  DemuxerData CreateReadFromDemuxerAckForAudio(int packet_id) {
+    DemuxerData data;
+    data.type = DemuxerStream::AUDIO;
+    data.access_units.resize(1);
+    data.access_units[0].status = DemuxerStream::kOk;
     scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(
         base::StringPrintf("vorbis-packet-%d", packet_id));
-    ack_params.access_units[0].data = std::vector<uint8>(
+    data.access_units[0].data = std::vector<uint8>(
         buffer->data(), buffer->data() + buffer->data_size());
     // Vorbis needs 4 extra bytes padding on Android to decode properly. Check
     // NuMediaExtractor.cpp in Android source code.
     uint8 padding[4] = { 0xff , 0xff , 0xff , 0xff };
-    ack_params.access_units[0].data.insert(
-        ack_params.access_units[0].data.end(), padding, padding + 4);
-    return ack_params;
+    data.access_units[0].data.insert(
+        data.access_units[0].data.end(), padding, padding + 4);
+    return data;
   }
 
-  MediaPlayerHostMsg_ReadFromDemuxerAck_Params
-        CreateReadFromDemuxerAckForVideo() {
-    MediaPlayerHostMsg_ReadFromDemuxerAck_Params ack_params;
-    ack_params.type = DemuxerStream::VIDEO;
-    ack_params.access_units.resize(1);
-    ack_params.access_units[0].status = DemuxerStream::kOk;
+  DemuxerData CreateReadFromDemuxerAckForVideo() {
+    DemuxerData data;
+    data.type = DemuxerStream::VIDEO;
+    data.access_units.resize(1);
+    data.access_units[0].status = DemuxerStream::kOk;
     scoped_refptr<DecoderBuffer> buffer =
         ReadTestDataFile("vp8-I-frame-320x240");
-    ack_params.access_units[0].data = std::vector<uint8>(
+    data.access_units[0].data = std::vector<uint8>(
         buffer->data(), buffer->data() + buffer->data_size());
-    return ack_params;
+    return data;
   }
 
-  MediaPlayerHostMsg_ReadFromDemuxerAck_Params CreateEOSAck(bool is_audio) {
-      MediaPlayerHostMsg_ReadFromDemuxerAck_Params ack_params;
-      ack_params.type = is_audio ? DemuxerStream::AUDIO : DemuxerStream::VIDEO;
-      ack_params.access_units.resize(1);
-      ack_params.access_units[0].status = DemuxerStream::kOk;
-      ack_params.access_units[0].end_of_stream = true;
-      return ack_params;
+  DemuxerData CreateEOSAck(bool is_audio) {
+      DemuxerData data;
+      data.type = is_audio ? DemuxerStream::AUDIO : DemuxerStream::VIDEO;
+      data.access_units.resize(1);
+      data.access_units[0].status = DemuxerStream::kOk;
+      data.access_units[0].end_of_stream = true;
+      return data;
     }
 
   base::TimeTicks StartTimeTicks() {
@@ -202,16 +200,16 @@
     return;
 
   // Test audio decoder job will not be created when failed to start the codec.
-  MediaPlayerHostMsg_DemuxerReady_Params params;
-  params.audio_codec = kCodecVorbis;
-  params.audio_channels = 2;
-  params.audio_sampling_rate = 44100;
-  params.is_audio_encrypted = false;
-  params.duration_ms = kDefaultDurationInMs;
+  DemuxerConfigs configs;
+  configs.audio_codec = kCodecVorbis;
+  configs.audio_channels = 2;
+  configs.audio_sampling_rate = 44100;
+  configs.is_audio_encrypted = false;
+  configs.duration_ms = kDefaultDurationInMs;
   uint8 invalid_codec_data[] = { 0x00, 0xff, 0xff, 0xff, 0xff };
-  params.audio_extra_data.insert(params.audio_extra_data.begin(),
+  configs.audio_extra_data.insert(configs.audio_extra_data.begin(),
                                  invalid_codec_data, invalid_codec_data + 4);
-  Start(params);
+  Start(configs);
   EXPECT_EQ(NULL, GetMediaDecoderJob(true));
   EXPECT_EQ(0, manager_->num_requests());
 }
@@ -221,8 +219,8 @@
     return;
 
   // Test video decoder job will be created when surface is valid.
-  scoped_refptr<gfx::SurfaceTextureBridge> surface_texture(
-      new gfx::SurfaceTextureBridge(0));
+  scoped_refptr<gfx::SurfaceTexture> surface_texture(
+      new gfx::SurfaceTexture(0));
   gfx::ScopedJavaSurface surface(surface_texture.get());
   StartVideoDecoderJob();
   // Video decoder job will not be created until surface is available.
@@ -242,8 +240,8 @@
     return;
 
   // Test video decoder job will be created when surface is valid.
-  scoped_refptr<gfx::SurfaceTextureBridge> surface_texture(
-      new gfx::SurfaceTextureBridge(0));
+  scoped_refptr<gfx::SurfaceTexture> surface_texture(
+      new gfx::SurfaceTexture(0));
   gfx::ScopedJavaSurface surface(surface_texture.get());
   StartVideoDecoderJob();
   // Video decoder job will not be created until surface is available.
@@ -283,8 +281,8 @@
 
   // Test SetVideoSurface() will not cause an extra seek while the player is
   // waiting for a seek ACK.
-  scoped_refptr<gfx::SurfaceTextureBridge> surface_texture(
-      new gfx::SurfaceTextureBridge(0));
+  scoped_refptr<gfx::SurfaceTexture> surface_texture(
+      new gfx::SurfaceTexture(0));
   gfx::ScopedJavaSurface surface(surface_texture.get());
   StartVideoDecoderJob();
   // Player is still waiting for SetVideoSurface(), so no request is sent.
@@ -307,13 +305,13 @@
     return;
 
   // Test decoder job will not start until all pending seek event is handled.
-  MediaPlayerHostMsg_DemuxerReady_Params params;
-  params.audio_codec = kCodecVorbis;
-  params.audio_channels = 2;
-  params.audio_sampling_rate = 44100;
-  params.is_audio_encrypted = false;
-  params.duration_ms = kDefaultDurationInMs;
-  player_->DemuxerReady(params);
+  DemuxerConfigs configs;
+  configs.audio_codec = kCodecVorbis;
+  configs.audio_channels = 2;
+  configs.audio_sampling_rate = 44100;
+  configs.is_audio_encrypted = false;
+  configs.duration_ms = kDefaultDurationInMs;
+  player_->DemuxerReady(configs);
   EXPECT_EQ(NULL, GetMediaDecoderJob(true));
   EXPECT_EQ(0, manager_->num_requests());
 
@@ -371,24 +369,24 @@
 
   // Test that when Start() is called, video decoder jobs will wait for audio
   // decoder job before start decoding the data.
-  MediaPlayerHostMsg_DemuxerReady_Params params;
-  params.audio_codec = kCodecVorbis;
-  params.audio_channels = 2;
-  params.audio_sampling_rate = 44100;
-  params.is_audio_encrypted = false;
+  DemuxerConfigs configs;
+  configs.audio_codec = kCodecVorbis;
+  configs.audio_channels = 2;
+  configs.audio_sampling_rate = 44100;
+  configs.is_audio_encrypted = false;
   scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile("vorbis-extradata");
-  params.audio_extra_data = std::vector<uint8>(
+  configs.audio_extra_data = std::vector<uint8>(
       buffer->data(),
       buffer->data() + buffer->data_size());
-  params.video_codec = kCodecVP8;
-  params.video_size = gfx::Size(320, 240);
-  params.is_video_encrypted = false;
-  params.duration_ms = kDefaultDurationInMs;
-  Start(params);
+  configs.video_codec = kCodecVP8;
+  configs.video_size = gfx::Size(320, 240);
+  configs.is_video_encrypted = false;
+  configs.duration_ms = kDefaultDurationInMs;
+  Start(configs);
   EXPECT_EQ(0, manager_->num_requests());
 
-  scoped_refptr<gfx::SurfaceTextureBridge> surface_texture(
-      new gfx::SurfaceTextureBridge(0));
+  scoped_refptr<gfx::SurfaceTexture> surface_texture(
+      new gfx::SurfaceTexture(0));
   gfx::ScopedJavaSurface surface(surface_texture.get());
   player_->SetVideoSurface(surface.Pass());
   EXPECT_EQ(1u, manager_->last_seek_request_id());
@@ -448,8 +446,8 @@
 
   // Test MediaSourcePlayer will not request for new data after input EOS is
   // reached.
-  scoped_refptr<gfx::SurfaceTextureBridge> surface_texture(
-      new gfx::SurfaceTextureBridge(0));
+  scoped_refptr<gfx::SurfaceTexture> surface_texture(
+      new gfx::SurfaceTexture(0));
   gfx::ScopedJavaSurface surface(surface_texture.get());
   player_->SetVideoSurface(surface.Pass());
   StartVideoDecoderJob();
diff --git a/media/base/android/video_decoder_job.cc b/media/base/android/video_decoder_job.cc
index 0cd7386..6ee595c 100644
--- a/media/base/android/video_decoder_job.cc
+++ b/media/base/android/video_decoder_job.cc
@@ -27,17 +27,18 @@
 
 VideoDecoderJob* VideoDecoderJob::Create(
     const VideoCodec video_codec, const gfx::Size& size, jobject surface,
-    jobject media_crypto) {
+    jobject media_crypto, const base::Closure& request_data_cb) {
   scoped_ptr<VideoCodecBridge> codec(VideoCodecBridge::Create(video_codec));
   if (codec && codec->Start(video_codec, size, surface, media_crypto))
-    return new VideoDecoderJob(codec.Pass());
+    return new VideoDecoderJob(codec.Pass(), request_data_cb);
   return NULL;
 }
 
 VideoDecoderJob::VideoDecoderJob(
-    scoped_ptr<VideoCodecBridge> video_codec_bridge)
+    scoped_ptr<VideoCodecBridge> video_codec_bridge,
+    const base::Closure& request_data_cb)
     : MediaDecoderJob(g_video_decoder_thread.Pointer()->message_loop_proxy(),
-                      video_codec_bridge.get()),
+                      video_codec_bridge.get(), request_data_cb),
       video_codec_bridge_(video_codec_bridge.Pass()) {
 }
 
diff --git a/media/base/android/video_decoder_job.h b/media/base/android/video_decoder_job.h
index 16f3229..a26fa2b 100644
--- a/media/base/android/video_decoder_job.h
+++ b/media/base/android/video_decoder_job.h
@@ -24,12 +24,14 @@
   // |surface| - The surface to render the frames to.
   // |media_crypto| - Handle to a Java object responsible for decrypting the
   // video data.
+  // |request_data_cb| - Callback used to request more data for the decoder.
   static VideoDecoderJob* Create(
       const VideoCodec video_codec, const gfx::Size& size, jobject surface,
-      jobject media_crypto);
+      jobject media_crypto, const base::Closure& request_data_cb);
 
  private:
-  VideoDecoderJob(scoped_ptr<VideoCodecBridge> video_codec_bridge);
+  VideoDecoderJob(scoped_ptr<VideoCodecBridge> video_codec_bridge,
+                  const base::Closure& request_data_cb);
 
   // MediaDecoderJob implementation.
   virtual void ReleaseOutputBuffer(
diff --git a/media/base/demuxer.h b/media/base/demuxer.h
index 81ac33b..853a21a 100644
--- a/media/base/demuxer.h
+++ b/media/base/demuxer.h
@@ -5,6 +5,8 @@
 #ifndef MEDIA_BASE_DEMUXER_H_
 #define MEDIA_BASE_DEMUXER_H_
 
+#include <vector>
+
 #include "base/time/time.h"
 #include "media/base/data_source.h"
 #include "media/base/demuxer_stream.h"
@@ -29,6 +31,12 @@
 
 class MEDIA_EXPORT Demuxer {
  public:
+  // A new potentially encrypted stream has been parsed.
+  // First parameter - The type of initialization data.
+  // Second parameter - The initialization data associated with the stream.
+  typedef base::Callback<void(const std::string& type,
+                              const std::vector<uint8>& init_data)> NeedKeyCB;
+
   Demuxer();
   virtual ~Demuxer();
 
diff --git a/media/base/keyboard_event_counter.cc b/media/base/keyboard_event_counter.cc
new file mode 100644
index 0000000..a4ae109
--- /dev/null
+++ b/media/base/keyboard_event_counter.cc
@@ -0,0 +1,41 @@
+// 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 "media/base/keyboard_event_counter.h"
+
+#include "base/atomicops.h"
+#include "base/logging.h"
+
+namespace media {
+
+KeyboardEventCounter::KeyboardEventCounter() : total_key_presses_(0) {}
+
+KeyboardEventCounter::~KeyboardEventCounter() {}
+
+void KeyboardEventCounter::Reset() {
+  pressed_keys_.clear();
+  total_key_presses_ = 0;
+}
+
+void KeyboardEventCounter::OnKeyboardEvent(ui::EventType event,
+                                           ui::KeyboardCode key_code) {
+  // Updates the pressed keys and the total count of key presses.
+  if (event == ui::ET_KEY_PRESSED) {
+    if (pressed_keys_.find(key_code) != pressed_keys_.end())
+      return;
+    pressed_keys_.insert(key_code);
+    base::subtle::NoBarrier_AtomicIncrement(
+        reinterpret_cast<base::subtle::AtomicWord*>(&total_key_presses_), 1);
+  } else {
+    DCHECK_EQ(ui::ET_KEY_RELEASED, event);
+    pressed_keys_.erase(key_code);
+  }
+}
+
+size_t KeyboardEventCounter::GetKeyPressCount() const {
+  return base::subtle::NoBarrier_Load(
+      reinterpret_cast<const base::subtle::AtomicWord*>(&total_key_presses_));
+}
+
+}  // namespace media
diff --git a/media/base/keyboard_event_counter.h b/media/base/keyboard_event_counter.h
new file mode 100644
index 0000000..c82ca59
--- /dev/null
+++ b/media/base/keyboard_event_counter.h
@@ -0,0 +1,48 @@
+// 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 MEDIA_BASE_KEYBOARD_EVENT_COUNTER_H_
+#define MEDIA_BASE_KEYBOARD_EVENT_COUNTER_H_
+
+#include <set>
+
+#include "base/synchronization/lock.h"
+#include "ui/base/events/event_constants.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+
+namespace media {
+
+// This class tracks the total number of keypresses based on the OnKeyboardEvent
+// calls it receives from the client.
+// Multiple key down events for the same key are counted as one keypress until
+// the same key is released.
+class KeyboardEventCounter {
+ public:
+  KeyboardEventCounter();
+  ~KeyboardEventCounter();
+
+  // Resets the count to 0. Must be called on the same thread as
+  // OnKeyboardEvent.
+  void Reset();
+
+  // Returns the total number of keypresses since its creation or last Reset()
+  // call. Can be called on any thread.
+  size_t GetKeyPressCount() const;
+
+  // The client should call this method on key down or key up events.
+  // Must be called on a single thread.
+  void OnKeyboardEvent(ui::EventType event, ui::KeyboardCode key_code);
+
+ private:
+  // The set of keys currently held down.
+  std::set<ui::KeyboardCode> pressed_keys_;
+
+  size_t total_key_presses_;
+
+  DISALLOW_COPY_AND_ASSIGN(KeyboardEventCounter);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_BASE_KEYBOARD_EVENT_COUNTER_H_
diff --git a/media/base/media_keys.h b/media/base/media_keys.h
index 4822483..cf170f0 100644
--- a/media/base/media_keys.h
+++ b/media/base/media_keys.h
@@ -82,8 +82,7 @@
 
 typedef base::Callback<void(const std::string& session_id,
                             const std::string& type,
-                            scoped_ptr<uint8[]> init_data,
-                            int init_data_size)> NeedKeyCB;
+                            const std::vector<uint8>& init_data)> NeedKeyCB;
 
 }  // namespace media
 
diff --git a/media/base/pipeline_unittest.cc b/media/base/pipeline_unittest.cc
index 360f233..4c8640c 100644
--- a/media/base/pipeline_unittest.cc
+++ b/media/base/pipeline_unittest.cc
@@ -5,7 +5,6 @@
 #include <vector>
 
 #include "base/bind.h"
-#include "base/callback_helpers.h"
 #include "base/message_loop/message_loop.h"
 #include "base/stl_util.h"
 #include "base/test/simple_test_tick_clock.h"
@@ -1134,18 +1133,7 @@
   void DoStopOrError(StopOrError stop_or_error) {
     InSequence s;
 
-    // Save the callback and run it after the error teardown path has started
-    // running.
-    //
-    // TODO(scherkus): Remove after https://codereview.chromium.org/22850009
-    // lands.
-    base::Closure stop_cb;
-    if (stop_or_error == kErrorAndStop) {
-      EXPECT_CALL(*demuxer_, Stop(_)).WillOnce(SaveArg<0>(&stop_cb));
-    } else {
-      EXPECT_CALL(*demuxer_, Stop(_)).WillOnce(RunClosure<0>());
-    }
-
+    EXPECT_CALL(*demuxer_, Stop(_)).WillOnce(RunClosure<0>());
     EXPECT_CALL(*audio_renderer_, Stop(_)).WillOnce(RunClosure<0>());
     EXPECT_CALL(*video_renderer_, Stop(_)).WillOnce(RunClosure<0>());
 
@@ -1162,11 +1150,8 @@
         break;
 
       case kErrorAndStop:
-        pipeline_->SetErrorForTesting(PIPELINE_ERROR_READ);
-        message_loop_.RunUntilIdle();
-        base::ResetAndReturn(&stop_cb).Run();
-
         EXPECT_CALL(callbacks_, OnStop());
+        pipeline_->SetErrorForTesting(PIPELINE_ERROR_READ);
         pipeline_->Stop(base::Bind(
             &CallbackHelper::OnStop, base::Unretained(&callbacks_)));
         break;
diff --git a/media/base/run_all_unittests.cc b/media/base/run_all_unittests.cc
index 4274634..28ef5c6 100644
--- a/media/base/run_all_unittests.cc
+++ b/media/base/run_all_unittests.cc
@@ -11,6 +11,7 @@
 #if defined(OS_ANDROID)
 #include "base/android/jni_android.h"
 #include "media/base/android/media_jni_registrar.h"
+#include "ui/gl/android/gl_jni_registrar.h"
 #endif
 
 class TestSuiteNoAtExit : public base::TestSuite {
@@ -28,6 +29,8 @@
 #if defined(OS_ANDROID)
   // Register JNI bindings for android.
   JNIEnv* env = base::android::AttachCurrentThread();
+  // Needed for surface texture support.
+  ui::gl::android::RegisterJni(env);
   media::RegisterJni(env);
 #endif
 
diff --git a/media/base/serial_runner.cc b/media/base/serial_runner.cc
index fa39133..dfc4a0b 100644
--- a/media/base/serial_runner.cc
+++ b/media/base/serial_runner.cc
@@ -59,9 +59,15 @@
       message_loop_(base::MessageLoopProxy::current()),
       bound_fns_(bound_fns),
       done_cb_(done_cb) {
-  message_loop_->PostTask(FROM_HERE, base::Bind(
-      &SerialRunner::RunNextInSeries, weak_this_.GetWeakPtr(),
-      PIPELINE_OK));
+  // Respect both cancellation and calling stack guarantees for |done_cb|
+  // when empty.
+  if (bound_fns_.empty()) {
+    message_loop_->PostTask(FROM_HERE, base::Bind(
+        &SerialRunner::RunNextInSeries, weak_this_.GetWeakPtr(), PIPELINE_OK));
+    return;
+  }
+
+  RunNextInSeries(PIPELINE_OK);
 }
 
 SerialRunner::~SerialRunner() {}
diff --git a/media/base/serial_runner.h b/media/base/serial_runner.h
index ef4b5d7..eaae625 100644
--- a/media/base/serial_runner.h
+++ b/media/base/serial_runner.h
@@ -51,6 +51,13 @@
   // All bound functions are executed on the thread that Run() is called on,
   // including |done_cb|.
   //
+  // To eliminate an unnecessary posted task, the first function is executed
+  // immediately on the caller's stack. It is *strongly advised* to ensure
+  // the calling code does no more work after the call to Run().
+  //
+  // In all cases, |done_cb| is guaranteed to execute on a separate calling
+  // stack.
+  //
   // Deleting the object will prevent execution of any unstarted bound
   // functions, including |done_cb|.
   static scoped_ptr<SerialRunner> Run(
diff --git a/media/base/serial_runner_unittest.cc b/media/base/serial_runner_unittest.cc
index 8df28be..6d21968 100644
--- a/media/base/serial_runner_unittest.cc
+++ b/media/base/serial_runner_unittest.cc
@@ -52,9 +52,10 @@
   void RunBoundFunction(PipelineStatus status,
                         size_t index,
                         const PipelineStatusCB& status_cb) {
-    EXPECT_FALSE(inside_start_)
-        << "Bound functions should not run on same stack as "
-        << "SerialRunner::Run()\n" << base::debug::StackTrace().ToString();
+    EXPECT_EQ(index == 0u, inside_start_)
+        << "First bound function should run on same stack as "
+        << "SerialRunner::Run() while all others should not\n"
+        << base::debug::StackTrace().ToString();
 
     called_[index] = true;
     status_cb.Run(status);
diff --git a/media/base/stream_parser.h b/media/base/stream_parser.h
index a0fbb71..33a336d 100644
--- a/media/base/stream_parser.h
+++ b/media/base/stream_parser.h
@@ -73,9 +73,8 @@
   // First parameter - The type of the initialization data associated with the
   //                   stream.
   // Second parameter - The initialization data associated with the stream.
-  // Third parameter - Number of bytes of the initialization data.
   typedef base::Callback<void(const std::string&,
-                              scoped_ptr<uint8[]>, int)> NeedKeyCB;
+                              const std::vector<uint8>&)> NeedKeyCB;
 
   // Initialize the parser with necessary callbacks. Must be called before any
   // data is passed to Parse(). |init_cb| will be called once enough data has
diff --git a/media/base/user_input_monitor.cc b/media/base/user_input_monitor.cc
index 18b4c80..34e07d8 100644
--- a/media/base/user_input_monitor.cc
+++ b/media/base/user_input_monitor.cc
@@ -16,9 +16,12 @@
 }
 #endif  // DISABLE_USER_INPUT_MONITOR
 
+UserInputMonitor::UserInputMonitor()
+    : monitoring_mouse_(false), key_press_counter_references_(0) {}
+
 UserInputMonitor::~UserInputMonitor() {
   DCHECK(!monitoring_mouse_);
-  DCHECK(!monitoring_keyboard_);
+  DCHECK(!key_press_counter_references_);
 }
 
 void UserInputMonitor::AddMouseListener(MouseEventListener* listener) {
@@ -30,58 +33,42 @@
     DVLOG(2) << "Started mouse monitoring.";
   }
 }
+
 void UserInputMonitor::RemoveMouseListener(MouseEventListener* listener) {
   base::AutoLock auto_lock(lock_);
   mouse_listeners_.RemoveObserver(listener);
-  if (mouse_listeners_.size() == 0) {
+  if (!mouse_listeners_.might_have_observers()) {
     StopMouseMonitoring();
     monitoring_mouse_ = false;
     DVLOG(2) << "Stopped mouse monitoring.";
   }
 }
-void UserInputMonitor::AddKeyStrokeListener(KeyStrokeListener* listener) {
+
+void UserInputMonitor::EnableKeyPressMonitoring() {
   base::AutoLock auto_lock(lock_);
-  key_stroke_listeners_.AddObserver(listener);
-  if (!monitoring_keyboard_) {
+  ++key_press_counter_references_;
+  if (key_press_counter_references_ == 1) {
     StartKeyboardMonitoring();
-    monitoring_keyboard_ = true;
     DVLOG(2) << "Started keyboard monitoring.";
   }
 }
-void UserInputMonitor::RemoveKeyStrokeListener(KeyStrokeListener* listener) {
+
+void UserInputMonitor::DisableKeyPressMonitoring() {
   base::AutoLock auto_lock(lock_);
-  key_stroke_listeners_.RemoveObserver(listener);
-  if (key_stroke_listeners_.size() == 0) {
+  DCHECK_NE(key_press_counter_references_, 0u);
+  --key_press_counter_references_;
+  if (key_press_counter_references_ == 0) {
     StopKeyboardMonitoring();
-    monitoring_keyboard_ = false;
     DVLOG(2) << "Stopped keyboard monitoring.";
   }
 }
 
-UserInputMonitor::UserInputMonitor()
-    : monitoring_mouse_(false), monitoring_keyboard_(false) {}
-
 void UserInputMonitor::OnMouseEvent(const SkIPoint& position) {
   base::AutoLock auto_lock(lock_);
+  if (!monitoring_mouse_)
+    return;
   FOR_EACH_OBSERVER(
       MouseEventListener, mouse_listeners_, OnMouseMoved(position));
 }
 
-void UserInputMonitor::OnKeyboardEvent(ui::EventType event,
-                                       ui::KeyboardCode key_code) {
-  base::AutoLock auto_lock(lock_);
-  // Updates the pressed keys and maybe notifies the key_stroke_listeners_.
-  if (event == ui::ET_KEY_PRESSED) {
-    if (pressed_keys_.find(key_code) != pressed_keys_.end())
-      return;
-    pressed_keys_.insert(key_code);
-    DVLOG(6) << "Key stroke detected.";
-    FOR_EACH_OBSERVER(KeyStrokeListener, key_stroke_listeners_, OnKeyStroke());
-  } else {
-    DCHECK_EQ(ui::ET_KEY_RELEASED, event);
-    DCHECK(pressed_keys_.find(key_code) != pressed_keys_.end());
-    pressed_keys_.erase(key_code);
-  }
-}
-
 }  // namespace media
diff --git a/media/base/user_input_monitor.h b/media/base/user_input_monitor.h
index 9eb82f3..567907c 100644
--- a/media/base/user_input_monitor.h
+++ b/media/base/user_input_monitor.h
@@ -5,16 +5,10 @@
 #ifndef MEDIA_BASE_USER_INPUT_MONITOR_H_
 #define MEDIA_BASE_USER_INPUT_MONITOR_H_
 
-#include <set>
-
-#include "base/memory/ref_counted.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/synchronization/lock.h"
 #include "media/base/media_export.h"
-#include "ui/base/events/event_constants.h"
-#include "ui/base/keycodes/keyboard_codes.h"
 
 struct SkIPoint;
 
@@ -28,6 +22,8 @@
 // Thread safe. The thread on which the listenters are called is not guaranteed.
 // The callers should not perform expensive/blocking tasks in the callback since
 // it might be called on the browser UI/IO threads.
+// The object must outlive the browser UI/IO threads to make sure the callbacks
+// will not access a deleted object.
 class MEDIA_EXPORT UserInputMonitor {
  public:
   // The interface to receive mouse movement events.
@@ -39,18 +35,8 @@
    protected:
     virtual ~MouseEventListener() {}
   };
-  // The interface to receive key stroke events.
-  class MEDIA_EXPORT KeyStrokeListener {
-   public:
-    // Called when any key is pressed. Called only once until the key is
-    // released, i.e. holding down a key for a long period will generate one
-    // callback just when the key is pressed down.
-    virtual void OnKeyStroke() = 0;
 
-   protected:
-    virtual ~KeyStrokeListener() {}
-  };
-
+  UserInputMonitor();
   virtual ~UserInputMonitor();
 
   // Creates a platform-specific instance of UserInputMonitor.
@@ -65,16 +51,24 @@
   // destroyed.
   void AddMouseListener(MouseEventListener* listener);
   void RemoveMouseListener(MouseEventListener* listener);
-  void AddKeyStrokeListener(KeyStrokeListener* listener);
-  void RemoveKeyStrokeListener(KeyStrokeListener* listener);
+
+  // A caller must call EnableKeyPressMonitoring and
+  // DisableKeyPressMonitoring in pair.
+  void EnableKeyPressMonitoring();
+  void DisableKeyPressMonitoring();
+
+  // Returns the number of keypresses. The starting point from when it is
+  // counted is not guaranteed, but consistent within the pair of calls of
+  // EnableKeyPressMonitoring and DisableKeyPressMonitoring. So a caller can
+  // use the difference between the values returned at two times to get the
+  // number of keypresses happened within that time period, but should not make
+  // any assumption on the initial value.
+  virtual size_t GetKeyPressCount() const = 0;
 
  protected:
-  UserInputMonitor();
-
   // Called by the platform-specific sub-classes to propagate the events to the
   // listeners.
   void OnMouseEvent(const SkIPoint& position);
-  void OnKeyboardEvent(ui::EventType event, ui::KeyboardCode key_code);
 
  private:
   virtual void StartMouseMonitoring() = 0;
@@ -84,12 +78,8 @@
 
   base::Lock lock_;
   ObserverList<MouseEventListener, true> mouse_listeners_;
-  ObserverList<KeyStrokeListener, true> key_stroke_listeners_;
   bool monitoring_mouse_;
-  bool monitoring_keyboard_;
-  // The set of keys currently held down. Used for convering raw keyboard events
-  // into KeyStrokeListener callbacks.
-  std::set<ui::KeyboardCode> pressed_keys_;
+  size_t key_press_counter_references_;
 
   DISALLOW_COPY_AND_ASSIGN(UserInputMonitor);
 };
diff --git a/media/base/user_input_monitor_linux.cc b/media/base/user_input_monitor_linux.cc
index ee1b774..196b4e7 100644
--- a/media/base/user_input_monitor_linux.cc
+++ b/media/base/user_input_monitor_linux.cc
@@ -15,12 +15,11 @@
 #include "base/compiler_specific.h"
 #include "base/location.h"
 #include "base/logging.h"
-#include "base/memory/weak_ptr.h"
 #include "base/message_loop/message_loop.h"
 #include "base/message_loop/message_pump_libevent.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/single_thread_task_runner.h"
-#include "base/threading/non_thread_safe.h"
+#include "media/base/keyboard_event_counter.h"
 #include "third_party/skia/include/core/SkPoint.h"
 #include "ui/base/keycodes/keyboard_code_conversion_x.h"
 
@@ -31,73 +30,54 @@
 #include <X11/extensions/record.h>
 
 namespace media {
-
 namespace {
 
-class UserInputMonitorLinux : public base::NonThreadSafe,
-                              public UserInputMonitor {
+class UserInputMonitorLinux : public UserInputMonitor ,
+                              public base::MessagePumpLibevent::Watcher {
  public:
-  UserInputMonitorLinux(
+  explicit UserInputMonitorLinux(
       const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner);
   virtual ~UserInputMonitorLinux();
 
+  virtual size_t GetKeyPressCount() const OVERRIDE;
+
  private:
   enum EventType {
     MOUSE_EVENT,
     KEYBOARD_EVENT
   };
 
-  // The actual implementation resides in UserInputMonitorLinux::Core class.
-  // Must be called on the io_task_runner thread.
-  class Core : public base::RefCountedThreadSafe<Core>,
-               public base::MessagePumpLibevent::Watcher {
-   public:
-    typedef const base::Callback<void(const SkIPoint&)> MouseCallback;
-    typedef base::Callback<void(ui::EventType event, ui::KeyboardCode key_code)>
-        KeyboardCallback;
-    Core(const MouseCallback& mouse_callback,
-         const KeyboardCallback& keyboard_callback);
-
-    void StartMonitor(EventType type);
-    void StopMonitor(EventType type);
-
-   private:
-    friend class base::RefCountedThreadSafe<Core>;
-    virtual ~Core();
-
-    // base::MessagePumpLibevent::Watcher interface.
-    virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
-    virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
-
-    // Processes key and mouse events.
-    void ProcessXEvent(xEvent* event);
-    static void ProcessReply(XPointer self, XRecordInterceptData* data);
-
-    // Used to receive base::MessagePumpLibevent::Watcher events.
-    base::MessagePumpLibevent::FileDescriptorWatcher controller_;
-
-    Display* display_;
-    Display* x_record_display_;
-    XRecordRange* x_record_range_[2];
-    XRecordContext x_record_context_;
-    base::Callback<void(const SkIPoint&)> mouse_callback_;
-    base::Callback<void(ui::EventType event, ui::KeyboardCode key_code)>
-        keyboard_callback_;
-
-    DISALLOW_COPY_AND_ASSIGN(Core);
-  };
-
   virtual void StartMouseMonitoring() OVERRIDE;
   virtual void StopMouseMonitoring() OVERRIDE;
   virtual void StartKeyboardMonitoring() OVERRIDE;
   virtual void StopKeyboardMonitoring() OVERRIDE;
 
-  void OnMouseEvent(const SkIPoint& position);
-  void OnKeyboardEvent(ui::EventType event, ui::KeyboardCode key_code);
+  //
+  // The following methods must be called on the IO thread.
+  //
+  void StartMonitor(EventType type);
+  void StopMonitor(EventType type);
+
+  // base::MessagePumpLibevent::Watcher interface.
+  virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
+  virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
+
+  // Processes key and mouse events.
+  void ProcessXEvent(xEvent* event);
+  static void ProcessReply(XPointer self, XRecordInterceptData* data);
 
   // Task runner on which X Window events are received.
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
-  scoped_refptr<Core> core_;
+
+  //
+  // The following members should only be accessed on the IO thread.
+  //
+  base::MessagePumpLibevent::FileDescriptorWatcher controller_;
+  Display* display_;
+  Display* x_record_display_;
+  XRecordRange* x_record_range_[2];
+  XRecordContext x_record_context_;
+  KeyboardEventCounter counter_;
 
   DISALLOW_COPY_AND_ASSIGN(UserInputMonitorLinux);
 };
@@ -105,54 +85,14 @@
 UserInputMonitorLinux::UserInputMonitorLinux(
     const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
     : io_task_runner_(io_task_runner),
-      core_(new Core(base::Bind(&UserInputMonitorLinux::OnMouseEvent,
-                                base::Unretained(this)),
-                     base::Bind(&UserInputMonitorLinux::OnKeyboardEvent,
-                                base::Unretained(this)))) {}
-
-UserInputMonitorLinux::~UserInputMonitorLinux() {}
-
-void UserInputMonitorLinux::StartMouseMonitoring() {
-  io_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&Core::StartMonitor, core_.get(), MOUSE_EVENT));
-}
-
-void UserInputMonitorLinux::StopMouseMonitoring() {
-  io_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&Core::StopMonitor, core_.get(), MOUSE_EVENT));
-}
-
-void UserInputMonitorLinux::StartKeyboardMonitoring() {
-  io_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&Core::StartMonitor, core_.get(), KEYBOARD_EVENT));
-}
-
-void UserInputMonitorLinux::StopKeyboardMonitoring() {
-  io_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&Core::StopMonitor, core_.get(), KEYBOARD_EVENT));
-}
-
-void UserInputMonitorLinux::OnMouseEvent(const SkIPoint& position) {
-  UserInputMonitor::OnMouseEvent(position);
-}
-
-void UserInputMonitorLinux::OnKeyboardEvent(ui::EventType event,
-                                            ui::KeyboardCode key_code) {
-  UserInputMonitor::OnKeyboardEvent(event, key_code);
-}
-
-UserInputMonitorLinux::Core::Core(const MouseCallback& mouse_callback,
-                                  const KeyboardCallback& keyboard_callback)
-    : display_(NULL),
+      display_(NULL),
       x_record_display_(NULL),
-      x_record_context_(0),
-      mouse_callback_(mouse_callback),
-      keyboard_callback_(keyboard_callback) {
+      x_record_context_(0) {
   x_record_range_[0] = NULL;
   x_record_range_[1] = NULL;
 }
 
-UserInputMonitorLinux::Core::~Core() {
+UserInputMonitorLinux::~UserInputMonitorLinux() {
   DCHECK(!display_);
   DCHECK(!x_record_display_);
   DCHECK(!x_record_range_[0]);
@@ -160,8 +100,64 @@
   DCHECK(!x_record_context_);
 }
 
-void UserInputMonitorLinux::Core::StartMonitor(EventType type) {
-  DCHECK(base::MessageLoopForIO::current());
+size_t UserInputMonitorLinux::GetKeyPressCount() const {
+  return counter_.GetKeyPressCount();
+}
+
+void UserInputMonitorLinux::StartMouseMonitoring() {
+  if (!io_task_runner_->BelongsToCurrentThread()) {
+    io_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&UserInputMonitorLinux::StartMonitor,
+                   base::Unretained(this),
+                   MOUSE_EVENT));
+    return;
+  }
+  StartMonitor(MOUSE_EVENT);
+}
+
+void UserInputMonitorLinux::StopMouseMonitoring() {
+  if (!io_task_runner_->BelongsToCurrentThread()) {
+    io_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&UserInputMonitorLinux::StopMonitor,
+                   base::Unretained(this),
+                   MOUSE_EVENT));
+    return;
+  }
+  StopMonitor(MOUSE_EVENT);
+}
+
+void UserInputMonitorLinux::StartKeyboardMonitoring() {
+  if (!io_task_runner_->BelongsToCurrentThread()) {
+    io_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&UserInputMonitorLinux::StartMonitor,
+                   base::Unretained(this),
+                   KEYBOARD_EVENT));
+    return;
+  }
+  StartMonitor(KEYBOARD_EVENT);
+}
+
+void UserInputMonitorLinux::StopKeyboardMonitoring() {
+  if (!io_task_runner_->BelongsToCurrentThread()) {
+    io_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&UserInputMonitorLinux::StopMonitor,
+                   base::Unretained(this),
+                   KEYBOARD_EVENT));
+    return;
+  }
+  StopMonitor(KEYBOARD_EVENT);
+}
+
+void UserInputMonitorLinux::StartMonitor(EventType type) {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+  if (type == KEYBOARD_EVENT)
+    counter_.Reset();
+
   // TODO(jamiewalch): We should pass the display in. At that point, since
   // XRecord needs a private connection to the X Server for its data channel
   // and both channels are used from a separate thread, we'll need to duplicate
@@ -226,7 +222,7 @@
 
   if (!XRecordEnableContextAsync(x_record_display_,
                                  x_record_context_,
-                                 &Core::ProcessReply,
+                                 &UserInputMonitorLinux::ProcessReply,
                                  reinterpret_cast<XPointer>(this))) {
     LOG(ERROR) << "XRecordEnableContextAsync failed.";
     return;
@@ -252,8 +248,8 @@
   OnFileCanReadWithoutBlocking(ConnectionNumber(x_record_display_));
 }
 
-void UserInputMonitorLinux::Core::StopMonitor(EventType type) {
-  DCHECK(base::MessageLoopForIO::current());
+void UserInputMonitorLinux::StopMonitor(EventType type) {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
 
   if (x_record_range_[type]) {
     XFree(x_record_range_[type]);
@@ -282,8 +278,8 @@
   }
 }
 
-void UserInputMonitorLinux::Core::OnFileCanReadWithoutBlocking(int fd) {
-  DCHECK(base::MessageLoopForIO::current());
+void UserInputMonitorLinux::OnFileCanReadWithoutBlocking(int fd) {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
   XEvent event;
   // Fetch pending events if any.
   while (XPending(x_record_display_)) {
@@ -291,15 +287,16 @@
   }
 }
 
-void UserInputMonitorLinux::Core::OnFileCanWriteWithoutBlocking(int fd) {
+void UserInputMonitorLinux::OnFileCanWriteWithoutBlocking(int fd) {
   NOTREACHED();
 }
 
-void UserInputMonitorLinux::Core::ProcessXEvent(xEvent* event) {
+void UserInputMonitorLinux::ProcessXEvent(xEvent* event) {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
   if (event->u.u.type == MotionNotify) {
     SkIPoint position(SkIPoint::Make(event->u.keyButtonPointer.rootX,
                                      event->u.keyButtonPointer.rootY));
-    mouse_callback_.Run(position);
+    OnMouseEvent(position);
   } else {
     ui::EventType type;
     if (event->u.u.type == KeyPress) {
@@ -308,20 +305,21 @@
       type = ui::ET_KEY_RELEASED;
     } else {
       NOTREACHED();
+      return;
     }
 
     KeySym key_sym = XkbKeycodeToKeysym(display_, event->u.u.detail, 0, 0);
     ui::KeyboardCode key_code = ui::KeyboardCodeFromXKeysym(key_sym);
-    keyboard_callback_.Run(type, key_code);
+    counter_.OnKeyboardEvent(type, key_code);
   }
 }
 
 // static
-void UserInputMonitorLinux::Core::ProcessReply(XPointer self,
-                                               XRecordInterceptData* data) {
+void UserInputMonitorLinux::ProcessReply(XPointer self,
+                                         XRecordInterceptData* data) {
   if (data->category == XRecordFromServer) {
     xEvent* event = reinterpret_cast<xEvent*>(data->data);
-    reinterpret_cast<Core*>(self)->ProcessXEvent(event);
+    reinterpret_cast<UserInputMonitorLinux*>(self)->ProcessXEvent(event);
   }
   XRecordFreeData(data);
 }
diff --git a/media/base/user_input_monitor_mac.cc b/media/base/user_input_monitor_mac.cc
new file mode 100644
index 0000000..3d19134
--- /dev/null
+++ b/media/base/user_input_monitor_mac.cc
@@ -0,0 +1,57 @@
+// 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 "media/base/user_input_monitor.h"
+
+#include <ApplicationServices/ApplicationServices.h>
+
+namespace media {
+namespace {
+
+class UserInputMonitorMac : public UserInputMonitor {
+ public:
+  UserInputMonitorMac();
+  virtual ~UserInputMonitorMac();
+
+  virtual size_t GetKeyPressCount() const OVERRIDE;
+
+ private:
+  virtual void StartMouseMonitoring() OVERRIDE;
+  virtual void StopMouseMonitoring() OVERRIDE;
+  virtual void StartKeyboardMonitoring() OVERRIDE;
+  virtual void StopKeyboardMonitoring() OVERRIDE;
+
+  DISALLOW_COPY_AND_ASSIGN(UserInputMonitorMac);
+};
+
+UserInputMonitorMac::UserInputMonitorMac() {}
+
+UserInputMonitorMac::~UserInputMonitorMac() {}
+
+size_t UserInputMonitorMac::GetKeyPressCount() const {
+  // Use |kCGEventSourceStateHIDSystemState| since we only want to count
+  // hardware generated events.
+  return CGEventSourceCounterForEventType(kCGEventSourceStateHIDSystemState,
+                                          kCGEventKeyDown);
+}
+
+// TODO(jiayl): add the impl.
+void UserInputMonitorMac::StartMouseMonitoring() { NOTIMPLEMENTED(); }
+
+// TODO(jiayl): add the impl.
+void UserInputMonitorMac::StopMouseMonitoring() { NOTIMPLEMENTED(); }
+
+void UserInputMonitorMac::StartKeyboardMonitoring() {}
+
+void UserInputMonitorMac::StopKeyboardMonitoring() {}
+
+}  // namespace
+
+scoped_ptr<UserInputMonitor> UserInputMonitor::Create(
+    const scoped_refptr<base::SingleThreadTaskRunner>& input_task_runner,
+    const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner) {
+  return scoped_ptr<UserInputMonitor>(new UserInputMonitorMac());
+}
+
+}  // namespace media
diff --git a/media/base/user_input_monitor_mac.mm b/media/base/user_input_monitor_mac.mm
deleted file mode 100644
index 4ffad42..0000000
--- a/media/base/user_input_monitor_mac.mm
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/base/user_input_monitor.h"
-
-namespace media {
-
-// TODO(jiayl): add the implementation.
-scoped_ptr<UserInputMonitor> UserInputMonitor::Create(
-    const scoped_refptr<base::SingleThreadTaskRunner>& input_task_runner,
-    const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner) {
-  return scoped_ptr<UserInputMonitor>();
-}
-
-}  // namespace media
diff --git a/media/base/video_frame.cc b/media/base/video_frame.cc
index 08e7e1a..a372889 100644
--- a/media/base/video_frame.cc
+++ b/media/base/video_frame.cc
@@ -124,9 +124,13 @@
     const gfx::Rect& visible_rect,
     const gfx::Size& natural_size,
     uint8* data,
+    size_t data_size,
     base::SharedMemoryHandle handle,
     base::TimeDelta timestamp,
     const base::Closure& no_longer_needed_cb) {
+  if (data_size < AllocationSize(format, coded_size))
+    return NULL;
+
   switch (format) {
     case I420: {
       scoped_refptr<VideoFrame> frame(new VideoFrame(
@@ -249,6 +253,39 @@
   return ((value + (alignment - 1)) & ~(alignment-1));
 }
 
+// static
+size_t VideoFrame::AllocationSize(Format format, const gfx::Size& coded_size) {
+  switch (format) {
+    case VideoFrame::RGB32:
+      return coded_size.GetArea() * 4;
+    case VideoFrame::YV12:
+    case VideoFrame::I420: {
+      const size_t rounded_size =
+          RoundUp(coded_size.width(), 2) * RoundUp(coded_size.height(), 2);
+      return rounded_size * 3 / 2;
+    }
+    case VideoFrame::YV12A: {
+      const size_t rounded_size =
+          RoundUp(coded_size.width(), 2) * RoundUp(coded_size.height(), 2);
+      return rounded_size * 5 / 2;
+    }
+    case VideoFrame::YV16: {
+      const size_t rounded_size =
+          RoundUp(coded_size.width(), 2) * RoundUp(coded_size.height(), 2);
+      return rounded_size * 2;
+    }
+    case VideoFrame::INVALID:
+    case VideoFrame::EMPTY:
+    case VideoFrame::NATIVE_TEXTURE:
+#if defined(GOOGLE_TV)
+    case VideoFrame::HOLE:
+#endif
+      break;
+  }
+  NOTREACHED() << "Unsupported video frame format: " << format;
+  return 0;
+}
+
 // Release data allocated by AllocateRGB() or AllocateYUV().
 static void ReleaseData(uint8* data) {
   DCHECK(data);
diff --git a/media/base/video_frame.h b/media/base/video_frame.h
index 82a08a9..df383d0 100644
--- a/media/base/video_frame.h
+++ b/media/base/video_frame.h
@@ -148,6 +148,7 @@
       const gfx::Rect& visible_rect,
       const gfx::Size& natural_size,
       uint8* data,
+      size_t data_size,
       base::SharedMemoryHandle handle,
       base::TimeDelta timestamp,
       const base::Closure& no_longer_needed_cb);
@@ -192,6 +193,10 @@
 
   static size_t NumPlanes(Format format);
 
+  // Returns the required allocation size for a (tightly packed) frame of the
+  // given coded size and format.
+  static size_t AllocationSize(Format format, const gfx::Size& coded_size);
+
   Format format() const { return format_; }
 
   const gfx::Size& coded_size() const { return coded_size_; }
diff --git a/media/cast/DEPS b/media/cast/DEPS
index e566fc7..8e10c67 100644
--- a/media/cast/DEPS
+++ b/media/cast/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
+  "+net",
   "+third_party/webrtc",
 ]
diff --git a/media/cast/cast.gyp b/media/cast/cast.gyp
index 229a833..776bb46 100644
--- a/media/cast/cast.gyp
+++ b/media/cast/cast.gyp
@@ -11,7 +11,7 @@
       'target_name': 'cast_config',
       'type': 'static_library',
       'include_dirs': [
-        '../..',
+        '<(DEPTH)/',
       ],
       'sources': [
         'cast_config.h',
@@ -44,15 +44,30 @@
           'dependencies': [
             'cast_sender',
             'cast_receiver',
-            '../../base/base.gyp:run_all_unittests',
+            'rtcp/rtcp.gyp:cast_rtcp_test',
+            '<(DEPTH)/base/base.gyp:run_all_unittests',
+            '<(DEPTH)/net/net.gyp:net',
             '<(DEPTH)/testing/gmock.gyp:gmock',
             '<(DEPTH)/testing/gtest.gyp:gtest',
           ],
           'include_dirs': [
-            '../..',
+            '<(DEPTH)/',
+            '<(DEPTH)/third_party/',
+            '<(DEPTH)/third_party/webrtc/',
           ],
           'sources': [
             'congestion_control/congestion_control_unittest.cc',
+            'framer/cast_message_builder_unittest.cc',
+            'framer/frame_buffer_unittest.cc',
+            'framer/framer_unittest.cc',
+            'rtp_sender/packet_storage/packet_storage_unittest.cc',
+            'rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc',
+            'rtp_sender/rtp_packetizer/test/rtp_header_parser.cc',
+            'rtp_sender/rtp_packetizer/test/rtp_header_parser.h',
+            'pacing/paced_sender_unittest.cc',
+            'rtcp/rtcp_receiver_unittest.cc',
+            'rtcp/rtcp_sender_unittest.cc',
+            'rtcp/rtcp_unittest.cc',
           ], # source
         },
       ],  # targets
diff --git a/media/cast/cast_receiver.gyp b/media/cast/cast_receiver.gyp
index 576f2d8..0fb5e38 100644
--- a/media/cast/cast_receiver.gyp
+++ b/media/cast/cast_receiver.gyp
@@ -19,7 +19,8 @@
       'dependencies': [
 #        'audio_receiver',
 #        'video_receiver',
-#        '<(DEPTH)/cast/pacing/paced_sender.gyp:*',
+        'framer/framer.gyp:cast_framer',
+        'pacing/paced_sender.gyp:paced_sender',
       ],
     },
   ],
diff --git a/media/cast/cast_sender.gyp b/media/cast/cast_sender.gyp
index 9bb37fe..c41bd64 100644
--- a/media/cast/cast_sender.gyp
+++ b/media/cast/cast_sender.gyp
@@ -18,10 +18,12 @@
 #        'cast_sender_impl.h',
       ], # source
       'dependencies': [
-#        '<(DEPTH)/media/cast/pacing/paced_sender.gyp:*',
-#        'audio_sender',
+#       'audio_sender',
         'congestion_control',
-#        'video_sender',
+        'pacing/paced_sender.gyp:paced_sender',
+        'rtcp/rtcp.gyp:cast_rtcp',
+        'rtp_sender/rtp_sender.gyp:cast_rtp_sender',
+#       'video_sender',
       ], # dependencies
     },
   ],
diff --git a/media/cast/congestion_control/congestion_control.cc b/media/cast/congestion_control/congestion_control.cc
index 68ba3b2..f8ca98c 100644
--- a/media/cast/congestion_control/congestion_control.cc
+++ b/media/cast/congestion_control/congestion_control.cc
@@ -28,7 +28,8 @@
       max_bitrate_configured_(max_bitrate_configured),
       min_bitrate_configured_(min_bitrate_configured),
       bitrate_(start_bitrate),
-      testing_clock_(NULL) {
+      default_tick_clock_(new base::DefaultTickClock()),
+      clock_(default_tick_clock_.get()) {
   DCHECK_GT(congestion_control_back_off, 0.0f) << "Invalid config";
   DCHECK_LT(congestion_control_back_off, 1.0f) << "Invalid config";
   DCHECK_GE(max_bitrate_configured, min_bitrate_configured) << "Invalid config";
@@ -37,8 +38,7 @@
 }
 
 bool CongestionControl::OnAck(base::TimeDelta rtt, uint32* new_bitrate) {
-  base::TimeTicks now = testing_clock_ ?
-      testing_clock_->NowTicks() : base::TimeTicks::Now();
+  base::TimeTicks now = clock_->NowTicks();
 
   // First feedback?
   if (time_last_increase_.is_null()) {
@@ -79,8 +79,7 @@
 }
 
 bool CongestionControl::OnNack(base::TimeDelta rtt, uint32* new_bitrate) {
-  base::TimeTicks now = testing_clock_ ?
-      testing_clock_->NowTicks() : base::TimeTicks::Now();
+  base::TimeTicks now = clock_->NowTicks();
 
   // First feedback?
   if (time_last_decrease_.is_null()) {
diff --git a/media/cast/congestion_control/congestion_control.gypi b/media/cast/congestion_control/congestion_control.gypi
index d429623..9f1accf 100644
--- a/media/cast/congestion_control/congestion_control.gypi
+++ b/media/cast/congestion_control/congestion_control.gypi
@@ -8,15 +8,15 @@
       'target_name': 'congestion_control',
       'type': 'static_library',
       'include_dirs': [
-        '../../../',
+        '<(DEPTH)/',
       ],
       'sources': [
         'congestion_control.h',
         'congestion_control.cc',
       ], # source
       'dependencies': [
-        '../../base/base.gyp:base',
-        '../../base/base.gyp:test_support_base',
+        '<(DEPTH)/base/base.gyp:base',
+        '<(DEPTH)/base/base.gyp:test_support_base',
       ],
   },
   ],
diff --git a/media/cast/congestion_control/congestion_control.h b/media/cast/congestion_control/congestion_control.h
index 41c04fb..f1b9b28 100644
--- a/media/cast/congestion_control/congestion_control.h
+++ b/media/cast/congestion_control/congestion_control.h
@@ -6,7 +6,9 @@
 #define MEDIA_CAST_CONGESTION_CONTROL_CONGESTION_CONTROL_H_
 
 #include "base/basictypes.h"
-#include "base/test/simple_test_tick_clock.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/default_tick_clock.h"
+#include "base/time/tick_clock.h"
 #include "base/time/time.h"
 
 namespace media {
@@ -28,8 +30,8 @@
   // Returns true if the bitrate have changed.
   bool OnNack(base::TimeDelta rtt_ms, uint32* new_bitrate);
 
-  void set_testing_clock(base::SimpleTestTickClock* clock) {
-    testing_clock_ = clock;
+  void set_clock(base::TickClock* clock) {
+    clock_ = clock;
   }
 
  private:
@@ -40,8 +42,8 @@
   base::TimeTicks time_last_increase_;
   base::TimeTicks time_last_decrease_;
 
-  // Used only for helping test. Not owned and can be NULL.
-  base::SimpleTestTickClock* testing_clock_;
+  scoped_ptr<base::TickClock> default_tick_clock_;
+  base::TickClock* clock_;
 
   DISALLOW_COPY_AND_ASSIGN(CongestionControl);
 };
diff --git a/media/cast/congestion_control/congestion_control_unittest.cc b/media/cast/congestion_control/congestion_control_unittest.cc
index f55a39a..eff0a8c 100644
--- a/media/cast/congestion_control/congestion_control_unittest.cc
+++ b/media/cast/congestion_control/congestion_control_unittest.cc
@@ -27,7 +27,7 @@
                             kStartBitrate) {
     testing_clock_.Advance(
         base::TimeDelta::FromMilliseconds(kStartMillisecond));
-    congestion_control_.set_testing_clock(&testing_clock_);
+    congestion_control_.set_clock(&testing_clock_);
   }
 
   base::SimpleTestTickClock testing_clock_;
diff --git a/media/cast/framer/cast_message_builder.cc b/media/cast/framer/cast_message_builder.cc
new file mode 100644
index 0000000..eec1211
--- /dev/null
+++ b/media/cast/framer/cast_message_builder.cc
@@ -0,0 +1,193 @@
+// 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 "media/cast/framer/cast_message_builder.h"
+
+namespace media {
+namespace cast {
+
+static const uint16 kCompleteFrameLost = 0xffff;
+
+CastMessageBuilder::CastMessageBuilder(
+    RtpPayloadFeedback* incoming_payload_feedback,
+    FrameIdMap* frame_id_map,
+    uint32 media_ssrc,
+    bool decoder_faster_than_max_frame_rate,
+    int max_unacked_frames)
+    : cast_feedback_(incoming_payload_feedback),
+      frame_id_map_(frame_id_map),
+      media_ssrc_(media_ssrc),
+      decoder_faster_than_max_frame_rate_(decoder_faster_than_max_frame_rate),
+      max_unacked_frames_(max_unacked_frames),
+      cast_msg_(media_ssrc),
+      waiting_for_key_frame_(true),
+      slowing_down_ack_(false),
+      acked_last_frame_(true),
+      last_acked_frame_id_(kStartFrameId),
+      default_tick_clock_(new base::DefaultTickClock()),
+      clock_(default_tick_clock_.get()) {
+  cast_msg_.ack_frame_id_ = kStartFrameId;
+}
+
+CastMessageBuilder::~CastMessageBuilder() {}
+
+void CastMessageBuilder::CompleteFrameReceived(uint8 frame_id,
+                                               bool is_key_frame) {
+  if (last_update_time_.is_null()) {
+    // Our first update.
+    last_update_time_ = clock_->NowTicks();
+  }
+  if (waiting_for_key_frame_) {
+    if (!is_key_frame) {
+      // Ignore that we have received this complete frame since we are
+      // waiting on a key frame.
+      return;
+    }
+    waiting_for_key_frame_ = false;
+    cast_msg_.missing_frames_and_packets_.clear();
+    cast_msg_.ack_frame_id_ = frame_id;
+    last_update_time_ = clock_->NowTicks();
+    // We might have other complete frames waiting after we receive the last
+    // packet in the key-frame.
+    UpdateAckMessage();
+  } else {
+    if (!UpdateAckMessage())
+      return;
+
+    BuildPacketList();
+  }
+  // Send cast message.
+  cast_feedback_->CastFeedback(cast_msg_);
+}
+
+bool CastMessageBuilder::UpdateAckMessage() {
+  if (!decoder_faster_than_max_frame_rate_) {
+    int complete_frame_count = frame_id_map_->NumberOfCompleteFrames();
+    if (complete_frame_count > max_unacked_frames_) {
+      // We have too many frames pending in our framer; slow down ACK.
+      slowing_down_ack_ = true;
+    } else if (complete_frame_count <= 1) {
+      // We are down to one or less frames in our framer; ACK normally.
+      slowing_down_ack_ = false;
+    }
+  }
+  if (slowing_down_ack_) {
+    // We are slowing down acknowledgment by acknowledging every other frame.
+    if (acked_last_frame_) {
+      acked_last_frame_ = false;
+    } else {
+      acked_last_frame_ = true;
+      last_acked_frame_id_++;
+      // Note: frame skipping and slowdown ACK is not supported at the same
+      // time; and it's not needed since we can skip frames to catch up.
+    }
+  } else {
+    uint8 frame_id = frame_id_map_->LastContinuousFrame();
+
+    // Is it a new frame?
+    if (last_acked_frame_id_ == frame_id) return false;
+
+    last_acked_frame_id_ = frame_id;
+    acked_last_frame_ = true;
+  }
+  cast_msg_.ack_frame_id_ = last_acked_frame_id_;
+  cast_msg_.missing_frames_and_packets_.clear();
+  last_update_time_ = clock_->NowTicks();
+  return true;
+}
+
+bool CastMessageBuilder::TimeToSendNextCastMessage(
+    base::TimeTicks* time_to_send) {
+  // We haven't received any packets.
+  if (last_update_time_.is_null() && frame_id_map_->Empty()) return false;
+
+  *time_to_send = last_update_time_ +
+      base::TimeDelta::FromMilliseconds(kCastMessageUpdateIntervalMs);
+  return true;
+}
+
+void CastMessageBuilder::UpdateCastMessage() {
+  RtcpCastMessage message(media_ssrc_);
+  if (!UpdateCastMessageInternal(&message)) return;
+
+  // Send cast message.
+  cast_feedback_->CastFeedback(message);
+}
+
+void CastMessageBuilder::Reset() {
+  waiting_for_key_frame_ = true;
+  cast_msg_.ack_frame_id_ = kStartFrameId;
+  cast_msg_.missing_frames_and_packets_.clear();
+  time_last_nacked_map_.clear();
+}
+
+bool CastMessageBuilder::UpdateCastMessageInternal(RtcpCastMessage* message) {
+  if (last_update_time_.is_null()) {
+    if (!frame_id_map_->Empty()) {
+      // We have received packets.
+      last_update_time_ = clock_->NowTicks();
+    }
+    return false;
+  }
+  // Is it time to update the cast message?
+  base::TimeTicks now = clock_->NowTicks();
+  if (now - last_update_time_ <
+      base::TimeDelta::FromMilliseconds(kCastMessageUpdateIntervalMs)) {
+    return false;
+  }
+  last_update_time_ = now;
+
+  UpdateAckMessage();  // Needed to cover when a frame is skipped.
+  BuildPacketList();
+  *message = cast_msg_;
+  return true;
+}
+
+void CastMessageBuilder::BuildPacketList() {
+  base::TimeTicks now = clock_->NowTicks();
+
+  // Clear message NACK list.
+  cast_msg_.missing_frames_and_packets_.clear();
+
+  // Are we missing packets?
+  if (frame_id_map_->Empty()) return;
+
+  uint8 newest_frame_id = frame_id_map_->NewestFrameId();
+  uint8 next_expected_frame_id =
+      static_cast<uint8>(cast_msg_.ack_frame_id_ + 1);
+
+  // Iterate over all frames.
+  for (; !IsNewerFrameId(next_expected_frame_id, newest_frame_id);
+       ++next_expected_frame_id) {
+    TimeLastNackMap::iterator it =
+        time_last_nacked_map_.find(next_expected_frame_id);
+    if (it != time_last_nacked_map_.end()) {
+      // We have sent a NACK in this frame before, make sure enough time have
+      // passed.
+      if (now - it->second <
+          base::TimeDelta::FromMilliseconds(kNackRepeatIntervalMs)) {
+        continue;
+      }
+    }
+
+    PacketIdSet missing;
+    if (frame_id_map_->FrameExists(next_expected_frame_id)) {
+      bool last_frame = (newest_frame_id == next_expected_frame_id);
+      frame_id_map_->GetMissingPackets(next_expected_frame_id, last_frame,
+                                       &missing);
+      if (!missing.empty()) {
+        time_last_nacked_map_[next_expected_frame_id] = now;
+        cast_msg_.missing_frames_and_packets_.insert(
+            std::make_pair(next_expected_frame_id, missing));
+      }
+    } else {
+      time_last_nacked_map_[next_expected_frame_id] = now;
+      missing.insert(kCompleteFrameLost);
+      cast_msg_.missing_frames_and_packets_[next_expected_frame_id] = missing;
+    }
+  }
+}
+
+}  //  namespace cast
+}  //  namespace media
diff --git a/media/cast/framer/cast_message_builder.h b/media/cast/framer/cast_message_builder.h
new file mode 100644
index 0000000..b941178
--- /dev/null
+++ b/media/cast/framer/cast_message_builder.h
@@ -0,0 +1,73 @@
+// 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.
+
+// Handles NACK list and manages ACK.
+
+#ifndef MEDIA_CAST_FRAMER_CAST_MESSAGE_BUILDER_H_
+#define MEDIA_CAST_FRAMER_CAST_MESSAGE_BUILDER_H_
+
+#include <map>
+
+#include "media/cast/framer/frame_id_map.h"
+#include "media/cast/rtcp/rtcp.h"
+#include "media/cast/rtp_common/rtp_defines.h"
+
+namespace media {
+namespace cast {
+
+class RtpPayloadFeedback;
+
+typedef std::map<uint8, base::TimeTicks> TimeLastNackMap;
+
+class CastMessageBuilder {
+ public:
+  CastMessageBuilder(RtpPayloadFeedback* incoming_payload_feedback,
+                     FrameIdMap* frame_id_map,
+                     uint32 media_ssrc,
+                     bool decoder_faster_than_max_frame_rate,
+                     int max_unacked_frames);
+  ~CastMessageBuilder();
+
+  void CompleteFrameReceived(uint8 frame_id, bool is_key_frame);
+  bool TimeToSendNextCastMessage(base::TimeTicks* time_to_send);
+  void UpdateCastMessage();
+  void Reset();
+
+  void set_clock(base::TickClock* clock) {
+    clock_ = clock;
+  }
+
+ private:
+  bool UpdateAckMessage();
+  void BuildPacketList();
+  bool UpdateCastMessageInternal(RtcpCastMessage* message);
+
+  RtpPayloadFeedback* const cast_feedback_;
+
+  // CastMessageBuilder has only const access to the frame id mapper.
+  const FrameIdMap* const frame_id_map_;
+  const uint32 media_ssrc_;
+  const bool decoder_faster_than_max_frame_rate_;
+  const int max_unacked_frames_;
+
+  RtcpCastMessage cast_msg_;
+  base::TimeTicks last_update_time_;
+  bool waiting_for_key_frame_;
+
+  TimeLastNackMap time_last_nacked_map_;
+
+  bool slowing_down_ack_;
+  bool acked_last_frame_;
+  uint8 last_acked_frame_id_;
+
+  scoped_ptr<base::TickClock> default_tick_clock_;
+  base::TickClock* clock_;
+
+  DISALLOW_COPY_AND_ASSIGN(CastMessageBuilder);
+};
+
+}  // namespace cast
+}  // namespace media
+
+#endif  //  MEDIA_CAST_FRAMER_CAST_MESSAGE_BUILDER_H_
diff --git a/media/cast/framer/cast_message_builder_unittest.cc b/media/cast/framer/cast_message_builder_unittest.cc
new file mode 100644
index 0000000..13c6a46
--- /dev/null
+++ b/media/cast/framer/cast_message_builder_unittest.cc
@@ -0,0 +1,513 @@
+// 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 "base/memory/scoped_ptr.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "media/cast/framer/cast_message_builder.h"
+#include "media/cast/rtcp/rtcp.h"
+#include "media/cast/rtp_common/rtp_defines.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+namespace cast {
+
+static const uint32 kSsrc = 0x1234;
+static const uint32 kShortTimeIncrementMs = 10;
+static const uint32 kLongTimeIncrementMs = 40;
+static const int64 kStartMillisecond = 123456789;
+
+typedef std::map<uint8, int> MissingPacketsMap;
+
+class NackFeedbackVerification : public RtpPayloadFeedback {
+ public:
+  NackFeedbackVerification()
+      : triggered_(false),
+        missing_packets_(),
+        last_frame_acked_(0) {}
+
+
+  virtual void RequestKeyFrame() OVERRIDE {
+    request_key_frame_ = true;
+  }
+
+  virtual void CastFeedback(const RtcpCastMessage& cast_feedback) OVERRIDE {
+    EXPECT_EQ(kSsrc, cast_feedback.media_ssrc_);
+
+    last_frame_acked_ = cast_feedback.ack_frame_id_;
+
+    MissingFramesAndPacketsMap::const_iterator frame_it =
+        cast_feedback.missing_frames_and_packets_.begin();
+
+    // Keep track of the number of missing packets per frame.
+    missing_packets_.clear();
+    while (frame_it != cast_feedback.missing_frames_and_packets_.end()) {
+      missing_packets_.insert(
+          std::make_pair(frame_it->first, frame_it->second.size()));
+      ++frame_it;
+    }
+    triggered_ = true;
+  }
+
+  int num_missing_packets(uint8 frame_id) {
+    MissingPacketsMap::iterator it;
+    it = missing_packets_.find(frame_id);
+    if (it == missing_packets_.end()) return 0;
+
+    return it->second;
+  }
+
+  // Holds value for one call.
+  bool triggered() {
+    bool ret_val =  triggered_;
+    triggered_ = false;
+    return ret_val;
+  }
+
+  bool request_key_frame() {
+    bool ret_val = request_key_frame_;
+    request_key_frame_ = false;
+    return ret_val;
+  }
+  uint8 last_frame_acked() { return last_frame_acked_; }
+
+ private:
+  bool triggered_;
+  MissingPacketsMap missing_packets_;  // Missing packets per frame.
+  uint8 last_frame_acked_;
+  bool request_key_frame_;
+};
+
+class CastMessageBuilderTest : public ::testing::Test {
+ protected:
+  CastMessageBuilderTest()
+      : cast_msg_builder_(new CastMessageBuilder(&feedback_,
+                                                 &frame_id_map_,
+                                                 kSsrc,
+                                                 true,
+                                                 0)) {
+    rtp_header_.webrtc.header.ssrc = kSsrc;
+    rtp_header_.is_key_frame = false;
+    testing_clock_.Advance(
+        base::TimeDelta::FromMilliseconds(kStartMillisecond));
+    cast_msg_builder_->set_clock(&testing_clock_);
+  }
+
+  ~CastMessageBuilderTest() {}
+
+  void SetFrameId(uint8 frame_id) {
+    rtp_header_.frame_id = frame_id;
+  }
+
+  void SetPacketId(uint16 packet_id) {
+    rtp_header_.packet_id = packet_id;
+  }
+
+  void SetMaxPacketId(uint16 max_packet_id) {
+    rtp_header_.max_packet_id = max_packet_id;
+  }
+
+  void SetKeyFrame(bool is_key) {
+    rtp_header_.is_key_frame = is_key;
+  }
+
+  void SetReferenceFrameId(uint8 reference_frame_id) {
+    rtp_header_.is_reference = true;
+    rtp_header_.reference_frame_id = reference_frame_id;
+  }
+
+  void InsertPacket() {
+    bool complete = false;
+    frame_id_map_.InsertPacket(rtp_header_, &complete);
+    if (complete) {
+      cast_msg_builder_->CompleteFrameReceived(rtp_header_.frame_id,
+                                               rtp_header_.is_key_frame);
+    }
+    cast_msg_builder_->UpdateCastMessage();
+  }
+
+  void SetDecoderSlowerThanMaxFrameRate(int max_unacked_frames) {
+    cast_msg_builder_.reset(new CastMessageBuilder(&feedback_,
+                                                   &frame_id_map_,
+                                                   kSsrc,
+                                                   false,
+                                                   max_unacked_frames));
+  }
+
+  NackFeedbackVerification feedback_;
+  scoped_ptr<CastMessageBuilder> cast_msg_builder_;
+  RtpCastHeader rtp_header_;
+  FrameIdMap frame_id_map_;
+  base::SimpleTestTickClock testing_clock_;
+};
+
+TEST_F(CastMessageBuilderTest, StartWithAKeyFrame) {
+  SetFrameId(3);
+  SetPacketId(0);
+  SetMaxPacketId(0);
+  InsertPacket();
+  // Should not trigger ack.
+  EXPECT_FALSE(feedback_.triggered());
+  SetFrameId(5);
+  SetPacketId(0);
+  SetMaxPacketId(0);
+  SetKeyFrame(true);
+  InsertPacket();
+  frame_id_map_.RemoveOldFrames(5);  // Simulate 5 being pulled for rendering.
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  cast_msg_builder_->UpdateCastMessage();
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(5, feedback_.last_frame_acked());
+}
+
+TEST_F(CastMessageBuilderTest, OneFrameNackList) {
+  SetFrameId(0);
+  SetPacketId(4);
+  SetMaxPacketId(10);
+  InsertPacket();
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
+  EXPECT_FALSE(feedback_.triggered());
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  SetPacketId(5);
+  InsertPacket();
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(4, feedback_.num_missing_packets(0));
+}
+
+TEST_F(CastMessageBuilderTest, CompleteFrameMissing) {
+  // TODO(mikhal): Add indication.
+  SetFrameId(0);
+  SetPacketId(2);
+  SetMaxPacketId(5);
+  InsertPacket();
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  SetFrameId(2);
+  SetPacketId(2);
+  SetMaxPacketId(5);
+  InsertPacket();
+}
+
+TEST_F(CastMessageBuilderTest, FastForwardAck) {
+  SetFrameId(1);
+  SetPacketId(0);
+  SetMaxPacketId(0);
+  InsertPacket();
+  EXPECT_FALSE(feedback_.triggered());
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  SetFrameId(2);
+  SetPacketId(0);
+  SetMaxPacketId(0);
+  InsertPacket();
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(255, feedback_.last_frame_acked());
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  SetFrameId(0);
+  SetPacketId(0);
+  SetMaxPacketId(0);
+  SetKeyFrame(true);
+  InsertPacket();
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(2, feedback_.last_frame_acked());
+}
+
+TEST_F(CastMessageBuilderTest, RemoveOldFrames) {
+  SetFrameId(1);
+  SetPacketId(0);
+  SetMaxPacketId(1);
+  InsertPacket();
+  EXPECT_FALSE(feedback_.triggered());
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  SetFrameId(2);
+  SetPacketId(0);
+  SetMaxPacketId(0);
+  InsertPacket();
+  EXPECT_TRUE(feedback_.triggered());
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  SetFrameId(3);
+  SetPacketId(0);
+  SetMaxPacketId(5);
+  InsertPacket();
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(255, feedback_.last_frame_acked());
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  SetFrameId(5);
+  SetPacketId(0);
+  SetMaxPacketId(0);
+  SetKeyFrame(true);
+  InsertPacket();
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  frame_id_map_.RemoveOldFrames(5);  // Simulate 5 being pulled for rendering.
+  cast_msg_builder_->UpdateCastMessage();
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(5, feedback_.last_frame_acked());
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
+  SetFrameId(1);
+  SetPacketId(1);
+  SetMaxPacketId(1);
+  InsertPacket();
+  EXPECT_FALSE(feedback_.triggered());
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  InsertPacket();
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(5, feedback_.last_frame_acked());
+}
+
+TEST_F(CastMessageBuilderTest, WrapFastForward) {
+  SetFrameId(254);
+  SetPacketId(0);
+  SetMaxPacketId(1);
+  SetKeyFrame(true);
+  InsertPacket();
+  EXPECT_FALSE(feedback_.triggered());
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  SetFrameId(255);
+  SetPacketId(0);
+  SetMaxPacketId(0);
+  SetKeyFrame(false);
+  InsertPacket();
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(253, feedback_.last_frame_acked());
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  SetFrameId(0);
+  SetPacketId(0);
+  SetMaxPacketId(0);
+  SetKeyFrame(false);
+  InsertPacket();
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(253, feedback_.last_frame_acked());
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  SetFrameId(254);
+  SetPacketId(1);
+  SetMaxPacketId(1);
+  SetKeyFrame(true);
+  InsertPacket();
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(0, feedback_.last_frame_acked());
+}
+
+TEST_F(CastMessageBuilderTest, NackUntilMaxReceivedPacket) {
+  SetFrameId(0);
+  SetPacketId(0);
+  SetMaxPacketId(20);
+  SetKeyFrame(true);
+  InsertPacket();
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  SetPacketId(5);
+  InsertPacket();
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(4, feedback_.num_missing_packets(0));
+}
+
+TEST_F(CastMessageBuilderTest, NackUntilMaxReceivedPacketNextFrame) {
+  SetFrameId(0);
+  SetPacketId(0);
+  SetMaxPacketId(20);
+  SetKeyFrame(true);
+  InsertPacket();
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  SetPacketId(5);
+  InsertPacket();
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(4, feedback_.num_missing_packets(0));
+  SetFrameId(1);
+  SetMaxPacketId(2);
+  SetPacketId(0);
+  SetKeyFrame(false);
+  InsertPacket();
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(21 - 2, feedback_.num_missing_packets(0));
+}
+
+TEST_F(CastMessageBuilderTest, NackUntilMaxReceivedPacketNextKey) {
+  SetFrameId(0);
+  SetPacketId(0);
+  SetMaxPacketId(20);
+  SetKeyFrame(true);
+  InsertPacket();
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  SetPacketId(5);
+  InsertPacket();
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(4, feedback_.num_missing_packets(0));
+  SetFrameId(1);
+  SetMaxPacketId(0);
+  SetPacketId(0);
+  SetKeyFrame(true);
+  InsertPacket();
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(0, feedback_.num_missing_packets(0));
+}
+
+TEST_F(CastMessageBuilderTest, Reset) {
+  InsertPacket();
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  cast_msg_builder_->Reset();
+  frame_id_map_.Clear();
+  // Should reset nack list state and request a key frame.
+  cast_msg_builder_->UpdateCastMessage();
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(0, feedback_.num_missing_packets(0));
+}
+
+TEST_F(CastMessageBuilderTest, DeltaAfterReset) {
+  SetFrameId(0);
+  SetPacketId(0);
+  SetMaxPacketId(0);
+  SetKeyFrame(true);
+  InsertPacket();
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(0, feedback_.num_missing_packets(0));
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  cast_msg_builder_->Reset();
+  SetFrameId(1);
+  SetPacketId(0);
+  SetMaxPacketId(0);
+  SetKeyFrame(true);
+  EXPECT_FALSE(feedback_.triggered());
+}
+
+TEST_F(CastMessageBuilderTest, BasicRps) {
+  SetFrameId(0);
+  SetPacketId(0);
+  SetMaxPacketId(0);
+  SetKeyFrame(true);
+  InsertPacket();
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(0, feedback_.last_frame_acked());
+  SetFrameId(3);
+  SetKeyFrame(false);
+  SetReferenceFrameId(0);
+  InsertPacket();
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(0, feedback_.last_frame_acked());
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kLongTimeIncrementMs));
+  frame_id_map_.RemoveOldFrames(3);  // Simulate 3 being pulled for rendering.
+  cast_msg_builder_->UpdateCastMessage();
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(3, feedback_.last_frame_acked());
+}
+
+TEST_F(CastMessageBuilderTest, InOrderRps) {
+  // Create a pattern - skip to rps, and don't look back.
+  SetFrameId(0);
+  SetPacketId(0);
+  SetMaxPacketId(0);
+  SetKeyFrame(true);
+  InsertPacket();
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(0, feedback_.last_frame_acked());
+  SetFrameId(1);
+  SetPacketId(0);
+  SetMaxPacketId(1);
+  SetKeyFrame(false);
+  InsertPacket();
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
+  EXPECT_FALSE(feedback_.triggered());
+  SetFrameId(3);
+  SetPacketId(0);
+  SetMaxPacketId(0);
+  SetKeyFrame(false);
+  SetReferenceFrameId(0);
+  InsertPacket();
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
+  frame_id_map_.RemoveOldFrames(3);  // Simulate 3 being pulled for rendering.
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
+  cast_msg_builder_->UpdateCastMessage();
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(3, feedback_.last_frame_acked());
+  // Make an old frame complete - should not trigger an ack.
+  SetFrameId(1);
+  SetPacketId(1);
+  SetMaxPacketId(1);
+  SetKeyFrame(false);
+  InsertPacket();
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
+  EXPECT_FALSE(feedback_.triggered());
+  EXPECT_EQ(3, feedback_.last_frame_acked());
+}
+
+TEST_F(CastMessageBuilderTest, SlowDownAck) {
+  SetDecoderSlowerThanMaxFrameRate(3);
+  SetFrameId(0);
+  SetPacketId(0);
+  SetMaxPacketId(0);
+  SetKeyFrame(true);
+  InsertPacket();
+
+  int frame_id;
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
+  SetKeyFrame(false);
+  for (frame_id = 1; frame_id < 3; ++frame_id) {
+    EXPECT_TRUE(feedback_.triggered());
+    EXPECT_EQ(frame_id - 1, feedback_.last_frame_acked());
+    SetFrameId(frame_id);
+    InsertPacket();
+    testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
+  }
+  // We should now have entered the slowdown ACK state.
+  uint8_t expected_frame_id = 1;
+  for (; frame_id < 10; ++frame_id) {
+    if (frame_id % 2)  ++expected_frame_id;
+    EXPECT_TRUE(feedback_.triggered());
+    EXPECT_EQ(expected_frame_id, feedback_.last_frame_acked());
+    SetFrameId(frame_id);
+    InsertPacket();
+    testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
+  }
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(expected_frame_id, feedback_.last_frame_acked());
+
+  // Simulate frame_id being pulled for rendering.
+  frame_id_map_.RemoveOldFrames(frame_id);
+  // We should now leave the slowdown ACK state.
+  ++frame_id;
+  SetFrameId(frame_id);
+  InsertPacket();
+  testing_clock_.Advance(
+      base::TimeDelta::FromMilliseconds(kShortTimeIncrementMs));
+  EXPECT_TRUE(feedback_.triggered());
+  EXPECT_EQ(frame_id, feedback_.last_frame_acked());
+}
+
+}  // namespace cast
+}  // namespace media
diff --git a/media/cast/framer/frame_buffer.cc b/media/cast/framer/frame_buffer.cc
new file mode 100644
index 0000000..ed7e11f
--- /dev/null
+++ b/media/cast/framer/frame_buffer.cc
@@ -0,0 +1,103 @@
+// 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 "media/cast/framer/frame_buffer.h"
+
+namespace media {
+namespace cast {
+
+FrameBuffer::FrameBuffer()
+    : frame_id_(0),
+      max_packet_id_(0),
+      num_packets_received_(0),
+      is_key_frame_(false),
+      total_data_size_(0),
+      last_referenced_frame_id_(0),
+      packets_() {}
+
+FrameBuffer::~FrameBuffer() {}
+
+void FrameBuffer::InsertPacket(const uint8* payload_data,
+                               int payload_size,
+                               const RtpCastHeader& rtp_header) {
+  // Is this the first packet in the frame?
+  if (packets_.empty()) {
+    frame_id_ = rtp_header.frame_id;
+    max_packet_id_ = rtp_header.max_packet_id;
+    is_key_frame_ = rtp_header.is_key_frame;
+    if (rtp_header.is_reference) {
+      last_referenced_frame_id_ = rtp_header.reference_frame_id;
+    } else {
+      last_referenced_frame_id_ = static_cast<uint8>(rtp_header.frame_id - 1);
+    }
+
+    rtp_timestamp_ = rtp_header.webrtc.header.timestamp;
+  }
+  // Is this the correct frame?
+  if (rtp_header.frame_id != frame_id_) return;
+
+  // Insert every packet only once.
+  if (packets_.find(rtp_header.packet_id) != packets_.end()) return;
+
+  std::vector<uint8> data;
+  std::pair<PacketMap::iterator, bool> retval =
+      packets_.insert(make_pair(rtp_header.packet_id, data));
+
+  // Insert the packet.
+  retval.first->second.resize(payload_size);
+  std::copy(payload_data, payload_data + payload_size,
+            retval.first->second.begin());
+
+  ++num_packets_received_;
+  total_data_size_ += payload_size;
+}
+
+bool FrameBuffer::Complete() const {
+  return num_packets_received_ - 1 == max_packet_id_;
+}
+
+bool FrameBuffer::GetEncodedAudioFrame(EncodedAudioFrame* audio_frame,
+                                       uint32* rtp_timestamp) const {
+  if (!Complete()) return false;
+
+  *rtp_timestamp = rtp_timestamp_;
+
+  // Frame is complete -> construct.
+  audio_frame->frame_id = frame_id_;
+
+  // Build the data vector.
+  audio_frame->data.clear();
+  audio_frame->data.reserve(total_data_size_);
+  PacketMap::const_iterator it;
+  for (it = packets_.begin(); it != packets_.end(); ++it) {
+    audio_frame->data.insert(audio_frame->data.end(),
+                             it->second.begin(), it->second.end());
+  }
+  return true;
+}
+
+bool FrameBuffer::GetEncodedVideoFrame(EncodedVideoFrame* video_frame,
+                                       uint32* rtp_timestamp) const {
+  if (!Complete()) return false;
+
+  *rtp_timestamp = rtp_timestamp_;
+
+  // Frame is complete -> construct.
+  video_frame->key_frame = is_key_frame_;
+  video_frame->frame_id = frame_id_;
+  video_frame->last_referenced_frame_id = last_referenced_frame_id_;
+
+  // Build the data vector.
+  video_frame->data.clear();
+  video_frame->data.reserve(total_data_size_);
+  PacketMap::const_iterator it;
+  for (it = packets_.begin(); it != packets_.end(); ++it) {
+    video_frame->data.insert(video_frame->data.end(),
+                             it->second.begin(), it->second.end());
+  }
+  return true;
+}
+
+}  // namespace cast
+}  // namespace media
diff --git a/media/cast/framer/frame_buffer.h b/media/cast/framer/frame_buffer.h
new file mode 100644
index 0000000..d2b52cb
--- /dev/null
+++ b/media/cast/framer/frame_buffer.h
@@ -0,0 +1,54 @@
+// 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 MEDIA_CAST_FRAMER_FRAME_BUFFER
+#define MEDIA_CAST_FRAMER_FRAME_BUFFER
+
+#include <map>
+#include <vector>
+
+#include "media/cast/cast_config.h"
+#include "media/cast/rtp_common/rtp_defines.h"
+
+namespace media {
+namespace cast {
+
+typedef std::map<uint16, std::vector<uint8> > PacketMap;
+
+class FrameBuffer {
+ public:
+  FrameBuffer();
+  ~FrameBuffer();
+  void InsertPacket(const uint8* payload_data,
+                    int payload_size,
+                    const RtpCastHeader& rtp_header);
+  bool Complete() const;
+
+  bool GetEncodedAudioFrame(EncodedAudioFrame* audio_frame,
+                            uint32* rtp_timestamp) const;
+
+  bool GetEncodedVideoFrame(EncodedVideoFrame* video_frame,
+                            uint32* rtp_timestamp) const;
+
+  bool is_key_frame() const { return is_key_frame_; }
+  uint8 frame_id() const { return frame_id_; }
+  uint8 last_referenced_frame_id() const { return last_referenced_frame_id_; }
+
+ private:
+  uint8 frame_id_;
+  uint16 max_packet_id_;
+  uint16 num_packets_received_;
+  bool is_key_frame_;
+  int total_data_size_;
+  uint8 last_referenced_frame_id_;
+  uint32 rtp_timestamp_;
+  PacketMap packets_;
+
+  DISALLOW_COPY_AND_ASSIGN(FrameBuffer);
+};
+
+}  // namespace cast
+}  // namespace media
+
+#endif  // MEDIA_CAST_FRAMER_FRAME_BUFFER
diff --git a/media/cast/framer/frame_buffer_unittest.cc b/media/cast/framer/frame_buffer_unittest.cc
new file mode 100644
index 0000000..26998f5
--- /dev/null
+++ b/media/cast/framer/frame_buffer_unittest.cc
@@ -0,0 +1,88 @@
+// 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 "media/cast/framer/frame_buffer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+namespace cast {
+
+class FrameBufferTest : public ::testing::Test {
+ protected:
+  FrameBufferTest() {}
+
+  ~FrameBufferTest() {}
+
+  void SetUp() {
+    payload_.assign(kIpPacketSize, 0);
+
+    // Build a default one packet frame - populate webrtc header.
+    rtp_header_.is_key_frame = false;
+    rtp_header_.frame_id = 0;
+    rtp_header_.packet_id = 0;
+    rtp_header_.max_packet_id = 0;
+    rtp_header_.is_reference = false;
+    rtp_header_.reference_frame_id = 0;
+  }
+
+  FrameBuffer buffer_;
+  std::vector<uint8> payload_;
+  RtpCastHeader rtp_header_;
+};
+
+TEST_F(FrameBufferTest, EmptyBuffer) {
+  EXPECT_FALSE(buffer_.Complete());
+  EXPECT_FALSE(buffer_.is_key_frame());
+  EncodedVideoFrame frame;
+  uint32 rtp_timestamp;
+  EXPECT_FALSE(buffer_.GetEncodedVideoFrame(&frame, &rtp_timestamp));
+}
+
+TEST_F(FrameBufferTest, DefaultOnePacketFrame) {
+  buffer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  EXPECT_TRUE(buffer_.Complete());
+  EXPECT_FALSE(buffer_.is_key_frame());
+  EncodedVideoFrame frame;
+  uint32 rtp_timestamp;
+  EXPECT_TRUE(buffer_.GetEncodedVideoFrame(&frame, &rtp_timestamp));
+  EXPECT_EQ(payload_.size(), frame.data.size());
+}
+
+TEST_F(FrameBufferTest, MultiplePacketFrame) {
+  rtp_header_.is_key_frame = true;
+  rtp_header_.max_packet_id = 2;
+  buffer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  ++rtp_header_.packet_id;
+  buffer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  ++rtp_header_.packet_id;
+  buffer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  ++rtp_header_.packet_id;
+  EXPECT_TRUE(buffer_.Complete());
+  EXPECT_TRUE(buffer_.is_key_frame());
+  EncodedVideoFrame frame;
+  uint32 rtp_timestamp;
+  EXPECT_TRUE(buffer_.GetEncodedVideoFrame(&frame, &rtp_timestamp));
+  EXPECT_EQ(3 * payload_.size(), frame.data.size());
+}
+
+TEST_F(FrameBufferTest, InCompleteFrame) {
+  rtp_header_.max_packet_id = 4;
+  buffer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  ++rtp_header_.packet_id;
+  buffer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  ++rtp_header_.packet_id;
+  // Increment again - skip packet #2.
+  ++rtp_header_.packet_id;
+  buffer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  ++rtp_header_.packet_id;
+  buffer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  EXPECT_FALSE(buffer_.Complete());
+  // Insert missing packet.
+  rtp_header_.packet_id = 2;
+  buffer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  EXPECT_TRUE(buffer_.Complete());
+}
+
+}  // namespace media
+}  // namespace cast
diff --git a/media/cast/framer/frame_id_map.cc b/media/cast/framer/frame_id_map.cc
new file mode 100644
index 0000000..cf86684
--- /dev/null
+++ b/media/cast/framer/frame_id_map.cc
@@ -0,0 +1,252 @@
+// 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 "media/cast/framer/frame_id_map.h"
+
+#include "base/logging.h"
+#include "media/cast/rtp_common/rtp_defines.h"
+
+namespace media {
+namespace cast {
+
+FrameInfo::FrameInfo(uint8 frame_id,
+                     uint8 referenced_frame_id,
+                     uint16 max_packet_id,
+                     bool key_frame)
+    : is_key_frame_(key_frame),
+      frame_id_(frame_id),
+      referenced_frame_id_(referenced_frame_id),
+      max_received_packet_id_(0) {
+  if (max_packet_id > 0) {
+    // Create the set with all packets missing.
+    for (uint16 i = 0; i <= max_packet_id; i++) {
+      missing_packets_.insert(i);
+    }
+  }
+}
+
+FrameInfo::~FrameInfo() {}
+
+bool FrameInfo::InsertPacket(uint16 packet_id) {
+  // Update the last received packet id.
+  if (IsNewerPacketId(packet_id, max_received_packet_id_)) {
+    max_received_packet_id_ = packet_id;
+  }
+  missing_packets_.erase(packet_id);
+  return missing_packets_.empty();
+}
+
+bool FrameInfo::Complete() const {
+  return missing_packets_.empty();
+}
+
+void FrameInfo::GetMissingPackets(bool newest_frame,
+                                  PacketIdSet* missing_packets) const {
+  if (newest_frame) {
+    // Missing packets capped by max_received_packet_id_.
+    PacketIdSet::const_iterator it_after_last_received =
+        missing_packets_.lower_bound(max_received_packet_id_);
+    missing_packets->insert(missing_packets_.begin(), it_after_last_received);
+  } else {
+    missing_packets->insert(missing_packets_.begin(), missing_packets_.end());
+  }
+}
+
+
+FrameIdMap::FrameIdMap()
+    : waiting_for_key_(true),
+      last_released_frame_(kStartFrameId),
+      newest_frame_id_(kStartFrameId) {
+}
+
+FrameIdMap::~FrameIdMap() {}
+
+bool FrameIdMap::InsertPacket(const RtpCastHeader& rtp_header, bool* complete) {
+  uint8 frame_id = rtp_header.frame_id;
+  uint8 reference_frame_id;
+  if (rtp_header.is_reference) {
+    reference_frame_id = rtp_header.reference_frame_id;
+  } else {
+    reference_frame_id = static_cast<uint8>(frame_id - 1);
+  }
+
+  if (rtp_header.is_key_frame && waiting_for_key_) {
+    last_released_frame_ = static_cast<uint8>(frame_id - 1);
+    waiting_for_key_ = false;
+  }
+
+  if (IsOlderFrameId(frame_id, last_released_frame_) && !waiting_for_key_) {
+    return false;
+  }
+
+  // Update the last received frame id.
+  if (IsNewerFrameId(frame_id, newest_frame_id_)) {
+    newest_frame_id_ = frame_id;
+  }
+
+  // Does this packet belong to a new frame?
+  FrameMap::iterator it = frame_map_.find(frame_id);
+  if (it == frame_map_.end()) {
+    // New frame.
+    linked_ptr<FrameInfo> frame_info(new FrameInfo(frame_id,
+                                                   reference_frame_id,
+                                                   rtp_header.max_packet_id,
+                                                   rtp_header.is_key_frame));
+    std::pair<FrameMap::iterator, bool> retval =
+        frame_map_.insert(std::make_pair(frame_id, frame_info));
+
+    *complete = retval.first->second->InsertPacket(rtp_header.packet_id);
+  } else {
+    // Insert packet to existing frame.
+    *complete = it->second->InsertPacket(rtp_header.packet_id);
+  }
+  return true;
+}
+
+void FrameIdMap::RemoveOldFrames(uint8 frame_id) {
+  FrameMap::iterator it = frame_map_.begin();
+
+  while (it != frame_map_.end()) {
+    if (IsNewerFrameId(it->first, frame_id)) {
+      ++it;
+    } else {
+      // Older or equal; erase.
+      frame_map_.erase(it++);
+    }
+  }
+  last_released_frame_ = frame_id;
+}
+
+void FrameIdMap::Clear() {
+  frame_map_.clear();
+  waiting_for_key_ = true;
+  last_released_frame_ = kStartFrameId;
+  newest_frame_id_ = kStartFrameId;
+}
+
+uint8 FrameIdMap::NewestFrameId() const {
+  return newest_frame_id_;
+}
+
+bool FrameIdMap::NextContinuousFrame(uint8* frame_id) const {
+  FrameMap::const_iterator it;
+
+  for (it = frame_map_.begin(); it != frame_map_.end(); ++it) {
+    if (it->second->Complete() && ContinuousFrame(it->second.get())) {
+      *frame_id = it->first;
+      return true;
+    }
+  }
+  return false;
+}
+
+uint8 FrameIdMap::LastContinuousFrame() const {
+  uint8 last_continuous_frame_id = last_released_frame_;
+  uint8 next_expected_frame = last_released_frame_;
+
+  FrameMap::const_iterator it;
+
+  do {
+    next_expected_frame++;
+    it = frame_map_.find(next_expected_frame);
+    if (it == frame_map_.end()) break;
+    if (!it->second->Complete()) break;
+
+    // We found the next continuous frame.
+    last_continuous_frame_id = it->first;
+  } while (next_expected_frame != newest_frame_id_);
+  return last_continuous_frame_id;
+}
+
+bool FrameIdMap::NextAudioFrameAllowingMissingFrames(uint8* frame_id) const {
+  // First check if we have continuous frames.
+  if (NextContinuousFrame(frame_id)) return true;
+
+  // Find the oldest frame.
+  FrameMap::const_iterator it_best_match = frame_map_.end();
+  FrameMap::const_iterator it;
+
+  // Find first complete frame.
+  for (it = frame_map_.begin(); it != frame_map_.end(); ++it) {
+    if (it->second->Complete()) {
+      it_best_match = it;
+      break;
+    }
+  }
+  if (it_best_match == frame_map_.end()) return false;  // No complete frame.
+
+  ++it;
+  for (; it != frame_map_.end(); ++it) {
+    if (it->second->Complete() &&
+        IsOlderFrameId(it->first, it_best_match->first)) {
+      it_best_match = it;
+    }
+  }
+  *frame_id = it_best_match->first;
+  return true;
+}
+
+bool FrameIdMap::NextVideoFrameAllowingSkippingFrames(uint8* frame_id) const {
+  // Find the oldest decodable frame.
+  FrameMap::const_iterator it_best_match = frame_map_.end();
+  FrameMap::const_iterator it;
+  for (it = frame_map_.begin(); it != frame_map_.end(); ++it) {
+    if (it->second->Complete() && DecodableVideoFrame(it->second.get())) {
+      it_best_match = it;
+    }
+  }
+  if (it_best_match == frame_map_.end()) return false;
+
+  *frame_id = it_best_match->first;
+  return true;
+}
+
+bool FrameIdMap::Empty() const {
+  return frame_map_.empty();
+}
+
+int FrameIdMap::NumberOfCompleteFrames() const {
+  int count = 0;
+  FrameMap::const_iterator it;
+  for (it = frame_map_.begin(); it != frame_map_.end(); ++it) {
+    if (it->second->Complete()) {
+      ++count;
+    }
+  }
+  return count;
+}
+
+bool FrameIdMap::FrameExists(uint8 frame_id) const {
+  return frame_map_.end() != frame_map_.find(frame_id);
+}
+
+void FrameIdMap::GetMissingPackets(uint8 frame_id,
+                                   bool last_frame,
+                                   PacketIdSet* missing_packets) const {
+  FrameMap::const_iterator it = frame_map_.find(frame_id);
+  if (it == frame_map_.end()) return;
+
+  it->second->GetMissingPackets(last_frame, missing_packets);
+}
+
+bool FrameIdMap::ContinuousFrame(FrameInfo* frame) const {
+  DCHECK(frame);
+  if (waiting_for_key_ && !frame->is_key_frame()) return false;
+  return static_cast<uint8>(last_released_frame_ + 1) == frame->frame_id();
+}
+
+bool FrameIdMap::DecodableVideoFrame(FrameInfo* frame) const {
+  if (frame->is_key_frame()) return true;
+  if (waiting_for_key_ && !frame->is_key_frame()) return false;
+
+  // Current frame is not necessarily referencing the last frame.
+  // Do we have the reference frame?
+  if (IsOlderFrameId(frame->referenced_frame_id(), last_released_frame_)) {
+    return true;
+  }
+  return frame->referenced_frame_id() == last_released_frame_;
+}
+
+}  //  namespace cast
+}  //  namespace media
diff --git a/media/cast/framer/frame_id_map.h b/media/cast/framer/frame_id_map.h
new file mode 100644
index 0000000..6bf72a0
--- /dev/null
+++ b/media/cast/framer/frame_id_map.h
@@ -0,0 +1,93 @@
+// 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 MEDIA_CAST_FRAMER_FRAME_ID_MAP_H_
+#define MEDIA_CAST_FRAMER_FRAME_ID_MAP_H_
+
+#include <map>
+#include <set>
+
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "media/cast/cast_config.h"
+#include "media/cast/rtcp/rtcp_defines.h"
+#include "media/cast/rtp_common/rtp_defines.h"
+
+namespace media {
+namespace cast {
+
+class FrameInfo {
+ public:
+  FrameInfo(uint8 frame_id,
+            uint8 referenced_frame_id,
+            uint16 max_packet_id,
+            bool key_frame);
+  ~FrameInfo();
+
+  // Returns true if frame is complete after the insert.
+  bool InsertPacket(uint16 packet_id);
+  bool Complete() const;
+  void GetMissingPackets(bool newest_frame,
+                         PacketIdSet* missing_packets) const;
+
+  bool is_key_frame() const { return is_key_frame_; }
+  uint8 frame_id() const { return frame_id_; }
+  uint8 referenced_frame_id() const { return referenced_frame_id_; }
+
+ private:
+  const bool is_key_frame_;
+  const uint8 frame_id_;
+  const uint8 referenced_frame_id_;
+
+  uint16 max_received_packet_id_;
+  PacketIdSet missing_packets_;
+
+  DISALLOW_COPY_AND_ASSIGN(FrameInfo);
+};
+
+typedef std::map<uint8, linked_ptr<FrameInfo> > FrameMap;
+
+class FrameIdMap {
+ public:
+  FrameIdMap();
+  ~FrameIdMap();
+
+  // Returns false if not a valid (old) packet, otherwise returns true.
+  bool InsertPacket(const RtpCastHeader& rtp_header, bool* complete);
+
+  bool Empty() const;
+  bool FrameExists(uint8 frame_id) const;
+  uint8 NewestFrameId() const;
+
+  void RemoveOldFrames(uint8 frame_id);
+  void Clear();
+
+  // Identifies the next frame to be released (rendered).
+  bool NextContinuousFrame(uint8* frame_id) const;
+  uint8 LastContinuousFrame() const;
+
+  bool NextAudioFrameAllowingMissingFrames(uint8* frame_id) const;
+  bool NextVideoFrameAllowingSkippingFrames(uint8* frame_id) const;
+
+  int NumberOfCompleteFrames() const;
+  void GetMissingPackets(uint8 frame_id,
+                         bool last_frame,
+                         PacketIdSet* missing_packets) const;
+
+ private:
+  bool ContinuousFrame(FrameInfo* frame) const;
+  bool DecodableVideoFrame(FrameInfo* frame) const;
+
+  FrameMap frame_map_;
+  bool waiting_for_key_;
+  uint8 last_released_frame_;
+  uint8 newest_frame_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(FrameIdMap);
+};
+
+}  //  namespace cast
+}  //  namespace media
+
+#endif  // MEDIA_CAST_FRAMER_FRAME_ID_MAP_H_
diff --git a/media/cast/framer/framer.cc b/media/cast/framer/framer.cc
new file mode 100644
index 0000000..9504820
--- /dev/null
+++ b/media/cast/framer/framer.cc
@@ -0,0 +1,146 @@
+// 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 "media/cast/framer/framer.h"
+
+#include "base/logging.h"
+
+namespace media {
+namespace cast {
+
+typedef FrameList::const_iterator ConstFrameIterator;
+
+Framer::Framer(RtpPayloadFeedback* incoming_payload_feedback,
+               uint32 ssrc,
+               bool decoder_faster_than_max_frame_rate,
+               int max_unacked_frames)
+    : decoder_faster_than_max_frame_rate_(decoder_faster_than_max_frame_rate),
+      clock_(&default_tick_clock_),
+      cast_msg_builder_(new CastMessageBuilder(incoming_payload_feedback,
+          &frame_id_map_, ssrc, decoder_faster_than_max_frame_rate,
+          max_unacked_frames)) {
+  DCHECK(incoming_payload_feedback) << "Invalid argument";
+}
+
+Framer::~Framer() {}
+
+void Framer::InsertPacket(const uint8* payload_data,
+                          int payload_size,
+                          const RtpCastHeader& rtp_header) {
+  bool complete = false;
+  if (!frame_id_map_.InsertPacket(rtp_header, &complete)) return;
+
+  // Does this packet belong to a new frame?
+  FrameList::iterator it = frames_.find(rtp_header.frame_id);
+  if (it == frames_.end()) {
+    // New frame.
+    linked_ptr<FrameBuffer> frame_buffer(new FrameBuffer());
+    frame_buffer->InsertPacket(payload_data, payload_size, rtp_header);
+    frames_.insert(std::make_pair(rtp_header.frame_id, frame_buffer));
+  } else {
+    // Insert packet to existing frame buffer.
+    it->second->InsertPacket(payload_data, payload_size, rtp_header);
+  }
+
+  if (complete) {
+    // ACK as soon as possible.
+    cast_msg_builder_->CompleteFrameReceived(rtp_header.frame_id,
+                                             rtp_header.is_key_frame);
+  }
+}
+
+// This does not release the frame.
+bool Framer::GetEncodedAudioFrame(const base::TimeTicks& timeout,
+                                  EncodedAudioFrame* audio_frame,
+                                  uint32* rtp_timestamp,
+                                  bool* next_frame) {
+  uint8 frame_id;
+  // Find frame id.
+  if (frame_id_map_.NextContinuousFrame(&frame_id)) {
+    // We have our next frame.
+    *next_frame = true;
+  } else {
+    if (WaitForNextFrame(timeout)) return false;
+
+    if (!frame_id_map_.NextAudioFrameAllowingMissingFrames(&frame_id)) {
+      return false;
+    }
+    *next_frame = false;
+  }
+
+  ConstFrameIterator it = frames_.find(frame_id);
+  DCHECK(it != frames_.end());
+  if (it == frames_.end()) return false;
+
+  return it->second->GetEncodedAudioFrame(audio_frame, rtp_timestamp);
+}
+
+// This does not release the frame.
+bool Framer::GetEncodedVideoFrame(const base::TimeTicks& timeout,
+                                  EncodedVideoFrame* video_frame,
+                                  uint32* rtp_timestamp,
+                                  bool* next_frame) {
+  uint8 frame_id;
+  // Find frame id.
+  if (frame_id_map_.NextContinuousFrame(&frame_id)) {
+    // We have our next frame.
+    *next_frame = true;
+  } else {
+    if (WaitForNextFrame(timeout)) return false;
+
+    // Check if we can skip frames when our decoder is too slow.
+    if (!decoder_faster_than_max_frame_rate_) return false;
+
+    if (!frame_id_map_.NextVideoFrameAllowingSkippingFrames(&frame_id)) {
+      return false;
+    }
+    *next_frame = false;
+  }
+
+  ConstFrameIterator it = frames_.find(frame_id);
+  DCHECK(it != frames_.end());
+  if (it == frames_.end())  return false;
+
+  return it->second->GetEncodedVideoFrame(video_frame, rtp_timestamp);
+}
+
+bool Framer::WaitForNextFrame(const base::TimeTicks& timeout) const {
+  base::TimeDelta wait_time = timeout - clock_->NowTicks();
+  if (wait_time.InMilliseconds() > 0)
+    return true;
+
+  return false;
+}
+
+void Framer::Reset() {
+  frame_id_map_.Clear();
+  frames_.clear();
+  cast_msg_builder_->Reset();
+}
+
+void Framer::ReleaseFrame(uint8 frame_id) {
+  frame_id_map_.RemoveOldFrames(frame_id);
+  frames_.erase(frame_id);
+
+  // We have a frame - remove all frames with lower frame id.
+  FrameList::iterator it;
+  for (it = frames_.begin(); it != frames_.end(); ) {
+    if (IsOlderFrameId(it->first, frame_id)) {
+      frames_.erase(it++);
+    } else {
+      ++it;
+    }
+  }
+}
+
+bool Framer::TimeToSendNextCastMessage(base::TimeTicks* time_to_send) {
+  return cast_msg_builder_->TimeToSendNextCastMessage(time_to_send);
+}
+
+void Framer::SendCastMessage() {
+  cast_msg_builder_->UpdateCastMessage();
+}
+
+}  // namespace cast
+}  // namespace media
diff --git a/media/cast/framer/framer.gyp b/media/cast/framer/framer.gyp
new file mode 100644
index 0000000..7b124f0
--- /dev/null
+++ b/media/cast/framer/framer.gyp
@@ -0,0 +1,27 @@
+# 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.
+
+{
+  'targets': [
+    {
+      'target_name': 'cast_framer',
+      'type': 'static_library',
+      'include_dirs': [
+        '<(DEPTH)/',
+        '<(DEPTH)/third_party/',
+        '<(DEPTH)/third_party/webrtc',
+      ],
+      'sources': [
+        'cast_message_builder.cc',
+        'cast_message_builder.h',
+        'frame_buffer.cc',
+        'frame_buffer.h',
+        'frame_id_map.cc',
+        'frame_id_map.h',
+        'framer.cc',
+        'framer.h',
+      ],
+    },
+  ], # targets
+}
diff --git a/media/cast/framer/framer.h b/media/cast/framer/framer.h
new file mode 100644
index 0000000..93d7906
--- /dev/null
+++ b/media/cast/framer/framer.h
@@ -0,0 +1,84 @@
+// 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 MEDIA_CAST_FRAMER_FRAMER_H_
+#define MEDIA_CAST_FRAMER_FRAMER_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/default_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "media/cast/framer/cast_message_builder.h"
+#include "media/cast/framer/frame_buffer.h"
+#include "media/cast/framer/frame_id_map.h"
+#include "media/cast/rtcp/rtcp.h"
+#include "media/cast/rtp_common/rtp_defines.h"
+
+namespace media {
+namespace cast {
+
+typedef std::map<uint8, linked_ptr<FrameBuffer> > FrameList;
+
+class Framer {
+ public:
+  Framer(RtpPayloadFeedback* incoming_payload_feedback,
+         uint32 ssrc,
+         bool decoder_faster_than_max_frame_rate,
+         int max_unacked_frames);
+  ~Framer();
+
+  void InsertPacket(const uint8* payload_data,
+                    int payload_size,
+                    const RtpCastHeader& rtp_header);
+
+  // Extracts a complete encoded frame - will only return a complete continuous
+  // frame.
+  // Returns false if the frame does not exist or if the frame is not complete
+  // within the given time frame.
+  bool GetEncodedVideoFrame(const base::TimeTicks& timeout,
+                            EncodedVideoFrame* video_frame,
+                            uint32* rtp_timestamp,
+                            bool* next_frame);
+
+  bool GetEncodedAudioFrame(const base::TimeTicks& timeout,
+                            EncodedAudioFrame* audio_frame,
+                            uint32* rtp_timestamp,
+                            bool* next_frame);
+
+  void ReleaseFrame(uint8 frame_id);
+
+  // Reset framer state to original state and flush all pending buffers.
+  void Reset();
+  bool TimeToSendNextCastMessage(base::TimeTicks* time_to_send);
+  void SendCastMessage();
+
+  void set_clock(base::TickClock* clock) {
+    clock_ = clock;
+    cast_msg_builder_->set_clock(clock);
+  }
+
+ private:
+  // Return true if we should wait.
+  bool WaitForNextFrame(const base::TimeTicks& timeout) const;
+
+  const bool decoder_faster_than_max_frame_rate_;
+  FrameList frames_;
+  FrameIdMap frame_id_map_;
+
+  base::DefaultTickClock default_tick_clock_;
+  base::TickClock* clock_;
+
+  scoped_ptr<CastMessageBuilder> cast_msg_builder_;
+
+  DISALLOW_COPY_AND_ASSIGN(Framer);
+};
+
+}  //  namespace cast
+}  //  namespace media
+
+#endif  // MEDIA_CAST_FRAMER_FRAMER_H_
diff --git a/media/cast/framer/framer_unittest.cc b/media/cast/framer/framer_unittest.cc
new file mode 100644
index 0000000..6f83706
--- /dev/null
+++ b/media/cast/framer/framer_unittest.cc
@@ -0,0 +1,351 @@
+// 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 "base/test/simple_test_tick_clock.h"
+#include "media/cast/framer/framer.h"
+#include "media/cast/rtp_common/mock_rtp_payload_feedback.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+namespace cast {
+
+static const int64 kFrameTimeMillisecond = 33;
+
+class FramerTest : public ::testing::Test {
+ protected:
+  FramerTest()
+      : mock_rtp_payload_feedback_(),
+        framer_(&mock_rtp_payload_feedback_, 0, true, 0) {
+    framer_.set_clock(&testing_clock_);
+  }
+
+  ~FramerTest() {}
+
+  void SetUp() {
+    // Build a default one packet frame - populate webrtc header.
+    rtp_header_.is_key_frame = false;
+    rtp_header_.frame_id = 0;
+    rtp_header_.packet_id = 0;
+    rtp_header_.max_packet_id = 0;
+    rtp_header_.is_reference = false;
+    rtp_header_.reference_frame_id = 0;
+    payload_.assign(kIpPacketSize, 0);
+
+    EXPECT_CALL(mock_rtp_payload_feedback_,
+                CastFeedback(testing::_)).WillRepeatedly(testing::Return());
+  }
+
+  std::vector<uint8> payload_;
+  RtpCastHeader rtp_header_;
+  MockRtpPayloadFeedback mock_rtp_payload_feedback_;
+  Framer framer_;
+  base::SimpleTestTickClock testing_clock_;
+};
+
+
+TEST_F(FramerTest, EmptyState) {
+  EncodedVideoFrame frame;
+  uint32_t rtp_timestamp;
+  bool next_frame = false;
+  base::TimeTicks timeout;
+  EXPECT_FALSE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                            &next_frame));
+}
+
+TEST_F(FramerTest, AlwaysStartWithKey) {
+  EncodedVideoFrame frame;
+  uint32_t rtp_timestamp;
+  bool next_frame = false;
+  base::TimeTicks timeout;
+
+  // Insert non key first frame.
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  EXPECT_FALSE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                            &next_frame));
+  rtp_header_.frame_id = 1;
+  rtp_header_.is_key_frame = true;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  EXPECT_TRUE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                           &next_frame));
+  EXPECT_TRUE(next_frame);
+  EXPECT_EQ(1, frame.frame_id);
+  EXPECT_TRUE(frame.key_frame);
+  framer_.ReleaseFrame(frame.frame_id);
+}
+
+TEST_F(FramerTest, CompleteFrame) {
+  EncodedVideoFrame frame;
+  uint32_t rtp_timestamp;
+  bool next_frame = false;
+  base::TimeTicks timeout;
+
+  // start with a complete key frame.
+  rtp_header_.is_key_frame = true;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  EXPECT_TRUE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                           &next_frame));
+  EXPECT_TRUE(next_frame);
+  EXPECT_EQ(0, frame.frame_id);
+  EXPECT_TRUE(frame.key_frame);
+  framer_.ReleaseFrame(frame.frame_id);
+
+  // Incomplete delta.
+  ++rtp_header_.frame_id;
+  rtp_header_.is_key_frame = false;
+  rtp_header_.max_packet_id = 2;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  EXPECT_FALSE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                            &next_frame));
+
+  // Complete delta - can't skip, as incomplete sequence.
+  ++rtp_header_.frame_id;
+  rtp_header_.max_packet_id = 0;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  EXPECT_FALSE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                            &next_frame));
+}
+
+TEST_F(FramerTest, ContinuousSequence) {
+  EncodedVideoFrame frame;
+  uint32_t rtp_timestamp;
+  bool next_frame = false;
+  base::TimeTicks timeout;
+
+  // start with a complete key frame.
+  rtp_header_.is_key_frame = true;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  EXPECT_TRUE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                           &next_frame));
+  EXPECT_TRUE(next_frame);
+  EXPECT_EQ(0, frame.frame_id);
+  EXPECT_TRUE(frame.key_frame);
+  framer_.ReleaseFrame(frame.frame_id);
+
+  // Complete - not continuous.
+  rtp_header_.frame_id = 2;
+  rtp_header_.is_key_frame = false;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  EXPECT_FALSE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                            &next_frame));
+}
+
+TEST_F(FramerTest, Wrap) {
+  // Insert key frame, frame_id = 255 (will jump to that)
+  EncodedVideoFrame frame;
+  uint32_t rtp_timestamp;
+  bool next_frame = false;
+  base::TimeTicks timeout;
+
+  // Start with a complete key frame.
+  rtp_header_.is_key_frame = true;
+  rtp_header_.frame_id = 255;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  EXPECT_TRUE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                           &next_frame));
+  EXPECT_TRUE(next_frame);
+  EXPECT_EQ(255, frame.frame_id);
+  framer_.ReleaseFrame(frame.frame_id);
+
+  // Insert wrapped delta frame - should be continuous.
+  rtp_header_.is_key_frame = false;
+  rtp_header_.frame_id = 0;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  EXPECT_TRUE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                           &next_frame));
+  EXPECT_TRUE(next_frame);
+  EXPECT_EQ(0, frame.frame_id);
+  framer_.ReleaseFrame(frame.frame_id);
+}
+
+TEST_F(FramerTest, Reset) {
+  EncodedVideoFrame frame;
+  uint32_t rtp_timestamp;
+  bool next_frame = false;
+  base::TimeTicks timeout;
+
+  // Start with a complete key frame.
+  rtp_header_.is_key_frame = true;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  framer_.Reset();
+  EXPECT_FALSE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                            &next_frame));
+}
+
+TEST_F(FramerTest, RequireKeyAfterReset) {
+  EncodedVideoFrame frame;
+  uint32_t rtp_timestamp;
+  bool next_frame = false;
+  base::TimeTicks timeout;
+  framer_.Reset();
+
+  // Start with a complete key frame.
+  rtp_header_.is_key_frame = false;
+  rtp_header_.frame_id = 0;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  EXPECT_FALSE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                            &next_frame));
+  rtp_header_.frame_id = 1;
+  rtp_header_.is_key_frame = true;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  EXPECT_TRUE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                           &next_frame));
+  EXPECT_TRUE(next_frame);
+}
+
+TEST_F(FramerTest, BasicNonLastReferenceId) {
+  EncodedVideoFrame frame;
+  uint32_t rtp_timestamp;
+  bool next_frame = false;
+  rtp_header_.is_key_frame = true;
+  rtp_header_.frame_id = 0;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+
+  base::TimeTicks timeout;
+  EXPECT_TRUE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                           &next_frame));
+  framer_.ReleaseFrame(frame.frame_id);
+
+  rtp_header_.is_key_frame = false;
+  rtp_header_.is_reference = true;
+  rtp_header_.reference_frame_id = 0;
+  rtp_header_.frame_id = 5;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+
+  timeout += base::TimeDelta::FromMilliseconds(kFrameTimeMillisecond);
+  EXPECT_FALSE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                            &next_frame));
+  testing_clock_.Advance(
+        base::TimeDelta::FromMilliseconds(kFrameTimeMillisecond));
+
+  EXPECT_TRUE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                           &next_frame));
+  EXPECT_FALSE(next_frame);
+}
+
+TEST_F(FramerTest, InOrderReferenceFrameSelection) {
+  // Create pattern: 0, 1, 4, 5.
+  EncodedVideoFrame frame;
+  uint32_t rtp_timestamp;
+  bool next_frame = false;
+  base::TimeTicks timeout;
+  rtp_header_.is_key_frame = true;
+  rtp_header_.frame_id = 0;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  rtp_header_.is_key_frame = false;
+  rtp_header_.frame_id = 1;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+
+  // Insert frame #2 partially.
+  rtp_header_.frame_id = 2;
+  rtp_header_.max_packet_id = 1;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  rtp_header_.frame_id = 4;
+  rtp_header_.max_packet_id = 0;
+  rtp_header_.is_reference = true;
+  rtp_header_.reference_frame_id = 0;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  EXPECT_TRUE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                           &next_frame));
+  EXPECT_EQ(0, frame.frame_id);
+  framer_.ReleaseFrame(frame.frame_id);
+  EXPECT_TRUE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                           &next_frame));
+  EXPECT_TRUE(next_frame);
+  EXPECT_EQ(1, frame.frame_id);
+  framer_.ReleaseFrame(frame.frame_id);
+  EXPECT_TRUE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                           &next_frame));
+  EXPECT_FALSE(next_frame);
+  EXPECT_EQ(4, frame.frame_id);
+  framer_.ReleaseFrame(frame.frame_id);
+  // Insert remaining packet of frame #2 - should no be continuous.
+  rtp_header_.frame_id = 2;
+  rtp_header_.packet_id = 1;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  EXPECT_FALSE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                            &next_frame));
+  rtp_header_.is_reference = false;
+  rtp_header_.frame_id = 5;
+  rtp_header_.packet_id = 0;
+  rtp_header_.max_packet_id = 0;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  EXPECT_TRUE(framer_.GetEncodedVideoFrame(timeout, &frame, &rtp_timestamp,
+                                           &next_frame));
+  EXPECT_TRUE(next_frame);
+  EXPECT_EQ(5, frame.frame_id);
+}
+
+TEST_F(FramerTest, AudioWrap) {
+  // All audio frames are marked as key frames.
+  EncodedAudioFrame frame;
+  uint32_t rtp_timestamp;
+  base::TimeTicks timeout;
+  bool next_frame = false;
+  rtp_header_.is_key_frame = true;
+  rtp_header_.frame_id = 254;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  EXPECT_TRUE(framer_.GetEncodedAudioFrame(timeout, &frame, &rtp_timestamp,
+                                           &next_frame));
+  EXPECT_TRUE(next_frame);
+  EXPECT_EQ(254, frame.frame_id);
+  framer_.ReleaseFrame(frame.frame_id);
+
+  rtp_header_.frame_id = 255;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+
+  // Insert wrapped frame - should be continuous.
+  rtp_header_.frame_id = 0;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+
+  EXPECT_TRUE(framer_.GetEncodedAudioFrame(timeout, &frame, &rtp_timestamp,
+                                           &next_frame));
+  EXPECT_TRUE(next_frame);
+  EXPECT_EQ(255, frame.frame_id);
+  framer_.ReleaseFrame(frame.frame_id);
+
+  EXPECT_TRUE(framer_.GetEncodedAudioFrame(timeout, &frame, &rtp_timestamp,
+                                           &next_frame));
+  EXPECT_TRUE(next_frame);
+  EXPECT_EQ(0, frame.frame_id);
+  framer_.ReleaseFrame(frame.frame_id);
+}
+
+TEST_F(FramerTest, AudioWrapWithMissingFrame) {
+  // All audio frames are marked as key frames.
+  EncodedAudioFrame frame;
+  uint32_t rtp_timestamp;
+  bool next_frame = false;
+  base::TimeTicks timeout;
+
+  // Insert and get first packet.
+  rtp_header_.is_key_frame = true;
+  rtp_header_.frame_id = 253;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  EXPECT_TRUE(framer_.GetEncodedAudioFrame(timeout, &frame, &rtp_timestamp,
+                                           &next_frame));
+  EXPECT_TRUE(next_frame);
+  EXPECT_EQ(253, frame.frame_id);
+  framer_.ReleaseFrame(frame.frame_id);
+
+  // Insert third and fourth packets.
+  rtp_header_.frame_id = 255;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+  rtp_header_.frame_id = 0;
+  framer_.InsertPacket(payload_.data(), payload_.size(), rtp_header_);
+
+  // Get third and fourth packets.
+  EXPECT_TRUE(framer_.GetEncodedAudioFrame(timeout, &frame, &rtp_timestamp,
+                                           &next_frame));
+  EXPECT_FALSE(next_frame);
+  EXPECT_EQ(255, frame.frame_id);
+  framer_.ReleaseFrame(frame.frame_id);
+  EXPECT_TRUE(framer_.GetEncodedAudioFrame(timeout, &frame, &rtp_timestamp,
+                                           &next_frame));
+  EXPECT_TRUE(next_frame);
+  EXPECT_EQ(0, frame.frame_id);
+  framer_.ReleaseFrame(frame.frame_id);
+}
+
+}  // namespace cast
+}  // namespace media
diff --git a/media/cast/pacing/mock_paced_packet_sender.h b/media/cast/pacing/mock_paced_packet_sender.h
new file mode 100644
index 0000000..40d3e62
--- /dev/null
+++ b/media/cast/pacing/mock_paced_packet_sender.h
@@ -0,0 +1,26 @@
+// 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 MEDIA_CAST_PACING_MOCK_PACED_PACKET_SENDER_H_
+#define MEDIA_CAST_PACING_MOCK_PACED_PACKET_SENDER_H_
+
+#include "media/cast/pacing/paced_sender.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+namespace cast {
+
+class MockPacedPacketSender : public PacedPacketSender {
+ public:
+  MOCK_METHOD2(SendPacket,
+               bool(const std::vector<uint8>& packet, int num_of_packets));
+  MOCK_METHOD2(ResendPacket,
+               bool(const std::vector<uint8>& packet, int num_of_packets));
+  MOCK_METHOD1(SendRtcpPacket, bool(const std::vector<uint8>& packet));
+};
+
+}  // namespace cast
+}  // namespace media
+
+#endif  // MEDIA_CAST_PACING_MOCK_PACED_PACKET_SENDER_H_
diff --git a/media/cast/pacing/mock_packet_sender.h b/media/cast/pacing/mock_packet_sender.h
new file mode 100644
index 0000000..bad9bac
--- /dev/null
+++ b/media/cast/pacing/mock_packet_sender.h
@@ -0,0 +1,22 @@
+// 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 MEDIA_CAST_PACING_MOCK_PACKET_SENDER_H_
+#define MEDIA_CAST_PACING_MOCK_PACKET_SENDER_H_
+
+#include "media/cast/cast_config.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+namespace cast {
+
+class MockPacketSender : public PacketSender {
+ public:
+  MOCK_METHOD2(SendPacket, bool(const uint8* packet, int length));
+};
+
+}  // namespace cast
+}  // namespace media
+
+#endif  // MEDIA_CAST_PACING_MOCK_PACKET_SENDER_H_
diff --git a/media/cast/pacing/paced_sender.cc b/media/cast/pacing/paced_sender.cc
new file mode 100644
index 0000000..f89361b
--- /dev/null
+++ b/media/cast/pacing/paced_sender.cc
@@ -0,0 +1,119 @@
+// 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 "media/cast/pacing/paced_sender.h"
+
+namespace media {
+namespace cast {
+
+static const int64 kPacingIntervalMs = 10;
+static const int64 kPacingMinIntervalMs = 7;
+static const int kPacingMaxBurstsPerFrame = 3;
+
+PacedSender::PacedSender(PacketSender* transport)
+    : burst_size_(1),
+      packets_sent_in_burst_(0),
+      transport_(transport),
+      default_tick_clock_(new base::DefaultTickClock()),
+      clock_(default_tick_clock_.get()) {
+}
+
+PacedSender::~PacedSender() {}
+
+bool PacedSender::SendPacket(const std::vector<uint8>& packet,
+                             int num_of_packets_in_frame) {
+  if (!packet_list_.empty()) {
+    // We have a queue put the new packets last in the list.
+    packet_list_.push_back(packet);
+    UpdateBurstSize(num_of_packets_in_frame);
+    return true;
+  }
+  UpdateBurstSize(num_of_packets_in_frame);
+
+  if (packets_sent_in_burst_ >= burst_size_) {
+    packet_list_.push_back(packet);
+    return true;
+  }
+  ++packets_sent_in_burst_;
+  return transport_->SendPacket(&(packet[0]), packet.size());
+}
+
+bool PacedSender::ResendPacket(const std::vector<uint8>& packet,
+                               int num_of_packets_to_resend) {
+  if (!packet_list_.empty() || !resend_packet_list_.empty()) {
+    // We have a queue put the resend packets in the list.
+    resend_packet_list_.push_back(packet);
+    UpdateBurstSize(num_of_packets_to_resend);
+    return true;
+  }
+  UpdateBurstSize(num_of_packets_to_resend);
+
+  if (packets_sent_in_burst_ >= burst_size_) {
+    resend_packet_list_.push_back(packet);
+    return true;
+  }
+  ++packets_sent_in_burst_;
+  return transport_->SendPacket(&(packet[0]), packet.size());
+}
+
+bool PacedSender::SendRtcpPacket(const std::vector<uint8>& packet) {
+  // We pass the RTCP packets straight through.
+  return transport_->SendPacket(&(packet[0]), packet.size());
+}
+
+base::TimeTicks PacedSender::TimeNextProcess() {
+  return time_last_process_ +
+      base::TimeDelta::FromMilliseconds(kPacingIntervalMs);
+}
+
+void PacedSender::Process() {
+  int packets_to_send = 0;
+  base::TimeTicks now = clock_->NowTicks();
+
+  base::TimeDelta min_pacing_interval =
+    base::TimeDelta::FromMilliseconds(kPacingMinIntervalMs);
+
+  // Have enough time have passed?
+  if (now - time_last_process_ < min_pacing_interval)  return;
+
+  time_last_process_ = now;
+  packets_to_send = burst_size_;
+  // Allow new packets to be inserted while we loop over our packets to send.
+  for (int i = 0; i < packets_to_send; ++i) {
+    SendStoredPacket();
+  }
+}
+
+void PacedSender::SendStoredPacket() {
+  if (packet_list_.empty() && resend_packet_list_.empty())  return;
+
+  if (!resend_packet_list_.empty()) {
+    // Send our re-send packets first.
+    const std::vector<uint8>& packet = resend_packet_list_.front();
+    transport_->SendPacket(&(packet[0]), packet.size());
+    resend_packet_list_.pop_front();
+  } else {
+    const std::vector<uint8>& packet = packet_list_.front();
+    transport_->SendPacket(&(packet[0]), packet.size());
+    packet_list_.pop_front();
+
+    if (packet_list_.empty()) {
+      burst_size_ = 1;  // Reset burst size after we sent the last stored packet
+      packets_sent_in_burst_ = 0;
+    }
+  }
+}
+
+void PacedSender::UpdateBurstSize(int packets_to_send) {
+  packets_to_send = std::max(packets_to_send,
+      static_cast<int>(resend_packet_list_.size() + packet_list_.size()));
+
+  packets_to_send += (kPacingMaxBurstsPerFrame - 1);  // Round up.
+
+  burst_size_ = std::max(packets_to_send / kPacingMaxBurstsPerFrame,
+                         burst_size_);
+}
+
+}  // namespace cast
+}  // namespace media
diff --git a/media/cast/pacing/paced_sender.gyp b/media/cast/pacing/paced_sender.gyp
new file mode 100644
index 0000000..53a1cdb
--- /dev/null
+++ b/media/cast/pacing/paced_sender.gyp
@@ -0,0 +1,23 @@
+# 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.
+
+{
+  'targets': [
+    {
+      'target_name': 'paced_sender',
+      'type': 'static_library',
+      'include_dirs': [
+        '<(DEPTH)/',
+      ],
+      'sources': [
+        'paced_sender.h',
+        'paced_sender.cc',
+      ],
+      'dependencies': [
+        '<(DEPTH)/base/base.gyp:base',
+        '<(DEPTH)/base/base.gyp:test_support_base',
+      ],
+    },
+  ], # targets
+}
diff --git a/media/cast/pacing/paced_sender.h b/media/cast/pacing/paced_sender.h
new file mode 100644
index 0000000..45c8dc1
--- /dev/null
+++ b/media/cast/pacing/paced_sender.h
@@ -0,0 +1,81 @@
+// 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 MEDIA_CAST_PACING_PACED_SENDER_H_
+#define MEDIA_CAST_PACING_PACED_SENDER_H_
+
+#include <list>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/default_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "media/cast/cast_config.h"
+
+namespace media {
+namespace cast {
+
+class PacedPacketSender {
+ public:
+  // Inform the pacer / sender of the total number of packets.
+  virtual bool SendPacket(const std::vector<uint8>& packet,
+                          int num_of_packets) = 0;
+
+  virtual bool ResendPacket(const std::vector<uint8>& packet,
+                            int num_of_packets) = 0;
+
+  virtual bool SendRtcpPacket(const std::vector<uint8>& packet) = 0;
+
+ protected:
+  virtual ~PacedPacketSender() {}
+};
+
+class PacedSender : public PacedPacketSender {
+ public:
+  explicit PacedSender(PacketSender* transport);
+  virtual ~PacedSender();
+
+  // Returns the time when the pacer want a worker thread to call Process.
+  base::TimeTicks TimeNextProcess();
+
+  // Process any pending packets in the queue(s).
+  void Process();
+
+  virtual bool SendPacket(const std::vector<uint8>& packet,
+                          int num_of_packets) OVERRIDE;
+
+  virtual bool ResendPacket(const std::vector<uint8>& packet,
+                            int num_of_packets) OVERRIDE;
+
+  virtual bool SendRtcpPacket(const std::vector<uint8>& packet) OVERRIDE;
+
+  void set_clock(base::TickClock* clock) {
+    clock_ = clock;
+  }
+
+ private:
+  void SendStoredPacket();
+  void UpdateBurstSize(int num_of_packets);
+
+  typedef std::list<std::vector<uint8> > PacketList;
+
+  int burst_size_;
+  int packets_sent_in_burst_;
+  base::TimeTicks time_last_process_;
+  PacketList packet_list_;
+  PacketList resend_packet_list_;
+  PacketSender* transport_;
+
+  scoped_ptr<base::TickClock> default_tick_clock_;
+  base::TickClock* clock_;
+
+  DISALLOW_COPY_AND_ASSIGN(PacedSender);
+};
+
+}  // namespace cast
+}  // namespace media
+
+#endif  // MEDIA_CAST_PACING_PACED_SENDER_H_
diff --git a/media/cast/pacing/paced_sender_unittest.cc b/media/cast/pacing/paced_sender_unittest.cc
new file mode 100644
index 0000000..9108965
--- /dev/null
+++ b/media/cast/pacing/paced_sender_unittest.cc
@@ -0,0 +1,246 @@
+// 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 "base/test/simple_test_tick_clock.h"
+#include "media/cast/pacing/mock_packet_sender.h"
+#include "media/cast/pacing/paced_sender.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using testing::_;
+
+namespace media {
+namespace cast {
+
+static const uint8 kValue = 123;
+static const size_t kSize1 = 100;
+static const size_t kSize2 = 101;
+static const size_t kSize3 = 102;
+static const size_t kSize4 = 103;
+static const size_t kNackSize = 104;
+static const int64 kStartMillisecond = 123456789;
+
+class PacedSenderTest : public ::testing::Test {
+ protected:
+  PacedSenderTest()
+      : paced_sender_(&mock_transport_) {
+    testing_clock_.Advance(
+        base::TimeDelta::FromMilliseconds(kStartMillisecond));
+    paced_sender_.set_clock(&testing_clock_);
+  }
+
+  virtual ~PacedSenderTest() {}
+
+  MockPacketSender mock_transport_;
+  PacedSender paced_sender_;
+  base::SimpleTestTickClock testing_clock_;
+};
+
+TEST_F(PacedSenderTest, PassThroughRtcp) {
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize1)).Times(1).WillRepeatedly(
+      testing::Return(true));
+
+  std::vector<uint8> packet(kSize1, kValue);
+  int num_of_packets = 1;
+  EXPECT_TRUE(paced_sender_.SendPacket(packet, num_of_packets));
+
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize1)).Times(0);
+  EXPECT_TRUE(paced_sender_.ResendPacket(packet, num_of_packets));
+
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize1)).Times(1).WillRepeatedly(
+      testing::Return(true));
+  EXPECT_TRUE(paced_sender_.SendRtcpPacket(packet));
+}
+
+TEST_F(PacedSenderTest, BasicPace) {
+  std::vector<uint8> packet(kSize1, kValue);
+  int num_of_packets = 9;
+
+  EXPECT_CALL(mock_transport_,
+      SendPacket(_, kSize1)).Times(3).WillRepeatedly(testing::Return(true));
+  for (int i = 0; i < num_of_packets; ++i) {
+    EXPECT_TRUE(paced_sender_.SendPacket(packet, num_of_packets));
+  }
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
+
+  // Check that we get the next burst.
+  EXPECT_CALL(mock_transport_,
+      SendPacket(_, kSize1)).Times(3).WillRepeatedly(testing::Return(true));
+  paced_sender_.Process();
+
+  // If we call process too early make sure we don't send any packets.
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(5));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize1)).Times(0);
+  paced_sender_.Process();
+
+  // Check that we get the next burst.
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(5));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize1)).Times(3).WillRepeatedly(
+      testing::Return(true));
+  paced_sender_.Process();
+
+  // Check that we don't get any more packets.
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize1)).Times(0);
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
+  paced_sender_.Process();
+}
+
+TEST_F(PacedSenderTest, PaceWithNack) {
+  // Testing what happen when we get multiple NACK requests for a fully lost
+  // frames just as we sent the first packets in a frame.
+  std::vector<uint8> firts_packet(kSize1, kValue);
+  std::vector<uint8> second_packet(kSize2, kValue);
+  std::vector<uint8> nack_packet(kNackSize, kValue);
+  int num_of_packets_in_frame = 9;
+  int num_of_packets_in_nack = 9;
+
+  // Check that the first burst of the frame go out on the wire.
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize1)).Times(3).WillRepeatedly(
+      testing::Return(true));
+  for (int i = 0; i < num_of_packets_in_frame; ++i) {
+    EXPECT_TRUE(paced_sender_.SendPacket(firts_packet,
+                                          num_of_packets_in_frame));
+  }
+  // Add first NACK request.
+  for (int i = 0; i < num_of_packets_in_nack; ++i) {
+    EXPECT_TRUE(paced_sender_.ResendPacket(nack_packet,
+                                            num_of_packets_in_nack));
+  }
+  // Check that we get the first NACK burst.
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kNackSize)).Times(5).
+      WillRepeatedly(testing::Return(true));
+  paced_sender_.Process();
+
+  // Add second NACK request.
+  for (int i = 0; i < num_of_packets_in_nack; ++i) {
+    EXPECT_TRUE(paced_sender_.ResendPacket(nack_packet,
+                                           num_of_packets_in_nack));
+  }
+
+  // Check that we get the next NACK burst.
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kNackSize)).Times(7)
+      .WillRepeatedly(testing::Return(true));
+  paced_sender_.Process();
+
+  // End of NACK plus a packet from the oldest frame.
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kNackSize)).Times(6)
+      .WillRepeatedly(testing::Return(true));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize1)).Times(1)
+      .WillRepeatedly(testing::Return(true));
+  paced_sender_.Process();
+
+  // Add second frame.
+  // Make sure we don't delay the second frame due to the previous packets.
+  for (int i = 0; i < num_of_packets_in_frame; ++i) {
+    EXPECT_TRUE(paced_sender_.SendPacket(second_packet,
+                                         num_of_packets_in_frame));
+  }
+
+  // Last packets of frame 1 and the first packets of frame 2.
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize1)).Times(5).WillRepeatedly(
+      testing::Return(true));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize2)).Times(2).WillRepeatedly(
+      testing::Return(true));
+  paced_sender_.Process();
+
+  // Last packets of frame 2.
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize2)).Times(7).WillRepeatedly(
+      testing::Return(true));
+  paced_sender_.Process();
+
+  // No more packets.
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize2)).Times(0);
+  paced_sender_.Process();
+}
+
+TEST_F(PacedSenderTest, PaceWith60fps) {
+  // Testing what happen when we get multiple NACK requests for a fully lost
+  // frames just as we sent the first packets in a frame.
+  std::vector<uint8_t> firts_packet(kSize1, kValue);
+  std::vector<uint8_t> second_packet(kSize2, kValue);
+  std::vector<uint8_t> third_packet(kSize3, kValue);
+  std::vector<uint8_t> fourth_packet(kSize4, kValue);
+  int num_of_packets_in_frame = 9;
+
+  // Check that the first burst of the frame go out on the wire.
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize1)).Times(3).WillRepeatedly(
+      testing::Return(true));
+  for (int i = 0; i < num_of_packets_in_frame; ++i) {
+    EXPECT_TRUE(paced_sender_.SendPacket(firts_packet,
+                                         num_of_packets_in_frame));
+  }
+
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize1)).Times(3).
+      WillRepeatedly(testing::Return(true));
+  paced_sender_.Process();
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(6));
+
+  // Add second frame, after 16 ms.
+  for (int i = 0; i < num_of_packets_in_frame; ++i) {
+    EXPECT_TRUE(paced_sender_.SendPacket(second_packet,
+                                         num_of_packets_in_frame));
+  }
+
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(4));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize1)).Times(3)
+      .WillRepeatedly(testing::Return(true));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize2)).Times(1)
+      .WillRepeatedly(testing::Return(true));
+  paced_sender_.Process();
+
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize2)).Times(4)
+      .WillRepeatedly(testing::Return(true));
+  paced_sender_.Process();
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(3));
+
+  // Add third frame, after 33 ms.
+  for (int i = 0; i < num_of_packets_in_frame; ++i) {
+    EXPECT_TRUE(paced_sender_.SendPacket(third_packet,
+                                         num_of_packets_in_frame));
+  }
+
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(7));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize2)).Times(4)
+      .WillRepeatedly(testing::Return(true));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize3)).Times(1)
+      .WillRepeatedly(testing::Return(true));
+  paced_sender_.Process();
+
+  // Add fourth frame, after 50 ms.
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
+  for (int i = 0; i < num_of_packets_in_frame; ++i) {
+    EXPECT_TRUE(paced_sender_.SendPacket(fourth_packet,
+                                         num_of_packets_in_frame));
+  }
+
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize3)).Times(6)
+      .WillRepeatedly(testing::Return(true));
+  paced_sender_.Process();
+
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize3)).Times(2)
+      .WillRepeatedly(testing::Return(true));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize4)).Times(4)
+      .WillRepeatedly(testing::Return(true));
+  paced_sender_.Process();
+
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize4)).Times(5)
+      .WillRepeatedly(testing::Return(true));
+  paced_sender_.Process();
+
+  testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
+  EXPECT_CALL(mock_transport_, SendPacket(_, kSize4)).Times(0);
+  paced_sender_.Process();
+}
+
+}  // namespace cast
+}  // namespace media
diff --git a/media/cast/rtcp/mock_rtcp_receiver_feedback.h b/media/cast/rtcp/mock_rtcp_receiver_feedback.h
new file mode 100644
index 0000000..09e2cb4
--- /dev/null
+++ b/media/cast/rtcp/mock_rtcp_receiver_feedback.h
@@ -0,0 +1,38 @@
+// 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 MEDIA_CAST_RTCP_MOCK_RTCP_RECEIVER_FEEDBACK_H_
+#define MEDIA_CAST_RTCP_MOCK_RTCP_RECEIVER_FEEDBACK_H_
+
+#include <vector>
+
+#include "media/cast/rtcp/rtcp_receiver.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+namespace cast {
+
+class MockRtcpReceiverFeedback : public RtcpReceiverFeedback {
+ public:
+  MOCK_METHOD1(OnReceivedSenderReport,
+               void(const RtcpSenderInfo& remote_sender_info));
+
+  MOCK_METHOD1(OnReceiverReferenceTimeReport,
+               void(const RtcpReceiverReferenceTimeReport& remote_time_report));
+
+  MOCK_METHOD0(OnReceivedSendReportRequest, void());
+};
+
+class MockRtcpRttFeedback : public RtcpRttFeedback {
+ public:
+  MOCK_METHOD3(OnReceivedDelaySinceLastReport,
+               void(uint32 media_ssrc,
+                    uint32 last_report,
+                    uint32 delay_since_last_report));
+};
+
+}  // namespace cast
+}  // namespace media
+
+#endif  // MEDIA_CAST_RTCP_MOCK_RTCP_RECEIVER_FEEDBACK_H_
diff --git a/media/cast/rtcp/mock_rtcp_sender_feedback.h b/media/cast/rtcp/mock_rtcp_sender_feedback.h
new file mode 100644
index 0000000..3947625
--- /dev/null
+++ b/media/cast/rtcp/mock_rtcp_sender_feedback.h
@@ -0,0 +1,37 @@
+// 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 MEDIA_CAST_RTCP_MOCK_RTCP_SENDER_FEEDBACK_H_
+#define MEDIA_CAST_RTCP_MOCK_RTCP_SENDER_FEEDBACK_H_
+
+#include <vector>
+
+#include "media/cast/rtcp/rtcp_receiver.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+namespace cast {
+
+class MockRtcpSenderFeedback : public RtcpSenderFeedback {
+ public:
+  MOCK_METHOD1(OnReceivedReportBlock,
+               void(const RtcpReportBlock& report_block));
+
+  MOCK_METHOD0(OnReceivedIntraFrameRequest, void());
+
+  MOCK_METHOD2(OnReceivedRpsi, void(uint8 payload_type, uint64 picture_id));
+
+  MOCK_METHOD1(OnReceivedRemb, void(uint32 bitrate));
+
+  MOCK_METHOD1(OnReceivedNackRequest,
+               void(const std::list<uint16>& nack_sequence_numbers));
+
+  MOCK_METHOD1(OnReceivedCastFeedback,
+               void(const RtcpCastMessage& cast_feedback));
+};
+
+}  // namespace cast
+}  // namespace media
+
+#endif  // MEDIA_CAST_RTCP_MOCK_RTCP_SENDER_FEEDBACK_H_
diff --git a/media/cast/rtcp/rtcp.cc b/media/cast/rtcp/rtcp.cc
new file mode 100644
index 0000000..2ee2631
--- /dev/null
+++ b/media/cast/rtcp/rtcp.cc
@@ -0,0 +1,432 @@
+// 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 "media/cast/rtcp/rtcp.h"
+
+#include "base/debug/trace_event.h"
+#include "base/rand_util.h"
+#include "media/cast/cast_config.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/rtcp/rtcp_defines.h"
+#include "media/cast/rtcp/rtcp_receiver.h"
+#include "media/cast/rtcp/rtcp_sender.h"
+#include "media/cast/rtcp/rtcp_utility.h"
+
+namespace media {
+namespace cast {
+
+static const int kMaxRttMs = 1000000;  // 1000 seconds.
+
+// Time limit for received RTCP messages when we stop using it for lip-sync.
+static const int64 kMaxDiffSinceReceivedRtcpMs = 100000;  // 100 seconds.
+
+// Magic fractional unit.
+static const double kMagicFractionalUnit = 4.294967296E3;
+
+class LocalRtcpRttFeedback : public RtcpRttFeedback {
+ public:
+  explicit LocalRtcpRttFeedback(Rtcp* rtcp)
+      : rtcp_(rtcp) {
+  }
+
+  virtual void OnReceivedDelaySinceLastReport(
+      uint32 receivers_ssrc,
+      uint32 last_report,
+      uint32 delay_since_last_report) OVERRIDE {
+    rtcp_->OnReceivedDelaySinceLastReport(receivers_ssrc,
+                                          last_report,
+                                          delay_since_last_report);
+  }
+
+ private:
+  Rtcp* rtcp_;
+};
+
+RtcpCastMessage::RtcpCastMessage(uint32 media_ssrc)
+    : media_ssrc_(media_ssrc) {}
+
+RtcpCastMessage::~RtcpCastMessage() {}
+
+RtcpNackMessage::RtcpNackMessage() {}
+RtcpNackMessage::~RtcpNackMessage() {}
+
+RtcpRembMessage::RtcpRembMessage() {}
+RtcpRembMessage::~RtcpRembMessage() {}
+
+
+class LocalRtcpReceiverFeedback : public RtcpReceiverFeedback {
+ public:
+  explicit LocalRtcpReceiverFeedback(Rtcp* rtcp)
+      : rtcp_(rtcp) {
+  }
+
+  virtual void OnReceivedSenderReport(
+      const RtcpSenderInfo& remote_sender_info) OVERRIDE {
+    rtcp_->OnReceivedNtp(remote_sender_info.ntp_seconds,
+                         remote_sender_info.ntp_fraction);
+    if (remote_sender_info.send_packet_count != 0) {
+      rtcp_->OnReceivedLipSyncInfo(remote_sender_info.rtp_timestamp,
+                                   remote_sender_info.ntp_seconds,
+                                   remote_sender_info.ntp_fraction);
+    }
+  }
+
+  virtual void OnReceiverReferenceTimeReport(
+      const RtcpReceiverReferenceTimeReport& remote_time_report) OVERRIDE {
+    rtcp_->OnReceivedNtp(remote_time_report.ntp_seconds,
+                         remote_time_report.ntp_fraction);
+  }
+
+  virtual void OnReceivedSendReportRequest() OVERRIDE {
+    rtcp_->OnReceivedSendReportRequest();
+  }
+
+ private:
+  Rtcp* rtcp_;
+};
+
+Rtcp::Rtcp(RtcpSenderFeedback* sender_feedback,
+           PacedPacketSender* paced_packet_sender,
+           RtpSenderStatistics* rtp_sender_statistics,
+           RtpReceiverStatistics* rtp_receiver_statistics,
+           RtcpMode rtcp_mode,
+           const base::TimeDelta& rtcp_interval,
+           bool sending_media,
+           uint32 local_ssrc,
+           const std::string& c_name)
+    : rtcp_interval_(rtcp_interval),
+      rtcp_mode_(rtcp_mode),
+      sending_media_(sending_media),
+      local_ssrc_(local_ssrc),
+      rtp_sender_statistics_(rtp_sender_statistics),
+      rtp_receiver_statistics_(rtp_receiver_statistics),
+      receiver_feedback_(new LocalRtcpReceiverFeedback(this)),
+      rtt_feedback_(new LocalRtcpRttFeedback(this)),
+      rtcp_sender_(new RtcpSender(paced_packet_sender, local_ssrc, c_name)),
+      last_report_sent_(0),
+      last_report_received_(0),
+      last_received_rtp_timestamp_(0),
+      last_received_ntp_seconds_(0),
+      last_received_ntp_fraction_(0),
+      min_rtt_(base::TimeDelta::FromMilliseconds(kMaxRttMs)),
+      number_of_rtt_in_avg_(0),
+      clock_(&default_tick_clock_) {
+  rtcp_receiver_.reset(new RtcpReceiver(sender_feedback,
+                                        receiver_feedback_.get(),
+                                        rtt_feedback_.get(),
+                                        local_ssrc));
+}
+
+Rtcp::~Rtcp() {}
+
+base::TimeTicks Rtcp::TimeToSendNextRtcpReport() {
+  if (next_time_to_send_rtcp_.is_null()) {
+    UpdateNextTimeToSendRtcp();
+  }
+  return next_time_to_send_rtcp_;
+}
+
+void Rtcp::SetRemoteSSRC(uint32 ssrc) {
+  rtcp_receiver_->SetRemoteSSRC(ssrc);
+}
+
+bool Rtcp::IsRtcpPacket(const uint8* packet, int length) {
+  DCHECK_GE(length, 8) << "Invalid RTCP packet";
+  if (length < 8) return false;
+
+  uint8 packet_type = packet[1];
+  if (packet_type >= kPacketTypeLow && packet_type <= kPacketTypeHigh) {
+    return true;
+  }
+  return false;
+}
+
+void Rtcp::IncomingRtcpPacket(const uint8* rtcp_buffer, int length) {
+  RtcpParser rtcp_parser(rtcp_buffer, length);
+  if (!rtcp_parser.IsValid()) {
+    // Silently ignore packet.
+    DLOG(ERROR) << "Received invalid RTCP packet";
+    return;
+  }
+  rtcp_receiver_->IncomingRtcpPacket(&rtcp_parser);
+}
+
+void Rtcp::SendRtcpCast(const RtcpCastMessage& cast_message) {
+  uint32 packet_type_flags = 0;
+  base::TimeTicks now = clock_->NowTicks();
+
+  if (rtcp_mode_ == kRtcpCompound || now >= next_time_to_send_rtcp_) {
+    if (sending_media_) {
+      packet_type_flags = RtcpSender::kRtcpSr;
+    } else {
+      packet_type_flags = RtcpSender::kRtcpRr;
+    }
+  }
+  packet_type_flags |= RtcpSender::kRtcpCast;
+
+  SendRtcp(now, packet_type_flags, 0, &cast_message);
+}
+
+void Rtcp::SendRtcpPli(uint32 pli_remote_ssrc) {
+  uint32 packet_type_flags = 0;
+  base::TimeTicks now = clock_->NowTicks();
+
+  if (rtcp_mode_ == kRtcpCompound || now >= next_time_to_send_rtcp_) {
+    if (sending_media_) {
+      packet_type_flags = RtcpSender::kRtcpSr;
+    } else {
+      packet_type_flags = RtcpSender::kRtcpRr;
+    }
+  }
+  packet_type_flags |= RtcpSender::kRtcpPli;
+  SendRtcp(now, packet_type_flags, pli_remote_ssrc, NULL);
+}
+
+void Rtcp::SendRtcpReport(uint32 media_ssrc) {
+  uint32 packet_type_flags;
+  base::TimeTicks now = clock_->NowTicks();
+  if (sending_media_) {
+    packet_type_flags = RtcpSender::kRtcpSr;
+  } else {
+    packet_type_flags = RtcpSender::kRtcpRr;
+  }
+  SendRtcp(now, packet_type_flags, media_ssrc, NULL);
+}
+
+void Rtcp::SendRtcp(const base::TimeTicks& now,
+                    uint32 packet_type_flags,
+                    uint32 media_ssrc,
+                    const RtcpCastMessage* cast_message) {
+  if (packet_type_flags & RtcpSender::kRtcpSr ||
+      packet_type_flags & RtcpSender::kRtcpRr) {
+    UpdateNextTimeToSendRtcp();
+  }
+  if (packet_type_flags & RtcpSender::kRtcpSr) {
+    RtcpSenderInfo sender_info;
+
+    if (rtp_sender_statistics_) {
+      rtp_sender_statistics_->GetStatistics(now, &sender_info);
+    } else {
+      memset(&sender_info, 0, sizeof(sender_info));
+    }
+    time_last_report_sent_ = now;
+    last_report_sent_ = (sender_info.ntp_seconds << 16) +
+                        (sender_info.ntp_fraction >> 16);
+
+    RtcpDlrrReportBlock dlrr;
+    if (!time_last_report_received_.is_null()) {
+      packet_type_flags |= RtcpSender::kRtcpDlrr;
+      dlrr.last_rr = last_report_received_;
+      uint32 delay_seconds = 0;
+      uint32 delay_fraction = 0;
+      base::TimeDelta delta = now - time_last_report_received_;
+      ConvertTimeToFractions(delta.InMicroseconds(),
+                             &delay_seconds,
+                             &delay_fraction);
+
+      dlrr.delay_since_last_rr =
+         ConvertToNtpDiff(delay_seconds, delay_fraction);
+    }
+    rtcp_sender_->SendRtcp(packet_type_flags,
+                           &sender_info,
+                           NULL,
+                           media_ssrc,
+                           &dlrr,
+                           NULL,
+                           NULL);
+  } else {
+    RtcpReportBlock report_block;
+    report_block.remote_ssrc = 0;  // Not needed to set send side.
+    report_block.media_ssrc = media_ssrc;  // SSRC of the RTP packet sender.
+    if (rtp_receiver_statistics_) {
+      rtp_receiver_statistics_->GetStatistics(
+          &report_block.fraction_lost,
+          &report_block.cumulative_lost,
+          &report_block.extended_high_sequence_number,
+          &report_block.jitter);
+    }
+
+    report_block.last_sr = last_report_received_;
+    if (!time_last_report_received_.is_null()) {
+      uint32 delay_seconds = 0;
+      uint32 delay_fraction = 0;
+      base::TimeDelta delta = now - time_last_report_received_;
+      ConvertTimeToFractions(delta.InMicroseconds(),
+                             &delay_seconds,
+                             &delay_fraction);
+      report_block.delay_since_last_sr =
+          ConvertToNtpDiff(delay_seconds, delay_fraction);
+    } else {
+      report_block.delay_since_last_sr = 0;
+    }
+
+    packet_type_flags |= RtcpSender::kRtcpRrtr;
+    RtcpReceiverReferenceTimeReport rrtr;
+    ConvertTimeToNtp(now, &rrtr.ntp_seconds, &rrtr.ntp_fraction);
+
+    time_last_report_sent_ = now;
+    last_report_sent_ = ConvertToNtpDiff(rrtr.ntp_seconds, rrtr.ntp_fraction);
+
+    rtcp_sender_->SendRtcp(packet_type_flags,
+                           NULL,
+                           &report_block,
+                           media_ssrc,
+                           NULL,
+                           &rrtr,
+                           cast_message);
+  }
+}
+
+void Rtcp::OnReceivedNtp(uint32 ntp_seconds, uint32 ntp_fraction) {
+  last_report_received_ = (ntp_seconds << 16) + (ntp_fraction >> 16);
+
+  base::TimeTicks now = clock_->NowTicks();
+  time_last_report_received_ = now;
+}
+
+void Rtcp::OnReceivedLipSyncInfo(uint32 rtp_timestamp,
+                                 uint32 ntp_seconds,
+                                 uint32 ntp_fraction) {
+  last_received_rtp_timestamp_ = rtp_timestamp;
+  last_received_ntp_seconds_ = ntp_seconds;
+  last_received_ntp_fraction_ = ntp_fraction;
+}
+
+void Rtcp::OnReceivedSendReportRequest() {
+  base::TimeTicks now = clock_->NowTicks();
+
+  // Trigger a new RTCP report at next timer.
+  next_time_to_send_rtcp_ = now;
+}
+
+bool Rtcp::RtpTimestampInSenderTime(int frequency, uint32 rtp_timestamp,
+      base::TimeTicks* rtp_timestamp_in_ticks) const {
+  if (last_received_ntp_seconds_ == 0)  return false;
+
+  int wrap = CheckForWrapAround(rtp_timestamp, last_received_rtp_timestamp_);
+  int64 rtp_timestamp_int64 = rtp_timestamp;
+  int64 last_received_rtp_timestamp_int64 = last_received_rtp_timestamp_;
+
+  if (wrap == 1) {
+    rtp_timestamp_int64 += (1LL << 32);
+  } else if (wrap == -1) {
+    last_received_rtp_timestamp_int64 += (1LL << 32);
+  }
+  // Time since the last RTCP message.
+  // Note that this can be negative since we can compare a rtp timestamp from
+  // a frame older than the last received RTCP message.
+  int64 rtp_timestamp_diff =
+      rtp_timestamp_int64 - last_received_rtp_timestamp_int64;
+
+  int frequency_khz = frequency / 1000;
+  int64 rtp_time_diff_ms = rtp_timestamp_diff / frequency_khz;
+
+  // Sanity check.
+  if (abs(rtp_time_diff_ms) > kMaxDiffSinceReceivedRtcpMs)  return false;
+
+  *rtp_timestamp_in_ticks =
+     ConvertNtpToTime(last_received_ntp_seconds_, last_received_ntp_fraction_) +
+     base::TimeDelta::FromMilliseconds(rtp_time_diff_ms);
+  return true;
+}
+
+void Rtcp::OnReceivedDelaySinceLastReport(uint32 receivers_ssrc,
+                                          uint32 last_report,
+                                          uint32 delay_since_last_report) {
+  if (last_report_sent_ != last_report)  return;  // Feedback on another report.
+  if (time_last_report_sent_.is_null())  return;
+
+  base::TimeDelta sender_delay = clock_->NowTicks() - time_last_report_sent_;
+  UpdateRtt(sender_delay, ConvertFromNtpDiff(delay_since_last_report));
+}
+
+void Rtcp::UpdateRtt(const base::TimeDelta& sender_delay,
+                     const base::TimeDelta& receiver_delay) {
+  base::TimeDelta rtt = sender_delay - receiver_delay;
+  rtt = std::max(rtt, base::TimeDelta::FromMilliseconds(1));
+  rtt_ = rtt;
+  min_rtt_ = std::min(min_rtt_, rtt);
+  max_rtt_ = std::max(max_rtt_, rtt);
+
+  if (number_of_rtt_in_avg_ != 0) {
+    float ac = static_cast<float>(number_of_rtt_in_avg_);
+    avg_rtt_ms_= ((ac / (ac + 1.0)) * avg_rtt_ms_) +
+        ((1.0 / (ac + 1.0)) * rtt.InMilliseconds());
+  } else {
+    avg_rtt_ms_ = rtt.InMilliseconds();
+  }
+  number_of_rtt_in_avg_++;
+  TRACE_COUNTER_ID1("cast_rtcp", "RTT", local_ssrc_, rtt.InMilliseconds());
+}
+
+bool Rtcp::Rtt(base::TimeDelta* rtt,
+               base::TimeDelta* avg_rtt,
+               base::TimeDelta* min_rtt,
+               base::TimeDelta* max_rtt) const {
+  DCHECK(rtt) << "Invalid argument";
+  DCHECK(avg_rtt) << "Invalid argument";
+  DCHECK(min_rtt) << "Invalid argument";
+  DCHECK(max_rtt) << "Invalid argument";
+
+  if (number_of_rtt_in_avg_ == 0)  return false;
+
+  *rtt = rtt_;
+  *avg_rtt = base::TimeDelta::FromMilliseconds(avg_rtt_ms_);
+  *min_rtt = min_rtt_;
+  *max_rtt = max_rtt_;
+  return true;
+}
+
+void Rtcp::ConvertTimeToFractions(int64 time_us,
+                                  uint32* seconds,
+                                  uint32* fractions) const {
+  *seconds = static_cast<uint32>(time_us / base::Time::kMicrosecondsPerSecond);
+  *fractions = static_cast<uint32>(
+      (time_us % base::Time::kMicrosecondsPerSecond) * kMagicFractionalUnit);
+}
+
+void Rtcp::ConvertTimeToNtp(const base::TimeTicks& time,
+                            uint32* ntp_seconds,
+                            uint32* ntp_fractions) const {
+  int64 time_us = time.ToInternalValue() - kNtpEpochDeltaMicroseconds;
+  ConvertTimeToFractions(time_us, ntp_seconds, ntp_fractions);
+}
+
+base::TimeTicks Rtcp::ConvertNtpToTime(uint32 ntp_seconds,
+                                       uint32 ntp_fractions) const {
+  int64 ntp_time_us = static_cast<int64>(ntp_seconds) *
+       base::Time::kMicrosecondsPerSecond;
+  ntp_time_us += static_cast<int64>(ntp_fractions) / kMagicFractionalUnit;
+  return base::TimeTicks::FromInternalValue(ntp_time_us +
+      kNtpEpochDeltaMicroseconds);
+}
+
+int Rtcp::CheckForWrapAround(uint32 new_timestamp,
+                             uint32 old_timestamp) const {
+  if (new_timestamp < old_timestamp) {
+    // This difference should be less than -2^31 if we have had a wrap around
+    // (e.g. |new_timestamp| = 1, |rtcp_rtp_timestamp| = 2^32 - 1). Since it is
+    // cast to a int32_t, it should be positive.
+    if (static_cast<int32>(new_timestamp - old_timestamp) > 0) {
+      return 1;  // Forward wrap around.
+    }
+  } else if (static_cast<int32>(old_timestamp - new_timestamp) > 0) {
+    // This difference should be less than -2^31 if we have had a backward wrap
+    // around. Since it is cast to a int32, it should be positive.
+    return -1;
+  }
+  return 0;
+}
+
+void Rtcp::UpdateNextTimeToSendRtcp() {
+  int random = base::RandInt(0, 999);
+  base::TimeDelta time_to_next = (rtcp_interval_ / 2) +
+      (rtcp_interval_ * random / 1000);
+
+  base::TimeTicks now = clock_->NowTicks();
+  next_time_to_send_rtcp_ = now + time_to_next;
+}
+
+}  // namespace cast
+}  // namespace media
diff --git a/media/cast/rtcp/rtcp.gyp b/media/cast/rtcp/rtcp.gyp
new file mode 100644
index 0000000..1411998
--- /dev/null
+++ b/media/cast/rtcp/rtcp.gyp
@@ -0,0 +1,46 @@
+# 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.
+
+{
+  'targets': [
+    {
+      'target_name': 'cast_rtcp',
+      'type': 'static_library',
+      'include_dirs': [
+        '<(DEPTH)/',
+      ],
+      'sources': [
+        'rtcp_defines.h',
+        'rtcp.h',
+        'rtcp.cc',
+        'rtcp_receiver.cc',
+        'rtcp_receiver.h',
+        'rtcp_sender.cc',
+        'rtcp_sender.h',
+        'rtcp_utility.cc',
+        'rtcp_utility.h',
+      ], # source
+      'dependencies': [
+        '<(DEPTH)/base/base.gyp:base',
+        '<(DEPTH)/net/net.gyp:net',
+      ],
+    },
+    {
+      'target_name': 'cast_rtcp_test',
+      'type': 'static_library',
+      'include_dirs': [
+        '<(DEPTH)/',
+      ],
+      'sources': [
+        'test_rtcp_packet_builder.cc',
+        'test_rtcp_packet_builder.h',
+      ], # source
+      'dependencies': [
+        'cast_rtcp',
+        '<(DEPTH)/testing/gtest.gyp:gtest',
+      ],
+    },
+  ],
+}
+
diff --git a/media/cast/rtcp/rtcp.h b/media/cast/rtcp/rtcp.h
new file mode 100644
index 0000000..9cf9708
--- /dev/null
+++ b/media/cast/rtcp/rtcp.h
@@ -0,0 +1,199 @@
+// 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 MEDIA_CAST_RTCP_RTCP_H_
+#define MEDIA_CAST_RTCP_RTCP_H_
+
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/default_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "media/cast/cast_config.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/rtcp/rtcp_defines.h"
+
+namespace media {
+namespace cast {
+
+class LocalRtcpReceiverFeedback;
+class LocalRtcpRttFeedback;
+class PacedPacketSender;
+class RtcpReceiver;
+class RtcpSender;
+
+class RtcpSenderFeedback {
+ public:
+  virtual void OnReceivedReportBlock(const RtcpReportBlock& report_block) = 0;
+
+  virtual void OnReceivedIntraFrameRequest() = 0;
+
+  virtual void OnReceivedRpsi(uint8 payload_type, uint64 picture_id) = 0;
+
+  virtual void OnReceivedRemb(uint32 bitrate) = 0;
+
+  virtual void OnReceivedNackRequest(
+      const std::list<uint16>& nack_sequence_numbers) = 0;
+
+  virtual void OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) = 0;
+
+  virtual ~RtcpSenderFeedback() {}
+};
+
+class RtpSenderStatistics {
+ public:
+  virtual void GetStatistics(const base::TimeTicks& now,
+                             RtcpSenderInfo* sender_info) = 0;
+
+  virtual ~RtpSenderStatistics() {}
+};
+
+class RtpReceiverStatistics {
+ public:
+  virtual void GetStatistics(uint8* fraction_lost,
+                             uint32* cumulative_lost,  // 24 bits valid.
+                             uint32* extended_high_sequence_number,
+                             uint32* jitter) = 0;
+
+  virtual ~RtpReceiverStatistics() {}
+};
+
+class Rtcp {
+ public:
+  // Network Time Protocol (NTP), which is in seconds relative to 0h UTC on
+  // 1 January 1900.
+  static const int64 kNtpEpochDeltaSeconds = GG_INT64_C(9435484800);
+  static const int64 kNtpEpochDeltaMicroseconds =
+      kNtpEpochDeltaSeconds * base::Time::kMicrosecondsPerSecond;
+
+  Rtcp(RtcpSenderFeedback* sender_feedback,
+       PacedPacketSender* paced_packet_sender,
+       RtpSenderStatistics* rtp_sender_statistics,
+       RtpReceiverStatistics* rtp_receiver_statistics,
+       RtcpMode rtcp_mode,
+       const base::TimeDelta& rtcp_interval,
+       bool sending_media,
+       uint32 local_ssrc,
+       const std::string& c_name);
+
+  virtual ~Rtcp();
+
+  static bool IsRtcpPacket(const uint8* rtcp_buffer, int length);
+
+  base::TimeTicks TimeToSendNextRtcpReport();
+  void SendRtcpReport(uint32 media_ssrc);
+  void SendRtcpPli(uint32 media_ssrc);
+  void SendRtcpCast(const RtcpCastMessage& cast_message);
+  void SetRemoteSSRC(uint32 ssrc);
+
+  void IncomingRtcpPacket(const uint8* rtcp_buffer, int length);
+  bool Rtt(base::TimeDelta* rtt, base::TimeDelta* avg_rtt,
+           base::TimeDelta* min_rtt,  base::TimeDelta* max_rtt) const;
+  bool RtpTimestampInSenderTime(int frequency,
+                                uint32 rtp_timestamp,
+                                base::TimeTicks* rtp_timestamp_in_ticks) const;
+
+  void set_clock(base::TickClock* clock) {
+    clock_ = clock;
+  }
+
+ protected:
+  void ConvertTimeToNtp(const base::TimeTicks& time,
+                        uint32* ntp_seconds,
+                        uint32* ntp_fractions) const;
+
+  base::TimeTicks ConvertNtpToTime(uint32 ntp_seconds,
+                                   uint32 ntp_fractions) const;
+
+  int CheckForWrapAround(uint32 new_timestamp,
+                         uint32 old_timestamp) const;
+
+  void OnReceivedLipSyncInfo(uint32 rtp_timestamp,
+                             uint32 ntp_seconds,
+                             uint32 ntp_fraction);
+ private:
+  friend class LocalRtcpRttFeedback;
+  friend class LocalRtcpReceiverFeedback;
+
+  void SendRtcp(const base::TimeTicks& now,
+                uint32 packet_type_flags,
+                uint32 media_ssrc,
+                const RtcpCastMessage* cast_message);
+
+  void OnReceivedNtp(uint32 ntp_seconds, uint32 ntp_fraction);
+
+  void OnReceivedDelaySinceLastReport(uint32 receivers_ssrc,
+                                      uint32 last_report,
+                                      uint32 delay_since_last_report);
+
+  void OnReceivedSendReportRequest();
+
+  void UpdateRtt(const base::TimeDelta& sender_delay,
+                 const base::TimeDelta& receiver_delay);
+
+  void ConvertTimeToFractions(int64 time_us,
+                              uint32* seconds,
+                              uint32* fractions) const;
+
+  void UpdateNextTimeToSendRtcp();
+
+  inline uint32 ConvertToNtpDiff(uint32 delay_seconds, uint32 delay_fraction) {
+    return ((delay_seconds & 0x0000FFFF) << 16) +
+           ((delay_fraction & 0xFFFF0000) >> 16);
+  }
+
+  inline base::TimeDelta ConvertFromNtpDiff(uint32 ntp_delay) {
+    uint32 delay_ms = (ntp_delay & 0x0000ffff) * 1000;
+    delay_ms /= 65536;
+    delay_ms += ((ntp_delay & 0xffff0000) >> 16) * 1000;
+    return base::TimeDelta::FromMilliseconds(delay_ms);
+  }
+
+  const base::TimeDelta rtcp_interval_;
+  const RtcpMode rtcp_mode_;
+  const bool sending_media_;
+  const uint32 local_ssrc_;
+
+  // Not owned by this class.
+  RtpSenderStatistics* const rtp_sender_statistics_;
+  RtpReceiverStatistics* const rtp_receiver_statistics_;
+
+  scoped_ptr<LocalRtcpRttFeedback> rtt_feedback_;
+  scoped_ptr<LocalRtcpReceiverFeedback> receiver_feedback_;
+  scoped_ptr<RtcpSender> rtcp_sender_;
+  scoped_ptr<RtcpReceiver> rtcp_receiver_;
+
+  base::TimeTicks next_time_to_send_rtcp_;
+
+  base::TimeTicks time_last_report_sent_;
+  uint32 last_report_sent_;
+
+  base::TimeTicks time_last_report_received_;
+  uint32 last_report_received_;
+
+  uint32 last_received_rtp_timestamp_;
+  uint32 last_received_ntp_seconds_;
+  uint32 last_received_ntp_fraction_;
+
+  base::TimeDelta rtt_;
+  base::TimeDelta min_rtt_;
+  base::TimeDelta max_rtt_;
+  int number_of_rtt_in_avg_;
+  float avg_rtt_ms_;
+
+  base::DefaultTickClock default_tick_clock_;
+  base::TickClock* clock_;
+
+  DISALLOW_COPY_AND_ASSIGN(Rtcp);
+};
+
+}  // namespace cast
+}  // namespace media
+
+#endif  // MEDIA_CAST_RTCP_RTCP_H_
diff --git a/media/cast/rtcp/rtcp_defines.h b/media/cast/rtcp/rtcp_defines.h
new file mode 100644
index 0000000..102e321
--- /dev/null
+++ b/media/cast/rtcp/rtcp_defines.h
@@ -0,0 +1,118 @@
+// 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 MEDIA_CAST_RTCP_RTCP_DEFINES_H_
+#define MEDIA_CAST_RTCP_RTCP_DEFINES_H_
+
+#include <list>
+#include <map>
+#include <set>
+
+#include "media/cast/cast_config.h"
+#include "media/cast/cast_defines.h"
+
+namespace media {
+namespace cast {
+
+const uint16 kRtcpCastAllPacketsLost = 0xffff;
+
+typedef std::set<uint16> PacketIdSet;
+typedef std::map<uint8, PacketIdSet> MissingFramesAndPacketsMap;
+
+class RtcpCastMessage {
+ public:
+  explicit RtcpCastMessage(uint32 media_ssrc);
+  ~RtcpCastMessage();
+
+  uint32 media_ssrc_;
+  uint8 ack_frame_id_;
+  MissingFramesAndPacketsMap missing_frames_and_packets_;
+};
+
+struct RtcpSenderInfo {
+  // First three members are used for lipsync.
+  // First two members are used for rtt.
+  uint32 ntp_seconds;
+  uint32 ntp_fraction;
+  uint32 rtp_timestamp;
+  uint32 send_packet_count;
+  uint32 send_octet_count;
+};
+
+struct RtcpReportBlock {
+  uint32 remote_ssrc;  // SSRC of sender of this report.
+  uint32 media_ssrc;  // SSRC of the RTP packet sender.
+  uint8 fraction_lost;
+  uint32 cumulative_lost;  // 24 bits valid.
+  uint32 extended_high_sequence_number;
+  uint32 jitter;
+  uint32 last_sr;
+  uint32 delay_since_last_sr;
+};
+
+struct RtcpRpsiMessage {
+  uint32 remote_ssrc;
+  uint8 payload_type;
+  uint64 picture_id;
+};
+
+class RtcpNackMessage {
+ public:
+  RtcpNackMessage();
+  ~RtcpNackMessage();
+
+  uint32 remote_ssrc;
+  std::list<uint16> nack_list;
+};
+
+class RtcpRembMessage {
+ public:
+  RtcpRembMessage();
+  ~RtcpRembMessage();
+
+  uint32 remb_bitrate;
+  std::list<uint32> remb_ssrcs;
+};
+
+struct RtcpReceiverReferenceTimeReport {
+  uint32 remote_ssrc;
+  uint32 ntp_seconds;
+  uint32 ntp_fraction;
+};
+
+struct RtcpDlrrReportBlock {
+  uint32 last_rr;
+  uint32 delay_since_last_rr;
+};
+
+inline bool operator==(RtcpReportBlock lhs, RtcpReportBlock rhs) {
+  return lhs.remote_ssrc == rhs.remote_ssrc &&
+      lhs.media_ssrc == rhs.media_ssrc &&
+      lhs.fraction_lost == rhs.fraction_lost &&
+      lhs.cumulative_lost == rhs.cumulative_lost &&
+      lhs.extended_high_sequence_number == rhs.extended_high_sequence_number &&
+      lhs.jitter == rhs.jitter &&
+      lhs.last_sr == rhs.last_sr &&
+      lhs.delay_since_last_sr == rhs.delay_since_last_sr;
+}
+
+inline bool operator==(RtcpSenderInfo lhs, RtcpSenderInfo rhs) {
+  return lhs.ntp_seconds == rhs.ntp_seconds &&
+      lhs.ntp_fraction == rhs.ntp_fraction &&
+      lhs.rtp_timestamp == rhs.rtp_timestamp &&
+      lhs.send_packet_count == rhs.send_packet_count &&
+      lhs.send_octet_count == rhs.send_octet_count;
+}
+
+inline bool operator==(RtcpReceiverReferenceTimeReport lhs,
+                       RtcpReceiverReferenceTimeReport rhs) {
+  return lhs.remote_ssrc == rhs.remote_ssrc &&
+      lhs.ntp_seconds == rhs.ntp_seconds &&
+      lhs.ntp_fraction == rhs.ntp_fraction;
+}
+
+}  // namespace cast
+}  // namespace media
+
+#endif  // MEDIA_CAST_RTCP_RTCP_DEFINES_H_
diff --git a/media/cast/rtcp/rtcp_receiver.cc b/media/cast/rtcp/rtcp_receiver.cc
new file mode 100644
index 0000000..c0e9b9b
--- /dev/null
+++ b/media/cast/rtcp/rtcp_receiver.cc
@@ -0,0 +1,465 @@
+// 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 "media/cast/rtcp/rtcp_receiver.h"
+
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "media/cast/rtcp/rtcp_utility.h"
+
+namespace media {
+namespace cast {
+
+RtcpReceiver::RtcpReceiver(RtcpSenderFeedback* sender_feedback,
+                           RtcpReceiverFeedback* receiver_feedback,
+                           RtcpRttFeedback* rtt_feedback,
+                           uint32 local_ssrc)
+    :  ssrc_(local_ssrc),
+      remote_ssrc_(0),
+      sender_feedback_(sender_feedback),
+      receiver_feedback_(receiver_feedback),
+      rtt_feedback_(rtt_feedback) {
+}
+
+RtcpReceiver::~RtcpReceiver() {}
+
+void RtcpReceiver::SetRemoteSSRC(uint32 ssrc) {
+  remote_ssrc_ = ssrc;
+}
+
+void RtcpReceiver::IncomingRtcpPacket(RtcpParser* rtcp_parser) {
+  RtcpFieldTypes field_type = rtcp_parser->Begin();
+  while (field_type != kRtcpNotValidCode) {
+    // Each "case" is responsible for iterate the parser to the next top
+    // level packet.
+    switch (field_type) {
+      case kRtcpSrCode:
+        HandleSenderReport(rtcp_parser);
+        break;
+      case kRtcpRrCode:
+        HandleReceiverReport(rtcp_parser);
+        break;
+      case kRtcpSdesCode:
+        HandleSDES(rtcp_parser);
+        break;
+      case kRtcpByeCode:
+        HandleBYE(rtcp_parser);
+        break;
+      case kRtcpXrCode:
+        HandleXr(rtcp_parser);
+        break;
+      case kRtcpGenericRtpFeedbackNackCode:
+        HandleNACK(rtcp_parser);
+        break;
+      case kRtcpGenericRtpFeedbackSrReqCode:
+        HandleSendReportRequest(rtcp_parser);
+        break;
+      case kRtcpPayloadSpecificPliCode:
+        HandlePLI(rtcp_parser);
+        break;
+      case kRtcpPayloadSpecificRpsiCode:
+        HandleRpsi(rtcp_parser);
+        break;
+      case kRtcpPayloadSpecificFirCode:
+        HandleFIR(rtcp_parser);
+        break;
+      case kRtcpPayloadSpecificAppCode:
+        HandlePayloadSpecificApp(rtcp_parser);
+        break;
+      case kRtcpPayloadSpecificRembCode:
+      case kRtcpPayloadSpecificRembItemCode:
+        // Ignore this until we want to support interop with webrtc.
+        rtcp_parser->Iterate();
+        break;
+      case kRtcpPayloadSpecificCastCode:
+      case kRtcpPayloadSpecificCastNackItemCode:
+        rtcp_parser->Iterate();
+        break;
+      case kRtcpNotValidCode:
+      case kRtcpReportBlockItemCode:
+      case kRtcpSdesChunkCode:
+      case kRtcpGenericRtpFeedbackNackItemCode:
+      case kRtcpPayloadSpecificFirItemCode:
+      case kRtcpXrRrtrCode:
+      case kRtcpXrDlrrCode:
+      case kRtcpXrUnknownItemCode:
+        rtcp_parser->Iterate();
+        DCHECK(false) << "Invalid state";
+        break;
+    }
+    field_type = rtcp_parser->FieldType();
+  }
+}
+
+void RtcpReceiver::HandleSenderReport(RtcpParser* rtcp_parser) {
+  RtcpFieldTypes rtcp_field_type = rtcp_parser->FieldType();
+  const RtcpField& rtcp_field = rtcp_parser->Field();
+
+  DCHECK(rtcp_field_type == kRtcpSrCode) << "Invalid state";
+
+  // Synchronization source identifier for the originator of this SR packet.
+  uint32 remote_ssrc = rtcp_field.sender_report.sender_ssrc;
+
+  TRACE_EVENT_INSTANT1("cast_rtcp", "SR", TRACE_EVENT_SCOPE_THREAD,
+                       "remote_ssrc", remote_ssrc);
+
+  if (remote_ssrc_ == remote_ssrc) {
+    RtcpSenderInfo remote_sender_info;
+    remote_sender_info.ntp_seconds =
+        rtcp_field.sender_report.ntp_most_significant;
+    remote_sender_info.ntp_fraction =
+        rtcp_field.sender_report.ntp_least_significant;
+    remote_sender_info.rtp_timestamp =
+        rtcp_field.sender_report.rtp_timestamp;
+    remote_sender_info.send_packet_count =
+        rtcp_field.sender_report.sender_packet_count;
+    remote_sender_info.send_octet_count =
+        rtcp_field.sender_report.sender_octet_count;
+    if (receiver_feedback_) {
+      receiver_feedback_->OnReceivedSenderReport(remote_sender_info);
+    }
+  }
+  rtcp_field_type = rtcp_parser->Iterate();
+  while (rtcp_field_type == kRtcpReportBlockItemCode) {
+    HandleReportBlock(&rtcp_field, remote_ssrc);
+    rtcp_field_type = rtcp_parser->Iterate();
+  }
+}
+
+void RtcpReceiver::HandleReceiverReport(RtcpParser* rtcp_parser) {
+  RtcpFieldTypes rtcp_field_type = rtcp_parser->FieldType();
+  const RtcpField& rtcp_field = rtcp_parser->Field();
+
+  DCHECK(rtcp_field_type == kRtcpRrCode) << "Invalid state";
+
+  uint32 remote_ssrc = rtcp_field.receiver_report.sender_ssrc;
+
+  TRACE_EVENT_INSTANT1("cast_rtcp", "RR", TRACE_EVENT_SCOPE_THREAD,
+                       "remote_ssrc", remote_ssrc);
+
+  rtcp_field_type = rtcp_parser->Iterate();
+  while (rtcp_field_type == kRtcpReportBlockItemCode) {
+    HandleReportBlock(&rtcp_field, remote_ssrc);
+    rtcp_field_type = rtcp_parser->Iterate();
+  }
+}
+
+void RtcpReceiver::HandleReportBlock(const RtcpField* rtcp_field,
+                                     uint32 remote_ssrc) {
+  // This will be called once per report block in the Rtcp packet.
+  // We filter out all report blocks that are not for us.
+  // Each packet has max 31 RR blocks.
+  //
+  // We can calculate RTT if we send a send report and get a report block back.
+
+  // |rtcp_field.ReportBlockItem.ssrc| is the ssrc identifier of the source to
+  // which the information in this reception report block pertains.
+
+  const RtcpFieldReportBlockItem& rb = rtcp_field->report_block_item;
+
+  // Filter out all report blocks that are not for us.
+  if (rb.ssrc != ssrc_) {
+    // This block is not for us ignore it.
+    return;
+  }
+  TRACE_EVENT_INSTANT2("cast_rtcp", "RB", TRACE_EVENT_SCOPE_THREAD,
+                       "remote_ssrc", remote_ssrc,
+                       "ssrc", ssrc_);
+
+  TRACE_COUNTER_ID1("cast_rtcp", "RtcpReceiver::FractionLost",
+                    rb.ssrc, rb.fraction_lost);
+  TRACE_COUNTER_ID1("cast_rtcp", "RtcpReceiver::CumulativeNumberOfPacketsLost",
+                    rb.ssrc, rb.cumulative_number_of_packets_lost);
+  TRACE_COUNTER_ID1("cast_rtcp", "RtcpReceiver::Jitter",
+                    rb.ssrc, rb.jitter);
+
+  RtcpReportBlock report_block;
+  report_block.remote_ssrc = remote_ssrc;
+  report_block.media_ssrc = rb.ssrc;
+  report_block.fraction_lost = rb.fraction_lost;
+  report_block.cumulative_lost = rb.cumulative_number_of_packets_lost;
+  report_block.extended_high_sequence_number =
+      rb.extended_highest_sequence_number;
+  report_block.jitter = rb.jitter;
+  report_block.last_sr = rb.last_sender_report;
+  report_block.delay_since_last_sr = rb.delay_last_sender_report;
+
+  if (sender_feedback_) {
+    sender_feedback_->OnReceivedReportBlock(report_block);
+  }
+  if (rtt_feedback_) {
+    rtt_feedback_->OnReceivedDelaySinceLastReport(rb.ssrc,
+                                                  rb.last_sender_report,
+                                                  rb.delay_last_sender_report);
+  }
+}
+
+void RtcpReceiver::HandleSDES(RtcpParser* rtcp_parser) {
+  RtcpFieldTypes field_type = rtcp_parser->Iterate();
+  while (field_type == kRtcpSdesChunkCode) {
+    HandleSDESChunk(rtcp_parser);
+    field_type = rtcp_parser->Iterate();
+  }
+}
+
+void RtcpReceiver::HandleSDESChunk(RtcpParser* rtcp_parser) {
+  const RtcpField& rtcp_field = rtcp_parser->Field();
+  TRACE_EVENT_INSTANT1("cast_rtcp", "SDES", TRACE_EVENT_SCOPE_THREAD,
+                       "cname", TRACE_STR_COPY(rtcp_field.c_name.name));
+}
+
+void RtcpReceiver::HandleXr(RtcpParser* rtcp_parser) {
+  RtcpFieldTypes rtcp_field_type = rtcp_parser->FieldType();
+  const RtcpField& rtcp_field = rtcp_parser->Field();
+
+  DCHECK(rtcp_field_type == kRtcpXrCode) << "Invalid state";
+
+  uint32 remote_ssrc = rtcp_field.extended_report.sender_ssrc;
+  rtcp_field_type = rtcp_parser->Iterate();
+
+  while (rtcp_field_type == kRtcpXrDlrrCode ||
+         rtcp_field_type == kRtcpXrRrtrCode ||
+         rtcp_field_type == kRtcpXrUnknownItemCode) {
+    if (rtcp_field_type == kRtcpXrRrtrCode) {
+      HandleRrtr(rtcp_parser, remote_ssrc);
+    } else if (rtcp_field_type == kRtcpXrDlrrCode) {
+      HandleDlrr(rtcp_parser);
+    }
+    rtcp_field_type = rtcp_parser->Iterate();
+  }
+}
+
+void RtcpReceiver::HandleRrtr(RtcpParser* rtcp_parser, uint32 remote_ssrc) {
+  if (remote_ssrc_ != remote_ssrc) {
+    // Not to us.
+    return;
+  }
+  const RtcpField& rtcp_field = rtcp_parser->Field();
+  RtcpReceiverReferenceTimeReport remote_time_report;
+  remote_time_report.remote_ssrc = remote_ssrc;
+  remote_time_report.ntp_seconds = rtcp_field.rrtr.ntp_most_significant;
+  remote_time_report.ntp_fraction = rtcp_field.rrtr.ntp_least_significant;
+
+  if (receiver_feedback_) {
+    receiver_feedback_->OnReceiverReferenceTimeReport(remote_time_report);
+  }
+}
+
+void RtcpReceiver::HandleDlrr(RtcpParser* rtcp_parser) {
+  const RtcpField& rtcp_field = rtcp_parser->Field();
+  if (remote_ssrc_ != rtcp_field.dlrr.receivers_ssrc) {
+    // Not to us.
+    return;
+  }
+  if (rtt_feedback_) {
+    rtt_feedback_->OnReceivedDelaySinceLastReport(
+        rtcp_field.dlrr.receivers_ssrc,
+        rtcp_field.dlrr.last_receiver_report,
+        rtcp_field.dlrr.delay_last_receiver_report);
+  }
+}
+
+void RtcpReceiver::HandleNACK(RtcpParser* rtcp_parser) {
+  const RtcpField& rtcp_field = rtcp_parser->Field();
+  if (ssrc_ != rtcp_field.nack.media_ssrc) {
+    // Not to us.
+    rtcp_parser->Iterate();
+    return;
+  }
+  std::list<uint16> nackSequenceNumbers;
+
+  RtcpFieldTypes field_type = rtcp_parser->Iterate();
+  while (field_type == kRtcpGenericRtpFeedbackNackItemCode) {
+    HandleNACKItem(&rtcp_field, &nackSequenceNumbers);
+    field_type = rtcp_parser->Iterate();
+  }
+  if (sender_feedback_) {
+    sender_feedback_->OnReceivedNackRequest(nackSequenceNumbers);
+  }
+}
+
+void RtcpReceiver::HandleNACKItem(const RtcpField* rtcp_field,
+                                  std::list<uint16>* nack_sequence_numbers) {
+  nack_sequence_numbers->push_back(rtcp_field->nack_item.packet_id);
+
+  uint16 bitmask = rtcp_field->nack_item.bitmask;
+  if (bitmask) {
+    for (int i = 1; i <= 16; ++i) {
+      if (bitmask & 1) {
+        nack_sequence_numbers->push_back(rtcp_field->nack_item.packet_id + i);
+      }
+      bitmask = bitmask >> 1;
+    }
+  }
+}
+
+void RtcpReceiver::HandleBYE(RtcpParser* rtcp_parser) {
+  const RtcpField& rtcp_field = rtcp_parser->Field();
+  uint32 remote_ssrc = rtcp_field.bye.sender_ssrc;
+  if (remote_ssrc_ == remote_ssrc) {
+    TRACE_EVENT_INSTANT1("cast_rtcp", "BYE", TRACE_EVENT_SCOPE_THREAD,
+                         "remote_ssrc", remote_ssrc);
+  }
+  rtcp_parser->Iterate();
+}
+
+void RtcpReceiver::HandlePLI(RtcpParser* rtcp_parser) {
+  const RtcpField& rtcp_field = rtcp_parser->Field();
+  if (ssrc_ == rtcp_field.pli.media_ssrc) {
+    // Received a signal that we need to send a new key frame.
+    if (sender_feedback_) {
+      sender_feedback_->OnReceivedIntraFrameRequest();
+    }
+  }
+  rtcp_parser->Iterate();
+}
+
+void RtcpReceiver::HandleSendReportRequest(RtcpParser* rtcp_parser) {
+  if (receiver_feedback_) {
+    receiver_feedback_->OnReceivedSendReportRequest();
+  }
+  rtcp_parser->Iterate();
+}
+
+void RtcpReceiver::HandleRpsi(RtcpParser* rtcp_parser) {
+  const RtcpField& rtcp_field = rtcp_parser->Field();
+  if (rtcp_parser->Iterate() != kRtcpPayloadSpecificRpsiCode) {
+    return;
+  }
+  if (rtcp_field.rpsi.number_of_valid_bits % 8 != 0) {
+    // Continue
+    return;
+  }
+  uint64 rpsi_picture_id = 0;
+
+  // Convert native_bit_string to rpsi_picture_id
+  uint8 bytes = rtcp_field.rpsi.number_of_valid_bits / 8;
+  for (uint8 n = 0; n < (bytes - 1); ++n) {
+    rpsi_picture_id += (rtcp_field.rpsi.native_bit_string[n] & 0x7f);
+    rpsi_picture_id <<= 7;  // Prepare next.
+  }
+  rpsi_picture_id += (rtcp_field.rpsi.native_bit_string[bytes - 1] & 0x7f);
+  if (sender_feedback_) {
+    sender_feedback_->OnReceivedRpsi(rtcp_field.rpsi.payload_type,
+                                     rpsi_picture_id);
+  }
+}
+
+void RtcpReceiver::HandlePayloadSpecificApp(RtcpParser* rtcp_parser) {
+  const RtcpField& rtcp_field = rtcp_parser->Field();
+  uint32 remote_ssrc = rtcp_field.application_specific.sender_ssrc;
+  if (remote_ssrc_ != remote_ssrc) {
+    // Message not to us.
+    rtcp_parser->Iterate();
+    return;
+  }
+
+  RtcpFieldTypes packet_type = rtcp_parser->Iterate();
+  switch (packet_type) {
+    case kRtcpPayloadSpecificRembCode:
+      packet_type = rtcp_parser->Iterate();
+      if (packet_type == kRtcpPayloadSpecificRembItemCode) {
+        HandlePayloadSpecificRembItem(rtcp_parser);
+        rtcp_parser->Iterate();
+      }
+      break;
+    case kRtcpPayloadSpecificCastCode:
+      packet_type = rtcp_parser->Iterate();
+      if (packet_type == kRtcpPayloadSpecificCastCode) {
+        HandlePayloadSpecificCastItem(rtcp_parser);
+      }
+      break;
+    default:
+      return;
+  }
+}
+
+void RtcpReceiver::HandlePayloadSpecificRembItem(RtcpParser* rtcp_parser) {
+  const RtcpField& rtcp_field = rtcp_parser->Field();
+
+  for (int i = 0; i < rtcp_field.remb_item.number_of_ssrcs; ++i) {
+    if (rtcp_field.remb_item.ssrcs[i] == ssrc_) {
+      // Found matching ssrc.
+      if (sender_feedback_) {
+        sender_feedback_->OnReceivedRemb(rtcp_field.remb_item.bitrate);
+      }
+      return;
+    }
+  }
+}
+
+void RtcpReceiver::HandlePayloadSpecificCastItem(RtcpParser* rtcp_parser) {
+  const RtcpField& rtcp_field = rtcp_parser->Field();
+
+  RtcpCastMessage cast_message(remote_ssrc_);
+  cast_message.ack_frame_id_ = rtcp_field.cast_item.last_frame_id;
+
+  RtcpFieldTypes packet_type = rtcp_parser->Iterate();
+  while (packet_type == kRtcpPayloadSpecificCastNackItemCode) {
+    const RtcpField& rtcp_field = rtcp_parser->Field();
+    HandlePayloadSpecificCastNackItem(
+        &rtcp_field, &cast_message.missing_frames_and_packets_);
+    packet_type = rtcp_parser->Iterate();
+  }
+  if (sender_feedback_) {
+    sender_feedback_->OnReceivedCastFeedback(cast_message);
+  }
+}
+
+void RtcpReceiver::HandlePayloadSpecificCastNackItem(
+    const RtcpField* rtcp_field,
+    std::map<uint8, std::set<uint16> >* missing_frames_and_packets) {
+
+  std::map<uint8, std::set<uint16> >::iterator frame_it =
+      missing_frames_and_packets->find(rtcp_field->cast_nack_item.frame_id);
+
+  if (frame_it == missing_frames_and_packets->end()) {
+    // First missing packet in a frame.
+    std::set<uint16> empty_set;
+    std::pair<std::map<uint8, std::set<uint16> >::iterator, bool> ret;
+    ret = missing_frames_and_packets->insert(
+        std::pair<uint8, std::set<uint16> >(
+            rtcp_field->cast_nack_item.frame_id, empty_set));
+    frame_it = ret.first;
+    DCHECK(frame_it != missing_frames_and_packets->end()) << "Invalid state";
+  }
+  if (rtcp_field->cast_nack_item.packet_id == kRtcpCastAllPacketsLost) {
+    // Special case all packets in a frame is missing.
+    return;
+  }
+  uint16 packet_id = rtcp_field->cast_nack_item.packet_id;
+  uint8 bitmask = rtcp_field->cast_nack_item.bitmask;
+
+  frame_it->second.insert(packet_id);
+
+  if (bitmask) {
+    for (int i = 1; i <= 8; ++i) {
+      if (bitmask & 1) {
+        frame_it->second.insert(packet_id + i);
+      }
+      bitmask = bitmask >> 1;
+    }
+  }
+}
+
+void RtcpReceiver::HandleFIR(RtcpParser* rtcp_parser) {
+  const RtcpField& rtcp_field = rtcp_parser->Field();
+
+  RtcpFieldTypes field_type = rtcp_parser->Iterate();
+  while (field_type == kRtcpPayloadSpecificFirItemCode) {
+    HandleFIRItem(&rtcp_field);
+    field_type = rtcp_parser->Iterate();
+  }
+}
+
+void RtcpReceiver::HandleFIRItem(const RtcpField* rtcp_field) {
+  // Is it our sender that is requested to generate a new keyframe.
+  if (ssrc_ != rtcp_field->fir_item.ssrc)  return;
+  if (sender_feedback_) {
+    sender_feedback_->OnReceivedIntraFrameRequest();
+  }
+}
+
+}  // namespace cast
+}  // namespace media
diff --git a/media/cast/rtcp/rtcp_receiver.h b/media/cast/rtcp/rtcp_receiver.h
new file mode 100644
index 0000000..8c315d0
--- /dev/null
+++ b/media/cast/rtcp/rtcp_receiver.h
@@ -0,0 +1,106 @@
+// 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 MEDIA_CAST_RTCP_RTCP_RECEIVER_H_
+#define MEDIA_CAST_RTCP_RTCP_RECEIVER_H_
+
+#include "media/cast/rtcp/rtcp.h"
+#include "media/cast/rtcp/rtcp_defines.h"
+#include "media/cast/rtcp/rtcp_utility.h"
+
+namespace media {
+namespace cast {
+
+class RtcpReceiverFeedback {
+ public:
+  virtual void OnReceivedSenderReport(
+      const RtcpSenderInfo& remote_sender_info) = 0;
+
+  virtual void OnReceiverReferenceTimeReport(
+      const RtcpReceiverReferenceTimeReport& remote_time_report) = 0;
+
+  virtual void OnReceivedSendReportRequest() = 0;
+
+  virtual ~RtcpReceiverFeedback() {}
+};
+
+class RtcpRttFeedback {
+ public:
+  virtual void OnReceivedDelaySinceLastReport(
+      uint32 receivers_ssrc,
+      uint32 last_report,
+      uint32 delay_since_last_report) = 0;
+
+  virtual ~RtcpRttFeedback() {}
+};
+
+class RtcpReceiver {
+ public:
+  explicit RtcpReceiver(RtcpSenderFeedback* sender_feedback,
+                        RtcpReceiverFeedback* receiver_feedback,
+                        RtcpRttFeedback* rtt_feedback,
+                        uint32 local_ssrc);
+  virtual ~RtcpReceiver();
+
+  void SetRemoteSSRC(uint32 ssrc);
+
+  void IncomingRtcpPacket(RtcpParser* rtcp_parser);
+
+ private:
+  void HandleSenderReport(RtcpParser* rtcp_parser);
+
+  void HandleReceiverReport(RtcpParser* rtcp_parser);
+
+  void HandleReportBlock(const RtcpField* rtcp_field,
+                         uint32 remote_ssrc);
+
+  void HandleSDES(RtcpParser* rtcp_parser);
+  void HandleSDESChunk(RtcpParser* rtcp_parser);
+
+  void HandleBYE(RtcpParser* rtcp_parser);
+
+  void HandleXr(RtcpParser* rtcp_parser);
+  void HandleRrtr(RtcpParser* rtcp_parser, uint32 remote_ssrc);
+  void HandleDlrr(RtcpParser* rtcp_parser);
+
+  //  Generic RTP Feedback.
+  void HandleNACK(RtcpParser* rtcp_parser);
+  void HandleNACKItem(const RtcpField* rtcp_field,
+                      std::list<uint16>* nack_sequence_numbers);
+
+  void HandleSendReportRequest(RtcpParser* rtcp_parser);
+
+  // Payload-specific.
+  void HandlePLI(RtcpParser* rtcp_parser);
+
+  void HandleSLI(RtcpParser* rtcp_parser);
+  void HandleSLIItem(RtcpField* rtcpPacket);
+
+  void HandleRpsi(RtcpParser* rtcp_parser);
+
+  void HandleFIR(RtcpParser* rtcp_parser);
+  void HandleFIRItem(const RtcpField* rtcp_field);
+
+  void HandlePayloadSpecificApp(RtcpParser* rtcp_parser);
+  void HandlePayloadSpecificRembItem(RtcpParser* rtcp_parser);
+  void HandlePayloadSpecificCastItem(RtcpParser* rtcp_parser);
+  void HandlePayloadSpecificCastNackItem(
+      const RtcpField* rtcp_field,
+      std::map<uint8, std::set<uint16> >* missing_frames_and_packets);
+
+  const uint32 ssrc_;
+  uint32 remote_ssrc_;
+
+  // Not owned by this class.
+  RtcpSenderFeedback* const sender_feedback_;
+  RtcpReceiverFeedback* const receiver_feedback_;
+  RtcpRttFeedback* const rtt_feedback_;
+
+  DISALLOW_COPY_AND_ASSIGN(RtcpReceiver);
+};
+
+}  // namespace cast
+}  // namespace media
+
+#endif  // MEDIA_CAST_RTCP_RTCP_RECEIVER_H_
diff --git a/media/cast/rtcp/rtcp_receiver_unittest.cc b/media/cast/rtcp/rtcp_receiver_unittest.cc
new file mode 100644
index 0000000..5073944
--- /dev/null
+++ b/media/cast/rtcp/rtcp_receiver_unittest.cc
@@ -0,0 +1,380 @@
+// 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 "base/memory/scoped_ptr.h"
+#include "media/cast/rtcp/mock_rtcp_receiver_feedback.h"
+#include "media/cast/rtcp/mock_rtcp_sender_feedback.h"
+#include "media/cast/rtcp/rtcp_receiver.h"
+#include "media/cast/rtcp/rtcp_utility.h"
+#include "media/cast/rtcp/test_rtcp_packet_builder.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+namespace cast {
+
+using testing::_;
+
+static const uint32 kSenderSsrc = 0x10203;
+static const uint32 kSourceSsrc = 0x40506;
+static const uint32 kUnknownSsrc = 0xDEAD;
+static const std::string kCName("test@10.1.1.1");
+
+class SenderFeedbackCastVerification : public RtcpSenderFeedback {
+ public:
+  SenderFeedbackCastVerification() : called_(false) {}
+  virtual void OnReceivedReportBlock(
+      const RtcpReportBlock& report_block) OVERRIDE {};
+  virtual void OnReceivedIntraFrameRequest() OVERRIDE {};
+  virtual void OnReceivedRpsi(uint8 payload_type,
+                              uint64 picture_id) OVERRIDE {};
+  virtual void OnReceivedRemb(uint32 bitrate) OVERRIDE {};
+  virtual void OnReceivedNackRequest(
+      const std::list<uint16>& nack_sequence_numbers) OVERRIDE {};
+
+  virtual void OnReceivedCastFeedback(
+      const RtcpCastMessage& cast_feedback) OVERRIDE {
+    EXPECT_EQ(cast_feedback.media_ssrc_, kSenderSsrc);
+    EXPECT_EQ(cast_feedback.ack_frame_id_, kAckFrameId);
+
+    std::map<uint8, std::set<uint16> >::const_iterator frame_it =
+        cast_feedback.missing_frames_and_packets_.begin();
+
+    EXPECT_TRUE(frame_it != cast_feedback.missing_frames_and_packets_.end());
+    EXPECT_EQ(kLostFrameId, frame_it->first);
+    EXPECT_TRUE(frame_it->second.empty());
+    ++frame_it;
+    EXPECT_TRUE(frame_it != cast_feedback.missing_frames_and_packets_.end());
+    EXPECT_EQ(kFrameIdWithLostPackets, frame_it->first);
+    EXPECT_EQ(3UL, frame_it->second.size());
+    std::set<uint16>::const_iterator packet_it = frame_it->second.begin();
+    EXPECT_EQ(kLostPacketId1, *packet_it);
+    ++packet_it;
+    EXPECT_EQ(kLostPacketId2, *packet_it);
+    ++packet_it;
+    EXPECT_EQ(kLostPacketId3, *packet_it);
+    ++frame_it;
+    EXPECT_EQ(frame_it, cast_feedback.missing_frames_and_packets_.end());
+    called_ = true;
+  }
+
+  bool called() { return called_; }
+
+ private:
+  bool called_;
+};
+
+
+class RtcpReceiverTest : public ::testing::Test {
+ protected:
+  RtcpReceiverTest()
+      : rtcp_receiver_(new RtcpReceiver(&mock_sender_feedback_,
+                                        &mock_receiver_feedback_,
+                                        &mock_rtt_feedback_,
+                                        kSourceSsrc)) {
+  }
+
+  ~RtcpReceiverTest() {}
+
+  void SetUp() OVERRIDE {
+    EXPECT_CALL(mock_receiver_feedback_, OnReceivedSenderReport(_)).Times(0);
+    EXPECT_CALL(mock_receiver_feedback_,
+                OnReceiverReferenceTimeReport(_)).Times(0);
+    EXPECT_CALL(mock_receiver_feedback_,
+                OnReceivedSendReportRequest()).Times(0);
+
+    EXPECT_CALL(mock_sender_feedback_, OnReceivedReportBlock(_)).Times(0);
+    EXPECT_CALL(mock_sender_feedback_, OnReceivedIntraFrameRequest()).Times(0);
+    EXPECT_CALL(mock_sender_feedback_, OnReceivedRpsi(_, _)).Times(0);
+    EXPECT_CALL(mock_sender_feedback_, OnReceivedRemb(_)).Times(0);
+    EXPECT_CALL(mock_sender_feedback_, OnReceivedNackRequest(_)).Times(0);
+    EXPECT_CALL(mock_sender_feedback_, OnReceivedCastFeedback(_)).Times(0);
+
+    EXPECT_CALL(mock_rtt_feedback_,
+                OnReceivedDelaySinceLastReport(_, _, _)).Times(0);
+
+    expected_sender_info_.ntp_seconds = kNtpHigh;
+    expected_sender_info_.ntp_fraction = kNtpLow;
+    expected_sender_info_.rtp_timestamp = kRtpTimestamp;
+    expected_sender_info_.send_packet_count = kSendPacketCount;
+    expected_sender_info_.send_octet_count = kSendOctetCount;
+
+    expected_report_block_.remote_ssrc = kSenderSsrc;
+    expected_report_block_.media_ssrc = kSourceSsrc;
+    expected_report_block_.fraction_lost = kLoss >> 24;
+    expected_report_block_.cumulative_lost = kLoss & 0xffffff;
+    expected_report_block_.extended_high_sequence_number = kExtendedMax;
+    expected_report_block_.jitter = kJitter;
+    expected_report_block_.last_sr = kLastSr;
+    expected_report_block_.delay_since_last_sr = kDelayLastSr;
+    expected_receiver_reference_report_.remote_ssrc = kSenderSsrc;
+    expected_receiver_reference_report_.ntp_seconds = kNtpHigh;
+    expected_receiver_reference_report_.ntp_fraction = kNtpLow;
+  }
+
+  // Injects an RTCP packet into the receiver.
+  void InjectRtcpPacket(const uint8* packet, uint16 length) {
+    RtcpParser rtcp_parser(packet, length);
+    rtcp_receiver_->IncomingRtcpPacket(&rtcp_parser);
+  }
+
+  MockRtcpReceiverFeedback mock_receiver_feedback_;
+  MockRtcpRttFeedback mock_rtt_feedback_;
+  MockRtcpSenderFeedback mock_sender_feedback_;
+  scoped_ptr<RtcpReceiver> rtcp_receiver_;
+  RtcpSenderInfo expected_sender_info_;
+  RtcpReportBlock expected_report_block_;
+  RtcpReceiverReferenceTimeReport expected_receiver_reference_report_;
+};
+
+TEST_F(RtcpReceiverTest, BrokenPacketIsIgnored) {
+  const uint8 bad_packet[] = {0, 0, 0, 0};
+  InjectRtcpPacket(bad_packet, sizeof(bad_packet));
+}
+
+TEST_F(RtcpReceiverTest, InjectSenderReportPacket) {
+  TestRtcpPacketBuilder p;
+  p.AddSr(kSenderSsrc, 0);
+
+  // Expected to be ignored since the sender ssrc does not match our
+  // remote ssrc.
+  InjectRtcpPacket(p.Packet(), p.Length());
+
+  EXPECT_CALL(mock_receiver_feedback_,
+              OnReceivedSenderReport(expected_sender_info_)).Times(1);
+  rtcp_receiver_->SetRemoteSSRC(kSenderSsrc);
+
+  // Expected to be pass through since the sender ssrc match our remote ssrc.
+  InjectRtcpPacket(p.Packet(), p.Length());
+}
+
+TEST_F(RtcpReceiverTest, InjectReceiveReportPacket) {
+  TestRtcpPacketBuilder p1;
+  p1.AddRr(kSenderSsrc, 1);
+  p1.AddRb(kUnknownSsrc);
+
+  // Expected to be ignored since the source ssrc does not match our
+  // local ssrc.
+  InjectRtcpPacket(p1.Packet(), p1.Length());
+
+  EXPECT_CALL(mock_sender_feedback_,
+              OnReceivedReportBlock(expected_report_block_)).Times(1);
+
+  EXPECT_CALL(mock_rtt_feedback_,
+      OnReceivedDelaySinceLastReport(kSourceSsrc,
+          kLastSr,
+          kDelayLastSr)).Times(1);
+
+  TestRtcpPacketBuilder p2;
+  p2.AddRr(kSenderSsrc, 1);
+  p2.AddRb(kSourceSsrc);
+
+  // Expected to be pass through since the sender ssrc match our local ssrc.
+  InjectRtcpPacket(p2.Packet(), p2.Length());
+}
+
+TEST_F(RtcpReceiverTest, InjectSenderReportWithReportBlockPacket) {
+  TestRtcpPacketBuilder p1;
+  p1.AddSr(kSenderSsrc, 1);
+  p1.AddRb(kUnknownSsrc);
+
+  // Sender report expected to be ignored since the sender ssrc does not match
+  // our remote ssrc.
+  // Report block expected to be ignored since the source ssrc does not match
+  // our local ssrc.
+  InjectRtcpPacket(p1.Packet(), p1.Length());
+
+  EXPECT_CALL(mock_receiver_feedback_,
+              OnReceivedSenderReport(expected_sender_info_)).Times(1);
+  rtcp_receiver_->SetRemoteSSRC(kSenderSsrc);
+
+  // Sender report expected to be pass through since the sender ssrc match our
+  // remote ssrc.
+  // Report block expected to be ignored since the source ssrc does not match
+  // our local ssrc.
+  InjectRtcpPacket(p1.Packet(), p1.Length());
+
+  EXPECT_CALL(mock_receiver_feedback_, OnReceivedSenderReport(_)).Times(0);
+  EXPECT_CALL(mock_sender_feedback_,
+              OnReceivedReportBlock(expected_report_block_)).Times(1);
+  EXPECT_CALL(mock_rtt_feedback_,
+      OnReceivedDelaySinceLastReport(kSourceSsrc,
+        kLastSr,
+        kDelayLastSr)).Times(1);
+
+  rtcp_receiver_->SetRemoteSSRC(0);
+
+  TestRtcpPacketBuilder p2;
+  p2.AddSr(kSenderSsrc, 1);
+  p2.AddRb(kSourceSsrc);
+
+  // Sender report expected to be ignored since the sender ssrc does not match
+  // our remote ssrc.
+  // Receiver report expected to be pass through since the sender ssrc match
+  // our local ssrc.
+  InjectRtcpPacket(p2.Packet(), p2.Length());
+
+  EXPECT_CALL(mock_receiver_feedback_,
+              OnReceivedSenderReport(expected_sender_info_)).Times(1);
+  EXPECT_CALL(mock_sender_feedback_,
+              OnReceivedReportBlock(expected_report_block_)).Times(1);
+  EXPECT_CALL(mock_rtt_feedback_,
+      OnReceivedDelaySinceLastReport(kSourceSsrc,
+          kLastSr,
+          kDelayLastSr)).Times(1);
+
+  rtcp_receiver_->SetRemoteSSRC(kSenderSsrc);
+
+  // Sender report expected to be pass through since the sender ssrc match our
+  // remote ssrc.
+  // Receiver report expected to be pass through since the sender ssrc match
+  // our local ssrc.
+  InjectRtcpPacket(p2.Packet(), p2.Length());
+}
+
+TEST_F(RtcpReceiverTest, InjectSenderReportPacketWithDlrr) {
+  TestRtcpPacketBuilder p;
+  p.AddSr(kSenderSsrc, 0);
+  p.AddXrHeader(kSenderSsrc);
+  p.AddXrUnknownBlock();
+  p.AddXrExtendedDlrrBlock(kSenderSsrc);
+  p.AddXrUnknownBlock();
+  p.AddSdesCname(kSenderSsrc, kCName);
+
+  // Expected to be ignored since the source ssrc does not match our
+  // local ssrc.
+  InjectRtcpPacket(p.Packet(), p.Length());
+
+  EXPECT_CALL(mock_receiver_feedback_,
+              OnReceivedSenderReport(expected_sender_info_)).Times(1);
+  EXPECT_CALL(mock_rtt_feedback_,
+      OnReceivedDelaySinceLastReport(kSenderSsrc,
+          kLastSr,
+          kDelayLastSr)).Times(1);
+
+  // Enable receiving sender report.
+  rtcp_receiver_->SetRemoteSSRC(kSenderSsrc);
+
+  // Expected to be pass through since the sender ssrc match our local ssrc.
+  InjectRtcpPacket(p.Packet(), p.Length());
+}
+
+TEST_F(RtcpReceiverTest, InjectReceiverReportPacketWithRrtr) {
+  TestRtcpPacketBuilder p1;
+  p1.AddRr(kSenderSsrc, 1);
+  p1.AddRb(kUnknownSsrc);
+  p1.AddXrHeader(kSenderSsrc);
+  p1.AddXrRrtrBlock();
+
+  // Expected to be ignored since the source ssrc does not match our
+  // local ssrc.
+  InjectRtcpPacket(p1.Packet(), p1.Length());
+
+  EXPECT_CALL(mock_sender_feedback_,
+              OnReceivedReportBlock(expected_report_block_)).Times(1);
+  EXPECT_CALL(mock_rtt_feedback_,
+      OnReceivedDelaySinceLastReport(kSourceSsrc,
+          kLastSr,
+          kDelayLastSr)).Times(1);
+  EXPECT_CALL(mock_receiver_feedback_, OnReceiverReferenceTimeReport(
+      expected_receiver_reference_report_)).Times(1);
+
+  // Enable receiving reference time report.
+  rtcp_receiver_->SetRemoteSSRC(kSenderSsrc);
+
+  TestRtcpPacketBuilder p2;
+  p2.AddRr(kSenderSsrc, 1);
+  p2.AddRb(kSourceSsrc);
+  p2.AddXrHeader(kSenderSsrc);
+  p2.AddXrRrtrBlock();
+
+  // Expected to be pass through since the sender ssrc match our local ssrc.
+  InjectRtcpPacket(p2.Packet(), p2.Length());
+}
+
+TEST_F(RtcpReceiverTest, InjectReceiverReportPacketWithIntraFrameRequest) {
+  TestRtcpPacketBuilder p1;
+  p1.AddRr(kSenderSsrc, 1);
+  p1.AddRb(kUnknownSsrc);
+  p1.AddPli(kSenderSsrc, kUnknownSsrc);
+
+  // Expected to be ignored since the source ssrc does not match our
+  // local ssrc.
+  InjectRtcpPacket(p1.Packet(), p1.Length());
+
+  EXPECT_CALL(mock_sender_feedback_,
+              OnReceivedReportBlock(expected_report_block_)).Times(1);
+  EXPECT_CALL(mock_rtt_feedback_,
+      OnReceivedDelaySinceLastReport(kSourceSsrc,
+          kLastSr,
+          kDelayLastSr)).Times(1);
+  EXPECT_CALL(mock_sender_feedback_, OnReceivedIntraFrameRequest()).Times(1);
+
+  TestRtcpPacketBuilder p2;
+  p2.AddRr(kSenderSsrc, 1);
+  p2.AddRb(kSourceSsrc);
+  p2.AddPli(kSenderSsrc, kSourceSsrc);
+
+  // Expected to be pass through since the sender ssrc match our local ssrc.
+  InjectRtcpPacket(p2.Packet(), p2.Length());
+}
+
+TEST_F(RtcpReceiverTest, InjectReceiverReportPacketWithCastFeedback) {
+  TestRtcpPacketBuilder p1;
+  p1.AddRr(kSenderSsrc, 1);
+  p1.AddRb(kUnknownSsrc);
+  p1.AddCast(kSenderSsrc, kUnknownSsrc);
+
+  // Expected to be ignored since the source ssrc does not match our
+  // local ssrc.
+  InjectRtcpPacket(p1.Packet(), p1.Length());
+
+  EXPECT_CALL(mock_sender_feedback_,
+              OnReceivedReportBlock(expected_report_block_)).Times(1);
+  EXPECT_CALL(mock_rtt_feedback_,
+      OnReceivedDelaySinceLastReport(kSourceSsrc,
+          kLastSr,
+          kDelayLastSr)).Times(1);
+  EXPECT_CALL(mock_sender_feedback_, OnReceivedCastFeedback(_)).Times(1);
+
+  // Enable receiving the cast feedback.
+  rtcp_receiver_->SetRemoteSSRC(kSenderSsrc);
+
+  TestRtcpPacketBuilder p2;
+  p2.AddRr(kSenderSsrc, 1);
+  p2.AddRb(kSourceSsrc);
+  p2.AddCast(kSenderSsrc, kSourceSsrc);
+
+  // Expected to be pass through since the sender ssrc match our local ssrc.
+  InjectRtcpPacket(p2.Packet(), p2.Length());
+}
+
+TEST_F(RtcpReceiverTest, InjectReceiverReportPacketWithCastVerification) {
+  SenderFeedbackCastVerification sender_feedback_cast_verification;
+  RtcpReceiver rtcp_receiver(&sender_feedback_cast_verification,
+                             &mock_receiver_feedback_,
+                             &mock_rtt_feedback_,
+                             kSourceSsrc);
+
+  EXPECT_CALL(mock_rtt_feedback_,
+      OnReceivedDelaySinceLastReport(kSourceSsrc,
+          kLastSr,
+          kDelayLastSr)).Times(1);
+
+  // Enable receiving the cast feedback.
+  rtcp_receiver.SetRemoteSSRC(kSenderSsrc);
+
+  TestRtcpPacketBuilder p;
+  p.AddRr(kSenderSsrc, 1);
+  p.AddRb(kSourceSsrc);
+  p.AddCast(kSenderSsrc, kSourceSsrc);
+
+  // Expected to be pass through since the sender ssrc match our local ssrc.
+  RtcpParser rtcp_parser(p.Packet(), p.Length());
+  rtcp_receiver.IncomingRtcpPacket(&rtcp_parser);
+
+  EXPECT_TRUE(sender_feedback_cast_verification.called());
+}
+
+}  // namespace cast
+}  // namespace media
diff --git a/media/cast/rtcp/rtcp_sender.cc b/media/cast/rtcp/rtcp_sender.cc
new file mode 100644
index 0000000..89ea05e
--- /dev/null
+++ b/media/cast/rtcp/rtcp_sender.cc
@@ -0,0 +1,544 @@
+// 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 "media/cast/rtcp/rtcp_sender.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "media/cast/pacing/paced_sender.h"
+#include "media/cast/rtcp/rtcp_utility.h"
+#include "net/base/big_endian.h"
+
+namespace media {
+namespace cast {
+
+static const int kRtcpMaxNackFields = 253;
+static const int kRtcpMaxCastLossFields = 100;
+
+RtcpSender::RtcpSender(PacedPacketSender* outgoing_transport,
+                       uint32 sending_ssrc,
+                       const std::string& c_name)
+     : ssrc_(sending_ssrc),
+       c_name_(c_name),
+       transport_(outgoing_transport) {
+  DCHECK_LT(c_name_.length(), kRtcpCnameSize) << "Invalid config";
+}
+
+RtcpSender::~RtcpSender() {}
+
+void RtcpSender::SendRtcp(uint32 packet_type_flags,
+                          const RtcpSenderInfo* sender_info,
+                          const RtcpReportBlock* report_block,
+                          uint32 pli_remote_ssrc,
+                          const RtcpDlrrReportBlock* dlrr,
+                          const RtcpReceiverReferenceTimeReport* rrtr,
+                          const RtcpCastMessage* cast_message) {
+  std::vector<uint8> packet;
+  packet.reserve(kIpPacketSize);
+  if (packet_type_flags & kRtcpSr) {
+    DCHECK(sender_info) << "Invalid argument";
+    BuildSR(*sender_info, report_block, &packet);
+    BuildSdec(&packet);
+  } else if (packet_type_flags & kRtcpRr) {
+    BuildRR(report_block, &packet);
+    if (!c_name_.empty()) {
+      BuildSdec(&packet);
+    }
+  }
+  if (packet_type_flags & kRtcpPli) {
+    BuildPli(pli_remote_ssrc, &packet);
+  }
+  if (packet_type_flags & kRtcpBye) {
+    BuildBye(&packet);
+  }
+  if (packet_type_flags & kRtcpRpsi) {
+    // Implement this for webrtc interop.
+    NOTIMPLEMENTED();
+  }
+  if (packet_type_flags & kRtcpRemb) {
+    // Implement this for webrtc interop.
+    NOTIMPLEMENTED();
+  }
+  if (packet_type_flags & kRtcpNack) {
+    // Implement this for webrtc interop.
+    NOTIMPLEMENTED();
+  }
+  if (packet_type_flags & kRtcpDlrr) {
+    DCHECK(dlrr) << "Invalid argument";
+    BuildDlrrRb(dlrr, &packet);
+  }
+  if (packet_type_flags & kRtcpRrtr) {
+    DCHECK(rrtr) << "Invalid argument";
+    BuildRrtr(rrtr, &packet);
+  }
+  if (packet_type_flags & kRtcpCast) {
+    DCHECK(cast_message) << "Invalid argument";
+    BuildCast(cast_message, &packet);
+  }
+
+  if (packet.empty()) return;  // Sanity don't send empty packets.
+
+  transport_->SendRtcpPacket(packet);
+}
+
+void RtcpSender::BuildSR(const RtcpSenderInfo& sender_info,
+                         const RtcpReportBlock* report_block,
+                         std::vector<uint8>* packet) const {
+  // Sender report.
+  size_t start_size = packet->size();
+  DCHECK_LT(start_size + 52, kIpPacketSize) << "Not enough buffer space";
+  if (start_size + 52 > kIpPacketSize) return;
+
+  uint16 number_of_rows = (report_block) ? 12 : 6;
+  packet->resize(start_size + 28);
+
+  net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 28);
+  big_endian_writer.WriteU8(0x80 + (report_block ? 1 : 0));
+  big_endian_writer.WriteU8(200);
+  big_endian_writer.WriteU16(number_of_rows);
+  big_endian_writer.WriteU32(ssrc_);
+  big_endian_writer.WriteU32(sender_info.ntp_seconds);
+  big_endian_writer.WriteU32(sender_info.ntp_fraction);
+  big_endian_writer.WriteU32(sender_info.rtp_timestamp);
+  big_endian_writer.WriteU32(sender_info.send_packet_count);
+  big_endian_writer.WriteU32(sender_info.send_octet_count);
+
+  if (report_block) {
+    AddReportBlocks(*report_block, packet);  // Adds 24 bytes.
+  }
+}
+
+void RtcpSender::BuildRR(const RtcpReportBlock* report_block,
+                         std::vector<uint8>* packet) const {
+  size_t start_size = packet->size();
+  DCHECK_LT(start_size + 32, kIpPacketSize) << "Not enough buffer space";
+  if (start_size + 32 > kIpPacketSize) return;
+
+  uint16 number_of_rows = (report_block) ? 7 : 1;
+  packet->resize(start_size + 8);
+
+  net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 8);
+  big_endian_writer.WriteU8(0x80 + (report_block ? 1 : 0));
+  big_endian_writer.WriteU8(201);
+  big_endian_writer.WriteU16(number_of_rows);
+  big_endian_writer.WriteU32(ssrc_);
+
+  if (report_block) {
+    AddReportBlocks(*report_block, packet);  // Adds 24 bytes.
+  }
+}
+
+void RtcpSender::AddReportBlocks(const RtcpReportBlock& report_block,
+                                 std::vector<uint8>* packet) const {
+  size_t start_size = packet->size();
+  DCHECK_LT(start_size + 24, kIpPacketSize) << "Not enough buffer space";
+  if (start_size + 24 > kIpPacketSize) return;
+
+  packet->resize(start_size + 24);
+
+  net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 24);
+  big_endian_writer.WriteU32(report_block.media_ssrc);
+  big_endian_writer.WriteU8(report_block.fraction_lost);
+  big_endian_writer.WriteU8(report_block.cumulative_lost >> 16);
+  big_endian_writer.WriteU8(report_block.cumulative_lost >> 8);
+  big_endian_writer.WriteU8(report_block.cumulative_lost);
+
+  // Extended highest seq_no, contain the highest sequence number received.
+  big_endian_writer.WriteU32(report_block.extended_high_sequence_number);
+  big_endian_writer.WriteU32(report_block.jitter);
+
+  // Last SR timestamp; our NTP time when we received the last report.
+  // This is the value that we read from the send report packet not when we
+  // received it.
+  big_endian_writer.WriteU32(report_block.last_sr);
+
+  // Delay since last received report, time since we received the report.
+  big_endian_writer.WriteU32(report_block.delay_since_last_sr);
+}
+
+void RtcpSender::BuildSdec(std::vector<uint8>* packet) const {
+  size_t start_size = packet->size();
+  DCHECK_LT(start_size +  12 + c_name_.length(), kIpPacketSize)
+      << "Not enough buffer space";
+  if (start_size + 12 > kIpPacketSize) return;
+
+  // SDES Source Description.
+  packet->resize(start_size + 10);
+
+  net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 10);
+  // We always need to add one SDES CNAME.
+  big_endian_writer.WriteU8(0x80 + 1);
+  big_endian_writer.WriteU8(202);
+
+  // Handle SDES length later on.
+  uint32 sdes_length_position = start_size + 3;
+  big_endian_writer.WriteU16(0);
+  big_endian_writer.WriteU32(ssrc_);  // Add our own SSRC.
+  big_endian_writer.WriteU8(1);  // CNAME = 1
+  big_endian_writer.WriteU8(static_cast<uint8>(c_name_.length()));
+
+  size_t sdes_length = 10 + c_name_.length();
+  packet->insert(packet->end(), c_name_.c_str(),
+                 c_name_.c_str() + c_name_.length());
+
+  size_t padding = 0;
+
+  // We must have a zero field even if we have an even multiple of 4 bytes.
+  if ((packet->size() % 4) == 0) {
+    padding++;
+    packet->push_back(0);
+  }
+  while ((packet->size() % 4) != 0) {
+    padding++;
+    packet->push_back(0);
+  }
+  sdes_length += padding;
+
+  // In 32-bit words minus one and we don't count the header.
+  uint8 buffer_length = (sdes_length / 4) - 1;
+  (*packet)[sdes_length_position] = buffer_length;
+}
+
+void RtcpSender::BuildPli(uint32 remote_ssrc,
+                          std::vector<uint8>* packet) const {
+  size_t start_size = packet->size();
+  DCHECK_LT(start_size + 12, kIpPacketSize) << "Not enough buffer space";
+  if (start_size + 12 > kIpPacketSize) return;
+
+  packet->resize(start_size + 12);
+
+  net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 12);
+  uint8 FMT = 1;  // Picture loss indicator.
+  big_endian_writer.WriteU8(0x80 + FMT);
+  big_endian_writer.WriteU8(206);
+  big_endian_writer.WriteU16(2);  // Used fixed length of 2.
+  big_endian_writer.WriteU32(ssrc_);  // Add our own SSRC.
+  big_endian_writer.WriteU32(remote_ssrc);  // Add the remote SSRC.
+  TRACE_EVENT_INSTANT2("cast_rtcp", "RtcpSender::PLI", TRACE_EVENT_SCOPE_THREAD,
+                       "remote_ssrc", remote_ssrc,
+                       "ssrc", ssrc_);
+}
+
+/*
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |      PB       |0| Payload Type|    Native Rpsi bit string     |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |   defined per codec          ...                | Padding (0) |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+void RtcpSender::BuildRpsi(const RtcpRpsiMessage* rpsi,
+                           std::vector<uint8>* packet) const {
+  size_t start_size = packet->size();
+  DCHECK_LT(start_size + 24, kIpPacketSize) << "Not enough buffer space";
+  if (start_size + 24 > kIpPacketSize) return;
+
+  packet->resize(start_size + 24);
+
+  net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 24);
+  uint8 FMT = 3;  // Reference Picture Selection Indication.
+  big_endian_writer.WriteU8(0x80 + FMT);
+  big_endian_writer.WriteU8(206);
+
+  // Calculate length.
+  uint32 bits_required = 7;
+  uint8 bytes_required = 1;
+  while ((rpsi->picture_id >> bits_required) > 0) {
+    bits_required += 7;
+    bytes_required++;
+  }
+  uint8 size = 3;
+  if (bytes_required > 6) {
+    size = 5;
+  } else if (bytes_required > 2) {
+    size = 4;
+  }
+  big_endian_writer.WriteU8(0);
+  big_endian_writer.WriteU8(size);
+  big_endian_writer.WriteU32(ssrc_);
+  big_endian_writer.WriteU32(rpsi->remote_ssrc);
+
+  uint8 padding_bytes = 4 - ((2 + bytes_required) % 4);
+  if (padding_bytes == 4) {
+    padding_bytes = 0;
+  }
+  // Add padding length in bits, padding can be 0, 8, 16 or 24.
+  big_endian_writer.WriteU8(padding_bytes * 8);
+  big_endian_writer.WriteU8(rpsi->payload_type);
+
+  // Add picture ID.
+  for (int i = bytes_required - 1; i > 0; i--) {
+    big_endian_writer.WriteU8(
+        0x80 | static_cast<uint8>(rpsi->picture_id >> (i * 7)));
+  }
+  // Add last byte of picture ID.
+  big_endian_writer.WriteU8(static_cast<uint8>(rpsi->picture_id & 0x7f));
+
+  // Add padding.
+  for (int j = 0; j < padding_bytes; ++j) {
+    big_endian_writer.WriteU8(0);
+  }
+}
+
+void RtcpSender::BuildRemb(const RtcpRembMessage* remb,
+                           std::vector<uint8>* packet) const {
+  size_t start_size = packet->size();
+  size_t remb_size = 20 + 4 * remb->remb_ssrcs.size();
+  DCHECK_LT(start_size + remb_size, kIpPacketSize)
+      << "Not enough buffer space";
+  if (start_size + remb_size > kIpPacketSize) return;
+
+  packet->resize(start_size + remb_size);
+
+  net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), remb_size);
+
+  // Add application layer feedback.
+  uint8 FMT = 15;
+  big_endian_writer.WriteU8(0x80 + FMT);
+  big_endian_writer.WriteU8(206);
+  big_endian_writer.WriteU8(0);
+  big_endian_writer.WriteU8(remb->remb_ssrcs.size() + 4);
+  big_endian_writer.WriteU32(ssrc_);  // Add our own SSRC.
+  big_endian_writer.WriteU32(0);  // Remote SSRC must be 0.
+  big_endian_writer.WriteU32(kRemb);
+  big_endian_writer.WriteU8(remb->remb_ssrcs.size());
+
+  // 6 bit exponent and a 18 bit mantissa.
+  uint8 bitrate_exponent;
+  uint32 bitrate_mantissa;
+  BitrateToRembExponentBitrate(remb->remb_bitrate,
+                               &bitrate_exponent,
+                               &bitrate_mantissa);
+
+  big_endian_writer.WriteU8(static_cast<uint8>((bitrate_exponent << 2) +
+      ((bitrate_mantissa >> 16) & 0x03)));
+  big_endian_writer.WriteU8(static_cast<uint8>(bitrate_mantissa >> 8));
+  big_endian_writer.WriteU8(static_cast<uint8>(bitrate_mantissa));
+
+  std::list<uint32>::const_iterator it = remb->remb_ssrcs.begin();
+  for (; it != remb->remb_ssrcs.end(); ++it) {
+    big_endian_writer.WriteU32(*it);
+  }
+  TRACE_COUNTER_ID1("cast_rtcp", "RtcpSender::RembBitrate", ssrc_,
+                    remb->remb_bitrate);
+}
+
+void RtcpSender::BuildNack(const RtcpNackMessage* nack,
+                           std::vector<uint8>* packet) const {
+  size_t start_size = packet->size();
+  DCHECK_LT(start_size + 16, kIpPacketSize) << "Not enough buffer space";
+  if (start_size + 16 > kIpPacketSize) return;
+
+  packet->resize(start_size + 16);
+
+  net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 16);
+
+  uint8 FMT = 1;
+  big_endian_writer.WriteU8(0x80 + FMT);
+  big_endian_writer.WriteU8(205);
+  big_endian_writer.WriteU8(0);
+  int nack_size_pos = start_size + 3;
+  big_endian_writer.WriteU8(3);
+  big_endian_writer.WriteU32(ssrc_);  // Add our own SSRC.
+  big_endian_writer.WriteU32(nack->remote_ssrc);  // Add the remote SSRC.
+
+  // Build NACK bitmasks and write them to the Rtcp message.
+  // The nack list should be sorted and not contain duplicates.
+  int number_of_nack_fields = 0;
+  int max_number_of_nack_fields =
+      std::min<int>(kRtcpMaxNackFields, (kIpPacketSize - packet->size()) / 4);
+
+  std::list<uint16>::const_iterator it = nack->nack_list.begin();
+  while (it != nack->nack_list.end() &&
+         number_of_nack_fields < max_number_of_nack_fields) {
+    uint16 nack_sequence_number = *it;
+    uint16 bitmask = 0;
+    ++it;
+    while (it != nack->nack_list.end()) {
+      int shift = static_cast<uint16>(*it - nack_sequence_number) - 1;
+      if (shift >= 0 && shift <= 15) {
+        bitmask |= (1 << shift);
+        ++it;
+      } else {
+        break;
+      }
+    }
+    // Write the sequence number and the bitmask to the packet.
+    start_size = packet->size();
+    DCHECK_LT(start_size + 4, kIpPacketSize) << "Not enough buffer space";
+    if (start_size + 4 > kIpPacketSize) return;
+
+    packet->resize(start_size + 4);
+    net::BigEndianWriter big_endian_nack_writer(&((*packet)[start_size]), 4);
+    big_endian_nack_writer.WriteU16(nack_sequence_number);
+    big_endian_nack_writer.WriteU16(bitmask);
+    number_of_nack_fields++;
+  }
+  (*packet)[nack_size_pos] = static_cast<uint8>(2 + number_of_nack_fields);
+  TRACE_COUNTER_ID1("cast_rtcp", "RtcpSender::NACK", ssrc_,
+                    nack->nack_list.size());
+}
+
+void RtcpSender::BuildBye(std::vector<uint8>* packet) const {
+  size_t start_size = packet->size();
+  DCHECK_LT(start_size + 8, kIpPacketSize) << "Not enough buffer space";
+  if (start_size + 8 > kIpPacketSize) return;
+
+  packet->resize(start_size + 8);
+
+  net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 8);
+  big_endian_writer.WriteU8(0x80 + 1);
+  big_endian_writer.WriteU8(203);
+  big_endian_writer.WriteU16(1);  // Length.
+  big_endian_writer.WriteU32(ssrc_);  // Add our own SSRC.
+}
+
+/*
+   0                   1                   2                   3
+   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  |V=2|P|reserved |   PT=XR=207   |             length            |
+  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  |                              SSRC                             |
+  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  |     BT=5      |   reserved    |         block length          |
+  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+  |                 SSRC_1 (SSRC of first receiver)               | sub-
+  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
+  |                         last RR (LRR)                         |   1
+  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  |                   delay since last RR (DLRR)                  |
+  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+*/
+void RtcpSender::BuildDlrrRb(const RtcpDlrrReportBlock* dlrr,
+                             std::vector<uint8>* packet) const {
+  size_t start_size = packet->size();
+  DCHECK_LT(start_size + 24, kIpPacketSize) << "Not enough buffer space";
+  if (start_size + 24 > kIpPacketSize) return;
+
+  packet->resize(start_size + 24);
+
+  net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 24);
+  big_endian_writer.WriteU8(0x80);
+  big_endian_writer.WriteU8(207);
+  big_endian_writer.WriteU16(5);  // Length.
+  big_endian_writer.WriteU32(ssrc_);  // Add our own SSRC.
+  big_endian_writer.WriteU8(5);  // Add block type.
+  big_endian_writer.WriteU8(0);  // Add reserved.
+  big_endian_writer.WriteU16(3);  // Block length.
+  big_endian_writer.WriteU32(ssrc_);  // Add the media (received RTP) SSRC.
+  big_endian_writer.WriteU32(dlrr->last_rr);
+  big_endian_writer.WriteU32(dlrr->delay_since_last_rr);
+}
+
+void RtcpSender::BuildRrtr(const RtcpReceiverReferenceTimeReport* rrtr,
+                           std::vector<uint8>* packet) const {
+  size_t start_size = packet->size();
+  DCHECK_LT(start_size + 20, kIpPacketSize) << "Not enough buffer space";
+  if (start_size + 20 > kIpPacketSize) return;
+
+  packet->resize(start_size + 20);
+
+  net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 20);
+
+  big_endian_writer.WriteU8(0x80);
+  big_endian_writer.WriteU8(207);
+  big_endian_writer.WriteU16(4);  // Length.
+  big_endian_writer.WriteU32(ssrc_);  // Add our own SSRC.
+  big_endian_writer.WriteU8(4);  // Add block type.
+  big_endian_writer.WriteU8(0);  // Add reserved.
+  big_endian_writer.WriteU16(2);  // Block length.
+
+  // Add the media (received RTP) SSRC.
+  big_endian_writer.WriteU32(rrtr->ntp_seconds);
+  big_endian_writer.WriteU32(rrtr->ntp_fraction);
+}
+
+void RtcpSender::BuildCast(const RtcpCastMessage* cast,
+                           std::vector<uint8>* packet) const {
+  size_t start_size = packet->size();
+  DCHECK_LT(start_size + 20, kIpPacketSize) << "Not enough buffer space";
+  if (start_size + 20 > kIpPacketSize) return;
+
+  packet->resize(start_size + 20);
+
+  net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 20);
+  uint8 FMT = 15;  // Application layer feedback.
+  big_endian_writer.WriteU8(0x80 + FMT);
+  big_endian_writer.WriteU8(206);
+  big_endian_writer.WriteU8(0);
+  int cast_size_pos = start_size + 3;  // Save length position.
+  big_endian_writer.WriteU8(4);
+  big_endian_writer.WriteU32(ssrc_);  // Add our own SSRC.
+  big_endian_writer.WriteU32(cast->media_ssrc_);  // Remote SSRC.
+  big_endian_writer.WriteU32(kCast);
+  big_endian_writer.WriteU8(cast->ack_frame_id_);
+  int cast_loss_field_pos = start_size + 17;  // Save loss field position.
+  big_endian_writer.WriteU8(0);  // Overwritten with number_of_loss_fields.
+  big_endian_writer.WriteU8(0);  // Reserved.
+  big_endian_writer.WriteU8(0);  // Reserved.
+
+  int number_of_loss_fields = 0;
+  int max_number_of_loss_fields = std::min<int>(kRtcpMaxCastLossFields,
+      (kIpPacketSize - packet->size()) / 4);
+
+  std::map<uint8, std::set<uint16> >::const_iterator frame_it =
+      cast->missing_frames_and_packets_.begin();
+
+  for (; frame_it != cast->missing_frames_and_packets_.end() &&
+      number_of_loss_fields < max_number_of_loss_fields; ++frame_it) {
+    // Iterate through all frames with missing packets.
+    if (frame_it->second.empty()) {
+      // Special case all packets in a frame is missing.
+      start_size = packet->size();
+      packet->resize(start_size + 4);
+      net::BigEndianWriter big_endian_nack_writer(&((*packet)[start_size]), 4);
+      big_endian_nack_writer.WriteU8(frame_it->first);
+      big_endian_nack_writer.WriteU16(kRtcpCastAllPacketsLost);
+      big_endian_nack_writer.WriteU8(0);
+      ++number_of_loss_fields;
+    } else {
+      std::set<uint16>::const_iterator packet_it = frame_it->second.begin();
+      while (packet_it != frame_it->second.end()) {
+        uint16 packet_id = *packet_it;
+
+        start_size = packet->size();
+        packet->resize(start_size + 4);
+        net::BigEndianWriter big_endian_nack_writer(
+            &((*packet)[start_size]), 4);
+
+        // Write frame and packet id to buffer before calculating bitmask.
+        big_endian_nack_writer.WriteU8(frame_it->first);
+        big_endian_nack_writer.WriteU16(packet_id);
+
+        uint8 bitmask = 0;
+        ++packet_it;
+        while (packet_it != frame_it->second.end()) {
+          int shift = static_cast<uint8>(*packet_it - packet_id) - 1;
+          if (shift >= 0 && shift <= 7) {
+            bitmask |= (1 << shift);
+            ++packet_it;
+          } else {
+            break;
+          }
+        }
+        big_endian_nack_writer.WriteU8(bitmask);
+        ++number_of_loss_fields;
+      }
+    }
+  }
+  (*packet)[cast_size_pos] = static_cast<uint8>(4 + number_of_loss_fields);
+  (*packet)[cast_loss_field_pos] = static_cast<uint8>(number_of_loss_fields);
+
+  // Frames with missing packets.
+  TRACE_COUNTER_ID1("cast_rtcp", "RtcpSender::CastNACK", ssrc_,
+                    cast->missing_frames_and_packets_.size());
+}
+
+}  // namespace cast
+}  // namespace media
diff --git a/media/cast/rtcp/rtcp_sender.h b/media/cast/rtcp/rtcp_sender.h
new file mode 100644
index 0000000..7dbbc0f
--- /dev/null
+++ b/media/cast/rtcp/rtcp_sender.h
@@ -0,0 +1,111 @@
+// 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 MEDIA_CAST_RTCP_RTCP_SENDER_H_
+#define MEDIA_CAST_RTCP_RTCP_SENDER_H_
+
+#include <list>
+#include <string>
+
+#include "media/cast/cast_config.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/rtcp/rtcp.h"
+#include "media/cast/rtcp/rtcp_defines.h"
+
+namespace media {
+namespace cast {
+
+class RtcpSender {
+ public:
+  RtcpSender(PacedPacketSender* const paced_packet_sender,
+             uint32 sending_ssrc,
+             const std::string& c_name);
+
+  virtual ~RtcpSender();
+
+  void SendRtcp(uint32 packet_type_flags,
+                const RtcpSenderInfo* sender_info,
+                const RtcpReportBlock* report_block,
+                uint32 pli_remote_ssrc,
+                const RtcpDlrrReportBlock* dlrr,
+                const RtcpReceiverReferenceTimeReport* rrtr,
+                const RtcpCastMessage* cast_message);
+
+  enum RtcpPacketType {
+    kRtcpSr     = 0x0002,
+    kRtcpRr     = 0x0004,
+    kRtcpBye    = 0x0008,
+    kRtcpPli    = 0x0010,
+    kRtcpNack   = 0x0020,
+    kRtcpFir    = 0x0040,
+    kRtcpSrReq  = 0x0200,
+    kRtcpDlrr   = 0x0400,
+    kRtcpRrtr   = 0x0800,
+    kRtcpRpsi   = 0x8000,
+    kRtcpRemb   = 0x10000,
+    kRtcpCast   = 0x20000,
+  };
+
+ private:
+  void BuildSR(const RtcpSenderInfo& sender_info,
+               const RtcpReportBlock* report_block,
+               std::vector<uint8>* packet) const;
+
+  void BuildRR(const RtcpReportBlock* report_block,
+               std::vector<uint8>* packet) const;
+
+  void AddReportBlocks(const RtcpReportBlock& report_block,
+                       std::vector<uint8>* packet) const;
+
+  void BuildSdec(std::vector<uint8>* packet) const;
+
+  void BuildPli(uint32 remote_ssrc,
+                std::vector<uint8>* packet) const;
+
+  void BuildRemb(const RtcpRembMessage* remb,
+                 std::vector<uint8>* packet) const;
+
+  void BuildRpsi(const RtcpRpsiMessage* rpsi,
+                 std::vector<uint8>* packet) const;
+
+  void BuildNack(const RtcpNackMessage* nack,
+                 std::vector<uint8>* packet) const;
+
+  void BuildBye(std::vector<uint8>* packet) const;
+
+  void BuildDlrrRb(const RtcpDlrrReportBlock* dlrr,
+                   std::vector<uint8>* packet) const;
+
+  void BuildRrtr(const RtcpReceiverReferenceTimeReport* rrtr,
+                 std::vector<uint8>* packet) const;
+
+  void BuildCast(const RtcpCastMessage* cast_message,
+                 std::vector<uint8>* packet) const;
+
+  inline void BitrateToRembExponentBitrate(uint32 bitrate,
+                                           uint8* exponent,
+                                           uint32* mantissa) const {
+    // 6 bit exponent and a 18 bit mantissa.
+    *exponent = 0;
+    for (int i = 0; i < 64; ++i) {
+      if (bitrate <= (262143u << i)) {
+        *exponent = i;
+        break;
+      }
+    }
+    *mantissa = (bitrate >> *exponent);
+  }
+
+  const uint32 ssrc_;
+  const std::string c_name_;
+
+  // Not owned by this class.
+  PacedPacketSender* transport_;
+
+  DISALLOW_COPY_AND_ASSIGN(RtcpSender);
+};
+
+}  // namespace cast
+}  // namespace media
+#endif  // MEDIA_CAST_RTCP_RTCP_SENDER_H_
diff --git a/media/cast/rtcp/rtcp_sender_unittest.cc b/media/cast/rtcp/rtcp_sender_unittest.cc
new file mode 100644
index 0000000..b7daf37
--- /dev/null
+++ b/media/cast/rtcp/rtcp_sender_unittest.cc
@@ -0,0 +1,285 @@
+// 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 "base/memory/scoped_ptr.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/pacing/paced_sender.h"
+#include "media/cast/rtcp/rtcp_sender.h"
+#include "media/cast/rtcp/test_rtcp_packet_builder.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+namespace cast {
+
+static const int kRtcpInterval = 1000;
+static const uint32 kSendingSsrc = 0x12345678;
+static const uint32 kMediaSsrc = 0x87654321;
+static const std::string kCName("test@10.1.1.1");
+
+class TestRtcpTransport : public PacedPacketSender {
+ public:
+  TestRtcpTransport()
+      : expected_packet_length_(0),
+        packet_count_(0) {
+  }
+
+  virtual bool SendRtcpPacket(const std::vector<uint8>& packet) OVERRIDE {
+    EXPECT_EQ(expected_packet_length_, packet.size());
+    EXPECT_EQ(0, memcmp(expected_packet_, &(packet[0]), packet.size()));
+    packet_count_++;
+    return true;
+  }
+
+  virtual bool SendPacket(const std::vector<uint8>& packet,
+                          int num_of_packets) {
+    return false;
+  }
+
+  virtual bool ResendPacket(const std::vector<uint8>& packet,
+                            int num_of_packets) {
+    return false;
+  }
+
+  void SetExpectedRtcpPacket(const uint8* rtcp_buffer, int length) {
+    expected_packet_length_ = length;
+    memcpy(expected_packet_, rtcp_buffer, length);
+  }
+
+  int packet_count() { return packet_count_; }
+
+ private:
+  uint8 expected_packet_[kIpPacketSize];
+  size_t expected_packet_length_;
+  int packet_count_;
+};
+
+class RtcpSenderTest : public ::testing::Test {
+ protected:
+  RtcpSenderTest()
+      : rtcp_sender_(new RtcpSender(&test_transport_,
+                                    kSendingSsrc,
+                                    kCName)) {
+  }
+
+  TestRtcpTransport test_transport_;
+  scoped_ptr<RtcpSender> rtcp_sender_;
+};
+
+TEST_F(RtcpSenderTest, RtcpSenderReport) {
+  RtcpSenderInfo sender_info;
+  sender_info.ntp_seconds = kNtpHigh;
+  sender_info.ntp_fraction = kNtpLow;
+  sender_info.rtp_timestamp = kRtpTimestamp;
+  sender_info.send_packet_count = kSendPacketCount;
+  sender_info.send_octet_count = kSendOctetCount;
+
+  // Sender report + c_name.
+  TestRtcpPacketBuilder p;
+  p.AddSr(kSendingSsrc, 0);
+  p.AddSdesCname(kSendingSsrc, kCName);
+  test_transport_.SetExpectedRtcpPacket(p.Packet(), p.Length());
+
+  rtcp_sender_->SendRtcp(RtcpSender::kRtcpSr,
+                         &sender_info,
+                         NULL,
+                         0,
+                         NULL,
+                         NULL,
+                         NULL);
+
+  EXPECT_EQ(1, test_transport_.packet_count());
+}
+
+TEST_F(RtcpSenderTest, RtcpReceiverReport) {
+  // Empty receiver report + c_name.
+  TestRtcpPacketBuilder p1;
+  p1.AddRr(kSendingSsrc, 0);
+  p1.AddSdesCname(kSendingSsrc, kCName);
+  test_transport_.SetExpectedRtcpPacket(p1.Packet(), p1.Length());
+
+  rtcp_sender_->SendRtcp(RtcpSender::kRtcpRr,
+                         NULL,
+                         NULL,
+                         0,
+                         NULL,
+                         NULL,
+                         NULL);
+
+  EXPECT_EQ(1, test_transport_.packet_count());
+
+  // Receiver report with report block + c_name.
+  TestRtcpPacketBuilder p2;
+  p2.AddRr(kSendingSsrc, 1);
+  p2.AddRb(kMediaSsrc);
+  p2.AddSdesCname(kSendingSsrc, kCName);
+  test_transport_.SetExpectedRtcpPacket(p2.Packet(), p2.Length());
+
+  RtcpReportBlock report_block;
+  // Initialize remote_ssrc to a "clearly illegal" value.
+  report_block.remote_ssrc = 0xDEAD;
+  report_block.media_ssrc = kMediaSsrc;  // SSRC of the RTP packet sender.
+  report_block.fraction_lost = kLoss >> 24;
+  report_block.cumulative_lost = kLoss;  // 24 bits valid.
+  report_block.extended_high_sequence_number =
+      kExtendedMax;
+  report_block.jitter = kJitter;
+  report_block.last_sr = kLastSr;
+  report_block.delay_since_last_sr = kDelayLastSr;
+
+  rtcp_sender_->SendRtcp(RtcpSender::kRtcpRr,
+                         NULL,
+                         &report_block,
+                         0,
+                         NULL,
+                         NULL,
+                         NULL);
+
+  EXPECT_EQ(2, test_transport_.packet_count());
+}
+
+TEST_F(RtcpSenderTest, RtcpSenderReportWithDlrr) {
+  RtcpSenderInfo sender_info;
+  sender_info.ntp_seconds = kNtpHigh;
+  sender_info.ntp_fraction = kNtpLow;
+  sender_info.rtp_timestamp = kRtpTimestamp;
+  sender_info.send_packet_count = kSendPacketCount;
+  sender_info.send_octet_count = kSendOctetCount;
+
+  // Sender report + c_name + dlrr.
+  TestRtcpPacketBuilder p1;
+  p1.AddSr(kSendingSsrc, 0);
+  p1.AddSdesCname(kSendingSsrc, kCName);
+  p1.AddXrHeader(kSendingSsrc);
+  p1.AddXrDlrrBlock(kSendingSsrc);
+  test_transport_.SetExpectedRtcpPacket(p1.Packet(), p1.Length());
+
+  RtcpDlrrReportBlock dlrr_rb;
+  dlrr_rb.last_rr = kLastRr;
+  dlrr_rb.delay_since_last_rr = kDelayLastRr;
+
+  rtcp_sender_->SendRtcp(RtcpSender::kRtcpSr | RtcpSender::kRtcpDlrr,
+                         &sender_info,
+                         NULL,
+                         0,
+                         &dlrr_rb,
+                         NULL,
+                         NULL);
+
+  EXPECT_EQ(1, test_transport_.packet_count());
+}
+
+TEST_F(RtcpSenderTest, RtcpReceiverReportWithRrtr) {
+  // Receiver report with report block + c_name.
+  TestRtcpPacketBuilder p;
+  p.AddRr(kSendingSsrc, 1);
+  p.AddRb(kMediaSsrc);
+  p.AddSdesCname(kSendingSsrc, kCName);
+  p.AddXrHeader(kSendingSsrc);
+  p.AddXrRrtrBlock();
+  test_transport_.SetExpectedRtcpPacket(p.Packet(), p.Length());
+
+  RtcpReportBlock report_block;
+  // Initialize remote_ssrc to a "clearly illegal" value.
+  report_block.remote_ssrc = 0xDEAD;
+  report_block.media_ssrc = kMediaSsrc;  // SSRC of the RTP packet sender.
+  report_block.fraction_lost = kLoss >> 24;
+  report_block.cumulative_lost = kLoss;  // 24 bits valid.
+  report_block.extended_high_sequence_number =
+      kExtendedMax;
+  report_block.jitter = kJitter;
+  report_block.last_sr = kLastSr;
+  report_block.delay_since_last_sr = kDelayLastSr;
+
+  RtcpReceiverReferenceTimeReport rrtr;
+  rrtr.ntp_seconds = kNtpHigh;
+  rrtr.ntp_fraction = kNtpLow;
+
+  rtcp_sender_->SendRtcp(RtcpSender::kRtcpRr | RtcpSender::kRtcpRrtr,
+                         NULL,
+                         &report_block,
+                         0,
+                         NULL,
+                         &rrtr,
+                         NULL);
+
+  EXPECT_EQ(1, test_transport_.packet_count());
+}
+
+TEST_F(RtcpSenderTest, RtcpReceiverReportWithCast) {
+  // Receiver report with report block + c_name.
+  TestRtcpPacketBuilder p;
+  p.AddRr(kSendingSsrc, 1);
+  p.AddRb(kMediaSsrc);
+  p.AddSdesCname(kSendingSsrc, kCName);
+  p.AddCast(kSendingSsrc, kMediaSsrc);
+  test_transport_.SetExpectedRtcpPacket(p.Packet(), p.Length());
+
+  RtcpReportBlock report_block;
+  // Initialize remote_ssrc to a "clearly illegal" value.
+  report_block.remote_ssrc = 0xDEAD;
+  report_block.media_ssrc = kMediaSsrc;  // SSRC of the RTP packet sender.
+  report_block.fraction_lost = kLoss >> 24;
+  report_block.cumulative_lost = kLoss;  // 24 bits valid.
+  report_block.extended_high_sequence_number = kExtendedMax;
+  report_block.jitter = kJitter;
+  report_block.last_sr = kLastSr;
+  report_block.delay_since_last_sr = kDelayLastSr;
+
+  RtcpCastMessage cast_message(kMediaSsrc);
+  cast_message.ack_frame_id_ = kAckFrameId;
+  std::set<uint16_t> missing_packets;
+  cast_message.missing_frames_and_packets_[
+      kLostFrameId] = missing_packets;
+
+  missing_packets.insert(kLostPacketId1);
+  missing_packets.insert(kLostPacketId2);
+  missing_packets.insert(kLostPacketId3);
+  cast_message.missing_frames_and_packets_[kFrameIdWithLostPackets] =
+      missing_packets;
+
+  rtcp_sender_->SendRtcp(RtcpSender::kRtcpRr | RtcpSender::kRtcpCast,
+                         NULL,
+                         &report_block,
+                         0,
+                         NULL,
+                         NULL,
+                         &cast_message);
+
+  EXPECT_EQ(1, test_transport_.packet_count());
+}
+
+TEST_F(RtcpSenderTest, RtcpReceiverReportWithIntraFrameRequest) {
+  // Receiver report with report block + c_name.
+  TestRtcpPacketBuilder p;
+  p.AddRr(kSendingSsrc, 1);
+  p.AddRb(kMediaSsrc);
+  p.AddSdesCname(kSendingSsrc, kCName);
+  p.AddPli(kSendingSsrc, kMediaSsrc);
+  test_transport_.SetExpectedRtcpPacket(p.Packet(), p.Length());
+
+  RtcpReportBlock report_block;
+  // Initialize remote_ssrc to a "clearly illegal" value.
+  report_block.remote_ssrc = 0xDEAD;
+  report_block.media_ssrc = kMediaSsrc;  // SSRC of the RTP packet sender.
+  report_block.fraction_lost = kLoss >> 24;
+  report_block.cumulative_lost = kLoss;  // 24 bits valid.
+  report_block.extended_high_sequence_number =
+      kExtendedMax;
+  report_block.jitter = kJitter;
+  report_block.last_sr = kLastSr;
+  report_block.delay_since_last_sr = kDelayLastSr;
+
+  rtcp_sender_->SendRtcp(RtcpSender::kRtcpRr | RtcpSender::kRtcpPli,
+                         NULL,
+                         &report_block,
+                         kMediaSsrc,
+                         NULL,
+                         NULL,
+                         NULL);
+
+  EXPECT_EQ(1, test_transport_.packet_count());
+}
+
+}  // namespace cast
+}  // namespace media
diff --git a/media/cast/rtcp/rtcp_unittest.cc b/media/cast/rtcp/rtcp_unittest.cc
new file mode 100644
index 0000000..049fbeb
--- /dev/null
+++ b/media/cast/rtcp/rtcp_unittest.cc
@@ -0,0 +1,414 @@
+// 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 "base/test/simple_test_tick_clock.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/pacing/paced_sender.h"
+#include "media/cast/rtcp/mock_rtcp_receiver_feedback.h"
+#include "media/cast/rtcp/mock_rtcp_sender_feedback.h"
+#include "media/cast/rtcp/rtcp.h"
+#include "media/cast/rtcp/test_rtcp_packet_builder.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+namespace cast {
+
+using testing::_;
+
+static const uint32 kSenderSsrc = 0x10203;
+static const uint32 kReceiverSsrc = 0x40506;
+static const uint32 kUnknownSsrc = 0xDEAD;
+static const std::string kCName("test@10.1.1.1");
+static const uint32 kRtcpIntervalMs = 500;
+static const int64 kStartMillisecond = 123456789;
+static const int64 kAddedDelay = 123;
+static const int64 kAddedShortDelay= 100;
+
+class LocalRtcpTransport : public PacedPacketSender {
+ public:
+  explicit LocalRtcpTransport(base::SimpleTestTickClock* testing_clock)
+      : short_delay_(false),
+        testing_clock_(testing_clock) {}
+
+  void SetRtcpReceiver(Rtcp* rtcp)  { rtcp_ = rtcp; }
+
+  void SetShortDelay()  { short_delay_ = true; }
+
+  virtual bool SendRtcpPacket(const std::vector<uint8>& packet) OVERRIDE {
+    if (short_delay_) {
+      testing_clock_->Advance(
+          base::TimeDelta::FromMilliseconds(kAddedShortDelay));
+    } else {
+      testing_clock_->Advance(base::TimeDelta::FromMilliseconds(kAddedDelay));
+    }
+    rtcp_->IncomingRtcpPacket(&(packet[0]), packet.size());
+    return true;
+  }
+
+  virtual bool SendPacket(const std::vector<uint8>& packet,
+                          int num_of_packets) OVERRIDE {
+    return false;
+  }
+
+  virtual bool ResendPacket(const std::vector<uint8>& packet,
+                            int num_of_packets) OVERRIDE {
+    return false;
+  }
+
+ private:
+  bool short_delay_;
+  Rtcp* rtcp_;
+  base::SimpleTestTickClock* testing_clock_;
+};
+
+class RtcpPeer : public Rtcp {
+ public:
+  RtcpPeer(RtcpSenderFeedback* sender_feedback,
+           PacedPacketSender* const paced_packet_sender,
+           RtpSenderStatistics* rtp_sender_statistics,
+           RtpReceiverStatistics* rtp_receiver_statistics,
+           RtcpMode rtcp_mode,
+           const base::TimeDelta& rtcp_interval,
+           bool sending_media,
+           uint32 local_ssrc,
+           const std::string& c_name)
+      : Rtcp(sender_feedback,
+             paced_packet_sender,
+             rtp_sender_statistics,
+             rtp_receiver_statistics,
+             rtcp_mode,
+             rtcp_interval,
+             sending_media,
+             local_ssrc,
+             c_name) {
+  }
+
+  using Rtcp::ConvertTimeToNtp;
+  using Rtcp::ConvertNtpToTime;
+  using Rtcp::CheckForWrapAround;
+  using Rtcp::OnReceivedLipSyncInfo;
+};
+
+class RtcpTest : public ::testing::Test {
+ protected:
+  RtcpTest()
+      : transport_(&testing_clock_) {
+    testing_clock_.Advance(
+        base::TimeDelta::FromMilliseconds(kStartMillisecond));
+  }
+
+  ~RtcpTest() {}
+
+  void SetUp() {
+    EXPECT_CALL(mock_sender_feedback_, OnReceivedReportBlock(_)).Times(0);
+    EXPECT_CALL(mock_sender_feedback_, OnReceivedIntraFrameRequest()).Times(0);
+    EXPECT_CALL(mock_sender_feedback_, OnReceivedRpsi(_, _)).Times(0);
+    EXPECT_CALL(mock_sender_feedback_, OnReceivedRemb(_)).Times(0);
+    EXPECT_CALL(mock_sender_feedback_, OnReceivedNackRequest(_)).Times(0);
+    EXPECT_CALL(mock_sender_feedback_, OnReceivedCastFeedback(_)).Times(0);
+  }
+
+  base::SimpleTestTickClock testing_clock_;
+  LocalRtcpTransport transport_;
+  MockRtcpSenderFeedback mock_sender_feedback_;
+};
+
+TEST_F(RtcpTest, TimeToSend) {
+  base::TimeTicks start_time =
+      base::TimeTicks::FromInternalValue(kStartMillisecond * 1000);
+  Rtcp rtcp(&mock_sender_feedback_,
+            &transport_,
+            NULL,
+            NULL,
+            kRtcpCompound,
+            base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
+            true,  // Media sender.
+            kSenderSsrc,
+            kCName);
+  rtcp.set_clock(&testing_clock_);
+  transport_.SetRtcpReceiver(&rtcp);
+  EXPECT_LE(start_time, rtcp.TimeToSendNextRtcpReport());
+  EXPECT_GE(start_time + base::TimeDelta::FromMilliseconds(
+                kRtcpIntervalMs * 3 / 2),
+            rtcp.TimeToSendNextRtcpReport());
+  base::TimeDelta delta = rtcp.TimeToSendNextRtcpReport() - start_time;
+  testing_clock_.Advance(delta);
+  EXPECT_EQ(testing_clock_.NowTicks(), rtcp.TimeToSendNextRtcpReport());
+}
+
+TEST_F(RtcpTest, BasicSenderReport) {
+  Rtcp rtcp(&mock_sender_feedback_,
+            &transport_,
+            NULL,
+            NULL,
+            kRtcpCompound,
+            base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
+            true,  // Media sender.
+            kSenderSsrc,
+            kCName);
+  transport_.SetRtcpReceiver(&rtcp);
+  rtcp.SendRtcpReport(kUnknownSsrc);
+}
+
+TEST_F(RtcpTest, BasicReceiverReport) {
+  EXPECT_CALL(mock_sender_feedback_, OnReceivedReportBlock(_)).Times(1);
+  Rtcp rtcp(&mock_sender_feedback_,
+            &transport_,
+            NULL,
+            NULL,
+            kRtcpCompound,
+            base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
+            false,  // Media receiver.
+            kSenderSsrc,
+            kCName);
+  transport_.SetRtcpReceiver(&rtcp);
+  rtcp.SetRemoteSSRC(kSenderSsrc);
+  rtcp.SendRtcpReport(kSenderSsrc);
+}
+
+TEST_F(RtcpTest, BasicPli) {
+  EXPECT_CALL(mock_sender_feedback_, OnReceivedReportBlock(_)).Times(1);
+  EXPECT_CALL(mock_sender_feedback_, OnReceivedIntraFrameRequest()).Times(1);
+
+  // Media receiver.
+  Rtcp rtcp(&mock_sender_feedback_,
+            &transport_,
+            NULL,
+            NULL,
+            kRtcpReducedSize,
+            base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
+            false,
+            kSenderSsrc,
+            kCName);
+  rtcp.set_clock(&testing_clock_);
+  transport_.SetRtcpReceiver(&rtcp);
+  rtcp.SetRemoteSSRC(kSenderSsrc);
+  rtcp.SendRtcpPli(kSenderSsrc);
+}
+
+TEST_F(RtcpTest, BasicCast) {
+  EXPECT_CALL(mock_sender_feedback_, OnReceivedCastFeedback(_)).Times(1);
+
+  // Media receiver.
+  Rtcp rtcp(&mock_sender_feedback_,
+            &transport_,
+            NULL,
+            NULL,
+            kRtcpReducedSize,
+            base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
+            false,
+            kSenderSsrc,
+            kCName);
+  rtcp.set_clock(&testing_clock_);
+  transport_.SetRtcpReceiver(&rtcp);
+  rtcp.SetRemoteSSRC(kSenderSsrc);
+  RtcpCastMessage cast_message(kSenderSsrc);
+  cast_message.ack_frame_id_ = kAckFrameId;
+  std::set<uint16_t> missing_packets;
+  cast_message.missing_frames_and_packets_[
+      kLostFrameId] = missing_packets;
+
+  missing_packets.insert(kLostPacketId1);
+  missing_packets.insert(kLostPacketId2);
+  missing_packets.insert(kLostPacketId3);
+  cast_message.missing_frames_and_packets_[
+      kFrameIdWithLostPackets] = missing_packets;
+  rtcp.SendRtcpCast(cast_message);
+}
+
+TEST_F(RtcpTest, Rtt) {
+  // Media receiver.
+  LocalRtcpTransport receiver_transport(&testing_clock_);
+  Rtcp rtcp_receiver(&mock_sender_feedback_,
+                     &receiver_transport,
+                     NULL,
+                     NULL,
+                     kRtcpReducedSize,
+                     base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
+                     false,
+                     kReceiverSsrc,
+                     kCName);
+  rtcp_receiver.set_clock(&testing_clock_);
+
+  // Media sender.
+  LocalRtcpTransport sender_transport(&testing_clock_);
+  Rtcp rtcp_sender(&mock_sender_feedback_,
+                   &sender_transport,
+                   NULL,
+                   NULL,
+                   kRtcpReducedSize,
+                   base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
+                   true,
+                   kSenderSsrc,
+                   kCName);
+
+  rtcp_sender.set_clock(&testing_clock_);
+  receiver_transport.SetRtcpReceiver(&rtcp_sender);
+  sender_transport.SetRtcpReceiver(&rtcp_receiver);
+
+  rtcp_sender.SetRemoteSSRC(kReceiverSsrc);
+  rtcp_receiver.SetRemoteSSRC(kSenderSsrc);
+
+  EXPECT_CALL(mock_sender_feedback_, OnReceivedReportBlock(_)).Times(2);
+
+  base::TimeDelta rtt;
+  base::TimeDelta avg_rtt;
+  base::TimeDelta min_rtt;
+  base::TimeDelta max_rtt;
+  EXPECT_FALSE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt,  &max_rtt));
+  EXPECT_FALSE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt,  &max_rtt));
+
+  rtcp_sender.SendRtcpReport(kSenderSsrc);
+  rtcp_receiver.SendRtcpReport(kSenderSsrc);
+  EXPECT_TRUE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt,  &max_rtt));
+  EXPECT_FALSE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt,  &max_rtt));
+  EXPECT_NEAR(2 * kAddedDelay, rtt.InMilliseconds(), 1);
+  EXPECT_NEAR(2 * kAddedDelay, avg_rtt.InMilliseconds(), 1);
+  EXPECT_NEAR(2 * kAddedDelay, min_rtt.InMilliseconds(), 1);
+  EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 1);
+  rtcp_sender.SendRtcpReport(kSenderSsrc);
+  EXPECT_TRUE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt,  &max_rtt));
+
+  EXPECT_NEAR(2 * kAddedDelay, rtt.InMilliseconds(), 1);
+  EXPECT_NEAR(2 * kAddedDelay, avg_rtt.InMilliseconds(), 1);
+  EXPECT_NEAR(2 * kAddedDelay, min_rtt.InMilliseconds(), 1);
+  EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 1);
+
+  receiver_transport.SetShortDelay();
+  sender_transport.SetShortDelay();
+  rtcp_receiver.SendRtcpReport(kSenderSsrc);
+  EXPECT_TRUE(rtcp_sender.Rtt(&rtt, &avg_rtt, &min_rtt,  &max_rtt));
+
+  EXPECT_NEAR(kAddedDelay + kAddedShortDelay, rtt.InMilliseconds(), 1);
+  EXPECT_NEAR((kAddedShortDelay + 3 * kAddedDelay) / 2,
+               avg_rtt.InMilliseconds(),
+               1);
+  EXPECT_NEAR(kAddedDelay + kAddedShortDelay, min_rtt.InMilliseconds(), 1);
+  EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 1);
+  rtcp_sender.SendRtcpReport(kSenderSsrc);
+  EXPECT_TRUE(rtcp_receiver.Rtt(&rtt, &avg_rtt, &min_rtt,  &max_rtt));
+
+  EXPECT_NEAR(2 * kAddedShortDelay, rtt.InMilliseconds(), 1);
+  EXPECT_NEAR((2 * kAddedShortDelay + 2 * kAddedDelay) / 2,
+               avg_rtt.InMilliseconds(),
+               1);
+  EXPECT_NEAR(2 *  kAddedShortDelay, min_rtt.InMilliseconds(), 1);
+  EXPECT_NEAR(2 * kAddedDelay, max_rtt.InMilliseconds(), 1);
+}
+
+TEST_F(RtcpTest, NtpAndTime) {
+  RtcpPeer rtcp_peer(&mock_sender_feedback_,
+                     NULL,
+                     NULL,
+                     NULL,
+                     kRtcpReducedSize,
+                     base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
+                     false,
+                     kReceiverSsrc,
+                     kCName);
+  rtcp_peer.set_clock(&testing_clock_);
+  int64 input_time_us = 12345678901000LL + Rtcp::kNtpEpochDeltaMicroseconds;
+  uint32 ntp_seconds = 0;
+  uint32 ntp_fractions = 0;
+  base::TimeTicks input_time =
+      base::TimeTicks::FromInternalValue(input_time_us);
+  rtcp_peer.ConvertTimeToNtp(input_time, &ntp_seconds, &ntp_fractions);
+  EXPECT_EQ(12345678u, ntp_seconds);
+  EXPECT_EQ(input_time,
+            rtcp_peer.ConvertNtpToTime(ntp_seconds, ntp_fractions));
+}
+
+TEST_F(RtcpTest, WrapAround) {
+  RtcpPeer rtcp_peer(&mock_sender_feedback_,
+                     NULL,
+                     NULL,
+                     NULL,
+                     kRtcpReducedSize,
+                     base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
+                     false,
+                     kReceiverSsrc,
+                     kCName);
+  rtcp_peer.set_clock(&testing_clock_);
+  uint32 new_timestamp = 0;
+  uint32 old_timestamp = 0;
+  EXPECT_EQ(0, rtcp_peer.CheckForWrapAround(new_timestamp, old_timestamp));
+  new_timestamp = 1234567890;
+  old_timestamp = 1234567000;
+  EXPECT_EQ(0, rtcp_peer.CheckForWrapAround(new_timestamp, old_timestamp));
+  new_timestamp = 1234567000;
+  old_timestamp = 1234567890;
+  EXPECT_EQ(0, rtcp_peer.CheckForWrapAround(new_timestamp, old_timestamp));
+  new_timestamp = 123;
+  old_timestamp = 4234567890;
+  EXPECT_EQ(1, rtcp_peer.CheckForWrapAround(new_timestamp, old_timestamp));
+  new_timestamp = 4234567890;
+  old_timestamp = 123;
+  EXPECT_EQ(-1, rtcp_peer.CheckForWrapAround(new_timestamp, old_timestamp));
+}
+
+TEST_F(RtcpTest, RtpTimestampInSenderTime) {
+  RtcpPeer rtcp_peer(&mock_sender_feedback_,
+                     NULL,
+                     NULL,
+                     NULL,
+                     kRtcpReducedSize,
+                     base::TimeDelta::FromMilliseconds(kRtcpIntervalMs),
+                     false,
+                     kReceiverSsrc,
+                     kCName);
+  rtcp_peer.set_clock(&testing_clock_);
+  int frequency = 32000;
+  uint32 rtp_timestamp = 64000;
+  base::TimeTicks rtp_timestamp_in_ticks;
+
+  // Test fail before we get a OnReceivedLipSyncInfo.
+  EXPECT_FALSE(rtcp_peer.RtpTimestampInSenderTime(frequency, rtp_timestamp,
+                                                   &rtp_timestamp_in_ticks));
+
+  int64 input_time_us = 12345678901000LL + Rtcp::kNtpEpochDeltaMicroseconds;
+  uint32 ntp_seconds = 0;
+  uint32 ntp_fractions = 0;
+  base::TimeTicks input_time =
+      base::TimeTicks::FromInternalValue(input_time_us);
+
+  // Test exact match.
+  rtcp_peer.ConvertTimeToNtp(input_time, &ntp_seconds, &ntp_fractions);
+  rtcp_peer.OnReceivedLipSyncInfo(rtp_timestamp, ntp_seconds, ntp_fractions);
+  EXPECT_TRUE(rtcp_peer.RtpTimestampInSenderTime(frequency, rtp_timestamp,
+                                                  &rtp_timestamp_in_ticks));
+  EXPECT_EQ(input_time, rtp_timestamp_in_ticks);
+
+  // Test older rtp_timestamp.
+  rtp_timestamp = 32000;
+  EXPECT_TRUE(rtcp_peer.RtpTimestampInSenderTime(frequency, rtp_timestamp,
+                                                  &rtp_timestamp_in_ticks));
+  EXPECT_EQ(input_time - base::TimeDelta::FromMilliseconds(1000),
+            rtp_timestamp_in_ticks);
+
+  // Test older rtp_timestamp with wrap.
+  rtp_timestamp = 4294903296;
+  EXPECT_TRUE(rtcp_peer.RtpTimestampInSenderTime(frequency, rtp_timestamp,
+                                                  &rtp_timestamp_in_ticks));
+  EXPECT_EQ(input_time - base::TimeDelta::FromMilliseconds(4000),
+            rtp_timestamp_in_ticks);
+
+  // Test newer rtp_timestamp.
+  rtp_timestamp = 128000;
+  EXPECT_TRUE(rtcp_peer.RtpTimestampInSenderTime(frequency, rtp_timestamp,
+                                                  &rtp_timestamp_in_ticks));
+  EXPECT_EQ(input_time + base::TimeDelta::FromMilliseconds(2000),
+            rtp_timestamp_in_ticks);
+
+  // Test newer rtp_timestamp with wrap.
+  rtp_timestamp = 4294903296;
+  rtcp_peer.OnReceivedLipSyncInfo(rtp_timestamp, ntp_seconds, ntp_fractions);
+  rtp_timestamp = 64000;
+  EXPECT_TRUE(rtcp_peer.RtpTimestampInSenderTime(frequency, rtp_timestamp,
+                                                  &rtp_timestamp_in_ticks));
+  EXPECT_EQ(input_time + base::TimeDelta::FromMilliseconds(4000),
+            rtp_timestamp_in_ticks);
+}
+
+}  // namespace cast
+}  // namespace media
diff --git a/media/cast/rtcp/rtcp_utility.cc b/media/cast/rtcp/rtcp_utility.cc
new file mode 100644
index 0000000..4f9d2ec
--- /dev/null
+++ b/media/cast/rtcp/rtcp_utility.cc
@@ -0,0 +1,862 @@
+// 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 "media/cast/rtcp/rtcp_utility.h"
+
+#include "base/logging.h"
+#include "net/base/big_endian.h"
+
+namespace media {
+namespace cast {
+
+RtcpParser::RtcpParser(const uint8* rtcpData, size_t rtcpDataLength)
+    : rtcp_data_begin_(rtcpData),
+      rtcp_data_end_(rtcpData + rtcpDataLength),
+      valid_packet_(false),
+      rtcp_data_(rtcpData),
+      rtcp_block_end_(NULL),
+      state_(kStateTopLevel),
+      number_of_blocks_(0),
+      field_type_(kRtcpNotValidCode) {
+  Validate();
+}
+
+RtcpParser::~RtcpParser() {}
+
+RtcpFieldTypes RtcpParser::FieldType() const {
+  return field_type_;
+}
+
+const RtcpField& RtcpParser::Field() const {
+  return field_;
+}
+
+RtcpFieldTypes RtcpParser::Begin() {
+  rtcp_data_ = rtcp_data_begin_;
+  return Iterate();
+}
+
+RtcpFieldTypes RtcpParser::Iterate() {
+  // Reset packet type
+  field_type_ = kRtcpNotValidCode;
+
+  if (!IsValid()) return kRtcpNotValidCode;
+
+  switch (state_) {
+    case kStateTopLevel:
+      IterateTopLevel();
+      break;
+    case kStateReportBlock:
+      IterateReportBlockItem();
+      break;
+    case kStateSdes:
+      IterateSdesItem();
+      break;
+    case kStateBye:
+      IterateByeItem();
+      break;
+    case kStateExtendedReportBlock:
+      IterateExtendedReportItem();
+      break;
+    case kStateExtendedReportDelaySinceLastReceiverReport:
+      IterateExtendedReportDelaySinceLastReceiverReportItem();
+      break;
+    case kStateGenericRtpFeedbackNack:
+      IterateNackItem();
+      break;
+    case kStatePayloadSpecificRpsi:
+      IterateRpsiItem();
+      break;
+    case kStatePayloadSpecificFir:
+      IterateFirItem();
+      break;
+    case kStatePayloadSpecificApplication:
+      IteratePayloadSpecificAppItem();
+      break;
+    case kStatePayloadSpecificRemb:
+      IteratePayloadSpecificRembItem();
+      break;
+    case kStatePayloadSpecificCast:
+      IteratePayloadSpecificCastItem();
+      break;
+    case kStatePayloadSpecificCastNack:
+      IteratePayloadSpecificCastNackItem();
+      break;
+  }
+  return field_type_;
+}
+
+void RtcpParser::IterateTopLevel() {
+  for (;;) {
+    RtcpCommonHeader header;
+
+    bool success = RtcpParseCommonHeader(rtcp_data_, rtcp_data_end_, &header);
+    if (!success) return;
+
+    rtcp_block_end_ = rtcp_data_ + header.length_in_octets;
+
+    if (rtcp_block_end_ > rtcp_data_end_) return;  // Bad block!
+
+    switch (header.PT) {
+      case kPacketTypeSenderReport:
+        // number of Report blocks
+        number_of_blocks_ = header.IC;
+        ParseSR();
+        return;
+      case kPacketTypeReceiverReport:
+        // number of Report blocks
+        number_of_blocks_ = header.IC;
+        ParseRR();
+        return;
+      case kPacketTypeSdes:
+        // number of Sdes blocks
+        number_of_blocks_ = header.IC;
+        if (!ParseSdes()) {
+          break;  // Nothing supported found, continue to next block!
+        }
+        return;
+      case kPacketTypeBye:
+        number_of_blocks_ = header.IC;
+        if (!ParseBye()) {
+          // Nothing supported found, continue to next block!
+          break;
+        }
+        return;
+      case kPacketTypeGenericRtpFeedback:  // Fall through!
+      case kPacketTypePayloadSpecific:
+        if (!ParseFeedBackCommon(header)) {
+          // Nothing supported found, continue to next block!
+          break;
+        }
+        return;
+      case kPacketTypeXr:
+        if (!ParseExtendedReport()) {
+          break;  // Nothing supported found, continue to next block!
+        }
+        return;
+      default:
+        // Not supported! Skip!
+        EndCurrentBlock();
+        break;
+    }
+  }
+}
+
+void RtcpParser::IterateReportBlockItem() {
+  bool success = ParseReportBlockItem();
+  if (!success) Iterate();
+}
+
+void RtcpParser::IterateSdesItem() {
+  bool success = ParseSdesItem();
+  if (!success) Iterate();
+}
+
+void RtcpParser::IterateByeItem() {
+  bool success = ParseByeItem();
+  if (!success) Iterate();
+}
+
+void RtcpParser::IterateExtendedReportItem() {
+  bool success = ParseExtendedReportItem();
+  if (!success) Iterate();
+}
+
+void RtcpParser::IterateExtendedReportDelaySinceLastReceiverReportItem() {
+  bool success = ParseExtendedReportDelaySinceLastReceiverReport();
+  if (!success) Iterate();
+}
+
+void RtcpParser::IterateNackItem() {
+  bool success = ParseNackItem();
+  if (!success) Iterate();
+}
+
+void RtcpParser::IterateRpsiItem() {
+  bool success = ParseRpsiItem();
+  if (!success) Iterate();
+}
+
+void RtcpParser::IterateFirItem() {
+  bool success = ParseFirItem();
+  if (!success) Iterate();
+}
+
+void RtcpParser::IteratePayloadSpecificAppItem() {
+  bool success = ParsePayloadSpecificAppItem();
+  if (!success) Iterate();
+}
+
+void RtcpParser::IteratePayloadSpecificRembItem() {
+  bool success = ParsePayloadSpecificRembItem();
+  if (!success) Iterate();
+}
+
+void RtcpParser::IteratePayloadSpecificCastItem() {
+  bool success = ParsePayloadSpecificCastItem();
+  if (!success) Iterate();
+}
+
+void RtcpParser::IteratePayloadSpecificCastNackItem() {
+  bool success = ParsePayloadSpecificCastNackItem();
+  if (!success) Iterate();
+}
+
+void RtcpParser::Validate() {
+  if (rtcp_data_ == NULL) return;  // NOT VALID
+
+  RtcpCommonHeader header;
+  bool success = RtcpParseCommonHeader(rtcp_data_begin_, rtcp_data_end_,
+                                       &header);
+
+  if (!success) return;  // NOT VALID!
+
+  valid_packet_ = true;
+}
+
+bool RtcpParser::IsValid() const {
+  return valid_packet_;
+}
+
+void RtcpParser::EndCurrentBlock() {
+  rtcp_data_ = rtcp_block_end_;
+}
+
+bool RtcpParser::RtcpParseCommonHeader(const uint8* data_begin,
+                                       const uint8* data_end,
+                                       RtcpCommonHeader* parsed_header) const {
+  if (!data_begin || !data_end) return false;
+
+  //  0                   1                   2                   3
+  //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  // |V=2|P|    IC   |      PT       |             length            |
+  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  //
+  // Common header for all Rtcp packets, 4 octets.
+
+  if ((data_end - data_begin) < 4) return false;
+
+  parsed_header->V  = data_begin[0] >> 6;
+  parsed_header->P  = ((data_begin[0] & 0x20) == 0) ? false : true;
+  parsed_header->IC = data_begin[0] & 0x1f;
+  parsed_header->PT = data_begin[1];
+
+  parsed_header->length_in_octets =
+      ((data_begin[2] << 8) + data_begin[3] + 1) * 4;
+
+  if (parsed_header->length_in_octets == 0) return false;
+
+  // Check if RTP version field == 2.
+  if (parsed_header->V != 2) return false;
+
+  return true;
+}
+
+bool RtcpParser::ParseRR() {
+  ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
+  if (length < 8) return false;
+
+  field_type_ = kRtcpRrCode;
+
+  net::BigEndianReader big_endian_reader(rtcp_data_, length);
+  big_endian_reader.Skip(4);  // Skip header
+  big_endian_reader.ReadU32(&field_.receiver_report.sender_ssrc);
+  field_.receiver_report.number_of_report_blocks = number_of_blocks_;
+  rtcp_data_ += 8;
+
+  // State transition
+  state_ = kStateReportBlock;
+  return true;
+}
+
+bool RtcpParser::ParseSR() {
+  ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
+  if (length < 28) {
+    EndCurrentBlock();
+    return false;
+  }
+  field_type_ = kRtcpSrCode;
+
+  net::BigEndianReader big_endian_reader(rtcp_data_, length);
+  big_endian_reader.Skip(4);  // Skip header
+  big_endian_reader.ReadU32(&field_.sender_report.sender_ssrc);
+  big_endian_reader.ReadU32(&field_.sender_report.ntp_most_significant);
+  big_endian_reader.ReadU32(&field_.sender_report.ntp_least_significant);
+  big_endian_reader.ReadU32(&field_.sender_report.rtp_timestamp);
+  big_endian_reader.ReadU32(&field_.sender_report.sender_packet_count);
+  big_endian_reader.ReadU32(&field_.sender_report.sender_octet_count);
+  field_.sender_report.number_of_report_blocks = number_of_blocks_;
+  rtcp_data_ += 28;
+
+  if (number_of_blocks_ != 0) {
+    // State transition.
+    state_ = kStateReportBlock;
+  } else {
+    // Don't go to state report block item if 0 report blocks.
+    state_ = kStateTopLevel;
+    EndCurrentBlock();
+  }
+  return true;
+}
+
+bool RtcpParser::ParseReportBlockItem() {
+  ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
+  if (length < 24 || number_of_blocks_ <= 0) {
+    state_ = kStateTopLevel;
+    EndCurrentBlock();
+    return false;
+  }
+
+  net::BigEndianReader big_endian_reader(rtcp_data_, length);
+  big_endian_reader.ReadU32(&field_.report_block_item.ssrc);
+  big_endian_reader.ReadU8(&field_.report_block_item.fraction_lost);
+
+  uint8 temp_number_of_packets_lost;
+  big_endian_reader.ReadU8(&temp_number_of_packets_lost);
+  field_.report_block_item.cumulative_number_of_packets_lost =
+      temp_number_of_packets_lost << 16;
+  big_endian_reader.ReadU8(&temp_number_of_packets_lost);
+  field_.report_block_item.cumulative_number_of_packets_lost +=
+      temp_number_of_packets_lost << 8;
+  big_endian_reader.ReadU8(&temp_number_of_packets_lost);
+  field_.report_block_item.cumulative_number_of_packets_lost +=
+      temp_number_of_packets_lost;
+
+  big_endian_reader.ReadU32(
+      &field_.report_block_item.extended_highest_sequence_number);
+  big_endian_reader.ReadU32(&field_.report_block_item.jitter);
+  big_endian_reader.ReadU32(&field_.report_block_item.last_sender_report);
+  big_endian_reader.ReadU32(&field_.report_block_item.delay_last_sender_report);
+  rtcp_data_ += 24;
+
+  number_of_blocks_--;
+  field_type_ = kRtcpReportBlockItemCode;
+  return true;
+}
+
+bool RtcpParser::ParseSdes() {
+  ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
+
+  if (length < 8) {
+    state_ = kStateTopLevel;
+    EndCurrentBlock();
+    return false;
+  }
+  rtcp_data_ += 4;  // Skip header
+
+  state_ = kStateSdes;
+  field_type_ = kRtcpSdesCode;
+  return true;
+}
+
+bool RtcpParser::ParseSdesItem() {
+  if (number_of_blocks_ <= 0) {
+    state_ = kStateTopLevel;
+    EndCurrentBlock();
+    return false;
+  }
+  number_of_blocks_--;
+
+  // Find c_name item in a Sdes chunk.
+  while (rtcp_data_ < rtcp_block_end_) {
+    ptrdiff_t data_length = rtcp_block_end_ - rtcp_data_;
+    if (data_length < 4) {
+      state_ = kStateTopLevel;
+      EndCurrentBlock();
+      return false;
+    }
+
+    uint32 ssrc;
+    net::BigEndianReader big_endian_reader(rtcp_data_, data_length);
+    big_endian_reader.ReadU32(&ssrc);
+    rtcp_data_ += 4;
+
+    bool found_c_name = ParseSdesTypes();
+    if (found_c_name) {
+      field_.c_name.sender_ssrc = ssrc;
+      return true;
+    }
+  }
+  state_ = kStateTopLevel;
+  EndCurrentBlock();
+  return false;
+}
+
+bool RtcpParser::ParseSdesTypes() {
+  // Only the c_name item is mandatory. RFC 3550 page 46.
+  bool found_c_name = false;
+  ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
+  net::BigEndianReader big_endian_reader(rtcp_data_, length);
+
+  while (big_endian_reader.remaining() > 0) {
+    uint8 tag;
+    big_endian_reader.ReadU8(&tag);
+
+    if (tag == 0) {
+      // End tag! 4 octet aligned.
+      rtcp_data_ = rtcp_block_end_;
+      return found_c_name;
+    }
+
+    if (big_endian_reader.remaining() > 0) {
+      uint8 len;
+      big_endian_reader.ReadU8(&len);
+
+      if (tag == 1) {  // c_name.
+        // Sanity check.
+        if (big_endian_reader.remaining() < len) {
+          state_ = kStateTopLevel;
+          EndCurrentBlock();
+          return false;
+        }
+        int i = 0;
+        for (; i < len; ++i) {
+          uint8 c;
+          big_endian_reader.ReadU8(&c);
+          if ((c < ' ') || (c > '{') || (c == '%') || (c == '\\')) {
+            // Illegal char.
+            state_ = kStateTopLevel;
+            EndCurrentBlock();
+            return false;
+          }
+          field_.c_name.name[i] = c;
+        }
+        // Make sure we are null terminated.
+        field_.c_name.name[i] = 0;
+        field_type_ = kRtcpSdesChunkCode;
+        found_c_name = true;
+      } else {
+        big_endian_reader.Skip(len);
+      }
+    }
+  }
+  // No end tag found!
+  state_ = kStateTopLevel;
+  EndCurrentBlock();
+  return false;
+}
+
+bool RtcpParser::ParseBye() {
+  rtcp_data_ += 4;  // Skip header.
+  state_ = kStateBye;
+  return ParseByeItem();
+}
+
+bool RtcpParser::ParseByeItem() {
+  ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
+  if (length < 4 || number_of_blocks_ == 0) {
+    state_ = kStateTopLevel;
+    EndCurrentBlock();
+    return false;
+  }
+
+  field_type_ = kRtcpByeCode;
+
+  net::BigEndianReader big_endian_reader(rtcp_data_, length);
+  big_endian_reader.ReadU32(&field_.bye.sender_ssrc);
+  rtcp_data_ += 4;
+
+  // We can have several CSRCs attached.
+  if (length >= 4 * number_of_blocks_) {
+    rtcp_data_ += (number_of_blocks_ - 1) * 4;
+  }
+  number_of_blocks_ = 0;
+  return true;
+}
+
+bool RtcpParser::ParseFeedBackCommon(const RtcpCommonHeader& header) {
+  DCHECK((header.PT == kPacketTypeGenericRtpFeedback) ||
+         (header.PT == kPacketTypePayloadSpecific)) << "Invalid state";
+
+  ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
+
+  if (length < 12) {  // 4 * 3, RFC4585 section 6.1
+    EndCurrentBlock();
+    return false;
+  }
+
+  uint32 sender_ssrc;
+  uint32 media_ssrc;
+  net::BigEndianReader big_endian_reader(rtcp_data_, length);
+  big_endian_reader.Skip(4);  // Skip header.
+  big_endian_reader.ReadU32(&sender_ssrc);
+  big_endian_reader.ReadU32(&media_ssrc);
+
+  rtcp_data_ += 12;
+
+  if (header.PT == kPacketTypeGenericRtpFeedback) {
+    // Transport layer feedback
+    switch (header.IC) {
+      case 1:
+        // Nack
+        field_type_ = kRtcpGenericRtpFeedbackNackCode;
+        field_.nack.sender_ssrc = sender_ssrc;
+        field_.nack.media_ssrc  = media_ssrc;
+        state_ = kStateGenericRtpFeedbackNack;
+        return true;
+      case 2:
+        // Used to be ACK is this code point, which is removed conficts with
+        // http://tools.ietf.org/html/draft-levin-avt-rtcp-burst-00
+        break;
+      case 3:
+        // Tmmbr
+        break;
+      case 4:
+        // Tmmbn
+        break;
+      case 5:
+        // RFC 6051 RTCP-sender_report-REQ Rapid Synchronisation of RTP Flows
+        // Trigger a new Rtcp sender_report
+        field_type_ = kRtcpGenericRtpFeedbackSrReqCode;
+
+        // Note: No state transition, sender report REQ is empty!
+        return true;
+      default:
+        break;
+    }
+    EndCurrentBlock();
+    return false;
+
+  } else if (header.PT == kPacketTypePayloadSpecific) {
+    // Payload specific feedback
+    switch (header.IC) {
+      case 1:
+        // PLI
+        field_type_ = kRtcpPayloadSpecificPliCode;
+        field_.pli.sender_ssrc = sender_ssrc;
+        field_.pli.media_ssrc  = media_ssrc;
+
+        // Note: No state transition, PLI FCI is empty!
+        return true;
+      case 2:
+        // Sli
+        break;
+      case 3:
+        field_type_ = kRtcpPayloadSpecificRpsiCode;
+        field_.rpsi.sender_ssrc = sender_ssrc;
+        field_.rpsi.media_ssrc  = media_ssrc;
+        state_ = kStatePayloadSpecificRpsi;
+        return true;
+      case 4:
+        // fir
+        break;
+      case 15:
+        field_type_ = kRtcpPayloadSpecificAppCode;
+        field_.application_specific.sender_ssrc = sender_ssrc;
+        field_.application_specific.media_ssrc  = media_ssrc;
+        state_ = kStatePayloadSpecificApplication;
+        return true;
+      default:
+        break;
+    }
+
+    EndCurrentBlock();
+    return false;
+  } else {
+    DCHECK(false) << "Invalid state";
+    EndCurrentBlock();
+    return false;
+  }
+}
+
+bool RtcpParser::ParseRpsiItem() {
+  // RFC 4585 6.3.3.  Reference Picture Selection Indication (rpsi)
+  /*
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |      PB       |0| Payload Type|    Native rpsi bit string     |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |   defined per codec          ...                | Padding (0) |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   */
+  ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
+
+  if (length < 4) {
+    state_ = kStateTopLevel;
+    EndCurrentBlock();
+    return false;
+  }
+  if (length > 2 + kRtcpRpsiDataSize) {
+    state_ = kStateTopLevel;
+    EndCurrentBlock();
+    return false;
+  }
+
+  field_type_ = kRtcpPayloadSpecificRpsiCode;
+
+  uint8 padding_bits;
+  net::BigEndianReader big_endian_reader(rtcp_data_, length);
+  big_endian_reader.ReadU8(&padding_bits);
+  big_endian_reader.ReadU8(&field_.rpsi.payload_type);
+  big_endian_reader.ReadBytes(&field_.rpsi.native_bit_string, length - 2);
+  field_.rpsi.number_of_valid_bits =
+      static_cast<uint16>(length - 2) * 8 - padding_bits;
+
+  rtcp_data_ += length;
+  return true;
+}
+
+bool RtcpParser::ParseNackItem() {
+  // RFC 4585 6.2.1. Generic Nack
+
+  ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
+  if (length < 4) {
+    state_ = kStateTopLevel;
+    EndCurrentBlock();
+    return false;
+  }
+
+  field_type_ = kRtcpGenericRtpFeedbackNackItemCode;
+
+  net::BigEndianReader big_endian_reader(rtcp_data_, length);
+  big_endian_reader.ReadU16(&field_.nack_item.packet_id);
+  big_endian_reader.ReadU16(&field_.nack_item.bitmask);
+  rtcp_data_ += 4;
+  return true;
+}
+
+bool RtcpParser::ParsePayloadSpecificAppItem() {
+  ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
+
+  if (length < 4) {
+    state_ = kStateTopLevel;
+    EndCurrentBlock();
+    return false;
+  }
+  uint32 name;
+  net::BigEndianReader big_endian_reader(rtcp_data_, length);
+  big_endian_reader.ReadU32(&name);
+  rtcp_data_ += 4;
+
+  if (name == kRemb) {
+    field_type_ = kRtcpPayloadSpecificRembCode;
+    state_ = kStatePayloadSpecificRemb;
+    return true;
+  } else if (name == kCast) {
+    field_type_ = kRtcpPayloadSpecificCastCode;
+    state_ = kStatePayloadSpecificCast;
+    return true;
+  }
+  state_ = kStateTopLevel;
+  EndCurrentBlock();
+  return false;
+}
+
+bool RtcpParser::ParsePayloadSpecificRembItem() {
+  ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
+
+  if (length < 4) {
+    state_ = kStateTopLevel;
+    EndCurrentBlock();
+    return false;
+  }
+
+  net::BigEndianReader big_endian_reader(rtcp_data_, length);
+  big_endian_reader.ReadU8(&field_.remb_item.number_of_ssrcs);
+
+  uint8 byte_1;
+  uint8 byte_2;
+  uint8 byte_3;
+  big_endian_reader.ReadU8(&byte_1);
+  big_endian_reader.ReadU8(&byte_2);
+  big_endian_reader.ReadU8(&byte_3);
+  rtcp_data_ += 4;
+
+  uint8 br_exp = (byte_1 >> 2) & 0x3F;
+  uint32 br_mantissa = ((byte_1 & 0x03) << 16) + (byte_2 << 8) + byte_3;
+  field_.remb_item.bitrate = (br_mantissa << br_exp);
+
+  ptrdiff_t length_ssrcs = rtcp_block_end_ - rtcp_data_;
+  if (length_ssrcs < 4 * field_.remb_item.number_of_ssrcs) {
+    state_ = kStateTopLevel;
+    EndCurrentBlock();
+    return false;
+  }
+
+  field_type_ = kRtcpPayloadSpecificRembItemCode;
+
+  for (int i = 0; i < field_.remb_item.number_of_ssrcs; i++) {
+    big_endian_reader.ReadU32(&field_.remb_item.ssrcs[i]);
+  }
+  return true;
+}
+
+bool RtcpParser::ParsePayloadSpecificCastItem() {
+  ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
+
+  if (length < 4) {
+    state_ = kStateTopLevel;
+    EndCurrentBlock();
+    return false;
+  }
+  field_type_ = kRtcpPayloadSpecificCastCode;
+
+  net::BigEndianReader big_endian_reader(rtcp_data_, length);
+  big_endian_reader.ReadU8(&field_.cast_item.last_frame_id);
+  big_endian_reader.ReadU8(&field_.cast_item.number_of_lost_fields);
+
+  rtcp_data_ += 4;
+
+  if (field_.cast_item.number_of_lost_fields != 0) {
+    // State transition
+    state_ = kStatePayloadSpecificCastNack;
+  } else {
+    // Don't go to state cast nack item if got 0 fields.
+    state_ = kStateTopLevel;
+    EndCurrentBlock();
+  }
+  return true;
+}
+
+bool RtcpParser::ParsePayloadSpecificCastNackItem() {
+  ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
+  if (length < 4) {
+    state_ = kStateTopLevel;
+    EndCurrentBlock();
+    return false;
+  }
+  field_type_ = kRtcpPayloadSpecificCastNackItemCode;
+
+  net::BigEndianReader big_endian_reader(rtcp_data_, length);
+  big_endian_reader.ReadU8(&field_.cast_nack_item.frame_id);
+  big_endian_reader.ReadU16(&field_.cast_nack_item.packet_id);
+  big_endian_reader.ReadU8(&field_.cast_nack_item.bitmask);
+
+  rtcp_data_ += 4;
+  return true;
+}
+
+bool RtcpParser::ParseFirItem() {
+  // RFC 5104 4.3.1. Full Intra Request (fir)
+  ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
+
+  if (length < 8) {
+    state_ = kStateTopLevel;
+    EndCurrentBlock();
+    return false;
+  }
+  field_type_ = kRtcpPayloadSpecificFirItemCode;
+
+  net::BigEndianReader big_endian_reader(rtcp_data_, length);
+  big_endian_reader.ReadU32(&field_.fir_item.ssrc);
+  big_endian_reader.ReadU8(&field_.fir_item.command_sequence_number);
+
+  rtcp_data_ += 8;
+  return true;
+}
+
+bool RtcpParser::ParseExtendedReport() {
+  ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
+  if (length < 8) return false;
+
+  field_type_ = kRtcpXrCode;
+
+  net::BigEndianReader big_endian_reader(rtcp_data_, length);
+  big_endian_reader.Skip(4);  // Skip header.
+  big_endian_reader.ReadU32(&field_.extended_report.sender_ssrc);
+
+  rtcp_data_ += 8;
+
+  state_ = kStateExtendedReportBlock;
+  return true;
+}
+
+bool RtcpParser::ParseExtendedReportItem() {
+  ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
+  if (length < 4) {
+    state_ = kStateTopLevel;
+    EndCurrentBlock();
+    return false;
+  }
+
+  uint8 block_type;
+  uint16 block_length;
+  net::BigEndianReader big_endian_reader(rtcp_data_, length);
+  big_endian_reader.ReadU8(&block_type);
+  big_endian_reader.Skip(1);  // Ignore reserved.
+  big_endian_reader.ReadU16(&block_length);
+
+  rtcp_data_ += 4;
+
+  switch (block_type) {
+    case 4:
+      if (block_length != 2) {
+        // Invalid block length.
+        state_ = kStateTopLevel;
+        EndCurrentBlock();
+        return false;
+      }
+      return ParseExtendedReportReceiverReferenceTimeReport();
+    case 5:
+      if (block_length % 3 != 0) {
+        // Invalid block length.
+        state_ = kStateTopLevel;
+        EndCurrentBlock();
+        return false;
+      }
+      if (block_length >= 3) {
+        number_of_blocks_ = block_length / 3;
+        state_ = kStateExtendedReportDelaySinceLastReceiverReport;
+        return ParseExtendedReportDelaySinceLastReceiverReport();
+      }
+      return true;
+    default:
+      if (length < block_length * 4) {
+        state_ = kStateTopLevel;
+        EndCurrentBlock();
+        return false;
+      }
+      field_type_ = kRtcpXrUnknownItemCode;
+      rtcp_data_ += block_length * 4;
+      return true;
+  }
+}
+
+bool RtcpParser::ParseExtendedReportReceiverReferenceTimeReport() {
+  ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
+  if (length < 8) {
+    state_ = kStateTopLevel;
+    EndCurrentBlock();
+    return false;
+  }
+
+  net::BigEndianReader big_endian_reader(rtcp_data_, length);
+  big_endian_reader.ReadU32(&field_.rrtr.ntp_most_significant);
+  big_endian_reader.ReadU32(&field_.rrtr.ntp_least_significant);
+
+  rtcp_data_ += 8;
+
+  field_type_ = kRtcpXrRrtrCode;
+  return true;
+}
+
+bool RtcpParser::ParseExtendedReportDelaySinceLastReceiverReport() {
+  ptrdiff_t length = rtcp_block_end_ - rtcp_data_;
+  if (length < 12) {
+    state_ = kStateTopLevel;
+    EndCurrentBlock();
+    return false;
+  }
+  if (number_of_blocks_ == 0) {
+    // Continue parsing the extended report block.
+    state_ = kStateExtendedReportBlock;
+    return false;
+  }
+
+  net::BigEndianReader big_endian_reader(rtcp_data_, length);
+  big_endian_reader.ReadU32(&field_.dlrr.receivers_ssrc);
+  big_endian_reader.ReadU32(&field_.dlrr.last_receiver_report);
+  big_endian_reader.ReadU32(&field_.dlrr.delay_last_receiver_report);
+
+  rtcp_data_ += 12;
+
+  number_of_blocks_--;
+  field_type_ = kRtcpXrDlrrCode;
+  return true;
+}
+
+}  // namespace cast
+}  // namespace media
diff --git a/media/cast/rtcp/rtcp_utility.h b/media/cast/rtcp/rtcp_utility.h
new file mode 100644
index 0000000..2df13e7
--- /dev/null
+++ b/media/cast/rtcp/rtcp_utility.h
@@ -0,0 +1,319 @@
+// 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 MEDIA_CAST_RTCP_RTCP_UTILITY_H_
+#define MEDIA_CAST_RTCP_RTCP_UTILITY_H_
+
+#include "media/cast/cast_config.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/rtcp/rtcp_defines.h"
+
+namespace media {
+namespace cast {
+
+static const int kRtcpRpsiDataSize = 30;
+static const int kRtcpCnameSize = 256;  // RFC 3550 page 44, including end null.
+static const int kRtcpMaxNumberOfRembFeedbackSsrcs = 255;
+
+static const uint32 kRemb = ('R' << 24) + ('E' << 16) + ('M' << 8) + 'B';
+static const uint32 kCast = ('C' << 24) + ('A' << 16) + ('S' << 8) + 'T';
+
+struct RtcpFieldReceiverReport {
+  // RFC 3550.
+  uint32 sender_ssrc;
+  uint8 number_of_report_blocks;
+};
+
+struct RtcpFieldSenderReport {
+  // RFC 3550.
+  uint32 sender_ssrc;
+  uint8  number_of_report_blocks;
+  uint32 ntp_most_significant;
+  uint32 ntp_least_significant;
+  uint32 rtp_timestamp;
+  uint32 sender_packet_count;
+  uint32 sender_octet_count;
+};
+
+struct RtcpFieldReportBlockItem {
+  // RFC 3550.
+  uint32 ssrc;
+  uint8  fraction_lost;
+  uint32 cumulative_number_of_packets_lost;
+  uint32 extended_highest_sequence_number;
+  uint32 jitter;
+  uint32 last_sender_report;
+  uint32 delay_last_sender_report;
+};
+
+struct RtcpFieldSdesCName {
+  // RFC 3550
+  uint32 sender_ssrc;
+  char name[kRtcpCnameSize];
+};
+
+struct RtcpFieldBye {
+  // RFC 3550.
+  uint32 sender_ssrc;
+};
+
+struct RtcpFieldGenericRtpFeedbackNack {
+  // RFC 4585.
+  uint32 sender_ssrc;
+  uint32 media_ssrc;
+};
+
+struct RtcpFieldGenericRtpFeedbackNackItem {
+  // RFC 4585.
+  uint16 packet_id;
+  uint16 bitmask;
+};
+
+struct RtcpFieldPayloadSpecificFir {
+  // RFC 5104.
+  uint32 sender_ssrc;
+  uint32 media_ssrc;  // zero!
+};
+
+struct RtcpFieldPayloadSpecificFirItem {
+  // RFC 5104.
+  uint32 ssrc;
+  uint8 command_sequence_number;
+};
+
+struct RtcpFieldPayloadSpecificPli {
+  // RFC 4585.
+  uint32 sender_ssrc;
+  uint32 media_ssrc;
+};
+
+struct RtcpFieldPayloadSpecificRpsi {
+  // RFC 4585.
+  uint32 sender_ssrc;
+  uint32 media_ssrc;
+  uint8  payload_type;
+  uint16 number_of_valid_bits;
+  uint8  native_bit_string[kRtcpRpsiDataSize];
+};
+
+struct RtcpFieldXr {
+  // RFC 3611.
+  uint32 sender_ssrc;
+};
+
+struct RtcpFieldXrRrtr {
+  // RFC 3611.
+  uint32 ntp_most_significant;
+  uint32 ntp_least_significant;
+};
+
+struct RtcpFieldXrDlrr {
+  // RFC 3611.
+  uint32 receivers_ssrc;
+  uint32 last_receiver_report;
+  uint32 delay_last_receiver_report;
+};
+
+struct RtcpFieldPayloadSpecificApplication {
+  uint32 sender_ssrc;
+  uint32 media_ssrc;
+};
+
+struct RtcpFieldPayloadSpecificRembItem {
+  uint32 bitrate;
+  uint8 number_of_ssrcs;
+  uint32 ssrcs[kRtcpMaxNumberOfRembFeedbackSsrcs];
+};
+
+struct RtcpFieldPayloadSpecificCastItem {
+  uint8 last_frame_id;
+  uint8 number_of_lost_fields;
+};
+
+struct RtcpFieldPayloadSpecificCastNackItem {
+  uint8 frame_id;
+  uint16 packet_id;
+  uint8 bitmask;
+};
+
+union RtcpField {
+  RtcpFieldReceiverReport               receiver_report;
+  RtcpFieldSenderReport                 sender_report;
+  RtcpFieldReportBlockItem              report_block_item;
+  RtcpFieldSdesCName                    c_name;
+  RtcpFieldBye                          bye;
+
+  RtcpFieldXr                           extended_report;
+  RtcpFieldXrRrtr                       rrtr;
+  RtcpFieldXrDlrr                       dlrr;
+
+  RtcpFieldGenericRtpFeedbackNack       nack;
+  RtcpFieldGenericRtpFeedbackNackItem   nack_item;
+
+  RtcpFieldPayloadSpecificPli           pli;
+  RtcpFieldPayloadSpecificRpsi          rpsi;
+  RtcpFieldPayloadSpecificFir           fir;
+  RtcpFieldPayloadSpecificFirItem       fir_item;
+  RtcpFieldPayloadSpecificApplication   application_specific;
+  RtcpFieldPayloadSpecificRembItem      remb_item;
+  RtcpFieldPayloadSpecificCastItem      cast_item;
+  RtcpFieldPayloadSpecificCastNackItem  cast_nack_item;
+};
+
+enum RtcpFieldTypes {
+  kRtcpNotValidCode,
+
+  // RFC 3550.
+  kRtcpRrCode,
+  kRtcpSrCode,
+  kRtcpReportBlockItemCode,
+
+  kRtcpSdesCode,
+  kRtcpSdesChunkCode,
+  kRtcpByeCode,
+
+  // RFC 3611.
+  kRtcpXrCode,
+  kRtcpXrRrtrCode,
+  kRtcpXrDlrrCode,
+  kRtcpXrUnknownItemCode,
+
+  // RFC 4585.
+  kRtcpGenericRtpFeedbackNackCode,
+  kRtcpGenericRtpFeedbackNackItemCode,
+
+  kRtcpPayloadSpecificPliCode,
+  kRtcpPayloadSpecificRpsiCode,
+  kRtcpPayloadSpecificAppCode,
+
+  kRtcpPayloadSpecificRembCode,
+  kRtcpPayloadSpecificRembItemCode,
+  kRtcpPayloadSpecificCastCode,
+  kRtcpPayloadSpecificCastNackItemCode,
+
+  // RFC 5104.
+  kRtcpPayloadSpecificFirCode,
+  kRtcpPayloadSpecificFirItemCode,
+
+  // RFC 6051.
+  kRtcpGenericRtpFeedbackSrReqCode,
+};
+
+struct RtcpCommonHeader {
+  uint8  V;   // Version.
+  bool   P;   // Padding.
+  uint8  IC;  // Item count / subtype.
+  uint8  PT;  // Packet Type.
+  uint16 length_in_octets;
+};
+
+enum RtcpPacketTypes {
+  kPacketTypeLow = 194,  // SMPTE time-code mapping.
+  kPacketTypeInterArrivalJitterReport = 195,
+  kPacketTypeSenderReport = 200,
+  kPacketTypeReceiverReport = 201,
+  kPacketTypeSdes= 202,
+  kPacketTypeBye = 203,
+  kPacketTypeApplicationDefined = 204,
+  kPacketTypeGenericRtpFeedback = 205,
+  kPacketTypePayloadSpecific  = 206,
+  kPacketTypeXr = 207,
+  kPacketTypeHigh = 210,  // Port Mapping.
+};
+
+class RtcpParser {
+ public:
+  RtcpParser(const uint8* rtcp_data, size_t rtcp_length);
+  ~RtcpParser();
+
+  RtcpFieldTypes FieldType() const;
+  const RtcpField& Field() const;
+
+  bool IsValid() const;
+
+  RtcpFieldTypes Begin();
+  RtcpFieldTypes Iterate();
+
+ private:
+  enum ParseState {
+    kStateTopLevel,     // Top level packet
+    kStateReportBlock,  // Sender/Receiver report report blocks.
+    kStateSdes,
+    kStateBye,
+    kStateExtendedReportBlock,
+    kStateExtendedReportDelaySinceLastReceiverReport,
+    kStateGenericRtpFeedbackNack,
+    kStatePayloadSpecificRpsi,
+    kStatePayloadSpecificFir,
+    kStatePayloadSpecificApplication,
+    kStatePayloadSpecificRemb,      // Application specific Remb.
+    kStatePayloadSpecificCast,      // Application specific Cast.
+    kStatePayloadSpecificCastNack,  // Application specific Nack for Cast.
+  };
+
+  bool RtcpParseCommonHeader(const uint8* begin,
+                             const uint8* end,
+                             RtcpCommonHeader* parsed_header) const;
+
+  void IterateTopLevel();
+  void IterateReportBlockItem();
+  void IterateSdesItem();
+  void IterateByeItem();
+  void IterateExtendedReportItem();
+  void IterateExtendedReportDelaySinceLastReceiverReportItem();
+  void IterateNackItem();
+  void IterateRpsiItem();
+  void IterateFirItem();
+  void IteratePayloadSpecificAppItem();
+  void IteratePayloadSpecificRembItem();
+  void IteratePayloadSpecificCastItem();
+  void IteratePayloadSpecificCastNackItem();
+
+  void Validate();
+  void EndCurrentBlock();
+
+  bool ParseRR();
+  bool ParseSR();
+  bool ParseReportBlockItem();
+
+  bool ParseSdes();
+  bool ParseSdesItem();
+  bool ParseSdesTypes();
+  bool ParseBye();
+  bool ParseByeItem();
+
+  bool ParseExtendedReport();
+  bool ParseExtendedReportItem();
+  bool ParseExtendedReportReceiverReferenceTimeReport();
+  bool ParseExtendedReportDelaySinceLastReceiverReport();
+
+  bool ParseFeedBackCommon(const RtcpCommonHeader& header);
+  bool ParseNackItem();
+  bool ParseRpsiItem();
+  bool ParseFirItem();
+  bool ParsePayloadSpecificAppItem();
+  bool ParsePayloadSpecificRembItem();
+  bool ParsePayloadSpecificCastItem();
+  bool ParsePayloadSpecificCastNackItem();
+
+ private:
+  const uint8* const rtcp_data_begin_;
+  const uint8* const rtcp_data_end_;
+
+  bool valid_packet_;
+  const uint8* rtcp_data_;
+  const uint8* rtcp_block_end_;
+
+  ParseState state_;
+  uint8 number_of_blocks_;
+  RtcpFieldTypes field_type_;
+  RtcpField field_;
+
+  DISALLOW_COPY_AND_ASSIGN(RtcpParser);
+};
+
+}  // namespace cast
+}  // namespace media
+
+#endif  // MEDIA_CAST_RTCP_RTCP_UTILITY_H_
diff --git a/media/cast/rtcp/test_rtcp_packet_builder.cc b/media/cast/rtcp/test_rtcp_packet_builder.cc
new file mode 100644
index 0000000..d6468e5
--- /dev/null
+++ b/media/cast/rtcp/test_rtcp_packet_builder.cc
@@ -0,0 +1,230 @@
+// 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 "media/cast/rtcp/test_rtcp_packet_builder.h"
+
+#include "base/logging.h"
+
+namespace media {
+namespace cast {
+
+TestRtcpPacketBuilder::TestRtcpPacketBuilder()
+    : ptr_of_length_(NULL),
+      big_endian_writer_(buffer_, kIpPacketSize) {
+}
+
+void TestRtcpPacketBuilder::AddSr(uint32 sender_ssrc,
+                                  int number_of_report_blocks) {
+  AddRtcpHeader(200, number_of_report_blocks);
+  big_endian_writer_.WriteU32(sender_ssrc);
+  big_endian_writer_.WriteU32(kNtpHigh);  // NTP timestamp.
+  big_endian_writer_.WriteU32(kNtpLow);
+  big_endian_writer_.WriteU32(kRtpTimestamp);
+  big_endian_writer_.WriteU32(kSendPacketCount);
+  big_endian_writer_.WriteU32(kSendOctetCount);
+}
+
+void TestRtcpPacketBuilder::AddRr(uint32 sender_ssrc,
+                                  int number_of_report_blocks) {
+  AddRtcpHeader(201, number_of_report_blocks);
+  big_endian_writer_.WriteU32(sender_ssrc);
+}
+
+void TestRtcpPacketBuilder::AddRb(uint32 rtp_ssrc) {
+  big_endian_writer_.WriteU32(rtp_ssrc);
+  big_endian_writer_.WriteU32(kLoss);
+  big_endian_writer_.WriteU32(kExtendedMax);
+  big_endian_writer_.WriteU32(kJitter);
+  big_endian_writer_.WriteU32(kLastSr);
+  big_endian_writer_.WriteU32(kDelayLastSr);
+}
+
+void TestRtcpPacketBuilder::AddSdesCname(uint32 sender_ssrc,
+                                         const std::string& c_name) {
+  AddRtcpHeader(202, 1);
+  big_endian_writer_.WriteU32(sender_ssrc);
+  big_endian_writer_.WriteU8(1);  // c_name.
+  big_endian_writer_.WriteU8(c_name.size());  // c_name length in bytes.
+  for (size_t i = 0; i < c_name.size(); ++i) {
+    big_endian_writer_.WriteU8(c_name.c_str()[i]);
+  }
+  int padding;
+  switch (c_name.size() % 4) {
+    case 0:
+      padding = 2;
+      break;
+    case 1:
+      padding = 1;
+      break;
+    case 2:
+      padding = 4;
+      break;
+    case 3:
+      padding = 3;
+      break;
+  }
+  for (int j = 0; j < padding; ++j) {
+    big_endian_writer_.WriteU8(0);
+  }
+}
+
+void TestRtcpPacketBuilder::AddXrHeader(uint32 sender_ssrc) {
+  AddRtcpHeader(207, 0);
+  big_endian_writer_.WriteU32(sender_ssrc);
+}
+
+void TestRtcpPacketBuilder::AddXrUnknownBlock() {
+  big_endian_writer_.WriteU8(9);  // Block type.
+  big_endian_writer_.WriteU8(0);  // Reserved.
+  big_endian_writer_.WriteU16(4);  // Block length.
+  // First receiver same as sender of this report.
+  big_endian_writer_.WriteU32(0);
+  big_endian_writer_.WriteU32(0);
+  big_endian_writer_.WriteU32(0);
+  big_endian_writer_.WriteU32(0);
+}
+
+void TestRtcpPacketBuilder::AddXrDlrrBlock(uint32 sender_ssrc) {
+  big_endian_writer_.WriteU8(5);  // Block type.
+  big_endian_writer_.WriteU8(0);  // Reserved.
+  big_endian_writer_.WriteU16(3);  // Block length.
+
+  // First receiver same as sender of this report.
+  big_endian_writer_.WriteU32(sender_ssrc);
+  big_endian_writer_.WriteU32(kLastRr);
+  big_endian_writer_.WriteU32(kDelayLastRr);
+}
+
+void TestRtcpPacketBuilder::AddXrExtendedDlrrBlock(uint32 sender_ssrc) {
+  big_endian_writer_.WriteU8(5);  // Block type.
+  big_endian_writer_.WriteU8(0);  // Reserved.
+  big_endian_writer_.WriteU16(9);  // Block length.
+  big_endian_writer_.WriteU32(0xaaaaaaaa);
+  big_endian_writer_.WriteU32(0xaaaaaaaa);
+  big_endian_writer_.WriteU32(0xaaaaaaaa);
+
+  // First receiver same as sender of this report.
+  big_endian_writer_.WriteU32(sender_ssrc);
+  big_endian_writer_.WriteU32(kLastRr);
+  big_endian_writer_.WriteU32(kDelayLastRr);
+  big_endian_writer_.WriteU32(0xbbbbbbbb);
+  big_endian_writer_.WriteU32(0xbbbbbbbb);
+  big_endian_writer_.WriteU32(0xbbbbbbbb);
+}
+
+void TestRtcpPacketBuilder::AddXrRrtrBlock() {
+  big_endian_writer_.WriteU8(4);  // Block type.
+  big_endian_writer_.WriteU8(0);  // Reserved.
+  big_endian_writer_.WriteU16(2);  // Block length.
+  big_endian_writer_.WriteU32(kNtpHigh);
+  big_endian_writer_.WriteU32(kNtpLow);
+}
+
+void TestRtcpPacketBuilder::AddNack(uint32 sender_ssrc, uint32 media_ssrc) {
+  AddRtcpHeader(205, 1);
+  big_endian_writer_.WriteU32(sender_ssrc);
+  big_endian_writer_.WriteU32(media_ssrc);
+  big_endian_writer_.WriteU16(kMissingPacket);
+  big_endian_writer_.WriteU16(0);
+}
+
+void TestRtcpPacketBuilder::AddSendReportRequest(uint32 sender_ssrc,
+                                                 uint32 media_ssrc) {
+  AddRtcpHeader(205, 5);
+  big_endian_writer_.WriteU32(sender_ssrc);
+  big_endian_writer_.WriteU32(media_ssrc);
+}
+
+void TestRtcpPacketBuilder::AddPli(uint32 sender_ssrc, uint32 media_ssrc) {
+  AddRtcpHeader(206, 1);
+  big_endian_writer_.WriteU32(sender_ssrc);
+  big_endian_writer_.WriteU32(media_ssrc);
+}
+
+void TestRtcpPacketBuilder::AddRpsi(uint32 sender_ssrc, uint32 media_ssrc) {
+  AddRtcpHeader(206, 3);
+  big_endian_writer_.WriteU32(sender_ssrc);
+  big_endian_writer_.WriteU32(media_ssrc);
+  big_endian_writer_.WriteU8(0);  // Padding bits.
+  big_endian_writer_.WriteU8(kPayloadtype);
+  uint64 picture_id = kPictureId;
+
+  for (int i = 9; i > 0; i--) {
+    big_endian_writer_.WriteU8(
+        0x80 | static_cast<uint8>(picture_id >> (i * 7)));
+  }
+  // Add last byte of picture ID.
+  big_endian_writer_.WriteU8(static_cast<uint8>(picture_id & 0x7f));
+}
+
+void TestRtcpPacketBuilder::AddRemb(uint32 sender_ssrc, uint32 media_ssrc) {
+  AddRtcpHeader(206, 15);
+  big_endian_writer_.WriteU32(sender_ssrc);
+  big_endian_writer_.WriteU32(0);
+  big_endian_writer_.WriteU8('R');
+  big_endian_writer_.WriteU8('E');
+  big_endian_writer_.WriteU8('M');
+  big_endian_writer_.WriteU8('B');
+  big_endian_writer_.WriteU8(1);  // Number of SSRCs.
+  big_endian_writer_.WriteU8(1);  // BR Exp.
+  //  BR Mantissa.
+  big_endian_writer_.WriteU16(static_cast<uint16>(kRembBitrate / 2));
+  big_endian_writer_.WriteU32(media_ssrc);
+}
+
+void TestRtcpPacketBuilder::AddCast(uint32 sender_ssrc, uint32 media_ssrc) {
+  AddRtcpHeader(206, 15);
+  big_endian_writer_.WriteU32(sender_ssrc);
+  big_endian_writer_.WriteU32(media_ssrc);
+  big_endian_writer_.WriteU8('C');
+  big_endian_writer_.WriteU8('A');
+  big_endian_writer_.WriteU8('S');
+  big_endian_writer_.WriteU8('T');
+  big_endian_writer_.WriteU8(kAckFrameId);
+  big_endian_writer_.WriteU8(3);  // Loss fields.
+  big_endian_writer_.WriteU16(0);  // Reserved.
+  big_endian_writer_.WriteU8(kLostFrameId);
+  big_endian_writer_.WriteU16(kRtcpCastAllPacketsLost);
+  big_endian_writer_.WriteU8(0);  // Lost packet id mask.
+  big_endian_writer_.WriteU8(kFrameIdWithLostPackets);
+  big_endian_writer_.WriteU16(kLostPacketId1);
+  big_endian_writer_.WriteU8(0x2);  // Lost packet id mask.
+  big_endian_writer_.WriteU8(kFrameIdWithLostPackets);
+  big_endian_writer_.WriteU16(kLostPacketId3);
+  big_endian_writer_.WriteU8(0);  // Lost packet id mask.
+}
+
+const uint8* TestRtcpPacketBuilder::Packet() {
+  PatchLengthField();
+  return buffer_;
+}
+
+void TestRtcpPacketBuilder::PatchLengthField() {
+  if (ptr_of_length_) {
+    // Back-patch the packet length. The client must have taken
+    // care of proper padding to 32-bit words.
+    int this_packet_length = (big_endian_writer_.ptr() - ptr_of_length_ - 2);
+    DCHECK_EQ(0, this_packet_length % 4)
+        << "Packets must be a multiple of 32 bits long";
+    *ptr_of_length_ = this_packet_length >> 10;
+    *(ptr_of_length_ + 1) = (this_packet_length >> 2) & 0xFF;
+    ptr_of_length_ = NULL;
+  }
+}
+
+// Set the 5-bit value in the 1st byte of the header
+// and the payload type. Set aside room for the length field,
+// and make provision for back-patching it.
+void TestRtcpPacketBuilder::AddRtcpHeader(int payload, int format_or_count) {
+  PatchLengthField();
+  big_endian_writer_.WriteU8(0x80 | (format_or_count & 0x1F));
+  big_endian_writer_.WriteU8(payload);
+  ptr_of_length_ = big_endian_writer_.ptr();
+
+  // Initialize length to "clearly illegal".
+  big_endian_writer_.WriteU16(0xDEAD);
+}
+
+}  // namespace cast
+}  // namespace media
diff --git a/media/cast/rtcp/test_rtcp_packet_builder.h b/media/cast/rtcp/test_rtcp_packet_builder.h
new file mode 100644
index 0000000..be93f0a
--- /dev/null
+++ b/media/cast/rtcp/test_rtcp_packet_builder.h
@@ -0,0 +1,94 @@
+// 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.
+
+// A very simple packet builder class for building RTCP packets.
+// Used for testing only.
+#ifndef MEDIA_CAST_RTCP_TEST_RTCP_PACKET_BUILDER_H_
+#define MEDIA_CAST_RTCP_TEST_RTCP_PACKET_BUILDER_H_
+
+#include "media/cast/rtcp/rtcp_defines.h"
+#include "net/base/big_endian.h"
+
+namespace media {
+namespace cast {
+
+// Sender report.
+static const int kNtpHigh = 0x01020304;
+static const int kNtpLow = 0x05060708;
+static const int kRtpTimestamp = 0x10203;
+static const int kSendPacketCount = 987;
+static const int kSendOctetCount = 87654;
+
+// Report block.
+static const int kLoss = 0x01000123;
+static const int kExtendedMax = 0x15678;
+static const int kJitter = 0x10203;
+static const int kLastSr = 0x34561234;
+static const int kDelayLastSr = 1000;
+
+// DLRR block.
+static const int kLastRr = 0x34561234;
+static const int kDelayLastRr = 1000;
+
+// REMB.
+static const int kRembBitrate = 524286;
+
+// RPSI.
+static const int kPayloadtype = 126;
+static const uint64 kPictureId = 0x1234567890;
+
+// NACK.
+static const int kMissingPacket = 34567;
+
+// CAST.
+static const int kAckFrameId = 17;
+static const int kLostFrameId = 18;
+static const int kFrameIdWithLostPackets = 19;
+static const int kLostPacketId1 = 3;
+static const int kLostPacketId2 = 5;
+static const int kLostPacketId3 = 12;
+
+class TestRtcpPacketBuilder {
+ public:
+  TestRtcpPacketBuilder();
+
+  void AddSr(uint32 sender_ssrc, int number_of_report_blocks);
+  void AddRr(uint32 sender_ssrc, int number_of_report_blocks);
+  void AddRb(uint32 rtp_ssrc);
+  void AddSdesCname(uint32 sender_ssrc, const std::string& c_name);
+
+  void AddXrHeader(uint32 sender_ssrc);
+  void AddXrDlrrBlock(uint32 sender_ssrc);
+  void AddXrExtendedDlrrBlock(uint32 sender_ssrc);
+  void AddXrRrtrBlock();
+  void AddXrUnknownBlock();
+
+  void AddNack(uint32 sender_ssrc, uint32 media_ssrc);
+  void AddSendReportRequest(uint32 sender_ssrc, uint32 media_ssrc);
+
+  void AddPli(uint32 sender_ssrc, uint32 media_ssrc);
+  void AddRpsi(uint32 sender_ssrc, uint32 media_ssrc);
+  void AddRemb(uint32 sender_ssrc, uint32 media_ssrc);
+  void AddCast(uint32 sender_ssrc, uint32 media_ssrc);
+
+  const uint8* Packet();
+  int Length() { return kIpPacketSize - big_endian_writer_.remaining(); }
+
+ private:
+  void AddRtcpHeader(int payload, int format_or_count);
+  void PatchLengthField();
+
+  // Where the length field of the current packet is.
+  // Note: 0 is not a legal value, it is used for "uninitialized".
+  uint8 buffer_[kIpPacketSize];
+  char* ptr_of_length_;
+  net::BigEndianWriter big_endian_writer_;
+};
+
+}  // namespace cast
+}  // namespace media
+
+#endif //  MEDIA_CAST_RTCP_TEST_RTCP_PACKET_BUILDER_H_
+
+
diff --git a/media/cast/rtp_common/mock_rtp_payload_feedback.h b/media/cast/rtp_common/mock_rtp_payload_feedback.h
new file mode 100644
index 0000000..00ba183
--- /dev/null
+++ b/media/cast/rtp_common/mock_rtp_payload_feedback.h
@@ -0,0 +1,25 @@
+// 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 MEDIA_CAST_RTP_COMMON_MOCK_RTP_PAYLOAD_FEEDBACK_H_
+#define MEDIA_CAST_RTP_COMMON_MOCK_RTP_PAYLOAD_FEEDBACK_H_
+
+#include "media/cast/rtp_common/rtp_defines.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+namespace cast {
+
+class MockRtpPayloadFeedback : public RtpPayloadFeedback {
+ public:
+  MOCK_METHOD1(CastFeedback,
+               void(const RtcpCastMessage& cast_feedback));
+
+  MOCK_METHOD0(RequestKeyFrame, void());
+};
+
+}  // namespace cast
+}  // namespace media
+
+#endif  // MEDIA_CAST_RTP_COMMON_MOCK_RTP_PAYLOAD_FEEDBACK_H_
diff --git a/media/cast/rtp_common/rtp_defines.h b/media/cast/rtp_common/rtp_defines.h
new file mode 100644
index 0000000..89ee019
--- /dev/null
+++ b/media/cast/rtp_common/rtp_defines.h
@@ -0,0 +1,71 @@
+// 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 MEDIA_CAST_RTP_COMMON_RTP_DEFINES_H_
+#define MEDIA_CAST_RTP_COMMON_RTP_DEFINES_H_
+
+#include <cstring>
+
+#include "base/basictypes.h"
+#include "media/cast/cast_config.h"
+#include "media/cast/rtcp/rtcp_defines.h"
+#include "third_party/webrtc/modules/interface/module_common_types.h"
+
+namespace media {
+namespace cast {
+
+const uint8 kRtpMarkerBitMask = 0x80;
+
+struct RtpCastHeader {
+  void InitRTPVideoHeaderCast() {
+    is_key_frame = false;
+    frame_id = 0;
+    packet_id = 0;
+    max_packet_id = 0;
+    is_reference = false;
+    reference_frame_id = 0;
+  }
+  webrtc::WebRtcRTPHeader webrtc;
+  bool is_key_frame;
+  uint8 frame_id;
+  uint16 packet_id;
+  uint16 max_packet_id;
+  bool is_reference;  // Set to true if the previous frame is not available,
+                      // and the reference frame id  is available.
+  uint8 reference_frame_id;
+};
+
+class RtpPayloadFeedback {
+ public:
+  virtual void CastFeedback(const RtcpCastMessage& cast_feedback) = 0;
+  virtual void RequestKeyFrame() = 0;  // TODO(pwestin): can we remove this?
+
+ protected:
+  virtual ~RtpPayloadFeedback() {}
+};
+
+inline bool IsNewerFrameId(uint8 frame_id, uint8 prev_frame_id) {
+  return (frame_id != prev_frame_id) &&
+      static_cast<uint8>(frame_id - prev_frame_id) < 0x80;
+}
+
+inline bool IsOlderFrameId(uint8 frame_id, uint8 prev_frame_id) {
+  return (frame_id == prev_frame_id) || IsNewerFrameId(prev_frame_id, frame_id);
+}
+
+inline bool IsNewerPacketId(uint16 packet_id, uint16 prev_packet_id) {
+  return (packet_id != prev_packet_id) &&
+      static_cast<uint16>(packet_id - prev_packet_id) < 0x8000;
+}
+
+inline bool IsNewerSequenceNumber(uint16 sequence_number,
+                                  uint16 prev_sequence_number) {
+  // Same function as IsNewerPacketId just different data and name.
+  return IsNewerPacketId(sequence_number, prev_sequence_number);
+}
+
+}  // namespace cast
+}  // namespace media
+
+#endif  // MEDIA_CAST_RTP_COMMON_RTP_DEFINES_H_
diff --git a/media/cast/rtp_sender/mock_rtp_sender.h b/media/cast/rtp_sender/mock_rtp_sender.h
new file mode 100644
index 0000000..334bc88
--- /dev/null
+++ b/media/cast/rtp_sender/mock_rtp_sender.h
@@ -0,0 +1,34 @@
+// 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 MEDIA_CAST_RTP_SENDER_MOCK_RTP_SENDER_H_
+#define MEDIA_CAST_RTP_SENDER_MOCK_RTP_SENDER_H_
+
+#include <vector>
+
+#include "media/cast/rtp_sender/rtp_sender.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+namespace cast {
+
+class MockRtpSender : public RtpSender {
+ public:
+  MOCK_METHOD2(IncomingEncodedVideoFrame,
+               bool(const EncodedVideoFrame& frame, int64 capture_time));
+
+  MOCK_METHOD2(IncomingEncodedAudioFrame,
+               bool(const EncodedAudioFrame& frame, int64 recorded_time));
+
+  MOCK_METHOD3(ResendPacket,
+               bool(bool is_audio, uint8 frame_id, uint16 packet_id));
+
+  MOCK_METHOD0(RtpStatistics, void());
+};
+
+}  // namespace cast
+}  // namespace media
+
+#endif  // MEDIA_CAST_RTP_SENDER_MOCK_RTP_SENDER_H_
+
diff --git a/media/cast/rtp_sender/packet_storage/packet_storage.cc b/media/cast/rtp_sender/packet_storage/packet_storage.cc
new file mode 100644
index 0000000..9c2d7ff
--- /dev/null
+++ b/media/cast/rtp_sender/packet_storage/packet_storage.cc
@@ -0,0 +1,141 @@
+// 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 "media/cast/rtp_sender/packet_storage/packet_storage.h"
+
+#include <string>
+
+#include "base/logging.h"
+#include "media/cast/cast_config.h"
+#include "media/cast/cast_defines.h"
+
+namespace media {
+namespace cast {
+
+// Limit the max time delay to avoid frame id wrap around; 256 / 60 fps.
+const int kMaxAllowedTimeStoredMs = 4000;
+
+typedef PacketMap::iterator PacketMapIterator;
+typedef TimeToPacketMap::iterator TimeToPacketIterator;
+
+class StoredPacket {
+ public:
+  StoredPacket() {
+    packet_.reserve(kIpPacketSize);
+  }
+
+  void Save(const std::vector<uint8>& packet) {
+    DCHECK_LT(packet.size(), kIpPacketSize) << "Invalid argument";
+    packet_.clear();
+    packet_.insert(packet_.begin(), packet.begin(), packet.end());
+  }
+
+  void GetCopy(std::vector<uint8>* packet) {
+    packet->insert(packet->begin(), packet_.begin(), packet_.end());
+  }
+
+ private:
+  std::vector<uint8> packet_;
+};
+
+
+PacketStorage::PacketStorage(int max_time_stored_ms)
+    : default_tick_clock_(new base::DefaultTickClock()),
+      clock_(default_tick_clock_.get()) {
+  max_time_stored_ = base::TimeDelta::FromMilliseconds(max_time_stored_ms);
+  DCHECK_LE(max_time_stored_ms, kMaxAllowedTimeStoredMs) << "Invalid argument";
+}
+
+PacketStorage::~PacketStorage() {
+  time_to_packet_map_.clear();
+
+  PacketMapIterator store_it = stored_packets_.begin();
+  for (; store_it != stored_packets_.end();
+      store_it = stored_packets_.begin()) {
+    stored_packets_.erase(store_it);
+  }
+  while (!free_packets_.empty()) {
+    free_packets_.pop_front();
+  }
+}
+
+void PacketStorage::CleanupOldPackets(base::TimeTicks now) {
+  TimeToPacketIterator time_it = time_to_packet_map_.begin();
+
+  // Check max size.
+  while (time_to_packet_map_.size() >= kMaxStoredPackets) {
+    PacketMapIterator store_it = stored_packets_.find(time_it->second);
+
+    // We should always find the packet.
+    DCHECK(store_it != stored_packets_.end()) << "Invalid state";
+    time_to_packet_map_.erase(time_it);
+    // Save the pointer.
+    linked_ptr<StoredPacket> storted_packet = store_it->second;
+    stored_packets_.erase(store_it);
+    // Add this packet to the free list for later re-use.
+    free_packets_.push_back(storted_packet);
+    time_it = time_to_packet_map_.begin();
+  }
+
+  // Time out old packets.
+  while (time_it != time_to_packet_map_.end()) {
+    if (now < time_it->first + max_time_stored_) {
+      break;
+    }
+    // Packet too old.
+    PacketMapIterator store_it = stored_packets_.find(time_it->second);
+
+    // We should always find the packet.
+    DCHECK(store_it != stored_packets_.end()) << "Invalid state";
+    time_to_packet_map_.erase(time_it);
+    // Save the pointer.
+    linked_ptr<StoredPacket> storted_packet = store_it->second;
+    stored_packets_.erase(store_it);
+    // Add this packet to the free list for later re-use.
+    free_packets_.push_back(storted_packet);
+    time_it = time_to_packet_map_.begin();
+  }
+}
+
+void PacketStorage::StorePacket(uint8 frame_id,
+                                uint16 packet_id,
+                                const std::vector<uint8>& packet) {
+  base::TimeTicks now = clock_->NowTicks();
+  CleanupOldPackets(now);
+
+  uint32 index = (static_cast<uint32>(frame_id) << 16) + packet_id;
+  PacketMapIterator it = stored_packets_.find(index);
+  if (it != stored_packets_.end()) {
+    // We have already saved this.
+    DCHECK(false) << "Invalid state";
+    return;
+  }
+  linked_ptr<StoredPacket> stored_packet;
+  if (free_packets_.empty()) {
+    // No previous allocated packets allocate one.
+    stored_packet.reset(new StoredPacket());
+  } else {
+    // Re-use previous allocated packet.
+    stored_packet = free_packets_.front();
+    free_packets_.pop_front();
+  }
+  stored_packet->Save(packet);
+  stored_packets_[index] = stored_packet;
+  time_to_packet_map_.insert(std::make_pair(now, index));
+}
+
+bool PacketStorage::GetPacket(uint8 frame_id,
+                              uint16 packet_id,
+                              std::vector<uint8>* packet) {
+  uint32 index = (static_cast<uint32>(frame_id) << 16) + packet_id;
+  PacketMapIterator it = stored_packets_.find(index);
+  if (it == stored_packets_.end()) {
+    return false;
+  }
+  it->second->GetCopy(packet);
+  return true;
+}
+
+}  // namespace cast
+}  // namespace media
diff --git a/media/cast/rtp_sender/packet_storage/packet_storage.gypi b/media/cast/rtp_sender/packet_storage/packet_storage.gypi
new file mode 100644
index 0000000..f691d9e
--- /dev/null
+++ b/media/cast/rtp_sender/packet_storage/packet_storage.gypi
@@ -0,0 +1,23 @@
+# 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.
+
+{
+  'targets': [
+    {
+      'target_name': 'packet_storage',
+      'type': 'static_library',
+      'include_dirs': [
+        '<(DEPTH)/',
+      ],
+      'sources': [
+        'packet_storage.h',
+        'packet_storage.cc',
+      ], # source
+      'dependencies': [
+        '<(DEPTH)/base/base.gyp:base',
+      ],
+    },
+  ],
+}
+
diff --git a/media/cast/rtp_sender/packet_storage/packet_storage.h b/media/cast/rtp_sender/packet_storage/packet_storage.h
new file mode 100644
index 0000000..e1e3bcb
--- /dev/null
+++ b/media/cast/rtp_sender/packet_storage/packet_storage.h
@@ -0,0 +1,59 @@
+// 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 MEDIA_CAST_RTP_SENDER_PACKET_STORAGE_INCLUDE_PACKET_STORAGE_H_
+#define MEDIA_CAST_RTP_SENDER_PACKET_STORAGE_INCLUDE_PACKET_STORAGE_H_
+
+#include <list>
+#include <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/default_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+
+namespace media {
+namespace cast {
+
+class StoredPacket;
+typedef std::map<uint32, linked_ptr<StoredPacket> > PacketMap;
+typedef std::multimap<base::TimeTicks, uint32> TimeToPacketMap;
+
+class PacketStorage {
+ public:
+  static const int kMaxStoredPackets = 1000;
+
+  explicit PacketStorage(int max_time_stored_ms);
+  virtual ~PacketStorage();
+
+  void StorePacket(uint8 frame_id,
+                   uint16 packet_id,
+                   const std::vector<uint8>& packet);
+
+  // Copies packet into the buffer pointed to by rtp_buffer.
+  bool GetPacket(uint8 frame_id,
+                 uint16 packet_id,
+                 std::vector<uint8>* packet);
+  void set_clock(base::TickClock* clock) {
+    clock_ = clock;
+  }
+
+ private:
+  void CleanupOldPackets(base::TimeTicks now);
+
+  base::TimeDelta max_time_stored_;
+  PacketMap stored_packets_;
+  TimeToPacketMap time_to_packet_map_;
+  std::list<linked_ptr<StoredPacket> > free_packets_;
+  scoped_ptr<base::TickClock> default_tick_clock_;
+  base::TickClock* clock_;
+};
+
+}  // namespace cast
+}  // namespace media
+
+#endif // MEDIA_CAST_RTP_SENDER_PACKET_STORAGE_INCLUDE_PACKET_STORAGE_H_
diff --git a/media/cast/rtp_sender/packet_storage/packet_storage_unittest.cc b/media/cast/rtp_sender/packet_storage/packet_storage_unittest.cc
new file mode 100644
index 0000000..d6de08d
--- /dev/null
+++ b/media/cast/rtp_sender/packet_storage/packet_storage_unittest.cc
@@ -0,0 +1,114 @@
+// 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 "media/cast/rtp_sender/packet_storage/packet_storage.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <vector>
+
+#include "base/test/simple_test_tick_clock.h"
+#include "base/time/time.h"
+
+namespace media {
+namespace cast {
+
+static const int kMaxDeltaStoredMs = 500;
+static const base::TimeDelta kDeltaBetweenFrames =
+    base::TimeDelta::FromMilliseconds(33);
+
+static const int64 kStartMillisecond = 123456789;
+
+class PacketStorageTest : public ::testing::Test {
+ protected:
+  PacketStorageTest() : packet_storage_(kMaxDeltaStoredMs) {
+    testing_clock_.Advance(
+        base::TimeDelta::FromMilliseconds(kStartMillisecond));
+    packet_storage_.set_clock(&testing_clock_);
+  }
+
+  PacketStorage packet_storage_;
+  base::SimpleTestTickClock testing_clock_;
+};
+
+TEST_F(PacketStorageTest, TimeOut) {
+  std::vector<uint8> test_123(100, 123);  // 100 insertions of the value 123.
+
+  for (uint8 frame_id = 0; frame_id < 30; ++frame_id) {
+    for (uint16 packet_id = 0; packet_id < 10; ++packet_id) {
+      packet_storage_.StorePacket(frame_id, packet_id, test_123);
+    }
+    testing_clock_.Advance(kDeltaBetweenFrames);
+  }
+
+  // All packets belonging to the first 14 frames is expected to be expired.
+  for (uint8 frame_id = 0; frame_id < 14; ++frame_id) {
+    for (uint16 packet_id = 0; packet_id < 10; ++packet_id) {
+      std::vector<uint8> packet;
+      EXPECT_FALSE(packet_storage_.GetPacket(frame_id, packet_id, &packet));
+    }
+  }
+  // All packets belonging to the next 15 frames is expected to be valid.
+  for (uint8 frame_id = 14; frame_id < 30; ++frame_id) {
+    for (uint16 packet_id = 0; packet_id < 10; ++packet_id) {
+      std::vector<uint8> packet;
+      EXPECT_TRUE(packet_storage_.GetPacket(frame_id, packet_id, &packet));
+      EXPECT_TRUE(packet == test_123);
+    }
+  }
+}
+
+TEST_F(PacketStorageTest, MaxNumberOfPackets) {
+  std::vector<uint8> test_123(100, 123);  // 100 insertions of the value 123.
+
+  uint8 frame_id = 0;
+  for (uint16 packet_id = 0; packet_id <= PacketStorage::kMaxStoredPackets;
+      ++packet_id) {
+    packet_storage_.StorePacket(frame_id, packet_id, test_123);
+  }
+  std::vector<uint8> packet;
+  uint16 packet_id = 0;
+  EXPECT_FALSE(packet_storage_.GetPacket(frame_id, packet_id, &packet));
+
+  ++packet_id;
+  for (; packet_id <= PacketStorage::kMaxStoredPackets; ++packet_id) {
+    std::vector<uint8> packet;
+    EXPECT_TRUE(packet_storage_.GetPacket(frame_id, packet_id, &packet));
+    EXPECT_TRUE(packet == test_123);
+  }
+}
+
+TEST_F(PacketStorageTest, PacketContent) {
+  std::vector<uint8> test_123(100, 123);  // 100 insertions of the value 123.
+  std::vector<uint8> test_234(200, 234);  // 200 insertions of the value 234.
+
+  for (uint8 frame_id = 0; frame_id < 10; ++frame_id) {
+    for (uint16 packet_id = 0; packet_id < 10; ++packet_id) {
+      // Every other packet.
+      if (packet_id % 2 == 0) {
+        packet_storage_.StorePacket(frame_id, packet_id, test_123);
+      } else {
+        packet_storage_.StorePacket(frame_id, packet_id, test_234);
+      }
+    }
+    testing_clock_.Advance(kDeltaBetweenFrames);
+  }
+  for (uint8 frame_id = 0; frame_id < 10; ++frame_id) {
+    for (uint16 packet_id = 0; packet_id < 10; ++packet_id) {
+      std::vector<uint8> packet;
+      EXPECT_TRUE(packet_storage_.GetPacket(frame_id, packet_id, &packet));
+      // Every other packet.
+      if (packet_id % 2 == 0) {
+        EXPECT_TRUE(packet == test_123);
+      } else {
+        EXPECT_TRUE(packet == test_234);
+      }
+    }
+  }
+}
+
+}  // namespace cast
+}  // namespace media
+
diff --git a/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.cc b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.cc
new file mode 100644
index 0000000..264701c
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.cc
@@ -0,0 +1,145 @@
+// 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 "media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.h"
+
+#include "base/logging.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/pacing/paced_sender.h"
+#include "net/base/big_endian.h"
+
+namespace media {
+namespace cast {
+
+static const uint16 kCommonRtpHeaderLength = 12;
+static const uint16 kCastRtpHeaderLength = 7;
+static const uint8 kCastKeyFrameBitMask = 0x80;
+static const uint8 kCastReferenceFrameIdBitMask = 0x40;
+
+RtpPacketizer::RtpPacketizer(PacedPacketSender* transport,
+                             PacketStorage* packet_storage,
+                             RtpPacketizerConfig rtp_packetizer_config)
+    : config_(rtp_packetizer_config),
+      transport_(transport),
+      packet_storage_(packet_storage),
+      time_last_sent_rtp_timestamp_(0),
+      sequence_number_(config_.sequence_number),
+      rtp_timestamp_(config_.rtp_timestamp),
+      frame_id_(0),
+      packet_id_(0),
+      send_packets_count_(0),
+      send_octet_count_(0) {
+  DCHECK(transport) << "Invalid argument";
+}
+
+RtpPacketizer::~RtpPacketizer() {}
+
+void RtpPacketizer::IncomingEncodedVideoFrame(
+    const EncodedVideoFrame& video_frame,
+    int64 capture_time_ms) {
+  DCHECK(!config_.audio) << "Invalid state";
+  if (config_.audio) return;
+
+  // Timestamp is in 90 KHz for video.
+  rtp_timestamp_ = static_cast<uint32>(capture_time_ms * 90);
+  time_last_sent_rtp_timestamp_ = capture_time_ms;
+
+  Cast(video_frame.key_frame,
+       video_frame.last_referenced_frame_id,
+       rtp_timestamp_,
+       video_frame.data);
+}
+
+void RtpPacketizer::IncomingEncodedAudioFrame(
+    const EncodedAudioFrame& audio_frame,
+    int64 recorded_time) {
+  DCHECK(config_.audio) << "Invalid state";
+  if (!config_.audio) return;
+
+  rtp_timestamp_ += audio_frame.samples;  // Timestamp is in samples for audio.
+  time_last_sent_rtp_timestamp_ = recorded_time;
+  Cast(true, 0, rtp_timestamp_, audio_frame.data);
+}
+
+uint16 RtpPacketizer::NextSequenceNumber() {
+  ++sequence_number_;
+  return sequence_number_ - 1;
+}
+
+bool RtpPacketizer::LastSentTimestamp(int64* time_sent,
+                                      uint32* rtp_timestamp) const {
+  if (time_last_sent_rtp_timestamp_ == 0) return false;
+
+  *time_sent = time_last_sent_rtp_timestamp_;
+  *rtp_timestamp = rtp_timestamp_;
+  return true;
+}
+
+void RtpPacketizer::Cast(bool is_key,
+                         uint8 reference_frame_id,
+                         uint32 timestamp,
+                         std::vector<uint8> data) {
+  uint16 rtp_header_length = kCommonRtpHeaderLength + kCastRtpHeaderLength;
+  uint16 max_length = config_.max_payload_length - rtp_header_length - 1;
+  // Split the payload evenly (round number up).
+  uint32 num_packets = (data.size() + max_length) / max_length;
+  uint32 payload_length = (data.size() + num_packets) / num_packets;
+  DCHECK_LE(payload_length, max_length) << "Invalid argument";
+
+  std::vector<uint8> packet;
+  packet.reserve(kIpPacketSize);
+  size_t remaining_size = data.size();
+  uint8* data_ptr = data.data();
+  while (remaining_size > 0) {
+    packet.clear();
+    if (remaining_size < payload_length) {
+      payload_length = remaining_size;
+    }
+    remaining_size -= payload_length;
+    BuildCommonRTPheader(&packet, remaining_size == 0, timestamp);
+    // Build Cast header.
+    packet.push_back(
+        (is_key ? kCastKeyFrameBitMask : 0) | kCastReferenceFrameIdBitMask);
+    packet.push_back(frame_id_);
+    int start_size = packet.size();
+    packet.resize(start_size + 32);
+    net::BigEndianWriter big_endian_writer(&((packet)[start_size]), 32);
+    big_endian_writer.WriteU16(packet_id_);
+    big_endian_writer.WriteU16(num_packets - 1);
+    packet.push_back(reference_frame_id);
+
+    // Copy payload data.
+    packet.insert(packet.end(), data_ptr, data_ptr + payload_length);
+    // Store packet.
+    packet_storage_->StorePacket(frame_id_, packet_id_, packet);
+    // Send to network.
+    transport_->SendPacket(packet, num_packets);
+    ++packet_id_;
+    data_ptr += payload_length;
+    // Update stats.
+    ++send_packets_count_;
+    send_octet_count_ += payload_length;
+  }
+  DCHECK(packet_id_ == num_packets) << "Invalid state";
+  // Prepare for next frame.
+  packet_id_ = 0;
+  frame_id_ = static_cast<uint8>(frame_id_ + 1);
+}
+
+void RtpPacketizer::BuildCommonRTPheader(
+    std::vector<uint8>* packet, bool marker_bit, uint32 time_stamp) {
+  packet->push_back(0x80);
+  packet->push_back(static_cast<uint8>(config_.payload_type) |
+                    (marker_bit ? kRtpMarkerBitMask : 0));
+  int start_size = packet->size();
+  packet->resize(start_size + 80);
+  net::BigEndianWriter big_endian_writer(&((*packet)[start_size]), 80);
+  big_endian_writer.WriteU16(sequence_number_);
+  big_endian_writer.WriteU32(time_stamp);
+  big_endian_writer.WriteU32(config_.ssrc);
+  ++sequence_number_;
+}
+
+}  // namespace cast
+}  // namespace media
diff --git a/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.gypi b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.gypi
new file mode 100644
index 0000000..09ceb3b
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.gypi
@@ -0,0 +1,25 @@
+# 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.
+
+{
+  'targets': [
+    {
+      'target_name': 'cast_rtp_packetizer',
+      'type': 'static_library',
+      'include_dirs': [
+        '<(DEPTH)/',
+        '<(DEPTH)/third_party/',
+        '<(DEPTH)/third_party/webrtc/',
+      ],
+      'sources': [
+        'rtp_packetizer.cc',
+        'rtp_packetizer.h',
+      ], # source
+      'dependencies': [
+        '<(DEPTH)/base/base.gyp:base',
+        '<(DEPTH)/net/net.gyp:*',
+      ],
+    },
+  ],
+}
diff --git a/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.h b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.h
new file mode 100644
index 0000000..f1941cd
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.h
@@ -0,0 +1,65 @@
+// 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 MEDIA_CAST_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_H_
+#define MEDIA_CAST_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_H_
+
+#include <cmath>
+#include <list>
+#include <map>
+
+#include "media/cast/rtp_common/rtp_defines.h"
+#include "media/cast/rtp_sender/packet_storage/packet_storage.h"
+#include "media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_config.h"
+
+namespace media {
+namespace cast {
+
+class PacedPacketSender;
+
+class RtpPacketizer {
+ public:
+  RtpPacketizer(PacedPacketSender* transport,
+                PacketStorage* packet_storage,
+                RtpPacketizerConfig rtp_packetizer_config);
+  ~RtpPacketizer();
+
+  void IncomingEncodedVideoFrame(const EncodedVideoFrame& video_frame,
+                                 int64 capture_time);
+
+  void IncomingEncodedAudioFrame(const EncodedAudioFrame& audio_frame,
+                                 int64 recorded_time);
+
+  bool LastSentTimestamp(int64* time_sent, uint32* rtp_timestamp) const;
+
+  // Return the next sequence number, and increment by one. Enables unique
+  // incremental sequence numbers for every packet (including retransmissions).
+  uint16 NextSequenceNumber();
+
+  uint32 send_packets_count() {return send_packets_count_;}
+  uint32 send_octet_count() {return send_octet_count_;}
+
+ private:
+  void Cast(bool is_key, uint8 reference_frame_id,
+    uint32 timestamp, std::vector<uint8> data);
+  void BuildCommonRTPheader(std::vector<uint8>* packet, bool marker_bit,
+      uint32 time_stamp);
+  RtpPacketizerConfig config_;
+  PacedPacketSender* transport_;
+  PacketStorage* packet_storage_;
+
+  int64 time_last_sent_rtp_timestamp_;
+  uint16 sequence_number_;
+  uint32 rtp_timestamp_;
+  uint8 frame_id_;
+  uint16 packet_id_;
+
+  uint32 send_packets_count_;
+  uint32 send_octet_count_;
+};
+
+}  // namespace cast
+}  // namespace media
+
+#endif  // MEDIA_CAST_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_H_
diff --git a/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_config.h b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_config.h
new file mode 100644
index 0000000..cd005d5
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_config.h
@@ -0,0 +1,47 @@
+// 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 CAST_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_CONFIG_H_
+#define CAST_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_CONFIG_H_
+
+#include "media/cast/cast_config.h"
+#include "media/cast/rtp_common/rtp_defines.h"
+
+namespace media {
+namespace cast {
+
+struct RtpPacketizerConfig {
+  RtpPacketizerConfig() {
+    ssrc = 0;
+    max_payload_length = kIpPacketSize - 28;   // Default is IP-v4/UDP.
+    audio = false;
+    frequency = 8000;
+    payload_type = -1;
+    sequence_number = 0;
+    rtp_timestamp = 0;
+  }
+
+  // General.
+  bool audio;
+  int payload_type;
+  uint16 max_payload_length;
+  uint16 sequence_number;
+  uint32 rtp_timestamp;
+  int frequency;
+
+  // SSRC.
+  unsigned int ssrc;
+
+  // Video.
+  VideoCodec video_codec;
+
+  // Audio.
+  uint8 channels;
+  AudioCodec audio_codec;
+};
+
+}  // namespace cast
+}  // namespace media
+
+#endif  // CAST_RTP_SENDER_RTP_PACKETIZER_RTP_PACKETIZER_CONFIG_H_
diff --git a/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc
new file mode 100644
index 0000000..7d99a46
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_unittest.cc
@@ -0,0 +1,139 @@
+// 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 "media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.h"
+
+#include <gtest/gtest.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "media/cast/cast_config.h"
+#include "media/cast/pacing/paced_sender.h"
+#include "media/cast/rtp_common/rtp_defines.h"
+#include "media/cast/rtp_sender/packet_storage/packet_storage.h"
+#include "media/cast/rtp_sender/rtp_packetizer/test/rtp_header_parser.h"
+
+namespace media {
+namespace cast {
+
+static const int kPayload = 127;
+static const uint32 kTimestamp = 10;
+static const uint16 kSeqNum = 33;
+static const int kTimeOffset = 22222;
+static const int kMaxPacketLength = 1500;
+static const bool kMarkerBit = true;
+static const int kSsrc = 0x12345;
+static const uint8 kFrameId = 1;
+static const unsigned int kFrameSize = 5000;
+static const int kTotalHeaderLength = 19;
+static const int kMaxPacketStorageTimeMs = 300;
+
+class TestRtpPacketTransport : public PacedPacketSender {
+ public:
+  explicit TestRtpPacketTransport(RtpPacketizerConfig config)
+       : config_(config),
+         sequence_number_(kSeqNum),
+         packets_sent_(0),
+         expected_number_of_packets_(0) {}
+
+  void VerifyRtpHeader(const RtpCastHeader& rtp_header) {
+    VerifyCommonRtpHeader(rtp_header);
+    VerifyCastRtpHeader(rtp_header);
+  }
+
+  void VerifyCommonRtpHeader(const RtpCastHeader& rtp_header) {
+    EXPECT_EQ(expected_number_of_packets_ == packets_sent_,
+        rtp_header.webrtc.header.markerBit);
+    EXPECT_EQ(kPayload, rtp_header.webrtc.header.payloadType);
+    EXPECT_EQ(sequence_number_, rtp_header.webrtc.header.sequenceNumber);
+    EXPECT_EQ(kTimestamp * 90, rtp_header.webrtc.header.timestamp);
+    EXPECT_EQ(config_.ssrc, rtp_header.webrtc.header.ssrc);
+    EXPECT_EQ(0, rtp_header.webrtc.header.numCSRCs);
+  }
+
+  void VerifyCastRtpHeader(const RtpCastHeader& rtp_header) {
+    // TODO(mikhal)
+  }
+
+  virtual bool SendPacket(const std::vector<uint8>& packet,
+                          int num_packets) OVERRIDE {
+    EXPECT_EQ(expected_number_of_packets_, num_packets);
+    ++packets_sent_;
+    RtpHeaderParser parser(packet.data(), packet.size());
+    RtpCastHeader rtp_header;
+    parser.Parse(&rtp_header);
+    VerifyRtpHeader(rtp_header);
+    ++sequence_number_;
+    return true;
+  }
+
+  virtual bool ResendPacket(const std::vector<uint8>& packet,
+                            int num_of_packets) OVERRIDE {
+    EXPECT_TRUE(false);
+    return false;
+  }
+
+  virtual bool SendRtcpPacket(const std::vector<uint8>& packet) OVERRIDE {
+    EXPECT_TRUE(false);
+    return false;
+  }
+
+  void SetExpectedNumberOfPackets(int num) {
+    expected_number_of_packets_ = num;
+  }
+
+  RtpPacketizerConfig config_;
+  uint32 sequence_number_;
+  int packets_sent_;
+  int expected_number_of_packets_;
+};
+
+class RtpPacketizerTest : public ::testing::Test {
+ protected:
+  RtpPacketizerTest()
+      :video_frame_(),
+       packet_storage_(kMaxPacketStorageTimeMs) {
+    config_.sequence_number = kSeqNum;
+    config_.ssrc = kSsrc;
+    config_.payload_type = kPayload;
+    config_.max_payload_length = kMaxPacketLength;
+    transport_.reset(new TestRtpPacketTransport(config_));
+    rtp_packetizer_.reset(
+        new RtpPacketizer(transport_.get(), &packet_storage_, config_));
+  }
+
+  ~RtpPacketizerTest() {}
+
+  void SetUp() {
+    video_frame_.key_frame = false;
+    video_frame_.frame_id = kFrameId;
+    video_frame_.last_referenced_frame_id = kFrameId - 1;
+    video_frame_.data.assign(kFrameSize, 123);
+  }
+
+  scoped_ptr<RtpPacketizer> rtp_packetizer_;
+  RtpPacketizerConfig config_;
+  scoped_ptr<TestRtpPacketTransport> transport_;
+  EncodedVideoFrame video_frame_;
+  PacketStorage packet_storage_;
+};
+
+TEST_F(RtpPacketizerTest, SendStandardPackets) {
+  int expected_num_of_packets = kFrameSize / kMaxPacketLength + 1;
+  transport_->SetExpectedNumberOfPackets(expected_num_of_packets);
+  rtp_packetizer_->IncomingEncodedVideoFrame(video_frame_, kTimestamp);
+}
+
+TEST_F(RtpPacketizerTest, Stats) {
+  EXPECT_FALSE(rtp_packetizer_->send_packets_count());
+  EXPECT_FALSE(rtp_packetizer_->send_octet_count());
+  // Insert packets at varying lengths.
+  unsigned int expected_num_of_packets = kFrameSize / kMaxPacketLength + 1;
+  transport_->SetExpectedNumberOfPackets(expected_num_of_packets);
+  rtp_packetizer_->IncomingEncodedVideoFrame(video_frame_, kTimestamp);
+  EXPECT_EQ(expected_num_of_packets, rtp_packetizer_->send_packets_count());
+  EXPECT_EQ(kFrameSize, rtp_packetizer_->send_octet_count());
+}
+
+}  // namespace cast
+}  // namespace media
diff --git a/media/cast/rtp_sender/rtp_packetizer/test/rtp_header_parser.cc b/media/cast/rtp_sender/rtp_packetizer/test/rtp_header_parser.cc
new file mode 100644
index 0000000..b4a3021
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_packetizer/test/rtp_header_parser.cc
@@ -0,0 +1,70 @@
+// 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 "media/cast/rtp_sender/rtp_packetizer/test/rtp_header_parser.h"
+
+#include <cstddef>
+
+#include "net/base/big_endian.h"
+
+namespace media {
+namespace cast {
+
+RtpHeaderParser::RtpHeaderParser(const uint8* rtp_data,
+                                 const uint32 rtp_data_length)
+  : rtp_data_begin_(rtp_data),
+    rtp_data_end_(rtp_data ? (rtp_data + rtp_data_length) : NULL) {
+}
+
+RtpHeaderParser::~RtpHeaderParser() {}
+
+bool RtpHeaderParser::Parse(RtpCastHeader* parsed_packet) const {
+  const ptrdiff_t length = rtp_data_end_ - rtp_data_begin_;
+
+  if (length < 12) {
+    return false;
+  }
+
+  const uint8 version  = rtp_data_begin_[0] >> 6;
+  if (version != 2) {
+    return false;
+  }
+
+  const uint8 num_csrcs = rtp_data_begin_[0] & 0x0f;
+  const bool marker = ((rtp_data_begin_[1] & 0x80) == 0) ? false : true;
+
+  const uint8 payload_type = rtp_data_begin_[1] & 0x7f;
+
+  const uint16 sequence_number = (rtp_data_begin_[2] << 8) +
+      rtp_data_begin_[3];
+
+  const uint8* ptr = &rtp_data_begin_[4];
+
+  net::BigEndianReader big_endian_reader(ptr, 64);
+  uint32 rtp_timestamp, ssrc;
+  big_endian_reader.ReadU32(&rtp_timestamp);
+  big_endian_reader.ReadU32(&ssrc);
+
+  const uint8 csrc_octs = num_csrcs * 4;
+
+  if ((ptr + csrc_octs) > rtp_data_end_) {
+    return false;
+  }
+
+  parsed_packet->webrtc.header.markerBit      = marker;
+  parsed_packet->webrtc.header.payloadType    = payload_type;
+  parsed_packet->webrtc.header.sequenceNumber = sequence_number;
+  parsed_packet->webrtc.header.timestamp      = rtp_timestamp;
+  parsed_packet->webrtc.header.ssrc           = ssrc;
+  parsed_packet->webrtc.header.numCSRCs       = num_csrcs;
+
+  parsed_packet->webrtc.type.Audio.numEnergy =
+      parsed_packet->webrtc.header.numCSRCs;
+
+  parsed_packet->webrtc.header.headerLength   = 12 + csrc_octs;
+  return true;
+}
+
+}  // namespace cast
+}  // namespace media
\ No newline at end of file
diff --git a/media/cast/rtp_sender/rtp_packetizer/test/rtp_header_parser.h b/media/cast/rtp_sender/rtp_packetizer/test/rtp_header_parser.h
new file mode 100644
index 0000000..e0d84ac
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_packetizer/test/rtp_header_parser.h
@@ -0,0 +1,31 @@
+// 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.
+
+// Utility parser for rtp packetizer unittests
+#ifndef MEDIA_CAST_RTP_SENDER_RTP_PACKETIZER_TEST_RTP_HEADER_PARSER_H_
+#define MEDIA_CAST_RTP_SENDER_RTP_PACKETIZER_TEST_RTP_HEADER_PARSER_H_
+
+#include "media/cast/rtp_common/rtp_defines.h"
+
+namespace media {
+namespace cast {
+
+class RtpHeaderParser {
+ public:
+  RtpHeaderParser(const uint8* rtpData,
+                  const uint32 rtpDataLength);
+  ~RtpHeaderParser();
+
+  bool Parse(RtpCastHeader* parsed_packet) const;
+ private:
+  const uint8* const rtp_data_begin_;
+  const uint8* const rtp_data_end_;
+
+  DISALLOW_COPY_AND_ASSIGN(RtpHeaderParser);
+};
+
+}  // namespace cast
+}  // namespace media
+
+#endif  // MEDIA_CAST_RTP_SENDER_RTP_PACKETIZER_TEST_RTP_HEADER_PARSER_H_
diff --git a/media/cast/rtp_sender/rtp_sender.cc b/media/cast/rtp_sender/rtp_sender.cc
new file mode 100644
index 0000000..c735dd0
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_sender.cc
@@ -0,0 +1,166 @@
+// 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 "media/cast/rtp_sender/rtp_sender.h"
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/pacing/paced_sender.h"
+#include "media/cast/rtcp/rtcp_defines.h"
+
+namespace media {
+namespace cast {
+namespace {
+
+// January 1970, in milliseconds.
+static const int64 kNtpJan1970 = 2208988800000LL;
+
+// Magic fractional unit.
+static const uint32 kMagicFractionalUnit = 4294967;
+
+void ConvertTimeToFractions(int64 time_ms, uint32* seconds,
+                            uint32* fractions) {
+  *seconds = static_cast<uint32>(time_ms / 1000);
+  *fractions = static_cast<uint32>((time_ms % 1000) * kMagicFractionalUnit);
+}
+
+void ConvertTimeToNtp(int64 time_ms, uint32* ntp_seconds,
+                      uint32* ntp_fractions) {
+  ConvertTimeToFractions(time_ms + kNtpJan1970, ntp_seconds, ntp_fractions);
+}
+}  // namespace
+
+RtpSender::RtpSender(const AudioSenderConfig* audio_config,
+                     const VideoSenderConfig* video_config,
+                     PacedPacketSender* transport)
+    : config_(),
+      transport_(transport),
+      default_tick_clock_(new base::DefaultTickClock()),
+      clock_(default_tick_clock_.get()) {
+  // Store generic cast config and create packetizer config.
+  DCHECK(audio_config || video_config) << "Invalid argument";
+  if (audio_config) {
+    storage_.reset(new PacketStorage(audio_config->rtp_history_ms));
+    config_.audio = true;
+    config_.ssrc = audio_config->sender_ssrc;
+    config_.payload_type = audio_config->rtp_payload_type;
+    config_.frequency = audio_config->frequency;
+    config_.audio_codec = audio_config->codec;
+  } else {
+    storage_.reset(new PacketStorage(video_config->rtp_history_ms));
+    config_.audio = false;
+    config_.ssrc = video_config->sender_ssrc;
+    config_.payload_type = video_config->rtp_payload_type;
+    config_.frequency = kVideoFrequency;
+    config_.video_codec = video_config->codec;
+  }
+  // Randomly set start values.
+  config_.sequence_number = base::RandInt(0, 65535);
+  config_.rtp_timestamp = base::RandInt(0, 65535);
+  config_.rtp_timestamp += base::RandInt(0, 65535) << 16;
+  packetizer_.reset(new RtpPacketizer(transport, storage_.get(), config_));
+}
+
+RtpSender::~RtpSender() {}
+
+void RtpSender::IncomingEncodedVideoFrame(const EncodedVideoFrame& video_frame,
+                                          int64 capture_time) {
+  packetizer_->IncomingEncodedVideoFrame(video_frame, capture_time);
+}
+
+void RtpSender::IncomingEncodedAudioFrame(const EncodedAudioFrame& audio_frame,
+                                          int64 recorded_time) {
+  packetizer_->IncomingEncodedAudioFrame(audio_frame, recorded_time);
+}
+
+void RtpSender::ResendPackets(
+    const MissingFramesAndPackets& missing_frames_and_packets) {
+  std::vector<uint8> packet;
+  // Iterate over all frames in the list.
+  for (std::map<uint8, std::set<uint16> >::const_iterator it =
+       missing_frames_and_packets.begin();
+       it != missing_frames_and_packets.end(); ++it) {
+    uint8 frame_id = it->first;
+    // Iterate over all of the packets in the frame.
+    const std::set<uint16>& packets = it->second;
+    if (packets.empty()) {
+      VLOG(1) << "Missing all packets in frame " << static_cast<int>(frame_id);
+
+      bool success = false;
+      uint16 packet_id = 0;
+      do {
+        // Get packet from storage.
+        packet.clear();
+        success = storage_->GetPacket(frame_id, packet_id, &packet);
+
+        // Resend packet to the network.
+        if (success) {
+          VLOG(1) << "Resend " << static_cast<int>(frame_id) << ":"
+              << packet_id << " size: " << packets.size();
+          // Set a unique incremental sequence number for every packet.
+          UpdateSequenceNumber(&packet);
+          // Set the size as correspond to each frame.
+          transport_->ResendPacket(packet, packets.size());
+          ++packet_id;
+        }
+      } while (success);
+
+
+    } else {
+      for (std::set<uint16>::const_iterator set_it = packets.begin();
+          set_it != packets.end(); ++set_it) {
+        uint16 packet_id = *set_it;
+        // Get packet from storage.
+        packet.clear();
+        bool success = storage_->GetPacket(frame_id, packet_id, &packet);
+        // Resend packet to the network.
+        if (success) {
+          VLOG(1) << "Resend " << static_cast<int>(frame_id) << ":"
+              << packet_id << " size: " << packet.size();
+          UpdateSequenceNumber(&packet);
+          // Set the size as correspond to each frame.
+          transport_->ResendPacket(packet, packets.size());
+        } else {
+          VLOG(1) << "Failed to resend " << static_cast<int>(frame_id) << ":"
+              << packet_id;
+        }
+      }
+    }
+  }
+}
+
+void RtpSender::UpdateSequenceNumber(std::vector<uint8>* packet) {
+  uint16 new_sequence_number = packetizer_->NextSequenceNumber();
+  int index = 2;
+  (*packet)[index] = (static_cast<uint8>(new_sequence_number));
+  (*packet)[index + 1] =(static_cast<uint8>(new_sequence_number >> 8));
+}
+
+void RtpSender::RtpStatistics(int64 now_ms, RtcpSenderInfo* sender_info) {
+  // The timestamp of this Rtcp packet should be estimated as the timestamp of
+  // the frame being captured at this moment. We are calculating that
+  // timestamp as the last frame's timestamp + the time since the last frame
+  // was captured.
+  uint32 ntp_seconds = 0;
+  uint32 ntp_fraction = 0;
+  ConvertTimeToNtp(now_ms, &ntp_seconds, &ntp_fraction);
+  // sender_info->ntp_seconds = ntp_seconds;
+  sender_info->ntp_fraction = ntp_fraction;
+
+  int64 time_sent_ms;
+  uint32 rtp_timestamp;
+  if (packetizer_->LastSentTimestamp(&time_sent_ms, &rtp_timestamp)) {
+    int64 time_since_last_send_ms = now_ms - time_sent_ms;
+    sender_info->rtp_timestamp = rtp_timestamp +
+        time_since_last_send_ms * (config_.frequency / 1000);
+  } else {
+    sender_info->rtp_timestamp = 0;
+  }
+  sender_info->send_packet_count = packetizer_->send_packets_count();
+  sender_info->send_octet_count = packetizer_->send_octet_count();
+}
+
+}  //  namespace cast
+}  // namespace media
diff --git a/media/cast/rtp_sender/rtp_sender.gyp b/media/cast/rtp_sender/rtp_sender.gyp
new file mode 100644
index 0000000..77722c9
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_sender.gyp
@@ -0,0 +1,27 @@
+# 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.
+
+{
+  'targets': [
+    {
+      'target_name': 'cast_rtp_sender',
+      'type': 'static_library',
+      'include_dirs': [
+        '<(DEPTH)/',
+        '<(DEPTH)/third_party/',
+        '<(DEPTH)/third_party/webrtc/',
+      ],
+      'sources': [
+        'rtp_sender.cc',
+        'rtp_sender.h',
+      ], # source
+      'dependencies': [
+        '<(DEPTH)/base/base.gyp:base',
+        '<(DEPTH)/base/base.gyp:test_support_base',
+        'packet_storage/packet_storage.gypi:*',
+        'rtp_packetizer/rtp_packetizer.gypi:*',
+      ],
+    },
+  ],
+}
diff --git a/media/cast/rtp_sender/rtp_sender.h b/media/cast/rtp_sender/rtp_sender.h
new file mode 100644
index 0000000..9352fd3
--- /dev/null
+++ b/media/cast/rtp_sender/rtp_sender.h
@@ -0,0 +1,63 @@
+// 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.
+
+// This file contains the interface to the cast RTP sender.
+
+#ifndef MEDIA_CAST_RTP_SENDER_RTP_SENDER_H_
+#define MEDIA_CAST_RTP_SENDER_RTP_SENDER_H_
+
+#include <map>
+#include <set>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/time/default_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "media/cast/cast_config.h"
+#include "media/cast/rtp_sender/packet_storage/packet_storage.h"
+#include "media/cast/rtp_sender/rtp_packetizer/rtp_packetizer.h"
+#include "media/cast/rtp_sender/rtp_packetizer/rtp_packetizer_config.h"
+
+namespace media {
+namespace cast {
+
+class PacedPacketSender;
+struct RtcpSenderInfo;
+
+typedef std::map<uint8, std::set<uint16> > MissingFramesAndPackets;
+
+class RtpSender {
+ public:
+  RtpSender(const AudioSenderConfig* audio_config,
+            const VideoSenderConfig* video_config,
+            PacedPacketSender* transport);
+
+  ~RtpSender();
+
+  void IncomingEncodedVideoFrame(const EncodedVideoFrame& video_frame,
+                                 int64 capture_time);
+
+  void IncomingEncodedAudioFrame(const EncodedAudioFrame& audio_frame,
+                                 int64 recorded_time);
+
+  void ResendPackets(
+      const MissingFramesAndPackets& missing_frames_and_packets);
+
+  void RtpStatistics(int64 now_ms, RtcpSenderInfo* sender_info);
+
+ private:
+  void UpdateSequenceNumber(std::vector<uint8>* packet);
+
+  RtpPacketizerConfig config_;
+  scoped_ptr<RtpPacketizer> packetizer_;
+  scoped_ptr<PacketStorage> storage_;
+  PacedPacketSender* transport_;
+  scoped_ptr<base::TickClock> default_tick_clock_;
+  base::TickClock* clock_;
+};
+
+}  // namespace cast
+}  // namespace media
+
+#endif  // MEDIA_CAST_RTP_SENDER_RTP_SENDER_H_
diff --git a/media/ffmpeg/ffmpeg_unittest.cc b/media/ffmpeg/ffmpeg_unittest.cc
index d774a06..3557ada 100644
--- a/media/ffmpeg/ffmpeg_unittest.cc
+++ b/media/ffmpeg/ffmpeg_unittest.cc
@@ -17,9 +17,9 @@
 #include "base/files/memory_mapped_file.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/path_service.h"
-#include "base/perftimer.h"
 #include "base/strings/string_util.h"
 #include "base/test/perf_test_suite.h"
+#include "base/test/perftimer.h"
 #include "media/base/media.h"
 #include "media/ffmpeg/ffmpeg_common.h"
 #include "media/filters/ffmpeg_glue.h"
diff --git a/media/filters/audio_renderer_algorithm.cc b/media/filters/audio_renderer_algorithm.cc
index 97f0811..572e263 100644
--- a/media/filters/audio_renderer_algorithm.cc
+++ b/media/filters/audio_renderer_algorithm.cc
@@ -12,41 +12,78 @@
 #include "media/audio/audio_util.h"
 #include "media/base/audio_buffer.h"
 #include "media/base/audio_bus.h"
+#include "media/filters/wsola_internals.h"
 
 namespace media {
 
-// The starting size in frames for |audio_buffer_|. Previous usage maintained a
-// queue of 16 AudioBuffers, each of 512 frames. This worked well, so we
-// maintain this number of frames.
-static const int kStartingBufferSizeInFrames = 16 * 512;
+
+// Waveform Similarity Overlap-and-add (WSOLA).
+//
+// One WSOLA iteration
+//
+// 1) Extract |target_block_| as input frames at indices
+//    [|target_block_index_|, |target_block_index_| + |ola_window_size_|).
+//    Note that |target_block_| is the "natural" continuation of the output.
+//
+// 2) Extract |search_block_| as input frames at indices
+//    [|search_block_index_|,
+//     |search_block_index_| + |num_candidate_blocks_| + |ola_window_size_|).
+//
+// 3) Find a block within the |search_block_| that is most similar
+//    to |target_block_|. Let |optimal_index| be the index of such block and
+//    write it to |optimal_block_|.
+//
+// 4) Update:
+//    |optimal_block_| = |transition_window_| * |target_block_| +
+//    (1 - |transition_window_|) * |optimal_block_|.
+//
+// 5) Overlap-and-add |optimal_block_| to the |wsola_output_|.
+//
+// 6) Update:
+//    |target_block_| = |optimal_index| + |ola_window_size_| / 2.
+//    |output_index_| = |output_index_| + |ola_window_size_| / 2,
+//    |search_block_center_offset_| = |output_index_| * |playback_rate_|, and
+//    |search_block_index_| = |search_block_center_offset_| -
+//        |search_block_center_offset_|.
 
 // The maximum size in frames for the |audio_buffer_|. Arbitrarily determined.
 // This number represents 3 seconds of 96kHz/16 bit 7.1 surround sound.
 static const int kMaxBufferSizeInFrames = 3 * 96000;
 
-// Duration of audio segments used for crossfading (in seconds).
-static const double kWindowDuration = 0.08;
-
-// Duration of crossfade between audio segments (in seconds).
-static const double kCrossfadeDuration = 0.008;
-
 // Max/min supported playback rates for fast/slow audio. Audio outside of these
 // ranges are muted.
 // Audio at these speeds would sound better under a frequency domain algorithm.
 static const float kMinPlaybackRate = 0.5f;
 static const float kMaxPlaybackRate = 4.0f;
 
+// Overlap-and-add window size in milliseconds.
+static const int kOlaWindowSizeMs = 20;
+
+// Size of search interval in milliseconds. The search interval is
+// [-delta delta] around |output_index_| * |playback_rate_|. So the search
+// interval is 2 * delta.
+static const int kWsolaSearchIntervalMs = 30;
+
+// The starting size in frames for |audio_buffer_|. Previous usage maintained a
+// queue of 16 AudioBuffers, each of 512 frames. This worked well, so we
+// maintain this number of frames.
+static const int kStartingBufferSizeInFrames = 16 * 512;
+
 AudioRendererAlgorithm::AudioRendererAlgorithm()
     : channels_(0),
       samples_per_second_(0),
       playback_rate_(0),
-      frames_in_crossfade_(0),
-      index_into_window_(0),
-      crossfade_frame_number_(0),
       muted_(false),
       muted_partial_frame_(0),
-      window_size_(0),
-      capacity_(kStartingBufferSizeInFrames) {
+      capacity_(kStartingBufferSizeInFrames),
+      output_time_(0.0),
+      search_block_center_offset_(0),
+      search_block_index_(0),
+      num_candidate_blocks_(0),
+      target_block_index_(0),
+      ola_window_size_(0),
+      ola_hop_size_(0),
+      num_complete_frames_(0) {
 }
 
 AudioRendererAlgorithm::~AudioRendererAlgorithm() {}
@@ -58,16 +95,59 @@
   channels_ = params.channels();
   samples_per_second_ = params.sample_rate();
   SetPlaybackRate(initial_playback_rate);
+  num_candidate_blocks_ = (kWsolaSearchIntervalMs * samples_per_second_) / 1000;
+  ola_window_size_ = kOlaWindowSizeMs * samples_per_second_ / 1000;
 
-  window_size_ = samples_per_second_ * kWindowDuration;
-  frames_in_crossfade_ = samples_per_second_ * kCrossfadeDuration;
-  crossfade_buffer_ = AudioBus::Create(channels_, frames_in_crossfade_);
+  // Make sure window size in an even number.
+  ola_window_size_ += ola_window_size_ & 1;
+  ola_hop_size_ = ola_window_size_ / 2;
+
+  // |num_candidate_blocks_| / 2 is the offset of the center of the search
+  // block to the center of the first (left most) candidate block. The offset
+  // of the center of a candidate block to its left most point is
+  // |ola_window_size_| / 2 - 1. Note that |ola_window_size_| is even and in
+  // our convention the center belongs to the left half, so we need to subtract
+  // one frame to get the correct offset.
+  //
+  //                             Search Block
+  //              <------------------------------------------->
+  //
+  //   |ola_window_size_| / 2 - 1
+  //              <----
+  //
+  //             |num_candidate_blocks_| / 2
+  //                   <----------------
+  //                                 center
+  //              X----X----------------X---------------X-----X
+  //              <---------->                     <---------->
+  //                Candidate      ...               Candidate
+  //                   1,          ...         |num_candidate_blocks_|
+  search_block_center_offset_ = num_candidate_blocks_ / 2 +
+      (ola_window_size_ / 2 - 1);
+
+  ola_window_.reset(new float[ola_window_size_]);
+  internal::GetSymmetricHanningWindow(ola_window_size_, ola_window_.get());
+
+  transition_window_.reset(new float[ola_window_size_ * 2]);
+  internal::GetSymmetricHanningWindow(2 * ola_window_size_,
+                                      transition_window_.get());
+
+  wsola_output_ = AudioBus::Create(channels_, ola_window_size_ + ola_hop_size_);
+  wsola_output_->Zero();  // Initialize for overlap-and-add of the first block.
+
+  // Auxiliary containers.
+  optimal_block_ = AudioBus::Create(channels_, ola_window_size_);
+  search_block_ = AudioBus::Create(
+      channels_, num_candidate_blocks_ + (ola_window_size_ - 1));
+  target_block_ = AudioBus::Create(channels_, ola_window_size_);
 }
 
 int AudioRendererAlgorithm::FillBuffer(AudioBus* dest, int requested_frames) {
   if (playback_rate_ == 0)
     return 0;
 
+  DCHECK_EQ(channels_, dest->channels());
+
   // Optimize the |muted_| case to issue a single clear instead of performing
   // the full crossfade and clearing each crossfaded frame.
   if (muted_) {
@@ -93,12 +173,12 @@
     return frames_to_render;
   }
 
-  int slower_step = ceil(window_size_ * playback_rate_);
-  int faster_step = ceil(window_size_ / playback_rate_);
+  int slower_step = ceil(ola_window_size_ * playback_rate_);
+  int faster_step = ceil(ola_window_size_ / playback_rate_);
 
   // Optimize the most common |playback_rate_| ~= 1 case to use a single copy
   // instead of copying frame by frame.
-  if (window_size_ <= faster_step && slower_step >= window_size_) {
+  if (ola_window_size_ <= faster_step && slower_step >= ola_window_size_) {
     const int frames_to_copy =
         std::min(audio_buffer_.frames(), requested_frames);
     const int frames_read = audio_buffer_.ReadFrames(frames_to_copy, 0, dest);
@@ -106,243 +186,12 @@
     return frames_read;
   }
 
-  int total_frames_rendered = 0;
-  while (total_frames_rendered < requested_frames) {
-    if (index_into_window_ >= window_size_)
-      ResetWindow();
-
-    int rendered_frames = 0;
-    if (window_size_ > faster_step) {
-      rendered_frames =
-          OutputFasterPlayback(dest,
-                               total_frames_rendered,
-                               requested_frames - total_frames_rendered,
-                               window_size_,
-                               faster_step);
-    } else if (slower_step < window_size_) {
-      rendered_frames =
-          OutputSlowerPlayback(dest,
-                               total_frames_rendered,
-                               requested_frames - total_frames_rendered,
-                               slower_step,
-                               window_size_);
-    } else {
-      NOTREACHED();
-    }
-
-    if (rendered_frames == 0)
-      break;
-
-    total_frames_rendered += rendered_frames;
-  }
-  return total_frames_rendered;
-}
-
-void AudioRendererAlgorithm::ResetWindow() {
-  DCHECK_LE(index_into_window_, window_size_);
-  index_into_window_ = 0;
-  crossfade_frame_number_ = 0;
-}
-
-int AudioRendererAlgorithm::OutputFasterPlayback(AudioBus* dest,
-                                                 int dest_offset,
-                                                 int requested_frames,
-                                                 int input_step,
-                                                 int output_step) {
-  // Ensure we don't run into OOB read/write situation.
-  CHECK_GT(input_step, output_step);
-  DCHECK_LT(index_into_window_, window_size_);
-  DCHECK_GT(playback_rate_, 1.0);
-  DCHECK(!muted_);
-
-  if (audio_buffer_.frames() < 1)
-    return 0;
-
-  // The audio data is output in a series of windows. For sped-up playback,
-  // the window is comprised of the following phases:
-  //
-  //  a) Output raw data.
-  //  b) Save bytes for crossfade in |crossfade_buffer_|.
-  //  c) Drop data.
-  //  d) Output crossfaded audio leading up to the next window.
-  //
-  // The duration of each phase is computed below based on the |window_size_|
-  // and |playback_rate_|.
-  DCHECK_LE(frames_in_crossfade_, output_step);
-
-  // This is the index of the end of phase a, beginning of phase b.
-  int outtro_crossfade_begin = output_step - frames_in_crossfade_;
-
-  // This is the index of the end of phase b, beginning of phase c.
-  int outtro_crossfade_end = output_step;
-
-  // This is the index of the end of phase c, beginning of phase d.
-  // This phase continues until |index_into_window_| reaches |window_size_|, at
-  // which point the window restarts.
-  int intro_crossfade_begin = input_step - frames_in_crossfade_;
-
-  // a) Output raw frames if we haven't reached the crossfade section.
-  if (index_into_window_ < outtro_crossfade_begin) {
-    // Read as many frames as we can and return the count. If it's not enough,
-    // we will get called again.
-    const int frames_to_copy =
-        std::min(requested_frames, outtro_crossfade_begin - index_into_window_);
-    int copied = audio_buffer_.ReadFrames(frames_to_copy, dest_offset, dest);
-    index_into_window_ += copied;
-    return copied;
-  }
-
-  // b) Save outtro crossfade frames into intermediate buffer, but do not output
-  //    anything to |dest|.
-  if (index_into_window_ < outtro_crossfade_end) {
-    // This phase only applies if there are bytes to crossfade.
-    DCHECK_GT(frames_in_crossfade_, 0);
-    int crossfade_start = index_into_window_ - outtro_crossfade_begin;
-    int crossfade_count = outtro_crossfade_end - index_into_window_;
-    int copied = audio_buffer_.ReadFrames(
-        crossfade_count, crossfade_start, crossfade_buffer_.get());
-    index_into_window_ += copied;
-
-    // Did we get all the frames we need? If not, return and let subsequent
-    // calls try to get the rest.
-    if (copied != crossfade_count)
-      return 0;
-  }
-
-  // c) Drop frames until we reach the intro crossfade section.
-  if (index_into_window_ < intro_crossfade_begin) {
-    // Check if there is enough data to skip all the frames needed. If not,
-    // return 0 and let subsequent calls try to skip it all.
-    int seek_frames = intro_crossfade_begin - index_into_window_;
-    if (audio_buffer_.frames() < seek_frames)
-      return 0;
-    audio_buffer_.SeekFrames(seek_frames);
-
-    // We've dropped all the frames that need to be dropped.
-    index_into_window_ += seek_frames;
-  }
-
-  // d) Crossfade and output a frame, as long as we have data.
-  if (audio_buffer_.frames() < 1)
-    return 0;
-  DCHECK_GT(frames_in_crossfade_, 0);
-  DCHECK_LT(index_into_window_, window_size_);
-
-  int offset_into_buffer = index_into_window_ - intro_crossfade_begin;
-  int copied = audio_buffer_.ReadFrames(1, dest_offset, dest);
-  DCHECK_EQ(copied, 1);
-  CrossfadeFrame(crossfade_buffer_.get(),
-                 offset_into_buffer,
-                 dest,
-                 dest_offset,
-                 offset_into_buffer);
-  index_into_window_ += copied;
-  return copied;
-}
-
-int AudioRendererAlgorithm::OutputSlowerPlayback(AudioBus* dest,
-                                                 int dest_offset,
-                                                 int requested_frames,
-                                                 int input_step,
-                                                 int output_step) {
-  // Ensure we don't run into OOB read/write situation.
-  CHECK_LT(input_step, output_step);
-  DCHECK_LT(index_into_window_, window_size_);
-  DCHECK_LT(playback_rate_, 1.0);
-  DCHECK_NE(playback_rate_, 0);
-  DCHECK(!muted_);
-
-  if (audio_buffer_.frames() < 1)
-    return 0;
-
-  // The audio data is output in a series of windows. For slowed down playback,
-  // the window is comprised of the following phases:
-  //
-  //  a) Output raw data.
-  //  b) Output and save bytes for crossfade in |crossfade_buffer_|.
-  //  c) Output* raw data.
-  //  d) Output* crossfaded audio leading up to the next window.
-  //
-  // * Phases c) and d) do not progress |audio_buffer_|'s cursor so that the
-  // |audio_buffer_|'s cursor is in the correct place for the next window.
-  //
-  // The duration of each phase is computed below based on the |window_size_|
-  // and |playback_rate_|.
-  DCHECK_LE(frames_in_crossfade_, input_step);
-
-  // This is the index of the end of phase a, beginning of phase b.
-  int intro_crossfade_begin = input_step - frames_in_crossfade_;
-
-  // This is the index of the end of phase b, beginning of phase c.
-  int intro_crossfade_end = input_step;
-
-  // This is the index of the end of phase c,  beginning of phase d.
-  // This phase continues until |index_into_window_| reaches |window_size_|, at
-  // which point the window restarts.
-  int outtro_crossfade_begin = output_step - frames_in_crossfade_;
-
-  // a) Output raw frames.
-  if (index_into_window_ < intro_crossfade_begin) {
-    // Read as many frames as we can and return the count. If it's not enough,
-    // we will get called again.
-    const int frames_to_copy =
-        std::min(requested_frames, intro_crossfade_begin - index_into_window_);
-    int copied = audio_buffer_.ReadFrames(frames_to_copy, dest_offset, dest);
-    index_into_window_ += copied;
-    return copied;
-  }
-
-  // b) Save the raw frames for the intro crossfade section, then copy the
-  //    same frames to |dest|.
-  if (index_into_window_ < intro_crossfade_end) {
-    const int frames_to_copy =
-        std::min(requested_frames, intro_crossfade_end - index_into_window_);
-    int offset = index_into_window_ - intro_crossfade_begin;
-    int copied = audio_buffer_.ReadFrames(
-        frames_to_copy, offset, crossfade_buffer_.get());
-    crossfade_buffer_->CopyPartialFramesTo(offset, copied, dest_offset, dest);
-    index_into_window_ += copied;
-    return copied;
-  }
-
-  // c) Output a raw frame into |dest| without advancing the |audio_buffer_|
-  //    cursor.
-  int audio_buffer_offset = index_into_window_ - intro_crossfade_end;
-  DCHECK_GE(audio_buffer_offset, 0);
-  if (audio_buffer_.frames() <= audio_buffer_offset)
-    return 0;
-  int copied =
-      audio_buffer_.PeekFrames(1, audio_buffer_offset, dest_offset, dest);
-  DCHECK_EQ(1, copied);
-
-  // d) Crossfade the next frame of |crossfade_buffer_| into |dest| if we've
-  //    reached the outtro crossfade section of the window.
-  if (index_into_window_ >= outtro_crossfade_begin) {
-    int offset_into_crossfade_buffer =
-        index_into_window_ - outtro_crossfade_begin;
-    CrossfadeFrame(dest,
-                   dest_offset,
-                   crossfade_buffer_.get(),
-                   offset_into_crossfade_buffer,
-                   offset_into_crossfade_buffer);
-  }
-
-  index_into_window_ += copied;
-  return copied;
-}
-
-void AudioRendererAlgorithm::CrossfadeFrame(AudioBus* intro,
-                                            int intro_offset,
-                                            AudioBus* outtro,
-                                            int outtro_offset,
-                                            int fade_offset) {
-  float crossfade_ratio =
-      static_cast<float>(fade_offset) / frames_in_crossfade_;
-  for (int channel = 0; channel < channels_; ++channel) {
-    outtro->channel(channel)[outtro_offset] =
-        (1.0f - crossfade_ratio) * intro->channel(channel)[intro_offset] +
-        (crossfade_ratio) * outtro->channel(channel)[outtro_offset];
-  }
+  int rendered_frames = 0;
+  do {
+    rendered_frames += WriteCompletedFramesTo(
+        requested_frames - rendered_frames, rendered_frames, dest);
+  } while (rendered_frames < requested_frames && RunOneWsolaIteration());
+  return rendered_frames;
 }
 
 void AudioRendererAlgorithm::SetPlaybackRate(float new_rate) {
@@ -350,15 +199,16 @@
   playback_rate_ = new_rate;
   muted_ =
       playback_rate_ < kMinPlaybackRate || playback_rate_ > kMaxPlaybackRate;
-
-  ResetWindow();
 }
 
 void AudioRendererAlgorithm::FlushBuffers() {
-  ResetWindow();
-
   // Clear the queue of decoded packets (releasing the buffers).
   audio_buffer_.Clear();
+  output_time_ = 0.0;
+  search_block_index_ = 0;
+  target_block_index_ = 0;
+  wsola_output_->Zero();
+  num_complete_frames_ = 0;
 }
 
 base::TimeDelta AudioRendererAlgorithm::GetTime() {
@@ -379,4 +229,158 @@
   capacity_ = std::min(2 * capacity_, kMaxBufferSizeInFrames);
 }
 
+bool AudioRendererAlgorithm::CanPerformWsola() const {
+  const int search_block_size = num_candidate_blocks_ + (ola_window_size_ - 1);
+  const int frames = audio_buffer_.frames();
+  return target_block_index_ + ola_window_size_ <= frames &&
+      search_block_index_ + search_block_size <= frames;
+}
+
+bool AudioRendererAlgorithm::RunOneWsolaIteration() {
+  if (!CanPerformWsola())
+    return false;
+
+  GetOptimalBlock();
+
+  // Overlap-and-add.
+  for (int k = 0; k < channels_; ++k) {
+    const float* const ch_opt_frame = optimal_block_->channel(k);
+    float* ch_output = wsola_output_->channel(k) + num_complete_frames_;
+    for (int n = 0; n < ola_hop_size_; ++n) {
+      ch_output[n] = ch_output[n] * ola_window_[ola_hop_size_ + n] +
+          ch_opt_frame[n] * ola_window_[n];
+    }
+
+    // Copy the second half to the output.
+    memcpy(&ch_output[ola_hop_size_], &ch_opt_frame[ola_hop_size_],
+           sizeof(*ch_opt_frame) * ola_hop_size_);
+  }
+
+  num_complete_frames_ += ola_hop_size_;
+  UpdateOutputTime(ola_hop_size_);
+  RemoveOldInputFrames();
+  return true;
+}
+
+void AudioRendererAlgorithm::UpdateOutputTime(double time_change) {
+  output_time_ += time_change;
+  // Center of the search region, in frames.
+  const int search_block_center_index = static_cast<int>(
+      output_time_ * playback_rate_ + 0.5);
+  search_block_index_ = search_block_center_index - search_block_center_offset_;
+}
+
+void AudioRendererAlgorithm::RemoveOldInputFrames() {
+  const int earliest_used_index = std::min(target_block_index_,
+                                           search_block_index_);
+  if (earliest_used_index <= 0)
+    return;  // Nothing to remove.
+
+  // Remove frames from input and adjust indices accordingly.
+  audio_buffer_.SeekFrames(earliest_used_index);
+  target_block_index_ -= earliest_used_index;
+
+  // Adjust output index.
+  double output_time_change = static_cast<double>(earliest_used_index) /
+      playback_rate_;
+  CHECK_GE(output_time_, output_time_change);
+  UpdateOutputTime(-output_time_change);
+}
+
+int AudioRendererAlgorithm::WriteCompletedFramesTo(
+    int requested_frames, int dest_offset, AudioBus* dest) {
+  int rendered_frames = std::min(num_complete_frames_, requested_frames);
+
+  if (rendered_frames == 0)
+    return 0;  // There is nothing to read from |wsola_output_|, return.
+
+  wsola_output_->CopyPartialFramesTo(0, rendered_frames, dest_offset, dest);
+
+  // Remove the frames which are read.
+  int frames_to_move = wsola_output_->frames() - rendered_frames;
+  for (int k = 0; k < channels_; ++k) {
+    float* ch = wsola_output_->channel(k);
+    memmove(ch, &ch[rendered_frames], sizeof(*ch) * frames_to_move);
+  }
+  num_complete_frames_ -= rendered_frames;
+  return rendered_frames;
+}
+
+bool AudioRendererAlgorithm::TargetIsWithinSearchRegion() const {
+  const int search_block_size = num_candidate_blocks_ + (ola_window_size_ - 1);
+
+  return target_block_index_ >= search_block_index_ &&
+      target_block_index_ + ola_window_size_ <=
+      search_block_index_ + search_block_size;
+}
+
+void AudioRendererAlgorithm::GetOptimalBlock() {
+  int optimal_index = 0;
+
+  // An interval around last optimal block which is excluded from the search.
+  // This is to reduce the buzzy sound. The number 160 is rather arbitrary and
+  // derived heuristically.
+  const int kExcludeIntervalLengthFrames = 160;
+  if (TargetIsWithinSearchRegion()) {
+    optimal_index = target_block_index_;
+    PeekAudioWithZeroPrepend(optimal_index, optimal_block_.get());
+  } else {
+    PeekAudioWithZeroPrepend(target_block_index_, target_block_.get());
+    PeekAudioWithZeroPrepend(search_block_index_, search_block_.get());
+    int last_optimal = target_block_index_ - ola_hop_size_ -
+        search_block_index_;
+    internal::Interval exclude_iterval = std::make_pair(
+        last_optimal - kExcludeIntervalLengthFrames / 2,
+        last_optimal + kExcludeIntervalLengthFrames / 2);
+
+    // |optimal_index| is in frames and it is relative to the beginning of the
+    // |search_block_|.
+    optimal_index = internal::OptimalIndex(
+        search_block_.get(), target_block_.get(), exclude_iterval);
+
+    // Translate |index| w.r.t. the beginning of |audio_buffer_| and extract the
+    // optimal block.
+    optimal_index += search_block_index_;
+    PeekAudioWithZeroPrepend(optimal_index, optimal_block_.get());
+
+    // Make a transition from target block to the optimal block if different.
+    // Target block has the best continuation to the current output.
+    // Optimal block is the most similar block to the target, however, it might
+    // introduce some discontinuity when over-lap-added. Therefore, we combine
+    // them for a smoother transition. The length of transition window is twice
+    // as that of the optimal-block which makes it like a weighting function
+    // where target-block has higher weight close to zero (weight of 1 at index
+    // 0) and lower weight close the end.
+    for (int k = 0; k < channels_; ++k) {
+      float* ch_opt = optimal_block_->channel(k);
+      const float* const ch_target = target_block_->channel(k);
+      for (int n = 0; n < ola_window_size_; ++n) {
+        ch_opt[n] = ch_opt[n] * transition_window_[n] + ch_target[n] *
+            transition_window_[ola_window_size_ + n];
+      }
+    }
+  }
+
+  // Next target is one hop ahead of the current optimal.
+  target_block_index_ = optimal_index + ola_hop_size_;
+}
+
+void AudioRendererAlgorithm::PeekAudioWithZeroPrepend(
+    int read_offset_frames, AudioBus* dest) {
+  CHECK_LE(read_offset_frames + dest->frames(), audio_buffer_.frames());
+
+  int write_offset = 0;
+  int num_frames_to_read = dest->frames();
+  if (read_offset_frames < 0) {
+    int num_zero_frames_appended = std::min(-read_offset_frames,
+                                            num_frames_to_read);
+    read_offset_frames = 0;
+    num_frames_to_read -= num_zero_frames_appended;
+    write_offset = num_zero_frames_appended;
+    dest->ZeroFrames(num_zero_frames_appended);
+  }
+  audio_buffer_.PeekFrames(num_frames_to_read, read_offset_frames,
+                           write_offset, dest);
+}
+
 }  // namespace media
diff --git a/media/filters/audio_renderer_algorithm.h b/media/filters/audio_renderer_algorithm.h
index 26790b9..39e4db6 100644
--- a/media/filters/audio_renderer_algorithm.h
+++ b/media/filters/audio_renderer_algorithm.h
@@ -12,11 +12,15 @@
 // This class is *not* thread-safe. Calls to enqueue and retrieve data must be
 // locked if called from multiple threads.
 //
-// AudioRendererAlgorithm uses a simple pitch-preservation algorithm to
-// stretch and compress audio data to meet playback speeds less than and
-// greater than the natural playback of the audio stream.
+// AudioRendererAlgorithm uses the Waveform Similarity Overlap and Add (WSOLA)
+// algorithm to stretch or compress audio data to meet playback speeds less than
+// or greater than the natural playback of the audio stream. The algorithm
+// preserves local properties of the audio, therefore, pitch and harmonics are
+// are preserved. See audio_renderer_algorith.cc for a more elaborate
+// description of the algorithm.
 //
 // Audio at very low or very high playback rates are muted to preserve quality.
+//
 
 #ifndef MEDIA_FILTERS_AUDIO_RENDERER_ALGORITHM_H_
 #define MEDIA_FILTERS_AUDIO_RENDERER_ALGORITHM_H_
@@ -84,46 +88,45 @@
   bool is_muted() { return muted_; }
 
  private:
-  // Fills |dest| with up to |requested_frames| frames of audio data at faster
-  // than normal speed. Returns the number of frames inserted into |dest|. If
-  // not enough data available, returns 0.
-  //
-  // When the audio playback is > 1.0, we use a variant of Overlap-Add to squish
-  // audio output while preserving pitch. Essentially, we play a bit of audio
-  // data at normal speed, then we "fast forward" by dropping the next bit of
-  // audio data, and then we stich the pieces together by crossfading from one
-  // audio chunk to the next.
-  int OutputFasterPlayback(AudioBus* dest,
-                           int dest_offset,
-                           int requested_frames,
-                           int input_step,
-                           int output_step);
+  // Within |search_block_|, find the block of data that is most similar to
+  // |target_block_|, and write it in |optimal_block_|. This method assumes that
+  // there is enough data to perform a search, i.e. |search_block_| and
+  // |target_block_| can be extracted from the available frames.
+  void GetOptimalBlock();
 
-  // Fills |dest| with up to |requested_frames| frames of audio data at slower
-  // than normal speed. Returns the number of frames inserted into |dest|. If
-  // not enough data available, returns 0.
-  //
-  // When the audio playback is < 1.0, we use a variant of Overlap-Add to
-  // stretch audio output while preserving pitch. This works by outputting a
-  // segment of audio data at normal speed. The next audio segment then starts
-  // by repeating some of the audio data from the previous audio segment.
-  // Segments are stiched together by crossfading from one audio chunk to the
-  // next.
-  int OutputSlowerPlayback(AudioBus* dest,
-                           int dest_offset,
-                           int requested_frames,
-                           int input_step,
-                           int output_step);
+  // Read a maximum of |requested_frames| frames from |wsola_output_|. Returns
+  // number of frames actually read.
+  int WriteCompletedFramesTo(
+      int requested_frames, int output_offset, AudioBus* dest);
 
-  // Resets the window state to the start of a new window.
-  void ResetWindow();
+  // Fill |dest| with frames from |audio_buffer_| starting from frame
+  // |read_offset_frames|. |dest| is expected to have the same number of
+  // channels as |audio_buffer_|. A negative offset, i.e.
+  // |read_offset_frames| < 0, is accepted assuming that |audio_buffer| is zero
+  // for negative indices. This might happen for few first frames. This method
+  // assumes there is enough frames to fill |dest|, i.e. |read_offset_frames| +
+  // |dest->frames()| does not extend to future.
+  void PeekAudioWithZeroPrepend(int read_offset_frames, AudioBus* dest);
 
-  // Does a linear crossfade from |intro| into |outtro| for one frame.
-  void CrossfadeFrame(AudioBus* intro,
-                      int intro_offset,
-                      AudioBus* outtro,
-                      int outtro_offset,
-                      int fade_offset);
+  // Run one iteration of WSOLA, if there are sufficient frames. This will
+  // overlap-and-add one block to |wsola_output_|, hence, |num_complete_frames_|
+  // is incremented by |ola_hop_size_|.
+  bool RunOneWsolaIteration();
+
+  // Seek |audio_buffer_| forward to remove frames from input that are not used
+  // any more. State of the WSOLA will be updated accordingly.
+  void RemoveOldInputFrames();
+
+  // Update |output_time_| by |time_change|. In turn |search_block_index_| is
+  // updated.
+  void UpdateOutputTime(double time_change);
+
+  // Is |target_block_| fully within |search_block_|? If so, we don't need to
+  // perform the search.
+  bool TargetIsWithinSearchRegion() const;
+
+  // Do we have enough data to perform one round of WSOLA?
+  bool CanPerformWsola() const;
 
   // Number of channels in audio stream.
   int channels_;
@@ -137,32 +140,79 @@
   // Buffered audio data.
   AudioBufferQueue audio_buffer_;
 
-  // Length for crossfade in frames.
-  int frames_in_crossfade_;
-
-  // The current location in the audio window, between 0 and |window_size_|.
-  // When |index_into_window_| reaches |window_size_|, the window resets.
-  // Indexed by frame.
-  int index_into_window_;
-
-  // The frame number in the crossfade.
-  int crossfade_frame_number_;
-
   // True if the audio should be muted.
   bool muted_;
 
   // If muted, keep track of partial frames that should have been skipped over.
   double muted_partial_frame_;
 
-  // Temporary buffer to hold crossfade data.
-  scoped_ptr<AudioBus> crossfade_buffer_;
-
-  // Window size, in frames (calculated from audio properties).
-  int window_size_;
-
   // How many frames to have in the queue before we report the queue is full.
   int capacity_;
 
+  // Book keeping of the current time of generated audio, in frames. This
+  // should be appropriately updated when out samples are generated, regardless
+  // of whether we push samples out when FillBuffer() is called or we store
+  // audio in |wsola_output_| for the subsequent calls to FillBuffer().
+  // Furthermore, if samples from |audio_buffer_| are evicted then this
+  // member variable should be updated based on |playback_rate_|.
+  // Note that this member should be updated ONLY by calling UpdateOutputTime(),
+  // so that |search_block_index_| is update accordingly.
+  double output_time_;
+
+  // The offset of the center frame of |search_block_| w.r.t. its first frame.
+  int search_block_center_offset_;
+
+  // Index of the beginning of the |search_block_|, in frames.
+  int search_block_index_;
+
+  // Number of Blocks to search to find the most similar one to the target
+  // frame.
+  int num_candidate_blocks_;
+
+  // Index of the beginning of the target block, counted in frames.
+  int target_block_index_;
+
+  // Overlap-and-add window size in frames.
+  int ola_window_size_;
+
+  // The hop size of overlap-and-add in frames. This implementation assumes 50%
+  // overlap-and-add.
+  int ola_hop_size_;
+
+  // Number of frames in |wsola_output_| that overlap-and-add is completed for
+  // them and can be copied to output if FillBuffer() is called. It also
+  // specifies the index where the next WSOLA window has to overlap-and-add.
+  int num_complete_frames_;
+
+  // This stores a part of the output that is created but couldn't be rendered.
+  // Output is generated frame-by-frame which at some point might exceed the
+  // number of requested samples. Furthermore, due to overlap-and-add,
+  // the last half-window of the output is incomplete, which is stored in this
+  // buffer.
+  scoped_ptr<AudioBus> wsola_output_;
+
+  // Overlap-and-add window.
+  scoped_ptr<float[]> ola_window_;
+
+  // Transition window, used to update |optimal_block_| by a weighted sum of
+  // |optimal_block_| and |target_block_|.
+  scoped_ptr<float[]> transition_window_;
+
+  // Auxiliary variables to avoid allocation in every iteration.
+
+  // Stores the optimal block in every iteration. This is the most
+  // similar block to |target_block_| within |search_block_| and it is
+  // overlap-and-added to |wsola_output_|.
+  scoped_ptr<AudioBus> optimal_block_;
+
+  // A block of data that search is performed over to find the |optimal_block_|.
+  scoped_ptr<AudioBus> search_block_;
+
+  // Stores the target block, denoted as |target| above. |search_block_| is
+  // searched for a block (|optimal_block_|) that is most similar to
+  // |target_block_|.
+  scoped_ptr<AudioBus> target_block_;
+
   DISALLOW_COPY_AND_ASSIGN(AudioRendererAlgorithm);
 };
 
diff --git a/media/filters/audio_renderer_algorithm_unittest.cc b/media/filters/audio_renderer_algorithm_unittest.cc
index d5119c0..649e058 100644
--- a/media/filters/audio_renderer_algorithm_unittest.cc
+++ b/media/filters/audio_renderer_algorithm_unittest.cc
@@ -8,16 +8,20 @@
 // correct rate.  We always pass in a very large destination buffer with the
 // expectation that FillBuffer() will fill as much as it can but no more.
 
+#include <algorithm>  // For std::min().
 #include <cmath>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
 #include "media/base/audio_buffer.h"
 #include "media/base/audio_bus.h"
 #include "media/base/buffers.h"
 #include "media/base/channel_layout.h"
 #include "media/base/test_helpers.h"
 #include "media/filters/audio_renderer_algorithm.h"
+#include "media/filters/wsola_internals.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace media {
@@ -25,6 +29,41 @@
 static const int kFrameSize = 250;
 static const int kSamplesPerSecond = 3000;
 static const SampleFormat kSampleFormat = kSampleFormatS16;
+static const int kOutputDurationInSec = 10;
+
+static void FillWithSquarePulseTrain(
+    int half_pulse_width, int offset, int num_samples, float* data) {
+  ASSERT_GE(offset, 0);
+  ASSERT_LE(offset, num_samples);
+
+  // Fill backward from |offset| - 1 toward zero, starting with -1, alternating
+  // between -1 and 1 every |pulse_width| samples.
+  float pulse = -1.0f;
+  for (int n = offset - 1, k = 0; n >= 0; --n, ++k) {
+    if (k >= half_pulse_width) {
+      pulse = -pulse;
+      k = 0;
+    }
+    data[n] = pulse;
+  }
+
+  // Fill forward from |offset| towards the end, starting with 1, alternating
+  // between 1 and -1 every |pulse_width| samples.
+  pulse = 1.0f;
+  for (int n = offset, k = 0; n < num_samples; ++n, ++k) {
+    if (k >= half_pulse_width) {
+      pulse = -pulse;
+      k = 0;
+    }
+    data[n] = pulse;
+  }
+}
+
+static void FillWithSquarePulseTrain(
+    int half_pulse_width, int offset, int channel, AudioBus* audio_bus) {
+  FillWithSquarePulseTrain(half_pulse_width, offset, audio_bus->frames(),
+                           audio_bus->channel(channel));
+}
 
 class AudioRendererAlgorithmTest : public testing::Test {
  public:
@@ -118,7 +157,8 @@
 
   void TestPlaybackRate(double playback_rate) {
     const int kDefaultBufferSize = algorithm_.samples_per_second() / 100;
-    const int kDefaultFramesRequested = 2 * algorithm_.samples_per_second();
+    const int kDefaultFramesRequested = kOutputDurationInSec *
+        algorithm_.samples_per_second();
 
     TestPlaybackRate(
         playback_rate, kDefaultBufferSize, kDefaultFramesRequested);
@@ -141,12 +181,21 @@
     }
 
     int frames_remaining = total_frames_requested;
+    bool first_fill_buffer = true;
     while (frames_remaining > 0) {
       int frames_requested = std::min(buffer_size_in_frames, frames_remaining);
       int frames_written = algorithm_.FillBuffer(bus.get(), frames_requested);
       ASSERT_GT(frames_written, 0) << "Requested: " << frames_requested
                                    << ", playing at " << playback_rate;
-      CheckFakeData(bus.get(), frames_written);
+
+      // Do not check data if it is first pull out and only one frame written.
+      // The very first frame out of WSOLA is always zero because of
+      // overlap-and-add window, which is zero for the first sample. Therefore,
+      // if at very first buffer-fill only one frame is written, that is zero
+      // which might cause exception in CheckFakeData().
+      if (!first_fill_buffer || frames_written > 1)
+        CheckFakeData(bus.get(), frames_written);
+      first_fill_buffer = false;
       frames_remaining -= frames_written;
 
       FillAlgorithmQueue();
@@ -175,6 +224,79 @@
     EXPECT_NEAR(playback_rate, actual_playback_rate, playback_rate / 100.0);
   }
 
+  void WsolaTest(float playback_rate) {
+    const int kSampleRateHz = 48000;
+    const media::ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO;
+    const int kBytesPerSample = 2;
+    const int kNumFrames = kSampleRateHz / 100;  // 10 milliseconds.
+
+    channels_ = ChannelLayoutToChannelCount(kChannelLayout);
+    AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout,
+                           kSampleRateHz, kBytesPerSample * 8, kNumFrames);
+    algorithm_.Initialize(playback_rate, params);
+
+    // A pulse is 6 milliseconds (even number of samples).
+    const int kPulseWidthSamples = 6 * kSampleRateHz / 1000;
+    const int kHalfPulseWidthSamples = kPulseWidthSamples / 2;
+
+    // For the ease of implementation get 1 frame every call to FillBuffer().
+    scoped_ptr<AudioBus> output = AudioBus::Create(channels_, 1);
+
+    // Input buffer to inject pulses.
+    scoped_refptr<AudioBuffer> input = AudioBuffer::CreateBuffer(
+        kSampleFormatPlanarF32, channels_, kPulseWidthSamples);
+
+    const std::vector<uint8*>& channel_data = input->channel_data();
+
+    // Fill |input| channels.
+    FillWithSquarePulseTrain(kHalfPulseWidthSamples, 0, kPulseWidthSamples,
+                             reinterpret_cast<float*>(channel_data[0]));
+    FillWithSquarePulseTrain(kHalfPulseWidthSamples, kHalfPulseWidthSamples,
+                             kPulseWidthSamples,
+                             reinterpret_cast<float*>(channel_data[1]));
+
+    // A buffer for the output until a complete pulse is created. Then
+    // reference pulse is compared with this buffer.
+    scoped_ptr<AudioBus> pulse_buffer = AudioBus::Create(
+        channels_, kPulseWidthSamples);
+
+    const float kTolerance = 0.000001f;
+    // Equivalent of 4 seconds.
+    const int kNumRequestedPulses = kSampleRateHz * 4 / kPulseWidthSamples;
+    for (int n = 0; n < kNumRequestedPulses; ++n) {
+      int num_buffered_frames = 0;
+      while (num_buffered_frames < kPulseWidthSamples) {
+        int num_samples = algorithm_.FillBuffer(output.get(), 1);
+        ASSERT_LE(num_samples, 1);
+        if (num_samples > 0) {
+          output->CopyPartialFramesTo(0, num_samples, num_buffered_frames,
+                                      pulse_buffer.get());
+          num_buffered_frames++;
+        } else {
+          algorithm_.EnqueueBuffer(input);
+        }
+      }
+
+      // Pulses in the first half of WSOLA AOL frame are not constructed
+      // perfectly. Do not check them.
+      if (n > 3) {
+         for (int m = 0; m < channels_; ++m) {
+          const float* pulse_ch = pulse_buffer->channel(m);
+
+          // Because of overlap-and-add we might have round off error.
+          for (int k = 0; k < kPulseWidthSamples; ++k) {
+            ASSERT_NEAR(reinterpret_cast<float*>(channel_data[m])[k],
+                        pulse_ch[k], kTolerance) << " loop " << n
+                                << " channel/sample " << m << "/" << k;
+          }
+        }
+      }
+
+      // Zero out the buffer to be sure the next comparison is relevant.
+      pulse_buffer->Zero();
+    }
+  }
+
  protected:
   AudioRendererAlgorithm algorithm_;
   int frames_enqueued_;
@@ -270,7 +392,7 @@
 TEST_F(AudioRendererAlgorithmTest, FillBuffer_SmallBufferSize) {
   Initialize();
   static const int kBufferSizeInFrames = 1;
-  static const int kFramesRequested = 2 * kSamplesPerSecond;
+  static const int kFramesRequested = kOutputDurationInSec * kSamplesPerSecond;
   TestPlaybackRate(1.0, kBufferSizeInFrames, kFramesRequested);
   TestPlaybackRate(0.5, kBufferSizeInFrames, kFramesRequested);
   TestPlaybackRate(1.5, kBufferSizeInFrames, kFramesRequested);
@@ -297,4 +419,195 @@
   TestPlaybackRate(1.5);
 }
 
+TEST_F(AudioRendererAlgorithmTest, DotProduct) {
+  const int kChannels = 3;
+  const int kFrames = 20;
+  const int kHalfPulseWidth = 2;
+
+  scoped_ptr<AudioBus> a = AudioBus::Create(kChannels, kFrames);
+  scoped_ptr<AudioBus> b = AudioBus::Create(kChannels, kFrames);
+
+  scoped_ptr<float[]> dot_prod(new float[kChannels]);
+
+  FillWithSquarePulseTrain(kHalfPulseWidth, 0, 0, a.get());
+  FillWithSquarePulseTrain(kHalfPulseWidth, 1, 1, a.get());
+  FillWithSquarePulseTrain(kHalfPulseWidth, 2, 2, a.get());
+
+  FillWithSquarePulseTrain(kHalfPulseWidth, 0, 0, b.get());
+  FillWithSquarePulseTrain(kHalfPulseWidth, 0, 1, b.get());
+  FillWithSquarePulseTrain(kHalfPulseWidth, 0, 2, b.get());
+
+  internal::MultiChannelDotProduct(a.get(), 0, b.get(), 0, kFrames,
+                                   dot_prod.get());
+
+  EXPECT_FLOAT_EQ(kFrames, dot_prod[0]);
+  EXPECT_FLOAT_EQ(0, dot_prod[1]);
+  EXPECT_FLOAT_EQ(-kFrames, dot_prod[2]);
+
+  internal::MultiChannelDotProduct(a.get(), 4, b.get(), 8, kFrames / 2,
+                                   dot_prod.get());
+
+  EXPECT_FLOAT_EQ(kFrames / 2, dot_prod[0]);
+  EXPECT_FLOAT_EQ(0, dot_prod[1]);
+  EXPECT_FLOAT_EQ(-kFrames / 2, dot_prod[2]);
+}
+
+TEST_F(AudioRendererAlgorithmTest, MovingBlockEnergy) {
+  const int kChannels = 2;
+  const int kFrames = 20;
+  const int kFramesPerBlock = 3;
+  const int kNumBlocks = kFrames - (kFramesPerBlock - 1);
+  scoped_ptr<AudioBus> a = AudioBus::Create(kChannels, kFrames);
+  scoped_ptr<float[]> energies(new float[kChannels * kNumBlocks]);
+  float* ch_left = a->channel(0);
+  float* ch_right = a->channel(1);
+
+  // Fill up both channels.
+  for (int n = 0; n < kFrames; ++n) {
+    ch_left[n] = n;
+    ch_right[n] = kFrames - 1 - n;
+  }
+
+  internal::MultiChannelMovingBlockEnergies(a.get(), kFramesPerBlock,
+                                            energies.get());
+
+  // Check if the energy of candidate blocks of each channel computed correctly.
+  for (int n = 0; n < kNumBlocks; ++n) {
+    float expected_energy = 0;
+    for (int k = 0; k < kFramesPerBlock; ++k)
+      expected_energy += ch_left[n + k] * ch_left[n + k];
+
+    // Left (first) channel.
+    EXPECT_FLOAT_EQ(expected_energy, energies[2 * n]);
+
+    expected_energy = 0;
+    for (int k = 0; k < kFramesPerBlock; ++k)
+      expected_energy += ch_right[n + k] * ch_right[n + k];
+
+    // Second (right) channel.
+    EXPECT_FLOAT_EQ(expected_energy, energies[2 * n + 1]);
+  }
+}
+
+TEST_F(AudioRendererAlgorithmTest, FullAndDecimatedSearch) {
+  const int kFramesInSearchRegion = 12;
+  const int kChannels = 2;
+  float ch_0[] = {
+      0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+  float ch_1[] = {
+      0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.1f, 1.0f, 0.1f, 0.0f, 0.0f };
+  ASSERT_EQ(sizeof(ch_0), sizeof(ch_1));
+  ASSERT_EQ(static_cast<size_t>(kFramesInSearchRegion),
+            sizeof(ch_0) / sizeof(*ch_0));
+  scoped_ptr<AudioBus> search_region = AudioBus::Create(kChannels,
+                                                        kFramesInSearchRegion);
+  float* ch = search_region->channel(0);
+  memcpy(ch, ch_0, sizeof(float) * kFramesInSearchRegion);
+  ch = search_region->channel(1);
+  memcpy(ch, ch_1, sizeof(float) * kFramesInSearchRegion);
+
+  const int kFramePerBlock = 4;
+  float target_0[] = { 1.0f, 1.0f, 1.0f, 0.0f };
+  float target_1[] = { 0.0f, 1.0f, 0.1f, 1.0f };
+  ASSERT_EQ(sizeof(target_0), sizeof(target_1));
+  ASSERT_EQ(static_cast<size_t>(kFramePerBlock),
+            sizeof(target_0) / sizeof(*target_0));
+
+  scoped_ptr<AudioBus> target = AudioBus::Create(kChannels,
+                                                 kFramePerBlock);
+  ch = target->channel(0);
+  memcpy(ch, target_0, sizeof(float) * kFramePerBlock);
+  ch = target->channel(1);
+  memcpy(ch, target_1, sizeof(float) * kFramePerBlock);
+
+  scoped_ptr<float[]> energy_target(new float[kChannels]);
+
+  internal::MultiChannelDotProduct(target.get(), 0, target.get(), 0,
+                                   kFramePerBlock, energy_target.get());
+
+  ASSERT_EQ(3.f, energy_target[0]);
+  ASSERT_EQ(2.01f, energy_target[1]);
+
+  const int kNumCandidBlocks = kFramesInSearchRegion - (kFramePerBlock - 1);
+  scoped_ptr<float[]> energy_candid_blocks(new float[kNumCandidBlocks *
+                                                     kChannels]);
+
+  internal::MultiChannelMovingBlockEnergies(
+      search_region.get(), kFramePerBlock, energy_candid_blocks.get());
+
+  // Check the energy of the candidate blocks of the first channel.
+  ASSERT_FLOAT_EQ(0, energy_candid_blocks[0]);
+  ASSERT_FLOAT_EQ(0, energy_candid_blocks[2]);
+  ASSERT_FLOAT_EQ(1, energy_candid_blocks[4]);
+  ASSERT_FLOAT_EQ(2, energy_candid_blocks[6]);
+  ASSERT_FLOAT_EQ(3, energy_candid_blocks[8]);
+  ASSERT_FLOAT_EQ(3, energy_candid_blocks[10]);
+  ASSERT_FLOAT_EQ(2, energy_candid_blocks[12]);
+  ASSERT_FLOAT_EQ(1, energy_candid_blocks[14]);
+  ASSERT_FLOAT_EQ(0, energy_candid_blocks[16]);
+
+  // Check the energy of the candidate blocks of the second channel.
+  ASSERT_FLOAT_EQ(0, energy_candid_blocks[1]);
+  ASSERT_FLOAT_EQ(0, energy_candid_blocks[3]);
+  ASSERT_FLOAT_EQ(0, energy_candid_blocks[5]);
+  ASSERT_FLOAT_EQ(0, energy_candid_blocks[7]);
+  ASSERT_FLOAT_EQ(0.01f, energy_candid_blocks[9]);
+  ASSERT_FLOAT_EQ(1.01f, energy_candid_blocks[11]);
+  ASSERT_FLOAT_EQ(1.02f, energy_candid_blocks[13]);
+  ASSERT_FLOAT_EQ(1.02f, energy_candid_blocks[15]);
+  ASSERT_FLOAT_EQ(1.01f, energy_candid_blocks[17]);
+
+  // An interval which is of no effect.
+  internal::Interval exclude_interval = std::make_pair(-100, -10);
+  EXPECT_EQ(5, internal::FullSearch(
+      0, kNumCandidBlocks - 1, exclude_interval, target.get(),
+      search_region.get(), energy_target.get(), energy_candid_blocks.get()));
+
+  // Exclude the the best match.
+  exclude_interval = std::make_pair(2, 5);
+  EXPECT_EQ(7, internal::FullSearch(
+      0, kNumCandidBlocks - 1, exclude_interval, target.get(),
+      search_region.get(), energy_target.get(), energy_candid_blocks.get()));
+
+  // An interval which is of no effect.
+  exclude_interval = std::make_pair(-100, -10);
+  EXPECT_EQ(4, internal::DecimatedSearch(
+      4, exclude_interval, target.get(), search_region.get(),
+      energy_target.get(), energy_candid_blocks.get()));
+
+  EXPECT_EQ(5, internal::OptimalIndex(search_region.get(), target.get(),
+                                      exclude_interval));
+}
+
+TEST_F(AudioRendererAlgorithmTest, CubicInterpolation) {
+  // Arbitrary coefficients.
+  const float kA = 0.7f;
+  const float kB = 1.2f;
+  const float kC = 0.8f;
+
+  float y_values[3];
+  y_values[0] = kA - kB + kC;
+  y_values[1] = kC;
+  y_values[2] = kA + kB + kC;
+
+  float extremum;
+  float extremum_value;
+
+  internal::CubicInterpolation(y_values, &extremum, &extremum_value);
+
+  float x_star = -kB / (2.f * kA);
+  float y_star = kA * x_star * x_star + kB * x_star + kC;
+
+  EXPECT_FLOAT_EQ(x_star, extremum);
+  EXPECT_FLOAT_EQ(y_star, extremum_value);
+}
+
+TEST_F(AudioRendererAlgorithmTest, WsolaSlowdown) {
+  WsolaTest(0.6f);
+}
+
+TEST_F(AudioRendererAlgorithmTest, WsolaSpeedup) {
+  WsolaTest(1.6f);
+}
+
 }  // namespace media
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index 6f45a1f..a4a67f2 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -874,7 +874,6 @@
 ChunkDemuxer::Status ChunkDemuxer::AddId(const std::string& id,
                                          const std::string& type,
                                          std::vector<std::string>& codecs) {
-  DCHECK_GT(codecs.size(), 0u);
   base::AutoLock auto_lock(lock_);
 
   if ((state_ != WAITING_FOR_INIT && state_ != INITIALIZING) || IsValidId(id))
diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h
index 0a2f67b..e7f6cae 100644
--- a/media/filters/chunk_demuxer.h
+++ b/media/filters/chunk_demuxer.h
@@ -34,10 +34,6 @@
     kReachedIdLimit,  // Reached ID limit. We can't handle any more IDs.
   };
 
-  typedef base::Callback<void(const std::string& type,
-                              scoped_ptr<uint8[]> init_data,
-                              int init_data_size)> NeedKeyCB;
-
   // |open_cb| Run when Initialize() is called to signal that the demuxer
   //   is ready to receive media data via AppenData().
   // |need_key_cb| Run when the demuxer determines that an encryption key is
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index 7f88a61..3d9b26f 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -157,7 +157,7 @@
   void CreateNewDemuxer() {
     base::Closure open_cb =
         base::Bind(&ChunkDemuxerTest::DemuxerOpened, base::Unretained(this));
-    ChunkDemuxer::NeedKeyCB need_key_cb =
+    Demuxer::NeedKeyCB need_key_cb =
         base::Bind(&ChunkDemuxerTest::DemuxerNeedKey, base::Unretained(this));
     AddTextTrackCB add_text_track_cb =
         base::Bind(&ChunkDemuxerTest::OnTextTrack, base::Unretained(this));
@@ -856,8 +856,9 @@
   MOCK_METHOD3(NeedKeyMock, void(const std::string& type,
                                  const uint8* init_data, int init_data_size));
   void DemuxerNeedKey(const std::string& type,
-                      scoped_ptr<uint8[]> init_data, int init_data_size) {
-    NeedKeyMock(type, init_data.get(), init_data_size);
+                      const std::vector<uint8>& init_data) {
+    const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
+    NeedKeyMock(type, init_data_ptr, init_data.size());
   }
 
   scoped_ptr<TextTrack> OnTextTrack(TextKind kind,
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index e95dcef..30cb0c0 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -287,7 +287,7 @@
 FFmpegDemuxer::FFmpegDemuxer(
     const scoped_refptr<base::MessageLoopProxy>& message_loop,
     DataSource* data_source,
-    const FFmpegNeedKeyCB& need_key_cb,
+    const NeedKeyCB& need_key_cb,
     const scoped_refptr<MediaLog>& media_log)
     : host_(NULL),
       message_loop_(message_loop),
@@ -812,10 +812,9 @@
 
 void FFmpegDemuxer::FireNeedKey(const std::string& init_data_type,
                                 const std::string& encryption_key_id) {
-  int key_id_size = encryption_key_id.size();
-  scoped_ptr<uint8[]> key_id_local(new uint8[key_id_size]);
-  memcpy(key_id_local.get(), encryption_key_id.data(), key_id_size);
-  need_key_cb_.Run(init_data_type, key_id_local.Pass(), key_id_size);
+  std::vector<uint8> key_id_local(encryption_key_id.begin(),
+                                  encryption_key_id.end());
+  need_key_cb_.Run(init_data_type, key_id_local);
 }
 
 void FFmpegDemuxer::NotifyCapacityAvailable() {
diff --git a/media/filters/ffmpeg_demuxer.h b/media/filters/ffmpeg_demuxer.h
index 92b3eab..7304bea 100644
--- a/media/filters/ffmpeg_demuxer.h
+++ b/media/filters/ffmpeg_demuxer.h
@@ -44,14 +44,6 @@
 
 namespace media {
 
-// A new potentially encrypted stream has been parsed.
-// First parameter - The type of initialization data.
-// Second parameter - The initialization data associated with the stream.
-// Third parameter - Number of bytes of the initialization data.
-typedef base::Callback<void(const std::string& type,
-                            scoped_ptr<uint8[]> init_data,
-                            int init_data_size)> FFmpegNeedKeyCB;
-
 class MediaLog;
 class FFmpegDemuxer;
 class FFmpegGlue;
@@ -138,7 +130,7 @@
  public:
   FFmpegDemuxer(const scoped_refptr<base::MessageLoopProxy>& message_loop,
                 DataSource* data_source,
-                const FFmpegNeedKeyCB& need_key_cb,
+                const NeedKeyCB& need_key_cb,
                 const scoped_refptr<MediaLog>& media_log);
   virtual ~FFmpegDemuxer();
 
@@ -250,7 +242,7 @@
   BlockingUrlProtocol url_protocol_;
   scoped_ptr<FFmpegGlue> glue_;
 
-  const FFmpegNeedKeyCB need_key_cb_;
+  const NeedKeyCB need_key_cb_;
 
   DISALLOW_COPY_AND_ASSIGN(FFmpegDemuxer);
 };
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
index 4fa2ab2..f5b0e97 100644
--- a/media/filters/ffmpeg_demuxer_unittest.cc
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -81,7 +81,7 @@
 
     CreateDataSource(name);
 
-    media::FFmpegNeedKeyCB need_key_cb =
+    Demuxer::NeedKeyCB need_key_cb =
         base::Bind(&FFmpegDemuxerTest::NeedKeyCB, base::Unretained(this));
     demuxer_.reset(new FFmpegDemuxer(message_loop_.message_loop_proxy(),
                                      data_source_.get(),
@@ -136,8 +136,9 @@
   MOCK_METHOD3(NeedKeyCBMock, void(const std::string& type,
                                    const uint8* init_data, int init_data_size));
   void NeedKeyCB(const std::string& type,
-                 scoped_ptr<uint8[]> init_data, int init_data_size) {
-    NeedKeyCBMock(type, init_data.get(), init_data_size);
+                 const std::vector<uint8>& init_data) {
+    const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
+    NeedKeyCBMock(type, init_data_ptr, init_data.size());
   }
 
   // Accessor to demuxer internals.
diff --git a/media/filters/pipeline_integration_test.cc b/media/filters/pipeline_integration_test.cc
index fe5ab5c..e79f631 100644
--- a/media/filters/pipeline_integration_test.cc
+++ b/media/filters/pipeline_integration_test.cc
@@ -86,7 +86,7 @@
 
     virtual void NeedKey(const std::string& session_id,
                          const std::string& type,
-                         scoped_ptr<uint8[]> init_data, int init_data_length,
+                         const std::vector<uint8>& init_data,
                          AesDecryptor* decryptor) = 0;
   };
 
@@ -123,9 +123,8 @@
 
   void NeedKey(const std::string& session_id,
                const std::string& type,
-               scoped_ptr<uint8[]> init_data, int init_data_length) {
-    app_->NeedKey(session_id, type, init_data.Pass(), init_data_length,
-                  &decryptor_);
+               const std::vector<uint8>& init_data) {
+    app_->NeedKey(session_id, type, init_data, &decryptor_);
   }
 
  private:
@@ -151,7 +150,7 @@
 
   virtual void NeedKey(const std::string& session_id,
                        const std::string& type,
-                       scoped_ptr<uint8[]> init_data, int init_data_length,
+                       const std::vector<uint8>& init_data,
                        AesDecryptor* decryptor) OVERRIDE {
     current_session_id_ = session_id;
 
@@ -165,8 +164,8 @@
     // Clear Key really needs the key ID in |init_data|. For WebM, they are the
     // same, but this is not the case for ISO CENC. Therefore, provide the
     // correct key ID.
-    const uint8* key_id = init_data.get();
-    int key_id_length = init_data_length;
+    const uint8* key_id = init_data.empty() ? NULL : &init_data[0];
+    size_t key_id_length = init_data.size();
     if (type == kMP4AudioType || type == kMP4VideoType) {
       key_id = kKeyId;
       key_id_length = arraysize(kKeyId);
@@ -197,7 +196,7 @@
 
   virtual void NeedKey(const std::string& session_id,
                        const std::string& type,
-                       scoped_ptr<uint8[]> init_data, int init_data_length,
+                       const std::vector<uint8>& init_data,
                        AesDecryptor* decryptor) OVERRIDE {
   }
 };
@@ -298,11 +297,10 @@
   }
 
   void DemuxerNeedKey(const std::string& type,
-                      scoped_ptr<uint8[]> init_data, int init_data_size) {
-    DCHECK(init_data.get());
-    DCHECK_GT(init_data_size, 0);
+                      const std::vector<uint8>& init_data) {
+    DCHECK(!init_data.empty());
     CHECK(!need_key_cb_.is_null());
-    need_key_cb_.Run(std::string(), type, init_data.Pass(), init_data_size);
+    need_key_cb_.Run(std::string(), type, init_data);
   }
 
   scoped_ptr<TextTrack> OnTextTrack(TextKind kind,
diff --git a/media/filters/pipeline_integration_test_base.cc b/media/filters/pipeline_integration_test_base.cc
index 31ae8ad..3f0910a 100644
--- a/media/filters/pipeline_integration_test_base.cc
+++ b/media/filters/pipeline_integration_test_base.cc
@@ -65,12 +65,10 @@
 
 void PipelineIntegrationTestBase::DemuxerNeedKeyCB(
     const std::string& type,
-    scoped_ptr<uint8[]> init_data,
-    int init_data_size) {
-  DCHECK(init_data.get());
-  DCHECK_GT(init_data_size, 0);
+    const std::vector<uint8>& init_data) {
+  DCHECK(!init_data.empty());
   CHECK(!need_key_cb_.is_null());
-  need_key_cb_.Run(std::string(), type, init_data.Pass(), init_data_size);
+  need_key_cb_.Run(std::string(), type, init_data);
 }
 
 void PipelineIntegrationTestBase::OnEnded() {
@@ -212,7 +210,7 @@
   CHECK(file_data_source->Initialize(file_path));
   data_source_.reset(file_data_source);
 
-  media::FFmpegNeedKeyCB need_key_cb = base::Bind(
+  Demuxer::NeedKeyCB need_key_cb = base::Bind(
       &PipelineIntegrationTestBase::DemuxerNeedKeyCB, base::Unretained(this));
   scoped_ptr<Demuxer> demuxer(
       new FFmpegDemuxer(message_loop_.message_loop_proxy(),
diff --git a/media/filters/pipeline_integration_test_base.h b/media/filters/pipeline_integration_test_base.h
index f91d8c4..ade9ad6 100644
--- a/media/filters/pipeline_integration_test_base.h
+++ b/media/filters/pipeline_integration_test_base.h
@@ -103,8 +103,8 @@
   void OnStatusCallback(PipelineStatus status);
   PipelineStatusCB QuitOnStatusCB(PipelineStatus expected_status);
   void DemuxerNeedKeyCB(const std::string& type,
-                        scoped_ptr<uint8[]> init_data, int init_data_size);
-  void set_need_key_cb(const NeedKeyCB& need_key_cb) {
+                        const std::vector<uint8>& init_data);
+    void set_need_key_cb(const NeedKeyCB& need_key_cb) {
     need_key_cb_ = need_key_cb;
   }
 
diff --git a/media/filters/skcanvas_video_renderer.cc b/media/filters/skcanvas_video_renderer.cc
index 5a889e3..f0bf13d 100644
--- a/media/filters/skcanvas_video_renderer.cc
+++ b/media/filters/skcanvas_video_renderer.cc
@@ -56,7 +56,7 @@
       SkScalarNearlyZero(total_matrix.getSkewY()) &&
       total_matrix.getScaleX() > 0 &&
       total_matrix.getScaleY() > 0) {
-    SkDevice* device = canvas->getDevice();
+    SkBaseDevice* device = canvas->getDevice();
     const SkBitmap::Config config = device->config();
 
     if (config == SkBitmap::kARGB_8888_Config && device->isOpaque()) {
diff --git a/media/filters/skcanvas_video_renderer_unittest.cc b/media/filters/skcanvas_video_renderer_unittest.cc
index e5eff5b..1550dac 100644
--- a/media/filters/skcanvas_video_renderer_unittest.cc
+++ b/media/filters/skcanvas_video_renderer_unittest.cc
@@ -5,8 +5,8 @@
 #include "media/base/video_frame.h"
 #include "media/base/video_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
 #include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkDevice.h"
 #include "media/filters/skcanvas_video_renderer.h"
 
 using media::VideoFrame;
@@ -75,9 +75,9 @@
   scoped_refptr<VideoFrame> smaller_frame_;
   scoped_refptr<VideoFrame> cropped_frame_;
 
-  SkDevice fast_path_device_;
+  SkBitmapDevice fast_path_device_;
   SkCanvas fast_path_canvas_;
-  SkDevice slow_path_device_;
+  SkBitmapDevice slow_path_device_;
   SkCanvas slow_path_canvas_;
 
   DISALLOW_COPY_AND_ASSIGN(SkCanvasVideoRendererTest);
diff --git a/media/filters/stream_parser_factory.cc b/media/filters/stream_parser_factory.cc
index 25db8f0..c158769 100644
--- a/media/filters/stream_parser_factory.cc
+++ b/media/filters/stream_parser_factory.cc
@@ -20,7 +20,7 @@
 namespace media {
 
 typedef bool (*CodecIDValidatorFunction)(
-    const std::string& codecs_id, const media::LogCB& log_cb);
+    const std::string& codecs_id, const LogCB& log_cb);
 
 struct CodecInfo {
   enum Type {
@@ -46,9 +46,9 @@
   HistogramTag tag;
 };
 
-typedef media::StreamParser* (*ParserFactoryFunction)(
+typedef StreamParser* (*ParserFactoryFunction)(
     const std::vector<std::string>& codecs,
-    const media::LogCB& log_cb);
+    const LogCB& log_cb);
 
 struct SupportedTypeInfo {
   const char* type;
@@ -75,10 +75,10 @@
   NULL
 };
 
-static media::StreamParser* BuildWebMParser(
+static StreamParser* BuildWebMParser(
     const std::vector<std::string>& codecs,
-    const media::LogCB& log_cb) {
-  return new media::WebMStreamParser();
+    const LogCB& log_cb) {
+  return new WebMStreamParser();
 }
 
 #if defined(USE_PROPRIETARY_CODECS)
@@ -87,7 +87,7 @@
 static const int kAACSBRObjectType = 5;
 
 static int GetMP4AudioObjectType(const std::string& codec_id,
-                                 const media::LogCB& log_cb) {
+                                 const LogCB& log_cb) {
   int audio_object_type;
   std::vector<std::string> tokens;
   if (Tokenize(codec_id, ".", &tokens) != 3 ||
@@ -101,8 +101,7 @@
   return audio_object_type;
 }
 
-bool ValidateMP4ACodecID(const std::string& codec_id,
-                         const media::LogCB& log_cb) {
+bool ValidateMP4ACodecID(const std::string& codec_id, const LogCB& log_cb) {
   int audio_object_type = GetMP4AudioObjectType(codec_id, log_cb);
   if (audio_object_type == kAACLCObjectType ||
       audio_object_type == kAACSBRObjectType) {
@@ -145,8 +144,8 @@
   NULL
 };
 
-static media::StreamParser* BuildMP4Parser(
-    const std::vector<std::string>& codecs, const media::LogCB& log_cb) {
+static StreamParser* BuildMP4Parser(
+    const std::vector<std::string>& codecs, const LogCB& log_cb) {
   std::set<int> audio_object_types;
   bool has_sbr = false;
 #if defined(ENABLE_EAC3_PLAYBACK)
@@ -156,12 +155,12 @@
   for (size_t i = 0; i < codecs.size(); ++i) {
     std::string codec_id = codecs[i];
     if (MatchPattern(codec_id, kMPEG2AACLCCodecInfo.pattern)) {
-      audio_object_types.insert(media::mp4::kISO_13818_7_AAC_LC);
+      audio_object_types.insert(mp4::kISO_13818_7_AAC_LC);
     } else if (MatchPattern(codec_id, kMPEG4AACCodecInfo.pattern)) {
       int audio_object_type = GetMP4AudioObjectType(codec_id, log_cb);
       DCHECK_GT(audio_object_type, 0);
 
-      audio_object_types.insert(media::mp4::kISO_14496_3);
+      audio_object_types.insert(mp4::kISO_14496_3);
 
       if (audio_object_type == kAACSBRObjectType) {
         has_sbr = true;
@@ -169,12 +168,12 @@
       }
 #if defined(ENABLE_EAC3_PLAYBACK)
     } else if (enable_eac3 && MatchPattern(codec_id, kEAC3CodecInfo.pattern)) {
-      audio_object_types.insert(media::mp4::kEAC3);
+      audio_object_types.insert(mp4::kEAC3);
 #endif
     }
   }
 
-  return new media::mp4::MP4StreamParser(audio_object_types, has_sbr);
+  return new mp4::MP4StreamParser(audio_object_types, has_sbr);
 }
 #endif
 
@@ -241,16 +240,22 @@
 static bool CheckTypeAndCodecs(
     const std::string& type,
     const std::vector<std::string>& codecs,
-    const media::LogCB& log_cb,
+    const LogCB& log_cb,
     ParserFactoryFunction* factory_function,
     std::vector<CodecInfo::HistogramTag>* audio_codecs,
     std::vector<CodecInfo::HistogramTag>* video_codecs) {
-  DCHECK_GT(codecs.size(), 0u);
 
   // Search for the SupportedTypeInfo for |type|.
   for (size_t i = 0; i < arraysize(kSupportedTypeInfo); ++i) {
     const SupportedTypeInfo& type_info = kSupportedTypeInfo[i];
     if (type == type_info.type) {
+
+      if (codecs.size() == 0u) {
+        MEDIA_LOG(log_cb) << "A codecs parameter must be provided for '"
+                          << type << "'";
+        return false;
+      }
+
       // Make sure all the codecs specified in |codecs| are
       // in the supported type info.
       for (size_t j = 0; j < codecs.size(); ++j) {
@@ -287,16 +292,16 @@
 
 bool StreamParserFactory::IsTypeSupported(
     const std::string& type, const std::vector<std::string>& codecs) {
-  return CheckTypeAndCodecs(type, codecs, media::LogCB(), NULL, NULL, NULL);
+  return CheckTypeAndCodecs(type, codecs, LogCB(), NULL, NULL, NULL);
 }
 
-scoped_ptr<media::StreamParser> StreamParserFactory::Create(
+scoped_ptr<StreamParser> StreamParserFactory::Create(
     const std::string& type,
     const std::vector<std::string>& codecs,
-    const media::LogCB& log_cb,
+    const LogCB& log_cb,
     bool* has_audio,
     bool* has_video) {
-  scoped_ptr<media::StreamParser> stream_parser;
+  scoped_ptr<StreamParser> stream_parser;
   ParserFactoryFunction factory_function;
   std::vector<CodecInfo::HistogramTag> audio_codecs;
   std::vector<CodecInfo::HistogramTag> video_codecs;
diff --git a/media/filters/wsola_internals.cc b/media/filters/wsola_internals.cc
new file mode 100644
index 0000000..45cdd8f
--- /dev/null
+++ b/media/filters/wsola_internals.cc
@@ -0,0 +1,264 @@
+// 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.
+
+// MSVC++ requires this to be set before any other includes to get M_PI.
+#define _USE_MATH_DEFINES
+
+#include "media/filters/wsola_internals.h"
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "media/base/audio_bus.h"
+
+namespace media {
+
+namespace internal {
+
+bool InInterval(int n, Interval q) {
+  return n >= q.first && n <= q.second;
+}
+
+float MultiChannelSimilarityMeasure(const float* dot_prod_a_b,
+                                    const float* energy_a,
+                                    const float* energy_b,
+                                    int channels) {
+  const float kEpsilon = 1e-12f;
+  float similarity_measure = 0.0f;
+  for (int n = 0; n < channels; ++n) {
+    similarity_measure += dot_prod_a_b[n] / sqrt(energy_a[n] * energy_b[n] +
+                                                 kEpsilon);
+  }
+  return similarity_measure;
+}
+
+void MultiChannelDotProduct(const AudioBus* a,
+                            int frame_offset_a,
+                            const AudioBus* b,
+                            int frame_offset_b,
+                            int num_frames,
+                            float* dot_product) {
+  DCHECK_EQ(a->channels(), b->channels());
+  DCHECK_GE(frame_offset_a, 0);
+  DCHECK_GE(frame_offset_b, 0);
+  DCHECK_LE(frame_offset_a + num_frames, a->frames());
+  DCHECK_LE(frame_offset_b + num_frames, b->frames());
+
+  memset(dot_product, 0, sizeof(*dot_product) * a->channels());
+  for (int k = 0; k < a->channels(); ++k) {
+    const float* ch_a = a->channel(k) + frame_offset_a;
+    const float* ch_b = b->channel(k) + frame_offset_b;
+    for (int n = 0; n < num_frames; ++n) {
+      dot_product[k] += *ch_a++ * *ch_b++;
+    }
+  }
+}
+
+void MultiChannelMovingBlockEnergies(const AudioBus* input,
+                                     int frames_per_block,
+                                     float* energy) {
+  int num_blocks = input->frames() - (frames_per_block - 1);
+  int channels = input->channels();
+
+  for (int k = 0; k < input->channels(); ++k) {
+    const float* input_channel = input->channel(k);
+
+    energy[k] = 0;
+
+    // First block of channel |k|.
+    for (int m = 0; m < frames_per_block; ++m) {
+      energy[k] += input_channel[m] * input_channel[m];
+    }
+
+    const float* slide_out = input_channel;
+    const float* slide_in = input_channel + frames_per_block;
+    for (int n = 1; n < num_blocks; ++n, ++slide_in, ++slide_out) {
+      energy[k + n * channels] = energy[k + (n - 1) * channels] - *slide_out *
+          *slide_out + *slide_in * *slide_in;
+    }
+  }
+}
+
+// Fit the curve f(x) = a * x^2 + b * x + c such that
+// f(-1) = |y[0]|
+// f(0) = |y[1]|
+// f(1) = |y[2]|.
+void CubicInterpolation(const float* y_values,
+                        float* extremum,
+                        float* extremum_value) {
+  float a = 0.5f * (y_values[2] + y_values[0]) - y_values[1];
+  float b = 0.5f * (y_values[2] - y_values[0]);
+  float c = y_values[1];
+
+  DCHECK_NE(a, 0);
+  *extremum = -b / (2.f * a);
+  *extremum_value = a * (*extremum) * (*extremum) + b * (*extremum) + c;
+}
+
+int DecimatedSearch(int decimation,
+                    Interval exclude_interval,
+                    const AudioBus* target_block,
+                    const AudioBus* search_segment,
+                    const float* energy_target_block,
+                    const float* energy_candidate_blocks) {
+  int channels = search_segment->channels();
+  int block_size = target_block->frames();
+  int num_candidate_blocks = search_segment->frames() - (block_size - 1);
+  scoped_ptr<float[]> dot_prod(new float[channels]);
+  float similarity[3];  // Three elements for cubic interpolation.
+
+  int n = 0;
+  MultiChannelDotProduct(target_block, 0, search_segment, n, block_size,
+                         dot_prod.get());
+  similarity[0] = MultiChannelSimilarityMeasure(
+      dot_prod.get(), energy_target_block,
+      &energy_candidate_blocks[n * channels], channels);
+
+  // Set the starting point as optimal point.
+  float best_similarity = similarity[0];
+  int optimal_index = 0;
+
+  n += decimation;
+  if (n >= num_candidate_blocks) {
+    return 0;
+  }
+
+  MultiChannelDotProduct(target_block, 0, search_segment, n, block_size,
+                         dot_prod.get());
+  similarity[1] = MultiChannelSimilarityMeasure(
+      dot_prod.get(), energy_target_block,
+      &energy_candidate_blocks[n * channels], channels);
+
+  n += decimation;
+  if (n >= num_candidate_blocks) {
+    // We cannot do any more sampling. Compare these two values and return the
+    // optimal index.
+    return similarity[1] > similarity[0] ? decimation : 0;
+  }
+
+  for (; n < num_candidate_blocks; n += decimation) {
+    MultiChannelDotProduct(target_block, 0, search_segment, n, block_size,
+                           dot_prod.get());
+
+    similarity[2] = MultiChannelSimilarityMeasure(
+        dot_prod.get(), energy_target_block,
+        &energy_candidate_blocks[n * channels], channels);
+
+    if ((similarity[1] > similarity[0] && similarity[1] >= similarity[2]) ||
+        (similarity[1] >= similarity[0] && similarity[1] > similarity[2])) {
+      // A local maximum is found. Do a cubic interpolation for a better
+      // estimate of candidate maximum.
+      float normalized_candidate_index;
+      float candidate_similarity;
+      CubicInterpolation(similarity, &normalized_candidate_index,
+                         &candidate_similarity);
+
+      int candidate_index = n - decimation + static_cast<int>(
+          normalized_candidate_index * decimation +  0.5f);
+      if (candidate_similarity > best_similarity &&
+          !InInterval(candidate_index, exclude_interval)) {
+        optimal_index = candidate_index;
+        best_similarity = candidate_similarity;
+      }
+    } else if (n + decimation >= num_candidate_blocks &&
+               similarity[2] > best_similarity &&
+               !InInterval(n, exclude_interval)) {
+      // If this is the end-point and has a better similarity-measure than
+      // optimal, then we accept it as optimal point.
+      optimal_index = n;
+      best_similarity = similarity[2];
+    }
+    memmove(similarity, &similarity[1], 2 * sizeof(*similarity));
+  }
+  return optimal_index;
+}
+
+int FullSearch(int low_limit,
+               int high_limit,
+               Interval exclude_interval,
+               const AudioBus* target_block,
+               const AudioBus* search_block,
+               const float* energy_target_block,
+               const float* energy_candidate_blocks) {
+  int channels = search_block->channels();
+  int block_size = target_block->frames();
+  scoped_ptr<float[]> dot_prod(new float[channels]);
+
+  float best_similarity = std::numeric_limits<float>::min();
+  int optimal_index = 0;
+
+  for (int n = low_limit; n <= high_limit; ++n) {
+    if (InInterval(n, exclude_interval)) {
+      continue;
+    }
+    MultiChannelDotProduct(target_block, 0, search_block, n, block_size,
+                           dot_prod.get());
+
+    float similarity = MultiChannelSimilarityMeasure(
+        dot_prod.get(), energy_target_block,
+        &energy_candidate_blocks[n * channels], channels);
+
+    if (similarity > best_similarity) {
+      best_similarity = similarity;
+      optimal_index = n;
+    }
+  }
+
+  return optimal_index;
+}
+
+int OptimalIndex(const AudioBus* search_block,
+                 const AudioBus* target_block,
+                 Interval exclude_interval) {
+  int channels = search_block->channels();
+  DCHECK_EQ(channels, target_block->channels());
+  int target_size = target_block->frames();
+  int num_candidate_blocks = search_block->frames() - (target_size - 1);
+
+  // This is a compromise between complexity reduction and search accuracy. I
+  // don't have a proof that down sample of order 5 is optimal. One can compute
+  // a decimation factor that minimizes complexity given the size of
+  // |search_block| and |target_block|. However, my experiments show the rate of
+  // missing the optimal index is significant. This value is chosen
+  // heuristically based on experiments.
+  const int kSearchDecimation = 5;
+
+  scoped_ptr<float[]> energy_target_block(new float[channels]);
+  scoped_ptr<float[]> energy_candidate_blocks(
+      new float[channels * num_candidate_blocks]);
+
+  // Energy of all candid frames.
+  MultiChannelMovingBlockEnergies(search_block, target_size,
+                                  energy_candidate_blocks.get());
+
+  // Energy of target frame.
+  MultiChannelDotProduct(target_block, 0, target_block, 0,
+                         target_size, energy_target_block.get());
+
+  int optimal_index = DecimatedSearch(kSearchDecimation,
+                                      exclude_interval, target_block,
+                                      search_block, energy_target_block.get(),
+                                      energy_candidate_blocks.get());
+
+  int lim_low = std::max(0, optimal_index - kSearchDecimation);
+  int lim_high = std::min(num_candidate_blocks - 1,
+                          optimal_index + kSearchDecimation);
+  return FullSearch(lim_low, lim_high, exclude_interval, target_block,
+                    search_block, energy_target_block.get(),
+                    energy_candidate_blocks.get());
+}
+
+void GetSymmetricHanningWindow(int window_length, float* window) {
+  const float scale = 2.0f * M_PI / window_length;
+  for (int n = 0; n < window_length; ++n)
+    window[n] = 0.5f * (1.0f - cosf(n * scale));
+}
+
+}  // namespace internal
+
+}  // namespace media
+
diff --git a/media/filters/wsola_internals.h b/media/filters/wsola_internals.h
new file mode 100644
index 0000000..55fff04
--- /dev/null
+++ b/media/filters/wsola_internals.h
@@ -0,0 +1,93 @@
+// 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.
+
+// A set of utility functions to perform WSOLA.
+
+#ifndef MEDIA_FILTERS_WSOLA_INTERNALS_H_
+#define MEDIA_FILTERS_WSOLA_INTERNALS_H_
+
+#include <utility>
+
+#include "media/base/media_export.h"
+
+namespace media {
+
+class AudioBus;
+
+namespace internal {
+
+typedef std::pair<int, int> Interval;
+
+// Dot-product of channels of two AudioBus. For each AudioBus an offset is
+// given. |dot_product[k]| is the dot-product of channel |k|. The caller should
+// allocate sufficient space for |dot_product|.
+MEDIA_EXPORT void MultiChannelDotProduct(const AudioBus* a,
+                                         int frame_offset_a,
+                                         const AudioBus* b,
+                                         int frame_offset_b,
+                                         int num_frames,
+                                         float* dot_product);
+
+// Energies of sliding windows of channels are interleaved.
+// The number windows is |input->frames()| - (|frames_per_window| - 1), hence,
+// the method assumes |energy| must be, at least, of size
+// (|input->frames()| - (|frames_per_window| - 1)) * |input->channels()|.
+MEDIA_EXPORT void MultiChannelMovingBlockEnergies(const AudioBus* input,
+                                                  int frames_per_window,
+                                                  float* energy);
+
+// Fit the curve f(x) = a * x^2 + b * x + c such that
+//
+// f(-1) = |y[0]|
+// f(0) = |y[1]|
+// f(1) = |y[2]|.
+//
+// Then compute the |extremum| point -b / (2*a) and |extremum_value|
+// b^2 / (4*a) - b^2 / (2*a) + c.
+//
+// It is not expected that this function is called with
+// y[0] == y[1] == y[2].
+MEDIA_EXPORT void CubicInterpolation(const float* y_values,
+                                     float* extremum,
+                                     float* extremum_value);
+
+// Search a subset of all candid blocks. The search is performed every
+// |decimation| frames. This reduces complexity by a factor of about
+// 1 / |decimation|. A cubic interpolation is used to have a better estimate of
+// the best match.
+MEDIA_EXPORT int DecimatedSearch(int decimation,
+                                 Interval exclude_interval,
+                                 const AudioBus* target_block,
+                                 const AudioBus* search_segment,
+                                 const float* energy_target_block,
+                                 const float* energy_candid_blocks);
+
+// Search [|low_limit|, |high_limit|] of |search_segment| to find a block that
+// is most similar to |target_block|. |energy_target_block| is the energy of the
+// |target_block|. |energy_candidate_blocks| is the energy of all blocks within
+// |search_block|.
+MEDIA_EXPORT int FullSearch(int low_limit,
+                            int hight_limimit,
+                            Interval exclude_interval,
+                            const AudioBus* target_block,
+                            const AudioBus* search_block,
+                            const float* energy_target_block,
+                            const float* energy_candidate_blocks);
+
+// Find the index of the block, within |search_block|, that is most similar
+// to |target_block|. Obviously, the returned index is w.r.t. |search_block|.
+// |exclude_interval| is an interval that is excluded from the search.
+MEDIA_EXPORT int OptimalIndex(const AudioBus* search_block,
+                              const AudioBus* target_block,
+                              Interval exclude_interval);
+
+// Return a "periodic" Hann window. This is the first L samples of an L+1
+// Hann window. It is perfect reconstruction for overlap-and-add.
+MEDIA_EXPORT void GetSymmetricHanningWindow(int window_length, float* window);
+
+}  // namespace internal
+
+}  // namespace media
+
+#endif  // MEDIA_FILTERS_WSOLA_INTERNALS_H_
diff --git a/media/media.gyp b/media/media.gyp
index 6408f4b..ab27018 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -104,10 +104,6 @@
         'audio/cras/cras_input.h',
         'audio/cras/cras_unified.cc',
         'audio/cras/cras_unified.h',
-        'audio/cross_process_notification.cc',
-        'audio/cross_process_notification.h',
-        'audio/cross_process_notification_posix.cc',
-        'audio/cross_process_notification_win.cc',
         'audio/fake_audio_consumer.cc',
         'audio/fake_audio_consumer.h',
         'audio/fake_audio_input_stream.cc',
@@ -258,6 +254,8 @@
         'base/djb2.h',
         'base/filter_collection.cc',
         'base/filter_collection.h',
+        'base/keyboard_event_counter.cc',
+        'base/keyboard_event_counter.h',
         'base/media.cc',
         'base/media.h',
         'base/media_file_checker.cc',
@@ -304,7 +302,7 @@
         'base/user_input_monitor.cc',
         'base/user_input_monitor.h',
         'base/user_input_monitor_linux.cc',
-        'base/user_input_monitor_mac.mm',
+        'base/user_input_monitor_mac.cc',
         'base/user_input_monitor_win.cc',
         'base/video_decoder.cc',
         'base/video_decoder.h',
@@ -376,6 +374,8 @@
         'filters/video_renderer_base.h',
         'filters/vpx_video_decoder.cc',
         'filters/vpx_video_decoder.h',
+        'filters/wsola_internals.cc',
+        'filters/wsola_internals.h',
         'midi/midi_manager.cc',
         'midi/midi_manager.h',
         'midi/midi_manager_mac.cc',
@@ -392,7 +392,6 @@
         'video/capture/mac/video_capture_device_mac.mm',
         'video/capture/mac/video_capture_device_qtkit_mac.h',
         'video/capture/mac/video_capture_device_qtkit_mac.mm',
-
         'video/capture/video_capture.h',
         'video/capture/video_capture_device.cc',
         'video/capture/video_capture_device.h',
@@ -687,6 +686,8 @@
             'audio/cras/cras_input.h',
             'audio/cras/cras_unified.cc',
             'audio/cras/cras_unified.h',
+            'base/keyboard_event_counter.cc',
+            'base/keyboard_event_counter.h',
           ],
         }],
         ['use_pulseaudio==1', {
@@ -917,7 +918,6 @@
         'audio/audio_output_proxy_unittest.cc',
         'audio/audio_parameters_unittest.cc',
         'audio/audio_power_monitor_unittest.cc',
-        'audio/cross_process_notification_unittest.cc',
         'audio/fake_audio_consumer_unittest.cc',
         'audio/ios/audio_manager_ios_unittest.cc',
         'audio/linux/alsa_output_unittest.cc',
@@ -1358,17 +1358,6 @@
           ],
         },
         {
-          'target_name': 'seek_tester',
-          'type': 'executable',
-          'dependencies': [
-            'media',
-            '../base/base.gyp:base',
-          ],
-          'sources': [
-            'tools/seek_tester/seek_tester.cc',
-          ],
-        },
-        {
           'target_name': 'demuxer_bench',
           'type': 'executable',
           'dependencies': [
@@ -1383,60 +1372,6 @@
         },
       ],
     }],
-    ['(OS=="win" or toolkit_uses_gtk==1) and use_aura!=1', {
-      'targets': [
-        {
-          'target_name': 'shader_bench',
-          'type': 'executable',
-          'dependencies': [
-            'media',
-            '../base/base.gyp:base',
-            '../ui/gl/gl.gyp:gl',
-            '../ui/ui.gyp:ui',
-          ],
-          'sources': [
-            'tools/shader_bench/cpu_color_painter.cc',
-            'tools/shader_bench/cpu_color_painter.h',
-            'tools/shader_bench/gpu_color_painter.cc',
-            'tools/shader_bench/gpu_color_painter.h',
-            'tools/shader_bench/gpu_painter.cc',
-            'tools/shader_bench/gpu_painter.h',
-            'tools/shader_bench/painter.cc',
-            'tools/shader_bench/painter.h',
-            'tools/shader_bench/shader_bench.cc',
-            'tools/shader_bench/window.cc',
-            'tools/shader_bench/window.h',
-          ],
-          'conditions': [
-            ['toolkit_uses_gtk==1', {
-              'dependencies': [
-                '../build/linux/system.gyp:gtk',
-              ],
-              'sources': [
-                'tools/shader_bench/window_linux.cc',
-              ],
-            }],
-            ['OS=="win"', {
-              'dependencies': [
-                '../third_party/angle_dx11/src/build_angle.gyp:libEGL',
-                '../third_party/angle_dx11/src/build_angle.gyp:libGLESv2',
-              ],
-              'sources': [
-                'tools/shader_bench/window_win.cc',
-              ],
-            }],
-            # See http://crbug.com/162998#c4 for why this is needed.
-            ['OS=="linux" and linux_use_tcmalloc==1', {
-              'dependencies': [
-                '../base/allocator/allocator.gyp:allocator',
-              ],
-            }],
-          ],
-          # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-          'msvs_disabled_warnings': [ 4267, ],
-        },
-      ],
-    }],
     ['use_x11==1', {
       'targets': [
         {
@@ -1649,34 +1584,6 @@
             }],
           ],
         },
-        {
-          'target_name': 'ffmpeg_tests',
-          'type': 'executable',
-          'dependencies': [
-            '../base/base.gyp:base',
-            '../third_party/ffmpeg/ffmpeg.gyp:ffmpeg',
-            'media',
-          ],
-          'sources': [
-            'test/ffmpeg_tests/ffmpeg_tests.cc',
-          ],
-          # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-          'msvs_disabled_warnings': [ 4267, ],
-        },
-        {
-          'target_name': 'media_bench',
-          'type': 'executable',
-          'dependencies': [
-            '../base/base.gyp:base',
-            '../third_party/ffmpeg/ffmpeg.gyp:ffmpeg',
-            'media',
-          ],
-          'sources': [
-            'tools/media_bench/media_bench.cc',
-          ],
-          # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
-          'msvs_disabled_warnings': [ 4267, ],
-        },
       ],
     }],
   ],
diff --git a/media/media.target.darwin-arm.mk b/media/media.target.darwin-arm.mk
index b909eae..05d668a 100644
--- a/media/media.target.darwin-arm.mk
+++ b/media/media.target.darwin-arm.mk
@@ -49,8 +49,6 @@
 	media/audio/audio_power_monitor.cc \
 	media/audio/audio_util.cc \
 	media/audio/clockless_audio_sink.cc \
-	media/audio/cross_process_notification.cc \
-	media/audio/cross_process_notification_posix.cc \
 	media/audio/fake_audio_consumer.cc \
 	media/audio/fake_audio_input_stream.cc \
 	media/audio/fake_audio_output_stream.cc \
@@ -135,6 +133,7 @@
 	media/filters/video_decoder_selector.cc \
 	media/filters/video_frame_stream.cc \
 	media/filters/video_renderer_base.cc \
+	media/filters/wsola_internals.cc \
 	media/midi/midi_manager.cc \
 	media/midi/midi_port_info.cc \
 	media/video/capture/android/video_capture_device_android.cc \
diff --git a/media/media.target.darwin-mips.mk b/media/media.target.darwin-mips.mk
index 40dcf70..09f2346 100644
--- a/media/media.target.darwin-mips.mk
+++ b/media/media.target.darwin-mips.mk
@@ -49,8 +49,6 @@
 	media/audio/audio_power_monitor.cc \
 	media/audio/audio_util.cc \
 	media/audio/clockless_audio_sink.cc \
-	media/audio/cross_process_notification.cc \
-	media/audio/cross_process_notification_posix.cc \
 	media/audio/fake_audio_consumer.cc \
 	media/audio/fake_audio_input_stream.cc \
 	media/audio/fake_audio_output_stream.cc \
@@ -135,6 +133,7 @@
 	media/filters/video_decoder_selector.cc \
 	media/filters/video_frame_stream.cc \
 	media/filters/video_renderer_base.cc \
+	media/filters/wsola_internals.cc \
 	media/midi/midi_manager.cc \
 	media/midi/midi_port_info.cc \
 	media/video/capture/android/video_capture_device_android.cc \
diff --git a/media/media.target.darwin-x86.mk b/media/media.target.darwin-x86.mk
index c6eafe0..bf86bb2 100644
--- a/media/media.target.darwin-x86.mk
+++ b/media/media.target.darwin-x86.mk
@@ -49,8 +49,6 @@
 	media/audio/audio_power_monitor.cc \
 	media/audio/audio_util.cc \
 	media/audio/clockless_audio_sink.cc \
-	media/audio/cross_process_notification.cc \
-	media/audio/cross_process_notification_posix.cc \
 	media/audio/fake_audio_consumer.cc \
 	media/audio/fake_audio_input_stream.cc \
 	media/audio/fake_audio_output_stream.cc \
@@ -135,6 +133,7 @@
 	media/filters/video_decoder_selector.cc \
 	media/filters/video_frame_stream.cc \
 	media/filters/video_renderer_base.cc \
+	media/filters/wsola_internals.cc \
 	media/midi/midi_manager.cc \
 	media/midi/midi_port_info.cc \
 	media/video/capture/android/video_capture_device_android.cc \
diff --git a/media/media.target.linux-arm.mk b/media/media.target.linux-arm.mk
index b909eae..05d668a 100644
--- a/media/media.target.linux-arm.mk
+++ b/media/media.target.linux-arm.mk
@@ -49,8 +49,6 @@
 	media/audio/audio_power_monitor.cc \
 	media/audio/audio_util.cc \
 	media/audio/clockless_audio_sink.cc \
-	media/audio/cross_process_notification.cc \
-	media/audio/cross_process_notification_posix.cc \
 	media/audio/fake_audio_consumer.cc \
 	media/audio/fake_audio_input_stream.cc \
 	media/audio/fake_audio_output_stream.cc \
@@ -135,6 +133,7 @@
 	media/filters/video_decoder_selector.cc \
 	media/filters/video_frame_stream.cc \
 	media/filters/video_renderer_base.cc \
+	media/filters/wsola_internals.cc \
 	media/midi/midi_manager.cc \
 	media/midi/midi_port_info.cc \
 	media/video/capture/android/video_capture_device_android.cc \
diff --git a/media/media.target.linux-mips.mk b/media/media.target.linux-mips.mk
index 40dcf70..09f2346 100644
--- a/media/media.target.linux-mips.mk
+++ b/media/media.target.linux-mips.mk
@@ -49,8 +49,6 @@
 	media/audio/audio_power_monitor.cc \
 	media/audio/audio_util.cc \
 	media/audio/clockless_audio_sink.cc \
-	media/audio/cross_process_notification.cc \
-	media/audio/cross_process_notification_posix.cc \
 	media/audio/fake_audio_consumer.cc \
 	media/audio/fake_audio_input_stream.cc \
 	media/audio/fake_audio_output_stream.cc \
@@ -135,6 +133,7 @@
 	media/filters/video_decoder_selector.cc \
 	media/filters/video_frame_stream.cc \
 	media/filters/video_renderer_base.cc \
+	media/filters/wsola_internals.cc \
 	media/midi/midi_manager.cc \
 	media/midi/midi_port_info.cc \
 	media/video/capture/android/video_capture_device_android.cc \
diff --git a/media/media.target.linux-x86.mk b/media/media.target.linux-x86.mk
index c6eafe0..bf86bb2 100644
--- a/media/media.target.linux-x86.mk
+++ b/media/media.target.linux-x86.mk
@@ -49,8 +49,6 @@
 	media/audio/audio_power_monitor.cc \
 	media/audio/audio_util.cc \
 	media/audio/clockless_audio_sink.cc \
-	media/audio/cross_process_notification.cc \
-	media/audio/cross_process_notification_posix.cc \
 	media/audio/fake_audio_consumer.cc \
 	media/audio/fake_audio_input_stream.cc \
 	media/audio/fake_audio_output_stream.cc \
@@ -135,6 +133,7 @@
 	media/filters/video_decoder_selector.cc \
 	media/filters/video_frame_stream.cc \
 	media/filters/video_renderer_base.cc \
+	media/filters/wsola_internals.cc \
 	media/midi/midi_manager.cc \
 	media/midi/midi_port_info.cc \
 	media/video/capture/android/video_capture_device_android.cc \
diff --git a/media/midi/midi_manager.cc b/media/midi/midi_manager.cc
index 05fcfa4..b3262e4 100644
--- a/media/midi/midi_manager.cc
+++ b/media/midi/midi_manager.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/message_loop/message_loop.h"
 #include "base/threading/thread.h"
 
 namespace media {
@@ -52,7 +53,7 @@
 }
 
 void MIDIManager::ReceiveMIDIData(
-    int port_index,
+    uint32 port_index,
     const uint8* data,
     size_t length,
     double timestamp) {
@@ -62,10 +63,13 @@
     (*i)->ReceiveMIDIData(port_index, data, length, timestamp);
 }
 
+bool MIDIManager::CurrentlyOnMIDISendThread() {
+  return send_thread_->message_loop() == base::MessageLoop::current();
+}
+
 void MIDIManager::DispatchSendMIDIData(MIDIManagerClient* client,
-                                       int port_index,
-                                       const uint8* data,
-                                       size_t length,
+                                       uint32 port_index,
+                                       const std::vector<uint8>& data,
                                        double timestamp) {
   // Lazily create the thread when first needed.
   if (!send_thread_) {
@@ -77,7 +81,7 @@
   send_message_loop_->PostTask(
      FROM_HERE,
      base::Bind(&MIDIManager::SendMIDIData, base::Unretained(this),
-         client, port_index, data, length, timestamp));
+         client, port_index, data, timestamp));
 }
 
 }  // namespace media
diff --git a/media/midi/midi_manager.h b/media/midi/midi_manager.h
index c2b26ab..6a301a94 100644
--- a/media/midi/midi_manager.h
+++ b/media/midi/midi_manager.h
@@ -6,6 +6,7 @@
 #define MEDIA_MIDI_MIDI_MANAGER_H_
 
 #include <set>
+#include <vector>
 
 #include "base/basictypes.h"
 #include "base/memory/scoped_ptr.h"
@@ -33,7 +34,7 @@
   // |data| represents a series of bytes encoding one or more MIDI messages.
   // |length| is the number of bytes in |data|.
   // |timestamp| is the time the data was received, in seconds.
-  virtual void ReceiveMIDIData(int port_index,
+  virtual void ReceiveMIDIData(uint32 port_index,
                                const uint8* data,
                                size_t length,
                                double timestamp) = 0;
@@ -70,9 +71,8 @@
   // |timestamp| is the time to send the data, in seconds. A value of 0
   // means send "now" or as soon as possible.
   void DispatchSendMIDIData(MIDIManagerClient* client,
-                            int port_index,
-                            const uint8* data,
-                            size_t length,
+                            uint32 port_index,
+                            const std::vector<uint8>& data,
                             double timestamp);
 
   // input_ports() is a list of MIDI ports for receiving MIDI data.
@@ -90,21 +90,23 @@
   virtual bool Initialize() = 0;
 
   // Implements the platform-specific details of sending MIDI data.
+  // This function runs on MIDISendThread.
   virtual void SendMIDIData(MIDIManagerClient* client,
-                            int port_index,
-                            const uint8* data,
-                            size_t length,
+                            uint32 port_index,
+                            const std::vector<uint8>& data,
                             double timestamp) = 0;
 
   void AddInputPort(const MIDIPortInfo& info);
   void AddOutputPort(const MIDIPortInfo& info);
 
   // Dispatches to all clients.
-  void ReceiveMIDIData(
-      int port_index,
-      const uint8* data,
-      size_t length,
-      double timestamp);
+  void ReceiveMIDIData(uint32 port_index,
+                       const uint8* data,
+                       size_t length,
+                       double timestamp);
+
+  // Checks if current thread is MIDISendThread.
+  bool CurrentlyOnMIDISendThread();
 
   bool initialized_;
 
diff --git a/media/midi/midi_manager_mac.cc b/media/midi/midi_manager_mac.cc
index 7bc8f23..4477944 100644
--- a/media/midi/midi_manager_mac.cc
+++ b/media/midi/midi_manager_mac.cc
@@ -54,7 +54,7 @@
   result = MIDIInputPortCreate(
       midi_client_,
       CFSTR("MIDI Input"),
-      ReadMidiDispatch,
+      ReadMIDIDispatch,
       this,
       &coremidi_input_);
   if (result != noErr)
@@ -67,10 +67,10 @@
   if (result != noErr)
     return false;
 
-  int destination_count = MIDIGetNumberOfDestinations();
+  uint32 destination_count = MIDIGetNumberOfDestinations();
   destinations_.resize(destination_count);
 
-  for (int i = 0; i < destination_count ; i++) {
+  for (uint32 i = 0; i < destination_count ; i++) {
     MIDIEndpointRef destination = MIDIGetDestination(i);
 
     // Keep track of all destinations (known as outputs by the Web MIDI API).
@@ -82,9 +82,9 @@
   }
 
   // Open connections from all sources.
-  int source_count = MIDIGetNumberOfSources();
+  uint32 source_count = MIDIGetNumberOfSources();
 
-  for (int i = 0; i < source_count; ++i)  {
+  for (uint32 i = 0; i < source_count; ++i)  {
     // Receive from all sources.
     MIDIEndpointRef src = MIDIGetSource(i);
     MIDIPortConnectSource(coremidi_input_, src, reinterpret_cast<void*>(src));
@@ -110,7 +110,7 @@
     MIDIPortDispose(coremidi_output_);
 }
 
-void MIDIManagerMac::ReadMidiDispatch(const MIDIPacketList* packet_list,
+void MIDIManagerMac::ReadMIDIDispatch(const MIDIPacketList* packet_list,
                                       void* read_proc_refcon,
                                       void* src_conn_refcon) {
   MIDIManagerMac* manager = static_cast<MIDIManagerMac*>(read_proc_refcon);
@@ -121,16 +121,16 @@
 #endif
 
   // Dispatch to class method.
-  manager->ReadMidi(source, packet_list);
+  manager->ReadMIDI(source, packet_list);
 }
 
-void MIDIManagerMac::ReadMidi(MIDIEndpointRef source,
+void MIDIManagerMac::ReadMIDI(MIDIEndpointRef source,
                               const MIDIPacketList* packet_list) {
   // Lookup the port index based on the source.
   SourceMap::iterator j = source_map_.find(source);
   if (j == source_map_.end())
     return;
-  int port_index = source_map_[source];
+  uint32 port_index = source_map_[source];
 
   // Go through each packet and process separately.
   for(size_t i = 0; i < packet_list->numPackets; i++) {
@@ -147,10 +147,11 @@
 }
 
 void MIDIManagerMac::SendMIDIData(MIDIManagerClient* client,
-                                  int port_index,
-                                  const uint8* data,
-                                  size_t length,
+                                  uint32 port_index,
+                                  const std::vector<uint8>& data,
                                   double timestamp) {
+  DCHECK(CurrentlyOnMIDISendThread());
+
   // System Exclusive has already been filtered.
   MIDITimeStamp coremidi_timestamp = SecondsToMIDITimeStamp(timestamp);
 
@@ -159,14 +160,11 @@
       kMaxPacketListSize,
       midi_packet_,
       coremidi_timestamp,
-      length,
-      data);
+      data.size(),
+      &data[0]);
 
   // Lookup the destination based on the port index.
-  // TODO(crogers): re-factor |port_index| to use unsigned
-  // to avoid the need for this check.
-  if (port_index < 0 ||
-      static_cast<size_t>(port_index) >= destinations_.size())
+  if (static_cast<size_t>(port_index) >= destinations_.size())
     return;
 
   MIDIEndpointRef destination = destinations_[port_index];
@@ -176,7 +174,7 @@
   // Re-initialize for next time.
   midi_packet_ = MIDIPacketListInit(packet_list_);
 
-  client->AccumulateMIDIBytesSent(length);
+  client->AccumulateMIDIBytesSent(data.size());
 }
 
 MIDIPortInfo MIDIManagerMac::GetPortInfoFromEndpoint(
diff --git a/media/midi/midi_manager_mac.h b/media/midi/midi_manager_mac.h
index ed7b524..2397b80 100644
--- a/media/midi/midi_manager_mac.h
+++ b/media/midi/midi_manager_mac.h
@@ -8,6 +8,7 @@
 #include <CoreMIDI/MIDIServices.h>
 #include <map>
 #include <string>
+#include <vector>
 
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
@@ -24,20 +25,19 @@
   // MIDIManager implementation.
   virtual bool Initialize() OVERRIDE;
   virtual void SendMIDIData(MIDIManagerClient* client,
-                            int port_index,
-                            const uint8* data,
-                            size_t length,
+                            uint32 port_index,
+                            const std::vector<uint8>& data,
                             double timestamp) OVERRIDE;
 
  private:
   // CoreMIDI callback for MIDI data.
   // Each callback can contain multiple packets, each of which can contain
   // multiple MIDI messages.
-  static void ReadMidiDispatch(
+  static void ReadMIDIDispatch(
       const MIDIPacketList *pktlist,
       void *read_proc_refcon,
       void *src_conn_refcon);
-  virtual void ReadMidi(MIDIEndpointRef source, const MIDIPacketList *pktlist);
+  virtual void ReadMIDI(MIDIEndpointRef source, const MIDIPacketList *pktlist);
 
   // Helper
   static media::MIDIPortInfo GetPortInfoFromEndpoint(MIDIEndpointRef endpoint);
@@ -54,7 +54,7 @@
   MIDIPacketList* packet_list_;
   MIDIPacket* midi_packet_;
 
-  typedef std::map<MIDIEndpointRef, int> SourceMap;
+  typedef std::map<MIDIEndpointRef, uint32> SourceMap;
 
   // Keeps track of the index (0-based) for each of our sources.
   SourceMap source_map_;
diff --git a/media/mp4/mp4_stream_parser.cc b/media/mp4/mp4_stream_parser.cc
index fc4ee8a..51ed756 100644
--- a/media/mp4/mp4_stream_parser.cc
+++ b/media/mp4/mp4_stream_parser.cc
@@ -339,14 +339,14 @@
   for (size_t i = 0; i < headers.size(); i++)
     total_size += headers[i].raw_box.size();
 
-  scoped_ptr<uint8[]> init_data(new uint8[total_size]);
+  std::vector<uint8> init_data(total_size);
   size_t pos = 0;
   for (size_t i = 0; i < headers.size(); i++) {
-    memcpy(&init_data.get()[pos], &headers[i].raw_box[0],
+    memcpy(&init_data[pos], &headers[i].raw_box[0],
            headers[i].raw_box.size());
     pos += headers[i].raw_box.size();
   }
-  need_key_cb_.Run(kMp4InitDataType, init_data.Pass(), total_size);
+  need_key_cb_.Run(kMp4InitDataType, init_data);
 }
 
 bool MP4StreamParser::PrepareAVCBuffer(
diff --git a/media/mp4/mp4_stream_parser_unittest.cc b/media/mp4/mp4_stream_parser_unittest.cc
index fa880ac..816a210 100644
--- a/media/mp4/mp4_stream_parser_unittest.cc
+++ b/media/mp4/mp4_stream_parser_unittest.cc
@@ -94,11 +94,10 @@
   }
 
   void KeyNeededF(const std::string& type,
-                  scoped_ptr<uint8[]> init_data, int init_data_size) {
-    DVLOG(1) << "KeyNeededF: " << init_data_size;
+                  const std::vector<uint8>& init_data) {
+    DVLOG(1) << "KeyNeededF: " << init_data.size();
     EXPECT_EQ(kMp4InitDataType, type);
-    EXPECT_TRUE(init_data.get());
-    EXPECT_GT(init_data_size, 0);
+    EXPECT_FALSE(init_data.empty());
   }
 
   scoped_ptr<TextTrack> AddTextTrackF(
diff --git a/media/test/data/README b/media/test/data/README
index b28b749..2dd1fd2 100644
--- a/media/test/data/README
+++ b/media/test/data/README
@@ -3,6 +3,15 @@
 // found in the LICENSE file.
 
 bear-320x240.webm - WebM encode of bear.1280x720.mp4 resized to 320x240.
+bear-vp9.webm - VP9 video only WebM file.
+bear-vp9-opus.webm - VP9 Video with Opus Audio.
+bear-vp8-webvtt.webm - WebM VP8 video with WebVTT subtitle track.
+bear-vp8a.webm - WebM VP8 video with alpha channel.
+bear-opus.webm - Opus Audio only WebM file.
+bear-opus-end-trimming.webm - File to test end trimming. It has one byte 
+                              artificially added so that there is maximum 
+                              padding at the end. It is an Opus Audio only WebM 
+                              file.
 no_streams.webm - Header, Info, & Tracks element from bear-320x240.webm slightly corrupted so it looks 
                   like there are no tracks.
 nonzero-start-time.webm - Has the same headers as bear-320x240.webm but the first cluster of this file 
diff --git a/media/test/data/bear-opus-end-trimming.webm b/media/test/data/bear-opus-end-trimming.webm
new file mode 100644
index 0000000..d727d86
--- /dev/null
+++ b/media/test/data/bear-opus-end-trimming.webm
Binary files differ
diff --git a/media/test/ffmpeg_tests/ffmpeg_tests.cc b/media/test/ffmpeg_tests/ffmpeg_tests.cc
deleted file mode 100644
index 628eafc..0000000
--- a/media/test/ffmpeg_tests/ffmpeg_tests.cc
+++ /dev/null
@@ -1,500 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Software qualification test for FFmpeg.  This test is used to certify that
-// software decoding quality and performance of FFmpeg meets a mimimum
-// standard.
-
-#include <iomanip>
-#include <iostream>
-#include <string>
-
-#include "base/at_exit.h"
-#include "base/basictypes.h"
-#include "base/command_line.h"
-#include "base/file_util.h"
-#include "base/files/file_path.h"
-#include "base/files/memory_mapped_file.h"
-#include "base/logging.h"
-#include "base/md5.h"
-#include "base/path_service.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/time/time.h"
-#include "media/base/djb2.h"
-#include "media/base/media.h"
-#include "media/ffmpeg/ffmpeg_common.h"
-#include "media/filters/ffmpeg_glue.h"
-#include "media/filters/ffmpeg_video_decoder.h"
-#include "media/filters/in_memory_url_protocol.h"
-
-#ifdef DEBUG
-#define SHOW_VERBOSE 1
-#else
-#define SHOW_VERBOSE 0
-#endif
-
-#if defined(OS_WIN)
-
-// Enable to build with exception handler
-//#define ENABLE_WINDOWS_EXCEPTIONS 1
-
-#ifdef ENABLE_WINDOWS_EXCEPTIONS
-// warning: disable warning about exception handler.
-#pragma warning(disable:4509)
-#endif
-
-// Thread priorities to make benchmark more stable.
-
-void EnterTimingSection() {
-  SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
-}
-
-void LeaveTimingSection() {
-  SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
-}
-#else
-void EnterTimingSection() {
-  pthread_attr_t pta;
-  struct sched_param param;
-
-  pthread_attr_init(&pta);
-  memset(&param, 0, sizeof(param));
-  param.sched_priority = 78;
-  pthread_attr_setschedparam(&pta, &param);
-  pthread_attr_destroy(&pta);
-}
-
-void LeaveTimingSection() {
-}
-#endif
-
-int main(int argc, const char** argv) {
-  base::AtExitManager exit_manager;
-
-  CommandLine::Init(argc, argv);
-  const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
-
-  const CommandLine::StringVector& filenames = cmd_line->GetArgs();
-
-  if (filenames.empty()) {
-    std::cerr << "Usage: " << argv[0] << " MEDIAFILE" << std::endl;
-    return 1;
-  }
-
-  // Initialize our media library (try loading DLLs, etc.) before continuing.
-  base::FilePath media_path;
-  PathService::Get(base::DIR_MODULE, &media_path);
-  if (!media::InitializeMediaLibrary(media_path)) {
-    std::cerr << "Unable to initialize the media library.";
-    return 1;
-  }
-
-  // Retrieve command line options.
-  base::FilePath in_path(filenames[0]);
-  base::FilePath out_path;
-  if (filenames.size() > 1)
-    out_path = base::FilePath(filenames[1]);
-
-  // Default flags that match Chrome defaults.
-  int video_threads = 2;
-  int max_frames = 0;
-  int max_loops = 0;
-  bool flush = false;
-
-  unsigned int hash_value = 5381u;  // Seed for DJB2.
-  bool hash_djb2 = false;
-
-  base::MD5Context ctx;  // Intermediate MD5 data: do not use
-  base::MD5Init(&ctx);
-  bool hash_md5 = false;
-
-  std::ostream* log_out = &std::cout;
-#if defined(ENABLE_WINDOWS_EXCEPTIONS)
-  // Catch exceptions so this tool can be used in automated testing.
-  __try {
-#endif
-
-  base::MemoryMappedFile file_data;
-  file_data.Initialize(in_path);
-  media::InMemoryUrlProtocol protocol(
-      file_data.data(), file_data.length(), false);
-
-  // Register FFmpeg and attempt to open file.
-  media::FFmpegGlue glue(&protocol);
-  if (!glue.OpenContext()) {
-    std::cerr << "Error: Could not open input for "
-              << in_path.value() << std::endl;
-    return 1;
-  }
-
-  AVFormatContext* format_context = glue.format_context();
-
-  // Open output file.
-  FILE *output = NULL;
-  if (!out_path.empty()) {
-    output = file_util::OpenFile(out_path, "wb");
-    if (!output) {
-      std::cerr << "Error: Could not open output "
-                << out_path.value() << std::endl;
-      return 1;
-    }
-  }
-
-  // Parse a little bit of the stream to fill out the format context.
-  if (avformat_find_stream_info(format_context, NULL) < 0) {
-    std::cerr << "Error: Could not find stream info for "
-              << in_path.value() << std::endl;
-    return 1;
-  }
-
-  // Find our target stream(s)
-  int video_stream = -1;
-  int audio_stream = -1;
-  for (size_t i = 0; i < format_context->nb_streams; ++i) {
-    AVCodecContext* codec_context = format_context->streams[i]->codec;
-
-    if (codec_context->codec_type == AVMEDIA_TYPE_VIDEO && video_stream < 0) {
-#if SHOW_VERBOSE
-      *log_out << "V ";
-#endif
-      video_stream = i;
-    } else {
-      if (codec_context->codec_type == AVMEDIA_TYPE_AUDIO && audio_stream < 0) {
-#if SHOW_VERBOSE
-        *log_out << "A ";
-#endif
-        audio_stream = i;
-      } else {
-#if SHOW_VERBOSE
-      *log_out << "  ";
-#endif
-      }
-    }
-
-#if SHOW_VERBOSE
-    AVCodec* codec = avcodec_find_decoder(codec_context->codec_id);
-    if (!codec || (codec_context->codec_type == AVMEDIA_TYPE_UNKNOWN)) {
-      *log_out << "Stream #" << i << ": Unknown" << std::endl;
-    } else {
-      // Print out stream information
-      *log_out << "Stream #" << i << ": " << codec->name << " ("
-               << codec->long_name << ")" << std::endl;
-    }
-#endif
-  }
-  int target_stream = video_stream;
-  AVMediaType target_codec = AVMEDIA_TYPE_VIDEO;
-  if (target_stream < 0) {
-    target_stream = audio_stream;
-    target_codec = AVMEDIA_TYPE_AUDIO;
-  }
-
-  // Only continue if we found our target stream.
-  if (target_stream < 0) {
-    std::cerr << "Error: Could not find target stream "
-              << target_stream << " for " << in_path.value() << std::endl;
-    return 1;
-  }
-
-  // Prepare FFmpeg structures.
-  AVPacket packet;
-  AVCodecContext* codec_context = format_context->streams[target_stream]->codec;
-  AVCodec* codec = avcodec_find_decoder(codec_context->codec_id);
-
-  // Only continue if we found our codec.
-  if (!codec) {
-    std::cerr << "Error: Could not find codec for "
-              << in_path.value() << std::endl;
-    return 1;
-  }
-
-  codec_context->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK;
-
-  // Initialize threaded decode.
-  if (target_codec == AVMEDIA_TYPE_VIDEO && video_threads > 0) {
-    codec_context->thread_count = video_threads;
-  }
-
-  // Initialize our codec.
-  if (avcodec_open2(codec_context, codec, NULL) < 0) {
-    std::cerr << "Error: Could not open codec "
-              << codec_context->codec->name << " for "
-              << in_path.value() << std::endl;
-    return 1;
-  }
-
-  // Buffer used for audio decoding.
-  scoped_ptr_malloc<AVFrame, media::ScopedPtrAVFree> audio_frame(
-      avcodec_alloc_frame());
-  if (!audio_frame) {
-    std::cerr << "Error: avcodec_alloc_frame for "
-              << in_path.value() << std::endl;
-    return 1;
-  }
-
-  // Buffer used for video decoding.
-  scoped_ptr_malloc<AVFrame, media::ScopedPtrAVFree> video_frame(
-      avcodec_alloc_frame());
-  if (!video_frame) {
-    std::cerr << "Error: avcodec_alloc_frame for "
-              << in_path.value() << std::endl;
-    return 1;
-  }
-
-  // Stats collector.
-  EnterTimingSection();
-  std::vector<double> decode_times;
-  decode_times.reserve(4096);
-  // Parse through the entire stream until we hit EOF.
-#if SHOW_VERBOSE
-  base::TimeTicks start = base::TimeTicks::HighResNow();
-#endif
-  int frames = 0;
-  int read_result = 0;
-  do {
-    read_result = av_read_frame(format_context, &packet);
-
-    if (read_result < 0) {
-      if (max_loops) {
-        --max_loops;
-      }
-      if (max_loops > 0) {
-        av_seek_frame(format_context, -1, 0, AVSEEK_FLAG_BACKWARD);
-        read_result = 0;
-        continue;
-      }
-      if (flush) {
-        packet.stream_index = target_stream;
-        packet.size = 0;
-      } else {
-        break;
-      }
-    }
-
-    // Only decode packets from our target stream.
-    if (packet.stream_index == target_stream) {
-      int result = -1;
-      if (target_codec == AVMEDIA_TYPE_AUDIO) {
-        int size_out = 0;
-        int got_audio = 0;
-
-        avcodec_get_frame_defaults(audio_frame.get());
-
-        base::TimeTicks decode_start = base::TimeTicks::HighResNow();
-        result = avcodec_decode_audio4(codec_context, audio_frame.get(),
-                                       &got_audio, &packet);
-        base::TimeDelta delta = base::TimeTicks::HighResNow() - decode_start;
-
-        if (got_audio) {
-          size_out = av_samples_get_buffer_size(
-              NULL, codec_context->channels, audio_frame->nb_samples,
-              codec_context->sample_fmt, 1);
-        }
-
-        if (got_audio && size_out) {
-          decode_times.push_back(delta.InMillisecondsF());
-          ++frames;
-          read_result = 0;  // Force continuation.
-
-          if (output) {
-            if (fwrite(audio_frame->data[0], 1, size_out, output) !=
-                static_cast<size_t>(size_out)) {
-              std::cerr << "Error: Could not write "
-                        << size_out << " bytes for " << in_path.value()
-                        << std::endl;
-              return 1;
-            }
-          }
-
-          const uint8* u8_samples =
-              reinterpret_cast<const uint8*>(audio_frame->data[0]);
-          if (hash_djb2) {
-            hash_value = DJB2Hash(u8_samples, size_out, hash_value);
-          }
-          if (hash_md5) {
-            base::MD5Update(
-                &ctx,
-                base::StringPiece(reinterpret_cast<const char*>(u8_samples),
-                                                                size_out));
-          }
-        }
-      } else if (target_codec == AVMEDIA_TYPE_VIDEO) {
-        int got_picture = 0;
-
-        avcodec_get_frame_defaults(video_frame.get());
-
-        base::TimeTicks decode_start = base::TimeTicks::HighResNow();
-        result = avcodec_decode_video2(codec_context, video_frame.get(),
-                                       &got_picture, &packet);
-        base::TimeDelta delta = base::TimeTicks::HighResNow() - decode_start;
-
-        if (got_picture) {
-          decode_times.push_back(delta.InMillisecondsF());
-          ++frames;
-          read_result = 0;  // Force continuation.
-
-          for (int plane = 0; plane < 3; ++plane) {
-            const uint8* source = video_frame->data[plane];
-            const size_t source_stride = video_frame->linesize[plane];
-            size_t bytes_per_line = codec_context->width;
-            size_t copy_lines = codec_context->height;
-            if (plane != 0) {
-              switch (codec_context->pix_fmt) {
-                case PIX_FMT_YUV420P:
-                case PIX_FMT_YUVJ420P:
-                  bytes_per_line /= 2;
-                  copy_lines = (copy_lines + 1) / 2;
-                  break;
-                case PIX_FMT_YUV422P:
-                case PIX_FMT_YUVJ422P:
-                  bytes_per_line /= 2;
-                  break;
-                case PIX_FMT_YUV444P:
-                case PIX_FMT_YUVJ444P:
-                  break;
-                default:
-                  std::cerr << "Error: Unknown video format "
-                            << codec_context->pix_fmt;
-                  return 1;
-              }
-            }
-            if (output) {
-              for (size_t i = 0; i < copy_lines; ++i) {
-                if (fwrite(source, 1, bytes_per_line, output) !=
-                           bytes_per_line) {
-                  std::cerr << "Error: Could not write data after "
-                            << copy_lines << " lines for "
-                            << in_path.value() << std::endl;
-                  return 1;
-                }
-                source += source_stride;
-              }
-            }
-            if (hash_djb2) {
-              for (size_t i = 0; i < copy_lines; ++i) {
-                hash_value = DJB2Hash(source, bytes_per_line, hash_value);
-                source += source_stride;
-              }
-            }
-            if (hash_md5) {
-              for (size_t i = 0; i < copy_lines; ++i) {
-                base::MD5Update(
-                    &ctx,
-                    base::StringPiece(reinterpret_cast<const char*>(source),
-                                bytes_per_line));
-                source += source_stride;
-              }
-            }
-          }
-        }
-      } else {
-        NOTREACHED();
-      }
-
-      // Make sure our decoding went OK.
-      if (result < 0) {
-        std::cerr << "Error: avcodec_decode returned "
-                  << result << " for " << in_path.value() << std::endl;
-        return 1;
-      }
-    }
-    // Free our packet.
-    av_free_packet(&packet);
-
-    if (max_frames && (frames >= max_frames))
-      break;
-  } while (read_result >= 0);
-#if SHOW_VERBOSE
-  base::TimeDelta total = base::TimeTicks::HighResNow() - start;
-#endif
-  LeaveTimingSection();
-
-  // Clean up.
-  if (output)
-    file_util::CloseFile(output);
-
-  // Calculate the sum of times.  Note that some of these may be zero.
-  double sum = 0;
-  for (size_t i = 0; i < decode_times.size(); ++i) {
-    sum += decode_times[i];
-  }
-
-  if (sum > 0) {
-    if (target_codec == AVMEDIA_TYPE_AUDIO) {
-      // Calculate the average milliseconds per frame.
-      // Audio decoding is usually in the millisecond or range, and
-      // best expressed in time (ms) rather than FPS, which can approach
-      // infinity.
-      double ms = sum / frames;
-      // Print our results.
-      log_out->setf(std::ios::fixed);
-      log_out->precision(2);
-      *log_out << "TIME PER FRAME (MS):" << std::setw(11) << ms << std::endl;
-    } else if (target_codec == AVMEDIA_TYPE_VIDEO) {
-      // Calculate the average frames per second.
-      // Video decoding is expressed in Frames Per Second - a term easily
-      // understood and should exceed a typical target of 30 fps.
-      double fps = frames * 1000.0 / sum;
-      // Print our results.
-      log_out->setf(std::ios::fixed);
-      log_out->precision(2);
-      *log_out << "FPS:" << std::setw(11) << fps << std::endl;
-    }
-  }
-
-#if SHOW_VERBOSE
-  // Print our results.
-  log_out->setf(std::ios::fixed);
-  log_out->precision(2);
-  *log_out << std::endl;
-  *log_out << "     Frames:" << std::setw(11) << frames
-           << std::endl;
-  *log_out << "      Total:" << std::setw(11) << total.InMillisecondsF()
-           << " ms" << std::endl;
-  *log_out << "  Summation:" << std::setw(11) << sum
-           << " ms" << std::endl;
-
-  if (frames > 0) {
-    // Calculate the average time per frame.
-    double average = sum / frames;
-
-    // Calculate the sum of the squared differences.
-    // Standard deviation will only be accurate if no threads are used.
-    // TODO(fbarchard): Rethink standard deviation calculation.
-    double squared_sum = 0;
-    for (int i = 0; i < frames; ++i) {
-      double difference = decode_times[i] - average;
-      squared_sum += difference * difference;
-    }
-
-    // Calculate the standard deviation (jitter).
-    double stddev = sqrt(squared_sum / frames);
-
-    *log_out << "    Average:" << std::setw(11) << average
-             << " ms" << std::endl;
-    *log_out << "     StdDev:" << std::setw(11) << stddev
-             << " ms" << std::endl;
-  }
-  if (hash_djb2) {
-    *log_out << "  DJB2 Hash:" << std::setw(11) << hash_value
-             << "  " << in_path.value() << std::endl;
-  }
-  if (hash_md5) {
-    base::MD5Digest digest;  // The result of the computation.
-    base::MD5Final(&digest, &ctx);
-    *log_out << "   MD5 Hash: " << base::MD5DigestToBase16(digest)
-             << " " << in_path.value() << std::endl;
-  }
-#endif  // SHOW_VERBOSE
-#if defined(ENABLE_WINDOWS_EXCEPTIONS)
-  } __except(EXCEPTION_EXECUTE_HANDLER) {
-    *log_out << "  Exception:" << std::setw(11) << GetExceptionCode()
-             << " " << in_path.value() << std::endl;
-    return 1;
-  }
-#endif
-  CommandLine::Reset();
-  return 0;
-}
diff --git a/media/tools/bug_hunter/bug_hunter.py b/media/tools/bug_hunter/bug_hunter.py
deleted file mode 100755
index 19a2f8f..0000000
--- a/media/tools/bug_hunter/bug_hunter.py
+++ /dev/null
@@ -1,380 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""This script queries the Chromium issue tracker and e-mails the results.
-
-It queries issue tracker using Issue Tracker API. The query
-parameters can be specified by command-line arguments. For example, with the
-following command:
-
-  'python bug_hunter.py -q video Status:Unconfirmed OR audio Status:Unconfirmed
-   -s sender@chromium.org -r receiver@chromium.org -v 100 -u days'
-
-You will find all 'Unconfirmed' issues created in the last 100 days containing
-'video' or 'audio' in their content/comments. The content of these issues are
-sent to receiver@chromium.org.
-
-TODO(imasaki): users can specify the interval as say: "100d" for "100 days".
-
-There are two limitations in the current implementation of issue tracker API
-and UI:
-* only outermost OR is valid. For example, the query
-  'video OR audio Status:Unconfirmed' is translated into
-  'video OR (audio AND Status:Unconfirmed)'
-* brackets are not supported. For example, the query
-  '(video OR audio) Status:Unconfirmed' does not work.
-
-You need to install following to run this script
-  gdata-python-client (http://code.google.com/p/gdata-python-client/)
-  rfc3339.py (http://henry.precheur.org/projects/rfc3339)
-
-Links:
-* Chromium issue tracker: http://code.google.com/p/chromium/issues/list
-* Issue tracker API: http://code.google.com/p/support/wiki/IssueTrackerAPI
-* Search tips for the issue tracker:
-    http://code.google.com/p/chromium/issues/searchtips
-"""
-
-import csv
-import datetime
-from email.mime.multipart import MIMEMultipart
-from email.mime.text import MIMEText
-import logging
-from operator import itemgetter
-import optparse
-import re
-import smtplib
-import socket
-import sys
-import urllib
-
-try:
-  import gdata.data
-  import gdata.projecthosting.client
-except ImportError:
-  logging.error('gdata-client needs to be installed. Please install\n'
-                'and try again (http://code.google.com/p/gdata-python-client/)')
-  sys.exit(1)
-
-try:
-  import rfc3339
-except ImportError:
-  logging.error('rfc3339 needs to be installed. Please install\n'
-                'and try again (http://henry.precheur.org/projects/rfc3339)')
-  sys.exit(1)
-
-# A list of default values.
-_DEFAULT_INTERVAL_UNIT = 'hours'
-_DEFAULT_ISSUE_ELEMENT_IN_EMAIL = ('author', 'status', 'state', 'content',
-                                   'comments', 'labels', 'urls')
-_DEFAULT_PROJECT_NAME = 'chromium'
-_DEFAULT_QUERY_TITLE = 'potential media bugs'
-_DEFAULT_QUERY = ('video -has:Feature -has:Owner -label:nomedia '
-                  'status:Unconfirmed OR audio -has:Feature -has:Owner '
-                  '-label:nomedia status:Unconfirmed')
-_DEFAULT_OUTPUT_FILENAME = 'output.csv'
-_DETAULT_MAX_COMMENTS = 1000
-
-_INTERVAL_UNIT_CHOICES = ('hours', 'days', 'weeks')
-
-# URLs in this list are excluded from URL extraction from bug
-# content/comments. Each list element should not contain the url ending in
-# '/'. For example, the element should be 'http://www.google.com' but not
-# 'http://www.google.com/'
-_URL_EXCLUSION_LIST = ('http://www.youtube.com/html5',
-                       'http://www.google.com')
-_ISSUE_ELEMENT_IN_EMAIL_CHOICES = ('issue_id', 'author', 'status', 'state',
-                                   'content', 'comments', 'labels', 'urls',
-                                   'mstone')
-
-
-def ParseArgs():
-  """Returns options dictionary from parsed command line arguments."""
-  parser = optparse.OptionParser()
-
-  parser.add_option('-e', '--email-entries',
-                    help=('A comma-separated list of issue entries that are '
-                          'sent in the email content. '
-                          'Possible strings are %s. Default: %%default.' %
-                          ', '.join(_ISSUE_ELEMENT_IN_EMAIL_CHOICES)),
-                    default=','.join(_DEFAULT_ISSUE_ELEMENT_IN_EMAIL))
-  parser.add_option('-l', '--max-comments',
-                    help=('The maximum number of comments returned for each '
-                          'issue in a reverse chronological order. '
-                          'Default: %default.'),
-                    type='int', default=_DETAULT_MAX_COMMENTS)
-  parser.add_option('-o', '--output-filename',
-                    help=('Filename for result output in CSV format. '
-                          'Default: %default.'),
-                    default=_DEFAULT_OUTPUT_FILENAME, metavar='FILE')
-  parser.add_option('-p', '--project-name', default=_DEFAULT_PROJECT_NAME,
-                    help='Project name string. Default: %default')
-  parser.add_option('-q', '--query', default=_DEFAULT_QUERY,
-                    help=('Query to be used to find bugs. The detail can be '
-                          'found in Chromium Issue tracker page '
-                          'http://code.google.com/p/chromium/issues/searchtips.'
-                          ' Default: "%default".'))
-  parser.add_option('-r', '--receiver-email-address',
-                    help="Receiver's email address (Required).")
-  parser.add_option('-s', '--sender-email-address',
-                    help="Sender's email address (Required).")
-  parser.add_option('-t', '--query-title',
-                    default=_DEFAULT_QUERY_TITLE, dest='query_title',
-                    help=('Query title string used in the subject of the '
-                          'result email. Default: %default.'))
-  parser.add_option('-u', '--interval_unit', default=_DEFAULT_INTERVAL_UNIT,
-                    choices=_INTERVAL_UNIT_CHOICES,
-                    help=('Unit name for |interval_value|. Valid options are '
-                          '%s. Default: %%default' % (
-                              ', '.join(_INTERVAL_UNIT_CHOICES))))
-  parser.add_option('-v', '--interval-value', type='int',
-                    help=('Interval value to find bugs. '
-                          'The script looks for bugs during '
-                          'that interval (up to now). This option is used in '
-                          'conjunction with |--interval_unit| option. '
-                          'The script looks for all bugs if this is not '
-                          'specified.'))
-
-  options = parser.parse_args()[0]
-
-  options.email_entries = options.email_entries.split(',')
-  options.email_entries = [entry for entry in options.email_entries
-                           if entry in _ISSUE_ELEMENT_IN_EMAIL_CHOICES]
-  if not options.email_entries:
-    logging.warning('No issue elements in email in option. '
-                    'Default email entries will be used.')
-    options.email_entries = _DEFAULT_ISSUE_ELEMENT_IN_EMAIL
-  logging.info('The following is the issue elements in email: %s ' + (
-      ', '.join(options.email_entries)))
-  return options
-
-
-class BugHunter(object):
-  """This class queries issue trackers and e-mails the results."""
-
-  _ISSUE_SEARCH_LINK_BASE = ('http://code.google.com/p/chromium/issues/list?'
-                             'can=2&colspec=ID+Pri+Mstone+ReleaseBlock+Area'
-                             '+Feature+Status+Owner+Summary&cells=tiles'
-                             '&sort=-id')
-  # TODO(imasaki): Convert these into template library.
-  _EMAIL_ISSUE_TEMPLATE = ('<li><a href="http://crbug.com/%(issue_id)s">'
-                           '%(issue_id)s %(title)s</a> ')
-  _EMAIL_SUBJECT_TEMPLATE = ('BugHunter found %(n_issues)d %(query_title)s '
-                             'bug%(plural)s%(time_msg)s!')
-  _EMAIL_MSG_TEMPLATE = ('<a href="%(link_base)s&q=%(unquote_query_text)s">'
-                         'Used Query</a>: %(query_text)s<br><br>'
-                         'The number of issues : %(n_issues)d<br>'
-                         '<ul>%(issues)s</ul>')
-
-  def __init__(self, options):
-    """Sets up initial state for Bug Hunter.
-
-    Args:
-      options: Command-line options.
-    """
-    self._client = gdata.projecthosting.client.ProjectHostingClient()
-    self._options = options
-    self._issue_template = BugHunter._EMAIL_ISSUE_TEMPLATE
-    for entry in options.email_entries:
-      self._issue_template += '%%(%s)s ' % entry
-    self._issue_template += '</li>'
-
-  def GetComments(self, issue_id, max_comments):
-    """Get comments for a issue.
-
-    Args:
-      issue_id: Issue id for each issue in the issue tracker.
-      max_comments: The maximum number of comments to be returned. The comments
-        are returned in a reverse chronological order.
-
-    Returns:
-      A list of (author name, comments, updated time) tuples.
-    """
-    comments_feed = self._client.get_comments(self._options.project_name,
-                                              issue_id)
-    comment_list = [(comment.content.text, comment.author[0].name.text,
-                     comment.updated.text)
-                    for comment
-                    in list(reversed(comments_feed.entry))[0:max_comments]]
-    return comment_list
-
-  def GetIssues(self):
-    """Get issues from issue tracker and return them.
-
-    Returns:
-      A list of issues in descending order by issue_id. Each element in the
-        list is a dictionary where the keys are 'issue_id', 'title', 'author',
-        'status', 'state', 'content', 'comments', 'labels', 'urls'.
-        Returns an empty list when there is no matching issue.
-    """
-    min_time = None
-    if self._options.interval_value:
-      # Issue Tracker Data API uses RFC 3339 timestamp format, For example:
-      # 2005-08-09T10:57:00-08:00
-      # (http://code.google.com/p/support/wiki/IssueTrackerAPIPython)
-      delta = datetime.timedelta(
-          **{self._options.interval_unit: self._options.interval_value})
-      dt = datetime.datetime.now() - delta
-      min_time = rfc3339.rfc3339(dt)
-
-    query = gdata.projecthosting.client.Query(text_query=self._options.query,
-                                              max_results=1000,
-                                              published_min=min_time)
-
-    feed = self._client.get_issues(self._options.project_name, query=query)
-    if not feed.entry:
-      logging.info('No issues available to match query %s.',
-                   self._options.query)
-      return []
-    issues = []
-    for entry in feed.entry:
-      # The fully qualified id is a URL. We just want the number.
-      issue_id = entry.id.text.split('/')[-1]
-      if not issue_id.isdigit():
-        logging.warning('Issue_id is not correct: %s. Skipping.', issue_id)
-        continue
-      label_list = [label.text for label in entry.label]
-      comments = ''
-      if 'comments' in self._options.email_entries:
-        comments = ''.join(
-            [''.join(comment) if not comment else ''
-             for comment
-             in self.GetComments(issue_id, self._options.max_comments)])
-      content = BugHunterUtils.StripHTML(entry.content.text)
-      url_list = list(
-          set(re.findall(r'(https?://\S+)', content + comments)))
-      url_list = [url for url in url_list
-                  if not url.rstrip('/') in _URL_EXCLUSION_LIST]
-      mstone = ''
-      r = re.compile(r'Mstone-(\d*)')
-      for label in label_list:
-        m = r.search(label)
-        if m:
-          mstone = m.group(1)
-      issues.append(
-          {'issue_id': issue_id, 'title': entry.title.text,
-           'author': entry.author[0].name.text,
-           'status': entry.status.text if entry.status is not None else '',
-           'state': entry.state.text if entry.state is not None else '',
-           'content': content, 'mstone': mstone, 'comments': comments,
-           'labels': label_list, 'urls': url_list})
-    return sorted(issues, key=itemgetter('issue_id'), reverse=True)
-
-  def _SetUpEmailSubjectMsg(self, issues):
-    """Set up email subject and its content.
-
-    Args:
-      issues: Please refer to the return value in GetIssues().
-
-    Returns:
-      A tuple of two strings (email subject and email content).
-    """
-    time_msg = ''
-    if self._options.interval_value:
-      time_msg = ' in the past %s %s%s' % (
-          self._options.interval_value, self._options.interval_unit[:-1],
-          's' if self._options.interval_value > 1 else '')
-    subject = BugHunter._EMAIL_SUBJECT_TEMPLATE % {
-        'n_issues': len(issues),
-        'query_title': self._options.query_title,
-        'plural': 's' if len(issues) > 1 else '',
-        'time_msg': time_msg}
-    content = BugHunter._EMAIL_MSG_TEMPLATE % {
-        'link_base': BugHunter._ISSUE_SEARCH_LINK_BASE,
-        'unquote_query_text': urllib.quote(self._options.query),
-        'query_text': self._options.query,
-        'n_issues': len(issues),
-        'issues': ''.join(
-            [self._issue_template % issue for issue in issues])}
-    return (subject, content)
-
-  def SendResultEmail(self, issues):
-    """Send result email.
-
-    Args:
-      issues: Please refer to the return value in GetIssues().
-    """
-    subject, content = self._SetUpEmailSubjectMsg(issues)
-    BugHunterUtils.SendEmail(
-        content, self._options.sender_email_address,
-        self._options.receiver_email_address, subject)
-
-  def WriteIssuesToFileInCSV(self, issues, filename):
-    """Write issues to a file in CSV format.
-
-    Args:
-      issues: Please refer to the return value in GetIssues().
-      filename: File name for CSV file.
-    """
-    with open(filename, 'w') as f:
-      writer = csv.writer(f)
-      # Write header first.
-      writer.writerow(issues[0].keys())
-      for issue in issues:
-        writer.writerow(
-            [unicode(value).encode('utf-8') for value in issue.values()])
-
-
-class BugHunterUtils(object):
-  """Utility class for Bug Hunter."""
-
-  @staticmethod
-  def StripHTML(string_with_html):
-    """Strip HTML tags from string.
-
-    Args:
-      string_with_html: A string with HTML tags.
-
-    Returns:
-      A string without HTML tags.
-    """
-    return re.sub('<[^<]+?>', '', string_with_html)
-
-  @staticmethod
-  def SendEmail(message, sender_email_address, receivers_email_address,
-                subject):
-    """Send email using localhost's mail server.
-
-    Args:
-      message: Email message to be sent.
-      sender_email_address: Sender's email address.
-      receivers_email_address: Receiver's email address.
-      subject: Email subject.
-
-    Returns:
-      True if successful; False, otherwise.
-    """
-    try:
-      html = '<html><head></head><body>%s</body></html>' % message
-      msg = MIMEMultipart('alternative')
-      msg['Subject'] = subject
-      msg['From'] = sender_email_address
-      msg['To'] = receivers_email_address
-      msg.attach(MIMEText(html.encode('utf-8'), 'html', _charset='utf-8'))
-      smtp_obj = smtplib.SMTP('localhost')
-      smtp_obj.sendmail(sender_email_address, receivers_email_address,
-                        msg.as_string())
-      logging.info('Successfully sent email.')
-      smtp_obj.quit()
-      return True
-    except smtplib.SMTPException:
-      logging.exception('Authentication failed, unable to send email.')
-    except (socket.gaierror, socket.error, socket.herror):
-      logging.exception('Unable to send email.')
-    return False
-
-
-def Main():
-  ops = ParseArgs()
-  bh = BugHunter(ops)
-  issues = bh.GetIssues()
-  if issues and ops.sender_email_address and ops.receiver_email_address:
-    bh.SendResultEmail(issues)
-  if issues:
-    bh.WriteIssuesToFileInCSV(issues, ops.output_filename)
-
-
-if __name__ == '__main__':
-  Main()
diff --git a/media/tools/bug_hunter/bug_hunter_test.py b/media/tools/bug_hunter/bug_hunter_test.py
deleted file mode 100644
index 0dafd8a..0000000
--- a/media/tools/bug_hunter/bug_hunter_test.py
+++ /dev/null
@@ -1,96 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Integration tests for bug hunter."""
-
-import csv
-from optparse import Values
-import os
-import unittest
-
-from bug_hunter import BugHunter
-
-try:
-  import gdata.data
-  import gdata.projecthosting.client
-except ImportError:
-  logging.error('gdata-client needs to be installed. Please install\n'
-                'and try again (http://code.google.com/p/gdata-python-client/)')
-  sys.exit(1)
-
-
-class BugHunterTest(unittest.TestCase):
-  """Unit tests for the Bug Hunter class."""
-  _TEST_FILENAME = 'test.csv'
-
-  def _CleanTestFile(self):
-    if os.path.exists(self._TEST_FILENAME):
-      os.remove(self._TEST_FILENAME)
-
-  def setUp(self):
-    self._CleanTestFile()
-
-  def tearDown(self):
-    self._CleanTestFile()
-
-  def _GetIssue(self):
-    return [{'issue_id': '0', 'title': 'title', 'author': 'author',
-             'status': 'status', 'state': 'state', 'content': 'content',
-             'comments': [], 'labels': [], 'urls': []}]
-
-  def _GetDefaultOption(self, set_10_days_ago, query='steps'):
-    ops = Values()
-    ops.query = query
-    if set_10_days_ago:
-      ops.interval_value = 10
-      ops.interval_unit = 'days'
-    else:
-      ops.interval_value = None
-    ops.email_entries = ['comments']
-    ops.project_name = 'chromium'
-    ops.query_title = 'query title'
-    ops.max_comments = None
-    return ops
-
-  def testGetIssueReturnedIssue(self):
-    bh = BugHunter(
-        self._GetDefaultOption(False,
-                               query=('audio opened-after:2010/10/10'
-                                      ' opened-before:2010/10/20')))
-    self.assertEquals(len(bh.GetIssues()), 18)
-
-  def testGetIssueReturnedIssueWithStatus(self):
-    ops = self._GetDefaultOption(False)
-    ops.query = 'Feature:Media* Status:Unconfirmed'
-    issues = BugHunter(ops).GetIssues()
-    for issue in issues:
-      self.assertEquals(issue['status'], 'Unconfirmed')
-
-  def testGetIssueReturnNoIssue(self):
-    ops = self._GetDefaultOption(True)
-    ops.query = 'thisshouldnotmatchpleaseignorethis*'
-    self.assertFalse(BugHunter(ops).GetIssues())
-
-  def testGetComments(self):
-    comments = BugHunter(self._GetDefaultOption(False)).GetComments(100000, 2)
-    self.assertEquals(len(comments), 2)
-    expected_comments = [(None, 'rby...@chromium.org',
-                          '2011-10-31T19:54:40.000Z'),
-                         (None, 'backer@chromium.org',
-                          '2011-10-14T13:59:37.000Z')]
-    self.assertEquals(comments, expected_comments)
-
-  def testWriteIssuesToFileInCSV(self):
-    ops = self._GetDefaultOption(False)
-    bh = BugHunter(ops)
-    bh.WriteIssuesToFileInCSV(self._GetIssue(), self._TEST_FILENAME)
-
-    with open(self._TEST_FILENAME, 'r') as f:
-      reader = csv.reader(f)
-      self.assertEquals(reader.next(), ['status', 'content', 'state',
-                                        'issue_id', 'urls', 'title', 'labels',
-                                        'author', 'comments'])
-      self.assertEquals(reader.next(), ['status', 'content', 'state', '0',
-                                        '[]', 'title', '[]', 'author', '[]'])
-      self.assertRaises(StopIteration, reader.next)
diff --git a/media/tools/bug_hunter/bug_hunter_unittest.py b/media/tools/bug_hunter/bug_hunter_unittest.py
deleted file mode 100644
index 0cb11b6..0000000
--- a/media/tools/bug_hunter/bug_hunter_unittest.py
+++ /dev/null
@@ -1,182 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Unit Tests for bug hunter."""
-
-import logging
-from optparse import Values
-import smtplib
-import sys
-import unittest
-
-from bug_hunter import BugHunter
-from bug_hunter import BugHunterUtils
-
-try:
-  import atom.data
-  import gdata.data
-  import gdata.projecthosting.client
-except ImportError:
-  logging.error('gdata-client needs to be installed. Please install\n'
-                'and try again (http://code.google.com/p/gdata-python-client/)')
-  sys.exit(1)
-
-
-class MockClient(object):
-  """A mock class for gdata.projecthosting.client.ProjectHostingClient.
-
-  Mocking the very simple method invocations for get_issues() and
-  get_comments().
-  """
-
-  def _CreateIssues(self, n_issues):
-    feed = gdata.projecthosting.data.IssuesFeed()
-    for i in xrange(n_issues):
-      feed.entry.append(gdata.projecthosting.data.IssueEntry(
-          title=atom.data.Title(text='title'),
-          content=atom.data.Content(text='http://www.content.com'),
-          id=atom.data.Id(text='/' + str(i)),
-          status=gdata.projecthosting.data.Status(text='Unconfirmed'),
-          state=gdata.projecthosting.data.State(text='open'),
-          label=[gdata.projecthosting.data.Label('label1')],
-          author=[atom.data.Author(name=atom.data.Name(text='author'))]))
-    return feed
-
-  def get_issues(self, project_name, query):
-    """Get issues using mock object without calling the issue tracker API.
-
-    Based on query argument, this returns the dummy issues. The number of
-    dummy issues are specified in query.text_query.
-
-    Args:
-      project_name: A string for project name in the issue tracker.
-      query: A query object for querying the issue tracker.
-
-    Returns:
-       A IssuesFeed object that contains a simple test issue.
-    """
-    n_issues = 1
-    if query.text_query.isdigit():
-      n_issues = int(query.text_query)
-    return self._CreateIssues(n_issues)
-
-  def get_comments(self, project_name, issue_id):
-    """Get comments using mock object without calling the issue tracker API.
-
-    Args:
-      project_name: A string for project name in the issue tracker.
-      issue_id: Issue_id string.
-
-    Returns:
-       A CommentsFeed object that contains a simple test comment.
-    """
-    feed = gdata.projecthosting.data.CommentsFeed()
-    feed.entry = [gdata.projecthosting.data.CommentEntry(
-        id=atom.data.Id(text='/0'),
-        content=atom.data.Content(text='http://www.comments.com'),
-        updated=atom.data.Updated(text='Updated'),
-        author=[atom.data.Author(name=atom.data.Name(text='cauthor'))])]
-    return feed
-
-
-class BugHunterUnitTest(unittest.TestCase):
-  """Unit tests for the Bug Hunter class."""
-
-  def setUp(self):
-    self._old_client = gdata.projecthosting.client.ProjectHostingClient
-    gdata.projecthosting.client.ProjectHostingClient = MockClient
-
-  def tearDown(self):
-    gdata.projecthosting.client.ProjectHostingClient = self._old_client
-
-  def _GetDefaultOption(self, set_10_days_ago, query='steps'):
-    ops = Values()
-    ops.query = query
-    if set_10_days_ago:
-      ops.interval_value = 10
-      ops.interval_unit = 'days'
-    else:
-      ops.interval_value = None
-    ops.email_entries = ['comments']
-    ops.project_name = 'chromium'
-    ops.query_title = 'query title'
-    ops.max_comments = None
-    return ops
-
-  def _GetIssue(self, n_issues):
-    issues = []
-    for i in xrange(n_issues):
-      issues.append({'issue_id': str(i), 'title': 'title', 'author': 'author',
-                     'status': 'status', 'state': 'state',
-                     'content': 'content', 'comments': [],
-                     'labels': [], 'urls': []})
-    return issues
-
-  def testSetUpEmailSubjectMsg(self):
-    bh = BugHunter(self._GetDefaultOption(False))
-    subject, content = bh._SetUpEmailSubjectMsg(self._GetIssue(1))
-    self.assertEquals(subject,
-                      'BugHunter found 1 query title bug!')
-    self.assertEquals(content,
-                      ('<a href="http://code.google.com/p/chromium/issues/'
-                       'list?can=2&colspec=ID+Pri+Mstone+ReleaseBlock+Area+'
-                       'Feature+Status+Owner+Summary&cells=tiles&sort=-id&'
-                       'q=steps">Used Query</a>: steps<br><br>The number of '
-                       'issues : 1<br><ul><li><a href="http://crbug.com/0">0 '
-                       'title</a> [] </li></ul>'))
-
-  def testSetUpEmailSubjectMsgMultipleIssues(self):
-    bh = BugHunter(self._GetDefaultOption(False))
-    subject, content = bh._SetUpEmailSubjectMsg(self._GetIssue(2))
-    self.assertEquals(subject,
-                      'BugHunter found 2 query title bugs!')
-
-  def testSetUpEmailSubjectMsgWith10DaysAgoAndAssertSubject(self):
-    bh = BugHunter(self._GetDefaultOption(True))
-    subject, _ = bh._SetUpEmailSubjectMsg(self._GetIssue(1))
-    self.assertEquals(subject,
-                      ('BugHunter found 1 query title bug in the past 10 '
-                       'days!'))
-
-  def testGetIssuesWithMockClient(self):
-    bh = BugHunter(self._GetDefaultOption(False,
-                                          query=('dummy')))
-    expected_issues = [{'issue_id': '0', 'title': 'title', 'author': 'author',
-                        'status': 'Unconfirmed', 'state': 'open',
-                        'content': 'http://www.content.com',
-                        'comments': '', 'labels': ['label1'],
-                        'urls': ['http://www.content.com']}]
-    self.assertEquals(expected_issues, bh.GetIssues())
-
-
-class MockSmtp(object):
-  """A mock class for SMTP."""
-
-  def __init__(self, server):
-    pass
-
-  def sendmail(self, sender_email_address, receivers_email_addresses,
-               msg):
-    # TODO(imasaki): Do something here.
-    return True
-
-  def quit(self):
-    pass
-
-
-class BugHunterUtilsTest(unittest.TestCase):
-  """Unit tests for the Bug Hunter utility."""
-
-  def testStripHTML(self):
-    self.assertEquals(BugHunterUtils.StripHTML('<p>X</p>'), 'X')
-
-  def testStripHTMLEmpty(self):
-    self.assertEquals(BugHunterUtils.StripHTML(''), '')
-
-  def testSendEmail(self):
-    smtplib.SMTP = MockSmtp
-    self.assertEqual(BugHunterUtils.SendEmail('message', 'sender_email_address',
-                                              'receivers_email_addresses',
-                                              'subject'),
-                     True)
diff --git a/media/tools/demuxer_bench/demuxer_bench.cc b/media/tools/demuxer_bench/demuxer_bench.cc
index d38e587..ab8b313 100644
--- a/media/tools/demuxer_bench/demuxer_bench.cc
+++ b/media/tools/demuxer_bench/demuxer_bench.cc
@@ -48,8 +48,8 @@
   message_loop->PostTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
 }
 
-static void NeedKey(const std::string& type, scoped_ptr<uint8[]> init_data,
-             int init_data_size) {
+static void NeedKey(const std::string& type,
+                    const std::vector<uint8>& init_data) {
   LOG(INFO) << "File is encrypted.";
 }
 
@@ -194,7 +194,7 @@
   media::FileDataSource data_source;
   CHECK(data_source.Initialize(file_path));
 
-  media::FFmpegNeedKeyCB need_key_cb = base::Bind(&NeedKey);
+  media::Demuxer::NeedKeyCB need_key_cb = base::Bind(&NeedKey);
   media::FFmpegDemuxer demuxer(message_loop.message_loop_proxy(),
                                &data_source,
                                need_key_cb,
diff --git a/media/tools/media_bench/media_bench.cc b/media/tools/media_bench/media_bench.cc
deleted file mode 100644
index 4214988..0000000
--- a/media/tools/media_bench/media_bench.cc
+++ /dev/null
@@ -1,588 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Standalone benchmarking application based on FFmpeg.  This tool is used to
-// measure decoding performance between different FFmpeg compile and run-time
-// options.  We also use this tool to measure performance regressions when
-// testing newer builds of FFmpeg from trunk.
-
-#include <iomanip>
-#include <iostream>
-#include <string>
-
-#include "base/at_exit.h"
-#include "base/basictypes.h"
-#include "base/command_line.h"
-#include "base/file_util.h"
-#include "base/files/file_path.h"
-#include "base/files/memory_mapped_file.h"
-#include "base/logging.h"
-#include "base/md5.h"
-#include "base/path_service.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/time/time.h"
-#include "build/build_config.h"
-#include "media/base/djb2.h"
-#include "media/base/media.h"
-#include "media/ffmpeg/ffmpeg_common.h"
-#include "media/filters/ffmpeg_glue.h"
-#include "media/filters/ffmpeg_video_decoder.h"
-#include "media/filters/in_memory_url_protocol.h"
-
-// For pipe _setmode to binary
-#if defined(OS_WIN)
-#include <fcntl.h>
-#include <io.h>
-#endif
-
-namespace switches {
-const char kStream[]       = "stream";
-const char kVideoThreads[] = "video-threads";
-const char kFast2[]        = "fast2";
-const char kErrorCorrection[] = "error-correction";
-const char kSkip[]         = "skip";
-const char kFlush[]        = "flush";
-const char kDjb2[]         = "djb2";
-const char kMd5[]          = "md5";
-const char kFrames[]       = "frames";
-const char kLoop[]         = "loop";
-
-}  // namespace switches
-
-#if defined(OS_WIN)
-
-// Enable to build with exception handler
-// #define ENABLE_WINDOWS_EXCEPTIONS 1
-
-#ifdef ENABLE_WINDOWS_EXCEPTIONS
-// warning: disable warning about exception handler.
-#pragma warning(disable:4509)
-#endif
-
-// Thread priorities to make benchmark more stable.
-
-void EnterTimingSection() {
-  SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
-}
-
-void LeaveTimingSection() {
-  SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
-}
-#else
-void EnterTimingSection() {
-  pthread_attr_t pta;
-  struct sched_param param;
-
-  pthread_attr_init(&pta);
-  memset(&param, 0, sizeof(param));
-  param.sched_priority = 78;
-  pthread_attr_setschedparam(&pta, &param);
-  pthread_attr_destroy(&pta);
-}
-
-void LeaveTimingSection() {
-}
-#endif
-
-int main(int argc, const char** argv) {
-  base::AtExitManager exit_manager;
-
-  CommandLine::Init(argc, argv);
-
-  logging::LoggingSettings settings;
-  settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
-  logging::InitLogging(settings);
-
-  const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
-  const CommandLine::StringVector& filenames = cmd_line->GetArgs();
-  if (filenames.empty()) {
-    std::cerr << "Usage: " << argv[0] << " [OPTIONS] FILE [DUMPFILE]\n"
-              << "  --stream=[audio|video]          "
-              << "Benchmark either the audio or video stream\n"
-              << "  --video-threads=N               "
-              << "Decode video using N threads\n"
-              << "  --frames=N                      "
-              << "Decode N frames\n"
-              << "  --loop=N                        "
-              << "Loop N times\n"
-              << "  --fast2                         "
-              << "Enable fast2 flag\n"
-              << "  --error-correction              "
-              << "Enable ffmpeg error correction\n"
-              << "  --flush                         "
-              << "Flush last frame\n"
-              << "  --djb2 (aka --hash)             "
-              << "Hash decoded buffers (DJB2)\n"
-              << "  --md5                           "
-              << "Hash decoded buffers (MD5)\n"
-              << "  --skip=[1|2|3]                  "
-              << "1=loop nonref, 2=loop, 3= frame nonref\n" << std::endl;
-    return 1;
-  }
-
-  // Initialize our media library (try loading DLLs, etc.) before continuing.
-  base::FilePath media_path;
-  PathService::Get(base::DIR_MODULE, &media_path);
-  if (!media::InitializeMediaLibrary(media_path)) {
-    std::cerr << "Unable to initialize the media library." << std::endl;
-    return 1;
-  }
-
-  // Retrieve command line options.
-  base::FilePath in_path(filenames[0]);
-  base::FilePath out_path;
-  if (filenames.size() > 1)
-    out_path = base::FilePath(filenames[1]);
-  AVMediaType target_codec = AVMEDIA_TYPE_UNKNOWN;
-
-  // Determine whether to benchmark audio or video decoding.
-  std::string stream(cmd_line->GetSwitchValueASCII(switches::kStream));
-  if (!stream.empty()) {
-    if (stream.compare("audio") == 0) {
-      target_codec = AVMEDIA_TYPE_AUDIO;
-    } else if (stream.compare("video") == 0) {
-      target_codec = AVMEDIA_TYPE_VIDEO;
-    } else {
-      std::cerr << "Unknown --stream option " << stream << std::endl;
-      return 1;
-    }
-  }
-
-  // Determine number of threads to use for video decoding (optional).
-  int video_threads = 0;
-  std::string threads(cmd_line->GetSwitchValueASCII(switches::kVideoThreads));
-  if (!threads.empty() &&
-      !base::StringToInt(threads, &video_threads)) {
-    video_threads = 0;
-  }
-
-  // Determine number of frames to decode (optional).
-  int max_frames = 0;
-  std::string frames_opt(cmd_line->GetSwitchValueASCII(switches::kFrames));
-  if (!frames_opt.empty() &&
-      !base::StringToInt(frames_opt, &max_frames)) {
-    max_frames = 0;
-  }
-
-  // Determine number of times to loop (optional).
-  int max_loops = 0;
-  std::string loop_opt(cmd_line->GetSwitchValueASCII(switches::kLoop));
-  if (!loop_opt.empty() &&
-      !base::StringToInt(loop_opt, &max_loops)) {
-    max_loops = 0;
-  }
-
-  bool fast2 = false;
-  if (cmd_line->HasSwitch(switches::kFast2)) {
-    fast2 = true;
-  }
-
-  bool error_correction = false;
-  if (cmd_line->HasSwitch(switches::kErrorCorrection)) {
-    error_correction = true;
-  }
-
-  bool flush = false;
-  if (cmd_line->HasSwitch(switches::kFlush)) {
-    flush = true;
-  }
-
-  unsigned int hash_value = 5381u;  // Seed for DJB2.
-  bool hash_djb2 = false;
-  if (cmd_line->HasSwitch(switches::kDjb2)) {
-    hash_djb2 = true;
-  }
-
-  base::MD5Context ctx;  // Intermediate MD5 data: do not use
-  base::MD5Init(&ctx);
-  bool hash_md5 = false;
-  if (cmd_line->HasSwitch(switches::kMd5))
-    hash_md5 = true;
-
-  int skip = 0;
-  if (cmd_line->HasSwitch(switches::kSkip)) {
-    std::string skip_opt(cmd_line->GetSwitchValueASCII(switches::kSkip));
-    if (!base::StringToInt(skip_opt, &skip)) {
-      skip = 0;
-    }
-  }
-
-  std::ostream* log_out = &std::cout;
-#if defined(ENABLE_WINDOWS_EXCEPTIONS)
-  // Catch exceptions so this tool can be used in automated testing.
-  __try {
-#endif
-
-  base::MemoryMappedFile file_data;
-  file_data.Initialize(in_path);
-  media::InMemoryUrlProtocol protocol(
-      file_data.data(), file_data.length(), false);
-
-  // Register FFmpeg and attempt to open file.
-  media::FFmpegGlue glue(&protocol);
-  if (!glue.OpenContext()) {
-    std::cerr << "Error: Could not open input for "
-              << in_path.value() << std::endl;
-    return 1;
-  }
-
-  AVFormatContext* format_context = glue.format_context();
-
-  // Open output file.
-  FILE *output = NULL;
-  if (!out_path.empty()) {
-    // TODO(fbarchard): Add pipe:1 for piping to stderr.
-    if (out_path.value().substr(0, 5) == FILE_PATH_LITERAL("pipe:") ||
-        out_path.value() == FILE_PATH_LITERAL("-")) {
-      output = stdout;
-      log_out = &std::cerr;
-#if defined(OS_WIN)
-      _setmode(_fileno(stdout), _O_BINARY);
-#endif
-    } else {
-      output = file_util::OpenFile(out_path, "wb");
-    }
-    if (!output) {
-      std::cerr << "Error: Could not open output "
-                << out_path.value() << std::endl;
-      return 1;
-    }
-  }
-
-  // Parse a little bit of the stream to fill out the format context.
-  if (avformat_find_stream_info(format_context, NULL) < 0) {
-    std::cerr << "Error: Could not find stream info for "
-              << in_path.value() << std::endl;
-    return 1;
-  }
-
-  // Find our target stream.
-  int target_stream = -1;
-  for (size_t i = 0; i < format_context->nb_streams; ++i) {
-    AVCodecContext* codec_context = format_context->streams[i]->codec;
-    AVCodec* codec = avcodec_find_decoder(codec_context->codec_id);
-
-    // See if we found our target codec.
-    if (codec_context->codec_type == target_codec && target_stream < 0) {
-      *log_out << "* ";
-      target_stream = i;
-    } else {
-      *log_out << "  ";
-    }
-
-    if (!codec || (codec_context->codec_type == AVMEDIA_TYPE_UNKNOWN)) {
-      *log_out << "Stream #" << i << ": Unknown" << std::endl;
-    } else {
-      // Print out stream information
-      *log_out << "Stream #" << i << ": " << codec->name << " ("
-               << codec->long_name << ")" << std::endl;
-    }
-  }
-
-  // Only continue if we found our target stream.
-  if (target_stream < 0) {
-    std::cerr << "Error: Could not find target stream "
-              << target_stream << " for " << in_path.value() << std::endl;
-    return 1;
-  }
-
-  // Prepare FFmpeg structures.
-  AVPacket packet;
-  AVCodecContext* codec_context = format_context->streams[target_stream]->codec;
-  AVCodec* codec = avcodec_find_decoder(codec_context->codec_id);
-
-  // Only continue if we found our codec.
-  if (!codec) {
-    std::cerr << "Error: Could not find codec for "
-              << in_path.value() << std::endl;
-    return 1;
-  }
-
-  if (skip == 1) {
-    codec_context->skip_loop_filter = AVDISCARD_NONREF;
-  } else if (skip == 2) {
-    codec_context->skip_loop_filter = AVDISCARD_ALL;
-  } else if (skip == 3) {
-    codec_context->skip_loop_filter = AVDISCARD_ALL;
-    codec_context->skip_frame = AVDISCARD_NONREF;
-  }
-  if (fast2) {
-    // Note this flag is no longer necessary for H264 multithreading.
-    codec_context->flags2 |= CODEC_FLAG2_FAST;
-  }
-  if (error_correction) {
-    codec_context->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK;
-  }
-
-  // Initialize threaded decode.
-  if (target_codec == AVMEDIA_TYPE_VIDEO && video_threads > 0) {
-    codec_context->thread_count = video_threads;
-  }
-
-  // Initialize our codec.
-  if (avcodec_open2(codec_context, codec, NULL) < 0) {
-    std::cerr << "Error: Could not open codec "
-              << (codec_context->codec ? codec_context->codec->name : "(NULL)")
-              << " for " << in_path.value() << std::endl;
-    return 1;
-  }
-
-  // Buffer used for audio decoding.
-  scoped_ptr_malloc<AVFrame, media::ScopedPtrAVFree> audio_frame(
-      avcodec_alloc_frame());
-  if (!audio_frame) {
-    std::cerr << "Error: avcodec_alloc_frame for "
-              << in_path.value() << std::endl;
-    return 1;
-  }
-
-  // Buffer used for video decoding.
-  scoped_ptr_malloc<AVFrame, media::ScopedPtrAVFree> video_frame(
-      avcodec_alloc_frame());
-  if (!video_frame) {
-    std::cerr << "Error: avcodec_alloc_frame for "
-              << in_path.value() << std::endl;
-    return 1;
-  }
-
-  // Remember size of video.
-  int video_width = codec_context->width;
-  int video_height = codec_context->height;
-
-  // Stats collector.
-  EnterTimingSection();
-  std::vector<double> decode_times;
-  decode_times.reserve(4096);
-  // Parse through the entire stream until we hit EOF.
-  base::TimeTicks start = base::TimeTicks::HighResNow();
-  int frames = 0;
-  int read_result = 0;
-  do {
-    read_result = av_read_frame(format_context, &packet);
-
-    if (read_result < 0) {
-      if (max_loops) {
-        --max_loops;
-      }
-      if (max_loops > 0) {
-        av_seek_frame(format_context, -1, 0, AVSEEK_FLAG_BACKWARD);
-        read_result = 0;
-        continue;
-      }
-      if (flush) {
-        packet.stream_index = target_stream;
-        packet.size = 0;
-      } else {
-        break;
-      }
-    }
-
-    // Only decode packets from our target stream.
-    if (packet.stream_index == target_stream) {
-      int result = -1;
-      if (target_codec == AVMEDIA_TYPE_AUDIO) {
-        int size_out = 0;
-        int got_audio = 0;
-
-        avcodec_get_frame_defaults(audio_frame.get());
-
-        base::TimeTicks decode_start = base::TimeTicks::HighResNow();
-        result = avcodec_decode_audio4(codec_context, audio_frame.get(),
-                                       &got_audio, &packet);
-        base::TimeDelta delta = base::TimeTicks::HighResNow() - decode_start;
-
-        if (got_audio) {
-          size_out = av_samples_get_buffer_size(
-              NULL, codec_context->channels, audio_frame->nb_samples,
-              codec_context->sample_fmt, 1);
-        }
-
-        if (got_audio && size_out) {
-          decode_times.push_back(delta.InMillisecondsF());
-          ++frames;
-          read_result = 0;  // Force continuation.
-
-          if (output) {
-            if (fwrite(audio_frame->data[0], 1, size_out, output) !=
-                static_cast<size_t>(size_out)) {
-              std::cerr << "Error: Could not write "
-                        << size_out << " bytes for " << in_path.value()
-                        << std::endl;
-              return 1;
-            }
-          }
-
-          const uint8* u8_samples =
-              reinterpret_cast<const uint8*>(audio_frame->data[0]);
-          if (hash_djb2) {
-            hash_value = DJB2Hash(u8_samples, size_out, hash_value);
-          }
-          if (hash_md5) {
-            base::MD5Update(
-                &ctx,
-                base::StringPiece(reinterpret_cast<const char*>(u8_samples),
-                                                                size_out));
-          }
-        }
-      } else if (target_codec == AVMEDIA_TYPE_VIDEO) {
-        int got_picture = 0;
-
-        avcodec_get_frame_defaults(video_frame.get());
-
-        base::TimeTicks decode_start = base::TimeTicks::HighResNow();
-        result = avcodec_decode_video2(codec_context, video_frame.get(),
-                                       &got_picture, &packet);
-        base::TimeDelta delta = base::TimeTicks::HighResNow() - decode_start;
-
-        if (got_picture) {
-          decode_times.push_back(delta.InMillisecondsF());
-          ++frames;
-          read_result = 0;  // Force continuation.
-
-          for (int plane = 0; plane < 3; ++plane) {
-            const uint8* source = video_frame->data[plane];
-            const size_t source_stride = video_frame->linesize[plane];
-            size_t bytes_per_line = codec_context->width;
-            size_t copy_lines = codec_context->height;
-            if (plane != 0) {
-              switch (codec_context->pix_fmt) {
-                case PIX_FMT_YUV420P:
-                case PIX_FMT_YUVJ420P:
-                  bytes_per_line /= 2;
-                  copy_lines = (copy_lines + 1) / 2;
-                  break;
-                case PIX_FMT_YUV422P:
-                case PIX_FMT_YUVJ422P:
-                  bytes_per_line /= 2;
-                  break;
-                case PIX_FMT_YUV444P:
-                case PIX_FMT_YUVJ444P:
-                  break;
-                default:
-                  std::cerr << "Error: Unknown video format "
-                            << codec_context->pix_fmt;
-                  return 1;
-              }
-            }
-            if (output) {
-              for (size_t i = 0; i < copy_lines; ++i) {
-                if (fwrite(source, 1, bytes_per_line, output) !=
-                           bytes_per_line) {
-                  std::cerr << "Error: Could not write data after "
-                            << copy_lines << " lines for "
-                            << in_path.value() << std::endl;
-                  return 1;
-                }
-                source += source_stride;
-              }
-            }
-            if (hash_djb2) {
-              for (size_t i = 0; i < copy_lines; ++i) {
-                hash_value = DJB2Hash(source, bytes_per_line, hash_value);
-                source += source_stride;
-              }
-            }
-            if (hash_md5) {
-              for (size_t i = 0; i < copy_lines; ++i) {
-                base::MD5Update(
-                    &ctx,
-                    base::StringPiece(reinterpret_cast<const char*>(source),
-                                      bytes_per_line));
-                source += source_stride;
-              }
-            }
-          }
-        }
-      } else {
-        NOTREACHED();
-      }
-
-      // Make sure our decoding went OK.
-      if (result < 0) {
-        std::cerr << "Error: avcodec_decode returned "
-                  << result << " for " << in_path.value() << std::endl;
-        return 1;
-      }
-    }
-    // Free our packet.
-    av_free_packet(&packet);
-
-    if (max_frames && (frames >= max_frames))
-      break;
-  } while (read_result >= 0);
-  base::TimeDelta total = base::TimeTicks::HighResNow() - start;
-  LeaveTimingSection();
-
-  // Clean up.
-  if (output)
-    file_util::CloseFile(output);
-
-  // Calculate the sum of times.  Note that some of these may be zero.
-  double sum = 0;
-  for (size_t i = 0; i < decode_times.size(); ++i) {
-    sum += decode_times[i];
-  }
-
-  double average = 0;
-  double stddev = 0;
-  double fps = 0;
-  if (frames > 0) {
-    // Calculate the average time per frame.
-    average = sum / frames;
-
-    // Calculate the sum of the squared differences.
-    // Standard deviation will only be accurate if no threads are used.
-    // TODO(fbarchard): Rethink standard deviation calculation.
-    double squared_sum = 0;
-    for (int i = 0; i < frames; ++i) {
-      double difference = decode_times[i] - average;
-      squared_sum += difference * difference;
-    }
-
-    // Calculate the standard deviation (jitter).
-    stddev = sqrt(squared_sum / frames);
-
-    // Calculate frames per second.
-    fps = frames * 1000.0 / sum;
-  }
-
-  // Print our results.
-  log_out->setf(std::ios::fixed);
-  log_out->precision(2);
-  *log_out << std::endl;
-  *log_out << "     Frames:" << std::setw(11) << frames << std::endl;
-  *log_out << "      Width:" << std::setw(11) << video_width << std::endl;
-  *log_out << "     Height:" << std::setw(11) << video_height << std::endl;
-  *log_out << "      Total:" << std::setw(11) << total.InMillisecondsF()
-           << " ms" << std::endl;
-  *log_out << "  Summation:" << std::setw(11) << sum
-           << " ms" << std::endl;
-  *log_out << "    Average:" << std::setw(11) << average
-           << " ms" << std::endl;
-  *log_out << "     StdDev:" << std::setw(11) << stddev
-           << " ms" << std::endl;
-  *log_out << "        FPS:" << std::setw(11) << fps
-           << std::endl;
-  if (hash_djb2) {
-    *log_out << "  DJB2 Hash:" << std::setw(11) << hash_value
-             << "  " << in_path.value() << std::endl;
-  }
-  if (hash_md5) {
-    base::MD5Digest digest;  // The result of the computation.
-    base::MD5Final(&digest, &ctx);
-    *log_out << "   MD5 Hash: " << base::MD5DigestToBase16(digest)
-             << " " << in_path.value() << std::endl;
-  }
-#if defined(ENABLE_WINDOWS_EXCEPTIONS)
-  } __except(EXCEPTION_EXECUTE_HANDLER) {
-    *log_out << "  Exception:" << std::setw(11) << GetExceptionCode()
-             << " " << in_path.value() << std::endl;
-    return 1;
-  }
-#endif
-  CommandLine::Reset();
-  return 0;
-}
diff --git a/media/tools/player_x11/player_x11.cc b/media/tools/player_x11/player_x11.cc
index cef8912..c154e69 100644
--- a/media/tools/player_x11/player_x11.cc
+++ b/media/tools/player_x11/player_x11.cc
@@ -95,8 +95,8 @@
 
 static void OnBufferingState(media::Pipeline::BufferingState buffering_state) {}
 
-static void NeedKey(const std::string& type, scoped_ptr<uint8[]> init_data,
-             int init_data_size) {
+static void NeedKey(const std::string& type,
+                    const std::vector<uint8>& init_data) {
   std::cout << "File is encrypted." << std::endl;
 }
 
diff --git a/media/tools/seek_tester/seek_tester.cc b/media/tools/seek_tester/seek_tester.cc
deleted file mode 100644
index d3f6a35..0000000
--- a/media/tools/seek_tester/seek_tester.cc
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// This standalone binary is a helper for diagnosing seek behavior of the
-// demuxer setup in media/ code.  It answers the question: "if I ask the demuxer
-// to Seek to X ms, where will it actually seek to? (necessitating
-// frame-dropping until the original seek target is reached)".  Sample run:
-//
-// $ ./out/Debug/seek_tester .../LayoutTests/media/content/test.ogv 6300
-// [0207/130327:INFO:seek_tester.cc(63)] Requested: 6123ms
-// [0207/130327:INFO:seek_tester.cc(68)]   audio seeked to: 5526ms
-// [0207/130327:INFO:seek_tester.cc(74)]   video seeked to: 5577ms
-
-
-#include "base/at_exit.h"
-#include "base/bind.h"
-#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/strings/string_number_conversions.h"
-#include "media/base/media.h"
-#include "media/base/media_log.h"
-#include "media/filters/ffmpeg_demuxer.h"
-#include "media/filters/file_data_source.h"
-
-class DemuxerHostImpl : public media::DemuxerHost {
- public:
-  // DataSourceHost implementation.
-  virtual void SetTotalBytes(int64 total_bytes) OVERRIDE {}
-  virtual void AddBufferedByteRange(int64 start, int64 end) OVERRIDE {}
-  virtual void AddBufferedTimeRange(base::TimeDelta start,
-                                    base::TimeDelta end) OVERRIDE {}
-
-  // DemuxerHost implementation.
-  virtual void SetDuration(base::TimeDelta duration) OVERRIDE {}
-  virtual void OnDemuxerError(media::PipelineStatus error) OVERRIDE {}
-};
-
-void QuitMessageLoop(base::MessageLoop* loop, media::PipelineStatus status) {
-  CHECK_EQ(status, media::PIPELINE_OK);
-  loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
-}
-
-void TimestampExtractor(uint64* timestamp_ms,
-                        base::MessageLoop* loop,
-                        media::DemuxerStream::Status status,
-                        const scoped_refptr<media::DecoderBuffer>& buffer) {
-  CHECK_EQ(status, media::DemuxerStream::kOk);
-  if (buffer->timestamp() == media::kNoTimestamp())
-    *timestamp_ms = -1;
-  else
-    *timestamp_ms = buffer->timestamp().InMillisecondsF();
-  loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
-}
-
-static void NeedKey(const std::string& type, scoped_ptr<uint8[]> init_data,
-             int init_data_size) {
-  LOG(INFO) << "File is encrypted.";
-}
-
-int main(int argc, char** argv) {
-  base::AtExitManager at_exit;
-  media::InitializeMediaLibraryForTesting();
-
-  CHECK_EQ(argc, 3) << "\nUsage: " << argv[0] << " <file> <seekTimeInMs>";
-  uint64 seek_target_ms;
-  CHECK(base::StringToUint64(argv[2], &seek_target_ms));
-  scoped_ptr<media::FileDataSource> file_data_source(
-      new media::FileDataSource());
-  CHECK(file_data_source->Initialize(base::FilePath::FromUTF8Unsafe(argv[1])));
-
-  DemuxerHostImpl host;
-  base::MessageLoop loop;
-  media::PipelineStatusCB quitter = base::Bind(&QuitMessageLoop, &loop);
-  media::FFmpegNeedKeyCB need_key_cb = base::Bind(&NeedKey);
-  scoped_ptr<media::FFmpegDemuxer> demuxer(
-      new media::FFmpegDemuxer(loop.message_loop_proxy(),
-                               file_data_source.get(),
-                               need_key_cb,
-                               new media::MediaLog()));
-  demuxer->Initialize(&host, quitter);
-  loop.Run();
-
-  demuxer->Seek(base::TimeDelta::FromMilliseconds(seek_target_ms), quitter);
-  loop.Run();
-
-  uint64 audio_seeked_to_ms;
-  uint64 video_seeked_to_ms;
-  media::DemuxerStream* audio_stream =
-      demuxer->GetStream(media::DemuxerStream::AUDIO);
-  media::DemuxerStream* video_stream =
-      demuxer->GetStream(media::DemuxerStream::VIDEO);
-  LOG(INFO) << "Requested: " << seek_target_ms << "ms";
-  if (audio_stream) {
-    audio_stream->Read(base::Bind(
-        &TimestampExtractor, &audio_seeked_to_ms, &loop));
-    loop.Run();
-    LOG(INFO) << "  audio seeked to: " << audio_seeked_to_ms << "ms";
-  }
-  if (video_stream) {
-    video_stream->Read(
-        base::Bind(&TimestampExtractor, &video_seeked_to_ms, &loop));
-    loop.Run();
-    LOG(INFO) << "  video seeked to: " << video_seeked_to_ms << "ms";
-  }
-
-  demuxer->Stop(base::Bind(&base::MessageLoop::Quit, base::Unretained(&loop)));
-  loop.Run();
-
-  return 0;
-}
diff --git a/media/tools/shader_bench/cpu_color_painter.cc b/media/tools/shader_bench/cpu_color_painter.cc
deleted file mode 100644
index a7cb570..0000000
--- a/media/tools/shader_bench/cpu_color_painter.cc
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/base/yuv_convert.h"
-#include "media/tools/shader_bench/cpu_color_painter.h"
-
-enum { kNumRGBPlanes = 1 };
-
-// Pass-through vertex shader.
-static const char kVertexShader[] =
-    "precision highp float;\n"
-    "precision highp int;\n"
-    "varying vec2 interp_tc;\n"
-    "\n"
-    "attribute vec4 in_pos;\n"
-    "attribute vec2 in_tc;\n"
-    "\n"
-    "void main() {\n"
-    "  interp_tc = in_tc;\n"
-    "  gl_Position = in_pos;\n"
-    "}\n";
-
-// RGB pixel shader.
-static const char kFragmentShader[] =
-    "precision mediump float;\n"
-    "precision mediump int;\n"
-    "varying vec2 interp_tc;\n"
-    "\n"
-    "uniform sampler2D rgba_tex;\n"
-    "\n"
-    "void main() {\n"
-    "  vec4 texColor = texture2D(rgba_tex, interp_tc);"
-    "  gl_FragColor = vec4(texColor.z, texColor.y, texColor.x, texColor.w);\n"
-    "}\n";
-
-CPUColorPainter::CPUColorPainter()
-    : program_id_(-1) {
-}
-
-CPUColorPainter::~CPUColorPainter() {
-  if (program_id_) {
-    glDeleteProgram(program_id_);
-    glDeleteTextures(kNumRGBPlanes, textures_);
-  }
-}
-
-void CPUColorPainter::Initialize(int width, int height) {
-  glGenTextures(kNumRGBPlanes, textures_);
-  glActiveTexture(GL_TEXTURE0);
-  glBindTexture(GL_TEXTURE_2D, textures_[0]);
-  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
-               GL_RGBA, GL_UNSIGNED_BYTE, 0);
-
-  GLuint program = CreateShaderProgram(kVertexShader, kFragmentShader);
-
-  // Bind parameters.
-  glUniform1i(glGetUniformLocation(program, "rgba_tex"), 0);
-  program_id_ = program;
-}
-
-void CPUColorPainter::Paint(scoped_refptr<media::VideoFrame> video_frame) {
-  // Convert to RGB32 frame.
-  scoped_refptr<media::VideoFrame> rgba_frame =
-      media::VideoFrame::CreateFrame(media::VideoFrame::RGB32,
-                                     video_frame->coded_size(),
-                                     video_frame->visible_rect(),
-                                     video_frame->natural_size(),
-                                     base::TimeDelta());
-
-  media::ConvertYUVToRGB32(video_frame->data(media::VideoFrame::kYPlane),
-                           video_frame->data(media::VideoFrame::kUPlane),
-                           video_frame->data(media::VideoFrame::kVPlane),
-                           rgba_frame->data(0),
-                           video_frame->coded_size().width(),
-                           video_frame->coded_size().height(),
-                           video_frame->stride(media::VideoFrame::kYPlane),
-                           video_frame->stride(media::VideoFrame::kUPlane),
-                           rgba_frame->stride(0),
-                           media::YV12);
-
-  glBindTexture(GL_TEXTURE_2D, textures_[0]);
-  // Not accounting for x/y offset presently.
-  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
-                  rgba_frame->visible_rect().width(),
-                  rgba_frame->visible_rect().height(),
-                  GL_RGBA, GL_UNSIGNED_BYTE,
-                  rgba_frame->data(0));
-
-  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-  surface()->SwapBuffers();
-}
diff --git a/media/tools/shader_bench/cpu_color_painter.h b/media/tools/shader_bench/cpu_color_painter.h
deleted file mode 100644
index 7aba3cd..0000000
--- a/media/tools/shader_bench/cpu_color_painter.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_TOOLS_SHADER_BENCH_CPU_COLOR_PAINTER_H_
-#define MEDIA_TOOLS_SHADER_BENCH_CPU_COLOR_PAINTER_H_
-
-#include "base/compiler_specific.h"
-#include "base/memory/ref_counted.h"
-#include "media/base/video_frame.h"
-#include "media/tools/shader_bench/gpu_painter.h"
-
-// Does color conversion using CPU, rendering on GPU.
-class CPUColorPainter : public GPUPainter {
- public:
-  CPUColorPainter();
-  virtual ~CPUColorPainter();
-
-  // Painter interface.
-  virtual void Initialize(int width, int height) OVERRIDE;
-  virtual void Paint(scoped_refptr<media::VideoFrame> video_frame) OVERRIDE;
-
- private:
-  // Shader program id.
-  GLuint program_id_;
-
-  // ID of rgba texture.
-  GLuint textures_[1];
-
-  DISALLOW_COPY_AND_ASSIGN(CPUColorPainter);
-};
-
-#endif  // MEDIA_TOOLS_SHADER_BENCH_CPU_COLOR_PAINTER_H_
diff --git a/media/tools/shader_bench/gpu_color_painter.cc b/media/tools/shader_bench/gpu_color_painter.cc
deleted file mode 100644
index 17155ee..0000000
--- a/media/tools/shader_bench/gpu_color_painter.cc
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/tools/shader_bench/gpu_color_painter.h"
-#include "ui/gl/gl_context.h"
-
-enum { kNumYUVPlanes = 3 };
-
-// Matrix used for the YUV to RGB conversion.
-static const float kYUV2RGB[9] = {
-  1.f, 0.f, 1.403f,
-  1.f, -.344f, -.714f,
-  1.f, 1.772f, 0.f,
-};
-
-static const float kYUV2RGB_TRANS[9] = {
-  1.f, 1.f, 1.f,
-  0.f, -.344f, 1.772f,
-  1.403f, -.714f, 0.f,
-};
-
-// Pass-through vertex shader.
-static const char kVertexShader[] =
-    "precision highp float;\n"
-    "precision highp int;\n"
-    "varying vec2 interp_tc;\n"
-    "\n"
-    "attribute vec4 in_pos;\n"
-    "attribute vec2 in_tc;\n"
-    "\n"
-    "void main() {\n"
-    "  interp_tc = in_tc;\n"
-    "  gl_Position = in_pos;\n"
-    "}\n";
-
-// YUV to RGB pixel shader. Loads a pixel from each plane and pass through the
-// matrix.
-static const char kFragmentShader[] =
-    "precision mediump float;\n"
-    "precision mediump int;\n"
-    "varying vec2 interp_tc;\n"
-    "\n"
-    "uniform sampler2D y_tex;\n"
-    "uniform sampler2D u_tex;\n"
-    "uniform sampler2D v_tex;\n"
-    "uniform mat3 yuv2rgb;\n"
-    "\n"
-    "void main() {\n"
-    "  float y = texture2D(y_tex, interp_tc).x;\n"
-    "  float u = texture2D(u_tex, interp_tc).r - .5;\n"
-    "  float v = texture2D(v_tex, interp_tc).r - .5;\n"
-    "  vec3 rgb = yuv2rgb * vec3(y, u, v);\n"
-    "  gl_FragColor = vec4(rgb, 1);\n"
-    "}\n";
-
-GPUColorWithLuminancePainter::GPUColorWithLuminancePainter()
-    : program_id_(-1) {
-}
-
-GPUColorWithLuminancePainter::~GPUColorWithLuminancePainter() {
-  if (program_id_) {
-    glDeleteProgram(program_id_);
-    glDeleteTextures(kNumYUVPlanes, textures_);
-  }
-}
-
-void GPUColorWithLuminancePainter::Initialize(int width, int height) {
-  // Create 3 textures, one for each plane, and bind them to different
-  // texture units.
-  glGenTextures(kNumYUVPlanes, textures_);
-
-  for (unsigned int i = 0; i < kNumYUVPlanes; ++i) {
-    unsigned int texture_width = (i == media::VideoFrame::kYPlane) ?
-        width : width / 2;
-    unsigned int texture_height = (i == media::VideoFrame::kYPlane) ?
-        height : height / 2;
-    glActiveTexture(GL_TEXTURE0 + i);
-    glBindTexture(GL_TEXTURE_2D, textures_[i]);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, texture_width, texture_height,
-                 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0);
-  }
-
-  GLuint program = CreateShaderProgram(kVertexShader, kFragmentShader);
-
-  // Bind parameters.
-  glUniform1i(glGetUniformLocation(program, "y_tex"), 0);
-  glUniform1i(glGetUniformLocation(program, "u_tex"), 1);
-  glUniform1i(glGetUniformLocation(program, "v_tex"), 2);
-  int yuv2rgb_location = glGetUniformLocation(program, "yuv2rgb");
-
-  // DesktopGL supports transpose matrices.
-  if (gfx::GetGLImplementation() == gfx::kGLImplementationDesktopGL)
-    glUniformMatrix3fv(yuv2rgb_location, 1, GL_TRUE, kYUV2RGB);
-  else
-    glUniformMatrix3fv(yuv2rgb_location, 1, GL_FALSE, kYUV2RGB_TRANS);
-
-  program_id_ = program;
-}
-
-void GPUColorWithLuminancePainter::Paint(
-    scoped_refptr<media::VideoFrame> video_frame) {
-  // Not accounting for x/y offset presently.
-  int width = video_frame->visible_rect().width();
-  int height = video_frame->visible_rect().height();
-  for (unsigned int i = 0; i < kNumYUVPlanes; ++i) {
-    unsigned int plane_width =
-        (i == media::VideoFrame::kYPlane) ? width : width / 2;
-    unsigned int plane_height =
-        (i == media::VideoFrame::kYPlane) ? height : height / 2;
-    glBindTexture(GL_TEXTURE_2D, textures_[i]);
-    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, plane_width, plane_height,
-                    GL_LUMINANCE, GL_UNSIGNED_BYTE, video_frame->data(i));
-  }
-
-  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-  surface()->SwapBuffers();
-}
diff --git a/media/tools/shader_bench/gpu_color_painter.h b/media/tools/shader_bench/gpu_color_painter.h
deleted file mode 100644
index 63c6f52..0000000
--- a/media/tools/shader_bench/gpu_color_painter.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_TOOLS_SHADER_BENCH_GPU_COLOR_PAINTER_H_
-#define MEDIA_TOOLS_SHADER_BENCH_GPU_COLOR_PAINTER_H_
-
-#include "base/compiler_specific.h"
-#include "base/memory/ref_counted.h"
-#include "media/base/video_frame.h"
-#include "media/tools/shader_bench/gpu_painter.h"
-#include "ui/gl/gl_context.h"
-
-// Does color space conversion using luminance textures on GPU,
-// renders using GPU.
-class GPUColorWithLuminancePainter : public GPUPainter {
- public:
-  GPUColorWithLuminancePainter();
-  virtual ~GPUColorWithLuminancePainter();
-
-  // Painter interface.
-  virtual void Initialize(int width, int height) OVERRIDE;
-  virtual void Paint(scoped_refptr<media::VideoFrame> video_frame) OVERRIDE;
-
- private:
-  // Shader program id.
-  GLuint program_id_;
-
-  // IDs of 3 luminance textures.
-  GLuint textures_[3];
-
-  DISALLOW_COPY_AND_ASSIGN(GPUColorWithLuminancePainter);
-};
-
-#endif  // MEDIA_TOOLS_SHADER_BENCH_GPU_COLOR_PAINTER_H_
diff --git a/media/tools/shader_bench/gpu_painter.cc b/media/tools/shader_bench/gpu_painter.cc
deleted file mode 100644
index e635011..0000000
--- a/media/tools/shader_bench/gpu_painter.cc
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright (c) 2011 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/logging.h"
-#include "media/tools/shader_bench/gpu_painter.h"
-
-// Vertices for a full screen quad.
-static const float kVertices[8] = {
-  -1.f, 1.f,
-  -1.f, -1.f,
-  1.f, 1.f,
-  1.f, -1.f,
-};
-
-// Texture Coordinates mapping the entire texture.
-static const float kTextureCoords[8] = {
-  0, 0,
-  0, 1,
-  1, 0,
-  1, 1,
-};
-
-// Buffer size for compile errors.
-static const unsigned int kErrorSize = 4096;
-
-GPUPainter::GPUPainter()
-    : surface_(NULL),
-      context_(NULL) {
-}
-
-GPUPainter::~GPUPainter() {
-}
-
-void GPUPainter::SetGLContext(gfx::GLSurface* surface,
-                              gfx::GLContext* context) {
-  surface_ = surface;
-  context_ = context;
-}
-
-GLuint GPUPainter::LoadShader(unsigned type, const char* shader_source) {
-  GLuint shader = glCreateShader(type);
-  glShaderSource(shader, 1, &shader_source, NULL);
-  glCompileShader(shader);
-  int result = GL_FALSE;
-  glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
-  if (!result) {
-    char log[kErrorSize];
-    int len;
-    glGetShaderInfoLog(shader, kErrorSize - 1, &len, log);
-    log[kErrorSize - 1] = 0;
-    LOG(FATAL) << "Shader did not compile: " << log;
-  }
-  return shader;
-}
-
-GLuint GPUPainter::CreateShaderProgram(const char* vertex_shader_source,
-                                       const char* fragment_shader_source) {
-
-  // Create vertex and pixel shaders.
-  GLuint vertex_shader = LoadShader(GL_VERTEX_SHADER, vertex_shader_source);
-  GLuint fragment_shader =
-      LoadShader(GL_FRAGMENT_SHADER, fragment_shader_source);
-
-  // Create program and attach shaders.
-  GLuint program = glCreateProgram();
-  glAttachShader(program, vertex_shader);
-  glAttachShader(program, fragment_shader);
-  glDeleteShader(vertex_shader);
-  glDeleteShader(fragment_shader);
-  glLinkProgram(program);
-  int result = GL_FALSE;
-  glGetProgramiv(program, GL_LINK_STATUS, &result);
-  if (!result) {
-    char log[kErrorSize];
-    int len;
-    glGetProgramInfoLog(program, kErrorSize - 1, &len, log);
-    log[kErrorSize - 1] = 0;
-    LOG(FATAL) << "Program did not link: " << log;
-  }
-  glUseProgram(program);
-
-  // Set common vertex parameters.
-  int pos_location = glGetAttribLocation(program, "in_pos");
-  glEnableVertexAttribArray(pos_location);
-  glVertexAttribPointer(pos_location, 2, GL_FLOAT, GL_FALSE, 0, kVertices);
-
-  int tc_location = glGetAttribLocation(program, "in_tc");
-  glEnableVertexAttribArray(tc_location);
-  glVertexAttribPointer(tc_location, 2, GL_FLOAT, GL_FALSE, 0, kTextureCoords);
-  return program;
-}
diff --git a/media/tools/shader_bench/gpu_painter.h b/media/tools/shader_bench/gpu_painter.h
deleted file mode 100644
index e68305b..0000000
--- a/media/tools/shader_bench/gpu_painter.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_TOOLS_SHADER_BENCH_GPU_PAINTER_H_
-#define MEDIA_TOOLS_SHADER_BENCH_GPU_PAINTER_H_
-
-#include "media/tools/shader_bench/painter.h"
-#include "ui/gl/gl_bindings.h"
-#include "ui/gl/gl_context.h"
-#include "ui/gl/gl_implementation.h"
-#include "ui/gl/gl_surface.h"
-
-// Class that renders video frames to a window via GPU.
-class GPUPainter : public Painter {
- public:
-  GPUPainter();
-  virtual ~GPUPainter();
-
-  // Returns a reference to the GL context.
-  gfx::GLSurface* surface() const { return surface_; }
-
-  // Sets context for subsequent gl calls in this painter.
-  virtual void SetGLContext(gfx::GLSurface* surface, gfx::GLContext* context);
-
-  // Creates shader program into given context, from the vertex and fragment
-  // shader source code. Returns the id of the shader program.
-  virtual GLuint CreateShaderProgram(const char* vertex_shader_source,
-                                     const char* fragment_shader_source);
-
- private:
-  // Loads shader into given context, from the source code of the
-  // shader. type refers to the shader type, either GL_VERTEX_SHADER or
-  // GL_FRAGMENT_SHADER. Returns id of shader.
-  GLuint LoadShader(unsigned type, const char* shader_source);
-
-  // Reference to the gl context.
-  gfx::GLSurface* surface_;
-  gfx::GLContext* context_;
-
-  DISALLOW_COPY_AND_ASSIGN(GPUPainter);
-};
-
-#endif  // MEDIA_TOOLS_SHADER_BENCH_GPU_PAINTER_H_
diff --git a/media/tools/shader_bench/painter.cc b/media/tools/shader_bench/painter.cc
deleted file mode 100644
index ab8fc59..0000000
--- a/media/tools/shader_bench/painter.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) 2010 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 "media/tools/shader_bench/painter.h"
-
-Painter::Painter()
-    : frames_(NULL) {
-}
-
-Painter::~Painter() {
-}
-
-void Painter::OnPaint() {
-  if (frames_ && !frames_->empty()) {
-    scoped_refptr<media::VideoFrame> cur_frame = frames_->front();
-    Paint(cur_frame);
-    frames_->pop_front();
-    frames_->push_back(cur_frame);
-  }
-}
-
-void Painter::LoadFrames(
-    std::deque<scoped_refptr<media::VideoFrame> >* frames) {
-  frames_ = frames;
-}
diff --git a/media/tools/shader_bench/painter.h b/media/tools/shader_bench/painter.h
deleted file mode 100644
index 2dd92ab..0000000
--- a/media/tools/shader_bench/painter.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_TOOLS_SHADER_BENCH_PAINTER_H_
-#define MEDIA_TOOLS_SHADER_BENCH_PAINTER_H_
-
-#include <deque>
-
-#include "base/memory/ref_counted.h"
-#include "media/base/video_frame.h"
-
-// Class that paints video frames to a window.
-class Painter {
- public:
-  Painter();
-  virtual ~Painter();
-
-  // Loads frames into Painter. Painter does not take ownership of frames.
-  virtual void LoadFrames(
-      std::deque<scoped_refptr<media::VideoFrame> >* frames);
-
-  // Called window is ready to be painted.
-  virtual void OnPaint();
-
-  // Initialize a Painter class with a width and a height
-  virtual void Initialize(int width, int height) = 0;
-
-  // Paint a single frame to a window.
-  virtual void Paint(scoped_refptr<media::VideoFrame> video_frame) = 0;
-
- private:
-  // Frames that the Painter will paint.
-  std::deque<scoped_refptr<media::VideoFrame> >* frames_;
-
-  DISALLOW_COPY_AND_ASSIGN(Painter);
-};
-
-#endif  // MEDIA_TOOLS_SHADER_BENCH_PAINTER_H_
diff --git a/media/tools/shader_bench/shader_bench.cc b/media/tools/shader_bench/shader_bench.cc
deleted file mode 100644
index 0b4236f..0000000
--- a/media/tools/shader_bench/shader_bench.cc
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <deque>
-#include <ostream>
-
-#include "base/at_exit.h"
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/time/time.h"
-#include "media/base/media.h"
-#include "media/base/video_frame.h"
-#include "media/tools/shader_bench/cpu_color_painter.h"
-#include "media/tools/shader_bench/gpu_color_painter.h"
-#include "media/tools/shader_bench/painter.h"
-#include "media/tools/shader_bench/window.h"
-#include "ui/gfx/native_widget_types.h"
-#include "ui/gl/gl_bindings.h"
-#include "ui/gl/gl_context.h"
-#include "ui/gl/gl_implementation.h"
-#include "ui/gl/gl_surface.h"
-
-#if defined(TOOLKIT_GTK)
-#include <gtk/gtk.h>
-#endif
-
-static const int kNumFramesToPaint = 500;
-static base::TimeTicks g_start_;
-static base::TimeTicks g_end_;
-
-long CalculateYUVFrameSize(FILE* file_handle, int num_frames) {
-  fseek(file_handle, 0, SEEK_END);
-  long file_size = (long) ftell(file_handle);
-  rewind(file_handle);
-  return file_size / num_frames;
-}
-
-void GetFrames(std::string file_name,
-               int width, int height, int num_frames,
-               std::deque<scoped_refptr<media::VideoFrame> >& out_frames) {
-  FILE* file_handle = fopen(file_name.c_str(), "rb");
-  if (!file_handle) {
-    printf("Could not open %s\n", file_name.c_str());
-    exit(1);
-  }
-
-  long frame_size = CalculateYUVFrameSize(file_handle, num_frames);
-
-  gfx::Size size(width, height);
-  for (int i = 0; i < num_frames; i++) {
-    scoped_refptr<media::VideoFrame> video_frame =
-        media::VideoFrame::CreateFrame(media::VideoFrame::YV12, size,
-                                       gfx::Rect(size), size,
-                                       base::TimeDelta());
-    long bytes_read =
-        fread(video_frame->data(0), 1, frame_size, file_handle);
-
-    if (bytes_read != frame_size) {
-      printf("Could not read %s\n", file_name.c_str());
-      fclose(file_handle);
-      exit(1);
-    }
-    out_frames.push_back(video_frame);
-  }
-
-  fclose(file_handle);
-}
-
-void TestFinished() {
-  g_end_ = base::TimeTicks::HighResNow();
-  double time_in_seconds =
-      static_cast<double>((g_end_ - g_start_).InMilliseconds()) / 1000;
-  double fps = kNumFramesToPaint / time_in_seconds;
-  printf("Printed %f frames per second.\n", fps);
-}
-
-void RunTest(media::Window* window, Painter* painter) {
-  g_start_ = base::TimeTicks::HighResNow();
-  window->Start(kNumFramesToPaint, base::Bind(&TestFinished), painter);
-}
-
-int main(int argc, char** argv) {
-  // Read arguments.
-  if (argc == 1) {
-    printf("Usage: %s --file=FILE --wxh=DIMENSIONS --frames=NUM_FRAMES\n"
-           "FILE is a raw .yuv file with 1+ frames in it\n"
-           "DIMENSIONS is the width and height of the frame in pixels\n"
-           "NUM_FRAMES is the number of frames in FILE\n", argv[0]);
-    return 1;
-  }
-
-  // Read command line.
-#if defined(TOOLKIT_GTK)
-  gtk_init(&argc, &argv);
-#endif
-  CommandLine::Init(argc, argv);
-
-  // Determine file name.
-  std::string file_name =
-      CommandLine::ForCurrentProcess()->GetSwitchValueASCII("file");
-
-  // Determine number of frames.
-  int num_frames = 0;
-  std::string str_num_frames =
-      CommandLine::ForCurrentProcess()->GetSwitchValueASCII("frames");
-  base::StringToInt(str_num_frames, &num_frames);
-
-  // Determine video dimensions.
-  int width = 0;
-  int height = 0;
-  std::string dimensions =
-      CommandLine::ForCurrentProcess()->GetSwitchValueASCII("wxh");
-  int x_index = dimensions.find('x');
-  std::string str_width = dimensions.substr(0, x_index);
-  std::string str_height =
-      dimensions.substr(x_index + 1, dimensions.length() - x_index - 1);
-  base::StringToInt(str_width, &width);
-  base::StringToInt(str_height, &height);
-
-  // Process files.
-  std::deque<scoped_refptr<media::VideoFrame> > frames;
-  GetFrames(file_name, width, height, num_frames, frames);
-
-  // Initialize window and graphics context.
-  base::AtExitManager at_exit_manager;
-  media::InitializeMediaLibraryForTesting();
-  gfx::GLSurface::InitializeOneOff();
-  scoped_ptr<media::Window> window(new media::Window(width, height));
-  scoped_refptr<gfx::GLSurface> surface =
-      gfx::GLSurface::CreateViewGLSurface(window->PluginWindow());
-  scoped_refptr<gfx::GLContext> context = gfx::GLContext::CreateGLContext(
-      NULL, surface.get(), gfx::PreferDiscreteGpu);
-  context->MakeCurrent(surface.get());
-  // This sets D3DPRESENT_INTERVAL_IMMEDIATE on Windows.
-  context->SetSwapInterval(0);
-
-  // Initialize and name GPU painters.
-  const struct {
-    const char* name;
-    GPUPainter* painter;
-  } painters[] = {
-    { "CPU CSC + GPU Render", new CPUColorPainter() },
-    { "GPU CSC/Render", new GPUColorWithLuminancePainter() },
-  };
-
-  // Run GPU painter tests.
-  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(painters); i++) {
-    scoped_ptr<GPUPainter> painter(painters[i].painter);
-    painter->LoadFrames(&frames);
-    painter->SetGLContext(surface, context);
-    painter->Initialize(width, height);
-    printf("Running %s tests...", painters[i].name);
-    RunTest(window.get(), painter.get());
-  }
-
-  return 0;
-}
diff --git a/media/tools/shader_bench/window.cc b/media/tools/shader_bench/window.cc
deleted file mode 100644
index 3eb26f4..0000000
--- a/media/tools/shader_bench/window.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) 2011 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 "media/tools/shader_bench/window.h"
-
-namespace media {
-
-Window::Window(int width, int height)
-    : painter_(NULL),
-      limit_(0),
-      count_(0),
-      running_(false) {
-  window_handle_ = CreateNativeWindow(width, height);
-}
-
-Window::~Window() {}
-
-}  // namespace media
diff --git a/media/tools/shader_bench/window.h b/media/tools/shader_bench/window.h
deleted file mode 100644
index d66e849..0000000
--- a/media/tools/shader_bench/window.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (c) 2011 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 MEDIA_TOOLS_SHADER_BENCH_WINDOW_H_
-#define MEDIA_TOOLS_SHADER_BENCH_WINDOW_H_
-
-#include "base/callback.h"
-#include "ui/gfx/native_widget_types.h"
-
-class Painter;
-
-namespace media {
-
-class Window {
- public:
-  Window(int width, int height);
-  ~Window();
-
-  // Creates and returns a handle to a native window of the given dimensions.
-  gfx::NativeWindow CreateNativeWindow(int width, int height);
-
-  // Returns the NPAPI plugin window handle of the window.
-  gfx::PluginWindowHandle PluginWindow();
-
-  // Kicks off frame painting with the given limit, painter, and
-  // callback to run when painting task is complete.
-  void Start(int limit, const base::Closure& callback, Painter* painter);
-
-  // Called when window is expected to paint self.
-  void OnPaint();
-
-  // Main loop for window.
-  void MainLoop();
-
- private:
-  // Closure to run when frame painting is completed. Will be reset after
-  // running.
-  base::Closure callback_;
-
-  // Reference to painter Window uses to paint frames.
-  Painter* painter_;
-
-  // Number of frames to paint before closing the window.
-  int limit_;
-
-  // Number of frames currently painted.
-  int count_;
-
-  // True if the window is painting video frames to the screen, false otherwise.
-  bool running_;
-
-  // This window's native handle.
-  gfx::NativeWindow window_handle_;
-
-  DISALLOW_COPY_AND_ASSIGN(Window);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_TOOLS_SHADER_BENCH_WINDOW_H_
diff --git a/media/tools/shader_bench/window_linux.cc b/media/tools/shader_bench/window_linux.cc
deleted file mode 100644
index a0a3493..0000000
--- a/media/tools/shader_bench/window_linux.cc
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (c) 2011 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 "media/tools/shader_bench/window.h"
-
-#include "media/tools/shader_bench/painter.h"
-
-#include <gdk/gdkx.h>
-#include <gtk/gtk.h>
-
-namespace media {
-
-static gboolean OnDelete(GtkWidget* widget, GdkEventExpose* event) {
-  gtk_main_quit();
-  return FALSE;
-}
-
-static gboolean OnExpose(GtkWidget* widget,
-                         GdkEventExpose* event,
-                         gpointer data) {
-  Window* window = reinterpret_cast<Window*>(data);
-  if (window)
-    window->OnPaint();
-  return FALSE;
-}
-
-gfx::NativeWindow Window::CreateNativeWindow(int width, int height) {
-  GtkWidget* hwnd = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-
-  gtk_window_set_default_size(GTK_WINDOW(hwnd), width, height);
-  gtk_widget_set_double_buffered(hwnd, FALSE);
-  gtk_widget_set_app_paintable(hwnd, TRUE);
-  gtk_widget_show(hwnd);
-
-  return GTK_WINDOW(hwnd);
-}
-
-gfx::PluginWindowHandle Window::PluginWindow() {
-  return GDK_WINDOW_XWINDOW(GTK_WIDGET(window_handle_)->window);
-}
-
-void Window::Start(int limit, const base::Closure& callback,
-                   Painter* painter) {
-  running_ = true;
-  count_ = 0;
-  limit_ = limit;
-  callback_ = callback;
-  painter_ = painter;
-
-  gtk_signal_connect(GTK_OBJECT(window_handle_),
-                     "delete_event",
-                     reinterpret_cast<GtkSignalFunc>(OnDelete),
-                     NULL);
-
-  gtk_signal_connect(GTK_OBJECT(window_handle_),
-                     "expose_event",
-                     reinterpret_cast<GtkSignalFunc>(OnExpose),
-                     this);
-
-  gtk_widget_queue_draw(GTK_WIDGET(window_handle_));
-  MainLoop();
-}
-
-void Window::OnPaint() {
-  if (!running_)
-    return;
-
-  if (count_ < limit_) {
-    painter_->OnPaint();
-    count_++;
-    gtk_widget_queue_draw(GTK_WIDGET(window_handle_));
-  } else {
-    running_ = false;
-    if (!callback_.is_null()) {
-      callback_.Run();
-      callback_.Reset();
-    }
-    gtk_main_quit();
-  }
-}
-
-void Window::MainLoop() {
-  gtk_main();
-}
-
-}  // namespace media
diff --git a/media/tools/shader_bench/window_win.cc b/media/tools/shader_bench/window_win.cc
deleted file mode 100644
index abc6fc4..0000000
--- a/media/tools/shader_bench/window_win.cc
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright (c) 2011 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 "media/tools/shader_bench/window.h"
-
-#include "media/tools/shader_bench/painter.h"
-
-namespace media {
-
-static LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg,
-                                   WPARAM w_param, LPARAM l_param) {
-  LRESULT result = 0;
-  switch (msg) {
-    case WM_CLOSE:
-      ::DestroyWindow(hwnd);
-      break;
-    case WM_DESTROY:
-      ::PostQuitMessage(0);
-      break;
-    case WM_ERASEBKGND:
-      // Return a non-zero value to indicate that the background has been
-      // erased.
-      result = 1;
-      break;
-    case WM_PAINT: {
-      Window* window =
-          reinterpret_cast<Window*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
-      if (window != NULL)
-        window->OnPaint();
-      ::ValidateRect(hwnd, NULL);
-      break;
-    }
-    default:
-      result = ::DefWindowProc(hwnd, msg, w_param, l_param);
-      break;
-  }
-  return result;
-}
-
-gfx::NativeWindow Window::CreateNativeWindow(int width, int height) {
-  WNDCLASS wnd_class = {0};
-  HINSTANCE instance = GetModuleHandle(NULL);
-  wnd_class.style = CS_OWNDC;
-  wnd_class.lpfnWndProc = WindowProc;
-  wnd_class.hInstance = instance;
-  wnd_class.hbrBackground =
-      reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
-  wnd_class.lpszClassName = L"gpu_demo";
-  if (!RegisterClass(&wnd_class))
-    return NULL;
-
-  DWORD wnd_style = WS_OVERLAPPED | WS_SYSMENU;
-  RECT wnd_rect;
-  wnd_rect.left = 0;
-  wnd_rect.top = 0;
-  wnd_rect.right = width;
-  wnd_rect.bottom = height;
-  AdjustWindowRect(&wnd_rect, wnd_style, FALSE);
-
-  HWND hwnd = CreateWindow(
-      wnd_class.lpszClassName,
-      L"",
-      wnd_style,
-      0,
-      0,
-      wnd_rect.right - wnd_rect.left,
-      wnd_rect.bottom - wnd_rect.top,
-      NULL,
-      NULL,
-      instance,
-      NULL);
-  if (hwnd == NULL)
-    return NULL;
-
-  return hwnd;
-}
-
-gfx::PluginWindowHandle Window::PluginWindow() {
-  return window_handle_;
-}
-
-void Window::Start(int limit, const base::Closure& callback,
-                   Painter* painter) {
-  running_ = true;
-  count_ = 0;
-  limit_ = limit;
-  callback_ = callback;
-  painter_ = painter;
-
-  SetWindowLongPtr(window_handle_, GWLP_USERDATA,
-                   reinterpret_cast<LONG_PTR>(this));
-
-  ShowWindow(window_handle_, SW_SHOWNORMAL);
-
-  // Post first invalidate call to kick off painting.
-  ::InvalidateRect(window_handle_, NULL, FALSE);
-
-  MainLoop();
-}
-
-void Window::OnPaint() {
-  if (!running_)
-    return;
-
-  if (count_ < limit_) {
-    painter_->OnPaint();
-    count_++;
-  } else {
-    running_ = false;
-    if (!callback_.is_null()) {
-      ShowWindow(window_handle_, SW_HIDE);
-      callback_.Run();
-      callback_.Reset();
-    }
-  }
-}
-
-void Window::MainLoop() {
-  MSG msg;
-  bool done = false;
-  while (!done) {
-    while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
-      if (msg.message == WM_QUIT || !running_)
-        done = true;
-      ::TranslateMessage(&msg);
-      ::DispatchMessage(&msg);
-      if (!done)
-        ::InvalidateRect(window_handle_, NULL, FALSE);
-    }
-  }
-}
-
-}  // namespace media
diff --git a/media/video/capture/fake_video_capture_device.cc b/media/video/capture/fake_video_capture_device.cc
index 665f728..ae7c07b 100644
--- a/media/video/capture/fake_video_capture_device.cc
+++ b/media/video/capture/fake_video_capture_device.cc
@@ -57,7 +57,8 @@
       observer_(NULL),
       state_(kIdle),
       capture_thread_("CaptureThread"),
-      frame_count_(0) {
+      frame_count_(0),
+      capabilities_roster_index_(0) {
 }
 
 FakeVideoCaptureDevice::~FakeVideoCaptureDevice() {
@@ -91,8 +92,9 @@
     capture_format_.frame_rate = 30;
   }
 
-  size_t fake_frame_size =
-      capture_format_.width * capture_format_.height * 3 / 2;
+  const size_t fake_frame_size = VideoFrame::AllocationSize(
+      VideoFrame::I420,
+      gfx::Size(capture_format_.width, capture_format_.height));
   fake_frame_.reset(new uint8[fake_frame_size]);
 
   state_ = kAllocated;
@@ -107,8 +109,9 @@
   DVLOG(3) << "Reallocating FakeVideoCaptureDevice, new capture resolution ("
            << capture_format_.width << "x" << capture_format_.height << ")";
 
-  size_t fake_frame_size =
-      capture_format_.width * capture_format_.height * 3 / 2;
+  const size_t fake_frame_size = VideoFrame::AllocationSize(
+      VideoFrame::I420,
+      gfx::Size(capture_format_.width, capture_format_.height));
   fake_frame_.reset(new uint8[fake_frame_size]);
 
   observer_->OnFrameInfoChanged(capture_format_);
@@ -151,7 +154,9 @@
     return;
   }
 
-  int frame_size = capture_format_.width * capture_format_.height * 3 / 2;
+  const size_t frame_size = VideoFrame::AllocationSize(
+      VideoFrame::I420,
+      gfx::Size(capture_format_.width, capture_format_.height));
   memset(fake_frame_.get(), 0, frame_size);
 
   SkBitmap bitmap;
diff --git a/media/webm/webm_stream_parser.cc b/media/webm/webm_stream_parser.cc
index 796a1b3..12be449 100644
--- a/media/webm/webm_stream_parser.cc
+++ b/media/webm/webm_stream_parser.cc
@@ -319,11 +319,8 @@
 }
 
 void WebMStreamParser::FireNeedKey(const std::string& key_id) {
-  int key_id_size = key_id.size();
-  DCHECK_GT(key_id_size, 0);
-  scoped_ptr<uint8[]> key_id_array(new uint8[key_id_size]);
-  memcpy(key_id_array.get(), key_id.data(), key_id_size);
-  need_key_cb_.Run(kWebMEncryptInitDataType, key_id_array.Pass(), key_id_size);
+  std::vector<uint8> key_id_vector(key_id.begin(), key_id.end());
+  need_key_cb_.Run(kWebMEncryptInitDataType, key_id_vector);
 }
 
 }  // namespace media
diff --git a/native_client_sdk/src/build_tools/build_projects.py b/native_client_sdk/src/build_tools/build_projects.py
index 20f3f7c..953b56f 100755
--- a/native_client_sdk/src/build_tools/build_projects.py
+++ b/native_client_sdk/src/build_tools/build_projects.py
@@ -204,7 +204,7 @@
     BuildProjectsBranch(pepperdir, branch, deps, clean, config)
 
 
-def main(args):
+def main(argv):
   parser = optparse.OptionParser()
   parser.add_option('-c', '--clobber',
       help='Clobber project directories before copying new files',
@@ -227,9 +227,10 @@
       action='append')
   parser.add_option('-v', '--verbose', action='store_true')
 
-  options, args = parser.parse_args(args[1:])
-  if args:
-    parser.error('Not expecting any arguments.')
+  options, args = parser.parse_args(argv[1:])
+  if options.project:
+    parser.error('The -p/--project option is deprecated.\n'
+                 'Just use positional paramaters instead.')
 
   if 'NACL_SDK_ROOT' in os.environ:
     # We don't want the currently configured NACL_SDK_ROOT to have any effect
@@ -258,9 +259,9 @@
   if options.dest:
     filters['DEST'] = options.dest
     print 'Filter by type: ' + str(options.dest)
-  if options.project:
-    filters['NAME'] = options.project
-    print 'Filter by name: ' + str(options.project)
+  if args:
+    filters['NAME'] = args
+    print 'Filter by name: ' + str(args)
 
   try:
     project_tree = parse_dsc.LoadProjectTree(SDK_SRC_DIR, include=filters)
diff --git a/native_client_sdk/src/build_tools/test_projects.py b/native_client_sdk/src/build_tools/test_projects.py
index 04fe73d..64e3143 100755
--- a/native_client_sdk/src/build_tools/test_projects.py
+++ b/native_client_sdk/src/build_tools/test_projects.py
@@ -59,6 +59,9 @@
     # TODO(binji): Disable 3D examples on linux/win. See
     # http://crbug.com/262379.
     {'name': 'graphics_3d', 'platform': ('win', 'linux')},
+    # TODO(binji): These tests timeout on the trybots because the NEXEs take
+    # more than 40 seconds to load (!). See http://crbug.com/280753
+    {'name': 'nacl_io_test', 'platform': 'win', 'toolchain': 'glibc'},
 ]
 
 def ValidateToolchains(toolchains):
@@ -85,6 +88,9 @@
 
 
 def GetBrowserTesterCommand(desc, toolchain, config):
+  if browser_path is None:
+    buildbot_common.ErrorExit('Failed to find chrome browser using FindChrome.')
+
   args = [
     sys.executable,
     browser_tester_py,
@@ -110,6 +116,9 @@
       ppapi_plugin += '.so'
     args.extend(['--ppapi_plugin', ppapi_plugin])
 
+    ppapi_plugin_mimetype = 'application/x-ppapi-%s' % config.lower()
+    args.extend(['--ppapi_plugin_mimetype', ppapi_plugin_mimetype])
+
   if toolchain == 'pnacl':
     args.extend(['--browser_flag', '--enable-pnacl'])
 
@@ -294,8 +303,9 @@
           type='int', default=1)
 
   options, args = parser.parse_args(args[1:])
-  if args:
-    parser.error('Not expecting any arguments.')
+  if options.project:
+    parser.error('The -p/--project option is deprecated.\n'
+                 'Just use positional paramaters instead.')
 
   if not options.toolchain:
     options.toolchain = ['newlib', 'glibc', 'pnacl', 'host']
@@ -316,9 +326,9 @@
   if options.dest:
     include['DEST'] = options.dest
     print 'Filter by type: ' + str(options.dest)
-  if options.project:
-    include['NAME'] = options.project
-    print 'Filter by name: ' + str(options.project)
+  if args:
+    include['NAME'] = args
+    print 'Filter by name: ' + str(args)
   if not options.config:
     options.config = ALL_CONFIGS
 
diff --git a/native_client_sdk/src/build_tools/tests/update_nacl_manifest_test.py b/native_client_sdk/src/build_tools/tests/update_nacl_manifest_test.py
index 7adb8f7..de292d1 100755
--- a/native_client_sdk/src/build_tools/tests/update_nacl_manifest_test.py
+++ b/native_client_sdk/src/build_tools/tests/update_nacl_manifest_test.py
@@ -31,6 +31,7 @@
 OS_M = ('mac',)
 OS_ML = ('mac', 'linux')
 OS_MW = ('mac', 'win')
+OS_LW = ('linux', 'win')
 OS_MLW = ('mac', 'linux', 'win')
 OS_ALL = ('all',)
 POST_STABLE = 'post_stable'
@@ -238,6 +239,7 @@
 V21_0_1145_0 = '21.0.1145.0'
 V21_0_1166_0 = '21.0.1166.0'
 V26_0_1386_0 = '26.0.1386.0'
+V26_0_1386_1 = '26.0.1386.1'
 VTRUNK_140819 = 'trunk.140819'
 B18_0_1025_163_MLW = MakePlatformBundle(18, 132135, V18_0_1025_163, OS_MLW)
 B18_0_1025_184_MLW = MakePlatformBundle(18, 134900, V18_0_1025_184, OS_MLW)
@@ -250,6 +252,7 @@
 B21_0_1166_0_MW = MakePlatformBundle(21, 140819, V21_0_1166_0, OS_MW)
 B26_NONE = MakePlatformBundle(26)
 B26_0_1386_0_MLW = MakePlatformBundle(26, 177362, V26_0_1386_0, OS_MLW)
+B26_0_1386_1_MLW = MakePlatformBundle(26, 177439, V26_0_1386_1, OS_MLW)
 BTRUNK_140819_MLW = MakePlatformBundle(21, 140819, VTRUNK_140819, OS_MLW)
 NON_PEPPER_BUNDLE_NOARCHIVES = MakeNonPepperBundle('foo')
 NON_PEPPER_BUNDLE_ARCHIVES = MakeNonPepperBundle('bar', with_archives=True)
@@ -703,6 +706,45 @@
     self.assertRaises(update_nacl_manifest.UnknownLockedBundleException,
                       self._Run, OS_MLW)
 
+  def testIgnoreLastDigitOnCanary(self):
+    # The final number in a canary build does not include any different
+    # changes, it is just a different experiment (e.g. using ASAN, or using
+    # aura). We should not compare these versions differently.
+    #
+    # Note that the version mapping will show that 31.0.1608.0 is different
+    # from 31.0.1608.1 -- this is because 31.0.1608.1 is built from the branch,
+    # not from trunk. Inspecting the branch would show that there are no
+    # changes (why would there be? No one has any reason to merge changes to a
+    # canary branch.)
+    self.manifest = MakeManifest(copy.deepcopy(BCANARY_NONE))
+    history = """win,canary,31.0.1608.1,2013-08-22 09:33:24.469760
+mac,canary,31.0.1608.0,2013-08-22 07:18:09.762600"""
+    self._AddCsvHistory(history)
+    self.version_mapping['31.0.1608.1'] = 'trunk.218914'
+    self.version_mapping['31.0.1608.0'] = 'trunk.218872'
+    my_bundle = MakePlatformBundle(31, 218872, '31.0.1608.0', OS_MLW)
+    self.files.Add(my_bundle)
+    self._MakeDelegate()
+    self._Run(OS_MLW)
+    self._ReadUploadedManifest()
+    self._AssertUploadedManifestHasBundle(my_bundle, CANARY)
+
+  def testDontIgnoreLastDigitForNonCanary(self):
+    self.manifest = MakeManifest(B26_NONE)
+    self.history.Add(OS_M, BETA, V26_0_1386_1)  # Only Mac
+    self.history.Add(OS_LW, BETA, V26_0_1386_0)  # Only Linux, Windows.
+    self.files.Add(B26_0_1386_0_MLW)
+
+    self._MakeDelegate()
+    # This raises because pepper_26 is not found in the history, and therefore
+    # "locked", but it also doesn't have an online version, therefore there is
+    # no good version number to upload.
+    #
+    # Basically we're asserting that 26.0.1386.1 != 26.0.1386.0, which would be
+    # true if it were canary.
+    self.assertRaises(update_nacl_manifest.UnknownLockedBundleException,
+                      self._Run, OS_MLW)
+
 
 class TestUpdateVitals(unittest.TestCase):
   def setUp(self):
diff --git a/native_client_sdk/src/build_tools/update_nacl_manifest.py b/native_client_sdk/src/build_tools/update_nacl_manifest.py
index f0db23a..07430f4 100755
--- a/native_client_sdk/src/build_tools/update_nacl_manifest.py
+++ b/native_client_sdk/src/build_tools/update_nacl_manifest.py
@@ -368,11 +368,17 @@
       A tuple (version, channel, archives). The version is a string such as
       "19.0.1084.41". The channel is always 'canary'. |archives| is a list of
       archive URLs."""
+    # Canary versions that differ in the last digit shouldn't be considered
+    # different; this number is typically used to represent an experiment, e.g.
+    # using ASAN or aura.
+    def CanaryKey(version):
+      return version[:-1]
+
     # We don't ship canary on Linux, so it won't appear in self.history.
     # Instead, we can use the matching Linux trunk build for that version.
     shared_version_generator = self._FindNextSharedVersion(
         set(self.platforms) - set(('linux',)),
-        self._GetPlatformCanaryHistory)
+        self._GetPlatformCanaryHistory, CanaryKey)
     return self._DoGetMostRecentSharedVersion(shared_version_generator,
                                               allow_trunk_revisions=True)
 
@@ -499,7 +505,7 @@
         yield channel, version
 
 
-  def _FindNextSharedVersion(self, platforms, generator_func):
+  def _FindNextSharedVersion(self, platforms, generator_func, key_func=None):
     """Yields versions of Chrome that exist on all given platforms, in order of
        newest to oldest.
 
@@ -511,11 +517,17 @@
           ('mac', 'linux', 'win')
       generator_func: A function which takes a platform and returns a
           generator that yields (channel, version) tuples.
+      key_func: A function to convert the version into a value that should be
+          used for comparison. See python built-in sorted() or min(), for
+          an example.
     Returns:
       A generator that yields a tuple (version, channel) for each version that
       matches all platforms and the major version. The version returned is a
       string (e.g. "18.0.1025.164").
     """
+    if not key_func:
+      key_func = lambda x: x
+
     platform_generators = []
     for platform in platforms:
       platform_generators.append(generator_func(platform))
@@ -533,13 +545,19 @@
               platform, JoinVersion(platform_versions[i][1])))
         logger.info('Checking versions: %s' % ', '.join(msg_info))
 
-      shared_version = min(v for c, v in platform_versions)
+      shared_version = min((v for c, v in platform_versions), key=key_func)
 
-      if all(v == shared_version for c, v in platform_versions):
+      if all(key_func(v) == key_func(shared_version)
+             for c, v in platform_versions):
+        # The real shared_version should be the real minimum version. This will
+        # be different from shared_version above only if key_func compares two
+        # versions with different values as equal.
+        min_version = min((v for c, v in platform_versions))
+
         # grab the channel from an arbitrary platform
         first_platform = platform_versions[0]
         channel = first_platform[0]
-        yield JoinVersion(shared_version), channel
+        yield JoinVersion(min_version), channel
 
         # force increment to next version for all platforms
         shared_version = None
diff --git a/native_client_sdk/src/doc/OWNERS b/native_client_sdk/src/doc/OWNERS
new file mode 100644
index 0000000..dce9369
--- /dev/null
+++ b/native_client_sdk/src/doc/OWNERS
@@ -0,0 +1,2 @@
+sehr@chromium.org
+eliben@chromium.org
diff --git a/native_client_sdk/src/examples/api/core/example.js b/native_client_sdk/src/examples/api/core/example.js
index 99875eb..e126026 100644
--- a/native_client_sdk/src/examples/api/core/example.js
+++ b/native_client_sdk/src/examples/api/core/example.js
@@ -9,60 +9,63 @@
 var itrRecv = new Float64Array(itrMax);
 var delay = 0;
 
+function getTimeInMilliseconds() {
+  return (new Date()).getTime();
+}
+
 function attachListeners() {
-  document.getElementById('start').addEventListener('click',
-      startTest);
-  count_pp = document.getElementById('count')
-  count_pp.textContent = itrMax;
+  document.getElementById('start').addEventListener('click', startTest);
+  countEl = document.getElementById('count');
+  countEl.textContent = itrMax;
 }
 
 function startTest() {
   if (common.naclModule) {
-    var startButton = document.getElementById('start');
-    startButton.disabled = true;
+    var startEl = document.getElementById('start');
+    startEl.disabled = true;
 
-    var delayControl = document.getElementById('delay');
-    delay = parseInt(delayControl.value, 10);
+    var delayEl = document.getElementById('delay');
+    delay = parseInt(delayEl.value, 10);
 
     common.updateStatus('Running Test');
     itrCount = 0;
-    itrSend[0] = (new Date()).getTime();
+    itrSend[0] = getTimeInMilliseconds();
     common.naclModule.postMessage(delay);
   }
 }
 
 function setStats(nacl, compute, total) {
-  var statNaCl = document.getElementById('NaCl');
-  var statRound = document.getElementById('Round');
-  var statAll = document.getElementById('Total');
+  var statNaClEl = document.getElementById('NaCl');
+  var statRoundEl = document.getElementById('Round');
+  var statTotalEl = document.getElementById('Total');
 
-  statNaCl.textContent = (nacl / itrMax) + ' ms';
-  statRound.textContent = (compute / itrMax) + ' ms';
-  statAll.textContent = (total / itrMax) + ' ms';
+  statNaClEl.textContent = (nacl / itrMax) + ' ms';
+  statRoundEl.textContent = (compute / itrMax) + ' ms';
+  statTotalEl.textContent = (total / itrMax) + ' ms';
 }
 
 // Called by the common.js module.
 function handleMessage(message_event) {
   // Convert NaCl Seconds elapsed to MS
   itrNaCl[itrCount] = message_event.data * 1000.0;
-  itrRecv[itrCount] = (new Date()).getTime();
+  itrRecv[itrCount] = getTimeInMilliseconds();
   itrCount++;
 
-  if (itrCount == itrMax) {
+  if (itrCount === itrMax) {
     common.updateStatus('Test Finished');
-    var startButton = document.getElementById('start');
-    startButton.disabled = false;
+    var startEl = document.getElementById('start');
+    startEl.disabled = false;
 
     var naclMS = 0.0;
     var computeMS = 0.0;
-    for (var i=0; i < itrMax; i++) {
+    for (var i = 0; i < itrMax; i++) {
       naclMS += itrNaCl[i];
       computeMS += itrRecv[i] - itrSend[i];
     }
 
     setStats(naclMS, computeMS, itrRecv[itrMax - 1] - itrSend[0]);
   } else {
-    itrSend[itrCount] = (new Date()).getTime();
+    itrSend[itrCount] = getTimeInMilliseconds();
     common.naclModule.postMessage(delay);
   }
 }
diff --git a/native_client_sdk/src/examples/api/core/test.js b/native_client_sdk/src/examples/api/core/test.js
new file mode 100644
index 0000000..27f58ce
--- /dev/null
+++ b/native_client_sdk/src/examples/api/core/test.js
@@ -0,0 +1,51 @@
+// Copyright (c) 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.
+
+function addTests() {
+  function getNaClTimeMs() {
+    return parseFloat(document.getElementById('NaCl').textContent);
+  }
+
+  function getRoundTimeMs() {
+    return parseFloat(document.getElementById('Round').textContent);
+  }
+
+  function getTotalTimeMs() {
+    return parseFloat(document.getElementById('Total').textContent);
+  }
+
+  function delayTest(test, delayMs) {
+    test.log('Setting delay to ' + delayMs + 'ms');
+    document.getElementById('delay').value = delayMs;
+
+    test.log('Clicking start.');
+    var startEl = document.getElementById('start');
+    startEl.dispatchEvent(new CustomEvent('click'));
+
+    test.log('Waiting 1 second for test to finish.');
+    var intervalId = window.setInterval(function() {
+      if (itrCount !== itrMax) {
+        test.log('Not finished, waiting another second.');
+        return;
+      }
+
+      window.clearInterval(intervalId);
+      test.log('NaCl time: ' + getNaClTimeMs().toFixed(2) + 'ms');
+      test.log('Roundtrip time: ' + getRoundTimeMs().toFixed(2) + 'ms');
+      test.log('Total time: ' + getTotalTimeMs().toFixed(2) + 'ms');
+      test.log('Finished.');
+      test.pass();
+    }, 1000);
+  }
+
+  common.tester.addAsyncTest('delay_0', function(test) {
+    var delayMs = 0;
+    delayTest(test, delayMs);
+  });
+
+  common.tester.addAsyncTest('delay_3', function(test) {
+    var delayMs = 3;
+    delayTest(test, delayMs);
+  });
+}
diff --git a/native_client_sdk/src/examples/api/file_io/file_io.cc b/native_client_sdk/src/examples/api/file_io/file_io.cc
index cf8be26..2b2745d 100644
--- a/native_client_sdk/src/examples/api/file_io/file_io.cc
+++ b/native_client_sdk/src/examples/api/file_io/file_io.cc
@@ -198,7 +198,7 @@
       ShowErrorMessage("File fail to flush", flush_result);
       return;
     }
-    ShowStatusMessage("Save successful");
+    ShowStatusMessage("Save success");
   }
 
   void Load(int32_t /* result */, const std::string& file_name) {
@@ -212,7 +212,7 @@
     int32_t open_result =
         file.Open(ref, PP_FILEOPENFLAG_READ, pp::BlockUntilComplete());
     if (open_result == PP_ERROR_FILENOTFOUND) {
-      ShowStatusMessage("File not found");
+      ShowErrorMessage("File not found", open_result);
       return;
     } else if (open_result != PP_OK) {
       ShowErrorMessage("File open for read failed", open_result);
@@ -233,24 +233,25 @@
     std::vector<char> data(info.size);
     int64_t offset = 0;
     int32_t bytes_read = 0;
-    do {
+    int32_t bytes_to_read = info.size;
+    while (bytes_to_read > 0) {
       bytes_read = file.Read(offset,
                              &data[offset],
                              data.size() - offset,
                              pp::BlockUntilComplete());
-      if (bytes_read > 0)
+      if (bytes_read > 0) {
         offset += bytes_read;
-    } while (bytes_read > 0);
-    // If bytes_read < PP_OK then it indicates the error code.
-    if (bytes_read < PP_OK) {
-      ShowErrorMessage("File read failed", bytes_read);
-      return;
+        bytes_to_read -= bytes_read;
+      } else if (bytes_read < 0) {
+        // If bytes_read < PP_OK then it indicates the error code.
+        ShowErrorMessage("File read failed", bytes_read);
+        return;
+      }
     }
-    PP_DCHECK(bytes_read == 0);
     // Done reading, send content to the user interface
     std::string string_data(data.begin(), data.end());
     PostMessage("DISP|" + string_data);
-    ShowStatusMessage("Load complete");
+    ShowStatusMessage("Load success");
   }
 
   void Delete(int32_t /* result */, const std::string& file_name) {
@@ -268,7 +269,7 @@
       ShowErrorMessage("Deletion failed", result);
       return;
     }
-    ShowStatusMessage("File/Directory deleted");
+    ShowStatusMessage("Delete success");
   }
 
   void List(int32_t /* result */, const std::string& dir_name) {
@@ -301,6 +302,7 @@
       }
     }
     PostMessage(ss.str());
+    ShowStatusMessage("List success");
   }
 
   void MakeDir(int32_t /* result */, const std::string& dir_name) {
@@ -315,7 +317,7 @@
       ShowErrorMessage("Make directory failed", result);
       return;
     }
-    ShowStatusMessage("Made directory");
+    ShowStatusMessage("Make directory success");
   }
 
   /// Encapsulates our simple javascript communication protocol
diff --git a/native_client_sdk/src/examples/api/file_io/test.js b/native_client_sdk/src/examples/api/file_io/test.js
new file mode 100644
index 0000000..bcf2b0e
--- /dev/null
+++ b/native_client_sdk/src/examples/api/file_io/test.js
@@ -0,0 +1,253 @@
+// Copyright (c) 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.
+
+function addTests() {
+  var currentTest = null;
+
+  function dispatchClick(element) {
+    element.dispatchEvent(new MouseEvent('click'));
+  }
+
+  function getVisibleElementByTagName(tag) {
+    var selector = '.function:not([hidden]) ' + tag;
+    return document.querySelector(selector);
+  }
+
+  function clickRadio(name) {
+    currentTest.log('Clicking ' + name + ' radio button.');
+    dispatchClick(document.getElementById('radio_' + name));
+  }
+
+  function setInputValue(value) {
+    currentTest.log('Setting input box to "' + value + '".');
+    getVisibleElementByTagName('input').value = value;
+  }
+
+  function setTextareaValue(value) {
+    currentTest.log('Setting textarea to "' + value + '".');
+    getVisibleElementByTagName('textarea').value = value;
+  }
+
+  function getTextareaValue() {
+    return getVisibleElementByTagName('textarea').value;
+  }
+
+  function getLastLogMessage() {
+    var logEl = document.getElementById('log');
+    var logLines = logEl.textContent.split('\n');
+    return logLines[logLines.length - 1];
+  }
+
+  function waitForLog(logMessage, onLogChanged, onError) {
+    // First see if the message we want is already there.
+    if (getLastLogMessage() === logMessage) {
+      onLogChanged();
+      return;
+    }
+
+    // Clear the log. This prevents a previous failure from propagating to the
+    // current check. (NOTE: the log is backed by an array, so as soon as a
+    // message is logged it will be refilled with its previous data in addition
+    // to the new message.)
+    document.getElementById('log').textContent = '';
+
+    // Poll for log changes.
+    var intervalId = window.setInterval(function() {
+      var lastLogMessage = getLastLogMessage();
+
+      if (lastLogMessage.lastIndexOf('Error:', 0) === 0) {
+        window.clearInterval(intervalId);
+        if (onError) {
+          currentTest.log('Got error message, continuing.');
+          onError();
+        } else {
+          currentTest.fail('Unexpected failure waiting for log change.');
+        }
+
+        return;
+      }
+
+      if (logMessage !== lastLogMessage)
+        return;
+
+      currentTest.log('Got log message, continuing.');
+      window.clearInterval(intervalId);
+      onLogChanged();
+    }, 100);
+  }
+
+  function clickExecuteButtonAndWaitForLog(logMessage, onLogChanged, onError) {
+    waitForLog(logMessage, onLogChanged, onError);
+    currentTest.log('Clicking button.');
+    dispatchClick(getVisibleElementByTagName('button'));
+    currentTest.log('Waiting for log message: "' + logMessage + '".');
+  }
+
+  function isFilenameInDirectoryList(filename) {
+    var listItemEls = document.querySelectorAll('#listDirOutput li');
+
+    currentTest.log('Looking for ' + filename);
+    for (var i = 0; i < listItemEls.length; ++i) {
+      var itemText = listItemEls[i].textContent;
+      currentTest.log('Found ' + itemText);
+      if (itemText === filename) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  function saveFile(filename, fileText, onFileSaved, onError) {
+    clickRadio('saveFile');
+    setInputValue(filename);
+    setTextareaValue(fileText);
+    clickExecuteButtonAndWaitForLog('Save success', onFileSaved, onError);
+  }
+
+  function loadFile(filename, onFileLoaded, onError) {
+    clickRadio('loadFile');
+    setInputValue(filename);
+    setTextareaValue('');  // Clear the textarea.
+    clickExecuteButtonAndWaitForLog('Load success', onFileLoaded, onError);
+  }
+
+  function deleteFile(filename, onFileDeleted, onError) {
+    clickRadio('delete');
+    setInputValue(filename);
+    clickExecuteButtonAndWaitForLog('Delete success', onFileDeleted, onError);
+  }
+
+  function listDir(dirname, onDirectoryListed, onError) {
+    clickRadio('listDir');
+    setInputValue(dirname);
+    clickExecuteButtonAndWaitForLog('List success', onDirectoryListed, onError);
+  }
+
+  function makeDir(dirname, onDirectoryMade, onError) {
+    clickRadio('makeDir');
+    setInputValue(dirname);
+    clickExecuteButtonAndWaitForLog('Make directory success',
+                                    onDirectoryMade, onError);
+  }
+
+  function expectEq(expected, actual, additionalInfo) {
+    var message;
+    if (expected !== actual) {
+      if (additionalInfo)
+        message = additionalInfo + ': ';
+      message += 'Expected "' + expected + '", got "' + actual + '".';
+      currentTest.fail(message);
+    } else {
+      message = 'OK, "' + expected + '" === "' + actual + '".';
+      currentTest.log(message);
+    }
+  }
+
+  function expectContains(needle, haystack, additionalInfo) {
+    if (haystack.indexOf(needle) === -1) {
+      if (additionalInfo)
+        message = additionalInfo + ': ';
+      message += 'Expected to find "' + needle + '" in "' + haystack + '".';
+      currentTest.fail(message);
+    } else {
+      message = 'OK, "' + needle + '" in "' + haystack + '".';
+      currentTest.log(message);
+    }
+  }
+
+  function expectFilenameInDirectoryList(filename, additionalInfo) {
+    if (!isFilenameInDirectoryList(filename)) {
+      if (additionalInfo)
+        message = additionalInfo + ': ';
+      message += 'Expected to find "' + filename + '" in directory list.';
+      currentTest.fail(message);
+    } else {
+      message = 'OK, found "' + filename + ' in the directory list.';
+      currentTest.log(message);
+    }
+  }
+
+  common.tester.addAsyncTest('filesystem_ready', function(test) {
+    // This is a bit fragile; we rely on this test being run first (and
+    // completing) before we can run any of the other tests.
+    currentTest = test;
+    waitForLog('Filesystem ready!', function() {
+      test.pass();
+    }, function() {
+      test.fail('Got unexpected error waiting for filesystem: ');
+    });
+  });
+
+  common.tester.addAsyncTest('save_and_load', function(test) {
+    currentTest = test;
+    var filename = '/save_and_load.txt';
+    var fileText = 'A penny saved is a penny earned.';
+
+    // Save the file.
+    saveFile(filename, fileText, function() {
+      // Now try to load it.
+      loadFile(filename, function() {
+        // Make sure the text matches.
+        expectEq(fileText, getTextareaValue(), 'Incorrect textarea');
+        test.pass();
+      });
+    });
+  });
+
+  common.tester.addAsyncTest('delete_file', function(test) {
+    currentTest = test;
+    var filename = '/delete_file.txt';
+
+    saveFile(filename, 'Here today, gone tomorrow.', function() {
+      deleteFile(filename, function() {
+        loadFile(filename, function() {
+          test.fail('Unexpected load success.');
+        },
+        function() {
+          expectEq('', getTextareaValue(), 'Unexpected data in file');
+          expectContains('File not found', getLastLogMessage(),
+                         'Unexpected log message');
+          test.pass();
+        });
+      });
+    });
+  });
+
+  common.tester.addAsyncTest('list_directory', function(test) {
+    currentTest = test;
+    var filename = '/list_directory.txt';
+
+    saveFile(filename, 'I\'ve got a little list...', function() {
+      listDir('/', function() {
+        // Directory listings are relative, so it will not have the leading
+        // slash.
+        var relativeFilename = filename.slice(1);
+        expectFilenameInDirectoryList(relativeFilename);
+        test.pass();
+      });
+    });
+  });
+
+  common.tester.addAsyncTest('make_directory', function(test) {
+    currentTest = test;
+    var dirname = '/new_directory';
+
+    makeDir(dirname, function() {
+      listDir('/', function() {
+        // Directory listings are relative, so it will not have the leading
+        // slash.
+        var relativeDirname = dirname.slice(1);
+        expectFilenameInDirectoryList(relativeDirname);
+
+        // Let's see if the file can be written to this directory.
+        var filename = dirname + '/file.txt';
+        var fileText = 'A file within a directory.';
+        saveFile(filename, fileText, function() {
+          test.pass();
+        });
+      });
+    });
+  });
+}
diff --git a/native_client_sdk/src/examples/common.js b/native_client_sdk/src/examples/common.js
index 79687d5..dc5328f 100644
--- a/native_client_sdk/src/examples/common.js
+++ b/native_client_sdk/src/examples/common.js
@@ -84,11 +84,9 @@
   /**
    * Run all tests for this example.
    *
-   * @param {bool} waitForModule True if the tests should wait for the module
-   *     to load. This is not necessary for trusted plugins (i.e. host plugins).
    * @param {Object} moduleEl The module DOM element.
    */
-  function runTests(waitForModule, moduleEl) {
+  function runTests(moduleEl) {
     console.log('runTests()');
     common.tester = new Tester();
 
@@ -107,9 +105,7 @@
       window.addTests();
     }
 
-    if (waitForModule) {
-      common.tester.waitFor(moduleEl);
-    }
+    common.tester.waitFor(moduleEl);
     common.tester.run();
   }
 
@@ -155,9 +151,11 @@
     var isHost = isHostToolchain(tool);
     if (isHost) {
       window.setTimeout(function() {
-        var evt = document.createEvent('Event');
-        evt.initEvent('load', true, true);  // bubbles, cancelable
-        moduleEl.dispatchEvent(evt);
+        moduleEl.readyState = 1;
+        moduleEl.dispatchEvent(new CustomEvent('loadstart'));
+        moduleEl.readyState = 4;
+        moduleEl.dispatchEvent(new CustomEvent('load'));
+        moduleEl.dispatchEvent(new CustomEvent('loadend'));
       }, 100);  // 100 ms
     }
 
@@ -165,8 +163,7 @@
     if (isTest) {
       var loadNaClTest = function() {
         injectScript('nacltest.js', function() {
-          var waitForModule = !isHost;
-          runTests(waitForModule, moduleEl);
+          runTests(moduleEl);
         });
       };
 
diff --git a/native_client_sdk/src/gonacl_appengine/static/pnacl-demo/example.js b/native_client_sdk/src/gonacl_appengine/static/pnacl-demo/example.js
index bcf3780..fac8405 100644
--- a/native_client_sdk/src/gonacl_appengine/static/pnacl-demo/example.js
+++ b/native_client_sdk/src/gonacl_appengine/static/pnacl-demo/example.js
@@ -186,8 +186,10 @@
                           'value': value});
 }
 
-// Add event listeners after the NaCl module has loaded.  These listeners will
-// forward messages to the NaCl module via postMessage()
+/**
+ * Add event listeners after the NaCl module has loaded.  These listeners will
+ * forward messages to the NaCl module via postMessage()
+ */
 function attachListeners() {
   $('threadCount').addEventListener('change', setThreadCount);
   $('zoomRange').addEventListener('change',
@@ -204,7 +206,10 @@
     });
 }
 
-// Load a texture and send pixel data down to NaCl module.
+/**
+ * Load a texture and send pixel data down to NaCl module.
+ * @param {string} name
+ */
 function loadTexture(name) {
   // Load image from jpg, decompress into canvas.
   var img = new Image();
@@ -229,7 +234,10 @@
   img.src = getDataURL(name);
 }
 
-// Handle a message coming from the NaCl module.
+/**
+ * Handle a message coming from the NaCl module.
+ * @param {Object} message_event
+ */
 function handleMessage(message_event) {
   if (message_event.data['message'] == 'set_zoom') {
     // zoom slider
@@ -247,8 +255,10 @@
   }
 }
 
-// Listen for the DOM content to be loaded. This event is fired when parsing of
-// the page's document has finished.
+/**
+ * Listen for the DOM content to be loaded. This event is fired when parsing of
+ * the page's document has finished.
+ */
 document.addEventListener('DOMContentLoaded', function() {
   updateStatus('Loading...');
   if (!browserSupportsPNaCl()) {
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc
index e2e2f61..5f195f1 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc
+++ b/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc
@@ -7,20 +7,27 @@
 #include <errno.h>
 #include <string.h>
 
+#include <algorithm>
+
 #include "nacl_io/osstat.h"
 #include "sdk_util/auto_lock.h"
 
 namespace nacl_io {
 
-#define BLOCK_SIZE (1 << 16)
-#define BLOCK_MASK (BLOCK_SIZE - 1)
+namespace {
+
+// The maximum size to reserve in addition to the requested size. Resize() will
+// allocate twice as much as requested, up to this value.
+const size_t kMaxResizeIncrement = 16 * 1024 * 1024;
+
+}  // namespace
 
 MountNodeMem::MountNodeMem(Mount* mount)
-    : MountNode(mount), data_(NULL), capacity_(0) {
+    : MountNode(mount) {
   stat_.st_mode |= S_IFREG;
 }
 
-MountNodeMem::~MountNodeMem() { free(data_); }
+MountNodeMem::~MountNodeMem() {}
 
 Error MountNodeMem::Read(size_t offs, void* buf, size_t count, int* out_bytes) {
   *out_bytes = 0;
@@ -51,10 +58,7 @@
     return 0;
 
   if (count + offs > stat_.st_size) {
-    Error error = FTruncate(count + offs);
-    if (error)
-      return error;
-
+    Resize(count + offs);
     count = stat_.st_size - offs;
   }
 
@@ -64,36 +68,24 @@
 }
 
 Error MountNodeMem::FTruncate(off_t new_size) {
-  size_t need = (new_size + BLOCK_MASK) & ~BLOCK_MASK;
-  size_t old_size = stat_.st_size;
+  AUTO_LOCK(node_lock_);
+  Resize(new_size);
+  return 0;
+}
 
-  // If the current capacity is correct, just adjust and return
-  if (need == capacity_) {
-    stat_.st_size = static_cast<off_t>(new_size);
-    return 0;
+void MountNodeMem::Resize(off_t new_size) {
+  if (new_size > data_.capacity()) {
+    // While the node size is small, grow exponentially. When it starts to get
+    // larger, grow linearly.
+    size_t extra = std::min<size_t>(new_size, kMaxResizeIncrement);
+    data_.reserve(new_size + extra);
+  } else if (new_size < stat_.st_size) {
+    // Shrink to fit. std::vector usually doesn't reduce allocation size, so
+    // use the swap trick.
+    std::vector<char>(data_).swap(data_);
   }
-
-  // Attempt to realloc the block
-  char* newdata = static_cast<char*>(realloc(data_, need));
-  if (newdata != NULL) {
-    // Zero out new space.
-    if (new_size > old_size)
-      memset(newdata + old_size, 0, need - old_size);
-
-    data_ = newdata;
-    capacity_ = need;
-    stat_.st_size = static_cast<off_t>(new_size);
-    return 0;
-  }
-
-  // If we failed, then adjust size according to what we keep
-  if (new_size > capacity_)
-    new_size = capacity_;
-
-  // Update the size and return the new size
-  stat_.st_size = static_cast<off_t>(new_size);
-  return EIO;
+  data_.resize(new_size);
+  stat_.st_size = new_size;
 }
 
 }  // namespace nacl_io
-
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_mem.h b/native_client_sdk/src/libraries/nacl_io/mount_node_mem.h
index d645cd6..6f0bc1f 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount_node_mem.h
+++ b/native_client_sdk/src/libraries/nacl_io/mount_node_mem.h
@@ -7,6 +7,8 @@
 
 #include "nacl_io/mount_node.h"
 
+#include <vector>
+
 namespace nacl_io {
 
 class MountNodeMem : public MountNode {
@@ -26,8 +28,9 @@
   virtual Error FTruncate(off_t size);
 
  private:
-  char* data_;
-  size_t capacity_;
+  void Resize(off_t size);
+
+  std::vector<char> data_;
   friend class MountMem;
 };
 
diff --git a/native_client_sdk/src/tests/nacl_io_test/mount_node_test.cc b/native_client_sdk/src/tests/nacl_io_test/mount_node_test.cc
index 962c3d0..74c1295 100644
--- a/native_client_sdk/src/tests/nacl_io_test/mount_node_test.cc
+++ b/native_client_sdk/src/tests/nacl_io_test/mount_node_test.cc
@@ -98,6 +98,50 @@
   EXPECT_EQ(NULL_NODE, result_node.get());
 }
 
+TEST(MountNodeTest, FTruncate) {
+  MockMemory file;
+  size_t result_size = 0;
+  int result_bytes = 0;
+
+  char data[1024];
+  char buffer[1024];
+  char zero[1024];
+
+  for (size_t a = 0; a < sizeof(data); a++)
+    data[a] = a;
+  memset(buffer, 0, sizeof(buffer));
+  memset(zero, 0, sizeof(zero));
+
+  // Write the data to the file.
+  ASSERT_EQ(0, file.Write(0, data, sizeof(data), &result_bytes));
+  ASSERT_EQ(sizeof(data), result_bytes);
+
+  // Double the size of the file.
+  EXPECT_EQ(0, file.FTruncate(sizeof(data) * 2));
+  EXPECT_EQ(0, file.GetSize(&result_size));
+  EXPECT_EQ(sizeof(data) * 2, result_size);
+
+  // Read the first half of the file, it shouldn't have changed.
+  EXPECT_EQ(0, file.Read(0, buffer, sizeof(buffer), &result_bytes));
+  EXPECT_EQ(sizeof(buffer), result_bytes);
+  EXPECT_EQ(0, memcmp(buffer, data, sizeof(buffer)));
+
+  // Read the second half of the file, it should be all zeroes.
+  EXPECT_EQ(0, file.Read(sizeof(data), buffer, sizeof(buffer), &result_bytes));
+  EXPECT_EQ(sizeof(buffer), result_bytes);
+  EXPECT_EQ(0, memcmp(buffer, zero, sizeof(buffer)));
+
+  // Decrease the size of the file.
+  EXPECT_EQ(0, file.FTruncate(100));
+  EXPECT_EQ(0, file.GetSize(&result_size));
+  EXPECT_EQ(100, result_size);
+
+  // Data should still be there.
+  EXPECT_EQ(0, file.Read(0, buffer, sizeof(buffer), &result_bytes));
+  EXPECT_EQ(100, result_bytes);
+  EXPECT_EQ(0, memcmp(buffer, data, 100));
+}
+
 TEST(MountNodeTest, Directory) {
   s_AllocNum = 0;
   MockDir root;
diff --git a/net/base/winsock_util.cc b/net/base/winsock_util.cc
index 5e5c312..5522e27 100644
--- a/net/base/winsock_util.cc
+++ b/net/base/winsock_util.cc
@@ -31,8 +31,6 @@
 #pragma optimize( "", on )
 #pragma warning(pop)
 
-net::PlatformSocketFactory* g_socket_factory = NULL;
-
 }  // namespace
 
 void AssertEventNotSignaled(WSAEVENT hEvent) {
@@ -51,15 +49,4 @@
   return true;
 }
 
-void PlatformSocketFactory::SetInstance(PlatformSocketFactory* factory) {
-  g_socket_factory = factory;
-}
-
-SOCKET CreatePlatformSocket(int family, int type, int protocol) {
-  if (g_socket_factory)
-    return g_socket_factory->CreateSocket(family, type, protocol);
-  else
-    return ::WSASocket(family, type, protocol, NULL, 0, WSA_FLAG_OVERLAPPED);
-}
-
 }  // namespace net
diff --git a/net/base/winsock_util.h b/net/base/winsock_util.h
index 06ac448..36d670b 100644
--- a/net/base/winsock_util.h
+++ b/net/base/winsock_util.h
@@ -24,26 +24,6 @@
 // optimization.  The code still works if this function simply returns false.
 bool ResetEventIfSignaled(WSAEVENT hEvent);
 
-// Interface to create Windows Socket.
-// Usually such factories are used for testing purposes, which is not true in
-// this case. This interface is used to substitute WSASocket to make possible
-// execution of some network code in sandbox.
-class NET_EXPORT PlatformSocketFactory {
- public:
-  PlatformSocketFactory() {}
-  virtual ~PlatformSocketFactory() {}
-
-  // Creates Windows socket. See WSASocket documentation of parameters.
-  virtual SOCKET CreateSocket(int family, int type, int protocol) = 0;
-
-  // Replace WSASocket with given factory. The factory will be used by
-  // CreatePlatformSocket.
-  static void SetInstance(PlatformSocketFactory* factory);
-};
-
-// Creates Windows Socket. See WSASocket documentation of parameters.
-SOCKET CreatePlatformSocket(int family, int type, int protocol);
-
 }  // namespace net
 
 #endif  // NET_BASE_WINSOCK_UTIL_H_
diff --git a/net/cert/cert_verify_proc_unittest.cc b/net/cert/cert_verify_proc_unittest.cc
index 8890e3b..2f3afe0 100644
--- a/net/cert/cert_verify_proc_unittest.cc
+++ b/net/cert/cert_verify_proc_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <vector>
 
+#include "base/callback_helpers.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/sha1.h"
diff --git a/net/cert/cert_verify_proc_win.cc b/net/cert/cert_verify_proc_win.cc
index d3e8b62..b64797a 100644
--- a/net/cert/cert_verify_proc_win.cc
+++ b/net/cert/cert_verify_proc_win.cc
@@ -647,6 +647,7 @@
     chain_flags &= ~CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY;
     verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
 
+    CertFreeCertificateChain(chain_context);
     if (!CertGetCertificateChain(
              chain_engine,
              cert_list.get(),
diff --git a/net/cert/x509_certificate_mac.cc b/net/cert/x509_certificate_mac.cc
index 2f8ce43..96f7b52 100644
--- a/net/cert/x509_certificate_mac.cc
+++ b/net/cert/x509_certificate_mac.cc
@@ -283,9 +283,9 @@
       for (uint32 i = 0; i < results_->NumberOfResults; i++) {
         crypto::CSSMFree(encCert[i].CertBlob.Data);
       }
+      crypto::CSSMFree(results_->Results);
+      crypto::CSSMFree(results_);
     }
-    crypto::CSSMFree(results_->Results);
-    crypto::CSSMFree(results_);
   }
 
  private:
diff --git a/net/cookies/cookie_monster_perftest.cc b/net/cookies/cookie_monster_perftest.cc
index c70516f..8187bcb 100644
--- a/net/cookies/cookie_monster_perftest.cc
+++ b/net/cookies/cookie_monster_perftest.cc
@@ -6,9 +6,9 @@
 
 #include "base/bind.h"
 #include "base/message_loop/message_loop.h"
-#include "base/perftimer.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/perftimer.h"
 #include "net/cookies/canonical_cookie.h"
 #include "net/cookies/cookie_monster.h"
 #include "net/cookies/cookie_monster_store_test.h"
diff --git a/net/disk_cache/backend_unittest.cc b/net/disk_cache/backend_unittest.cc
index 7eeeee1..e2144d2 100644
--- a/net/disk_cache/backend_unittest.cc
+++ b/net/disk_cache/backend_unittest.cc
@@ -1509,6 +1509,7 @@
 
   AddDelay();
   Time middle_end = Time::Now();
+  AddDelay();
 
   ASSERT_EQ(net::OK, CreateEntry("fourth", &entry));
   entry->Close();
diff --git a/net/disk_cache/block_files.cc b/net/disk_cache/block_files.cc
index fc378e6..9246ef1 100644
--- a/net/disk_cache/block_files.cc
+++ b/net/disk_cache/block_files.cc
@@ -687,7 +687,7 @@
 
 base::FilePath BlockFiles::Name(int index) {
   // The file format allows for 256 files.
-  DCHECK(index < 256 || index >= 0);
+  DCHECK(index < 256 && index >= 0);
   std::string tmp = base::StringPrintf("%s%d", kBlockName, index);
   return path_.AppendASCII(tmp);
 }
diff --git a/net/disk_cache/disk_cache_perftest.cc b/net/disk_cache/disk_cache_perftest.cc
index f7a1b59..abf39b9 100644
--- a/net/disk_cache/disk_cache_perftest.cc
+++ b/net/disk_cache/disk_cache_perftest.cc
@@ -8,8 +8,8 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/hash.h"
-#include "base/perftimer.h"
 #include "base/strings/string_util.h"
+#include "base/test/perftimer.h"
 #include "base/test/test_file_util.h"
 #include "base/threading/thread.h"
 #include "base/timer/timer.h"
diff --git a/net/disk_cache/disk_cache_test_base.cc b/net/disk_cache/disk_cache_test_base.cc
index dc7bb6c..9eea949 100644
--- a/net/disk_cache/disk_cache_test_base.cc
+++ b/net/disk_cache/disk_cache_test_base.cc
@@ -7,6 +7,7 @@
 #include "base/file_util.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
+#include "base/threading/platform_thread.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
 #include "net/base/test_completion_callback.h"
@@ -221,6 +222,17 @@
 }
 
 void DiskCacheTestWithCache::AddDelay() {
+  if (simple_cache_mode_) {
+    // The simple cache uses second resolution for many timeouts, so it's safest
+    // to advance by at least whole seconds before falling back into the normal
+    // disk cache epsilon advance.
+    const base::Time initial_time = base::Time::Now();
+    do {
+      base::PlatformThread::YieldCurrentThread();
+    } while (base::Time::Now() -
+             initial_time < base::TimeDelta::FromSeconds(1));
+  }
+
   base::Time initial = base::Time::Now();
   while (base::Time::Now() <= initial) {
     base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
diff --git a/net/disk_cache/entry_unittest.cc b/net/disk_cache/entry_unittest.cc
index 7addc85..a7e6d9e 100644
--- a/net/disk_cache/entry_unittest.cc
+++ b/net/disk_cache/entry_unittest.cc
@@ -3079,6 +3079,7 @@
   EXPECT_EQ(kWriteSize,
             WriteData(entry, 0, 0, buffer.get(), kWriteSize, false));
   entry->Close();
+  AddDelay();
 
   std::string key2("the key prefix");
   for (int i = 0; i < kNumExtraEntries; i++) {
diff --git a/net/disk_cache/simple/simple_backend_impl.cc b/net/disk_cache/simple/simple_backend_impl.cc
index 4aed36c..c8ff759 100644
--- a/net/disk_cache/simple/simple_backend_impl.cc
+++ b/net/disk_cache/simple/simple_backend_impl.cc
@@ -403,9 +403,10 @@
                << path.LossyDisplayName();
     result.net_error = net::ERR_FAILED;
   } else {
-    bool mtime_result =
-        disk_cache::simple_util::GetMTime(path, &result.cache_dir_mtime);
-    DCHECK(mtime_result);
+    base::PlatformFileInfo file_info;
+    bool file_info_result = file_util::GetFileInfo(path, &file_info);
+    DCHECK(file_info_result);
+    result.cache_dir_mtime = file_info.last_modified;
     if (!result.max_size) {
       int64 available = base::SysInfo::AmountOfFreeDiskSpace(path);
       if (available < 0)
diff --git a/net/disk_cache/simple/simple_index.cc b/net/disk_cache/simple/simple_index.cc
index e17ada7..97b3be4 100644
--- a/net/disk_cache/simple/simple_index.cc
+++ b/net/disk_cache/simple/simple_index.cc
@@ -4,6 +4,7 @@
 
 #include "net/disk_cache/simple/simple_index.h"
 
+#include <limits>
 #include <utility>
 
 #include "base/bind.h"
@@ -72,31 +73,63 @@
 
 namespace disk_cache {
 
-EntryMetadata::EntryMetadata() : last_used_time_(0), entry_size_(0) {}
+EntryMetadata::EntryMetadata()
+  : last_used_time_seconds_since_epoch_(0),
+    entry_size_(0) {
+}
 
-EntryMetadata::EntryMetadata(base::Time last_used_time, uint64 entry_size)
-    : last_used_time_(last_used_time.ToInternalValue()),
-      entry_size_(entry_size) {}
+EntryMetadata::EntryMetadata(base::Time last_used_time, int entry_size)
+    : last_used_time_seconds_since_epoch_(0),
+      entry_size_(entry_size) {
+  SetLastUsedTime(last_used_time);
+}
 
 base::Time EntryMetadata::GetLastUsedTime() const {
-  return base::Time::FromInternalValue(last_used_time_);
+  // Preserve nullity.
+  if (last_used_time_seconds_since_epoch_ == 0)
+    return base::Time();
+
+  return base::Time::UnixEpoch() +
+      base::TimeDelta::FromSeconds(last_used_time_seconds_since_epoch_);
 }
 
 void EntryMetadata::SetLastUsedTime(const base::Time& last_used_time) {
-  last_used_time_ = last_used_time.ToInternalValue();
+  // Preserve nullity.
+  if (last_used_time.is_null()) {
+    last_used_time_seconds_since_epoch_ = 0;
+    return;
+  }
+
+  const base::TimeDelta since_unix_epoch =
+      last_used_time - base::Time::UnixEpoch();
+  const int64 seconds_since_unix_epoch = since_unix_epoch.InSeconds();
+  DCHECK_LE(implicit_cast<int64>(std::numeric_limits<uint32>::min()),
+            seconds_since_unix_epoch);
+  DCHECK_GE(implicit_cast<int64>(std::numeric_limits<uint32>::max()),
+            seconds_since_unix_epoch);
+
+  last_used_time_seconds_since_epoch_ = seconds_since_unix_epoch;
+  // Avoid accidental nullity.
+  if (last_used_time_seconds_since_epoch_ == 0)
+    last_used_time_seconds_since_epoch_ = 1;
 }
 
 void EntryMetadata::Serialize(Pickle* pickle) const {
   DCHECK(pickle);
-  COMPILE_ASSERT(sizeof(EntryMetadata) == (sizeof(int64) + sizeof(uint64)),
-                 EntryMetadata_has_two_member_variables);
-  pickle->WriteInt64(last_used_time_);
+  int64 internal_last_used_time = GetLastUsedTime().ToInternalValue();
+  pickle->WriteInt64(internal_last_used_time);
   pickle->WriteUInt64(entry_size_);
 }
 
 bool EntryMetadata::Deserialize(PickleIterator* it) {
   DCHECK(it);
-  return it->ReadInt64(&last_used_time_) && it->ReadUInt64(&entry_size_);
+  int64 tmp_last_used_time;
+  uint64 tmp_entry_size;
+  if (!it->ReadInt64(&tmp_last_used_time) || !it->ReadUInt64(&tmp_entry_size))
+    return false;
+  SetLastUsedTime(base::Time::FromInternalValue(tmp_last_used_time));
+  entry_size_ = tmp_entry_size;
+  return true;
 }
 
 SimpleIndex::SimpleIndex(base::SingleThreadTaskRunner* io_thread,
@@ -292,7 +325,7 @@
       base::Bind(&SimpleIndex::EvictionDone, AsWeakPtr()));
 }
 
-bool SimpleIndex::UpdateEntrySize(uint64 entry_hash, uint64 entry_size) {
+bool SimpleIndex::UpdateEntrySize(uint64 entry_hash, int entry_size) {
   DCHECK(io_thread_checker_.CalledOnValidThread());
   EntrySet::iterator it = entries_set_.find(entry_hash);
   if (it == entries_set_.end())
@@ -336,10 +369,10 @@
 }
 
 void SimpleIndex::UpdateEntryIteratorSize(EntrySet::iterator* it,
-                                          uint64 entry_size) {
+                                          int entry_size) {
   // Update the total cache size with the new entry size.
   DCHECK(io_thread_checker_.CalledOnValidThread());
-  DCHECK_GE(cache_size_, (*it)->second.GetEntrySize());
+  DCHECK_GE(cache_size_, implicit_cast<uint64>((*it)->second.GetEntrySize()));
   cache_size_ -= (*it)->second.GetEntrySize();
   cache_size_ += entry_size;
   (*it)->second.SetEntrySize(entry_size);
@@ -434,9 +467,17 @@
 }
 
 scoped_ptr<SimpleIndex::HashList> SimpleIndex::ExtractEntriesBetween(
-    const base::Time initial_time, const base::Time end_time,
+    base::Time initial_time,
+    base::Time end_time,
     bool delete_entries) {
   DCHECK_EQ(true, initialized_);
+
+  if (!initial_time.is_null())
+    initial_time -= EntryMetadata::GetLowerEpsilonForTimeComparisons();
+  if (end_time.is_null())
+    end_time = base::Time::Max();
+  else
+    end_time += EntryMetadata::GetUpperEpsilonForTimeComparisons();
   const base::Time extended_end_time =
       end_time.is_null() ? base::Time::Max() : end_time;
   DCHECK(extended_end_time >= initial_time);
diff --git a/net/disk_cache/simple/simple_index.h b/net/disk_cache/simple/simple_index.h
index 9890e29..d35bd39 100644
--- a/net/disk_cache/simple/simple_index.h
+++ b/net/disk_cache/simple/simple_index.h
@@ -38,32 +38,36 @@
 class NET_EXPORT_PRIVATE EntryMetadata {
  public:
   EntryMetadata();
-  EntryMetadata(base::Time last_used_time, uint64 entry_size);
+  EntryMetadata(base::Time last_used_time, int entry_size);
 
   base::Time GetLastUsedTime() const;
   void SetLastUsedTime(const base::Time& last_used_time);
 
-  uint64 GetEntrySize() const { return entry_size_; }
-  void SetEntrySize(uint64 entry_size) { entry_size_ = entry_size; }
+  int GetEntrySize() const { return entry_size_; }
+  void SetEntrySize(int entry_size) { entry_size_ = entry_size; }
 
   // Serialize the data into the provided pickle.
   void Serialize(Pickle* pickle) const;
   bool Deserialize(PickleIterator* it);
 
+  static base::TimeDelta GetLowerEpsilonForTimeComparisons() {
+    return base::TimeDelta::FromSeconds(1);
+  }
+  static base::TimeDelta GetUpperEpsilonForTimeComparisons() {
+    return base::TimeDelta();
+  }
+
  private:
   friend class SimpleIndexFileTest;
 
   // When adding new members here, you should update the Serialize() and
   // Deserialize() methods.
 
-  // This is the serialized format from Time::ToInternalValue().
-  // If you want to make calculations/comparisons, you should use the
-  // base::Time() class. Use the GetLastUsedTime() method above.
-  // TODO(felipeg): Use Time() here.
-  int64 last_used_time_;
+  uint32 last_used_time_seconds_since_epoch_;
 
-  uint64 entry_size_;  // Storage size in bytes.
+  int32 entry_size_;  // Storage size in bytes.
 };
+COMPILE_ASSERT(sizeof(EntryMetadata) == 8, metadata_size);
 
 // This class is not Thread-safe.
 class NET_EXPORT_PRIVATE SimpleIndex
@@ -97,7 +101,7 @@
   // Update the size (in bytes) of an entry, in the metadata stored in the
   // index. This should be the total disk-file size including all streams of the
   // entry.
-  bool UpdateEntrySize(uint64 entry_hash, uint64 entry_size);
+  bool UpdateEntrySize(uint64 entry_hash, int entry_size);
 
   typedef base::hash_map<uint64, EntryMetadata> EntrySet;
 
@@ -136,7 +140,7 @@
 
   void PostponeWritingToDisk();
 
-  void UpdateEntryIteratorSize(EntrySet::iterator* it, uint64 entry_size);
+  void UpdateEntryIteratorSize(EntrySet::iterator* it, int entry_size);
 
   // Must run on IO Thread.
   void MergeInitializingSet(scoped_ptr<SimpleIndexLoadResult> load_result);
diff --git a/net/disk_cache/simple/simple_index_file.cc b/net/disk_cache/simple/simple_index_file.cc
index 0136be1..938ae8e 100644
--- a/net/disk_cache/simple/simple_index_file.cc
+++ b/net/disk_cache/simple/simple_index_file.cc
@@ -242,9 +242,10 @@
     index_file_state = INDEX_STATE_STALE;
   } else {
     index_file_state = INDEX_STATE_FRESH;
-    base::Time latest_dir_mtime;
-    if (simple_util::GetMTime(cache_directory, &latest_dir_mtime) &&
-        IsIndexFileStale(latest_dir_mtime, index_file_path)) {
+    base::PlatformFileInfo file_info;
+    bool file_info_result = file_util::GetFileInfo(index_file_path, &file_info);
+    DCHECK(file_info_result);
+    if (IsIndexFileStale(file_info.last_modified, index_file_path)) {
       // A file operation has updated the directory since we last looked at it
       // during backend initialization.
       index_file_state = INDEX_STATE_FRESH_CONCURRENT_UPDATES;
@@ -418,10 +419,10 @@
 // static
 bool SimpleIndexFile::IsIndexFileStale(base::Time cache_last_modified,
                                        const base::FilePath& index_file_path) {
-  base::Time index_mtime;
-  if (!simple_util::GetMTime(index_file_path, &index_mtime))
+  base::PlatformFileInfo file_info;
+  if (!file_util::GetFileInfo(index_file_path, &file_info))
     return true;
-  return index_mtime < cache_last_modified;
+  return file_info.last_modified < cache_last_modified;
 }
 
 }  // namespace disk_cache
diff --git a/net/disk_cache/simple/simple_index_file_unittest.cc b/net/disk_cache/simple/simple_index_file_unittest.cc
index bf7ee83..9105141 100644
--- a/net/disk_cache/simple/simple_index_file_unittest.cc
+++ b/net/disk_cache/simple/simple_index_file_unittest.cc
@@ -75,8 +75,10 @@
 class SimpleIndexFileTest : public testing::Test {
  public:
   bool CompareTwoEntryMetadata(const EntryMetadata& a, const EntryMetadata& b) {
-    return a.last_used_time_ == b.last_used_time_ &&
-           a.entry_size_ == b.entry_size_;
+    return
+        a.last_used_time_seconds_since_epoch_ ==
+            b.last_used_time_seconds_since_epoch_ &&
+        a.entry_size_ == b.entry_size_;
   }
 
  protected:
@@ -108,8 +110,7 @@
                                                 456);
   for (size_t i = 0; i < kNumHashes; ++i) {
     uint64 hash = kHashes[i];
-    metadata_entries[i] =
-        EntryMetadata(Time::FromInternalValue(hash), hash);
+    metadata_entries[i] = EntryMetadata(Time(), hash);
     SimpleIndex::InsertInEntrySet(hash, metadata_entries[i], &entries);
   }
 
@@ -135,34 +136,34 @@
 TEST_F(SimpleIndexFileTest, IsIndexFileStale) {
   base::ScopedTempDir cache_dir;
   ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
-  base::Time cache_mtime;
+  base::PlatformFileInfo file_info;
   const base::FilePath cache_path = cache_dir.path();
 
-  ASSERT_TRUE(simple_util::GetMTime(cache_path, &cache_mtime));
+  ASSERT_TRUE(file_util::GetFileInfo(cache_path, &file_info));
   WrappedSimpleIndexFile simple_index_file(cache_path);
   const base::FilePath& index_path = simple_index_file.GetIndexFilePath();
-  EXPECT_TRUE(WrappedSimpleIndexFile::IsIndexFileStale(cache_mtime,
+  EXPECT_TRUE(WrappedSimpleIndexFile::IsIndexFileStale(file_info.last_modified,
                                                        index_path));
   const std::string kDummyData = "nothing to be seen here";
   EXPECT_EQ(static_cast<int>(kDummyData.size()),
             file_util::WriteFile(index_path,
                                  kDummyData.data(),
                                  kDummyData.size()));
-  ASSERT_TRUE(simple_util::GetMTime(cache_path, &cache_mtime));
-  EXPECT_FALSE(WrappedSimpleIndexFile::IsIndexFileStale(cache_mtime,
+  ASSERT_TRUE(file_util::GetFileInfo(cache_path, &file_info));
+  EXPECT_FALSE(WrappedSimpleIndexFile::IsIndexFileStale(file_info.last_modified,
                                                         index_path));
 
   const base::Time past_time = base::Time::Now() -
       base::TimeDelta::FromSeconds(10);
   EXPECT_TRUE(file_util::TouchFile(index_path, past_time, past_time));
   EXPECT_TRUE(file_util::TouchFile(cache_path, past_time, past_time));
-  ASSERT_TRUE(simple_util::GetMTime(cache_path, &cache_mtime));
-  EXPECT_FALSE(WrappedSimpleIndexFile::IsIndexFileStale(cache_mtime,
+  ASSERT_TRUE(file_util::GetFileInfo(cache_path, &file_info));
+  EXPECT_FALSE(WrappedSimpleIndexFile::IsIndexFileStale(file_info.last_modified,
                                                         index_path));
   const base::Time even_older =
       past_time - base::TimeDelta::FromSeconds(10);
   EXPECT_TRUE(file_util::TouchFile(index_path, even_older, even_older));
-  EXPECT_TRUE(WrappedSimpleIndexFile::IsIndexFileStale(cache_mtime,
+  EXPECT_TRUE(WrappedSimpleIndexFile::IsIndexFileStale(file_info.last_modified,
                                                        index_path));
 
 }
@@ -177,8 +178,7 @@
   EntryMetadata metadata_entries[kNumHashes];
   for (size_t i = 0; i < kNumHashes; ++i) {
     uint64 hash = kHashes[i];
-    metadata_entries[i] =
-        EntryMetadata(Time::FromInternalValue(hash), hash);
+    metadata_entries[i] = EntryMetadata(Time(), hash);
     SimpleIndex::InsertInEntrySet(hash, metadata_entries[i], &entries);
   }
 
@@ -192,11 +192,11 @@
   }
 
   WrappedSimpleIndexFile simple_index_file(cache_dir.path());
-  base::Time fake_cache_mtime;
-  ASSERT_TRUE(simple_util::GetMTime(simple_index_file.GetIndexFilePath(),
-                                    &fake_cache_mtime));
+  base::PlatformFileInfo file_info;
+  ASSERT_TRUE(file_util::GetFileInfo(simple_index_file.GetIndexFilePath(),
+                                     &file_info));
   SimpleIndexLoadResult load_index_result;
-  simple_index_file.LoadIndexEntries(fake_cache_mtime,
+  simple_index_file.LoadIndexEntries(file_info.last_modified,
                                      GetCallback(),
                                      &load_index_result);
   base::RunLoop().RunUntilIdle();
@@ -222,14 +222,14 @@
             file_util::WriteFile(index_path,
                                  kDummyData.data(),
                                  kDummyData.size()));
-  base::Time fake_cache_mtime;
-  ASSERT_TRUE(simple_util::GetMTime(simple_index_file.GetIndexFilePath(),
-                                    &fake_cache_mtime));
-  EXPECT_FALSE(WrappedSimpleIndexFile::IsIndexFileStale(fake_cache_mtime,
+  base::PlatformFileInfo file_info;
+  ASSERT_TRUE(file_util::GetFileInfo(simple_index_file.GetIndexFilePath(),
+                                     &file_info));
+  EXPECT_FALSE(WrappedSimpleIndexFile::IsIndexFileStale(file_info.last_modified,
                                                         index_path));
 
   SimpleIndexLoadResult load_index_result;
-  simple_index_file.LoadIndexEntries(fake_cache_mtime,
+  simple_index_file.LoadIndexEntries(file_info.last_modified,
                                      GetCallback(),
                                      &load_index_result);
   base::RunLoop().RunUntilIdle();
diff --git a/net/disk_cache/simple/simple_index_unittest.cc b/net/disk_cache/simple/simple_index_unittest.cc
index a2c9016..3dc0dec 100644
--- a/net/disk_cache/simple/simple_index_unittest.cc
+++ b/net/disk_cache/simple/simple_index_unittest.cc
@@ -21,10 +21,9 @@
 namespace disk_cache {
 namespace {
 
-const int64 kTestLastUsedTimeInternal = 12345;
 const base::Time kTestLastUsedTime =
-    base::Time::FromInternalValue(kTestLastUsedTimeInternal);
-const uint64 kTestEntrySize = 789;
+    base::Time::UnixEpoch() + base::TimeDelta::FromDays(20);
+const int kTestEntrySize = 789;
 
 }  // namespace
 
@@ -36,7 +35,10 @@
   }
 
   void CheckEntryMetadataValues(const EntryMetadata& entry_metadata) {
-    EXPECT_EQ(kTestLastUsedTime, entry_metadata.GetLastUsedTime());
+    EXPECT_LT(kTestLastUsedTime - base::TimeDelta::FromSeconds(2),
+              entry_metadata.GetLastUsedTime());
+    EXPECT_GT(kTestLastUsedTime + base::TimeDelta::FromSeconds(2),
+              entry_metadata.GetLastUsedTime());
     EXPECT_EQ(kTestEntrySize, entry_metadata.GetEntrySize());
   }
 };
@@ -119,11 +121,11 @@
   }
 
   void WaitForTimeChange() {
-    base::Time now(base::Time::Now());
-
+    const base::Time initial_time = base::Time::Now();
     do {
       base::PlatformThread::YieldCurrentThread();
-    } while (now == base::Time::Now());
+    } while (base::Time::Now() -
+             initial_time < base::TimeDelta::FromSeconds(1));
   }
 
   // Redirect to allow single "friend" declaration in base class.
@@ -137,7 +139,7 @@
 
   void InsertIntoIndexFileReturn(uint64 hash_key,
                                  base::Time last_used_time,
-                                 uint64 entry_size) {
+                                 int entry_size) {
     index_file_->load_result()->entries.insert(std::make_pair(
         hash_key, EntryMetadata(last_used_time, entry_size)));
   }
@@ -158,15 +160,19 @@
 
 TEST_F(EntryMetadataTest, Basics) {
   EntryMetadata entry_metadata;
-  EXPECT_EQ(base::Time::FromInternalValue(0), entry_metadata.GetLastUsedTime());
-  EXPECT_EQ(size_t(0), entry_metadata.GetEntrySize());
+  EXPECT_EQ(base::Time(), entry_metadata.GetLastUsedTime());
+  EXPECT_EQ(0, entry_metadata.GetEntrySize());
 
   entry_metadata = NewEntryMetadataWithValues();
   CheckEntryMetadataValues(entry_metadata);
 
-  const base::Time new_time = base::Time::FromInternalValue(5);
+  const base::Time new_time = base::Time::Now();
   entry_metadata.SetLastUsedTime(new_time);
-  EXPECT_EQ(new_time, entry_metadata.GetLastUsedTime());
+
+  EXPECT_LT(new_time - base::TimeDelta::FromSeconds(2),
+            entry_metadata.GetLastUsedTime());
+  EXPECT_GT(new_time + base::TimeDelta::FromSeconds(2),
+            entry_metadata.GetLastUsedTime());
 }
 
 TEST_F(EntryMetadataTest, Serialize) {
@@ -216,7 +222,7 @@
   // Confirm blank state.
   EntryMetadata metadata;
   EXPECT_EQ(base::Time(), metadata.GetLastUsedTime());
-  EXPECT_EQ(0ul, metadata.GetEntrySize());
+  EXPECT_EQ(0, metadata.GetEntrySize());
 
   // Confirm state after insert.
   index()->Insert(hashes_.at<1>());
@@ -224,14 +230,14 @@
   base::Time now(base::Time::Now());
   EXPECT_LT(now - base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime());
   EXPECT_GT(now + base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime());
-  EXPECT_EQ(0ul, metadata.GetEntrySize());
+  EXPECT_EQ(0, metadata.GetEntrySize());
 
   // Confirm state after remove.
   metadata = EntryMetadata();
   index()->Remove(hashes_.at<1>());
   EXPECT_FALSE(GetEntryForTesting(hashes_.at<1>(), &metadata));
   EXPECT_EQ(base::Time(), metadata.GetLastUsedTime());
-  EXPECT_EQ(0ul, metadata.GetEntrySize());
+  EXPECT_EQ(0, metadata.GetEntrySize());
 }
 
 TEST_F(SimpleIndexTest, Has) {
@@ -304,17 +310,22 @@
   index()->SetMaxSize(1000);
 
   const uint64 kHash1 = hashes_.at<1>();
-  InsertIntoIndexFileReturn(kHash1, now - base::TimeDelta::FromDays(2), 475u);
+  InsertIntoIndexFileReturn(kHash1, now - base::TimeDelta::FromDays(2), 475);
   ReturnIndexFile();
 
   EntryMetadata metadata;
   EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata));
-  EXPECT_EQ(now - base::TimeDelta::FromDays(2), metadata.GetLastUsedTime());
-  EXPECT_EQ(475u, metadata.GetEntrySize());
+  EXPECT_LT(
+      now - base::TimeDelta::FromDays(2) - base::TimeDelta::FromSeconds(1),
+      metadata.GetLastUsedTime());
+  EXPECT_GT(
+      now - base::TimeDelta::FromDays(2) + base::TimeDelta::FromSeconds(1),
+      metadata.GetLastUsedTime());
+  EXPECT_EQ(475, metadata.GetEntrySize());
 
   index()->UpdateEntrySize(kHash1, 600u);
   EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata));
-  EXPECT_EQ(600u, metadata.GetEntrySize());
+  EXPECT_EQ(600, metadata.GetEntrySize());
   EXPECT_EQ(1, index()->GetEntryCount());
 }
 
@@ -357,11 +368,21 @@
 
   EntryMetadata metadata;
   EXPECT_TRUE(GetEntryForTesting(hashes_.at<1>(), &metadata));
-  EXPECT_EQ(now - base::TimeDelta::FromDays(2), metadata.GetLastUsedTime());
-  EXPECT_EQ(10ul, metadata.GetEntrySize());
+  EXPECT_LT(
+      now - base::TimeDelta::FromDays(2) - base::TimeDelta::FromSeconds(1),
+      metadata.GetLastUsedTime());
+  EXPECT_GT(
+      now - base::TimeDelta::FromDays(2) + base::TimeDelta::FromSeconds(1),
+      metadata.GetLastUsedTime());
+  EXPECT_EQ(10, metadata.GetEntrySize());
   EXPECT_TRUE(GetEntryForTesting(hashes_.at<2>(), &metadata));
-  EXPECT_EQ(now - base::TimeDelta::FromDays(3), metadata.GetLastUsedTime());
-  EXPECT_EQ(100ul, metadata.GetEntrySize());
+  EXPECT_LT(
+      now - base::TimeDelta::FromDays(3) - base::TimeDelta::FromSeconds(1),
+      metadata.GetLastUsedTime());
+  EXPECT_GT(
+      now - base::TimeDelta::FromDays(3) + base::TimeDelta::FromSeconds(1),
+      metadata.GetLastUsedTime());
+  EXPECT_EQ(100, metadata.GetEntrySize());
 }
 
 // Remove something that's going to come in from the loaded index.
@@ -393,7 +414,7 @@
   base::Time now(base::Time::Now());
   EXPECT_LT(now - base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime());
   EXPECT_GT(now + base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime());
-  EXPECT_EQ(0ul, metadata.GetEntrySize());
+  EXPECT_EQ(0, metadata.GetEntrySize());
 }
 
 // Insert and Remove something that's going to come in from the loaded index.
@@ -426,7 +447,7 @@
   base::Time now(base::Time::Now());
   EXPECT_LT(now - base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime());
   EXPECT_GT(now + base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime());
-  EXPECT_EQ(0ul, metadata.GetEntrySize());
+  EXPECT_EQ(0, metadata.GetEntrySize());
 }
 
 // Do all above tests at once + a non-conflict to test for cross-key
@@ -464,18 +485,25 @@
   EXPECT_TRUE(GetEntryForTesting(hashes_.at<2>(), &metadata));
   EXPECT_LT(now - base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime());
   EXPECT_GT(now + base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime());
-  EXPECT_EQ(0ul, metadata.GetEntrySize());
+  EXPECT_EQ(0, metadata.GetEntrySize());
 
   EXPECT_FALSE(index()->Has(hashes_.at<3>()));
 
   EXPECT_TRUE(GetEntryForTesting(hashes_.at<4>(), &metadata));
   EXPECT_LT(now - base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime());
   EXPECT_GT(now + base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime());
-  EXPECT_EQ(0ul, metadata.GetEntrySize());
+  EXPECT_EQ(0, metadata.GetEntrySize());
 
   EXPECT_TRUE(GetEntryForTesting(hashes_.at<5>(), &metadata));
-  EXPECT_EQ(now - base::TimeDelta::FromDays(6), metadata.GetLastUsedTime());
-  EXPECT_EQ(100000u, metadata.GetEntrySize());
+
+  EXPECT_GT(
+      now - base::TimeDelta::FromDays(6) + base::TimeDelta::FromSeconds(1),
+      metadata.GetLastUsedTime());
+  EXPECT_LT(
+      now - base::TimeDelta::FromDays(6) - base::TimeDelta::FromSeconds(1),
+      metadata.GetLastUsedTime());
+
+  EXPECT_EQ(100000, metadata.GetEntrySize());
 }
 
 TEST_F(SimpleIndexTest, BasicEviction) {
@@ -564,7 +592,7 @@
   const EntryMetadata& entry1(entry_set.begin()->second);
   EXPECT_LT(now - base::TimeDelta::FromMinutes(1), entry1.GetLastUsedTime());
   EXPECT_GT(now + base::TimeDelta::FromMinutes(1), entry1.GetLastUsedTime());
-  EXPECT_EQ(20u, entry1.GetEntrySize());
+  EXPECT_EQ(20, entry1.GetEntrySize());
 }
 
 TEST_F(SimpleIndexTest, DiskWritePostponed) {
diff --git a/net/disk_cache/simple/simple_synchronous_entry.cc b/net/disk_cache/simple/simple_synchronous_entry.cc
index 5452624..ced59cd 100644
--- a/net/disk_cache/simple/simple_synchronous_entry.cc
+++ b/net/disk_cache/simple/simple_synchronous_entry.cc
@@ -503,16 +503,12 @@
     for (int i = 0; i < kSimpleEntryFileCount; ++i) {
       PlatformFileInfo file_info;
       bool success = GetPlatformFileInfo(files_[i], &file_info);
-      base::Time file_last_modified;
       if (!success) {
         DLOG(WARNING) << "Could not get platform file info.";
         continue;
       }
       out_entry_stat->last_used = file_info.last_accessed;
-      if (simple_util::GetMTime(path_, &file_last_modified))
-        out_entry_stat->last_modified = file_last_modified;
-      else
-        out_entry_stat->last_modified = file_info.last_modified;
+      out_entry_stat->last_modified = file_info.last_modified;
 
       base::TimeDelta entry_age =
           base::Time::Now() - out_entry_stat->last_modified;
diff --git a/net/disk_cache/simple/simple_util.cc b/net/disk_cache/simple/simple_util.cc
index e9ec067..2317549 100644
--- a/net/disk_cache/simple/simple_util.cc
+++ b/net/disk_cache/simple/simple_util.cc
@@ -85,16 +85,6 @@
   return headers_size + data_offset;
 }
 
-// TODO(clamy, gavinp): this should go in base
-bool GetMTime(const base::FilePath& path, base::Time* out_mtime) {
-  DCHECK(out_mtime);
-  base::PlatformFileInfo file_info;
-  if (!file_util::GetFileInfo(path, &file_info))
-    return false;
-  *out_mtime = file_info.last_modified;
-  return true;
-}
-
-}  // namespace simple_backend
+}  // namespace simple_util
 
 }  // namespace disk_cache
diff --git a/net/disk_cache/simple/simple_util.h b/net/disk_cache/simple/simple_util.h
index 3bb80b9..154ee23 100644
--- a/net/disk_cache/simple/simple_util.h
+++ b/net/disk_cache/simple/simple_util.h
@@ -63,11 +63,7 @@
     const std::string& key,
     int data_offset);
 
-// Fills |out_time| with the time the file last modified time.
-// TODO(gavinp): Remove this function.
-NET_EXPORT_PRIVATE bool GetMTime(const base::FilePath& path,
-                                 base::Time* out_mtime);
-}  // namespace simple_backend
+}  // namespace simple_util
 
 }  // namespace disk_cache
 
diff --git a/net/dns/mdns_client_impl.cc b/net/dns/mdns_client_impl.cc
index 8f79edf..a90ac64 100644
--- a/net/dns/mdns_client_impl.cc
+++ b/net/dns/mdns_client_impl.cc
@@ -378,7 +378,7 @@
   observer_list_iterator->second->RemoveObserver(listener);
 
   // Remove the observer list from the map if it is empty
-  if (observer_list_iterator->second->size() == 0) {
+  if (!observer_list_iterator->second->might_have_observers()) {
     // Schedule the actual removal for later in case the listener removal
     // happens while iterating over the observer list.
     base::MessageLoop::current()->PostTask(
@@ -389,7 +389,7 @@
 
 void MDnsClientImpl::Core::CleanupObserverList(const ListenerKey& key) {
   ListenerMap::iterator found = listeners_.find(key);
-  if (found != listeners_.end() && found->second->size() == 0) {
+  if (found != listeners_.end() && !found->second->might_have_observers()) {
     delete found->second;
     listeners_.erase(found);
   }
diff --git a/net/http/http_auth_cache.h b/net/http/http_auth_cache.h
index 1d6f2d2..75b379f 100644
--- a/net/http/http_auth_cache.h
+++ b/net/http/http_auth_cache.h
@@ -26,7 +26,77 @@
 // Entries can be looked up by either (origin, realm, scheme) or (origin, path).
 class NET_EXPORT_PRIVATE HttpAuthCache {
  public:
-  class Entry;
+  class NET_EXPORT_PRIVATE Entry {
+   public:
+    ~Entry();
+
+    const GURL& origin() const {
+      return origin_;
+    }
+
+    // The case-sensitive realm string of the challenge.
+    const std::string realm() const {
+      return realm_;
+    }
+
+    // The authentication scheme of the challenge.
+    HttpAuth::Scheme scheme() const {
+      return scheme_;
+    }
+
+    // The authentication challenge.
+    const std::string auth_challenge() const {
+      return auth_challenge_;
+    }
+
+    // The login credentials.
+    const AuthCredentials& credentials() const {
+      return credentials_;
+    }
+
+    int IncrementNonceCount() {
+      return ++nonce_count_;
+    }
+
+    void UpdateStaleChallenge(const std::string& auth_challenge);
+
+   private:
+    friend class HttpAuthCache;
+    FRIEND_TEST_ALL_PREFIXES(HttpAuthCacheTest, AddPath);
+    FRIEND_TEST_ALL_PREFIXES(HttpAuthCacheTest, AddToExistingEntry);
+
+    typedef std::list<std::string> PathList;
+
+    Entry();
+
+    // Adds a path defining the realm's protection space. If the path is
+    // already contained in the protection space, is a no-op.
+    void AddPath(const std::string& path);
+
+    // Returns true if |dir| is contained within the realm's protection
+    // space.  |*path_len| is set to the length of the enclosing path if
+    // such a path exists and |path_len| is non-NULL.  If no enclosing
+    // path is found, |*path_len| is left unmodified.
+    //
+    // Note that proxy auth cache entries are associated with empty
+    // paths.  Therefore it is possible for HasEnclosingPath() to return
+    // true and set |*path_len| to 0.
+    bool HasEnclosingPath(const std::string& dir, size_t* path_len);
+
+    // |origin_| contains the {protocol, host, port} of the server.
+    GURL origin_;
+    std::string realm_;
+    HttpAuth::Scheme scheme_;
+
+    // Identity.
+    std::string auth_challenge_;
+    AuthCredentials credentials_;
+
+    int nonce_count_;
+
+    // List of paths that define the realm's protection space.
+    PathList paths_;
+  };
 
   // Prevent unbounded memory growth. These are safeguards for abuse; it is
   // not expected that the limits will be reached in ordinary usage.
@@ -106,78 +176,6 @@
 };
 
 // An authentication realm entry.
-class NET_EXPORT_PRIVATE HttpAuthCache::Entry {
- public:
-  ~Entry();
-
-  const GURL& origin() const {
-    return origin_;
-  }
-
-  // The case-sensitive realm string of the challenge.
-  const std::string realm() const {
-    return realm_;
-  }
-
-  // The authentication scheme of the challenge.
-  HttpAuth::Scheme scheme() const {
-    return scheme_;
-  }
-
-  // The authentication challenge.
-  const std::string auth_challenge() const {
-    return auth_challenge_;
-  }
-
-  // The login credentials.
-  const AuthCredentials& credentials() const {
-    return credentials_;
-  }
-
-  int IncrementNonceCount() {
-    return ++nonce_count_;
-  }
-
-  void UpdateStaleChallenge(const std::string& auth_challenge);
-
- private:
-  friend class HttpAuthCache;
-  FRIEND_TEST_ALL_PREFIXES(HttpAuthCacheTest, AddPath);
-  FRIEND_TEST_ALL_PREFIXES(HttpAuthCacheTest, AddToExistingEntry);
-
-  typedef std::list<std::string> PathList;
-
-  Entry();
-
-  // Adds a path defining the realm's protection space. If the path is
-  // already contained in the protection space, is a no-op.
-  void AddPath(const std::string& path);
-
-  // Returns true if |dir| is contained within the realm's protection
-  // space.  |*path_len| is set to the length of the enclosing path if
-  // such a path exists and |path_len| is non-NULL.  If no enclosing
-  // path is found, |*path_len| is left unmodified.
-  //
-  // Note that proxy auth cache entries are associated with empty
-  // paths.  Therefore it is possible for HasEnclosingPath() to return
-  // true and set |*path_len| to 0.
-  bool HasEnclosingPath(const std::string& dir, size_t* path_len);
-
-  // |origin_| contains the {protocol, host, port} of the server.
-  GURL origin_;
-  std::string realm_;
-  HttpAuth::Scheme scheme_;
-
-  // Identity.
-  std::string auth_challenge_;
-  AuthCredentials credentials_;
-
-  int nonce_count_;
-
-  // List of paths that define the realm's protection space.
-  PathList paths_;
-};
-
 }  // namespace net
 
 #endif  // NET_HTTP_HTTP_AUTH_CACHE_H_
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index 37e5334..82d461c 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -5316,6 +5316,61 @@
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 }
 
+// Verify that no-cache resources are stored in cache, but are not fetched from
+// cache during normal loads.
+TEST(HttpCache, CacheControlNoCacheNormalLoad) {
+  MockHttpCache cache;
+
+  ScopedMockTransaction transaction(kSimpleGET_Transaction);
+  transaction.response_headers = "cache-control: no-cache\n";
+
+  // Initial load.
+  RunTransactionTest(cache.http_cache(), transaction);
+
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  // Try loading again; it should result in a network fetch.
+  RunTransactionTest(cache.http_cache(), transaction);
+
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  EXPECT_EQ(1, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  disk_cache::Entry* entry;
+  EXPECT_TRUE(cache.OpenBackendEntry(transaction.url, &entry));
+  entry->Close();
+}
+
+// Verify that no-cache resources are stored in cache and fetched from cache
+// when the LOAD_PREFERRING_CACHE flag is set.
+TEST(HttpCache, CacheControlNoCacheHistoryLoad) {
+  MockHttpCache cache;
+
+  ScopedMockTransaction transaction(kSimpleGET_Transaction);
+  transaction.response_headers = "cache-control: no-cache\n";
+
+  // Initial load.
+  RunTransactionTest(cache.http_cache(), transaction);
+
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  // Try loading again with LOAD_PREFERRING_CACHE.
+  transaction.load_flags = net::LOAD_PREFERRING_CACHE;
+  RunTransactionTest(cache.http_cache(), transaction);
+
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(1, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  disk_cache::Entry* entry;
+  EXPECT_TRUE(cache.OpenBackendEntry(transaction.url, &entry));
+  entry->Close();
+}
+
 TEST(HttpCache, CacheControlNoStore) {
   MockHttpCache cache;
 
diff --git a/net/http/transport_security_state_static.certs b/net/http/transport_security_state_static.certs
index 610e56a..dceafe4 100644
--- a/net/http/transport_security_state_static.certs
+++ b/net/http/transport_security_state_static.certs
@@ -85,6 +85,27 @@
 GoogleG2
 sha1/Q9rWMO5T+KmAym79hfRqo3mQ4Oo=
 
+ThawteSGCCA
+-----BEGIN CERTIFICATE-----
+MIIDIzCCAoygAwIBAgIEMAAAAjANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJV
+UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsTLkNsYXNzIDMgUHVi
+bGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNTEzMDAw
+MDAwWhcNMTQwNTEyMjM1OTU5WjBMMQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhh
+d3RlIENvbnN1bHRpbmcgKFB0eSkgTHRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBD
+QTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1NNn0I0Vf67NMf59HZGhPwtx
+PKzMyGT7Y/wySweUvW+Aui/hBJPAM/wJMyPpC3QrccQDxtLN4i/1CWPN/0ilAL/g
+5/OIty0y3pg25gqtAHvEZEo7hHUD8nCSfQ5i9SGraTaEMXWQ+L/HbIgbBpV8yeWo
+3nWhLHpo39XKHIdYYBkCAwEAAaOB/jCB+zASBgNVHRMBAf8ECDAGAQH/AgEAMAsG
+A1UdDwQEAwIBBjARBglghkgBhvhCAQEEBAMCAQYwKAYDVR0RBCEwH6QdMBsxGTAX
+BgNVBAMTEFByaXZhdGVMYWJlbDMtMTUwMQYDVR0fBCowKDAmoCSgIoYgaHR0cDov
+L2NybC52ZXJpc2lnbi5jb20vcGNhMy5jcmwwMgYIKwYBBQUHAQEEJjAkMCIGCCsG
+AQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMDQGA1UdJQQtMCsGCCsGAQUF
+BwMBBggrBgEFBQcDAgYJYIZIAYb4QgQBBgpghkgBhvhFAQgBMA0GCSqGSIb3DQEB
+BQUAA4GBAFWsY+reod3SkF+fC852vhNRj5PZBSvIG3dLrWlQoe7e3P3bB+noOZTc
+q3J5Lwa/q4FwxKjt6lM07e8eU9kGx1Yr0Vz00YqOtCuxN5BICEIlxT6Ky3/rbwTR
+bcV0oveifHtgPHfNDs5IAn8BL7abN+AqKjbc1YXWrOU/VG+WHgWv
+-----END CERTIFICATE-----
+
 EquifaxSecureCA
 -----BEGIN CERTIFICATE-----
 MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
diff --git a/net/http/transport_security_state_static.h b/net/http/transport_security_state_static.h
index f7ca89d..0cc888f 100644
--- a/net/http/transport_security_state_static.h
+++ b/net/http/transport_security_state_static.h
@@ -42,6 +42,10 @@
     "\x43\xda\xd6\x30\xee\x53\xf8\xa9\x80\xca"
     "\x6e\xfd\x85\xf4\x6a\xa3\x79\x90\xe0\xea";
 
+static const char kSPKIHash_ThawteSGCCA[] =
+    "\x87\x31\xea\x0e\x3d\xf5\xe8\x70\x3e\x83"
+    "\x72\x57\x77\xa9\x65\x3b\x3b\xfa\x5e\x14";
+
 static const char kSPKIHash_EquifaxSecureCA[] =
     "\x48\xe6\x68\xf9\x2b\xd2\xb2\x95\xd7\x47"
     "\xd8\x23\x20\x10\x4f\x33\x98\x90\x9f\xd4";
@@ -264,6 +268,7 @@
   kSPKIHash_Intel,
   kSPKIHash_TCTrustCenter,
   kSPKIHash_Vodafone,
+  kSPKIHash_ThawteSGCCA,
   NULL,
 };
 #define kGooglePins { \
@@ -415,12 +420,12 @@
   {17, true, "\004code\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM },
   {16, true, "\012googlecode\003com", false, kGooglePins, DOMAIN_GOOGLECODE_COM },
   {15, true, "\002dl\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM },
+  {26, true, "\011translate\012googleapis\003com", true, kGooglePins, DOMAIN_GOOGLEAPIS_COM },
   {23, true, "\005chart\004apis\006google\003com", false, kGooglePins, DOMAIN_GOOGLE_COM },
   {11, true, "\005ytimg\003com", false, kGooglePins, DOMAIN_YTIMG_COM },
   {23, true, "\021googleusercontent\003com", false, kGooglePins, DOMAIN_GOOGLEUSERCONTENT_COM },
   {13, true, "\007youtube\003com", false, kGooglePins, DOMAIN_YOUTUBE_COM },
   {16, true, "\012googleapis\003com", false, kGooglePins, DOMAIN_GOOGLEAPIS_COM },
-  {26, true, "\011translate\012googleapis\003com", true, kGooglePins, DOMAIN_GOOGLEAPIS_COM },
   {22, true, "\020googleadservices\003com", false, kGooglePins, DOMAIN_GOOGLEADSERVICES_COM },
   {13, true, "\007appspot\003com", false, kGooglePins, DOMAIN_APPSPOT_COM },
   {23, true, "\021googlesyndication\003com", false, kGooglePins, DOMAIN_GOOGLESYNDICATION_COM },
@@ -836,6 +841,9 @@
   {10, true, "\005haste\002ch", true, kNoPins, DOMAIN_NOT_PINNED },
   {12, true, "\007mudcrab\002us", true, kNoPins, DOMAIN_NOT_PINNED },
   {13, true, "\010mediacru\002sh", true, kNoPins, DOMAIN_NOT_PINNED },
+  {13, true, "\010lolicore\002ch", true, kNoPins, DOMAIN_NOT_PINNED },
+  {16, true, "\007cloudns\003com\002au", true, kNoPins, DOMAIN_NOT_PINNED },
+  {19, true, "\005oplop\007appspot\003com", true, kNoPins, DOMAIN_NOT_PINNED },
 };
 static const size_t kNumPreloadedSTS = ARRAYSIZE_UNSAFE(kPreloadedSTS);
 
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index f2ea764..6c7f5c7 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -56,7 +56,8 @@
         "Aetna",
         "Intel",
         "TCTrustCenter",
-        "Vodafone"
+        "Vodafone",
+        "ThawteSGCCA"
       ]
     },
     {
@@ -626,6 +627,9 @@
     { "name": "haste.ch", "include_subdomains": true, "mode": "force-https" },
     { "name": "mudcrab.us", "include_subdomains": true, "mode": "force-https" },
     { "name": "mediacru.sh", "include_subdomains": true, "mode": "force-https" },
+    { "name": "lolicore.ch", "include_subdomains": true, "mode": "force-https" },
+    { "name": "cloudns.com.au", "include_subdomains": true, "mode": "force-https" },
+    { "name": "oplop.appspot.com", "include_subdomains": true, "mode": "force-https" },
 
     // Entries that are only valid if the client supports SNI.
     { "name": "gmail.com", "mode": "force-https", "pins": "google", "snionly": true },
diff --git a/net/net.gyp b/net/net.gyp
index 4f99432..a189432 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -869,6 +869,8 @@
         'socket/nss_ssl_util.cc',
         'socket/nss_ssl_util.h',
         'socket/server_socket.h',
+        'socket/socket_descriptor.cc',
+        'socket/socket_descriptor.h',
         'socket/socket_net_log_params.cc',
         'socket/socket_net_log_params.h',
         'socket/socket.h',
@@ -2911,6 +2913,21 @@
         },
       ],
     }],
+    ['OS == "android" or OS == "linux"', {
+      'targets': [
+        {
+          'target_name': 'disk_cache_memory_test',
+          'type': 'executable',
+          'dependencies': [
+            '../base/base.gyp:base',
+            'net',
+          ],
+          'sources': [
+            'tools/disk_cache_memory_test/disk_cache_memory_test.cc',
+          ],
+        },
+      ],
+    }],
     ['test_isolation_mode != "noop"', {
       'targets': [
         {
diff --git a/net/net.target.darwin-arm.mk b/net/net.target.darwin-arm.mk
index bce0d76..491b6d9 100644
--- a/net/net.target.darwin-arm.mk
+++ b/net/net.target.darwin-arm.mk
@@ -349,6 +349,7 @@
 	net/socket/client_socket_pool_histograms.cc \
 	net/socket/client_socket_pool_manager.cc \
 	net/socket/client_socket_pool_manager_impl.cc \
+	net/socket/socket_descriptor.cc \
 	net/socket/socket_net_log_params.cc \
 	net/socket/socks5_client_socket.cc \
 	net/socket/socks_client_socket.cc \
diff --git a/net/net.target.darwin-mips.mk b/net/net.target.darwin-mips.mk
index 1425b90..981fc29 100644
--- a/net/net.target.darwin-mips.mk
+++ b/net/net.target.darwin-mips.mk
@@ -349,6 +349,7 @@
 	net/socket/client_socket_pool_histograms.cc \
 	net/socket/client_socket_pool_manager.cc \
 	net/socket/client_socket_pool_manager_impl.cc \
+	net/socket/socket_descriptor.cc \
 	net/socket/socket_net_log_params.cc \
 	net/socket/socks5_client_socket.cc \
 	net/socket/socks_client_socket.cc \
diff --git a/net/net.target.darwin-x86.mk b/net/net.target.darwin-x86.mk
index 28e8768..a1773d7 100644
--- a/net/net.target.darwin-x86.mk
+++ b/net/net.target.darwin-x86.mk
@@ -349,6 +349,7 @@
 	net/socket/client_socket_pool_histograms.cc \
 	net/socket/client_socket_pool_manager.cc \
 	net/socket/client_socket_pool_manager_impl.cc \
+	net/socket/socket_descriptor.cc \
 	net/socket/socket_net_log_params.cc \
 	net/socket/socks5_client_socket.cc \
 	net/socket/socks_client_socket.cc \
diff --git a/net/net.target.linux-arm.mk b/net/net.target.linux-arm.mk
index bce0d76..491b6d9 100644
--- a/net/net.target.linux-arm.mk
+++ b/net/net.target.linux-arm.mk
@@ -349,6 +349,7 @@
 	net/socket/client_socket_pool_histograms.cc \
 	net/socket/client_socket_pool_manager.cc \
 	net/socket/client_socket_pool_manager_impl.cc \
+	net/socket/socket_descriptor.cc \
 	net/socket/socket_net_log_params.cc \
 	net/socket/socks5_client_socket.cc \
 	net/socket/socks_client_socket.cc \
diff --git a/net/net.target.linux-mips.mk b/net/net.target.linux-mips.mk
index 1425b90..981fc29 100644
--- a/net/net.target.linux-mips.mk
+++ b/net/net.target.linux-mips.mk
@@ -349,6 +349,7 @@
 	net/socket/client_socket_pool_histograms.cc \
 	net/socket/client_socket_pool_manager.cc \
 	net/socket/client_socket_pool_manager_impl.cc \
+	net/socket/socket_descriptor.cc \
 	net/socket/socket_net_log_params.cc \
 	net/socket/socks5_client_socket.cc \
 	net/socket/socks_client_socket.cc \
diff --git a/net/net.target.linux-x86.mk b/net/net.target.linux-x86.mk
index 28e8768..a1773d7 100644
--- a/net/net.target.linux-x86.mk
+++ b/net/net.target.linux-x86.mk
@@ -349,6 +349,7 @@
 	net/socket/client_socket_pool_histograms.cc \
 	net/socket/client_socket_pool_manager.cc \
 	net/socket/client_socket_pool_manager_impl.cc \
+	net/socket/socket_descriptor.cc \
 	net/socket/socket_net_log_params.cc \
 	net/socket/socks5_client_socket.cc \
 	net/socket/socks_client_socket.cc \
diff --git a/net/proxy/dhcp_proxy_script_adapter_fetcher_win.cc b/net/proxy/dhcp_proxy_script_adapter_fetcher_win.cc
index 56e4747..676f6c3 100644
--- a/net/proxy/dhcp_proxy_script_adapter_fetcher_win.cc
+++ b/net/proxy/dhcp_proxy_script_adapter_fetcher_win.cc
@@ -10,7 +10,7 @@
 #include "base/message_loop/message_loop_proxy.h"
 #include "base/metrics/histogram.h"
 #include "base/strings/sys_string_conversions.h"
-#include "base/threading/worker_pool.h"
+#include "base/task_runner.h"
 #include "base/time/time.h"
 #include "net/base/net_errors.h"
 #include "net/proxy/dhcpcsvc_init_win.h"
@@ -32,8 +32,10 @@
 namespace net {
 
 DhcpProxyScriptAdapterFetcher::DhcpProxyScriptAdapterFetcher(
-    URLRequestContext* url_request_context)
-    : state_(STATE_START),
+    URLRequestContext* url_request_context,
+    scoped_refptr<base::TaskRunner> task_runner)
+    : task_runner_(task_runner),
+      state_(STATE_START),
       result_(ERR_IO_PENDING),
       url_request_context_(url_request_context) {
   DCHECK(url_request_context_);
@@ -55,7 +57,7 @@
   wait_timer_.Start(FROM_HERE, ImplGetTimeout(),
                     this, &DhcpProxyScriptAdapterFetcher::OnTimeout);
   scoped_refptr<DhcpQuery> dhcp_query(ImplCreateDhcpQuery());
-  base::WorkerPool::PostTaskAndReply(
+  task_runner_->PostTaskAndReply(
       FROM_HERE,
       base::Bind(
           &DhcpProxyScriptAdapterFetcher::DhcpQuery::GetPacURLForAdapter,
@@ -64,8 +66,7 @@
       base::Bind(
           &DhcpProxyScriptAdapterFetcher::OnDhcpQueryDone,
           AsWeakPtr(),
-          dhcp_query),
-      true);
+          dhcp_query));
 }
 
 void DhcpProxyScriptAdapterFetcher::Cancel() {
diff --git a/net/proxy/dhcp_proxy_script_adapter_fetcher_win.h b/net/proxy/dhcp_proxy_script_adapter_fetcher_win.h
index fadf234..59597d9 100644
--- a/net/proxy/dhcp_proxy_script_adapter_fetcher_win.h
+++ b/net/proxy/dhcp_proxy_script_adapter_fetcher_win.h
@@ -17,6 +17,10 @@
 #include "net/base/net_export.h"
 #include "url/gurl.h"
 
+namespace base {
+class TaskRunner;
+}
+
 namespace net {
 
 class ProxyScriptFetcher;
@@ -29,8 +33,9 @@
       NON_EXPORTED_BASE(public base::NonThreadSafe) {
  public:
   // |url_request_context| must outlive DhcpProxyScriptAdapterFetcher.
-  explicit DhcpProxyScriptAdapterFetcher(
-      URLRequestContext* url_request_context);
+  // |task_runner| will be used to post tasks to a thread.
+  DhcpProxyScriptAdapterFetcher(URLRequestContext* url_request_context,
+                                scoped_refptr<base::TaskRunner> task_runner);
   virtual ~DhcpProxyScriptAdapterFetcher();
 
   // Starts a fetch.  On completion (but not cancellation), |callback|
@@ -144,6 +149,9 @@
   void OnFetcherDone(int result);
   void TransitionToFinish();
 
+  // TaskRunner for posting tasks to a worker thread.
+  scoped_refptr<base::TaskRunner> task_runner_;
+
   // Current state of this state machine.
   State state_;
 
diff --git a/net/proxy/dhcp_proxy_script_adapter_fetcher_win_unittest.cc b/net/proxy/dhcp_proxy_script_adapter_fetcher_win_unittest.cc
index be177fa..1728512 100644
--- a/net/proxy/dhcp_proxy_script_adapter_fetcher_win_unittest.cc
+++ b/net/proxy/dhcp_proxy_script_adapter_fetcher_win_unittest.cc
@@ -4,9 +4,10 @@
 
 #include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h"
 
-#include "base/perftimer.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/test/perftimer.h"
 #include "base/test/test_timeouts.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "base/timer/timer.h"
 #include "net/base/net_errors.h"
 #include "net/base/test_completion_callback.h"
@@ -33,8 +34,10 @@
 class MockDhcpProxyScriptAdapterFetcher
     : public DhcpProxyScriptAdapterFetcher {
  public:
-  explicit MockDhcpProxyScriptAdapterFetcher(URLRequestContext* context)
-      : DhcpProxyScriptAdapterFetcher(context),
+  explicit MockDhcpProxyScriptAdapterFetcher(
+      URLRequestContext* context,
+      scoped_refptr<base::TaskRunner> task_runner)
+      : DhcpProxyScriptAdapterFetcher(context, task_runner),
         dhcp_delay_(base::TimeDelta::FromMilliseconds(1)),
         timeout_(TestTimeouts::action_timeout()),
         configured_url_(kPacUrl),
@@ -132,8 +135,16 @@
  public:
   FetcherClient()
       : url_request_context_(new TestURLRequestContext()),
-        fetcher_(
-            new MockDhcpProxyScriptAdapterFetcher(url_request_context_.get())) {
+        worker_pool_(
+            new base::SequencedWorkerPool(4, "DhcpAdapterFetcherTest")),
+        fetcher_(new MockDhcpProxyScriptAdapterFetcher(
+            url_request_context_.get(),
+            worker_pool_->GetTaskRunnerWithShutdownBehavior(
+                base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN))) {
+  }
+
+  ~FetcherClient() {
+    worker_pool_->Shutdown();
   }
 
   void WaitForResult(int expected_error) {
@@ -151,6 +162,7 @@
 
   TestCompletionCallback callback_;
   scoped_ptr<URLRequestContext> url_request_context_;
+  scoped_refptr<base::SequencedWorkerPool> worker_pool_;
   scoped_ptr<MockDhcpProxyScriptAdapterFetcher> fetcher_;
   base::string16 pac_text_;
 };
@@ -253,8 +265,9 @@
     : public MockDhcpProxyScriptAdapterFetcher {
  public:
   explicit MockDhcpRealFetchProxyScriptAdapterFetcher(
-      URLRequestContext* context)
-      : MockDhcpProxyScriptAdapterFetcher(context),
+      URLRequestContext* context,
+      scoped_refptr<base::TaskRunner> task_runner)
+      : MockDhcpProxyScriptAdapterFetcher(context, task_runner),
         url_request_context_(context) {
   }
 
@@ -280,9 +293,12 @@
 
   FetcherClient client;
   TestURLRequestContext url_request_context;
+  scoped_refptr<base::TaskRunner> runner =
+      client.worker_pool_->GetTaskRunnerWithShutdownBehavior(
+          base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
   client.fetcher_.reset(
       new MockDhcpRealFetchProxyScriptAdapterFetcher(
-          &url_request_context));
+          &url_request_context, runner));
   client.fetcher_->configured_url_ = configured_url.spec();
   client.RunTest();
   client.WaitForResult(OK);
diff --git a/net/proxy/dhcp_proxy_script_fetcher_win.cc b/net/proxy/dhcp_proxy_script_fetcher_win.cc
index 9e34f51..ac28e0f 100644
--- a/net/proxy/dhcp_proxy_script_fetcher_win.cc
+++ b/net/proxy/dhcp_proxy_script_fetcher_win.cc
@@ -7,8 +7,8 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/metrics/histogram.h"
-#include "base/perftimer.h"
-#include "base/threading/worker_pool.h"
+#include "base/test/perftimer.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "net/base/net_errors.h"
 #include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h"
 
@@ -18,6 +18,27 @@
 
 namespace {
 
+// How many threads to use at maximum to do DHCP lookups. This is
+// chosen based on the following UMA data:
+// - When OnWaitTimer fires, ~99.8% of users have 6 or fewer network
+//   adapters enabled for DHCP in total.
+// - At the same measurement point, ~99.7% of users have 3 or fewer pending
+//   DHCP adapter lookups.
+// - There is however a very long and thin tail of users who have
+//   systems reporting up to 100+ adapters (this must be some very weird
+//   OS bug (?), probably the cause of http://crbug.com/240034).
+//
+// The maximum number of threads is chosen such that even systems that
+// report a huge number of network adapters should not run out of
+// memory from this number of threads, while giving a good chance of
+// getting back results for any responsive adapters.
+//
+// The ~99.8% of systems that have 6 or fewer network adapters will
+// not grow the thread pool to its maximum size (rather, they will
+// grow it to 6 or fewer threads) so setting the limit lower would not
+// improve performance or memory usage on those systems.
+const int kMaxDhcpLookupThreads = 12;
+
 // How long to wait at maximum after we get results (a PAC file or
 // knowledge that no PAC file is configured) from whichever network
 // adapter finishes first.
@@ -42,11 +63,16 @@
       destination_string_(NULL),
       url_request_context_(url_request_context) {
   DCHECK(url_request_context_);
+
+  worker_pool_ = new base::SequencedWorkerPool(kMaxDhcpLookupThreads,
+                                               "PacDhcpLookup");
 }
 
 DhcpProxyScriptFetcherWin::~DhcpProxyScriptFetcherWin() {
   // Count as user-initiated if we are not yet in STATE_DONE.
   Cancel();
+
+  worker_pool_->Shutdown();
 }
 
 int DhcpProxyScriptFetcherWin::Fetch(base::string16* utf16_text,
@@ -64,7 +90,7 @@
   destination_string_ = utf16_text;
 
   last_query_ = ImplCreateAdapterQuery();
-  base::WorkerPool::PostTaskAndReply(
+  GetTaskRunner()->PostTaskAndReply(
       FROM_HERE,
       base::Bind(
           &DhcpProxyScriptFetcherWin::AdapterQuery::GetCandidateAdapterNames,
@@ -72,8 +98,7 @@
       base::Bind(
           &DhcpProxyScriptFetcherWin::OnGetCandidateAdapterNamesDone,
           AsWeakPtr(),
-          last_query_),
-      true);
+          last_query_));
 
   return ERR_IO_PENDING;
 }
@@ -267,9 +292,15 @@
   return url_request_context_;
 }
 
+scoped_refptr<base::TaskRunner> DhcpProxyScriptFetcherWin::GetTaskRunner() {
+  return worker_pool_->GetTaskRunnerWithShutdownBehavior(
+      base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
+}
+
 DhcpProxyScriptAdapterFetcher*
     DhcpProxyScriptFetcherWin::ImplCreateAdapterFetcher() {
-  return new DhcpProxyScriptAdapterFetcher(url_request_context_);
+  return new DhcpProxyScriptAdapterFetcher(url_request_context_,
+                                           GetTaskRunner());
 }
 
 DhcpProxyScriptFetcherWin::AdapterQuery*
diff --git a/net/proxy/dhcp_proxy_script_fetcher_win.h b/net/proxy/dhcp_proxy_script_fetcher_win.h
index 79fc4b3..d6f14f9 100644
--- a/net/proxy/dhcp_proxy_script_fetcher_win.h
+++ b/net/proxy/dhcp_proxy_script_fetcher_win.h
@@ -16,6 +16,10 @@
 #include "base/timer/timer.h"
 #include "net/proxy/dhcp_proxy_script_fetcher.h"
 
+namespace base {
+class SequencedWorkerPool;
+}
+
 namespace net {
 
 class DhcpProxyScriptAdapterFetcher;
@@ -50,6 +54,8 @@
 
   URLRequestContext* url_request_context() const;
 
+  scoped_refptr<base::TaskRunner> GetTaskRunner();
+
   // This inner class encapsulate work done on a worker pool thread.
   // The class calls GetCandidateAdapterNames, which can take a couple of
   // hundred milliseconds.
@@ -161,6 +167,9 @@
   // Time |Fetch()| was last called, 0 if never.
   base::TimeTicks fetch_start_time_;
 
+  // Worker pool we use for all DHCP lookup tasks.
+  scoped_refptr<base::SequencedWorkerPool> worker_pool_;
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(DhcpProxyScriptFetcherWin);
 };
 
diff --git a/net/proxy/dhcp_proxy_script_fetcher_win_unittest.cc b/net/proxy/dhcp_proxy_script_fetcher_win_unittest.cc
index 919787a..cf0cee0 100644
--- a/net/proxy/dhcp_proxy_script_fetcher_win_unittest.cc
+++ b/net/proxy/dhcp_proxy_script_fetcher_win_unittest.cc
@@ -9,8 +9,8 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/message_loop/message_loop.h"
-#include "base/perftimer.h"
 #include "base/rand_util.h"
+#include "base/test/perftimer.h"
 #include "base/test/test_timeouts.h"
 #include "base/threading/platform_thread.h"
 #include "net/base/completion_callback.h"
@@ -156,9 +156,10 @@
 class DelayingDhcpProxyScriptAdapterFetcher
     : public DhcpProxyScriptAdapterFetcher {
  public:
-  explicit DelayingDhcpProxyScriptAdapterFetcher(
-      URLRequestContext* url_request_context)
-      : DhcpProxyScriptAdapterFetcher(url_request_context) {
+  DelayingDhcpProxyScriptAdapterFetcher(
+      URLRequestContext* url_request_context,
+      scoped_refptr<base::TaskRunner> task_runner)
+      : DhcpProxyScriptAdapterFetcher(url_request_context, task_runner) {
   }
 
   class DelayingDhcpQuery : public DhcpQuery {
@@ -189,7 +190,8 @@
   }
 
   DhcpProxyScriptAdapterFetcher* ImplCreateAdapterFetcher() OVERRIDE {
-    return new DelayingDhcpProxyScriptAdapterFetcher(url_request_context());
+    return new DelayingDhcpProxyScriptAdapterFetcher(url_request_context(),
+                                                     GetTaskRunner());
   }
 };
 
@@ -212,8 +214,9 @@
 class DummyDhcpProxyScriptAdapterFetcher
     : public DhcpProxyScriptAdapterFetcher {
  public:
-  explicit DummyDhcpProxyScriptAdapterFetcher(URLRequestContext* context)
-      : DhcpProxyScriptAdapterFetcher(context),
+  DummyDhcpProxyScriptAdapterFetcher(URLRequestContext* context,
+                                     scoped_refptr<base::TaskRunner> runner)
+      : DhcpProxyScriptAdapterFetcher(context, runner),
         did_finish_(false),
         result_(OK),
         pac_script_(L"bingo"),
@@ -297,6 +300,8 @@
     ResetTestState();
   }
 
+  using DhcpProxyScriptFetcherWin::GetTaskRunner;
+
   // Adds a fetcher object to the queue of fetchers used by
   // |ImplCreateAdapterFetcher()|, and its name to the list of adapters
   // returned by ImplGetCandidateAdapterNames.
@@ -312,7 +317,8 @@
                                    base::string16 pac_script,
                                    base::TimeDelta fetch_delay) {
     scoped_ptr<DummyDhcpProxyScriptAdapterFetcher> adapter_fetcher(
-        new DummyDhcpProxyScriptAdapterFetcher(url_request_context()));
+        new DummyDhcpProxyScriptAdapterFetcher(url_request_context(),
+                                               GetTaskRunner()));
     adapter_fetcher->Configure(
         did_finish, result, pac_script, fetch_delay.InMilliseconds());
     PushBackAdapter(adapter_name, adapter_fetcher.release());
@@ -372,7 +378,7 @@
 };
 
 class FetcherClient {
-public:
+ public:
   FetcherClient()
       : context_(new TestURLRequestContext),
         fetcher_(context_.get()),
@@ -414,6 +420,10 @@
     fetcher_.ResetTestState();
   }
 
+  scoped_refptr<base::TaskRunner> GetTaskRunner() {
+    return fetcher_.GetTaskRunner();
+  }
+
   scoped_ptr<URLRequestContext> context_;
   MockDhcpProxyScriptFetcherWin fetcher_;
   bool finished_;
@@ -426,7 +436,8 @@
 void TestNormalCaseURLConfiguredOneAdapter(FetcherClient* client) {
   TestURLRequestContext context;
   scoped_ptr<DummyDhcpProxyScriptAdapterFetcher> adapter_fetcher(
-      new DummyDhcpProxyScriptAdapterFetcher(&context));
+      new DummyDhcpProxyScriptAdapterFetcher(&context,
+                                             client->GetTaskRunner()));
   adapter_fetcher->Configure(true, OK, L"bingo", 1);
   client->fetcher_.PushBackAdapter("a", adapter_fetcher.release());
   client->RunTest();
@@ -586,7 +597,8 @@
 void TestImmediateCancel(FetcherClient* client) {
   TestURLRequestContext context;
   scoped_ptr<DummyDhcpProxyScriptAdapterFetcher> adapter_fetcher(
-      new DummyDhcpProxyScriptAdapterFetcher(&context));
+      new DummyDhcpProxyScriptAdapterFetcher(&context,
+                                             client->GetTaskRunner()));
   adapter_fetcher->Configure(true, OK, L"bingo", 1);
   client->fetcher_.PushBackAdapter("a", adapter_fetcher.release());
   client->RunTest();
diff --git a/net/proxy/proxy_resolver_perftest.cc b/net/proxy/proxy_resolver_perftest.cc
index 3faf396..bb94ff0 100644
--- a/net/proxy/proxy_resolver_perftest.cc
+++ b/net/proxy/proxy_resolver_perftest.cc
@@ -6,8 +6,8 @@
 #include "base/compiler_specific.h"
 #include "base/file_util.h"
 #include "base/path_service.h"
-#include "base/perftimer.h"
 #include "base/strings/string_util.h"
+#include "base/test/perftimer.h"
 #include "net/base/net_errors.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/proxy/proxy_info.h"
diff --git a/net/quic/crypto/aes_128_gcm_12_encrypter.h b/net/quic/crypto/aes_128_gcm_12_encrypter.h
index 451f84d..ca9a2b1 100644
--- a/net/quic/crypto/aes_128_gcm_12_encrypter.h
+++ b/net/quic/crypto/aes_128_gcm_12_encrypter.h
@@ -61,8 +61,6 @@
   unsigned char key_[16];
   // The nonce prefix.
   unsigned char nonce_prefix_[4];
-  // last_seq_num_ is the last sequence number observed.
-  QuicPacketSequenceNumber last_seq_num_;
 
 #if defined(USE_OPENSSL)
   ScopedEVPCipherCtx ctx_;
diff --git a/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc b/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc
index 1cd3540..ae6adab 100644
--- a/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc
+++ b/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc
@@ -250,7 +250,7 @@
 
 }  // namespace
 
-Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() : last_seq_num_(0) {
+Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() {
   ignore_result(g_gcm_support_checker.Get());
 }
 
@@ -350,12 +350,8 @@
   size_t ciphertext_size = GetCiphertextSize(plaintext.length());
   scoped_ptr<char[]> ciphertext(new char[ciphertext_size]);
 
-  if (last_seq_num_ != 0 && sequence_number <= last_seq_num_) {
-    DLOG(FATAL) << "Sequence numbers regressed";
-    return NULL;
-  }
-  last_seq_num_ = sequence_number;
-
+  // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the
+  // same sequence number twice.
   uint8 nonce[kNoncePrefixSize + sizeof(sequence_number)];
   COMPILE_ASSERT(sizeof(nonce) == kAESNonceSize, bad_sequence_number_size);
   memcpy(nonce, nonce_prefix_, kNoncePrefixSize);
diff --git a/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc b/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc
index 79d0ec1..166fd55 100644
--- a/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc
+++ b/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc
@@ -21,7 +21,7 @@
 
 }  // namespace
 
-Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() : last_seq_num_(0) {}
+Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() {}
 
 Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {}
 
@@ -118,12 +118,8 @@
   size_t ciphertext_size = GetCiphertextSize(plaintext.length());
   scoped_ptr<char[]> ciphertext(new char[ciphertext_size]);
 
-  if (last_seq_num_ != 0 && sequence_number <= last_seq_num_) {
-    DLOG(FATAL) << "Sequence numbers regressed";
-    return NULL;
-  }
-  last_seq_num_ = sequence_number;
-
+  // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the
+  // same sequence number twice.
   uint8 nonce[kNoncePrefixSize + sizeof(sequence_number)];
   COMPILE_ASSERT(sizeof(nonce) == kAESNonceSize, bad_sequence_number_size);
   memcpy(nonce, nonce_prefix_, kNoncePrefixSize);
diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc
index 1a30f43..5e5ef3c 100644
--- a/net/quic/crypto/crypto_handshake.cc
+++ b/net/quic/crypto/crypto_handshake.cc
@@ -758,6 +758,10 @@
         0 /* sequence number */,
         StringPiece() /* associated data */,
         cetv_plaintext.AsStringPiece()));
+    if (!cetv_ciphertext.get()) {
+      *error_details = "Packet encryption failed";
+      return QUIC_ENCRYPTION_FAILURE;
+    }
 
     out->SetStringPiece(kCETV, cetv_ciphertext->AsStringPiece());
     out->MarkDirty();
diff --git a/net/quic/quic_client_session.cc b/net/quic/quic_client_session.cc
index ac51eab..0fde5b9 100644
--- a/net/quic/quic_client_session.cc
+++ b/net/quic/quic_client_session.cc
@@ -238,7 +238,16 @@
 
 void QuicClientSession::CloseStream(QuicStreamId stream_id) {
   QuicSession::CloseStream(stream_id);
+  OnClosedStream();
+}
 
+void QuicClientSession::SendRstStream(QuicStreamId id,
+                                      QuicRstStreamErrorCode error) {
+  QuicSession::SendRstStream(id, error);
+  OnClosedStream();
+}
+
+void QuicClientSession::OnClosedStream() {
   if (GetNumOpenStreams() < get_max_open_streams() &&
       !stream_requests_.empty() &&
       crypto_stream_->encryption_established() &&
@@ -285,6 +294,13 @@
     UMA_HISTOGRAM_SPARSE_SLOWLY(
         "Net.QuicSession.ConnectionCloseErrorCodeClient", error);
   }
+
+  if (error == QUIC_CONNECTION_TIMED_OUT) {
+    UMA_HISTOGRAM_SPARSE_SLOWLY(
+        "Net.QuicSession.ConnectionClose.NumOpenStreams.TimedOut",
+        GetNumOpenStreams());
+  }
+
   UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.QuicVersion",
                               connection()->version());
   if (!callback_.is_null()) {
diff --git a/net/quic/quic_client_session.h b/net/quic/quic_client_session.h
index bdfaf2f..9c5b705 100644
--- a/net/quic/quic_client_session.h
+++ b/net/quic/quic_client_session.h
@@ -102,6 +102,8 @@
   virtual QuicReliableClientStream* CreateOutgoingReliableStream() OVERRIDE;
   virtual QuicCryptoClientStream* GetCryptoStream() OVERRIDE;
   virtual void CloseStream(QuicStreamId stream_id) OVERRIDE;
+  virtual void SendRstStream(QuicStreamId id,
+                             QuicRstStreamErrorCode error) OVERRIDE;
   virtual void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) OVERRIDE;
   virtual void OnCryptoHandshakeMessageSent(
       const CryptoHandshakeMessage& message) OVERRIDE;
@@ -144,6 +146,8 @@
   // A completion callback invoked when a read completes.
   void OnReadComplete(int result);
 
+  void OnClosedStream();
+
   void CloseSessionOnErrorInner(int error);
 
   // Posts a task to notify the factory that this session has been closed.
diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc
index 0f3caf9..6e329e0 100644
--- a/net/quic/quic_connection.cc
+++ b/net/quic/quic_connection.cc
@@ -814,6 +814,7 @@
 
 void QuicConnection::SendRstStream(QuicStreamId id,
                                    QuicRstStreamErrorCode error) {
+  LOG(INFO) << "Sending RST_STREAM: " << id << " code: " << error;
   packet_generator_.AddControlFrame(
       QuicFrame(new QuicRstStreamFrame(id, error)));
 }
@@ -945,14 +946,8 @@
 bool QuicConnection::WriteQueuedPackets() {
   DCHECK(!write_blocked_);
 
-  size_t num_queued_packets = queued_packets_.size() + 1;
   QueuedPacketList::iterator packet_iterator = queued_packets_.begin();
   while (!write_blocked_ && packet_iterator != queued_packets_.end()) {
-    // Ensure that from one iteration of this loop to the next we
-    // succeeded in sending a packet so we don't infinitely loop.
-    // TODO(rch): clean up and close the connection if we really hit this.
-    DCHECK_LT(queued_packets_.size(), num_queued_packets);
-    num_queued_packets = queued_packets_.size();
     if (WritePacket(packet_iterator->encryption_level,
                     packet_iterator->sequence_number,
                     packet_iterator->packet,
@@ -1213,6 +1208,12 @@
 
   scoped_ptr<QuicEncryptedPacket> encrypted(
       framer_.EncryptPacket(level, sequence_number, *packet));
+  if (encrypted.get() == NULL) {
+    LOG(DFATAL) << ENDPOINT << "Failed to encrypt packet number "
+                << sequence_number;
+    CloseConnection(QUIC_ENCRYPTION_FAILURE, false);
+    return false;
+  }
   DLOG(INFO) << ENDPOINT << "Sending packet number " << sequence_number
              << " : " << (packet->is_fec_packet() ? "FEC " :
                  (retransmittable == HAS_RETRANSMITTABLE_DATA
diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h
index ef90d23..c2b0712 100644
--- a/net/quic/quic_connection.h
+++ b/net/quic/quic_connection.h
@@ -385,6 +385,8 @@
   const QuicDecrypter* decrypter() const;
   const QuicDecrypter* alternative_decrypter() const;
 
+  bool is_server() const { return is_server_; }
+
  protected:
   // Send a packet to the peer using encryption |level|. If |sequence_number|
   // is present in the |retransmission_map_|, then contents of this packet will
diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc
index 32d0fe7..7e364de 100644
--- a/net/quic/quic_framer_test.cc
+++ b/net/quic/quic_framer_test.cc
@@ -425,6 +425,20 @@
         << " wire_sequence_number: " << wire_sequence_number;
   }
 
+  char LastCharOfVersion() {
+    switch (GetParam()) {
+      case QUIC_VERSION_7:
+        return '7';
+      case QUIC_VERSION_8:
+        return '8';
+      case QUIC_VERSION_9:
+        return '9';
+      default:
+        CHECK(0) << "Invalid version";
+        return 0;
+    }
+  }
+
   test::TestEncrypter* encrypter_;
   test::TestDecrypter* decrypter_;
   QuicVersion version_;
@@ -800,7 +814,7 @@
     0x10, 0x32, 0x54, 0x76,
     0x98, 0xBA, 0xDC, 0xFE,
     // version tag
-    'Q', '0', '0', (GetParam() == QUIC_VERSION_7 ? '7' : '8'),
+    'Q', '0', '0', LastCharOfVersion(),
     // packet sequence number
     0xBC, 0x9A, 0x78, 0x56,
     0x34, 0x12,
@@ -1058,7 +1072,7 @@
     0x10, 0x32, 0x54, 0x76,
     0x98, 0xBA, 0xDC, 0xFE,
     // version tag
-    'Q', '0', '0', (GetParam() == QUIC_VERSION_7 ? '7' : '8'),
+    'Q', '0', '0', LastCharOfVersion(),
     // packet sequence number
     0xBC, 0x9A, 0x78, 0x56,
     0x34, 0x12,
@@ -1420,7 +1434,7 @@
     0x10, 0x32, 0x54, 0x76,
     0x98, 0xBA, 0xDC, 0xFE,
     // version tag
-    'Q', '0', '0', (GetParam() == QUIC_VERSION_7 ? '7' : '8'),
+    'Q', '0', '0', LastCharOfVersion(),
     // packet sequence number
     0xBC, 0x9A, 0x78, 0x56,
     0x34, 0x12,
@@ -2205,7 +2219,7 @@
     0x10, 0x32, 0x54, 0x76,
     0x98, 0xBA, 0xDC, 0xFE,
     // version tag
-    'Q', '0', '0', (GetParam() == QUIC_VERSION_7 ? '7' : '8'),
+    'Q', '0', '0', LastCharOfVersion(),
     'Q', '2', '.', '0',
   };
 
@@ -2529,7 +2543,7 @@
     0x10, 0x32, 0x54, 0x76,
     0x98, 0xBA, 0xDC, 0xFE,
     // version tag
-    'Q', '0', '0', (GetParam() == QUIC_VERSION_7 ? '7' : '8'),
+    'Q', '0', '0', LastCharOfVersion(),
     // packet sequence number
     0xBC, 0x9A, 0x78, 0x56,
     0x34, 0x12,
@@ -2572,7 +2586,7 @@
     0x10, 0x32, 0x54, 0x76,
     0x98, 0xBA, 0xDC, 0xFE,
     // version tag
-    'Q', '0', '0', (GetParam() == QUIC_VERSION_7 ? '7' : '8')
+    'Q', '0', '0', LastCharOfVersion(),
   };
 
   QuicVersionVector versions;
diff --git a/net/quic/quic_http_stream.cc b/net/quic/quic_http_stream.cc
index 7358124..85af67a 100644
--- a/net/quic/quic_http_stream.cc
+++ b/net/quic/quic_http_stream.cc
@@ -86,7 +86,11 @@
   SpdyHeaderBlock headers;
   CreateSpdyHeadersFromHttpRequest(*request_info_, request_headers,
                                    &headers, 3, /*direct=*/true);
-  request_ = stream_->compressor()->CompressHeaders(headers);
+  if (session_->connection()->version() < QUIC_VERSION_9) {
+    request_ = stream_->compressor()->CompressHeaders(headers);
+  } else {
+    request_ = stream_->compressor()->CompressHeadersWithPriority(0, headers);
+  }
   // Log the actual request with the URL Request's net log.
   stream_net_log_.AddEvent(
       NetLog::TYPE_HTTP_TRANSACTION_SPDY_SEND_REQUEST_HEADERS,
@@ -201,7 +205,9 @@
   // Note: the not_reusable flag has no meaning for SPDY streams.
   if (stream_) {
     stream_->SetDelegate(NULL);
-    stream_->Close(QUIC_STREAM_NO_ERROR);
+    // TODO(rch): use new CANCELLED error code here once quic 11
+    // is everywhere.
+    stream_->Close(QUIC_SERVER_ERROR_PROCESSING_STREAM);
     stream_ = NULL;
   }
 }
diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc
index 7d107cb..d59ce70 100644
--- a/net/quic/quic_http_stream_test.cc
+++ b/net/quic/quic_http_stream_test.cc
@@ -34,6 +34,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using testing::_;
+using testing::AnyNumber;
+using testing::Return;
 
 namespace net {
 namespace test {
@@ -168,14 +170,17 @@
     runner_ = new TestTaskRunner(&clock_);
     send_algorithm_ = new MockSendAlgorithm();
     receive_algorithm_ = new TestReceiveAlgorithm(NULL);
+    EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _)).
+        Times(AnyNumber());
+    EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(AnyNumber());
     EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly(
-        testing::Return(QuicTime::Delta::Zero()));
+        Return(QuicTime::Delta::Zero()));
     EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _, _)).
-        WillRepeatedly(testing::Return(QuicTime::Delta::Zero()));
+        WillRepeatedly(Return(QuicTime::Delta::Zero()));
     EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillRepeatedly(
-        testing::Return(QuicTime::Delta::Zero()));
+        Return(QuicTime::Delta::Zero()));
     EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillRepeatedly(
-        testing::Return(QuicBandwidth::Zero()));
+        Return(QuicBandwidth::Zero()));
     helper_ = new QuicConnectionHelper(runner_.get(), &clock_,
                                        &random_generator_, socket);
     connection_ = new TestQuicConnection(guid_, peer_addr_, helper_);
@@ -203,7 +208,7 @@
     headers[":path"] = path;
     headers[":scheme"] = "http";
     headers[":version"] = "HTTP/1.1";
-    request_data_ = SerializeHeaderBlock(headers);
+    request_data_ = SerializeHeaderBlock(headers, true);
   }
 
   void SetResponseString(const std::string& status, const std::string& body) {
@@ -211,15 +216,19 @@
     headers[":status"] = status;
     headers[":version"] = "HTTP/1.1";
     headers["content-type"] = "text/plain";
-    response_data_ = SerializeHeaderBlock(headers) + body;
+    response_data_ = SerializeHeaderBlock(headers, false) + body;
   }
 
-  std::string SerializeHeaderBlock(const SpdyHeaderBlock& headers) {
+  std::string SerializeHeaderBlock(const SpdyHeaderBlock& headers,
+                                   bool write_priority) {
     QuicSpdyCompressor compressor;
+    if (framer_.version() >= QUIC_VERSION_9 && write_priority) {
+      return compressor.CompressHeadersWithPriority(0, headers);
+    }
     return compressor.CompressHeaders(headers);
   }
 
-  // Returns a newly created packet to send kData on stream 1.
+  // Returns a newly created packet to send kData on stream 3.
   QuicEncryptedPacket* ConstructDataPacket(
       QuicPacketSequenceNumber sequence_number,
       bool should_include_version,
@@ -231,6 +240,14 @@
     return ConstructPacket(header_, QuicFrame(&frame));
   }
 
+  // Returns a newly created packet to RST_STREAM stream 3.
+  QuicEncryptedPacket* ConstructRstStreamPacket(
+      QuicPacketSequenceNumber sequence_number) {
+    InitializeHeader(sequence_number, false);
+    QuicRstStreamFrame frame(3, QUIC_SERVER_ERROR_PROCESSING_STREAM);
+    return ConstructPacket(header_, QuicFrame(&frame));
+  }
+
   // Returns a newly created packet to send ack data.
   QuicEncryptedPacket* ConstructAckPacket(
       QuicPacketSequenceNumber sequence_number,
@@ -533,6 +550,7 @@
 TEST_F(QuicHttpStreamTest, DestroyedEarly) {
   SetRequestString("GET", "/");
   AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, request_data_));
+  AddWrite(SYNCHRONOUS, ConstructRstStreamPacket(2));
   use_closing_stream_ = true;
   Initialize();
 
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index 9d762cc..801c7ef 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -177,6 +177,9 @@
 
   std::string SerializeHeaderBlock(const SpdyHeaderBlock& headers) {
     QuicSpdyCompressor compressor;
+    if (QuicVersionMax() >= QUIC_VERSION_9) {
+      return compressor.CompressHeadersWithPriority(0, headers);
+    }
     return compressor.CompressHeaders(headers);
   }
 
diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc
index 0a4b1b0..3134542 100644
--- a/net/quic/quic_protocol.cc
+++ b/net/quic/quic_protocol.cc
@@ -124,6 +124,8 @@
       return MakeQuicTag('Q', '0', '0', '7');
     case QUIC_VERSION_8:
       return MakeQuicTag('Q', '0', '0', '8');
+    case QUIC_VERSION_9:
+      return MakeQuicTag('Q', '0', '0', '9');
     default:
       // This shold be an ERROR because we should never attempt to convert an
       // invalid QuicVersion to be written to the wire.
@@ -135,11 +137,14 @@
 QuicVersion QuicTagToQuicVersion(const QuicTag version_tag) {
   const QuicTag quic_tag_v7 = MakeQuicTag('Q', '0', '0', '7');
   const QuicTag quic_tag_v8 = MakeQuicTag('Q', '0', '0', '8');
+  const QuicTag quic_tag_v9 = MakeQuicTag('Q', '0', '0', '9');
 
   if (version_tag == quic_tag_v7) {
     return QUIC_VERSION_7;
   } else if (version_tag == quic_tag_v8) {
     return QUIC_VERSION_8;
+  }  else if (version_tag == quic_tag_v9) {
+    return QUIC_VERSION_9;
   } else {
     // Reading from the client so this should not be considered an ERROR.
     DLOG(INFO) << "Unsupported QuicTag version: "
@@ -156,6 +161,7 @@
   switch (version) {
     RETURN_STRING_LITERAL(QUIC_VERSION_7);
     RETURN_STRING_LITERAL(QUIC_VERSION_8);
+    RETURN_STRING_LITERAL(QUIC_VERSION_9);
     default:
       return "QUIC_VERSION_UNSUPPORTED";
   }
diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h
index 39105a3..db902a0 100644
--- a/net/quic/quic_protocol.h
+++ b/net/quic/quic_protocol.h
@@ -41,6 +41,7 @@
 // QuicTag is the type of a tag in the wire protocol.
 typedef uint32 QuicTag;
 typedef std::vector<QuicTag> QuicTagVector;
+typedef uint32 QuicPriority;
 
 // TODO(rch): Consider Quic specific names for these constants.
 // Maximum size in bytes of a QUIC packet.
@@ -188,7 +189,8 @@
   QUIC_VERSION_UNSUPPORTED = 0,
 
   QUIC_VERSION_7 = 7,
-  QUIC_VERSION_8 = 8,  // Current version.
+  QUIC_VERSION_8 = 8,
+  QUIC_VERSION_9 = 9,  // Current version.
 };
 
 // This vector contains QUIC versions which we currently support.
@@ -196,7 +198,7 @@
 // element, with subsequent elements in descending order (versions can be
 // skipped as necessary).
 static const QuicVersion kSupportedQuicVersions[] =
-    {QUIC_VERSION_8, QUIC_VERSION_7};
+    {QUIC_VERSION_9, QUIC_VERSION_8, QUIC_VERSION_7};
 
 typedef std::vector<QuicVersion> QuicVersionVector;
 
@@ -216,6 +218,10 @@
 // Returns QUIC_VERSION_UNSUPPORTED if version_tag cannot be understood.
 NET_EXPORT_PRIVATE QuicVersion QuicTagToQuicVersion(const QuicTag version_tag);
 
+// Returns the appropriate QuicTag for a properly formed version string
+// (e.g. Q008).
+NET_EXPORT_PRIVATE QuicTag StringToQuicTag(std::string version);
+
 // Helper function which translates from a QuicVersion to a string.
 // Returns strings corresponding to enum names (e.g. QUIC_VERSION_6).
 NET_EXPORT_PRIVATE std::string QuicVersionToString(const QuicVersion version);
@@ -273,6 +279,8 @@
   QUIC_STREAM_CONNECTION_ERROR,
   // GoAway frame sent. No more stream can be created.
   QUIC_STREAM_PEER_GOING_AWAY,
+  // The stream has been cancelled.
+  QUIC_STREAM_CANCELLED,
 
   // No error. Used as bound while iterating.
   QUIC_STREAM_LAST_ERROR,
diff --git a/net/quic/quic_protocol_test.cc b/net/quic/quic_protocol_test.cc
index b073d85..4144aee 100644
--- a/net/quic/quic_protocol_test.cc
+++ b/net/quic/quic_protocol_test.cc
@@ -133,10 +133,11 @@
             QuicVersionToString(QUIC_VERSION_UNSUPPORTED));
 
   QuicVersion single_version[] = {QUIC_VERSION_7};
-  EXPECT_EQ("QUIC_VERSION_7,", QuicVersionArrayToString(single_version,
-                                   arraysize(single_version)));
-  QuicVersion multiple_versions[] = {QUIC_VERSION_8, QUIC_VERSION_7};
-  EXPECT_EQ("QUIC_VERSION_8,QUIC_VERSION_7,",
+  EXPECT_EQ("QUIC_VERSION_7,", QuicVersionArrayToString(
+                                   single_version, arraysize(single_version)));
+  QuicVersion multiple_versions[] =
+      {QUIC_VERSION_9, QUIC_VERSION_8, QUIC_VERSION_7};
+  EXPECT_EQ("QUIC_VERSION_9,QUIC_VERSION_8,QUIC_VERSION_7,",
             QuicVersionArrayToString(multiple_versions,
                                      arraysize(multiple_versions)));
 }
diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc
index aa83ab0..8f6403f 100644
--- a/net/quic/quic_session.cc
+++ b/net/quic/quic_session.cc
@@ -18,6 +18,7 @@
 namespace net {
 
 const size_t kMaxPrematurelyClosedStreamsTracked = 20;
+const size_t kMaxZombieStreams = 20;
 
 #define ENDPOINT (is_server_ ? "Server: " : " Client: ")
 
@@ -133,9 +134,19 @@
   }
 
   for (size_t i = 0; i < frames.size(); ++i) {
-    ReliableQuicStream* stream = GetStream(frames[i].stream_id);
-    if (stream) {
-      stream->OnStreamFrame(frames[i]);
+    QuicStreamId stream_id = frames[i].stream_id;
+    ReliableQuicStream* stream = GetStream(stream_id);
+    if (!stream) {
+      continue;
+    }
+    stream->OnStreamFrame(frames[i]);
+
+    // If the stream had been prematurely closed, and the
+    // headers are now decompressed, then we are finally finished
+    // with this stream.
+    if (ContainsKey(zombie_streams_, stream_id) &&
+        stream->headers_decompressed()) {
+      CloseZombieStream(stream_id);
     }
   }
 
@@ -162,6 +173,16 @@
   if (!stream) {
     return;  // Errors are handled by GetStream.
   }
+  if (ContainsKey(zombie_streams_, stream->id())) {
+    // If this was a zombie stream then we close it out now.
+    CloseZombieStream(stream->id());
+    // However, since the headers still have not been decompressed, we want to
+    // mark it a prematurely closed so that if we ever receive frames
+    // for this stream we can close the connection.
+    DCHECK(!stream->headers_decompressed());
+    AddPrematurelyClosedStream(frame.stream_id);
+    return;
+  }
   stream->OnStreamReset(frame.error_code);
 }
 
@@ -171,6 +192,7 @@
 }
 
 void QuicSession::ConnectionClose(QuicErrorCode error, bool from_peer) {
+  DCHECK(!connection_->connected());
   if (error_ == QUIC_NO_ERROR) {
     error_ = error;
   }
@@ -221,7 +243,7 @@
 void QuicSession::SendRstStream(QuicStreamId id,
                                 QuicRstStreamErrorCode error) {
   connection_->SendRstStream(id, error);
-  CloseStream(id);
+  CloseStreamInner(id, true);
 }
 
 void QuicSession::SendGoAway(QuicErrorCode error_code, const string& reason) {
@@ -230,6 +252,11 @@
 }
 
 void QuicSession::CloseStream(QuicStreamId stream_id) {
+  CloseStreamInner(stream_id, false);
+}
+
+void QuicSession::CloseStreamInner(QuicStreamId stream_id,
+                                   bool locally_reset) {
   DLOG(INFO) << ENDPOINT << "Closing stream " << stream_id;
 
   ReliableStreamMap::iterator it = stream_map_.find(stream_id);
@@ -238,18 +265,62 @@
     return;
   }
   ReliableQuicStream* stream = it->second;
-  if (!stream->headers_decompressed()) {
-    if (prematurely_closed_streams_.size() ==
-        kMaxPrematurelyClosedStreamsTracked) {
-      prematurely_closed_streams_.erase(prematurely_closed_streams_.begin());
+  if (connection_->connected() && !stream->headers_decompressed()) {
+    // If the stream is being closed locally (for example a client cancelling
+    // a request before receiving the response) then we need to make sure that
+    // we keep the stream alive long enough to process any response or
+    // RST_STREAM frames.
+    if (locally_reset && !is_server_) {
+      AddZombieStream(stream_id);
+      return;
     }
-    prematurely_closed_streams_.insert(make_pair(stream->id(), true));
+
+    // This stream has been closed before the headers were decompressed.
+    // This might cause problems with head of line blocking of headers.
+    // If the peer sent headers which were lost but we now close the stream
+    // we will never be able to decompress headers for other streams.
+    // To deal with this, we keep track of streams which have been closed
+    // prematurely.  If we ever receive data frames for this steam, then we
+    // know there actually has been a problem and we close the connection.
+    AddPrematurelyClosedStream(stream->id());
   }
   closed_streams_.push_back(it->second);
   stream_map_.erase(it);
   stream->OnClose();
 }
 
+void QuicSession::AddZombieStream(QuicStreamId stream_id) {
+  if (zombie_streams_.size() == kMaxZombieStreams) {
+    QuicStreamId oldest_zombie_stream_id = zombie_streams_.begin()->first;
+    CloseZombieStream(oldest_zombie_stream_id);
+    // However, since the headers still have not been decompressed, we want to
+    // mark it a prematurely closed so that if we ever receive frames
+    // for this stream we can close the connection.
+    AddPrematurelyClosedStream(oldest_zombie_stream_id);
+  }
+  zombie_streams_.insert(make_pair(stream_id, true));
+}
+
+void QuicSession::CloseZombieStream(QuicStreamId stream_id) {
+  DCHECK(ContainsKey(zombie_streams_, stream_id));
+  zombie_streams_.erase(stream_id);
+  ReliableQuicStream* stream = GetStream(stream_id);
+  if (!stream) {
+    return;
+  }
+  stream_map_.erase(stream_id);
+  stream->OnClose();
+  closed_streams_.push_back(stream);
+}
+
+void QuicSession::AddPrematurelyClosedStream(QuicStreamId stream_id) {
+  if (prematurely_closed_streams_.size() ==
+      kMaxPrematurelyClosedStreamsTracked) {
+    prematurely_closed_streams_.erase(prematurely_closed_streams_.begin());
+  }
+  prematurely_closed_streams_.insert(make_pair(stream_id, true));
+}
+
 bool QuicSession::IsEncryptionEstablished() {
   return GetCryptoStream()->encryption_established();
 }
@@ -376,7 +447,10 @@
   if (id == kCryptoStreamId) {
     return false;
   }
-  if (stream_map_.count(id) != 0) {
+  if (ContainsKey(zombie_streams_, id)) {
+    return true;
+  }
+  if (ContainsKey(stream_map_, id)) {
     // Stream is active
     return false;
   }
@@ -392,7 +466,8 @@
 }
 
 size_t QuicSession::GetNumOpenStreams() const {
-  return stream_map_.size() + implicitly_created_streams_.size();
+  return stream_map_.size() + implicitly_created_streams_.size() -
+      zombie_streams_.size();
 }
 
 void QuicSession::MarkWriteBlocked(QuicStreamId id) {
diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h
index 75c3ea0..c37b6e1 100644
--- a/net/quic/quic_session.h
+++ b/net/quic/quic_session.h
@@ -211,6 +211,24 @@
 
   typedef base::hash_map<QuicStreamId, ReliableQuicStream*> ReliableStreamMap;
 
+  // Performs the work required to close |stream_id|.  If |locally_reset|
+  // then the stream has been reset by this endpoint, not by the peer.  This
+  // means the stream may become a zombie stream which needs to stay
+  // around until headers have been decompressed.
+  void CloseStreamInner(QuicStreamId stream_id, bool locally_reset);
+
+  // Adds |stream_id| to the zobmie stream map, closing the oldest
+  // zombie stream if the set is full.
+  void AddZombieStream(QuicStreamId stream_id);
+
+  // Closes the zombie stream |stream_id| and removes it from the zombie
+  // stream map.
+  void CloseZombieStream(QuicStreamId stream_id);
+
+  // Adds |stream_id| to the prematurely closed stream map, removing the
+  // oldest prematurely closed stream if the set is full.
+  void AddPrematurelyClosedStream(QuicStreamId stream_id);
+
   scoped_ptr<QuicConnection> connection_;
 
   // Tracks the last 20 streams which closed without decompressing headers.
@@ -218,6 +236,12 @@
   // Ideally this would be a linked_hash_set as the boolean is unused.
   linked_hash_map<QuicStreamId, bool> prematurely_closed_streams_;
 
+  // Streams which have been locally reset before decompressing headers
+  // from the peer.  These streams need to stay open long enough to
+  // process any headers from the peer.
+  // Ideally this would be a linked_hash_set as the boolean is unused.
+  linked_hash_map<QuicStreamId, bool> zombie_streams_;
+
   // A shim to stand between the connection and the session, to handle stream
   // deletions.
   scoped_ptr<VisitorShim> visitor_shim_;
diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc
index 4df7222..77234e1 100644
--- a/net/quic/quic_session_test.cc
+++ b/net/quic/quic_session_test.cc
@@ -13,6 +13,7 @@
 #include "net/quic/quic_protocol.h"
 #include "net/quic/test_tools/quic_connection_peer.h"
 #include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/reliable_quic_stream_peer.h"
 #include "net/spdy/spdy_framer.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -22,6 +23,7 @@
 using std::vector;
 using testing::_;
 using testing::InSequence;
+using testing::StrictMock;
 
 namespace net {
 namespace test {
@@ -105,6 +107,33 @@
       : guid_(1),
         connection_(new MockConnection(guid_, IPEndPoint(), false)),
         session_(connection_, true) {
+    headers_[":host"] = "www.google.com";
+    headers_[":path"] = "/index.hml";
+    headers_[":scheme"] = "http";
+    headers_["cookie"] =
+        "__utma=208381060.1228362404.1372200928.1372200928.1372200928.1; "
+        "__utmc=160408618; "
+        "GX=DQAAAOEAAACWJYdewdE9rIrW6qw3PtVi2-d729qaa-74KqOsM1NVQblK4VhX"
+        "hoALMsy6HOdDad2Sz0flUByv7etmo3mLMidGrBoljqO9hSVA40SLqpG_iuKKSHX"
+        "RW3Np4bq0F0SDGDNsW0DSmTS9ufMRrlpARJDS7qAI6M3bghqJp4eABKZiRqebHT"
+        "pMU-RXvTI5D5oCF1vYxYofH_l1Kviuiy3oQ1kS1enqWgbhJ2t61_SNdv-1XJIS0"
+        "O3YeHLmVCs62O6zp89QwakfAWK9d3IDQvVSJzCQsvxvNIvaZFa567MawWlXg0Rh"
+        "1zFMi5vzcns38-8_Sns; "
+        "GA=v*2%2Fmem*57968640*47239936%2Fmem*57968640*47114716%2Fno-nm-"
+        "yj*15%2Fno-cc-yj*5%2Fpc-ch*133685%2Fpc-s-cr*133947%2Fpc-s-t*1339"
+        "47%2Fno-nm-yj*4%2Fno-cc-yj*1%2Fceft-as*1%2Fceft-nqas*0%2Fad-ra-c"
+        "v_p%2Fad-nr-cv_p-f*1%2Fad-v-cv_p*859%2Fad-ns-cv_p-f*1%2Ffn-v-ad%"
+        "2Fpc-t*250%2Fpc-cm*461%2Fpc-s-cr*722%2Fpc-s-t*722%2Fau_p*4"
+        "SICAID=AJKiYcHdKgxum7KMXG0ei2t1-W4OD1uW-ecNsCqC0wDuAXiDGIcT_HA2o1"
+        "3Rs1UKCuBAF9g8rWNOFbxt8PSNSHFuIhOo2t6bJAVpCsMU5Laa6lewuTMYI8MzdQP"
+        "ARHKyW-koxuhMZHUnGBJAM1gJODe0cATO_KGoX4pbbFxxJ5IicRxOrWK_5rU3cdy6"
+        "edlR9FsEdH6iujMcHkbE5l18ehJDwTWmBKBzVD87naobhMMrF6VvnDGxQVGp9Ir_b"
+        "Rgj3RWUoPumQVCxtSOBdX0GlJOEcDTNCzQIm9BSfetog_eP_TfYubKudt5eMsXmN6"
+        "QnyXHeGeK2UINUzJ-D30AFcpqYgH9_1BvYSpi7fc7_ydBU8TaD8ZRxvtnzXqj0RfG"
+        "tuHghmv3aD-uzSYJ75XDdzKdizZ86IG6Fbn1XFhYZM-fbHhm3mVEXnyRW4ZuNOLFk"
+        "Fas6LMcVC6Q8QLlHYbXBpdNFuGbuZGUnav5C-2I_-46lL0NGg3GewxGKGHvHEfoyn"
+        "EFFlEYHsBQ98rXImL8ySDycdLEFvBPdtctPmWCfTxwmoSMLHU2SCVDhbqMWU5b0yr"
+        "JBCScs_ejbKaqBDoB7ZGxTvqlrB__2ZmnHHjCr8RgMRtKNtIeuZAo ";
   }
 
   void CheckClosedStreams() {
@@ -126,6 +155,7 @@
   MockConnection* connection_;
   TestSession session_;
   set<QuicStreamId> closed_streams_;
+  SpdyHeaderBlock headers_;
 };
 
 TEST_F(QuicSessionTest, PeerAddress) {
@@ -149,8 +179,10 @@
 TEST_F(QuicSessionTest, IsClosedStreamLocallyCreated) {
   TestStream* stream2 = session_.CreateOutgoingReliableStream();
   EXPECT_EQ(2u, stream2->id());
+  ReliableQuicStreamPeer::SetHeadersDecompressed(stream2, true);
   TestStream* stream4 = session_.CreateOutgoingReliableStream();
   EXPECT_EQ(4u, stream4->id());
+  ReliableQuicStreamPeer::SetHeadersDecompressed(stream4, true);
 
   CheckClosedStreams();
   CloseStream(4);
@@ -160,15 +192,18 @@
 }
 
 TEST_F(QuicSessionTest, IsClosedStreamPeerCreated) {
-  session_.GetIncomingReliableStream(3);
-  session_.GetIncomingReliableStream(5);
+  ReliableQuicStream* stream3 = session_.GetIncomingReliableStream(3);
+  ReliableQuicStreamPeer::SetHeadersDecompressed(stream3, true);
+  ReliableQuicStream* stream5 = session_.GetIncomingReliableStream(5);
+  ReliableQuicStreamPeer::SetHeadersDecompressed(stream5, true);
 
   CheckClosedStreams();
   CloseStream(3);
   CheckClosedStreams();
   CloseStream(5);
   // Create stream id 9, and implicitly 7
-  session_.GetIncomingReliableStream(9);
+  ReliableQuicStream* stream9 = session_.GetIncomingReliableStream(9);
+  ReliableQuicStreamPeer::SetHeadersDecompressed(stream9, true);
   CheckClosedStreams();
   // Close 9, but make sure 7 is still not closed
   CloseStream(9);
@@ -229,10 +264,6 @@
 // Regression test for http://crbug.com/248737
 TEST_F(QuicSessionTest, OutOfOrderHeaders) {
   QuicSpdyCompressor compressor;
-  SpdyHeaderBlock headers;
-  headers[":host"] = "www.google.com";
-  headers[":path"] = "/index.hml";
-  headers[":scheme"] = "http";
   vector<QuicStreamFrame> frames;
   QuicPacketHeader header;
   header.public_header.guid = session_.guid();
@@ -243,11 +274,11 @@
   stream4->CloseWriteSide();
 
   // Create frame with headers for stream2.
-  string compressed_headers1 = compressor.CompressHeaders(headers);
+  string compressed_headers1 = compressor.CompressHeaders(headers_);
   QuicStreamFrame frame1(stream2->id(), false, 0, compressed_headers1);
 
   // Create frame with headers for stream4.
-  string compressed_headers2 = compressor.CompressHeaders(headers);
+  string compressed_headers2 = compressor.CompressHeaders(headers_);
   QuicStreamFrame frame2(stream4->id(), true, 0, compressed_headers2);
 
   // Process the second frame first.  This will cause the headers to
@@ -260,7 +291,7 @@
   session_.OnPacket(IPEndPoint(), IPEndPoint(), header, frames);
 
   // Ensure that the streams actually close and we don't DCHECK.
-  session_.ConnectionClose(QUIC_CONNECTION_TIMED_OUT, true);
+  connection_->CloseConnection(QUIC_CONNECTION_TIMED_OUT, true);
 }
 
 TEST_F(QuicSessionTest, SendGoAway) {
@@ -284,6 +315,40 @@
             QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds());
 }
 
+TEST_F(QuicSessionTest, ZombieStream) {
+  StrictMock<MockConnection>* connection =
+      new StrictMock<MockConnection>(guid_, IPEndPoint(), false);
+  TestSession session(connection, /*is_server=*/ false);
+
+  TestStream* stream3 = session.CreateOutgoingReliableStream();
+  EXPECT_EQ(3u, stream3->id());
+  TestStream* stream5 = session.CreateOutgoingReliableStream();
+  EXPECT_EQ(5u, stream5->id());
+
+  // Reset the stream, but since the headers have not been decompressed
+  // it will become a zombie and will continue to process data
+  // until the headers are decompressed.
+  EXPECT_CALL(*connection, SendRstStream(3, QUIC_STREAM_CANCELLED));
+  session.SendRstStream(3, QUIC_STREAM_CANCELLED);
+
+  vector<QuicStreamFrame> frames;
+  QuicPacketHeader header;
+  header.public_header.guid = session_.guid();
+
+  // Create frame with headers for stream2.
+  QuicSpdyCompressor compressor;
+  string compressed_headers1 = compressor.CompressHeaders(headers_);
+  QuicStreamFrame frame1(stream3->id(), false, 0, compressed_headers1);
+
+  // Process the second frame first.  This will cause the headers to
+  // be queued up and processed after the first frame is processed.
+  frames.push_back(frame1);
+  EXPECT_FALSE(stream3->headers_decompressed());
+  session.OnPacket(IPEndPoint(), IPEndPoint(), header, frames);
+
+  EXPECT_TRUE(connection->connected());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace net
diff --git a/net/quic/quic_spdy_compressor.cc b/net/quic/quic_spdy_compressor.cc
index 7efd45c..6681493 100644
--- a/net/quic/quic_spdy_compressor.cc
+++ b/net/quic/quic_spdy_compressor.cc
@@ -20,8 +20,21 @@
 QuicSpdyCompressor::~QuicSpdyCompressor() {
 }
 
+string QuicSpdyCompressor::CompressHeadersWithPriority(
+    QuicPriority priority,
+    const SpdyHeaderBlock& headers) {
+  return CompressHeadersInternal(priority, headers, true);
+}
+
 string QuicSpdyCompressor::CompressHeaders(
     const SpdyHeaderBlock& headers) {
+  return CompressHeadersInternal(0, headers, false);
+}
+
+string QuicSpdyCompressor::CompressHeadersInternal(
+    QuicPriority priority,
+    const SpdyHeaderBlock& headers,
+    bool write_priority) {
   // TODO(rch): Modify the SpdyFramer to expose a
   // CreateCompressedHeaderBlock method, or some such.
   SpdyStreamId stream_id = 3;    // unused.
@@ -34,12 +47,19 @@
   string serialized = string(frame->data() + header_frame_prefix_len,
                              frame->size() - header_frame_prefix_len);
   uint32 serialized_len = serialized.length();
+  char priority_str[sizeof(priority)];
+  memcpy(&priority_str, &priority, sizeof(priority));
   char id_str[sizeof(header_sequence_id_)];
   memcpy(&id_str, &header_sequence_id_, sizeof(header_sequence_id_));
   char len_str[sizeof(serialized_len)];
   memcpy(&len_str, &serialized_len, sizeof(serialized_len));
   string compressed;
-  compressed.reserve(arraysize(id_str) + arraysize(len_str) + serialized_len);
+  int priority_len = write_priority ? arraysize(priority_str) : 0;
+  compressed.reserve(
+      priority_len + arraysize(id_str) + arraysize(len_str) + serialized_len);
+  if (write_priority) {
+    compressed.append(priority_str, arraysize(priority_str));
+  }
   compressed.append(id_str, arraysize(id_str));
   compressed.append(len_str, arraysize(len_str));
   compressed.append(serialized);
diff --git a/net/quic/quic_spdy_compressor.h b/net/quic/quic_spdy_compressor.h
index c88c47e..53a7060 100644
--- a/net/quic/quic_spdy_compressor.h
+++ b/net/quic/quic_spdy_compressor.h
@@ -24,9 +24,19 @@
   QuicSpdyCompressor();
   ~QuicSpdyCompressor();
 
+  // Returns a string comprised of [header_sequence_id, compressed_headers].
   std::string CompressHeaders(const SpdyHeaderBlock& headers);
 
+  // Returns a string comprised of
+  // [priority, header_sequence_id, compressed_headers]
+  std::string CompressHeadersWithPriority(QuicPriority priority,
+                                          const SpdyHeaderBlock& headers);
+
  private:
+  std::string CompressHeadersInternal(QuicPriority priority,
+                                      const SpdyHeaderBlock& headers,
+                                      bool write_priority);
+
   SpdyFramer spdy_framer_;
   QuicHeaderId header_sequence_id_;
 
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index 5c604ea..fb9d474 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -47,11 +47,12 @@
     header.public_header.reset_flag = false;
     header.public_header.version_flag = true;
     header.packet_sequence_number = num;
+    header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
     header.entropy_flag = false;
     header.fec_flag = false;
     header.fec_group = 0;
 
-    QuicRstStreamFrame rst(stream_id, QUIC_STREAM_NO_ERROR);
+    QuicRstStreamFrame rst(stream_id, QUIC_SERVER_ERROR_PROCESSING_STREAM);
     return scoped_ptr<QuicEncryptedPacket>(
         ConstructPacket(header, QuicFrame(&rst)));
   }
@@ -173,7 +174,12 @@
   MockRead reads[] = {
     MockRead(ASYNC, OK, 0)  // EOF
   };
-  DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
+  scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket(1, 3));
+  MockWrite writes[] = {
+    MockWrite(ASYNC, rst->data(), rst->length(), 1),
+  };
+  DeterministicSocketData socket_data(reads, arraysize(reads),
+                                      writes, arraysize(writes));
   socket_factory_.AddSocketDataProvider(&socket_data);
   socket_data.StopAfter(1);
 
diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc
index 9c79a9b..b481969 100644
--- a/net/quic/quic_utils.cc
+++ b/net/quic/quic_utils.cc
@@ -124,6 +124,7 @@
     RETURN_STRING_LITERAL(QUIC_MULTIPLE_TERMINATION_OFFSETS);
     RETURN_STRING_LITERAL(QUIC_BAD_APPLICATION_PAYLOAD);
     RETURN_STRING_LITERAL(QUIC_STREAM_PEER_GOING_AWAY);
+    RETURN_STRING_LITERAL(QUIC_STREAM_CANCELLED);
     RETURN_STRING_LITERAL(QUIC_STREAM_LAST_ERROR);
   }
   // Return a default value so that we return this when |error| doesn't match
diff --git a/net/quic/reliable_quic_stream.cc b/net/quic/reliable_quic_stream.cc
index c2cb3f1..4bcf00a 100644
--- a/net/quic/reliable_quic_stream.cc
+++ b/net/quic/reliable_quic_stream.cc
@@ -12,6 +12,36 @@
 
 namespace net {
 
+namespace {
+
+// This is somewhat arbitrary.  It's possible, but unlikely, we will either fail
+// to set a priority client-side, or cancel a stream before stripping the
+// priority from the wire server-side.  In either case, start out with a
+// priority in the middle.
+QuicPriority kDefaultPriority = 3;
+
+// Appends bytes from data into partial_data_buffer.  Once partial_data_buffer
+// reaches 4 bytes, copies the data into 'result' and clears
+// partial_data_buffer.
+// Returns the number of bytes consumed.
+uint32 StripUint32(const char* data, uint32 data_len,
+                   string* partial_data_buffer,
+                   uint32* result) {
+  DCHECK_GT(4u, partial_data_buffer->length());
+  size_t missing_size = 4 - partial_data_buffer->length();
+  if (data_len < missing_size) {
+    StringPiece(data, data_len).AppendToString(partial_data_buffer);
+    return data_len;
+  }
+  StringPiece(data, missing_size).AppendToString(partial_data_buffer);
+  DCHECK_EQ(4u, partial_data_buffer->length());
+  memcpy(result, partial_data_buffer->data(), 4);
+  partial_data_buffer->clear();
+  return missing_size;
+}
+
+}  // namespace
+
 ReliableQuicStream::ReliableQuicStream(QuicStreamId id,
                                        QuicSession* session)
     : sequencer_(this),
@@ -21,12 +51,14 @@
       stream_bytes_read_(0),
       stream_bytes_written_(0),
       headers_decompressed_(false),
+      priority_(kDefaultPriority),
       headers_id_(0),
       decompression_failed_(false),
       stream_error_(QUIC_STREAM_NO_ERROR),
       connection_error_(QUIC_NO_ERROR),
       read_side_closed_(false),
       write_side_closed_(false),
+      priority_parsed_(false),
       fin_buffered_(false),
       fin_sent_(false) {
 }
@@ -245,28 +277,17 @@
     // The crypto stream does not use compression.
     return ProcessData(data, data_len);
   }
+
   uint32 total_bytes_consumed = 0;
   if (headers_id_ == 0u) {
-    // The headers ID has not yet been read.  Strip it from the beginning of
-    // the data stream.
-    DCHECK_GT(4u, headers_id_buffer_.length());
-    size_t missing_size = 4 - headers_id_buffer_.length();
-    if (data_len < missing_size) {
-      StringPiece(data, data_len).AppendToString(&headers_id_buffer_);
-      return data_len;
+    total_bytes_consumed += StripPriorityAndHeaderId(data, data_len);
+    data += total_bytes_consumed;
+    data_len -= total_bytes_consumed;
+    if (data_len == 0) {
+      return total_bytes_consumed;
     }
-    total_bytes_consumed += missing_size;
-    StringPiece(data, missing_size).AppendToString(&headers_id_buffer_);
-    DCHECK_EQ(4u, headers_id_buffer_.length());
-    memcpy(&headers_id_, headers_id_buffer_.data(), 4);
-    headers_id_buffer_.clear();
-    data += missing_size;
-    data_len -= missing_size;
   }
   DCHECK_NE(0u, headers_id_);
-  if (data_len == 0) {
-    return total_bytes_consumed;
-  }
 
   // Once the headers are finished, we simply pass the data through.
   if (headers_decompressed_) {
@@ -433,4 +454,29 @@
   }
 }
 
+uint32 ReliableQuicStream::StripPriorityAndHeaderId(
+    const char* data, uint32 data_len) {
+  uint32 total_bytes_parsed = 0;
+
+  if (!priority_parsed_ &&
+      session_->connection()->version() >= QUIC_VERSION_9 &&
+      session_->connection()->is_server()) {
+    total_bytes_parsed = StripUint32(
+        data, data_len, &headers_id_and_priority_buffer_, &priority_);
+    if (total_bytes_parsed > 0 && headers_id_and_priority_buffer_.size() == 0) {
+      // TODO(alyssar) check for priority out of bounds.
+      priority_parsed_ = true;
+    }
+    data += total_bytes_parsed;
+    data_len -= total_bytes_parsed;
+  }
+  if (data_len > 0 && headers_id_ == 0u) {
+    // The headers ID has not yet been read.  Strip it from the beginning of
+    // the data stream.
+    total_bytes_parsed += StripUint32(
+        data, data_len, &headers_id_and_priority_buffer_, &headers_id_);
+  }
+  return total_bytes_parsed;
+}
+
 }  // namespace net
diff --git a/net/quic/reliable_quic_stream.h b/net/quic/reliable_quic_stream.h
index 166ca61..993e66f 100644
--- a/net/quic/reliable_quic_stream.h
+++ b/net/quic/reliable_quic_stream.h
@@ -116,6 +116,7 @@
   bool GetSSLInfo(SSLInfo* ssl_info);
 
   bool headers_decompressed() const { return headers_decompressed_; }
+  QuicPriority priority() const { return priority_; }
 
  protected:
   // Returns a pair with the number of bytes consumed from data, and a boolean
@@ -152,6 +153,8 @@
   friend class test::ReliableQuicStreamPeer;
   friend class QuicStreamUtils;
 
+  uint32 StripPriorityAndHeaderId(const char* data, uint32 data_len);
+
   std::list<string> queued_data_;
 
   QuicStreamSequencer sequencer_;
@@ -165,11 +168,13 @@
   uint64 stream_bytes_written_;
   // True if the headers have been completely decompresssed.
   bool headers_decompressed_;
+  // The priority of the stream, once parsed.
+  QuicPriority priority_;
   // ID of the header block sent by the peer, once parsed.
   QuicHeaderId headers_id_;
-  // Buffer into which we write bytes from the headers_id_
-  // until it is fully parsed.
-  string headers_id_buffer_;
+  // Buffer into which we write bytes from priority_ and headers_id_
+  // until each is fully parsed.
+  string headers_id_and_priority_buffer_;
   // Contains a copy of the decompressed headers_ until they are consumed
   // via ProcessData or Readv.
   string decompressed_headers_;
@@ -189,6 +194,8 @@
   // True if the write side is closed, and further writes should fail.
   bool write_side_closed_;
 
+  // True if the priority has been read, false otherwise.
+  bool priority_parsed_;
   bool fin_buffered_;
   bool fin_sent_;
 };
diff --git a/net/quic/reliable_quic_stream_test.cc b/net/quic/reliable_quic_stream_test.cc
index e8ba52a..1df13cf 100644
--- a/net/quic/reliable_quic_stream_test.cc
+++ b/net/quic/reliable_quic_stream_test.cc
@@ -237,18 +237,21 @@
 TEST_F(ReliableQuicStreamTest, ProcessHeaders) {
   Initialize(kShouldProcessData);
 
-  string compressed_headers = compressor_->CompressHeaders(headers_);
+  string compressed_headers =
+      compressor_->CompressHeadersWithPriority(0, headers_);
   QuicStreamFrame frame(kStreamId, false, 0, compressed_headers);
 
   stream_->OnStreamFrame(frame);
   EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_), stream_->data());
+  EXPECT_EQ(0u, stream_->priority());
 }
 
 TEST_F(ReliableQuicStreamTest, ProcessHeadersWithInvalidHeaderId) {
   Initialize(kShouldProcessData);
 
-  string compressed_headers = compressor_->CompressHeaders(headers_);
-  compressed_headers.replace(0, 1, 1, '\xFF');  // Illegal header id.
+  string compressed_headers =
+      compressor_->CompressHeadersWithPriority(0, headers_);
+  compressed_headers.replace(4, 1, 1, '\xFF');  // Illegal header id.
   QuicStreamFrame frame(kStreamId, false, 0, compressed_headers);
 
   EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_HEADER_ID));
@@ -258,7 +261,8 @@
 TEST_F(ReliableQuicStreamTest, ProcessHeadersAndBody) {
   Initialize(kShouldProcessData);
 
-  string compressed_headers = compressor_->CompressHeaders(headers_);
+  string compressed_headers =
+      compressor_->CompressHeadersWithPriority(0, headers_);
   string body = "this is the body";
   string data = compressed_headers + body;
   QuicStreamFrame frame(kStreamId, false, 0, data);
@@ -271,7 +275,8 @@
 TEST_F(ReliableQuicStreamTest, ProcessHeadersAndBodyFragments) {
   Initialize(kShouldProcessData);
 
-  string compressed_headers = compressor_->CompressHeaders(headers_);
+  string compressed_headers =
+      compressor_->CompressHeadersWithPriority(7, headers_);
   string body = "this is the body";
   string data = compressed_headers + body;
 
@@ -303,12 +308,14 @@
     ASSERT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_) + body,
               stream_->data()) << "split_point: " << split_point;
   }
+  EXPECT_EQ(7u, stream_->priority());
 }
 
 TEST_F(ReliableQuicStreamTest, ProcessHeadersAndBodyReadv) {
   Initialize(!kShouldProcessData);
 
-  string compressed_headers = compressor_->CompressHeaders(headers_);
+  string compressed_headers =
+      compressor_->CompressHeadersWithPriority(0, headers_);
   string body = "this is the body";
   string data = compressed_headers + body;
   QuicStreamFrame frame(kStreamId, false, 0, data);
@@ -337,7 +344,8 @@
 TEST_F(ReliableQuicStreamTest, ProcessHeadersAndBodyIncrementalReadv) {
   Initialize(!kShouldProcessData);
 
-  string compressed_headers = compressor_->CompressHeaders(headers_);
+  string compressed_headers =
+      compressor_->CompressHeadersWithPriority(0, headers_);
   string body = "this is the body";
   string data = compressed_headers + body;
   QuicStreamFrame frame(kStreamId, false, 0, data);
@@ -362,7 +370,8 @@
 TEST_F(ReliableQuicStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) {
   Initialize(!kShouldProcessData);
 
-  string compressed_headers = compressor_->CompressHeaders(headers_);
+  string compressed_headers =
+      compressor_->CompressHeadersWithPriority(0, headers_);
   string body = "this is the body";
   string data = compressed_headers + body;
   QuicStreamFrame frame(kStreamId, false, 0, data);
@@ -391,13 +400,15 @@
 TEST_F(ReliableQuicStreamTest, ProcessCorruptHeadersEarly) {
   Initialize(kShouldProcessData);
 
-  string compressed_headers1 = compressor_->CompressHeaders(headers_);
+  string compressed_headers1 =
+      compressor_->CompressHeadersWithPriority(0, headers_);
   QuicStreamFrame frame1(stream_->id(), false, 0, compressed_headers1);
   string decompressed_headers1 =
       SpdyUtils::SerializeUncompressedHeaders(headers_);
 
   headers_["content-type"] = "text/plain";
-  string compressed_headers2 = compressor_->CompressHeaders(headers_);
+  string compressed_headers2 =
+      compressor_->CompressHeadersWithPriority(0, headers_);
   // Corrupt the compressed data.
   compressed_headers2[compressed_headers2.length() - 1] ^= 0xA1;
   QuicStreamFrame frame2(stream2_->id(), false, 0, compressed_headers2);
@@ -429,13 +440,15 @@
 TEST_F(ReliableQuicStreamTest, ProcessPartialHeadersEarly) {
   Initialize(kShouldProcessData);
 
-  string compressed_headers1 = compressor_->CompressHeaders(headers_);
+  string compressed_headers1 =
+      compressor_->CompressHeadersWithPriority(0, headers_);
   QuicStreamFrame frame1(stream_->id(), false, 0, compressed_headers1);
   string decompressed_headers1 =
       SpdyUtils::SerializeUncompressedHeaders(headers_);
 
   headers_["content-type"] = "text/plain";
-  string compressed_headers2 = compressor_->CompressHeaders(headers_);
+  string compressed_headers2 =
+      compressor_->CompressHeadersWithPriority(0, headers_);
   string partial_compressed_headers =
       compressed_headers2.substr(0, compressed_headers2.length() / 2);
   QuicStreamFrame frame2(stream2_->id(), false, 0, partial_compressed_headers);
@@ -478,13 +491,15 @@
 TEST_F(ReliableQuicStreamTest, ProcessHeadersEarly) {
   Initialize(kShouldProcessData);
 
-  string compressed_headers1 = compressor_->CompressHeaders(headers_);
+  string compressed_headers1 =
+      compressor_->CompressHeadersWithPriority(0, headers_);
   QuicStreamFrame frame1(stream_->id(), false, 0, compressed_headers1);
   string decompressed_headers1 =
       SpdyUtils::SerializeUncompressedHeaders(headers_);
 
   headers_["content-type"] = "text/plain";
-  string compressed_headers2 = compressor_->CompressHeaders(headers_);
+  string compressed_headers2 =
+      compressor_->CompressHeadersWithPriority(0, headers_);
   QuicStreamFrame frame2(stream2_->id(), false, 0, compressed_headers2);
   string decompressed_headers2 =
       SpdyUtils::SerializeUncompressedHeaders(headers_);
@@ -512,7 +527,8 @@
 TEST_F(ReliableQuicStreamTest, ProcessHeadersDelay) {
   Initialize(!kShouldProcessData);
 
-  string compressed_headers = compressor_->CompressHeaders(headers_);
+  string compressed_headers =
+      compressor_->CompressHeadersWithPriority(0, headers_);
   QuicStreamFrame frame1(stream_->id(), false, 0, compressed_headers);
   string decompressed_headers =
       SpdyUtils::SerializeUncompressedHeaders(headers_);
diff --git a/net/socket/client_socket_handle.cc b/net/socket/client_socket_handle.cc
index 9687a5a..e42e9fc 100644
--- a/net/socket/client_socket_handle.cc
+++ b/net/socket/client_socket_handle.cc
@@ -34,22 +34,29 @@
 }
 
 void ClientSocketHandle::ResetInternal(bool cancel) {
-  if (group_name_.empty())  // Was Init called?
-    return;
-  if (is_initialized()) {
-    // Because of http://crbug.com/37810 we may not have a pool, but have
-    // just a raw socket.
-    socket_->NetLog().EndEvent(NetLog::TYPE_SOCKET_IN_USE);
-    if (pool_)
-      // If we've still got a socket, release it back to the ClientSocketPool so
-      // it can be deleted or reused.
-      pool_->ReleaseSocket(group_name_, PassSocket(), pool_id_);
-  } else if (cancel) {
-    // If we did not get initialized yet, we've got a socket request pending.
-    // Cancel it.
-    pool_->CancelRequest(group_name_, this);
+  // Was Init called?
+  if (!group_name_.empty()) {
+    // If so, we must have a pool.
+    CHECK(pool_);
+    if (is_initialized()) {
+      if (socket_) {
+        socket_->NetLog().EndEvent(NetLog::TYPE_SOCKET_IN_USE);
+        // Release the socket back to the ClientSocketPool so it can be
+        // deleted or reused.
+        pool_->ReleaseSocket(group_name_, socket_.Pass(), pool_id_);
+      } else {
+        // If the handle has been initialized, we should still have a
+        // socket.
+        NOTREACHED();
+      }
+    } else if (cancel) {
+      // If we did not get initialized yet and we have a socket
+      // request pending, cancel it.
+      pool_->CancelRequest(group_name_, this);
+    }
   }
   is_initialized_ = false;
+  socket_.reset();
   group_name_.clear();
   is_reused_ = false;
   user_callback_.Reset();
diff --git a/net/socket/client_socket_handle.h b/net/socket/client_socket_handle.h
index 8f2d4ae..30b7c03 100644
--- a/net/socket/client_socket_handle.h
+++ b/net/socket/client_socket_handle.h
@@ -122,6 +122,9 @@
                          LoadTimingInfo* load_timing_info) const;
 
   // Used by ClientSocketPool to initialize the ClientSocketHandle.
+  //
+  // SetSocket() may also be used if this handle is used as simply for
+  // socket storage (e.g., http://crbug.com/37810).
   void SetSocket(scoped_ptr<StreamSocket> s);
   void set_is_reused(bool is_reused) { is_reused_ = is_reused; }
   void set_idle_time(base::TimeDelta idle_time) { idle_time_ = idle_time; }
@@ -149,9 +152,13 @@
     return pending_http_proxy_connection_.release();
   }
 
-  // These may only be used if is_initialized() is true.
-  scoped_ptr<StreamSocket> PassSocket();
   StreamSocket* socket() { return socket_.get(); }
+
+  // SetSocket() must be called with a new socket before this handle
+  // is destroyed if is_initialized() is true.
+  scoped_ptr<StreamSocket> PassSocket();
+
+  // These may only be used if is_initialized() is true.
   const std::string& group_name() const { return group_name_; }
   int id() const { return pool_id_; }
   bool is_reused() const { return is_reused_; }
diff --git a/net/socket/socket_descriptor.cc b/net/socket/socket_descriptor.cc
new file mode 100644
index 0000000..5a2e53c
--- /dev/null
+++ b/net/socket/socket_descriptor.cc
@@ -0,0 +1,48 @@
+// 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 "net/socket/socket_descriptor.h"
+
+#if defined(OS_POSIX)
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+
+#include "base/basictypes.h"
+
+#if defined(OS_WIN)
+#include "net/base/winsock_init.h"
+#endif
+
+namespace net {
+
+PlatformSocketFactory* g_socket_factory = NULL;
+
+PlatformSocketFactory::PlatformSocketFactory() {
+}
+
+PlatformSocketFactory::~PlatformSocketFactory() {
+}
+
+void PlatformSocketFactory::SetInstance(PlatformSocketFactory* factory) {
+  g_socket_factory = factory;
+}
+
+SocketDescriptor CreateSocketDefault(int family, int type, int protocol) {
+#if defined(OS_WIN)
+  EnsureWinsockInit();
+  return ::WSASocket(family, type, protocol, NULL, 0, WSA_FLAG_OVERLAPPED);
+#else  // OS_WIN
+  return ::socket(family, type, protocol);
+#endif  // OS_WIN
+}
+
+SocketDescriptor CreatePlatformSocket(int family, int type, int protocol) {
+  if (g_socket_factory)
+    return g_socket_factory->CreateSocket(family, type, protocol);
+  else
+    return CreateSocketDefault(family, type, protocol);
+}
+
+}  // namespace net
diff --git a/net/socket/socket_descriptor.h b/net/socket/socket_descriptor.h
new file mode 100644
index 0000000..b2a2223
--- /dev/null
+++ b/net/socket/socket_descriptor.h
@@ -0,0 +1,49 @@
+// 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 NET_SOCKET_SOCKET_DESCRIPTOR_H_
+#define NET_SOCKET_SOCKET_DESCRIPTOR_H_
+
+#include "build/build_config.h"
+#include "net/base/net_export.h"
+
+#if defined(OS_WIN)
+#include <winsock2.h>
+#endif  // OS_WIN
+
+namespace net {
+
+#if defined(OS_POSIX)
+typedef int SocketDescriptor;
+const SocketDescriptor kInvalidSocket = -1;
+#elif defined(OS_WIN)
+typedef SOCKET SocketDescriptor;
+const SocketDescriptor kInvalidSocket = INVALID_SOCKET;
+#endif
+
+// Interface to create native socket.
+// Usually such factories are used for testing purposes, which is not true in
+// this case. This interface is used to substitute WSASocket/socket to make
+// possible execution of some network code in sandbox.
+class NET_EXPORT PlatformSocketFactory {
+ public:
+  PlatformSocketFactory();
+  virtual ~PlatformSocketFactory();
+
+  // Replace WSASocket/socket with given factory. The factory will be used by
+  // CreatePlatformSocket.
+  static void SetInstance(PlatformSocketFactory* factory);
+
+  // Creates  socket. See WSASocket/socket documentation of parameters.
+  virtual SocketDescriptor CreateSocket(int family, int type, int protocol) = 0;
+};
+
+// Creates  socket. See WSASocket/socket documentation of parameters.
+SocketDescriptor NET_EXPORT CreatePlatformSocket(int family,
+                                                 int type,
+                                                 int protocol);
+
+}  // namespace net
+
+#endif  // NET_SOCKET_SOCKET_DESCRIPTOR_H_
diff --git a/net/socket/stream_listen_socket.cc b/net/socket/stream_listen_socket.cc
index c85c671..1109e75 100644
--- a/net/socket/stream_listen_socket.cc
+++ b/net/socket/stream_listen_socket.cc
@@ -27,6 +27,7 @@
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_util.h"
+#include "net/socket/socket_descriptor.h"
 
 using std::string;
 
@@ -43,10 +44,8 @@
 }  // namespace
 
 #if defined(OS_WIN)
-const SocketDescriptor StreamListenSocket::kInvalidSocket = INVALID_SOCKET;
 const int StreamListenSocket::kSocketError = SOCKET_ERROR;
 #elif defined(OS_POSIX)
-const SocketDescriptor StreamListenSocket::kInvalidSocket = -1;
 const int StreamListenSocket::kSocketError = -1;
 #endif
 
diff --git a/net/socket/stream_listen_socket.h b/net/socket/stream_listen_socket.h
index 6f03eef..4964b92 100644
--- a/net/socket/stream_listen_socket.h
+++ b/net/socket/stream_listen_socket.h
@@ -31,13 +31,7 @@
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
 #include "net/base/net_export.h"
-#include "net/socket/stream_listen_socket.h"
-
-#if defined(OS_POSIX)
-typedef int SocketDescriptor;
-#else
-typedef SOCKET SocketDescriptor;
-#endif
+#include "net/socket/socket_descriptor.h"
 
 namespace net {
 
@@ -78,7 +72,6 @@
   // Copies the local address to |address|. Returns a network error code.
   int GetLocalAddress(IPEndPoint* address);
 
-  static const SocketDescriptor kInvalidSocket;
   static const int kSocketError;
 
  protected:
diff --git a/net/socket/tcp_client_socket_libevent.cc b/net/socket/tcp_client_socket_libevent.cc
index 2f7e4b4..cbcd25f 100644
--- a/net/socket/tcp_client_socket_libevent.cc
+++ b/net/socket/tcp_client_socket_libevent.cc
@@ -26,6 +26,7 @@
 #include "net/base/net_log.h"
 #include "net/base/net_util.h"
 #include "net/base/network_change_notifier.h"
+#include "net/socket/socket_descriptor.h"
 #include "net/socket/socket_net_log_params.h"
 
 // If we don't have a definition for TCPI_OPT_SYN_DATA, create one.
@@ -37,7 +38,6 @@
 
 namespace {
 
-const int kInvalidSocket = -1;
 const int kTCPKeepAliveSeconds = 45;
 
 // SetTCPNoDelay turns on/off buffering in the kernel. By default, TCP sockets
@@ -90,7 +90,7 @@
 // Creates a new socket and sets default parameters for it. Returns
 // the OS error code (or 0 on success).
 int CreateSocket(int family, int* socket) {
-  *socket = ::socket(family, SOCK_STREAM, IPPROTO_TCP);
+  *socket = CreatePlatformSocket(family, SOCK_STREAM, IPPROTO_TCP);
   if (*socket == kInvalidSocket)
     return errno;
   int error = SetupSocket(*socket);
diff --git a/net/socket/tcp_client_socket_win.cc b/net/socket/tcp_client_socket_win.cc
index 9b0a5b5..f1334e7 100644
--- a/net/socket/tcp_client_socket_win.cc
+++ b/net/socket/tcp_client_socket_win.cc
@@ -21,6 +21,7 @@
 #include "net/base/network_change_notifier.h"
 #include "net/base/winsock_init.h"
 #include "net/base/winsock_util.h"
+#include "net/socket/socket_descriptor.h"
 #include "net/socket/socket_net_log_params.h"
 
 namespace net {
@@ -182,16 +183,6 @@
   // The TCPClientSocketWin is going away.
   void Detach() { socket_ = NULL; }
 
-  // Throttle the read size based on our current slow start state.
-  // Returns the throttled read size.
-  int ThrottleReadSize(int size) {
-    if (slow_start_throttle_ < kMaxSlowStartThrottle) {
-      size = std::min(size, slow_start_throttle_);
-      slow_start_throttle_ *= 2;
-    }
-    return size;
-  }
-
   // The separate OVERLAPPED variables for asynchronous operation.
   // |read_overlapped_| is used for both Connect() and Read().
   // |write_overlapped_| is only used for Write();
@@ -204,9 +195,6 @@
   int read_buffer_length_;
   int write_buffer_length_;
 
-  // Remember the state of g_disable_overlapped_reads for the duration of the
-  // socket based on what it was when the socket was created.
-  bool disable_overlapped_reads_;
   bool non_blocking_reads_initialized_;
 
  private:
@@ -251,13 +239,6 @@
   // |write_watcher_| watches for events from Write();
   base::win::ObjectWatcher write_watcher_;
 
-  // When doing reads from the socket, we try to mirror TCP's slow start.
-  // We do this because otherwise the async IO subsystem artifically delays
-  // returning data to the application.
-  static const int kInitialSlowStartThrottle = 1 * 1024;
-  static const int kMaxSlowStartThrottle = 32 * kInitialSlowStartThrottle;
-  int slow_start_throttle_;
-
   DISALLOW_COPY_AND_ASSIGN(Core);
 };
 
@@ -265,12 +246,10 @@
     TCPClientSocketWin* socket)
     : read_buffer_length_(0),
       write_buffer_length_(0),
-      disable_overlapped_reads_(g_disable_overlapped_reads),
       non_blocking_reads_initialized_(false),
       socket_(socket),
       reader_(this),
-      writer_(this),
-      slow_start_throttle_(kInitialSlowStartThrottle) {
+      writer_(this) {
   memset(&read_overlapped_, 0, sizeof(read_overlapped_));
   memset(&write_overlapped_, 0, sizeof(write_overlapped_));
 
@@ -307,13 +286,10 @@
     HANDLE object) {
   DCHECK_EQ(object, core_->read_overlapped_.hEvent);
   if (core_->socket_) {
-    if (core_->socket_->waiting_connect()) {
+    if (core_->socket_->waiting_connect())
       core_->socket_->DidCompleteConnect();
-    } else if (core_->disable_overlapped_reads_) {
+    else
       core_->socket_->DidSignalRead();
-    } else {
-      core_->socket_->DidCompleteRead();
-    }
   }
 
   core_->Release();
@@ -788,10 +764,6 @@
   return DisableNagle(socket_, no_delay);
 }
 
-void TCPClientSocketWin::DisableOverlappedReads() {
-  g_disable_overlapped_reads = true;
-}
-
 void TCPClientSocketWin::LogConnectCompletion(int net_error) {
   if (net_error == OK)
     UpdateConnectionTypeHistograms(CONNECTION_ANY);
@@ -822,64 +794,30 @@
 
 int TCPClientSocketWin::DoRead(IOBuffer* buf, int buf_len,
                                const CompletionCallback& callback) {
-  if (core_->disable_overlapped_reads_) {
-    if (!core_->non_blocking_reads_initialized_) {
-      WSAEventSelect(socket_, core_->read_overlapped_.hEvent,
-                     FD_READ | FD_CLOSE);
-      core_->non_blocking_reads_initialized_ = true;
-    }
-    int rv = recv(socket_, buf->data(), buf_len, 0);
-    if (rv == SOCKET_ERROR) {
-      int os_error = WSAGetLastError();
-      if (os_error != WSAEWOULDBLOCK) {
-        int net_error = MapSystemError(os_error);
-        net_log_.AddEvent(NetLog::TYPE_SOCKET_READ_ERROR,
-            CreateNetLogSocketErrorCallback(net_error, os_error));
-        return net_error;
-      }
-    } else {
-      base::StatsCounter read_bytes("tcp.read_bytes");
-      if (rv > 0) {
-        use_history_.set_was_used_to_convey_data();
-        read_bytes.Add(rv);
-      }
-      net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, rv,
-                                    buf->data());
-      return rv;
+  if (!core_->non_blocking_reads_initialized_) {
+    WSAEventSelect(socket_, core_->read_overlapped_.hEvent,
+                   FD_READ | FD_CLOSE);
+    core_->non_blocking_reads_initialized_ = true;
+  }
+  int rv = recv(socket_, buf->data(), buf_len, 0);
+  if (rv == SOCKET_ERROR) {
+    int os_error = WSAGetLastError();
+    if (os_error != WSAEWOULDBLOCK) {
+      int net_error = MapSystemError(os_error);
+      net_log_.AddEvent(
+          NetLog::TYPE_SOCKET_READ_ERROR,
+          CreateNetLogSocketErrorCallback(net_error, os_error));
+      return net_error;
     }
   } else {
-    buf_len = core_->ThrottleReadSize(buf_len);
-
-    WSABUF read_buffer;
-    read_buffer.len = buf_len;
-    read_buffer.buf = buf->data();
-
-    // TODO(wtc): Remove the assertion after enough testing.
-    AssertEventNotSignaled(core_->read_overlapped_.hEvent);
-    DWORD num;
-    DWORD flags = 0;
-    int rv = WSARecv(socket_, &read_buffer, 1, &num, &flags,
-                     &core_->read_overlapped_, NULL);
-    if (rv == 0) {
-      if (ResetEventIfSignaled(core_->read_overlapped_.hEvent)) {
-        base::StatsCounter read_bytes("tcp.read_bytes");
-        if (num > 0) {
-          use_history_.set_was_used_to_convey_data();
-          read_bytes.Add(num);
-        }
-        net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, num,
-                                      buf->data());
-        return static_cast<int>(num);
-      }
-    } else {
-      int os_error = WSAGetLastError();
-      if (os_error != WSA_IO_PENDING) {
-        int net_error = MapSystemError(os_error);
-        net_log_.AddEvent(NetLog::TYPE_SOCKET_READ_ERROR,
-            CreateNetLogSocketErrorCallback(net_error, os_error));
-        return net_error;
-      }
+    base::StatsCounter read_bytes("tcp.read_bytes");
+    if (rv > 0) {
+      use_history_.set_was_used_to_convey_data();
+      read_bytes.Add(rv);
     }
+    net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED, rv,
+                                  buf->data());
+    return rv;
   }
 
   waiting_read_ = true;
@@ -904,7 +842,7 @@
   DCHECK_NE(rv, ERR_IO_PENDING);
   DCHECK(!write_callback_.is_null());
 
-  // since Run may result in Write being called, clear write_callback_ up front.
+  // Since Run may result in Write being called, clear write_callback_ up front.
   CompletionCallback c = write_callback_;
   write_callback_.Reset();
   c.Run(rv);
@@ -938,33 +876,6 @@
   }
 }
 
-void TCPClientSocketWin::DidCompleteRead() {
-  DCHECK(waiting_read_);
-  DWORD num_bytes, flags;
-  BOOL ok = WSAGetOverlappedResult(socket_, &core_->read_overlapped_,
-                                   &num_bytes, FALSE, &flags);
-  waiting_read_ = false;
-  int rv;
-  if (ok) {
-    base::StatsCounter read_bytes("tcp.read_bytes");
-    read_bytes.Add(num_bytes);
-    if (num_bytes > 0)
-      use_history_.set_was_used_to_convey_data();
-    net_log_.AddByteTransferEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED,
-                                  num_bytes, core_->read_iobuffer_->data());
-    rv = static_cast<int>(num_bytes);
-  } else {
-    int os_error = WSAGetLastError();
-    rv = MapSystemError(os_error);
-    net_log_.AddEvent(NetLog::TYPE_SOCKET_READ_ERROR,
-                      CreateNetLogSocketErrorCallback(rv, os_error));
-  }
-  WSAResetEvent(core_->read_overlapped_.hEvent);
-  core_->read_iobuffer_ = NULL;
-  core_->read_buffer_length_ = 0;
-  DoReadCallback(rv);
-}
-
 void TCPClientSocketWin::DidCompleteWrite() {
   DCHECK(waiting_write_);
 
diff --git a/net/socket/tcp_client_socket_win.h b/net/socket/tcp_client_socket_win.h
index 26c8b9f..c899f27 100644
--- a/net/socket/tcp_client_socket_win.h
+++ b/net/socket/tcp_client_socket_win.h
@@ -70,10 +70,6 @@
   virtual bool SetKeepAlive(bool enable, int delay);
   virtual bool SetNoDelay(bool no_delay);
 
-  // Perform reads in non-blocking mode instead of overlapped mode.
-  // Used for experiments.
-  static void DisableOverlappedReads();
-
  private:
   // State machine for connecting the socket.
   enum ConnectState {
@@ -105,7 +101,6 @@
   void DoReadCallback(int rv);
   void DoWriteCallback(int rv);
   void DidCompleteConnect();
-  void DidCompleteRead();
   void DidCompleteWrite();
   void DidSignalRead();
 
diff --git a/net/socket/tcp_listen_socket.cc b/net/socket/tcp_listen_socket.cc
index aab2e45..0ece37f 100644
--- a/net/socket/tcp_listen_socket.cc
+++ b/net/socket/tcp_listen_socket.cc
@@ -23,6 +23,7 @@
 #include "build/build_config.h"
 #include "net/base/net_util.h"
 #include "net/base/winsock_init.h"
+#include "net/socket/socket_descriptor.h"
 
 using std::string;
 
@@ -47,11 +48,7 @@
 TCPListenSocket::~TCPListenSocket() {}
 
 SocketDescriptor TCPListenSocket::CreateAndBind(const string& ip, int port) {
-#if defined(OS_WIN)
-  EnsureWinsockInit();
-#endif
-
-  SocketDescriptor s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+  SocketDescriptor s = CreatePlatformSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
   if (s != kInvalidSocket) {
 #if defined(OS_POSIX)
     // Allow rapid reuse.
diff --git a/net/socket/tcp_listen_socket.h b/net/socket/tcp_listen_socket.h
index dbc5347..1756975 100644
--- a/net/socket/tcp_listen_socket.h
+++ b/net/socket/tcp_listen_socket.h
@@ -10,6 +10,7 @@
 #include "base/basictypes.h"
 #include "base/memory/ref_counted.h"
 #include "net/base/net_export.h"
+#include "net/socket/socket_descriptor.h"
 #include "net/socket/stream_listen_socket.h"
 
 namespace net {
diff --git a/net/socket/tcp_listen_socket_unittest.cc b/net/socket/tcp_listen_socket_unittest.cc
index d13b784..9589ce2 100644
--- a/net/socket/tcp_listen_socket_unittest.cc
+++ b/net/socket/tcp_listen_socket_unittest.cc
@@ -10,13 +10,14 @@
 #include "base/bind.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/sys_byteorder.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
 #include "net/base/net_util.h"
+#include "net/socket/socket_descriptor.h"
 #include "testing/platform_test.h"
 
 namespace net {
 
-const int TCPListenSocketTester::kTestPort = 9999;
-
 static const int kReadBufSize = 1024;
 static const char kHelloWorld[] = "HELLO, WORLD";
 static const int kMaxQueueSize = 20;
@@ -24,7 +25,11 @@
 static const int kDefaultTimeoutMs = 5000;
 
 TCPListenSocketTester::TCPListenSocketTester()
-    : loop_(NULL), server_(NULL), connection_(NULL), cv_(&lock_) {}
+    : loop_(NULL),
+      server_(NULL),
+      connection_(NULL),
+      cv_(&lock_),
+      server_port_(0) {}
 
 void TCPListenSocketTester::SetUp() {
   base::Thread::Options options;
@@ -41,13 +46,16 @@
   ASSERT_FALSE(server_.get() == NULL);
   ASSERT_EQ(ACTION_LISTEN, last_action_.type());
 
+  int server_port = GetServerPort();
+  ASSERT_GT(server_port, 0);
+
   // verify the connect/accept and setup test_socket_
-  test_socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-  ASSERT_NE(StreamListenSocket::kInvalidSocket, test_socket_);
+  test_socket_ = CreatePlatformSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+  ASSERT_NE(kInvalidSocket, test_socket_);
   struct sockaddr_in client;
   client.sin_family = AF_INET;
   client.sin_addr.s_addr = inet_addr(kLoopback);
-  client.sin_port = base::HostToNet16(kTestPort);
+  client.sin_port = base::HostToNet16(server_port);
   int ret = HANDLE_EINTR(
       connect(test_socket_, reinterpret_cast<sockaddr*>(&client),
               sizeof(client)));
@@ -124,6 +132,12 @@
   server_ = DoListen();
   ASSERT_TRUE(server_.get());
   server_->AddRef();
+
+  // The server's port will be needed to open the client socket.
+  IPEndPoint local_address;
+  ASSERT_EQ(OK, server_->GetLocalAddress(&local_address));
+  SetServerPort(local_address.port());
+
   ReportAction(TCPListenSocketTestAction(ACTION_LISTEN));
 }
 
@@ -248,10 +262,21 @@
 TCPListenSocketTester::~TCPListenSocketTester() {}
 
 scoped_refptr<TCPListenSocket> TCPListenSocketTester::DoListen() {
-  return TCPListenSocket::CreateAndListen(kLoopback, kTestPort, this);
+  // Let the OS pick a free port.
+  return TCPListenSocket::CreateAndListen(kLoopback, 0, this);
 }
 
-class TCPListenSocketTest: public PlatformTest {
+int TCPListenSocketTester::GetServerPort() {
+  base::AutoLock locked(lock_);
+  return server_port_;
+}
+
+void TCPListenSocketTester::SetServerPort(int server_port) {
+  base::AutoLock locked(lock_);
+  server_port_ = server_port;
+}
+
+class TCPListenSocketTest : public PlatformTest {
  public:
   TCPListenSocketTest() {
     tester_ = NULL;
diff --git a/net/socket/tcp_listen_socket_unittest.h b/net/socket/tcp_listen_socket_unittest.h
index 048a018..93adbd5 100644
--- a/net/socket/tcp_listen_socket_unittest.h
+++ b/net/socket/tcp_listen_socket_unittest.h
@@ -103,18 +103,25 @@
   TCPListenSocketTestAction last_action_;
 
   SocketDescriptor test_socket_;
-  static const int kTestPort;
 
-  base::Lock lock_;  // protects |queue_| and wraps |cv_|
+  base::Lock lock_;  // Protects |queue_| and |server_port_|. Wraps |cv_|.
   base::ConditionVariable cv_;
   std::deque<TCPListenSocketTestAction> queue_;
 
- protected:
+ private:
   friend class base::RefCountedThreadSafe<TCPListenSocketTester>;
 
   virtual ~TCPListenSocketTester();
 
   virtual scoped_refptr<TCPListenSocket> DoListen();
+
+  // Getters/setters for |server_port_|. They use |lock_| for thread safety.
+  int GetServerPort();
+  void SetServerPort(int server_port);
+
+  // Port the server is using. Must have |lock_| to access. Set by Listen() on
+  // the server's thread.
+  int server_port_;
 };
 
 }  // namespace net
diff --git a/net/socket/tcp_server_socket_libevent.cc b/net/socket/tcp_server_socket_libevent.cc
index 38dda96..9f0e39d 100644
--- a/net/socket/tcp_server_socket_libevent.cc
+++ b/net/socket/tcp_server_socket_libevent.cc
@@ -19,17 +19,12 @@
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_util.h"
+#include "net/socket/socket_descriptor.h"
 #include "net/socket/socket_net_log_params.h"
 #include "net/socket/tcp_client_socket.h"
 
 namespace net {
 
-namespace {
-
-const int kInvalidSocket = -1;
-
-}  // namespace
-
 TCPServerSocketLibevent::TCPServerSocketLibevent(
     net::NetLog* net_log,
     const net::NetLog::Source& source)
@@ -51,7 +46,8 @@
   DCHECK_GT(backlog, 0);
   DCHECK_EQ(socket_, kInvalidSocket);
 
-  socket_ = socket(address.GetSockAddrFamily(), SOCK_STREAM, IPPROTO_TCP);
+  socket_ = CreatePlatformSocket(address.GetSockAddrFamily(), SOCK_STREAM,
+                                 IPPROTO_TCP);
   if (socket_ < 0) {
     PLOG(ERROR) << "socket() returned an error";
     return MapSystemError(errno);
diff --git a/net/socket/tcp_server_socket_win.cc b/net/socket/tcp_server_socket_win.cc
index 0ac77be..e7c4b6c 100644
--- a/net/socket/tcp_server_socket_win.cc
+++ b/net/socket/tcp_server_socket_win.cc
@@ -11,6 +11,7 @@
 #include "net/base/net_util.h"
 #include "net/base/winsock_init.h"
 #include "net/base/winsock_util.h"
+#include "net/socket/socket_descriptor.h"
 #include "net/socket/socket_net_log_params.h"
 #include "net/socket/tcp_client_socket.h"
 
@@ -44,7 +45,8 @@
     return ERR_FAILED;
   }
 
-  socket_ = socket(address.GetSockAddrFamily(), SOCK_STREAM, IPPROTO_TCP);
+  socket_ = CreatePlatformSocket(address.GetSockAddrFamily(), SOCK_STREAM,
+                                 IPPROTO_TCP);
   if (socket_ == INVALID_SOCKET) {
     PLOG(ERROR) << "socket() returned an error";
     return MapSystemError(WSAGetLastError());
diff --git a/net/socket/unix_domain_socket_posix.cc b/net/socket/unix_domain_socket_posix.cc
index 5b6b249..9166024 100644
--- a/net/socket/unix_domain_socket_posix.cc
+++ b/net/socket/unix_domain_socket_posix.cc
@@ -21,6 +21,7 @@
 #include "build/build_config.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_util.h"
+#include "net/socket/socket_descriptor.h"
 
 namespace net {
 
@@ -48,7 +49,7 @@
 }  // namespace
 
 // static
-UnixDomainSocket::AuthCallback NoAuthentication() {
+UnixDomainSocket::AuthCallback UnixDomainSocket::NoAuthentication() {
   return base::Bind(NoAuthenticationCallback);
 }
 
@@ -106,7 +107,7 @@
   static const size_t kPathMax = sizeof(addr.sun_path);
   if (use_abstract_namespace + path.size() + 1 /* '\0' */ > kPathMax)
     return kInvalidSocket;
-  const SocketDescriptor s = socket(PF_UNIX, SOCK_STREAM, 0);
+  const SocketDescriptor s = CreatePlatformSocket(PF_UNIX, SOCK_STREAM, 0);
   if (s == kInvalidSocket)
     return kInvalidSocket;
   memset(&addr, 0, sizeof(addr));
diff --git a/net/socket/unix_domain_socket_posix_unittest.cc b/net/socket/unix_domain_socket_posix_unittest.cc
index 5abe03b..65fda04 100644
--- a/net/socket/unix_domain_socket_posix_unittest.cc
+++ b/net/socket/unix_domain_socket_posix_unittest.cc
@@ -29,6 +29,7 @@
 #include "base/synchronization/lock.h"
 #include "base/threading/platform_thread.h"
 #include "base/threading/thread.h"
+#include "net/socket/socket_descriptor.h"
 #include "net/socket/unix_domain_socket_posix.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -187,10 +188,10 @@
   }
 
   SocketDescriptor CreateClientSocket() {
-    const SocketDescriptor sock = socket(PF_UNIX, SOCK_STREAM, 0);
+    const SocketDescriptor sock = CreatePlatformSocket(PF_UNIX, SOCK_STREAM, 0);
     if (sock < 0) {
       LOG(ERROR) << "socket() error";
-      return StreamListenSocket::kInvalidSocket;
+      return kInvalidSocket;
     }
     sockaddr_un addr;
     memset(&addr, 0, sizeof(addr));
@@ -200,7 +201,7 @@
     addr_len = sizeof(sockaddr_un);
     if (connect(sock, reinterpret_cast<sockaddr*>(&addr), addr_len) != 0) {
       LOG(ERROR) << "connect() error";
-      return StreamListenSocket::kInvalidSocket;
+      return kInvalidSocket;
     }
     return sock;
   }
@@ -291,7 +292,7 @@
 
   // Create the client socket.
   const SocketDescriptor sock = CreateClientSocket();
-  ASSERT_NE(StreamListenSocket::kInvalidSocket, sock);
+  ASSERT_NE(kInvalidSocket, sock);
   event = event_manager_->WaitForEvent();
   ASSERT_EQ(EVENT_AUTH_GRANTED, event);
   event = event_manager_->WaitForEvent();
@@ -316,7 +317,7 @@
   EventType event = event_manager_->WaitForEvent();
   ASSERT_EQ(EVENT_LISTEN, event);
   const SocketDescriptor sock = CreateClientSocket();
-  ASSERT_NE(StreamListenSocket::kInvalidSocket, sock);
+  ASSERT_NE(kInvalidSocket, sock);
 
   event = event_manager_->WaitForEvent();
   ASSERT_EQ(EVENT_AUTH_DENIED, event);
diff --git a/net/test/embedded_test_server/embedded_test_server.cc b/net/test/embedded_test_server/embedded_test_server.cc
index 9175d6c..a54ff7e 100644
--- a/net/test/embedded_test_server/embedded_test_server.cc
+++ b/net/test/embedded_test_server/embedded_test_server.cc
@@ -153,7 +153,7 @@
 
   SocketDescriptor socket_descriptor =
       TCPListenSocket::CreateAndBindAnyPort("127.0.0.1", &port_);
-  if (socket_descriptor == TCPListenSocket::kInvalidSocket)
+  if (socket_descriptor == kInvalidSocket)
     return;
 
   listen_socket_ = new HttpListenSocket(socket_descriptor, this);
diff --git a/net/tools/disk_cache_memory_test/disk_cache_memory_test.cc b/net/tools/disk_cache_memory_test/disk_cache_memory_test.cc
new file mode 100644
index 0000000..004f311
--- /dev/null
+++ b/net/tools/disk_cache_memory_test/disk_cache_memory_test.cc
@@ -0,0 +1,257 @@
+// 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 <cstdlib>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/logging.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/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
+#include "net/base/cache_type.h"
+#include "net/base/net_errors.h"
+#include "net/disk_cache/disk_cache.h"
+#include "net/disk_cache/simple/simple_backend_impl.h"
+#include "net/disk_cache/simple/simple_index.h"
+
+namespace disk_cache {
+namespace {
+
+const char kBlockFileBackendType[] = "block_file";
+const char kSimpleBackendType[] = "simple";
+
+const char kDiskCacheType[] = "disk_cache";
+const char kAppCacheType[] = "app_cache";
+
+const char kPrivateDirty[] = "Private_Dirty:";
+const char kReadWrite[] = "rw-";
+const char kHeap[] = "[heap]";
+const char kKb[] = "kB";
+
+void SetSuccessCodeOnCompletion(base::RunLoop* run_loop,
+                                bool* succeeded,
+                                int net_error) {
+  if (net_error == net::OK) {
+    *succeeded = true;
+  } else {
+    *succeeded = false;
+  }
+  run_loop->Quit();
+}
+
+scoped_ptr<Backend> CreateAndInitBackend(
+    const net::BackendType& backend_type,
+    const net::CacheType& cache_type,
+    const base::FilePath& cache_path) {
+  scoped_ptr<Backend> result;
+  scoped_ptr<Backend> backend;
+  bool succeeded = false;
+  base::RunLoop run_loop;
+  const net::CompletionCallback callback = base::Bind(
+      &SetSuccessCodeOnCompletion,
+      base::Unretained(&run_loop),
+      base::Unretained(&succeeded));
+  const int net_error = CreateCacheBackend(
+      cache_type, backend_type, cache_path, 0, false,
+      base::MessageLoopProxy::current(), NULL, &backend, callback);
+  if (net_error == net::OK)
+    callback.Run(net::OK);
+  else
+    run_loop.Run();
+  if (!succeeded) {
+    LOG(ERROR) << "Could not initialize backend in "
+               << cache_path.LossyDisplayName();
+    return result.Pass();
+  }
+  // For the simple cache, the index may not be initialized yet.
+  if (backend_type == net::CACHE_BACKEND_SIMPLE) {
+    base::RunLoop index_run_loop;
+    const net::CompletionCallback index_callback = base::Bind(
+        &SetSuccessCodeOnCompletion,
+        base::Unretained(&index_run_loop),
+        base::Unretained(&succeeded));
+    SimpleBackendImpl* simple_backend =
+        static_cast<SimpleBackendImpl*>(backend.get());
+    const int index_net_error =
+        simple_backend->index()->ExecuteWhenReady(index_callback);
+    if (index_net_error == net::OK)
+      index_callback.Run(net::OK);
+    else
+      index_run_loop.Run();
+    if (!succeeded) {
+      LOG(ERROR) << "Could not initialize Simple Cache in "
+                 << cache_path.LossyDisplayName();
+      return result.Pass();
+    }
+  }
+  DCHECK(backend);
+  result.swap(backend);
+  return result.Pass();
+}
+
+// Parses range lines from /proc/<PID>/smaps, e.g. (anonymous read write):
+// 7f819d88b000-7f819d890000 rw-p 00000000 00:00 0
+bool ParseRangeLine(const std::string& line,
+                    std::vector<std::string>* tokens,
+                    bool* is_anonymous_read_write) {
+  tokens->clear();
+  base::SplitStringAlongWhitespace(line, tokens);
+  if (tokens->size() == 5) {
+    const std::string& mode = (*tokens)[1];
+    *is_anonymous_read_write = !mode.compare(0, 3, kReadWrite);
+    return true;
+  }
+  // On Android, most of the memory is allocated in the heap, instead of being
+  // mapped.
+  if (tokens->size() == 6) {
+    const std::string& type = (*tokens)[5];
+    *is_anonymous_read_write = (type == kHeap);
+    return true;
+  }
+  return false;
+}
+
+// Parses range property lines from /proc/<PID>/smaps, e.g.:
+// Private_Dirty:        16 kB
+bool ParseRangeProperty(const std::string& line,
+                        std::vector<std::string>* tokens,
+                        uint64* size,
+                        bool* is_private_dirty) {
+  tokens->clear();
+  base::SplitStringAlongWhitespace(line, tokens);
+  if (tokens->size() != 3)
+    return false;
+  const std::string& type = (*tokens)[0];
+  if (type != kPrivateDirty)
+    return true;
+  const std::string& unit = (*tokens)[2];
+  if (unit != kKb) {
+    LOG(WARNING) << "Discarding value not in kB: " << line;
+    return true;
+  }
+  const std::string& size_str = (*tokens)[1];
+  uint64 map_size = 0;
+  if (!base::StringToUint64(size_str, &map_size))
+    return false;
+  *is_private_dirty = true;
+  *size = map_size;
+  return true;
+}
+
+uint64 GetMemoryConsumption() {
+  std::ifstream maps_file(
+      base::StringPrintf("/proc/%d/smaps", getpid()).c_str());
+  if (!maps_file.good()) {
+    LOG(ERROR) << "Could not open smaps file.";
+    return false;
+  }
+  std::string line;
+  std::vector<std::string> tokens;
+  uint64 total_size = 0;
+  if (!std::getline(maps_file, line) || line.empty())
+    return total_size;
+  while (true) {
+    bool is_anonymous_read_write = false;
+    if (!ParseRangeLine(line, &tokens, &is_anonymous_read_write)) {
+      LOG(WARNING) << "Parsing smaps - did not expect line: " << line;
+    }
+    if (!std::getline(maps_file, line) || line.empty())
+      return total_size;
+    bool is_private_dirty = false;
+    uint64 size = 0;
+    while (ParseRangeProperty(line, &tokens, &size, &is_private_dirty)) {
+      if (is_anonymous_read_write && is_private_dirty) {
+        total_size += size;
+        is_private_dirty = false;
+      }
+      if (!std::getline(maps_file, line) || line.empty())
+        return total_size;
+    }
+  }
+  return total_size;
+}
+
+bool CacheMemTest(const net::BackendType& backend_type,
+                  const net::CacheType& cache_type,
+                  const base::FilePath& file_path) {
+  const scoped_ptr<Backend> backend =
+      CreateAndInitBackend(backend_type, cache_type, file_path);
+  if (!backend)
+    return false;
+  std::cout << "Number of entries: " << backend->GetEntryCount() << std::endl;
+  const uint64 memory_consumption = GetMemoryConsumption();
+  std::cout << "Private dirty memory: " << memory_consumption << " kB"
+            << std::endl;
+  return true;
+}
+
+void PrintUsage(std::ostream* stream) {
+  *stream << "Usage: cache_mem_test "
+          << "--backend-type=<backend_type> "
+          << "--cache-type=<cache_type> "
+          << "--cache-path=<cache_path>"
+          << std::endl
+          << "  with <backend_type>='block_file'|'simple'" << std::endl
+          << "       <cache_type>='disk_cache'|'app_cache'" << std::endl
+          << "       <cache_path>=file system path" << std::endl;
+}
+
+bool Main(int argc, char** argv) {
+  base::AtExitManager at_exit_manager;
+  base::MessageLoopForIO message_loop;
+  CommandLine::Init(argc, argv);
+  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+  if (command_line.HasSwitch("help")) {
+    PrintUsage(&std::cout);
+    return true;
+  }
+  if (command_line.GetSwitches().size() != 3 ||
+      !command_line.HasSwitch("backend-type") ||
+      !command_line.HasSwitch("cache-type") ||
+      !command_line.HasSwitch("cache-path")) {
+    PrintUsage(&std::cerr);
+    return false;
+  }
+  const std::string backend_type_str =
+      command_line.GetSwitchValueASCII("backend-type");
+  const std::string cache_type_str =
+      command_line.GetSwitchValueASCII("cache-type");
+  const base::FilePath cache_path =
+      command_line.GetSwitchValuePath("cache-path");
+  if (backend_type_str != kBlockFileBackendType &&
+      backend_type_str != kSimpleBackendType) {
+    PrintUsage(&std::cerr);
+    return false;
+  }
+  if (cache_type_str != kDiskCacheType && cache_type_str != kAppCacheType) {
+    PrintUsage(&std::cerr);
+    return false;
+  }
+  const net::BackendType backend_type =
+      (backend_type_str == kBlockFileBackendType) ?
+      net::CACHE_BACKEND_BLOCKFILE : net::CACHE_BACKEND_SIMPLE;
+  const net::CacheType cache_type = (cache_type_str == kDiskCacheType) ?
+      net::DISK_CACHE : net::APP_CACHE;
+  return CacheMemTest(backend_type, cache_type, cache_path);
+}
+
+}  // namespace
+}  // namespace disk_cache
+
+int main(int argc, char** argv) {
+  return !disk_cache::Main(argc, argv);
+}
diff --git a/net/tools/fetch/http_listen_socket.cc b/net/tools/fetch/http_listen_socket.cc
index 10b601e..c8e1b1b 100644
--- a/net/tools/fetch/http_listen_socket.cc
+++ b/net/tools/fetch/http_listen_socket.cc
@@ -13,7 +13,7 @@
 #include "net/tools/fetch/http_server_request_info.h"
 #include "net/tools/fetch/http_server_response_info.h"
 
-HttpListenSocket::HttpListenSocket(SocketDescriptor s,
+HttpListenSocket::HttpListenSocket(net::SocketDescriptor s,
                                    HttpListenSocket::Delegate* delegate)
     : net::TCPListenSocket(s, this),
       delegate_(delegate) {
@@ -23,9 +23,9 @@
 }
 
 void HttpListenSocket::Accept() {
-  SocketDescriptor conn = net::TCPListenSocket::AcceptSocket();
-  DCHECK_NE(conn, net::TCPListenSocket::kInvalidSocket);
-  if (conn == net::TCPListenSocket::kInvalidSocket) {
+  net::SocketDescriptor conn = net::TCPListenSocket::AcceptSocket();
+  DCHECK_NE(conn, net::kInvalidSocket);
+  if (conn == net::kInvalidSocket) {
     // TODO
   } else {
     scoped_refptr<HttpListenSocket> sock(
@@ -40,8 +40,8 @@
     const std::string& ip,
     int port,
     HttpListenSocket::Delegate* delegate) {
-  SocketDescriptor s = net::TCPListenSocket::CreateAndBind(ip, port);
-  if (s == net::TCPListenSocket::kInvalidSocket) {
+  net::SocketDescriptor s = net::TCPListenSocket::CreateAndBind(ip, port);
+  if (s == net::kInvalidSocket) {
     // TODO (ibrar): error handling.
   } else {
     scoped_refptr<HttpListenSocket> serv = new HttpListenSocket(s, delegate);
diff --git a/net/tools/fetch/http_listen_socket.h b/net/tools/fetch/http_listen_socket.h
index 379f73c..246948b 100644
--- a/net/tools/fetch/http_listen_socket.h
+++ b/net/tools/fetch/http_listen_socket.h
@@ -49,7 +49,7 @@
   static const int kReadBufSize = 16 * 1024;
 
   // Must run in the IO thread.
-  HttpListenSocket(SocketDescriptor s, HttpListenSocket::Delegate* del);
+  HttpListenSocket(net::SocketDescriptor s, HttpListenSocket::Delegate* del);
   virtual ~HttpListenSocket();
 
   // Expects the raw data to be stored in recv_data_. If parsing is successful,
diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc
index facf72a..c256d61 100644
--- a/net/tools/quic/end_to_end_test.cc
+++ b/net/tools/quic/end_to_end_test.cc
@@ -630,6 +630,28 @@
   EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
 }
 
+TEST_P(EndToEndTest, MaxStreamsUberTest) {
+  //  FLAGS_fake_packet_loss_percentage = 1;
+  ASSERT_TRUE(Initialize());
+  string large_body;
+  GenerateBody(&large_body, 10240);
+  int max_streams = 100;
+
+  AddToCache("GET", "/large_response", "HTTP/1.1", "200", "OK", large_body);;
+
+  client_->client()->WaitForCryptoHandshakeConfirmed();
+  // FLAGS_fake_packet_loss_percentage = 10;
+
+  for (int i = 0; i < max_streams; ++i) {
+    EXPECT_LT(0, client_->SendRequest("/large_response"));
+  }
+
+  // WaitForEvents waits 50ms and returns true if there are outstanding
+  // requests.
+  while (client_->client()->WaitForEvents() == true) {
+  }
+}
+
 class WrongAddressWriter : public QuicPacketWriter {
  public:
   explicit WrongAddressWriter(int fd) : fd_(fd) {
diff --git a/net/tools/quic/quic_spdy_client_stream.cc b/net/tools/quic/quic_spdy_client_stream.cc
index 6294953..368f93e 100644
--- a/net/tools/quic/quic_spdy_client_stream.cc
+++ b/net/tools/quic/quic_spdy_client_stream.cc
@@ -62,8 +62,13 @@
   SpdyHeaderBlock header_block =
       SpdyUtils::RequestHeadersToSpdyHeaders(headers);
 
-  string headers_string =
-      session()->compressor()->CompressHeaders(header_block);
+  string headers_string;
+  if (session()->connection()->version() >= QUIC_VERSION_9) {
+    headers_string = session()->compressor()->CompressHeadersWithPriority(
+        priority(), header_block);
+  } else {
+    headers_string = session()->compressor()->CompressHeaders(header_block);
+  }
 
   bool has_body = !body.empty();
 
diff --git a/net/tools/quic/quic_spdy_server_stream.cc b/net/tools/quic/quic_spdy_server_stream.cc
index d6f3b75..2ebe1dc 100644
--- a/net/tools/quic/quic_spdy_server_stream.cc
+++ b/net/tools/quic/quic_spdy_server_stream.cc
@@ -69,10 +69,11 @@
     const BalsaHeaders& response_headers) {
   SpdyHeaderBlock header_block =
       SpdyUtils::ResponseHeadersToSpdyHeaders(response_headers);
-  string headers =
-      session()->compressor()->CompressHeaders(header_block);
 
-  WriteData(headers, false);
+  string headers_string;
+  headers_string = session()->compressor()->CompressHeaders(header_block);
+
+  WriteData(headers_string, false);
 }
 
 int QuicSpdyServerStream::ParseRequestHeaders() {
diff --git a/net/tools/testserver/testserver.py b/net/tools/testserver/testserver.py
index d99c5ff..f343f70 100755
--- a/net/tools/testserver/testserver.py
+++ b/net/tools/testserver/testserver.py
@@ -300,6 +300,7 @@
       'jpg' : 'image/jpeg',
       'json': 'application/json',
       'pdf' : 'application/pdf',
+      'txt' : 'text/plain',
       'wav' : 'audio/wav',
       'xml' : 'text/xml'
     }
diff --git a/net/udp/udp_socket_libevent.cc b/net/udp/udp_socket_libevent.cc
index 90c7da6..5ed52f5 100644
--- a/net/udp/udp_socket_libevent.cc
+++ b/net/udp/udp_socket_libevent.cc
@@ -21,6 +21,7 @@
 #include "net/base/net_errors.h"
 #include "net/base/net_log.h"
 #include "net/base/net_util.h"
+#include "net/socket/socket_descriptor.h"
 #include "net/udp/udp_net_log_parameters.h"
 
 namespace {
@@ -381,7 +382,7 @@
 
 int UDPSocketLibevent::CreateSocket(const IPEndPoint& address) {
   addr_family_ = address.GetSockAddrFamily();
-  socket_ = socket(addr_family_, SOCK_DGRAM, 0);
+  socket_ = CreatePlatformSocket(addr_family_, SOCK_DGRAM, 0);
   if (socket_ == kInvalidSocket)
     return MapSystemError(errno);
   if (SetNonBlocking(socket_)) {
diff --git a/net/udp/udp_socket_libevent.h b/net/udp/udp_socket_libevent.h
index 8f68a9b..6c8bf61 100644
--- a/net/udp/udp_socket_libevent.h
+++ b/net/udp/udp_socket_libevent.h
@@ -15,6 +15,7 @@
 #include "net/base/net_export.h"
 #include "net/base/net_log.h"
 #include "net/base/rand_callback.h"
+#include "net/socket/socket_descriptor.h"
 #include "net/udp/datagram_socket.h"
 
 namespace net {
@@ -152,8 +153,6 @@
   int SetMulticastLoopbackMode(bool loopback);
 
  private:
-  static const int kInvalidSocket = -1;
-
   enum SocketOptions {
     SOCKET_OPTION_REUSE_ADDRESS  = 1 << 0,
     SOCKET_OPTION_BROADCAST      = 1 << 1,
diff --git a/net/udp/udp_socket_win.cc b/net/udp/udp_socket_win.cc
index 1f0c337..f9ce679 100644
--- a/net/udp/udp_socket_win.cc
+++ b/net/udp/udp_socket_win.cc
@@ -20,6 +20,7 @@
 #include "net/base/net_util.h"
 #include "net/base/winsock_init.h"
 #include "net/base/winsock_util.h"
+#include "net/socket/socket_descriptor.h"
 #include "net/udp/udp_net_log_parameters.h"
 
 namespace {
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 13462ff..1cb5f6f 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -5727,6 +5727,34 @@
             static_cast<bool>(cert_status & CERT_STATUS_REV_CHECKING_ENABLED));
 }
 
+TEST_F(HTTPSEVCRLSetTest, MissingCRLSetAndRevokedOCSP) {
+  if (!SystemSupportsOCSP()) {
+    LOG(WARNING) << "Skipping test because system doesn't support OCSP";
+    return;
+  }
+
+  SpawnedTestServer::SSLOptions ssl_options(
+      SpawnedTestServer::SSLOptions::CERT_AUTO);
+  ssl_options.ocsp_status = SpawnedTestServer::SSLOptions::OCSP_REVOKED;
+  SSLConfigService::SetCRLSet(scoped_refptr<CRLSet>());
+
+  CertStatus cert_status;
+  DoConnection(ssl_options, &cert_status);
+
+  // Currently only works for Windows. When using NSS or OS X, it's not
+  // possible to determine whether the check failed because of actual
+  // revocation or because there was an OCSP failure.
+#if defined(OS_WIN)
+  EXPECT_EQ(CERT_STATUS_REVOKED, cert_status & CERT_STATUS_ALL_ERRORS);
+#else
+  EXPECT_EQ(0u, cert_status & CERT_STATUS_ALL_ERRORS);
+#endif
+
+  EXPECT_FALSE(cert_status & CERT_STATUS_IS_EV);
+  EXPECT_EQ(SystemUsesChromiumEVMetadata(),
+            static_cast<bool>(cert_status & CERT_STATUS_REV_CHECKING_ENABLED));
+}
+
 TEST_F(HTTPSEVCRLSetTest, MissingCRLSetAndGoodOCSP) {
   if (!SystemSupportsOCSP()) {
     LOG(WARNING) << "Skipping test because system doesn't support OCSP";
diff --git a/net/websockets/websocket_channel.cc b/net/websockets/websocket_channel.cc
index fd845f9..3db457c 100644
--- a/net/websockets/websocket_channel.cc
+++ b/net/websockets/websocket_channel.cc
@@ -31,19 +31,6 @@
 // WebSocketFrameHeader::payload_length in websocket_frame.h.
 const uint64 kMaxControlFramePayload = 125;
 
-// Concatenate the data from two IOBufferWithSize objects into a single one.
-IOBufferWithSize* ConcatenateIOBuffers(
-    const scoped_refptr<IOBufferWithSize>& part1,
-    const scoped_refptr<IOBufferWithSize>& part2) {
-  int newsize = part1->size() + part2->size();
-  IOBufferWithSize* newbuffer = new IOBufferWithSize(newsize);
-  std::copy(part1->data(), part1->data() + part1->size(), newbuffer->data());
-  std::copy(part2->data(),
-            part2->data() + part2->size(),
-            newbuffer->data() + part1->size());
-  return newbuffer;
-}
-
 }  // namespace
 
 // A class to encapsulate a set of frames and information about the size of
@@ -90,10 +77,10 @@
   }
 
  private:
-  // A pointer to the WebSocketChannel that created us. We do not need to worry
-  // about this pointer being stale, because deleting WebSocketChannel cancels
-  // the connect process, deleting this object and preventing its callbacks from
-  // being called.
+  // A pointer to the WebSocketChannel that created this object. There is no
+  // danger of this pointer being stale, because deleting the WebSocketChannel
+  // cancels the connect process, deleting this object and preventing its
+  // callbacks from being called.
   WebSocketChannel* const creator_;
 
   DISALLOW_COPY_AND_ASSIGN(ConnectDelegate);
@@ -129,8 +116,8 @@
 }
 
 bool WebSocketChannel::InClosingState() const {
-  // We intentionally do not support state RECV_CLOSED here, because it is only
-  // used in one code path and should not leak into the code in general.
+  // The state RECV_CLOSED is not supported here, because it is only used in one
+  // code path and should not leak into the code in general.
   DCHECK_NE(RECV_CLOSED, state_)
       << "InClosingState called with state_ == RECV_CLOSED";
   return state_ == SEND_CLOSED || state_ == CLOSE_WAIT || state_ == CLOSED;
@@ -172,9 +159,9 @@
   }
   current_send_quota_ -= data.size();
   // TODO(ricea): If current_send_quota_ has dropped below
-  // send_quota_low_water_mark_, we may want to consider increasing the "low
-  // water mark" and "high water mark", but only if we think we are not
-  // saturating the link to the WebSocket server.
+  // send_quota_low_water_mark_, it might be good to increase the "low
+  // water mark" and "high water mark", but only if the link to the WebSocket
+  // server is not saturated.
   // TODO(ricea): For kOpCodeText, do UTF-8 validation?
   scoped_refptr<IOBufferWithSize> buffer(new IOBufferWithSize(data.size()));
   std::copy(data.begin(), data.end(), buffer->data());
@@ -241,7 +228,7 @@
   current_send_quota_ = send_quota_high_water_mark_;
   event_interface_->OnFlowControl(send_quota_high_water_mark_);
 
-  // We don't need this any more.
+  // |stream_request_| is not used once the connection has succeeded.
   stream_request_.reset();
   ReadFrames();
 }
@@ -256,8 +243,8 @@
 void WebSocketChannel::WriteFrames() {
   int result = OK;
   do {
-    // This use of base::Unretained is safe because we own the WebSocketStream
-    // and destroying it cancels all callbacks.
+    // This use of base::Unretained is safe because this object owns the
+    // WebSocketStream and destroying it cancels all callbacks.
     result = stream_->WriteFrames(
         data_being_sent_->frames(),
         base::Bind(
@@ -317,8 +304,9 @@
 void WebSocketChannel::ReadFrames() {
   int result = OK;
   do {
-    // This use of base::Unretained is safe because we own the WebSocketStream,
-    // and any pending reads will be cancelled when it is destroyed.
+    // This use of base::Unretained is safe because this object owns the
+    // WebSocketStream, and any pending reads will be cancelled when it is
+    // destroyed.
     result = stream_->ReadFrames(
         &read_frame_chunks_,
         base::Bind(
@@ -345,7 +333,7 @@
         ProcessFrameChunk(chunk.Pass());
       }
       read_frame_chunks_.clear();
-      // We need to always keep a call to ReadFrames pending.
+      // There should always be a call to ReadFrames pending.
       if (!synchronous && state_ != CLOSED) {
         ReadFrames();
       }
@@ -388,10 +376,11 @@
     }
   }
   if (!current_frame_header_) {
-    // If we rejected the previous chunk as invalid, then we will have reset
-    // current_frame_header_ to avoid using it. More chunks of the invalid frame
-    // may still arrive, so this is not necessarily a bug on our side. However,
-    // if this happens when state_ is CONNECTED, it is definitely a bug.
+    // If this channel rejected the previous chunk as invalid, then it will have
+    // reset |current_frame_header_| and closed the channel. More chunks of the
+    // invalid frame may still arrive, and it is not necessarily a bug for that
+    // to happen. However, if this happens when state_ is CONNECTED, it is
+    // definitely a bug.
     DCHECK(state_ != CONNECTED) << "Unexpected header-less frame received "
                                 << "(final_chunk = " << chunk->final_chunk
                                 << ", data size = " << chunk->data->size()
@@ -402,7 +391,7 @@
   data_buffer.swap(chunk->data);
   const bool is_final_chunk = chunk->final_chunk;
   chunk.reset();
-  WebSocketFrameHeader::OpCode opcode = current_frame_header_->opcode;
+  const WebSocketFrameHeader::OpCode opcode = current_frame_header_->opcode;
   if (WebSocketFrameHeader::IsKnownControlOpCode(opcode)) {
     if (!current_frame_header_->final) {
       FailChannel(SEND_REAL_ERROR,
@@ -419,32 +408,35 @@
     if (!is_final_chunk) {
       VLOG(2) << "Encountered a split control frame, opcode " << opcode;
       if (incomplete_control_frame_body_) {
-        // The really horrid case. We need to create a new IOBufferWithSize
-        // combining the new one and the old one. This should virtually never
-        // happen.
-        // TODO(ricea): This algorithm is O(N^2). Use a fixed 127-byte buffer
-        // instead.
-        VLOG(3) << "Hit the really horrid case";
-        incomplete_control_frame_body_ =
-            ConcatenateIOBuffers(incomplete_control_frame_body_, data_buffer);
+        VLOG(3) << "Appending to an existing split control frame.";
+        AddToIncompleteControlFrameBody(data_buffer);
       } else {
-        // The merely horrid case. Store the IOBufferWithSize to use when the
-        // rest of the control frame arrives.
-        incomplete_control_frame_body_.swap(data_buffer);
+        VLOG(3) << "Creating new storage for an incomplete control frame.";
+        incomplete_control_frame_body_ = new GrowableIOBuffer();
+        // This method checks for oversize control frames above, so as long as
+        // the frame parser is working correctly, this won't overflow. If a bug
+        // does cause it to overflow, it will CHECK() in
+        // AddToIncompleteControlFrameBody() without writing outside the buffer.
+        incomplete_control_frame_body_->SetCapacity(kMaxControlFramePayload);
+        AddToIncompleteControlFrameBody(data_buffer);
       }
       return;  // Handle when complete.
     }
     if (incomplete_control_frame_body_) {
       VLOG(2) << "Rejoining a split control frame, opcode " << opcode;
-      data_buffer =
-          ConcatenateIOBuffers(incomplete_control_frame_body_, data_buffer);
+      AddToIncompleteControlFrameBody(data_buffer);
+      const int body_size = incomplete_control_frame_body_->offset();
+      data_buffer = new IOBufferWithSize(body_size);
+      memcpy(data_buffer->data(),
+             incomplete_control_frame_body_->StartOfBuffer(),
+             body_size);
       incomplete_control_frame_body_ = NULL;  // Frame now complete.
     }
   }
 
   // Apply basic sanity checks to the |payload_length| field from the frame
-  // header. We can only apply a strict check when we know we have the whole
-  // frame in one chunk.
+  // header. A check for exact equality can only be used when the whole frame
+  // arrives in one chunk.
   DCHECK_GE(current_frame_header_->payload_length,
             base::checked_numeric_cast<uint64>(data_buffer->size()));
   DCHECK(!is_first_chunk || !is_final_chunk ||
@@ -455,11 +447,24 @@
   HandleFrame(opcode, is_first_chunk, is_final_chunk, data_buffer);
 
   if (is_final_chunk) {
-    // Make sure we do not apply this frame header to any future chunks.
+    // Make sure that this frame header is not applied to any future chunks.
     current_frame_header_.reset();
   }
 }
 
+void WebSocketChannel::AddToIncompleteControlFrameBody(
+    const scoped_refptr<IOBufferWithSize>& data_buffer) {
+  const int new_offset =
+      incomplete_control_frame_body_->offset() + data_buffer->size();
+  CHECK_GE(incomplete_control_frame_body_->capacity(), new_offset)
+      << "Control frame body larger than frame header indicates; frame parser "
+         "bug?";
+  memcpy(incomplete_control_frame_body_->data(),
+         data_buffer->data(),
+         data_buffer->size());
+  incomplete_control_frame_body_->set_offset(new_offset);
+}
+
 void WebSocketChannel::HandleFrame(
     const WebSocketFrameHeader::OpCode opcode,
     bool is_first_chunk,
@@ -496,8 +501,8 @@
         frame_name = "Unknown frame type";
         break;
     }
-    // SEND_REAL_ERROR makes no difference here, as we won't send another Close
-    // frame.
+    // SEND_REAL_ERROR makes no difference here, as FailChannel() won't send
+    // another Close frame.
     FailChannel(SEND_REAL_ERROR,
                 kWebSocketErrorProtocolError,
                 frame_name + " received after close");
@@ -515,12 +520,12 @@
         const char* const data_begin = data_buffer->data();
         const char* const data_end = data_begin + data_buffer->size();
         const std::vector<char> data(data_begin, data_end);
-        // TODO(ricea): Handle the (improbable) case when ReadFrames returns far
-        // more data at once than we want to send in a single IPC (in which case
-        // we need to buffer the data and return to the event loop with a
-        // callback to send the rest in 32K chunks).
+        // TODO(ricea): Handle the case when ReadFrames returns far
+        // more data at once than should be sent in a single IPC. This needs to
+        // be handled carefully, as an overloaded IO thread is one possible
+        // cause of receiving very large chunks.
 
-        // Send the received frame to the renderer process.
+        // Sends the received frame to the renderer process.
         event_interface_->OnDataFrame(
             final,
             is_first_chunk ? opcode : WebSocketFrameHeader::kOpCodeContinuation,
@@ -542,7 +547,7 @@
 
     case WebSocketFrameHeader::kOpCodePong:
       VLOG(1) << "Got Pong of size " << data_buffer->size();
-      // We do not need to do anything with pong messages.
+      // There is no need to do anything with pong messages.
       return;
 
     case WebSocketFrameHeader::kOpCodeClose: {
@@ -599,10 +604,10 @@
   chunk->final_chunk = true;
   chunk->data = buffer;
   if (data_being_sent_) {
-    // Either the link to the WebSocket server is saturated, or we are simply
-    // processing a batch of messages.
-    // TODO(ricea): We need to keep some statistics to work out which situation
-    // we are in and adjust quota appropriately.
+    // Either the link to the WebSocket server is saturated, or several messages
+    // are being sent in a batch.
+    // TODO(ricea): Keep some statistics to work out the situation and adjust
+    // quota appropriately.
     if (!data_to_send_next_)
       data_to_send_next_.reset(new SendBuffer);
     data_to_send_next_->AddFrame(chunk.Pass());
@@ -629,13 +634,14 @@
     }
     SendClose(send_code, send_reason);  // Sets state_ to SEND_CLOSED
   }
-  // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates we should close
-  // the connection ourselves without waiting for the closing handshake.
+  // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates the browser
+  // should close the connection itself without waiting for the closing
+  // handshake.
   stream_->Close();
   state_ = CLOSED;
 
-  // We may be in the middle of processing several chunks. We should not re-use
-  // the frame header.
+  // The channel may be in the middle of processing several chunks. It should
+  // not use this frame header for subsequent chunks.
   current_frame_header_.reset();
   if (old_state != CLOSED) {
     event_interface_->OnDropChannel(code, reason);
diff --git a/net/websockets/websocket_channel.h b/net/websockets/websocket_channel.h
index d81f83a..c997d6a 100644
--- a/net/websockets/websocket_channel.h
+++ b/net/websockets/websocket_channel.h
@@ -19,8 +19,10 @@
 
 namespace net {
 
+class GrowableIOBuffer;
 class URLRequestContext;
 class WebSocketEventInterface;
+class BoundNetLog;
 
 // Transport-independent implementation of WebSockets. Implements protocol
 // semantics that do not depend on the underlying transport. Provides the
@@ -72,7 +74,7 @@
   // send up to |quota| units of data.
   void SendFlowControl(int64 quota);
 
-  // Start the closing handshake for a client-initiated shutdown of the
+  // Starts the closing handshake for a client-initiated shutdown of the
   // connection. There is no API to close the connection without a closing
   // handshake, but destroying the WebSocketChannel object while connected will
   // effectively do that. |code| must be in the range 1000-4999. |reason| should
@@ -92,16 +94,16 @@
       const WebSocketStreamFactory& factory);
 
  private:
-  // We have a simple linear progression of states from FRESHLY_CONSTRUCTED to
-  // CLOSED, except that the SEND_CLOSED and RECV_CLOSED states may be skipped
-  // in case of error.
+  // The object passes through a linear progression of states from
+  // FRESHLY_CONSTRUCTED to CLOSED, except that the SEND_CLOSED and RECV_CLOSED
+  // states may be skipped in case of error.
   enum State {
     FRESHLY_CONSTRUCTED,
     CONNECTING,
     CONNECTED,
-    SEND_CLOSED,  // We have sent a Close frame but not received a Close frame.
+    SEND_CLOSED,  // A Close frame has been sent but not received.
     RECV_CLOSED,  // Used briefly between receiving a Close frame and sending
-                  // the response. Once we have responded, the state changes
+                  // the response. Once the response is sent, the state changes
                   // to CLOSED.
     CLOSE_WAIT,   // The Closing Handshake has completed, but the remote server
                   // has not yet closed the connection.
@@ -109,17 +111,18 @@
                   // has been closed; or the connection is failed.
   };
 
-  // When failing a channel, we may or may not want to send the real reason for
-  // failing to the remote server. This enum is used by FailChannel() to
-  // choose.
+  // When failing a channel, sometimes it is inappropriate to expose the real
+  // reason for failing to the remote server. This enum is used by FailChannel()
+  // to select between sending the real status or a "Going Away" status.
   enum ExposeError {
     SEND_REAL_ERROR,
     SEND_GOING_AWAY,
   };
 
-  // Our implementation of WebSocketStream::ConnectDelegate. We do not inherit
-  // from WebSocketStream::ConnectDelegate directly to avoid cluttering our
-  // public interface with the implementation of those methods, and because the
+  // Implementation of WebSocketStream::ConnectDelegate for
+  // WebSocketChannel. WebSocketChannel does not inherit from
+  // WebSocketStream::ConnectDelegate directly to avoid cluttering the public
+  // interface with the implementation of those methods, and because the
   // lifetime of a WebSocketChannel is longer than the lifetime of the
   // connection process.
   class ConnectDelegate;
@@ -164,7 +167,11 @@
   // Processes a single chunk that has been read from the stream.
   void ProcessFrameChunk(scoped_ptr<WebSocketFrameChunk> chunk);
 
-  // Handle a frame that we have received enough of to process. May call
+  // Appends |data_buffer| to |incomplete_control_frame_body_|.
+  void AddToIncompleteControlFrameBody(
+      const scoped_refptr<IOBufferWithSize>& data_buffer);
+
+  // Handles a frame that the object has received enough of to process. May call
   // event_interface_ methods, send responses to the server, and change the
   // value of state_.
   void HandleFrame(const WebSocketFrameHeader::OpCode opcode,
@@ -181,13 +188,13 @@
                             WebSocketFrameHeader::OpCode op_code,
                             const scoped_refptr<IOBufferWithSize>& buffer);
 
-  // Perform the "Fail the WebSocket Connection" operation as defined in
+  // Performs the "Fail the WebSocket Connection" operation as defined in
   // RFC6455. The supplied code and reason are sent back to the renderer in an
   // OnDropChannel message. If state_ is CONNECTED then a Close message is sent
   // to the remote host. If |expose| is SEND_REAL_ERROR then the remote host is
-  // given the same status code we gave the renderer; otherwise it is sent a
-  // fixed "Going Away" code.  Resets current_frame_header_, closes the
-  // stream_, and sets state_ to CLOSED.
+  // given the same status code passed to the renderer; otherwise it is sent a
+  // fixed "Going Away" code.  Resets current_frame_header_, closes the stream_,
+  // and sets state_ to CLOSED.
   void FailChannel(ExposeError expose, uint16 code, const std::string& reason);
 
   // Sends a Close frame to Start the WebSocket Closing Handshake, or to respond
@@ -205,13 +212,13 @@
                   uint16* code,
                   std::string* reason);
 
-  // The URL to which we connect.
+  // The URL of the remote server.
   const GURL socket_url_;
 
   // The object receiving events.
   const scoped_ptr<WebSocketEventInterface> event_interface_;
 
-  // The WebSocketStream to which we are sending/receiving data.
+  // The WebSocketStream on which to send and receive data.
   scoped_ptr<WebSocketStream> stream_;
 
   // A data structure containing a vector of frames to be sent and the total
@@ -226,32 +233,32 @@
   // Destination for the current call to WebSocketStream::ReadFrames
   ScopedVector<WebSocketFrameChunk> read_frame_chunks_;
   // Frame header for the frame currently being received. Only non-NULL while we
-  // are processing the frame. If the frame arrives in multiple chunks, can
-  // remain non-NULL while we wait for additional chunks to arrive. If the
-  // header of the frame was invalid, this is set to NULL, the channel is
-  // failed, and subsequent chunks of the same frame will be ignored.
+  // are processing the frame. If the frame arrives in multiple chunks, it can
+  // remain non-NULL until additional chunks arrive. If the header of the frame
+  // was invalid, this is set to NULL, the channel is failed, and subsequent
+  // chunks of the same frame will be ignored.
   scoped_ptr<WebSocketFrameHeader> current_frame_header_;
   // Handle to an in-progress WebSocketStream creation request. Only non-NULL
   // during the connection process.
   scoped_ptr<WebSocketStreamRequest> stream_request_;
-  // Although it will almost never happen in practice, we can be passed an
-  // incomplete control frame, in which case we need to keep the data around
-  // long enough to reassemble it. This variable will be NULL the rest of the
-  // time.
-  scoped_refptr<IOBufferWithSize> incomplete_control_frame_body_;
-  // The point at which we give the renderer a quota refresh (quota units).
-  // "quota units" are currently bytes. TODO(ricea): Update the definition of
-  // quota units when necessary.
+  // Although it should rarely happen in practice, a control frame can arrive
+  // broken into chunks. This variable provides storage for a partial control
+  // frame until the rest arrives. It will be NULL the rest of the time.
+  scoped_refptr<GrowableIOBuffer> incomplete_control_frame_body_;
+  // If the renderer's send quota reaches this level, it is sent a quota
+  // refresh. "quota units" are currently bytes. TODO(ricea): Update the
+  // definition of quota units when necessary.
   int send_quota_low_water_mark_;
-  // The amount which we refresh the quota to when it reaches the
-  // low_water_mark (quota units).
+  // The level the quota is refreshed to when it reaches the low_water_mark
+  // (quota units).
   int send_quota_high_water_mark_;
   // The current amount of quota that the renderer has available for sending
   // on this logical channel (quota units).
   int current_send_quota_;
 
-  // Storage for the status code and reason from the time we receive the Close
-  // frame until the connection is closed and we can call OnDropChannel().
+  // Storage for the status code and reason from the time the Close frame
+  // arrives until the connection is closed and they are passed to
+  // OnDropChannel().
   uint16 closing_code_;
   std::string closing_reason_;
 
diff --git a/ppapi/api/private/ppb_nacl_private.idl b/ppapi/api/private/ppb_nacl_private.idl
index 9a67404..755b8d7 100644
--- a/ppapi/api/private/ppb_nacl_private.idl
+++ b/ppapi/api/private/ppb_nacl_private.idl
@@ -6,6 +6,10 @@
 /* This file contains NaCl private interfaces. This interface is not versioned
  * and is for internal Chrome use. It may change without notice. */
 
+label Chrome {
+  M25 = 1.0
+};
+
 #inline c
 #include "ppapi/c/private/pp_file_handle.h"
 #include "ppapi/c/private/ppb_instance_private.h"
diff --git a/ppapi/native_client/src/trusted/plugin/pnacl_coordinator.cc b/ppapi/native_client/src/trusted/plugin/pnacl_coordinator.cc
index 4362ec4..10311a9 100644
--- a/ppapi/native_client/src/trusted/plugin/pnacl_coordinator.cc
+++ b/ppapi/native_client/src/trusted/plugin/pnacl_coordinator.cc
@@ -672,8 +672,9 @@
   if (pp_error != PP_OK) {
     ReportNonPpapiError(
         ERROR_PNACL_RESOURCE_FETCH,
-        nacl::string("The Portable Native Client component is not installed"
-                     " or has been disabled."));
+        nacl::string("The Portable Native Client (pnacl) component is not "
+                     "installed. Please consult chrome://components for more "
+                     "information."));
     return;
   }
 
diff --git a/ppapi/native_client/src/trusted/plugin/pnacl_resources.cc b/ppapi/native_client/src/trusted/plugin/pnacl_resources.cc
index 6ba90c8..d712bc4 100644
--- a/ppapi/native_client/src/trusted/plugin/pnacl_resources.cc
+++ b/ppapi/native_client/src/trusted/plugin/pnacl_resources.cc
@@ -121,8 +121,9 @@
     // File-open failed. Assume this means that the file is
     // not actually installed.
     ReadResourceInfoError(
-        nacl::string("The Portable Native Client component is not installed"
-                     " or has been disabled."));
+        nacl::string("The Portable Native Client (pnacl) component is not "
+                     "installed. Please consult chrome://components for more "
+                     "information."));
     return;
   }
 
@@ -222,8 +223,9 @@
       // ReadResourceInfo() should happen first, and error out.
       coordinator_->ReportNonPpapiError(
           ERROR_PNACL_RESOURCE_FETCH,
-          nacl::string("The Portable Native Client component is not installed "
-                       "or has been disabled. Cannot open file: ") + filename);
+        nacl::string("The Portable Native Client (pnacl) component is not "
+                     "installed. Please consult chrome://components for more "
+                     "information."));
       result = PP_ERROR_FILENOTFOUND;
       break;
     } else {
diff --git a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c
index 93ccb85..1578e6c 100644
--- a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c
+++ b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c
@@ -2806,72 +2806,72 @@
 
 /* Begin wrapper methods for PPB_NaCl_Private_1_0 */
 
-static PP_ExternalPluginResult Pnacl_M13_PPB_NaCl_Private_LaunchSelLdr(PP_Instance instance, const char* alleged_url, PP_Bool uses_irt, PP_Bool uses_ppapi, PP_Bool enable_ppapi_dev, PP_Bool enable_dyncode_syscalls, PP_Bool enable_exception_handling, PP_Bool enable_crash_throttling, void* imc_handle, struct PP_Var* error_message) {
+static PP_ExternalPluginResult Pnacl_M25_PPB_NaCl_Private_LaunchSelLdr(PP_Instance instance, const char* alleged_url, PP_Bool uses_irt, PP_Bool uses_ppapi, PP_Bool enable_ppapi_dev, PP_Bool enable_dyncode_syscalls, PP_Bool enable_exception_handling, PP_Bool enable_crash_throttling, void* imc_handle, struct PP_Var* error_message) {
   const struct PPB_NaCl_Private_1_0 *iface = Pnacl_WrapperInfo_PPB_NaCl_Private_1_0.real_iface;
   return iface->LaunchSelLdr(instance, alleged_url, uses_irt, uses_ppapi, enable_ppapi_dev, enable_dyncode_syscalls, enable_exception_handling, enable_crash_throttling, imc_handle, error_message);
 }
 
-static PP_ExternalPluginResult Pnacl_M13_PPB_NaCl_Private_StartPpapiProxy(PP_Instance instance) {
+static PP_ExternalPluginResult Pnacl_M25_PPB_NaCl_Private_StartPpapiProxy(PP_Instance instance) {
   const struct PPB_NaCl_Private_1_0 *iface = Pnacl_WrapperInfo_PPB_NaCl_Private_1_0.real_iface;
   return iface->StartPpapiProxy(instance);
 }
 
-static int32_t Pnacl_M13_PPB_NaCl_Private_UrandomFD(void) {
+static int32_t Pnacl_M25_PPB_NaCl_Private_UrandomFD(void) {
   const struct PPB_NaCl_Private_1_0 *iface = Pnacl_WrapperInfo_PPB_NaCl_Private_1_0.real_iface;
   return iface->UrandomFD();
 }
 
-static PP_Bool Pnacl_M13_PPB_NaCl_Private_Are3DInterfacesDisabled(void) {
+static PP_Bool Pnacl_M25_PPB_NaCl_Private_Are3DInterfacesDisabled(void) {
   const struct PPB_NaCl_Private_1_0 *iface = Pnacl_WrapperInfo_PPB_NaCl_Private_1_0.real_iface;
   return iface->Are3DInterfacesDisabled();
 }
 
-static int32_t Pnacl_M13_PPB_NaCl_Private_BrokerDuplicateHandle(PP_FileHandle source_handle, uint32_t process_id, PP_FileHandle* target_handle, uint32_t desired_access, uint32_t options) {
+static int32_t Pnacl_M25_PPB_NaCl_Private_BrokerDuplicateHandle(PP_FileHandle source_handle, uint32_t process_id, PP_FileHandle* target_handle, uint32_t desired_access, uint32_t options) {
   const struct PPB_NaCl_Private_1_0 *iface = Pnacl_WrapperInfo_PPB_NaCl_Private_1_0.real_iface;
   return iface->BrokerDuplicateHandle(source_handle, process_id, target_handle, desired_access, options);
 }
 
-static int32_t Pnacl_M13_PPB_NaCl_Private_EnsurePnaclInstalled(PP_Instance instance, struct PP_CompletionCallback* callback) {
+static int32_t Pnacl_M25_PPB_NaCl_Private_EnsurePnaclInstalled(PP_Instance instance, struct PP_CompletionCallback* callback) {
   const struct PPB_NaCl_Private_1_0 *iface = Pnacl_WrapperInfo_PPB_NaCl_Private_1_0.real_iface;
   return iface->EnsurePnaclInstalled(instance, *callback);
 }
 
-static PP_FileHandle Pnacl_M13_PPB_NaCl_Private_GetReadonlyPnaclFd(const char* filename) {
+static PP_FileHandle Pnacl_M25_PPB_NaCl_Private_GetReadonlyPnaclFd(const char* filename) {
   const struct PPB_NaCl_Private_1_0 *iface = Pnacl_WrapperInfo_PPB_NaCl_Private_1_0.real_iface;
   return iface->GetReadonlyPnaclFd(filename);
 }
 
-static PP_FileHandle Pnacl_M13_PPB_NaCl_Private_CreateTemporaryFile(PP_Instance instance) {
+static PP_FileHandle Pnacl_M25_PPB_NaCl_Private_CreateTemporaryFile(PP_Instance instance) {
   const struct PPB_NaCl_Private_1_0 *iface = Pnacl_WrapperInfo_PPB_NaCl_Private_1_0.real_iface;
   return iface->CreateTemporaryFile(instance);
 }
 
-static int32_t Pnacl_M13_PPB_NaCl_Private_GetNexeFd(PP_Instance instance, const char* pexe_url, uint32_t abi_version, uint32_t opt_level, const char* last_modified, const char* etag, PP_Bool* is_hit, PP_FileHandle* nexe_handle, struct PP_CompletionCallback* callback) {
+static int32_t Pnacl_M25_PPB_NaCl_Private_GetNexeFd(PP_Instance instance, const char* pexe_url, uint32_t abi_version, uint32_t opt_level, const char* last_modified, const char* etag, PP_Bool* is_hit, PP_FileHandle* nexe_handle, struct PP_CompletionCallback* callback) {
   const struct PPB_NaCl_Private_1_0 *iface = Pnacl_WrapperInfo_PPB_NaCl_Private_1_0.real_iface;
   return iface->GetNexeFd(instance, pexe_url, abi_version, opt_level, last_modified, etag, is_hit, nexe_handle, *callback);
 }
 
-static void Pnacl_M13_PPB_NaCl_Private_ReportTranslationFinished(PP_Instance instance, PP_Bool success) {
+static void Pnacl_M25_PPB_NaCl_Private_ReportTranslationFinished(PP_Instance instance, PP_Bool success) {
   const struct PPB_NaCl_Private_1_0 *iface = Pnacl_WrapperInfo_PPB_NaCl_Private_1_0.real_iface;
   iface->ReportTranslationFinished(instance, success);
 }
 
-static PP_Bool Pnacl_M13_PPB_NaCl_Private_IsOffTheRecord(void) {
+static PP_Bool Pnacl_M25_PPB_NaCl_Private_IsOffTheRecord(void) {
   const struct PPB_NaCl_Private_1_0 *iface = Pnacl_WrapperInfo_PPB_NaCl_Private_1_0.real_iface;
   return iface->IsOffTheRecord();
 }
 
-static PP_Bool Pnacl_M13_PPB_NaCl_Private_IsPnaclEnabled(void) {
+static PP_Bool Pnacl_M25_PPB_NaCl_Private_IsPnaclEnabled(void) {
   const struct PPB_NaCl_Private_1_0 *iface = Pnacl_WrapperInfo_PPB_NaCl_Private_1_0.real_iface;
   return iface->IsPnaclEnabled();
 }
 
-static PP_ExternalPluginResult Pnacl_M13_PPB_NaCl_Private_ReportNaClError(PP_Instance instance, PP_NaClError message_id) {
+static PP_ExternalPluginResult Pnacl_M25_PPB_NaCl_Private_ReportNaClError(PP_Instance instance, PP_NaClError message_id) {
   const struct PPB_NaCl_Private_1_0 *iface = Pnacl_WrapperInfo_PPB_NaCl_Private_1_0.real_iface;
   return iface->ReportNaClError(instance, message_id);
 }
 
-static PP_FileHandle Pnacl_M13_PPB_NaCl_Private_OpenNaClExecutable(PP_Instance instance, const char* file_url, uint64_t* file_token_lo, uint64_t* file_token_hi) {
+static PP_FileHandle Pnacl_M25_PPB_NaCl_Private_OpenNaClExecutable(PP_Instance instance, const char* file_url, uint64_t* file_token_lo, uint64_t* file_token_hi) {
   const struct PPB_NaCl_Private_1_0 *iface = Pnacl_WrapperInfo_PPB_NaCl_Private_1_0.real_iface;
   return iface->OpenNaClExecutable(instance, file_url, file_token_lo, file_token_hi);
 }
@@ -4650,20 +4650,20 @@
 };
 
 struct PPB_NaCl_Private_1_0 Pnacl_Wrappers_PPB_NaCl_Private_1_0 = {
-    .LaunchSelLdr = (PP_ExternalPluginResult (*)(PP_Instance instance, const char* alleged_url, PP_Bool uses_irt, PP_Bool uses_ppapi, PP_Bool enable_ppapi_dev, PP_Bool enable_dyncode_syscalls, PP_Bool enable_exception_handling, PP_Bool enable_crash_throttling, void* imc_handle, struct PP_Var* error_message))&Pnacl_M13_PPB_NaCl_Private_LaunchSelLdr,
-    .StartPpapiProxy = (PP_ExternalPluginResult (*)(PP_Instance instance))&Pnacl_M13_PPB_NaCl_Private_StartPpapiProxy,
-    .UrandomFD = (int32_t (*)(void))&Pnacl_M13_PPB_NaCl_Private_UrandomFD,
-    .Are3DInterfacesDisabled = (PP_Bool (*)(void))&Pnacl_M13_PPB_NaCl_Private_Are3DInterfacesDisabled,
-    .BrokerDuplicateHandle = (int32_t (*)(PP_FileHandle source_handle, uint32_t process_id, PP_FileHandle* target_handle, uint32_t desired_access, uint32_t options))&Pnacl_M13_PPB_NaCl_Private_BrokerDuplicateHandle,
-    .EnsurePnaclInstalled = (int32_t (*)(PP_Instance instance, struct PP_CompletionCallback callback))&Pnacl_M13_PPB_NaCl_Private_EnsurePnaclInstalled,
-    .GetReadonlyPnaclFd = (PP_FileHandle (*)(const char* filename))&Pnacl_M13_PPB_NaCl_Private_GetReadonlyPnaclFd,
-    .CreateTemporaryFile = (PP_FileHandle (*)(PP_Instance instance))&Pnacl_M13_PPB_NaCl_Private_CreateTemporaryFile,
-    .GetNexeFd = (int32_t (*)(PP_Instance instance, const char* pexe_url, uint32_t abi_version, uint32_t opt_level, const char* last_modified, const char* etag, PP_Bool* is_hit, PP_FileHandle* nexe_handle, struct PP_CompletionCallback callback))&Pnacl_M13_PPB_NaCl_Private_GetNexeFd,
-    .ReportTranslationFinished = (void (*)(PP_Instance instance, PP_Bool success))&Pnacl_M13_PPB_NaCl_Private_ReportTranslationFinished,
-    .IsOffTheRecord = (PP_Bool (*)(void))&Pnacl_M13_PPB_NaCl_Private_IsOffTheRecord,
-    .IsPnaclEnabled = (PP_Bool (*)(void))&Pnacl_M13_PPB_NaCl_Private_IsPnaclEnabled,
-    .ReportNaClError = (PP_ExternalPluginResult (*)(PP_Instance instance, PP_NaClError message_id))&Pnacl_M13_PPB_NaCl_Private_ReportNaClError,
-    .OpenNaClExecutable = (PP_FileHandle (*)(PP_Instance instance, const char* file_url, uint64_t* file_token_lo, uint64_t* file_token_hi))&Pnacl_M13_PPB_NaCl_Private_OpenNaClExecutable
+    .LaunchSelLdr = (PP_ExternalPluginResult (*)(PP_Instance instance, const char* alleged_url, PP_Bool uses_irt, PP_Bool uses_ppapi, PP_Bool enable_ppapi_dev, PP_Bool enable_dyncode_syscalls, PP_Bool enable_exception_handling, PP_Bool enable_crash_throttling, void* imc_handle, struct PP_Var* error_message))&Pnacl_M25_PPB_NaCl_Private_LaunchSelLdr,
+    .StartPpapiProxy = (PP_ExternalPluginResult (*)(PP_Instance instance))&Pnacl_M25_PPB_NaCl_Private_StartPpapiProxy,
+    .UrandomFD = (int32_t (*)(void))&Pnacl_M25_PPB_NaCl_Private_UrandomFD,
+    .Are3DInterfacesDisabled = (PP_Bool (*)(void))&Pnacl_M25_PPB_NaCl_Private_Are3DInterfacesDisabled,
+    .BrokerDuplicateHandle = (int32_t (*)(PP_FileHandle source_handle, uint32_t process_id, PP_FileHandle* target_handle, uint32_t desired_access, uint32_t options))&Pnacl_M25_PPB_NaCl_Private_BrokerDuplicateHandle,
+    .EnsurePnaclInstalled = (int32_t (*)(PP_Instance instance, struct PP_CompletionCallback callback))&Pnacl_M25_PPB_NaCl_Private_EnsurePnaclInstalled,
+    .GetReadonlyPnaclFd = (PP_FileHandle (*)(const char* filename))&Pnacl_M25_PPB_NaCl_Private_GetReadonlyPnaclFd,
+    .CreateTemporaryFile = (PP_FileHandle (*)(PP_Instance instance))&Pnacl_M25_PPB_NaCl_Private_CreateTemporaryFile,
+    .GetNexeFd = (int32_t (*)(PP_Instance instance, const char* pexe_url, uint32_t abi_version, uint32_t opt_level, const char* last_modified, const char* etag, PP_Bool* is_hit, PP_FileHandle* nexe_handle, struct PP_CompletionCallback callback))&Pnacl_M25_PPB_NaCl_Private_GetNexeFd,
+    .ReportTranslationFinished = (void (*)(PP_Instance instance, PP_Bool success))&Pnacl_M25_PPB_NaCl_Private_ReportTranslationFinished,
+    .IsOffTheRecord = (PP_Bool (*)(void))&Pnacl_M25_PPB_NaCl_Private_IsOffTheRecord,
+    .IsPnaclEnabled = (PP_Bool (*)(void))&Pnacl_M25_PPB_NaCl_Private_IsPnaclEnabled,
+    .ReportNaClError = (PP_ExternalPluginResult (*)(PP_Instance instance, PP_NaClError message_id))&Pnacl_M25_PPB_NaCl_Private_ReportNaClError,
+    .OpenNaClExecutable = (PP_FileHandle (*)(PP_Instance instance, const char* file_url, uint64_t* file_token_lo, uint64_t* file_token_hi))&Pnacl_M25_PPB_NaCl_Private_OpenNaClExecutable
 };
 
 struct PPB_NetAddress_Private_0_1 Pnacl_Wrappers_PPB_NetAddress_Private_0_1 = {
diff --git a/ppapi/native_client/tools/browser_tester/browser_tester.py b/ppapi/native_client/tools/browser_tester/browser_tester.py
index ab24d0c..c01f4e3c 100755
--- a/ppapi/native_client/tools/browser_tester/browser_tester.py
+++ b/ppapi/native_client/tools/browser_tester/browser_tester.py
@@ -73,6 +73,10 @@
   parser.add_option('--ppapi_plugin', dest='ppapi_plugin', action='store',
                     type='string', default=None,
                     help='Use the browser plugin located here.')
+  parser.add_option('--ppapi_plugin_mimetype', dest='ppapi_plugin_mimetype',
+                    action='store', type='string', default='application/x-nacl',
+                    help='Associate this mimetype with the browser plugin. '
+                    'Unused if --ppapi_plugin is not specified.')
   parser.add_option('--sel_ldr', dest='sel_ldr', action='store',
                     type='string', default=None,
                     help='Use the sel_ldr located here.')
diff --git a/ppapi/native_client/tools/browser_tester/browsertester/browserlauncher.py b/ppapi/native_client/tools/browser_tester/browsertester/browserlauncher.py
index 5631690..67ea291 100755
--- a/ppapi/native_client/tools/browser_tester/browsertester/browserlauncher.py
+++ b/ppapi/native_client/tools/browser_tester/browsertester/browserlauncher.py
@@ -304,8 +304,9 @@
       if disable_sandbox:
         cmd.append('--no-sandbox')
     else:
-      cmd.append('--register-pepper-plugins=%s;application/x-nacl'
-                 % self.options.ppapi_plugin)
+      cmd.append('--register-pepper-plugins=%s;%s'
+                 % (self.options.ppapi_plugin,
+                    self.options.ppapi_plugin_mimetype))
       cmd.append('--no-sandbox')
     if self.options.browser_extensions:
       cmd.append('--load-extension=%s' %
diff --git a/ppapi/ppapi_proxy.gypi b/ppapi/ppapi_proxy.gypi
index 884dc15..52b97e9 100644
--- a/ppapi/ppapi_proxy.gypi
+++ b/ppapi/ppapi_proxy.gypi
@@ -33,10 +33,12 @@
           'proxy/dispatcher.cc',
           'proxy/dispatcher.h',
           'proxy/enter_proxy.h',
-          'proxy/extensions_common_resource.cc',
-          'proxy/extensions_common_resource.h',
+          'proxy/error_conversion.cc',
+          'proxy/error_conversion.h',
           'proxy/ext_crx_file_system_private_resource.cc',
           'proxy/ext_crx_file_system_private_resource.h',
+          'proxy/extensions_common_resource.cc',
+          'proxy/extensions_common_resource.h',
           'proxy/file_chooser_resource.cc',
           'proxy/file_chooser_resource.h',
           'proxy/file_ref_resource.cc',
@@ -101,8 +103,8 @@
           'proxy/plugin_var_serialization_rules.h',
           'proxy/plugin_var_tracker.cc',
           'proxy/plugin_var_tracker.h',
-          'proxy/ppapi_command_buffer_proxy.h',
           'proxy/ppapi_command_buffer_proxy.cc',
+          'proxy/ppapi_command_buffer_proxy.h',
           'proxy/ppapi_messages.h',
           'proxy/ppapi_message_utils.h',
           'proxy/ppb_audio_proxy.cc',
@@ -127,10 +129,6 @@
           'proxy/ppb_message_loop_proxy.h',
           'proxy/ppb_network_monitor_private_proxy.cc',
           'proxy/ppb_network_monitor_private_proxy.h',
-          'proxy/ppb_tcp_socket_proxy.cc',
-          'proxy/ppb_tcp_socket_proxy.h',
-          'proxy/ppb_tcp_socket_private_proxy.cc',
-          'proxy/ppb_tcp_socket_private_proxy.h',
           'proxy/ppb_testing_proxy.cc',
           'proxy/ppb_testing_proxy.h',
           'proxy/ppb_var_deprecated_proxy.cc',
@@ -178,6 +176,12 @@
           'proxy/talk_resource.h',
           'proxy/tcp_server_socket_private_resource.cc',
           'proxy/tcp_server_socket_private_resource.h',
+          'proxy/tcp_socket_private_resource.cc',
+          'proxy/tcp_socket_private_resource.h',
+          'proxy/tcp_socket_resource.cc',
+          'proxy/tcp_socket_resource.h',
+          'proxy/tcp_socket_resource_base.cc',
+          'proxy/tcp_socket_resource_base.h',
           'proxy/truetype_font_resource.cc',
           'proxy/truetype_font_resource.h',
           'proxy/truetype_font_singleton_resource.cc',
diff --git a/ppapi/ppapi_shared.gypi b/ppapi/ppapi_shared.gypi
index 1160143..1d0e44f 100644
--- a/ppapi/ppapi_shared.gypi
+++ b/ppapi/ppapi_shared.gypi
@@ -99,8 +99,6 @@
           'shared_impl/scoped_pp_var.h',
           'shared_impl/socket_option_data.cc',
           'shared_impl/socket_option_data.h',
-          'shared_impl/tcp_socket_shared.cc',
-          'shared_impl/tcp_socket_shared.h',
           'shared_impl/thread_aware_callback.cc',
           'shared_impl/thread_aware_callback.h',
           'shared_impl/time_conversion.cc',
@@ -126,8 +124,6 @@
           'shared_impl/private/ppb_char_set_shared.h',
           'shared_impl/private/ppb_x509_certificate_private_shared.cc',
           'shared_impl/private/ppb_x509_certificate_private_shared.h',
-          'shared_impl/private/tcp_socket_private_impl.cc',
-          'shared_impl/private/tcp_socket_private_impl.h',
 
           'thunk/enter.cc',
           'thunk/enter.h',
@@ -323,8 +319,6 @@
               'shared_impl/ppb_opengles2_shared.cc',
               'shared_impl/private/ppb_host_resolver_shared.cc',
               'shared_impl/private/net_address_private_impl.cc',
-              'shared_impl/private/tcp_socket_private_impl.cc',
-              'shared_impl/private/udp_socket_private_impl.cc',
               'thunk/ppb_graphics_3d_thunk.cc',
               'thunk/ppb_host_resolver_private_thunk.cc',
               'thunk/ppb_network_list_private_thunk.cc',
diff --git a/ppapi/proxy/error_conversion.cc b/ppapi/proxy/error_conversion.cc
new file mode 100644
index 0000000..f3587ec
--- /dev/null
+++ b/ppapi/proxy/error_conversion.cc
@@ -0,0 +1,26 @@
+// 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 "ppapi/proxy/error_conversion.h"
+
+#include "ppapi/c/pp_errors.h"
+
+namespace ppapi {
+namespace proxy {
+
+int32_t ConvertNetworkAPIErrorForCompatibility(int32_t pp_error,
+                                               bool private_api) {
+  // The private API doesn't return network-specific error codes or
+  // PP_ERROR_NOACCESS. In order to preserve the behavior, we convert those to
+  // PP_ERROR_FAILED.
+  if (private_api &&
+      (pp_error <= PP_ERROR_CONNECTION_CLOSED ||
+       pp_error == PP_ERROR_NOACCESS)) {
+    return PP_ERROR_FAILED;
+  }
+  return pp_error;
+}
+
+}  // namespace proxy
+}  // namespace ppapi
diff --git a/ppapi/proxy/error_conversion.h b/ppapi/proxy/error_conversion.h
new file mode 100644
index 0000000..84bfb13
--- /dev/null
+++ b/ppapi/proxy/error_conversion.h
@@ -0,0 +1,24 @@
+// 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 PPAPI_PROXY_ERROR_CONVERSION_H_
+#define PPAPI_PROXY_ERROR_CONVERSION_H_
+
+#include "ppapi/c/pp_stdint.h"
+#include "ppapi/proxy/ppapi_proxy_export.h"
+
+namespace ppapi {
+namespace proxy {
+
+// When |private_api| is true, coverts all network-related errors +;
+// PP_ERROR_NOACCESS to PP_ERROR_FAILED. Otherwise, returns |pp_error|
+// as is.
+PPAPI_PROXY_EXPORT int32_t ConvertNetworkAPIErrorForCompatibility(
+    int32_t pp_error,
+    bool private_api);
+
+}  // namespace proxy
+}  // namespace ppapi
+
+#endif  // PPAPI_PROXY_ERROR_CONVERSION_H_
diff --git a/ppapi/proxy/ext_crx_file_system_private_resource.cc b/ppapi/proxy/ext_crx_file_system_private_resource.cc
index f0de598..be4c3f7 100644
--- a/ppapi/proxy/ext_crx_file_system_private_resource.cc
+++ b/ppapi/proxy/ext_crx_file_system_private_resource.cc
@@ -6,6 +6,8 @@
 
 #include "base/bind.h"
 #include "ppapi/c/pp_errors.h"
+#include "ppapi/c/pp_file_info.h"
+#include "ppapi/proxy/file_system_resource.h"
 #include "ppapi/proxy/ppapi_messages.h"
 #include "ppapi/proxy/resource_message_params.h"
 #include "ppapi/shared_impl/host_resource.h"
@@ -15,6 +17,13 @@
 namespace ppapi {
 namespace proxy {
 
+namespace {
+void RunTrackedCallback(scoped_refptr<TrackedCallback> callback,
+                        int32_t rc) {
+  callback->Run(rc);
+}
+}  // namespace
+
 ExtCrxFileSystemPrivateResource::ExtCrxFileSystemPrivateResource(
     Connection connection, PP_Instance instance)
     : PluginResource(connection, instance), called_open_(false) {
@@ -61,19 +70,12 @@
     return;
   }
 
-  thunk::EnterResourceCreationNoLock enter(pp_instance());
-  if (enter.failed()) {
-    callback->Run(enter.retval());
-    return;
-  }
-
-  *file_system_resource = enter.functions()->CreateIsolatedFileSystem(
-      pp_instance(), fsid.c_str());
-  if (*file_system_resource != 0) {
-    callback->Run(PP_OK);
-  } else {
+  FileSystemResource* fs = new FileSystemResource(
+      connection(), pp_instance(), PP_FILESYSTEMTYPE_ISOLATED);
+  *file_system_resource = fs->GetReference();
+  if (*file_system_resource == 0)
     callback->Run(PP_ERROR_FAILED);
-  }
+  fs->InitIsolatedFileSystem(fsid, base::Bind(&RunTrackedCallback, callback));
 }
 
 }  // namespace proxy
diff --git a/ppapi/proxy/file_system_resource.cc b/ppapi/proxy/file_system_resource.cc
index 5acac00..bab7719 100644
--- a/ppapi/proxy/file_system_resource.cc
+++ b/ppapi/proxy/file_system_resource.cc
@@ -38,6 +38,7 @@
 
 int32_t FileSystemResource::Open(int64_t expected_size,
                                  scoped_refptr<TrackedCallback> callback) {
+  DCHECK(type_ != PP_FILESYSTEMTYPE_ISOLATED);
   if (called_open_)
     return PP_ERROR_FAILED;
   called_open_ = true;
@@ -59,11 +60,27 @@
   return type_;
 }
 
-void FileSystemResource::InitIsolatedFileSystem(const char* fsid) {
-  Post(RENDERER,
-       PpapiHostMsg_FileSystem_InitIsolatedFileSystem(std::string(fsid)));
-  Post(BROWSER,
-       PpapiHostMsg_FileSystem_InitIsolatedFileSystem(std::string(fsid)));
+int32_t FileSystemResource::InitIsolatedFileSystem(
+    const std::string& fsid,
+    const base::Callback<void(int32_t)>& callback) {
+  // This call is mutually exclusive with Open() above, so we can reuse the
+  // called_open state.
+  DCHECK(type_ == PP_FILESYSTEMTYPE_ISOLATED);
+  if (called_open_)
+    return PP_ERROR_FAILED;
+  called_open_ = true;
+
+  Call<PpapiPluginMsg_FileSystem_InitIsolatedFileSystemReply>(RENDERER,
+      PpapiHostMsg_FileSystem_InitIsolatedFileSystem(fsid),
+      base::Bind(&FileSystemResource::InitIsolatedFileSystemComplete,
+      this,
+      callback));
+  Call<PpapiPluginMsg_FileSystem_InitIsolatedFileSystemReply>(BROWSER,
+      PpapiHostMsg_FileSystem_InitIsolatedFileSystem(fsid),
+      base::Bind(&FileSystemResource::InitIsolatedFileSystemComplete,
+      this,
+      callback));
+  return PP_OK_COMPLETIONPENDING;
 }
 
 void FileSystemResource::OpenComplete(
@@ -75,5 +92,14 @@
     callback->Run(params.result());
 }
 
+void FileSystemResource::InitIsolatedFileSystemComplete(
+    const base::Callback<void(int32_t)>& callback,
+    const ResourceMessageReplyParams& params) {
+  ++callback_count_;
+  // Received callback from browser and renderer.
+  if (callback_count_ == 2)
+    callback.Run(params.result());
+}
+
 }  // namespace proxy
 }  // namespace ppapi
diff --git a/ppapi/proxy/file_system_resource.h b/ppapi/proxy/file_system_resource.h
index b104db3..74677f7 100644
--- a/ppapi/proxy/file_system_resource.h
+++ b/ppapi/proxy/file_system_resource.h
@@ -38,13 +38,18 @@
                        scoped_refptr<TrackedCallback> callback) OVERRIDE;
   virtual PP_FileSystemType GetType() OVERRIDE;
 
-  void InitIsolatedFileSystem(const char* fsid);
+  int32_t InitIsolatedFileSystem(const std::string& fsid,
+                                 const base::Callback<void(int32_t)>& callback);
  private:
-
   // Called when the host has responded to our open request.
   void OpenComplete(scoped_refptr<TrackedCallback> callback,
                     const ResourceMessageReplyParams& params);
 
+  // Called when the host has responded to our InitIsolatedFileSystem request.
+  void InitIsolatedFileSystemComplete(
+      const base::Callback<void(int32_t)>& callback,
+      const ResourceMessageReplyParams& params);
+
   PP_FileSystemType type_;
   bool called_open_;
   uint32_t callback_count_;
diff --git a/ppapi/proxy/host_resolver_resource_base.cc b/ppapi/proxy/host_resolver_resource_base.cc
index 5cbbbb2..d552b76 100644
--- a/ppapi/proxy/host_resolver_resource_base.cc
+++ b/ppapi/proxy/host_resolver_resource_base.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "ppapi/c/pp_errors.h"
+#include "ppapi/proxy/error_conversion.h"
 #include "ppapi/proxy/net_address_resource.h"
 #include "ppapi/proxy/ppapi_messages.h"
 #include "ppapi/shared_impl/tracked_callback.h"
@@ -14,25 +15,6 @@
 namespace ppapi {
 namespace proxy {
 
-namespace {
-
-int32_t ConvertPPError(int32_t pp_error, bool private_api) {
-  // The private API doesn't return network-specific error codes or
-  // PP_ERROR_NOACCESS. In order to preserve the behavior, we convert those to
-  // PP_ERROR_FAILED.
-  // TODO(yzshen): Consider defining ranges for different kinds of PP_Error
-  // codes, so that we can detect network-specific error codes in a better way.
-  if (private_api &&
-      (pp_error <= PP_ERROR_CONNECTION_CLOSED ||
-       pp_error == PP_ERROR_NOACCESS)) {
-    return PP_ERROR_FAILED;
-  }
-
-  return pp_error;
-}
-
-}  // namespace
-
 HostResolverResourceBase::HostResolverResourceBase(Connection connection,
                                                    PP_Instance instance,
                                                    bool private_api)
@@ -110,7 +92,8 @@
     canonical_name_.clear();
     net_address_list_.clear();
   }
-  resolve_callback_->Run(ConvertPPError(params.result(), private_api_));
+  resolve_callback_->Run(ConvertNetworkAPIErrorForCompatibility(params.result(),
+                                                                private_api_));
 }
 
 void HostResolverResourceBase::SendResolve(
diff --git a/ppapi/proxy/interface_list.cc b/ppapi/proxy/interface_list.cc
index d9feb89..115516f 100644
--- a/ppapi/proxy/interface_list.cc
+++ b/ppapi/proxy/interface_list.cc
@@ -108,8 +108,6 @@
 #include "ppapi/proxy/ppb_instance_proxy.h"
 #include "ppapi/proxy/ppb_message_loop_proxy.h"
 #include "ppapi/proxy/ppb_network_monitor_private_proxy.h"
-#include "ppapi/proxy/ppb_tcp_socket_private_proxy.h"
-#include "ppapi/proxy/ppb_tcp_socket_proxy.h"
 #include "ppapi/proxy/ppb_testing_proxy.h"
 #include "ppapi/proxy/ppb_var_deprecated_proxy.h"
 #include "ppapi/proxy/ppb_video_decoder_proxy.h"
diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h
index a377193..f108812 100644
--- a/ppapi/proxy/ppapi_messages.h
+++ b/ppapi/proxy/ppapi_messages.h
@@ -703,32 +703,6 @@
                     std::string /* serialized_block_info */)
 #endif  // !defined(OS_NACL) && !defined(NACL_WIN64)
 
-// PPB_TCPSocket and PPB_TCPSocket_Private.
-IPC_MESSAGE_ROUTED5(PpapiMsg_PPBTCPSocket_ConnectACK,
-                    uint32 /* plugin_dispatcher_id */,
-                    uint32 /* socket_id */,
-                    int32_t /* result */,
-                    PP_NetAddress_Private /* local_addr */,
-                    PP_NetAddress_Private /* remote_addr */)
-IPC_MESSAGE_ROUTED4(PpapiMsg_PPBTCPSocket_SSLHandshakeACK,
-                    uint32 /* plugin_dispatcher_id */,
-                    uint32 /* socket_id */,
-                    bool /* succeeded */,
-                    ppapi::PPB_X509Certificate_Fields /* certificate_fields */)
-IPC_MESSAGE_ROUTED4(PpapiMsg_PPBTCPSocket_ReadACK,
-                    uint32 /* plugin_dispatcher_id */,
-                    uint32 /* socket_id */,
-                    int32_t /* result */,
-                    std::string /* data */)
-IPC_MESSAGE_ROUTED3(PpapiMsg_PPBTCPSocket_WriteACK,
-                    uint32 /* plugin_dispatcher_id */,
-                    uint32 /* socket_id */,
-                    int32_t /* result */)
-IPC_MESSAGE_ROUTED3(PpapiMsg_PPBTCPSocket_SetOptionACK,
-                    uint32 /* plugin_dispatcher_id */,
-                    uint32 /* socket_id */,
-                    int32_t /* result */)
-
 #if !defined(OS_NACL) && !defined(NACL_WIN64)
 // PPP_Instance_Private.
 IPC_SYNC_MESSAGE_ROUTED1_1(PpapiMsg_PPPInstancePrivate_GetInstanceObject,
@@ -1164,45 +1138,6 @@
                            ppapi::HostResource /* flash_message_loop */)
 #endif  // !defined(OS_NACL) && !defined(NACL_WIN64)
 
-// PPB_TCPSocket and PPB_TCPSocket_Private.
-// Creates a PPB_TCPSocket resource.
-IPC_SYNC_MESSAGE_CONTROL2_1(PpapiHostMsg_PPBTCPSocket_Create,
-                            int32 /* routing_id */,
-                            uint32 /* plugin_dispatcher_id */,
-                            uint32 /* socket_id */)
-// Creates a PPB_TCPSocket_Private resource.
-IPC_SYNC_MESSAGE_CONTROL2_1(PpapiHostMsg_PPBTCPSocket_CreatePrivate,
-                            int32 /* routing_id */,
-                            uint32 /* plugin_dispatcher_id */,
-                            uint32 /* socket_id */)
-IPC_MESSAGE_CONTROL4(PpapiHostMsg_PPBTCPSocket_Connect,
-                     int32 /* routing_id */,
-                     uint32 /* socket_id */,
-                     std::string /* host */,
-                     uint16_t /* port */)
-IPC_MESSAGE_CONTROL3(PpapiHostMsg_PPBTCPSocket_ConnectWithNetAddress,
-                     int32 /* routing_id */,
-                     uint32 /* socket_id */,
-                     PP_NetAddress_Private /* net_addr */)
-IPC_MESSAGE_CONTROL5(PpapiHostMsg_PPBTCPSocket_SSLHandshake,
-                     uint32 /* socket_id */,
-                     std::string /* server_name */,
-                     uint16_t /* server_port */,
-                     std::vector<std::vector<char> > /* trusted_certs */,
-                     std::vector<std::vector<char> > /* untrusted_certs */)
-IPC_MESSAGE_CONTROL2(PpapiHostMsg_PPBTCPSocket_Read,
-                     uint32 /* socket_id */,
-                     int32_t /* bytes_to_read */)
-IPC_MESSAGE_CONTROL2(PpapiHostMsg_PPBTCPSocket_Write,
-                     uint32 /* socket_id */,
-                     std::string /* data */)
-IPC_MESSAGE_CONTROL1(PpapiHostMsg_PPBTCPSocket_Disconnect,
-                     uint32 /* socket_id */)
-IPC_MESSAGE_CONTROL3(PpapiHostMsg_PPBTCPSocket_SetOption,
-                     uint32 /* socket_id */,
-                     PP_TCPSocket_Option /* name */,
-                     ppapi::SocketOptionData /* value */)
-
 // PPB_X509Certificate_Private
 IPC_SYNC_MESSAGE_CONTROL1_2(PpapiHostMsg_PPBX509Certificate_ParseDER,
                             std::vector<char> /* der */,
@@ -1437,6 +1372,7 @@
 IPC_MESSAGE_CONTROL0(PpapiPluginMsg_FileSystem_OpenReply)
 IPC_MESSAGE_CONTROL1(PpapiHostMsg_FileSystem_InitIsolatedFileSystem,
                      std::string /* fsid */)
+IPC_MESSAGE_CONTROL0(PpapiPluginMsg_FileSystem_InitIsolatedFileSystemReply)
 
 // Flash DRM ------------------------------------------------------------------
 IPC_MESSAGE_CONTROL0(PpapiHostMsg_FlashDRM_Create)
@@ -1562,6 +1498,41 @@
 IPC_MESSAGE_CONTROL1(PpapiPluginMsg_Printing_GetDefaultPrintSettingsReply,
                      PP_PrintSettings_Dev /* print_settings */)
 
+// TCP Socket ------------------------------------------------------------------
+// Creates a PPB_TCPSocket resource.
+IPC_MESSAGE_CONTROL0(PpapiHostMsg_TCPSocket_Create)
+
+// Creates a PPB_TCPSocket_Private resource.
+IPC_MESSAGE_CONTROL0(PpapiHostMsg_TCPSocket_CreatePrivate)
+
+IPC_MESSAGE_CONTROL2(PpapiHostMsg_TCPSocket_Connect,
+                     std::string /* host */,
+                     uint16_t /* port */)
+IPC_MESSAGE_CONTROL1(PpapiHostMsg_TCPSocket_ConnectWithNetAddress,
+                     PP_NetAddress_Private /* net_addr */)
+IPC_MESSAGE_CONTROL2(PpapiPluginMsg_TCPSocket_ConnectReply,
+                     PP_NetAddress_Private /* local_addr */,
+                     PP_NetAddress_Private /* remote_addr */)
+IPC_MESSAGE_CONTROL4(PpapiHostMsg_TCPSocket_SSLHandshake,
+                     std::string /* server_name */,
+                     uint16_t /* server_port */,
+                     std::vector<std::vector<char> > /* trusted_certs */,
+                     std::vector<std::vector<char> > /* untrusted_certs */)
+IPC_MESSAGE_CONTROL1(PpapiPluginMsg_TCPSocket_SSLHandshakeReply,
+                     ppapi::PPB_X509Certificate_Fields /* certificate_fields */)
+IPC_MESSAGE_CONTROL1(PpapiHostMsg_TCPSocket_Read,
+                     int32_t /* bytes_to_read */)
+IPC_MESSAGE_CONTROL1(PpapiPluginMsg_TCPSocket_ReadReply,
+                     std::string /* data */)
+IPC_MESSAGE_CONTROL1(PpapiHostMsg_TCPSocket_Write,
+                     std::string /* data */)
+IPC_MESSAGE_CONTROL0(PpapiPluginMsg_TCPSocket_WriteReply)
+IPC_MESSAGE_CONTROL0(PpapiHostMsg_TCPSocket_Disconnect)
+IPC_MESSAGE_CONTROL2(PpapiHostMsg_TCPSocket_SetOption,
+                     PP_TCPSocket_Option /* name */,
+                     ppapi::SocketOptionData /* value */)
+IPC_MESSAGE_CONTROL0(PpapiPluginMsg_TCPSocket_SetOptionReply)
+
 // TCP Server Socket -----------------------------------------------------------
 // Creates a PPB_TCPServerSocket_Private resource.
 IPC_MESSAGE_CONTROL0(PpapiHostMsg_TCPServerSocket_CreatePrivate)
@@ -1571,10 +1542,9 @@
                      int32_t /* backlog */)
 IPC_MESSAGE_CONTROL1(PpapiPluginMsg_TCPServerSocket_ListenReply,
                      PP_NetAddress_Private /* local_addr */)
-IPC_MESSAGE_CONTROL1(PpapiHostMsg_TCPServerSocket_Accept,
-                     uint32 /* plugin_dispatcher_id */)
+IPC_MESSAGE_CONTROL0(PpapiHostMsg_TCPServerSocket_Accept)
 IPC_MESSAGE_CONTROL3(PpapiPluginMsg_TCPServerSocket_AcceptReply,
-                     uint32 /* accepted_socket_id */,
+                     int /* pending_resource_id */,
                      PP_NetAddress_Private /* local_addr */,
                      PP_NetAddress_Private /* remote_addr */)
 IPC_MESSAGE_CONTROL0(PpapiHostMsg_TCPServerSocket_StopListening)
diff --git a/ppapi/proxy/ppb_graphics_3d_proxy.cc b/ppapi/proxy/ppb_graphics_3d_proxy.cc
index 7d18850..204466b 100644
--- a/ppapi/proxy/ppb_graphics_3d_proxy.cc
+++ b/ppapi/proxy/ppb_graphics_3d_proxy.cc
@@ -62,7 +62,9 @@
 // Graphics3D context; this isn't allowed, and will likely either crash or
 // result in undefined behavior.  It is assumed that the thread which creates
 // the Graphics3D context will be the thread on which subsequent gl rendering
-// will be done.
+// will be done. This is why it is okay to read need_to_lock_ without the lock;
+// it should only ever be read and written on the same thread where the context
+// was created.
 //
 // TODO(nfullagar): At some point, allow multiple threads to concurrently render
 // each to its own context.  First step is to allow a single thread (either main
@@ -180,7 +182,8 @@
 }
 
 Graphics3D::~Graphics3D() {
-  DestroyGLES2Impl();
+  if (gles2_impl())
+    DestroyGLES2Impl();
 }
 
 bool Graphics3D::Init(gpu::gles2::GLES2Implementation* share_gles2) {
@@ -258,6 +261,10 @@
 
 void Graphics3D::PushAlreadyLocked() {
   ppapi::ProxyLock::AssertAcquired();
+  if (!locking_command_buffer_) {
+    NOTREACHED();
+    return;
+  }
   if (num_already_locked_calls_ == 0)
     locking_command_buffer_->set_need_to_lock(false);
   ++num_already_locked_calls_;
@@ -268,6 +275,10 @@
   DCHECK(!locking_command_buffer_->need_to_lock());
   DCHECK_GT(num_already_locked_calls_, 0);
   ppapi::ProxyLock::AssertAcquired();
+  if (!locking_command_buffer_) {
+    NOTREACHED();
+    return;
+  }
   --num_already_locked_calls_;
   if (num_already_locked_calls_ == 0)
     locking_command_buffer_->set_need_to_lock(true);
diff --git a/ppapi/proxy/ppb_tcp_socket_private_proxy.cc b/ppapi/proxy/ppb_tcp_socket_private_proxy.cc
deleted file mode 100644
index 58199fd..0000000
--- a/ppapi/proxy/ppb_tcp_socket_private_proxy.cc
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ppapi/proxy/ppb_tcp_socket_private_proxy.h"
-
-#include <map>
-
-#include "base/logging.h"
-#include "ppapi/proxy/plugin_dispatcher.h"
-#include "ppapi/proxy/plugin_globals.h"
-#include "ppapi/proxy/plugin_resource_tracker.h"
-#include "ppapi/proxy/ppapi_messages.h"
-#include "ppapi/shared_impl/private/ppb_x509_certificate_private_shared.h"
-#include "ppapi/shared_impl/private/tcp_socket_private_impl.h"
-#include "ppapi/shared_impl/resource.h"
-#include "ppapi/shared_impl/socket_option_data.h"
-#include "ppapi/thunk/thunk.h"
-
-namespace ppapi {
-namespace proxy {
-
-namespace {
-
-typedef std::map<uint32, TCPSocketPrivateImpl*> IDToSocketMap;
-IDToSocketMap* g_id_to_socket = NULL;
-
-class TCPSocket : public TCPSocketPrivateImpl {
- public:
-  // C-tor for new sockets.
-  TCPSocket(const HostResource& resource, uint32 socket_id);
-  // C-tor for already connected sockets.
-  TCPSocket(const HostResource& resource,
-            uint32 socket_id,
-            const PP_NetAddress_Private& local_addr,
-            const PP_NetAddress_Private& remote_addr);
-  virtual ~TCPSocket();
-
-  virtual void SendConnect(const std::string& host, uint16_t port) OVERRIDE;
-  virtual void SendConnectWithNetAddress(
-      const PP_NetAddress_Private& addr) OVERRIDE;
-  virtual void SendSSLHandshake(
-      const std::string& server_name,
-      uint16_t server_port,
-      const std::vector<std::vector<char> >& trusted_certs,
-      const std::vector<std::vector<char> >& untrusted_certs) OVERRIDE;
-  virtual void SendRead(int32_t bytes_to_read) OVERRIDE;
-  virtual void SendWrite(const std::string& buffer) OVERRIDE;
-  virtual void SendDisconnect() OVERRIDE;
-  virtual void SendSetOption(PP_TCPSocket_Option name,
-                             const SocketOptionData& value) OVERRIDE;
-
- private:
-  void SendToBrowser(IPC::Message* msg);
-
-  DISALLOW_COPY_AND_ASSIGN(TCPSocket);
-};
-
-TCPSocket::TCPSocket(const HostResource& resource, uint32 socket_id)
-    : TCPSocketPrivateImpl(resource, socket_id) {
-  if (!g_id_to_socket)
-    g_id_to_socket = new IDToSocketMap();
-  DCHECK(g_id_to_socket->find(socket_id) == g_id_to_socket->end());
-  (*g_id_to_socket)[socket_id] = this;
-}
-
-TCPSocket::TCPSocket(const HostResource& resource,
-                     uint32 socket_id,
-                     const PP_NetAddress_Private& local_addr,
-                     const PP_NetAddress_Private& remote_addr)
-    : TCPSocketPrivateImpl(resource, socket_id) {
-  if (!g_id_to_socket)
-    g_id_to_socket = new IDToSocketMap();
-  DCHECK(g_id_to_socket->find(socket_id) == g_id_to_socket->end());
-
-  connection_state_ = CONNECTED;
-  local_addr_ = local_addr;
-  remote_addr_ = remote_addr;
-
-  (*g_id_to_socket)[socket_id] = this;
-}
-
-TCPSocket::~TCPSocket() {
-  Disconnect();
-}
-
-void TCPSocket::SendConnect(const std::string& host, uint16_t port) {
-  SendToBrowser(new PpapiHostMsg_PPBTCPSocket_Connect(
-      API_ID_PPB_TCPSOCKET_PRIVATE, socket_id_, host, port));
-}
-
-void TCPSocket::SendConnectWithNetAddress(const PP_NetAddress_Private& addr) {
-  SendToBrowser(new PpapiHostMsg_PPBTCPSocket_ConnectWithNetAddress(
-      API_ID_PPB_TCPSOCKET_PRIVATE, socket_id_, addr));
-}
-
-void TCPSocket::SendSSLHandshake(
-    const std::string& server_name,
-    uint16_t server_port,
-    const std::vector<std::vector<char> >& trusted_certs,
-    const std::vector<std::vector<char> >& untrusted_certs) {
-  SendToBrowser(new PpapiHostMsg_PPBTCPSocket_SSLHandshake(
-      socket_id_, server_name, server_port, trusted_certs, untrusted_certs));
-}
-
-void TCPSocket::SendRead(int32_t bytes_to_read) {
-  SendToBrowser(new PpapiHostMsg_PPBTCPSocket_Read(socket_id_, bytes_to_read));
-}
-
-void TCPSocket::SendWrite(const std::string& buffer) {
-  SendToBrowser(new PpapiHostMsg_PPBTCPSocket_Write(socket_id_, buffer));
-}
-
-void TCPSocket::SendDisconnect() {
-  // After removed from the mapping, this object won't receive any notifications
-  // from the proxy.
-  DCHECK(g_id_to_socket->find(socket_id_) != g_id_to_socket->end());
-  g_id_to_socket->erase(socket_id_);
-  SendToBrowser(new PpapiHostMsg_PPBTCPSocket_Disconnect(socket_id_));
-}
-
-void TCPSocket::SendSetOption(PP_TCPSocket_Option name,
-                              const SocketOptionData& value) {
-  SendToBrowser(
-      new PpapiHostMsg_PPBTCPSocket_SetOption(socket_id_, name, value));
-}
-
-void TCPSocket::SendToBrowser(IPC::Message* msg) {
-  PluginGlobals::Get()->GetBrowserSender()->Send(msg);
-}
-
-}  // namespace
-
-//------------------------------------------------------------------------------
-
-PPB_TCPSocket_Private_Proxy::PPB_TCPSocket_Private_Proxy(Dispatcher* dispatcher)
-    : InterfaceProxy(dispatcher) {
-}
-
-PPB_TCPSocket_Private_Proxy::~PPB_TCPSocket_Private_Proxy() {
-}
-
-// static
-PP_Resource PPB_TCPSocket_Private_Proxy::CreateProxyResource(
-    PP_Instance instance) {
-  PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
-  if (!dispatcher)
-    return 0;
-
-  uint32 socket_id = 0;
-  PluginGlobals::Get()->GetBrowserSender()->Send(
-      new PpapiHostMsg_PPBTCPSocket_CreatePrivate(
-          API_ID_PPB_TCPSOCKET_PRIVATE, dispatcher->plugin_dispatcher_id(),
-          &socket_id));
-  if (socket_id == 0)
-    return 0;
-  return (new TCPSocket(HostResource::MakeInstanceOnly(instance),
-                        socket_id))->GetReference();
-}
-
-// static
-PP_Resource PPB_TCPSocket_Private_Proxy::CreateProxyResourceForConnectedSocket(
-    PP_Instance instance,
-    uint32 socket_id,
-    const PP_NetAddress_Private& local_addr,
-    const PP_NetAddress_Private& remote_addr) {
-  return (new TCPSocket(HostResource::MakeInstanceOnly(instance),
-                        socket_id,
-                        local_addr,
-                        remote_addr))->GetReference();
-}
-
-bool PPB_TCPSocket_Private_Proxy::OnMessageReceived(const IPC::Message& msg) {
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(PPB_TCPSocket_Private_Proxy, msg)
-    IPC_MESSAGE_HANDLER(PpapiMsg_PPBTCPSocket_ConnectACK,
-                        OnMsgConnectACK)
-    IPC_MESSAGE_HANDLER(PpapiMsg_PPBTCPSocket_SSLHandshakeACK,
-                        OnMsgSSLHandshakeACK)
-    IPC_MESSAGE_HANDLER(PpapiMsg_PPBTCPSocket_ReadACK, OnMsgReadACK)
-    IPC_MESSAGE_HANDLER(PpapiMsg_PPBTCPSocket_WriteACK, OnMsgWriteACK)
-    IPC_MESSAGE_HANDLER(PpapiMsg_PPBTCPSocket_SetOptionACK, OnMsgSetOptionACK)
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-  return handled;
-}
-
-void PPB_TCPSocket_Private_Proxy::OnMsgConnectACK(
-    uint32 /* plugin_dispatcher_id */,
-    uint32 socket_id,
-    int32_t result,
-    const PP_NetAddress_Private& local_addr,
-    const PP_NetAddress_Private& remote_addr) {
-  if (!g_id_to_socket) {
-    NOTREACHED();
-    return;
-  }
-  IDToSocketMap::iterator iter = g_id_to_socket->find(socket_id);
-  if (iter == g_id_to_socket->end())
-    return;
-  iter->second->OnConnectCompleted(result, local_addr, remote_addr);
-}
-
-void PPB_TCPSocket_Private_Proxy::OnMsgSSLHandshakeACK(
-    uint32 /* plugin_dispatcher_id */,
-    uint32 socket_id,
-    bool succeeded,
-    const PPB_X509Certificate_Fields& certificate_fields) {
-  if (!g_id_to_socket) {
-    NOTREACHED();
-    return;
-  }
-  IDToSocketMap::iterator iter = g_id_to_socket->find(socket_id);
-  if (iter == g_id_to_socket->end())
-    return;
-  iter->second->OnSSLHandshakeCompleted(succeeded, certificate_fields);
-}
-
-void PPB_TCPSocket_Private_Proxy::OnMsgReadACK(
-    uint32 /* plugin_dispatcher_id */,
-    uint32 socket_id,
-    int32_t result,
-    const std::string& data) {
-  if (!g_id_to_socket) {
-    NOTREACHED();
-    return;
-  }
-  IDToSocketMap::iterator iter = g_id_to_socket->find(socket_id);
-  if (iter == g_id_to_socket->end())
-    return;
-  iter->second->OnReadCompleted(result, data);
-}
-
-void PPB_TCPSocket_Private_Proxy::OnMsgWriteACK(
-    uint32 /* plugin_dispatcher_id */,
-    uint32 socket_id,
-    int32_t result) {
-  if (!g_id_to_socket) {
-    NOTREACHED();
-    return;
-  }
-  IDToSocketMap::iterator iter = g_id_to_socket->find(socket_id);
-  if (iter == g_id_to_socket->end())
-    return;
-  iter->second->OnWriteCompleted(result);
-}
-
-void PPB_TCPSocket_Private_Proxy::OnMsgSetOptionACK(
-    uint32 /* plugin_dispatcher_id */,
-    uint32 socket_id,
-    int32_t result) {
-  if (!g_id_to_socket) {
-    NOTREACHED();
-    return;
-  }
-  IDToSocketMap::iterator iter = g_id_to_socket->find(socket_id);
-  if (iter == g_id_to_socket->end())
-    return;
-  iter->second->OnSetOptionCompleted(result);
-}
-
-}  // namespace proxy
-}  // namespace ppapi
diff --git a/ppapi/proxy/ppb_tcp_socket_private_proxy.h b/ppapi/proxy/ppb_tcp_socket_private_proxy.h
deleted file mode 100644
index 0813531..0000000
--- a/ppapi/proxy/ppb_tcp_socket_private_proxy.h
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef PPAPI_PROXY_PPB_TCP_SOCKET_PRIVATE_PROXY_H_
-#define PPAPI_PROXY_PPB_TCP_SOCKET_PRIVATE_PROXY_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "ppapi/c/pp_instance.h"
-#include "ppapi/c/pp_resource.h"
-#include "ppapi/c/private/ppb_tcp_socket_private.h"
-#include "ppapi/proxy/interface_proxy.h"
-#include "ppapi/proxy/ppapi_proxy_export.h"
-
-namespace ppapi {
-
-class PPB_X509Certificate_Fields;
-
-namespace proxy {
-
-class PPB_TCPSocket_Private_Proxy : public InterfaceProxy {
- public:
-  explicit PPB_TCPSocket_Private_Proxy(Dispatcher* dispatcher);
-  virtual ~PPB_TCPSocket_Private_Proxy();
-
-  static PP_Resource CreateProxyResource(PP_Instance instance);
-  static PP_Resource CreateProxyResourceForConnectedSocket(
-      PP_Instance instance,
-      uint32 socket_id,
-      const PP_NetAddress_Private& local_addr,
-      const PP_NetAddress_Private& remote_addr);
-
-  // InterfaceProxy implementation.
-  virtual bool OnMessageReceived(const IPC::Message& msg);
-
-  static const ApiID kApiID = API_ID_PPB_TCPSOCKET_PRIVATE;
-
- private:
-  // Browser->plugin message handlers.
-  void OnMsgConnectACK(uint32 plugin_dispatcher_id,
-                       uint32 socket_id,
-                       int32_t result,
-                       const PP_NetAddress_Private& local_addr,
-                       const PP_NetAddress_Private& remote_addr);
-  void OnMsgSSLHandshakeACK(
-      uint32 plugin_dispatcher_id,
-      uint32 socket_id,
-      bool succeeded,
-      const PPB_X509Certificate_Fields& certificate_fields);
-  void OnMsgReadACK(uint32 plugin_dispatcher_id,
-                    uint32 socket_id,
-                    int32_t result,
-                    const std::string& data);
-  void OnMsgWriteACK(uint32 plugin_dispatcher_id,
-                     uint32 socket_id,
-                     int32_t result);
-  void OnMsgSetOptionACK(uint32 plugin_dispatcher_id,
-                         uint32 socket_id,
-                         int32_t result);
-
-  DISALLOW_COPY_AND_ASSIGN(PPB_TCPSocket_Private_Proxy);
-};
-
-}  // namespace proxy
-}  // namespace ppapi
-
-#endif  // PPAPI_PROXY_PPB_TCP_SOCKET_PRIVATE_PROXY_H_
diff --git a/ppapi/proxy/ppb_tcp_socket_proxy.cc b/ppapi/proxy/ppb_tcp_socket_proxy.cc
deleted file mode 100644
index 2ec1660..0000000
--- a/ppapi/proxy/ppb_tcp_socket_proxy.cc
+++ /dev/null
@@ -1,299 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ppapi/proxy/ppb_tcp_socket_proxy.h"
-
-#include <map>
-
-#include "base/logging.h"
-#include "ppapi/proxy/plugin_dispatcher.h"
-#include "ppapi/proxy/plugin_globals.h"
-#include "ppapi/proxy/ppapi_messages.h"
-#include "ppapi/shared_impl/resource.h"
-#include "ppapi/shared_impl/socket_option_data.h"
-#include "ppapi/shared_impl/tcp_socket_shared.h"
-#include "ppapi/thunk/enter.h"
-#include "ppapi/thunk/ppb_net_address_api.h"
-#include "ppapi/thunk/ppb_tcp_socket_api.h"
-#include "ppapi/thunk/thunk.h"
-
-namespace ppapi {
-namespace proxy {
-
-namespace {
-
-typedef thunk::EnterResourceNoLock<thunk::PPB_NetAddress_API>
-    EnterNetAddressNoLock;
-
-typedef std::map<uint32, TCPSocketShared*> IDToSocketMap;
-IDToSocketMap* g_id_to_socket = NULL;
-
-class TCPSocket : public thunk::PPB_TCPSocket_API,
-                  public Resource,
-                  public TCPSocketShared {
- public:
-  TCPSocket(const HostResource& resource, uint32 socket_id);
-  virtual ~TCPSocket();
-
-  // Resource overrides.
-  virtual thunk::PPB_TCPSocket_API* AsPPB_TCPSocket_API() OVERRIDE;
-
-  // thunk::PPB_TCPSocket_API implementation.
-  virtual int32_t Connect(PP_Resource addr,
-                          scoped_refptr<TrackedCallback> callback) OVERRIDE;
-  virtual PP_Resource GetLocalAddress() OVERRIDE;
-  virtual PP_Resource GetRemoteAddress() OVERRIDE;
-  virtual int32_t Read(char* buffer,
-                       int32_t bytes_to_read,
-                       scoped_refptr<TrackedCallback> callback) OVERRIDE;
-  virtual int32_t Write(const char* buffer,
-                        int32_t bytes_to_write,
-                        scoped_refptr<TrackedCallback> callback) OVERRIDE;
-  virtual void Close() OVERRIDE;
-  virtual int32_t SetOption(PP_TCPSocket_Option name,
-                            const PP_Var& value,
-                            scoped_refptr<TrackedCallback> callback) OVERRIDE;
-
-  // TCPSocketShared implementation.
-  virtual void SendConnect(const std::string& host, uint16_t port) OVERRIDE;
-  virtual void SendConnectWithNetAddress(
-      const PP_NetAddress_Private& addr) OVERRIDE;
-  virtual void SendSSLHandshake(
-      const std::string& server_name,
-      uint16_t server_port,
-      const std::vector<std::vector<char> >& trusted_certs,
-      const std::vector<std::vector<char> >& untrusted_certs) OVERRIDE;
-  virtual void SendRead(int32_t bytes_to_read) OVERRIDE;
-  virtual void SendWrite(const std::string& buffer) OVERRIDE;
-  virtual void SendDisconnect() OVERRIDE;
-  virtual void SendSetOption(PP_TCPSocket_Option name,
-                             const SocketOptionData& value) OVERRIDE;
-  virtual Resource* GetOwnerResource() OVERRIDE;
-
- private:
-  void SendToBrowser(IPC::Message* msg);
-
-  DISALLOW_COPY_AND_ASSIGN(TCPSocket);
-};
-
-TCPSocket::TCPSocket(const HostResource& resource, uint32 socket_id)
-    : Resource(OBJECT_IS_PROXY, resource),
-      TCPSocketShared(OBJECT_IS_PROXY, socket_id) {
-  if (!g_id_to_socket)
-    g_id_to_socket = new IDToSocketMap();
-  DCHECK(g_id_to_socket->find(socket_id) == g_id_to_socket->end());
-  (*g_id_to_socket)[socket_id] = this;
-}
-
-TCPSocket::~TCPSocket() {
-  DisconnectImpl();
-}
-
-thunk::PPB_TCPSocket_API* TCPSocket::AsPPB_TCPSocket_API() {
-  return this;
-}
-
-int32_t TCPSocket::Connect(PP_Resource addr,
-                           scoped_refptr<TrackedCallback> callback) {
-  EnterNetAddressNoLock enter(addr, true);
-  if (enter.failed())
-    return PP_ERROR_BADARGUMENT;
-
-  return ConnectWithNetAddressImpl(&enter.object()->GetNetAddressPrivate(),
-                                   callback);
-}
-
-PP_Resource TCPSocket::GetLocalAddress() {
-  PP_NetAddress_Private addr_private;
-  if (!GetLocalAddressImpl(&addr_private))
-    return 0;
-
-  thunk::EnterResourceCreationNoLock enter(pp_instance());
-  if (enter.failed())
-    return 0;
-  return enter.functions()->CreateNetAddressFromNetAddressPrivate(
-      pp_instance(), addr_private);
-}
-
-PP_Resource TCPSocket::GetRemoteAddress() {
-  PP_NetAddress_Private addr_private;
-  if (!GetRemoteAddressImpl(&addr_private))
-    return 0;
-
-  thunk::EnterResourceCreationNoLock enter(pp_instance());
-  if (enter.failed())
-    return 0;
-  return enter.functions()->CreateNetAddressFromNetAddressPrivate(
-      pp_instance(), addr_private);
-}
-
-int32_t TCPSocket::Read(char* buffer,
-                        int32_t bytes_to_read,
-                        scoped_refptr<TrackedCallback> callback) {
-  return ReadImpl(buffer, bytes_to_read, callback);
-}
-
-int32_t TCPSocket::Write(const char* buffer,
-                         int32_t bytes_to_write,
-                         scoped_refptr<TrackedCallback> callback) {
-  return WriteImpl(buffer, bytes_to_write, callback);
-}
-
-void TCPSocket::Close() {
-  DisconnectImpl();
-}
-
-int32_t TCPSocket::SetOption(PP_TCPSocket_Option name,
-                             const PP_Var& value,
-                             scoped_refptr<TrackedCallback> callback) {
-  return SetOptionImpl(name, value, callback);
-}
-
-void TCPSocket::SendConnect(const std::string& host, uint16_t port) {
-  NOTREACHED();
-}
-
-void TCPSocket::SendConnectWithNetAddress(const PP_NetAddress_Private& addr) {
-  SendToBrowser(new PpapiHostMsg_PPBTCPSocket_ConnectWithNetAddress(
-      API_ID_PPB_TCPSOCKET, socket_id_, addr));
-}
-
-void TCPSocket::SendSSLHandshake(
-    const std::string& server_name,
-    uint16_t server_port,
-    const std::vector<std::vector<char> >& trusted_certs,
-    const std::vector<std::vector<char> >& untrusted_certs) {
-  NOTREACHED();
-}
-
-void TCPSocket::SendRead(int32_t bytes_to_read) {
-  SendToBrowser(new PpapiHostMsg_PPBTCPSocket_Read(socket_id_, bytes_to_read));
-}
-
-void TCPSocket::SendWrite(const std::string& buffer) {
-  SendToBrowser(new PpapiHostMsg_PPBTCPSocket_Write(socket_id_, buffer));
-}
-
-void TCPSocket::SendDisconnect() {
-  // After removed from the mapping, this object won't receive any notifications
-  // from the proxy.
-  DCHECK(g_id_to_socket->find(socket_id_) != g_id_to_socket->end());
-  g_id_to_socket->erase(socket_id_);
-  SendToBrowser(new PpapiHostMsg_PPBTCPSocket_Disconnect(socket_id_));
-}
-
-void TCPSocket::SendSetOption(PP_TCPSocket_Option name,
-                              const SocketOptionData& value) {
-  SendToBrowser(
-      new PpapiHostMsg_PPBTCPSocket_SetOption(socket_id_, name, value));
-}
-
-Resource* TCPSocket::GetOwnerResource() {
-  return this;
-}
-
-void TCPSocket::SendToBrowser(IPC::Message* msg) {
-  PluginGlobals::Get()->GetBrowserSender()->Send(msg);
-}
-
-}  // namespace
-
-//------------------------------------------------------------------------------
-
-PPB_TCPSocket_Proxy::PPB_TCPSocket_Proxy(Dispatcher* dispatcher)
-    : InterfaceProxy(dispatcher) {
-}
-
-PPB_TCPSocket_Proxy::~PPB_TCPSocket_Proxy() {
-}
-
-// static
-PP_Resource PPB_TCPSocket_Proxy::CreateProxyResource(PP_Instance instance) {
-  PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
-  if (!dispatcher)
-    return 0;
-
-  uint32 socket_id = 0;
-  PluginGlobals::Get()->GetBrowserSender()->Send(
-      new PpapiHostMsg_PPBTCPSocket_Create(
-          API_ID_PPB_TCPSOCKET, dispatcher->plugin_dispatcher_id(),
-          &socket_id));
-  if (socket_id == 0)
-    return 0;
-  return (new TCPSocket(HostResource::MakeInstanceOnly(instance),
-                        socket_id))->GetReference();
-}
-
-bool PPB_TCPSocket_Proxy::OnMessageReceived(const IPC::Message& msg) {
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(PPB_TCPSocket_Proxy, msg)
-    IPC_MESSAGE_HANDLER(PpapiMsg_PPBTCPSocket_ConnectACK,
-                        OnMsgConnectACK)
-    IPC_MESSAGE_HANDLER(PpapiMsg_PPBTCPSocket_ReadACK, OnMsgReadACK)
-    IPC_MESSAGE_HANDLER(PpapiMsg_PPBTCPSocket_WriteACK, OnMsgWriteACK)
-    IPC_MESSAGE_HANDLER(PpapiMsg_PPBTCPSocket_SetOptionACK,
-                        OnMsgSetOptionACK)
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-  return handled;
-}
-
-void PPB_TCPSocket_Proxy::OnMsgConnectACK(
-    uint32 /* plugin_dispatcher_id */,
-    uint32 socket_id,
-    int32_t result,
-    const PP_NetAddress_Private& local_addr,
-    const PP_NetAddress_Private& remote_addr) {
-  if (!g_id_to_socket) {
-    NOTREACHED();
-    return;
-  }
-  IDToSocketMap::iterator iter = g_id_to_socket->find(socket_id);
-  if (iter == g_id_to_socket->end())
-    return;
-  iter->second->OnConnectCompleted(result, local_addr, remote_addr);
-}
-
-void PPB_TCPSocket_Proxy::OnMsgReadACK(uint32 /* plugin_dispatcher_id */,
-                                       uint32 socket_id,
-                                       int32_t result,
-                                       const std::string& data) {
-  if (!g_id_to_socket) {
-    NOTREACHED();
-    return;
-  }
-  IDToSocketMap::iterator iter = g_id_to_socket->find(socket_id);
-  if (iter == g_id_to_socket->end())
-    return;
-  iter->second->OnReadCompleted(result, data);
-}
-
-void PPB_TCPSocket_Proxy::OnMsgWriteACK(uint32 /* plugin_dispatcher_id */,
-                                        uint32 socket_id,
-                                        int32_t result) {
-  if (!g_id_to_socket) {
-    NOTREACHED();
-    return;
-  }
-  IDToSocketMap::iterator iter = g_id_to_socket->find(socket_id);
-  if (iter == g_id_to_socket->end())
-    return;
-  iter->second->OnWriteCompleted(result);
-}
-
-void PPB_TCPSocket_Proxy::OnMsgSetOptionACK(uint32 /* plugin_dispatcher_id */,
-                                            uint32 socket_id,
-                                            int32_t result) {
-  if (!g_id_to_socket) {
-    NOTREACHED();
-    return;
-  }
-  IDToSocketMap::iterator iter = g_id_to_socket->find(socket_id);
-  if (iter == g_id_to_socket->end())
-    return;
-  iter->second->OnSetOptionCompleted(result);
-}
-
-}  // namespace proxy
-}  // namespace ppapi
diff --git a/ppapi/proxy/ppb_tcp_socket_proxy.h b/ppapi/proxy/ppb_tcp_socket_proxy.h
deleted file mode 100644
index c5a3de8..0000000
--- a/ppapi/proxy/ppb_tcp_socket_proxy.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef PPAPI_PROXY_PPB_TCP_SOCKET_PROXY_H_
-#define PPAPI_PROXY_PPB_TCP_SOCKET_PROXY_H_
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "ppapi/c/pp_instance.h"
-#include "ppapi/c/pp_resource.h"
-#include "ppapi/proxy/interface_proxy.h"
-#include "ppapi/proxy/ppapi_proxy_export.h"
-
-struct PP_NetAddress_Private;
-
-namespace ppapi {
-namespace proxy {
-
-class PPB_TCPSocket_Proxy : public InterfaceProxy {
- public:
-  explicit PPB_TCPSocket_Proxy(Dispatcher* dispatcher);
-  virtual ~PPB_TCPSocket_Proxy();
-
-  static PP_Resource CreateProxyResource(PP_Instance instance);
-
-  // InterfaceProxy implementation.
-  virtual bool OnMessageReceived(const IPC::Message& msg);
-
-  static const ApiID kApiID = API_ID_PPB_TCPSOCKET;
-
- private:
-  // Browser->plugin message handlers.
-  void OnMsgConnectACK(uint32 plugin_dispatcher_id,
-                       uint32 socket_id,
-                       int32_t result,
-                       const PP_NetAddress_Private& local_addr,
-                       const PP_NetAddress_Private& remote_addr);
-  void OnMsgReadACK(uint32 plugin_dispatcher_id,
-                    uint32 socket_id,
-                    int32_t result,
-                    const std::string& data);
-  void OnMsgWriteACK(uint32 plugin_dispatcher_id,
-                     uint32 socket_id,
-                     int32_t result);
-  void OnMsgSetOptionACK(uint32 plugin_dispatcher_id,
-                         uint32 socket_id,
-                         int32_t result);
-
-  DISALLOW_COPY_AND_ASSIGN(PPB_TCPSocket_Proxy);
-};
-
-}  // namespace proxy
-}  // namespace ppapi
-
-#endif  // PPAPI_PROXY_PPB_TCP_SOCKET_PROXY_H_
diff --git a/ppapi/proxy/ppp_messaging_proxy_perftest.cc b/ppapi/proxy/ppp_messaging_proxy_perftest.cc
index c52ea11..8520c24 100644
--- a/ppapi/proxy/ppp_messaging_proxy_perftest.cc
+++ b/ppapi/proxy/ppp_messaging_proxy_perftest.cc
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #include "base/command_line.h"
-#include "base/perftimer.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/test/perftimer.h"
 #include "ppapi/c/ppp_messaging.h"
 #include "ppapi/proxy/ppapi_proxy_test.h"
 #include "ppapi/proxy/serialized_var.h"
diff --git a/ppapi/proxy/resource_creation_proxy.cc b/ppapi/proxy/resource_creation_proxy.cc
index 53cade1..4814c97 100644
--- a/ppapi/proxy/resource_creation_proxy.cc
+++ b/ppapi/proxy/resource_creation_proxy.cc
@@ -31,13 +31,13 @@
 #include "ppapi/proxy/ppb_graphics_3d_proxy.h"
 #include "ppapi/proxy/ppb_image_data_proxy.h"
 #include "ppapi/proxy/ppb_network_monitor_private_proxy.h"
-#include "ppapi/proxy/ppb_tcp_socket_private_proxy.h"
-#include "ppapi/proxy/ppb_tcp_socket_proxy.h"
 #include "ppapi/proxy/ppb_video_decoder_proxy.h"
 #include "ppapi/proxy/ppb_x509_certificate_private_proxy.h"
 #include "ppapi/proxy/printing_resource.h"
 #include "ppapi/proxy/talk_resource.h"
 #include "ppapi/proxy/tcp_server_socket_private_resource.h"
+#include "ppapi/proxy/tcp_socket_private_resource.h"
+#include "ppapi/proxy/tcp_socket_resource.h"
 #include "ppapi/proxy/truetype_font_resource.h"
 #include "ppapi/proxy/udp_socket_private_resource.h"
 #include "ppapi/proxy/udp_socket_resource.h"
@@ -96,15 +96,6 @@
                                  type))->GetReference();
 }
 
-PP_Resource ResourceCreationProxy::CreateIsolatedFileSystem(
-    PP_Instance instance,
-    const char* fsid) {
-  FileSystemResource* fs = new FileSystemResource(
-      GetConnection(), instance, PP_FILESYSTEMTYPE_ISOLATED);
-  fs->InitIsolatedFileSystem(fsid);
-  return fs->GetReference();
-}
-
 PP_Resource ResourceCreationProxy::CreateIMEInputEvent(
     PP_Instance instance,
     PP_InputEvent_Type type,
@@ -330,12 +321,13 @@
 
 PP_Resource ResourceCreationProxy::CreateTCPSocket(
     PP_Instance instance) {
-  return PPB_TCPSocket_Proxy::CreateProxyResource(instance);
+  return (new TCPSocketResource(GetConnection(), instance))->GetReference();
 }
 
 PP_Resource ResourceCreationProxy::CreateTCPSocketPrivate(
     PP_Instance instance) {
-  return PPB_TCPSocket_Private_Proxy::CreateProxyResource(instance);
+  return (new TCPSocketPrivateResource(GetConnection(), instance))->
+      GetReference();
 }
 
 PP_Resource ResourceCreationProxy::CreateUDPSocket(PP_Instance instance) {
diff --git a/ppapi/proxy/resource_creation_proxy.h b/ppapi/proxy/resource_creation_proxy.h
index 47b40a8..edca902 100644
--- a/ppapi/proxy/resource_creation_proxy.h
+++ b/ppapi/proxy/resource_creation_proxy.h
@@ -45,9 +45,6 @@
       const PPB_FileRef_CreateInfo& create_info) OVERRIDE;
   virtual PP_Resource CreateFileSystem(PP_Instance instance,
                                        PP_FileSystemType type) OVERRIDE;
-  virtual PP_Resource CreateIsolatedFileSystem(
-      PP_Instance instance,
-      const char* fsid) OVERRIDE;
   virtual PP_Resource CreateIMEInputEvent(PP_Instance instance,
                                           PP_InputEvent_Type type,
                                           PP_TimeTicks time_stamp,
diff --git a/ppapi/proxy/tcp_server_socket_private_resource.cc b/ppapi/proxy/tcp_server_socket_private_resource.cc
index 6bbf466..745ed62 100644
--- a/ppapi/proxy/tcp_server_socket_private_resource.cc
+++ b/ppapi/proxy/tcp_server_socket_private_resource.cc
@@ -4,9 +4,8 @@
 
 #include "ppapi/proxy/tcp_server_socket_private_resource.h"
 
-#include "ppapi/proxy/plugin_dispatcher.h"
 #include "ppapi/proxy/ppapi_messages.h"
-#include "ppapi/proxy/ppb_tcp_socket_private_proxy.h"
+#include "ppapi/proxy/tcp_socket_private_resource.h"
 
 namespace ppapi {
 namespace proxy {
@@ -16,15 +15,8 @@
     PP_Instance instance)
     : PluginResource(connection, instance),
       state_(STATE_BEFORE_LISTENING),
-      local_addr_(),
-      plugin_dispatcher_id_(0) {
+      local_addr_() {
   SendCreate(BROWSER, PpapiHostMsg_TCPServerSocket_CreatePrivate());
-
-  PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
-  if (dispatcher)
-    plugin_dispatcher_id_ = dispatcher->plugin_dispatcher_id();
-  else
-    NOTREACHED();
 }
 
 TCPServerSocketPrivateResource::~TCPServerSocketPrivateResource() {
@@ -71,7 +63,7 @@
 
   Call<PpapiPluginMsg_TCPServerSocket_AcceptReply>(
       BROWSER,
-      PpapiHostMsg_TCPServerSocket_Accept(plugin_dispatcher_id_),
+      PpapiHostMsg_TCPServerSocket_Accept(),
       base::Bind(&TCPServerSocketPrivateResource::OnPluginMsgAcceptReply,
                  base::Unretained(this), tcp_socket));
   return PP_OK_COMPLETIONPENDING;
@@ -115,7 +107,7 @@
 void TCPServerSocketPrivateResource::OnPluginMsgAcceptReply(
     PP_Resource* tcp_socket,
     const ResourceMessageReplyParams& params,
-    uint32 accepted_socket_id,
+    int pending_resource_id,
     const PP_NetAddress_Private& local_addr,
     const PP_NetAddress_Private& remote_addr) {
   DCHECK(tcp_socket);
@@ -124,12 +116,10 @@
     return;
   }
   if (params.result() == PP_OK) {
-    *tcp_socket =
-        PPB_TCPSocket_Private_Proxy::CreateProxyResourceForConnectedSocket(
-            pp_instance(),
-            accepted_socket_id,
-            local_addr,
-            remote_addr);
+    *tcp_socket = (new TCPSocketPrivateResource(connection(), pp_instance(),
+                                                pending_resource_id,
+                                                local_addr,
+                                                remote_addr))->GetReference();
   }
   accept_callback_->Run(params.result());
 }
diff --git a/ppapi/proxy/tcp_server_socket_private_resource.h b/ppapi/proxy/tcp_server_socket_private_resource.h
index 95febde..21e190a 100644
--- a/ppapi/proxy/tcp_server_socket_private_resource.h
+++ b/ppapi/proxy/tcp_server_socket_private_resource.h
@@ -48,15 +48,13 @@
                               const PP_NetAddress_Private& local_addr);
   void OnPluginMsgAcceptReply(PP_Resource* tcp_socket,
                               const ResourceMessageReplyParams& params,
-                              uint32 accepted_socket_id,
+                              int pending_resource_id,
                               const PP_NetAddress_Private& local_addr,
                               const PP_NetAddress_Private& remote_addr);
 
   State state_;
   PP_NetAddress_Private local_addr_;
 
-  uint32 plugin_dispatcher_id_;
-
   scoped_refptr<TrackedCallback> listen_callback_;
   scoped_refptr<TrackedCallback> accept_callback_;
 
diff --git a/ppapi/proxy/tcp_socket_private_resource.cc b/ppapi/proxy/tcp_socket_private_resource.cc
new file mode 100644
index 0000000..0698e49
--- /dev/null
+++ b/ppapi/proxy/tcp_socket_private_resource.cc
@@ -0,0 +1,113 @@
+// 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 "ppapi/proxy/tcp_socket_private_resource.h"
+
+#include "ppapi/proxy/ppapi_messages.h"
+
+namespace ppapi {
+namespace proxy {
+
+TCPSocketPrivateResource::TCPSocketPrivateResource(Connection connection,
+                                                   PP_Instance instance)
+    : TCPSocketResourceBase(connection, instance, true) {
+  SendCreate(BROWSER, PpapiHostMsg_TCPSocket_CreatePrivate());
+}
+
+TCPSocketPrivateResource::TCPSocketPrivateResource(
+    Connection connection,
+    PP_Instance instance,
+    int pending_resource_id,
+    const PP_NetAddress_Private& local_addr,
+    const PP_NetAddress_Private& remote_addr)
+    : TCPSocketResourceBase(connection, instance, true,
+                            local_addr,
+                            remote_addr) {
+  AttachToPendingHost(BROWSER, pending_resource_id);
+}
+
+TCPSocketPrivateResource::~TCPSocketPrivateResource() {
+  DisconnectImpl();
+}
+
+thunk::PPB_TCPSocket_Private_API*
+TCPSocketPrivateResource::AsPPB_TCPSocket_Private_API() {
+  return this;
+}
+
+int32_t TCPSocketPrivateResource::Connect(
+    const char* host,
+    uint16_t port,
+    scoped_refptr<TrackedCallback> callback) {
+  return ConnectImpl(host, port, callback);
+}
+
+int32_t TCPSocketPrivateResource::ConnectWithNetAddress(
+    const PP_NetAddress_Private* addr,
+    scoped_refptr<TrackedCallback> callback) {
+  return ConnectWithNetAddressImpl(addr, callback);
+}
+
+PP_Bool TCPSocketPrivateResource::GetLocalAddress(
+    PP_NetAddress_Private* local_addr) {
+  return GetLocalAddressImpl(local_addr);
+}
+
+PP_Bool TCPSocketPrivateResource::GetRemoteAddress(
+    PP_NetAddress_Private* remote_addr) {
+  return GetRemoteAddressImpl(remote_addr);
+}
+
+int32_t TCPSocketPrivateResource::SSLHandshake(
+    const char* server_name,
+    uint16_t server_port,
+    scoped_refptr<TrackedCallback> callback) {
+  return SSLHandshakeImpl(server_name, server_port, callback);
+}
+
+PP_Resource TCPSocketPrivateResource::GetServerCertificate() {
+  return GetServerCertificateImpl();
+}
+
+PP_Bool TCPSocketPrivateResource::AddChainBuildingCertificate(
+    PP_Resource certificate,
+    PP_Bool trusted) {
+  return AddChainBuildingCertificateImpl(certificate, trusted);
+}
+
+int32_t TCPSocketPrivateResource::Read(
+    char* buffer,
+    int32_t bytes_to_read,
+    scoped_refptr<TrackedCallback> callback) {
+  return ReadImpl(buffer, bytes_to_read, callback);
+}
+
+int32_t TCPSocketPrivateResource::Write(
+    const char* buffer,
+    int32_t bytes_to_write,
+    scoped_refptr<TrackedCallback> callback) {
+  return WriteImpl(buffer, bytes_to_write, callback);
+}
+
+void TCPSocketPrivateResource::Disconnect() {
+  DisconnectImpl();
+}
+
+int32_t TCPSocketPrivateResource::SetOption(
+    PP_TCPSocketOption_Private name,
+    const PP_Var& value,
+    scoped_refptr<TrackedCallback> callback) {
+  switch (name) {
+    case PP_TCPSOCKETOPTION_PRIVATE_INVALID:
+      return PP_ERROR_BADARGUMENT;
+    case PP_TCPSOCKETOPTION_PRIVATE_NO_DELAY:
+      return SetOptionImpl(PP_TCPSOCKET_OPTION_NO_DELAY, value, callback);
+    default:
+      NOTREACHED();
+      return PP_ERROR_BADARGUMENT;
+  }
+}
+
+}  // namespace proxy
+}  // namespace ppapi
diff --git a/ppapi/proxy/tcp_socket_private_resource.h b/ppapi/proxy/tcp_socket_private_resource.h
new file mode 100644
index 0000000..f79ab9d
--- /dev/null
+++ b/ppapi/proxy/tcp_socket_private_resource.h
@@ -0,0 +1,69 @@
+// 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 PPAPI_PROXY_TCP_SOCKET_PRIVATE_RESOURCE_H_
+#define PPAPI_PROXY_TCP_SOCKET_PRIVATE_RESOURCE_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "ppapi/proxy/tcp_socket_resource_base.h"
+#include "ppapi/thunk/ppb_tcp_socket_private_api.h"
+
+namespace ppapi {
+namespace proxy {
+
+class PPAPI_PROXY_EXPORT TCPSocketPrivateResource
+    : public thunk::PPB_TCPSocket_Private_API,
+      public TCPSocketResourceBase {
+ public:
+  // C-tor used for new sockets.
+  TCPSocketPrivateResource(Connection connection, PP_Instance instance);
+
+  // C-tor used for already accepted sockets.
+  TCPSocketPrivateResource(Connection connection,
+                           PP_Instance instance,
+                           int pending_resource_id,
+                           const PP_NetAddress_Private& local_addr,
+                           const PP_NetAddress_Private& remote_addr);
+
+  virtual ~TCPSocketPrivateResource();
+
+  // PluginResource overrides.
+  virtual PPB_TCPSocket_Private_API* AsPPB_TCPSocket_Private_API() OVERRIDE;
+
+  // PPB_TCPSocket_Private_API implementation.
+  virtual int32_t Connect(const char* host,
+                          uint16_t port,
+                          scoped_refptr<TrackedCallback> callback) OVERRIDE;
+  virtual int32_t ConnectWithNetAddress(
+      const PP_NetAddress_Private* addr,
+      scoped_refptr<TrackedCallback> callback) OVERRIDE;
+  virtual PP_Bool GetLocalAddress(PP_NetAddress_Private* local_addr) OVERRIDE;
+  virtual PP_Bool GetRemoteAddress(PP_NetAddress_Private* remote_addr) OVERRIDE;
+  virtual int32_t SSLHandshake(
+      const char* server_name,
+      uint16_t server_port,
+      scoped_refptr<TrackedCallback> callback) OVERRIDE;
+  virtual PP_Resource GetServerCertificate() OVERRIDE;
+  virtual PP_Bool AddChainBuildingCertificate(PP_Resource certificate,
+                                              PP_Bool trusted) OVERRIDE;
+  virtual int32_t Read(char* buffer,
+                      int32_t bytes_to_read,
+                      scoped_refptr<TrackedCallback> callback) OVERRIDE;
+  virtual int32_t Write(const char* buffer,
+                        int32_t bytes_to_write,
+                        scoped_refptr<TrackedCallback> callback) OVERRIDE;
+  virtual void Disconnect() OVERRIDE;
+  virtual int32_t SetOption(PP_TCPSocketOption_Private name,
+                            const PP_Var& value,
+                            scoped_refptr<TrackedCallback> callback) OVERRIDE;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TCPSocketPrivateResource);
+};
+
+}  // namespace proxy
+}  // namespace ppapi
+
+#endif  // PPAPI_PROXY_TCP_SOCKET_PRIVATE_RESOURCE_H_
diff --git a/ppapi/proxy/tcp_socket_resource.cc b/ppapi/proxy/tcp_socket_resource.cc
new file mode 100644
index 0000000..085aab3
--- /dev/null
+++ b/ppapi/proxy/tcp_socket_resource.cc
@@ -0,0 +1,92 @@
+// 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 "ppapi/proxy/tcp_socket_resource.h"
+
+#include "ppapi/proxy/ppapi_messages.h"
+#include "ppapi/thunk/enter.h"
+#include "ppapi/thunk/ppb_net_address_api.h"
+
+namespace ppapi {
+namespace proxy {
+
+namespace {
+
+typedef thunk::EnterResourceNoLock<thunk::PPB_NetAddress_API>
+    EnterNetAddressNoLock;
+
+}  // namespace
+
+TCPSocketResource::TCPSocketResource(Connection connection,
+                                     PP_Instance instance)
+    : TCPSocketResourceBase(connection, instance, false) {
+  SendCreate(BROWSER, PpapiHostMsg_TCPSocket_Create());
+}
+
+TCPSocketResource::~TCPSocketResource() {
+  DisconnectImpl();
+}
+
+thunk::PPB_TCPSocket_API* TCPSocketResource::AsPPB_TCPSocket_API() {
+  return this;
+}
+
+int32_t TCPSocketResource::Connect(PP_Resource addr,
+                                   scoped_refptr<TrackedCallback> callback) {
+  EnterNetAddressNoLock enter(addr, true);
+  if (enter.failed())
+    return PP_ERROR_BADARGUMENT;
+
+  return ConnectWithNetAddressImpl(&enter.object()->GetNetAddressPrivate(),
+                                   callback);
+}
+
+PP_Resource TCPSocketResource::GetLocalAddress() {
+  PP_NetAddress_Private addr_private;
+  if (!GetLocalAddressImpl(&addr_private))
+    return 0;
+
+  thunk::EnterResourceCreationNoLock enter(pp_instance());
+  if (enter.failed())
+    return 0;
+  return enter.functions()->CreateNetAddressFromNetAddressPrivate(
+      pp_instance(), addr_private);
+}
+
+PP_Resource TCPSocketResource::GetRemoteAddress() {
+  PP_NetAddress_Private addr_private;
+  if (!GetRemoteAddressImpl(&addr_private))
+    return 0;
+
+  thunk::EnterResourceCreationNoLock enter(pp_instance());
+  if (enter.failed())
+    return 0;
+  return enter.functions()->CreateNetAddressFromNetAddressPrivate(
+      pp_instance(), addr_private);
+}
+
+int32_t TCPSocketResource::Read(char* buffer,
+                                int32_t bytes_to_read,
+                                scoped_refptr<TrackedCallback> callback) {
+  return ReadImpl(buffer, bytes_to_read, callback);
+}
+
+int32_t TCPSocketResource::Write(const char* buffer,
+                                 int32_t bytes_to_write,
+                                 scoped_refptr<TrackedCallback> callback) {
+  return WriteImpl(buffer, bytes_to_write, callback);
+}
+
+void TCPSocketResource::Close() {
+  DisconnectImpl();
+}
+
+int32_t TCPSocketResource::SetOption(PP_TCPSocket_Option name,
+                                     const PP_Var& value,
+                                     scoped_refptr<TrackedCallback> callback) {
+  return SetOptionImpl(name, value, callback);
+}
+
+}  // namespace proxy
+}  // namespace ppapi
diff --git a/ppapi/proxy/tcp_socket_resource.h b/ppapi/proxy/tcp_socket_resource.h
new file mode 100644
index 0000000..05b4fe3
--- /dev/null
+++ b/ppapi/proxy/tcp_socket_resource.h
@@ -0,0 +1,48 @@
+// 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 PPAPI_PROXY_TCP_SOCKET_RESOURCE_H_
+#define PPAPI_PROXY_TCP_SOCKET_RESOURCE_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "ppapi/proxy/tcp_socket_resource_base.h"
+#include "ppapi/thunk/ppb_tcp_socket_api.h"
+
+namespace ppapi {
+namespace proxy {
+
+class PPAPI_PROXY_EXPORT TCPSocketResource : public thunk::PPB_TCPSocket_API,
+                                             public TCPSocketResourceBase {
+ public:
+  TCPSocketResource(Connection connection, PP_Instance instance);
+  virtual ~TCPSocketResource();
+
+  // PluginResource overrides.
+  virtual thunk::PPB_TCPSocket_API* AsPPB_TCPSocket_API() OVERRIDE;
+
+  // thunk::PPB_TCPSocket_API implementation.
+  virtual int32_t Connect(PP_Resource addr,
+                          scoped_refptr<TrackedCallback> callback) OVERRIDE;
+  virtual PP_Resource GetLocalAddress() OVERRIDE;
+  virtual PP_Resource GetRemoteAddress() OVERRIDE;
+  virtual int32_t Read(char* buffer,
+                       int32_t bytes_to_read,
+                       scoped_refptr<TrackedCallback> callback) OVERRIDE;
+  virtual int32_t Write(const char* buffer,
+                        int32_t bytes_to_write,
+                        scoped_refptr<TrackedCallback> callback) OVERRIDE;
+  virtual void Close() OVERRIDE;
+  virtual int32_t SetOption(PP_TCPSocket_Option name,
+                            const PP_Var& value,
+                            scoped_refptr<TrackedCallback> callback) OVERRIDE;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TCPSocketResource);
+};
+
+}  // namespace proxy
+}  // namespace ppapi
+
+#endif  // PPAPI_PROXY_TCP_SOCKET_RESOURCE_H_
diff --git a/ppapi/proxy/tcp_socket_resource_base.cc b/ppapi/proxy/tcp_socket_resource_base.cc
new file mode 100644
index 0000000..f02895b
--- /dev/null
+++ b/ppapi/proxy/tcp_socket_resource_base.cc
@@ -0,0 +1,407 @@
+// 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 "ppapi/proxy/tcp_socket_resource_base.h"
+
+#include <cstring>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "ppapi/c/pp_bool.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/proxy/error_conversion.h"
+#include "ppapi/proxy/ppapi_messages.h"
+#include "ppapi/shared_impl/ppapi_globals.h"
+#include "ppapi/shared_impl/private/ppb_x509_certificate_private_shared.h"
+#include "ppapi/shared_impl/socket_option_data.h"
+#include "ppapi/shared_impl/var.h"
+#include "ppapi/shared_impl/var_tracker.h"
+#include "ppapi/thunk/enter.h"
+#include "ppapi/thunk/ppb_x509_certificate_private_api.h"
+
+namespace ppapi {
+namespace proxy {
+
+const int32_t TCPSocketResourceBase::kMaxReadSize = 1024 * 1024;
+const int32_t TCPSocketResourceBase::kMaxWriteSize = 1024 * 1024;
+const int32_t TCPSocketResourceBase::kMaxSendBufferSize =
+    1024 * TCPSocketResourceBase::kMaxWriteSize;
+const int32_t TCPSocketResourceBase::kMaxReceiveBufferSize =
+    1024 * TCPSocketResourceBase::kMaxReadSize;
+
+TCPSocketResourceBase::TCPSocketResourceBase(Connection connection,
+                                             PP_Instance instance,
+                                             bool private_api)
+    : PluginResource(connection, instance),
+      connection_state_(BEFORE_CONNECT),
+      read_buffer_(NULL),
+      bytes_to_read_(-1),
+      private_api_(private_api) {
+  local_addr_.size = 0;
+  memset(local_addr_.data, 0,
+         arraysize(local_addr_.data) * sizeof(*local_addr_.data));
+  remote_addr_.size = 0;
+  memset(remote_addr_.data, 0,
+         arraysize(remote_addr_.data) * sizeof(*remote_addr_.data));
+}
+
+TCPSocketResourceBase::TCPSocketResourceBase(
+    Connection connection,
+    PP_Instance instance,
+    bool private_api,
+    const PP_NetAddress_Private& local_addr,
+    const PP_NetAddress_Private& remote_addr)
+    : PluginResource(connection, instance),
+      connection_state_(CONNECTED),
+      read_buffer_(NULL),
+      bytes_to_read_(-1),
+      local_addr_(local_addr),
+      remote_addr_(remote_addr),
+      private_api_(private_api) {
+}
+
+TCPSocketResourceBase::~TCPSocketResourceBase() {
+}
+
+int32_t TCPSocketResourceBase::ConnectImpl(
+    const char* host,
+    uint16_t port,
+    scoped_refptr<TrackedCallback> callback) {
+  if (!host)
+    return PP_ERROR_BADARGUMENT;
+  if (connection_state_ != BEFORE_CONNECT)
+    return PP_ERROR_FAILED;
+  if (TrackedCallback::IsPending(connect_callback_))
+    return PP_ERROR_INPROGRESS;  // Can only have one pending request.
+
+  connect_callback_ = callback;
+
+  Call<PpapiPluginMsg_TCPSocket_ConnectReply>(
+      BROWSER,
+      PpapiHostMsg_TCPSocket_Connect(host, port),
+      base::Bind(&TCPSocketResourceBase::OnPluginMsgConnectReply,
+                 base::Unretained(this)));
+  return PP_OK_COMPLETIONPENDING;
+}
+
+int32_t TCPSocketResourceBase::ConnectWithNetAddressImpl(
+    const PP_NetAddress_Private* addr,
+    scoped_refptr<TrackedCallback> callback) {
+  if (!addr)
+    return PP_ERROR_BADARGUMENT;
+  if (connection_state_ != BEFORE_CONNECT)
+    return PP_ERROR_FAILED;
+  if (TrackedCallback::IsPending(connect_callback_))
+    return PP_ERROR_INPROGRESS;  // Can only have one pending request.
+
+  connect_callback_ = callback;
+
+  Call<PpapiPluginMsg_TCPSocket_ConnectReply>(
+      BROWSER,
+      PpapiHostMsg_TCPSocket_ConnectWithNetAddress(*addr),
+      base::Bind(&TCPSocketResourceBase::OnPluginMsgConnectReply,
+                 base::Unretained(this)));
+  return PP_OK_COMPLETIONPENDING;
+}
+
+PP_Bool TCPSocketResourceBase::GetLocalAddressImpl(
+    PP_NetAddress_Private* local_addr) {
+  if (!IsConnected() || !local_addr)
+    return PP_FALSE;
+  *local_addr = local_addr_;
+  return PP_TRUE;
+}
+
+PP_Bool TCPSocketResourceBase::GetRemoteAddressImpl(
+    PP_NetAddress_Private* remote_addr) {
+  if (!IsConnected() || !remote_addr)
+    return PP_FALSE;
+  *remote_addr = remote_addr_;
+  return PP_TRUE;
+}
+
+int32_t TCPSocketResourceBase::SSLHandshakeImpl(
+    const char* server_name,
+    uint16_t server_port,
+    scoped_refptr<TrackedCallback> callback) {
+  if (!server_name)
+    return PP_ERROR_BADARGUMENT;
+
+  if (connection_state_ != CONNECTED)
+    return PP_ERROR_FAILED;
+  if (TrackedCallback::IsPending(ssl_handshake_callback_) ||
+      TrackedCallback::IsPending(read_callback_) ||
+      TrackedCallback::IsPending(write_callback_)) {
+    return PP_ERROR_INPROGRESS;
+  }
+
+  ssl_handshake_callback_ = callback;
+
+  Call<PpapiPluginMsg_TCPSocket_SSLHandshakeReply>(
+      BROWSER,
+      PpapiHostMsg_TCPSocket_SSLHandshake(server_name,
+                                          server_port,
+                                          trusted_certificates_,
+                                          untrusted_certificates_),
+      base::Bind(&TCPSocketResourceBase::OnPluginMsgSSLHandshakeReply,
+                 base::Unretained(this)));
+  return PP_OK_COMPLETIONPENDING;
+}
+
+PP_Resource TCPSocketResourceBase::GetServerCertificateImpl() {
+  if (!server_certificate_.get())
+    return 0;
+  return server_certificate_->GetReference();
+}
+
+PP_Bool TCPSocketResourceBase::AddChainBuildingCertificateImpl(
+    PP_Resource certificate,
+    PP_Bool trusted) {
+  // TODO(raymes): The plumbing for this functionality is implemented but the
+  // certificates aren't yet used for the connection, so just return false for
+  // now.
+  return PP_FALSE;
+
+  thunk::EnterResourceNoLock<thunk::PPB_X509Certificate_Private_API>
+  enter_cert(certificate, true);
+  if (enter_cert.failed())
+    return PP_FALSE;
+
+  PP_Var der_var = enter_cert.object()->GetField(
+      PP_X509CERTIFICATE_PRIVATE_RAW);
+  ArrayBufferVar* der_array_buffer = ArrayBufferVar::FromPPVar(der_var);
+  PP_Bool success = PP_FALSE;
+  if (der_array_buffer) {
+    const char* der_bytes = static_cast<const char*>(der_array_buffer->Map());
+    uint32_t der_length = der_array_buffer->ByteLength();
+    std::vector<char> der(der_bytes, der_bytes + der_length);
+    if (PP_ToBool(trusted))
+      trusted_certificates_.push_back(der);
+    else
+      untrusted_certificates_.push_back(der);
+    success = PP_TRUE;
+  }
+  PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(der_var);
+  return success;
+}
+
+int32_t TCPSocketResourceBase::ReadImpl(
+    char* buffer,
+    int32_t bytes_to_read,
+    scoped_refptr<TrackedCallback> callback) {
+  if (!buffer || bytes_to_read <= 0)
+    return PP_ERROR_BADARGUMENT;
+
+  if (!IsConnected())
+    return PP_ERROR_FAILED;
+  if (TrackedCallback::IsPending(read_callback_) ||
+      TrackedCallback::IsPending(ssl_handshake_callback_))
+    return PP_ERROR_INPROGRESS;
+  read_buffer_ = buffer;
+  bytes_to_read_ = std::min(bytes_to_read, kMaxReadSize);
+  read_callback_ = callback;
+
+  Call<PpapiPluginMsg_TCPSocket_ReadReply>(
+      BROWSER,
+      PpapiHostMsg_TCPSocket_Read(bytes_to_read_),
+      base::Bind(&TCPSocketResourceBase::OnPluginMsgReadReply,
+                 base::Unretained(this)));
+  return PP_OK_COMPLETIONPENDING;
+}
+
+int32_t TCPSocketResourceBase::WriteImpl(
+    const char* buffer,
+    int32_t bytes_to_write,
+    scoped_refptr<TrackedCallback> callback) {
+  if (!buffer || bytes_to_write <= 0)
+    return PP_ERROR_BADARGUMENT;
+
+  if (!IsConnected())
+    return PP_ERROR_FAILED;
+  if (TrackedCallback::IsPending(write_callback_) ||
+      TrackedCallback::IsPending(ssl_handshake_callback_))
+    return PP_ERROR_INPROGRESS;
+
+  if (bytes_to_write > kMaxWriteSize)
+    bytes_to_write = kMaxWriteSize;
+
+  write_callback_ = callback;
+
+  Call<PpapiPluginMsg_TCPSocket_WriteReply>(
+      BROWSER,
+      PpapiHostMsg_TCPSocket_Write(std::string(buffer, bytes_to_write)),
+      base::Bind(&TCPSocketResourceBase::OnPluginMsgWriteReply,
+                 base::Unretained(this)));
+  return PP_OK_COMPLETIONPENDING;
+}
+
+void TCPSocketResourceBase::DisconnectImpl() {
+  if (connection_state_ == DISCONNECTED)
+    return;
+
+  connection_state_ = DISCONNECTED;
+
+  Post(BROWSER, PpapiHostMsg_TCPSocket_Disconnect());
+
+  PostAbortIfNecessary(&connect_callback_);
+  PostAbortIfNecessary(&ssl_handshake_callback_);
+  PostAbortIfNecessary(&read_callback_);
+  PostAbortIfNecessary(&write_callback_);
+  read_buffer_ = NULL;
+  bytes_to_read_ = -1;
+  server_certificate_ = NULL;
+}
+
+int32_t TCPSocketResourceBase::SetOptionImpl(
+    PP_TCPSocket_Option name,
+    const PP_Var& value,
+    scoped_refptr<TrackedCallback> callback) {
+  if (!IsConnected())
+    return PP_ERROR_FAILED;
+
+  SocketOptionData option_data;
+  switch (name) {
+    case PP_TCPSOCKET_OPTION_NO_DELAY: {
+      if (value.type != PP_VARTYPE_BOOL)
+        return PP_ERROR_BADARGUMENT;
+      option_data.SetBool(PP_ToBool(value.value.as_bool));
+      break;
+    }
+    case PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE:
+    case PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE: {
+      if (value.type != PP_VARTYPE_INT32)
+        return PP_ERROR_BADARGUMENT;
+      option_data.SetInt32(value.value.as_int);
+      break;
+    }
+    default: {
+      NOTREACHED();
+      return PP_ERROR_BADARGUMENT;
+    }
+  }
+
+  set_option_callbacks_.push(callback);
+
+  Call<PpapiPluginMsg_TCPSocket_SetOptionReply>(
+      BROWSER,
+      PpapiHostMsg_TCPSocket_SetOption(name, option_data),
+      base::Bind(&TCPSocketResourceBase::OnPluginMsgSetOptionReply,
+                 base::Unretained(this)));
+  return PP_OK_COMPLETIONPENDING;
+}
+
+bool TCPSocketResourceBase::IsConnected() const {
+  return connection_state_ == CONNECTED || connection_state_ == SSL_CONNECTED;
+}
+
+void TCPSocketResourceBase::PostAbortIfNecessary(
+    scoped_refptr<TrackedCallback>* callback) {
+  if (TrackedCallback::IsPending(*callback))
+    (*callback)->PostAbort();
+}
+
+void TCPSocketResourceBase::OnPluginMsgConnectReply(
+    const ResourceMessageReplyParams& params,
+    const PP_NetAddress_Private& local_addr,
+    const PP_NetAddress_Private& remote_addr) {
+  // It is possible that |connect_callback_| is pending while
+  // |connection_state_| is not BEFORE_CONNECT: DisconnectImpl() has been
+  // called, but a ConnectCompleted notification came earlier than the task to
+  // abort |connect_callback_|. We don't want to update |connection_state_| or
+  // other members in that case.
+  if (connection_state_ != BEFORE_CONNECT ||
+      !TrackedCallback::IsPending(connect_callback_)) {
+    return;
+  }
+
+  if (params.result() == PP_OK) {
+    local_addr_ = local_addr;
+    remote_addr_ = remote_addr;
+    connection_state_ = CONNECTED;
+  }
+  RunCallback(connect_callback_, params.result());
+}
+
+void TCPSocketResourceBase::OnPluginMsgSSLHandshakeReply(
+      const ResourceMessageReplyParams& params,
+      const PPB_X509Certificate_Fields& certificate_fields) {
+  // It is possible that |ssl_handshake_callback_| is pending while
+  // |connection_state_| is not CONNECT: DisconnectImpl() has been
+  // called, but a SSLHandshakeCompleted notification came earlier than the task
+  // to abort |ssl_handshake_callback_|. We don't want to update
+  // |connection_state_| or other members in that case.
+  if (connection_state_ != CONNECTED ||
+      !TrackedCallback::IsPending(ssl_handshake_callback_)) {
+    return;
+  }
+
+  if (params.result() == PP_OK) {
+    connection_state_ = SSL_CONNECTED;
+    server_certificate_ = new PPB_X509Certificate_Private_Shared(
+        OBJECT_IS_PROXY,
+        pp_instance(),
+        certificate_fields);
+    RunCallback(ssl_handshake_callback_, params.result());
+  } else {
+    // The resource might be released in the callback so we need to hold
+    // a reference so we can Disconnect() first.
+    AddRef();
+    RunCallback(ssl_handshake_callback_, params.result());
+    DisconnectImpl();
+    Release();
+  }
+}
+
+void TCPSocketResourceBase::OnPluginMsgReadReply(
+    const ResourceMessageReplyParams& params,
+    const std::string& data) {
+  // It is possible that |read_callback_| is pending while |read_buffer_| is
+  // NULL: DisconnectImpl() has been called, but a ReadCompleted notification
+  // came earlier than the task to abort |read_callback_|. We shouldn't access
+  // the buffer in that case. The user may have released it.
+  if (!TrackedCallback::IsPending(read_callback_) || !read_buffer_)
+    return;
+
+  const bool succeeded = params.result() == PP_OK;
+  if (succeeded) {
+    CHECK_LE(static_cast<int32_t>(data.size()), bytes_to_read_);
+    if (!data.empty())
+      memmove(read_buffer_, data.c_str(), data.size());
+  }
+  read_buffer_ = NULL;
+  bytes_to_read_ = -1;
+
+  read_callback_->Run(succeeded ?
+                      static_cast<int32_t>(data.size()) :
+                      ConvertNetworkAPIErrorForCompatibility(params.result(),
+                                                             private_api_));
+}
+
+void TCPSocketResourceBase::OnPluginMsgWriteReply(
+    const ResourceMessageReplyParams& params) {
+  if (!TrackedCallback::IsPending(write_callback_))
+    return;
+  RunCallback(write_callback_, params.result());
+}
+
+void TCPSocketResourceBase::OnPluginMsgSetOptionReply(
+    const ResourceMessageReplyParams& params) {
+  if (set_option_callbacks_.empty()) {
+    NOTREACHED();
+    return;
+  }
+  scoped_refptr<TrackedCallback> callback = set_option_callbacks_.front();
+  set_option_callbacks_.pop();
+  if (TrackedCallback::IsPending(callback))
+    RunCallback(callback, params.result());
+}
+
+void TCPSocketResourceBase::RunCallback(scoped_refptr<TrackedCallback> callback,
+                                        int32_t pp_result) {
+  callback->Run(ConvertNetworkAPIErrorForCompatibility(pp_result,
+                                                       private_api_));
+}
+
+}  // namespace ppapi
+}  // namespace proxy
diff --git a/ppapi/proxy/tcp_socket_resource_base.h b/ppapi/proxy/tcp_socket_resource_base.h
new file mode 100644
index 0000000..7e8a481
--- /dev/null
+++ b/ppapi/proxy/tcp_socket_resource_base.h
@@ -0,0 +1,144 @@
+// 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 PPAPI_PROXY_TCP_SOCKET_RESOURCE_BASE_H_
+#define PPAPI_PROXY_TCP_SOCKET_RESOURCE_BASE_H_
+
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "ppapi/c/ppb_tcp_socket.h"
+#include "ppapi/c/private/ppb_net_address_private.h"
+#include "ppapi/proxy/plugin_resource.h"
+#include "ppapi/proxy/ppapi_proxy_export.h"
+#include "ppapi/shared_impl/tracked_callback.h"
+
+namespace ppapi {
+
+class PPB_X509Certificate_Fields;
+class PPB_X509Certificate_Private_Shared;
+class SocketOptionData;
+
+namespace proxy {
+
+class PPAPI_PROXY_EXPORT TCPSocketResourceBase : public PluginResource {
+ public:
+  // The maximum number of bytes that each PpapiHostMsg_PPBTCPSocket_Read
+  // message is allowed to request.
+  static const int32_t kMaxReadSize;
+  // The maximum number of bytes that each PpapiHostMsg_PPBTCPSocket_Write
+  // message is allowed to carry.
+  static const int32_t kMaxWriteSize;
+
+  // The maximum number that we allow for setting
+  // PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE. This number is only for input
+  // argument sanity check, it doesn't mean the browser guarantees to support
+  // such a buffer size.
+  static const int32_t kMaxSendBufferSize;
+  // The maximum number that we allow for setting
+  // PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE. This number is only for input
+  // argument sanity check, it doesn't mean the browser guarantees to support
+  // such a buffer size.
+  static const int32_t kMaxReceiveBufferSize;
+
+ protected:
+  enum ConnectionState {
+    // Before a connection is successfully established (including a connect
+    // request is pending or a previous connect request failed).
+    BEFORE_CONNECT,
+    // A connection has been successfully established (including a request of
+    // initiating SSL is pending).
+    CONNECTED,
+    // An SSL connection has been successfully established.
+    SSL_CONNECTED,
+    // The connection has been ended.
+    DISCONNECTED
+  };
+
+  // C-tor used for new sockets.
+  TCPSocketResourceBase(Connection connection,
+                        PP_Instance instance,
+                        bool private_api);
+
+  // C-tor used for already accepted sockets.
+  TCPSocketResourceBase(Connection connection,
+                        PP_Instance instance,
+                        bool private_api,
+                        const PP_NetAddress_Private& local_addr,
+                        const PP_NetAddress_Private& remote_addr);
+
+  virtual ~TCPSocketResourceBase();
+
+  int32_t ConnectImpl(const char* host,
+                      uint16_t port,
+                      scoped_refptr<TrackedCallback> callback);
+  int32_t ConnectWithNetAddressImpl(const PP_NetAddress_Private* addr,
+                                    scoped_refptr<TrackedCallback> callback);
+  PP_Bool GetLocalAddressImpl(PP_NetAddress_Private* local_addr);
+  PP_Bool GetRemoteAddressImpl(PP_NetAddress_Private* remote_addr);
+  int32_t SSLHandshakeImpl(const char* server_name,
+                           uint16_t server_port,
+                           scoped_refptr<TrackedCallback> callback);
+  PP_Resource GetServerCertificateImpl();
+  PP_Bool AddChainBuildingCertificateImpl(PP_Resource certificate,
+                                          PP_Bool trusted);
+  int32_t ReadImpl(char* buffer,
+                   int32_t bytes_to_read,
+                   scoped_refptr<TrackedCallback> callback);
+  int32_t WriteImpl(const char* buffer,
+                    int32_t bytes_to_write,
+                    scoped_refptr<TrackedCallback> callback);
+  void DisconnectImpl();
+  int32_t SetOptionImpl(PP_TCPSocket_Option name,
+                        const PP_Var& value,
+                        scoped_refptr<TrackedCallback> callback);
+
+  bool IsConnected() const;
+  void PostAbortIfNecessary(scoped_refptr<TrackedCallback>* callback);
+
+  // IPC message handlers.
+  void OnPluginMsgConnectReply(const ResourceMessageReplyParams& params,
+                               const PP_NetAddress_Private& local_addr,
+                               const PP_NetAddress_Private& remote_addr);
+  void OnPluginMsgSSLHandshakeReply(
+      const ResourceMessageReplyParams& params,
+      const PPB_X509Certificate_Fields& certificate_fields);
+  void OnPluginMsgReadReply(const ResourceMessageReplyParams& params,
+                            const std::string& data);
+  void OnPluginMsgWriteReply(const ResourceMessageReplyParams& params);
+  void OnPluginMsgSetOptionReply(const ResourceMessageReplyParams& params);
+
+  scoped_refptr<TrackedCallback> connect_callback_;
+  scoped_refptr<TrackedCallback> ssl_handshake_callback_;
+  scoped_refptr<TrackedCallback> read_callback_;
+  scoped_refptr<TrackedCallback> write_callback_;
+  std::queue<scoped_refptr<TrackedCallback> > set_option_callbacks_;
+
+  ConnectionState connection_state_;
+  char* read_buffer_;
+  int32_t bytes_to_read_;
+
+  PP_NetAddress_Private local_addr_;
+  PP_NetAddress_Private remote_addr_;
+
+  scoped_refptr<PPB_X509Certificate_Private_Shared> server_certificate_;
+
+  std::vector<std::vector<char> > trusted_certificates_;
+  std::vector<std::vector<char> > untrusted_certificates_;
+
+ private:
+  void RunCallback(scoped_refptr<TrackedCallback> callback, int32_t pp_result);
+
+  bool private_api_;
+
+  DISALLOW_COPY_AND_ASSIGN(TCPSocketResourceBase);
+};
+
+}  // namespace proxy
+}  // namespace ppapi
+
+#endif  // PPAPI_PROXY_TCP_SOCKET_RESOURCE_BASE_H_
diff --git a/ppapi/proxy/udp_socket_resource_base.cc b/ppapi/proxy/udp_socket_resource_base.cc
index 166f2e3..8ef3835 100644
--- a/ppapi/proxy/udp_socket_resource_base.cc
+++ b/ppapi/proxy/udp_socket_resource_base.cc
@@ -11,6 +11,7 @@
 #include "ppapi/c/pp_bool.h"
 #include "ppapi/c/pp_completion_callback.h"
 #include "ppapi/c/pp_errors.h"
+#include "ppapi/proxy/error_conversion.h"
 #include "ppapi/proxy/ppapi_messages.h"
 #include "ppapi/shared_impl/socket_option_data.h"
 #include "ppapi/thunk/enter.h"
@@ -19,23 +20,6 @@
 namespace ppapi {
 namespace proxy {
 
-namespace {
-
-int32_t ConvertPPError(int32_t pp_error, bool private_api) {
-  // The private API doesn't return network-specific error codes or
-  // PP_ERROR_NOACCESS. In order to preserve the behavior, we convert those to
-  // PP_ERROR_FAILED.
-  if (private_api &&
-      (pp_error <= PP_ERROR_CONNECTION_CLOSED ||
-       pp_error == PP_ERROR_NOACCESS)) {
-    return PP_ERROR_FAILED;
-  }
-
-  return pp_error;
-}
-
-}  // namespace
-
 const int32_t UDPSocketResourceBase::kMaxReadSize = 1024 * 1024;
 const int32_t UDPSocketResourceBase::kMaxWriteSize = 1024 * 1024;
 const int32_t UDPSocketResourceBase::kMaxSendBufferSize =
@@ -227,7 +211,7 @@
     scoped_refptr<TrackedCallback> callback,
     const ResourceMessageReplyParams& params) {
   if (TrackedCallback::IsPending(callback))
-    callback->Run(ConvertPPError(params.result(), private_api_));
+    RunCallback(callback, params.result());
 }
 
 void UDPSocketResourceBase::OnPluginMsgBindReply(
@@ -243,7 +227,7 @@
   if (params.result() == PP_OK)
     bound_ = true;
   bound_addr_ = bound_addr;
-  bind_callback_->Run(ConvertPPError(params.result(), private_api_));
+  RunCallback(bind_callback_, params.result());
 }
 
 void UDPSocketResourceBase::OnPluginMsgRecvFromReply(
@@ -280,9 +264,9 @@
   recvfrom_addr_ = addr;
 
   if (result == PP_OK)
-    recvfrom_callback_->Run(static_cast<int32_t>(data.size()));
+    RunCallback(recvfrom_callback_, static_cast<int32_t>(data.size()));
   else
-    recvfrom_callback_->Run(ConvertPPError(result, private_api_));
+    RunCallback(recvfrom_callback_, result);
 }
 
 void UDPSocketResourceBase::OnPluginMsgSendToReply(
@@ -292,9 +276,15 @@
     return;
 
   if (params.result() == PP_OK)
-    sendto_callback_->Run(bytes_written);
+    RunCallback(sendto_callback_, bytes_written);
   else
-    sendto_callback_->Run(ConvertPPError(params.result(), private_api_));
+    RunCallback(sendto_callback_, params.result());
+}
+
+void UDPSocketResourceBase::RunCallback(scoped_refptr<TrackedCallback> callback,
+                                        int32_t pp_result) {
+  callback->Run(ConvertNetworkAPIErrorForCompatibility(pp_result,
+                                                       private_api_));
 }
 
 }  // namespace proxy
diff --git a/ppapi/proxy/udp_socket_resource_base.h b/ppapi/proxy/udp_socket_resource_base.h
index 925475e..978e774 100644
--- a/ppapi/proxy/udp_socket_resource_base.h
+++ b/ppapi/proxy/udp_socket_resource_base.h
@@ -80,6 +80,8 @@
   void OnPluginMsgSendToReply(const ResourceMessageReplyParams& params,
                               int32_t bytes_written);
 
+  void RunCallback(scoped_refptr<TrackedCallback> callback, int32_t pp_result);
+
   bool private_api_;
   bool bound_;
   bool closed_;
diff --git a/ppapi/shared_impl/api_id.h b/ppapi/shared_impl/api_id.h
index 3707719..49e0dcb 100644
--- a/ppapi/shared_impl/api_id.h
+++ b/ppapi/shared_impl/api_id.h
@@ -39,8 +39,6 @@
   API_ID_PPB_OPENGLES2,
   API_ID_PPB_PDF,
   API_ID_PPB_SURFACE_3D,
-  API_ID_PPB_TCPSOCKET,
-  API_ID_PPB_TCPSOCKET_PRIVATE,
   API_ID_PPB_TALK,
   API_ID_PPB_TESTING,
   API_ID_PPB_TEXT_INPUT,
diff --git a/ppapi/shared_impl/private/tcp_socket_private_impl.cc b/ppapi/shared_impl/private/tcp_socket_private_impl.cc
deleted file mode 100644
index 56a3585..0000000
--- a/ppapi/shared_impl/private/tcp_socket_private_impl.cc
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ppapi/shared_impl/private/tcp_socket_private_impl.h"
-
-#include "base/logging.h"
-#include "ppapi/c/pp_errors.h"
-
-namespace ppapi {
-
-TCPSocketPrivateImpl::TCPSocketPrivateImpl(PP_Instance instance,
-                                           uint32 socket_id)
-    : Resource(OBJECT_IS_IMPL, instance),
-      TCPSocketShared(OBJECT_IS_IMPL, socket_id) {
-}
-
-TCPSocketPrivateImpl::TCPSocketPrivateImpl(const HostResource& resource,
-                                           uint32 socket_id)
-    : Resource(OBJECT_IS_PROXY, resource),
-      TCPSocketShared(OBJECT_IS_PROXY, socket_id) {
-}
-
-TCPSocketPrivateImpl::~TCPSocketPrivateImpl() {
-}
-
-thunk::PPB_TCPSocket_Private_API*
-TCPSocketPrivateImpl::AsPPB_TCPSocket_Private_API() {
-  return this;
-}
-
-int32_t TCPSocketPrivateImpl::Connect(const char* host,
-                                      uint16_t port,
-                                      scoped_refptr<TrackedCallback> callback) {
-  return ConnectImpl(host, port, callback);
-}
-
-int32_t TCPSocketPrivateImpl::ConnectWithNetAddress(
-    const PP_NetAddress_Private* addr,
-    scoped_refptr<TrackedCallback> callback) {
-  return ConnectWithNetAddressImpl(addr, callback);
-}
-
-PP_Bool TCPSocketPrivateImpl::GetLocalAddress(
-    PP_NetAddress_Private* local_addr) {
-  return GetLocalAddressImpl(local_addr);
-}
-
-PP_Bool TCPSocketPrivateImpl::GetRemoteAddress(
-    PP_NetAddress_Private* remote_addr) {
-  return GetRemoteAddressImpl(remote_addr);
-}
-
-int32_t TCPSocketPrivateImpl::SSLHandshake(
-    const char* server_name,
-    uint16_t server_port,
-    scoped_refptr<TrackedCallback> callback) {
-  return SSLHandshakeImpl(server_name, server_port, callback);
-}
-
-PP_Resource TCPSocketPrivateImpl::GetServerCertificate() {
-  return GetServerCertificateImpl();
-}
-
-PP_Bool TCPSocketPrivateImpl::AddChainBuildingCertificate(
-    PP_Resource certificate,
-    PP_Bool trusted) {
-  return AddChainBuildingCertificateImpl(certificate, trusted);
-}
-
-int32_t TCPSocketPrivateImpl::Read(char* buffer,
-                                   int32_t bytes_to_read,
-                                   scoped_refptr<TrackedCallback> callback) {
-  return ReadImpl(buffer, bytes_to_read, callback);
-}
-
-int32_t TCPSocketPrivateImpl::Write(const char* buffer,
-                                    int32_t bytes_to_write,
-                                    scoped_refptr<TrackedCallback> callback) {
-  return WriteImpl(buffer, bytes_to_write, callback);
-}
-
-void TCPSocketPrivateImpl::Disconnect() {
-  DisconnectImpl();
-}
-
-int32_t TCPSocketPrivateImpl::SetOption(
-    PP_TCPSocketOption_Private name,
-    const PP_Var& value,
-    scoped_refptr<TrackedCallback> callback) {
-  switch (name) {
-    case PP_TCPSOCKETOPTION_PRIVATE_INVALID:
-      return PP_ERROR_BADARGUMENT;
-    case PP_TCPSOCKETOPTION_PRIVATE_NO_DELAY:
-      return SetOptionImpl(PP_TCPSOCKET_OPTION_NO_DELAY, value, callback);
-    default:
-      NOTREACHED();
-      return PP_ERROR_BADARGUMENT;
-  }
-}
-
-Resource* TCPSocketPrivateImpl::GetOwnerResource() {
-  return this;
-}
-
-int32_t TCPSocketPrivateImpl::OverridePPError(int32_t pp_error) {
-  // PPB_TCPSocket_Private treats all errors from the browser process as
-  // PP_ERROR_FAILED.
-  if (pp_error < 0)
-    return PP_ERROR_FAILED;
-
-  return pp_error;
-}
-
-}  // namespace ppapi
diff --git a/ppapi/shared_impl/private/tcp_socket_private_impl.h b/ppapi/shared_impl/private/tcp_socket_private_impl.h
deleted file mode 100644
index 36303fa..0000000
--- a/ppapi/shared_impl/private/tcp_socket_private_impl.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef PPAPI_SHARED_IMPL_PRIVATE_TCP_SOCKET_PRIVATE_IMPL_H_
-#define PPAPI_SHARED_IMPL_PRIVATE_TCP_SOCKET_PRIVATE_IMPL_H_
-
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "ppapi/shared_impl/resource.h"
-#include "ppapi/shared_impl/tcp_socket_shared.h"
-#include "ppapi/thunk/ppb_tcp_socket_private_api.h"
-
-namespace ppapi {
-
-// This class provides the shared implementation of a
-// PPB_TCPSocket_Private.  The functions that actually send messages
-// to browser are implemented differently for the proxied and
-// non-proxied derived classes.
-class PPAPI_SHARED_EXPORT TCPSocketPrivateImpl
-    : public thunk::PPB_TCPSocket_Private_API,
-      public Resource,
-      public TCPSocketShared {
- public:
-  // C-tor used in Impl case.
-  TCPSocketPrivateImpl(PP_Instance instance, uint32 socket_id);
-  // C-tor used in Proxy case.
-  TCPSocketPrivateImpl(const HostResource& resource, uint32 socket_id);
-
-  virtual ~TCPSocketPrivateImpl();
-
-  // Resource overrides.
-  virtual PPB_TCPSocket_Private_API* AsPPB_TCPSocket_Private_API() OVERRIDE;
-
-  // PPB_TCPSocket_Private_API implementation.
-  virtual int32_t Connect(const char* host,
-                          uint16_t port,
-                          scoped_refptr<TrackedCallback> callback) OVERRIDE;
-  virtual int32_t ConnectWithNetAddress(
-      const PP_NetAddress_Private* addr,
-      scoped_refptr<TrackedCallback> callback) OVERRIDE;
-  virtual PP_Bool GetLocalAddress(PP_NetAddress_Private* local_addr) OVERRIDE;
-  virtual PP_Bool GetRemoteAddress(PP_NetAddress_Private* remote_addr) OVERRIDE;
-  virtual int32_t SSLHandshake(
-      const char* server_name,
-      uint16_t server_port,
-      scoped_refptr<TrackedCallback> callback) OVERRIDE;
-  virtual PP_Resource GetServerCertificate() OVERRIDE;
-  virtual PP_Bool AddChainBuildingCertificate(PP_Resource certificate,
-                                              PP_Bool trusted) OVERRIDE;
-  virtual int32_t Read(char* buffer,
-                      int32_t bytes_to_read,
-                      scoped_refptr<TrackedCallback> callback) OVERRIDE;
-  virtual int32_t Write(const char* buffer,
-                        int32_t bytes_to_write,
-                        scoped_refptr<TrackedCallback> callback) OVERRIDE;
-  virtual void Disconnect() OVERRIDE;
-  virtual int32_t SetOption(PP_TCPSocketOption_Private name,
-                            const PP_Var& value,
-                            scoped_refptr<TrackedCallback> callback) OVERRIDE;
-
-  // TCPSocketShared implementation.
-  virtual Resource* GetOwnerResource() OVERRIDE;
-
-  // TCPSocketShared overrides.
-  virtual int32_t OverridePPError(int32_t pp_error) OVERRIDE;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TCPSocketPrivateImpl);
-};
-
-}  // namespace ppapi
-
-#endif  // PPAPI_SHARED_IMPL_PRIVATE_TCP_SOCKET_PRIVATE_IMPL_H_
diff --git a/ppapi/shared_impl/tcp_socket_shared.cc b/ppapi/shared_impl/tcp_socket_shared.cc
deleted file mode 100644
index ae23b28..0000000
--- a/ppapi/shared_impl/tcp_socket_shared.cc
+++ /dev/null
@@ -1,372 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ppapi/shared_impl/tcp_socket_shared.h"
-
-#include <string.h>
-
-#include <algorithm>
-
-#include "base/basictypes.h"
-#include "base/bind.h"
-#include "base/logging.h"
-#include "ppapi/c/pp_bool.h"
-#include "ppapi/c/pp_completion_callback.h"
-#include "ppapi/c/pp_errors.h"
-#include "ppapi/shared_impl/ppapi_globals.h"
-#include "ppapi/shared_impl/private/ppb_x509_certificate_private_shared.h"
-#include "ppapi/shared_impl/socket_option_data.h"
-#include "ppapi/shared_impl/var_tracker.h"
-#include "ppapi/shared_impl/var.h"
-#include "ppapi/thunk/enter.h"
-#include "ppapi/thunk/ppb_x509_certificate_private_api.h"
-
-namespace ppapi {
-
-const int32_t TCPSocketShared::kMaxReadSize = 1024 * 1024;
-const int32_t TCPSocketShared::kMaxWriteSize = 1024 * 1024;
-const int32_t TCPSocketShared::kMaxSendBufferSize =
-    1024 * TCPSocketShared::kMaxWriteSize;
-const int32_t TCPSocketShared::kMaxReceiveBufferSize =
-    1024 * TCPSocketShared::kMaxReadSize;
-
-TCPSocketShared::TCPSocketShared(ResourceObjectType resource_type,
-                                 uint32 socket_id)
-    : resource_type_(resource_type) {
-  Init(socket_id);
-}
-
-TCPSocketShared::~TCPSocketShared() {
-}
-
-void TCPSocketShared::OnConnectCompleted(
-    int32_t result,
-    const PP_NetAddress_Private& local_addr,
-    const PP_NetAddress_Private& remote_addr) {
-  // It is possible that |connect_callback_| is pending while
-  // |connection_state_| is not BEFORE_CONNECT: DisconnectImpl() has been
-  // called, but a ConnectCompleted notification came earlier than the task to
-  // abort |connect_callback_|. We don't want to update |connection_state_| or
-  // other members in that case.
-  if (connection_state_ != BEFORE_CONNECT ||
-      !TrackedCallback::IsPending(connect_callback_)) {
-    return;
-  }
-
-  result = OverridePPError(result);
-  if (result == PP_OK) {
-    local_addr_ = local_addr;
-    remote_addr_ = remote_addr;
-    connection_state_ = CONNECTED;
-  }
-  connect_callback_->Run(result);
-}
-
-void TCPSocketShared::OnSSLHandshakeCompleted(
-    bool succeeded,
-    const PPB_X509Certificate_Fields& certificate_fields) {
-  // It is possible that |ssl_handshake_callback_| is pending while
-  // |connection_state_| is not CONNECT: DisconnectImpl() has been
-  // called, but a SSLHandshakeCompleted notification came earlier than the task
-  // to abort |ssl_handshake_callback_|. We don't want to update
-  // |connection_state_| or other members in that case.
-  if (connection_state_ != CONNECTED ||
-      !TrackedCallback::IsPending(ssl_handshake_callback_)) {
-    return;
-  }
-
-  if (succeeded) {
-    connection_state_ = SSL_CONNECTED;
-    server_certificate_ = new PPB_X509Certificate_Private_Shared(
-        resource_type_,
-        GetOwnerResource()->pp_instance(),
-        certificate_fields);
-    ssl_handshake_callback_->Run(PP_OK);
-  } else {
-    // The resource might be released in the callback so we need to hold
-    // a reference so we can Disconnect() first.
-    GetOwnerResource()->AddRef();
-    ssl_handshake_callback_->Run(PP_ERROR_FAILED);
-    DisconnectImpl();
-    GetOwnerResource()->Release();
-  }
-}
-
-void TCPSocketShared::OnReadCompleted(int32_t result,
-                                      const std::string& data) {
-  // It is possible that |read_callback_| is pending while |read_buffer_| is
-  // NULL: DisconnectImpl() has been called, but a ReadCompleted notification
-  // came earlier than the task to abort |read_callback_|. We shouldn't access
-  // the buffer in that case. The user may have released it.
-  if (!TrackedCallback::IsPending(read_callback_) || !read_buffer_)
-    return;
-
-  result = OverridePPError(result);
-  bool succeeded = result == PP_OK;
-  if (succeeded) {
-    CHECK_LE(static_cast<int32_t>(data.size()), bytes_to_read_);
-    if (!data.empty())
-      memcpy(read_buffer_, data.c_str(), data.size());
-  }
-  read_buffer_ = NULL;
-  bytes_to_read_ = -1;
-
-  read_callback_->Run(
-      succeeded ? static_cast<int32_t>(data.size()) : result);
-}
-
-void TCPSocketShared::OnWriteCompleted(int32_t result) {
-  if (!TrackedCallback::IsPending(write_callback_))
-    return;
-
-  result = OverridePPError(result);
-  write_callback_->Run(result);
-}
-
-void TCPSocketShared::OnSetOptionCompleted(int32_t result) {
-  if (set_option_callbacks_.empty()) {
-    NOTREACHED();
-    return;
-  }
-
-  result = OverridePPError(result);
-  scoped_refptr<TrackedCallback> callback = set_option_callbacks_.front();
-  set_option_callbacks_.pop();
-
-  if (TrackedCallback::IsPending(callback))
-    callback->Run(result);
-}
-
-int32_t TCPSocketShared::OverridePPError(int32_t pp_error) {
-  return pp_error;
-}
-
-int32_t TCPSocketShared::ConnectImpl(const char* host,
-                                     uint16_t port,
-                                     scoped_refptr<TrackedCallback> callback) {
-  if (!host)
-    return PP_ERROR_BADARGUMENT;
-  if (connection_state_ != BEFORE_CONNECT)
-    return PP_ERROR_FAILED;
-  if (TrackedCallback::IsPending(connect_callback_))
-    return PP_ERROR_INPROGRESS;  // Can only have one pending request.
-
-  connect_callback_ = callback;
-  // Send the request, the browser will call us back via ConnectACK.
-  SendConnect(host, port);
-  return PP_OK_COMPLETIONPENDING;
-}
-
-int32_t TCPSocketShared::ConnectWithNetAddressImpl(
-    const PP_NetAddress_Private* addr,
-    scoped_refptr<TrackedCallback> callback) {
-  if (!addr)
-    return PP_ERROR_BADARGUMENT;
-  if (connection_state_ != BEFORE_CONNECT)
-    return PP_ERROR_FAILED;
-  if (TrackedCallback::IsPending(connect_callback_))
-    return PP_ERROR_INPROGRESS;  // Can only have one pending request.
-
-  connect_callback_ = callback;
-  // Send the request, the browser will call us back via ConnectACK.
-  SendConnectWithNetAddress(*addr);
-  return PP_OK_COMPLETIONPENDING;
-}
-
-PP_Bool TCPSocketShared::GetLocalAddressImpl(
-    PP_NetAddress_Private* local_addr) {
-  if (!IsConnected() || !local_addr)
-    return PP_FALSE;
-
-  *local_addr = local_addr_;
-  return PP_TRUE;
-}
-
-PP_Bool TCPSocketShared::GetRemoteAddressImpl(
-    PP_NetAddress_Private* remote_addr) {
-  if (!IsConnected() || !remote_addr)
-    return PP_FALSE;
-
-  *remote_addr = remote_addr_;
-  return PP_TRUE;
-}
-
-int32_t TCPSocketShared::SSLHandshakeImpl(
-    const char* server_name,
-    uint16_t server_port,
-    scoped_refptr<TrackedCallback> callback) {
-  if (!server_name)
-    return PP_ERROR_BADARGUMENT;
-
-  if (connection_state_ != CONNECTED)
-    return PP_ERROR_FAILED;
-  if (TrackedCallback::IsPending(ssl_handshake_callback_) ||
-      TrackedCallback::IsPending(read_callback_) ||
-      TrackedCallback::IsPending(write_callback_))
-    return PP_ERROR_INPROGRESS;
-
-  ssl_handshake_callback_ = callback;
-
-  // Send the request, the browser will call us back via SSLHandshakeACK.
-  SendSSLHandshake(server_name, server_port, trusted_certificates_,
-      untrusted_certificates_);
-  return PP_OK_COMPLETIONPENDING;
-}
-
-PP_Resource TCPSocketShared::GetServerCertificateImpl() {
-  if (!server_certificate_.get())
-    return 0;
-  return server_certificate_->GetReference();
-}
-
-PP_Bool TCPSocketShared::AddChainBuildingCertificateImpl(
-    PP_Resource certificate,
-    PP_Bool trusted) {
-  // TODO(raymes): The plumbing for this functionality is implemented but the
-  // certificates aren't yet used for the connection, so just return false for
-  // now.
-  return PP_FALSE;
-
-  thunk::EnterResourceNoLock<thunk::PPB_X509Certificate_Private_API>
-  enter_cert(certificate, true);
-  if (enter_cert.failed())
-    return PP_FALSE;
-
-  PP_Var der_var = enter_cert.object()->GetField(
-      PP_X509CERTIFICATE_PRIVATE_RAW);
-  ArrayBufferVar* der_array_buffer = ArrayBufferVar::FromPPVar(der_var);
-  PP_Bool success = PP_FALSE;
-  if (der_array_buffer) {
-    const char* der_bytes = static_cast<const char*>(der_array_buffer->Map());
-    uint32_t der_length = der_array_buffer->ByteLength();
-    std::vector<char> der(der_bytes, der_bytes + der_length);
-    if (PP_ToBool(trusted))
-      trusted_certificates_.push_back(der);
-    else
-      untrusted_certificates_.push_back(der);
-    success = PP_TRUE;
-  }
-  PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(der_var);
-  return success;
-}
-
-int32_t TCPSocketShared::ReadImpl(char* buffer,
-                                  int32_t bytes_to_read,
-                                  scoped_refptr<TrackedCallback> callback) {
-  if (!buffer || bytes_to_read <= 0)
-    return PP_ERROR_BADARGUMENT;
-
-  if (!IsConnected())
-    return PP_ERROR_FAILED;
-  if (TrackedCallback::IsPending(read_callback_) ||
-      TrackedCallback::IsPending(ssl_handshake_callback_))
-    return PP_ERROR_INPROGRESS;
-  read_buffer_ = buffer;
-  bytes_to_read_ = std::min(bytes_to_read, kMaxReadSize);
-  read_callback_ = callback;
-
-  // Send the request, the browser will call us back via ReadACK.
-  SendRead(bytes_to_read_);
-  return PP_OK_COMPLETIONPENDING;
-}
-
-int32_t TCPSocketShared::WriteImpl(const char* buffer,
-                                   int32_t bytes_to_write,
-                                   scoped_refptr<TrackedCallback> callback) {
-  if (!buffer || bytes_to_write <= 0)
-    return PP_ERROR_BADARGUMENT;
-
-  if (!IsConnected())
-    return PP_ERROR_FAILED;
-  if (TrackedCallback::IsPending(write_callback_) ||
-      TrackedCallback::IsPending(ssl_handshake_callback_))
-    return PP_ERROR_INPROGRESS;
-
-  if (bytes_to_write > kMaxWriteSize)
-    bytes_to_write = kMaxWriteSize;
-
-  write_callback_ = callback;
-
-  // Send the request, the browser will call us back via WriteACK.
-  SendWrite(std::string(buffer, bytes_to_write));
-  return PP_OK_COMPLETIONPENDING;
-}
-
-void TCPSocketShared::DisconnectImpl() {
-  if (connection_state_ == DISCONNECTED)
-    return;
-
-  connection_state_ = DISCONNECTED;
-
-  SendDisconnect();
-  socket_id_ = 0;
-
-  PostAbortIfNecessary(&connect_callback_);
-  PostAbortIfNecessary(&ssl_handshake_callback_);
-  PostAbortIfNecessary(&read_callback_);
-  PostAbortIfNecessary(&write_callback_);
-  read_buffer_ = NULL;
-  bytes_to_read_ = -1;
-  server_certificate_ = NULL;
-}
-
-int32_t TCPSocketShared::SetOptionImpl(
-    PP_TCPSocket_Option name,
-    const PP_Var& value,
-    scoped_refptr<TrackedCallback> callback) {
-  if (!IsConnected())
-    return PP_ERROR_FAILED;
-
-  SocketOptionData option_data;
-  switch (name) {
-    case PP_TCPSOCKET_OPTION_NO_DELAY: {
-      if (value.type != PP_VARTYPE_BOOL)
-        return PP_ERROR_BADARGUMENT;
-      option_data.SetBool(PP_ToBool(value.value.as_bool));
-      break;
-    }
-    case PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE:
-    case PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE: {
-      if (value.type != PP_VARTYPE_INT32)
-        return PP_ERROR_BADARGUMENT;
-      option_data.SetInt32(value.value.as_int);
-      break;
-    }
-    default: {
-      NOTREACHED();
-      return PP_ERROR_BADARGUMENT;
-    }
-  }
-
-  set_option_callbacks_.push(callback);
-  SendSetOption(name, option_data);
-  return PP_OK_COMPLETIONPENDING;
-}
-
-void TCPSocketShared::Init(uint32 socket_id) {
-  DCHECK(socket_id != 0);
-  socket_id_ = socket_id;
-  connection_state_ = BEFORE_CONNECT;
-  read_buffer_ = NULL;
-  bytes_to_read_ = -1;
-
-  local_addr_.size = 0;
-  memset(local_addr_.data, 0,
-         arraysize(local_addr_.data) * sizeof(*local_addr_.data));
-  remote_addr_.size = 0;
-  memset(remote_addr_.data, 0,
-         arraysize(remote_addr_.data) * sizeof(*remote_addr_.data));
-}
-
-bool TCPSocketShared::IsConnected() const {
-  return connection_state_ == CONNECTED || connection_state_ == SSL_CONNECTED;
-}
-
-void TCPSocketShared::PostAbortIfNecessary(
-    scoped_refptr<TrackedCallback>* callback) {
-  if (TrackedCallback::IsPending(*callback))
-    (*callback)->PostAbort();
-}
-
-}  // namespace ppapi
diff --git a/ppapi/shared_impl/tcp_socket_shared.h b/ppapi/shared_impl/tcp_socket_shared.h
deleted file mode 100644
index 20e8e51..0000000
--- a/ppapi/shared_impl/tcp_socket_shared.h
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef PPAPI_SHARED_IMPL_TCP_SOCKET_SHARED_H_
-#define PPAPI_SHARED_IMPL_TCP_SOCKET_SHARED_H_
-
-#include <queue>
-#include <string>
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "ppapi/c/ppb_tcp_socket.h"
-#include "ppapi/c/private/ppb_net_address_private.h"
-#include "ppapi/shared_impl/resource.h"
-#include "ppapi/shared_impl/tracked_callback.h"
-
-namespace ppapi {
-
-class PPB_X509Certificate_Fields;
-class PPB_X509Certificate_Private_Shared;
-class SocketOptionData;
-
-// This class provides the shared implementation for both PPB_TCPSocket and
-// PPB_TCPSocket_Private.
-class PPAPI_SHARED_EXPORT TCPSocketShared {
- public:
-  // The maximum number of bytes that each PpapiHostMsg_PPBTCPSocket_Read
-  // message is allowed to request.
-  static const int32_t kMaxReadSize;
-  // The maximum number of bytes that each PpapiHostMsg_PPBTCPSocket_Write
-  // message is allowed to carry.
-  static const int32_t kMaxWriteSize;
-
-  // The maximum number that we allow for setting
-  // PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE. This number is only for input
-  // argument sanity check, it doesn't mean the browser guarantees to support
-  // such a buffer size.
-  static const int32_t kMaxSendBufferSize;
-  // The maximum number that we allow for setting
-  // PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE. This number is only for input
-  // argument sanity check, it doesn't mean the browser guarantees to support
-  // such a buffer size.
-  static const int32_t kMaxReceiveBufferSize;
-
-  // Notifications on operations completion.
-  void OnConnectCompleted(int32_t result,
-                          const PP_NetAddress_Private& local_addr,
-                          const PP_NetAddress_Private& remote_addr);
-  void OnSSLHandshakeCompleted(
-      bool succeeded,
-      const PPB_X509Certificate_Fields& certificate_fields);
-  void OnReadCompleted(int32_t result, const std::string& data);
-  void OnWriteCompleted(int32_t result);
-  void OnSetOptionCompleted(int32_t result);
-
-  // Send functions that need to be implemented differently for the
-  // proxied and non-proxied derived classes.
-  virtual void SendConnect(const std::string& host, uint16_t port) = 0;
-  virtual void SendConnectWithNetAddress(const PP_NetAddress_Private& addr) = 0;
-  virtual void SendSSLHandshake(
-      const std::string& server_name,
-      uint16_t server_port,
-      const std::vector<std::vector<char> >& trusted_certs,
-      const std::vector<std::vector<char> >& untrusted_certs) = 0;
-  virtual void SendRead(int32_t bytes_to_read) = 0;
-  virtual void SendWrite(const std::string& buffer) = 0;
-  virtual void SendDisconnect() = 0;
-  virtual void SendSetOption(PP_TCPSocket_Option name,
-                             const SocketOptionData& value) = 0;
-
-  virtual Resource* GetOwnerResource() = 0;
-
-  // Used to override PP_Error codes received from the browser side.
-  virtual int32_t OverridePPError(int32_t pp_error);
-
- protected:
-  enum ConnectionState {
-    // Before a connection is successfully established (including a connect
-    // request is pending or a previous connect request failed).
-    BEFORE_CONNECT,
-    // A connection has been successfully established (including a request of
-    // initiating SSL is pending).
-    CONNECTED,
-    // An SSL connection has been successfully established.
-    SSL_CONNECTED,
-    // The connection has been ended.
-    DISCONNECTED
-  };
-
-  TCPSocketShared(ResourceObjectType resource_type, uint32 socket_id);
-  virtual ~TCPSocketShared();
-
-  int32_t ConnectImpl(const char* host,
-                      uint16_t port,
-                      scoped_refptr<TrackedCallback> callback);
-  int32_t ConnectWithNetAddressImpl(const PP_NetAddress_Private* addr,
-                                    scoped_refptr<TrackedCallback> callback);
-  PP_Bool GetLocalAddressImpl(PP_NetAddress_Private* local_addr);
-  PP_Bool GetRemoteAddressImpl(PP_NetAddress_Private* remote_addr);
-  int32_t SSLHandshakeImpl(const char* server_name,
-                           uint16_t server_port,
-                           scoped_refptr<TrackedCallback> callback);
-  PP_Resource GetServerCertificateImpl();
-  PP_Bool AddChainBuildingCertificateImpl(PP_Resource certificate,
-                                          PP_Bool trusted);
-  int32_t ReadImpl(char* buffer,
-                   int32_t bytes_to_read,
-                   scoped_refptr<TrackedCallback> callback);
-  int32_t WriteImpl(const char* buffer,
-                    int32_t bytes_to_write,
-                    scoped_refptr<TrackedCallback> callback);
-  void DisconnectImpl();
-  int32_t SetOptionImpl(PP_TCPSocket_Option name,
-                        const PP_Var& value,
-                        scoped_refptr<TrackedCallback> callback);
-
-  void Init(uint32 socket_id);
-  bool IsConnected() const;
-  void PostAbortIfNecessary(scoped_refptr<TrackedCallback>* callback);
-
-  ResourceObjectType resource_type_;
-
-  uint32 socket_id_;
-  ConnectionState connection_state_;
-
-  scoped_refptr<TrackedCallback> connect_callback_;
-  scoped_refptr<TrackedCallback> ssl_handshake_callback_;
-  scoped_refptr<TrackedCallback> read_callback_;
-  scoped_refptr<TrackedCallback> write_callback_;
-  std::queue<scoped_refptr<TrackedCallback> > set_option_callbacks_;
-
-  char* read_buffer_;
-  int32_t bytes_to_read_;
-
-  PP_NetAddress_Private local_addr_;
-  PP_NetAddress_Private remote_addr_;
-
-  scoped_refptr<PPB_X509Certificate_Private_Shared> server_certificate_;
-
-  std::vector<std::vector<char> > trusted_certificates_;
-  std::vector<std::vector<char> > untrusted_certificates_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TCPSocketShared);
-};
-
-}  // namespace ppapi
-
-#endif  // PPAPI_SHARED_IMPL_TCP_SOCKET_SHARED_H_
diff --git a/ppapi/tests/test_ime_input_event.cc b/ppapi/tests/test_ime_input_event.cc
index 70b77e7..c28a044 100644
--- a/ppapi/tests/test_ime_input_event.cc
+++ b/ppapi/tests/test_ime_input_event.cc
@@ -16,12 +16,14 @@
 
 namespace {
 
-// Japanese Kanji letters meaning "a string" ('mo' 'ji' 'retsu' in Kanji)
+// Japanese Kanji letters
 const char* kCompositionChar[] = {
-    "\xE6\x96\x87", "\xE5\xAD\x97", "\xE5\x88\x97"
+    "\xE6\x96\x87",  // An example character of normal unicode.
+    "\xF0\xA0\xAE\x9F", // An example character of surrogate pair.
+    "\xF0\x9F\x98\x81"  // An example character of surrogate pair(emoji).
 };
 
-const char kCompositionText[] = "\xE6\x96\x87\xE5\xAD\x97\xE5\x88\x97";
+const char kCompositionText[] = "\xE6\x96\x87\xF0\xA0\xAE\x9F\xF0\x9F\x98\x81";
 
 #define FINISHED_WAITING_MESSAGE "TEST_IME_INPUT_EVENT_FINISHED_WAITING"
 
@@ -318,10 +320,10 @@
   std::vector<uint32_t> segments;
   segments.push_back(0U);
   segments.push_back(3U);
-  segments.push_back(6U);
-  segments.push_back(9U);
+  segments.push_back(7U);
+  segments.push_back(11U);
   pp::InputEvent update_event = CreateImeCompositionUpdateEvent(
-      kCompositionText, segments, 1, std::make_pair(3U, 6U));
+      kCompositionText, segments, 1, std::make_pair(3U, 7U));
 
   expected_events_.clear();
   expected_events_.push_back(CreateImeCompositionStartEvent());
@@ -347,10 +349,10 @@
   std::vector<uint32_t> segments;
   segments.push_back(0U);
   segments.push_back(3U);
-  segments.push_back(6U);
-  segments.push_back(9U);
+  segments.push_back(7U);
+  segments.push_back(11U);
   pp::InputEvent update_event = CreateImeCompositionUpdateEvent(
-      kCompositionText, segments, 1, std::make_pair(3U, 6U));
+      kCompositionText, segments, 1, std::make_pair(3U, 7U));
 
   expected_events_.clear();
   expected_events_.push_back(CreateImeCompositionStartEvent());
@@ -376,10 +378,10 @@
   std::vector<uint32_t> segments;
   segments.push_back(0U);
   segments.push_back(3U);
-  segments.push_back(6U);
-  segments.push_back(9U);
+  segments.push_back(7U);
+  segments.push_back(11U);
   pp::InputEvent update_event = CreateImeCompositionUpdateEvent(
-      kCompositionText, segments, 1, std::make_pair(3U, 6U));
+      kCompositionText, segments, 1, std::make_pair(3U, 7U));
 
   expected_events_.clear();
   expected_events_.push_back(CreateCharEvent(kCompositionChar[0]));
@@ -406,10 +408,10 @@
   std::vector<uint32_t> segments;
   segments.push_back(0U);
   segments.push_back(3U);
-  segments.push_back(6U);
-  segments.push_back(9U);
+  segments.push_back(7U);
+  segments.push_back(11U);
   pp::InputEvent update_event = CreateImeCompositionUpdateEvent(
-      kCompositionText, segments, 1, std::make_pair(3U, 6U));
+      kCompositionText, segments, 1, std::make_pair(3U, 7U));
 
   expected_events_.clear();
 
diff --git a/ppapi/tests/test_tcp_server_socket_private.cc b/ppapi/tests/test_tcp_server_socket_private.cc
index 880515d..20b9d5e 100644
--- a/ppapi/tests/test_tcp_server_socket_private.cc
+++ b/ppapi/tests/test_tcp_server_socket_private.cc
@@ -170,12 +170,21 @@
   TCPSocketPrivate client_socket(instance_);
   ForceConnect(&client_socket, &address);
 
+  PP_NetAddress_Private client_local_addr, client_remote_addr;
+  ASSERT_TRUE(client_socket.GetLocalAddress(&client_local_addr));
+  ASSERT_TRUE(client_socket.GetRemoteAddress(&client_remote_addr));
+
   accept_callback.WaitForResult(accept_rv);
   CHECK_CALLBACK_BEHAVIOR(accept_callback);
   ASSERT_EQ(PP_OK, accept_callback.result());
 
   ASSERT_TRUE(resource != 0);
   TCPSocketPrivate accepted_socket(pp::PassRef(), resource);
+  PP_NetAddress_Private accepted_local_addr, accepted_remote_addr;
+  ASSERT_TRUE(accepted_socket.GetLocalAddress(&accepted_local_addr));
+  ASSERT_TRUE(accepted_socket.GetRemoteAddress(&accepted_remote_addr));
+  ASSERT_TRUE(NetAddressPrivate::AreEqual(client_local_addr,
+                                          accepted_remote_addr));
 
   const char kSentByte = 'a';
   ASSERT_SUBTEST_SUCCESS(SyncWrite(&client_socket,
diff --git a/ppapi/tests/test_tcp_socket_private.cc b/ppapi/tests/test_tcp_socket_private.cc
index ba1c8f1..d3efbe3 100644
--- a/ppapi/tests/test_tcp_socket_private.cc
+++ b/ppapi/tests/test_tcp_socket_private.cc
@@ -6,9 +6,11 @@
 
 #include <stdlib.h>
 
+#include <new>
+
 #include "ppapi/cpp/private/tcp_socket_private.h"
-#include "ppapi/tests/testing_instance.h"
 #include "ppapi/tests/test_utils.h"
+#include "ppapi/tests/testing_instance.h"
 
 namespace {
 
@@ -52,6 +54,7 @@
   RUN_CALLBACK_TEST(TestTCPSocketPrivate, ReadWriteSSL, filter);
   RUN_CALLBACK_TEST(TestTCPSocketPrivate, ConnectAddress, filter);
   RUN_CALLBACK_TEST(TestTCPSocketPrivate, SetOption, filter);
+  RUN_CALLBACK_TEST(TestTCPSocketPrivate, LargeRead, filter);
 }
 
 std::string TestTCPSocketPrivate::TestBasic() {
@@ -181,6 +184,35 @@
   PASS();
 }
 
+std::string TestTCPSocketPrivate::TestLargeRead() {
+  pp::TCPSocketPrivate socket(instance_);
+  {
+    TestCompletionCallback cb(instance_->pp_instance(), callback_type());
+
+    cb.WaitForResult(socket.Connect(host_.c_str(), port_, cb.GetCallback()));
+    CHECK_CALLBACK_BEHAVIOR(cb);
+    ASSERT_EQ(PP_OK, cb.result());
+  }
+
+  ASSERT_EQ(PP_OK, WriteStringToSocket(&socket, "GET / HTTP/1.0\r\n\r\n"));
+
+  const size_t kReadSize = 1024 * 1024 + 32;
+  // Create large buffer in heap to prevent run-time errors related to
+  // limits on stack size.
+  char* buffer = new (std::nothrow) char[kReadSize];
+  ASSERT_TRUE(buffer != NULL);
+
+  TestCompletionCallback cb(instance_->pp_instance(), callback_type());
+  cb.WaitForResult(socket.Read(buffer, kReadSize * sizeof(*buffer),
+                               cb.GetCallback()));
+  CHECK_CALLBACK_BEHAVIOR(cb);
+  ASSERT_LE(0, cb.result());
+
+  delete [] buffer;
+
+  PASS();
+}
+
 int32_t TestTCPSocketPrivate::ReadFirstLineFromSocket(
     pp::TCPSocketPrivate* socket,
     std::string* s) {
diff --git a/ppapi/tests/test_tcp_socket_private.h b/ppapi/tests/test_tcp_socket_private.h
index a7b747f..4396cfc 100644
--- a/ppapi/tests/test_tcp_socket_private.h
+++ b/ppapi/tests/test_tcp_socket_private.h
@@ -28,6 +28,7 @@
   std::string TestReadWriteSSL();
   std::string TestConnectAddress();
   std::string TestSetOption();
+  std::string TestLargeRead();
 
   int32_t ReadFirstLineFromSocket(pp::TCPSocketPrivate* socket, std::string* s);
   int32_t WriteStringToSocket(pp::TCPSocketPrivate* socket,
diff --git a/ppapi/thunk/interfaces_ppb_private_no_permissions.h b/ppapi/thunk/interfaces_ppb_private_no_permissions.h
index 44cbc36..eae9023 100644
--- a/ppapi/thunk/interfaces_ppb_private_no_permissions.h
+++ b/ppapi/thunk/interfaces_ppb_private_no_permissions.h
@@ -9,7 +9,6 @@
 
 // These interfaces don't require private permissions. However, they only work
 // for whitelisted origins.
-PROXIED_API(PPB_TCPSocket_Private)
 UNPROXIED_API(PPB_NetworkList_Private)
 PROXIED_API(PPB_NetworkMonitor_Private)
 
@@ -19,11 +18,11 @@
               PPB_TCPServerSocket_Private_0_1)
 PROXIED_IFACE(NoAPIName, PPB_TCPSERVERSOCKET_PRIVATE_INTERFACE_0_2,
               PPB_TCPServerSocket_Private_0_2)
-PROXIED_IFACE(PPB_TCPSocket_Private, PPB_TCPSOCKET_PRIVATE_INTERFACE_0_3,
+PROXIED_IFACE(NoAPIName, PPB_TCPSOCKET_PRIVATE_INTERFACE_0_3,
               PPB_TCPSocket_Private_0_3)
-PROXIED_IFACE(PPB_TCPSocket_Private, PPB_TCPSOCKET_PRIVATE_INTERFACE_0_4,
+PROXIED_IFACE(NoAPIName, PPB_TCPSOCKET_PRIVATE_INTERFACE_0_4,
               PPB_TCPSocket_Private_0_4)
-PROXIED_IFACE(PPB_TCPSocket_Private, PPB_TCPSOCKET_PRIVATE_INTERFACE_0_5,
+PROXIED_IFACE(NoAPIName, PPB_TCPSOCKET_PRIVATE_INTERFACE_0_5,
               PPB_TCPSocket_Private_0_5)
 PROXIED_IFACE(NoAPIName, PPB_UDPSOCKET_PRIVATE_INTERFACE_0_2,
               PPB_UDPSocket_Private_0_2)
diff --git a/ppapi/thunk/interfaces_ppb_public_stable.h b/ppapi/thunk/interfaces_ppb_public_stable.h
index b0918a3..19626aa 100644
--- a/ppapi/thunk/interfaces_ppb_public_stable.h
+++ b/ppapi/thunk/interfaces_ppb_public_stable.h
@@ -23,7 +23,6 @@
 PROXIED_API(PPB_Graphics3D)
 PROXIED_API(PPB_ImageData)
 PROXIED_API(PPB_Instance)
-PROXIED_API(PPB_TCPSocket)
 
 // AudioConfig isn't proxied in the normal way, we have only local classes and
 // serialize it to a struct when we need it on the host side.
@@ -78,7 +77,7 @@
 PROXIED_IFACE(PPB_Instance, PPB_MOUSELOCK_INTERFACE_1_0, PPB_MouseLock_1_0)
 PROXIED_IFACE(NoAPIName, PPB_NETADDRESS_INTERFACE_1_0, PPB_NetAddress_1_0)
 PROXIED_IFACE(NoAPIName, PPB_NETWORKPROXY_INTERFACE_1_0, PPB_NetworkProxy_1_0)
-PROXIED_IFACE(PPB_TCPSocket, PPB_TCPSOCKET_INTERFACE_1_0, PPB_TCPSocket_1_0)
+PROXIED_IFACE(NoAPIName, PPB_TCPSOCKET_INTERFACE_1_0, PPB_TCPSocket_1_0)
 PROXIED_IFACE(NoAPIName, PPB_TEXTINPUTCONTROLLER_INTERFACE_1_0,
               PPB_TextInputController_1_0)
 PROXIED_IFACE(NoAPIName, PPB_UDPSOCKET_INTERFACE_1_0, PPB_UDPSocket_1_0)
diff --git a/ppapi/thunk/resource_creation_api.h b/ppapi/thunk/resource_creation_api.h
index 082f1cc..4930755 100644
--- a/ppapi/thunk/resource_creation_api.h
+++ b/ppapi/thunk/resource_creation_api.h
@@ -60,8 +60,6 @@
       const PPB_FileRef_CreateInfo& serialized) = 0;
   virtual PP_Resource CreateFileSystem(PP_Instance instance,
                                        PP_FileSystemType type) = 0;
-  virtual PP_Resource CreateIsolatedFileSystem(PP_Instance instance,
-                                               const char* fsid) = 0;
   virtual PP_Resource CreateIMEInputEvent(PP_Instance instance,
                                           PP_InputEvent_Type type,
                                           PP_TimeTicks time_stamp,
diff --git a/printing/emf_win.cc b/printing/emf_win.cc
index 98c8f8f..3003a2f 100644
--- a/printing/emf_win.cc
+++ b/printing/emf_win.cc
@@ -517,7 +517,7 @@
   return res;
 }
 
-SkDevice* Emf::StartPageForVectorCanvas(
+SkBaseDevice* Emf::StartPageForVectorCanvas(
     const gfx::Size& page_size, const gfx::Rect& content_area,
     const float& scale_factor) {
   if (!StartPage(page_size, content_area, scale_factor))
diff --git a/printing/emf_win.h b/printing/emf_win.h
index 3516f4e..fa27409 100644
--- a/printing/emf_win.h
+++ b/printing/emf_win.h
@@ -54,7 +54,7 @@
   virtual bool InitFromData(const void* src_buffer,
                             uint32 src_buffer_size) OVERRIDE;
 
-  virtual SkDevice* StartPageForVectorCanvas(
+  virtual SkBaseDevice* StartPageForVectorCanvas(
       const gfx::Size& page_size, const gfx::Rect& content_area,
       const float& scale_factor) OVERRIDE;
   // Inserts a custom GDICOMMENT records indicating StartPage/EndPage calls
diff --git a/printing/metafile.h b/printing/metafile.h
index e331069..5f771e5 100644
--- a/printing/metafile.h
+++ b/printing/metafile.h
@@ -27,7 +27,7 @@
 class Size;
 }
 
-class SkDevice;
+class SkBaseDevice;
 
 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
 namespace base {
@@ -87,7 +87,7 @@
   // This method calls StartPage and then returns an appropriate
   // VectorPlatformDevice implementation bound to the context created by
   // StartPage or NULL on error.
-  virtual SkDevice* StartPageForVectorCanvas(
+  virtual SkBaseDevice* StartPageForVectorCanvas(
       const gfx::Size& page_size,
       const gfx::Rect& content_area,
       const float& scale_factor) = 0;
diff --git a/printing/pdf_metafile_cg_mac.cc b/printing/pdf_metafile_cg_mac.cc
index cd7c5c7..8668738 100644
--- a/printing/pdf_metafile_cg_mac.cc
+++ b/printing/pdf_metafile_cg_mac.cc
@@ -110,7 +110,7 @@
   return true;
 }
 
-SkDevice* PdfMetafileCg::StartPageForVectorCanvas(
+SkBaseDevice* PdfMetafileCg::StartPageForVectorCanvas(
     const gfx::Size& page_size, const gfx::Rect& content_area,
     const float& scale_factor) {
   NOTIMPLEMENTED();
diff --git a/printing/pdf_metafile_cg_mac.h b/printing/pdf_metafile_cg_mac.h
index 77f7031..331129a 100644
--- a/printing/pdf_metafile_cg_mac.h
+++ b/printing/pdf_metafile_cg_mac.h
@@ -37,7 +37,7 @@
                             uint32 src_buffer_size) OVERRIDE;
 
   // Not implemented on mac.
-  virtual SkDevice* StartPageForVectorCanvas(
+  virtual SkBaseDevice* StartPageForVectorCanvas(
       const gfx::Size& page_size, const gfx::Rect& content_area,
       const float& scale_factor) OVERRIDE;
   virtual bool StartPage(const gfx::Size& page_size,
diff --git a/printing/pdf_metafile_skia.cc b/printing/pdf_metafile_skia.cc
index 31c8c74..2242cfc 100644
--- a/printing/pdf_metafile_skia.cc
+++ b/printing/pdf_metafile_skia.cc
@@ -48,7 +48,7 @@
   return data_->pdf_stream_.write(src_buffer, src_buffer_size);
 }
 
-SkDevice* PdfMetafileSkia::StartPageForVectorCanvas(
+SkBaseDevice* PdfMetafileSkia::StartPageForVectorCanvas(
     const gfx::Size& page_size, const gfx::Rect& content_area,
     const float& scale_factor) {
   DCHECK(!page_outstanding_);
diff --git a/printing/pdf_metafile_skia.h b/printing/pdf_metafile_skia.h
index c6f1971..b44133f 100644
--- a/printing/pdf_metafile_skia.h
+++ b/printing/pdf_metafile_skia.h
@@ -30,7 +30,7 @@
   virtual bool InitFromData(const void* src_buffer,
                             uint32 src_buffer_size) OVERRIDE;
 
-  virtual SkDevice* StartPageForVectorCanvas(
+  virtual SkBaseDevice* StartPageForVectorCanvas(
       const gfx::Size& page_size,
       const gfx::Rect& content_area,
       const float& scale_factor) OVERRIDE;
diff --git a/remoting/android/java/AndroidManifest.xml b/remoting/android/java/AndroidManifest.xml
index 7a21b2d..26b6d77 100644
--- a/remoting/android/java/AndroidManifest.xml
+++ b/remoting/android/java/AndroidManifest.xml
@@ -1,8 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="org.chromium.chromoting"
-        android:versionCode="11"
-        android:versionName="0.11">
+        package="org.chromium.chromoting">
     <uses-sdk android:minSdkVersion="14"
             android:targetSdkVersion="14"/>
     <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
diff --git a/remoting/client/jni/chromoting_jni_instance.cc b/remoting/client/jni/chromoting_jni_instance.cc
index 7e45f7c..5eef55a 100644
--- a/remoting/client/jni/chromoting_jni_instance.cc
+++ b/remoting/client/jni/chromoting_jni_instance.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/logging.h"
+#include "jingle/glue/xmpp_client_socket_factory.h"
 #include "net/socket/client_socket_factory.h"
 #include "remoting/client/audio_player.h"
 #include "remoting/client/jni/android_keymap.h"
@@ -248,9 +249,11 @@
 
   view_->set_frame_producer(client_->GetFrameProducer());
 
-  signaling_.reset(new XmppSignalStrategy(
-      net::ClientSocketFactory::GetDefaultFactory(),
-      jni_runtime_->url_requester(), xmpp_config_));
+  scoped_ptr<jingle_glue::ResolvingClientSocketFactory> socket_factory(
+      new jingle_glue::XmppClientSocketFactory(
+          net::ClientSocketFactory::GetDefaultFactory(), net::SSLConfig(),
+          jni_runtime_->url_requester(), false));
+  signaling_.reset(new XmppSignalStrategy(socket_factory.Pass(), xmpp_config_));
 
   network_settings_.reset(new NetworkSettings(
       NetworkSettings::NAT_TRAVERSAL_ENABLED));
diff --git a/remoting/client/plugin/chromoting_instance.cc b/remoting/client/plugin/chromoting_instance.cc
index 55e6954..37bceee 100644
--- a/remoting/client/plugin/chromoting_instance.cc
+++ b/remoting/client/plugin/chromoting_instance.cc
@@ -204,6 +204,7 @@
   UnregisterLoggingInstance();
 
   // PepperView must be destroyed before the client.
+  view_weak_factory_.reset();
   view_.reset();
 
   client_.reset();
@@ -605,7 +606,9 @@
                                      consumer_proxy, audio_player.Pass()));
 
   view_.reset(new PepperView(this, &context_, client_->GetFrameProducer()));
-  consumer_proxy->Attach(view_->AsWeakPtr());
+  view_weak_factory_.reset(
+      new base::WeakPtrFactory<FrameConsumer>(view_.get()));
+  consumer_proxy->Attach(view_weak_factory_->GetWeakPtr());
   if (!plugin_view_.is_null()) {
     view_->SetView(plugin_view_);
   }
@@ -641,6 +644,7 @@
   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
 
   // PepperView must be destroyed before the client.
+  view_weak_factory_.reset();
   view_.reset();
 
   LOG(INFO) << "Disconnecting from host.";
diff --git a/remoting/client/plugin/chromoting_instance.h b/remoting/client/plugin/chromoting_instance.h
index a97ca55..3dd36c5 100644
--- a/remoting/client/plugin/chromoting_instance.h
+++ b/remoting/client/plugin/chromoting_instance.h
@@ -16,18 +16,8 @@
 #include "ppapi/c/pp_instance.h"
 #include "ppapi/c/pp_rect.h"
 #include "ppapi/c/pp_resource.h"
-#include "ppapi/cpp/var.h"
-#include "third_party/skia/include/core/SkPoint.h"
-#include "third_party/skia/include/core/SkRegion.h"
-#include "third_party/skia/include/core/SkSize.h"
-
-// Windows defines 'PostMessage', so we have to undef it before we
-// include instance_private.h
-#if defined(PostMessage)
-#undef PostMessage
-#endif
-
 #include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/var.h"
 #include "remoting/client/client_context.h"
 #include "remoting/client/client_user_interface.h"
 #include "remoting/client/key_event_mapper.h"
@@ -43,6 +33,9 @@
 #include "remoting/protocol/mouse_input_filter.h"
 #include "remoting/protocol/negotiating_client_authenticator.h"
 #include "remoting/protocol/third_party_client_authenticator.h"
+#include "third_party/skia/include/core/SkPoint.h"
+#include "third_party/skia/include/core/SkRegion.h"
+#include "third_party/skia/include/core/SkSize.h"
 
 namespace base {
 class DictionaryValue;
@@ -59,6 +52,7 @@
 class ChromotingStats;
 class ClientContext;
 class DelegatingSignalStrategy;
+class FrameConsumer;
 class FrameConsumerProxy;
 class PepperAudioPlayer;
 class PepperTokenFetcher;
@@ -239,6 +233,7 @@
   ClientContext context_;
   scoped_refptr<RectangleUpdateDecoder> rectangle_decoder_;
   scoped_ptr<PepperView> view_;
+  scoped_ptr<base::WeakPtrFactory<FrameConsumer> > view_weak_factory_;
   pp::View plugin_view_;
 
   // Contains the most-recently-reported desktop shape, if any.
diff --git a/remoting/client/plugin/pepper_packet_socket_factory.cc b/remoting/client/plugin/pepper_packet_socket_factory.cc
index ea6ad98..2f37ee2 100644
--- a/remoting/client/plugin/pepper_packet_socket_factory.cc
+++ b/remoting/client/plugin/pepper_packet_socket_factory.cc
@@ -6,10 +6,10 @@
 
 #include "base/bind.h"
 #include "base/logging.h"
-#include "base/memory/weak_ptr.h"
 #include "net/base/io_buffer.h"
-#include "ppapi/cpp/private/net_address_private.h"
-#include "ppapi/cpp/private/udp_socket_private.h"
+#include "ppapi/cpp/net_address.h"
+#include "ppapi/cpp/udp_socket.h"
+#include "ppapi/utility/completion_callback_factory.h"
 #include "remoting/client/plugin/pepper_util.h"
 #include "third_party/libjingle/source/talk/base/asyncpacketsocket.h"
 
@@ -55,10 +55,10 @@
   struct PendingPacket {
     PendingPacket(const void* buffer,
                   int buffer_size,
-                  const PP_NetAddress_Private& address);
+                  const pp::NetAddress& address);
 
     scoped_refptr<net::IOBufferWithSize> data;
-    PP_NetAddress_Private address;
+    pp::NetAddress address;
   };
 
   void OnBindCompleted(int error);
@@ -67,10 +67,12 @@
   void OnSendCompleted(int result);
 
   void DoRead();
-  void OnReadCompleted(int result);
-  void HandleReadResult(int result);
+  void OnReadCompleted(int result, pp::NetAddress address);
+  void HandleReadResult(int result, pp::NetAddress address);
 
-  pp::UDPSocketPrivate socket_;
+  pp::InstanceHandle instance_;
+
+  pp::UDPSocket socket_;
 
   State state_;
   int error_;
@@ -88,7 +90,7 @@
   std::list<PendingPacket> send_queue_;
   int send_queue_size_;
 
-  base::WeakPtrFactory<UdpPacketSocket> weak_factory_;
+  pp::CompletionCallbackFactory<UdpPacketSocket> callback_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(UdpPacketSocket);
 };
@@ -96,21 +98,22 @@
 UdpPacketSocket::PendingPacket::PendingPacket(
     const void* buffer,
     int buffer_size,
-    const PP_NetAddress_Private& address)
+    const pp::NetAddress& address)
     : data(new net::IOBufferWithSize(buffer_size)),
       address(address) {
   memcpy(data->data(), buffer, buffer_size);
 }
 
 UdpPacketSocket::UdpPacketSocket(const pp::InstanceHandle& instance)
-    : socket_(instance),
+    : instance_(instance),
+      socket_(instance),
       state_(STATE_CLOSED),
       error_(0),
       min_port_(0),
       max_port_(0),
       send_pending_(false),
       send_queue_size_(0),
-      weak_factory_(this) {
+      callback_factory_(this) {
 }
 
 UdpPacketSocket::~UdpPacketSocket() {
@@ -128,15 +131,15 @@
   max_port_ = max_port;
   min_port_ = min_port;
 
-  PP_NetAddress_Private pp_local_address;
-  if (!SocketAddressToPpAddressWithPort(local_address_, &pp_local_address,
-                                        min_port_)) {
+  pp::NetAddress pp_local_address;
+  if (!SocketAddressToPpNetAddressWithPort(
+          instance_, local_address_, &pp_local_address, min_port_)) {
     return false;
   }
 
-  int result = socket_.Bind(&pp_local_address, PpCompletionCallback(
-      base::Bind(&UdpPacketSocket::OnBindCompleted,
-                 weak_factory_.GetWeakPtr())));
+  pp::CompletionCallback callback =
+      callback_factory_.NewCallback(&UdpPacketSocket::OnBindCompleted);
+  int result = socket_.Bind(pp_local_address, callback);
   DCHECK_EQ(result, PP_OK_COMPLETIONPENDING);
   state_ = STATE_BINDING;
 
@@ -152,14 +155,8 @@
   }
 
   if (result == PP_OK) {
-    PP_NetAddress_Private address;
-    if (socket_.GetBoundAddress(&address)) {
-      PpAddressToSocketAddress(address, &local_address_);
-    } else {
-      LOG(ERROR) << "Failed to get bind address for bound socket?";
-      error_ = EINVAL;
-      return;
-    }
+    pp::NetAddress address = socket_.GetBoundAddress();
+    PpNetAddressToSocketAddress(address, &local_address_);
     state_ = STATE_BOUND;
     SignalAddressReady(this, local_address_);
     DoRead();
@@ -169,12 +166,12 @@
   if (min_port_ < max_port_) {
     // Try to bind to the next available port.
     ++min_port_;
-    PP_NetAddress_Private pp_local_address;
-    if (SocketAddressToPpAddressWithPort(local_address_, &pp_local_address,
-                                         min_port_)) {
-      int result = socket_.Bind(&pp_local_address, PpCompletionCallback(
-          base::Bind(&UdpPacketSocket::OnBindCompleted,
-                     weak_factory_.GetWeakPtr())));
+    pp::NetAddress pp_local_address;
+    if (SocketAddressToPpNetAddressWithPort(
+            instance_, local_address_, &pp_local_address, min_port_)) {
+      pp::CompletionCallback callback =
+          callback_factory_.NewCallback(&UdpPacketSocket::OnBindCompleted);
+      int result = socket_.Bind(pp_local_address, callback);
       DCHECK_EQ(result, PP_OK_COMPLETIONPENDING);
     }
   } else {
@@ -212,8 +209,8 @@
     return error_;
   }
 
-  PP_NetAddress_Private pp_address;
-  if (!SocketAddressToPpAddress(address, &pp_address)) {
+  pp::NetAddress pp_address;
+  if (!SocketAddressToPpNetAddress(instance_, address, &pp_address)) {
     return EINVAL;
   }
 
@@ -259,11 +256,12 @@
   if (send_pending_ || send_queue_.empty())
     return;
 
+  pp::CompletionCallback callback =
+      callback_factory_.NewCallback(&UdpPacketSocket::OnSendCompleted);
   int result = socket_.SendTo(
       send_queue_.front().data->data(), send_queue_.front().data->size(),
-      &send_queue_.front().address,
-      PpCompletionCallback(base::Bind(&UdpPacketSocket::OnSendCompleted,
-                                      weak_factory_.GetWeakPtr())));
+      send_queue_.front().address,
+      callback);
   DCHECK_EQ(result, PP_OK_COMPLETIONPENDING);
   send_pending_ = true;
 }
@@ -307,33 +305,26 @@
 
 void UdpPacketSocket::DoRead() {
   receive_buffer_.resize(kReceiveBufferSize);
-  int result = socket_.RecvFrom(
-      &receive_buffer_[0], receive_buffer_.size(),
-      PpCompletionCallback(base::Bind(&UdpPacketSocket::OnReadCompleted,
-                                      weak_factory_.GetWeakPtr())));
+  pp::CompletionCallbackWithOutput<pp::NetAddress> callback =
+      callback_factory_.NewCallbackWithOutput(
+          &UdpPacketSocket::OnReadCompleted);
+  int result =
+      socket_.RecvFrom(&receive_buffer_[0], receive_buffer_.size(), callback);
   DCHECK_EQ(result, PP_OK_COMPLETIONPENDING);
 }
 
-void UdpPacketSocket::OnReadCompleted(int result) {
-  HandleReadResult(result);
+void UdpPacketSocket::OnReadCompleted(int result, pp::NetAddress address) {
+  HandleReadResult(result, address);
   if (result > 0) {
     DoRead();
   }
 }
 
-void UdpPacketSocket::HandleReadResult(int result) {
+  void UdpPacketSocket::HandleReadResult(int result, pp::NetAddress address) {
   if (result > 0) {
-    PP_NetAddress_Private pp_address;
-    if (!socket_.GetRecvFromAddress(&pp_address)) {
-      LOG(ERROR) << "GetRecvFromAddress() failed after successfull RecvFrom().";
-      return;
-    }
-    talk_base::SocketAddress address;
-    if (!PpAddressToSocketAddress(pp_address, &address)) {
-      LOG(ERROR) << "Failed to covert address received from RecvFrom().";
-      return;
-    }
-    SignalReadPacket(this, &receive_buffer_[0], result, address);
+    talk_base::SocketAddress socket_address;
+    PpNetAddressToSocketAddress(address, &socket_address);
+    SignalReadPacket(this, &receive_buffer_[0], result, socket_address);
   } else if (result != PP_ERROR_ABORTED) {
     LOG(ERROR) << "Received error when reading from UDP socket: " << result;
   }
diff --git a/remoting/client/plugin/pepper_port_allocator.cc b/remoting/client/plugin/pepper_port_allocator.cc
index 3b5c241..c04d7aa 100644
--- a/remoting/client/plugin/pepper_port_allocator.cc
+++ b/remoting/client/plugin/pepper_port_allocator.cc
@@ -8,11 +8,12 @@
 #include "base/strings/string_number_conversions.h"
 #include "net/base/net_util.h"
 #include "ppapi/c/pp_errors.h"
-#include "ppapi/cpp/completion_callback.h"
-#include "ppapi/cpp/private/host_resolver_private.h"
+#include "ppapi/cpp/host_resolver.h"
+#include "ppapi/cpp/net_address.h"
 #include "ppapi/cpp/url_loader.h"
 #include "ppapi/cpp/url_request_info.h"
 #include "ppapi/cpp/url_response_info.h"
+#include "ppapi/utility/completion_callback_factory.h"
 #include "remoting/client/plugin/pepper_network_manager.h"
 #include "remoting/client/plugin/pepper_packet_socket_factory.h"
 #include "remoting/client/plugin/pepper_util.h"
@@ -58,7 +59,7 @@
 
   pp::InstanceHandle instance_;
 
-  pp::HostResolverPrivate stun_address_resolver_;
+  pp::HostResolver stun_address_resolver_;
   talk_base::SocketAddress stun_address_;
   int stun_port_;
 
@@ -66,8 +67,7 @@
   std::vector<char> relay_response_body_;
   bool relay_response_received_;
 
-  // Used to safely cancel completion callbacks from PPAPI calls.
-  base::WeakPtrFactory<PepperPortAllocatorSession> weak_factory_;
+  pp::CompletionCallbackFactory<PepperPortAllocatorSession> callback_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(PepperPortAllocatorSession);
 };
@@ -95,7 +95,7 @@
       stun_address_resolver_(instance_),
       stun_port_(0),
       relay_response_received_(false),
-      weak_factory_(this) {
+      callback_factory_(this) {
   if (stun_hosts.size() > 0) {
     stun_address_ = stun_hosts[0];
   }
@@ -153,15 +153,15 @@
   std::string hostname = stun_address_.hostname();
   uint16 port = stun_address_.port();
 
-  PP_HostResolver_Private_Hint hint;
+  PP_HostResolver_Hint hint;
   hint.flags = 0;
-  hint.family = PP_NETADDRESSFAMILY_PRIVATE_IPV4;
-  int result = stun_address_resolver_.Resolve(
-      hostname, port, hint,
-      PpCompletionCallback(base::Bind(
-          &PepperPortAllocatorSession::OnStunAddressResolved,
-          weak_factory_.GetWeakPtr())));
-
+  hint.family = PP_NETADDRESS_FAMILY_IPV4;
+  pp::CompletionCallback callback = callback_factory_.NewCallback(
+      &PepperPortAllocatorSession::OnStunAddressResolved);
+  int result = stun_address_resolver_.Resolve(hostname.c_str(),
+                                              port,
+                                              hint,
+                                              callback);
   DCHECK_EQ(result, PP_OK_COMPLETIONPENDING);
 }
 
@@ -172,20 +172,20 @@
     return;
   }
 
-  if (!stun_address_resolver_.GetSize()) {
+  if (!stun_address_resolver_.GetNetAddressCount()) {
     LOG(WARNING) << "Received 0 addresses for stun server "
                << stun_address_.hostname();
     return;
   }
 
-  PP_NetAddress_Private address;
-  if (!stun_address_resolver_.GetNetAddress(0, &address) ||
-      !PpAddressToSocketAddress(address, &stun_address_)) {
+  pp::NetAddress address = stun_address_resolver_.GetNetAddress(0);
+  if (address.is_null()) {
     LOG(ERROR) << "Failed to get address for STUN server "
                << stun_address_.hostname();
     return;
   }
 
+  PpNetAddressToSocketAddress(address, &stun_address_);
   DCHECK(!stun_address_.IsUnresolved());
 
   if (relay_response_received_) {
@@ -221,11 +221,9 @@
   headers << "X-Stream-Type: " << "chromoting" << "\n\r";
   request_info.SetHeaders(headers.str());
 
-  int result = relay_url_loader_->Open(
-      request_info, PpCompletionCallback(base::Bind(
-          &PepperPortAllocatorSession::OnUrlOpened,
-          weak_factory_.GetWeakPtr())));
-
+  pp::CompletionCallback callback =
+      callback_factory_.NewCallback(&PepperPortAllocatorSession::OnUrlOpened);
+  int result = relay_url_loader_->Open(request_info, callback);
   DCHECK_EQ(result, PP_OK_COMPLETIONPENDING);
 }
 
@@ -257,11 +255,11 @@
 void PepperPortAllocatorSession::ReadResponseBody() {
   int pos = relay_response_body_.size();
   relay_response_body_.resize(pos + kReadSize);
-  int result = relay_url_loader_->ReadResponseBody(
-      &relay_response_body_[pos], kReadSize,
-      PpCompletionCallback(base::Bind(
-          &PepperPortAllocatorSession::OnResponseBodyRead,
-          weak_factory_.GetWeakPtr())));
+  pp::CompletionCallback callback = callback_factory_.NewCallback(
+      &PepperPortAllocatorSession::OnResponseBodyRead);
+  int result = relay_url_loader_->ReadResponseBody(&relay_response_body_[pos],
+                                                   kReadSize,
+                                                   callback);
   DCHECK_EQ(result, PP_OK_COMPLETIONPENDING);
 }
 
diff --git a/remoting/client/plugin/pepper_util.cc b/remoting/client/plugin/pepper_util.cc
index 3dc8e81..5acfa01 100644
--- a/remoting/client/plugin/pepper_util.cc
+++ b/remoting/client/plugin/pepper_util.cc
@@ -6,85 +6,77 @@
 
 #include "base/callback.h"
 #include "base/logging.h"
-#include "ppapi/c/pp_completion_callback.h"
+#include "base/sys_byteorder.h"
 #include "ppapi/cpp/module.h"
-#include "ppapi/cpp/private/net_address_private.h"
+#include "ppapi/cpp/net_address.h"
 #include "third_party/libjingle/source/talk/base/socketaddress.h"
 
 namespace remoting {
 
-static void CallbackAdapter(void* user_data, int32_t result) {
-  scoped_ptr<base::Callback<void(int)> > callback(
-      reinterpret_cast<base::Callback<void(int)>*>(user_data));
-  callback->Run(result);
-}
-
-pp::CompletionCallback PpCompletionCallback(
-    base::Callback<void(int)> callback) {
-  return pp::CompletionCallback(&CallbackAdapter,
-                                new base::Callback<void(int)>(callback));
-}
-
-bool SocketAddressToPpAddressWithPort(const talk_base::SocketAddress& address,
-                                      PP_NetAddress_Private* pp_address,
-                                      uint16_t port) {
-  bool result = false;
+bool SocketAddressToPpNetAddressWithPort(
+    const pp::InstanceHandle& instance,
+    const talk_base::SocketAddress& address,
+    pp::NetAddress* pp_address,
+    uint16_t port) {
   switch (address.ipaddr().family()) {
     case AF_INET: {
-      in_addr addr = address.ipaddr().ipv4_address();
-      result = pp::NetAddressPrivate::CreateFromIPv4Address(
-          reinterpret_cast<uint8_t*>(&addr), port, pp_address);
-      break;
+      in_addr ipv4_addr = address.ipaddr().ipv4_address();
+      PP_NetAddress_IPv4 ip_addr;
+      ip_addr.port = base::HostToNet16(port);
+      memcpy(&ip_addr.addr, &ipv4_addr, sizeof(ip_addr.addr));
+      *pp_address = pp::NetAddress(instance, ip_addr);
+      return true;
     }
     case AF_INET6: {
-      in6_addr addr = address.ipaddr().ipv6_address();
-      result = pp::NetAddressPrivate::CreateFromIPv6Address(
-          addr.s6_addr, 0, port, pp_address);
-      break;
+      in6_addr ipv6_addr = address.ipaddr().ipv6_address();
+      PP_NetAddress_IPv6 ip_addr;
+      ip_addr.port = base::HostToNet16(port);
+      memcpy(&ip_addr.addr, &ipv6_addr, sizeof(ip_addr.addr));
+      *pp_address = pp::NetAddress(instance, ip_addr);
+      return true;
     }
     default: {
       LOG(WARNING) << "Unknown address family: " << address.ipaddr().family();
+      return false;
     }
   }
-  if (!result) {
-    LOG(WARNING) << "Failed to convert address: " << address.ToString();
-  }
-  return result;
 }
 
-bool SocketAddressToPpAddress(const talk_base::SocketAddress& address,
-                              PP_NetAddress_Private* pp_address) {
-  return SocketAddressToPpAddressWithPort(address, pp_address, address.port());
+bool SocketAddressToPpNetAddress(const pp::InstanceHandle& instance,
+                                 const talk_base::SocketAddress& address,
+                                 pp::NetAddress* pp_net_address) {
+  return SocketAddressToPpNetAddressWithPort(instance,
+                                             address,
+                                             pp_net_address,
+                                             address.port());
 }
 
-bool PpAddressToSocketAddress(const PP_NetAddress_Private& pp_address,
-                              talk_base::SocketAddress* address) {
-  uint8_t addr_storage[16];
-  bool result = pp::NetAddressPrivate::GetAddress(
-      pp_address, &addr_storage, sizeof(addr_storage));
-
-  if (result) {
-    switch (pp::NetAddressPrivate::GetFamily(pp_address)) {
-      case PP_NETADDRESSFAMILY_PRIVATE_IPV4:
-        address->SetIP(talk_base::IPAddress(
-            *reinterpret_cast<in_addr*>(addr_storage)));
-        break;
-      case PP_NETADDRESSFAMILY_PRIVATE_IPV6:
-        address->SetIP(talk_base::IPAddress(
-            *reinterpret_cast<in6_addr*>(addr_storage)));
-        break;
-      default:
-        result = false;
+void PpNetAddressToSocketAddress(const pp::NetAddress& pp_net_address,
+                                 talk_base::SocketAddress* address) {
+  switch (pp_net_address.GetFamily()) {
+    case PP_NETADDRESS_FAMILY_IPV4: {
+      PP_NetAddress_IPv4 ipv4_addr;
+      CHECK(pp_net_address.DescribeAsIPv4Address(&ipv4_addr));
+      address->SetIP(talk_base::IPAddress(
+          *reinterpret_cast<in_addr*>(&ipv4_addr.addr)));
+      address->SetPort(base::NetToHost16(ipv4_addr.port));
+      return;
     }
-  }
+    case PP_NETADDRESS_FAMILY_IPV6: {
+      PP_NetAddress_IPv6 ipv6_addr;
+      CHECK(pp_net_address.DescribeAsIPv6Address(&ipv6_addr));
+      address->SetIP(talk_base::IPAddress(
+          *reinterpret_cast<in6_addr*>(&ipv6_addr.addr)));
+      address->SetPort(base::NetToHost16(ipv6_addr.port));
+      return;
+    }
+    case PP_NETADDRESS_FAMILY_UNSPECIFIED: {
+      break;
+    }
+  };
 
-  if (!result) {
-    LOG(WARNING) << "Failed to convert address: "
-                 << pp::NetAddressPrivate::Describe(pp_address, true);
-  } else {
-    address->SetPort(pp::NetAddressPrivate::GetPort(pp_address));
-  }
-  return result;
+  NOTREACHED();
+  address->Clear();
 }
 
 }  // namespace remoting
diff --git a/remoting/client/plugin/pepper_util.h b/remoting/client/plugin/pepper_util.h
index 63c0bce..d0d2ad5 100644
--- a/remoting/client/plugin/pepper_util.h
+++ b/remoting/client/plugin/pepper_util.h
@@ -5,35 +5,32 @@
 #ifndef REMOTING_CLIENT_PLUGIN_PLUGIN_UTIL_H_
 #define REMOTING_CLIENT_PLUGIN_PLUGIN_UTIL_H_
 
-#include "base/basictypes.h"
+#include <stdint.h>
+
 #include "base/callback_forward.h"
 
-#include "ppapi/cpp/completion_callback.h"
-
-struct PP_NetAddress_Private;
+namespace pp {
+class InstanceHandle;
+class NetAddress;
+}
 
 namespace talk_base {
 class SocketAddress;
-}  // namespace talk_base
+}
 
 namespace remoting {
 
-// Adapts a base::Callback to a pp::CompletionCallback, which may be passed to
-// exactly one Pepper API. If the adapted callback is not used then a copy of
-// |callback| will be leaked, including references & passed values bound to it.
-// Pepper guarantees that each completion callback is called once and only once
-// (aborted callbacks are called with PP_ABOIRTED), so there should be no leaks
-// as long as the result of this function is passed to Pepper.
-pp::CompletionCallback PpCompletionCallback(base::Callback<void(int)> callback);
-
 // Helpers to convert between different socket address representations.
-bool SocketAddressToPpAddressWithPort(const talk_base::SocketAddress& address,
-                                      PP_NetAddress_Private* pp_address,
-                                      uint16_t port);
-bool SocketAddressToPpAddress(const talk_base::SocketAddress& address,
-                              PP_NetAddress_Private* pp_address);
-bool PpAddressToSocketAddress(const PP_NetAddress_Private& pp_address,
-                              talk_base::SocketAddress* address);
+bool SocketAddressToPpNetAddressWithPort(
+    const pp::InstanceHandle& instance,
+    const talk_base::SocketAddress& address,
+    pp::NetAddress* pp_net_address,
+    uint16_t port);
+bool SocketAddressToPpNetAddress(const pp::InstanceHandle& instance,
+                                 const talk_base::SocketAddress& address,
+                                 pp::NetAddress* pp_net_address);
+void PpNetAddressToSocketAddress(const pp::NetAddress& pp_net_address,
+                                 talk_base::SocketAddress* address);
 
 }  // namespace remoting
 
diff --git a/remoting/client/plugin/pepper_view.cc b/remoting/client/plugin/pepper_view.cc
index 2848526..c412316 100644
--- a/remoting/client/plugin/pepper_view.cc
+++ b/remoting/client/plugin/pepper_view.cc
@@ -11,8 +11,6 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/time/time.h"
 #include "ppapi/cpp/completion_callback.h"
-#include "ppapi/cpp/dev/graphics_2d_dev.h"
-#include "ppapi/cpp/dev/view_dev.h"
 #include "ppapi/cpp/image_data.h"
 #include "ppapi/cpp/point.h"
 #include "ppapi/cpp/rect.h"
@@ -22,7 +20,6 @@
 #include "remoting/client/client_context.h"
 #include "remoting/client/frame_producer.h"
 #include "remoting/client/plugin/chromoting_instance.h"
-#include "remoting/client/plugin/pepper_util.h"
 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
 
 using base::Passed;
@@ -80,7 +77,8 @@
     source_dpi_(SkIPoint::Make(0, 0)),
     flush_pending_(false),
     is_initialized_(false),
-    frame_received_(false) {
+    frame_received_(false),
+    callback_factory_(this) {
   InitiateDrawing();
 }
 
@@ -104,8 +102,7 @@
 
   pp::Rect pp_size = view.GetRect();
   SkISize new_dips_size = SkISize::Make(pp_size.width(), pp_size.height());
-  pp::ViewDev view_dev(view);
-  float new_dips_to_device_scale = view_dev.GetDeviceScale();
+  float new_dips_to_device_scale = view.GetDeviceScale();
 
   if (dips_size_ != new_dips_size ||
       dips_to_device_scale_ != new_dips_to_device_scale) {
@@ -135,8 +132,7 @@
     graphics2d_ = pp::Graphics2D(instance_, pp_size, false);
 
     // Specify the scale from our coordinates to DIPs.
-    pp::Graphics2D_Dev graphics2d_dev(graphics2d_);
-    graphics2d_dev.SetScale(1.0f / dips_to_view_scale_);
+    graphics2d_.SetScale(1.0f / dips_to_view_scale_);
 
     bool result = instance_->BindGraphics(graphics2d_);
 
@@ -303,9 +299,11 @@
   }
 
   // Flush the updated areas to the screen.
-  int error = graphics2d_.Flush(
-      PpCompletionCallback(base::Bind(
-          &PepperView::OnFlushDone, AsWeakPtr(), start_time, buffer)));
+  pp::CompletionCallback callback =
+      callback_factory_.NewCallback(&PepperView::OnFlushDone,
+                                    start_time,
+                                    buffer);
+  int error = graphics2d_.Flush(callback);
   CHECK(error == PP_OK_COMPLETIONPENDING);
   flush_pending_ = true;
 
@@ -315,9 +313,9 @@
     instance_->SetDesktopShape(*buffer_shape);
 }
 
-void PepperView::OnFlushDone(base::Time paint_start,
-                             webrtc::DesktopFrame* buffer,
-                             int result) {
+void PepperView::OnFlushDone(int result,
+                             const base::Time& paint_start,
+                             webrtc::DesktopFrame* buffer) {
   DCHECK(context_->main_task_runner()->BelongsToCurrentThread());
   DCHECK(flush_pending_);
 
diff --git a/remoting/client/plugin/pepper_view.h b/remoting/client/plugin/pepper_view.h
index 7f405d4..4848a8e 100644
--- a/remoting/client/plugin/pepper_view.h
+++ b/remoting/client/plugin/pepper_view.h
@@ -10,10 +10,11 @@
 
 #include <list>
 
-#include "base/memory/weak_ptr.h"
+#include "base/compiler_specific.h"
 #include "ppapi/cpp/graphics_2d.h"
 #include "ppapi/cpp/view.h"
 #include "ppapi/cpp/point.h"
+#include "ppapi/utility/completion_callback_factory.h"
 #include "remoting/client/frame_consumer.h"
 
 namespace base {
@@ -30,8 +31,7 @@
 class ClientContext;
 class FrameProducer;
 
-class PepperView : public FrameConsumer,
-                   public base::SupportsWeakPtr<PepperView> {
+class PepperView : public FrameConsumer {
  public:
   // Constructs a PepperView for the |instance|. The |instance|, |context|
   // and |producer| must outlive this class.
@@ -86,9 +86,9 @@
 
   // Handles completion of FlushBuffer(), triggering a new buffer to be
   // returned to FrameProducer for rendering.
-  void OnFlushDone(base::Time paint_start,
-                   webrtc::DesktopFrame* buffer,
-                   int result);
+  void OnFlushDone(int result,
+                   const base::Time& paint_start,
+                   webrtc::DesktopFrame* buffer);
 
   // Reference to the creating plugin instance. Needed for interacting with
   // pepper.  Marking explicitly as const since it must be initialized at
@@ -142,6 +142,8 @@
   // True after the first call to ApplyBuffer().
   bool frame_received_;
 
+  pp::CompletionCallbackFactory<PepperView> callback_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(PepperView);
 };
 
diff --git a/remoting/client/rectangle_update_decoder.cc b/remoting/client/rectangle_update_decoder.cc
index 3e93446..741357d 100644
--- a/remoting/client/rectangle_update_decoder.cc
+++ b/remoting/client/rectangle_update_decoder.cc
@@ -5,8 +5,8 @@
 #include "remoting/client/rectangle_update_decoder.h"
 
 #include "base/bind.h"
-#include "base/bind_helpers.h"
 #include "base/callback.h"
+#include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/single_thread_task_runner.h"
diff --git a/remoting/host/DEPS b/remoting/host/DEPS
index ef945f3..90d75ca 100644
--- a/remoting/host/DEPS
+++ b/remoting/host/DEPS
@@ -1,8 +1,9 @@
 include_rules = [
+  "+jingle/glue",
   "+net",
   "+remoting/codec",
-  "+remoting/protocol",
   "+remoting/jingle_glue",
+  "+remoting/protocol",
   "+third_party/jsoncpp",
   "+third_party/modp_b64",
   "+third_party/npapi",
diff --git a/remoting/host/plugin/host_script_object.cc b/remoting/host/plugin/host_script_object.cc
index b766cad..3ad35ab 100644
--- a/remoting/host/plugin/host_script_object.cc
+++ b/remoting/host/plugin/host_script_object.cc
@@ -15,6 +15,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/platform_thread.h"
 #include "base/values.h"
+#include "jingle/glue/xmpp_client_socket_factory.h"
 #include "net/base/net_util.h"
 #include "net/socket/client_socket_factory.h"
 #include "remoting/base/auth_token_util.h"
@@ -331,10 +332,12 @@
   host_key_pair_ = RsaKeyPair::Generate();
 
   // Create XMPP connection.
+  scoped_ptr<jingle_glue::ResolvingClientSocketFactory> socket_factory(
+      new jingle_glue::XmppClientSocketFactory(
+          net::ClientSocketFactory::GetDefaultFactory(), net::SSLConfig(),
+          host_context_->url_request_context_getter(), false));
   scoped_ptr<SignalStrategy> signal_strategy(
-      new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(),
-                             host_context_->url_request_context_getter(),
-                             xmpp_server_config_));
+      new XmppSignalStrategy(socket_factory.Pass(), xmpp_server_config_));
 
   // Request registration of the host for support.
   scoped_ptr<RegisterSupportHostRequest> register_request(
diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc
index 9ed3383..e1ba452 100644
--- a/remoting/host/remoting_me2me_host.cc
+++ b/remoting/host/remoting_me2me_host.cc
@@ -27,6 +27,7 @@
 #include "ipc/ipc_channel.h"
 #include "ipc/ipc_channel_proxy.h"
 #include "ipc/ipc_listener.h"
+#include "jingle/glue/xmpp_client_socket_factory.h"
 #include "media/base/media.h"
 #include "net/base/network_change_notifier.h"
 #include "net/socket/client_socket_factory.h"
@@ -965,9 +966,12 @@
          state_ == HOST_STOPPED) << state_;
   state_ = HOST_STARTED;
 
+  scoped_ptr<jingle_glue::ResolvingClientSocketFactory> socket_factory(
+      new jingle_glue::XmppClientSocketFactory(
+          net::ClientSocketFactory::GetDefaultFactory(), net::SSLConfig(),
+          context_->url_request_context_getter(), false));
   signal_strategy_.reset(
-      new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(),
-                             context_->url_request_context_getter(),
+      new XmppSignalStrategy(socket_factory.Pass(),
                              xmpp_server_config_));
 
   scoped_ptr<DnsBlackholeChecker> dns_blackhole_checker(
diff --git a/remoting/host/setup/daemon_controller_linux.cc b/remoting/host/setup/daemon_controller_linux.cc
index e2d6566..3d45885 100644
--- a/remoting/host/setup/daemon_controller_linux.cc
+++ b/remoting/host/setup/daemon_controller_linux.cc
@@ -110,10 +110,12 @@
   // As long as we're relying on running an external binary from the
   // PATH, don't do it as root.
   if (getuid() == 0) {
+    LOG(ERROR) << "Refusing to run script as root.";
     return false;
   }
   base::FilePath script_path;
   if (!GetScriptPath(&script_path)) {
+    LOG(ERROR) << "GetScriptPath() failed.";
     return false;
   }
   CommandLine command_line(script_path);
@@ -130,11 +132,15 @@
   base::LaunchOptions options;
   options.fds_to_remap = &fds_to_remap;
   if (!base::LaunchProcess(command_line, options, &process_handle)) {
+    LOG(ERROR) << "Failed to run command: "
+               << command_line.GetCommandLineString();
     return false;
   }
 
   if (!base::WaitForExitCodeWithTimeout(process_handle, exit_code, timeout)) {
     base::KillProcess(process_handle, 0, false);
+    LOG(ERROR) << "Timeout exceeded for command: "
+               << command_line.GetCommandLineString();
     return false;
   }
 
diff --git a/remoting/host/setup/host_starter.cc b/remoting/host/setup/host_starter.cc
index 3a0646b..6758f58 100644
--- a/remoting/host/setup/host_starter.cc
+++ b/remoting/host/setup/host_starter.cc
@@ -25,6 +25,7 @@
       service_client_(service_client.Pass()),
       daemon_controller_(daemon_controller.Pass()),
       consent_to_data_collection_(false),
+      unregistering_host_(false),
       weak_ptr_factory_(this),
       weak_ptr_(weak_ptr_factory_.GetWeakPtr()) {
   main_task_runner_ = base::ThreadTaskRunnerHandle::Get();
@@ -178,14 +179,13 @@
     return;
   }
   if (result != DaemonController::RESULT_OK) {
+    unregistering_host_ = true;
     service_client_->UnregisterHost(host_id_, access_token_, this);
     return;
   }
-  Result done_result = (result == DaemonController::RESULT_OK) ?
-      START_COMPLETE : START_ERROR;
   CompletionCallback cb = on_done_;
   on_done_.Reset();
-  cb.Run(done_result);
+  cb.Run(START_COMPLETE);
 }
 
 void HostStarter::OnOAuthError() {
@@ -196,7 +196,12 @@
   }
   CompletionCallback cb = on_done_;
   on_done_.Reset();
-  cb.Run(OAUTH_ERROR);
+  if (unregistering_host_) {
+    LOG(ERROR) << "OAuth error occurred when unregistering host.";
+    cb.Run(START_ERROR);
+  } else {
+    cb.Run(OAUTH_ERROR);
+  }
 }
 
 void HostStarter::OnNetworkError(int response_code) {
@@ -207,7 +212,12 @@
   }
   CompletionCallback cb = on_done_;
   on_done_.Reset();
-  cb.Run(NETWORK_ERROR);
+  if (unregistering_host_) {
+    LOG(ERROR) << "Network error occurred when unregistering host.";
+    cb.Run(START_ERROR);
+  } else {
+    cb.Run(NETWORK_ERROR);
+  }
 }
 
 void HostStarter::OnHostUnregistered() {
diff --git a/remoting/host/setup/host_starter.h b/remoting/host/setup/host_starter.h
index 3bb2e3e..ef97d21 100644
--- a/remoting/host/setup/host_starter.h
+++ b/remoting/host/setup/host_starter.h
@@ -91,6 +91,11 @@
   std::string host_id_;
   bool use_service_account_;
 
+  // True if the host was not started and unregistration was requested. If this
+  // is set and a network/OAuth error occurs during unregistration, this will
+  // be logged, but the error will still be reported as START_ERROR.
+  bool unregistering_host_;
+
   base::WeakPtrFactory<HostStarter> weak_ptr_factory_;
   base::WeakPtr<HostStarter> weak_ptr_;
 
diff --git a/remoting/host/setup/service_client.cc b/remoting/host/setup/service_client.cc
index 215b04e..43f24ca 100644
--- a/remoting/host/setup/service_client.cc
+++ b/remoting/host/setup/service_client.cc
@@ -134,7 +134,10 @@
     delegate_->OnOAuthError();
     return;
   }
-  if (source->GetResponseCode() == net::HTTP_OK) {
+
+  // Treat codes 2xx as successful; for example, HTTP_NO_CONTENT (204) can be
+  // returned from a DELETE_REQUEST.
+  if (source->GetResponseCode() / 100 == 2) {
     switch (old_type) {
       case PENDING_REQUEST_NONE:
         break;
diff --git a/remoting/jingle_glue/xmpp_signal_strategy.cc b/remoting/jingle_glue/xmpp_signal_strategy.cc
index ed27833..a9da0de 100644
--- a/remoting/jingle_glue/xmpp_signal_strategy.cc
+++ b/remoting/jingle_glue/xmpp_signal_strategy.cc
@@ -11,12 +11,11 @@
 #include "base/strings/string_util.h"
 #include "base/thread_task_runner_handle.h"
 #include "jingle/glue/chrome_async_socket.h"
+#include "jingle/glue/resolving_client_socket_factory.h"
 #include "jingle/glue/task_pump.h"
-#include "jingle/glue/xmpp_client_socket_factory.h"
 #include "jingle/notifier/base/gaia_constants.h"
 #include "jingle/notifier/base/gaia_token_pre_xmpp_auth.h"
 #include "net/socket/client_socket_factory.h"
-#include "net/url_request/url_request_context_getter.h"
 #include "third_party/libjingle/source/talk/base/thread.h"
 #include "third_party/libjingle/source/talk/xmpp/prexmppauth.h"
 #include "third_party/libjingle/source/talk/xmpp/saslcookiemechanism.h"
@@ -38,11 +37,9 @@
 XmppSignalStrategy::XmppServerConfig::~XmppServerConfig() {}
 
 XmppSignalStrategy::XmppSignalStrategy(
-    net::ClientSocketFactory* socket_factory,
-    scoped_refptr<net::URLRequestContextGetter> request_context_getter,
+    scoped_ptr<jingle_glue::ResolvingClientSocketFactory> socket_factory,
     const XmppSignalStrategy::XmppServerConfig& xmpp_server_config)
-    : socket_factory_(socket_factory),
-      request_context_getter_(request_context_getter),
+    : socket_factory_(socket_factory.Pass()),
       resource_name_(kDefaultResourceName),
       xmpp_client_(NULL),
       xmpp_server_config_(xmpp_server_config),
@@ -82,11 +79,8 @@
   settings.set_use_tls(
       xmpp_server_config_.use_tls ? buzz::TLS_ENABLED : buzz::TLS_DISABLED);
 
-  scoped_ptr<jingle_glue::XmppClientSocketFactory> xmpp_socket_factory(
-      new jingle_glue::XmppClientSocketFactory(
-          socket_factory_, net::SSLConfig(), request_context_getter_, false));
   buzz::AsyncSocket* socket = new jingle_glue::ChromeAsyncSocket(
-    xmpp_socket_factory.release(), kReadBufferSize, kWriteBufferSize);
+    socket_factory_.release(), kReadBufferSize, kWriteBufferSize);
 
   task_runner_.reset(new jingle_glue::TaskPump());
   xmpp_client_ = new buzz::XmppClient(task_runner_.get());
diff --git a/remoting/jingle_glue/xmpp_signal_strategy.h b/remoting/jingle_glue/xmpp_signal_strategy.h
index 4ef6b70..ecb13cc 100644
--- a/remoting/jingle_glue/xmpp_signal_strategy.h
+++ b/remoting/jingle_glue/xmpp_signal_strategy.h
@@ -21,10 +21,9 @@
 #include "third_party/libjingle/source/talk/base/sigslot.h"
 #include "third_party/libjingle/source/talk/xmpp/xmppclient.h"
 
-namespace net {
-class ClientSocketFactory;
-class URLRequestContextGetter;
-}  // namespace net
+namespace jingle_glue {
+class ResolvingClientSocketFactory;
+}  // namespace jingle_glue
 
 namespace talk_base {
 class TaskRunner;
@@ -54,8 +53,7 @@
   };
 
   XmppSignalStrategy(
-      net::ClientSocketFactory* socket_factory,
-      scoped_refptr<net::URLRequestContextGetter> request_context_getter,
+      scoped_ptr<jingle_glue::ResolvingClientSocketFactory> socket_factory,
       const XmppServerConfig& xmpp_server_config);
   virtual ~XmppSignalStrategy();
 
@@ -93,8 +91,7 @@
 
   void SendKeepAlive();
 
-  net::ClientSocketFactory* socket_factory_;
-  scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+  scoped_ptr<jingle_glue::ResolvingClientSocketFactory> socket_factory_;
   std::string resource_name_;
   scoped_ptr<talk_base::TaskRunner> task_runner_;
   buzz::XmppClient* xmpp_client_;
diff --git a/remoting/protocol/client_control_dispatcher.cc b/remoting/protocol/client_control_dispatcher.cc
index a42c3f6..76376e2 100644
--- a/remoting/protocol/client_control_dispatcher.cc
+++ b/remoting/protocol/client_control_dispatcher.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind_helpers.h"
 #include "base/callback.h"
+#include "base/callback_helpers.h"
 #include "base/message_loop/message_loop_proxy.h"
 #include "net/socket/stream_socket.h"
 #include "remoting/base/constants.h"
diff --git a/remoting/protocol/host_control_dispatcher.cc b/remoting/protocol/host_control_dispatcher.cc
index 26f09fc..671c801 100644
--- a/remoting/protocol/host_control_dispatcher.cc
+++ b/remoting/protocol/host_control_dispatcher.cc
@@ -4,6 +4,7 @@
 
 #include "remoting/protocol/host_control_dispatcher.h"
 
+#include "base/callback_helpers.h"
 #include "base/message_loop/message_loop_proxy.h"
 #include "net/socket/stream_socket.h"
 #include "remoting/base/constants.h"
diff --git a/remoting/protocol/host_event_dispatcher.cc b/remoting/protocol/host_event_dispatcher.cc
index f589e0d..c5206f4 100644
--- a/remoting/protocol/host_event_dispatcher.cc
+++ b/remoting/protocol/host_event_dispatcher.cc
@@ -4,7 +4,7 @@
 
 #include "remoting/protocol/host_event_dispatcher.h"
 
-#include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
 #include "net/socket/stream_socket.h"
 #include "remoting/base/constants.h"
 #include "remoting/proto/event.pb.h"
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index 89ad20d..ec6d747 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -1857,6 +1857,8 @@
           ],
           'variables': {
             'apk_name': 'Chromoting',
+            'android_app_version_name': '<(version_full)',
+            'android_app_version_code': '<!(python ../build/util/lastchange.py --revision-only)',
             'manifest_package_name': 'org.chromium.chromoting',
             'native_lib_target': 'libremoting_client_jni',
             'java_in_dir': 'android/java',
diff --git a/remoting/test/me2me_browsertest.cc b/remoting/test/me2me_browsertest.cc
new file mode 100644
index 0000000..115bd93
--- /dev/null
+++ b/remoting/test/me2me_browsertest.cc
@@ -0,0 +1,29 @@
+// 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 "remote_desktop_browsertest.h"
+
+namespace remoting {
+
+IN_PROC_BROWSER_TEST_F(RemoteDesktopBrowserTest,
+                       MANUAL_Me2Me_Connect_Localhost) {
+  VerifyInternetAccess();
+
+  Install();
+
+  LaunchChromotingApp();
+
+  // Authorize, Authenticate, and Approve.
+  Auth();
+
+  StartMe2Me();
+
+  ConnectToLocalHost();
+
+  EnterPin(me2me_pin());
+
+  Cleanup();
+}
+
+}  // namespace remoting
diff --git a/remoting/test/remote_desktop_browsertest.cc b/remoting/test/remote_desktop_browsertest.cc
index 8d91a06..bc49ed4 100644
--- a/remoting/test/remote_desktop_browsertest.cc
+++ b/remoting/test/remote_desktop_browsertest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -43,6 +43,26 @@
   EXPECT_EQ(GetCurrentURL().host(), "www.google.com");
 }
 
+bool RemoteDesktopBrowserTest::HtmlElementVisible(const std::string& name) {
+  _ASSERT_TRUE(HtmlElementExists(name));
+
+  ExecuteScript(
+      "function isElementVisible(name) {"
+      "  var element = document.getElementById(name);"
+      "  /* The existence of the element has already been ASSERTed. */"
+      "  do {"
+      "    if (element.hidden) {"
+      "      return false;"
+      "    }"
+      "    element = element.parentNode;"
+      "  } while (element != null);"
+      "  return true;"
+      "};");
+
+  return ExecuteScriptAndExtractBool(
+      "isElementVisible(\"" + name + "\")");
+}
+
 void RemoteDesktopBrowserTest::InstallChromotingApp() {
   base::FilePath install_dir(WebAppCrxPath());
   scoped_refptr<const Extension> extension(InstallExtensionWithUIAutoConfirm(
@@ -157,6 +177,27 @@
       "remoting.OAuth2.prototype.isAuthenticated()"));
 }
 
+void RemoteDesktopBrowserTest::StartMe2Me() {
+  // The chromoting extension should be installed.
+  ASSERT_FALSE(ChromotingID().empty());
+
+  // The active tab should have the chromoting app loaded.
+  ASSERT_EQ(GetCurrentURL(), Chromoting_Main_URL());
+  EXPECT_TRUE(ExecuteScriptAndExtractBool(
+      "remoting.OAuth2.prototype.isAuthenticated()"));
+
+  // The Me2Me host list should be hidden.
+  ASSERT_FALSE(HtmlElementVisible("me2me-content"));
+  // The Me2Me "Get Start" button should be visible.
+  ASSERT_TRUE(HtmlElementVisible("get-started-me2me"));
+
+  // Starting Me2Me.
+  ExecuteScript("remoting.showMe2MeUiAndSave();");
+
+  EXPECT_TRUE(HtmlElementVisible("me2me-content"));
+  EXPECT_FALSE(HtmlElementVisible("me2me-first-run"));
+}
+
 void RemoteDesktopBrowserTest::Install() {
   // TODO: add support for installing unpacked extension (the v2 app needs it).
   if (!NoInstall()) {
@@ -186,6 +227,24 @@
   Approve();
 }
 
+void RemoteDesktopBrowserTest::ConnectToLocalHost() {
+  // Wait until remoting.hostList.localHost_ is initialized.
+  // This can take a while.
+  // TODO: Instead of polling, can we register a callback to
+  // remoting.hostList.setLocalHost_?
+  while (ExecuteScriptAndExtractBool(
+      "remoting.hostList.localHost_ == null")) {
+  }
+
+  ASSERT_TRUE(ExecuteScriptAndExtractBool(
+      "remoting.hostList.localHost_.hostName && "
+      "remoting.hostList.localHost_.hostId && "
+      "remoting.hostList.localHost_.status && "
+      "remoting.hostList.localHost_.status == 'ONLINE'"));
+
+  ClickOnControl("this-host-connect");
+}
+
 void RemoteDesktopBrowserTest::EnableDNSLookupForThisTest(
     net::RuleBasedHostResolverProc* host_resolver) {
   // mock_host_resolver_override_ takes ownership of the resolver.
@@ -229,6 +288,7 @@
 
   username_ = command_line->GetSwitchValueASCII(kUsername);
   password_ = command_line->GetSwitchValueASCII(kkPassword);
+  me2me_pin_ = command_line->GetSwitchValueASCII(kMe2MePin);
 
   no_cleanup_ = command_line->HasSwitch(kNoCleanup);
   no_install_ = command_line->HasSwitch(kNoInstall);
@@ -282,8 +342,6 @@
 bool RemoteDesktopBrowserTest::ExecuteScriptAndExtractBool(
     const std::string& script) {
   bool result;
-  // Using a private assert function because ASSERT_TRUE can only be used in
-  // void returning functions.
   _ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
       browser()->tab_strip_model()->GetActiveWebContents(),
       "window.domAutomationController.send(" + script + ");",
@@ -292,6 +350,28 @@
   return result;
 }
 
+int RemoteDesktopBrowserTest::ExecuteScriptAndExtractInt(
+    const std::string& script) {
+  int result;
+  _ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
+      browser()->tab_strip_model()->GetActiveWebContents(),
+      "window.domAutomationController.send(" + script + ");",
+      &result));
+
+  return result;
+}
+
+std::string RemoteDesktopBrowserTest::ExecuteScriptAndExtractString(
+    const std::string& script) {
+  std::string result;
+  _ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+      browser()->tab_strip_model()->GetActiveWebContents(),
+      "window.domAutomationController.send(" + script + ");",
+      &result));
+
+  return result;
+}
+
 // Helper to navigate to a given url.
 void RemoteDesktopBrowserTest::NavigateToURLAndWait(const GURL& url) {
   content::WindowedNotificationObserver observer(
@@ -304,4 +384,43 @@
   observer.Wait();
 }
 
+void RemoteDesktopBrowserTest::ClickOnControl(const std::string& name) {
+  _ASSERT_TRUE(HtmlElementExists(name));
+  _ASSERT_TRUE(HtmlElementVisible(name));
+
+  ExecuteScript("document.getElementById(\"" + name + "\").click();");
+}
+
+void RemoteDesktopBrowserTest::EnterPin(const std::string& pin) {
+  // Wait for the pin-form to be displayed. This can take a while.
+  // We also need to dismiss the host-needs-update dialog if it comes up.
+  // TODO 1: Instead of polling, can we register a callback to be called
+  // when the pin-form is ready?
+  // TODO 2: Instead of blindly dismiss the host-needs-update dialog,
+  // we should verify that it only pops up at the right circumstance. That
+  // probably belongs in a separate test case though.
+  do {
+    if (HtmlElementVisible("host-needs-update-connect-button")) {
+      ClickOnControl("host-needs-update-connect-button");
+    }
+  } while (!HtmlElementVisible("pin-form"));
+
+  ExecuteScript(
+      "document.getElementById(\"pin-entry\").value = \"" + pin + "\";");
+
+  ClickOnControl("pin-connect-button");
+
+  WaitForConnection();
+}
+
+void RemoteDesktopBrowserTest::WaitForConnection() {
+  // Wait until the client has connected to the server.
+  // This can take a while.
+  // TODO: Instead of polling, can we register a callback to
+  // remoting.clientSession.onStageChange_?
+  while (ExecuteScriptAndExtractBool(
+      "remoting.clientSession == null")) {
+  }
+}
+
 }  // namespace remoting
diff --git a/remoting/test/remote_desktop_browsertest.h b/remoting/test/remote_desktop_browsertest.h
index 96a5b5e..bab191f 100644
--- a/remoting/test/remote_desktop_browsertest.h
+++ b/remoting/test/remote_desktop_browsertest.h
@@ -21,9 +21,11 @@
 const char kWebAppCrx[] = "webapp-crx";
 const char kUsername[] = "username";
 const char kkPassword[] = "password";
+const char kMe2MePin[] = "me2me-pin";
 
-// ASSERT_TRUE can only be used in void returning functions.
-void _ASSERT_TRUE(bool condition) {
+// ASSERT_TRUE can only be used in void returning functions. This version
+// should be used in non-void-returning functions.
+inline void _ASSERT_TRUE(bool condition) {
   ASSERT_TRUE(condition);
   return;
 }
@@ -76,6 +78,9 @@
   // Approve: grant the chromoting app necessary permissions.
   void Approve();
 
+  // Click on "Get Started" in the Me2Me section and show the host list.
+  void StartMe2Me();
+
 
   /*                                                      */
   /* The following helpers each perform a composite task. */
@@ -92,6 +97,15 @@
   // on the chromoting main page authenticated and ready to go.
   void Auth();
 
+  // Connect to the local host through Me2Me.
+  void ConnectToLocalHost();
+
+  // Enter the pin number and connect.
+  void EnterPin(const std::string& name);
+
+  // Helper to get the pin number used for me2me authentication.
+  std::string me2me_pin() { return me2me_pin_; }
+
  private:
   // Change behavior of the default host resolver to allow DNS lookup
   // to proceed instead of being blocked by the test infrastructure.
@@ -153,16 +167,33 @@
   // extract the boolean result.
   bool ExecuteScriptAndExtractBool(const std::string& script);
 
+  // Helper to execute a javascript code snippet on the current page and
+  // extract the int result.
+  int ExecuteScriptAndExtractInt(const std::string& script);
+
+  // Helper to execute a javascript code snippet on the current page and
+  // extract the string result.
+  std::string ExecuteScriptAndExtractString(const std::string& script);
+
   // Helper to navigate to a given url.
   void NavigateToURLAndWait(const GURL& url);
 
-  // Helper to check whether a html element with the given name exists on
+  // Helper to check whether an html element with the given name exists on
   // the current page.
   bool HtmlElementExists(const std::string& name) {
     return ExecuteScriptAndExtractBool(
         "document.getElementById(\"" + name + "\") != null");
   }
 
+  // Helper to check whether a html element with the given name is visible.
+  bool HtmlElementVisible(const std::string& name);
+
+  // Click on the named HTML control.
+  void ClickOnControl(const std::string& name);
+
+  // Wait for the me2me connection to be established.
+  void WaitForConnection();
+
 
   /*        */
   /* Fields */
@@ -179,6 +210,7 @@
   base::FilePath webapp_crx_;
   std::string username_;
   std::string password_;
+  std::string me2me_pin_;
 };
 
 }  // namespace remoting
diff --git a/remoting/webapp/client_screen.js b/remoting/webapp/client_screen.js
index 3368e52..f56643c 100644
--- a/remoting/webapp/client_screen.js
+++ b/remoting/webapp/client_screen.js
@@ -296,6 +296,12 @@
       pinField.value = '';
       if (event.target == pinForm) {
         event.preventDefault();
+
+        // Set the focus away from the password field. This has to be done
+        // before the password field gets hidden, to work around a Blink
+        // clipboard-handling bug - http://crbug.com/281523.
+        document.getElementById('pin-connect-button').focus();
+
         remoting.setMode(remoting.AppMode.CLIENT_CONNECTING);
         onPinFetched(pin);
         if (/** @type {boolean} */(rememberPinCheckbox.checked)) {
diff --git a/remoting/webapp/client_session.js b/remoting/webapp/client_session.js
index 6587216..3630871 100644
--- a/remoting/webapp/client_session.js
+++ b/remoting/webapp/client_session.js
@@ -23,7 +23,6 @@
 var remoting = remoting || {};
 
 /**
- * @param {string} clientJid The jid of the WCS client.
  * @param {string} accessCode The IT2Me access code. Blank for Me2Me.
  * @param {function(boolean, function(string): void): void} fetchPin
  *     Called by Me2Me connections when a PIN needs to be obtained
@@ -46,17 +45,14 @@
  *     paired secret for this client, as issued by the host.
  * @constructor
  */
-remoting.ClientSession = function(clientJid, accessCode, fetchPin,
-                                  fetchThirdPartyToken, authenticationMethods,
+remoting.ClientSession = function(accessCode, fetchPin, fetchThirdPartyToken,
+                                  authenticationMethods,
                                   hostId, hostJid, hostPublicKey, mode,
                                   clientPairingId, clientPairedSecret) {
   /** @private */
   this.state_ = remoting.ClientSession.State.CREATED;
 
   /** @private */
-  this.clientJid_ = clientJid;
-
-  /** @private */
   this.error_ = remoting.Error.NONE;
 
   /** @private */
@@ -459,7 +455,7 @@
       this.onDesktopSizeChanged_.bind(this);
   this.plugin_.onSetCapabilitiesHandler =
       this.onSetCapabilities_.bind(this);
-  this.connectPluginToWcs_();
+  this.initiateConnection_();
 };
 
 /**
@@ -747,14 +743,37 @@
   remoting.wcsSandbox.sendIq(msg);
 };
 
+remoting.ClientSession.prototype.initiateConnection_ = function() {
+  /** @type {remoting.ClientSession} */
+  var that = this;
+
+  remoting.wcsSandbox.connect(onWcsConnected, this.resetWithError_.bind(this));
+
+  /** @param {string} localJid Local JID. */
+  function onWcsConnected(localJid) {
+    that.connectPluginToWcs_(localJid);
+    that.getSharedSecret_(onSharedSecretReceived.bind(null, localJid));
+  }
+
+  /** @param {string} localJid Local JID.
+    * @param {string} sharedSecret Shared secret. */
+  function onSharedSecretReceived(localJid, sharedSecret) {
+    that.plugin_.connect(
+        that.hostJid_, that.hostPublicKey_, localJid, sharedSecret,
+        that.authenticationMethods_, that.hostId_, that.clientPairingId_,
+        that.clientPairedSecret_);
+  };
+}
+
 /**
  * Connects the plugin to WCS.
  *
  * @private
+ * @param {string} localJid Local JID.
  * @return {void} Nothing.
  */
-remoting.ClientSession.prototype.connectPluginToWcs_ = function() {
-  remoting.formatIq.setJids(this.clientJid_, this.hostJid_);
+remoting.ClientSession.prototype.connectPluginToWcs_ = function(localJid) {
+  remoting.formatIq.setJids(localJid, this.hostJid_);
   var forwardIq = this.plugin_.onIncomingIq.bind(this.plugin_);
   /** @param {string} stanza The IQ stanza received. */
   var onIncomingIq = function(stanza) {
@@ -777,7 +796,16 @@
     forwardIq(stanza);
   };
   remoting.wcsSandbox.setOnIq(onIncomingIq);
+}
 
+/**
+ * Gets shared secret to be used for connection.
+ *
+ * @param {function(string)} callback Callback called with the shared secret.
+ * @return {void} Nothing.
+ * @private
+ */
+remoting.ClientSession.prototype.getSharedSecret_ = function(callback) {
   /** @type remoting.ClientSession */
   var that = this;
   if (this.plugin_.hasFeature(remoting.ClientPlugin.Feature.THIRD_PARTY_AUTH)) {
@@ -787,11 +815,11 @@
           tokenUrl, hostPublicKey, scope,
           that.plugin_.onThirdPartyTokenFetched.bind(that.plugin_));
     };
-    that.plugin_.fetchThirdPartyTokenHandler = fetchThirdPartyToken;
+    this.plugin_.fetchThirdPartyTokenHandler = fetchThirdPartyToken;
   }
   if (this.accessCode_) {
     // Shared secret was already supplied before connecting (It2Me case).
-    this.connectToHost_(this.accessCode_);
+    callback(this.accessCode_);
   } else if (this.plugin_.hasFeature(
       remoting.ClientPlugin.Feature.ASYNC_PIN)) {
     // Plugin supports asynchronously asking for the PIN.
@@ -802,28 +830,15 @@
                      that.plugin_.onPinFetched.bind(that.plugin_));
     };
     this.plugin_.fetchPinHandler = fetchPin;
-    this.connectToHost_('');
+    callback('');
   } else {
     // Clients that don't support asking for a PIN asynchronously also don't
     // support pairing, so request the PIN now without offering to remember it.
-    this.fetchPin_(false, this.connectToHost_.bind(this));
+    this.fetchPin_(false, callback);
   }
 };
 
 /**
- * Connects to the host.
- *
- * @param {string} sharedSecret Shared secret for SPAKE negotiation.
- * @return {void} Nothing.
- * @private
- */
-remoting.ClientSession.prototype.connectToHost_ = function(sharedSecret) {
-  this.plugin_.connect(this.hostJid_, this.hostPublicKey_, this.clientJid_,
-                       sharedSecret, this.authenticationMethods_, this.hostId_,
-                       this.clientPairingId_, this.clientPairedSecret_);
-};
-
-/**
  * Callback that the plugin invokes to indicate that the connection
  * status has changed.
  *
diff --git a/remoting/webapp/session_connector.js b/remoting/webapp/session_connector.js
index 1264e5c..077b292 100644
--- a/remoting/webapp/session_connector.js
+++ b/remoting/webapp/session_connector.js
@@ -49,19 +49,8 @@
    */
   this.connectionMode_ = remoting.ClientSession.Mode.ME2ME;
 
-  /**
-   * A timer that polls for an updated access token.
-   *
-   * @type {number}
-   * @private
-   */
-  this.wcsAccessTokenRefreshTimer_ = 0;
-
   // Initialize/declare per-connection state.
   this.reset();
-
-  // Pre-load WCS to improve connection time.
-  remoting.identity.callWithToken(this.loadWcs_.bind(this), this.onError_);
 };
 
 /**
@@ -244,7 +233,7 @@
   this.connectionMode_ = remoting.ClientSession.Mode.ME2ME;
   this.refreshHostJidIfOffline_ = refreshHostJidIfOffline;
   this.updatePairingInfo(clientPairingId, clientPairedSecret);
-  this.createSessionIfReady_();
+  this.createSession_();
 };
 
 /**
@@ -365,7 +354,7 @@
       this.hostJid_ = host.data.jabberId;
       this.hostPublicKey_ = host.data.publicKey;
       this.hostDisplayName_ = this.hostJid_.split('/')[0];
-      this.createSessionIfReady_();
+      this.createSession_();
       return;
     } else {
       console.error('Invalid "support-hosts" response from server.');
@@ -376,42 +365,9 @@
 };
 
 /**
- * Load the WCS driver script.
- *
- * @param {string} token An OAuth2 access token.
- * @return {void} Nothing.
- * @private
+ * Creates ClientSession object.
  */
-remoting.SessionConnector.prototype.loadWcs_ = function(token) {
-  remoting.wcsSandbox.setOnLocalJid(this.onLocalJid_.bind(this));
-  remoting.wcsSandbox.setOnError(this.onError_);
-  remoting.wcsSandbox.setAccessToken(token);
-  this.startAccessTokenRefreshTimer_();
-};
-
-/**
- * Continue an IT2Me or Me2Me connection once WCS has been loaded.
- *
- * @param {string} clientJid The full JID of the WCS client.
- * @return {void} Nothing.
- * @private
- */
-remoting.SessionConnector.prototype.onLocalJid_ = function(clientJid) {
-  this.clientJid_ = clientJid;
-  this.createSessionIfReady_();
-};
-
-/**
- * If both the client and host JIDs are available, create a session and connect.
- *
- * @return {void} Nothing.
- * @private
- */
-remoting.SessionConnector.prototype.createSessionIfReady_ = function() {
-  if (!this.clientJid_ || !this.hostJid_) {
-    return;
-  }
-
+remoting.SessionConnector.prototype.createSession_ = function() {
   // In some circumstances, the WCS <iframe> can get reloaded, which results
   // in a new clientJid and a new callback. In this case, remove the old
   // client plugin before instantiating a new one.
@@ -423,10 +379,9 @@
   var authenticationMethods =
      'third_party,spake2_pair,spake2_hmac,spake2_plain';
   this.clientSession_ = new remoting.ClientSession(
-      this.clientJid_, this.passPhrase_, this.fetchPin_,
-      this.fetchThirdPartyToken_, authenticationMethods, this.hostId_,
-      this.hostJid_, this.hostPublicKey_, this.connectionMode_,
-      this.clientPairingId_, this.clientPairedSecret_);
+      this.passPhrase_, this.fetchPin_, this.fetchThirdPartyToken_,
+      authenticationMethods, this.hostId_, this.hostJid_, this.hostPublicKey_,
+      this.connectionMode_, this.clientPairingId_, this.clientPairedSecret_);
   this.clientSession_.logHostOfflineErrors(!this.refreshHostJidIfOffline_);
   this.clientSession_.setOnStateChange(this.onStateChange_.bind(this));
   this.clientSession_.createPluginAndConnect(this.pluginParent_);
@@ -520,35 +475,6 @@
 };
 
 /**
- * Start a timer to periodically refresh the access token used by WCS. Access
- * tokens have a limited lifespan, and since the WCS driver runs in a sandbox,
- * it can't obtain a new one directly.
- *
- * @return {void} Nothing.
- * @private
- */
-remoting.SessionConnector.prototype.startAccessTokenRefreshTimer_ = function() {
-  if (this.wcsAccessTokenRefreshTimer_ != 0) {
-    return;
-  }
-
-  /** @type {remoting.SessionConnector} */
-  var that = this;
-  var refreshAccessToken = function() {
-    remoting.identity.callWithToken(
-        remoting.wcsSandbox.setAccessToken.bind(remoting.wcsSandbox),
-        that.onError_);
-  };
-  /**
-   * A timer that polls for an updated access token.
-   * @type {number}
-   * @private
-   */
-  this.wcsAccessTokenRefreshTimer_ = setInterval(refreshAccessToken,
-                                                 60 * 1000);
-}
-
-/**
  * @param {number} error An HTTP error code returned by the support-hosts
  *     endpoint.
  * @return {remoting.Error} The equivalent remoting.Error code.
diff --git a/remoting/webapp/wcs_sandbox_container.js b/remoting/webapp/wcs_sandbox_container.js
index 04260a7..a592625 100644
--- a/remoting/webapp/wcs_sandbox_container.js
+++ b/remoting/webapp/wcs_sandbox_container.js
@@ -24,7 +24,7 @@
   this.sandbox_ = sandbox;
   /** @type {?function(string):void}
     * @private */
-  this.onLocalJid_ = null;
+  this.onConnected_ = null;
   /** @type {function(remoting.Error):void}
     * @private */
   this.onError_ = function(error) {};
@@ -34,6 +34,11 @@
   /** @type {Object.<number, XMLHttpRequest>}
     * @private */
   this.pendingXhrs_ = {};
+  /** @private */
+  this.localJid_ = '';
+
+  /** @private */
+  this.accessTokenRefreshTimerStarted_ = false;
 
   window.addEventListener('message', this.onMessage_.bind(this), false);
 
@@ -46,23 +51,20 @@
 };
 
 /**
- * @param {?function(string):void} onLocalJid Callback invoked with the client
- *     JID when the WCS code has loaded. Note that this may be called more than
- *     once (potentially with a different JID) if the WCS node is reloaded for
- *     any reason.
+ * @param {function(string):void} onConnected Callback to be called when WCS is
+ *     connected. May be called synchronously if WCS is already connected.
+ * @param {function(remoting.Error):void} onError called in case of an error.
  * @return {void} Nothing.
  */
-remoting.WcsSandboxContainer.prototype.setOnLocalJid = function(onLocalJid) {
-  this.onLocalJid_ = onLocalJid;
-};
-
-/**
- * @param {function(remoting.Error):void} onError Callback invoked if the WCS
- *     code cannot be loaded.
- * @return {void} Nothing.
- */
-remoting.WcsSandboxContainer.prototype.setOnError = function(onError) {
+remoting.WcsSandboxContainer.prototype.connect = function(
+    onConnected, onError) {
   this.onError_ = onError;
+  this.ensureAccessTokenRefreshTimer_();
+  if (this.localJid_) {
+    onConnected(this.localJid_);
+  } else {
+    this.onConnected_ = onConnected;
+  }
 };
 
 /**
@@ -75,10 +77,36 @@
 };
 
 /**
+ * Refreshes access token and starts a timer to update it periodically.
+ *
+ * @private
+ */
+remoting.WcsSandboxContainer.prototype.ensureAccessTokenRefreshTimer_ =
+    function() {
+  if (this.accessTokenRefreshTimerStarted_) {
+    return;
+  }
+
+  this.refreshAccessToken_();
+  setInterval(this.refreshAccessToken_.bind(this), 60 * 1000);
+  this.accessTokenRefreshTimerStarted_ = true;
+}
+
+/**
+ * @private
+ * @return {void} Nothing.
+ */
+remoting.WcsSandboxContainer.prototype.refreshAccessToken_ = function() {
+  remoting.identity.callWithToken(
+      this.setAccessToken_.bind(this), this.onError_);
+};
+
+/**
+ * @private
  * @param {string} token The access token.
  * @return {void}
  */
-remoting.WcsSandboxContainer.prototype.setAccessToken = function(token) {
+remoting.WcsSandboxContainer.prototype.setAccessToken_ = function(token) {
   var message = {
     'command': 'setAccessToken',
     'token': token
@@ -113,8 +141,12 @@
         console.error('onReady: missing localJid');
         break;
       }
-      if (this.onLocalJid_)
-        this.onLocalJid_(localJid);
+      this.localJid_ = localJid;
+      if (this.onConnected_) {
+        var callback = this.onConnected_;
+        this.onConnected_ = null;
+        callback(localJid);
+      }
       break;
 
     case 'onError':
diff --git a/sandbox/linux/services/broker_process.cc b/sandbox/linux/services/broker_process.cc
index 7999e77..2922207 100644
--- a/sandbox/linux/services/broker_process.cc
+++ b/sandbox/linux/services/broker_process.cc
@@ -43,9 +43,6 @@
 // over unix sockets just fine, so a receiver that would (incorrectly) look at
 // O_CLOEXEC instead of FD_CLOEXEC may be tricked in thinking that the file
 // descriptor will or won't be closed on execve().
-// Since we have to account for buggy userland (see crbug.com/237283), we will
-// open(2) the file with O_CLOEXEC in the broker process if necessary, in
-// addition to calling recvmsg(2) with MSG_CMSG_CLOEXEC.
 static const int kCurrentProcessOpenFlagsMask = O_CLOEXEC;
 
 // Check whether |requested_filename| is in |allowed_file_names|.
@@ -81,7 +78,7 @@
 // we're ok to allow in the broker.
 // I.e. here is where we wouldn't add O_RESET_FILE_SYSTEM.
 bool IsAllowedOpenFlags(int flags) {
-  // First, check the access mode
+  // First, check the access mode.
   const int access_mode = flags & O_ACCMODE;
   if (access_mode != O_RDONLY && access_mode != O_WRONLY &&
       access_mode != O_RDWR) {
@@ -95,13 +92,8 @@
 
   // Some flags affect the behavior of the current process. We don't support
   // them and don't allow them for now.
-  if (flags & kCurrentProcessOpenFlagsMask) {
-    // We make an exception for O_CLOEXEC. Buggy userland could check for
-    // O_CLOEXEC and the only way to set it is to originally open with this
-    // flag. See the comment around kCurrentProcessOpenFlagsMask.
-    if (!(flags & O_CLOEXEC))
-      return false;
-  }
+  if (flags & kCurrentProcessOpenFlagsMask)
+    return false;
 
   // Now check that all the flags are known to us.
   const int creation_and_status_flags = flags & ~O_ACCMODE;
@@ -217,6 +209,7 @@
     // this code if other flags are added.
     RAW_CHECK(kCurrentProcessOpenFlagsMask == O_CLOEXEC);
     recvmsg_flags |= MSG_CMSG_CLOEXEC;
+    flags &= ~O_CLOEXEC;
   }
 
   // There is no point in forwarding a request that we know will be denied.
diff --git a/sandbox/linux/services/broker_process_unittest.cc b/sandbox/linux/services/broker_process_unittest.cc
index c0393ed..8ff4e00 100644
--- a/sandbox/linux/services/broker_process_unittest.cc
+++ b/sandbox/linux/services/broker_process_unittest.cc
@@ -405,14 +405,6 @@
   // Important: use F_GETFD, not F_GETFL. The O_CLOEXEC flag in F_GETFL
   // is actually not used by the kernel.
   ASSERT_TRUE(FD_CLOEXEC & ret);
-
-  // There is buggy userland code that can check for O_CLOEXEC with fcntl(2)
-  // even though it doesn't mean anything. We need to support this case.
-  // See crbug.com/237283.
-  ret = fcntl(fd, F_GETFL);
-  ASSERT_NE(-1, ret);
-  ASSERT_TRUE(O_CLOEXEC & ret);
-
   ASSERT_EQ(0, close(fd));
 
   fd = open_broker.Open(kCpuInfo, O_RDONLY | O_NONBLOCK);
diff --git a/sandbox/win/src/sandbox_nt_util.h b/sandbox/win/src/sandbox_nt_util.h
index 40f261b..e5d45fa 100644
--- a/sandbox/win/src/sandbox_nt_util.h
+++ b/sandbox/win/src/sandbox_nt_util.h
@@ -5,6 +5,8 @@
 #ifndef SANDBOX_SRC_SANDBOX_NT_UTIL_H_
 #define SANDBOX_SRC_SANDBOX_NT_UTIL_H_
 
+#include <intrin.h>
+
 #include "base/basictypes.h"
 #include "sandbox/win/src/nt_internals.h"
 #include "sandbox/win/src/sandbox_nt_types.h"
@@ -47,6 +49,11 @@
 
 namespace sandbox {
 
+#if defined(_M_X64)
+#pragma intrinsic(_InterlockedCompareExchange)
+#pragma intrinsic(_InterlockedCompareExchangePointer)
+
+#elif defined(_M_IX86)
 extern "C" long _InterlockedCompareExchange(long volatile* destination,
                                             long exchange, long comperand);
 
@@ -64,6 +71,11 @@
   return reinterpret_cast<void*>(static_cast<size_t>(ret));
 }
 
+#else
+#error Architecture not supported.
+
+#endif
+
 // Returns a pointer to the IPC shared memory.
 void* GetGlobalIPCMemory();
 
diff --git a/skia/OWNERS b/skia/OWNERS
index fe892fd..4f48079 100644
--- a/skia/OWNERS
+++ b/skia/OWNERS
@@ -5,7 +5,7 @@
 djsollen@google.com
 edisonn@google.com
 epoger@google.com
-fmalita@google.com
+fmalita@chromium.org
 junov@chromium.org
 jvanverth@google.com
 reed@google.com
diff --git a/skia/ext/SkDiscardableMemory_chrome.cc b/skia/ext/SkDiscardableMemory_chrome.cc
new file mode 100644
index 0000000..2e78788
--- /dev/null
+++ b/skia/ext/SkDiscardableMemory_chrome.cc
@@ -0,0 +1,49 @@
+// 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 "SkDiscardableMemory_chrome.h"
+
+SkDiscardableMemoryChrome::SkDiscardableMemoryChrome()
+    : discardable_(new base::DiscardableMemory()) {
+}
+
+SkDiscardableMemoryChrome::~SkDiscardableMemoryChrome() {
+}
+
+bool SkDiscardableMemoryChrome::lock() {
+  base::LockDiscardableMemoryStatus status = discardable_->Lock();
+  switch (status) {
+    case base::DISCARDABLE_MEMORY_SUCCESS:
+      return true;
+    case base::DISCARDABLE_MEMORY_PURGED:
+      discardable_->Unlock();
+      return false;
+    default:
+      discardable_.reset();
+      return false;
+  }
+}
+
+void* SkDiscardableMemoryChrome::data() {
+  return discardable_->Memory();
+}
+
+void SkDiscardableMemoryChrome::unlock() {
+  discardable_->Unlock();
+}
+
+bool SkDiscardableMemoryChrome::InitializeAndLock(size_t bytes) {
+  return discardable_->InitializeAndLock(bytes);
+}
+
+SkDiscardableMemory* SkDiscardableMemory::Create(size_t bytes) {
+  if (!base::DiscardableMemory::Supported()) {
+    return NULL;
+  }
+  scoped_ptr<SkDiscardableMemoryChrome> discardable(
+      new SkDiscardableMemoryChrome());
+  if (discardable->InitializeAndLock(bytes))
+    return discardable.release();
+  return NULL;
+}
diff --git a/skia/ext/SkDiscardableMemory_chrome.h b/skia/ext/SkDiscardableMemory_chrome.h
new file mode 100644
index 0000000..13dfef8
--- /dev/null
+++ b/skia/ext/SkDiscardableMemory_chrome.h
@@ -0,0 +1,33 @@
+// 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 SKIA_EXT_SK_DISCARDABLE_MEMORY_CHROME_H_
+#define SKIA_EXT_SK_DISCARDABLE_MEMORY_CHROME_H_
+
+#include "base/memory/discardable_memory.h"
+#include "base/memory/scoped_ptr.h"
+#include "third_party/skia/src/core/SkDiscardableMemory.h"
+
+// This class implements the SkDiscardableMemory interface using
+// base::DiscardableMemory.
+class SK_API SkDiscardableMemoryChrome : public SkDiscardableMemory {
+public:
+  SkDiscardableMemoryChrome();
+  virtual ~SkDiscardableMemoryChrome();
+
+  // Initialize the SkDiscardableMemoryChrome object and lock the memory.
+  // Returns true on success. No memory is allocated if this call returns
+  // false. This call should only be called once.
+  bool InitializeAndLock(size_t bytes);
+
+  // Implementation of SkDiscardableMemory interface.
+  virtual bool lock() OVERRIDE;
+  virtual void* data() OVERRIDE;
+  virtual void unlock() OVERRIDE;
+
+private:
+  scoped_ptr<base::DiscardableMemory> discardable_;
+};
+
+#endif  // SKIA_EXT_SK_DISCARDABLE_MEMORY_CHROME_H_
diff --git a/skia/ext/analysis_canvas.cc b/skia/ext/analysis_canvas.cc
index d710fb6..9db42f1 100644
--- a/skia/ext/analysis_canvas.cc
+++ b/skia/ext/analysis_canvas.cc
@@ -283,7 +283,7 @@
 }
 
 void AnalysisDevice::drawDevice(const SkDraw& draw,
-                                SkDevice* device,
+                                SkBaseDevice* device,
                                 int x,
                                 int y,
                                 const SkPaint& paint) {
diff --git a/skia/ext/analysis_canvas.h b/skia/ext/analysis_canvas.h
index b257b9f..78d72ff 100644
--- a/skia/ext/analysis_canvas.h
+++ b/skia/ext/analysis_canvas.h
@@ -6,8 +6,8 @@
 #define SKIA_EXT_ANALYSIS_CANVAS_H_
 
 #include "base/compiler_specific.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
 #include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkDevice.h"
 #include "third_party/skia/include/core/SkPicture.h"
 
 namespace skia {
@@ -58,7 +58,10 @@
   int force_not_transparent_stack_level_;
 };
 
-class SK_API AnalysisDevice : public SkDevice {
+// TODO(robertphillips): Once Skia's SkBaseDevice API is refactored to
+// remove the bitmap-specific entry points, it might make sense for this
+// to be derived from SkBaseDevice (rather than SkBitmapDevice)
+class SK_API AnalysisDevice : public SkBitmapDevice {
  public:
   AnalysisDevice(const SkBitmap& bitmap);
   virtual ~AnalysisDevice();
@@ -70,7 +73,7 @@
   void SetForceNotTransparent(bool flag);
 
  protected:
-  // SkDevice overrides.
+  // SkBaseDevice overrides.
   virtual void clear(SkColor color) OVERRIDE;
   virtual void drawPaint(const SkDraw& draw, const SkPaint& paint) OVERRIDE;
   virtual void drawPoints(const SkDraw& draw,
@@ -143,13 +146,13 @@
                             int index_count,
                             const SkPaint& paint) OVERRIDE;
   virtual void drawDevice(const SkDraw& draw,
-                          SkDevice* device,
+                          SkBaseDevice* device,
                           int x,
                           int y,
                           const SkPaint& paint) OVERRIDE;
 
  private:
-  typedef SkDevice INHERITED;
+  typedef SkBitmapDevice INHERITED;
 
   bool is_forced_not_solid_;
   bool is_forced_not_transparent_;
diff --git a/skia/ext/benchmarking_canvas.cc b/skia/ext/benchmarking_canvas.cc
index fbd2eb1..557827f 100644
--- a/skia/ext/benchmarking_canvas.cc
+++ b/skia/ext/benchmarking_canvas.cc
@@ -6,7 +6,7 @@
 #include "base/logging.h"
 #include "base/time/time.h"
 #include "skia/ext/benchmarking_canvas.h"
-#include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
 #include "third_party/skia/include/utils/SkProxyCanvas.h"
 
 namespace skia {
@@ -25,8 +25,8 @@
 public:
   TimingCanvas(int width, int height, const BenchmarkingCanvas* track_canvas)
       : tracking_canvas_(track_canvas) {
-    skia::RefPtr<SkDevice> device = skia::AdoptRef(
-        SkNEW_ARGS(SkDevice, (SkBitmap::kARGB_8888_Config, width, height)));
+    skia::RefPtr<SkBaseDevice> device = skia::AdoptRef(
+        SkNEW_ARGS(SkBitmapDevice, (SkBitmap::kARGB_8888_Config, width, height)));
     canvas_ = skia::AdoptRef(SkNEW_ARGS(SkCanvas, (device.get())));
 
     setProxy(canvas_.get());
diff --git a/skia/ext/bitmap_platform_device_android.cc b/skia/ext/bitmap_platform_device_android.cc
index 8a29586..32f447f 100644
--- a/skia/ext/bitmap_platform_device_android.cc
+++ b/skia/ext/bitmap_platform_device_android.cc
@@ -46,14 +46,14 @@
 }
 
 BitmapPlatformDevice::BitmapPlatformDevice(const SkBitmap& bitmap)
-    : SkDevice(bitmap) {
+    : SkBitmapDevice(bitmap) {
   SetPlatformDevice(this, this);
 }
 
 BitmapPlatformDevice::~BitmapPlatformDevice() {
 }
 
-SkDevice* BitmapPlatformDevice::onCreateCompatibleDevice(
+SkBaseDevice* BitmapPlatformDevice::onCreateCompatibleDevice(
     SkBitmap::Config config, int width, int height, bool isOpaque,
     Usage /*usage*/) {
   SkASSERT(config == SkBitmap::kARGB_8888_Config);
@@ -76,7 +76,7 @@
 
 SkCanvas* CreatePlatformCanvas(int width, int height, bool is_opaque,
                                uint8_t* data, OnFailureType failureType) {
-  skia::RefPtr<SkDevice> dev = skia::AdoptRef(
+  skia::RefPtr<SkBaseDevice> dev = skia::AdoptRef(
       BitmapPlatformDevice::Create(width, height, is_opaque, data));
   return CreateCanvas(dev, failureType);
 }
diff --git a/skia/ext/bitmap_platform_device_android.h b/skia/ext/bitmap_platform_device_android.h
index b5755cb..4e1735f 100644
--- a/skia/ext/bitmap_platform_device_android.h
+++ b/skia/ext/bitmap_platform_device_android.h
@@ -12,13 +12,13 @@
 namespace skia {
 
 // -----------------------------------------------------------------------------
-// For now we just use SkBitmap for SkDevice
+// For now we just use SkBitmap for SkBitmapDevice
 //
 // This is all quite ok for test_shell. In the future we will want to use
 // shared memory between the renderer and the main process at least. In this
 // case we'll probably create the buffer from a precreated region of memory.
 // -----------------------------------------------------------------------------
-class BitmapPlatformDevice : public SkDevice, public PlatformDevice {
+class BitmapPlatformDevice : public SkBitmapDevice, public PlatformDevice {
  public:
   // Construct a BitmapPlatformDevice. |is_opaque| should be set if the caller
   // knows the bitmap will be completely opaque and allows some optimizations.
@@ -47,9 +47,9 @@
                                    const PlatformRect* src_rect) OVERRIDE;
 
  protected:
-  virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config, int width,
-                                             int height, bool isOpaque,
-                                             Usage usage) OVERRIDE;
+  virtual SkBaseDevice* onCreateCompatibleDevice(SkBitmap::Config, int width,
+                                                 int height, bool isOpaque,
+                                                 Usage usage) OVERRIDE;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(BitmapPlatformDevice);
diff --git a/skia/ext/bitmap_platform_device_linux.cc b/skia/ext/bitmap_platform_device_linux.cc
index c9a0214..af26806 100644
--- a/skia/ext/bitmap_platform_device_linux.cc
+++ b/skia/ext/bitmap_platform_device_linux.cc
@@ -133,11 +133,11 @@
 }
 
 // The device will own the bitmap, which corresponds to also owning the pixel
-// data. Therefore, we do not transfer ownership to the SkDevice's bitmap.
+// data. Therefore, we do not transfer ownership to the SkBitmapDevice's bitmap.
 BitmapPlatformDevice::BitmapPlatformDevice(
     const SkBitmap& bitmap,
     BitmapPlatformDeviceData* data)
-    : SkDevice(bitmap),
+    : SkBitmapDevice(bitmap),
       data_(data) {
   SetPlatformDevice(this, this);
 }
@@ -145,7 +145,7 @@
 BitmapPlatformDevice::~BitmapPlatformDevice() {
 }
 
-SkDevice* BitmapPlatformDevice::onCreateCompatibleDevice(
+SkBaseDevice* BitmapPlatformDevice::onCreateCompatibleDevice(
     SkBitmap::Config config, int width, int height, bool isOpaque,
     Usage /*usage*/) {
   SkASSERT(config == SkBitmap::kARGB_8888_Config);
@@ -180,7 +180,7 @@
 
 SkCanvas* CreatePlatformCanvas(int width, int height, bool is_opaque,
                                uint8_t* data, OnFailureType failureType) {
-  skia::RefPtr<SkDevice> dev = skia::AdoptRef(
+  skia::RefPtr<SkBaseDevice> dev = skia::AdoptRef(
       BitmapPlatformDevice::Create(width, height, is_opaque, data));
   return CreateCanvas(dev, failureType);
 }
diff --git a/skia/ext/bitmap_platform_device_linux.h b/skia/ext/bitmap_platform_device_linux.h
index e1f9a75..048092e 100644
--- a/skia/ext/bitmap_platform_device_linux.h
+++ b/skia/ext/bitmap_platform_device_linux.h
@@ -56,7 +56,7 @@
 // shared memory between the renderer and the main process at least. In this
 // case we'll probably create the buffer from a precreated region of memory.
 // -----------------------------------------------------------------------------
-class BitmapPlatformDevice : public SkDevice, public PlatformDevice {
+class BitmapPlatformDevice : public SkBitmapDevice, public PlatformDevice {
   // A reference counted cairo surface
   class BitmapPlatformDeviceData;
 
@@ -86,7 +86,7 @@
   static BitmapPlatformDevice* Create(int width, int height, bool is_opaque,
                                       uint8_t* data);
 
-  // Overridden from SkDevice:
+  // Overridden from SkBaseDevice:
   virtual void setMatrixClip(const SkMatrix& transform, const SkRegion& region,
                              const SkClipStack&) OVERRIDE;
 
@@ -96,9 +96,9 @@
                                    const PlatformRect* src_rect) OVERRIDE;
 
  protected:
-  virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config, int width,
-                                             int height, bool isOpaque,
-                                             Usage usage) OVERRIDE;
+  virtual SkBaseDevice* onCreateCompatibleDevice(SkBitmap::Config, int width,
+                                                 int height, bool isOpaque,
+                                                 Usage usage) OVERRIDE;
 
  private:
   static BitmapPlatformDevice* Create(int width, int height, bool is_opaque,
diff --git a/skia/ext/bitmap_platform_device_mac.cc b/skia/ext/bitmap_platform_device_mac.cc
index b7b05e5..66298b2 100644
--- a/skia/ext/bitmap_platform_device_mac.cc
+++ b/skia/ext/bitmap_platform_device_mac.cc
@@ -125,15 +125,17 @@
     return NULL;
 
   SkBitmap bitmap;
+  // TODO: verify that the CG Context's pixels will have tight rowbytes or pass in the correct
+  // rowbytes for the case when context != NULL.
   bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
-  if (bitmap.allocPixels() != true)
-    return NULL;
 
-  void* data = NULL;
+  void* data;
   if (context) {
     data = CGBitmapContextGetData(context);
     bitmap.setPixels(data);
   } else {
+    if (!bitmap.allocPixels())
+      return NULL;
     data = bitmap.getPixels();
   }
 
@@ -193,10 +195,10 @@
 }
 
 // The device will own the bitmap, which corresponds to also owning the pixel
-// data. Therefore, we do not transfer ownership to the SkDevice's bitmap.
+// data. Therefore, we do not transfer ownership to the SkBitmapDevice's bitmap.
 BitmapPlatformDevice::BitmapPlatformDevice(
     const skia::RefPtr<BitmapPlatformDeviceData>& data, const SkBitmap& bitmap)
-    : SkDevice(bitmap),
+    : SkBitmapDevice(bitmap),
       data_(data) {
   SetPlatformDevice(this, this);
 }
@@ -246,17 +248,13 @@
     data_->ReleaseBitmapContext();
 }
 
-const SkBitmap& BitmapPlatformDevice::onAccessBitmap(SkBitmap* bitmap) {
-  // Not needed in CoreGraphics
-  return *bitmap;
-}
-
-SkDevice* BitmapPlatformDevice::onCreateCompatibleDevice(
+SkBaseDevice* BitmapPlatformDevice::onCreateCompatibleDevice(
     SkBitmap::Config config, int width, int height, bool isOpaque,
     Usage /*usage*/) {
   SkASSERT(config == SkBitmap::kARGB_8888_Config);
-  SkDevice* bitmap_device = BitmapPlatformDevice::CreateAndClear(width, height,
-                                                                 isOpaque);
+  SkBaseDevice* bitmap_device = BitmapPlatformDevice::CreateAndClear(width, 
+                                                                     height,
+                                                                     isOpaque);
   return bitmap_device;
 }
 
@@ -264,14 +262,14 @@
 
 SkCanvas* CreatePlatformCanvas(CGContextRef ctx, int width, int height,
                                bool is_opaque, OnFailureType failureType) {
-  skia::RefPtr<SkDevice> dev = skia::AdoptRef(
+  skia::RefPtr<SkBaseDevice> dev = skia::AdoptRef(
       BitmapPlatformDevice::Create(ctx, width, height, is_opaque));
   return CreateCanvas(dev, failureType);
 }
 
 SkCanvas* CreatePlatformCanvas(int width, int height, bool is_opaque,
                                uint8_t* data, OnFailureType failureType) {
-  skia::RefPtr<SkDevice> dev = skia::AdoptRef(
+  skia::RefPtr<SkBaseDevice> dev = skia::AdoptRef(
       BitmapPlatformDevice::CreateWithData(data, width, height, is_opaque));
   return CreateCanvas(dev, failureType);
 }
diff --git a/skia/ext/bitmap_platform_device_mac.h b/skia/ext/bitmap_platform_device_mac.h
index a1c5894..07b0084 100644
--- a/skia/ext/bitmap_platform_device_mac.h
+++ b/skia/ext/bitmap_platform_device_mac.h
@@ -26,7 +26,7 @@
 // For us, that other bitmap will become invalid as soon as the device becomes
 // invalid, which may lead to subtle bugs. Therefore, DO NOT ASSIGN THE
 // DEVICE'S PIXEL DATA TO ANOTHER BITMAP, make sure you copy instead.
-class SK_API BitmapPlatformDevice : public SkDevice, public PlatformDevice {
+class SK_API BitmapPlatformDevice : public SkBitmapDevice, public PlatformDevice {
  public:
   // Creates a BitmapPlatformDevice instance. |is_opaque| should be set if the
   // caller knows the bitmap will be completely opaque and allows some
@@ -55,7 +55,7 @@
   virtual void DrawToNativeContext(CGContextRef context, int x, int y,
                                    const CGRect* src_rect) OVERRIDE;
 
-  // SkDevice overrides
+  // SkBaseDevice overrides
   virtual void setMatrixClip(const SkMatrix& transform, const SkRegion& region,
                              const SkClipStack&) OVERRIDE;
 
@@ -68,14 +68,9 @@
   BitmapPlatformDevice(const skia::RefPtr<BitmapPlatformDeviceData>& data,
                        const SkBitmap& bitmap);
 
-  // Flushes the CoreGraphics context so that the pixel data can be accessed
-  // directly by Skia. Overridden from SkDevice, this is called when Skia
-  // starts accessing pixel data.
-  virtual const SkBitmap& onAccessBitmap(SkBitmap*) OVERRIDE;
-
-  virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config, int width,
-                                             int height, bool isOpaque,
-                                             Usage usage) OVERRIDE;
+  virtual SkBaseDevice* onCreateCompatibleDevice(SkBitmap::Config, int width,
+                                                 int height, bool isOpaque,
+                                                 Usage usage) OVERRIDE;
 
   // Data associated with this device, guaranteed non-null.
   skia::RefPtr<BitmapPlatformDeviceData> data_;
diff --git a/skia/ext/bitmap_platform_device_win.cc b/skia/ext/bitmap_platform_device_win.cc
index 201866a..0088403 100644
--- a/skia/ext/bitmap_platform_device_win.cc
+++ b/skia/ext/bitmap_platform_device_win.cc
@@ -207,11 +207,11 @@
 }
 
 // The device will own the HBITMAP, which corresponds to also owning the pixel
-// data. Therefore, we do not transfer ownership to the SkDevice's bitmap.
+// data. Therefore, we do not transfer ownership to the SkBitmapDevice's bitmap.
 BitmapPlatformDevice::BitmapPlatformDevice(
     const skia::RefPtr<BitmapPlatformDeviceData>& data,
     const SkBitmap& bitmap)
-    : SkDevice(bitmap),
+    : SkBitmapDevice(bitmap),
       data_(data) {
   // The data object is already ref'ed for us by create().
   SkDEBUGCODE(begin_paint_count_ = 0);
@@ -293,15 +293,15 @@
     data_->ReleaseBitmapDC();
 }
 
-const SkBitmap& BitmapPlatformDevice::onAccessBitmap(SkBitmap* bitmap) {
+const SkBitmap& BitmapPlatformDevice::onAccessBitmap() {
   // FIXME(brettw) OPTIMIZATION: We should only flush if we know a GDI
   // operation has occurred on our DC.
   if (data_->IsBitmapDCCreated())
     GdiFlush();
-  return *bitmap;
+  return SkBitmapDevice::onAccessBitmap();
 }
 
-SkDevice* BitmapPlatformDevice::onCreateCompatibleDevice(
+SkBaseDevice* BitmapPlatformDevice::onCreateCompatibleDevice(
     SkBitmap::Config config, int width, int height, bool isOpaque, Usage) {
   SkASSERT(config == SkBitmap::kARGB_8888_Config);
   return BitmapPlatformDevice::CreateAndClear(width, height, isOpaque);
@@ -314,7 +314,7 @@
                                bool is_opaque,
                                HANDLE shared_section,
                                OnFailureType failureType) {
-  skia::RefPtr<SkDevice> dev = skia::AdoptRef(
+  skia::RefPtr<SkBaseDevice> dev = skia::AdoptRef(
       BitmapPlatformDevice::Create(width, height, is_opaque, shared_section));
   return CreateCanvas(dev, failureType);
 }
diff --git a/skia/ext/bitmap_platform_device_win.h b/skia/ext/bitmap_platform_device_win.h
index c896c0a..1261e51 100644
--- a/skia/ext/bitmap_platform_device_win.h
+++ b/skia/ext/bitmap_platform_device_win.h
@@ -26,7 +26,7 @@
 // For us, that other bitmap will become invalid as soon as the device becomes
 // invalid, which may lead to subtle bugs. Therefore, DO NOT ASSIGN THE
 // DEVICE'S PIXEL DATA TO ANOTHER BITMAP, make sure you copy instead.
-class SK_API BitmapPlatformDevice : public SkDevice, public PlatformDevice {
+class SK_API BitmapPlatformDevice : public SkBitmapDevice, public PlatformDevice {
  public:
   // Factory function. is_opaque should be set if the caller knows the bitmap
   // will be completely opaque and allows some optimizations.
@@ -60,19 +60,19 @@
                                    const RECT* src_rect) OVERRIDE;
 
   // Loads the given transform and clipping region into the HDC. This is
-  // overridden from SkDevice.
+  // overridden from SkBaseDevice.
   virtual void setMatrixClip(const SkMatrix& transform, const SkRegion& region,
                              const SkClipStack&) OVERRIDE;
 
  protected:
   // Flushes the Windows device context so that the pixel data can be accessed
-  // directly by Skia. Overridden from SkDevice, this is called when Skia
+  // directly by Skia. Overridden from SkBaseDevice, this is called when Skia
   // starts accessing pixel data.
-  virtual const SkBitmap& onAccessBitmap(SkBitmap* bitmap) OVERRIDE;
+  virtual const SkBitmap& onAccessBitmap() OVERRIDE;
 
-  virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config, int width,
-                                             int height, bool isOpaque,
-                                             Usage usage) OVERRIDE;
+  virtual SkBaseDevice* onCreateCompatibleDevice(SkBitmap::Config, int width,
+                                                 int height, bool isOpaque,
+                                                 Usage usage) OVERRIDE;
 
  private:
   // Reference counted data that can be shared between multiple devices. This
diff --git a/skia/ext/lazy_pixel_ref_utils.cc b/skia/ext/lazy_pixel_ref_utils.cc
index f0caef2..09f44a5 100644
--- a/skia/ext/lazy_pixel_ref_utils.cc
+++ b/skia/ext/lazy_pixel_ref_utils.cc
@@ -5,9 +5,9 @@
 #include "skia/ext/lazy_pixel_ref_utils.h"
 
 #include "skia/ext/lazy_pixel_ref.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkData.h"
-#include "third_party/skia/include/core/SkDevice.h"
 #include "third_party/skia/include/core/SkDraw.h"
 #include "third_party/skia/include/core/SkPixelRef.h"
 #include "third_party/skia/include/core/SkRRect.h"
@@ -44,10 +44,10 @@
   std::vector<LazyPixelRefUtils::PositionLazyPixelRef>* pixel_refs_;
 };
 
-class GatherPixelRefDevice : public SkDevice {
+class GatherPixelRefDevice : public SkBitmapDevice {
  public:
   GatherPixelRefDevice(const SkBitmap& bm, LazyPixelRefSet* lazy_pixel_ref_set)
-      : SkDevice(bm), lazy_pixel_ref_set_(lazy_pixel_ref_set) {}
+      : SkBitmapDevice(bm), lazy_pixel_ref_set_(lazy_pixel_ref_set) {}
 
   virtual void clear(SkColor color) SK_OVERRIDE {}
   virtual void writePixels(const SkBitmap& bitmap,
@@ -315,7 +315,7 @@
         draw, SkCanvas::kPolygon_PointMode, vertex_count, verts, paint);
   }
   virtual void drawDevice(const SkDraw&,
-                          SkDevice*,
+                          SkBaseDevice*,
                           int x,
                           int y,
                           const SkPaint&) SK_OVERRIDE {}
@@ -349,7 +349,7 @@
 
 class NoSaveLayerCanvas : public SkCanvas {
  public:
-  NoSaveLayerCanvas(SkDevice* device) : INHERITED(device) {}
+  NoSaveLayerCanvas(SkBaseDevice* device) : INHERITED(device) {}
 
   // Turn saveLayer() into save() for speed, should not affect correctness.
   virtual int saveLayer(const SkRect* bounds,
diff --git a/skia/ext/opacity_draw_filter.cc b/skia/ext/opacity_draw_filter.cc
new file mode 100644
index 0000000..7132b5d
--- /dev/null
+++ b/skia/ext/opacity_draw_filter.cc
@@ -0,0 +1,27 @@
+// 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 "skia/ext/opacity_draw_filter.h"
+#include "third_party/skia/include/core/SkPaint.h"
+
+namespace skia {
+
+OpacityDrawFilter::OpacityDrawFilter(float opacity,
+                                     bool disable_image_filtering)
+    : alpha_(SkScalarRound(opacity * 255)),
+      disable_image_filtering_(disable_image_filtering) {}
+
+OpacityDrawFilter::~OpacityDrawFilter() {}
+
+bool OpacityDrawFilter::filter(SkPaint* paint, Type type) {
+  if (alpha_ < 255)
+    paint->setAlpha(alpha_);
+  if (disable_image_filtering_)
+    paint->setFilterLevel(SkPaint::kNone_FilterLevel);
+  return true;
+}
+
+}  // namespace skia
+
+
diff --git a/skia/ext/opacity_draw_filter.h b/skia/ext/opacity_draw_filter.h
new file mode 100644
index 0000000..a2a686c
--- /dev/null
+++ b/skia/ext/opacity_draw_filter.h
@@ -0,0 +1,33 @@
+// 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 SKIA_EXT_OPACITY_DRAW_FILTER_H
+#define SKIA_EXT_OPACITY_DRAW_FILTER_H
+
+#include "base/values.h"
+#include "third_party/skia/include/core/SkDrawFilter.h"
+
+class SkPaint;
+
+namespace skia {
+
+// This filter allows setting an opacity on every draw call to a canvas, and to
+// disable image filtering. Note that the opacity setting is only correct in
+// very limited conditions: when there is only zero or one opaque, nonlayer
+// draw for every pixel in the surface.
+class SK_API OpacityDrawFilter : public SkDrawFilter {
+ public:
+  OpacityDrawFilter(float opacity, bool disable_image_filtering);
+  virtual ~OpacityDrawFilter();
+  virtual bool filter(SkPaint* paint, SkDrawFilter::Type type) OVERRIDE;
+
+ private:
+  int alpha_;
+  bool disable_image_filtering_;
+};
+
+}  // namespace skia
+
+#endif  // SKIA_EXT_OPACITY_DRAW_FILTER_H
+
diff --git a/skia/ext/platform_canvas.cc b/skia/ext/platform_canvas.cc
index 6a25481..f0d1717 100644
--- a/skia/ext/platform_canvas.cc
+++ b/skia/ext/platform_canvas.cc
@@ -9,7 +9,7 @@
 
 namespace skia {
 
-SkDevice* GetTopDevice(const SkCanvas& canvas) {
+SkBaseDevice* GetTopDevice(const SkCanvas& canvas) {
   return canvas.getTopDevice(true);
 }
 
@@ -64,7 +64,7 @@
   return 4 * width;
 }
 
-SkCanvas* CreateCanvas(const skia::RefPtr<SkDevice>& device, OnFailureType failureType) {
+SkCanvas* CreateCanvas(const skia::RefPtr<SkBaseDevice>& device, OnFailureType failureType) {
   if (!device) {
     if (CRASH_ON_FAILURE == failureType)
       SK_CRASH();
diff --git a/skia/ext/platform_canvas.h b/skia/ext/platform_canvas.h
index 9e2bc82..85ad85e 100644
--- a/skia/ext/platform_canvas.h
+++ b/skia/ext/platform_canvas.h
@@ -74,7 +74,7 @@
   return CreatePlatformCanvas(width, height, is_opaque, 0, CRASH_ON_FAILURE);
 }
 
-SK_API SkCanvas* CreateCanvas(const skia::RefPtr<SkDevice>& device,
+SK_API SkCanvas* CreateCanvas(const skia::RefPtr<SkBaseDevice>& device,
                               OnFailureType failure_type);
 
 static inline SkCanvas* CreateBitmapCanvas(int width,
@@ -95,7 +95,7 @@
 // alignment reasons we may wish to increase that.
 SK_API size_t PlatformCanvasStrideForWidth(unsigned width);
 
-// Returns the SkDevice pointer of the topmost rect with a non-empty
+// Returns the SkBaseDevice pointer of the topmost rect with a non-empty
 // clip. In practice, this is usually either the top layer or nothing, since
 // we usually set the clip to new layers when we make them.
 //
@@ -106,7 +106,7 @@
 //
 // Danger: the resulting device should not be saved. It will be invalidated
 // by the next call to save() or restore().
-SK_API SkDevice* GetTopDevice(const SkCanvas& canvas);
+SK_API SkBaseDevice* GetTopDevice(const SkCanvas& canvas);
 
 // Returns true if native platform routines can be used to draw on the
 // given canvas. If this function returns false, BeginPlatformPaint will
diff --git a/skia/ext/platform_canvas_unittest.cc b/skia/ext/platform_canvas_unittest.cc
index 7595bca..d0d83dd 100644
--- a/skia/ext/platform_canvas_unittest.cc
+++ b/skia/ext/platform_canvas_unittest.cc
@@ -33,7 +33,7 @@
 bool VerifyRect(const PlatformCanvas& canvas,
                 uint32_t canvas_color, uint32_t rect_color,
                 int x, int y, int w, int h) {
-  SkDevice* device = skia::GetTopDevice(canvas);
+  SkBaseDevice* device = skia::GetTopDevice(canvas);
   const SkBitmap& bitmap = device->accessBitmap(false);
   SkAutoLockPixels lock(bitmap);
 
@@ -72,7 +72,7 @@
 bool VerifyRoundedRect(const PlatformCanvas& canvas,
                        uint32_t canvas_color, uint32_t rect_color,
                        int x, int y, int w, int h) {
-  SkDevice* device = skia::GetTopDevice(canvas);
+  SkBaseDevice* device = skia::GetTopDevice(canvas);
   const SkBitmap& bitmap = device->accessBitmap(false);
   SkAutoLockPixels lock(bitmap);
 
diff --git a/skia/ext/platform_device.cc b/skia/ext/platform_device.cc
index c7f1565..ae720df 100644
--- a/skia/ext/platform_device.cc
+++ b/skia/ext/platform_device.cc
@@ -33,12 +33,12 @@
 
 }  // namespace
 
-void SetPlatformDevice(SkDevice* device, PlatformDevice* platform_behaviour) {
+void SetPlatformDevice(SkBaseDevice* device, PlatformDevice* platform_behaviour) {
   SkMetaData& meta_data = device->getMetaData();
   meta_data.setPtr(kDevicePlatformBehaviour, platform_behaviour);
 }
 
-PlatformDevice* GetPlatformDevice(SkDevice* device) {
+PlatformDevice* GetPlatformDevice(SkBaseDevice* device) {
   if (device) {
     SkMetaData& meta_data = device->getMetaData();
     PlatformDevice* device_behaviour = NULL;
@@ -50,7 +50,7 @@
 }
 
 SkMetaData& getMetaData(const SkCanvas& canvas) {
-  SkDevice* device = canvas.getDevice();
+  SkBaseDevice* device = canvas.getDevice();
   DCHECK(device != NULL);
   return device->getMetaData();
 }
diff --git a/skia/ext/platform_device.h b/skia/ext/platform_device.h
index 4ac3aee..d46b5d4 100644
--- a/skia/ext/platform_device.h
+++ b/skia/ext/platform_device.h
@@ -13,7 +13,7 @@
 #endif
 
 #include "third_party/skia/include/core/SkColor.h"
-#include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
 #include "third_party/skia/include/core/SkPreConfig.h"
 
 class SkMatrix;
@@ -50,29 +50,27 @@
 #endif
 
 // The following routines provide accessor points for the functionality
-// exported by the various PlatformDevice ports.  The PlatformDevice, and
-// BitmapPlatformDevice classes inherit directly from SkDevice, which is no
-// longer a supported usage-pattern for skia.  In preparation of the removal of
-// these classes, all calls to PlatformDevice::* should be routed through these
+// exported by the various PlatformDevice ports.  
+// All calls to PlatformDevice::* should be routed through these 
 // helper functions.
 
 // Bind a PlatformDevice instance, |platform_device| to |device|.  Subsequent
 // calls to the functions exported below will forward the request to the
 // corresponding method on the bound PlatformDevice instance.    If no
-// PlatformDevice has been bound to the SkDevice passed, then the routines are
-// NOPS.
-SK_API void SetPlatformDevice(SkDevice* device,
+// PlatformDevice has been bound to the SkBaseDevice passed, then the 
+// routines are NOPS.
+SK_API void SetPlatformDevice(SkBaseDevice* device,
                               PlatformDevice* platform_device);
-SK_API PlatformDevice* GetPlatformDevice(SkDevice* device);
+SK_API PlatformDevice* GetPlatformDevice(SkBaseDevice* device);
 
 
 #if defined(OS_WIN)
 // Initializes the default settings and colors in a device context.
 SK_API void InitializeDC(HDC context);
 #elif defined(OS_MACOSX)
-// Returns the CGContext that backing the SkDevice.  Forwards to the bound
+// Returns the CGContext that backing the SkBaseDevice.  Forwards to the bound
 // PlatformDevice.  Returns NULL if no PlatformDevice is bound.
-SK_API CGContextRef GetBitmapContext(SkDevice* device);
+SK_API CGContextRef GetBitmapContext(SkBaseDevice* device);
 #endif
 
 // Following routines are used in print preview workflow to mark the draft mode
@@ -86,14 +84,18 @@
 SK_API bool IsPreviewMetafile(const SkCanvas& canvas);
 #endif
 
-// A SkDevice is basically a wrapper around SkBitmap that provides a surface for
-// SkCanvas to draw into. PlatformDevice provides a surface Windows can also
-// write to. It also provides functionality to play well with GDI drawing
-// functions. This class is abstract and must be subclassed. It provides the
-// basic interface to implement it either with or without a bitmap backend.
+// A SkBitmapDevice is basically a wrapper around SkBitmap that provides a 
+// surface for SkCanvas to draw into. PlatformDevice provides a surface 
+// Windows can also write to. It also provides functionality to play well 
+// with GDI drawing functions. This class is abstract and must be subclassed. 
+// It provides the basic interface to implement it either with or without 
+// a bitmap backend.
 //
-// PlatformDevice provides an interface which sub-classes of SkDevice can also
-// provide to allow for drawing by the native platform into the device.
+// PlatformDevice provides an interface which sub-classes of SkBaseDevice can 
+// also provide to allow for drawing by the native platform into the device.
+// TODO(robertphillips): Once the bitmap-specific entry points are removed
+// from SkBaseDevice it might make sense for PlatformDevice to be derived
+// from it.
 class SK_API PlatformDevice {
  public:
   virtual ~PlatformDevice() {}
diff --git a/skia/ext/platform_device_mac.cc b/skia/ext/platform_device_mac.cc
index 6ff017d..f66372b 100644
--- a/skia/ext/platform_device_mac.cc
+++ b/skia/ext/platform_device_mac.cc
@@ -14,7 +14,7 @@
 
 namespace skia {
 
-CGContextRef GetBitmapContext(SkDevice* device) {
+CGContextRef GetBitmapContext(SkBaseDevice* device) {
   PlatformDevice* platform_device = GetPlatformDevice(device);
   if (platform_device)
     return platform_device->GetBitmapContext();
diff --git a/skia/ext/skia_utils_mac.mm b/skia/ext/skia_utils_mac.mm
index 213bb00..50217d0 100644
--- a/skia/ext/skia_utils_mac.mm
+++ b/skia/ext/skia_utils_mac.mm
@@ -192,7 +192,7 @@
   int width = CGImageGetWidth(image);
   int height = CGImageGetHeight(image);
 
-  scoped_ptr<SkDevice> device(
+  scoped_ptr<SkBaseDevice> device(
       skia::BitmapPlatformDevice::Create(NULL, width, height, false));
 
   CGContextRef context = skia::GetBitmapContext(device.get());
@@ -362,7 +362,7 @@
 }
 
 CGContextRef SkiaBitLocker::cgContext() {
-  SkDevice* device = canvas_->getTopDevice();
+  SkBaseDevice* device = canvas_->getTopDevice();
   DCHECK(device);
   if (!device)
     return 0;
diff --git a/skia/ext/vector_canvas.cc b/skia/ext/vector_canvas.cc
index 9de6b3d..13025eb 100644
--- a/skia/ext/vector_canvas.cc
+++ b/skia/ext/vector_canvas.cc
@@ -7,7 +7,7 @@
 
 namespace skia {
 
-VectorCanvas::VectorCanvas(SkDevice* device)
+VectorCanvas::VectorCanvas(SkBaseDevice* device)
     : PlatformCanvas(device) {
 }
 
@@ -30,8 +30,8 @@
 }
 
 bool VectorCanvas::IsTopDeviceVectorial() const {
-  SkDevice* device = GetTopDevice(*this);
-  return device->getDeviceCapabilities() & SkDevice::kVector_Capability;
+  SkBaseDevice* device = GetTopDevice(*this);
+  return device->getDeviceCapabilities() & SkBaseDevice::kVector_Capability;
 }
 
 }  // namespace skia
diff --git a/skia/ext/vector_canvas.h b/skia/ext/vector_canvas.h
index e7a67fc..66ef216 100644
--- a/skia/ext/vector_canvas.h
+++ b/skia/ext/vector_canvas.h
@@ -8,7 +8,8 @@
 #include "base/compiler_specific.h"
 #include "skia/ext/platform_canvas.h"
 
-class SkDevice;
+// TODO(robertphillips): change this to "class SkBaseDevice;"
+#include "third_party/skia/include/core/SkDevice.h"
 
 namespace skia {
 
@@ -19,7 +20,7 @@
 class SK_API VectorCanvas : public PlatformCanvas {
  public:
   // Ownership of |device| is transfered to VectorCanvas.
-  explicit VectorCanvas(SkDevice* device);
+  explicit VectorCanvas(SkBaseDevice* device);
   virtual ~VectorCanvas();
 
   virtual SkBounder* setBounder(SkBounder* bounder) OVERRIDE;
diff --git a/skia/ext/vector_platform_device_emf_win.cc b/skia/ext/vector_platform_device_emf_win.cc
index 16d0844..fe1ba29 100644
--- a/skia/ext/vector_platform_device_emf_win.cc
+++ b/skia/ext/vector_platform_device_emf_win.cc
@@ -22,7 +22,7 @@
     do { if (paint.isNoDrawAnnotation()) { return; } } while (0)
 
 // static
-SkDevice* VectorPlatformDeviceEmf::CreateDevice(
+SkBaseDevice* VectorPlatformDeviceEmf::CreateDevice(
     int width, int height, bool is_opaque, HANDLE shared_section) {
   if (!is_opaque) {
     // TODO(maruel):  http://crbug.com/18382 When restoring a semi-transparent
@@ -46,7 +46,7 @@
   // SkScalarRound(value) as SkScalarRound(value * 10). Safari is already
   // doing the same for text rendering.
   SkASSERT(shared_section);
-  SkDevice* device = VectorPlatformDeviceEmf::create(
+  SkBaseDevice* device = VectorPlatformDeviceEmf::create(
       reinterpret_cast<HDC>(shared_section), width, height);
   return device;
 }
@@ -65,7 +65,7 @@
   hdr->biClrImportant = 0;
 }
 
-SkDevice* VectorPlatformDeviceEmf::create(HDC dc, int width, int height) {
+SkBaseDevice* VectorPlatformDeviceEmf::create(HDC dc, int width, int height) {
   InitializeDC(dc);
 
   // Link the SkBitmap to the current selected bitmap in the device context.
@@ -100,7 +100,7 @@
 }
 
 VectorPlatformDeviceEmf::VectorPlatformDeviceEmf(HDC dc, const SkBitmap& bitmap)
-    : SkDevice(bitmap),
+    : SkBitmapDevice(bitmap),
       hdc_(dc),
       previous_brush_(NULL),
       previous_pen_(NULL) {
@@ -118,7 +118,7 @@
 }
 
 uint32_t VectorPlatformDeviceEmf::getDeviceCapabilities() {
-  return SkDevice::getDeviceCapabilities() | kVector_Capability;
+  return SkBitmapDevice::getDeviceCapabilities() | kVector_Capability;
 }
 
 void VectorPlatformDeviceEmf::drawPaint(const SkDraw& draw,
@@ -586,7 +586,7 @@
 }
 
 void VectorPlatformDeviceEmf::drawDevice(const SkDraw& draw,
-                                         SkDevice* device,
+                                         SkBaseDevice* device,
                                          int x,
                                          int y,
                                          const SkPaint& paint) {
@@ -693,7 +693,7 @@
   LoadClippingRegionToDC(hdc_, clip_region_, t);
 }
 
-SkDevice* VectorPlatformDeviceEmf::onCreateCompatibleDevice(
+SkBaseDevice* VectorPlatformDeviceEmf::onCreateCompatibleDevice(
     SkBitmap::Config config, int width, int height, bool isOpaque,
     Usage /*usage*/) {
   SkASSERT(config == SkBitmap::kARGB_8888_Config);
diff --git a/skia/ext/vector_platform_device_emf_win.h b/skia/ext/vector_platform_device_emf_win.h
index 9baee32..61ea441 100644
--- a/skia/ext/vector_platform_device_emf_win.h
+++ b/skia/ext/vector_platform_device_emf_win.h
@@ -17,13 +17,15 @@
 // SkCanvas to draw into. This specific device is not not backed by a surface
 // and is thus unreadable. This is because the backend is completely vectorial.
 // This device is a simple wrapper over a Windows device context (HDC) handle.
-class VectorPlatformDeviceEmf : public SkDevice, public PlatformDevice {
+// TODO(robertphillips): Once Skia's SkBaseDevice is refactored to remove
+// the bitmap-specific entry points, this class should derive from it.
+class VectorPlatformDeviceEmf : public SkBitmapDevice, public PlatformDevice {
  public:
-  SK_API static SkDevice* CreateDevice(int width, int height, bool isOpaque,
-                                       HANDLE shared_section);
+  SK_API static SkBaseDevice* CreateDevice(int width, int height, bool isOpaque,
+                                           HANDLE shared_section);
 
   // Factory function. The DC is kept as the output context.
-  static SkDevice* create(HDC dc, int width, int height);
+  static SkBaseDevice* create(HDC dc, int width, int height);
 
   VectorPlatformDeviceEmf(HDC dc, const SkBitmap& bitmap);
   virtual ~VectorPlatformDeviceEmf();
@@ -32,7 +34,7 @@
   virtual PlatformSurface BeginPlatformPaint() OVERRIDE;
   virtual void DrawToNativeContext(HDC dc, int x, int y,
                                    const RECT* src_rect) OVERRIDE;
-  // SkDevice methods.
+  // SkBaseDevice methods.
   virtual uint32_t getDeviceCapabilities();
   virtual void drawPaint(const SkDraw& draw, const SkPaint& paint) OVERRIDE;
   virtual void drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
@@ -67,7 +69,7 @@
                             const SkColor colors[], SkXfermode* xmode,
                             const uint16_t indices[], int indexCount,
                             const SkPaint& paint) OVERRIDE;
-  virtual void drawDevice(const SkDraw& draw, SkDevice*, int x, int y,
+  virtual void drawDevice(const SkDraw& draw, SkBaseDevice*, int x, int y,
                           const SkPaint&) OVERRIDE;
 
   virtual void setMatrixClip(const SkMatrix& transform, const SkRegion& region,
@@ -76,9 +78,9 @@
   void LoadClipRegion();
 
  protected:
-  virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config, int width,
-                                             int height, bool isOpaque,
-                                             Usage usage) OVERRIDE;
+  virtual SkBaseDevice* onCreateCompatibleDevice(SkBitmap::Config, int width,
+                                                 int height, bool isOpaque,
+                                                 Usage usage) OVERRIDE;
 
  private:
   // Applies the SkPaint's painting properties in the current GDI context, if
diff --git a/skia/ext/vector_platform_device_skia.h b/skia/ext/vector_platform_device_skia.h
index 4a6d2d9..fe71ba3 100644
--- a/skia/ext/vector_platform_device_skia.h
+++ b/skia/ext/vector_platform_device_skia.h
@@ -10,7 +10,6 @@
 #include "base/logging.h"
 #include "skia/ext/platform_device.h"
 #include "skia/ext/refptr.h"
-#include "third_party/skia/include/core/SkTScopedPtr.h"
 #include "third_party/skia/include/pdf/SkPDFDevice.h"
 
 class SkMatrix;
diff --git a/skia/skia_chrome.gypi b/skia/skia_chrome.gypi
index 00a0f49..203e3f6 100644
--- a/skia/skia_chrome.gypi
+++ b/skia/skia_chrome.gypi
@@ -49,6 +49,8 @@
     'ext/lazy_pixel_ref_utils.cc',
     'ext/lazy_pixel_ref_utils.h',
     'ext/SkThread_chrome.cc',
+    'ext/opacity_draw_filter.cc',
+    'ext/opacity_draw_filter.h',
     'ext/paint_simplifier.cc',
     'ext/paint_simplifier.h',
     'ext/platform_canvas.cc',
@@ -61,6 +63,8 @@
     'ext/recursive_gaussian_convolution.cc',
     'ext/recursive_gaussian_convolution.h',
     'ext/refptr.h',
+    'ext/SkDiscardableMemory_chrome.h',
+    'ext/SkDiscardableMemory_chrome.cc',
     'ext/SkMemory_new_handler.cpp',
     'ext/skia_trace_shim.h',
     'ext/skia_utils_base.cc',
diff --git a/skia/skia_chrome.target.darwin-arm.mk b/skia/skia_chrome.target.darwin-arm.mk
index 12da9d6..3886a97 100644
--- a/skia/skia_chrome.target.darwin-arm.mk
+++ b/skia/skia_chrome.target.darwin-arm.mk
@@ -36,11 +36,13 @@
 	skia/ext/image_operations.cc \
 	skia/ext/lazy_pixel_ref.cc \
 	skia/ext/lazy_pixel_ref_utils.cc \
+	skia/ext/opacity_draw_filter.cc \
 	skia/ext/paint_simplifier.cc \
 	skia/ext/platform_canvas.cc \
 	skia/ext/platform_device.cc \
 	skia/ext/platform_device_linux.cc \
 	skia/ext/recursive_gaussian_convolution.cc \
+	skia/ext/SkDiscardableMemory_chrome.cc \
 	skia/ext/skia_utils_base.cc \
 	skia/ext/vector_canvas.cc
 
diff --git a/skia/skia_chrome.target.darwin-mips.mk b/skia/skia_chrome.target.darwin-mips.mk
index 355d00b..3aa5ef9 100644
--- a/skia/skia_chrome.target.darwin-mips.mk
+++ b/skia/skia_chrome.target.darwin-mips.mk
@@ -36,11 +36,13 @@
 	skia/ext/image_operations.cc \
 	skia/ext/lazy_pixel_ref.cc \
 	skia/ext/lazy_pixel_ref_utils.cc \
+	skia/ext/opacity_draw_filter.cc \
 	skia/ext/paint_simplifier.cc \
 	skia/ext/platform_canvas.cc \
 	skia/ext/platform_device.cc \
 	skia/ext/platform_device_linux.cc \
 	skia/ext/recursive_gaussian_convolution.cc \
+	skia/ext/SkDiscardableMemory_chrome.cc \
 	skia/ext/skia_utils_base.cc \
 	skia/ext/vector_canvas.cc
 
diff --git a/skia/skia_chrome.target.darwin-x86.mk b/skia/skia_chrome.target.darwin-x86.mk
index f40fb32..0e79a1d 100644
--- a/skia/skia_chrome.target.darwin-x86.mk
+++ b/skia/skia_chrome.target.darwin-x86.mk
@@ -36,11 +36,13 @@
 	skia/ext/image_operations.cc \
 	skia/ext/lazy_pixel_ref.cc \
 	skia/ext/lazy_pixel_ref_utils.cc \
+	skia/ext/opacity_draw_filter.cc \
 	skia/ext/paint_simplifier.cc \
 	skia/ext/platform_canvas.cc \
 	skia/ext/platform_device.cc \
 	skia/ext/platform_device_linux.cc \
 	skia/ext/recursive_gaussian_convolution.cc \
+	skia/ext/SkDiscardableMemory_chrome.cc \
 	skia/ext/skia_utils_base.cc \
 	skia/ext/vector_canvas.cc
 
diff --git a/skia/skia_chrome.target.linux-arm.mk b/skia/skia_chrome.target.linux-arm.mk
index 12da9d6..3886a97 100644
--- a/skia/skia_chrome.target.linux-arm.mk
+++ b/skia/skia_chrome.target.linux-arm.mk
@@ -36,11 +36,13 @@
 	skia/ext/image_operations.cc \
 	skia/ext/lazy_pixel_ref.cc \
 	skia/ext/lazy_pixel_ref_utils.cc \
+	skia/ext/opacity_draw_filter.cc \
 	skia/ext/paint_simplifier.cc \
 	skia/ext/platform_canvas.cc \
 	skia/ext/platform_device.cc \
 	skia/ext/platform_device_linux.cc \
 	skia/ext/recursive_gaussian_convolution.cc \
+	skia/ext/SkDiscardableMemory_chrome.cc \
 	skia/ext/skia_utils_base.cc \
 	skia/ext/vector_canvas.cc
 
diff --git a/skia/skia_chrome.target.linux-mips.mk b/skia/skia_chrome.target.linux-mips.mk
index 355d00b..3aa5ef9 100644
--- a/skia/skia_chrome.target.linux-mips.mk
+++ b/skia/skia_chrome.target.linux-mips.mk
@@ -36,11 +36,13 @@
 	skia/ext/image_operations.cc \
 	skia/ext/lazy_pixel_ref.cc \
 	skia/ext/lazy_pixel_ref_utils.cc \
+	skia/ext/opacity_draw_filter.cc \
 	skia/ext/paint_simplifier.cc \
 	skia/ext/platform_canvas.cc \
 	skia/ext/platform_device.cc \
 	skia/ext/platform_device_linux.cc \
 	skia/ext/recursive_gaussian_convolution.cc \
+	skia/ext/SkDiscardableMemory_chrome.cc \
 	skia/ext/skia_utils_base.cc \
 	skia/ext/vector_canvas.cc
 
diff --git a/skia/skia_chrome.target.linux-x86.mk b/skia/skia_chrome.target.linux-x86.mk
index f40fb32..0e79a1d 100644
--- a/skia/skia_chrome.target.linux-x86.mk
+++ b/skia/skia_chrome.target.linux-x86.mk
@@ -36,11 +36,13 @@
 	skia/ext/image_operations.cc \
 	skia/ext/lazy_pixel_ref.cc \
 	skia/ext/lazy_pixel_ref_utils.cc \
+	skia/ext/opacity_draw_filter.cc \
 	skia/ext/paint_simplifier.cc \
 	skia/ext/platform_canvas.cc \
 	skia/ext/platform_device.cc \
 	skia/ext/platform_device_linux.cc \
 	skia/ext/recursive_gaussian_convolution.cc \
+	skia/ext/SkDiscardableMemory_chrome.cc \
 	skia/ext/skia_utils_base.cc \
 	skia/ext/vector_canvas.cc
 
diff --git a/skia/skia_library.gypi b/skia/skia_library.gypi
index 23e5b7c..a1e9b8e 100644
--- a/skia/skia_library.gypi
+++ b/skia/skia_library.gypi
@@ -61,6 +61,7 @@
   'includes': [
     '../third_party/skia/gyp/core.gypi',
     '../third_party/skia/gyp/effects.gypi',
+    '../third_party/skia/gyp/pdf.gypi',
   ],
 
   'sources': [
@@ -77,33 +78,6 @@
 
     '../third_party/skia/src/opts/opts_check_SSE2.cpp',
 
-    '../third_party/skia/src/pdf/SkPDFCatalog.cpp',
-    '../third_party/skia/src/pdf/SkPDFCatalog.h',
-    '../third_party/skia/src/pdf/SkPDFDevice.cpp',
-    '../third_party/skia/src/pdf/SkPDFDocument.cpp',
-    '../third_party/skia/src/pdf/SkPDFFont.cpp',
-    '../third_party/skia/src/pdf/SkPDFFont.h',
-    '../third_party/skia/src/pdf/SkPDFFormXObject.cpp',
-    '../third_party/skia/src/pdf/SkPDFFormXObject.h',
-    '../third_party/skia/src/pdf/SkPDFGraphicState.cpp',
-    '../third_party/skia/src/pdf/SkPDFGraphicState.h',
-    '../third_party/skia/src/pdf/SkPDFImage.cpp',
-    '../third_party/skia/src/pdf/SkPDFImage.h',
-    '../third_party/skia/src/pdf/SkPDFImageStream.cpp',
-    '../third_party/skia/src/pdf/SkPDFImageStream.h',
-    '../third_party/skia/src/pdf/SkPDFPage.cpp',
-    '../third_party/skia/src/pdf/SkPDFPage.h',
-    '../third_party/skia/src/pdf/SkPDFResourceDict.cpp',
-    '../third_party/skia/src/pdf/SkPDFResourceDict.h',
-    '../third_party/skia/src/pdf/SkPDFShader.cpp',
-    '../third_party/skia/src/pdf/SkPDFShader.h',
-    '../third_party/skia/src/pdf/SkPDFStream.cpp',
-    '../third_party/skia/src/pdf/SkPDFStream.h',
-    '../third_party/skia/src/pdf/SkPDFTypes.cpp',
-    '../third_party/skia/src/pdf/SkPDFTypes.h',
-    '../third_party/skia/src/pdf/SkPDFUtils.cpp',
-    '../third_party/skia/src/pdf/SkPDFUtils.h',
-
     '../third_party/skia/src/ports/SkPurgeableMemoryBlock_none.cpp',
 
     '../third_party/skia/src/ports/SkFontConfigInterface_android.cpp',
@@ -163,8 +137,6 @@
     '../third_party/skia/src/utils/win/SkDWriteGeometrySink.cpp',
     '../third_party/skia/src/utils/win/SkDWriteGeometrySink.h',
     '../third_party/skia/src/utils/win/SkHRESULT.cpp',
-    '../third_party/skia/include/pdf/SkPDFDevice.h',
-    '../third_party/skia/include/pdf/SkPDFDocument.h',
 
     '../third_party/skia/include/ports/SkTypeface_win.h',
 
diff --git a/skia/skia_library.target.darwin-arm.mk b/skia/skia_library.target.darwin-arm.mk
index bea158c..eded98e 100644
--- a/skia/skia_library.target.darwin-arm.mk
+++ b/skia/skia_library.target.darwin-arm.mk
@@ -96,6 +96,7 @@
 	third_party/skia/src/core/SkDebug.cpp \
 	third_party/skia/src/core/SkDeque.cpp \
 	third_party/skia/src/core/SkDevice.cpp \
+	third_party/skia/src/core/SkDeviceLooper.cpp \
 	third_party/skia/src/core/SkDeviceProfile.cpp \
 	third_party/skia/src/core/SkDither.cpp \
 	third_party/skia/src/core/SkDraw.cpp \
@@ -257,6 +258,7 @@
 	third_party/skia/src/effects/SkLayerRasterizer.cpp \
 	third_party/skia/src/effects/SkLerpXfermode.cpp \
 	third_party/skia/src/effects/SkLightingImageFilter.cpp \
+	third_party/skia/src/effects/SkLumaXfermode.cpp \
 	third_party/skia/src/effects/SkMagnifierImageFilter.cpp \
 	third_party/skia/src/effects/SkMatrixConvolutionImageFilter.cpp \
 	third_party/skia/src/effects/SkMergeImageFilter.cpp \
diff --git a/skia/skia_library.target.darwin-mips.mk b/skia/skia_library.target.darwin-mips.mk
index d64f1af..08729ce 100644
--- a/skia/skia_library.target.darwin-mips.mk
+++ b/skia/skia_library.target.darwin-mips.mk
@@ -96,6 +96,7 @@
 	third_party/skia/src/core/SkDebug.cpp \
 	third_party/skia/src/core/SkDeque.cpp \
 	third_party/skia/src/core/SkDevice.cpp \
+	third_party/skia/src/core/SkDeviceLooper.cpp \
 	third_party/skia/src/core/SkDeviceProfile.cpp \
 	third_party/skia/src/core/SkDither.cpp \
 	third_party/skia/src/core/SkDraw.cpp \
@@ -257,6 +258,7 @@
 	third_party/skia/src/effects/SkLayerRasterizer.cpp \
 	third_party/skia/src/effects/SkLerpXfermode.cpp \
 	third_party/skia/src/effects/SkLightingImageFilter.cpp \
+	third_party/skia/src/effects/SkLumaXfermode.cpp \
 	third_party/skia/src/effects/SkMagnifierImageFilter.cpp \
 	third_party/skia/src/effects/SkMatrixConvolutionImageFilter.cpp \
 	third_party/skia/src/effects/SkMergeImageFilter.cpp \
diff --git a/skia/skia_library.target.darwin-x86.mk b/skia/skia_library.target.darwin-x86.mk
index 1cc2f86..f194b52 100644
--- a/skia/skia_library.target.darwin-x86.mk
+++ b/skia/skia_library.target.darwin-x86.mk
@@ -97,6 +97,7 @@
 	third_party/skia/src/core/SkDebug.cpp \
 	third_party/skia/src/core/SkDeque.cpp \
 	third_party/skia/src/core/SkDevice.cpp \
+	third_party/skia/src/core/SkDeviceLooper.cpp \
 	third_party/skia/src/core/SkDeviceProfile.cpp \
 	third_party/skia/src/core/SkDither.cpp \
 	third_party/skia/src/core/SkDraw.cpp \
@@ -258,6 +259,7 @@
 	third_party/skia/src/effects/SkLayerRasterizer.cpp \
 	third_party/skia/src/effects/SkLerpXfermode.cpp \
 	third_party/skia/src/effects/SkLightingImageFilter.cpp \
+	third_party/skia/src/effects/SkLumaXfermode.cpp \
 	third_party/skia/src/effects/SkMagnifierImageFilter.cpp \
 	third_party/skia/src/effects/SkMatrixConvolutionImageFilter.cpp \
 	third_party/skia/src/effects/SkMergeImageFilter.cpp \
diff --git a/skia/skia_library.target.linux-arm.mk b/skia/skia_library.target.linux-arm.mk
index bea158c..eded98e 100644
--- a/skia/skia_library.target.linux-arm.mk
+++ b/skia/skia_library.target.linux-arm.mk
@@ -96,6 +96,7 @@
 	third_party/skia/src/core/SkDebug.cpp \
 	third_party/skia/src/core/SkDeque.cpp \
 	third_party/skia/src/core/SkDevice.cpp \
+	third_party/skia/src/core/SkDeviceLooper.cpp \
 	third_party/skia/src/core/SkDeviceProfile.cpp \
 	third_party/skia/src/core/SkDither.cpp \
 	third_party/skia/src/core/SkDraw.cpp \
@@ -257,6 +258,7 @@
 	third_party/skia/src/effects/SkLayerRasterizer.cpp \
 	third_party/skia/src/effects/SkLerpXfermode.cpp \
 	third_party/skia/src/effects/SkLightingImageFilter.cpp \
+	third_party/skia/src/effects/SkLumaXfermode.cpp \
 	third_party/skia/src/effects/SkMagnifierImageFilter.cpp \
 	third_party/skia/src/effects/SkMatrixConvolutionImageFilter.cpp \
 	third_party/skia/src/effects/SkMergeImageFilter.cpp \
diff --git a/skia/skia_library.target.linux-mips.mk b/skia/skia_library.target.linux-mips.mk
index d64f1af..08729ce 100644
--- a/skia/skia_library.target.linux-mips.mk
+++ b/skia/skia_library.target.linux-mips.mk
@@ -96,6 +96,7 @@
 	third_party/skia/src/core/SkDebug.cpp \
 	third_party/skia/src/core/SkDeque.cpp \
 	third_party/skia/src/core/SkDevice.cpp \
+	third_party/skia/src/core/SkDeviceLooper.cpp \
 	third_party/skia/src/core/SkDeviceProfile.cpp \
 	third_party/skia/src/core/SkDither.cpp \
 	third_party/skia/src/core/SkDraw.cpp \
@@ -257,6 +258,7 @@
 	third_party/skia/src/effects/SkLayerRasterizer.cpp \
 	third_party/skia/src/effects/SkLerpXfermode.cpp \
 	third_party/skia/src/effects/SkLightingImageFilter.cpp \
+	third_party/skia/src/effects/SkLumaXfermode.cpp \
 	third_party/skia/src/effects/SkMagnifierImageFilter.cpp \
 	third_party/skia/src/effects/SkMatrixConvolutionImageFilter.cpp \
 	third_party/skia/src/effects/SkMergeImageFilter.cpp \
diff --git a/skia/skia_library.target.linux-x86.mk b/skia/skia_library.target.linux-x86.mk
index 1cc2f86..f194b52 100644
--- a/skia/skia_library.target.linux-x86.mk
+++ b/skia/skia_library.target.linux-x86.mk
@@ -97,6 +97,7 @@
 	third_party/skia/src/core/SkDebug.cpp \
 	third_party/skia/src/core/SkDeque.cpp \
 	third_party/skia/src/core/SkDevice.cpp \
+	third_party/skia/src/core/SkDeviceLooper.cpp \
 	third_party/skia/src/core/SkDeviceProfile.cpp \
 	third_party/skia/src/core/SkDither.cpp \
 	third_party/skia/src/core/SkDraw.cpp \
@@ -258,6 +259,7 @@
 	third_party/skia/src/effects/SkLayerRasterizer.cpp \
 	third_party/skia/src/effects/SkLerpXfermode.cpp \
 	third_party/skia/src/effects/SkLightingImageFilter.cpp \
+	third_party/skia/src/effects/SkLumaXfermode.cpp \
 	third_party/skia/src/effects/SkMagnifierImageFilter.cpp \
 	third_party/skia/src/effects/SkMatrixConvolutionImageFilter.cpp \
 	third_party/skia/src/effects/SkMergeImageFilter.cpp \
diff --git a/skia/skia_library_opts.gyp b/skia/skia_library_opts.gyp
index c87b982..1526376 100644
--- a/skia/skia_library_opts.gyp
+++ b/skia/skia_library_opts.gyp
@@ -101,12 +101,14 @@
         }],
         [ 'target_arch == "arm" and arm_version < 6', {
           'sources': [
+            '../third_party/skia/src/opts/SkBlitMask_opts_none.cpp',
             '../third_party/skia/src/opts/SkBlitRow_opts_none.cpp',
             '../third_party/skia/src/opts/SkUtils_opts_none.cpp',
           ],
         }],
         [ 'target_arch == "arm" and arm_version >= 6', {
           'sources': [
+            '../third_party/skia/src/opts/SkBlitMask_opts_arm.cpp',
             '../third_party/skia/src/opts/SkBlitRow_opts_arm.cpp',
             '../third_party/skia/src/opts/SkBlitRow_opts_arm.h',
             '../third_party/skia/src/opts/opts_check_arm.cpp',
@@ -118,6 +120,7 @@
           ],
           'sources': [
             '../third_party/skia/src/opts/SkBitmapProcState_opts_none.cpp',
+            '../third_party/skia/src/opts/SkBlitMask_opts_none.cpp',
             '../third_party/skia/src/opts/SkBlitRow_opts_none.cpp',
             '../third_party/skia/src/opts/SkUtils_opts_none.cpp',
           ],
@@ -178,6 +181,7 @@
       ],
       'sources': [
         '../third_party/skia/src/opts/SkBitmapProcState_opts_none.cpp',
+        '../third_party/skia/src/opts/SkBlitMask_opts_none.cpp',
         '../third_party/skia/src/opts/SkBlitRow_opts_none.cpp',
         '../third_party/skia/src/opts/SkUtils_opts_none.cpp',
       ],
diff --git a/skia/skia_opts.target.darwin-arm.mk b/skia/skia_opts.target.darwin-arm.mk
index aca7c09..204fda4 100644
--- a/skia/skia_opts.target.darwin-arm.mk
+++ b/skia/skia_opts.target.darwin-arm.mk
@@ -24,6 +24,7 @@
 LOCAL_SRC_FILES := \
 	third_party/skia/src/opts/SkBitmapProcState_opts_arm.cpp \
 	third_party/skia/src/opts/memset.arm.S \
+	third_party/skia/src/opts/SkBlitMask_opts_arm.cpp \
 	third_party/skia/src/opts/SkBlitRow_opts_arm.cpp \
 	third_party/skia/src/opts/opts_check_arm.cpp
 
diff --git a/skia/skia_opts.target.darwin-mips.mk b/skia/skia_opts.target.darwin-mips.mk
index 8b7204f..8b2eae6 100644
--- a/skia/skia_opts.target.darwin-mips.mk
+++ b/skia/skia_opts.target.darwin-mips.mk
@@ -23,6 +23,7 @@
 
 LOCAL_SRC_FILES := \
 	third_party/skia/src/opts/SkBitmapProcState_opts_none.cpp \
+	third_party/skia/src/opts/SkBlitMask_opts_none.cpp \
 	third_party/skia/src/opts/SkBlitRow_opts_none.cpp \
 	third_party/skia/src/opts/SkUtils_opts_none.cpp
 
diff --git a/skia/skia_opts.target.linux-arm.mk b/skia/skia_opts.target.linux-arm.mk
index aca7c09..204fda4 100644
--- a/skia/skia_opts.target.linux-arm.mk
+++ b/skia/skia_opts.target.linux-arm.mk
@@ -24,6 +24,7 @@
 LOCAL_SRC_FILES := \
 	third_party/skia/src/opts/SkBitmapProcState_opts_arm.cpp \
 	third_party/skia/src/opts/memset.arm.S \
+	third_party/skia/src/opts/SkBlitMask_opts_arm.cpp \
 	third_party/skia/src/opts/SkBlitRow_opts_arm.cpp \
 	third_party/skia/src/opts/opts_check_arm.cpp
 
diff --git a/skia/skia_opts.target.linux-mips.mk b/skia/skia_opts.target.linux-mips.mk
index 8b7204f..8b2eae6 100644
--- a/skia/skia_opts.target.linux-mips.mk
+++ b/skia/skia_opts.target.linux-mips.mk
@@ -23,6 +23,7 @@
 
 LOCAL_SRC_FILES := \
 	third_party/skia/src/opts/SkBitmapProcState_opts_none.cpp \
+	third_party/skia/src/opts/SkBlitMask_opts_none.cpp \
 	third_party/skia/src/opts/SkBlitRow_opts_none.cpp \
 	third_party/skia/src/opts/SkUtils_opts_none.cpp
 
diff --git a/skia/skia_test_expectations.txt b/skia/skia_test_expectations.txt
index f4d8325..fe4cda7 100644
--- a/skia/skia_test_expectations.txt
+++ b/skia/skia_test_expectations.txt
@@ -51,25 +51,11 @@
 # Image scaling difference due to ongoing Skia image resizing changes
 crbug.com/263331 virtual/deferred/fast/images/webp-color-profile-lossy.html [ ImageOnlyFailure ]
 
-# With Skia r10399 Skia's Windows text metrics have been improved
-crbug.com/265448 [ Win ] svg/batik/text/textEffect3.svg [ ImageOnlyFailure ]
-crbug.com/265448 [ Win ] svg/custom/use-referencing-nonexisting-symbol.svg [ ImageOnlyFailure ]
-crbug.com/265448 [ Win ] svg/transforms/animated-path-inside-transformed-html.xhtml [ ImageOnlyFailure ]
-crbug.com/265448 [ Win ] svg/transforms/text-with-pattern-inside-transformed-html.xhtml [ ImageOnlyFailure ]
-crbug.com/265448 [ Win ] svg/transforms/text-with-pattern-with-svg-transform.svg [ ImageOnlyFailure ]
-
-# With skia r10444 some imperceptible differences on the edges of blurs were caused
-crbug.com/266315 fast/box-shadow/box-shadow-clipped-slices.html [ ImageOnlyFailure ]
-crbug.com/266315 fast/repaint/box-shadow-h.html [ ImageOnlyFailure ]
-crbug.com/266315 fast/repaint/box-shadow-v.html [ ImageOnlyFailure ]
-crbug.com/266315 fast/repaint/shadow-multiple-horizontal.html [ ImageOnlyFailure ]
-crbug.com/266315 fast/repaint/shadow-multiple-strict-horizontal.html [ ImageOnlyFailure ]
-crbug.com/266315 fast/repaint/shadow-multiple-strict-vertical.html [ ImageOnlyFailure ]
-crbug.com/266315 fast/repaint/shadow-multiple-vertical.html [ ImageOnlyFailure ]
-
-# With Skia r10769 the rendering of lines was slightly changed resulting in two
-# image layout differences
-crbug.com/275475 virtual/gpu/fast/canvas/canvas-transforms-during-path.html [ ImageOnlyFailure ]
-crbug.com/275475 virtual/gpu/fast/canvas/quadraticCurveTo.xml [ ImageOnlyFailure ]
+# With Skia r10936 several blur layout tests slightly changed [ ImageOnlyFailure ]
+crbug.com/280292 fast/forms/range/range-appearance-basic.html [ ImageOnlyFailure ]
+crbug.com/280292 fast/forms/textarea/textarea-appearance-basic.html [ ImageOnlyFailure ]
+crbug.com/280292 inspector/debugger/live-edit.html [ ImageOnlyFailure ]
+crbug.com/280292 virtual/gpu/fast/canvas/canvas-scale-strokePath-shadow.html [ Failure ]
+crbug.com/280292 fast/forms/input-appearance-height.html [ ImageOnlyFailure ]
 
 # END OVERRIDES HERE (this line ensures that the file is newline-terminated)
diff --git a/sync/engine/build_commit_command.cc b/sync/engine/build_commit_command.cc
index 0143370..53c5221 100644
--- a/sync/engine/build_commit_command.cc
+++ b/sync/engine/build_commit_command.cc
@@ -98,14 +98,14 @@
 }
 
 namespace {
-void SetEntrySpecifics(Entry* meta_entry,
+void SetEntrySpecifics(const Entry& meta_entry,
                        sync_pb::SyncEntity* sync_entry) {
   // Add the new style extension and the folder bit.
-  sync_entry->mutable_specifics()->CopyFrom(meta_entry->Get(SPECIFICS));
-  sync_entry->set_folder(meta_entry->Get(syncable::IS_DIR));
+  sync_entry->mutable_specifics()->CopyFrom(meta_entry.Get(SPECIFICS));
+  sync_entry->set_folder(meta_entry.Get(syncable::IS_DIR));
 
   CHECK(!sync_entry->specifics().password().has_client_only_encrypted_data());
-  DCHECK_EQ(meta_entry->GetModelType(), GetModelType(*sync_entry));
+  DCHECK_EQ(meta_entry.GetModelType(), GetModelType(*sync_entry));
 }
 }  // namespace
 
@@ -121,7 +121,7 @@
   for (size_t i = 0; i < batch_commit_set_.Size(); i++) {
     Id id = batch_commit_set_.GetCommitIdAt(i);
     sync_pb::SyncEntity* sync_entry = commit_message->add_entries();
-    sync_entry->set_id_string(SyncableIdToProto(id));
+
     Entry meta_entry(trans_, syncable::GET_BY_ID, id);
     CHECK(meta_entry.good());
 
@@ -130,85 +130,96 @@
                   meta_entry.GetModelType()))
         << "Committing change to datatype that's not actively enabled.";
 
-    string name = meta_entry.Get(syncable::NON_UNIQUE_NAME);
-    CHECK(!name.empty());  // Make sure this isn't an update.
-    // Note: Truncation is also performed in WriteNode::SetTitle(..). But this
-    // call is still necessary to handle any title changes that might originate
-    // elsewhere, or already be persisted in the directory.
-    TruncateUTF8ToByteSize(name, 255, &name);
-    sync_entry->set_name(name);
-
-    // Set the non_unique_name.  If we do, the server ignores
-    // the |name| value (using |non_unique_name| instead), and will return
-    // in the CommitResponse a unique name if one is generated.
-    // We send both because it may aid in logging.
-    sync_entry->set_non_unique_name(name);
-
-    if (!meta_entry.Get(syncable::UNIQUE_CLIENT_TAG).empty()) {
-      sync_entry->set_client_defined_unique_tag(
-          meta_entry.Get(syncable::UNIQUE_CLIENT_TAG));
-    }
-
-    // Deleted items with server-unknown parent ids can be a problem so we set
-    // the parent to 0. (TODO(sync): Still true in protocol?).
-    Id new_parent_id;
-    if (meta_entry.Get(syncable::IS_DEL) &&
-        !meta_entry.Get(syncable::PARENT_ID).ServerKnows()) {
-      new_parent_id = trans_->root_id();
-    } else {
-      new_parent_id = meta_entry.Get(syncable::PARENT_ID);
-    }
-    sync_entry->set_parent_id_string(SyncableIdToProto(new_parent_id));
-
-    // If our parent has changed, send up the old one so the server
-    // can correctly deal with multiple parents.
-    // TODO(nick): With the server keeping track of the primary sync parent,
-    // it should not be necessary to provide the old_parent_id: the version
-    // number should suffice.
-    if (new_parent_id != meta_entry.Get(syncable::SERVER_PARENT_ID) &&
-        0 != meta_entry.Get(syncable::BASE_VERSION) &&
-        syncable::CHANGES_VERSION != meta_entry.Get(syncable::BASE_VERSION)) {
-      sync_entry->set_old_parent_id(
-          SyncableIdToProto(meta_entry.Get(syncable::SERVER_PARENT_ID)));
-    }
-
-    int64 version = meta_entry.Get(syncable::BASE_VERSION);
-    if (syncable::CHANGES_VERSION == version || 0 == version) {
-      // Undeletions are only supported for items that have a client tag.
-      DCHECK(!id.ServerKnows() ||
-             !meta_entry.Get(syncable::UNIQUE_CLIENT_TAG).empty())
-          << meta_entry;
-
-      // Version 0 means to create or undelete an object.
-      sync_entry->set_version(0);
-    } else {
-      DCHECK(id.ServerKnows()) << meta_entry;
-      sync_entry->set_version(meta_entry.Get(syncable::BASE_VERSION));
-    }
-    sync_entry->set_ctime(TimeToProtoTime(meta_entry.Get(syncable::CTIME)));
-    sync_entry->set_mtime(TimeToProtoTime(meta_entry.Get(syncable::MTIME)));
-
-    // Deletion is final on the server, let's move things and then delete them.
-    if (meta_entry.Get(IS_DEL)) {
-      sync_entry->set_deleted(true);
-    } else {
-      if (meta_entry.Get(SPECIFICS).has_bookmark()) {
-        // Both insert_after_item_id and position_in_parent fields are set only
-        // for legacy reasons.  See comments in sync.proto for more information.
-        const Id& prev_id = meta_entry.GetPredecessorId();
-        string prev_id_string =
-            prev_id.IsRoot() ? string() : prev_id.GetServerId();
-        sync_entry->set_insert_after_item_id(prev_id_string);
-        sync_entry->set_position_in_parent(
-            meta_entry.Get(UNIQUE_POSITION).ToInt64());
-        meta_entry.Get(UNIQUE_POSITION).ToProto(
-            sync_entry->mutable_unique_position());
-      }
-      SetEntrySpecifics(&meta_entry, sync_entry);
-    }
+    BuildCommitItem(meta_entry, sync_entry);
   }
 
+
   return SYNCER_OK;
 }
 
+// static.
+void BuildCommitCommand::BuildCommitItem(
+    const syncable::Entry& meta_entry,
+    sync_pb::SyncEntity* sync_entry) {
+  syncable::Id id = meta_entry.Get(syncable::ID);
+  sync_entry->set_id_string(SyncableIdToProto(id));
+
+  string name = meta_entry.Get(syncable::NON_UNIQUE_NAME);
+  CHECK(!name.empty());  // Make sure this isn't an update.
+  // Note: Truncation is also performed in WriteNode::SetTitle(..). But this
+  // call is still necessary to handle any title changes that might originate
+  // elsewhere, or already be persisted in the directory.
+  TruncateUTF8ToByteSize(name, 255, &name);
+  sync_entry->set_name(name);
+
+  // Set the non_unique_name.  If we do, the server ignores
+  // the |name| value (using |non_unique_name| instead), and will return
+  // in the CommitResponse a unique name if one is generated.
+  // We send both because it may aid in logging.
+  sync_entry->set_non_unique_name(name);
+
+  if (!meta_entry.Get(syncable::UNIQUE_CLIENT_TAG).empty()) {
+    sync_entry->set_client_defined_unique_tag(
+        meta_entry.Get(syncable::UNIQUE_CLIENT_TAG));
+  }
+
+  // Deleted items with server-unknown parent ids can be a problem so we set
+  // the parent to 0. (TODO(sync): Still true in protocol?).
+  Id new_parent_id;
+  if (meta_entry.Get(syncable::IS_DEL) &&
+      !meta_entry.Get(syncable::PARENT_ID).ServerKnows()) {
+    new_parent_id = syncable::BaseTransaction::root_id();
+  } else {
+    new_parent_id = meta_entry.Get(syncable::PARENT_ID);
+  }
+  sync_entry->set_parent_id_string(SyncableIdToProto(new_parent_id));
+
+  // If our parent has changed, send up the old one so the server
+  // can correctly deal with multiple parents.
+  // TODO(nick): With the server keeping track of the primary sync parent,
+  // it should not be necessary to provide the old_parent_id: the version
+  // number should suffice.
+  if (new_parent_id != meta_entry.Get(syncable::SERVER_PARENT_ID) &&
+      0 != meta_entry.Get(syncable::BASE_VERSION) &&
+      syncable::CHANGES_VERSION != meta_entry.Get(syncable::BASE_VERSION)) {
+    sync_entry->set_old_parent_id(
+        SyncableIdToProto(meta_entry.Get(syncable::SERVER_PARENT_ID)));
+  }
+
+  int64 version = meta_entry.Get(syncable::BASE_VERSION);
+  if (syncable::CHANGES_VERSION == version || 0 == version) {
+    // Undeletions are only supported for items that have a client tag.
+    DCHECK(!id.ServerKnows() ||
+           !meta_entry.Get(syncable::UNIQUE_CLIENT_TAG).empty())
+        << meta_entry;
+
+    // Version 0 means to create or undelete an object.
+    sync_entry->set_version(0);
+  } else {
+    DCHECK(id.ServerKnows()) << meta_entry;
+    sync_entry->set_version(meta_entry.Get(syncable::BASE_VERSION));
+  }
+  sync_entry->set_ctime(TimeToProtoTime(meta_entry.Get(syncable::CTIME)));
+  sync_entry->set_mtime(TimeToProtoTime(meta_entry.Get(syncable::MTIME)));
+
+  // Deletion is final on the server, let's move things and then delete them.
+  if (meta_entry.Get(IS_DEL)) {
+    sync_entry->set_deleted(true);
+  } else {
+    if (meta_entry.Get(SPECIFICS).has_bookmark()) {
+      // Both insert_after_item_id and position_in_parent fields are set only
+      // for legacy reasons.  See comments in sync.proto for more information.
+      const Id& prev_id = meta_entry.GetPredecessorId();
+      string prev_id_string =
+          prev_id.IsRoot() ? string() : prev_id.GetServerId();
+      sync_entry->set_insert_after_item_id(prev_id_string);
+      sync_entry->set_position_in_parent(
+          meta_entry.Get(UNIQUE_POSITION).ToInt64());
+      meta_entry.Get(UNIQUE_POSITION).ToProto(
+          sync_entry->mutable_unique_position());
+    }
+    SetEntrySpecifics(meta_entry, sync_entry);
+  }
+}
+
 }  // namespace syncer
diff --git a/sync/engine/build_commit_command.h b/sync/engine/build_commit_command.h
index 405cdff..a47c62a 100644
--- a/sync/engine/build_commit_command.h
+++ b/sync/engine/build_commit_command.h
@@ -48,6 +48,11 @@
   // SyncerCommand implementation.
   virtual SyncerError ExecuteImpl(sessions::SyncSession* session) OVERRIDE;
 
+  // Helper function that takes a snapshot of |meta_entry| and puts it into a
+  // protobuf suitable for use in a commit request message.
+  static void BuildCommitItem(const syncable::Entry& meta_entry,
+                              sync_pb::SyncEntity* sync_entry);
+
  private:
   FRIEND_TEST_ALL_PREFIXES(BuildCommitCommandTest, InterpolatePosition);
 
diff --git a/sync/js/sync_js_controller.cc b/sync/js/sync_js_controller.cc
index 49f3cc7..4d3148f 100644
--- a/sync/js/sync_js_controller.cc
+++ b/sync/js/sync_js_controller.cc
@@ -73,7 +73,7 @@
     // event handler we pass to it if we don't have any event
     // handlers.
     WeakHandle<JsEventHandler> backend_event_handler =
-        (js_event_handlers_.size() > 0) ?
+        js_event_handlers_.might_have_observers() ?
         MakeWeakHandle(AsWeakPtr()) : WeakHandle<SyncJsController>();
     js_backend_.Call(FROM_HERE, &JsBackend::SetJsEventHandler,
                      backend_event_handler);
diff --git a/sync/protocol/proto_value_conversions.cc b/sync/protocol/proto_value_conversions.cc
index f4fe44d..a42b902 100644
--- a/sync/protocol/proto_value_conversions.cc
+++ b/sync/protocol/proto_value_conversions.cc
@@ -202,6 +202,7 @@
   SET_STR(favicon_url);
   SET_ENUM(blocked_state, GetBlockedStateString);
   SET_STR_REP(content_pack_categories);
+  SET_INT32(http_status_code);
   return value;
 }
 
@@ -240,11 +241,36 @@
   return value;
 }
 
+base::DictionaryValue* SyncedNotificationImageToValue(
+    const sync_pb::SyncedNotificationImage& proto) {
+  base::DictionaryValue* value = new base::DictionaryValue();
+  SET_STR(url);
+  return value;
+}
+
+base::DictionaryValue* SyncedNotificationProfileImageToValue(
+    const sync_pb::SyncedNotificationProfileImage& proto) {
+  base::DictionaryValue* value = new base::DictionaryValue();
+  SET_STR(image_url);
+  return value;
+}
+
+base::DictionaryValue* MediaToValue(
+    const sync_pb::Media& proto) {
+  base::DictionaryValue* value = new base::DictionaryValue();
+  SET(image, SyncedNotificationImageToValue);
+  return value;
+}
+
 base::DictionaryValue* SimpleCollapsedLayoutToValue(
     const sync_pb::SimpleCollapsedLayout& proto) {
   base::DictionaryValue* value = new base::DictionaryValue();
   SET_STR(heading);
   SET_STR(description);
+  SET_STR(annotation);
+  SET_REP(media, MediaToValue);
+  SET_REP(profile_image, SyncedNotificationProfileImageToValue);
+  SET(app_icon, SyncedNotificationImageToValue);
   return value;
 }
 
@@ -258,6 +284,7 @@
 base::DictionaryValue* RenderInfoToValue(
     const sync_pb::SyncedNotificationRenderInfo& proto) {
   base::DictionaryValue* value = new base::DictionaryValue();
+  // TODO(petewil): Add the expanded info values too.
   SET(collapsed_info, CollapsedInfoToValue);
   return value;
 }
@@ -267,6 +294,8 @@
   base::DictionaryValue* value = new base::DictionaryValue();
   SET_STR(key);
   SET_INT32(read_state);
+  SET_INT64(creation_time_msec);
+  SET_INT32(priority);
   SET(render_info, RenderInfoToValue);
   return value;
 }
diff --git a/sync/protocol/proto_value_conversions.h b/sync/protocol/proto_value_conversions.h
index 39b8e0e..f5306d7 100644
--- a/sync/protocol/proto_value_conversions.h
+++ b/sync/protocol/proto_value_conversions.h
@@ -43,6 +43,7 @@
 class GlobalIdDirective;
 class HistoryDeleteDirectiveSpecifics;
 class KeystoreEncryptionFlagsSpecifics;
+class Media;
 class ManagedUserSettingSpecifics;
 class ManagedUserSpecifics;
 class NigoriSpecifics;
@@ -57,6 +58,8 @@
 class SessionWindow;
 class SimpleCollapsedLayout;
 class SyncCycleCompletedEventInfo;
+class SyncedNotificationImage;
+class SyncedNotificationProfileImage;
 class SyncedNotificationRenderInfo;
 class SyncedNotificationSpecifics;
 class TabNavigation;
@@ -192,6 +195,9 @@
 SYNC_EXPORT_PRIVATE base::DictionaryValue* ManagedUserSpecificsToValue(
     const sync_pb::ManagedUserSpecifics& managed_user_specifics);
 
+SYNC_EXPORT_PRIVATE base::DictionaryValue* MediaToValue(
+    const sync_pb::Media& media);
+
 SYNC_EXPORT_PRIVATE base::DictionaryValue* NigoriSpecificsToValue(
     const sync_pb::NigoriSpecifics& nigori_specifics);
 
@@ -211,6 +217,13 @@
 SYNC_EXPORT_PRIVATE base::DictionaryValue* SessionSpecificsToValue(
     const sync_pb::SessionSpecifics& session_specifics);
 
+SYNC_EXPORT_PRIVATE base::DictionaryValue* SyncedNotificationImageToValue(
+    const sync_pb::SyncedNotificationImage& image);
+
+SYNC_EXPORT_PRIVATE base::DictionaryValue*
+    SyncedNotificationProfileImageToValue(
+        const sync_pb::SyncedNotificationProfileImage& image);
+
 SYNC_EXPORT_PRIVATE base::DictionaryValue* ThemeSpecificsToValue(
     const sync_pb::ThemeSpecifics& theme_specifics);
 
diff --git a/sync/protocol/session_specifics.proto b/sync/protocol/session_specifics.proto
index f7b7d11..4bb8b3e 100644
--- a/sync/protocol/session_specifics.proto
+++ b/sync/protocol/session_specifics.proto
@@ -132,4 +132,6 @@
   optional BlockedState blocked_state = 18 [default=STATE_ALLOWED];
   // A list of category identifiers for the URL.
   repeated string content_pack_categories = 19;
+  // The status code from the last navigation.
+  optional int32 http_status_code = 20;
 }
diff --git a/sync/syncable/syncable_base_transaction.cc b/sync/syncable/syncable_base_transaction.cc
index d068bb9..a1d3e85 100644
--- a/sync/syncable/syncable_base_transaction.cc
+++ b/sync/syncable/syncable_base_transaction.cc
@@ -10,12 +10,13 @@
 namespace syncer {
 namespace syncable {
 
-Directory* BaseTransaction::directory() const {
-  return directory_;
+// static
+Id BaseTransaction::root_id() {
+  return Id();
 }
 
-Id BaseTransaction::root_id() const {
-  return Id();
+Directory* BaseTransaction::directory() const {
+  return directory_;
 }
 
 void BaseTransaction::Lock() {
diff --git a/sync/syncable/syncable_base_transaction.h b/sync/syncable/syncable_base_transaction.h
index e15a38b..58c7bfa 100644
--- a/sync/syncable/syncable_base_transaction.h
+++ b/sync/syncable/syncable_base_transaction.h
@@ -33,8 +33,9 @@
 
 class SYNC_EXPORT BaseTransaction {
  public:
+  static Id root_id();
+
   Directory* directory() const;
-  Id root_id() const;
 
   virtual ~BaseTransaction();
 
diff --git a/sync/tools/sync_client.cc b/sync/tools/sync_client.cc
index e382546..b677531 100644
--- a/sync/tools/sync_client.cc
+++ b/sync/tools/sync_client.cc
@@ -186,8 +186,7 @@
 };
 
 void LogUnrecoverableErrorContext() {
-  base::debug::StackTrace stack_trace;
-  stack_trace.PrintBacktrace();
+  base::debug::StackTrace().Print();
 }
 
 notifier::NotifierOptions ParseNotifierOptions(
diff --git a/third_party/cacheinvalidation/cacheinvalidation.gyp b/third_party/cacheinvalidation/cacheinvalidation.gyp
index bd8586f..166dbef 100644
--- a/third_party/cacheinvalidation/cacheinvalidation.gyp
+++ b/third_party/cacheinvalidation/cacheinvalidation.gyp
@@ -7,6 +7,7 @@
     # This library should build cleanly with the extra warnings turned on
     # for Chromium.
     'chromium_code': 1,
+    'emma_never_instrument': 1,
   },
   'targets': [
     # The C++ files generated from the cache invalidation protocol buffers.
diff --git a/third_party/libjingle/README.chromium b/third_party/libjingle/README.chromium
index aba9c46..3f97de9 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: 4595
+Revision: 4612
 License: BSD
 License File: source/talk/COPYING
 Security Critical: yes
diff --git a/third_party/libjingle/libjingle.gyp b/third_party/libjingle/libjingle.gyp
index 28e15c9..5757c4b 100644
--- a/third_party/libjingle/libjingle.gyp
+++ b/third_party/libjingle/libjingle.gyp
@@ -448,6 +448,8 @@
         '<(libjingle_source)/talk/p2p/base/transportchannelimpl.h',
         '<(libjingle_source)/talk/p2p/base/transportchannelproxy.cc',
         '<(libjingle_source)/talk/p2p/base/transportchannelproxy.h',
+        '<(libjingle_source)/talk/p2p/base/transportdescription.cc',
+        '<(libjingle_source)/talk/p2p/base/transportdescription.h',
         '<(libjingle_source)/talk/p2p/base/transportdescriptionfactory.cc',
         '<(libjingle_source)/talk/p2p/base/transportdescriptionfactory.h',
         '<(libjingle_source)/talk/p2p/base/turnport.cc',
diff --git a/third_party/libjingle/libjingle.target.darwin-arm.mk b/third_party/libjingle/libjingle.target.darwin-arm.mk
index 84046b6..856ef55 100644
--- a/third_party/libjingle/libjingle.target.darwin-arm.mk
+++ b/third_party/libjingle/libjingle.target.darwin-arm.mk
@@ -111,6 +111,7 @@
 	third_party/libjingle/source/talk/p2p/base/transport.cc \
 	third_party/libjingle/source/talk/p2p/base/transportchannel.cc \
 	third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc \
+	third_party/libjingle/source/talk/p2p/base/transportdescription.cc \
 	third_party/libjingle/source/talk/p2p/base/transportdescriptionfactory.cc \
 	third_party/libjingle/source/talk/p2p/base/turnport.cc \
 	third_party/libjingle/source/talk/p2p/client/basicportallocator.cc \
diff --git a/third_party/libjingle/libjingle.target.darwin-mips.mk b/third_party/libjingle/libjingle.target.darwin-mips.mk
index a90f250..ff3f5c1 100644
--- a/third_party/libjingle/libjingle.target.darwin-mips.mk
+++ b/third_party/libjingle/libjingle.target.darwin-mips.mk
@@ -111,6 +111,7 @@
 	third_party/libjingle/source/talk/p2p/base/transport.cc \
 	third_party/libjingle/source/talk/p2p/base/transportchannel.cc \
 	third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc \
+	third_party/libjingle/source/talk/p2p/base/transportdescription.cc \
 	third_party/libjingle/source/talk/p2p/base/transportdescriptionfactory.cc \
 	third_party/libjingle/source/talk/p2p/base/turnport.cc \
 	third_party/libjingle/source/talk/p2p/client/basicportallocator.cc \
diff --git a/third_party/libjingle/libjingle.target.darwin-x86.mk b/third_party/libjingle/libjingle.target.darwin-x86.mk
index 4f5fd1a..9d89602 100644
--- a/third_party/libjingle/libjingle.target.darwin-x86.mk
+++ b/third_party/libjingle/libjingle.target.darwin-x86.mk
@@ -111,6 +111,7 @@
 	third_party/libjingle/source/talk/p2p/base/transport.cc \
 	third_party/libjingle/source/talk/p2p/base/transportchannel.cc \
 	third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc \
+	third_party/libjingle/source/talk/p2p/base/transportdescription.cc \
 	third_party/libjingle/source/talk/p2p/base/transportdescriptionfactory.cc \
 	third_party/libjingle/source/talk/p2p/base/turnport.cc \
 	third_party/libjingle/source/talk/p2p/client/basicportallocator.cc \
diff --git a/third_party/libjingle/libjingle.target.linux-arm.mk b/third_party/libjingle/libjingle.target.linux-arm.mk
index 84046b6..856ef55 100644
--- a/third_party/libjingle/libjingle.target.linux-arm.mk
+++ b/third_party/libjingle/libjingle.target.linux-arm.mk
@@ -111,6 +111,7 @@
 	third_party/libjingle/source/talk/p2p/base/transport.cc \
 	third_party/libjingle/source/talk/p2p/base/transportchannel.cc \
 	third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc \
+	third_party/libjingle/source/talk/p2p/base/transportdescription.cc \
 	third_party/libjingle/source/talk/p2p/base/transportdescriptionfactory.cc \
 	third_party/libjingle/source/talk/p2p/base/turnport.cc \
 	third_party/libjingle/source/talk/p2p/client/basicportallocator.cc \
diff --git a/third_party/libjingle/libjingle.target.linux-mips.mk b/third_party/libjingle/libjingle.target.linux-mips.mk
index a90f250..ff3f5c1 100644
--- a/third_party/libjingle/libjingle.target.linux-mips.mk
+++ b/third_party/libjingle/libjingle.target.linux-mips.mk
@@ -111,6 +111,7 @@
 	third_party/libjingle/source/talk/p2p/base/transport.cc \
 	third_party/libjingle/source/talk/p2p/base/transportchannel.cc \
 	third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc \
+	third_party/libjingle/source/talk/p2p/base/transportdescription.cc \
 	third_party/libjingle/source/talk/p2p/base/transportdescriptionfactory.cc \
 	third_party/libjingle/source/talk/p2p/base/turnport.cc \
 	third_party/libjingle/source/talk/p2p/client/basicportallocator.cc \
diff --git a/third_party/libjingle/libjingle.target.linux-x86.mk b/third_party/libjingle/libjingle.target.linux-x86.mk
index 4f5fd1a..9d89602 100644
--- a/third_party/libjingle/libjingle.target.linux-x86.mk
+++ b/third_party/libjingle/libjingle.target.linux-x86.mk
@@ -111,6 +111,7 @@
 	third_party/libjingle/source/talk/p2p/base/transport.cc \
 	third_party/libjingle/source/talk/p2p/base/transportchannel.cc \
 	third_party/libjingle/source/talk/p2p/base/transportchannelproxy.cc \
+	third_party/libjingle/source/talk/p2p/base/transportdescription.cc \
 	third_party/libjingle/source/talk/p2p/base/transportdescriptionfactory.cc \
 	third_party/libjingle/source/talk/p2p/base/turnport.cc \
 	third_party/libjingle/source/talk/p2p/client/basicportallocator.cc \
diff --git a/third_party/libusb/README.chromium b/third_party/libusb/README.chromium
index b0e8ada..348d919 100644
--- a/third_party/libusb/README.chromium
+++ b/third_party/libusb/README.chromium
@@ -15,3 +15,4 @@
 - Exposing an API (libusb_interrupt_handle_event) to explicitly interrupt
   libusb_handle_event.
 - windows-build.patch has been applied.
+- darwin-sigfpe.patch has been applied.
diff --git a/third_party/libusb/darwin-sigfpe.patch b/third_party/libusb/darwin-sigfpe.patch
new file mode 100644
index 0000000..3c74db1
--- /dev/null
+++ b/third_party/libusb/darwin-sigfpe.patch
@@ -0,0 +1,22 @@
+diff --git a/libusb/os/darwin_usb.c b/libusb/os/darwin_usb.c
+index b0ee5b2..fe40899 100644
+--- a/libusb/os/darwin_usb.c
++++ b/libusb/os/darwin_usb.c
+@@ -1449,8 +1449,14 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) {
+ 
+   cInterface = &priv->interfaces[iface];
+ 
+-  (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
+-                                                 &transferType, &maxPacketSize, &interval);
++  ret = (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
++                                                       &transferType, &maxPacketSize, &interval);
++
++  if (ret) {
++    usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out",
++              darwin_error_str(ret), ret);
++    return darwin_to_libusb (ret);
++  }
+ 
+   if (0 != (transfer->length % maxPacketSize)) {
+     /* do not need a zero packet */
+
diff --git a/third_party/libusb/src/libusb/os/darwin_usb.c b/third_party/libusb/src/libusb/os/darwin_usb.c
index a24558c..3f315e8 100644
--- a/third_party/libusb/src/libusb/os/darwin_usb.c
+++ b/third_party/libusb/src/libusb/os/darwin_usb.c
@@ -1448,8 +1448,14 @@
 
   cInterface = &priv->interfaces[iface];
 
-  (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
-                                                 &transferType, &maxPacketSize, &interval);
+  ret = (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number,
+                                                       &transferType, &maxPacketSize, &interval);
+
+  if (ret) {
+    usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out",
+              darwin_error_str(ret), ret);
+    return darwin_to_libusb (ret);
+  }
 
   if (0 != (transfer->length % maxPacketSize)) {
     /* do not need a zero packet */
diff --git a/third_party/mt19937ar/OWNERS b/third_party/mt19937ar/OWNERS
index be8a9f8..b9e8da9 100644
--- a/third_party/mt19937ar/OWNERS
+++ b/third_party/mt19937ar/OWNERS
@@ -1 +1 @@
-asvitkine
+asvitkine@chromium.org
diff --git a/third_party/snappy/README.chromium b/third_party/snappy/README.chromium
index 358a5f3..f7e3742 100644
--- a/third_party/snappy/README.chromium
+++ b/third_party/snappy/README.chromium
@@ -1,7 +1,7 @@
 Name: Snappy: A fast compressor/decompressor
 Short Name: snappy
 URL: http://code.google.com/p/snappy/
-Version: r74
+Version: r80
 License: New BSD
 License File: src/COPYING
 Security Critical: yes
@@ -14,5 +14,4 @@
   * Suppress clang header-hygiene warning - https://code.google.com/p/snappy/issues/detail?id=70
   * Suppress MSVC signed/unsigned warning - https://code.google.com/p/snappy/issues/detail?id=71
   * Suppress MSVC x64 size_t warnings - https://code.google.com/p/snappy/issues/detail?id=75
-* {mac,linux}/config.h autogenerated configuration script
-* {mac,linux,win}/snappy-stubs-public.h autogenerated public headers
+* {mac,linux,win32}/snappy-stubs-public.h autogenerated public headers
diff --git a/third_party/snappy/linux/config.h b/third_party/snappy/linux/config.h
deleted file mode 100644
index d993481..0000000
--- a/third_party/snappy/linux/config.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/* config.h.  Generated from config.h.in by configure.  */
-/* config.h.in.  Generated from configure.ac by autoheader.  */
-
-/* Define if building universal (internal helper macro) */
-/* #undef AC_APPLE_UNIVERSAL_BUILD */
-
-/* Define to 1 if the compiler supports __builtin_ctz and friends. */
-#define HAVE_BUILTIN_CTZ 1
-
-/* Define to 1 if the compiler supports __builtin_expect. */
-#define HAVE_BUILTIN_EXPECT 1
-
-/* Define to 1 if you have the <dlfcn.h> header file. */
-#define HAVE_DLFCN_H 1
-
-/* Use the gflags package for command-line parsing. */
-/* #undef HAVE_GFLAGS */
-
-/* Defined when Google Test is available. */
-/* #undef HAVE_GTEST */
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#define HAVE_INTTYPES_H 1
-
-/* Define to 1 if you have the `fastlz' library (-lfastlz). */
-/* #undef HAVE_LIBFASTLZ */
-
-/* Define to 1 if you have the `lzf' library (-llzf). */
-/* #undef HAVE_LIBLZF */
-
-/* Define to 1 if you have the `lzo2' library (-llzo2). */
-/* #undef HAVE_LIBLZO2 */
-
-/* Define to 1 if you have the `quicklz' library (-lquicklz). */
-/* #undef HAVE_LIBQUICKLZ */
-
-/* Define to 1 if you have the `z' library (-lz). */
-#define HAVE_LIBZ 1
-
-/* Define to 1 if you have the <memory.h> header file. */
-#define HAVE_MEMORY_H 1
-
-/* Define to 1 if you have the <stddef.h> header file. */
-#define HAVE_STDDEF_H 1
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#define HAVE_STDINT_H 1
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#define HAVE_STDLIB_H 1
-
-/* Define to 1 if you have the <strings.h> header file. */
-#define HAVE_STRINGS_H 1
-
-/* Define to 1 if you have the <string.h> header file. */
-#define HAVE_STRING_H 1
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#define HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#define HAVE_SYS_TYPES_H 1
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#define HAVE_UNISTD_H 1
-
-/* Define to the sub-directory in which libtool stores uninstalled libraries.
-   */
-#define LT_OBJDIR ".libs/"
-
-/* Name of package */
-#define PACKAGE "snappy"
-
-/* Define to the address where bug reports for this package should be sent. */
-#define PACKAGE_BUGREPORT ""
-
-/* Define to the full name of this package. */
-#define PACKAGE_NAME "snappy"
-
-/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "snappy 1.0.5"
-
-/* Define to the one symbol short name of this package. */
-#define PACKAGE_TARNAME "snappy"
-
-/* Define to the home page for this package. */
-#define PACKAGE_URL ""
-
-/* Define to the version of this package. */
-#define PACKAGE_VERSION "1.0.5"
-
-/* Define to 1 if you have the ANSI C header files. */
-#define STDC_HEADERS 1
-
-/* Version number of package */
-#define VERSION "1.0.5"
-
-/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
-   significant byte first (like Motorola and SPARC, unlike Intel). */
-#if defined AC_APPLE_UNIVERSAL_BUILD
-# if defined __BIG_ENDIAN__
-#  define WORDS_BIGENDIAN 1
-# endif
-#else
-# ifndef WORDS_BIGENDIAN
-/* #  undef WORDS_BIGENDIAN */
-# endif
-#endif
diff --git a/third_party/snappy/linux/snappy-stubs-public.h b/third_party/snappy/linux/snappy-stubs-public.h
index 9ee4ca5..1095511 100644
--- a/third_party/snappy/linux/snappy-stubs-public.h
+++ b/third_party/snappy/linux/snappy-stubs-public.h
@@ -44,9 +44,13 @@
 #include <stddef.h>
 #endif
 
+#if 1
+#include <sys/uio.h>
+#endif
+
 #define SNAPPY_MAJOR 1
-#define SNAPPY_MINOR 0
-#define SNAPPY_PATCHLEVEL 5
+#define SNAPPY_MINOR 1
+#define SNAPPY_PATCHLEVEL 0
 #define SNAPPY_VERSION \
     ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL)
 
@@ -80,6 +84,15 @@
   TypeName(const TypeName&);               \
   void operator=(const TypeName&)
 
+#if 0
+// Windows does not have an iovec type, yet the concept is universally useful.
+// It is simple to define it ourselves, so we put it inside our own namespace.
+struct iovec {
+	void* iov_base;
+	size_t iov_len;
+};
+#endif
+
 }  // namespace snappy
 
 #endif  // UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_
diff --git a/third_party/snappy/mac/config.h b/third_party/snappy/mac/config.h
deleted file mode 100644
index d993481..0000000
--- a/third_party/snappy/mac/config.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/* config.h.  Generated from config.h.in by configure.  */
-/* config.h.in.  Generated from configure.ac by autoheader.  */
-
-/* Define if building universal (internal helper macro) */
-/* #undef AC_APPLE_UNIVERSAL_BUILD */
-
-/* Define to 1 if the compiler supports __builtin_ctz and friends. */
-#define HAVE_BUILTIN_CTZ 1
-
-/* Define to 1 if the compiler supports __builtin_expect. */
-#define HAVE_BUILTIN_EXPECT 1
-
-/* Define to 1 if you have the <dlfcn.h> header file. */
-#define HAVE_DLFCN_H 1
-
-/* Use the gflags package for command-line parsing. */
-/* #undef HAVE_GFLAGS */
-
-/* Defined when Google Test is available. */
-/* #undef HAVE_GTEST */
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#define HAVE_INTTYPES_H 1
-
-/* Define to 1 if you have the `fastlz' library (-lfastlz). */
-/* #undef HAVE_LIBFASTLZ */
-
-/* Define to 1 if you have the `lzf' library (-llzf). */
-/* #undef HAVE_LIBLZF */
-
-/* Define to 1 if you have the `lzo2' library (-llzo2). */
-/* #undef HAVE_LIBLZO2 */
-
-/* Define to 1 if you have the `quicklz' library (-lquicklz). */
-/* #undef HAVE_LIBQUICKLZ */
-
-/* Define to 1 if you have the `z' library (-lz). */
-#define HAVE_LIBZ 1
-
-/* Define to 1 if you have the <memory.h> header file. */
-#define HAVE_MEMORY_H 1
-
-/* Define to 1 if you have the <stddef.h> header file. */
-#define HAVE_STDDEF_H 1
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#define HAVE_STDINT_H 1
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#define HAVE_STDLIB_H 1
-
-/* Define to 1 if you have the <strings.h> header file. */
-#define HAVE_STRINGS_H 1
-
-/* Define to 1 if you have the <string.h> header file. */
-#define HAVE_STRING_H 1
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#define HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#define HAVE_SYS_TYPES_H 1
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#define HAVE_UNISTD_H 1
-
-/* Define to the sub-directory in which libtool stores uninstalled libraries.
-   */
-#define LT_OBJDIR ".libs/"
-
-/* Name of package */
-#define PACKAGE "snappy"
-
-/* Define to the address where bug reports for this package should be sent. */
-#define PACKAGE_BUGREPORT ""
-
-/* Define to the full name of this package. */
-#define PACKAGE_NAME "snappy"
-
-/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "snappy 1.0.5"
-
-/* Define to the one symbol short name of this package. */
-#define PACKAGE_TARNAME "snappy"
-
-/* Define to the home page for this package. */
-#define PACKAGE_URL ""
-
-/* Define to the version of this package. */
-#define PACKAGE_VERSION "1.0.5"
-
-/* Define to 1 if you have the ANSI C header files. */
-#define STDC_HEADERS 1
-
-/* Version number of package */
-#define VERSION "1.0.5"
-
-/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
-   significant byte first (like Motorola and SPARC, unlike Intel). */
-#if defined AC_APPLE_UNIVERSAL_BUILD
-# if defined __BIG_ENDIAN__
-#  define WORDS_BIGENDIAN 1
-# endif
-#else
-# ifndef WORDS_BIGENDIAN
-/* #  undef WORDS_BIGENDIAN */
-# endif
-#endif
diff --git a/third_party/snappy/mac/snappy-stubs-public.h b/third_party/snappy/mac/snappy-stubs-public.h
index 9ee4ca5..1095511 100644
--- a/third_party/snappy/mac/snappy-stubs-public.h
+++ b/third_party/snappy/mac/snappy-stubs-public.h
@@ -44,9 +44,13 @@
 #include <stddef.h>
 #endif
 
+#if 1
+#include <sys/uio.h>
+#endif
+
 #define SNAPPY_MAJOR 1
-#define SNAPPY_MINOR 0
-#define SNAPPY_PATCHLEVEL 5
+#define SNAPPY_MINOR 1
+#define SNAPPY_PATCHLEVEL 0
 #define SNAPPY_VERSION \
     ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL)
 
@@ -80,6 +84,15 @@
   TypeName(const TypeName&);               \
   void operator=(const TypeName&)
 
+#if 0
+// Windows does not have an iovec type, yet the concept is universally useful.
+// It is simple to define it ourselves, so we put it inside our own namespace.
+struct iovec {
+	void* iov_base;
+	size_t iov_len;
+};
+#endif
+
 }  // namespace snappy
 
 #endif  // UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_
diff --git a/third_party/snappy/win32/snappy-stubs-public.h b/third_party/snappy/win32/snappy-stubs-public.h
index 5b169c1..3915c65 100644
--- a/third_party/snappy/win32/snappy-stubs-public.h
+++ b/third_party/snappy/win32/snappy-stubs-public.h
@@ -44,9 +44,13 @@
 #include <stddef.h>
 #endif
 
+#if 0
+#include <sys/uio.h>
+#endif
+
 #define SNAPPY_MAJOR 1
-#define SNAPPY_MINOR 0
-#define SNAPPY_PATCHLEVEL 5
+#define SNAPPY_MINOR 1
+#define SNAPPY_PATCHLEVEL 0
 #define SNAPPY_VERSION \
     ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL)
 
@@ -80,6 +84,20 @@
   TypeName(const TypeName&);               \
   void operator=(const TypeName&)
 
+#if 1
+// Windows does not have an iovec type, yet the concept is universally useful.
+// It is simple to define it ourselves, so we put it inside our own namespace.
+struct iovec {
+  void* iov_base;
+  size_t iov_len;
+};
+#endif
+
+// MSVC does not have ssize_t by default; autoconf suggests defining as `int'.
+// Would be in config.h, but Chromium does not use automake/autoconf.
+#include <BaseTsd.h>
+typedef SSIZE_T ssize_t;
+
 }  // namespace snappy
 
 #endif  // UTIL_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_
diff --git a/third_party/widevine/cdm/widevine_cdm_common.h b/third_party/widevine/cdm/widevine_cdm_common.h
index 7ea705c..2b3ef0d 100644
--- a/third_party/widevine/cdm/widevine_cdm_common.h
+++ b/third_party/widevine/cdm/widevine_cdm_common.h
@@ -19,11 +19,11 @@
 // Will be parsed as HTML.
 const char kWidevineCdmDescription[] =
     "Enables Widevine licenses for playback of HTML audio/video content.";
+
 #if defined(ENABLE_PEPPER_CDMS)
 const char kWidevineCdmPluginMimeType[] = "application/x-ppapi-widevine-cdm";
 const char kWidevineCdmPluginMimeTypeDescription[] =
     "Widevine Content Decryption Module";
-#endif
 
 // File name of the CDM on different platforms.
 const char kWidevineCdmFileName[] =
@@ -35,7 +35,6 @@
     "libwidevinecdm.so";
 #endif
 
-#if defined(ENABLE_PEPPER_CDMS)
 // File name of the adapter on different platforms.
 const char kWidevineCdmAdapterFileName[] =
 #if defined(OS_MACOSX)
@@ -45,12 +44,12 @@
 #else  // OS_LINUX, etc.
     "libwidevinecdmadapter.so";
 #endif
-#endif  // defined(ENABLE_PEPPER_CDMS)
 
 
-#if defined(ENABLE_PEPPER_CDMS) && (defined(OS_MACOSX) || defined(OS_WIN))
+#if defined(OS_MACOSX) || defined(OS_WIN)
 // CDM is installed by the component installer instead of the Chrome installer.
 #define WIDEVINE_CDM_IS_COMPONENT
-#endif
+#endif  // defined(OS_MACOSX) || defined(OS_WIN)
+#endif  // defined(ENABLE_PEPPER_CDMS)
 
 #endif  // WIDEVINE_CDM_WIDEVINE_CDM_COMMON_H_
diff --git a/tools/android/forwarder2/device_controller.cc b/tools/android/forwarder2/device_controller.cc
index 87d0e17..a6d370b 100644
--- a/tools/android/forwarder2/device_controller.cc
+++ b/tools/android/forwarder2/device_controller.cc
@@ -7,7 +7,7 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_loop_proxy.h"
diff --git a/tools/android/forwarder2/forwarder.gyp b/tools/android/forwarder2/forwarder.gyp
index fdc19aa..3f84e43 100644
--- a/tools/android/forwarder2/forwarder.gyp
+++ b/tools/android/forwarder2/forwarder.gyp
@@ -61,6 +61,7 @@
       'toolsets': ['host'],
       'dependencies': [
         '../../../base/base.gyp:base',
+        '../../../build/linux/system.gyp:x11',
         '../common/common.gyp:android_tools_common',
       ],
       'include_dirs': [
@@ -75,8 +76,8 @@
         'host_forwarder_main.cc',
         'pipe_notifier.cc',
         'socket.cc',
-        # TODO(pliard): Remove this. This is needed to avoid undefined
-        # references at link time.
+        # TODO(pliard): Remove this and x11 dependency above. This is needed
+        # to avoid undefined references at link time.
         '../../../base/message_loop/message_pump_glib.cc',
         '../../../base/message_loop/message_pump_gtk.cc',
       ],
diff --git a/tools/android/memdump/memdump.cc b/tools/android/memdump/memdump.cc
index 26035b2..42ade35 100644
--- a/tools/android/memdump/memdump.cc
+++ b/tools/android/memdump/memdump.cc
@@ -19,7 +19,7 @@
 #include "base/base64.h"
 #include "base/basictypes.h"
 #include "base/bind.h"
-#include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
 #include "base/containers/hash_tables.h"
 #include "base/file_util.h"
 #include "base/logging.h"
diff --git a/tools/android/memdump/memreport.py b/tools/android/memdump/memreport.py
index 80e12a8..b477535 100755
--- a/tools/android/memdump/memreport.py
+++ b/tools/android/memdump/memreport.py
@@ -202,7 +202,7 @@
 
   def _CollectStats(count):
     adb = android_commands.AndroidCommands()
-    pid_list = adb.ExtractPid('com.google.android.apps.chrome')
+    pid_list = adb.ExtractPid(package_name)
     memdump = adb.RunShellCommand('/data/local/tmp/memdump ' +
                                   ' '.join(pid_list))
     process_stats = _CollectMemoryStats(memdump,
diff --git a/tools/bisect-builds.py b/tools/bisect-builds.py
index 3e8254b..38fe6a4 100755
--- a/tools/bisect-builds.py
+++ b/tools/bisect-builds.py
@@ -60,13 +60,15 @@
 class PathContext(object):
   """A PathContext is used to carry the information used to construct URLs and
   paths when dealing with the storage server and archives."""
-  def __init__(self, platform, good_revision, bad_revision, is_official):
+  def __init__(self, platform, good_revision, bad_revision, is_official,
+               is_aura):
     super(PathContext, self).__init__()
     # Store off the input parameters.
     self.platform = platform  # What's passed in to the '-a/--archive' option.
     self.good_revision = good_revision
     self.bad_revision = bad_revision
     self.is_official = is_official
+    self.is_aura = is_aura
 
     # The name of the ZIP file in a revision directory on the server.
     self.archive_name = None
@@ -100,7 +102,10 @@
         self._listing_platform_dir = 'mac/'
         self._binary_name = 'Google Chrome.app/Contents/MacOS/Google Chrome'
       elif self.platform == 'win':
-        self._listing_platform_dir = 'win/'
+        if self.is_aura:
+          self._listing_platform_dir = 'win-aura/'
+        else:
+          self._listing_platform_dir = 'win/'
     else:
       if self.platform in ('linux', 'linux64', 'linux-arm'):
         self.archive_name = 'chrome-linux.zip'
@@ -144,6 +149,14 @@
     that is used to run the executable."""
     return os.path.join(self._archive_extract_dir, self._binary_name)
 
+  def IsAuraBuild(self, build):
+    """Check the given build is Aura."""
+    return build.split('.')[3] == '1'
+
+  def IsASANBuild(self, build):
+    """Check the given build is ASAN build."""
+    return build.split('.')[3] == '2'
+
   def ParseDirectoryIndex(self):
     """Parses the Google Storage directory listing into a list of revision
     numbers."""
@@ -233,7 +246,17 @@
         if build_number > maxrev:
           break
         if build_number >= minrev:
-          final_list.append(str(build_number))
+          # If we are bisecting Aura, we want to include only builds which
+          # ends with ".1".
+          if self.is_aura:
+            if self.IsAuraBuild(str(build_number)):
+              final_list.append(str(build_number))
+          # If we are bisecting only official builds (without --aura),
+          # we can not include builds which ends with '.1' or '.2' since
+          # they have different folder hierarchy inside.
+          elif (not self.IsAuraBuild(str(build_number)) and
+                not self.IsASANBuild(str(build_number))):
+            final_list.append(str(build_number))
       except urllib.HTTPError, e:
         pass
     return final_list
@@ -395,6 +418,7 @@
 
 def Bisect(platform,
            official_builds,
+           is_aura,
            good_rev=0,
            bad_rev=0,
            num_runs=1,
@@ -433,7 +457,7 @@
   if not profile:
     profile = 'profile'
 
-  context = PathContext(platform, good_rev, bad_rev, official_builds)
+  context = PathContext(platform, good_rev, bad_rev, official_builds, is_aura)
   cwd = os.getcwd()
 
 
@@ -656,6 +680,12 @@
                     'Defaults to "%p %a". Note that any extra paths ' +
                     'specified should be absolute.',
                     default = '%p %a');
+  parser.add_option('--aura',
+                    dest='aura',
+                    action='store_true',
+                    default=False,
+                    help='Allow the script to bisect aura builds')
+
   (opts, args) = parser.parse_args()
 
   if opts.archive is None:
@@ -664,8 +694,14 @@
     parser.print_help()
     return 1
 
+  if opts.aura:
+    if opts.archive != 'win' or not opts.official_builds:
+      print 'Error: Aura is supported only on Windows platform '\
+            'and official builds.'
+      return 1
+
   # Create the context. Initialize 0 for the revisions as they are set below.
-  context = PathContext(opts.archive, 0, 0, opts.official_builds)
+  context = PathContext(opts.archive, 0, 0, opts.official_builds, opts.aura)
   # Pick a starting point, try to get HEAD for this.
   if opts.bad:
     bad_rev = opts.bad
@@ -694,8 +730,8 @@
     return 1
 
   (min_chromium_rev, max_chromium_rev) = Bisect(
-      opts.archive, opts.official_builds, good_rev, bad_rev, opts.times,
-      opts.command, args, opts.profile)
+      opts.archive, opts.official_builds, opts.aura, good_rev, bad_rev,
+      opts.times, opts.command, args, opts.profile)
 
   # Get corresponding blink revisions.
   try:
diff --git a/tools/bisect-manual-test.py b/tools/bisect-manual-test.py
index d01adc9..81519cb 100755
--- a/tools/bisect-manual-test.py
+++ b/tools/bisect-manual-test.py
@@ -42,7 +42,7 @@
            'Starts browser with an optional url and asks user whether '
            'revision is good or bad.\n')
 
-  options = browser_options.BrowserOptions()
+  options = browser_options.BrowserFinderOptions()
   parser = options.CreateParser(usage)
   options, args = parser.parse_args()
 
diff --git a/tools/bisect-perf-regression.py b/tools/bisect-perf-regression.py
index 9eaf50b..716e85f 100755
--- a/tools/bisect-perf-regression.py
+++ b/tools/bisect-perf-regression.py
@@ -75,22 +75,25 @@
     "depends" : None,
     "from" : 'chromium'
   },
+  'angle' : {
+    "src" : "src/third_party/angle_dx11",
+    "recurse" : True,
+    "depends" : None,
+    "from" : 'chromium'
+  },
   'v8' : {
     "src" : "src/v8",
     "recurse" : True,
     "depends" : None,
-    # Bisecting into v8 is broken at the moment.
-    # crbug.com/274818
-    #"build_with": 'v8_bleeding_edge',
     "from" : 'chromium',
     "custom_deps": bisect_utils.GCLIENT_CUSTOM_DEPS_V8
   },
   'v8_bleeding_edge' : {
     "src" : "src/v8_bleeding_edge",
-    "recurse" : False,
+    "recurse" : True,
     "depends" : None,
     "svn": "https://v8.googlecode.com/svn/branches/bleeding_edge",
-    "from" : 'chromium'
+    "from" : 'v8',
   },
   'skia/src' : {
     "src" : "src/third_party/skia/src",
@@ -809,7 +812,8 @@
       # The working directory of each depot is just the path to the depot, but
       # since we're already in 'src', we can skip that part.
 
-      self.depot_cwd[d] = self.src_cwd + DEPOT_DEPS_NAME[d]['src'][3:]
+      self.depot_cwd[d] = os.path.join(
+          self.src_cwd, DEPOT_DEPS_NAME[d]['src'][4:])
 
   def PerformCleanup(self):
     """Performs cleanup when script is finished."""
@@ -856,7 +860,7 @@
 
     return revision_work_list
 
-  def Get3rdPartyRevisionsFromCurrentRevision(self, depot):
+  def Get3rdPartyRevisionsFromCurrentRevision(self, depot, revision):
     """Parses the DEPS file to determine WebKit/v8/etc... versions.
 
     Returns:
@@ -923,6 +927,39 @@
           os.chdir(cwd)
 
           results['chromium'] = output.strip()
+    elif depot == 'v8':
+      results['v8_bleeding_edge'] = None
+
+      svn_revision = self.source_control.SVNFindRev(revision)
+
+      if IsStringInt(svn_revision):
+        # V8 is tricky to bisect, in that there are only a few instances when
+        # we can dive into bleeding_edge and get back a meaningful result.
+        # Try to detect a V8 "business as usual" case, which is when:
+        #  1. trunk revision N has description "Version X.Y.Z"
+        #  2. bleeding_edge revision (N-1) has description "Prepare push to
+        #     trunk. Now working on X.Y.(Z+1)."
+        self.ChangeToDepotWorkingDirectory(depot)
+
+        revision_info = self.source_control.QueryRevisionInfo(revision)
+
+        version_re = re.compile("Version (?P<values>[0-9,.]+)")
+
+        regex_results = version_re.search(revision_info['subject'])
+
+        if regex_results:
+          version = regex_results.group('values')
+
+          self.ChangeToDepotWorkingDirectory('v8_bleeding_edge')
+
+          git_revision = self.source_control.ResolveToRevision(
+              int(svn_revision) - 1, 'v8_bleeding_edge', -1)
+
+          if git_revision:
+            revision_info = self.source_control.QueryRevisionInfo(git_revision)
+
+            if 'Prepare push to trunk' in revision_info['subject']:
+              results['v8_bleeding_edge'] = git_revision
 
     return results
 
@@ -1364,11 +1401,11 @@
           results = self.RunPerformanceTestAndParseResults(command_to_run,
                                                            metric)
 
-          if results[1] == 0 and sync_client:
+          if results[1] == 0:
             external_revisions = self.Get3rdPartyRevisionsFromCurrentRevision(
-                depot)
+                depot, revision)
 
-            if external_revisions:
+            if not external_revisions is None:
               return (results[0], results[1], external_revisions)
             else:
               return ('Failed to parse DEPS file for external revisions.',
@@ -1419,6 +1456,37 @@
                     ' was added without proper support?' %\
                     (depot_name,)
 
+  def FindNextDepotToBisect(self, current_revision, min_revision_data,
+      max_revision_data):
+    """Given the state of the bisect, decides which depot the script should
+    dive into next (if any).
+
+    Args:
+      current_revision: Current revision synced to.
+      min_revision_data: Data about the earliest revision in the bisect range.
+      max_revision_data: Data about the latest revision in the bisect range.
+
+    Returns:
+      The depot to bisect next, or None.
+    """
+    external_depot = None
+    for current_depot in DEPOT_NAMES:
+      if not (DEPOT_DEPS_NAME[current_depot]["recurse"] and
+          DEPOT_DEPS_NAME[current_depot]['from'] ==
+          min_revision_data['depot']):
+        continue
+
+      if (min_revision_data['external'][current_depot] ==
+          max_revision_data['external'][current_depot]):
+        continue
+
+      if (min_revision_data['external'][current_depot] and
+          max_revision_data['external'][current_depot]):
+        external_depot = current_depot
+        break
+
+    return external_depot
+
   def PrepareToBisectOnDepot(self,
                              current_depot,
                              end_revision,
@@ -1441,57 +1509,38 @@
     """
     # Change into working directory of external library to run
     # subsequent commands.
-    old_cwd = os.getcwd()
-    os.chdir(self.depot_cwd[current_depot])
+    self.ChangeToDepotWorkingDirectory(current_depot)
 
     # V8 (and possibly others) is merged in periodically. Bisecting
     # this directory directly won't give much good info.
-    if DEPOT_DEPS_NAME[current_depot].has_key('build_with'):
-      if (DEPOT_DEPS_NAME[current_depot].has_key('custom_deps') and
-          previous_depot == 'chromium'):
-        config_path = os.path.join(self.src_cwd, '..')
-        if bisect_utils.RunGClientAndCreateConfig(self.opts,
-            DEPOT_DEPS_NAME[current_depot]['custom_deps'], cwd=config_path):
-          return []
-        if bisect_utils.RunGClient(
-            ['sync', '--revision', previous_revision], cwd=self.src_cwd):
-          return []
+    if DEPOT_DEPS_NAME[current_depot].has_key('custom_deps'):
+      config_path = os.path.join(self.src_cwd, '..')
+      if bisect_utils.RunGClientAndCreateConfig(self.opts,
+          DEPOT_DEPS_NAME[current_depot]['custom_deps'], cwd=config_path):
+        return []
+      if bisect_utils.RunGClient(
+          ['sync', '--revision', previous_revision], cwd=self.src_cwd):
+        return []
 
-      new_depot = DEPOT_DEPS_NAME[current_depot]['build_with']
+    if current_depot == 'v8_bleeding_edge':
+      self.ChangeToDepotWorkingDirectory('chromium')
 
-      svn_start_revision = self.source_control.SVNFindRev(start_revision)
-      svn_end_revision = self.source_control.SVNFindRev(end_revision)
-      os.chdir(self.depot_cwd[new_depot])
+      shutil.move('v8', 'v8.bak')
+      shutil.move('v8_bleeding_edge', 'v8')
 
-      start_revision = self.source_control.ResolveToRevision(
-          svn_start_revision, new_depot, -1000)
-      end_revision = self.source_control.ResolveToRevision(
-          svn_end_revision, new_depot, -1000)
+      self.cleanup_commands.append(['mv', 'v8', 'v8_bleeding_edge'])
+      self.cleanup_commands.append(['mv', 'v8.bak', 'v8'])
 
-      old_name = DEPOT_DEPS_NAME[current_depot]['src'][4:]
-      new_name = DEPOT_DEPS_NAME[new_depot]['src'][4:]
+      self.depot_cwd['v8_bleeding_edge'] = os.path.join(self.src_cwd, 'v8')
+      self.depot_cwd['v8'] = os.path.join(self.src_cwd, 'v8.bak')
 
-      os.chdir(self.src_cwd)
-
-      shutil.move(old_name, old_name + '.bak')
-      shutil.move(new_name, old_name)
-      os.chdir(self.depot_cwd[current_depot])
-
-      self.cleanup_commands.append(['mv', old_name, new_name])
-      self.cleanup_commands.append(['mv', old_name + '.bak', old_name])
-
-      os.chdir(self.depot_cwd[current_depot])
-
-    if current_depot == 'v8':
-      self.warnings.append('Unfortunately, V8 bisection is broken at '
-          'the moment. The script won\'t be able to narrow down the range '
-          'past major releases of V8.')
+      self.ChangeToDepotWorkingDirectory(current_depot)
 
     depot_revision_list = self.GetRevisionList(current_depot,
                                                end_revision,
                                                start_revision)
 
-    os.chdir(old_cwd)
+    self.ChangeToDepotWorkingDirectory('chromium')
 
     return depot_revision_list
 
@@ -1816,32 +1865,29 @@
         max_revision_data = revision_data[revision_list[max_revision]]
 
         if max_revision - min_revision <= 1:
+          current_depot = min_revision_data['depot']
           if min_revision_data['passed'] == '?':
             next_revision_index = min_revision
           elif max_revision_data['passed'] == '?':
             next_revision_index = max_revision
-          elif min_revision_data['depot'] == 'chromium' or\
-               min_revision_data['depot'] == 'cros':
+          elif current_depot in ['cros', 'chromium', 'v8']:
+            previous_revision = revision_list[min_revision]
             # If there were changes to any of the external libraries we track,
             # should bisect the changes there as well.
-            external_depot = None
-
-            for current_depot in DEPOT_NAMES:
-              if DEPOT_DEPS_NAME[current_depot]["recurse"] and\
-                 DEPOT_DEPS_NAME[current_depot]['from'] ==\
-                 min_revision_data['depot']:
-                if min_revision_data['external'][current_depot] !=\
-                   max_revision_data['external'][current_depot]:
-                  external_depot = current_depot
-                  break
+            external_depot = self.FindNextDepotToBisect(
+                previous_revision, min_revision_data, max_revision_data)
 
             # If there was no change in any of the external depots, the search
             # is over.
             if not external_depot:
+              if current_depot == 'v8':
+                self.warnings.append('Unfortunately, V8 bisection couldn\'t '
+                    'continue any further. The script can only bisect into '
+                    'V8\'s bleeding_edge repository if both the current and '
+                    'previous revisions in trunk map directly to revisions in '
+                    'bleeding_edge.')
               break
 
-            previous_revision = revision_list[min_revision]
-
             earliest_revision = max_revision_data['external'][external_depot]
             latest_revision = min_revision_data['external'][external_depot]
 
@@ -1854,7 +1900,7 @@
             if not new_revision_list:
               results['error'] = 'An error occurred attempting to retrieve'\
                                  ' revision range: [%s..%s]' %\
-                                 (depot_rev_range[1], depot_rev_range[0])
+                                 (earliest_revision, latest_revision)
               return results
 
             self.AddRevisionsIntoRevisionData(new_revision_list,
@@ -1969,11 +2015,11 @@
 
       if type(build_status) is bool:
         if build_status:
-          build_status = 'Passed'
+          build_status = 'Good'
         else:
-          build_status = 'Failed'
+          build_status = 'Bad'
 
-      print '  %8s  %40s  %s' % (current_data['depot'],
+      print '  %20s  %40s  %s' % (current_data['depot'],
                                  current_id, build_status)
     print
 
@@ -1996,7 +2042,7 @@
 
     print
     print 'Tested commits:'
-    print '  %8s  %40s  %12s %14s %13s' % ('Depot'.center(8, ' '),
+    print '  %20s  %40s  %12s %14s %13s' % ('Depot'.center(20, ' '),
         'Commit SHA'.center(40, ' '), 'Mean'.center(12, ' '),
         'Std. Error'.center(14, ' '), 'State'.center(13, ' '))
     state = 0
@@ -2016,10 +2062,10 @@
 
         std_error = ('+-%.02f' %
             current_data['value']['std_err']).center(14, ' ')
-        mean = ('+-%.02f' %
-            current_data['value']['mean']).center(12, ' ')
-        print '  %8s  %40s  %12s %14s %13s' % (
-            current_data['depot'], current_id, mean, std_error, state_str)
+        mean = ('%.02f' % current_data['value']['mean']).center(12, ' ')
+        print '  %20s  %40s  %12s %14s %13s' % (
+            current_data['depot'].center(20, ' '), current_id, mean,
+            std_error, state_str)
 
     if last_broken_revision != None and first_working_revision != None:
       # Give a "confidence" in the bisect. At the moment we use how distinct the
diff --git a/tools/bisect_utils.py b/tools/bisect_utils.py
index bdb1675..e9867dc 100644
--- a/tools/bisect_utils.py
+++ b/tools/bisect_utils.py
@@ -426,6 +426,19 @@
   return True
 
 
+def CheckIfBisectDepotExists(opts):
+  """Checks if the bisect directory already exists.
+
+  Args:
+    opts: The options parsed from the command line through parse_args().
+
+  Returns:
+    Returns True if it exists.
+  """
+  path_to_dir = os.path.join(opts.working_directory, 'bisect', 'src')
+  return os.path.exists(path_to_dir)
+
+
 def CreateBisectDirectoryAndSetupDepot(opts, custom_deps):
   """Sets up a subdirectory 'bisect' and then retrieves a copy of the depot
   there using gclient.
diff --git a/tools/clang/scripts/package.sh b/tools/clang/scripts/package.sh
index 582125b..f57a28b 100755
--- a/tools/clang/scripts/package.sh
+++ b/tools/clang/scripts/package.sh
@@ -62,11 +62,12 @@
 cp "${LLVM_LIB_DIR}/libprofile_rt.${SO_EXT}" $PDIR/lib
 
 # Copy built-in headers (lib/clang/3.2/include).
-# libcompiler-rt puts all kinds of libraries there too, but we want only ASan.
+# libcompiler-rt puts all kinds of libraries there too, but we want only some.
 if [ "$(uname -s)" = "Darwin" ]; then
-  # Keep only Release+Asserts/lib/clang/3.2/lib/darwin/libclang_rt.asan_osx.a
-  find "${LLVM_LIB_DIR}/clang" -type f -path '*lib/darwin*' | grep -v asan | \
-       xargs rm
+  # Keep only
+  # Release+Asserts/lib/clang/*/lib/darwin/libclang_rt.{asan,profile}_osx*
+  find "${LLVM_LIB_DIR}/clang" -type f -path '*lib/darwin*' \
+       ! -name '*asan_osx*' ! -name '*profile_osx*' | xargs rm
   # Fix LC_ID_DYLIB for the ASan dynamic library to be relative to
   # @executable_path.
   # TODO(glider): this is transitional. We'll need to fix the dylib name
@@ -76,11 +77,9 @@
   install_name_tool -id @executable_path/${ASAN_DYLIB_NAME} "${ASAN_DYLIB}"
 else
   # Keep only
-  # Release+Asserts/lib/clang/3.2/lib/linux/libclang_rt.{a,t,m}san-x86_64.a
-  # TODO(thakis): Make sure the 32bit version of ASan runtime is kept too once
-  # that's built. TSan and MSan runtimes exist only for 64 bits.
-  find "${LLVM_LIB_DIR}/clang" -type f -path '*lib/linux*' | \
-       grep -v "asan\|tsan\|msan" | xargs rm
+  # Release+Asserts/lib/clang/*/lib/linux/libclang_rt.{[atm]san,profile}-*.a
+  find "${LLVM_LIB_DIR}/clang" -type f -path '*lib/linux*' \
+       ! -name '*[atm]san*' ! -name '*profile*' | xargs rm
 fi
 
 cp -R "${LLVM_LIB_DIR}/clang" $PDIR/lib
diff --git a/tools/clang/scripts/update.sh b/tools/clang/scripts/update.sh
index ea6f2b2..1c49d74 100755
--- a/tools/clang/scripts/update.sh
+++ b/tools/clang/scripts/update.sh
@@ -8,7 +8,7 @@
 # Do NOT CHANGE this if you don't know what you're doing -- see
 # https://code.google.com/p/chromium/wiki/UpdatingClang
 # Reverting problematic clang rolls is safe, though.
-CLANG_REVISION=186332
+CLANG_REVISION=188423
 
 THIS_DIR="$(dirname "${0}")"
 LLVM_DIR="${THIS_DIR}/../../../third_party/llvm"
diff --git a/tools/deep_memory_profiler/accumulate.py b/tools/deep_memory_profiler/accumulate.py
index 6998dc0..0a8efd2 100755
--- a/tools/deep_memory_profiler/accumulate.py
+++ b/tools/deep_memory_profiler/accumulate.py
@@ -150,7 +150,7 @@
   return flattened_labels, flattened_table
 
 
-def output_csv(output, category_trees, data, first_time):
+def output_csv(output, category_trees, data, first_time, output_exponent):
   flattened_labels, flattened_table = flatten_all_category_trees(category_trees)
 
   sorted_flattened_labels = sorted(flattened_labels)
@@ -159,7 +159,12 @@
     values = [str(data['snapshots'][index]['time'] - first_time)]
     for label in sorted_flattened_labels:
       if label in row:
-        values.append(str(row[label] / 1024.0 / 1024.0))
+        divisor = 1
+        if output_exponent.upper() == 'K':
+          divisor = 1024.0
+        elif output_exponent.upper() == 'M':
+          divisor = 1024.0 * 1024.0
+        values.append(str(row[label] / divisor))
       else:
         values.append('0')
     print >> output, ','.join(values)
@@ -194,12 +199,16 @@
     print >> output, ''
 
 
-def do_main(cat_input, output, template_label, output_format):
+def do_main(cat_input, output, template_label, output_format, output_exponent):
   """Does the main work: accumulate for every snapshot and print a result."""
   if output_format not in ['csv', 'json', 'tree']:
     raise NotImplementedError('The output format \"%s\" is not implemented.' %
                               output_format)
 
+  if output_exponent.upper() not in ['B', 'K', 'M']:
+    raise NotImplementedError('The exponent \"%s\" is not implemented.' %
+                              output_exponent)
+
   data = json.loads(cat_input.read(), object_pairs_hook=OrderedDict)
 
   templates = data['templates']
@@ -229,7 +238,7 @@
     category_trees.append(category_tree)
 
   if output_format == 'csv':
-    output_csv(output, category_trees, data, first_time)
+    output_csv(output, category_trees, data, first_time, output_exponent)
   elif output_format == 'json':
     output_json(output, category_trees, data, first_time, template_label)
   elif output_format == 'tree':
@@ -250,9 +259,12 @@
                     help='Apply TEMPLATE to list up.')
   parser.add_option('-f', '--format', dest='format', default='csv',
                     help='Specify the output format: csv, json or tree.')
+  parser.add_option('-e', '--exponent', dest='exponent', default='M',
+                    help='Specify B (bytes), K (kilobytes) or M (megabytes).')
 
   options, _ = parser.parse_args(sys.argv)
-  do_main(sys.stdin, sys.stdout, options.template, options.format)
+  do_main(sys.stdin, sys.stdout,
+          options.template, options.format, options.exponent)
 
 
 if __name__ == '__main__':
diff --git a/tools/deep_memory_profiler/lib/sorter.py b/tools/deep_memory_profiler/lib/sorter.py
index bd56292..1f083fc 100644
--- a/tools/deep_memory_profiler/lib/sorter.py
+++ b/tools/deep_memory_profiler/lib/sorter.py
@@ -16,10 +16,16 @@
 BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 
 DEFAULT_SORTERS = [
-    os.path.join(BASE_PATH, 'sorter.malloc-component.json'),
-    os.path.join(BASE_PATH, 'sorter.malloc-type.json'),
-    os.path.join(BASE_PATH, 'sorter.vm-map.json'),
-    os.path.join(BASE_PATH, 'sorter.vm-sharing.json'),
+    os.path.join(BASE_PATH, 'sorters', 'malloc.browser-module.json'),
+    os.path.join(BASE_PATH, 'sorters', 'malloc.renderer-module.json'),
+    os.path.join(BASE_PATH, 'sorters', 'malloc.type.json'),
+    os.path.join(BASE_PATH, 'sorters', 'malloc.WebCore.json'),
+    os.path.join(BASE_PATH, 'sorters', 'vm.Android-specific.json'),
+    os.path.join(BASE_PATH, 'sorters', 'vm.base.json'),
+    os.path.join(BASE_PATH, 'sorters', 'vm.GPU.json'),
+    os.path.join(BASE_PATH, 'sorters', 'vm.sharing.json'),
+    os.path.join(BASE_PATH, 'sorters', 'vm.Skia.json'),
+    os.path.join(BASE_PATH, 'sorters', 'vm.V8.json'),
     ]
 
 DEFAULT_TEMPLATES = os.path.join(BASE_PATH, 'templates.json')
@@ -439,7 +445,9 @@
     if not default:
       default = DEFAULT_SORTERS
     self._sorters = {}
+    LOGGER.info('Loading sorters.')
     for filename in default + additional:
+      LOGGER.info('  Loading a sorter "%s".' % filename)
       sorter = AbstractSorter.load(filename)
       if sorter.world not in self._sorters:
         self._sorters[sorter.world] = []
diff --git a/tools/deep_memory_profiler/sorters/malloc.WebCore.json b/tools/deep_memory_profiler/sorters/malloc.WebCore.json
new file mode 100644
index 0000000..e1f396d
--- /dev/null
+++ b/tools/deep_memory_profiler/sorters/malloc.WebCore.json
@@ -0,0 +1,89 @@
+{
+  "type": "sorter",
+  "version": 1,
+  "world": "malloc",
+  "name": "WebCore",
+  "order": {},
+  "rules": [
+    {
+      "name": "RenderArena",
+      "backtrace_function": ".*WebCore::RenderArena::allocate.*"
+    },
+    {
+      "name": "RenderStyle",
+      "backtrace_function": ".*WebCore::RenderStyle::(create|clone).*"
+    },
+    {
+      "name": "SharedBuffer",
+      "backtrace_function": ".*WebCore::SharedBuffer::(create|buffer|append).*"
+    },
+    {
+      "name": "XMLHttpRequest",
+      "backtrace_function": ".*WebCore::XMLHttpRequest::create .*"
+    },
+    {
+      "name": "XMLHttpRequest-received",
+      "backtrace_function": ".*WebCore::XMLHttpRequest::didReceiveData.*"
+    },
+    {
+      "name": "DocumentWriter-addData",
+      "backtrace_function": ".*WebCore::DocumentWriter::addData.*"
+    },
+    {
+      "name": "Element-create",
+      "backtrace_function": ".*WebCore::(HTML[A-Za-z0-9_]*Element|Text|Comment|HTMLDocument|CSSStyleRule|Attribute|DOMWindow)::create .*"
+    },
+    {
+      "name": "Element-factory",
+      "backtrace_function": ".*WebCore::HTML[a-zA-Z0-9_]*Factory::create[a-zA-Z0-9_]*Element.*"
+    },
+    {
+      "name": "ElementWrapper",
+      "backtrace_function": ".*WebCore::createHTML[a-zA-Z0-9_]*ElementWrapper.*"
+    },
+    {
+      "name": "StylePropertySet",
+      "backtrace_function": ".*WebCore::StylePropertySet::create .*"
+    },
+    {
+      "name": "StyleElement-createSheet",
+      "backtrace_function": ".*WebCore::StyleElement::createSheet.*"
+    },
+    {
+      "name": "CachedResource",
+      "backtrace_function": ".*WebCore::CachedResource::(data|load) .*"
+    },
+    {
+      "name": "ScriptElement-execute",
+      "backtrace_function": ".*WebCore::ScriptElement::execute.*"
+    },
+    {
+      "name": "Events",
+      "backtrace_function": ".*WebCore::(createAttributeEventListener|V8LazyEventListener::create|V8EventListener::create|Event::create |EventListener::create ).*"
+    },
+    {
+      "name": "Document-write",
+      "backtrace_function": ".*WebCore::Document::write.*"
+    },
+    {
+      "name": "Node-createRenderer",
+      "backtrace_function": ".*WebCore::Node::createRendererIfNeeded.*"
+    },
+    {
+      "name": "ImageFrameGenerator",
+      "stacktrace": ".*WebCore::ImageFrameGenerator.*"
+    },
+    {
+      "name": "Render",
+      "backtrace_function": ".*WebCore::(RenderLayer|RenderBlock|RenderWidget|RenderView|RenderViewImpl|RenderStyle|RenderText).*"
+    },
+    {
+      "name": "setInnerHTML",
+      "backtrace_function": ".*WebCore::HTMLElement::setInnerHTML.*"
+    },
+    {
+      "name": "Others-create",
+      "backtrace_function": ".*WebCore::[a-zA-Z0-9_]*::create .*"
+    }
+  ]
+}
diff --git a/tools/deep_memory_profiler/sorters/malloc.browser-module.json b/tools/deep_memory_profiler/sorters/malloc.browser-module.json
new file mode 100644
index 0000000..8899283
--- /dev/null
+++ b/tools/deep_memory_profiler/sorters/malloc.browser-module.json
@@ -0,0 +1,86 @@
+{
+  "type": "sorter",
+  "version": 1,
+  "world": "malloc",
+  "name": "browser-module",
+  "order": {},
+  "rules": [
+    {
+      "name": "V8",
+      "backtrace_function": ".*v8::.*"
+    },
+    {
+      "name": "Skia",
+      "backtrace_function": ".* Sk[A-Z][A-Za-z0-9_]+::.*"
+    },
+    {
+      "name": "disk_cache",
+      "backtrace_function": ".*disk_cache::.*"
+    },
+    {
+      "name": "SQLite",
+      "backtrace_function": ".*sqlite3Mem(Malloc|Realloc).*"
+    },
+    {
+      "name": "Angle",
+      "backtrace_function": ".*TPoolAllocator::allocate.*"
+    },
+    {
+      "name": "CRYPTO",
+      "backtrace_function": ".*(CRYPTO_malloc|CRYPTO_realloc).*"
+    },
+    {
+      "name": "net-IOBuffer",
+      "backtrace_function": ".*net::IOBuffer::IOBuffer.*"
+    },
+    {
+      "name": "syncer",
+      "backtrace_function": ".* syncer::.*"
+    },
+    {
+      "name": "DOMStorage",
+      "backtrace_function": ".*content::DOMStorageMessageFilter::OnMessageReceived.*"
+    },
+    {
+      "name": "SafeBrowsingDatabase",
+      "backtrace_function": ".*SafeBrowsingDatabaseNew::UpdateFinished.*"
+    },
+    {
+      "name": "ThreadContext",
+      "backtrace_function": ".*tracked_objects::ThreadData::InitializeThreadContext.*"
+    },
+    {
+      "name": "LevelDB",
+      "backtrace_function": ".*leveldb::.*"
+    },
+    {
+      "name": "SPDY",
+      "backtrace_sourcefile": ".*\\.\\./\\.\\./net/spdy/.*"
+    },
+    {
+      "name": "net-SSLConnectJob",
+      "backtrace_function": ".*net::SSLConnectJob::DoLoop.*"
+    },
+    {
+      "name": "history-thumbnails",
+      "backtrace_function": ".*history::.*",
+      "backtrace_sourcefile": ".*\\.\\./\\.\\./chrome/browser/thumbnails/.*"
+    },
+    {
+      "name": "history-URLIndexPrivateData",
+      "backtrace_function": ".*history::URLIndexPrivateData::.*"
+    },
+    {
+      "name": "CRLSet",
+      "backtrace_function": ".*CRLSetFetcher::LoadFromDisk.*"
+    },
+    {
+      "name": "ExtensionHost",
+      "backtrace_function": ".*extensions::ExtensionHost::ExtensionHost.*"
+    },
+    {
+      "name": "BookmarkCodec",
+      "backtrace_function": ".*BookmarkCodec::.*"
+    }
+  ]
+}
diff --git a/tools/deep_memory_profiler/sorters/malloc.renderer-module.json b/tools/deep_memory_profiler/sorters/malloc.renderer-module.json
new file mode 100644
index 0000000..84473d9
--- /dev/null
+++ b/tools/deep_memory_profiler/sorters/malloc.renderer-module.json
@@ -0,0 +1,32 @@
+{
+  "type": "sorter",
+  "version": 1,
+  "world": "malloc",
+  "name": "renderer-module",
+  "order": {},
+  "rules": [
+    {
+      "name": "V8",
+      "backtrace_function": ".*v8::.*"
+    },
+    {
+      "name": "Skia",
+      "backtrace_function": ".* Sk[A-Z][A-Za-z0-9_]+::.*"
+    },
+    {
+      "name": "WebCore",
+      "subs": [
+        [ "malloc", "WebCore" ]
+      ],
+      "backtrace_function": ".*WebCore::.*"
+    },
+    {
+      "name": "Blink-others",
+      "backtrace_function": ".*(WTF::|WebKit::).*"
+    },
+    {
+      "name": "others",
+      "backtrace_function": ".*"
+    }
+  ]
+}
diff --git a/tools/deep_memory_profiler/sorters/malloc.type.json b/tools/deep_memory_profiler/sorters/malloc.type.json
new file mode 100644
index 0000000..9f7d6f4
--- /dev/null
+++ b/tools/deep_memory_profiler/sorters/malloc.type.json
@@ -0,0 +1,77 @@
+{
+  "type": "sorter",
+  "version": 1,
+  "world": "malloc",
+  "name": "type",
+  "order": {},
+  "rules": [
+    {
+      "name": "STL-string",
+      "typeinfo": "std::basic_string.*"
+    },
+    {
+      "name": "STL-rbtree",
+      "typeinfo": "std::priv::_Rb_tree::.*"
+    },
+    {
+      "name": "STL-vector",
+      "typeinfo": "std::priv::_Impl_vector::.*"
+    },
+    {
+      "name": "STL-hashtable",
+      "typeinfo": "std::hashtable::.*"
+    },
+    {
+      "name": "STL-others",
+      "typeinfo": "std::.*"
+    },
+    {
+      "name": "WTF-String",
+      "typeinfo": "WTF::String.*"
+    },
+    {
+      "name": "Skia",
+      "typeinfo": "(skia::|SkGlyph).*"
+    },
+    {
+      "name": "WebCore-Style",
+      "typeinfo": "WebCore::Style.*"
+    },
+    {
+      "name": "STL-string",
+      "backtrace_function": ".*std::basic_string::.*"
+    },
+    {
+      "name": "STL-rbtree",
+      "backtrace_function": ".*std::priv::_Rb_tree::.*"
+    },
+    {
+      "name": "STL-rbtree",
+      "backtrace_function": ".*::allocate std::(_Rb_tree|__1::__tree).*"
+    },
+    {
+      "name": "STL-hashtable",
+      "backtrace_function": ".*std::hashtable::.*"
+    },
+    {
+      "name": "STL-hashtable",
+      "backtrace_function": ".*(std::vector::reserve __gnu_cxx::hashtable|::allocate std::_Hashtable|::allocate std::__1::__hash_table).*"
+    },
+    {
+      "name": "STL-vector",
+      "backtrace_function": ".*std::priv::_Impl_vector::.*"
+    },
+    {
+      "name": "STL-vector",
+      "backtrace_function": ".*std::(_Vector_base::_M_allocate|__1::vector::allocate).*"
+    },
+    {
+      "name": "STL-others",
+      "backtrace_function": ".*(__gnu_cxx::new_allocator::allocate|std::__1::allocator::allocate).*"
+    },
+    {
+      "name": "WTF-String",
+      "backtrace_function": ".*WTF::StringImpl::getData16SlowCase.*"
+    }
+  ]
+}
diff --git a/tools/deep_memory_profiler/sorters/vm.Android-specific.json b/tools/deep_memory_profiler/sorters/vm.Android-specific.json
new file mode 100644
index 0000000..bee8629
--- /dev/null
+++ b/tools/deep_memory_profiler/sorters/vm.Android-specific.json
@@ -0,0 +1,77 @@
+{
+  "type": "sorter",
+  "version": 1,
+  "world": "vm",
+  "name": "android-specific",
+  "root": false,
+  "order": {},
+  "rules": [
+    {
+      "name": "ashmem-dalvik-heap",
+      "mapped_pathname": "/dev/ashmem/dalvik-heap.*",
+      "mmap": false
+    },
+    {
+      "name": "ashmem-dalvik-LinearAlloc",
+      "mapped_pathname": "/dev/ashmem/dalvik-LinearAlloc.*",
+      "mmap": false
+    },
+    {
+      "name": "ashmem-dalvik-aux-structure",
+      "mapped_pathname": "/dev/ashmem/dalvik-aux-structure.*",
+      "mmap": false
+    },
+    {
+      "name": "ashmem-dalvik-bitmap",
+      "mapped_pathname": "/dev/ashmem/dalvik-bitmap.*",
+      "mmap": false
+    },
+    {
+      "name": "ashmem-dalvik-other",
+      "mapped_pathname": "/dev/ashmem/dalvik.*",
+      "mmap": false
+    },
+    {
+      "name": "pvrsrvkm",
+      "mapped_pathname": "/dev/pvrsrvkm.*",
+      "mmap": false
+    },
+    {
+      "name": "system-dex",
+      "mapped_pathname": "/data/dalvik-cache/system.*.dex.*",
+      "mmap": false
+    },
+    {
+      "name": "chrome-dex",
+      "mapped_pathname": "^/.*?(chrome|content).*?apk@classes.dex",
+      "mmap": false
+    },
+    {
+      "name": "other-ashmem",
+      "mapped_pathname": "/dev/ashmem/.*",
+      "mmap": false
+    },
+    {
+      "name": "file-exec-lib-chrome",
+      "mapped_pathname": "^/.*?(chromeview|content).*",
+      "mapped_permission": "..x.",
+      "mmap": false
+    },
+    {
+      "name": "file-exec",
+      "mapped_pathname": "^/.*",
+      "mapped_permission": "..x.",
+      "mmap": false
+    },
+    {
+      "name": "file-nonexec-lib-chrome",
+      "mapped_pathname": "^/.*?(chromeview|content).*",
+      "mmap": false
+    },
+    {
+      "name": "file-nonexec",
+      "mapped_pathname": "^/.*",
+      "mmap": false
+    }
+  ]
+}
diff --git a/tools/deep_memory_profiler/sorters/vm.GPU.json b/tools/deep_memory_profiler/sorters/vm.GPU.json
new file mode 100644
index 0000000..19aa593
--- /dev/null
+++ b/tools/deep_memory_profiler/sorters/vm.GPU.json
@@ -0,0 +1,35 @@
+{
+  "type": "sorter",
+  "version": 1,
+  "world": "vm",
+  "name": "gpu",
+  "root": false,
+  "order": {},
+  "rules": [
+    {
+      "name": "mappedmemorymanager",
+      "backtrace_function": ".*gpu::MappedMemoryManager::Alloc.*",
+      "mmap": true
+    },
+    {
+      "name": "command-ringbuffer",
+      "backtrace_function": ".*gpu::CommandBufferHelper::AllocateRingBuffer.*",
+      "mmap": true
+    },
+    {
+      "name": "transfer-ringbuffer",
+      "backtrace_function": ".*gpu::TransferBuffer::AllocateRingBuffer.*",
+      "mmap": true
+    },
+    {
+      "name": "transferbuffermanager",
+      "backtrace_function": ".*gpu::TransferBufferManager::RegisterTransferBuffer.*",
+      "allocator": "mmap"
+    },
+    {
+      "name": "gles2-createbuffer",
+      "backtrace_function": ".*gpu::gles2::BufferTracker::CreateBuffer.*",
+      "mmap": true
+    }
+  ]
+}
diff --git a/tools/deep_memory_profiler/sorters/vm.Skia.json b/tools/deep_memory_profiler/sorters/vm.Skia.json
new file mode 100644
index 0000000..47c2446
--- /dev/null
+++ b/tools/deep_memory_profiler/sorters/vm.Skia.json
@@ -0,0 +1,15 @@
+{
+  "type": "sorter",
+  "version": 1,
+  "world": "vm",
+  "name": "skia",
+  "root": false,
+  "order": {},
+  "rules": [
+    {
+      "name": "font",
+      "backtrace_function": ".*SkTypeface::openStream.*",
+      "mmap": true
+    }
+  ]
+}
diff --git a/tools/deep_memory_profiler/sorters/vm.V8.json b/tools/deep_memory_profiler/sorters/vm.V8.json
new file mode 100644
index 0000000..6fef124
--- /dev/null
+++ b/tools/deep_memory_profiler/sorters/vm.V8.json
@@ -0,0 +1,25 @@
+{
+  "type": "sorter",
+  "version": 1,
+  "world": "vm",
+  "name": "v8",
+  "root": false,
+  "order": {},
+  "rules": [
+    {
+      "name": "newspace",
+      "backtrace_function": ".*v8::internal::NewSpace::SetUp.*",
+      "mmap": true
+    },
+    {
+      "name": "coderange",
+      "backtrace_function": ".*v8::internal::CodeRange::SetUp.*",
+      "mmap": true
+    },
+    {
+      "name": "pagedspace",
+      "backtrace_function": ".*v8::internal::PagedSpace::AllocateRaw.*",
+      "mmap": true
+    }
+  ]
+}
diff --git a/tools/deep_memory_profiler/sorters/vm.base.json b/tools/deep_memory_profiler/sorters/vm.base.json
new file mode 100644
index 0000000..ec503c6
--- /dev/null
+++ b/tools/deep_memory_profiler/sorters/vm.base.json
@@ -0,0 +1,75 @@
+{
+  "type": "sorter",
+  "version": 1,
+  "world": "vm",
+  "name": "base",
+  "root": true,
+  "order": {},
+  "rules": [
+    {
+      "name": "mmap-profiler",
+      "backtrace_function": ".*(TypeProfilerMalloc|ProfilerMalloc|MemoryRegionMap::).*",
+      "mmap": true,
+      "hidden": true
+    },
+    {
+      "name": "unhooked-anonymous",
+      "mapped_pathname": "^$",
+      "mmap": false
+    },
+    {
+      "name": "unhooked-file",
+      "mapped_pathname": "^/.*",
+      "mmap": false
+    },
+    {
+      "name": "unhooked-stack",
+      "mapped_pathname": ".stack.",
+      "mmap": false
+    },
+    {
+      "name": "unhooked-other",
+      "mapped_pathname": ".*",
+      "mmap": false
+    },
+    {
+      "name": "mmap-tcmalloc",
+      "backtrace_function": ".*(DoAllocWithArena|SbrkSysAllocator::Alloc|MmapSysAllocator::Alloc|LowLevelAlloc::Alloc|LowLevelAlloc::AllocWithArena).*",
+      "subs": [
+        [ "malloc", "browser-module" ],
+        [ "malloc", "renderer-module" ],
+        [ "malloc", "type" ]
+      ],
+      "mmap": true
+    },
+    {
+      "name": "mmap-v8",
+      "backtrace_function": ".*v8::.*",
+      "subs": [
+        [ "vm", "v8" ]
+      ],
+      "mmap": true
+    },
+    {
+      "name": "mmap-gpu",
+      "backtrace_function": ".*gpu::.*",
+      "subs": [
+        [ "vm", "gpu" ]
+      ],
+      "mmap": true
+    },
+    {
+      "name": "mmap-skia",
+      "backtrace_function": ".*Sk[a-zA-Z0-9_]*::.*",
+      "subs": [
+        [ "vm", "skia" ]
+      ],
+      "mmap": true
+    },
+    {
+      "name": "mmap-others",
+      "backtrace_function": ".*",
+      "mmap": true
+    }
+  ]
+}
diff --git a/tools/deep_memory_profiler/sorters/vm.sharing.json b/tools/deep_memory_profiler/sorters/vm.sharing.json
new file mode 100644
index 0000000..4cb52a6
--- /dev/null
+++ b/tools/deep_memory_profiler/sorters/vm.sharing.json
@@ -0,0 +1,25 @@
+{
+  "type": "sorter",
+  "version": 1,
+  "world": "vm",
+  "name": "sharing",
+  "root": true,
+  "order": {},
+  "rules": [
+    {
+      "name": "others",
+      "sharedwith": ["others"]
+    },
+    {
+      "name": "chrome",
+      "sharedwith": ["group"]
+    },
+    {
+      "name": "private",
+      "sharedwith": ["private"]
+    },
+    {
+      "name": "any"
+    }
+  ]
+}
diff --git a/tools/deep_memory_profiler/subcommands/cat.py b/tools/deep_memory_profiler/subcommands/cat.py
index 6a333a4..9fdc32d 100644
--- a/tools/deep_memory_profiler/subcommands/cat.py
+++ b/tools/deep_memory_profiler/subcommands/cat.py
@@ -64,7 +64,7 @@
       for sorter in sorters.iter_world(world):
         order = []
         for rule in sorter.iter_rule():
-          if (not order) or (rule.name != order[-1]):
+          if rule.name not in order:
             order.append(rule.name)
         orders['worlds'][world]['breakdown'][sorter.name] = order
     json_root['orders'] = orders
diff --git a/tools/deep_memory_profiler/templates.json b/tools/deep_memory_profiler/templates.json
index b4d9a4e..68c1240 100644
--- a/tools/deep_memory_profiler/templates.json
+++ b/tools/deep_memory_profiler/templates.json
@@ -1,12 +1,49 @@
 {
-  "l2": ["vm", "map", {
-    "mmap-tcmalloc": ["malloc", "component", {}]
+  "l0": ["vm", "base", {}],
+  "l1": ["vm", "base", {
+    "mmap-v8": ["vm", "v8", {}],
+    "mmap-gpu": ["vm", "gpu", {}],
+    "mmap-skia": ["vm", "skia", {}],
+    "mmap-tcmalloc": ["malloc", "renderer-module", {}]
   }],
-  "details": ["vm", "map", {
-    "mmap-tcmalloc": ["malloc", "component", {
-      "webkit": ["malloc", "webkit-details", {}],
-      "skia": ["malloc", "skia-details", {}]
-    }],
-    "mmap-v8-heap": ["javascript", "type", {}]
+  "l2": ["vm", "base", {
+    "mmap-v8": ["vm", "v8", {}],
+    "mmap-gpu": ["vm", "gpu", {}],
+    "mmap-skia": ["vm", "skia", {}],
+    "mmap-tcmalloc": ["malloc", "renderer-module", {
+      "WebCore": ["malloc", "WebCore", {}]
+    }]
+  }],
+  "linux.browser": ["vm", "base", {
+    "mmap-v8": ["vm", "v8", {}],
+    "mmap-gpu": ["vm", "gpu", {}],
+    "mmap-skia": ["vm", "skia", {}],
+    "mmap-tcmalloc": ["malloc", "browser-module", {}]
+  }],
+  "android.browser": ["vm", "base", {
+    "unhooked-file": ["vm", "android-specific", {}],
+    "mmap-v8": ["vm", "v8", {}],
+    "mmap-gpu": ["vm", "gpu", {}],
+    "mmap-skia": ["vm", "skia", {}],
+    "mmap-tcmalloc": ["malloc", "browser-module", {}]
+  }],
+  "android.renderer": ["vm", "base", {
+    "unhooked-file": ["vm", "android-specific", {}],
+    "mmap-v8": ["vm", "v8", {}],
+    "mmap-gpu": ["vm", "gpu", {}],
+    "mmap-skia": ["vm", "skia", {}],
+    "mmap-tcmalloc": ["malloc", "renderer-module", {
+      "WebCore": ["malloc", "WebCore", {}]
+    }]
+  }],
+  "android.webview": ["vm", "base", {
+    "unhooked-file": ["vm", "android-specific", {}],
+    "mmap-v8": ["vm", "v8", {}],
+    "mmap-gpu": ["vm", "gpu", {}],
+    "mmap-skia": ["vm", "skia", {}],
+    "mmap-tcmalloc": ["malloc", "renderer-module", {
+      "WebCore": ["malloc", "WebCore", {}],
+      "others": ["malloc", "browser-module", {}]
+    }]
   }]
 }
diff --git a/tools/deep_memory_profiler/visualizer/graph-view.js b/tools/deep_memory_profiler/visualizer/graph-view.js
new file mode 100644
index 0000000..e34ed6b
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/graph-view.js
@@ -0,0 +1,126 @@
+// 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.
+
+/**
+ * This is a view class showing flot graph.
+ * @param {Object} model Must have addListener method.
+ * @construct
+ */
+var GraphView = function(model) {
+  this.model_ = model;
+  // Update graph view and menu view when model changed.
+  model.addListener('changed', this.redraw.bind(this));
+};
+
+/**
+ * Generate lines for flot plotting.
+ * @param {Array.<Object>} modelTrees
+ * @return {Array.<Object>}
+ */
+GraphView.prototype.generateLines_ = function(modelTrees) {
+  function getLeaves(node, categories) {
+    if ('children' in node) {
+      node.children.forEach(function(child) {
+        getLeaves(child, categories);
+      });
+    } else {
+      categories.push(node);
+    }
+  }
+
+  var lines = {};
+  var snapshotNum = modelTrees.length;
+  // Initialize lines with all zero.
+  modelTrees.forEach(function(modelTree) {
+    var categories = [];
+    getLeaves(modelTree, categories);
+    categories.forEach(function(category) {
+      var name = category.name;
+      if (lines[name])
+        return;
+      lines[name] = [];
+      for (var i = 0; i < snapshotNum; ++i)
+        lines[name].push([i, 0]);
+    });
+  });
+
+  // Assignment lines with values of modelTrees.
+  modelTrees.forEach(function(modelTree, index) {
+    var categories = [];
+    getLeaves(modelTree, categories);
+    categories.forEach(function(category) {
+      var name = category.name;
+      var memory = category.memory;
+      lines[name][index] = [index, memory];
+    });
+  });
+
+  return Object.keys(lines).map(function(name) {
+    return {
+      label: name,
+      data: lines[name]
+    };
+  });
+};
+
+/**
+ * Update graph view when model updated.
+ * TODO(junjianx): use redraw function to improve perfomance.
+ * @param {Array.<Object>} modelTrees
+ */
+GraphView.prototype.redraw = function(modelTrees) {
+  var placeholder = '#graph-div';
+  var lines = this.generateLines_(modelTrees);
+  var graph = $.plot(placeholder, lines, {
+    series: {
+      stack: true,
+      lines: { show: true, fill: true }
+    },
+    grid: {
+      hoverable: true,
+      clickable: true
+    }
+  });
+
+  // Bind click event so that user can select category by clicking stack
+  // area. It firstly checks x range which clicked point is in, and all lines
+  // share same x values, so it is checked only once at first. Secondly, it
+  // checked y range by accumulated y values because this is a stack graph.
+  $(placeholder).bind('plotclick', function(event, pos, item) {
+    // If only <=1 line exists or axis area clicked, return.
+    var right = binarySearch.call(lines[0].data.map(function(point) {
+      return point[0];
+    }), pos.x);
+    if (lines.length <= 1 || right === lines.length || right === 0)
+      return;
+
+    // Calculate interpolate y value of every line.
+    for (var i = 0; i < lines.length; ++i) {
+      var line = lines[i].data;
+      // [left, right] is the range including clicked point.
+      var left = right - 1;
+      var leftPoint = {
+        x: line[left][0],
+        y: (leftPoint ? leftPoint.y : 0) + line[left][1]
+      };
+      var rightPoint = {
+        x: line[right][0],
+        y: (rightPoint ? rightPoint.y : 0) + line[right][1]
+      };
+
+      // Calculate slope of the linear equation.
+      var slope = (rightPoint.y - leftPoint.y) / (rightPoint.x - leftPoint.x);
+      var interpolateY = slope * (pos.x - rightPoint.x) + rightPoint.y;
+      if (interpolateY >= pos.y)
+        break;
+    }
+
+    // If pos.y is higher than all lines, return.
+    if (i === lines.length)
+      return;
+
+    // TODO(junjianx): temporary log for checking selected object.
+    console.log('line ' + i + ' is selected.');
+  });
+};
diff --git a/tools/deep_memory_profiler/visualizer/index.css b/tools/deep_memory_profiler/visualizer/index.css
new file mode 100644
index 0000000..e807d31
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/index.css
@@ -0,0 +1,30 @@
+/* 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. */
+
+#graph-div {
+  width: 1024px;
+  height: 600px;
+  margin-top: 30px;
+  margin-left: 50px;
+  float: left;
+}
+
+#info-div {
+  width: 240px;
+  height: 600px;
+  margin-top: 35px;
+  margin-left: 50px;
+  float: left;
+  box-shadow: 0 4px 16px rgba(0,0,0,0.2);
+  outline: 1px solid rgba(0,0,0,0.2);
+  overflow: auto;
+}
+
+#category-menu {
+  padding-left: 15px;
+}
+
+#subs-dropdown {
+  padding-left: 15px;
+}
\ No newline at end of file
diff --git a/tools/deep_memory_profiler/visualizer/index.html b/tools/deep_memory_profiler/visualizer/index.html
new file mode 100644
index 0000000..6afb950
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/index.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!--
+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.
+-->
+<meta charset="utf-8">
+<link rel="stylesheet" href="index.css">
+<link rel="stylesheet" href="third_party/jqTree/jqtree.css">
+
+<script src="../../../third_party/flot/jquery.min.js"></script>
+<script src="../../../third_party/flot/jquery.flot.min.js"></script>
+<script src="../../../third_party/flot/jquery.flot.stack.min.js"></script>
+<script src="third_party/jqTree/tree.jquery.js"></script>
+<script src="utility.js"></script>
+<script src="profiler-model.js"></script>
+<script src="graph-view.js"></script>
+<script src="menu-view.js"></script>
+<script src="index.js"></script>
+
+<body>
+  <h2>Deep Memory Profiler Visulaizer</h2>
+  <div id="graph-div"></div>
+  <div id="info-div">
+    <div id="subs-dropdown"></div>
+    <div id="category-menu"></div>
+  </div>
+</body>
diff --git a/tools/deep_memory_profiler/visualizer/index.js b/tools/deep_memory_profiler/visualizer/index.js
new file mode 100644
index 0000000..8cc106f
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/index.js
@@ -0,0 +1,17 @@
+// 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.
+
+$(function() {
+  // Read original data and plot.
+  $.getJSON('data/result.json', function(jsonData) {
+    // Create model.
+    var profiler = new Profiler(jsonData);
+    // Create views subscribing model events.
+    var graphView = new GraphView(profiler);
+    var menuView = new MenuView(profiler);
+
+    // initialize categories according to roots information.
+    profiler.initialize();
+  });
+});
diff --git a/tools/deep_memory_profiler/visualizer/main.css b/tools/deep_memory_profiler/visualizer/main.css
deleted file mode 100644
index 5d9ed53..0000000
--- a/tools/deep_memory_profiler/visualizer/main.css
+++ /dev/null
@@ -1,9 +0,0 @@
-/* Copyright 2013 The Chromium Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-.plot-container {
-  width: 1240px;
-  height: 720px;
-  margin: 30px auto 30px auto;
-}
\ No newline at end of file
diff --git a/tools/deep_memory_profiler/visualizer/main.html b/tools/deep_memory_profiler/visualizer/main.html
deleted file mode 100644
index 75df003..0000000
--- a/tools/deep_memory_profiler/visualizer/main.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!DOCTYPE html>
-<!--
-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.
--->
-<meta charset="utf-8">
-<link rel="stylesheet" href="main.css">
-<script src="../../../third_party/flot/jquery.min.js"></script>
-<script src="../../../third_party/flot/jquery.flot.min.js"></script>
-<script src="../../../third_party/flot/jquery.flot.stack.min.js"></script>
-<script src="utility.js"></script>
-<script src="profiler.js"></script>
-<script src="main.js"></script>
-
-<body>
-  <h2>Deep Memory Profiler Visulaizer</h2>
-  <div id="plot" class="plot-container"></div>
-</body>
diff --git a/tools/deep_memory_profiler/visualizer/main.js b/tools/deep_memory_profiler/visualizer/main.js
deleted file mode 100644
index 1d96213..0000000
--- a/tools/deep_memory_profiler/visualizer/main.js
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * Generate lines for flot plotting.
- * @param  {Array.<Object>} categories
- * @return {Array.<Object>}
- */
-var generateLines = function(categories) {
-  var lines = {};
-  var snapshotNum = categories.length;
-
-  // Initialize lines with all zero.
-  categories.forEach(function(categories) {
-    Object.keys(categories).forEach(function(breakdownName) {
-      if (lines[breakdownName])
-        return;
-      lines[breakdownName] = [];
-      for (var i = 0; i < snapshotNum; ++i)
-        lines[breakdownName].push([i, 0]);
-    });
-  });
-
-  // Assignment lines with values of categories.
-  categories.forEach(function(categories, index) {
-    Object.keys(categories).forEach(function(breakdownName) {
-      lines[breakdownName][index] = [index, categories[breakdownName]];
-    });
-  });
-
-  return Object.keys(lines).map(function(breakdownName) {
-    return {
-      label: breakdownName,
-      data: lines[breakdownName]
-    };
-  });
-};
-
-$(function() {
-  // Read original data and plot.
-  $.getJSON('data/result.json', function(jsonData) {
-    var profiler = new Profiler(jsonData);
-    var categories = profiler.getCategories();
-    var lines = generateLines(categories);
-    var placeholder = '#plot';
-
-    // Bind click event so that user can select breakdown by clicking stack
-    // area. It firstly checks x range which clicked point is in, and all lines
-    // share same x values, so it is checked only once at first. Secondly, it
-    // checked y range by accumulated y values because this is a stack graph.
-    $(placeholder).bind('plotclick', function(event, pos, item) {
-      // If only <=1 line exists or axis area clicked, return.
-      var right = binarySearch.call(lines[0].data.map(function(point) {
-        return point[0];
-      }), pos.x);
-      if (lines.length <= 1 || right === lines.length || right === 0)
-        return;
-
-      // Calculate interpolate y value of every line.
-      for (var i = 0; i < lines.length; ++i) {
-        var line = lines[i].data;
-        // [left, right] is the range including clicked point.
-        var left = right - 1;
-        var leftPoint = {
-          x: line[left][0],
-          y: (leftPoint ? leftPoint.y : 0) + line[left][1]
-        };
-        var rightPoint = {
-          x: line[right][0],
-          y: (rightPoint ? rightPoint.y : 0) + line[right][1]
-        };
-
-        // Calculate slope of the linear equation.
-        var slope = (rightPoint.y - leftPoint.y) / (rightPoint.x - leftPoint.x);
-        var interpolateY = slope * (pos.x - rightPoint.x) + rightPoint.y;
-        if (interpolateY >= pos.y)
-          break;
-      }
-
-      // If pos.y is higher than all lines, return.
-      if (i === lines.length)
-        return;
-
-      // TODO(junjianx): temporary log for checking selected object.
-      console.log('line ' + i + ' is selected.');
-    });
-
-    // Plot stack graph.
-    $.plot(placeholder, lines, {
-      series: {
-        stack: true,
-        lines: { show: true, fill: true }
-      },
-      grid: {
-        hoverable: true,
-        clickable: true
-      }
-    });
-  });
-});
diff --git a/tools/deep_memory_profiler/visualizer/menu-view.js b/tools/deep_memory_profiler/visualizer/menu-view.js
new file mode 100644
index 0000000..80d63d5
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/menu-view.js
@@ -0,0 +1,75 @@
+// 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.
+
+/**
+ * This is a view class showing tree-menu.
+ * @param {Object} model Must have addListener method.
+ * @construct
+ */
+var MenuView = function(model) {
+  this.model_ = model;
+  // Update graph view and menu view when model changed.
+  model.addListener('changed', this.redraw.bind(this));
+};
+
+/**
+ * Update menu view when model updated.
+ * @param {Array.<Object>} modelTrees
+ */
+MenuView.prototype.redraw = function(modelTrees) {
+  function convert(origin, target) {
+    target.label = origin.name;
+
+    if ('children' in origin) {
+      target.children = [];
+      origin.children.forEach(function(originChild) {
+        var targetChild = {};
+        target.children.push(targetChild);
+        convert(originChild, targetChild);
+      });
+    }
+  }
+
+  function merge(left, right) {
+    if (!('children' in right) && 'children' in left)
+      return;
+    if ('children' in right && !('children' in left))
+      left.children = right.children;
+    if ('children' in right && 'children' in left) {
+      right.children.forEach(function(child) {
+        // Find child with the same label in right tree.
+        var index = left.children.reduce(function(previous, current, index) {
+          if (child.label === current.label)
+            return index;
+          return previous;
+        }, -1);
+        if (index === -1)
+          left.children.push(child);
+        else
+          merge(child, left.children[index]);
+      });
+    }
+ }
+
+  // Merge trees in all snapshots.
+  var union = null;
+  modelTrees.forEach(function(modelTree) {
+    var viewTree = {};
+    convert(modelTree, viewTree);
+    if (!union)
+      union = viewTree;
+    else
+      merge(union, viewTree);
+  });
+
+  var placeholder = '#category-menu';
+  // Draw breakdown menu.
+  $(placeholder).tree({
+    data: [union],
+    autoOpen: true,
+    onCreateLi: function(node, $li) {
+      // TODO(junjianx): Add checkbox to decide the breakdown visibility.
+    }
+  });
+};
diff --git a/tools/deep_memory_profiler/visualizer/profiler-model.js b/tools/deep_memory_profiler/visualizer/profiler-model.js
new file mode 100644
index 0000000..0bf806b
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/profiler-model.js
@@ -0,0 +1,174 @@
+// 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.
+
+/**
+ * This class provides data access interface for dump file profiler.
+ * @constructor
+ */
+var Profiler = function(jsonData) {
+  this.jsonData_ = jsonData;
+  // Initialize template with templates information.
+  // TODO(junjianx): Make file path an argument.
+  this.template_ = jsonData.templates['l2'];
+  // Initialize selected category, and nothing selected at first.
+  this.selected_ = null;
+
+  // Trigger event.
+  this.callbacks_ = {};
+};
+
+/**
+ * Mimic Eventemitter in node. Add new listener for event.
+ * @param {string} event
+ * @param {Function} callback
+ */
+Profiler.prototype.addListener = function(event, callback) {
+  if (!this.callbacks_[event])
+    this.callbacks_[event] = $.Callbacks();
+  this.callbacks_[event].add(callback);
+};
+
+/**
+ * This function will emit the event.
+ * @param {string} event
+ */
+Profiler.prototype.emit = function(event) {
+  // Listeners should be able to receive arbitrary number of parameters.
+  var eventArguments = Array.prototype.slice.call(arguments, 1);
+
+  if (this.callbacks_[event])
+    this.callbacks_[event].fire.apply(this, eventArguments);
+};
+
+/**
+ * Remove listener from event.
+ * @param {string} event
+ * @param {Function} callback
+ */
+Profiler.prototype.removeListener = function(event, callback) {
+  if (this.callbacks_[event])
+    this.callbacks_[event].remove(callback);
+};
+
+/**
+ * Calcualte initial tree according default template.
+ */
+Profiler.prototype.initialize = function() {
+  this.tree_ = this.parseTemplate_();
+  this.emit('changed', this.tree_);
+};
+
+Profiler.prototype.accumulate_ = function(
+  template, snapshot, worldUnits, localUnits, nodePath, nodeName) {
+  var self = this;
+  var totalMemory = 0;
+  var worldName = template[0];
+  var breakdownName = template[1];
+  var categories = snapshot.worlds[worldName].breakdown[breakdownName];
+  // Make deep copy of localUnits.
+  var remainderUnits = localUnits.slice(0);
+  var node = {
+    name: nodeName || worldName + '-' + breakdownName,
+    nodePath: nodePath.slice(0),
+    children: []
+  };
+
+  Object.keys(categories).forEach(function(categoryName) {
+    var category = categories[categoryName];
+    if (category['hidden'] === true)
+      return;
+
+    // Accumulate categories.
+    var matchedUnits = intersection(category.units, localUnits);
+    var memory = matchedUnits.reduce(function(previous, current) {
+      return previous + worldUnits[worldName][current];
+    }, 0);
+    totalMemory += memory;
+    remainderUnits = difference(remainderUnits, matchedUnits);
+
+    // Handle subs options if exists.
+    var child = null;
+    if (!(categoryName in template[2])) {
+      // Calculate child for current category.
+      child = {
+        name: categoryName,
+        memory: memory
+      };
+      if ('subs' in category && category.subs.length)
+        node.subs = category.subs;
+
+      node.children.push(child);
+    } else {
+      // Calculate child recursively.
+      var subTemplate = template[2][categoryName];
+      var subWorldName = subTemplate[0];
+      var subNodePath = nodePath.slice(0).concat([categoryName, 2]);
+      var returnValue = null;
+
+      if (subWorldName === worldName) {
+        // If subs is in the same world, units should be filtered.
+        returnValue = self.accumulate_(subTemplate, snapshot, worldUnits,
+          matchedUnits, subNodePath, categoryName);
+        node.children.push(returnValue.node);
+        if (!returnValue.remainderUnits.length)
+          return;
+
+        var remainMemory =
+          returnValue.remainderUnits.reduce(function(previous, current) {
+            return previous + worldUnits[subWorldName][current];
+          }, 0);
+
+        node.children.push({
+          name: categoryName + '-remaining',
+          memory: remainMemory
+        });
+      } else {
+        // If subs is in different world, use all units in that world.
+        var subLocalUnits = Object.keys(worldUnits[subWorldName]);
+        subLocalUnits = subLocalUnits.map(function(unitID) {
+          return parseInt(unitID, 10);
+        });
+
+        returnValue = self.accumulate_(subTemplate, snapshot, worldUnits,
+          subLocalUnits, subNodePath, categoryName);
+        node.children.push(returnValue.node);
+
+        if (memory > returnValue.totalMemory) {
+          node.children.push({
+            name: categoryName + '-remaining',
+            memory: memory - returnValue.totalMemory
+          });
+        }
+      }
+    }
+  });
+
+  return {
+    node: node,
+    totalMemory: totalMemory,
+    remainderUnits: remainderUnits
+  };
+};
+
+Profiler.prototype.parseTemplate_ = function() {
+  var self = this;
+
+  return self.jsonData_.snapshots.map(function(snapshot) {
+    var worldUnits = {};
+    for (var worldName in snapshot.worlds) {
+      worldUnits[worldName] = {};
+      var units = snapshot.worlds[worldName].units;
+      for (var unitID in units)
+        worldUnits[worldName][unitID] = units[unitID][0];
+    }
+    var localUnits = Object.keys(worldUnits[self.template_[0]]);
+    localUnits = localUnits.map(function(unitID) {
+      return parseInt(unitID, 10);
+    });
+
+    var returnValue =
+      self.accumulate_(self.template_, snapshot, worldUnits, localUnits, [2]);
+    return returnValue.node;
+  });
+};
\ No newline at end of file
diff --git a/tools/deep_memory_profiler/visualizer/profiler.js b/tools/deep_memory_profiler/visualizer/profiler.js
deleted file mode 100644
index 672c8f0..0000000
--- a/tools/deep_memory_profiler/visualizer/profiler.js
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * This class provides data access interface for dump file profiler
- * @constructor
- */
-var Profiler = function(jsonData) {
-  this._jsonData = jsonData;
-};
-
-/**
- * Get units of a snapshot in a world.
- * Exception will be thrown when no world of given name exists.
- * @param  {string} worldName
- * @param  {number} snapshotIndex
- * @return {Object.<string, number>}
- * @private
- */
-Profiler.prototype.getUnits_ = function(worldName, snapshotIndex) {
-  var snapshot = this._jsonData.snapshots[snapshotIndex];
-  if (!snapshot.worlds[worldName])
-    throw 'no world ' + worldName + ' in snapshot ' + index;
-
-  // Return units.
-  var world = snapshot.worlds[worldName];
-  var units = {};
-  for (var unitName in world.units)
-    units[unitName] = world.units[unitName][0];
-  return units;
-};
-
-/**
- * Get first-level breakdowns of a snapshot in a world.
- * Exception will be thrown when no world of given name exists.
- * @param  {string} worldName
- * @param  {number} snapshotIndex
- * @return {Object.<string, Object>}
- * @private
- */
-Profiler.prototype.getBreakdowns_ = function(worldName, snapshotIndex) {
-  var snapshot = this._jsonData.snapshots[snapshotIndex];
-  if (!snapshot.worlds[worldName])
-    throw 'no world ' + worldName + ' in snapshot ' + index;
-
-  // Return breakdowns.
-  // TODO(junjianx): handle breakdown with arbitrary-level structure.
-  return snapshot.worlds[worldName].breakdown;
-};
-
-/**
- * Get categories from fixed hard-coded worlds and breakdowns temporarily.
- * TODO(junjianx): remove the hard-code and support general cases.
- * @return {Array.<Object>}
- */
-Profiler.prototype.getCategories = function() {
-  var categories = [];
-  var snapshotNum = this._jsonData.snapshots.length;
-
-  for (var snapshotIndex = 0; snapshotIndex < snapshotNum; ++snapshotIndex) {
-    // Initial categories object for one snapshot.
-    categories.push({});
-
-    // Handle breakdowns in malloc world.
-    var mallocBreakdown = this.getBreakdowns_('malloc', snapshotIndex);
-    var mallocUnits = this.getUnits_('malloc', snapshotIndex);
-    if (!mallocBreakdown['component'])
-      throw 'no breakdown ' + 'component' + ' in snapshot ' + snapshotIndex;
-
-    var componentBreakdown = mallocBreakdown['component'];
-    var componentMemory = 0;
-    Object.keys(componentBreakdown).forEach(function(breakdownName) {
-      var breakdown = componentBreakdown[breakdownName];
-      var memory = breakdown.units.reduce(function(previous, current) {
-        return previous + mallocUnits[current];
-      }, 0);
-      componentMemory += memory;
-
-      if (componentBreakdown['hidden'] === true)
-        return;
-      else
-        categories[snapshotIndex][breakdownName] = memory;
-    });
-
-    // Handle breakdowns in vm world.
-    var vmBreakdown = this.getBreakdowns_('vm', snapshotIndex);
-    var vmUnits = this.getUnits_('vm', snapshotIndex);
-    if (!vmBreakdown['map'])
-      throw 'no breakdown ' + 'map' + ' in snapshot ' + snapshotIndex;
-
-    var mapBreakdown = vmBreakdown['map'];
-
-    Object.keys(mapBreakdown).forEach(function(breakdownName) {
-      var breakdown = mapBreakdown[breakdownName];
-      var memory = breakdown.units.reduce(function(previous, current) {
-        return previous + vmUnits[current];
-      }, 0);
-
-      if (vmBreakdown['hidden'] === true)
-        return;
-      else if (breakdownName === 'mmap-tcmalloc')
-        categories[snapshotIndex]['tc-unused'] = memory - componentMemory;
-      else
-        categories[snapshotIndex][breakdownName] = memory;
-    });
-  }
-
-  return categories;
-};
diff --git a/tools/deep_memory_profiler/visualizer/third_party/jqTree/LICENSE b/tools/deep_memory_profiler/visualizer/third_party/jqTree/LICENSE
new file mode 100644
index 0000000..d09301f
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/third_party/jqTree/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2011 Marco Braak
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/tools/deep_memory_profiler/visualizer/third_party/jqTree/OWNERS b/tools/deep_memory_profiler/visualizer/third_party/jqTree/OWNERS
new file mode 100644
index 0000000..a207fbd
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/third_party/jqTree/OWNERS
@@ -0,0 +1,2 @@
+junjianx@chromium.org
+dmikurube@chromium.org
diff --git a/tools/deep_memory_profiler/visualizer/third_party/jqTree/README.chromium b/tools/deep_memory_profiler/visualizer/third_party/jqTree/README.chromium
new file mode 100644
index 0000000..ae316fd
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/third_party/jqTree/README.chromium
@@ -0,0 +1,29 @@
+Name: jqTree
+URL: http://mbraak.github.io/jqTree/
+Version: 0.17
+License: Apache 2.0
+License File: LICENSE
+Security Critical: no
+
+Description:
+This is a jquery plugin to draw a tree from a JSON data. This library is
+required only by src/tools/deep_memory_profiler, and it doesn't go into the
+binary.
+
+Local Modifications:
+Removed the following files:
+* bower.json
+* build
+* docs_src/
+* examples/
+* extra/
+* intodex.html
+* jqtree-circle.png
+* jqtree.jquery.jsonon
+* phantomjs/
+* screenshot.png
+* sitemap.txt
+* src/
+* test/
+* tree_logo.png
+* .travis.yml
diff --git a/tools/deep_memory_profiler/visualizer/third_party/jqTree/README.md b/tools/deep_memory_profiler/visualizer/third_party/jqTree/README.md
new file mode 100644
index 0000000..d9ee14d
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/third_party/jqTree/README.md
@@ -0,0 +1,21 @@
+[![Travis Status](https://secure.travis-ci.org/mbraak/jqTree.png)](http://travis-ci.org/mbraak/jqTree)
+
+#jqTree
+
+JqTree is a tree widget. Read more in the [documentation](http://mbraak.github.io/jqTree/).
+
+##Features
+
+* Create a tree from JSON data
+* Drag and drop
+* Works on ie7+, firefox 3.6+, chrome and safari
+* Written in Coffeescript
+
+The project is hosted on [github](https://github.com/mbraak/jqTree), has a [test suite](http://mbraak.github.io/jqTree/test/test.html).
+
+See index.html for the full documentation. The documentation is also on [github](http://mbraak.github.io/jqTree/)
+
+##Thanks
+
+The code for the mouse widget is heavily inspired by the mouse widget from jquery ui.
+Tree designed by Hernan D. Schlosman from The Noun Project.
diff --git a/tools/deep_memory_profiler/visualizer/third_party/jqTree/jqtree.css b/tools/deep_memory_profiler/visualizer/third_party/jqTree/jqtree.css
new file mode 100644
index 0000000..5f040ce
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/third_party/jqTree/jqtree.css
@@ -0,0 +1,139 @@
+ul.jqtree-tree {
+    margin-left: 12px;
+}
+
+ul.jqtree-tree,
+ul.jqtree-tree ul.jqtree_common {
+    list-style: none outside;
+    margin-bottom: 0;
+    padding: 0;
+}
+
+ul.jqtree-tree ul.jqtree_common {
+    display: block;
+    margin-left: 12px;
+    margin-right: 0;
+}
+ul.jqtree-tree li.jqtree-closed > ul.jqtree_common {
+    display: none;
+}
+
+ul.jqtree-tree li.jqtree_common {
+    clear: both;
+    list-style-type: none;
+}
+ul.jqtree-tree .jqtree-toggler {
+    display: block;
+    position: absolute;
+    left: -1.5em;
+    top: 30%;
+    *top: 0;  /* fix for ie7 */
+    font-size: 12px;
+    line-height: 12px;
+    font-family: arial;  /* fix for ie9 */
+    border-bottom: none;
+    color: #333;
+}
+
+ul.jqtree-tree .jqtree-toggler:hover {
+    color: #000;
+}
+
+ul.jqtree-tree .jqtree-element {
+    cursor: pointer;
+}
+
+ul.jqtree-tree .jqtree-title {
+    color: #1C4257;
+    vertical-align: middle;
+}
+
+ul.jqtree-tree li.jqtree-folder {
+    margin-bottom: 4px;
+}
+
+ul.jqtree-tree li.jqtree-folder.jqtree-closed {
+    margin-bottom: 1px;
+}
+
+ul.jqtree-tree li.jqtree-folder .jqtree-title {
+    margin-left: 0;
+}
+
+ul.jqtree-tree .jqtree-toggler.jqtree-closed {
+    background-position: 0 0;
+}
+
+span.jqtree-dragging {
+    color: #fff;
+    background: #000;
+    opacity: 0.6;
+    cursor: pointer;
+    padding: 2px 8px;
+}
+
+ul.jqtree-tree li.jqtree-ghost {
+    position: relative;
+    z-index: 10;
+    margin-right: 10px;
+}
+
+ul.jqtree-tree li.jqtree-ghost span {
+    display: block;
+}
+
+ul.jqtree-tree li.jqtree-ghost span.jqtree-circle {
+    background-image: url(jqtree-circle.png);
+    background-repeat: no-repeat;
+    height: 8px;
+    width: 8px;
+    position: absolute;
+    top: -4px;
+    left: 2px;
+}
+
+ul.jqtree-tree li.jqtree-ghost span.jqtree-line {
+    background-color: #0000ff;
+    height: 2px;
+    padding: 0;
+    position: absolute;
+    top: -1px;
+    left: 10px;
+    width: 100%;
+}
+
+ul.jqtree-tree li.jqtree-ghost.jqtree-inside {
+    margin-left: 48px;
+}
+
+ul.jqtree-tree span.jqtree-border {
+    position: absolute;
+    display: block;
+    left: -2px;
+    top: 0;
+    border: solid 2px #0000ff;
+    -webkit-border-radius: 6px;
+    -moz-border-radius: 6px;
+    border-radius: 6px;
+    margin: 0;
+}
+
+ul.jqtree-tree .jqtree-element {
+    width: 100%; /* todo: why is this in here? */
+    *width: auto; /* ie7 fix; issue 41 */
+    position: relative;
+}
+
+ul.jqtree-tree li.jqtree-selected > .jqtree-element,
+ul.jqtree-tree li.jqtree-selected > .jqtree-element:hover {
+    background-color: #97BDD6;
+    background: -webkit-gradient(linear, left top, left bottom, from(#BEE0F5), to(#89AFCA));
+    background: -moz-linear-gradient(top, #BEE0F5, #89AFCA);
+    background: -ms-linear-gradient(top, #BEE0F5, #89AFCA);
+    background: -o-linear-gradient(top, #BEE0F5, #89AFCA);
+    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
+}
+
+ul.jqtree-tree .jqtree-moving > .jqtree-element .jqtree-title {
+    outline: dashed 1px #0000ff;
+}
\ No newline at end of file
diff --git a/tools/deep_memory_profiler/visualizer/third_party/jqTree/tree.jquery.js b/tools/deep_memory_profiler/visualizer/third_party/jqTree/tree.jquery.js
new file mode 100644
index 0000000..2ebff8b
--- /dev/null
+++ b/tools/deep_memory_profiler/visualizer/third_party/jqTree/tree.jquery.js
@@ -0,0 +1,2609 @@
+// Generated by CoffeeScript 1.6.3
+/*
+Copyright 2013 Marco Braak
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+(function() {
+  var $, BorderDropHint, DragAndDropHandler, DragElement, FolderElement, GhostDropHint, JqTreeWidget, KeyHandler, MouseWidget, Node, NodeElement, Position, SaveStateHandler, ScrollHandler, SelectNodeHandler, SimpleWidget, html_escape, indexOf, json_escapable, json_meta, json_quote, json_str, _indexOf, _ref, _ref1, _ref2,
+    __slice = [].slice,
+    __hasProp = {}.hasOwnProperty,
+    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
+
+  $ = this.jQuery;
+
+  SimpleWidget = (function() {
+    SimpleWidget.prototype.defaults = {};
+
+    function SimpleWidget(el, options) {
+      this.$el = $(el);
+      this.options = $.extend({}, this.defaults, options);
+    }
+
+    SimpleWidget.prototype.destroy = function() {
+      return this._deinit();
+    };
+
+    SimpleWidget.prototype._init = function() {
+      return null;
+    };
+
+    SimpleWidget.prototype._deinit = function() {
+      return null;
+    };
+
+    SimpleWidget.register = function(widget_class, widget_name) {
+      var callFunction, createWidget, destroyWidget, getDataKey;
+      getDataKey = function() {
+        return "simple_widget_" + widget_name;
+      };
+      createWidget = function($el, options) {
+        var data_key, el, widget, _i, _len;
+        data_key = getDataKey();
+        for (_i = 0, _len = $el.length; _i < _len; _i++) {
+          el = $el[_i];
+          widget = new widget_class(el, options);
+          if (!$.data(el, data_key)) {
+            $.data(el, data_key, widget);
+          }
+          widget._init();
+        }
+        return $el;
+      };
+      destroyWidget = function($el) {
+        var data_key, el, widget, _i, _len, _results;
+        data_key = getDataKey();
+        _results = [];
+        for (_i = 0, _len = $el.length; _i < _len; _i++) {
+          el = $el[_i];
+          widget = $.data(el, data_key);
+          if (widget && (widget instanceof SimpleWidget)) {
+            widget.destroy();
+          }
+          _results.push($.removeData(el, data_key));
+        }
+        return _results;
+      };
+      callFunction = function($el, function_name, args) {
+        var el, result, widget, widget_function, _i, _len;
+        result = null;
+        for (_i = 0, _len = $el.length; _i < _len; _i++) {
+          el = $el[_i];
+          widget = $.data(el, getDataKey());
+          if (widget && (widget instanceof SimpleWidget)) {
+            widget_function = widget[function_name];
+            if (widget_function && (typeof widget_function === 'function')) {
+              result = widget_function.apply(widget, args);
+            }
+          }
+        }
+        return result;
+      };
+      return $.fn[widget_name] = function() {
+        var $el, args, argument1, function_name, options;
+        argument1 = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+        $el = this;
+        if (argument1 === void 0 || typeof argument1 === 'object') {
+          options = argument1;
+          return createWidget($el, options);
+        } else if (typeof argument1 === 'string' && argument1[0] !== '_') {
+          function_name = argument1;
+          if (function_name === 'destroy') {
+            return destroyWidget($el);
+          } else {
+            return callFunction($el, function_name, args);
+          }
+        }
+      };
+    };
+
+    return SimpleWidget;
+
+  })();
+
+  this.SimpleWidget = SimpleWidget;
+
+  /*
+  This widget does the same a the mouse widget in jqueryui.
+  */
+
+
+  MouseWidget = (function(_super) {
+    __extends(MouseWidget, _super);
+
+    function MouseWidget() {
+      _ref = MouseWidget.__super__.constructor.apply(this, arguments);
+      return _ref;
+    }
+
+    MouseWidget.is_mouse_handled = false;
+
+    MouseWidget.prototype._init = function() {
+      this.$el.bind('mousedown.mousewidget', $.proxy(this._mouseDown, this));
+      this.$el.bind('touchstart.mousewidget', $.proxy(this._touchStart, this));
+      this.is_mouse_started = false;
+      this.mouse_delay = 0;
+      this._mouse_delay_timer = null;
+      this._is_mouse_delay_met = true;
+      return this.mouse_down_info = null;
+    };
+
+    MouseWidget.prototype._deinit = function() {
+      var $document;
+      this.$el.unbind('mousedown.mousewidget');
+      this.$el.unbind('touchstart.mousewidget');
+      $document = $(document);
+      $document.unbind('mousemove.mousewidget');
+      return $document.unbind('mouseup.mousewidget');
+    };
+
+    MouseWidget.prototype._mouseDown = function(e) {
+      var result;
+      if (e.which !== 1) {
+        return;
+      }
+      result = this._handleMouseDown(e, this._getPositionInfo(e));
+      if (result) {
+        e.preventDefault();
+      }
+      return result;
+    };
+
+    MouseWidget.prototype._handleMouseDown = function(e, position_info) {
+      if (MouseWidget.is_mouse_handled) {
+        return;
+      }
+      if (this.is_mouse_started) {
+        this._handleMouseUp(position_info);
+      }
+      this.mouse_down_info = position_info;
+      if (!this._mouseCapture(position_info)) {
+        return;
+      }
+      this._handleStartMouse();
+      this.is_mouse_handled = true;
+      return true;
+    };
+
+    MouseWidget.prototype._handleStartMouse = function() {
+      var $document;
+      $document = $(document);
+      $document.bind('mousemove.mousewidget', $.proxy(this._mouseMove, this));
+      $document.bind('touchmove.mousewidget', $.proxy(this._touchMove, this));
+      $document.bind('mouseup.mousewidget', $.proxy(this._mouseUp, this));
+      $document.bind('touchend.mousewidget', $.proxy(this._touchEnd, this));
+      if (this.mouse_delay) {
+        return this._startMouseDelayTimer();
+      }
+    };
+
+    MouseWidget.prototype._startMouseDelayTimer = function() {
+      var _this = this;
+      if (this._mouse_delay_timer) {
+        clearTimeout(this._mouse_delay_timer);
+      }
+      this._mouse_delay_timer = setTimeout(function() {
+        return _this._is_mouse_delay_met = true;
+      }, this.mouse_delay);
+      return this._is_mouse_delay_met = false;
+    };
+
+    MouseWidget.prototype._mouseMove = function(e) {
+      return this._handleMouseMove(e, this._getPositionInfo(e));
+    };
+
+    MouseWidget.prototype._handleMouseMove = function(e, position_info) {
+      if (this.is_mouse_started) {
+        this._mouseDrag(position_info);
+        return e.preventDefault();
+      }
+      if (this.mouse_delay && !this._is_mouse_delay_met) {
+        return true;
+      }
+      this.is_mouse_started = this._mouseStart(this.mouse_down_info) !== false;
+      if (this.is_mouse_started) {
+        this._mouseDrag(position_info);
+      } else {
+        this._handleMouseUp(position_info);
+      }
+      return !this.is_mouse_started;
+    };
+
+    MouseWidget.prototype._getPositionInfo = function(e) {
+      return {
+        page_x: e.pageX,
+        page_y: e.pageY,
+        target: e.target,
+        original_event: e
+      };
+    };
+
+    MouseWidget.prototype._mouseUp = function(e) {
+      return this._handleMouseUp(this._getPositionInfo(e));
+    };
+
+    MouseWidget.prototype._handleMouseUp = function(position_info) {
+      var $document;
+      $document = $(document);
+      $document.unbind('mousemove.mousewidget');
+      $document.unbind('touchmove.mousewidget');
+      $document.unbind('mouseup.mousewidget');
+      $document.unbind('touchend.mousewidget');
+      if (this.is_mouse_started) {
+        this.is_mouse_started = false;
+        this._mouseStop(position_info);
+      }
+    };
+
+    MouseWidget.prototype._mouseCapture = function(position_info) {
+      return true;
+    };
+
+    MouseWidget.prototype._mouseStart = function(position_info) {
+      return null;
+    };
+
+    MouseWidget.prototype._mouseDrag = function(position_info) {
+      return null;
+    };
+
+    MouseWidget.prototype._mouseStop = function(position_info) {
+      return null;
+    };
+
+    MouseWidget.prototype.setMouseDelay = function(mouse_delay) {
+      return this.mouse_delay = mouse_delay;
+    };
+
+    MouseWidget.prototype._touchStart = function(e) {
+      var touch;
+      if (e.originalEvent.touches.length > 1) {
+        return;
+      }
+      touch = e.originalEvent.changedTouches[0];
+      return this._handleMouseDown(e, this._getPositionInfo(touch));
+    };
+
+    MouseWidget.prototype._touchMove = function(e) {
+      var touch;
+      if (e.originalEvent.touches.length > 1) {
+        return;
+      }
+      touch = e.originalEvent.changedTouches[0];
+      return this._handleMouseMove(e, this._getPositionInfo(touch));
+    };
+
+    MouseWidget.prototype._touchEnd = function(e) {
+      var touch;
+      if (e.originalEvent.touches.length > 1) {
+        return;
+      }
+      touch = e.originalEvent.changedTouches[0];
+      return this._handleMouseUp(this._getPositionInfo(touch));
+    };
+
+    return MouseWidget;
+
+  })(SimpleWidget);
+
+  this.Tree = {};
+
+  $ = this.jQuery;
+
+  Position = {
+    getName: function(position) {
+      return Position.strings[position - 1];
+    },
+    nameToIndex: function(name) {
+      var i, _i, _ref1;
+      for (i = _i = 1, _ref1 = Position.strings.length; 1 <= _ref1 ? _i <= _ref1 : _i >= _ref1; i = 1 <= _ref1 ? ++_i : --_i) {
+        if (Position.strings[i - 1] === name) {
+          return i;
+        }
+      }
+      return 0;
+    }
+  };
+
+  Position.BEFORE = 1;
+
+  Position.AFTER = 2;
+
+  Position.INSIDE = 3;
+
+  Position.NONE = 4;
+
+  Position.strings = ['before', 'after', 'inside', 'none'];
+
+  this.Tree.Position = Position;
+
+  Node = (function() {
+    function Node(o, is_root, node_class) {
+      if (is_root == null) {
+        is_root = false;
+      }
+      if (node_class == null) {
+        node_class = Node;
+      }
+      this.setData(o);
+      this.children = [];
+      this.parent = null;
+      if (is_root) {
+        this.id_mapping = {};
+        this.tree = this;
+        this.node_class = node_class;
+      }
+    }
+
+    Node.prototype.setData = function(o) {
+      var key, value, _results;
+      if (typeof o !== 'object') {
+        return this.name = o;
+      } else {
+        _results = [];
+        for (key in o) {
+          value = o[key];
+          if (key === 'label') {
+            _results.push(this.name = value);
+          } else {
+            _results.push(this[key] = value);
+          }
+        }
+        return _results;
+      }
+    };
+
+    Node.prototype.initFromData = function(data) {
+      var addChildren, addNode,
+        _this = this;
+      addNode = function(node_data) {
+        _this.setData(node_data);
+        if (node_data.children) {
+          return addChildren(node_data.children);
+        }
+      };
+      addChildren = function(children_data) {
+        var child, node, _i, _len;
+        for (_i = 0, _len = children_data.length; _i < _len; _i++) {
+          child = children_data[_i];
+          node = new _this.tree.node_class('');
+          node.initFromData(child);
+          _this.addChild(node);
+        }
+        return null;
+      };
+      addNode(data);
+      return null;
+    };
+
+    /*
+    Create tree from data.
+    
+    Structure of data is:
+    [
+        {
+            label: 'node1',
+            children: [
+                { label: 'child1' },
+                { label: 'child2' }
+            ]
+        },
+        {
+            label: 'node2'
+        }
+    ]
+    */
+
+
+    Node.prototype.loadFromData = function(data) {
+      var node, o, _i, _len;
+      this.removeChildren();
+      for (_i = 0, _len = data.length; _i < _len; _i++) {
+        o = data[_i];
+        node = new this.tree.node_class(o);
+        this.addChild(node);
+        if (typeof o === 'object' && o.children) {
+          node.loadFromData(o.children);
+        }
+      }
+      return null;
+    };
+
+    /*
+    Add child.
+    
+    tree.addChild(
+        new Node('child1')
+    );
+    */
+
+
+    Node.prototype.addChild = function(node) {
+      this.children.push(node);
+      return node._setParent(this);
+    };
+
+    /*
+    Add child at position. Index starts at 0.
+    
+    tree.addChildAtPosition(
+        new Node('abc'),
+        1
+    );
+    */
+
+
+    Node.prototype.addChildAtPosition = function(node, index) {
+      this.children.splice(index, 0, node);
+      return node._setParent(this);
+    };
+
+    Node.prototype._setParent = function(parent) {
+      this.parent = parent;
+      this.tree = parent.tree;
+      return this.tree.addNodeToIndex(this);
+    };
+
+    /*
+    Remove child. This also removes the children of the node.
+    
+    tree.removeChild(tree.children[0]);
+    */
+
+
+    Node.prototype.removeChild = function(node) {
+      node.removeChildren();
+      return this._removeChild(node);
+    };
+
+    Node.prototype._removeChild = function(node) {
+      this.children.splice(this.getChildIndex(node), 1);
+      return this.tree.removeNodeFromIndex(node);
+    };
+
+    /*
+    Get child index.
+    
+    var index = getChildIndex(node);
+    */
+
+
+    Node.prototype.getChildIndex = function(node) {
+      return $.inArray(node, this.children);
+    };
+
+    /*
+    Does the tree have children?
+    
+    if (tree.hasChildren()) {
+        //
+    }
+    */
+
+
+    Node.prototype.hasChildren = function() {
+      return this.children.length !== 0;
+    };
+
+    Node.prototype.isFolder = function() {
+      return this.hasChildren() || this.load_on_demand;
+    };
+
+    /*
+    Iterate over all the nodes in the tree.
+    
+    Calls callback with (node, level).
+    
+    The callback must return true to continue the iteration on current node.
+    
+    tree.iterate(
+        function(node, level) {
+           console.log(node.name);
+    
+           // stop iteration after level 2
+           return (level <= 2);
+        }
+    );
+    */
+
+
+    Node.prototype.iterate = function(callback) {
+      var _iterate,
+        _this = this;
+      _iterate = function(node, level) {
+        var child, result, _i, _len, _ref1;
+        if (node.children) {
+          _ref1 = node.children;
+          for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
+            child = _ref1[_i];
+            result = callback(child, level);
+            if (_this.hasChildren() && result) {
+              _iterate(child, level + 1);
+            }
+          }
+          return null;
+        }
+      };
+      _iterate(this, 0);
+      return null;
+    };
+
+    /*
+    Move node relative to another node.
+    
+    Argument position: Position.BEFORE, Position.AFTER or Position.Inside
+    
+    // move node1 after node2
+    tree.moveNode(node1, node2, Position.AFTER);
+    */
+
+
+    Node.prototype.moveNode = function(moved_node, target_node, position) {
+      if (moved_node.isParentOf(target_node)) {
+        return;
+      }
+      moved_node.parent._removeChild(moved_node);
+      if (position === Position.AFTER) {
+        return target_node.parent.addChildAtPosition(moved_node, target_node.parent.getChildIndex(target_node) + 1);
+      } else if (position === Position.BEFORE) {
+        return target_node.parent.addChildAtPosition(moved_node, target_node.parent.getChildIndex(target_node));
+      } else if (position === Position.INSIDE) {
+        return target_node.addChildAtPosition(moved_node, 0);
+      }
+    };
+
+    /*
+    Get the tree as data.
+    */
+
+
+    Node.prototype.getData = function() {
+      var getDataFromNodes,
+        _this = this;
+      getDataFromNodes = function(nodes) {
+        var data, k, node, tmp_node, v, _i, _len;
+        data = [];
+        for (_i = 0, _len = nodes.length; _i < _len; _i++) {
+          node = nodes[_i];
+          tmp_node = {};
+          for (k in node) {
+            v = node[k];
+            if ((k !== 'parent' && k !== 'children' && k !== 'element' && k !== 'tree') && Object.prototype.hasOwnProperty.call(node, k)) {
+              tmp_node[k] = v;
+            }
+          }
+          if (node.hasChildren()) {
+            tmp_node.children = getDataFromNodes(node.children);
+          }
+          data.push(tmp_node);
+        }
+        return data;
+      };
+      return getDataFromNodes(this.children);
+    };
+
+    Node.prototype.getNodeByName = function(name) {
+      var result;
+      result = null;
+      this.iterate(function(node) {
+        if (node.name === name) {
+          result = node;
+          return false;
+        } else {
+          return true;
+        }
+      });
+      return result;
+    };
+
+    Node.prototype.addAfter = function(node_info) {
+      var child_index, node;
+      if (!this.parent) {
+        return null;
+      } else {
+        node = new this.tree.node_class(node_info);
+        child_index = this.parent.getChildIndex(this);
+        this.parent.addChildAtPosition(node, child_index + 1);
+        return node;
+      }
+    };
+
+    Node.prototype.addBefore = function(node_info) {
+      var child_index, node;
+      if (!this.parent) {
+        return null;
+      } else {
+        node = new this.tree.node_class(node_info);
+        child_index = this.parent.getChildIndex(this);
+        this.parent.addChildAtPosition(node, child_index);
+        return node;
+      }
+    };
+
+    Node.prototype.addParent = function(node_info) {
+      var child, new_parent, original_parent, _i, _len, _ref1;
+      if (!this.parent) {
+        return null;
+      } else {
+        new_parent = new this.tree.node_class(node_info);
+        new_parent._setParent(this.tree);
+        original_parent = this.parent;
+        _ref1 = original_parent.children;
+        for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
+          child = _ref1[_i];
+          new_parent.addChild(child);
+        }
+        original_parent.children = [];
+        original_parent.addChild(new_parent);
+        return new_parent;
+      }
+    };
+
+    Node.prototype.remove = function() {
+      if (this.parent) {
+        this.parent.removeChild(this);
+        return this.parent = null;
+      }
+    };
+
+    Node.prototype.append = function(node_info) {
+      var node;
+      node = new this.tree.node_class(node_info);
+      this.addChild(node);
+      return node;
+    };
+
+    Node.prototype.prepend = function(node_info) {
+      var node;
+      node = new this.tree.node_class(node_info);
+      this.addChildAtPosition(node, 0);
+      return node;
+    };
+
+    Node.prototype.isParentOf = function(node) {
+      var parent;
+      parent = node.parent;
+      while (parent) {
+        if (parent === this) {
+          return true;
+        }
+        parent = parent.parent;
+      }
+      return false;
+    };
+
+    Node.prototype.getLevel = function() {
+      var level, node;
+      level = 0;
+      node = this;
+      while (node.parent) {
+        level += 1;
+        node = node.parent;
+      }
+      return level;
+    };
+
+    Node.prototype.getNodeById = function(node_id) {
+      return this.id_mapping[node_id];
+    };
+
+    Node.prototype.addNodeToIndex = function(node) {
+      if (node.id) {
+        return this.id_mapping[node.id] = node;
+      }
+    };
+
+    Node.prototype.removeNodeFromIndex = function(node) {
+      if (node.id) {
+        return delete this.id_mapping[node.id];
+      }
+    };
+
+    Node.prototype.removeChildren = function() {
+      var _this = this;
+      this.iterate(function(child) {
+        _this.tree.removeNodeFromIndex(child);
+        return true;
+      });
+      return this.children = [];
+    };
+
+    Node.prototype.getPreviousSibling = function() {
+      var previous_index;
+      if (!this.parent) {
+        return null;
+      } else {
+        previous_index = this.parent.getChildIndex(this) - 1;
+        if (previous_index >= 0) {
+          return this.parent.children[previous_index];
+        } else {
+          return null;
+        }
+      }
+    };
+
+    Node.prototype.getNextSibling = function() {
+      var next_index;
+      if (!this.parent) {
+        return null;
+      } else {
+        next_index = this.parent.getChildIndex(this) + 1;
+        if (next_index < this.parent.children.length) {
+          return this.parent.children[next_index];
+        } else {
+          return null;
+        }
+      }
+    };
+
+    return Node;
+
+  })();
+
+  this.Tree.Node = Node;
+
+  /*
+  Copyright 2013 Marco Braak
+  
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+  
+      http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  */
+
+
+  JqTreeWidget = (function(_super) {
+    __extends(JqTreeWidget, _super);
+
+    function JqTreeWidget() {
+      _ref1 = JqTreeWidget.__super__.constructor.apply(this, arguments);
+      return _ref1;
+    }
+
+    JqTreeWidget.prototype.defaults = {
+      autoOpen: false,
+      saveState: false,
+      dragAndDrop: false,
+      selectable: true,
+      useContextMenu: true,
+      onCanSelectNode: null,
+      onSetStateFromStorage: null,
+      onGetStateFromStorage: null,
+      onCreateLi: null,
+      onIsMoveHandle: null,
+      onCanMove: null,
+      onCanMoveTo: null,
+      onLoadFailed: null,
+      autoEscape: true,
+      dataUrl: null,
+      closedIcon: '&#x25ba;',
+      openedIcon: '&#x25bc;',
+      slide: true,
+      nodeClass: Node
+    };
+
+    JqTreeWidget.prototype.toggle = function(node, slide) {
+      if (slide == null) {
+        slide = true;
+      }
+      if (node.is_open) {
+        return this.closeNode(node, slide);
+      } else {
+        return this.openNode(node, slide);
+      }
+    };
+
+    JqTreeWidget.prototype.getTree = function() {
+      return this.tree;
+    };
+
+    JqTreeWidget.prototype.selectNode = function(node) {
+      return this._selectNode(node, true);
+    };
+
+    JqTreeWidget.prototype._selectNode = function(node, must_toggle) {
+      var canSelect, openParents, saveState,
+        _this = this;
+      if (must_toggle == null) {
+        must_toggle = false;
+      }
+      if (!this.select_node_handler) {
+        return;
+      }
+      canSelect = function() {
+        if (_this.options.onCanSelectNode) {
+          return _this.options.selectable && _this.options.onCanSelectNode(node);
+        } else {
+          return _this.options.selectable;
+        }
+      };
+      openParents = function() {
+        var parent;
+        parent = node.parent;
+        if (parent && parent.parent && !parent.is_open) {
+          return _this.openNode(parent, false);
+        }
+      };
+      saveState = function() {
+        if (_this.options.saveState) {
+          return _this.save_state_handler.saveState();
+        }
+      };
+      if (!node) {
+        this._deselectCurrentNode();
+        saveState();
+        return;
+      }
+      if (!canSelect()) {
+        return;
+      }
+      if (this.select_node_handler.isNodeSelected(node)) {
+        if (must_toggle) {
+          this._deselectCurrentNode();
+          this._triggerEvent('tree.select', {
+            node: null,
+            previous_node: node
+          });
+        }
+      } else {
+        this._deselectCurrentNode();
+        this.addToSelection(node);
+        this._triggerEvent('tree.select', {
+          node: node
+        });
+        openParents();
+      }
+      return saveState();
+    };
+
+    JqTreeWidget.prototype.getSelectedNode = function() {
+      return this.select_node_handler.getSelectedNode();
+    };
+
+    JqTreeWidget.prototype.toJson = function() {
+      return JSON.stringify(this.tree.getData());
+    };
+
+    JqTreeWidget.prototype.loadData = function(data, parent_node) {
+      return this._loadData(data, parent_node);
+    };
+
+    JqTreeWidget.prototype.loadDataFromUrl = function(url, parent_node, on_finished) {
+      if ($.type(url) !== 'string') {
+        on_finished = parent_node;
+        parent_node = url;
+        url = null;
+      }
+      return this._loadDataFromUrl(url, parent_node, on_finished);
+    };
+
+    JqTreeWidget.prototype._loadDataFromUrl = function(url_info, parent_node, on_finished) {
+      var $el, addLoadingClass, parseUrlInfo, removeLoadingClass,
+        _this = this;
+      $el = null;
+      addLoadingClass = function() {
+        var folder_element;
+        if (!parent_node) {
+          $el = _this.element;
+        } else {
+          folder_element = new FolderElement(parent_node, _this);
+          $el = folder_element.getLi();
+        }
+        return $el.addClass('jqtree-loading');
+      };
+      removeLoadingClass = function() {
+        if ($el) {
+          return $el.removeClass('jqtree-loading');
+        }
+      };
+      parseUrlInfo = function() {
+        if ($.type(url_info) === 'string') {
+          url_info = {
+            url: url_info
+          };
+        }
+        if (!url_info.method) {
+          return url_info.method = 'get';
+        }
+      };
+      addLoadingClass();
+      if (!url_info) {
+        url_info = this._getDataUrlInfo(parent_node);
+      }
+      parseUrlInfo();
+      return $.ajax({
+        url: url_info.url,
+        data: url_info.data,
+        type: url_info.method.toUpperCase(),
+        cache: false,
+        dataType: 'json',
+        success: function(response) {
+          var data;
+          if ($.isArray(response) || typeof response === 'object') {
+            data = response;
+          } else {
+            data = $.parseJSON(response);
+          }
+          removeLoadingClass();
+          _this._loadData(data, parent_node);
+          if (on_finished && $.isFunction(on_finished)) {
+            return on_finished();
+          }
+        },
+        error: function(response) {
+          removeLoadingClass();
+          if (_this.options.onLoadFailed) {
+            return _this.options.onLoadFailed(response);
+          }
+        }
+      });
+    };
+
+    JqTreeWidget.prototype._loadData = function(data, parent_node) {
+      var n, selected_nodes_under_parent, _i, _len;
+      this._triggerEvent('tree.load_data', {
+        tree_data: data
+      });
+      if (!parent_node) {
+        this._initTree(data);
+      } else {
+        selected_nodes_under_parent = this.select_node_handler.getSelectedNodes(parent_node);
+        for (_i = 0, _len = selected_nodes_under_parent.length; _i < _len; _i++) {
+          n = selected_nodes_under_parent[_i];
+          this.select_node_handler.removeFromSelection(n);
+        }
+        parent_node.loadFromData(data);
+        parent_node.load_on_demand = false;
+        this._refreshElements(parent_node.parent);
+      }
+      if (this.is_dragging) {
+        return this.dnd_handler.refreshHitAreas();
+      }
+    };
+
+    JqTreeWidget.prototype.getNodeById = function(node_id) {
+      return this.tree.getNodeById(node_id);
+    };
+
+    JqTreeWidget.prototype.getNodeByName = function(name) {
+      return this.tree.getNodeByName(name);
+    };
+
+    JqTreeWidget.prototype.openNode = function(node, slide) {
+      if (slide == null) {
+        slide = true;
+      }
+      return this._openNode(node, slide);
+    };
+
+    JqTreeWidget.prototype._openNode = function(node, slide, on_finished) {
+      var doOpenNode, parent,
+        _this = this;
+      if (slide == null) {
+        slide = true;
+      }
+      doOpenNode = function(_node, _slide, _on_finished) {
+        var folder_element;
+        folder_element = new FolderElement(_node, _this);
+        return folder_element.open(_on_finished, _slide);
+      };
+      if (node.isFolder()) {
+        if (node.load_on_demand) {
+          return this._loadFolderOnDemand(node, slide, on_finished);
+        } else {
+          parent = node.parent;
+          while (parent && !parent.is_open) {
+            if (parent.parent) {
+              doOpenNode(parent, false, null);
+            }
+            parent = parent.parent;
+          }
+          doOpenNode(node, slide, on_finished);
+          return this._saveState();
+        }
+      }
+    };
+
+    JqTreeWidget.prototype._loadFolderOnDemand = function(node, slide, on_finished) {
+      var _this = this;
+      if (slide == null) {
+        slide = true;
+      }
+      return this._loadDataFromUrl(null, node, function() {
+        return _this._openNode(node, slide, on_finished);
+      });
+    };
+
+    JqTreeWidget.prototype.closeNode = function(node, slide) {
+      if (slide == null) {
+        slide = true;
+      }
+      if (node.isFolder()) {
+        new FolderElement(node, this).close(slide);
+        return this._saveState();
+      }
+    };
+
+    JqTreeWidget.prototype.isDragging = function() {
+      return this.is_dragging;
+    };
+
+    JqTreeWidget.prototype.refreshHitAreas = function() {
+      return this.dnd_handler.refreshHitAreas();
+    };
+
+    JqTreeWidget.prototype.addNodeAfter = function(new_node_info, existing_node) {
+      var new_node;
+      new_node = existing_node.addAfter(new_node_info);
+      this._refreshElements(existing_node.parent);
+      return new_node;
+    };
+
+    JqTreeWidget.prototype.addNodeBefore = function(new_node_info, existing_node) {
+      var new_node;
+      new_node = existing_node.addBefore(new_node_info);
+      this._refreshElements(existing_node.parent);
+      return new_node;
+    };
+
+    JqTreeWidget.prototype.addParentNode = function(new_node_info, existing_node) {
+      var new_node;
+      new_node = existing_node.addParent(new_node_info);
+      this._refreshElements(new_node.parent);
+      return new_node;
+    };
+
+    JqTreeWidget.prototype.removeNode = function(node) {
+      var parent;
+      parent = node.parent;
+      if (parent) {
+        this.select_node_handler.removeFromSelection(node, true);
+        node.remove();
+        return this._refreshElements(parent.parent);
+      }
+    };
+
+    JqTreeWidget.prototype.appendNode = function(new_node_info, parent_node) {
+      var is_already_root_node, node;
+      if (!parent_node) {
+        parent_node = this.tree;
+      }
+      is_already_root_node = parent_node.isFolder();
+      node = parent_node.append(new_node_info);
+      if (is_already_root_node) {
+        this._refreshElements(parent_node);
+      } else {
+        this._refreshElements(parent_node.parent);
+      }
+      return node;
+    };
+
+    JqTreeWidget.prototype.prependNode = function(new_node_info, parent_node) {
+      var node;
+      if (!parent_node) {
+        parent_node = this.tree;
+      }
+      node = parent_node.prepend(new_node_info);
+      this._refreshElements(parent_node);
+      return node;
+    };
+
+    JqTreeWidget.prototype.updateNode = function(node, data) {
+      var id_is_changed;
+      id_is_changed = data.id && data.id !== node.id;
+      if (id_is_changed) {
+        this.tree.removeNodeFromIndex(node);
+      }
+      node.setData(data);
+      if (id_is_changed) {
+        this.tree.addNodeToIndex(node);
+      }
+      this._refreshElements(node.parent);
+      return this._selectCurrentNode();
+    };
+
+    JqTreeWidget.prototype.moveNode = function(node, target_node, position) {
+      var position_index;
+      position_index = Position.nameToIndex(position);
+      this.tree.moveNode(node, target_node, position_index);
+      return this._refreshElements();
+    };
+
+    JqTreeWidget.prototype.getStateFromStorage = function() {
+      return this.save_state_handler.getStateFromStorage();
+    };
+
+    JqTreeWidget.prototype.addToSelection = function(node) {
+      this.select_node_handler.addToSelection(node);
+      return this._getNodeElementForNode(node).select();
+    };
+
+    JqTreeWidget.prototype.getSelectedNodes = function() {
+      return this.select_node_handler.getSelectedNodes();
+    };
+
+    JqTreeWidget.prototype.isNodeSelected = function(node) {
+      return this.select_node_handler.isNodeSelected(node);
+    };
+
+    JqTreeWidget.prototype.removeFromSelection = function(node) {
+      this.select_node_handler.removeFromSelection(node);
+      return this._getNodeElementForNode(node).deselect();
+    };
+
+    JqTreeWidget.prototype.scrollToNode = function(node) {
+      var $element, top;
+      $element = $(node.element);
+      top = $element.offset().top - this.$el.offset().top;
+      return this.scroll_handler.scrollTo(top);
+    };
+
+    JqTreeWidget.prototype.getState = function() {
+      return this.save_state_handler.getState();
+    };
+
+    JqTreeWidget.prototype.setState = function(state) {
+      this.save_state_handler.setState(state);
+      return this._refreshElements();
+    };
+
+    JqTreeWidget.prototype._init = function() {
+      JqTreeWidget.__super__._init.call(this);
+      this.element = this.$el;
+      this.mouse_delay = 300;
+      this.is_initialized = false;
+      if (typeof SaveStateHandler !== "undefined" && SaveStateHandler !== null) {
+        this.save_state_handler = new SaveStateHandler(this);
+      } else {
+        this.options.saveState = false;
+      }
+      if (typeof SelectNodeHandler !== "undefined" && SelectNodeHandler !== null) {
+        this.select_node_handler = new SelectNodeHandler(this);
+      }
+      if (typeof DragAndDropHandler !== "undefined" && DragAndDropHandler !== null) {
+        this.dnd_handler = new DragAndDropHandler(this);
+      } else {
+        this.options.dragAndDrop = false;
+      }
+      if (typeof ScrollHandler !== "undefined" && ScrollHandler !== null) {
+        this.scroll_handler = new ScrollHandler(this);
+      }
+      if ((typeof KeyHandler !== "undefined" && KeyHandler !== null) && (typeof SelectNodeHandler !== "undefined" && SelectNodeHandler !== null)) {
+        this.key_handler = new KeyHandler(this);
+      }
+      this._initData();
+      this.element.click($.proxy(this._click, this));
+      if (this.options.useContextMenu) {
+        return this.element.bind('contextmenu', $.proxy(this._contextmenu, this));
+      }
+    };
+
+    JqTreeWidget.prototype._deinit = function() {
+      this.element.empty();
+      this.element.unbind();
+      this.key_handler.deinit();
+      this.tree = null;
+      return JqTreeWidget.__super__._deinit.call(this);
+    };
+
+    JqTreeWidget.prototype._initData = function() {
+      if (this.options.data) {
+        return this._loadData(this.options.data);
+      } else {
+        return this._loadDataFromUrl(this._getDataUrlInfo());
+      }
+    };
+
+    JqTreeWidget.prototype._getDataUrlInfo = function(node) {
+      var data, data_url, url_info;
+      data_url = this.options.dataUrl || this.element.data('url');
+      if ($.isFunction(data_url)) {
+        return data_url(node);
+      } else if ($.type(data_url) === 'string') {
+        url_info = {
+          url: data_url
+        };
+        if (node && node.id) {
+          data = {
+            node: node.id
+          };
+          url_info['data'] = data;
+        }
+        return url_info;
+      } else {
+        return data_url;
+      }
+    };
+
+    JqTreeWidget.prototype._initTree = function(data) {
+      this.tree = new this.options.nodeClass(null, true, this.options.nodeClass);
+      if (this.select_node_handler) {
+        this.select_node_handler.clear();
+      }
+      this.tree.loadFromData(data);
+      this._openNodes();
+      this._refreshElements();
+      if (!this.is_initialized) {
+        this.is_initialized = true;
+        return this._triggerEvent('tree.init');
+      }
+    };
+
+    JqTreeWidget.prototype._openNodes = function() {
+      var max_level;
+      if (this.options.saveState) {
+        if (this.save_state_handler.restoreState()) {
+          return;
+        }
+      }
+      if (this.options.autoOpen === false) {
+        return;
+      } else if (this.options.autoOpen === true) {
+        max_level = -1;
+      } else {
+        max_level = parseInt(this.options.autoOpen);
+      }
+      return this.tree.iterate(function(node, level) {
+        if (node.hasChildren()) {
+          node.is_open = true;
+        }
+        return level !== max_level;
+      });
+    };
+
+    JqTreeWidget.prototype._refreshElements = function(from_node) {
+      var $element, createFolderLi, createLi, createNodeLi, createUl, doCreateDomElements, escapeIfNecessary, is_root_node, node_element,
+        _this = this;
+      if (from_node == null) {
+        from_node = null;
+      }
+      escapeIfNecessary = function(value) {
+        if (_this.options.autoEscape) {
+          return html_escape(value);
+        } else {
+          return value;
+        }
+      };
+      createUl = function(is_root_node) {
+        var class_string;
+        if (is_root_node) {
+          class_string = 'jqtree-tree';
+        } else {
+          class_string = '';
+        }
+        return $("<ul class=\"jqtree_common " + class_string + "\"></ul>");
+      };
+      createLi = function(node) {
+        var $li;
+        if (node.isFolder()) {
+          $li = createFolderLi(node);
+        } else {
+          $li = createNodeLi(node);
+        }
+        if (_this.options.onCreateLi) {
+          _this.options.onCreateLi(node, $li);
+        }
+        return $li;
+      };
+      createNodeLi = function(node) {
+        var class_string, escaped_name, li_classes;
+        li_classes = ['jqtree_common'];
+        if (_this.select_node_handler && _this.select_node_handler.isNodeSelected(node)) {
+          li_classes.push('jqtree-selected');
+        }
+        class_string = li_classes.join(' ');
+        escaped_name = escapeIfNecessary(node.name);
+        return $("<li class=\"" + class_string + "\"><div class=\"jqtree-element jqtree_common\"><span class=\"jqtree-title jqtree_common\">" + escaped_name + "</span></div></li>");
+      };
+      createFolderLi = function(node) {
+        var button_char, button_classes, escaped_name, folder_classes, getButtonClasses, getFolderClasses;
+        getButtonClasses = function() {
+          var classes;
+          classes = ['jqtree-toggler'];
+          if (!node.is_open) {
+            classes.push('jqtree-closed');
+          }
+          return classes.join(' ');
+        };
+        getFolderClasses = function() {
+          var classes;
+          classes = ['jqtree-folder'];
+          if (!node.is_open) {
+            classes.push('jqtree-closed');
+          }
+          if (_this.select_node_handler && _this.select_node_handler.isNodeSelected(node)) {
+            classes.push('jqtree-selected');
+          }
+          return classes.join(' ');
+        };
+        button_classes = getButtonClasses();
+        folder_classes = getFolderClasses();
+        escaped_name = escapeIfNecessary(node.name);
+        if (node.is_open) {
+          button_char = _this.options.openedIcon;
+        } else {
+          button_char = _this.options.closedIcon;
+        }
+        return $("<li class=\"jqtree_common " + folder_classes + "\"><div class=\"jqtree-element jqtree_common\"><a class=\"jqtree_common " + button_classes + "\">" + button_char + "</a><span class=\"jqtree_common jqtree-title\">" + escaped_name + "</span></div></li>");
+      };
+      doCreateDomElements = function($element, children, is_root_node, is_open) {
+        var $li, $ul, child, _i, _len;
+        $ul = createUl(is_root_node);
+        $element.append($ul);
+        for (_i = 0, _len = children.length; _i < _len; _i++) {
+          child = children[_i];
+          $li = createLi(child);
+          $ul.append($li);
+          child.element = $li[0];
+          $li.data('node', child);
+          if (child.hasChildren()) {
+            doCreateDomElements($li, child.children, false, child.is_open);
+          }
+        }
+        return null;
+      };
+      if (from_node && from_node.parent) {
+        is_root_node = false;
+        node_element = this._getNodeElementForNode(from_node);
+        node_element.getUl().remove();
+        $element = node_element.$element;
+      } else {
+        from_node = this.tree;
+        $element = this.element;
+        $element.empty();
+        is_root_node = true;
+      }
+      doCreateDomElements($element, from_node.children, is_root_node, is_root_node);
+      return this._triggerEvent('tree.refresh');
+    };
+
+    JqTreeWidget.prototype._click = function(e) {
+      var $button, $el, $target, event, node;
+      $target = $(e.target);
+      $button = $target.closest('.jqtree-toggler');
+      if ($button.length) {
+        node = this._getNode($button);
+        if (node) {
+          this.toggle(node, this.options.slide);
+          e.preventDefault();
+          return e.stopPropagation();
+        }
+      } else {
+        $el = $target.closest('.jqtree-element');
+        if ($el.length) {
+          node = this._getNode($el);
+          if (node) {
+            event = this._triggerEvent('tree.click', {
+              node: node
+            });
+            if (!event.isDefaultPrevented()) {
+              return this._selectNode(node, true);
+            }
+          }
+        }
+      }
+    };
+
+    JqTreeWidget.prototype._getNode = function($element) {
+      var $li;
+      $li = $element.closest('li');
+      if ($li.length === 0) {
+        return null;
+      } else {
+        return $li.data('node');
+      }
+    };
+
+    JqTreeWidget.prototype._getNodeElementForNode = function(node) {
+      if (node.isFolder()) {
+        return new FolderElement(node, this);
+      } else {
+        return new NodeElement(node, this);
+      }
+    };
+
+    JqTreeWidget.prototype._getNodeElement = function($element) {
+      var node;
+      node = this._getNode($element);
+      if (node) {
+        return this._getNodeElementForNode(node);
+      } else {
+        return null;
+      }
+    };
+
+    JqTreeWidget.prototype._contextmenu = function(e) {
+      var $div, node;
+      $div = $(e.target).closest('ul.jqtree-tree .jqtree-element');
+      if ($div.length) {
+        node = this._getNode($div);
+        if (node) {
+          e.preventDefault();
+          e.stopPropagation();
+          this._triggerEvent('tree.contextmenu', {
+            node: node,
+            click_event: e
+          });
+          return false;
+        }
+      }
+    };
+
+    JqTreeWidget.prototype._saveState = function() {
+      if (this.options.saveState) {
+        return this.save_state_handler.saveState();
+      }
+    };
+
+    JqTreeWidget.prototype._mouseCapture = function(position_info) {
+      if (this.options.dragAndDrop) {
+        return this.dnd_handler.mouseCapture(position_info);
+      } else {
+        return false;
+      }
+    };
+
+    JqTreeWidget.prototype._mouseStart = function(position_info) {
+      if (this.options.dragAndDrop) {
+        return this.dnd_handler.mouseStart(position_info);
+      } else {
+        return false;
+      }
+    };
+
+    JqTreeWidget.prototype._mouseDrag = function(position_info) {
+      var result;
+      if (this.options.dragAndDrop) {
+        result = this.dnd_handler.mouseDrag(position_info);
+        if (this.scroll_handler) {
+          this.scroll_handler.checkScrolling();
+        }
+        return result;
+      } else {
+        return false;
+      }
+    };
+
+    JqTreeWidget.prototype._mouseStop = function(position_info) {
+      if (this.options.dragAndDrop) {
+        return this.dnd_handler.mouseStop(position_info);
+      } else {
+        return false;
+      }
+    };
+
+    JqTreeWidget.prototype._triggerEvent = function(event_name, values) {
+      var event;
+      event = $.Event(event_name);
+      $.extend(event, values);
+      this.element.trigger(event);
+      return event;
+    };
+
+    JqTreeWidget.prototype.testGenerateHitAreas = function(moving_node) {
+      this.dnd_handler.current_item = this._getNodeElementForNode(moving_node);
+      this.dnd_handler.generateHitAreas();
+      return this.dnd_handler.hit_areas;
+    };
+
+    JqTreeWidget.prototype._selectCurrentNode = function() {
+      var node, node_element;
+      node = this.getSelectedNode();
+      if (node) {
+        node_element = this._getNodeElementForNode(node);
+        if (node_element) {
+          return node_element.select();
+        }
+      }
+    };
+
+    JqTreeWidget.prototype._deselectCurrentNode = function() {
+      var node;
+      node = this.getSelectedNode();
+      if (node) {
+        return this.removeFromSelection(node);
+      }
+    };
+
+    return JqTreeWidget;
+
+  })(MouseWidget);
+
+  SimpleWidget.register(JqTreeWidget, 'tree');
+
+  NodeElement = (function() {
+    function NodeElement(node, tree_widget) {
+      this.init(node, tree_widget);
+    }
+
+    NodeElement.prototype.init = function(node, tree_widget) {
+      this.node = node;
+      this.tree_widget = tree_widget;
+      return this.$element = $(node.element);
+    };
+
+    NodeElement.prototype.getUl = function() {
+      return this.$element.children('ul:first');
+    };
+
+    NodeElement.prototype.getSpan = function() {
+      return this.$element.children('.jqtree-element').find('span.jqtree-title');
+    };
+
+    NodeElement.prototype.getLi = function() {
+      return this.$element;
+    };
+
+    NodeElement.prototype.addDropHint = function(position) {
+      if (position === Position.INSIDE) {
+        return new BorderDropHint(this.$element);
+      } else {
+        return new GhostDropHint(this.node, this.$element, position);
+      }
+    };
+
+    NodeElement.prototype.select = function() {
+      return this.getLi().addClass('jqtree-selected');
+    };
+
+    NodeElement.prototype.deselect = function() {
+      return this.getLi().removeClass('jqtree-selected');
+    };
+
+    return NodeElement;
+
+  })();
+
+  FolderElement = (function(_super) {
+    __extends(FolderElement, _super);
+
+    function FolderElement() {
+      _ref2 = FolderElement.__super__.constructor.apply(this, arguments);
+      return _ref2;
+    }
+
+    FolderElement.prototype.open = function(on_finished, slide) {
+      var $button, doOpen,
+        _this = this;
+      if (slide == null) {
+        slide = true;
+      }
+      if (!this.node.is_open) {
+        this.node.is_open = true;
+        $button = this.getButton();
+        $button.removeClass('jqtree-closed');
+        $button.html(this.tree_widget.options.openedIcon);
+        doOpen = function() {
+          _this.getLi().removeClass('jqtree-closed');
+          if (on_finished) {
+            on_finished();
+          }
+          return _this.tree_widget._triggerEvent('tree.open', {
+            node: _this.node
+          });
+        };
+        if (slide) {
+          return this.getUl().slideDown('fast', doOpen);
+        } else {
+          this.getUl().show();
+          return doOpen();
+        }
+      }
+    };
+
+    FolderElement.prototype.close = function(slide) {
+      var $button, doClose,
+        _this = this;
+      if (slide == null) {
+        slide = true;
+      }
+      if (this.node.is_open) {
+        this.node.is_open = false;
+        $button = this.getButton();
+        $button.addClass('jqtree-closed');
+        $button.html(this.tree_widget.options.closedIcon);
+        doClose = function() {
+          _this.getLi().addClass('jqtree-closed');
+          return _this.tree_widget._triggerEvent('tree.close', {
+            node: _this.node
+          });
+        };
+        if (slide) {
+          return this.getUl().slideUp('fast', doClose);
+        } else {
+          this.getUl().hide();
+          return doClose();
+        }
+      }
+    };
+
+    FolderElement.prototype.getButton = function() {
+      return this.$element.children('.jqtree-element').find('a.jqtree-toggler');
+    };
+
+    FolderElement.prototype.addDropHint = function(position) {
+      if (!this.node.is_open && position === Position.INSIDE) {
+        return new BorderDropHint(this.$element);
+      } else {
+        return new GhostDropHint(this.node, this.$element, position);
+      }
+    };
+
+    return FolderElement;
+
+  })(NodeElement);
+
+  html_escape = function(string) {
+    return ('' + string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g, '&#x2F;');
+  };
+
+  _indexOf = function(array, item) {
+    var i, value, _i, _len;
+    for (i = _i = 0, _len = array.length; _i < _len; i = ++_i) {
+      value = array[i];
+      if (value === item) {
+        return i;
+      }
+    }
+    return -1;
+  };
+
+  indexOf = function(array, item) {
+    if (array.indexOf) {
+      return array.indexOf(item);
+    } else {
+      return _indexOf(array, item);
+    }
+  };
+
+  this.Tree.indexOf = indexOf;
+
+  this.Tree._indexOf = _indexOf;
+
+  if (!((this.JSON != null) && (this.JSON.stringify != null) && typeof this.JSON.stringify === 'function')) {
+    json_escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
+    json_meta = {
+      '\b': '\\b',
+      '\t': '\\t',
+      '\n': '\\n',
+      '\f': '\\f',
+      '\r': '\\r',
+      '"': '\\"',
+      '\\': '\\\\'
+    };
+    json_quote = function(string) {
+      json_escapable.lastIndex = 0;
+      if (json_escapable.test(string)) {
+        return '"' + string.replace(json_escapable, function(a) {
+          var c;
+          c = json_meta[a];
+          return (typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4));
+        }) + '"';
+      } else {
+        return '"' + string + '"';
+      }
+    };
+    json_str = function(key, holder) {
+      var i, k, partial, v, value, _i, _len;
+      value = holder[key];
+      switch (typeof value) {
+        case 'string':
+          return json_quote(value);
+        case 'number':
+          if (isFinite(value)) {
+            return String(value);
+          } else {
+            return 'null';
+          }
+        case 'boolean':
+        case 'null':
+          return String(value);
+        case 'object':
+          if (!value) {
+            return 'null';
+          }
+          partial = [];
+          if (Object.prototype.toString.apply(value) === '[object Array]') {
+            for (i = _i = 0, _len = value.length; _i < _len; i = ++_i) {
+              v = value[i];
+              partial[i] = json_str(i, value) || 'null';
+            }
+            return (partial.length === 0 ? '[]' : '[' + partial.join(',') + ']');
+          }
+          for (k in value) {
+            if (Object.prototype.hasOwnProperty.call(value, k)) {
+              v = json_str(k, value);
+              if (v) {
+                partial.push(json_quote(k) + ':' + v);
+              }
+            }
+          }
+          return (partial.length === 0 ? '{}' : '{' + partial.join(',') + '}');
+      }
+    };
+    if (this.JSON == null) {
+      this.JSON = {};
+    }
+    this.JSON.stringify = function(value) {
+      return json_str('', {
+        '': value
+      });
+    };
+  }
+
+  SaveStateHandler = (function() {
+    function SaveStateHandler(tree_widget) {
+      this.tree_widget = tree_widget;
+    }
+
+    SaveStateHandler.prototype.saveState = function() {
+      var state;
+      state = JSON.stringify(this.getState());
+      if (this.tree_widget.options.onSetStateFromStorage) {
+        return this.tree_widget.options.onSetStateFromStorage(state);
+      } else if (typeof localStorage !== "undefined" && localStorage !== null) {
+        return localStorage.setItem(this.getCookieName(), state);
+      } else if ($.cookie) {
+        $.cookie.raw = true;
+        return $.cookie(this.getCookieName(), state, {
+          path: '/'
+        });
+      }
+    };
+
+    SaveStateHandler.prototype.restoreState = function() {
+      var state;
+      state = this.getStateFromStorage();
+      if (state) {
+        this.setState($.parseJSON(state));
+        return true;
+      } else {
+        return false;
+      }
+    };
+
+    SaveStateHandler.prototype.getStateFromStorage = function() {
+      if (this.tree_widget.options.onGetStateFromStorage) {
+        return this.tree_widget.options.onGetStateFromStorage();
+      } else if (typeof localStorage !== "undefined" && localStorage !== null) {
+        return localStorage.getItem(this.getCookieName());
+      } else if ($.cookie) {
+        $.cookie.raw = true;
+        return $.cookie(this.getCookieName());
+      } else {
+        return null;
+      }
+    };
+
+    SaveStateHandler.prototype.getState = function() {
+      var open_nodes, selected_node, selected_node_id,
+        _this = this;
+      open_nodes = [];
+      this.tree_widget.tree.iterate(function(node) {
+        if (node.is_open && node.id && node.hasChildren()) {
+          open_nodes.push(node.id);
+        }
+        return true;
+      });
+      selected_node = this.tree_widget.getSelectedNode();
+      if (selected_node) {
+        selected_node_id = selected_node.id;
+      } else {
+        selected_node_id = '';
+      }
+      return {
+        open_nodes: open_nodes,
+        selected_node: selected_node_id
+      };
+    };
+
+    SaveStateHandler.prototype.setState = function(state) {
+      var open_nodes, selected_node, selected_node_id,
+        _this = this;
+      if (state) {
+        open_nodes = state.open_nodes;
+        selected_node_id = state.selected_node;
+        this.tree_widget.tree.iterate(function(node) {
+          node.is_open = node.id && node.hasChildren() && (indexOf(open_nodes, node.id) >= 0);
+          return true;
+        });
+        if (selected_node_id && this.tree_widget.select_node_handler) {
+          this.tree_widget.select_node_handler.clear();
+          selected_node = this.tree_widget.getNodeById(selected_node_id);
+          if (selected_node) {
+            return this.tree_widget.select_node_handler.addToSelection(selected_node);
+          }
+        }
+      }
+    };
+
+    SaveStateHandler.prototype.getCookieName = function() {
+      if (typeof this.tree_widget.options.saveState === 'string') {
+        return this.tree_widget.options.saveState;
+      } else {
+        return 'tree';
+      }
+    };
+
+    return SaveStateHandler;
+
+  })();
+
+  SelectNodeHandler = (function() {
+    function SelectNodeHandler(tree_widget) {
+      this.tree_widget = tree_widget;
+      this.clear();
+    }
+
+    SelectNodeHandler.prototype.getSelectedNode = function() {
+      var selected_nodes;
+      selected_nodes = this.getSelectedNodes();
+      if (selected_nodes.length) {
+        return selected_nodes[0];
+      } else {
+        return false;
+      }
+    };
+
+    SelectNodeHandler.prototype.getSelectedNodes = function() {
+      var id, node, selected_nodes;
+      if (this.selected_single_node) {
+        return [this.selected_single_node];
+      } else {
+        selected_nodes = [];
+        for (id in this.selected_nodes) {
+          node = this.tree_widget.getNodeById(id);
+          if (node) {
+            selected_nodes.push(node);
+          }
+        }
+        return selected_nodes;
+      }
+    };
+
+    SelectNodeHandler.prototype.isNodeSelected = function(node) {
+      if (node.id) {
+        return this.selected_nodes[node.id];
+      } else if (this.selected_single_node) {
+        return this.selected_single_node.element === node.element;
+      } else {
+        return false;
+      }
+    };
+
+    SelectNodeHandler.prototype.clear = function() {
+      this.selected_nodes = {};
+      return this.selected_single_node = null;
+    };
+
+    SelectNodeHandler.prototype.removeFromSelection = function(node, include_children) {
+      var _this = this;
+      if (include_children == null) {
+        include_children = false;
+      }
+      if (!node.id) {
+        if (node.element === this.selected_single_node.element) {
+          return this.selected_single_node = null;
+        }
+      } else {
+        delete this.selected_nodes[node.id];
+        if (include_children) {
+          return node.iterate(function(n) {
+            delete _this.selected_nodes[node.id];
+            return true;
+          });
+        }
+      }
+    };
+
+    SelectNodeHandler.prototype.addToSelection = function(node) {
+      if (node.id) {
+        return this.selected_nodes[node.id] = true;
+      } else {
+        return this.selected_single_node = node;
+      }
+    };
+
+    return SelectNodeHandler;
+
+  })();
+
+  DragAndDropHandler = (function() {
+    function DragAndDropHandler(tree_widget) {
+      this.tree_widget = tree_widget;
+      this.hovered_area = null;
+      this.$ghost = null;
+      this.hit_areas = [];
+      this.is_dragging = false;
+    }
+
+    DragAndDropHandler.prototype.mouseCapture = function(position_info) {
+      var $element, node_element;
+      $element = $(position_info.target);
+      if (this.tree_widget.options.onIsMoveHandle && !this.tree_widget.options.onIsMoveHandle($element)) {
+        return null;
+      }
+      node_element = this.tree_widget._getNodeElement($element);
+      if (node_element && this.tree_widget.options.onCanMove) {
+        if (!this.tree_widget.options.onCanMove(node_element.node)) {
+          node_element = null;
+        }
+      }
+      this.current_item = node_element;
+      return this.current_item !== null;
+    };
+
+    DragAndDropHandler.prototype.mouseStart = function(position_info) {
+      var offset;
+      this.refreshHitAreas();
+      offset = $(position_info.target).offset();
+      this.drag_element = new DragElement(this.current_item.node, position_info.page_x - offset.left, position_info.page_y - offset.top, this.tree_widget.element);
+      this.is_dragging = true;
+      this.current_item.$element.addClass('jqtree-moving');
+      return true;
+    };
+
+    DragAndDropHandler.prototype.mouseDrag = function(position_info) {
+      var area, can_move_to;
+      this.drag_element.move(position_info.page_x, position_info.page_y);
+      area = this.findHoveredArea(position_info.page_x, position_info.page_y);
+      can_move_to = this.canMoveToArea(area);
+      if (area) {
+        if (this.hovered_area !== area) {
+          this.hovered_area = area;
+          if (this.mustOpenFolderTimer(area)) {
+            this.startOpenFolderTimer(area.node);
+          }
+          if (can_move_to) {
+            this.updateDropHint();
+          }
+        }
+      } else {
+        this.removeHover();
+        this.removeDropHint();
+        this.stopOpenFolderTimer();
+      }
+      return true;
+    };
+
+    DragAndDropHandler.prototype.canMoveToArea = function(area) {
+      var position_name;
+      if (!area) {
+        return false;
+      } else if (this.tree_widget.options.onCanMoveTo) {
+        position_name = Position.getName(area.position);
+        return this.tree_widget.options.onCanMoveTo(this.current_item.node, area.node, position_name);
+      } else {
+        return true;
+      }
+    };
+
+    DragAndDropHandler.prototype.mouseStop = function(position_info) {
+      this.moveItem(position_info);
+      this.clear();
+      this.removeHover();
+      this.removeDropHint();
+      this.removeHitAreas();
+      if (this.current_item) {
+        this.current_item.$element.removeClass('jqtree-moving');
+      }
+      this.is_dragging = false;
+      return false;
+    };
+
+    DragAndDropHandler.prototype.refreshHitAreas = function() {
+      this.removeHitAreas();
+      return this.generateHitAreas();
+    };
+
+    DragAndDropHandler.prototype.removeHitAreas = function() {
+      return this.hit_areas = [];
+    };
+
+    DragAndDropHandler.prototype.clear = function() {
+      this.drag_element.remove();
+      return this.drag_element = null;
+    };
+
+    DragAndDropHandler.prototype.removeDropHint = function() {
+      if (this.previous_ghost) {
+        return this.previous_ghost.remove();
+      }
+    };
+
+    DragAndDropHandler.prototype.removeHover = function() {
+      return this.hovered_area = null;
+    };
+
+    DragAndDropHandler.prototype.generateHitAreas = function() {
+      var addPosition, getTop, groupPositions, handleAfterOpenFolder, handleClosedFolder, handleFirstNode, handleNode, handleOpenFolder, hit_areas, last_top, positions,
+        _this = this;
+      positions = [];
+      last_top = 0;
+      getTop = function($element) {
+        return $element.offset().top;
+      };
+      addPosition = function(node, position, top) {
+        positions.push({
+          top: top,
+          node: node,
+          position: position
+        });
+        return last_top = top;
+      };
+      groupPositions = function(handle_group) {
+        var group, position, previous_top, _i, _len;
+        previous_top = -1;
+        group = [];
+        for (_i = 0, _len = positions.length; _i < _len; _i++) {
+          position = positions[_i];
+          if (position.top !== previous_top) {
+            if (group.length) {
+              handle_group(group, previous_top, position.top);
+            }
+            previous_top = position.top;
+            group = [];
+          }
+          group.push(position);
+        }
+        return handle_group(group, previous_top, _this.tree_widget.element.offset().top + _this.tree_widget.element.height());
+      };
+      handleNode = function(node, next_node, $element) {
+        var top;
+        top = getTop($element);
+        if (node === _this.current_item.node) {
+          addPosition(node, Position.NONE, top);
+        } else {
+          addPosition(node, Position.INSIDE, top);
+        }
+        if (next_node === _this.current_item.node || node === _this.current_item.node) {
+          return addPosition(node, Position.NONE, top);
+        } else {
+          return addPosition(node, Position.AFTER, top);
+        }
+      };
+      handleOpenFolder = function(node, $element) {
+        if (node === _this.current_item.node) {
+          return false;
+        }
+        if (node.children[0] !== _this.current_item.node) {
+          addPosition(node, Position.INSIDE, getTop($element));
+        }
+        return true;
+      };
+      handleAfterOpenFolder = function(node, next_node, $element) {
+        if (node === _this.current_item.node || next_node === _this.current_item.node) {
+          return addPosition(node, Position.NONE, last_top);
+        } else {
+          return addPosition(node, Position.AFTER, last_top);
+        }
+      };
+      handleClosedFolder = function(node, next_node, $element) {
+        var top;
+        top = getTop($element);
+        if (node === _this.current_item.node) {
+          return addPosition(node, Position.NONE, top);
+        } else {
+          addPosition(node, Position.INSIDE, top);
+          if (next_node !== _this.current_item.node) {
+            return addPosition(node, Position.AFTER, top);
+          }
+        }
+      };
+      handleFirstNode = function(node, $element) {
+        if (node !== _this.current_item.node) {
+          return addPosition(node, Position.BEFORE, getTop($(node.element)));
+        }
+      };
+      this.iterateVisibleNodes(handleNode, handleOpenFolder, handleClosedFolder, handleAfterOpenFolder, handleFirstNode);
+      hit_areas = [];
+      groupPositions(function(positions_in_group, top, bottom) {
+        var area_height, area_top, position, _i, _len;
+        area_height = (bottom - top) / positions_in_group.length;
+        area_top = top;
+        for (_i = 0, _len = positions_in_group.length; _i < _len; _i++) {
+          position = positions_in_group[_i];
+          hit_areas.push({
+            top: area_top,
+            bottom: area_top + area_height,
+            node: position.node,
+            position: position.position
+          });
+          area_top += area_height;
+        }
+        return null;
+      });
+      return this.hit_areas = hit_areas;
+    };
+
+    DragAndDropHandler.prototype.iterateVisibleNodes = function(handle_node, handle_open_folder, handle_closed_folder, handle_after_open_folder, handle_first_node) {
+      var is_first_node, iterate,
+        _this = this;
+      is_first_node = true;
+      iterate = function(node, next_node) {
+        var $element, child, children_length, i, must_iterate_inside, _i, _len, _ref3;
+        must_iterate_inside = (node.is_open || !node.element) && node.hasChildren();
+        if (node.element) {
+          $element = $(node.element);
+          if (!$element.is(':visible')) {
+            return;
+          }
+          if (is_first_node) {
+            handle_first_node(node, $element);
+            is_first_node = false;
+          }
+          if (!node.hasChildren()) {
+            handle_node(node, next_node, $element);
+          } else if (node.is_open) {
+            if (!handle_open_folder(node, $element)) {
+              must_iterate_inside = false;
+            }
+          } else {
+            handle_closed_folder(node, next_node, $element);
+          }
+        }
+        if (must_iterate_inside) {
+          children_length = node.children.length;
+          _ref3 = node.children;
+          for (i = _i = 0, _len = _ref3.length; _i < _len; i = ++_i) {
+            child = _ref3[i];
+            if (i === (children_length - 1)) {
+              iterate(node.children[i], null);
+            } else {
+              iterate(node.children[i], node.children[i + 1]);
+            }
+          }
+          if (node.is_open) {
+            return handle_after_open_folder(node, next_node, $element);
+          }
+        }
+      };
+      return iterate(this.tree_widget.tree);
+    };
+
+    DragAndDropHandler.prototype.findHoveredArea = function(x, y) {
+      var area, high, low, mid, tree_offset;
+      tree_offset = this.tree_widget.element.offset();
+      if (x < tree_offset.left || y < tree_offset.top || x > (tree_offset.left + this.tree_widget.element.width()) || y > (tree_offset.top + this.tree_widget.element.height())) {
+        return null;
+      }
+      low = 0;
+      high = this.hit_areas.length;
+      while (low < high) {
+        mid = (low + high) >> 1;
+        area = this.hit_areas[mid];
+        if (y < area.top) {
+          high = mid;
+        } else if (y > area.bottom) {
+          low = mid + 1;
+        } else {
+          return area;
+        }
+      }
+      return null;
+    };
+
+    DragAndDropHandler.prototype.mustOpenFolderTimer = function(area) {
+      var node;
+      node = area.node;
+      return node.isFolder() && !node.is_open && area.position === Position.INSIDE;
+    };
+
+    DragAndDropHandler.prototype.updateDropHint = function() {
+      var node_element;
+      if (!this.hovered_area) {
+        return;
+      }
+      this.removeDropHint();
+      node_element = this.tree_widget._getNodeElementForNode(this.hovered_area.node);
+      return this.previous_ghost = node_element.addDropHint(this.hovered_area.position);
+    };
+
+    DragAndDropHandler.prototype.startOpenFolderTimer = function(folder) {
+      var openFolder,
+        _this = this;
+      openFolder = function() {
+        return _this.tree_widget._openNode(folder, _this.tree_widget.options.slide, function() {
+          _this.refreshHitAreas();
+          return _this.updateDropHint();
+        });
+      };
+      return this.open_folder_timer = setTimeout(openFolder, 500);
+    };
+
+    DragAndDropHandler.prototype.stopOpenFolderTimer = function() {
+      if (this.open_folder_timer) {
+        clearTimeout(this.open_folder_timer);
+        return this.open_folder_timer = null;
+      }
+    };
+
+    DragAndDropHandler.prototype.moveItem = function(position_info) {
+      var doMove, event, moved_node, position, previous_parent, target_node,
+        _this = this;
+      if (this.hovered_area && this.hovered_area.position !== Position.NONE && this.canMoveToArea(this.hovered_area)) {
+        moved_node = this.current_item.node;
+        target_node = this.hovered_area.node;
+        position = this.hovered_area.position;
+        previous_parent = moved_node.parent;
+        if (position === Position.INSIDE) {
+          this.hovered_area.node.is_open = true;
+        }
+        doMove = function() {
+          _this.tree_widget.tree.moveNode(moved_node, target_node, position);
+          _this.tree_widget.element.empty();
+          return _this.tree_widget._refreshElements();
+        };
+        event = this.tree_widget._triggerEvent('tree.move', {
+          move_info: {
+            moved_node: moved_node,
+            target_node: target_node,
+            position: Position.getName(position),
+            previous_parent: previous_parent,
+            do_move: doMove,
+            original_event: position_info.original_event
+          }
+        });
+        if (!event.isDefaultPrevented()) {
+          return doMove();
+        }
+      }
+    };
+
+    return DragAndDropHandler;
+
+  })();
+
+  DragElement = (function() {
+    function DragElement(node, offset_x, offset_y, $tree) {
+      this.offset_x = offset_x;
+      this.offset_y = offset_y;
+      this.$element = $("<span class=\"jqtree-title jqtree-dragging\">" + node.name + "</span>");
+      this.$element.css("position", "absolute");
+      $tree.append(this.$element);
+    }
+
+    DragElement.prototype.move = function(page_x, page_y) {
+      return this.$element.offset({
+        left: page_x - this.offset_x,
+        top: page_y - this.offset_y
+      });
+    };
+
+    DragElement.prototype.remove = function() {
+      return this.$element.remove();
+    };
+
+    return DragElement;
+
+  })();
+
+  GhostDropHint = (function() {
+    function GhostDropHint(node, $element, position) {
+      this.$element = $element;
+      this.node = node;
+      this.$ghost = $('<li class="jqtree_common jqtree-ghost"><span class="jqtree_common jqtree-circle"></span><span class="jqtree_common jqtree-line"></span></li>');
+      if (position === Position.AFTER) {
+        this.moveAfter();
+      } else if (position === Position.BEFORE) {
+        this.moveBefore();
+      } else if (position === Position.INSIDE) {
+        if (node.isFolder() && node.is_open) {
+          this.moveInsideOpenFolder();
+        } else {
+          this.moveInside();
+        }
+      }
+    }
+
+    GhostDropHint.prototype.remove = function() {
+      return this.$ghost.remove();
+    };
+
+    GhostDropHint.prototype.moveAfter = function() {
+      return this.$element.after(this.$ghost);
+    };
+
+    GhostDropHint.prototype.moveBefore = function() {
+      return this.$element.before(this.$ghost);
+    };
+
+    GhostDropHint.prototype.moveInsideOpenFolder = function() {
+      return $(this.node.children[0].element).before(this.$ghost);
+    };
+
+    GhostDropHint.prototype.moveInside = function() {
+      this.$element.after(this.$ghost);
+      return this.$ghost.addClass('jqtree-inside');
+    };
+
+    return GhostDropHint;
+
+  })();
+
+  BorderDropHint = (function() {
+    function BorderDropHint($element) {
+      var $div, width;
+      $div = $element.children('.jqtree-element');
+      width = $element.width() - 4;
+      this.$hint = $('<span class="jqtree-border"></span>');
+      $div.append(this.$hint);
+      this.$hint.css({
+        width: width,
+        height: $div.height() - 4
+      });
+    }
+
+    BorderDropHint.prototype.remove = function() {
+      return this.$hint.remove();
+    };
+
+    return BorderDropHint;
+
+  })();
+
+  ScrollHandler = (function() {
+    function ScrollHandler(tree_widget) {
+      this.tree_widget = tree_widget;
+      this.previous_top = -1;
+      this._initScrollParent();
+    }
+
+    ScrollHandler.prototype._initScrollParent = function() {
+      var $scroll_parent, getParentWithOverflow, setDocumentAsScrollParent,
+        _this = this;
+      getParentWithOverflow = function() {
+        var css_value, css_values, parent, scroll_parent, _i, _j, _len, _len1, _ref3, _ref4;
+        css_values = ['overflow', 'overflow-y'];
+        scroll_parent = null;
+        _ref3 = _this.tree_widget.$el.parents();
+        for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
+          parent = _ref3[_i];
+          for (_j = 0, _len1 = css_values.length; _j < _len1; _j++) {
+            css_value = css_values[_j];
+            if ((_ref4 = $.css(parent, css_value)) === 'auto' || _ref4 === 'scroll') {
+              return $(parent);
+            }
+          }
+        }
+        return null;
+      };
+      setDocumentAsScrollParent = function() {
+        _this.scroll_parent_top = 0;
+        return _this.$scroll_parent = null;
+      };
+      if (this.tree_widget.$el.css('position') === 'fixed') {
+        setDocumentAsScrollParent();
+      }
+      $scroll_parent = getParentWithOverflow();
+      if ($scroll_parent && $scroll_parent.length && $scroll_parent[0].tagName !== 'HTML') {
+        this.$scroll_parent = $scroll_parent;
+        return this.scroll_parent_top = this.$scroll_parent.offset().top;
+      } else {
+        return setDocumentAsScrollParent();
+      }
+    };
+
+    ScrollHandler.prototype.checkScrolling = function() {
+      var hovered_area;
+      hovered_area = this.tree_widget.dnd_handler.hovered_area;
+      if (hovered_area && hovered_area.top !== this.previous_top) {
+        this.previous_top = hovered_area.top;
+        if (this.$scroll_parent) {
+          return this._handleScrollingWithScrollParent(hovered_area);
+        } else {
+          return this._handleScrollingWithDocument(hovered_area);
+        }
+      }
+    };
+
+    ScrollHandler.prototype._handleScrollingWithScrollParent = function(area) {
+      var distance_bottom;
+      distance_bottom = this.scroll_parent_top + this.$scroll_parent[0].offsetHeight - area.bottom;
+      if (distance_bottom < 20) {
+        this.$scroll_parent[0].scrollTop += 20;
+        this.tree_widget.refreshHitAreas();
+        return this.previous_top = -1;
+      } else if ((area.top - this.scroll_parent_top) < 20) {
+        this.$scroll_parent[0].scrollTop -= 20;
+        this.tree_widget.refreshHitAreas();
+        return this.previous_top = -1;
+      }
+    };
+
+    ScrollHandler.prototype._handleScrollingWithDocument = function(area) {
+      var distance_top;
+      distance_top = area.top - $(document).scrollTop();
+      if (distance_top < 20) {
+        return $(document).scrollTop($(document).scrollTop() - 20);
+      } else if ($(window).height() - (area.bottom - $(document).scrollTop()) < 20) {
+        return $(document).scrollTop($(document).scrollTop() + 20);
+      }
+    };
+
+    ScrollHandler.prototype.scrollTo = function(top) {
+      var tree_top;
+      if (this.$scroll_parent) {
+        return this.$scroll_parent[0].scrollTop = top;
+      } else {
+        tree_top = this.tree_widget.$el.offset().top;
+        return $(document).scrollTop(top + tree_top);
+      }
+    };
+
+    ScrollHandler.prototype.isScrolledIntoView = function(element) {
+      var $element, element_bottom, element_top, view_bottom, view_top;
+      $element = $(element);
+      if (this.$scroll_parent) {
+        view_top = 0;
+        view_bottom = this.$scroll_parent.height();
+        element_top = $element.offset().top - this.scroll_parent_top;
+        element_bottom = element_top + $element.height();
+      } else {
+        view_top = $(window).scrollTop();
+        view_bottom = view_top + $(window).height();
+        element_top = $element.offset().top;
+        element_bottom = element_top + $element.height();
+      }
+      return (element_bottom <= view_bottom) && (element_top >= view_top);
+    };
+
+    return ScrollHandler;
+
+  })();
+
+  KeyHandler = (function() {
+    var DOWN, LEFT, RIGHT, UP;
+
+    LEFT = 37;
+
+    UP = 38;
+
+    RIGHT = 39;
+
+    DOWN = 40;
+
+    function KeyHandler(tree_widget) {
+      this.tree_widget = tree_widget;
+      $(document).bind('keydown.jqtree', $.proxy(this.handleKeyDown, this));
+    }
+
+    KeyHandler.prototype.deinit = function() {
+      return $(document).unbind('keydown.jqtree');
+    };
+
+    KeyHandler.prototype.handleKeyDown = function(e) {
+      var current_node, key, moveDown, moveLeft, moveRight, moveUp, selectNode,
+        _this = this;
+      current_node = this.tree_widget.getSelectedNode();
+      selectNode = function(node) {
+        if (node) {
+          _this.tree_widget.selectNode(node);
+          if (_this.tree_widget.scroll_handler && (!_this.tree_widget.scroll_handler.isScrolledIntoView($(node.element).find('.jqtree-element')))) {
+            _this.tree_widget.scrollToNode(node);
+          }
+          return false;
+        } else {
+          return true;
+        }
+      };
+      moveDown = function() {
+        return selectNode(_this.getNextNode(current_node));
+      };
+      moveUp = function() {
+        return selectNode(_this.getPreviousNode(current_node));
+      };
+      moveRight = function() {
+        if (current_node.hasChildren() && !current_node.is_open) {
+          _this.tree_widget.openNode(current_node);
+          return false;
+        } else {
+          return true;
+        }
+      };
+      moveLeft = function() {
+        if (current_node.hasChildren() && current_node.is_open) {
+          _this.tree_widget.closeNode(current_node);
+          return false;
+        } else {
+          return true;
+        }
+      };
+      if (!current_node) {
+        return true;
+      } else {
+        key = e.which;
+        switch (key) {
+          case DOWN:
+            return moveDown();
+          case UP:
+            return moveUp();
+          case RIGHT:
+            return moveRight();
+          case LEFT:
+            return moveLeft();
+        }
+      }
+    };
+
+    KeyHandler.prototype.getNextNode = function(node, include_children) {
+      var next_sibling;
+      if (include_children == null) {
+        include_children = true;
+      }
+      if (include_children && node.hasChildren() && node.is_open) {
+        return node.children[0];
+      } else {
+        if (!node.parent) {
+          return null;
+        } else {
+          next_sibling = node.getNextSibling();
+          if (next_sibling) {
+            return next_sibling;
+          } else {
+            return this.getNextNode(node.parent, false);
+          }
+        }
+      }
+    };
+
+    KeyHandler.prototype.getPreviousNode = function(node) {
+      var previous_sibling;
+      if (!node.parent) {
+        return null;
+      } else {
+        previous_sibling = node.getPreviousSibling();
+        if (previous_sibling) {
+          if (!previous_sibling.hasChildren() || !previous_sibling.is_open) {
+            return previous_sibling;
+          } else {
+            return this.getLastChild(previous_sibling);
+          }
+        } else {
+          if (node.parent.parent) {
+            return node.parent;
+          } else {
+            return null;
+          }
+        }
+      }
+    };
+
+    KeyHandler.prototype.getLastChild = function(node) {
+      var last_child;
+      if (!node.hasChildren()) {
+        return null;
+      } else {
+        last_child = node.children[node.children.length - 1];
+        if (!last_child.hasChildren() || !last_child.is_open) {
+          return last_child;
+        } else {
+          return this.getLastChild(last_child);
+        }
+      }
+    };
+
+    return KeyHandler;
+
+  })();
+
+}).call(this);
diff --git a/tools/deep_memory_profiler/visualizer/utility.js b/tools/deep_memory_profiler/visualizer/utility.js
index f595fd0..cdb7243 100644
--- a/tools/deep_memory_profiler/visualizer/utility.js
+++ b/tools/deep_memory_profiler/visualizer/utility.js
@@ -6,22 +6,51 @@
  * This function returns the first element index that >= target, when no element
  * in the array >= target, return array.length.
  * This function must be called in the shape of binarySearch(array, target).
- * @param  {number} target
+ * @param {number} target
  * @return {number}
  */
 var binarySearch = function(target) {
-    'use strict';
+  'use strict';
 
-    var left = 0;
-    var right = this.length - 1;
-    while (left <= right) {
-        var mid = Math.floor((left + right) / 2);
-        if (this[mid] < target)
-            left = mid + 1;
-        else if (this[mid] > target)
-            right = mid - 1;
-        else
-            return mid;
-    }
-    return left;
+  var left = 0;
+  var right = this.length - 1;
+  while (left <= right) {
+    var mid = Math.floor((left + right) / 2);
+    if (this[mid] < target)
+      left = mid + 1;
+    else if (this[mid] > target)
+      right = mid - 1;
+    else
+      return mid;
+  }
+  return left;
 };
+
+/**
+ * Return the intersection set of two arrays.
+ * @param {Array.<*>} left
+ * @param {Array.<*>} right
+ * @return {Array.<*>}
+ */
+var intersection = function(left, right) {
+  return left.reduce(function(previous, current) {
+    if (right.indexOf(current) != -1)
+      previous.push(current);
+    return previous;
+  }, []);
+};
+
+/**
+ * Return the difference set of left with right.
+ * Notice: difference(left, right) differentiates with difference(right, left).
+ * @param {Array.<*>} left
+ * @param {Array.<*>} right
+ * @return {Array.<*>}
+ */
+var difference = function(left, right) {
+  return left.reduce(function(previous, current) {
+    if (right.indexOf(current) == -1)
+      previous.push(current);
+    return previous;
+  }, []);
+};
\ No newline at end of file
diff --git a/tools/gn/BUILD.gn b/tools/gn/BUILD.gn
index a2647e2..a993a19 100644
--- a/tools/gn/BUILD.gn
+++ b/tools/gn/BUILD.gn
@@ -9,6 +9,7 @@
     "command_args.cc",
     "command_desc.cc",
     "command_gen.cc",
+    "command_gyp.cc",
     "command_help.cc",
     "command_refs.cc",
     "commands.cc",
@@ -159,6 +160,7 @@
     "parser_unittest.cc",
     "path_output_unittest.cc",
     "pattern_unittest.cc",
+    "scope_per_file_provider_unittest.cc",
     "source_dir_unittest.cc",
     "string_utils_unittest.cc",
     "target_generator_unittest.cc",
@@ -167,9 +169,9 @@
   ]
   deps = [
     ":gn_lib",
-    "//base/test:run_all_unittests",
-    "//base/test:test_support_base",
-    "//testing/gtest",
+    "//base:run_all_unittests",
+    "//base:test_support_base",
+    "//testing:gtest",
   ]
 }
 
diff --git a/tools/gn/args.cc b/tools/gn/args.cc
index d340713..d11892b 100644
--- a/tools/gn/args.cc
+++ b/tools/gn/args.cc
@@ -55,12 +55,12 @@
 Args::~Args() {
 }
 
-void Args::SwapInArgOverrides(Scope::KeyValueMap* overrides) {
-  DCHECK(overrides_.empty());
-  overrides_.swap(*overrides);
-  for (Scope::KeyValueMap::const_iterator i = overrides_.begin();
-       i != overrides_.end(); ++i)
+void Args::AddArgOverrides(const Scope::KeyValueMap& overrides) {
+  for (Scope::KeyValueMap::const_iterator i = overrides.begin();
+       i != overrides.end(); ++i) {
+    overrides_.insert(*i);
     all_overrides_.insert(*i);
+  }
 }
 
 void Args::SetupRootScope(Scope* dest,
diff --git a/tools/gn/args.h b/tools/gn/args.h
index 4d2287d..d3225ca 100644
--- a/tools/gn/args.h
+++ b/tools/gn/args.h
@@ -27,7 +27,7 @@
 
   // Specifies overrides of the build arguments. These are normally specified
   // on the command line.
-  void SwapInArgOverrides(Scope::KeyValueMap* overrides);
+  void AddArgOverrides(const Scope::KeyValueMap& overrides);
 
   // Sets up the root scope for a toolchain. This applies the default system
   // flags, then any overrides stored in this object, then applies any
diff --git a/tools/gn/binary_target_generator.cc b/tools/gn/binary_target_generator.cc
index 4638be4..6d12768 100644
--- a/tools/gn/binary_target_generator.cc
+++ b/tools/gn/binary_target_generator.cc
@@ -7,6 +7,7 @@
 #include "tools/gn/config_values_generator.h"
 #include "tools/gn/err.h"
 #include "tools/gn/scope.h"
+#include "tools/gn/variables.h"
 
 BinaryTargetGenerator::BinaryTargetGenerator(Target* target,
                                              Scope* scope,
@@ -23,6 +24,7 @@
 void BinaryTargetGenerator::DoRun() {
   target_->set_output_type(output_type_);
 
+  FillExternal();
   FillSources();
   FillConfigs();
 
diff --git a/tools/gn/build_settings.cc b/tools/gn/build_settings.cc
index ebe0323..6b11a28 100644
--- a/tools/gn/build_settings.cc
+++ b/tools/gn/build_settings.cc
@@ -7,7 +7,8 @@
 #include "tools/gn/filesystem_utils.h"
 
 BuildSettings::BuildSettings()
-    : item_tree_(),
+    : using_external_generator_(false),
+      item_tree_(),
       target_manager_(this),
       toolchain_manager_(this) {
 }
diff --git a/tools/gn/build_settings.h b/tools/gn/build_settings.h
index 454eca4..429a183 100644
--- a/tools/gn/build_settings.h
+++ b/tools/gn/build_settings.h
@@ -39,6 +39,13 @@
   }
   void SetSecondarySourcePath(const SourceDir& d);
 
+  // Set when we're running an external generator (e.g. GYP) and should
+  // enable "external" flags on targets.
+  bool using_external_generator() const { return using_external_generator_; }
+  void set_using_external_generator(bool ueg) {
+    using_external_generator_ = ueg;
+  }
+
   // Path of the python executable to run scripts with.
   base::FilePath python_path() const { return python_path_; }
   void set_python_path(const base::FilePath& p) { python_path_ = p; }
@@ -98,6 +105,7 @@
   base::FilePath root_path_;
   std::string root_path_utf8_;
   base::FilePath secondary_source_path_;
+  bool using_external_generator_;
   base::FilePath python_path_;
 
   SourceFile build_config_file_;
diff --git a/tools/gn/command_gen.cc b/tools/gn/command_gen.cc
index 7d8203d..9f6d38f 100644
--- a/tools/gn/command_gen.cc
+++ b/tools/gn/command_gen.cc
@@ -39,6 +39,7 @@
     "\n"
     "  See \"gn help\" for the common command-line switches.\n";
 
+// Note: partially duplicated in command_gyp.cc.
 int RunGen(const std::vector<std::string>& args) {
   base::TimeTicks begin_time = base::TimeTicks::Now();
 
diff --git a/tools/gn/command_gyp.cc b/tools/gn/command_gyp.cc
new file mode 100644
index 0000000..80b23cb
--- /dev/null
+++ b/tools/gn/command_gyp.cc
@@ -0,0 +1,245 @@
+// Copyright (c) 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 <fstream>
+
+#include "base/atomicops.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/process/launch.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "tools/gn/build_settings.h"
+#include "tools/gn/commands.h"
+#include "tools/gn/err.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/ninja_target_writer.h"
+#include "tools/gn/ninja_writer.h"
+#include "tools/gn/path_output.h"
+#include "tools/gn/setup.h"
+#include "tools/gn/standard_out.h"
+
+namespace commands {
+
+namespace {
+
+// Suppress output on success.
+const char kSwitchQuiet[] = "q";
+
+// Skip actually executing GYP. This is for when you're working on the GN
+// build and don't want to wait for GYP to regenerate. All GN files are
+// regenerated, but the GYP ones are not.
+const char kSwitchNoGyp[] = "no-gyp";
+
+void TargetResolvedCallback(base::subtle::Atomic32* write_counter,
+                            const Target* target) {
+  base::subtle::NoBarrier_AtomicIncrement(write_counter, 1);
+  NinjaTargetWriter::RunAndWriteFile(target);
+}
+
+bool SimpleNinjaParse(const std::string& data,
+                      std::set<std::string>* subninjas,
+                      size_t* first_subninja_offset) {
+  const size_t kSubninjaPrefixLen = 10;
+  const char kSubninjaPrefix[kSubninjaPrefixLen + 1] = "\nsubninja ";
+
+  *first_subninja_offset = std::string::npos;
+  size_t next_subninja = 0;
+  while ((next_subninja = data.find(kSubninjaPrefix, next_subninja)) !=
+         std::string::npos) {
+    if (*first_subninja_offset == std::string::npos)
+      *first_subninja_offset = next_subninja;
+
+    size_t line_end = data.find('\n', next_subninja + 1);
+    if (line_end == std::string::npos)
+      return false;
+
+    std::string filename = data.substr(
+        next_subninja + kSubninjaPrefixLen,
+        line_end - next_subninja - kSubninjaPrefixLen);
+    subninjas->insert(filename);
+
+    next_subninja = line_end;
+  }
+  return *first_subninja_offset != std::string::npos;
+}
+
+bool FixupBuildNinja(const BuildSettings* build_settings,
+                     const base::FilePath& buildfile) {
+  std::string contents;
+  if (!file_util::ReadFileToString(buildfile, &contents)) {
+    Err(Location(), "Could not load " + FilePathToUTF8(buildfile))
+        .PrintToStdout();
+    return false;
+  }
+
+  std::set<std::string> subninjas;
+  size_t first_subninja_offset = 0;
+  if (!SimpleNinjaParse(contents, &subninjas, &first_subninja_offset)) {
+    Err(Location(), "Could not parse " + FilePathToUTF8(buildfile))
+        .PrintToStdout();
+    return false;
+  }
+
+  // Write toolchain files.
+  std::vector<const Settings*> all_settings;
+  if (!NinjaWriter::RunAndWriteToolchainFiles(
+          build_settings, subninjas, &all_settings))
+    return false;
+
+  // Copy first part of buildfile to the output.
+  std::ofstream file;
+  file.open(FilePathToUTF8(buildfile).c_str(),
+            std::ios_base::out | std::ios_base::binary);
+  if (file.fail()) {
+    Err(Location(), "Could not write " + FilePathToUTF8(buildfile))
+        .PrintToStdout();
+    return false;
+  }
+  file.write(contents.data(), first_subninja_offset);
+
+  // Add refs for our toolchains to the original build.ninja.
+  NinjaHelper helper(build_settings);
+  PathOutput path_output(build_settings->build_dir(), ESCAPE_NINJA, true);
+  file << "\n# GN-added toolchain files.\n";
+  for (size_t i = 0; i < all_settings.size(); i++) {
+    file << "subninja ";
+    path_output.WriteFile(file,
+                          helper.GetNinjaFileForToolchain(all_settings[i]));
+    file << std::endl;
+  }
+  file << "\n# GYP-written subninjas.";
+
+  // Write remaining old subninjas from original file.
+  file.write(&contents[first_subninja_offset],
+             contents.size() - first_subninja_offset);
+  return true;
+}
+
+bool RunGyp(const BuildSettings* build_settings) {
+  if (!CommandLine::ForCurrentProcess()->HasSwitch(kSwitchQuiet))
+    OutputString("Running GYP...\n");
+
+  const base::FilePath& python_path = build_settings->python_path();
+
+  // Construct the command line. Note that AppendArgPath and AppendSwitchPath
+  // don't preserve the relative ordering, and we need the python file to be
+  // first, so we have to convert switch values to strings before appending.
+  //
+  // Note that GYP will get confused if this path is quoted, so don't quote it
+  // and hope that there are no spaces!
+  CommandLine cmdline(python_path);
+  cmdline.AppendArgPath(
+      build_settings->GetFullPath(SourceFile("//build/gyp_chromium.py")));
+  cmdline.AppendArg("-G");
+  cmdline.AppendArg("output_dir=out.gn");
+  cmdline.AppendArg("-f");
+  cmdline.AppendArg("ninja");
+
+  std::string output;
+  if (!base::GetAppOutput(cmdline, &output)) {
+    Err(Location(), "GYP execution failed.", output).PrintToStdout();
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+const char kGyp[] = "gyp";
+const char kGyp_HelpShort[] =
+    "gyp: Run GYP and then GN.";
+const char kGyp_Help[] =
+    "gyp: Run GYP and then GN.\n"
+    "\n"
+    "  Generate a hybrid GYP/GN build where some targets are generated by\n"
+    "  each of the tools. As long as target names and locations match between\n"
+    "  the two tools, they can depend on each other.\n"
+    "\n"
+    "  When GN is run in this mode, it will not write out any targets\n"
+    "  annotated with \"external = true\". Otherwise, GYP targets with the\n"
+    "  same name and location will be overwritten.\n"
+    "\n"
+    "  References to the GN ninja files will be inserted into the\n"
+    "  GYP-generated build.ninja file.\n"
+    "\n"
+    "Option:\n"
+    "  --no-gyp\n"
+    "      Don't actually run GYP or modify build.ninja. This is used when\n"
+    "      working on the GN build when it is known that no GYP files have\n"
+    "      changed and you want it to run faster.\n";
+
+// Note: partially duplicated from command_gen.cc.
+int RunGyp(const std::vector<std::string>& args) {
+  const CommandLine* cmdline = CommandLine::ForCurrentProcess();
+  bool no_gyp = cmdline->HasSwitch(kSwitchNoGyp);
+
+  // Deliberately leaked to avoid expensive process teardown.
+  Setup* setup = new Setup;
+  if (!setup->DoSetup())
+    return 1;
+
+  setup->build_settings().SetBuildDir(SourceDir("//out.gn/Debug/"));
+  setup->build_settings().set_using_external_generator(true);
+
+  // Provide a way for buildfiles to know we're doing a GYP build.
+  /*
+  Scope::KeyValueMap variable_overrides;
+  variable_overrides["is_gyp"] = Value(NULL, true);
+  setup->build_settings().build_args().AddArgOverrides(variable_overrides);
+  */
+
+  base::TimeTicks begin_time = base::TimeTicks::Now();
+  if (!no_gyp) {
+    if (!RunGyp(&setup->build_settings()))
+      return 1;
+  }
+  base::TimeTicks end_gyp_time = base::TimeTicks::Now();
+
+  if (!cmdline->HasSwitch(kSwitchQuiet))
+    OutputString("Running GN...\n");
+
+  // Cause the load to also generate the ninja files for each target. We wrap
+  // the writing to maintain a counter.
+  base::subtle::Atomic32 write_counter = 0;
+  setup->build_settings().set_target_resolved_callback(
+      base::Bind(&TargetResolvedCallback, &write_counter));
+
+  // Do the actual load. This will also write out the target ninja files.
+  if (!setup->Run())
+    return 1;
+
+  // Integrate with the GYP build.
+  if (!no_gyp) {
+    base::FilePath ninja_buildfile(setup->build_settings().GetFullPath(
+        SourceFile(setup->build_settings().build_dir().value() +
+                   "build.ninja")));
+    if (!FixupBuildNinja(&setup->build_settings(), ninja_buildfile))
+      return 1;
+  }
+
+  // Timing info.
+  base::TimeTicks end_time = base::TimeTicks::Now();
+  if (!cmdline->HasSwitch(kSwitchQuiet)) {
+    OutputString("Done. ", DECORATION_GREEN);
+
+    std::string stats = "Wrote " +
+        base::IntToString(static_cast<int>(write_counter)) +
+        " targets from " +
+        base::IntToString(
+            setup->scheduler().input_file_manager()->GetInputFileCount()) +
+        " files in " +
+        base::IntToString((end_time - end_gyp_time).InMilliseconds()) + "ms " +
+        "(GYP took " +
+        base::IntToString((end_gyp_time - begin_time).InMilliseconds()) +
+        "ms)\n";
+
+    OutputString(stats);
+  }
+
+  return 0;
+}
+
+}  // namespace commands
diff --git a/tools/gn/commands.cc b/tools/gn/commands.cc
index c632cce..e600872 100644
--- a/tools/gn/commands.cc
+++ b/tools/gn/commands.cc
@@ -37,6 +37,7 @@
     INSERT_COMMAND(Args)
     INSERT_COMMAND(Desc)
     INSERT_COMMAND(Gen)
+    INSERT_COMMAND(Gyp)
     INSERT_COMMAND(Help)
     INSERT_COMMAND(Refs)
 
diff --git a/tools/gn/commands.h b/tools/gn/commands.h
index 1900820..bcd0c2e 100644
--- a/tools/gn/commands.h
+++ b/tools/gn/commands.h
@@ -34,6 +34,11 @@
 extern const char kGen_Help[];
 int RunGen(const std::vector<std::string>& args);
 
+extern const char kGyp[];
+extern const char kGyp_HelpShort[];
+extern const char kGyp_Help[];
+int RunGyp(const std::vector<std::string>& args);
+
 extern const char kHelp[];
 extern const char kHelp_HelpShort[];
 extern const char kHelp_Help[];
diff --git a/tools/gn/copy_target_generator.cc b/tools/gn/copy_target_generator.cc
index 6347ca2..18ecc3f 100644
--- a/tools/gn/copy_target_generator.cc
+++ b/tools/gn/copy_target_generator.cc
@@ -22,6 +22,7 @@
 void CopyTargetGenerator::DoRun() {
   target_->set_output_type(Target::COPY_FILES);
 
+  FillExternal();
   FillSources();
   FillDestDir();
 
diff --git a/tools/gn/filesystem_utils.cc b/tools/gn/filesystem_utils.cc
index dfaa303..75b72b2 100644
--- a/tools/gn/filesystem_utils.cc
+++ b/tools/gn/filesystem_utils.cc
@@ -5,6 +5,7 @@
 #include "tools/gn/filesystem_utils.h"
 
 #include "base/logging.h"
+#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "tools/gn/location.h"
@@ -63,7 +64,49 @@
   return NOT_A_DIRECTORY;
 }
 
-}  // namesapce
+#if defined(OS_WIN)
+inline char NormalizeWindowsPathChar(char c) {
+  if (c == '/')
+    return '\\';
+  return base::ToLowerASCII(c);
+}
+
+// Attempts to do a case and slash-insensitive comparison of two 8-bit Windows
+// paths.
+bool AreAbsoluteWindowsPathsEqual(const base::StringPiece& a,
+                                  const base::StringPiece& b) {
+  if (a.size() != b.size())
+    return false;
+
+  // For now, just do a case-insensitive ASCII comparison. We could convert to
+  // UTF-16 and use ICU if necessary. Or maybe base::strcasecmp is good enough?
+  for (size_t i = 0; i < a.size(); i++) {
+    if (NormalizeWindowsPathChar(a[i]) != NormalizeWindowsPathChar(b[i]))
+      return false;
+  }
+  return true;
+}
+
+bool DoesBeginWindowsDriveLetter(const base::StringPiece& path) {
+  if (path.size() < 3)
+    return false;
+
+  // Check colon first, this will generally fail fastest.
+  if (path[1] != ':')
+    return false;
+
+  // Check drive letter
+  if (!((path[0] >= 'A' && path[0] <= 'Z') ||
+         path[0] >= 'a' && path[0] <= 'z'))
+    return false;
+
+  if (path[2] != '/' && path[2] != '\\')
+    return false;
+  return true;
+}
+#endif
+
+}  // namespace
 
 SourceFileType GetSourceFileType(const SourceFile& file,
                                  Settings::TargetOS os) {
@@ -286,25 +329,60 @@
   // Source root should be canonical on Windows.
   DCHECK(source_root.size() > 2 && source_root[0] != '/' &&
          source_root[1] == ':' && source_root[2] =='\\');
-#error
+
+  size_t after_common_index = std::string::npos;
+  if (DoesBeginWindowsDriveLetter(path)) {
+    // Handle "C:\foo"
+    if (AreAbsoluteWindowsPathsEqual(source_root,
+                                     path.substr(0, source_root.size())))
+      after_common_index = source_root.size();
+    else
+      return false;
+  } else if (path[0] == '/' && source_root.size() <= path.size() - 1 &&
+             DoesBeginWindowsDriveLetter(path.substr(1))) {
+    // Handle "/C:/foo"
+    if (AreAbsoluteWindowsPathsEqual(source_root,
+                                     path.substr(1, source_root.size())))
+      after_common_index = source_root.size() + 1;
+    else
+      return false;
+  } else {
+    return false;
+  }
+
+  // If we get here, there's a match and after_common_index identifies the
+  // part after it.
+
+  // The base may or may not have a trailing slash, so skip all slashes from
+  // the path after our prefix match.
+  size_t first_after_slash = after_common_index;
+  while (first_after_slash < path.size() &&
+         (path[first_after_slash] == '/' || path[first_after_slash] == '\\'))
+    first_after_slash++;
+
+  dest->assign("//");  // Result is source root relative.
+  dest->append(&path.data()[first_after_slash],
+               path.size() - first_after_slash);
+  return true;
+
 #else
+
   // On non-Windows this is easy. Since we know both are absolute, just do a
   // prefix check.
   if (path.substr(0, source_root.size()) == source_root) {
-    dest->assign("//");  // Result is source root relative.
-
     // The base may or may not have a trailing slash, so skip all slashes from
     // the path after our prefix match.
     size_t first_after_slash = source_root.size();
     while (first_after_slash < path.size() && path[first_after_slash] == '/')
       first_after_slash++;
 
+    dest->assign("//");  // Result is source root relative.
     dest->append(&path.data()[first_after_slash],
                  path.size() - first_after_slash);
     return true;
   }
-#endif
   return false;
+#endif
 }
 
 std::string InvertDir(const SourceDir& path) {
diff --git a/tools/gn/filesystem_utils.h b/tools/gn/filesystem_utils.h
index 4ab4ec5..90e9d50 100644
--- a/tools/gn/filesystem_utils.h
+++ b/tools/gn/filesystem_utils.h
@@ -111,6 +111,8 @@
 // The source_root should be a base::FilePath converted to UTF-8. On Windows,
 // it should begin with a "C:/" rather than being our SourceFile's style
 // ("/C:/"). The source root can end with a slash or not.
+//
+// Note that this does not attempt to normalize slashes in the output.
 bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& source_root,
                                         const base::StringPiece& path,
                                         std::string* dest);
diff --git a/tools/gn/filesystem_utils_unittest.cc b/tools/gn/filesystem_utils_unittest.cc
index 27e65e2..6c80cdb 100644
--- a/tools/gn/filesystem_utils_unittest.cc
+++ b/tools/gn/filesystem_utils_unittest.cc
@@ -93,6 +93,23 @@
 TEST(FilesystemUtils, MakeAbsolutePathRelativeIfPossible) {
   std::string dest;
 
+#if defined(OS_WIN)
+  EXPECT_TRUE(MakeAbsolutePathRelativeIfPossible("C:\\base", "C:\\base\\foo",
+                                                 &dest));
+  EXPECT_EQ("//foo", dest);
+  EXPECT_TRUE(MakeAbsolutePathRelativeIfPossible("C:\\base", "/C:/base/foo",
+                                                 &dest));
+  EXPECT_EQ("//foo", dest);
+  EXPECT_TRUE(MakeAbsolutePathRelativeIfPossible("c:\\base", "C:\\base\\foo\\",
+                                                 &dest));
+  EXPECT_EQ("//foo\\", dest);
+
+  EXPECT_FALSE(MakeAbsolutePathRelativeIfPossible("C:\\base", "C:\\ba", &dest));
+  EXPECT_FALSE(MakeAbsolutePathRelativeIfPossible("C:\\base",
+                                                  "C:\\/notbase/foo",
+                                                  &dest));
+#else
+
   EXPECT_TRUE(MakeAbsolutePathRelativeIfPossible("/base", "/base/foo/", &dest));
   EXPECT_EQ("//foo/", dest);
   EXPECT_TRUE(MakeAbsolutePathRelativeIfPossible("/base", "/base/foo", &dest));
@@ -104,9 +121,6 @@
   EXPECT_FALSE(MakeAbsolutePathRelativeIfPossible("/base", "/ba", &dest));
   EXPECT_FALSE(MakeAbsolutePathRelativeIfPossible("/base", "/notbase/foo",
                                                   &dest));
-
-#if defined(OS_WIN)
-  //EXPECT_TRUE(MakeAbsolutePathRelativeIfPossible("
 #endif
 }
 
diff --git a/tools/gn/function_exec_script.cc b/tools/gn/function_exec_script.cc
index 34c5e96..1d4c511 100644
--- a/tools/gn/function_exec_script.cc
+++ b/tools/gn/function_exec_script.cc
@@ -8,6 +8,7 @@
 #include "base/process/kill.h"
 #include "base/process/launch.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "tools/gn/err.h"
diff --git a/tools/gn/gn.gyp b/tools/gn/gn.gyp
index f86a520..52275f4 100644
--- a/tools/gn/gn.gyp
+++ b/tools/gn/gn.gyp
@@ -19,6 +19,7 @@
         'command_args.cc',
         'command_desc.cc',
         'command_gen.cc',
+        'command_gyp.cc',
         'command_help.cc',
         'command_refs.cc',
         'commands.cc',
@@ -169,6 +170,7 @@
         'parser_unittest.cc',
         'path_output_unittest.cc',
         'pattern_unittest.cc',
+        'scope_per_file_provider_unittest.cc',
         'source_dir_unittest.cc',
         'string_utils_unittest.cc',
         'target_generator_unittest.cc',
diff --git a/tools/gn/group_target_generator.cc b/tools/gn/group_target_generator.cc
index 6f6fe32..30238e7 100644
--- a/tools/gn/group_target_generator.cc
+++ b/tools/gn/group_target_generator.cc
@@ -18,4 +18,5 @@
   target_->set_output_type(Target::GROUP);
   // Groups only have the default types filled in by the target generator
   // base class.
+  FillExternal();
 }
diff --git a/tools/gn/ninja_copy_target_writer.cc b/tools/gn/ninja_copy_target_writer.cc
index 4d2ebe0..d4a65ab 100644
--- a/tools/gn/ninja_copy_target_writer.cc
+++ b/tools/gn/ninja_copy_target_writer.cc
@@ -50,7 +50,9 @@
   // Write out the rule for the target to copy all of them.
   out_ << std::endl << "build ";
   path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_));
-  out_ << ": stamp";
+  out_ << ": "
+       << helper_.GetRulePrefix(target_->settings()->toolchain())
+       << "stamp";
   for (size_t i = 0; i < dest_files.size(); i++) {
     out_ << " ";
     path_output_.WriteFile(out_, dest_files[i]);
diff --git a/tools/gn/ninja_group_target_writer.cc b/tools/gn/ninja_group_target_writer.cc
index f31bf1a..df95a82 100644
--- a/tools/gn/ninja_group_target_writer.cc
+++ b/tools/gn/ninja_group_target_writer.cc
@@ -20,7 +20,9 @@
   // the deps in the group.
   out_ << std::endl << "build ";
   path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_));
-  out_ << ": stamp";
+  out_ << ": "
+       << helper_.GetRulePrefix(target_->settings()->toolchain())
+       << "stamp";
 
   const std::vector<const Target*>& deps = target_->deps();
   for (size_t i = 0; i < deps.size(); i++) {
diff --git a/tools/gn/ninja_helper.cc b/tools/gn/ninja_helper.cc
index 15c28af..0a4ebfd 100644
--- a/tools/gn/ninja_helper.cc
+++ b/tools/gn/ninja_helper.cc
@@ -114,11 +114,14 @@
 OutputFile NinjaHelper::GetTargetOutputFile(const Target* target) const {
   OutputFile ret;
 
-  // This is prepended to the output file name.
+  // This is prepended to the output file name. Some platforms get "lib"
+  // prepended to library names. but be careful not to make a duplicate (e.g.
+  // some targets like "libxml" already have the "lib" in the name).
   const char* prefix;
   if (!target->settings()->IsWin() &&
       (target->output_type() == Target::SHARED_LIBRARY ||
-       target->output_type() == Target::STATIC_LIBRARY))
+       target->output_type() == Target::STATIC_LIBRARY) &&
+      target->label().name().compare(0, 3, "lib") != 0)
     prefix = "lib";
   else
     prefix = "";
@@ -182,8 +185,12 @@
 }
 
 std::string NinjaHelper::GetRulePrefix(const Toolchain* toolchain) const {
-  if (toolchain->is_default())
-    return std::string();  // Default toolchain has no prefix.
+  // This code doesn't prefix the default toolchain commands. This is disabled
+  // so we can coexist with GYP's commands (which aren't prefixed). If we don't
+  // need to coexist with GYP anymore, we can uncomment this to make things a
+  // bit prettier.
+  //if (toolchain->is_default())
+  //  return std::string();  // Default toolchain has no prefix.
   return toolchain->label().name() + "_";
 }
 
diff --git a/tools/gn/ninja_script_target_writer.cc b/tools/gn/ninja_script_target_writer.cc
index e259fff..be53a77 100644
--- a/tools/gn/ninja_script_target_writer.cc
+++ b/tools/gn/ninja_script_target_writer.cc
@@ -223,7 +223,9 @@
     const std::vector<OutputFile>& output_files) {
   out_ << "build ";
   path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_));
-  out_ << ": stamp";
+  out_ << ": "
+       << helper_.GetRulePrefix(target_->settings()->toolchain())
+       << "stamp";
   for (size_t i = 0; i < output_files.size(); i++) {
     out_ << " ";
     path_output_.WriteFile(out_, output_files[i]);
diff --git a/tools/gn/ninja_target_writer.cc b/tools/gn/ninja_target_writer.cc
index 2852c9f..14a8bd7 100644
--- a/tools/gn/ninja_target_writer.cc
+++ b/tools/gn/ninja_target_writer.cc
@@ -30,6 +30,13 @@
 
 // static
 void NinjaTargetWriter::RunAndWriteFile(const Target* target) {
+  // External targets don't get written to disk, we assume they're managed by
+  // an external program. If we're not using an external generator, this is
+  // ignored.
+  if (target->settings()->build_settings()->using_external_generator() &&
+      target->external())
+    return;
+
   const Settings* settings = target->settings();
   NinjaHelper helper(settings->build_settings());
 
diff --git a/tools/gn/ninja_toolchain_writer.cc b/tools/gn/ninja_toolchain_writer.cc
index 95cb408..0a45e17 100644
--- a/tools/gn/ninja_toolchain_writer.cc
+++ b/tools/gn/ninja_toolchain_writer.cc
@@ -17,9 +17,11 @@
 NinjaToolchainWriter::NinjaToolchainWriter(
     const Settings* settings,
     const std::vector<const Target*>& targets,
+    const std::set<std::string>& skip_files,
     std::ostream& out)
     : settings_(settings),
       targets_(targets),
+      skip_files_(skip_files),
       out_(out),
       path_output_(settings_->build_settings()->build_dir(),
                    ESCAPE_NINJA, true),
@@ -37,7 +39,8 @@
 // static
 bool NinjaToolchainWriter::RunAndWriteFile(
     const Settings* settings,
-    const std::vector<const Target*>& targets) {
+    const std::vector<const Target*>& targets,
+    const std::set<std::string>& skip_files) {
   NinjaHelper helper(settings->build_settings());
   base::FilePath ninja_file(settings->build_settings()->GetFullPath(
       helper.GetNinjaFileForToolchain(settings).GetSourceFile(
@@ -50,7 +53,7 @@
   if (file.fail())
     return false;
 
-  NinjaToolchainWriter gen(settings, targets, file);
+  NinjaToolchainWriter gen(settings, targets, skip_files, file);
   gen.Run();
   return true;
 }
@@ -90,10 +93,17 @@
 void NinjaToolchainWriter::WriteSubninjas() {
   // Write subninja commands for each generated target.
   for (size_t i = 0; i < targets_.size(); i++) {
-    if (!targets_[i]->item_node()->should_generate())
+    if (!targets_[i]->item_node()->should_generate() ||
+        (targets_[i]->settings()->build_settings()->using_external_generator()
+         && targets_[i]->external()))
       continue;
+
+    OutputFile ninja_file = helper_.GetNinjaFileForTarget(targets_[i]);
+    if (skip_files_.find(ninja_file.value()) != skip_files_.end())
+      continue;
+
     out_ << "subninja ";
-    path_output_.WriteFile(out_, helper_.GetNinjaFileForTarget(targets_[i]));
+    path_output_.WriteFile(out_, ninja_file);
     out_ << std::endl;
   }
   out_ << std::endl;
diff --git a/tools/gn/ninja_toolchain_writer.h b/tools/gn/ninja_toolchain_writer.h
index 71759ef..5d3daa5 100644
--- a/tools/gn/ninja_toolchain_writer.h
+++ b/tools/gn/ninja_toolchain_writer.h
@@ -6,6 +6,8 @@
 #define TOOLS_GN_NINJA_TOOLCHAIN_WRITER_H_
 
 #include <iosfwd>
+#include <set>
+#include <string>
 #include <vector>
 
 #include "tools/gn/ninja_helper.h"
@@ -18,13 +20,16 @@
 class NinjaToolchainWriter {
  public:
   // Takes the settings for the toolchain, as well as the list of all targets
-  // assicoated with the toolchain.
+  // assicoated with the toolchain. Ninja files exactly matching "skip_files"
+  // will not be included in the subninja list.
   static bool RunAndWriteFile(const Settings* settings,
-                              const std::vector<const Target*>& targets);
+                              const std::vector<const Target*>& targets,
+                              const std::set<std::string>& skip_files);
 
  private:
   NinjaToolchainWriter(const Settings* settings,
                        const std::vector<const Target*>& targets,
+                       const std::set<std::string>& skip_files,
                        std::ostream& out);
   ~NinjaToolchainWriter();
 
@@ -35,6 +40,7 @@
 
   const Settings* settings_;
   std::vector<const Target*> targets_;
+  const std::set<std::string>& skip_files_;
   std::ostream& out_;
   PathOutput path_output_;
 
diff --git a/tools/gn/ninja_writer.cc b/tools/gn/ninja_writer.cc
index 9eec865..1fe893e 100644
--- a/tools/gn/ninja_writer.cc
+++ b/tools/gn/ninja_writer.cc
@@ -8,7 +8,6 @@
 #include "tools/gn/ninja_build_writer.h"
 #include "tools/gn/ninja_toolchain_writer.h"
 
-
 NinjaWriter::NinjaWriter(const BuildSettings* build_settings)
     : build_settings_(build_settings) {
 }
@@ -19,10 +18,29 @@
 // static
 bool NinjaWriter::RunAndWriteFiles(const BuildSettings* build_settings) {
   NinjaWriter writer(build_settings);
-  return writer.WriteRootBuildfiles();
+
+  std::vector<const Settings*> all_settings;
+  std::vector<const Target*> default_targets;
+  if (!writer.WriteToolchains(std::set<std::string>(),
+                              &all_settings, &default_targets))
+    return false;
+  return writer.WriteRootBuildfiles(all_settings, default_targets);
 }
 
-bool NinjaWriter::WriteRootBuildfiles() {
+// static
+bool NinjaWriter::RunAndWriteToolchainFiles(
+    const BuildSettings* build_settings,
+    const std::set<std::string>& skip_files,
+    std::vector<const Settings*>* all_settings) {
+  NinjaWriter writer(build_settings);
+  std::vector<const Target*> default_targets;
+  return writer.WriteToolchains(skip_files, all_settings, &default_targets);
+}
+
+bool NinjaWriter::WriteToolchains(
+    const std::set<std::string>& skip_files,
+    std::vector<const Settings*>* all_settings,
+    std::vector<const Target*>* default_targets) {
   // Categorize all targets by toolchain.
   typedef std::map<Label, std::vector<const Target*> > CategorizedMap;
   CategorizedMap categorized;
@@ -45,8 +63,6 @@
 
   // Write out the toolchain buildfiles, and also accumulate the set of
   // all settings and find the list of targets in the default toolchain.
-  std::vector<const Settings*> all_settings;
-  const std::vector<const Target*>* default_targets = NULL;
   for (CategorizedMap::const_iterator i = categorized.begin();
        i != categorized.end(); ++i) {
     const Settings* settings;
@@ -57,19 +73,26 @@
           build_settings_->toolchain_manager().GetSettingsForToolchainLocked(
               LocationRange(), i->first, &ignored);
     }
-    if (i->first == default_label)
-      default_targets = &i->second;
-    all_settings.push_back(settings);
-    if (!NinjaToolchainWriter::RunAndWriteFile(settings, i->second)) {
+    all_settings->push_back(settings);
+    if (!NinjaToolchainWriter::RunAndWriteFile(settings, i->second,
+                                               skip_files)) {
       Err(Location(),
           "Couldn't open toolchain buildfile(s) for writing").PrintToStdout();
       return false;
     }
   }
 
+  *default_targets = categorized[
+      build_settings_->toolchain_manager().GetDefaultToolchainUnlocked()];
+  return true;
+}
+
+bool NinjaWriter::WriteRootBuildfiles(
+    const std::vector<const Settings*>& all_settings,
+    const std::vector<const Target*>& default_targets) {
   // Write the root buildfile.
   if (!NinjaBuildWriter::RunAndWriteFile(build_settings_, all_settings,
-                                         *default_targets)) {
+                                         default_targets)) {
     Err(Location(),
         "Couldn't open toolchain buildfile(s) for writing").PrintToStdout();
     return false;
diff --git a/tools/gn/ninja_writer.h b/tools/gn/ninja_writer.h
index a4050a8..c2efc92 100644
--- a/tools/gn/ninja_writer.h
+++ b/tools/gn/ninja_writer.h
@@ -5,20 +5,42 @@
 #ifndef TOOLS_GN_NINJA_WRITER_H_
 #define TOOLS_GN_NINJA_WRITER_H_
 
+#include <set>
+#include <string>
+#include <vector>
+
 #include "base/basictypes.h"
 
 class BuildSettings;
+class Settings;
+class Target;
 
 class NinjaWriter {
  public:
   // On failure will print an error and will return false.
   static bool RunAndWriteFiles(const BuildSettings* build_settings);
 
+  // Writes only the toolchain.ninja files, skipping the root buildfile. The
+  // settings for the files written will be added to the vector.
+  //
+  // The skip files will avoid writing "subninja" rules when we're doing a
+  // side-by-side GYP build. .ninja files exactly matching the ones in the set
+  // will be ignored.
+  static bool RunAndWriteToolchainFiles(
+      const BuildSettings* build_settings,
+      const std::set<std::string>& skip_files,
+      std::vector<const Settings*>* all_settings);
+
  private:
   NinjaWriter(const BuildSettings* build_settings);
   ~NinjaWriter();
 
-  bool WriteRootBuildfiles();
+  bool WriteToolchains(
+      const std::set<std::string>& skip_files,
+      std::vector<const Settings*>* all_settings,
+      std::vector<const Target*>* default_targets);
+  bool WriteRootBuildfiles(const std::vector<const Settings*>& all_settings,
+                           const std::vector<const Target*>& default_targets);
 
   const BuildSettings* build_settings_;
 
diff --git a/tools/gn/scope_per_file_provider.cc b/tools/gn/scope_per_file_provider.cc
index 6e61dad..28f7e79 100644
--- a/tools/gn/scope_per_file_provider.cc
+++ b/tools/gn/scope_per_file_provider.cc
@@ -11,10 +11,8 @@
 #include "tools/gn/value.h"
 #include "tools/gn/variables.h"
 
-ScopePerFileProvider::ScopePerFileProvider(Scope* scope,
-                                           const SourceFile& source_file)
-    : ProgrammaticProvider(scope),
-      source_file_(source_file) {
+ScopePerFileProvider::ScopePerFileProvider(Scope* scope)
+    : ProgrammaticProvider(scope) {
 }
 
 ScopePerFileProvider::~ScopePerFileProvider() {
@@ -41,6 +39,10 @@
     return GetRelativeTargetOutputDir();
   if (ident == variables::kRelativeTargetGenDir)
     return GetRelativeTargetGenDir();
+  if (ident == variables::kRootGenDir)
+    return GetRootGenDir();
+  if (ident == variables::kTargetGenDir)
+    return GetTargetGenDir();
   return NULL;
 }
 
@@ -111,7 +113,7 @@
   if (!relative_target_output_dir_) {
     relative_target_output_dir_.reset(new Value(NULL,
         GetRelativeRootWithNoLastSlash() +
-        GetRootOutputDirWithNoLastSlash(scope_->settings()) + "obj/" +
+        GetRootOutputDirWithNoLastSlash(scope_->settings()) + "/obj" +
         GetFileDirWithNoLastSlash()));
   }
   return relative_target_output_dir_.get();
@@ -127,6 +129,24 @@
   return relative_target_gen_dir_.get();
 }
 
+const Value* ScopePerFileProvider::GetRootGenDir() {
+  if (!root_gen_dir_) {
+    root_gen_dir_.reset(new Value(NULL,
+        "/" + GetRootGenDirWithNoLastSlash(scope_->settings())));
+  }
+  return root_gen_dir_.get();
+}
+
+const Value* ScopePerFileProvider::GetTargetGenDir() {
+  if (!target_gen_dir_) {
+    target_gen_dir_.reset(new Value(NULL,
+        "/" +
+        GetRootGenDirWithNoLastSlash(scope_->settings()) +
+        GetFileDirWithNoLastSlash()));
+  }
+  return target_gen_dir_.get();
+}
+
 // static
 std::string ScopePerFileProvider::GetRootOutputDirWithNoLastSlash(
     const Settings* settings) {
diff --git a/tools/gn/scope_per_file_provider.h b/tools/gn/scope_per_file_provider.h
index 27443b4..9e96d7b 100644
--- a/tools/gn/scope_per_file_provider.h
+++ b/tools/gn/scope_per_file_provider.h
@@ -14,7 +14,7 @@
 // variable support.
 class ScopePerFileProvider : public Scope::ProgrammaticProvider {
  public:
-  ScopePerFileProvider(Scope* scope, const SourceFile& source_file);
+  ScopePerFileProvider(Scope* scope);
   virtual ~ScopePerFileProvider();
 
   // ProgrammaticProvider implementation.
@@ -31,6 +31,8 @@
   const Value* GetRelativeSourceRootDir();
   const Value* GetRelativeTargetOutputDir();
   const Value* GetRelativeTargetGenDir();
+  const Value* GetRootGenDir();
+  const Value* GetTargetGenDir();
 
   static std::string GetRootOutputDirWithNoLastSlash(const Settings* settings);
   static std::string GetRootGenDirWithNoLastSlash(const Settings* settings);
@@ -42,8 +44,6 @@
   // result would be empty, "." is returned to indicate the current dir.
   static std::string InvertDirWithNoLastSlash(const SourceDir& dir);
 
-  SourceFile source_file_;
-
   // All values are lazily created.
   scoped_ptr<Value> current_toolchain_;
   scoped_ptr<Value> default_toolchain_;
@@ -54,6 +54,8 @@
   scoped_ptr<Value> relative_source_root_dir_;
   scoped_ptr<Value> relative_target_output_dir_;
   scoped_ptr<Value> relative_target_gen_dir_;
+  scoped_ptr<Value> root_gen_dir_;
+  scoped_ptr<Value> target_gen_dir_;
 
   DISALLOW_COPY_AND_ASSIGN(ScopePerFileProvider);
 };
diff --git a/tools/gn/scope_per_file_provider_unittest.cc b/tools/gn/scope_per_file_provider_unittest.cc
new file mode 100644
index 0000000..7e195ce
--- /dev/null
+++ b/tools/gn/scope_per_file_provider_unittest.cc
@@ -0,0 +1,50 @@
+// Copyright (c) 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 "testing/gtest/include/gtest/gtest.h"
+#include "tools/gn/build_settings.h"
+#include "tools/gn/scope_per_file_provider.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/toolchain.h"
+#include "tools/gn/variables.h"
+
+TEST(ScopePerFileProvider, Expected) {
+  Err err;
+
+  BuildSettings build_settings;
+  build_settings.toolchain_manager().SetDefaultToolchainUnlocked(
+      Label(SourceDir("//toolchain/"), "default", SourceDir(), ""),
+      LocationRange(), &err);
+  EXPECT_FALSE(err.has_error());
+
+  build_settings.SetBuildDir(SourceDir("//out/Debug/"));
+
+  Toolchain toolchain(Label(SourceDir("//toolchain/"), "tc", SourceDir(), ""));
+  Settings settings(&build_settings, &toolchain, std::string());
+  Scope scope(&settings);
+  scope.set_source_dir(SourceDir("//source/"));
+
+  ScopePerFileProvider provider(&scope);
+
+  EXPECT_EQ("//toolchain:tc", provider.GetProgrammaticValue(
+      variables::kCurrentToolchain)->string_value());
+  EXPECT_EQ("//toolchain:default", provider.GetProgrammaticValue(
+      variables::kDefaultToolchain)->string_value());
+  EXPECT_EQ("../..",provider.GetProgrammaticValue(
+      variables::kRelativeBuildToSourceRootDir)->string_value());
+  EXPECT_EQ("../out/Debug", provider.GetProgrammaticValue(
+      variables::kRelativeRootOutputDir)->string_value());
+  EXPECT_EQ("../out/Debug/gen", provider.GetProgrammaticValue(
+      variables::kRelativeRootGenDir)->string_value());
+  EXPECT_EQ("..", provider.GetProgrammaticValue(
+      variables::kRelativeSourceRootDir)->string_value());
+  EXPECT_EQ("../out/Debug/obj/source", provider.GetProgrammaticValue(
+      variables::kRelativeTargetOutputDir)->string_value());
+  EXPECT_EQ("../out/Debug/gen/source", provider.GetProgrammaticValue(
+      variables::kRelativeTargetGenDir)->string_value());
+  EXPECT_EQ("//out/Debug/gen",provider.GetProgrammaticValue(
+      variables::kRootGenDir)->string_value());
+  EXPECT_EQ("//out/Debug/gen/source",provider.GetProgrammaticValue(
+      variables::kTargetGenDir)->string_value());
+}
diff --git a/tools/gn/script_target_generator.cc b/tools/gn/script_target_generator.cc
index 8df05b6..1725b7f 100644
--- a/tools/gn/script_target_generator.cc
+++ b/tools/gn/script_target_generator.cc
@@ -23,6 +23,7 @@
 void ScriptTargetGenerator::DoRun() {
   target_->set_output_type(Target::CUSTOM);
 
+  FillExternal();
   FillSources();
   FillScript();
   FillScriptArgs();
diff --git a/tools/gn/secondary/BUILD.gn b/tools/gn/secondary/BUILD.gn
index 0ed32b1..25efbf0 100644
--- a/tools/gn/secondary/BUILD.gn
+++ b/tools/gn/secondary/BUILD.gn
@@ -5,13 +5,14 @@
 group("root") {
   deps = [
     "//base(//build/toolchain/nacl:x86_newlib)",
+    "//chrome",
     "//crypto",
     "//ipc",
     "//net",
     "//net/third_party/nss/ssl:crssl",
     "//sdch",
     "//third_party/icu:icudata",
-    "//third_party/zlib",
+    "//third_party/zlib:chrome_zlib",
     "//tools/gn",
     "//url:url_lib",
   ]
diff --git a/tools/gn/secondary/base/BUILD.gn b/tools/gn/secondary/base/BUILD.gn
index 0287b13..3ebd0d1 100644
--- a/tools/gn/secondary/base/BUILD.gn
+++ b/tools/gn/secondary/base/BUILD.gn
@@ -9,6 +9,7 @@
 }
 
 component("base") {
+  external = true
   sources = [
     "../build/build_config.h",
     "third_party/dmg_fp/dmg_fp.h",
@@ -876,3 +877,147 @@
   #},
 }
 
+# TODO(brettw) move to base/test.
+static_library("test_support_base") {
+  external = true
+  sources = [
+    "test/expectations/expectation.cc",
+    "test/expectations/expectation.h",
+    "test/expectations/parser.cc",
+    "test/expectations/parser.h",
+    "test/gtest_xml_util.cc",
+    "test/gtest_xml_util.h",
+    "test/mock_chrome_application_mac.h",
+    "test/mock_chrome_application_mac.mm",
+    "test/mock_devices_changed_observer.cc",
+    "test/mock_devices_changed_observer.h",
+    "test/mock_time_provider.cc",
+    "test/mock_time_provider.h",
+    "test/multiprocess_test.cc",
+    "test/multiprocess_test.h",
+    "test/multiprocess_test_android.cc",
+    "test/null_task_runner.cc",
+    "test/null_task_runner.h",
+    "test/perf_test_suite.cc",
+    "test/perf_test_suite.h",
+    "test/perftimer.cc",
+    "test/scoped_locale.cc",
+    "test/scoped_locale.h",
+    "test/scoped_path_override.cc",
+    "test/scoped_path_override.h",
+    "test/sequenced_task_runner_test_template.cc",
+    "test/sequenced_task_runner_test_template.h",
+    "test/sequenced_worker_pool_owner.cc",
+    "test/sequenced_worker_pool_owner.h",
+    "test/simple_test_clock.cc",
+    "test/simple_test_clock.h",
+    "test/simple_test_tick_clock.cc",
+    "test/simple_test_tick_clock.h",
+    "test/task_runner_test_template.cc",
+    "test/task_runner_test_template.h",
+    "test/test_file_util.h",
+    "test/test_file_util_linux.cc",
+    "test/test_file_util_mac.cc",
+    "test/test_file_util_posix.cc",
+    "test/test_file_util_win.cc",
+    "test/test_launcher.cc",
+    "test/test_launcher.h",
+    "test/test_listener_ios.h",
+    "test/test_listener_ios.mm",
+    "test/test_pending_task.cc",
+    "test/test_pending_task.h",
+    "test/test_process_killer_win.cc",
+    "test/test_process_killer_win.h",
+    "test/test_reg_util_win.cc",
+    "test/test_reg_util_win.h",
+    "test/test_shortcut_win.cc",
+    "test/test_shortcut_win.h",
+    "test/test_simple_task_runner.cc",
+    "test/test_simple_task_runner.h",
+    "test/test_suite.cc",
+    "test/test_suite.h",
+    "test/test_support_android.cc",
+    "test/test_support_android.h",
+    "test/test_support_ios.h",
+    "test/test_support_ios.mm",
+    "test/test_switches.cc",
+    "test/test_switches.h",
+    "test/test_timeouts.cc",
+    "test/test_timeouts.h",
+    "test/thread_test_helper.cc",
+    "test/thread_test_helper.h",
+    "test/trace_event_analyzer.cc",
+    "test/trace_event_analyzer.h",
+    "test/unit_test_launcher.cc",
+    "test/unit_test_launcher.h",
+    "test/unit_test_launcher_ios.cc",
+    "test/values_test_util.cc",
+    "test/values_test_util.h",
+  ]
+  deps = [
+    "//base",
+    "//base:base_static",
+    "//base:base_i18n",
+    "//testing:gmock",
+    "//testing:gtest",
+    "//third_party/libxml:libxml2",
+  ]
+
+  if (!is_posix) {
+    sources -= [
+      "scoped_locale.cc",
+      "scoped_locale.h",
+    ]
+  }
+  if (is_ios) {
+    # Pull in specific Mac files for iOS (which have been filtered out
+    # by file name rules).
+    set_sources_assignment_filter([])
+    sources += "test_file_util_mac.cc"
+  }
+  #if (!is_bsd) {
+  #  sources -= "test/test_file_util_linux.cc"
+  #}
+  #if (use_gtk) {
+  #  deps += "/build/linux/system:gtk"
+  #}
+  #export_dependent_settings [
+  #  'base',
+  #]
+}
+
+# TODO(brettw) move to base/test.
+config("perf_test_config") {
+  defines = [ "PERF_TEST" ]
+}
+
+# TODO(brettw) move to base/test.
+static_library("test_support_perf") {
+  external = true
+  sources = [
+    "test/perftimer.cc",
+    "test/run_all_perftests.cc",
+  ]
+  deps = [
+    "//base",
+    "//testing:gtest",
+  ]
+
+  direct_dependent_configs = [ ":perf_test_config" ]
+
+  #if (toolkit_uses_gtk) {
+  #  deps += "/build/linux/system:gtk",
+  #}
+}
+
+# TODO(brettw) move to base/test.
+static_library("run_all_unittests") {
+  external = true
+  sources = [
+    "test/run_all_unittests.cc",
+  ]
+  deps = [
+    ":test_support_base",
+  ]
+}
+
diff --git a/tools/gn/secondary/base/allocator/BUILD.gn b/tools/gn/secondary/base/allocator/BUILD.gn
index 6766911..64d052d 100644
--- a/tools/gn/secondary/base/allocator/BUILD.gn
+++ b/tools/gn/secondary/base/allocator/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 static_library("allocator_extension_thunks") {
+  external = true
   sources = [
     "allocator_extension_thunks.cc",
     "allocator_extension_thunks.h",
diff --git a/tools/gn/secondary/base/test/BUILD.gn b/tools/gn/secondary/base/test/BUILD.gn
deleted file mode 100644
index 0e097ed..0000000
--- a/tools/gn/secondary/base/test/BUILD.gn
+++ /dev/null
@@ -1,135 +0,0 @@
-# Copyright (c) 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.
-
-static_library("test_support_base") {
-  sources = [
-    # TODO(brettw) move this file to this directory.
-    "../perftimer.cc",
-    "expectations/expectation.cc",
-    "expectations/expectation.h",
-    "expectations/parser.cc",
-    "expectations/parser.h",
-    "mock_chrome_application_mac.h",
-    "mock_chrome_application_mac.mm",
-    "mock_devices_changed_observer.cc",
-    "mock_devices_changed_observer.h",
-    "mock_time_provider.cc",
-    "mock_time_provider.h",
-    "multiprocess_test.cc",
-    "multiprocess_test.h",
-    "multiprocess_test_android.cc",
-    "null_task_runner.cc",
-    "null_task_runner.h",
-    "perf_test_suite.cc",
-    "perf_test_suite.h",
-    "scoped_locale.cc",
-    "scoped_locale.h",
-    "scoped_path_override.cc",
-    "scoped_path_override.h",
-    "sequenced_task_runner_test_template.cc",
-    "sequenced_task_runner_test_template.h",
-    "sequenced_worker_pool_owner.cc",
-    "sequenced_worker_pool_owner.h",
-    "simple_test_clock.cc",
-    "simple_test_clock.h",
-    "simple_test_tick_clock.cc",
-    "simple_test_tick_clock.h",
-    "task_runner_test_template.cc",
-    "task_runner_test_template.h",
-    "test_file_util.h",
-    "test_file_util_linux.cc",
-    "test_file_util_mac.cc",
-    "test_file_util_posix.cc",
-    "test_file_util_win.cc",
-    "test_listener_ios.h",
-    "test_listener_ios.mm",
-    "test_pending_task.cc",
-    "test_pending_task.h",
-    "test_process_killer_win.cc",
-    "test_process_killer_win.h",
-    "test_reg_util_win.cc",
-    "test_reg_util_win.h",
-    "test_shortcut_win.cc",
-    "test_shortcut_win.h",
-    "test_simple_task_runner.cc",
-    "test_simple_task_runner.h",
-    "test_suite.cc",
-    "test_suite.h",
-    "test_support_android.cc",
-    "test_support_android.h",
-    "test_support_ios.h",
-    "test_support_ios.mm",
-    "test_switches.cc",
-    "test_switches.h",
-    "test_timeouts.cc",
-    "test_timeouts.h",
-    "thread_test_helper.cc",
-    "thread_test_helper.h",
-    "trace_event_analyzer.cc",
-    "trace_event_analyzer.h",
-    "values_test_util.cc",
-    "values_test_util.h",
-  ]
-  deps = [
-    "//base",
-    "//base:base_static",
-    "//base:base_i18n",
-    "//testing/gmock",
-    "//testing/gtest",
-  ]
-
-  if (!is_posix) {
-    sources -= [
-      "scoped_locale.cc",
-      "scoped_locale.h",
-    ]
-  }
-  if (is_ios) {
-    # Pull in specific Mac files for iOS (which have been filtered out
-    # by file name rules).
-    set_sources_assignment_filter([])
-    sources += "test_file_util_mac.cc"
-  }
-  #if (!is_bsd) {
-  #  sources -= "test/test_file_util_linux.cc"
-  #}
-  #if (use_gtk) {
-  #  deps += "/build/linux/system:gtk"
-  #}
-  #export_dependent_settings [
-  #  'base',
-  #]
-}
-
-config("perf_test_config") {
-  defines = [ "PERF_TEST" ]
-}
-
-static_library("test_support_perf") {
-  sources = [
-    # TODO(brettw) move this file to this directory.
-    "../perftimer.cc",
-    "run_all_perftests.cc",
-  ]
-  deps = [
-    "//base",
-    "//testing/gtest",
-  ]
-
-  direct_dependent_configs = [ ":perf_test_config" ]
-
-  #if (toolkit_uses_gtk) {
-  #  deps += "/build/linux/system:gtk",
-  #}
-}
-
-static_library("run_all_unittests") {
-  sources = [
-    "run_all_unittests.cc",
-  ]
-  deps = [
-    ":test_support_base",
-  ]
-}
-
diff --git a/tools/gn/secondary/base/third_party/dynamic_annotations/BUILD.gn b/tools/gn/secondary/base/third_party/dynamic_annotations/BUILD.gn
index bf050bf..02beae0 100644
--- a/tools/gn/secondary/base/third_party/dynamic_annotations/BUILD.gn
+++ b/tools/gn/secondary/base/third_party/dynamic_annotations/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 static_library("dynamic_annotations") {
+  external = true
   sources = [
     "dynamic_annotations.c",
     "dynamic_annotations.h",
diff --git a/tools/gn/secondary/base/third_party/nspr/BUILD.gn b/tools/gn/secondary/base/third_party/nspr/BUILD.gn
index dafd9f1..226f3d8 100644
--- a/tools/gn/secondary/base/third_party/nspr/BUILD.gn
+++ b/tools/gn/secondary/base/third_party/nspr/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 static_library("nspr") {
+  external = true
   sources = [
     "prcpucfg.h",
     "prcpucfg_freebsd.h",
diff --git a/tools/gn/secondary/base/third_party/symbolize/BUILD.gn b/tools/gn/secondary/base/third_party/symbolize/BUILD.gn
index c13c0d8..f4af9bd 100644
--- a/tools/gn/secondary/base/third_party/symbolize/BUILD.gn
+++ b/tools/gn/secondary/base/third_party/symbolize/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 static_library("symbolize") {
+  external = true
   sources = [
     "config.h",
     "demangle.cc",
diff --git a/tools/gn/secondary/base/third_party/xdg_mime/BUILD.gn b/tools/gn/secondary/base/third_party/xdg_mime/BUILD.gn
index 9e51bc1..32d9ac4 100644
--- a/tools/gn/secondary/base/third_party/xdg_mime/BUILD.gn
+++ b/tools/gn/secondary/base/third_party/xdg_mime/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 static_library("xdg_mime") {
+  external = true
   sources = [
     "xdgmime.c",
     "xdgmime.h",
diff --git a/tools/gn/secondary/base/third_party/xdg_user_dirs/BUILD.gn b/tools/gn/secondary/base/third_party/xdg_user_dirs/BUILD.gn
index f2145b7..eda4e02 100644
--- a/tools/gn/secondary/base/third_party/xdg_user_dirs/BUILD.gn
+++ b/tools/gn/secondary/base/third_party/xdg_user_dirs/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 static_library("xdg_user_dirs") {
+  external = true
   sources = [
     "xdg_user_dir_lookup.cc",
     "xdg_user_dir_lookup.h",
diff --git a/tools/gn/secondary/build/config/linux/BUILD.gn b/tools/gn/secondary/build/config/linux/BUILD.gn
index dec72a7..d6a8499 100644
--- a/tools/gn/secondary/build/config/linux/BUILD.gn
+++ b/tools/gn/secondary/build/config/linux/BUILD.gn
@@ -30,6 +30,8 @@
   # misconfigured systems.
   gtk_packages = "gmodule-2.0 gtk+-2.0 gthread-2.0"
 
+  defines = [ "TOOLKIT_GTK" ]
+
   cflags = exec_script(pkg_script, [ "--cflags", gtk_packages ], "list lines")
   ldflags = exec_script(pkg_script, [ "--libs", gtk_packages ], "list lines")
 }
@@ -37,6 +39,8 @@
 config("x11") {
   x11_packages = "x11 xi"
 
+  defines = [ "USE_X11" ]
+
   cflags = exec_script(pkg_script, [ "--cflags", x11_packages ], "list lines")
   ldflags = exec_script(pkg_script, [ "--libs", x11_packages ], "list lines")
 }
diff --git a/tools/gn/secondary/crypto/BUILD.gn b/tools/gn/secondary/crypto/BUILD.gn
index ff6a0c3..02676bd 100644
--- a/tools/gn/secondary/crypto/BUILD.gn
+++ b/tools/gn/secondary/crypto/BUILD.gn
@@ -5,6 +5,7 @@
 import("ssl/flags.gni")
 
 component("crypto") {
+  external = true
   sources = [
     "apple_keychain.h",
     "apple_keychain_ios.mm",
@@ -166,6 +167,7 @@
 # A minimal crypto subset for core features that small standalone targets can
 # use to reduce code size.
 static_library("crypto_minimal") {
+  external = true
   sources = [
     "hmac.cc",
     "hmac.h",
@@ -187,6 +189,7 @@
 }
 
 test("crypto_unittests") {
+  external = true
   sources = [
     # Infrastructure files.
     "run_all_unittests.cc",
@@ -224,9 +227,9 @@
   deps = [
     ":crypto",
     "//base",
-    "//base/test:test_support_base",
-    "//testing/gmock",
-    "//testing/gtest",
+    "//base:test_support_base",
+    "//testing:gmock",
+    "//testing:gtest",
   ]
 
   if (is_mac) {
diff --git a/tools/gn/secondary/ipc/BUILD.gn b/tools/gn/secondary/ipc/BUILD.gn
index a0b5f2b..d65d5c6 100644
--- a/tools/gn/secondary/ipc/BUILD.gn
+++ b/tools/gn/secondary/ipc/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 component("ipc") {
+  external = true
   sources = [
     "file_descriptor_set_posix.cc",
     "file_descriptor_set_posix.h",
@@ -79,6 +80,7 @@
 }
 
 test("ipc_tests") {
+  external = true
   sources = [
     "file_descriptor_set_posix_unittest.cc",
     "ipc_channel_posix_unittest.cc",
@@ -116,13 +118,14 @@
     ":test_support_ipc",
     "//base",
     "//base:base_i18n",
-    "//base/test:run_all_unittests",
-    "//base/test:test_support_base",
-    "//testing/gtest",
+    "//base:run_all_unittests",
+    "//base:test_support_base",
+    "//testing:gtest",
   ]
 }
 
 test("ipc_perftests") {
+  external = true
   sources = [
     "ipc_perftests.cc",
     "ipc_test_base.cc",
@@ -146,13 +149,14 @@
     ":test_support_ipc",
     "//base",
     "//base:base_i18n",
-    "//base/test:test_support_base",
-    "//base/test:test_support_perf",
-    "//testing/gtest",
+    "//base:test_support_base",
+    "//base:test_support_perf",
+    "//testing:gtest",
   ]
 }
 
 static_library("test_support_ipc") {
+  external = true
   sources = [
     "ipc_multiprocess_test.cc",
     "ipc_multiprocess_test.h",
@@ -162,7 +166,7 @@
   deps = [
     ":ipc",
     "//base",
-    "//testing/gtest",
+    "//testing:gtest",
   ]
 }
 
diff --git a/tools/gn/secondary/net/BUILD.gn b/tools/gn/secondary/net/BUILD.gn
index cf70629..a0941d9 100644
--- a/tools/gn/secondary/net/BUILD.gn
+++ b/tools/gn/secondary/net/BUILD.gn
@@ -2,24 +2,1203 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//crypto/ssl/flags.gni")
 import("//tools/grit/grit_rule.gni")
 
 component("net") {
+  external = true
+  sources = [
+    "android/cert_verify_result_android.h",
+    "android/cert_verify_result_android_list.h",
+    "android/gurl_utils.cc",
+    "android/gurl_utils.h",
+    "android/keystore.cc",
+    "android/keystore.h",
+    "android/keystore_openssl.cc",
+    "android/keystore_openssl.h",
+    "android/net_jni_registrar.cc",
+    "android/net_jni_registrar.h",
+    "android/network_change_notifier_android.cc",
+    "android/network_change_notifier_android.h",
+    "android/network_change_notifier_delegate_android.cc",
+    "android/network_change_notifier_delegate_android.h",
+    "android/network_change_notifier_factory_android.cc",
+    "android/network_change_notifier_factory_android.h",
+    "android/network_library.cc",
+    "android/network_library.h",
+    "base/address_family.h",
+    "base/address_list.cc",
+    "base/address_list.h",
+    "base/address_tracker_linux.cc",
+    "base/address_tracker_linux.h",
+    "base/auth.cc",
+    "base/auth.h",
+    "base/backoff_entry.cc",
+    "base/backoff_entry.h",
+    "base/bandwidth_metrics.cc",
+    "base/bandwidth_metrics.h",
+    "base/big_endian.cc",
+    "base/big_endian.h",
+    "base/cache_type.h",
+    "base/completion_callback.h",
+    "base/connection_type_histograms.cc",
+    "base/connection_type_histograms.h",
+    "base/crypto_module.h",
+    "base/crypto_module_nss.cc",
+    "base/crypto_module_openssl.cc",
+    "base/data_url.cc",
+    "base/data_url.h",
+    "base/directory_lister.cc",
+    "base/directory_lister.h",
+    "base/dns_reloader.cc",
+    "base/dns_reloader.h",
+    "base/dns_util.cc",
+    "base/dns_util.h",
+    "base/escape.cc",
+    "base/escape.h",
+    "base/expiring_cache.h",
+    "base/file_stream.cc",
+    "base/file_stream.h",
+    "base/file_stream_context.cc",
+    "base/file_stream_context.h",
+    "base/file_stream_context_posix.cc",
+    "base/file_stream_context_win.cc",
+    "base/file_stream_metrics.cc",
+    "base/file_stream_metrics.h",
+    "base/file_stream_metrics_posix.cc",
+    "base/file_stream_metrics_win.cc",
+    "base/file_stream_net_log_parameters.cc",
+    "base/file_stream_net_log_parameters.h",
+    "base/file_stream_whence.h",
+    "base/filter.cc",
+    "base/filter.h",
+    "base/int128.cc",
+    "base/int128.h",
+    "base/gzip_filter.cc",
+    "base/gzip_filter.h",
+    "base/gzip_header.cc",
+    "base/gzip_header.h",
+    "base/hash_value.cc",
+    "base/hash_value.h",
+    "base/host_mapping_rules.cc",
+    "base/host_mapping_rules.h",
+    "base/host_port_pair.cc",
+    "base/host_port_pair.h",
+    "base/io_buffer.cc",
+    "base/io_buffer.h",
+    "base/iovec.h",
+    "base/ip_endpoint.cc",
+    "base/ip_endpoint.h",
+    "base/keygen_handler.cc",
+    "base/keygen_handler.h",
+    "base/keygen_handler_mac.cc",
+    "base/keygen_handler_nss.cc",
+    "base/keygen_handler_openssl.cc",
+    "base/keygen_handler_win.cc",
+    "base/linked_hash_map.h",
+    "base/load_flags.h",
+    "base/load_flags_list.h",
+    "base/load_states.h",
+    "base/load_states_list.h",
+    "base/load_timing_info.cc",
+    "base/load_timing_info.h",
+    "base/mime_sniffer.cc",
+    "base/mime_sniffer.h",
+    "base/mime_util.cc",
+    "base/mime_util.h",
+    "base/net_error_list.h",
+    "base/net_errors.cc",
+    "base/net_errors.h",
+    "base/net_errors_posix.cc",
+    "base/net_errors_win.cc",
+    "base/net_export.h",
+    "base/net_log.cc",
+    "base/net_log.h",
+    "base/net_log_logger.cc",
+    "base/net_log_logger.h",
+    "base/net_log_event_type_list.h",
+    "base/net_log_source_type_list.h",
+    "base/net_module.cc",
+    "base/net_module.h",
+    "base/net_util.cc",
+    "base/net_util.h",
+    "base/net_util_posix.cc",
+    "base/net_util_win.cc",
+    "base/network_change_notifier.cc",
+    "base/network_change_notifier.h",
+    "base/network_change_notifier_factory.h",
+    "base/network_change_notifier_linux.cc",
+    "base/network_change_notifier_linux.h",
+    "base/network_change_notifier_mac.cc",
+    "base/network_change_notifier_mac.h",
+    "base/network_change_notifier_win.cc",
+    "base/network_change_notifier_win.h",
+    "base/network_config_watcher_mac.cc",
+    "base/network_config_watcher_mac.h",
+    "base/network_delegate.cc",
+    "base/network_delegate.h",
+    "base/network_time_notifier.cc",
+    "base/network_time_notifier.h",
+    "base/nss_memio.c",
+    "base/nss_memio.h",
+    "base/openssl_private_key_store.h",
+    "base/openssl_private_key_store_android.cc",
+    "base/openssl_private_key_store_memory.cc",
+    "base/platform_mime_util.h",
+    # TODO(tc): gnome-vfs? xdgmime? /etc/mime.types?
+    "base/platform_mime_util_linux.cc",
+    "base/platform_mime_util_mac.mm",
+    "base/platform_mime_util_win.cc",
+    "base/prioritized_dispatcher.cc",
+    "base/prioritized_dispatcher.h",
+    "base/priority_queue.h",
+    "base/rand_callback.h",
+    "base/registry_controlled_domains/registry_controlled_domain.cc",
+    "base/registry_controlled_domains/registry_controlled_domain.h",
+    "base/request_priority.h",
+    "base/sdch_filter.cc",
+    "base/sdch_filter.h",
+    "base/sdch_manager.cc",
+    "base/sdch_manager.h",
+    "base/static_cookie_policy.cc",
+    "base/static_cookie_policy.h",
+    "base/sys_addrinfo.h",
+    "base/test_data_stream.cc",
+    "base/test_data_stream.h",
+    "base/upload_bytes_element_reader.cc",
+    "base/upload_bytes_element_reader.h",
+    "base/upload_data.cc",
+    "base/upload_data.h",
+    "base/upload_data_stream.cc",
+    "base/upload_data_stream.h",
+    "base/upload_element.cc",
+    "base/upload_element.h",
+    "base/upload_element_reader.cc",
+    "base/upload_element_reader.h",
+    "base/upload_file_element_reader.cc",
+    "base/upload_file_element_reader.h",
+    "base/upload_progress.h",
+    "base/url_util.cc",
+    "base/url_util.h",
+    "base/winsock_init.cc",
+    "base/winsock_init.h",
+    "base/winsock_util.cc",
+    "base/winsock_util.h",
+    "base/zap.cc",
+    "base/zap.h",
+    "cert/asn1_util.cc",
+    "cert/asn1_util.h",
+    "cert/cert_database.cc",
+    "cert/cert_database.h",
+    "cert/cert_database_android.cc",
+    "cert/cert_database_ios.cc",
+    "cert/cert_database_mac.cc",
+    "cert/cert_database_nss.cc",
+    "cert/cert_database_openssl.cc",
+    "cert/cert_database_win.cc",
+    "cert/cert_status_flags.cc",
+    "cert/cert_status_flags.h",
+    "cert/cert_trust_anchor_provider.h",
+    "cert/cert_verifier.cc",
+    "cert/cert_verifier.h",
+    "cert/cert_verify_proc.cc",
+    "cert/cert_verify_proc.h",
+    "cert/cert_verify_proc_android.cc",
+    "cert/cert_verify_proc_android.h",
+    "cert/cert_verify_proc_mac.cc",
+    "cert/cert_verify_proc_mac.h",
+    "cert/cert_verify_proc_nss.cc",
+    "cert/cert_verify_proc_nss.h",
+    "cert/cert_verify_proc_openssl.cc",
+    "cert/cert_verify_proc_openssl.h",
+    "cert/cert_verify_proc_win.cc",
+    "cert/cert_verify_proc_win.h",
+    "cert/cert_verify_result.cc",
+    "cert/cert_verify_result.h",
+    "cert/crl_set.cc",
+    "cert/crl_set.h",
+    "cert/ev_root_ca_metadata.cc",
+    "cert/ev_root_ca_metadata.h",
+    "cert/jwk_serializer_nss.cc",
+    "cert/jwk_serializer_openssl.cc",
+    "cert/jwk_serializer.h",
+    "cert/multi_threaded_cert_verifier.cc",
+    "cert/multi_threaded_cert_verifier.h",
+    "cert/nss_cert_database.cc",
+    "cert/nss_cert_database.h",
+    "cert/pem_tokenizer.cc",
+    "cert/pem_tokenizer.h",
+    "cert/single_request_cert_verifier.cc",
+    "cert/single_request_cert_verifier.h",
+    "cert/test_root_certs.cc",
+    "cert/test_root_certs.h",
+    "cert/test_root_certs_mac.cc",
+    "cert/test_root_certs_nss.cc",
+    "cert/test_root_certs_openssl.cc",
+    "cert/test_root_certs_android.cc",
+    "cert/test_root_certs_win.cc",
+    "cert/x509_cert_types.cc",
+    "cert/x509_cert_types.h",
+    "cert/x509_cert_types_mac.cc",
+    "cert/x509_cert_types_win.cc",
+    "cert/x509_certificate.cc",
+    "cert/x509_certificate.h",
+    "cert/x509_certificate_ios.cc",
+    "cert/x509_certificate_mac.cc",
+    "cert/x509_certificate_net_log_param.cc",
+    "cert/x509_certificate_net_log_param.h",
+    "cert/x509_certificate_nss.cc",
+    "cert/x509_certificate_openssl.cc",
+    "cert/x509_certificate_win.cc",
+    "cert/x509_util.h",
+    "cert/x509_util.cc",
+    "cert/x509_util_ios.cc",
+    "cert/x509_util_ios.h",
+    "cert/x509_util_mac.cc",
+    "cert/x509_util_mac.h",
+    "cert/x509_util_nss.cc",
+    "cert/x509_util_nss.h",
+    "cert/x509_util_openssl.cc",
+    "cert/x509_util_openssl.h",
+    "cookies/canonical_cookie.cc",
+    "cookies/canonical_cookie.h",
+    "cookies/cookie_constants.cc",
+    "cookies/cookie_constants.h",
+    "cookies/cookie_monster.cc",
+    "cookies/cookie_monster.h",
+    "cookies/cookie_options.h",
+    "cookies/cookie_store.cc",
+    "cookies/cookie_store.h",
+    "cookies/cookie_util.cc",
+    "cookies/cookie_util.h",
+    "cookies/parsed_cookie.cc",
+    "cookies/parsed_cookie.h",
+    "disk_cache/addr.cc",
+    "disk_cache/addr.h",
+    "disk_cache/backend_impl.cc",
+    "disk_cache/backend_impl.h",
+    "disk_cache/bitmap.cc",
+    "disk_cache/bitmap.h",
+    "disk_cache/block_files.cc",
+    "disk_cache/block_files.h",
+    "disk_cache/cache_creator.cc",
+    "disk_cache/cache_util.h",
+    "disk_cache/cache_util.cc",
+    "disk_cache/cache_util_posix.cc",
+    "disk_cache/cache_util_win.cc",
+    "disk_cache/disk_cache.h",
+    "disk_cache/disk_format.cc",
+    "disk_cache/disk_format.h",
+    "disk_cache/disk_format_base.h",
+    "disk_cache/entry_impl.cc",
+    "disk_cache/entry_impl.h",
+    "disk_cache/errors.h",
+    "disk_cache/eviction.cc",
+    "disk_cache/eviction.h",
+    "disk_cache/experiments.h",
+    "disk_cache/file.cc",
+    "disk_cache/file.h",
+    "disk_cache/file_block.h",
+    "disk_cache/file_lock.cc",
+    "disk_cache/file_lock.h",
+    "disk_cache/file_posix.cc",
+    "disk_cache/file_win.cc",
+    "disk_cache/histogram_macros.h",
+    "disk_cache/in_flight_backend_io.cc",
+    "disk_cache/in_flight_backend_io.h",
+    "disk_cache/in_flight_io.cc",
+    "disk_cache/in_flight_io.h",
+    "disk_cache/mapped_file.h",
+    "disk_cache/mapped_file_posix.cc",
+    "disk_cache/mapped_file_avoid_mmap_posix.cc",
+    "disk_cache/mapped_file_win.cc",
+    "disk_cache/mem_backend_impl.cc",
+    "disk_cache/mem_backend_impl.h",
+    "disk_cache/mem_entry_impl.cc",
+    "disk_cache/mem_entry_impl.h",
+    "disk_cache/mem_rankings.cc",
+    "disk_cache/mem_rankings.h",
+    "disk_cache/net_log_parameters.cc",
+    "disk_cache/net_log_parameters.h",
+    "disk_cache/rankings.cc",
+    "disk_cache/rankings.h",
+    "disk_cache/sparse_control.cc",
+    "disk_cache/sparse_control.h",
+    "disk_cache/stats.cc",
+    "disk_cache/stats.h",
+    "disk_cache/stats_histogram.cc",
+    "disk_cache/stats_histogram.h",
+    "disk_cache/storage_block-inl.h",
+    "disk_cache/storage_block.h",
+    "disk_cache/stress_support.h",
+    "disk_cache/trace.cc",
+    "disk_cache/trace.h",
+    "disk_cache/tracing_cache_backend.cc",
+    "disk_cache/tracing_cache_backend.h",
+    "disk_cache/simple/simple_backend_impl.cc",
+    "disk_cache/simple/simple_backend_impl.h",
+    "disk_cache/simple/simple_entry_format.cc",
+    "disk_cache/simple/simple_entry_format.h",
+    "disk_cache/simple/simple_entry_impl.cc",
+    "disk_cache/simple/simple_entry_impl.h",
+    "disk_cache/simple/simple_entry_operation.cc",
+    "disk_cache/simple/simple_entry_operation.h",
+    "disk_cache/simple/simple_index.cc",
+    "disk_cache/simple/simple_index.h",
+    "disk_cache/simple/simple_index_file.cc",
+    "disk_cache/simple/simple_index_file.h",
+    "disk_cache/simple/simple_index_file_posix.cc",
+    "disk_cache/simple/simple_index_file_win.cc",
+    "disk_cache/simple/simple_net_log_parameters.cc",
+    "disk_cache/simple/simple_net_log_parameters.h",
+    "disk_cache/simple/simple_synchronous_entry.cc",
+    "disk_cache/simple/simple_synchronous_entry.h",
+    "disk_cache/simple/simple_util.cc",
+    "disk_cache/simple/simple_util.h",
+    "disk_cache/flash/flash_entry_impl.cc",
+    "disk_cache/flash/flash_entry_impl.h",
+    "disk_cache/flash/format.h",
+    "disk_cache/flash/internal_entry.cc",
+    "disk_cache/flash/internal_entry.h",
+    "disk_cache/flash/log_store.cc",
+    "disk_cache/flash/log_store.h",
+    "disk_cache/flash/log_store_entry.cc",
+    "disk_cache/flash/log_store_entry.h",
+    "disk_cache/flash/segment.cc",
+    "disk_cache/flash/segment.h",
+    "disk_cache/flash/storage.cc",
+    "disk_cache/flash/storage.h",
+    "disk_cache/v3/disk_format_v3.h",
+    "dns/address_sorter.h",
+    "dns/address_sorter_posix.cc",
+    "dns/address_sorter_posix.h",
+    "dns/address_sorter_win.cc",
+    "dns/dns_client.cc",
+    "dns/dns_client.h",
+    "dns/dns_config_service.cc",
+    "dns/dns_config_service.h",
+    "dns/dns_config_service_posix.cc",
+    "dns/dns_config_service_posix.h",
+    "dns/dns_config_service_win.cc",
+    "dns/dns_config_service_win.h",
+    "dns/dns_hosts.cc",
+    "dns/dns_hosts.h",
+    "dns/dns_protocol.h",
+    "dns/dns_query.cc",
+    "dns/dns_query.h",
+    "dns/dns_response.cc",
+    "dns/dns_response.h",
+    "dns/dns_session.cc",
+    "dns/dns_session.h",
+    "dns/dns_socket_pool.cc",
+    "dns/dns_socket_pool.h",
+    "dns/dns_transaction.cc",
+    "dns/dns_transaction.h",
+    "dns/host_cache.cc",
+    "dns/host_cache.h",
+    "dns/host_resolver.cc",
+    "dns/host_resolver.h",
+    "dns/host_resolver_impl.cc",
+    "dns/host_resolver_impl.h",
+    "dns/host_resolver_proc.cc",
+    "dns/host_resolver_proc.h",
+    "dns/mapped_host_resolver.cc",
+    "dns/mapped_host_resolver.h",
+    "dns/mdns_cache.cc",
+    "dns/mdns_cache.h",
+    "dns/mdns_client.cc",
+    "dns/mdns_client.h",
+    "dns/mdns_client_impl.cc",
+    "dns/mdns_client_impl.h",
+    "dns/notify_watcher_mac.cc",
+    "dns/notify_watcher_mac.h",
+    "dns/record_parsed.cc",
+    "dns/record_parsed.h",
+    "dns/record_rdata.cc",
+    "dns/record_rdata.h",
+    "dns/serial_worker.cc",
+    "dns/serial_worker.h",
+    "dns/single_request_host_resolver.cc",
+    "dns/single_request_host_resolver.h",
+    "ftp/ftp_auth_cache.cc",
+    "ftp/ftp_auth_cache.h",
+    "ftp/ftp_ctrl_response_buffer.cc",
+    "ftp/ftp_ctrl_response_buffer.h",
+    "ftp/ftp_directory_listing_parser.cc",
+    "ftp/ftp_directory_listing_parser.h",
+    "ftp/ftp_directory_listing_parser_ls.cc",
+    "ftp/ftp_directory_listing_parser_ls.h",
+    "ftp/ftp_directory_listing_parser_netware.cc",
+    "ftp/ftp_directory_listing_parser_netware.h",
+    "ftp/ftp_directory_listing_parser_os2.cc",
+    "ftp/ftp_directory_listing_parser_os2.h",
+    "ftp/ftp_directory_listing_parser_vms.cc",
+    "ftp/ftp_directory_listing_parser_vms.h",
+    "ftp/ftp_directory_listing_parser_windows.cc",
+    "ftp/ftp_directory_listing_parser_windows.h",
+    "ftp/ftp_network_layer.cc",
+    "ftp/ftp_network_layer.h",
+    "ftp/ftp_network_session.cc",
+    "ftp/ftp_network_session.h",
+    "ftp/ftp_network_transaction.cc",
+    "ftp/ftp_network_transaction.h",
+    "ftp/ftp_request_info.h",
+    "ftp/ftp_response_info.cc",
+    "ftp/ftp_response_info.h",
+    "ftp/ftp_server_type_histograms.cc",
+    "ftp/ftp_server_type_histograms.h",
+    "ftp/ftp_transaction.h",
+    "ftp/ftp_transaction_factory.h",
+    "ftp/ftp_util.cc",
+    "ftp/ftp_util.h",
+    "http/des.cc",
+    "http/des.h",
+    "http/http_atom_list.h",
+    "http/http_auth.cc",
+    "http/http_auth.h",
+    "http/http_auth_cache.cc",
+    "http/http_auth_cache.h",
+    "http/http_auth_controller.cc",
+    "http/http_auth_controller.h",
+    "http/http_auth_filter.cc",
+    "http/http_auth_filter.h",
+    "http/http_auth_filter_win.h",
+    "http/http_auth_gssapi_posix.cc",
+    "http/http_auth_gssapi_posix.h",
+    "http/http_auth_handler.cc",
+    "http/http_auth_handler.h",
+    "http/http_auth_handler_basic.cc",
+    "http/http_auth_handler_basic.h",
+    "http/http_auth_handler_digest.cc",
+    "http/http_auth_handler_digest.h",
+    "http/http_auth_handler_factory.cc",
+    "http/http_auth_handler_factory.h",
+    "http/http_auth_handler_negotiate.cc",
+    "http/http_auth_handler_negotiate.h",
+    "http/http_auth_handler_ntlm.cc",
+    "http/http_auth_handler_ntlm.h",
+    "http/http_auth_handler_ntlm_portable.cc",
+    "http/http_auth_handler_ntlm_win.cc",
+    "http/http_auth_sspi_win.cc",
+    "http/http_auth_sspi_win.h",
+    "http/http_basic_stream.cc",
+    "http/http_basic_stream.h",
+    "http/http_byte_range.cc",
+    "http/http_byte_range.h",
+    "http/http_cache.cc",
+    "http/http_cache.h",
+    "http/http_cache_transaction.cc",
+    "http/http_cache_transaction.h",
+    "http/http_content_disposition.cc",
+    "http/http_content_disposition.h",
+    "http/http_chunked_decoder.cc",
+    "http/http_chunked_decoder.h",
+    "http/http_network_layer.cc",
+    "http/http_network_layer.h",
+    "http/http_network_session.cc",
+    "http/http_network_session.h",
+    "http/http_network_session_peer.cc",
+    "http/http_network_session_peer.h",
+    "http/http_network_transaction.cc",
+    "http/http_network_transaction.h",
+    "http/http_pipelined_connection.h",
+    "http/http_pipelined_connection_impl.cc",
+    "http/http_pipelined_connection_impl.h",
+    "http/http_pipelined_host.cc",
+    "http/http_pipelined_host.h",
+    "http/http_pipelined_host_capability.h",
+    "http/http_pipelined_host_forced.cc",
+    "http/http_pipelined_host_forced.h",
+    "http/http_pipelined_host_impl.cc",
+    "http/http_pipelined_host_impl.h",
+    "http/http_pipelined_host_pool.cc",
+    "http/http_pipelined_host_pool.h",
+    "http/http_pipelined_stream.cc",
+    "http/http_pipelined_stream.h",
+    "http/http_proxy_client_socket.cc",
+    "http/http_proxy_client_socket.h",
+    "http/http_proxy_client_socket_pool.cc",
+    "http/http_proxy_client_socket_pool.h",
+    "http/http_request_headers.cc",
+    "http/http_request_headers.h",
+    "http/http_request_info.cc",
+    "http/http_request_info.h",
+    "http/http_response_body_drainer.cc",
+    "http/http_response_body_drainer.h",
+    "http/http_response_headers.cc",
+    "http/http_response_headers.h",
+    "http/http_response_info.cc",
+    "http/http_response_info.h",
+    "http/http_security_headers.cc",
+    "http/http_security_headers.h",
+    "http/http_server_properties.cc",
+    "http/http_server_properties.h",
+    "http/http_server_properties_impl.cc",
+    "http/http_server_properties_impl.h",
+    "http/http_status_code.cc",
+    "http/http_status_code.h",
+    "http/http_stream.h",
+    "http/http_stream_base.h",
+    "http/http_stream_factory.cc",
+    "http/http_stream_factory.h",
+    "http/http_stream_factory_impl.cc",
+    "http/http_stream_factory_impl.h",
+    "http/http_stream_factory_impl_job.cc",
+    "http/http_stream_factory_impl_job.h",
+    "http/http_stream_factory_impl_request.cc",
+    "http/http_stream_factory_impl_request.h",
+    "http/http_stream_parser.cc",
+    "http/http_stream_parser.h",
+    "http/http_transaction.h",
+    "http/http_transaction_delegate.h",
+    "http/http_transaction_factory.h",
+    "http/http_util.cc",
+    "http/http_util.h",
+    "http/http_util_icu.cc",
+    "http/http_vary_data.cc",
+    "http/http_vary_data.h",
+    "http/http_version.h",
+    "http/md4.cc",
+    "http/md4.h",
+    "http/partial_data.cc",
+    "http/partial_data.h",
+    "http/proxy_client_socket.h",
+    "http/proxy_client_socket.cc",
+    "http/proxy_connect_redirect_http_stream.h",
+    "http/proxy_connect_redirect_http_stream.cc",
+    "http/transport_security_state.cc",
+    "http/transport_security_state.h",
+    "http/transport_security_state_static.h",
+    "http/url_security_manager.cc",
+    "http/url_security_manager.h",
+    "http/url_security_manager_posix.cc",
+    "http/url_security_manager_win.cc",
+    "ocsp/nss_ocsp.cc",
+    "ocsp/nss_ocsp.h",
+    "proxy/dhcp_proxy_script_adapter_fetcher_win.cc",
+    "proxy/dhcp_proxy_script_adapter_fetcher_win.h",
+    "proxy/dhcp_proxy_script_fetcher.cc",
+    "proxy/dhcp_proxy_script_fetcher.h",
+    "proxy/dhcp_proxy_script_fetcher_factory.cc",
+    "proxy/dhcp_proxy_script_fetcher_factory.h",
+    "proxy/dhcp_proxy_script_fetcher_win.cc",
+    "proxy/dhcp_proxy_script_fetcher_win.h",
+    "proxy/dhcpcsvc_init_win.cc",
+    "proxy/dhcpcsvc_init_win.h",
+    "proxy/multi_threaded_proxy_resolver.cc",
+    "proxy/multi_threaded_proxy_resolver.h",
+    "proxy/network_delegate_error_observer.cc",
+    "proxy/network_delegate_error_observer.h",
+    "proxy/polling_proxy_config_service.cc",
+    "proxy/polling_proxy_config_service.h",
+    "proxy/proxy_bypass_rules.cc",
+    "proxy/proxy_bypass_rules.h",
+    "proxy/proxy_config.cc",
+    "proxy/proxy_config.h",
+    "proxy/proxy_config_service.h",
+    "proxy/proxy_config_service_android.cc",
+    "proxy/proxy_config_service_android.h",
+    "proxy/proxy_config_service_fixed.cc",
+    "proxy/proxy_config_service_fixed.h",
+    "proxy/proxy_config_service_ios.cc",
+    "proxy/proxy_config_service_ios.h",
+    "proxy/proxy_config_service_linux.cc",
+    "proxy/proxy_config_service_linux.h",
+    "proxy/proxy_config_service_mac.cc",
+    "proxy/proxy_config_service_mac.h",
+    "proxy/proxy_config_service_win.cc",
+    "proxy/proxy_config_service_win.h",
+    "proxy/proxy_config_source.cc",
+    "proxy/proxy_config_source.h",
+    "proxy/proxy_info.cc",
+    "proxy/proxy_info.h",
+    "proxy/proxy_list.cc",
+    "proxy/proxy_list.h",
+    "proxy/proxy_resolver.h",
+    "proxy/proxy_resolver_error_observer.h",
+    "proxy/proxy_resolver_mac.cc",
+    "proxy/proxy_resolver_mac.h",
+    "proxy/proxy_resolver_script.h",
+    "proxy/proxy_resolver_script_data.cc",
+    "proxy/proxy_resolver_script_data.h",
+    "proxy/proxy_resolver_winhttp.cc",
+    "proxy/proxy_resolver_winhttp.h",
+    "proxy/proxy_retry_info.h",
+    "proxy/proxy_script_decider.cc",
+    "proxy/proxy_script_decider.h",
+    "proxy/proxy_script_fetcher.h",
+    "proxy/proxy_script_fetcher_impl.cc",
+    "proxy/proxy_script_fetcher_impl.h",
+    "proxy/proxy_server.cc",
+    "proxy/proxy_server.h",
+    "proxy/proxy_server_mac.cc",
+    "proxy/proxy_service.cc",
+    "proxy/proxy_service.h",
+    "quic/congestion_control/available_channel_estimator.cc",
+    "quic/congestion_control/available_channel_estimator.h",
+    "quic/congestion_control/channel_estimator.cc",
+    "quic/congestion_control/channel_estimator.h",
+    "quic/congestion_control/cube_root.cc",
+    "quic/congestion_control/cube_root.h",
+    "quic/congestion_control/cubic.cc",
+    "quic/congestion_control/cubic.h",
+    "quic/congestion_control/fix_rate_receiver.cc",
+    "quic/congestion_control/fix_rate_receiver.h",
+    "quic/congestion_control/fix_rate_sender.cc",
+    "quic/congestion_control/fix_rate_sender.h",
+    "quic/congestion_control/hybrid_slow_start.cc",
+    "quic/congestion_control/hybrid_slow_start.h",
+    "quic/congestion_control/inter_arrival_bitrate_ramp_up.cc",
+    "quic/congestion_control/inter_arrival_bitrate_ramp_up.h",
+    "quic/congestion_control/inter_arrival_overuse_detector.cc",
+    "quic/congestion_control/inter_arrival_overuse_detector.h",
+    "quic/congestion_control/inter_arrival_probe.cc",
+    "quic/congestion_control/inter_arrival_probe.h",
+    "quic/congestion_control/inter_arrival_receiver.cc",
+    "quic/congestion_control/inter_arrival_receiver.h",
+    "quic/congestion_control/inter_arrival_sender.cc",
+    "quic/congestion_control/inter_arrival_sender.h",
+    "quic/congestion_control/inter_arrival_state_machine.cc",
+    "quic/congestion_control/inter_arrival_state_machine.h",
+    "quic/congestion_control/leaky_bucket.cc",
+    "quic/congestion_control/leaky_bucket.h",
+    "quic/congestion_control/paced_sender.cc",
+    "quic/congestion_control/paced_sender.h",
+    "quic/congestion_control/quic_congestion_manager.cc",
+    "quic/congestion_control/quic_congestion_manager.h",
+    "quic/congestion_control/quic_max_sized_map.h",
+    "quic/congestion_control/receive_algorithm_interface.cc",
+    "quic/congestion_control/receive_algorithm_interface.h",
+    "quic/congestion_control/send_algorithm_interface.cc",
+    "quic/congestion_control/send_algorithm_interface.h",
+    "quic/congestion_control/tcp_cubic_sender.cc",
+    "quic/congestion_control/tcp_cubic_sender.h",
+    "quic/congestion_control/tcp_receiver.cc",
+    "quic/congestion_control/tcp_receiver.h",
+    "quic/crypto/aes_128_gcm_12_decrypter.h",
+    "quic/crypto/aes_128_gcm_12_decrypter_nss.cc",
+    "quic/crypto/aes_128_gcm_12_decrypter_openssl.cc",
+    "quic/crypto/aes_128_gcm_12_encrypter.h",
+    "quic/crypto/aes_128_gcm_12_encrypter_nss.cc",
+    "quic/crypto/aes_128_gcm_12_encrypter_openssl.cc",
+    "quic/crypto/cert_compressor.cc",
+    "quic/crypto/cert_compressor.h",
+    "quic/crypto/channel_id.cc",
+    "quic/crypto/channel_id.h",
+    "quic/crypto/channel_id_nss.cc",
+    "quic/crypto/channel_id_openssl.cc",
+    "quic/crypto/common_cert_set.cc",
+    "quic/crypto/common_cert_set.h",
+    "quic/crypto/crypto_framer.cc",
+    "quic/crypto/crypto_framer.h",
+    "quic/crypto/crypto_handshake.cc",
+    "quic/crypto/crypto_handshake.h",
+    "quic/crypto/crypto_protocol.h",
+    "quic/crypto/crypto_secret_boxer.cc",
+    "quic/crypto/crypto_secret_boxer.h",
+    "quic/crypto/crypto_server_config.cc",
+    "quic/crypto/crypto_server_config.h",
+    "quic/crypto/crypto_server_config_protobuf.cc",
+    "quic/crypto/crypto_server_config_protobuf.h",
+    "quic/crypto/crypto_utils.cc",
+    "quic/crypto/crypto_utils.h",
+    "quic/crypto/curve25519_key_exchange.cc",
+    "quic/crypto/curve25519_key_exchange.h",
+    "quic/crypto/ephemeral_key_source.h",
+    "quic/crypto/key_exchange.h",
+    "quic/crypto/null_decrypter.cc",
+    "quic/crypto/null_decrypter.h",
+    "quic/crypto/null_encrypter.cc",
+    "quic/crypto/null_encrypter.h",
+    "quic/crypto/p256_key_exchange.h",
+    "quic/crypto/p256_key_exchange_nss.cc",
+    "quic/crypto/p256_key_exchange_openssl.cc",
+    "quic/crypto/proof_source.h",
+    "quic/crypto/proof_source_chromium.cc",
+    "quic/crypto/proof_source_chromium.h",
+    "quic/crypto/proof_verifier.cc",
+    "quic/crypto/proof_verifier_chromium.cc",
+    "quic/crypto/proof_verifier_chromium.h",
+    "quic/crypto/quic_decrypter.cc",
+    "quic/crypto/quic_decrypter.h",
+    "quic/crypto/quic_encrypter.cc",
+    "quic/crypto/quic_encrypter.h",
+    "quic/crypto/quic_random.cc",
+    "quic/crypto/quic_random.h",
+    "quic/crypto/scoped_evp_cipher_ctx.cc",
+    "quic/crypto/scoped_evp_cipher_ctx.h",
+    "quic/crypto/strike_register.cc",
+    "quic/crypto/strike_register.h",
+    "quic/crypto/source_address_token.cc",
+    "quic/crypto/source_address_token.h",
+    "quic/quic_alarm.cc",
+    "quic/quic_alarm.h",
+    "quic/quic_bandwidth.cc",
+    "quic/quic_bandwidth.h",
+    "quic/quic_blocked_writer_interface.h",
+    "quic/quic_client_session.cc",
+    "quic/quic_client_session.h",
+    "quic/quic_config.cc",
+    "quic/quic_config.h",
+    "quic/quic_crypto_client_stream.cc",
+    "quic/quic_crypto_client_stream.h",
+    "quic/quic_crypto_client_stream_factory.h",
+    "quic/quic_crypto_server_stream.cc",
+    "quic/quic_crypto_server_stream.h",
+    "quic/quic_crypto_stream.cc",
+    "quic/quic_crypto_stream.h",
+    "quic/quic_clock.cc",
+    "quic/quic_clock.h",
+    "quic/quic_connection.cc",
+    "quic/quic_connection.h",
+    "quic/quic_connection_helper.cc",
+    "quic/quic_connection_helper.h",
+    "quic/quic_connection_logger.cc",
+    "quic/quic_connection_logger.h",
+    "quic/quic_connection_stats.cc",
+    "quic/quic_connection_stats.h",
+    "quic/quic_data_reader.cc",
+    "quic/quic_data_reader.h",
+    "quic/quic_data_writer.cc",
+    "quic/quic_data_writer.h",
+    "quic/quic_fec_group.cc",
+    "quic/quic_fec_group.h",
+    "quic/quic_framer.cc",
+    "quic/quic_framer.h",
+    "quic/quic_http_stream.cc",
+    "quic/quic_http_stream.h",
+    "quic/quic_packet_creator.cc",
+    "quic/quic_packet_creator.h",
+    "quic/quic_packet_generator.cc",
+    "quic/quic_packet_generator.h",
+    "quic/quic_protocol.cc",
+    "quic/quic_protocol.h",
+    "quic/quic_received_packet_manager.cc",
+    "quic/quic_received_packet_manager.h",
+    "quic/quic_reliable_client_stream.cc",
+    "quic/quic_reliable_client_stream.h",
+    "quic/quic_sent_entropy_manager.cc",
+    "quic/quic_sent_entropy_manager.h",
+    "quic/quic_session.cc",
+    "quic/quic_session.h",
+    "quic/quic_spdy_compressor.cc",
+    "quic/quic_spdy_compressor.h",
+    "quic/quic_spdy_decompressor.cc",
+    "quic/quic_spdy_decompressor.h",
+    "quic/quic_stream_factory.cc",
+    "quic/quic_stream_factory.h",
+    "quic/quic_stream_sequencer.cc",
+    "quic/quic_stream_sequencer.h",
+    "quic/quic_time.cc",
+    "quic/quic_time.h",
+    "quic/quic_utils.cc",
+    "quic/quic_utils.h",
+    "quic/reliable_quic_stream.cc",
+    "quic/reliable_quic_stream.h",
+    "quic/spdy_utils.cc",
+    "quic/spdy_utils.h",
+    "socket/buffered_write_stream_socket.cc",
+    "socket/buffered_write_stream_socket.h",
+    "socket/client_socket_factory.cc",
+    "socket/client_socket_factory.h",
+    "socket/client_socket_handle.cc",
+    "socket/client_socket_handle.h",
+    "socket/client_socket_pool.cc",
+    "socket/client_socket_pool.h",
+    "socket/client_socket_pool_base.cc",
+    "socket/client_socket_pool_base.h",
+    "socket/client_socket_pool_histograms.cc",
+    "socket/client_socket_pool_histograms.h",
+    "socket/client_socket_pool_manager.cc",
+    "socket/client_socket_pool_manager.h",
+    "socket/client_socket_pool_manager_impl.cc",
+    "socket/client_socket_pool_manager_impl.h",
+    "socket/next_proto.h",
+    "socket/nss_ssl_util.cc",
+    "socket/nss_ssl_util.h",
+    "socket/server_socket.h",
+    "socket/socket_net_log_params.cc",
+    "socket/socket_net_log_params.h",
+    "socket/socket.h",
+    "socket/socks5_client_socket.cc",
+    "socket/socks5_client_socket.h",
+    "socket/socks_client_socket.cc",
+    "socket/socks_client_socket.h",
+    "socket/socks_client_socket_pool.cc",
+    "socket/socks_client_socket_pool.h",
+    "socket/ssl_client_socket.cc",
+    "socket/ssl_client_socket.h",
+    "socket/ssl_client_socket_nss.cc",
+    "socket/ssl_client_socket_nss.h",
+    "socket/ssl_client_socket_openssl.cc",
+    "socket/ssl_client_socket_openssl.h",
+    "socket/ssl_client_socket_pool.cc",
+    "socket/ssl_client_socket_pool.h",
+    "socket/ssl_error_params.cc",
+    "socket/ssl_error_params.h",
+    "socket/ssl_server_socket.h",
+    "socket/ssl_server_socket_nss.cc",
+    "socket/ssl_server_socket_nss.h",
+    "socket/ssl_server_socket_openssl.cc",
+    "socket/ssl_socket.h",
+    "socket/stream_listen_socket.cc",
+    "socket/stream_listen_socket.h",
+    "socket/stream_socket.cc",
+    "socket/stream_socket.h",
+    "socket/tcp_client_socket.cc",
+    "socket/tcp_client_socket.h",
+    "socket/tcp_client_socket_libevent.cc",
+    "socket/tcp_client_socket_libevent.h",
+    "socket/tcp_client_socket_win.cc",
+    "socket/tcp_client_socket_win.h",
+    "socket/tcp_listen_socket.cc",
+    "socket/tcp_listen_socket.h",
+    "socket/tcp_server_socket.h",
+    "socket/tcp_server_socket_libevent.cc",
+    "socket/tcp_server_socket_libevent.h",
+    "socket/tcp_server_socket_win.cc",
+    "socket/tcp_server_socket_win.h",
+    "socket/transport_client_socket_pool.cc",
+    "socket/transport_client_socket_pool.h",
+    "socket/unix_domain_socket_posix.cc",
+    "socket/unix_domain_socket_posix.h",
+    "socket_stream/socket_stream.cc",
+    "socket_stream/socket_stream.h",
+    "socket_stream/socket_stream_job.cc",
+    "socket_stream/socket_stream_job.h",
+    "socket_stream/socket_stream_job_manager.cc",
+    "socket_stream/socket_stream_job_manager.h",
+    "socket_stream/socket_stream_metrics.cc",
+    "socket_stream/socket_stream_metrics.h",
+    "spdy/buffered_spdy_framer.cc",
+    "spdy/buffered_spdy_framer.h",
+    "spdy/spdy_bitmasks.h",
+    "spdy/spdy_buffer.cc",
+    "spdy/spdy_buffer.h",
+    "spdy/spdy_buffer_producer.cc",
+    "spdy/spdy_buffer_producer.h",
+    "spdy/spdy_credential_builder.cc",
+    "spdy/spdy_credential_builder.h",
+    "spdy/spdy_credential_state.cc",
+    "spdy/spdy_credential_state.h",
+    "spdy/spdy_frame_builder.cc",
+    "spdy/spdy_frame_builder.h",
+    "spdy/spdy_frame_reader.cc",
+    "spdy/spdy_frame_reader.h",
+    "spdy/spdy_framer.cc",
+    "spdy/spdy_framer.h",
+    "spdy/spdy_header_block.cc",
+    "spdy/spdy_header_block.h",
+    "spdy/spdy_http_stream.cc",
+    "spdy/spdy_http_stream.h",
+    "spdy/spdy_http_utils.cc",
+    "spdy/spdy_http_utils.h",
+    "spdy/spdy_priority_forest.h",
+    "spdy/spdy_protocol.cc",
+    "spdy/spdy_protocol.h",
+    "spdy/spdy_proxy_client_socket.cc",
+    "spdy/spdy_proxy_client_socket.h",
+    "spdy/spdy_read_queue.cc",
+    "spdy/spdy_read_queue.h",
+    "spdy/spdy_session.cc",
+    "spdy/spdy_session.h",
+    "spdy/spdy_session_key.cc",
+    "spdy/spdy_session_key.h",
+    "spdy/spdy_session_pool.cc",
+    "spdy/spdy_session_pool.h",
+    "spdy/spdy_stream.cc",
+    "spdy/spdy_stream.h",
+    "spdy/spdy_websocket_stream.cc",
+    "spdy/spdy_websocket_stream.h",
+    "spdy/spdy_write_queue.cc",
+    "spdy/spdy_write_queue.h",
+    "spdy/write_blocked_list.h",
+    "ssl/client_cert_store.h",
+    "ssl/client_cert_store_impl.h",
+    "ssl/client_cert_store_impl_mac.cc",
+    "ssl/client_cert_store_impl_nss.cc",
+    "ssl/client_cert_store_impl_win.cc",
+    "ssl/default_server_bound_cert_store.cc",
+    "ssl/default_server_bound_cert_store.h",
+    "ssl/openssl_client_key_store.cc",
+    "ssl/openssl_client_key_store.h",
+    "ssl/server_bound_cert_service.cc",
+    "ssl/server_bound_cert_service.h",
+    "ssl/server_bound_cert_store.cc",
+    "ssl/server_bound_cert_store.h",
+    "ssl/ssl_cert_request_info.cc",
+    "ssl/ssl_cert_request_info.h",
+    "ssl/ssl_cipher_suite_names.cc",
+    "ssl/ssl_cipher_suite_names.h",
+    "ssl/ssl_client_auth_cache.cc",
+    "ssl/ssl_client_auth_cache.h",
+    "ssl/ssl_client_cert_type.h",
+    "ssl/ssl_config_service.cc",
+    "ssl/ssl_config_service.h",
+    "ssl/ssl_config_service_defaults.cc",
+    "ssl/ssl_config_service_defaults.h",
+    "ssl/ssl_info.cc",
+    "ssl/ssl_info.h",
+    "third_party/mozilla_security_manager/nsKeygenHandler.cpp",
+    "third_party/mozilla_security_manager/nsKeygenHandler.h",
+    "third_party/mozilla_security_manager/nsNSSCertificateDB.cpp",
+    "third_party/mozilla_security_manager/nsNSSCertificateDB.h",
+    "third_party/mozilla_security_manager/nsPKCS12Blob.cpp",
+    "third_party/mozilla_security_manager/nsPKCS12Blob.h",
+    "udp/datagram_client_socket.h",
+    "udp/datagram_server_socket.h",
+    "udp/datagram_socket.h",
+    "udp/udp_client_socket.cc",
+    "udp/udp_client_socket.h",
+    "udp/udp_net_log_parameters.cc",
+    "udp/udp_net_log_parameters.h",
+    "udp/udp_server_socket.cc",
+    "udp/udp_server_socket.h",
+    "udp/udp_socket.h",
+    "udp/udp_socket_libevent.cc",
+    "udp/udp_socket_libevent.h",
+    "udp/udp_socket_win.cc",
+    "udp/udp_socket_win.h",
+    "url_request/data_protocol_handler.cc",
+    "url_request/data_protocol_handler.h",
+    "url_request/file_protocol_handler.cc",
+    "url_request/file_protocol_handler.h",
+    "url_request/fraudulent_certificate_reporter.h",
+    "url_request/ftp_protocol_handler.cc",
+    "url_request/ftp_protocol_handler.h",
+    "url_request/http_user_agent_settings.h",
+    "url_request/protocol_intercept_job_factory.cc",
+    "url_request/protocol_intercept_job_factory.h",
+    "url_request/static_http_user_agent_settings.cc",
+    "url_request/static_http_user_agent_settings.h",
+    "url_request/url_fetcher.cc",
+    "url_request/url_fetcher.h",
+    "url_request/url_fetcher_core.cc",
+    "url_request/url_fetcher_core.h",
+    "url_request/url_fetcher_delegate.cc",
+    "url_request/url_fetcher_delegate.h",
+    "url_request/url_fetcher_factory.h",
+    "url_request/url_fetcher_impl.cc",
+    "url_request/url_fetcher_impl.h",
+    "url_request/url_fetcher_response_writer.cc",
+    "url_request/url_fetcher_response_writer.h",
+    "url_request/url_request.cc",
+    "url_request/url_request.h",
+    "url_request/url_request_about_job.cc",
+    "url_request/url_request_about_job.h",
+    "url_request/url_request_context.cc",
+    "url_request/url_request_context.h",
+    "url_request/url_request_context_builder.cc",
+    "url_request/url_request_context_builder.h",
+    "url_request/url_request_context_getter.cc",
+    "url_request/url_request_context_getter.h",
+    "url_request/url_request_context_storage.cc",
+    "url_request/url_request_context_storage.h",
+    "url_request/url_request_data_job.cc",
+    "url_request/url_request_data_job.h",
+    "url_request/url_request_error_job.cc",
+    "url_request/url_request_error_job.h",
+    "url_request/url_request_file_dir_job.cc",
+    "url_request/url_request_file_dir_job.h",
+    "url_request/url_request_file_job.cc",
+    "url_request/url_request_file_job.h",
+    "url_request/url_request_filter.cc",
+    "url_request/url_request_filter.h",
+    "url_request/url_request_ftp_job.cc",
+    "url_request/url_request_ftp_job.h",
+    "url_request/url_request_http_job.cc",
+    "url_request/url_request_http_job.h",
+    "url_request/url_request_job.cc",
+    "url_request/url_request_job.h",
+    "url_request/url_request_job_factory.cc",
+    "url_request/url_request_job_factory.h",
+    "url_request/url_request_job_factory_impl.cc",
+    "url_request/url_request_job_factory_impl.h",
+    "url_request/url_request_job_manager.cc",
+    "url_request/url_request_job_manager.h",
+    "url_request/url_request_netlog_params.cc",
+    "url_request/url_request_netlog_params.h",
+    "url_request/url_request_redirect_job.cc",
+    "url_request/url_request_redirect_job.h",
+    "url_request/url_request_simple_job.cc",
+    "url_request/url_request_simple_job.h",
+    "url_request/url_request_status.h",
+    "url_request/url_request_test_job.cc",
+    "url_request/url_request_test_job.h",
+    "url_request/url_request_throttler_entry.cc",
+    "url_request/url_request_throttler_entry.h",
+    "url_request/url_request_throttler_entry_interface.h",
+    "url_request/url_request_throttler_header_adapter.cc",
+    "url_request/url_request_throttler_header_adapter.h",
+    "url_request/url_request_throttler_header_interface.h",
+    "url_request/url_request_throttler_manager.cc",
+    "url_request/url_request_throttler_manager.h",
+    "url_request/view_cache_helper.cc",
+    "url_request/view_cache_helper.h",
+    "websockets/websocket_channel.cc",
+    "websockets/websocket_channel.h",
+    "websockets/websocket_errors.cc",
+    "websockets/websocket_errors.h",
+    "websockets/websocket_frame.cc",
+    "websockets/websocket_frame.h",
+    "websockets/websocket_frame_parser.cc",
+    "websockets/websocket_frame_parser.h",
+    "websockets/websocket_handshake_handler.cc",
+    "websockets/websocket_handshake_handler.h",
+    "websockets/websocket_job.cc",
+    "websockets/websocket_job.h",
+    "websockets/websocket_mux.h",
+    "websockets/websocket_net_log_params.cc",
+    "websockets/websocket_net_log_params.h",
+    "websockets/websocket_stream.cc",
+    "websockets/websocket_stream.h",
+    "websockets/websocket_stream_base.h",
+    "websockets/websocket_throttle.cc",
+    "websockets/websocket_throttle.h",
+  ]
+
+  defines = [ "NET_IMPLEMENTATION" ]
+
   deps = [
     ":net_resources",
     "//base",
     "//base:base_i18n",
     "//base/third_party/dynamic_annotations",
     "//crypto",
+    "//crypto/ssl:metassl",
     "//sdch",
     "//third_party/icu:icui18n",
     "//third_party/icu:icuuc",
-    "//third_party/zlib",
+    "//third_party/zlib:chrome_zlib",
     "//url:url_lib",
   ]
+
+  if (is_win) {
+    sources -= [
+      "http/http_auth_handler_ntlm_portable.cc",
+      "socket/tcp_client_socket_libevent.cc",
+      "socket/tcp_client_socket_libevent.h",
+      "socket/tcp_server_socket_libevent.cc",
+      "socket/tcp_server_socket_libevent.h",
+      "ssl/client_cert_store_impl_nss.cc",
+      "udp/udp_socket_libevent.cc",
+      "udp/udp_socket_libevent.h",
+    ]
+    deps += [
+      #"//net/third_party/nss/ssl:crssl",
+      #"//third_party/nss:nspr",
+      #"//third_party/nss:nss",
+    ]
+  } else {  # !is_win
+    sources -= [
+      "base/winsock_init.cc",
+      "base/winsock_init.h",
+      "base/winsock_util.cc",
+      "base/winsock_util.h",
+      "proxy/proxy_resolver_winhttp.cc",
+      "proxy/proxy_resolver_winhttp.h",
+    ]
+  }
+
+  if (is_mac) {
+    sources -= [
+      "ssl/client_cert_store_impl_nss.cc",
+    ]
+    deps += [
+      "//net/third_party/nss/ssl:crssl",
+      "//third_party/nss:nspr",
+      "//third_party/nss:nss",
+    ]
+  }
+
+  if (is_chromeos) {
+    sources -= [
+       "base/network_change_notifier_linux.cc",
+       "base/network_change_notifier_linux.h",
+       "base/network_change_notifier_netlink_linux.cc",
+       "base/network_change_notifier_netlink_linux.h",
+       "proxy/proxy_config_service_linux.cc",
+       "proxy/proxy_config_service_linux.h",
+    ]
+  }
+
+  if (use_openssl) {
+    sources -= [
+      "base/crypto_module_nss.cc",
+      "base/keygen_handler_nss.cc",
+      "base/nss_memio.c",
+      "base/nss_memio.h",
+      "cert/cert_database_nss.cc",
+      "cert/cert_verify_proc_nss.cc",
+      "cert/cert_verify_proc_nss.h",
+      "cert/jwk_serializer_nss.cc",
+      "cert/nss_cert_database.cc",
+      "cert/nss_cert_database.h",
+      "cert/test_root_certs_nss.cc",
+      "cert/x509_certificate_nss.cc",
+      "cert/x509_util_nss.cc",
+      "cert/x509_util_nss.h",
+      "ocsp/nss_ocsp.cc",
+      "ocsp/nss_ocsp.h",
+      "quic/crypto/aes_128_gcm_12_decrypter_nss.cc",
+      "quic/crypto/aes_128_gcm_12_encrypter_nss.cc",
+      "quic/crypto/channel_id_nss.cc",
+      "quic/crypto/p256_key_exchange_nss.cc",
+      "socket/nss_ssl_util.cc",
+      "socket/nss_ssl_util.h",
+      "socket/ssl_client_socket_nss.cc",
+      "socket/ssl_client_socket_nss.h",
+      "socket/ssl_server_socket_nss.cc",
+      "socket/ssl_server_socket_nss.h",
+      "ssl/client_cert_store_impl_nss.cc",
+      "third_party/mozilla_security_manager/nsKeygenHandler.cpp",
+      "third_party/mozilla_security_manager/nsKeygenHandler.h",
+      "third_party/mozilla_security_manager/nsNSSCertificateDB.cpp",
+      "third_party/mozilla_security_manager/nsNSSCertificateDB.h",
+      "third_party/mozilla_security_manager/nsPKCS12Blob.cpp",
+      "third_party/mozilla_security_manager/nsPKCS12Blob.h",
+    ]
+  } else {  # !use_openssl
+    sources -= [
+      "base/crypto_module_openssl.cc",
+      "base/keygen_handler_openssl.cc",
+      "base/openssl_private_key_store.h",
+      #"base/openssl_private_key_store_android.cc",
+      "base/openssl_private_key_store_memory.cc",
+      "cert/cert_database_openssl.cc",
+      "cert/cert_verify_proc_openssl.cc",
+      "cert/cert_verify_proc_openssl.h",
+      "cert/jwk_serializer_openssl.cc",
+      "cert/test_root_certs_openssl.cc",
+      "cert/x509_certificate_openssl.cc",
+      "cert/x509_util_openssl.cc",
+      "cert/x509_util_openssl.h",
+      "quic/crypto/aes_128_gcm_12_decrypter_openssl.cc",
+      "quic/crypto/aes_128_gcm_12_encrypter_openssl.cc",
+      "quic/crypto/channel_id_openssl.cc",
+      "quic/crypto/p256_key_exchange_openssl.cc",
+      "quic/crypto/scoped_evp_cipher_ctx.cc",
+      "quic/crypto/scoped_evp_cipher_ctx.h",
+      "socket/ssl_client_socket_openssl.cc",
+      "socket/ssl_client_socket_openssl.h",
+      "socket/ssl_server_socket_openssl.cc",
+      "ssl/openssl_client_key_store.cc",
+      "ssl/openssl_client_key_store.h",
+    ]
+  }
+
+  if (is_posix) {
+    posix_avoid_mmap = false  # TODO(brettw) should be true on 32-bit Android.
+    if (posix_avoid_mmap) {
+      defines = [ "POSIX_AVOID_MMAP" ]
+      sources -= "disk_cache/mapped_file_posix.cc"
+    } else {  # !posix_avoid_mmap
+      sources -= "disk_cache/mapped_file_avoid_mmap_posix.cc"
+    }
+  }
 }
 
-
 grit("net_resources") {
+  external = true
   source = "base/net_resources.grd"
 }
diff --git a/tools/gn/secondary/net/third_party/nss/ssl/BUILD.gn b/tools/gn/secondary/net/third_party/nss/ssl/BUILD.gn
index d5658c6..e72988d 100644
--- a/tools/gn/secondary/net/third_party/nss/ssl/BUILD.gn
+++ b/tools/gn/secondary/net/third_party/nss/ssl/BUILD.gn
@@ -2,8 +2,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+config("crssl_config") {
+  includes = [
+    "//net/third_party/nss/ssl",
+  ]
+}
+
 # Not named "ssl" so the lib doesn't conflict with OpenSSL's libssl
 component("crssl") {
+  external = true
   sources = [
     "authcert.c",
     "cmpcert.c",
@@ -59,6 +66,8 @@
   configs -= "//build/config/compiler:chromium_code"
   configs += "//build/config/compiler:no_chromium_code"
 
+  direct_dependent_configs = [ ":crssl_config" ]
+
   if (is_win) {
     sources -= [
       "unix_err.c",
@@ -73,7 +82,6 @@
 
   if (is_linux) {
     includes = [ "bodge" ]
-    direct_dependent_configs = [ "//net/third_party/nss:nss_linux_config" ]
   }
   if (is_mac) {
     sources -= "bodge/secitem_array.c"
diff --git a/tools/gn/secondary/sdch/BUILD.gn b/tools/gn/secondary/sdch/BUILD.gn
index 2c5849c..9396624 100644
--- a/tools/gn/secondary/sdch/BUILD.gn
+++ b/tools/gn/secondary/sdch/BUILD.gn
@@ -7,6 +7,7 @@
 }
 
 static_library("sdch") {
+  external = true
   sources = [
     "open-vcdiff/src/addrcache.cc",
     "open-vcdiff/src/blockhash.cc",
@@ -51,5 +52,5 @@
     includes = [ "win" ]
   }
 
-  deps = [ "//third_party/zlib" ]
+  deps = [ "//third_party/zlib:chrome_zlib" ]
 }
diff --git a/tools/gn/secondary/testing/BUILD.gn b/tools/gn/secondary/testing/BUILD.gn
new file mode 100644
index 0000000..8e88278
--- /dev/null
+++ b/tools/gn/secondary/testing/BUILD.gn
@@ -0,0 +1,98 @@
+# Copyright (c) 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.
+
+# TODO(brettw) move to testing/gtest/BUILD.gn
+config("gtest_config") {
+  defines = [ "UNIT_TEST" ]
+  includes = [ "gtest/include" ]  # Gtest headers need to be able to find themselves.
+}
+
+# TODO(brettw) move to testing/gtest/BUILD.gn
+static_library("gtest") {
+  external = true
+  sources = [
+    "gtest/include/gtest/gtest-death-test.h",
+    "gtest/include/gtest/gtest-message.h",
+    "gtest/include/gtest/gtest-param-test.h",
+    "gtest/include/gtest/gtest-printers.h",
+    "gtest/include/gtest/gtest-spi.h",
+    "gtest/include/gtest/gtest-test-part.h",
+    "gtest/include/gtest/gtest-typed-test.h",
+    "gtest/include/gtest/gtest.h",
+    "gtest/include/gtest/gtest_pred_impl.h",
+    "gtest/include/gtest/internal/gtest-death-test-internal.h",
+    "gtest/include/gtest/internal/gtest-filepath.h",
+    "gtest/include/gtest/internal/gtest-internal.h",
+    "gtest/include/gtest/internal/gtest-linked_ptr.h",
+    "gtest/include/gtest/internal/gtest-param-util-generated.h",
+    "gtest/include/gtest/internal/gtest-param-util.h",
+    "gtest/include/gtest/internal/gtest-port.h",
+    "gtest/include/gtest/internal/gtest-string.h",
+    "gtest/include/gtest/internal/gtest-tuple.h",
+    "gtest/include/gtest/internal/gtest-type-util.h",
+    #"gtest/src/gtest-all.cc",  # Not needed by our build.
+    "gtest/src/gtest-death-test.cc",
+    "gtest/src/gtest-filepath.cc",
+    "gtest/src/gtest-internal-inl.h",
+    "gtest/src/gtest-port.cc",
+    "gtest/src/gtest-printers.cc",
+    "gtest/src/gtest-test-part.cc",
+    "gtest/src/gtest-typed-test.cc",
+    "gtest/src/gtest.cc",
+    "multiprocess_func_list.cc",
+    "multiprocess_func_list.h",
+    "platform_test.h",
+  ]
+
+  includes = [ "gtest" ]
+  direct_dependent_configs = [ ":gtest_config" ]
+}
+
+# TODO(brettw) move to testing/gmock/BUILD.gn
+config("gmock_config") {
+  # Gmock headers need to be able to find themselves.
+  includes = [ "gmock/include" ]
+}
+
+# TODO(brettw) move to testing/gmock/BUILD.gn
+static_library("gmock") {
+  external = true
+  sources = [
+    # Sources based on files in r173 of gmock.
+    "gmock/include/gmock/gmock-actions.h",
+    "gmock/include/gmock/gmock-cardinalities.h",
+    "gmock/include/gmock/gmock-generated-actions.h",
+    "gmock/include/gmock/gmock-generated-function-mockers.h",
+    "gmock/include/gmock/gmock-generated-matchers.h",
+    "gmock/include/gmock/gmock-generated-nice-strict.h",
+    "gmock/include/gmock/gmock-matchers.h",
+    "gmock/include/gmock/gmock-spec-builders.h",
+    "gmock/include/gmock/gmock.h",
+    "gmock/include/gmock/internal/gmock-generated-internal-utils.h",
+    "gmock/include/gmock/internal/gmock-internal-utils.h",
+    "gmock/include/gmock/internal/gmock-port.h",
+    #"gmock/src/gmock-all.cc",  # Not needed by our build.
+    "gmock/src/gmock-cardinalities.cc",
+    "gmock/src/gmock-internal-utils.cc",
+    "gmock/src/gmock-matchers.cc",
+    "gmock/src/gmock-spec-builders.cc",
+    "gmock/src/gmock.cc",
+    "gmock_mutant.h",  # gMock helpers
+  ]
+
+  # This project includes some stuff form gtest's guts.
+  includes = [ "gtest/include" ]
+
+  direct_dependent_configs = [
+    ":gmock_config",
+    "//testing:gtest_config",
+  ]
+}
+
+# TODO(brettw) move to testing/gmock/BUILD.gn
+static_library("gmock_main") {
+  external = true
+  sources = [ "src/gmock_main.cc" ]
+  deps = [ ":gmock" ]
+}
diff --git a/tools/gn/secondary/testing/gmock/BUILD.gn b/tools/gn/secondary/testing/gmock/BUILD.gn
deleted file mode 100644
index 532d2a4..0000000
--- a/tools/gn/secondary/testing/gmock/BUILD.gn
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright (c) 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.
-
-config("gmock_config") {
-  # Gmock headers need to be able to find themselves.
-  includes = [ "include" ]
-}
-
-static_library("gmock") {
-  sources = [
-    # Sources based on files in r173 of gmock.
-    "include/gmock/gmock-actions.h",
-    "include/gmock/gmock-cardinalities.h",
-    "include/gmock/gmock-generated-actions.h",
-    "include/gmock/gmock-generated-function-mockers.h",
-    "include/gmock/gmock-generated-matchers.h",
-    "include/gmock/gmock-generated-nice-strict.h",
-    "include/gmock/gmock-matchers.h",
-    "include/gmock/gmock-spec-builders.h",
-    "include/gmock/gmock.h",
-    "include/gmock/internal/gmock-generated-internal-utils.h",
-    "include/gmock/internal/gmock-internal-utils.h",
-    "include/gmock/internal/gmock-port.h",
-    #"src/gmock-all.cc",  # Not needed by our build.
-    "src/gmock-cardinalities.cc",
-    "src/gmock-internal-utils.cc",
-    "src/gmock-matchers.cc",
-    "src/gmock-spec-builders.cc",
-    "src/gmock.cc",
-    "../gmock_mutant.h",  # gMock helpers
-  ]
-
-  # This project includes some stuff form gtest's guts.
-  includes = [ "../gtest/include" ]
-
-  direct_dependent_configs = [
-    ":gmock_config",
-    "//testing/gtest:gtest_config",
-  ]
-}
-
-static_library("gmock_main") {
-  sources = [ "src/gmock_main.cc" ]
-  deps = [ ":gmock" ]
-}
diff --git a/tools/gn/secondary/testing/gtest/BUILD.gn b/tools/gn/secondary/testing/gtest/BUILD.gn
deleted file mode 100644
index 05bedda..0000000
--- a/tools/gn/secondary/testing/gtest/BUILD.gn
+++ /dev/null
@@ -1,47 +0,0 @@
-# Copyright (c) 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.
-
-config("gtest_config") {
-  defines = [ "UNIT_TEST" ]
-  includes = [ "include" ]  # Gtest headers need to be able to find themselves.
-}
-
-static_library("gtest") {
-  sources = [
-    "include/gtest/gtest-death-test.h",
-    "include/gtest/gtest-message.h",
-    "include/gtest/gtest-param-test.h",
-    "include/gtest/gtest-printers.h",
-    "include/gtest/gtest-spi.h",
-    "include/gtest/gtest-test-part.h",
-    "include/gtest/gtest-typed-test.h",
-    "include/gtest/gtest.h",
-    "include/gtest/gtest_pred_impl.h",
-    "include/gtest/internal/gtest-death-test-internal.h",
-    "include/gtest/internal/gtest-filepath.h",
-    "include/gtest/internal/gtest-internal.h",
-    "include/gtest/internal/gtest-linked_ptr.h",
-    "include/gtest/internal/gtest-param-util-generated.h",
-    "include/gtest/internal/gtest-param-util.h",
-    "include/gtest/internal/gtest-port.h",
-    "include/gtest/internal/gtest-string.h",
-    "include/gtest/internal/gtest-tuple.h",
-    "include/gtest/internal/gtest-type-util.h",
-    #"src/gtest-all.cc",  # Not needed by our build.
-    "src/gtest-death-test.cc",
-    "src/gtest-filepath.cc",
-    "src/gtest-internal-inl.h",
-    "src/gtest-port.cc",
-    "src/gtest-printers.cc",
-    "src/gtest-test-part.cc",
-    "src/gtest-typed-test.cc",
-    "src/gtest.cc",
-    "../multiprocess_func_list.cc",
-    "../multiprocess_func_list.h",
-    "../platform_test.h",
-  ]
-
-  includes = [ "." ]
-  direct_dependent_configs = [ ":gtest_config" ]
-}
diff --git a/tools/gn/secondary/third_party/icu/BUILD.gn b/tools/gn/secondary/third_party/icu/BUILD.gn
index f78d981..1db4c2e 100644
--- a/tools/gn/secondary/third_party/icu/BUILD.gn
+++ b/tools/gn/secondary/third_party/icu/BUILD.gn
@@ -45,6 +45,7 @@
 }
 
 component("icui18n") {
+  external = true
   sources = [
     "source/i18n/anytrans.cpp",
     "source/i18n/astro.cpp",
@@ -217,6 +218,7 @@
 }
 
 component("icuuc") {
+  external = true
   sources = [
     "source/common/bmpset.cpp",
     "source/common/brkeng.cpp",
@@ -400,11 +402,13 @@
 if (is_win) {
   # On Windows the target DLL is pre-built so just use a copy rule.
   copy("icudata") {
+    external = true
     sources = [ "windows/icudt.dll" ]
     destdir = root_output_dir
   }
 } else {
   static_library("icudata") {
+    external = true
     sources = [
       # These are hand-generated, but will do for now.  The linux version is an
       # identical copy of the (mac) icudt46l_dat.S file, modulo removal of the
diff --git a/tools/gn/secondary/third_party/libevent/BUILD.gn b/tools/gn/secondary/third_party/libevent/BUILD.gn
index afbd67d..ea7cb27 100644
--- a/tools/gn/secondary/third_party/libevent/BUILD.gn
+++ b/tools/gn/secondary/third_party/libevent/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 static_library("libevent") {
+  external = true
   sources = [
     "buffer.c",
     "epoll.c",
diff --git a/tools/gn/secondary/third_party/libxml/BUILD.gn b/tools/gn/secondary/third_party/libxml/BUILD.gn
new file mode 100644
index 0000000..2a7a3de
--- /dev/null
+++ b/tools/gn/secondary/third_party/libxml/BUILD.gn
@@ -0,0 +1,176 @@
+# Copyright (c) 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.
+
+# Define an "os_include" variable that points at the OS-specific generated
+# headers.  These were generated by running the configure script offline.
+if (is_linux) {
+  os_include = "linux"
+} else if (is_mac) {
+  os_include = "mac"
+} else if (is_win) {
+  os_include = "win32"
+}
+
+config("libxml_config") {
+  # Define LIBXML_STATIC as nothing to match how libxml.h (an internal header)
+  # defines LIBXML_STATIC, otherwise we get the macro redefined warning from
+  # GCC. ("defines" does "-DFOO" which defines the macro FOO as 1.)
+  cflags = [ "-DLIBXML_STATIC=" ]
+
+  includes = [
+    "src/include",
+    "$os_include/include",
+  ]
+}
+
+static_library("libxml2") {
+  external = true
+  sources = [
+    "chromium/libxml_utils.h",
+    "chromium/libxml_utils.cc",
+    "linux/config.h",
+    "linux/include/libxml/xmlversion.h",
+    "mac/config.h",
+    "mac/include/libxml/xmlversion.h",
+    "src/include/libxml/c14n.h",
+    "src/include/libxml/catalog.h",
+    "src/include/libxml/chvalid.h",
+    "src/include/libxml/debugXML.h",
+    "src/include/libxml/dict.h",
+    "src/include/libxml/DOCBparser.h",
+    "src/include/libxml/encoding.h",
+    "src/include/libxml/entities.h",
+    "src/include/libxml/globals.h",
+    "src/include/libxml/hash.h",
+    "src/include/libxml/HTMLparser.h",
+    "src/include/libxml/HTMLtree.h",
+    "src/include/libxml/list.h",
+    "src/include/libxml/nanoftp.h",
+    "src/include/libxml/nanohttp.h",
+    "src/include/libxml/parser.h",
+    "src/include/libxml/parserInternals.h",
+    "src/include/libxml/pattern.h",
+    "src/include/libxml/relaxng.h",
+    "src/include/libxml/SAX.h",
+    "src/include/libxml/SAX2.h",
+    "src/include/libxml/schemasInternals.h",
+    "src/include/libxml/schematron.h",
+    "src/include/libxml/threads.h",
+    "src/include/libxml/tree.h",
+    "src/include/libxml/uri.h",
+    "src/include/libxml/valid.h",
+    "src/include/libxml/xinclude.h",
+    "src/include/libxml/xlink.h",
+    "src/include/libxml/xmlautomata.h",
+    "src/include/libxml/xmlerror.h",
+    "src/include/libxml/xmlexports.h",
+    "src/include/libxml/xmlIO.h",
+    "src/include/libxml/xmlmemory.h",
+    "src/include/libxml/xmlmodule.h",
+    "src/include/libxml/xmlreader.h",
+    "src/include/libxml/xmlregexp.h",
+    "src/include/libxml/xmlsave.h",
+    "src/include/libxml/xmlschemas.h",
+    "src/include/libxml/xmlschemastypes.h",
+    "src/include/libxml/xmlstring.h",
+    "src/include/libxml/xmlunicode.h",
+    "src/include/libxml/xmlwriter.h",
+    "src/include/libxml/xpath.h",
+    "src/include/libxml/xpathInternals.h",
+    "src/include/libxml/xpointer.h",
+    "src/include/win32config.h",
+    "src/include/wsockcompat.h",
+    "src/acconfig.h",
+    "src/c14n.c",
+    "src/catalog.c",
+    "src/chvalid.c",
+    "src/debugXML.c",
+    "src/dict.c",
+    "src/DOCBparser.c",
+    "src/elfgcchack.h",
+    "src/encoding.c",
+    "src/entities.c",
+    "src/error.c",
+    "src/globals.c",
+    "src/hash.c",
+    "src/HTMLparser.c",
+    "src/HTMLtree.c",
+    "src/legacy.c",
+    "src/libxml.h",
+    "src/list.c",
+    "src/nanoftp.c",
+    "src/nanohttp.c",
+    "src/parser.c",
+    "src/parserInternals.c",
+    "src/pattern.c",
+    "src/relaxng.c",
+    "src/SAX.c",
+    "src/SAX2.c",
+    "src/schematron.c",
+    "src/threads.c",
+    "src/tree.c",
+    #"src/trio.c",
+    #"src/trio.h",
+    #"src/triodef.h",
+    #"src/trionan.c",
+    #"src/trionan.h",
+    #"src/triop.h",
+    #"src/triostr.c",
+    #"src/triostr.h",
+    "src/uri.c",
+    "src/valid.c",
+    "src/xinclude.c",
+    "src/xlink.c",
+    "src/xmlIO.c",
+    "src/xmlmemory.c",
+    "src/xmlmodule.c",
+    "src/xmlreader.c",
+    "src/xmlregexp.c",
+    "src/xmlsave.c",
+    "src/xmlschemas.c",
+    "src/xmlschemastypes.c",
+    "src/xmlstring.c",
+    "src/xmlunicode.c",
+    "src/xmlwriter.c",
+    "src/xpath.c",
+    "src/xpointer.c",
+    "win32/config.h",
+    "win32/include/libxml/xmlversion.h",
+  ]
+
+  configs -= "//build/config/compiler:chromium_code"
+  configs += "//build/config/compiler:no_chromium_code"
+
+  direct_dependent_configs = [ ":libxml_config" ]
+  forward_dependent_configs_from = [ "//third_party/icu:icuuc" ]
+
+  deps = [
+    "//third_party/icu:icuuc",
+    "//third_party/zlib:chrome_zlib",
+  ]
+
+  if (is_linux) {
+    # We need dl for dlopen() and friends.
+    ldflags = [ "-ldl" ]
+  }
+
+  if (is_clang) {
+    cflags = [
+      # libxml passes `const unsigned char*` through `const char*`.
+      "-Wno-pointer-sign",
+
+      # pattern.c and uri.c both have an intentional `for (...);` /
+      # `while(...);` loop. I submitted a patch to move the `'` to its own
+      # line, but until that's landed suppress the warning:
+      "-Wno-empty-body",
+
+      # See http://crbug.com/138571#c8
+      "-Wno-ignored-attributes",
+    ]
+  }
+
+  includes = [
+    "$os_include",
+  ]
+}
diff --git a/tools/gn/secondary/third_party/mach_override/BUILD.gn b/tools/gn/secondary/third_party/mach_override/BUILD.gn
index 13317b1..1786c1d 100644
--- a/tools/gn/secondary/third_party/mach_override/BUILD.gn
+++ b/tools/gn/secondary/third_party/mach_override/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 static_library("mach_override") {
+  external = true
   sources = [
     "mach_override.c",
     "mach_override.h",
diff --git a/tools/gn/secondary/third_party/modp_b64/BUILD.gn b/tools/gn/secondary/third_party/modp_b64/BUILD.gn
index 539abe1..bfa7479 100644
--- a/tools/gn/secondary/third_party/modp_b64/BUILD.gn
+++ b/tools/gn/secondary/third_party/modp_b64/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 static_library("modp_b64") {
+  external = true
   sources = [
     "modp_b64.cc",
     "modp_b64.h",
diff --git a/tools/gn/secondary/third_party/wtl/BUILD.gn b/tools/gn/secondary/third_party/wtl/BUILD.gn
index 07c6ab2..a21ff7e 100644
--- a/tools/gn/secondary/third_party/wtl/BUILD.gn
+++ b/tools/gn/secondary/third_party/wtl/BUILD.gn
@@ -10,5 +10,6 @@
 # actually generate anything linkable, and inject the required config for
 # making the include directories work.
 group("wtl") {
+  external = true
   all_dependent_configs = ":wtl_includes"
 }
diff --git a/tools/gn/secondary/third_party/zlib/BUILD.gn b/tools/gn/secondary/third_party/zlib/BUILD.gn
index 7805328..749043c 100644
--- a/tools/gn/secondary/third_party/zlib/BUILD.gn
+++ b/tools/gn/secondary/third_party/zlib/BUILD.gn
@@ -6,7 +6,8 @@
   includes = [ "." ]
 }
 
-static_library("zlib") {
+static_library("chrome_zlib") {
+  external = true
   sources = [
     "adler32.c",
     "compress.c",
@@ -41,6 +42,7 @@
 }
 
 static_library("minizip") {
+  external = true
   sources = [
     "contrib/minizip/ioapi.c",
     "contrib/minizip/ioapi.h",
@@ -68,12 +70,13 @@
     cflags = [ "-Wno-parentheses-equality" ]
   }
 
-  deps = [ ":zlib" ]
+  deps = [ ":chrome_zlib" ]
 
   direct_dependent_configs = [ ":zlib_config" ]
 }
 
 static_library("zip") {
+  external = true
   sources = [
     "google/zip.cc",
     "google/zip.h",
diff --git a/tools/gn/secondary/tools/grit/grit_rule.gni b/tools/gn/secondary/tools/grit/grit_rule.gni
index 7b3ba48..5f68f97 100644
--- a/tools/gn/secondary/tools/grit/grit_rule.gni
+++ b/tools/gn/secondary/tools/grit/grit_rule.gni
@@ -22,7 +22,17 @@
     [ "--outputs", "$output_dir", source, "-f", resource_ids ],
     "list lines")
 
-  custom(target_name + "_grit") {
+  # The current grit setup makes an file in $target_gen_dir/grit/foo.h that
+  # the source code expects to include via "grit/foo.h". It would be nice to
+  # change this to including absolute paths relative to the root gen directory
+  # (like "mycomponent/foo.h"). This config sets up the include path.
+  grit_config = target_name + "_grit_config"
+  config(grit_config) {
+    includes = [ target_gen_dir ]
+  }
+
+  grit_custom_target = target_name + "_grit"
+  custom(grit_custom_target) {
     script = "$relative_source_root_dir/tools/grit/grit.py"
     data = grit_inputs  # TODO(brettw) this should be inputs or something
     outputs = grit_outputs
@@ -39,6 +49,7 @@
   # same as the argument the template was invoked with.
   static_library(target_name) {
     sources = grit_outputs
-    deps = [ ":${target_name}_grit" ]
+    deps = [ ":$grit_custom_target" ]
+    direct_dependent_configs = [ ":$grit_config" ]
   }
 }
diff --git a/tools/gn/secondary/url/BUILD.gn b/tools/gn/secondary/url/BUILD.gn
index b9d400a..4b6ee1d 100644
--- a/tools/gn/secondary/url/BUILD.gn
+++ b/tools/gn/secondary/url/BUILD.gn
@@ -6,6 +6,7 @@
 # for a Windows component build, and that will confuse Windows, which has a
 # system DLL with the same name.
 component("url_lib") {
+  external = true
   sources = [
     "gurl.cc",
     "gurl.h",
@@ -48,6 +49,7 @@
 }
 
 test("url_unittests") {
+  external = true
   sources = [
     "gurl_unittest.cc",
     "url_canon_unittest.cc",
@@ -59,8 +61,8 @@
   deps = [
     ":url_lib",
     "//base:base_i18n",
-    "//base/test:run_all_unittests",
-    "//testing/gtest",
+    "//base:run_all_unittests",
+    "//testing:gtest",
     "//third_party/icu:icuuc",
   ]
 }
diff --git a/tools/gn/setup.cc b/tools/gn/setup.cc
index 8fba875..69cbaa0 100644
--- a/tools/gn/setup.cc
+++ b/tools/gn/setup.cc
@@ -173,7 +173,7 @@
   // Save the result of the command args.
   Scope::KeyValueMap overrides;
   arg_scope.GetCurrentScopeValues(&overrides);
-  build_settings_.build_args().SwapInArgOverrides(&overrides);
+  build_settings_.build_args().AddArgOverrides(overrides);
   return true;
 }
 
diff --git a/tools/gn/target.cc b/tools/gn/target.cc
index 0d7a492..eabc532 100644
--- a/tools/gn/target.cc
+++ b/tools/gn/target.cc
@@ -63,6 +63,7 @@
     : Item(label),
       settings_(settings),
       output_type_(UNKNOWN),
+      external_(false),
       generated_(false),
       generator_function_(NULL) {
 }
diff --git a/tools/gn/target.h b/tools/gn/target.h
index 2e55805..fda4839 100644
--- a/tools/gn/target.h
+++ b/tools/gn/target.h
@@ -108,6 +108,9 @@
     forward_dependent_configs_.swap(*t);
   }
 
+  bool external() const { return external_; }
+  void set_external(bool e) { external_ = e; }
+
   const std::set<const Target*>& inherited_libraries() const {
     return inherited_libraries_;
   }
@@ -135,6 +138,8 @@
   std::vector<const Config*> direct_dependent_configs_;
   std::vector<const Target*> forward_dependent_configs_;
 
+  bool external_;
+
   // Libraries from transitive deps. Libraries need to be linked only
   // with the end target (executable, shared library). These do not get
   // pushed beyond shared library boundaries.
diff --git a/tools/gn/target_generator.cc b/tools/gn/target_generator.cc
index 5421d93..c8e9ff8 100644
--- a/tools/gn/target_generator.cc
+++ b/tools/gn/target_generator.cc
@@ -159,6 +159,15 @@
   FillForwardDependentConfigs();
 }
 
+void TargetGenerator::FillExternal() {
+  const Value* value = scope_->GetValue(variables::kExternal, true);
+  if (!value)
+    return;
+  if (!value->VerifyTypeIs(Value::BOOLEAN, err_))
+    return;
+  target_->set_external(value->boolean_value());
+}
+
 void TargetGenerator::SetToolchainDependency() {
   // TODO(brettw) currently we lock separately for each config, dep, and
   // toolchain we add which is bad! Do this in one lock.
diff --git a/tools/gn/target_generator.h b/tools/gn/target_generator.h
index 2b16ee0..be291d7 100644
--- a/tools/gn/target_generator.h
+++ b/tools/gn/target_generator.h
@@ -50,6 +50,7 @@
 
   void FillSources();
   void FillConfigs();
+  void FillExternal();
 
   // Sets the current toolchain as a dependecy of this target. All targets with
   // a dependency on the toolchain should call this function.
diff --git a/tools/gn/toolchain_manager.cc b/tools/gn/toolchain_manager.cc
index 3e84530..bd70baa 100644
--- a/tools/gn/toolchain_manager.cc
+++ b/tools/gn/toolchain_manager.cc
@@ -537,7 +537,7 @@
     }
 
     Scope our_scope(info->settings.base_config());
-    ScopePerFileProvider per_file_provider(&our_scope, file_name);
+    ScopePerFileProvider per_file_provider(&our_scope);
     our_scope.set_source_dir(file_name.GetDir());
 
     Err err;
diff --git a/tools/gn/variables.cc b/tools/gn/variables.cc
index f4d1732..8e8719e 100644
--- a/tools/gn/variables.cc
+++ b/tools/gn/variables.cc
@@ -115,6 +115,8 @@
     "\n"
     "  Generally scripts should use \"relative_target_output_dir\" instead.\n"
     "\n"
+    "  See also \"root_gen_dir\".\n"
+    "\n"
     "Example:\n"
     "\n"
     "  If your current build file is in \"//tools\", you might write\n"
@@ -154,7 +156,6 @@
     "    command = \"$relative_build_to_source_root_dir/third_party/gold/ld\n"
     "  }\n";
 
-
 const char kRelativeTargetGenDir[] = "relative_target_gen_dir";
 const char kRelativeTargetGenDir_HelpShort[] =
     "relative_target_gen_dir: [string] Relative dir for generated files.";
@@ -167,9 +168,11 @@
     "  Normally used when invoking scripts (the current directory of which is\n"
     "  that of the invoking buildfile) that need to write files.\n"
     "\n"
-    "  Scripts generating final rather than intermetiate files should use the\n"
+    "  Scripts generating final rather than intermediate files should use the\n"
     "  \"relative_target_output_dir\" instead.\n"
     "\n"
+    "  See also \"target_gen_dir\".\n"
+    "\n"
     "Example:\n"
     "\n"
     "  If your current build file is in \"//tools\", you might write\n"
@@ -195,6 +198,41 @@
     "  If your current build file is in \"//tools\", you might write\n"
     "  args = [ \"$relative_target_output_dir/final.lib\" ]\n";
 
+const char kRootGenDir[] = "root_gen_dir";
+const char kRootGenDir_HelpShort[] =
+    "root_gen_dir: [string] Absolute root dir for generated files.";
+const char kRootGenDir_Help[] =
+    "root_gen_dir: Absolute root dir for generated files.\n"
+    "\n"
+    "  Absolute path to the root of the generated output directory tree for\n"
+    "  the current toolchain. An example value might be \"//out/Debug/gen\".\n"
+    "  It will not have a trailing slash.\n"
+    "\n"
+    "  This is primarily useful for setting up include paths for generated\n"
+    "  files. Scripts will want the \"relative_root_gen_dir\" instead\n"
+    "  which will be relative to the scripts' current directory.\n"
+    "\n"
+    "  See also \"relative_root_gen_dir\" and \"target_gen_dir\".\n";
+
+const char kTargetGenDir[] = "target_gen_dir";
+const char kTargetGenDir_HelpShort[] =
+    "target_gen_dir: [string] Absolute dir for generated files.";
+const char kTargetGenDir_Help[] =
+    "target_gen_dir: Absolute dir for generated files.\n"
+    "\n"
+    "  Absolute path to the target's generated file directory. If your\n"
+    "  current target is in \"//tools/doom_melon\" then this value might be\n"
+    "  \"//out/Debug/gen/tools/doom_melon\". It will not have a trailing\n"
+    "  slash.\n"
+    "\n"
+    "  Scripts generating files will generally want the relative version of\n"
+    "  this instead: \"relative_target_gen_dir\".\n"
+    "\n"
+    "Example:\n"
+    "\n"
+    "  # Add the gen directory to the include path.\n"
+    "  args = [ target_gen_dir ]\n";
+
 // Target variables ------------------------------------------------------------
 
 const char kAllDependentConfigs[] = "all_dependent_configs";
@@ -355,6 +393,24 @@
     "\n"
     "  See also \"all_dependent_configs\".\n";
 
+const char kExternal[] = "external";
+const char kExternal_HelpShort[] =
+    "external: [boolean] Declares a target as externally generated.";
+const char kExternal_Help[] =
+    "external: Declares a target as externally generated.\n"
+    "\n"
+    "  External targets are treated like normal targets as far as dependent\n"
+    "  targets are concerned, but do not actually have their .ninja file\n"
+    "  written to disk. This allows them to be generated by an external\n"
+    "  program (e.g. GYP).\n"
+    "\n"
+    "  See also \"gn help gyp\".\n"
+    "\n"
+    "Example:\n"
+    "  static_library(\"foo\") {\n"
+    "    external = true\n"
+    "  }\n";
+
 const char kForwardDependentConfigsFrom[] = "forward_dependent_configs_from";
 const char kForwardDependentConfigsFrom_HelpShort[] =
     "forward_dependent_configs_from: [label list] Forward dependent's configs.";
@@ -444,6 +500,8 @@
     INSERT_VARIABLE(RelativeSourceRootDir)
     INSERT_VARIABLE(RelativeTargetGenDir)
     INSERT_VARIABLE(RelativeTargetOutputDir)
+    INSERT_VARIABLE(RootGenDir)
+    INSERT_VARIABLE(TargetGenDir)
   }
   return info_map;
 }
@@ -461,6 +519,7 @@
     INSERT_VARIABLE(Datadeps)
     INSERT_VARIABLE(Deps)
     INSERT_VARIABLE(DirectDependentConfigs)
+    INSERT_VARIABLE(External)
     INSERT_VARIABLE(ForwardDependentConfigsFrom)
     INSERT_VARIABLE(Ldflags)
     INSERT_VARIABLE(Sources)
diff --git a/tools/gn/variables.h b/tools/gn/variables.h
index c1d2a29..bddd32a 100644
--- a/tools/gn/variables.h
+++ b/tools/gn/variables.h
@@ -65,6 +65,14 @@
 extern const char kRelativeTargetOutputDir_HelpShort[];
 extern const char kRelativeTargetOutputDir_Help[];
 
+extern const char kRootGenDir[];
+extern const char kRootGenDir_HelpShort[];
+extern const char kRootGenDir_Help[];
+
+extern const char kTargetGenDir[];
+extern const char kTargetGenDir_HelpShort[];
+extern const char kTargetGenDir_Help[];
+
 // Target vars -----------------------------------------------------------------
 
 extern const char kAllDependentConfigs[];
@@ -111,6 +119,10 @@
 extern const char kDirectDependentConfigs_HelpShort[];
 extern const char kDirectDependentConfigs_Help[];
 
+extern const char kExternal[];
+extern const char kExternal_HelpShort[];
+extern const char kExternal_Help[];
+
 extern const char kForwardDependentConfigsFrom[];
 extern const char kForwardDependentConfigsFrom_HelpShort[];
 extern const char kForwardDependentConfigsFrom_Help[];
diff --git a/tools/heapcheck/suppressions.txt b/tools/heapcheck/suppressions.txt
index 96ad3f4..f0ceab2 100644
--- a/tools/heapcheck/suppressions.txt
+++ b/tools/heapcheck/suppressions.txt
@@ -831,12 +831,6 @@
    fun:message_center::MessageCenterViewTest::SetUp
 }
 {
-   bug_272083
-   Heapcheck:Leak
-   fun:ThemeService::SetManagedUserTheme
-   fun:ThemeService::OnManagedUserInitialized
-}
-{
    bug_272596
    Heapcheck:Leak
    fun:*
@@ -867,3 +861,27 @@
    fun:views::Widget::Init
    fun:BrowserFrame::InitBrowserFrame
 }
+{
+   bug_280192
+   Heapcheck:Leak
+   ...
+   fun:GtkThemeService::GtkThemeService
+   ...
+   fun:SavedFilesServiceUnitTest::SavedFilesServiceUnitTest
+}
+{
+   bug_280931
+   Heapcheck:Leak
+   ...
+   fun:net::QuicFramer::BuildDataPacket
+   fun:net::QuicPacketCreator::SerializePacket
+   fun:net::QuicPacketGenerator::SerializeAndSendPacket
+   fun:net::QuicPacketGenerator::SendQueuedFrames
+   fun:net::QuicPacketGenerator::AddControlFrame
+   fun:net::QuicConnection::SendRstStream
+   fun:net::QuicSession::SendRstStream
+   fun:net::QuicClientSession::SendRstStream
+   fun:net::ReliableQuicStream::Close
+   fun:net::QuicHttpStream::Close
+   fun:net::test::QuicStreamFactoryTest_MaxOpenStream_Test::TestBody
+}
diff --git a/tools/json_schema_compiler/model.py b/tools/json_schema_compiler/model.py
index 50a9c58..082fbc6 100644
--- a/tools/json_schema_compiler/model.py
+++ b/tools/json_schema_compiler/model.py
@@ -173,6 +173,7 @@
           for i, choice in enumerate(json['choices'])]
     elif json_type == 'object':
       if not (
+          'isInstanceOf' in json or
           'properties' in json or
           'additionalProperties' in json or
           'functions' in json or
diff --git a/tools/licenses.py b/tools/licenses.py
index d2c535b..be06282 100755
--- a/tools/licenses.py
+++ b/tools/licenses.py
@@ -56,6 +56,7 @@
 
     # Chromium code in third_party.
     os.path.join('third_party','fuzzymatch'),
+    os.path.join('tools', 'swarm_client'),
 
     # Stuff pulled in from chrome-internal for official builds/tools.
     os.path.join('third_party', 'clear_cache'),
diff --git a/tools/metrics/actions/chromeactions.txt b/tools/metrics/actions/chromeactions.txt
index 8f07473..da97179 100644
--- a/tools/metrics/actions/chromeactions.txt
+++ b/tools/metrics/actions/chromeactions.txt
@@ -148,6 +148,7 @@
 0x6ebaa5e3651107fa	BadMessageTerminate_EFD
 0xff06108fb2aa73fa	BadMessageTerminate_FAMF
 0xbecb3852be04c506	BadMessageTerminate_IDBMF
+0x9e69131e5123433a	BadMessageTerminate_MIDI
 0xd7e4d61883121c76	BadMessageTerminate_NC
 0x448f44d226b839b7	BadMessageTerminate_NC17
 0xd267646495d87640	BadMessageTerminate_RDH
@@ -317,6 +318,11 @@
 0x417f35273c2e009b	CriticalNotification_Restart
 0xfdf6ee64a0588855	Cryptohome.PKCS11InitFail
 0xeb334dca00e390e0	Cut
+0x24c15c9cd91f5be0	DataReductionProxy_PromoDisplayed
+0xdfc419c441193fae	DataReductionProxy_PromoLearnMore
+0x3e571d8a556acc27	DataReductionProxy_TurnedOff
+0xb69483d9936c050e	DataReductionProxy_TurnedOn
+0x92ebcc84f3abf206	DataReductionProxy_TurnedOnFromPromo
 0xb94d8a074eddd267	Debugger
 0xf2a6c498fb90ee34	Delete
 0xee9ce204f091eb10	DeleteBackward
diff --git a/tools/metrics/actions/extract_actions.py b/tools/metrics/actions/extract_actions.py
index f6819af..5e380e1 100755
--- a/tools/metrics/actions/extract_actions.py
+++ b/tools/metrics/actions/extract_actions.py
@@ -197,6 +197,11 @@
   Arguments
     actions: set of actions to add to.
   """
+  actions.add('DataReductionProxy_PromoDisplayed');
+  actions.add('DataReductionProxy_PromoLearnMore');
+  actions.add('DataReductionProxy_TurnedOn');
+  actions.add('DataReductionProxy_TurnedOnFromPromo');
+  actions.add('DataReductionProxy_TurnedOff');
   actions.add('MobileBeamCallbackSuccess')
   actions.add('MobileBeamInvalidAppState')
   actions.add('MobileBreakpadUploadAttempt')
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 05e686c..281252b 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -1349,6 +1349,432 @@
   </summary>
 </histogram>
 
+<histogram name="DataReductionProxy.PromoAction"
+    enum="DataReductionProxyPromoAction">
+  <summary>
+    Samples which method was used by the user to dismiss the proxy promo. This
+    is sampled when the promo leaves view, with the sampled value depending on
+    which of four possible controls the user used.
+  </summary>
+</histogram>
+
+<histogram name="DataReductionProxy.SettingsConversion"
+    enum="DataReductionProxySettingsConversion">
+  <summary>
+    Samples of user interactions with the ON/OFF switch in the settings menu for
+    reducing data usage. Only the setting changes between entering the reducing
+    data usage setting menu and leaving the menu will be sampled. So if a user
+    enters the menu with OFF and leaves it with OFF, it is counted as one OFF to
+    OFF conversion regardless of how many times he or she toggles the ON/OFF
+    switch.
+  </summary>
+</histogram>
+
+<histogram name="DataReductionProxy.StartupState"
+    enum="DataReductionProxyStartupState">
+  <summary>
+    Samples of the state of the data reduction proxy on Chrome startup. The
+    proxy will either be unavailable (the feature hasn't been rolled out to this
+    user yet), not enabled (the feature is available but the user doesn't have
+    it turned on), or enabled (the feature is enabled and turned on).
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.ConflictingDlls" enum="DiagnosticsResult">
+  <summary>
+    TBD - Not run automatically yet, so this is just a placeholder for future
+    metrics collection.  Any samples collected here represent users running
+    diagnostics manually.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.DiskSpace" enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the DiskSpace recovery step that runs
+    on recovery startups.  The recovery step attempts to guarantee the DiskSpace
+    test, which checks that the disk space in the volume where the user data
+    directory normally lives is not dangerously low, would pass on the next
+    startup.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.InstallType" enum="DiagnosticsResult">
+  <summary>
+    TBD - Not run automatically yet, so this is just a placeholder for future
+    metrics collection.  Any samples collected here represent users running
+    diagnostics manually.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.JSONBookmarks" enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the JSONBookmarks recovery step that
+    runs on recovery startups. The recovery step attempts to guarantee the
+    JSONBookmarks test, which makes sure that the JSON-encoded Bookmarks file is
+    properly formed, would pass on the next startup.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.JSONLocalState" enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the JSONLocalState recovery step that
+    runs on recovery startups. The recovery step attempts to guarantee the
+    JSONLocalState test, which makes sure that the JSON-encoded Local State file
+    is properly formed, would pass on the next startup.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.JSONPreferences" enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the JSONPreferences recovery step
+    that runs on recovery startups. The recovery step attempts to guarantee the
+    JSONPreferences test, which makes sure that the JSON-encoded Preferences
+    file is properly formed, would pass on the next startup.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.OperatingSystem" enum="DiagnosticsResult">
+  <summary>
+    TBD - Not run automatically yet, so this is just a placeholder for future
+    metrics collection.  Any samples collected here represent users running
+    diagnostics manually.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.PathDictionaries"
+    enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the PathDictionaries recovery step
+    that runs on recovery startups. The recovery step attempts to guarantee the
+    PathDictionaries test, which makes sure that the path to the Dictionaries
+    directory exists and has the right permissions, would pass on the next
+    startup.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.PathLocalState" enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the PathLocalState recovery step that
+    runs on recovery startups. The recovery step attempts to guarantee the
+    PathLocalState test, which makes sure that the path to the Local State file
+    exists and has the right permissions, would pass on the next startup.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.PathResources" enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the PathResources recovery step that
+    runs on recovery startups. The recovery step attempts to guarantee the
+    PathResources test, which makes sure that the path to the Resources
+    directory exists and has the right permissions, would pass on the next
+    startup.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.PathUserData" enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the PathUserData recovery step that
+    runs on recovery startups. The recovery step attempts to guarantee the
+    PathUserData test, which makes sure that the path to the User Data directory
+    exists and has the right permissions, would pass on the next startup.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.SQLiteIntegrityAppCache"
+    enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the SQLiteIntegrityAppCache recovery
+    step that runs on recovery startups.  The recovery step attempts to
+    guarantee the SQLiteIntegrityAppCache test, which checks the integrity of
+    the App Cache database, would pass on the next startup.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.SQLiteIntegrityArchivedHistory"
+    enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the SQLiteIntegrityArchivedHistory
+    recovery step that runs on recovery startups.  The recovery step attempts to
+    guarantee the SQLiteIntegrityArchivedHistory test, which checks the
+    integrity of the Archived History database, would pass on the next startup.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.SQLiteIntegrityCookie"
+    enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the SQLiteIntegrityCookie recovery
+    step that runs on recovery startups.  The recovery step attempts to
+    guarantee the SQLiteIntegrityCookie test, which checks the integrity of the
+    Cookie database, would pass on the next startup.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.SQLiteIntegrityDatabaseTracker"
+    enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the SQLiteIntegrityDatabaseTracker
+    recovery step that runs on recovery startups.  The recovery step attempts to
+    guarantee the SQLiteIntegrityDatabaseTracker test, which checks the
+    integrity of the Database Tracker database, would pass on the next startup.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.SQLiteIntegrityHistory"
+    enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the SQLiteIntegrityHistory recovery
+    step that runs on recovery startups.  The recovery step attempts to
+    guarantee the SQLiteIntegrityHistory test, which checks the integrity of the
+    History database, would pass on the next startup.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.SQLiteIntegrityNSSCert"
+    enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the SQLiteIntegrityNSSCert recovery
+    step that runs on recovery startups.  The recovery step attempts to
+    guarantee the SQLiteIntegrityNSSCert test, which checks the integrity of the
+    NSS Certificate database, would pass on the next startup.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.SQLiteIntegrityNSSKey"
+    enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the SQLiteIntegrityNSSKey recovery
+    step that runs on recovery startups.  The recovery step attempts to
+    guarantee the SQLiteIntegrityNSSKey test, which checks the integrity of the
+    NSS Key database, would pass on the next startup.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.SQLiteIntegrityThumbnails"
+    enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the SQLiteIntegrityThumbnails
+    recovery step that runs on recovery startups.  The recovery step attempts to
+    guarantee the SQLiteIntegrityThumbnails test, which checks the integrity of
+    the Thumbnails database, would pass on the next startup.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.SQLiteIntegrityWebData"
+    enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the SQLiteIntegrityWebData recovery
+    step that runs on recovery startups.  The recovery step attempts to
+    guarantee the SQLiteIntegrityWebData test, which checks the integrity of the
+    Web Data database, would pass on the next startup.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Recovery.Version" enum="DiagnosticsResult">
+  <summary>
+    TBD - Not run automatically yet, so this is just a placeholder for future
+    metrics collection.  Any samples collected here represent users running
+    diagnostics manually.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.RecoveryRun" enum="DiagnosticsRecoveryRun">
+  <summary>
+    Count of the number of times diagnostics recovery is invoked or not, and how
+    it was invoked.  A sample is added to this histogram once for each startup
+    of Chrome.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.ConflictingDlls" enum="DiagnosticsResult">
+  <summary>
+    TBD - Not run automatically yet, so this is just a placeholder for future
+    metrics collection.  Any samples collected here represent users running
+    diagnostics manually.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.DiskSpace" enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of diagnostics for the DiskSpace test
+    that runs on recovery startups.  The DiskSpace test checks that the disk
+    space in the volume where the user data directory normally lives is not
+    dangerously low.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.InstallType" enum="DiagnosticsResult">
+  <summary>
+    TBD - Not run automatically yet, so this is just a placeholder for future
+    metrics collection.  Any samples collected here represent users running
+    diagnostics manually.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.JSONBookmarks" enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of diagnostics for the JSONBookmarks
+    test that runs on recovery startups.  The JSONBookmarks test checks to make
+    sure that the JSON encoded bookmarks file is properly formed.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.JSONLocalState" enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of diagnostics for the JSONLocalState
+    test that runs on recovery startups.  The JSONLocalState test checks to make
+    sure that the JSON encoded Local State file is properly formed.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.JSONPreferences" enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of diagnostics for the JSONPreferences
+    test that runs on recovery startups.  The JSONPreferences test checks to
+    make sure that the Preferences file is properly formed.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.OperatingSystem" enum="DiagnosticsResult">
+  <summary>
+    TBD - Not run automatically yet, so this is just a placeholder for future
+    metrics collection.  Any samples collected here represent users running
+    diagnostics manually.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.PathDictionaries" enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of diagnostics for the PathDictionaries
+    test that runs on recovery startups.  The PathDictionaries test checks makes
+    sure that the path to the Dictionaries folder exists and has the right
+    permissions.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.PathLocalState" enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of diagnostics for the PathLocalState
+    test that runs on recovery startups.   The PathLocalState test checks makes
+    sure that the path to the Local State folder exists and has the right
+    permissions.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.PathResources" enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of diagnostics for the PathResources
+    test that runs on recovery startups.  The PathResources test checks makes
+    sure that the path to the Resources folder exists and has the right
+    permissions.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.PathUserData" enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of diagnostics for the PathUserData test
+    that runs on recovery startups. The PathUserData test checks makes sure that
+    the path to the User Data folder exists and has the right permissions.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.SQLiteIntegrityAppCache"
+    enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the SQLiteIntegrityAppCache test that
+    runs on recovery startups.  The test checks the integrity of the App Cache
+    database.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.SQLiteIntegrityArchivedHistory"
+    enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the SQLiteIntegrityArchivedHistory
+    test that runs on recovery startups.  The test checks the integrity of the
+    Archived History database.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.SQLiteIntegrityCookie"
+    enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the SQLiteIntegrityCookie test that
+    runs on recovery startups.  The test checks the integrity of the Cookie
+    database.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.SQLiteIntegrityDatabaseTracker"
+    enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the SQLiteIntegrityDatabaseTracker
+    test that runs on recovery startups.  The test checks the integrity of the
+    Database Tracker database.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.SQLiteIntegrityHistory"
+    enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the SQLiteIntegrityHistory test that
+    runs on recovery startups.  The test checks the integrity of the History
+    database.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.SQLiteIntegrityNSSCert"
+    enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the SQLiteIntegrityNSSCert test that
+    runs on recovery startups.  The test checks the integrity of the NSS
+    Certificate database.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.SQLiteIntegrityNSSKey"
+    enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the SQLiteIntegrityNSSKey test that
+    runs on recovery startups.  The test checks the integrity of the NSS Key
+    database.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.SQLiteIntegrityThumbnails"
+    enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the SQLiteIntegrityThumbnails test
+    that runs on recovery startups.  The test checks the integrity of the
+    Thumbnails database.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.SQLiteIntegrityWebData"
+    enum="DiagnosticsResult">
+  <summary>
+    Shows the success and failure rates of the SQLiteIntegrityWebData test that
+    runs on recovery startups.  The test checks the integrity of the Web Data
+    database.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.Test.Version" enum="DiagnosticsResult">
+  <summary>
+    TBD - Not run automatically yet, so this is just a placeholder for future
+    metrics collection.  Any samples collected here represent users running
+    diagnostics manually.
+  </summary>
+</histogram>
+
+<histogram name="Diagnostics.TestFailures" enum="DiagnosticsTestName">
+  <summary>
+    Histogram comparing the various types of diagnostic test failures when
+    diagnostic tests are run.  Note that some types of test failures cause the
+    rest of the tests to be skipped.
+  </summary>
+</histogram>
+
 <histogram name="DisabledExtension.ExtensionWipedStatus" enum="BooleanWiped">
   <summary>Whether an extension has been wiped out.</summary>
 </histogram>
@@ -4092,6 +4518,14 @@
   <summary>Events in Google Now component extension.</summary>
 </histogram>
 
+<histogram name="History.DeleteFTSIndexDatabases">
+  <summary>
+    Count of &quot;History Index *&quot; databases deleted.  These databases
+    stored the full-text-search data for history, which was removed at r213442,
+    this histogram tracks cleanup.
+  </summary>
+</histogram>
+
 <histogram name="History.TopSitesVisitsByRank" units="rank">
   <summary>
     Page visits to each of a user's top 50 sites. Visits to all other sites go
@@ -4208,6 +4642,13 @@
   </summary>
 </histogram>
 
+<histogram name="Installer.AttemptsCount.Total" units="count">
+  <summary>
+    The number of update attempts until the update has been applied. This is
+    reported every time the device has completed an update.
+  </summary>
+</histogram>
+
 <histogram name="Installer.DevModeErrorCodes" enum="UpdateEngineErrorCode">
   <summary>Errors from update_engine process when running in dev mode.</summary>
 </histogram>
@@ -4228,18 +4669,61 @@
   </summary>
 </histogram>
 
+<histogram name="Installer.FullPayloadAttemptNumber" units="count">
+  <summary>
+    The number of update attempts with a full update payload until the update
+    has been applied. This is reported on every update attempt.
+  </summary>
+</histogram>
+
 <histogram name="Installer.NormalErrorCodes" enum="UpdateEngineErrorCode">
   <summary>
     Errors from update_engine process when running in normal mode.
   </summary>
 </histogram>
 
+<histogram name="Installer.PayloadAttemptNumber" units="count">
+  <summary>
+    The number of update attempts until the update has been applied. This is
+    reported on every update attempt.
+  </summary>
+</histogram>
+
+<histogram name="Installer.PayloadFormat" enum="UpdateEnginePayloadFormat">
+  <summary>
+    The type of update payload used to update the device. The difference between
+    &quot;Full&quot; and &quot;Forced Full&quot; is that in the latter, the
+    request sent to Omaha included a directive saying that a delta payload
+    wasn't accepted. A &quot;Full&quot; payload is one where a delta payload was
+    accepted but Omaha provided a full payload. This is reported every time the
+    device has completed an update.
+  </summary>
+</histogram>
+
+<histogram name="Installer.RebootToNewPartitionAttempt" units="count">
+  <summary>
+    The number of consecutive times a device has failed to boot an update that
+    successfully applied. This metric is reported every time the firmware fails
+    to boot the slot with the update and fell back to the slot it originally
+    updated from.
+  </summary>
+</histogram>
+
 <histogram name="Installer.SuccessfulMBsDownloadedFrom" units="MB">
   <summary>
     Number of MBs downloaded from during an update that completed successfully.
   </summary>
 </histogram>
 
+<histogram name="Installer.TimeToRebootMinutes" units="Minutes">
+  <summary>
+    Wall-clock duration between when an update has successfully completed (and
+    the user is presented with the &quot;reboot arrow&quot;) and when the system
+    has booted into the new update. This is reported every time the device is
+    rebooted after an update has been applied.
+  </summary>
+</histogram>
+
 <histogram name="Installer.TotalMBsDownloadedFrom" units="MB">
   <summary>
     Total number of MBs downloaded since the last successful update. This also
@@ -4249,9 +4733,9 @@
 
 <histogram name="Installer.UpdateDurationMinutes" units="Minutes">
   <summary>
-    Absolute wallclock time duration it took for the update to complete from the
-    time an update first began.  It includes not just the time the device was
-    up, but also includes the time the device spent sleeping.
+    Absolute wall-clock time duration it took for the update to complete from
+    the time an update first began.  It includes not just the time the device
+    was up, but also includes the time the device spent sleeping.
   </summary>
 </histogram>
 
@@ -4271,6 +4755,22 @@
   </summary>
 </histogram>
 
+<histogram name="Installer.UpdatesAbandonedCount" units="count">
+  <summary>
+    The number of update attempts that didn't complete because a newer update
+    was detected during the update operation. This is reported every time the
+    device has completed an update.
+  </summary>
+</histogram>
+
+<histogram name="Installer.UpdatesAbandonedEventCount" units="count">
+  <summary>
+    The number of consecutive different abandoned update payloads since the last
+    successful update. This is reported every time an update payload is
+    abandoned because a newer update payload is available.
+  </summary>
+</histogram>
+
 <histogram name="Installer.UpdateURLSwitches" units="count">
   <summary>
     Number of times the download URLs were switched due to failures.
@@ -7150,6 +7650,10 @@
   </summary>
 </histogram>
 
+<histogram name="Net.QuicSession.ConnectionClose.NumOpenStreams.TimedOut">
+  <summary>The number of streams open when a QUIC session timed out.</summary>
+</histogram>
+
 <histogram name="Net.QuicSession.ConnectionCloseErrorCode"
     enum="QuicErrorCodes">
   <summary>
@@ -9650,6 +10154,126 @@
   </summary>
 </histogram>
 
+<histogram name="P2P.Client.Canceled.WaitingTimeSeconds" units="seconds">
+  <summary>
+    The wall-clock time spent until a lookup was canceled.  This is reported
+    every time p2p is used to find a candidate but the request was canceled.
+  </summary>
+</histogram>
+
+<histogram name="P2P.Client.Found.CandidateCount" units="count">
+  <summary>
+    The number of candidates on the LAN, i.e. the number of peers on the LAN
+    offering at least N bytes of the requested file X. This is reported after
+    examining responses from all peers on the LAN and picking a candidate.
+  </summary>
+</histogram>
+
+<histogram name="P2P.Client.Found.ConnectionCount" units="count">
+  <summary>
+    The number of p2p downloads of the peer that the returned URL points to.
+    This is reported after examining responses from all peers on the LAN and
+    picking a candidate.
+  </summary>
+</histogram>
+
+<histogram name="P2P.Client.Found.WaitingTimeSeconds" units="seconds">
+  <summary>
+    The wall-clock time spent waiting for the LAN-wide number of p2p downloads
+    (i.e. the sum of p2p downloads from each peer on the LAN) to drop below the
+    threshold.  This is reported after examining responses from all peers on the
+    LAN and picking a candidate.
+  </summary>
+</histogram>
+
+<histogram name="P2P.Client.LookupResult" enum="P2PLookupResult">
+  <summary>
+    The result of the lookup. Possible values include &quot;Found&quot; (if a
+    candidate - i.e. a peer offering at least N bytes of file X - was chosen),
+    &quot;Not Found&quot; (if no candidate could be found), &quot;Vanished&quot;
+    (if a candidate was found but vanished while waiting in line),
+    &quot;Canceled&quot; (if a candidate was found but the request was canceled
+    while waiting in line), and &quot;Filtered&quot; (if it was detected that
+    mDNS was filtered). This is reported after examining responses from all
+    peers on the LAN when p2p is used to find a candidate.
+  </summary>
+</histogram>
+
+<histogram name="P2P.Client.NumPeers" units="count">
+  <summary>
+    The number of peers implementing p2p file sharing on the network. This is
+    reported every time p2p is used to look up a resource on a network where
+    mDNS is not filtered.
+  </summary>
+</histogram>
+
+<histogram name="P2P.Client.Vanished.WaitingTimeSeconds" units="seconds">
+  <summary>
+    The wall-clock time spent waiting for one or more candidates (i.e. peers
+    offering at least N bytes of file X) that all vanished before the LAN-wide
+    number of p2p downloads dropped below the threshold. This is reported every
+    time candidates were found using p2p but then vanished.
+  </summary>
+</histogram>
+
+<histogram name="P2P.Server.ClientCount" units="count">
+  <summary>
+    The number of currently connected HTTP clients. This is reported every time
+    a HTTP client connects.
+  </summary>
+</histogram>
+
+<histogram name="P2P.Server.ContentServedInterruptedMB" units="MB">
+  <summary>
+    Number of megabytes (1,000,000 bytes) served from the device (via HTTP)
+    where the client disconnects prematurely. This is reported every time a file
+    is served and the client disconnects before receiving all data.
+  </summary>
+</histogram>
+
+<histogram name="P2P.Server.ContentServedSuccessfullyMB" units="MB">
+  <summary>
+    Number of megabytes (1,000,000 bytes) served from the device (via HTTP).
+    This is reported every time a file have been served successfully.
+  </summary>
+</histogram>
+
+<histogram name="P2P.Server.DownloadSpeedKBps" units="kB/s">
+  <summary>
+    The average speed at which the download was served at, in kB/s. This is
+    reported every time a file have been served successfully.
+  </summary>
+</histogram>
+
+<histogram name="P2P.Server.FileCount" units="count">
+  <summary>
+    The number of files available via p2p. This is reported every time a file is
+    added or removed to the /var/cache/p2p directory.
+  </summary>
+</histogram>
+
+<histogram name="P2P.Server.RangeBeginPercentage" units="%">
+  <summary>
+    When a client resumes a download, the HTTP request includes range specifier
+    to skip the bytes it already has. This metric conveys this as a percentage
+    of the the file size.  This is reported every time a file is served, even if
+    the request does not include a range specifier (in which case 0 is
+    reported).
+  </summary>
+</histogram>
+
+<histogram name="P2P.Server.RequestResult" enum="P2PServerResult">
+  <summary>
+    The result of the HTTP request. Possible values include &quot;Response
+    Sent&quot; (the resource was found and the response was successfully sent),
+    &quot;Response Interrupted&quot; (the resource was found but the client
+    disconnected), &quot;Malformed&quot; (the request was malformed), &quot;Not
+    Found&quot; (the request was for a resource that was not found), and
+    &quot;Index&quot; (the request was for the '/' or '/index.html' resource).
+    This is reported for every HTTP request handled.
+  </summary>
+</histogram>
+
 <histogram name="PasswordManager.OtherPossibleUsernamesUsage"
     enum="OtherPossibleUsernamesUsage">
   <summary>
@@ -11890,6 +12514,13 @@
   </summary>
 </histogram>
 
+<histogram name="Quickoffice.xlsFormattedCellCount">
+  <summary>
+    Records the number of cells that contain formatting data in the default
+    worksheet when a compound binary format spreadsheet is opened.
+  </summary>
+</histogram>
+
 <histogram name="Quickoffice.xlsNonEmptyCellCount">
   <summary>
     Records the number of non-empty cells in the default worksheet when a
@@ -11904,6 +12535,13 @@
   </summary>
 </histogram>
 
+<histogram name="Quickoffice.xlsxFormattedCellCount">
+  <summary>
+    Records the number of cells that contain formatting data in the default
+    worksheet when an OOXML format spreadsheet is opened.
+  </summary>
+</histogram>
+
 <histogram name="Quickoffice.xlsxNonEmptyCellCount">
   <summary>
     Records the number of non-empty cells when an OOXML format spreadsheet is
@@ -12268,6 +12906,15 @@
   <summary>Time to capture a renderer snapshot.</summary>
 </histogram>
 
+<histogram name="Renderer4.SoftwareCompositorThreadImplDrawDelay"
+    units="milliseconds">
+  <summary>
+    Time between frames when the software renderer is being used, as measured on
+    the compositor thread. This is collected once per frame while it is being
+    drawn to the screen in the compositor.
+  </summary>
+</histogram>
+
 <histogram name="Renderer4.SoftwareDoDeferredUpdateDelay">
   <summary>Time between frames when the page is not GPU accelerated.</summary>
 </histogram>
@@ -13987,6 +14634,14 @@
   <summary>The outcome of Entry::WriteData in the simple cache.</summary>
 </histogram>
 
+<histogram name="SiteIsolation.AllResponses">
+  <summary>
+    The count of all network responses received by a renderer. Each response is
+    corresponding to one URL requested by a renderer. Incremented when the first
+    network packet of a response of this type is received.
+  </summary>
+</histogram>
+
 <histogram name="SiteIsolation.BrowsingInstanceCount">
   <summary>
     The count of all current BrowsingInstances.  Recorded once per UMA ping.
@@ -14059,6 +14714,332 @@
   </summary>
 </histogram>
 
+<histogram name="SiteIsolation.XSD.DataLength" units="byte">
+  <summary>
+    The number of bytes in the first network packet for a response with headers
+    that imply potential illegal cross-site access. Recorded when the first
+    network packet of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.HTML.Blocked">
+  <summary>
+    The count of blocked cross-site document responses due to having HTML
+    content type header and contents sniffed as HTML. Sampled with value of 1
+    when the first network packet of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.HTML.Blocked.NonRenderableStatusCode">
+  <summary>
+    The count of responses with a nonrenderable HTTP status code among blocked
+    cross-site document responses due to their HTML contents. Sampled with value
+    1 when the first network packet of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.HTML.Blocked.RenderableStatusCode"
+    enum="SiteIsolationResourceType">
+  <summary>
+    The count of responses with a renderable HTTP status code sub-categorized by
+    their requesting context type (e.g., image, script, etc.) among blocked
+    cross-site document responses due to their HTML contents. Sampled with a
+    resource type (0-14) when the first network packet of a response of this
+    type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.HTML.NoSniffBlocked.NonRenderableStatusCode">
+  <summary>
+    The count of responses with a nonrenderable HTTP status code among blocked
+    cross-site document responses due to having HTML content type and nosniff
+    headers. Sampled with value 1 when the first network packet of a response of
+    this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.HTML.NoSniffBlocked.RenderableStatusCode"
+    enum="SiteIsolationResourceType">
+  <summary>
+    The count of responses with a renderable HTTP status code sub-categorized by
+    their requesting context type (e.g., image, script, etc.), among blocked
+    cross-site document responses due to having HTML content type and nosniff
+    headers. Sampled with a resource type (0-14) when the first network packet
+    of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.HTML.NotBlocked">
+  <summary>
+    The count of not blocked responses despite having an HTML content type
+    header due to the failure of content sniffing. Sampled with value 1 when the
+    first network packet of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.HTML.NotBlocked.MaybeJS">
+  <summary>
+    The count of responses that may be parsed as JavaScript among not blocked
+    responses. Sampled with value 1 when the first network packet of a response
+    of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.JSON.Blocked">
+  <summary>
+    The count of blocked cross-site document responses due to having JSON
+    content type header and contents sniffed as JSON. Sampled with value 1 when
+    the first network packet of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.JSON.Blocked.NonRenderableStatusCode">
+  <summary>
+    The count of responses with a nonrenderable HTTP status code among blocked
+    cross-site document responses due to their JSON contents. Sampled with value
+    1 when the first network packet of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.JSON.Blocked.RenderableStatusCode"
+    enum="SiteIsolationResourceType">
+  <summary>
+    The count of responses with a renderable HTTP status code sub-categorized by
+    their requesting context type (e.g., image, script, etc.), among blocked
+    cross-site document responses due to their JSON contents. Sampled with a
+    resource type (0-14) when the first network packet of a response of this
+    type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.JSON.NoSniffBlocked.NonRenderableStatusCode">
+  <summary>
+    The count of responses with a nonrenderable HTTP status code among blocked
+    cross-site document responses due to having JSON content type and nosniff
+    headers. Sampled with value 1 when the first network packet of a response of
+    this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.JSON.NoSniffBlocked.RenderableStatusCode"
+    enum="SiteIsolationResourceType">
+  <summary>
+    The count of responses with a renderable HTTP status code sub-categorized by
+    their requesting context type (e.g., image, script, etc.), among blocked
+    cross-site document responses due to having JSON content type and nosniff
+    headers. Sampled with a resource type (0-14) when the first network packet
+    of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.JSON.NotBlocked">
+  <summary>
+    The count of not blocked responses despite having an JSON content type
+    header due to the failure of content sniffing. Sampled with value 1 when the
+    first network packet of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.JSON.NotBlocked.MaybeJS">
+  <summary>
+    The count of responses that may be parsed as JavaScript among not blocked
+    responses with a JSON content type header. Sampled with value 1 when the
+    first network packet of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.MimeType" enum="SiteIsolationMimeType">
+  <summary>
+    MIME type codes for content type header values of potentially cross-site
+    document responses, excluding same-site or not http(s) urls. Sampled with a
+    MIME type code (0-4) when the first network packet of a response of this
+    type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.Plain.HTML.Blocked">
+  <summary>
+    The count of blocked cross-site document responses due to having Plain
+    content type header and contents sniffed as HTML. Sampled with value 1 when
+    the first network packet of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.Plain.HTML.Blocked.NonRenderableStatusCode">
+  <summary>
+    The count of responses with a nonrenderable HTTP status code among blocked
+    responses due to their Plain.HTML contents. Sampled with value 1 when the
+    first network packet of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.Plain.HTML.Blocked.RenderableStatusCode"
+    enum="SiteIsolationResourceType">
+  <summary>
+    The count of responses with a renderable HTTP status code sub-categorized by
+    their requesting context type (e.g., image, script, etc.), among blocked
+    cross-site document responses due to their Plain.HTML contents. Sampled with
+    a resource type (0-14) when the first network packet of a response of this
+    type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.Plain.JSON.Blocked">
+  <summary>
+    The count of blocked cross-site document responses due to having Plain
+    content type header and contents sniffed as JSON. Sampled with value 1 when
+    the first network packet of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.Plain.JSON.Blocked.NonRenderableStatusCode">
+  <summary>
+    The count of responses with a nonrenderable HTTP status code among blocked
+    cross-site document responses due to their Plain.JSON contents. Sampled with
+    value 1 when the first network packet of a response of this type is
+    received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.Plain.JSON.Blocked.RenderableStatusCode"
+    enum="SiteIsolationResourceType">
+  <summary>
+    The count of responses with a renderable HTTP status code sub-categorized by
+    their requesting context type (e.g., image, script, etc.), among blocked
+    cross-site document responses due to their Plain.JSON contents. Sampled with
+    a resource type (0-14) when the first network packet of a response of this
+    type is received.
+  </summary>
+</histogram>
+
+<histogram
+    name="SiteIsolation.XSD.Plain.NoSniffBlocked.NonRenderableStatusCode">
+  <summary>
+    The count of responses with a nonrenderable HTTP status code among blocked
+    cross-site document responses due to having Plain content type and nosniff
+    headers. Sampled with value 1 when the first network packet of a response of
+    this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.Plain.NoSniffBlocked.RenderableStatusCode"
+    enum="SiteIsolationResourceType">
+  <summary>
+    The count of responses with a renderable HTTP status code sub-categorized by
+    their requesting context type (e.g., image, script, etc.), among blocked
+    cross-site document responses due to having Plain content type and nosniff
+    header. Sampled with a resource type (0-14) when the first network packet of
+    a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.Plain.NotBlocked">
+  <summary>
+    The count of not blocked responses despite having an Plain content type
+    header due to the failure of content sniffing. Sampled with value 1 when the
+    first network packet of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.Plain.NotBlocked.MaybeJS">
+  <summary>
+    The count of responses that may be parsed as JavaScript among not blocked
+    responses with a Plain content type header. Sampled with value 1 when the
+    first network packet of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.Plain.XML.Blocked">
+  <summary>
+    The count of blocked cross-site document responses due to having Plain
+    content type header and contents sniffed as XML. Sampled with value 1 when
+    the first network packet of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.Plain.XML.Blocked.NonRenderableStatusCode">
+  <summary>
+    The count of responses with a nonrenderable HTTP status code among blocked
+    cross-site document responses due to their Plain.XML contents. Sampled with
+    value 1 when the first network packet of a response of this type is
+    received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.Plain.XML.Blocked.RenderableStatusCode"
+    enum="SiteIsolationResourceType">
+  <summary>
+    The count of responses with renderable HTTP status codes sub-categorized by
+    their requesting context type (e.g., image, script, etc.), among blocked
+    cross-site document responses due to their Plain.XML contents. Sampled with
+    a resource type (0-14) when the first network packet of a response of this
+    type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.XML.Blocked">
+  <summary>
+    The count of blocked cross-site document responses due to having XML content
+    type header and contents sniffed as XML. Sampled with value 1 when the first
+    network packet of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.XML.Blocked.NonRenderableStatusCode">
+  <summary>
+    The count of responses with nonrenderable HTTP status codes among blocked
+    cross-site document responses due to their XML contents. Sampled with value
+    1 when the first network packet of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.XML.Blocked.RenderableStatusCode"
+    enum="SiteIsolationResourceType">
+  <summary>
+    The count of responses with renderable HTTP status codes sub-categorized by
+    their requesting context type (e.g., image, script, etc.), among blocked
+    cross-site document responses due to their XML contents. Sampled with a
+    resource type (0-14) when the first network packet of a response of this
+    type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.XML.NoSniffBlocked.NonRenderableStatusCode">
+  <summary>
+    The count of responses with a nonrenderable HTTP status code among blocked
+    cross-site document responses due to having XML content type and nosniff
+    headers. Sampled with value 1 when the first network packet of a response of
+    this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.XML.NoSniffBlocked.RenderableStatusCode"
+    enum="SiteIsolationResourceType">
+  <summary>
+    The count of responses with a renderable HTTP status code sub-categorized by
+    their requesting context type (e.g., image, script, etc.), among blocked
+    cross-site document responses due to having XML content type and nosniff
+    headers. Sampled with a resource type (0-14) when the first network packet
+    of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.XML.NotBlocked">
+  <summary>
+    The count of not blocked responses despite having an XML content type header
+    due to the failure of content sniffing. Sampled with value 1 when the first
+    network packet of a response of this type is received.
+  </summary>
+</histogram>
+
+<histogram name="SiteIsolation.XSD.XML.NotBlocked.MaybeJS">
+  <summary>
+    The count of responses that may be parsed as JavaScript among not blocked
+    responses with an XML content type. Sampled with value 1 when the first
+    network packet of a response of this type is received.
+  </summary>
+</histogram>
+
 <histogram name="Sqlite.AppCache.Error" enum="SqliteErrorCode">
   <summary>Error codes returned by sqlite for the appcache db.</summary>
 </histogram>
@@ -14861,6 +15842,76 @@
   </summary>
 </histogram>
 
+<histogram name="Uptime.ChromeExecToLoginPromptVisibleAfterLogout" units="ms">
+  <summary>
+    Measures the time elapsed on Chrome OS between when Chrome is started, and
+    when the login prompt is again visible after a logout.  This statistic is
+    only collected when preceeded by a logout.
+  </summary>
+</histogram>
+
+<histogram name="Uptime.LoginPromptSetupTimeAfterLogout" units="ms">
+  <summary>
+    Measures the time elapsed on Chrome OS for setting up for a login after a
+    logout. More specifically, it is the time between when the Cryptohome is
+    unmounted (the last step in the logout process) and when the login prompt is
+    again visible after a logout.
+  </summary>
+</histogram>
+
+<histogram name="Uptime.Logout" units="ms">
+  <summary>
+    Measures the time elapsed on Chrome OS when performing a logout. More
+    specifically, it is the time between when a logout is initiated and when the
+    Cryptohome is unmounted, signaling the last step in the logout process. This
+    statistic is not collected when the logout is part of a restart or shutdown.
+  </summary>
+</histogram>
+
+<histogram name="Uptime.LogoutToLoginPromptVisible" units="ms">
+  <summary>
+    Measures the time elapsed on Chrome OS between initiating a logout and the
+    next time the login prompt is visible again.  This statistic is not
+    collected if the machine is shutdown between the logout initiation and the
+    prompt becoming visible.
+  </summary>
+</histogram>
+
+<histogram name="Uptime.LogoutToUIStopAfterLogout" units="ms">
+  <summary>
+    Measures the time elapsed on Chrome OS between when a logout is initiated
+    and the UI has stopped (and Chrome has exited) during the logout process.
+    This statistic is not collected if the logout is part of a restart or
+    shutdown.
+  </summary>
+</histogram>
+
+<histogram name="Uptime.ProcessesTerminatedToXTerminatedAfterLogout" units="ms">
+  <summary>
+    Measures the time elapsed on Chrome OS between when all user-associated
+    processes (including the X server) have been terminated during the logout
+    process.  This statistic is not collected if the logout is part of a restart
+    or shutdown.
+  </summary>
+</histogram>
+
+<histogram name="Uptime.UIStopToProcessesTerminatedAfterLogout" units="ms">
+  <summary>
+    Measures the time elapsed on Chrome OS between when the UI has stopped
+    (Chrome has exited), and when all other associated processes have been
+    terminated during the logout process. This statistic is not collected if the
+    logout is part of a restart or shutdown.
+  </summary>
+</histogram>
+
+<histogram name="Uptime.XTerminatedToChromeExecAfterLogout" units="ms">
+  <summary>
+    Measures the time elapsed on Chrome OS between when the X server has been
+    terminated from a previous logout and when Chrome is started again to show
+    the login screen.
+  </summary>
+</histogram>
+
 <histogram name="UserManager.LoginUserType" enum="UserType">
   <summary>
     The number of users of different types that log in to the system (Chrome
@@ -16456,6 +17507,26 @@
   <int value="8" label="PlayMusic"/>
 </enum>
 
+<enum name="DataReductionProxyPromoAction" type="int">
+  <int value="0" label="Dismissed from first screen"/>
+  <int value="1" label="Dismissed from second screen"/>
+  <int value="2" label="Enabled from first screen"/>
+  <int value="3" label="Enabled from second screen"/>
+</enum>
+
+<enum name="DataReductionProxySettingsConversion" type="int">
+  <int value="0" label="OFF to OFF"/>
+  <int value="1" label="OFF to ON"/>
+  <int value="2" label="ON to OFF"/>
+  <int value="3" label="ON to ON"/>
+</enum>
+
+<enum name="DataReductionProxyStartupState" type="int">
+  <int value="0" label="Proxy not available"/>
+  <int value="1" label="Proxy available but not enabled"/>
+  <int value="2" label="Proxy available and enabled"/>
+</enum>
+
 <enum name="DevicePermissionActions" type="int">
   <int value="0" label="AllowHttps"/>
   <int value="1" label="AllowHttp"/>
@@ -16463,6 +17534,43 @@
   <int value="3" label="Cancel"/>
 </enum>
 
+<enum name="DiagnosticsRecoveryRun" type="int">
+  <int value="0" label="Recovery not run"/>
+  <int value="1" label="Recovery run because of crash"/>
+  <int value="2" label="Recovery run by user"/>
+</enum>
+
+<enum name="DiagnosticsResult" type="int">
+  <int value="0" label="Not run (regular startup)"/>
+  <int value="1" label="Success (crash startup)"/>
+  <int value="2" label="Failure (crash startup)"/>
+  <int value="3" label="Skipped (crash startup)"/>
+</enum>
+
+<enum name="DiagnosticsTestName" type="int">
+  <int value="0" label="Conflicting DLLs Test"/>
+  <int value="1" label="Disk Space Test"/>
+  <int value="2" label="Install Type Test"/>
+  <int value="3" label="JSON Bookmarks Test"/>
+  <int value="4" label="JSON Local State Test"/>
+  <int value="5" label="JSON Preferences Test"/>
+  <int value="6" label="Operating System Test"/>
+  <int value="7" label="Path Dictionaries Test"/>
+  <int value="8" label="Path Local State Test"/>
+  <int value="9" label="Path Resources Test"/>
+  <int value="10" label="Path User Data Test"/>
+  <int value="11" label="Version Test"/>
+  <int value="12" label="SQLite Integrity App Cache Test"/>
+  <int value="13" label="SQLite Integrity Archived History Test"/>
+  <int value="14" label="SQLite Integrity Cookie Test"/>
+  <int value="15" label="SQLite Integrity Database Tracker Test"/>
+  <int value="16" label="SQLite Integrity History Test"/>
+  <int value="17" label="SQLite Integrity Nss Cert Test"/>
+  <int value="18" label="SQLite Integrity Nss Key Test"/>
+  <int value="19" label="SQLite Integrity Thumbnails Test"/>
+  <int value="20" label="SQLite Integrity Web Data Test"/>
+</enum>
+
 <enum name="DNSEmptyAddressListAndNoError" type="int">
   <int value="0" label="Error reported or Address List is not empty"/>
   <int value="1" label="Success reported but Address List is empty"/>
@@ -17640,7 +18748,7 @@
   <int value="306" label="TYPES_CHROMESETTING_SET"/>
   <int value="307" label="BROWSERACTION_SETICON"/>
   <int value="308" label="EXPERIMENTAL_ACCESSIBILITY_SETACCESSIBILITYENABLED"/>
-  <int value="309" label="FILEBROWSERPRIVATE_VIEWFILES"/>
+  <int value="309" label="DELETED_FILEBROWSERPRIVATE_VIEWFILES"/>
   <int value="310" label="BLUETOOTH_GETSERVICES"/>
   <int value="311" label="TABS_UPDATE"/>
   <int value="312" label="BROWSINGDATA_REMOVEFORMDATA"/>
@@ -18120,6 +19228,11 @@
   <int value="120" label="NodeNamespaceURI"/>
   <int value="121" label="NodePrefix"/>
   <int value="122" label="NodeLocalName"/>
+  <int value="123" label="NavigatorProductSub"/>
+  <int value="124" label="NavigatorVendor"/>
+  <int value="125" label="NavigatorVendorSub"/>
+  <int value="126" label="FileError"/>
+  <int value="127" label="DocumentCharset"/>
 </enum>
 
 <enum name="FFmpegCodecs" type="int">
@@ -20807,6 +21920,22 @@
   <int value="4" label="East">Scrolled from left towards right</int>
 </enum>
 
+<enum name="P2PLookupResult" type="int">
+  <int value="0" label="Found"/>
+  <int value="1" label="Not Found"/>
+  <int value="2" label="Vanished"/>
+  <int value="3" label="Canceled"/>
+  <int value="4" label="Filtered"/>
+</enum>
+
+<enum name="P2PServerResult" type="int">
+  <int value="0" label="Response Sent"/>
+  <int value="1" label="Response Interrupted"/>
+  <int value="2" label="Malformed"/>
+  <int value="3" label="Not Found"/>
+  <int value="4" label="Index"/>
+</enum>
+
 <enum name="ParsedCookieStatus" type="int">
   <int value="0" label="All cookie values valid and without control chars"/>
   <int value="1" label="Cookie contains control chars"/>
@@ -21860,6 +22989,32 @@
   <int value="3" label="Fresh index with cache updated since backend start"/>
 </enum>
 
+<enum name="SiteIsolationMimeType" type="int">
+  <int value="0" label="HTML"/>
+  <int value="1" label="XML"/>
+  <int value="2" label="JSON"/>
+  <int value="3" label="Plain"/>
+  <int value="4" label="Others"/>
+</enum>
+
+<enum name="SiteIsolationResourceType" type="int">
+  <int value="0" label="MAIN_FRAME"/>
+  <int value="1" label="SUB_FRAME"/>
+  <int value="2" label="STYLESHEET"/>
+  <int value="3" label="SCRIPT"/>
+  <int value="4" label="IMAGE"/>
+  <int value="5" label="FONT_RESOURCE"/>
+  <int value="6" label="SUB_RESOURCE"/>
+  <int value="7" label="OBJECT"/>
+  <int value="8" label="MEDIA"/>
+  <int value="9" label="WORKER"/>
+  <int value="10" label="SHARED_WORKER"/>
+  <int value="11" label="PREFETCH"/>
+  <int value="12" label="FAVICON"/>
+  <int value="13" label="XHR"/>
+  <int value="14" label="LAST_TYPE"/>
+</enum>
+
 <enum name="SocketStreamConnectionType" type="int">
   <int value="0" label="none"/>
   <int value="1" label="all connections"/>
@@ -22517,6 +23672,7 @@
   <int value="5" label="Reloaded due to incognito"/>
   <int value="6" label="Reloaded due to cold start (fg tab on start)"/>
   <int value="7" label="Reloaded due to cold start (bg tab on switch)"/>
+  <int value="8" label="Lazy load for 'Open in new tab'"/>
 </enum>
 
 <enum name="TabSwitchedToForegroundLaunchedWithURL" type="int">
@@ -22736,6 +23892,12 @@
   <int value="42" label="kErrorCodeUpdateCanceledByChannelChange"/>
 </enum>
 
+<enum name="UpdateEnginePayloadFormat" type="int">
+  <int value="0" label="Full"/>
+  <int value="1" label="Delta"/>
+  <int value="2" label="Forced Full"/>
+</enum>
+
 <enum name="UrlResolutionResult" type="int">
   <int value="0" label="Absolute URL"/>
   <int value="1" label="Resolutions Differ"/>
@@ -25041,7 +26203,7 @@
   <group name="Predictor" label="Predictor"/>
   <group name="Quota" label="Quota"/>
   <group name="SyncDirectory" label="SyncDirectory"/>
-  <group name="Text" label="Text"/>
+  <group name="Text" label="Text (obsolete 7/24/13)"/>
   <group name="Thumbnail" label="Thumbnail"/>
   <group name="TopSites" label="TopSites"/>
   <group name="Web" label="Web"/>
diff --git a/tools/perf/benchmarks/endure.py b/tools/perf/benchmarks/endure.py
new file mode 100644
index 0000000..1d52247
--- /dev/null
+++ b/tools/perf/benchmarks/endure.py
@@ -0,0 +1,63 @@
+# 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.
+
+from telemetry import test
+
+from measurements import endure
+
+
+class EndureCalendarForwardBackward(test.Test):
+  test = endure.Endure
+  page_set = 'page_sets/calendar_forward_backward.json'
+  options = {
+      'output_format': 'csv',
+      'skip_navigate_on_repeat': True,
+      'page_repeat_secs': 3600,
+      'perf_stats_interval': 30
+  }
+
+
+class EndureGmailAltThreadlistConversation(test.Test):
+  test = endure.Endure
+  page_set = 'page_sets/gmail_alt_threadlist_conversation.json'
+  options = {
+      'output_format': 'csv',
+      'skip_navigate_on_repeat': True,
+      'page_repeat_secs': 3600,
+      'perf_stats_interval': 30
+  }
+
+
+class EndureGmailAltTwoLabels(test.Test):
+  test = endure.Endure
+  page_set = 'page_sets/gmail_alt_two_labels.json'
+  options = {
+      'output_format': 'csv',
+      'skip_navigate_on_repeat': True,
+      'page_repeat_secs': 3600,
+      'perf_stats_interval': 30
+  }
+
+
+class EndureGmailExpandCollapseConversation(test.Test):
+  test = endure.Endure
+  page_set = 'page_sets/gmail_expand_collapse_conversation.json'
+  options = {
+      'output_format': 'csv',
+      'skip_navigate_on_repeat': True,
+      'page_repeat_secs': 3600,
+      'perf_stats_interval': 30
+  }
+
+
+class EndurePlusAltPostsPhotos(test.Test):
+  test = endure.Endure
+  page_set = 'page_sets/plus_alt_posts_photos.json'
+  options = {
+      'output_format': 'csv',
+      'skip_navigate_on_repeat': True,
+      'page_repeat_secs': 3600,
+      'perf_stats_interval': 30
+  }
+
diff --git a/tools/perf/generate_profile b/tools/perf/generate_profile
new file mode 100755
index 0000000..312c8f3
--- /dev/null
+++ b/tools/perf/generate_profile
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import sys
+
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, 'telemetry'))
+
+from telemetry.page import profile_generator
+
+if __name__ == '__main__':
+  sys.exit(profile_generator.Main())
diff --git a/tools/perf/measurements/page_cycler.py b/tools/perf/measurements/page_cycler.py
index e373eea..9e18706 100644
--- a/tools/perf/measurements/page_cycler.py
+++ b/tools/perf/measurements/page_cycler.py
@@ -15,6 +15,7 @@
 cycling all pages.
 """
 
+import collections
 import os
 import sys
 
@@ -32,6 +33,9 @@
       self._page_cycler_js = f.read()
 
     self._memory_metric = None
+    self._number_warm_runs = None
+    self._cold_runs_requested = False
+    self._has_loaded_page = collections.defaultdict(int)
 
   def AddCommandLineOptions(self, parser):
     # The page cyclers should default to 10 iterations. In order to change the
@@ -42,6 +46,9 @@
     parser.remove_option('--pageset-repeat')
     parser.add_option(pageset_repeat_option)
 
+    parser.add_option('--cold-load-percent', type='int', default=0,
+                      help='%d of page visits for which a cold load is forced')
+
   def DidStartBrowser(self, browser):
     """Initialize metrics once right after the browser has been launched."""
     self._memory_metric = memory.MemoryMetric(browser)
@@ -53,6 +60,8 @@
 
   def WillNavigateToPage(self, page, tab):
     page.script_to_evaluate_on_commit = self._page_cycler_js
+    if self.ShouldRunCold(page.url):
+      tab.ClearCache()
 
   def DidNavigateToPage(self, page, tab):
     self._memory_metric.Start(page, tab)
@@ -62,18 +71,53 @@
     io.IOMetric.CustomizeBrowserOptions(options)
     options.AppendExtraBrowserArg('--js-flags=--expose_gc')
 
-    # Temporarily disable typical_25 page set on mac.
-    if sys.platform == 'darwin' and sys.argv[-1].endswith('/typical_25.json'):
-      print 'typical_25 is currently disabled on mac. Skipping test.'
+    # A disk cache bug causes some page cyclers to hang on mac.
+    # TODO(tonyg): Re-enable these tests when crbug.com/268646 is fixed.
+    if (sys.platform == 'darwin' and
+        (sys.argv[-1].endswith('/intl_hi_ru.json') or
+         sys.argv[-1].endswith('/tough_layout_cases.json') or
+         sys.argv[-1].endswith('/typical_25.json'))):
+      print '%s is currently disabled on mac. Skipping test.' % sys.argv[-1]
       sys.exit(0)
 
+    # Handle requests for cold cache runs
+    if (options.cold_load_percent and
+        (options.repeat_options.page_repeat_secs or
+         options.repeat_options.pageset_repeat_secs)):
+      raise Exception('--cold-load-percent is incompatible with timed repeat')
+
+    if (options.cold_load_percent and
+        (options.cold_load_percent < 0 or options.cold_load_percent > 100)):
+      raise Exception('--cold-load-percent must be in the range [0-100]')
+
+    # TODO(rdsmith): Properly handle interaction of page_repeat with
+    # dropping the first run.
+    number_warm_pageset_runs = int(
+        (int(options.repeat_options.pageset_repeat_iters) - 1) *
+        (100 - options.cold_load_percent) / 100)
+
+    # Make sure _number_cold_runs is an integer multiple of page_repeat.
+    # Without this, --pageset_shuffle + --page_repeat could lead to
+    # assertion failures on _started_warm in WillNavigateToPage.
+    self._number_warm_runs = (number_warm_pageset_runs *
+                              options.repeat_options.page_repeat_iters)
+    self._cold_runs_requested = bool(options.cold_load_percent)
+    self.discard_first_result = (bool(options.cold_load_percent) or
+                                 self.discard_first_result)
+
   def MeasurePage(self, page, tab, results):
     def _IsDone():
       return bool(tab.EvaluateJavaScript('__pc_load_time'))
     util.WaitFor(_IsDone, 60)
+    chart_name = ('times' if not self._cold_runs_requested else
+                  'cold_times' if self.ShouldRunCold(page.url) else
+                  'warm_times')
+
     results.Add('page_load_time', 'ms',
                 int(float(tab.EvaluateJavaScript('__pc_load_time'))),
-                chart_name='times')
+                chart_name=chart_name)
+
+    self._has_loaded_page[page.url] += 1
 
     self._memory_metric.Stop(page, tab)
     self._memory_metric.AddResults(tab, results)
@@ -82,3 +126,15 @@
     self._memory_metric.AddSummaryResults(results)
     io.IOMetric().AddSummaryResults(tab, results)
 
+  def ShouldRunCold(self, url):
+    # We do the warm runs first for two reasons.  The first is so we can
+    # preserve any initial profile cache for as long as possible.
+    # The second is that, if we did cold runs first, we'd have a transition
+    # page set during which we wanted the run for each URL to both
+    # contribute to the cold data and warm the catch for the following
+    # warm run, and clearing the cache before the load of the following
+    # URL would eliminate the intended warmup for the previous URL.
+    return self._has_loaded_page[url] >= self._number_warm_runs + 1
+
+  def results_are_the_same_on_every_page(self):
+    return not self._cold_runs_requested
diff --git a/tools/perf/measurements/page_cycler_unittest.py b/tools/perf/measurements/page_cycler_unittest.py
new file mode 100644
index 0000000..db8c198
--- /dev/null
+++ b/tools/perf/measurements/page_cycler_unittest.py
@@ -0,0 +1,133 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import unittest
+
+from measurements import page_cycler
+from telemetry.core import browser_options
+from telemetry.page import page_measurement_results
+from telemetry.unittest import simple_mock
+
+# Allow testing protected members in the unit test.
+# pylint: disable=W0212
+
+# Used instead of simple_mock.MockObject so that the precise order and
+# number of calls need not be specified.
+class MockMemoryMetric(object):
+  def __init__(self):
+    pass
+
+  def Start(self, page, tab):
+    pass
+
+  def Stop(self, page, tab):
+    pass
+
+  def AddResults(self, tab, results):
+    pass
+
+  def AddSummaryResults(self, tab, results):
+    pass
+
+class PageCyclerUnitTest(unittest.TestCase):
+
+  # TODO(tonyg): Remove this backfill when we can assume python 2.7 everywhere.
+  def assertIn(self, first, second, msg=None):
+    self.assertTrue(first in second,
+                    msg="'%s' not found in '%s'" % (first, second))
+
+  def setupCycler(self, args):
+    cycler = page_cycler.PageCycler()
+    options = browser_options.BrowserFinderOptions()
+    parser = options.CreateParser()
+    cycler.AddCommandLineOptions(parser)
+    parser.parse_args(args)
+    cycler.CustomizeBrowserOptions(options)
+
+    return cycler
+
+  def testOptionsColdLoadNoArgs(self):
+    cycler = self.setupCycler([])
+
+    self.assertFalse(cycler._cold_runs_requested)
+    self.assertEqual(cycler._number_warm_runs, 9)
+
+  def testOptionsColdLoadPagesetRepeat(self):
+    cycler = self.setupCycler(['--pageset-repeat=20', '--page-repeat=2'])
+
+    self.assertFalse(cycler._cold_runs_requested)
+    self.assertEqual(cycler._number_warm_runs, 38)
+
+  def testOptionsColdLoadRequested(self):
+    cycler = self.setupCycler(['--pageset-repeat=21', '--page-repeat=2',
+                               '--cold-load-percent=40'])
+
+    self.assertTrue(cycler._cold_runs_requested)
+    self.assertEqual(cycler._number_warm_runs, 24)
+
+  def testIncompatibleOptions(self):
+    exception_seen = False
+    try:
+      self.setupCycler(['--pageset-repeat=20s', '--page-repeat=2s',
+                        '--cold-load-percent=40'])
+    except Exception as e:
+      exception_seen = True
+      self.assertEqual('--cold-load-percent is incompatible with '
+                       'timed repeat', e.args[0])
+
+    self.assertTrue(exception_seen)
+
+  def testCacheHandled(self):
+    cycler = self.setupCycler(['--pageset-repeat=5',
+                               '--cold-load-percent=50'])
+
+    # Mock out memory metrics; the real ones require a real browser.
+    mock_memory_metric = MockMemoryMetric()
+
+    mock_memory_module = simple_mock.MockObject()
+    mock_memory_module.ExpectCall(
+      'MemoryMetric').WithArgs(simple_mock.DONT_CARE).WillReturn(
+      mock_memory_metric)
+
+    real_memory_module = page_cycler.memory
+    try:
+      page_cycler.memory = mock_memory_module
+      cycler.DidStartBrowser(None)
+    finally:
+      page_cycler.memory = real_memory_module
+
+    class FakePage(object):
+      def __init__(self, url):
+        self.url = url
+
+    class FakeTab(object):
+      def __init__(self):
+        self.clear_cache_calls = 0
+      def ClearCache(self):
+        self.clear_cache_calls += 1
+      def EvaluateJavaScript(self, _):
+        return 1
+
+    url_name = "http://fakepage.com"
+    page = FakePage(url_name)
+    tab = FakeTab()
+    results = page_measurement_results.PageMeasurementResults()
+
+    for i in range(5):
+      cycler.WillNavigateToPage(page, tab)
+      self.assertEqual(max(0, i - 2), tab.clear_cache_calls,
+                       "Iteration %d tab.clear_cache_calls %d" %
+                       (i, tab.clear_cache_calls))
+      num_results = len(results.page_results)
+      results.WillMeasurePage(page)
+      cycler.MeasurePage(page, tab, results)
+      results.DidMeasurePage()
+      self.assertEqual(num_results+1, len(results.page_results))
+      result = results[-1]
+      self.assertEqual(result.page, page)
+      self.assertEqual(1, len(result.values))
+      self.assertEqual(result.values[0].trace_name, 'page_load_time')
+      self.assertEqual(result.values[0].units, 'ms')
+      self.assertEqual(result.values[0].chart_name,
+                       'warm_times' if i < 3 else 'cold_times')
+      cycler.DidNavigateToPage(page, tab)
diff --git a/tools/perf/measurements/smoothness.py b/tools/perf/measurements/smoothness.py
index a00c17d..47a7802 100644
--- a/tools/perf/measurements/smoothness.py
+++ b/tools/perf/measurements/smoothness.py
@@ -35,6 +35,7 @@
     return hasattr(page, 'smoothness')
 
   def WillRunAction(self, page, tab, action):
+    tab.browser.StartTracing('webkit,cc,benchmark', 60)
     if tab.browser.platform.IsRawDisplayFrameRateSupported():
       tab.browser.platform.StartRawDisplayFrameRateMeasurement()
     self._metrics = smoothness.SmoothnessMetrics(tab)
@@ -48,6 +49,16 @@
       tab.browser.platform.StopRawDisplayFrameRateMeasurement()
     if not action.CanBeBound():
       self._metrics.Stop()
+    tab.browser.StopTracing()
+
+  def FindTimelineMarker(self, timeline):
+    events = [s for
+              s in timeline.GetAllEventsOfName(
+                  smoothness.TIMELINE_MARKER)
+              if s.parent_slice == None]
+    if len(events) != 1:
+      raise LookupError, 'timeline marker not found'
+    return events[0]
 
   def MeasurePage(self, page, tab, results):
     rendering_stats_deltas = self._metrics.deltas
@@ -59,7 +70,11 @@
 
     smoothness.CalcFirstPaintTimeResults(results, tab)
 
-    benchmark_stats = GpuRenderingStats(rendering_stats_deltas)
+    timeline = tab.browser.GetTraceResultAndReset().AsTimelineModel()
+    timeline_marker = self.FindTimelineMarker(timeline)
+    benchmark_stats = GpuRenderingStats(timeline_marker,
+                                        rendering_stats_deltas,
+                                        self._metrics.is_using_gpu_benchmarking)
     smoothness.CalcResults(benchmark_stats, results)
 
     if self.options.report_all_results:
diff --git a/tools/perf/measurements/tab_switching.py b/tools/perf/measurements/tab_switching.py
index 90beb13..b12a174 100644
--- a/tools/perf/measurements/tab_switching.py
+++ b/tools/perf/measurements/tab_switching.py
@@ -36,7 +36,7 @@
   def MeasurePage(self, _, tab, results):
     """Although this is called MeasurePage, we're actually using this function
     to cycle through each tab that was opened via DidNavigateToPage and
-    thenrecord a single histogram for the tab switching metric.
+    then record a single histogram for the tab switching metric.
     """
     histogram_name = 'MPArch.RWH_TabSwitchPaintDuration'
     histogram_type = histogram_util.BROWSER_HISTOGRAM
diff --git a/tools/perf/metrics/gpu_rendering_stats.py b/tools/perf/metrics/gpu_rendering_stats.py
index 9af9f9e..12ac37d 100644
--- a/tools/perf/metrics/gpu_rendering_stats.py
+++ b/tools/perf/metrics/gpu_rendering_stats.py
@@ -3,40 +3,78 @@
 # found in the LICENSE file.
 
 class GpuRenderingStats(object):
-  def __init__(self, rendering_stats_deltas):
-    rs = rendering_stats_deltas
+  def __init__(self, timeline_marker, rendering_stats_deltas,
+               used_gpu_benchmarking):
+    """
+    Utility class for extracting rendering statistics from the timeline (or
+    other loggin facilities), and providing them in a common format to classes
+    that compute benchmark metrics from this data.
 
-    # Scroll Stats
-    self.total_time = rs.get('totalTimeInSeconds', 0)
+    Stats can either be numbers, or lists of numbers. Classes that calculate
+    metrics from the stats must be able to handle both cases. The length of
+    different list stats may vary.
+
+    All *_time values are measured in seconds.
+    """
+    self.renderer_process = timeline_marker.start_thread.parent
+    self.start = timeline_marker.start
+    self.end = self.start + timeline_marker.duration
+
+    self.total_time = (self.end - self.start) / 1000.0
+    self.animation_frame_count = []
+    self.screen_frame_count = []
+    self.paint_time = []
+    self.record_time = []
+    self.commit_time = []
+    self.commit_count = []
+    self.painted_pixel_count = []
+    self.recorded_pixel_count = []
+    self.image_gathering_count = []
+    self.image_gathering_time = []
+    self.dropped_frame_count = []
+    self.rasterize_time = []
+    self.rasterize_time_for_now_bins_on_pending_tree = []
+    self.best_rasterize_time = []
+    self.rasterized_pixel_count = []
+    self.impl_thread_scroll_count = []
+    self.main_thread_scroll_count = []
+    self.drawn_layer_count = []
+    self.missing_tile_count = []
+    self.deferred_image_decode_count = []
+    self.deferred_image_cache_hit_count = []
+    self.tile_analysis_count = []
+    self.solid_color_tile_analysis_count = []
+    self.deferred_image_decode_time = []
+    self.tile_analysis_time = []
+    self.texture_upload_count = []
+    self.texture_upload_time = []
+    self.input_event_count = []
+    self.input_event_latency = []
+    self.touch_ui_count = []
+    self.touch_ui_latency = []
+    self.touch_acked_count = []
+    self.touch_acked_latency = []
+    self.scroll_update_count = []
+    self.scroll_update_latency = []
+
+    if used_gpu_benchmarking:
+      self.initMainThreadStatsFromTimeline()
+      self.initImplThreadStatsFromTimeline()
+    else:
+      self.initFrameCountsFromRenderingStats(rendering_stats_deltas)
+    self.initTextureStatsFromRenderingStats(rendering_stats_deltas)
+    self.initLatencyStatsFromRenderingStats(rendering_stats_deltas)
+
+  def initFrameCountsFromRenderingStats(self, rs):
+    self.animation_frame_count = rs.get('numAnimationFrames', 0)
     self.screen_frame_count = rs.get('numFramesSentToScreen', 0)
     self.dropped_frame_count = rs.get('droppedFrameCount', 0)
-    self.impl_thread_scroll_count = rs.get('numImplThreadScrolls', 0)
-    self.main_thread_scroll_count = rs.get('numMainThreadScrolls', 0)
-    self.drawn_layers_count = rs.get('numLayersDrawn', 0)
-    self.missing_tile_count = rs.get('numMissingTiles', 0)
 
-    # Texture Upload Stats
+  def initTextureStatsFromRenderingStats(self, rs):
     self.texture_upload_count = rs.get('textureUploadCount', 0)
     self.texture_upload_time = rs.get('totalTextureUploadTimeInSeconds', 0)
-    self.commit_count = rs.get('totalCommitCount', 0)
-    self.commit_time = rs.get('totalCommitTimeInSeconds', 0)
 
-    # Image Decoding Stats
-    self.deferred_image_decode_count = rs.get(
-        'totalDeferredImageDecodeCount', 0)
-    self.deferred_image_decode_time = rs.get(
-        'totalDeferredImageDecodeTimeInSeconds', 0)
-    self.deferred_image_cache_hits = rs.get(
-        'totalDeferredImageCacheHitCount', 0)
-    self.image_gathering_count = rs.get('totalImageGatheringCount', 0)
-    self.image_gathering_time = rs.get('totalImageGatheringTimeInSeconds', 0)
-
-    # Tile Analysis Stats
-    self.tile_analysis_count = rs.get('totalTilesAnalyzed', 0)
-    self.tile_analysis_time = rs.get('totalTileAnalysisTimeInSeconds', 0)
-    self.solid_color_tile_analysis_count = rs.get('solidColorTilesAnalyzed', 0)
-
-    # Latency Stats
+  def initLatencyStatsFromRenderingStats(self, rs):
     self.input_event_count = rs.get('inputEventCount', 0)
     self.input_event_latency = rs.get('totalInputLatency', 0)
     self.touch_ui_count = rs.get('touchUICount', 0)
@@ -45,3 +83,69 @@
     self.touch_acked_latency = rs.get('totalTouchAckedLatency', 0)
     self.scroll_update_count = rs.get('scrollUpdateCount', 0)
     self.scroll_update_latency = rs.get('totalScrollUpdateLatency', 0)
+
+  def initMainThreadStatsFromTimeline(self):
+    for event in self.renderer_process.IterAllSlicesOfName(
+        'MainThreadRenderingStats::IssueTraceEvent'):
+      if event.start >= self.start and event.end <= self.end:
+        if 'data' not in event.args:
+          continue
+        self.animation_frame_count.append(
+            event.args['data']['animation_frame_count'])
+        self.screen_frame_count.append(
+            event.args['data']['screen_frame_count'])
+        self.paint_time.append(
+            event.args['data']['paint_time'])
+        self.record_time.append(
+            event.args['data']['record_time'])
+        self.commit_time.append(
+            event.args['data']['commit_time'])
+        self.commit_count.append(
+            event.args['data']['commit_count'])
+        self.painted_pixel_count.append(
+            event.args['data']['painted_pixel_count'])
+        self.recorded_pixel_count.append(
+            event.args['data']['recorded_pixel_count'])
+        self.image_gathering_count.append(
+            event.args['data']['image_gathering_count'])
+        self.image_gathering_time.append(
+            event.args['data']['image_gathering_time'])
+
+  def initImplThreadStatsFromTimeline(self):
+    for event in self.renderer_process.IterAllSlicesOfName(
+        'ImplThreadRenderingStats::IssueTraceEvent'):
+      if event.start >= self.start and event.end <= self.end:
+        if 'data' not in event.args:
+          continue
+        self.screen_frame_count.append(
+            event.args['data']['screen_frame_count'])
+        self.dropped_frame_count.append(
+            event.args['data']['dropped_frame_count'])
+        self.rasterize_time.append(
+            event.args['data']['rasterize_time'])
+        self.rasterize_time_for_now_bins_on_pending_tree.append(
+            event.args['data']['rasterize_time_for_now_bins_on_pending_tree'])
+        self.best_rasterize_time.append(
+            event.args['data']['best_rasterize_time'])
+        self.rasterized_pixel_count.append(
+            event.args['data']['rasterized_pixel_count'])
+        self.impl_thread_scroll_count.append(
+            event.args['data']['impl_thread_scroll_count'])
+        self.main_thread_scroll_count.append(
+            event.args['data']['main_thread_scroll_count'])
+        self.drawn_layer_count.append(
+            event.args['data']['drawn_layer_count'])
+        self.missing_tile_count.append(
+            event.args['data']['missing_tile_count'])
+        self.deferred_image_decode_count.append(
+            event.args['data']['deferred_image_decode_count'])
+        self.deferred_image_cache_hit_count.append(
+            event.args['data']['deferred_image_cache_hit_count'])
+        self.tile_analysis_count.append(
+            event.args['data']['tile_analysis_count'])
+        self.solid_color_tile_analysis_count.append(
+            event.args['data']['solid_color_tile_analysis_count'])
+        self.deferred_image_decode_time.append(
+            event.args['data']['deferred_image_decode_time'])
+        self.tile_analysis_time.append(
+            event.args['data']['tile_analysis_time'])
diff --git a/tools/perf/metrics/smoothness.js b/tools/perf/metrics/smoothness.js
index 833ac06..e563dcb 100644
--- a/tools/perf/metrics/smoothness.js
+++ b/tools/perf/metrics/smoothness.js
@@ -81,6 +81,10 @@
     return stats;
   };
 
+  GpuBenchmarkingRenderingStats.prototype.isUsingGpuBenchmarking = function() {
+    return true;
+  }
+
   GpuBenchmarkingRenderingStats.prototype.getRenderingStats_ = function() {
     var stats = chrome.gpuBenchmarking.renderingStats();
     stats.totalTimeInSeconds = getTimeMs() / 1000;
@@ -131,6 +135,10 @@
     return endValues;
   };
 
+  RafRenderingStats.prototype.isUsingGpuBenchmarking = function() {
+    return false;
+  }
+
   RafRenderingStats.prototype.recordFrameTime_ = function(timestamp) {
     if (!this.recording_)
       return;
diff --git a/tools/perf/metrics/smoothness.py b/tools/perf/metrics/smoothness.py
index aeee61e..cfb19b0 100644
--- a/tools/perf/metrics/smoothness.py
+++ b/tools/perf/metrics/smoothness.py
@@ -5,6 +5,8 @@
 
 from telemetry.core import util
 
+TIMELINE_MARKER = 'smoothness_scroll'
+
 class SmoothnessMetrics(object):
   def __init__(self, tab):
     self._tab = tab
@@ -36,9 +38,17 @@
     # Make the scroll test start and stop measurement automatically.
     self._tab.ExecuteJavaScript(
         'window.__renderingStats = new __RenderingStats();')
-    action.BindMeasurementJavaScript(self._tab,
-                                     'window.__renderingStats.start();',
-                                     'window.__renderingStats.stop();')
+    action.BindMeasurementJavaScript(
+        self._tab,
+        'window.__renderingStats.start(); ' +
+        'console.time("' + TIMELINE_MARKER + '")',
+        'window.__renderingStats.stop(); ' +
+        'console.timeEnd("' + TIMELINE_MARKER + '")')
+
+  @property
+  def is_using_gpu_benchmarking(self):
+    return self._tab.EvaluateJavaScript(
+      'window.__renderingStats.isUsingGpuBenchmarking()')
 
   @property
   def start_values(self):
@@ -55,10 +65,23 @@
     return self._tab.EvaluateJavaScript(
       'window.__renderingStats.getDeltas()')
 
+def Total(data):
+  if type(data) == float:
+    total = data
+  elif type(data) == int:
+    total = float(data)
+  elif type(data) == list:
+    total = float(sum(data))
+  else:
+    raise TypeError
+  return total
+
 def Average(numerator, denominator, scale = None, precision = None):
-  if denominator == 0:
+  numerator_total = Total(numerator)
+  denominator_total = Total(denominator)
+  if denominator_total == 0:
     return 0
-  avg = float(numerator) / float(denominator)
+  avg = numerator_total / denominator_total
   if scale:
     avg *= scale
   if precision:
@@ -101,7 +124,7 @@
                       100, 1),
               data_type='unimportant')
   results.Add('average_num_layers_drawn', '',
-              Average(s.drawn_layers_count, s.screen_frame_count, 1, 1),
+              Average(s.drawn_layer_count, s.screen_frame_count, 1, 1),
               data_type='unimportant')
   results.Add('average_num_missing_tiles', '',
               Average(s.missing_tile_count, s.screen_frame_count, 1, 1),
@@ -112,31 +135,31 @@
               Average(s.commit_time, s.commit_count, 1000, 3),
               data_type='unimportant')
   results.Add('texture_upload_count', 'count',
-              s.texture_upload_count)
+              Total(s.texture_upload_count))
   results.Add('total_texture_upload_time', 'seconds',
-              s.texture_upload_time)
+              Total(s.texture_upload_time))
 
   # Image Decoding Results
   results.Add('total_deferred_image_decode_count', 'count',
-              s.deferred_image_decode_count,
+              Total(s.deferred_image_decode_count),
               data_type='unimportant')
   results.Add('total_image_cache_hit_count', 'count',
-              s.deferred_image_cache_hits,
+              Total(s.deferred_image_cache_hit_count),
               data_type='unimportant')
   results.Add('average_image_gathering_time', 'ms',
               Average(s.image_gathering_time, s.image_gathering_count,
                       1000, 3),
               data_type='unimportant')
   results.Add('total_deferred_image_decoding_time', 'seconds',
-              s.deferred_image_decode_time,
+              Total(s.deferred_image_decode_time),
               data_type='unimportant')
 
   # Tile Analysis Results
   results.Add('total_tiles_analyzed', 'count',
-              s.tile_analysis_count,
+              Total(s.tile_analysis_count),
               data_type='unimportant')
   results.Add('solid_color_tiles_analyzed', 'count',
-              s.solid_color_tile_analysis_count,
+              Total(s.solid_color_tile_analysis_count),
               data_type='unimportant')
   results.Add('average_tile_analysis_time', 'ms',
               Average(s.tile_analysis_time, s.tile_analysis_count,
diff --git a/tools/perf/metrics/smoothness_unittest.py b/tools/perf/metrics/smoothness_unittest.py
index 2a42f9d..59d830a 100644
--- a/tools/perf/metrics/smoothness_unittest.py
+++ b/tools/perf/metrics/smoothness_unittest.py
@@ -2,112 +2,307 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 import unittest
+import random
 
 from metrics import smoothness
 from metrics.gpu_rendering_stats import GpuRenderingStats
 from telemetry.page import page
 from telemetry.page.page_measurement_results import PageMeasurementResults
+from telemetry.core.backends.chrome.tracing_backend import RawTraceResultImpl
+from telemetry.core.backends.chrome.trace_result import TraceResult
+
+class MockTimer(object):
+  """ An instance of this class is used as a global timer to generate
+      random durations for stats and consistent timestamps for all mock trace
+      events.
+  """
+  def __init__(self):
+    self.microseconds = 0
+
+  def Advance(self, low = 0, high = 100000):
+    duration = random.randint(low, high)
+    self.microseconds += duration
+    return duration
+
+class MockFrame(object):
+  """ This class mocks rendering stats, texture stats and latency stats for
+      a single frame
+  """
+  def __init__(self, mock_timer):
+    """ Initialize the stats to random values """
+    self.start = mock_timer.microseconds
+    self.main_stats = {}
+    self.impl_stats = {}
+    self.texture_stats = {}
+    self.latency_stats = {}
+    self.main_stats["animation_frame_count"] = 0
+    self.main_stats['screen_frame_count'] = 0
+    self.main_stats['paint_time'] = mock_timer.Advance()
+    self.main_stats['record_time'] = mock_timer.Advance()
+    self.main_stats['commit_time'] = mock_timer.Advance()
+    self.main_stats['commit_count'] = 1
+    self.main_stats['painted_pixel_count'] = random.randint(0, 2000000)
+    self.main_stats['recorded_pixel_count'] = random.randint(0, 2000000)
+    self.main_stats['image_gathering_count'] = random.randint(0, 100)
+    self.main_stats['image_gathering_time'] = mock_timer.Advance()
+    self.impl_stats['screen_frame_count'] = 1
+    self.impl_stats['dropped_frame_count'] = random.randint(0, 4)
+    self.impl_stats['rasterize_time'] = mock_timer.Advance()
+    self.impl_stats['rasterize_time_for_now_bins_on_pending_tree'] = \
+        mock_timer.Advance()
+    self.impl_stats['best_rasterize_time'] = mock_timer.Advance()
+    self.impl_stats['rasterized_pixel_count'] = random.randint(0, 2000000)
+    self.impl_stats['impl_thread_scroll_count'] = random.randint(0, 4)
+    self.impl_stats['main_thread_scroll_count'] = random.randint(0, 4)
+    self.impl_stats['drawn_layer_count'] = random.randint(0, 10)
+    self.impl_stats['missing_tile_count'] = random.randint(0, 10000)
+    self.impl_stats['deferred_image_decode_count'] = random.randint(0, 100)
+    self.impl_stats['deferred_image_cache_hit_count'] = random.randint(0, 50)
+    self.impl_stats['tile_analysis_count'] = random.randint(0, 10000)
+    self.impl_stats['solid_color_tile_analysis_count'] = random.randint(0, 1000)
+    self.impl_stats['deferred_image_decode_time'] = mock_timer.Advance()
+    self.impl_stats['tile_analysis_time'] = mock_timer.Advance()
+    self.texture_stats['texture_upload_count'] = random.randint(0, 1000)
+    self.texture_stats['texture_upload_time'] = mock_timer.Advance()
+    self.latency_stats['input_event_count'] = random.randint(0, 20)
+    self.latency_stats['input_event_latency'] = mock_timer.Advance()
+    self.latency_stats['touch_ui_count'] = random.randint(0, 20)
+    self.latency_stats['touch_ui_latency'] = mock_timer.Advance()
+    self.latency_stats['touch_acked_count'] = random.randint(0, 20)
+    self.latency_stats['touch_acked_latency'] = mock_timer.Advance()
+    self.latency_stats['scroll_update_count'] = random.randint(0, 20)
+    self.latency_stats['scroll_update_latency'] = mock_timer.Advance()
+    self.end = mock_timer.microseconds
+    self.duration = self.end - self.start
+
+  def AddToRenderingStats(self, rendering_stats):
+    """ Add the stats for this frame to a mock RenderingStats object (i.e. a
+        dictionary)
+    """
+    rs = rendering_stats
+    rs['totalTimeInSeconds'] += self.duration / 1e6
+    rs['numFramesSentToScreen'] += (self.main_stats['screen_frame_count'] +
+                                    self.impl_stats['screen_frame_count'])
+    rs['droppedFrameCount'] += self.impl_stats['dropped_frame_count']
+    rs['numImplThreadScrolls'] += self.impl_stats['impl_thread_scroll_count']
+    rs['numMainThreadScrolls'] += self.impl_stats['main_thread_scroll_count']
+    rs['numLayersDrawn'] += self.impl_stats['drawn_layer_count']
+    rs['numMissingTiles'] += self.impl_stats['missing_tile_count']
+    rs['textureUploadCount'] += self.texture_stats['texture_upload_count']
+    rs['totalTextureUploadTimeInSeconds'] += \
+        self.texture_stats['texture_upload_time']
+    rs['totalCommitCount'] += self.main_stats['commit_count']
+    rs['totalCommitTimeInSeconds'] += self.main_stats['commit_time']
+    rs['totalDeferredImageDecodeCount'] += self.impl_stats[
+        'deferred_image_decode_count']
+    rs['totalDeferredImageDecodeTimeInSeconds'] += self.impl_stats[
+        'deferred_image_decode_time']
+    rs['totalDeferredImageCacheHitCount'] += self.impl_stats[
+        'deferred_image_cache_hit_count']
+    rs['totalImageGatheringCount'] += self.main_stats['image_gathering_count']
+    rs['totalImageGatheringTimeInSeconds'] += self.main_stats[
+        'image_gathering_time']
+    rs['totalTilesAnalyzed'] += self.impl_stats['tile_analysis_count']
+    rs['totalTileAnalysisTimeInSeconds'] += self.impl_stats[
+        'tile_analysis_time']
+    rs['solidColorTilesAnalyzed'] += self.impl_stats[
+        'solid_color_tile_analysis_count']
+    rs['inputEventCount'] += self.latency_stats['input_event_count']
+    rs['totalInputLatency'] += self.latency_stats['input_event_latency']
+    rs['touchUICount'] += self.latency_stats['touch_ui_count']
+    rs['totalTouchUILatency'] += self.latency_stats['touch_ui_latency']
+    rs['touchAckedCount'] += self.latency_stats['touch_acked_count']
+    rs['totalTouchAckedLatency'] += self.latency_stats['touch_acked_latency']
+    rs['scrollUpdateCount'] += self.latency_stats['scroll_update_count']
+    rs['totalScrollUpdateLatency'] += self.latency_stats[
+        'scroll_update_latency']
+
+  def AppendTraceEventForMainThreadStats(self, trace):
+    """ Append a trace event with the main thread stats to trace """
+    event = {'name': 'MainThreadRenderingStats::IssueTraceEvent',
+             'tts': self.end,
+             'pid': 20978,
+             'ts': self.end,
+             'cat': 'benchmark',
+             's': 't',
+             'tid': 11,
+             'ph': 'i',
+             'args': {'data': self.main_stats}}
+    trace.append(event)
+
+  def AppendTraceEventForImplThreadStats(self, trace):
+    """ Append a trace event with the impl thread stat to trace """
+    event = {'name': 'ImplThreadRenderingStats::IssueTraceEvent',
+             'tts': self.end,
+             'pid': 20978,
+             'ts': self.end,
+             'cat': 'benchmark',
+             's': 't',
+             'tid': 11,
+             'ph': 'i',
+             'args': {'data': self.impl_stats}}
+    trace.append(event)
 
 class SmoothnessMetricsUnitTest(unittest.TestCase):
-  def testCalcResultsRealRenderStats(self):
-    mock_rendering_stats_deltas = {
-        'totalTimeInSeconds': 1.0,
-        'numFramesSentToScreen': 100,
-        'droppedFrameCount': 20,
-        'numImplThreadScrolls': 50,
-        'numMainThreadScrolls': 50,
-        'numLayersDrawn': 240,
-        'numMissingTiles': 10,
-        'textureUploadCount': 120,
-        'totalTextureUploadTimeInSeconds': 1.2,
-        'totalCommitCount': 130,
-        'totalCommitTimeInSeconds': 1.3,
-        'totalDeferredImageDecodeCount': 140,
-        'totalDeferredImageDecodeTimeInSeconds': 1.4,
-        'totalDeferredImageCacheHitCount': 30,
-        'totalImageGatheringCount': 150,
-        'totalImageGatheringTimeInSeconds': 1.5,
-        'totalTilesAnalyzed': 160,
-        'totalTileAnalysisTimeInSeconds': 1.6,
-        'solidColorTilesAnalyzed': 40,
-        'inputEventCount': 170,
-        'totalInputLatency': 1.7,
-        'touchUICount': 180,
-        'totalTouchUILatency': 1.8,
-        'touchAckedCount': 190,
-        'totalTouchAckedLatency': 1.9,
-        'scrollUpdateCount': 200,
-        'totalScrollUpdateLatency': 2.0}
-    stats = GpuRenderingStats(mock_rendering_stats_deltas)
+  def FindTimelineMarker(self, timeline):
+    events = [s for
+              s in timeline.GetAllEventsOfName(
+                  smoothness.TIMELINE_MARKER)
+              if s.parent_slice == None]
+    if len(events) != 1:
+      raise LookupError, 'timeline marker not found'
+    return events[0]
+
+  def testCalcResultsTraceEvents(self):
+    # Make the test repeatable by seeding the random number generator
+    random.seed(1234567)
+    mock_timer = MockTimer()
+    trace = []
+    rendering_stats = {
+        'totalTimeInSeconds': 0.0,
+        'numFramesSentToScreen': 0.0,
+        'droppedFrameCount': 0.0,
+        'numImplThreadScrolls': 0.0,
+        'numMainThreadScrolls': 0.0,
+        'numLayersDrawn': 0.0,
+        'numMissingTiles': 0.0,
+        'textureUploadCount': 0.0,
+        'totalTextureUploadTimeInSeconds': 0.0,
+        'totalCommitCount': 0.0,
+        'totalCommitTimeInSeconds': 0.0,
+        'totalDeferredImageDecodeCount': 0.0,
+        'totalDeferredImageDecodeTimeInSeconds': 0.0,
+        'totalDeferredImageCacheHitCount': 0.0,
+        'totalImageGatheringCount': 0.0,
+        'totalImageGatheringTimeInSeconds': 0.0,
+        'totalTilesAnalyzed': 0.0,
+        'totalTileAnalysisTimeInSeconds': 0.0,
+        'solidColorTilesAnalyzed': 0.0,
+        'inputEventCount': 0.0,
+        'totalInputLatency': 0.0,
+        'touchUICount': 0.0,
+        'totalTouchUILatency': 0.0,
+        'touchAckedCount': 0.0,
+        'totalTouchAckedLatency': 0.0,
+        'scrollUpdateCount': 0.0,
+        'totalScrollUpdateLatency': 0.0}
+
+    # Append a start trace event for the timeline marker
+    trace.append({'name': smoothness.TIMELINE_MARKER,
+                  'tts': mock_timer.microseconds,
+                  'args': {},
+                  'pid': 20978,
+                  'ts': mock_timer.microseconds,
+                  'cat': 'webkit',
+                  'tid': 11,
+                  'ph': 'S',
+                  'id': '0xafb37737e249e055'})
+    # Generate 100 random mock frames, append their trace events, and accumulate
+    # stats in rendering_stats.
+    for _ in xrange(0, 100):
+      mock_frame = MockFrame(mock_timer)
+      mock_frame.AppendTraceEventForMainThreadStats(trace)
+      mock_frame.AppendTraceEventForImplThreadStats(trace)
+      mock_frame.AddToRenderingStats(rendering_stats)
+    # Append finish trace event for timeline marker
+    trace.append({'name': smoothness.TIMELINE_MARKER,
+                  'tts': mock_timer.microseconds,
+                  'args': {},
+                  'pid': 20978,
+                  'ts': mock_timer.microseconds,
+                  'cat': 'webkit',
+                  'tid': 11,
+                  'ph': 'F',
+                  'id': '0xafb37737e249e055'})
+
+    # Create timeline object from the trace
+    trace_impl = RawTraceResultImpl(trace)
+    trace_result = TraceResult(trace_impl)
+    timeline = trace_result.AsTimelineModel()
+
+
+    timeline_marker = self.FindTimelineMarker(timeline)
+    stats = GpuRenderingStats(timeline_marker,
+                              rendering_stats,
+                              True)
 
     res = PageMeasurementResults()
     res.WillMeasurePage(page.Page('http://foo.com/', None))
     smoothness.CalcResults(stats, res)
     res.DidMeasurePage()
 
+    rs = rendering_stats
+
     # Scroll Results
     self.assertAlmostEquals(
-        1.0 / 100.0 * 1000.0,
+        round(rs['totalTimeInSeconds'] / rs['numFramesSentToScreen'] * 1000.0,
+              3),
         res.page_results[0]['mean_frame_time'].value, 2)
     self.assertAlmostEquals(
-        20.0 / 100.0 * 100.0,
+        round(rs['droppedFrameCount'] / rs['numFramesSentToScreen'] * 100.0, 1),
         res.page_results[0]['dropped_percent'].value)
     self.assertAlmostEquals(
-        50.0 / (50.0 + 50.0) * 100.0,
+        round(rs['numImplThreadScrolls'] / (rs['numImplThreadScrolls'] +
+                                            rs['numMainThreadScrolls']) * 100.0,
+              1),
         res.page_results[0]['percent_impl_scrolled'].value)
     self.assertAlmostEquals(
-        240.0 / 100.0,
+        round(rs['numLayersDrawn'] / rs['numFramesSentToScreen'], 1),
         res.page_results[0]['average_num_layers_drawn'].value)
     self.assertAlmostEquals(
-        10.0 / 100.0,
+        round(rs['numMissingTiles'] / rs['numFramesSentToScreen'], 1),
         res.page_results[0]['average_num_missing_tiles'].value)
 
     # Texture Upload Results
     self.assertAlmostEquals(
-        1.3 / 130.0 * 1000.0,
+        round(rs['totalCommitTimeInSeconds'] / rs['totalCommitCount'] * 1000.0,
+              3),
         res.page_results[0]['average_commit_time'].value)
     self.assertEquals(
-        120,
+        rs['textureUploadCount'],
         res.page_results[0]['texture_upload_count'].value)
     self.assertEquals(
-        1.2,
+        rs['totalTextureUploadTimeInSeconds'],
         res.page_results[0]['total_texture_upload_time'].value)
 
     # Image Decoding Results
     self.assertEquals(
-        140,
+        rs['totalDeferredImageDecodeCount'],
         res.page_results[0]['total_deferred_image_decode_count'].value)
     self.assertEquals(
-        30,
+        rs['totalDeferredImageCacheHitCount'],
         res.page_results[0]['total_image_cache_hit_count'].value)
     self.assertAlmostEquals(
-        1.5 / 150.0 * 1000.0,
+        round(rs['totalImageGatheringTimeInSeconds'] /
+              rs['totalImageGatheringCount'] * 1000.0, 3),
         res.page_results[0]['average_image_gathering_time'].value)
     self.assertEquals(
-        1.4,
+        rs['totalDeferredImageDecodeTimeInSeconds'],
         res.page_results[0]['total_deferred_image_decoding_time'].value)
 
     # Tile Analysis Results
     self.assertEquals(
-        160,
+        rs['totalTilesAnalyzed'],
         res.page_results[0]['total_tiles_analyzed'].value)
     self.assertEquals(
-        40,
+        rs['solidColorTilesAnalyzed'],
         res.page_results[0]['solid_color_tiles_analyzed'].value)
     self.assertAlmostEquals(
-        1.6 / 160.0 * 1000.0,
+        round(rs['totalTileAnalysisTimeInSeconds'] /
+              rs['totalTilesAnalyzed'] * 1000.0, 3),
         res.page_results[0]['average_tile_analysis_time'].value)
 
     # Latency Results
     self.assertAlmostEquals(
-        1.7 / 170.0 * 1000.0,
+        round(rs['totalInputLatency'] / rs['inputEventCount'] * 1000.0, 3),
         res.page_results[0]['average_latency'].value)
     self.assertAlmostEquals(
-        1.8 / 180.0 * 1000.0,
+        round(rs['totalTouchUILatency'] / rs['touchUICount'] * 1000.0, 3),
         res.page_results[0]['average_touch_ui_latency'].value)
     self.assertAlmostEquals(
-        1.9 / 190.0 * 1000.0,
+        round(rs['totalTouchAckedLatency'] / rs['touchAckedCount'] * 1000.0, 3),
         res.page_results[0]['average_touch_acked_latency'].value)
     self.assertAlmostEquals(
-        2.0 / 200.0 * 1000.0,
+        round(rs['totalScrollUpdateLatency'] / rs['scrollUpdateCount'] * 1000.0,
+              3),
         res.page_results[0]['average_scroll_update_latency'].value)
diff --git a/tools/perf/page_sets/2012Q3.json b/tools/perf/page_sets/2012Q3.json
index 151a1d6..777d5db 100644
--- a/tools/perf/page_sets/2012Q3.json
+++ b/tools/perf/page_sets/2012Q3.json
@@ -3,7 +3,11 @@
   "archive_data_file": "../data/2012Q3.json",
   "credentials_path": "../data/credentials.json",
   "smoothness": { "action": "scroll" },
-  "reload_and_gc": [{"action": "reload", "wait_seconds": 1}, {"action": "js_collect_garbage"}],
+  "reload_and_gc": [
+    {"action": "reload" },
+    {"action": "wait", "seconds": 1 },
+    {"action": "js_collect_garbage" }
+  ],
   "stress_memory": { "action": "reload_and_gc", "repeat": 3 },
   "pages": [
     { "url": "http://www.facebook.com/barackobama" },
diff --git a/tools/perf/page_sets/calendar_forward_backward.json b/tools/perf/page_sets/calendar_forward_backward.json
index 7e5257f..bb095a9 100644
--- a/tools/perf/page_sets/calendar_forward_backward.json
+++ b/tools/perf/page_sets/calendar_forward_backward.json
@@ -8,58 +8,37 @@
       "url": "https://www.google.com/calendar/",
       "why": "Click forward(4x) and backwards(4x) repeatedly",
       "credentials": "google",
-      "wait_for_element_with_selector": "div[class~=\"navForward\"]",
-      "post_navigate_javascript_to_execute": "(function() { var elem = document.createElement('meta');elem.name='viewport';elem.content='initial-scale=1';document.body.appendChild(elem); })();",
-      "wait_seconds": 2,
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "selector":  "div[class~=\"navForward\"]" },
+        { "action": "javascript", "expression": "(function() { var elem = document.createElement('meta');elem.name='viewport';elem.content='initial-scale=1';document.body.appendChild(elem); })();" }
+      ],
       "endure": [
-        {
-          "action": "click_element",
-          "selector": "div[class~=\"navForward\"]",
-          "wait_seconds": 2,
-          "wait_for_element_with_selector": "div[class~=\"navForward\"]"
-        },
-        {
-          "action": "click_element",
-          "selector": "div[class~=\"navForward\"]",
-          "wait_seconds": 2,
-          "wait_for_element_with_selector": "div[class~=\"navForward\"]"
-        },
-        {
-          "action": "click_element",
-          "selector": "div[class~=\"navForward\"]",
-          "wait_seconds": 2,
-          "wait_for_element_with_selector": "div[class~=\"navForward\"]"
-        },
-        {
-          "action": "click_element",
-          "selector": "div[class~=\"navForward\"]",
-          "wait_seconds": 2,
-          "wait_for_element_with_selector": "div[class~=\"navBack\"]"
-        },
-        {
-          "action": "click_element",
-          "selector": "div[class~=\"navBack\"]",
-          "wait_seconds": 2,
-          "wait_for_element_with_selector": "div[class~=\"navBack\"]"
-        },
-        {
-          "action": "click_element",
-          "selector": "div[class~=\"navBack\"]",
-          "wait_seconds": 2,
-          "wait_for_element_with_selector": "div[class~=\"navBack\"]"
-        },
-        {
-          "action": "click_element",
-          "selector": "div[class~=\"navBack\"]",
-          "wait_seconds": 2,
-          "wait_for_element_with_selector": "div[class~=\"navBack\"]"
-        },
-        {
-          "action": "click_element",
-          "selector": "div[class~=\"navBack\"]",
-          "wait_seconds": 2,
-          "wait_for_element_with_selector": "div[class~=\"navForward\"]"
-        }
+        { "action": "click_element", "selector": "div[class~=\"navForward\"]" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "selector": "div[class~=\"navForward\"]" },
+        { "action": "click_element", "selector": "div[class~=\"navForward\"]" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "selector": "div[class~=\"navForward\"]" },
+        { "action": "click_element", "selector": "div[class~=\"navForward\"]" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "selector": "div[class~=\"navForward\"]" },
+        { "action": "click_element", "selector": "div[class~=\"navForward\"]" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "selector": "div[class~=\"navBack\"]" },
+        { "action": "click_element", "selector": "div[class~=\"navBack\"]" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "selector": "div[class~=\"navBack\"]" },
+        { "action": "click_element", "selector": "div[class~=\"navBack\"]" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "selector": "div[class~=\"navBack\"]" },
+        { "action": "click_element", "selector": "div[class~=\"navBack\"]" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "selector": "div[class~=\"navBack\"]" },
+        { "action": "click_element", "selector": "div[class~=\"navBack\"]" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "selector": "div[class~=\"navForward\"]" }
       ]
     }
   ]
diff --git a/tools/perf/page_sets/gmail_alt_threadlist_conversation.json b/tools/perf/page_sets/gmail_alt_threadlist_conversation.json
new file mode 100644
index 0000000..1d857df
--- /dev/null
+++ b/tools/perf/page_sets/gmail_alt_threadlist_conversation.json
@@ -0,0 +1,31 @@
+{
+  "description": "Chrome Endure test for GMail.",
+  "archive_data_file": "../data/gmail_alt_threadlist_conversation.json",
+  "credentials_path": "../data/credentials.json",
+  "user_agent_type": "desktop",
+  "pages": [
+    {
+      "url": "https://mail.google.com/mail/",
+      "why": "Alternate between Inbox and the first email conversation",
+      "credentials": "google",
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "javascript": "window.gmonkey !== undefined && document.getElementById('gb') !== null" }
+      ],
+      "endure": [
+        {
+          "action": "click_element",
+          "xpath": "//span[@email]"
+        },
+        { "action": "wait", "condition": "href_change" },
+        { "action": "wait", "seconds": 1 },
+        {
+          "action": "click_element",
+          "selector": "a[href=\"https://mail.google.com/mail/u/0/?shva=1#inbox\"]"
+        },
+        { "action": "wait", "condition": "href_change" },
+        { "action": "wait", "seconds": 1 }
+      ]
+    }
+  ]
+}
diff --git a/tools/perf/page_sets/gmail_alt_two_labels.json b/tools/perf/page_sets/gmail_alt_two_labels.json
new file mode 100644
index 0000000..ebcfa5c
--- /dev/null
+++ b/tools/perf/page_sets/gmail_alt_two_labels.json
@@ -0,0 +1,31 @@
+{
+  "description": "Chrome Endure test for GMail.",
+  "archive_data_file": "../data/gmail_alt_two_labels.json",
+  "credentials_path": "../data/credentials.json",
+  "user_agent_type": "desktop",
+  "pages": [
+    {
+      "url": "https://mail.google.com/mail/",
+      "why": "Alternate between Inbox and Sent Mail",
+      "credentials": "google",
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "javascript": "window.gmonkey !== undefined && document.getElementById('gb') !== null" }
+      ],
+      "endure": [
+        {
+          "action": "click_element",
+          "selector": "a[href=\"https://mail.google.com/mail/u/0/?shva=1#sent\"]"
+        },
+        { "action": "wait", "condition": "href_change" },
+        { "action": "wait", "seconds": 1 },
+        {
+          "action": "click_element",
+          "selector": "a[href=\"https://mail.google.com/mail/u/0/?shva=1#inbox\"]"
+        },
+        { "action": "wait", "condition": "href_change" },
+        { "action": "wait", "seconds": 1 }
+      ]
+    }
+  ]
+}
diff --git a/tools/perf/page_sets/gmail_expand_collapse_conversation.json b/tools/perf/page_sets/gmail_expand_collapse_conversation.json
new file mode 100644
index 0000000..e03105a
--- /dev/null
+++ b/tools/perf/page_sets/gmail_expand_collapse_conversation.json
@@ -0,0 +1,30 @@
+{
+  "description": "Chrome Endure test for GMail.",
+  "archive_data_file": "../data/gmail_expand_collapse_conversation.json",
+  "credentials_path": "../data/credentials.json",
+  "user_agent_type": "desktop",
+  "pages": [
+    {
+      "url": "https://mail.google.com/mail/u/0/#inbox/13c6a141fa95ffe0",
+      "why": "Expand and Collapse a long conversation. # TODO(edmundyan): Find a long conversation rather than hardcode url",
+      "credentials": "google",
+      "navigate_steps": [
+        { "action": "navigate" },
+        { "action": "wait", "condition": "element", "selector": "img[alt=\"Expand all\"]" },
+        { "action": "click_element", "selector": "img[alt=\"Expand all\"]" },
+        { "action": "wait", "seconds": 5 },
+        { "action": "wait", "condition": "element", "selector": "img[alt=\"Collapse all\"]" },
+        { "action": "click_element", "selector": "img[alt=\"Collapse all\"]" },
+        { "action": "wait", "seconds": 1 }
+      ],
+      "endure": [
+        { "action": "wait", "condition": "element", "selector": "img[alt=\"Expand all\"]" },
+        { "action": "click_element", "selector": "img[alt=\"Expand all\"]" },
+        { "action": "wait", "seconds": 1 },
+        { "action": "wait", "condition": "element", "selector": "img[alt=\"Collapse all\"]" },
+        { "action": "click_element", "selector": "img[alt=\"Collapse all\"]" },
+        { "action": "wait", "seconds": 1 }
+      ]
+    }
+  ]
+}
diff --git a/tools/perf/page_sets/image_decoding_measurement.json b/tools/perf/page_sets/image_decoding_measurement.json
index fcf1ce2..cfe6ff7 100644
--- a/tools/perf/page_sets/image_decoding_measurement.json
+++ b/tools/perf/page_sets/image_decoding_measurement.json
@@ -1,7 +1,10 @@
 {
   "description": "A directed benchmark of image decoding performance",
-  "post_navigate_javascript_to_execute": "runBenchmark();",
-  "wait_for_javascript_expression": "isDone",
+  "navigate_steps": [
+    { "action": "navigate" },
+    { "action": "javascript", "expression": "runBenchmark();" },
+    { "action": "wait", "javascript": "isDone" }
+  ],
   "image_decoding_measurement_limit_results_to_min_iterations": true,
   "pages": [
     { "url": "file:///../../../chrome/test/data/image_decoding/image_decoding.html?gif" },
diff --git a/tools/perf/page_sets/key_desktop_sites.json b/tools/perf/page_sets/key_desktop_sites.json
index 85b05e8..69613a2 100644
--- a/tools/perf/page_sets/key_desktop_sites.json
+++ b/tools/perf/page_sets/key_desktop_sites.json
@@ -620,10 +620,10 @@
       "url": "https://mail.google.com/mail/",
       "credentials": "google",
       "scrollable_element_function": "function(callback) { gmonkey.load('2.0', function(api) { callback(api.getScrollableElement()); }); }",
-      "smoothness": {
-        "action": "scroll",
-        "wait_for_javascript_expression": "window.gmonkey !== undefined && document.getElementById('gb') !== null"
-      }
+      "smoothness": [
+        { "action": "scroll" },
+        { "action": "wait", "javascript": "window.gmonkey !== undefined && document.getElementById('gb') !== null" }
+      ]
     },
     {
       "url": "https://www.google.com/calendar/",
@@ -634,19 +634,19 @@
       "url": "https://drive.google.com",
       "credentials": "google",
       "scrollable_element_function": "function(callback) { callback(document.getElementsByClassName('doclistview-list')[0]); }",
-      "smoothness": {
-        "action": "scroll",
-        "wait_for_javascript_expression": "document.getElementsByClassName('doclistview-list').length"
-      }
+      "smoothness": [
+        { "action": "scroll" },
+        { "action": "wait", "javascript": "document.getElementsByClassName('doclistview-list').length" }
+      ]
     },
     {
       "url": "https://docs.google.com/a/google.com/document/d/1XMAtPiVFZfItsMUOYl39v5YA8bcSPe4LDrVO25OdsCU/edit",
       "credentials": "google",
       "scrollable_element_function": "function(callback) { callback(document.getElementsByClassName('kix-appview-editor')[0]); }",
-      "smoothness": {
-        "action": "scroll",
-        "wait_for_javascript_expression": "document.getElementsByClassName('kix-appview-editor').length"
-      }
+      "smoothness": [
+        { "action": "scroll" },
+        { "action": "wait", "javascript": "document.getElementsByClassName('kix-appview-editor').length" }
+      ]
     },
     { "url": "https://maps.google.com" },
     { "url": "http://reader.google.com" },
diff --git a/tools/perf/page_sets/key_mobile_sites.json b/tools/perf/page_sets/key_mobile_sites.json
index 9e3b68c..dec87c8 100644
--- a/tools/perf/page_sets/key_mobile_sites.json
+++ b/tools/perf/page_sets/key_mobile_sites.json
@@ -16,7 +16,10 @@
     {
       "url": "http://iphone.capitolvolkswagen.com/index.htm#new-inventory_p_2Fsb-new_p_2Ehtm_p_3Freset_p_3DInventoryListing",
       "why": "Typical mobile business site",
-      "wait_for_element_with_text": "Next 35"
+      "navigate_steps": [
+        { "action": "navigate" },
+        { "action": "wait", "condition": "element", "text": "Next 35" }
+      ]
     },
     {
       "url": "http://gsp.ro",
@@ -33,12 +36,18 @@
     {
       "url": "http://www.theverge.com/2012/10/28/3568746/amazon-7-inch-fire-hd-ipad-mini-ad-ballsy",
       "why": "Top tech blog",
-      "wait_for_javascript_expression": "window.Chorus !== undefined && window.Chorus.Comments !== undefined && window.Chorus.Comments.Json !== undefined && (window.Chorus.Comments.loaded || window.Chorus.Comments.Json.load_comments())"
+      "navigate_steps": [
+        { "action": "navigate" },
+        { "action": "wait", "javascript": "window.Chorus !== undefined && window.Chorus.Comments !== undefined && window.Chorus.Comments.Json !== undefined && (window.Chorus.Comments.loaded || window.Chorus.Comments.Json.load_comments())" }
+      ]
     },
     {
       "url": "http://www.cnn.com/2012/10/03/politics/michelle-obama-debate/index.html",
       "why": "Top news site",
-      "wait_seconds": 8
+      "navigate_steps": [
+        { "action": "navigate" },
+        { "action": "wait", "seconds": 8 }
+      ]
     },
     {
       "url": "http://digg.com",
@@ -59,12 +68,18 @@
     {
       "url": "https://facebook.com/barackobama",
       "why": "#1 (Alexa global)",
-      "wait_for_javascript_expression": "document.getElementById('timelineBody') !== null"
+      "navigate_steps": [
+        { "action": "navigate" },
+        { "action": "wait", "javascript": "document.getElementById('u_0_c') !== null" }
+      ]
     },
     {
       "url": "http://m.youtube.com/watch?v=9hBpF_Zj4OA",
       "why": "#3 (Alexa global)",
-      "wait_for_javascript_expression": "document.getElementById('paginatortarget') !== null"
+      "navigate_steps": [
+        { "action": "navigate"},
+        { "action": "wait", "javascript": "document.getElementById('paginatortarget') !== null"}
+      ]
     },
     {
       "name": "Blogger",
@@ -80,8 +95,10 @@
       "name": "LinkedIn",
       "url": "https://www.linkedin.com/in/linustorvalds",
       "why": "#12 (Alexa global),Public profile",
-      "post_navigate_javascript_to_execute": "getAppJs.noApp();",
-      "wait_for_javascript_expression": "document.getElementById('profile-view-scroller') !== null"
+      "navigate_steps": [
+        { "action": "navigate" },
+        { "action": "wait", "javascript": "document.getElementById('profile-view-scroller') !== null" }
+      ]
     },
     {
       "name": "Wikipedia (1 tab)",
@@ -141,23 +158,17 @@
       "url": "https://mail.google.com/mail/",
       "why": "productivity, top google properties",
       "credentials": "google",
-      "wait_for_element_with_text": "Inbox",
+      "navigate_steps": [
+        { "action": "navigate" },
+        { "action": "wait", "condition": "element", "text": "Inbox" }
+      ],
       "smoothness": {
         "action": "scroll",
         "scrollable_element_function": "function(callback) { callback(document.getElementById('views').childNodes[1].firstChild); }"
       }
     },
     {
-      "url": "https://www.google.com/calendar/",
-      "why": "productivity, top google properties",
-      "credentials": "google",
-      "smoothness": {
-        "action": "scroll",
-        "scrollable_element_function": "function(callback) { callback(document.getElementById('scrolltimedeventswk')); }"
-      }
-    },
-    {
-      "url": "http://mlb.mlb.com/index.jsp",
+      "url": "http://mlb.com/",
       "why": "#6 Alexa sports"
     },
     {
@@ -203,8 +214,11 @@
     {
       "url": "http://groupcloned.com",
       "why": "crbug.com/172906",
-      "wait_for_javascript_expression": "document.getElementById('element-19') !== null && document.getElementById('element-19').contentDocument.getElementById('element-22') !== null",
-      "wait_seconds": 5,
+      "navigate_steps": [
+        { "action": "navigate" },
+        { "action": "wait", "seconds": 5 },
+        { "action": "wait", "javascript": "document.getElementById('element-19') !== null && document.getElementById('element-19').contentDocument.getElementById('element-22') !== null && document.getElementById('element-19').contentDocument.getElementsByClassName('container list-item gc-list-item stretched').length !== 0" }
+      ],
       "smoothness": {
         "action": "scroll",
         "scroll_requires_touch": true,
@@ -212,18 +226,6 @@
       }
     },
     {
-      "url": "http://mobile-news.sandbox.google.com/news/p#2",
-      "why": "crbug.com/223726",
-      "wait_for_javascript_expression": "document.getElementById(':h') !== null",
-      "wait_seconds": 5,
-      "smoothness": {
-        "action": "scroll",
-        "scroll_requires_touch": true,
-        "scrollable_element_function": "function(callback) { callback(document.getElementById(':5')); }",
-        "remaining_scroll_distance_function": "function() { return Math.max(0, 1500 + document.getElementById(':h').getBoundingClientRect().top); }"
-      }
-    },
-    {
       "disabled": "Doesn't scroll; crbug.com/249736",
       "url": "http://forecast.io",
       "why": "crbug.com/231413"
diff --git a/tools/perf/page_sets/mobile_memory.json b/tools/perf/page_sets/mobile_memory.json
index 5788474..b9e65a7 100644
--- a/tools/perf/page_sets/mobile_memory.json
+++ b/tools/perf/page_sets/mobile_memory.json
@@ -3,7 +3,11 @@
   "archive_data_file": "../data/mobile_memory.json",
   "credentials_path": "../data/credentials.json",
   "user_agent_type": "mobile",
-  "reload_and_gc": [{"action": "reload", "wait_seconds": 15}, {"action": "js_collect_garbage"}],
+  "reload_and_gc": [
+    {"action": "reload" },
+    {"action": "wait", "seconds": 15 },
+    {"action": "js_collect_garbage" }
+  ],
   "stress_memory": { "action": "reload_and_gc", "repeat": 3 },
   "pages": [
     {
diff --git a/tools/perf/page_sets/pica.json b/tools/perf/page_sets/pica.json
index 2315276..11527f6 100644
--- a/tools/perf/page_sets/pica.json
+++ b/tools/perf/page_sets/pica.json
@@ -5,7 +5,10 @@
     { "url": "http://www.polymer-project.org/polymer-all/projects/pica/index.html",
       "script_to_evaluate_on_commit":
           "document.addEventListener('WebComponentsReady', function(){var unused = document.body.offsetHeight; window.__pica_load_time = performance.now(); setTimeout(function(){window.__web_components_ready=true}, 1000)})",
-      "wait_for_javascript_expression": "window.__web_components_ready",
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "javascript": "window.__web_components_ready" }
+      ],
       "smoothness": {
         "action": "tap_element",
         "find_element_expression": "document.querySelector('pi-app').$.Topics.$.topics.$.container.querySelector('.item > .card')",
diff --git a/tools/perf/page_sets/plus_alt_posts_photos.json b/tools/perf/page_sets/plus_alt_posts_photos.json
new file mode 100644
index 0000000..8a5727f
--- /dev/null
+++ b/tools/perf/page_sets/plus_alt_posts_photos.json
@@ -0,0 +1,26 @@
+{
+  "description": "Chrome Endure test for Google Plus.",
+  "archive_data_file": "../data/plus_alt_posts_photos.json",
+  "credentials_path": "../data/credentials.json",
+  "user_agent_type": "desktop",
+  "pages": [
+    {
+      "url": "https://plus.google.com/+BarackObama/posts",
+      "why": "Alternate between clicking posts and albums",
+      "credentials": "google",
+      "navigate_steps": [
+        { "action": "navigate" },
+        { "action": "wait", "condition": "element", "text": "Barack Obama" },
+        { "action": "wait", "condition": "element", "selector": "span[guidedhelpid=\"posts_tab_profile\"][class*=\"s6U8x\"]" }
+      ],
+      "endure": [
+        { "action": "click_element", "selector": "span[guidedhelpid=\"posts_tab_profile\"]" },
+        { "action": "wait", "condition": "element", "selector": "span[guidedhelpid=\"posts_tab_profile\"][class*=\"s6U8x\"]" },
+        { "action": "wait", "seconds": 5 },
+        { "action": "click_element", "selector": "span[guidedhelpid=\"photos_tab_profile\"]" },
+        { "action": "wait", "condition": "element", "selector": "span[guidedhelpid=\"photos_tab_profile\"][class*=\"s6U8x\"]" },
+        { "action": "wait", "seconds": 5 }
+      ]
+    }
+  ]
+}
diff --git a/tools/perf/page_sets/top_10.json b/tools/perf/page_sets/top_10.json
index b14ae24..31f5db5 100644
--- a/tools/perf/page_sets/top_10.json
+++ b/tools/perf/page_sets/top_10.json
@@ -8,26 +8,38 @@
     {
       "url": "https://www.google.com/#hl=en&q=barack+obama",
       "why": "top google property; a google tab is often open",
-      "wait_for_element_with_text": "Next"
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "condition": "element", "text": "Next" }
+      ]
     },
     {
       "url": "https://mail.google.com/mail/",
       "why": "productivity, top google properties",
       "credentials": "google",
-      "wait_for_javascript_expression": "window.gmonkey !== undefined && document.getElementById('gb') !== null"
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "javascript": "window.gmonkey !== undefined && document.getElementById('gb') !== null" }
+      ]
     },
     {
       "url": "https://www.google.com/calendar/",
       "why": "productivity, top google properties",
       "credentials": "google",
-      "wait_for_element_with_selector": "div[class~=\"navForward\"]",
-      "post_navigate_javascript_to_execute": "(function() { var elem = document.createElement('meta');elem.name='viewport';elem.content='initial-scale=1';document.body.appendChild(elem); })();",
-      "wait_seconds": 2
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "javascript", "expression": "(function() { var elem = document.createElement('meta');elem.name='viewport';elem.content='initial-scale=1';document.body.appendChild(elem); })();" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "selector": "div[class~=\"navForward\"]" }
+      ]
     },
     {
       "url": "http://www.youtube.com",
       "why": "#3 (Alexa global)",
-      "wait_seconds": 2,
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "seconds": 2 }
+      ],
       "credentials": "google"
     },
     {
@@ -35,7 +47,10 @@
       "url": "http://www.facebook.com/barackobama",
       "why": "top social,Public profile",
       "credentials": "facebook",
-      "wait_for_element_with_text": "About"
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "condition": "element", "text": "About" }
+      ]
     },
     {
       "name": "Wikipedia (1 tab)",
diff --git a/tools/perf/page_sets/top_25.json b/tools/perf/page_sets/top_25.json
index 3f47fa6..619ff77 100644
--- a/tools/perf/page_sets/top_25.json
+++ b/tools/perf/page_sets/top_25.json
@@ -8,148 +8,109 @@
     {
       "url": "https://www.google.com/#hl=en&q=barack+obama",
       "why": "top google property; a google tab is often open",
-      "wait_for_element_with_text": "Next",
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "condition": "element", "text": "Next" }
+      ],
       "stress_memory": [
         { "action": "scroll" },
-        {
-          "action": "click_element",
-          "text": "Next",
-          "wait_for_href_change": true,
-          "wait_for_element_with_text": "Next"
-        },
+        { "action": "click_element", "text": "Next" },
+        { "action": "wait", "condition": "href_change" },
+        { "action": "wait", "condition": "element", "text": "Next" },
         { "action": "scroll" },
-        {
-          "action": "click_element",
-          "text": "Next",
-          "wait_for_href_change": true,
-          "wait_for_element_with_text": "Next"
-        },
+        { "action": "click_element", "text": "Next" },
+        { "action": "wait", "condition": "href_change" },
+        { "action": "wait", "condition": "element", "text": "Next" },
         { "action": "scroll" },
-        {
-          "action": "click_element",
-          "text": "Next",
-          "wait_for_href_change": true,
-          "wait_for_element_with_text": "Previous"
-        },
+        { "action": "click_element", "text": "Next" },
+        { "action": "wait", "condition": "href_change" },
+        { "action": "wait", "condition": "element", "text": "Previous" },
         { "action": "scroll" },
-        {
-          "action": "click_element",
-          "text": "Previous",
-          "wait_for_href_change": true,
-          "wait_for_element_with_text": "Previous"
-        },
+        { "action": "click_element", "text": "Previous" },
+        { "action": "wait", "condition": "href_change" },
+        { "action": "wait", "condition": "element", "text": "Previous" },
         { "action": "scroll" },
-        {
-          "action": "click_element",
-          "text": "Previous",
-          "wait_for_href_change": true,
-          "wait_for_element_with_text": "Previous"
-        },
+        { "action": "click_element", "text": "Previous" },
+        { "action": "wait", "condition": "href_change" },
+        { "action": "wait", "condition": "element", "text": "Previous" },
         { "action": "scroll" },
-        {
-          "action": "click_element",
-          "text": "Previous",
-          "wait_for_href_change": true,
-          "wait_for_element_with_text": "Images"
-        },
+        { "action": "click_element", "text": "Previous" },
+        { "action": "wait", "condition": "href_change" },
+        { "action": "wait", "condition": "element", "text": "Images" },
         { "action": "scroll" },
-        {
-          "action": "click_element",
-          "text": "Images",
-          "wait_for_href_change": true,
-          "wait_for_element_with_text": "Images"
-        }
+        { "action": "click_element", "text": "Images" },
+        { "action": "wait", "condition": "href_change" },
+        { "action": "wait", "condition": "element", "text": "Images" }
       ]
     },
     {
       "url": "https://mail.google.com/mail/",
       "why": "productivity, top google properties",
       "credentials": "google",
-      "wait_for_javascript_expression": "window.gmonkey !== undefined && document.getElementById('gb') !== null",
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "javascript": "window.gmonkey !== undefined && document.getElementById('gb') !== null" }
+      ],
       "smoothness": {
         "action": "scroll",
         "scrollable_element_function": "function(callback) { gmonkey.load('2.0', function(api) { callback(api.getScrollableElement()); }); }"
       },
       "stress_memory": [
-        {
-          "action": "click_element",
-          "selector": "a[href=\"https://mail.google.com/mail/u/0/?shva=1#starred\"]",
-          "wait_for_href_change": true
-        },
-        {
-          "action": "click_element",
-          "selector": "a[href=\"https://mail.google.com/mail/u/0/?shva=1#inbox\"]",
-          "wait_for_href_change": true
-        }
+        { "action": "click_element", "selector": "a[href=\"https://mail.google.com/mail/u/0/?shva=1#starred\"]" },
+        { "action": "wait", "condition": "href_change" },
+        { "action": "click_element", "selector": "a[href=\"https://mail.google.com/mail/u/0/?shva=1#inbox\"]" },
+        { "action": "wait", "condition": "href_change" }
       ]
     },
     {
       "url": "https://www.google.com/calendar/",
       "why": "productivity, top google properties",
       "credentials": "google",
-      "wait_for_element_with_selector": "div[class~=\"navForward\"]",
-      "post_navigate_javascript_to_execute": "(function() { var elem = document.createElement('meta');elem.name='viewport';elem.content='initial-scale=1';document.body.appendChild(elem); })();",
-      "wait_seconds": 2,
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "seconds":  2 },
+        { "action": "wait", "condition": "element", "selector":  "div[class~=\"navForward\"]" },
+        { "action": "javascript", "expression": "(function() { var elem = document.createElement('meta');elem.name='viewport';elem.content='initial-scale=1';document.body.appendChild(elem); })();" }
+      ],
       "smoothness": {
         "action": "scroll",
         "scrollable_element_function": "function(callback) { callback(document.getElementById('scrolltimedeventswk')); }"
       },
       "stress_memory": [
-        {
-          "action": "click_element",
-          "selector": "div[class~=\"navForward\"]",
-          "wait_for_seconds": 2,
-          "wait_for_element_with_selector": "div[class~=\"navForward\"]"
-        },
-        {
-          "action": "click_element",
-          "selector": "div[class~=\"navForward\"]",
-          "wait_for_seconds": 2,
-          "wait_for_element_with_selector": "div[class~=\"navForward\"]"
-        },
-        {
-          "action": "click_element",
-          "selector": "div[class~=\"navForward\"]",
-          "wait_for_seconds": 2,
-          "wait_for_element_with_selector": "div[class~=\"navForward\"]"
-        },
-        {
-          "action": "click_element",
-          "selector": "div[class~=\"navForward\"]",
-          "wait_for_seconds": 2,
-          "wait_for_element_with_selector": "div[class~=\"navBack\"]"
-        },
-        {
-          "action": "click_element",
-          "selector": "div[class~=\"navBack\"]",
-          "wait_for_seconds": 2,
-          "wait_for_element_with_selector": "div[class~=\"navBack\"]"
-        },
-        {
-          "action": "click_element",
-          "selector": "div[class~=\"navBack\"]",
-          "wait_for_seconds": 2,
-          "wait_for_element_with_selector": "div[class~=\"navBack\"]"
-        },
-        {
-          "action": "click_element",
-          "selector": "div[class~=\"navBack\"]",
-          "wait_for_seconds": 2,
-          "wait_for_element_with_selector": "div[class~=\"navBack\"]"
-        },
-        {
-          "action": "click_element",
-          "selector": "div[class~=\"navBack\"]",
-          "wait_for_seconds": 2,
-          "wait_for_element_with_selector": "div[class~=\"navBack\"]"
-        }
+        { "action": "click_element", "selector": "div[class~=\"navForward\"]" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "selector": "div[class~=\"navForward\"]" },
+        { "action": "click_element", "selector": "div[class~=\"navForward\"]" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "selector": "div[class~=\"navForward\"]" },
+        { "action": "click_element", "selector": "div[class~=\"navForward\"]" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "selector": "div[class~=\"navForward\"]" },
+        { "action": "click_element", "selector": "div[class~=\"navForward\"]" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "selector": "div[class~=\"navBack\"]" },
+        { "action": "click_element", "selector": "div[class~=\"navBack\"]" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "selector": "div[class~=\"navBack\"]" },
+        { "action": "click_element", "selector": "div[class~=\"navBack\"]" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "selector": "div[class~=\"navBack\"]" },
+        { "action": "click_element", "selector": "div[class~=\"navBack\"]" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "selector": "div[class~=\"navBack\"]" },
+        { "action": "click_element", "selector": "div[class~=\"navBack\"]" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "selector": "div[class~=\"navBack\"]" }
       ]
     },
     {
       "url": "https://drive.google.com",
       "why": "productivity, top google properties",
       "credentials": "google",
-      "wait_for_javascript_expression": "document.getElementsByClassName('doclistview-list').length",
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "javascript": "document.getElementsByClassName('doclistview-list').length" }
+      ],
       "smoothness": {
         "action": "scroll",
         "scrollable_element_function": "function(callback) { callback(document.getElementsByClassName('doclistview-list')[0]); }"
@@ -160,8 +121,11 @@
       "url": "https://docs.google.com/document/d/1X-IKNjtEnx-WW5JIKRLsyhz5sbsat3mfTpAPUSX3_s4/view",
       "why": "productivity, top google properties; Sample doc in the link",
       "credentials": "google",
-      "wait_for_javascript_expression": "document.getElementsByClassName('kix-appview-editor').length",
-      "wait_seconds": 2,
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "javascript": "document.getElementsByClassName('kix-appview-editor').length" }
+      ],
       "smoothness": {
         "action": "scroll",
         "scrollable_element_function": "function(callback) { callback(document.getElementsByClassName('kix-appview-editor')[0]); }"
@@ -171,112 +135,88 @@
       "url": "https://plus.google.com/110031535020051778989/posts",
       "why": "social; top google property; Public profile; infinite scrolls",
       "credentials": "google",
-      "wait_for_element_with_text": "Home",
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "condition": "element", "text": "Home" }
+      ],
       "smoothness": {
         "action": "scroll",
         "scroll_is_infinite": true
       },
       "stress_memory": [
-        {
-          "action": "click_element",
-          "text": "Home",
-          "wait_seconds": 2,
-          "wait_for_element_with_text": "Profile"
-        },
-        {
-          "action": "click_element",
-          "text": "Profile",
-          "wait_seconds": 2,
-          "wait_for_element_with_text": "Explore"
-        },
-        {
-          "action": "click_element",
-          "text": "Explore",
-          "wait_seconds": 2,
-          "wait_for_element_with_text": "Events"
-        },
-        {
-          "action": "click_element",
-          "text": "Events",
-          "wait_seconds": 2,
-          "wait_for_element_with_text": "Communities"
-        },
-        {
-          "action": "click_element",
-          "text": "Communities",
-          "wait_seconds": 2,
-          "wait_for_element_with_text": "Home"
-        }
+        { "action": "click_element", "text": "Home" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "text": "Profile" },
+        { "action": "click_element", "text": "Profile" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "text": "Explore" },
+        { "action": "click_element", "text": "Explore" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "text": "Events" },
+        { "action": "click_element", "text": "Events" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "text": "Communities" },
+        { "action": "click_element", "text": "Communities" },
+        { "action": "wait", "seconds": 2 },
+        { "action": "wait", "condition": "element", "text": "Home" }
       ]
     },
     {
       "url": "http://www.youtube.com",
       "why": "#3 (Alexa global)",
-      "wait_seconds": 2,
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "seconds": 2 }
+      ],
       "credentials": "google"
     },
     {
       "name": "Blogger",
       "url": "http://googlewebmastercentral.blogspot.com/",
       "why": "#11 (Alexa global), google property; some blogger layouts have infinite scroll but more interesting",
-      "wait_for_element_with_text": "accessibility",
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "condition": "element", "text": "accessibility" }
+      ],
       "stress_memory": [
-        {
-          "action": "click_element",
-          "text": "accessibility",
-          "wait_for_navigate": true,
-          "wait_for_element_with_text": "advanced"
-        },
+        { "action": "click_element", "text": "accessibility" },
+        { "action": "wait", "condition": "navigate" },
+        { "action": "wait", "condition": "element", "text": "advanced" },
         { "action": "scroll" },
-        {
-          "action": "click_element",
-          "text": "advanced",
-          "wait_for_navigate": true,
-          "wait_for_element_with_text": "beginner"
-        },
+        { "action": "click_element", "text": "advanced" },
+        { "action": "wait", "condition": "navigate" },
+        { "action": "wait", "condition": "element", "text": "beginner" },
         { "action": "scroll" },
-        {
-          "action": "click_element",
-          "text": "beginner",
-          "wait_for_navigate": true,
-          "wait_for_element_with_text": "Home"
-        },
+        { "action": "click_element", "text": "beginner" },
+        { "action": "wait", "condition": "navigate" },
+        { "action": "wait", "condition": "element", "text": "Home" },
         { "action": "scroll" },
-        {
-          "action": "click_element",
-          "text": "Home",
-          "wait_for_navigate": true,
-          "wait_for_element_with_text": "Home"
-        }
+        { "action": "click_element", "text": "Home" },
+        { "action": "wait", "condition": "navigate" },
+        { "action": "wait", "condition": "element", "text": "Home" }
       ]
     },
     {
       "name": "Wordpress",
       "url": "http://en.blog.wordpress.com/2012/09/04/freshly-pressed-editors-picks-for-august-2012/",
       "why": "#18 (Alexa global), Picked an interesting post",
-      "wait_for_element_with_selector": "a[href=\"http://en.blog.wordpress.com/2012/08/30/new-themes-able-and-sight/\"]",
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "condition": "element", "selector": "a[href=\"http://en.blog.wordpress.com/2012/08/30/new-themes-able-and-sight/\"]" }
+      ],
       "stress_memory": [
         { "action": "scroll" },
-        {
-          "action": "click_element",
-          "selector": "a[href=\"http://en.blog.wordpress.com/2012/08/30/new-themes-able-and-sight/\"]",
-          "wait_for_navigate": true,
-          "wait_for_element_with_text": "Features"
-        },
+        { "action": "click_element", "selector": "a[href=\"http://en.blog.wordpress.com/2012/08/30/new-themes-able-and-sight/\"]" },
+        { "action": "wait", "condition": "navigate" },
+        { "action": "wait", "condition": "element", "text":  "Features" },
         { "action": "scroll" },
-        {
-          "action": "click_element",
-          "text": "Features",
-          "wait_for_navigate": true,
-          "wait_for_element_with_text": "News"
-        },
+        { "action": "click_element", "text": "Features" },
+        { "action": "wait", "condition": "navigate" },
+        { "action": "wait", "condition": "element", "text":  "News" },
         { "action": "scroll" },
-        {
-          "action": "click_element",
-          "text": "News",
-          "wait_for_navigate": true,
-          "wait_for_element_with_text": "News"
-        },
+        { "action": "click_element", "text": "News" },
+        { "action": "wait", "condition": "navigate" },
+        { "action": "wait", "condition": "element", "text": "News" },
         { "action": "scroll" }
       ]
     },
@@ -285,48 +225,33 @@
       "url": "http://www.facebook.com/barackobama",
       "why": "top social,Public profile",
       "credentials": "facebook",
-      "wait_for_element_with_text": "About",
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "condition": "element", "text": "About" }
+      ],
       "smoothness": {
         "action": "scroll",
         "scroll_is_infinite": true
       },
       "stress_memory": [
-        {
-          "action": "click_element",
-          "text": "About",
-          "wait_for_navigate": true,
-          "wait_for_element_with_text": "The Audacity of Hope"
-        },
-        {
-          "action": "click_element",
-          "text": "The Audacity of Hope",
-          "wait_for_navigate": true,
-          "wait_for_element_with_text": "Back to Barack Obama's Timeline"
-        },
-        {
-          "action": "click_element",
-          "text": "Back to Barack Obama's Timeline",
-          "wait_for_navigate": true,
-          "wait_for_element_with_text": "About"
-        },
-        {
-          "action": "click_element",
-          "text": "About",
-          "wait_for_navigate": true,
-          "wait_for_element_with_text": "Elected to U.S. Senate"
-        },
-        {
-          "action": "click_element",
-          "text": "Elected to U.S. Senate",
-          "wait_for_navigate": true,
-          "wait_for_element_with_text": "Home"
-        },
-        {
-          "action": "click_element",
-          "text": "Home",
-          "wait_for_navigate": true,
-          "wait_for_element_with_text": "Home"
-        }
+        { "action": "click_element", "text": "About" },
+        { "action": "wait", "condition": "navigate" },
+        { "action": "wait", "condition": "element", "text": "The Audacity of Hope" },
+        { "action": "click_element", "text": "The Audacity of Hope" },
+        { "action": "wait", "condition": "navigate" },
+        { "action": "wait", "condition": "element", "text": "Back to Barack Obama's Timeline" },
+        { "action": "click_element", "text": "Back to Barack Obama's Timeline" },
+        { "action": "wait", "condition": "navigate" },
+        { "action": "wait", "condition": "element", "text": "About" },
+        { "action": "click_element", "text": "About" },
+        { "action": "wait", "condition": "navigate" },
+        { "action": "wait", "condition": "element", "text": "Elected to U.S. Senate" },
+        { "action": "click_element", "text": "Elected to U.S. Senate" },
+        { "action": "wait", "condition": "navigate" },
+        { "action": "wait", "condition": "element", "text": "Home" },
+        { "action": "click_element", "text": "Home" },
+        { "action": "wait", "condition": "navigate" },
+        { "action": "wait", "condition": "element", "text": "Home" }
       ]
     },
     {
@@ -343,7 +268,10 @@
       "name": "Twitter",
       "url": "https://twitter.com/katyperry",
       "why": "#8 (Alexa global),Picked an interesting page",
-      "wait_seconds": 2,
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "seconds": 2 }
+      ],
       "smoothness": {
         "action": "scroll",
         "scroll_is_infinite": true
@@ -391,7 +319,10 @@
     {
       "url": "http://games.yahoo.com",
       "why": "#1 games according to Alexa (with actual games in it)",
-      "wait_seconds": 2
+      "navigate_steps" : [
+        { "action": "navigate" },
+        { "action": "wait", "seconds": 2 }
+      ]
     },
     {
       "url": "http://booking.com",
diff --git a/tools/perf/page_sets/tough_animation_cases.json b/tools/perf/page_sets/tough_animation_cases.json
index 1e26bcf..cba0f31 100644
--- a/tools/perf/page_sets/tough_animation_cases.json
+++ b/tools/perf/page_sets/tough_animation_cases.json
@@ -1,6 +1,6 @@
 {
   "description": "A collection of animation performance tests",
-  "smoothness": { "action": "wait", "condition": "duration", "seconds": 5 },
+  "smoothness": { "action": "wait", "seconds": 5 },
   "pages": [
     {
       "url": "file:///tough_animation_cases/keyframed_animations.html",
diff --git a/tools/perf/page_sets/tough_canvas_cases.json b/tools/perf/page_sets/tough_canvas_cases.json
index dde8ef8..a2ca25b 100644
--- a/tools/perf/page_sets/tough_canvas_cases.json
+++ b/tools/perf/page_sets/tough_canvas_cases.json
@@ -2,7 +2,7 @@
   "description": "Self-driven Canvas2D animation examples",
   "archive_data_file": "../data/tough_canvas_cases.json",
   "make_javascript_deterministic": false,
-  "smoothness": { "action": "wait", "condition": "duration", "seconds": 5 },
+  "smoothness": { "action": "wait", "seconds": 5 },
   "pages": [
     { "url":"http://mudcu.be/labs/JS1k/BreathingGalaxies.html" },
     { "url":"http://runway.countlessprojects.com/prototype/performance_test.html" },
diff --git a/tools/perf/perf_tools/__init__.py b/tools/perf/perf_tools/__init__.py
index 72020f9..f9e87cc 100644
--- a/tools/perf/perf_tools/__init__.py
+++ b/tools/perf/perf_tools/__init__.py
@@ -8,26 +8,6 @@
 import sys
 
 
-def _RemoveAllStalePycFiles():
-  for dirname, _, filenames in os.walk(os.path.dirname(__file__)):
-    if '.svn' in dirname or '.git' in dirname:
-      continue
-    for filename in filenames:
-      root, ext = os.path.splitext(filename)
-      if ext != '.pyc':
-        continue
-
-      pyc_path = os.path.join(dirname, filename)
-      py_path = os.path.join(dirname, root + '.py')
-      if not os.path.exists(py_path):
-        print >> sys.stderr, 'Removing stale .pyc file:', pyc_path
-        os.remove(pyc_path)
-
-    if os.path.isdir(dirname) and not os.listdir(dirname):
-      print >> sys.stderr, 'Removing empty directory:', dirname
-      os.removedirs(dirname)
-
-
 def _Init():
   telemetry_path = os.path.join(os.path.dirname(__file__),
                                 os.pardir, os.pardir, 'telemetry')
@@ -39,5 +19,7 @@
   sys.path.append(absolute_telemetry_tools_path)
 
 
-_RemoveAllStalePycFiles()
 _Init()
+
+from telemetry import RemoveAllStalePycFiles
+RemoveAllStalePycFiles(os.path.dirname(__file__))
diff --git a/tools/perf/profile_creators/small_profile_creator.py b/tools/perf/profile_creators/small_profile_creator.py
index 020f81f..26b1baf 100644
--- a/tools/perf/profile_creators/small_profile_creator.py
+++ b/tools/perf/profile_creators/small_profile_creator.py
@@ -4,20 +4,21 @@
 
 import os
 
-from telemetry.core import profile_creator
+from telemetry.core import util
 from telemetry.page import page_set
+from telemetry.page import profile_creator
 
 class SmallProfileCreator(profile_creator.ProfileCreator):
   """
   Runs a browser through a series of operations to fill in a small test profile.
   """
 
-  def CreateProfile(self):
-    top_25 = os.path.join(os.path.dirname(__file__),
-                          '..', 'page_sets', 'top_25.json')
-    pages_to_load = page_set.PageSet.FromFile(top_25)
-    tab = self._browser.tabs[0]
-    for page in pages_to_load:
-      tab.Navigate(page.url)
-      tab.WaitForDocumentReadyStateToBeComplete()
-    tab.Disconnect()
+  def __init__(self):
+    super(SmallProfileCreator, self).__init__()
+    top_25 = os.path.join(util.GetBaseDir(), 'page_sets', 'top_25.json')
+    self._page_set = page_set.PageSet.FromFile(top_25)
+
+  def MeasurePage(self, _, tab, results):
+    # Multiple tabs would help make this faster, but that can't be done until
+    # crbug.com/258113 is fixed.
+    tab.WaitForDocumentReadyStateToBeComplete()
diff --git a/tools/perf_expectations/perf_expectations.json b/tools/perf_expectations/perf_expectations.json
index 4f2e4b0..3917801 100644
--- a/tools/perf_expectations/perf_expectations.json
+++ b/tools/perf_expectations/perf_expectations.json
@@ -7,7 +7,7 @@
  "linux-release-64/sizes/nacl_helper-data/data": {"reva": 195191, "revb": 203172, "type": "absolute", "better": "lower", "improve": 22942, "regress": 26878, "sha1": "6d52a881"},
  "linux-release-64/sizes/nacl_helper-si/initializers": {"reva": 187981, "revb": 188124, "type": "absolute", "better": "lower", "improve": 14, "regress": 16, "sha1": "747e9d1f"},
  "linux-release-64/sizes/nacl_helper-text/text": {"reva": 202278, "revb": 202303, "type": "absolute", "better": "lower", "improve": 1801689, "regress": 1991341, "sha1": "73c0c6cd"},
- "linux-release-64/sizes/nacl_helper/nacl_helper": {"reva": 202278, "revb": 202303, "type": "absolute", "better": "lower", "improve": 2186565, "regress": 2416731, "sha1": "a0ba8954"},
+ "linux-release-64/sizes/nacl_helper/nacl_helper": {"reva": 220047, "revb": 220058, "type": "absolute", "better": "lower", "improve": 2299448, "regress": 2541496, "sha1": "bce47aaf"},
  "linux-release-64/sizes/nacl_helper_bootstrap-bss/bss": {"reva": 114822, "revb": 115019, "type": "absolute", "better": "lower", "improve": 3898, "regress": 4310, "sha1": "788b809e"},
  "linux-release-64/sizes/nacl_helper_bootstrap-data/data": {"reva": 114822, "revb": 115019, "type": "absolute", "better": "lower", "improve": 38, "regress": 42, "sha1": "02fba443"},
  "linux-release-64/sizes/nacl_helper_bootstrap-si/initializers": {"reva": 114822, "revb": 115019, "type": "absolute", "better": "lower", "improve": 0, "regress": 0, "sha1": "228221af"},
diff --git a/tools/prepare-bisect-perf-regression.py b/tools/prepare-bisect-perf-regression.py
index efcafc2..c12edbd 100755
--- a/tools/prepare-bisect-perf-regression.py
+++ b/tools/prepare-bisect-perf-regression.py
@@ -57,9 +57,11 @@
     parser.print_help()
     return 1
 
-  return bisect_utils.CreateBisectDirectoryAndSetupDepot(
-             opts,
-             bisect_utils.DEFAULT_GCLIENT_CUSTOM_DEPS)
+  if not bisect_utils.CheckIfBisectDepotExists(opts):
+    return bisect_utils.CreateBisectDirectoryAndSetupDepot(
+               opts,
+               bisect_utils.DEFAULT_GCLIENT_CUSTOM_DEPS)
+  return 0
 
 
 if __name__ == '__main__':
diff --git a/tools/resources/find_unused_resources.py b/tools/resources/find_unused_resources.py
new file mode 100755
index 0000000..87bae4b
--- /dev/null
+++ b/tools/resources/find_unused_resources.py
@@ -0,0 +1,194 @@
+#!/usr/bin/env python
+# 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.
+
+"""This script searches for unused art assets listed in a .grd file.
+
+It uses git grep to look for references to the IDR resource id or the base
+filename. If neither is found, the file is reported unused.
+
+Requires a git checkout. Must be run from your checkout's "src" root.
+
+Example:
+  cd /work/chrome/src
+  tools/resources/find_unused_resouces.py ash/resources/ash_resources.grd
+"""
+
+__author__ = 'jamescook@chromium.org (James Cook)'
+
+
+import os
+import re
+import subprocess
+import sys
+
+
+def GetBaseResourceId(resource_id):
+  """Removes common suffixes from a resource ID.
+
+  Removes suffixies that may be added by macros like IMAGE_GRID or IMAGE_BORDER.
+  For example, converts IDR_FOO_LEFT and IDR_FOO_RIGHT to just IDR_FOO.
+
+  Args:
+    resource_id: String resource ID.
+
+  Returns:
+    A string with the base part of the resource ID.
+  """
+  suffixes = [
+      '_TOP_LEFT', '_TOP', '_TOP_RIGHT',
+      '_LEFT', '_MIDDLE', '_RIGHT',
+      '_BOTTOM_LEFT', '_BOTTOM', '_BOTTOM_RIGHT',
+      '_TL', '_T', '_TR',
+      '_L', '_M', '_R',
+      '_BL', '_B', '_BR']
+  # Note: This does not check _HOVER, _PRESSED, _HOT, etc. as those are never
+  # used in macros.
+  for suffix in suffixes:
+    if resource_id.endswith(suffix):
+      resource_id = resource_id[:-len(suffix)]
+  return resource_id
+
+
+def FindFilesWithContents(string_a, string_b):
+  """Returns list of paths of files that contain |string_a| or |string_b|.
+
+  Uses --name-only to print the file paths. The default behavior of git grep
+  is to OR together multiple patterns.
+
+  Args:
+    string_a: A string to search for (not a regular expression).
+    string_b: As above.
+
+  Returns:
+    A list of file paths as strings.
+  """
+  matching_files = subprocess.check_output([
+      'git', 'grep', '--name-only', '--fixed-strings', '-e', string_a,
+      '-e', string_b])
+  files_list = matching_files.split('\n')
+  # The output ends in a newline, so slice that off.
+  files_list = files_list[:-1]
+  return files_list
+
+
+def GetUnusedResources(grd_filepath):
+  """Returns a list of resources that are unused in the code.
+
+  Prints status lines to the console because this function is quite slow.
+
+  Args:
+    grd_filepath: Path to a .grd file listing resources.
+
+  Returns:
+    A list of pairs of [resource_id, filepath] for the unused resources.
+  """
+  unused_resources = []
+  grd_file = open(grd_filepath, 'r')
+  grd_data = grd_file.read()
+  print 'Checking:'
+  # Match the resource id and file path out of substrings like:
+  # ...name="IDR_FOO_123" file="common/foo.png"...
+  # by matching between the quotation marks.
+  pattern = re.compile(
+      r"""name="([^"]*)"  # Match resource ID between quotes.
+      \s*                 # Run of whitespace, including newlines.
+      file="([^"]*)"      # Match file path between quotes.""",
+      re.VERBOSE)
+  # Use finditer over the file contents because there may be newlines between
+  # the name and file attributes.
+  for result in pattern.finditer(grd_data):
+    # Extract the IDR resource id and file path.
+    resource_id = result.group(1)
+    filepath = result.group(2)
+    filename = os.path.basename(filepath)
+    # Print progress as we go along.
+    print resource_id
+    # Ensure the resource isn't used anywhere by checking both for the resource
+    # id (which should appear in C++ code) and the raw filename (in case the
+    # file is referenced in a script, test HTML file, etc.).
+    base_resource_id = GetBaseResourceId(resource_id)
+    matching_files = FindFilesWithContents(base_resource_id, filename)
+    # Each file is matched once in the resource file itself. If there are no
+    # other matching files, it is unused.
+    if len(matching_files) == 1:
+      # Give the user some happy news.
+      print 'Unused!'
+      unused_resources.append([resource_id, filepath])
+
+  return unused_resources
+
+
+def GetScaleDirectories(resources_path):
+  """Returns a list of paths to per-scale-factor resource directories.
+
+  Assumes the directory names end in '_percent', for example,
+  ash/resources/default_200_percent or
+  chrome/app/theme/resources/touch_140_percent
+
+  Args:
+    resources_path: The base path of interest.
+
+  Returns:
+    A list of paths relative to the 'src' directory.
+  """
+  file_list = os.listdir(resources_path)
+  scale_directories = []
+  for file_entry in file_list:
+    file_path = os.path.join(resources_path, file_entry)
+    if os.path.isdir(file_path) and file_path.endswith('_percent'):
+      scale_directories.append(file_path)
+
+  scale_directories.sort()
+  return scale_directories
+
+
+def main():
+  # The script requires exactly one parameter, the .grd file path.
+  if len(sys.argv) != 2:
+    print 'Usage: tools/resources/find_unused_resources.py <path/to/grd>'
+    sys.exit(1)
+  grd_filepath = sys.argv[1]
+
+  # Try to ensure we are in a source checkout.
+  current_dir = os.getcwd()
+  if os.path.basename(current_dir) != 'src':
+    print 'Script must be run in your "src" directory.'
+    sys.exit(1)
+
+  # We require a git checkout to use git grep.
+  if not os.path.exists(current_dir + '/.git'):
+    print 'You must use a git checkout for this script to run.'
+    print current_dir + '/.git', 'not found.'
+    sys.exit(1)
+
+  # Look up the scale-factor directories.
+  resources_path = os.path.dirname(grd_filepath)
+  scale_directories = GetScaleDirectories(resources_path)
+  if not scale_directories:
+    print 'No scale directories (like "default_100_percent") found.'
+    sys.exit(1)
+
+  # |unused_resources| stores pairs of [resource_id, filepath] for resource ids
+  # that are not referenced in the code.
+  unused_resources = GetUnusedResources(grd_filepath)
+  if not unused_resources:
+    print 'All resources are used.'
+    sys.exit(0)
+
+  # Dump our output for the user.
+  print
+  print 'Unused resource ids:'
+  for resource_id, filepath in unused_resources:
+    print resource_id
+  # Print a list of 'git rm' command lines to remove unused assets.
+  print
+  print 'Unused files:'
+  for resource_id, filepath in unused_resources:
+    for directory in scale_directories:
+      print 'git rm ' + os.path.join(directory, filepath)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/tools/run-bisect-manual-test.py b/tools/run-bisect-manual-test.py
index 3b69358..863d9ba 100755
--- a/tools/run-bisect-manual-test.py
+++ b/tools/run-bisect-manual-test.py
@@ -8,6 +8,11 @@
 An example usage:
 tools/run-bisect-manual-test.py -g 201281 -b 201290
 
+On Linux platform, follow the instructions in this document
+https://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment
+to setup the sandbox manually before running the script. Otherwise the script
+fails to launch Chrome and exits with an error.
+
 """
 
 import os
@@ -77,7 +82,7 @@
   usage = ('%prog [options]\n'
            'Used to run the bisection script with a manual test.')
 
-  options = browser_options.BrowserOptions('release')
+  options = browser_options.BrowserFinderOptions('release')
   parser = options.CreateParser(usage)
 
   parser.add_option('-b', '--bad_revision',
@@ -109,6 +114,13 @@
     parser.print_help()
     return 1
 
+  if sys.platform.startswith('linux'):
+    if not os.environ.get('CHROME_DEVEL_SANDBOX'):
+      print 'SUID sandbox has not been setup.'\
+            ' See https://code.google.com/p/chromium/wiki/'\
+            'LinuxSUIDSandboxDevelopment for more information.'
+      return 1
+
   return _RunBisectionScript(options)
 
 
diff --git a/tools/set_default_handler/set_default_handler_main.cc b/tools/set_default_handler/set_default_handler_main.cc
index 7f2d9da..98e50a9 100644
--- a/tools/set_default_handler/set_default_handler_main.cc
+++ b/tools/set_default_handler/set_default_handler_main.cc
@@ -60,7 +60,7 @@
     printf("failed to set program. possible choices: %ls\n",
            JoinString(choices, L", ").c_str());
   } else {
-    printf("failed with HRESULT: 0x08X\n", result);
+    printf("failed with HRESULT: %0x08X\n", result);
   }
 
   return FAILED(result);
diff --git a/tools/sharding_supervisor/sharding_supervisor.py b/tools/sharding_supervisor/sharding_supervisor.py
index 4bddeb2..1c7e81e 100755
--- a/tools/sharding_supervisor/sharding_supervisor.py
+++ b/tools/sharding_supervisor/sharding_supervisor.py
@@ -56,6 +56,13 @@
   group.add_option(
       '--slave-index', type='int', default=0, help='Converted to --shards')
   parser.add_option_group(group)
+  group = optparse.OptionGroup(
+      parser, 'Options of run_test_cases.py passed through')
+  group.add_option(
+      '--retries', type='int', help='Kept as --retries')
+  group.add_option(
+      '--verbose', action='count', default=0, help='Kept as --verbose')
+  parser.add_option_group(group)
 
   parser.disable_interspersed_args()
   options, args = parser.parse_args()
@@ -72,6 +79,10 @@
   ]
   if options.timeout is not None:
     cmd.extend(['--timeout', str(options.timeout)])
+  if options.retries is not None:
+    cmd.extend(['--retries', str(options.retries)])
+  if options.verbose is not None:
+    cmd.extend(['--verbose'] * options.verbose)
 
   run_test_cases_extra_args, rest = pop_known_arguments(args)
 
diff --git a/tools/telemetry/bin/IEDriverServer_32.exe.sha1 b/tools/telemetry/bin/IEDriverServer_32.exe.sha1
new file mode 100644
index 0000000..abd7aa3
--- /dev/null
+++ b/tools/telemetry/bin/IEDriverServer_32.exe.sha1
@@ -0,0 +1 @@
+6ccc03cc123d97db1b9752fc5acb0c897f425f00
\ No newline at end of file
diff --git a/tools/telemetry/bin/IEDriverServer_64.exe.sha1 b/tools/telemetry/bin/IEDriverServer_64.exe.sha1
new file mode 100644
index 0000000..2fa03a4
--- /dev/null
+++ b/tools/telemetry/bin/IEDriverServer_64.exe.sha1
@@ -0,0 +1 @@
+6ba5743e98d1b7a61fd965cc7aeefaf516f04aec
\ No newline at end of file
diff --git a/tools/telemetry/bin/README.chromium b/tools/telemetry/bin/README.chromium
new file mode 100644
index 0000000..a5a8ac2
--- /dev/null
+++ b/tools/telemetry/bin/README.chromium
@@ -0,0 +1,2 @@
+IEDriverServer binary:
+  Both 32-bit and 64-bit are of version 2.35.2.
diff --git a/tools/telemetry/examples/telemetry_perf_test.py b/tools/telemetry/examples/telemetry_perf_test.py
index 14f20cc..f509939 100755
--- a/tools/telemetry/examples/telemetry_perf_test.py
+++ b/tools/telemetry/examples/telemetry_perf_test.py
@@ -12,7 +12,7 @@
 from telemetry.core import browser_options
 
 def Main(args):
-  options = browser_options.BrowserOptions()
+  options = browser_options.BrowserFinderOptions()
   parser = options.CreateParser('telemetry_perf_test.py')
   options, args = parser.parse_args(args)
 
diff --git a/tools/telemetry/telemetry/__init__.py b/tools/telemetry/telemetry/__init__.py
index cb4f71d..744b1df 100644
--- a/tools/telemetry/telemetry/__init__.py
+++ b/tools/telemetry/telemetry/__init__.py
@@ -9,7 +9,7 @@
 import sys
 
 from telemetry.core.browser import Browser
-from telemetry.core.browser_options import BrowserOptions
+from telemetry.core.browser_options import BrowserFinderOptions
 from telemetry.core.tab import Tab
 
 from telemetry.page.page_measurement import PageMeasurement
@@ -22,7 +22,7 @@
 for x in dir():
   if x.startswith('_'):
     continue
-  if x in (inspect, sys):
+  if x in (inspect, os, sys):
     continue
   m = sys.modules[__name__]
   if (inspect.isclass(getattr(m, x)) or
@@ -30,8 +30,8 @@
     __all__.append(x)
 
 
-def _RemoveAllStalePycFiles():
-  for dirname, _, filenames in os.walk(os.path.dirname(__file__)):
+def RemoveAllStalePycFiles(base_dir):
+  for dirname, _, filenames in os.walk(base_dir):
     if '.svn' in dirname or '.git' in dirname:
       continue
     for filename in filenames:
@@ -41,13 +41,23 @@
 
       pyc_path = os.path.join(dirname, filename)
       py_path = os.path.join(dirname, root + '.py')
-      if not os.path.exists(py_path):
-        print >> sys.stderr, 'Removing stale .pyc file:', pyc_path
+      if os.path.exists(py_path):
+        continue
+
+      try:
         os.remove(pyc_path)
+      except OSError:
+        # Avoid race, in case we're running simultaneous instances.
+        pass
 
-    if os.path.isdir(dirname) and not os.listdir(dirname):
-      print >> sys.stderr, 'Removing empty directory:', dirname
+    if os.listdir(dirname):
+      continue
+
+    try:
       os.removedirs(dirname)
+    except OSError:
+      # Avoid race, in case we're running simultaneous instances.
+      pass
 
 
-_RemoveAllStalePycFiles()
+RemoveAllStalePycFiles(os.path.dirname(__file__))
diff --git a/tools/telemetry/telemetry/core/backends/adb_commands.py b/tools/telemetry/telemetry/core/backends/adb_commands.py
new file mode 100644
index 0000000..be822c2
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/adb_commands.py
@@ -0,0 +1,181 @@
+# 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.
+"""Brings in Chrome Android's android_commands module, which itself is a
+thin(ish) wrapper around adb."""
+import os
+
+from telemetry.core import util
+
+# This is currently a thin wrapper around Chrome Android's
+# build scripts, located in chrome/build/android. This file exists mainly to
+# deal with locating the module.
+
+util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android')
+try:
+  from pylib import android_commands  # pylint: disable=F0401
+  from pylib import cmd_helper  # pylint: disable=F0401
+  from pylib import forwarder  # pylint: disable=F0401
+  from pylib import ports  # pylint: disable=F0401
+except Exception:
+  android_commands = None
+
+
+def IsAndroidSupported():
+  return android_commands != None
+
+
+def GetAttachedDevices():
+  """Returns a list of attached, online android devices.
+
+  If a preferred device has been set with ANDROID_SERIAL, it will be first in
+  the returned list."""
+  return android_commands.GetAttachedDevices()
+
+
+def AllocateTestServerPort():
+  return ports.AllocateTestServerPort()
+
+
+def ResetTestServerPortAllocation():
+  return ports.ResetTestServerPortAllocation()
+
+
+def GetOutDirectory():
+  return cmd_helper.OutDirectory.get()
+
+
+class AdbCommands(object):
+  """A thin wrapper around ADB"""
+
+  def __init__(self, device):
+    self._adb = android_commands.AndroidCommands(device)
+    self._device = device
+
+  def device(self):
+    return self._device
+
+  def Adb(self):
+    return self._adb
+
+  def Forward(self, local, remote):
+    ret = self._adb.Adb().SendCommand('forward %s %s' % (local, remote))
+    assert ret == ''
+
+  def RunShellCommand(self, command, timeout_time=20, log_result=False):
+    """Send a command to the adb shell and return the result.
+
+    Args:
+      command: String containing the shell command to send. Must not include
+               the single quotes as we use them to escape the whole command.
+      timeout_time: Number of seconds to wait for command to respond before
+        retrying, used by AdbInterface.SendShellCommand.
+      log_result: Boolean to indicate whether we should log the result of the
+                  shell command.
+
+    Returns:
+      list containing the lines of output received from running the command
+    """
+    return self._adb.RunShellCommand(command, timeout_time, log_result)
+
+  def CloseApplication(self, package):
+    """Attempt to close down the application, using increasing violence.
+
+    Args:
+      package: Name of the process to kill off, e.g.
+      com.google.android.apps.chrome
+    """
+    self._adb.CloseApplication(package)
+
+  def KillAll(self, process):
+    """Android version of killall, connected via adb.
+
+    Args:
+      process: name of the process to kill off
+
+    Returns:
+      the number of processess killed
+    """
+    return self._adb.KillAll(process)
+
+  def ExtractPid(self, process_name):
+    """Extracts Process Ids for a given process name from Android Shell.
+
+    Args:
+      process_name: name of the process on the device.
+
+    Returns:
+      List of all the process ids (as strings) that match the given name.
+      If the name of a process exactly matches the given name, the pid of
+      that process will be inserted to the front of the pid list.
+    """
+    return self._adb.ExtractPid(process_name)
+
+  def StartActivity(self, package, activity, wait_for_completion=False,
+                    action='android.intent.action.VIEW',
+                    category=None, data=None,
+                    extras=None, trace_file_name=None,
+                    flags=None):
+    """Starts |package|'s activity on the device.
+
+    Args:
+      package: Name of package to start (e.g. 'com.google.android.apps.chrome').
+      activity: Name of activity (e.g. '.Main' or
+        'com.google.android.apps.chrome.Main').
+      wait_for_completion: wait for the activity to finish launching (-W flag).
+      action: string (e.g. 'android.intent.action.MAIN'). Default is VIEW.
+      category: string (e.g. 'android.intent.category.HOME')
+      data: Data string to pass to activity (e.g. 'http://www.example.com/').
+      extras: Dict of extras to pass to activity. Values are significant.
+      trace_file_name: If used, turns on and saves the trace to this file name.
+    """
+    return self._adb.StartActivity(package, activity, wait_for_completion,
+                    action,
+                    category, data,
+                    extras, trace_file_name,
+                    flags)
+
+  def Push(self, local, remote):
+    return self._adb.Adb().Push(local, remote)
+
+  def Pull(self, remote, local):
+    return self._adb.Adb().Pull(remote, local)
+
+  def FileExistsOnDevice(self, file_name):
+    return self._adb.FileExistsOnDevice(file_name)
+
+  def IsRootEnabled(self):
+    return self._adb.IsRootEnabled()
+
+def HasForwarder(buildtype=None):
+  if not buildtype:
+    return (HasForwarder(buildtype='Release') or
+            HasForwarder(buildtype='Debug'))
+  return (os.path.exists(os.path.join(GetOutDirectory(), buildtype,
+                                      'device_forwarder')) and
+          os.path.exists(os.path.join(GetOutDirectory(), buildtype,
+                                      'host_forwarder')))
+
+class Forwarder(object):
+  def __init__(self, adb, *port_pairs):
+    self._adb = adb.Adb()
+    self._host_port = port_pairs[0].local_port
+
+    new_port_pairs = [(port_pair.local_port, port_pair.remote_port)
+                      for port_pair in port_pairs]
+
+    self._port_pairs = new_port_pairs
+    forwarder.Forwarder.Map(new_port_pairs, self._adb)
+
+  @staticmethod
+  def _GetBuildType():
+    assert HasForwarder()
+    return 'Debug' if HasForwarder('Debug') else 'Release'
+
+  @property
+  def url(self):
+    return 'http://localhost:%i' % self._host_port
+
+  def Close(self):
+    for (device_port, _) in self._port_pairs:
+      forwarder.Forwarder.UnmapDevicePort(device_port, self._adb)
diff --git a/tools/telemetry/telemetry/core/backends/chrome/android_browser_backend.py b/tools/telemetry/telemetry/core/backends/chrome/android_browser_backend.py
new file mode 100644
index 0000000..16d9f9b
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/android_browser_backend.py
@@ -0,0 +1,317 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import os
+import subprocess
+import sys
+import time
+
+from telemetry.core import exceptions
+from telemetry.core import util
+from telemetry.core.backends import adb_commands
+from telemetry.core.backends import browser_backend
+from telemetry.core.backends.chrome import chrome_browser_backend
+
+
+class AndroidBrowserBackendSettings(object):
+  def __init__(self, adb, activity, cmdline_file, package, pseudo_exec_name):
+    self.adb = adb
+    self.activity = activity
+    self.cmdline_file = cmdline_file
+    self.package = package
+    self.pseudo_exec_name = pseudo_exec_name
+
+  def GetDevtoolsRemotePort(self):
+    raise NotImplementedError()
+
+  def RemoveProfile(self):
+    self.adb.RunShellCommand(
+        'su -c rm -r "%s"' % self.profile_dir)
+
+  def PushProfile(self, _):
+    logging.critical('Profiles cannot be overriden with current configuration')
+    sys.exit(1)
+
+  @property
+  def is_content_shell(self):
+    return False
+
+  @property
+  def profile_dir(self):
+    raise NotImplementedError()
+
+
+class ChromeBackendSettings(AndroidBrowserBackendSettings):
+  # Stores a default Preferences file, re-used to speed up "--page-repeat".
+  _default_preferences_file = None
+
+  def __init__(self, adb, package):
+    super(ChromeBackendSettings, self).__init__(
+        adb=adb,
+        activity='com.google.android.apps.chrome.Main',
+        cmdline_file='/data/local/chrome-command-line',
+        package=package,
+        pseudo_exec_name='chrome')
+
+  def GetDevtoolsRemotePort(self):
+    return 'localabstract:chrome_devtools_remote'
+
+  def PushProfile(self, new_profile_dir):
+    self.adb.Push(new_profile_dir, self.profile_dir)
+
+  @property
+  def profile_dir(self):
+    return '/data/data/%s/app_chrome/' % self.package
+
+
+class ContentShellBackendSettings(AndroidBrowserBackendSettings):
+  def __init__(self, adb, package):
+    super(ContentShellBackendSettings, self).__init__(
+        adb=adb,
+        activity='org.chromium.content_shell_apk.ContentShellActivity',
+        cmdline_file='/data/local/tmp/content-shell-command-line',
+        package=package,
+        pseudo_exec_name='content_shell')
+
+  def GetDevtoolsRemotePort(self):
+    return 'localabstract:content_shell_devtools_remote'
+
+  @property
+  def is_content_shell(self):
+    return True
+
+  @property
+  def profile_dir(self):
+    return '/data/data/%s/app_content_shell/' % self.package
+
+
+class ChromiumTestShellBackendSettings(AndroidBrowserBackendSettings):
+  def __init__(self, adb, package):
+    super(ChromiumTestShellBackendSettings, self).__init__(
+          adb=adb,
+          activity='org.chromium.chrome.testshell.ChromiumTestShellActivity',
+          cmdline_file='/data/local/tmp/chromium-testshell-command-line',
+          package=package,
+          pseudo_exec_name='chromium_testshell')
+
+  def GetDevtoolsRemotePort(self):
+    return 'localabstract:chromium_testshell_devtools_remote'
+
+  @property
+  def is_content_shell(self):
+    return True
+
+  @property
+  def profile_dir(self):
+    return '/data/data/%s/app_chromiumtestshell/' % self.package
+
+
+class WebviewBackendSettings(AndroidBrowserBackendSettings):
+  def __init__(self, adb, package):
+    super(WebviewBackendSettings, self).__init__(
+        adb=adb,
+        activity='com.android.webview.chromium.shell.TelemetryActivity',
+        cmdline_file='/data/local/tmp/webview-command-line',
+        package=package,
+        pseudo_exec_name='webview')
+
+  def GetDevtoolsRemotePort(self):
+    # The DevTools socket name for WebView depends on the activity PID's.
+    retries = 0
+    timeout = 1
+    pid = None
+    while True:
+      pids = self.adb.ExtractPid(self.package)
+      if (len(pids) > 0):
+        pid = pids[-1]
+        break
+      time.sleep(timeout)
+      retries += 1
+      timeout *= 2
+      if retries == 4:
+        logging.critical('android_browser_backend: Timeout while waiting for '
+                         'activity %s:%s to come up',
+                         self.package,
+                         self.activity)
+        raise exceptions.BrowserGoneException('Timeout waiting for PID.')
+    return 'localabstract:webview_devtools_remote_%s' % str(pid)
+
+  @property
+  def profile_dir(self):
+    return '/data/data/%s/app_webview/' % self.package
+
+
+class AndroidBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
+  """The backend for controlling a browser instance running on Android.
+  """
+  def __init__(self, options, backend_settings):
+    super(AndroidBrowserBackend, self).__init__(
+        is_content_shell=backend_settings.is_content_shell,
+        supports_extensions=False, options=options)
+    if len(options.extensions_to_load) > 0:
+      raise browser_backend.ExtensionsNotSupportedException(
+          'Android browser does not support extensions.')
+    # Initialize fields so that an explosion during init doesn't break in Close.
+    self._options = options
+    self._adb = backend_settings.adb
+    self._backend_settings = backend_settings
+    self._saved_cmdline = None
+    if not options.keep_test_server_ports:
+      adb_commands.ResetTestServerPortAllocation()
+    self._port = adb_commands.AllocateTestServerPort()
+
+    # Kill old browser.
+    self._adb.CloseApplication(self._backend_settings.package)
+
+    if self._adb.Adb().CanAccessProtectedFileContents():
+      if not options.dont_override_profile:
+        self._backend_settings.RemoveProfile()
+      if options.profile_dir:
+        self._backend_settings.PushProfile(options.profile_dir)
+
+    # Set up the command line.
+    self._saved_cmdline = ''.join(self._adb.Adb().GetProtectedFileContents(
+        self._backend_settings.cmdline_file) or [])
+    args = [backend_settings.pseudo_exec_name]
+    args.extend(self.GetBrowserStartupArgs())
+    def QuoteIfNeeded(arg):
+      # Escape 'key=valueA valueB' to 'key="valueA valueB"'
+      # Already quoted values, or values without space are left untouched.
+      # This is required so CommandLine.java can parse valueB correctly rather
+      # than as a separate switch.
+      params = arg.split('=')
+      if len(params) != 2:
+        return arg
+      key, values = params
+      if ' ' not in values:
+        return arg
+      if values[0] in '"\'' and values[-1] == values[0]:
+        return arg
+      return '%s="%s"' % (key, values)
+    args = map(QuoteIfNeeded, args)
+    self._adb.Adb().SetProtectedFileContents(
+        self._backend_settings.cmdline_file, ' '.join(args))
+    cmdline = self._adb.Adb().GetProtectedFileContents(
+        self._backend_settings.cmdline_file)
+    if len(cmdline) != 1 or cmdline[0] != ' '.join(args):
+      logging.critical('Failed to set Chrome command line. '
+                       'Fix this by flashing to a userdebug build.')
+      sys.exit(1)
+
+  def Start(self):
+    self._adb.RunShellCommand('logcat -c')
+    self._adb.StartActivity(self._backend_settings.package,
+                            self._backend_settings.activity,
+                            True,
+                            None,
+                            'chrome://newtab/')
+
+    self._adb.Forward('tcp:%d' % self._port,
+                      self._backend_settings.GetDevtoolsRemotePort())
+
+    try:
+      self._WaitForBrowserToComeUp()
+      self._PostBrowserStartupInitialization()
+    except exceptions.BrowserGoneException:
+      logging.critical('Failed to connect to browser.')
+      if not self._adb.Adb().CanAccessProtectedFileContents():
+        logging.critical(
+          'Resolve this by either: '
+          '(1) Flashing to a userdebug build OR '
+          '(2) Manually enabling web debugging in Chrome at '
+          'Settings > Developer tools > Enable USB Web debugging.')
+      sys.exit(1)
+    except:
+      import traceback
+      traceback.print_exc()
+      self.Close()
+      raise
+
+  def GetBrowserStartupArgs(self):
+    args = super(AndroidBrowserBackend, self).GetBrowserStartupArgs()
+    args.append('--enable-remote-debugging')
+    args.append('--no-restore-state')
+    args.append('--disable-fre')
+    return args
+
+  @property
+  def adb(self):
+    return self._adb
+
+  @property
+  def pid(self):
+    return int(self._adb.ExtractPid(self._backend_settings.package)[0])
+
+  @property
+  def browser_directory(self):
+    return None
+
+  @property
+  def profile_directory(self):
+    return self._backend_settings.profile_dir
+
+  @property
+  def package(self):
+    return self._backend_settings.package
+
+  def __del__(self):
+    self.Close()
+
+  def Close(self):
+    super(AndroidBrowserBackend, self).Close()
+
+    if self._saved_cmdline:
+      self._adb.Adb().SetProtectedFileContents(
+          self._backend_settings.cmdline_file,
+          self._saved_cmdline)
+    else:
+      self._adb.RunShellCommand('rm %s' % self._backend_settings.cmdline_file)
+    self._adb.CloseApplication(self._backend_settings.package)
+
+    if self._options.output_profile_path:
+      logging.info("Pulling profile directory from device: '%s'->'%s'." %
+          (self._backend_settings.profile_dir,
+          self._options.output_profile_path))
+      # To minimize bandwidth it might be good to look at whether all the data
+      # pulled down is really needed e.g. .pak files.
+      self._adb.Pull(self._backend_settings.profile_dir,
+          self._options.output_profile_path)
+
+  def IsBrowserRunning(self):
+    pids = self._adb.ExtractPid(self._backend_settings.package)
+    return len(pids) != 0
+
+  def GetRemotePort(self, local_port):
+    return local_port
+
+  def GetStandardOutput(self):
+    return '\n'.join(self._adb.RunShellCommand('logcat -d -t 500'))
+
+  def GetStackTrace(self):
+    def Decorate(title, content):
+      return title + '\n' + content + '\n' + '*' * 80 + '\n'
+    # Get the last lines of logcat (large enough to contain stacktrace)
+    logcat = self.GetStandardOutput()
+    ret = Decorate('Logcat', logcat)
+    stack = os.path.join(util.GetChromiumSrcDir(), 'third_party',
+                         'android_platform', 'development', 'scripts', 'stack')
+    # Try to symbolize logcat.
+    if os.path.exists(stack):
+      p = subprocess.Popen([stack], stdin=subprocess.PIPE,
+                           stdout=subprocess.PIPE)
+      ret += Decorate('Stack from Logcat', p.communicate(input=logcat)[0])
+
+    # Try to get tombstones.
+    tombstones = os.path.join(util.GetChromiumSrcDir(), 'build', 'android',
+                              'tombstones.py')
+    if os.path.exists(tombstones):
+      ret += Decorate('Tombstones',
+                      subprocess.Popen([tombstones, '-w', '--device',
+                                        self._adb.device()],
+                                       stdout=subprocess.PIPE).communicate()[0])
+    return ret
+
+  def CreateForwarder(self, *port_pairs):
+    return adb_commands.Forwarder(self._adb, *port_pairs)
diff --git a/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py b/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py
new file mode 100644
index 0000000..c74af15
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder.py
@@ -0,0 +1,186 @@
+# 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.
+
+"""Finds android browsers that can be controlled by telemetry."""
+
+import os
+import logging as real_logging
+import re
+import subprocess
+import sys
+
+from telemetry.core import browser
+from telemetry.core import possible_browser
+from telemetry.core import util
+from telemetry.core.backends import adb_commands
+from telemetry.core.backends.chrome import android_browser_backend
+from telemetry.core.platform import android_platform_backend
+
+CHROME_PACKAGE_NAMES = {
+  'android-chrome': 'com.google.android.apps.chrome',
+  'android-chrome-beta': 'com.chrome.beta',
+  'android-chrome-dev': 'com.google.android.apps.chrome_dev',
+  'android-jb-system-chrome': 'com.android.chrome'
+}
+
+ALL_BROWSER_TYPES = ','.join([
+                                'android-chromium-testshell',
+                                'android-content-shell',
+                                'android-webview',
+                             ] + CHROME_PACKAGE_NAMES.keys())
+
+CONTENT_SHELL_PACKAGE = 'org.chromium.content_shell_apk'
+CHROMIUM_TESTSHELL_PACKAGE = 'org.chromium.chrome.testshell'
+WEBVIEW_PACKAGE = 'com.android.webview.chromium.shell'
+
+
+# adb shell pm list packages
+# adb
+# intents to run (pass -D url for the rest)
+#   com.android.chrome/.Main
+#   com.google.android.apps.chrome/.Main
+
+class PossibleAndroidBrowser(possible_browser.PossibleBrowser):
+  """A launchable android browser instance."""
+  def __init__(self, browser_type, finder_options, backend_settings):
+    super(PossibleAndroidBrowser, self).__init__(browser_type, finder_options)
+    self._backend_settings = backend_settings
+
+  def __repr__(self):
+    return 'PossibleAndroidBrowser(browser_type=%s)' % self.browser_type
+
+  def Create(self):
+    backend = android_browser_backend.AndroidBrowserBackend(
+        self.finder_options, self._backend_settings)
+    platform_backend = android_platform_backend.AndroidPlatformBackend(
+        self._backend_settings.adb.Adb(),
+        self.finder_options.no_performance_mode)
+    b = browser.Browser(backend, platform_backend)
+    return b
+
+  def SupportsOptions(self, finder_options):
+    if len(finder_options.extensions_to_load) != 0:
+      return False
+    return True
+
+def SelectDefaultBrowser(_):
+  return None
+
+adb_works = None
+def CanFindAvailableBrowsers(logging=real_logging):
+  if not adb_commands.IsAndroidSupported():
+    return False
+
+  global adb_works
+
+  if adb_works == None:
+    try:
+      with open(os.devnull, 'w') as devnull:
+        proc = subprocess.Popen(['adb', 'devices'],
+                                stdout=subprocess.PIPE,
+                                stderr=subprocess.PIPE,
+                                stdin=devnull)
+        stdout, _ = proc.communicate()
+        if re.search(re.escape('????????????\tno permissions'), stdout) != None:
+          logging.warn(
+              ('adb devices reported a permissions error. Consider '
+               'restarting adb as root:'))
+          logging.warn('  adb kill-server')
+          logging.warn('  sudo `which adb` devices\n\n')
+        adb_works = True
+    except OSError:
+      platform_tools_path = os.path.join(util.GetChromiumSrcDir(),
+          'third_party', 'android_tools', 'sdk', 'platform-tools')
+      if (sys.platform.startswith('linux') and
+          os.path.exists(os.path.join(platform_tools_path, 'adb'))):
+        os.environ['PATH'] = os.pathsep.join([platform_tools_path,
+                                              os.environ['PATH']])
+        adb_works = True
+      else:
+        adb_works = False
+  if adb_works and sys.platform.startswith('linux'):
+    # Workaround for crbug.com/268450
+    import psutil  # pylint: disable=F0401
+    adb_commands.GetAttachedDevices()
+    pids  = [p.pid for p in psutil.process_iter() if 'adb' in p.name]
+    with open(os.devnull, 'w') as devnull:
+      for pid in pids:
+        ret = subprocess.call(['taskset', '-p', '0x1', str(pid)],
+                              stdout=subprocess.PIPE,
+                              stderr=subprocess.PIPE,
+                              stdin=devnull)
+        if ret:
+          logging.warn('Failed to taskset %d (%s)', pid, ret)
+
+  return adb_works
+
+def FindAllAvailableBrowsers(finder_options, logging=real_logging):
+  """Finds all the desktop browsers available on this machine."""
+  if not CanFindAvailableBrowsers(logging=logging):
+    logging.info('No adb command found. ' +
+                 'Will not try searching for Android browsers.')
+    return []
+
+  device = None
+  if finder_options.android_device:
+    devices = [finder_options.android_device]
+  else:
+    devices = adb_commands.GetAttachedDevices()
+
+  if len(devices) == 0:
+    logging.info('No android devices found.')
+    return []
+
+  if len(devices) > 1:
+    logging.warn('Multiple devices attached. ' +
+                 'Please specify a device explicitly.')
+    return []
+
+  device = devices[0]
+
+  adb = adb_commands.AdbCommands(device=device)
+
+  packages = adb.RunShellCommand('pm list packages')
+  possible_browsers = []
+  if 'package:' + CONTENT_SHELL_PACKAGE in packages:
+    b = PossibleAndroidBrowser(
+        'android-content-shell',
+        finder_options, android_browser_backend.ContentShellBackendSettings(
+            adb, CONTENT_SHELL_PACKAGE))
+    possible_browsers.append(b)
+
+  if 'package:' + CHROMIUM_TESTSHELL_PACKAGE in packages:
+    b = PossibleAndroidBrowser(
+        'android-chromium-testshell',
+        finder_options,
+        android_browser_backend.ChromiumTestShellBackendSettings(
+            adb, CHROMIUM_TESTSHELL_PACKAGE))
+    possible_browsers.append(b)
+
+  if 'package:' + WEBVIEW_PACKAGE in packages:
+    b = PossibleAndroidBrowser(
+        'android-webview',
+        finder_options,
+        android_browser_backend.WebviewBackendSettings(adb, WEBVIEW_PACKAGE))
+    possible_browsers.append(b)
+
+  for name, package in CHROME_PACKAGE_NAMES.iteritems():
+    if 'package:' + package in packages:
+      b = PossibleAndroidBrowser(
+          name,
+          finder_options,
+          android_browser_backend.ChromeBackendSettings(adb, package))
+      possible_browsers.append(b)
+
+  # See if the "forwarder" is installed -- we need this to host content locally
+  # but make it accessible to the device.
+  if len(possible_browsers) and not adb_commands.HasForwarder():
+    logging.warn('telemetry detected an android device. However,')
+    logging.warn('Chrome\'s port-forwarder app is not available.')
+    logging.warn('To build:')
+    logging.warn('  ninja -C out/Release forwarder2 md5sum')
+    logging.warn('')
+    logging.warn('')
+    return []
+  return possible_browsers
diff --git a/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder_unittest.py b/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder_unittest.py
new file mode 100644
index 0000000..ef3bd5d
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/android_browser_finder_unittest.py
@@ -0,0 +1,89 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import unittest
+
+from telemetry.core import browser_options
+from telemetry.core.backends.chrome import android_browser_finder
+from telemetry.unittest import system_stub
+
+class LoggingStub(object):
+  def __init__(self):
+    self.warnings = []
+
+  def info(self, msg, *args):
+    pass
+
+  def warn(self, msg, *args):
+    self.warnings.append(msg % args)
+
+class AndroidBrowserFinderTest(unittest.TestCase):
+  def setUp(self):
+    self._stubs = system_stub.Override(android_browser_finder,
+                                       ['adb_commands', 'subprocess'])
+    android_browser_finder.adb_works = None  # Blow cache between runs.
+
+  def tearDown(self):
+    self._stubs.Restore()
+
+  def test_no_adb(self):
+    finder_options = browser_options.BrowserFinderOptions()
+
+    def NoAdb(*args, **kargs): # pylint: disable=W0613
+      raise OSError('not found')
+    self._stubs.subprocess.Popen = NoAdb
+    browsers = android_browser_finder.FindAllAvailableBrowsers(finder_options)
+    self.assertEquals(0, len(browsers))
+
+  def test_adb_no_devices(self):
+    finder_options = browser_options.BrowserFinderOptions()
+
+    browsers = android_browser_finder.FindAllAvailableBrowsers(finder_options)
+    self.assertEquals(0, len(browsers))
+
+
+  def test_adb_permissions_error(self):
+    finder_options = browser_options.BrowserFinderOptions()
+
+    self._stubs.subprocess.Popen.communicate_result = (
+        """List of devices attached
+????????????\tno permissions""",
+        """* daemon not running. starting it now on port 5037 *
+* daemon started successfully *
+""")
+
+    log_stub = LoggingStub()
+    browsers = android_browser_finder.FindAllAvailableBrowsers(
+      finder_options, log_stub)
+    self.assertEquals(3, len(log_stub.warnings))
+    self.assertEquals(0, len(browsers))
+
+
+  def test_adb_two_devices(self):
+    finder_options = browser_options.BrowserFinderOptions()
+
+    self._stubs.adb_commands.attached_devices = ['015d14fec128220c',
+                                                 '015d14fec128220d']
+
+    log_stub = LoggingStub()
+    browsers = android_browser_finder.FindAllAvailableBrowsers(
+      finder_options, log_stub)
+    self.assertEquals(1, len(log_stub.warnings))
+    self.assertEquals(0, len(browsers))
+
+  def test_adb_one_device(self):
+    finder_options = browser_options.BrowserFinderOptions()
+
+    self._stubs.adb_commands.attached_devices = ['015d14fec128220c']
+
+    def OnPM(args):
+      assert args[0] == 'pm'
+      assert args[1] == 'list'
+      assert args[2] == 'packages'
+      return ['package:org.chromium.content_shell_apk',
+              'package.com.google.android.setupwizard']
+
+    self._stubs.adb_commands.shell_command_handlers['pm'] = OnPM
+
+    browsers = android_browser_finder.FindAllAvailableBrowsers(finder_options)
+    self.assertEquals(1, len(browsers))
diff --git a/tools/telemetry/telemetry/core/backends/chrome/chrome_browser_backend.py b/tools/telemetry/telemetry/core/backends/chrome/chrome_browser_backend.py
index de8a611..9f48782 100644
--- a/tools/telemetry/telemetry/core/backends/chrome/chrome_browser_backend.py
+++ b/tools/telemetry/telemetry/core/backends/chrome/chrome_browser_backend.py
@@ -16,11 +16,11 @@
 from telemetry.core import wpr_modes
 from telemetry.core import wpr_server
 from telemetry.core.backends import browser_backend
-from telemetry.core.chrome import extension_dict_backend
-from telemetry.core.chrome import misc_web_contents_backend
-from telemetry.core.chrome import system_info_backend
-from telemetry.core.chrome import tab_list_backend
-from telemetry.core.chrome import tracing_backend
+from telemetry.core.backends.chrome import extension_dict_backend
+from telemetry.core.backends.chrome import misc_web_contents_backend
+from telemetry.core.backends.chrome import system_info_backend
+from telemetry.core.backends.chrome import tab_list_backend
+from telemetry.core.backends.chrome import tracing_backend
 from telemetry.unittest import options_for_unittests
 
 class ChromeBrowserBackend(browser_backend.BrowserBackend):
diff --git a/tools/telemetry/telemetry/core/chrome/chromeos_login_ext/main.html b/tools/telemetry/telemetry/core/backends/chrome/chromeos_login_ext/main.html
similarity index 100%
rename from tools/telemetry/telemetry/core/chrome/chromeos_login_ext/main.html
rename to tools/telemetry/telemetry/core/backends/chrome/chromeos_login_ext/main.html
diff --git a/tools/telemetry/telemetry/core/backends/chrome/chromeos_login_ext/main.js b/tools/telemetry/telemetry/core/backends/chrome/chromeos_login_ext/main.js
new file mode 100644
index 0000000..7ec6a6c
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/chromeos_login_ext/main.js
@@ -0,0 +1,17 @@
+// 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.
+
+var PARENT_PAGE = 'chrome://oobe/';
+
+var msg = {
+  'method': 'loginUILoaded'
+};
+window.parent.postMessage(msg, PARENT_PAGE);
+
+var msg = {
+  'method': 'completeLogin',
+  'email': 'test@test.test',
+  'password': ''
+};
+window.parent.postMessage(msg, PARENT_PAGE);
diff --git a/tools/telemetry/telemetry/core/chrome/chromeos_login_ext/manifest.json b/tools/telemetry/telemetry/core/backends/chrome/chromeos_login_ext/manifest.json
similarity index 100%
rename from tools/telemetry/telemetry/core/chrome/chromeos_login_ext/manifest.json
rename to tools/telemetry/telemetry/core/backends/chrome/chromeos_login_ext/manifest.json
diff --git a/tools/telemetry/telemetry/core/backends/chrome/cros_browser_backend.py b/tools/telemetry/telemetry/core/backends/chrome/cros_browser_backend.py
new file mode 100644
index 0000000..91c422d
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/cros_browser_backend.py
@@ -0,0 +1,434 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import logging
+import os
+import subprocess
+
+from telemetry.core import exceptions
+from telemetry.core import util
+from telemetry.core.backends import browser_backend
+from telemetry.core.backends.chrome import chrome_browser_backend
+
+class CrOSBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
+  # Some developers' workflow includes running the Chrome process from
+  # /usr/local/... instead of the default location. We have to check for both
+  # paths in order to support this workflow.
+  CHROME_PATHS = ['/opt/google/chrome/chrome ',
+                  '/usr/local/opt/google/chrome/chrome ']
+
+  def __init__(self, browser_type, options, cri, is_guest):
+    super(CrOSBrowserBackend, self).__init__(
+        is_content_shell=False, supports_extensions=not is_guest,
+        options=options)
+    # Initialize fields so that an explosion during init doesn't break in Close.
+    self._browser_type = browser_type
+    self._options = options
+    self._cri = cri
+    self._is_guest = is_guest
+
+    self._remote_debugging_port = self._cri.GetRemotePort()
+    self._port = self._remote_debugging_port
+    self._forwarder = None
+
+    self._SetBranchNumber(self._GetChromeVersion())
+
+    self._login_ext_dir = os.path.join(os.path.dirname(__file__),
+                                       'chromeos_login_ext')
+
+    # Push a dummy login extension to the device.
+    # This extension automatically logs in as test@test.test
+    # Note that we also perform this copy locally to ensure that
+    # the owner of the extensions is set to chronos.
+    logging.info('Copying dummy login extension to the device')
+    cri.PushFile(self._login_ext_dir, '/tmp/')
+    self._login_ext_dir = '/tmp/chromeos_login_ext'
+    cri.RunCmdOnDevice(['chown', '-R', 'chronos:chronos',
+                        self._login_ext_dir])
+
+    # Copy extensions to temp directories on the device.
+    # Note that we also perform this copy locally to ensure that
+    # the owner of the extensions is set to chronos.
+    for e in options.extensions_to_load:
+      output = cri.RunCmdOnDevice(['mktemp', '-d', '/tmp/extension_XXXXX'])
+      extension_dir = output[0].rstrip()
+      cri.PushFile(e.path, extension_dir)
+      cri.RunCmdOnDevice(['chown', '-R', 'chronos:chronos', extension_dir])
+      e.local_path = os.path.join(extension_dir, os.path.basename(e.path))
+
+    # Ensure the UI is running and logged out.
+    self._RestartUI()
+    util.WaitFor(lambda: self.IsBrowserRunning(), 20)  # pylint: disable=W0108
+
+    # Delete test@test.test's cryptohome vault (user data directory).
+    if not options.dont_override_profile:
+      logging.info('Deleting user\'s cryptohome vault (the user data dir)')
+      self._cri.RunCmdOnDevice(
+          ['cryptohome', '--action=remove', '--force', '--user=test@test.test'])
+    if options.profile_dir:
+      profile_dir = '/home/chronos/Default'
+      cri.RunCmdOnDevice(['rm', '-rf', profile_dir])
+      cri.PushFile(options.profile_dir + '/Default', profile_dir)
+      cri.RunCmdOnDevice(['chown', '-R', 'chronos:chronos', profile_dir])
+
+  def GetBrowserStartupArgs(self):
+    self.webpagereplay_remote_http_port = self._cri.GetRemotePort()
+    self.webpagereplay_remote_https_port = self._cri.GetRemotePort()
+
+    args = super(CrOSBrowserBackend, self).GetBrowserStartupArgs()
+
+    args.extend([
+            '--enable-smooth-scrolling',
+            '--enable-threaded-compositing',
+            '--enable-per-tile-painting',
+            '--force-compositing-mode',
+            # Disables the start page, as well as other external apps that can
+            # steal focus or make measurements inconsistent.
+            '--disable-default-apps',
+            # Skip user image selection screen, and post login screens.
+            '--oobe-skip-postlogin',
+            # Allow devtools to connect to chrome.
+            '--remote-debugging-port=%i' % self._remote_debugging_port,
+            # Open a maximized window.
+            '--start-maximized',
+            # Debug logging for login flake (crbug.com/263527).
+            '--vmodule=*/browser/automation/*=2,*/chromeos/net/*=2,' +
+                '*/chromeos/login/*=2'])
+
+
+    if self.chrome_branch_number <= 1599:
+      args.extend([
+          # Jump to the login screen, skipping network selection, eula, etc.
+          '--login-screen=login',
+          # Skip hwid check, for VMs and pre-MP lab devices.
+          '--skip-hwid-check',])
+      if not self._is_guest:
+        # This extension bypasses gaia and logs us in.
+        args.append('--auth-ext-path=%s' % self._login_ext_dir)
+
+    return args
+
+  def _GetSessionManagerPid(self, procs):
+    """Returns the pid of the session_manager process, given the list of
+    processes."""
+    for pid, process, _, _ in procs:
+      if process.startswith('/sbin/session_manager '):
+        return pid
+    return None
+
+  def _GetChromeProcess(self):
+    """Locates the the main chrome browser process.
+
+    Chrome on cros is usually in /opt/google/chrome, but could be in
+    /usr/local/ for developer workflows - debug chrome is too large to fit on
+    rootfs.
+
+    Chrome spawns multiple processes for renderers. pids wrap around after they
+    are exhausted so looking for the smallest pid is not always correct. We
+    locate the session_manager's pid, and look for the chrome process that's an
+    immediate child. This is the main browser process.
+    """
+    procs = self._cri.ListProcesses()
+    session_manager_pid = self._GetSessionManagerPid(procs)
+    if not session_manager_pid:
+      return None
+
+    # Find the chrome process that is the child of the session_manager.
+    for pid, process, ppid, _ in procs:
+      if ppid != session_manager_pid:
+        continue
+      for path in self.CHROME_PATHS:
+        if process.startswith(path):
+          return {'pid': pid, 'path': path}
+    return None
+
+  def _GetChromeVersion(self):
+    util.WaitFor(lambda: self._GetChromeProcess(), # pylint: disable=W0108
+                 timeout=30)
+    result = self._GetChromeProcess()
+    assert result and result['path']
+    (version, _) = self._cri.RunCmdOnDevice([result['path'], '--version'])
+    assert version
+    return version
+
+  @property
+  def pid(self):
+    result = self._GetChromeProcess()
+    if result and 'pid' in result:
+      return result['pid']
+    return None
+
+  @property
+  def browser_directory(self):
+    result = self._GetChromeProcess()
+    if result and 'path' in result:
+      return os.path.dirname(result['path'])
+    return None
+
+  @property
+  def profile_directory(self):
+    return '/home/chronos/Default'
+
+  @property
+  def hwid(self):
+    return self._cri.RunCmdOnDevice(['/usr/bin/crossystem', 'hwid'])[0]
+
+  def GetRemotePort(self, _):
+    return self._cri.GetRemotePort()
+
+  def __del__(self):
+    self.Close()
+
+  def Start(self):
+    # Escape all commas in the startup arguments we pass to Chrome
+    # because dbus-send delimits array elements by commas
+    startup_args = [a.replace(',', '\\,') for a in self.GetBrowserStartupArgs()]
+
+    # Restart Chrome with the login extension and remote debugging.
+    logging.info('Restarting Chrome with flags and login')
+    args = ['dbus-send', '--system', '--type=method_call',
+            '--dest=org.chromium.SessionManager',
+            '/org/chromium/SessionManager',
+            'org.chromium.SessionManagerInterface.EnableChromeTesting',
+            'boolean:true',
+            'array:string:"%s"' % ','.join(startup_args)]
+    self._cri.RunCmdOnDevice(args)
+
+    if not self._cri.local:
+      # Find a free local port.
+      self._port = util.GetAvailableLocalPort()
+
+      # Forward the remote debugging port.
+      logging.info('Forwarding remote debugging port')
+      self._forwarder = SSHForwarder(
+        self._cri, 'L',
+        util.PortPair(self._port, self._remote_debugging_port))
+
+    # Wait for the browser to come up.
+    logging.info('Waiting for browser to be ready')
+    try:
+      self._WaitForBrowserToComeUp()
+      self._PostBrowserStartupInitialization()
+    except:
+      import traceback
+      traceback.print_exc()
+      self.Close()
+      raise
+
+    # chrome_branch_number is set in _PostBrowserStartupInitialization.
+    # Without --skip-hwid-check (introduced in crrev.com/203397), devices/VMs
+    # will be stuck on the bad hwid screen.
+    if self.chrome_branch_number <= 1500 and not self.hwid:
+      raise exceptions.LoginException(
+          'Hardware id not set on device/VM. --skip-hwid-check not supported '
+          'with chrome branches 1500 or earlier.')
+
+    if self._is_guest:
+      pid = self.pid
+      self._NavigateGuestLogin()
+      # Guest browsing shuts down the current browser and launches an incognito
+      # browser in a separate process, which we need to wait for.
+      util.WaitFor(lambda: pid != self.pid, 10)
+      self._WaitForBrowserToComeUp()
+    else:
+      self._NavigateLogin()
+
+    logging.info('Browser is up!')
+
+  def Close(self):
+    super(CrOSBrowserBackend, self).Close()
+
+    self._RestartUI() # Logs out.
+
+    if not self._cri.local:
+      if self._forwarder:
+        self._forwarder.Close()
+        self._forwarder = None
+
+    if self._login_ext_dir:
+      self._cri.RmRF(self._login_ext_dir)
+      self._login_ext_dir = None
+
+    for e in self._options.extensions_to_load:
+      self._cri.RmRF(os.path.dirname(e.local_path))
+
+    self._cri = None
+
+  def IsBrowserRunning(self):
+    return bool(self.pid)
+
+  def GetStandardOutput(self):
+    return 'Cannot get standard output on CrOS'
+
+  def GetStackTrace(self):
+    return 'Cannot get stack trace on CrOS'
+
+  def CreateForwarder(self, *port_pairs):
+    assert self._cri
+    return (browser_backend.DoNothingForwarder(*port_pairs) if self._cri.local
+        else SSHForwarder(self._cri, 'R', *port_pairs))
+
+  def _RestartUI(self):
+    if self._cri:
+      logging.info('(Re)starting the ui (logs the user out)')
+      if self._cri.IsServiceRunning('ui'):
+        self._cri.RunCmdOnDevice(['restart', 'ui'])
+      else:
+        self._cri.RunCmdOnDevice(['start', 'ui'])
+
+  @property
+  def oobe(self):
+    return self.misc_web_contents_backend.GetOobe()
+
+  def _SigninUIState(self):
+    """Returns the signin ui state of the oobe. HIDDEN: 0, GAIA_SIGNIN: 1,
+    ACCOUNT_PICKER: 2, WRONG_HWID_WARNING: 3, MANAGED_USER_CREATION_FLOW: 4.
+    These values are in
+    chrome/browser/resources/chromeos/login/display_manager.js
+    """
+    return self.oobe.EvaluateJavaScript('''
+      loginHeader = document.getElementById('login-header-bar')
+      if (loginHeader) {
+        loginHeader.signinUIState_;
+      }
+    ''')
+
+  def _IsCryptohomeMounted(self):
+    """Returns True if a cryptohome vault is mounted at /home/chronos/user."""
+    return self._cri.FilesystemMountedAt('/home/chronos/user').startswith(
+        '/home/.shadow/')
+
+  def _HandleUserImageSelectionScreen(self):
+    """If we're stuck on the user image selection screen, we click the ok
+    button.
+    """
+    oobe = self.oobe
+    if oobe:
+      try:
+        oobe.EvaluateJavaScript("""
+            var ok = document.getElementById("ok-button");
+            if (ok) {
+              ok.click();
+            }
+        """)
+      except (exceptions.TabCrashException):
+        pass
+
+  def _IsLoggedIn(self):
+    """Returns True if we're logged in (cryptohome has mounted), and the oobe
+    has been dismissed."""
+    if self.chrome_branch_number <= 1547:
+      self._HandleUserImageSelectionScreen()
+    return self._IsCryptohomeMounted() and not self.oobe
+
+  def _StartupWindow(self):
+    """Closes the startup window, which is an extension on official builds,
+    and a webpage on chromiumos"""
+    startup_window_ext_id = 'honijodknafkokifofgiaalefdiedpko'
+    return (self.extension_dict_backend[startup_window_ext_id]
+        if startup_window_ext_id in self.extension_dict_backend
+        else self.tab_list_backend.Get(0, None))
+
+  def _WaitForAccountPicker(self):
+    """Waits for the oobe screen to be in the account picker state."""
+    util.WaitFor(lambda: self._SigninUIState() == 2, 20)
+
+  def _ClickBrowseAsGuest(self):
+    """Click the Browse As Guest button on the account picker screen. This will
+    restart the browser, and we could have a tab crash or a browser crash."""
+    try:
+      self.oobe.EvaluateJavaScript("""
+          var guest = document.getElementById("guest-user-button");
+          if (guest) {
+            guest.click();
+          }
+      """)
+    except (exceptions.TabCrashException,
+            exceptions.BrowserConnectionGoneException):
+      pass
+
+  def _WaitForGuestFsMounted(self):
+    """Waits for /home/chronos/user to be mounted as guestfs"""
+    util.WaitFor(lambda: (self._cri.FilesystemMountedAt('/home/chronos/user') ==
+                          'guestfs'), 20)
+
+  def _NavigateGuestLogin(self):
+    """Navigates through oobe login screen as guest"""
+    assert self.oobe
+    self._WaitForAccountPicker()
+    self._ClickBrowseAsGuest()
+    self._WaitForGuestFsMounted()
+
+  def _NavigateLogin(self):
+    """Navigates through oobe login screen"""
+    if self.chrome_branch_number > 1599:
+      util.WaitFor(lambda: self.oobe, 10)
+      util.WaitFor(lambda: self.oobe.EvaluateJavaScript(
+          'typeof Oobe !== \'undefined\''), 10)
+
+      if self.oobe.EvaluateJavaScript(
+          'typeof Oobe.loginForTesting == \'undefined\''):
+        raise exceptions.LoginException('Oobe.loginForTesting js api missing')
+
+      username = 'test@test.test'
+      password = ''
+      self.oobe.ExecuteJavaScript(
+          'Oobe.loginForTesting(\'%s\', \'%s\');' % (username, password))
+
+    try:
+      util.WaitFor(lambda: self._IsLoggedIn(), 60) # pylint: disable=W0108
+    except util.TimeoutException:
+      self._cri.TakeScreenShot('login-screen')
+      raise exceptions.LoginException(
+          'Timed out going through oobe screen. Make sure the custom auth '
+          'extension passed through --auth-ext-path is valid and belongs '
+          'to user "chronos".')
+
+    if self.chrome_branch_number < 1500:
+      # Wait for the startup window, then close it. Startup window doesn't exist
+      # post-M27. crrev.com/197900
+      util.WaitFor(lambda: self._StartupWindow() is not None, 20)
+      self._StartupWindow().Close()
+    else:
+      # Open a new window/tab.
+      self.tab_list_backend.New(15)
+
+
+class SSHForwarder(object):
+  def __init__(self, cri, forwarding_flag, *port_pairs):
+    self._proc = None
+
+    if forwarding_flag == 'R':
+      self._host_port = port_pairs[0].remote_port
+      command_line = ['-%s%i:localhost:%i' % (forwarding_flag,
+                                              port_pair.remote_port,
+                                              port_pair.local_port)
+                      for port_pair in port_pairs]
+    else:
+      self._host_port = port_pairs[0].local_port
+      command_line = ['-%s%i:localhost:%i' % (forwarding_flag,
+                                              port_pair.local_port,
+                                              port_pair.remote_port)
+                      for port_pair in port_pairs]
+
+    self._device_port = port_pairs[0].remote_port
+
+    self._proc = subprocess.Popen(
+      cri.FormSSHCommandLine(['sleep', '999999999'], command_line),
+      stdout=subprocess.PIPE,
+      stderr=subprocess.PIPE,
+      stdin=subprocess.PIPE,
+      shell=False)
+
+    util.WaitFor(lambda: cri.IsHTTPServerRunningOnPort(self._device_port), 60)
+
+  @property
+  def url(self):
+    assert self._proc
+    return 'http://localhost:%i' % self._host_port
+
+  def Close(self):
+    if self._proc:
+      self._proc.kill()
+      self._proc = None
+
diff --git a/tools/telemetry/telemetry/core/backends/chrome/cros_browser_finder.py b/tools/telemetry/telemetry/core/backends/chrome/cros_browser_finder.py
new file mode 100644
index 0000000..9782bac
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/cros_browser_finder.py
@@ -0,0 +1,112 @@
+# 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.
+"""Finds CrOS browsers that can be controlled by telemetry."""
+
+import logging
+
+from telemetry.core import browser
+from telemetry.core import possible_browser
+from telemetry.core.backends.chrome import cros_browser_backend
+from telemetry.core.backends.chrome import cros_interface
+from telemetry.core.platform import cros_platform_backend
+
+ALL_BROWSER_TYPES = ','.join([
+    'cros-chrome',
+    'cros-chrome-guest',
+    'system-guest',
+    ])
+
+class PossibleCrOSBrowser(possible_browser.PossibleBrowser):
+  """A launchable chromeos browser instance."""
+  def __init__(self, browser_type, finder_options, cri, is_guest):
+    super(PossibleCrOSBrowser, self).__init__(browser_type, finder_options)
+    self._cri = cri
+    self._is_guest = is_guest
+
+  def __repr__(self):
+    return 'PossibleCrOSBrowser(browser_type=%s)' % self.browser_type
+
+  def Create(self):
+    if self.finder_options.output_profile_path:
+      raise Exception("Profile generation is not currently supported on Chrome"
+          " OS")
+
+    backend = cros_browser_backend.CrOSBrowserBackend(
+        self.browser_type, self.finder_options, self._cri, self._is_guest)
+    b = browser.Browser(backend,
+                        cros_platform_backend.CrosPlatformBackend(self._cri))
+    return b
+
+  def SupportsOptions(self, finder_options):
+    if (len(finder_options.extensions_to_load) != 0) and self._is_guest:
+      return False
+    return True
+
+def SelectDefaultBrowser(possible_browsers):
+  if cros_interface.IsRunningOnCrosDevice():
+    for b in possible_browsers:
+      if b.browser_type == 'system':
+        return b
+  return None
+
+def CanFindAvailableBrowsers(finder_options):
+  return (cros_interface.IsRunningOnCrosDevice() or
+          finder_options.cros_remote or
+          cros_interface.HasSSH())
+
+def FindAllAvailableBrowsers(finder_options):
+  """Finds all available chromeos browsers, locally and remotely."""
+  if cros_interface.IsRunningOnCrosDevice():
+    return [PossibleCrOSBrowser('system', finder_options,
+                                cros_interface.CrOSInterface(),
+                                is_guest=False),
+            PossibleCrOSBrowser('system-guest', finder_options,
+                                cros_interface.CrOSInterface(),
+                                is_guest=True)]
+
+  if finder_options.cros_remote == None:
+    logging.debug('No --remote specified, will not probe for CrOS.')
+    return []
+
+  if not cros_interface.HasSSH():
+    logging.debug('ssh not found. Cannot talk to CrOS devices.')
+    return []
+  cri = cros_interface.CrOSInterface(finder_options.cros_remote,
+                                     finder_options.cros_ssh_identity)
+
+  # Check ssh
+  try:
+    cri.TryLogin()
+  except cros_interface.LoginException, ex:
+    if isinstance(ex, cros_interface.KeylessLoginRequiredException):
+      logging.warn('Could not ssh into %s. Your device must be configured',
+                   finder_options.cros_remote)
+      logging.warn('to allow passwordless login as root.')
+      logging.warn('For a test-build device, pass this to your script:')
+      logging.warn('   --identity $(CHROMITE)/ssh_keys/testing_rsa')
+      logging.warn('')
+      logging.warn('For a developer-mode device, the steps are:')
+      logging.warn(' - Ensure you have an id_rsa.pub (etc) on this computer')
+      logging.warn(' - On the chromebook:')
+      logging.warn('   -  Control-Alt-T; shell; sudo -s')
+      logging.warn('   -  openssh-server start')
+      logging.warn('   -  scp <this machine>:.ssh/id_rsa.pub /tmp/')
+      logging.warn('   -  mkdir /root/.ssh')
+      logging.warn('   -  chown go-rx /root/.ssh')
+      logging.warn('   -  cat /tmp/id_rsa.pub >> /root/.ssh/authorized_keys')
+      logging.warn('   -  chown 0600 /root/.ssh/authorized_keys')
+      logging.warn('There, that was easy!')
+      logging.warn('')
+      logging.warn('P.S. Please, tell your manager how INANE this is.')
+
+    from telemetry.core import browser_finder
+    raise browser_finder.BrowserFinderException(str(ex))
+
+  if not cri.FileExistsOnDevice('/opt/google/chrome/chrome'):
+    logging.warn('Could not find a chrome on ' % cri.hostname)
+
+  return [PossibleCrOSBrowser('cros-chrome', finder_options, cri,
+                              is_guest=False),
+          PossibleCrOSBrowser('cros-chrome-guest', finder_options, cri,
+                              is_guest=True)]
diff --git a/tools/telemetry/telemetry/core/backends/chrome/cros_browser_finder_unittest.py b/tools/telemetry/telemetry/core/backends/chrome/cros_browser_finder_unittest.py
new file mode 100644
index 0000000..e4acc65
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/cros_browser_finder_unittest.py
@@ -0,0 +1,11 @@
+# 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.
+
+# TODO(nduca): Add basic unit test for cros_browser_finder.
+#
+# Here, we should mock the cros_interface module (assuming its working) and
+# verify that the finder does the right thing. Because the finder delegates most
+# of its work to the CRI, the test code here is going to be comparatively
+# simple.
+
diff --git a/tools/telemetry/telemetry/core/backends/chrome/cros_interface.py b/tools/telemetry/telemetry/core/backends/chrome/cros_interface.py
new file mode 100644
index 0000000..a6c1457
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/cros_interface.py
@@ -0,0 +1,345 @@
+# 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.
+"""A wrapper around ssh for common operations on a CrOS-based device"""
+import logging
+import os
+import re
+import subprocess
+import sys
+import tempfile
+
+# TODO(nduca): This whole file is built up around making individual ssh calls
+# for each operation. It really could get away with a single ssh session built
+# around pexpect, I suspect, if we wanted it to be faster. But, this was
+# convenient.
+
+def IsRunningOnCrosDevice():
+  """Returns True if we're on a ChromeOS device."""
+  lsb_release = '/etc/lsb-release'
+  if sys.platform.startswith('linux') and os.path.exists(lsb_release):
+    with open(lsb_release, 'r') as f:
+      res = f.read()
+      if res.count('CHROMEOS_RELEASE_NAME'):
+        return True
+  return False
+
+def RunCmd(args, cwd=None, quiet=False):
+  """Opens a subprocess to execute a program and returns its return value.
+
+  Args:
+    args: A string or a sequence of program arguments. The program to execute is
+      the string or the first item in the args sequence.
+    cwd: If not None, the subprocess's current directory will be changed to
+      |cwd| before it's executed.
+
+  Returns:
+    Return code from the command execution.
+  """
+  if not quiet:
+    logging.debug(' '.join(args) + ' ' + (cwd or ''))
+  with open(os.devnull, 'w') as devnull:
+    p = subprocess.Popen(args=args, cwd=cwd, stdout=devnull,
+                         stderr=devnull, stdin=devnull, shell=False)
+    return p.wait()
+
+def GetAllCmdOutput(args, cwd=None, quiet=False):
+  """Open a subprocess to execute a program and returns its output.
+
+  Args:
+    args: A string or a sequence of program arguments. The program to execute is
+      the string or the first item in the args sequence.
+    cwd: If not None, the subprocess's current directory will be changed to
+      |cwd| before it's executed.
+
+  Returns:
+    Captures and returns the command's stdout.
+    Prints the command's stderr to logger (which defaults to stdout).
+  """
+  if not quiet:
+    logging.debug(' '.join(args) + ' ' + (cwd or ''))
+  with open(os.devnull, 'w') as devnull:
+    p = subprocess.Popen(args=args, cwd=cwd, stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE, stdin=devnull)
+    stdout, stderr = p.communicate()
+    if not quiet:
+      logging.debug(' > stdout=[%s], stderr=[%s]', stdout, stderr)
+    return stdout, stderr
+
+def HasSSH():
+  try:
+    RunCmd(['ssh'], quiet=True)
+    RunCmd(['scp'], quiet=True)
+    logging.debug("HasSSH()->True")
+    return True
+  except OSError:
+    logging.debug("HasSSH()->False")
+    return False
+
+class LoginException(Exception):
+  pass
+
+class KeylessLoginRequiredException(LoginException):
+  pass
+
+class CrOSInterface(object):
+  # pylint: disable=R0923
+  def __init__(self, hostname = None, ssh_identity = None):
+    self._hostname = hostname
+    # List of ports generated from GetRemotePort() that may not be in use yet.
+    self._reserved_ports = []
+
+    if self.local:
+      return
+
+    self._ssh_identity = None
+    self._hostfile = tempfile.NamedTemporaryFile()
+    self._hostfile.flush()
+    self._ssh_args = ['-o ConnectTimeout=5',
+                      '-o StrictHostKeyChecking=no',
+                      '-o KbdInteractiveAuthentication=no',
+                      '-o PreferredAuthentications=publickey',
+                      '-o UserKnownHostsFile=%s' % self._hostfile.name]
+
+    if ssh_identity:
+      self._ssh_identity = os.path.abspath(os.path.expanduser(ssh_identity))
+
+  @property
+  def local(self):
+    return not self._hostname
+
+  @property
+  def hostname(self):
+    return self._hostname
+
+  def FormSSHCommandLine(self, args, extra_ssh_args=None):
+    if self.local:
+      # We run the command through the shell locally for consistency with
+      # how commands are run through SSH (crbug.com/239161). This work
+      # around will be unnecessary once we implement a persistent SSH
+      # connection to run remote commands (crbug.com/239607).
+      return ['sh', '-c', " ".join(args)]
+
+    full_args = ['ssh',
+                 '-o ForwardX11=no',
+                 '-o ForwardX11Trusted=no',
+                 '-n'] + self._ssh_args
+    if self._ssh_identity is not None:
+      full_args.extend(['-i', self._ssh_identity])
+    if extra_ssh_args:
+      full_args.extend(extra_ssh_args)
+    full_args.append('root@%s' % self._hostname)
+    full_args.extend(args)
+    return full_args
+
+  def _RemoveSSHWarnings(self, toClean):
+    """Removes specific ssh warning lines from a string.
+
+    Args:
+      toClean: A string that may be containing multiple lines.
+
+    Returns:
+      A copy of toClean with all the Warning lines removed.
+    """
+    # Remove the Warning about connecting to a new host for the first time.
+    return re.sub('Warning: Permanently added [^\n]* to the list of known '
+                  'hosts.\s\n', '', toClean)
+
+  def RunCmdOnDevice(self, args, cwd=None, quiet=False):
+    stdout, stderr = GetAllCmdOutput(
+        self.FormSSHCommandLine(args), cwd, quiet=quiet)
+    # The initial login will add the host to the hosts file but will also print
+    # a warning to stderr that we need to remove.
+    stderr = self._RemoveSSHWarnings(stderr)
+    return stdout, stderr
+
+  def TryLogin(self):
+    logging.debug('TryLogin()')
+    assert not self.local
+    stdout, stderr = self.RunCmdOnDevice(['echo', '$USER'], quiet=True)
+    if stderr != '':
+      if 'Host key verification failed' in stderr:
+        raise LoginException(('%s host key verification failed. ' +
+                             'SSH to it manually to fix connectivity.') %
+            self._hostname)
+      if 'Operation timed out' in stderr:
+        raise LoginException('Timed out while logging into %s' % self._hostname)
+      if 'UNPROTECTED PRIVATE KEY FILE!' in stderr:
+        raise LoginException('Permissions for %s are too open. To fix this,\n'
+                             'chmod 600 %s' % (self._ssh_identity,
+                                               self._ssh_identity))
+      if 'Permission denied (publickey,keyboard-interactive)' in stderr:
+        raise KeylessLoginRequiredException(
+          'Need to set up ssh auth for %s' % self._hostname)
+      raise LoginException('While logging into %s, got %s' % (
+          self._hostname, stderr))
+    if stdout != 'root\n':
+      raise LoginException(
+        'Logged into %s, expected $USER=root, but got %s.' % (
+          self._hostname, stdout))
+
+  def FileExistsOnDevice(self, file_name):
+    if self.local:
+      return os.path.exists(file_name)
+
+    stdout, stderr = self.RunCmdOnDevice([
+        'if', 'test', '-e', file_name, ';',
+        'then', 'echo', '1', ';',
+        'fi'
+        ], quiet=True)
+    if stderr != '':
+      if "Connection timed out" in stderr:
+        raise OSError('Machine wasn\'t responding to ssh: %s' %
+                      stderr)
+      raise OSError('Unepected error: %s' % stderr)
+    exists = stdout == '1\n'
+    logging.debug("FileExistsOnDevice(<text>, %s)->%s" % (file_name, exists))
+    return exists
+
+  def PushFile(self, filename, remote_filename):
+    if self.local:
+      args = ['cp', '-r', filename, remote_filename]
+      stdout, stderr = GetAllCmdOutput(args, quiet=True)
+      if stderr != '':
+        raise OSError('No such file or directory %s' % stderr)
+      return
+
+    args = ['scp', '-r' ] + self._ssh_args
+    if self._ssh_identity:
+      args.extend(['-i', self._ssh_identity])
+
+    args.extend([os.path.abspath(filename),
+                 'root@%s:%s' % (self._hostname, remote_filename)])
+
+    stdout, stderr = GetAllCmdOutput(args, quiet=True)
+    stderr = self._RemoveSSHWarnings(stderr)
+    if stderr != '':
+      raise OSError('No such file or directory %s' % stderr)
+
+  def PushContents(self, text, remote_filename):
+    logging.debug("PushContents(<text>, %s)" % remote_filename)
+    with tempfile.NamedTemporaryFile() as f:
+      f.write(text)
+      f.flush()
+      self.PushFile(f.name, remote_filename)
+
+  def GetFileContents(self, filename):
+    assert not self.local
+    with tempfile.NamedTemporaryFile() as f:
+      args = ['scp'] + self._ssh_args
+      if self._ssh_identity:
+        args.extend(['-i', self._ssh_identity])
+
+      args.extend(['root@%s:%s' % (self._hostname, filename),
+                   os.path.abspath(f.name)])
+
+      stdout, stderr = GetAllCmdOutput(args, quiet=True)
+      stderr = self._RemoveSSHWarnings(stderr)
+
+      if stderr != '':
+        raise OSError('No such file or directory %s' % stderr)
+
+      with open(f.name, 'r') as f2:
+        res = f2.read()
+        logging.debug("GetFileContents(%s)->%s" % (filename, res))
+        return res
+
+  def ListProcesses(self):
+    """Returns (pid, cmd, ppid, state) of all processes on the device."""
+    stdout, stderr = self.RunCmdOnDevice([
+        '/bin/ps', '--no-headers',
+        '-A',
+        '-o', 'pid,ppid,args,state'], quiet=True)
+    assert stderr == '', stderr
+    procs = []
+    for l in stdout.split('\n'): # pylint: disable=E1103
+      if l == '':
+        continue
+      m = re.match('^\s*(\d+)\s+(\d+)\s+(.+)\s+(.+)', l, re.DOTALL)
+      assert m
+      procs.append((int(m.group(1)), m.group(3), int(m.group(2)), m.group(4)))
+    logging.debug("ListProcesses(<predicate>)->[%i processes]" % len(procs))
+    return procs
+
+  def RmRF(self, filename):
+    logging.debug("rm -rf %s" % filename)
+    self.RunCmdOnDevice(['rm', '-rf', filename], quiet=True)
+
+  def KillAllMatching(self, predicate):
+    kills = ['kill', '-KILL']
+    for pid, cmd, _, _ in self.ListProcesses():
+      if predicate(cmd):
+        logging.info('Killing %s, pid %d' % cmd, pid)
+        kills.append(pid)
+    logging.debug("KillAllMatching(<predicate>)->%i" % (len(kills) - 2))
+    if len(kills) > 2:
+      self.RunCmdOnDevice(kills, quiet=True)
+    return len(kills) - 2
+
+  def IsServiceRunning(self, service_name):
+    stdout, stderr = self.RunCmdOnDevice([
+        'status', service_name], quiet=True)
+    assert stderr == '', stderr
+    running = 'running, process' in stdout
+    logging.debug("IsServiceRunning(%s)->%s" % (service_name, running))
+    return running
+
+  def GetRemotePort(self):
+    netstat = self.RunCmdOnDevice(['netstat', '-ant'])
+    netstat = netstat[0].split('\n')
+    ports_in_use = []
+
+    for line in netstat[2:]:
+      if not line:
+        continue
+      address_in_use = line.split()[3]
+      port_in_use = address_in_use.split(':')[-1]
+      ports_in_use.append(int(port_in_use))
+
+    ports_in_use.extend(self._reserved_ports)
+
+    new_port = sorted(ports_in_use)[-1] + 1
+    self._reserved_ports.append(new_port)
+
+    return new_port
+
+  def IsHTTPServerRunningOnPort(self, port):
+    wget_output = self.RunCmdOnDevice(
+        ['wget', 'localhost:%i' % (port), '-T1', '-t1'])
+
+    if 'Connection refused' in wget_output[1]:
+      return False
+
+    return True
+
+  def FilesystemMountedAt(self, path):
+    """Returns the filesystem mounted at |path|"""
+    df_out, _ = self.RunCmdOnDevice(['/bin/df', path])
+    df_ary = df_out.split('\n')
+    # 3 lines for title, mount info, and empty line.
+    if len(df_ary) == 3:
+      line_ary = df_ary[1].split()
+      if line_ary:
+        return line_ary[0]
+    return None
+
+  def TakeScreenShot(self, screenshot_prefix):
+    """Takes a screenshot, useful for debugging failures."""
+    # TODO(achuith): Find a better location for screenshots. Cros autotests
+    # upload everything in /var/log so use /var/log/screenshots for now.
+    SCREENSHOT_DIR = '/var/log/screenshots/'
+    SCREENSHOT_EXT = '.png'
+
+    self.RunCmdOnDevice(['mkdir', '-p', SCREENSHOT_DIR])
+    for i in xrange(25):
+      screenshot_file = ('%s%s-%d%s' %
+                         (SCREENSHOT_DIR, screenshot_prefix, i, SCREENSHOT_EXT))
+      if not self.FileExistsOnDevice(screenshot_file):
+        self.RunCmdOnDevice([
+            'DISPLAY=:0.0 XAUTHORITY=/home/chronos/.Xauthority '
+            '/usr/local/bin/import',
+            '-window root',
+            '-depth 8',
+            screenshot_file])
+        return
+    logging.warning('screenshot directory full.')
diff --git a/tools/telemetry/telemetry/core/backends/chrome/cros_interface_unittest.py b/tools/telemetry/telemetry/core/backends/chrome/cros_interface_unittest.py
new file mode 100644
index 0000000..4eaa8cd
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/cros_interface_unittest.py
@@ -0,0 +1,158 @@
+# 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.
+
+# TODO(nduca): Rewrite what some of these tests to use mocks instead of
+# actually talking to the device. This would improve our coverage quite
+# a bit.
+import unittest
+import socket
+import sys
+
+from telemetry.core import util
+from telemetry.core.backends.chrome import cros_browser_backend
+from telemetry.core.backends.chrome import cros_interface
+from telemetry.unittest import options_for_unittests
+from telemetry.unittest import RequiresBrowserOfType
+
+class CrOSInterfaceTest(unittest.TestCase):
+  @RequiresBrowserOfType('cros-chrome')
+  def testPushContents(self):
+    remote = options_for_unittests.GetCopy().cros_remote
+    cri = cros_interface.CrOSInterface(
+      remote,
+      options_for_unittests.GetCopy().cros_ssh_identity)
+    cri.RunCmdOnDevice(['rm', '-rf', '/tmp/testPushContents'])
+    cri.PushContents('hello world', '/tmp/testPushContents')
+    contents = cri.GetFileContents('/tmp/testPushContents')
+    self.assertEquals(contents, 'hello world')
+
+  @RequiresBrowserOfType('cros-chrome')
+  def testExists(self):
+    remote = options_for_unittests.GetCopy().cros_remote
+    cri = cros_interface.CrOSInterface(
+      remote,
+      options_for_unittests.GetCopy().cros_ssh_identity)
+    self.assertTrue(cri.FileExistsOnDevice('/proc/cpuinfo'))
+    self.assertTrue(cri.FileExistsOnDevice('/etc/passwd'))
+    self.assertFalse(cri.FileExistsOnDevice('/etc/sdlfsdjflskfjsflj'))
+
+  def testExistsLocal(self):
+    if not sys.platform.startswith('linux'):
+      return
+
+    cri = cros_interface.CrOSInterface()
+    self.assertTrue(cri.FileExistsOnDevice('/proc/cpuinfo'))
+    self.assertTrue(cri.FileExistsOnDevice('/etc/passwd'))
+    self.assertFalse(cri.FileExistsOnDevice('/etc/sdlfsdjflskfjsflj'))
+
+  @RequiresBrowserOfType('cros-chrome')
+  def testGetFileContents(self): # pylint: disable=R0201
+    remote = options_for_unittests.GetCopy().cros_remote
+    cri = cros_interface.CrOSInterface(
+      remote,
+      options_for_unittests.GetCopy().cros_ssh_identity)
+    hosts = cri.GetFileContents('/etc/hosts')
+    assert hosts.startswith('# /etc/hosts')
+
+  @RequiresBrowserOfType('cros-chrome')
+  def testGetFileContentsForSomethingThatDoesntExist(self):
+    remote = options_for_unittests.GetCopy().cros_remote
+    cri = cros_interface.CrOSInterface(
+      remote,
+      options_for_unittests.GetCopy().cros_ssh_identity)
+    self.assertRaises(
+      OSError,
+      lambda: cri.GetFileContents('/tmp/209fuslfskjf/dfsfsf'))
+
+  @RequiresBrowserOfType('cros-chrome')
+  def testIsServiceRunning(self):
+    remote = options_for_unittests.GetCopy().cros_remote
+    cri = cros_interface.CrOSInterface(
+      remote,
+      options_for_unittests.GetCopy().cros_ssh_identity)
+
+    self.assertTrue(cri.IsServiceRunning('openssh-server'))
+
+  def testIsServiceRunningLocal(self):
+    if not sys.platform.startswith('linux'):
+      return
+    cri = cros_interface.CrOSInterface()
+    self.assertTrue(cri.IsServiceRunning('dbus'))
+
+  @RequiresBrowserOfType('cros-chrome')
+  def testGetRemotePortAndIsHTTPServerRunningOnPort(self):
+    remote = options_for_unittests.GetCopy().cros_remote
+    cri = cros_interface.CrOSInterface(
+      remote,
+      options_for_unittests.GetCopy().cros_ssh_identity)
+
+    # Create local server.
+    sock = socket.socket()
+    sock.bind(('', 0))
+    port = sock.getsockname()[1]
+    sock.listen(0)
+
+    # Get remote port and ensure that it was unused.
+    remote_port = cri.GetRemotePort()
+    self.assertFalse(cri.IsHTTPServerRunningOnPort(remote_port))
+
+    # Forward local server's port to remote device's remote_port.
+    forwarder = cros_browser_backend.SSHForwarder(
+        cri, 'R', util.PortPair(port, remote_port))
+
+    # At this point, remote device should be able to connect to local server.
+    self.assertTrue(cri.IsHTTPServerRunningOnPort(remote_port))
+
+    # Next remote port shouldn't be the same as remote_port, since remote_port
+    # is now in use.
+    self.assertTrue(cri.GetRemotePort() != remote_port)
+
+    # Close forwarder and local server ports.
+    forwarder.Close()
+    sock.close()
+
+    # Device should no longer be able to connect to remote_port since it is no
+    # longer in use.
+    self.assertFalse(cri.IsHTTPServerRunningOnPort(remote_port))
+
+  @RequiresBrowserOfType('cros-chrome')
+  def testGetRemotePortReservedPorts(self):
+    remote = options_for_unittests.GetCopy().cros_remote
+    cri = cros_interface.CrOSInterface(
+      remote,
+      options_for_unittests.GetCopy().cros_ssh_identity)
+
+    # Should return 2 separate ports even though the first one isn't technically
+    # being used yet.
+    remote_port_1 = cri.GetRemotePort()
+    remote_port_2 = cri.GetRemotePort()
+
+    self.assertTrue(remote_port_1 != remote_port_2)
+
+  # TODO(tengs): It would be best if we can filter this test and other tests
+  # that need to be run locally based on the platform of the system browser.
+  def testEscapeCmdArguments(self):
+    ''' Commands and their arguments that are executed through the cros
+    interface should follow bash syntax. This test needs to run on remotely
+    and locally on the device to check for consistency.
+    '''
+    if not sys.platform.startswith('linux'):
+      return
+
+    cri = cros_interface.CrOSInterface(
+      options_for_unittests.GetCopy().cros_remote,
+      options_for_unittests.GetCopy().cros_ssh_identity)
+
+    # Check arguments with no special characters
+    stdout, _ = cri.RunCmdOnDevice(['echo', '--arg1=value1', '--arg2=value2',
+        '--arg3="value3"'])
+    assert(stdout.strip() == '--arg1=value1 --arg2=value2 --arg3=value3')
+
+    # Check argument with special characters escaped
+    stdout, _ = cri.RunCmdOnDevice(['echo', '--arg=A\\; echo \\"B\\"'])
+    assert(stdout.strip() == '--arg=A; echo "B"')
+
+    # Check argument with special characters in quotes
+    stdout, _ = cri.RunCmdOnDevice(['echo', "--arg='$HOME;;$PATH'"])
+    assert(stdout.strip() == "--arg=$HOME;;$PATH")
diff --git a/tools/telemetry/telemetry/core/backends/chrome/crx_id.py b/tools/telemetry/telemetry/core/backends/chrome/crx_id.py
new file mode 100644
index 0000000..427c5d4
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/crx_id.py
@@ -0,0 +1,13 @@
+# 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.
+from __future__ import absolute_import
+
+from telemetry.core import util
+
+util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'tools')
+from crx_id import crx_id  # pylint: disable=F0401
+
+
+GetCRXAppID = crx_id.GetCRXAppID
+HasPublicKey = crx_id.HasPublicKey
diff --git a/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_backend.py b/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_backend.py
new file mode 100644
index 0000000..fe739ee
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_backend.py
@@ -0,0 +1,247 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import glob
+import heapq
+import logging
+import os
+import subprocess as subprocess
+import shutil
+import sys
+import tempfile
+import time
+
+from telemetry.core import util
+from telemetry.core.backends import browser_backend
+from telemetry.core.backends.chrome import chrome_browser_backend
+
+class DesktopBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
+  """The backend for controlling a locally-executed browser instance, on Linux,
+  Mac or Windows.
+  """
+  def __init__(self, options, executable, flash_path, is_content_shell,
+               browser_directory, output_profile_path=None):
+    super(DesktopBrowserBackend, self).__init__(
+        is_content_shell=is_content_shell,
+        supports_extensions=not is_content_shell,
+        options=options)
+
+    # Initialize fields so that an explosion during init doesn't break in Close.
+    self._proc = None
+    self._tmp_profile_dir = None
+    self._tmp_output_file = None
+
+    self._executable = executable
+    if not self._executable:
+      raise Exception('Cannot create browser, no executable found!')
+
+    self._flash_path = flash_path
+    if self._flash_path and not os.path.exists(self._flash_path):
+      logging.warning(('Could not find flash at %s. Running without flash.\n\n'
+                       'To fix this see http://go/read-src-internal') %
+                      self._flash_path)
+      self._flash_path = None
+
+    if len(options.extensions_to_load) > 0 and is_content_shell:
+      raise browser_backend.ExtensionsNotSupportedException(
+          'Content shell does not support extensions.')
+
+    self._browser_directory = browser_directory
+    self._port = util.GetAvailableLocalPort()
+    self._profile_dir = None
+    self._supports_net_benchmarking = True
+    self._output_profile_path = output_profile_path
+    self._tmp_minidump_dir = tempfile.mkdtemp()
+
+    self._SetupProfile()
+
+  def _SetupProfile(self):
+    if not self.options.dont_override_profile:
+      if self._output_profile_path:
+        # If both |_output_profile_path| and |profile_dir| are specified then
+        # the calling code will throw an exception, so we don't need to worry
+        # about that case here.
+        self._tmp_profile_dir = self._output_profile_path
+      else:
+        self._tmp_profile_dir = tempfile.mkdtemp()
+      profile_dir = self._profile_dir or self.options.profile_dir
+      if profile_dir:
+        if self.is_content_shell:
+          logging.critical('Profiles cannot be used with content shell')
+          sys.exit(1)
+        logging.info("Using profile directory:'%s'." % profile_dir)
+        shutil.rmtree(self._tmp_profile_dir)
+        shutil.copytree(profile_dir, self._tmp_profile_dir)
+
+  def _LaunchBrowser(self):
+    args = [self._executable]
+    args.extend(self.GetBrowserStartupArgs())
+    env = os.environ.copy()
+    env['CHROME_HEADLESS'] = '1'  # Don't upload minidumps.
+    env['BREAKPAD_DUMP_LOCATION'] = self._tmp_minidump_dir
+    if not self.options.show_stdout:
+      self._tmp_output_file = tempfile.NamedTemporaryFile('w', 0)
+      self._proc = subprocess.Popen(
+          args, stdout=self._tmp_output_file, stderr=subprocess.STDOUT, env=env)
+    else:
+      self._proc = subprocess.Popen(args, env=env)
+
+    try:
+      self._WaitForBrowserToComeUp()
+      self._PostBrowserStartupInitialization()
+    except:
+      self.Close()
+      raise
+
+  def GetBrowserStartupArgs(self):
+    args = super(DesktopBrowserBackend, self).GetBrowserStartupArgs()
+    args.append('--remote-debugging-port=%i' % self._port)
+    args.append('--enable-crash-reporter-for-testing')
+    if not self.is_content_shell:
+      args.append('--window-size=1280,1024')
+      if self._flash_path:
+        args.append('--ppapi-flash-path=%s' % self._flash_path)
+      if self._supports_net_benchmarking:
+        args.append('--enable-net-benchmarking')
+      else:
+        args.append('--enable-benchmarking')
+      if not self.options.dont_override_profile:
+        args.append('--user-data-dir=%s' % self._tmp_profile_dir)
+    return args
+
+  def SetProfileDirectory(self, profile_dir):
+    # Make sure _profile_dir hasn't already been set.
+    assert self._profile_dir is None
+
+    if self.is_content_shell:
+      logging.critical('Profile creation cannot be used with content shell')
+      sys.exit(1)
+
+    self._profile_dir = profile_dir
+
+  def Start(self):
+    self._LaunchBrowser()
+
+    # For old chrome versions, might have to relaunch to have the
+    # correct net_benchmarking switch.
+    if self.chrome_branch_number < 1418:
+      self.Close()
+      self._supports_net_benchmarking = False
+      self._LaunchBrowser()
+
+  @property
+  def pid(self):
+    if self._proc:
+      return self._proc.pid
+    return None
+
+  @property
+  def browser_directory(self):
+    return self._browser_directory
+
+  @property
+  def profile_directory(self):
+    return self._tmp_profile_dir
+
+  def IsBrowserRunning(self):
+    return self._proc.poll() == None
+
+  def GetStandardOutput(self):
+    assert self._tmp_output_file, "Can't get standard output with show_stdout"
+    self._tmp_output_file.flush()
+    try:
+      with open(self._tmp_output_file.name) as f:
+        return f.read()
+    except IOError:
+      return ''
+
+  def GetStackTrace(self):
+    stackwalk = util.FindSupportBinary('minidump_stackwalk')
+    if not stackwalk:
+      logging.warning('minidump_stackwalk binary not found. Must build it to '
+                      'symbolize crash dumps. Returning browser stdout.')
+      return self.GetStandardOutput()
+
+    dumps = glob.glob(os.path.join(self._tmp_minidump_dir, '*.dmp'))
+    if not dumps:
+      logging.warning('No crash dump found. Returning browser stdout.')
+      return self.GetStandardOutput()
+    most_recent_dump = heapq.nlargest(1, dumps, os.path.getmtime)[0]
+    if os.path.getmtime(most_recent_dump) < (time.time() - (5 * 60)):
+      logging.warn('Crash dump is older than 5 minutes. May not be correct.')
+
+    minidump = most_recent_dump + '.stripped'
+    with open(most_recent_dump, 'rb') as infile:
+      with open(minidump, 'wb') as outfile:
+        outfile.write(''.join(infile.read().partition('MDMP')[1:]))
+
+    symbols = glob.glob(os.path.join(os.path.dirname(stackwalk), '*.breakpad*'))
+    if not symbols:
+      logging.warning('No breakpad symbols found. Returning browser stdout.')
+      return self.GetStandardOutput()
+
+    symbols_path = os.path.join(self._tmp_minidump_dir, 'symbols')
+    for symbol in symbols:
+      with open(symbol, 'r') as f:
+        fields = f.readline().split()
+        if not fields:
+          continue
+        sha = fields[3]
+        binary = ' '.join(fields[4:])
+      symbol_path = os.path.join(symbols_path, binary, sha)
+      os.makedirs(symbol_path)
+      shutil.copyfile(symbol, os.path.join(symbol_path, binary + '.sym'))
+
+    error = tempfile.NamedTemporaryFile('w', 0)
+    return subprocess.Popen(
+        [stackwalk, minidump, symbols_path],
+        stdout=subprocess.PIPE, stderr=error).communicate()[0]
+
+  def __del__(self):
+    self.Close()
+
+  def Close(self):
+    super(DesktopBrowserBackend, self).Close()
+
+    if self._proc:
+
+      def IsClosed():
+        if not self._proc:
+          return True
+        return self._proc.poll() != None
+
+      # Try to politely shutdown, first.
+      self._proc.terminate()
+      try:
+        util.WaitFor(IsClosed, timeout=1)
+        self._proc = None
+      except util.TimeoutException:
+        pass
+
+      # Kill it.
+      if not IsClosed():
+        self._proc.kill()
+        try:
+          util.WaitFor(IsClosed, timeout=5)
+          self._proc = None
+        except util.TimeoutException:
+          self._proc = None
+          raise Exception('Could not shutdown the browser.')
+
+    if self._output_profile_path:
+      # If we need the output then double check that it exists.
+      if not (self._tmp_profile_dir and os.path.exists(self._tmp_profile_dir)):
+        raise Exception("No profile directory generated by Chrome: '%s'." %
+            self._tmp_profile_dir)
+    else:
+      # If we don't need the profile after the run then cleanup.
+      if self._tmp_profile_dir and os.path.exists(self._tmp_profile_dir):
+        shutil.rmtree(self._tmp_profile_dir, ignore_errors=True)
+        self._tmp_profile_dir = None
+
+    if self._tmp_output_file:
+      self._tmp_output_file.close()
+      self._tmp_output_file = None
+
+  def CreateForwarder(self, *port_pairs):
+    return browser_backend.DoNothingForwarder(*port_pairs)
diff --git a/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_finder.py b/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_finder.py
new file mode 100644
index 0000000..02db73e
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_finder.py
@@ -0,0 +1,228 @@
+# 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.
+"""Finds desktop browsers that can be controlled by telemetry."""
+
+import logging
+from operator import attrgetter
+import os
+import platform
+import subprocess
+import sys
+
+from telemetry.core import browser
+from telemetry.core import platform as core_platform
+from telemetry.core import possible_browser
+from telemetry.core import util
+from telemetry.core.backends.chrome import cros_interface
+from telemetry.core.backends.chrome import desktop_browser_backend
+
+ALL_BROWSER_TYPES = ','.join([
+    'exact',
+    'release',
+    'release_x64',
+    'debug',
+    'debug_x64',
+    'canary',
+    'content-shell-debug',
+    'content-shell-release',
+    'system'])
+
+class PossibleDesktopBrowser(possible_browser.PossibleBrowser):
+  """A desktop browser that can be controlled."""
+
+  def __init__(self, browser_type, finder_options, executable, flash_path,
+               is_content_shell, browser_directory, is_local_build=False):
+    super(PossibleDesktopBrowser, self).__init__(browser_type, finder_options)
+    self._local_executable = executable
+    self._flash_path = flash_path
+    self._is_content_shell = is_content_shell
+    self._browser_directory = browser_directory
+    self.is_local_build = is_local_build
+
+  def __repr__(self):
+    return 'PossibleDesktopBrowser(browser_type=%s)' % self.browser_type
+
+  def Create(self):
+    backend = desktop_browser_backend.DesktopBrowserBackend(
+        self.finder_options, self._local_executable, self._flash_path,
+        self._is_content_shell, self._browser_directory,
+        output_profile_path=self.finder_options.output_profile_path)
+    b = browser.Browser(backend,
+                        core_platform.CreatePlatformBackendForCurrentOS())
+    return b
+
+  def SupportsOptions(self, finder_options):
+    if (len(finder_options.extensions_to_load) != 0) and self._is_content_shell:
+      return False
+    return True
+
+  @property
+  def last_modification_time(self):
+    if os.path.exists(self._local_executable):
+      return os.path.getmtime(self._local_executable)
+    return -1
+
+def SelectDefaultBrowser(possible_browsers):
+  local_builds_by_date = [
+      b for b in sorted(possible_browsers,
+                        key=attrgetter('last_modification_time'))
+      if b.is_local_build]
+  if local_builds_by_date:
+    return local_builds_by_date[-1]
+  return None
+
+def CanFindAvailableBrowsers():
+  return not cros_interface.IsRunningOnCrosDevice()
+
+def FindAllAvailableBrowsers(finder_options):
+  """Finds all the desktop browsers available on this machine."""
+  browsers = []
+
+  if not CanFindAvailableBrowsers():
+    return []
+
+  has_display = True
+  if (sys.platform.startswith('linux') and
+      os.getenv('DISPLAY') == None):
+    has_display = False
+
+  # Look for a browser in the standard chrome build locations.
+  if finder_options.chrome_root:
+    chrome_root = finder_options.chrome_root
+  else:
+    chrome_root = util.GetChromiumSrcDir()
+
+  if sys.platform == 'darwin':
+    chromium_app_name = 'Chromium.app/Contents/MacOS/Chromium'
+    content_shell_app_name = 'Content Shell.app/Contents/MacOS/Content Shell'
+    mac_dir = 'mac'
+    if platform.architecture()[0] == '64bit':
+      mac_dir = 'mac_64'
+    flash_path = os.path.join(
+        chrome_root, 'third_party', 'adobe', 'flash', 'binaries', 'ppapi',
+        mac_dir, 'PepperFlashPlayer.plugin')
+  elif sys.platform.startswith('linux'):
+    chromium_app_name = 'chrome'
+    content_shell_app_name = 'content_shell'
+    linux_dir = 'linux'
+    if platform.architecture()[0] == '64bit':
+      linux_dir = 'linux_x64'
+    flash_path = os.path.join(
+        chrome_root, 'third_party', 'adobe', 'flash', 'binaries', 'ppapi',
+        linux_dir, 'libpepflashplayer.so')
+  elif sys.platform.startswith('win'):
+    chromium_app_name = 'chrome.exe'
+    content_shell_app_name = 'content_shell.exe'
+    win_dir = 'win'
+    if platform.architecture()[0] == '64bit':
+      win_dir = 'win_x64'
+    flash_path = os.path.join(
+        chrome_root, 'third_party', 'adobe', 'flash', 'binaries', 'ppapi',
+        win_dir, 'pepflashplayer.dll')
+  else:
+    raise Exception('Platform not recognized')
+
+  def IsExecutable(path):
+    return os.path.isfile(path) and os.access(path, os.X_OK)
+
+  # Add the explicit browser executable if given.
+  if finder_options.browser_executable:
+    normalized_executable = os.path.expanduser(
+        finder_options.browser_executable)
+    if IsExecutable(normalized_executable):
+      browser_directory = os.path.dirname(finder_options.browser_executable)
+      browsers.append(PossibleDesktopBrowser('exact', finder_options,
+                                             normalized_executable, flash_path,
+                                             False, browser_directory))
+    else:
+      logging.warning('%s specified by browser_executable does not exist',
+                      normalized_executable)
+
+  def AddIfFound(browser_type, build_dir, type_dir, app_name, content_shell):
+    browser_directory = os.path.join(chrome_root, build_dir, type_dir)
+    app = os.path.join(browser_directory, app_name)
+    if IsExecutable(app):
+      browsers.append(PossibleDesktopBrowser(browser_type, finder_options,
+                                             app, flash_path, content_shell,
+                                             browser_directory,
+                                             is_local_build=True))
+      return True
+    return False
+
+  # Add local builds
+  for build_dir, build_type in util.GetBuildDirectories():
+    AddIfFound(build_type.lower(), build_dir, build_type,
+               chromium_app_name, False)
+    AddIfFound('content-shell-' + build_type.lower(), build_dir, build_type,
+               content_shell_app_name, True)
+
+  # Mac-specific options.
+  if sys.platform == 'darwin':
+    mac_canary_root = '/Applications/Google Chrome Canary.app/'
+    mac_canary = mac_canary_root + 'Contents/MacOS/Google Chrome Canary'
+    mac_system_root = '/Applications/Google Chrome.app'
+    mac_system = mac_system_root + '/Contents/MacOS/Google Chrome'
+    if IsExecutable(mac_canary):
+      browsers.append(PossibleDesktopBrowser('canary', finder_options,
+                                             mac_canary, None, False,
+                                             mac_canary_root))
+
+    if IsExecutable(mac_system):
+      browsers.append(PossibleDesktopBrowser('system', finder_options,
+                                             mac_system, None, False,
+                                             mac_system_root))
+
+  # Linux specific options.
+  if sys.platform.startswith('linux'):
+    # Look for a google-chrome instance.
+    found = False
+    try:
+      with open(os.devnull, 'w') as devnull:
+        found = subprocess.call(['google-chrome', '--version'],
+                                stdout=devnull, stderr=devnull) == 0
+    except OSError:
+      pass
+    if found:
+      browsers.append(PossibleDesktopBrowser('system', finder_options,
+                                             'google-chrome', None, False,
+                                             '/opt/google/chrome'))
+
+  # Win32-specific options.
+  if sys.platform.startswith('win'):
+    system_path = os.path.join('Google', 'Chrome', 'Application')
+    canary_path = os.path.join('Google', 'Chrome SxS', 'Application')
+
+    win_search_paths = [os.getenv('PROGRAMFILES(X86)'),
+                        os.getenv('PROGRAMFILES'),
+                        os.getenv('LOCALAPPDATA')]
+
+    def AddIfFoundWin(browser_name, app_path):
+      browser_directory = os.path.join(path, app_path)
+      app = os.path.join(browser_directory, chromium_app_name)
+      if IsExecutable(app):
+        browsers.append(PossibleDesktopBrowser(browser_name, finder_options,
+                                               app, flash_path, False,
+                                               browser_directory))
+        return True
+      return False
+
+    for path in win_search_paths:
+      if not path:
+        continue
+      if AddIfFoundWin('canary', canary_path):
+        break
+
+    for path in win_search_paths:
+      if not path:
+        continue
+      if AddIfFoundWin('system', system_path):
+        break
+
+  if len(browsers) and not has_display:
+    logging.warning(
+      'Found (%s), but you do not have a DISPLAY environment set.' %
+      ','.join([b.browser_type for b in browsers]))
+    return []
+
+  return browsers
diff --git a/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_finder_unittest.py b/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_finder_unittest.py
new file mode 100644
index 0000000..eda64bb
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/desktop_browser_finder_unittest.py
@@ -0,0 +1,188 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import unittest
+
+from telemetry.core import browser_options
+from telemetry.core.backends.chrome import desktop_browser_finder
+from telemetry.unittest import system_stub
+
+# This file verifies the logic for finding a browser instance on all platforms
+# at once. It does so by providing stubs for the OS/sys/subprocess primitives
+# that the underlying finding logic usually uses to locate a suitable browser.
+# We prefer this approach to having to run the same test on every platform on
+# which we want this code to work.
+
+class FindTestBase(unittest.TestCase):
+  def setUp(self):
+    self._finder_options = browser_options.BrowserFinderOptions()
+    self._finder_options.chrome_root = '../../../'
+    self._stubs = system_stub.Override(desktop_browser_finder,
+                                       ['os', 'subprocess', 'sys'])
+
+  def tearDown(self):
+    self._stubs.Restore()
+
+  @property
+  def _files(self):
+    return self._stubs.os.path.files
+
+  def DoFindAll(self):
+    return desktop_browser_finder.FindAllAvailableBrowsers(self._finder_options)
+
+  def DoFindAllTypes(self):
+    browsers = self.DoFindAll()
+    return [b.browser_type for b in browsers]
+
+def has_type(array, browser_type):
+  return len([x for x in array if x.browser_type == browser_type]) != 0
+
+class FindSystemTest(FindTestBase):
+  def setUp(self):
+    super(FindSystemTest, self).setUp()
+    self._stubs.sys.platform = 'win32'
+
+  def testFindProgramFiles(self):
+    self._files.append(
+        'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe')
+    self._stubs.os.program_files = 'C:\\Program Files'
+    self.assertTrue('system' in self.DoFindAllTypes())
+
+  def testFindProgramFilesX86(self):
+    self._files.append(
+        'C:\\Program Files(x86)\\Google\\Chrome\\Application\\chrome.exe')
+    self._stubs.os.program_files_x86 = 'C:\\Program Files(x86)'
+    self.assertTrue('system' in self.DoFindAllTypes())
+
+  def testFindLocalAppData(self):
+    self._files.append(
+        'C:\\Local App Data\\Google\\Chrome\\Application\\chrome.exe')
+    self._stubs.os.local_app_data = 'C:\\Local App Data'
+    self.assertTrue('system' in self.DoFindAllTypes())
+
+class FindLocalBuildsTest(FindTestBase):
+  def setUp(self):
+    super(FindLocalBuildsTest, self).setUp()
+    self._stubs.sys.platform = 'win32'
+
+  def testFindBuild(self):
+    self._files.append('..\\..\\..\\build\\Release\\chrome.exe')
+    self.assertTrue('release' in self.DoFindAllTypes())
+
+  def testFindOut(self):
+    self._files.append('..\\..\\..\\out\\Release\\chrome.exe')
+    self.assertTrue('release' in self.DoFindAllTypes())
+
+  def testFindSconsbuild(self):
+    self._files.append('..\\..\\..\\sconsbuild\\Release\\chrome.exe')
+    self.assertTrue('release' in self.DoFindAllTypes())
+
+  def testFindXcodebuild(self):
+    self._files.append('..\\..\\..\\xcodebuild\\Release\\chrome.exe')
+    self.assertTrue('release' in self.DoFindAllTypes())
+
+class OSXFindTest(FindTestBase):
+  def setUp(self):
+    super(OSXFindTest, self).setUp()
+    self._stubs.sys.platform = 'darwin'
+    self._files.append('/Applications/Google Chrome Canary.app/'
+                       'Contents/MacOS/Google Chrome Canary')
+    self._files.append('/Applications/Google Chrome.app/' +
+                       'Contents/MacOS/Google Chrome')
+    self._files.append(
+      '../../../out/Release/Chromium.app/Contents/MacOS/Chromium')
+    self._files.append(
+      '../../../out/Debug/Chromium.app/Contents/MacOS/Chromium')
+    self._files.append(
+      '../../../out/Release/Content Shell.app/Contents/MacOS/Content Shell')
+    self._files.append(
+      '../../../out/Debug/Content Shell.app/Contents/MacOS/Content Shell')
+
+  def testFindAll(self):
+    types = self.DoFindAllTypes()
+    self.assertEquals(
+      set(types),
+      set(['debug', 'release',
+           'content-shell-debug', 'content-shell-release',
+           'canary', 'system']))
+
+
+class LinuxFindTest(FindTestBase):
+  def setUp(self):
+    super(LinuxFindTest, self).setUp()
+
+    self._stubs.sys.platform = 'linux2'
+    self._files.append('/foo/chrome')
+    self._files.append('../../../out/Release/chrome')
+    self._files.append('../../../out/Debug/chrome')
+    self._files.append('../../../out/Release/content_shell')
+    self._files.append('../../../out/Debug/content_shell')
+
+    self.has_google_chrome_on_path = False
+    this = self
+    def call_hook(*args, **kwargs): # pylint: disable=W0613
+      if this.has_google_chrome_on_path:
+        return 0
+      raise OSError('Not found')
+    self._stubs.subprocess.call = call_hook
+
+  def testFindAllWithExact(self):
+    types = self.DoFindAllTypes()
+    self.assertEquals(
+        set(types),
+        set(['debug', 'release',
+             'content-shell-debug', 'content-shell-release']))
+
+  def testFindWithProvidedExecutable(self):
+    self._finder_options.browser_executable = '/foo/chrome'
+    self.assertTrue('exact' in self.DoFindAllTypes())
+
+  def testFindUsingDefaults(self):
+    self.has_google_chrome_on_path = True
+    self.assertTrue('release' in self.DoFindAllTypes())
+
+    del self._files[1]
+    self.has_google_chrome_on_path = True
+    self.assertTrue('system' in self.DoFindAllTypes())
+
+    self.has_google_chrome_on_path = False
+    del self._files[1]
+    self.assertEquals(['content-shell-debug', 'content-shell-release'],
+                      self.DoFindAllTypes())
+
+  def testFindUsingRelease(self):
+    self.assertTrue('release' in self.DoFindAllTypes())
+
+
+class WinFindTest(FindTestBase):
+  def setUp(self):
+    super(WinFindTest, self).setUp()
+
+    self._stubs.sys.platform = 'win32'
+    self._stubs.os.local_app_data = 'c:\\Users\\Someone\\AppData\\Local'
+    self._files.append('c:\\tmp\\chrome.exe')
+    self._files.append('..\\..\\..\\build\\Release\\chrome.exe')
+    self._files.append('..\\..\\..\\build\\Debug\\chrome.exe')
+    self._files.append('..\\..\\..\\build\\Release\\content_shell.exe')
+    self._files.append('..\\..\\..\\build\\Debug\\content_shell.exe')
+    self._files.append(self._stubs.os.local_app_data + '\\' +
+                       'Google\\Chrome\\Application\\chrome.exe')
+    self._files.append(self._stubs.os.local_app_data + '\\' +
+                       'Google\\Chrome SxS\\Application\\chrome.exe')
+
+  def testFindAllGivenDefaults(self):
+    types = self.DoFindAllTypes()
+    self.assertEquals(set(types),
+                      set(['debug', 'release',
+                           'content-shell-debug', 'content-shell-release',
+                           'system', 'canary']))
+
+  def testFindAllWithExact(self):
+    self._finder_options.browser_executable = 'c:\\tmp\\chrome.exe'
+    types = self.DoFindAllTypes()
+    self.assertEquals(
+        set(types),
+        set(['exact',
+             'debug', 'release',
+             'content-shell-debug', 'content-shell-release',
+             'system', 'canary']))
diff --git a/tools/telemetry/telemetry/core/backends/chrome/extension_dict_backend.py b/tools/telemetry/telemetry/core/backends/chrome/extension_dict_backend.py
new file mode 100644
index 0000000..40e76d0
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/extension_dict_backend.py
@@ -0,0 +1,71 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import json
+import re
+import weakref
+
+from telemetry.core import extension_page
+from telemetry.core.backends.chrome import inspector_backend
+
+class ExtensionNotFoundException(Exception):
+  pass
+
+class ExtensionDictBackend(object):
+  def __init__(self, browser_backend):
+    self._browser_backend = browser_backend
+    # Maps extension ids to ExtensionPage objects.
+    self._extension_dict = weakref.WeakValueDictionary()
+
+  def __getitem__(self, extension_id):
+    extension_object = self._extension_dict.get(extension_id)
+    if not extension_object:
+      extension_object = self._CreateExtensionObject(extension_id)
+      assert extension_object
+      self._extension_dict[extension_id] = extension_object
+    return extension_object
+
+  def __contains__(self, extension_id):
+    return extension_id in self._GetExtensionIds()
+
+  @staticmethod
+  def _ExtractExtensionId(url):
+    m = re.match(r"(chrome-extension://)([^/]+)", url)
+    assert m
+    return m.group(2)
+
+  @staticmethod
+  def _GetExtensionId(extension_info):
+    if 'url' not in extension_info:
+      return None
+    return ExtensionDictBackend._ExtractExtensionId(extension_info['url'])
+
+  def _CreateExtensionObject(self, extension_id):
+    extension_info = self._FindExtensionInfo(extension_id)
+    if not extension_info or not 'webSocketDebuggerUrl' in extension_info:
+      raise ExtensionNotFoundException()
+    return extension_page.ExtensionPage(
+        self._CreateInspectorBackendForDebuggerUrl(
+            extension_info['webSocketDebuggerUrl']))
+
+  def _CreateInspectorBackendForDebuggerUrl(self, debugger_url):
+    return inspector_backend.InspectorBackend(self._browser_backend.browser,
+                                              self._browser_backend,
+                                              debugger_url)
+
+  def _FindExtensionInfo(self, extension_id):
+    for extension_info in self._GetExtensionInfoList():
+      if self._GetExtensionId(extension_info) == extension_id:
+        return extension_info
+    return None
+
+  def _GetExtensionInfoList(self, timeout=None):
+    data = self._browser_backend.Request('', timeout=timeout)
+    return self._FilterExtensions(json.loads(data))
+
+  def _FilterExtensions(self, all_pages):
+    return [page_info for page_info in all_pages
+            if page_info['url'].startswith('chrome-extension://')]
+
+  def _GetExtensionIds(self):
+    return map(self._GetExtensionId, self._GetExtensionInfoList())
diff --git a/tools/telemetry/telemetry/core/backends/chrome/inspector_backend.py b/tools/telemetry/telemetry/core/backends/chrome/inspector_backend.py
new file mode 100644
index 0000000..f702ae2
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/inspector_backend.py
@@ -0,0 +1,326 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import json
+import logging
+import socket
+import sys
+
+from telemetry.core import util
+from telemetry.core import exceptions
+from telemetry.core.backends import png_bitmap
+from telemetry.core.backends.chrome import inspector_console
+from telemetry.core.backends.chrome import inspector_memory
+from telemetry.core.backends.chrome import inspector_network
+from telemetry.core.backends.chrome import inspector_page
+from telemetry.core.backends.chrome import inspector_runtime
+from telemetry.core.backends.chrome import inspector_timeline
+from telemetry.core.backends.chrome import websocket
+
+class InspectorException(Exception):
+  pass
+
+class InspectorBackend(object):
+  def __init__(self, browser, browser_backend, debugger_url):
+    assert debugger_url
+    self._browser = browser
+    self._browser_backend = browser_backend
+    self._debugger_url = debugger_url
+    self._socket = None
+    self._domain_handlers = {}
+    self._cur_socket_timeout = 0
+    self._next_request_id = 0
+
+    self._console = inspector_console.InspectorConsole(self)
+    self._memory = inspector_memory.InspectorMemory(self)
+    self._page = inspector_page.InspectorPage(self)
+    self._runtime = inspector_runtime.InspectorRuntime(self)
+    self._timeline = inspector_timeline.InspectorTimeline(self)
+    self._network = inspector_network.InspectorNetwork(self)
+
+  def __del__(self):
+    self.Disconnect()
+
+  def _Connect(self):
+    if self._socket:
+      return
+    try:
+      self._socket = websocket.create_connection(self._debugger_url)
+    except (websocket.WebSocketException):
+      if self._browser_backend.IsBrowserRunning():
+        raise exceptions.TabCrashException(sys.exc_info()[1])
+      else:
+        raise exceptions.BrowserGoneException()
+
+    self._cur_socket_timeout = 0
+    self._next_request_id = 0
+
+  def Disconnect(self):
+    for _, handlers in self._domain_handlers.items():
+      _, will_close_handler = handlers
+      will_close_handler()
+    self._domain_handlers = {}
+
+    if self._socket:
+      self._socket.close()
+      self._socket = None
+
+  # General public methods.
+
+  @property
+  def browser(self):
+    return self._browser
+
+  @property
+  def url(self):
+    self.Disconnect()
+    return self._browser_backend.tab_list_backend.GetTabUrl(self._debugger_url)
+
+  def Activate(self):
+    self._Connect()
+    self._browser_backend.tab_list_backend.ActivateTab(self._debugger_url)
+
+  def Close(self):
+    self.Disconnect()
+    self._browser_backend.tab_list_backend.CloseTab(self._debugger_url)
+
+  # Public methods implemented in JavaScript.
+
+  def WaitForDocumentReadyStateToBeComplete(self, timeout):
+    util.WaitFor(
+        lambda: self._runtime.Evaluate('document.readyState') == 'complete',
+        timeout)
+
+  def WaitForDocumentReadyStateToBeInteractiveOrBetter(
+      self, timeout):
+    def IsReadyStateInteractiveOrBetter():
+      rs = self._runtime.Evaluate('document.readyState')
+      return rs == 'complete' or rs == 'interactive'
+    util.WaitFor(IsReadyStateInteractiveOrBetter, timeout)
+
+  @property
+  def screenshot_supported(self):
+    if self._runtime.Evaluate(
+        'window.chrome.gpuBenchmarking === undefined'):
+      return False
+
+    if self._runtime.Evaluate(
+        'window.chrome.gpuBenchmarking.beginWindowSnapshotPNG === undefined'):
+      return False
+
+    return self._browser_backend.chrome_branch_number >= 1391
+
+  def Screenshot(self, timeout):
+    if self._runtime.Evaluate(
+        'window.chrome.gpuBenchmarking === undefined'):
+      raise Exception("Browser was not started with --enable-gpu-benchmarking")
+
+    if self._runtime.Evaluate(
+        'window.chrome.gpuBenchmarking.beginWindowSnapshotPNG === undefined'):
+      raise Exception("Browser does not support window snapshot API.")
+
+    self._runtime.Evaluate("""
+        if(!window.__telemetry) {
+          window.__telemetry = {}
+        }
+        window.__telemetry.snapshotComplete = false;
+        window.__telemetry.snapshotData = null;
+        window.chrome.gpuBenchmarking.beginWindowSnapshotPNG(
+          function(snapshot) {
+            window.__telemetry.snapshotData = snapshot;
+            window.__telemetry.snapshotComplete = true;
+          }
+        );
+    """)
+
+    def IsSnapshotComplete():
+      return self._runtime.Evaluate('window.__telemetry.snapshotComplete')
+
+    util.WaitFor(IsSnapshotComplete, timeout)
+
+    snap = self._runtime.Evaluate("""
+      (function() {
+        var data = window.__telemetry.snapshotData;
+        delete window.__telemetry.snapshotComplete;
+        delete window.__telemetry.snapshotData;
+        return data;
+      })()
+    """)
+    if snap:
+      return png_bitmap.PngBitmap.FromBase64(snap['data'])
+    return None
+
+  # Console public methods.
+
+  @property
+  def message_output_stream(self):  # pylint: disable=E0202
+    return self._console.message_output_stream
+
+  @message_output_stream.setter
+  def message_output_stream(self, stream):  # pylint: disable=E0202
+    self._console.message_output_stream = stream
+
+  # Memory public methods.
+
+  def GetDOMStats(self, timeout):
+    dom_counters = self._memory.GetDOMCounters(timeout)
+    return {
+      'document_count': dom_counters['documents'],
+      'node_count': dom_counters['nodes'],
+      'event_listener_count': dom_counters['jsEventListeners']
+    }
+
+  # Page public methods.
+
+  def PerformActionAndWaitForNavigate(self, action_function, timeout):
+    self._page.PerformActionAndWaitForNavigate(action_function, timeout)
+
+  def Navigate(self, url, script_to_evaluate_on_commit, timeout):
+    self._page.Navigate(url, script_to_evaluate_on_commit, timeout)
+
+  def GetCookieByName(self, name, timeout):
+    return self._page.GetCookieByName(name, timeout)
+
+  # Runtime public methods.
+
+  def ExecuteJavaScript(self, expr, timeout):
+    self._runtime.Execute(expr, timeout)
+
+  def EvaluateJavaScript(self, expr, timeout):
+    return self._runtime.Evaluate(expr, timeout)
+
+  # Timeline public methods.
+
+  @property
+  def timeline_model(self):
+    return self._timeline.timeline_model
+
+  def StartTimelineRecording(self):
+    self._timeline.Start()
+
+  def StopTimelineRecording(self):
+    self._timeline.Stop()
+
+  # Network public methods.
+
+  def ClearCache(self):
+    self._network.ClearCache()
+
+  # Methods used internally by other backends.
+
+  def DispatchNotifications(self, timeout=10):
+    self._Connect()
+    self._SetTimeout(timeout)
+
+    try:
+      data = self._socket.recv()
+    except (socket.error, websocket.WebSocketException):
+      if self._browser_backend.tab_list_backend.DoesDebuggerUrlExist(
+          self._debugger_url):
+        return
+      raise exceptions.TabCrashException(sys.exc_info()[1])
+
+    res = json.loads(data)
+    logging.debug('got [%s]', data)
+    if 'method' in res:
+      self._HandleNotification(res)
+
+  def _HandleNotification(self, res):
+    if (res['method'] == 'Inspector.detached' and
+        res.get('params', {}).get('reason','') == 'replaced_with_devtools'):
+      self._WaitForInspectorToGoAwayAndReconnect()
+      return
+    if res['method'] == 'Inspector.targetCrashed':
+      raise exceptions.TabCrashException()
+
+    mname = res['method']
+    dot_pos = mname.find('.')
+    domain_name = mname[:dot_pos]
+    if domain_name in self._domain_handlers:
+      try:
+        self._domain_handlers[domain_name][0](res)
+      except Exception:
+        import traceback
+        traceback.print_exc()
+    else:
+      logging.debug('Unhandled inspector message: %s', res)
+
+  def SendAndIgnoreResponse(self, req):
+    self._Connect()
+    req['id'] = self._next_request_id
+    self._next_request_id += 1
+    data = json.dumps(req)
+    self._socket.send(data)
+    logging.debug('sent [%s]', data)
+
+  def _SetTimeout(self, timeout):
+    if self._cur_socket_timeout != timeout:
+      self._socket.settimeout(timeout)
+      self._cur_socket_timeout = timeout
+
+  def _WaitForInspectorToGoAwayAndReconnect(self):
+    sys.stderr.write('The connection to Chrome was lost to the Inspector UI.\n')
+    sys.stderr.write('Telemetry is waiting for the inspector to be closed...\n')
+    self._socket.close()
+    self._socket = None
+    def IsBack():
+      return self._browser_backend.tab_list_backend.DoesDebuggerUrlExist(
+        self._debugger_url)
+    util.WaitFor(IsBack, 512, 0.5)
+    sys.stderr.write('\n')
+    sys.stderr.write('Inspector\'s UI closed. Telemetry will now resume.\n')
+    self._Connect()
+
+  def SyncRequest(self, req, timeout=10):
+    self._Connect()
+    # TODO(nduca): Listen to the timeout argument
+    # pylint: disable=W0613
+    self._SetTimeout(timeout)
+    self.SendAndIgnoreResponse(req)
+
+    while True:
+      try:
+        data = self._socket.recv()
+      except (socket.error, websocket.WebSocketException):
+        if self._browser_backend.tab_list_backend.DoesDebuggerUrlExist(
+            self._debugger_url):
+          raise util.TimeoutException(
+            'Timed out waiting for reply. This is unusual.')
+        raise exceptions.TabCrashException(sys.exc_info()[1])
+
+      res = json.loads(data)
+      logging.debug('got [%s]', data)
+      if 'method' in res:
+        self._HandleNotification(res)
+        continue
+
+      if res['id'] != req['id']:
+        logging.debug('Dropped reply: %s', json.dumps(res))
+        continue
+      return res
+
+  def RegisterDomain(self,
+      domain_name, notification_handler, will_close_handler):
+    """Registers a given domain for handling notification methods.
+
+    For example, given inspector_backend:
+       def OnConsoleNotification(msg):
+          if msg['method'] == 'Console.messageAdded':
+             print msg['params']['message']
+          return
+       def OnConsoleClose(self):
+          pass
+       inspector_backend.RegisterDomain('Console',
+                                        OnConsoleNotification, OnConsoleClose)
+       """
+    assert domain_name not in self._domain_handlers
+    self._domain_handlers[domain_name] = (notification_handler,
+                                          will_close_handler)
+
+  def UnregisterDomain(self, domain_name):
+    """Unregisters a previously registered domain."""
+    assert domain_name in self._domain_handlers
+    self._domain_handlers.pop(domain_name)
+
+  def CollectGarbage(self):
+    self._page.CollectGarbage()
diff --git a/tools/telemetry/telemetry/core/backends/chrome/inspector_console.py b/tools/telemetry/telemetry/core/backends/chrome/inspector_console.py
new file mode 100644
index 0000000..3f73a5d
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/inspector_console.py
@@ -0,0 +1,61 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import json
+import logging
+
+class InspectorConsole(object):
+  def __init__(self, inspector_backend):
+    self._inspector_backend = inspector_backend
+    self._inspector_backend.RegisterDomain(
+        'Console',
+        self._OnNotification,
+        self._OnClose)
+    self._message_output_stream = None
+    self._last_message = None
+    self._console_enabled = False
+
+  def _OnNotification(self, msg):
+    logging.debug('Notification: %s', json.dumps(msg, indent=2))
+    if msg['method'] == 'Console.messageAdded':
+      if msg['params']['message']['url'] == 'chrome://newtab/':
+        return
+      self._last_message = 'At %s:%i: %s' % (
+        msg['params']['message']['url'],
+        msg['params']['message']['line'],
+        msg['params']['message']['text'])
+      if self._message_output_stream:
+        self._message_output_stream.write(
+          '%s\n' % self._last_message)
+
+    elif msg['method'] == 'Console.messageRepeatCountUpdated':
+      if self._message_output_stream:
+        self._message_output_stream.write(
+          '%s\n' % self._last_message)
+
+  def _OnClose(self):
+    pass
+
+  # False positive in PyLint 0.25.1: http://www.logilab.org/89092
+  @property
+  def message_output_stream(self):  # pylint: disable=E0202
+    return self._message_output_stream
+
+  @message_output_stream.setter
+  def message_output_stream(self, stream):  # pylint: disable=E0202
+    self._message_output_stream = stream
+    self._UpdateConsoleEnabledState()
+
+  def _UpdateConsoleEnabledState(self):
+    enabled = self._message_output_stream != None
+    if enabled == self._console_enabled:
+      return
+
+    if enabled:
+      method_name = 'enable'
+    else:
+      method_name = 'disable'
+    self._inspector_backend.SyncRequest({
+        'method': 'Console.%s' % method_name
+        })
+    self._console_enabled = enabled
diff --git a/tools/telemetry/telemetry/core/backends/chrome/inspector_console_unittest.py b/tools/telemetry/telemetry/core/backends/chrome/inspector_console_unittest.py
new file mode 100644
index 0000000..79a75f4
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/inspector_console_unittest.py
@@ -0,0 +1,34 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import re
+import StringIO
+
+from telemetry.core import util
+from telemetry.unittest import tab_test_case
+
+class TabConsoleTest(tab_test_case.TabTestCase):
+  def testConsoleOutputStream(self):
+    self._browser.SetHTTPServerDirectories(util.GetUnittestDataDir())
+
+    stream = StringIO.StringIO()
+    self._tab.message_output_stream = stream
+
+    self._tab.Navigate(
+      self._browser.http_server.UrlOf('page_that_logs_to_console.html'))
+    self._tab.WaitForDocumentReadyStateToBeComplete()
+
+    initial = self._tab.EvaluateJavaScript('window.__logCount')
+    def GotLog():
+      current = self._tab.EvaluateJavaScript('window.__logCount')
+      return current > initial
+    util.WaitFor(GotLog, 5)
+
+    lines = [l for l in stream.getvalue().split('\n') if len(l)]
+
+    self.assertTrue(len(lines) >= 1)
+    for line in lines:
+      prefix = 'http://(.+)/page_that_logs_to_console.html:9'
+      expected_line = 'At %s: Hello, world' % prefix
+      self.assertTrue(re.match(expected_line, line))
+
diff --git a/tools/telemetry/telemetry/core/backends/chrome/inspector_memory.py b/tools/telemetry/telemetry/core/backends/chrome/inspector_memory.py
new file mode 100644
index 0000000..703869b
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/inspector_memory.py
@@ -0,0 +1,50 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import json
+
+class InspectorMemoryException(Exception):
+  pass
+
+class InspectorMemory(object):
+  """Communicates with the remote inspector's Memory domain."""
+
+  def __init__(self, inspector_backend):
+    self._inspector_backend = inspector_backend
+    self._inspector_backend.RegisterDomain(
+        'Memory',
+        self._OnNotification,
+        self._OnClose)
+
+  def _OnNotification(self, msg):
+    pass
+
+  def _OnClose(self):
+    pass
+
+  def GetDOMCounters(self, timeout):
+    """Retrieves DOM element counts.
+
+    Args:
+      timeout: The number of seconds to wait for the inspector backend to
+          service the request before timing out.
+
+    Returns:
+      A dictionary containing the counts associated with "nodes", "documents",
+      and "jsEventListeners".
+    """
+    res = self._inspector_backend.SyncRequest({
+      'method': 'Memory.getDOMCounters'
+    }, timeout)
+    if ('result' not in res or
+        'nodes' not in res['result'] or
+        'documents' not in res['result'] or
+        'jsEventListeners' not in res['result']):
+      raise InspectorMemoryException(
+          'Inspector returned unexpected result for Memory.getDOMCounters:\n' +
+          json.dumps(res, indent=2))
+    return {
+        'nodes': res['result']['nodes'],
+        'documents': res['result']['documents'],
+        'jsEventListeners': res['result']['jsEventListeners']
+    }
diff --git a/tools/telemetry/telemetry/core/backends/chrome/inspector_memory_unittest.py b/tools/telemetry/telemetry/core/backends/chrome/inspector_memory_unittest.py
new file mode 100644
index 0000000..347ee0e
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/inspector_memory_unittest.py
@@ -0,0 +1,23 @@
+# 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.
+
+from telemetry.core import util
+from telemetry.unittest import tab_test_case
+
+class InspectorMemoryTest(tab_test_case.TabTestCase):
+  def testGetDOMStats(self):
+    self._browser.SetHTTPServerDirectories(util.GetUnittestDataDir())
+
+    # Due to an issue with CrOS, we create a new tab here rather than
+    # using self._tab to get a consistent starting page on all platforms
+    tab = self._browser.tabs.New()
+
+    tab.Navigate(
+      self._browser.http_server.UrlOf('dom_counter_sample.html'))
+    tab.WaitForDocumentReadyStateToBeComplete()
+
+    counts = tab.dom_stats
+    self.assertEqual(counts['document_count'], 2)
+    self.assertEqual(counts['node_count'], 18)
+    self.assertEqual(counts['event_listener_count'], 2)
diff --git a/tools/telemetry/telemetry/core/backends/chrome/inspector_network.py b/tools/telemetry/telemetry/core/backends/chrome/inspector_network.py
new file mode 100644
index 0000000..5ef2edb
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/inspector_network.py
@@ -0,0 +1,17 @@
+# 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.
+
+class InspectorNetwork(object):
+  def __init__(self, inspector_backend):
+    self._inspector_backend = inspector_backend
+
+  def ClearCache(self, timeout=60):
+    """Clears the browser's disk and memory cache."""
+    res = self._inspector_backend.SyncRequest({
+        'method': 'Network.canClearBrowserCache'
+        }, timeout)
+    assert res['result'], 'Cache clearing is not supported by this browser'
+    self._inspector_backend.SyncRequest({
+        'method': 'Network.clearBrowserCache'
+        }, timeout)
diff --git a/tools/telemetry/telemetry/core/backends/chrome/inspector_page.py b/tools/telemetry/telemetry/core/backends/chrome/inspector_page.py
new file mode 100644
index 0000000..505a398
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/inspector_page.py
@@ -0,0 +1,114 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import json
+import logging
+
+from telemetry.core import util
+
+class InspectorPage(object):
+  def __init__(self, inspector_backend):
+    self._inspector_backend = inspector_backend
+    self._inspector_backend.RegisterDomain(
+        'Page',
+        self._OnNotification,
+        self._OnClose)
+    self._navigation_pending = False
+
+  def _OnNotification(self, msg):
+    logging.debug('Notification: %s', json.dumps(msg, indent=2))
+    if msg['method'] == 'Page.frameNavigated' and self._navigation_pending:
+      url = msg['params']['frame']['url']
+      if (not url == 'chrome://newtab/' and not url == 'about:blank'
+          and not 'parentId' in msg['params']['frame']):
+        # Marks the navigation as complete and unblocks the
+        # PerformActionAndWaitForNavigate call.
+        self._navigation_pending = False
+
+  def _OnClose(self):
+    pass
+
+  def PerformActionAndWaitForNavigate(self, action_function, timeout=60):
+    """Executes action_function, and waits for the navigation to complete.
+
+    action_function is expect to result in a navigation. This function returns
+    when the navigation is complete or when the timeout has been exceeded.
+    """
+
+    # Turn on notifications. We need them to get the Page.frameNavigated event.
+    request = {
+        'method': 'Page.enable'
+        }
+    res = self._inspector_backend.SyncRequest(request, timeout)
+    assert len(res['result'].keys()) == 0
+
+    def DisablePageNotifications():
+      request = {
+          'method': 'Page.disable'
+          }
+      res = self._inspector_backend.SyncRequest(request, timeout)
+      assert len(res['result'].keys()) == 0
+
+    self._navigation_pending = True
+    try:
+      action_function()
+    except:
+      DisablePageNotifications()
+      raise
+
+    def IsNavigationDone(time_left):
+      self._inspector_backend.DispatchNotifications(time_left)
+      return not self._navigation_pending
+    util.WaitFor(IsNavigationDone, timeout, pass_time_left_to_func=True)
+
+    DisablePageNotifications()
+
+  def Navigate(self, url, script_to_evaluate_on_commit=None, timeout=60):
+    """Navigates to |url|.
+
+    If |script_to_evaluate_on_commit| is given, the script source string will be
+    evaluated when the navigation is committed. This is after the context of
+    the page exists, but before any script on the page itself has executed.
+    """
+
+    def DoNavigate():
+      if script_to_evaluate_on_commit:
+        request = {
+            'method': 'Page.addScriptToEvaluateOnLoad',
+            'params': {
+                'scriptSource': script_to_evaluate_on_commit,
+                }
+            }
+        self._inspector_backend.SyncRequest(request)
+      # Navigate the page. However, there seems to be a bug in chrome devtools
+      # protocol where the request id for this event gets held on the browser
+      # side pretty much indefinitely.
+      #
+      # So, instead of waiting for the event to actually complete, wait for the
+      # Page.frameNavigated event.
+      request = {
+          'method': 'Page.navigate',
+          'params': {
+              'url': url,
+              }
+          }
+      self._inspector_backend.SendAndIgnoreResponse(request)
+    self.PerformActionAndWaitForNavigate(DoNavigate, timeout)
+
+  def GetCookieByName(self, name, timeout=60):
+    """Returns the value of the cookie by the given |name|."""
+    request = {
+        'method': 'Page.getCookies'
+        }
+    res = self._inspector_backend.SyncRequest(request, timeout)
+    cookies = res['result']['cookies']
+    for cookie in cookies:
+      if cookie['name'] == name:
+        return cookie['value']
+    return None
+
+  def CollectGarbage(self, timeout=60):
+    request = {
+        'method': 'HeapProfiler.CollectGarbage'
+        }
+    self._inspector_backend.SyncRequest(request, timeout)
diff --git a/tools/telemetry/telemetry/core/backends/chrome/inspector_page_unittest.py b/tools/telemetry/telemetry/core/backends/chrome/inspector_page_unittest.py
new file mode 100644
index 0000000..364e28d
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/inspector_page_unittest.py
@@ -0,0 +1,53 @@
+# 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.
+
+from telemetry.core import util
+from telemetry.unittest import tab_test_case
+
+
+class InspectorPageTest(tab_test_case.TabTestCase):
+  def __init__(self, *args):
+    super(InspectorPageTest, self).__init__(*args)
+
+  def setUp(self):
+    super(InspectorPageTest, self).setUp()
+    self._browser.SetHTTPServerDirectories(util.GetUnittestDataDir())
+
+  def testPageNavigateToNormalUrl(self):
+    self._tab.Navigate(self._browser.http_server.UrlOf('blank.html'))
+    self._tab.WaitForDocumentReadyStateToBeComplete()
+
+  def testCustomActionToNavigate(self):
+    self._tab.Navigate(
+      self._browser.http_server.UrlOf('page_with_link.html'))
+    self._tab.WaitForDocumentReadyStateToBeComplete()
+    self.assertEquals(
+        self._tab.EvaluateJavaScript('document.location.pathname;'),
+        '/page_with_link.html')
+
+    custom_action_called = [False]
+    def CustomAction():
+      custom_action_called[0] = True
+      self._tab.ExecuteJavaScript('document.getElementById("clickme").click();')
+
+    self._tab.PerformActionAndWaitForNavigate(CustomAction)
+
+    self.assertTrue(custom_action_called[0])
+    self.assertEquals(
+        self._tab.EvaluateJavaScript('document.location.pathname;'),
+        '/blank.html')
+
+  def testGetCookieByName(self):
+    self._tab.Navigate(
+      self._browser.http_server.UrlOf('blank.html'))
+    self._tab.WaitForDocumentReadyStateToBeComplete()
+    self._tab.ExecuteJavaScript('document.cookie="foo=bar"')
+    self.assertEquals(self._tab.GetCookieByName('foo'), 'bar')
+
+  def testScriptToEvaluateOnCommit(self):
+    self._tab.Navigate(
+      self._browser.http_server.UrlOf('blank.html'),
+      script_to_evaluate_on_commit='var foo = "bar";')
+    self._tab.WaitForDocumentReadyStateToBeComplete()
+    self.assertEquals(self._tab.EvaluateJavaScript('foo'), 'bar')
diff --git a/tools/telemetry/telemetry/core/backends/chrome/inspector_runtime.py b/tools/telemetry/telemetry/core/backends/chrome/inspector_runtime.py
new file mode 100644
index 0000000..529d128
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/inspector_runtime.py
@@ -0,0 +1,56 @@
+# 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.
+from telemetry.core import exceptions
+
+class InspectorRuntime(object):
+  def __init__(self, inspector_backend):
+    self._inspector_backend = inspector_backend
+    self._inspector_backend.RegisterDomain(
+        'Runtime',
+        self._OnNotification,
+        self._OnClose)
+
+  def _OnNotification(self, msg):
+    pass
+
+  def _OnClose(self):
+    pass
+
+  def Execute(self, expr, timeout=60):
+    """Executes expr in javascript. Does not return the result.
+
+    If the expression failed to evaluate, EvaluateException will be raised.
+    """
+    self.Evaluate(expr + '; 0;', timeout)
+
+  def Evaluate(self, expr, timeout=60):
+    """Evalutes expr in javascript and returns the JSONized result.
+
+    Consider using Execute for cases where the result of the expression is not
+    needed.
+
+    If evaluation throws in javascript, a python EvaluateException will
+    be raised.
+
+    If the result of the evaluation cannot be JSONized, then an
+    EvaluationException will be raised.
+    """
+    request = {
+      'method': 'Runtime.evaluate',
+      'params': {
+        'expression': expr,
+        'returnByValue': True
+        }
+      }
+    res = self._inspector_backend.SyncRequest(request, timeout)
+    if 'error' in res:
+      raise exceptions.EvaluateException(res['error']['message'])
+
+    if 'wasThrown' in res['result'] and res['result']['wasThrown']:
+      # TODO(nduca): propagate stacks from javascript up to the python
+      # exception.
+      raise exceptions.EvaluateException(res['result']['result']['description'])
+    if res['result']['result']['type'] == 'undefined':
+      return None
+    return res['result']['result']['value']
diff --git a/tools/telemetry/telemetry/core/backends/chrome/inspector_runtime_unittest.py b/tools/telemetry/telemetry/core/backends/chrome/inspector_runtime_unittest.py
new file mode 100644
index 0000000..c5687dd
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/inspector_runtime_unittest.py
@@ -0,0 +1,31 @@
+# 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.
+from telemetry.core import exceptions
+from telemetry.unittest import tab_test_case
+
+class InspectorRuntimeTest(tab_test_case.TabTestCase):
+  def testRuntimeEvaluateSimple(self):
+    res = self._tab.EvaluateJavaScript('1+1')
+    assert res == 2
+
+  def testRuntimeEvaluateThatFails(self):
+    self.assertRaises(exceptions.EvaluateException,
+                      lambda: self._tab.EvaluateJavaScript('fsdfsdfsf'))
+
+  def testRuntimeEvaluateOfSomethingThatCantJSONize(self):
+
+    def test():
+      self._tab.EvaluateJavaScript("""
+        var cur = {};
+        var root = {next: cur};
+        for (var i = 0; i < 1000; i++) {
+          next = {};
+          cur.next = next;
+          cur = next;
+        }
+        root;""")
+    self.assertRaises(exceptions.EvaluateException, test)
+
+  def testRuntimeExecuteOfSomethingThatCantJSONize(self):
+    self._tab.ExecuteJavaScript('window')
diff --git a/tools/telemetry/telemetry/core/backends/chrome/inspector_timeline.py b/tools/telemetry/telemetry/core/backends/chrome/inspector_timeline.py
new file mode 100644
index 0000000..92e83d3
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/inspector_timeline.py
@@ -0,0 +1,71 @@
+# 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.
+from telemetry.core.timeline import model
+
+class TabBackendException(Exception):
+  pass
+
+class InspectorTimeline(object):
+  """Implementation of dev tools timeline."""
+  class Recorder(object):
+    """Utility class to Start / Stop recording timeline."""
+    def __init__(self, tab):
+      self._tab = tab
+
+    def __enter__(self):
+      self._tab.StartTimelineRecording()
+
+    def __exit__(self, *args):
+      self._tab.StopTimelineRecording()
+
+  def __init__(self, inspector_backend):
+    self._inspector_backend = inspector_backend
+    self._is_recording = False
+    self._timeline_model = None
+    self._raw_events = None
+
+  @property
+  def timeline_model(self):
+    return self._timeline_model
+
+  def Start(self):
+    if self._is_recording:
+      return
+    self._is_recording = True
+    self._timeline_model = None
+    self._raw_events = []
+    self._inspector_backend.RegisterDomain('Timeline',
+       self._OnNotification, self._OnClose)
+    req = {'method': 'Timeline.start'}
+    self._SendSyncRequest(req)
+
+  def Stop(self):
+    if not self._is_recording:
+      raise TabBackendException('Stop() called but not started')
+    self._is_recording = False
+    self._timeline_model = model.TimelineModel(event_data=self._raw_events,
+                                               shift_world_to_zero=False)
+    req = {'method': 'Timeline.stop'}
+    self._SendSyncRequest(req)
+    self._inspector_backend.UnregisterDomain('Timeline')
+
+  def _SendSyncRequest(self, req, timeout=60):
+    res = self._inspector_backend.SyncRequest(req, timeout)
+    if 'error' in res:
+      raise TabBackendException(res['error']['message'])
+    return res['result']
+
+  def _OnNotification(self, msg):
+    if not self._is_recording:
+      return
+    if 'method' in msg and msg['method'] == 'Timeline.eventRecorded':
+      self._OnEventRecorded(msg)
+
+  def _OnEventRecorded(self, msg):
+    record = msg.get('params', {}).get('record')
+    if record:
+      self._raw_events.append(record)
+
+  def _OnClose(self):
+    pass
diff --git a/tools/telemetry/telemetry/core/backends/chrome/inspector_timeline_unittest.py b/tools/telemetry/telemetry/core/backends/chrome/inspector_timeline_unittest.py
new file mode 100644
index 0000000..67e3d33
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/inspector_timeline_unittest.py
@@ -0,0 +1,34 @@
+# 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.
+
+from telemetry.core import util
+from telemetry.core.backends.chrome import inspector_timeline
+from telemetry.unittest import tab_test_case
+
+class InspectorTimelineTabTest(tab_test_case.TabTestCase):
+  def _StartServer(self):
+    self._browser.SetHTTPServerDirectories(util.GetUnittestDataDir())
+
+  def _WaitForAnimationFrame(self):
+    def _IsDone():
+      js_is_done = """done"""
+      return bool(self._tab.EvaluateJavaScript(js_is_done))
+    util.WaitFor(_IsDone, 5)
+
+  def testGotTimeline(self):
+    with inspector_timeline.InspectorTimeline.Recorder(self._tab):
+      self._tab.ExecuteJavaScript(
+"""
+var done = false;
+function sleep(ms) {
+  var endTime = (new Date().getTime()) + ms;
+  while ((new Date().getTime()) < endTime);
+}
+window.webkitRequestAnimationFrame(function() { sleep(10); done = true; });
+""")
+      self._WaitForAnimationFrame()
+
+    r = self._tab.timeline_model.GetAllEventsOfName('FireAnimationFrame')
+    self.assertTrue(len(r) > 0)
+    self.assertTrue(r[0].duration > 0)
diff --git a/tools/telemetry/telemetry/core/backends/chrome/misc_web_contents_backend.py b/tools/telemetry/telemetry/core/backends/chrome/misc_web_contents_backend.py
new file mode 100644
index 0000000..f2327c0
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/misc_web_contents_backend.py
@@ -0,0 +1,38 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import json
+
+from telemetry.core import web_contents
+from telemetry.core.backends.chrome import inspector_backend
+
+class MiscWebContentsBackend(object):
+  """Provides acccess to chrome://oobe/login page, which is neither an extension
+  nor a tab."""
+  def __init__(self, browser_backend):
+    self._browser_backend = browser_backend
+
+  def GetOobe(self):
+    oobe_web_contents_info = self._FindWebContentsInfo()
+    if oobe_web_contents_info:
+      debugger_url = oobe_web_contents_info.get('webSocketDebuggerUrl')
+      if debugger_url:
+        inspector = self._CreateInspectorBackend(debugger_url)
+        return web_contents.WebContents(inspector)
+    return None
+
+  def _CreateInspectorBackend(self, debugger_url):
+    return inspector_backend.InspectorBackend(self._browser_backend.browser,
+                                              self._browser_backend,
+                                              debugger_url)
+
+  def _ListWebContents(self, timeout=None):
+    data = self._browser_backend.Request('', timeout=timeout)
+    return json.loads(data)
+
+  def _FindWebContentsInfo(self):
+    for web_contents_info in self._ListWebContents():
+      # Prior to crrev.com/203152, url was chrome://oobe/login.
+      if (web_contents_info.get('url').startswith('chrome://oobe')):
+        return web_contents_info
+    return None
diff --git a/tools/telemetry/telemetry/core/backends/chrome/system_info_backend.py b/tools/telemetry/telemetry/core/backends/chrome/system_info_backend.py
new file mode 100644
index 0000000..d4ce119
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/system_info_backend.py
@@ -0,0 +1,24 @@
+# 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.
+
+from telemetry.core import camel_case
+from telemetry.core import system_info
+from telemetry.core.backends.chrome import websocket_browser_connection
+
+
+class SystemInfoBackend(object):
+  def __init__(self, devtools_port):
+    self._conn = websocket_browser_connection.WebSocketBrowserConnection(
+        devtools_port)
+
+  def Close(self):
+    self._conn.Close()
+
+  def GetSystemInfo(self, timeout=10):
+    req = {'method': 'SystemInfo.getInfo'}
+    res = self._conn.SyncRequest(req, timeout)
+    if 'error' in res:
+      return None
+    return system_info.SystemInfo.FromDict(
+        camel_case.ToUnderscore(res['result']))
diff --git a/tools/telemetry/telemetry/core/backends/chrome/tab_list_backend.py b/tools/telemetry/telemetry/core/backends/chrome/tab_list_backend.py
new file mode 100644
index 0000000..debb334
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/tab_list_backend.py
@@ -0,0 +1,135 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import json
+import urllib2
+import weakref
+
+from telemetry.core import util
+from telemetry.core import tab
+from telemetry.core.backends.chrome import inspector_backend
+
+class TabListBackend(object):
+  def __init__(self, browser_backend):
+    self._browser_backend = browser_backend
+
+    # Stores web socket debugger URLs in iteration order.
+    self._tab_list = []
+    # Maps debugger URLs to Tab objects.
+    self._tab_dict = weakref.WeakValueDictionary()
+
+  def Init(self):
+    self._UpdateTabList()
+
+  def New(self, timeout):
+    assert self._browser_backend.supports_tab_control
+
+    self._browser_backend.Request('new', timeout=timeout)
+    return self[-1]
+
+  def DoesDebuggerUrlExist(self, debugger_url):
+    return self._FindTabInfo(debugger_url) is not None
+
+  def CloseTab(self, debugger_url, timeout=None):
+    assert self._browser_backend.supports_tab_control
+
+    # TODO(dtu): crbug.com/160946, allow closing the last tab on some platforms.
+    # For now, just create a new tab before closing the last tab.
+    if len(self) <= 1:
+      self.New(timeout)
+
+    tab_id = debugger_url.split('/')[-1]
+    try:
+      response = self._browser_backend.Request('close/%s' % tab_id,
+                                               timeout=timeout,
+                                               throw_network_exception=True)
+    except urllib2.HTTPError:
+      raise Exception('Unable to close tab, tab id not found: %s' % tab_id)
+    assert response == 'Target is closing'
+
+    util.WaitFor(lambda: not self._FindTabInfo(debugger_url), timeout=5)
+
+    if debugger_url in self._tab_dict:
+      del self._tab_dict[debugger_url]
+    self._UpdateTabList()
+
+  def ActivateTab(self, debugger_url, timeout=None):
+    assert self._browser_backend.supports_tab_control
+
+    assert debugger_url in self._tab_dict
+    tab_id = debugger_url.split('/')[-1]
+    try:
+      response = self._browser_backend.Request('activate/%s' % tab_id,
+                                               timeout=timeout,
+                                               throw_network_exception=True)
+    except urllib2.HTTPError:
+      raise Exception('Unable to activate tab, tab id not found: %s' % tab_id)
+    assert response == 'Target activated'
+
+  def GetTabUrl(self, debugger_url):
+    tab_info = self._FindTabInfo(debugger_url)
+    # TODO(hartmanng): crbug.com/166886 (uncomment the following assert and
+    # remove the extra None check when _ListTabs is fixed):
+    # assert tab_info is not None
+    return tab_info['url'] if tab_info else None
+
+  def __iter__(self):
+    self._UpdateTabList()
+    return self._tab_list.__iter__()
+
+  def __len__(self):
+    self._UpdateTabList()
+    return len(self._tab_list)
+
+  def Get(self, index, ret):
+    """Returns self[index] if it exists, or ret if index is out of bounds.
+    """
+    self._UpdateTabList()
+    if len(self._tab_list) <= index:
+      return ret
+    debugger_url = self._tab_list[index]
+    # Lazily get/create a Tab object.
+    tab_object = self._tab_dict.get(debugger_url)
+    if not tab_object:
+      backend = inspector_backend.InspectorBackend(
+          self._browser_backend.browser,
+          self._browser_backend,
+          debugger_url)
+      tab_object = tab.Tab(backend)
+      self._tab_dict[debugger_url] = tab_object
+    return tab_object
+
+  def __getitem__(self, index):
+    tab_object = self.Get(index, None)
+    if tab_object is None:
+      raise IndexError('list index out of range')
+    return tab_object
+
+  def _ListTabs(self, timeout=None):
+    def _IsTab(context):
+      if 'type' in context:
+        return context['type'] == 'page'
+      # TODO: For compatibility with Chrome before r177683.
+      # This check is not completely correct, see crbug.com/190592.
+      return not context['url'].startswith('chrome-extension://')
+    data = self._browser_backend.Request('', timeout=timeout)
+    all_contexts = json.loads(data)
+    tabs = filter(_IsTab, all_contexts)
+    return tabs
+
+  def _UpdateTabList(self):
+    def GetDebuggerUrl(tab_info):
+      if 'webSocketDebuggerUrl' not in tab_info:
+        return None
+      return tab_info['webSocketDebuggerUrl']
+    new_tab_list = map(GetDebuggerUrl, self._ListTabs())
+    self._tab_list = [t for t in self._tab_list
+                      if t in self._tab_dict or t in new_tab_list]
+    self._tab_list += [t for t in new_tab_list
+                       if t is not None and t not in self._tab_list]
+
+  def _FindTabInfo(self, debugger_url):
+    for tab_info in self._ListTabs():
+      if tab_info.get('webSocketDebuggerUrl') == debugger_url:
+        return tab_info
+    return None
diff --git a/tools/telemetry/telemetry/core/backends/chrome/trace_result.py b/tools/telemetry/telemetry/core/backends/chrome/trace_result.py
new file mode 100644
index 0000000..bcbcc3a
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/trace_result.py
@@ -0,0 +1,16 @@
+# 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.
+
+class TraceResult(object):
+  def __init__(self, impl):
+    self._impl = impl
+
+  def Serialize(self, f):
+    """Serializes the trace result to a file-like object"""
+    return self._impl.Serialize(f)
+
+  def AsTimelineModel(self):
+    """Parses the trace result into a timeline model for in-memory
+    manipulation."""
+    return self._impl.AsTimelineModel()
diff --git a/tools/telemetry/telemetry/core/backends/chrome/tracing_backend.py b/tools/telemetry/telemetry/core/backends/chrome/tracing_backend.py
new file mode 100644
index 0000000..4d0c68a
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/tracing_backend.py
@@ -0,0 +1,133 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import cStringIO
+import json
+import logging
+import socket
+import threading
+
+
+from telemetry.core.backends.chrome import trace_result
+from telemetry.core.backends.chrome import websocket
+from telemetry.core.backends.chrome import websocket_browser_connection
+from telemetry.core.timeline import model
+
+
+class TracingUnsupportedException(Exception):
+  pass
+
+# This class supports legacy format of trace presentation within DevTools
+# protocol, where trace data were sent as JSON-serialized strings. DevTools
+# now send the data as raw objects within the protocol message JSON, so there's
+# no need in extra de-serialization. We might want to remove this in the future.
+class TraceResultImpl(object):
+  def __init__(self, tracing_data):
+    self._tracing_data = tracing_data
+
+  def Serialize(self, f):
+    f.write('{"traceEvents": [')
+    d = self._tracing_data
+    # Note: we're not using ','.join here because the strings that are in the
+    # tracing data are typically many megabytes in size. In the fast case, f is
+    # just a file, so by skipping the in memory step we keep our memory
+    # footprint low and avoid additional processing.
+    if len(d) == 0:
+      pass
+    elif len(d) == 1:
+      f.write(d[0])
+    else:
+      f.write(d[0])
+      for i in range(1, len(d)):
+        f.write(',')
+        f.write(d[i])
+    f.write(']}')
+
+  def AsTimelineModel(self):
+    f = cStringIO.StringIO()
+    self.Serialize(f)
+    return model.TimelineModel(
+      event_data=f.getvalue(),
+      shift_world_to_zero=False)
+
+# RawTraceResultImpl differs from TraceResultImpl above in that
+# data are kept as a list of dicts, not strings.
+class RawTraceResultImpl(object):
+  def __init__(self, tracing_data):
+    self._tracing_data = tracing_data
+
+  def Serialize(self, f):
+    f.write('{"traceEvents":')
+    json.dump(self._tracing_data, f)
+    f.write('}')
+
+  def AsTimelineModel(self):
+    return model.TimelineModel(self._tracing_data)
+
+class TracingBackend(object):
+  def __init__(self, devtools_port):
+    self._conn = websocket_browser_connection.WebSocketBrowserConnection(
+        devtools_port)
+    self._thread = None
+    self._tracing_data = []
+
+  def BeginTracing(self, custom_categories=None, timeout=10):
+    self._CheckNotificationSupported()
+    req = {'method': 'Tracing.start'}
+    if custom_categories:
+      req['params'] = {'categories': custom_categories}
+    self._conn.SendRequest(req, timeout)
+    # Tracing.start will send asynchronous notifications containing trace
+    # data, until Tracing.end is called.
+    self._thread = threading.Thread(target=self._TracingReader)
+    self._thread.start()
+
+  def EndTracing(self):
+    req = {'method': 'Tracing.end'}
+    self._conn.SendRequest(req)
+    self._thread.join()
+    self._thread = None
+
+  def GetTraceResultAndReset(self):
+    assert not self._thread
+    if self._tracing_data and type(self._tracing_data[0]) in [str, unicode]:
+      result_impl = TraceResultImpl(self._tracing_data)
+    else:
+      result_impl = RawTraceResultImpl(self._tracing_data)
+    self._tracing_data = []
+    return trace_result.TraceResult(result_impl)
+
+  def _TracingReader(self):
+    while self._conn.socket:
+      try:
+        data = self._conn.socket.recv()
+        if not data:
+          break
+        res = json.loads(data)
+        logging.debug('got [%s]', data)
+        if 'Tracing.dataCollected' == res.get('method'):
+          value = res.get('params', {}).get('value')
+          if type(value) in [str, unicode]:
+            self._tracing_data.append(value)
+          elif type(value) is list:
+            self._tracing_data.extend(value)
+          else:
+            logging.warning('Unexpected type in tracing data')
+        elif 'Tracing.tracingComplete' == res.get('method'):
+          break
+      except (socket.error, websocket.WebSocketException):
+        logging.warning('Timeout waiting for tracing response, unusual.')
+
+  def Close(self):
+    self._conn.Close()
+
+  def _CheckNotificationSupported(self):
+    """Ensures we're running against a compatible version of chrome."""
+    req = {'method': 'Tracing.hasCompleted'}
+    res = self._conn.SyncRequest(req)
+    if res.get('response'):
+      raise TracingUnsupportedException(
+          'Tracing not supported for this browser')
+    elif 'error' in res:
+      return
diff --git a/tools/telemetry/telemetry/core/backends/chrome/tracing_backend_unittest.py b/tools/telemetry/telemetry/core/backends/chrome/tracing_backend_unittest.py
new file mode 100644
index 0000000..afbb3b8
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/tracing_backend_unittest.py
@@ -0,0 +1,89 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import cStringIO
+import json
+import logging
+import unittest
+
+from telemetry.core import util
+from telemetry.core.backends.chrome import tracing_backend
+from telemetry.unittest import tab_test_case
+
+
+class TracingBackendTest(tab_test_case.TabTestCase):
+  def _StartServer(self):
+    self._browser.SetHTTPServerDirectories(util.GetUnittestDataDir())
+
+  def _WaitForAnimationFrame(self):
+    def _IsDone():
+      js_is_done = """done"""
+      return bool(self._tab.EvaluateJavaScript(js_is_done))
+    util.WaitFor(_IsDone, 5)
+
+  def testGotTrace(self):
+    if not self._browser.supports_tracing:
+      logging.warning('Browser does not support tracing, skipping test.')
+      return
+    self._StartServer()
+    self._browser.StartTracing()
+    self._browser.StopTracing()
+
+    # TODO(tengs): check model for correctness after trace_event_importer
+    # is implemented (crbug.com/173327).
+
+
+class TracingResultImplTest(unittest.TestCase):
+  # Override TestCase.run to run a test with all possible
+  # implementations of TraceResult.
+  def __init__(self, method_name):
+    self._traceResultImplClass = None
+    super(TracingResultImplTest, self).__init__(method_name)
+
+  def run(self, result=None):
+    def RawTraceResultImplWrapper(strings):
+      return tracing_backend.RawTraceResultImpl(map(json.loads, strings))
+    classes = [
+        tracing_backend.TraceResultImpl,
+        RawTraceResultImplWrapper
+    ]
+    for cls in classes:
+      self._traceResultImplClass = cls
+      super(TracingResultImplTest, self).run(result)
+
+  def testWrite1(self):
+    ri = self._traceResultImplClass([])
+    f = cStringIO.StringIO()
+    ri.Serialize(f)
+    v = f.getvalue()
+
+    j = json.loads(v)
+    assert 'traceEvents' in j
+    self.assertEquals(j['traceEvents'], [])
+
+  def testWrite2(self):
+    ri = self._traceResultImplClass([
+        '"foo"',
+        '"bar"'])
+    f = cStringIO.StringIO()
+    ri.Serialize(f)
+    v = f.getvalue()
+
+    j = json.loads(v)
+    assert 'traceEvents' in j
+    self.assertEquals(j['traceEvents'], ['foo', 'bar'])
+
+  def testWrite3(self):
+    ri = self._traceResultImplClass([
+        '"foo"',
+        '"bar"',
+        '"baz"'])
+    f = cStringIO.StringIO()
+    ri.Serialize(f)
+    v = f.getvalue()
+
+    j = json.loads(v)
+    assert 'traceEvents' in j
+    self.assertEquals(j['traceEvents'],
+                      ['foo', 'bar', 'baz'])
diff --git a/tools/telemetry/telemetry/core/backends/chrome/websocket.py b/tools/telemetry/telemetry/core/backends/chrome/websocket.py
new file mode 100644
index 0000000..34defe4
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/websocket.py
@@ -0,0 +1,11 @@
+# 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.
+from __future__ import absolute_import
+
+from telemetry.core import util
+
+util.AddDirToPythonPath(
+    util.GetTelemetryDir(), 'third_party', 'websocket-client')
+from websocket import create_connection  # pylint: disable=W0611
+from websocket import WebSocketException  # pylint: disable=W0611
diff --git a/tools/telemetry/telemetry/core/backends/chrome/websocket_browser_connection.py b/tools/telemetry/telemetry/core/backends/chrome/websocket_browser_connection.py
new file mode 100644
index 0000000..a88ed43
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/websocket_browser_connection.py
@@ -0,0 +1,59 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import logging
+import socket
+
+from telemetry.core import util
+from telemetry.core.backends.chrome import websocket
+
+class WebSocketBrowserConnection(object):
+  """Represents a websocket connection to the browser for backends
+     which use one."""
+
+  def __init__(self, devtools_port):
+    debugger_url = 'ws://localhost:%i/devtools/browser' % devtools_port
+    self._socket = websocket.create_connection(debugger_url)
+    self._next_request_id = 0
+    self._cur_socket_timeout = 0
+
+  def Close(self):
+    if self._socket:
+      self._socket.close()
+      self._socket = None
+
+  def SendRequest(self, req, timeout=10):
+    self._SetTimeout(timeout)
+    req['id'] = self._next_request_id
+    self._next_request_id += 1
+    data = json.dumps(req)
+    logging.debug('will send [%s]', data)
+    self._socket.send(data)
+
+  def SyncRequest(self, req, timeout=10):
+    self.SendRequest(req, timeout)
+    while True:
+      try:
+        data = self._socket.recv()
+      except (socket.error, websocket.WebSocketException):
+        raise util.TimeoutException(
+            "Timed out waiting for reply. This is unusual.")
+      res = json.loads(data)
+      logging.debug('got [%s]', data)
+      if res['id'] != req['id']:
+        logging.debug('Dropped reply: %s', json.dumps(res))
+        continue
+      return res
+
+  @property
+  def socket(self):
+    """Returns the socket for raw access. Please be sure you know what
+       you are doing."""
+    return self._socket
+
+  def _SetTimeout(self, timeout):
+    if self._cur_socket_timeout != timeout:
+      self._socket.settimeout(timeout)
+      self._cur_socket_timeout = timeout
diff --git a/tools/telemetry/telemetry/core/backends/chrome/websocket_unittest.py b/tools/telemetry/telemetry/core/backends/chrome/websocket_unittest.py
new file mode 100644
index 0000000..a9a1e8e
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/chrome/websocket_unittest.py
@@ -0,0 +1,10 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import unittest
+
+from telemetry.core.backends.chrome import websocket
+
+class TestWebSocket(unittest.TestCase):
+  def testExports(self):
+    self.assertNotEqual(websocket.create_connection, None)
diff --git a/tools/telemetry/telemetry/core/backends/facebook_credentials_backend.py b/tools/telemetry/telemetry/core/backends/facebook_credentials_backend.py
new file mode 100644
index 0000000..131353f
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/facebook_credentials_backend.py
@@ -0,0 +1,32 @@
+# 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.
+
+from telemetry.core.backends import form_based_credentials_backend
+
+class FacebookCredentialsBackend(
+    form_based_credentials_backend.FormBasedCredentialsBackend):
+  def IsAlreadyLoggedIn(self, tab):
+    return tab.EvaluateJavaScript(
+        'document.getElementById("fbNotificationsList")!== null || '
+        'document.getElementById("m_home_notice")!== null')
+
+  @property
+  def credentials_type(self):
+    return 'facebook'
+
+  @property
+  def url(self):
+    return 'http://www.facebook.com/'
+
+  @property
+  def login_form_id(self):
+    return 'login_form'
+
+  @property
+  def login_input_id(self):
+    return 'email'
+
+  @property
+  def password_input_id(self):
+    return 'pass'
diff --git a/tools/telemetry/telemetry/core/backends/facebook_credentials_backend_unittest.py b/tools/telemetry/telemetry/core/backends/facebook_credentials_backend_unittest.py
new file mode 100644
index 0000000..c19dd1b
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/facebook_credentials_backend_unittest.py
@@ -0,0 +1,16 @@
+# 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.
+from telemetry.core.backends import form_based_credentials_backend_unittest_base
+from telemetry.core.backends import facebook_credentials_backend
+
+class TestFacebookCredentialsBackend(
+    form_based_credentials_backend_unittest_base.
+    FormBasedCredentialsBackendUnitTestBase):
+  def setUp(self):
+    self._credentials_type = 'facebook'
+
+  def testLoginUsingMock(self):
+    self._LoginUsingMock(
+        facebook_credentials_backend.FacebookCredentialsBackend(),
+        'http://www.facebook.com/', 'email', 'pass')
diff --git a/tools/telemetry/telemetry/core/backends/form_based_credentials_backend.py b/tools/telemetry/telemetry/core/backends/form_based_credentials_backend.py
new file mode 100644
index 0000000..4da670f
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/form_based_credentials_backend.py
@@ -0,0 +1,110 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import logging
+
+from telemetry.core import util
+
+
+def _WaitForLoginFormToLoad(backend, login_form_id, tab):
+  def IsFormLoadedOrAlreadyLoggedIn():
+    return tab.EvaluateJavaScript(
+        'document.querySelector("#%s")!== null' % login_form_id) or \
+            backend.IsAlreadyLoggedIn(tab)
+
+  # Wait until the form is submitted and the page completes loading.
+  util.WaitFor(lambda: IsFormLoadedOrAlreadyLoggedIn(), # pylint: disable=W0108
+               60)
+
+def _SubmitFormAndWait(form_id, tab):
+  js = 'document.getElementById("%s").submit();' % form_id
+  tab.ExecuteJavaScript(js)
+
+  def IsLoginStillHappening():
+    return tab.EvaluateJavaScript(
+        'document.querySelector("#%s")!== null' % form_id)
+
+  # Wait until the form is submitted and the page completes loading.
+  util.WaitFor(lambda: not IsLoginStillHappening(), 60)
+
+class FormBasedCredentialsBackend(object):
+  def __init__(self):
+    self._logged_in = False
+
+  def IsAlreadyLoggedIn(self, tab):
+    raise NotImplementedError()
+
+  @property
+  def credentials_type(self):
+    raise NotImplementedError()
+
+  @property
+  def url(self):
+    raise NotImplementedError()
+
+  @property
+  def login_form_id(self):
+    raise NotImplementedError()
+
+  @property
+  def login_input_id(self):
+    raise NotImplementedError()
+
+  @property
+  def password_input_id(self):
+    raise NotImplementedError()
+
+  def IsLoggedIn(self):
+    return self._logged_in
+
+  def _ResetLoggedInState(self):
+    """Makes the backend think we're not logged in even though we are.
+    Should only be used in unit tests to simulate --dont-override-profile.
+    """
+    self._logged_in = False
+
+  def LoginNeeded(self, tab, config):
+    """Logs in to a test account.
+
+    Raises:
+      RuntimeError: if could not get credential information.
+    """
+    if self._logged_in:
+      return True
+
+    if 'username' not in config or 'password' not in config:
+      message = ('Credentials for "%s" must include username and password.' %
+                 self.credentials_type)
+      raise RuntimeError(message)
+
+    logging.debug('Logging into %s account...' % self.credentials_type)
+
+    try:
+      logging.info('Loading %s...', self.url)
+      tab.Navigate(self.url)
+      _WaitForLoginFormToLoad(self, self.login_form_id, tab)
+
+      if self.IsAlreadyLoggedIn(tab):
+        self._logged_in = True
+        return True
+
+      tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
+      logging.info('Loaded page: %s', self.url)
+
+      email_id = 'document.querySelector("#%s").%s.value = "%s"; ' % (
+          self.login_form_id, self.login_input_id, config['username'])
+      password = 'document.querySelector("#%s").%s.value = "%s"; ' % (
+          self.login_form_id, self.password_input_id, config['password'])
+      tab.ExecuteJavaScript(email_id)
+      tab.ExecuteJavaScript(password)
+
+      _SubmitFormAndWait(self.login_form_id, tab)
+
+      self._logged_in = True
+      return True
+    except util.TimeoutException:
+      logging.warning('Timed out while loading: %s', self.url)
+      return False
+
+  def LoginNoLongerNeeded(self, tab): # pylint: disable=W0613
+    assert self._logged_in
diff --git a/tools/telemetry/telemetry/core/backends/form_based_credentials_backend_unittest_base.py b/tools/telemetry/telemetry/core/backends/form_based_credentials_backend_unittest_base.py
new file mode 100644
index 0000000..2edc586
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/form_based_credentials_backend_unittest_base.py
@@ -0,0 +1,128 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import logging
+import os
+import unittest
+
+from telemetry.core import browser_finder
+from telemetry.core import util
+from telemetry.unittest import simple_mock
+from telemetry.unittest import options_for_unittests
+from telemetry.unittest import DisabledTest
+
+_ = simple_mock.DONT_CARE
+
+
+def _GetCredentialsPath():
+  # TODO: This shouldn't depend on tools/perf.
+  credentials_path = os.path.join(util.GetChromiumSrcDir(),
+      'tools', 'perf', 'data', 'credentials.json')
+  if not os.path.exists(credentials_path):
+    return None
+  return credentials_path
+
+
+class FormBasedCredentialsBackendUnitTestBase(unittest.TestCase):
+  def setUp(self):
+    self._credentials_type = None
+
+  @DisabledTest
+  def testRealLoginIfPossible(self):
+    credentials_path = _GetCredentialsPath()
+    if not credentials_path:
+      logging.warning('Credentials file not found, skipping test.')
+      return
+
+    options = options_for_unittests.GetCopy()
+    with browser_finder.FindBrowser(options).Create() as b:
+      b.Start()
+      b.credentials.credentials_path = credentials_path
+      if not b.credentials.CanLogin(self._credentials_type):
+        return
+      ret = b.credentials.LoginNeeded(b.tabs[0], self._credentials_type)
+      self.assertTrue(ret)
+
+  @DisabledTest
+  def testRealLoginWithDontOverrideProfileIfPossible(self):
+    credentials_path = _GetCredentialsPath()
+    if not credentials_path:
+      logging.warning('Credentials file not found, skipping test.')
+      return
+
+    options = options_for_unittests.GetCopy()
+
+    # Login once to make sure our default profile is logged in.
+    with browser_finder.FindBrowser(options).Create() as b:
+      b.Start()
+      b.credentials.credentials_path = credentials_path
+
+      if not b.credentials.CanLogin(self._credentials_type):
+        return
+
+      tab = b.tabs[0]
+
+      # Should not be logged in, since this is a fresh credentials
+      # instance.
+      self.assertFalse(b.credentials.IsLoggedIn(self._credentials_type))
+
+      # Log in.
+      ret = b.credentials.LoginNeeded(tab, self._credentials_type)
+
+      # Make sure login was successful.
+      self.assertTrue(ret)
+      self.assertTrue(b.credentials.IsLoggedIn(self._credentials_type))
+
+      # Reset state. Now the backend thinks we're logged out, even
+      # though we are logged in in our current browser session. This
+      # simulates the effects of running with --dont-override-profile.
+      b.credentials._ResetLoggedInState() # pylint: disable=W0212
+
+      # Make sure the backend thinks we're logged out.
+      self.assertFalse(b.credentials.IsLoggedIn(self._credentials_type))
+      self.assertTrue(b.credentials.CanLogin(self._credentials_type))
+
+      # Attempt to login again. This should detect that we've hit
+      # the 'logged in' page instead of the login form, and succeed
+      # instead of timing out.
+      ret = b.credentials.LoginNeeded(tab, self._credentials_type)
+
+      # Make sure our login attempt did in fact succeed and set the
+      # backend's internal state to 'logged in'.
+      self.assertTrue(ret)
+      self.assertTrue(b.credentials.IsLoggedIn(self._credentials_type))
+
+  def testLoginUsingMock(self):
+    raise NotImplementedError()
+
+  def _LoginUsingMock(self, backend, login_page_url, email_element_id,
+                      password_element_id): # pylint: disable=R0201
+    tab = simple_mock.MockObject()
+
+    config = {'username': 'blah',
+              'password': 'blargh'}
+
+    tab.ExpectCall('Navigate', login_page_url)
+    tab.ExpectCall('EvaluateJavaScript', _).WillReturn(False)
+    tab.ExpectCall('EvaluateJavaScript', _).WillReturn(True)
+    tab.ExpectCall('EvaluateJavaScript', _).WillReturn(False)
+    tab.ExpectCall('WaitForDocumentReadyStateToBeInteractiveOrBetter')
+
+    def VerifyEmail(js):
+      assert email_element_id in js
+      assert 'blah' in js
+    tab.ExpectCall('ExecuteJavaScript', _).WhenCalled(VerifyEmail)
+
+    def VerifyPw(js):
+      assert password_element_id in js
+      assert 'largh' in js
+    tab.ExpectCall('ExecuteJavaScript', _).WhenCalled(VerifyPw)
+
+    def VerifySubmit(js):
+      assert '.submit' in js
+    tab.ExpectCall('ExecuteJavaScript', _).WhenCalled(VerifySubmit)
+
+    # Checking for form still up.
+    tab.ExpectCall('EvaluateJavaScript', _).WillReturn(False)
+
+    backend.LoginNeeded(tab, config)
diff --git a/tools/telemetry/telemetry/core/backends/google_credentials_backend.py b/tools/telemetry/telemetry/core/backends/google_credentials_backend.py
new file mode 100644
index 0000000..7a10795
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/google_credentials_backend.py
@@ -0,0 +1,31 @@
+# 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.
+
+from telemetry.core.backends import form_based_credentials_backend
+
+class GoogleCredentialsBackend(
+    form_based_credentials_backend.FormBasedCredentialsBackend):
+  def IsAlreadyLoggedIn(self, tab):
+    return tab.EvaluateJavaScript(
+        'document.getElementById("gb")!== null')
+
+  @property
+  def credentials_type(self):
+    return 'google'
+
+  @property
+  def url(self):
+    return 'https://accounts.google.com/'
+
+  @property
+  def login_form_id(self):
+    return 'gaia_loginform'
+
+  @property
+  def login_input_id(self):
+    return 'Email'
+
+  @property
+  def password_input_id(self):
+    return 'Passwd'
diff --git a/tools/telemetry/telemetry/core/backends/google_credentials_backend_unittest.py b/tools/telemetry/telemetry/core/backends/google_credentials_backend_unittest.py
new file mode 100644
index 0000000..f98cc4d
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/google_credentials_backend_unittest.py
@@ -0,0 +1,16 @@
+# 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.
+from telemetry.core.backends import form_based_credentials_backend_unittest_base
+from telemetry.core.backends import google_credentials_backend
+
+class TestGoogleCredentialsBackend(
+    form_based_credentials_backend_unittest_base.
+    FormBasedCredentialsBackendUnitTestBase):
+  def setUp(self):
+    self._credentials_type = 'google'
+
+  def testLoginUsingMock(self):
+    self._LoginUsingMock(google_credentials_backend.GoogleCredentialsBackend(),
+                         'https://accounts.google.com/', 'Email',
+                         'Passwd')
diff --git a/tools/telemetry/telemetry/core/backends/png_bitmap.py b/tools/telemetry/telemetry/core/backends/png_bitmap.py
new file mode 100644
index 0000000..98c7f47
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/png_bitmap.py
@@ -0,0 +1,136 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import base64
+import cStringIO
+
+from telemetry.core import util
+
+util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'png')
+import png  # pylint: disable=F0401
+
+
+class PngColor(object):
+  """Encapsulates an RGB color retreived from a PngBitmap"""
+
+  def __init__(self, r, g, b, a=255):
+    self.r = r
+    self.g = g
+    self.b = b
+    self.a = a
+
+  def IsEqual(self, expected_color, tolerance=0):
+    """Verifies that the color is within a given tolerance of
+    the expected color"""
+    r_diff = abs(self.r - expected_color.r)
+    g_diff = abs(self.g - expected_color.g)
+    b_diff = abs(self.b - expected_color.b)
+    a_diff = abs(self.a - expected_color.a)
+    return (r_diff <= tolerance and g_diff <= tolerance
+        and b_diff <= tolerance and a_diff <= tolerance)
+
+  def AssertIsRGB(self, r, g, b, tolerance=0):
+    assert self.IsEqual(PngColor(r, g, b), tolerance)
+
+  def AssertIsRGBA(self, r, g, b, a, tolerance=0):
+    assert self.IsEqual(PngColor(r, g, b, a), tolerance)
+
+
+class PngBitmap(object):
+  """Utilities for parsing and inspecting a PNG"""
+
+  def __init__(self, png_data):
+    self._png_data = png_data
+    self._png = png.Reader(bytes=self._png_data)
+    rgba8_data = self._png.asRGBA8()
+    self._width = rgba8_data[0]
+    self._height = rgba8_data[1]
+    self._pixels = list(rgba8_data[2])
+    self._metadata = rgba8_data[3]
+
+  @property
+  def width(self):
+    """Width of the snapshot"""
+    return self._width
+
+  @property
+  def height(self):
+    """Height of the snapshot"""
+    return self._height
+
+  def GetPixelColor(self, x, y):
+    """Returns a PngColor for the pixel at (x, y)"""
+    row = self._pixels[y]
+    offset = x * 4
+    return PngColor(row[offset], row[offset+1], row[offset+2], row[offset+3])
+
+  def WriteFile(self, path):
+    with open(path, "wb") as f:
+      f.write(self._png_data)
+
+  @staticmethod
+  def FromFile(path):
+    with open(path, "rb") as f:
+      return PngBitmap(f.read())
+
+  @staticmethod
+  def FromBase64(base64_png):
+    return PngBitmap(base64.b64decode(base64_png))
+
+  def IsEqual(self, expected_png, tolerance=0):
+    """Verifies that two PngBitmaps are identical within a given tolerance"""
+
+    # Dimensions must be equal
+    if self.width != expected_png.width or self.height != expected_png.height:
+      return False
+
+    # Loop over each pixel and test for equality
+    for y in range(self.height):
+      for x in range(self.width):
+        c0 = self.GetPixelColor(x, y)
+        c1 = expected_png.GetPixelColor(x, y)
+        if not c0.IsEqual(c1, tolerance):
+          return False
+
+    return True
+
+  def Diff(self, other_png):
+    """Returns a new PngBitmap that represents the difference between this image
+    and another PngBitmap"""
+
+    # Output dimensions will be the maximum of the two input dimensions
+    out_width = max(self.width, other_png.width)
+    out_height = max(self.height, other_png.height)
+
+    diff = [[0 for x in xrange(out_width * 3)] for x in xrange(out_height)]
+
+    # Loop over each pixel and test for equality
+    for y in range(out_height):
+      for x in range(out_width):
+        if x < self.width and y < self.height:
+          c0 = self.GetPixelColor(x, y)
+        else:
+          c0 = PngColor(0, 0, 0, 0)
+
+        if x < other_png.width and y < other_png.height:
+          c1 = other_png.GetPixelColor(x, y)
+        else:
+          c1 = PngColor(0, 0, 0, 0)
+
+        offset = x * 3
+        diff[y][offset] = abs(c0.r - c1.r)
+        diff[y][offset+1] = abs(c0.g - c1.g)
+        diff[y][offset+2] = abs(c0.b - c1.b)
+
+    # This particular method can only save to a file, so the result will be
+    # written into an in-memory buffer and read back into a PngBitmap
+    diff_img = png.from_array(diff, mode='RGB')
+    output = cStringIO.StringIO()
+    try:
+      diff_img.save(output)
+      diff_png = PngBitmap(output.getvalue())
+    finally:
+      output.close()
+
+    return diff_png
+
diff --git a/tools/telemetry/telemetry/core/backends/png_bitmap_unittest.py b/tools/telemetry/telemetry/core/backends/png_bitmap_unittest.py
new file mode 100644
index 0000000..0e3c233
--- /dev/null
+++ b/tools/telemetry/telemetry/core/backends/png_bitmap_unittest.py
@@ -0,0 +1,82 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+import os
+
+from telemetry.core import util
+from telemetry.core.backends import png_bitmap
+
+
+# This is a simple base64 encoded 2x2 PNG which contains, in order, a single
+# Red, Yellow, Blue, and Green pixel.
+test_png = """
+iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91
+JpzAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACx
+MBAJqcGAAAABZJREFUCNdj/M/AwPCfgYGB4T/DfwY
+AHAAD/iOWZXsAAAAASUVORK5CYII=
+"""
+
+
+test_png_path = os.path.join(util.GetUnittestDataDir(), 'test_png.png')
+test_png_2_path = os.path.join(util.GetUnittestDataDir(), 'test_png_2.png')
+
+
+class PngBitmapTest(unittest.TestCase):
+  def testReadFromBase64(self):
+    png = png_bitmap.PngBitmap.FromBase64(test_png)
+
+    self.assertEquals(2, png.width)
+    self.assertEquals(2, png.height)
+
+    png.GetPixelColor(0, 0).AssertIsRGB(255, 0, 0)
+    png.GetPixelColor(1, 1).AssertIsRGB(0, 255, 0)
+    png.GetPixelColor(0, 1).AssertIsRGB(0, 0, 255)
+    png.GetPixelColor(1, 0).AssertIsRGB(255, 255, 0)
+
+  def testReadFromFile(self):
+    file_png = png_bitmap.PngBitmap.FromFile(test_png_path)
+
+    self.assertEquals(2, file_png.width)
+    self.assertEquals(2, file_png.height)
+
+    file_png.GetPixelColor(0, 0).AssertIsRGB(255, 0, 0)
+    file_png.GetPixelColor(1, 1).AssertIsRGB(0, 255, 0)
+    file_png.GetPixelColor(0, 1).AssertIsRGB(0, 0, 255)
+    file_png.GetPixelColor(1, 0).AssertIsRGB(255, 255, 0)
+
+  def testIsEqual(self):
+    png = png_bitmap.PngBitmap.FromBase64(test_png)
+    file_png = png_bitmap.PngBitmap.FromFile(test_png_path)
+    self.assertTrue(png.IsEqual(file_png))
+
+  def testDiff(self):
+    file_png = png_bitmap.PngBitmap.FromFile(test_png_path)
+    file_png_2 = png_bitmap.PngBitmap.FromFile(test_png_2_path)
+
+    diff_png = file_png.Diff(file_png)
+
+    self.assertEquals(2, diff_png.width)
+    self.assertEquals(2, diff_png.height)
+
+    diff_png.GetPixelColor(0, 0).AssertIsRGB(0, 0, 0)
+    diff_png.GetPixelColor(1, 1).AssertIsRGB(0, 0, 0)
+    diff_png.GetPixelColor(0, 1).AssertIsRGB(0, 0, 0)
+    diff_png.GetPixelColor(1, 0).AssertIsRGB(0, 0, 0)
+
+    diff_png = file_png.Diff(file_png_2)
+
+    self.assertEquals(3, diff_png.width)
+    self.assertEquals(3, diff_png.height)
+
+    diff_png.GetPixelColor(0, 0).AssertIsRGB(0, 255, 255)
+    diff_png.GetPixelColor(1, 1).AssertIsRGB(255, 0, 255)
+    diff_png.GetPixelColor(0, 1).AssertIsRGB(255, 255, 0)
+    diff_png.GetPixelColor(1, 0).AssertIsRGB(0, 0, 255)
+
+    diff_png.GetPixelColor(0, 2).AssertIsRGB(255, 255, 255)
+    diff_png.GetPixelColor(1, 2).AssertIsRGB(255, 255, 255)
+    diff_png.GetPixelColor(2, 0).AssertIsRGB(255, 255, 255)
+    diff_png.GetPixelColor(2, 1).AssertIsRGB(255, 255, 255)
+    diff_png.GetPixelColor(2, 2).AssertIsRGB(255, 255, 255)
diff --git a/tools/telemetry/telemetry/core/backends/webdriver/webdriver_desktop_browser_finder.py b/tools/telemetry/telemetry/core/backends/webdriver/webdriver_desktop_browser_finder.py
index e75fcba..a66721a 100644
--- a/tools/telemetry/telemetry/core/backends/webdriver/webdriver_desktop_browser_finder.py
+++ b/tools/telemetry/telemetry/core/backends/webdriver/webdriver_desktop_browser_finder.py
@@ -12,6 +12,7 @@
 from telemetry.core import platform
 from telemetry.core import util
 from telemetry.core.backends.webdriver import webdriver_browser_backend
+from telemetry.page import cloud_storage
 
 # Try to import the selenium python lib which may be not available.
 util.AddDirToPythonPath(
@@ -35,8 +36,8 @@
 class PossibleWebDriverBrowser(possible_browser.PossibleBrowser):
   """A browser that can be controlled through webdriver API."""
 
-  def __init__(self, browser_type, options):
-    super(PossibleWebDriverBrowser, self).__init__(browser_type, options)
+  def __init__(self, browser_type, finder_options):
+    super(PossibleWebDriverBrowser, self).__init__(browser_type, finder_options)
 
   def CreateWebDriverBackend(self):
     raise NotImplementedError()
@@ -46,7 +47,7 @@
     b = browser.Browser(backend, platform.CreatePlatformBackendForCurrentOS())
     return b
 
-  def SupportsOptions(self, options):
+  def SupportsOptions(self, finder_options):
     # TODO(chrisgao): Check if some options are not supported.
     return True
 
@@ -56,23 +57,25 @@
 
 
 class PossibleDesktopIE(PossibleWebDriverBrowser):
-  def __init__(self, browser_type, options, architecture):
-    super(PossibleDesktopIE, self).__init__(browser_type, options)
+  def __init__(self, browser_type, finder_options, architecture):
+    super(PossibleDesktopIE, self).__init__(browser_type, finder_options)
     self._architecture = architecture
 
   def CreateWebDriverBackend(self):
     assert webdriver
     def DriverCreator():
-      # TODO(chrisgao): Check in IEDriverServer.exe and specify path to it when
-      # creating the webdriver instance. crbug.com/266170
-      return webdriver.Ie()
+      ie_driver_exe = os.path.join(util.GetTelemetryDir(), 'bin',
+                                   'IEDriverServer_%s.exe' % self._architecture)
+      cloud_storage.GetIfChanged(cloud_storage.CHROMIUM_TELEMETRY_BUCKET,
+                                 ie_driver_exe)
+      return webdriver.Ie(executable_path=ie_driver_exe)
     return webdriver_browser_backend.WebDriverBrowserBackend(
-        DriverCreator, False, self.options)
+        DriverCreator, False, self.finder_options)
 
 def SelectDefaultBrowser(_):
   return None
 
-def FindAllAvailableBrowsers(options):
+def FindAllAvailableBrowsers(finder_options):
   """Finds all the desktop browsers available on this machine."""
   browsers = []
   if not webdriver:
@@ -91,6 +94,6 @@
         continue
       if os.path.exists(os.path.join(ie_info['path'], ie_path)):
         browsers.append(
-            PossibleDesktopIE(ie_info['type'], options, architecture))
+            PossibleDesktopIE(ie_info['type'], finder_options, architecture))
 
   return browsers
diff --git a/tools/telemetry/telemetry/core/backends/webdriver/webdriver_tab_backend.py b/tools/telemetry/telemetry/core/backends/webdriver/webdriver_tab_backend.py
index df2e7ae..d4dd75c 100644
--- a/tools/telemetry/telemetry/core/backends/webdriver/webdriver_tab_backend.py
+++ b/tools/telemetry/telemetry/core/backends/webdriver/webdriver_tab_backend.py
@@ -4,8 +4,7 @@
 
 import logging
 
-# TODO(chrisgao): Make png_bitmap sharable for both chrome and webdriver.
-from telemetry.core.chrome import png_bitmap
+from telemetry.core.backends import png_bitmap
 
 class WebDriverTabBackend(object):
   def __init__(self, browser_backend, window_handle):
diff --git a/tools/telemetry/telemetry/core/browser_credentials.py b/tools/telemetry/telemetry/core/browser_credentials.py
index d53c6a8..80fb181 100644
--- a/tools/telemetry/telemetry/core/browser_credentials.py
+++ b/tools/telemetry/telemetry/core/browser_credentials.py
@@ -1,15 +1,17 @@
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
+
 import logging
 import json
 import os
 
 from telemetry.core import util
-from telemetry.core.chrome import facebook_credentials_backend
-from telemetry.core.chrome import google_credentials_backend
+from telemetry.core.backends import facebook_credentials_backend
+from telemetry.core.backends import google_credentials_backend
 from telemetry.unittest import options_for_unittests
 
+
 class BrowserCredentials(object):
   def __init__(self, backends = None):
     self._credentials = {}
diff --git a/tools/telemetry/telemetry/core/browser_finder.py b/tools/telemetry/telemetry/core/browser_finder.py
index 0248857..3afd574 100644
--- a/tools/telemetry/telemetry/core/browser_finder.py
+++ b/tools/telemetry/telemetry/core/browser_finder.py
@@ -6,9 +6,9 @@
 import logging
 
 from telemetry.core.backends.webdriver import webdriver_desktop_browser_finder
-from telemetry.core.chrome import android_browser_finder
-from telemetry.core.chrome import cros_browser_finder
-from telemetry.core.chrome import desktop_browser_finder
+from telemetry.core.backends.chrome import android_browser_finder
+from telemetry.core.backends.chrome import cros_browser_finder
+from telemetry.core.backends.chrome import desktop_browser_finder
 
 BROWSER_FINDERS = [
   desktop_browser_finder,
diff --git a/tools/telemetry/telemetry/core/browser_options.py b/tools/telemetry/telemetry/core/browser_options.py
index 4078fe4..9e082c5 100644
--- a/tools/telemetry/telemetry/core/browser_options.py
+++ b/tools/telemetry/telemetry/core/browser_options.py
@@ -16,7 +16,7 @@
 from telemetry.core import wpr_modes
 from telemetry.core.platform.profiler import profiler_finder
 
-class BrowserOptions(optparse.Values):
+class BrowserFinderOptions(optparse.Values):
   """Options to be used for discovering and launching a browser."""
 
   def __init__(self, browser_type=None):
@@ -39,6 +39,9 @@
     self.extensions_to_load = []
     self.clear_sytem_cache_for_browser_and_profile_on_start = False
 
+    # If set, copy the generated profile to this path on exit.
+    self.output_profile_path = None
+
     self.cros_remote = None
     self.wpr_mode = wpr_modes.WPR_OFF
 
@@ -219,9 +222,10 @@
       # Parse repeat options
       self.repeat_options.UpdateFromParseResults(self, parser)
 
-      # TODO(jeremy): I'm in the process of adding explicit knowledge of profile
-      # directories to Telemetry. As part of this work profile_type needs to be
-      # reworked to not override profile_dir.
+      if self.profile_dir and self.profile_type != 'clean':
+        raise Exception("It's illegal to specify both --profile-type and"
+            " --profile-dir.")
+
       if not self.profile_dir:
         self.profile_dir = profile_types.GetProfileDir(self.profile_type)
 
diff --git a/tools/telemetry/telemetry/core/browser_options_unittest.py b/tools/telemetry/telemetry/core/browser_options_unittest.py
index 9f016fa..c06a480 100644
--- a/tools/telemetry/telemetry/core/browser_options_unittest.py
+++ b/tools/telemetry/telemetry/core/browser_options_unittest.py
@@ -8,21 +8,21 @@
 
 class BrowserOptionsTest(unittest.TestCase):
   def testDefaults(self):
-    options = browser_options.BrowserOptions()
+    options = browser_options.BrowserFinderOptions()
     parser = options.CreateParser()
     parser.add_option('-x', action='store', default=3)
     parser.parse_args(['--browser', 'any'])
     self.assertEquals(options.x, 3) # pylint: disable=E1101
 
   def testDefaultsPlusOverride(self):
-    options = browser_options.BrowserOptions()
+    options = browser_options.BrowserFinderOptions()
     parser = options.CreateParser()
     parser.add_option('-x', action='store', default=3)
     parser.parse_args(['--browser', 'any', '-x', 10])
     self.assertEquals(options.x, 10) # pylint: disable=E1101
 
   def testDefaultsDontClobberPresetValue(self):
-    options = browser_options.BrowserOptions()
+    options = browser_options.BrowserFinderOptions()
     setattr(options, 'x', 7)
     parser = options.CreateParser()
     parser.add_option('-x', action='store', default=3)
@@ -30,21 +30,21 @@
     self.assertEquals(options.x, 7) # pylint: disable=E1101
 
   def testCount0(self):
-    options = browser_options.BrowserOptions()
+    options = browser_options.BrowserFinderOptions()
     parser = options.CreateParser()
     parser.add_option('-x', action='count', dest='v')
     parser.parse_args(['--browser', 'any'])
     self.assertEquals(options.v, None) # pylint: disable=E1101
 
   def testCount2(self):
-    options = browser_options.BrowserOptions()
+    options = browser_options.BrowserFinderOptions()
     parser = options.CreateParser()
     parser.add_option('-x', action='count', dest='v')
     parser.parse_args(['--browser', 'any', '-xx'])
     self.assertEquals(options.v, 2) # pylint: disable=E1101
 
   def testOptparseMutabilityWhenSpecified(self):
-    options = browser_options.BrowserOptions()
+    options = browser_options.BrowserFinderOptions()
     parser = options.CreateParser()
     parser.add_option('-x', dest='verbosity', action='store_true')
     options_ret, _ = parser.parse_args(['--browser', 'any', '-x'])
@@ -52,7 +52,7 @@
     self.assertTrue(options.verbosity)
 
   def testOptparseMutabilityWhenNotSpecified(self):
-    options = browser_options.BrowserOptions()
+    options = browser_options.BrowserFinderOptions()
 
     parser = options.CreateParser()
     parser.add_option('-x', dest='verbosity', action='store_true')
@@ -61,19 +61,19 @@
     self.assertFalse(options.verbosity)
 
   def testProfileDirDefault(self):
-    options = browser_options.BrowserOptions()
+    options = browser_options.BrowserFinderOptions()
     parser = options.CreateParser()
     parser.parse_args(['--browser', 'any'])
     self.assertEquals(options.profile_dir, None)
 
   def testProfileDir(self):
-    options = browser_options.BrowserOptions()
+    options = browser_options.BrowserFinderOptions()
     parser = options.CreateParser()
     parser.parse_args(['--browser', 'any', '--profile-dir', 'foo'])
     self.assertEquals(options.profile_dir, 'foo')
 
   def testMergeDefaultValues(self):
-    options = browser_options.BrowserOptions()
+    options = browser_options.BrowserFinderOptions()
     options.already_true = True
     options.already_false = False
     options.override_to_true = False
diff --git a/tools/telemetry/telemetry/core/chrome/__init__.py b/tools/telemetry/telemetry/core/chrome/__init__.py
deleted file mode 100644
index 96196cf..0000000
--- a/tools/telemetry/telemetry/core/chrome/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
diff --git a/tools/telemetry/telemetry/core/chrome/adb_commands.py b/tools/telemetry/telemetry/core/chrome/adb_commands.py
deleted file mode 100644
index d1ee0bb..0000000
--- a/tools/telemetry/telemetry/core/chrome/adb_commands.py
+++ /dev/null
@@ -1,181 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-"""Brings in Chrome Android's android_commands module, which itself is a
-thin(ish) wrapper around adb."""
-import os
-
-from telemetry.core import util
-
-# This is currently a thin wrapper around Chrome Android's
-# build scripts, located in chrome/build/android. This file exists mainly to
-# deal with locating the module.
-
-util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android')
-try:
-  from pylib import android_commands  # pylint: disable=F0401
-  from pylib import cmd_helper  # pylint: disable=F0401
-  from pylib import forwarder  # pylint: disable=F0401
-  from pylib import ports  # pylint: disable=F0401
-except Exception:
-  android_commands = None
-
-
-def IsAndroidSupported():
-  return android_commands != None
-
-
-def GetAttachedDevices():
-  """Returns a list of attached, online android devices.
-
-  If a preferred device has been set with ANDROID_SERIAL, it will be first in
-  the returned list."""
-  return android_commands.GetAttachedDevices()
-
-
-def AllocateTestServerPort():
-  return ports.AllocateTestServerPort()
-
-
-def ResetTestServerPortAllocation():
-  return ports.ResetTestServerPortAllocation()
-
-
-def GetOutDirectory():
-  return cmd_helper.OutDirectory.get()
-
-
-class AdbCommands(object):
-  """A thin wrapper around ADB"""
-
-  def __init__(self, device):
-    self._adb = android_commands.AndroidCommands(device)
-    self._device = device
-
-  def device(self):
-    return self._device
-
-  def Adb(self):
-    return self._adb
-
-  def Forward(self, local, remote):
-    ret = self._adb.Adb().SendCommand('forward %s %s' % (local, remote))
-    assert ret == ''
-
-  def RunShellCommand(self, command, timeout_time=20, log_result=False):
-    """Send a command to the adb shell and return the result.
-
-    Args:
-      command: String containing the shell command to send. Must not include
-               the single quotes as we use them to escape the whole command.
-      timeout_time: Number of seconds to wait for command to respond before
-        retrying, used by AdbInterface.SendShellCommand.
-      log_result: Boolean to indicate whether we should log the result of the
-                  shell command.
-
-    Returns:
-      list containing the lines of output received from running the command
-    """
-    return self._adb.RunShellCommand(command, timeout_time, log_result)
-
-  def CloseApplication(self, package):
-    """Attempt to close down the application, using increasing violence.
-
-    Args:
-      package: Name of the process to kill off, e.g.
-      com.google.android.apps.chrome
-    """
-    self._adb.CloseApplication(package)
-
-  def KillAll(self, process):
-    """Android version of killall, connected via adb.
-
-    Args:
-      process: name of the process to kill off
-
-    Returns:
-      the number of processess killed
-    """
-    return self._adb.KillAll(process)
-
-  def ExtractPid(self, process_name):
-    """Extracts Process Ids for a given process name from Android Shell.
-
-    Args:
-      process_name: name of the process on the device.
-
-    Returns:
-      List of all the process ids (as strings) that match the given name.
-      If the name of a process exactly matches the given name, the pid of
-      that process will be inserted to the front of the pid list.
-    """
-    return self._adb.ExtractPid(process_name)
-
-  def StartActivity(self, package, activity, wait_for_completion=False,
-                    action='android.intent.action.VIEW',
-                    category=None, data=None,
-                    extras=None, trace_file_name=None,
-                    flags=None):
-    """Starts |package|'s activity on the device.
-
-    Args:
-      package: Name of package to start (e.g. 'com.google.android.apps.chrome').
-      activity: Name of activity (e.g. '.Main' or
-        'com.google.android.apps.chrome.Main').
-      wait_for_completion: wait for the activity to finish launching (-W flag).
-      action: string (e.g. 'android.intent.action.MAIN'). Default is VIEW.
-      category: string (e.g. 'android.intent.category.HOME')
-      data: Data string to pass to activity (e.g. 'http://www.example.com/').
-      extras: Dict of extras to pass to activity. Values are significant.
-      trace_file_name: If used, turns on and saves the trace to this file name.
-    """
-    return self._adb.StartActivity(package, activity, wait_for_completion,
-                    action,
-                    category, data,
-                    extras, trace_file_name,
-                    flags)
-
-  def Push(self, local, remote):
-    return self._adb.Adb().Push(local, remote)
-
-  def Pull(self, remote, local):
-    return self._adb.Adb().Pull(remote, local)
-
-  def FileExistsOnDevice(self, file_name):
-    return self._adb.FileExistsOnDevice(file_name)
-
-  def IsRootEnabled(self):
-    return self._adb.IsRootEnabled()
-
-def HasForwarder(buildtype=None):
-  if not buildtype:
-    return (HasForwarder(buildtype='Release') or
-            HasForwarder(buildtype='Debug'))
-  return (os.path.exists(os.path.join(GetOutDirectory(), buildtype,
-                                      'device_forwarder')) and
-          os.path.exists(os.path.join(GetOutDirectory(), buildtype,
-                                      'host_forwarder')))
-
-class Forwarder(object):
-  def __init__(self, adb, *port_pairs):
-    self._adb = adb.Adb()
-    self._host_port = port_pairs[0].local_port
-
-    new_port_pairs = [(port_pair.local_port, port_pair.remote_port)
-                      for port_pair in port_pairs]
-
-    self._port_pairs = new_port_pairs
-    forwarder.Forwarder.Map(new_port_pairs, self._adb)
-
-  @staticmethod
-  def _GetBuildType():
-    assert HasForwarder()
-    return 'Debug' if HasForwarder('Debug') else 'Release'
-
-  @property
-  def url(self):
-    return 'http://localhost:%i' % self._host_port
-
-  def Close(self):
-    for (device_port, _) in self._port_pairs:
-      forwarder.Forwarder.UnmapDevicePort(device_port, self._adb)
diff --git a/tools/telemetry/telemetry/core/chrome/android_browser_backend.py b/tools/telemetry/telemetry/core/chrome/android_browser_backend.py
deleted file mode 100644
index 34aa933..0000000
--- a/tools/telemetry/telemetry/core/chrome/android_browser_backend.py
+++ /dev/null
@@ -1,304 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import logging
-import os
-import subprocess
-import sys
-import time
-
-from telemetry.core import exceptions
-from telemetry.core import util
-from telemetry.core.backends import browser_backend
-from telemetry.core.backends.chrome import chrome_browser_backend
-from telemetry.core.chrome import adb_commands
-
-
-class AndroidBrowserBackendSettings(object):
-  def __init__(self, adb, activity, cmdline_file, package, pseudo_exec_name):
-    self.adb = adb
-    self.activity = activity
-    self.cmdline_file = cmdline_file
-    self.package = package
-    self.pseudo_exec_name = pseudo_exec_name
-
-  def GetDevtoolsRemotePort(self):
-    raise NotImplementedError()
-
-  def RemoveProfile(self):
-    self.adb.RunShellCommand(
-        'su -c rm -r "%s"' % self.profile_dir)
-
-  def PushProfile(self, _):
-    logging.critical('Profiles cannot be overriden with current configuration')
-    sys.exit(1)
-
-  @property
-  def is_content_shell(self):
-    return False
-
-  @property
-  def profile_dir(self):
-    raise NotImplementedError()
-
-
-class ChromeBackendSettings(AndroidBrowserBackendSettings):
-  # Stores a default Preferences file, re-used to speed up "--page-repeat".
-  _default_preferences_file = None
-
-  def __init__(self, adb, package):
-    super(ChromeBackendSettings, self).__init__(
-        adb=adb,
-        activity='com.google.android.apps.chrome.Main',
-        cmdline_file='/data/local/chrome-command-line',
-        package=package,
-        pseudo_exec_name='chrome')
-
-  def GetDevtoolsRemotePort(self):
-    return 'localabstract:chrome_devtools_remote'
-
-  def PushProfile(self, new_profile_dir):
-    self.adb.Push(new_profile_dir, self.profile_dir)
-
-  @property
-  def profile_dir(self):
-    return '/data/data/%s/app_chrome/' % self.package
-
-
-class ContentShellBackendSettings(AndroidBrowserBackendSettings):
-  def __init__(self, adb, package):
-    super(ContentShellBackendSettings, self).__init__(
-        adb=adb,
-        activity='org.chromium.content_shell_apk.ContentShellActivity',
-        cmdline_file='/data/local/tmp/content-shell-command-line',
-        package=package,
-        pseudo_exec_name='content_shell')
-
-  def GetDevtoolsRemotePort(self):
-    return 'localabstract:content_shell_devtools_remote'
-
-  @property
-  def is_content_shell(self):
-    return True
-
-  @property
-  def profile_dir(self):
-    return '/data/data/%s/app_content_shell/' % self.package
-
-
-class ChromiumTestShellBackendSettings(AndroidBrowserBackendSettings):
-  def __init__(self, adb, package):
-    super(ChromiumTestShellBackendSettings, self).__init__(
-          adb=adb,
-          activity='org.chromium.chrome.testshell.ChromiumTestShellActivity',
-          cmdline_file='/data/local/tmp/chromium-testshell-command-line',
-          package=package,
-          pseudo_exec_name='chromium_testshell')
-
-  def GetDevtoolsRemotePort(self):
-    return 'localabstract:chromium_testshell_devtools_remote'
-
-  @property
-  def is_content_shell(self):
-    return True
-
-  @property
-  def profile_dir(self):
-    return '/data/data/%s/app_chromiumtestshell/' % self.package
-
-
-class WebviewBackendSettings(AndroidBrowserBackendSettings):
-  def __init__(self, adb, package):
-    super(WebviewBackendSettings, self).__init__(
-        adb=adb,
-        activity='com.android.webview.chromium.shell.TelemetryActivity',
-        cmdline_file='/data/local/tmp/webview-command-line',
-        package=package,
-        pseudo_exec_name='webview')
-
-  def GetDevtoolsRemotePort(self):
-    # The DevTools socket name for WebView depends on the activity PID's.
-    retries = 0
-    timeout = 1
-    pid = None
-    while True:
-      pids = self.adb.ExtractPid(self.package)
-      if (len(pids) > 0):
-        pid = pids[-1]
-        break
-      time.sleep(timeout)
-      retries += 1
-      timeout *= 2
-      if retries == 4:
-        logging.critical('android_browser_backend: Timeout while waiting for '
-                         'activity %s:%s to come up',
-                         self.package,
-                         self.activity)
-        raise exceptions.BrowserGoneException('Timeout waiting for PID.')
-    return 'localabstract:webview_devtools_remote_%s' % str(pid)
-
-  @property
-  def profile_dir(self):
-    return '/data/data/%s/app_webview/' % self.package
-
-
-class AndroidBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
-  """The backend for controlling a browser instance running on Android.
-  """
-  def __init__(self, options, backend_settings):
-    super(AndroidBrowserBackend, self).__init__(
-        is_content_shell=backend_settings.is_content_shell,
-        supports_extensions=False, options=options)
-    if len(options.extensions_to_load) > 0:
-      raise browser_backend.ExtensionsNotSupportedException(
-          'Android browser does not support extensions.')
-    # Initialize fields so that an explosion during init doesn't break in Close.
-    self._options = options
-    self._adb = backend_settings.adb
-    self._backend_settings = backend_settings
-    self._saved_cmdline = None
-    if not options.keep_test_server_ports:
-      adb_commands.ResetTestServerPortAllocation()
-    self._port = adb_commands.AllocateTestServerPort()
-
-    # Kill old browser.
-    self._adb.CloseApplication(self._backend_settings.package)
-
-    if self._adb.Adb().CanAccessProtectedFileContents():
-      if not options.dont_override_profile:
-        self._backend_settings.RemoveProfile()
-      if options.profile_dir:
-        self._backend_settings.PushProfile(options.profile_dir)
-
-    # Set up the command line.
-    self._saved_cmdline = ''.join(self._adb.Adb().GetProtectedFileContents(
-        self._backend_settings.cmdline_file) or [])
-    args = [backend_settings.pseudo_exec_name]
-    args.extend(self.GetBrowserStartupArgs())
-    def QuoteIfNeeded(arg):
-      # Escape 'key=valueA valueB' to 'key="valueA valueB"'
-      # Already quoted values, or values without space are left untouched.
-      # This is required so CommandLine.java can parse valueB correctly rather
-      # than as a separate switch.
-      params = arg.split('=')
-      if len(params) != 2:
-        return arg
-      key, values = params
-      if ' ' not in values:
-        return arg
-      if values[0] in '"\'' and values[-1] == values[0]:
-        return arg
-      return '%s="%s"' % (key, values)
-    args = map(QuoteIfNeeded, args)
-    self._adb.Adb().SetProtectedFileContents(
-        self._backend_settings.cmdline_file, ' '.join(args))
-    cmdline = self._adb.Adb().GetProtectedFileContents(
-        self._backend_settings.cmdline_file)
-    if len(cmdline) != 1 or cmdline[0] != ' '.join(args):
-      logging.critical('Failed to set Chrome command line. '
-                       'Fix this by flashing to a userdebug build.')
-      sys.exit(1)
-
-  def Start(self):
-    self._adb.RunShellCommand('logcat -c')
-    self._adb.StartActivity(self._backend_settings.package,
-                            self._backend_settings.activity,
-                            True,
-                            None,
-                            'chrome://newtab/')
-
-    self._adb.Forward('tcp:%d' % self._port,
-                      self._backend_settings.GetDevtoolsRemotePort())
-
-    try:
-      self._WaitForBrowserToComeUp()
-      self._PostBrowserStartupInitialization()
-    except exceptions.BrowserGoneException:
-      logging.critical('Failed to connect to browser.')
-      if not self._adb.Adb().CanAccessProtectedFileContents():
-        logging.critical(
-          'Resolve this by either: '
-          '(1) Flashing to a userdebug build OR '
-          '(2) Manually enabling web debugging in Chrome at '
-          'Settings > Developer tools > Enable USB Web debugging.')
-      sys.exit(1)
-    except:
-      import traceback
-      traceback.print_exc()
-      self.Close()
-      raise
-
-  def GetBrowserStartupArgs(self):
-    args = super(AndroidBrowserBackend, self).GetBrowserStartupArgs()
-    args.append('--enable-remote-debugging')
-    args.append('--no-restore-state')
-    args.append('--disable-fre')
-    return args
-
-  @property
-  def adb(self):
-    return self._adb
-
-  @property
-  def pid(self):
-    return int(self._adb.ExtractPid(self._backend_settings.package)[0])
-
-  @property
-  def browser_directory(self):
-    return None
-
-  @property
-  def profile_directory(self):
-    return self._backend_settings.profile_dir
-
-  def __del__(self):
-    self.Close()
-
-  def Close(self):
-    super(AndroidBrowserBackend, self).Close()
-
-    if self._saved_cmdline:
-      self._adb.Adb().SetProtectedFileContents(
-          self._backend_settings.cmdline_file,
-          self._saved_cmdline)
-    else:
-      self._adb.RunShellCommand('rm %s' % self._backend_settings.cmdline_file)
-    self._adb.CloseApplication(self._backend_settings.package)
-
-  def IsBrowserRunning(self):
-    pids = self._adb.ExtractPid(self._backend_settings.package)
-    return len(pids) != 0
-
-  def GetRemotePort(self, local_port):
-    return local_port
-
-  def GetStandardOutput(self):
-    return '\n'.join(self._adb.RunShellCommand('logcat -d -t 500'))
-
-  def GetStackTrace(self):
-    def Decorate(title, content):
-      return title + '\n' + content + '\n' + '*' * 80 + '\n'
-    # Get the last lines of logcat (large enough to contain stacktrace)
-    logcat = self.GetStandardOutput()
-    ret = Decorate('Logcat', logcat)
-    stack = os.path.join(util.GetChromiumSrcDir(), 'third_party',
-                         'android_platform', 'development', 'scripts', 'stack')
-    # Try to symbolize logcat.
-    if os.path.exists(stack):
-      p = subprocess.Popen([stack], stdin=subprocess.PIPE,
-                           stdout=subprocess.PIPE)
-      ret += Decorate('Stack from Logcat', p.communicate(input=logcat)[0])
-
-    # Try to get tombstones.
-    tombstones = os.path.join(util.GetChromiumSrcDir(), 'build', 'android',
-                              'tombstones.py')
-    if os.path.exists(tombstones):
-      ret += Decorate('Tombstones',
-                      subprocess.Popen([tombstones, '-w', '--device',
-                                        self._adb.device()],
-                                       stdout=subprocess.PIPE).communicate()[0])
-    return ret
-
-  def CreateForwarder(self, *port_pairs):
-    return adb_commands.Forwarder(self._adb, *port_pairs)
diff --git a/tools/telemetry/telemetry/core/chrome/android_browser_finder.py b/tools/telemetry/telemetry/core/chrome/android_browser_finder.py
deleted file mode 100644
index c85d32a..0000000
--- a/tools/telemetry/telemetry/core/chrome/android_browser_finder.py
+++ /dev/null
@@ -1,187 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-"""Finds android browsers that can be controlled by telemetry."""
-
-import os
-import logging as real_logging
-import re
-import subprocess
-import sys
-
-from telemetry.core import browser
-from telemetry.core import possible_browser
-from telemetry.core import profile_types
-from telemetry.core import util
-from telemetry.core.chrome import adb_commands
-from telemetry.core.chrome import android_browser_backend
-from telemetry.core.platform import android_platform_backend
-
-CHROME_PACKAGE_NAMES = {
-  'android-chrome': 'com.google.android.apps.chrome',
-  'android-chrome-beta': 'com.chrome.beta',
-  'android-chrome-dev': 'com.google.android.apps.chrome_dev',
-  'android-jb-system-chrome': 'com.android.chrome'
-}
-
-ALL_BROWSER_TYPES = ','.join([
-                                'android-chromium-testshell',
-                                'android-content-shell',
-                                'android-webview',
-                             ] + CHROME_PACKAGE_NAMES.keys())
-
-CONTENT_SHELL_PACKAGE = 'org.chromium.content_shell_apk'
-CHROMIUM_TESTSHELL_PACKAGE = 'org.chromium.chrome.testshell'
-WEBVIEW_PACKAGE = 'com.android.webview.chromium.shell'
-
-
-# adb shell pm list packages
-# adb
-# intents to run (pass -D url for the rest)
-#   com.android.chrome/.Main
-#   com.google.android.apps.chrome/.Main
-
-class PossibleAndroidBrowser(possible_browser.PossibleBrowser):
-  """A launchable android browser instance."""
-  def __init__(self, browser_type, options, backend_settings):
-    super(PossibleAndroidBrowser, self).__init__(browser_type, options)
-    self._backend_settings = backend_settings
-
-  def __repr__(self):
-    return 'PossibleAndroidBrowser(browser_type=%s)' % self.browser_type
-
-  def Create(self):
-    if profile_types.GetProfileCreator(self.options.profile_type):
-      raise Exception("Profile creation not currently supported on Android")
-
-    backend = android_browser_backend.AndroidBrowserBackend(
-        self._options, self._backend_settings)
-    platform_backend = android_platform_backend.AndroidPlatformBackend(
-        self._backend_settings.adb.Adb(), self._options.no_performance_mode)
-    b = browser.Browser(backend, platform_backend)
-    return b
-
-  def SupportsOptions(self, options):
-    if len(options.extensions_to_load) != 0:
-      return False
-    return True
-
-def SelectDefaultBrowser(_):
-  return None
-
-adb_works = None
-def CanFindAvailableBrowsers(logging=real_logging):
-  if not adb_commands.IsAndroidSupported():
-    return False
-
-  global adb_works
-
-  if adb_works == None:
-    try:
-      with open(os.devnull, 'w') as devnull:
-        proc = subprocess.Popen(['adb', 'devices'],
-                                stdout=subprocess.PIPE,
-                                stderr=subprocess.PIPE,
-                                stdin=devnull)
-        stdout, _ = proc.communicate()
-        if re.search(re.escape('????????????\tno permissions'), stdout) != None:
-          logging.warn(
-              ('adb devices reported a permissions error. Consider '
-               'restarting adb as root:'))
-          logging.warn('  adb kill-server')
-          logging.warn('  sudo `which adb` devices\n\n')
-        adb_works = True
-    except OSError:
-      platform_tools_path = os.path.join(util.GetChromiumSrcDir(),
-          'third_party', 'android_tools', 'sdk', 'platform-tools')
-      if (sys.platform.startswith('linux') and
-          os.path.exists(os.path.join(platform_tools_path, 'adb'))):
-        os.environ['PATH'] = os.pathsep.join([platform_tools_path,
-                                              os.environ['PATH']])
-        adb_works = True
-      else:
-        adb_works = False
-  if adb_works and sys.platform.startswith('linux'):
-    # Workaround for crbug.com/268450
-    import psutil
-    adb_commands.GetAttachedDevices()
-    pids  = [p.pid for p in psutil.process_iter() if 'adb' in p.name]
-    with open(os.devnull, 'w') as devnull:
-      for pid in pids:
-        ret = subprocess.call(['taskset', '-p', '0x1', str(pid)],
-                              stdout=subprocess.PIPE,
-                              stderr=subprocess.PIPE,
-                              stdin=devnull)
-        if ret:
-          logging.warn('Failed to taskset %d (%s)', pid, ret)
-
-  return adb_works
-
-def FindAllAvailableBrowsers(options, logging=real_logging):
-  """Finds all the desktop browsers available on this machine."""
-  if not CanFindAvailableBrowsers(logging=logging):
-    logging.info('No adb command found. ' +
-                 'Will not try searching for Android browsers.')
-    return []
-
-  device = None
-  if options.android_device:
-    devices = [options.android_device]
-  else:
-    devices = adb_commands.GetAttachedDevices()
-
-  if len(devices) == 0:
-    logging.info('No android devices found.')
-    return []
-
-  if len(devices) > 1:
-    logging.warn('Multiple devices attached. ' +
-                 'Please specify a device explicitly.')
-    return []
-
-  device = devices[0]
-
-  adb = adb_commands.AdbCommands(device=device)
-
-  packages = adb.RunShellCommand('pm list packages')
-  possible_browsers = []
-  if 'package:' + CONTENT_SHELL_PACKAGE in packages:
-    b = PossibleAndroidBrowser(
-        'android-content-shell',
-        options, android_browser_backend.ContentShellBackendSettings(
-            adb, CONTENT_SHELL_PACKAGE))
-    possible_browsers.append(b)
-
-  if 'package:' + CHROMIUM_TESTSHELL_PACKAGE in packages:
-    b = PossibleAndroidBrowser(
-        'android-chromium-testshell',
-        options, android_browser_backend.ChromiumTestShellBackendSettings(
-            adb, CHROMIUM_TESTSHELL_PACKAGE))
-    possible_browsers.append(b)
-
-  if 'package:' + WEBVIEW_PACKAGE in packages:
-    b = PossibleAndroidBrowser(
-        'android-webview',
-        options,
-        android_browser_backend.WebviewBackendSettings(adb, WEBVIEW_PACKAGE))
-    possible_browsers.append(b)
-
-  for name, package in CHROME_PACKAGE_NAMES.iteritems():
-    if 'package:' + package in packages:
-      b = PossibleAndroidBrowser(
-          name,
-          options,
-          android_browser_backend.ChromeBackendSettings(adb, package))
-      possible_browsers.append(b)
-
-  # See if the "forwarder" is installed -- we need this to host content locally
-  # but make it accessible to the device.
-  if len(possible_browsers) and not adb_commands.HasForwarder():
-    logging.warn('telemetry detected an android device. However,')
-    logging.warn('Chrome\'s port-forwarder app is not available.')
-    logging.warn('To build:')
-    logging.warn('  ninja -C out/Release forwarder2 md5sum')
-    logging.warn('')
-    logging.warn('')
-    return []
-  return possible_browsers
diff --git a/tools/telemetry/telemetry/core/chrome/android_browser_finder_unittest.py b/tools/telemetry/telemetry/core/chrome/android_browser_finder_unittest.py
deleted file mode 100644
index c1e4fcf..0000000
--- a/tools/telemetry/telemetry/core/chrome/android_browser_finder_unittest.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import unittest
-
-from telemetry.core import browser_options
-from telemetry.core.chrome import android_browser_finder
-from telemetry.unittest import system_stub
-
-class LoggingStub(object):
-  def __init__(self):
-    self.warnings = []
-
-  def info(self, msg, *args):
-    pass
-
-  def warn(self, msg, *args):
-    self.warnings.append(msg % args)
-
-class AndroidBrowserFinderTest(unittest.TestCase):
-  def setUp(self):
-    self._stubs = system_stub.Override(android_browser_finder,
-                                       ['adb_commands', 'subprocess'])
-    android_browser_finder.adb_works = None  # Blow cache between runs.
-
-  def tearDown(self):
-    self._stubs.Restore()
-
-  def test_no_adb(self):
-    options = browser_options.BrowserOptions()
-
-    def NoAdb(*args, **kargs): # pylint: disable=W0613
-      raise OSError('not found')
-    self._stubs.subprocess.Popen = NoAdb
-    browsers = android_browser_finder.FindAllAvailableBrowsers(options)
-    self.assertEquals(0, len(browsers))
-
-  def test_adb_no_devices(self):
-    options = browser_options.BrowserOptions()
-
-    browsers = android_browser_finder.FindAllAvailableBrowsers(options)
-    self.assertEquals(0, len(browsers))
-
-
-  def test_adb_permissions_error(self):
-    options = browser_options.BrowserOptions()
-
-    self._stubs.subprocess.Popen.communicate_result = (
-        """List of devices attached
-????????????\tno permissions""",
-        """* daemon not running. starting it now on port 5037 *
-* daemon started successfully *
-""")
-
-    log_stub = LoggingStub()
-    browsers = android_browser_finder.FindAllAvailableBrowsers(
-      options, log_stub)
-    self.assertEquals(3, len(log_stub.warnings))
-    self.assertEquals(0, len(browsers))
-
-
-  def test_adb_two_devices(self):
-    options = browser_options.BrowserOptions()
-
-    self._stubs.adb_commands.attached_devices = ['015d14fec128220c',
-                                                 '015d14fec128220d']
-
-    log_stub = LoggingStub()
-    browsers = android_browser_finder.FindAllAvailableBrowsers(
-      options, log_stub)
-    self.assertEquals(1, len(log_stub.warnings))
-    self.assertEquals(0, len(browsers))
-
-  def test_adb_one_device(self):
-    options = browser_options.BrowserOptions()
-
-    self._stubs.adb_commands.attached_devices = ['015d14fec128220c']
-
-    def OnPM(args):
-      assert args[0] == 'pm'
-      assert args[1] == 'list'
-      assert args[2] == 'packages'
-      return ['package:org.chromium.content_shell_apk',
-              'package.com.google.android.setupwizard']
-
-    self._stubs.adb_commands.shell_command_handlers['pm'] = OnPM
-
-    browsers = android_browser_finder.FindAllAvailableBrowsers(options)
-    self.assertEquals(1, len(browsers))
diff --git a/tools/telemetry/telemetry/core/chrome/chromeos_login_ext/main.js b/tools/telemetry/telemetry/core/chrome/chromeos_login_ext/main.js
deleted file mode 100644
index 79e3cfc..0000000
--- a/tools/telemetry/telemetry/core/chrome/chromeos_login_ext/main.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-var PARENT_PAGE = 'chrome://oobe/';
-
-var msg = {
-  'method': 'loginUILoaded'
-};
-window.parent.postMessage(msg, PARENT_PAGE);
-
-var msg = {
-  'method': 'completeLogin',
-  'email': 'test@test.test',
-  'password': ''
-};
-window.parent.postMessage(msg, PARENT_PAGE);
diff --git a/tools/telemetry/telemetry/core/chrome/cros_browser_backend.py b/tools/telemetry/telemetry/core/chrome/cros_browser_backend.py
deleted file mode 100644
index 5165420..0000000
--- a/tools/telemetry/telemetry/core/chrome/cros_browser_backend.py
+++ /dev/null
@@ -1,416 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import logging
-import os
-import subprocess
-
-from telemetry.core import exceptions
-from telemetry.core import util
-from telemetry.core.backends import browser_backend
-from telemetry.core.backends.chrome import chrome_browser_backend
-
-class CrOSBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
-  # Some developers' workflow includes running the Chrome process from
-  # /usr/local/... instead of the default location. We have to check for both
-  # paths in order to support this workflow.
-  CHROME_PATHS = ['/opt/google/chrome/chrome ',
-                  '/usr/local/opt/google/chrome/chrome ']
-
-  def __init__(self, browser_type, options, cri, is_guest):
-    super(CrOSBrowserBackend, self).__init__(
-        is_content_shell=False, supports_extensions=not is_guest,
-        options=options)
-    # Initialize fields so that an explosion during init doesn't break in Close.
-    self._browser_type = browser_type
-    self._options = options
-    self._cri = cri
-    self._is_guest = is_guest
-
-    self._remote_debugging_port = self._cri.GetRemotePort()
-    self._port = self._remote_debugging_port
-    self._forwarder = None
-
-    self._SetBranchNumber(self._GetChromeVersion())
-
-    self._login_ext_dir = os.path.join(os.path.dirname(__file__),
-                                       'chromeos_login_ext')
-
-    # Push a dummy login extension to the device.
-    # This extension automatically logs in as test@test.test
-    # Note that we also perform this copy locally to ensure that
-    # the owner of the extensions is set to chronos.
-    logging.info('Copying dummy login extension to the device')
-    cri.PushFile(self._login_ext_dir, '/tmp/')
-    self._login_ext_dir = '/tmp/chromeos_login_ext'
-    cri.RunCmdOnDevice(['chown', '-R', 'chronos:chronos',
-                        self._login_ext_dir])
-
-    # Copy extensions to temp directories on the device.
-    # Note that we also perform this copy locally to ensure that
-    # the owner of the extensions is set to chronos.
-    for e in options.extensions_to_load:
-      output = cri.RunCmdOnDevice(['mktemp', '-d', '/tmp/extension_XXXXX'])
-      extension_dir = output[0].rstrip()
-      cri.PushFile(e.path, extension_dir)
-      cri.RunCmdOnDevice(['chown', '-R', 'chronos:chronos', extension_dir])
-      e.local_path = os.path.join(extension_dir, os.path.basename(e.path))
-
-    # Ensure the UI is running and logged out.
-    self._RestartUI()
-    util.WaitFor(lambda: self.IsBrowserRunning(), 20)  # pylint: disable=W0108
-
-    # Delete test@test.test's cryptohome vault (user data directory).
-    if not options.dont_override_profile:
-      logging.info('Deleting user\'s cryptohome vault (the user data dir)')
-      self._cri.RunCmdOnDevice(
-          ['cryptohome', '--action=remove', '--force', '--user=test@test.test'])
-    if options.profile_dir:
-      profile_dir = '/home/chronos/Default'
-      cri.RunCmdOnDevice(['rm', '-rf', profile_dir])
-      cri.PushFile(options.profile_dir + '/Default', profile_dir)
-      cri.RunCmdOnDevice(['chown', '-R', 'chronos:chronos', profile_dir])
-
-  def GetBrowserStartupArgs(self):
-    self.webpagereplay_remote_http_port = self._cri.GetRemotePort()
-    self.webpagereplay_remote_https_port = self._cri.GetRemotePort()
-
-    args = super(CrOSBrowserBackend, self).GetBrowserStartupArgs()
-
-    args.extend([
-            '--enable-smooth-scrolling',
-            '--enable-threaded-compositing',
-            '--enable-per-tile-painting',
-            '--force-compositing-mode',
-            # Disables the start page, as well as other external apps that can
-            # steal focus or make measurements inconsistent.
-            '--disable-default-apps',
-            # Jump to the login screen, skipping network selection, eula, etc.
-            '--login-screen=login',
-            # Skip user image selection screen, and post login screens.
-            '--oobe-skip-postlogin',
-            # Skip hwid check, for VMs and pre-MP lab devices.
-            '--skip-hwid-check',
-            # Allow devtools to connect to chrome.
-            '--remote-debugging-port=%i' % self._remote_debugging_port,
-            # Open a maximized window.
-            '--start-maximized',
-            # Debug logging for login flake (crbug.com/263527).
-            '--vmodule=*/browser/automation/*=2,*/chromeos/net/*=2,' +
-                '*/chromeos/login/*=2'])
-
-    if not self._is_guest:
-      # This extension bypasses gaia and logs us in.
-      args.append('--auth-ext-path=%s' % self._login_ext_dir)
-
-    return args
-
-  def _GetSessionManagerPid(self, procs):
-    """Returns the pid of the session_manager process, given the list of
-    processes."""
-    for pid, process, _, _ in procs:
-      if process.startswith('/sbin/session_manager '):
-        return pid
-    return None
-
-  def _GetChromeProcess(self):
-    """Locates the the main chrome browser process.
-
-    Chrome on cros is usually in /opt/google/chrome, but could be in
-    /usr/local/ for developer workflows - debug chrome is too large to fit on
-    rootfs.
-
-    Chrome spawns multiple processes for renderers. pids wrap around after they
-    are exhausted so looking for the smallest pid is not always correct. We
-    locate the session_manager's pid, and look for the chrome process that's an
-    immediate child. This is the main browser process.
-    """
-    procs = self._cri.ListProcesses()
-    session_manager_pid = self._GetSessionManagerPid(procs)
-    if not session_manager_pid:
-      return None
-
-    # Find the chrome process that is the child of the session_manager.
-    for pid, process, ppid, _ in procs:
-      if ppid != session_manager_pid:
-        continue
-      for path in self.CHROME_PATHS:
-        if process.startswith(path):
-          return {'pid': pid, 'path': path}
-    return None
-
-  def _GetChromeVersion(self):
-    result = self._GetChromeProcess()
-    assert result and result['path']
-    (version, _) = self._cri.RunCmdOnDevice([result['path'], '--version'])
-    assert version
-    return version
-
-  @property
-  def pid(self):
-    result = self._GetChromeProcess()
-    if result and 'pid' in result:
-      return result['pid']
-    return None
-
-  @property
-  def browser_directory(self):
-    result = self._GetChromeProcess()
-    if result and 'path' in result:
-      return os.path.dirname(result['path'])
-    return None
-
-  @property
-  def profile_directory(self):
-    return '/home/chronos/Default'
-
-  @property
-  def hwid(self):
-    return self._cri.RunCmdOnDevice(['/usr/bin/crossystem', 'hwid'])[0]
-
-  def GetRemotePort(self, _):
-    return self._cri.GetRemotePort()
-
-  def __del__(self):
-    self.Close()
-
-  def Start(self):
-    # Escape all commas in the startup arguments we pass to Chrome
-    # because dbus-send delimits array elements by commas
-    startup_args = [a.replace(',', '\\,') for a in self.GetBrowserStartupArgs()]
-
-    # Restart Chrome with the login extension and remote debugging.
-    logging.info('Restarting Chrome with flags and login')
-    args = ['dbus-send', '--system', '--type=method_call',
-            '--dest=org.chromium.SessionManager',
-            '/org/chromium/SessionManager',
-            'org.chromium.SessionManagerInterface.EnableChromeTesting',
-            'boolean:true',
-            'array:string:"%s"' % ','.join(startup_args)]
-    self._cri.RunCmdOnDevice(args)
-
-    if not self._cri.local:
-      # Find a free local port.
-      self._port = util.GetAvailableLocalPort()
-
-      # Forward the remote debugging port.
-      logging.info('Forwarding remote debugging port')
-      self._forwarder = SSHForwarder(
-        self._cri, 'L',
-        util.PortPair(self._port, self._remote_debugging_port))
-
-    # Wait for the browser to come up.
-    logging.info('Waiting for browser to be ready')
-    try:
-      self._WaitForBrowserToComeUp()
-      self._PostBrowserStartupInitialization()
-    except:
-      import traceback
-      traceback.print_exc()
-      self.Close()
-      raise
-
-    # chrome_branch_number is set in _PostBrowserStartupInitialization.
-    # Without --skip-hwid-check (introduced in crrev.com/203397), devices/VMs
-    # will be stuck on the bad hwid screen.
-    if self.chrome_branch_number <= 1500 and not self.hwid:
-      raise exceptions.LoginException(
-          'Hardware id not set on device/VM. --skip-hwid-check not supported '
-          'with chrome branches 1500 or earlier.')
-
-    if self._is_guest:
-      pid = self.pid
-      self._NavigateGuestLogin()
-      # Guest browsing shuts down the current browser and launches an incognito
-      # browser in a separate process, which we need to wait for.
-      util.WaitFor(lambda: pid != self.pid, 10)
-      self._WaitForBrowserToComeUp()
-    else:
-      self._NavigateLogin()
-
-    logging.info('Browser is up!')
-
-  def Close(self):
-    super(CrOSBrowserBackend, self).Close()
-
-    self._RestartUI() # Logs out.
-
-    if not self._cri.local:
-      if self._forwarder:
-        self._forwarder.Close()
-        self._forwarder = None
-
-    if self._login_ext_dir:
-      self._cri.RmRF(self._login_ext_dir)
-      self._login_ext_dir = None
-
-    for e in self._options.extensions_to_load:
-      self._cri.RmRF(os.path.dirname(e.local_path))
-
-    self._cri = None
-
-  def IsBrowserRunning(self):
-    return bool(self.pid)
-
-  def GetStandardOutput(self):
-    return 'Cannot get standard output on CrOS'
-
-  def GetStackTrace(self):
-    return 'Cannot get stack trace on CrOS'
-
-  def CreateForwarder(self, *port_pairs):
-    assert self._cri
-    return (browser_backend.DoNothingForwarder(*port_pairs) if self._cri.local
-        else SSHForwarder(self._cri, 'R', *port_pairs))
-
-  def _RestartUI(self):
-    if self._cri:
-      logging.info('(Re)starting the ui (logs the user out)')
-      if self._cri.IsServiceRunning('ui'):
-        self._cri.RunCmdOnDevice(['restart', 'ui'])
-      else:
-        self._cri.RunCmdOnDevice(['start', 'ui'])
-
-  @property
-  def oobe(self):
-    return self.misc_web_contents_backend.GetOobe()
-
-  def _SigninUIState(self):
-    """Returns the signin ui state of the oobe. HIDDEN: 0, GAIA_SIGNIN: 1,
-    ACCOUNT_PICKER: 2, WRONG_HWID_WARNING: 3, MANAGED_USER_CREATION_FLOW: 4.
-    These values are in
-    chrome/browser/resources/chromeos/login/display_manager.js
-    """
-    return self.oobe.EvaluateJavaScript('''
-      loginHeader = document.getElementById('login-header-bar')
-      if (loginHeader) {
-        loginHeader.signinUIState_;
-      }
-    ''')
-
-  def _IsCryptohomeMounted(self):
-    """Returns True if a cryptohome vault is mounted at /home/chronos/user."""
-    return self._cri.FilesystemMountedAt('/home/chronos/user').startswith(
-        '/home/.shadow/')
-
-  def _HandleUserImageSelectionScreen(self):
-    """If we're stuck on the user image selection screen, we click the ok
-    button.
-    """
-    oobe = self.oobe
-    if oobe:
-      try:
-        oobe.EvaluateJavaScript("""
-            var ok = document.getElementById("ok-button");
-            if (ok) {
-              ok.click();
-            }
-        """)
-      except (exceptions.TabCrashException):
-        pass
-
-  def _IsLoggedIn(self):
-    """Returns True if we're logged in (cryptohome has mounted), and the oobe
-    has been dismissed."""
-    if self.chrome_branch_number <= 1547:
-      self._HandleUserImageSelectionScreen()
-    return self._IsCryptohomeMounted() and not self.oobe
-
-  def _StartupWindow(self):
-    """Closes the startup window, which is an extension on official builds,
-    and a webpage on chromiumos"""
-    startup_window_ext_id = 'honijodknafkokifofgiaalefdiedpko'
-    return (self.extension_dict_backend[startup_window_ext_id]
-        if startup_window_ext_id in self.extension_dict_backend
-        else self.tab_list_backend.Get(0, None))
-
-  def _WaitForAccountPicker(self):
-    """Waits for the oobe screen to be in the account picker state."""
-    util.WaitFor(lambda: self._SigninUIState() == 2, 20)
-
-  def _ClickBrowseAsGuest(self):
-    """Click the Browse As Guest button on the account picker screen. This will
-    restart the browser, and we could have a tab crash or a browser crash."""
-    try:
-      self.oobe.EvaluateJavaScript("""
-          var guest = document.getElementById("guest-user-button");
-          if (guest) {
-            guest.click();
-          }
-      """)
-    except (exceptions.TabCrashException,
-            exceptions.BrowserConnectionGoneException):
-      pass
-
-  def _WaitForGuestFsMounted(self):
-    """Waits for /home/chronos/user to be mounted as guestfs"""
-    util.WaitFor(lambda: (self._cri.FilesystemMountedAt('/home/chronos/user') ==
-                          'guestfs'), 20)
-
-  def _NavigateGuestLogin(self):
-    """Navigates through oobe login screen as guest"""
-    assert self.oobe
-    self._WaitForAccountPicker()
-    self._ClickBrowseAsGuest()
-    self._WaitForGuestFsMounted()
-
-  def _NavigateLogin(self):
-    """Navigates through oobe login screen"""
-    # Dismiss the user image selection screen.
-    try:
-      util.WaitFor(lambda: self._IsLoggedIn(), 60) # pylint: disable=W0108
-    except util.TimeoutException:
-      self._cri.TakeScreenShot('login-screen')
-      raise exceptions.LoginException(
-          'Timed out going through oobe screen. Make sure the custom auth '
-          'extension passed through --auth-ext-path is valid and belongs '
-          'to user "chronos".')
-
-    if self.chrome_branch_number < 1500:
-      # Wait for the startup window, then close it. Startup window doesn't exist
-      # post-M27. crrev.com/197900
-      util.WaitFor(lambda: self._StartupWindow() is not None, 20)
-      self._StartupWindow().Close()
-    else:
-      # Open a new window/tab.
-      self.tab_list_backend.New(15)
-
-
-class SSHForwarder(object):
-  def __init__(self, cri, forwarding_flag, *port_pairs):
-    self._proc = None
-
-    if forwarding_flag == 'R':
-      self._host_port = port_pairs[0].remote_port
-      command_line = ['-%s%i:localhost:%i' % (forwarding_flag,
-                                              port_pair.remote_port,
-                                              port_pair.local_port)
-                      for port_pair in port_pairs]
-    else:
-      self._host_port = port_pairs[0].local_port
-      command_line = ['-%s%i:localhost:%i' % (forwarding_flag,
-                                              port_pair.local_port,
-                                              port_pair.remote_port)
-                      for port_pair in port_pairs]
-
-    self._device_port = port_pairs[0].remote_port
-
-    self._proc = subprocess.Popen(
-      cri.FormSSHCommandLine(['sleep', '999999999'], command_line),
-      stdout=subprocess.PIPE,
-      stderr=subprocess.PIPE,
-      stdin=subprocess.PIPE,
-      shell=False)
-
-    util.WaitFor(lambda: cri.IsHTTPServerRunningOnPort(self._device_port), 60)
-
-  @property
-  def url(self):
-    assert self._proc
-    return 'http://localhost:%i' % self._host_port
-
-  def Close(self):
-    if self._proc:
-      self._proc.kill()
-      self._proc = None
-
diff --git a/tools/telemetry/telemetry/core/chrome/cros_browser_finder.py b/tools/telemetry/telemetry/core/chrome/cros_browser_finder.py
deleted file mode 100644
index da795fa..0000000
--- a/tools/telemetry/telemetry/core/chrome/cros_browser_finder.py
+++ /dev/null
@@ -1,110 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-"""Finds CrOS browsers that can be controlled by telemetry."""
-
-import logging
-
-from telemetry.core import browser
-from telemetry.core import possible_browser
-from telemetry.core import profile_types
-from telemetry.core.chrome import cros_browser_backend
-from telemetry.core.chrome import cros_interface
-from telemetry.core.platform import cros_platform_backend
-
-ALL_BROWSER_TYPES = ','.join([
-    'cros-chrome',
-    'cros-chrome-guest',
-    'system-guest',
-    ])
-
-class PossibleCrOSBrowser(possible_browser.PossibleBrowser):
-  """A launchable chromeos browser instance."""
-  def __init__(self, browser_type, options, cri, is_guest):
-    super(PossibleCrOSBrowser, self).__init__(browser_type, options)
-    self._cri = cri
-    self._is_guest = is_guest
-
-  def __repr__(self):
-    return 'PossibleCrOSBrowser(browser_type=%s)' % self.browser_type
-
-  def Create(self):
-    if profile_types.GetProfileCreator(self.options.profile_type):
-      raise Exception("Profile creation not currently supported on Chrome OS")
-
-    backend = cros_browser_backend.CrOSBrowserBackend(
-        self.browser_type, self._options, self._cri, self._is_guest)
-    b = browser.Browser(backend,
-                        cros_platform_backend.CrosPlatformBackend(self._cri))
-    return b
-
-  def SupportsOptions(self, options):
-    if (len(options.extensions_to_load) != 0) and self._is_guest:
-      return False
-    return True
-
-def SelectDefaultBrowser(possible_browsers):
-  if cros_interface.IsRunningOnCrosDevice():
-    for b in possible_browsers:
-      if b.browser_type == 'system':
-        return b
-  return None
-
-def CanFindAvailableBrowsers(options):
-  return (cros_interface.IsRunningOnCrosDevice() or
-          options.cros_remote or
-          cros_interface.HasSSH())
-
-def FindAllAvailableBrowsers(options):
-  """Finds all available chromeos browsers, locally and remotely."""
-  if cros_interface.IsRunningOnCrosDevice():
-    return [PossibleCrOSBrowser('system', options,
-                                cros_interface.CrOSInterface(),
-                                is_guest=False),
-            PossibleCrOSBrowser('system-guest', options,
-                                cros_interface.CrOSInterface(),
-                                is_guest=True)]
-
-  if options.cros_remote == None:
-    logging.debug('No --remote specified, will not probe for CrOS.')
-    return []
-
-  if not cros_interface.HasSSH():
-    logging.debug('ssh not found. Cannot talk to CrOS devices.')
-    return []
-  cri = cros_interface.CrOSInterface(options.cros_remote,
-                                     options.cros_ssh_identity)
-
-  # Check ssh
-  try:
-    cri.TryLogin()
-  except cros_interface.LoginException, ex:
-    if isinstance(ex, cros_interface.KeylessLoginRequiredException):
-      logging.warn('Could not ssh into %s. Your device must be configured',
-                      options.cros_remote)
-      logging.warn('to allow passwordless login as root.')
-      logging.warn('For a test-build device, pass this to your script:')
-      logging.warn('   --identity $(CHROMITE)/ssh_keys/testing_rsa')
-      logging.warn('')
-      logging.warn('For a developer-mode device, the steps are:')
-      logging.warn(' - Ensure you have an id_rsa.pub (etc) on this computer')
-      logging.warn(' - On the chromebook:')
-      logging.warn('   -  Control-Alt-T; shell; sudo -s')
-      logging.warn('   -  openssh-server start')
-      logging.warn('   -  scp <this machine>:.ssh/id_rsa.pub /tmp/')
-      logging.warn('   -  mkdir /root/.ssh')
-      logging.warn('   -  chown go-rx /root/.ssh')
-      logging.warn('   -  cat /tmp/id_rsa.pub >> /root/.ssh/authorized_keys')
-      logging.warn('   -  chown 0600 /root/.ssh/authorized_keys')
-      logging.warn('There, that was easy!')
-      logging.warn('')
-      logging.warn('P.S. Please, tell your manager how INANE this is.')
-
-    from telemetry.core import browser_finder
-    raise browser_finder.BrowserFinderException(str(ex))
-
-  if not cri.FileExistsOnDevice('/opt/google/chrome/chrome'):
-    logging.warn('Could not find a chrome on ' % cri.hostname)
-
-  return [PossibleCrOSBrowser('cros-chrome', options, cri, is_guest=False),
-          PossibleCrOSBrowser('cros-chrome-guest', options, cri, is_guest=True)]
diff --git a/tools/telemetry/telemetry/core/chrome/cros_browser_finder_unittest.py b/tools/telemetry/telemetry/core/chrome/cros_browser_finder_unittest.py
deleted file mode 100644
index 07f2e03..0000000
--- a/tools/telemetry/telemetry/core/chrome/cros_browser_finder_unittest.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# TODO(nduca): Add basic unit test for cros_browser_finder.
-#
-# Here, we should mock the cros_interface module (assuming its working) and
-# verify that the finder does the right thing. Because the finder delegates most
-# of its work to the CRI, the test code here is going to be comparatively
-# simple.
-
diff --git a/tools/telemetry/telemetry/core/chrome/cros_interface.py b/tools/telemetry/telemetry/core/chrome/cros_interface.py
deleted file mode 100644
index 6f1f160..0000000
--- a/tools/telemetry/telemetry/core/chrome/cros_interface.py
+++ /dev/null
@@ -1,345 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-"""A wrapper around ssh for common operations on a CrOS-based device"""
-import logging
-import os
-import re
-import subprocess
-import sys
-import tempfile
-
-# TODO(nduca): This whole file is built up around making individual ssh calls
-# for each operation. It really could get away with a single ssh session built
-# around pexpect, I suspect, if we wanted it to be faster. But, this was
-# convenient.
-
-def IsRunningOnCrosDevice():
-  """Returns True if we're on a ChromeOS device."""
-  lsb_release = '/etc/lsb-release'
-  if sys.platform.startswith('linux') and os.path.exists(lsb_release):
-    with open(lsb_release, 'r') as f:
-      res = f.read()
-      if res.count('CHROMEOS_RELEASE_NAME'):
-        return True
-  return False
-
-def RunCmd(args, cwd=None, quiet=False):
-  """Opens a subprocess to execute a program and returns its return value.
-
-  Args:
-    args: A string or a sequence of program arguments. The program to execute is
-      the string or the first item in the args sequence.
-    cwd: If not None, the subprocess's current directory will be changed to
-      |cwd| before it's executed.
-
-  Returns:
-    Return code from the command execution.
-  """
-  if not quiet:
-    logging.debug(' '.join(args) + ' ' + (cwd or ''))
-  with open(os.devnull, 'w') as devnull:
-    p = subprocess.Popen(args=args, cwd=cwd, stdout=devnull,
-                         stderr=devnull, stdin=devnull, shell=False)
-    return p.wait()
-
-def GetAllCmdOutput(args, cwd=None, quiet=False):
-  """Open a subprocess to execute a program and returns its output.
-
-  Args:
-    args: A string or a sequence of program arguments. The program to execute is
-      the string or the first item in the args sequence.
-    cwd: If not None, the subprocess's current directory will be changed to
-      |cwd| before it's executed.
-
-  Returns:
-    Captures and returns the command's stdout.
-    Prints the command's stderr to logger (which defaults to stdout).
-  """
-  if not quiet:
-    logging.debug(' '.join(args) + ' ' + (cwd or ''))
-  with open(os.devnull, 'w') as devnull:
-    p = subprocess.Popen(args=args, cwd=cwd, stdout=subprocess.PIPE,
-                         stderr=subprocess.PIPE, stdin=devnull)
-    stdout, stderr = p.communicate()
-    if not quiet:
-      logging.debug(' > stdout=[%s], stderr=[%s]', stdout, stderr)
-    return stdout, stderr
-
-def HasSSH():
-  try:
-    RunCmd(['ssh'], quiet=True)
-    RunCmd(['scp'], quiet=True)
-    logging.debug("HasSSH()->True")
-    return True
-  except OSError:
-    logging.debug("HasSSH()->False")
-    return False
-
-class LoginException(Exception):
-  pass
-
-class KeylessLoginRequiredException(LoginException):
-  pass
-
-class CrOSInterface(object):
-  # pylint: disable=R0923
-  def __init__(self, hostname = None, ssh_identity = None):
-    self._hostname = hostname
-    # List of ports generated from GetRemotePort() that may not be in use yet.
-    self._reserved_ports = []
-
-    if self.local:
-      return
-
-    self._ssh_identity = None
-    self._hostfile = tempfile.NamedTemporaryFile()
-    self._hostfile.flush()
-    self._ssh_args = ['-o ConnectTimeout=5',
-                      '-o StrictHostKeyChecking=no',
-                      '-o KbdInteractiveAuthentication=no',
-                      '-o PreferredAuthentications=publickey',
-                      '-o UserKnownHostsFile=%s' % self._hostfile.name]
-
-    if ssh_identity:
-      self._ssh_identity = os.path.abspath(os.path.expanduser(ssh_identity))
-
-  @property
-  def local(self):
-    return not self._hostname
-
-  @property
-  def hostname(self):
-    return self._hostname
-
-  def FormSSHCommandLine(self, args, extra_ssh_args=None):
-    if self.local:
-      # We run the command through the shell locally for consistency with
-      # how commands are run through SSH (crbug.com/239161). This work
-      # around will be unnecessary once we implement a persistent SSH
-      # connection to run remote commands (crbug.com/239607).
-      return ['sh', '-c', " ".join(args)]
-
-    full_args = ['ssh',
-                 '-o ForwardX11=no',
-                 '-o ForwardX11Trusted=no',
-                 '-n'] + self._ssh_args
-    if self._ssh_identity is not None:
-      full_args.extend(['-i', self._ssh_identity])
-    if extra_ssh_args:
-      full_args.extend(extra_ssh_args)
-    full_args.append('root@%s' % self._hostname)
-    full_args.extend(args)
-    return full_args
-
-  def _RemoveSSHWarnings(self, toClean):
-    """Removes specific ssh warning lines from a string.
-
-    Args:
-      toClean: A string that may be containing multiple lines.
-
-    Returns:
-      A copy of toClean with all the Warning lines removed.
-    """
-    # Remove the Warning about connecting to a new host for the first time.
-    return re.sub('Warning: Permanently added [^\n]* to the list of known '
-                  'hosts.\s\n', '', toClean)
-
-  def RunCmdOnDevice(self, args, cwd=None, quiet=False):
-    stdout, stderr = GetAllCmdOutput(
-        self.FormSSHCommandLine(args), cwd, quiet=quiet)
-    # The initial login will add the host to the hosts file but will also print
-    # a warning to stderr that we need to remove.
-    stderr = self._RemoveSSHWarnings(stderr)
-    return stdout, stderr
-
-  def TryLogin(self):
-    logging.debug('TryLogin()')
-    assert not self.local
-    stdout, stderr = self.RunCmdOnDevice(['echo', '$USER'], quiet=True)
-    if stderr != '':
-      if 'Host key verification failed' in stderr:
-        raise LoginException(('%s host key verification failed. ' +
-                             'SSH to it manually to fix connectivity.') %
-            self._hostname)
-      if 'Operation timed out' in stderr:
-        raise LoginException('Timed out while logging into %s' % self._hostname)
-      if 'UNPROTECTED PRIVATE KEY FILE!' in stderr:
-        raise LoginException('Permissions for %s are too open. To fix this,\n'
-                             'chmod 600 %s' % (self._ssh_identity,
-                                               self._ssh_identity))
-      if 'Permission denied (publickey,keyboard-interactive)' in stderr:
-        raise KeylessLoginRequiredException(
-          'Need to set up ssh auth for %s' % self._hostname)
-      raise LoginException('While logging into %s, got %s' % (
-          self._hostname, stderr))
-    if stdout != 'root\n':
-      raise LoginException(
-        'Logged into %s, expected $USER=root, but got %s.' % (
-          self._hostname, stdout))
-
-  def FileExistsOnDevice(self, file_name):
-    if self.local:
-      return os.path.exists(file_name)
-
-    stdout, stderr = self.RunCmdOnDevice([
-        'if', 'test', '-e', file_name, ';',
-        'then', 'echo', '1', ';',
-        'fi'
-        ], quiet=True)
-    if stderr != '':
-      if "Connection timed out" in stderr:
-        raise OSError('Machine wasn\'t responding to ssh: %s' %
-                      stderr)
-      raise OSError('Unepected error: %s' % stderr)
-    exists = stdout == '1\n'
-    logging.debug("FileExistsOnDevice(<text>, %s)->%s" % (file_name, exists))
-    return exists
-
-  def PushFile(self, filename, remote_filename):
-    if self.local:
-      args = ['cp', '-r', filename, remote_filename]
-      stdout, stderr = GetAllCmdOutput(args, quiet=True)
-      if stderr != '':
-        raise OSError('No such file or directory %s' % stderr)
-      return
-
-    args = ['scp', '-r' ] + self._ssh_args
-    if self._ssh_identity:
-      args.extend(['-i', self._ssh_identity])
-
-    args.extend([os.path.abspath(filename),
-                 'root@%s:%s' % (self._hostname, remote_filename)])
-
-    stdout, stderr = GetAllCmdOutput(args, quiet=True)
-    stderr = self._RemoveSSHWarnings(stderr)
-    if stderr != '':
-      raise OSError('No such file or directory %s' % stderr)
-
-  def PushContents(self, text, remote_filename):
-    logging.debug("PushContents(<text>, %s)" % remote_filename)
-    with tempfile.NamedTemporaryFile() as f:
-      f.write(text)
-      f.flush()
-      self.PushFile(f.name, remote_filename)
-
-  def GetFileContents(self, filename):
-    assert not self.local
-    with tempfile.NamedTemporaryFile() as f:
-      args = ['scp'] + self._ssh_args
-      if self._ssh_identity:
-        args.extend(['-i', self._ssh_identity])
-
-      args.extend(['root@%s:%s' % (self._hostname, filename),
-                   os.path.abspath(f.name)])
-
-      stdout, stderr = GetAllCmdOutput(args, quiet=True)
-      stderr = self._RemoveSSHWarnings(stderr)
-
-      if stderr != '':
-        raise OSError('No such file or directory %s' % stderr)
-
-      with open(f.name, 'r') as f2:
-        res = f2.read()
-        logging.debug("GetFileContents(%s)->%s" % (filename, res))
-        return res
-
-  def ListProcesses(self):
-    """Returns (pid, cmd, ppid, state) of all processes on the device."""
-    stdout, stderr = self.RunCmdOnDevice([
-        '/bin/ps', '--no-headers',
-        '-A',
-        '-o', 'pid,ppid,args,state'], quiet=True)
-    assert stderr == '', stderr
-    procs = []
-    for l in stdout.split('\n'): # pylint: disable=E1103
-      if l == '':
-        continue
-      m = re.match('^\s*(\d+)\s+(\d+)\s+(.+)\s+(.+)', l, re.DOTALL)
-      assert m
-      procs.append((int(m.group(1)), m.group(3), int(m.group(2)), m.group(4)))
-    logging.debug("ListProcesses(<predicate>)->[%i processes]" % len(procs))
-    return procs
-
-  def RmRF(self, filename):
-    logging.debug("rm -rf %s" % filename)
-    self.RunCmdOnDevice(['rm', '-rf', filename], quiet=True)
-
-  def KillAllMatching(self, predicate):
-    kills = ['kill', '-KILL']
-    for pid, cmd, _, _ in self.ListProcesses():
-      if predicate(cmd):
-        logging.info('Killing %s, pid %d' % cmd, pid)
-        kills.append(pid)
-    logging.debug("KillAllMatching(<predicate>)->%i" % (len(kills) - 2))
-    if len(kills) > 2:
-      self.RunCmdOnDevice(kills, quiet=True)
-    return len(kills) - 2
-
-  def IsServiceRunning(self, service_name):
-    stdout, stderr = self.RunCmdOnDevice([
-        'status', service_name], quiet=True)
-    assert stderr == '', stderr
-    running = 'running, process' in stdout
-    logging.debug("IsServiceRunning(%s)->%s" % (service_name, running))
-    return running
-
-  def GetRemotePort(self):
-    netstat = self.RunCmdOnDevice(['netstat', '-ant'])
-    netstat = netstat[0].split('\n')
-    ports_in_use = []
-
-    for line in netstat[2:]:
-      if not line:
-        continue
-      address_in_use = line.split()[3]
-      port_in_use = address_in_use.split(':')[-1]
-      ports_in_use.append(int(port_in_use))
-
-    ports_in_use.extend(self._reserved_ports)
-
-    new_port = sorted(ports_in_use)[-1] + 1
-    self._reserved_ports.append(new_port)
-
-    return new_port
-
-  def IsHTTPServerRunningOnPort(self, port):
-    wget_output = self.RunCmdOnDevice(
-        ['wget', 'localhost:%i' % (port), '-T1', '-t1'])
-
-    if 'Connection refused' in wget_output[1]:
-      return False
-
-    return True
-
-  def FilesystemMountedAt(self, path):
-    """Returns the filesystem mounted at |path|"""
-    df_out, _ = self.RunCmdOnDevice(['/bin/df', path])
-    df_ary = df_out.split('\n')
-    # 3 lines for title, mount info, and empty line.
-    if len(df_ary) == 3:
-      line_ary = df_ary[1].split()
-      if line_ary:
-        return line_ary[0]
-    return None
-
-  def TakeScreenShot(self, screenshot_prefix):
-    """Takes a screenshot, useful for debugging failures."""
-    # TODO(achuith): Find a better location for screenshots. Cros autotests
-    # upload everything in /var/log so use /var/log/screenshots for now.
-    SCREENSHOT_DIR = '/var/log/screenshots/'
-    SCREENSHOT_EXT = '.png'
-
-    self.RunCmdOnDevice(['mkdir', '-p', SCREENSHOT_DIR])
-    for i in xrange(25):
-      screenshot_file = ('%s%s-%d%s' %
-                         (SCREENSHOT_DIR, screenshot_prefix, i, SCREENSHOT_EXT))
-      if not self.FileExistsOnDevice(screenshot_file):
-        self.RunCmdOnDevice([
-            'DISPLAY=:0.0 XAUTHORITY=/home/chronos/.Xauthority '
-            '/usr/local/bin/import',
-            '-window root',
-            '-depth 8',
-            screenshot_file])
-        return
-    logging.warning('screenshot directory full.')
diff --git a/tools/telemetry/telemetry/core/chrome/cros_interface_unittest.py b/tools/telemetry/telemetry/core/chrome/cros_interface_unittest.py
deleted file mode 100644
index 994b051..0000000
--- a/tools/telemetry/telemetry/core/chrome/cros_interface_unittest.py
+++ /dev/null
@@ -1,158 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# TODO(nduca): Rewrite what some of these tests to use mocks instead of
-# actually talking to the device. This would improve our coverage quite
-# a bit.
-import unittest
-import socket
-import sys
-
-from telemetry.core import util
-from telemetry.core.chrome import cros_browser_backend
-from telemetry.core.chrome import cros_interface
-from telemetry.unittest import options_for_unittests
-from telemetry.unittest import RequiresBrowserOfType
-
-class CrOSInterfaceTest(unittest.TestCase):
-  @RequiresBrowserOfType('cros-chrome')
-  def testPushContents(self):
-    remote = options_for_unittests.GetCopy().cros_remote
-    cri = cros_interface.CrOSInterface(
-      remote,
-      options_for_unittests.GetCopy().cros_ssh_identity)
-    cri.RunCmdOnDevice(['rm', '-rf', '/tmp/testPushContents'])
-    cri.PushContents('hello world', '/tmp/testPushContents')
-    contents = cri.GetFileContents('/tmp/testPushContents')
-    self.assertEquals(contents, 'hello world')
-
-  @RequiresBrowserOfType('cros-chrome')
-  def testExists(self):
-    remote = options_for_unittests.GetCopy().cros_remote
-    cri = cros_interface.CrOSInterface(
-      remote,
-      options_for_unittests.GetCopy().cros_ssh_identity)
-    self.assertTrue(cri.FileExistsOnDevice('/proc/cpuinfo'))
-    self.assertTrue(cri.FileExistsOnDevice('/etc/passwd'))
-    self.assertFalse(cri.FileExistsOnDevice('/etc/sdlfsdjflskfjsflj'))
-
-  def testExistsLocal(self):
-    if not sys.platform.startswith('linux'):
-      return
-
-    cri = cros_interface.CrOSInterface()
-    self.assertTrue(cri.FileExistsOnDevice('/proc/cpuinfo'))
-    self.assertTrue(cri.FileExistsOnDevice('/etc/passwd'))
-    self.assertFalse(cri.FileExistsOnDevice('/etc/sdlfsdjflskfjsflj'))
-
-  @RequiresBrowserOfType('cros-chrome')
-  def testGetFileContents(self): # pylint: disable=R0201
-    remote = options_for_unittests.GetCopy().cros_remote
-    cri = cros_interface.CrOSInterface(
-      remote,
-      options_for_unittests.GetCopy().cros_ssh_identity)
-    hosts = cri.GetFileContents('/etc/hosts')
-    assert hosts.startswith('# /etc/hosts')
-
-  @RequiresBrowserOfType('cros-chrome')
-  def testGetFileContentsForSomethingThatDoesntExist(self):
-    remote = options_for_unittests.GetCopy().cros_remote
-    cri = cros_interface.CrOSInterface(
-      remote,
-      options_for_unittests.GetCopy().cros_ssh_identity)
-    self.assertRaises(
-      OSError,
-      lambda: cri.GetFileContents('/tmp/209fuslfskjf/dfsfsf'))
-
-  @RequiresBrowserOfType('cros-chrome')
-  def testIsServiceRunning(self):
-    remote = options_for_unittests.GetCopy().cros_remote
-    cri = cros_interface.CrOSInterface(
-      remote,
-      options_for_unittests.GetCopy().cros_ssh_identity)
-
-    self.assertTrue(cri.IsServiceRunning('openssh-server'))
-
-  def testIsServiceRunningLocal(self):
-    if not sys.platform.startswith('linux'):
-      return
-    cri = cros_interface.CrOSInterface()
-    self.assertTrue(cri.IsServiceRunning('dbus'))
-
-  @RequiresBrowserOfType('cros-chrome')
-  def testGetRemotePortAndIsHTTPServerRunningOnPort(self):
-    remote = options_for_unittests.GetCopy().cros_remote
-    cri = cros_interface.CrOSInterface(
-      remote,
-      options_for_unittests.GetCopy().cros_ssh_identity)
-
-    # Create local server.
-    sock = socket.socket()
-    sock.bind(('', 0))
-    port = sock.getsockname()[1]
-    sock.listen(0)
-
-    # Get remote port and ensure that it was unused.
-    remote_port = cri.GetRemotePort()
-    self.assertFalse(cri.IsHTTPServerRunningOnPort(remote_port))
-
-    # Forward local server's port to remote device's remote_port.
-    forwarder = cros_browser_backend.SSHForwarder(
-        cri, 'R', util.PortPair(port, remote_port))
-
-    # At this point, remote device should be able to connect to local server.
-    self.assertTrue(cri.IsHTTPServerRunningOnPort(remote_port))
-
-    # Next remote port shouldn't be the same as remote_port, since remote_port
-    # is now in use.
-    self.assertTrue(cri.GetRemotePort() != remote_port)
-
-    # Close forwarder and local server ports.
-    forwarder.Close()
-    sock.close()
-
-    # Device should no longer be able to connect to remote_port since it is no
-    # longer in use.
-    self.assertFalse(cri.IsHTTPServerRunningOnPort(remote_port))
-
-  @RequiresBrowserOfType('cros-chrome')
-  def testGetRemotePortReservedPorts(self):
-    remote = options_for_unittests.GetCopy().cros_remote
-    cri = cros_interface.CrOSInterface(
-      remote,
-      options_for_unittests.GetCopy().cros_ssh_identity)
-
-    # Should return 2 separate ports even though the first one isn't technically
-    # being used yet.
-    remote_port_1 = cri.GetRemotePort()
-    remote_port_2 = cri.GetRemotePort()
-
-    self.assertTrue(remote_port_1 != remote_port_2)
-
-  # TODO(tengs): It would be best if we can filter this test and other tests
-  # that need to be run locally based on the platform of the system browser.
-  def testEscapeCmdArguments(self):
-    ''' Commands and their arguments that are executed through the cros
-    interface should follow bash syntax. This test needs to run on remotely
-    and locally on the device to check for consistency.
-    '''
-    if not sys.platform.startswith('linux'):
-      return
-
-    cri = cros_interface.CrOSInterface(
-      options_for_unittests.GetCopy().cros_remote,
-      options_for_unittests.GetCopy().cros_ssh_identity)
-
-    # Check arguments with no special characters
-    stdout, _ = cri.RunCmdOnDevice(['echo', '--arg1=value1', '--arg2=value2',
-        '--arg3="value3"'])
-    assert(stdout.strip() == '--arg1=value1 --arg2=value2 --arg3=value3')
-
-    # Check argument with special characters escaped
-    stdout, _ = cri.RunCmdOnDevice(['echo', '--arg=A\\; echo \\"B\\"'])
-    assert(stdout.strip() == '--arg=A; echo "B"')
-
-    # Check argument with special characters in quotes
-    stdout, _ = cri.RunCmdOnDevice(['echo', "--arg='$HOME;;$PATH'"])
-    assert(stdout.strip() == "--arg=$HOME;;$PATH")
diff --git a/tools/telemetry/telemetry/core/chrome/crx_id.py b/tools/telemetry/telemetry/core/chrome/crx_id.py
deleted file mode 100644
index db56353..0000000
--- a/tools/telemetry/telemetry/core/chrome/crx_id.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-from __future__ import absolute_import
-
-from telemetry.core import util
-
-util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'tools')
-from crx_id import crx_id  # pylint: disable=F0401
-
-
-GetCRXAppID = crx_id.GetCRXAppID
-HasPublicKey = crx_id.HasPublicKey
diff --git a/tools/telemetry/telemetry/core/chrome/desktop_browser_backend.py b/tools/telemetry/telemetry/core/chrome/desktop_browser_backend.py
deleted file mode 100644
index eccdb5a..0000000
--- a/tools/telemetry/telemetry/core/chrome/desktop_browser_backend.py
+++ /dev/null
@@ -1,234 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import glob
-import heapq
-import logging
-import os
-import subprocess as subprocess
-import shutil
-import sys
-import tempfile
-import time
-
-from telemetry.core import util
-from telemetry.core.backends import browser_backend
-from telemetry.core.backends.chrome import chrome_browser_backend
-
-class DesktopBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
-  """The backend for controlling a locally-executed browser instance, on Linux,
-  Mac or Windows.
-  """
-  def __init__(self, options, executable, flash_path, is_content_shell,
-               browser_directory, delete_profile_dir_after_run=True):
-    super(DesktopBrowserBackend, self).__init__(
-        is_content_shell=is_content_shell,
-        supports_extensions=not is_content_shell,
-        options=options)
-
-    # Initialize fields so that an explosion during init doesn't break in Close.
-    self._proc = None
-    self._tmp_profile_dir = None
-    self._tmp_output_file = None
-
-    self._executable = executable
-    if not self._executable:
-      raise Exception('Cannot create browser, no executable found!')
-
-    self._flash_path = flash_path
-    if self._flash_path and not os.path.exists(self._flash_path):
-      logging.warning(('Could not find flash at %s. Running without flash.\n\n'
-                       'To fix this see http://go/read-src-internal') %
-                      self._flash_path)
-      self._flash_path = None
-
-    if len(options.extensions_to_load) > 0 and is_content_shell:
-      raise browser_backend.ExtensionsNotSupportedException(
-          'Content shell does not support extensions.')
-
-    self._browser_directory = browser_directory
-    self._port = util.GetAvailableLocalPort()
-    self._profile_dir = None
-    self._supports_net_benchmarking = True
-    self._delete_profile_dir_after_run = delete_profile_dir_after_run
-    self._tmp_minidump_dir = tempfile.mkdtemp()
-
-    self._SetupProfile()
-
-  def _SetupProfile(self):
-    if not self.options.dont_override_profile:
-      self._tmp_profile_dir = tempfile.mkdtemp()
-      profile_dir = self._profile_dir or self.options.profile_dir
-      if profile_dir:
-        if self.is_content_shell:
-          logging.critical('Profiles cannot be used with content shell')
-          sys.exit(1)
-        shutil.rmtree(self._tmp_profile_dir)
-        shutil.copytree(profile_dir, self._tmp_profile_dir)
-
-  def _LaunchBrowser(self):
-    args = [self._executable]
-    args.extend(self.GetBrowserStartupArgs())
-    env = os.environ.copy()
-    env['CHROME_HEADLESS'] = '1'  # Don't upload minidumps.
-    env['BREAKPAD_DUMP_LOCATION'] = self._tmp_minidump_dir
-    if not self.options.show_stdout:
-      self._tmp_output_file = tempfile.NamedTemporaryFile('w', 0)
-      self._proc = subprocess.Popen(
-          args, stdout=self._tmp_output_file, stderr=subprocess.STDOUT, env=env)
-    else:
-      self._proc = subprocess.Popen(args, env=env)
-
-    try:
-      self._WaitForBrowserToComeUp()
-      self._PostBrowserStartupInitialization()
-    except:
-      self.Close()
-      raise
-
-  def GetBrowserStartupArgs(self):
-    args = super(DesktopBrowserBackend, self).GetBrowserStartupArgs()
-    args.append('--remote-debugging-port=%i' % self._port)
-    args.append('--enable-crash-reporter-for-testing')
-    if not self.is_content_shell:
-      args.append('--window-size=1280,1024')
-      if self._flash_path:
-        args.append('--ppapi-flash-path=%s' % self._flash_path)
-      if self._supports_net_benchmarking:
-        args.append('--enable-net-benchmarking')
-      else:
-        args.append('--enable-benchmarking')
-      if not self.options.dont_override_profile:
-        args.append('--user-data-dir=%s' % self._tmp_profile_dir)
-    return args
-
-  def SetProfileDirectory(self, profile_dir):
-    # Make sure _profile_dir hasn't already been set.
-    assert self._profile_dir is None
-
-    if self.is_content_shell:
-      logging.critical('Profile creation cannot be used with content shell')
-      sys.exit(1)
-
-    self._profile_dir = profile_dir
-
-  def Start(self):
-    self._LaunchBrowser()
-
-    # For old chrome versions, might have to relaunch to have the
-    # correct net_benchmarking switch.
-    if self.chrome_branch_number < 1418:
-      self.Close()
-      self._supports_net_benchmarking = False
-      self._LaunchBrowser()
-
-  @property
-  def pid(self):
-    if self._proc:
-      return self._proc.pid
-    return None
-
-  @property
-  def browser_directory(self):
-    return self._browser_directory
-
-  @property
-  def profile_directory(self):
-    return self._tmp_profile_dir
-
-  def IsBrowserRunning(self):
-    return self._proc.poll() == None
-
-  def GetStandardOutput(self):
-    assert self._tmp_output_file, "Can't get standard output with show_stdout"
-    self._tmp_output_file.flush()
-    try:
-      with open(self._tmp_output_file.name) as f:
-        return f.read()
-    except IOError:
-      return ''
-
-  def GetStackTrace(self):
-    stackwalk = util.FindSupportBinary('minidump_stackwalk')
-    if not stackwalk:
-      logging.warning('minidump_stackwalk binary not found. Must build it to '
-                      'symbolize crash dumps. Returning browser stdout.')
-      return self.GetStandardOutput()
-
-    dumps = glob.glob(os.path.join(self._tmp_minidump_dir, '*.dmp'))
-    if not dumps:
-      logging.warning('No crash dump found. Returning browser stdout.')
-      return self.GetStandardOutput()
-    most_recent_dump = heapq.nlargest(1, dumps, os.path.getmtime)[0]
-    if os.path.getmtime(most_recent_dump) < (time.time() - (5 * 60)):
-      logging.warn('Crash dump is older than 5 minutes. May not be correct.')
-
-    minidump = most_recent_dump + '.stripped'
-    with open(most_recent_dump, 'rb') as infile:
-      with open(minidump, 'wb') as outfile:
-        outfile.write(''.join(infile.read().partition('MDMP')[1:]))
-
-    symbols = glob.glob(os.path.join(os.path.dirname(stackwalk), '*.breakpad*'))
-    if not symbols:
-      logging.warning('No breakpad symbols found. Returning browser stdout.')
-      return self.GetStandardOutput()
-
-    symbols_path = os.path.join(self._tmp_minidump_dir, 'symbols')
-    for symbol in symbols:
-      with open(symbol, 'r') as f:
-        fields = f.readline().split()
-        if not fields:
-          continue
-        sha = fields[3]
-        binary = ' '.join(fields[4:])
-      symbol_path = os.path.join(symbols_path, binary, sha)
-      os.makedirs(symbol_path)
-      shutil.copyfile(symbol, os.path.join(symbol_path, binary + '.sym'))
-
-    error = tempfile.NamedTemporaryFile('w', 0)
-    return subprocess.Popen(
-        [stackwalk, minidump, symbols_path],
-        stdout=subprocess.PIPE, stderr=error).communicate()[0]
-
-  def __del__(self):
-    self.Close()
-
-  def Close(self):
-    super(DesktopBrowserBackend, self).Close()
-
-    if self._proc:
-
-      def IsClosed():
-        if not self._proc:
-          return True
-        return self._proc.poll() != None
-
-      # Try to politely shutdown, first.
-      self._proc.terminate()
-      try:
-        util.WaitFor(IsClosed, timeout=1)
-        self._proc = None
-      except util.TimeoutException:
-        pass
-
-      # Kill it.
-      if not IsClosed():
-        self._proc.kill()
-        try:
-          util.WaitFor(IsClosed, timeout=5)
-          self._proc = None
-        except util.TimeoutException:
-          self._proc = None
-          raise Exception('Could not shutdown the browser.')
-
-    if self._delete_profile_dir_after_run and \
-        self._tmp_profile_dir and os.path.exists(self._tmp_profile_dir):
-      shutil.rmtree(self._tmp_profile_dir, ignore_errors=True)
-      self._tmp_profile_dir = None
-
-    if self._tmp_output_file:
-      self._tmp_output_file.close()
-      self._tmp_output_file = None
-
-  def CreateForwarder(self, *port_pairs):
-    return browser_backend.DoNothingForwarder(*port_pairs)
diff --git a/tools/telemetry/telemetry/core/chrome/desktop_browser_finder.py b/tools/telemetry/telemetry/core/chrome/desktop_browser_finder.py
deleted file mode 100644
index 93f33d8..0000000
--- a/tools/telemetry/telemetry/core/chrome/desktop_browser_finder.py
+++ /dev/null
@@ -1,254 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-"""Finds desktop browsers that can be controlled by telemetry."""
-
-import logging
-from operator import attrgetter
-import os
-import platform
-import subprocess
-import sys
-
-from telemetry.core import browser
-from telemetry.core import platform as core_platform
-from telemetry.core import possible_browser
-from telemetry.core import profile_types
-from telemetry.core import util
-from telemetry.core.chrome import cros_interface
-from telemetry.core.chrome import desktop_browser_backend
-
-ALL_BROWSER_TYPES = ','.join([
-    'exact',
-    'release',
-    'release_x64',
-    'debug',
-    'debug_x64',
-    'canary',
-    'content-shell-debug',
-    'content-shell-release',
-    'system'])
-
-class PossibleDesktopBrowser(possible_browser.PossibleBrowser):
-  """A desktop browser that can be controlled."""
-
-  def __init__(self, browser_type, options, executable, flash_path,
-               is_content_shell, browser_directory, is_local_build=False):
-    super(PossibleDesktopBrowser, self).__init__(browser_type, options)
-    self._local_executable = executable
-    self._flash_path = flash_path
-    self._is_content_shell = is_content_shell
-    self._browser_directory = browser_directory
-    self.is_local_build = is_local_build
-
-  def __repr__(self):
-    return 'PossibleDesktopBrowser(browser_type=%s)' % self.browser_type
-
-  # Constructs a browser.
-  # Returns a touple of the form: (browser, backend)
-  def _CreateBrowserInternal(self, delete_profile_dir_after_run):
-    backend = desktop_browser_backend.DesktopBrowserBackend(
-        self._options, self._local_executable, self._flash_path,
-        self._is_content_shell, self._browser_directory,
-        delete_profile_dir_after_run=delete_profile_dir_after_run)
-    b = browser.Browser(backend,
-                        core_platform.CreatePlatformBackendForCurrentOS())
-    return b
-
-  def Create(self):
-    # If a dirty profile is needed, instantiate an initial browser object and
-    # use that to create a dirty profile.
-    creator_class = profile_types.GetProfileCreator(self.options.profile_type)
-    if creator_class:
-      logging.info(
-          'Creating a dirty profile of type: %s', self.options.profile_type)
-      (b, backend) = \
-          self._CreateBrowserInternal(delete_profile_dir_after_run=False)
-      with b as b:
-        creator = creator_class(b)
-        creator.CreateProfile()
-        dirty_profile_dir = backend.profile_directory
-        logging.info(
-            "Dirty profile created succesfully in '%s'", dirty_profile_dir)
-
-      # Now create another browser to run tests on using the dirty profile
-      # we just created.
-      b = self._CreateBrowserInternal(delete_profile_dir_after_run=True)
-      backend.SetProfileDirectory(dirty_profile_dir)
-    else:
-      b = self._CreateBrowserInternal(delete_profile_dir_after_run=True)
-    return b
-
-  def SupportsOptions(self, options):
-    if (len(options.extensions_to_load) != 0) and self._is_content_shell:
-      return False
-    return True
-
-  @property
-  def last_modification_time(self):
-    if os.path.exists(self._local_executable):
-      return os.path.getmtime(self._local_executable)
-    return -1
-
-def SelectDefaultBrowser(possible_browsers):
-  local_builds_by_date = [
-      b for b in sorted(possible_browsers,
-                        key=attrgetter('last_modification_time'))
-      if b.is_local_build]
-  if local_builds_by_date:
-    return local_builds_by_date[-1]
-  return None
-
-def CanFindAvailableBrowsers():
-  return not cros_interface.IsRunningOnCrosDevice()
-
-def FindAllAvailableBrowsers(options):
-  """Finds all the desktop browsers available on this machine."""
-  browsers = []
-
-  if not CanFindAvailableBrowsers():
-    return []
-
-  has_display = True
-  if (sys.platform.startswith('linux') and
-      os.getenv('DISPLAY') == None):
-    has_display = False
-
-  # Look for a browser in the standard chrome build locations.
-  if options.chrome_root:
-    chrome_root = options.chrome_root
-  else:
-    chrome_root = util.GetChromiumSrcDir()
-
-  if sys.platform == 'darwin':
-    chromium_app_name = 'Chromium.app/Contents/MacOS/Chromium'
-    content_shell_app_name = 'Content Shell.app/Contents/MacOS/Content Shell'
-    mac_dir = 'mac'
-    if platform.architecture()[0] == '64bit':
-      mac_dir = 'mac_64'
-    flash_path = os.path.join(
-        chrome_root, 'third_party', 'adobe', 'flash', 'binaries', 'ppapi',
-        mac_dir, 'PepperFlashPlayer.plugin')
-  elif sys.platform.startswith('linux'):
-    chromium_app_name = 'chrome'
-    content_shell_app_name = 'content_shell'
-    linux_dir = 'linux'
-    if platform.architecture()[0] == '64bit':
-      linux_dir = 'linux_x64'
-    flash_path = os.path.join(
-        chrome_root, 'third_party', 'adobe', 'flash', 'binaries', 'ppapi',
-        linux_dir, 'libpepflashplayer.so')
-  elif sys.platform.startswith('win'):
-    chromium_app_name = 'chrome.exe'
-    content_shell_app_name = 'content_shell.exe'
-    win_dir = 'win'
-    if platform.architecture()[0] == '64bit':
-      win_dir = 'win_x64'
-    flash_path = os.path.join(
-        chrome_root, 'third_party', 'adobe', 'flash', 'binaries', 'ppapi',
-        win_dir, 'pepflashplayer.dll')
-  else:
-    raise Exception('Platform not recognized')
-
-  def IsExecutable(path):
-    return os.path.isfile(path) and os.access(path, os.X_OK)
-
-  # Add the explicit browser executable if given.
-  if options.browser_executable:
-    normalized_executable = os.path.expanduser(options.browser_executable)
-    if IsExecutable(normalized_executable):
-      browser_directory = os.path.dirname(options.browser_executable)
-      browsers.append(PossibleDesktopBrowser('exact', options,
-                                             normalized_executable, flash_path,
-                                             False, browser_directory))
-    else:
-      logging.warning('%s specified by browser_executable does not exist',
-                      normalized_executable)
-
-  def AddIfFound(browser_type, build_dir, type_dir, app_name, content_shell):
-    browser_directory = os.path.join(chrome_root, build_dir, type_dir)
-    app = os.path.join(browser_directory, app_name)
-    if IsExecutable(app):
-      browsers.append(PossibleDesktopBrowser(browser_type, options,
-                                             app, flash_path, content_shell,
-                                             browser_directory,
-                                             is_local_build=True))
-      return True
-    return False
-
-  # Add local builds
-  for build_dir, build_type in util.GetBuildDirectories():
-    AddIfFound(build_type.lower(), build_dir, build_type,
-               chromium_app_name, False)
-    AddIfFound('content-shell-' + build_type.lower(), build_dir, build_type,
-               content_shell_app_name, True)
-
-  # Mac-specific options.
-  if sys.platform == 'darwin':
-    mac_canary_root = '/Applications/Google Chrome Canary.app/'
-    mac_canary = mac_canary_root + 'Contents/MacOS/Google Chrome Canary'
-    mac_system_root = '/Applications/Google Chrome.app'
-    mac_system = mac_system_root + '/Contents/MacOS/Google Chrome'
-    if IsExecutable(mac_canary):
-      browsers.append(PossibleDesktopBrowser('canary', options,
-                                             mac_canary, None, False,
-                                             mac_canary_root))
-
-    if IsExecutable(mac_system):
-      browsers.append(PossibleDesktopBrowser('system', options,
-                                             mac_system, None, False,
-                                             mac_system_root))
-
-  # Linux specific options.
-  if sys.platform.startswith('linux'):
-    # Look for a google-chrome instance.
-    found = False
-    try:
-      with open(os.devnull, 'w') as devnull:
-        found = subprocess.call(['google-chrome', '--version'],
-                                stdout=devnull, stderr=devnull) == 0
-    except OSError:
-      pass
-    if found:
-      browsers.append(PossibleDesktopBrowser('system', options,
-                                             'google-chrome', None, False,
-                                             '/opt/google/chrome'))
-
-  # Win32-specific options.
-  if sys.platform.startswith('win'):
-    system_path = os.path.join('Google', 'Chrome', 'Application')
-    canary_path = os.path.join('Google', 'Chrome SxS', 'Application')
-
-    win_search_paths = [os.getenv('PROGRAMFILES(X86)'),
-                        os.getenv('PROGRAMFILES'),
-                        os.getenv('LOCALAPPDATA')]
-
-    def AddIfFoundWin(browser_name, app_path):
-      browser_directory = os.path.join(path, app_path)
-      app = os.path.join(browser_directory, chromium_app_name)
-      if IsExecutable(app):
-        browsers.append(PossibleDesktopBrowser(browser_name, options,
-                                               app, flash_path, False,
-                                               browser_directory))
-        return True
-      return False
-
-    for path in win_search_paths:
-      if not path:
-        continue
-      if AddIfFoundWin('canary', canary_path):
-        break
-
-    for path in win_search_paths:
-      if not path:
-        continue
-      if AddIfFoundWin('system', system_path):
-        break
-
-  if len(browsers) and not has_display:
-    logging.warning(
-      'Found (%s), but you do not have a DISPLAY environment set.' %
-      ','.join([b.browser_type for b in browsers]))
-    return []
-
-  return browsers
diff --git a/tools/telemetry/telemetry/core/chrome/desktop_browser_finder_unittest.py b/tools/telemetry/telemetry/core/chrome/desktop_browser_finder_unittest.py
deleted file mode 100644
index fe0e6ae..0000000
--- a/tools/telemetry/telemetry/core/chrome/desktop_browser_finder_unittest.py
+++ /dev/null
@@ -1,188 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import unittest
-
-from telemetry.core import browser_options
-from telemetry.core.chrome import desktop_browser_finder
-from telemetry.unittest import system_stub
-
-# This file verifies the logic for finding a browser instance on all platforms
-# at once. It does so by providing stubs for the OS/sys/subprocess primitives
-# that the underlying finding logic usually uses to locate a suitable browser.
-# We prefer this approach to having to run the same test on every platform on
-# which we want this code to work.
-
-class FindTestBase(unittest.TestCase):
-  def setUp(self):
-    self._options = browser_options.BrowserOptions()
-    self._options.chrome_root = '../../../'
-    self._stubs = system_stub.Override(desktop_browser_finder,
-                                       ['os', 'subprocess', 'sys'])
-
-  def tearDown(self):
-    self._stubs.Restore()
-
-  @property
-  def _files(self):
-    return self._stubs.os.path.files
-
-  def DoFindAll(self):
-    return desktop_browser_finder.FindAllAvailableBrowsers(self._options)
-
-  def DoFindAllTypes(self):
-    browsers = self.DoFindAll()
-    return [b.browser_type for b in browsers]
-
-def has_type(array, browser_type):
-  return len([x for x in array if x.browser_type == browser_type]) != 0
-
-class FindSystemTest(FindTestBase):
-  def setUp(self):
-    super(FindSystemTest, self).setUp()
-    self._stubs.sys.platform = 'win32'
-
-  def testFindProgramFiles(self):
-    self._files.append(
-        'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe')
-    self._stubs.os.program_files = 'C:\\Program Files'
-    self.assertTrue('system' in self.DoFindAllTypes())
-
-  def testFindProgramFilesX86(self):
-    self._files.append(
-        'C:\\Program Files(x86)\\Google\\Chrome\\Application\\chrome.exe')
-    self._stubs.os.program_files_x86 = 'C:\\Program Files(x86)'
-    self.assertTrue('system' in self.DoFindAllTypes())
-
-  def testFindLocalAppData(self):
-    self._files.append(
-        'C:\\Local App Data\\Google\\Chrome\\Application\\chrome.exe')
-    self._stubs.os.local_app_data = 'C:\\Local App Data'
-    self.assertTrue('system' in self.DoFindAllTypes())
-
-class FindLocalBuildsTest(FindTestBase):
-  def setUp(self):
-    super(FindLocalBuildsTest, self).setUp()
-    self._stubs.sys.platform = 'win32'
-
-  def testFindBuild(self):
-    self._files.append('..\\..\\..\\build\\Release\\chrome.exe')
-    self.assertTrue('release' in self.DoFindAllTypes())
-
-  def testFindOut(self):
-    self._files.append('..\\..\\..\\out\\Release\\chrome.exe')
-    self.assertTrue('release' in self.DoFindAllTypes())
-
-  def testFindSconsbuild(self):
-    self._files.append('..\\..\\..\\sconsbuild\\Release\\chrome.exe')
-    self.assertTrue('release' in self.DoFindAllTypes())
-
-  def testFindXcodebuild(self):
-    self._files.append('..\\..\\..\\xcodebuild\\Release\\chrome.exe')
-    self.assertTrue('release' in self.DoFindAllTypes())
-
-class OSXFindTest(FindTestBase):
-  def setUp(self):
-    super(OSXFindTest, self).setUp()
-    self._stubs.sys.platform = 'darwin'
-    self._files.append('/Applications/Google Chrome Canary.app/'
-                       'Contents/MacOS/Google Chrome Canary')
-    self._files.append('/Applications/Google Chrome.app/' +
-                       'Contents/MacOS/Google Chrome')
-    self._files.append(
-      '../../../out/Release/Chromium.app/Contents/MacOS/Chromium')
-    self._files.append(
-      '../../../out/Debug/Chromium.app/Contents/MacOS/Chromium')
-    self._files.append(
-      '../../../out/Release/Content Shell.app/Contents/MacOS/Content Shell')
-    self._files.append(
-      '../../../out/Debug/Content Shell.app/Contents/MacOS/Content Shell')
-
-  def testFindAll(self):
-    types = self.DoFindAllTypes()
-    self.assertEquals(
-      set(types),
-      set(['debug', 'release',
-           'content-shell-debug', 'content-shell-release',
-           'canary', 'system']))
-
-
-class LinuxFindTest(FindTestBase):
-  def setUp(self):
-    super(LinuxFindTest, self).setUp()
-
-    self._stubs.sys.platform = 'linux2'
-    self._files.append('/foo/chrome')
-    self._files.append('../../../out/Release/chrome')
-    self._files.append('../../../out/Debug/chrome')
-    self._files.append('../../../out/Release/content_shell')
-    self._files.append('../../../out/Debug/content_shell')
-
-    self.has_google_chrome_on_path = False
-    this = self
-    def call_hook(*args, **kwargs): # pylint: disable=W0613
-      if this.has_google_chrome_on_path:
-        return 0
-      raise OSError('Not found')
-    self._stubs.subprocess.call = call_hook
-
-  def testFindAllWithExact(self):
-    types = self.DoFindAllTypes()
-    self.assertEquals(
-        set(types),
-        set(['debug', 'release',
-             'content-shell-debug', 'content-shell-release']))
-
-  def testFindWithProvidedExecutable(self):
-    self._options.browser_executable = '/foo/chrome'
-    self.assertTrue('exact' in self.DoFindAllTypes())
-
-  def testFindUsingDefaults(self):
-    self.has_google_chrome_on_path = True
-    self.assertTrue('release' in self.DoFindAllTypes())
-
-    del self._files[1]
-    self.has_google_chrome_on_path = True
-    self.assertTrue('system' in self.DoFindAllTypes())
-
-    self.has_google_chrome_on_path = False
-    del self._files[1]
-    self.assertEquals(['content-shell-debug', 'content-shell-release'],
-                      self.DoFindAllTypes())
-
-  def testFindUsingRelease(self):
-    self.assertTrue('release' in self.DoFindAllTypes())
-
-
-class WinFindTest(FindTestBase):
-  def setUp(self):
-    super(WinFindTest, self).setUp()
-
-    self._stubs.sys.platform = 'win32'
-    self._stubs.os.local_app_data = 'c:\\Users\\Someone\\AppData\\Local'
-    self._files.append('c:\\tmp\\chrome.exe')
-    self._files.append('..\\..\\..\\build\\Release\\chrome.exe')
-    self._files.append('..\\..\\..\\build\\Debug\\chrome.exe')
-    self._files.append('..\\..\\..\\build\\Release\\content_shell.exe')
-    self._files.append('..\\..\\..\\build\\Debug\\content_shell.exe')
-    self._files.append(self._stubs.os.local_app_data + '\\' +
-                       'Google\\Chrome\\Application\\chrome.exe')
-    self._files.append(self._stubs.os.local_app_data + '\\' +
-                       'Google\\Chrome SxS\\Application\\chrome.exe')
-
-  def testFindAllGivenDefaults(self):
-    types = self.DoFindAllTypes()
-    self.assertEquals(set(types),
-                      set(['debug', 'release',
-                           'content-shell-debug', 'content-shell-release',
-                           'system', 'canary']))
-
-  def testFindAllWithExact(self):
-    self._options.browser_executable = 'c:\\tmp\\chrome.exe'
-    types = self.DoFindAllTypes()
-    self.assertEquals(
-        set(types),
-        set(['exact',
-             'debug', 'release',
-             'content-shell-debug', 'content-shell-release',
-             'system', 'canary']))
diff --git a/tools/telemetry/telemetry/core/chrome/extension_dict_backend.py b/tools/telemetry/telemetry/core/chrome/extension_dict_backend.py
deleted file mode 100644
index d84a995..0000000
--- a/tools/telemetry/telemetry/core/chrome/extension_dict_backend.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import json
-import re
-import weakref
-
-from telemetry.core import extension_page
-from telemetry.core.chrome import inspector_backend
-
-class ExtensionNotFoundException(Exception):
-  pass
-
-class ExtensionDictBackend(object):
-  def __init__(self, browser_backend):
-    self._browser_backend = browser_backend
-    # Maps extension ids to ExtensionPage objects.
-    self._extension_dict = weakref.WeakValueDictionary()
-
-  def __getitem__(self, extension_id):
-    extension_object = self._extension_dict.get(extension_id)
-    if not extension_object:
-      extension_object = self._CreateExtensionObject(extension_id)
-      assert extension_object
-      self._extension_dict[extension_id] = extension_object
-    return extension_object
-
-  def __contains__(self, extension_id):
-    return extension_id in self._GetExtensionIds()
-
-  @staticmethod
-  def _ExtractExtensionId(url):
-    m = re.match(r"(chrome-extension://)([^/]+)", url)
-    assert m
-    return m.group(2)
-
-  @staticmethod
-  def _GetExtensionId(extension_info):
-    if 'url' not in extension_info:
-      return None
-    return ExtensionDictBackend._ExtractExtensionId(extension_info['url'])
-
-  def _CreateExtensionObject(self, extension_id):
-    extension_info = self._FindExtensionInfo(extension_id)
-    if not extension_info or not 'webSocketDebuggerUrl' in extension_info:
-      raise ExtensionNotFoundException()
-    return extension_page.ExtensionPage(
-        self._CreateInspectorBackendForDebuggerUrl(
-            extension_info['webSocketDebuggerUrl']))
-
-  def _CreateInspectorBackendForDebuggerUrl(self, debugger_url):
-    return inspector_backend.InspectorBackend(self._browser_backend.browser,
-                                              self._browser_backend,
-                                              debugger_url)
-
-  def _FindExtensionInfo(self, extension_id):
-    for extension_info in self._GetExtensionInfoList():
-      if self._GetExtensionId(extension_info) == extension_id:
-        return extension_info
-    return None
-
-  def _GetExtensionInfoList(self, timeout=None):
-    data = self._browser_backend.Request('', timeout=timeout)
-    return self._FilterExtensions(json.loads(data))
-
-  def _FilterExtensions(self, all_pages):
-    return [page_info for page_info in all_pages
-            if page_info['url'].startswith('chrome-extension://')]
-
-  def _GetExtensionIds(self):
-    return map(self._GetExtensionId, self._GetExtensionInfoList())
diff --git a/tools/telemetry/telemetry/core/chrome/facebook_credentials_backend.py b/tools/telemetry/telemetry/core/chrome/facebook_credentials_backend.py
deleted file mode 100644
index b882132..0000000
--- a/tools/telemetry/telemetry/core/chrome/facebook_credentials_backend.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-from telemetry.core.chrome import form_based_credentials_backend
-
-class FacebookCredentialsBackend(
-    form_based_credentials_backend.FormBasedCredentialsBackend):
-  def IsAlreadyLoggedIn(self, tab):
-    return tab.EvaluateJavaScript(
-        'document.getElementById("fbNotificationsList")!== null || '
-        'document.getElementById("m_home_notice")!== null')
-
-  @property
-  def credentials_type(self):
-    return 'facebook'
-
-  @property
-  def url(self):
-    return 'http://www.facebook.com/'
-
-  @property
-  def login_form_id(self):
-    return 'login_form'
-
-  @property
-  def login_input_id(self):
-    return 'email'
-
-  @property
-  def password_input_id(self):
-    return 'pass'
diff --git a/tools/telemetry/telemetry/core/chrome/facebook_credentials_backend_unittest.py b/tools/telemetry/telemetry/core/chrome/facebook_credentials_backend_unittest.py
deleted file mode 100644
index 51a2571..0000000
--- a/tools/telemetry/telemetry/core/chrome/facebook_credentials_backend_unittest.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 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.
-from telemetry.core.chrome import form_based_credentials_backend_unittest_base
-from telemetry.core.chrome import facebook_credentials_backend
-
-class TestFacebookCredentialsBackend(
-    form_based_credentials_backend_unittest_base.
-    FormBasedCredentialsBackendUnitTestBase):
-  def setUp(self):
-    self._credentials_type = 'facebook'
-
-  def testLoginUsingMock(self):
-    self._LoginUsingMock(
-        facebook_credentials_backend.FacebookCredentialsBackend(),
-        'http://www.facebook.com/', 'email', 'pass')
diff --git a/tools/telemetry/telemetry/core/chrome/form_based_credentials_backend.py b/tools/telemetry/telemetry/core/chrome/form_based_credentials_backend.py
deleted file mode 100644
index 1cc7c75..0000000
--- a/tools/telemetry/telemetry/core/chrome/form_based_credentials_backend.py
+++ /dev/null
@@ -1,110 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import logging
-
-from telemetry.core import util
-
-
-def _WaitForLoginFormToLoad(backend, login_form_id, tab):
-  def IsFormLoadedOrAlreadyLoggedIn():
-    return tab.EvaluateJavaScript(
-        'document.querySelector("#%s")!== null' % login_form_id) or \
-            backend.IsAlreadyLoggedIn(tab)
-
-  # Wait until the form is submitted and the page completes loading.
-  util.WaitFor(lambda: IsFormLoadedOrAlreadyLoggedIn(), # pylint: disable=W0108
-               60)
-
-def _SubmitFormAndWait(form_id, tab):
-  js = 'document.getElementById("%s").submit();' % form_id
-  tab.ExecuteJavaScript(js)
-
-  def IsLoginStillHappening():
-    return tab.EvaluateJavaScript(
-        'document.querySelector("#%s")!== null' % form_id)
-
-  # Wait until the form is submitted and the page completes loading.
-  util.WaitFor(lambda: not IsLoginStillHappening(), 60)
-
-class FormBasedCredentialsBackend(object):
-  def __init__(self):
-    self._logged_in = False
-
-  def IsAlreadyLoggedIn(self, tab):
-    raise NotImplementedError()
-
-  @property
-  def credentials_type(self):
-    raise NotImplementedError()
-
-  @property
-  def url(self):
-    raise NotImplementedError()
-
-  @property
-  def login_form_id(self):
-    raise NotImplementedError()
-
-  @property
-  def login_input_id(self):
-    raise NotImplementedError()
-
-  @property
-  def password_input_id(self):
-    raise NotImplementedError()
-
-  def IsLoggedIn(self):
-    return self._logged_in
-
-  def _ResetLoggedInState(self):
-    """Makes the backend think we're not logged in even though we are.
-    Should only be used in unit tests to simulate --dont-override-profile.
-    """
-    self._logged_in = False
-
-  def LoginNeeded(self, tab, config):
-    """Logs in to a test account.
-
-    Raises:
-      RuntimeError: if could not get credential information.
-    """
-    if self._logged_in:
-      return True
-
-    if 'username' not in config or 'password' not in config:
-      message = ('Credentials for "%s" must include username and password.' %
-                 self.credentials_type)
-      raise RuntimeError(message)
-
-    logging.debug('Logging into %s account...' % self.credentials_type)
-
-    try:
-      logging.info('Loading %s...', self.url)
-      tab.Navigate(self.url)
-      _WaitForLoginFormToLoad(self, self.login_form_id, tab)
-
-      if self.IsAlreadyLoggedIn(tab):
-        self._logged_in = True
-        return True
-
-      tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
-      logging.info('Loaded page: %s', self.url)
-
-      email_id = 'document.querySelector("#%s").%s.value = "%s"; ' % (
-          self.login_form_id, self.login_input_id, config['username'])
-      password = 'document.querySelector("#%s").%s.value = "%s"; ' % (
-          self.login_form_id, self.password_input_id, config['password'])
-      tab.ExecuteJavaScript(email_id)
-      tab.ExecuteJavaScript(password)
-
-      _SubmitFormAndWait(self.login_form_id, tab)
-
-      self._logged_in = True
-      return True
-    except util.TimeoutException:
-      logging.warning('Timed out while loading: %s', self.url)
-      return False
-
-  def LoginNoLongerNeeded(self, tab): # pylint: disable=W0613
-    assert self._logged_in
diff --git a/tools/telemetry/telemetry/core/chrome/form_based_credentials_backend_unittest_base.py b/tools/telemetry/telemetry/core/chrome/form_based_credentials_backend_unittest_base.py
deleted file mode 100644
index 2577b93..0000000
--- a/tools/telemetry/telemetry/core/chrome/form_based_credentials_backend_unittest_base.py
+++ /dev/null
@@ -1,128 +0,0 @@
-# Copyright (c) 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import logging
-import os
-import unittest
-
-from telemetry.core import browser_finder
-from telemetry.core import util
-from telemetry.unittest import simple_mock
-from telemetry.unittest import options_for_unittests
-from telemetry.unittest import DisabledTest
-
-_ = simple_mock.DONT_CARE
-
-
-def _GetCredentialsPath():
-  # TODO: This shouldn't depend on tools/perf.
-  credentials_path = os.path.join(util.GetChromiumSrcDir(),
-      'tools', 'perf', 'data', 'credentials.json')
-  if not os.path.exists(credentials_path):
-    return None
-  return credentials_path
-
-
-class FormBasedCredentialsBackendUnitTestBase(unittest.TestCase):
-  def setUp(self):
-    self._credentials_type = None
-
-  @DisabledTest
-  def testRealLoginIfPossible(self):
-    credentials_path = _GetCredentialsPath()
-    if not credentials_path:
-      logging.warning('Credentials file not found, skipping test.')
-      return
-
-    options = options_for_unittests.GetCopy()
-    with browser_finder.FindBrowser(options).Create() as b:
-      b.Start()
-      b.credentials.credentials_path = credentials_path
-      if not b.credentials.CanLogin(self._credentials_type):
-        return
-      ret = b.credentials.LoginNeeded(b.tabs[0], self._credentials_type)
-      self.assertTrue(ret)
-
-  @DisabledTest
-  def testRealLoginWithDontOverrideProfileIfPossible(self):
-    credentials_path = _GetCredentialsPath()
-    if not credentials_path:
-      logging.warning('Credentials file not found, skipping test.')
-      return
-
-    options = options_for_unittests.GetCopy()
-
-    # Login once to make sure our default profile is logged in.
-    with browser_finder.FindBrowser(options).Create() as b:
-      b.Start()
-      b.credentials.credentials_path = credentials_path
-
-      if not b.credentials.CanLogin(self._credentials_type):
-        return
-
-      tab = b.tabs[0]
-
-      # Should not be logged in, since this is a fresh credentials
-      # instance.
-      self.assertFalse(b.credentials.IsLoggedIn(self._credentials_type))
-
-      # Log in.
-      ret = b.credentials.LoginNeeded(tab, self._credentials_type)
-
-      # Make sure login was successful.
-      self.assertTrue(ret)
-      self.assertTrue(b.credentials.IsLoggedIn(self._credentials_type))
-
-      # Reset state. Now the backend thinks we're logged out, even
-      # though we are logged in in our current browser session. This
-      # simulates the effects of running with --dont-override-profile.
-      b.credentials._ResetLoggedInState() # pylint: disable=W0212
-
-      # Make sure the backend thinks we're logged out.
-      self.assertFalse(b.credentials.IsLoggedIn(self._credentials_type))
-      self.assertTrue(b.credentials.CanLogin(self._credentials_type))
-
-      # Attempt to login again. This should detect that we've hit
-      # the 'logged in' page instead of the login form, and succeed
-      # instead of timing out.
-      ret = b.credentials.LoginNeeded(tab, self._credentials_type)
-
-      # Make sure our login attempt did in fact succeed and set the
-      # backend's internal state to 'logged in'.
-      self.assertTrue(ret)
-      self.assertTrue(b.credentials.IsLoggedIn(self._credentials_type))
-
-  def testLoginUsingMock(self):
-    raise NotImplementedError()
-
-  def _LoginUsingMock(self, backend, login_page_url, email_element_id,
-                      password_element_id): # pylint: disable=R0201
-    tab = simple_mock.MockObject()
-
-    config = {'username': 'blah',
-              'password': 'blargh'}
-
-    tab.ExpectCall('Navigate', login_page_url)
-    tab.ExpectCall('EvaluateJavaScript', _).WillReturn(False)
-    tab.ExpectCall('EvaluateJavaScript', _).WillReturn(True)
-    tab.ExpectCall('EvaluateJavaScript', _).WillReturn(False)
-    tab.ExpectCall('WaitForDocumentReadyStateToBeInteractiveOrBetter')
-
-    def VerifyEmail(js):
-      assert email_element_id in js
-      assert 'blah' in js
-    tab.ExpectCall('ExecuteJavaScript', _).WhenCalled(VerifyEmail)
-
-    def VerifyPw(js):
-      assert password_element_id in js
-      assert 'largh' in js
-    tab.ExpectCall('ExecuteJavaScript', _).WhenCalled(VerifyPw)
-
-    def VerifySubmit(js):
-      assert '.submit' in js
-    tab.ExpectCall('ExecuteJavaScript', _).WhenCalled(VerifySubmit)
-
-    # Checking for form still up.
-    tab.ExpectCall('EvaluateJavaScript', _).WillReturn(False)
-
-    backend.LoginNeeded(tab, config)
diff --git a/tools/telemetry/telemetry/core/chrome/google_credentials_backend.py b/tools/telemetry/telemetry/core/chrome/google_credentials_backend.py
deleted file mode 100644
index 1489bd5..0000000
--- a/tools/telemetry/telemetry/core/chrome/google_credentials_backend.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-from telemetry.core.chrome import form_based_credentials_backend
-
-class GoogleCredentialsBackend(
-    form_based_credentials_backend.FormBasedCredentialsBackend):
-  def IsAlreadyLoggedIn(self, tab):
-    return tab.EvaluateJavaScript(
-        'document.getElementById("gb")!== null')
-
-  @property
-  def credentials_type(self):
-    return 'google'
-
-  @property
-  def url(self):
-    return 'https://accounts.google.com/'
-
-  @property
-  def login_form_id(self):
-    return 'gaia_loginform'
-
-  @property
-  def login_input_id(self):
-    return 'Email'
-
-  @property
-  def password_input_id(self):
-    return 'Passwd'
diff --git a/tools/telemetry/telemetry/core/chrome/google_credentials_backend_unittest.py b/tools/telemetry/telemetry/core/chrome/google_credentials_backend_unittest.py
deleted file mode 100644
index 42ad8ed..0000000
--- a/tools/telemetry/telemetry/core/chrome/google_credentials_backend_unittest.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-from telemetry.core.chrome import form_based_credentials_backend_unittest_base
-from telemetry.core.chrome import google_credentials_backend
-
-class TestGoogleCredentialsBackend(
-    form_based_credentials_backend_unittest_base.
-    FormBasedCredentialsBackendUnitTestBase):
-  def setUp(self):
-    self._credentials_type = 'google'
-
-  def testLoginUsingMock(self):
-    self._LoginUsingMock(google_credentials_backend.GoogleCredentialsBackend(),
-                         'https://accounts.google.com/', 'Email',
-                         'Passwd')
diff --git a/tools/telemetry/telemetry/core/chrome/inspector_backend.py b/tools/telemetry/telemetry/core/chrome/inspector_backend.py
deleted file mode 100644
index 1b75024..0000000
--- a/tools/telemetry/telemetry/core/chrome/inspector_backend.py
+++ /dev/null
@@ -1,326 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import json
-import logging
-import socket
-import sys
-
-from telemetry.core import util
-from telemetry.core import exceptions
-from telemetry.core.chrome import inspector_console
-from telemetry.core.chrome import inspector_memory
-from telemetry.core.chrome import inspector_network
-from telemetry.core.chrome import inspector_page
-from telemetry.core.chrome import inspector_runtime
-from telemetry.core.chrome import inspector_timeline
-from telemetry.core.chrome import png_bitmap
-from telemetry.core.chrome import websocket
-
-class InspectorException(Exception):
-  pass
-
-class InspectorBackend(object):
-  def __init__(self, browser, browser_backend, debugger_url):
-    assert debugger_url
-    self._browser = browser
-    self._browser_backend = browser_backend
-    self._debugger_url = debugger_url
-    self._socket = None
-    self._domain_handlers = {}
-    self._cur_socket_timeout = 0
-    self._next_request_id = 0
-
-    self._console = inspector_console.InspectorConsole(self)
-    self._memory = inspector_memory.InspectorMemory(self)
-    self._page = inspector_page.InspectorPage(self)
-    self._runtime = inspector_runtime.InspectorRuntime(self)
-    self._timeline = inspector_timeline.InspectorTimeline(self)
-    self._network = inspector_network.InspectorNetwork(self)
-
-  def __del__(self):
-    self.Disconnect()
-
-  def _Connect(self):
-    if self._socket:
-      return
-    try:
-      self._socket = websocket.create_connection(self._debugger_url)
-    except (websocket.WebSocketException):
-      if self._browser_backend.IsBrowserRunning():
-        raise exceptions.TabCrashException(sys.exc_info()[1])
-      else:
-        raise exceptions.BrowserGoneException()
-
-    self._cur_socket_timeout = 0
-    self._next_request_id = 0
-
-  def Disconnect(self):
-    for _, handlers in self._domain_handlers.items():
-      _, will_close_handler = handlers
-      will_close_handler()
-    self._domain_handlers = {}
-
-    if self._socket:
-      self._socket.close()
-      self._socket = None
-
-  # General public methods.
-
-  @property
-  def browser(self):
-    return self._browser
-
-  @property
-  def url(self):
-    self.Disconnect()
-    return self._browser_backend.tab_list_backend.GetTabUrl(self._debugger_url)
-
-  def Activate(self):
-    self._Connect()
-    self._browser_backend.tab_list_backend.ActivateTab(self._debugger_url)
-
-  def Close(self):
-    self.Disconnect()
-    self._browser_backend.tab_list_backend.CloseTab(self._debugger_url)
-
-  # Public methods implemented in JavaScript.
-
-  def WaitForDocumentReadyStateToBeComplete(self, timeout):
-    util.WaitFor(
-        lambda: self._runtime.Evaluate('document.readyState') == 'complete',
-        timeout)
-
-  def WaitForDocumentReadyStateToBeInteractiveOrBetter(
-      self, timeout):
-    def IsReadyStateInteractiveOrBetter():
-      rs = self._runtime.Evaluate('document.readyState')
-      return rs == 'complete' or rs == 'interactive'
-    util.WaitFor(IsReadyStateInteractiveOrBetter, timeout)
-
-  @property
-  def screenshot_supported(self):
-    if self._runtime.Evaluate(
-        'window.chrome.gpuBenchmarking === undefined'):
-      return False
-
-    if self._runtime.Evaluate(
-        'window.chrome.gpuBenchmarking.beginWindowSnapshotPNG === undefined'):
-      return False
-
-    return self._browser_backend.chrome_branch_number >= 1391
-
-  def Screenshot(self, timeout):
-    if self._runtime.Evaluate(
-        'window.chrome.gpuBenchmarking === undefined'):
-      raise Exception("Browser was not started with --enable-gpu-benchmarking")
-
-    if self._runtime.Evaluate(
-        'window.chrome.gpuBenchmarking.beginWindowSnapshotPNG === undefined'):
-      raise Exception("Browser does not support window snapshot API.")
-
-    self._runtime.Evaluate("""
-        if(!window.__telemetry) {
-          window.__telemetry = {}
-        }
-        window.__telemetry.snapshotComplete = false;
-        window.__telemetry.snapshotData = null;
-        window.chrome.gpuBenchmarking.beginWindowSnapshotPNG(
-          function(snapshot) {
-            window.__telemetry.snapshotData = snapshot;
-            window.__telemetry.snapshotComplete = true;
-          }
-        );
-    """)
-
-    def IsSnapshotComplete():
-      return self._runtime.Evaluate('window.__telemetry.snapshotComplete')
-
-    util.WaitFor(IsSnapshotComplete, timeout)
-
-    snap = self._runtime.Evaluate("""
-      (function() {
-        var data = window.__telemetry.snapshotData;
-        delete window.__telemetry.snapshotComplete;
-        delete window.__telemetry.snapshotData;
-        return data;
-      })()
-    """)
-    if snap:
-      return png_bitmap.PngBitmap(snap['data'])
-    return None
-
-  # Console public methods.
-
-  @property
-  def message_output_stream(self):  # pylint: disable=E0202
-    return self._console.message_output_stream
-
-  @message_output_stream.setter
-  def message_output_stream(self, stream):  # pylint: disable=E0202
-    self._console.message_output_stream = stream
-
-  # Memory public methods.
-
-  def GetDOMStats(self, timeout):
-    dom_counters = self._memory.GetDOMCounters(timeout)
-    return {
-      'document_count': dom_counters['documents'],
-      'node_count': dom_counters['nodes'],
-      'event_listener_count': dom_counters['jsEventListeners']
-    }
-
-  # Page public methods.
-
-  def PerformActionAndWaitForNavigate(self, action_function, timeout):
-    self._page.PerformActionAndWaitForNavigate(action_function, timeout)
-
-  def Navigate(self, url, script_to_evaluate_on_commit, timeout):
-    self._page.Navigate(url, script_to_evaluate_on_commit, timeout)
-
-  def GetCookieByName(self, name, timeout):
-    return self._page.GetCookieByName(name, timeout)
-
-  # Runtime public methods.
-
-  def ExecuteJavaScript(self, expr, timeout):
-    self._runtime.Execute(expr, timeout)
-
-  def EvaluateJavaScript(self, expr, timeout):
-    return self._runtime.Evaluate(expr, timeout)
-
-  # Timeline public methods.
-
-  @property
-  def timeline_model(self):
-    return self._timeline.timeline_model
-
-  def StartTimelineRecording(self):
-    self._timeline.Start()
-
-  def StopTimelineRecording(self):
-    self._timeline.Stop()
-
-  # Network public methods.
-
-  def ClearCache(self):
-    self._network.ClearCache()
-
-  # Methods used internally by other backends.
-
-  def DispatchNotifications(self, timeout=10):
-    self._Connect()
-    self._SetTimeout(timeout)
-
-    try:
-      data = self._socket.recv()
-    except (socket.error, websocket.WebSocketException):
-      if self._browser_backend.tab_list_backend.DoesDebuggerUrlExist(
-          self._debugger_url):
-        return
-      raise exceptions.TabCrashException(sys.exc_info()[1])
-
-    res = json.loads(data)
-    logging.debug('got [%s]', data)
-    if 'method' in res:
-      self._HandleNotification(res)
-
-  def _HandleNotification(self, res):
-    if (res['method'] == 'Inspector.detached' and
-        res.get('params', {}).get('reason','') == 'replaced_with_devtools'):
-      self._WaitForInspectorToGoAwayAndReconnect()
-      return
-    if res['method'] == 'Inspector.targetCrashed':
-      raise exceptions.TabCrashException()
-
-    mname = res['method']
-    dot_pos = mname.find('.')
-    domain_name = mname[:dot_pos]
-    if domain_name in self._domain_handlers:
-      try:
-        self._domain_handlers[domain_name][0](res)
-      except Exception:
-        import traceback
-        traceback.print_exc()
-    else:
-      logging.debug('Unhandled inspector message: %s', res)
-
-  def SendAndIgnoreResponse(self, req):
-    self._Connect()
-    req['id'] = self._next_request_id
-    self._next_request_id += 1
-    data = json.dumps(req)
-    self._socket.send(data)
-    logging.debug('sent [%s]', data)
-
-  def _SetTimeout(self, timeout):
-    if self._cur_socket_timeout != timeout:
-      self._socket.settimeout(timeout)
-      self._cur_socket_timeout = timeout
-
-  def _WaitForInspectorToGoAwayAndReconnect(self):
-    sys.stderr.write('The connection to Chrome was lost to the Inspector UI.\n')
-    sys.stderr.write('Telemetry is waiting for the inspector to be closed...\n')
-    self._socket.close()
-    self._socket = None
-    def IsBack():
-      return self._browser_backend.tab_list_backend.DoesDebuggerUrlExist(
-        self._debugger_url)
-    util.WaitFor(IsBack, 512, 0.5)
-    sys.stderr.write('\n')
-    sys.stderr.write('Inspector\'s UI closed. Telemetry will now resume.\n')
-    self._Connect()
-
-  def SyncRequest(self, req, timeout=10):
-    self._Connect()
-    # TODO(nduca): Listen to the timeout argument
-    # pylint: disable=W0613
-    self._SetTimeout(timeout)
-    self.SendAndIgnoreResponse(req)
-
-    while True:
-      try:
-        data = self._socket.recv()
-      except (socket.error, websocket.WebSocketException):
-        if self._browser_backend.tab_list_backend.DoesDebuggerUrlExist(
-            self._debugger_url):
-          raise util.TimeoutException(
-            'Timed out waiting for reply. This is unusual.')
-        raise exceptions.TabCrashException(sys.exc_info()[1])
-
-      res = json.loads(data)
-      logging.debug('got [%s]', data)
-      if 'method' in res:
-        self._HandleNotification(res)
-        continue
-
-      if res['id'] != req['id']:
-        logging.debug('Dropped reply: %s', json.dumps(res))
-        continue
-      return res
-
-  def RegisterDomain(self,
-      domain_name, notification_handler, will_close_handler):
-    """Registers a given domain for handling notification methods.
-
-    For example, given inspector_backend:
-       def OnConsoleNotification(msg):
-          if msg['method'] == 'Console.messageAdded':
-             print msg['params']['message']
-          return
-       def OnConsoleClose(self):
-          pass
-       inspector_backend.RegisterDomain('Console',
-                                        OnConsoleNotification, OnConsoleClose)
-       """
-    assert domain_name not in self._domain_handlers
-    self._domain_handlers[domain_name] = (notification_handler,
-                                          will_close_handler)
-
-  def UnregisterDomain(self, domain_name):
-    """Unregisters a previously registered domain."""
-    assert domain_name in self._domain_handlers
-    self._domain_handlers.pop(domain_name)
-
-  def CollectGarbage(self):
-    self._page.CollectGarbage()
diff --git a/tools/telemetry/telemetry/core/chrome/inspector_console.py b/tools/telemetry/telemetry/core/chrome/inspector_console.py
deleted file mode 100644
index be37478..0000000
--- a/tools/telemetry/telemetry/core/chrome/inspector_console.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import json
-import logging
-
-class InspectorConsole(object):
-  def __init__(self, inspector_backend):
-    self._inspector_backend = inspector_backend
-    self._inspector_backend.RegisterDomain(
-        'Console',
-        self._OnNotification,
-        self._OnClose)
-    self._message_output_stream = None
-    self._last_message = None
-    self._console_enabled = False
-
-  def _OnNotification(self, msg):
-    logging.debug('Notification: %s', json.dumps(msg, indent=2))
-    if msg['method'] == 'Console.messageAdded':
-      if msg['params']['message']['url'] == 'chrome://newtab/':
-        return
-      self._last_message = 'At %s:%i: %s' % (
-        msg['params']['message']['url'],
-        msg['params']['message']['line'],
-        msg['params']['message']['text'])
-      if self._message_output_stream:
-        self._message_output_stream.write(
-          '%s\n' % self._last_message)
-
-    elif msg['method'] == 'Console.messageRepeatCountUpdated':
-      if self._message_output_stream:
-        self._message_output_stream.write(
-          '%s\n' % self._last_message)
-
-  def _OnClose(self):
-    pass
-
-  # False positive in PyLint 0.25.1: http://www.logilab.org/89092
-  @property
-  def message_output_stream(self):  # pylint: disable=E0202
-    return self._message_output_stream
-
-  @message_output_stream.setter
-  def message_output_stream(self, stream):  # pylint: disable=E0202
-    self._message_output_stream = stream
-    self._UpdateConsoleEnabledState()
-
-  def _UpdateConsoleEnabledState(self):
-    enabled = self._message_output_stream != None
-    if enabled == self._console_enabled:
-      return
-
-    if enabled:
-      method_name = 'enable'
-    else:
-      method_name = 'disable'
-    self._inspector_backend.SyncRequest({
-        'method': 'Console.%s' % method_name
-        })
-    self._console_enabled = enabled
diff --git a/tools/telemetry/telemetry/core/chrome/inspector_console_unittest.py b/tools/telemetry/telemetry/core/chrome/inspector_console_unittest.py
deleted file mode 100644
index ec85cc8..0000000
--- a/tools/telemetry/telemetry/core/chrome/inspector_console_unittest.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import re
-import StringIO
-
-from telemetry.core import util
-from telemetry.unittest import tab_test_case
-
-class TabConsoleTest(tab_test_case.TabTestCase):
-  def testConsoleOutputStream(self):
-    self._browser.SetHTTPServerDirectories(util.GetUnittestDataDir())
-
-    stream = StringIO.StringIO()
-    self._tab.message_output_stream = stream
-
-    self._tab.Navigate(
-      self._browser.http_server.UrlOf('page_that_logs_to_console.html'))
-    self._tab.WaitForDocumentReadyStateToBeComplete()
-
-    initial = self._tab.EvaluateJavaScript('window.__logCount')
-    def GotLog():
-      current = self._tab.EvaluateJavaScript('window.__logCount')
-      return current > initial
-    util.WaitFor(GotLog, 5)
-
-    lines = [l for l in stream.getvalue().split('\n') if len(l)]
-
-    self.assertTrue(len(lines) >= 1)
-    for line in lines:
-      prefix = 'http://(.+)/page_that_logs_to_console.html:9'
-      expected_line = 'At %s: Hello, world' % prefix
-      self.assertTrue(re.match(expected_line, line))
-
diff --git a/tools/telemetry/telemetry/core/chrome/inspector_memory.py b/tools/telemetry/telemetry/core/chrome/inspector_memory.py
deleted file mode 100644
index 931cb6e..0000000
--- a/tools/telemetry/telemetry/core/chrome/inspector_memory.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright (c) 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import json
-
-class InspectorMemoryException(Exception):
-  pass
-
-class InspectorMemory(object):
-  """Communicates with the remote inspector's Memory domain."""
-
-  def __init__(self, inspector_backend):
-    self._inspector_backend = inspector_backend
-    self._inspector_backend.RegisterDomain(
-        'Memory',
-        self._OnNotification,
-        self._OnClose)
-
-  def _OnNotification(self, msg):
-    pass
-
-  def _OnClose(self):
-    pass
-
-  def GetDOMCounters(self, timeout):
-    """Retrieves DOM element counts.
-
-    Args:
-      timeout: The number of seconds to wait for the inspector backend to
-          service the request before timing out.
-
-    Returns:
-      A dictionary containing the counts associated with "nodes", "documents",
-      and "jsEventListeners".
-    """
-    res = self._inspector_backend.SyncRequest({
-      'method': 'Memory.getDOMCounters'
-    }, timeout)
-    if ('result' not in res or
-        'nodes' not in res['result'] or
-        'documents' not in res['result'] or
-        'jsEventListeners' not in res['result']):
-      raise InspectorMemoryException(
-          'Inspector returned unexpected result for Memory.getDOMCounters:\n' +
-          json.dumps(res, indent=2))
-    return {
-        'nodes': res['result']['nodes'],
-        'documents': res['result']['documents'],
-        'jsEventListeners': res['result']['jsEventListeners']
-    }
diff --git a/tools/telemetry/telemetry/core/chrome/inspector_memory_unittest.py b/tools/telemetry/telemetry/core/chrome/inspector_memory_unittest.py
deleted file mode 100644
index bb4761b..0000000
--- a/tools/telemetry/telemetry/core/chrome/inspector_memory_unittest.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright (c) 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.
-
-from telemetry.core import util
-from telemetry.unittest import tab_test_case
-
-class InspectorMemoryTest(tab_test_case.TabTestCase):
-  def testGetDOMStats(self):
-    self._browser.SetHTTPServerDirectories(util.GetUnittestDataDir())
-
-    # Due to an issue with CrOS, we create a new tab here rather than
-    # using self._tab to get a consistent starting page on all platforms
-    tab = self._browser.tabs.New()
-
-    tab.Navigate(
-      self._browser.http_server.UrlOf('dom_counter_sample.html'))
-    tab.WaitForDocumentReadyStateToBeComplete()
-
-    counts = tab.dom_stats
-    self.assertEqual(counts['document_count'], 2)
-    self.assertEqual(counts['node_count'], 18)
-    self.assertEqual(counts['event_listener_count'], 2)
diff --git a/tools/telemetry/telemetry/core/chrome/inspector_network.py b/tools/telemetry/telemetry/core/chrome/inspector_network.py
deleted file mode 100644
index e533a90..0000000
--- a/tools/telemetry/telemetry/core/chrome/inspector_network.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright (c) 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.
-
-class InspectorNetwork(object):
-  def __init__(self, inspector_backend):
-    self._inspector_backend = inspector_backend
-
-  def ClearCache(self, timeout=60):
-    """Clears the browser's disk and memory cache."""
-    res = self._inspector_backend.SyncRequest({
-        'method': 'Network.canClearBrowserCache'
-        }, timeout)
-    assert res['result'], 'Cache clearing is not supported by this browser'
-    self._inspector_backend.SyncRequest({
-        'method': 'Network.clearBrowserCache'
-        }, timeout)
diff --git a/tools/telemetry/telemetry/core/chrome/inspector_page.py b/tools/telemetry/telemetry/core/chrome/inspector_page.py
deleted file mode 100644
index 43813ed..0000000
--- a/tools/telemetry/telemetry/core/chrome/inspector_page.py
+++ /dev/null
@@ -1,114 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import json
-import logging
-
-from telemetry.core import util
-
-class InspectorPage(object):
-  def __init__(self, inspector_backend):
-    self._inspector_backend = inspector_backend
-    self._inspector_backend.RegisterDomain(
-        'Page',
-        self._OnNotification,
-        self._OnClose)
-    self._navigation_pending = False
-
-  def _OnNotification(self, msg):
-    logging.debug('Notification: %s', json.dumps(msg, indent=2))
-    if msg['method'] == 'Page.frameNavigated' and self._navigation_pending:
-      url = msg['params']['frame']['url']
-      if (not url == 'chrome://newtab/' and not url == 'about:blank'
-          and not 'parentId' in msg['params']['frame']):
-        # Marks the navigation as complete and unblocks the
-        # PerformActionAndWaitForNavigate call.
-        self._navigation_pending = False
-
-  def _OnClose(self):
-    pass
-
-  def PerformActionAndWaitForNavigate(self, action_function, timeout=60):
-    """Executes action_function, and waits for the navigation to complete.
-
-    action_function is expect to result in a navigation. This function returns
-    when the navigation is complete or when the timeout has been exceeded.
-    """
-
-    # Turn on notifications. We need them to get the Page.frameNavigated event.
-    request = {
-        'method': 'Page.enable'
-        }
-    res = self._inspector_backend.SyncRequest(request, timeout)
-    assert len(res['result'].keys()) == 0
-
-    def DisablePageNotifications():
-      request = {
-          'method': 'Page.disable'
-          }
-      res = self._inspector_backend.SyncRequest(request, timeout)
-      assert len(res['result'].keys()) == 0
-
-    self._navigation_pending = True
-    try:
-      action_function()
-    except:
-      DisablePageNotifications()
-      raise
-
-    def IsNavigationDone(time_left):
-      self._inspector_backend.DispatchNotifications(time_left)
-      return not self._navigation_pending
-    util.WaitFor(IsNavigationDone, timeout, pass_time_left_to_func=True)
-
-    DisablePageNotifications()
-
-  def Navigate(self, url, script_to_evaluate_on_commit=None, timeout=60):
-    """Navigates to |url|.
-
-    If |script_to_evaluate_on_commit| is given, the script source string will be
-    evaluated when the navigation is committed. This is after the context of
-    the page exists, but before any script on the page itself has executed.
-    """
-
-    def DoNavigate():
-      if script_to_evaluate_on_commit:
-        request = {
-            'method': 'Page.addScriptToEvaluateOnLoad',
-            'params': {
-                'scriptSource': script_to_evaluate_on_commit,
-                }
-            }
-        self._inspector_backend.SyncRequest(request)
-      # Navigate the page. However, there seems to be a bug in chrome devtools
-      # protocol where the request id for this event gets held on the browser
-      # side pretty much indefinitely.
-      #
-      # So, instead of waiting for the event to actually complete, wait for the
-      # Page.frameNavigated event.
-      request = {
-          'method': 'Page.navigate',
-          'params': {
-              'url': url,
-              }
-          }
-      self._inspector_backend.SendAndIgnoreResponse(request)
-    self.PerformActionAndWaitForNavigate(DoNavigate, timeout)
-
-  def GetCookieByName(self, name, timeout=60):
-    """Returns the value of the cookie by the given |name|."""
-    request = {
-        'method': 'Page.getCookies'
-        }
-    res = self._inspector_backend.SyncRequest(request, timeout)
-    cookies = res['result']['cookies']
-    for cookie in cookies:
-      if cookie['name'] == name:
-        return cookie['value']
-    return None
-
-  def CollectGarbage(self, timeout=60):
-    request = {
-        'method': 'HeapProfiler.CollectGarbage'
-        }
-    self._inspector_backend.SyncRequest(request, timeout)
diff --git a/tools/telemetry/telemetry/core/chrome/inspector_page_unittest.py b/tools/telemetry/telemetry/core/chrome/inspector_page_unittest.py
deleted file mode 100644
index 97cb413..0000000
--- a/tools/telemetry/telemetry/core/chrome/inspector_page_unittest.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-from telemetry.core import util
-from telemetry.unittest import tab_test_case
-
-
-class InspectorPageTest(tab_test_case.TabTestCase):
-  def __init__(self, *args):
-    super(InspectorPageTest, self).__init__(*args)
-
-  def setUp(self):
-    super(InspectorPageTest, self).setUp()
-    self._browser.SetHTTPServerDirectories(util.GetUnittestDataDir())
-
-  def testPageNavigateToNormalUrl(self):
-    self._tab.Navigate(self._browser.http_server.UrlOf('blank.html'))
-    self._tab.WaitForDocumentReadyStateToBeComplete()
-
-  def testCustomActionToNavigate(self):
-    self._tab.Navigate(
-      self._browser.http_server.UrlOf('page_with_link.html'))
-    self._tab.WaitForDocumentReadyStateToBeComplete()
-    self.assertEquals(
-        self._tab.EvaluateJavaScript('document.location.pathname;'),
-        '/page_with_link.html')
-
-    custom_action_called = [False]
-    def CustomAction():
-      custom_action_called[0] = True
-      self._tab.ExecuteJavaScript('document.getElementById("clickme").click();')
-
-    self._tab.PerformActionAndWaitForNavigate(CustomAction)
-
-    self.assertTrue(custom_action_called[0])
-    self.assertEquals(
-        self._tab.EvaluateJavaScript('document.location.pathname;'),
-        '/blank.html')
-
-  def testGetCookieByName(self):
-    self._tab.Navigate(
-      self._browser.http_server.UrlOf('blank.html'))
-    self._tab.WaitForDocumentReadyStateToBeComplete()
-    self._tab.ExecuteJavaScript('document.cookie="foo=bar"')
-    self.assertEquals(self._tab.GetCookieByName('foo'), 'bar')
-
-  def testScriptToEvaluateOnCommit(self):
-    self._tab.Navigate(
-      self._browser.http_server.UrlOf('blank.html'),
-      script_to_evaluate_on_commit='var foo = "bar";')
-    self._tab.WaitForDocumentReadyStateToBeComplete()
-    self.assertEquals(self._tab.EvaluateJavaScript('foo'), 'bar')
diff --git a/tools/telemetry/telemetry/core/chrome/inspector_runtime.py b/tools/telemetry/telemetry/core/chrome/inspector_runtime.py
deleted file mode 100644
index e72ab5f..0000000
--- a/tools/telemetry/telemetry/core/chrome/inspector_runtime.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-from telemetry.core import exceptions
-
-class InspectorRuntime(object):
-  def __init__(self, inspector_backend):
-    self._inspector_backend = inspector_backend
-    self._inspector_backend.RegisterDomain(
-        'Runtime',
-        self._OnNotification,
-        self._OnClose)
-
-  def _OnNotification(self, msg):
-    pass
-
-  def _OnClose(self):
-    pass
-
-  def Execute(self, expr, timeout=60):
-    """Executes expr in javascript. Does not return the result.
-
-    If the expression failed to evaluate, EvaluateException will be raised.
-    """
-    self.Evaluate(expr + '; 0;', timeout)
-
-  def Evaluate(self, expr, timeout=60):
-    """Evalutes expr in javascript and returns the JSONized result.
-
-    Consider using Execute for cases where the result of the expression is not
-    needed.
-
-    If evaluation throws in javascript, a python EvaluateException will
-    be raised.
-
-    If the result of the evaluation cannot be JSONized, then an
-    EvaluationException will be raised.
-    """
-    request = {
-      'method': 'Runtime.evaluate',
-      'params': {
-        'expression': expr,
-        'returnByValue': True
-        }
-      }
-    res = self._inspector_backend.SyncRequest(request, timeout)
-    if 'error' in res:
-      raise exceptions.EvaluateException(res['error']['message'])
-
-    if 'wasThrown' in res['result'] and res['result']['wasThrown']:
-      # TODO(nduca): propagate stacks from javascript up to the python
-      # exception.
-      raise exceptions.EvaluateException(res['result']['result']['description'])
-    if res['result']['result']['type'] == 'undefined':
-      return None
-    return res['result']['result']['value']
diff --git a/tools/telemetry/telemetry/core/chrome/inspector_runtime_unittest.py b/tools/telemetry/telemetry/core/chrome/inspector_runtime_unittest.py
deleted file mode 100644
index a2d2829..0000000
--- a/tools/telemetry/telemetry/core/chrome/inspector_runtime_unittest.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-from telemetry.core import exceptions
-from telemetry.unittest import tab_test_case
-
-class InspectorRuntimeTest(tab_test_case.TabTestCase):
-  def testRuntimeEvaluateSimple(self):
-    res = self._tab.EvaluateJavaScript('1+1')
-    assert res == 2
-
-  def testRuntimeEvaluateThatFails(self):
-    self.assertRaises(exceptions.EvaluateException,
-                      lambda: self._tab.EvaluateJavaScript('fsdfsdfsf'))
-
-  def testRuntimeEvaluateOfSomethingThatCantJSONize(self):
-
-    def test():
-      self._tab.EvaluateJavaScript("""
-        var cur = {};
-        var root = {next: cur};
-        for (var i = 0; i < 1000; i++) {
-          next = {};
-          cur.next = next;
-          cur = next;
-        }
-        root;""")
-    self.assertRaises(exceptions.EvaluateException, test)
-
-  def testRuntimeExecuteOfSomethingThatCantJSONize(self):
-    self._tab.ExecuteJavaScript('window')
diff --git a/tools/telemetry/telemetry/core/chrome/inspector_timeline.py b/tools/telemetry/telemetry/core/chrome/inspector_timeline.py
deleted file mode 100644
index 76c9683..0000000
--- a/tools/telemetry/telemetry/core/chrome/inspector_timeline.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-from telemetry.core.timeline import model
-
-class TabBackendException(Exception):
-  pass
-
-class InspectorTimeline(object):
-  """Implementation of dev tools timeline."""
-  class Recorder(object):
-    """Utility class to Start / Stop recording timeline."""
-    def __init__(self, tab):
-      self._tab = tab
-
-    def __enter__(self):
-      self._tab.StartTimelineRecording()
-
-    def __exit__(self, *args):
-      self._tab.StopTimelineRecording()
-
-  def __init__(self, inspector_backend):
-    self._inspector_backend = inspector_backend
-    self._is_recording = False
-    self._timeline_model = None
-    self._raw_events = None
-
-  @property
-  def timeline_model(self):
-    return self._timeline_model
-
-  def Start(self):
-    if self._is_recording:
-      return
-    self._is_recording = True
-    self._timeline_model = None
-    self._raw_events = []
-    self._inspector_backend.RegisterDomain('Timeline',
-       self._OnNotification, self._OnClose)
-    req = {'method': 'Timeline.start'}
-    self._SendSyncRequest(req)
-
-  def Stop(self):
-    if not self._is_recording:
-      raise TabBackendException('Stop() called but not started')
-    self._is_recording = False
-    self._timeline_model = model.TimelineModel(event_data=self._raw_events,
-                                               shift_world_to_zero=False)
-    req = {'method': 'Timeline.stop'}
-    self._SendSyncRequest(req)
-    self._inspector_backend.UnregisterDomain('Timeline')
-
-  def _SendSyncRequest(self, req, timeout=60):
-    res = self._inspector_backend.SyncRequest(req, timeout)
-    if 'error' in res:
-      raise TabBackendException(res['error']['message'])
-    return res['result']
-
-  def _OnNotification(self, msg):
-    if not self._is_recording:
-      return
-    if 'method' in msg and msg['method'] == 'Timeline.eventRecorded':
-      self._OnEventRecorded(msg)
-
-  def _OnEventRecorded(self, msg):
-    record = msg.get('params', {}).get('record')
-    if record:
-      self._raw_events.append(record)
-
-  def _OnClose(self):
-    pass
diff --git a/tools/telemetry/telemetry/core/chrome/inspector_timeline_unittest.py b/tools/telemetry/telemetry/core/chrome/inspector_timeline_unittest.py
deleted file mode 100644
index 67c6137..0000000
--- a/tools/telemetry/telemetry/core/chrome/inspector_timeline_unittest.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-from telemetry.core import util
-from telemetry.core.chrome import inspector_timeline
-from telemetry.unittest import tab_test_case
-
-class InspectorTimelineTabTest(tab_test_case.TabTestCase):
-  def _StartServer(self):
-    self._browser.SetHTTPServerDirectories(util.GetUnittestDataDir())
-
-  def _WaitForAnimationFrame(self):
-    def _IsDone():
-      js_is_done = """done"""
-      return bool(self._tab.EvaluateJavaScript(js_is_done))
-    util.WaitFor(_IsDone, 5)
-
-  def testGotTimeline(self):
-    with inspector_timeline.InspectorTimeline.Recorder(self._tab):
-      self._tab.ExecuteJavaScript(
-"""
-var done = false;
-function sleep(ms) {
-  var endTime = (new Date().getTime()) + ms;
-  while ((new Date().getTime()) < endTime);
-}
-window.webkitRequestAnimationFrame(function() { sleep(10); done = true; });
-""")
-      self._WaitForAnimationFrame()
-
-    r = self._tab.timeline_model.GetAllEventsOfName('FireAnimationFrame')
-    self.assertTrue(len(r) > 0)
-    self.assertTrue(r[0].duration > 0)
diff --git a/tools/telemetry/telemetry/core/chrome/misc_web_contents_backend.py b/tools/telemetry/telemetry/core/chrome/misc_web_contents_backend.py
deleted file mode 100644
index f3a4ab7..0000000
--- a/tools/telemetry/telemetry/core/chrome/misc_web_contents_backend.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import json
-
-from telemetry.core import web_contents
-from telemetry.core.chrome import inspector_backend
-
-class MiscWebContentsBackend(object):
-  """Provides acccess to chrome://oobe/login page, which is neither an extension
-  nor a tab."""
-  def __init__(self, browser_backend):
-    self._browser_backend = browser_backend
-
-  def GetOobe(self):
-    oobe_web_contents_info = self._FindWebContentsInfo()
-    if oobe_web_contents_info:
-      debugger_url = oobe_web_contents_info.get('webSocketDebuggerUrl')
-      if debugger_url:
-        inspector = self._CreateInspectorBackend(debugger_url)
-        return web_contents.WebContents(inspector)
-    return None
-
-  def _CreateInspectorBackend(self, debugger_url):
-    return inspector_backend.InspectorBackend(self._browser_backend.browser,
-                                              self._browser_backend,
-                                              debugger_url)
-
-  def _ListWebContents(self, timeout=None):
-    data = self._browser_backend.Request('', timeout=timeout)
-    return json.loads(data)
-
-  def _FindWebContentsInfo(self):
-    for web_contents_info in self._ListWebContents():
-      # Prior to crrev.com/203152, url was chrome://oobe/login.
-      if (web_contents_info.get('url').startswith('chrome://oobe')):
-        return web_contents_info
-    return None
diff --git a/tools/telemetry/telemetry/core/chrome/png_bitmap.py b/tools/telemetry/telemetry/core/chrome/png_bitmap.py
deleted file mode 100644
index dede6fb..0000000
--- a/tools/telemetry/telemetry/core/chrome/png_bitmap.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import base64
-
-from telemetry.core import util
-
-util.AddDirToPythonPath(util.GetTelemetryDir(), 'third_party', 'png')
-import png  # pylint: disable=F0401
-
-
-class PngColor(object):
-  """Encapsulates an RGB color retreived from a PngBitmap"""
-
-  def __init__(self, r, g, b, a=255):
-    self.r = r
-    self.g = g
-    self.b = b
-    self.a = a
-
-  def IsEqual(self, expected_color, tolerance=0):
-    """Verifies that the color is within a given tolerance of
-    the expected color"""
-    r_diff = abs(self.r - expected_color.r)
-    g_diff = abs(self.g - expected_color.g)
-    b_diff = abs(self.b - expected_color.b)
-    a_diff = abs(self.a - expected_color.a)
-    return (r_diff <= tolerance and g_diff <= tolerance
-        and b_diff <= tolerance and a_diff <= tolerance)
-
-  def AssertIsRGB(self, r, g, b, tolerance=0):
-    assert self.IsEqual(PngColor(r, g, b), tolerance)
-
-  def AssertIsRGBA(self, r, g, b, a, tolerance=0):
-    assert self.IsEqual(PngColor(r, g, b, a), tolerance)
-
-
-class PngBitmap(object):
-  """Utilities for parsing and inspecting inspecting a PNG"""
-
-  def __init__(self, base64_png):
-    self._png_data = base64.b64decode(base64_png)
-    self._png = png.Reader(bytes=self._png_data)
-    rgba8_data = self._png.asRGBA8()
-    self._width = rgba8_data[0]
-    self._height = rgba8_data[1]
-    self._pixels = list(rgba8_data[2])
-    self._metadata = rgba8_data[3]
-
-  @property
-  def width(self):
-    """Width of the snapshot"""
-    return self._width
-
-  @property
-  def height(self):
-    """Height of the snapshot"""
-    return self._height
-
-  def GetPixelColor(self, x, y):
-    """Returns a PngColor for the pixel at (x, y)"""
-    row = self._pixels[y]
-    offset = x * 4
-    return PngColor(row[offset], row[offset+1], row[offset+2], row[offset+3])
-
-  def WriteFile(self, path):
-    with open(path, "wb") as f:
-      f.write(self._png_data)
diff --git a/tools/telemetry/telemetry/core/chrome/png_bitmap_unittest.py b/tools/telemetry/telemetry/core/chrome/png_bitmap_unittest.py
deleted file mode 100644
index ae33198..0000000
--- a/tools/telemetry/telemetry/core/chrome/png_bitmap_unittest.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import unittest
-
-from telemetry.core.chrome import png_bitmap
-
-# This is a simple base64 encoded 2x2 PNG which contains, in order, a single
-# Red, Yellow, Blue, and Green pixel.
-test_png = """
-iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91
-JpzAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACx
-MBAJqcGAAAABZJREFUCNdj/M/AwPCfgYGB4T/DfwY
-AHAAD/iOWZXsAAAAASUVORK5CYII=
-"""
-
-class PngBitmapTest(unittest.TestCase):
-  def testRead(self):
-    png = png_bitmap.PngBitmap(test_png)
-
-    self.assertEquals(2, png.width)
-    self.assertEquals(2, png.height)
-
-    png.GetPixelColor(0, 0).AssertIsRGB(255, 0, 0)
-    png.GetPixelColor(1, 1).AssertIsRGB(0, 255, 0)
-    png.GetPixelColor(0, 1).AssertIsRGB(0, 0, 255)
-    png.GetPixelColor(1, 0).AssertIsRGB(255, 255, 0)
diff --git a/tools/telemetry/telemetry/core/chrome/system_info_backend.py b/tools/telemetry/telemetry/core/chrome/system_info_backend.py
deleted file mode 100644
index 9ff12e1..0000000
--- a/tools/telemetry/telemetry/core/chrome/system_info_backend.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-from telemetry.core import camel_case
-from telemetry.core import system_info
-from telemetry.core.chrome import websocket_browser_connection as browser_conn
-
-
-class SystemInfoBackend(object):
-  def __init__(self, devtools_port):
-    self._conn = browser_conn.WebSocketBrowserConnection(devtools_port)
-
-  def Close(self):
-    self._conn.Close()
-
-  def GetSystemInfo(self, timeout=10):
-    req = {'method': 'SystemInfo.getInfo'}
-    res = self._conn.SyncRequest(req, timeout)
-    if 'error' in res:
-      return None
-    return system_info.SystemInfo.FromDict(
-        camel_case.ToUnderscore(res['result']))
diff --git a/tools/telemetry/telemetry/core/chrome/tab_list_backend.py b/tools/telemetry/telemetry/core/chrome/tab_list_backend.py
deleted file mode 100644
index b61e4ab..0000000
--- a/tools/telemetry/telemetry/core/chrome/tab_list_backend.py
+++ /dev/null
@@ -1,135 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import json
-import urllib2
-import weakref
-
-from telemetry.core import util
-from telemetry.core import tab
-from telemetry.core.chrome import inspector_backend
-
-class TabListBackend(object):
-  def __init__(self, browser_backend):
-    self._browser_backend = browser_backend
-
-    # Stores web socket debugger URLs in iteration order.
-    self._tab_list = []
-    # Maps debugger URLs to Tab objects.
-    self._tab_dict = weakref.WeakValueDictionary()
-
-  def Init(self):
-    self._UpdateTabList()
-
-  def New(self, timeout):
-    assert self._browser_backend.supports_tab_control
-
-    self._browser_backend.Request('new', timeout=timeout)
-    return self[-1]
-
-  def DoesDebuggerUrlExist(self, debugger_url):
-    return self._FindTabInfo(debugger_url) is not None
-
-  def CloseTab(self, debugger_url, timeout=None):
-    assert self._browser_backend.supports_tab_control
-
-    # TODO(dtu): crbug.com/160946, allow closing the last tab on some platforms.
-    # For now, just create a new tab before closing the last tab.
-    if len(self) <= 1:
-      self.New(timeout)
-
-    tab_id = debugger_url.split('/')[-1]
-    try:
-      response = self._browser_backend.Request('close/%s' % tab_id,
-                                               timeout=timeout,
-                                               throw_network_exception=True)
-    except urllib2.HTTPError:
-      raise Exception('Unable to close tab, tab id not found: %s' % tab_id)
-    assert response == 'Target is closing'
-
-    util.WaitFor(lambda: not self._FindTabInfo(debugger_url), timeout=5)
-
-    if debugger_url in self._tab_dict:
-      del self._tab_dict[debugger_url]
-    self._UpdateTabList()
-
-  def ActivateTab(self, debugger_url, timeout=None):
-    assert self._browser_backend.supports_tab_control
-
-    assert debugger_url in self._tab_dict
-    tab_id = debugger_url.split('/')[-1]
-    try:
-      response = self._browser_backend.Request('activate/%s' % tab_id,
-                                               timeout=timeout,
-                                               throw_network_exception=True)
-    except urllib2.HTTPError:
-      raise Exception('Unable to activate tab, tab id not found: %s' % tab_id)
-    assert response == 'Target activated'
-
-  def GetTabUrl(self, debugger_url):
-    tab_info = self._FindTabInfo(debugger_url)
-    # TODO(hartmanng): crbug.com/166886 (uncomment the following assert and
-    # remove the extra None check when _ListTabs is fixed):
-    # assert tab_info is not None
-    return tab_info['url'] if tab_info else None
-
-  def __iter__(self):
-    self._UpdateTabList()
-    return self._tab_list.__iter__()
-
-  def __len__(self):
-    self._UpdateTabList()
-    return len(self._tab_list)
-
-  def Get(self, index, ret):
-    """Returns self[index] if it exists, or ret if index is out of bounds.
-    """
-    self._UpdateTabList()
-    if len(self._tab_list) <= index:
-      return ret
-    debugger_url = self._tab_list[index]
-    # Lazily get/create a Tab object.
-    tab_object = self._tab_dict.get(debugger_url)
-    if not tab_object:
-      backend = inspector_backend.InspectorBackend(
-          self._browser_backend.browser,
-          self._browser_backend,
-          debugger_url)
-      tab_object = tab.Tab(backend)
-      self._tab_dict[debugger_url] = tab_object
-    return tab_object
-
-  def __getitem__(self, index):
-    tab_object = self.Get(index, None)
-    if tab_object is None:
-      raise IndexError('list index out of range')
-    return tab_object
-
-  def _ListTabs(self, timeout=None):
-    def _IsTab(context):
-      if 'type' in context:
-        return context['type'] == 'page'
-      # TODO: For compatibility with Chrome before r177683.
-      # This check is not completely correct, see crbug.com/190592.
-      return not context['url'].startswith('chrome-extension://')
-    data = self._browser_backend.Request('', timeout=timeout)
-    all_contexts = json.loads(data)
-    tabs = filter(_IsTab, all_contexts)
-    return tabs
-
-  def _UpdateTabList(self):
-    def GetDebuggerUrl(tab_info):
-      if 'webSocketDebuggerUrl' not in tab_info:
-        return None
-      return tab_info['webSocketDebuggerUrl']
-    new_tab_list = map(GetDebuggerUrl, self._ListTabs())
-    self._tab_list = [t for t in self._tab_list
-                      if t in self._tab_dict or t in new_tab_list]
-    self._tab_list += [t for t in new_tab_list
-                       if t is not None and t not in self._tab_list]
-
-  def _FindTabInfo(self, debugger_url):
-    for tab_info in self._ListTabs():
-      if tab_info.get('webSocketDebuggerUrl') == debugger_url:
-        return tab_info
-    return None
diff --git a/tools/telemetry/telemetry/core/chrome/trace_result.py b/tools/telemetry/telemetry/core/chrome/trace_result.py
deleted file mode 100644
index e2d851c..0000000
--- a/tools/telemetry/telemetry/core/chrome/trace_result.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-class TraceResult(object):
-  def __init__(self, impl):
-    self._impl = impl
-
-  def Serialize(self, f):
-    """Serializes the trace result to a file-like object"""
-    return self._impl.Serialize(f)
-
-  def AsTimelineModel(self):
-    """Parses the trace result into a timeline model for in-memory
-    manipulation."""
-    return self._impl.AsTimelineModel()
diff --git a/tools/telemetry/telemetry/core/chrome/tracing_backend.py b/tools/telemetry/telemetry/core/chrome/tracing_backend.py
deleted file mode 100644
index 0c02d2e..0000000
--- a/tools/telemetry/telemetry/core/chrome/tracing_backend.py
+++ /dev/null
@@ -1,132 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import cStringIO
-import json
-import logging
-import socket
-import threading
-
-
-from telemetry.core.chrome import trace_result
-from telemetry.core.chrome import websocket
-from telemetry.core.chrome import websocket_browser_connection as browser_conn
-from telemetry.core.timeline import model
-
-
-class TracingUnsupportedException(Exception):
-  pass
-
-# This class supports legacy format of trace presentation within DevTools
-# protocol, where trace data were sent as JSON-serialized strings. DevTools
-# now send the data as raw objects within the protocol message JSON, so there's
-# no need in extra de-serialization. We might want to remove this in the future.
-class TraceResultImpl(object):
-  def __init__(self, tracing_data):
-    self._tracing_data = tracing_data
-
-  def Serialize(self, f):
-    f.write('{"traceEvents": [')
-    d = self._tracing_data
-    # Note: we're not using ','.join here because the strings that are in the
-    # tracing data are typically many megabytes in size. In the fast case, f is
-    # just a file, so by skipping the in memory step we keep our memory
-    # footprint low and avoid additional processing.
-    if len(d) == 0:
-      pass
-    elif len(d) == 1:
-      f.write(d[0])
-    else:
-      f.write(d[0])
-      for i in range(1, len(d)):
-        f.write(',')
-        f.write(d[i])
-    f.write(']}')
-
-  def AsTimelineModel(self):
-    f = cStringIO.StringIO()
-    self.Serialize(f)
-    return model.TimelineModel(
-      event_data=f.getvalue(),
-      shift_world_to_zero=False)
-
-# RawTraceResultImpl differs from TraceResultImpl above in that
-# data are kept as a list of dicts, not strings.
-class RawTraceResultImpl(object):
-  def __init__(self, tracing_data):
-    self._tracing_data = tracing_data
-
-  def Serialize(self, f):
-    f.write('{"traceEvents":')
-    json.dump(self._tracing_data, f)
-    f.write('}')
-
-  def AsTimelineModel(self):
-    return model.TimelineModel(self._tracing_data)
-
-class TracingBackend(object):
-  def __init__(self, devtools_port):
-    self._conn = browser_conn.WebSocketBrowserConnection(devtools_port)
-    self._thread = None
-    self._tracing_data = []
-
-  def BeginTracing(self, custom_categories=None, timeout=10):
-    self._CheckNotificationSupported()
-    req = {'method': 'Tracing.start'}
-    if custom_categories:
-      req['params'] = {'categories': custom_categories}
-    self._conn.SendRequest(req, timeout)
-    # Tracing.start will send asynchronous notifications containing trace
-    # data, until Tracing.end is called.
-    self._thread = threading.Thread(target=self._TracingReader)
-    self._thread.start()
-
-  def EndTracing(self):
-    req = {'method': 'Tracing.end'}
-    self._conn.SendRequest(req)
-    self._thread.join()
-    self._thread = None
-
-  def GetTraceResultAndReset(self):
-    assert not self._thread
-    if self._tracing_data and type(self._tracing_data[0]) in [str, unicode]:
-      result_impl = TraceResultImpl(self._tracing_data)
-    else:
-      result_impl = RawTraceResultImpl(self._tracing_data)
-    self._tracing_data = []
-    return trace_result.TraceResult(result_impl)
-
-  def _TracingReader(self):
-    while self._conn.socket:
-      try:
-        data = self._conn.socket.recv()
-        if not data:
-          break
-        res = json.loads(data)
-        logging.debug('got [%s]', data)
-        if 'Tracing.dataCollected' == res.get('method'):
-          value = res.get('params', {}).get('value')
-          if type(value) in [str, unicode]:
-            self._tracing_data.append(value)
-          elif type(value) is list:
-            self._tracing_data.extend(value)
-          else:
-            logging.warning('Unexpected type in tracing data')
-        elif 'Tracing.tracingComplete' == res.get('method'):
-          break
-      except (socket.error, websocket.WebSocketException):
-        logging.warning('Timeout waiting for tracing response, unusual.')
-
-  def Close(self):
-    self._conn.Close()
-
-  def _CheckNotificationSupported(self):
-    """Ensures we're running against a compatible version of chrome."""
-    req = {'method': 'Tracing.hasCompleted'}
-    res = self._conn.SyncRequest(req)
-    if res.get('response'):
-      raise TracingUnsupportedException(
-          'Tracing not supported for this browser')
-    elif 'error' in res:
-      return
diff --git a/tools/telemetry/telemetry/core/chrome/tracing_backend_unittest.py b/tools/telemetry/telemetry/core/chrome/tracing_backend_unittest.py
deleted file mode 100644
index f383078..0000000
--- a/tools/telemetry/telemetry/core/chrome/tracing_backend_unittest.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import cStringIO
-import json
-import logging
-import unittest
-
-from telemetry.core import util
-from telemetry.core.chrome import tracing_backend
-from telemetry.unittest import tab_test_case
-
-
-class TracingBackendTest(tab_test_case.TabTestCase):
-  def _StartServer(self):
-    self._browser.SetHTTPServerDirectories(util.GetUnittestDataDir())
-
-  def _WaitForAnimationFrame(self):
-    def _IsDone():
-      js_is_done = """done"""
-      return bool(self._tab.EvaluateJavaScript(js_is_done))
-    util.WaitFor(_IsDone, 5)
-
-  def testGotTrace(self):
-    if not self._browser.supports_tracing:
-      logging.warning('Browser does not support tracing, skipping test.')
-      return
-    self._StartServer()
-    self._browser.StartTracing()
-    self._browser.StopTracing()
-
-    # TODO(tengs): check model for correctness after trace_event_importer
-    # is implemented (crbug.com/173327).
-
-
-class TracingResultImplTest(unittest.TestCase):
-  # Override TestCase.run to run a test with all possible
-  # implementations of TraceResult.
-  def __init__(self, method_name):
-    self._traceResultImplClass = None
-    super(TracingResultImplTest, self).__init__(method_name)
-
-  def run(self, result=None):
-    def RawTraceResultImplWrapper(strings):
-      return tracing_backend.RawTraceResultImpl(map(json.loads, strings))
-    classes = [
-        tracing_backend.TraceResultImpl,
-        RawTraceResultImplWrapper
-    ]
-    for cls in classes:
-      self._traceResultImplClass = cls
-      super(TracingResultImplTest, self).run(result)
-
-  def testWrite1(self):
-    ri = self._traceResultImplClass([])
-    f = cStringIO.StringIO()
-    ri.Serialize(f)
-    v = f.getvalue()
-
-    j = json.loads(v)
-    assert 'traceEvents' in j
-    self.assertEquals(j['traceEvents'], [])
-
-  def testWrite2(self):
-    ri = self._traceResultImplClass([
-        '"foo"',
-        '"bar"'])
-    f = cStringIO.StringIO()
-    ri.Serialize(f)
-    v = f.getvalue()
-
-    j = json.loads(v)
-    assert 'traceEvents' in j
-    self.assertEquals(j['traceEvents'], ['foo', 'bar'])
-
-  def testWrite3(self):
-    ri = self._traceResultImplClass([
-        '"foo"',
-        '"bar"',
-        '"baz"'])
-    f = cStringIO.StringIO()
-    ri.Serialize(f)
-    v = f.getvalue()
-
-    j = json.loads(v)
-    assert 'traceEvents' in j
-    self.assertEquals(j['traceEvents'],
-                      ['foo', 'bar', 'baz'])
diff --git a/tools/telemetry/telemetry/core/chrome/websocket.py b/tools/telemetry/telemetry/core/chrome/websocket.py
deleted file mode 100644
index 55f3fc0..0000000
--- a/tools/telemetry/telemetry/core/chrome/websocket.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-from __future__ import absolute_import
-
-from telemetry.core import util
-
-util.AddDirToPythonPath(
-    util.GetTelemetryDir(), 'third_party', 'websocket-client')
-from websocket import create_connection  # pylint: disable=W0611
-from websocket import WebSocketException  # pylint: disable=W0611
diff --git a/tools/telemetry/telemetry/core/chrome/websocket_browser_connection.py b/tools/telemetry/telemetry/core/chrome/websocket_browser_connection.py
deleted file mode 100644
index efaba9c..0000000
--- a/tools/telemetry/telemetry/core/chrome/websocket_browser_connection.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import json
-import logging
-import socket
-
-from telemetry.core import util
-from telemetry.core.chrome import websocket
-
-class WebSocketBrowserConnection(object):
-  """Represents a websocket connection to the browser for backends
-     which use one."""
-
-  def __init__(self, devtools_port):
-    debugger_url = 'ws://localhost:%i/devtools/browser' % devtools_port
-    self._socket = websocket.create_connection(debugger_url)
-    self._next_request_id = 0
-    self._cur_socket_timeout = 0
-
-  def Close(self):
-    if self._socket:
-      self._socket.close()
-      self._socket = None
-
-  def SendRequest(self, req, timeout=10):
-    self._SetTimeout(timeout)
-    req['id'] = self._next_request_id
-    self._next_request_id += 1
-    data = json.dumps(req)
-    logging.debug('will send [%s]', data)
-    self._socket.send(data)
-
-  def SyncRequest(self, req, timeout=10):
-    self.SendRequest(req, timeout)
-    while True:
-      try:
-        data = self._socket.recv()
-      except (socket.error, websocket.WebSocketException):
-        raise util.TimeoutException(
-            "Timed out waiting for reply. This is unusual.")
-      res = json.loads(data)
-      logging.debug('got [%s]', data)
-      if res['id'] != req['id']:
-        logging.debug('Dropped reply: %s', json.dumps(res))
-        continue
-      return res
-
-  @property
-  def socket(self):
-    """Returns the socket for raw access. Please be sure you know what
-       you are doing."""
-    return self._socket
-
-  def _SetTimeout(self, timeout):
-    if self._cur_socket_timeout != timeout:
-      self._socket.settimeout(timeout)
-      self._cur_socket_timeout = timeout
diff --git a/tools/telemetry/telemetry/core/chrome/websocket_unittest.py b/tools/telemetry/telemetry/core/chrome/websocket_unittest.py
deleted file mode 100644
index e1418e8..0000000
--- a/tools/telemetry/telemetry/core/chrome/websocket_unittest.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-import unittest
-
-from telemetry.core.chrome import websocket
-
-class TestWebSocket(unittest.TestCase):
-  def testExports(self):
-    self.assertNotEqual(websocket.create_connection, None)
diff --git a/tools/telemetry/telemetry/core/extension_to_load.py b/tools/telemetry/telemetry/core/extension_to_load.py
index b5c5000..85244db 100644
--- a/tools/telemetry/telemetry/core/extension_to_load.py
+++ b/tools/telemetry/telemetry/core/extension_to_load.py
@@ -3,7 +3,7 @@
 # found in the LICENSE file.
 import os
 
-from telemetry.core.chrome import crx_id
+from telemetry.core.backends.chrome import crx_id
 
 class ExtensionPathNonExistentException(Exception):
   pass
diff --git a/tools/telemetry/telemetry/core/extension_unittest.py b/tools/telemetry/telemetry/core/extension_unittest.py
index 986139e..b15fd99 100644
--- a/tools/telemetry/telemetry/core/extension_unittest.py
+++ b/tools/telemetry/telemetry/core/extension_unittest.py
@@ -1,6 +1,7 @@
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
+
 import logging
 import os
 import shutil
@@ -10,9 +11,10 @@
 from telemetry.core import browser_finder
 from telemetry.core import extension_to_load
 from telemetry.core import util
-from telemetry.core.chrome import extension_dict_backend
+from telemetry.core.backends.chrome import extension_dict_backend
 from telemetry.unittest import options_for_unittests
 
+
 class ExtensionTest(unittest.TestCase):
   def setUp(self):
     extension_path = os.path.join(util.GetUnittestDataDir(), 'simple_extension')
@@ -126,6 +128,7 @@
       extension.ExecuteJavaScript('setTestVar("abcdef")')
       self.assertEquals('abcdef', extension.EvaluateJavaScript('_testVar'))
 
+
 class ComponentExtensionTest(unittest.TestCase):
   def testComponentExtensionBasic(self):
     extension_path = os.path.join(
diff --git a/tools/telemetry/telemetry/core/platform/profiler/android_memreport_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/android_memreport_profiler.py
new file mode 100644
index 0000000..5d98ada
--- /dev/null
+++ b/tools/telemetry/telemetry/core/platform/profiler/android_memreport_profiler.py
@@ -0,0 +1,41 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import subprocess
+
+from telemetry.core import util
+from telemetry.core.backends.chrome import android_browser_finder
+from telemetry.core.platform import profiler
+
+class AndroidMemReportProfiler(profiler.Profiler):
+  """Android-specific, collects 'memreport' graphs."""
+
+  def __init__(self, browser_backend, platform_backend, output_path):
+    super(AndroidMemReportProfiler, self).__init__(
+        browser_backend, platform_backend, output_path)
+    self._html_file = output_path + '.html'
+    self._memreport = subprocess.Popen(
+        [os.path.join(util.GetChromiumSrcDir(),
+                      'tools', 'android', 'memdump', 'memreport.py'),
+         '--manual-graph', '--package', browser_backend.package],
+         stdout=file(self._html_file, 'w'),
+         stdin=subprocess.PIPE)
+
+  @classmethod
+  def name(cls):
+    return 'android-memreport'
+
+  @classmethod
+  def is_supported(cls, options):
+    if not options:
+      return android_browser_finder.CanFindAvailableBrowsers()
+    return options.browser_type.startswith('android')
+
+  def CollectProfile(self):
+    self._memreport.communicate(input='\n')
+    self._memreport.wait()
+    print 'To view the memory report, open:'
+    print self._html_file
+    return [self._html_file]
diff --git a/tools/telemetry/telemetry/core/platform/profiler/java_heap_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/java_heap_profiler.py
index efc9b64..c8d62a9 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/java_heap_profiler.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/java_heap_profiler.py
@@ -7,7 +7,7 @@
 import threading
 
 from telemetry.core import util
-from telemetry.core.chrome import android_browser_finder
+from telemetry.core.backends.chrome import android_browser_finder
 from telemetry.core.platform import profiler
 
 class JavaHeapProfiler(profiler.Profiler):
diff --git a/tools/telemetry/telemetry/core/platform/profiler/tcmalloc_heap_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/tcmalloc_heap_profiler.py
index 95b6a8f..abda0c1 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/tcmalloc_heap_profiler.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/tcmalloc_heap_profiler.py
@@ -6,7 +6,7 @@
 import os
 import sys
 
-from telemetry.core.chrome import android_browser_finder
+from telemetry.core.backends.chrome import android_browser_finder
 from telemetry.core.platform import profiler
 
 # Enviroment variables to (android properties, default value) mapping.
diff --git a/tools/telemetry/telemetry/core/platform/profiler/tcpdump_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/tcpdump_profiler.py
index 540c2b6..97e2ac5 100644
--- a/tools/telemetry/telemetry/core/platform/profiler/tcpdump_profiler.py
+++ b/tools/telemetry/telemetry/core/platform/profiler/tcpdump_profiler.py
@@ -8,7 +8,7 @@
 import sys
 import tempfile
 
-from telemetry.core.chrome import android_browser_finder
+from telemetry.core.backends.chrome import android_browser_finder
 from telemetry.core.platform import profiler
 
 
diff --git a/tools/telemetry/telemetry/core/possible_browser.py b/tools/telemetry/telemetry/core/possible_browser.py
index 3c55c22..3907f63 100644
--- a/tools/telemetry/telemetry/core/possible_browser.py
+++ b/tools/telemetry/telemetry/core/possible_browser.py
@@ -7,9 +7,9 @@
   Call Create() to launch the browser and begin manipulating it..
   """
 
-  def __init__(self, browser_type, options):
+  def __init__(self, browser_type, finder_options):
     self._browser_type = browser_type
-    self._options = options
+    self._finder_options = finder_options
 
   def __repr__(self):
     return 'PossibleBrowser(browser_type=%s)' % self.browser_type
@@ -19,12 +19,12 @@
     return self._browser_type
 
   @property
-  def options(self):
-    return self._options
+  def finder_options(self):
+    return self._finder_options
 
   def Create(self):
     raise NotImplementedError()
 
-  def SupportsOptions(self, options):
+  def SupportsOptions(self, finder_options):
     """Tests for extension support."""
     raise NotImplementedError()
diff --git a/tools/telemetry/telemetry/core/profile_creator.py b/tools/telemetry/telemetry/core/profile_creator.py
deleted file mode 100644
index 29a2cbb..0000000
--- a/tools/telemetry/telemetry/core/profile_creator.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright (c) 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.
-
-class ProfileCreator(object):
-  """Base class for an object that constructs a Chrome profile."""
-
-  def __init__(self, browser):
-    self._browser = browser
-
-  def CreateProfile(self):
-    """Fill in the profile in question."""
-    raise NotImplementedError
diff --git a/tools/telemetry/telemetry/core/profile_types.py b/tools/telemetry/telemetry/core/profile_types.py
index 6ea4ee3..67024eb 100644
--- a/tools/telemetry/telemetry/core/profile_types.py
+++ b/tools/telemetry/telemetry/core/profile_types.py
@@ -3,70 +3,79 @@
 # found in the LICENSE file.
 
 import os
+import tempfile
 
-from telemetry.core import discover
-from telemetry.core import profile_creator
 from telemetry.core import util
 
-BASE_PROFILE_TYPES = ['clean', 'default']
 
-PROFILE_CREATORS = {}
+BASE_PROFILE_TYPES = ['clean', 'default']
 
 PROFILE_TYPE_MAPPING = {
   'typical_user': 'chrome/test/data/extensions/profiles/content_scripts1',
   'power_user': 'chrome/test/data/extensions/profiles/extension_webrequest',
 }
 
-def _DiscoverCreateableProfiles(profile_creators_dir, base_dir):
-  """Returns a dictionary of all the profile creators we can use to create
-  a Chrome profile for testing located in |profile_creators_dir|.
-  The returned value consists of 'class_name' -> 'test class' dictionary where
-  class_name is the name of the class with the _creator suffix removed e.g.
-  'small_profile_creator will be 'small_profile'.
-  """
-  profile_creators_unfiltered = discover.DiscoverClasses(
-      profile_creators_dir, base_dir, profile_creator.ProfileCreator)
+GENERATED_PROFILE_TYPES = []
 
-  # Remove '_creator' suffix from keys.
-  profile_creators = {}
-  for test_name, test_class in profile_creators_unfiltered.iteritems():
-    assert test_name.endswith('_creator')
-    test_name = test_name[:-len('_creator')]
-    profile_creators[test_name] = test_class
+def _GetFirstExistingBuildDir():
+  # Look for the first build directory that exists, this is a bit weird because
+  # if a developer machine has both Debug and Release build directories this
+  # will return the first one, but on the bots this should not be an issue.
+  chrome_root = util.GetChromiumSrcDir()
+  for build_dir, build_type in util.GetBuildDirectories():
+    candidate = os.path.join(chrome_root, build_dir, build_type)
+    if os.path.isdir(candidate):
+      return candidate
+  return None
 
-  return profile_creators
+def GetGeneratedProfilesDir():
+  build_directory = _GetFirstExistingBuildDir()
+  if not build_directory:
+    build_directory = tempfile.gettempdir()
 
-def ClearProfieCreatorsForTests():
-  """Clears the discovered profile creator objects.  Used for unit tests."""
-  PROFILE_CREATORS.clear()
+  return os.path.abspath(os.path.join(build_directory, 'generated_profiles'))
 
-def FindProfileCreators(profile_creators_dir, base_dir):
-  """Discover all the ProfileCreator objects in |profile_creators_dir|."""
-  assert not PROFILE_CREATORS  # It's illegal to call this function twice.
-  PROFILE_CREATORS.update(_DiscoverCreateableProfiles(
-      profile_creators_dir, base_dir))
+def ScanForGeneratedProfiles():
+  # It's illegal to call this function twice.
+  assert not GENERATED_PROFILE_TYPES
+
+  generated_profiles_dir = GetGeneratedProfilesDir()
+  if not os.path.exists(generated_profiles_dir):
+    return
+
+  # Get list of subdirectories which are assumed to be generated profiles.
+  subdirs = os.listdir(generated_profiles_dir)
+  subdirs = filter(
+      lambda d:os.path.isdir(os.path.join(generated_profiles_dir, d)), subdirs)
+
+  # Make sure we don't have any conflics with hard coded profile names.
+  hardcoded_profile_names = BASE_PROFILE_TYPES + PROFILE_TYPE_MAPPING.keys()
+  for d in subdirs:
+    if d in hardcoded_profile_names:
+      raise Exception('Conflicting generated profile name: %s.' % d)
+
+  GENERATED_PROFILE_TYPES.extend(subdirs)
 
 def GetProfileTypes():
   """Returns a list of all command line options that can be specified for
   profile type."""
-  return (BASE_PROFILE_TYPES + PROFILE_TYPE_MAPPING.keys() +
-      PROFILE_CREATORS.keys())
+  return BASE_PROFILE_TYPES + PROFILE_TYPE_MAPPING.keys() + \
+      GENERATED_PROFILE_TYPES
 
 def GetProfileDir(profile_type):
   """Given a |profile_type| (as returned by GetProfileTypes()), return the
-  directory to use for that profile or None if the profile needs to be generated
-  or doesn't need a profile directory (e.g. using the browser default profile).
+  directory to use for that profile or None if the profile doesn't need a
+  profile directory (e.g. using the browser default profile).
   """
-  if (profile_type in BASE_PROFILE_TYPES or
-      profile_type in PROFILE_CREATORS):
+  if profile_type in BASE_PROFILE_TYPES:
     return None
 
-  path = os.path.join(
-    util.GetChromiumSrcDir(), *PROFILE_TYPE_MAPPING[profile_type].split('/'))
+  if profile_type in PROFILE_TYPE_MAPPING.keys():
+    path = os.path.join(
+      util.GetChromiumSrcDir(), *PROFILE_TYPE_MAPPING[profile_type].split('/'))
+  else:
+    # Generated profile.
+    path = os.path.join(GetGeneratedProfilesDir(), profile_type)
+
   assert os.path.exists(path)
   return path
-
-def GetProfileCreator(profile_type):
-  """Returns the profile creator object corresponding to the |profile_type|
-  string."""
-  return PROFILE_CREATORS.get(profile_type)
diff --git a/tools/telemetry/telemetry/core/profile_types_unittest.py b/tools/telemetry/telemetry/core/profile_types_unittest.py
index 3dcab2e..764e2f2 100644
--- a/tools/telemetry/telemetry/core/profile_types_unittest.py
+++ b/tools/telemetry/telemetry/core/profile_types_unittest.py
@@ -1,11 +1,10 @@
 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-import os
 import unittest
 
 from telemetry.core import profile_types
-from telemetry.core import util
+
 
 class ProfileTypesTest(unittest.TestCase):
   def testGetProfileTypes(self):
@@ -16,17 +15,3 @@
 
   def testGetProfileDir(self):
     self.assertFalse(profile_types.GetProfileDir('typical_user') is None)
-
-  def testGetProfileCreatorTypes(self):
-    profile_creators_dir = os.path.join(
-        util.GetUnittestDataDir(), 'discoverable_classes')
-    base_dir = util.GetUnittestDataDir()
-
-    profile_types.FindProfileCreators(profile_creators_dir, base_dir)
-    types = profile_types.GetProfileTypes()
-    self.assertTrue(len(types) > 0)
-    self.assertTrue('dummy_profile' in types)
-
-    dummy_profile_creator = profile_types.GetProfileCreator('dummy_profile')
-    self.assertTrue(dummy_profile_creator.__name__ == 'DummyProfileCreator')
-    profile_types.ClearProfieCreatorsForTests()
diff --git a/tools/telemetry/telemetry/core/tab_unittest.py b/tools/telemetry/telemetry/core/tab_unittest.py
index 7c356bf..f555dc2 100644
--- a/tools/telemetry/telemetry/core/tab_unittest.py
+++ b/tools/telemetry/telemetry/core/tab_unittest.py
@@ -8,6 +8,7 @@
 from telemetry.core import util
 from telemetry.core import exceptions
 from telemetry.unittest import tab_test_case
+from telemetry.unittest import DisabledTest
 
 
 def _IsDocumentVisible(tab):
@@ -55,6 +56,7 @@
     self._extra_browser_args = ['--enable-gpu-benchmarking']
     super(GpuTabTest, self).setUp()
 
+  @DisabledTest
   def testScreenshot(self):
     if not self._tab.screenshot_supported:
       logging.warning('Browser does not support screenshots, skipping test.')
diff --git a/tools/telemetry/telemetry/page/actions/click_element.py b/tools/telemetry/telemetry/page/actions/click_element.py
index 8d4aa79..7ff2dc4 100644
--- a/tools/telemetry/telemetry/page/actions/click_element.py
+++ b/tools/telemetry/telemetry/page/actions/click_element.py
@@ -1,9 +1,11 @@
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
+
+import re
+
 from telemetry.core import util
 from telemetry.core import exceptions
-from telemetry.page import page as page_module
 from telemetry.page.actions import page_action
 
 class ClickElementAction(page_action.PageAction):
@@ -12,7 +14,6 @@
 
   def RunAction(self, page, tab, previous_action):
     def DoClick():
-      assert hasattr(self, 'selector') or hasattr(self, 'text')
       if hasattr(self, 'selector'):
         code = 'document.querySelector(\'' + self.selector + '\').click();'
         try:
@@ -20,23 +21,28 @@
         except exceptions.EvaluateException:
           raise page_action.PageActionFailed(
               'Cannot find element with selector ' + self.selector)
-      else:
+      elif hasattr(self, 'text'):
         callback_code = 'function(element) { element.click(); }'
         try:
           util.FindElementAndPerformAction(tab, self.text, callback_code)
         except exceptions.EvaluateException:
           raise page_action.PageActionFailed(
               'Cannot find element with text ' + self.text)
+      elif hasattr(self, 'xpath'):
+        code = ('document.evaluate("%s",'
+                                   'document,'
+                                   'null,'
+                                   'XPathResult.FIRST_ORDERED_NODE_TYPE,'
+                                   'null)'
+                  '.singleNodeValue.click()' % re.escape(self.xpath))
+        try:
+          tab.ExecuteJavaScript(code)
+        except exceptions.EvaluateException:
+          raise page_action.PageActionFailed(
+              'Cannot find element with xpath ' + self.xpath)
+      else:
+        raise page_action.PageActionFailed(
+            'No condition given to click_element')
 
-    if hasattr(self, 'wait_for_navigate'):
-      tab.PerformActionAndWaitForNavigate(DoClick)
-    elif hasattr(self, 'wait_for_href_change'):
-      old_url = tab.EvaluateJavaScript('document.location.href')
-      DoClick()
-      util.WaitFor(lambda: tab.EvaluateJavaScript(
-          'document.location.href') != old_url, 60)
-    else:
-      DoClick()
-
-    page_module.Page.WaitForPageToLoad(self, tab, 60)
+    DoClick()
     tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
diff --git a/tools/telemetry/telemetry/page/actions/click_element_unittest.py b/tools/telemetry/telemetry/page/actions/click_element_unittest.py
index a32baec..e0eec82 100644
--- a/tools/telemetry/telemetry/page/actions/click_element_unittest.py
+++ b/tools/telemetry/telemetry/page/actions/click_element_unittest.py
@@ -4,6 +4,7 @@
 
 from telemetry.core import util
 from telemetry.page.actions import click_element
+from telemetry.page.actions import wait
 from telemetry.unittest import tab_test_case
 
 class ClickElementActionTest(tab_test_case.TabTestCase):
@@ -16,9 +17,11 @@
         self._tab.EvaluateJavaScript('document.location.pathname;'),
         '/page_with_link.html')
 
-    data = {'selector': 'a[id="clickme"]', 'wait_for_href_change': True}
+    data = {'selector': 'a[id="clickme"]'}
     i = click_element.ClickElementAction(data)
-    i.RunAction(None, self._tab, None)
+    data = {'condition': 'href_change'}
+    j = wait.WaitAction(data)
+    j.RunAction(None, self._tab, i)
 
     self.assertEquals(
         self._tab.EvaluateJavaScript('document.location.pathname;'),
@@ -33,9 +36,30 @@
         self._tab.EvaluateJavaScript('document.location.pathname;'),
         '/page_with_link.html')
 
-    data = {'text': 'Click me', 'wait_for_href_change': True}
+    data = {'text': 'Click me'}
     i = click_element.ClickElementAction(data)
-    i.RunAction(None, self._tab, None)
+    data = {'condition': 'href_change'}
+    j = wait.WaitAction(data)
+    j.RunAction(None, self._tab, i)
+
+    self.assertEquals(
+        self._tab.EvaluateJavaScript('document.location.pathname;'),
+        '/blank.html')
+
+  def testClickWithXPathWaitForRefChange(self):
+    self._browser.SetHTTPServerDirectories(util.GetUnittestDataDir())
+    self._tab.Navigate(
+      self._browser.http_server.UrlOf('page_with_link.html'))
+    self._tab.WaitForDocumentReadyStateToBeComplete()
+    self.assertEquals(
+        self._tab.EvaluateJavaScript('document.location.pathname;'),
+        '/page_with_link.html')
+
+    data = {'xpath': '//a[@id="clickme"]'}
+    i = click_element.ClickElementAction(data)
+    data = {'condition': 'href_change'}
+    j = wait.WaitAction(data)
+    j.RunAction(None, self._tab, i)
 
     self.assertEquals(
         self._tab.EvaluateJavaScript('document.location.pathname;'),
diff --git a/tools/telemetry/telemetry/page/actions/play_unittest.py b/tools/telemetry/telemetry/page/actions/play_unittest.py
index a20a6de..37ed425 100644
--- a/tools/telemetry/telemetry/page/actions/play_unittest.py
+++ b/tools/telemetry/telemetry/page/actions/play_unittest.py
@@ -59,7 +59,7 @@
     self.assertTrue(self._tab.EvaluateJavaScript(AUDIO_1_PLAYING_CHECK))
 
   # http://crbug.com/273887
-  def DISABLED_testPlayWaitForPlayTimeout(self):
+  def testPlayWaitForPlayTimeout(self):
     """Tests that wait_for_playing timeouts if video does not play."""
     data = {'selector': '#video_1',
             'wait_for_playing': True,
diff --git a/tools/telemetry/telemetry/page/actions/reload.py b/tools/telemetry/telemetry/page/actions/reload.py
index caa362e..c98853c 100644
--- a/tools/telemetry/telemetry/page/actions/reload.py
+++ b/tools/telemetry/telemetry/page/actions/reload.py
@@ -1,7 +1,7 @@
 # Copyright (c) 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.
-from telemetry.page import page as page_module
+
 from telemetry.page.actions import page_action
 
 class ReloadAction(page_action.PageAction):
@@ -10,5 +10,4 @@
 
   def RunAction(self, page, tab, previous_action):
     tab.ExecuteJavaScript('window.location.reload()')
-    page_module.Page.WaitForPageToLoad(self, tab, 60)
     tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
diff --git a/tools/telemetry/telemetry/page/actions/seek_unittest.py b/tools/telemetry/telemetry/page/actions/seek_unittest.py
index b14dda7..e3a3286 100644
--- a/tools/telemetry/telemetry/page/actions/seek_unittest.py
+++ b/tools/telemetry/telemetry/page/actions/seek_unittest.py
@@ -55,7 +55,7 @@
     self.assertTrue(self._tab.EvaluateJavaScript(AUDIO_1_SEEKED_CHECK))
 
   # http://crbug.com/273887
-  def DISABLED_testSeekWaitForSeekTimeout(self):
+  def testSeekWaitForSeekTimeout(self):
     """Tests that wait_for_seeked timeouts if video does not seek."""
     data = {'selector': '#video_1',
             'wait_for_seeked': True,
diff --git a/tools/telemetry/telemetry/page/actions/tap_element.py b/tools/telemetry/telemetry/page/actions/tap_element.py
index f3a4734..f8c669e 100644
--- a/tools/telemetry/telemetry/page/actions/tap_element.py
+++ b/tools/telemetry/telemetry/page/actions/tap_element.py
@@ -4,7 +4,6 @@
 
 from telemetry.core import exceptions
 from telemetry.core import util
-from telemetry.page import page as page_module
 from telemetry.page.actions import page_action
 
 class TapElementAction(page_action.PageAction):
@@ -41,5 +40,3 @@
           'window.__tap_event_finished'), 60)
     else:
       DoTap()
-
-    page_module.Page.WaitForPageToLoad(self, tab, 60)
diff --git a/tools/telemetry/telemetry/page/actions/wait.py b/tools/telemetry/telemetry/page/actions/wait.py
index 2210a2d..861c916 100644
--- a/tools/telemetry/telemetry/page/actions/wait.py
+++ b/tools/telemetry/telemetry/page/actions/wait.py
@@ -14,17 +14,14 @@
     super(WaitAction, self).__init__(attributes)
 
   def RunsPreviousAction(self):
-    assert hasattr(self, 'condition')
-    return self.condition == 'navigate' or self.condition == 'href_change'
+    return (getattr(self, 'condition', None) == 'navigate' or
+            getattr(self, 'condition', None) == 'href_change')
 
   def RunAction(self, page, tab, previous_action):
-    assert hasattr(self, 'condition')
-
-    if self.condition == 'duration':
-      assert hasattr(self, 'seconds')
+    if hasattr(self, 'seconds'):
       time.sleep(self.seconds)
 
-    elif self.condition == 'navigate':
+    elif getattr(self, 'condition', None) == 'navigate':
       if not previous_action:
         raise page_action.PageActionFailed('You need to perform an action '
                                            'before waiting for navigate.')
@@ -32,7 +29,7 @@
       action_to_perform = lambda: previous_action.RunAction(page, tab, None)
       tab.PerformActionAndWaitForNavigate(action_to_perform, self.timeout)
 
-    elif self.condition == 'href_change':
+    elif getattr(self, 'condition', None) == 'href_change':
       if not previous_action:
         raise page_action.PageActionFailed('You need to perform an action '
                                            'before waiting for a href change.')
@@ -42,19 +39,30 @@
       util.WaitFor(lambda: tab.EvaluateJavaScript(
           'document.location.href') != old_url, self.timeout)
 
-    elif self.condition == 'element':
-      assert hasattr(self, 'text') or hasattr(self, 'selector')
+    elif getattr(self, 'condition', None) == 'element':
       if hasattr(self, 'text'):
         callback_code = 'function(element) { return element != null; }'
         util.WaitFor(
             lambda: util.FindElementAndPerformAction(
                 tab, self.text, callback_code), self.timeout)
-      else:
+      elif hasattr(self, 'selector'):
         util.WaitFor(lambda: tab.EvaluateJavaScript(
              'document.querySelector("%s") != null' % re.escape(self.selector)),
              self.timeout)
-
-    elif self.condition == 'javascript':
-      assert hasattr(self, 'javascript')
+      elif hasattr(self, 'xpath'):
+        code = ('document.evaluate("%s",'
+                                   'document,'
+                                   'null,'
+                                   'XPathResult.FIRST_ORDERED_NODE_TYPE,'
+                                   'null)'
+                  '.singleNodeValue' % re.escape(self.xpath))
+        util.WaitFor(lambda: tab.EvaluateJavaScript('%s != null' % code),
+             self.timeout)
+      else:
+        raise page_action.PageActionFailed(
+            'No element condition given to wait')
+    elif hasattr(self, 'javascript'):
       util.WaitFor(lambda: tab.EvaluateJavaScript(self.javascript),
                    self.timeout)
+    else:
+      raise page_action.PageActionFailed('No wait condition found')
diff --git a/tools/telemetry/telemetry/page/cloud_storage.py b/tools/telemetry/telemetry/page/cloud_storage.py
index 1188252..bf08ee6 100644
--- a/tools/telemetry/telemetry/page/cloud_storage.py
+++ b/tools/telemetry/telemetry/page/cloud_storage.py
@@ -17,6 +17,7 @@
 
 
 DEFAULT_BUCKET = 'chromium-wpr'
+CHROMIUM_TELEMETRY_BUCKET = 'chromium-telemetry'
 
 
 _GSUTIL_URL = 'http://storage.googleapis.com/pub/gsutil.tar.gz'
@@ -27,12 +28,24 @@
   pass
 
 
-class CredentialsError(CloudStorageError):
+class PermissionError(CloudStorageError):
+  def __init__(self, gsutil_path):
+    super(PermissionError, self).__init__(
+        'Attempted to access a file from Cloud Storage but you don\'t '
+        'have permission. ' + self._GetConfigInstructions(gsutil_path))
+
+  @staticmethod
+  def _GetConfigInstructions(gsutil_path):
+    return ('Run "%s config" to configure your credentials. '
+        'If you have a @google.com account, use that one. '
+        'The project-id field can be left blank.' % gsutil_path)
+
+
+class CredentialsError(PermissionError):
   def __init__(self, gsutil_path):
     super(CredentialsError, self).__init__(
-        'Attempted to download a file from Cloud Storage but you have no '
-        'configured credentials. Run "%s config" to configure your '
-        'credentials. The project-id field can be left blank.' % gsutil_path)
+        'Attempted to access a file from Cloud Storage but you have no '
+        'configured credentials. ' + self._GetConfigInstructions(gsutil_path))
 
 
 class NotFoundError(CloudStorageError):
@@ -76,9 +89,11 @@
   stdout, stderr = gsutil.communicate()
 
   if gsutil.returncode:
-    if ('You are attempting to access protected data with '
-        'no configured credentials.' in stderr):
+    if stderr.startswith('You are attempting to access protected data with '
+        'no configured credentials.'):
       raise CredentialsError(gsutil_path)
+    if 'status=403' in stderr:
+      raise PermissionError(gsutil_path)
     if stderr.startswith('InvalidUriError') or 'No such object' in stderr:
       raise NotFoundError(stderr)
     raise CloudStorageError(stderr)
diff --git a/tools/telemetry/telemetry/page/page.py b/tools/telemetry/telemetry/page/page.py
index 5d06076..145f036 100644
--- a/tools/telemetry/telemetry/page/page.py
+++ b/tools/telemetry/telemetry/page/page.py
@@ -3,10 +3,8 @@
 # found in the LICENSE file.
 import os
 import re
-import time
 import urlparse
 
-from telemetry.core import util
 
 def _UrlPathJoin(*args):
   """Joins each path in |args| for insertion into a URL path.
@@ -111,30 +109,3 @@
 
   def __str__(self):
     return self.url
-
-  def WaitToLoad(self, tab, timeout, poll_interval=0.1):
-    Page.WaitForPageToLoad(self, tab, timeout, poll_interval)
-
-  # TODO(dtu): Remove this method when no page sets use a click interaction
-  # with a wait condition. crbug.com/168431
-  @staticmethod
-  def WaitForPageToLoad(obj, tab, timeout, poll_interval=0.1):
-    """Waits for various wait conditions present in obj."""
-    if hasattr(obj, 'wait_seconds'):
-      time.sleep(obj.wait_seconds)
-    if hasattr(obj, 'wait_for_element_with_text'):
-      callback_code = 'function(element) { return element != null; }'
-      util.WaitFor(
-          lambda: util.FindElementAndPerformAction(
-              tab, obj.wait_for_element_with_text, callback_code),
-          timeout, poll_interval)
-    if hasattr(obj, 'wait_for_element_with_selector'):
-      util.WaitFor(lambda: tab.EvaluateJavaScript(
-           'document.querySelector(\'' + obj.wait_for_element_with_selector +
-           '\') != null'), timeout, poll_interval)
-    if hasattr(obj, 'post_navigate_javascript_to_execute'):
-      tab.EvaluateJavaScript(obj.post_navigate_javascript_to_execute)
-    if hasattr(obj, 'wait_for_javascript_expression'):
-      util.WaitFor(
-          lambda: tab.EvaluateJavaScript(obj.wait_for_javascript_expression),
-          timeout, poll_interval)
diff --git a/tools/telemetry/telemetry/page/page_runner.py b/tools/telemetry/telemetry/page/page_runner.py
index e622072..de0ebff 100644
--- a/tools/telemetry/telemetry/page/page_runner.py
+++ b/tools/telemetry/telemetry/page/page_runner.py
@@ -95,13 +95,13 @@
       # not overwrite it.
       self._append_to_existing_wpr = True
 
-  def StartProfiling(self, page, options):
+  def StartProfiling(self, page, finder_options):
     if not self.profiler_dir:
       self.profiler_dir = tempfile.mkdtemp()
     output_file = os.path.join(self.profiler_dir, page.url_as_file_safe_name)
-    if options.repeat_options.IsRepeating():
+    if finder_options.repeat_options.IsRepeating():
       output_file = _GetSequentialFileName(output_file)
-    self.browser.StartProfiling(options.profiler, output_file)
+    self.browser.StartProfiling(finder_options.profiler, output_file)
 
   def StopProfiling(self):
     self.browser.StopProfiling()
@@ -138,7 +138,6 @@
     else:
       i = navigate.NavigateAction()
       i.RunAction(page, tab, None)
-      page.WaitToLoad(tab, 60)
 
   def CleanUpPage(self, page, tab):
     if page.credentials and self._did_login:
@@ -162,13 +161,13 @@
   logging.warning('%s%s', title, stack_trace)
 
 
-def _PrepareAndRunPage(test, page_set, expectations, options, page,
+def _PrepareAndRunPage(test, page_set, expectations, finder_options, page,
                        credentials_path, possible_browser, results, state):
-  if options.wpr_mode != wpr_modes.WPR_RECORD:
+  if finder_options.wpr_mode != wpr_modes.WPR_RECORD:
     if page.archive_path and os.path.isfile(page.archive_path):
-      possible_browser.options.wpr_mode = wpr_modes.WPR_REPLAY
+      possible_browser.finder_options.wpr_mode = wpr_modes.WPR_REPLAY
     else:
-      possible_browser.options.wpr_mode = wpr_modes.WPR_OFF
+      possible_browser.finder_options.wpr_mode = wpr_modes.WPR_OFF
   results_for_current_run = results
   if state.first_page[page] and test.discard_first_result:
     # If discarding results, substitute a dummy object.
@@ -184,18 +183,18 @@
 
       _WaitForThermalThrottlingIfNeeded(state.browser.platform)
 
-      if options.profiler:
-        state.StartProfiling(page, options)
+      if finder_options.profiler:
+        state.StartProfiling(page, finder_options)
 
       try:
         _RunPage(test, page, state, expectation,
-                 results_for_current_run, options)
+                 results_for_current_run, finder_options)
         _CheckThermalThrottling(state.browser.platform)
       except exceptions.TabCrashException:
         _LogStackTrace('Tab crashed: %s' % page.url, state.browser)
         state.StopBrowser()
 
-      if options.profiler:
+      if finder_options.profiler:
         state.StopProfiling()
 
       if test.NeedsBrowserRestartAfterEachRun(state.tab):
@@ -213,31 +212,32 @@
   results_for_current_run.StopTest(page)
 
 
-def Run(test, page_set, expectations, options):
+def Run(test, page_set, expectations, finder_options):
   """Runs a given test against a given page_set with the given options."""
-  results = results_options.PrepareResults(test, options)
+  results = results_options.PrepareResults(test, finder_options)
 
   # Create a possible_browser with the given options.
-  test.CustomizeBrowserOptions(options)
-  if options.profiler:
-    profiler_class = profiler_finder.FindProfiler(options.profiler)
-    profiler_class.CustomizeBrowserOptions(options)
+  test.CustomizeBrowserOptions(finder_options)
+  if finder_options.profiler:
+    profiler_class = profiler_finder.FindProfiler(finder_options.profiler)
+    profiler_class.CustomizeBrowserOptions(finder_options)
   try:
-    possible_browser = browser_finder.FindBrowser(options)
+    possible_browser = browser_finder.FindBrowser(finder_options)
   except browser_finder.BrowserTypeRequiredException, e:
     sys.stderr.write(str(e) + '\n')
     sys.exit(1)
   if not possible_browser:
     sys.stderr.write(
         'No browser found. Available browsers:\n' +
-        '\n'.join(browser_finder.GetAllAvailableBrowserTypes(options)) + '\n')
+        '\n'.join(browser_finder.GetAllAvailableBrowserTypes(finder_options)) +
+        '\n')
     sys.exit(1)
 
   # Reorder page set based on options.
-  pages = _ShuffleAndFilterPageSet(page_set, options)
+  pages = _ShuffleAndFilterPageSet(page_set, finder_options)
 
-  if (not options.allow_live_sites and
-      options.wpr_mode != wpr_modes.WPR_RECORD):
+  if (not finder_options.allow_live_sites and
+      finder_options.wpr_mode != wpr_modes.WPR_RECORD):
     pages = _CheckArchives(page_set, pages, results)
 
   # Verify credentials path.
@@ -250,10 +250,10 @@
 
   # Set up user agent.
   if page_set.user_agent_type:
-    options.browser_user_agent_type = page_set.user_agent_type
+    finder_options.browser_user_agent_type = page_set.user_agent_type
 
   for page in pages:
-    test.CustomizeBrowserOptionsForPage(page, possible_browser.options)
+    test.CustomizeBrowserOptionsForPage(page, possible_browser.finder_options)
 
   for page in list(pages):
     if not test.CanRunForPage(page):
@@ -270,7 +270,7 @@
   try:
     test.WillRunTest(state.tab)
     state.repeat_state = page_runner_repeat.PageRunnerRepeatState(
-                             options.repeat_options)
+                             finder_options.repeat_options)
 
     state.repeat_state.WillRunPageSet()
     while state.repeat_state.ShouldRepeatPageSet():
@@ -279,7 +279,7 @@
         test.WillRunPageRepeats(page, state.tab)
         while state.repeat_state.ShouldRepeatPage():
           # execute test on page
-          _PrepareAndRunPage(test, page_set, expectations, options, page,
+          _PrepareAndRunPage(test, page_set, expectations, finder_options, page,
                              credentials_path, possible_browser, results, state)
           state.repeat_state.DidRunPage()
         test.DidRunPageRepeats(page, state.tab)
@@ -292,18 +292,19 @@
   return results
 
 
-def _ShuffleAndFilterPageSet(page_set, options):
-  if options.pageset_shuffle_order_file and not options.pageset_shuffle:
+def _ShuffleAndFilterPageSet(page_set, finder_options):
+  if (finder_options.pageset_shuffle_order_file and
+      not finder_options.pageset_shuffle):
     raise Exception('--pageset-shuffle-order-file requires --pageset-shuffle.')
 
-  if options.pageset_shuffle_order_file:
-    return page_set.ReorderPageSet(options.pageset_shuffle_order_file)
+  if finder_options.pageset_shuffle_order_file:
+    return page_set.ReorderPageSet(finder_options.pageset_shuffle_order_file)
 
-  page_filter = page_filter_module.PageFilter(options)
+  page_filter = page_filter_module.PageFilter(finder_options)
   pages = [page for page in page_set.pages[:]
            if not page.disabled and page_filter.IsSelected(page)]
 
-  if options.pageset_shuffle:
+  if finder_options.pageset_shuffle:
     random.Random().shuffle(pages)
 
   return pages
@@ -365,7 +366,11 @@
           pages_missing_archive_path + pages_missing_archive_data]
 
 
-def _RunPage(test, page, state, expectation, results, options):
+def _RunPage(test, page, state, expectation, results, finder_options):
+  if expectation == 'skip':
+    logging.warning('Skipped %s' % page.url)
+    return
+
   logging.info('Running %s' % page.url)
 
   page_state = PageState()
@@ -381,9 +386,10 @@
 
   try:
     page_state.PreparePage(page, tab, test)
-    if state.repeat_state.ShouldNavigate(options.skip_navigate_on_repeat):
+    if state.repeat_state.ShouldNavigate(
+        finder_options.skip_navigate_on_repeat):
       page_state.ImplicitPageNavigation(page, tab, test)
-    test.Run(options, page, tab, results)
+    test.Run(finder_options, page, tab, results)
     util.CloseConnections(tab)
   except page_test.Failure:
     logging.warning('%s:\n%s', page.url, traceback.format_exc())
diff --git a/tools/telemetry/telemetry/page/page_set.py b/tools/telemetry/telemetry/page/page_set.py
index 807c365..ce79f38 100644
--- a/tools/telemetry/telemetry/page/page_set.py
+++ b/tools/telemetry/telemetry/page/page_set.py
@@ -17,6 +17,7 @@
     self.credentials_path = None
     self.user_agent_type = None
     self.make_javascript_deterministic = True
+    self.navigate_steps = {'action': 'navigate'}
 
     if attributes:
       for k, v in attributes.iteritems():
diff --git a/tools/telemetry/telemetry/page/page_test.py b/tools/telemetry/telemetry/page/page_test.py
index e9d7147..405a621 100644
--- a/tools/telemetry/telemetry/page/page_test.py
+++ b/tools/telemetry/telemetry/page/page_test.py
@@ -205,22 +205,15 @@
   def RunNavigateSteps(self, page, tab):
     """Navigates the tab to the page URL attribute.
 
-    If 'navigate_steps' is defined for the page, this will attempt to
-    run it as a compound action.
+    Runs the 'navigate_steps' page attribute as a compound action.
     """
-    if hasattr(page, 'navigate_steps'):
-      navigate_actions = GetCompoundActionFromPage(page, 'navigate_steps')
-      if not any(isinstance(action, navigate.NavigateAction)
-          for action in navigate_actions):
-        raise page_action.PageActionFailed(
-            'No NavigateAction in navigate_steps')
-      self._RunCompoundAction(page, tab, navigate_actions, False)
-    else:
-      # TODO(edmundyan): Make a default navigate_steps action on the page object
-      # once we can deprecate page.WaitToLoad()
-      i = navigate.NavigateAction()
-      i.RunAction(page, tab, None)
-      page.WaitToLoad(tab, 60)
+    navigate_actions = GetCompoundActionFromPage(page, 'navigate_steps')
+    if not any(isinstance(action, navigate.NavigateAction)
+        for action in navigate_actions):
+      raise page_action.PageActionFailed(
+          'No NavigateAction in navigate_steps')
+
+    self._RunCompoundAction(page, tab, navigate_actions, False)
 
   @property
   def action_name_to_run(self):
diff --git a/tools/telemetry/telemetry/page/page_test_runner.py b/tools/telemetry/telemetry/page/page_test_runner.py
index 623a5c8..c4c717f 100644
--- a/tools/telemetry/telemetry/page/page_test_runner.py
+++ b/tools/telemetry/telemetry/page/page_test_runner.py
@@ -127,10 +127,11 @@
                     page_set_arg)
 
   def ParseCommandLine(self, args, base_dir, page_set_filenames):
-    # Need to collect profile creators before creating command line parser.
-    profile_types.FindProfileCreators(base_dir, base_dir)
+    # Need to collect generated profile names before creating command line
+    # parser.
+    profile_types.ScanForGeneratedProfiles()
 
-    self._options = browser_options.BrowserOptions()
+    self._options = browser_options.BrowserFinderOptions()
     self._parser = self._options.CreateParser(
         '%%prog [options] %s page_set' % self.test_class_name)
 
diff --git a/tools/telemetry/telemetry/page/profile_creator.py b/tools/telemetry/telemetry/page/profile_creator.py
new file mode 100644
index 0000000..1fc092f
--- /dev/null
+++ b/tools/telemetry/telemetry/page/profile_creator.py
@@ -0,0 +1,19 @@
+# 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.
+
+from telemetry.page import page_measurement
+
+class ProfileCreator(page_measurement.PageMeasurement):
+  """Base class for an object that constructs a Chrome profile."""
+
+  def __init__(self):
+    super(ProfileCreator, self).__init__()
+    self._page_set = None
+
+  @property
+  def page_set(self):
+    return self._page_set
+
+  def MeasurePage(self, _, tab, results):
+    pass
\ No newline at end of file
diff --git a/tools/telemetry/telemetry/page/profile_generator.py b/tools/telemetry/telemetry/page/profile_generator.py
new file mode 100644
index 0000000..ed92a1d
--- /dev/null
+++ b/tools/telemetry/telemetry/page/profile_generator.py
@@ -0,0 +1,105 @@
+# 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.
+
+"""Handles generating profiles and transferring them to/from mobile devices."""
+
+import logging
+import optparse
+import os
+import shutil
+import sys
+import tempfile
+
+from telemetry.core import browser_options
+from telemetry.core import discover
+from telemetry.core import util
+from telemetry.core import profile_types
+from telemetry.page import page_runner
+from telemetry.page import profile_creator
+from telemetry.page import test_expectations
+
+
+def _DiscoverProfileCreatorClasses():
+  profile_creators_dir = os.path.abspath(os.path.join(util.GetBaseDir(),
+      os.pardir, 'perf', 'profile_creators'))
+  base_dir = os.path.abspath(os.path.join(profile_creators_dir, os.pardir))
+
+  profile_creators_unfiltered = discover.DiscoverClasses(
+      profile_creators_dir, base_dir, profile_creator.ProfileCreator)
+
+  # Remove '_creator' suffix from keys.
+  profile_creators = {}
+  for test_name, test_class in profile_creators_unfiltered.iteritems():
+    assert test_name.endswith('_creator')
+    test_name = test_name[:-len('_creator')]
+    profile_creators[test_name] = test_class
+  return profile_creators
+
+def GenerateProfiles(profile_creator_class, profile_creator_name, options):
+  """Generate a profile"""
+  expectations = test_expectations.TestExpectations()
+  test = profile_creator_class()
+
+  temp_output_directory = tempfile.mkdtemp()
+  options.output_profile_path = temp_output_directory
+
+  results = page_runner.Run(test, test.page_set, expectations, options)
+
+  if results.errors or results.failures:
+    logging.warning('Some pages failed.')
+    if results.errors or results.failures:
+      logging.warning('Failed pages:\n%s',
+                      '\n'.join(zip(*results.errors + results.failures)[0]))
+    return 1
+
+  # Everything is a-ok, move results to final destination.
+  generated_profiles_dir = profile_types.GetGeneratedProfilesDir()
+  if not os.path.exists(generated_profiles_dir):
+    os.makedirs(generated_profiles_dir)
+  out_path = os.path.join(generated_profiles_dir, profile_creator_name)
+  if os.path.exists(out_path):
+    shutil.rmtree(out_path)
+
+  shutil.move(temp_output_directory, out_path)
+  sys.stderr.write("SUCCESS: Generated profile copied to: '%s'.\n" % out_path)
+
+  return 0
+
+def Main():
+  profile_creators = _DiscoverProfileCreatorClasses()
+  legal_profile_creators = '|'.join(profile_creators.keys())
+
+  options = browser_options.BrowserFinderOptions()
+  parser = options.CreateParser(
+      "%%prog <--profile-type-to-generate=...> <--browser=...>")
+  page_runner.AddCommandLineOptions(parser)
+
+  group = optparse.OptionGroup(parser, 'Profile generation options')
+  group.add_option('--profile-type-to-generate',
+      dest='profile_type_to_generate',
+      default=None,
+      help='Type of profile to generate. '
+           'Supported values: %s' % legal_profile_creators)
+  parser.add_option_group(group)
+
+  _, _ = parser.parse_args()
+
+  # Sanity check arguments.
+  if not options.profile_type_to_generate:
+    raise Exception("Must specify --profile-type-to-generate option.")
+
+  if options.profile_type_to_generate not in profile_creators.keys():
+    raise Exception("Invalid profile type, legal values are: %s." %
+        legal_profile_creators)
+
+  if not options.browser_type:
+    raise Exception("Must specify --browser option.")
+
+  if options.dont_override_profile:
+    raise Exception("Can't use existing profile when generating profile.")
+
+  # Generate profile.
+  profile_creator_class = profile_creators[options.profile_type_to_generate]
+  return GenerateProfiles(profile_creator_class,
+      options.profile_type_to_generate, options)
diff --git a/tools/telemetry/telemetry/page/record_wpr.py b/tools/telemetry/telemetry/page/record_wpr.py
index 1329073..bbec64d 100755
--- a/tools/telemetry/telemetry/page/record_wpr.py
+++ b/tools/telemetry/telemetry/page/record_wpr.py
@@ -65,7 +65,7 @@
                                           page_measurement.PageMeasurement)
   tests = discover.DiscoverClasses(base_dir, base_dir, test.Test,
                                    index_by_class_name=True)
-  options = browser_options.BrowserOptions()
+  options = browser_options.BrowserFinderOptions()
   parser = options.CreateParser('%prog <PageSet|Measurement|Test>')
   page_runner.AddCommandLineOptions(parser)
 
diff --git a/tools/telemetry/telemetry/page/test_expectations.py b/tools/telemetry/telemetry/page/test_expectations.py
index cd9244d..bd99e71 100644
--- a/tools/telemetry/telemetry/page/test_expectations.py
+++ b/tools/telemetry/telemetry/page/test_expectations.py
@@ -56,6 +56,9 @@
   def Fail(self, url_pattern, conditions=None, bug=None):
     self._Expect('fail', url_pattern, conditions, bug)
 
+  def Skip(self, url_pattern, conditions=None, bug=None):
+    self._Expect('skip', url_pattern, conditions, bug)
+
   def _Expect(self, expectation, url_pattern, conditions=None, bug=None):
     self.expectations.append(Expectation(expectation, url_pattern, conditions,
       bug))
diff --git a/tools/telemetry/telemetry/test_runner.py b/tools/telemetry/telemetry/test_runner.py
index d3c4def..9555341 100644
--- a/tools/telemetry/telemetry/test_runner.py
+++ b/tools/telemetry/telemetry/test_runner.py
@@ -89,7 +89,7 @@
   usage = 'test_name [...] [<args>]'
 
   def CreateParser(self):
-    options = browser_options.BrowserOptions()
+    options = browser_options.BrowserFinderOptions()
     parser = options.CreateParser('%%prog %s %s' % (self.name, self.usage))
     return parser
 
diff --git a/tools/telemetry/telemetry/unittest/run_tests.py b/tools/telemetry/telemetry/unittest/run_tests.py
index 89f4ad1..f38348f 100644
--- a/tools/telemetry/telemetry/unittest/run_tests.py
+++ b/tools/telemetry/telemetry/unittest/run_tests.py
@@ -85,7 +85,7 @@
   # Add unittest_data to the path so we can import packages from it.
   util.AddDirToPythonPath(util.GetUnittestDataDir())
 
-  default_options = browser_options.BrowserOptions()
+  default_options = browser_options.BrowserFinderOptions()
   default_options.browser_type = 'any'
 
   parser = default_options.CreateParser('run_tests [options] [test names]')
diff --git a/tools/telemetry/unittest_data/discoverable_classes/dummy_profile_creator.py b/tools/telemetry/unittest_data/discoverable_classes/dummy_profile_creator.py
deleted file mode 100644
index 867ded3..0000000
--- a/tools/telemetry/unittest_data/discoverable_classes/dummy_profile_creator.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 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.
-
-from telemetry.core import profile_creator
-
-class DummyProfileCreator(profile_creator.ProfileCreator):
-  def CreateProfile(self):
-    pass
diff --git a/tools/telemetry/unittest_data/test_png.png b/tools/telemetry/unittest_data/test_png.png
new file mode 100644
index 0000000..3aaf03b
--- /dev/null
+++ b/tools/telemetry/unittest_data/test_png.png
Binary files differ
diff --git a/tools/telemetry/unittest_data/test_png_2.png b/tools/telemetry/unittest_data/test_png_2.png
new file mode 100644
index 0000000..f44a4a6
--- /dev/null
+++ b/tools/telemetry/unittest_data/test_png_2.png
Binary files differ
diff --git a/tools/valgrind/gtest_exclude/content_unittests.gtest_mac.txt b/tools/valgrind/gtest_exclude/content_unittests.gtest_mac.txt
index 6e10951..0cf77b6 100644
--- a/tools/valgrind/gtest_exclude/content_unittests.gtest_mac.txt
+++ b/tools/valgrind/gtest_exclude/content_unittests.gtest_mac.txt
@@ -50,3 +50,9 @@
 
 # http://crbug.com/270254
 DeviceOrientationEventPumpTest.*
+
+# http://crbug.com/280495
+SyntheticGestureControllerTest.Tick
+
+# http://crbug.com/280583
+DesktopCaptureDeviceTest.ScreenResolutionChangeVariableResolution
diff --git a/tools/valgrind/memcheck/suppressions.txt b/tools/valgrind/memcheck/suppressions.txt
index 929207d..a7fa5ba 100644
--- a/tools/valgrind/memcheck/suppressions.txt
+++ b/tools/valgrind/memcheck/suppressions.txt
@@ -5658,13 +5658,6 @@
    fun:_ZNK6SkDraw8drawPathERK6SkPathRK7SkPaintPK8SkMatrixb
 }
 {
-   bug_163111
-   Memcheck:Leak
-   fun:_Znw*
-   fun:_ZN4dbus8Response14FromMethodCallEPNS_10MethodCallE
-   fun:_ZN8chromeos20IBusPanelServiceImpl*
-}
-{
    bug_163922
    Memcheck:Leak
    fun:_Znw*
@@ -6383,12 +6376,12 @@
 {
    bug_222883
    Memcheck:Uninitialized
-   fun:_ZN2v88internal15ScavengeVisitor15ScavengePointerEPPNS0_6ObjectE.isra.327
+   fun:_ZN2v88internal15ScavengeVisitor15ScavengePointerEPPNS0_6ObjectE.isra.*
    fun:_ZN2v88internal15ScavengeVisitor13VisitPointersEPPNS0_6ObjectES4_
    fun:_ZNK2v88internal13StandardFrame18IterateExpressionsEPNS0_13ObjectVisitorE
    ...
    fun:_ZN2v88internal4Heap8ScavengeEv
-   fun:_ZN2v88internal4Heap24PerformGarbageCollectionENS0_16GarbageCollectorEPNS0_8GCTracerE.constprop.678
+   fun:_ZN2v88internal4Heap24PerformGarbageCollectionENS0_16GarbageCollectorEPNS0_8GCTracerE.constprop.*
 }
 {
    bug_222887
@@ -7204,13 +7197,6 @@
    fun:_ZN8chromeos12_GLOBAL__N_119LoadNSSCertificatesEPSt6vectorI13scoped_refptrIN3net15X509CertificateEESaIS5_EE
 }
 {
-   bug_272083
-   Memcheck:Leak
-   fun:_Znw*
-   fun:_ZN12ThemeService19SetManagedUserThemeEv
-   fun:_ZN12ThemeService24OnManagedUserInitializedEv
-}
-{
    bug_273398
    Memcheck:Leak
    ...
@@ -7236,3 +7222,20 @@
    fun:_ZN3gpu5gles216GLES2DecoderImpl9DoCommandEjjPKv
    fun:_ZN3gpu13CommandParser14ProcessCommandEv
 }
+{
+   bug_280931
+   Memcheck:Leak
+   fun:_Znw*
+   fun:_ZN3net10QuicPacket13NewDataPacketEPcmbNS_14QuicGuidLengthEbNS_24QuicSequenceNumberLengthE
+   fun:_ZN3net10QuicFramer15BuildDataPacketERKNS_16QuicPacketHeaderERKSt6vectorINS_9QuicFrameESaIS5_EEm
+   fun:_ZN3net17QuicPacketCreator15SerializePacketEv
+   fun:_ZN3net19QuicPacketGenerator22SerializeAndSendPacketEv
+   fun:_ZN3net19QuicPacketGenerator16SendQueuedFramesEv
+   fun:_ZN3net19QuicPacketGenerator15AddControlFrameERKNS_9QuicFrameE
+   fun:_ZN3net14QuicConnection13SendRstStreamEjNS_22QuicRstStreamErrorCodeE
+   fun:_ZN3net11QuicSession13SendRstStreamEjNS_22QuicRstStreamErrorCodeE
+   fun:_ZN3net17QuicClientSession13SendRstStreamEjNS_22QuicRstStreamErrorCodeE
+   fun:_ZN3net18ReliableQuicStream5CloseENS_22QuicRstStreamErrorCodeE
+   fun:_ZN3net14QuicHttpStream5CloseEb
+   fun:_ZN3net4test40QuicStreamFactoryTest_MaxOpenStream_Test8TestBodyEv
+}
diff --git a/tools/valgrind/memcheck/suppressions_mac.txt b/tools/valgrind/memcheck/suppressions_mac.txt
index 714321a..ce36ce0 100644
--- a/tools/valgrind/memcheck/suppressions_mac.txt
+++ b/tools/valgrind/memcheck/suppressions_mac.txt
@@ -2595,3 +2595,15 @@
    fun:_ZN10disk_cache15SimpleIndexFile19SyncRestoreFromDiskERKN4base8FilePathE
    fun:_ZN10disk_cache15SimpleIndexFile20SyncLoadIndexEntriesERKN4base8FilePathE13scoped_refptrINS1_22SingleThreadTaskRunnerEERKNS1_8CallbackIFv10scoped_ptrIN9__gnu_cxx8hash_mapIyNS_13EntryMetadataENSA_4hashIyEESt8equal_toIyESaISC_EEENS1_14DefaultDeleterISI_EEEbEEE
 }
+{
+   bug_280583
+   Memcheck:Unaddressable
+   ...
+   fun:_ZN6libyuv21ScaleARGBBilinearDownEiiiiiPKhPhiiii
+   fun:_ZN6libyuv16ScaleARGBAnySizeEiiiiiiiiPKhPhiiiiNS_10FilterModeE
+   fun:_ZN6libyuv9ScaleARGBEPKhiiiPhiiiiiiiNS_10FilterModeE
+   fun:ARGBScale
+   fun:_ZN7content20DesktopCaptureDevice4Core18OnCaptureCompletedEPN6webrtc12DesktopFrameE
+   fun:_ZN6webrtc12_GLOBAL__N_117ScreenCapturerMac7CaptureERKNS_13DesktopRegionE
+   fun:_ZN7content20DesktopCaptureDevice4Core9DoCaptureEv
+}
diff --git a/tools/valgrind/tsan_v2/suppressions.txt b/tools/valgrind/tsan_v2/suppressions.txt
index 274059e..0111c72 100644
--- a/tools/valgrind/tsan_v2/suppressions.txt
+++ b/tools/valgrind/tsan_v2/suppressions.txt
@@ -87,10 +87,6 @@
 # http://crbug.com/246974
 race:content::GpuWatchdogThread::CheckArmed
 
-# http://crbug.com/248101
-race:sqlite3Config
-race:mem0
-
 # http://crbug.com/257396
 race:base::debug::TraceEventTestFixture_TraceSamplingScope_Test::TestBody
 
diff --git a/tools/webrtc_perf_expectations/README b/tools/webrtc_perf_expectations/README
deleted file mode 100644
index 972aceb..0000000
--- a/tools/webrtc_perf_expectations/README
+++ /dev/null
@@ -1,13 +0,0 @@
-========
-Overview
-========
-
-See http://dev.chromium.org/developers/tree-sheriffs/perf-sheriffs#TOC-Selecting-an-acceptable-range-of-results
-for a general explanation of how performance expectations work.
-
-Instead of the regular make_expectations.py call, run this:
-
-../perf_expectations/make_expectations.py -c webrtc_perf_expectations.cfg
-
-We keep the WebRTC perf expectations aside since the tests are running on an
-internal master for now.
\ No newline at end of file
diff --git a/tools/webrtc_perf_expectations/perf_expectations.json b/tools/webrtc_perf_expectations/perf_expectations.json
deleted file mode 100644
index bd31c56..0000000
--- a/tools/webrtc_perf_expectations/perf_expectations.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{"linux-chrome-pure/webrtc_pyauto/cpu/jsep01_call": {"reva": 199965, "revb": 200469, "type": "absolute", "improve": 27.55, "regress": 84.84, "sha1": "a35b38f1"},
- "linux-chrome-pure/webrtc_pyauto/memory/jsep01_call": {"reva": 199600, "revb": 200178, "type": "absolute", "improve": 39672, "regress": 46402, "sha1": "e5906b10"},
- "mac-chrome-pure/webrtc_pyauto/cpu/jsep01_call": {"reva": 189929, "revb": 193335, "type": "absolute", "improve": 35.15, "regress": 55.02, "sha1": "3fb872b1"},
- "mac-chrome-pure/webrtc_pyauto/memory/jsep01_call": {"reva": 199600, "revb": 200178, "type": "absolute", "improve": 55537, "regress": 62769, "sha1": "3301ac33"},
- "win-chrome-pure/webrtc_pyauto/cpu/jsep01_call": {"reva": 197094, "revb": 198850, "type": "absolute", "improve": 28.88, "regress": 51.45, "sha1": "0ce37cd6"},
- "win-chrome-pure/webrtc_pyauto/memory/jsep01_call": {"reva": 199600, "revb": 200178, "type": "absolute", "improve": 39649, "regress": 44726, "sha1": "2beba373"},
- "load": true
-}
diff --git a/tools/webrtc_perf_expectations/webrtc_perf_expectations.cfg b/tools/webrtc_perf_expectations/webrtc_perf_expectations.cfg
deleted file mode 100644
index d6ecf30..0000000
--- a/tools/webrtc_perf_expectations/webrtc_perf_expectations.cfg
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "base_url": "http://www.corp.google.com/~webrtc-cb/perf/",
-  "perf_file": "perf_expectations.json"
-}
\ No newline at end of file
diff --git a/ui/android/java/src/org/chromium/ui/WindowAndroid.java b/ui/android/java/src/org/chromium/ui/WindowAndroid.java
index 3fec600..c6b263c 100644
--- a/ui/android/java/src/org/chromium/ui/WindowAndroid.java
+++ b/ui/android/java/src/org/chromium/ui/WindowAndroid.java
@@ -34,6 +34,7 @@
 
     private int mNextRequestCode = 0;
     protected Activity mActivity;
+    protected Context mApplicationContext;
     protected SparseArray<IntentCallback> mOutstandingIntents;
     protected HashMap<Integer, String> mIntentErrors;
 
@@ -42,6 +43,7 @@
      */
     public WindowAndroid(Activity activity) {
         mActivity = activity;
+        mApplicationContext = mActivity.getApplicationContext();
         mOutstandingIntents = new SparseArray<IntentCallback>();
         mIntentErrors = new HashMap<Integer, String>();
 
@@ -107,6 +109,7 @@
     /**
      * TODO(nileshagrawal): Stop returning Activity Context crbug.com/233440.
      * @return Activity context.
+     * @see #getApplicationContext()
      */
     @Deprecated
     public Context getContext() {
@@ -114,6 +117,13 @@
     }
 
     /**
+     * @return The application context for this activity.
+     */
+    public Context getApplicationContext() {
+        return mApplicationContext;
+    }
+
+    /**
      * Saves the error messages that should be shown if any pending intents would return
      * after the application has been put onPause.
      * @param bundle The bundle to save the information in onPause
diff --git a/ui/android/java/src/org/chromium/ui/gfx/SurfaceTextureListener.java b/ui/android/java/src/org/chromium/ui/gfx/SurfaceTextureListener.java
index 8efe6dd..1b77370 100644
--- a/ui/android/java/src/org/chromium/ui/gfx/SurfaceTextureListener.java
+++ b/ui/android/java/src/org/chromium/ui/gfx/SurfaceTextureListener.java
@@ -6,7 +6,6 @@
 
 import android.graphics.SurfaceTexture;
 
-import org.chromium.base.CalledByNative;
 import org.chromium.base.JNINamespace;
 
 /**
@@ -15,9 +14,9 @@
 @JNINamespace("gfx")
 class SurfaceTextureListener implements SurfaceTexture.OnFrameAvailableListener {
     // Used to determine the class instance to dispatch the native call to.
-    private int mNativeSurfaceTextureListener = 0;
+    private final int mNativeSurfaceTextureListener;
 
-    private SurfaceTextureListener(int nativeSurfaceTextureListener) {
+    SurfaceTextureListener(int nativeSurfaceTextureListener) {
         assert nativeSurfaceTextureListener != 0;
         mNativeSurfaceTextureListener = nativeSurfaceTextureListener;
     }
@@ -36,11 +35,6 @@
         }
     }
 
-    @CalledByNative
-    private static SurfaceTextureListener create(int nativeSurfaceTextureListener) {
-        return new SurfaceTextureListener(nativeSurfaceTextureListener);
-    }
-
     private native void nativeFrameAvailable(int nativeSurfaceTextureListener);
     private native void nativeDestroy(int nativeSurfaceTextureListener);
 }
diff --git a/ui/android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java b/ui/android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java
new file mode 100644
index 0000000..cd8e407
--- /dev/null
+++ b/ui/android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java
@@ -0,0 +1,64 @@
+// 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.
+
+package org.chromium.ui.gfx;
+
+import android.graphics.SurfaceTexture;
+import android.os.Build;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+
+/**
+ * Wrapper class for the underlying platform's SurfaceTexture in order to
+ * provide a stable JNI API.
+ */
+@JNINamespace("gfx")
+class SurfaceTexturePlatformWrapper {
+    @CalledByNative
+    private static SurfaceTexture create(int textureId) {
+        return new SurfaceTexture(textureId);
+    }
+
+    @CalledByNative
+    private static void destroy(SurfaceTexture surfaceTexture) {
+        surfaceTexture.setOnFrameAvailableListener(null);
+        surfaceTexture.release();
+    }
+
+    @CalledByNative
+    private static void setFrameAvailableCallback(SurfaceTexture surfaceTexture,
+            int nativeSurfaceTextureListener) {
+       surfaceTexture.setOnFrameAvailableListener(
+               new SurfaceTextureListener(nativeSurfaceTextureListener));
+    }
+
+    @CalledByNative
+    private static void updateTexImage(SurfaceTexture surfaceTexture) {
+        surfaceTexture.updateTexImage();
+    }
+
+    @CalledByNative
+    private static void setDefaultBufferSize(SurfaceTexture surfaceTexture, int width,
+            int height) {
+        surfaceTexture.setDefaultBufferSize(width, height);
+    }
+
+    @CalledByNative
+    private static void getTransformMatrix(SurfaceTexture surfaceTexture, float[] matrix) {
+        surfaceTexture.getTransformMatrix(matrix);
+    }
+
+    @CalledByNative
+    private static void attachToGLContext(SurfaceTexture surfaceTexture, int texName) {
+        assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
+        surfaceTexture.attachToGLContext(texName);
+    }
+
+    @CalledByNative
+    private static void detachFromGLContext(SurfaceTexture surfaceTexture) {
+        assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
+        surfaceTexture.detachFromGLContext();
+    }
+}
\ No newline at end of file
diff --git a/ui/app_list/app_list_item_model.h b/ui/app_list/app_list_item_model.h
index d437564..1fa8064 100644
--- a/ui/app_list/app_list_item_model.h
+++ b/ui/app_list/app_list_item_model.h
@@ -49,7 +49,8 @@
   void AddObserver(AppListItemModelObserver* observer);
   void RemoveObserver(AppListItemModelObserver* observer);
 
-  // Returns the context menu model for this item.
+  // Returns the context menu model for this item, or NULL if there is currently
+  // no menu for the item (e.g. during install).
   // Note the returned menu model is owned by this item.
   virtual ui::MenuModel* GetContextMenuModel();
 
diff --git a/ui/app_list/cocoa/apps_grid_controller_unittest.mm b/ui/app_list/cocoa/apps_grid_controller_unittest.mm
index dba0065..2ee1651 100644
--- a/ui/app_list/cocoa/apps_grid_controller_unittest.mm
+++ b/ui/app_list/cocoa/apps_grid_controller_unittest.mm
@@ -122,17 +122,27 @@
 
 class AppListItemWithMenu : public AppListItemModel {
  public:
-  AppListItemWithMenu(const std::string& title) : menu_model_(NULL) {
+  AppListItemWithMenu(const std::string& title)
+      : menu_model_(NULL),
+        menu_ready_(true) {
     SetTitle(title);
     menu_model_.AddItem(0, UTF8ToUTF16("Menu For: " + title));
   }
 
+  void SetMenuReadyForTesting(bool ready) {
+    menu_ready_ = ready;
+  }
+
   virtual ui::MenuModel* GetContextMenuModel() OVERRIDE {
+    if (!menu_ready_)
+      return NULL;
+
     return &menu_model_;
   }
 
  private:
   ui::SimpleMenuModel menu_model_;
+  bool menu_ready_;
 
   DISALLOW_COPY_AND_ASSIGN(AppListItemWithMenu);
 };
@@ -891,8 +901,9 @@
 }
 
 TEST_F(AppsGridControllerTest, ContextMenus) {
+  AppListItemWithMenu* item_two_model = new AppListItemWithMenu("Item Two");
   model()->apps()->AddAt(0, new AppListItemWithMenu("Item One"));
-  model()->apps()->AddAt(1, new AppListItemWithMenu("Item Two"));
+  model()->apps()->AddAt(1, item_two_model);
   EXPECT_EQ(2u, [apps_grid_controller_ itemCount]);
 
   NSCollectionView* page = [apps_grid_controller_ collectionViewAtPageIndex:0];
@@ -903,6 +914,12 @@
   EXPECT_EQ(1, [menu numberOfItems]);
   EXPECT_NSEQ(@"Menu For: Item One", [[menu itemAtIndex:0] title]);
 
+  // Test a context menu request while the item is still installing.
+  item_two_model->SetMenuReadyForTesting(false);
+  menu = [page menuForEvent:mouse_at_cell_1];
+  EXPECT_EQ(nil, menu);
+
+  item_two_model->SetMenuReadyForTesting(true);
   menu = [page menuForEvent:mouse_at_cell_1];
   EXPECT_EQ(1, [menu numberOfItems]);
   EXPECT_NSEQ(@"Menu For: Item Two", [[menu itemAtIndex:0] title]);
diff --git a/ui/app_list/cocoa/apps_grid_view_item.mm b/ui/app_list/cocoa/apps_grid_view_item.mm
index 9d83d15..d88fb26 100644
--- a/ui/app_list/cocoa/apps_grid_view_item.mm
+++ b/ui/app_list/cocoa/apps_grid_view_item.mm
@@ -99,8 +99,12 @@
 
 NSMenu* ItemModelObserverBridge::GetContextMenu() {
   if (!context_menu_controller_) {
+    ui::MenuModel* menu_model = model_->GetContextMenuModel();
+    if (!menu_model)
+      return nil;
+
     context_menu_controller_.reset(
-        [[MenuController alloc] initWithModel:model_->GetContextMenuModel()
+        [[MenuController alloc] initWithModel:menu_model
                        useWithPopUpButtonCell:NO]);
   }
   return [context_menu_controller_ menu];
diff --git a/ui/app_list/cocoa/apps_search_results_controller_unittest.mm b/ui/app_list/cocoa/apps_search_results_controller_unittest.mm
index 218ad44..3645e1e 100644
--- a/ui/app_list/cocoa/apps_search_results_controller_unittest.mm
+++ b/ui/app_list/cocoa/apps_search_results_controller_unittest.mm
@@ -56,19 +56,28 @@
 
 class SearchResultWithMenu : public SearchResult {
  public:
-  SearchResultWithMenu(const std::string& title,
-                       const std::string& details) : menu_model_(NULL) {
+  SearchResultWithMenu(const std::string& title, const std::string& details)
+      : menu_model_(NULL),
+        menu_ready_(true) {
     set_title(ASCIIToUTF16(title));
     set_details(ASCIIToUTF16(details));
     menu_model_.AddItem(0, UTF8ToUTF16("Menu For: " + title));
   }
 
+  void SetMenuReadyForTesting(bool ready) {
+    menu_ready_ = ready;
+  }
+
   virtual ui::MenuModel* GetContextMenuModel() OVERRIDE {
+    if (!menu_ready_)
+      return NULL;
+
     return &menu_model_;
   }
 
  private:
   ui::SimpleMenuModel menu_model_;
+  bool menu_ready_;
 
   DISALLOW_COPY_AND_ASSIGN(SearchResultWithMenu);
 };
@@ -95,6 +104,12 @@
                                         row:index];
   }
 
+  void SetMenuReadyAt(size_t index, bool ready) {
+    SearchResultWithMenu* result =
+        static_cast<SearchResultWithMenu*>(ModelResultAt(index));
+    result->SetMenuReadyForTesting(ready);
+  }
+
   BOOL SimulateKeyAction(SEL c) {
     return [apps_search_results_controller_ handleCommandBySelector:c];
   }
@@ -268,6 +283,12 @@
   EXPECT_EQ(1, [menu numberOfItems]);
   EXPECT_NSEQ(@"Menu For: Result 0", [[menu itemAtIndex:0] title]);
 
+  // Test a context menu request while the item is still installing.
+  SetMenuReadyAt(1, false);
+  menu = [table_view menuForEvent:mouse_in_row_1];
+  EXPECT_EQ(nil, menu);
+
+  SetMenuReadyAt(1, true);
   menu = [table_view menuForEvent:mouse_in_row_1];
   EXPECT_EQ(1, [menu numberOfItems]);
   EXPECT_NSEQ(@"Menu For: Result 1", [[menu itemAtIndex:0] title]);
diff --git a/ui/app_list/cocoa/apps_search_results_model_bridge.mm b/ui/app_list/cocoa/apps_search_results_model_bridge.mm
index 66d6f07..69a3205 100644
--- a/ui/app_list/cocoa/apps_search_results_model_bridge.mm
+++ b/ui/app_list/cocoa/apps_search_results_model_bridge.mm
@@ -31,8 +31,12 @@
 
   NSMenu* GetContextMenu() {
     if (!context_menu_controller_) {
+      ui::MenuModel* menu_model = result_->GetContextMenuModel();
+      if (!menu_model)
+        return nil;
+
       context_menu_controller_.reset(
-          [[MenuController alloc] initWithModel:result_->GetContextMenuModel()
+          [[MenuController alloc] initWithModel:menu_model
                          useWithPopUpButtonCell:NO]);
     }
     return [context_menu_controller_ menu];
diff --git a/ui/app_list/search_result.h b/ui/app_list/search_result.h
index 5baa50f..012f9e3 100644
--- a/ui/app_list/search_result.h
+++ b/ui/app_list/search_result.h
@@ -106,7 +106,8 @@
   void AddObserver(SearchResultObserver* observer);
   void RemoveObserver(SearchResultObserver* observer);
 
-  // Returns the context menu model for this item.
+  // Returns the context menu model for this item, or NULL if there is currently
+  // no menu for the item (e.g. during install).
   // Note the returned menu model is owned by this item.
   virtual ui::MenuModel* GetContextMenuModel();
 
diff --git a/ui/app_list/views/app_list_main_view.cc b/ui/app_list/views/app_list_main_view.cc
index d2df1de..7dc19ff 100644
--- a/ui/app_list/views/app_list_main_view.cc
+++ b/ui/app_list/views/app_list_main_view.cc
@@ -6,8 +6,10 @@
 
 #include <algorithm>
 
+#include "base/bind.h"
 #include "base/callback.h"
 #include "base/files/file_path.h"
+#include "base/message_loop/message_loop.h"
 #include "base/strings/string_util.h"
 #include "ui/app_list/app_list_constants.h"
 #include "ui/app_list/app_list_item_model.h"
@@ -77,13 +79,14 @@
 AppListMainView::AppListMainView(AppListViewDelegate* delegate,
                                  AppListModel* model,
                                  PaginationModel* pagination_model,
-                                 views::View* anchor)
+                                 gfx::NativeView parent)
     : delegate_(delegate),
       model_(model),
       search_box_view_(NULL),
-      contents_view_(NULL) {
+      contents_view_(NULL),
+      weak_ptr_factory_(this) {
   // Starts icon loading early.
-  PreloadIcons(pagination_model, anchor);
+  PreloadIcons(pagination_model, parent);
 
   SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical,
                                         kInnerPadding,
@@ -139,12 +142,10 @@
 }
 
 void AppListMainView::PreloadIcons(PaginationModel* pagination_model,
-                                   views::View* anchor) {
+                                   gfx::NativeView parent) {
   ui::ScaleFactor scale_factor = ui::SCALE_FACTOR_100P;
-  if (anchor && anchor->GetWidget()) {
-    scale_factor = ui::GetScaleFactorForNativeView(
-        anchor->GetWidget()->GetNativeView());
-  }
+  if (parent)
+    scale_factor = ui::GetScaleFactorForNativeView(parent);
 
   // |pagination_model| could have -1 as the initial selected page and
   // assumes first page (i.e. index 0) will be used in this case.
@@ -230,7 +231,13 @@
 }
 
 void AppListMainView::OnResultUninstalled(SearchResult* result) {
-  QueryChanged(search_box_view_);
+  // Resubmit the query via a posted task so that all observers for the
+  // uninstall notification are notified.
+  base::MessageLoop::current()->PostTask(
+      FROM_HERE,
+      base::Bind(&AppListMainView::QueryChanged,
+                 weak_ptr_factory_.GetWeakPtr(),
+                 search_box_view_));
 }
 
 }  // namespace app_list
diff --git a/ui/app_list/views/app_list_main_view.h b/ui/app_list/views/app_list_main_view.h
index 1bb6981..0c9a738 100644
--- a/ui/app_list/views/app_list_main_view.h
+++ b/ui/app_list/views/app_list_main_view.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
 #include "ui/app_list/views/apps_grid_view_delegate.h"
 #include "ui/app_list/views/search_box_view_delegate.h"
@@ -39,7 +40,7 @@
   explicit AppListMainView(AppListViewDelegate* delegate,
                            AppListModel* model,
                            PaginationModel* pagination_model,
-                           views::View* anchor);
+                           gfx::NativeView parent);
   virtual ~AppListMainView();
 
   void ShowAppListWhenReady();
@@ -59,9 +60,9 @@
   class IconLoader;
 
   // Loads icon image for the apps in the selected page of |pagination_model|.
-  // |anchor| is used to determine the image scale factor to use.
+  // |parent| is used to determine the image scale factor to use.
   void PreloadIcons(PaginationModel* pagination_model,
-                    views::View* anchor);
+                    gfx::NativeView parent);
 
   // Invoked when |icon_loading_wait_timer_| fires.
   void OnIconLoadingWaitTimer();
@@ -99,6 +100,8 @@
 
   ScopedVector<IconLoader> pending_icon_loaders_;
 
+  base::WeakPtrFactory<AppListMainView> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(AppListMainView);
 };
 
diff --git a/ui/app_list/views/app_list_view.cc b/ui/app_list/views/app_list_view.cc
index 29b3f7c..a74e6e9 100644
--- a/ui/app_list/views/app_list_view.cc
+++ b/ui/app_list/views/app_list_view.cc
@@ -73,7 +73,7 @@
   app_list_main_view_ = new AppListMainView(delegate_.get(),
                                             model_.get(),
                                             pagination_model,
-                                            anchor);
+                                            parent);
   AddChildView(app_list_main_view_);
 #if defined(USE_AURA)
   app_list_main_view_->SetPaintToLayer(true);
diff --git a/ui/aura/client/cursor_client.h b/ui/aura/client/cursor_client.h
index c68c9aa..e8740ac 100644
--- a/ui/aura/client/cursor_client.h
+++ b/ui/aura/client/cursor_client.h
@@ -7,6 +7,7 @@
 
 #include "base/strings/string16.h"
 #include "ui/aura/aura_export.h"
+#include "ui/base/cursor/cursor.h"
 #include "ui/gfx/native_widget_types.h"
 
 namespace gfx {
@@ -34,6 +35,9 @@
   // Sets the scale of the mouse cursor icon.
   virtual void SetScale(float scale) = 0;
 
+  // Sets the type of the mouse cursor icon.
+  virtual void SetCursorSet(ui::CursorSetType cursor_set) = 0;
+
   // Gets whether the cursor is visible.
   virtual bool IsCursorVisible() const = 0;
 
diff --git a/ui/aura/root_window.cc b/ui/aura/root_window.cc
index e65e2b2..d5ab05b 100644
--- a/ui/aura/root_window.cc
+++ b/ui/aura/root_window.cc
@@ -6,7 +6,6 @@
 
 #include <vector>
 
-#include "base/auto_reset.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/debug/trace_event.h"
@@ -654,6 +653,14 @@
 
 void RootWindow::UpdateCapture(Window* old_capture,
                                Window* new_capture) {
+  if (!new_capture && old_capture && old_capture->GetRootWindow() != this) {
+    // If we no longer contain the window that had capture make sure we clean
+    // state in the GestureRecognizer. Since we don't contain the window we'll
+    // never get notification of its destruction and clean up state.
+    // We do this early on as OnCaptureLost() may delete |old_capture|.
+    gesture_recognizer_->CleanupStateForConsumer(old_capture);
+  }
+
   if (old_capture && old_capture->GetRootWindow() == this &&
       old_capture->delegate()) {
     // Send a capture changed event with bogus location data.
@@ -674,9 +681,8 @@
   }
 
   if (new_capture) {
-    // Make all subsequent mouse events and touch go to the capture window. We
-    // shouldn't need to send an event here as OnCaptureLost should take care of
-    // that.
+    // Make all subsequent mouse events go to the capture window. We shouldn't
+    // need to send an event here as OnCaptureLost() should take care of that.
     if (mouse_moved_handler_ || Env::GetInstance()->is_mouse_button_down())
       mouse_moved_handler_ = new_capture;
   } else {
@@ -827,7 +833,7 @@
 
 void RootWindow::CleanupGestureRecognizerState(Window* window) {
   gesture_recognizer_->CleanupStateForConsumer(window);
-  Windows windows = window->children();
+  const Windows& windows = window->children();
   for (Windows::const_iterator iter = windows.begin();
       iter != windows.end();
       ++iter) {
@@ -1051,19 +1057,28 @@
       ui::EF_LEFT_MOUSE_BUTTON |
       ui::EF_MIDDLE_MOUSE_BUTTON |
       ui::EF_RIGHT_MOUSE_BUTTON;
-  base::AutoReset<Window*> reset(&mouse_event_dispatch_target_, target);
+  // WARNING: because of nested message loops |this| may be deleted after
+  // dispatching any event. Do not use AutoReset or the like here.
+  WindowTracker destroyed_tracker;
+  destroyed_tracker.Add(this);
+  Window* old_mouse_event_dispatch_target = mouse_event_dispatch_target_;
+  mouse_event_dispatch_target_ = target;
   SetLastMouseLocation(this, event->location());
   synthesize_mouse_move_ = false;
   switch (event->type()) {
     case ui::ET_MOUSE_EXITED:
       if (!target) {
         DispatchMouseEnterOrExit(*event, ui::ET_MOUSE_EXITED);
+        if (!destroyed_tracker.Contains(this))
+          return false;
         mouse_moved_handler_ = NULL;
       }
       break;
     case ui::ET_MOUSE_MOVED:
       mouse_event_dispatch_target_ = target;
       HandleMouseMoved(*event, target);
+      if (!destroyed_tracker.Contains(this))
+        return false;
       if (mouse_event_dispatch_target_ != target)
         return false;
       break;
@@ -1085,14 +1100,20 @@
     default:
       break;
   }
+  bool result;
   if (target) {
     event->ConvertLocationToTarget(static_cast<Window*>(this), target);
     if (IsNonClientLocation(target, event->location()))
       event->set_flags(event->flags() | ui::EF_IS_NON_CLIENT);
     ProcessEvent(target, event);
-    return event->handled();
+    if (!destroyed_tracker.Contains(this))
+      return false;
+    result = event->handled();
+  } else {
+    result = false;
   }
-  return false;
+  mouse_event_dispatch_target_ = old_mouse_event_dispatch_target;
+  return result;
 }
 
 bool RootWindow::DispatchTouchEventImpl(ui::TouchEvent* event) {
diff --git a/ui/aura/test/event_generator.cc b/ui/aura/test/event_generator.cc
index fd6c2c6..d111220 100644
--- a/ui/aura/test/event_generator.cc
+++ b/ui/aura/test/event_generator.cc
@@ -145,6 +145,14 @@
   ReleaseButton(ui::EF_RIGHT_MOUSE_BUTTON);
 }
 
+void EventGenerator::SendMouseExit() {
+  gfx::Point exit_location(current_location_);
+  ConvertPointToTarget(current_root_window_, &exit_location);
+  ui::MouseEvent mouseev(ui::ET_MOUSE_EXITED, exit_location, exit_location,
+                         flags_);
+  Dispatch(&mouseev);
+}
+
 void EventGenerator::MoveMouseToInHost(const gfx::Point& point_in_host) {
   const ui::EventType event_type = (flags_ & ui::EF_LEFT_MOUSE_BUTTON) ?
       ui::ET_MOUSE_DRAGGED : ui::ET_MOUSE_MOVED;
diff --git a/ui/aura/test/event_generator.h b/ui/aura/test/event_generator.h
index a4ffca7..511dd77 100644
--- a/ui/aura/test/event_generator.h
+++ b/ui/aura/test/event_generator.h
@@ -135,6 +135,9 @@
   // Generates a right button release event.
   void ReleaseRightButton();
 
+  // Generates a mouse exit.
+  void SendMouseExit();
+
   // Generates events to move mouse to be the given |point| in the
   // |current_root_window_|'s host window coordinates.
   void MoveMouseToInHost(const gfx::Point& point_in_host);
diff --git a/ui/aura/test/test_cursor_client.cc b/ui/aura/test/test_cursor_client.cc
index 463589f..edf8ca0 100644
--- a/ui/aura/test/test_cursor_client.cc
+++ b/ui/aura/test/test_cursor_client.cc
@@ -36,6 +36,9 @@
                     OnCursorVisibilityChanged(false));
 }
 
+void TestCursorClient::SetCursorSet(ui::CursorSetType cursor_set) {
+}
+
 void TestCursorClient::SetScale(float scale) {
 }
 
diff --git a/ui/aura/test/test_cursor_client.h b/ui/aura/test/test_cursor_client.h
index 850ff68..c10552a 100644
--- a/ui/aura/test/test_cursor_client.h
+++ b/ui/aura/test/test_cursor_client.h
@@ -21,6 +21,7 @@
   virtual void SetCursor(gfx::NativeCursor cursor) OVERRIDE;
   virtual void ShowCursor() OVERRIDE;
   virtual void HideCursor() OVERRIDE;
+  virtual void SetCursorSet(ui::CursorSetType cursor_set) OVERRIDE;
   virtual void SetScale(float scale) OVERRIDE;
   virtual bool IsCursorVisible() const OVERRIDE;
   virtual void EnableMouseEvents() OVERRIDE;
diff --git a/ui/aura/test/test_screen.cc b/ui/aura/test/test_screen.cc
index 13d1232..c43ffb4 100644
--- a/ui/aura/test/test_screen.cc
+++ b/ui/aura/test/test_screen.cc
@@ -108,15 +108,22 @@
   return Env::GetInstance()->last_mouse_location();
 }
 
-gfx::NativeWindow TestScreen::GetWindowAtCursorScreenPoint() {
-  const gfx::Point point = GetCursorScreenPoint();
+gfx::NativeWindow TestScreen::GetWindowUnderCursor() {
+  return GetWindowAtScreenPoint(GetCursorScreenPoint());
+}
+
+gfx::NativeWindow TestScreen::GetWindowAtScreenPoint(const gfx::Point& point) {
   return root_window_->GetTopWindowContainingPoint(point);
 }
 
-int TestScreen::GetNumDisplays() {
+int TestScreen::GetNumDisplays() const {
   return 1;
 }
 
+std::vector<gfx::Display> TestScreen::GetAllDisplays() const {
+  return std::vector<gfx::Display>(1, display_);
+}
+
 gfx::Display TestScreen::GetDisplayNearestWindow(
     gfx::NativeWindow window) const {
   return display_;
diff --git a/ui/aura/test/test_screen.h b/ui/aura/test/test_screen.h
index c4615c7..c958279 100644
--- a/ui/aura/test/test_screen.h
+++ b/ui/aura/test/test_screen.h
@@ -47,8 +47,11 @@
   // gfx::Screen overrides:
   virtual bool IsDIPEnabled() OVERRIDE;
   virtual gfx::Point GetCursorScreenPoint() OVERRIDE;
-  virtual gfx::NativeWindow GetWindowAtCursorScreenPoint() OVERRIDE;
-  virtual int GetNumDisplays() OVERRIDE;
+  virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE;
+  virtual gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point)
+      OVERRIDE;
+  virtual int GetNumDisplays() const OVERRIDE;
+  virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE;
   virtual gfx::Display GetDisplayNearestWindow(
       gfx::NativeView view) const OVERRIDE;
   virtual gfx::Display GetDisplayNearestPoint(
diff --git a/ui/aura/window.cc b/ui/aura/window.cc
index e5289e4..5c7007e 100644
--- a/ui/aura/window.cc
+++ b/ui/aura/window.cc
@@ -224,9 +224,9 @@
 }
 
 void Window::SetTransparent(bool transparent) {
-  // Cannot change transparent flag after the window is initialized.
-  DCHECK(!layer());
   transparent_ = transparent;
+  if (layer())
+    layer_->SetFillsBoundsOpaquely(!transparent_);
 }
 
 RootWindow* Window::GetRootWindow() {
diff --git a/ui/base/base_window.cc b/ui/base/base_window.cc
new file mode 100644
index 0000000..231a177
--- /dev/null
+++ b/ui/base/base_window.cc
@@ -0,0 +1,16 @@
+// 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 "ui/base/base_window.h"
+
+namespace ui {
+
+bool BaseWindow::IsRestored(const BaseWindow& window) {
+  return !window.IsMaximized() &&
+     !window.IsMinimized() &&
+     !window.IsFullscreen();
+}
+
+}  // namespace ui
+
diff --git a/ui/base/base_window.h b/ui/base/base_window.h
index 4f847d1..d5fd2fa 100644
--- a/ui/base/base_window.h
+++ b/ui/base/base_window.h
@@ -19,7 +19,7 @@
 
 // Provides an interface to perform actions on windows, and query window
 // state.
-class BaseWindow {
+class UI_EXPORT BaseWindow {
  public:
   // Returns true if the window is currently the active/focused window.
   virtual bool IsActive() const = 0;
@@ -33,6 +33,10 @@
   // Returns true if the window is full screen.
   virtual bool IsFullscreen() const = 0;
 
+  // Returns true if the window is fully restored (not Fullscreen, Maximized,
+  // Minimized).
+  static bool IsRestored(const BaseWindow& window);
+
   // Return a platform dependent identifier for this window.
   virtual gfx::NativeWindow GetNativeWindow() = 0;
 
diff --git a/ui/base/cocoa/controls/hyperlink_button_cell.mm b/ui/base/cocoa/controls/hyperlink_button_cell.mm
index c723b03..57b7077 100644
--- a/ui/base/cocoa/controls/hyperlink_button_cell.mm
+++ b/ui/base/cocoa/controls/hyperlink_button_cell.mm
@@ -52,6 +52,14 @@
   return self;
 }
 
+- (id)copyWithZone:(NSZone*)zone {
+  NSColor* color = textColor_.release();
+  HyperlinkButtonCell* cell = [super copyWithZone:zone];
+  cell->textColor_.reset([color copy]);
+  textColor_.reset(color);
+  return cell;
+}
+
 // Because an NSButtonCell has multiple initializers, this method performs the
 // common cell customization code.
 - (void)customizeButtonCell {
diff --git a/ui/base/cocoa/controls/hyperlink_button_cell_unittest.mm b/ui/base/cocoa/controls/hyperlink_button_cell_unittest.mm
index 479141b..3d24f01 100644
--- a/ui/base/cocoa/controls/hyperlink_button_cell_unittest.mm
+++ b/ui/base/cocoa/controls/hyperlink_button_cell_unittest.mm
@@ -8,6 +8,7 @@
 
 #include "base/mac/foundation_util.h"
 #include "base/mac/scoped_nsobject.h"
+#import "testing/gtest_mac.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 #import "ui/base/test/ui_cocoa_test_helper.h"
@@ -115,6 +116,17 @@
   EXPECT_FALSE(HasUnderlineAttribute([cell_ linkAttributes]));
 }
 
+TEST_F(HyperlinkButtonCellTest, Copy) {
+  base::scoped_nsobject<HyperlinkButtonCell> cell1([[HyperlinkButtonCell alloc]
+      initTextCell:@"Cell"]);
+  [cell1 setTextColor:[NSColor redColor]];
+
+  base::scoped_nsobject<HyperlinkButtonCell> cell2([cell1 copy]);
+  EXPECT_NSEQ([cell1 textColor], [cell2 textColor]);
+  [cell1 setTextColor:[NSColor purpleColor]];
+  [cell2 setTextColor:[NSColor greenColor]];
+}
+
 }  // namespace
 
 }  // namespace ui
diff --git a/ui/base/cursor/cursor.h b/ui/base/cursor/cursor.h
index 43c201d..bb832fa 100644
--- a/ui/base/cursor/cursor.h
+++ b/ui/base/cursor/cursor.h
@@ -82,6 +82,11 @@
 const int kCursorGrabbing = 43;
 const int kCursorCustom = 44;
 
+enum CursorSetType {
+  CURSOR_SET_NORMAL,
+  CURSOR_SET_LARGE
+};
+
 // Ref-counted cursor that supports both default and custom cursors.
 class UI_EXPORT Cursor {
  public:
diff --git a/ui/base/cursor/cursors_aura.cc b/ui/base/cursor/cursors_aura.cc
index 17349a7..bb5947a 100644
--- a/ui/base/cursor/cursors_aura.cc
+++ b/ui/base/cursor/cursors_aura.cc
@@ -5,7 +5,6 @@
 #include "ui/base/cursor/cursors_aura.h"
 
 #include "grit/ui_resources.h"
-#include "ui/base/cursor/cursor.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/point.h"
 
@@ -24,7 +23,15 @@
   HotPoint hot_2x;
 };
 
-const CursorData kImageCursors[] = {
+struct CursorSet {
+  const CursorSetType id;
+  const CursorData* cursors;
+  const int length;
+  const CursorData* animated_cursors;
+  const int animated_length;
+};
+
+const CursorData kNormalCursors[] = {
   {ui::kCursorNull, IDR_AURA_CURSOR_PTR, {4, 4}, {8, 9}},
   {ui::kCursorPointer, IDR_AURA_CURSOR_PTR, {4, 4}, {8, 9}},
   {ui::kCursorNoDrop, IDR_AURA_CURSOR_NO_DROP, {9, 9}, {18, 18}},
@@ -67,11 +74,80 @@
   {ui::kCursorGrabbing, IDR_AURA_CURSOR_GRABBING, {9, 9}, {18, 18}},
 };
 
+const CursorData kLargeCursors[] = {
+  {ui::kCursorNull, IDR_AURA_CURSOR_BIG_PTR, {10, 10}, {10, 10}},
+  {ui::kCursorPointer, IDR_AURA_CURSOR_BIG_PTR, {10, 10}, {10, 10}},
+  {ui::kCursorNoDrop, IDR_AURA_CURSOR_BIG_NO_DROP, {23, 23}, {23, 23}},
+  {ui::kCursorNotAllowed, IDR_AURA_CURSOR_BIG_NO_DROP, {23, 23}, {23, 23}},
+  {ui::kCursorCopy, IDR_AURA_CURSOR_BIG_COPY, {23, 23}, {23, 23}},
+  {ui::kCursorHand, IDR_AURA_CURSOR_BIG_HAND, {23, 10}, {23, 10}},
+  {ui::kCursorMove, IDR_AURA_CURSOR_BIG_MOVE, {28, 28}, {28, 28}},
+  {ui::kCursorNorthEastResize, IDR_AURA_CURSOR_BIG_NORTH_EAST_RESIZE,
+   {31, 28}, {31, 28}},
+  {ui::kCursorSouthWestResize, IDR_AURA_CURSOR_BIG_SOUTH_WEST_RESIZE,
+   {31, 28}, {31, 28}},
+  {ui::kCursorSouthEastResize, IDR_AURA_CURSOR_BIG_SOUTH_EAST_RESIZE,
+   {28, 28}, {28, 28}},
+  {ui::kCursorNorthWestResize, IDR_AURA_CURSOR_BIG_NORTH_WEST_RESIZE,
+   {28, 28}, {28, 28}},
+  {ui::kCursorNorthResize, IDR_AURA_CURSOR_BIG_NORTH_RESIZE,
+   {28, 31}, {28, 31}},
+  {ui::kCursorSouthResize, IDR_AURA_CURSOR_BIG_SOUTH_RESIZE,
+   {28, 31}, {28, 31}},
+  {ui::kCursorEastResize, IDR_AURA_CURSOR_BIG_EAST_RESIZE, {31, 28}, {31, 28}},
+  {ui::kCursorWestResize, IDR_AURA_CURSOR_BIG_WEST_RESIZE, {31, 28}, {31, 28}},
+  {ui::kCursorIBeam, IDR_AURA_CURSOR_BIG_IBEAM, {31, 31}, {31, 31}},
+  {ui::kCursorAlias, IDR_AURA_CURSOR_BIG_ALIAS, {51, 15}, {51, 11}},
+  {ui::kCursorCell, IDR_AURA_CURSOR_BIG_CELL, {28, 28}, {24, 23}},
+  {ui::kCursorContextMenu, IDR_AURA_CURSOR_BIG_CONTEXT_MENU, {4, 4}, {8, 9}},
+  {ui::kCursorCross, IDR_AURA_CURSOR_BIG_CROSSHAIR, {31, 31}, {31, 31}},
+  {ui::kCursorHelp, IDR_AURA_CURSOR_BIG_HELP, {10, 10}, {8, 9}},
+  {ui::kCursorVerticalText, IDR_AURA_CURSOR_BIG_XTERM_HORIZ,
+   {31, 28}, {31, 28}},
+  {ui::kCursorZoomIn, IDR_AURA_CURSOR_BIG_ZOOM_IN, {26, 26}, {26, 26}},
+  {ui::kCursorZoomOut, IDR_AURA_CURSOR_BIG_ZOOM_OUT, {26, 26}, {26, 26}},
+  {ui::kCursorRowResize, IDR_AURA_CURSOR_BIG_ROW_RESIZE, {28, 31}, {28, 31}},
+  {ui::kCursorColumnResize, IDR_AURA_CURSOR_BIG_COL_RESIZE, {31, 28}, {31, 28}},
+  {ui::kCursorEastWestResize, IDR_AURA_CURSOR_BIG_EAST_WEST_RESIZE,
+   {31, 28}, {31, 28}},
+  {ui::kCursorNorthSouthResize, IDR_AURA_CURSOR_BIG_NORTH_SOUTH_RESIZE,
+   {28, 31}, {28, 31}},
+  {ui::kCursorNorthEastSouthWestResize,
+   IDR_AURA_CURSOR_BIG_NORTH_EAST_SOUTH_WEST_RESIZE, {31, 28}, {31, 28}},
+  {ui::kCursorNorthWestSouthEastResize,
+   IDR_AURA_CURSOR_BIG_NORTH_WEST_SOUTH_EAST_RESIZE, {28, 28}, {28, 28}},
+  {ui::kCursorGrab, IDR_AURA_CURSOR_BIG_GRAB, {20, 13}, {20, 13}},
+  {ui::kCursorGrabbing, IDR_AURA_CURSOR_BIG_GRABBING, {23, 23}, {23, 23}},
+};
+
 const CursorData kAnimatedCursors[] = {
   {ui::kCursorWait, IDR_THROBBER, {7, 7}, {14, 14}},
   {ui::kCursorProgress, IDR_THROBBER, {7, 7}, {14, 14}},
 };
 
+const CursorSet kCursorSets[] = {
+  {
+    CURSOR_SET_NORMAL,
+    kNormalCursors, arraysize(kNormalCursors),
+    kAnimatedCursors, arraysize(kAnimatedCursors)
+  },
+  {
+    CURSOR_SET_LARGE,
+    kLargeCursors, arraysize(kLargeCursors),
+    // TODO(yoshiki): Replace animated cursors with big assets. crbug.com/247254
+    kAnimatedCursors, arraysize(kAnimatedCursors)
+  },
+};
+
+const CursorSet* GetCursorSetByType(CursorSetType cursor_set_id) {
+  for (size_t i = 0; i < arraysize(kCursorSets); ++i) {
+    if (kCursorSets[i].id == cursor_set_id)
+      return &kCursorSets[i];
+  }
+
+  return NULL;
+}
+
 bool SearchTable(const CursorData* table,
                  size_t table_length,
                  int id,
@@ -96,19 +172,45 @@
 
 }  // namespace
 
-bool GetCursorDataFor(int id,
+bool GetCursorDataFor(CursorSetType cursor_set_id,
+                      int id,
                       float scale_factor,
                       int* resource_id,
                       gfx::Point* point) {
-  return SearchTable(kImageCursors, arraysize(kImageCursors),
+  const CursorSet* cursor_set = GetCursorSetByType(cursor_set_id);
+  if (cursor_set &&
+      SearchTable(cursor_set->cursors,
+                  cursor_set->length,
+                  id, scale_factor, resource_id, point)) {
+      return true;
+  }
+
+  // Falls back to the default cursor set.
+  cursor_set = GetCursorSetByType(ui::CURSOR_SET_NORMAL);
+  DCHECK(cursor_set);
+  return SearchTable(cursor_set->cursors,
+                     cursor_set->length,
                      id, scale_factor, resource_id, point);
 }
 
-bool GetAnimatedCursorDataFor(int id,
+bool GetAnimatedCursorDataFor(CursorSetType cursor_set_id,
+                              int id,
                               float scale_factor,
                               int* resource_id,
                               gfx::Point* point) {
-  return SearchTable(kAnimatedCursors, arraysize(kAnimatedCursors),
+  const CursorSet* cursor_set = GetCursorSetByType(cursor_set_id);
+  if (cursor_set &&
+      SearchTable(cursor_set->animated_cursors,
+                  cursor_set->animated_length,
+                  id, scale_factor, resource_id, point)) {
+    return true;
+  }
+
+  // Falls back to the default cursor set.
+  cursor_set = GetCursorSetByType(ui::CURSOR_SET_NORMAL);
+  DCHECK(cursor_set);
+  return SearchTable(cursor_set->animated_cursors,
+                     cursor_set->animated_length,
                      id, scale_factor, resource_id, point);
 }
 
diff --git a/ui/base/cursor/cursors_aura.h b/ui/base/cursor/cursors_aura.h
index 4565a70..e916c28 100644
--- a/ui/base/cursor/cursors_aura.h
+++ b/ui/base/cursor/cursors_aura.h
@@ -5,6 +5,7 @@
 #ifndef UI_BASE_CURSOR_CURSORS_AURA_H_
 #define UI_BASE_CURSOR_CURSORS_AURA_H_
 
+#include "ui/base/cursor/cursor.h"
 #include "ui/base/ui_export.h"
 
 namespace gfx {
@@ -19,13 +20,15 @@
 // ui::kCursorHelp. The IDR will be placed in |resource_id| and the hotspots
 // for the different DPIs will be placed in |hot_1x| and |hot_2x|. Returns
 // false if |id| is invalid.
-bool UI_EXPORT GetCursorDataFor(int id,
+bool UI_EXPORT GetCursorDataFor(CursorSetType cursor_set_id,
+                                int id,
                                 float scale_factor,
                                 int* resource_id,
                                 gfx::Point* point);
 
 // Like above, but for animated cursors.
-bool UI_EXPORT GetAnimatedCursorDataFor(int id,
+bool UI_EXPORT GetAnimatedCursorDataFor(CursorSetType cursor_set_id,
+                                        int id,
                                         float scale_factor,
                                         int* resource_id,
                                         gfx::Point* point);
diff --git a/ui/base/default_theme_provider.cc b/ui/base/default_theme_provider.cc
index e60c900..9777b39 100644
--- a/ui/base/default_theme_provider.cc
+++ b/ui/base/default_theme_provider.cc
@@ -26,8 +26,8 @@
   return 0xff0000ff;
 }
 
-bool DefaultThemeProvider::GetDisplayProperty(int id, int* result) const {
-  return false;
+int DefaultThemeProvider::GetDisplayProperty(int id) const {
+  return -1;
 }
 
 bool DefaultThemeProvider::ShouldUseNativeFrame() const {
diff --git a/ui/base/default_theme_provider.h b/ui/base/default_theme_provider.h
index 2594dfb..15fa4a9 100644
--- a/ui/base/default_theme_provider.h
+++ b/ui/base/default_theme_provider.h
@@ -26,7 +26,7 @@
   // Overridden from ui::ThemeProvider:
   virtual gfx::ImageSkia* GetImageSkiaNamed(int id) const OVERRIDE;
   virtual SkColor GetColor(int id) const OVERRIDE;
-  virtual bool GetDisplayProperty(int id, int* result) const OVERRIDE;
+  virtual int GetDisplayProperty(int id) const OVERRIDE;
   virtual bool ShouldUseNativeFrame() const OVERRIDE;
   virtual bool HasCustomImage(int id) const OVERRIDE;
   virtual base::RefCountedMemory* GetRawData(
diff --git a/ui/base/gestures/gesture_recognizer.h b/ui/base/gestures/gesture_recognizer.h
index b32bade..2affc7a 100644
--- a/ui/base/gestures/gesture_recognizer.h
+++ b/ui/base/gestures/gesture_recognizer.h
@@ -25,7 +25,7 @@
   virtual ~GestureRecognizer() {}
 
   // Invoked for each touch event that could contribute to the current gesture.
-  // Returns list of  zero or more GestureEvents identified after processing
+  // Returns list of zero or more GestureEvents identified after processing
   // TouchEvent.
   // Caller would be responsible for freeing up Gestures.
   virtual Gestures* ProcessTouchEventForGesture(const TouchEvent& event,
diff --git a/ui/base/ime/dummy_input_method.cc b/ui/base/ime/dummy_input_method.cc
index f762e3b..b9f6941 100644
--- a/ui/base/ime/dummy_input_method.cc
+++ b/ui/base/ime/dummy_input_method.cc
@@ -72,6 +72,10 @@
   return TEXT_INPUT_TYPE_NONE;
 }
 
+TextInputMode DummyInputMethod::GetTextInputMode() const {
+  return TEXT_INPUT_MODE_DEFAULT;
+}
+
 bool DummyInputMethod::CanComposeInline() const {
   return true;
 }
diff --git a/ui/base/ime/dummy_input_method.h b/ui/base/ime/dummy_input_method.h
index 2276ae5..165bc52 100644
--- a/ui/base/ime/dummy_input_method.h
+++ b/ui/base/ime/dummy_input_method.h
@@ -36,6 +36,7 @@
   virtual base::i18n::TextDirection GetInputTextDirection() OVERRIDE;
   virtual bool IsActive() OVERRIDE;
   virtual TextInputType GetTextInputType() const OVERRIDE;
+  virtual TextInputMode GetTextInputMode() const OVERRIDE;
   virtual bool CanComposeInline() const OVERRIDE;
   virtual bool IsCandidatePopupOpen() const OVERRIDE;
   virtual void AddObserver(InputMethodObserver* observer) OVERRIDE;
diff --git a/ui/base/ime/fake_input_method.cc b/ui/base/ime/fake_input_method.cc
index 1cf236d..15f1283 100644
--- a/ui/base/ime/fake_input_method.cc
+++ b/ui/base/ime/fake_input_method.cc
@@ -141,8 +141,12 @@
   return false;
 }
 
-ui::TextInputType FakeInputMethod::GetTextInputType() const {
-  return ui::TEXT_INPUT_TYPE_NONE;
+TextInputType FakeInputMethod::GetTextInputType() const {
+  return TEXT_INPUT_TYPE_NONE;
+}
+
+TextInputMode FakeInputMethod::GetTextInputMode() const {
+  return TEXT_INPUT_MODE_DEFAULT;
 }
 
 bool FakeInputMethod::CanComposeInline() const {
diff --git a/ui/base/ime/fake_input_method.h b/ui/base/ime/fake_input_method.h
index 12f9aac..e80d2bc 100644
--- a/ui/base/ime/fake_input_method.h
+++ b/ui/base/ime/fake_input_method.h
@@ -44,7 +44,8 @@
   virtual std::string GetInputLocale() OVERRIDE;
   virtual base::i18n::TextDirection GetInputTextDirection() OVERRIDE;
   virtual bool IsActive() OVERRIDE;
-  virtual ui::TextInputType GetTextInputType() const OVERRIDE;
+  virtual TextInputType GetTextInputType() const OVERRIDE;
+  virtual TextInputMode GetTextInputMode() const OVERRIDE;
   virtual bool CanComposeInline() const OVERRIDE;
   virtual bool IsCandidatePopupOpen() const OVERRIDE;
   virtual void AddObserver(InputMethodObserver* observer) OVERRIDE;
diff --git a/ui/base/ime/input_method.h b/ui/base/ime/input_method.h
index 575cbcd..0d4046f 100644
--- a/ui/base/ime/input_method.h
+++ b/ui/base/ime/input_method.h
@@ -10,6 +10,7 @@
 #include "base/basictypes.h"
 #include "base/event_types.h"
 #include "base/i18n/rtl.h"
+#include "ui/base/ime/text_input_mode.h"
 #include "ui/base/ime/text_input_type.h"
 #include "ui/base/keycodes/keyboard_codes.h"
 #include "ui/base/ui_export.h"
@@ -140,10 +141,18 @@
   // is not active.
   virtual bool IsActive() = 0;
 
+  // TODO(yoichio): Following 3 methods(GetTextInputType, GetTextInputMode and
+  // CanComposeInline) calls client's same method and returns its value. It is
+  // not InputMethod itself's infomation. So rename these to
+  // GetClientTextInputType and so on.
   // Gets the text input type of the focused text input client. Returns
   // ui::TEXT_INPUT_TYPE_NONE if there is no focused client.
   virtual TextInputType GetTextInputType() const = 0;
 
+  // Gets the text input mode of the focused text input client. Returns
+  // ui::TEXT_INPUT_TYPE_DEFAULT if there is no focused client.
+  virtual TextInputMode GetTextInputMode() const = 0;
+
   // Checks if the focused text input client supports inline composition.
   virtual bool CanComposeInline() const = 0;
 
diff --git a/ui/base/ime/input_method_base.cc b/ui/base/ime/input_method_base.cc
index 6972eed..23d21ba 100644
--- a/ui/base/ime/input_method_base.cc
+++ b/ui/base/ime/input_method_base.cc
@@ -67,6 +67,11 @@
   return client ? client->GetTextInputType() : TEXT_INPUT_TYPE_NONE;
 }
 
+TextInputMode InputMethodBase::GetTextInputMode() const {
+  TextInputClient* client = GetTextInputClient();
+  return client ? client->GetTextInputMode() : TEXT_INPUT_MODE_DEFAULT;
+}
+
 bool InputMethodBase::CanComposeInline() const {
   TextInputClient* client = GetTextInputClient();
   return client ? client->CanComposeInline() : true;
diff --git a/ui/base/ime/input_method_base.h b/ui/base/ime/input_method_base.h
index e467e0b..655fd4a 100644
--- a/ui/base/ime/input_method_base.h
+++ b/ui/base/ime/input_method_base.h
@@ -44,6 +44,7 @@
   virtual void OnTextInputTypeChanged(const TextInputClient* client) OVERRIDE;
 
   virtual TextInputType GetTextInputType() const OVERRIDE;
+  virtual TextInputMode GetTextInputMode() const OVERRIDE;
   virtual bool CanComposeInline() const OVERRIDE;
 
   virtual void AddObserver(InputMethodObserver* observer) OVERRIDE;
diff --git a/ui/base/ime/input_method_tsf.cc b/ui/base/ime/input_method_tsf.cc
index 21a57a8..b9e6c7c 100644
--- a/ui/base/ime/input_method_tsf.cc
+++ b/ui/base/ime/input_method_tsf.cc
@@ -117,13 +117,15 @@
 
 void InputMethodTSF::SetFocusedTextInputClient(TextInputClient* client) {
   if (IsWindowFocused(client)) {
-    ui::TSFBridge::GetInstance()->SetFocusedClient(
-        GetAttachedWindowHandle(client), client);
-  } else if (!client) {
-    // SetFocusedTextInputClient(NULL) must be interpreted as
-    // "Remove the attached client".
-    ui::TSFBridge::GetInstance()->RemoveFocusedClient(
-        ui::TSFBridge::GetInstance()->GetFocusedTextInputClient());
+    if (IsTextInputClientFocused(client)) {
+      ui::TSFBridge::GetInstance()->SetFocusedClient(
+          GetAttachedWindowHandle(client), client);
+    } else {
+      // SetFocusedTextInputClient(NULL) must be interpreted as
+      // "Remove the attached client".
+      ui::TSFBridge::GetInstance()->RemoveFocusedClient(
+          ui::TSFBridge::GetInstance()->GetFocusedTextInputClient());
+    }
   }
   InputMethodWin::SetFocusedTextInputClient(client);
 }
@@ -142,9 +144,10 @@
 
 void InputMethodTSF::OnDidChangeFocusedClient(TextInputClient* focused_before,
                                               TextInputClient* focused) {
-  if (IsWindowFocused(focused)) {
+  if (IsWindowFocused(focused) && IsTextInputClientFocused(focused)) {
     ui::TSFBridge::GetInstance()->SetFocusedClient(
         GetAttachedWindowHandle(focused), focused);
+
     // Force to update the input type since client's TextInputStateChanged()
     // function might not be called if text input types before the client loses
     // focus and after it acquires focus again are the same.
@@ -162,8 +165,6 @@
 }
 
 bool InputMethodTSF::IsWindowFocused(const TextInputClient* client) const {
-  if (!client)
-    return false;
   HWND attached_window_handle = GetAttachedWindowHandle(client);
   return attached_window_handle && GetFocus() == attached_window_handle;
 }
diff --git a/ui/base/ime/mock_input_method.cc b/ui/base/ime/mock_input_method.cc
index 7701b5c..aed83c2 100644
--- a/ui/base/ime/mock_input_method.cc
+++ b/ui/base/ime/mock_input_method.cc
@@ -83,8 +83,12 @@
   return true;
 }
 
-ui::TextInputType MockInputMethod::GetTextInputType() const {
-  return ui::TEXT_INPUT_TYPE_NONE;
+TextInputType MockInputMethod::GetTextInputType() const {
+  return TEXT_INPUT_TYPE_NONE;
+}
+
+TextInputMode MockInputMethod::GetTextInputMode() const {
+  return TEXT_INPUT_MODE_DEFAULT;
 }
 
 bool MockInputMethod::CanComposeInline() const {
diff --git a/ui/base/ime/mock_input_method.h b/ui/base/ime/mock_input_method.h
index f318ca4..fdeff5e 100644
--- a/ui/base/ime/mock_input_method.h
+++ b/ui/base/ime/mock_input_method.h
@@ -60,6 +60,7 @@
   virtual base::i18n::TextDirection GetInputTextDirection() OVERRIDE;
   virtual bool IsActive() OVERRIDE;
   virtual TextInputType GetTextInputType() const OVERRIDE;
+  virtual TextInputMode GetTextInputMode() const OVERRIDE;
   virtual bool CanComposeInline() const OVERRIDE;
   virtual bool IsCandidatePopupOpen() const OVERRIDE;
   virtual void AddObserver(InputMethodObserver* observer) OVERRIDE;
diff --git a/ui/base/ime/win/tsf_text_store.cc b/ui/base/ime/win/tsf_text_store.cc
index 27739ad..ce56689 100644
--- a/ui/base/ime/win/tsf_text_store.cc
+++ b/ui/base/ime/win/tsf_text_store.cc
@@ -610,7 +610,8 @@
   attribute_buffer[0].idAttr = GUID_PROP_INPUTSCOPE;
   attribute_buffer[0].varValue.vt = VT_UNKNOWN;
   attribute_buffer[0].varValue.punkVal = tsf_inputscope::CreateInputScope(
-      text_input_client_->GetTextInputType(), TEXT_INPUT_MODE_DEFAULT);
+      text_input_client_->GetTextInputType(),
+      text_input_client_->GetTextInputMode());
   attribute_buffer[0].varValue.punkVal->AddRef();
   *attribute_buffer_copied = 1;
   return S_OK;
diff --git a/ui/base/models/simple_menu_model.cc b/ui/base/models/simple_menu_model.cc
index 2687822..a2fa4bb 100644
--- a/ui/base/models/simple_menu_model.cc
+++ b/ui/base/models/simple_menu_model.cc
@@ -135,6 +135,7 @@
 void SimpleMenuModel::RemoveTrailingSeparators() {
   while (!items_.empty() && items_.back().type == TYPE_SEPARATOR)
     items_.pop_back();
+  MenuItemsChanged();
 }
 
 void SimpleMenuModel::AddButtonItem(int command_id,
@@ -227,14 +228,17 @@
 
 void SimpleMenuModel::SetIcon(int index, const gfx::Image& icon) {
   items_[ValidateItemIndex(index)].icon = icon;
+  MenuItemsChanged();
 }
 
 void SimpleMenuModel::SetSublabel(int index, const base::string16& sublabel) {
   items_[ValidateItemIndex(index)].sublabel = sublabel;
+  MenuItemsChanged();
 }
 
 void SimpleMenuModel::Clear() {
   items_.clear();
+  MenuItemsChanged();
 }
 
 int SimpleMenuModel::GetIndexOfCommandId(int command_id) {
@@ -390,6 +394,12 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// SimpleMenuModel, Protected:
+
+void SimpleMenuModel::MenuItemsChanged() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // SimpleMenuModel, Private:
 
 int SimpleMenuModel::ValidateItemIndex(int index) const {
@@ -401,11 +411,13 @@
 void SimpleMenuModel::AppendItem(const Item& item) {
   ValidateItem(item);
   items_.push_back(item);
+  MenuItemsChanged();
 }
 
 void SimpleMenuModel::InsertItemAtIndex(const Item& item, int index) {
   ValidateItem(item);
   items_.insert(items_.begin() + index, item);
+  MenuItemsChanged();
 }
 
 void SimpleMenuModel::ValidateItem(const Item& item) {
diff --git a/ui/base/models/simple_menu_model.h b/ui/base/models/simple_menu_model.h
index 2204b98..6582dfe 100644
--- a/ui/base/models/simple_menu_model.h
+++ b/ui/base/models/simple_menu_model.h
@@ -164,6 +164,10 @@
   void set_delegate(Delegate* delegate) { delegate_ = delegate; }
   Delegate* delegate() { return delegate_; }
 
+  // One or more of the menu menu items associated with the model has changed.
+  // Do any handling if necessary.
+  virtual void MenuItemsChanged();
+
  private:
   struct Item;
 
diff --git a/ui/base/ozone/surface_factory_ozone.h b/ui/base/ozone/surface_factory_ozone.h
index 5df6bd9..0febe36 100644
--- a/ui/base/ozone/surface_factory_ozone.h
+++ b/ui/base/ozone/surface_factory_ozone.h
@@ -12,7 +12,7 @@
 namespace gfx {
 class Screen;
 class VSyncProvider;
-} //  namespace gfx
+}  // namespace gfx
 
 namespace ui {
 
diff --git a/ui/base/range/range.cc b/ui/base/range/range.cc
index 7532564..a76ddba 100644
--- a/ui/base/range/range.cc
+++ b/ui/base/range/range.cc
@@ -80,4 +80,8 @@
   return base::StringPrintf("{%" PRIuS ",%" PRIuS "}", start(), end());
 }
 
-}  // namespace gfx
+std::ostream& operator<<(std::ostream& os, const Range& range) {
+  return os << range.ToString();
+}
+
+}  // namespace ui
diff --git a/ui/base/range/range.h b/ui/base/range/range.h
index b8bf478..c425da7 100644
--- a/ui/base/range/range.h
+++ b/ui/base/range/range.h
@@ -5,6 +5,7 @@
 #ifndef UI_BASE_RANGE_RANGE_H_
 #define UI_BASE_RANGE_RANGE_H_
 
+#include <ostream>
 #include <string>
 
 #include "base/basictypes.h"
@@ -109,6 +110,8 @@
   size_t end_;
 };
 
+UI_EXPORT std::ostream& operator<<(std::ostream& os, const Range& range);
+
 }  // namespace ui
 
 #endif  // UI_BASE_RANGE_RANGE_H_
diff --git a/ui/base/strings/ui_strings.grd b/ui/base/strings/ui_strings.grd
index fd4eb5a..e412ed6 100644
--- a/ui/base/strings/ui_strings.grd
+++ b/ui/base/strings/ui_strings.grd
@@ -1515,11 +1515,8 @@
       <message name="IDS_MESSAGE_CENTER_ACCESSIBLE_NAME" desc="The accessible name for the Notification Center window.">
         Notification Center
       </message>
-      <message name="IDS_MESSAGE_CENTER_EXTENSIONS_DISABLE" desc="The menu entry for disabling extensions from a notification.">
-        Disable notifications from <ph name="extension_name">$1<ex>Notification Galore!</ex></ph>
-      </message>
-      <message name="IDS_MESSAGE_CENTER_SITE_DISABLE" desc="The menu entry for disabling notification from a site.">
-        Disable notifications from <ph name="site">$1<ex>mail.google.com</ex></ph>
+      <message name="IDS_MESSAGE_CENTER_NOTIFIER_DISABLE" desc="The menu entry for disabling a notifier from a notification.">
+        Disable notifications from <ph name="notifier_name">$1<ex>Notification Galore!</ex></ph>
       </message>
       <message name="IDS_MESSAGE_CENTER_FOOTER_TITLE" desc="The label in the footer of the message center">
 	Notifications
diff --git a/ui/base/theme_provider.h b/ui/base/theme_provider.h
index 22f70a0..d12e265 100644
--- a/ui/base/theme_provider.h
+++ b/ui/base/theme_provider.h
@@ -59,7 +59,7 @@
 
   // Get the property (e.g. an alignment expressed in an enum, or a width or
   // height) specified by |id|.
-  virtual bool GetDisplayProperty(int id, int* result) const = 0;
+  virtual int GetDisplayProperty(int id) const = 0;
 
   // Whether we should use the native system frame (typically Aero glass) or
   // a custom frame.
diff --git a/ui/base/win/dpi.cc b/ui/base/win/dpi.cc
index 6380459..cbb5890 100644
--- a/ui/base/win/dpi.cc
+++ b/ui/base/win/dpi.cc
@@ -7,6 +7,7 @@
 #include <windows.h>
 #include "base/command_line.h"
 #include "base/win/scoped_hdc.h"
+#include "base/win/windows_version.h"
 #include "ui/base/layout.h"
 #include "base/win/registry.h"
 #include "ui/base/ui_base_switches.h"
@@ -162,6 +163,15 @@
   return scale;
 }
 
+
+double GetUndocumentedDPITouchScale() {
+  static double scale =
+      (base::win::GetVersion() < base::win::VERSION_WIN8_1) ?
+      GetUndocumentedDPIScale() : 1.0;
+  return scale;
+}
+
+
 }  // namespace win
 
 }  // namespace ui
diff --git a/ui/base/win/dpi.h b/ui/base/win/dpi.h
index 90edc7c..86a3883 100644
--- a/ui/base/win/dpi.h
+++ b/ui/base/win/dpi.h
@@ -44,13 +44,19 @@
 // GetSystemMetrics for the given |metric|, then converts the result to DIP.
 UI_EXPORT int GetSystemMetricsInDIP(int metric);
 
-// The OS secretly scales apps that are not DPIAware. This is not visible
-// through standard OS calls like GetWindowPos(), or through GetDPIScale().
+// Sometimes the OS secretly scales apps that are not DPIAware. This is not
+// visible through standard OS calls like GetWindowPos(), or through
+// GetDPIScale().
 // Returns the scale factor of the display, where 96 DPI is 1.0.
 // (Avoid this function... use GetDPIScale() instead.)
 // TODO(girard): Remove this once DPIAware is enabled - http://crbug.com/149881
 UI_EXPORT double GetUndocumentedDPIScale();
 
+// Win7 and Win8 send touch events scaled according to the current DPI
+// scaling. Win8.1 corrects this, and sends touch events in DPI units.
+// This function returns the appropriate scaling factor for touch events.
+UI_EXPORT double GetUndocumentedDPITouchScale();
+
 }  // namespace win
 
 }  // namespace ui
diff --git a/ui/base/win/events_win.cc b/ui/base/win/events_win.cc
index 6cabff9..bcd7272 100644
--- a/ui/base/win/events_win.cc
+++ b/ui/base/win/events_win.cc
@@ -357,6 +357,8 @@
     modifiers |= EF_CONTROL_DOWN;
   if (base::win::IsAltPressed())
     modifiers |= EF_ALT_DOWN;
+  if (base::win::IsAltGrPressed())
+    modifiers |= EF_ALTGR_DOWN;
   return modifiers;
 }
 
diff --git a/ui/base/win/hwnd_subclass.cc b/ui/base/win/hwnd_subclass.cc
index c1133f0..fc4f6c9 100644
--- a/ui/base/win/hwnd_subclass.cc
+++ b/ui/base/win/hwnd_subclass.cc
@@ -143,8 +143,10 @@
     if (GetTouchInputInfoWrapper(reinterpret_cast<HTOUCHINPUT>(l_param), 1,
                                  &point, sizeof(TOUCHINPUT))) {
       POINT touch_location = {
-        TOUCH_COORD_TO_PIXEL(point.x) / ui::win::GetUndocumentedDPIScale(),
-        TOUCH_COORD_TO_PIXEL(point.y) / ui::win::GetUndocumentedDPIScale()};
+          TOUCH_COORD_TO_PIXEL(point.x) /
+          ui::win::GetUndocumentedDPITouchScale(),
+          TOUCH_COORD_TO_PIXEL(point.y) /
+          ui::win::GetUndocumentedDPITouchScale()};
       HWND actual_target = WindowFromPoint(touch_location);
       if (actual_target != hwnd) {
         return SendMessage(actual_target, message, w_param, l_param);
diff --git a/ui/compositor/DEPS b/ui/compositor/DEPS
index 66c3932..7a2d98b 100644
--- a/ui/compositor/DEPS
+++ b/ui/compositor/DEPS
@@ -4,6 +4,5 @@
 include_rules = [
   "+cc",
   "+third_party/WebKit/public/platform/WebGraphicsContext3D.h",
-  "+third_party/WebKit/public/platform/WebString.h",
   "+webkit/common/gpu",
 ]
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc
index f5bf42c..c464ed7 100644
--- a/ui/compositor/layer.cc
+++ b/ui/compositor/layer.cc
@@ -444,6 +444,7 @@
     DCHECK(parent_->cc_layer_);
     parent_->cc_layer_->ReplaceChild(cc_layer_, new_layer);
   }
+  cc_layer_->SetLayerClient(NULL);
   cc_layer_->RemoveLayerAnimationEventObserver(this);
   new_layer->SetOpacity(cc_layer_->opacity());
   new_layer->SetTransform(cc_layer_->transform());
@@ -460,6 +461,7 @@
     DCHECK(children_[i]->cc_layer_);
     cc_layer_->AddChild(children_[i]->cc_layer_);
   }
+  cc_layer_->SetLayerClient(this);
   cc_layer_->SetAnchorPoint(gfx::PointF());
   cc_layer_->SetContentsOpaque(fills_bounds_opaquely_);
   cc_layer_->SetForceRenderSurface(force_render_surface_);
@@ -675,6 +677,10 @@
   cc_layer_->SetForceRenderSurface(force_render_surface_);
 }
 
+std::string Layer::DebugName() {
+  return name_;
+}
+
 void Layer::OnAnimationStarted(const cc::AnimationEvent& event) {
   if (animator_.get())
     animator_->OnThreadedAnimationStarted(event);
@@ -917,6 +923,7 @@
   cc_layer_->SetContentsOpaque(true);
   cc_layer_->SetIsDrawable(type_ != LAYER_NOT_DRAWN);
   cc_layer_->AddLayerAnimationEventObserver(this);
+  cc_layer_->SetLayerClient(this);
   RecomputePosition();
 }
 
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h
index cd640b1..1416f5f 100644
--- a/ui/compositor/layer.h
+++ b/ui/compositor/layer.h
@@ -16,6 +16,7 @@
 #include "cc/animation/layer_animation_event_observer.h"
 #include "cc/base/scoped_ptr_vector.h"
 #include "cc/layers/content_layer_client.h"
+#include "cc/layers/layer_client.h"
 #include "cc/layers/texture_layer_client.h"
 #include "cc/resources/texture_mailbox.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -62,6 +63,7 @@
     : public LayerAnimationDelegate,
       NON_EXPORTED_BASE(public cc::ContentLayerClient),
       NON_EXPORTED_BASE(public cc::TextureLayerClient),
+      NON_EXPORTED_BASE(public cc::LayerClient),
       NON_EXPORTED_BASE(public cc::LayerAnimationEventObserver) {
  public:
   Layer();
@@ -330,6 +332,9 @@
   void SetForceRenderSurface(bool force);
   bool force_render_surface() const { return force_render_surface_; }
 
+  // LayerClient
+  virtual std::string DebugName() OVERRIDE;
+
   // LayerAnimationEventObserver
   virtual void OnAnimationStarted(const cc::AnimationEvent& event) OVERRIDE;
 
diff --git a/ui/compositor/layer_animator_unittest.cc b/ui/compositor/layer_animator_unittest.cc
index 8b4d792..5cae43c 100644
--- a/ui/compositor/layer_animator_unittest.cc
+++ b/ui/compositor/layer_animator_unittest.cc
@@ -1764,13 +1764,13 @@
   animator->StartAnimation(sequence);
 
   // |observer| should be attached to |sequence|.
-  EXPECT_EQ(static_cast<size_t>(1), sequence->observers_.size());
+  EXPECT_TRUE(sequence->observers_.might_have_observers());
 
   // Now, release |observer|
   observer.reset();
 
   // And |sequence| should no longer be attached to |observer|.
-  EXPECT_EQ(static_cast<size_t>(0), sequence->observers_.size());
+  EXPECT_FALSE(sequence->observers_.might_have_observers());
 }
 
 TEST(LayerAnimatorTest, ObserverAttachedAfterAnimationStarted) {
diff --git a/ui/gfx/codec/png_codec.cc b/ui/gfx/codec/png_codec.cc
index ed503fb..30e4eb6 100644
--- a/ui/gfx/codec/png_codec.cc
+++ b/ui/gfx/codec/png_codec.cc
@@ -472,7 +472,7 @@
 
 // static
 SkBitmap* PNGCodec::CreateSkBitmapFromBGRAFormat(
-    std::vector<unsigned char>& bgra, int width, int height) {
+    const std::vector<unsigned char>& bgra, int width, int height) {
   SkBitmap* bitmap = new SkBitmap();
   bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
   bitmap->allocPixels();
@@ -654,29 +654,14 @@
   return true;
 }
 
-}  // namespace
-
-// static
-bool PNGCodec::Encode(const unsigned char* input, ColorFormat format,
-                      const Size& size, int row_byte_width,
-                      bool discard_transparency,
-                      const std::vector<Comment>& comments,
-                      std::vector<unsigned char>* output) {
-  return PNGCodec::EncodeWithCompressionLevel(input, format, size,
-                                              row_byte_width,
-                                              discard_transparency,
-                                              comments, Z_DEFAULT_COMPRESSION,
-                                              output);
-}
-
-// static
-bool PNGCodec::EncodeWithCompressionLevel(const unsigned char* input,
-                                          ColorFormat format, const Size& size,
-                                          int row_byte_width,
-                                          bool discard_transparency,
-                                          const std::vector<Comment>& comments,
-                                          int compression_level,
-                                          std::vector<unsigned char>* output) {
+bool EncodeWithCompressionLevel(const unsigned char* input,
+                                PNGCodec::ColorFormat format,
+                                const Size& size,
+                                int row_byte_width,
+                                bool discard_transparency,
+                                const std::vector<PNGCodec::Comment>& comments,
+                                int compression_level,
+                                std::vector<unsigned char>* output) {
   // Run to convert an input row into the output row format, NULL means no
   // conversion is necessary.
   FormatConverter converter = NULL;
@@ -684,13 +669,13 @@
   int input_color_components, output_color_components;
   int png_output_color_type;
   switch (format) {
-    case FORMAT_RGB:
+    case PNGCodec::FORMAT_RGB:
       input_color_components = 3;
       output_color_components = 3;
       png_output_color_type = PNG_COLOR_TYPE_RGB;
       break;
 
-    case FORMAT_RGBA:
+    case PNGCodec::FORMAT_RGBA:
       input_color_components = 4;
       if (discard_transparency) {
         output_color_components = 3;
@@ -703,7 +688,7 @@
       }
       break;
 
-    case FORMAT_BGRA:
+    case PNGCodec::FORMAT_BGRA:
       input_color_components = 4;
       if (discard_transparency) {
         output_color_components = 3;
@@ -716,7 +701,7 @@
       }
       break;
 
-    case FORMAT_SkBitmap:
+    case PNGCodec::FORMAT_SkBitmap:
       input_color_components = 4;
       if (discard_transparency) {
         output_color_components = 3;
@@ -758,18 +743,37 @@
   return success;
 }
 
+
+}  // namespace
+
+// static
+bool PNGCodec::Encode(const unsigned char* input, ColorFormat format,
+                      const Size& size, int row_byte_width,
+                      bool discard_transparency,
+                      const std::vector<Comment>& comments,
+                      std::vector<unsigned char>* output) {
+  return EncodeWithCompressionLevel(input,
+                                    format,
+                                    size,
+                                    row_byte_width,
+                                    discard_transparency,
+                                    comments,
+                                    Z_DEFAULT_COMPRESSION,
+                                    output);
+}
+
 // static
 bool PNGCodec::EncodeBGRASkBitmap(const SkBitmap& input,
                                   bool discard_transparency,
                                   std::vector<unsigned char>* output) {
   static const int bbp = 4;
 
-  SkAutoLockPixels lock_input(input);
   if (input.empty())
     return false;
   DCHECK_EQ(input.bytesPerPixel(), bbp);
   DCHECK_GE(static_cast<int>(input.rowBytes()), input.width() * bbp);
 
+  SkAutoLockPixels lock_input(input);
   return Encode(reinterpret_cast<unsigned char*>(input.getAddr32(0, 0)),
                 FORMAT_SkBitmap, Size(input.width(), input.height()),
                 static_cast<int>(input.rowBytes()), discard_transparency,
@@ -782,12 +786,12 @@
                                       std::vector<unsigned char>* output) {
   static const int bbp = 4;
 
-  SkAutoLockPixels lock_input(input);
   if (input.empty())
     return false;
   DCHECK_EQ(input.bytesPerPixel(), bbp);
   DCHECK_GE(static_cast<int>(input.rowBytes()), input.width() * bbp);
 
+  SkAutoLockPixels lock_input(input);
   return EncodeWithCompressionLevel(
       reinterpret_cast<unsigned char*>(input.getAddr32(0, 0)),
       FORMAT_SkBitmap, Size(input.width(), input.height()),
diff --git a/ui/gfx/codec/png_codec.h b/ui/gfx/codec/png_codec.h
index eec36ae..0088bb3 100644
--- a/ui/gfx/codec/png_codec.h
+++ b/ui/gfx/codec/png_codec.h
@@ -51,16 +51,6 @@
     std::string text;
   };
 
-  // Calls PNGCodec::EncodeWithCompressionLevel with the default compression
-  // level.
-  static bool Encode(const unsigned char* input,
-                     ColorFormat format,
-                     const Size& size,
-                     int row_byte_width,
-                     bool discard_transparency,
-                     const std::vector<Comment>& comments,
-                     std::vector<unsigned char>* output);
-
   // Encodes the given raw 'input' data, with each pixel being represented as
   // given in 'format'. The encoded PNG data will be written into the supplied
   // vector and true will be returned on success. On failure (false), the
@@ -78,16 +68,13 @@
   //   written to the resulting file. Otherwise, alpha values in the input
   //   will be preserved.
   // comments: comments to be written in the png's metadata.
-  // compression_level: An integer between -1 and 9, corresponding to zlib's
-  //   compression levels. -1 is the default.
-  static bool EncodeWithCompressionLevel(const unsigned char* input,
-                                         ColorFormat format,
-                                         const Size& size,
-                                         int row_byte_width,
-                                         bool discard_transparency,
-                                         const std::vector<Comment>& comments,
-                                         int compression_level,
-                                         std::vector<unsigned char>* output);
+  static bool Encode(const unsigned char* input,
+                     ColorFormat format,
+                     const Size& size,
+                     int row_byte_width,
+                     bool discard_transparency,
+                     const std::vector<Comment>& comments,
+                     std::vector<unsigned char>* output);
 
   // Call PNGCodec::Encode on the supplied SkBitmap |input|, which is assumed
   // to be BGRA, 32 bits per pixel. The params |discard_transparency| and
@@ -130,7 +117,7 @@
   // Create a SkBitmap from a decoded BGRA DIB. The caller owns the returned
   // SkBitmap.
   static SkBitmap* CreateSkBitmapFromBGRAFormat(
-      std::vector<unsigned char>& bgra, int width, int height);
+      const std::vector<unsigned char>& bgra, int width, int height);
 
  private:
   DISALLOW_COPY_AND_ASSIGN(PNGCodec);
diff --git a/ui/gfx/codec/png_codec_unittest.cc b/ui/gfx/codec/png_codec_unittest.cc
index e3540a2..f1654d9 100644
--- a/ui/gfx/codec/png_codec_unittest.cc
+++ b/ui/gfx/codec/png_codec_unittest.cc
@@ -14,6 +14,7 @@
 #include "third_party/zlib/zlib.h"
 #include "ui/gfx/codec/png_codec.h"
 #include "ui/gfx/size.h"
+#include "ui/gfx/skia_util.h"
 
 namespace gfx {
 
@@ -1119,43 +1120,31 @@
 
   // create an image with known values, a must be opaque because it will be
   // lost during encoding
-  std::vector<unsigned char> original;
-  MakeRGBAImage(w, h, true, &original);
+  SkBitmap original_bitmap;
+  MakeTestSkBitmap(w, h, &original_bitmap);
 
   // encode
-  std::vector<unsigned char> encoded_fast;
-  EXPECT_TRUE(PNGCodec::EncodeWithCompressionLevel(
-        &original[0], PNGCodec::FORMAT_RGBA, Size(w, h), w * 4, false,
-        std::vector<PNGCodec::Comment>(), Z_BEST_SPEED, &encoded_fast));
+  std::vector<unsigned char> encoded_normal;
+  EXPECT_TRUE(
+      PNGCodec::EncodeBGRASkBitmap(original_bitmap, false, &encoded_normal));
 
-  std::vector<unsigned char> encoded_best;
-  EXPECT_TRUE(PNGCodec::EncodeWithCompressionLevel(
-        &original[0], PNGCodec::FORMAT_RGBA, Size(w, h), w * 4, false,
-        std::vector<PNGCodec::Comment>(), Z_BEST_COMPRESSION, &encoded_best));
+  std::vector<unsigned char> encoded_fast;
+  EXPECT_TRUE(
+      PNGCodec::FastEncodeBGRASkBitmap(original_bitmap, false, &encoded_fast));
 
   // Make sure the different compression settings actually do something; the
   // sizes should be different.
-  EXPECT_NE(encoded_fast.size(), encoded_best.size());
+  EXPECT_NE(encoded_normal.size(), encoded_fast.size());
 
-  // decode, it should have the same size as the original
-  std::vector<unsigned char> decoded;
-  int outw, outh;
-  EXPECT_TRUE(PNGCodec::Decode(&encoded_fast[0], encoded_fast.size(),
-                               PNGCodec::FORMAT_RGBA, &decoded,
-                               &outw, &outh));
-  ASSERT_EQ(w, outw);
-  ASSERT_EQ(h, outh);
-  ASSERT_EQ(original.size(), decoded.size());
+  // decode, they should be identical to the original.
+  SkBitmap decoded;
+  EXPECT_TRUE(
+      PNGCodec::Decode(&encoded_normal[0], encoded_normal.size(), &decoded));
+  EXPECT_TRUE(BitmapsAreEqual(decoded, original_bitmap));
 
-  EXPECT_TRUE(PNGCodec::Decode(&encoded_best[0], encoded_best.size(),
-                               PNGCodec::FORMAT_RGBA, &decoded,
-                               &outw, &outh));
-  ASSERT_EQ(w, outw);
-  ASSERT_EQ(h, outh);
-  ASSERT_EQ(original.size(), decoded.size());
-
-  // Images must be exactly equal
-  ASSERT_TRUE(original == decoded);
+  EXPECT_TRUE(
+      PNGCodec::Decode(&encoded_fast[0], encoded_fast.size(), &decoded));
+  EXPECT_TRUE(BitmapsAreEqual(decoded, original_bitmap));
 }
 
 
diff --git a/ui/gfx/image/image_family.h b/ui/gfx/image/image_family.h
index b2a2c07..902dede 100644
--- a/ui/gfx/image/image_family.h
+++ b/ui/gfx/image/image_family.h
@@ -27,8 +27,16 @@
 // include high-DPI representations).
 class UI_EXPORT ImageFamily {
  private:
-  // Forward declaration.
-  struct MapKey;
+  // An <aspect ratio, DIP width> pair.
+  // A 0x0 image has aspect ratio 1.0. 0xN and Nx0 images are treated as 0x0.
+  struct MapKey : std::pair<float, int> {
+    MapKey(float aspect, int width)
+        : std::pair<float, int>(aspect, width) {}
+
+    float aspect() const { return first; }
+
+    int width() const { return second; }
+  };
 
  public:
   // Type for iterating over all images in the family, in order.
@@ -127,17 +135,6 @@
   const gfx::Image* GetBest(const gfx::Size& size) const;
 
  private:
-  // An <aspect ratio, DIP width> pair.
-  // A 0x0 image has aspect ratio 1.0. 0xN and Nx0 images are treated as 0x0.
-  struct MapKey : std::pair<float, int> {
-    MapKey(float aspect, int width)
-        : std::pair<float, int>(aspect, width) {}
-
-    float aspect() const { return first; }
-
-    int width() const { return second; }
-  };
-
   // Find the closest aspect ratio in the map to |desired_aspect|.
   // Ties are broken by the thinner aspect.
   // |map_| must not be empty. |desired_aspect| must be > 0.0.
diff --git a/ui/gfx/screen.h b/ui/gfx/screen.h
index 9410d02..d94e3e0 100644
--- a/ui/gfx/screen.h
+++ b/ui/gfx/screen.h
@@ -5,6 +5,8 @@
 #ifndef UI_GFX_SCREEN_H_
 #define UI_GFX_SCREEN_H_
 
+#include <vector>
+
 #include "base/basictypes.h"
 #include "ui/base/ui_export.h"
 #include "ui/gfx/display.h"
@@ -50,17 +52,23 @@
   virtual gfx::Point GetCursorScreenPoint() = 0;
 
   // Returns the window under the cursor.
-  virtual gfx::NativeWindow GetWindowAtCursorScreenPoint() = 0;
+  virtual gfx::NativeWindow GetWindowUnderCursor() = 0;
+
+  // Returns the window at the given screen coordinate |point|.
+  virtual gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) = 0;
 
   // Returns the number of displays.
   // Mirrored displays are excluded; this method is intended to return the
   // number of distinct, usable displays.
-  virtual int GetNumDisplays() = 0;
+  virtual int GetNumDisplays() const = 0;
+
+  // Returns the list of displays that are currently available.
+  virtual std::vector<gfx::Display> GetAllDisplays() const = 0;
 
   // Returns the display nearest the specified window.
   virtual gfx::Display GetDisplayNearestWindow(NativeView view) const = 0;
 
-  // Returns the the display nearest the specified point.
+  // Returns the display nearest the specified point.
   virtual gfx::Display GetDisplayNearestPoint(
       const gfx::Point& point) const = 0;
 
diff --git a/ui/gfx/screen_android.cc b/ui/gfx/screen_android.cc
index 6c4d633..e90bb76 100644
--- a/ui/gfx/screen_android.cc
+++ b/ui/gfx/screen_android.cc
@@ -19,7 +19,13 @@
 
   virtual gfx::Point GetCursorScreenPoint() OVERRIDE { return gfx::Point(); }
 
-  virtual gfx::NativeWindow GetWindowAtCursorScreenPoint() OVERRIDE {
+  virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE {
+    NOTIMPLEMENTED();
+    return NULL;
+  }
+
+  virtual gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point)
+      OVERRIDE {
     NOTIMPLEMENTED();
     return NULL;
   }
@@ -50,7 +56,11 @@
     return GetPrimaryDisplay();
   }
 
-  virtual int GetNumDisplays() OVERRIDE { return 1; }
+  virtual int GetNumDisplays() const OVERRIDE { return 1; }
+
+  virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE {
+    return std::vector<gfx::Display>(1, GetPrimaryDisplay());
+  }
 
   virtual gfx::Display GetDisplayMatching(
       const gfx::Rect& match_rect) const OVERRIDE {
diff --git a/ui/gfx/screen_gtk.cc b/ui/gfx/screen_gtk.cc
index 8ef803d..16a7873 100644
--- a/ui/gfx/screen_gtk.cc
+++ b/ui/gfx/screen_gtk.cc
@@ -49,8 +49,9 @@
 
 gfx::Rect NativePrimaryMonitorBounds() {
   GdkScreen* screen = gdk_screen_get_default();
+  gint primary_monitor_index = gdk_screen_get_primary_monitor(screen);
   GdkRectangle rect;
-  gdk_screen_get_monitor_geometry(screen, 0, &rect);
+  gdk_screen_get_monitor_geometry(screen, primary_monitor_index, &rect);
   return gfx::Rect(rect);
 }
 
@@ -90,7 +91,7 @@
   }
 
   // Returns the window under the cursor.
-  virtual gfx::NativeWindow GetWindowAtCursorScreenPoint() OVERRIDE {
+  virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE {
     GdkWindow* window = gdk_window_at_pointer(NULL, NULL);
     if (!window)
       return NULL;
@@ -104,16 +105,27 @@
     return GTK_IS_WINDOW(widget) ? GTK_WINDOW(widget) : NULL;
   }
 
+  virtual gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point)
+      OVERRIDE {
+    NOTIMPLEMENTED();
+    return NULL;
+  }
+
   // Returns the number of displays.
   // Mirrored displays are excluded; this method is intended to return the
   // number of distinct, usable displays.
-  virtual int GetNumDisplays() OVERRIDE {
+  virtual int GetNumDisplays() const OVERRIDE {
     // This query is kinda bogus for Linux -- do we want number of X screens?
     // The number of monitors Xinerama has?  We'll just use whatever GDK uses.
     GdkScreen* screen = gdk_screen_get_default();
     return gdk_screen_get_n_monitors(screen);
   }
 
+  virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE {
+    NOTIMPLEMENTED();
+    return std::vector<gfx::Display>(1, GetPrimaryDisplay());
+  }
+
   // Returns the display nearest the specified window.
   virtual gfx::Display GetDisplayNearestWindow(
       gfx::NativeView view) const OVERRIDE {
diff --git a/ui/gfx/screen_ios.mm b/ui/gfx/screen_ios.mm
index dfb0d1b..a277029 100644
--- a/ui/gfx/screen_ios.mm
+++ b/ui/gfx/screen_ios.mm
@@ -21,12 +21,18 @@
     return gfx::Point(0, 0);
   }
 
-  virtual gfx::NativeWindow GetWindowAtCursorScreenPoint() OVERRIDE {
+  virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE {
     NOTIMPLEMENTED();
     return gfx::NativeWindow();
   }
 
-  virtual int GetNumDisplays() OVERRIDE {
+  virtual gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point)
+      OVERRIDE {
+    NOTIMPLEMENTED();
+    return gfx::NativeWindow();
+  }
+
+  virtual int GetNumDisplays() const OVERRIDE {
 #if TARGET_IPHONE_SIMULATOR
     // UIScreen does not reliably return correct results on the simulator.
     return 1;
@@ -35,6 +41,11 @@
 #endif
   }
 
+  virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE {
+    NOTIMPLEMENTED();
+    return std::vector<gfx::Display>(1, GetPrimaryDisplay());
+  }
+
   // Returns the display nearest the specified window.
   virtual gfx::Display GetDisplayNearestWindow(
       gfx::NativeView view) const OVERRIDE {
diff --git a/ui/gfx/screen_mac.mm b/ui/gfx/screen_mac.mm
index d9e1f0a..28707ca 100644
--- a/ui/gfx/screen_mac.mm
+++ b/ui/gfx/screen_mac.mm
@@ -84,12 +84,18 @@
     return gfx::Point(mouseLocation.x, mouseLocation.y);
   }
 
-  virtual gfx::NativeWindow GetWindowAtCursorScreenPoint() OVERRIDE {
+  virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE {
     NOTIMPLEMENTED();
     return gfx::NativeWindow();
   }
 
-  virtual int GetNumDisplays() OVERRIDE {
+  virtual gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point)
+      OVERRIDE {
+    NOTIMPLEMENTED();
+    return gfx::NativeWindow();
+  }
+
+  virtual int GetNumDisplays() const OVERRIDE {
     // Don't just return the number of online displays.  It includes displays
     // that mirror other displays, which are not desired in the count.  It's
     // tempting to use the count returned by CGGetActiveDisplayList, but active
@@ -123,6 +129,11 @@
     return display_count;
   }
 
+  virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE {
+    NOTIMPLEMENTED();
+    return std::vector<gfx::Display>(1, GetPrimaryDisplay());
+  }
+
   virtual gfx::Display GetDisplayNearestWindow(
       gfx::NativeView view) const OVERRIDE {
     NSWindow* window = [view window];
diff --git a/ui/gfx/screen_win.cc b/ui/gfx/screen_win.cc
index 8bb1548..95847c4 100644
--- a/ui/gfx/screen_win.cc
+++ b/ui/gfx/screen_win.cc
@@ -49,16 +49,25 @@
   return gfx::Point(pt);
 }
 
-gfx::NativeWindow ScreenWin::GetWindowAtCursorScreenPoint() {
-  POINT location;
-  HWND window_hwnd = GetCursorPos(&location) ? WindowFromPoint(location) : NULL;
-  return GetNativeWindowFromHWND(window_hwnd);
+gfx::NativeWindow ScreenWin::GetWindowUnderCursor() {
+  POINT cursor_loc;
+  HWND hwnd = GetCursorPos(&cursor_loc) ? WindowFromPoint(cursor_loc) : NULL;
+  return GetNativeWindowFromHWND(hwnd);
 }
 
-int ScreenWin::GetNumDisplays() {
+gfx::NativeWindow ScreenWin::GetWindowAtScreenPoint(const gfx::Point& point) {
+  return GetNativeWindowFromHWND(WindowFromPoint(point.ToPOINT()));
+}
+
+int ScreenWin::GetNumDisplays() const {
   return GetSystemMetrics(SM_CMONITORS);
 }
 
+std::vector<gfx::Display> ScreenWin::GetAllDisplays() const {
+  NOTIMPLEMENTED();
+  return std::vector<gfx::Display>(1, GetPrimaryDisplay());
+}
+
 gfx::Display ScreenWin::GetDisplayNearestWindow(gfx::NativeView window) const {
   HWND window_hwnd = GetHWNDFromNativeView(window);
   if (!window_hwnd) {
diff --git a/ui/gfx/screen_win.h b/ui/gfx/screen_win.h
index f6b7d40..8905d46 100644
--- a/ui/gfx/screen_win.h
+++ b/ui/gfx/screen_win.h
@@ -20,8 +20,11 @@
   // Overridden from gfx::Screen:
   virtual bool IsDIPEnabled() OVERRIDE;
   virtual gfx::Point GetCursorScreenPoint() OVERRIDE;
-  virtual gfx::NativeWindow GetWindowAtCursorScreenPoint() OVERRIDE;
-  virtual int GetNumDisplays() OVERRIDE;
+  virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE;
+  virtual gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point)
+      OVERRIDE;
+  virtual int GetNumDisplays() const OVERRIDE;
+  virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE;
   virtual gfx::Display GetDisplayNearestWindow(
       gfx::NativeView window) const OVERRIDE;
   virtual gfx::Display GetDisplayNearestPoint(
diff --git a/ui/gl/android/gl_jni_registrar.cc b/ui/gl/android/gl_jni_registrar.cc
index 997268c..91ae65f 100644
--- a/ui/gl/android/gl_jni_registrar.cc
+++ b/ui/gl/android/gl_jni_registrar.cc
@@ -6,6 +6,7 @@
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_registrar.h"
+#include "ui/gl/android/surface_texture.h"
 #include "ui/gl/android/surface_texture_listener.h"
 
 namespace ui {
@@ -13,6 +14,8 @@
 namespace android {
 
 static base::android::RegistrationMethod kGLRegisteredMethods[] = {
+  { "SurfaceTexture",
+    gfx::SurfaceTexture::RegisterSurfaceTexture },
   { "SurfaceTextureListener",
     gfx::SurfaceTextureListener::RegisterSurfaceTextureListener },
 };
diff --git a/ui/gl/android/scoped_java_surface.cc b/ui/gl/android/scoped_java_surface.cc
index fe6a75e..36f635f 100644
--- a/ui/gl/android/scoped_java_surface.cc
+++ b/ui/gl/android/scoped_java_surface.cc
@@ -6,7 +6,7 @@
 
 #include "base/logging.h"
 #include "jni/Surface_jni.h"
-#include "ui/gl/android/surface_texture_bridge.h"
+#include "ui/gl/android/surface_texture.h"
 
 namespace {
 
@@ -37,7 +37,7 @@
 }
 
 ScopedJavaSurface::ScopedJavaSurface(
-    const SurfaceTextureBridge* surface_texture)
+    const SurfaceTexture* surface_texture)
     : auto_release_(true),
       is_protected_(false) {
   JNIEnv* env = base::android::AttachCurrentThread();
diff --git a/ui/gl/android/scoped_java_surface.h b/ui/gl/android/scoped_java_surface.h
index b50485b..54ed2a4 100644
--- a/ui/gl/android/scoped_java_surface.h
+++ b/ui/gl/android/scoped_java_surface.h
@@ -13,7 +13,7 @@
 
 namespace gfx {
 
-class SurfaceTextureBridge;
+class SurfaceTexture;
 
 // A helper class for holding a scoped reference to a Java Surface instance.
 // When going out of scope, Surface.release() is called on the Java object to
@@ -29,7 +29,7 @@
 
   // Creates a Java Surface from a SurfaceTexture and wraps it in a
   // ScopedJavaSurface.
-  explicit ScopedJavaSurface(const SurfaceTextureBridge* surface_texture);
+  explicit ScopedJavaSurface(const SurfaceTexture* surface_texture);
 
   // Move constructor. Take the surface from another ScopedJavaSurface object,
   // the latter no longer owns the surface afterwards.
diff --git a/ui/gl/android/surface_texture.cc b/ui/gl/android/surface_texture.cc
new file mode 100644
index 0000000..d900972
--- /dev/null
+++ b/ui/gl/android/surface_texture.cc
@@ -0,0 +1,116 @@
+// 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 "ui/gl/android/surface_texture.h"
+
+#include <android/native_window_jni.h>
+
+// TODO(boliu): Remove this include when we move off ICS.
+#include "base/android/build_info.h"
+#include "base/android/jni_android.h"
+#include "base/logging.h"
+#include "jni/SurfaceTexturePlatformWrapper_jni.h"
+#include "ui/gl/android/scoped_java_surface.h"
+#include "ui/gl/android/surface_texture_listener.h"
+#include "ui/gl/gl_bindings.h"
+
+// TODO(boliu): Remove this method when when we move off ICS. See
+// http://crbug.com/161864.
+bool GlContextMethodsAvailable() {
+  bool available = base::android::BuildInfo::GetInstance()->sdk_int() >= 16;
+  if (!available)
+    LOG(WARNING) << "Running on unsupported device: rendering may not work";
+  return available;
+}
+
+namespace gfx {
+
+SurfaceTexture::SurfaceTexture(int texture_id) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  j_surface_texture_.Reset(
+      Java_SurfaceTexturePlatformWrapper_create(env, texture_id));
+}
+
+SurfaceTexture::~SurfaceTexture() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_SurfaceTexturePlatformWrapper_destroy(env, j_surface_texture_.obj());
+}
+
+void SurfaceTexture::SetFrameAvailableCallback(
+    const base::Closure& callback) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_SurfaceTexturePlatformWrapper_setFrameAvailableCallback(
+      env,
+      j_surface_texture_.obj(),
+      reinterpret_cast<int>(new SurfaceTextureListener(callback)));
+}
+
+void SurfaceTexture::UpdateTexImage() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_SurfaceTexturePlatformWrapper_updateTexImage(env,
+                                                    j_surface_texture_.obj());
+}
+
+void SurfaceTexture::GetTransformMatrix(float mtx[16]) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+
+  base::android::ScopedJavaLocalRef<jfloatArray> jmatrix(
+      env, env->NewFloatArray(16));
+  Java_SurfaceTexturePlatformWrapper_getTransformMatrix(
+      env, j_surface_texture_.obj(), jmatrix.obj());
+
+  jboolean is_copy;
+  jfloat* elements = env->GetFloatArrayElements(jmatrix.obj(), &is_copy);
+  for (int i = 0; i < 16; ++i) {
+    mtx[i] = static_cast<float>(elements[i]);
+  }
+  env->ReleaseFloatArrayElements(jmatrix.obj(), elements, JNI_ABORT);
+}
+
+void SurfaceTexture::SetDefaultBufferSize(int width, int height) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+
+  if (width > 0 && height > 0) {
+    Java_SurfaceTexturePlatformWrapper_setDefaultBufferSize(
+        env, j_surface_texture_.obj(), static_cast<jint>(width),
+        static_cast<jint>(height));
+  } else {
+    LOG(WARNING) << "Not setting surface texture buffer size - "
+                    "width or height is 0";
+  }
+}
+
+void SurfaceTexture::AttachToGLContext() {
+  if (GlContextMethodsAvailable()) {
+    int texture_id;
+    glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texture_id);
+    DCHECK(texture_id);
+    JNIEnv* env = base::android::AttachCurrentThread();
+    Java_SurfaceTexturePlatformWrapper_attachToGLContext(
+        env, j_surface_texture_.obj(), texture_id);
+  }
+}
+
+void SurfaceTexture::DetachFromGLContext() {
+  if (GlContextMethodsAvailable()) {
+    JNIEnv* env = base::android::AttachCurrentThread();
+    Java_SurfaceTexturePlatformWrapper_detachFromGLContext(
+        env, j_surface_texture_.obj());
+  }
+}
+
+ANativeWindow* SurfaceTexture::CreateSurface() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ScopedJavaSurface surface(this);
+  ANativeWindow* native_window = ANativeWindow_fromSurface(
+      env, surface.j_surface().obj());
+  return native_window;
+}
+
+// static
+bool SurfaceTexture::RegisterSurfaceTexture(JNIEnv* env) {
+  return RegisterNativesImpl(env);
+}
+
+}  // namespace gfx
diff --git a/ui/gl/android/surface_texture.h b/ui/gl/android/surface_texture.h
new file mode 100644
index 0000000..62e375f
--- /dev/null
+++ b/ui/gl/android/surface_texture.h
@@ -0,0 +1,74 @@
+// 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 UI_GL_ANDROID_SURFACE_TEXTURE_H_
+#define UI_GL_ANDROID_SURFACE_TEXTURE_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "ui/gl/gl_export.h"
+
+struct ANativeWindow;
+
+namespace gfx {
+
+// This class serves as a bridge for native code to call java functions inside
+// android SurfaceTexture class.
+class GL_EXPORT SurfaceTexture
+    : public base::RefCountedThreadSafe<SurfaceTexture>{
+ public:
+  explicit SurfaceTexture(int texture_id);
+
+  // Set the listener callback, which will be invoked on the same thread that
+  // is being called from here for registration.
+  // Note: Since callbacks come in from Java objects that might outlive objects
+  // being referenced from the callback, the only robust way here is to create
+  // the callback from a weak pointer to your object.
+  void SetFrameAvailableCallback(const base::Closure& callback);
+
+  // Update the texture image to the most recent frame from the image stream.
+  void UpdateTexImage();
+
+  // Retrieve the 4x4 texture coordinate transform matrix associated with the
+  // texture image set by the most recent call to updateTexImage.
+  void GetTransformMatrix(float mtx[16]);
+
+  // Set the default size of the image buffers.
+  void SetDefaultBufferSize(int width, int height);
+
+  // Attach the SurfaceTexture to the texture currently bound to
+  // GL_TEXTURE_EXTERNAL_OES.
+  void AttachToGLContext();
+
+  // Detaches the SurfaceTexture from the context that owns its current GL
+  // texture. Must be called with that context current on the calling thread.
+  void DetachFromGLContext();
+
+  // Creates a native render surface for this surface texture.
+  // The caller must release the underlying reference when done with the handle
+  // by calling ANativeWindow_release().
+  ANativeWindow* CreateSurface();
+
+  const base::android::JavaRef<jobject>& j_surface_texture() const {
+    return j_surface_texture_;
+  }
+
+  static bool RegisterSurfaceTexture(JNIEnv* env);
+
+ private:
+  friend class base::RefCountedThreadSafe<SurfaceTexture>;
+  ~SurfaceTexture();
+
+  // Java SurfaceTexture instance.
+  base::android::ScopedJavaGlobalRef<jobject> j_surface_texture_;
+
+  DISALLOW_COPY_AND_ASSIGN(SurfaceTexture);
+};
+
+}  // namespace gfx
+
+#endif  // UI_GL_ANDROID_SURFACE_TEXTURE_H_
diff --git a/ui/gl/android/surface_texture_bridge.cc b/ui/gl/android/surface_texture_bridge.cc
deleted file mode 100644
index c4206a9..0000000
--- a/ui/gl/android/surface_texture_bridge.cc
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright (c) 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 "ui/gl/android/surface_texture_bridge.h"
-
-#include <android/native_window_jni.h>
-
-// TODO(boliu): Remove this include when we move off ICS.
-#include "base/android/build_info.h"
-#include "base/android/jni_android.h"
-#include "base/logging.h"
-#include "jni/SurfaceTexture_jni.h"
-#include "ui/gl/android/scoped_java_surface.h"
-#include "ui/gl/android/surface_texture_listener.h"
-#include "ui/gl/gl_bindings.h"
-
-using base::android::AttachCurrentThread;
-using base::android::CheckException;
-using base::android::GetClass;
-using base::android::ScopedJavaLocalRef;
-
-namespace {
-bool g_jni_initialized = false;
-
-void RegisterNativesIfNeeded(JNIEnv* env) {
-  if (!g_jni_initialized) {
-    JNI_SurfaceTexture::RegisterNativesImpl(env);
-    g_jni_initialized = true;
-  }
-}
-
-// TODO(boliu): Remove this method when when we move off ICS. See
-// http://crbug.com/161864.
-bool GlContextMethodsAvailable() {
-  bool available = base::android::BuildInfo::GetInstance()->sdk_int() >= 16;
-  if (!available)
-    LOG(WARNING) << "Running on unsupported device: rendering may not work";
-  return available;
-}
-
-}  // namespace
-
-namespace gfx {
-
-SurfaceTextureBridge::SurfaceTextureBridge(int texture_id) {
-  JNIEnv* env = AttachCurrentThread();
-  CHECK(env);
-  RegisterNativesIfNeeded(env);
-
-  ScopedJavaLocalRef<jobject> tmp(
-      JNI_SurfaceTexture::Java_SurfaceTexture_Constructor(
-          env, texture_id));
-  DCHECK(!tmp.is_null());
-  j_surface_texture_.Reset(tmp);
-}
-
-SurfaceTextureBridge::~SurfaceTextureBridge() {
-  JNIEnv* env = AttachCurrentThread();
-  CHECK(env);
-
-  // Release the listener.
-  JNI_SurfaceTexture::Java_SurfaceTexture_setOnFrameAvailableListener(
-      env, j_surface_texture_.obj(), NULL);
-
-  // Release graphics memory.
-  JNI_SurfaceTexture::Java_SurfaceTexture_release(
-      env, j_surface_texture_.obj());
-}
-
-void SurfaceTextureBridge::SetFrameAvailableCallback(
-    const base::Closure& callback) {
-  JNIEnv* env = AttachCurrentThread();
-  CHECK(env);
-
-  // Since the listener is owned by the Java SurfaceTexture object, setting
-  // a new listener here will release an existing one at the same time.
-  ScopedJavaLocalRef<jobject> j_listener(
-      env,
-      SurfaceTextureListener::CreateSurfaceTextureListener(env, callback));
-  DCHECK(!j_listener.is_null());
-
-  // Set it as the onFrameAvailableListener for our SurfaceTexture instance.
-  JNI_SurfaceTexture::Java_SurfaceTexture_setOnFrameAvailableListener(
-      env, j_surface_texture_.obj(), j_listener.obj());
-}
-
-void SurfaceTextureBridge::UpdateTexImage() {
-  JNIEnv* env = AttachCurrentThread();
-  CHECK(env);
-
-  JNI_SurfaceTexture::Java_SurfaceTexture_updateTexImage(
-      env, j_surface_texture_.obj());
-}
-
-void SurfaceTextureBridge::GetTransformMatrix(float mtx[16]) {
-  JNIEnv* env = AttachCurrentThread();
-  CHECK(env);
-
-  ScopedJavaLocalRef<jfloatArray> jmatrix(env, env->NewFloatArray(16));
-  JNI_SurfaceTexture::Java_SurfaceTexture_getTransformMatrix(
-      env, j_surface_texture_.obj(), jmatrix.obj());
-
-  jboolean is_copy;
-  jfloat* elements = env->GetFloatArrayElements(jmatrix.obj(), &is_copy);
-  for (int i = 0; i < 16; ++i) {
-    mtx[i] = static_cast<float>(elements[i]);
-  }
-  env->ReleaseFloatArrayElements(jmatrix.obj(), elements, JNI_ABORT);
-}
-
-void SurfaceTextureBridge::SetDefaultBufferSize(int width, int height) {
-  JNIEnv* env = AttachCurrentThread();
-  CHECK(env);
-
-  if (width > 0 && height > 0) {
-    JNI_SurfaceTexture::Java_SurfaceTexture_setDefaultBufferSize(
-        env, j_surface_texture_.obj(), static_cast<jint>(width),
-        static_cast<jint>(height));
-  } else {
-    LOG(WARNING) << "Not setting surface texture buffer size - "
-                    "width or height is 0";
-  }
-}
-
-void SurfaceTextureBridge::AttachToGLContext() {
-  if (GlContextMethodsAvailable()) {
-    int texture_id;
-    glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texture_id);
-    DCHECK(texture_id);
-    JNIEnv* env = AttachCurrentThread();
-    // Note: This method is only available on JB and greater.
-    JNI_SurfaceTexture::Java_SurfaceTexture_attachToGLContext(
-        env, j_surface_texture_.obj(), texture_id);
-  }
-}
-
-void SurfaceTextureBridge::DetachFromGLContext() {
-  if (GlContextMethodsAvailable()) {
-    JNIEnv* env = AttachCurrentThread();
-    // Note: This method is only available on JB and greater.
-    JNI_SurfaceTexture::Java_SurfaceTexture_detachFromGLContext(
-        env, j_surface_texture_.obj());
-  }
-}
-
-ANativeWindow* SurfaceTextureBridge::CreateSurface() {
-  JNIEnv* env = AttachCurrentThread();
-  ScopedJavaSurface surface(this);
-  ANativeWindow* native_window =
-      ANativeWindow_fromSurface(env, surface.j_surface().obj());
-  return native_window;
-}
-
-}  // namespace gfx
diff --git a/ui/gl/android/surface_texture_bridge.h b/ui/gl/android/surface_texture_bridge.h
deleted file mode 100644
index fc7fb54..0000000
--- a/ui/gl/android/surface_texture_bridge.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) 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 UI_GL_ANDROID_SURFACE_TEXTURE_BRIDGE_H_
-#define UI_GL_ANDROID_SURFACE_TEXTURE_BRIDGE_H_
-
-#include <jni.h>
-
-#include "base/android/scoped_java_ref.h"
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-#include "ui/gl/gl_export.h"
-
-struct ANativeWindow;
-
-namespace gfx {
-
-// This class serves as a bridge for native code to call java functions inside
-// android SurfaceTexture class.
-class GL_EXPORT SurfaceTextureBridge
-    : public base::RefCountedThreadSafe<SurfaceTextureBridge>{
- public:
-  explicit SurfaceTextureBridge(int texture_id);
-
-  // Set the listener callback, which will be invoked on the same thread that
-  // is being called from here for registration.
-  // Note: Since callbacks come in from Java objects that might outlive objects
-  // being referenced from the callback, the only robust way here is to create
-  // the callback from a weak pointer to your object.
-  void SetFrameAvailableCallback(const base::Closure& callback);
-
-  // Update the texture image to the most recent frame from the image stream.
-  void UpdateTexImage();
-
-  // Retrieve the 4x4 texture coordinate transform matrix associated with the
-  // texture image set by the most recent call to updateTexImage.
-  void GetTransformMatrix(float mtx[16]);
-
-  // Set the default size of the image buffers.
-  void SetDefaultBufferSize(int width, int height);
-
-  // Attach the SurfaceTexture to the texture currently bound to
-  // GL_TEXTURE_EXTERNAL_OES.
-  void AttachToGLContext();
-
-  // Detaches the SurfaceTexture from the context that owns its current GL
-  // texture. Must be called with that context current on the calling thread.
-  void DetachFromGLContext();
-
-  // Creates a native render surface for this surface texture.
-  // The caller must release the underlying reference when done with the handle
-  // by calling ANativeWindow_release().
-  ANativeWindow* CreateSurface();
-
-  const base::android::JavaRef<jobject>& j_surface_texture() const {
-    return j_surface_texture_;
-  }
-
- private:
-  friend class base::RefCountedThreadSafe<SurfaceTextureBridge>;
-  ~SurfaceTextureBridge();
-
-  // Java SurfaceTexture instance.
-  base::android::ScopedJavaGlobalRef<jobject> j_surface_texture_;
-
-  DISALLOW_COPY_AND_ASSIGN(SurfaceTextureBridge);
-};
-
-}  // namespace gfx
-
-#endif  // UI_GL_ANDROID_SURFACE_TEXTURE_BRIDGE_H_
diff --git a/ui/gl/android/surface_texture_listener.cc b/ui/gl/android/surface_texture_listener.cc
index 0547720..8e2f89c 100644
--- a/ui/gl/android/surface_texture_listener.cc
+++ b/ui/gl/android/surface_texture_listener.cc
@@ -8,23 +8,10 @@
 #include "base/logging.h"
 #include "base/message_loop/message_loop_proxy.h"
 #include "jni/SurfaceTextureListener_jni.h"
-#include "ui/gl/android/surface_texture_bridge.h"
+#include "ui/gl/android/surface_texture.h"
 
 namespace gfx {
 
-// static
-jobject SurfaceTextureListener::CreateSurfaceTextureListener(
-    JNIEnv* env,
-    const base::Closure& callback) {
-  // The java listener object owns and releases the native instance.
-  // This is necessary to avoid races with incoming notifications.
-  ScopedJavaLocalRef<jobject> listener(Java_SurfaceTextureListener_create(env,
-      reinterpret_cast<int>(new SurfaceTextureListener(callback))));
-
-  DCHECK(!listener.is_null());
-  return listener.Release();
-}
-
 SurfaceTextureListener::SurfaceTextureListener(const base::Closure& callback)
     : callback_(callback),
       browser_loop_(base::MessageLoopProxy::current()) {
diff --git a/ui/gl/android/surface_texture_listener.h b/ui/gl/android/surface_texture_listener.h
index faab6be..823bc30 100644
--- a/ui/gl/android/surface_texture_listener.h
+++ b/ui/gl/android/surface_texture_listener.h
@@ -18,7 +18,7 @@
 
 // Listener class for all the callbacks from android SurfaceTexture.
 class GL_EXPORT SurfaceTextureListener {
-public:
+ public:
   // Destroy this listener.
   void Destroy(JNIEnv* env, jobject obj);
 
@@ -27,18 +27,13 @@
 
   static bool RegisterSurfaceTextureListener(JNIEnv* env);
 
-private:
+ private:
+  // Native code should not hold any reference to this object, and instead pass
+  // it up to Java for being referenced by a SurfaceTexture instance.
   SurfaceTextureListener(const base::Closure& callback);
   ~SurfaceTextureListener();
 
-  friend class SurfaceTextureBridge;
-
-  // Static factory method for the creation of a SurfaceTextureListener.
-  // The native code should not hold any reference to the returned object,
-  // but only use it to pass it up to Java for being referenced by a
-  // SurfaceTexture instance.
-  static jobject CreateSurfaceTextureListener(JNIEnv* env,
-                                              const base::Closure& callback);
+  friend class SurfaceTexture;
 
   base::Closure callback_;
 
diff --git a/ui/gl/generate_bindings.py b/ui/gl/generate_bindings.py
index 3b37a17..3e51dcb 100755
--- a/ui/gl/generate_bindings.py
+++ b/ui/gl/generate_bindings.py
@@ -432,6 +432,13 @@
 { 'return_type': 'void*',
   'names': ['glMapBuffer', 'glMapBufferOES'],
   'arguments': 'GLenum target, GLenum access', },
+{ 'return_type': 'void*',
+  'names': ['glMapBufferRange'],
+  'arguments':
+      'GLenum target, GLintptr offset, GLsizeiptr length, GLenum access', },
+{ 'return_type': 'void',
+  'names': ['glFlushMappedBufferRange'],
+  'arguments': 'GLenum target, GLintptr offset, GLsizeiptr length', },
 { 'return_type': 'void',
   'names': ['glPixelStorei'],
   'arguments': 'GLenum pname, GLint param', },
diff --git a/ui/gl/gl.gyp b/ui/gl/gl.gyp
index 940578b..bee5874 100644
--- a/ui/gl/gl.gyp
+++ b/ui/gl/gl.gyp
@@ -44,8 +44,8 @@
         'android/gl_jni_registrar.h',
         'android/scoped_java_surface.cc',
         'android/scoped_java_surface.h',
-        'android/surface_texture_bridge.cc',
-        'android/surface_texture_bridge.h',
+        'android/surface_texture.cc',
+        'android/surface_texture.h',
         'android/surface_texture_listener.cc',
         'android/surface_texture_listener.h',
         'gl_bindings.h',
@@ -313,15 +313,6 @@
     ['OS=="android"' , {
       'targets': [
         {
-          'target_name': 'surface_texture_jni_headers',
-          'type': 'none',
-          'variables': {
-            'jni_gen_package': 'ui/gl',
-            'input_java_class': 'android/graphics/SurfaceTexture.class',
-          },
-          'includes': [ '../../build/jar_file_jni_generator.gypi' ],
-        },
-        {
           'target_name': 'surface_jni_headers',
           'type': 'none',
           'variables': {
@@ -334,10 +325,10 @@
           'target_name': 'gl_jni_headers',
           'type': 'none',
           'dependencies': [
-            'surface_texture_jni_headers',
             'surface_jni_headers',
           ],
           'sources': [
+            '../android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java',
             '../android/java/src/org/chromium/ui/gfx/SurfaceTextureListener.java',
           ],
           'variables': {
diff --git a/ui/gl/gl.target.darwin-arm.mk b/ui/gl/gl.target.darwin-arm.mk
index e3a29d8..1fae2431 100644
--- a/ui/gl/gl.target.darwin-arm.mk
+++ b/ui/gl/gl.target.darwin-arm.mk
@@ -105,7 +105,7 @@
 LOCAL_SRC_FILES := \
 	ui/gl/android/gl_jni_registrar.cc \
 	ui/gl/android/scoped_java_surface.cc \
-	ui/gl/android/surface_texture_bridge.cc \
+	ui/gl/android/surface_texture.cc \
 	ui/gl/android/surface_texture_listener.cc \
 	ui/gl/gl_bindings_skia_in_process.cc \
 	ui/gl/gl_context.cc \
diff --git a/ui/gl/gl.target.darwin-mips.mk b/ui/gl/gl.target.darwin-mips.mk
index 0ff5684..5c822e2 100644
--- a/ui/gl/gl.target.darwin-mips.mk
+++ b/ui/gl/gl.target.darwin-mips.mk
@@ -105,7 +105,7 @@
 LOCAL_SRC_FILES := \
 	ui/gl/android/gl_jni_registrar.cc \
 	ui/gl/android/scoped_java_surface.cc \
-	ui/gl/android/surface_texture_bridge.cc \
+	ui/gl/android/surface_texture.cc \
 	ui/gl/android/surface_texture_listener.cc \
 	ui/gl/gl_bindings_skia_in_process.cc \
 	ui/gl/gl_context.cc \
diff --git a/ui/gl/gl.target.darwin-x86.mk b/ui/gl/gl.target.darwin-x86.mk
index 9ddadf2..b859f37 100644
--- a/ui/gl/gl.target.darwin-x86.mk
+++ b/ui/gl/gl.target.darwin-x86.mk
@@ -105,7 +105,7 @@
 LOCAL_SRC_FILES := \
 	ui/gl/android/gl_jni_registrar.cc \
 	ui/gl/android/scoped_java_surface.cc \
-	ui/gl/android/surface_texture_bridge.cc \
+	ui/gl/android/surface_texture.cc \
 	ui/gl/android/surface_texture_listener.cc \
 	ui/gl/gl_bindings_skia_in_process.cc \
 	ui/gl/gl_context.cc \
diff --git a/ui/gl/gl.target.linux-arm.mk b/ui/gl/gl.target.linux-arm.mk
index e3a29d8..1fae2431 100644
--- a/ui/gl/gl.target.linux-arm.mk
+++ b/ui/gl/gl.target.linux-arm.mk
@@ -105,7 +105,7 @@
 LOCAL_SRC_FILES := \
 	ui/gl/android/gl_jni_registrar.cc \
 	ui/gl/android/scoped_java_surface.cc \
-	ui/gl/android/surface_texture_bridge.cc \
+	ui/gl/android/surface_texture.cc \
 	ui/gl/android/surface_texture_listener.cc \
 	ui/gl/gl_bindings_skia_in_process.cc \
 	ui/gl/gl_context.cc \
diff --git a/ui/gl/gl.target.linux-mips.mk b/ui/gl/gl.target.linux-mips.mk
index 0ff5684..5c822e2 100644
--- a/ui/gl/gl.target.linux-mips.mk
+++ b/ui/gl/gl.target.linux-mips.mk
@@ -105,7 +105,7 @@
 LOCAL_SRC_FILES := \
 	ui/gl/android/gl_jni_registrar.cc \
 	ui/gl/android/scoped_java_surface.cc \
-	ui/gl/android/surface_texture_bridge.cc \
+	ui/gl/android/surface_texture.cc \
 	ui/gl/android/surface_texture_listener.cc \
 	ui/gl/gl_bindings_skia_in_process.cc \
 	ui/gl/gl_context.cc \
diff --git a/ui/gl/gl.target.linux-x86.mk b/ui/gl/gl.target.linux-x86.mk
index 9ddadf2..b859f37 100644
--- a/ui/gl/gl.target.linux-x86.mk
+++ b/ui/gl/gl.target.linux-x86.mk
@@ -105,7 +105,7 @@
 LOCAL_SRC_FILES := \
 	ui/gl/android/gl_jni_registrar.cc \
 	ui/gl/android/scoped_java_surface.cc \
-	ui/gl/android/surface_texture_bridge.cc \
+	ui/gl/android/surface_texture.cc \
 	ui/gl/android/surface_texture_listener.cc \
 	ui/gl/gl_bindings_skia_in_process.cc \
 	ui/gl/gl_context.cc \
diff --git a/ui/gl/gl_bindings.h b/ui/gl/gl_bindings.h
index 987ec20..debb074 100644
--- a/ui/gl/gl_bindings.h
+++ b/ui/gl/gl_bindings.h
@@ -124,8 +124,8 @@
 #define GL_LATENCY_QUERY_CHROMIUM                        0x84F4
 
 /* GL_CHROMIUM_async_pixel_transfers */
-#define GL_ASYNC_PIXEL_TRANSFERS_COMPLETED_CHROMIUM      0x84F5
-#define GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM          0x84F6
+#define GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM         0x84F5
+#define GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM           0x84F6
 
 // GL_OES_texure_3D
 #define GL_SAMPLER_3D_OES                                0x8B5F
diff --git a/ui/gl/gl_fence.cc b/ui/gl/gl_fence.cc
index b9d9513..9b85300 100644
--- a/ui/gl/gl_fence.cc
+++ b/ui/gl/gl_fence.cc
@@ -80,6 +80,7 @@
   EGLFenceSync() {
     display_ = eglGetCurrentDisplay();
     sync_ = eglCreateSyncKHR(display_, EGL_SYNC_FENCE_KHR, NULL);
+    glFlush();
   }
 
   virtual bool HasCompleted() OVERRIDE {
@@ -90,7 +91,7 @@
   }
 
   virtual void ClientWait() OVERRIDE {
-    EGLint flags = EGL_SYNC_FLUSH_COMMANDS_BIT_KHR;
+    EGLint flags = 0;
     EGLTimeKHR time = EGL_FOREVER_KHR;
     eglClientWaitSyncKHR(display_, sync_, flags, time);
   }
diff --git a/ui/gl/gl_jni_headers.target.darwin-arm.mk b/ui/gl/gl_jni_headers.target.darwin-arm.mk
index 497701a..3af8ab0 100644
--- a/ui/gl/gl_jni_headers.target.darwin-arm.mk
+++ b/ui/gl/gl_jni_headers.target.darwin-arm.mk
@@ -12,12 +12,21 @@
 
 # Make sure our deps are built first.
 GYP_TARGET_DEPENDENCIES := \
-	$(call intermediates-dir-for,GYP,ui_gl_surface_texture_jni_headers_gyp)/surface_texture_jni_headers.stamp \
 	$(call intermediates-dir-for,GYP,ui_gl_surface_jni_headers_gyp)/surface_jni_headers.stamp
 
 
 ### Generated for rule "ui_gl_gl_gyp_gl_jni_headers_target_generate_jni_headers":
-# "{'inputs': ['../../base/android/jni_generator/jni_generator.py', '../../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/ui/gl/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['../android/java/src/org/chromium/ui/gfx/SurfaceTextureListener.java'], 'action': ['../../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/ui/gl/jni', '--optimize_generation', '0', '--jarjar', '../../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
+# "{'inputs': ['../../base/android/jni_generator/jni_generator.py', '../../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/ui/gl/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['../android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java', '../android/java/src/org/chromium/ui/gfx/SurfaceTextureListener.java'], 'action': ['../../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/ui/gl/jni', '--optimize_generation', '0', '--jarjar', '../../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: $(LOCAL_PATH)/ui/android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
+	mkdir -p $(gyp_shared_intermediate_dir)/ui/gl/jni; cd $(gyp_local_path)/ui/gl; ../../base/android/jni_generator/jni_generator.py --input_file ../android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java --output_dir "$(gyp_shared_intermediate_dir)/ui/gl/jni" --optimize_generation 0 --jarjar ../../android_webview/build/jarjar-rules.txt
+
+.PHONY: ui_gl_gl_jni_headers_gyp_rule_trigger
+ui_gl_gl_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h
+
 $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
@@ -31,12 +40,14 @@
 ### Finished generating for all rules
 
 GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h \
 	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h
 
 # Make sure our deps and generated files are built first.
 LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
 
 LOCAL_GENERATED_SOURCES := \
+	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h \
 	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h \
 	ui_gl_gl_jni_headers_gyp_rule_trigger
 
diff --git a/ui/gl/gl_jni_headers.target.darwin-mips.mk b/ui/gl/gl_jni_headers.target.darwin-mips.mk
index ee2cba4..bf1c221 100644
--- a/ui/gl/gl_jni_headers.target.darwin-mips.mk
+++ b/ui/gl/gl_jni_headers.target.darwin-mips.mk
@@ -12,12 +12,21 @@
 
 # Make sure our deps are built first.
 GYP_TARGET_DEPENDENCIES := \
-	$(call intermediates-dir-for,GYP,ui_gl_surface_texture_jni_headers_gyp)/surface_texture_jni_headers.stamp \
 	$(call intermediates-dir-for,GYP,ui_gl_surface_jni_headers_gyp)/surface_jni_headers.stamp
 
 
 ### Generated for rule "ui_gl_gl_gyp_gl_jni_headers_target_generate_jni_headers":
-# "{'inputs': ['../../base/android/jni_generator/jni_generator.py', '../../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/ui/gl/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['../android/java/src/org/chromium/ui/gfx/SurfaceTextureListener.java'], 'action': ['../../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/ui/gl/jni', '--optimize_generation', '0', '--jarjar', '../../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
+# "{'inputs': ['../../base/android/jni_generator/jni_generator.py', '../../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/ui/gl/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['../android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java', '../android/java/src/org/chromium/ui/gfx/SurfaceTextureListener.java'], 'action': ['../../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/ui/gl/jni', '--optimize_generation', '0', '--jarjar', '../../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: $(LOCAL_PATH)/ui/android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
+	mkdir -p $(gyp_shared_intermediate_dir)/ui/gl/jni; cd $(gyp_local_path)/ui/gl; ../../base/android/jni_generator/jni_generator.py --input_file ../android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java --output_dir "$(gyp_shared_intermediate_dir)/ui/gl/jni" --optimize_generation 0 --jarjar ../../android_webview/build/jarjar-rules.txt
+
+.PHONY: ui_gl_gl_jni_headers_gyp_rule_trigger
+ui_gl_gl_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h
+
 $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
@@ -31,12 +40,14 @@
 ### Finished generating for all rules
 
 GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h \
 	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h
 
 # Make sure our deps and generated files are built first.
 LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
 
 LOCAL_GENERATED_SOURCES := \
+	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h \
 	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h \
 	ui_gl_gl_jni_headers_gyp_rule_trigger
 
diff --git a/ui/gl/gl_jni_headers.target.darwin-x86.mk b/ui/gl/gl_jni_headers.target.darwin-x86.mk
index c228a07..4ad7243 100644
--- a/ui/gl/gl_jni_headers.target.darwin-x86.mk
+++ b/ui/gl/gl_jni_headers.target.darwin-x86.mk
@@ -12,12 +12,21 @@
 
 # Make sure our deps are built first.
 GYP_TARGET_DEPENDENCIES := \
-	$(call intermediates-dir-for,GYP,ui_gl_surface_texture_jni_headers_gyp)/surface_texture_jni_headers.stamp \
 	$(call intermediates-dir-for,GYP,ui_gl_surface_jni_headers_gyp)/surface_jni_headers.stamp
 
 
 ### Generated for rule "ui_gl_gl_gyp_gl_jni_headers_target_generate_jni_headers":
-# "{'inputs': ['../../base/android/jni_generator/jni_generator.py', '../../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/ui/gl/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['../android/java/src/org/chromium/ui/gfx/SurfaceTextureListener.java'], 'action': ['../../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/ui/gl/jni', '--optimize_generation', '0', '--jarjar', '../../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
+# "{'inputs': ['../../base/android/jni_generator/jni_generator.py', '../../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/ui/gl/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['../android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java', '../android/java/src/org/chromium/ui/gfx/SurfaceTextureListener.java'], 'action': ['../../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/ui/gl/jni', '--optimize_generation', '0', '--jarjar', '../../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: $(LOCAL_PATH)/ui/android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
+	mkdir -p $(gyp_shared_intermediate_dir)/ui/gl/jni; cd $(gyp_local_path)/ui/gl; ../../base/android/jni_generator/jni_generator.py --input_file ../android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java --output_dir "$(gyp_shared_intermediate_dir)/ui/gl/jni" --optimize_generation 0 --jarjar ../../android_webview/build/jarjar-rules.txt
+
+.PHONY: ui_gl_gl_jni_headers_gyp_rule_trigger
+ui_gl_gl_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h
+
 $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
@@ -31,12 +40,14 @@
 ### Finished generating for all rules
 
 GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h \
 	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h
 
 # Make sure our deps and generated files are built first.
 LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
 
 LOCAL_GENERATED_SOURCES := \
+	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h \
 	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h \
 	ui_gl_gl_jni_headers_gyp_rule_trigger
 
diff --git a/ui/gl/gl_jni_headers.target.linux-arm.mk b/ui/gl/gl_jni_headers.target.linux-arm.mk
index 497701a..3af8ab0 100644
--- a/ui/gl/gl_jni_headers.target.linux-arm.mk
+++ b/ui/gl/gl_jni_headers.target.linux-arm.mk
@@ -12,12 +12,21 @@
 
 # Make sure our deps are built first.
 GYP_TARGET_DEPENDENCIES := \
-	$(call intermediates-dir-for,GYP,ui_gl_surface_texture_jni_headers_gyp)/surface_texture_jni_headers.stamp \
 	$(call intermediates-dir-for,GYP,ui_gl_surface_jni_headers_gyp)/surface_jni_headers.stamp
 
 
 ### Generated for rule "ui_gl_gl_gyp_gl_jni_headers_target_generate_jni_headers":
-# "{'inputs': ['../../base/android/jni_generator/jni_generator.py', '../../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/ui/gl/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['../android/java/src/org/chromium/ui/gfx/SurfaceTextureListener.java'], 'action': ['../../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/ui/gl/jni', '--optimize_generation', '0', '--jarjar', '../../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
+# "{'inputs': ['../../base/android/jni_generator/jni_generator.py', '../../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/ui/gl/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['../android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java', '../android/java/src/org/chromium/ui/gfx/SurfaceTextureListener.java'], 'action': ['../../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/ui/gl/jni', '--optimize_generation', '0', '--jarjar', '../../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: $(LOCAL_PATH)/ui/android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
+	mkdir -p $(gyp_shared_intermediate_dir)/ui/gl/jni; cd $(gyp_local_path)/ui/gl; ../../base/android/jni_generator/jni_generator.py --input_file ../android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java --output_dir "$(gyp_shared_intermediate_dir)/ui/gl/jni" --optimize_generation 0 --jarjar ../../android_webview/build/jarjar-rules.txt
+
+.PHONY: ui_gl_gl_jni_headers_gyp_rule_trigger
+ui_gl_gl_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h
+
 $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
@@ -31,12 +40,14 @@
 ### Finished generating for all rules
 
 GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h \
 	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h
 
 # Make sure our deps and generated files are built first.
 LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
 
 LOCAL_GENERATED_SOURCES := \
+	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h \
 	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h \
 	ui_gl_gl_jni_headers_gyp_rule_trigger
 
diff --git a/ui/gl/gl_jni_headers.target.linux-mips.mk b/ui/gl/gl_jni_headers.target.linux-mips.mk
index ee2cba4..bf1c221 100644
--- a/ui/gl/gl_jni_headers.target.linux-mips.mk
+++ b/ui/gl/gl_jni_headers.target.linux-mips.mk
@@ -12,12 +12,21 @@
 
 # Make sure our deps are built first.
 GYP_TARGET_DEPENDENCIES := \
-	$(call intermediates-dir-for,GYP,ui_gl_surface_texture_jni_headers_gyp)/surface_texture_jni_headers.stamp \
 	$(call intermediates-dir-for,GYP,ui_gl_surface_jni_headers_gyp)/surface_jni_headers.stamp
 
 
 ### Generated for rule "ui_gl_gl_gyp_gl_jni_headers_target_generate_jni_headers":
-# "{'inputs': ['../../base/android/jni_generator/jni_generator.py', '../../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/ui/gl/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['../android/java/src/org/chromium/ui/gfx/SurfaceTextureListener.java'], 'action': ['../../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/ui/gl/jni', '--optimize_generation', '0', '--jarjar', '../../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
+# "{'inputs': ['../../base/android/jni_generator/jni_generator.py', '../../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/ui/gl/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['../android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java', '../android/java/src/org/chromium/ui/gfx/SurfaceTextureListener.java'], 'action': ['../../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/ui/gl/jni', '--optimize_generation', '0', '--jarjar', '../../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: $(LOCAL_PATH)/ui/android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
+	mkdir -p $(gyp_shared_intermediate_dir)/ui/gl/jni; cd $(gyp_local_path)/ui/gl; ../../base/android/jni_generator/jni_generator.py --input_file ../android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java --output_dir "$(gyp_shared_intermediate_dir)/ui/gl/jni" --optimize_generation 0 --jarjar ../../android_webview/build/jarjar-rules.txt
+
+.PHONY: ui_gl_gl_jni_headers_gyp_rule_trigger
+ui_gl_gl_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h
+
 $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
@@ -31,12 +40,14 @@
 ### Finished generating for all rules
 
 GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h \
 	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h
 
 # Make sure our deps and generated files are built first.
 LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
 
 LOCAL_GENERATED_SOURCES := \
+	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h \
 	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h \
 	ui_gl_gl_jni_headers_gyp_rule_trigger
 
diff --git a/ui/gl/gl_jni_headers.target.linux-x86.mk b/ui/gl/gl_jni_headers.target.linux-x86.mk
index c228a07..4ad7243 100644
--- a/ui/gl/gl_jni_headers.target.linux-x86.mk
+++ b/ui/gl/gl_jni_headers.target.linux-x86.mk
@@ -12,12 +12,21 @@
 
 # Make sure our deps are built first.
 GYP_TARGET_DEPENDENCIES := \
-	$(call intermediates-dir-for,GYP,ui_gl_surface_texture_jni_headers_gyp)/surface_texture_jni_headers.stamp \
 	$(call intermediates-dir-for,GYP,ui_gl_surface_jni_headers_gyp)/surface_jni_headers.stamp
 
 
 ### Generated for rule "ui_gl_gl_gyp_gl_jni_headers_target_generate_jni_headers":
-# "{'inputs': ['../../base/android/jni_generator/jni_generator.py', '../../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/ui/gl/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['../android/java/src/org/chromium/ui/gfx/SurfaceTextureListener.java'], 'action': ['../../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/ui/gl/jni', '--optimize_generation', '0', '--jarjar', '../../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
+# "{'inputs': ['../../base/android/jni_generator/jni_generator.py', '../../android_webview/build/jarjar-rules.txt'], 'process_outputs_as_sources': '1', 'extension': 'java', 'outputs': ['$(gyp_shared_intermediate_dir)/ui/gl/jni/%(INPUT_ROOT)s_jni.h'], 'rule_name': 'generate_jni_headers', 'rule_sources': ['../android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java', '../android/java/src/org/chromium/ui/gfx/SurfaceTextureListener.java'], 'action': ['../../base/android/jni_generator/jni_generator.py', '--input_file', '$(RULE_SOURCES)', '--output_dir', '$(gyp_shared_intermediate_dir)/ui/gl/jni', '--optimize_generation', '0', '--jarjar', '../../android_webview/build/jarjar-rules.txt'], 'message': 'Generating JNI bindings from $(RULE_SOURCES)'}":
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: gyp_local_path := $(LOCAL_PATH)
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
+$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h: $(LOCAL_PATH)/ui/android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(LOCAL_PATH)/android_webview/build/jarjar-rules.txt $(GYP_TARGET_DEPENDENCIES)
+	mkdir -p $(gyp_shared_intermediate_dir)/ui/gl/jni; cd $(gyp_local_path)/ui/gl; ../../base/android/jni_generator/jni_generator.py --input_file ../android/java/src/org/chromium/ui/gfx/SurfaceTexturePlatformWrapper.java --output_dir "$(gyp_shared_intermediate_dir)/ui/gl/jni" --optimize_generation 0 --jarjar ../../android_webview/build/jarjar-rules.txt
+
+.PHONY: ui_gl_gl_jni_headers_gyp_rule_trigger
+ui_gl_gl_jni_headers_gyp_rule_trigger: $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h
+
 $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h: gyp_local_path := $(LOCAL_PATH)
 $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
 $(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
@@ -31,12 +40,14 @@
 ### Finished generating for all rules
 
 GYP_GENERATED_OUTPUTS := \
+	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h \
 	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h
 
 # Make sure our deps and generated files are built first.
 LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
 
 LOCAL_GENERATED_SOURCES := \
+	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexturePlatformWrapper_jni.h \
 	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTextureListener_jni.h \
 	ui_gl_gl_jni_headers_gyp_rule_trigger
 
diff --git a/ui/gl/surface_texture_jni_headers.target.darwin-arm.mk b/ui/gl/surface_texture_jni_headers.target.darwin-arm.mk
deleted file mode 100644
index 100f19e..0000000
--- a/ui/gl/surface_texture_jni_headers.target.darwin-arm.mk
+++ /dev/null
@@ -1,218 +0,0 @@
-# This file is generated by gyp; do not edit.
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_CLASS := GYP
-LOCAL_MODULE := ui_gl_surface_texture_jni_headers_gyp
-LOCAL_MODULE_STEM := surface_texture_jni_headers
-LOCAL_MODULE_SUFFIX := .stamp
-LOCAL_MODULE_TAGS := optional
-gyp_intermediate_dir := $(call local-intermediates-dir)
-gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
-
-# Make sure our deps are built first.
-GYP_TARGET_DEPENDENCIES :=
-
-### Rules for action "generate_jni_headers_from_jar_file":
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(PWD)/prebuilts/sdk/17/android.jar $(GYP_TARGET_DEPENDENCIES)
-	@echo "Gyp action: Generating JNI bindings from  $(PWD)/prebuilts/sdk/17/android.jar/android/graphics/SurfaceTexture.class ($@)"
-	$(hide)cd $(gyp_local_path)/ui/gl; mkdir -p $(gyp_shared_intermediate_dir)/ui/gl/jni; ../../base/android/jni_generator/jni_generator.py -j "$(PWD)/prebuilts/sdk/17/android.jar" --input_file android/graphics/SurfaceTexture.class --output_dir "$(gyp_shared_intermediate_dir)/ui/gl/jni" --optimize_generation 0
-
-
-
-GYP_GENERATED_OUTPUTS := \
-	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h
-
-# Make sure our deps and generated files are built first.
-LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
-
-LOCAL_GENERATED_SOURCES := \
-	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h
-
-GYP_COPIED_SOURCE_ORIGIN_DIRS :=
-
-LOCAL_SRC_FILES :=
-
-
-# Flags passed to both C and C++ files.
-MY_CFLAGS_Debug := \
-	-fstack-protector \
-	--param=ssp-buffer-size=4 \
-	-Werror \
-	-fno-exceptions \
-	-fno-strict-aliasing \
-	-Wall \
-	-Wno-unused-parameter \
-	-Wno-missing-field-initializers \
-	-fvisibility=hidden \
-	-pipe \
-	-fPIC \
-	-fno-tree-sra \
-	-fuse-ld=gold \
-	-Wno-psabi \
-	-ffunction-sections \
-	-funwind-tables \
-	-g \
-	-fstack-protector \
-	-fno-short-enums \
-	-finline-limit=64 \
-	-Wa,--noexecstack \
-	-U_FORTIFY_SOURCE \
-	-Wno-extra \
-	-Wno-ignored-qualifiers \
-	-Wno-type-limits \
-	-Os \
-	-g \
-	-fomit-frame-pointer \
-	-fdata-sections \
-	-ffunction-sections
-
-MY_DEFS_Debug := \
-	'-DANGLE_DX11' \
-	'-D_FILE_OFFSET_BITS=64' \
-	'-DNO_TCMALLOC' \
-	'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
-	'-DSYSTEM_NATIVELY_SIGNALS_MEMORY_PRESSURE' \
-	'-DDISABLE_NACL' \
-	'-DCHROMIUM_BUILD' \
-	'-DUSE_LIBJPEG_TURBO=1' \
-	'-DUSE_PROPRIETARY_CODECS' \
-	'-DENABLE_CONFIGURATION_POLICY' \
-	'-DENABLE_GPU=1' \
-	'-DUSE_OPENSSL=1' \
-	'-DENABLE_EGLIMAGE=1' \
-	'-D__STDC_CONSTANT_MACROS' \
-	'-D__STDC_FORMAT_MACROS' \
-	'-DANDROID' \
-	'-D__GNU_SOURCE=1' \
-	'-DUSE_STLPORT=1' \
-	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
-	'-DCHROME_BUILD_ID=""' \
-	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
-	'-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \
-	'-D_DEBUG'
-
-
-# Include paths placed before CFLAGS/CPPFLAGS
-LOCAL_C_INCLUDES_Debug := \
-	$(PWD)/frameworks/wilhelm/include \
-	$(PWD)/bionic \
-	$(PWD)/external/stlport/stlport
-
-
-# Flags passed to only C++ (and not C) files.
-LOCAL_CPPFLAGS_Debug := \
-	-fno-rtti \
-	-fno-threadsafe-statics \
-	-fvisibility-inlines-hidden \
-	-Wsign-compare \
-	-Wno-abi \
-	-Wno-error=c++0x-compat \
-	-Wno-non-virtual-dtor \
-	-Wno-sign-promo
-
-
-# Flags passed to both C and C++ files.
-MY_CFLAGS_Release := \
-	-fstack-protector \
-	--param=ssp-buffer-size=4 \
-	-Werror \
-	-fno-exceptions \
-	-fno-strict-aliasing \
-	-Wall \
-	-Wno-unused-parameter \
-	-Wno-missing-field-initializers \
-	-fvisibility=hidden \
-	-pipe \
-	-fPIC \
-	-fno-tree-sra \
-	-fuse-ld=gold \
-	-Wno-psabi \
-	-ffunction-sections \
-	-funwind-tables \
-	-g \
-	-fstack-protector \
-	-fno-short-enums \
-	-finline-limit=64 \
-	-Wa,--noexecstack \
-	-U_FORTIFY_SOURCE \
-	-Wno-extra \
-	-Wno-ignored-qualifiers \
-	-Wno-type-limits \
-	-Os \
-	-fno-ident \
-	-fdata-sections \
-	-ffunction-sections \
-	-fomit-frame-pointer
-
-MY_DEFS_Release := \
-	'-DANGLE_DX11' \
-	'-D_FILE_OFFSET_BITS=64' \
-	'-DNO_TCMALLOC' \
-	'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
-	'-DSYSTEM_NATIVELY_SIGNALS_MEMORY_PRESSURE' \
-	'-DDISABLE_NACL' \
-	'-DCHROMIUM_BUILD' \
-	'-DUSE_LIBJPEG_TURBO=1' \
-	'-DUSE_PROPRIETARY_CODECS' \
-	'-DENABLE_CONFIGURATION_POLICY' \
-	'-DENABLE_GPU=1' \
-	'-DUSE_OPENSSL=1' \
-	'-DENABLE_EGLIMAGE=1' \
-	'-D__STDC_CONSTANT_MACROS' \
-	'-D__STDC_FORMAT_MACROS' \
-	'-DANDROID' \
-	'-D__GNU_SOURCE=1' \
-	'-DUSE_STLPORT=1' \
-	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
-	'-DCHROME_BUILD_ID=""' \
-	'-DNDEBUG' \
-	'-DNVALGRIND' \
-	'-DDYNAMIC_ANNOTATIONS_ENABLED=0' \
-	'-D_FORTIFY_SOURCE=2'
-
-
-# Include paths placed before CFLAGS/CPPFLAGS
-LOCAL_C_INCLUDES_Release := \
-	$(PWD)/frameworks/wilhelm/include \
-	$(PWD)/bionic \
-	$(PWD)/external/stlport/stlport
-
-
-# Flags passed to only C++ (and not C) files.
-LOCAL_CPPFLAGS_Release := \
-	-fno-rtti \
-	-fno-threadsafe-statics \
-	-fvisibility-inlines-hidden \
-	-Wsign-compare \
-	-Wno-abi \
-	-Wno-error=c++0x-compat \
-	-Wno-non-virtual-dtor \
-	-Wno-sign-promo
-
-
-LOCAL_CFLAGS := $(MY_CFLAGS_$(GYP_CONFIGURATION)) $(MY_DEFS_$(GYP_CONFIGURATION))
-LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES_$(GYP_CONFIGURATION))
-LOCAL_CPPFLAGS := $(LOCAL_CPPFLAGS_$(GYP_CONFIGURATION))
-### Rules for final target.
-# Add target alias to "gyp_all_modules" target.
-.PHONY: gyp_all_modules
-gyp_all_modules: ui_gl_surface_texture_jni_headers_gyp
-
-# Alias gyp target name.
-.PHONY: surface_texture_jni_headers
-surface_texture_jni_headers: ui_gl_surface_texture_jni_headers_gyp
-
-LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
-LOCAL_UNINSTALLABLE_MODULE := true
-
-include $(BUILD_SYSTEM)/base_rules.mk
-
-$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
-	$(hide) echo "Gyp timestamp: $@"
-	$(hide) mkdir -p $(dir $@)
-	$(hide) touch $@
diff --git a/ui/gl/surface_texture_jni_headers.target.darwin-mips.mk b/ui/gl/surface_texture_jni_headers.target.darwin-mips.mk
deleted file mode 100644
index c57e903..0000000
--- a/ui/gl/surface_texture_jni_headers.target.darwin-mips.mk
+++ /dev/null
@@ -1,216 +0,0 @@
-# This file is generated by gyp; do not edit.
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_CLASS := GYP
-LOCAL_MODULE := ui_gl_surface_texture_jni_headers_gyp
-LOCAL_MODULE_STEM := surface_texture_jni_headers
-LOCAL_MODULE_SUFFIX := .stamp
-LOCAL_MODULE_TAGS := optional
-gyp_intermediate_dir := $(call local-intermediates-dir)
-gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
-
-# Make sure our deps are built first.
-GYP_TARGET_DEPENDENCIES :=
-
-### Rules for action "generate_jni_headers_from_jar_file":
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(PWD)/prebuilts/sdk/17/android.jar $(GYP_TARGET_DEPENDENCIES)
-	@echo "Gyp action: Generating JNI bindings from  $(PWD)/prebuilts/sdk/17/android.jar/android/graphics/SurfaceTexture.class ($@)"
-	$(hide)cd $(gyp_local_path)/ui/gl; mkdir -p $(gyp_shared_intermediate_dir)/ui/gl/jni; ../../base/android/jni_generator/jni_generator.py -j "$(PWD)/prebuilts/sdk/17/android.jar" --input_file android/graphics/SurfaceTexture.class --output_dir "$(gyp_shared_intermediate_dir)/ui/gl/jni" --optimize_generation 0
-
-
-
-GYP_GENERATED_OUTPUTS := \
-	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h
-
-# Make sure our deps and generated files are built first.
-LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
-
-LOCAL_GENERATED_SOURCES := \
-	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h
-
-GYP_COPIED_SOURCE_ORIGIN_DIRS :=
-
-LOCAL_SRC_FILES :=
-
-
-# Flags passed to both C and C++ files.
-MY_CFLAGS_Debug := \
-	-fstack-protector \
-	--param=ssp-buffer-size=4 \
-	 \
-	-fno-exceptions \
-	-fno-strict-aliasing \
-	-Wall \
-	-Wno-unused-parameter \
-	-Wno-missing-field-initializers \
-	-fvisibility=hidden \
-	-pipe \
-	-fPIC \
-	-EL \
-	-mhard-float \
-	-ffunction-sections \
-	-funwind-tables \
-	-g \
-	-fstack-protector \
-	-fno-short-enums \
-	-finline-limit=64 \
-	-Wa,--noexecstack \
-	-U_FORTIFY_SOURCE \
-	-Wno-extra \
-	-Wno-ignored-qualifiers \
-	-Wno-type-limits \
-	-Os \
-	-g \
-	-fomit-frame-pointer \
-	-fdata-sections \
-	-ffunction-sections
-
-MY_DEFS_Debug := \
-	'-DANGLE_DX11' \
-	'-D_FILE_OFFSET_BITS=64' \
-	'-DNO_TCMALLOC' \
-	'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
-	'-DSYSTEM_NATIVELY_SIGNALS_MEMORY_PRESSURE' \
-	'-DDISABLE_NACL' \
-	'-DCHROMIUM_BUILD' \
-	'-DUSE_LIBJPEG_TURBO=1' \
-	'-DUSE_PROPRIETARY_CODECS' \
-	'-DENABLE_CONFIGURATION_POLICY' \
-	'-DENABLE_GPU=1' \
-	'-DUSE_OPENSSL=1' \
-	'-DENABLE_EGLIMAGE=1' \
-	'-D__STDC_CONSTANT_MACROS' \
-	'-D__STDC_FORMAT_MACROS' \
-	'-DANDROID' \
-	'-D__GNU_SOURCE=1' \
-	'-DUSE_STLPORT=1' \
-	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
-	'-DCHROME_BUILD_ID=""' \
-	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
-	'-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \
-	'-D_DEBUG'
-
-
-# Include paths placed before CFLAGS/CPPFLAGS
-LOCAL_C_INCLUDES_Debug := \
-	$(PWD)/frameworks/wilhelm/include \
-	$(PWD)/bionic \
-	$(PWD)/external/stlport/stlport
-
-
-# Flags passed to only C++ (and not C) files.
-LOCAL_CPPFLAGS_Debug := \
-	-fno-rtti \
-	-fno-threadsafe-statics \
-	-fvisibility-inlines-hidden \
-	-Wsign-compare \
-	-Wno-uninitialized \
-	-Wno-error=c++0x-compat \
-	-Wno-non-virtual-dtor \
-	-Wno-sign-promo
-
-
-# Flags passed to both C and C++ files.
-MY_CFLAGS_Release := \
-	-fstack-protector \
-	--param=ssp-buffer-size=4 \
-	 \
-	-fno-exceptions \
-	-fno-strict-aliasing \
-	-Wall \
-	-Wno-unused-parameter \
-	-Wno-missing-field-initializers \
-	-fvisibility=hidden \
-	-pipe \
-	-fPIC \
-	-EL \
-	-mhard-float \
-	-ffunction-sections \
-	-funwind-tables \
-	-g \
-	-fstack-protector \
-	-fno-short-enums \
-	-finline-limit=64 \
-	-Wa,--noexecstack \
-	-U_FORTIFY_SOURCE \
-	-Wno-extra \
-	-Wno-ignored-qualifiers \
-	-Wno-type-limits \
-	-Os \
-	-fno-ident \
-	-fdata-sections \
-	-ffunction-sections \
-	-fomit-frame-pointer
-
-MY_DEFS_Release := \
-	'-DANGLE_DX11' \
-	'-D_FILE_OFFSET_BITS=64' \
-	'-DNO_TCMALLOC' \
-	'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
-	'-DSYSTEM_NATIVELY_SIGNALS_MEMORY_PRESSURE' \
-	'-DDISABLE_NACL' \
-	'-DCHROMIUM_BUILD' \
-	'-DUSE_LIBJPEG_TURBO=1' \
-	'-DUSE_PROPRIETARY_CODECS' \
-	'-DENABLE_CONFIGURATION_POLICY' \
-	'-DENABLE_GPU=1' \
-	'-DUSE_OPENSSL=1' \
-	'-DENABLE_EGLIMAGE=1' \
-	'-D__STDC_CONSTANT_MACROS' \
-	'-D__STDC_FORMAT_MACROS' \
-	'-DANDROID' \
-	'-D__GNU_SOURCE=1' \
-	'-DUSE_STLPORT=1' \
-	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
-	'-DCHROME_BUILD_ID=""' \
-	'-DNDEBUG' \
-	'-DNVALGRIND' \
-	'-DDYNAMIC_ANNOTATIONS_ENABLED=0' \
-	'-D_FORTIFY_SOURCE=2'
-
-
-# Include paths placed before CFLAGS/CPPFLAGS
-LOCAL_C_INCLUDES_Release := \
-	$(PWD)/frameworks/wilhelm/include \
-	$(PWD)/bionic \
-	$(PWD)/external/stlport/stlport
-
-
-# Flags passed to only C++ (and not C) files.
-LOCAL_CPPFLAGS_Release := \
-	-fno-rtti \
-	-fno-threadsafe-statics \
-	-fvisibility-inlines-hidden \
-	-Wsign-compare \
-	-Wno-uninitialized \
-	-Wno-error=c++0x-compat \
-	-Wno-non-virtual-dtor \
-	-Wno-sign-promo
-
-
-LOCAL_CFLAGS := $(MY_CFLAGS_$(GYP_CONFIGURATION)) $(MY_DEFS_$(GYP_CONFIGURATION))
-LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES_$(GYP_CONFIGURATION))
-LOCAL_CPPFLAGS := $(LOCAL_CPPFLAGS_$(GYP_CONFIGURATION))
-### Rules for final target.
-# Add target alias to "gyp_all_modules" target.
-.PHONY: gyp_all_modules
-gyp_all_modules: ui_gl_surface_texture_jni_headers_gyp
-
-# Alias gyp target name.
-.PHONY: surface_texture_jni_headers
-surface_texture_jni_headers: ui_gl_surface_texture_jni_headers_gyp
-
-LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
-LOCAL_UNINSTALLABLE_MODULE := true
-
-include $(BUILD_SYSTEM)/base_rules.mk
-
-$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
-	$(hide) echo "Gyp timestamp: $@"
-	$(hide) mkdir -p $(dir $@)
-	$(hide) touch $@
diff --git a/ui/gl/surface_texture_jni_headers.target.darwin-x86.mk b/ui/gl/surface_texture_jni_headers.target.darwin-x86.mk
deleted file mode 100644
index e205dd3..0000000
--- a/ui/gl/surface_texture_jni_headers.target.darwin-x86.mk
+++ /dev/null
@@ -1,222 +0,0 @@
-# This file is generated by gyp; do not edit.
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_CLASS := GYP
-LOCAL_MODULE := ui_gl_surface_texture_jni_headers_gyp
-LOCAL_MODULE_STEM := surface_texture_jni_headers
-LOCAL_MODULE_SUFFIX := .stamp
-LOCAL_MODULE_TAGS := optional
-gyp_intermediate_dir := $(call local-intermediates-dir)
-gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
-
-# Make sure our deps are built first.
-GYP_TARGET_DEPENDENCIES :=
-
-### Rules for action "generate_jni_headers_from_jar_file":
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(PWD)/prebuilts/sdk/17/android.jar $(GYP_TARGET_DEPENDENCIES)
-	@echo "Gyp action: Generating JNI bindings from  $(PWD)/prebuilts/sdk/17/android.jar/android/graphics/SurfaceTexture.class ($@)"
-	$(hide)cd $(gyp_local_path)/ui/gl; mkdir -p $(gyp_shared_intermediate_dir)/ui/gl/jni; ../../base/android/jni_generator/jni_generator.py -j "$(PWD)/prebuilts/sdk/17/android.jar" --input_file android/graphics/SurfaceTexture.class --output_dir "$(gyp_shared_intermediate_dir)/ui/gl/jni" --optimize_generation 0
-
-
-
-GYP_GENERATED_OUTPUTS := \
-	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h
-
-# Make sure our deps and generated files are built first.
-LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
-
-LOCAL_GENERATED_SOURCES := \
-	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h
-
-GYP_COPIED_SOURCE_ORIGIN_DIRS :=
-
-LOCAL_SRC_FILES :=
-
-
-# Flags passed to both C and C++ files.
-MY_CFLAGS_Debug := \
-	--param=ssp-buffer-size=4 \
-	-Werror \
-	-fno-exceptions \
-	-fno-strict-aliasing \
-	-Wall \
-	-Wno-unused-parameter \
-	-Wno-missing-field-initializers \
-	-fvisibility=hidden \
-	-pipe \
-	-fPIC \
-	-m32 \
-	-mmmx \
-	-march=pentium4 \
-	-msse2 \
-	-mfpmath=sse \
-	-fuse-ld=gold \
-	-ffunction-sections \
-	-funwind-tables \
-	-g \
-	-fno-short-enums \
-	-finline-limit=64 \
-	-Wa,--noexecstack \
-	-U_FORTIFY_SOURCE \
-	-Wno-extra \
-	-Wno-ignored-qualifiers \
-	-Wno-type-limits \
-	-fno-stack-protector \
-	-Os \
-	-g \
-	-fomit-frame-pointer \
-	-fdata-sections \
-	-ffunction-sections
-
-MY_DEFS_Debug := \
-	'-DANGLE_DX11' \
-	'-D_FILE_OFFSET_BITS=64' \
-	'-DNO_TCMALLOC' \
-	'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
-	'-DSYSTEM_NATIVELY_SIGNALS_MEMORY_PRESSURE' \
-	'-DDISABLE_NACL' \
-	'-DCHROMIUM_BUILD' \
-	'-DUSE_LIBJPEG_TURBO=1' \
-	'-DUSE_PROPRIETARY_CODECS' \
-	'-DENABLE_CONFIGURATION_POLICY' \
-	'-DENABLE_GPU=1' \
-	'-DUSE_OPENSSL=1' \
-	'-DENABLE_EGLIMAGE=1' \
-	'-D__STDC_CONSTANT_MACROS' \
-	'-D__STDC_FORMAT_MACROS' \
-	'-DANDROID' \
-	'-D__GNU_SOURCE=1' \
-	'-DUSE_STLPORT=1' \
-	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
-	'-DCHROME_BUILD_ID=""' \
-	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
-	'-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \
-	'-D_DEBUG'
-
-
-# Include paths placed before CFLAGS/CPPFLAGS
-LOCAL_C_INCLUDES_Debug := \
-	$(PWD)/frameworks/wilhelm/include \
-	$(PWD)/bionic \
-	$(PWD)/external/stlport/stlport
-
-
-# Flags passed to only C++ (and not C) files.
-LOCAL_CPPFLAGS_Debug := \
-	-fno-rtti \
-	-fno-threadsafe-statics \
-	-fvisibility-inlines-hidden \
-	-Wsign-compare \
-	-Wno-error=c++0x-compat \
-	-Wno-non-virtual-dtor \
-	-Wno-sign-promo
-
-
-# Flags passed to both C and C++ files.
-MY_CFLAGS_Release := \
-	--param=ssp-buffer-size=4 \
-	-Werror \
-	-fno-exceptions \
-	-fno-strict-aliasing \
-	-Wall \
-	-Wno-unused-parameter \
-	-Wno-missing-field-initializers \
-	-fvisibility=hidden \
-	-pipe \
-	-fPIC \
-	-m32 \
-	-mmmx \
-	-march=pentium4 \
-	-msse2 \
-	-mfpmath=sse \
-	-fuse-ld=gold \
-	-ffunction-sections \
-	-funwind-tables \
-	-g \
-	-fno-short-enums \
-	-finline-limit=64 \
-	-Wa,--noexecstack \
-	-U_FORTIFY_SOURCE \
-	-Wno-extra \
-	-Wno-ignored-qualifiers \
-	-Wno-type-limits \
-	-fno-stack-protector \
-	-Os \
-	-fno-ident \
-	-fdata-sections \
-	-ffunction-sections \
-	-fomit-frame-pointer \
-	-fno-unwind-tables \
-	-fno-asynchronous-unwind-tables
-
-MY_DEFS_Release := \
-	'-DANGLE_DX11' \
-	'-D_FILE_OFFSET_BITS=64' \
-	'-DNO_TCMALLOC' \
-	'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
-	'-DSYSTEM_NATIVELY_SIGNALS_MEMORY_PRESSURE' \
-	'-DDISABLE_NACL' \
-	'-DCHROMIUM_BUILD' \
-	'-DUSE_LIBJPEG_TURBO=1' \
-	'-DUSE_PROPRIETARY_CODECS' \
-	'-DENABLE_CONFIGURATION_POLICY' \
-	'-DENABLE_GPU=1' \
-	'-DUSE_OPENSSL=1' \
-	'-DENABLE_EGLIMAGE=1' \
-	'-D__STDC_CONSTANT_MACROS' \
-	'-D__STDC_FORMAT_MACROS' \
-	'-DANDROID' \
-	'-D__GNU_SOURCE=1' \
-	'-DUSE_STLPORT=1' \
-	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
-	'-DCHROME_BUILD_ID=""' \
-	'-DNDEBUG' \
-	'-DNVALGRIND' \
-	'-DDYNAMIC_ANNOTATIONS_ENABLED=0' \
-	'-D_FORTIFY_SOURCE=2'
-
-
-# Include paths placed before CFLAGS/CPPFLAGS
-LOCAL_C_INCLUDES_Release := \
-	$(PWD)/frameworks/wilhelm/include \
-	$(PWD)/bionic \
-	$(PWD)/external/stlport/stlport
-
-
-# Flags passed to only C++ (and not C) files.
-LOCAL_CPPFLAGS_Release := \
-	-fno-rtti \
-	-fno-threadsafe-statics \
-	-fvisibility-inlines-hidden \
-	-Wsign-compare \
-	-Wno-error=c++0x-compat \
-	-Wno-non-virtual-dtor \
-	-Wno-sign-promo
-
-
-LOCAL_CFLAGS := $(MY_CFLAGS_$(GYP_CONFIGURATION)) $(MY_DEFS_$(GYP_CONFIGURATION))
-LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES_$(GYP_CONFIGURATION))
-LOCAL_CPPFLAGS := $(LOCAL_CPPFLAGS_$(GYP_CONFIGURATION))
-### Rules for final target.
-# Add target alias to "gyp_all_modules" target.
-.PHONY: gyp_all_modules
-gyp_all_modules: ui_gl_surface_texture_jni_headers_gyp
-
-# Alias gyp target name.
-.PHONY: surface_texture_jni_headers
-surface_texture_jni_headers: ui_gl_surface_texture_jni_headers_gyp
-
-LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
-LOCAL_UNINSTALLABLE_MODULE := true
-
-include $(BUILD_SYSTEM)/base_rules.mk
-
-$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
-	$(hide) echo "Gyp timestamp: $@"
-	$(hide) mkdir -p $(dir $@)
-	$(hide) touch $@
diff --git a/ui/gl/surface_texture_jni_headers.target.linux-arm.mk b/ui/gl/surface_texture_jni_headers.target.linux-arm.mk
deleted file mode 100644
index 100f19e..0000000
--- a/ui/gl/surface_texture_jni_headers.target.linux-arm.mk
+++ /dev/null
@@ -1,218 +0,0 @@
-# This file is generated by gyp; do not edit.
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_CLASS := GYP
-LOCAL_MODULE := ui_gl_surface_texture_jni_headers_gyp
-LOCAL_MODULE_STEM := surface_texture_jni_headers
-LOCAL_MODULE_SUFFIX := .stamp
-LOCAL_MODULE_TAGS := optional
-gyp_intermediate_dir := $(call local-intermediates-dir)
-gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
-
-# Make sure our deps are built first.
-GYP_TARGET_DEPENDENCIES :=
-
-### Rules for action "generate_jni_headers_from_jar_file":
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(PWD)/prebuilts/sdk/17/android.jar $(GYP_TARGET_DEPENDENCIES)
-	@echo "Gyp action: Generating JNI bindings from  $(PWD)/prebuilts/sdk/17/android.jar/android/graphics/SurfaceTexture.class ($@)"
-	$(hide)cd $(gyp_local_path)/ui/gl; mkdir -p $(gyp_shared_intermediate_dir)/ui/gl/jni; ../../base/android/jni_generator/jni_generator.py -j "$(PWD)/prebuilts/sdk/17/android.jar" --input_file android/graphics/SurfaceTexture.class --output_dir "$(gyp_shared_intermediate_dir)/ui/gl/jni" --optimize_generation 0
-
-
-
-GYP_GENERATED_OUTPUTS := \
-	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h
-
-# Make sure our deps and generated files are built first.
-LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
-
-LOCAL_GENERATED_SOURCES := \
-	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h
-
-GYP_COPIED_SOURCE_ORIGIN_DIRS :=
-
-LOCAL_SRC_FILES :=
-
-
-# Flags passed to both C and C++ files.
-MY_CFLAGS_Debug := \
-	-fstack-protector \
-	--param=ssp-buffer-size=4 \
-	-Werror \
-	-fno-exceptions \
-	-fno-strict-aliasing \
-	-Wall \
-	-Wno-unused-parameter \
-	-Wno-missing-field-initializers \
-	-fvisibility=hidden \
-	-pipe \
-	-fPIC \
-	-fno-tree-sra \
-	-fuse-ld=gold \
-	-Wno-psabi \
-	-ffunction-sections \
-	-funwind-tables \
-	-g \
-	-fstack-protector \
-	-fno-short-enums \
-	-finline-limit=64 \
-	-Wa,--noexecstack \
-	-U_FORTIFY_SOURCE \
-	-Wno-extra \
-	-Wno-ignored-qualifiers \
-	-Wno-type-limits \
-	-Os \
-	-g \
-	-fomit-frame-pointer \
-	-fdata-sections \
-	-ffunction-sections
-
-MY_DEFS_Debug := \
-	'-DANGLE_DX11' \
-	'-D_FILE_OFFSET_BITS=64' \
-	'-DNO_TCMALLOC' \
-	'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
-	'-DSYSTEM_NATIVELY_SIGNALS_MEMORY_PRESSURE' \
-	'-DDISABLE_NACL' \
-	'-DCHROMIUM_BUILD' \
-	'-DUSE_LIBJPEG_TURBO=1' \
-	'-DUSE_PROPRIETARY_CODECS' \
-	'-DENABLE_CONFIGURATION_POLICY' \
-	'-DENABLE_GPU=1' \
-	'-DUSE_OPENSSL=1' \
-	'-DENABLE_EGLIMAGE=1' \
-	'-D__STDC_CONSTANT_MACROS' \
-	'-D__STDC_FORMAT_MACROS' \
-	'-DANDROID' \
-	'-D__GNU_SOURCE=1' \
-	'-DUSE_STLPORT=1' \
-	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
-	'-DCHROME_BUILD_ID=""' \
-	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
-	'-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \
-	'-D_DEBUG'
-
-
-# Include paths placed before CFLAGS/CPPFLAGS
-LOCAL_C_INCLUDES_Debug := \
-	$(PWD)/frameworks/wilhelm/include \
-	$(PWD)/bionic \
-	$(PWD)/external/stlport/stlport
-
-
-# Flags passed to only C++ (and not C) files.
-LOCAL_CPPFLAGS_Debug := \
-	-fno-rtti \
-	-fno-threadsafe-statics \
-	-fvisibility-inlines-hidden \
-	-Wsign-compare \
-	-Wno-abi \
-	-Wno-error=c++0x-compat \
-	-Wno-non-virtual-dtor \
-	-Wno-sign-promo
-
-
-# Flags passed to both C and C++ files.
-MY_CFLAGS_Release := \
-	-fstack-protector \
-	--param=ssp-buffer-size=4 \
-	-Werror \
-	-fno-exceptions \
-	-fno-strict-aliasing \
-	-Wall \
-	-Wno-unused-parameter \
-	-Wno-missing-field-initializers \
-	-fvisibility=hidden \
-	-pipe \
-	-fPIC \
-	-fno-tree-sra \
-	-fuse-ld=gold \
-	-Wno-psabi \
-	-ffunction-sections \
-	-funwind-tables \
-	-g \
-	-fstack-protector \
-	-fno-short-enums \
-	-finline-limit=64 \
-	-Wa,--noexecstack \
-	-U_FORTIFY_SOURCE \
-	-Wno-extra \
-	-Wno-ignored-qualifiers \
-	-Wno-type-limits \
-	-Os \
-	-fno-ident \
-	-fdata-sections \
-	-ffunction-sections \
-	-fomit-frame-pointer
-
-MY_DEFS_Release := \
-	'-DANGLE_DX11' \
-	'-D_FILE_OFFSET_BITS=64' \
-	'-DNO_TCMALLOC' \
-	'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
-	'-DSYSTEM_NATIVELY_SIGNALS_MEMORY_PRESSURE' \
-	'-DDISABLE_NACL' \
-	'-DCHROMIUM_BUILD' \
-	'-DUSE_LIBJPEG_TURBO=1' \
-	'-DUSE_PROPRIETARY_CODECS' \
-	'-DENABLE_CONFIGURATION_POLICY' \
-	'-DENABLE_GPU=1' \
-	'-DUSE_OPENSSL=1' \
-	'-DENABLE_EGLIMAGE=1' \
-	'-D__STDC_CONSTANT_MACROS' \
-	'-D__STDC_FORMAT_MACROS' \
-	'-DANDROID' \
-	'-D__GNU_SOURCE=1' \
-	'-DUSE_STLPORT=1' \
-	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
-	'-DCHROME_BUILD_ID=""' \
-	'-DNDEBUG' \
-	'-DNVALGRIND' \
-	'-DDYNAMIC_ANNOTATIONS_ENABLED=0' \
-	'-D_FORTIFY_SOURCE=2'
-
-
-# Include paths placed before CFLAGS/CPPFLAGS
-LOCAL_C_INCLUDES_Release := \
-	$(PWD)/frameworks/wilhelm/include \
-	$(PWD)/bionic \
-	$(PWD)/external/stlport/stlport
-
-
-# Flags passed to only C++ (and not C) files.
-LOCAL_CPPFLAGS_Release := \
-	-fno-rtti \
-	-fno-threadsafe-statics \
-	-fvisibility-inlines-hidden \
-	-Wsign-compare \
-	-Wno-abi \
-	-Wno-error=c++0x-compat \
-	-Wno-non-virtual-dtor \
-	-Wno-sign-promo
-
-
-LOCAL_CFLAGS := $(MY_CFLAGS_$(GYP_CONFIGURATION)) $(MY_DEFS_$(GYP_CONFIGURATION))
-LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES_$(GYP_CONFIGURATION))
-LOCAL_CPPFLAGS := $(LOCAL_CPPFLAGS_$(GYP_CONFIGURATION))
-### Rules for final target.
-# Add target alias to "gyp_all_modules" target.
-.PHONY: gyp_all_modules
-gyp_all_modules: ui_gl_surface_texture_jni_headers_gyp
-
-# Alias gyp target name.
-.PHONY: surface_texture_jni_headers
-surface_texture_jni_headers: ui_gl_surface_texture_jni_headers_gyp
-
-LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
-LOCAL_UNINSTALLABLE_MODULE := true
-
-include $(BUILD_SYSTEM)/base_rules.mk
-
-$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
-	$(hide) echo "Gyp timestamp: $@"
-	$(hide) mkdir -p $(dir $@)
-	$(hide) touch $@
diff --git a/ui/gl/surface_texture_jni_headers.target.linux-mips.mk b/ui/gl/surface_texture_jni_headers.target.linux-mips.mk
deleted file mode 100644
index c57e903..0000000
--- a/ui/gl/surface_texture_jni_headers.target.linux-mips.mk
+++ /dev/null
@@ -1,216 +0,0 @@
-# This file is generated by gyp; do not edit.
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_CLASS := GYP
-LOCAL_MODULE := ui_gl_surface_texture_jni_headers_gyp
-LOCAL_MODULE_STEM := surface_texture_jni_headers
-LOCAL_MODULE_SUFFIX := .stamp
-LOCAL_MODULE_TAGS := optional
-gyp_intermediate_dir := $(call local-intermediates-dir)
-gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
-
-# Make sure our deps are built first.
-GYP_TARGET_DEPENDENCIES :=
-
-### Rules for action "generate_jni_headers_from_jar_file":
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(PWD)/prebuilts/sdk/17/android.jar $(GYP_TARGET_DEPENDENCIES)
-	@echo "Gyp action: Generating JNI bindings from  $(PWD)/prebuilts/sdk/17/android.jar/android/graphics/SurfaceTexture.class ($@)"
-	$(hide)cd $(gyp_local_path)/ui/gl; mkdir -p $(gyp_shared_intermediate_dir)/ui/gl/jni; ../../base/android/jni_generator/jni_generator.py -j "$(PWD)/prebuilts/sdk/17/android.jar" --input_file android/graphics/SurfaceTexture.class --output_dir "$(gyp_shared_intermediate_dir)/ui/gl/jni" --optimize_generation 0
-
-
-
-GYP_GENERATED_OUTPUTS := \
-	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h
-
-# Make sure our deps and generated files are built first.
-LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
-
-LOCAL_GENERATED_SOURCES := \
-	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h
-
-GYP_COPIED_SOURCE_ORIGIN_DIRS :=
-
-LOCAL_SRC_FILES :=
-
-
-# Flags passed to both C and C++ files.
-MY_CFLAGS_Debug := \
-	-fstack-protector \
-	--param=ssp-buffer-size=4 \
-	 \
-	-fno-exceptions \
-	-fno-strict-aliasing \
-	-Wall \
-	-Wno-unused-parameter \
-	-Wno-missing-field-initializers \
-	-fvisibility=hidden \
-	-pipe \
-	-fPIC \
-	-EL \
-	-mhard-float \
-	-ffunction-sections \
-	-funwind-tables \
-	-g \
-	-fstack-protector \
-	-fno-short-enums \
-	-finline-limit=64 \
-	-Wa,--noexecstack \
-	-U_FORTIFY_SOURCE \
-	-Wno-extra \
-	-Wno-ignored-qualifiers \
-	-Wno-type-limits \
-	-Os \
-	-g \
-	-fomit-frame-pointer \
-	-fdata-sections \
-	-ffunction-sections
-
-MY_DEFS_Debug := \
-	'-DANGLE_DX11' \
-	'-D_FILE_OFFSET_BITS=64' \
-	'-DNO_TCMALLOC' \
-	'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
-	'-DSYSTEM_NATIVELY_SIGNALS_MEMORY_PRESSURE' \
-	'-DDISABLE_NACL' \
-	'-DCHROMIUM_BUILD' \
-	'-DUSE_LIBJPEG_TURBO=1' \
-	'-DUSE_PROPRIETARY_CODECS' \
-	'-DENABLE_CONFIGURATION_POLICY' \
-	'-DENABLE_GPU=1' \
-	'-DUSE_OPENSSL=1' \
-	'-DENABLE_EGLIMAGE=1' \
-	'-D__STDC_CONSTANT_MACROS' \
-	'-D__STDC_FORMAT_MACROS' \
-	'-DANDROID' \
-	'-D__GNU_SOURCE=1' \
-	'-DUSE_STLPORT=1' \
-	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
-	'-DCHROME_BUILD_ID=""' \
-	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
-	'-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \
-	'-D_DEBUG'
-
-
-# Include paths placed before CFLAGS/CPPFLAGS
-LOCAL_C_INCLUDES_Debug := \
-	$(PWD)/frameworks/wilhelm/include \
-	$(PWD)/bionic \
-	$(PWD)/external/stlport/stlport
-
-
-# Flags passed to only C++ (and not C) files.
-LOCAL_CPPFLAGS_Debug := \
-	-fno-rtti \
-	-fno-threadsafe-statics \
-	-fvisibility-inlines-hidden \
-	-Wsign-compare \
-	-Wno-uninitialized \
-	-Wno-error=c++0x-compat \
-	-Wno-non-virtual-dtor \
-	-Wno-sign-promo
-
-
-# Flags passed to both C and C++ files.
-MY_CFLAGS_Release := \
-	-fstack-protector \
-	--param=ssp-buffer-size=4 \
-	 \
-	-fno-exceptions \
-	-fno-strict-aliasing \
-	-Wall \
-	-Wno-unused-parameter \
-	-Wno-missing-field-initializers \
-	-fvisibility=hidden \
-	-pipe \
-	-fPIC \
-	-EL \
-	-mhard-float \
-	-ffunction-sections \
-	-funwind-tables \
-	-g \
-	-fstack-protector \
-	-fno-short-enums \
-	-finline-limit=64 \
-	-Wa,--noexecstack \
-	-U_FORTIFY_SOURCE \
-	-Wno-extra \
-	-Wno-ignored-qualifiers \
-	-Wno-type-limits \
-	-Os \
-	-fno-ident \
-	-fdata-sections \
-	-ffunction-sections \
-	-fomit-frame-pointer
-
-MY_DEFS_Release := \
-	'-DANGLE_DX11' \
-	'-D_FILE_OFFSET_BITS=64' \
-	'-DNO_TCMALLOC' \
-	'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
-	'-DSYSTEM_NATIVELY_SIGNALS_MEMORY_PRESSURE' \
-	'-DDISABLE_NACL' \
-	'-DCHROMIUM_BUILD' \
-	'-DUSE_LIBJPEG_TURBO=1' \
-	'-DUSE_PROPRIETARY_CODECS' \
-	'-DENABLE_CONFIGURATION_POLICY' \
-	'-DENABLE_GPU=1' \
-	'-DUSE_OPENSSL=1' \
-	'-DENABLE_EGLIMAGE=1' \
-	'-D__STDC_CONSTANT_MACROS' \
-	'-D__STDC_FORMAT_MACROS' \
-	'-DANDROID' \
-	'-D__GNU_SOURCE=1' \
-	'-DUSE_STLPORT=1' \
-	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
-	'-DCHROME_BUILD_ID=""' \
-	'-DNDEBUG' \
-	'-DNVALGRIND' \
-	'-DDYNAMIC_ANNOTATIONS_ENABLED=0' \
-	'-D_FORTIFY_SOURCE=2'
-
-
-# Include paths placed before CFLAGS/CPPFLAGS
-LOCAL_C_INCLUDES_Release := \
-	$(PWD)/frameworks/wilhelm/include \
-	$(PWD)/bionic \
-	$(PWD)/external/stlport/stlport
-
-
-# Flags passed to only C++ (and not C) files.
-LOCAL_CPPFLAGS_Release := \
-	-fno-rtti \
-	-fno-threadsafe-statics \
-	-fvisibility-inlines-hidden \
-	-Wsign-compare \
-	-Wno-uninitialized \
-	-Wno-error=c++0x-compat \
-	-Wno-non-virtual-dtor \
-	-Wno-sign-promo
-
-
-LOCAL_CFLAGS := $(MY_CFLAGS_$(GYP_CONFIGURATION)) $(MY_DEFS_$(GYP_CONFIGURATION))
-LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES_$(GYP_CONFIGURATION))
-LOCAL_CPPFLAGS := $(LOCAL_CPPFLAGS_$(GYP_CONFIGURATION))
-### Rules for final target.
-# Add target alias to "gyp_all_modules" target.
-.PHONY: gyp_all_modules
-gyp_all_modules: ui_gl_surface_texture_jni_headers_gyp
-
-# Alias gyp target name.
-.PHONY: surface_texture_jni_headers
-surface_texture_jni_headers: ui_gl_surface_texture_jni_headers_gyp
-
-LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
-LOCAL_UNINSTALLABLE_MODULE := true
-
-include $(BUILD_SYSTEM)/base_rules.mk
-
-$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
-	$(hide) echo "Gyp timestamp: $@"
-	$(hide) mkdir -p $(dir $@)
-	$(hide) touch $@
diff --git a/ui/gl/surface_texture_jni_headers.target.linux-x86.mk b/ui/gl/surface_texture_jni_headers.target.linux-x86.mk
deleted file mode 100644
index e205dd3..0000000
--- a/ui/gl/surface_texture_jni_headers.target.linux-x86.mk
+++ /dev/null
@@ -1,222 +0,0 @@
-# This file is generated by gyp; do not edit.
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_CLASS := GYP
-LOCAL_MODULE := ui_gl_surface_texture_jni_headers_gyp
-LOCAL_MODULE_STEM := surface_texture_jni_headers
-LOCAL_MODULE_SUFFIX := .stamp
-LOCAL_MODULE_TAGS := optional
-gyp_intermediate_dir := $(call local-intermediates-dir)
-gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
-
-# Make sure our deps are built first.
-GYP_TARGET_DEPENDENCIES :=
-
-### Rules for action "generate_jni_headers_from_jar_file":
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: gyp_local_path := $(LOCAL_PATH)
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: gyp_intermediate_dir := $(abspath $(gyp_intermediate_dir))
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: gyp_shared_intermediate_dir := $(abspath $(gyp_shared_intermediate_dir))
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: export PATH := $(subst $(ANDROID_BUILD_PATHS),,$(PATH))
-$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h: $(LOCAL_PATH)/base/android/jni_generator/jni_generator.py $(PWD)/prebuilts/sdk/17/android.jar $(GYP_TARGET_DEPENDENCIES)
-	@echo "Gyp action: Generating JNI bindings from  $(PWD)/prebuilts/sdk/17/android.jar/android/graphics/SurfaceTexture.class ($@)"
-	$(hide)cd $(gyp_local_path)/ui/gl; mkdir -p $(gyp_shared_intermediate_dir)/ui/gl/jni; ../../base/android/jni_generator/jni_generator.py -j "$(PWD)/prebuilts/sdk/17/android.jar" --input_file android/graphics/SurfaceTexture.class --output_dir "$(gyp_shared_intermediate_dir)/ui/gl/jni" --optimize_generation 0
-
-
-
-GYP_GENERATED_OUTPUTS := \
-	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h
-
-# Make sure our deps and generated files are built first.
-LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
-
-LOCAL_GENERATED_SOURCES := \
-	$(gyp_shared_intermediate_dir)/ui/gl/jni/SurfaceTexture_jni.h
-
-GYP_COPIED_SOURCE_ORIGIN_DIRS :=
-
-LOCAL_SRC_FILES :=
-
-
-# Flags passed to both C and C++ files.
-MY_CFLAGS_Debug := \
-	--param=ssp-buffer-size=4 \
-	-Werror \
-	-fno-exceptions \
-	-fno-strict-aliasing \
-	-Wall \
-	-Wno-unused-parameter \
-	-Wno-missing-field-initializers \
-	-fvisibility=hidden \
-	-pipe \
-	-fPIC \
-	-m32 \
-	-mmmx \
-	-march=pentium4 \
-	-msse2 \
-	-mfpmath=sse \
-	-fuse-ld=gold \
-	-ffunction-sections \
-	-funwind-tables \
-	-g \
-	-fno-short-enums \
-	-finline-limit=64 \
-	-Wa,--noexecstack \
-	-U_FORTIFY_SOURCE \
-	-Wno-extra \
-	-Wno-ignored-qualifiers \
-	-Wno-type-limits \
-	-fno-stack-protector \
-	-Os \
-	-g \
-	-fomit-frame-pointer \
-	-fdata-sections \
-	-ffunction-sections
-
-MY_DEFS_Debug := \
-	'-DANGLE_DX11' \
-	'-D_FILE_OFFSET_BITS=64' \
-	'-DNO_TCMALLOC' \
-	'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
-	'-DSYSTEM_NATIVELY_SIGNALS_MEMORY_PRESSURE' \
-	'-DDISABLE_NACL' \
-	'-DCHROMIUM_BUILD' \
-	'-DUSE_LIBJPEG_TURBO=1' \
-	'-DUSE_PROPRIETARY_CODECS' \
-	'-DENABLE_CONFIGURATION_POLICY' \
-	'-DENABLE_GPU=1' \
-	'-DUSE_OPENSSL=1' \
-	'-DENABLE_EGLIMAGE=1' \
-	'-D__STDC_CONSTANT_MACROS' \
-	'-D__STDC_FORMAT_MACROS' \
-	'-DANDROID' \
-	'-D__GNU_SOURCE=1' \
-	'-DUSE_STLPORT=1' \
-	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
-	'-DCHROME_BUILD_ID=""' \
-	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
-	'-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \
-	'-D_DEBUG'
-
-
-# Include paths placed before CFLAGS/CPPFLAGS
-LOCAL_C_INCLUDES_Debug := \
-	$(PWD)/frameworks/wilhelm/include \
-	$(PWD)/bionic \
-	$(PWD)/external/stlport/stlport
-
-
-# Flags passed to only C++ (and not C) files.
-LOCAL_CPPFLAGS_Debug := \
-	-fno-rtti \
-	-fno-threadsafe-statics \
-	-fvisibility-inlines-hidden \
-	-Wsign-compare \
-	-Wno-error=c++0x-compat \
-	-Wno-non-virtual-dtor \
-	-Wno-sign-promo
-
-
-# Flags passed to both C and C++ files.
-MY_CFLAGS_Release := \
-	--param=ssp-buffer-size=4 \
-	-Werror \
-	-fno-exceptions \
-	-fno-strict-aliasing \
-	-Wall \
-	-Wno-unused-parameter \
-	-Wno-missing-field-initializers \
-	-fvisibility=hidden \
-	-pipe \
-	-fPIC \
-	-m32 \
-	-mmmx \
-	-march=pentium4 \
-	-msse2 \
-	-mfpmath=sse \
-	-fuse-ld=gold \
-	-ffunction-sections \
-	-funwind-tables \
-	-g \
-	-fno-short-enums \
-	-finline-limit=64 \
-	-Wa,--noexecstack \
-	-U_FORTIFY_SOURCE \
-	-Wno-extra \
-	-Wno-ignored-qualifiers \
-	-Wno-type-limits \
-	-fno-stack-protector \
-	-Os \
-	-fno-ident \
-	-fdata-sections \
-	-ffunction-sections \
-	-fomit-frame-pointer \
-	-fno-unwind-tables \
-	-fno-asynchronous-unwind-tables
-
-MY_DEFS_Release := \
-	'-DANGLE_DX11' \
-	'-D_FILE_OFFSET_BITS=64' \
-	'-DNO_TCMALLOC' \
-	'-DDISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY' \
-	'-DSYSTEM_NATIVELY_SIGNALS_MEMORY_PRESSURE' \
-	'-DDISABLE_NACL' \
-	'-DCHROMIUM_BUILD' \
-	'-DUSE_LIBJPEG_TURBO=1' \
-	'-DUSE_PROPRIETARY_CODECS' \
-	'-DENABLE_CONFIGURATION_POLICY' \
-	'-DENABLE_GPU=1' \
-	'-DUSE_OPENSSL=1' \
-	'-DENABLE_EGLIMAGE=1' \
-	'-D__STDC_CONSTANT_MACROS' \
-	'-D__STDC_FORMAT_MACROS' \
-	'-DANDROID' \
-	'-D__GNU_SOURCE=1' \
-	'-DUSE_STLPORT=1' \
-	'-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
-	'-DCHROME_BUILD_ID=""' \
-	'-DNDEBUG' \
-	'-DNVALGRIND' \
-	'-DDYNAMIC_ANNOTATIONS_ENABLED=0' \
-	'-D_FORTIFY_SOURCE=2'
-
-
-# Include paths placed before CFLAGS/CPPFLAGS
-LOCAL_C_INCLUDES_Release := \
-	$(PWD)/frameworks/wilhelm/include \
-	$(PWD)/bionic \
-	$(PWD)/external/stlport/stlport
-
-
-# Flags passed to only C++ (and not C) files.
-LOCAL_CPPFLAGS_Release := \
-	-fno-rtti \
-	-fno-threadsafe-statics \
-	-fvisibility-inlines-hidden \
-	-Wsign-compare \
-	-Wno-error=c++0x-compat \
-	-Wno-non-virtual-dtor \
-	-Wno-sign-promo
-
-
-LOCAL_CFLAGS := $(MY_CFLAGS_$(GYP_CONFIGURATION)) $(MY_DEFS_$(GYP_CONFIGURATION))
-LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES_$(GYP_CONFIGURATION))
-LOCAL_CPPFLAGS := $(LOCAL_CPPFLAGS_$(GYP_CONFIGURATION))
-### Rules for final target.
-# Add target alias to "gyp_all_modules" target.
-.PHONY: gyp_all_modules
-gyp_all_modules: ui_gl_surface_texture_jni_headers_gyp
-
-# Alias gyp target name.
-.PHONY: surface_texture_jni_headers
-surface_texture_jni_headers: ui_gl_surface_texture_jni_headers_gyp
-
-LOCAL_MODULE_PATH := $(PRODUCT_OUT)/gyp_stamp
-LOCAL_UNINSTALLABLE_MODULE := true
-
-include $(BUILD_SYSTEM)/base_rules.mk
-
-$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)
-	$(hide) echo "Gyp timestamp: $@"
-	$(hide) mkdir -p $(dir $@)
-	$(hide) touch $@
diff --git a/ui/keyboard/keyboard_ui_handler.cc b/ui/keyboard/keyboard_ui_handler.cc
index 0c41267..398d2f4 100644
--- a/ui/keyboard/keyboard_ui_handler.cc
+++ b/ui/keyboard/keyboard_ui_handler.cc
@@ -12,7 +12,11 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_view.h"
 #include "content/public/browser/web_ui.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/root_window.h"
 #include "ui/aura/window.h"
+#include "ui/base/ime/input_method.h"
+#include "ui/base/ime/text_input_client.h"
 #include "ui/keyboard/keyboard_util.h"
 
 namespace keyboard {
@@ -28,6 +32,10 @@
       "insertText",
       base::Bind(&KeyboardUIHandler::HandleInsertTextMessage,
                  base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getInputContext",
+      base::Bind(&KeyboardUIHandler::HandleGetInputContextMessage,
+                 base::Unretained(this)));
 }
 
 void KeyboardUIHandler::HandleInsertTextMessage(const base::ListValue* args) {
@@ -48,4 +56,35 @@
     LOG(ERROR) << "insertText failed";
 }
 
+void KeyboardUIHandler::HandleGetInputContextMessage(
+    const base::ListValue* args) {
+  int request_id;
+  if (!args->GetInteger(0, &request_id)) {
+    LOG(ERROR) << "getInputContext failed: bad argument";
+    return;
+  }
+  base::DictionaryValue results;
+  results.SetInteger("requestId", request_id);
+
+  aura::RootWindow* root_window =
+      web_ui()->GetWebContents()->GetView()->GetNativeView()->GetRootWindow();
+  if (!root_window) {
+    LOG(ERROR) << "getInputContext failed: no root window";
+    return;
+  }
+  ui::InputMethod* input_method =
+      root_window->GetProperty(aura::client::kRootWindowInputMethodKey);
+  if (!input_method) {
+    LOG(ERROR) << "getInputContext failed: no input method";
+    return;
+  }
+
+  ui::TextInputClient* tic = input_method->GetTextInputClient();
+  results.SetInteger("type",
+                     tic ? tic->GetTextInputType() : ui::TEXT_INPUT_TYPE_NONE);
+
+  web_ui()->CallJavascriptFunction("GetInputContextCallback",
+                                   results);
+}
+
 }  // namespace keyboard
diff --git a/ui/keyboard/keyboard_ui_handler.h b/ui/keyboard/keyboard_ui_handler.h
index 0800453..5ff05a0 100644
--- a/ui/keyboard/keyboard_ui_handler.h
+++ b/ui/keyboard/keyboard_ui_handler.h
@@ -25,6 +25,10 @@
   // Callback for the "insertText" message.
   void HandleInsertTextMessage(const base::ListValue* args);
 
+  // Callback for the "getInputContext" message. The first element in
+  // |args| should be an integer representing request ID.
+  void HandleGetInputContextMessage(const base::ListValue* args);
+
   DISALLOW_COPY_AND_ASSIGN(KeyboardUIHandler);
 };
 
diff --git a/ui/keyboard/resources/elements/kb-keyboard.html b/ui/keyboard/resources/elements/kb-keyboard.html
index e6b8c5d..58bc2af 100644
--- a/ui/keyboard/resources/elements/kb-keyboard.html
+++ b/ui/keyboard/resources/elements/kb-keyboard.html
@@ -1,5 +1,5 @@
 <!--
-  -- Copyright (c) 2013 The Chromium Authors. All rights reserved.
+  -- 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.
   -->
@@ -179,6 +179,19 @@
       dblTimer_: null,
       swipeHandler: null,
 
+      /**
+      * Handles the state of the shift key.
+      */
+      keysetChanged: function() {
+        var keysetId = '#' + this.layout + '-' + this.keyset;
+        var keyset = this.querySelector(keysetId);
+
+        // Unlocks the keyboard if the current keyset is not lockable.
+        if (!keyset.getAttribute('lockable'))
+          this.classList.remove('locked');
+        
+      },
+
       ready: function() {
         this.voiceInput_ = new VoiceInput(this);
         this.swipeHandler = this.onSwipeUpdate.bind(this);
@@ -330,9 +343,15 @@
           this.dblDetail_.clickCount++;
           if (this.dblDetail_.clickCount == 2) {
             this.keyset = this.dblDetail_.toKeyset;
-            var keysetId = '#' + this.layout + '-' + this.keyset
-            this.querySelector(keysetId).nextKeyset = this.dblTimer_.nextKeyset;
+            var keysetId = '#' + this.layout + '-' + this.keyset;
+            var keyset = this.querySelector(keysetId);
+            keyset.nextKeyset = this.dblTimer_.nextKeyset;
             clearTimeout(this.dblTimer_);
+            
+            // Checks if shift is capitalized.
+            if (keyset.getAttribute('lockable'))
+              this.classList.add('locked');
+
             this.dblDetail_ = null;
           }
         }
@@ -417,6 +436,9 @@
           this.keyset = toKeyset;
           this.querySelector('#' + this.layout + '-' + this.keyset).nextKeyset =
               detail.nextKeyset;
+
+          // Locks the keyset before removing active to prevent flicker.
+          this.classList.add('locked');
           // Makes last pressed key inactive if transit to a new keyset on long
           // press.
           this.lastPressedKey.classList.remove('active');
@@ -424,7 +446,7 @@
       },
 
       /**
-       * Handles a change in the keyboard layout.  Auto-selects the default
+       * Handles a change in the keyboard layout. Auto-selects the default
        * keyset for the new layout.
        */
       layoutChanged: function() {
@@ -442,7 +464,7 @@
 
       /**
        * Selects the default keyset for a layout.
-       * @return {boolean} True if successful.  This method can fail if the
+       * @return {boolean} True if successful. This method can fail if the
        *     keysets corresponding to the layout have not been injected.
        */
       selectDefaultKeyset: function() {
diff --git a/ui/keyboard/resources/layouts/dvorak.html b/ui/keyboard/resources/layouts/dvorak.html
index 2f90470..3c41ba3 100644
--- a/ui/keyboard/resources/layouts/dvorak.html
+++ b/ui/keyboard/resources/layouts/dvorak.html
@@ -9,7 +9,7 @@
 <link id="spacebar-row" rel="import" href="spacebar-row.html">
 
 <template>
-  <kb-keyset id="dvorak-upper">
+  <kb-keyset id="dvorak-upper" lockable="true">
     <kb-row>
       <kb-key class="tab dark" char="&#x0009;" align="left">tab</kb-key>
       <kb-key-sequence keys="&quot;,.PYFGCRL" superscripts="1234567890"></kb-key-sequence>
diff --git a/ui/keyboard/resources/layouts/qwerty.html b/ui/keyboard/resources/layouts/qwerty.html
index 0f19ebf..0ead791 100644
--- a/ui/keyboard/resources/layouts/qwerty.html
+++ b/ui/keyboard/resources/layouts/qwerty.html
@@ -9,7 +9,7 @@
 <link id="spacebar-row" rel="import" href="spacebar-row.html">
 
 <template>
-  <kb-keyset id="qwerty-upper">
+  <kb-keyset id="qwerty-upper" lockable="true">
     <kb-row>
       <kb-key class="tab dark" char="&#x0009;" align="left">tab</kb-key>
       <kb-key-sequence keys="QWERTYUIOP" superscripts="1234567890"></kb-key-sequence>
diff --git a/ui/keyboard/resources/layouts/webui_qwerty.html b/ui/keyboard/resources/layouts/webui_qwerty.html
index 9abd2e6..160c6f5 100644
--- a/ui/keyboard/resources/layouts/webui_qwerty.html
+++ b/ui/keyboard/resources/layouts/webui_qwerty.html
@@ -5,7 +5,7 @@
   -->
 
 <template>
-  <kb-keyset id="qwerty-upper">
+  <kb-keyset id="qwerty-upper" lockable="true">
     <kb-row class="top">
       <kb-key>Q</kb-key><kb-key>W</kb-key><kb-key>E</kb-key><kb-key>R</kb-key>
       <kb-key>T</kb-key><kb-key>Y</kb-key><kb-key>U</kb-key>
diff --git a/ui/keyboard/resources/main.css b/ui/keyboard/resources/main.css
index 149384f..afebccc 100644
--- a/ui/keyboard/resources/main.css
+++ b/ui/keyboard/resources/main.css
@@ -68,6 +68,14 @@
   color: #ffffff;
 }
 
+/**
+* Controls whether the shift key should be highlighted or not.
+* Only highlights if we are in a lockable keyboard which is not locked.
+*/
+kb-keyboard:not(.locked) kb-keyset[lockable] kb-shift-key {
+  color: lightblue;
+}
+
 kb-shift-key.dark,
 kb-layout-selector.dark,
 kb-key.dark {
@@ -87,6 +95,7 @@
   text-align: center;
 }
 
+.locked kb-shift-key,
 .active {
   background-color: #848490 !important;
   border-top: 2px solid #A9A9AF !important;
@@ -158,4 +167,4 @@
 
 .layout-selector {
   background-image: url('images/keyboard.svg');
-}
+}
\ No newline at end of file
diff --git a/ui/keyboard/resources/webui/api_adapter.js b/ui/keyboard/resources/webui/api_adapter.js
index 1d24ba6..3072775 100644
--- a/ui/keyboard/resources/webui/api_adapter.js
+++ b/ui/keyboard/resources/webui/api_adapter.js
@@ -5,3 +5,59 @@
 function insertText(text) {
   chrome.send('insertText', [ text ]);
 }
+
+(function(exports) {
+  /**
+   * An array to save callbacks of each request.
+   * @type {Array.<function(Object)>}
+   */
+  var requestIdCallbackMap = [];
+
+  /**
+   * An incremental integer that represents a unique requestId.
+   * @type {number}
+   */
+  var requestId = 0;
+
+  /**
+   * Gets the context of the focused input field. The context is returned as a
+   * paramter in the |callback|.
+   * @param {function(Object)} callback The callback function after the webui
+   *     function finished.
+   * @return {number} The ID of the new request.
+   */
+  function GetInputContext(callback) {
+    var id = requestId;
+    requestIdCallbackMap[id] = callback;
+    chrome.send('getInputContext', [ id ]);
+    requestId++;
+    return id;
+  }
+
+  /**
+   * Cancel the callback specified by requestId.
+   * @param {number} requestId The requestId of the callback that about to
+   *     cancel.
+   */
+  function CancelRequest(requestId) {
+    requestIdCallbackMap[requestId] = undefined;
+  }
+
+  /**
+   * Webui function callback. Any call to chrome.send('getInputContext', [id])
+   * should trigger this function being called with the parameter
+   * inputContext.requestId == id.
+   * @param {Object} inputContext The context of focused input field. Note we
+   *     only have type(input box type) and requestId fields now.
+   */
+  function GetInputContextCallback(inputContext) {
+    var requestId = inputContext.requestId;
+    if (!requestIdCallbackMap[requestId])
+      return;
+    requestIdCallbackMap[requestId](inputContext);
+  }
+
+  exports.getInputContext = GetInputContext;
+  exports.cancelRequest = CancelRequest;
+  exports.GetInputContextCallback = GetInputContextCallback;
+})(this);
diff --git a/ui/linux_ui/status_icon_linux.cc b/ui/linux_ui/status_icon_linux.cc
index 1e273d3..0e28771 100644
--- a/ui/linux_ui/status_icon_linux.cc
+++ b/ui/linux_ui/status_icon_linux.cc
@@ -12,3 +12,6 @@
 
 StatusIconLinux::~StatusIconLinux() {
 }
+
+void StatusIconLinux::RefreshPlatformContextMenu() {
+}
diff --git a/ui/linux_ui/status_icon_linux.h b/ui/linux_ui/status_icon_linux.h
index fbbf28f..2e3d3a9 100644
--- a/ui/linux_ui/status_icon_linux.h
+++ b/ui/linux_ui/status_icon_linux.h
@@ -19,7 +19,7 @@
 // Since liblinux_ui cannot have dependencies on any chrome browser components
 // we cannot inherit from StatusIcon. So we implement the necessary methods
 // and let a wrapper class implement the StatusIcon interface and defer the
-// callbacks to a delegate.
+// callbacks to a delegate. For the same reason, do not use StatusIconMenuModel.
 class LINUX_UI_EXPORT StatusIconLinux {
  public:
   class Delegate {
@@ -43,6 +43,11 @@
   // subclass should destroy the existing native context menu on this call.
   virtual void UpdatePlatformContextMenu(ui::MenuModel* model) = 0;
 
+  // Update all the enabled/checked states and the dynamic labels. Some status
+  // icon implementations do not refresh the native menu before showing so we
+  // need to manually refresh it when the menu model changes.
+  virtual void RefreshPlatformContextMenu();
+
   Delegate* delegate() { return delegate_; }
   void set_delegate(Delegate* delegate) { delegate_ = delegate; }
 
diff --git a/ui/message_center/cocoa/notification_controller_unittest.mm b/ui/message_center/cocoa/notification_controller_unittest.mm
index ea7968e..87aced2 100644
--- a/ui/message_center/cocoa/notification_controller_unittest.mm
+++ b/ui/message_center/cocoa/notification_controller_unittest.mm
@@ -105,7 +105,7 @@
           ASCIIToUTF16("Jonathan and 5 others"),
           gfx::Image(),
           string16(),
-          std::string(),
+          message_center::NotifierId(),
           message_center::RichNotificationData(),
           NULL));
   notification->set_icon(gfx::Image([TestIcon() retain]));
@@ -135,7 +135,7 @@
                        "entire thing?"),
           gfx::Image(),
           string16(),
-          std::string(),
+          message_center::NotifierId(),
           message_center::RichNotificationData(),
           NULL));
   base::scoped_nsobject<MCNotificationController> controller(
@@ -156,7 +156,7 @@
           string16(),
           gfx::Image(),
           string16(),
-          std::string(),
+          message_center::NotifierId(),
           message_center::RichNotificationData(),
           NULL));
   MockMessageCenter message_center;
@@ -183,7 +183,7 @@
                        "default bounds."),
           gfx::Image(),
           string16(),
-          std::string(),
+          message_center::NotifierId(),
           message_center::RichNotificationData(),
           NULL));
   base::scoped_nsobject<MCNotificationController> controller(
@@ -219,7 +219,7 @@
           string16(),
           gfx::Image(),
           string16(),
-          std::string(),
+          message_center::NotifierId(),
           optional,
           NULL));
   MockMessageCenter message_center;
@@ -244,7 +244,7 @@
           string16(),
           gfx::Image(),
           string16(),
-          std::string(),
+          message_center::NotifierId(),
           message_center::RichNotificationData(),
           NULL));
   NSImage* image = [NSImage imageNamed:NSImageNameFolder];
@@ -281,7 +281,7 @@
           UTF8ToUTF16("Notification Message - should be hidden"),
           gfx::Image(),
           string16(),
-          std::string(),
+          message_center::NotifierId(),
           optional,
           NULL));
 
diff --git a/ui/message_center/cocoa/popup_collection_unittest.mm b/ui/message_center/cocoa/popup_collection_unittest.mm
index 105c824..2b8d140 100644
--- a/ui/message_center/cocoa/popup_collection_unittest.mm
+++ b/ui/message_center/cocoa/popup_collection_unittest.mm
@@ -51,7 +51,7 @@
                      " be displayed"),
         gfx::Image(),
         string16(),
-        std::string(),
+        message_center::NotifierId(),
         message_center::RichNotificationData(),
         NULL));
     center_->AddNotification(notification.Pass());
@@ -63,7 +63,7 @@
         ASCIIToUTF16("This is the second notification."),
         gfx::Image(),
         string16(),
-        std::string(),
+        message_center::NotifierId(),
         message_center::RichNotificationData(),
         NULL));
     center_->AddNotification(notification.Pass());
@@ -79,7 +79,7 @@
                      "set the screen size too small."),
         gfx::Image(),
         string16(),
-        std::string(),
+        message_center::NotifierId(),
         message_center::RichNotificationData(),
         NULL));
     center_->AddNotification(notification.Pass());
@@ -134,7 +134,7 @@
       ASCIIToUTF16("This is the fourth notification."),
       gfx::Image(),
       string16(),
-      std::string(),
+      message_center::NotifierId(),
       message_center::RichNotificationData(),
       NULL));
   center_->AddNotification(notification.Pass());
@@ -183,7 +183,7 @@
       ASCIIToUTF16("This is the fourth notification."),
       gfx::Image(),
       string16(),
-      std::string(),
+      message_center::NotifierId(),
       optional,
       NULL));
   center_->AddNotification(notification.Pass());
@@ -221,7 +221,7 @@
               " be displayed"),
       gfx::Image(),
       string16(),
-      std::string(),
+      message_center::NotifierId(),
       message_center::RichNotificationData(),
       NULL));
   center_->AddNotification(notification.Pass());
@@ -243,7 +243,7 @@
               "long notification."),
       gfx::Image(),
       string16(),
-      std::string(),
+      message_center::NotifierId(),
       message_center::RichNotificationData(),
       NULL));
   center_->UpdateNotification("1", notification.Pass());
@@ -287,7 +287,7 @@
               "longer body"),
       gfx::Image(),
       string16(),
-      std::string(),
+      message_center::NotifierId(),
       message_center::RichNotificationData(),
       NULL));
   center_->AddNotification(notification.Pass());
@@ -316,7 +316,7 @@
                    " be displayed"),
       gfx::Image(),
       string16(),
-      std::string(),
+      message_center::NotifierId(),
       message_center::RichNotificationData(),
       NULL));
   center_->AddNotification(notification.Pass());
@@ -349,7 +349,7 @@
       ASCIIToUTF16("New message."),
       gfx::Image(),
       string16(),
-      std::string(),
+      message_center::NotifierId(),
       message_center::RichNotificationData(),
       NULL));
   center_->UpdateNotification("1", notification.Pass());
diff --git a/ui/message_center/cocoa/popup_controller_unittest.mm b/ui/message_center/cocoa/popup_controller_unittest.mm
index 41f6279..c39d163 100644
--- a/ui/message_center/cocoa/popup_controller_unittest.mm
+++ b/ui/message_center/cocoa/popup_controller_unittest.mm
@@ -23,7 +23,7 @@
           ASCIIToUTF16("Jonathan and 5 others"),
           gfx::Image(),
           string16(),
-          std::string(),
+          message_center::NotifierId(),
           message_center::RichNotificationData(),
           NULL));
 
diff --git a/ui/message_center/cocoa/tray_view_controller_unittest.mm b/ui/message_center/cocoa/tray_view_controller_unittest.mm
index 6c82777..ce9b080 100644
--- a/ui/message_center/cocoa/tray_view_controller_unittest.mm
+++ b/ui/message_center/cocoa/tray_view_controller_unittest.mm
@@ -71,7 +71,7 @@
       ASCIIToUTF16("This is a simple test."),
       gfx::Image(),
       string16(),
-      std::string(),
+      message_center::NotifierId(),
       message_center::RichNotificationData(),
       NULL));
   center_->AddNotification(notification_data.Pass());
@@ -105,7 +105,7 @@
       ASCIIToUTF16("This is a simple test."),
       gfx::Image(),
       string16(),
-      std::string(),
+      message_center::NotifierId(),
       message_center::RichNotificationData(),
       NULL));
   center_->AddNotification(notification.Pass());
@@ -116,7 +116,7 @@
       ASCIIToUTF16("This is a simple test."),
       gfx::Image(),
       string16(),
-      std::string(),
+      message_center::NotifierId(),
       message_center::RichNotificationData(),
       NULL));
   center_->AddNotification(notification.Pass());
@@ -127,7 +127,7 @@
       ASCIIToUTF16("This is a simple test."),
       gfx::Image(),
       string16(),
-      std::string(),
+      message_center::NotifierId(),
       message_center::RichNotificationData(),
       NULL));
   center_->AddNotification(notification.Pass());
@@ -160,7 +160,7 @@
       ASCIIToUTF16("This is a simple test."),
       gfx::Image(),
       string16(),
-      std::string(),
+      message_center::NotifierId(),
       message_center::RichNotificationData(),
       NULL));
   center_->AddNotification(notification.Pass());
@@ -179,7 +179,7 @@
       ASCIIToUTF16("This is a simple test."),
       gfx::Image(),
       string16(),
-      std::string(),
+      message_center::NotifierId(),
       message_center::RichNotificationData(),
       NULL));
   center_->AddNotification(notification.Pass());
diff --git a/ui/message_center/fake_message_center.cc b/ui/message_center/fake_message_center.cc
index 4deff26..c397475 100644
--- a/ui/message_center/fake_message_center.cc
+++ b/ui/message_center/fake_message_center.cc
@@ -83,10 +83,8 @@
     const gfx::Image& image) {
 }
 
-void FakeMessageCenter::DisableNotificationsByExtension(const std::string& id) {
-}
-
-void FakeMessageCenter::DisableNotificationsByUrl(const std::string& id) {
+void FakeMessageCenter::DisableNotificationsByNotifier(
+    const NotifierId& notifier_id) {
 }
 
 void FakeMessageCenter::ShowNotificationSettings(const std::string& id) {
diff --git a/ui/message_center/fake_message_center.h b/ui/message_center/fake_message_center.h
index ebb9be2..40ad90e 100644
--- a/ui/message_center/fake_message_center.h
+++ b/ui/message_center/fake_message_center.h
@@ -45,8 +45,8 @@
   virtual void SetNotificationButtonIcon(const std::string& notification_id,
                                          int button_index,
                                          const gfx::Image& image) OVERRIDE;
-  virtual void DisableNotificationsByExtension(const std::string& id) OVERRIDE;
-  virtual void DisableNotificationsByUrl(const std::string& id) OVERRIDE;
+  virtual void DisableNotificationsByNotifier(
+      const NotifierId& notifier_id) OVERRIDE;
   virtual void ShowNotificationSettings(const std::string& id) OVERRIDE;
   virtual void ExpandNotification(const std::string& id) OVERRIDE;
   virtual void ClickOnNotification(const std::string& id) OVERRIDE;
diff --git a/ui/message_center/fake_notifier_settings_provider.cc b/ui/message_center/fake_notifier_settings_provider.cc
index d8054ca..f593ed9 100644
--- a/ui/message_center/fake_notifier_settings_provider.cc
+++ b/ui/message_center/fake_notifier_settings_provider.cc
@@ -15,7 +15,9 @@
 FakeNotifierSettingsProvider::NotifierGroupItem::~NotifierGroupItem() {
 }
 
-FakeNotifierSettingsProvider::FakeNotifierSettingsProvider() {
+FakeNotifierSettingsProvider::FakeNotifierSettingsProvider()
+    : closed_called_count_(0),
+      active_item_index_(0) {
 }
 
 FakeNotifierSettingsProvider::FakeNotifierSettingsProvider(
diff --git a/ui/message_center/fake_notifier_settings_provider.h b/ui/message_center/fake_notifier_settings_provider.h
index 85348f7..b71a7e9 100644
--- a/ui/message_center/fake_notifier_settings_provider.h
+++ b/ui/message_center/fake_notifier_settings_provider.h
@@ -14,7 +14,8 @@
 class FakeNotifierSettingsProvider : public NotifierSettingsProvider {
  public:
   FakeNotifierSettingsProvider();
-  FakeNotifierSettingsProvider(const std::vector<Notifier*>& notifiers);
+  explicit FakeNotifierSettingsProvider(
+      const std::vector<Notifier*>& notifiers);
   virtual ~FakeNotifierSettingsProvider();
 
   virtual size_t GetNotifierGroupCount() const OVERRIDE;
diff --git a/ui/message_center/message_center.h b/ui/message_center/message_center.h
index c055664..880540d 100644
--- a/ui/message_center/message_center.h
+++ b/ui/message_center/message_center.h
@@ -49,13 +49,6 @@
    public:
     virtual ~Delegate();
 
-    // Request to disable the extension associated with |notification_id|.
-    virtual void DisableExtension(const std::string& notification_id) = 0;
-
-    // Request to disable notifications from the source of |notification_id|.
-    virtual void DisableNotificationsFromSource(
-        const std::string& notification_id) = 0;
-
     // Request to show the notification settings (|notification_id| is used
     // to identify the requesting browser context).
     virtual void ShowSettings(const std::string& notification_id) = 0;
@@ -115,12 +108,8 @@
   // and settings.
   // Searches through the notifications and disables any that match the
   // extension id given.
-  virtual void DisableNotificationsByExtension(const std::string& id) = 0;
-
-  // Disables all notifications that match the given url by querying the
-  // delegate and also by matching display_source.
-  // TODO(dewittj): Is display_source matching necessary?
-  virtual void DisableNotificationsByUrl(const std::string& url) = 0;
+  virtual void DisableNotificationsByNotifier(
+      const NotifierId& notifier_id) = 0;
 
   // TODO(mukai): settings can be in another class?
   // Shows the settings for a web notification (profile is identified by the
diff --git a/ui/message_center/message_center_impl.cc b/ui/message_center/message_center_impl.cc
index c1d0179..3188e80 100644
--- a/ui/message_center/message_center_impl.cc
+++ b/ui/message_center/message_center_impl.cc
@@ -335,6 +335,9 @@
   for (NotificationList::Notifications::const_iterator iter =
            notifications.begin(); iter != notifications.end(); ++iter) {
     ids.insert((*iter)->id());
+    NotificationDelegate* delegate = (*iter)->delegate();
+    if (delegate)
+      delegate->Close(by_user);
   }
   notification_list_->RemoveAllNotifications();
 
@@ -374,27 +377,16 @@
   }
 }
 
-void MessageCenterImpl::DisableNotificationsByExtension(
-    const std::string& id) {
-  if (delegate_)
-    delegate_->DisableExtension(id);
-
-  NotificationList::Notifications notifications =
-      notification_list_->GetNotificationsByExtension(id);
-  for (NotificationList::Notifications::const_iterator iter =
-           notifications.begin(); iter != notifications.end();) {
-    std::string id = (*iter)->id();
-    iter++;
-    RemoveNotification(id, false);
+void MessageCenterImpl::DisableNotificationsByNotifier(
+    const NotifierId& notifier_id) {
+  if (settings_provider_) {
+    // TODO(mukai): SetNotifierEnabled can just accept notifier_id?
+    Notifier notifier(notifier_id, base::string16(), true);
+    settings_provider_->SetNotifierEnabled(notifier, false);
   }
-}
-
-void MessageCenterImpl::DisableNotificationsByUrl(const std::string& id) {
-  if (delegate_)
-    delegate_->DisableNotificationsFromSource(id);
 
   NotificationList::Notifications notifications =
-      notification_list_->GetNotificationsBySource(id);
+      notification_list_->GetNotificationsByNotifierId(notifier_id);
   for (NotificationList::Notifications::const_iterator iter =
            notifications.begin(); iter != notifications.end();) {
     std::string id = (*iter)->id();
@@ -477,12 +469,33 @@
 }
 
 void MessageCenterImpl::SetQuietMode(bool in_quiet_mode) {
-  notification_list_->SetQuietMode(in_quiet_mode);
+  if (in_quiet_mode != notification_list_->quiet_mode()) {
+    notification_list_->SetQuietMode(in_quiet_mode);
+    FOR_EACH_OBSERVER(MessageCenterObserver,
+                      observer_list_,
+                      OnQuietModeChanged(in_quiet_mode));
+  }
+  quiet_mode_timer_.reset();
 }
 
 void MessageCenterImpl::EnterQuietModeWithExpire(
     const base::TimeDelta& expires_in) {
-  notification_list_->EnterQuietModeWithExpire(expires_in);
+  if (quiet_mode_timer_.get()) {
+    // Note that the capital Reset() is the method to restart the timer, not
+    // scoped_ptr::reset().
+    quiet_mode_timer_->Reset();
+  } else {
+    notification_list_->SetQuietMode(true);
+    FOR_EACH_OBSERVER(
+        MessageCenterObserver, observer_list_, OnQuietModeChanged(true));
+
+    quiet_mode_timer_.reset(new base::OneShotTimer<MessageCenterImpl>);
+    quiet_mode_timer_->Start(
+        FROM_HERE,
+        expires_in,
+        base::Bind(
+            &MessageCenterImpl::SetQuietMode, base::Unretained(this), false));
+  }
 }
 
 void MessageCenterImpl::RestartPopupTimers() {
diff --git a/ui/message_center/message_center_impl.h b/ui/message_center/message_center_impl.h
index 412e0b3..b278cc2 100644
--- a/ui/message_center/message_center_impl.h
+++ b/ui/message_center/message_center_impl.h
@@ -161,8 +161,8 @@
   virtual void SetNotificationButtonIcon(const std::string& notification_id,
                                          int button_index,
                                          const gfx::Image& image) OVERRIDE;
-  virtual void DisableNotificationsByExtension(const std::string& id) OVERRIDE;
-  virtual void DisableNotificationsByUrl(const std::string& id) OVERRIDE;
+  virtual void DisableNotificationsByNotifier(
+      const NotifierId& notifier_id) OVERRIDE;
   virtual void ShowNotificationSettings(const std::string& id) OVERRIDE;
   virtual void ExpandNotification(const std::string& id) OVERRIDE;
   virtual void ClickOnNotification(const std::string& id) OVERRIDE;
@@ -187,6 +187,7 @@
   scoped_ptr<NotificationList> notification_list_;
   ObserverList<MessageCenterObserver> observer_list_;
   scoped_ptr<internal::PopupTimersController> popup_timers_controller_;
+  scoped_ptr<base::OneShotTimer<MessageCenterImpl> > quiet_mode_timer_;
   Delegate* delegate_;
   NotifierSettingsProvider* settings_provider_;
 
diff --git a/ui/message_center/message_center_observer.h b/ui/message_center/message_center_observer.h
index 8a52792..da7da21 100644
--- a/ui/message_center/message_center_observer.h
+++ b/ui/message_center/message_center_observer.h
@@ -45,6 +45,10 @@
   // Called when the notification list is no longer being displayed as a
   // notification center.
   virtual void OnNotificationCenterClosed() {}
+
+  // Called whenever the quiet mode changes as a result of user action or when
+  // quiet mode expires.
+  virtual void OnQuietModeChanged(bool in_quiet_mode) {}
 };
 
 }  // namespace message_center
diff --git a/ui/message_center/message_center_tray.cc b/ui/message_center/message_center_tray.cc
index 7a1fee8..a571a23 100644
--- a/ui/message_center/message_center_tray.cc
+++ b/ui/message_center/message_center_tray.cc
@@ -12,14 +12,6 @@
 #include "ui/message_center/message_center_tray_delegate.h"
 
 namespace message_center {
-namespace {
-
-// Menu commands
-const int kToggleQuietMode = 0;
-const int kEnableQuietModeHour = 1;
-const int kEnableQuietModeDay = 2;
-
-}
 
 MessageCenterTray::MessageCenterTray(
     MessageCenterTrayDelegate* delegate,
@@ -107,18 +99,6 @@
   message_center_visible_ = delegate_->ShowNotifierSettings();
 }
 
-ui::MenuModel* MessageCenterTray::CreateQuietModeMenu() {
-  ui::SimpleMenuModel* menu = new ui::SimpleMenuModel(this);
-
-  menu->AddCheckItem(kToggleQuietMode,
-                     l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_QUIET_MODE));
-  menu->AddItem(kEnableQuietModeHour,
-                l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_QUIET_MODE_1HOUR));
-  menu->AddItem(kEnableQuietModeDay,
-                l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_QUIET_MODE_1DAY));
-  return menu;
-}
-
 void MessageCenterTray::OnNotificationAdded(
     const std::string& notification_id) {
   OnMessageCenterChanged();
@@ -153,6 +133,10 @@
   NotifyMessageCenterTrayChanged();
 }
 
+void MessageCenterTray::OnQuietModeChanged(bool in_quiet_mode) {
+  NotifyMessageCenterTrayChanged();
+}
+
 void MessageCenterTray::OnMessageCenterChanged() {
   if (message_center_visible_) {
     if (message_center_->NotificationCount() == 0)
@@ -167,36 +151,6 @@
   NotifyMessageCenterTrayChanged();
 }
 
-bool MessageCenterTray::IsCommandIdChecked(int command_id) const {
-  if (command_id != kToggleQuietMode)
-    return false;
-  return message_center()->IsQuietMode();
-}
-
-bool MessageCenterTray::IsCommandIdEnabled(int command_id) const {
-  return true;
-}
-
-bool MessageCenterTray::GetAcceleratorForCommandId(
-    int command_id,
-    ui::Accelerator* accelerator) {
-  return false;
-}
-
-void MessageCenterTray::ExecuteCommand(int command_id, int event_flags) {
-  if (command_id == kToggleQuietMode) {
-    bool in_quiet_mode = message_center()->IsQuietMode();
-    message_center()->SetQuietMode(!in_quiet_mode);
-    NotifyMessageCenterTrayChanged();
-    return;
-  }
-  base::TimeDelta expires_in = command_id == kEnableQuietModeDay ?
-      base::TimeDelta::FromDays(1):
-      base::TimeDelta::FromHours(1);
-  message_center()->EnterQuietModeWithExpire(expires_in);
-  NotifyMessageCenterTrayChanged();
-}
-
 void MessageCenterTray::NotifyMessageCenterTrayChanged() {
   delegate_->OnMessageCenterTrayChanged();
 }
diff --git a/ui/message_center/message_center_tray.h b/ui/message_center/message_center_tray.h
index 03b86e9..80a6513 100644
--- a/ui/message_center/message_center_tray.h
+++ b/ui/message_center/message_center_tray.h
@@ -6,7 +6,6 @@
 #define UI_MESSAGE_CENTER_MESSAGE_CENTER_TRAY_H_
 
 #include "base/observer_list.h"
-#include "ui/base/models/simple_menu_model.h"
 #include "ui/message_center/message_center_export.h"
 #include "ui/message_center/message_center_observer.h"
 #include "ui/message_center/message_center_tray_delegate.h"
@@ -25,9 +24,7 @@
 // Class that observes a MessageCenter. Manages the popup and message center
 // bubbles. Tells the MessageCenterTrayHost when the tray is changed, as well
 // as when bubbles are shown and hidden.
-class MESSAGE_CENTER_EXPORT MessageCenterTray
-    : public MessageCenterObserver,
-      public ui::SimpleMenuModel::Delegate {
+class MESSAGE_CENTER_EXPORT MessageCenterTray : public MessageCenterObserver {
  public:
   MessageCenterTray(MessageCenterTrayDelegate* delegate,
                     message_center::MessageCenter* message_center);
@@ -57,10 +54,6 @@
   // Toggles the visibility of the settings view in the message center bubble.
   void ShowNotifierSettingsBubble();
 
-  // Creates the menu model for quiet mode and returns it. The caller must
-  // take the ownership of the return value.
-  ui::MenuModel* CreateQuietModeMenu();
-
   bool message_center_visible() { return message_center_visible_; }
   bool popups_visible() { return popups_visible_; }
   MessageCenterTrayDelegate* delegate() { return delegate_; }
@@ -82,14 +75,7 @@
       int button_index) OVERRIDE;
   virtual void OnNotificationDisplayed(
       const std::string& notification_id) OVERRIDE;
-
-  // Overridden from SimpleMenuModel::Delegate.
-  virtual bool IsCommandIdChecked(int command_id) const OVERRIDE;
-  virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE;
-  virtual bool GetAcceleratorForCommandId(
-      int command_id,
-      ui::Accelerator* accelerator) OVERRIDE;
-  virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE;
+  virtual void OnQuietModeChanged(bool in_quiet_mode) OVERRIDE;
 
  private:
   void OnMessageCenterChanged();
diff --git a/ui/message_center/message_center_tray_unittest.cc b/ui/message_center/message_center_tray_unittest.cc
index 108cee7..7517507 100644
--- a/ui/message_center/message_center_tray_unittest.cc
+++ b/ui/message_center/message_center_tray_unittest.cc
@@ -71,7 +71,7 @@
                          ASCIIToUTF16("Notification message body."),
                          gfx::Image(),
                          ASCIIToUTF16("www.test.org"),
-                         "" /* extension id */,
+                         NotifierId(),
                          message_center::RichNotificationData(),
                          NULL /* delegate */));
     message_center_->AddNotification(notification.Pass());
@@ -171,7 +171,7 @@
                        ASCIIToUTF16("Notification message body."),
                        gfx::Image(),
                        ASCIIToUTF16("www.test.org"),
-                       "" /* extension id */,
+                       NotifierId(),
                        message_center::RichNotificationData(),
                        NULL /* delegate */));
   notification->SetSystemPriority();
diff --git a/ui/message_center/notification.cc b/ui/message_center/notification.cc
index 692d140..6a90e30 100644
--- a/ui/message_center/notification.cc
+++ b/ui/message_center/notification.cc
@@ -28,7 +28,8 @@
     : priority(DEFAULT_PRIORITY),
       never_timeout(false),
       timestamp(base::Time::Now()),
-      progress(0) {}
+      progress(0),
+      should_make_spoken_feedback_for_popup_updates(true) {}
 
 RichNotificationData::RichNotificationData(const RichNotificationData& other)
     : priority(other.priority),
@@ -38,7 +39,9 @@
       image(other.image),
       items(other.items),
       progress(other.progress),
-      buttons(other.buttons) {}
+      buttons(other.buttons),
+      should_make_spoken_feedback_for_popup_updates(
+          other.should_make_spoken_feedback_for_popup_updates) {}
 
 RichNotificationData::~RichNotificationData() {}
 
@@ -48,7 +51,7 @@
                            const string16& message,
                            const gfx::Image& icon,
                            const string16& display_source,
-                           const std::string& extension_id,
+                           const NotifierId& notifier_id,
                            const RichNotificationData& optional_fields,
                            NotificationDelegate* delegate)
     : type_(type),
@@ -57,7 +60,7 @@
       message_(message),
       icon_(icon),
       display_source_(display_source),
-      extension_id_(extension_id),
+      notifier_id_(notifier_id),
       serial_number_(g_next_serial_number_++),
       optional_fields_(optional_fields),
       shown_as_popup_(false),
@@ -72,7 +75,7 @@
       message_(other.message_),
       icon_(other.icon_),
       display_source_(other.display_source_),
-      extension_id_(other.extension_id_),
+      notifier_id_(other.notifier_id_),
       serial_number_(other.serial_number_),
       optional_fields_(other.optional_fields_),
       shown_as_popup_(other.shown_as_popup_),
@@ -87,7 +90,7 @@
   message_ = other.message_;
   icon_ = other.icon_;
   display_source_ = other.display_source_;
-  extension_id_ = other.extension_id_;
+  notifier_id_ = other.notifier_id_;
   serial_number_ = other.serial_number_;
   optional_fields_ = other.optional_fields_;
   shown_as_popup_ = other.shown_as_popup_;
@@ -126,6 +129,7 @@
     const base::string16& title,
     const base::string16& message,
     const gfx::Image& icon,
+    int system_component_id,
     const base::Closure& click_callback) {
   scoped_ptr<Notification> notification(
       new Notification(
@@ -135,7 +139,7 @@
           message,
           icon,
           base::string16()  /* display_source */,
-          std::string()  /* extension_id */,
+          NotifierId(system_component_id),
           RichNotificationData(),
           new HandleNotificationClickedDelegate(click_callback)));
   notification->SetSystemPriority();
diff --git a/ui/message_center/notification.h b/ui/message_center/notification.h
index cfcef43..915f812 100644
--- a/ui/message_center/notification.h
+++ b/ui/message_center/notification.h
@@ -15,6 +15,7 @@
 #include "ui/message_center/message_center_export.h"
 #include "ui/message_center/notification_delegate.h"
 #include "ui/message_center/notification_types.h"
+#include "ui/message_center/notifier_settings.h"
 
 namespace message_center {
 
@@ -46,6 +47,7 @@
   std::vector<NotificationItem> items;
   int progress;
   std::vector<ButtonInfo> buttons;
+  bool should_make_spoken_feedback_for_popup_updates;
 };
 
 class MESSAGE_CENTER_EXPORT Notification {
@@ -56,7 +58,7 @@
                const string16& message,
                const gfx::Image& icon,
                const string16& display_source,
-               const std::string& extension_id,
+               const NotifierId& notifier_id,
                const RichNotificationData& optional_fields,
                NotificationDelegate* delegate);
 
@@ -81,10 +83,8 @@
 
   // A display string for the source of the notification.
   const string16& display_source() const { return display_source_; }
-  const std::string& extension_id() const { return extension_id_; }
-  void set_extension_id(const std::string& extension_id) {
-    extension_id_ = extension_id;
-  }
+
+  const NotifierId& notifier_id() const { return notifier_id_; }
 
   // Begin unpacked values from optional_fields.
   int priority() const { return optional_fields_.priority; }
@@ -174,6 +174,7 @@
       const base::string16& title,
       const base::string16& message,
       const gfx::Image& icon,
+      int system_component_id,
       const base::Closure& click_callback);
 
  protected:
@@ -192,7 +193,7 @@
   string16 display_source_;
 
  private:
-  std::string extension_id_;
+  NotifierId notifier_id_;
   unsigned serial_number_;
   RichNotificationData optional_fields_;
   bool shown_as_popup_;  // True if this has been shown as a popup.
diff --git a/ui/message_center/notification_list.cc b/ui/message_center/notification_list.cc
index d052792..4477d9c 100644
--- a/ui/message_center/notification_list.cc
+++ b/ui/message_center/notification_list.cc
@@ -114,33 +114,12 @@
   unread_count_ = 0;
 }
 
-NotificationList::Notifications NotificationList::GetNotificationsBySource(
-    const std::string& id) {
+NotificationList::Notifications NotificationList::GetNotificationsByNotifierId(
+        const NotifierId& notifier_id) {
   Notifications notifications;
-  Notifications::iterator source_iter = GetNotification(id);
-  if (source_iter == notifications_.end())
-    return notifications;
-
-  string16 display_source = (*source_iter)->display_source();
   for (Notifications::iterator iter = notifications_.begin();
        iter != notifications_.end(); ++iter) {
-    if ((*iter)->display_source() == display_source)
-      notifications.insert(*iter);
-  }
-  return notifications;
-}
-
-NotificationList::Notifications NotificationList::GetNotificationsByExtension(
-        const std::string& id) {
-  Notifications notifications;
-  Notifications::iterator source_iter = GetNotification(id);
-  if (source_iter == notifications_.end())
-    return notifications;
-
-  std::string extension_id = (*source_iter)->extension_id();
-  for (Notifications::iterator iter = notifications_.begin();
-       iter != notifications_.end(); ++iter) {
-    if ((*iter)->extension_id() == extension_id)
+    if ((*iter)->notifier_id() == notifier_id)
       notifications.insert(*iter);
   }
   return notifications;
@@ -285,21 +264,13 @@
 }
 
 void NotificationList::SetQuietMode(bool quiet_mode) {
-  SetQuietModeInternal(quiet_mode);
-  quiet_mode_timer_.reset();
-}
-
-void NotificationList::EnterQuietModeWithExpire(
-    const base::TimeDelta& expires_in) {
-  if (quiet_mode_timer_.get()) {
-    // Note that the capital Reset() is the method to restart the timer, not
-    // scoped_ptr::reset().
-    quiet_mode_timer_->Reset();
-  } else {
-    SetQuietModeInternal(true);
-    quiet_mode_timer_.reset(new base::OneShotTimer<NotificationList>);
-    quiet_mode_timer_->Start(FROM_HERE, expires_in, base::Bind(
-        &NotificationList::SetQuietMode, base::Unretained(this), false));
+  quiet_mode_ = quiet_mode;
+  if (quiet_mode_) {
+    for (Notifications::iterator iter = notifications_.begin();
+         iter != notifications_.end();
+         ++iter) {
+      (*iter)->set_shown_as_popup(true);
+    }
   }
 }
 
@@ -311,19 +282,8 @@
   return notifications_.size();
 }
 
-void NotificationList::SetQuietModeInternal(bool quiet_mode) {
-  quiet_mode_ = quiet_mode;
-  if (quiet_mode_) {
-    for (Notifications::iterator iter = notifications_.begin();
-         iter != notifications_.end();
-         ++iter) {
-      (*iter)->set_shown_as_popup(true);
-    }
-  }
-}
-
-NotificationList::Notifications::iterator
-    NotificationList::GetNotification(const std::string& id) {
+NotificationList::Notifications::iterator NotificationList::GetNotification(
+    const std::string& id) {
   for (Notifications::iterator iter = notifications_.begin();
        iter != notifications_.end(); ++iter) {
     if ((*iter)->id() == id)
diff --git a/ui/message_center/notification_list.h b/ui/message_center/notification_list.h
index ce24498..ab8b4a0 100644
--- a/ui/message_center/notification_list.h
+++ b/ui/message_center/notification_list.h
@@ -67,8 +67,7 @@
 
   void RemoveAllNotifications();
 
-  Notifications GetNotificationsBySource(const std::string& id);
-  Notifications GetNotificationsByExtension(const std::string& id);
+  Notifications GetNotificationsByNotifierId(const NotifierId& notifier_id);
 
   // Returns true if the notification exists and was updated.
   bool SetNotificationIcon(const std::string& notification_id,
@@ -114,8 +113,7 @@
 
   bool quiet_mode() const { return quiet_mode_; }
 
-  // Sets the current quiet mode status to |quiet_mode|. The new status is not
-  // expired.
+  // Sets the current quiet mode status to |quiet_mode|.
   void SetQuietMode(bool quiet_mode);
 
   // Sets the current quiet mode to true. The quiet mode will expire in the
@@ -139,14 +137,10 @@
 
   void PushNotification(scoped_ptr<Notification> notification);
 
-  // Sets the current quiet mode status to |quiet_mode|.
-  void SetQuietModeInternal(bool quiet_mode);
-
   Notifications notifications_;
   bool message_center_visible_;
   size_t unread_count_;
   bool quiet_mode_;
-  scoped_ptr<base::OneShotTimer<NotificationList> > quiet_mode_timer_;
 
   DISALLOW_COPY_AND_ASSIGN(NotificationList);
 };
diff --git a/ui/message_center/notification_list_unittest.cc b/ui/message_center/notification_list_unittest.cc
index cdd6ad4..8195d0a 100644
--- a/ui/message_center/notification_list_unittest.cc
+++ b/ui/message_center/notification_list_unittest.cc
@@ -40,7 +40,7 @@
         UTF8ToUTF16(base::StringPrintf(kMessageFormat, counter_)),
         gfx::Image(),
         UTF8ToUTF16(kDisplaySource),
-        kExtensionId,
+        NotifierId(NotifierId::APPLICATION, kExtensionId),
         optional_fields,
         NULL));
     notification_list_->AddNotification(notification.Pass());
@@ -172,7 +172,7 @@
                        UTF8ToUTF16("newbody"),
                        gfx::Image(),
                        UTF8ToUTF16(kDisplaySource),
-                       kExtensionId,
+                       NotifierId(NotifierId::APPLICATION, kExtensionId),
                        message_center::RichNotificationData(),
                        NULL));
   notification_list()->UpdateNotificationMessage(id0, notification.Pass());
@@ -184,7 +184,11 @@
   EXPECT_EQ(UTF8ToUTF16("newbody"), (*notifications.begin())->message());
 }
 
-TEST_F(NotificationListTest, GetNotificationsBySourceOrExtensions) {
+TEST_F(NotificationListTest, GetNotificationsByNotifierId) {
+  NotifierId id0(NotifierId::APPLICATION, "ext0");
+  NotifierId id1(NotifierId::APPLICATION, "ext1");
+  NotifierId id2(GURL("http://example.com"));
+  NotifierId id3(0);
   scoped_ptr<Notification> notification(
       new Notification(message_center::NOTIFICATION_TYPE_SIMPLE,
                        "id0",
@@ -192,7 +196,7 @@
                        UTF8ToUTF16("message0"),
                        gfx::Image(),
                        UTF8ToUTF16("source0"),
-                       "ext0",
+                       id0,
                        message_center::RichNotificationData(),
                        NULL));
   notification_list()->AddNotification(notification.Pass());
@@ -202,7 +206,7 @@
                                       UTF8ToUTF16("message1"),
                                       gfx::Image(),
                                       UTF8ToUTF16("source0"),
-                                      "ext0",
+                                      id0,
                                       message_center::RichNotificationData(),
                                       NULL));
   notification_list()->AddNotification(notification.Pass());
@@ -212,7 +216,7 @@
                                       UTF8ToUTF16("message1"),
                                       gfx::Image(),
                                       UTF8ToUTF16("source1"),
-                                      "ext0",
+                                      id0,
                                       message_center::RichNotificationData(),
                                       NULL));
   notification_list()->AddNotification(notification.Pass());
@@ -222,24 +226,63 @@
                                       UTF8ToUTF16("message1"),
                                       gfx::Image(),
                                       UTF8ToUTF16("source2"),
-                                      "ext1",
+                                      id1,
+                                      message_center::RichNotificationData(),
+                                      NULL));
+  notification_list()->AddNotification(notification.Pass());
+  notification.reset(new Notification(message_center::NOTIFICATION_TYPE_SIMPLE,
+                                      "id4",
+                                      UTF8ToUTF16("title1"),
+                                      UTF8ToUTF16("message1"),
+                                      gfx::Image(),
+                                      UTF8ToUTF16("source2"),
+                                      id2,
+                                      message_center::RichNotificationData(),
+                                      NULL));
+  notification_list()->AddNotification(notification.Pass());
+  notification.reset(new Notification(message_center::NOTIFICATION_TYPE_SIMPLE,
+                                      "id5",
+                                      UTF8ToUTF16("title1"),
+                                      UTF8ToUTF16("message1"),
+                                      gfx::Image(),
+                                      UTF8ToUTF16("source2"),
+                                      id3,
                                       message_center::RichNotificationData(),
                                       NULL));
   notification_list()->AddNotification(notification.Pass());
 
-  NotificationList::Notifications by_source =
-      notification_list()->GetNotificationsBySource("id0");
-  EXPECT_TRUE(IsInNotifications(by_source, "id0"));
-  EXPECT_TRUE(IsInNotifications(by_source, "id1"));
-  EXPECT_FALSE(IsInNotifications(by_source, "id2"));
-  EXPECT_FALSE(IsInNotifications(by_source, "id3"));
+  NotificationList::Notifications by_notifier_id =
+      notification_list()->GetNotificationsByNotifierId(id0);
+  EXPECT_TRUE(IsInNotifications(by_notifier_id, "id0"));
+  EXPECT_TRUE(IsInNotifications(by_notifier_id, "id1"));
+  EXPECT_TRUE(IsInNotifications(by_notifier_id, "id2"));
+  EXPECT_FALSE(IsInNotifications(by_notifier_id, "id3"));
+  EXPECT_FALSE(IsInNotifications(by_notifier_id, "id4"));
+  EXPECT_FALSE(IsInNotifications(by_notifier_id, "id5"));
 
-  NotificationList::Notifications by_extension =
-      notification_list()->GetNotificationsByExtension("id0");
-  EXPECT_TRUE(IsInNotifications(by_extension, "id0"));
-  EXPECT_TRUE(IsInNotifications(by_extension, "id1"));
-  EXPECT_TRUE(IsInNotifications(by_extension, "id2"));
-  EXPECT_FALSE(IsInNotifications(by_extension, "id3"));
+  by_notifier_id = notification_list()->GetNotificationsByNotifierId(id1);
+  EXPECT_FALSE(IsInNotifications(by_notifier_id, "id0"));
+  EXPECT_FALSE(IsInNotifications(by_notifier_id, "id1"));
+  EXPECT_FALSE(IsInNotifications(by_notifier_id, "id2"));
+  EXPECT_TRUE(IsInNotifications(by_notifier_id, "id3"));
+  EXPECT_FALSE(IsInNotifications(by_notifier_id, "id4"));
+  EXPECT_FALSE(IsInNotifications(by_notifier_id, "id5"));
+
+  by_notifier_id = notification_list()->GetNotificationsByNotifierId(id2);
+  EXPECT_FALSE(IsInNotifications(by_notifier_id, "id0"));
+  EXPECT_FALSE(IsInNotifications(by_notifier_id, "id1"));
+  EXPECT_FALSE(IsInNotifications(by_notifier_id, "id2"));
+  EXPECT_FALSE(IsInNotifications(by_notifier_id, "id3"));
+  EXPECT_TRUE(IsInNotifications(by_notifier_id, "id4"));
+  EXPECT_FALSE(IsInNotifications(by_notifier_id, "id5"));
+
+  by_notifier_id = notification_list()->GetNotificationsByNotifierId(id3);
+  EXPECT_FALSE(IsInNotifications(by_notifier_id, "id0"));
+  EXPECT_FALSE(IsInNotifications(by_notifier_id, "id1"));
+  EXPECT_FALSE(IsInNotifications(by_notifier_id, "id2"));
+  EXPECT_FALSE(IsInNotifications(by_notifier_id, "id3"));
+  EXPECT_FALSE(IsInNotifications(by_notifier_id, "id4"));
+  EXPECT_TRUE(IsInNotifications(by_notifier_id, "id5"));
 }
 
 TEST_F(NotificationListTest, OldPopupShouldNotBeHidden) {
@@ -355,7 +398,7 @@
                        UTF8ToUTF16("newbody"),
                        gfx::Image(),
                        UTF8ToUTF16(kDisplaySource),
-                       kExtensionId,
+                       NotifierId(NotifierId::APPLICATION, kExtensionId),
                        optional,
                        NULL));
   notification_list()->UpdateNotificationMessage(id0, notification.Pass());
@@ -386,7 +429,7 @@
                        UTF8ToUTF16("newbody"),
                        gfx::Image(),
                        UTF8ToUTF16(kDisplaySource),
-                       kExtensionId,
+                       NotifierId(NotifierId::APPLICATION, kExtensionId),
                        priority,
                        NULL));
   notification_list()->UpdateNotificationMessage(id0, notification.Pass());
@@ -401,7 +444,8 @@
                                       UTF8ToUTF16("newbody2"),
                                       gfx::Image(),
                                       UTF8ToUTF16(kDisplaySource),
-                                      kExtensionId,
+                                      NotifierId(NotifierId::APPLICATION,
+                                                 kExtensionId),
                                       priority,
                                       NULL));
   notification_list()->UpdateNotificationMessage(id0, notification.Pass());
@@ -415,7 +459,8 @@
                                       UTF8ToUTF16("newbody"),
                                       gfx::Image(),
                                       UTF8ToUTF16(kDisplaySource),
-                                      kExtensionId,
+                                      NotifierId(NotifierId::APPLICATION,
+                                                 kExtensionId),
                                       priority,
                                       NULL));
   notification_list()->UpdateNotificationMessage(id1, notification.Pass());
@@ -431,7 +476,8 @@
                                       UTF8ToUTF16("newbody2"),
                                       gfx::Image(),
                                       UTF8ToUTF16(kDisplaySource),
-                                      kExtensionId,
+                                      NotifierId(NotifierId::APPLICATION,
+                                                 kExtensionId),
                                       priority,
                                       NULL));
   notification_list()->UpdateNotificationMessage(id1, notification.Pass());
@@ -447,7 +493,8 @@
                                       UTF8ToUTF16("newbody3"),
                                       gfx::Image(),
                                       UTF8ToUTF16(kDisplaySource),
-                                      kExtensionId,
+                                      NotifierId(NotifierId::APPLICATION,
+                                                 kExtensionId),
                                       priority,
                                       NULL));
   notification_list()->UpdateNotificationMessage(id1, notification.Pass());
@@ -588,7 +635,7 @@
                        UTF8ToUTF16("newbody"),
                        gfx::Image(),
                        UTF8ToUTF16(kDisplaySource),
-                       kExtensionId,
+                       NotifierId(NotifierId::APPLICATION, kExtensionId),
                        message_center::RichNotificationData(),
                        NULL));
   notification_list()->UpdateNotificationMessage(id1, notification.Pass());
@@ -634,7 +681,7 @@
       UTF8ToUTF16("updated"),
       gfx::Image(),
       base::string16(),
-      std::string(),
+      NotifierId(),
       RichNotificationData(),
       NULL));
   notification_list()->AddNotification(updated_notification.Pass());
diff --git a/ui/message_center/notifier_settings.cc b/ui/message_center/notifier_settings.cc
index 4355b94..861e65b 100644
--- a/ui/message_center/notifier_settings.cc
+++ b/ui/message_center/notifier_settings.cc
@@ -11,15 +11,26 @@
                        const std::string& id)
     : type(type),
       id(id),
-      system_component_type(NONE) {
+      system_component_type(-1) {
   DCHECK(type == APPLICATION || type == SYNCED_NOTIFICATION_SERVICE);
+  DCHECK(!id.empty());
 }
 
 NotifierId::NotifierId(const GURL& url)
-    : type(WEB_PAGE), url(url), system_component_type(NONE) {}
+    : type(WEB_PAGE),
+      url(url),
+      system_component_type(-1) {}
 
-NotifierId::NotifierId(SystemComponentNotifierType system_component_type)
-    : type(SYSTEM_COMPONENT), system_component_type(system_component_type) {}
+NotifierId::NotifierId(int type)
+    : type(SYSTEM_COMPONENT),
+      system_component_type(type) {
+  DCHECK_LE(0, system_component_type);
+}
+
+NotifierId::NotifierId()
+    : type(SYSTEM_COMPONENT),
+      system_component_type(-1) {
+}
 
 bool NotifierId::operator==(const NotifierId& other) const {
   if (type != other.type)
@@ -58,23 +69,4 @@
 
 NotifierGroup::~NotifierGroup() {}
 
-std::string ToString(NotifierId::SystemComponentNotifierType type) {
-  switch (type) {
-    case NotifierId::SCREENSHOT:
-      return "screenshot";
-    default:
-      NOTREACHED();
-      return "";
-  }
-}
-
-NotifierId::SystemComponentNotifierType
-ParseSystemComponentName(const std::string& name) {
-  if (name == "screenshot") {
-    return NotifierId::SCREENSHOT;
-  } else {
-    NOTREACHED();
-    return NotifierId::NONE;
-  }
-}
 }  // namespace message_center
diff --git a/ui/message_center/notifier_settings.h b/ui/message_center/notifier_settings.h
index b8771a3..6188599 100644
--- a/ui/message_center/notifier_settings.h
+++ b/ui/message_center/notifier_settings.h
@@ -33,19 +33,17 @@
     SYNCED_NOTIFICATION_SERVICE,
   };
 
-  enum SystemComponentNotifierType {
-    NONE,
-    SCREENSHOT,
-  };
-
   // Constructor for APPLICATION and SYNCED_NOTIFICATION_SERVICE type.
   NotifierId(NotifierType type, const std::string& id);
 
   // Constructor for WEB_PAGE type.
   explicit NotifierId(const GURL& url);
 
-  // Constructor for SYSTEM_COMPONENT type.
-  explicit NotifierId(SystemComponentNotifierType type);
+  // Constructor for system component types. The type should be positive.
+  explicit NotifierId(int type);
+
+  // The default constructor which doesn't specify the notifier. Used for tests.
+  NotifierId();
 
   bool operator==(const NotifierId& other) const;
 
@@ -58,8 +56,9 @@
   // The URL pattern of the notifer.
   GURL url;
 
-  // The type of system component notifier.
-  SystemComponentNotifierType system_component_type;
+  // The type of system component notifier, usually used in ash. -1 if it's not
+  // the system component. See also: ash/system/system_notifier.h
+  int system_component_type;
 };
 
 // The struct to hold the information of notifiers. The information will be
@@ -108,11 +107,6 @@
   DISALLOW_COPY_AND_ASSIGN(NotifierGroup);
 };
 
-MESSAGE_CENTER_EXPORT std::string ToString(
-    NotifierId::SystemComponentNotifierType type);
-MESSAGE_CENTER_EXPORT NotifierId::SystemComponentNotifierType
-    ParseSystemComponentName(const std::string& name);
-
 // An observer class implemented by the view of the NotifierSettings to get
 // notified when the controller has changed data.
 class MESSAGE_CENTER_EXPORT NotifierSettingsObserver {
diff --git a/ui/message_center/views/message_center_view_unittest.cc b/ui/message_center/views/message_center_view_unittest.cc
index f6e05f4..b9e34a1 100644
--- a/ui/message_center/views/message_center_view_unittest.cc
+++ b/ui/message_center/views/message_center_view_unittest.cc
@@ -133,7 +133,7 @@
                             UTF8ToUTF16("message"),
                             gfx::Image(),
                             UTF8ToUTF16("display source"),
-                            std::string("extension id"),
+                            NotifierId(NotifierId::APPLICATION, "extension_id"),
                             message_center::RichNotificationData(),
                             NULL);
 
diff --git a/ui/message_center/views/message_popup_collection_unittest.cc b/ui/message_center/views/message_popup_collection_unittest.cc
index 942c09d..f431997 100644
--- a/ui/message_center/views/message_popup_collection_unittest.cc
+++ b/ui/message_center/views/message_popup_collection_unittest.cc
@@ -83,7 +83,7 @@
                          UTF8ToUTF16("test message"),
                          gfx::Image(),
                          string16() /* display_source */,
-                         "" /* extension_id */,
+                         NotifierId(),
                          message_center::RichNotificationData(),
                          NULL /* delegate */));
     MessageCenter::Get()->AddNotification(notification.Pass());
diff --git a/ui/message_center/views/message_view.cc b/ui/message_center/views/message_view.cc
index 7eb9838..93a5dd3 100644
--- a/ui/message_center/views/message_view.cc
+++ b/ui/message_center/views/message_view.cc
@@ -35,8 +35,7 @@
 
 // Menu constants
 const int kTogglePermissionCommand = 0;
-const int kToggleExtensionCommand = 1;
-const int kShowSettingsCommand = 2;
+const int kShowSettingsCommand = 1;
 
 // ControlButtons are ImageButtons whose image can be padded within the button.
 // This allows the creation of buttons like the notification close and expand
@@ -170,7 +169,7 @@
             message_center::MessageCenterTray* tray,
             const std::string& notification_id,
             const string16& display_source,
-            const std::string& extension_id);
+            const message_center::NotifierId& notifier_id);
   virtual ~MenuModel();
 
   // Overridden from ui::SimpleMenuModel::Delegate:
@@ -186,6 +185,7 @@
   message_center::MessageCenter* message_center_;  // Weak reference.
   message_center::MessageCenterTray* tray_;  // Weak reference.
   std::string notification_id_;
+  message_center::NotifierId notifier_id_;
 
   DISALLOW_COPY_AND_ASSIGN(MenuModel);
 };
@@ -194,19 +194,16 @@
                      message_center::MessageCenterTray* tray,
                      const std::string& notification_id,
                      const string16& display_source,
-                     const std::string& extension_id)
+                     const message_center::NotifierId& notifier_id)
     : ui::SimpleMenuModel(this),
       message_center_(message_center),
       tray_(tray),
-      notification_id_(notification_id) {
+      notification_id_(notification_id),
+      notifier_id_(notifier_id) {
   // Add 'disable notifications' menu item.
-  if (!extension_id.empty() && !display_source.empty()) {
-    AddItem(kToggleExtensionCommand,
-            l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_EXTENSIONS_DISABLE,
-                                       display_source));
-  } else if (!display_source.empty()) {
+  if (!display_source.empty()) {
     AddItem(kTogglePermissionCommand,
-            l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_SITE_DISABLE,
+            l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_NOTIFIER_DISABLE,
                                        display_source));
   }
   // Add settings menu item.
@@ -236,11 +233,8 @@
 
 void MenuModel::ExecuteCommand(int command_id, int event_flags) {
   switch (command_id) {
-    case kToggleExtensionCommand:
-      message_center_->DisableNotificationsByExtension(notification_id_);
-      break;
     case kTogglePermissionCommand:
-      message_center_->DisableNotificationsByUrl(notification_id_);
+      message_center_->DisableNotificationsByNotifier(notifier_id_);
       break;
     case kShowSettingsCommand:
       // |tray_| may be NULL in tests.
@@ -276,7 +270,7 @@
   MessageCenterTray* tray_;  // Weak reference.
   std::string notification_id_;
   string16 display_source_;
-  std::string extension_id_;
+  NotifierId notifier_id_;
 };
 
 MessageViewContextMenuController::MessageViewContextMenuController(
@@ -287,7 +281,7 @@
       tray_(tray),
       notification_id_(notification.id()),
       display_source_(notification.display_source()),
-      extension_id_(notification.extension_id()) {
+      notifier_id_(notification.notifier_id()) {
 }
 
 MessageViewContextMenuController::~MessageViewContextMenuController() {
@@ -298,7 +292,7 @@
     const gfx::Point& point,
     ui::MenuSourceType source_type) {
   MenuModel menu_model(message_center_, tray_, notification_id_,
-                       display_source_, extension_id_);
+                       display_source_, notifier_id_);
   if (menu_model.GetItemCount() == 0)
     return;
 
diff --git a/ui/message_center/views/toast_contents_view.cc b/ui/message_center/views/toast_contents_view.cc
index 6db7f21..93cd953 100644
--- a/ui/message_center/views/toast_contents_view.cc
+++ b/ui/message_center/views/toast_contents_view.cc
@@ -93,8 +93,20 @@
   // popup toast, and the new contents should be read through a11y feature.
   // The notification type should be ALERT, otherwise the accessibility message
   // won't be read for this view which returns ROLE_WINDOW.
-  if (already_has_contents)
-    NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_ALERT, false);
+  if (already_has_contents) {
+    const NotificationList::Notifications& notifications =
+        message_center_->GetNotifications();
+    for (NotificationList::Notifications::const_iterator iter =
+             notifications.begin(); iter != notifications.end(); ++iter) {
+      if ((*iter)->id() != id_)
+        continue;
+
+      const RichNotificationData& optional = (*iter)->rich_notification_data();
+      if (optional.should_make_spoken_feedback_for_popup_updates)
+        NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_ALERT, false);
+      break;
+    }
+  }
 }
 
 void ToastContentsView::RevealWithAnimation(gfx::Point origin) {
diff --git a/ui/resources/default_100_percent/common/pointers/alias_big.png b/ui/resources/default_100_percent/common/pointers/alias_big.png
new file mode 100644
index 0000000..283bf7f
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/alias_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/pointers/cell_big.png b/ui/resources/default_100_percent/common/pointers/cell_big.png
new file mode 100644
index 0000000..3dec5e5
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/cell_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/pointers/context_menu_big.png b/ui/resources/default_100_percent/common/pointers/context_menu_big.png
new file mode 100644
index 0000000..7c9e250
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/context_menu_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/pointers/copy_big.png b/ui/resources/default_100_percent/common/pointers/copy_big.png
new file mode 100644
index 0000000..3e38e45
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/copy_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/pointers/crosshair_big.png b/ui/resources/default_100_percent/common/pointers/crosshair_big.png
new file mode 100644
index 0000000..ea1f5fc
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/crosshair_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/pointers/fleur_big.png b/ui/resources/default_100_percent/common/pointers/fleur_big.png
new file mode 100644
index 0000000..2e32766
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/fleur_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/pointers/hand2_big.png b/ui/resources/default_100_percent/common/pointers/hand2_big.png
new file mode 100644
index 0000000..63bb959
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/hand2_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/pointers/hand3_big.png b/ui/resources/default_100_percent/common/pointers/hand3_big.png
new file mode 100644
index 0000000..3c54751
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/hand3_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/pointers/help_big.png b/ui/resources/default_100_percent/common/pointers/help_big.png
new file mode 100644
index 0000000..6552f9b
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/help_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/pointers/left_ptr_big.png b/ui/resources/default_100_percent/common/pointers/left_ptr_big.png
new file mode 100644
index 0000000..c5604c7
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/left_ptr_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/pointers/move_big.png b/ui/resources/default_100_percent/common/pointers/move_big.png
new file mode 100644
index 0000000..c29db87
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/move_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/pointers/nodrop_big.png b/ui/resources/default_100_percent/common/pointers/nodrop_big.png
new file mode 100644
index 0000000..da981df
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/nodrop_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/pointers/sb_h_double_arrow_big.png b/ui/resources/default_100_percent/common/pointers/sb_h_double_arrow_big.png
new file mode 100644
index 0000000..49dc3d3
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/sb_h_double_arrow_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/pointers/sb_v_double_arrow_big.png b/ui/resources/default_100_percent/common/pointers/sb_v_double_arrow_big.png
new file mode 100644
index 0000000..7b2135d
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/sb_v_double_arrow_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/pointers/top_left_corner_big.png b/ui/resources/default_100_percent/common/pointers/top_left_corner_big.png
new file mode 100644
index 0000000..eecaa89
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/top_left_corner_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/pointers/top_right_corner_big.png b/ui/resources/default_100_percent/common/pointers/top_right_corner_big.png
new file mode 100644
index 0000000..9d47ecf
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/top_right_corner_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/pointers/xterm_big.png b/ui/resources/default_100_percent/common/pointers/xterm_big.png
new file mode 100644
index 0000000..2fba190
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/xterm_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/pointers/xterm_horiz_big.png b/ui/resources/default_100_percent/common/pointers/xterm_horiz_big.png
new file mode 100644
index 0000000..94f5ddc
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/xterm_horiz_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/pointers/zoom_in_big.png b/ui/resources/default_100_percent/common/pointers/zoom_in_big.png
new file mode 100644
index 0000000..923ad79
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/zoom_in_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/common/pointers/zoom_out_big.png b/ui/resources/default_100_percent/common/pointers/zoom_out_big.png
new file mode 100644
index 0000000..aa47eb9
--- /dev/null
+++ b/ui/resources/default_100_percent/common/pointers/zoom_out_big.png
Binary files differ
diff --git a/ui/resources/default_100_percent/mac/notification_tray_attention.png b/ui/resources/default_100_percent/mac/notification_tray_attention.png
index f6fe829..6e49109 100644
--- a/ui/resources/default_100_percent/mac/notification_tray_attention.png
+++ b/ui/resources/default_100_percent/mac/notification_tray_attention.png
Binary files differ
diff --git a/ui/resources/default_100_percent/mac/notification_tray_attention_pressed.png b/ui/resources/default_100_percent/mac/notification_tray_attention_pressed.png
index e4f8cdb..931a4ab 100644
--- a/ui/resources/default_100_percent/mac/notification_tray_attention_pressed.png
+++ b/ui/resources/default_100_percent/mac/notification_tray_attention_pressed.png
Binary files differ
diff --git a/ui/resources/default_100_percent/mac/notification_tray_do_not_disturb_attention.png b/ui/resources/default_100_percent/mac/notification_tray_do_not_disturb_attention.png
index 042673f..335a302 100644
--- a/ui/resources/default_100_percent/mac/notification_tray_do_not_disturb_attention.png
+++ b/ui/resources/default_100_percent/mac/notification_tray_do_not_disturb_attention.png
Binary files differ
diff --git a/ui/resources/default_100_percent/mac/notification_tray_do_not_disturb_attention_pressed.png b/ui/resources/default_100_percent/mac/notification_tray_do_not_disturb_attention_pressed.png
index 324e373..7ab9ee0 100644
--- a/ui/resources/default_100_percent/mac/notification_tray_do_not_disturb_attention_pressed.png
+++ b/ui/resources/default_100_percent/mac/notification_tray_do_not_disturb_attention_pressed.png
Binary files differ
diff --git a/ui/resources/default_100_percent/mac/notification_tray_do_not_disturb_empty.png b/ui/resources/default_100_percent/mac/notification_tray_do_not_disturb_empty.png
index 57474d5..3c9149e 100644
--- a/ui/resources/default_100_percent/mac/notification_tray_do_not_disturb_empty.png
+++ b/ui/resources/default_100_percent/mac/notification_tray_do_not_disturb_empty.png
Binary files differ
diff --git a/ui/resources/default_100_percent/mac/notification_tray_do_not_disturb_empty_pressed.png b/ui/resources/default_100_percent/mac/notification_tray_do_not_disturb_empty_pressed.png
index bd40cfd..87acf99 100644
--- a/ui/resources/default_100_percent/mac/notification_tray_do_not_disturb_empty_pressed.png
+++ b/ui/resources/default_100_percent/mac/notification_tray_do_not_disturb_empty_pressed.png
Binary files differ
diff --git a/ui/resources/default_100_percent/mac/notification_tray_empty.png b/ui/resources/default_100_percent/mac/notification_tray_empty.png
index 7a92998..db17be9 100644
--- a/ui/resources/default_100_percent/mac/notification_tray_empty.png
+++ b/ui/resources/default_100_percent/mac/notification_tray_empty.png
Binary files differ
diff --git a/ui/resources/default_100_percent/mac/notification_tray_empty_pressed.png b/ui/resources/default_100_percent/mac/notification_tray_empty_pressed.png
index 15d30bf..2f3d8fc 100644
--- a/ui/resources/default_100_percent/mac/notification_tray_empty_pressed.png
+++ b/ui/resources/default_100_percent/mac/notification_tray_empty_pressed.png
Binary files differ
diff --git a/ui/resources/default_200_percent/mac/notification_tray_attention.png b/ui/resources/default_200_percent/mac/notification_tray_attention.png
index c0b374b..6587f34 100644
--- a/ui/resources/default_200_percent/mac/notification_tray_attention.png
+++ b/ui/resources/default_200_percent/mac/notification_tray_attention.png
Binary files differ
diff --git a/ui/resources/default_200_percent/mac/notification_tray_attention_pressed.png b/ui/resources/default_200_percent/mac/notification_tray_attention_pressed.png
index 43c496f..7d2521c 100644
--- a/ui/resources/default_200_percent/mac/notification_tray_attention_pressed.png
+++ b/ui/resources/default_200_percent/mac/notification_tray_attention_pressed.png
Binary files differ
diff --git a/ui/resources/default_200_percent/mac/notification_tray_do_not_disturb_attention.png b/ui/resources/default_200_percent/mac/notification_tray_do_not_disturb_attention.png
index 27dc58a..f0584e4 100644
--- a/ui/resources/default_200_percent/mac/notification_tray_do_not_disturb_attention.png
+++ b/ui/resources/default_200_percent/mac/notification_tray_do_not_disturb_attention.png
Binary files differ
diff --git a/ui/resources/default_200_percent/mac/notification_tray_do_not_disturb_attention_pressed.png b/ui/resources/default_200_percent/mac/notification_tray_do_not_disturb_attention_pressed.png
index e8e1e24..8f92234 100644
--- a/ui/resources/default_200_percent/mac/notification_tray_do_not_disturb_attention_pressed.png
+++ b/ui/resources/default_200_percent/mac/notification_tray_do_not_disturb_attention_pressed.png
Binary files differ
diff --git a/ui/resources/default_200_percent/mac/notification_tray_do_not_disturb_empty.png b/ui/resources/default_200_percent/mac/notification_tray_do_not_disturb_empty.png
index b0b799a..2370e7a 100644
--- a/ui/resources/default_200_percent/mac/notification_tray_do_not_disturb_empty.png
+++ b/ui/resources/default_200_percent/mac/notification_tray_do_not_disturb_empty.png
Binary files differ
diff --git a/ui/resources/default_200_percent/mac/notification_tray_do_not_disturb_empty_pressed.png b/ui/resources/default_200_percent/mac/notification_tray_do_not_disturb_empty_pressed.png
index 74e0958..1c6a7af 100644
--- a/ui/resources/default_200_percent/mac/notification_tray_do_not_disturb_empty_pressed.png
+++ b/ui/resources/default_200_percent/mac/notification_tray_do_not_disturb_empty_pressed.png
Binary files differ
diff --git a/ui/resources/default_200_percent/mac/notification_tray_empty.png b/ui/resources/default_200_percent/mac/notification_tray_empty.png
index 32f817e..e3fc1f3 100644
--- a/ui/resources/default_200_percent/mac/notification_tray_empty.png
+++ b/ui/resources/default_200_percent/mac/notification_tray_empty.png
Binary files differ
diff --git a/ui/resources/default_200_percent/mac/notification_tray_empty_pressed.png b/ui/resources/default_200_percent/mac/notification_tray_empty_pressed.png
index e44934c..21492e8 100644
--- a/ui/resources/default_200_percent/mac/notification_tray_empty_pressed.png
+++ b/ui/resources/default_200_percent/mac/notification_tray_empty_pressed.png
Binary files differ
diff --git a/ui/resources/ui_resources.grd b/ui/resources/ui_resources.grd
index 2cb91be..dd510db 100644
--- a/ui/resources/ui_resources.grd
+++ b/ui/resources/ui_resources.grd
@@ -36,6 +36,37 @@
         <structure type="chrome_scaled_image" name="IDR_APP_TOP_RIGHT" file="app_top_right.png" />
       </if>
       <if expr="is_linux and pp_ifdef('use_aura')">
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_ALIAS" file="common/pointers/alias_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_CELL" file="common/pointers/cell_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_COL_RESIZE" file="common/pointers/sb_h_double_arrow_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_CONTEXT_MENU" file="common/pointers/context_menu_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_COPY" file="common/pointers/copy_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_CROSSHAIR" file="common/pointers/crosshair_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_EAST_RESIZE" file="common/pointers/sb_h_double_arrow_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_EAST_WEST_RESIZE" file="common/pointers/sb_h_double_arrow_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_FLEUR" file="common/pointers/fleur_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_HAND" file="common/pointers/hand2_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_HELP" file="common/pointers/help_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_IBEAM" file="common/pointers/xterm_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_MOVE" file="common/pointers/move_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_NORTH_EAST_RESIZE" file="common/pointers/top_right_corner_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_NORTH_EAST_SOUTH_WEST_RESIZE" file="common/pointers/top_right_corner_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_NORTH_RESIZE" file="common/pointers/sb_v_double_arrow_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_NORTH_SOUTH_RESIZE" file="common/pointers/sb_v_double_arrow_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_NORTH_WEST_RESIZE" file="common/pointers/top_left_corner_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_NORTH_WEST_SOUTH_EAST_RESIZE" file="common/pointers/top_left_corner_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_NO_DROP" file="common/pointers/nodrop_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_PTR" file="common/pointers/left_ptr_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_ROW_RESIZE" file="common/pointers/sb_v_double_arrow_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_SOUTH_EAST_RESIZE" file="common/pointers/top_left_corner_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_SOUTH_RESIZE" file="common/pointers/sb_v_double_arrow_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_SOUTH_WEST_RESIZE" file="common/pointers/top_right_corner_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_WEST_RESIZE" file="common/pointers/sb_h_double_arrow_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_XTERM_HORIZ" file="common/pointers/xterm_horiz_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_ZOOM_IN" file="common/pointers/zoom_in_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_ZOOM_OUT" file="common/pointers/zoom_out_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_GRAB" file="common/pointers/fleur_big.png" />
+        <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_BIG_GRABBING" file="common/pointers/hand3_big.png" />
         <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_ALIAS" file="common/pointers/alias.png" />
         <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_CELL" file="common/pointers/cell.png" />
         <structure type="chrome_scaled_image" name="IDR_AURA_CURSOR_COL_RESIZE" file="common/pointers/sb_h_double_arrow.png" />
diff --git a/ui/shell_dialogs/select_file_dialog_win.cc b/ui/shell_dialogs/select_file_dialog_win.cc
index eff7a49..c028d22 100644
--- a/ui/shell_dialogs/select_file_dialog_win.cc
+++ b/ui/shell_dialogs/select_file_dialog_win.cc
@@ -414,7 +414,7 @@
                                 ui::SelectFilePolicy* policy);
 
   // BaseShellDialog implementation:
-  virtual bool IsRunning(gfx::NativeWindow owning_hwnd) const OVERRIDE;
+  virtual bool IsRunning(gfx::NativeWindow owning_window) const OVERRIDE;
   virtual void ListenerDestroyed() OVERRIDE;
 
  protected:
@@ -598,7 +598,7 @@
       return;
     }
   }
-  HWND owner = owning_window
+  HWND owner = owning_window && owning_window->GetRootWindow()
                ? owning_window->GetRootWindow()->GetAcceleratedWidget() : NULL;
 #else
   HWND owner = owning_window;
@@ -617,11 +617,13 @@
   return has_multiple_file_type_choices_;
 }
 
-bool SelectFileDialogImpl::IsRunning(gfx::NativeWindow owning_hwnd) const {
+bool SelectFileDialogImpl::IsRunning(gfx::NativeWindow owning_window) const {
 #if defined(USE_AURA)
-  HWND owner = owning_hwnd->GetRootWindow()->GetAcceleratedWidget();
+  if (!owning_window->GetRootWindow())
+    return false;
+  HWND owner = owning_window->GetRootWindow()->GetAcceleratedWidget();
 #else
-  HWND owner = owning_hwnd;
+  HWND owner = owning_window;
 #endif
   return listener_ && IsRunningDialogForOwner(owner);
 }
diff --git a/ui/surface/accelerated_surface_win.cc b/ui/surface/accelerated_surface_win.cc
index 74ebdac..2ec6382 100644
--- a/ui/surface/accelerated_surface_win.cc
+++ b/ui/surface/accelerated_surface_win.cc
@@ -8,8 +8,8 @@
 #include <algorithm>
 
 #include "base/bind.h"
-#include "base/bind_helpers.h"
 #include "base/callback.h"
+#include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/debug/trace_event.h"
 #include "base/files/file_path.h"
diff --git a/ui/ui.gyp b/ui/ui.gyp
index 8d5f9bc..8ee0694 100644
--- a/ui/ui.gyp
+++ b/ui/ui.gyp
@@ -79,6 +79,7 @@
         'base/animation/throb_animation.h',
         'base/animation/tween.cc',
         'base/animation/tween.h',
+        'base/base_window.cc',
         'base/base_window.h',
         'base/clipboard/clipboard.cc',
         'base/clipboard/clipboard.h',
diff --git a/ui/ui.target.darwin-arm.mk b/ui/ui.target.darwin-arm.mk
index 238d397..a2587bc 100644
--- a/ui/ui.target.darwin-arm.mk
+++ b/ui/ui.target.darwin-arm.mk
@@ -45,6 +45,7 @@
 	ui/base/animation/slide_animation.cc \
 	ui/base/animation/throb_animation.cc \
 	ui/base/animation/tween.cc \
+	ui/base/base_window.cc \
 	ui/base/clipboard/clipboard.cc \
 	ui/base/clipboard/clipboard_android.cc \
 	ui/base/clipboard/clipboard_constants.cc \
diff --git a/ui/ui.target.darwin-mips.mk b/ui/ui.target.darwin-mips.mk
index ece97a9..ae38b10 100644
--- a/ui/ui.target.darwin-mips.mk
+++ b/ui/ui.target.darwin-mips.mk
@@ -45,6 +45,7 @@
 	ui/base/animation/slide_animation.cc \
 	ui/base/animation/throb_animation.cc \
 	ui/base/animation/tween.cc \
+	ui/base/base_window.cc \
 	ui/base/clipboard/clipboard.cc \
 	ui/base/clipboard/clipboard_android.cc \
 	ui/base/clipboard/clipboard_constants.cc \
diff --git a/ui/ui.target.darwin-x86.mk b/ui/ui.target.darwin-x86.mk
index aa55166..7e63800 100644
--- a/ui/ui.target.darwin-x86.mk
+++ b/ui/ui.target.darwin-x86.mk
@@ -45,6 +45,7 @@
 	ui/base/animation/slide_animation.cc \
 	ui/base/animation/throb_animation.cc \
 	ui/base/animation/tween.cc \
+	ui/base/base_window.cc \
 	ui/base/clipboard/clipboard.cc \
 	ui/base/clipboard/clipboard_android.cc \
 	ui/base/clipboard/clipboard_constants.cc \
diff --git a/ui/ui.target.linux-arm.mk b/ui/ui.target.linux-arm.mk
index 238d397..a2587bc 100644
--- a/ui/ui.target.linux-arm.mk
+++ b/ui/ui.target.linux-arm.mk
@@ -45,6 +45,7 @@
 	ui/base/animation/slide_animation.cc \
 	ui/base/animation/throb_animation.cc \
 	ui/base/animation/tween.cc \
+	ui/base/base_window.cc \
 	ui/base/clipboard/clipboard.cc \
 	ui/base/clipboard/clipboard_android.cc \
 	ui/base/clipboard/clipboard_constants.cc \
diff --git a/ui/ui.target.linux-mips.mk b/ui/ui.target.linux-mips.mk
index ece97a9..ae38b10 100644
--- a/ui/ui.target.linux-mips.mk
+++ b/ui/ui.target.linux-mips.mk
@@ -45,6 +45,7 @@
 	ui/base/animation/slide_animation.cc \
 	ui/base/animation/throb_animation.cc \
 	ui/base/animation/tween.cc \
+	ui/base/base_window.cc \
 	ui/base/clipboard/clipboard.cc \
 	ui/base/clipboard/clipboard_android.cc \
 	ui/base/clipboard/clipboard_constants.cc \
diff --git a/ui/ui.target.linux-x86.mk b/ui/ui.target.linux-x86.mk
index aa55166..7e63800 100644
--- a/ui/ui.target.linux-x86.mk
+++ b/ui/ui.target.linux-x86.mk
@@ -45,6 +45,7 @@
 	ui/base/animation/slide_animation.cc \
 	ui/base/animation/throb_animation.cc \
 	ui/base/animation/tween.cc \
+	ui/base/base_window.cc \
 	ui/base/clipboard/clipboard.cc \
 	ui/base/clipboard/clipboard_android.cc \
 	ui/base/clipboard/clipboard_constants.cc \
diff --git a/ui/views/bubble/bubble_border.cc b/ui/views/bubble/bubble_border.cc
index 3c9a320..6494c7b 100644
--- a/ui/views/bubble/bubble_border.cc
+++ b/ui/views/bubble/bubble_border.cc
@@ -73,25 +73,53 @@
 // The border and arrow stroke size used in image assets, in pixels.
 const int kStroke = 1;
 
-// Macros to define arrays of IDR constants used with CreateImageGridPainter.
-#define IMAGE_BORDER(x) { x ## _TOP_LEFT,    x ## _TOP,    x ## _TOP_RIGHT, \
-                          x ## _LEFT,        0,            x ## _RIGHT, \
-                          x ## _BOTTOM_LEFT, x ## _BOTTOM, x ## _BOTTOM_RIGHT, }
-#define IMAGE_BORDER_ACRONYM(x) { x ## _TL, x ## _T, x ## _TR, \
-                                  x ## _L,  0,       x ## _R, \
-                                  x ## _BL, x ## _B, x ## _BR, }
-#define ARROWS(x) { x ## _LEFT, x ## _TOP, x ## _RIGHT, x ## _BOTTOM, }
-
-// Bubble border and arrow image resource ids.
-const int kShadowImages[] = IMAGE_BORDER_ACRONYM(IDR_BUBBLE_SHADOW);
+// Bubble border and arrow image resource ids. They don't use the IMAGE_GRID
+// macro because there is no center image.
+const int kShadowImages[] =  {
+    IDR_BUBBLE_SHADOW_TL, IDR_BUBBLE_SHADOW_T, IDR_BUBBLE_SHADOW_TR,
+    IDR_BUBBLE_SHADOW_L,  0,                   IDR_BUBBLE_SHADOW_R,
+    IDR_BUBBLE_SHADOW_BL, IDR_BUBBLE_SHADOW_B, IDR_BUBBLE_SHADOW_BR };
 const int kShadowArrows[] = { 0, 0, 0, 0 };
-const int kNoShadowImages[] = IMAGE_BORDER_ACRONYM(IDR_BUBBLE);
-const int kNoShadowArrows[] = { IDR_BUBBLE_L_ARROW, IDR_BUBBLE_T_ARROW,
-                                IDR_BUBBLE_R_ARROW, IDR_BUBBLE_B_ARROW, };
-const int kBigShadowImages[] = IMAGE_BORDER(IDR_WINDOW_BUBBLE_SHADOW_BIG);
-const int kBigShadowArrows[] = ARROWS(IDR_WINDOW_BUBBLE_SHADOW_SPIKE_BIG);
-const int kSmallShadowImages[] = IMAGE_BORDER(IDR_WINDOW_BUBBLE_SHADOW_SMALL);
-const int kSmallShadowArrows[] = ARROWS(IDR_WINDOW_BUBBLE_SHADOW_SPIKE_SMALL);
+
+const int kNoShadowImages[] = {
+    IDR_BUBBLE_TL, IDR_BUBBLE_T, IDR_BUBBLE_TR,
+    IDR_BUBBLE_L,  0,            IDR_BUBBLE_R,
+    IDR_BUBBLE_BL, IDR_BUBBLE_B, IDR_BUBBLE_BR };
+const int kNoShadowArrows[] = {
+    IDR_BUBBLE_L_ARROW, IDR_BUBBLE_T_ARROW,
+    IDR_BUBBLE_R_ARROW, IDR_BUBBLE_B_ARROW, };
+
+const int kBigShadowImages[] = {
+    IDR_WINDOW_BUBBLE_SHADOW_BIG_TOP_LEFT,
+    IDR_WINDOW_BUBBLE_SHADOW_BIG_TOP,
+    IDR_WINDOW_BUBBLE_SHADOW_BIG_TOP_RIGHT,
+    IDR_WINDOW_BUBBLE_SHADOW_BIG_LEFT,
+    0,
+    IDR_WINDOW_BUBBLE_SHADOW_BIG_RIGHT,
+    IDR_WINDOW_BUBBLE_SHADOW_BIG_BOTTOM_LEFT,
+    IDR_WINDOW_BUBBLE_SHADOW_BIG_BOTTOM,
+    IDR_WINDOW_BUBBLE_SHADOW_BIG_BOTTOM_RIGHT };
+const int kBigShadowArrows[] = {
+    IDR_WINDOW_BUBBLE_SHADOW_SPIKE_BIG_LEFT,
+    IDR_WINDOW_BUBBLE_SHADOW_SPIKE_BIG_TOP,
+    IDR_WINDOW_BUBBLE_SHADOW_SPIKE_BIG_RIGHT,
+    IDR_WINDOW_BUBBLE_SHADOW_SPIKE_BIG_BOTTOM };
+
+const int kSmallShadowImages[] = {
+    IDR_WINDOW_BUBBLE_SHADOW_SMALL_TOP_LEFT,
+    IDR_WINDOW_BUBBLE_SHADOW_SMALL_TOP,
+    IDR_WINDOW_BUBBLE_SHADOW_SMALL_TOP_RIGHT,
+    IDR_WINDOW_BUBBLE_SHADOW_SMALL_LEFT,
+    0,
+    IDR_WINDOW_BUBBLE_SHADOW_SMALL_RIGHT,
+    IDR_WINDOW_BUBBLE_SHADOW_SMALL_BOTTOM_LEFT,
+    IDR_WINDOW_BUBBLE_SHADOW_SMALL_BOTTOM,
+    IDR_WINDOW_BUBBLE_SHADOW_SMALL_BOTTOM_RIGHT };
+const int kSmallShadowArrows[] = {
+    IDR_WINDOW_BUBBLE_SHADOW_SPIKE_SMALL_LEFT,
+    IDR_WINDOW_BUBBLE_SHADOW_SPIKE_SMALL_TOP,
+    IDR_WINDOW_BUBBLE_SHADOW_SPIKE_SMALL_RIGHT,
+    IDR_WINDOW_BUBBLE_SHADOW_SPIKE_SMALL_BOTTOM };
 
 using internal::BorderImages;
 
diff --git a/ui/views/controls/button/menu_button.cc b/ui/views/controls/button/menu_button.cc
index 5a20d52..c432676 100644
--- a/ui/views/controls/button/menu_button.cc
+++ b/ui/views/controls/button/menu_button.cc
@@ -18,6 +18,7 @@
 #include "ui/gfx/screen.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/menu_button_listener.h"
+#include "ui/views/mouse_constants.h"
 #include "ui/views/widget/root_view.h"
 #include "ui/views/widget/widget.h"
 
@@ -32,7 +33,6 @@
 
 // static
 const char MenuButton::kViewClassName[] = "MenuButton";
-const int64 MenuButton::kMinimumTimeBetweenButtonClicks = 100;
 const int MenuButton::kMenuMarkerPaddingLeft = 3;
 const int MenuButton::kMenuMarkerPaddingRight = -1;
 
@@ -182,10 +182,8 @@
         HitTestPoint(event.location()) &&
         GetDragOperations(event.location()) == ui::DragDropTypes::DRAG_NONE) {
       TimeDelta delta = Time::Now() - menu_closed_time_;
-      int64 delta_in_milliseconds = delta.InMilliseconds();
-      if (delta_in_milliseconds > kMinimumTimeBetweenButtonClicks) {
+      if (delta.InMilliseconds() > kMinimumMsBetweenButtonClicks)
         return Activate();
-      }
     }
   }
   return true;
diff --git a/ui/views/controls/button/menu_button.h b/ui/views/controls/button/menu_button.h
index 575942b..20cdb83 100644
--- a/ui/views/controls/button/menu_button.h
+++ b/ui/views/controls/button/menu_button.h
@@ -28,10 +28,6 @@
  public:
   static const char kViewClassName[];
 
-  // The amount of time, in milliseconds, we wait before allowing another mouse
-  // pressed event to show the menu.
-  static const int64 kMinimumTimeBetweenButtonClicks;
-
   // How much padding to put on the left and right of the menu marker.
   static const int kMenuMarkerPaddingLeft;
   static const int kMenuMarkerPaddingRight;
diff --git a/ui/views/controls/combobox/native_combobox_views.cc b/ui/views/controls/combobox/native_combobox_views.cc
index 3cb3a5c..9a80ec6 100644
--- a/ui/views/controls/combobox/native_combobox_views.cc
+++ b/ui/views/controls/combobox/native_combobox_views.cc
@@ -19,11 +19,11 @@
 #include "ui/views/background.h"
 #include "ui/views/border.h"
 #include "ui/views/color_constants.h"
-#include "ui/views/controls/button/menu_button.h"
 #include "ui/views/controls/combobox/combobox.h"
 #include "ui/views/controls/focusable_border.h"
 #include "ui/views/controls/menu/menu_runner.h"
 #include "ui/views/controls/menu/submenu_view.h"
+#include "ui/views/mouse_constants.h"
 #include "ui/views/widget/root_view.h"
 #include "ui/views/widget/widget.h"
 
@@ -100,7 +100,7 @@
   combobox_->RequestFocus();
   const base::TimeDelta delta = base::Time::Now() - closed_time_;
   if (mouse_event.IsLeftMouseButton() &&
-      (delta.InMilliseconds() > MenuButton::kMinimumTimeBetweenButtonClicks)) {
+      delta.InMilliseconds() > kMinimumMsBetweenButtonClicks) {
     UpdateFromModel();
     ShowDropDownMenu(ui::MENU_SOURCE_MOUSE);
   }
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index afbd6b0..f261973 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -20,14 +20,20 @@
 #include "ui/base/keycodes/keyboard_codes.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/canvas.h"
+#include "ui/gfx/native_widget_types.h"
 #include "ui/gfx/screen.h"
+#include "ui/gfx/vector2d.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/views/controls/button/menu_button.h"
 #include "ui/views/controls/menu/menu_config.h"
 #include "ui/views/controls/menu/menu_controller_delegate.h"
+#include "ui/views/controls/menu/menu_host_root_view.h"
 #include "ui/views/controls/menu/menu_scroll_view_container.h"
 #include "ui/views/controls/menu/submenu_view.h"
 #include "ui/views/drag_utils.h"
+#include "ui/views/event_utils.h"
+#include "ui/views/focus/view_storage.h"
+#include "ui/views/mouse_constants.h"
 #include "ui/views/view_constants.h"
 #include "ui/views/views_delegate.h"
 #include "ui/views/widget/root_view.h"
@@ -36,7 +42,6 @@
 #if defined(USE_AURA)
 #include "ui/aura/env.h"
 #include "ui/aura/root_window.h"
-#include "ui/aura/window.h"
 #endif
 
 #if defined(OS_WIN)
@@ -68,12 +73,17 @@
 // When showing context menu on mouse down, the user might accidentally select
 // the menu item on the subsequent mouse up. To prevent this, we add the
 // following delay before the user is able to select an item.
-static int context_menu_selection_hold_time_ms = 200;
+static int menu_selection_hold_time_ms = kMinimumMsPressedToActivate;
 
 // The spacing offset for the bubble tip.
 const int kBubbleTipSizeLeftRight = 12;
 const int kBubbleTipSizeTopBottom = 11;
 
+// The maximum distance (in DIPS) that the mouse can be moved before it should
+// trigger a mouse menu item activation (regardless of how long the menu has
+// been showing).
+const float kMaximumLengthMovedToActivate = 4.0f;
+
 // Returns true if the mnemonic of |menu| matches key.
 bool MatchesMnemonic(MenuItemView* menu, char16 key) {
   return menu->GetMnemonic() == key;
@@ -268,7 +278,7 @@
 MenuController::State::State()
     : item(NULL),
       submenu_open(false),
-      anchor(views::MenuItemView::TOPLEFT),
+      anchor(MenuItemView::TOPLEFT),
       context_menu(false) {}
 
 MenuController::State::~State() {}
@@ -295,12 +305,26 @@
   drag_in_progress_ = false;
   closing_event_time_ = base::TimeDelta();
   menu_start_time_ = base::TimeTicks::Now();
+  menu_start_mouse_press_loc_ = gfx::Point();
 
   // If we are shown on mouse press, we will eat the subsequent mouse down and
   // the parent widget will not be able to reset its state (it might have mouse
   // capture from the mouse down). So we clear its state here.
-  if (parent && parent->GetRootView())
-    parent->GetRootView()->SetMouseHandler(NULL);
+  if (parent) {
+    View* root_view = parent->GetRootView();
+    if (root_view) {
+      root_view->SetMouseHandler(NULL);
+      const ui::Event* event =
+          static_cast<internal::RootView*>(root_view)->current_event();
+      if (event && event->type() == ui::ET_MOUSE_PRESSED) {
+        gfx::Point screen_loc(
+            static_cast<const ui::MouseEvent*>(event)->location());
+        View::ConvertPointToScreen(
+            static_cast<View*>(event->target()), &screen_loc);
+        menu_start_mouse_press_loc_ = screen_loc;
+      }
+    }
+  }
 
   bool nested_menu = showing_;
   if (showing_) {
@@ -485,9 +509,9 @@
     // If it is from an empty menu, use parent context menu instead of that.
     if (menu == NULL &&
         part.submenu->child_count() == 1 &&
-        part.submenu->child_at(0)->id()
-           == views::MenuItemView::kEmptyMenuItemViewID)
+        part.submenu->child_at(0)->id() == MenuItemView::kEmptyMenuItemViewID) {
       menu = part.parent;
+    }
 
     if (menu != NULL && ShowContextMenu(menu, source, event,
                                         ui::MENU_SOURCE_MOUSE))
@@ -500,10 +524,23 @@
   if (!part.is_scroll() && part.menu &&
       !(part.menu->HasSubmenu() &&
         (event.flags() & ui::EF_LEFT_MOUSE_BUTTON))) {
-    if (active_mouse_view_) {
+    if (GetActiveMouseView()) {
       SendMouseReleaseToActiveView(source, event);
       return;
     }
+    // If a mouse release was received quickly after showing.
+    base::TimeDelta time_shown = base::TimeTicks::Now() - menu_start_time_;
+    if (time_shown.InMilliseconds() < menu_selection_hold_time_ms) {
+      // And it wasn't far from the mouse press location.
+      gfx::Point screen_loc(event.location());
+      View::ConvertPointToScreen(source->GetScrollViewContainer(), &screen_loc);
+      gfx::Vector2d moved = screen_loc - menu_start_mouse_press_loc_;
+      if (moved.Length() < kMaximumLengthMovedToActivate) {
+        // Ignore the mouse release as it was likely this menu was shown under
+        // the mouse and the action was just a normal click.
+        return;
+      }
+    }
     if (part.menu->GetDelegate()->ShouldExecuteCommandWithoutClosingMenu(
             part.menu->GetCommand(), event)) {
       part.menu->GetDelegate()->ExecuteCommand(part.menu->GetCommand(),
@@ -512,11 +549,11 @@
     }
     if (!part.menu->NonIconChildViewsCount() &&
         part.menu->GetDelegate()->IsTriggerableEvent(part.menu, event)) {
-      int64 time_since_menu_start =
-          (base::TimeTicks::Now() - menu_start_time_).InMilliseconds();
+      base::TimeDelta shown_time = base::TimeTicks::Now() - menu_start_time_;
       if (!state_.context_menu || !View::ShouldShowContextMenuOnMousePress() ||
-          time_since_menu_start > context_menu_selection_hold_time_ms)
+          shown_time.InMilliseconds() > menu_selection_hold_time_ms) {
         Accept(part.menu, event.flags());
+      }
       return;
     }
   } else if (part.type == MenuPart::MENU_ITEM) {
@@ -740,7 +777,7 @@
     gfx::Point point = GetScreen()->GetCursorScreenPoint();
     const SubmenuView* root_submenu =
         submenu->GetMenuItem()->GetRootMenuItem()->GetSubmenu();
-    views::View::ConvertPointFromScreen(
+    View::ConvertPointFromScreen(
         root_submenu->GetWidget()->GetRootView(), &point);
     HandleMouseLocation(submenu, point);
   }
@@ -753,8 +790,8 @@
 }
 
 // static
-void MenuController::TurnOffContextMenuSelectionHoldForTest() {
-  context_menu_selection_hold_time_ms = -1;
+void MenuController::TurnOffMenuSelectionHoldForTest() {
+  menu_selection_hold_time_ms = -1;
 }
 
 void MenuController::SetSelection(MenuItemView* menu_item,
@@ -828,7 +865,7 @@
   if (!blocking_run_)
     return;
 
-  DCHECK(!active_mouse_view_);
+  DCHECK(!GetActiveMouseView());
 
   MenuPart part = GetMenuPart(source, event.location());
   if (part.is_scroll())
@@ -855,9 +892,15 @@
 
 #if defined(OS_WIN)
     // We're going to close and we own the mouse capture. We need to repost the
-    // mouse down, otherwise the window the user clicked on won't get the
-    // event.
-    RepostEvent(source, event);
+    // mouse down, otherwise the window the user clicked on won't get the event.
+    if (!state_.item) {
+      // We some times get an event after closing all the menus. Ignore it. Make
+      // sure the menu is in fact not visible. If the menu is visible, then
+      // we're in a bad state where we think the menu isn't visibile but it is.
+      DCHECK(!source->GetWidget()->IsVisible());
+    } else {
+      RepostEvent(source, event);
+    }
 #endif
 
     // And close.
@@ -914,7 +957,7 @@
   View::ConvertPointToTarget(NULL, item, &press_loc);
   gfx::Point widget_loc(press_loc);
   View::ConvertPointToWidget(item, &widget_loc);
-  scoped_ptr<gfx::Canvas> canvas(views::GetCanvasForDragImage(
+  scoped_ptr<gfx::Canvas> canvas(GetCanvasForDragImage(
       source->GetWidget(), gfx::Size(item->width(), item->height())));
   item->PaintButton(canvas.get(), MenuItemView::PB_FOR_DRAG);
 
@@ -1119,7 +1162,7 @@
       last_drop_operation_(MenuDelegate::DROP_UNKNOWN),
       showing_submenu_(false),
       menu_button_(NULL),
-      active_mouse_view_(NULL),
+      active_mouse_view_id_(ViewStorage::GetInstance()->CreateStorageID()),
       delegate_(delegate),
       message_loop_depth_(0),
       menu_config_(theme),
@@ -1218,8 +1261,7 @@
     return false;
   }
 
-  gfx::NativeWindow window_under_mouse =
-      GetScreen()->GetWindowAtCursorScreenPoint();
+  gfx::NativeWindow window_under_mouse = GetScreen()->GetWindowUnderCursor();
   // TODO(oshima): Replace with views only API.
   if (!owner_ || window_under_mouse != owner_->GetNativeWindow())
     return false;
@@ -1541,7 +1583,7 @@
 void MenuController::MenuChildrenChanged(MenuItemView* item) {
   DCHECK(item);
   // Menu shouldn't be updated during drag operation.
-  DCHECK(!active_mouse_view_);
+  DCHECK(!GetActiveMouseView());
 
   // If the current item or pending item is a descendant of the item
   // that changed, move the selection back to the changed item.
@@ -2058,94 +2100,52 @@
   return false;
 }
 
-#if defined(OS_WIN)
 void MenuController::RepostEvent(SubmenuView* source,
                                  const ui::LocatedEvent& event) {
-  if (!state_.item) {
-    // We some times get an event after closing all the menus. Ignore it.
-    // Make sure the menu is in fact not visible. If the menu is visible, then
-    // we're in a bad state where we think the menu isn't visibile but it is.
-    DCHECK(!source->GetWidget()->IsVisible());
-    return;
-  }
-
   gfx::Point screen_loc(event.location());
   View::ConvertPointToScreen(source->GetScrollViewContainer(), &screen_loc);
-  HWND window = WindowFromPoint(screen_loc.ToPOINT());
-  if (window) {
-    // Release the capture.
-    SubmenuView* submenu = state_.item->GetRootMenuItem()->GetSubmenu();
-    submenu->ReleaseCapture();
 
-    if (submenu->GetWidget()->GetNativeView() &&
-        GetWindowThreadProcessId(
-            views::HWNDForNativeView(submenu->GetWidget()->GetNativeView()),
-            NULL) != GetWindowThreadProcessId(window, NULL)) {
-      // Even though we have mouse capture, windows generates a mouse event
-      // if the other window is in a separate thread. Don't generate an event in
-      // this case else the target window can get double events leading to bad
-      // behavior.
+  gfx::NativeView native_view = source->GetWidget()->GetNativeView();
+  gfx::Screen* screen = gfx::Screen::GetScreenFor(native_view);
+  gfx::NativeWindow window = screen->GetWindowAtScreenPoint(screen_loc);
+
+  if (!window)
+    return;
+
+#if defined(OS_WIN)
+  // Release the capture.
+  SubmenuView* submenu = state_.item->GetRootMenuItem()->GetSubmenu();
+  submenu->ReleaseCapture();
+
+  gfx::NativeView view = submenu->GetWidget()->GetNativeView();
+  if (view) {
+    DWORD view_tid = GetWindowThreadProcessId(HWNDForNativeView(view), NULL);
+    if (view_tid != GetWindowThreadProcessId(HWNDForNativeView(window), NULL)) {
+      // Even though we have mouse capture, windows generates a mouse event if
+      // the other window is in a separate thread. Only repost an event if
+      // |view| was created on the same thread, else the target window can get
+      // double events leading to bad behavior.
       return;
     }
-
-    // Convert the coordinates to the target window.
-    RECT window_bounds;
-    GetWindowRect(window, &window_bounds);
-    int window_x = screen_loc.x() - window_bounds.left;
-    int window_y = screen_loc.y() - window_bounds.top;
-
-    // Determine whether the click was in the client area or not.
-    // NOTE: WM_NCHITTEST coordinates are relative to the screen.
-    LRESULT nc_hit_result = SendMessage(window, WM_NCHITTEST, 0,
-                                        MAKELPARAM(screen_loc.x(),
-                                                   screen_loc.y()));
-    const bool in_client_area = (nc_hit_result == HTCLIENT);
-
-    // TODO(sky): this isn't right. The event to generate should correspond
-    // with the event we just got. MouseEvent only tells us what is down,
-    // which may differ. Need to add ability to get changed button from
-    // MouseEvent.
-    int event_type;
-    int flags = event.flags();
-    if (flags & ui::EF_LEFT_MOUSE_BUTTON)
-      event_type = in_client_area ? WM_LBUTTONDOWN : WM_NCLBUTTONDOWN;
-    else if (flags & ui::EF_MIDDLE_MOUSE_BUTTON)
-      event_type = in_client_area ? WM_MBUTTONDOWN : WM_NCMBUTTONDOWN;
-    else if (flags & ui::EF_RIGHT_MOUSE_BUTTON)
-      event_type = in_client_area ? WM_RBUTTONDOWN : WM_NCRBUTTONDOWN;
-    else
-      event_type = 0;  // Unknown mouse press.
-
-    if (event_type) {
-      if (in_client_area) {
-        PostMessage(window, event_type, event.native_event().wParam,
-                    MAKELPARAM(window_x, window_y));
-      } else {
-        PostMessage(window, event_type, nc_hit_result,
-                    MAKELPARAM(screen_loc.x(), screen_loc.y()));
-      }
-    } else if (event.type() == ui::ET_GESTURE_TAP_DOWN) {
-#if defined(USE_AURA)
-      // Gesture events need to be posted to the target root window. In
-      // desktop chrome there could be multiple root windows.
-      aura::RootWindow* target_root =
-          aura::RootWindow::GetForAcceleratedWidget(window);
-      if (target_root)
-        target_root->RepostEvent(event);
-#endif
-    }
   }
-}
-#elif defined(USE_AURA)
-void MenuController::RepostEvent(SubmenuView* source,
-                                 const ui::LocatedEvent& event) {
-  aura::RootWindow* root_window =
-      source->GetWidget()->GetNativeWindow()->GetRootWindow();
-  DCHECK(root_window);
-  root_window->RepostEvent(event);
-}
 #endif
 
+  scoped_ptr<ui::LocatedEvent> clone;
+  if (event.IsMouseEvent()) {
+    clone.reset(new ui::MouseEvent(static_cast<const ui::MouseEvent&>(event)));
+  } else if (event.IsGestureEvent()) {
+    const ui::GestureEvent& ge = static_cast<const ui::GestureEvent&>(event);
+    clone.reset(new ui::GestureEvent(ge));
+  } else {
+    NOTREACHED();
+    return;
+  }
+  clone->set_location(screen_loc);
+
+  RepostLocatedEvent(window, *clone);
+}
+
+
 void MenuController::SetDropMenuItem(
     MenuItemView* new_target,
     MenuDelegate::DropPosition new_position) {
@@ -2194,63 +2194,75 @@
     if (target == target_menu || !target->enabled())
       target = NULL;
   }
-  if (target != active_mouse_view_) {
+  View* active_mouse_view = GetActiveMouseView();
+  if (target != active_mouse_view) {
     SendMouseCaptureLostToActiveView();
-    active_mouse_view_ = target;
-    if (active_mouse_view_) {
+    active_mouse_view = target;
+    SetActiveMouseView(active_mouse_view);
+    if (active_mouse_view) {
       gfx::Point target_point(target_menu_loc);
       View::ConvertPointToTarget(
-          target_menu, active_mouse_view_, &target_point);
+          target_menu, active_mouse_view, &target_point);
       ui::MouseEvent mouse_entered_event(ui::ET_MOUSE_ENTERED,
                                          target_point, target_point,
                                          0);
-      active_mouse_view_->OnMouseEntered(mouse_entered_event);
+      active_mouse_view->OnMouseEntered(mouse_entered_event);
 
       ui::MouseEvent mouse_pressed_event(ui::ET_MOUSE_PRESSED,
                                          target_point, target_point,
                                          event.flags());
-      active_mouse_view_->OnMousePressed(mouse_pressed_event);
+      active_mouse_view->OnMousePressed(mouse_pressed_event);
     }
   }
 
-  if (active_mouse_view_) {
+  if (active_mouse_view) {
     gfx::Point target_point(target_menu_loc);
-    View::ConvertPointToTarget(target_menu, active_mouse_view_, &target_point);
+    View::ConvertPointToTarget(target_menu, active_mouse_view, &target_point);
     ui::MouseEvent mouse_dragged_event(ui::ET_MOUSE_DRAGGED,
                                        target_point, target_point,
                                        event.flags());
-    active_mouse_view_->OnMouseDragged(mouse_dragged_event);
+    active_mouse_view->OnMouseDragged(mouse_dragged_event);
   }
 }
 
 void MenuController::SendMouseReleaseToActiveView(SubmenuView* event_source,
                                                   const ui::MouseEvent& event) {
-  if (!active_mouse_view_)
+  View* active_mouse_view = GetActiveMouseView();
+  if (!active_mouse_view)
     return;
 
   gfx::Point target_loc(event.location());
   View::ConvertPointToScreen(event_source->GetScrollViewContainer(),
                              &target_loc);
-  View::ConvertPointToTarget(NULL, active_mouse_view_, &target_loc);
-  ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED,
-                           target_loc, target_loc,
-                           event.flags());
-  // Reset the active_mouse_view_ before sending mouse released. That way if it
-  // calls back to us, we aren't in a weird state.
-  View* active_view = active_mouse_view_;
-  active_mouse_view_ = NULL;
-  active_view->OnMouseReleased(release_event);
+  View::ConvertPointToTarget(NULL, active_mouse_view, &target_loc);
+  ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, target_loc, target_loc,
+                               event.flags());
+  // Reset active mouse view before sending mouse released. That way if it calls
+  // back to us, we aren't in a weird state.
+  SetActiveMouseView(NULL);
+  active_mouse_view->OnMouseReleased(release_event);
 }
 
 void MenuController::SendMouseCaptureLostToActiveView() {
-  if (!active_mouse_view_)
+  View* active_mouse_view = GetActiveMouseView();
+  if (!active_mouse_view)
     return;
 
   // Reset the active_mouse_view_ before sending mouse capture lost. That way if
   // it calls back to us, we aren't in a weird state.
-  View* active_view = active_mouse_view_;
-  active_mouse_view_ = NULL;
-  active_view->OnMouseCaptureLost();
+  SetActiveMouseView(NULL);
+  active_mouse_view->OnMouseCaptureLost();
+}
+
+void MenuController::SetActiveMouseView(View* view) {
+  if (view)
+    ViewStorage::GetInstance()->StoreView(active_mouse_view_id_, view);
+  else
+    ViewStorage::GetInstance()->RemoveView(active_mouse_view_id_);
+}
+
+View* MenuController::GetActiveMouseView() {
+  return ViewStorage::GetInstance()->RetrieveView(active_mouse_view_id_);
 }
 
 void MenuController::SetExitType(ExitType type) {
diff --git a/ui/views/controls/menu/menu_controller.h b/ui/views/controls/menu/menu_controller.h
index 6dfdd52..3037289 100644
--- a/ui/views/controls/menu/menu_controller.h
+++ b/ui/views/controls/menu/menu_controller.h
@@ -137,7 +137,7 @@
   virtual void OnWidgetDestroying(Widget* widget) OVERRIDE;
 
   // Only used for testing.
-  static void TurnOffContextMenuSelectionHoldForTest();
+  static void TurnOffMenuSelectionHoldForTest();
 
  private:
   friend class internal::MenuRunnerImpl;
@@ -445,7 +445,7 @@
   // Stops scrolling.
   void StopScrolling();
 
-  // Updates |active_mouse_view_| from the location of the event and sends it
+  // Updates active mouse view from the location of the event and sends it
   // the appropriate events. This is used to send mouse events to child views so
   // that they react to click-drag-release as if the user clicked on the view
   // itself.
@@ -453,15 +453,19 @@
                              const ui::MouseEvent& event,
                              View* target_menu);
 
-  // Sends a mouse release event to the current |active_mouse_view_| and sets
+  // Sends a mouse release event to the current active mouse view and sets
   // it to null.
   void SendMouseReleaseToActiveView(SubmenuView* event_source,
                                     const ui::MouseEvent& event);
 
-  // Sends a mouse capture lost event to the current |active_mouse_view_| and
-  // sets it to null.
+  // Sends a mouse capture lost event to the current active mouse view and sets
+  // it to null.
   void SendMouseCaptureLostToActiveView();
 
+  // Sets/gets the active mouse view. See UpdateActiveMouseView() for details.
+  void SetActiveMouseView(View* view);
+  View* GetActiveMouseView();
+
   // Sets exit type.
   void SetExitType(ExitType type);
 
@@ -556,9 +560,9 @@
 
   MenuButton* menu_button_;
 
-  // If non-null mouse drag events are forwarded to this view. See
-  // UpdateActiveMouseView for details.
-  View* active_mouse_view_;
+  // ViewStorage id used to store the view mouse drag events are forwarded to.
+  // See UpdateActiveMouseView() for details.
+  const int active_mouse_view_id_;
 
   internal::MenuControllerDelegate* delegate_;
 
@@ -574,6 +578,10 @@
   // Time when the menu is first shown.
   base::TimeTicks menu_start_time_;
 
+  // If a mouse press triggered this menu, this will have its location (in
+  // screen coordinates). Otherwise this will be (0, 0).
+  gfx::Point menu_start_mouse_press_loc_;
+
   DISALLOW_COPY_AND_ASSIGN(MenuController);
 };
 
diff --git a/ui/views/controls/native/native_view_host_unittest.cc b/ui/views/controls/native/native_view_host_unittest.cc
index 0f8eca1..f30980b 100644
--- a/ui/views/controls/native/native_view_host_unittest.cc
+++ b/ui/views/controls/native/native_view_host_unittest.cc
@@ -72,33 +72,23 @@
 // times NativeViewHierarchyChanged() is invoked.
 class NativeViewHierarchyChangedTestView : public View {
  public:
-  NativeViewHierarchyChangedTestView()
-      : attached_count_(0),
-        detached_count_(0) {
+  NativeViewHierarchyChangedTestView() : notification_count_(0) {
   }
 
-  void ResetCounts() {
-    attached_count_ = detached_count_ = 0;
+  void ResetCount() {
+    notification_count_ = 0;
   }
 
-  int attached_count() const { return attached_count_; }
-  int detached_count() const { return detached_count_; }
+  int notification_count() const { return notification_count_; }
 
   // Overriden from View:
-  virtual void NativeViewHierarchyChanged(
-      bool attached,
-      gfx::NativeView native_view,
-      internal::RootView* root_view) OVERRIDE {
-    if (attached)
-      attached_count_++;
-    else
-      detached_count_++;
-    View::NativeViewHierarchyChanged(attached, native_view, root_view);
+  virtual void NativeViewHierarchyChanged() OVERRIDE {
+    ++notification_count_;
+    View::NativeViewHierarchyChanged();
   }
 
  private:
-  int attached_count_;
-  int detached_count_;
+  int notification_count_;
 
   DISALLOW_COPY_AND_ASSIGN(NativeViewHierarchyChangedTestView);
 };
@@ -158,22 +148,21 @@
                                               test_view,
                                               host));
 
-  EXPECT_EQ(0, test_view->attached_count());
-  EXPECT_EQ(0, test_view->detached_count());
-  test_view->ResetCounts();
+  EXPECT_EQ(0, test_view->notification_count());
+  test_view->ResetCount();
 
-  // Detach should send both an attach and detach as well as changing parent.
+  // Detaching should send a NativeViewHierarchyChanged() notification and
+  // change the parent.
   host->Detach();
-  EXPECT_EQ(1, test_view->attached_count());
-  EXPECT_EQ(1, test_view->detached_count());
+  EXPECT_EQ(1, test_view->notification_count());
   EXPECT_NE(toplevel()->GetNativeView(),
             GetNativeParent(child->GetNativeView()));
-  test_view->ResetCounts();
+  test_view->ResetCount();
 
-  // Attaching should send both an attach and detach and reset parent.
+  // Attaching should send a NativeViewHierarchyChanged() notification and
+  // reset the parent.
   host->Attach(child->GetNativeView());
-  EXPECT_EQ(1, test_view->attached_count());
-  EXPECT_EQ(1, test_view->detached_count());
+  EXPECT_EQ(1, test_view->notification_count());
   EXPECT_EQ(toplevel()->GetNativeView(),
             GetNativeParent(child->GetNativeView()));
 }
diff --git a/ui/views/controls/textfield/native_textfield_views.cc b/ui/views/controls/textfield/native_textfield_views.cc
index d572a72..76ba3cb 100644
--- a/ui/views/controls/textfield/native_textfield_views.cc
+++ b/ui/views/controls/textfield/native_textfield_views.cc
@@ -744,6 +744,8 @@
   bool editable = !textfield_->read_only();
   string16 result;
   switch (command_id) {
+    case IDS_APP_UNDO:
+      return editable && model_->CanUndo();
     case IDS_APP_CUT:
       return editable && model_->HasSelection() && !textfield_->IsObscured();
     case IDS_APP_COPY:
@@ -787,6 +789,12 @@
   } else {
     bool text_changed = false;
     switch (command_id) {
+      case IDS_APP_UNDO:
+        OnBeforeUserAction();
+        text_changed = model_->Undo();
+        UpdateAfterChange(text_changed, text_changed);
+        OnAfterUserAction();
+        break;
       case IDS_APP_CUT:
         OnBeforeUserAction();
         text_changed = Cut();
@@ -1321,6 +1329,8 @@
 void NativeTextfieldViews::UpdateContextMenu() {
   if (!context_menu_contents_.get()) {
     context_menu_contents_.reset(new ui::SimpleMenuModel(this));
+    context_menu_contents_->AddItemWithStringId(IDS_APP_UNDO, IDS_APP_UNDO);
+    context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR);
     context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT);
     context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY);
     context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE);
diff --git a/ui/views/controls/textfield/native_textfield_views_unittest.cc b/ui/views/controls/textfield/native_textfield_views_unittest.cc
index 5e8c689..51c0bf5 100644
--- a/ui/views/controls/textfield/native_textfield_views_unittest.cc
+++ b/ui/views/controls/textfield/native_textfield_views_unittest.cc
@@ -319,14 +319,16 @@
   }
 
   void VerifyTextfieldContextMenuContents(bool textfield_has_selection,
-                                          ui::MenuModel* menu_model) {
-    EXPECT_TRUE(menu_model->IsEnabledAt(4 /* Separator */));
-    EXPECT_TRUE(menu_model->IsEnabledAt(5 /* SELECT ALL */));
-    EXPECT_EQ(textfield_has_selection, menu_model->IsEnabledAt(0 /* CUT */));
-    EXPECT_EQ(textfield_has_selection, menu_model->IsEnabledAt(1 /* COPY */));
-    EXPECT_EQ(textfield_has_selection, menu_model->IsEnabledAt(3 /* DELETE */));
-    string16 str(GetClipboardText());
-    EXPECT_NE(str.empty(), menu_model->IsEnabledAt(2 /* PASTE */));
+                                          bool can_undo,
+                                          ui::MenuModel* menu) {
+    EXPECT_EQ(can_undo, menu->IsEnabledAt(0 /* UNDO */));
+    EXPECT_TRUE(menu->IsEnabledAt(1 /* Separator */));
+    EXPECT_EQ(textfield_has_selection, menu->IsEnabledAt(2 /* CUT */));
+    EXPECT_EQ(textfield_has_selection, menu->IsEnabledAt(3 /* COPY */));
+    EXPECT_NE(GetClipboardText().empty(), menu->IsEnabledAt(4 /* PASTE */));
+    EXPECT_EQ(textfield_has_selection, menu->IsEnabledAt(5 /* DELETE */));
+    EXPECT_TRUE(menu->IsEnabledAt(6 /* Separator */));
+    EXPECT_TRUE(menu->IsEnabledAt(7 /* SELECT ALL */));
   }
 
   // We need widget to populate wrapper class.
@@ -790,11 +792,23 @@
 TEST_F(NativeTextfieldViewsTest, ContextMenuDisplayTest) {
   InitTextfield(Textfield::STYLE_DEFAULT);
   textfield_->SetText(ASCIIToUTF16("hello world"));
+  ui::Clipboard::GetForCurrentThread()->Clear(ui::Clipboard::BUFFER_STANDARD);
+  textfield_view_->ClearEditHistory();
   EXPECT_TRUE(GetContextMenuModel());
-  VerifyTextfieldContextMenuContents(false, GetContextMenuModel());
+  VerifyTextfieldContextMenuContents(false, false, GetContextMenuModel());
 
   textfield_->SelectAll(false);
-  VerifyTextfieldContextMenuContents(true, GetContextMenuModel());
+  VerifyTextfieldContextMenuContents(true, false, GetContextMenuModel());
+
+  SendKeyEvent(ui::VKEY_T);
+  VerifyTextfieldContextMenuContents(false, true, GetContextMenuModel());
+
+  textfield_->SelectAll(false);
+  VerifyTextfieldContextMenuContents(true, true, GetContextMenuModel());
+
+  // Exercise the "paste enabled?" check in the verifier.
+  SetClipboardText("Test");
+  VerifyTextfieldContextMenuContents(true, true, GetContextMenuModel());
 }
 
 TEST_F(NativeTextfieldViewsTest, DoubleAndTripleClickTest) {
diff --git a/ui/views/corewm/capture_controller_unittest.cc b/ui/views/corewm/capture_controller_unittest.cc
index 395c6f6..32a1955 100644
--- a/ui/views/corewm/capture_controller_unittest.cc
+++ b/ui/views/corewm/capture_controller_unittest.cc
@@ -8,9 +8,11 @@
 #include "ui/aura/env.h"
 #include "ui/aura/root_window.h"
 #include "ui/aura/test/aura_test_base.h"
+#include "ui/aura/test/event_generator.h"
 #include "ui/aura/test/test_screen.h"
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/base/events/event.h"
+#include "ui/base/events/event_utils.h"
 #include "ui/views/test/views_test_base.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/root_view.h"
@@ -132,4 +134,36 @@
   EXPECT_EQ(w2.get(), GetSecondCaptureWindow());
 }
 
+// Verifies the touch target for the RootWindow gets reset on releasing capture.
+TEST_F(CaptureControllerTest, TouchTargetResetOnCaptureChange) {
+  // Create a window inside the RootWindow.
+  scoped_ptr<aura::Window> w1(CreateNormalWindow(1, root_window(), NULL));
+  aura::test::EventGenerator event_generator1(root_window());
+  event_generator1.PressTouch();
+  w1->SetCapture();
+  // Both capture clients should return the same capture window.
+  EXPECT_EQ(w1.get(), GetCaptureWindow());
+  EXPECT_EQ(w1.get(), GetSecondCaptureWindow());
+
+  // Build a window in the second RootWindow and give it capture. Both capture
+  // clients should return the same capture window.
+  scoped_ptr<aura::Window> w2(CreateNormalWindow(2, second_root_.get(), NULL));
+  w2->SetCapture();
+  EXPECT_EQ(w2.get(), GetCaptureWindow());
+  EXPECT_EQ(w2.get(), GetSecondCaptureWindow());
+
+  // Release capture on the window. Releasing capture should reset the touch
+  // target of the first RootWindow (as it no longer contains the capture
+  // target).
+  w2->ReleaseCapture();
+  EXPECT_EQ(static_cast<aura::Window*>(NULL), GetCaptureWindow());
+  EXPECT_EQ(static_cast<aura::Window*>(NULL), GetSecondCaptureWindow());
+  ui::TouchEvent touch_event(
+      ui::ET_TOUCH_PRESSED, gfx::Point(), 0, 0, ui::EventTimeForNow(), 1.0f,
+      1.0f, 1.0f, 1.0f);
+  EXPECT_EQ(static_cast<ui::GestureConsumer*>(NULL),
+            root_window()->gesture_recognizer()->GetTouchLockedTarget(
+                &touch_event));
+}
+
 }  // namespace views
diff --git a/ui/views/corewm/cursor_manager.cc b/ui/views/corewm/cursor_manager.cc
index 07a71dc..97a19ba 100644
--- a/ui/views/corewm/cursor_manager.cc
+++ b/ui/views/corewm/cursor_manager.cc
@@ -23,6 +23,7 @@
       : cursor_(ui::kCursorNone),
         visible_(true),
         scale_(1.f),
+        cursor_set_(ui::CURSOR_SET_NORMAL),
         mouse_events_enabled_(true),
         visible_on_mouse_events_enabled_(true) {
   }
@@ -42,6 +43,11 @@
     scale_ = scale;
   }
 
+  ui::CursorSetType cursor_set() const { return cursor_set_; }
+  void set_cursor_set(ui::CursorSetType cursor_set) {
+    cursor_set_ = cursor_set;
+  }
+
   bool mouse_events_enabled() const { return mouse_events_enabled_; }
   void SetMouseEventsEnabled(bool enabled) {
     if (mouse_events_enabled_ == enabled)
@@ -61,6 +67,7 @@
   gfx::NativeCursor cursor_;
   bool visible_;
   float scale_;
+  ui::CursorSetType cursor_set_;
   bool mouse_events_enabled_;
 
   // The visibility to set when mouse events are enabled.
@@ -119,10 +126,20 @@
     delegate_->SetScale(state_on_unlock_->scale(), this);
 }
 
+void CursorManager::SetCursorSet(ui::CursorSetType cursor_set) {
+  state_on_unlock_->set_cursor_set(cursor_set);
+  if (GetCurrentCursorSet() != state_on_unlock_->cursor_set())
+    delegate_->SetCursorSet(state_on_unlock_->cursor_set(), this);
+}
+
 float CursorManager::GetCurrentScale() const {
   return current_state_->scale();
 }
 
+ui::CursorSetType CursorManager::GetCurrentCursorSet() const {
+  return current_state_->cursor_set();
+}
+
 void CursorManager::EnableMouseEvents() {
   state_on_unlock_->SetMouseEventsEnabled(true);
   if (cursor_lock_count_ == 0 &&
@@ -212,6 +229,10 @@
   current_state_->set_scale(scale);
 }
 
+void CursorManager::CommitCursorSet(ui::CursorSetType cursor_set) {
+  current_state_->set_cursor_set(cursor_set);
+}
+
 void CursorManager::CommitMouseEventsEnabled(bool enabled) {
   current_state_->SetMouseEventsEnabled(enabled);
 }
diff --git a/ui/views/corewm/cursor_manager.h b/ui/views/corewm/cursor_manager.h
index 659992b..1706ed8 100644
--- a/ui/views/corewm/cursor_manager.h
+++ b/ui/views/corewm/cursor_manager.h
@@ -10,6 +10,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/observer_list.h"
 #include "ui/aura/client/cursor_client.h"
+#include "ui/base/cursor/cursor.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gfx/point.h"
 #include "ui/views/corewm/native_cursor_manager_delegate.h"
@@ -45,6 +46,8 @@
   virtual bool IsCursorVisible() const OVERRIDE;
   virtual void SetScale(float scale) OVERRIDE;
   virtual float GetCurrentScale() const OVERRIDE;
+  virtual void SetCursorSet(ui::CursorSetType cursor_set) OVERRIDE;
+  virtual ui::CursorSetType GetCurrentCursorSet() const OVERRIDE;
   virtual void EnableMouseEvents() OVERRIDE;
   virtual void DisableMouseEvents() OVERRIDE;
   virtual bool IsMouseEventsEnabled() const OVERRIDE;
@@ -65,6 +68,7 @@
   virtual void CommitCursor(gfx::NativeCursor cursor) OVERRIDE;
   virtual void CommitVisibility(bool visible) OVERRIDE;
   virtual void CommitScale(float scale) OVERRIDE;
+  virtual void CommitCursorSet(ui::CursorSetType cursor_set) OVERRIDE;
   virtual void CommitMouseEventsEnabled(bool enabled) OVERRIDE;
 
   scoped_ptr<NativeCursorManager> delegate_;
diff --git a/ui/views/corewm/cursor_manager_unittest.cc b/ui/views/corewm/cursor_manager_unittest.cc
index 650d5b8..1e6a165 100644
--- a/ui/views/corewm/cursor_manager_unittest.cc
+++ b/ui/views/corewm/cursor_manager_unittest.cc
@@ -38,6 +38,12 @@
     delegate->CommitMouseEventsEnabled(enabled);
   }
 
+  virtual void SetCursorSet(
+      ui::CursorSetType cursor_set,
+      views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE {
+    delegate->CommitCursorSet(cursor_set);
+  }
+
   virtual void SetScale(
       float scale,
       views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE {
@@ -186,6 +192,19 @@
   EXPECT_FALSE(cursor_manager_.IsMouseEventsEnabled());
 }
 
+TEST_F(CursorManagerTest, SetCursorSet) {
+  EXPECT_EQ(ui::CURSOR_SET_NORMAL, cursor_manager_.GetCurrentCursorSet());
+
+  cursor_manager_.SetCursorSet(ui::CURSOR_SET_NORMAL);
+  EXPECT_EQ(ui::CURSOR_SET_NORMAL, cursor_manager_.GetCurrentCursorSet());
+
+  cursor_manager_.SetCursorSet(ui::CURSOR_SET_LARGE);
+  EXPECT_EQ(ui::CURSOR_SET_LARGE, cursor_manager_.GetCurrentCursorSet());
+
+  cursor_manager_.SetCursorSet(ui::CURSOR_SET_NORMAL);
+  EXPECT_EQ(ui::CURSOR_SET_NORMAL, cursor_manager_.GetCurrentCursorSet());
+}
+
 TEST_F(CursorManagerTest, SetScale) {
   EXPECT_EQ(1.f, cursor_manager_.GetCurrentScale());
   cursor_manager_.SetScale(2.f);
diff --git a/ui/views/corewm/native_cursor_manager.h b/ui/views/corewm/native_cursor_manager.h
index c38fb34..75e4c85 100644
--- a/ui/views/corewm/native_cursor_manager.h
+++ b/ui/views/corewm/native_cursor_manager.h
@@ -48,6 +48,11 @@
       float scale,
       views::corewm::NativeCursorManagerDelegate* delegate) = 0;
 
+  // A request to set the scale of the cursor icon.
+  virtual void SetCursorSet(
+      ui::CursorSetType cursor_set,
+      views::corewm::NativeCursorManagerDelegate* delegate) = 0;
+
   // A request to set whether mouse events are disabled. At minimum,
   // implementer should call NativeCursorManagerDelegate::
   // CommitMouseEventsEnabled() with whether mouse events are actually enabled.
diff --git a/ui/views/corewm/native_cursor_manager_delegate.h b/ui/views/corewm/native_cursor_manager_delegate.h
index 1e6d46a..ea3788f 100644
--- a/ui/views/corewm/native_cursor_manager_delegate.h
+++ b/ui/views/corewm/native_cursor_manager_delegate.h
@@ -5,6 +5,7 @@
 #ifndef UI_VIEWS_COREWM_NATIVE_CURSOR_MANAGER_DELEGATE_H_
 #define UI_VIEWS_COREWM_NATIVE_CURSOR_MANAGER_DELEGATE_H_
 
+#include "ui/base/cursor/cursor.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/views/views_export.h"
 
@@ -21,11 +22,13 @@
   virtual gfx::NativeCursor GetCurrentCursor() const = 0;
   virtual bool GetCurrentVisibility() const = 0;
   virtual float GetCurrentScale() const = 0;
+  virtual ui::CursorSetType GetCurrentCursorSet() const = 0;
   virtual bool GetMouseEventsEnabled() const = 0;
 
   virtual void CommitCursor(gfx::NativeCursor cursor) = 0;
   virtual void CommitVisibility(bool visible) = 0;
   virtual void CommitScale(float scale) = 0;
+  virtual void CommitCursorSet(ui::CursorSetType cursor_set) = 0;
   virtual void CommitMouseEventsEnabled(bool enabled) = 0;
 };
 
diff --git a/ui/views/corewm/tooltip_controller.cc b/ui/views/corewm/tooltip_controller.cc
index 7e3d262..1c17d91 100644
--- a/ui/views/corewm/tooltip_controller.cc
+++ b/ui/views/corewm/tooltip_controller.cc
@@ -256,13 +256,17 @@
 void TooltipController::OnMouseEvent(ui::MouseEvent* event) {
   aura::Window* target = static_cast<aura::Window*>(event->target());
   switch (event->type()) {
+    case ui::ET_MOUSE_EXITED:
+      target = NULL;
+      // Fall through.
     case ui::ET_MOUSE_MOVED:
     case ui::ET_MOUSE_DRAGGED:
       if (tooltip_window_ != target) {
         if (tooltip_window_)
           tooltip_window_->RemoveObserver(this);
         tooltip_window_ = target;
-        tooltip_window_->AddObserver(this);
+        if (tooltip_window_)
+          tooltip_window_->AddObserver(this);
       }
       curr_mouse_loc_ = event->location();
       if (tooltip_timer_.IsRunning())
diff --git a/ui/views/corewm/tooltip_controller_unittest.cc b/ui/views/corewm/tooltip_controller_unittest.cc
index 5cb3fe6..2ea2129 100644
--- a/ui/views/corewm/tooltip_controller_unittest.cc
+++ b/ui/views/corewm/tooltip_controller_unittest.cc
@@ -409,6 +409,23 @@
   EXPECT_EQ(window, helper_->GetTooltipWindow());
 }
 
+// Verifies a mouse exit event hides the tooltips.
+TEST_F(TooltipControllerTest, HideOnExit) {
+  view_->set_tooltip_text(ASCIIToUTF16("Tooltip Text"));
+  generator_->MoveMouseToCenterOf(GetWindow());
+  string16 expected_tooltip = ASCIIToUTF16("Tooltip Text");
+  EXPECT_EQ(expected_tooltip, aura::client::GetTooltipText(GetWindow()));
+  EXPECT_EQ(string16(), helper_->GetTooltipText());
+  EXPECT_EQ(GetWindow(), helper_->GetTooltipWindow());
+
+  // Fire tooltip timer so tooltip becomes visible.
+  helper_->FireTooltipTimer();
+
+  EXPECT_TRUE(helper_->IsTooltipVisible());
+  generator_->SendMouseExit();
+  EXPECT_FALSE(helper_->IsTooltipVisible());
+}
+
 }  // namespace test
 }  // namespace corewm
 }  // namespace views
diff --git a/ui/views/event_utils.h b/ui/views/event_utils.h
new file mode 100644
index 0000000..3527c50
--- /dev/null
+++ b/ui/views/event_utils.h
@@ -0,0 +1,25 @@
+// 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 UI_VIEWS_EVENT_UTILS_H_
+#define UI_VIEWS_EVENT_UTILS_H_
+
+#include "ui/gfx/native_widget_types.h"
+#include "ui/views/views_export.h"
+
+namespace ui {
+class LocatedEvent;
+}
+
+namespace views {
+
+// Reposts a located event natively. Returns false when |event| could not be
+// reposted. |event| should be in screen coordinates. |window| is the target
+// window that the event will be forwarded to. Only some events are supported.
+VIEWS_EXPORT bool RepostLocatedEvent(gfx::NativeWindow window,
+                                     const ui::LocatedEvent& event);
+
+}  // namespace views
+
+#endif  // UI_VIEWS_EVENT_UTILS_H_
diff --git a/ui/views/event_utils_aura.cc b/ui/views/event_utils_aura.cc
new file mode 100644
index 0000000..37aeb88
--- /dev/null
+++ b/ui/views/event_utils_aura.cc
@@ -0,0 +1,49 @@
+// 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 "ui/views/event_utils.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/aura/client/screen_position_client.h"
+#include "ui/aura/root_window.h"
+#include "ui/base/events/event.h"
+#include "ui/gfx/point.h"
+
+using aura::client::ScreenPositionClient;
+
+namespace views {
+
+bool RepostLocatedEvent(gfx::NativeWindow window,
+                        const ui::LocatedEvent& event) {
+  if (!window)
+    return false;
+
+  aura::RootWindow* root_window = window->GetRootWindow();
+
+  gfx::Point root_loc(event.location());
+  ScreenPositionClient* spc = GetScreenPositionClient(root_window);
+  if (!spc)
+    return false;
+
+  spc->ConvertPointFromScreen(root_window, &root_loc);
+
+  scoped_ptr<ui::LocatedEvent> relocated;
+  if (event.IsMouseEvent()) {
+    const ui::MouseEvent& orig = static_cast<const ui::MouseEvent&>(event);
+    relocated.reset(new ui::MouseEvent(orig));
+  } else if (event.IsGestureEvent()) {
+    const ui::GestureEvent& orig = static_cast<const ui::GestureEvent&>(event);
+    relocated.reset(new ui::GestureEvent(orig));
+  } else {
+    NOTREACHED();
+    return false;
+  }
+  relocated->set_location(root_loc);
+
+  root_window->RepostEvent(*relocated);
+  return true;
+}
+
+}  // namespace views
diff --git a/ui/views/event_utils_win.cc b/ui/views/event_utils_win.cc
new file mode 100644
index 0000000..d9417a2
--- /dev/null
+++ b/ui/views/event_utils_win.cc
@@ -0,0 +1,59 @@
+// 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 "ui/views/event_utils.h"
+
+#include <windowsx.h>
+
+#include "base/logging.h"
+#include "ui/base/events/event.h"
+#include "ui/base/events/event_constants.h"
+#include "ui/gfx/point.h"
+
+namespace views {
+
+bool RepostLocatedEvent(gfx::NativeWindow window,
+                        const ui::LocatedEvent& event) {
+  if (!window)
+    return false;
+
+  // Determine whether the click was in the client area or not.
+  // NOTE: WM_NCHITTEST coordinates are relative to the screen.
+  const gfx::Point screen_loc = event.location();
+  LRESULT nc_hit_result = SendMessage(window, WM_NCHITTEST, 0,
+                                      MAKELPARAM(screen_loc.x(),
+                                                 screen_loc.y()));
+  const bool in_client_area = nc_hit_result == HTCLIENT;
+
+  // TODO(sky): this isn't right. The event to generate should correspond with
+  // the event we just got. MouseEvent only tells us what is down, which may
+  // differ. Need to add ability to get changed button from MouseEvent.
+  int event_type;
+  int flags = event.flags();
+  if (flags & ui::EF_LEFT_MOUSE_BUTTON) {
+    event_type = in_client_area ? WM_LBUTTONDOWN : WM_NCLBUTTONDOWN;
+  } else if (flags & ui::EF_MIDDLE_MOUSE_BUTTON) {
+    event_type = in_client_area ? WM_MBUTTONDOWN : WM_NCMBUTTONDOWN;
+  } else if (flags & ui::EF_RIGHT_MOUSE_BUTTON) {
+    event_type = in_client_area ? WM_RBUTTONDOWN : WM_NCRBUTTONDOWN;
+  } else {
+    NOTREACHED();
+    return false;
+  }
+
+  int window_x = screen_loc.x();
+  int window_y = screen_loc.y();
+  if (in_client_area) {
+    RECT window_bounds;
+    GetWindowRect(window, &window_bounds);
+    window_x -= window_bounds.left;
+    window_y -= window_bounds.top;
+  }
+
+  WPARAM target = in_client_area ? event.native_event().wParam : nc_hit_result;
+  PostMessage(window, event_type, target, MAKELPARAM(window_x, window_y));
+  return true;
+}
+
+}  // namespace views
diff --git a/ui/views/mouse_constants.cc b/ui/views/mouse_constants.cc
new file mode 100644
index 0000000..b2f3b96
--- /dev/null
+++ b/ui/views/mouse_constants.cc
@@ -0,0 +1,12 @@
+// 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 "ui/views/mouse_constants.h"
+
+namespace views {
+
+const int kMinimumMsPressedToActivate = 200;
+const int kMinimumMsBetweenButtonClicks = 100;
+
+}  // namespace views
diff --git a/ui/views/mouse_constants.h b/ui/views/mouse_constants.h
new file mode 100644
index 0000000..e22c75c
--- /dev/null
+++ b/ui/views/mouse_constants.h
@@ -0,0 +1,25 @@
+// 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 UI_VIEWS_MOUSE_CONSTANTS_H_
+#define UI_VIEWS_MOUSE_CONSTANTS_H_
+
+#include "base/basictypes.h"
+#include "ui/views/views_export.h"
+
+namespace views {
+
+// The amount of time the mouse should be down before a mouse release is
+// considered intentional. This is to prevent spurious mouse releases from
+// activating controls, especially when some UI element is revealed under the
+// source of the activation (ex. menus showing underneath menu buttons).
+VIEWS_EXPORT extern const int kMinimumMsPressedToActivate;
+
+// The amount of time, in milliseconds, between clicks until they're considered
+// intentionally different.
+VIEWS_EXPORT extern const int kMinimumMsBetweenButtonClicks;
+
+}  // namespace views
+
+#endif  // UI_VIEWS_MOUSE_CONSTANTS_H_
diff --git a/ui/views/mouse_watcher_view_host.cc b/ui/views/mouse_watcher_view_host.cc
index 1d420fa..54412cf 100644
--- a/ui/views/mouse_watcher_view_host.cc
+++ b/ui/views/mouse_watcher_view_host.cc
@@ -50,7 +50,7 @@
     return false;
 
   return gfx::Screen::GetScreenFor(widget->GetNativeView())->
-      GetWindowAtCursorScreenPoint() == widget->GetNativeWindow();
+      GetWindowUnderCursor() == widget->GetNativeWindow();
 }
 
 }  // namespace views
diff --git a/ui/views/view.cc b/ui/views/view.cc
index 73b1e4e..81f0207 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -9,7 +9,6 @@
 #include <algorithm>
 #include <cmath>
 
-#include "base/auto_reset.h"
 #include "base/debug/trace_event.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
@@ -177,7 +176,6 @@
       focus_border_(FocusBorder::CreateDashedFocusBorder()),
       flip_canvas_on_paint_for_rtl_ui_(false),
       paint_to_layer_(false),
-      accelerator_registration_delayed_(false),
       accelerator_focus_manager_(NULL),
       registered_accelerator_count_(0),
       next_focusable_view_(NULL),
@@ -186,7 +184,6 @@
       accessibility_focusable_(false),
       context_menu_controller_(NULL),
       drag_controller_(NULL),
-      currently_dragging_(false),
       post_dispatch_handler_(new internal::PostEventDispatchHandler(this)),
       native_view_accessibility_(NULL) {
   AddPostTargetHandler(post_dispatch_handler_.get());
@@ -196,10 +193,6 @@
   if (parent_)
     parent_->RemoveChildView(this);
 
-  // If we destroy the view during a drag, AutoReset will probably write to
-  // freed memory.
-  DCHECK(!currently_dragging_);
-
   for (Views::const_iterator i(children_.begin()); i != children_.end(); ++i) {
     (*i)->parent_ = NULL;
     if (!(*i)->owned_by_client_)
@@ -1305,21 +1298,13 @@
 void View::VisibilityChanged(View* starting_from, bool is_visible) {
 }
 
-void View::NativeViewHierarchyChanged(bool attached,
-                                      gfx::NativeView native_view,
-                                      internal::RootView* root_view) {
+void View::NativeViewHierarchyChanged() {
   FocusManager* focus_manager = GetFocusManager();
-  if (!accelerator_registration_delayed_ &&
-      accelerator_focus_manager_ &&
-      accelerator_focus_manager_ != focus_manager) {
+  if (accelerator_focus_manager_ != focus_manager) {
     UnregisterAccelerators(true);
-    accelerator_registration_delayed_ = true;
-  }
-  if (accelerator_registration_delayed_ && attached) {
-    if (focus_manager) {
+
+    if (focus_manager)
       RegisterPendingAccelerators();
-      accelerator_registration_delayed_ = false;
-    }
   }
 }
 
@@ -1821,14 +1806,10 @@
   ViewHierarchyChangedImpl(true, details);
 }
 
-void View::PropagateNativeViewHierarchyChanged(bool attached,
-                                               gfx::NativeView native_view,
-                                               internal::RootView* root_view) {
+void View::PropagateNativeViewHierarchyChanged() {
   for (int i = 0, count = child_count(); i < count; ++i)
-    child_at(i)->PropagateNativeViewHierarchyChanged(attached,
-                                                           native_view,
-                                                           root_view);
-  NativeViewHierarchyChanged(attached, native_view, root_view);
+    child_at(i)->PropagateNativeViewHierarchyChanged();
+  NativeViewHierarchyChanged();
 }
 
 void View::ViewHierarchyChangedImpl(
@@ -1838,13 +1819,8 @@
     if (details.is_add) {
       // If you get this registration, you are part of a subtree that has been
       // added to the view hierarchy.
-      if (GetFocusManager()) {
+      if (GetFocusManager())
         RegisterPendingAccelerators();
-      } else {
-        // Delay accelerator registration until visible as we do not have
-        // focus manager until then.
-        accelerator_registration_delayed_ = true;
-      }
     } else {
       if (details.child == this)
         UnregisterAccelerators(true);
@@ -1857,7 +1833,7 @@
     if (widget)
       widget->UpdateRootLayers();
   } else if (!details.is_add && details.child == this) {
-    // Make sure the layers beloning to the subtree rooted at |child| get
+    // Make sure the layers belonging to the subtree rooted at |child| get
     // removed from layers that do not belong in the same subtree.
     OrphanLayers();
     if (use_acceleration_when_possible) {
@@ -2235,9 +2211,6 @@
 
   if (GetWidget()) {
     if (accelerator_focus_manager_) {
-      // We may not have a FocusManager if the window containing us is being
-      // closed, in which case the FocusManager is being deleted so there is
-      // nothing to unregister.
       accelerator_focus_manager_->UnregisterAccelerators(this);
       accelerator_focus_manager_ = NULL;
     }
@@ -2324,14 +2297,20 @@
                   const gfx::Point& press_pt,
                   ui::DragDropTypes::DragEventSource source) {
 #if !defined(OS_MACOSX)
-  if (currently_dragging_)
-    return false;
-
   int drag_operations = GetDragOperations(press_pt);
   if (drag_operations == ui::DragDropTypes::DRAG_NONE)
     return false;
 
-  base::AutoReset<bool> updating_focus(&currently_dragging_, true);
+  Widget* widget = GetWidget();
+  // We should only start a drag from an event, implying we have a widget.
+  DCHECK(widget);
+
+  // Don't attempt to start a drag while in the process of dragging. This is
+  // especially important on X where we can get multiple mouse move events when
+  // we start the drag.
+  if (widget->dragged_view())
+    return false;
+
   OSExchangeData data;
   WriteDragData(press_pt, &data);
 
@@ -2339,9 +2318,8 @@
   // the RootView can detect it and avoid calling us back.
   gfx::Point widget_location(event.location());
   ConvertPointToWidget(this, &widget_location);
-  GetWidget()->RunShellDrag(this, data, widget_location, drag_operations,
-      source);
-
+  widget->RunShellDrag(this, data, widget_location, drag_operations, source);
+  // WARNING: we may have been deleted.
   return true;
 #else
   return false;
diff --git a/ui/views/view.h b/ui/views/view.h
index 337012c..0935c51 100644
--- a/ui/views/view.h
+++ b/ui/views/view.h
@@ -1032,17 +1032,13 @@
   // invoked for that view as well as all the children recursively.
   virtual void VisibilityChanged(View* starting_from, bool is_visible);
 
-  // Called when the native view hierarchy changed.
-  // |attached| is true if that view has been attached to a new NativeView
-  // hierarchy, false if it has been detached.
-  // |native_view| is the NativeView this view was attached/detached from, and
-  // |root_view| is the root view associated with the NativeView.
-  // Views created without a native view parent don't have a focus manager.
-  // When this function is called they could do the processing that requires
-  // it - like registering accelerators, for example.
-  virtual void NativeViewHierarchyChanged(bool attached,
-                                          gfx::NativeView native_view,
-                                          internal::RootView* root_view);
+  // This method is invoked when the parent NativeView of the widget that the
+  // view is attached to has changed and the view hierarchy has not changed.
+  // ViewHierarchyChanged() is called when the parent NativeView of the widget
+  // that the view is attached to is changed as a result of changing the view
+  // hierarchy. Overriding this method is useful for tracking which
+  // FocusManager manages this view.
+  virtual void NativeViewHierarchyChanged();
 
   // Painting ------------------------------------------------------------------
 
@@ -1260,9 +1256,7 @@
 
   // Propagates NativeViewHierarchyChanged() notification through all the
   // children.
-  void PropagateNativeViewHierarchyChanged(bool attached,
-                                           gfx::NativeView native_view,
-                                           internal::RootView* root_view);
+  void PropagateNativeViewHierarchyChanged();
 
   // Takes care of registering/unregistering accelerators if
   // |register_accelerators| true and calls ViewHierarchyChanged().
@@ -1503,10 +1497,6 @@
 
   // Accelerators --------------------------------------------------------------
 
-  // true if when we were added to hierarchy we were without focus manager
-  // attempt addition when ancestor chain changed.
-  bool accelerator_registration_delayed_;
-
   // Focus manager accelerators registered on.
   FocusManager* accelerator_focus_manager_;
 
@@ -1540,11 +1530,6 @@
 
   DragController* drag_controller_;
 
-  // True while we're performing DoDrag(). On X11, we can have multiple mouse
-  // move events in our event queue when we start a drag, so we need a way to
-  // ignore events after the first one.
-  bool currently_dragging_;
-
   // Input  --------------------------------------------------------------------
 
   scoped_ptr<internal::PostEventDispatchHandler> post_dispatch_handler_;
diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc
index dfb604e..6cd2a00 100644
--- a/ui/views/view_unittest.cc
+++ b/ui/views/view_unittest.cc
@@ -1545,98 +1545,71 @@
 ////////////////////////////////////////////////////////////////////////////////
 // Native view hierachy
 ////////////////////////////////////////////////////////////////////////////////
-class TestNativeViewHierarchy : public View {
+class ToplevelWidgetObserverView : public View {
  public:
-  TestNativeViewHierarchy() {
+  ToplevelWidgetObserverView() : toplevel_(NULL) {
+  }
+  virtual ~ToplevelWidgetObserverView() {
   }
 
-  virtual void NativeViewHierarchyChanged(
-      bool attached,
-      gfx::NativeView native_view,
-      internal::RootView* root_view) OVERRIDE {
-    NotificationInfo info;
-    info.attached = attached;
-    info.native_view = native_view;
-    info.root_view = root_view;
-    notifications_.push_back(info);
-  };
-  struct NotificationInfo {
-    bool attached;
-    gfx::NativeView native_view;
-    internal::RootView* root_view;
-  };
-  static const size_t kTotalViews = 2;
-  std::vector<NotificationInfo> notifications_;
+  // View overrides:
+  virtual void ViewHierarchyChanged(
+      const ViewHierarchyChangedDetails& details) OVERRIDE {
+    if (details.is_add) {
+      toplevel_ = GetWidget() ? GetWidget()->GetTopLevelWidget() : NULL;
+    } else {
+      toplevel_ = NULL;
+    }
+  }
+  virtual void NativeViewHierarchyChanged() OVERRIDE {
+    toplevel_ = GetWidget() ? GetWidget()->GetTopLevelWidget() : NULL;
+  }
+
+  Widget* toplevel() { return toplevel_; }
+
+ private:
+  Widget* toplevel_;
+
+  DISALLOW_COPY_AND_ASSIGN(ToplevelWidgetObserverView);
 };
 
-class TestChangeNativeViewHierarchy {
- public:
-  explicit TestChangeNativeViewHierarchy(ViewTest *view_test) {
-    view_test_ = view_test;
-    native_host_ = new NativeViewHost();
-    host_ = new Widget;
-    Widget::InitParams params =
-        view_test->CreateParams(Widget::InitParams::TYPE_POPUP);
-    params.bounds = gfx::Rect(0, 0, 500, 300);
-    host_->Init(params);
-    host_->GetRootView()->AddChildView(native_host_);
-    for (size_t i = 0; i < TestNativeViewHierarchy::kTotalViews; ++i) {
-      windows_[i] = new Widget;
-      Widget::InitParams params(Widget::InitParams::TYPE_CONTROL);
-      params.parent = host_->GetNativeView();
-      params.bounds = gfx::Rect(0, 0, 500, 300);
-      windows_[i]->Init(params);
-      root_views_[i] = windows_[i]->GetRootView();
-      test_views_[i] = new TestNativeViewHierarchy;
-      root_views_[i]->AddChildView(test_views_[i]);
-    }
-  }
+// Test that a view can track the current top level widget by overriding
+// View::ViewHierarchyChanged() and View::NativeViewHierarchyChanged().
+TEST_F(ViewTest, NativeViewHierarchyChanged) {
+  scoped_ptr<Widget> toplevel1(new Widget);
+  Widget::InitParams toplevel1_params =
+      CreateParams(Widget::InitParams::TYPE_POPUP);
+  toplevel1_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+  toplevel1->Init(toplevel1_params);
 
-  ~TestChangeNativeViewHierarchy() {
-    for (size_t i = 0; i < TestNativeViewHierarchy::kTotalViews; ++i) {
-      windows_[i]->Close();
-    }
-    host_->Close();
-    // Will close and self-delete widgets - no need to manually delete them.
-    view_test_->RunPendingMessages();
-  }
+  scoped_ptr<Widget> toplevel2(new Widget);
+  Widget::InitParams toplevel2_params =
+      CreateParams(Widget::InitParams::TYPE_POPUP);
+  toplevel2_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+  toplevel2->Init(toplevel2_params);
 
-  void CheckChangingHierarhy() {
-    size_t i;
-    for (i = 0; i < TestNativeViewHierarchy::kTotalViews; ++i) {
-      // TODO(georgey): use actual hierarchy changes to send notifications.
-      static_cast<internal::RootView*>(root_views_[i])->
-          NotifyNativeViewHierarchyChanged(false, host_->GetNativeView());
-      static_cast<internal::RootView*>(root_views_[i])->
-          NotifyNativeViewHierarchyChanged(true, host_->GetNativeView());
-    }
-    for (i = 0; i < TestNativeViewHierarchy::kTotalViews; ++i) {
-      ASSERT_EQ(static_cast<size_t>(2), test_views_[i]->notifications_.size());
-      EXPECT_FALSE(test_views_[i]->notifications_[0].attached);
-      EXPECT_EQ(host_->GetNativeView(),
-          test_views_[i]->notifications_[0].native_view);
-      EXPECT_EQ(root_views_[i], test_views_[i]->notifications_[0].root_view);
-      EXPECT_TRUE(test_views_[i]->notifications_[1].attached);
-      EXPECT_EQ(host_->GetNativeView(),
-          test_views_[i]->notifications_[1].native_view);
-      EXPECT_EQ(root_views_[i], test_views_[i]->notifications_[1].root_view);
-    }
-  }
+  Widget* child = new Widget;
+  Widget::InitParams child_params(Widget::InitParams::TYPE_CONTROL);
+  child_params.parent = toplevel1->GetNativeView();
+  child->Init(child_params);
 
-  NativeViewHost* native_host_;
-  Widget* host_;
-  Widget* windows_[TestNativeViewHierarchy::kTotalViews];
-  View* root_views_[TestNativeViewHierarchy::kTotalViews];
-  TestNativeViewHierarchy* test_views_[TestNativeViewHierarchy::kTotalViews];
-  ViewTest* view_test_;
-};
+  ToplevelWidgetObserverView* observer_view =
+      new ToplevelWidgetObserverView();
+  EXPECT_EQ(NULL, observer_view->toplevel());
 
-TEST_F(ViewTest, ChangeNativeViewHierarchyChangeHierarchy) {
-  // TODO(georgey): Fix the test for Linux
-#if defined(OS_WIN)
-  TestChangeNativeViewHierarchy test(this);
-  test.CheckChangingHierarhy();
-#endif
+  child->SetContentsView(observer_view);
+  EXPECT_EQ(toplevel1, observer_view->toplevel());
+
+  Widget::ReparentNativeView(child->GetNativeView(),
+                             toplevel2->GetNativeView());
+  EXPECT_EQ(toplevel2, observer_view->toplevel());
+
+  observer_view->parent()->RemoveChildView(observer_view);
+  EXPECT_EQ(NULL, observer_view->toplevel());
+
+  // Make |observer_view| |child|'s contents view again so that it gets deleted
+  // with the widget.
+  child->SetContentsView(observer_view);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/ui/views/views.gyp b/ui/views/views.gyp
index a8d444c..34e837a 100644
--- a/ui/views/views.gyp
+++ b/ui/views/views.gyp
@@ -275,6 +275,9 @@
         'drag_controller.h',
         'drag_utils.cc',
         'drag_utils.h',
+        'event_utils.h',
+        'event_utils_aura.cc',
+        'event_utils_win.cc',
         'focus/accelerator_handler.h',
         'focus/accelerator_handler_aura.cc',
         'focus/accelerator_handler_win.cc',
@@ -314,6 +317,8 @@
         'metrics.h',
         'metrics_aura.cc',
         'metrics_win.cc',
+        'mouse_constants.cc',
+        'mouse_constants.h',
         'mouse_watcher.cc',
         'mouse_watcher.h',
         'mouse_watcher_view_host.cc',
@@ -362,6 +367,8 @@
         'widget/desktop_aura/desktop_drag_drop_client_win.h',
         'widget/desktop_aura/desktop_drop_target_win.cc',
         'widget/desktop_aura/desktop_drop_target_win.h',
+        'widget/desktop_aura/desktop_factory_ozone.cc',
+        'widget/desktop_aura/desktop_factory_ozone.h',
         'widget/desktop_aura/desktop_focus_rules.cc',
         'widget/desktop_aura/desktop_focus_rules.h',
         'widget/desktop_aura/desktop_layout_manager.cc',
@@ -371,6 +378,7 @@
         'widget/desktop_aura/desktop_native_widget_aura.cc',
         'widget/desktop_aura/desktop_native_widget_aura.h',
         'widget/desktop_aura/desktop_root_window_host.h',
+        'widget/desktop_aura/desktop_root_window_host_ozone.cc',
         'widget/desktop_aura/desktop_root_window_host_win.cc',
         'widget/desktop_aura/desktop_root_window_host_win.h',
         'widget/desktop_aura/desktop_root_window_host_x11.cc',
@@ -382,6 +390,8 @@
         'widget/desktop_aura/desktop_screen_win.cc',
         'widget/desktop_aura/desktop_screen_win.h',
         'widget/desktop_aura/desktop_screen_x11.cc',
+        'widget/desktop_aura/scoped_tooltip_client.cc',
+        'widget/desktop_aura/scoped_tooltip_client.h',
         'widget/desktop_aura/x11_desktop_handler.cc',
         'widget/desktop_aura/x11_desktop_handler.h',
         'widget/desktop_aura/x11_desktop_window_move_client.cc',
@@ -615,6 +625,7 @@
         'test/test_widget_observer.h',
         'test/views_test_base.cc',
         'test/views_test_base.h',
+        'widget/root_view_test_helper.h',
       ],
       'conditions': [
         ['use_aura==1', {
@@ -732,19 +743,21 @@
         'ime/input_method_bridge_unittest.cc',
         'layout/box_layout_unittest.cc',
         'layout/grid_layout_unittest.cc',
+        'run_all_unittests.cc',
         'touchui/touch_selection_controller_impl_unittest.cc',
         'view_model_unittest.cc',
         'view_model_utils_unittest.cc',
         'view_unittest.cc',
-        'window/dialog_client_view_unittest.cc',
-        'window/dialog_delegate_unittest.cc',
+        'widget/desktop_aura/desktop_native_widget_aura_unittest.cc',
         'widget/desktop_aura/desktop_screen_position_client_unittest.cc',
         'widget/native_widget_aura_unittest.cc',
         'widget/native_widget_unittest.cc',
         'widget/native_widget_win_unittest.cc',
+        'widget/root_view_unittest.cc',
         'widget/widget_unittest.cc',
         'widget/window_reorderer_unittest.cc',
-        'run_all_unittests.cc',
+        'window/dialog_client_view_unittest.cc',
+        'window/dialog_delegate_unittest.cc',
       ],
       'conditions': [
         ['chromeos==0', {
@@ -798,6 +811,11 @@
             ['exclude', 'widget/window_reorderer_unittest.cc']
           ],
         }],
+        ['use_ozone==1', {
+          'sources!': [
+            'corewm/capture_controller_unittest.cc',
+          ],
+        }],
       ],
     },  # target_name: views_unittests
     {
diff --git a/ui/views/widget/desktop_aura/desktop_cursor_loader_updater_aurax11.cc b/ui/views/widget/desktop_aura/desktop_cursor_loader_updater_aurax11.cc
index e771f69..88f2193 100644
--- a/ui/views/widget/desktop_aura/desktop_cursor_loader_updater_aurax11.cc
+++ b/ui/views/widget/desktop_aura/desktop_cursor_loader_updater_aurax11.cc
@@ -35,8 +35,12 @@
   int resource_id;
   gfx::Point point;
   for (size_t i = 0; i < arraysize(kImageCursorIds); ++i) {
-    bool success = ui::GetCursorDataFor(kImageCursorIds[i], device_scale_factor,
-                                        &resource_id, &point);
+    bool success = ui::GetCursorDataFor(
+        ui::CURSOR_SET_NORMAL,  // Not support custom cursor set.
+        kImageCursorIds[i],
+        device_scale_factor,
+        &resource_id,
+        &point);
     DCHECK(success);
     loader->LoadImageCursor(kImageCursorIds[i], resource_id, point);
   }
diff --git a/ui/views/widget/desktop_aura/desktop_factory_ozone.cc b/ui/views/widget/desktop_aura/desktop_factory_ozone.cc
new file mode 100644
index 0000000..e0a4489
--- /dev/null
+++ b/ui/views/widget/desktop_aura/desktop_factory_ozone.cc
@@ -0,0 +1,29 @@
+// 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 "ui/views/widget/desktop_aura/desktop_factory_ozone.h"
+
+#include "base/logging.h"
+
+namespace views {
+
+// static
+DesktopFactoryOzone* DesktopFactoryOzone::impl_ = NULL;
+
+DesktopFactoryOzone::DesktopFactoryOzone() {
+}
+
+DesktopFactoryOzone::~DesktopFactoryOzone() {
+}
+
+DesktopFactoryOzone* DesktopFactoryOzone::GetInstance() {
+  CHECK(impl_) << "DesktopFactoryOzone accessed before constructed";
+  return impl_;
+}
+
+void DesktopFactoryOzone::SetInstance(DesktopFactoryOzone* impl) {
+  impl_ = impl;
+}
+
+} // namespace views
diff --git a/ui/views/widget/desktop_aura/desktop_factory_ozone.h b/ui/views/widget/desktop_aura/desktop_factory_ozone.h
new file mode 100644
index 0000000..18cf36e
--- /dev/null
+++ b/ui/views/widget/desktop_aura/desktop_factory_ozone.h
@@ -0,0 +1,46 @@
+// 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 UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_FACTORY_OZONE_H_
+#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_FACTORY_OZONE_H_
+
+#include "ui/views/views_export.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace views {
+class DesktopNativeWidgetAura;
+class DesktopRootWindowHost;
+
+namespace internal {
+class NativeWidgetDelegate;
+}
+
+class VIEWS_EXPORT DesktopFactoryOzone {
+ public:
+  DesktopFactoryOzone();
+  virtual ~DesktopFactoryOzone();
+
+  // Returns the instance.
+  static DesktopFactoryOzone* GetInstance();
+
+  // Sets the implementation delegate. Ownership is retained by the caller.
+  static void SetInstance(DesktopFactoryOzone* impl);
+
+  // Delegates implementation of DesktopRootWindowHost::Create externally to
+  // Ozone implementation.
+  virtual DesktopRootWindowHost* CreateRootWindowHost(
+      internal::NativeWidgetDelegate* native_widget_delegate,
+      DesktopNativeWidgetAura* desktop_native_widget_aura,
+      const gfx::Rect& initial_bounds) = 0;
+
+ private:
+  static DesktopFactoryOzone* impl_; // not owned
+};
+
+}  // namespace views
+
+#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_FACTORY_OZONE_H_
diff --git a/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc b/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc
index a9b902d..eed97f3 100644
--- a/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc
+++ b/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc
@@ -68,6 +68,11 @@
   root_window_->OnCursorVisibilityChanged(visible);
 }
 
+void DesktopNativeCursorManager::SetCursorSet(
+    ui::CursorSetType cursor_set,
+    views::corewm::NativeCursorManagerDelegate* delegate) {
+  NOTIMPLEMENTED();
+}
 
 void DesktopNativeCursorManager::SetScale(
     float scale,
diff --git a/ui/views/widget/desktop_aura/desktop_native_cursor_manager.h b/ui/views/widget/desktop_aura/desktop_native_cursor_manager.h
index f6a800b..563131d 100644
--- a/ui/views/widget/desktop_aura/desktop_native_cursor_manager.h
+++ b/ui/views/widget/desktop_aura/desktop_native_cursor_manager.h
@@ -49,6 +49,9 @@
   virtual void SetVisibility(
       bool visible,
       views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE;
+  virtual void SetCursorSet(
+      ui::CursorSetType cursor_set,
+      views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE;
   virtual void SetScale(
       float scale,
       views::corewm::NativeCursorManagerDelegate* delegate) OVERRIDE;
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
index 859decc..cf06875 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -29,13 +29,13 @@
 #include "ui/views/corewm/input_method_event_filter.h"
 #include "ui/views/corewm/shadow_controller.h"
 #include "ui/views/corewm/shadow_types.h"
-#include "ui/views/corewm/tooltip_controller.h"
 #include "ui/views/corewm/visibility_controller.h"
 #include "ui/views/corewm/window_modality_controller.h"
 #include "ui/views/drag_utils.h"
 #include "ui/views/ime/input_method.h"
 #include "ui/views/ime/input_method_bridge.h"
 #include "ui/views/widget/desktop_aura/desktop_root_window_host.h"
+#include "ui/views/widget/desktop_aura/scoped_tooltip_client.h"
 #include "ui/views/widget/drop_helper.h"
 #include "ui/views/widget/native_widget_aura.h"
 #include "ui/views/widget/native_widget_aura_window_observer.h"
@@ -56,38 +56,41 @@
 
 namespace {
 
-// This class provides functionality to create a top level fullscreen widget to
-// host a child window.
-class DesktopNativeWidgetFullscreenHandler : public aura::WindowObserver {
+// This class provides functionality to create a top level widget to host a
+// child window.
+class DesktopNativeWidgetTopLevelHandler : public aura::WindowObserver {
  public:
-  // This function creates a full screen widget with the bounds passed in
-  // which eventually becomes the parent of the child window passed in.
+  // This function creates a widget with the bounds passed in which eventually
+  // becomes the parent of the child window passed in.
   static aura::Window* CreateParentWindow(aura::Window* child_window,
-                                          const gfx::Rect& bounds) {
-    // This instance will get deleted when the fullscreen widget is destroyed.
-    DesktopNativeWidgetFullscreenHandler* full_screen_handler =
-        new DesktopNativeWidgetFullscreenHandler;
+                                          const gfx::Rect& bounds,
+                                          bool full_screen) {
+    // This instance will get deleted when the widget is destroyed.
+    DesktopNativeWidgetTopLevelHandler* top_level_handler =
+        new DesktopNativeWidgetTopLevelHandler;
 
     child_window->SetBounds(gfx::Rect(bounds.size()));
 
     Widget::InitParams init_params;
-    init_params.type = Widget::InitParams::TYPE_WINDOW;
+    init_params.type = full_screen ? Widget::InitParams::TYPE_WINDOW :
+        Widget::InitParams::TYPE_POPUP;
     init_params.bounds = bounds;
     init_params.ownership = Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET;
     init_params.layer_type = ui::LAYER_NOT_DRAWN;
+    init_params.accept_events = full_screen;
 
-    // This widget instance will get deleted when the fullscreen window is
+    // This widget instance will get deleted when the window is
     // destroyed.
-    full_screen_handler->full_screen_widget_ = new Widget();
-    full_screen_handler->full_screen_widget_->Init(init_params);
+    top_level_handler->top_level_widget_ = new Widget();
+    top_level_handler->top_level_widget_->Init(init_params);
 
-    full_screen_handler->full_screen_widget_->SetFullscreen(true);
-    full_screen_handler->full_screen_widget_->Show();
+    top_level_handler->top_level_widget_->SetFullscreen(full_screen);
+    top_level_handler->top_level_widget_->Show();
 
     aura::Window* native_window =
-        full_screen_handler->full_screen_widget_->GetNativeView();
-    child_window->AddObserver(full_screen_handler);
-    native_window->AddObserver(full_screen_handler);
+        top_level_handler->top_level_widget_->GetNativeView();
+    child_window->AddObserver(top_level_handler);
+    native_window->AddObserver(top_level_handler);
     return native_window;
   }
 
@@ -97,33 +100,33 @@
 
     // If the widget is being destroyed by the OS then we should not try and
     // destroy it again.
-    if (full_screen_widget_ &&
-        window == full_screen_widget_->GetNativeView()) {
-      full_screen_widget_ = NULL;
+    if (top_level_widget_ &&
+        window == top_level_widget_->GetNativeView()) {
+      top_level_widget_ = NULL;
       return;
     }
 
-    if (full_screen_widget_) {
-      DCHECK(full_screen_widget_->GetNativeView());
-      full_screen_widget_->GetNativeView()->RemoveObserver(this);
-      // When we receive a notification that the child of the fullscreen window
-      // created above is being destroyed we go ahead and initiate the
-      // destruction of the corresponding widget.
-      full_screen_widget_->Close();
-      full_screen_widget_ = NULL;
+    if (top_level_widget_) {
+      DCHECK(top_level_widget_->GetNativeView());
+      top_level_widget_->GetNativeView()->RemoveObserver(this);
+      // When we receive a notification that the child of the window created
+      // above is being destroyed we go ahead and initiate the destruction of
+      // the corresponding widget.
+      top_level_widget_->Close();
+      top_level_widget_ = NULL;
     }
     delete this;
   }
 
  private:
-  DesktopNativeWidgetFullscreenHandler()
-      : full_screen_widget_(NULL) {}
+  DesktopNativeWidgetTopLevelHandler()
+      : top_level_widget_(NULL) {}
 
-  virtual ~DesktopNativeWidgetFullscreenHandler() {}
+  virtual ~DesktopNativeWidgetTopLevelHandler() {}
 
-  Widget* full_screen_widget_;
+  Widget* top_level_widget_;
 
-  DISALLOW_COPY_AND_ASSIGN(DesktopNativeWidgetFullscreenHandler);
+  DISALLOW_COPY_AND_ASSIGN(DesktopNativeWidgetTopLevelHandler);
 };
 
 class DesktopNativeWidgetAuraStackingClient :
@@ -141,10 +144,11 @@
   virtual aura::Window* GetDefaultParent(aura::Window* context,
                                          aura::Window* window,
                                          const gfx::Rect& bounds) OVERRIDE {
-    if (window->GetProperty(aura::client::kShowStateKey) ==
-            ui::SHOW_STATE_FULLSCREEN) {
-      return DesktopNativeWidgetFullscreenHandler::CreateParentWindow(window,
-                                                                      bounds);
+    bool full_screen = window->GetProperty(aura::client::kShowStateKey) ==
+        ui::SHOW_STATE_FULLSCREEN;
+    if (full_screen || window->type() == aura::client::WINDOW_TYPE_MENU) {
+      return DesktopNativeWidgetTopLevelHandler::CreateParentWindow(
+          window, bounds, full_screen);
     }
     return root_window_;
   }
@@ -256,11 +260,8 @@
   aura::client::SetDragDropDelegate(window_, this);
 
   tooltip_manager_.reset(new views::TooltipManagerAura(window_, GetWidget()));
-  tooltip_controller_.reset(
-      new corewm::TooltipController(gfx::SCREEN_TYPE_NATIVE));
-  aura::client::SetTooltipClient(root_window_.get(),
-                                 tooltip_controller_.get());
-  root_window_->AddPreTargetHandler(tooltip_controller_.get());
+
+  scoped_tooltip_client_.reset(new ScopedTooltipClient(root_window_.get()));
 
   if (params.opacity == Widget::InitParams::TRANSLUCENT_WINDOW) {
     visibility_controller_.reset(new views::corewm::VisibilityController);
@@ -716,11 +717,7 @@
   shadow_controller_.reset();
   // The DesktopRootWindowHost implementation sends OnNativeWidgetDestroying().
   tooltip_manager_.reset();
-  if (tooltip_controller_.get()) {
-    root_window_->RemovePreTargetHandler(tooltip_controller_.get());
-    tooltip_controller_.reset();
-    aura::client::SetTooltipClient(root_window_.get(), NULL);
-  }
+  scoped_tooltip_client_.reset();
   if (window_modality_controller_) {
     root_window_->RemovePreTargetHandler(window_modality_controller_.get());
     window_modality_controller_.reset();
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
index ce95139..1b25199 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
@@ -29,7 +29,6 @@
 class InputMethodEventFilter;
 class ScopedCaptureClient;
 class ShadowController;
-class TooltipController;
 class VisibilityController;
 class WindowModalityController;
 }
@@ -37,6 +36,7 @@
 class DesktopRootWindowHost;
 class DropHelper;
 class NativeWidgetAuraWindowObserver;
+class ScopedTooltipClient;
 class TooltipManagerAura;
 class WindowReorderer;
 
@@ -247,7 +247,7 @@
   scoped_ptr<DropHelper> drop_helper_;
   int last_drop_operation_;
 
-  scoped_ptr<corewm::TooltipController> tooltip_controller_;
+  scoped_ptr<ScopedTooltipClient> scoped_tooltip_client_;
   scoped_ptr<TooltipManagerAura> tooltip_manager_;
 
   scoped_ptr<views::corewm::VisibilityController> visibility_controller_;
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc
new file mode 100644
index 0000000..1342aba
--- /dev/null
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc
@@ -0,0 +1,27 @@
+// 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 "ui/aura/window.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
+#include "ui/views/widget/widget.h"
+
+namespace views {
+
+typedef ViewsTestBase DesktopNativeWidgetAuraTest;
+
+// Verifies creating a Widget with a parent that is not in a RootWindow doesn't
+// crash.
+TEST_F(DesktopNativeWidgetAuraTest, CreateWithParentNotInRootWindow) {
+  scoped_ptr<aura::Window> window(new aura::Window(NULL));
+  Widget widget;
+  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
+  params.bounds = gfx::Rect(0, 0, 200, 200);
+  params.parent = window.get();
+  params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+  params.native_widget = new DesktopNativeWidgetAura(&widget);
+  widget.Init(params);
+}
+
+}  // namespace views
diff --git a/ui/views/widget/desktop_aura/desktop_root_window_host_ozone.cc b/ui/views/widget/desktop_aura/desktop_root_window_host_ozone.cc
new file mode 100644
index 0000000..ff905a3
--- /dev/null
+++ b/ui/views/widget/desktop_aura/desktop_root_window_host_ozone.cc
@@ -0,0 +1,22 @@
+// 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 "ui/aura/root_window_host.h"
+#include "ui/views/widget/desktop_aura/desktop_root_window_host.h"
+#include "ui/views/widget/desktop_aura/desktop_factory_ozone.h"
+
+namespace views {
+
+DesktopRootWindowHost* DesktopRootWindowHost::Create(
+    internal::NativeWidgetDelegate* native_widget_delegate,
+    DesktopNativeWidgetAura* desktop_native_widget_aura,
+    const gfx::Rect& initial_bounds) {
+  DesktopFactoryOzone* d_factory = DesktopFactoryOzone::GetInstance();
+
+  return d_factory->CreateRootWindowHost(native_widget_delegate,
+                                         desktop_native_widget_aura,
+                                         initial_bounds);
+}
+
+}  // namespace views
diff --git a/ui/views/widget/desktop_aura/desktop_root_window_host_win.cc b/ui/views/widget/desktop_aura/desktop_root_window_host_win.cc
index 52b1b50..c2e2e88 100644
--- a/ui/views/widget/desktop_aura/desktop_root_window_host_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_root_window_host_win.cc
@@ -108,9 +108,8 @@
                         native_widget_delegate_);
 
   HWND parent_hwnd = NULL;
-  aura::Window* parent_window = params.parent;
-  if (parent_window)
-    parent_hwnd = parent_window->GetRootWindow()->GetAcceleratedWidget();
+  if (params.parent && params.parent->GetRootWindow())
+    parent_hwnd = params.parent->GetRootWindow()->GetAcceleratedWidget();
 
   message_handler_->set_remove_standard_frame(params.remove_standard_frame);
 
@@ -123,13 +122,7 @@
   rw_params.host = this;
   root_window_ = new aura::RootWindow(rw_params);
 
-  // TODO(beng): We probably need to move these two calls to some function that
-  //             can change depending on the native-ness of the frame. For right
-  //             now in the hack-n-slash days of win-aura, we can just
-  //             unilaterally turn this on.
-  root_window_->compositor()->SetHostHasTransparentBackground(true);
-  root_window_->SetTransparent(true);
-
+  SetWindowTransparency();
   root_window_->Init();
   root_window_->AddChild(content_window_);
 
@@ -345,6 +338,7 @@
 
 void DesktopRootWindowHostWin::FrameTypeChanged() {
   message_handler_->FrameTypeChanged();
+  SetWindowTransparency();
 }
 
 NonClientFrameView* DesktopRootWindowHostWin::CreateNonClientFrameView() {
@@ -354,6 +348,7 @@
 
 void DesktopRootWindowHostWin::SetFullscreen(bool fullscreen) {
   message_handler_->fullscreen_handler()->SetFullscreen(fullscreen);
+  SetWindowTransparency();
 }
 
 bool DesktopRootWindowHostWin::IsFullscreen() const {
@@ -413,6 +408,7 @@
 }
 
 void DesktopRootWindowHostWin::ToggleFullScreen() {
+  SetWindowTransparency();
 }
 
 // GetBounds and SetBounds work in pixel coordinates, whereas other get/set
@@ -864,6 +860,12 @@
   return message_handler_->hwnd();
 }
 
+void DesktopRootWindowHostWin::SetWindowTransparency() {
+  bool transparent = ShouldUseNativeFrame() && !IsFullscreen();
+  root_window_->compositor()->SetHostHasTransparentBackground(transparent);
+  root_window_->SetTransparent(transparent);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // DesktopRootWindowHost, public:
 
diff --git a/ui/views/widget/desktop_aura/desktop_root_window_host_win.h b/ui/views/widget/desktop_aura/desktop_root_window_host_win.h
index 59e54d8..028e608 100644
--- a/ui/views/widget/desktop_aura/desktop_root_window_host_win.h
+++ b/ui/views/widget/desktop_aura/desktop_root_window_host_win.h
@@ -207,6 +207,8 @@
   HWND GetHWND() const;
 
  private:
+  void SetWindowTransparency();
+
   // We are owned by the RootWindow, but we have to have a back pointer to it.
   aura::RootWindow* root_window_;
 
diff --git a/ui/views/widget/desktop_aura/desktop_screen_x11.cc b/ui/views/widget/desktop_aura/desktop_screen_x11.cc
index 77dbaa4..e581b83 100644
--- a/ui/views/widget/desktop_aura/desktop_screen_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_screen_x11.cc
@@ -38,8 +38,11 @@
   // Overridden from gfx::Screen:
   virtual bool IsDIPEnabled() OVERRIDE;
   virtual gfx::Point GetCursorScreenPoint() OVERRIDE;
-  virtual gfx::NativeWindow GetWindowAtCursorScreenPoint() OVERRIDE;
-  virtual int GetNumDisplays() OVERRIDE;
+  virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE;
+  virtual gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point)
+      OVERRIDE;
+  virtual int GetNumDisplays() const OVERRIDE;
+  virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE;
   virtual gfx::Display GetDisplayNearestWindow(
       gfx::NativeView window) const OVERRIDE;
   virtual gfx::Display GetDisplayNearestPoint(
@@ -89,18 +92,29 @@
   return gfx::Point(root_x, root_y);
 }
 
-gfx::NativeWindow DesktopScreenX11::GetWindowAtCursorScreenPoint() {
+gfx::NativeWindow DesktopScreenX11::GetWindowUnderCursor() {
   // TODO(erg): Implement using the discussion at
   // http://codereview.chromium.org/10279005/
   return NULL;
 }
 
-int DesktopScreenX11::GetNumDisplays() {
+gfx::NativeWindow DesktopScreenX11::GetWindowAtScreenPoint(
+    const gfx::Point& point) {
+  NOTIMPLEMENTED();
+  return NULL;
+}
+
+int DesktopScreenX11::GetNumDisplays() const {
   // TODO(erg): Figure this out with oshima or piman because I have no clue
   // about the XRandR implications here.
   return 1;
 }
 
+std::vector<gfx::Display> DesktopScreenX11::GetAllDisplays() const {
+  // TODO(erg): Do the right thing once we know what that is.
+  return std::vector<gfx::Display>(1, GetPrimaryDisplay());
+}
+
 gfx::Display DesktopScreenX11::GetDisplayNearestWindow(
     gfx::NativeView window) const {
   // TODO(erg): Do the right thing once we know what that is.
diff --git a/ui/views/widget/desktop_aura/scoped_tooltip_client.cc b/ui/views/widget/desktop_aura/scoped_tooltip_client.cc
new file mode 100644
index 0000000..cf13102
--- /dev/null
+++ b/ui/views/widget/desktop_aura/scoped_tooltip_client.cc
@@ -0,0 +1,37 @@
+// 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 "ui/views/widget/desktop_aura/scoped_tooltip_client.h"
+
+#include "ui/aura/root_window.h"
+#include "ui/views/corewm/tooltip_controller.h"
+
+namespace views {
+
+// static
+corewm::TooltipController* ScopedTooltipClient::tooltip_controller_ = NULL;
+
+// static
+int ScopedTooltipClient::scoped_tooltip_client_count_ = 0;
+
+ScopedTooltipClient::ScopedTooltipClient(aura::RootWindow* root_window)
+    : root_window_(root_window) {
+  if (scoped_tooltip_client_count_++ == 0) {
+    tooltip_controller_ =
+        new corewm::TooltipController(gfx::SCREEN_TYPE_NATIVE);
+  }
+  aura::client::SetTooltipClient(root_window_, tooltip_controller_);
+  root_window_->AddPreTargetHandler(tooltip_controller_);
+}
+
+ScopedTooltipClient::~ScopedTooltipClient() {
+  root_window_->RemovePreTargetHandler(tooltip_controller_);
+  aura::client::SetTooltipClient(root_window_, NULL);
+  if (--scoped_tooltip_client_count_ == 0) {
+    delete tooltip_controller_;
+    tooltip_controller_ = NULL;
+  }
+}
+
+}  // namespace views
diff --git a/ui/views/widget/desktop_aura/scoped_tooltip_client.h b/ui/views/widget/desktop_aura/scoped_tooltip_client.h
new file mode 100644
index 0000000..bb3e57b
--- /dev/null
+++ b/ui/views/widget/desktop_aura/scoped_tooltip_client.h
@@ -0,0 +1,43 @@
+// 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 UI_VIEWS_WIDGET_DESKTOP_AURA_SCOPED_TOOLTIP_CLIENT_H_
+#define UI_VIEWS_WIDGET_DESKTOP_AURA_SCOPED_TOOLTIP_CLIENT_H_
+
+#include "base/basictypes.h"
+
+namespace aura {
+class RootWindow;
+}
+
+namespace views {
+
+namespace corewm {
+class TooltipController;
+}
+
+// ScopedTooltipClient is responsible for installing a TooltipClient
+// implementation on a RootWindow. Additionally it ensures only one
+// TooltipController is only ever created. In this way all
+// DesktopNativeWidgetAuras share the same TooltipClient.
+class ScopedTooltipClient {
+ public:
+  explicit ScopedTooltipClient(aura::RootWindow* root_window);
+  ~ScopedTooltipClient();
+
+ private:
+  // Single TooltipController.
+  static corewm::TooltipController* tooltip_controller_;
+
+  // Number of ScopedTooltipClients created.
+  static int scoped_tooltip_client_count_;
+
+  aura::RootWindow* root_window_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedTooltipClient);
+};
+
+}  // namespace views
+
+#endif  // UI_VIEWS_WIDGET_DESKTOP_AURA_SCOPED_TOOLTIP_CLIENT_H_
diff --git a/ui/views/widget/native_widget_aura.cc b/ui/views/widget/native_widget_aura.cc
index 5348f24..f9ebed7 100644
--- a/ui/views/widget/native_widget_aura.cc
+++ b/ui/views/widget/native_widget_aura.cc
@@ -1089,7 +1089,7 @@
   // from their previous parent.
   for (Widget::Widgets::iterator it = widgets.begin();
       it != widgets.end(); ++it) {
-    (*it)->NotifyNativeViewHierarchyChanged(false, previous_parent);
+    (*it)->NotifyNativeViewHierarchyWillChange();
   }
 
   if (new_parent) {
@@ -1114,7 +1114,7 @@
   // And now, notify them that they have a brand new parent.
   for (Widget::Widgets::iterator it = widgets.begin();
       it != widgets.end(); ++it) {
-    (*it)->NotifyNativeViewHierarchyChanged(true, new_parent);
+    (*it)->NotifyNativeViewHierarchyChanged();
   }
 }
 
diff --git a/ui/views/widget/native_widget_win.cc b/ui/views/widget/native_widget_win.cc
index c0b84b6..472ff9c 100644
--- a/ui/views/widget/native_widget_win.cc
+++ b/ui/views/widget/native_widget_win.cc
@@ -1009,9 +1009,7 @@
   // from their previous parent.
   for (Widget::Widgets::iterator it = widgets.begin();
        it != widgets.end(); ++it) {
-    // TODO(beng): Rename this notification to NotifyNativeViewChanging()
-    // and eliminate the bool parameter.
-    (*it)->NotifyNativeViewHierarchyChanged(false, previous_parent);
+    (*it)->NotifyNativeViewHierarchyWillChange();
   }
 
   ::SetParent(native_view, new_parent);
@@ -1019,7 +1017,7 @@
   // And now, notify them that they have a brand new parent.
   for (Widget::Widgets::iterator it = widgets.begin();
        it != widgets.end(); ++it) {
-    (*it)->NotifyNativeViewHierarchyChanged(true, new_parent);
+    (*it)->NotifyNativeViewHierarchyChanged();
   }
 }
 
diff --git a/ui/views/widget/root_view.cc b/ui/views/widget/root_view.cc
index b304c52..efdca92 100644
--- a/ui/views/widget/root_view.cc
+++ b/ui/views/widget/root_view.cc
@@ -102,9 +102,8 @@
   return child_count() > 0 ? child_at(0) : NULL;
 }
 
-void RootView::NotifyNativeViewHierarchyChanged(bool attached,
-                                                gfx::NativeView native_view) {
-  PropagateNativeViewHierarchyChanged(attached, native_view, this);
+void RootView::NotifyNativeViewHierarchyChanged() {
+  PropagateNativeViewHierarchyChanged();
 }
 
 // Input -----------------------------------------------------------------------
@@ -123,8 +122,7 @@
     return;
   }
 
-  for (; v && v != this && !event->handled(); v = v->parent())
-    DispatchEventToTarget(v, event);
+  DispatchKeyEventStartAt(v, event);
 }
 
 void RootView::DispatchScrollEvent(ui::ScrollEvent* event) {
@@ -699,6 +697,19 @@
   }
 }
 
+
+void RootView::DispatchKeyEventStartAt(View* view, ui::KeyEvent* event) {
+  if (event->handled() || !view)
+    return;
+
+  for (; view && view != this; view = view->parent()) {
+    DispatchEventToTarget(view, event);
+    // Do this check here rather than in the if as |view| may have been deleted.
+    if (event->handled())
+      return;
+  }
+}
+
 bool RootView::CanDispatchToTarget(ui::EventTarget* target) {
   return event_dispatch_target_ == target;
 }
diff --git a/ui/views/widget/root_view.h b/ui/views/widget/root_view.h
index b89ab8f..5d206b2 100644
--- a/ui/views/widget/root_view.h
+++ b/ui/views/widget/root_view.h
@@ -16,6 +16,7 @@
 namespace views {
 
 namespace test {
+class RootViewTestHelper;
 class WidgetTest;
 }
 
@@ -60,8 +61,7 @@
   View* GetContentsView();
 
   // Called when parent of the host changed.
-  void NotifyNativeViewHierarchyChanged(bool attached,
-                                        gfx::NativeView native_view);
+  void NotifyNativeViewHierarchyChanged();
 
   // Input ---------------------------------------------------------------------
 
@@ -127,6 +127,7 @@
  private:
   friend class ::views::View;
   friend class ::views::Widget;
+  friend class ::views::test::RootViewTestHelper;
   friend class ::views::test::WidgetTest;
 
   // Input ---------------------------------------------------------------------
@@ -153,6 +154,10 @@
                                    View* view,
                                    View* sibling);
 
+  // Dispatches the KeyEvent to |view| and ancestors until the event is
+  // handled.
+  void DispatchKeyEventStartAt(View* view, ui::KeyEvent* event);
+
   // Overridden from ui::EventDispatcherDelegate:
   virtual bool CanDispatchToTarget(ui::EventTarget* target) OVERRIDE;
 
diff --git a/ui/views/widget/root_view_test_helper.h b/ui/views/widget/root_view_test_helper.h
new file mode 100644
index 0000000..a263967
--- /dev/null
+++ b/ui/views/widget/root_view_test_helper.h
@@ -0,0 +1,33 @@
+// 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 UI_VIEWS_WIDGET_ROOT_VIEW_TEST_HELPER_H_
+#define UI_VIEWS_WIDGET_ROOT_VIEW_TEST_HELPER_H_
+
+#include "ui/views/widget/root_view.h"
+
+namespace views {
+namespace test {
+
+class RootViewTestHelper {
+ public:
+  explicit RootViewTestHelper(internal::RootView* root_view)
+      : root_view_(root_view) {
+  }
+  ~RootViewTestHelper() {}
+
+  void DispatchKeyEventStartAt(View* view, ui::KeyEvent* event) {
+    root_view_->DispatchKeyEventStartAt(view, event);
+  }
+
+ private:
+  internal::RootView* root_view_;
+
+  DISALLOW_COPY_AND_ASSIGN(RootViewTestHelper);
+};
+
+}  // namespace test
+}  // namespace views
+
+#endif  // UI_VIEWS_WIDGET_ROOT_VIEW_TEST_HELPER_H_
diff --git a/ui/views/widget/root_view_unittest.cc b/ui/views/widget/root_view_unittest.cc
new file mode 100644
index 0000000..ebe20d6
--- /dev/null
+++ b/ui/views/widget/root_view_unittest.cc
@@ -0,0 +1,55 @@
+// 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 "ui/views/test/views_test_base.h"
+#include "ui/views/widget/root_view_test_helper.h"
+
+namespace views {
+namespace test {
+
+typedef ViewsTestBase RootViewTest;
+
+class DeleteOnKeyEventView : public View {
+ public:
+  explicit DeleteOnKeyEventView(bool* set_on_key) : set_on_key_(set_on_key) {}
+  virtual ~DeleteOnKeyEventView() {}
+
+  virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE {
+    *set_on_key_ = true;
+    delete this;
+    return true;
+  }
+
+ private:
+  // Set to true in OnKeyPressed().
+  bool* set_on_key_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeleteOnKeyEventView);
+};
+
+// Verifies deleting a View in OnKeyPressed() doesn't crash.
+TEST_F(RootViewTest, DeleteViewDuringKeyEventDispatch) {
+  Widget widget;
+  Widget::InitParams init_params =
+      CreateParams(Widget::InitParams::TYPE_POPUP);
+  init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+  widget.Init(init_params);
+
+  bool got_key_event = false;
+
+  View* content = new View;
+  widget.SetContentsView(content);
+
+  View* child = new DeleteOnKeyEventView(&got_key_event);
+  content->AddChildView(child);
+
+  ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_ESCAPE, 0, false);
+  RootViewTestHelper test_helper(
+      static_cast<internal::RootView*>(widget.GetRootView()));
+  test_helper.DispatchKeyEventStartAt(child, &key_event);
+  EXPECT_TRUE(got_key_event);
+}
+
+}  // namespace test
+}  // namespace views
diff --git a/ui/views/widget/tooltip_manager.cc b/ui/views/widget/tooltip_manager.cc
index f600a12..af51f0d 100644
--- a/ui/views/widget/tooltip_manager.cc
+++ b/ui/views/widget/tooltip_manager.cc
@@ -9,9 +9,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/base/text/text_elider.h"
-#include "ui/gfx/font.h"
-
-namespace {
+#include "ui/gfx/text_utils.h"
 
 // Maximum number of characters we allow in a tooltip.
 const size_t kMaxTooltipLength = 1024;
@@ -19,8 +17,6 @@
 // Maximum number of lines we allow in the tooltip.
 const size_t kMaxLines = 6;
 
-}  // namespace
-
 namespace views {
 
 // static
@@ -49,13 +45,14 @@
   *line_count = static_cast<int>(lines.size());
 
   // Format each line to fit.
-  gfx::Font font = GetDefaultFont();
+  const gfx::FontList& font_list = GetDefaultFontList();
   string16 result;
   for (std::vector<string16>::iterator i = lines.begin(); i != lines.end();
        ++i) {
     string16 elided_text =
-        ui::ElideText(*i, font, available_width, ui::ELIDE_AT_END);
-    *max_width = std::max(*max_width, font.GetStringWidth(elided_text));
+        ui::ElideText(*i, font_list, available_width, ui::ELIDE_AT_END);
+    *max_width = std::max(*max_width,
+                          gfx::GetStringWidth(elided_text, font_list));
     if (!result.empty())
       result.push_back('\n');
     result.append(elided_text);
diff --git a/ui/views/widget/tooltip_manager.h b/ui/views/widget/tooltip_manager.h
index f8601dd..1228f5a 100644
--- a/ui/views/widget/tooltip_manager.h
+++ b/ui/views/widget/tooltip_manager.h
@@ -13,7 +13,7 @@
 #include "ui/views/views_export.h"
 
 namespace gfx {
-class Font;
+class FontList;
 }  // namespace gfx
 
 namespace views {
@@ -30,7 +30,7 @@
   static int GetTooltipHeight();
 
   // Returns the default font used by tooltips.
-  static gfx::Font GetDefaultFont();
+  static const gfx::FontList& GetDefaultFontList();
 
   // Returns the maximum width of the tooltip. |x| and |y| give the location
   // the tooltip is to be displayed on in screen coordinates. |context| is
diff --git a/ui/views/widget/tooltip_manager_aura.cc b/ui/views/widget/tooltip_manager_aura.cc
index f7a4761..6329463 100644
--- a/ui/views/widget/tooltip_manager_aura.cc
+++ b/ui/views/widget/tooltip_manager_aura.cc
@@ -8,7 +8,6 @@
 #include "ui/aura/client/tooltip_client.h"
 #include "ui/aura/root_window.h"
 #include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/font.h"
 #include "ui/gfx/rect.h"
 #include "ui/gfx/screen.h"
 #include "ui/views/widget/widget.h"
@@ -23,8 +22,8 @@
 }
 
 // static
-gfx::Font TooltipManager::GetDefaultFont() {
-  return ui::ResourceBundle::GetSharedInstance().GetFont(
+const gfx::FontList& TooltipManager::GetDefaultFontList() {
+  return ui::ResourceBundle::GetSharedInstance().GetFontList(
       ui::ResourceBundle::BaseFont);
 }
 
diff --git a/ui/views/widget/tooltip_manager_win.cc b/ui/views/widget/tooltip_manager_win.cc
index fa0ad45..803fa15 100644
--- a/ui/views/widget/tooltip_manager_win.cc
+++ b/ui/views/widget/tooltip_manager_win.cc
@@ -19,7 +19,7 @@
 #include "ui/base/win/dpi.h"
 #include "ui/base/win/hwnd_util.h"
 #include "ui/base/win/scoped_set_map_mode.h"
-#include "ui/gfx/font.h"
+#include "ui/gfx/font_list.h"
 #include "ui/gfx/screen.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/monitor_win.h"
@@ -52,11 +52,11 @@
 }
 
 // static
-gfx::Font TooltipManager::GetDefaultFont() {
-  static gfx::Font* font = NULL;
-  if (!font)
-    font = new gfx::Font(DetermineDefaultFont());
-  return *font;
+const gfx::FontList& TooltipManager::GetDefaultFontList() {
+  static gfx::FontList* font_list = NULL;
+  if (!font_list)
+    font_list = new gfx::FontList(DetermineDefaultFont());
+  return *font_list;
 }
 
 // static
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
index 0de8ccf..4bf2a85 100644
--- a/ui/views/widget/widget.cc
+++ b/ui/views/widget/widget.cc
@@ -427,16 +427,16 @@
   }
 }
 
-void Widget::NotifyNativeViewHierarchyChanged(bool attached,
-                                              gfx::NativeView native_view) {
-  if (!attached) {
-    FocusManager* focus_manager = GetFocusManager();
-    // We are being removed from a window hierarchy.  Treat this as
-    // the root_view_ being removed.
-    if (focus_manager)
-      focus_manager->ViewRemoved(root_view_.get());
-  }
-  root_view_->NotifyNativeViewHierarchyChanged(attached, native_view);
+void Widget::NotifyNativeViewHierarchyWillChange() {
+  FocusManager* focus_manager = GetFocusManager();
+  // We are being removed from a window hierarchy.  Treat this as
+  // the root_view_ being removed.
+  if (focus_manager)
+    focus_manager->ViewRemoved(root_view_.get());
+}
+
+void Widget::NotifyNativeViewHierarchyChanged() {
+  root_view_->NotifyNativeViewHierarchyChanged();
 }
 
 // Converted methods (see header) ----------------------------------------------
diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h
index ed64f3b..e6067eb 100644
--- a/ui/views/widget/widget.h
+++ b/ui/views/widget/widget.h
@@ -331,9 +331,13 @@
   // Forwarded from the RootView so that the widget can do any cleanup.
   void ViewHierarchyChanged(const View::ViewHierarchyChangedDetails& details);
 
-  // Performs any necessary cleanup and forwards to RootView.
-  void NotifyNativeViewHierarchyChanged(bool attached,
-                                        gfx::NativeView native_view);
+  // Called right before changing the widget's parent NativeView to do any
+  // cleanup.
+  void NotifyNativeViewHierarchyWillChange();
+
+  // Called after changing the widget's parent NativeView. Notifies the RootView
+  // about the change.
+  void NotifyNativeViewHierarchyChanged();
 
   // Returns the top level widget in a hierarchy (see is_top_level() for
   // the definition of top level widget.) Will return NULL if called
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc
index e00f93d..d21d64f 100644
--- a/ui/views/widget/widget_unittest.cc
+++ b/ui/views/widget/widget_unittest.cc
@@ -1541,27 +1541,28 @@
   widget.Close();
 }
 
-// This class provides functionality to test whether the destruction of full
-// screen child windows occurs correctly in desktop AURA without crashing.
+// This class provides functionality to create fullscreen and top level popup
+// windows. It additionally tests whether the destruction of these windows
+// occurs correctly in desktop AURA without crashing.
 // It provides facilities to test the following cases:-
 // 1. Child window destroyed which should lead to the destruction of the
 //    parent.
 // 2. Parent window destroyed which should lead to the child being destroyed.
-class DesktopAuraFullscreenChildWindowDestructionTest
+class DesktopAuraTopLevelWindowTest
     : public views::TestViewsDelegate,
       public aura::WindowObserver {
  public:
-  DesktopAuraFullscreenChildWindowDestructionTest()
-      : full_screen_widget_(NULL),
-        child_window_(NULL),
-        parent_destroyed_(false),
-        child_destroyed_(false) {}
+  DesktopAuraTopLevelWindowTest()
+      : top_level_widget_(NULL),
+        owned_window_(NULL),
+        owner_destroyed_(false),
+        owned_window_destroyed_(false) {}
 
-  virtual ~DesktopAuraFullscreenChildWindowDestructionTest() {
-    EXPECT_TRUE(parent_destroyed_);
-    EXPECT_TRUE(child_destroyed_);
-    full_screen_widget_ = NULL;
-    child_window_ = NULL;
+  virtual ~DesktopAuraTopLevelWindowTest() {
+    EXPECT_TRUE(owner_destroyed_);
+    EXPECT_TRUE(owned_window_destroyed_);
+    top_level_widget_ = NULL;
+    owned_window_ = NULL;
   }
 
   // views::TestViewsDelegate overrides.
@@ -1572,50 +1573,55 @@
       params->native_widget = new views::DesktopNativeWidgetAura(delegate);
   }
 
-  void CreateFullscreenChildWindow(const gfx::Rect& bounds) {
+  void CreateTopLevelWindow(const gfx::Rect& bounds, bool fullscreen) {
     Widget::InitParams init_params;
     init_params.type = Widget::InitParams::TYPE_WINDOW;
     init_params.bounds = bounds;
     init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
     init_params.layer_type = ui::LAYER_NOT_DRAWN;
+    init_params.accept_events = fullscreen;
 
     widget_.Init(init_params);
 
-    child_window_ = new aura::Window(&child_window_delegate_);
-    child_window_->SetType(aura::client::WINDOW_TYPE_NORMAL);
-    child_window_->Init(ui::LAYER_TEXTURED);
-    child_window_->SetName("TestFullscreenChildWindow");
-    child_window_->SetProperty(aura::client::kShowStateKey,
-                              ui::SHOW_STATE_FULLSCREEN);
-    child_window_->SetDefaultParentByRootWindow(
+    owned_window_ = new aura::Window(&child_window_delegate_);
+    owned_window_->SetType(aura::client::WINDOW_TYPE_NORMAL);
+    owned_window_->SetName("TestTopLevelWindow");
+    if (fullscreen) {
+      owned_window_->SetProperty(aura::client::kShowStateKey,
+                                 ui::SHOW_STATE_FULLSCREEN);
+    } else {
+      owned_window_->SetType(aura::client::WINDOW_TYPE_MENU);
+    }
+    owned_window_->Init(ui::LAYER_TEXTURED);
+    owned_window_->SetDefaultParentByRootWindow(
         widget_.GetNativeView()->GetRootWindow(), gfx::Rect(0, 0, 1900, 1600));
-    child_window_->Show();
-    child_window_->AddObserver(this);
+    owned_window_->Show();
+    owned_window_->AddObserver(this);
 
-    ASSERT_TRUE(child_window_->parent() != NULL);
-    child_window_->parent()->AddObserver(this);
+    ASSERT_TRUE(owned_window_->parent() != NULL);
+    owned_window_->parent()->AddObserver(this);
 
-    full_screen_widget_ =
-        views::Widget::GetWidgetForNativeView(child_window_->parent());
-    ASSERT_TRUE(full_screen_widget_ != NULL);
+    top_level_widget_ =
+        views::Widget::GetWidgetForNativeView(owned_window_->parent());
+    ASSERT_TRUE(top_level_widget_ != NULL);
   }
 
-  void DestroyChildWindow() {
-    ASSERT_TRUE(child_window_ != NULL);
-    delete child_window_;
+  void DestroyOwnedWindow() {
+    ASSERT_TRUE(owned_window_ != NULL);
+    delete owned_window_;
   }
 
-  void DestroyParentWindow() {
-    ASSERT_TRUE(full_screen_widget_ != NULL);
-    full_screen_widget_->CloseNow();
+  void DestroyOwnerWindow() {
+    ASSERT_TRUE(top_level_widget_ != NULL);
+    top_level_widget_->CloseNow();
   }
 
   virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
     window->RemoveObserver(this);
-    if (window == child_window_) {
-      child_destroyed_ = true;
-    } else if (window == full_screen_widget_->GetNativeView()) {
-      parent_destroyed_ = true;
+    if (window == owned_window_) {
+      owned_window_destroyed_ = true;
+    } else if (window == top_level_widget_->GetNativeView()) {
+      owner_destroyed_ = true;
     } else {
       ADD_FAILURE() << "Unexpected window destroyed callback: " << window;
     }
@@ -1623,35 +1629,46 @@
 
  private:
   views::Widget widget_;
-  views::Widget* full_screen_widget_;
-  aura::Window* child_window_;
-  bool parent_destroyed_;
-  bool child_destroyed_;
+  views::Widget* top_level_widget_;
+  aura::Window* owned_window_;
+  bool owner_destroyed_;
+  bool owned_window_destroyed_;
   aura::test::TestWindowDelegate child_window_delegate_;
 
-  DISALLOW_COPY_AND_ASSIGN(DesktopAuraFullscreenChildWindowDestructionTest);
+  DISALLOW_COPY_AND_ASSIGN(DesktopAuraTopLevelWindowTest);
 };
 
-TEST_F(WidgetTest, DesktopAuraFullscreenChildDestroyedBeforeParentTest) {
+TEST_F(WidgetTest, DesktopAuraFullscreenWindowDestroyedBeforeOwnerTest) {
   ViewsDelegate::views_delegate = NULL;
-  DesktopAuraFullscreenChildWindowDestructionTest full_screen_child_test;
-  ASSERT_NO_FATAL_FAILURE(full_screen_child_test.CreateFullscreenChildWindow(
-      gfx::Rect(0, 0, 200, 200)));
+  DesktopAuraTopLevelWindowTest fullscreen_window;
+  ASSERT_NO_FATAL_FAILURE(fullscreen_window.CreateTopLevelWindow(
+      gfx::Rect(0, 0, 200, 200), true));
 
   RunPendingMessages();
-  ASSERT_NO_FATAL_FAILURE(full_screen_child_test.DestroyChildWindow());
+  ASSERT_NO_FATAL_FAILURE(fullscreen_window.DestroyOwnedWindow());
   RunPendingMessages();
 }
 
-TEST_F(WidgetTest, DesktopAuraFullscreenChildParentDestroyed) {
+TEST_F(WidgetTest, DesktopAuraFullscreenWindowOwnerDestroyed) {
   ViewsDelegate::views_delegate = NULL;
 
-  DesktopAuraFullscreenChildWindowDestructionTest full_screen_child_test;
-  ASSERT_NO_FATAL_FAILURE(full_screen_child_test.CreateFullscreenChildWindow(
-      gfx::Rect(0, 0, 200, 200)));
+  DesktopAuraTopLevelWindowTest fullscreen_window;
+  ASSERT_NO_FATAL_FAILURE(fullscreen_window.CreateTopLevelWindow(
+      gfx::Rect(0, 0, 200, 200), true));
 
   RunPendingMessages();
-  ASSERT_NO_FATAL_FAILURE(full_screen_child_test.DestroyParentWindow());
+  ASSERT_NO_FATAL_FAILURE(fullscreen_window.DestroyOwnerWindow());
+  RunPendingMessages();
+}
+
+TEST_F(WidgetTest, DesktopAuraTopLevelOwnedPopupTest) {
+  ViewsDelegate::views_delegate = NULL;
+  DesktopAuraTopLevelWindowTest popup_window;
+  ASSERT_NO_FATAL_FAILURE(popup_window.CreateTopLevelWindow(
+      gfx::Rect(0, 0, 200, 200), false));
+
+  RunPendingMessages();
+  ASSERT_NO_FATAL_FAILURE(popup_window.DestroyOwnedWindow());
   RunPendingMessages();
 }
 
@@ -1678,6 +1695,116 @@
   EXPECT_TRUE(widget.GetNativeView()->IsVisible());
 }
 
+// The following code verifies we can correctly destroy a Widget from a mouse
+// enter/exit. We could test move/drag/enter/exit but in general we don't run
+// nested message loops from such events, nor has the code ever really dealt
+// with this situation.
+
+// Class that closes the widget (which ends up deleting it immediately) when the
+// appropriate event is received.
+class CloseWidgetView : public View {
+ public:
+  explicit CloseWidgetView(ui::EventType event_type)
+      : event_type_(event_type) {
+  }
+
+  // View overrides:
+  virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
+    if (!CloseWidget(event))
+      View::OnMousePressed(event);
+    return true;
+  }
+  virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE {
+    if (!CloseWidget(event))
+      View::OnMouseDragged(event);
+    return true;
+  }
+  virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE {
+    if (!CloseWidget(event))
+      View::OnMouseReleased(event);
+  }
+  virtual void OnMouseMoved(const ui::MouseEvent& event) OVERRIDE {
+    if (!CloseWidget(event))
+      View::OnMouseMoved(event);
+  }
+  virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE {
+    if (!CloseWidget(event))
+      View::OnMouseEntered(event);
+  }
+
+ private:
+  bool CloseWidget(const ui::LocatedEvent& event) {
+    if (event.type() == event_type_) {
+      // Go through NativeWidgetPrivate to simulate what happens if the OS
+      // deletes the NativeWindow out from under us.
+      GetWidget()->native_widget_private()->CloseNow();
+      return true;
+    }
+    return false;
+  }
+
+  const ui::EventType event_type_;
+
+  DISALLOW_COPY_AND_ASSIGN(CloseWidgetView);
+};
+
+// Generates two moves (first generates enter, second real move), a press, drag
+// and release stopping at |last_event_type|.
+void GenerateMouseEvents(Widget* widget, ui::EventType last_event_type) {
+  const gfx::Rect screen_bounds(widget->GetWindowBoundsInScreen());
+  ui::MouseEvent move_event(ui::ET_MOUSE_MOVED, screen_bounds.CenterPoint(),
+                            screen_bounds.CenterPoint(), 0);
+  aura::RootWindowHostDelegate* rwhd =
+      widget->GetNativeWindow()->GetRootWindow()->AsRootWindowHostDelegate();
+  rwhd->OnHostMouseEvent(&move_event);
+  if (last_event_type == ui::ET_MOUSE_ENTERED)
+    return;
+  rwhd->OnHostMouseEvent(&move_event);
+  if (last_event_type == ui::ET_MOUSE_MOVED)
+    return;
+
+  ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, screen_bounds.CenterPoint(),
+                             screen_bounds.CenterPoint(), 0);
+  rwhd->OnHostMouseEvent(&press_event);
+  if (last_event_type == ui::ET_MOUSE_PRESSED)
+    return;
+
+  gfx::Point end_point(screen_bounds.CenterPoint());
+  end_point.Offset(1, 1);
+  ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, end_point, end_point, 0);
+  rwhd->OnHostMouseEvent(&drag_event);
+  if (last_event_type == ui::ET_MOUSE_DRAGGED)
+    return;
+
+  ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, end_point, end_point, 0);
+  rwhd->OnHostMouseEvent(&release_event);
+}
+
+// Creates a widget and invokes GenerateMouseEvents() with |last_event_type|.
+void RunCloseWidgetDuringDispatchTest(WidgetTest* test,
+                                      ui::EventType last_event_type) {
+  // |widget| is deleted by CloseWidgetView.
+  Widget* widget = new Widget;
+  Widget::InitParams params =
+      test->CreateParams(Widget::InitParams::TYPE_POPUP);
+  params.native_widget = new DesktopNativeWidgetAura(widget);
+  params.bounds = gfx::Rect(0, 0, 50, 100);
+  widget->Init(params);
+  widget->SetContentsView(new CloseWidgetView(last_event_type));
+  widget->Show();
+  GenerateMouseEvents(widget, last_event_type);
+}
+
+// Verifies deleting the widget from a mouse pressed event doesn't crash.
+TEST_F(WidgetTest, CloseWidgetDuringMousePress) {
+  RunCloseWidgetDuringDispatchTest(this, ui::ET_MOUSE_PRESSED);
+}
+
+// Verifies deleting the widget from a mouse released event doesn't crash.
+TEST_F(WidgetTest, CloseWidgetDuringMouseReleased) {
+  RunCloseWidgetDuringDispatchTest(this, ui::ET_MOUSE_RELEASED);
+}
+
 #endif  // !defined(OS_CHROMEOS)
 
 // Tests that wheel events generated from scroll events are targetted to the
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index d17c841..6af4479 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -2068,9 +2068,9 @@
       if (touch_event_type != ui::ET_UNKNOWN) {
         POINT point;
         point.x = TOUCH_COORD_TO_PIXEL(input[i].x) /
-            ui::win::GetUndocumentedDPIScale();
+            ui::win::GetUndocumentedDPITouchScale();
         point.y = TOUCH_COORD_TO_PIXEL(input[i].y) /
-            ui::win::GetUndocumentedDPIScale();
+            ui::win::GetUndocumentedDPITouchScale();
 
         ScreenToClient(hwnd(), &point);
 
diff --git a/ui/views/win/hwnd_message_handler.h b/ui/views/win/hwnd_message_handler.h
index 8ae4619..80ad4aa 100644
--- a/ui/views/win/hwnd_message_handler.h
+++ b/ui/views/win/hwnd_message_handler.h
@@ -290,8 +290,8 @@
     MSG_WM_CREATE(OnCreate)
     MSG_WM_DESTROY(OnDestroy)
     MSG_WM_DISPLAYCHANGE(OnDisplayChange)
-    MSG_WM_ERASEBKGND(OnEraseBkgnd)
     MSG_WM_ENTERSIZEMOVE(OnEnterSizeMove)
+    MSG_WM_ERASEBKGND(OnEraseBkgnd)
     MSG_WM_EXITSIZEMOVE(OnExitSizeMove)
     MSG_WM_GETMINMAXINFO(OnGetMinMaxInfo)
     MSG_WM_INITMENU(OnInitMenu)
@@ -311,8 +311,8 @@
     MSG_WM_SIZE(OnSize)
     MSG_WM_SYSCOMMAND(OnSysCommand)
     MSG_WM_THEMECHANGED(OnThemeChanged)
-    MSG_WM_WINDOWPOSCHANGING(OnWindowPosChanging)
     MSG_WM_WINDOWPOSCHANGED(OnWindowPosChanged)
+    MSG_WM_WINDOWPOSCHANGING(OnWindowPosChanging)
   END_MSG_MAP()
 
   // Message Handlers.
diff --git a/ui/views/window/dialog_client_view.cc b/ui/views/window/dialog_client_view.cc
index d592292..9ca9227 100644
--- a/ui/views/window/dialog_client_view.cc
+++ b/ui/views/window/dialog_client_view.cc
@@ -283,6 +283,17 @@
   }
 }
 
+void DialogClientView::NativeViewHierarchyChanged() {
+  FocusManager* focus_manager = GetFocusManager();
+  if (focus_manager_ != focus_manager) {
+    if (focus_manager_)
+      focus_manager_->RemoveFocusChangeListener(this);
+    focus_manager_ = focus_manager;
+    if (focus_manager_)
+      focus_manager_->AddFocusChangeListener(this);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // DialogClientView, ButtonListener implementation:
 
diff --git a/ui/views/window/dialog_client_view.h b/ui/views/window/dialog_client_view.h
index 448eae7..d780806 100644
--- a/ui/views/window/dialog_client_view.h
+++ b/ui/views/window/dialog_client_view.h
@@ -62,6 +62,7 @@
   virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE;
   virtual void ViewHierarchyChanged(
       const ViewHierarchyChangedDetails& details) OVERRIDE;
+  virtual void NativeViewHierarchyChanged() OVERRIDE;
 
   // ButtonListener implementation:
   virtual void ButtonPressed(Button* sender, const ui::Event& event) OVERRIDE;
@@ -84,6 +85,8 @@
   virtual void ChildVisibilityChanged(View* child) OVERRIDE;
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(DialogClientViewTest, FocusManager);
+
   bool has_dialog_buttons() const { return ok_button_ || cancel_button_; }
 
   // Create a dialog button of the appropriate type.
diff --git a/ui/views/window/dialog_client_view_unittest.cc b/ui/views/window/dialog_client_view_unittest.cc
index 9f8c8c4..28b001f 100644
--- a/ui/views/window/dialog_client_view_unittest.cc
+++ b/ui/views/window/dialog_client_view_unittest.cc
@@ -4,10 +4,11 @@
 
 #include "base/basictypes.h"
 #include "base/strings/utf_string_conversions.h"
-#include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/test/test_views.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/widget/widget.h"
 #include "ui/views/window/dialog_client_view.h"
 #include "ui/views/window/dialog_delegate.h"
 
@@ -37,7 +38,7 @@
   DISALLOW_COPY_AND_ASSIGN(TestDialogClientView);
 };
 
-class DialogClientViewTest : public testing::Test,
+class DialogClientViewTest : public ViewsTestBase,
                              public DialogDelegateView {
  public:
   DialogClientViewTest()
@@ -51,6 +52,8 @@
     dialog_buttons_ = ui::DIALOG_BUTTON_NONE;
     contents_.reset(new StaticSizedView(gfx::Size(100, 200)));
     client_view_.reset(new TestDialogClientView(contents_.get(), this));
+
+    ViewsTestBase::SetUp();
   }
 
   // DialogDelegateView implementation.
@@ -233,4 +236,57 @@
             footnote_view->bounds().height());
 }
 
+// Test that the DialogClientView's FocusManager is properly updated when the
+// DialogClientView belongs to a non top level widget and the widget is
+// reparented. The DialogClientView belongs to a non top level widget in the
+// case of constrained windows. The constrained window's widget is reparented
+// when a browser tab is dragged to a different browser window.
+TEST_F(DialogClientViewTest, FocusManager) {
+  scoped_ptr<Widget> toplevel1(new Widget);
+  Widget::InitParams toplevel1_params =
+      CreateParams(Widget::InitParams::TYPE_WINDOW);
+  toplevel1_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+  toplevel1->Init(toplevel1_params);
+
+  scoped_ptr<Widget> toplevel2(new Widget);
+  Widget::InitParams toplevel2_params =
+      CreateParams(Widget::InitParams::TYPE_WINDOW);
+  toplevel2_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+  toplevel2->Init(toplevel2_params);
+
+  Widget* dialog = new Widget;
+  Widget::InitParams dialog_params =
+      CreateParams(Widget::InitParams::TYPE_WINDOW);
+  dialog_params.child = true;
+  dialog_params.delegate = new DialogDelegateView();
+  dialog_params.parent = toplevel1->GetNativeView();
+  dialog->Init(dialog_params);
+
+  // Test that the FocusManager has been properly set when the DialogClientView
+  // was parented to |dialog|.
+  DialogClientView* client_view =
+      static_cast<DialogClientView*>(dialog->client_view());
+  EXPECT_EQ(toplevel1->GetFocusManager(), client_view->focus_manager_);
+
+  // Test that the FocusManager is properly updated when the DialogClientView's
+  // top level widget is changed.
+  Widget::ReparentNativeView(dialog->GetNativeView(), NULL);
+  EXPECT_EQ(NULL, client_view->focus_manager_);
+  Widget::ReparentNativeView(dialog->GetNativeView(),
+                             toplevel2->GetNativeView());
+  EXPECT_EQ(toplevel2->GetFocusManager(), client_view->focus_manager_);
+  Widget::ReparentNativeView(dialog->GetNativeView(),
+                             toplevel1->GetNativeView());
+  EXPECT_NE(toplevel1->GetFocusManager(), toplevel2->GetFocusManager());
+  EXPECT_EQ(toplevel1->GetFocusManager(), client_view->focus_manager_);
+
+  // Test that the FocusManager is properly cleared when the DialogClientView is
+  // removed from |dialog| during the widget's destruction.
+  client_view->set_owned_by_client();
+  scoped_ptr<DialogClientView> owned_client_view(client_view);
+  toplevel1->CloseNow();
+  toplevel2->CloseNow();
+  EXPECT_EQ(NULL, owned_client_view->focus_manager_);
+}
+
 }  // namespace views
diff --git a/ui/webui/resources/css/apps/topbutton_bar.css b/ui/webui/resources/css/apps/topbutton_bar.css
index aa91875..35641e9 100644
--- a/ui/webui/resources/css/apps/topbutton_bar.css
+++ b/ui/webui/resources/css/apps/topbutton_bar.css
@@ -18,6 +18,7 @@
   border: 0;
   display: block;
   height: 32px;
+  min-width: 0;
   outline: none;
   padding: 0;
   width: 32px;
diff --git a/ui/webui/resources/css/menu.css b/ui/webui/resources/css/menu.css
index d22bcccf..625ca15 100644
--- a/ui/webui/resources/css/menu.css
+++ b/ui/webui/resources/css/menu.css
@@ -72,7 +72,6 @@
   display: inline-block;
   height: 9px;
   margin: 0 5px;
-  vertical-align: 50%;
   width: 9px;
 }
 
diff --git a/ui/webui/resources/js/cr.js b/ui/webui/resources/js/cr.js
index 2a7ab782..3ea3b91 100644
--- a/ui/webui/resources/js/cr.js
+++ b/ui/webui/resources/js/cr.js
@@ -280,21 +280,26 @@
     };
   }
 
+  var OriginalEvent = global.Event;
+
   /**
    * Creates a new event to be used with cr.EventTarget or DOM EventTarget
    * objects.
    * @param {string} type The name of the event.
    * @param {boolean=} opt_bubbles Whether the event bubbles.
    *     Default is false.
-   * @param {boolean=} opt_preventable Whether the default action of the event
-   *     can be prevented.
+   * @param {boolean=} opt_cancelable Whether the default action of the event
+   *     can be prevented. Unlike the DOM event constructor, this defaults to
+   *     true.
    * @constructor
    * @extends {Event}
    */
-  function Event(type, opt_bubbles, opt_preventable) {
-    var e = cr.doc.createEvent('Event');
-    e.initEvent(type, !!opt_bubbles, !!opt_preventable);
-    e.__proto__ = global.Event.prototype;
+  function Event(type, opt_bubbles, opt_cancelable) {
+    var e = new OriginalEvent(type, {
+      bubbles: opt_bubbles,
+      cancelable: opt_cancelable === undefined ? true : opt_cancelable
+    });
+    e.__proto__ = OriginalEvent.prototype;
     return e;
   };
 
@@ -319,7 +324,7 @@
       return;
     }
 
-    Event.prototype = {__proto__: global.Event.prototype};
+    Event.prototype = {__proto__: OriginalEvent.prototype};
 
     cr.doc = document;
 
diff --git a/ui/webui/resources/js/cr/event_target.js b/ui/webui/resources/js/cr/event_target.js
index 5bcb41d..d9bc681 100644
--- a/ui/webui/resources/js/cr/event_target.js
+++ b/ui/webui/resources/js/cr/event_target.js
@@ -76,9 +76,6 @@
       event.__defineGetter__('target', function() {
         return self;
       });
-      event.preventDefault = function() {
-        this.returnValue = false;
-      };
 
       var type = event.type;
       var prevented = 0;
@@ -93,7 +90,7 @@
         }
       }
 
-      return !prevented && event.returnValue;
+      return !prevented && !event.defaultPrevented;
     }
   };
 
diff --git a/webkit/browser/blob/blob_url_request_job_unittest.cc b/webkit/browser/blob/blob_url_request_job_unittest.cc
index 4b0749e..2161029 100644
--- a/webkit/browser/blob/blob_url_request_job_unittest.cc
+++ b/webkit/browser/blob/blob_url_request_job_unittest.cc
@@ -18,8 +18,8 @@
 #include "net/url_request/url_request_job_factory_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "webkit/browser/blob/blob_url_request_job.h"
+#include "webkit/browser/fileapi/async_file_test_helper.h"
 #include "webkit/browser/fileapi/file_system_context.h"
-#include "webkit/browser/fileapi/file_system_file_util.h"
 #include "webkit/browser/fileapi/file_system_operation_context.h"
 #include "webkit/browser/fileapi/file_system_url.h"
 #include "webkit/browser/fileapi/mock_file_system_context.h"
@@ -204,31 +204,14 @@
             kFileSystemType,
             base::FilePath().AppendASCII(filename));
 
-    fileapi::FileSystemFileUtil* file_util =
-        file_system_context_->GetFileUtil(kFileSystemType);
-
-    fileapi::FileSystemOperationContext context(file_system_context_.get());
-    context.set_allowed_bytes_growth(1024);
-
-    base::PlatformFile handle = base::kInvalidPlatformFileValue;
-    bool created = false;
-    ASSERT_EQ(base::PLATFORM_FILE_OK, file_util->CreateOrOpen(
-        &context,
-        url,
-        base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE,
-        &handle,
-        &created));
-    EXPECT_TRUE(created);
-    ASSERT_NE(base::kInvalidPlatformFileValue, handle);
-    ASSERT_EQ(buf_size,
-        base::WritePlatformFile(handle, 0 /* offset */, buf, buf_size));
-    base::ClosePlatformFile(handle);
+    ASSERT_EQ(base::PLATFORM_FILE_OK,
+              fileapi::AsyncFileTestHelper::CreateFileWithData(
+                  file_system_context_, url, buf, buf_size));
 
     base::PlatformFileInfo file_info;
-    base::FilePath platform_path;
     ASSERT_EQ(base::PLATFORM_FILE_OK,
-              file_util->GetFileInfo(&context, url, &file_info,
-                                     &platform_path));
+              fileapi::AsyncFileTestHelper::GetMetadata(
+                  file_system_context_, url, &file_info));
     if (modification_time)
       *modification_time = file_info.last_modified;
   }
diff --git a/webkit/browser/database/database_tracker.cc b/webkit/browser/database/database_tracker.cc
index e3b3c6a..f9adb3b 100644
--- a/webkit/browser/database/database_tracker.cc
+++ b/webkit/browser/database/database_tracker.cc
@@ -289,15 +289,13 @@
   if (!LazyInit())
     return base::FilePath();
 
-  int64 id = databases_table_->GetDatabaseID(
-      origin_identifier, database_name);
+  int64 id = databases_table_->GetDatabaseID(origin_identifier, database_name);
   if (id < 0)
     return base::FilePath();
 
-  base::FilePath file_name = base::FilePath::FromWStringHack(
-      UTF8ToWide(base::Int64ToString(id)));
-  return db_dir_.Append(base::FilePath::FromWStringHack(
-      UTF16ToWide(GetOriginDirectory(origin_identifier)))).Append(file_name);
+  return db_dir_.Append(base::FilePath::FromUTF16Unsafe(
+      GetOriginDirectory(origin_identifier))).AppendASCII(
+          base::Int64ToString(id));
 }
 
 bool DatabaseTracker::GetOriginInfo(const std::string& origin_identifier,
diff --git a/webkit/browser/database/database_tracker_unittest.cc b/webkit/browser/database/database_tracker_unittest.cc
index 25f0a69..860faa0 100644
--- a/webkit/browser/database/database_tracker_unittest.cc
+++ b/webkit/browser/database/database_tracker_unittest.cc
@@ -220,12 +220,12 @@
     tracker->DatabaseOpened(kOrigin2, kDB3, kDescription, 0,
                             &database_size);
 
-    EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append(
-        base::FilePath::FromWStringHack(UTF16ToWide(
-            tracker->GetOriginDirectory(kOrigin1))))));
-    EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append(
-        base::FilePath::FromWStringHack(UTF16ToWide(
-            tracker->GetOriginDirectory(kOrigin2))))));
+    EXPECT_TRUE(file_util::CreateDirectory(
+        tracker->DatabaseDirectory().Append(base::FilePath::FromUTF16Unsafe(
+            tracker->GetOriginDirectory(kOrigin1)))));
+    EXPECT_TRUE(file_util::CreateDirectory(
+        tracker->DatabaseDirectory().Append(base::FilePath::FromUTF16Unsafe(
+            tracker->GetOriginDirectory(kOrigin2)))));
     EXPECT_EQ(1, file_util::WriteFile(
         tracker->GetFullDBFilePath(kOrigin1, kDB1), "a", 1));
     EXPECT_EQ(2, file_util::WriteFile(
@@ -255,9 +255,9 @@
     // Recreate db1.
     tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0,
                             &database_size);
-    EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append(
-        base::FilePath::FromWStringHack(UTF16ToWide(
-            tracker->GetOriginDirectory(kOrigin1))))));
+    EXPECT_TRUE(file_util::CreateDirectory(
+        tracker->DatabaseDirectory().Append(base::FilePath::FromUTF16Unsafe(
+            tracker->GetOriginDirectory(kOrigin1)))));
     EXPECT_EQ(1, file_util::WriteFile(
         tracker->GetFullDBFilePath(kOrigin1, kDB1), "a", 1));
     tracker->DatabaseModified(kOrigin1, kDB1);
@@ -348,12 +348,12 @@
 
     // Write some data to each file and check that the listeners are
     // called with the appropriate values.
-    EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append(
-        base::FilePath::FromWStringHack(UTF16ToWide(
-            tracker->GetOriginDirectory(kOrigin1))))));
-    EXPECT_TRUE(file_util::CreateDirectory(tracker->DatabaseDirectory().Append(
-        base::FilePath::FromWStringHack(UTF16ToWide(
-            tracker->GetOriginDirectory(kOrigin2))))));
+    EXPECT_TRUE(file_util::CreateDirectory(
+        tracker->DatabaseDirectory().Append(base::FilePath::FromUTF16Unsafe(
+            tracker->GetOriginDirectory(kOrigin1)))));
+    EXPECT_TRUE(file_util::CreateDirectory(
+        tracker->DatabaseDirectory().Append(base::FilePath::FromUTF16Unsafe(
+            tracker->GetOriginDirectory(kOrigin2)))));
     EXPECT_EQ(1, file_util::WriteFile(
         tracker->GetFullDBFilePath(kOrigin1, kDB1), "a", 1));
     EXPECT_EQ(2, file_util::WriteFile(
diff --git a/webkit/browser/fileapi/file_system_backend.h b/webkit/browser/fileapi/file_system_backend.h
index b368db6..2c8be93 100644
--- a/webkit/browser/fileapi/file_system_backend.h
+++ b/webkit/browser/fileapi/file_system_backend.h
@@ -69,11 +69,6 @@
       OpenFileSystemMode mode,
       const OpenFileSystemCallback& callback) = 0;
 
-  // Returns the specialized FileSystemFileUtil for this backend.
-  // It is ok to return NULL if the filesystem doesn't support synchronous
-  // version of FileUtil.
-  virtual FileSystemFileUtil* GetFileUtil(FileSystemType type) = 0;
-
   // Returns the specialized AsyncFileUtil for this backend.
   virtual AsyncFileUtil* GetAsyncFileUtil(FileSystemType type) = 0;
 
diff --git a/webkit/browser/fileapi/file_system_context.cc b/webkit/browser/fileapi/file_system_context.cc
index f2973d3..2053cb1 100644
--- a/webkit/browser/fileapi/file_system_context.cc
+++ b/webkit/browser/fileapi/file_system_context.cc
@@ -200,14 +200,6 @@
   return backend->GetAsyncFileUtil(type);
 }
 
-FileSystemFileUtil* FileSystemContext::GetFileUtil(
-    FileSystemType type) const {
-  FileSystemBackend* backend = GetFileSystemBackend(type);
-  if (!backend)
-    return NULL;
-  return backend->GetFileUtil(type);
-}
-
 CopyOrMoveFileValidatorFactory*
 FileSystemContext::GetCopyOrMoveFileValidatorFactory(
     FileSystemType type, base::PlatformFileError* error_code) const {
diff --git a/webkit/browser/fileapi/file_system_context.h b/webkit/browser/fileapi/file_system_context.h
index 528f7a2..769420f 100644
--- a/webkit/browser/fileapi/file_system_context.h
+++ b/webkit/browser/fileapi/file_system_context.h
@@ -119,11 +119,6 @@
   // Returns the appropriate AsyncFileUtil instance for the given |type|.
   AsyncFileUtil* GetAsyncFileUtil(FileSystemType type) const;
 
-  // Returns the appropriate FileUtil instance for the given |type|.
-  // This may return NULL if it is given an invalid type or the filesystem
-  // does not support synchronous file operations.
-  FileSystemFileUtil* GetFileUtil(FileSystemType type) const;
-
   // Returns the appropriate CopyOrMoveFileValidatorFactory for the given
   // |type|.  If |error_code| is PLATFORM_FILE_OK and the result is NULL,
   // then no validator is required.
diff --git a/webkit/browser/fileapi/file_system_dir_url_request_job_unittest.cc b/webkit/browser/fileapi/file_system_dir_url_request_job_unittest.cc
index 1c197cd..8f39b2d 100644
--- a/webkit/browser/fileapi/file_system_dir_url_request_job_unittest.cc
+++ b/webkit/browser/fileapi/file_system_dir_url_request_job_unittest.cc
@@ -199,7 +199,7 @@
   }
 
   FileSystemFileUtil* file_util() {
-    return file_system_context_->GetFileUtil(kFileSystemTypeTemporary);
+    return file_system_context_->sandbox_delegate()->sync_file_util();
   }
 
   // Put the message loop at the top, so that it's the last thing deleted.
diff --git a/webkit/browser/fileapi/file_system_file_stream_reader_unittest.cc b/webkit/browser/fileapi/file_system_file_stream_reader_unittest.cc
index ca82338..681d1e2 100644
--- a/webkit/browser/fileapi/file_system_file_stream_reader_unittest.cc
+++ b/webkit/browser/fileapi/file_system_file_stream_reader_unittest.cc
@@ -15,10 +15,10 @@
 #include "net/base/net_errors.h"
 #include "net/base/test_completion_callback.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/async_file_test_helper.h"
 #include "webkit/browser/fileapi/external_mount_points.h"
 #include "webkit/browser/fileapi/file_system_context.h"
 #include "webkit/browser/fileapi/file_system_file_util.h"
-#include "webkit/browser/fileapi/file_system_operation_context.h"
 #include "webkit/browser/fileapi/mock_file_system_context.h"
 
 namespace fileapi {
@@ -102,32 +102,16 @@
                  const char* buf,
                  int buf_size,
                  base::Time* modification_time) {
-    FileSystemFileUtil* file_util = file_system_context_->GetFileUtil(
-        kFileSystemTypeTemporary);
     FileSystemURL url = GetFileSystemURL(file_name);
 
-    FileSystemOperationContext context(file_system_context_.get());
-    context.set_allowed_bytes_growth(1024);
-
-    base::PlatformFile handle = base::kInvalidPlatformFileValue;
-    bool created = false;
-    ASSERT_EQ(base::PLATFORM_FILE_OK, file_util->CreateOrOpen(
-        &context,
-        url,
-        base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE,
-        &handle,
-        &created));
-    EXPECT_TRUE(created);
-    ASSERT_NE(base::kInvalidPlatformFileValue, handle);
-    ASSERT_EQ(buf_size,
-              base::WritePlatformFile(handle, 0 /* offset */, buf, buf_size));
-    base::ClosePlatformFile(handle);
+    ASSERT_EQ(base::PLATFORM_FILE_OK,
+              fileapi::AsyncFileTestHelper::CreateFileWithData(
+                  file_system_context_, url, buf, buf_size));
 
     base::PlatformFileInfo file_info;
-    base::FilePath platform_path;
     ASSERT_EQ(base::PLATFORM_FILE_OK,
-              file_util->GetFileInfo(&context, url, &file_info,
-                                     &platform_path));
+              AsyncFileTestHelper::GetMetadata(
+                  file_system_context_, url, &file_info));
     if (modification_time)
       *modification_time = file_info.last_modified;
   }
diff --git a/webkit/browser/fileapi/file_system_operation.h b/webkit/browser/fileapi/file_system_operation.h
index f1adc19..a039dd4 100644
--- a/webkit/browser/fileapi/file_system_operation.h
+++ b/webkit/browser/fileapi/file_system_operation.h
@@ -139,12 +139,30 @@
   // |src_path| is a directory, the contents of |src_path| are copied to
   // |dest_path| recursively. A new file or directory is created at
   // |dest_path| as needed.
+  //
+  // For recursive case this internally creates new FileSystemOperations and
+  // calls:
+  // - ReadDirectory, CopyFileLocal and CreateDirectory
+  //   for same-filesystem case, or
+  // - ReadDirectory and CreateSnapshotFile on source filesystem and
+  //   CopyInForeignFile and CreateDirectory on dest filesystem
+  //   for cross-filesystem case.
+  //
   virtual void Copy(const FileSystemURL& src_path,
                     const FileSystemURL& dest_path,
                     const StatusCallback& callback) = 0;
 
   // Moves a file or directory from |src_path| to |dest_path|. A new file
   // or directory is created at |dest_path| as needed.
+  //
+  // For recursive case this internally creates new FileSystemOperations and
+  // calls:
+  // - ReadDirectory, MoveFileLocal, CreateDirectory and Remove
+  //   for same-filesystem case, or
+  // - ReadDirectory, CreateSnapshotFile and Remove on source filesystem and
+  //   CopyInForeignFile and CreateDirectory on dest filesystem
+  //   for cross-filesystem case.
+  //
   virtual void Move(const FileSystemURL& src_path,
                     const FileSystemURL& dest_path,
                     const StatusCallback& callback) = 0;
@@ -230,11 +248,6 @@
                         base::ProcessHandle peer_handle,
                         const OpenFileCallback& callback) = 0;
 
-  // For downcasting to FileSystemOperationImpl.
-  // TODO(kinuko): this hack should go away once appropriate upload-stream
-  // handling based on element types is supported.
-  virtual FileSystemOperationImpl* AsFileSystemOperationImpl() = 0;
-
   // Creates a local snapshot file for a given |path| and returns the
   // metadata and platform path of the snapshot file via |callback|.
   // In local filesystem cases the implementation may simply return
@@ -246,6 +259,82 @@
   virtual void CreateSnapshotFile(const FileSystemURL& path,
                                   const SnapshotFileCallback& callback) = 0;
 
+  // Copies in a single file from a different filesystem.
+  //
+  // This returns:
+  // - PLATFORM_FILE_ERROR_NOT_FOUND if |src_file_path|
+  //   or the parent directory of |dest_url| does not exist.
+  // - PLATFORM_FILE_ERROR_INVALID_OPERATION if |dest_url| exists and
+  //   is not a file.
+  // - PLATFORM_FILE_ERROR_FAILED if |dest_url| does not exist and
+  //   its parent path is a file.
+  //
+  virtual void CopyInForeignFile(const base::FilePath& src_local_disk_path,
+                                 const FileSystemURL& dest_url,
+                                 const StatusCallback& callback) = 0;
+
+  // Removes a single file.
+  //
+  // This returns:
+  // - PLATFORM_FILE_ERROR_NOT_FOUND if |url| does not exist.
+  // - PLATFORM_FILE_ERROR_NOT_A_FILE if |url| is not a file.
+  //
+  virtual void RemoveFile(const FileSystemURL& url,
+                          const StatusCallback& callback) = 0;
+
+  // Removes a single empty directory.
+  //
+  // This returns:
+  // - PLATFORM_FILE_ERROR_NOT_FOUND if |url| does not exist.
+  // - PLATFORM_FILE_ERROR_NOT_A_DIRECTORY if |url| is not a directory.
+  // - PLATFORM_FILE_ERROR_NOT_EMPTY if |url| is not empty.
+  //
+  virtual void RemoveDirectory(const FileSystemURL& url,
+                               const StatusCallback& callback) = 0;
+
+  // Copies a file from |src_url| to |dest_url|.
+  // This must be called for files that belong to the same filesystem
+  // (i.e. type() and origin() of the |src_url| and |dest_url| must match).
+  //
+  // This returns:
+  // - PLATFORM_FILE_ERROR_NOT_FOUND if |src_url|
+  //   or the parent directory of |dest_url| does not exist.
+  // - PLATFORM_FILE_ERROR_NOT_A_FILE if |src_url| exists but is not a file.
+  // - PLATFORM_FILE_ERROR_INVALID_OPERATION if |dest_url| exists and
+  //   is not a file.
+  // - PLATFORM_FILE_ERROR_FAILED if |dest_url| does not exist and
+  //   its parent path is a file.
+  //
+  virtual void CopyFileLocal(const FileSystemURL& src_url,
+                             const FileSystemURL& dest_url,
+                             const StatusCallback& callback) = 0;
+
+  // Moves a local file from |src_url| to |dest_url|.
+  // This must be called for files that belong to the same filesystem
+  // (i.e. type() and origin() of the |src_url| and |dest_url| must match).
+  //
+  // This returns:
+  // - PLATFORM_FILE_ERROR_NOT_FOUND if |src_url|
+  //   or the parent directory of |dest_url| does not exist.
+  // - PLATFORM_FILE_ERROR_NOT_A_FILE if |src_url| exists but is not a file.
+  // - PLATFORM_FILE_ERROR_INVALID_OPERATION if |dest_url| exists and
+  //   is not a file.
+  // - PLATFORM_FILE_ERROR_FAILED if |dest_url| does not exist and
+  //   its parent path is a file.
+  //
+  virtual void MoveFileLocal(const FileSystemURL& src_url,
+                             const FileSystemURL& dest_url,
+                             const StatusCallback& callback) = 0;
+
+  // Synchronously gets the platform path for the given |url|.
+  // This may fail if the given |url|'s filesystem type is neither
+  // temporary nor persistent.
+  // In such a case, base::PLATFORM_FILE_ERROR_INVALID_OPERATION will be
+  // returned.
+  virtual base::PlatformFileError SyncGetPlatformPath(
+      const FileSystemURL& url,
+      base::FilePath* platform_path) = 0;
+
  protected:
   // Used only for internal assertions.
   enum OperationType {
diff --git a/webkit/browser/fileapi/file_system_operation_impl.cc b/webkit/browser/fileapi/file_system_operation_impl.cc
index 60f7990..0e7b937 100644
--- a/webkit/browser/fileapi/file_system_operation_impl.cc
+++ b/webkit/browser/fileapi/file_system_operation_impl.cc
@@ -20,7 +20,7 @@
 #include "webkit/browser/fileapi/file_system_url.h"
 #include "webkit/browser/fileapi/file_writer_delegate.h"
 #include "webkit/browser/fileapi/remove_operation_delegate.h"
-#include "webkit/browser/fileapi/sandbox_file_stream_writer.h"
+#include "webkit/browser/fileapi/sandbox_file_system_backend.h"
 #include "webkit/browser/quota/quota_manager.h"
 #include "webkit/common/blob/shareable_file_reference.h"
 #include "webkit/common/fileapi/file_system_types.h"
@@ -39,7 +39,8 @@
       operation_context_(operation_context.Pass()),
       async_file_util_(NULL),
       peer_handle_(base::kNullProcessHandle),
-      pending_operation_(kOperationNone) {
+      pending_operation_(kOperationNone),
+      weak_factory_(this) {
   DCHECK(operation_context_.get());
   operation_context_->DetachUserDataThread();
   async_file_util_ = file_system_context_->GetAsyncFileUtil(url.type());
@@ -56,7 +57,7 @@
   GetUsageAndQuotaThenRunTask(
       url,
       base::Bind(&FileSystemOperationImpl::DoCreateFile,
-                 AsWeakPtr(), url, callback, exclusive),
+                 weak_factory_.GetWeakPtr(), url, callback, exclusive),
       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
 }
 
@@ -68,7 +69,8 @@
   GetUsageAndQuotaThenRunTask(
       url,
       base::Bind(&FileSystemOperationImpl::DoCreateDirectory,
-                 AsWeakPtr(), url, callback, exclusive, recursive),
+                 weak_factory_.GetWeakPtr(), url, callback,
+                 exclusive, recursive),
       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
 }
 
@@ -83,7 +85,7 @@
           src_url, dest_url,
           CopyOrMoveOperationDelegate::OPERATION_COPY,
           base::Bind(&FileSystemOperationImpl::DidFinishOperation,
-                     AsWeakPtr(), callback)));
+                     weak_factory_.GetWeakPtr(), callback)));
   recursive_operation_delegate_->RunRecursively();
 }
 
@@ -98,7 +100,7 @@
           src_url, dest_url,
           CopyOrMoveOperationDelegate::OPERATION_MOVE,
           base::Bind(&FileSystemOperationImpl::DidFinishOperation,
-                     AsWeakPtr(), callback)));
+                     weak_factory_.GetWeakPtr(), callback)));
   recursive_operation_delegate_->RunRecursively();
 }
 
@@ -108,7 +110,7 @@
   async_file_util_->GetFileInfo(
       operation_context_.Pass(), url,
       base::Bind(&FileSystemOperationImpl::DidDirectoryExists,
-                 AsWeakPtr(), callback));
+                 weak_factory_.GetWeakPtr(), callback));
 }
 
 void FileSystemOperationImpl::FileExists(const FileSystemURL& url,
@@ -117,7 +119,7 @@
   async_file_util_->GetFileInfo(
       operation_context_.Pass(), url,
       base::Bind(&FileSystemOperationImpl::DidFileExists,
-                 AsWeakPtr(), callback));
+                 weak_factory_.GetWeakPtr(), callback));
 }
 
 void FileSystemOperationImpl::GetMetadata(
@@ -146,7 +148,7 @@
     async_file_util_->DeleteRecursively(
         operation_context_.Pass(), url,
         base::Bind(&FileSystemOperationImpl::DidDeleteRecursively,
-                   AsWeakPtr(), url, callback));
+                   weak_factory_.GetWeakPtr(), url, callback));
     return;
   }
 
@@ -154,7 +156,7 @@
       new RemoveOperationDelegate(
           file_system_context(), url,
           base::Bind(&FileSystemOperationImpl::DidFinishOperation,
-                     AsWeakPtr(), callback)));
+                     weak_factory_.GetWeakPtr(), callback)));
   recursive_operation_delegate_->Run();
 }
 
@@ -167,8 +169,8 @@
   file_writer_delegate_ = writer_delegate.Pass();
   file_writer_delegate_->Start(
       blob_request.Pass(),
-      base::Bind(&FileSystemOperationImpl::DidWrite, AsWeakPtr(),
-                 url, callback));
+      base::Bind(&FileSystemOperationImpl::DidWrite,
+                 weak_factory_.GetWeakPtr(), url, callback));
 }
 
 void FileSystemOperationImpl::Truncate(const FileSystemURL& url, int64 length,
@@ -177,7 +179,7 @@
   GetUsageAndQuotaThenRunTask(
       url,
       base::Bind(&FileSystemOperationImpl::DoTruncate,
-                 AsWeakPtr(), url, callback, length),
+                 weak_factory_.GetWeakPtr(), url, callback, length),
       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
 }
 
@@ -190,7 +192,7 @@
       operation_context_.Pass(), url,
       last_access_time, last_modified_time,
       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
-                 AsWeakPtr(), callback));
+                 weak_factory_.GetWeakPtr(), callback));
 }
 
 void FileSystemOperationImpl::OpenFile(const FileSystemURL& url,
@@ -212,7 +214,7 @@
   GetUsageAndQuotaThenRunTask(
       url,
       base::Bind(&FileSystemOperationImpl::DoOpenFile,
-                 AsWeakPtr(),
+                 weak_factory_.GetWeakPtr(),
                  url, callback, file_flags),
       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED,
                  base::kInvalidPlatformFileValue,
@@ -237,22 +239,6 @@
   }
 }
 
-FileSystemOperationImpl* FileSystemOperationImpl::AsFileSystemOperationImpl() {
-  return this;
-}
-
-base::PlatformFileError FileSystemOperationImpl::SyncGetPlatformPath(
-    const FileSystemURL& url,
-    base::FilePath* platform_path) {
-  DCHECK(SetPendingOperationType(kOperationGetLocalPath));
-  FileSystemFileUtil* file_util = file_system_context()->GetFileUtil(
-      url.type());
-  if (!file_util)
-    return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
-  file_util->GetLocalFilePath(operation_context_.get(), url, platform_path);
-  return base::PLATFORM_FILE_OK;
-}
-
 void FileSystemOperationImpl::CreateSnapshotFile(
     const FileSystemURL& url,
     const SnapshotFileCallback& callback) {
@@ -269,7 +255,7 @@
   GetUsageAndQuotaThenRunTask(
       dest_url,
       base::Bind(&FileSystemOperationImpl::DoCopyInForeignFile,
-                 AsWeakPtr(), src_local_disk_file_path, dest_url,
+                 weak_factory_.GetWeakPtr(), src_local_disk_file_path, dest_url,
                  callback),
       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
 }
@@ -281,7 +267,7 @@
   async_file_util_->DeleteFile(
       operation_context_.Pass(), url,
       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
-                 AsWeakPtr(), callback));
+                 weak_factory_.GetWeakPtr(), callback));
 }
 
 void FileSystemOperationImpl::RemoveDirectory(
@@ -291,7 +277,7 @@
   async_file_util_->DeleteDirectory(
       operation_context_.Pass(), url,
       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
-                 AsWeakPtr(), callback));
+                 weak_factory_.GetWeakPtr(), callback));
 }
 
 void FileSystemOperationImpl::CopyFileLocal(
@@ -303,7 +289,7 @@
   GetUsageAndQuotaThenRunTask(
       dest_url,
       base::Bind(&FileSystemOperationImpl::DoCopyFileLocal,
-                 AsWeakPtr(), src_url, dest_url, callback),
+                 weak_factory_.GetWeakPtr(), src_url, dest_url, callback),
       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
 }
 
@@ -316,10 +302,22 @@
   GetUsageAndQuotaThenRunTask(
       dest_url,
       base::Bind(&FileSystemOperationImpl::DoMoveFileLocal,
-                 AsWeakPtr(), src_url, dest_url, callback),
+                 weak_factory_.GetWeakPtr(), src_url, dest_url, callback),
       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
 }
 
+base::PlatformFileError FileSystemOperationImpl::SyncGetPlatformPath(
+    const FileSystemURL& url,
+    base::FilePath* platform_path) {
+  DCHECK(SetPendingOperationType(kOperationGetLocalPath));
+  if (!file_system_context()->IsSandboxFileSystem(url.type()))
+    return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
+  FileSystemFileUtil* file_util =
+      file_system_context()->sandbox_delegate()->sync_file_util();
+  file_util->GetLocalFilePath(operation_context_.get(), url, platform_path);
+  return base::PLATFORM_FILE_OK;
+}
+
 void FileSystemOperationImpl::GetUsageAndQuotaThenRunTask(
     const FileSystemURL& url,
     const base::Closure& task,
@@ -341,7 +339,7 @@
       url.origin(),
       FileSystemTypeToQuotaStorageType(url.type()),
       base::Bind(&FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask,
-                 AsWeakPtr(), task, error_callback));
+                 weak_factory_.GetWeakPtr(), task, error_callback));
 }
 
 void FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask(
@@ -369,7 +367,7 @@
           exclusive ?
               &FileSystemOperationImpl::DidEnsureFileExistsExclusive :
               &FileSystemOperationImpl::DidEnsureFileExistsNonExclusive,
-          AsWeakPtr(), callback));
+          weak_factory_.GetWeakPtr(), callback));
 }
 
 void FileSystemOperationImpl::DoCreateDirectory(
@@ -380,7 +378,7 @@
       operation_context_.Pass(),
       url, exclusive, recursive,
       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
-                 AsWeakPtr(), callback));
+                 weak_factory_.GetWeakPtr(), callback));
 }
 
 void FileSystemOperationImpl::DoCopyFileLocal(
@@ -390,7 +388,7 @@
   async_file_util_->CopyFileLocal(
       operation_context_.Pass(), src_url, dest_url,
       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
-                 AsWeakPtr(), callback));
+                 weak_factory_.GetWeakPtr(), callback));
 }
 
 void FileSystemOperationImpl::DoMoveFileLocal(
@@ -400,7 +398,7 @@
   async_file_util_->MoveFileLocal(
       operation_context_.Pass(), src_url, dest_url,
       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
-                 AsWeakPtr(), callback));
+                 weak_factory_.GetWeakPtr(), callback));
 }
 
 void FileSystemOperationImpl::DoCopyInForeignFile(
@@ -411,7 +409,7 @@
       operation_context_.Pass(),
       src_local_disk_file_path, dest_url,
       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
-                 AsWeakPtr(), callback));
+                 weak_factory_.GetWeakPtr(), callback));
 }
 
 void FileSystemOperationImpl::DoTruncate(const FileSystemURL& url,
@@ -420,7 +418,7 @@
   async_file_util_->Truncate(
       operation_context_.Pass(), url, length,
       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
-                 AsWeakPtr(), callback));
+                 weak_factory_.GetWeakPtr(), callback));
 }
 
 void FileSystemOperationImpl::DoOpenFile(const FileSystemURL& url,
@@ -429,7 +427,7 @@
   async_file_util_->CreateOrOpen(
       operation_context_.Pass(), url, file_flags,
       base::Bind(&FileSystemOperationImpl::DidOpenFile,
-                 AsWeakPtr(), callback));
+                 weak_factory_.GetWeakPtr(), callback));
 }
 
 void FileSystemOperationImpl::DidEnsureFileExistsExclusive(
@@ -491,7 +489,7 @@
         new RemoveOperationDelegate(
             file_system_context(), url,
             base::Bind(&FileSystemOperationImpl::DidFinishOperation,
-                       AsWeakPtr(), callback)));
+                       weak_factory_.GetWeakPtr(), callback)));
     recursive_operation_delegate_->RunRecursively();
     return;
   }
diff --git a/webkit/browser/fileapi/file_system_operation_impl.h b/webkit/browser/fileapi/file_system_operation_impl.h
index 293ac51..af9768d 100644
--- a/webkit/browser/fileapi/file_system_operation_impl.h
+++ b/webkit/browser/fileapi/file_system_operation_impl.h
@@ -26,8 +26,7 @@
 
 // The default implementation of FileSystemOperation for file systems.
 class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemOperationImpl
-    : public NON_EXPORTED_BASE(FileSystemOperation),
-      public base::SupportsWeakPtr<FileSystemOperationImpl> {
+    : public NON_EXPORTED_BASE(FileSystemOperation) {
  public:
   // NOTE: This constructor should not be called outside FileSystemBackends;
   // instead please consider using
@@ -79,90 +78,31 @@
                         base::ProcessHandle peer_handle,
                         const OpenFileCallback& callback) OVERRIDE;
   virtual void Cancel(const StatusCallback& cancel_callback) OVERRIDE;
-  virtual FileSystemOperationImpl* AsFileSystemOperationImpl() OVERRIDE;
   virtual void CreateSnapshotFile(
       const FileSystemURL& path,
       const SnapshotFileCallback& callback) OVERRIDE;
-
-  // Copies in a single file from a different filesystem.
-  //
-  // This returns:
-  // - PLATFORM_FILE_ERROR_NOT_FOUND if |src_file_path|
-  //   or the parent directory of |dest_url| does not exist.
-  // - PLATFORM_FILE_ERROR_INVALID_OPERATION if |dest_url| exists and
-  //   is not a file.
-  // - PLATFORM_FILE_ERROR_FAILED if |dest_url| does not exist and
-  //   its parent path is a file.
-  //
   virtual void CopyInForeignFile(const base::FilePath& src_local_disk_path,
                                  const FileSystemURL& dest_url,
-                                 const StatusCallback& callback);
-
-  // Removes a single file.
-  //
-  // This returns:
-  // - PLATFORM_FILE_ERROR_NOT_FOUND if |url| does not exist.
-  // - PLATFORM_FILE_ERROR_NOT_A_FILE if |url| is not a file.
-  //
-  void RemoveFile(const FileSystemURL& url,
-                  const StatusCallback& callback);
-
-  // Removes a single empty directory.
-  //
-  // This returns:
-  // - PLATFORM_FILE_ERROR_NOT_FOUND if |url| does not exist.
-  // - PLATFORM_FILE_ERROR_NOT_A_DIRECTORY if |url| is not a directory.
-  // - PLATFORM_FILE_ERROR_NOT_EMPTY if |url| is not empty.
-  //
-  void RemoveDirectory(const FileSystemURL& url,
-                       const StatusCallback& callback);
-
-  // Copies a file from |src_url| to |dest_url|.
-  // This must be called for files that belong to the same filesystem
-  // (i.e. type() and origin() of the |src_url| and |dest_url| must match).
-  //
-  // This returns:
-  // - PLATFORM_FILE_ERROR_NOT_FOUND if |src_url|
-  //   or the parent directory of |dest_url| does not exist.
-  // - PLATFORM_FILE_ERROR_NOT_A_FILE if |src_url| exists but is not a file.
-  // - PLATFORM_FILE_ERROR_INVALID_OPERATION if |dest_url| exists and
-  //   is not a file.
-  // - PLATFORM_FILE_ERROR_FAILED if |dest_url| does not exist and
-  //   its parent path is a file.
-  //
-  void CopyFileLocal(const FileSystemURL& src_url,
-                     const FileSystemURL& dest_url,
-                     const StatusCallback& callback);
-
-  // Moves a local file from |src_url| to |dest_url|.
-  // This must be called for files that belong to the same filesystem
-  // (i.e. type() and origin() of the |src_url| and |dest_url| must match).
-  //
-  // This returns:
-  // - PLATFORM_FILE_ERROR_NOT_FOUND if |src_url|
-  //   or the parent directory of |dest_url| does not exist.
-  // - PLATFORM_FILE_ERROR_NOT_A_FILE if |src_url| exists but is not a file.
-  // - PLATFORM_FILE_ERROR_INVALID_OPERATION if |dest_url| exists and
-  //   is not a file.
-  // - PLATFORM_FILE_ERROR_FAILED if |dest_url| does not exist and
-  //   its parent path is a file.
-  //
-  void MoveFileLocal(const FileSystemURL& src_url,
-                     const FileSystemURL& dest_url,
-                     const StatusCallback& callback);
-
-  // Synchronously gets the platform path for the given |url|.
-  // This may fail if |file_system_context| returns NULL on GetFileUtil().
-  // In such a case, base::PLATFORM_FILE_ERROR_INVALID_OPERATION will be
-  // returned.
-  base::PlatformFileError SyncGetPlatformPath(const FileSystemURL& url,
-                                              base::FilePath* platform_path);
+                                 const StatusCallback& callback) OVERRIDE;
+  virtual void RemoveFile(const FileSystemURL& url,
+                          const StatusCallback& callback) OVERRIDE;
+  virtual void RemoveDirectory(const FileSystemURL& url,
+                               const StatusCallback& callback) OVERRIDE;
+  virtual void CopyFileLocal(const FileSystemURL& src_url,
+                             const FileSystemURL& dest_url,
+                             const StatusCallback& callback) OVERRIDE;
+  virtual void MoveFileLocal(const FileSystemURL& src_url,
+                             const FileSystemURL& dest_url,
+                             const StatusCallback& callback) OVERRIDE;
+  virtual base::PlatformFileError SyncGetPlatformPath(
+      const FileSystemURL& url,
+      base::FilePath* platform_path) OVERRIDE;
 
   FileSystemContext* file_system_context() const {
     return file_system_context_.get();
   }
 
- protected:
+ private:
   // Queries the quota and usage and then runs the given |task|.
   // If an error occurs during the quota query it runs |error_callback| instead.
   void GetUsageAndQuotaThenRunTask(
@@ -255,6 +195,8 @@
   // A flag to make sure we call operation only once per instance.
   OperationType pending_operation_;
 
+  base::WeakPtrFactory<FileSystemOperationImpl> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(FileSystemOperationImpl);
 };
 
diff --git a/webkit/browser/fileapi/file_system_operation_runner.cc b/webkit/browser/fileapi/file_system_operation_runner.cc
index 351536b..b899e1b 100644
--- a/webkit/browser/fileapi/file_system_operation_runner.cc
+++ b/webkit/browser/fileapi/file_system_operation_runner.cc
@@ -348,14 +348,14 @@
     const FileSystemURL& dest_url,
     const StatusCallback& callback) {
   base::PlatformFileError error = base::PLATFORM_FILE_OK;
-  FileSystemOperation* operation = CreateFileSystemOperationImpl(
-      dest_url, &error);
+  FileSystemOperation* operation =
+      file_system_context_->CreateFileSystemOperation(dest_url, &error);
   if (!operation) {
     callback.Run(error);
     return kErrorOperationID;
   }
   OperationID id = operations_.Add(operation);
-  operation->AsFileSystemOperationImpl()->CopyInForeignFile(
+  operation->CopyInForeignFile(
       src_local_disk_path, dest_url,
       base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
                  id, callback));
@@ -366,13 +366,14 @@
     const FileSystemURL& url,
     const StatusCallback& callback) {
   base::PlatformFileError error = base::PLATFORM_FILE_OK;
-  FileSystemOperation* operation = CreateFileSystemOperationImpl(url, &error);
+  FileSystemOperation* operation =
+      file_system_context_->CreateFileSystemOperation(url, &error);
   if (!operation) {
     callback.Run(error);
     return kErrorOperationID;
   }
   OperationID id = operations_.Add(operation);
-  operation->AsFileSystemOperationImpl()->RemoveFile(
+  operation->RemoveFile(
       url,
       base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
                  id, callback));
@@ -383,13 +384,14 @@
     const FileSystemURL& url,
     const StatusCallback& callback) {
   base::PlatformFileError error = base::PLATFORM_FILE_OK;
-  FileSystemOperation* operation = CreateFileSystemOperationImpl(url, &error);
+  FileSystemOperation* operation =
+      file_system_context_->CreateFileSystemOperation(url, &error);
   if (!operation) {
     callback.Run(error);
     return kErrorOperationID;
   }
   OperationID id = operations_.Add(operation);
-  operation->AsFileSystemOperationImpl()->RemoveDirectory(
+  operation->RemoveDirectory(
       url,
       base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
                  id, callback));
@@ -401,14 +403,14 @@
     const FileSystemURL& dest_url,
     const StatusCallback& callback) {
   base::PlatformFileError error = base::PLATFORM_FILE_OK;
-  FileSystemOperation* operation = CreateFileSystemOperationImpl(
-      src_url, &error);
+  FileSystemOperation* operation =
+      file_system_context_->CreateFileSystemOperation(src_url, &error);
   if (!operation) {
     callback.Run(error);
     return kErrorOperationID;
   }
   OperationID id = operations_.Add(operation);
-  operation->AsFileSystemOperationImpl()->CopyFileLocal(
+  operation->CopyFileLocal(
       src_url, dest_url,
       base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
                  id, callback));
@@ -420,14 +422,14 @@
     const FileSystemURL& dest_url,
     const StatusCallback& callback) {
   base::PlatformFileError error = base::PLATFORM_FILE_OK;
-  FileSystemOperation* operation = CreateFileSystemOperationImpl(
-      src_url, &error);
+  FileSystemOperation* operation =
+      file_system_context_->CreateFileSystemOperation(src_url, &error);
   if (!operation) {
     callback.Run(error);
     return kErrorOperationID;
   }
   OperationID id = operations_.Add(operation);
-  operation->AsFileSystemOperationImpl()->MoveFileLocal(
+  operation->MoveFileLocal(
       src_url, dest_url,
       base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
                  id, callback));
@@ -438,12 +440,11 @@
     const FileSystemURL& url,
     base::FilePath* platform_path) {
   base::PlatformFileError error = base::PLATFORM_FILE_OK;
-  FileSystemOperation* operation = CreateFileSystemOperationImpl(url, &error);
+  FileSystemOperation* operation =
+      file_system_context_->CreateFileSystemOperation(url, &error);
   if (!operation)
     return error;
-
-  return operation->AsFileSystemOperationImpl()->SyncGetPlatformPath(
-      url, platform_path);
+  return operation->SyncGetPlatformPath(url, platform_path);
 }
 
 FileSystemOperationRunner::FileSystemOperationRunner(
@@ -511,21 +512,6 @@
   FinishOperation(id);
 }
 
-FileSystemOperation*
-FileSystemOperationRunner::CreateFileSystemOperationImpl(
-    const FileSystemURL& url, base::PlatformFileError* error) {
-  FileSystemOperation* operation =
-      file_system_context_->CreateFileSystemOperation(url, error);
-  if (!operation)
-    return NULL;
-  if (!operation->AsFileSystemOperationImpl()) {
-    *error = base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
-    delete operation;
-    return NULL;
-  }
-  return operation;
-}
-
 void FileSystemOperationRunner::PrepareForWrite(OperationID id,
                                                 const FileSystemURL& url) {
   if (file_system_context_->GetUpdateObservers(url.type())) {
diff --git a/webkit/browser/fileapi/file_system_operation_runner.h b/webkit/browser/fileapi/file_system_operation_runner.h
index 67f30b8..3b36c43 100644
--- a/webkit/browser/fileapi/file_system_operation_runner.h
+++ b/webkit/browser/fileapi/file_system_operation_runner.h
@@ -31,12 +31,6 @@
 // operation fails, in addition to dispatching the callback with an error
 // code (therefore in most cases the caller does not need to check the
 // returned operation ID).
-//
-// Some operations (e.g. CopyInForeignFile, RemoveFile, RemoveDirectory,
-// CopyFileLocal, MoveFileLocal and SyncGetPlatformPath) are only supported
-// by filesystems which implement FileSystemOperationImpl.
-// If they are called on other filesystems
-// base::PLATFORM_FILE_ERROR_INVALID_OPERATION is returned via callback.
 class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemOperationRunner
     : public base::SupportsWeakPtr<FileSystemOperationRunner> {
  public:
@@ -263,15 +257,6 @@
       const base::FilePath& platform_path,
       const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref);
 
-  // A helper method for creating FileSystemOperationImpl for operations
-  // that are supported only in FileSystemOperationImpl.
-  // Note that this returns FileSystemOperation, so the caller needs to
-  // call AsFileSystemOperationImpl() (which is guaranteed to be non-null
-  // if this method returns without error).
-  FileSystemOperation* CreateFileSystemOperationImpl(
-      const FileSystemURL& url,
-      base::PlatformFileError* error);
-
   void PrepareForWrite(OperationID id, const FileSystemURL& url);
   void PrepareForRead(OperationID id, const FileSystemURL& url);
 
diff --git a/webkit/browser/fileapi/file_system_quota_client_unittest.cc b/webkit/browser/fileapi/file_system_quota_client_unittest.cc
index 477bb77..7b1d4c7 100644
--- a/webkit/browser/fileapi/file_system_quota_client_unittest.cc
+++ b/webkit/browser/fileapi/file_system_quota_client_unittest.cc
@@ -11,8 +11,8 @@
 #include "base/platform_file.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
+#include "webkit/browser/fileapi/async_file_test_helper.h"
 #include "webkit/browser/fileapi/file_system_context.h"
-#include "webkit/browser/fileapi/file_system_operation_context.h"
 #include "webkit/browser/fileapi/file_system_quota_client.h"
 #include "webkit/browser/fileapi/file_system_usage_cache.h"
 #include "webkit/browser/fileapi/mock_file_system_context.h"
@@ -110,32 +110,16 @@
                    weak_factory_.GetWeakPtr()));
   }
 
-  FileSystemOperationContext* CreateFileSystemOperationContext(
-      FileSystemType type) {
-    FileSystemOperationContext* context =
-        new FileSystemOperationContext(file_system_context_.get());
-    context->set_allowed_bytes_growth(100000000);
-    context->set_update_observers(
-        *file_system_context_->GetUpdateObservers(type));
-    return context;
-  }
-
   bool CreateFileSystemDirectory(const base::FilePath& file_path,
                                  const std::string& origin_url,
                                  quota::StorageType storage_type) {
     FileSystemType type = QuotaStorageTypeToFileSystemType(storage_type);
-    FileSystemFileUtil* file_util = file_system_context_->GetFileUtil(type);
-
     FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
         GURL(origin_url), type, file_path);
-    scoped_ptr<FileSystemOperationContext> context(
-        CreateFileSystemOperationContext(type));
 
     base::PlatformFileError result =
-        file_util->CreateDirectory(context.get(), url, false, false);
-    if (result != base::PLATFORM_FILE_OK)
-      return false;
-    return true;
+        AsyncFileTestHelper::CreateDirectory(file_system_context_, url);
+    return result == base::PLATFORM_FILE_OK;
   }
 
   bool CreateFileSystemFile(const base::FilePath& file_path,
@@ -146,22 +130,17 @@
       return false;
 
     FileSystemType type = QuotaStorageTypeToFileSystemType(storage_type);
-    FileSystemFileUtil* file_util = file_system_context_->GetFileUtil(type);
-
     FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
         GURL(origin_url), type, file_path);
-    scoped_ptr<FileSystemOperationContext> context(
-        CreateFileSystemOperationContext(type));
 
-    bool created = false;
-    if (base::PLATFORM_FILE_OK !=
-        file_util->EnsureFileExists(context.get(), url, &created))
+    base::PlatformFileError result =
+        AsyncFileTestHelper::CreateFile(file_system_context_, url);
+    if (result != base::PLATFORM_FILE_OK)
       return false;
-    EXPECT_TRUE(created);
-    if (base::PLATFORM_FILE_OK !=
-        file_util->Truncate(context.get(), url, file_size))
-      return false;
-    return true;
+
+    result = AsyncFileTestHelper::TruncateFile(
+        file_system_context_, url, file_size);
+    return result == base::PLATFORM_FILE_OK;
   }
 
   void InitializeOriginFiles(FileSystemQuotaClient* quota_client,
diff --git a/webkit/browser/fileapi/file_system_url.cc b/webkit/browser/fileapi/file_system_url.cc
index 6269296..930ec16 100644
--- a/webkit/browser/fileapi/file_system_url.cc
+++ b/webkit/browser/fileapi/file_system_url.cc
@@ -135,6 +135,20 @@
 
 FileSystemURL::~FileSystemURL() {}
 
+GURL FileSystemURL::ToGURL() const {
+  if (!is_valid_)
+    return GURL();
+
+  std::string url = GetFileSystemRootURI(origin_, mount_type_).spec();
+  if (url.empty())
+    return GURL();
+
+  url.append(virtual_path_.AsUTF8Unsafe());
+
+  // Build nested GURL.
+  return GURL(url);
+}
+
 std::string FileSystemURL::DebugString() const {
   if (!is_valid_)
     return "invalid filesystem: URL";
diff --git a/webkit/browser/fileapi/file_system_url.h b/webkit/browser/fileapi/file_system_url.h
index 6ac875d..36b862e 100644
--- a/webkit/browser/fileapi/file_system_url.h
+++ b/webkit/browser/fileapi/file_system_url.h
@@ -118,6 +118,9 @@
 
   FileSystemType mount_type() const { return mount_type_; }
 
+  // Returns the formatted URL of this instance.
+  GURL ToGURL() const;
+
   std::string DebugString() const;
 
   // Returns true if this URL is a strict parent of the |child|.
diff --git a/webkit/browser/fileapi/file_system_url_request_job_unittest.cc b/webkit/browser/fileapi/file_system_url_request_job_unittest.cc
index a8f6cab..45c1144 100644
--- a/webkit/browser/fileapi/file_system_url_request_job_unittest.cc
+++ b/webkit/browser/fileapi/file_system_url_request_job_unittest.cc
@@ -28,9 +28,9 @@
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/browser/fileapi/async_file_test_helper.h"
 #include "webkit/browser/fileapi/file_system_context.h"
 #include "webkit/browser/fileapi/file_system_file_util.h"
-#include "webkit/browser/fileapi/file_system_operation_context.h"
 #include "webkit/browser/fileapi/mock_file_system_context.h"
 
 namespace fileapi {
@@ -124,48 +124,23 @@
   }
 
   void CreateDirectory(const base::StringPiece& dir_name) {
-    FileSystemFileUtil* file_util = file_system_context_->GetFileUtil(
-        kFileSystemTypeTemporary);
     FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
         GURL("http://remote"),
         kFileSystemTypeTemporary,
         base::FilePath().AppendASCII(dir_name));
-
-    FileSystemOperationContext context(file_system_context_.get());
-    context.set_allowed_bytes_growth(1024);
-
-    ASSERT_EQ(base::PLATFORM_FILE_OK, file_util->CreateDirectory(
-        &context,
-        url,
-        false /* exclusive */,
-        false /* recursive */));
+    ASSERT_EQ(base::PLATFORM_FILE_OK, AsyncFileTestHelper::CreateDirectory(
+        file_system_context_, url));
   }
 
   void WriteFile(const base::StringPiece& file_name,
                  const char* buf, int buf_size) {
-    FileSystemFileUtil* file_util = file_system_context_->GetFileUtil(
-        kFileSystemTypeTemporary);
     FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
         GURL("http://remote"),
         kFileSystemTypeTemporary,
         base::FilePath().AppendASCII(file_name));
-
-    FileSystemOperationContext context(file_system_context_.get());
-    context.set_allowed_bytes_growth(1024);
-
-    base::PlatformFile handle = base::kInvalidPlatformFileValue;
-    bool created = false;
-    ASSERT_EQ(base::PLATFORM_FILE_OK, file_util->CreateOrOpen(
-        &context,
-        url,
-        base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE,
-        &handle,
-        &created));
-    EXPECT_TRUE(created);
-    ASSERT_NE(base::kInvalidPlatformFileValue, handle);
-    ASSERT_EQ(buf_size,
-        base::WritePlatformFile(handle, 0 /* offset */, buf, buf_size));
-    base::ClosePlatformFile(handle);
+    ASSERT_EQ(base::PLATFORM_FILE_OK,
+              AsyncFileTestHelper::CreateFileWithData(
+                  file_system_context_, url, buf, buf_size));
   }
 
   GURL CreateFileSystemURL(const std::string& path) {
diff --git a/webkit/browser/fileapi/file_system_url_unittest.cc b/webkit/browser/fileapi/file_system_url_unittest.cc
index ace6700..f1defbd 100644
--- a/webkit/browser/fileapi/file_system_url_unittest.cc
+++ b/webkit/browser/fileapi/file_system_url_unittest.cc
@@ -156,6 +156,23 @@
       CreateFileSystemURL(root3 + child)));
 }
 
+TEST(FileSystemURLTest, ToGURL) {
+  EXPECT_TRUE(FileSystemURL().ToGURL().is_empty());
+  const char* kTestURL[] = {
+    "filesystem:http://chromium.org/persistent/directory/file0",
+    "filesystem:http://chromium.org/temporary/directory/file1",
+    "filesystem:http://chromium.org/isolated/directory/file2",
+    "filesystem:http://chromium.org/external/directory/file2",
+    "filesystem:http://chromium.org/test/directory/file3",
+  };
+
+  for (size_t i = 0; i < arraysize(kTestURL); ++i) {
+    EXPECT_EQ(
+        kTestURL[i],
+        FileSystemURL::CreateForTest(GURL(kTestURL[i])).ToGURL().spec());
+  }
+}
+
 TEST(FileSystemURLTest, DebugString) {
   const GURL kOrigin("http://example.com");
   const base::FilePath kPath(FPL("dir/file"));
diff --git a/webkit/browser/fileapi/file_writer_delegate_unittest.cc b/webkit/browser/fileapi/file_writer_delegate_unittest.cc
index 340fcb0..01ba982 100644
--- a/webkit/browser/fileapi/file_writer_delegate_unittest.cc
+++ b/webkit/browser/fileapi/file_writer_delegate_unittest.cc
@@ -17,9 +17,8 @@
 #include "net/url_request/url_request_status.h"
 #include "testing/platform_test.h"
 #include "url/gurl.h"
+#include "webkit/browser/fileapi/async_file_test_helper.h"
 #include "webkit/browser/fileapi/file_system_context.h"
-#include "webkit/browser/fileapi/file_system_file_util.h"
-#include "webkit/browser/fileapi/file_system_operation_context.h"
 #include "webkit/browser/fileapi/file_system_quota_util.h"
 #include "webkit/browser/fileapi/file_writer_delegate.h"
 #include "webkit/browser/fileapi/mock_file_system_context.h"
@@ -80,10 +79,6 @@
   virtual void SetUp() OVERRIDE;
   virtual void TearDown() OVERRIDE;
 
-  FileSystemFileUtil* file_util() {
-    return file_system_context_->GetFileUtil(kFileSystemType);
-  }
-
   int64 usage() {
     return file_system_context_->GetQuotaUtil(kFileSystemType)
         ->GetOriginUsageOnFileThread(
@@ -98,10 +93,9 @@
 
     FileSystemURL url = GetFileSystemURL(test_file_path);
     base::PlatformFileInfo file_info;
-    base::FilePath platform_path;
     EXPECT_EQ(base::PLATFORM_FILE_OK,
-              file_util()->GetFileInfo(NewOperationContext().get(), url,
-                                       &file_info, &platform_path));
+              AsyncFileTestHelper::GetMetadata(
+                  file_system_context_, url, &file_info));
     return file_info.size;
   }
 
@@ -110,15 +104,6 @@
         kOrigin, kFileSystemType, base::FilePath().FromUTF8Unsafe(file_name));
   }
 
-  scoped_ptr<FileSystemOperationContext> NewOperationContext() {
-    FileSystemOperationContext* context =
-        new FileSystemOperationContext(file_system_context_.get());
-    context->set_update_observers(
-        *file_system_context_->GetUpdateObservers(kFileSystemType));
-    context->set_root_path(dir_.path());
-    return make_scoped_ptr(context);
-  }
-
   FileWriterDelegate* CreateWriterDelegate(
       const char* test_file_path,
       int64 offset,
@@ -231,16 +216,9 @@
 
   file_system_context_ = CreateFileSystemContextForTesting(
       NULL, dir_.path());
-
-  bool created = false;
-  scoped_ptr<FileSystemOperationContext> context = NewOperationContext();
-  context->set_allowed_bytes_growth(kint64max);
-  base::PlatformFileError error = file_util()->EnsureFileExists(
-      context.get(),
-      GetFileSystemURL("test"),
-      &created);
-  ASSERT_EQ(base::PLATFORM_FILE_OK, error);
-  ASSERT_TRUE(created);
+  ASSERT_EQ(base::PLATFORM_FILE_OK,
+            AsyncFileTestHelper::CreateFile(
+                file_system_context_, GetFileSystemURL("test")));
   net::URLRequest::Deprecated::RegisterProtocolFactory("blob", &Factory);
 }
 
@@ -336,11 +314,9 @@
   scoped_ptr<FileWriterDelegate> file_writer_delegate2;
   scoped_ptr<net::URLRequest> request2;
 
-  bool created = false;
-  file_util()->EnsureFileExists(NewOperationContext().get(),
-                                GetFileSystemURL("test2"),
-                                &created);
-  ASSERT_TRUE(created);
+  ASSERT_EQ(base::PLATFORM_FILE_OK,
+            AsyncFileTestHelper::CreateFile(
+                file_system_context_, GetFileSystemURL("test2")));
 
   const GURL kBlobURL("blob:nolimitconcurrent");
   const GURL kBlobURL2("blob:nolimitconcurrent2");
diff --git a/webkit/browser/fileapi/isolated_file_system_backend.cc b/webkit/browser/fileapi/isolated_file_system_backend.cc
index db59e35..1d4efe6 100644
--- a/webkit/browser/fileapi/isolated_file_system_backend.cc
+++ b/webkit/browser/fileapi/isolated_file_system_backend.cc
@@ -72,21 +72,6 @@
                  base::PLATFORM_FILE_ERROR_SECURITY));
 }
 
-FileSystemFileUtil* IsolatedFileSystemBackend::GetFileUtil(
-    FileSystemType type) {
-  switch (type) {
-    case kFileSystemTypeNativeLocal:
-      return isolated_file_util_->sync_file_util();
-    case kFileSystemTypeDragged:
-      return dragged_file_util_->sync_file_util();
-    case kFileSystemTypeForTransientFile:
-      return transient_file_util_->sync_file_util();
-    default:
-      NOTREACHED();
-  }
-  return NULL;
-}
-
 AsyncFileUtil* IsolatedFileSystemBackend::GetAsyncFileUtil(
     FileSystemType type) {
   switch (type) {
diff --git a/webkit/browser/fileapi/isolated_file_system_backend.h b/webkit/browser/fileapi/isolated_file_system_backend.h
index cdc7096..aa03844 100644
--- a/webkit/browser/fileapi/isolated_file_system_backend.h
+++ b/webkit/browser/fileapi/isolated_file_system_backend.h
@@ -25,7 +25,6 @@
       FileSystemType type,
       OpenFileSystemMode mode,
       const OpenFileSystemCallback& callback) OVERRIDE;
-  virtual FileSystemFileUtil* GetFileUtil(FileSystemType type) OVERRIDE;
   virtual AsyncFileUtil* GetAsyncFileUtil(FileSystemType type) OVERRIDE;
   virtual CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory(
       FileSystemType type,
diff --git a/webkit/browser/fileapi/local_file_util_unittest.cc b/webkit/browser/fileapi/local_file_util_unittest.cc
index b0b52e7..5792f18 100644
--- a/webkit/browser/fileapi/local_file_util_unittest.cc
+++ b/webkit/browser/fileapi/local_file_util_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "webkit/browser/fileapi/async_file_test_helper.h"
+#include "webkit/browser/fileapi/async_file_util_adapter.h"
 #include "webkit/browser/fileapi/file_system_context.h"
 #include "webkit/browser/fileapi/file_system_file_util.h"
 #include "webkit/browser/fileapi/file_system_operation_context.h"
@@ -57,8 +58,9 @@
   }
 
   LocalFileUtil* file_util() {
-    return static_cast<LocalFileUtil*>(
-        file_system_context_->GetFileUtil(kFileSystemType));
+    AsyncFileUtilAdapter* adapter = static_cast<AsyncFileUtilAdapter*>(
+        file_system_context_->GetAsyncFileUtil(kFileSystemType));
+    return static_cast<LocalFileUtil*>(adapter->sync_file_util());
   }
 
   FileSystemURL CreateURL(const std::string& file_name) {
diff --git a/webkit/browser/fileapi/recursive_operation_delegate.cc b/webkit/browser/fileapi/recursive_operation_delegate.cc
index c91c74c..2aac669 100644
--- a/webkit/browser/fileapi/recursive_operation_delegate.cc
+++ b/webkit/browser/fileapi/recursive_operation_delegate.cc
@@ -44,7 +44,7 @@
     callback_.Run(base::PLATFORM_FILE_OK);
     return;
   }
-  FileSystemURL url = pending_directories_.front();
+  FileSystemURL url = pending_directories_.top();
   pending_directories_.pop();
   inflight_operations_++;
   ProcessDirectory(
@@ -59,7 +59,7 @@
   }
   while (!pending_files_.empty() &&
          inflight_operations_ < kMaxInflightOperations) {
-    FileSystemURL url = pending_files_.front();
+    FileSystemURL url = pending_files_.top();
     pending_files_.pop();
     inflight_operations_++;
     base::MessageLoopProxy::current()->PostTask(
@@ -100,7 +100,7 @@
     bool has_more) {
   if (error != base::PLATFORM_FILE_OK) {
     if (error == base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY) {
-      // The given path may have been a file, so try RemoveFile now.
+      // The given path may have been a file, so try ProcessFile now.
       ProcessFile(parent,
                   base::Bind(&RecursiveOperationDelegate::DidTryProcessFile,
                              AsWeakPtr(), error));
diff --git a/webkit/browser/fileapi/recursive_operation_delegate.h b/webkit/browser/fileapi/recursive_operation_delegate.h
index 551d875..86da4c9 100644
--- a/webkit/browser/fileapi/recursive_operation_delegate.h
+++ b/webkit/browser/fileapi/recursive_operation_delegate.h
@@ -5,7 +5,7 @@
 #ifndef WEBKIT_BROWSER_FILEAPI_RECURSIVE_OPERATION_DELEGATE_H_
 #define WEBKIT_BROWSER_FILEAPI_RECURSIVE_OPERATION_DELEGATE_H_
 
-#include <queue>
+#include <stack>
 
 #include "base/basictypes.h"
 #include "base/callback.h"
@@ -19,16 +19,10 @@
 class FileSystemOperationRunner;
 
 // A base class for recursive operation delegates.
-// This also provides some convenient default implementations for subclasses
-// like StartRecursiveOperation() and  NewNestedOperation().
 //
 // In short, each subclass should override ProcessFile and ProcessDirectory
 // to process a directory or a file. To start the recursive operation it
 // should also call StartRecursiveOperation.
-//
-// Each subclass can call NewNestedOperation to create a new file system
-// operation to perform a sub-operations, e.g. can call RemoveFile for
-// recursive Remove.
 class RecursiveOperationDelegate
     : public base::SupportsWeakPtr<RecursiveOperationDelegate> {
  public:
@@ -89,8 +83,8 @@
 
   FileSystemContext* file_system_context_;
   StatusCallback callback_;
-  std::queue<FileSystemURL> pending_directories_;
-  std::queue<FileSystemURL> pending_files_;
+  std::stack<FileSystemURL> pending_directories_;
+  std::stack<FileSystemURL> pending_files_;
   int inflight_operations_;
 
   DISALLOW_COPY_AND_ASSIGN(RecursiveOperationDelegate);
diff --git a/webkit/browser/fileapi/sandbox_file_system_backend.cc b/webkit/browser/fileapi/sandbox_file_system_backend.cc
index 5cf2464..d61800f 100644
--- a/webkit/browser/fileapi/sandbox_file_system_backend.cc
+++ b/webkit/browser/fileapi/sandbox_file_system_backend.cc
@@ -86,11 +86,6 @@
       GetFileSystemRootURI(origin_url, type));
 }
 
-FileSystemFileUtil* SandboxFileSystemBackend::GetFileUtil(
-    FileSystemType type) {
-  return delegate_->sync_file_util();
-}
-
 AsyncFileUtil* SandboxFileSystemBackend::GetAsyncFileUtil(
     FileSystemType type) {
   DCHECK(delegate_);
diff --git a/webkit/browser/fileapi/sandbox_file_system_backend.h b/webkit/browser/fileapi/sandbox_file_system_backend.h
index 207600e..cefebc7 100644
--- a/webkit/browser/fileapi/sandbox_file_system_backend.h
+++ b/webkit/browser/fileapi/sandbox_file_system_backend.h
@@ -40,7 +40,6 @@
       FileSystemType type,
       OpenFileSystemMode mode,
       const OpenFileSystemCallback& callback) OVERRIDE;
-  virtual FileSystemFileUtil* GetFileUtil(FileSystemType type) OVERRIDE;
   virtual AsyncFileUtil* GetAsyncFileUtil(FileSystemType type) OVERRIDE;
   virtual CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory(
       FileSystemType type,
diff --git a/webkit/browser/fileapi/sandbox_file_system_backend_delegate.cc b/webkit/browser/fileapi/sandbox_file_system_backend_delegate.cc
index c1c8c88..eaca4d9 100644
--- a/webkit/browser/fileapi/sandbox_file_system_backend_delegate.cc
+++ b/webkit/browser/fileapi/sandbox_file_system_backend_delegate.cc
@@ -138,6 +138,7 @@
 }
 
 SandboxFileSystemBackendDelegate::~SandboxFileSystemBackendDelegate() {
+  io_thread_checker_.DetachFromThread();
   if (!file_task_runner_->RunsTasksOnCurrentThread()) {
     AsyncFileUtil* sandbox_file_util = sandbox_file_util_.release();
     SandboxQuotaObserver* quota_observer = quota_observer_.release();
@@ -244,6 +245,7 @@
                  base::Bind(callback, root_url, name),
                  base::Owned(error_ptr)));
 
+  io_thread_checker_.DetachFromThread();
   is_filesystem_opened_ = true;
 }
 
@@ -397,7 +399,7 @@
     FileSystemType type,
     FileUpdateObserver* observer,
     base::SequencedTaskRunner* task_runner) {
-  DCHECK(!is_filesystem_opened_);
+  DCHECK(!is_filesystem_opened_ || io_thread_checker_.CalledOnValidThread());
   update_observers_[type] =
       update_observers_[type].AddObserver(observer, task_runner);
 }
@@ -406,7 +408,7 @@
     FileSystemType type,
     FileChangeObserver* observer,
     base::SequencedTaskRunner* task_runner) {
-  DCHECK(!is_filesystem_opened_);
+  DCHECK(!is_filesystem_opened_ || io_thread_checker_.CalledOnValidThread());
   change_observers_[type] =
       change_observers_[type].AddObserver(observer, task_runner);
 }
@@ -415,7 +417,7 @@
     FileSystemType type,
     FileAccessObserver* observer,
     base::SequencedTaskRunner* task_runner) {
-  DCHECK(!is_filesystem_opened_);
+  DCHECK(!is_filesystem_opened_ || io_thread_checker_.CalledOnValidThread());
   access_observers_[type] =
       access_observers_[type].AddObserver(observer, task_runner);
 }
diff --git a/webkit/browser/fileapi/sandbox_file_system_backend_delegate.h b/webkit/browser/fileapi/sandbox_file_system_backend_delegate.h
index 61fe700..33d791e 100644
--- a/webkit/browser/fileapi/sandbox_file_system_backend_delegate.h
+++ b/webkit/browser/fileapi/sandbox_file_system_backend_delegate.h
@@ -218,6 +218,7 @@
   FileSystemOptions file_system_options_;
 
   bool is_filesystem_opened_;
+  base::ThreadChecker io_thread_checker_;
 
   // Accessed only on the file thread.
   std::set<GURL> visited_origins_;
diff --git a/webkit/browser/fileapi/sandbox_file_system_test_helper.cc b/webkit/browser/fileapi/sandbox_file_system_test_helper.cc
index 75c29e2..890db5c 100644
--- a/webkit/browser/fileapi/sandbox_file_system_test_helper.cc
+++ b/webkit/browser/fileapi/sandbox_file_system_test_helper.cc
@@ -136,7 +136,7 @@
   DCHECK(file_system_context_.get());
   DCHECK(file_system_context_->sandbox_backend()->CanHandleType(type_));
 
-  file_util_ = file_system_context_->GetFileUtil(type_);
+  file_util_ = file_system_context_->sandbox_delegate()->sync_file_util();
   DCHECK(file_util_);
 
   // Prepare the origin's root directory.
diff --git a/webkit/browser/fileapi/test_file_system_backend.cc b/webkit/browser/fileapi/test_file_system_backend.cc
index 1cff5c2..b789a8c 100644
--- a/webkit/browser/fileapi/test_file_system_backend.cc
+++ b/webkit/browser/fileapi/test_file_system_backend.cc
@@ -150,11 +150,6 @@
                base::PLATFORM_FILE_OK);
 }
 
-FileSystemFileUtil* TestFileSystemBackend::GetFileUtil(FileSystemType type) {
-  DCHECK(local_file_util_.get());
-  return local_file_util_->sync_file_util();
-}
-
 AsyncFileUtil* TestFileSystemBackend::GetAsyncFileUtil(FileSystemType type) {
   return local_file_util_.get();
 }
diff --git a/webkit/browser/fileapi/test_file_system_backend.h b/webkit/browser/fileapi/test_file_system_backend.h
index ef252c1..1dc81c1 100644
--- a/webkit/browser/fileapi/test_file_system_backend.h
+++ b/webkit/browser/fileapi/test_file_system_backend.h
@@ -41,7 +41,6 @@
       FileSystemType type,
       OpenFileSystemMode mode,
       const OpenFileSystemCallback& callback) OVERRIDE;
-  virtual FileSystemFileUtil* GetFileUtil(FileSystemType type) OVERRIDE;
   virtual AsyncFileUtil* GetAsyncFileUtil(FileSystemType type) OVERRIDE;
   virtual CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory(
       FileSystemType type,
diff --git a/webkit/child/resource_loader_bridge.h b/webkit/child/resource_loader_bridge.h
index 9d51b62..160bc84 100644
--- a/webkit/child/resource_loader_bridge.h
+++ b/webkit/child/resource_loader_bridge.h
@@ -127,7 +127,7 @@
   // These callbacks mirror net::URLRequest::Delegate and the order and
   // conditions in which they will be called are identical. See url_request.h
   // for more information.
-  class Peer {
+  class WEBKIT_CHILD_EXPORT Peer {
    public:
     // Called as upload progress is made.
     // note: only for requests with LOAD_ENABLE_UPLOAD_PROGRESS set
diff --git a/webkit/common/cursors/webcursor_mac.mm b/webkit/common/cursors/webcursor_mac.mm
index 1a1dc44..58cff81 100644
--- a/webkit/common/cursors/webcursor_mac.mm
+++ b/webkit/common/cursors/webcursor_mac.mm
@@ -208,8 +208,12 @@
   NSPoint dip_hotspot = NSPointFromCGPoint(gfx::ToFlooredPoint(
       gfx::ScalePoint(hotspot, 1 / custom_scale)).ToCGPoint());
 
+  // Both the image and its representation need to have the same size for
+  // cursors to appear in high resolution on retina displays. Note that the
+  // size of a representation is not the same as pixelsWide or pixelsHigh.
   NSImage* cursor_image = gfx::SkBitmapToNSImage(bitmap);
   [cursor_image setSize:dip_size];
+  [[[cursor_image representations] objectAtIndex:0] setSize:dip_size];
 
   NSCursor* cursor = [[NSCursor alloc] initWithImage:cursor_image
                                              hotSpot:dip_hotspot];
diff --git a/webkit/common/gpu/context_provider_in_process.cc b/webkit/common/gpu/context_provider_in_process.cc
index b7e3cc4..8ec17f4 100644
--- a/webkit/common/gpu/context_provider_in_process.cc
+++ b/webkit/common/gpu/context_provider_in_process.cc
@@ -133,8 +133,23 @@
   return true;
 }
 
-webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl*
-ContextProviderInProcess::Context3d() {
+cc::ContextProvider::Capabilities
+ContextProviderInProcess::ContextCapabilities() {
+  // We always use a WebGraphicsContext3DInProcessCommandBufferImpl which
+  // provides the following capabilities:
+  Capabilities caps;
+  caps.bind_uniform_location = true;
+  caps.discard_backbuffer = true;
+  caps.map_image = true;
+  caps.map_sub = true;
+  caps.set_visibility = true;
+  caps.shallow_flush = true;
+  caps.texture_format_bgra8888 = true;
+  caps.texture_rectangle = true;
+  return caps;
+}
+
+WebKit::WebGraphicsContext3D* ContextProviderInProcess::Context3d() {
   DCHECK(context3d_);
   DCHECK(lost_context_callback_proxy_);  // Is bound to thread.
   DCHECK(context_thread_checker_.CalledOnValidThread());
diff --git a/webkit/common/gpu/context_provider_in_process.h b/webkit/common/gpu/context_provider_in_process.h
index 2379dd2..5285f7b 100644
--- a/webkit/common/gpu/context_provider_in_process.h
+++ b/webkit/common/gpu/context_provider_in_process.h
@@ -33,8 +33,8 @@
   static scoped_refptr<ContextProviderInProcess> CreateOffscreen();
 
   virtual bool BindToCurrentThread() OVERRIDE;
-  virtual webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl*
-      Context3d() OVERRIDE;
+  virtual Capabilities ContextCapabilities() OVERRIDE;
+  virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE;
   virtual class GrContext* GrContext() OVERRIDE;
   virtual void VerifyContexts() OVERRIDE;
   virtual bool DestroyedOnMainThread() OVERRIDE;
diff --git a/webkit/renderer/compositor_bindings/web_compositing_reasons.cc b/webkit/renderer/compositor_bindings/web_compositing_reasons.cc
index bd7396d..04d7372 100644
--- a/webkit/renderer/compositor_bindings/web_compositing_reasons.cc
+++ b/webkit/renderer/compositor_bindings/web_compositing_reasons.cc
@@ -141,3 +141,11 @@
 COMPILE_ASSERT_MATCHING_UINT64(
     cc::kCompositingReasonLayerForMask,
     WebKit::CompositingReasonLayerForMask);
+
+COMPILE_ASSERT_MATCHING_UINT64(
+    cc::kCompositingReasonOverflowScrollingParent,
+    WebKit::CompositingReasonOverflowScrollingParent);
+
+COMPILE_ASSERT_MATCHING_UINT64(
+    cc::kCompositingReasonOutOfFlowClipping,
+    WebKit::CompositingReasonOutOfFlowClipping);
diff --git a/webkit/support/weburl_loader_mock_factory.cc b/webkit/support/weburl_loader_mock_factory.cc
index f18bc07..f459211 100644
--- a/webkit/support/weburl_loader_mock_factory.cc
+++ b/webkit/support/weburl_loader_mock_factory.cc
@@ -24,11 +24,6 @@
 using WebKit::WebURLRequest;
 using WebKit::WebURLResponse;
 
-struct WebURLLoaderMockFactory::ResponseInfo {
-  WebKit::WebURLResponse response;
-  base::FilePath file_path;
-};
-
 WebURLLoaderMockFactory::WebURLLoaderMockFactory() {}
 
 WebURLLoaderMockFactory::~WebURLLoaderMockFactory() {}
diff --git a/webkit/support/weburl_loader_mock_factory.h b/webkit/support/weburl_loader_mock_factory.h
index 78abd96..8f00fc0 100644
--- a/webkit/support/weburl_loader_mock_factory.h
+++ b/webkit/support/weburl_loader_mock_factory.h
@@ -9,12 +9,12 @@
 
 #include "base/files/file_path.h"
 #include "third_party/WebKit/public/platform/WebURL.h"
+#include "third_party/WebKit/public/platform/WebURLError.h"
 #include "third_party/WebKit/public/platform/WebURLRequest.h"
 #include "third_party/WebKit/public/platform/WebURLResponse.h"
 
 namespace WebKit {
 class WebData;
-struct WebURLError;
 class WebURLLoader;
 }
 
@@ -75,7 +75,11 @@
   void CancelLoad(WebURLLoaderMock* loader);
 
  private:
-  struct ResponseInfo;
+  struct ResponseInfo {
+    WebKit::WebURLResponse response;
+    base::FilePath file_path;
+  };
+
 
   // Loads the specified request and populates the response, error and data
   // accordingly.
diff --git a/win8/delegate_execute/command_execute_impl.cc b/win8/delegate_execute/command_execute_impl.cc
index 722f38c..bfb1e51 100644
--- a/win8/delegate_execute/command_execute_impl.cc
+++ b/win8/delegate_execute/command_execute_impl.cc
@@ -535,7 +535,15 @@
     }
   }
 
-  if (launch_mode >= ECHUIM_SYSTEM_LAUNCHER) {
+  // According to 'developing metro style enabled desktop browser' document
+  // ECHUIM_SYSTEM_LAUNCHER – Start menu launch (includes Tile activation,
+  // typing a URL into the search box in Start, etc.)
+  // In non aura world we apparently used ECHUIM_SYSTEM_LAUNCHER to mean
+  // launch on desktop. For Aura we are changing ECHUIM_SYSTEM to mean
+  // immersive mode.
+  if (launch_mode == ECHUIM_SYSTEM_LAUNCHER)
+    launch_mode = ECHUIM_IMMERSIVE;
+  else if (launch_mode > ECHUIM_SYSTEM_LAUNCHER) {
     // At the end if launch mode is not proper apply heuristics.
     launch_mode = base::win::IsTouchEnabledDevice() ?
                           ECHUIM_IMMERSIVE : ECHUIM_DESKTOP;